From b09ccfe0eca61180d6849999b7fe0b411f93e5dd Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Tue, 15 Aug 2023 09:36:25 -0400 Subject: [PATCH 001/414] Fix webhook reject scaling after stop and restart adb --- apis/database/v1alpha1/autonomousdatabase_webhook.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go index 9c01ad95..bac49c70 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -164,7 +164,9 @@ func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) error { // cannot change lifecycleState with other fields together (except the oci config) var lifecycleChanged, otherFieldsChanged bool - lifecycleChanged = oldADB.Spec.Details.LifecycleState != "" && oldADB.Spec.Details.LifecycleState != r.Spec.Details.LifecycleState + lifecycleChanged = oldADB.Spec.Details.LifecycleState != "" && + r.Spec.Details.LifecycleState != "" && + oldADB.Spec.Details.LifecycleState != r.Spec.Details.LifecycleState var copiedADB *AutonomousDatabaseSpec = r.Spec.DeepCopy() copiedADB.Details.LifecycleState = oldADB.Spec.Details.LifecycleState copiedADB.OCIConfig = oldADB.Spec.OCIConfig From 7f8634f15e34d6ebfeff7edf33dfb6cd5fb721c5 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Wed, 23 Aug 2023 19:44:25 +0000 Subject: [PATCH 002/414] Update the command of installing cert-manager --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 66cd62ec..5a4614ae 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,11 @@ Oracle strongly recommends that you ensure your system meets the following [Prer Install the certificate manager with the following command: ```sh - kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml + # Kubernetes 1.16+ + kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.1.1/cert-manager.yaml + + # Kubernetes <1.16 + kubectl apply --validate=false -f https://github.com/cert-manager/cert-manager/releases/download/v1.1.1/cert-manager-legacy.yaml ``` ## Quick Install of the Operator From f63c1a64fbb4d82463d688d239fd2355a1d721ee Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Mon, 28 Aug 2023 21:36:07 +0000 Subject: [PATCH 003/414] Update cert-manager command installation command in README.md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 5a4614ae..614f6c65 100644 --- a/README.md +++ b/README.md @@ -55,11 +55,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer Install the certificate manager with the following command: ```sh - # Kubernetes 1.16+ - kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.1.1/cert-manager.yaml - - # Kubernetes <1.16 - kubectl apply --validate=false -f https://github.com/cert-manager/cert-manager/releases/download/v1.1.1/cert-manager-legacy.yaml + kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml ``` ## Quick Install of the Operator From 53d92cf4cbd0b732d4617170d8b3bdfc9e282fd3 Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Fri, 8 Sep 2023 19:24:46 +0530 Subject: [PATCH 004/414] Enhance CI/CD pipeline to use commit ID --- .gitlab-ci.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d5080ff9..7008b235 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,17 +1,14 @@ build-operator: stage: build variables: - IMAGE: "$DOCKER_REPO:$CI_COMMIT_BRANCH" + IMAGE: "$DOCKER_REPO:$CI_COMMIT_SHORT_SHA" OP_YAML: oracle-database-operator.yaml script: - go version - - echo $CI_COMMIT_SHORT_SHA - make docker-build IMG="$IMAGE" - docker push "$IMAGE" - - newimage=$DOCKER_REPO@$(skopeo inspect docker://$IMAGE | jq -r .Digest) - - echo $newimage - docker rmi "$IMAGE" && docker system prune -f - - make operator-yaml IMG=$newimage + - make operator-yaml IMG=$IMAGE - if [ "$CI_COMMIT_BRANCH" != "master" ]; then sed -i "s/\(replicas.\) 3/\1 1/g" ./$OP_YAML; fi - curl -s --netrc-file $HOME/.netrc_gitlab $ARTIFACTORY_REPO/$CI_COMMIT_BRANCH/$OP_YAML -T ./$OP_YAML only: From 1cc87372113afbcf220c39c69bbc3cbdfa1e6abc Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Tue, 12 Sep 2023 08:30:42 +0000 Subject: [PATCH 005/414] Bugfix issue 67 --- commons/database/constants.go | 6 +-- commons/database/utils.go | 50 +++++++++---------- .../singleinstancedatabase_controller.go | 34 ++++++++----- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/commons/database/constants.go b/commons/database/constants.go index 33dce251..de07c0d4 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -212,8 +212,8 @@ const DataguardBrokerAddDBMaxAvailabilityCMD string = "ADD DATABASE ${ORACLE_SID "(CONNECT_DATA=(SERVICE_NAME=${ORACLE_SID}_DGMGRL)(INSTANCE_NAME=${ORACLE_SID})(SERVER=DEDICATED)))';" + "\nENABLE CONFIGURATION;" -const RemoveStandbyDBFromDGConfgCMD string = "DISABLE DATABASE ${ORACLE_SID};" + - "\nREMOVE DATABASE ${ORACLE_SID};" +const RemoveStandbyDBFromDGConfgCMD string = "DISABLE DATABASE ${ORACLE_SID};" + + "\nREMOVE DATABASE ${ORACLE_SID};" const DBShowConfigCMD string = "SHOW CONFIGURATION;" @@ -506,7 +506,7 @@ const SetApexUsers string = "\numask 177" + "\numask 022" // Get Sid, Pdbname, Edition for prebuilt db -const GetSidPdbEditionCMD string = "echo $ORACLE_SID,$ORACLE_PDB,$ORACLE_EDITION,Edition;" +const GetSidPdbEditionCMD string = "echo $ORACLE_SID,$ORACLE_PDB,$ORACLE_EDITION;" // Command to enable TCPS as a formatted string. The parameter would be the port at which TCPS is enabled. const EnableTcpsCMD string = "$ORACLE_BASE/$CONFIG_TCPS_FILE" diff --git a/commons/database/utils.go b/commons/database/utils.go index c1ecd680..6e6f6d02 100644 --- a/commons/database/utils.go +++ b/commons/database/utils.go @@ -507,23 +507,23 @@ func GetDatabaseRole(readyPod corev1.Pod, r client.Reader, func GetDatabaseOpenMode(readyPod corev1.Pod, r client.Reader, config *rest.Config, ctx context.Context, req ctrl.Request, edition string) (string, error) { - log := ctrllog.FromContext(ctx).WithValues("GetDatabaseOpenMode",req.NamespacedName) + log := ctrllog.FromContext(ctx).WithValues("GetDatabaseOpenMode", req.NamespacedName) - out,err := ExecCommand(r, config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | %s",GetDBOpenMode,SQLPlusCLI)) - if err != nil { - return "",err - } - log.Info(out) - if !strings.Contains(out, "no rows selected") && !strings.Contains(out, "ORA-") { - out1 := strings.Replace(out, " ", "_", -1) - // filtering output and storing databse_role in "database_role" - databaseOpenMode := strings.Fields(out1)[2] - // first 2 values in the slice will be column name(DATABASE_ROLE) and a seperator(--------------) . - return databaseOpenMode, nil - } - return "", errors.New("database open mode is nil") + out, err := ExecCommand(r, config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | %s", GetDBOpenMode, SQLPlusCLI)) + if err != nil { + return "", err + } + log.Info(out) + if !strings.Contains(out, "no rows selected") && !strings.Contains(out, "ORA-") { + out1 := strings.Replace(out, " ", "_", -1) + // filtering output and storing databse_role in "database_role" + databaseOpenMode := strings.Fields(out1)[2] + // first 2 values in the slice will be column name(DATABASE_ROLE) and a seperator(--------------) . + return databaseOpenMode, nil } + return "", errors.New("database open mode is nil") +} // Returns true if any of the pod in 'pods' is with pod.Status.Phase == phase func IsAnyPodWithStatus(pods []corev1.Pod, phase corev1.PodPhase) (bool, corev1.Pod) { @@ -590,29 +590,29 @@ func GetNodeIp(r client.Reader, ctx context.Context, req ctrl.Request) string { } // GetSidPdbEdition to display sid, pdbname, edition in ConnectionString -func GetSidPdbEdition(r client.Reader, config *rest.Config, ctx context.Context, req ctrl.Request) (string, string, string) { +func GetSidPdbEdition(r client.Reader, config *rest.Config, ctx context.Context, req ctrl.Request) (string, string, string, error) { - log := ctrllog.FromContext(ctx).WithValues("GetNodeIp", req.NamespacedName) + log := ctrllog.FromContext(ctx).WithValues("GetSidbPdbEdition", req.NamespacedName) readyPod, _, _, _, err := FindPods(r, "", "", req.Name, req.Namespace, ctx, req) if err != nil { log.Error(err, err.Error()) - return "", "", "" + return "", "", "", fmt.Errorf("error while fetching ready pod %s : \n %s", readyPod.Name, err.Error()) } if readyPod.Name != "" { out, err := ExecCommand(r, config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", GetSidPdbEditionCMD) if err != nil { log.Error(err, err.Error()) - return "", "", "" - } - splitstr := strings.Split(out, ",") - if len(splitstr) == 4 { - return splitstr[0], splitstr[1], splitstr[2] + return "", "", "", err } + log.Info("GetSidPdbEditionCMD output \n" + out) + splitstr := strings.Split((strings.TrimSpace(out)), ",") + return splitstr[0], splitstr[1], splitstr[2], nil } - - return "", "", "" + err = errors.New("ready pod name is nil") + log.Error(err, err.Error()) + return "", "", "", err } // Get Datapatch Status diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index a4baf0a9..e468214c 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -167,22 +167,22 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct return result, nil } - // Service creation - result, err = r.createOrReplaceSVC(ctx, req, singleInstanceDatabase) + // PVC Creation + result, err = r.createOrReplacePVC(ctx, req, singleInstanceDatabase) if result.Requeue { r.Log.Info("Reconcile queued") return result, nil } - // PVC Creation - result, err = r.createOrReplacePVC(ctx, req, singleInstanceDatabase) + // POD creation + result, err = r.createOrReplacePods(singleInstanceDatabase, cloneFromDatabase, referredPrimaryDatabase, ctx, req) if result.Requeue { r.Log.Info("Reconcile queued") return result, nil } - // POD creation - result, err = r.createOrReplacePods(singleInstanceDatabase, cloneFromDatabase, referredPrimaryDatabase, ctx, req) + // Service creation + result, err = r.createOrReplaceSVC(ctx, req, singleInstanceDatabase) if result.Requeue { r.Log.Info("Reconcile queued") return result, nil @@ -280,6 +280,7 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct // If LoadBalancer = true , ensure Connect String is updated if singleInstanceDatabase.Status.ConnectString == dbcommons.ValueUnavailable { + r.Log.Info("Connect string not available for the database " + singleInstanceDatabase.Name) return requeueY, nil } @@ -1494,17 +1495,24 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplaceSVC(ctx context.Contex extSvc = svc } - pdbName := strings.ToUpper(m.Spec.Pdbname) - sid := m.Spec.Sid + var sid, pdbName string + var getSidPdbEditionErr error if m.Spec.Image.PrebuiltDB { - edition := "" - sid, pdbName, edition = dbcommons.GetSidPdbEdition(r, r.Config, ctx, req) - if sid == "" || pdbName == "" || edition == "" { - return requeueN, nil + r.Log.Info("Initiliazing database sid, pdb, edition for prebuilt database") + var edition string + sid, pdbName, edition, getSidPdbEditionErr = dbcommons.GetSidPdbEdition(r, r.Config, ctx, ctrl.Request{NamespacedName: types.NamespacedName{Namespace: m.Namespace, Name: m.Name}}) + if getSidPdbEditionErr != nil { + return requeueY, getSidPdbEditionErr } + r.Log.Info(fmt.Sprintf("Prebuilt database: %s has SID : %s, PDB : %s, EDITION: %s", m.Name, sid, pdbName, edition)) m.Status.Edition = cases.Title(language.English).String(edition) } - + if sid == "" { + sid = strings.ToUpper(m.Spec.Sid) + } + if pdbName == "" { + pdbName = strings.ToUpper(m.Spec.Pdbname) + } if m.Spec.LoadBalancer { m.Status.ClusterConnectString = extSvc.Name + "." + extSvc.Namespace + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) if len(extSvc.Status.LoadBalancer.Ingress) > 0 { From 94e1d84f7a33319adc2cf66a05e914813e12328c Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Fri, 22 Sep 2023 14:03:56 +0000 Subject: [PATCH 006/414] Fix adb controller reconcile error --- Makefile | 2 +- .../database/autonomousdatabase_controller.go | 44 +++++++++++-------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index e904262b..033b8a36 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ test: manifests generate fmt vet envtest ## Run unit tests. E2ETEST ?= ./test/e2e/ e2e: manifests generate fmt vet envtest ## Run e2e tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" ginkgo -v --timeout=2h30m --fail-fast $(E2ETEST) + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $(E2ETEST) -test.timeout 0 -test.v --ginkgo.fail-fast ##@ Build diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 379ab86b..770a3fa4 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -157,10 +157,15 @@ func (r *AutonomousDatabaseReconciler) eventFilterPredicate() predicate.Predicat if adbOk { oldADB := e.ObjectOld.(*dbv1alpha1.AutonomousDatabase) - if !reflect.DeepEqual(oldADB.Status, desiredADB.Status) || - (controllerutil.ContainsFinalizer(oldADB, dbv1alpha1.LastSuccessfulSpec) != controllerutil.ContainsFinalizer(desiredADB, dbv1alpha1.LastSuccessfulSpec)) || + statusChanged := !reflect.DeepEqual(oldADB.Status, desiredADB.Status) + + oldLastSucSpec := oldADB.GetAnnotations()[dbv1alpha1.LastSuccessfulSpec] + desiredLastSucSpec := desiredADB.GetAnnotations()[dbv1alpha1.LastSuccessfulSpec] + lastSucSpecChanged := oldLastSucSpec != desiredLastSucSpec + + if statusChanged || lastSucSpecChanged || (controllerutil.ContainsFinalizer(oldADB, dbv1alpha1.ADBFinalizer) != controllerutil.ContainsFinalizer(desiredADB, dbv1alpha1.ADBFinalizer)) { - // Don't enqueue if the status, lastSucSpec, or the finalizler changes + // Don't enqueue if the status, lastSucSpec, or the finalizler changes the first time return false } @@ -458,8 +463,6 @@ func (r *AutonomousDatabaseReconciler) validateOperation( } else { l.Info("No operation specified; sync the resource") - testOldADB := adb.DeepCopy() - // The user doesn't change the spec and the controller should pull the spec from the OCI. specChanged, err := r.getADB(logger, adb) if err != nil { @@ -470,12 +473,12 @@ func (r *AutonomousDatabaseReconciler) validateOperation( l.Info("The local spec doesn't match the oci's spec; update the CR") // Erase the status.lifecycleState temporarily to avoid the webhook error. - oldADB := adb.DeepCopy() + tmpADB := adb.DeepCopy() adb.Status.LifecycleState = "" - r.KubeClient.Status().Update(context.TODO(), adb) - adb.Spec = oldADB.Spec - - adb.DeepCopy().RemoveUnchangedDetails(testOldADB.Spec) + if err := r.KubeClient.Status().Update(context.TODO(), adb); err != nil { + return false, emptyResult, err + } + adb.Spec = tmpADB.Spec if err := r.updateCR(adb); err != nil { return false, emptyResult, err @@ -580,7 +583,9 @@ func (r *AutonomousDatabaseReconciler) validateFinalizer(logger logr.Logger, adb // updateCR updates the lastSucSpec and the CR func (r *AutonomousDatabaseReconciler) updateCR(adb *dbv1alpha1.AutonomousDatabase) error { // Update the lastSucSpec - if err := adb.UpdateLastSuccessfulSpec(); err != nil { + // Should patch the lastSuccessfulSpec first, otherwise, the update event will be + // filtered out by predicate since the lastSuccessfulSpec is changed. + if err := r.patchLastSuccessfulSpec(adb); err != nil { return err } @@ -933,22 +938,23 @@ func (r *AutonomousDatabaseReconciler) validateDesiredLifecycleState( } // The logic of updating the network access configurations is as follows: -// 1. Shared databases: -// If the network access type changes -// a. to PUBLIC: +// +// 1. Shared databases: +// If the network access type changes +// a. to PUBLIC: // was RESTRICTED: re-enable IsMTLSConnectionRequired if its not. Then set WhitelistedIps to an array with a single empty string entry. // was PRIVATE: re-enable IsMTLSConnectionRequired if its not. Then set PrivateEndpointLabel to an emtpy string. -// b. to RESTRICTED: +// b. to RESTRICTED: // was PUBLIC: set WhitelistedIps to desired IPs/CIDR blocks/VCN OCID. Configure the IsMTLSConnectionRequired settings if it is set to disabled. // was PRIVATE: re-enable IsMTLSConnectionRequired if its not. Set the type to PUBLIC first, and then configure the WhitelistedIps. Finally resume the IsMTLSConnectionRequired settings if it was, or is configured as disabled. -// c. to PRIVATE: +// c. to PRIVATE: // was PUBLIC: set subnetOCID and nsgOCIDs. Configure the IsMTLSConnectionRequired settings if it is set. // was RESTRICTED: set subnetOCID and nsgOCIDs. Configure the IsMTLSConnectionRequired settings if it is set. // -// Otherwise, if the network access type remains the same, apply the network configuration, and then set the IsMTLSConnectionRequired. +// Otherwise, if the network access type remains the same, apply the network configuration, and then set the IsMTLSConnectionRequired. // -// 2. Dedicated databases: -// Apply the configs directly +// 2. Dedicated databases: +// Apply the configs directly func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase, From 121e0ed788752be66c8bb7a1a648cdba03edf628 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Thu, 28 Sep 2023 16:07:26 +0000 Subject: [PATCH 007/414] Fix adb reconcile --- .../database/autonomousdatabase_controller.go | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 770a3fa4..54ff3431 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -157,15 +157,17 @@ func (r *AutonomousDatabaseReconciler) eventFilterPredicate() predicate.Predicat if adbOk { oldADB := e.ObjectOld.(*dbv1alpha1.AutonomousDatabase) + specChanged := !reflect.DeepEqual(oldADB.Spec, desiredADB.Spec) statusChanged := !reflect.DeepEqual(oldADB.Status, desiredADB.Status) oldLastSucSpec := oldADB.GetAnnotations()[dbv1alpha1.LastSuccessfulSpec] desiredLastSucSpec := desiredADB.GetAnnotations()[dbv1alpha1.LastSuccessfulSpec] lastSucSpecChanged := oldLastSucSpec != desiredLastSucSpec - if statusChanged || lastSucSpecChanged || + if (!specChanged && statusChanged) || lastSucSpecChanged || (controllerutil.ContainsFinalizer(oldADB, dbv1alpha1.ADBFinalizer) != controllerutil.ContainsFinalizer(desiredADB, dbv1alpha1.ADBFinalizer)) { - // Don't enqueue if the status, lastSucSpec, or the finalizler changes the first time + // Don't enqueue in the folowing condition: + // 1. only status changes 2. lastSucSpec changes 3. ADBFinalizer changes return false } @@ -276,31 +278,36 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R } /****************************************************************** - * Requeue if it's in an intermediate state. Update the status right before - * exiting the reconcile, otherwise the modifiedADB will be overwritten - * by the object returned from the cluster. + * Update the resource if the spec has been changed. + * Requeue if it's in an intermediate state. Update the status first + * , otherwise the modifiedADB will be overwritten by the object + * returned from the cluster. ******************************************************************/ - if dbv1alpha1.IsADBIntermediateState(modifiedADB.Status.LifecycleState) { - logger.WithName("IsADBIntermediateState").Info("LifecycleState is " + string(modifiedADB.Status.LifecycleState) + "; reconcile queued") - - if err := r.KubeClient.Status().Update(context.TODO(), modifiedADB); err != nil { + if !reflect.DeepEqual(modifiedADB.Spec, desiredADB.Spec) { + if err := r.KubeClient.Update(context.TODO(), modifiedADB); err != nil { return r.manageError(logger.WithName("IsADBIntermediateState"), modifiedADB, err) } + return emptyResult, nil + } + copiedADB := modifiedADB.DeepCopy() + if err := r.KubeClient.Status().Update(context.TODO(), modifiedADB); err != nil { + return r.manageError(logger.WithName("Status().Update"), modifiedADB, err) + } + modifiedADB.Spec = copiedADB.Spec + + if dbv1alpha1.IsADBIntermediateState(modifiedADB.Status.LifecycleState) { + logger.WithName("IsADBIntermediateState").Info("LifecycleState is " + string(modifiedADB.Status.LifecycleState) + "; reconcile queued") return requeueResult, nil } /****************************************************************** - * Update the lastSucSpec and the status, and then finish the reconcile. - * Requeue if it's in an intermediate state or the modifiedADB - * doesn't match the desiredADB. + * Update the lastSucSpec, and then finish the reconcile. + * Requeue if the ADB is terminated, but the finalizer is not yet + * removed. ******************************************************************/ - // Do the comparison before updating the status to avoid being overwritten - var requeue bool = false - if !reflect.DeepEqual(modifiedADB.Spec, desiredADB.Spec) { - requeue = true - } + var requeue bool = false if modifiedADB.GetDeletionTimestamp() != nil && controllerutil.ContainsFinalizer(modifiedADB, dbv1alpha1.ADBFinalizer) && modifiedADB.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated { @@ -312,10 +319,6 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R return r.manageError(logger.WithName("patchLastSuccessfulSpec"), modifiedADB, err) } - if err := r.KubeClient.Status().Update(context.TODO(), modifiedADB); err != nil { - return r.manageError(logger.WithName("Status().Update"), modifiedADB, err) - } - if requeue { logger.Info("Reconcile queued") return requeueResult, nil From 2d1126df3876af41866d5109edc1f586554dff2c Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Fri, 13 Oct 2023 14:18:52 +0000 Subject: [PATCH 008/414] Tcps user certs --- .../v1alpha1/singleinstancedatabase_types.go | 3 + .../singleinstancedatabase_webhook.go | 13 ++++ commons/database/constants.go | 3 + .../samples/sidb/singleinstancedatabase.yaml | 6 +- .../sidb/singleinstancedatabase_tcps.yaml | 7 ++- .../singleinstancedatabase_controller.go | 62 ++++++++++++++++++- docs/sidb/README.md | 42 ++++++++----- 7 files changed, 116 insertions(+), 20 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index 81290735..03144530 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -69,6 +69,7 @@ type SingleInstanceDatabaseSpec struct { ForceLogging bool `json:"forceLog,omitempty"` EnableTCPS bool `json:"enableTCPS,omitempty"` TcpsCertRenewInterval string `json:"tcpsCertRenewInterval,omitempty"` + TcpsTlsSecret string `json:"tcpsTlsSecret,omitempty"` DgBrokerConfigured bool `json:"dgBrokerConfigured,omitempty"` CloneFrom string `json:"cloneFrom,omitempty"` @@ -162,6 +163,8 @@ type SingleInstanceDatabaseStatus struct { ClientWalletLoc string `json:"clientWalletLoc,omitempty"` PrimaryDatabase string `json:"primaryDatabase,omitempty"` DgBrokerConfigured bool `json:"dgBrokerConfigured,omitempty"` + // +kubebuilder:default:="" + TcpsTlsSecret string `json:"tcpsTlsSecret"` // +patchMergeKey=type // +patchStrategy=merge diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 5425d99c..d70bb0c9 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -328,6 +328,19 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { "Please specify tcpsCertRenewInterval in the range: 24h to 8760h")) } } + + // tcpsTlsSecret validations + if !r.Spec.EnableTCPS && r.Spec.TcpsTlsSecret != "" { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("tcpsTlsSecret"), + " is allowed only if enableTCPS is true")) + } + if r.Spec.TcpsTlsSecret != "" && r.Spec.TcpsCertRenewInterval != "" { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("tcpsCertRenewInterval"), + " is applicable only for self signed certs")) + } + if len(allErrs) == 0 { return nil } diff --git a/commons/database/constants.go b/commons/database/constants.go index de07c0d4..00064e07 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -517,6 +517,9 @@ const RenewCertsCMD string = EnableTcpsCMD // Command to disable TCPS const DisableTcpsCMD string = "$ORACLE_BASE/$CONFIG_TCPS_FILE disable" +// Location of tls certs +const TlsCertsLocation string = "/run/secrets/tls_secret" + // TCPS clientWallet update command const ClientWalletUpdate string = "sed -i -e 's/HOST.*$/HOST=%s)/g' -e 's/PORT.*$/PORT=%d)/g' ${ORACLE_BASE}/oradata/clientWallet/${ORACLE_SID}/tnsnames.ora" diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 407223fa..8f13ebe1 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -53,7 +53,11 @@ spec: forceLog: false ## Enable TCPS - enableTCPS: false + enableTCPS: + + ## User specified TLS-Cert Secret + ## The following specified TLS certs will be used instead of self-signed + tcpsTlsSecret: ## TCPS Certificate Renewal Interval: The time after which TCPS certificate will be renewed if TCPS connections are enabled. ## tcpsCertRenewInterval can be in hours(h), minutes(m) and seconds(s); e.g. 4380h, 8760h etc. diff --git a/config/samples/sidb/singleinstancedatabase_tcps.yaml b/config/samples/sidb/singleinstancedatabase_tcps.yaml index d11a0d2a..06389f96 100644 --- a/config/samples/sidb/singleinstancedatabase_tcps.yaml +++ b/config/samples/sidb/singleinstancedatabase_tcps.yaml @@ -34,7 +34,12 @@ spec: ## Enable TCPS enableTCPS: true - ## TCPS Certificate Renewal Interval: The time after which TCPS certificate will be renewed if TCPS connections are enabled. + ## User specified TLS-Cert Secret + ## The following specified TLS certs will be used instead of self-signed + tcpsTlsSecret: my-tls-secret + + ## TCPS Certificate Renewal Interval: (Valid for Self-Signed Certificates) + ## The time after which TCPS certificate will be renewed if TCPS connections are enabled. ## tcpsCertRenewInterval can be in hours(h), minutes(m) and seconds(s); e.g. 4380h, 8760h etc. ## Maximum value is 8760h (1 year), Minimum value is 24h; Default value is 8760h (1 year) ## If this field is commented out/removed from the yaml, it will disable the auto-renewal feature for TCPS certificate diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index e468214c..c394b821 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -661,6 +661,30 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }}, }, }, + }, { + Name: "tls-secret-vol", + VolumeSource: func() corev1.VolumeSource { + if m.Spec.TcpsTlsSecret == "" { + return corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}} + } + /* tls-secret is specified */ + return corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: m.Spec.TcpsTlsSecret, + Optional: func() *bool { i := true; return &i }(), + Items: []corev1.KeyToPath{ + { + Key: "tls.crt", // Mount the certificate + Path: "cert.crt", // Mount path inside the container + }, + { + Key: "tls.key", // Mount the private key + Path: "client.key", // Mount path inside the container + }, + }, + }, + } + }(), }}, InitContainers: func() []corev1.Container { initContainers := []corev1.Container{} @@ -797,6 +821,13 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns SubPath: "oracle_pwd", }) } + if m.Spec.TcpsTlsSecret != "" { + mounts = append(mounts, corev1.VolumeMount{ + MountPath: dbcommons.TlsCertsLocation, + ReadOnly: true, + Name: "tls-secret-vol", + }) + } return mounts }(), Env: func() []corev1.EnvVar { @@ -2191,16 +2222,33 @@ func (r *SingleInstanceDatabaseReconciler) updateClientWallet(m *dbapi.SingleIns func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDatabase, readyPod corev1.Pod, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { eventReason := "Configuring TCPS" - if m.Spec.EnableTCPS && !m.Status.IsTcpsEnabled { + + if (m.Spec.EnableTCPS) && + ((!m.Status.IsTcpsEnabled)|| // TCPS Enabled from a TCP state + (m.Spec.TcpsTlsSecret != "" && m.Status.TcpsTlsSecret == "") || // TCPS Secret is added in spec + (m.Spec.TcpsTlsSecret == "" && m.Status.TcpsTlsSecret != "") || // TCPS Secret is removed in spec + (m.Spec.TcpsTlsSecret != "" && m.Status.TcpsTlsSecret != "" && m.Spec.TcpsTlsSecret != m.Status.TcpsTlsSecret)) { //TCPS secret is changed + // Enable TCPS m.Status.Status = dbcommons.StatusUpdating r.Status().Update(ctx, m) eventMsg := "Enabling TCPS in the database..." r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + + var TcpsCommand = dbcommons.EnableTcpsCMD + if m.Spec.TcpsTlsSecret != "" { // case when tls secret is either added or changed + TcpsCommand = "export TCPS_CERTS_LOCATION=" + dbcommons.TlsCertsLocation + " && " + dbcommons.EnableTcpsCMD + + // call deletePods() with zero pods in avaiable and nil readyPod to delete all pods + result, err := r.deletePods(ctx, req, m, []corev1.Pod{}, corev1.Pod{}, 0, 0) + if result.Requeue { + return result, err + } + } out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", - ctx, req, false, "bash", "-c", dbcommons.EnableTcpsCMD) + ctx, req, false, "bash", "-c", TcpsCommand) if err != nil { r.Log.Error(err, err.Error()) eventMsg = "Error encountered in enabling TCPS!" @@ -2212,6 +2260,14 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat m.Status.CertCreationTimestamp = time.Now().Format(time.RFC3339) m.Status.IsTcpsEnabled = true m.Status.ClientWalletLoc = fmt.Sprintf(dbcommons.ClientWalletLocation, m.Spec.Sid) + // m.Spec.TcpsTlsSecret can be empty or non-empty + // Store secret name in case of tls-secret addition or change, otherwise would be "" + if m.Spec.TcpsTlsSecret != "" { + m.Status.TcpsTlsSecret = m.Spec.TcpsTlsSecret + } else{ + m.Status.TcpsTlsSecret = "" + } + r.Status().Update(ctx, m) eventMsg = "TCPS Enabled." @@ -2246,6 +2302,8 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat m.Status.CertCreationTimestamp = "" m.Status.IsTcpsEnabled = false m.Status.ClientWalletLoc = "" + m.Status.TcpsTlsSecret = "" + r.Status().Update(ctx, m) eventMsg = "TCPS Disabled." diff --git a/docs/sidb/README.md b/docs/sidb/README.md index eb5116a4..c606b111 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -540,39 +540,49 @@ $ kubectl --type=merge -p '{"spec":{"loadBalancer": true}}' patch singleinstance ``` ### Enabling TCPS Connections -You can enable TCPS connections in the database by setting the `enableTCPS` field to `true` in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and applying it using `kubectl apply` command. +You can enable TCPS connections in the database by setting the `enableTCPS` field to `true` in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and applying it. Alternatively, you can use the following command: ```bash kubectl patch --type=merge singleinstancedatabases.database.oracle.com sidb-sample -p '{"spec": {"enableTCPS": true}}' ``` -Once TCPS connections are enabled, the database connect string will change accordingly. The TCPS connections status can also be queried by the following command: +By default self signed certs are used for TCPS connections. The TCPS connections status can also be queried by the following command: ```bash kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.isTcpsEnabled}" true ``` -The following steps are required to connect the Database using TCPS: -- You need to download the wallet from the Persistent Volume (PV) attached with the database pod. The location of the wallet inside the pod is as `/opt/oracle/oradata/clientWallet/$ORACLE_SID`. **Let us assume the `ORACLE_SID` is `ORCL1`, and singleinstance database resource name is `sidb-sample` for the upcoming example command**. You can copy the wallet to the destination directory by the following command: +**With Self Signed Certs** +- When TCPS is enabled, a self-signed certificate is generated and stored in wallets. For users' convenience, a client-side wallet is generated in location `/opt/oracle/oradata/clientWallet/$ORACLE_SID` in the pod. +- The self-signed certificate used with TCPS has validity for 1 year. After the certificate is expired, it will be renewed by the `OraOperator` automatically. Download the wallet again after auto-renewal. +- You can set the certificate renew interval with the help of `tcpsCertRenewInterval` field in the **[config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)** file. The minimum accepted value is 24h, and the maximum value is 8760h (1 year). The certificates used with TCPS will automatically be renewed after this interval. If this field is omitted/commented in the yaml file, the certificates will not be renewed automatically. +- When the certificate gets created/renewed, the `.status.certCreationTimestamp` status variable gets updated accordingly. You can see this timestamp by using the following command: ```bash - kubectl cp $(kubectl get pods -l app=sidb-sample -o=jsonpath='{.items[0].metadata.name}'):/opt/oracle/oradata/clientWallet/ORCL1 + kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.certCreationTimestamp}" ``` -- This wallet includes the sample `tnsnames.ora` and `sqlnet.ora` files. All the TNS entries for the database (corresponding to the CDB and PDB) resides in `tnsnames.ora` file. You need to go inside the downloaded wallet directory and set the `TNS_ADMIN` environment variable to point to the current directory as follows: + +**With User Provided Certs** +- Users can provide custom certs to be used for TCPS connections instead of self signed ones. +- Specify the certs by creating a Kubernetes tls secret resource using following command: ```bash - # After going inside the downloaded wallet directory - export TNS_ADMIN=$(pwd) + kubectl create secret tls my-tls-secret --cert=path/to/cert/tls.crt --key=path/to/key/tls.key ``` - After this, you can connect using SQL\*Plus using the following sample commands: +- `tls.crt` is a certificate chain in the order of client, followed by intermediate and then root certificate and `tls.key` is client key. +- Specify the secret created above (`my-tls-secret`) as the value for the attribute `tcpsTlsSecret` in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. + +**Connecting to the Database using TCPS** +- Download the wallet from the Persistent Volume (PV) attached with the database pod. The location of the wallet inside the pod is as `/opt/oracle/oradata/clientWallet/$ORACLE_SID`. Let us assume the `ORACLE_SID` is `ORCL1`, and singleinstance database resource name is `sidb-sample` for the upcoming example command. You can copy the wallet to the destination directory by the following command: ```bash - sqlplus sys@ORCL1 as sysdba + kubectl cp $(kubectl get pods -l app=sidb-sample -o=jsonpath='{.items[0].metadata.name}'):/opt/oracle/oradata/clientWallet/ORCL1 ``` -**Note:** -- When TCPS is enabled, a self-signed certificate is generated and stored inside the wallets. For users' convenience, a client-side wallet is generated and stored at `/opt/oracle/oradata/clientWallet/$ORACLE_SID` location in the pod. -- The self-signed certificate used with TCPS has validity for 1 year. After the certificate is expired, it will be renewed by the `OraOperator` automatically. You need to download the wallet again after the auto-renewal. -- You can set the certificate renew interval with the help of `tcpsCertRenewInterval` field in the **[config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)** file. The minimum accepted value is 24h, and the maximum value is 8760h (1 year). The certificates used with TCPS will automatically be renewed after this interval. If this field is omitted/commented in the yaml file, the certificates will not be renewed automatically. -- When the certificate gets created/renewed, the `.status.certCreationTimestamp` status variable gets updated accordingly. You can see this timestamp by using the following command: +- This wallet includes the sample `tnsnames.ora` and `sqlnet.ora` files. All the TNS entries for the database (corresponding to the CDB and PDB) reside in the `tnsnames.ora` file. Switch to the downloaded wallet directory and set the `TNS_ADMIN` environment variable to point to the current directory as follows: ```bash - kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.certCreationTimestamp}" + cd + export TNS_ADMIN=$(pwd) + ``` + After this, connect using SQL\*Plus using the following sample commands: + ```bash + sqlplus sys@ORCL1 as sysdba ``` ### Specifying Custom Ports From 1d6361e4a7c89701d37b612e316ed261b96da0e3 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Tue, 7 Nov 2023 11:23:03 +0000 Subject: [PATCH 009/414] Enhance status updation of sidb controller --- .../v1alpha1/singleinstancedatabase_types.go | 10 +- .../singleinstancedatabase_webhook.go | 113 +++++--- commons/database/constants.go | 9 +- commons/database/utils.go | 101 ++++++- .../database/dataguardbroker_controller.go | 10 +- .../singleinstancedatabase_controller.go | 256 +++++++++++------- 6 files changed, 341 insertions(+), 158 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index 03144530..75d2968f 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -64,9 +64,9 @@ type SingleInstanceDatabaseSpec struct { ListenerPort int `json:"listenerPort,omitempty"` TcpsListenerPort int `json:"tcpsListenerPort,omitempty"` ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` - FlashBack bool `json:"flashBack,omitempty"` - ArchiveLog bool `json:"archiveLog,omitempty"` - ForceLogging bool `json:"forceLog,omitempty"` + FlashBack *bool `json:"flashBack,omitempty"` + ArchiveLog *bool `json:"archiveLog,omitempty"` + ForceLogging *bool `json:"forceLog,omitempty"` EnableTCPS bool `json:"enableTCPS,omitempty"` TcpsCertRenewInterval string `json:"tcpsCertRenewInterval,omitempty"` TcpsTlsSecret string `json:"tcpsTlsSecret,omitempty"` @@ -85,7 +85,7 @@ type SingleInstanceDatabaseSpec struct { AdminPassword SingleInstanceDatabaseAdminPassword `json:"adminPassword,omitempty"` Image SingleInstanceDatabaseImage `json:"image"` Persistence SingleInstanceDatabasePersistence `json:"persistence,omitempty"` - InitParams SingleInstanceDatabaseInitParams `json:"initParams,omitempty"` + InitParams *SingleInstanceDatabaseInitParams `json:"initParams,omitempty"` } // SingleInstanceDatabasePersistence defines the storage size and class for PVC @@ -164,7 +164,7 @@ type SingleInstanceDatabaseStatus struct { PrimaryDatabase string `json:"primaryDatabase,omitempty"` DgBrokerConfigured bool `json:"dgBrokerConfigured,omitempty"` // +kubebuilder:default:="" - TcpsTlsSecret string `json:"tcpsTlsSecret"` + TcpsTlsSecret string `json:"tcpsTlsSecret"` // +patchMergeKey=type // +patchStrategy=merge diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index d70bb0c9..a3cb5b77 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -39,9 +39,9 @@ package v1alpha1 import ( - "strings" - "time" "strconv" + "strings" + "time" dbcommons "github.com/oracle/oracle-database-operator/commons/database" @@ -74,20 +74,20 @@ func (r *SingleInstanceDatabase) Default() { singleinstancedatabaselog.Info("default", "name", r.Name) if r.Spec.LoadBalancer { - // Annotations required for a flexible load balancer on oci + // Annotations required for a flexible load balancer on oci if r.Spec.ServiceAnnotations == nil { - r.Spec.ServiceAnnotations= make(map[string]string) + r.Spec.ServiceAnnotations = make(map[string]string) } _, ok := r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] - if(!ok) { + if !ok { r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] = "flexible" } - _,ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] - if(!ok) { + _, ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] + if !ok { r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] = "10" } - _,ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] - if(!ok) { + _, ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] + if !ok { r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] = "100" } } @@ -168,6 +168,29 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { } } + if r.Spec.CreateAsStandby { + if r.Spec.ArchiveLog != nil { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("archiveLog"), + r.Spec.ArchiveLog, "archiveLog cannot be specified for standby databases")) + } + if r.Spec.FlashBack != nil { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("flashBack"), + r.Spec.FlashBack, "flashBack cannot be specified for standby databases")) + } + if r.Spec.ForceLogging != nil { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("forceLog"), + r.Spec.ForceLogging, "forceLog cannot be specified for standby databases")) + } + if r.Spec.InitParams != nil { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("initParams"), + r.Spec.InitParams, "initParams cannot be specified for standby databases")) + } + } + // Replica validation if r.Spec.Replicas > 1 { valMsg := "" @@ -182,17 +205,17 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { field.Invalid(field.NewPath("spec").Child("replicas"), r.Spec.Replicas, valMsg)) } } - + if r.Spec.Edition == "express" || r.Spec.Edition == "free" { if r.Spec.CloneFrom != "" { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("cloneFrom"), r.Spec.CloneFrom, - "Cloning not supported for " + r.Spec.Edition + " edition")) + "Cloning not supported for "+r.Spec.Edition+" edition")) } if r.Spec.CreateAsStandby { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("createAsStandby"), r.Spec.CreateAsStandby, - "Physical Standby Database creation is not supported for " + r.Spec.Edition + " edition")) + "Physical Standby Database creation is not supported for "+r.Spec.Edition+" edition")) } if r.Spec.Edition == "express" && strings.ToUpper(r.Spec.Sid) != "XE" { allErrs = append(allErrs, @@ -217,22 +240,22 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { if r.Spec.InitParams.CpuCount != 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("initParams").Child("cpuCount"), r.Spec.InitParams.CpuCount, - r.Spec.Edition + " edition does not support changing init parameter cpuCount.")) + r.Spec.Edition+" edition does not support changing init parameter cpuCount.")) } if r.Spec.InitParams.Processes != 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("initParams").Child("processes"), r.Spec.InitParams.Processes, - r.Spec.Edition + " edition does not support changing init parameter process.")) + r.Spec.Edition+" edition does not support changing init parameter process.")) } if r.Spec.InitParams.SgaTarget != 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("initParams").Child("sgaTarget"), r.Spec.InitParams.SgaTarget, - r.Spec.Edition + " edition does not support changing init parameter sgaTarget.")) + r.Spec.Edition+" edition does not support changing init parameter sgaTarget.")) } if r.Spec.InitParams.PgaAggregateTarget != 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("initParams").Child("pgaAggregateTarget"), r.Spec.InitParams.PgaAggregateTarget, - r.Spec.Edition + " edition does not support changing init parameter pgaAggregateTarget.")) + r.Spec.Edition+" edition does not support changing init parameter pgaAggregateTarget.")) } } else { if r.Spec.Sid == "XE" { @@ -260,16 +283,16 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { } } - if r.Status.FlashBack == "true" && r.Spec.FlashBack { - if !r.Spec.ArchiveLog { + if r.Status.FlashBack == "true" && r.Spec.FlashBack != nil && *r.Spec.FlashBack { + if r.Spec.ArchiveLog != nil && !*r.Spec.ArchiveLog { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("archiveLog"), r.Spec.ArchiveLog, "Cannot disable Archivelog. Please disable Flashback first.")) } } - if r.Status.ArchiveLog == "false" && !r.Spec.ArchiveLog { - if r.Spec.FlashBack { + if r.Status.ArchiveLog == "false" && r.Spec.ArchiveLog != nil && !*r.Spec.ArchiveLog { + if *r.Spec.FlashBack { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("flashBack"), r.Spec.FlashBack, "Cannot enable Flashback. Please enable Archivelog first.")) @@ -306,6 +329,7 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { "listenerPort can not be 2484 as the default port for tcpsListenerPort is 2484.")) } } + if r.Spec.EnableTCPS && r.Spec.ListenerPort != 0 && r.Spec.TcpsListenerPort != 0 && r.Spec.ListenerPort == r.Spec.TcpsListenerPort { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("tcpsListenerPort"), r.Spec.TcpsListenerPort, @@ -341,6 +365,14 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { " is applicable only for self signed certs")) } + if r.Spec.InitParams != nil { + if (r.Spec.InitParams.PgaAggregateTarget != 0 && r.Spec.InitParams.SgaTarget == 0) || (r.Spec.InitParams.PgaAggregateTarget == 0 && r.Spec.InitParams.SgaTarget != 0) { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("initParams"), + r.Spec.InitParams, "initParams value invalid : Provide values for both pgaAggregateTarget and SgaTarget")) + } + } + if len(allErrs) == 0 { return nil } @@ -375,39 +407,42 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) return nil } - if (old.Status.Role != dbcommons.ValueUnavailable && old.Status.Role != "PRIMARY") { + if old.Status.Role != dbcommons.ValueUnavailable && old.Status.Role != "PRIMARY" { // Restriciting Patching of secondary databases archiveLog, forceLog, flashBack statusArchiveLog, _ := strconv.ParseBool(old.Status.ArchiveLog) - if statusArchiveLog != r.Spec.ArchiveLog { + if r.Spec.ArchiveLog != nil && (statusArchiveLog != *r.Spec.ArchiveLog) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("archiveLog"), "cannot be changed")) } statusFlashBack, _ := strconv.ParseBool(old.Status.FlashBack) - if statusFlashBack != r.Spec.FlashBack { + if r.Spec.FlashBack != nil && (statusFlashBack != *r.Spec.FlashBack) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("flashBack"), "cannot be changed")) } statusForceLogging, _ := strconv.ParseBool(old.Status.ForceLogging) - if statusForceLogging != r.Spec.ForceLogging { + if r.Spec.ForceLogging != nil && (statusForceLogging != *r.Spec.ForceLogging) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("forceLog"), "cannot be changed")) } + // Restriciting Patching of secondary databases InitParams - if old.Status.InitParams.SgaTarget != r.Spec.InitParams.SgaTarget { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("InitParams").Child("sgaTarget"), "cannot be changed")) - } - if old.Status.InitParams.PgaAggregateTarget != r.Spec.InitParams.PgaAggregateTarget { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("InitParams").Child("pgaAggregateTarget"), "cannot be changed")) - } - if old.Status.InitParams.CpuCount != r.Spec.InitParams.CpuCount { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("InitParams").Child("cpuCount"), "cannot be changed")) - } - if old.Status.InitParams.Processes != r.Spec.InitParams.Processes { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("InitParams").Child("processes"), "cannot be changed")) + if r.Spec.InitParams != nil { + if old.Status.InitParams.SgaTarget != r.Spec.InitParams.SgaTarget { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("initParams").Child("sgaTarget"), "cannot be changed")) + } + if old.Status.InitParams.PgaAggregateTarget != r.Spec.InitParams.PgaAggregateTarget { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("initParams").Child("pgaAggregateTarget"), "cannot be changed")) + } + if old.Status.InitParams.CpuCount != r.Spec.InitParams.CpuCount { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("initParams").Child("cpuCount"), "cannot be changed")) + } + if old.Status.InitParams.Processes != r.Spec.InitParams.Processes { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("initParams").Child("processes"), "cannot be changed")) + } } } diff --git a/commons/database/constants.go b/commons/database/constants.go index 00064e07..b6c71363 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -422,13 +422,16 @@ const InitWalletCMD string = "if [ ! -f $ORACLE_BASE/oradata/.${ORACLE_SID}${CHE const InitPrebuiltDbCMD string = "if [ ! -d /mnt/oradata/${ORACLE_SID} -a -d $ORACLE_BASE/oradata/${ORACLE_SID} ]; then cp -v $ORACLE_BASE/oradata/.${ORACLE_SID}$CHECKPOINT_FILE_EXTN /mnt/oradata && " + " cp -vr $ORACLE_BASE/oradata/${ORACLE_SID} /mnt/oradata && cp -vr $ORACLE_BASE/oradata/dbconfig /mnt/oradata; fi " -const AlterSgaPgaCpuCMD string = "echo -e \"alter system set sga_target=%dM scope=both; \n alter system set pga_aggregate_target=%dM scope=both; \n alter system set cpu_count=%d; \" | %s " - +const AlterSgaPgaCMD string = "echo -e \"alter system set sga_target=%dM scope=both; \n alter system set pga_aggregate_target=%dM scope=both; \" | %s " +const AlterCpuCountCMD string = "echo -e \"alter system set cpu_count=%d; \" | %s" const AlterProcessesCMD string = "echo -e \"alter system set processes=%d scope=spfile; \" | %s && " + CreateChkFileCMD + " && " + "echo -e \"SHUTDOWN IMMEDIATE; \n STARTUP MOUNT; \n ALTER DATABASE OPEN; \n ALTER PLUGGABLE DATABASE ALL OPEN; \n ALTER SYSTEM REGISTER;\" | %s && " + RemoveChkFileCMD -const GetInitParamsSQL string = "echo -e \"select name,display_value from v\\$parameter where name in ('sga_target','pga_aggregate_target','cpu_count','processes') order by name asc;\" | %s" +const GetInitParamsSQL string = "column name format a20;" + + "\ncolumn display_value format a20;" + + "\nset linesize 100 pagesize 50;" + + "\nselect name,display_value from v\\$parameter where name in ('sga_target','pga_aggregate_target','cpu_count','processes') order by name asc;" const UnzipApexOnSIDBPod string = "if [ -f /opt/oracle/oradata/apex-latest.zip ]; then unzip -o /opt/oracle/oradata/apex-latest.zip -d /opt/oracle/oradata/${ORACLE_SID^^}; else echo \"apex-latest.zip not found\"; fi;" diff --git a/commons/database/utils.go b/commons/database/utils.go index 6e6f6d02..ec95e768 100644 --- a/commons/database/utils.go +++ b/commons/database/utils.go @@ -45,6 +45,7 @@ import ( "errors" "fmt" "math/rand" + "strconv" "strings" "time" "unicode" @@ -400,6 +401,75 @@ func CheckDBConfig(readyPod corev1.Pod, r client.Reader, config *rest.Config, return flashBackStatus, archiveLogStatus, forceLoggingStatus, requeueN } +func CheckDBInitParams(sidbReadyPod corev1.Pod, r client.Reader, config *rest.Config, + ctx context.Context, req ctrl.Request) (int, int, int, int, error) { + log := ctrllog.FromContext(ctx).WithValues("CheckDBParams", req.NamespacedName) + + if sidbReadyPod.Name == "" { + log.Info("No Pod is Ready") + // As No pod is ready now , turn on mode when pod is ready . so requeue the request + return -1, -1, -1, -1, fmt.Errorf("no pod is ready") + } + + log.Info("Check database init params") + + out, err := ExecCommand(r, config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", + ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | sqlplus -s / as sysdba", GetInitParamsSQL)) + if err != nil { + log.Error(err, err.Error()) + return -1, -1, -1, -1, err + } + if strings.Contains(out, "no rows selected") { + return -1, -1, -1, -1, errors.New("cannot fetch values for database init params") + } + if strings.Contains(out, "ORA-") { + return -1, -1, -1, -1, fmt.Errorf("error while getting database init params\n%s", out) + } + log.Info(fmt.Sprintf("Database initParams are \n%s", out)) + initParams := strings.Split(out, "\n") + initParams = initParams[3:] + log.Info(fmt.Sprintf("%v", initParams)) + log.Info(fmt.Sprintf("length of initParams is %v", len(initParams))) + + log.Info("After parsing init param are " + strings.Join(initParams, ",")) + + log.Info("Parsing cpuCount") + log.Info(strings.Fields(initParams[0])[1]) + cpu_count, err := strconv.Atoi(strings.Fields(initParams[0])[1]) + if err != nil { + return -1, -1, -1, -1, err + } + log.Info("After parsing cpuCount", "cpuCount", cpu_count) + + log.Info("Parsing pga_aggregate_target_value") + log.Info(strings.Fields(initParams[1])[1]) + pga_aggregate_target_value := strings.Fields(initParams[1])[1] + pga_aggregate_target, err := strconv.Atoi(pga_aggregate_target_value[0 : len(pga_aggregate_target_value)-1]) + if err != nil { + return -1, -1, -1, -1, err + } + log.Info("After parsing pga_aggregate_target_value", "pga_aggregate_target_value", pga_aggregate_target) + + log.Info("Parsing processes") + log.Info(strings.Fields(initParams[2])[1]) + processes, err := strconv.Atoi(strings.Fields(initParams[2])[1]) + if err != nil { + return -1, -1, -1, -1, err + } + log.Info("After parsing processes", "processes", processes) + + log.Info("parsing sga_target_value") + log.Info(strings.Fields(initParams[3])[1]) + sga_target_value := strings.Fields(initParams[3])[1] + sga_target, err := strconv.Atoi(sga_target_value[0 : len(sga_target_value)-1]) + if err != nil { + return -1, -1, -1, -1, err + } + log.Info("After parsing sgaTarget", "sgaTarget", sga_target) + + return cpu_count, pga_aggregate_target, processes, sga_target, nil +} + // CHECKS IF SID IN DATABASES SLICE , AND ITS DGROLE func IsDatabaseFound(sid string, databases []string, dgrole string) (bool, bool) { found := false @@ -458,34 +528,37 @@ func GetDatabasesInDgConfig(readyPod corev1.Pod, r client.Reader, // Returns Database version func GetDatabaseVersion(readyPod corev1.Pod, r client.Reader, - config *rest.Config, ctx context.Context, req ctrl.Request, edition string) (string, string, error) { + config *rest.Config, ctx context.Context, req ctrl.Request, edition string) (string, error) { + log := ctrllog.FromContext(ctx).WithValues("GetDatabaseVersion", req.NamespacedName) // ## FIND DATABASES PRESENT IN DG CONFIGURATION out, err := ExecCommand(r, config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", GetVersionSQL, SQLPlusCLI)) if err != nil { - return "", "", err + return "", err } log.Info("GetDatabaseVersion Output") log.Info(out) - - if !strings.Contains(out, "no rows selected") && !strings.Contains(out, "ORA-") { - out1 := strings.Replace(out, " ", "_", -1) - // filtering output and storing databses in dg configuration in "databases" slice - out2 := strings.Fields(out1) - - // first 2 values in the slice will be column name(VERSION) and a seperator(--------------) . so the version would be out2[2] - version := out2[2] - return version, out, nil + if strings.Contains(out, "no rows selected") { + return "", errors.New("cannot fetch database version") + } + if strings.Contains(out, "ORA-") { + return "", errors.New("error while trying to get the database version " + out) } - return "", out, errors.New("database version is nil") + out1 := strings.Replace(out, " ", "_", -1) + // filtering output and storing databses in dg configuration in "databases" slice + out2 := strings.Fields(out1) + // first 2 values in the slice will be column name(VERSION) and a seperator(--------------) . so the version would be out2[2] + version := out2[2] + return version, nil } // Fetch role by quering the DB func GetDatabaseRole(readyPod corev1.Pod, r client.Reader, - config *rest.Config, ctx context.Context, req ctrl.Request, edition string) (string, error) { + config *rest.Config, ctx context.Context, req ctrl.Request) (string, error) { + log := ctrllog.FromContext(ctx).WithValues("GetDatabaseRole", req.NamespacedName) out, err := ExecCommand(r, config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", @@ -497,7 +570,7 @@ func GetDatabaseRole(readyPod corev1.Pod, r client.Reader, if !strings.Contains(out, "no rows selected") && !strings.Contains(out, "ORA-") { out = strings.Replace(out, " ", "_", -1) // filtering output and storing databse_role in "database_role" - databaseRole := strings.Fields(out)[2] + databaseRole := strings.ToUpper(strings.Fields(out)[2]) // first 2 values in the slice will be column name(DATABASE_ROLE) and a seperator(--------------) . return databaseRole, nil diff --git a/controllers/database/dataguardbroker_controller.go b/controllers/database/dataguardbroker_controller.go index 183a1445..95222efd 100644 --- a/controllers/database/dataguardbroker_controller.go +++ b/controllers/database/dataguardbroker_controller.go @@ -961,7 +961,7 @@ func (r *DataguardBrokerReconciler) SetAsPrimaryDatabase(sidbSid string, targetS if err != nil { return requeueN } - out, err := dbcommons.GetDatabaseRole(primaryReadyPod, r, r.Config, ctx, primaryReq, n.Spec.Edition) + out, err := dbcommons.GetDatabaseRole(primaryReadyPod, r, r.Config, ctx, primaryReq) if err == nil { standbyDatabase.Status.Role = strings.ToUpper(out) } @@ -974,7 +974,7 @@ func (r *DataguardBrokerReconciler) SetAsPrimaryDatabase(sidbSid string, targetS Name: n.Name, }, } - out, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, sidbReq, n.Spec.Edition) + out, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, sidbReq) if err == nil { n.Status.Role = strings.ToUpper(out) } @@ -989,7 +989,7 @@ func (r *DataguardBrokerReconciler) SetAsPrimaryDatabase(sidbSid string, targetS if err != nil { return requeueN } - out, err := dbcommons.GetDatabaseRole(targetReadyPod, r, r.Config, ctx, targetReq, n.Spec.Edition) + out, err := dbcommons.GetDatabaseRole(targetReadyPod, r, r.Config, ctx, targetReq) if err == nil { standbyDatabase.Status.Role = strings.ToUpper(out) } @@ -1002,7 +1002,7 @@ func (r *DataguardBrokerReconciler) SetAsPrimaryDatabase(sidbSid string, targetS Name: n.Name, }, } - out, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, sidbReq, n.Spec.Edition) + out, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, sidbReq) if err == nil { n.Status.Role = strings.ToUpper(out) } @@ -1126,7 +1126,7 @@ func (r *DataguardBrokerReconciler) cleanupDataguardBroker(req ctrl.Request, ctx } // Get its Role - out, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, req, singleInstanceDatabase.Spec.Edition) + out, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, req) if err != nil { log.Error(err, err.Error()) return requeueY, err diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index c394b821..f2702858 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -189,8 +189,7 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct } // Validate readiness - var readyPod corev1.Pod - result, readyPod, err = r.validateDBReadiness(singleInstanceDatabase, ctx, req) + result, readyPod, err := r.validateDBReadiness(singleInstanceDatabase, ctx, req) if result.Requeue { r.Log.Info("Reconcile queued") return result, nil @@ -207,7 +206,9 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct } } - if strings.ToUpper(singleInstanceDatabase.Status.Role) == "PRIMARY" { + sidbRole, err := dbcommons.GetDatabaseRole(readyPod, r, r.Config, ctx, req) + + if sidbRole == "PRIMARY" { // Update DB config result, err = r.updateDBConfig(singleInstanceDatabase, readyPod, ctx, req) @@ -284,8 +285,11 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct return requeueY, nil } - // update status to Ready after all operations succeed - singleInstanceDatabase.Status.Status = dbcommons.StatusReady + // updating singleinstancedatabase Status + err = r.updateSidbStatus(singleInstanceDatabase, readyPod, ctx, req) + if err != nil { + return requeueY, err + } r.updateORDSStatus(singleInstanceDatabase, ctx, req) completed = true @@ -494,7 +498,7 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab return requeueY, err } - if !n.Spec.ArchiveLog { + if !*n.Spec.ArchiveLog { m.Status.Status = dbcommons.StatusPending eventReason := "Source Database Check" eventMsg := "enable ArchiveLog for database " + n.Name @@ -669,16 +673,16 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns } /* tls-secret is specified */ return corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: m.Spec.TcpsTlsSecret, Optional: func() *bool { i := true; return &i }(), Items: []corev1.KeyToPath{ { - Key: "tls.crt", // Mount the certificate + Key: "tls.crt", // Mount the certificate Path: "cert.crt", // Mount path inside the container }, { - Key: "tls.key", // Mount the private key + Key: "tls.key", // Mount the private key Path: "client.key", // Mount path inside the container }, }, @@ -1008,7 +1012,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns { Name: "INIT_SGA_SIZE", Value: func() string { - if m.Spec.InitParams.SgaTarget > 0 && m.Spec.InitParams.PgaAggregateTarget > 0 { + if m.Spec.InitParams != nil && m.Spec.InitParams.SgaTarget > 0 && m.Spec.InitParams.PgaAggregateTarget > 0 { return strconv.Itoa(m.Spec.InitParams.SgaTarget) } return "" @@ -1017,7 +1021,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns { Name: "INIT_PGA_SIZE", Value: func() string { - if m.Spec.InitParams.SgaTarget > 0 && m.Spec.InitParams.PgaAggregateTarget > 0 { + if m.Spec.InitParams != nil && m.Spec.InitParams.SgaTarget > 0 && m.Spec.InitParams.PgaAggregateTarget > 0 { return strconv.Itoa(m.Spec.InitParams.SgaTarget) } return "" @@ -2066,30 +2070,11 @@ func (r *SingleInstanceDatabaseReconciler) validateDBReadiness(m *dbapi.SingleIn return requeueY, readyPod, errors.New("no pod is ready currently") } - available = append(available, readyPod) - podNamesFinal := dbcommons.GetPodNames(available) - r.Log.Info("Final "+m.Name+" Pods After Deleting (or) Adding Extra Pods ( Including The Ready Pod ) ", "Pod Names", podNamesFinal) - r.Log.Info(m.Name+" Replicas Available", "Count", len(podNamesFinal)) - r.Log.Info(m.Name+" Replicas Required", "Count", m.Spec.Replicas) - - eventReason := "Database Ready" - eventMsg := "database open on pod " + readyPod.Name + " scheduled on node " + readyPod.Status.HostIP - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - - m.Status.DatafilesCreated = "true" - - // DB is ready, fetch and update other info - out, err := dbcommons.GetDatabaseRole(readyPod, r, r.Config, ctx, req, m.Spec.Edition) - if err == nil { - m.Status.Role = strings.ToUpper(out) - } - version, out, err := dbcommons.GetDatabaseVersion(readyPod, r, r.Config, ctx, req, m.Spec.Edition) - if err == nil { - if !strings.Contains(out, "ORA-") { - m.Status.ReleaseUpdate = version - } + version, err := dbcommons.GetDatabaseVersion(readyPod, r, r.Config, ctx, req, m.Spec.Edition) + if err != nil { + return requeueY, readyPod, err } - dbMajorVersion, err := strconv.Atoi(strings.Split(m.Status.ReleaseUpdate, ".")[0]) + dbMajorVersion, err := strconv.Atoi(strings.Split(version, ".")[0]) if err != nil { r.Log.Error(err, err.Error()) return requeueY, readyPod, err @@ -2100,20 +2085,18 @@ func (r *SingleInstanceDatabaseReconciler) validateDBReadiness(m *dbapi.SingleIn errMsg := "the Oracle Database Free is only available from version 23c onwards" r.Recorder.Eventf(m, corev1.EventTypeWarning, "Spec Error", errMsg) m.Status.Status = dbcommons.StatusError - return requeueN, readyPod, errors.New(errMsg) - } - // Checking if OEM is supported in the provided Database version - if dbMajorVersion >= 23 { - m.Status.OemExpressUrl = dbcommons.ValueUnavailable - } else { - m.Status.OemExpressUrl = oemExpressUrl + return requeueY, readyPod, errors.New(errMsg) } - if strings.ToUpper(m.Status.Role) == "PRIMARY" && m.Status.DatafilesPatched != "true" { - eventReason := "Datapatch Pending" - eventMsg := "datapatch execution pending" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - } + available = append(available, readyPod) + podNamesFinal := dbcommons.GetPodNames(available) + r.Log.Info("Final "+m.Name+" Pods After Deleting (or) Adding Extra Pods ( Including The Ready Pod ) ", "Pod Names", podNamesFinal) + r.Log.Info(m.Name+" Replicas Available", "Count", len(podNamesFinal)) + r.Log.Info(m.Name+" Replicas Required", "Count", m.Spec.Replicas) + + eventReason := "Database Ready" + eventMsg := "database open on pod " + readyPod.Name + " scheduled on node " + readyPod.Status.HostIP + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) return requeueN, readyPod, nil @@ -2223,11 +2206,11 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat readyPod corev1.Pod, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { eventReason := "Configuring TCPS" - if (m.Spec.EnableTCPS) && - ((!m.Status.IsTcpsEnabled)|| // TCPS Enabled from a TCP state - (m.Spec.TcpsTlsSecret != "" && m.Status.TcpsTlsSecret == "") || // TCPS Secret is added in spec - (m.Spec.TcpsTlsSecret == "" && m.Status.TcpsTlsSecret != "") || // TCPS Secret is removed in spec - (m.Spec.TcpsTlsSecret != "" && m.Status.TcpsTlsSecret != "" && m.Spec.TcpsTlsSecret != m.Status.TcpsTlsSecret)) { //TCPS secret is changed + if (m.Spec.EnableTCPS) && + ((!m.Status.IsTcpsEnabled) || // TCPS Enabled from a TCP state + (m.Spec.TcpsTlsSecret != "" && m.Status.TcpsTlsSecret == "") || // TCPS Secret is added in spec + (m.Spec.TcpsTlsSecret == "" && m.Status.TcpsTlsSecret != "") || // TCPS Secret is removed in spec + (m.Spec.TcpsTlsSecret != "" && m.Status.TcpsTlsSecret != "" && m.Spec.TcpsTlsSecret != m.Status.TcpsTlsSecret)) { //TCPS secret is changed // Enable TCPS m.Status.Status = dbcommons.StatusUpdating @@ -2235,7 +2218,7 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat eventMsg := "Enabling TCPS in the database..." r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - + var TcpsCommand = dbcommons.EnableTcpsCMD if m.Spec.TcpsTlsSecret != "" { // case when tls secret is either added or changed TcpsCommand = "export TCPS_CERTS_LOCATION=" + dbcommons.TlsCertsLocation + " && " + dbcommons.EnableTcpsCMD @@ -2264,7 +2247,7 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat // Store secret name in case of tls-secret addition or change, otherwise would be "" if m.Spec.TcpsTlsSecret != "" { m.Status.TcpsTlsSecret = m.Spec.TcpsTlsSecret - } else{ + } else { m.Status.TcpsTlsSecret = "" } @@ -2303,7 +2286,7 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat m.Status.IsTcpsEnabled = false m.Status.ClientWalletLoc = "" m.Status.TcpsTlsSecret = "" - + r.Status().Update(ctx, m) eventMsg = "TCPS Disabled." @@ -2434,26 +2417,49 @@ func (r *SingleInstanceDatabaseReconciler) updateInitParameters(m *dbapi.SingleI readyPod corev1.Pod, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithValues("updateInitParameters", req.NamespacedName) - if m.Status.InitParams == m.Spec.InitParams { + if m.Spec.InitParams == nil { + return requeueN, nil + } + if m.Status.InitParams == *m.Spec.InitParams { return requeueN, nil } - out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", - ctx, req, false, "bash", "-c", fmt.Sprintf(dbcommons.AlterSgaPgaCpuCMD, m.Spec.InitParams.SgaTarget, - m.Spec.InitParams.PgaAggregateTarget, m.Spec.InitParams.CpuCount, dbcommons.SQLPlusCLI)) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err + if (m.Spec.InitParams.PgaAggregateTarget != 0 && (m.Spec.InitParams.PgaAggregateTarget != m.Status.InitParams.PgaAggregateTarget)) || (m.Spec.InitParams.SgaTarget != 0 && (m.Spec.InitParams.SgaTarget != m.Status.InitParams.SgaTarget)) { + log.Info("Executing alter sga pga command", "pga_size", m.Spec.InitParams.PgaAggregateTarget, "sga_size", m.Spec.InitParams.SgaTarget) + out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", + ctx, req, false, "bash", "-c", fmt.Sprintf(dbcommons.AlterSgaPgaCMD, m.Spec.InitParams.SgaTarget, + m.Spec.InitParams.PgaAggregateTarget, dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return requeueY, err + } + // Notify the user about unsucessfull init-parameter value change + if strings.Contains(out, "ORA-") { + eventReason := "Invalid init-param value" + eventMsg := "Unable to change the init-param as specified. Error log: \n" + out + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + } + log.Info("AlterSgaPgaCpuCMD Output:" + out) } - // Notify the user about unsucessfull init-parameter value change - if strings.Contains(out, "ORA-") { - eventReason := "Invalid init-param value" - eventMsg := "Unable to change the init-param as specified. Error log: \n" + out - r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + + if (m.Spec.InitParams.CpuCount != 0) && (m.Status.InitParams.CpuCount != m.Spec.InitParams.CpuCount) { + log.Info("Executing alter cpu count command", "cpuCount", m.Spec.InitParams.CpuCount) + out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, + "bash", "-c", fmt.Sprintf(dbcommons.AlterCpuCountCMD, m.Spec.InitParams.CpuCount, dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return requeueY, err + } + if strings.Contains(out, "ORA-") { + eventReason := "Invalid init-param value" + eventMsg := "Unable to change the init-param as specified. Error log: \n" + out + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + } + log.Info("AlterCpuCountCMD Output:" + out) } - log.Info("AlterSgaPgaCpuCMD Output:" + out) - if m.Status.InitParams.Processes != m.Spec.InitParams.Processes { + if (m.Spec.InitParams.Processes != 0) && (m.Status.InitParams.Processes != m.Spec.InitParams.Processes) { + log.Info("Executing alter processes command", "processes", m.Spec.InitParams.Processes) // Altering 'Processes' needs database to be restarted out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf(dbcommons.AlterProcessesCMD, m.Spec.InitParams.Processes, dbcommons.SQLPlusCLI, @@ -2465,15 +2471,6 @@ func (r *SingleInstanceDatabaseReconciler) updateInitParameters(m *dbapi.SingleI log.Info("AlterProcessesCMD Output:" + out) } - out, err = dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", - ctx, req, false, "bash", "-c", fmt.Sprintf(dbcommons.GetInitParamsSQL, dbcommons.SQLPlusCLI)) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err - } - log.Info("GetInitParamsSQL Output:" + out) - - m.Status.InitParams = m.Spec.InitParams return requeueN, nil } @@ -2503,19 +2500,12 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc m.Status.Status = dbcommons.StatusNotReady return result, nil } - m.Status.ArchiveLog = strconv.FormatBool(archiveLogStatus) - m.Status.ForceLogging = strconv.FormatBool(forceLoggingStatus) - m.Status.FlashBack = strconv.FormatBool(flashBackStatus) - - log.Info("Flashback", "Status :", flashBackStatus) - log.Info("ArchiveLog", "Status :", archiveLogStatus) - log.Info("ForceLog", "Status :", forceLoggingStatus) //################################################################################################# // TURNING FLASHBACK , ARCHIVELOG , FORCELOGGING TO TRUE //################################################################################################# - if m.Spec.ArchiveLog && !archiveLogStatus { + if m.Spec.ArchiveLog != nil && *m.Spec.ArchiveLog && !archiveLogStatus { out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", dbcommons.CreateDBRecoveryDestCMD) @@ -2546,7 +2536,7 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc } - if m.Spec.ForceLogging && !forceLoggingStatus { + if m.Spec.ForceLogging != nil && *m.Spec.ForceLogging && !forceLoggingStatus { out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.ForceLoggingTrueSQL, dbcommons.SQLPlusCLI)) if err != nil { @@ -2557,7 +2547,7 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc log.Info(out) } - if m.Spec.FlashBack && !flashBackStatus { + if m.Spec.FlashBack != nil && *m.Spec.FlashBack && !flashBackStatus { _, archiveLogStatus, _, result := dbcommons.CheckDBConfig(readyPod, r, r.Config, ctx, req, m.Spec.Edition) if result.Requeue { m.Status.Status = dbcommons.StatusNotReady @@ -2589,7 +2579,7 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc // TURNING FLASHBACK , ARCHIVELOG , FORCELOGGING TO FALSE //################################################################################################# - if !m.Spec.FlashBack && flashBackStatus { + if m.Spec.FlashBack != nil && !*m.Spec.FlashBack && flashBackStatus { out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.FlashBackFalseSQL, dbcommons.SQLPlusCLI)) if err != nil { @@ -2599,7 +2589,7 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc log.Info("FlashBackFalse Output") log.Info(out) } - if !m.Spec.ArchiveLog && archiveLogStatus { + if m.Spec.ArchiveLog != nil && !*m.Spec.ArchiveLog && archiveLogStatus { flashBackStatus, _, _, result := dbcommons.CheckDBConfig(readyPod, r, r.Config, ctx, req, m.Spec.Edition) if result.Requeue { m.Status.Status = dbcommons.StatusNotReady @@ -2627,7 +2617,7 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc changeArchiveLog = true } } - if !m.Spec.ForceLogging && forceLoggingStatus { + if m.Spec.ForceLogging != nil && !*m.Spec.ForceLogging && forceLoggingStatus { out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.ForceLoggingFalseSQL, dbcommons.SQLPlusCLI)) if err != nil { @@ -2672,13 +2662,95 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc m.Status.FlashBack = strconv.FormatBool(flashBackStatus) - if !changeArchiveLog && (flashBackStatus != m.Spec.FlashBack || - archiveLogStatus != m.Spec.ArchiveLog || forceLoggingStatus != m.Spec.ForceLogging) { + if !changeArchiveLog && ((m.Spec.FlashBack != nil && (flashBackStatus != *m.Spec.FlashBack)) || + (m.Spec.ArchiveLog != nil && (archiveLogStatus != *m.Spec.ArchiveLog)) || (m.Spec.ForceLogging != nil && (forceLoggingStatus != *m.Spec.ForceLogging))) { return requeueY, nil } return requeueN, nil } +// ############################################################################# +// +// # Update Single instance database resource status +// +// ############################################################################# +func (r *SingleInstanceDatabaseReconciler) updateSidbStatus(sidb *dbapi.SingleInstanceDatabase, sidbReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) error { + + log := r.Log.WithValues("updateSidbStatus", req.NamespacedName) + + flashBackStatus, archiveLogStatus, forceLoggingStatus, result := dbcommons.CheckDBConfig(sidbReadyPod, r, r.Config, ctx, req, sidb.Spec.Edition) + if result.Requeue { + sidb.Status.Status = dbcommons.StatusNotReady + return fmt.Errorf("could not check the database conifg of %s", sidb.Name) + } + + log.Info("flashBack", "Status :", flashBackStatus, "Reconcile Step : ", "updateSidbStatus") + log.Info("ArchiveLog", "Status :", archiveLogStatus, "Reconcile Step : ", "updateSidbStatus") + log.Info("forceLogging", "Status :", forceLoggingStatus, "Reconcile Step : ", "updateSidbStatus") + + sidb.Status.ArchiveLog = strconv.FormatBool(archiveLogStatus) + sidb.Status.ForceLogging = strconv.FormatBool(forceLoggingStatus) + sidb.Status.FlashBack = strconv.FormatBool(flashBackStatus) + + cpu_count, pga_aggregate_target, processes, sga_target, err := dbcommons.CheckDBInitParams(sidbReadyPod, r, r.Config, ctx, req) + if err != nil { + return err + } + sidbInitParams := dbapi.SingleInstanceDatabaseInitParams{ + SgaTarget: sga_target, + PgaAggregateTarget: pga_aggregate_target, + Processes: processes, + CpuCount: cpu_count, + } + // log.Info("GetInitParamsSQL Output:" + out) + + sidb.Status.InitParams = sidbInitParams + // sidb.Status.InitParams = sidb.Spec.InitParams + + // Get database role and update the status + sidbRole, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, req) + if err != nil { + return err + } + log.Info("Database "+sidb.Name, "Database Role : ", sidbRole) + sidb.Status.Role = sidbRole + + // Get database version and update the status + version, err := dbcommons.GetDatabaseVersion(sidbReadyPod, r, r.Config, ctx, req, sidb.Spec.Edition) + if err != nil { + return err + } + log.Info("Database "+sidb.Name, "Database Version : ", version) + sidb.Status.ReleaseUpdate = version + + dbMajorVersion, err := strconv.Atoi(strings.Split(sidb.Status.ReleaseUpdate, ".")[0]) + if err != nil { + r.Log.Error(err, err.Error()) + return err + } + log.Info("Database "+sidb.Name, "Database Major Version : ", dbMajorVersion) + + // Checking if OEM is supported in the provided Database version + if dbMajorVersion >= 23 { + sidb.Status.OemExpressUrl = dbcommons.ValueUnavailable + } else { + sidb.Status.OemExpressUrl = oemExpressUrl + } + + if sidb.Status.Role == "PRIMARY" && sidb.Status.DatafilesPatched != "true" { + eventReason := "Datapatch Pending" + eventMsg := "datapatch execution pending" + r.Recorder.Eventf(sidb, corev1.EventTypeNormal, eventReason, eventMsg) + } + + // update status to Ready after all operations succeed + sidb.Status.Status = dbcommons.StatusReady + + r.Status().Update(ctx, sidb) + + return nil +} + // ############################################################################# // // Update ORDS Status From 75e6c7ff23ef8d3b67175b8208720fa6affac7a8 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Fri, 10 Nov 2023 13:29:09 +0000 Subject: [PATCH 010/414] Modifying singleinstancedatabase types attributes, for bug 35430671 resolution --- .../v1alpha1/singleinstancedatabase_types.go | 8 +- .../singleinstancedatabase_webhook.go | 53 ++- commons/database/constants.go | 2 - commons/database/utils.go | 3 +- .../samples/sidb/singleinstancedatabase.yaml | 15 +- .../sidb/singleinstancedatabase_clone.yaml | 5 +- .../sidb/singleinstancedatabase_standby.yaml | 4 +- .../singleinstancedatabase_controller.go | 359 ++++++++++++------ docs/sidb/README.md | 4 +- 9 files changed, 295 insertions(+), 158 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index 75d2968f..ab0d1913 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -72,9 +72,9 @@ type SingleInstanceDatabaseSpec struct { TcpsTlsSecret string `json:"tcpsTlsSecret,omitempty"` DgBrokerConfigured bool `json:"dgBrokerConfigured,omitempty"` - CloneFrom string `json:"cloneFrom,omitempty"` - PrimaryDatabaseRef string `json:"primaryDatabaseRef,omitempty"` - CreateAsStandby bool `json:"createAsStandby,omitempty"` + PrimaryDatabaseRef string `json:"primaryDatabaseRef,omitempty"` + // +kubebuilder:validation:Enum=primary;standby;clone + CreateAs string `json:"createAs,omitempty"` ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` ServiceAccountName string `json:"serviceAccountName,omitempty"` @@ -146,7 +146,7 @@ type SingleInstanceDatabaseStatus struct { Pdbname string `json:"pdbName,omitempty"` InitSgaSize int `json:"initSgaSize,omitempty"` InitPgaSize int `json:"initPgaSize,omitempty"` - CloneFrom string `json:"cloneFrom,omitempty"` + CreatedAs string `json:"createdAs,omitempty"` FlashBack string `json:"flashBack,omitempty"` ArchiveLog string `json:"archiveLog,omitempty"` ForceLogging string `json:"forceLog,omitempty"` diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index a3cb5b77..4e7bb1be 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -98,11 +98,15 @@ func (r *SingleInstanceDatabase) Default() { } if r.Spec.Edition == "" { - if r.Spec.CloneFrom == "" && !r.Spec.Image.PrebuiltDB { + if r.Spec.CreateAs == "clone" && !r.Spec.Image.PrebuiltDB { r.Spec.Edition = "enterprise" } } + if r.Spec.CreateAs == "" { + r.Spec.CreateAs = "primary" + } + if r.Spec.Sid == "" { if r.Spec.Edition == "express" { r.Spec.Sid = "XE" @@ -130,7 +134,7 @@ func (r *SingleInstanceDatabase) Default() { } } - if r.Spec.PrimaryDatabaseRef != "" && r.Spec.CreateAsStandby { + if r.Spec.PrimaryDatabaseRef != "" && r.Spec.CreateAs == "standby" { if r.Spec.Replicas == 0 { r.Spec.Replicas = 1 } @@ -168,7 +172,7 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { } } - if r.Spec.CreateAsStandby { + if r.Spec.CreateAs == "standby" { if r.Spec.ArchiveLog != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("archiveLog"), @@ -206,15 +210,20 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { } } + if (r.Spec.CreateAs == "clone" || r.Spec.CreateAs == "standby") && r.Spec.PrimaryDatabaseRef == "" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("primaryDatabaseRef"), r.Spec.PrimaryDatabaseRef, "Primary Database reference cannot be null for a secondary database")) + } + if r.Spec.Edition == "express" || r.Spec.Edition == "free" { - if r.Spec.CloneFrom != "" { + if r.Spec.CreateAs == "clone" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("cloneFrom"), r.Spec.CloneFrom, + field.Invalid(field.NewPath("spec").Child("createAs"), r.Spec.CreateAs, "Cloning not supported for "+r.Spec.Edition+" edition")) } - if r.Spec.CreateAsStandby { + if r.Spec.CreateAs == "standby" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("createAsStandby"), r.Spec.CreateAsStandby, + field.Invalid(field.NewPath("spec").Child("createAs"), r.Spec.CreateAs, "Physical Standby Database creation is not supported for "+r.Spec.Edition+" edition")) } if r.Spec.Edition == "express" && strings.ToUpper(r.Spec.Sid) != "XE" { @@ -270,15 +279,15 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { } } - if r.Spec.CloneFrom != "" { + if r.Spec.CreateAs != "clone" { if r.Spec.Image.PrebuiltDB { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("cloneFrom"), r.Spec.CloneFrom, + field.Invalid(field.NewPath("spec").Child("createAs"), r.Spec.CreateAs, "cannot clone to create a prebuilt db")) - } else if strings.Contains(r.Spec.CloneFrom, ":") && strings.Contains(r.Spec.CloneFrom, "/") && r.Spec.Edition == "" { + } else if strings.Contains(r.Spec.PrimaryDatabaseRef, ":") && strings.Contains(r.Spec.PrimaryDatabaseRef, "/") && r.Spec.Edition == "" { //Edition must be passed when cloning from a source database other than same k8s cluster allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("edition"), r.Spec.CloneFrom, + field.Invalid(field.NewPath("spec").Child("edition"), r.Spec.CreateAs, "Edition must be passed when cloning from a source database other than same k8s cluster")) } } @@ -407,6 +416,18 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) return nil } + if old.Status.CreatedAs == "clone" { + if r.Spec.Edition != "" && old.Status.Edition != "" && !strings.EqualFold(old.Status.Edition, r.Spec.Edition) { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("edition"), "Edition of a cloned singleinstancedatabase cannot be changed post creation")) + } + + if !strings.EqualFold(old.Status.PrimaryDatabase, r.Spec.PrimaryDatabaseRef) { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("primaryDatabaseRef"), "Primary database of a cloned singleinstancedatabase cannot be changed post creation")) + } + } + if old.Status.Role != dbcommons.ValueUnavailable && old.Status.Role != "PRIMARY" { // Restriciting Patching of secondary databases archiveLog, forceLog, flashBack statusArchiveLog, _ := strconv.ParseBool(old.Status.ArchiveLog) @@ -456,7 +477,7 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("image").Child("prebuiltDB"), "cannot be changed")) } - if r.Spec.CloneFrom == "" && old.Status.Edition != "" && !strings.EqualFold(old.Status.Edition, r.Spec.Edition) { + if r.Spec.Edition != "" && old.Status.Edition != "" && !strings.EqualFold(old.Status.Edition, r.Spec.Edition) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("edition"), "cannot be changed")) } @@ -472,11 +493,11 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("pdbname"), "cannot be changed")) } - if old.Status.CloneFrom != "" && - (old.Status.CloneFrom == dbcommons.NoCloneRef && r.Spec.CloneFrom != "" || - old.Status.CloneFrom != dbcommons.NoCloneRef && old.Status.CloneFrom != r.Spec.CloneFrom) { + if old.Status.CreatedAs == "clone" && + (old.Status.PrimaryDatabase == dbcommons.ValueUnavailable && r.Spec.PrimaryDatabaseRef != "" || + old.Status.PrimaryDatabase != dbcommons.ValueUnavailable && old.Status.PrimaryDatabase != r.Spec.PrimaryDatabaseRef) { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("cloneFrom"), "cannot be changed")) + field.Forbidden(field.NewPath("spec").Child("primaryDatabaseRef"), "cannot be changed")) } if old.Status.OrdsReference != "" && r.Status.Persistence != r.Spec.Persistence { allErrs = append(allErrs, diff --git a/commons/database/constants.go b/commons/database/constants.go index b6c71363..d655574f 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -50,8 +50,6 @@ const DBA_GUID int64 = 54322 const SQLPlusCLI string = "sqlplus -s / as sysdba" -const NoCloneRef string = "Unavailable" - const GetVersionSQL string = "SELECT VERSION_FULL FROM V\\$INSTANCE;" const CheckModesSQL string = "SELECT 'log_mode:' || log_mode AS log_mode ,'flashback_on:' || flashback_on AS flashback_on ,'force_logging:' || force_logging AS force_logging FROM v\\$database;" diff --git a/commons/database/utils.go b/commons/database/utils.go index ec95e768..eedeeeb4 100644 --- a/commons/database/utils.go +++ b/commons/database/utils.go @@ -430,7 +430,6 @@ func CheckDBInitParams(sidbReadyPod corev1.Pod, r client.Reader, config *rest.Co initParams = initParams[3:] log.Info(fmt.Sprintf("%v", initParams)) log.Info(fmt.Sprintf("length of initParams is %v", len(initParams))) - log.Info("After parsing init param are " + strings.Join(initParams, ",")) log.Info("Parsing cpuCount") @@ -528,7 +527,7 @@ func GetDatabasesInDgConfig(readyPod corev1.Pod, r client.Reader, // Returns Database version func GetDatabaseVersion(readyPod corev1.Pod, r client.Reader, - config *rest.Config, ctx context.Context, req ctrl.Request, edition string) (string, error) { + config *rest.Config, ctx context.Context, req ctrl.Request) (string, error) { log := ctrllog.FromContext(ctx).WithValues("GetDatabaseVersion", req.NamespacedName) diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 8f13ebe1..8d1a1cac 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -11,20 +11,17 @@ spec: ## Use only alphanumeric characters for sid up to a maximum of 8 characters sid: ORCL1 - - ## Specify a source database ref to copy/clone from any SIDB in current K8s cluster instead of creating a fresh one - ## If cloning from an external containerized DB which could be either standalone or in any K8s cluster, - ## specify connect string as `:/` instead of source database ref - cloneFrom: "" - ## Reference to a source primary database from which + ## Type of database. + ## Valid values for createAs are primary, standby or clone + createAs: primary + + ## Reference to a source primary database. + ## Valid for standby and clone databases. ## Format: 1. Intra-cluster: you can give name of the primary database or the database connect string in `:/` format ## 2. Inter-cluster: Database connect string in `:/` format primaryDatabaseRef: "" - ## Enable this flag for creating Physical Standby Database - createAsStandby: false - ## DB edition. N/A if cloning from a Source DB in current K8s cluster (if cloneFrom is set to a database ref) ## Valid values for edition are enterprise, standard or express edition: enterprise diff --git a/config/samples/sidb/singleinstancedatabase_clone.yaml b/config/samples/sidb/singleinstancedatabase_clone.yaml index c0e1eace..9b60e4ba 100644 --- a/config/samples/sidb/singleinstancedatabase_clone.yaml +++ b/config/samples/sidb/singleinstancedatabase_clone.yaml @@ -15,7 +15,10 @@ spec: ## A source database ref to clone from. ## Make sure the source database has been created by applying singeinstancedatabase_create.yaml - cloneFrom: sidb-sample + primaryDatabaseRef: sidb-sample + + ## Intended type of database. + createAs: clone ## Should refer to SourceDB secret ## Secret containing SIDB password mapped to secretKey diff --git a/config/samples/sidb/singleinstancedatabase_standby.yaml b/config/samples/sidb/singleinstancedatabase_standby.yaml index e93cc5e4..d5f2c04a 100644 --- a/config/samples/sidb/singleinstancedatabase_standby.yaml +++ b/config/samples/sidb/singleinstancedatabase_standby.yaml @@ -18,8 +18,8 @@ spec: ## Reference to a source primary database name primaryDatabaseRef: "sidb-sample" - ## Enable this flag for creating Physical Standby Database - createAsStandby: true + ## Intended type of database. + createAs: standby ## Secret containing SIDB password mapped to secretKey adminPassword: diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index f2702858..253ab1e8 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -412,26 +412,12 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab if m.Status.Sid != "" && !strings.EqualFold(m.Spec.Sid, m.Status.Sid) { eventMsgs = append(eventMsgs, "sid cannot be updated") } - if m.Spec.CloneFrom == "" && m.Spec.Edition != "" && m.Status.Edition != "" && !strings.EqualFold(m.Status.Edition, m.Spec.Edition) { - eventMsgs = append(eventMsgs, "edition cannot be updated") - } if m.Status.Charset != "" && !strings.EqualFold(m.Status.Charset, m.Spec.Charset) { eventMsgs = append(eventMsgs, "charset cannot be updated") } if m.Status.Pdbname != "" && !strings.EqualFold(m.Status.Pdbname, m.Spec.Pdbname) { eventMsgs = append(eventMsgs, "pdbName cannot be updated") } - if m.Status.CloneFrom != "" && - (m.Status.CloneFrom == dbcommons.NoCloneRef && m.Spec.CloneFrom != "" || - m.Status.CloneFrom != dbcommons.NoCloneRef && m.Status.CloneFrom != m.Spec.CloneFrom) { - eventMsgs = append(eventMsgs, "cloneFrom cannot be updated") - } - if (m.Spec.Edition == "express" || m.Spec.Edition == "free") && m.Spec.CloneFrom != "" { - eventMsgs = append(eventMsgs, "cloning not supported for "+m.Spec.Edition+" edition") - } - if (m.Spec.Edition == "express" || m.Spec.Edition == "free") && m.Spec.PrimaryDatabaseRef != "" && m.Spec.CreateAsStandby { - eventMsgs = append(eventMsgs, "Standby database creation is not supported for "+m.Spec.Edition+" edition") - } if m.Status.OrdsReference != "" && m.Status.Persistence.Size != "" && m.Status.Persistence != m.Spec.Persistence { eventMsgs = append(eventMsgs, "uninstall ORDS to change Peristence") } @@ -466,20 +452,16 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab m.Status.Pdbname = m.Spec.Pdbname m.Status.Persistence = m.Spec.Persistence m.Status.PrebuiltDB = m.Spec.Image.PrebuiltDB - if m.Spec.CloneFrom == "" { - m.Status.CloneFrom = dbcommons.NoCloneRef - } else { - m.Status.CloneFrom = m.Spec.CloneFrom - } - if m.Spec.CloneFrom != "" { + + if m.Spec.CreateAs == "clone" { // Once a clone database has created , it has no link with its reference if m.Status.DatafilesCreated == "true" || - !dbcommons.IsSourceDatabaseOnCluster(m.Spec.CloneFrom) { + !dbcommons.IsSourceDatabaseOnCluster(m.Spec.PrimaryDatabaseRef) { return requeueN, nil } // Fetch the Clone database reference - err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: m.Spec.CloneFrom}, n) + err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: m.Spec.PrimaryDatabaseRef}, n) if err != nil { if apierrors.IsNotFound(err) { r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, err.Error()) @@ -492,7 +474,7 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab if n.Status.Status != dbcommons.StatusReady { m.Status.Status = dbcommons.StatusPending eventReason := "Source Database Pending" - eventMsg := "status of database " + m.Spec.CloneFrom + " is not ready, retrying..." + eventMsg := "status of database " + n.Name + " is not ready, retrying..." r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) err = errors.New(eventMsg) return requeueY, err @@ -509,10 +491,10 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab } m.Status.Edition = n.Status.Edition - + m.Status.PrimaryDatabase = n.Name } - if m.Spec.PrimaryDatabaseRef != "" && m.Spec.CreateAsStandby { + if m.Spec.CreateAs == "standby" { // Fetch the Primary database reference, required for all iterations err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: m.Spec.PrimaryDatabaseRef}, rp) @@ -744,19 +726,19 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, { Name: "WALLET_DIR", - Value: "/opt/oracle/oradata/dbconfig/$(ORACLE_SID)/.wallet", + Value: "/opt/oracle/oradata/dbconfig/${ORACLE_SID}/.wallet", }, }, Command: []string{"/bin/sh"}, Args: func() []string { edition := "" - if m.Spec.CloneFrom == "" { + if m.Spec.CreateAs != "clone" { edition = m.Spec.Edition if m.Spec.Edition == "" { edition = "enterprise" } } else { - if !dbcommons.IsSourceDatabaseOnCluster(m.Spec.CloneFrom) { + if !dbcommons.IsSourceDatabaseOnCluster(m.Spec.PrimaryDatabaseRef) { edition = m.Spec.Edition } else { edition = n.Spec.Edition @@ -791,22 +773,41 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, Ports: []corev1.ContainerPort{{ContainerPort: dbcommons.CONTAINER_LISTENER_PORT}, {ContainerPort: 5500}}, - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"/bin/sh", "-c", "if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi "}, - }, - }, - InitialDelaySeconds: 20, - TimeoutSeconds: 20, - PeriodSeconds: func() int32 { - if m.Spec.ReadinessCheckPeriod > 0 { - return int32(m.Spec.ReadinessCheckPeriod) + ReadinessProbe: func() *corev1.Probe { + if m.Spec.CreateAs == "primary" { + return &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"/bin/sh", "-c", "if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi "}, + }, + }, + InitialDelaySeconds: 20, + TimeoutSeconds: 20, + PeriodSeconds: func() int32 { + if m.Spec.ReadinessCheckPeriod > 0 { + return int32(m.Spec.ReadinessCheckPeriod) + } + return 60 + }(), } - return 60 - }(), - }, - + } else { + return &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"/bin/sh", "-c", "if [ -f $ORACLE_BASE/oradata/.$ORACLE_SID$CHECKPOINT_FILE_EXTN ]; then if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi else true; fi "}, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 20, + PeriodSeconds: func() int32 { + if m.Spec.ReadinessCheckPeriod > 0 { + return int32(m.Spec.ReadinessCheckPeriod) + } + return 60 + }(), + } + } + }(), VolumeMounts: func() []corev1.VolumeMount { mounts := []corev1.VolumeMount{} if m.Spec.Persistence.Size != "" { @@ -816,8 +817,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }) } if m.Spec.Edition == "express" || m.Spec.Edition == "free" || m.Spec.Image.PrebuiltDB { - // mounts pwd as secrets for express edition - // or prebuilt db + // mounts pwd as secrets for express edition or prebuilt db mounts = append(mounts, corev1.VolumeMount{ MountPath: "/run/secrets/oracle_pwd", ReadOnly: true, @@ -856,7 +856,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, } } - if m.Spec.CloneFrom != "" { + if m.Spec.CreateAs == "clone" { // Clone DB use-case return []corev1.EnvVar{ { @@ -873,25 +873,18 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, { Name: "WALLET_DIR", - Value: "/opt/oracle/oradata/dbconfig/$(ORACLE_SID)/.wallet", + Value: "/opt/oracle/oradata/dbconfig/${ORACLE_SID}/.wallet", }, { Name: "PRIMARY_DB_CONN_STR", Value: func() string { - if dbcommons.IsSourceDatabaseOnCluster(m.Spec.CloneFrom) { + if dbcommons.IsSourceDatabaseOnCluster(m.Spec.PrimaryDatabaseRef) { return n.Name + ":" + strconv.Itoa(int(dbcommons.CONTAINER_LISTENER_PORT)) + "/" + n.Spec.Sid } - return m.Spec.CloneFrom + return m.Spec.PrimaryDatabaseRef }(), }, - { - Name: "ORACLE_HOSTNAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, - }, + CreateOracleHostnameEnvVarObj(m, n), { Name: "CLONE_DB", Value: "true", @@ -902,7 +895,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, } - } else if m.Spec.PrimaryDatabaseRef != "" && m.Spec.CreateAsStandby { + } else if m.Spec.CreateAs == "standby" { //Standby DB Usecase return []corev1.EnvVar{ { @@ -919,7 +912,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, { Name: "WALLET_DIR", - Value: "/opt/oracle/oradata/dbconfig/$(ORACLE_SID)/.wallet", + Value: "/opt/oracle/oradata/dbconfig/${ORACLE_SID}/.wallet", }, { Name: "PRIMARY_DB_CONN_STR", @@ -994,7 +987,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns if m.Spec.Image.PrebuiltDB { return "" // No wallets for prebuilt DB } - return "/opt/oracle/oradata/dbconfig/$(ORACLE_SID)/.wallet" + return "/opt/oracle/oradata/dbconfig/${ORACLE_SID}/.wallet" }(), }, { @@ -1071,7 +1064,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns } // Adding pod anti-affinity for standby cases - if m.Spec.PrimaryDatabaseRef != "" && m.Spec.CreateAsStandby { + if m.Spec.CreateAs == "standby" { weightedPodAffinityTerm := corev1.WeightedPodAffinityTerm{ Weight: 100, PodAffinityTerm: corev1.PodAffinityTerm{ @@ -1835,7 +1828,7 @@ func (r *SingleInstanceDatabaseReconciler) createWallet(m *dbapi.SingleInstanceD return requeueY, nil } - if m.Spec.CloneFrom == "" && m.Spec.Edition != "express" { + if m.Spec.CreateAs != "clone" && m.Spec.Edition != "express" { //Check if Edition of m.Spec.Sid is same as m.Spec.Edition getEditionFile := dbcommons.GetEnterpriseEditionFileCMD eventReason := m.Spec.Sid + " is a enterprise edition" @@ -2019,86 +2012,96 @@ func (r *SingleInstanceDatabaseReconciler) deletePods(ctx context.Context, req c // ValidateDBReadiness and return the ready POD // // ############################################################################# -func (r *SingleInstanceDatabaseReconciler) validateDBReadiness(m *dbapi.SingleInstanceDatabase, +func (r *SingleInstanceDatabaseReconciler) validateDBReadiness(sidb *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (ctrl.Result, corev1.Pod, error) { - readyPod, _, available, _, err := dbcommons.FindPods(r, m.Spec.Image.Version, - m.Spec.Image.PullFrom, m.Name, m.Namespace, ctx, req) + log := r.Log.WithValues("validateDBReadiness", req.NamespacedName) + + log.Info("Validating readiness for database") + + sidbReadyPod, _, available, _, err := dbcommons.FindPods(r, sidb.Spec.Image.Version, + sidb.Spec.Image.PullFrom, sidb.Name, sidb.Namespace, ctx, req) if err != nil { r.Log.Error(err, err.Error()) - return requeueY, readyPod, err + return requeueY, sidbReadyPod, err } - if readyPod.Name == "" { - m.Status.Status = dbcommons.StatusPending + + if sidbReadyPod.Name == "" { + sidb.Status.Status = dbcommons.StatusPending + log.Info("no pod currently in ready state") if ok, _ := dbcommons.IsAnyPodWithStatus(available, corev1.PodFailed); ok { eventReason := "Database Failed" eventMsg := "pod creation failed" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - } else if ok, runningPod := dbcommons.IsAnyPodWithStatus(available, corev1.PodRunning); ok { - r.Log.Info("Database Creating...", "Name", m.Name) - m.Status.Status = dbcommons.StatusCreating - if m.Spec.CloneFrom != "" { - // Required since clone creates the datafiles under primary database SID folder - r.Log.Info("Creating the SID directory link for clone database", "name", m.Spec.Sid) - _, err := dbcommons.ExecCommand(r, r.Config, runningPod.Name, runningPod.Namespace, "", - ctx, req, false, "bash", "-c", dbcommons.CreateSIDlinkCMD) - if err != nil { - r.Log.Info(err.Error()) - } - } - out, err := dbcommons.ExecCommand(r, r.Config, runningPod.Name, runningPod.Namespace, "", - ctx, req, false, "bash", "-c", dbcommons.GetCheckpointFileCMD) - if err != nil { - r.Log.Info(err.Error()) - } - r.Log.Info("GetCheckpointFileCMD Output : \n" + out) - - if out != "" { - eventReason := "Database Unhealthy" - eventMsg := "datafiles exists" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - m.Status.DatafilesCreated = "true" - m.Status.Status = dbcommons.StatusNotReady - r.updateORDSStatus(m, ctx, req) - } - + r.Recorder.Eventf(sidb, corev1.EventTypeNormal, eventReason, eventMsg) + } else if ok, _ := dbcommons.IsAnyPodWithStatus(available, corev1.PodRunning); ok { + log.Info("Database Creating....", "Name", sidb.Name) + sidb.Status.Status = dbcommons.StatusCreating } else { - r.Log.Info("Database Pending...", "Name", m.Name) + log.Info("Database Pending....", "Name", sidb.Name) } + log.Info("no pod currently in ready state") + return requeueY, sidbReadyPod, nil + } - // As No pod is ready now , turn on mode when pod is ready . so requeue the request - return requeueY, readyPod, errors.New("no pod is ready currently") + if sidb.Spec.CreateAs == "clone" { + // Required since clone creates the datafiles under primary database SID folder + r.Log.Info("Creating the SID directory link for clone database", "name", sidb.Spec.Sid) + _, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", + ctx, req, false, "bash", "-c", dbcommons.CreateSIDlinkCMD) + if err != nil { + r.Log.Info(err.Error()) + } + } + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", + ctx, req, false, "bash", "-c", dbcommons.GetCheckpointFileCMD) + if err != nil { + r.Log.Info(err.Error()) } - version, err := dbcommons.GetDatabaseVersion(readyPod, r, r.Config, ctx, req, m.Spec.Edition) + if out != "" { + log.Info("Database initialzied") + eventReason := "Database Unhealthy" + eventMsg := "datafiles exists" + r.Recorder.Eventf(sidb, corev1.EventTypeNormal, eventReason, eventMsg) + sidb.Status.DatafilesCreated = "true" + sidb.Status.Status = dbcommons.StatusNotReady + r.updateORDSStatus(sidb, ctx, req) + } else { + log.Info("Database Creating....", "Name", sidb.Name) + return requeueY, sidbReadyPod, nil + } + + version, err := dbcommons.GetDatabaseVersion(sidbReadyPod, r, r.Config, ctx, req) if err != nil { - return requeueY, readyPod, err + return requeueY, sidbReadyPod, err } dbMajorVersion, err := strconv.Atoi(strings.Split(version, ".")[0]) if err != nil { r.Log.Error(err, err.Error()) - return requeueY, readyPod, err + return requeueY, sidbReadyPod, err } r.Log.Info("DB Major Version is " + strconv.Itoa(dbMajorVersion)) // Validating that free edition of the database is only supported from database 23c onwards - if m.Spec.Edition == "free" && dbMajorVersion < 23 { + if sidb.Spec.Edition == "free" && dbMajorVersion < 23 { errMsg := "the Oracle Database Free is only available from version 23c onwards" - r.Recorder.Eventf(m, corev1.EventTypeWarning, "Spec Error", errMsg) - m.Status.Status = dbcommons.StatusError - return requeueY, readyPod, errors.New(errMsg) + r.Recorder.Eventf(sidb, corev1.EventTypeWarning, "Spec Error", errMsg) + sidb.Status.Status = dbcommons.StatusError + return requeueY, sidbReadyPod, errors.New(errMsg) } - available = append(available, readyPod) + available = append(available, sidbReadyPod) podNamesFinal := dbcommons.GetPodNames(available) - r.Log.Info("Final "+m.Name+" Pods After Deleting (or) Adding Extra Pods ( Including The Ready Pod ) ", "Pod Names", podNamesFinal) - r.Log.Info(m.Name+" Replicas Available", "Count", len(podNamesFinal)) - r.Log.Info(m.Name+" Replicas Required", "Count", m.Spec.Replicas) + r.Log.Info("Final "+sidb.Name+" Pods After Deleting (or) Adding Extra Pods ( Including The Ready Pod ) ", "Pod Names", podNamesFinal) + r.Log.Info(sidb.Name+" Replicas Available", "Count", len(podNamesFinal)) + r.Log.Info(sidb.Name+" Replicas Required", "Count", sidb.Spec.Replicas) eventReason := "Database Ready" - eventMsg := "database open on pod " + readyPod.Name + " scheduled on node " + readyPod.Status.HostIP - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + eventMsg := "database open on pod " + sidbReadyPod.Name + " scheduled on node " + sidbReadyPod.Status.HostIP + r.Recorder.Eventf(sidb, corev1.EventTypeNormal, eventReason, eventMsg) + + sidb.Status.CreatedAs = sidb.Spec.CreateAs - return requeueN, readyPod, nil + return requeueN, sidbReadyPod, nil } @@ -2470,7 +2473,6 @@ func (r *SingleInstanceDatabaseReconciler) updateInitParameters(m *dbapi.SingleI } log.Info("AlterProcessesCMD Output:" + out) } - return requeueN, nil } @@ -2716,7 +2718,7 @@ func (r *SingleInstanceDatabaseReconciler) updateSidbStatus(sidb *dbapi.SingleIn sidb.Status.Role = sidbRole // Get database version and update the status - version, err := dbcommons.GetDatabaseVersion(sidbReadyPod, r, r.Config, ctx, req, sidb.Spec.Edition) + version, err := dbcommons.GetDatabaseVersion(sidbReadyPod, r, r.Config, ctx, req) if err != nil { return err } @@ -2888,8 +2890,11 @@ func (r *SingleInstanceDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) er Complete(r) } -// StandbyDatabase functions -// Primary DB Validation +// ############################################################################# +// +// Check primary database status +// +// ############################################################################# func CheckPrimaryDatabaseStatus(p *dbapi.SingleInstanceDatabase) error { if p.Status.Status != dbcommons.StatusReady { @@ -2898,6 +2903,11 @@ func CheckPrimaryDatabaseStatus(p *dbapi.SingleInstanceDatabase) error { return nil } +// ############################################################################# +// +// Check if refered database is the primary database +// +// ############################################################################# func CheckDatabaseRoleAsPrimary(p *dbapi.SingleInstanceDatabase) error { if strings.ToUpper(p.Status.Role) != "PRIMARY" { @@ -2906,6 +2916,11 @@ func CheckDatabaseRoleAsPrimary(p *dbapi.SingleInstanceDatabase) error { return nil } +// ############################################################################# +// +// Get ready pod for the singleinstancedatabase resource +// +// ############################################################################# func GetDatabaseReadyPod(r client.Reader, d *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (corev1.Pod, error) { dbReadyPod, _, _, _, err := dbcommons.FindPods(r, d.Spec.Image.Version, @@ -2914,6 +2929,11 @@ func GetDatabaseReadyPod(r client.Reader, d *dbapi.SingleInstanceDatabase, ctx c return dbReadyPod, err } +// ############################################################################# +// +// Get admin password for singleinstancedatabase +// +// ############################################################################# func GetDatabaseAdminPassword(r client.Reader, d *dbapi.SingleInstanceDatabase, ctx context.Context) (string, error) { adminPasswordSecret := &corev1.Secret{} @@ -2926,6 +2946,11 @@ func GetDatabaseAdminPassword(r client.Reader, d *dbapi.SingleInstanceDatabase, return adminPassword, nil } +// ############################################################################# +// +// Validate primary singleinstancedatabase admin password +// +// ############################################################################# func ValidatePrimaryDatabaseAdminPassword(r *SingleInstanceDatabaseReconciler, p *dbapi.SingleInstanceDatabase, adminPassword string, ctx context.Context, req ctrl.Request) error { @@ -2952,6 +2977,11 @@ func ValidatePrimaryDatabaseAdminPassword(r *SingleInstanceDatabaseReconciler, p return nil } +// ############################################################################# +// +// Validate refered primary database db params are all enabled +// +// ############################################################################# func ValidateDatabaseConfiguration(p *dbapi.SingleInstanceDatabase) error { if p.Status.ArchiveLog == "false" || p.Status.FlashBack == "false" || p.Status.ForceLogging == "false" { return fmt.Errorf("all of Archivelog, Flashback and ForceLogging modes are not enabled in the primary database %v", p.Name) @@ -2959,6 +2989,11 @@ func ValidateDatabaseConfiguration(p *dbapi.SingleInstanceDatabase) error { return nil } +// ############################################################################# +// +// Validate refered primary database for standby sidb creation +// +// ############################################################################# func ValidatePrimaryDatabaseForStandbyCreation(r *SingleInstanceDatabaseReconciler, stdby *dbapi.SingleInstanceDatabase, primary *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) error { @@ -3010,7 +3045,11 @@ func ValidatePrimaryDatabaseForStandbyCreation(r *SingleInstanceDatabaseReconcil return nil } -// Primary DB Setup +// ############################################################################# +// +// Get total database pods for singleinstancedatabase +// +// ############################################################################# func GetTotalDatabasePods(r client.Reader, d *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (int, error) { _, totalPods, _, _, err := dbcommons.FindPods(r, d.Spec.Image.Version, d.Spec.Image.PullFrom, d.Name, d.Namespace, ctx, req) @@ -3018,6 +3057,11 @@ func GetTotalDatabasePods(r client.Reader, d *dbapi.SingleInstanceDatabase, ctx return totalPods, err } +// ############################################################################# +// +// Set tns names for primary database for dataguard configuraion +// +// ############################################################################# func SetupTnsNamesPrimaryForDG(r *SingleInstanceDatabaseReconciler, p *dbapi.SingleInstanceDatabase, s *dbapi.SingleInstanceDatabase, primaryReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) error { @@ -3048,6 +3092,11 @@ func SetupTnsNamesPrimaryForDG(r *SingleInstanceDatabaseReconciler, p *dbapi.Sin return nil } +// ############################################################################# +// +// Restarting listners in database +// +// ############################################################################# func RestartListenerInDatabase(r *SingleInstanceDatabaseReconciler, primaryReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) error { r.Log.Info("Restarting listener in the database through pod", "primary database pod name", primaryReadyPod.Name) out, err := dbcommons.ExecCommand(r, r.Config, primaryReadyPod.Name, primaryReadyPod.Namespace, "", @@ -3060,6 +3109,11 @@ func RestartListenerInDatabase(r *SingleInstanceDatabaseReconciler, primaryReady return nil } +// ############################################################################# +// +// Setup primary listener for dataguard configuration +// +// ############################################################################# func SetupListenerPrimaryForDG(r *SingleInstanceDatabaseReconciler, p *dbapi.SingleInstanceDatabase, s *dbapi.SingleInstanceDatabase, primaryReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) error { @@ -3091,6 +3145,11 @@ func SetupListenerPrimaryForDG(r *SingleInstanceDatabaseReconciler, p *dbapi.Sin return nil } +// ############################################################################# +// +// Setup init parameters of primary database for dataguard configuration +// +// ############################################################################# func SetupInitParamsPrimaryForDG(r *SingleInstanceDatabaseReconciler, primaryReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) error { r.Log.Info("Running StandbyDatabasePrerequisitesSQL in the primary database") out, err := dbcommons.ExecCommand(r, r.Config, primaryReadyPod.Name, primaryReadyPod.Namespace, "", ctx, req, false, "bash", "-c", @@ -3103,6 +3162,11 @@ func SetupInitParamsPrimaryForDG(r *SingleInstanceDatabaseReconciler, primaryRea return nil } +// ############################################################################# +// +// Setup primary database for standby singleinstancedatabase +// +// ############################################################################# func SetupPrimaryDatabase(r *SingleInstanceDatabaseReconciler, stdby *dbapi.SingleInstanceDatabase, primary *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) error { @@ -3144,7 +3208,11 @@ func SetupPrimaryDatabase(r *SingleInstanceDatabaseReconciler, stdby *dbapi.Sing } -// Standby DB Setup +// ############################################################################# +// +// Get all pdbs in a singleinstancedatabase +// +// ############################################################################# func GetAllPdbInDatabase(r *SingleInstanceDatabaseReconciler, dbReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) ([]string, error) { var pdbs []string out, err := dbcommons.ExecCommand(r, r.Config, dbReadyPod.Name, dbReadyPod.Namespace, "", @@ -3159,6 +3227,11 @@ func GetAllPdbInDatabase(r *SingleInstanceDatabaseReconciler, dbReadyPod corev1. return pdbs, nil } +// ############################################################################# +// +// Setup tnsnames.ora for all the pdb list in the singleinstancedatabase +// +// ############################################################################# func SetupTnsNamesForPDBListInDatabase(r *SingleInstanceDatabaseReconciler, d *dbapi.SingleInstanceDatabase, dbReadyPod corev1.Pod, ctx context.Context, req ctrl.Request, pdbList []string) error { for _, pdb := range pdbList { @@ -3196,6 +3269,11 @@ func SetupTnsNamesForPDBListInDatabase(r *SingleInstanceDatabaseReconciler, d *d return nil } +// ############################################################################# +// +// Setup tnsnames.ora in standby database for primary singleinstancedatabase +// +// ############################################################################# func SetupPrimaryDBTnsNamesInStandby(r *SingleInstanceDatabaseReconciler, s *dbapi.SingleInstanceDatabase, dbReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) error { @@ -3210,6 +3288,11 @@ func SetupPrimaryDBTnsNamesInStandby(r *SingleInstanceDatabaseReconciler, s *dba return nil } +// ############################################################################# +// +// Enabling flashback in singleinstancedatabase +// +// ############################################################################# func EnableFlashbackInDatabase(r *SingleInstanceDatabaseReconciler, dbReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) error { out, err := dbcommons.ExecCommand(r, r.Config, dbReadyPod.Name, dbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.FlashBackTrueSQL, dbcommons.GetSqlClient("enterprise"))) @@ -3221,6 +3304,11 @@ func EnableFlashbackInDatabase(r *SingleInstanceDatabaseReconciler, dbReadyPod c return nil } +// ############################################################################# +// +// setup standby database +// +// ############################################################################# func SetupStandbyDatabase(r *SingleInstanceDatabaseReconciler, stdby *dbapi.SingleInstanceDatabase, primary *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) error { @@ -3273,3 +3361,34 @@ func SetupStandbyDatabase(r *SingleInstanceDatabaseReconciler, stdby *dbapi.Sing return nil } + +// ############################################################################# +// +// Create oracle hostname environment variable object to be passed to sidb +// +// ############################################################################# +func CreateOracleHostnameEnvVarObj(sidb *dbapi.SingleInstanceDatabase, referedPrimaryDatabase *dbapi.SingleInstanceDatabase) corev1.EnvVar { + dbMajorVersion, err := strconv.Atoi(strings.Split(referedPrimaryDatabase.Status.ReleaseUpdate, ".")[0]) + if err != nil { + // r.Log.Error(err, err.Error()) + return corev1.EnvVar{ + Name: "ORACLE_HOSTNAME", + Value: "", + } + } + if dbMajorVersion >= 23 { + return corev1.EnvVar{ + Name: "ORACLE_HOSTNAME", + Value: sidb.Name, + } + } else { + return corev1.EnvVar{ + Name: "ORACLE_HOSTNAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "status.podIP", + }, + }, + } + } +} diff --git a/docs/sidb/README.md b/docs/sidb/README.md index c606b111..b749f415 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -423,7 +423,7 @@ The following attributes cannot be modified after creating the Single Instance D - `edition` - `charset` - `pdbName` -- `cloneFrom` +- `primaryDatabaseRef` If you attempt to changing one of these attributes, then you receive an error similar to the following: @@ -613,7 +613,7 @@ To create a standby database, edit and apply the sample yaml file [config/sample **Note:** - The `adminPassword` field of the above [config/samples/sidb/singleinstancedatabase_standby.yaml](../../config/samples/sidb/singleinstancedatabase_standby.yaml) contains an admin password secret of the primary database ref for Standby Database creation. This secret will get deleted after the database pod becomes ready if the `keepSecret` attribute of `adminPassword` field is set to `false`. By default `keepSecret` is set to `true`. - Mention referred primary database in `.spec.primaryDatabaseRef` in the yaml file. -- `.spec.createAsStandby` field of the yaml file should be true. +- `.spec.createAs` field of the yaml file should be set to "standby". - Database configuration like `Archivelog`, `FlashBack`, `ForceLog`, `TCPS connections` are not supported for standby database. #### List Standby Databases From 33057d6e61fedf5c6245b2f63e3f0302d9879763 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 16 Nov 2023 17:12:37 +0000 Subject: [PATCH 011/414] Bug 35895394 --- controllers/database/pdb_controller.go | 149 +++++++++++++++---------- 1 file changed, 92 insertions(+), 57 deletions(-) diff --git a/controllers/database/pdb_controller.go b/controllers/database/pdb_controller.go index 735deaf7..7a72e4f4 100644 --- a/controllers/database/pdb_controller.go +++ b/controllers/database/pdb_controller.go @@ -249,9 +249,11 @@ func (r *PDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R return requeueY, nil } -/************************************************* - * Validate the PDB Spec - /************************************************/ +/* +************************************************ + - Validate the PDB Spec + /*********************************************** +*/ func (r *PDBReconciler) validatePhase(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) { log := r.Log.WithValues("validatePhase", req.NamespacedName) @@ -282,9 +284,11 @@ func (r *PDBReconciler) validatePhase(ctx context.Context, req ctrl.Request, pdb log.Info("Validation complete") } -/**************************************************************** - * Check for Duplicate PDB. Same PDB name on the same CDB resource. - /***************************************************************/ +/* +*************************************************************** + - Check for Duplicate PDB. Same PDB name on the same CDB resource. + /************************************************************** +*/ func (r *PDBReconciler) checkDuplicatePDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { log := r.Log.WithValues("checkDuplicatePDB", req.NamespacedName) @@ -325,9 +329,11 @@ func (r *PDBReconciler) checkDuplicatePDB(ctx context.Context, req ctrl.Request, return nil } -/**************************************************************** - * Get the Custom Resource for the CDB mentioned in the PDB Spec - /***************************************************************/ +/* +*************************************************************** + - Get the Custom Resource for the CDB mentioned in the PDB Spec + /************************************************************** +*/ func (r *PDBReconciler) getCDBResource(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) (dbapi.CDB, error) { log := r.Log.WithValues("getCDBResource", req.NamespacedName) @@ -354,9 +360,11 @@ func (r *PDBReconciler) getCDBResource(ctx context.Context, req ctrl.Request, pd return cdb, nil } -/**************************************************************** - * Get the ORDS Pod for the CDB mentioned in the PDB Spec - /***************************************************************/ +/* +*************************************************************** + - Get the ORDS Pod for the CDB mentioned in the PDB Spec + /************************************************************** +*/ func (r *PDBReconciler) getORDSPod(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) (corev1.Pod, error) { log := r.Log.WithValues("getORDSPod", req.NamespacedName) @@ -382,9 +390,11 @@ func (r *PDBReconciler) getORDSPod(ctx context.Context, req ctrl.Request, pdb *d return cdbPod, nil } -/************************************************* - * Get Secret Key for a Secret Name - /************************************************/ +/* +************************************************ + - Get Secret Key for a Secret Name + /*********************************************** +*/ func (r *PDBReconciler) getSecret(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB, secretName string, keyName string) (string, error) { log := r.Log.WithValues("getSecret", req.NamespacedName) @@ -404,9 +414,11 @@ func (r *PDBReconciler) getSecret(ctx context.Context, req ctrl.Request, pdb *db return string(secret.Data[keyName]), nil } -/************************************************* - * Issue a REST API Call to the ORDS container - /************************************************/ +/* +************************************************ + - Issue a REST API Call to the ORDS container + /*********************************************** +*/ func (r *PDBReconciler) callAPI(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB, url string, payload map[string]string, action string) (string, error) { log := r.Log.WithValues("callAPI", req.NamespacedName) @@ -596,9 +608,11 @@ func (r *PDBReconciler) callAPI(ctx context.Context, req ctrl.Request, pdb *dbap return respData, nil } -/************************************************* - * Create a PDB - /************************************************/ +/* +************************************************ + - Create a PDB + /*********************************************** +*/ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { log := r.Log.WithValues("createPDB", req.NamespacedName) @@ -681,8 +695,8 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db if cdb.Spec.DBServer != "" { pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName } else { - ParseTnsAlias(&(cdb.Spec.DBTnsurl), &(pdb.Spec.PDBName)) pdb.Status.ConnString = cdb.Spec.DBTnsurl + ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) } log.Info("New connect strinng", "tnsurl", cdb.Spec.DBTnsurl) @@ -691,9 +705,11 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db return nil } -/************************************************* - * Clone a PDB - /************************************************/ +/* +************************************************ + - Clone a PDB + /*********************************************** +*/ func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { if pdb.Spec.PDBName == pdb.Spec.SrcPDBName { @@ -758,8 +774,8 @@ func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dba if cdb.Spec.DBServer != "" { pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName } else { - ParseTnsAlias(&(cdb.Spec.DBTnsurl), &(pdb.Spec.PDBName)) pdb.Status.ConnString = cdb.Spec.DBTnsurl + ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) } log.Info("Cloned PDB successfully", "Source PDB Name", pdb.Spec.SrcPDBName, "Clone PDB Name", pdb.Spec.PDBName) @@ -767,9 +783,11 @@ func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dba return nil } -/************************************************* - * Plug a PDB - /************************************************/ +/* +************************************************ + - Plug a PDB + /*********************************************** +*/ func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { log := r.Log.WithValues("plugPDB", req.NamespacedName) @@ -845,9 +863,11 @@ func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbap return nil } -/************************************************* - * Unplug a PDB - /************************************************/ +/* +************************************************ + - Unplug a PDB + /*********************************************** +*/ func (r *PDBReconciler) unplugPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { log := r.Log.WithValues("unplugPDB", req.NamespacedName) @@ -894,8 +914,8 @@ func (r *PDBReconciler) unplugPDB(ctx context.Context, req ctrl.Request, pdb *db if cdb.Spec.DBServer != "" { pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName } else { - ParseTnsAlias(&(cdb.Spec.DBTnsurl), &(pdb.Spec.PDBName)) pdb.Status.ConnString = cdb.Spec.DBTnsurl + ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) } if err := r.Status().Update(ctx, pdb); err != nil { @@ -928,9 +948,11 @@ func (r *PDBReconciler) unplugPDB(ctx context.Context, req ctrl.Request, pdb *db return nil } -/************************************************* - * Modify a PDB state - /************************************************/ +/* +************************************************ + - Modify a PDB state + /*********************************************** +*/ func (r *PDBReconciler) modifyPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { log := r.Log.WithValues("modifyPDB", req.NamespacedName) @@ -1003,9 +1025,11 @@ func (r *PDBReconciler) modifyPDB(ctx context.Context, req ctrl.Request, pdb *db return nil } -/************************************************* - * Get PDB State - /************************************************/ +/* +************************************************ + - Get PDB State + /*********************************************** +*/ func (r *PDBReconciler) getPDBState(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { log := r.Log.WithValues("getPDBState", req.NamespacedName) @@ -1052,9 +1076,11 @@ func (r *PDBReconciler) getPDBState(ctx context.Context, req ctrl.Request, pdb * return nil } -/************************************************* - * Map Database PDB to Kubernetes PDB CR - /************************************************/ +/* +************************************************ + - Map Database PDB to Kubernetes PDB CR + /*********************************************** +*/ func (r *PDBReconciler) mapPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { log := r.Log.WithValues("mapPDB", req.NamespacedName) @@ -1096,8 +1122,8 @@ func (r *PDBReconciler) mapPDB(ctx context.Context, req ctrl.Request, pdb *dbapi if cdb.Spec.DBServer != "" { pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName } else { - ParseTnsAlias(&(cdb.Spec.DBTnsurl), &(pdb.Spec.PDBName)) pdb.Status.ConnString = cdb.Spec.DBTnsurl + ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) } if err := r.Status().Update(ctx, pdb); err != nil { @@ -1108,9 +1134,11 @@ func (r *PDBReconciler) mapPDB(ctx context.Context, req ctrl.Request, pdb *dbapi return nil } -/************************************************* - * Delete a PDB - /************************************************/ +/* +************************************************ + - Delete a PDB + /*********************************************** +*/ func (r *PDBReconciler) deletePDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { log := r.Log.WithValues("deletePDB", req.NamespacedName) @@ -1143,9 +1171,11 @@ func (r *PDBReconciler) deletePDB(ctx context.Context, req ctrl.Request, pdb *db return nil } -/************************************************* - * Check PDB deletion - /************************************************/ +/* +************************************************ + - Check PDB deletion + /*********************************************** +*/ func (r *PDBReconciler) managePDBDeletion(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { log := r.Log.WithValues("managePDBDeletion", req.NamespacedName) @@ -1187,9 +1217,11 @@ func (r *PDBReconciler) managePDBDeletion(ctx context.Context, req ctrl.Request, return nil } -/************************************************* - * Finalization logic for PDBFinalizer - /************************************************/ +/* +************************************************ + - Finalization logic for PDBFinalizer + /*********************************************** +*/ func (r *PDBReconciler) deletePDBInstance(req ctrl.Request, ctx context.Context, pdb *dbapi.PDB) error { log := r.Log.WithValues("deletePDBInstance", req.NamespacedName) @@ -1227,9 +1259,11 @@ func (r *PDBReconciler) deletePDBInstance(req ctrl.Request, ctx context.Context, return nil } -/************************************************************** - * SetupWithManager sets up the controller with the Manager. - /*************************************************************/ +/* +************************************************************* + - SetupWithManager sets up the controller with the Manager. + /************************************************************ +*/ func (r *PDBReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&dbapi.PDB{}). @@ -1253,6 +1287,7 @@ Enh 35357707 - PROVIDE THE PDB TNSALIAS INFORMATION **************************************************************/ func ParseTnsAlias(tns *string, pdbsrv *string) { + var swaptns string fmt.Printf("Analyzing string [%s]\n", *tns) fmt.Printf("Relacing srv [%s]\n", *pdbsrv) @@ -1266,9 +1301,9 @@ func ParseTnsAlias(tns *string, pdbsrv *string) { return } - *pdbsrv = fmt.Sprintf("SERVICE_NAME=%s", *pdbsrv) + swaptns = fmt.Sprintf("SERVICE_NAME=%s", *pdbsrv) tnsreg := regexp.MustCompile(`SERVICE_NAME=\w+`) - *tns = tnsreg.ReplaceAllString(*tns, *pdbsrv) + *tns = tnsreg.ReplaceAllString(*tns, swaptns) fmt.Printf("Newstring [%s]\n", *tns) From 76e591626e0d07e56931c240c1cb5516c4d17cbe Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Fri, 1 Dec 2023 18:18:15 +0000 Subject: [PATCH 012/414] Update libs and Dockerfile --- .gitlab-ci.yml | 1 + Dockerfile | 13 ++- Makefile | 31 ++++-- .../autonomouscontainerdatabase_webhook.go | 17 ++-- .../v1alpha1/autonomousdatabase_webhook.go | 19 ++-- .../autonomousdatabasebackup_webhook.go | 17 ++-- .../autonomousdatabaserestore_webhook.go | 17 ++-- apis/database/v1alpha1/cdb_webhook.go | 21 ++-- .../v1alpha1/dataguardbroker_webhook.go | 25 ++--- .../v1alpha1/oraclerestdataservice_webhook.go | 23 ++--- apis/database/v1alpha1/pdb_webhook.go | 19 ++-- .../singleinstancedatabase_webhook.go | 29 +++--- apis/database/v1alpha1/webhook_suite_test.go | 14 ++- .../database/autonomousdatabase_controller.go | 7 +- go.mod | 91 +++++++++--------- go.sum | 96 +++++++++++++++++++ main.go | 7 +- test/e2e/util/util.go | 2 +- 18 files changed, 295 insertions(+), 154 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7008b235..170c87e3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,6 +3,7 @@ build-operator: variables: IMAGE: "$DOCKER_REPO:$CI_COMMIT_SHORT_SHA" OP_YAML: oracle-database-operator.yaml + BUILD_INTERNAL: "true" script: - go version - make docker-build IMG="$IMAGE" diff --git a/Dockerfile b/Dockerfile index 3c249825..d26fbcd4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,18 @@ # # Build the manager binary -FROM golang:1.19 as builder +ARG BUILDER_IMG +FROM ${BUILDER_IMG} as builder + +# Download golang if BUILD_INTERNAL is set to true +ARG GOLANG_VERSION +RUN if [[ -n "$GOLANG_VERSION" ]]; then \ + microdnf install gzip &&\ + curl -LJO https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ + rm -rf /usr/local/go && tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ + rm go${GOLANG_VERSION}.linux-amd64.tar.gz; \ + fi +ENV PATH=${GOLANG_VERSION:+"${PATH}:/usr/local/go/bin"} WORKDIR /workspace # Copy the Go Modules manifests diff --git a/Makefile b/Makefile index 033b8a36..44244435 100644 --- a/Makefile +++ b/Makefile @@ -71,12 +71,23 @@ build: generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go -docker-build: manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast - docker build --no-cache=true --build-arg http_proxy=${HTTP_PROXY} --build-arg https_proxy=${HTTPS_PROXY} \ - --build-arg CI_COMMIT_SHA=${CI_COMMIT_SHA} --build-arg CI_COMMIT_BRANCH=${CI_COMMIT_BRANCH} . -t ${IMG} +GOLANG_VERSION ?= 1.21.4 +## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. +## Otherwise, use golang image from docker hub as the builder. +ifeq ($(BUILD_INTERNAL), true) +BUILDER_IMG = oraclelinux:8-slim +BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) +else +BUILDER_IMG = golang:$(GOLANG_VERSION) +BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) +endif +docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast + docker build --no-cache=true --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ + --build-arg CI_COMMIT_SHA=$(CI_COMMIT_SHA) --build-arg CI_COMMIT_BRANCH=$(CI_COMMIT_BRANCH) \ + $(BUILD_ARGS) . -t $(IMG) docker-push: ## Push docker image with the manager. - docker push ${IMG} + docker push $(IMG) ##@ Deployment @@ -87,17 +98,17 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified $(KUSTOMIZE) build config/crd | kubectl delete -f - deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) $(KUSTOMIZE) build config/default | kubectl apply -f - # Bug:34265574 # Used sed to reposition the controller-manager Deployment after the certificate creation in the OPERATOR_YAML operator-yaml: manifests kustomize - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default > "${OPERATOR_YAML}" - sed -i.bak -e '/^apiVersion: apps\/v1/,/---/d' "${OPERATOR_YAML}" - (echo --- && sed '/^apiVersion: apps\/v1/,/---/!d' "${OPERATOR_YAML}.bak") >> "${OPERATOR_YAML}" - rm "${OPERATOR_YAML}.bak" + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) + $(KUSTOMIZE) build config/default > "$(OPERATOR_YAML)" + sed -i.bak -e '/^apiVersion: apps\/v1/,/---/d' "$(OPERATOR_YAML)" + (echo --- && sed '/^apiVersion: apps\/v1/,/---/!d' "$(OPERATOR_YAML).bak") >> "$(OPERATOR_YAML)" + rm "$(OPERATOR_YAML).bak" undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/default | kubectl delete -f - diff --git a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go index 3e9f5e1f..37e1d819 100644 --- a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go @@ -46,6 +46,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -62,15 +63,15 @@ func (r *AutonomousContainerDatabase) SetupWebhookWithManager(mgr ctrl.Manager) var _ webhook.Validator = &AutonomousContainerDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousContainerDatabase) ValidateCreate() error { +func (r *AutonomousContainerDatabase) ValidateCreate() (admission.Warnings, error) { autonomouscontainerdatabaselog.Info("validate create", "name", r.Name) // TODO(user): fill in your validation logic upon object creation. - return nil + return nil, nil } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) error { +func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList var oldACD *AutonomousContainerDatabase = old.(*AutonomousContainerDatabase) @@ -78,7 +79,7 @@ func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) error { // skip the update of adding ADB OCID or binding if oldACD.Status.LifecycleState == "" { - return nil + return nil, nil } // cannot update when the old state is in intermediate state, except for the terminate operatrion @@ -95,17 +96,17 @@ func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousContainerDatabase"}, r.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousContainerDatabase) ValidateDelete() error { +func (r *AutonomousContainerDatabase) ValidateDelete() (admission.Warnings, error) { autonomouscontainerdatabaselog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. - return nil + return nil, nil } diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go index bac49c70..a7d492d7 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -50,6 +50,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -97,7 +98,7 @@ var _ webhook.Validator = &AutonomousDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type // ValidateCreate checks if the spec is valid for a provisioning or a binding operation -func (r *AutonomousDatabase) ValidateCreate() error { +func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { var allErrs field.ErrorList @@ -115,15 +116,15 @@ func (r *AutonomousDatabase) ValidateCreate() error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabase"}, r.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) error { +func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList var oldADB *AutonomousDatabase = old.(*AutonomousDatabase) @@ -131,7 +132,7 @@ func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) error { // skip the update of adding ADB OCID or binding if oldADB.Status.LifecycleState == "" { - return nil + return nil, nil } // cannot update when the old state is in intermediate, except for the change to the hardLink or the terminate operatrion during valid lifecycleState @@ -187,9 +188,9 @@ func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) error { allErrs = validateNetworkAccess(r, allErrs) if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabase"}, r.Name, allErrs) } @@ -263,11 +264,11 @@ func validateNetworkAccess(adb *AutonomousDatabase, allErrs field.ErrorList) fie } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabase) ValidateDelete() error { +func (r *AutonomousDatabase) ValidateDelete() (admission.Warnings, error) { autonomousdatabaselog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. - return nil + return nil, nil } // Returns true if AutonomousContainerDatabaseOCID has value. diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go index c08c8c58..42624c4a 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go @@ -46,6 +46,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -76,7 +77,7 @@ func (r *AutonomousDatabaseBackup) Default() { var _ webhook.Validator = &AutonomousDatabaseBackup{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) ValidateCreate() error { +func (r *AutonomousDatabaseBackup) ValidateCreate() (admission.Warnings, error) { autonomousdatabasebackuplog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -92,15 +93,15 @@ func (r *AutonomousDatabaseBackup) ValidateCreate() error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseBackup"}, r.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) error { +func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { autonomousdatabasebackuplog.Info("validate update", "name", r.Name) var allErrs field.ErrorList @@ -132,17 +133,17 @@ func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseBackup"}, r.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) ValidateDelete() error { +func (r *AutonomousDatabaseBackup) ValidateDelete() (admission.Warnings, error) { autonomousdatabasebackuplog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. - return nil + return nil, nil } diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go index 8b4dd974..3f24f37d 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go @@ -46,6 +46,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -65,7 +66,7 @@ func (r *AutonomousDatabaseRestore) SetupWebhookWithManager(mgr ctrl.Manager) er var _ webhook.Validator = &AutonomousDatabaseRestore{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseRestore) ValidateCreate() error { +func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) { autonomousdatabaserestorelog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -104,31 +105,31 @@ func (r *AutonomousDatabaseRestore) ValidateCreate() error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseRestore"}, r.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseRestore) ValidateUpdate(old runtime.Object) error { +func (r *AutonomousDatabaseRestore) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { autonomousdatabaserestorelog.Info("validate update", "name", r.Name) var allErrs field.ErrorList if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseRestore"}, r.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseRestore) ValidateDelete() error { +func (r *AutonomousDatabaseRestore) ValidateDelete() (admission.Warnings, error) { autonomousdatabaserestorelog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. - return nil + return nil, nil } diff --git a/apis/database/v1alpha1/cdb_webhook.go b/apis/database/v1alpha1/cdb_webhook.go index 25975092..7e48c82c 100644 --- a/apis/database/v1alpha1/cdb_webhook.go +++ b/apis/database/v1alpha1/cdb_webhook.go @@ -49,6 +49,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -83,7 +84,7 @@ func (r *CDB) Default() { var _ webhook.Validator = &CDB{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateCreate() error { +func (r *CDB) ValidateCreate() (admission.Warnings, error) { cdblog.Info("ValidateCreate", "name", r.Name) var allErrs field.ErrorList @@ -159,20 +160,20 @@ func (r *CDB) ValidateCreate() error { field.Required(field.NewPath("spec").Child("webServerPwd"), "Please specify password for the Web Server User having SQL Administrator role")) } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "CDB"}, r.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateUpdate(old runtime.Object) error { +func (r *CDB) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { cdblog.Info("validate update", "name", r.Name) isCDBMarkedToBeDeleted := r.GetDeletionTimestamp() != nil if isCDBMarkedToBeDeleted { - return nil + return nil, nil } var allErrs field.ErrorList @@ -180,7 +181,7 @@ func (r *CDB) ValidateUpdate(old runtime.Object) error { // Check for updation errors oldCDB, ok := old.(*CDB) if !ok { - return nil + return nil, nil } if r.Spec.DBPort < 0 { @@ -201,18 +202,18 @@ func (r *CDB) ValidateUpdate(old runtime.Object) error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "CDB"}, r.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateDelete() error { +func (r *CDB) ValidateDelete() (admission.Warnings, error) { cdblog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. - return nil + return nil, nil } diff --git a/apis/database/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go index c947f7a3..a0e3ccbe 100644 --- a/apis/database/v1alpha1/dataguardbroker_webhook.go +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -48,6 +48,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -95,38 +96,38 @@ func (r *DataguardBroker) Default() { var _ webhook.Validator = &DataguardBroker{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *DataguardBroker) ValidateCreate() error { +func (r *DataguardBroker) ValidateCreate() (admission.Warnings, error) { dataguardbrokerlog.Info("validate create", "name", r.Name) // TODO(user): fill in your validation logic upon object creation. - return nil + return nil, nil } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *DataguardBroker) ValidateUpdate(old runtime.Object) error { +func (r *DataguardBroker) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { dataguardbrokerlog.Info("validate update", "name", r.Name) dataguardbrokerlog.Info("validate update", "name", r.Name) var allErrs field.ErrorList // check creation validations first - err := r.ValidateCreate() + _, err := r.ValidateCreate() if err != nil { - return err + return nil, err } // Validate Deletion if r.GetDeletionTimestamp() != nil { - err := r.ValidateDelete() + warnings, err := r.ValidateDelete() if err != nil { - return err + return warnings, err } } // Now check for updation errors oldObj, ok := old.(*DataguardBroker) if !ok { - return nil + return nil, nil } if oldObj.Status.ProtectionMode != "" && !strings.EqualFold(r.Spec.ProtectionMode, oldObj.Status.ProtectionMode) { @@ -139,18 +140,18 @@ func (r *DataguardBroker) ValidateUpdate(old runtime.Object) error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "DataguardBroker"}, r.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *DataguardBroker) ValidateDelete() error { +func (r *DataguardBroker) ValidateDelete() (admission.Warnings, error) { dataguardbrokerlog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. - return nil + return nil, nil } diff --git a/apis/database/v1alpha1/oraclerestdataservice_webhook.go b/apis/database/v1alpha1/oraclerestdataservice_webhook.go index be83fffc..bad35f05 100644 --- a/apis/database/v1alpha1/oraclerestdataservice_webhook.go +++ b/apis/database/v1alpha1/oraclerestdataservice_webhook.go @@ -46,6 +46,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -86,7 +87,7 @@ func (r *OracleRestDataService) Default() { var _ webhook.Validator = &OracleRestDataService{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *OracleRestDataService) ValidateCreate() error { +func (r *OracleRestDataService) ValidateCreate() (admission.Warnings, error) { oraclerestdataservicelog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -121,29 +122,29 @@ func (r *OracleRestDataService) ValidateCreate() error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "OracleRestDataService"}, r.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *OracleRestDataService) ValidateUpdate(oldRuntimeObject runtime.Object) error { +func (r *OracleRestDataService) ValidateUpdate(oldRuntimeObject runtime.Object) (admission.Warnings, error) { oraclerestdataservicelog.Info("validate update", "name", r.Name) var allErrs field.ErrorList // check creation validations first - err := r.ValidateCreate() + warnings, err := r.ValidateCreate() if err != nil { - return err + return warnings, err } // Now check for updation errors old, ok := oldRuntimeObject.(*OracleRestDataService) if !ok { - return nil + return nil, nil } if old.Status.DatabaseRef != "" && old.Status.DatabaseRef != r.Spec.DatabaseRef { @@ -156,18 +157,18 @@ func (r *OracleRestDataService) ValidateUpdate(oldRuntimeObject runtime.Object) } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "OracleRestDataService"}, r.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *OracleRestDataService) ValidateDelete() error { +func (r *OracleRestDataService) ValidateDelete() (admission.Warnings, error) { oraclerestdataservicelog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. - return nil + return nil, nil } diff --git a/apis/database/v1alpha1/pdb_webhook.go b/apis/database/v1alpha1/pdb_webhook.go index 0e78790a..3051971e 100644 --- a/apis/database/v1alpha1/pdb_webhook.go +++ b/apis/database/v1alpha1/pdb_webhook.go @@ -54,6 +54,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -121,7 +122,7 @@ func (r *PDB) Default() { var _ webhook.Validator = &PDB{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateCreate() error { +func (r *PDB) ValidateCreate() (admission.Warnings, error) { pdblog.Info("ValidateCreate-Validating PDB spec for : " + r.Name) var allErrs field.ErrorList @@ -134,9 +135,9 @@ func (r *PDB) ValidateCreate() error { if len(allErrs) == 0 { pdblog.Info("PDB Resource : " + r.Name + " successfully validated for Action : " + action) - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "PDB"}, r.Name, allErrs) } @@ -242,12 +243,12 @@ func (r *PDB) validateAction(allErrs *field.ErrorList) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateUpdate(old runtime.Object) error { +func (r *PDB) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { pdblog.Info("ValidateUpdate-Validating PDB spec for : " + r.Name) isPDBMarkedToBeDeleted := r.GetDeletionTimestamp() != nil if isPDBMarkedToBeDeleted { - return nil + return nil, nil } var allErrs field.ErrorList @@ -272,19 +273,19 @@ func (r *PDB) ValidateUpdate(old runtime.Object) error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "PDB"}, r.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateDelete() error { +func (r *PDB) ValidateDelete() (admission.Warnings, error) { pdblog.Info("ValidateDelete-Validating PDB spec for : " + r.Name) // TODO(user): fill in your validation logic upon object deletion. - return nil + return nil, nil } // Validate common specs needed for all PDB Actions diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 4e7bb1be..d4a53ec3 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -52,6 +52,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -147,7 +148,7 @@ func (r *SingleInstanceDatabase) Default() { var _ webhook.Validator = &SingleInstanceDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *SingleInstanceDatabase) ValidateCreate() error { +func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { singleinstancedatabaselog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -383,37 +384,37 @@ func (r *SingleInstanceDatabase) ValidateCreate() error { } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "SingleInstanceDatabase"}, r.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) error { +func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) (admission.Warnings, error) { singleinstancedatabaselog.Info("validate update", "name", r.Name) var allErrs field.ErrorList // check creation validations first - err := r.ValidateCreate() + warnings, err := r.ValidateCreate() if err != nil { - return err + return warnings, err } // Validate Deletion if r.GetDeletionTimestamp() != nil { - err := r.ValidateDelete() + warnings, err := r.ValidateDelete() if err != nil { - return err + return warnings, err } } // Now check for updation errors old, ok := oldRuntimeObject.(*SingleInstanceDatabase) if !ok { - return nil + return nil, nil } if old.Status.CreatedAs == "clone" { @@ -504,16 +505,16 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) field.Forbidden(field.NewPath("spec").Child("persistence"), "uninstall ORDS to change Persistence")) } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "SingleInstanceDatabase"}, r.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *SingleInstanceDatabase) ValidateDelete() error { +func (r *SingleInstanceDatabase) ValidateDelete() (admission.Warnings, error) { singleinstancedatabaselog.Info("validate delete", "name", r.Name) var allErrs field.ErrorList if r.Status.OrdsReference != "" { @@ -521,9 +522,9 @@ func (r *SingleInstanceDatabase) ValidateDelete() error { field.Forbidden(field.NewPath("status").Child("ordsReference"), "delete "+r.Status.OrdsReference+" to cleanup this SIDB")) } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid( + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "SingleInstanceDatabase"}, r.Name, allErrs) } diff --git a/apis/database/v1alpha1/webhook_suite_test.go b/apis/database/v1alpha1/webhook_suite_test.go index 343f279c..3f740a41 100644 --- a/apis/database/v1alpha1/webhook_suite_test.go +++ b/apis/database/v1alpha1/webhook_suite_test.go @@ -61,6 +61,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/webhook" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" ) // To avoid dot import @@ -136,11 +138,15 @@ var _ = BeforeSuite(func() { webhookInstallOptions := &testEnv.WebhookInstallOptions mgr, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme, - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, + WebhookServer: webhook.NewServer(webhook.Options{ + Port: webhookInstallOptions.LocalServingPort, + Host: webhookInstallOptions.LocalServingHost, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), LeaderElection: false, - MetricsBindAddress: "0", + Metrics: metricsserver.Options{ + BindAddress: "0", + }, }) Expect(err).NotTo(HaveOccurred()) diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 54ff3431..de36234c 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -66,7 +66,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" "github.com/oracle/oracle-database-operator/commons/annotations" @@ -92,11 +91,11 @@ func (r *AutonomousDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error return ctrl.NewControllerManagedBy(mgr). For(&dbv1alpha1.AutonomousDatabase{}). Watches( - &source.Kind{Type: &dbv1alpha1.AutonomousDatabaseBackup{}}, + &dbv1alpha1.AutonomousDatabaseBackup{}, handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn()), ). Watches( - &source.Kind{Type: &dbv1alpha1.AutonomousDatabaseRestore{}}, + &dbv1alpha1.AutonomousDatabaseRestore{}, handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn()), ). WithEventFilter(predicate.And(r.eventFilterPredicate(), r.watchPredicate())). @@ -105,7 +104,7 @@ func (r *AutonomousDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error } func (r *AutonomousDatabaseReconciler) enqueueMapFn() handler.MapFunc { - return func(o client.Object) []reconcile.Request { + return func(ctx context.Context, o client.Object) []reconcile.Request { reqs := make([]reconcile.Request, len(o.GetOwnerReferences())) for _, owner := range o.GetOwnerReferences() { diff --git a/go.mod b/go.mod index 81c4d435..82df5455 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,18 @@ module github.com/oracle/oracle-database-operator -go 1.19 +go 1.21 require ( - github.com/go-logr/logr v1.2.3 - github.com/onsi/ginkgo/v2 v2.5.0 - github.com/onsi/gomega v1.24.1 - github.com/oracle/oci-go-sdk/v65 v65.26.1 - go.uber.org/zap v1.23.0 - gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.25.4 - k8s.io/apimachinery v0.25.4 - k8s.io/client-go v0.25.4 - sigs.k8s.io/controller-runtime v0.13.1 + github.com/go-logr/logr v1.2.4 + github.com/onsi/ginkgo/v2 v2.12.1 + github.com/onsi/gomega v1.28.0 + github.com/oracle/oci-go-sdk/v65 v65.49.3 + go.uber.org/zap v1.26.0 + gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.28.2 + k8s.io/apimachinery v0.28.2 + k8s.io/client-go v0.28.2 + sigs.k8s.io/controller-runtime v0.16.2 sigs.k8s.io/yaml v1.3.0 ) @@ -20,63 +20,68 @@ require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.14 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.12.2 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.4.0 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.7.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/term v0.11.0 // indirect + golang.org/x/text v0.12.0 + golang.org/x/time v0.3.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.25.0 // indirect - k8s.io/component-base v0.25.0 // indirect - k8s.io/klog/v2 v2.70.1 // indirect - k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + k8s.io/apiextensions-apiserver v0.28.0 // indirect + k8s.io/component-base v0.28.1 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) require ( cloud.google.com/go/compute v1.2.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/go-logr/zapr v1.2.3 // indirect + github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.3.0 // indirect + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect + golang.org/x/tools v0.12.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index aacb116a..14b3e019 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,8 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -89,6 +91,8 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -104,6 +108,8 @@ github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -119,17 +125,29 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -168,11 +186,15 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -208,6 +230,7 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -245,6 +268,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -253,9 +277,13 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -275,10 +303,16 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls= github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= +github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= +github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= github.com/oracle/oci-go-sdk/v65 v65.26.1 h1:Ms20RSRj+CuvQmw5ET1TkmzxLBI+bmLjZ6NYANA3gkk= github.com/oracle/oci-go-sdk/v65 v65.26.1/go.mod h1:oyMrMa1vOzzKTmPN+kqrTR9y9kPA2tU1igN3NUSNTIE= +github.com/oracle/oci-go-sdk/v65 v65.49.3 h1:HHv+XMZiBYHtoU8Ac/fURdp9v1vJPPCpIbJAWeadREw= +github.com/oracle/oci-go-sdk/v65 v65.49.3/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -291,22 +325,30 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -323,6 +365,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -331,6 +374,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -347,14 +393,21 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -371,6 +424,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -437,6 +492,8 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -454,6 +511,8 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -523,11 +582,17 @@ golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -538,11 +603,15 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -597,12 +666,16 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -748,12 +821,15 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -781,28 +857,48 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs= k8s.io/api v0.25.4/go.mod h1:IG2+RzyPQLllQxnhzD8KQNEu4c4YvyDTpSMztf4A0OQ= +k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= +k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= +k8s.io/apiextensions-apiserver v0.28.0 h1:CszgmBL8CizEnj4sj7/PtLGey6Na3YgWyGCPONv7E9E= +k8s.io/apiextensions-apiserver v0.28.0/go.mod h1:uRdYiwIuu0SyqJKriKmqEN2jThIJPhVmOWETm8ud1VE= k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= +k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= k8s.io/client-go v0.25.4 h1:3RNRDffAkNU56M/a7gUfXaEzdhZlYhoW8dgViGy5fn8= k8s.io/client-go v0.25.4/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw= +k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= +k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= +k8s.io/component-base v0.28.1 h1:LA4AujMlK2mr0tZbQDZkjWbdhTV5bRyEyAFe0TJxlWg= +k8s.io/component-base v0.28.1/go.mod h1:jI11OyhbX21Qtbav7JkhehyBsIRfnO8oEgoAR12ArIU= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= +sigs.k8s.io/controller-runtime v0.16.2 h1:mwXAVuEk3EQf478PQwQ48zGOXvW27UJc8NHktQVuIPU= +sigs.k8s.io/controller-runtime v0.16.2/go.mod h1:vpMu3LpI5sYWtujJOa2uPK61nB5rbwlN7BAB8aSLvGU= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/main.go b/main.go index 6dbf2305..d449fdff 100644 --- a/main.go +++ b/main.go @@ -53,6 +53,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log/zap" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" databasecontroller "github.com/oracle/oracle-database-operator/controllers/database" @@ -88,10 +89,12 @@ func main() { ctrl.SetLogger(zap.New(func(o *zap.Options) { *o = *options })) + // By default, a Manager will create a WebhookServer with port 9443 mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, + Metrics: metricsserver.Options{ + BindAddress: metricsAddr, + }, LeaderElection: enableLeaderElection, LeaderElectionID: "a9d608ea.oracle.com", }) diff --git a/test/e2e/util/util.go b/test/e2e/util/util.go index 81b5259d..b9e76aa7 100644 --- a/test/e2e/util/util.go +++ b/test/e2e/util/util.go @@ -44,7 +44,7 @@ import ( "strings" "time" - goyaml "gopkg.in/yaml.v2" + goyaml "gopkg.in/yaml.v3" dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" From f9a4ef7e1fef17846f24897b6396920642b83ebb Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Fri, 1 Dec 2023 22:26:59 +0000 Subject: [PATCH 013/414] Update docs/adb/ADB_PREREQUISITES.md, docs/adb/README.md, docs/adb/ACD.md, README.md --- README.md | 2 +- docs/adb/ACD.md | 2 +- docs/adb/ADB_PREREQUISITES.md | 8 ++++---- docs/adb/README.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 614f6c65..c68e6c3f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ In this v1.0.0 production release, `OraOperator` supports the following database * Oracle Autonomous Database: * Oracle Autonomous Database shared Oracle Cloud Infrastructure (OCI) (ADB-S) * Oracle Autonomous Database on dedicated Cloud infrastructure (ADB-D) - * Oracle Autonomous Container Database (ACD) (infrastructure) the infrastructure for provisionning Autonomous Databases. + * Oracle Autonomous Container Database (ACD) (infrastructure) the infrastructure for provisioning Autonomous Databases. * Containerized Single Instance databases (SIDB) deployed in the Oracle Kubernetes Engine (OKE) and any k8s where OraOperator is deployed * Containerized Sharded databases (SHARDED) deployed in OKE and any k8s where OraOperator is deployed * Oracle Multitenant Databases (CDB/PDBs) diff --git a/docs/adb/ACD.md b/docs/adb/ACD.md index ffd09116..28ff2101 100644 --- a/docs/adb/ACD.md +++ b/docs/adb/ACD.md @@ -8,7 +8,7 @@ As indicated in the prerequisites (see above), to interact with OCI services, ei ## Required Permissions -The opeartor must be given the required type of access in a policy written by an administrator to manage the Autonomous Container Databases. See [Create an Autonomous Container Database](https://docs.oracle.com/en-us/iaas/autonomous-database/doc/create-acd.html) for the required policies. +The operator must be given the required type of access in a policy written by an administrator to manage the Autonomous Container Databases. See [Create an Autonomous Container Database](https://docs.oracle.com/en-us/iaas/autonomous-database/doc/create-acd.html) for the required policies. The permission to view the workrequests is also required, so that the operator will update the resources when the work is done. See [Viewing Work Requests](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengviewingworkrequests.htm#contengviewingworkrequests) for sample work request policies. diff --git a/docs/adb/ADB_PREREQUISITES.md b/docs/adb/ADB_PREREQUISITES.md index 3eaf5b4a..2ddaaa0e 100644 --- a/docs/adb/ADB_PREREQUISITES.md +++ b/docs/adb/ADB_PREREQUISITES.md @@ -12,7 +12,7 @@ To provide access, choose **one of the following approaches**: ## Authorized with API Key Authentication -API keys are supplied by users to authenticate the operator accessing Oracle Cloud Infrastructure (OCI) services. The operator reads the credintials of the OCI user from a ConfigMap and a Secret. If you're using Oracle Container Engine for Kubernetes (OKE), you may alternatively use [Instance Principal](#authorized-with-instance-principal) to avoid the need to configure user credentails or a configuration file. If the operator is deployed in a third-party Kubernetes cluster, then the credentials or a configuration file are needed, since Instance principal authorization applies only to instances that are running in the OCI. +API keys are supplied by users to authenticate the operator accessing Oracle Cloud Infrastructure (OCI) services. The operator reads the credentials of the OCI user from a ConfigMap and a Secret. If you're using Oracle Container Engine for Kubernetes (OKE), you may alternatively use [Instance Principal](#authorized-with-instance-principal) to avoid the need to configure user credentials or a configuration file. If the operator is deployed in a third-party Kubernetes cluster, then the credentials or a configuration file are needed, since Instance principal authorization applies only to instances that are running in the OCI. Oracle recommends using the helper script `set_ocicredentials.sh` in the root directory of the repository; this script will generate a ConfigMap and a Secret with the OCI credentials. By default, the script parses the **DEFAULT** profile in `~/.oci/config`. The default names of the ConfigMap and the Secret are, respectively: `oci-cred` and `oci-privatekey`. @@ -46,9 +46,9 @@ After creating the ConfigMap and the Secret, use their names as the values of `o ## Authorized with Instance Principal -Instance principal authorization enables the operator to make API calls from an instance (that is, a node) without requiring the `ociConfigMap`, and `ociSecret` attributes in the `.yaml` file. This approach applies only to instances that are running in the Oracle Cloud Infrastructure (OCI). In addition, this approach grants permissions to the nodes that matche the rules, which means that all the pods in the nodes can make the service calls. +Instance principal authorization enables the operator to make API calls from an instance (that is, a node) without requiring the `ociConfigMap`, and `ociSecret` attributes in the `.yaml` file. This approach applies only to instances that are running in the Oracle Cloud Infrastructure (OCI). In addition, this approach grants permissions to the nodes that match the rules, which means that all the pods in the nodes can make the service calls. -To set up the instance princials, you will have to: +To set up the instance principals, you will have to: * [Define dynamic group that includes the nodes in which the operator runs](#define-dynamic-group) * [Define policies that grant to the dynamic group the required permissions for the operator to its OCI interactions](#define-policies) @@ -111,7 +111,7 @@ To set up the instance princials, you will have to: Allow dynamic-group to manage all-resources in tenancy ``` - Example 3: enable a particular resouce access for the dynamic group to manage Oracle Autonomous Database in a given compartment + Example 3: enable a particular resource access for the dynamic group to manage Oracle Autonomous Database in a given compartment ```sh Allow dynamic-group to manage autonomous-database-family in compartment diff --git a/docs/adb/README.md b/docs/adb/README.md index 144f9d41..aa4bc60d 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -111,7 +111,7 @@ Follow these steps to provision an Autonomous Database that will map objects in 5. Choose the type of network access (optional): - By default, the network access type is set to PUBLIC, which allows secure connections from anywhere. Uncomment the code block if you want configure the network acess. See [Configuring Network Access of Autonomous Database](./NETWORK_ACCESS_OPTIONS.md) for more information. + By default, the network access type is set to PUBLIC, which allows secure connections from anywhere. Uncomment the code block if you want configure the network access. See [Configuring Network Access of Autonomous Database](./NETWORK_ACCESS_OPTIONS.md) for more information. 6. Apply the YAML: From eead5e14e347ae744983f3ab0d16c330dca06d8b Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Fri, 8 Dec 2023 20:58:29 +0530 Subject: [PATCH 014/414] Use oraclelinux 8 image instead of slim --- Dockerfile | 1 - Makefile | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index d26fbcd4..782ee9f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,6 @@ FROM ${BUILDER_IMG} as builder # Download golang if BUILD_INTERNAL is set to true ARG GOLANG_VERSION RUN if [[ -n "$GOLANG_VERSION" ]]; then \ - microdnf install gzip &&\ curl -LJO https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ rm -rf /usr/local/go && tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ rm go${GOLANG_VERSION}.linux-amd64.tar.gz; \ diff --git a/Makefile b/Makefile index 44244435..67ea779f 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,7 @@ GOLANG_VERSION ?= 1.21.4 ## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. ## Otherwise, use golang image from docker hub as the builder. ifeq ($(BUILD_INTERNAL), true) -BUILDER_IMG = oraclelinux:8-slim +BUILDER_IMG = oraclelinux:8 BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) else BUILDER_IMG = golang:$(GOLANG_VERSION) From 3d4a9adea0da2a62a8f206e6548d57ae9c16c6b8 Mon Sep 17 00:00:00 2001 From: matteo malvezzi Date: Tue, 12 Dec 2023 16:22:09 +0100 Subject: [PATCH 015/414] bug 36099948 --- commons/dbcssystem/dbcs_reconciler.go | 1 + .../dbcs_service_with_minimal_parameters.md | 13 +- .../dbcs_service_with_minimal_parameters.yaml | 22 +- ..._with_minimal_parameters_sample_output.log | 302 ++++++------------ 4 files changed, 112 insertions(+), 226 deletions(-) diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index d4df1ab4..34363bfe 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -94,6 +94,7 @@ func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient d dbcsDetails.CompartmentId = common.String(dbcs.Spec.DbSystem.CompartmentId) dbcsDetails.SubnetId = common.String(dbcs.Spec.DbSystem.SubnetId) dbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) + dbcsDetails.Domain = common.String(dbcs.Spec.DbSystem.Domain) if dbcs.Spec.DbSystem.DisplayName != "" { dbcsDetails.DisplayName = common.String(dbcs.Spec.DbSystem.DisplayName) } diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md index f91370ca..f6bc8185 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md @@ -8,16 +8,17 @@ This example uses `dbcs_service_with_minimal_parameters.yaml` to deploy a Single - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:PHX-AD-1` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya` +- Availability Domain for the DBCS VMDB as `OLou:EU-MILAN-1-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaaks5baeqlvv4kyj2jiwnrbxgzm3gsumcfy4c6ntj2ro5i3a5gzhhq` - Database Admin Credential as `admin-password` -- Database Name as `db0130` -- Oracle Database Software Image Version as `21c` +- Database Name as `dbsystem0130` +- Oracle Database Software Image Version as `19c` - Database Workload Type as Transaction Processing i.e. `OLTP` -- Database Hostname Prefix as `host0130` +- Database Hostname Prefix as `host1205` - Oracle VMDB Shape as `VM.Standard2.1` - SSH Public key for the DBCS system being deployed as `oci-publickey` -- OCID of the Subnet as `ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a` +- domain `vcndns.oraclevcn.com` +- OCID of the Subnet as `ocid1.subnet.oc1.eu-milan-1.aaaaaaaaeiy3tvcsnyg6upfp3ydtu7jmfnmoyifq2ax6y45b5qpdbpide5xa` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml index 9e4a5f3d..dc02cfa3 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml @@ -6,14 +6,20 @@ spec: ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + availabilityDomain: "OLou:EU-MILAN-1-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaaks5baeqlvv4kyj2jiwnrbxgzm3gsumcfy4c6ntj2ro5i3a5gzhhq" dbAdminPaswordSecret: "admin-password" - dbName: "db0130" - dbVersion: "21c" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "testdb" + displayName: "dbsystem0130" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "19c" dbWorkload: "OLTP" - hostName: "host0130" + hostName: "host1205" shape: "VM.Standard2.1" - sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + domain: "vcndns.oraclevcn.com" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.eu-milan-1.aaaaaaaaeiy3tvcsnyg6upfp3ydtu7jmfnmoyifq2ax6y45b5qpdbpide5xa" + + diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log index ddf1d81a..133f109c 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log @@ -1,257 +1,135 @@ -[root@docker-test-server test]# cat dbcs_service_with_minimal_parameters.yaml -apiVersion: database.oracle.com/v1alpha1 -kind: DbcsSystem -metadata: - name: dbcssystem-create -spec: - ociConfigMap: "oci-cred" - ociSecret: "oci-privatekey" - dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" - dbAdminPaswordSecret: "admin-password" - dbName: "db0130" - dbVersion: "21c" - dbWorkload: "OLTP" - hostName: "host0130" - shape: "VM.Standard2.1" - sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" -[root@docker-test-server test]# -[root@docker-test-server test]# kubectl apply -f dbcs_service_with_minimal_parameters.yaml -dbcssystem.database.oracle.com/dbcssystem-create created - - -[root@docker-test-server test]# kubectl get ns - -kubectl get allNAME STATUS AGE -cert-manager Active 13d -default Active 139d -kube-node-lease Active 139d -kube-public Active 139d -kube-system Active 139d -oracle-database-operator-system Active 13d -shns Active 88d -[root@docker-test-server test]# -[root@docker-test-server test]# kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 3 13d -pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 3 13d -pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 4 13d - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 13d -service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 13d - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 13d - -NAME DESIRED CURRENT READY AGE -replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 13d -[root@docker-test-server test]# -[root@docker-test-server test]# -[root@docker-test-server test]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-dlhls -n oracle-database-operator-system -. -. -2022-03-08T22:12:57.414Z INFO controller-runtime.manager.controller.dbcssystem OCI provider configured succesfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:12:58.013Z INFO controller-runtime.manager.controller.dbcssystem DbcsSystem DBSystem provisioning {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:13:05.772Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:14:06.499Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:15:07.256Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:16:07.877Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:17:08.237Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:18:08.852Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:19:09.184Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:20:10.253Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:21:10.576Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:22:10.948Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:23:11.443Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:24:11.872Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:25:12.206Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:26:12.543Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:27:13.053Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:28:13.582Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:29:13.927Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:30:14.415Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:31:14.712Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:32:15.236Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:33:16.113Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:34:16.397Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:35:16.723Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:36:17.178Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:37:17.454Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:38:17.849Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:39:18.305Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:40:18.724Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:41:19.050Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:42:19.570Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:43:19.836Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:44:20.258Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:45:20.592Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:46:20.917Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:47:21.225Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:48:21.587Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:49:21.841Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:50:22.214Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:51:22.519Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:52:22.875Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:53:23.324Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:54:23.755Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:55:24.273Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:56:24.672Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:57:25.518Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:58:26.113Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T22:59:26.373Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:00:26.650Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:01:26.895Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:02:27.437Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:03:27.835Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:04:28.231Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:05:28.653Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:06:29.286Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:07:29.637Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:08:30.061Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:09:30.327Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:10:30.838Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:11:31.312Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:12:31.755Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:13:32.075Z INFO controller-runtime.manager.controller.dbcssystem DbcsSystem system provisioned succesfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-08T23:13:34.641Z INFO controller-runtime.manager.controller.dbcssystem OCI provider configured succesfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} -2022-03-08T23:13:45.117Z INFO controller-runtime.manager.controller.dbcssystem OCI provider configured succesfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} - - -[root@docker-test-server test]# kubectl describe dbcssystems.database.oracle.com dbcssystem-create +/usr/bin/kubectl describe dbcssystems.database.oracle.com dbcssystem-create Name: dbcssystem-create Namespace: default Labels: -Annotations: lastSuccessfulSpec: - {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya","availabilityDomain":"O... +Annotations: kubectl.kubernetes.io/last-applied-configuration: + {"apiVersion":"database.oracle.com/v1alpha1","kind":"DbcsSystem","metadata":{"annotations":{},"name":"dbcssystem-create","namespace":"defa... API Version: database.oracle.com/v1alpha1 Kind: DbcsSystem Metadata: - Creation Timestamp: 2022-03-08T22:12:57Z + Creation Timestamp: 2023-12-12T12:59:58Z Generation: 1 Managed Fields: API Version: database.oracle.com/v1alpha1 Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: + Fields V 1: + F : Metadata: + F : Annotations: .: - f:kubectl.kubernetes.io/last-applied-configuration: - f:spec: + F : Kubectl . Kubernetes . Io / Last - Applied - Configuration: + F : Spec: .: - f:dbSystem: + F : Db System: .: - f:availabilityDomain: - f:compartmentId: - f:dbAdminPaswordSecret: - f:dbName: - f:dbVersion: - f:dbWorkload: - f:hostName: - f:shape: - f:sshPublicKeys: - f:subnetId: - f:ociConfigMap: - f:ociSecret: - Manager: kubectl-client-side-apply + F : Availability Domain: + F : Compartment Id: + F : Db Admin Pasword Secret: + F : Db Edition: + F : Db Name: + F : Db Version: + F : Db Workload: + F : Display Name: + F : Domain: + F : Host Name: + F : License Model: + F : Shape: + F : Ssh Public Keys: + F : Subnet Id: + F : Oci Config Map: + F : Oci Secret: + Manager: kubectl Operation: Update - Time: 2022-03-08T22:12:57Z + Time: 2023-12-12T12:59:58Z API Version: database.oracle.com/v1alpha1 Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - f:lastSuccessfulSpec: - f:spec: - f:dbSystem: - f:dbBackupConfig: - f:id: - f:status: + Fields V 1: + F : Status: .: - f:availabilityDomain: - f:cpuCoreCount: - f:dataStoragePercentage: - f:dataStorageSizeInGBs: - f:dbEdition: - f:dbInfo: - f:displayName: - f:id: - f:licenseModel: - f:network: + F : Availability Domain: + F : Cpu Core Count: + F : Data Storage Percentage: + F : Data Storage Size In G Bs: + F : Db Edition: + F : Db Info: + F : Display Name: + F : Id: + F : License Model: + F : Network: .: - f:clientSubnet: - f:domainName: - f:hostName: - f:listenerPort: - f:scanDnsName: - f:vcnName: - f:nodeCount: - f:recoStorageSizeInGB: - f:shape: - f:state: - f:storageManagement: - f:subnetId: - f:timeZone: - f:workRequests: + F : Client Subnet: + F : Domain Name: + F : Host Name: + F : Listener Port: + F : Vcn Name: + F : Node Count: + F : Reco Storage Size In GB: + F : Shape: + F : State: + F : Storage Management: + F : Subnet Id: + F : Time Zone: + F : Work Requests: Manager: manager Operation: Update - Time: 2022-03-08T23:13:34Z - Resource Version: 55187354 - UID: 0cdbebc0-3aeb-43b1-ae7f-eb36e6c56000 + Subresource: status + Time: 2023-12-12T14:12:36Z + Resource Version: 1571919 + UID: e11353f3-1334-4ca8-af31-4b638442f429 Spec: Db System: - Availability Domain: OLou:PHX-AD-1 - Compartment Id: ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya + Availability Domain: OLou:EU-MILAN-1-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaaks5baeqlvv4kyj2jiwnrbxgzm3gsumcfy4c6ntj2ro5i3a5gzhhq Db Admin Pasword Secret: admin-password - Db Name: db0130 - Db Version: 21c -apiVersion: database.oracle.com/v1alpha1 + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Name: testdb + Db Version: 19c Db Workload: OLTP - Host Name: host0130 + Display Name: dbsystem0130 + Domain: vcndns.oraclevcn.com + Host Name: host1205 + License Model: BRING_YOUR_OWN_LICENSE Shape: VM.Standard2.1 Ssh Public Keys: oci-publickey - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a + Subnet Id: ocid1.subnet.oc1.eu-milan-1.aaaaaaaaeiy3tvcsnyg6upfp3ydtu7jmfnmoyifq2ax6y45b5qpdbpide5xa Oci Config Map: oci-cred Oci Secret: oci-privatekey Status: - Availability Domain: OLou:PHX-AD-1 + Availability Domain: OLou:EU-MILAN-1-AD-1 Cpu Core Count: 1 Data Storage Percentage: 80 Data Storage Size In G Bs: 256 - Db Edition: ENTERPRISE_EDITION + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE Db Info: - Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqat52milqwt3gq6lwohhacwg5yi4mtzq7c7hag53lrkugq - Db Name: db0130 - Db Unique Name: db0130_phx1zn + Db Home Id: ocid1.dbhome.oc1.eu-milan-1.anwgsljrx2vveliazumnbyvudq3rwkbc4brtamgqzyrjuwfbtx5k7hlqwx2a + Db Name: testdb + Db Unique Name: testdb_fg4_lin Db Workload: OLTP - Id: ocid1.database.oc1.phx.anyhqljrabf7htyackgmsaqjfexoqgrzuuk33ju2q25z2al43tnd5mhhvkra - Display Name: dbsystem20220308221302 - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - License Model: LICENSE_INCLUDED + Id: ocid1.database.oc1.eu-milan-1.anwgsljrabf7htyasoe7b7mtfecc7tdfkp6w5knvvufxmk3phztxfktf6naq + Display Name: dbsystem0130 + Id: ocid1.dbsystem.oc1.eu-milan-1.anwgsljrabf7htyat3fsgcftfilt45bgbrfgawroa2oasamavsluwqyr5aya + License Model: BRING_YOUR_OWN_LICENSE Network: - Client Subnet: k8test-pubvcn - Domain Name: k8testpubvcn.k8test.oraclevcn.com - Host Name: host0130 + Client Subnet: vcnsbn + Domain Name: vcndns.oraclevcn.com + Host Name: host1205 Listener Port: 1521 - Scan Dns Name: host0130-scan.k8testpubvcn.k8test.oraclevcn.com - Vcn Name: k8test + Vcn Name: vcnnet Node Count: 1 Reco Storage Size In GB: 256 Shape: VM.Standard2.1 - State: AVAILABLE + State: PROVISIONING Storage Management: ASM - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a + Subnet Id: ocid1.subnet.oc1.eu-milan-1.aaaaaaaaeiy3tvcsnyg6upfp3ydtu7jmfnmoyifq2ax6y45b5qpdbpide5xa Time Zone: UTC Work Requests: - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrxivzvgzel47zuoyke5yk36o7mrgjl27vscd5z3bqptmyh3rxwbqq + Operation Id: ocid1.coreservicesworkrequest.oc1.eu-milan-1.abwgsljrkv6jwqtepnxyhnxgtzolw74bqfh5oqlwskq72dqgjpfs5rxu66wa Operation Type: Create DB System - Percent Complete: 100 - Time Accepted: 2022-03-08 22:13:02.999 +0000 UTC - Time Finished: 2022-03-08 23:11:50.46 +0000 UTC - Time Started: 2022-03-08 22:13:16.995 +0000 UTC + Percent Complete: 0 + Time Accepted: 2023-12-12 13:00:03.156 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.eu-milan-1.abwgsljrxudjz2qivun6ypupqhytam4axv4wago7nuauceqyapbceysjukfq + Operation Type: Create DB System + Percent Complete: 0 + Time Accepted: 2023-12-12 14:12:36.047 +0000 UTC Events: -[root@docker-test-server test]# + From a56e15b936a70a1c431f609ef51a14d420f00d14 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Tue, 12 Dec 2023 17:40:52 +0000 Subject: [PATCH 016/414] Use '[' instead of '[[' to be able to run in Shell --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 782ee9f9..32db3fe0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ FROM ${BUILDER_IMG} as builder # Download golang if BUILD_INTERNAL is set to true ARG GOLANG_VERSION -RUN if [[ -n "$GOLANG_VERSION" ]]; then \ +RUN if [ -n "$GOLANG_VERSION" ]; then \ curl -LJO https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ rm -rf /usr/local/go && tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ rm go${GOLANG_VERSION}.linux-amd64.tar.gz; \ From cd3f097bf281b03ac68f5d58788d01a9325fd5cc Mon Sep 17 00:00:00 2001 From: matteo malvezzi Date: Fri, 15 Dec 2023 15:32:31 +0100 Subject: [PATCH 017/414] issue66: add documentation page --- docs/dbcs/provisioning/database_connection.md | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/docs/dbcs/provisioning/database_connection.md b/docs/dbcs/provisioning/database_connection.md index e69de29b..66ac2b5d 100644 --- a/docs/dbcs/provisioning/database_connection.md +++ b/docs/dbcs/provisioning/database_connection.md @@ -0,0 +1,53 @@ +## Database connection + +In order to retrieve the database connection use the kubectl describe command + +```sh +kubectl describe dbcssystems.database.oracle.com dbcssystem-create +``` + +You can use the following script (tnsalias.awk) to get a simple tnsalias + +```awk +!#/usr/bin/awk +( $0 ~ / Db Unique Name:/ ) { DB_UNIQUE_NAME=$4 } +( $0 ~ /Domain Name:/ ) { DB_DOMAIN=$3 } +( $0 ~ /Host Name:/ ) { HOSTNAME=$3 } +( $0 ~ /Listener Port:/ ) { PORT=$3 } + +END { + printf ("db_unique_name=%s\n",DB_UNIQUE_NAME); + printf ("db_domain=%s\n",DB_DOMAIN); + printf ("hostname=%s\n",HOSTNAME); + printf ("port=%s\n",PORT); + printf ("====== TNSALIAS ======\n"); + printf ("(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=%s)(PORT=%s))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=%s.%s)))\n", + HOSTNAME,PORT,DB_UNIQUE_NAME,DB_DOMAIN); +``` + +```text +kubectl describe dbcssystems.database.oracle.com dbcssystem-create |awk -f tnsalias.awk +db_unique_name=testdb_fg4_lin +db_domain=vcndns.oraclevcn.com +hostname=host1205 +port=1521 +====== TNSALIAS ====== +(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=host1205)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=testdb_fg4_lin.vcndns.oraclevcn.com))) + +sqlplus scott@"(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=host1205)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=testdb_fg4_lin.vcndns.oraclevcn.com)))" + + +SQL*Plus: Release 19.0.0.0.0 - Production on Fri Dec 15 14:16:42 2023 +Version 19.15.0.0.0 + +Copyright (c) 1982, 2022, Oracle. All rights reserved. + +Enter password: +Last Successful login time: Fri Dec 15 2023 14:14:07 +00:00 + +Connected to: +Oracle Database 19c EE High Perf Release 19.0.0.0.0 - Production +Version 19.18.0.0.0 + +SQL> +``` From 08fcecbd122fbc0a4bcf659b3def9d4cc1a9abe7 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Fri, 15 Dec 2023 22:11:07 +0000 Subject: [PATCH 018/414] Support Wallet expiring date and Conditions --- README.md | 2 +- .../v1alpha1/autonomousdatabase_types.go | 18 ++- .../v1alpha1/zz_generated.deepcopy.go | 35 ++++- commons/oci/wallet.go | 20 ++- ...tabase.oracle.com_autonomousdatabases.yaml | 75 +++++++++++ .../adb/autonomousdatabase_backup.yaml | 2 +- .../database/autonomousdatabase_controller.go | 120 +++++++++++++----- docs/adb/ACD.md | 2 +- ...NUAL_BACKUP.md => ADB_ON_DEMAND_BACKUP.md} | 10 +- docs/adb/ADB_RESTORE.md | 2 +- test/e2e/resource/test_config.yaml | 2 +- 11 files changed, 234 insertions(+), 54 deletions(-) rename docs/adb/{ADB_MANUAL_BACKUP.md => ADB_ON_DEMAND_BACKUP.md} (70%) diff --git a/README.md b/README.md index c68e6c3f..517babdb 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Oracle will continue to extend `OraOperator` to support additional Oracle Databa This release of Oracle Database Operator for Kubernetes (the operator) supports the following lifecycle operations: -* ADB-S/ADB-D: Provision, Bind, Start, Stop, terminate (soft/hard), scale (up/down), Manual Backup, Manual Restore +* ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), on-demand backup, manual restore * ACD: provision, bind, restart, terminate (soft/hard) * SIDB: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (a basic observability console), Oracle REST Data Service (ORDS) to support REST based SQL, PDB management, SQL Developer Web, and Application Express (Apex) * SHARDED: Provision/deploy sharded databases and the shard topology, Add a new shard, Delete an existing shard diff --git a/apis/database/v1alpha1/autonomousdatabase_types.go b/apis/database/v1alpha1/autonomousdatabase_types.go index 413c16de..620c1d02 100644 --- a/apis/database/v1alpha1/autonomousdatabase_types.go +++ b/apis/database/v1alpha1/autonomousdatabase_types.go @@ -43,14 +43,14 @@ import ( "reflect" "github.com/oracle/oci-go-sdk/v65/database" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. // name of our custom finalizer -const ADBFinalizer = "database.oracle.com/adb-finalizer" +const ADB_FINALIZER = "database.oracle.com/adb-finalizer" // AutonomousDatabaseSpec defines the desired state of AutonomousDatabase // Important: Run "make" to regenerate code after modifying this file @@ -159,7 +159,13 @@ type AutonomousDatabaseStatus struct { // Important: Run "make" to regenerate code after modifying this file LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` TimeCreated string `json:"timeCreated,omitempty"` + WalletExpiringDate string `json:"walletExpiringDate,omitempty"` AllConnectionStrings []ConnectionStringProfile `json:"allConnectionStrings,omitempty"` + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty"` } type TLSAuthenticationEnum string @@ -192,8 +198,8 @@ type ConnectionStringSpec struct { // +kubebuilder:printcolumn:JSONPath=".spec.details.dbWorkload",name="Workload Type",type=string // +kubebuilder:printcolumn:JSONPath=".status.timeCreated",name="Created",type=string type AutonomousDatabase struct { - metaV1.TypeMeta `json:",inline"` - metaV1.ObjectMeta `json:"metadata,omitempty"` + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` Spec AutonomousDatabaseSpec `json:"spec,omitempty"` Status AutonomousDatabaseStatus `json:"status,omitempty"` @@ -203,8 +209,8 @@ type AutonomousDatabase struct { // AutonomousDatabaseList contains a list of AutonomousDatabase type AutonomousDatabaseList struct { - metaV1.TypeMeta `json:",inline"` - metaV1.ListMeta `json:"metadata,omitempty"` + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` Items []AutonomousDatabase `json:"items"` } diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index b6601b6b..752d48b9 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -543,6 +543,13 @@ func (in *AutonomousDatabaseStatus) DeepCopyInto(out *AutonomousDatabaseStatus) (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseStatus. @@ -962,6 +969,13 @@ func (in *DataguardBrokerSpec) DeepCopyInto(out *DataguardBrokerSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.ServiceAnnotations != nil { + in, out := &in.ServiceAnnotations, &out.ServiceAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.NodeSelector != nil { in, out := &in.NodeSelector, &out.NodeSelector *out = make(map[string]string, len(*in)) @@ -2349,6 +2363,21 @@ func (in *SingleInstanceDatabaseSpec) DeepCopyInto(out *SingleInstanceDatabaseSp (*out)[key] = val } } + if in.FlashBack != nil { + in, out := &in.FlashBack, &out.FlashBack + *out = new(bool) + **out = **in + } + if in.ArchiveLog != nil { + in, out := &in.ArchiveLog, &out.ArchiveLog + *out = new(bool) + **out = **in + } + if in.ForceLogging != nil { + in, out := &in.ForceLogging, &out.ForceLogging + *out = new(bool) + **out = **in + } if in.NodeSelector != nil { in, out := &in.NodeSelector, &out.NodeSelector *out = make(map[string]string, len(*in)) @@ -2359,7 +2388,11 @@ func (in *SingleInstanceDatabaseSpec) DeepCopyInto(out *SingleInstanceDatabaseSp in.AdminPassword.DeepCopyInto(&out.AdminPassword) out.Image = in.Image out.Persistence = in.Persistence - out.InitParams = in.InitParams + if in.InitParams != nil { + in, out := &in.InitParams, &out.InitParams + *out = new(SingleInstanceDatabaseInitParams) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseSpec. diff --git a/commons/oci/wallet.go b/commons/oci/wallet.go index 27ebb150..a5f9235e 100644 --- a/commons/oci/wallet.go +++ b/commons/oci/wallet.go @@ -42,6 +42,7 @@ import ( "archive/zip" "io" "io/ioutil" + "strings" ) // ExtractWallet extracts the wallet and returns a map object which holds the byte values of the unzipped files. @@ -76,27 +77,34 @@ func saveWalletZip(content io.ReadCloser) (string, error) { } func unzipWallet(path string) (map[string][]byte, error) { - data := map[string][]byte{} + files := map[string][]byte{} reader, err := zip.OpenReader(path) if err != nil { - return data, err + return files, err } defer reader.Close() for _, file := range reader.File { reader, err := file.Open() if err != nil { - return data, err + return files, err } content, err := ioutil.ReadAll(reader) if err != nil { - return data, err + return files, err } - data[file.Name] = content + files[file.Name] = content } - return data, nil + return files, nil } + +func WalletExpiringDate(files map[string][]byte) string { + data := string(files["README"]) + + line := data[strings.Index(data, "this wallet will expire on"):strings.Index(data, ".\nIn order to avoid")] + return strings.TrimSpace(strings.TrimPrefix(line, "this wallet will expire on")) +} \ No newline at end of file diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index c70b9dd6..f77407f3 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -228,6 +228,79 @@ spec: - connectionStrings type: object type: array + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map lifecycleState: description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying @@ -235,6 +308,8 @@ spec: type: string timeCreated: type: string + walletExpiringDate: + type: string type: object type: object served: true diff --git a/config/samples/adb/autonomousdatabase_backup.yaml b/config/samples/adb/autonomousdatabase_backup.yaml index ac3fb779..c134aaf6 100644 --- a/config/samples/adb/autonomousdatabase_backup.yaml +++ b/config/samples/adb/autonomousdatabase_backup.yaml @@ -7,7 +7,7 @@ kind: AutonomousDatabaseBackup metadata: name: autonomousdatabasebackup-sample spec: - # Before you can create manual backups, you must have an Object Storage bucket and your database must be configured to connect to it. This is a one-time operation. + # Before you can create on-demand backups, you must have an Object Storage bucket and your database must be configured to connect to it. This is a one-time operation. # See https://docs.oracle.com/en-us/iaas/Content/Database/Tasks/adbbackingup.htm#creatingbucket target: k8sADB: diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index de36234c..35fc2f34 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -52,8 +52,9 @@ import ( "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" - corev1 "k8s.io/api/core/v1" apiErrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -164,9 +165,9 @@ func (r *AutonomousDatabaseReconciler) eventFilterPredicate() predicate.Predicat lastSucSpecChanged := oldLastSucSpec != desiredLastSucSpec if (!specChanged && statusChanged) || lastSucSpecChanged || - (controllerutil.ContainsFinalizer(oldADB, dbv1alpha1.ADBFinalizer) != controllerutil.ContainsFinalizer(desiredADB, dbv1alpha1.ADBFinalizer)) { + (controllerutil.ContainsFinalizer(oldADB, dbv1alpha1.ADB_FINALIZER) != controllerutil.ContainsFinalizer(desiredADB, dbv1alpha1.ADB_FINALIZER)) { // Don't enqueue in the folowing condition: - // 1. only status changes 2. lastSucSpec changes 3. ADBFinalizer changes + // 1. only status changes 2. lastSucSpec changes 3. ADB_FINALIZER changes return false } @@ -278,18 +279,22 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R /****************************************************************** * Update the resource if the spec has been changed. - * Requeue if it's in an intermediate state. Update the status first - * , otherwise the modifiedADB will be overwritten by the object - * returned from the cluster. + * This will trigger another reconcile, so returns with an empty + * result. ******************************************************************/ if !reflect.DeepEqual(modifiedADB.Spec, desiredADB.Spec) { if err := r.KubeClient.Update(context.TODO(), modifiedADB); err != nil { - return r.manageError(logger.WithName("IsADBIntermediateState"), modifiedADB, err) + return r.manageError(logger.WithName("updateSpec"), modifiedADB, err) } return emptyResult, nil } + /****************************************************************** + * Update the status at the end of every reconcile. + ******************************************************************/ copiedADB := modifiedADB.DeepCopy() + + updateCondition(modifiedADB, nil) if err := r.KubeClient.Status().Update(context.TODO(), modifiedADB); err != nil { return r.manageError(logger.WithName("Status().Update"), modifiedADB, err) } @@ -308,7 +313,7 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R var requeue bool = false if modifiedADB.GetDeletionTimestamp() != nil && - controllerutil.ContainsFinalizer(modifiedADB, dbv1alpha1.ADBFinalizer) && + controllerutil.ContainsFinalizer(modifiedADB, dbv1alpha1.ADB_FINALIZER) && modifiedADB.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated { logger.Info("The ADB is TERMINATED. The CR is to be deleted but finalizer is not yet removed; reconcile queued") requeue = true @@ -350,20 +355,24 @@ func (r *AutonomousDatabaseReconciler) setupOCIClients(logger logr.Logger, adb * return nil } -func (r *AutonomousDatabaseReconciler) manageError(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase, issue error) (ctrl.Result, error) { +func (r *AutonomousDatabaseReconciler) manageError(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase, err error) (ctrl.Result, error) { l := logger.WithName("manageError") - // Has synced at least once - if adb.Status.LifecycleState != "" { - // Send event - r.Recorder.Event(adb, corev1.EventTypeWarning, "UpdateFailed", issue.Error()) + if adb.Status.LifecycleState == "" { + // First time entering reconcile + updateCondition(adb, err) - var finalIssue = issue + l.Error(err, "CreateFailed") + + return emptyResult, nil + } else { + // Has synced at least once + var finalError = err // Roll back ociADB := adb.DeepCopy() specChanged, err := r.getADB(l, ociADB) if err != nil { - finalIssue = k8s.CombineErrors(finalIssue, err) + finalError = k8s.CombineErrors(finalError, err) } // Will exit the Reconcile anyway after the manageError is called. @@ -371,25 +380,72 @@ func (r *AutonomousDatabaseReconciler) manageError(logger logr.Logger, adb *dbv1 // Clear the lifecycleState first to avoid the webhook error when update during an intermediate state adb.Status.LifecycleState = "" if err := r.KubeClient.Status().Update(context.TODO(), adb); err != nil { - finalIssue = k8s.CombineErrors(finalIssue, err) + finalError = k8s.CombineErrors(finalError, err) } adb.Spec = ociADB.Spec if err := r.KubeClient.Update(context.TODO(), adb); err != nil { - finalIssue = k8s.CombineErrors(finalIssue, err) + finalError = k8s.CombineErrors(finalError, err) } } - l.Error(finalIssue, "UpdateFailed") + updateCondition(adb, err) + + l.Error(finalError, "UpdateFailed") return emptyResult, nil + } +} + +const CONDITION_TYPE_COMPLETE = "Complete" +const CONDITION_REASON_COMPLETE = "ReconcileComplete" + +func updateCondition(adb *dbv1alpha1.AutonomousDatabase, err error) { + var condition metav1.Condition + + errMsg := func() string { + if err != nil { + return err.Error() + } + return "no reconcile errors" + }() + + // If error occurs, ReconcileComplete will be marked as true and the error message will still be listed + // If the ADB lifecycleState is intermediate, then ReconcileComplete will be marked as false + if err != nil { + condition = metav1.Condition { + Type: CONDITION_TYPE_COMPLETE, + LastTransitionTime: metav1.Now(), + ObservedGeneration: adb.GetGeneration(), + Reason: CONDITION_REASON_COMPLETE, + Message: errMsg, + Status: metav1.ConditionTrue, + } + } else if dbv1alpha1.IsADBIntermediateState(adb.Status.LifecycleState) { + condition = metav1.Condition { + Type: CONDITION_TYPE_COMPLETE, + LastTransitionTime: metav1.Now(), + ObservedGeneration: adb.GetGeneration(), + Reason: CONDITION_REASON_COMPLETE, + Message: errMsg, + Status: metav1.ConditionFalse, + } } else { - // Send event - r.Recorder.Event(adb, corev1.EventTypeWarning, "CreateFailed", issue.Error()) + condition = metav1.Condition{ + Type: CONDITION_TYPE_COMPLETE, + LastTransitionTime: metav1.Now(), + ObservedGeneration: adb.GetGeneration(), + Reason: CONDITION_REASON_COMPLETE, + Message: errMsg, + Status: metav1.ConditionTrue, + } + } - return emptyResult, issue + if len(adb.Status.Conditions) > 0 { + meta.RemoveStatusCondition(&adb.Status.Conditions, condition.Type) } + meta.SetStatusCondition(&adb.Status.Conditions, condition) } func (r *AutonomousDatabaseReconciler) validateOperation( @@ -397,7 +453,7 @@ func (r *AutonomousDatabaseReconciler) validateOperation( adb *dbv1alpha1.AutonomousDatabase, ociADB *dbv1alpha1.AutonomousDatabase) (exit bool, result ctrl.Result, err error) { - lastSpec, err := adb.GetLastSuccessfulSpec() + lastSucSpec, err := adb.GetLastSuccessfulSpec() if err != nil { return false, emptyResult, err } @@ -405,7 +461,7 @@ func (r *AutonomousDatabaseReconciler) validateOperation( l := logger.WithName("validateOperation") // If lastSucSpec is nil, then it's CREATE or BIND opertaion - if lastSpec == nil { + if lastSucSpec == nil { if adb.Spec.Details.AutonomousDatabaseOCID == nil { l.Info("Create operation") err := r.createADB(logger, adb) @@ -443,7 +499,7 @@ func (r *AutonomousDatabaseReconciler) validateOperation( // the user updates the spec (UPDATE operation), otherwise it's a SYNC operation. lastDifADB := adb.DeepCopy() - lastDetailsChanged, err := lastDifADB.RemoveUnchangedDetails(*lastSpec) + lastDetailsChanged, err := lastDifADB.RemoveUnchangedDetails(*lastSucSpec) if err != nil { return false, emptyResult, err } @@ -501,7 +557,7 @@ func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb * return false, nil } - if controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADBFinalizer) { + if controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADB_FINALIZER) { if adb.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminating { // Delete in progress, continue with the reconcile logic return false, nil @@ -511,7 +567,7 @@ func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb * // The adb has been deleted. Remove the finalizer and exit the reconcile. // Once all finalizers have been removed, the object will be deleted. l.Info("Resource is in TERMINATED state; remove the finalizer") - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADBFinalizer); err != nil { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { return false, err } return true, nil @@ -520,7 +576,7 @@ func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb * if adb.Spec.Details.AutonomousDatabaseOCID == nil { l.Info("Missing AutonomousDatabaseOCID to terminate Autonomous Database; remove the finalizer anyway", "Name", adb.Name, "Namespace", adb.Namespace) // Remove finalizer anyway. - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADBFinalizer); err != nil { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { return false, err } return true, nil @@ -552,18 +608,18 @@ func (r *AutonomousDatabaseReconciler) validateFinalizer(logger logr.Logger, adb // Delete is not schduled. Update the finalizer for this CR if hardLink is present var finalizerChanged = false if adb.Spec.HardLink != nil { - if *adb.Spec.HardLink && !controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADBFinalizer) { + if *adb.Spec.HardLink && !controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADB_FINALIZER) { l.Info("Finalizer added") - if err := k8s.AddFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADBFinalizer); err != nil { + if err := k8s.AddFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { return false, err } finalizerChanged = true - } else if !*adb.Spec.HardLink && controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADBFinalizer) { + } else if !*adb.Spec.HardLink && controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADB_FINALIZER) { l.Info("Finalizer removed") - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADBFinalizer); err != nil { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { return false, err } @@ -1221,6 +1277,8 @@ func (r *AutonomousDatabaseReconciler) validateWallet(logger logr.Logger, adb *d return err } + adb.Status.WalletExpiringDate = oci.WalletExpiringDate(data) + label := map[string]string{"app": adb.GetName()} if err := k8s.CreateSecret(r.KubeClient, adb.Namespace, walletName, data, adb, label); err != nil { diff --git a/docs/adb/ACD.md b/docs/adb/ACD.md index 28ff2101..81ee1a65 100644 --- a/docs/adb/ACD.md +++ b/docs/adb/ACD.md @@ -172,7 +172,7 @@ Here's a list of the values you can set for `action`: * `RESTART`: to restart the database * `TERMINATE`: to terminate the database -* `SYNC`: to sync the database, will describe in the next section +* `SYNC`: to sync the local database with the remote one 1. A sample .yaml file is available here: [config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml](./../../config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml) diff --git a/docs/adb/ADB_MANUAL_BACKUP.md b/docs/adb/ADB_ON_DEMAND_BACKUP.md similarity index 70% rename from docs/adb/ADB_MANUAL_BACKUP.md rename to docs/adb/ADB_ON_DEMAND_BACKUP.md index 2adec8e4..046d62ca 100644 --- a/docs/adb/ADB_MANUAL_BACKUP.md +++ b/docs/adb/ADB_ON_DEMAND_BACKUP.md @@ -1,14 +1,14 @@ -# Backing up an Oracle Autonomous Database Manually +# Creating an On-Demand Backup of an Oracle Autonomous Database -To create manual backups of Autonomous Databases, use this procedure. +To create on-demand backups of Autonomous Databases, use this procedure. -Oracle Cloud Infrastructure (OCI) automatically backs up your Autonomous Databases, and retains these backups for 60 days. You can restore and recover your database to any point-in-time in this retention period. Automatic backups are full backups taken every 60 days, with daily incremental backups. You can also create manual backups for your database. Manual backups are stored in an Object Storage bucket that you create, and are retained for 60 days. Note that Oracle doesn not recommend create or use manual backups. For more information, please visit [Backing Up and Restoring Autonomous Database](https://docs.oracle.com/en-us/iaas/Content/Database/Tasks/adbbackingup.htm) and [Backup and Restore Notes](https://docs.oracle.com/en-us/iaas/autonomous-database-shared/doc/backup-restore-notes.html). +Oracle Cloud Infrastructure (OCI) automatically backs up your Autonomous Databases, and retains these backups for 60 days. You can restore and recover your database to any point-in-time in this retention period. Automatic backups are full backups taken every 60 days, with daily incremental backups. You can also create on-demand backups for your database. On-demand backups are stored in an Object Storage bucket that you create, and are retained for 60 days. Note that Oracle doesn not recommend create or use on-demand backups. For more information, please visit [Backing Up and Restoring Autonomous Database](https://docs.oracle.com/en-us/iaas/Content/Database/Tasks/adbbackingup.htm) and [Backup and Restore Notes](https://docs.oracle.com/en-us/iaas/autonomous-database-shared/doc/backup-restore-notes.html). ## Prerequisites -To hold your Autonomous Database manual backups, you must create an Oracle Cloud Infrastructure Object Storage bucket, and configure your database to connect to it. To finish setting up manual backup storage, follow the steps in this page: [Setting Up a Bucket to Store Manual Backups](https://docs.oracle.com/en-us/iaas/Content/Database/Tasks/adbbackingup.htm#creatingbucket). Creating an Autonomous Database manual backup object storage bucket is a one-time operation. +To hold your Autonomous Database on-demand backups, you must create an Oracle Cloud Infrastructure Object Storage bucket, and configure your database to connect to it. To finish setting up on-demand backup storage, follow the steps in this page: [Setting Up a Bucket to Store Manual Backups](https://docs.oracle.com/en-us/iaas/Content/Database/Tasks/adbbackingup.htm#creatingbucket). Creating an Autonomous Database on-demand backup object storage bucket is a one-time operation. -## Create Manual Backup +## Create On-Demand Backup To back up an Autonomous Database, complete this procedure. diff --git a/docs/adb/ADB_RESTORE.md b/docs/adb/ADB_RESTORE.md index a7f67888..7a80090a 100644 --- a/docs/adb/ADB_RESTORE.md +++ b/docs/adb/ADB_RESTORE.md @@ -2,7 +2,7 @@ To restore an Autonomous Database from a backup, use this document. -You can either use any existing manual or automatic backup to restore your database, or you can restore and recover your database to any point in time in the 60-day retention period of your automatic backups. For point-in-time restores, you specify a timestamp. Your Autonomous Database identifies which backup to use for the fastest restore. +You can either use any existing on-demand or automatic backup to restore your database, or you can restore and recover your database to any point in time in the 60-day retention period of your automatic backups. For point-in-time restores, you specify a timestamp. Your Autonomous Database identifies which backup to use for the fastest restore. ## Restore an Autonomous Database diff --git a/test/e2e/resource/test_config.yaml b/test/e2e/resource/test_config.yaml index 1907af47..dc2768e2 100644 --- a/test/e2e/resource/test_config.yaml +++ b/test/e2e/resource/test_config.yaml @@ -18,7 +18,7 @@ instanceWalletPasswordOCID: ocid1.vaultsecret... subnetOCID: ocid1.subnet... # The OCID of the network security group used to test the network access settings nsgOCID: ocid1.networksecuritygroup... -# The URL of the bucket used for configure ADB manual backup +# The URL of the bucket used for configure ADB on-demand backup bucketURL: https://swiftobjectstorage.region.oraclecloud.com/v1/namespace-string/bucket_name # The auth token generated in OCI Console > Profile > User Settings > Auth Token authToken: token From 7194e30c0d5ec519dd72f933e2ae7f6bd18ba724 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Thu, 25 Jan 2024 15:29:31 +0000 Subject: [PATCH 019/414] Feature/idesai/storage expansion --- .../samples/sidb/singleinstancedatabase.yaml | 2 + .../singleinstancedatabase_controller.go | 53 ++++++++++++++++++- docs/sidb/README.md | 11 ++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 8d1a1cac..81599b92 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -88,6 +88,8 @@ spec: ## accessMode can only accept one of ReadWriteOnce, ReadWriteMany ## volumeName is optional. Specify for binding to a specific PV and set storageClass to an empty string to disable automatic volume provisioning persistence: + ## if the storageClass supports volume expansion, patch the size attribute to expand the volume + ## Shrinking volumes is not allowed size: 100Gi ## oci-bv applies to OCI block volumes. Use "standard" storageClass for dynamic provisioning in Minikube. Update as appropriate for other cloud service providers storageClass: "oci-bv" diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 253ab1e8..b2ee75dc 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -53,6 +53,7 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/resource" @@ -93,6 +94,7 @@ var oemExpressUrl string //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/finalizers,verbs=update //+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services;nodes;events,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups=storage.k8s.io,resources=storageclasses,verbs=get;list;watch // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -1221,17 +1223,23 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePVC(ctx context.Contex pvc := &corev1.PersistentVolumeClaim{} // Get retrieves an obj ( a struct pointer ) for the given object key from the Kubernetes Cluster. err := r.Get(ctx, types.NamespacedName{Name: m.Name, Namespace: m.Namespace}, pvc) + if err == nil { if *pvc.Spec.StorageClassName != m.Spec.Persistence.StorageClass || - pvc.Spec.Resources.Requests["storage"] != resource.MustParse(m.Spec.Persistence.Size) || (m.Spec.Persistence.VolumeName != "" && pvc.Spec.VolumeName != m.Spec.Persistence.VolumeName) || pvc.Spec.AccessModes[0] != corev1.PersistentVolumeAccessMode(m.Spec.Persistence.AccessMode) { - // call deletePods() with zero pods in avaiable and nil readyPod to delete all pods + // PV change use cases which would trigger recreation of SIDB pods are :- + // 1. Change in storage class + // 2. Change in volume name + // 3. Change in volume access mode + + // deleting singleinstancedatabase resource result, err := r.deletePods(ctx, req, m, []corev1.Pod{}, corev1.Pod{}, 0, 0) if result.Requeue { return result, err } + // deleting persistent volume claim log.Info("Deleting PVC", " name ", pvc.Name) err = r.Delete(ctx, pvc) if err != nil { @@ -1239,11 +1247,52 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePVC(ctx context.Contex return requeueN, err } pvcDeleted = true + + } else if pvc.Spec.Resources.Requests["storage"] != resource.MustParse(m.Spec.Persistence.Size) { + // check the storage class of the pvc + // if the storage class doesn't support resize the throw an error event and try expanding via deleting and recreating the pv and pods + if pvc.Spec.StorageClassName == nil || *pvc.Spec.StorageClassName == "" { + r.Recorder.Eventf(m, corev1.EventTypeWarning, "PVC not resizable", "Cannot resize pvc as storage class is either nil or default") + return requeueN, fmt.Errorf("cannot resize pvc as storage class is either nil or default") + } + + storageClassName := *pvc.Spec.StorageClassName + storageClass := &storagev1.StorageClass{} + err := r.Get(ctx, types.NamespacedName{Name: storageClassName}, storageClass) + if err != nil { + return requeueY, fmt.Errorf("error while fetching the storage class") + } + + if storageClass.AllowVolumeExpansion == nil || !*storageClass.AllowVolumeExpansion { + r.Recorder.Eventf(m, corev1.EventTypeWarning, "PVC not resizable", "The storage class doesn't support volume expansion") + return requeueN, fmt.Errorf("the storage class %s doesn't support volume expansion", storageClassName) + } + + newPVCSize := resource.MustParse(m.Spec.Persistence.Size) + newPVCSizeAdd := &newPVCSize + if newPVCSizeAdd.Cmp(pvc.Spec.Resources.Requests["storage"]) < 0 { + r.Recorder.Eventf(m, corev1.EventTypeWarning, "Cannot Resize PVC", "Forbidden: field can not be less than previous value") + return requeueN, fmt.Errorf("Resizing PVC to lower size volume not allowed") + } + + // Expanding the persistent volume claim + pvc.Spec.Resources.Requests["storage"] = resource.MustParse(m.Spec.Persistence.Size) + log.Info("Updating PVC", "pvc", pvc.Name, "volume", pvc.Spec.VolumeName) + r.Recorder.Eventf(m, corev1.EventTypeNormal, "Updating PVC - volume expansion", "Resizing the pvc for storage expansion") + err = r.Update(ctx, pvc) + if err != nil { + log.Error(err, "Error while updating the PVCs") + return requeueY, fmt.Errorf("error while updating the PVCs") + } + } else { + log.Info("Found Existing PVC", "Name", pvc.Name) return requeueN, nil + } } + if pvcDeleted || err != nil && apierrors.IsNotFound(err) { // Define a new PVC pvc = r.instantiatePVCSpec(m) diff --git a/docs/sidb/README.md b/docs/sidb/README.md index b749f415..8721e507 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -12,6 +12,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [Connecting to Database](#connecting-to-database) * [Database Persistence (Storage) Configuration Options](#database-persistence-storage-configuration-options) * [Dynamic Persistence](#dynamic-persistence) + * [Storage Expansion](#storage-expansion) * [Static Persistence](#static-persistence) * [Configuring a Database](#configuring-a-database) * [Switching Database Modes](#switching-database-modes) @@ -295,6 +296,16 @@ In **Dynamic Persistence Provisioning**, a persistent volume is provisioned by m - Generally, the `Reclaim Policy` of such dynamically provisioned volumes is `Delete`. These volumes are deleted when their corresponding database deployment is deleted. To retain volumes, use static provisioning, as explained in the Block Volume Static Provisioning section. - In **Minikube**, the dynamic persistence provisioning class is **standard**. +#### Storage Expansion +When using dynamic persistence, you can at any time scale up your persistent volumes by simply patching the singleinstancedatabase resource using the following command : +```sh +$ kubectl patch singleinstancedatabase sidb-sample -p '{"spec":{"persistence":{"size":"100Gi"}}}' --type=merge +``` + +**Note:** +- For storage expansion to work, the storage class should have been configured to `allowVolumeExpansion:true` +- User can only scale up a volume/storage and not scale down + #### Static Persistence In **Static Persistence Provisioning**, you have to create a volume manually, and then use the name of this volume with the `<.spec.persistence.volumeName>` field which corresponds to the `volumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. The `Reclaim Policy` of such volume can be set to `Retain`. So, this volume does not get deleted with the deletion of its corresponding deployment. For example in **Minikube**, a persistent volume can be provisioned using the sample yaml file below: From 679b60e55099e391373edf2684890b3800c4e9e1 Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Tue, 6 Feb 2024 06:37:53 +0000 Subject: [PATCH 020/414] Custom scripts --- .../v1alpha1/singleinstancedatabase_types.go | 3 +- .../singleinstancedatabase_webhook.go | 7 +- .../samples/sidb/singleinstancedatabase.yaml | 14 +- .../singleinstancedatabase_controller.go | 185 ++++++++++++++++-- docs/sidb/README.md | 12 +- 5 files changed, 199 insertions(+), 22 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index ab0d1913..264978e8 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -94,7 +94,8 @@ type SingleInstanceDatabasePersistence struct { StorageClass string `json:"storageClass,omitempty"` // +kubebuilder:validation:Enum=ReadWriteOnce;ReadWriteMany AccessMode string `json:"accessMode,omitempty"` - VolumeName string `json:"volumeName,omitempty"` + DatafilesVolumeName string `json:"datafilesVolumeName,omitempty"` + ScriptsVolumeName string `json:"scriptsVolumeName,omitempty"` VolumeClaimAnnotation string `json:"volumeClaimAnnotation,omitempty"` } diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index d4a53ec3..781f04ef 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -154,7 +154,7 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { // Persistence spec validation if r.Spec.Persistence.Size == "" && (r.Spec.Persistence.AccessMode != "" || - r.Spec.Persistence.StorageClass != "" || r.Spec.Persistence.VolumeName != "") { + r.Spec.Persistence.StorageClass != "" || r.Spec.Persistence.DatafilesVolumeName != "") { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), r.Spec.Persistence, "invalid persistence specification, specify required size")) @@ -194,6 +194,11 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { field.Invalid(field.NewPath("spec").Child("initParams"), r.Spec.InitParams, "initParams cannot be specified for standby databases")) } + if r.Spec.Persistence.ScriptsVolumeName != "" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("persistence").Child("scriptsVolumeName"), + r.Spec.Persistence.ScriptsVolumeName, "scriptsVolumeName cannot be specified for standby databases")) + } } // Replica validation diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 81599b92..d130dbef 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -83,18 +83,22 @@ spec: pullSecrets: prebuiltDB: false + + ## Database storage details ## size is the required minimum size of the persistent volume - ## storageClass is specified for automatic volume provisioning - ## accessMode can only accept one of ReadWriteOnce, ReadWriteMany - ## volumeName is optional. Specify for binding to a specific PV and set storageClass to an empty string to disable automatic volume provisioning + ## storageClass is specified for dynamic volume provisioning and datafilesVolumeName for static provisioning persistence: ## if the storageClass supports volume expansion, patch the size attribute to expand the volume ## Shrinking volumes is not allowed size: 100Gi - ## oci-bv applies to OCI block volumes. Use "standard" storageClass for dynamic provisioning in Minikube. Update as appropriate for other cloud service providers + ## oci-bv applies to OCI block volumes. Use "standard" storageClass for dynamic provisioning in Minikube. Update as appropriate for other cloud services storageClass: "oci-bv" + ## accessMode can only accept one of ReadWriteOnce, ReadWriteMany accessMode: "ReadWriteOnce" - volumeName: "" + ## datafilesVolumeName is optional. Specify for binding to a specific PV and set storageClass to an empty string to disable automatic volume provisioning + datafilesVolumeName: "" + ## Optionally specify a volume containing scripts in 'setup' and 'startup' folders to be executed during database setup and startup respectively. + scriptsVolumeName: "" ## Type of service . Applicable on cloud enviroments only ## if loadBalService : false, service type = "NodePort" else "LoadBalancer" diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index b2ee75dc..84efad66 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -93,7 +93,7 @@ var oemExpressUrl string //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services;nodes;events,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services;nodes;events;persistentvolumes,verbs=create;delete;get;list;patch;update;watch //+kubebuilder:rbac:groups=storage.k8s.io,resources=storageclasses,verbs=get;list;watch // Reconcile is part of the main kubernetes reconciliation loop which aims to @@ -169,13 +169,20 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct return result, nil } - // PVC Creation - result, err = r.createOrReplacePVC(ctx, req, singleInstanceDatabase) + // PVC Creation for Datafiles Volume + result, err = r.createOrReplacePVCforDatafilesVol(ctx, req, singleInstanceDatabase) if result.Requeue { r.Log.Info("Reconcile queued") return result, nil } + // PVC Creation for customScripts Volume + result, err = r.createOrReplacePVCforCustomScriptsVol(ctx, req, singleInstanceDatabase) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + // POD creation result, err = r.createOrReplacePods(singleInstanceDatabase, cloneFromDatabase, referredPrimaryDatabase, ctx, req) if result.Requeue { @@ -624,7 +631,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns } }(), Volumes: []corev1.Volume{{ - Name: "datamount", + Name: "datafiles-vol", VolumeSource: func() corev1.VolumeSource { if m.Spec.Persistence.Size == "" { return corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}} @@ -673,6 +680,20 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, } }(), + }, { + Name: "custom-scripts-vol", + VolumeSource: func() corev1.VolumeSource { + if m.Spec.Persistence.ScriptsVolumeName == "" || m.Spec.Persistence.ScriptsVolumeName == m.Spec.Persistence.DatafilesVolumeName { + return corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}} + } + /* Persistence.ScriptsVolumeName is specified */ + return corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: m.Name + "-" + m.Spec.Persistence.ScriptsVolumeName, + ReadOnly: false, + }, + } + }(), }}, InitContainers: func() []corev1.Container { initContainers := []corev1.Container{} @@ -687,7 +708,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, VolumeMounts: []corev1.VolumeMount{{ MountPath: "/opt/oracle/oradata", - Name: "datamount", + Name: "datafiles-vol", }}, }) if m.Spec.Image.PrebuiltDB { @@ -701,7 +722,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, VolumeMounts: []corev1.VolumeMount{{ MountPath: "/mnt/oradata", - Name: "datamount", + Name: "datafiles-vol", }}, Env: []corev1.EnvVar{ { @@ -757,7 +778,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, VolumeMounts: []corev1.VolumeMount{{ MountPath: "/opt/oracle/oradata", - Name: "datamount", + Name: "datafiles-vol", }}, }) } @@ -815,7 +836,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns if m.Spec.Persistence.Size != "" { mounts = append(mounts, corev1.VolumeMount{ MountPath: "/opt/oracle/oradata", - Name: "datamount", + Name: "datafiles-vol", }) } if m.Spec.Edition == "express" || m.Spec.Edition == "free" || m.Spec.Image.PrebuiltDB { @@ -834,6 +855,32 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns Name: "tls-secret-vol", }) } + if m.Spec.Persistence.ScriptsVolumeName != "" { + mounts = append(mounts, corev1.VolumeMount{ + MountPath: "/opt/oracle/scripts/startup/", + ReadOnly: true, + Name: func() string { + if m.Spec.Persistence.ScriptsVolumeName != m.Spec.Persistence.DatafilesVolumeName { + return "custom-scripts-vol" + } else { + return "datafiles-vol" + } + }(), + SubPath: "startup", + }) + mounts = append(mounts, corev1.VolumeMount{ + MountPath: "/opt/oracle/scripts/setup/", + ReadOnly: true, + Name: func() string { + if m.Spec.Persistence.ScriptsVolumeName != m.Spec.Persistence.DatafilesVolumeName { + return "custom-scripts-vol" + } else { + return "datafiles-vol" + } + }(), + SubPath: "setup", + }) + } return mounts }(), Env: func() []corev1.EnvVar { @@ -1180,7 +1227,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePVCSpec(m *dbapi.SingleIns }, }, StorageClassName: &m.Spec.Persistence.StorageClass, - VolumeName: m.Spec.Persistence.VolumeName, + VolumeName: m.Spec.Persistence.DatafilesVolumeName, Selector: func() *metav1.LabelSelector { if m.Spec.Persistence.StorageClass != "oci" { return nil @@ -1206,17 +1253,129 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePVCSpec(m *dbapi.SingleIns // ############################################################################# // -// Stake a claim for Persistent Volume +// Stake a claim for Persistent Volume for customScript Volume +// +// ############################################################################# + +func (r *SingleInstanceDatabaseReconciler) createOrReplacePVCforCustomScriptsVol(ctx context.Context, req ctrl.Request, + m *dbapi.SingleInstanceDatabase) (ctrl.Result, error) { + + log := r.Log.WithValues("createPVC CustomScripts Vol", req.NamespacedName) + + // if customScriptsVolumeName is not present or it is same than DatafilesVolumeName + if m.Spec.Persistence.ScriptsVolumeName == "" || m.Spec.Persistence.ScriptsVolumeName == m.Spec.Persistence.DatafilesVolumeName { + return requeueN, nil + } + + pvcDeleted := false + pvcName := string(m.Name) + "-" + string(m.Spec.Persistence.ScriptsVolumeName) + // Check if the PVC already exists using r.Get, if not create a new one using r.Create + pvc := &corev1.PersistentVolumeClaim{} + // Get retrieves an obj ( a struct pointer ) for the given object key from the Kubernetes Cluster. + err := r.Get(ctx, types.NamespacedName{Name: pvcName, Namespace: m.Namespace}, pvc) + + if err == nil { + if (m.Spec.Persistence.ScriptsVolumeName != "" && pvc.Spec.VolumeName != m.Spec.Persistence.ScriptsVolumeName) { + // call deletePods() with zero pods in avaiable and nil readyPod to delete all pods + result, err := r.deletePods(ctx, req, m, []corev1.Pod{}, corev1.Pod{}, 0, 0) + if result.Requeue { + return result, err + } + + log.Info("Deleting PVC", " name ", pvc.Name) + err = r.Delete(ctx, pvc) + if err != nil { + r.Log.Error(err, "Failed to delete Pvc", "Pvc.Name", pvc.Name) + return requeueN, err + } + pvcDeleted = true + } else { + log.Info("Found Existing PVC", "Name", pvc.Name) + return requeueN, nil + } + } + if pvcDeleted || err != nil && apierrors.IsNotFound(err) { + // Define a new PVC + + // get accessMode and storage of pv mentioned to be used in pvc spec + pv := &corev1.PersistentVolume{} + pvName := m.Spec.Persistence.ScriptsVolumeName + // Get retrieves an obj ( a struct pointer ) for the given object key from the Kubernetes Cluster. + pvErr := r.Get(ctx, types.NamespacedName{Name: pvName, Namespace: m.Namespace}, pv) + if pvErr != nil { + log.Error(pvErr, "Failed to get PV") + return requeueY, pvErr + } + + volumeQty := pv.Spec.Capacity[corev1.ResourceStorage] + + AccessMode := pv.Spec.AccessModes[0] + Storage := int(volumeQty.Value()) + StorageClass := "" + + log.Info(fmt.Sprintf("PV storage: %v\n",Storage)) + log.Info(fmt.Sprintf("PV AccessMode: %v\n",AccessMode)) + + pvc := &corev1.PersistentVolumeClaim{ + TypeMeta: metav1.TypeMeta{ + Kind: "PersistentVolumeClaim", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: pvcName , + Namespace: m.Namespace, + Labels: map[string]string{ + "app": m.Name, + }, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: func() []corev1.PersistentVolumeAccessMode { + var accessMode []corev1.PersistentVolumeAccessMode + accessMode = append(accessMode, corev1.PersistentVolumeAccessMode(AccessMode)) + return accessMode + }(), + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + // Requests describes the minimum amount of compute resources required + "storage": *resource.NewQuantity(int64(Storage), resource.BinarySI), + }, + }, + StorageClassName: &StorageClass, + VolumeName: pvName, + }, + } + + // Set SingleInstanceDatabase instance as the owner and controller + ctrl.SetControllerReference(m, pvc, r.Scheme) + + log.Info("Creating a new PVC", "PVC.Namespace", pvc.Namespace, "PVC.Name", pvc.Name) + err = r.Create(ctx, pvc) + if err != nil { + log.Error(err, "Failed to create new PVC", "PVC.Namespace", pvc.Namespace, "PVC.Name", pvc.Name) + return requeueY, err + } + return requeueN, nil + } else if err != nil { + log.Error(err, "Failed to get PVC") + return requeueY, err + } + + return requeueN, nil +} + +// ############################################################################# +// +// Stake a claim for Persistent Volume for Datafiles Volume // // ############################################################################# -func (r *SingleInstanceDatabaseReconciler) createOrReplacePVC(ctx context.Context, req ctrl.Request, +func (r *SingleInstanceDatabaseReconciler) createOrReplacePVCforDatafilesVol(ctx context.Context, req ctrl.Request, m *dbapi.SingleInstanceDatabase) (ctrl.Result, error) { + log := r.Log.WithValues("createPVC Datafiles-Vol", req.NamespacedName) + // Don't create PVC if persistence is not chosen if m.Spec.Persistence.Size == "" { return requeueN, nil } - log := r.Log.WithValues("createPVC", req.NamespacedName) pvcDeleted := false // Check if the PVC already exists using r.Get, if not create a new one using r.Create @@ -1226,7 +1385,7 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePVC(ctx context.Contex if err == nil { if *pvc.Spec.StorageClassName != m.Spec.Persistence.StorageClass || - (m.Spec.Persistence.VolumeName != "" && pvc.Spec.VolumeName != m.Spec.Persistence.VolumeName) || + (m.Spec.Persistence.DatafilesVolumeName != "" && pvc.Spec.VolumeName != m.Spec.Persistence.DatafilesVolumeName) || pvc.Spec.AccessModes[0] != corev1.PersistentVolumeAccessMode(m.Spec.Persistence.AccessMode) { // PV change use cases which would trigger recreation of SIDB pods are :- // 1. Change in storage class diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 8721e507..089f7fa2 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -31,6 +31,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [Perform a Switchover](#perform-a-switchover) * [Patch Primary and Standby databases in Data Guard configuration](#patch-primary-and-standby-databases-in-data-guard-configuration) * [Delete the Data Guard Configuration](#delete-the-data-guard-configuration) + * [Execute Custom Scripts](#execute-custom-scripts) * [OracleRestDataService Resource](#oraclerestdataservice-resource) * [REST Enable a Database](#rest-enable-a-database) * [Provision ORDS](#provision-ords) @@ -307,7 +308,7 @@ $ kubectl patch singleinstancedatabase sidb-sample -p '{"spec":{"persistence":{" - User can only scale up a volume/storage and not scale down #### Static Persistence -In **Static Persistence Provisioning**, you have to create a volume manually, and then use the name of this volume with the `<.spec.persistence.volumeName>` field which corresponds to the `volumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. The `Reclaim Policy` of such volume can be set to `Retain`. So, this volume does not get deleted with the deletion of its corresponding deployment. +In **Static Persistence Provisioning**, you have to create a volume manually, and then use the name of this volume with the `<.spec.persistence.datafilesVolumeName>` field which corresponds to the `datafilesVolumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. The `Reclaim Policy` of such volume can be set to `Retain`. So, this volume does not get deleted with the deletion of its corresponding deployment. For example in **Minikube**, a persistent volume can be provisioned using the sample yaml file below: ```yaml apiVersion: v1 @@ -323,7 +324,7 @@ spec: hostPath: path: /data/oradata ``` -The persistent volume name (i.e. db-vol) can be mentioned in the `volumeName` field of the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. `storageClass` field is not required in this case, and can be left empty. +The persistent volume name (i.e. db-vol) can be mentioned in the `datafilesVolumeName` field of the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. `storageClass` field is not required in this case, and can be left empty. Static Persistence Provisioning in Oracle Cloud Infrastructure (OCI) is explained in the following subsections: @@ -815,6 +816,13 @@ $ kubectl delete singleinstancedatabase stdby-1 singleinstancedatabase.database.oracle.com "stdby-1" deleted ``` +### Execute Custom Scripts + +Custom scripts (sql and/or shell scripts) can be executed after the initial database setup and/or after each startup of the database. SQL scripts will be executed as sysdba, shell scripts will be executed as the current user. To ensure proper order it is recommended to prefix your scripts with a number. For example `01_users.sql`, `02_permissions.sql`, etc. Place all such scripts in setup and startup folders created in a persistent volume to execute them post setup and post startup respectively. + +Create a persistent volume using [static provisioning](#static-persistence) and then specify the name of this volume with the `<.spec.persistence.scriptsVolumeName>` field which corresponds to the `scriptVolumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. + + ## OracleRestDataService Resource The Oracle Database Operator creates the `OracleRestDataService` as a custom resource. We will refer `OracleRestDataService` as ORDS from now onwards. Creating ORDS as a custom resource enables the RESTful API access to the Oracle Database in K8s and enables it to be managed as a native Kubernetes object. From 8fd63424080ed5e62b92fe57a29da817d1c450c9 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Tue, 13 Feb 2024 04:13:06 +0000 Subject: [PATCH 021/414] Feature/naberin/v1.1.0 release of DatabaseObserver v1.0.final --- .gitignore | 6 +- PROJECT | 9 + README.md | 7 + .../v1alpha1/databaseobserver_types.go | 144 ++ .../v1alpha1/groupversion_info.go | 58 + .../v1alpha1/zz_generated.deepcopy.go | 298 ++++ commons/observability/constants.go | 172 ++ commons/observability/utils.go | 402 +++++ ...vability.oracle.com_databaseobservers.yaml | 227 +++ config/crd/kustomization.yaml | 3 + .../cainjenction_in_databaseobservers.yaml | 8 + .../patches/webhook_in_databaseobservers.yaml | 17 + config/rbac/databaseobserver_editor_role.yaml | 24 + config/rbac/databaseobserver_viewer_role.yaml | 20 + config/rbac/role.yaml | 98 ++ config/samples/kustomization.yaml | 3 +- .../observability/databaseobserver.yaml | 44 + .../databaseobserver_custom_config.yaml | 28 + .../databaseobserver_minimal.yaml | 22 + .../observability/databaseobserver_vault.yaml | 25 + .../observability/sample-dashboard.json | 1414 +++++++++++++++++ .../samples/observability/sample_config.toml | 29 + .../databaseobserver_controller.go | 546 +++++++ .../databaseobserver_resource.go | 181 +++ controllers/observability/suite_test.go | 100 ++ docs/observability/README.md | 231 +++ go.mod | 8 +- go.sum | 686 +------- main.go | 25 +- 29 files changed, 4149 insertions(+), 686 deletions(-) create mode 100644 apis/observability/v1alpha1/databaseobserver_types.go create mode 100644 apis/observability/v1alpha1/groupversion_info.go create mode 100644 apis/observability/v1alpha1/zz_generated.deepcopy.go create mode 100644 commons/observability/constants.go create mode 100644 commons/observability/utils.go create mode 100644 config/crd/bases/observability.oracle.com_databaseobservers.yaml create mode 100644 config/crd/patches/cainjenction_in_databaseobservers.yaml create mode 100644 config/crd/patches/webhook_in_databaseobservers.yaml create mode 100644 config/rbac/databaseobserver_editor_role.yaml create mode 100644 config/rbac/databaseobserver_viewer_role.yaml create mode 100644 config/samples/observability/databaseobserver.yaml create mode 100644 config/samples/observability/databaseobserver_custom_config.yaml create mode 100644 config/samples/observability/databaseobserver_minimal.yaml create mode 100644 config/samples/observability/databaseobserver_vault.yaml create mode 100644 config/samples/observability/sample-dashboard.json create mode 100644 config/samples/observability/sample_config.toml create mode 100644 controllers/observability/databaseobserver_controller.go create mode 100644 controllers/observability/databaseobserver_resource.go create mode 100644 controllers/observability/suite_test.go create mode 100644 docs/observability/README.md diff --git a/.gitignore b/.gitignore index 0711d342..0af8ddc4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,8 @@ testbin/* onpremtest/* ords/*zip .gitattributes -.vscode \ No newline at end of file +.vscode + +# development +.idea +.local \ No newline at end of file diff --git a/PROJECT b/PROJECT index c402ecfd..5cbb036c 100644 --- a/PROJECT +++ b/PROJECT @@ -136,4 +136,13 @@ resources: defaulting: true validation: true webhookVersion: v1beta1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: oracle.com + group: observability + kind: DatabaseObserver + path: github.com/oracle/oracle-database-operator/apis/observability/v1alpha1 + version: v1alpha1 version: "3" diff --git a/README.md b/README.md index 517babdb..2adcb566 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ In this v1.0.0 production release, `OraOperator` supports the following database * Oracle Multitenant Databases (CDB/PDBs) * Oracle Base Database Cloud Service (BDBCS) * Oracle Data Guard (Preview status) +* Oracle Database Metrics Exporter (Preview status) Oracle will continue to extend `OraOperator` to support additional Oracle Database configurations. @@ -29,6 +30,7 @@ This release of Oracle Database Operator for Kubernetes (the operator) supports * Oracle Multitenant Database: Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB * Oracle Base Database Cloud Service (BDBCS): provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License * Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration +* Oracle Database Metrics Exporter: create, patch, delete The upcoming releases will support new configurations, operations and capabilities. @@ -97,6 +99,10 @@ The quickstarts are designed for specific database configurations: * [Oracle Multitenant Database](./docs/multitenant/README.md) * [Oracle Base Database Cloud Service (BDBCS)](./docs/dbcs/README.md) + +The quickstarts are designed for non-database configurations: +* [Oracle Database Metrics Exporter](./docs/observability/README.md) + YAML file templates are available under [`/config/samples`](./config/samples/). You can copy and edit these template files to configure them for your use cases. ## Uninstall the Operator @@ -119,6 +125,7 @@ YAML file templates are available under [`/config/samples`](./config/samples/). kubectl delete cdb.database.oracle.com --all -n kubectl delete pdb.database.oracle.com --all -n kubectl delete dataguardbrokers.database.oracle.com --all -n + kubectl delete databaseobserver.observability.oracle.com --all -n ``` After all CRD instances are deleted, it is safe to remove the CRDs, APIServices and operator deployment. To remove these files, use the following command: diff --git a/apis/observability/v1alpha1/databaseobserver_types.go b/apis/observability/v1alpha1/databaseobserver_types.go new file mode 100644 index 00000000..97827d17 --- /dev/null +++ b/apis/observability/v1alpha1/databaseobserver_types.go @@ -0,0 +1,144 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type StatusEnum string + +// DatabaseObserverSpec defines the desired state of DatabaseObserver +type DatabaseObserverSpec struct { + Database DatabaseObserverDatabase `json:"database,omitempty"` + Exporter DatabaseObserverExporterConfig `json:"exporter,omitempty"` + Prometheus PrometheusConfig `json:"prometheus,omitempty"` + OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + Replicas int32 `json:"replicas,omitempty"` +} + +// DatabaseObserverDatabase defines the database details used for DatabaseObserver +type DatabaseObserverDatabase struct { + DBUser DBSecret `json:"dbUser,omitempty"` + DBPassword DBSecretWithVault `json:"dbPassword,omitempty"` + DBWallet DBSecret `json:"dbWallet,omitempty"` + DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` +} + +// DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver +type DatabaseObserverExporterConfig struct { + ExporterImage string `json:"image,omitempty"` + ExporterConfig DatabaseObserverConfigMap `json:"configuration,omitempty"` + Service DatabaseObserverService `json:"service,omitempty"` +} + +// DatabaseObserverService defines the exporter service component of DatabaseObserver +type DatabaseObserverService struct { + Port int32 `json:"port,omitempty"` +} + +// PrometheusConfig defines the generated resources for Prometheus +type PrometheusConfig struct { + Labels map[string]string `json:"labels,omitempty"` + Port string `json:"port,omitempty"` +} + +type DBSecret struct { + Key string `json:"key,omitempty"` + SecretName string `json:"secret,omitempty"` +} + +type DBSecretWithVault struct { + Key string `json:"key,omitempty"` + SecretName string `json:"secret,omitempty"` + VaultOCID string `json:"vaultOCID,omitempty"` + VaultSecretName string `json:"vaultSecretName,omitempty"` +} + +type DatabaseObserverConfigMap struct { + Configmap ConfigMapDetails `json:"configmap,omitempty"` +} + +// ConfigMapDetails defines the configmap name +type ConfigMapDetails struct { + Key string `json:"key,omitempty"` + Name string `json:"configmapName,omitempty"` +} + +type OCIConfigSpec struct { + ConfigMapName string `json:"configMapName,omitempty"` + SecretName string `json:"secretName,omitempty"` +} + +// DatabaseObserverStatus defines the observed state of DatabaseObserver +type DatabaseObserverStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + Conditions []metav1.Condition `json:"conditions"` + Status string `json:"status,omitempty"` + ExporterConfig string `json:"exporterConfig"` + Replicas int `json:"replicas,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// DatabaseObserver is the Schema for the databaseobservers API +// +kubebuilder:printcolumn:JSONPath=".status.exporterConfig",name="ExporterConfig",type=string +// +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string +type DatabaseObserver struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DatabaseObserverSpec `json:"spec,omitempty"` + Status DatabaseObserverStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// DatabaseObserverList contains a list of DatabaseObserver +type DatabaseObserverList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DatabaseObserver `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DatabaseObserver{}, &DatabaseObserverList{}) +} diff --git a/apis/observability/v1alpha1/groupversion_info.go b/apis/observability/v1alpha1/groupversion_info.go new file mode 100644 index 00000000..304840f4 --- /dev/null +++ b/apis/observability/v1alpha1/groupversion_info.go @@ -0,0 +1,58 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Package v1alpha1 contains API Schema definitions for the observability v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=observability.oracle.com +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "observability.oracle.com", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/observability/v1alpha1/zz_generated.deepcopy.go b/apis/observability/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000..d0444b63 --- /dev/null +++ b/apis/observability/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,298 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapDetails) DeepCopyInto(out *ConfigMapDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapDetails. +func (in *ConfigMapDetails) DeepCopy() *ConfigMapDetails { + if in == nil { + return nil + } + out := new(ConfigMapDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBSecret) DeepCopyInto(out *DBSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecret. +func (in *DBSecret) DeepCopy() *DBSecret { + if in == nil { + return nil + } + out := new(DBSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBSecretWithVault) DeepCopyInto(out *DBSecretWithVault) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecretWithVault. +func (in *DBSecretWithVault) DeepCopy() *DBSecretWithVault { + if in == nil { + return nil + } + out := new(DBSecretWithVault) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserver) DeepCopyInto(out *DatabaseObserver) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserver. +func (in *DatabaseObserver) DeepCopy() *DatabaseObserver { + if in == nil { + return nil + } + out := new(DatabaseObserver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatabaseObserver) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverConfigMap) DeepCopyInto(out *DatabaseObserverConfigMap) { + *out = *in + out.Configmap = in.Configmap +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverConfigMap. +func (in *DatabaseObserverConfigMap) DeepCopy() *DatabaseObserverConfigMap { + if in == nil { + return nil + } + out := new(DatabaseObserverConfigMap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverDatabase) DeepCopyInto(out *DatabaseObserverDatabase) { + *out = *in + out.DBUser = in.DBUser + out.DBPassword = in.DBPassword + out.DBWallet = in.DBWallet + out.DBConnectionString = in.DBConnectionString +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDatabase. +func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { + if in == nil { + return nil + } + out := new(DatabaseObserverDatabase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverExporterConfig) DeepCopyInto(out *DatabaseObserverExporterConfig) { + *out = *in + out.ExporterConfig = in.ExporterConfig + out.Service = in.Service +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverExporterConfig. +func (in *DatabaseObserverExporterConfig) DeepCopy() *DatabaseObserverExporterConfig { + if in == nil { + return nil + } + out := new(DatabaseObserverExporterConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverList) DeepCopyInto(out *DatabaseObserverList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DatabaseObserver, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverList. +func (in *DatabaseObserverList) DeepCopy() *DatabaseObserverList { + if in == nil { + return nil + } + out := new(DatabaseObserverList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatabaseObserverList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverService. +func (in *DatabaseObserverService) DeepCopy() *DatabaseObserverService { + if in == nil { + return nil + } + out := new(DatabaseObserverService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { + *out = *in + out.Database = in.Database + out.Exporter = in.Exporter + in.Prometheus.DeepCopyInto(&out.Prometheus) + out.OCIConfig = in.OCIConfig +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. +func (in *DatabaseObserverSpec) DeepCopy() *DatabaseObserverSpec { + if in == nil { + return nil + } + out := new(DatabaseObserverSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverStatus. +func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { + if in == nil { + return nil + } + out := new(DatabaseObserverStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfigSpec. +func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { + if in == nil { + return nil + } + out := new(OCIConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrometheusConfig) DeepCopyInto(out *PrometheusConfig) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusConfig. +func (in *PrometheusConfig) DeepCopy() *PrometheusConfig { + if in == nil { + return nil + } + out := new(PrometheusConfig) + in.DeepCopyInto(out) + return out +} diff --git a/commons/observability/constants.go b/commons/observability/constants.go new file mode 100644 index 00000000..886fdead --- /dev/null +++ b/commons/observability/constants.go @@ -0,0 +1,172 @@ +package observability + +import "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + +const ( + UnknownValue = "UNKNOWN" + DefaultValue = "DEFAULT" +) + +// Observability Status +const ( + StatusObservabilityPending v1alpha1.StatusEnum = "PENDING" + StatusObservabilityError v1alpha1.StatusEnum = "ERROR" + StatusObservabilityReady v1alpha1.StatusEnum = "READY" +) + +// Log Names +const ( + LogReconcile = "ObservabilityExporterLogger" + LogExportersDeploy = "ObservabilityExporterDeploymentLogger" + LogExportersSVC = "ObservabilityExporterServiceLogger" + LogExportersServiceMonitor = "ObservabilityExporterServiceMonitorLogger" +) + +// Defaults +const ( + DefaultDbUserKey = "username" + DefaultDBPasswordKey = "password" + DefaultDBConnectionStringKey = "connection" + DefaultLabelKey = "app" + DefaultConfigVolumeString = "config-volume" + DefaultWalletVolumeString = "creds" + DefaultOCIPrivateKeyVolumeString = "ocikey" + DefaultOCIConfigFingerprintKey = "fingerprint" + DefaultOCIConfigRegionKey = "region" + DefaultOCIConfigTenancyKey = "tenancy" + DefaultOCIConfigUserKey = "user" + + DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:1.1.0" + DefaultServicePort = 9161 + DefaultPrometheusPort = "metrics" + DefaultReplicaCount = 1 + DefaultExporterConfigMountRootPath = "/oracle/observability" + DefaultOracleHome = "/lib/oracle/21/client64/lib" + DefaultOracleTNSAdmin = DefaultOracleHome + "/network/admin" + DefaultExporterConfigmapFilename = "config.toml" + DefaultVaultPrivateKeyRootPath = "/oracle/config" + DefaultPrivateKeyFileKey = "privatekey" + DefaultPrivateKeyFileName = "private.pem" + DefaultVaultPrivateKeyAbsolutePath = DefaultVaultPrivateKeyRootPath + "/" + DefaultPrivateKeyFileName + DefaultExporterConfigmapAbsolutePath = DefaultExporterConfigMountRootPath + "/" + DefaultExporterConfigmapFilename +) + +// default resource prefixes +const ( + DefaultServiceMonitorPrefix = "obs-servicemonitor-" + DefaultLabelPrefix = "obs-" + DefaultExporterDeploymentPrefix = "obs-deploy-" + DefaultExporterContainerName = "observability-exporter" +) + +// Known environment variables +const ( + EnvVarOracleHome = "ORACLE_HOME" + EnvVarDataSourceUser = "DB_USERNAME" + EnvVarDataSourcePassword = "DB_PASSWORD" + EnvVarDataSourceConnectString = "DB_CONNECT_STRING" + EnvVarDataSourcePwdVaultSecretName = "VAULT_SECRET_NAME" + EnvVarDataSourcePwdVaultId = "VAULT_ID" + EnvVarCustomConfigmap = "CUSTOM_METRICS" + EnvVarTNSAdmin = "TNS_ADMIN" + EnvVarVaultTenancyOCID = "vault_tenancy_ocid" + EnvVarVaultUserOCID = "vault_user_ocid" + EnvVarVaultFingerprint = "vault_fingerprint" + EnvVarVaultPrivateKeyPath = "vault_private_key_path" + EnvVarVaultRegion = "vault_region" +) + +// Positive ConditionTypes +const ( + IsCRAvailable = "ExporterReady" + IsExporterDeploymentReady = "DeploymentReady" + IsExporterServiceReady = "ServiceReady" + IsExporterServiceMonitorReady = "ServiceMonitorReady" +) + +// Reason +const ( + ReasonInitStart = "InitializationStarted" + ReasonReadyValidated = "ReadinessValidated" + ReasonValidationInProgress = "ReadinessValidationInProgress" + ReasonReadyFailed = "ReadinessValidationFailed" + ReasonDeploymentSpecValidationFailed = "SpecValidationFailed" + + ReasonDeploymentSuccessful = "ResourceDeployed" + ReasonDeploymentUpdated = "ResourceDeploymentUpdated" + ReasonDeploymentUpdateFailed = "ResourceDeploymentUpdateFailed" + ReasonDeploymentFailed = "ResourceDeploymentFailed" + ReasonDeploymentPending = "ResourceDeploymentInProgress" + + ReasonGeneralResourceGenerationFailed = "ResourceGenerationFailed" + ReasonGeneralResourceCreated = "ResourceCreated" + ReasonGeneralResourceCreationFailed = "ResourceCreationFailed" + ReasonGeneralResourceValidationCompleted = "ResourceDeployed" + ReasonGeneralResourceValidationFailureDueToError = "ResourceCouldNotBeValidated" +) + +// Log Errors +const ( + ErrorCRRetrieve = "an error occurred with retrieving the cr" + ErrorStatusUpdate = "an error occurred with updating the cr status" + ErrorSpecValidationMissingDBPassword = "an error occurred with validating the spec due to missing dbpassword values" + ErrorSpecValidationFailedDueToAnError = "an error occurred with validating the exporter deployment spec" + ErrorDeploymentPodsFailure = "an error occurred with deploying exporter deployment pods" + ErrorDeploymentUpdate = "an error occurred with updating exporter deployment" + ErrorResourceCreationFailure = "an error occurred with creating databaseobserver resource" + ErrorResourceRetrievalFailureDueToAnError = "an error occurred with retrieving databaseobserver resource" +) + +// Log Infos +const ( + LogCRStart = "Started DatabaseObserver instance reconciliation" + LogResourceCreated = "Created DatabaseObserver resource successfully" + LogResourceUpdated = "Updated DatabaseObserver resource successfully" + LogResourceFound = "Validated DatabaseObserver resource readiness" +) + +// Messages +const ( + MessageCRInitializationStarted = "Started initialization of custom resource" + MessageCRValidated = "Completed validation of custom resource readiness successfully" + MessageCRValidationFailed = "Failed to validate readiness of custom resource due to an error" + MessageCRValidationWaiting = "Waiting for other resources to be ready to fully validate readiness" + + MessageResourceCreated = "Completed creation of resource successfully" + MessageResourceCreationFailed = "Failed to create resource due to an error" + MessageResourceReadinessValidated = "Completed validation of resource readiness" + MessageResourceReadinessValidationFailed = "Failed to validate resource due to an error retrieving resource" + MessageResourceGenerationFailed = "Failed to generate resource due to an error" + + MessageExporterDeploymentSpecValidationFailed = "Failed to validate export deployment spec due to an error with the spec" + MessageExporterDeploymentImageUpdated = "Completed updating exporter deployment image successfully" + MessageExporterDeploymentEnvironmentUpdated = "Completed updating exporter deployment environment values successfully" + MessageExporterDeploymentReplicaUpdated = "Completed updating exporter deployment replicaCount successfully" + MessageExporterDeploymentVolumesUpdated = "Completed updating exporter deployment volumes successfully" + MessageExporterDeploymentUpdateFailed = "Failed to update exporter deployment due to an error" + MessageExporterDeploymentValidationFailed = "Failed to validate exporter deployment due to an error retrieving resource" + MessageExporterDeploymentSuccessful = "Completed validation of exporter deployment readiness" + MessageExporterDeploymentFailed = "Failed to deploy exporter deployment due to PodFailure" + MessageExporterDeploymentListingFailed = "Failed to list exporter deployment pods" + MessageExporterDeploymentPending = "Waiting for exporter deployment pods to be ready" +) + +// Event Recorder Outputs +const ( + EventReasonFailedCRRetrieval = "ExporterRetrievalFailed" + EventMessageFailedCRRetrieval = "Encountered error retrieving databaseObserver instance" + + EventReasonSpecError = "DeploymentSpecValidationFailed" + EventMessageSpecErrorDBPasswordMissing = "Spec validation failed due to missing dbPassword field values" + EventMessageSpecErrorDBPasswordSecretMissing = "Spec validation failed due to required dbPassword secret not found" + EventMessageSpecErrorDBConnectionStringSecretMissing = "Spec validation failed due to required dbConnectionString secret not found" + EventMessageSpecErrorDBPUserSecretMissing = "Spec validation failed due to dbUser secret not found" + EventMessageSpecErrorConfigmapMissing = "Spec validation failed due to custom config configmap not found" + EventMessageSpecErrorDBWalletSecretMissing = "Spec validation failed due to provided dbWallet secret not found" + + EventReasonUpdateSucceeded = "ExporterDeploymentUpdated" + EventMessageUpdatedImageSucceeded = "Exporter deployment image updated successfully" + EventMessageUpdatedEnvironmentSucceeded = "Exporter deployment environment values updated successfully" + EventMessageUpdatedVolumesSucceeded = "Exporter deployment volumes updated successfully" + EventMessageUpdatedReplicaSucceeded = "Exporter deployment replicaCount updated successfully" +) diff --git a/commons/observability/utils.go b/commons/observability/utils.go new file mode 100644 index 00000000..f396b95a --- /dev/null +++ b/commons/observability/utils.go @@ -0,0 +1,402 @@ +package observability + +import ( + apiv1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" +) + +// GetExporterLabels function retrieves exporter labels from api or provides default +func GetExporterLabels(api *apiv1.DatabaseObserver) map[string]string { + var l = make(map[string]string) + + if labels := api.Spec.Prometheus.Labels; labels != nil && len(labels) > 0 { + for k, v := range labels { + l[k] = v + } + l["release"] = "stable" + return l + } + return map[string]string{ + DefaultLabelKey: DefaultLabelPrefix + api.Name, + "release": "stable", + } + +} + +// GetExporterServicePort function retrieves exporter service port from api or provides default +func GetExporterServicePort(api *apiv1.DatabaseObserver) int32 { + if rPort := api.Spec.Exporter.Service.Port; rPort != 0 { + return rPort + } + return int32(DefaultServicePort) +} + +// GetExporterServiceMonitorPort function retrieves exporter service monitor port from api or provides default +func GetExporterServiceMonitorPort(api *apiv1.DatabaseObserver) string { + if rPort := api.Spec.Prometheus.Port; rPort != "" { + return rPort + } + return DefaultPrometheusPort + +} + +// GetExporterDeploymentVolumeMounts function retrieves volume mounts from api or provides default +func GetExporterDeploymentVolumeMounts(api *apiv1.DatabaseObserver) []corev1.VolumeMount { + + volM := make([]corev1.VolumeMount, 0) + + if cVolumeSourceName := api.Spec.Exporter.ExporterConfig.Configmap.Name; cVolumeSourceName != "" { + volM = append(volM, corev1.VolumeMount{ + Name: DefaultConfigVolumeString, + MountPath: DefaultExporterConfigMountRootPath, + }) + } + + // api.Spec.Database.DBWallet.SecretName optional + // if null, consider the database NON-ADB and connect as such + if secretName := api.Spec.Database.DBWallet.SecretName; secretName != "" { + volM = append(volM, corev1.VolumeMount{ + Name: DefaultWalletVolumeString, + MountPath: DefaultOracleTNSAdmin, + }) + } + + // api.Spec.OCIConfig.SecretName required if vault is used + if secretName := api.Spec.OCIConfig.SecretName; secretName != "" { + volM = append(volM, corev1.VolumeMount{ + Name: DefaultOCIPrivateKeyVolumeString, + MountPath: DefaultVaultPrivateKeyRootPath, + }) + } + return volM +} + +// GetExporterDeploymentVolumes function retrieves volumes from api or provides default +func GetExporterDeploymentVolumes(api *apiv1.DatabaseObserver) []corev1.Volume { + + vol := make([]corev1.Volume, 0) + + // config-volume Volume + // if null, the exporter uses the default built-in config + if cVolumeSourceName := api.Spec.Exporter.ExporterConfig.Configmap.Name; cVolumeSourceName != "" { + + cVolumeSourceKey := api.Spec.Exporter.ExporterConfig.Configmap.Key + cMSource := &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cVolumeSourceName, + }, + Items: []corev1.KeyToPath{{ + Key: cVolumeSourceKey, + Path: DefaultExporterConfigmapFilename, + }}, + } + + vol = append(vol, corev1.Volume{Name: DefaultConfigVolumeString, VolumeSource: corev1.VolumeSource{ConfigMap: cMSource}}) + } + + // creds Volume + // api.Spec.Database.DBWallet.SecretName optional + // if null, consider the database NON-ADB and connect as such + if secretName := api.Spec.Database.DBWallet.SecretName; secretName != "" { + + vol = append(vol, corev1.Volume{ + Name: DefaultWalletVolumeString, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: secretName, + }, + }, + }) + } + + // ocikey Volume + // api.Spec.Database.DBWallet.SecretName optional + if secretName := api.Spec.OCIConfig.SecretName; secretName != "" { + + OCIConfigSource := &corev1.SecretVolumeSource{ + SecretName: secretName, + Items: []corev1.KeyToPath{{ + Key: DefaultPrivateKeyFileKey, + Path: DefaultPrivateKeyFileName, + }}, + } + + vol = append(vol, corev1.Volume{ + Name: DefaultOCIPrivateKeyVolumeString, + VolumeSource: corev1.VolumeSource{Secret: OCIConfigSource}, + }) + } + return vol +} + +// GetExporterSelector function retrieves labels from api or provides default +func GetExporterSelector(api *apiv1.DatabaseObserver) map[string]string { + var s = make(map[string]string) + if labels := api.Spec.Prometheus.Labels; labels != nil && len(labels) > 0 { + for k, v := range labels { + s[k] = v + } + return s + + } + return map[string]string{DefaultLabelKey: DefaultLabelPrefix + api.Name} + +} + +// GetExporterEnvs function retrieves env from api or provides default +func GetExporterEnvs(api *apiv1.DatabaseObserver) []corev1.EnvVar { + + optional := true + rDBPasswordKey := api.Spec.Database.DBPassword.Key + rDBPasswordName := api.Spec.Database.DBPassword.SecretName + rDBConnectStrKey := api.Spec.Database.DBConnectionString.Key + rDBConnectStrName := api.Spec.Database.DBConnectionString.SecretName + rDBVaultSecretName := api.Spec.Database.DBPassword.VaultSecretName + rDBVaultOCID := api.Spec.Database.DBPassword.VaultOCID + rDBUserSKey := api.Spec.Database.DBUser.Key + rDBUserSName := api.Spec.Database.DBUser.SecretName + rOCIConfigCMName := api.Spec.OCIConfig.ConfigMapName + + var env = make([]corev1.EnvVar, 0) + + // DB_USERNAME environment variable + if rDBUserSKey == "" { // overwrite + rDBUserSKey = DefaultDbUserKey + } + envUser := &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: rDBUserSKey, + LocalObjectReference: corev1.LocalObjectReference{Name: rDBUserSName}, + Optional: &optional, + }} + env = append(env, corev1.EnvVar{Name: EnvVarDataSourceUser, ValueFrom: envUser}) + + // DB_CONNECT_STRING environment variable + if rDBConnectStrKey == "" { + rDBConnectStrKey = DefaultDBConnectionStringKey + } + envConnectStr := &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: rDBConnectStrKey, + LocalObjectReference: corev1.LocalObjectReference{Name: rDBConnectStrName}, + Optional: &optional, + }} + env = append(env, corev1.EnvVar{Name: EnvVarDataSourceConnectString, ValueFrom: envConnectStr}) + + // DB_PASSWORD environment variable + // if useVault, add environment variables for Vault ID and Vault Secret Name + useVault := rDBVaultSecretName != "" && rDBVaultOCID != "" + if useVault { + + env = append(env, corev1.EnvVar{Name: EnvVarDataSourcePwdVaultSecretName, Value: rDBVaultSecretName}) + env = append(env, corev1.EnvVar{Name: EnvVarDataSourcePwdVaultId, Value: rDBVaultOCID}) + + // Configuring the configProvider prefixed with vault_ + // https://github.com/oracle/oracle-db-appdev-monitoring/blob/main/vault/vault.go + configSourceFingerprintValue := &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + Key: DefaultOCIConfigFingerprintKey, + LocalObjectReference: corev1.LocalObjectReference{Name: rOCIConfigCMName}, + Optional: &optional, + }, + } + configSourceRegionValue := &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + Key: DefaultOCIConfigRegionKey, + LocalObjectReference: corev1.LocalObjectReference{Name: rOCIConfigCMName}, + Optional: &optional, + }, + } + configSourceTenancyValue := &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + Key: DefaultOCIConfigTenancyKey, + LocalObjectReference: corev1.LocalObjectReference{Name: rOCIConfigCMName}, + Optional: &optional, + }, + } + configSourceUserValue := &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + Key: DefaultOCIConfigUserKey, + LocalObjectReference: corev1.LocalObjectReference{Name: rOCIConfigCMName}, + Optional: &optional, + }, + } + + env = append(env, corev1.EnvVar{Name: EnvVarVaultFingerprint, ValueFrom: configSourceFingerprintValue}) + env = append(env, corev1.EnvVar{Name: EnvVarVaultUserOCID, ValueFrom: configSourceUserValue}) + env = append(env, corev1.EnvVar{Name: EnvVarVaultTenancyOCID, ValueFrom: configSourceTenancyValue}) + env = append(env, corev1.EnvVar{Name: EnvVarVaultRegion, ValueFrom: configSourceRegionValue}) + env = append(env, corev1.EnvVar{Name: EnvVarVaultPrivateKeyPath, Value: DefaultVaultPrivateKeyAbsolutePath}) + + } else { + + if rDBPasswordKey == "" { // overwrite + rDBPasswordKey = DefaultDBPasswordKey + } + dbPassword := &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: rDBPasswordKey, + LocalObjectReference: corev1.LocalObjectReference{Name: rDBPasswordName}, + Optional: &optional, + }} + + env = append(env, corev1.EnvVar{Name: EnvVarDataSourcePassword, ValueFrom: dbPassword}) + + } + + // CUSTOM_METRICS environment variable + if customMetricsName := api.Spec.Exporter.ExporterConfig.Configmap.Name; customMetricsName != "" { + customMetrics := DefaultExporterConfigmapAbsolutePath + env = append(env, corev1.EnvVar{Name: EnvVarCustomConfigmap, Value: customMetrics}) + } + + env = append(env, corev1.EnvVar{Name: EnvVarOracleHome, Value: DefaultOracleHome}) + env = append(env, corev1.EnvVar{Name: EnvVarTNSAdmin, Value: DefaultOracleTNSAdmin}) + return env +} + +// GetExporterReplicas function retrieves replicaCount from api or provides default +func GetExporterReplicas(api *apiv1.DatabaseObserver) int32 { + if rc := api.Spec.Replicas; rc != 0 { + return rc + } + return int32(DefaultReplicaCount) +} + +// GetExporterImage function retrieves image from api or provides default +func GetExporterImage(api *apiv1.DatabaseObserver) string { + if img := api.Spec.Exporter.ExporterImage; img != "" { + return img + } + return DefaultExporterImage + +} + +func IsUpdateRequiredForContainerImage(desired *appsv1.Deployment, found *appsv1.Deployment) bool { + foundImage := found.Spec.Template.Spec.Containers[0].Image + desiredImage := desired.Spec.Template.Spec.Containers[0].Image + + return foundImage != desiredImage +} + +func IsUpdateRequiredForEnvironmentVars(desired *appsv1.Deployment, found *appsv1.Deployment) bool { + var updateEnvsRequired bool + desiredEnvValues := make(map[string]string) + + foundEnvs := found.Spec.Template.Spec.Containers[0].Env + desiredEnvs := desired.Spec.Template.Spec.Containers[0].Env + if len(foundEnvs) != len(desiredEnvs) { + updateEnvsRequired = true + } else { + for _, v := range desiredEnvs { + + if v.Name == EnvVarDataSourceUser || + v.Name == EnvVarDataSourceConnectString || + v.Name == EnvVarDataSourcePassword { + + ref := *(*v.ValueFrom).SecretKeyRef + desiredEnvValues[v.Name] = ref.Key + "-" + ref.Name + + } else if v.Name == EnvVarVaultFingerprint || + v.Name == EnvVarVaultRegion || + v.Name == EnvVarVaultTenancyOCID || + v.Name == EnvVarVaultUserOCID { + + ref := *(*v.ValueFrom).ConfigMapKeyRef + desiredEnvValues[v.Name] = ref.Key + "-" + ref.Name + + } else if v.Name == EnvVarDataSourcePwdVaultId || + v.Name == EnvVarDataSourcePwdVaultSecretName || + v.Name == EnvVarCustomConfigmap { + + desiredEnvValues[v.Name] = v.Value + } + } + + for _, v := range foundEnvs { + var foundValue string + + if v.Name == EnvVarDataSourceUser || + v.Name == EnvVarDataSourceConnectString || + v.Name == EnvVarDataSourcePassword { + + ref := *(*v.ValueFrom).SecretKeyRef + foundValue = ref.Key + "-" + ref.Name + + } else if v.Name == EnvVarVaultFingerprint || + v.Name == EnvVarVaultRegion || + v.Name == EnvVarVaultTenancyOCID || + v.Name == EnvVarVaultUserOCID { + + ref := *(*v.ValueFrom).ConfigMapKeyRef + foundValue = ref.Key + "-" + ref.Name + + } else if v.Name == EnvVarDataSourcePwdVaultId || + v.Name == EnvVarDataSourcePwdVaultSecretName || + v.Name == EnvVarCustomConfigmap { + + foundValue = v.Value + } + + if desiredEnvValues[v.Name] != foundValue { + updateEnvsRequired = true + } + } + } + return updateEnvsRequired +} + +func IsUpdateRequiredForVolumes(desired *appsv1.Deployment, found *appsv1.Deployment) bool { + var updateVolumesRequired bool + var foundConfigmap, desiredConfigmap string + var foundWalletSecret, desiredWalletSecret string + var foundOCIConfig, desiredOCIConfig string + + desiredVolumes := desired.Spec.Template.Spec.Volumes + foundVolumes := found.Spec.Template.Spec.Volumes + + if len(desiredVolumes) != len(foundVolumes) { + updateVolumesRequired = true + } else { + for _, v := range desiredVolumes { + if v.Name == DefaultConfigVolumeString { + desiredConfigmap = v.ConfigMap.Name + for _, key := range v.ConfigMap.Items { + desiredConfigmap += key.Key + } + } else if v.Name == DefaultWalletVolumeString { + desiredWalletSecret = v.VolumeSource.Secret.SecretName + + } else if v.Name == DefaultOCIPrivateKeyVolumeString { + desiredOCIConfig = v.VolumeSource.Secret.SecretName + } + } + + for _, v := range foundVolumes { + if v.Name == DefaultConfigVolumeString { + foundConfigmap = v.ConfigMap.Name + for _, key := range v.ConfigMap.Items { + foundConfigmap += key.Key + } + } else if v.Name == DefaultWalletVolumeString { + foundWalletSecret = v.VolumeSource.Secret.SecretName + + } else if v.Name == DefaultOCIPrivateKeyVolumeString { + foundOCIConfig = v.VolumeSource.Secret.SecretName + } + } + } + + return updateVolumesRequired || + desiredConfigmap != foundConfigmap || + desiredWalletSecret != foundWalletSecret || + desiredOCIConfig != foundOCIConfig +} + +func IsUpdateRequiredForReplicas(desired *appsv1.Deployment, found *appsv1.Deployment) bool { + foundReplicas := *found.Spec.Replicas + desiredReplicas := *desired.Spec.Replicas + + return desiredReplicas != foundReplicas +} diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml new file mode 100644 index 00000000..b0801738 --- /dev/null +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -0,0 +1,227 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver + properties: + database: + description: DatabaseObserverDatabase defines the database details + used for DatabaseObserver + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + description: DatabaseObserverExporterConfig defines the configuration + details related to the exporters of DatabaseObserver + properties: + configuration: + properties: + configmap: + description: ConfigMapDetails defines the configmap name + properties: + configmapName: + type: string + key: + type: string + type: object + type: object + image: + type: string + service: + description: DatabaseObserverService defines the exporter service + component of DatabaseObserver + properties: + port: + format: int32 + type: integer + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + description: PrometheusConfig defines the generated resources for + Prometheus + properties: + labels: + additionalProperties: + type: string + type: object + port: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver + properties: + conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + required: + - conditions + - exporterConfig + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index c411e3ed..b9f3aa8c 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -17,6 +17,7 @@ resources: - bases/database.oracle.com_autonomouscontainerdatabases.yaml - bases/database.oracle.com_dbcssystems.yaml - bases/database.oracle.com_dataguardbrokers.yaml +- bases/observability.oracle.com_databaseobservers.yaml # +kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -32,6 +33,7 @@ patchesStrategicMerge: #- patches/webhook_in_autonomouscontainerdatabases.yaml #- patches/webhook_in_dbcssystems.yaml #- patches/webhook_in_dataguardbrokers.yaml +#- patches/webhook_in_databaseobservers.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. @@ -46,6 +48,7 @@ patchesStrategicMerge: #- patches/cainjection_in_autonomouscontainerdatabases.yaml #- patches/cainjection_in_dbcssystems.yaml #- patches/cainjection_in_dataguardbrokers.yaml +#- patches/cainjection_in_databaseobservers.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjenction_in_databaseobservers.yaml b/config/crd/patches/cainjenction_in_databaseobservers.yaml new file mode 100644 index 00000000..278ea7fa --- /dev/null +++ b/config/crd/patches/cainjenction_in_databaseobservers.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: databaseobservers.observability.oracle.com \ No newline at end of file diff --git a/config/crd/patches/webhook_in_databaseobservers.yaml b/config/crd/patches/webhook_in_databaseobservers.yaml new file mode 100644 index 00000000..e61411df --- /dev/null +++ b/config/crd/patches/webhook_in_databaseobservers.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: databaseobservers.observability.oracle.com +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert \ No newline at end of file diff --git a/config/rbac/databaseobserver_editor_role.yaml b/config/rbac/databaseobserver_editor_role.yaml new file mode 100644 index 00000000..900c4b88 --- /dev/null +++ b/config/rbac/databaseobserver_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit databaseobservers. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: databaseobserver-editor-role +rules: + - apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get \ No newline at end of file diff --git a/config/rbac/databaseobserver_viewer_role.yaml b/config/rbac/databaseobserver_viewer_role.yaml new file mode 100644 index 00000000..ef447b21 --- /dev/null +++ b/config/rbac/databaseobserver_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view databaseobservers. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: databaseobserver-viewer-role +rules: + - apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - get + - list + - watch + - apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get \ No newline at end of file diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 89b630dd..1467c250 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -6,6 +6,23 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - "" + resources: + - configmaps + - deployments + - events + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -51,6 +68,25 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - events + - nodes + - persistentvolumeclaims + - persistentvolumes + - pods + - pods/exec + - pods/log + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -81,6 +117,21 @@ rules: - patch - update - watch +- apiGroups: + - apps + resources: + - configmaps + - deployments + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - apps resources: @@ -428,3 +479,50 @@ rules: - get - patch - update +- apiGroups: + - monitoring.coreos.com + resources: + - prometheusrules + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/finalizers + verbs: + - update +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get + - patch + - update +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 0ad50fcf..3bf0eab5 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -16,5 +16,6 @@ resources: - sharding/shardingdatabase.yaml - sharding/sharding_v1alpha1_provshard.yaml - dbcs/database_v1alpha1_dbcssystem.yaml -- database_v1alpha1_dataguardbroker.yaml + - database_v1alpha1_dataguardbroker.yaml + - observability/databaseobserver.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/observability/databaseobserver.yaml b/config/samples/observability/databaseobserver.yaml new file mode 100644 index 00000000..b3140549 --- /dev/null +++ b/config/samples/observability/databaseobserver.yaml @@ -0,0 +1,44 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + exporter: + image: "container-registry.oracle.com/database/observability-exporter:latest" + configuration: + configmap: + key: "config.toml" + configmapName: "devcm-oradevdb-config" + + service: + port: 9161 + + prometheus: + port: metrics + labels: + app: app-sample-label + + replicas: 1 + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + + diff --git a/config/samples/observability/databaseobserver_custom_config.yaml b/config/samples/observability/databaseobserver_custom_config.yaml new file mode 100644 index 00000000..1e9fff47 --- /dev/null +++ b/config/samples/observability/databaseobserver_custom_config.yaml @@ -0,0 +1,28 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample + namespace: observer +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + exporter: + configuration: + configmap: + key: "config.toml" + configmapName: "devcm-oradevdb-config" \ No newline at end of file diff --git a/config/samples/observability/databaseobserver_minimal.yaml b/config/samples/observability/databaseobserver_minimal.yaml new file mode 100644 index 00000000..2eeaf3ab --- /dev/null +++ b/config/samples/observability/databaseobserver_minimal.yaml @@ -0,0 +1,22 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample + namespace: observer +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallets \ No newline at end of file diff --git a/config/samples/observability/databaseobserver_vault.yaml b/config/samples/observability/databaseobserver_vault.yaml new file mode 100644 index 00000000..fa2e09d4 --- /dev/null +++ b/config/samples/observability/databaseobserver_vault.yaml @@ -0,0 +1,25 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + vaultSecretName: sample_secret + vaultOCID: ocid1.vault.oc1.. + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/observability/sample-dashboard.json b/config/samples/observability/sample-dashboard.json new file mode 100644 index 00000000..5b05b05c --- /dev/null +++ b/config/samples/observability/sample-dashboard.json @@ -0,0 +1,1414 @@ +{ + "__inputs": [ + { + "name": "Prometheus", + "label": "prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.5.1" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 5, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 3, + "panels": [], + "title": "Oracle Database Details", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 1, + "text": "DEAD" + }, + "1": { + "index": 0, + "text": "ALIVE" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 10, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "oracledb_up", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Database Status", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 4, + "y": 1 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "oracledb_obaas_db_system_value{name=\"sga_max_size\"}", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "SGA Max Size", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 7, + "y": 1 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "oracledb_obaas_db_system_value{name=\"pga_aggregate_limit\"}", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "PGA Aggregate Limit", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 10, + "y": 1 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "oracledb_sessions_value{status=\"ACTIVE\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Active Sessions", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 15, + "y": 1 + }, + "id": 12, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "oracledb_activity_user_commits", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "User commits", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 18, + "y": 1 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "oracledb_activity_execute_count", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Execute count", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 21, + "y": 1 + }, + "id": 7, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": false + }, + "pluginVersion": "9.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": false, + "expr": "max(oracledb_obaas_db_platform_value) by (platform_name)", + "format": "table", + "instant": true, + "legendFormat": "{{platform_name}}", + "range": false, + "refId": "A" + } + ], + "title": "Database Platform", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Value": true + }, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "table" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 21, + "y": 4 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "oracledb_obaas_db_system_value{name=\"cpu_count\"}", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "CPU Count", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 7 + }, + "id": 8, + "panels": [], + "title": "Top SQL", + "type": "row" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "SQL ID" + }, + "properties": [ + { + "id": "custom.width", + "value": 226 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "SQL Text (extract)" + }, + "properties": [ + { + "id": "custom.width", + "value": 1311 + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 9, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "9.5.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": false, + "expr": "max(oracledb_obaas_top_sql_elapsed) by (sql_id, sql_text)", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Top SQL by elapsed time running", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "indexByName": {}, + "renameByName": { + "Value": "Elapsed Time", + "sql_id": "SQL ID", + "sql_text": "SQL Text (extract)" + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Elapsed Time" + } + ] + } + } + ], + "type": "table" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 4, + "panels": [], + "title": "System Wait Classes", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 19, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "µs" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "oracledb_wait_time_concurrency", + "interval": "$interval", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Wait time - Concurrency", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "µs" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "oracledb_wait_time_commit", + "interval": "$interval", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Wait time - Commit", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "µs" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 26 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "oracledb_wait_time_system_io", + "interval": "$interval", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Wait time - System I/O", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "µs" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 26 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "oracledb_wait_time_user_io", + "interval": "$interval", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Wait time - User I/O", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "µs" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 34 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "oracledb_wait_time_application", + "interval": "$interval", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Wait time - Application", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "µs" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 34 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "oracledb_wait_time_network", + "interval": "$interval", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Wait time - Network", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "auto": true, + "auto_count": 200, + "auto_min": "10s", + "current": { + "selected": false, + "text": "auto", + "value": "$__auto_interval_interval" + }, + "hide": 0, + "label": "Interval", + "name": "interval", + "options": [ + { + "selected": true, + "text": "auto", + "value": "$__auto_interval_interval" + }, + { + "selected": false, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "10m", + "value": "10m" + }, + { + "selected": false, + "text": "30m", + "value": "30m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + }, + { + "selected": false, + "text": "6h", + "value": "6h" + }, + { + "selected": false, + "text": "12h", + "value": "12h" + }, + { + "selected": false, + "text": "1d", + "value": "1d" + }, + { + "selected": false, + "text": "7d", + "value": "7d" + }, + { + "selected": false, + "text": "14d", + "value": "14d" + }, + { + "selected": false, + "text": "30d", + "value": "30d" + } + ], + "query": "1m,10m,30m,1h,6h,12h,1d,7d,14d,30d", + "refresh": 2, + "skipUrlSync": false, + "type": "interval" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Oracle Dashboard", + "uid": "obaas_oracle_dashboard", + "version": 20, + "weekStart": "" +} \ No newline at end of file diff --git a/config/samples/observability/sample_config.toml b/config/samples/observability/sample_config.toml new file mode 100644 index 00000000..0989d769 --- /dev/null +++ b/config/samples/observability/sample_config.toml @@ -0,0 +1,29 @@ +[[metric]] +context = "obaas_db_system" +labels = [ "name" ] +metricsdesc = { value = "Database system resources metric" } +request = ''' +select name, value +from v$parameter +where name in ('cpu_count', 'sga_max_size', 'pga_aggregate_limit') +''' + +[[metric]] +context = "obaas_db_platform" +labels = [ "platform_name" ] +metricsdesc = { value = "Database platform" } +request = ''' +SELECT platform_name, 1 as value FROM v$database +''' + +[[metric]] +context = "obaas_top_sql" +labels = [ "sql_id", "sql_text" ] +metricsdesc = { elapsed = "SQL statement elapsed time running" } +request = ''' +select * from ( +select sql_id, elapsed_time / 1000000 as elapsed, SUBSTRB(REPLACE(sql_text,'',' '),1,55) as sql_text +from V$SQLSTATS +order by elapsed_time desc +) where ROWNUM <= 15 +''' diff --git a/controllers/observability/databaseobserver_controller.go b/controllers/observability/databaseobserver_controller.go new file mode 100644 index 00000000..859540ef --- /dev/null +++ b/controllers/observability/databaseobserver_controller.go @@ -0,0 +1,546 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "context" + "errors" + "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apiError "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "time" + + apiv1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + constants "github.com/oracle/oracle-database-operator/commons/observability" +) + +// DatabaseObserverReconciler reconciles a DatabaseObserver object +type DatabaseObserverReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + Recorder record.EventRecorder +} + +//+kubebuilder:rbac:groups=observability.oracle.com,resources=databaseobservers,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=observability.oracle.com,resources=databaseobservers/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=observability.oracle.com,resources=databaseobservers/finalizers,verbs=update +//+kubebuilder:rbac:groups=apps,resources=pods;deployments;services;configmaps,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="",resources=pods;deployments;services;secrets;configmaps;events,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=monitoring.coreos.com,resources=prometheusrules;servicemonitors,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the DatabaseObserver object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.6.4/pkg/reconcile +func (r *DatabaseObserverReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + r.Log.WithName(constants.LogReconcile).Info(constants.LogCRStart, "NamespacedName", req.NamespacedName) + + // fetch databaseObserver + api := &apiv1.DatabaseObserver{} + if e := r.Get(context.TODO(), req.NamespacedName, api); e != nil { + + r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorCRRetrieve) + r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonFailedCRRetrieval, constants.EventMessageFailedCRRetrieval) + return ctrl.Result{}, e + + } + + // evaluate overall custom resource readiness at the end of the stack + defer r.validateCustomResourceReadiness(ctx, req) + + // initialize databaseObserver custom resource + if e := r.initialize(ctx, api, req); e != nil { + return ctrl.Result{}, e + } + + // validate specs + if e := r.validateSpecs(api); e != nil { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterDeploymentReady, + Status: metav1.ConditionFalse, + Reason: constants.ReasonDeploymentSpecValidationFailed, + Message: constants.MessageExporterDeploymentSpecValidationFailed, + }) + if e := r.Status().Update(ctx, api); e != nil { + r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorStatusUpdate) + } + r.Log.WithName(constants.LogExportersDeploy).Error(e, constants.ErrorSpecValidationFailedDueToAnError) + return ctrl.Result{}, e + } + + // create resource if they do not exist + exporterDeployment := &ObservabilityDeploymentResource{} + if res, e := r.createResourceIfNotExists(exporterDeployment, api, ctx, req); e != nil { + return res, e + } + + if res, e := r.checkDeploymentForUpdates(exporterDeployment, api, ctx, req); e != nil { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterDeploymentReady, + Status: metav1.ConditionFalse, + Reason: constants.ReasonDeploymentUpdateFailed, + Message: constants.MessageExporterDeploymentUpdateFailed, + }) + return res, e + } + + exporterService := &ObservabilityServiceResource{} + if res, e := r.createResourceIfNotExists(exporterService, api, ctx, req); e != nil { + return res, e + } + + exporterServiceMonitor := &ObservabilityServiceMonitorResource{} + if res, e := r.createResourceIfNotExists(exporterServiceMonitor, api, ctx, req); e != nil { + return res, e + } + + // check if deployment pods are ready + return r.validateDeploymentReadiness(api, ctx, req) +} + +// initialize method sets the initial status to PENDING, exporterConfig and sets the base condition +func (r *DatabaseObserverReconciler) initialize(ctx context.Context, api *apiv1.DatabaseObserver, req ctrl.Request) error { + + if api.Status.Conditions == nil || len(api.Status.Conditions) == 0 { + + // set condition + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsCRAvailable, + Status: metav1.ConditionFalse, + Reason: constants.ReasonInitStart, + Message: constants.MessageCRInitializationStarted, + }) + + api.Status.Status = string(constants.StatusObservabilityPending) + api.Status.ExporterConfig = constants.UnknownValue + if e := r.Status().Update(ctx, api); e != nil { + r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorStatusUpdate) + return e + } + + } + + return nil +} + +// validateSpecs method checks the values and secrets passed in the spec +func (r *DatabaseObserverReconciler) validateSpecs(api *apiv1.DatabaseObserver) error { + + // If all of {DB Password Secret Name and vaultOCID+vaultSecretName} have no value, then error out + if api.Spec.Database.DBPassword.SecretName == "" && + api.Spec.Database.DBPassword.VaultOCID == "" && + api.Spec.Database.DBPassword.VaultSecretName == "" { + r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPasswordMissing) + return errors.New(constants.ErrorSpecValidationMissingDBPassword) + } + + // If either Vault Fields are empty, then assume a DBPassword secret is supplied. If the DBPassword secret not found, then error out + if api.Spec.Database.DBPassword.VaultOCID == "" || api.Spec.Database.DBPassword.VaultSecretName == "" { + dbSecret := &corev1.Secret{} + if e := r.Get(context.TODO(), types.NamespacedName{Name: api.Spec.Database.DBPassword.SecretName, Namespace: api.Namespace}, dbSecret); e != nil { + r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPasswordSecretMissing) + return e + } + } + + // Does DB Connection String Secret Name actually exist + dbConnectSecret := &corev1.Secret{} + if e := r.Get(context.TODO(), types.NamespacedName{Name: api.Spec.Database.DBConnectionString.SecretName, Namespace: api.Namespace}, dbConnectSecret); e != nil { + r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBConnectionStringSecretMissing) + return e + } + + // Does DB User String Secret Name actually exist + dbUserSecret := &corev1.Secret{} + if e := r.Get(context.TODO(), types.NamespacedName{Name: api.Spec.Database.DBUser.SecretName, Namespace: api.Namespace}, dbUserSecret); e != nil { + r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPUserSecretMissing) + return e + } + + // Does a custom configuration configmap actually exist, if provided + if configurationCMName := api.Spec.Exporter.ExporterConfig.Configmap.Name; configurationCMName != "" { + configurationCM := &corev1.ConfigMap{} + if e := r.Get(context.TODO(), types.NamespacedName{Name: configurationCMName, Namespace: api.Namespace}, configurationCM); e != nil { + r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorConfigmapMissing) + return e + } + } + + // Does DBWallet actually exist, if provided + if dbWalletSecretName := api.Spec.Database.DBWallet.SecretName; dbWalletSecretName != "" { + dbWalletSecret := &corev1.Secret{} + if e := r.Get(context.TODO(), types.NamespacedName{Name: dbWalletSecretName, Namespace: api.Namespace}, dbWalletSecret); e != nil { + r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBWalletSecretMissing) + return e + } + } + + return nil // valid, did not encounter any errors +} + +// createResourceIfNotExists method creates an ObserverResource if they have not yet been created +func (r *DatabaseObserverReconciler) createResourceIfNotExists(or ObserverResource, api *apiv1.DatabaseObserver, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + conditionType, logger, groupVersionKind := or.identify() + + // update after + defer r.Status().Update(ctx, api) + + // generate desired object based on api.Spec + desiredObj, genErr := or.generate(api, r.Scheme) + if genErr != nil { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: conditionType, + Status: metav1.ConditionFalse, + Reason: constants.ReasonGeneralResourceGenerationFailed, + Message: constants.MessageResourceGenerationFailed, + }) + return ctrl.Result{}, genErr + } + + // if resource exists, retrieve the resource + foundObj := &unstructured.Unstructured{} + foundObj.SetGroupVersionKind(groupVersionKind) + getErr := r.Get(context.TODO(), types.NamespacedName{Name: desiredObj.GetName(), Namespace: req.Namespace}, foundObj) + + // if resource not found, create resource then return + if getErr != nil && apiError.IsNotFound(getErr) { + + if e := r.Create(context.TODO(), desiredObj); e != nil { // create + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: conditionType, + Status: metav1.ConditionFalse, + Reason: constants.ReasonGeneralResourceCreationFailed, + Message: constants.MessageResourceCreationFailed, + }) + r.Log.WithName(logger).Error(e, constants.ErrorResourceCreationFailure, "ResourceName", desiredObj.GetName(), "Kind", groupVersionKind, "Namespace", req.Namespace) + return ctrl.Result{}, e + } + + // mark ready if created + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: conditionType, + Status: metav1.ConditionTrue, + Reason: constants.ReasonGeneralResourceCreated, + Message: constants.MessageResourceCreated, + }) + r.Log.WithName(logger).Info(constants.LogResourceCreated, "ResourceName", desiredObj.GetName(), "Kind", groupVersionKind, "Namespace", req.Namespace) + + } else if getErr != nil { // if an error occurred + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: conditionType, + Status: metav1.ConditionFalse, + Reason: constants.ReasonGeneralResourceValidationFailureDueToError, + Message: constants.MessageResourceReadinessValidationFailed, + }) + r.Log.WithName(logger).Error(getErr, constants.ErrorResourceRetrievalFailureDueToAnError, "ResourceName", desiredObj.GetName(), "Kind", groupVersionKind, "Namespace", req.Namespace) + return ctrl.Result{}, getErr + + } else if getErr == nil && conditionType != constants.IsExporterDeploymentReady { // exclude deployment + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: conditionType, + Status: metav1.ConditionTrue, + Reason: constants.ReasonGeneralResourceValidationCompleted, + Message: constants.MessageResourceReadinessValidated, + }) + r.Log.WithName(logger).Info(constants.LogResourceFound, "ResourceName", desiredObj.GetName(), "Kind", groupVersionKind, "Namespace", req.Namespace) + + } + + // if no other error and resource, other than Deployments, have already been created before, end validation and return + return ctrl.Result{}, nil +} + +// checkDeploymentForUpdates method checks the deployment if it needs to be updated +func (r *DatabaseObserverReconciler) checkDeploymentForUpdates(or ObserverResource, api *apiv1.DatabaseObserver, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + // declare + foundDeployment := &appsv1.Deployment{} + + // generate object + desiredObj, genErr := or.generate(api, r.Scheme) + if genErr != nil { + return ctrl.Result{}, genErr + } + + // convert + desiredDeployment := &appsv1.Deployment{} + if e := r.Scheme.Convert(desiredObj, desiredDeployment, nil); e != nil { + return ctrl.Result{}, e + } + + // retrieve latest deployment + if e := r.Get(context.TODO(), types.NamespacedName{Name: desiredObj.GetName(), Namespace: req.Namespace}, foundDeployment); e != nil { + return ctrl.Result{}, e + } + // check for containerImage + if constants.IsUpdateRequiredForContainerImage(desiredDeployment, foundDeployment) { + foundDeployment.Spec.Template.Spec.Containers[0].Image = constants.GetExporterImage(api) + + if e := r.updateDeployment(api, ctx, req, foundDeployment, constants.MessageExporterDeploymentImageUpdated, constants.EventMessageUpdatedImageSucceeded); e != nil { + return ctrl.Result{}, e + } + } + + // retrieve latest deployment + if e := r.Get(context.TODO(), types.NamespacedName{Name: desiredObj.GetName(), Namespace: req.Namespace}, foundDeployment); e != nil { + return ctrl.Result{}, e + } + // check environment variables + if constants.IsUpdateRequiredForEnvironmentVars(desiredDeployment, foundDeployment) { + foundDeployment.Spec.Template.Spec.Containers[0].Env = constants.GetExporterEnvs(api) + + if e := r.updateDeployment(api, ctx, req, foundDeployment, constants.MessageExporterDeploymentEnvironmentUpdated, constants.EventMessageUpdatedEnvironmentSucceeded); e != nil { + return ctrl.Result{}, e + } + } + + // retrieve latest deployment + foundDeployment = &appsv1.Deployment{} + if e := r.Get(context.TODO(), types.NamespacedName{Name: desiredObj.GetName(), Namespace: req.Namespace}, foundDeployment); e != nil { + return ctrl.Result{}, e + } + // check config-volume, creds and ocikey + if constants.IsUpdateRequiredForVolumes(desiredDeployment, foundDeployment) { + foundDeployment.Spec.Template.Spec.Volumes = constants.GetExporterDeploymentVolumes(api) + foundDeployment.Spec.Template.Spec.Containers[0].VolumeMounts = constants.GetExporterDeploymentVolumeMounts(api) + + if e := r.updateDeployment(api, ctx, req, foundDeployment, constants.MessageExporterDeploymentVolumesUpdated, constants.EventMessageUpdatedVolumesSucceeded); e != nil { + return ctrl.Result{}, e + } + } + + // update status for exporter config + var setConfigmapNameStatus string + for _, v := range desiredDeployment.Spec.Template.Spec.Volumes { + if v.Name == constants.DefaultConfigVolumeString { + setConfigmapNameStatus = v.ConfigMap.Name + api.Status.ExporterConfig = setConfigmapNameStatus + } + } + if api.Status.ExporterConfig != setConfigmapNameStatus { + api.Status.ExporterConfig = constants.DefaultValue + } + r.Status().Update(ctx, api) + + // retrieve latest deployment + foundDeployment = &appsv1.Deployment{} + if e := r.Get(context.TODO(), types.NamespacedName{Name: desiredObj.GetName(), Namespace: req.Namespace}, foundDeployment); e != nil { + return ctrl.Result{}, e + } + // check replicateCount + if constants.IsUpdateRequiredForReplicas(desiredDeployment, foundDeployment) { + desiredReplicaCount := constants.GetExporterReplicas(api) + foundDeployment.Spec.Replicas = &desiredReplicaCount + + if e := r.updateDeployment(api, ctx, req, foundDeployment, constants.MessageExporterDeploymentReplicaUpdated, constants.EventMessageUpdatedReplicaSucceeded); e != nil { + return ctrl.Result{}, e + } + } + + return ctrl.Result{}, nil +} + +// updateDeployment method updates the deployment and sets the condition +func (r *DatabaseObserverReconciler) updateDeployment(api *apiv1.DatabaseObserver, ctx context.Context, req ctrl.Request, d *appsv1.Deployment, updateMessage string, recorderMessage string) error { + + // make update + defer r.Status().Update(ctx, api) + + if e := r.Update(context.TODO(), d); e != nil { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterDeploymentReady, + Status: metav1.ConditionFalse, + Reason: constants.ReasonDeploymentUpdateFailed, + Message: constants.MessageExporterDeploymentUpdateFailed, + }) + r.Log.WithName(constants.LogExportersDeploy).Error(e, constants.ErrorDeploymentUpdate, "ResourceName", d.GetName(), "Kind", "Deployment", "Namespace", req.Namespace) + return e + } + + // update completed, however the pods needs to be validated for readiness + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterDeploymentReady, + Status: metav1.ConditionFalse, + Reason: constants.ReasonDeploymentUpdated, + Message: updateMessage, + }) + r.Log.WithName(constants.LogExportersDeploy).Info(constants.LogResourceUpdated, "ResourceName", d.GetName(), "Kind", "Deployment", "Namespace", req.Namespace) + r.Recorder.Event(api, corev1.EventTypeNormal, constants.EventReasonUpdateSucceeded, recorderMessage) + + return nil +} + +// validateDeploymentReadiness method evaluates deployment readiness by checking the status of all deployment pods +func (r *DatabaseObserverReconciler) validateDeploymentReadiness(api *apiv1.DatabaseObserver, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + d := &appsv1.Deployment{} + rName := constants.DefaultExporterDeploymentPrefix + api.Name + + // update after + defer r.Status().Update(ctx, api) + + // get latest deployment + if e := r.Get(context.TODO(), types.NamespacedName{Name: rName, Namespace: api.Namespace}, d); e != nil { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterDeploymentReady, + Status: metav1.ConditionFalse, + Reason: constants.ReasonGeneralResourceValidationFailureDueToError, + Message: constants.MessageExporterDeploymentValidationFailed, + }) + return ctrl.Result{}, e + } + + // get deployment labels + labels := d.Spec.Template.Labels + cLabels := client.MatchingLabels{} + for k, v := range labels { + cLabels[k] = v + } + + // list pods + pods := &corev1.PodList{} + if e := r.List(context.TODO(), pods, []client.ListOption{cLabels}...); e != nil { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterDeploymentReady, + Status: metav1.ConditionFalse, + Reason: constants.ReasonDeploymentFailed, + Message: constants.MessageExporterDeploymentListingFailed, + }) + return ctrl.Result{}, e + } + + // check each pod phase + for _, pod := range pods.Items { + if pod.Status.Phase == corev1.PodFailed { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterDeploymentReady, + Status: metav1.ConditionFalse, + Reason: constants.ReasonDeploymentFailed, + Message: constants.MessageExporterDeploymentFailed, + }) + return ctrl.Result{}, errors.New(constants.ErrorDeploymentPodsFailure) + + } else if pod.Status.Phase != corev1.PodRunning { // pod could be creating, + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterDeploymentReady, + Status: metav1.ConditionUnknown, + Reason: constants.ReasonDeploymentPending, + Message: constants.MessageExporterDeploymentPending, + }) + return ctrl.Result{Requeue: true, RequeueAfter: 15 * time.Second}, nil + } + } + + // once all pods are found to be running, mark deployment as ready and the exporter as ready + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterDeploymentReady, + Status: metav1.ConditionTrue, + Reason: constants.ReasonDeploymentSuccessful, + Message: constants.MessageExporterDeploymentSuccessful, + }) + return ctrl.Result{}, nil +} + +// validateCustomResourceReadiness method evaluates CR readiness by cycling through all conditions and checking for any condition with False Status +func (r *DatabaseObserverReconciler) validateCustomResourceReadiness(ctx context.Context, req ctrl.Request) { + + // get latest object + api := &apiv1.DatabaseObserver{} + if e := r.Get(context.TODO(), req.NamespacedName, api); e != nil { + r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorCRRetrieve) + return + } + + // make update + defer r.Status().Update(ctx, api) + + if meta.IsStatusConditionPresentAndEqual(api.Status.Conditions, constants.IsExporterDeploymentReady, metav1.ConditionUnknown) { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsCRAvailable, + Status: metav1.ConditionFalse, + Reason: constants.ReasonValidationInProgress, + Message: constants.MessageCRValidationWaiting, + }) + api.Status.Status = string(constants.StatusObservabilityPending) + } else if meta.IsStatusConditionFalse(api.Status.Conditions, constants.IsExporterDeploymentReady) || + meta.IsStatusConditionFalse(api.Status.Conditions, constants.IsExporterServiceReady) || + meta.IsStatusConditionFalse(api.Status.Conditions, constants.IsExporterServiceMonitorReady) { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsCRAvailable, + Status: metav1.ConditionFalse, + Reason: constants.ReasonReadyFailed, + Message: constants.MessageCRValidationFailed, + }) + api.Status.Status = string(constants.StatusObservabilityError) + } else { + meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + Type: constants.IsCRAvailable, + Status: metav1.ConditionTrue, + Reason: constants.ReasonReadyValidated, + Message: constants.MessageCRValidated, + }) + api.Status.Status = string(constants.StatusObservabilityReady) + } +} + +// SetupWithManager sets up the controller with the Manager. +func (r *DatabaseObserverReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&apiv1.DatabaseObserver{}). + Owns(&appsv1.Deployment{}). + Owns(&corev1.Service{}). + Complete(r) +} diff --git a/controllers/observability/databaseobserver_resource.go b/controllers/observability/databaseobserver_resource.go new file mode 100644 index 00000000..75e05330 --- /dev/null +++ b/controllers/observability/databaseobserver_resource.go @@ -0,0 +1,181 @@ +package controllers + +import ( + apiv1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + constants "github.com/oracle/oracle-database-operator/commons/observability" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +/* +This handler file contains all the methods that +retrieve/find and create all related resources +on Kubernetes. +*/ + +type ObservabilityDeploymentResource struct{} +type ObservabilityServiceResource struct{} +type ObservabilityServiceMonitorResource struct{} + +type ObserverResource interface { + generate(*apiv1.DatabaseObserver, *runtime.Scheme) (*unstructured.Unstructured, error) + identify() (string, string, schema.GroupVersionKind) +} + +func (resource *ObservabilityDeploymentResource) generate(api *apiv1.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { + rName := constants.DefaultExporterDeploymentPrefix + api.Name + rContainerName := constants.DefaultExporterContainerName + rContainerImage := constants.GetExporterImage(api) + rVolumes := constants.GetExporterDeploymentVolumes(api) + rVolumeMounts := constants.GetExporterDeploymentVolumeMounts(api) + rSelectors := constants.GetExporterSelector(api) + rReplicas := constants.GetExporterReplicas(api) + rEnvs := constants.GetExporterEnvs(api) + + rPort := []corev1.ContainerPort{ + {ContainerPort: 8080}, + } + + obj := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: rName, + Namespace: api.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &rReplicas, + Selector: &metav1.LabelSelector{ + MatchLabels: rSelectors, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: rSelectors, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Image: rContainerImage, + ImagePullPolicy: corev1.PullAlways, + Name: rContainerName, + Env: rEnvs, + VolumeMounts: rVolumeMounts, + Ports: rPort, + }}, + RestartPolicy: corev1.RestartPolicyAlways, + Volumes: rVolumes, + }, + }, + }, + } + + if err := controllerutil.SetControllerReference(api, obj, scheme); err != nil { + return nil, err + } + + var u = &unstructured.Unstructured{} + if err := scheme.Convert(obj, u, nil); err != nil { + return nil, err + } + return u, nil +} + +func (resource *ObservabilityServiceResource) generate(api *apiv1.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { + rServiceName := "obs-svc-" + api.Name + rLabels := constants.GetExporterLabels(api) + rPort := constants.GetExporterServicePort(api) + rSelector := constants.GetExporterSelector(api) + + obj := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: rServiceName, + Labels: rLabels, + Namespace: api.Namespace, + }, + Spec: corev1.ServiceSpec{ + Type: "ClusterIP", + Selector: rSelector, + Ports: []corev1.ServicePort{ + { + Name: "metrics", + Port: rPort, + }, + }, + }, + } + + if err := controllerutil.SetControllerReference(api, obj, scheme); err != nil { + return nil, err + } + + var u = &unstructured.Unstructured{} + if err := scheme.Convert(obj, u, nil); err != nil { + return nil, err + } + return u, nil +} + +func (resource *ObservabilityServiceMonitorResource) generate(api *apiv1.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { + rName := constants.DefaultServiceMonitorPrefix + api.Name + rLabels := constants.GetExporterLabels(api) + rSelector := constants.GetExporterSelector(api) + rPort := constants.GetExporterServiceMonitorPort(api) + rInterval := "20s" + + obj := &monitorv1.ServiceMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: rName, + Labels: rLabels, + Namespace: api.Namespace, + }, + Spec: monitorv1.ServiceMonitorSpec{ + Endpoints: []monitorv1.Endpoint{{ + Interval: monitorv1.Duration(rInterval), + Port: rPort, + }}, + Selector: metav1.LabelSelector{ + MatchLabels: rSelector, + }, + }, + } + + // set reference + if e := controllerutil.SetControllerReference(api, obj, scheme); e != nil { + return nil, e + } + + // convert + var u = &unstructured.Unstructured{} + if e := scheme.Convert(obj, u, nil); e != nil { + return nil, e + } + + return u, nil +} + +func (resource *ObservabilityDeploymentResource) identify() (string, string, schema.GroupVersionKind) { + return constants.IsExporterDeploymentReady, constants.LogExportersDeploy, schema.GroupVersionKind{ + Group: "apps", + Version: "v1", + Kind: "Deployment", + } +} + +func (resource *ObservabilityServiceResource) identify() (string, string, schema.GroupVersionKind) { + return constants.IsExporterServiceReady, constants.LogExportersSVC, schema.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: "Service", + } +} + +func (resource *ObservabilityServiceMonitorResource) identify() (string, string, schema.GroupVersionKind) { + return constants.IsExporterServiceMonitorReady, constants.LogExportersServiceMonitor, schema.GroupVersionKind{ + Group: "monitoring.coreos.com", + Version: "v1", + Kind: "ServiceMonitor", + } +} diff --git a/controllers/observability/suite_test.go b/controllers/observability/suite_test.go new file mode 100644 index 00000000..4500ff5a --- /dev/null +++ b/controllers/observability/suite_test.go @@ -0,0 +1,100 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + //+kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func(done Done) { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = observabilityv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient).ToNot(BeNil()) + + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/docs/observability/README.md b/docs/observability/README.md new file mode 100644 index 00000000..e7cf2571 --- /dev/null +++ b/docs/observability/README.md @@ -0,0 +1,231 @@ +# Managing Observability on Kubernetes for Oracle Databases + +Oracle Database Operator for Kubernetes (`OraOperator`) includes the +Oracle Database Metrics Exporter controller for Oracle Databases, which enables users to observe +Oracle Databases by scraping database metrics using SQL queries. The controller +automates the deployment and maintenance of the metrics exporter container image, +metrics exporter service and a Prometheus servicemonitor. + +The following sections explains the configuration and functionality +of the controller. + +* [Prerequisites](#prerequisites) +* [The DatabaseObserver Custom Resource Definition](#the-databaseobserver-custom-resource) +* [Configuration of DatabaseObservers](#configuration) + * [Create](#create-resource) + * [List](#list-resource) + * [Get Status](#get-detailed-status) + * [Update](#patch-resource) + * [Delete](#delete-resource) +* [Debugging and troubleshooting](#debugging-and-troubleshooting) + +## Prerequisites +The `DatabaseObserver` custom resource has the following pre-requisites: + +1. Prometheus and its `servicemonitor` custom resource definition must be installed on the cluster. + + The Oracle Database metrics exporter controller creates multiple +2. Kubernetes resources that include + a Prometheus `servicemonitor`. In order for the controller + to create ServiceMonitors, the ServiceMonitor custom resource must exist. + +2. A pre-existing Oracle Database and the proper database grants and privileges. + + The Oracle Database metrics exporter exports metrics through SQL queries that the user can control + and specify through a _toml_ file. The necessary access privileges to the tables used in the queries + are not provided and applied automatically. + +### The DatabaseObserver Custom Resource +The Oracle Database Operator (__v1.1.0__) includes the Oracle Database Metrics Exporter controller which automates +the deployment and setting up of the Oracle Database metrics exporter and the related resources to make Oracle databases observable. + +In the sample YAML file found in +[./config/samples/observability/databaseobserver.yaml](../../config/samples/observability/databaseobserver.yaml), +the databaseObserver custom resource offers the following properties to be configured: + +| Attribute | Type | Default | Required? | Example | +|-------------------------------------------------------|---------|-----------------|--------------|-----------------------------------------------------------------------| +| `spec.database.dbUser.key` | string | user | Optional | _username_ | +| `spec.database.dbUser.secret` | string | - | Yes | _db-secret_ | +| `spec.database.dbPassword.key` | string | password | Optional | _admin-password_ | +| `spec.database.dbPassword.secret` | string | - | Conditional | _db-secret_ | +| `spec.database.dbPassword.vaultOCID` | string | - | Conditional | _ocid1.vault.oc1..._ | +| `spec.database.dbPassword.vaultSecretName` | string | - | Conditional | _db-vault_ | +| `spec.database.dbWallet.secret` | string | - | Conditional | _devsec-oradevdb-wallet_ | +| `spec.database.dbConnectionString.key` | string | connection | Optional | _connection_ | +| `spec.database.dbConnectionString.secret` | string | - | Yes | _db-secretg_ | +| `spec.exporter.image` | string | - | Optional | _container-registry.oracle.com/database/observability-exporter:1.0.2_ | +| `spec.exporter.configuration.configmap.key` | string | config.toml | Optional | _config.toml_ | +| `spec.exporter.configuration.configmap.configmapName` | string | - | Optional | _devcm-oradevdb-config_ | +| `spec.exporter.service.port` | number | 9161 | Optional | _9161_ | +| `spec.prometheus.port` | string | metrics | Optional | _metrics_ | +| `spec.prometheus.labels` | map | app: obs-{name} | Optional | _app: oradevdb-apps_ | +| `spec.replicas` | number | 1 | Optional | _1_ | +| `spec.ociConfig.configMapName` | string | - | Conditional | _oci-cred_ | +| `spec.ociConfig.secretName` | string | - | Conditional | _oci-privatekey_ | + + +### Configuration +The `databaseObserver` custom resource has the following fields for all configurations that are required: +* `spec.database.dbUser.secret` - secret containing the database username. The corresponding key can be any value but must match the key in the secret provided. +* `spec.database.dbPassword.secret` - secret containing the database password (if vault is NOT used). The corresponding key field can be any value but must match the key in the secret provided +* `spec.database.dbConnectionString.secret` - secret containing the database connection string. The corresponding key field can be any value but must match the key in the secret provided i + +If a database wallet is required to connect, the following field containing the secret is required: +* `spec.database.dbWallet.secret` - secret containing the database wallet. The filenames must be used as the keys + +If vault is used to store the database password instead, the following fields are required: +* `spec.database.dbPassword.vaultOCID` - OCID of the vault used +* `spec.database.dbPassword.vaultSecretName` - Name of the secret inside the desired vault +* `spec.ociConfig.configMapName` - holds the rest of the information of the OCI API signing key. The following keys must be used: `fingerprint`, `region`, `tenancy` and `user` +* `spec.ociConfig.secretName` - holds the private key of the OCI API signing key. The key to the file containing the user private key must be: `privatekey` + +The `databaseObserver` provides the remaining multiple fields that are optional: +* `spec.prometheus.labels` - labels to use for Service, ServiceMonitor and Deployment +* `spec.prometheus.port` - port to use for ServiceMonitor +* `spec.replicas` - number of replicas to deploy +* `spec.exporter.service.port` - port of service +* `spec.exporter.image` - image version of observability exporter to use + + +### Create Resource +Follow the steps below to create a new databaseObserver resource object. + +1. To begin, creating a databaseObserver requires you to create and provide kubernetes Secrets to provide connection details: +```bash +kubectl create secret generic db-secret \ + --from-literal=username='username' \ + --from-literal=password='password_here' \ + --from-literal=connection='dbsample_tp' +``` + +2. (Conditional) Create a Kubernetes secret for the wallet (if a wallet is required to connect to the database). + +You can create this secret by using a command similar to the following example below. +If you are connecting to an Autunomous Database and the operator is used to manage the Oracle Autonomous Database, +a client wallet can also be downloaded as a secret through kubectl commands. You can find out how, [here](../../docs/adb/README.md#download-wallets). + +Otherwise, you can create the wallet secret from a local directory containing the wallet files. +```bash +kubectl create secret generic db-wallet --from-file=wallet_dir +``` + +3. Finally, update the databaseObserver manifest with the resources you have created. You can use the example manifest +inside config/samples/observability to specify and create your databaseObserver object with a +YAML file. + +```YAML +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: db-wallet +``` + +```bash + kubectl apply -f databaseobserver.yaml +``` + +### List Resource +To list the Observability custom resources, use the following command as an example: +```bash +kubectl get databaseobserver -A +``` + +### Get Detailed Status +To obtain a quick status, use the following command as an example: + +> Note: The databaseobserver custom resource is named `obs-sample` in the next following sections. +> We will use this name as an example. + +```sh +$ kubectl get databaseobserver obs-sample +NAME EXPORTERCONFIG STATUS +obs-sample default READY +``` + + +To obtain a more detailed status, use the following command as an example: + +```bash +kubectl describe databaseobserver obs-sample +``` + +This provides details of the current state of your databaseObserver resource object. A successful +deployment of the databaseObserver resource object should display `READY` as the status and all conditions with a `True` +value for every ConditionType. + + +### Patch Resource +The metrics exporter controller currently supports updates for most of the fields in the manifest. An example of patching the databaseObserver resource is as follows: +```bash +kubectl --type=merge -p '{"spec":{"exporter":{"image":"container-registry.oracle.com/database/observability-exporter:latest"}}}' patch databaseobserver obs-sample +``` + +The fields listed below can be updated with the given example command: + +* spec.exporter.image +* spec.exporter.configuration.configmap.configmapName +* spec.exporter.configuration.configmap.key +* spec.database.dbUser.secret +* spec.database.dbPassword.secret +* spec.database.dbConnectionString.secret +* spec.database.dbWallet.secret +* spec.ociConfig.configMapName +* spec.ociConfig.secretName +* spec.replicas +* spec.database.dbPassword.vaultOCID +* spec.database.dbPassword.vaultSecretName + + +### Delete Resource + +To delete the DatabaseObserver custom resource and all related resources: + +```bash +kubectl delete databaseobserver obs-sample +``` + + +## Debugging and troubleshooting + +### Show the details of the resource +To get the verbose output of the current spec, use the command below: + +```sh +kubectl describe databaseobserver/database-observer-sample +``` + +If any error occurs during the reconciliation loop, the Operator either reports +the error using the resource's event stream, or will show the error under conditions. + +### Check the logs of the pod where the operator deploys +Follow the steps to check the logs. + +1. List the pod replicas + + ```sh + kubectl get pods -n oracle-database-operator-system + ``` + +2. Use the below command to check the logs of the deployment + + ```sh + kubectl logs deployment.apps/oracle-database-operator-controller-manager -n oracle-database-operator-system + ``` diff --git a/go.mod b/go.mod index 82df5455..ddba3387 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/onsi/ginkgo/v2 v2.12.1 github.com/onsi/gomega v1.28.0 github.com/oracle/oci-go-sdk/v65 v65.49.3 + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0 go.uber.org/zap v1.26.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.28.2 @@ -17,8 +18,6 @@ require ( ) require ( - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -32,7 +31,6 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -50,8 +48,6 @@ require ( github.com/prometheus/procfs v0.10.1 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.0 // indirect - go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/net v0.14.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect @@ -73,8 +69,6 @@ require ( ) require ( - cloud.google.com/go/compute v1.2.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/go-logr/zapr v1.2.4 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/gnostic-models v0.6.8 // indirect diff --git a/go.sum b/go.sum index 14b3e019..1b323d94 100644 --- a/go.sum +++ b/go.sum @@ -1,287 +1,82 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.2.0 h1:EKki8sSdvDU0OO9mAXGwPXOTOgPz2l08R0/IutDH11I= -cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC29qE3Xmw= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -289,382 +84,115 @@ github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0Gq github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls= -github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= -github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= -github.com/oracle/oci-go-sdk/v65 v65.26.1 h1:Ms20RSRj+CuvQmw5ET1TkmzxLBI+bmLjZ6NYANA3gkk= -github.com/oracle/oci-go-sdk/v65 v65.26.1/go.mod h1:oyMrMa1vOzzKTmPN+kqrTR9y9kPA2tU1igN3NUSNTIE= github.com/oracle/oci-go-sdk/v65 v65.49.3 h1:HHv+XMZiBYHtoU8Ac/fURdp9v1vJPPCpIbJAWeadREw= github.com/oracle/oci-go-sdk/v65 v65.49.3/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0 h1:55138zTXw/yRYizPxZ672I/aDD7Yte3uYRAfUjWUu2M= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0/go.mod h1:j51242bf6LQwvJ1JPKWApzTnifmCwcQq0i1p29ylWiM= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= @@ -672,231 +200,45 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.66.0/go.mod h1:I1dmXYpX7HGwz/ejRxwQp2qj5bFAz93HiCU1C1oYd9M= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220201184016-50beb8ab5c44/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs= -k8s.io/api v0.25.4/go.mod h1:IG2+RzyPQLllQxnhzD8KQNEu4c4YvyDTpSMztf4A0OQ= k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= k8s.io/apiextensions-apiserver v0.28.0 h1:CszgmBL8CizEnj4sj7/PtLGey6Na3YgWyGCPONv7E9E= k8s.io/apiextensions-apiserver v0.28.0/go.mod h1:uRdYiwIuu0SyqJKriKmqEN2jThIJPhVmOWETm8ud1VE= -k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= -k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= -k8s.io/client-go v0.25.4 h1:3RNRDffAkNU56M/a7gUfXaEzdhZlYhoW8dgViGy5fn8= -k8s.io/client-go v0.25.4/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw= k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= -k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= -k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= k8s.io/component-base v0.28.1 h1:LA4AujMlK2mr0tZbQDZkjWbdhTV5bRyEyAFe0TJxlWg= k8s.io/component-base v0.28.1/go.mod h1:jI11OyhbX21Qtbav7JkhehyBsIRfnO8oEgoAR12ArIU= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= -sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/controller-runtime v0.16.2 h1:mwXAVuEk3EQf478PQwQ48zGOXvW27UJc8NHktQVuIPU= sigs.k8s.io/controller-runtime v0.16.2/go.mod h1:vpMu3LpI5sYWtujJOa2uPK61nB5rbwlN7BAB8aSLvGU= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= diff --git a/main.go b/main.go index d449fdff..8ecef6e6 100644 --- a/main.go +++ b/main.go @@ -41,6 +41,7 @@ package main import ( "context" "flag" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "os" "strconv" "time" @@ -57,6 +58,9 @@ import ( databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" databasecontroller "github.com/oracle/oracle-database-operator/controllers/database" + + observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + observabilitycontroller "github.com/oracle/oracle-database-operator/controllers/observability" // +kubebuilder:scaffold:imports ) @@ -67,7 +71,8 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - + utilruntime.Must(observabilityv1alpha1.AddToScheme(scheme)) + utilruntime.Must(monitorv1.AddToScheme(scheme)) utilruntime.Must(databasev1alpha1.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -91,12 +96,12 @@ func main() { // By default, a Manager will create a WebhookServer with port 9443 mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - Metrics: metricsserver.Options{ + Scheme: scheme, + Metrics: metricsserver.Options{ BindAddress: metricsAddr, }, - LeaderElection: enableLeaderElection, - LeaderElectionID: "a9d608ea.oracle.com", + LeaderElection: enableLeaderElection, + LeaderElectionID: "a9d608ea.oracle.com", }) if err != nil { setupLog.Error(err, "unable to start manager") @@ -266,6 +271,16 @@ func main() { os.Exit(1) } + // Observability DatabaseObserver Reconciler + if err = (&observabilitycontroller.DatabaseObserverReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("observability").WithName("DatabaseObserver"), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("DatabaseObserver"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "DatabaseObserver") + os.Exit(1) + } // +kubebuilder:scaffold:builder // Add index for PDB CR to enable mgr to cache PDBs From d2490acb62f2a2ea811c2192cdd026f357e5bc4b Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Tue, 13 Feb 2024 11:00:42 +0000 Subject: [PATCH 022/414] Fix/idesai/rbac changes --- .../v1alpha1/singleinstancedatabase_types.go | 2 +- .../singleinstancedatabase_webhook.go | 2 +- .../database/dataguardbroker_controller.go | 3 ++- .../oraclerestdataservice_controller.go | 3 ++- .../singleinstancedatabase_controller.go | 26 ++++++++++--------- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index 264978e8..4e36c22c 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -95,7 +95,7 @@ type SingleInstanceDatabasePersistence struct { // +kubebuilder:validation:Enum=ReadWriteOnce;ReadWriteMany AccessMode string `json:"accessMode,omitempty"` DatafilesVolumeName string `json:"datafilesVolumeName,omitempty"` - ScriptsVolumeName string `json:"scriptsVolumeName,omitempty"` + ScriptsVolumeName string `json:"scriptsVolumeName,omitempty"` VolumeClaimAnnotation string `json:"volumeClaimAnnotation,omitempty"` } diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 781f04ef..f97fe5f4 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -197,7 +197,7 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { if r.Spec.Persistence.ScriptsVolumeName != "" { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("persistence").Child("scriptsVolumeName"), - r.Spec.Persistence.ScriptsVolumeName, "scriptsVolumeName cannot be specified for standby databases")) + r.Spec.Persistence.ScriptsVolumeName, "scriptsVolumeName cannot be specified for standby databases")) } } diff --git a/controllers/database/dataguardbroker_controller.go b/controllers/database/dataguardbroker_controller.go index 95222efd..2a8fe163 100644 --- a/controllers/database/dataguardbroker_controller.go +++ b/controllers/database/dataguardbroker_controller.go @@ -76,7 +76,8 @@ const dataguardBrokerFinalizer = "database.oracle.com/dataguardbrokerfinalizer" //+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services;nodes;events,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. diff --git a/controllers/database/oraclerestdataservice_controller.go b/controllers/database/oraclerestdataservice_controller.go index 1a3359f7..1bb40ac7 100644 --- a/controllers/database/oraclerestdataservice_controller.go +++ b/controllers/database/oraclerestdataservice_controller.go @@ -80,7 +80,8 @@ type OracleRestDataServiceReconciler struct { //+kubebuilder:rbac:groups=database.oracle.com,resources=oraclerestdataservices,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=database.oracle.com,resources=oraclerestdataservices/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=oraclerestdataservices/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services;nodes;events,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 84efad66..8a79391e 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -93,7 +93,9 @@ var oemExpressUrl string //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services;nodes;events;persistentvolumes,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get;list +//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch //+kubebuilder:rbac:groups=storage.k8s.io,resources=storageclasses,verbs=get;list;watch // Reconcile is part of the main kubernetes reconciliation loop which aims to @@ -182,7 +184,7 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct r.Log.Info("Reconcile queued") return result, nil } - + // POD creation result, err = r.createOrReplacePods(singleInstanceDatabase, cloneFromDatabase, referredPrimaryDatabase, ctx, req) if result.Requeue { @@ -865,8 +867,8 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns } else { return "datafiles-vol" } - }(), - SubPath: "startup", + }(), + SubPath: "startup", }) mounts = append(mounts, corev1.VolumeMount{ MountPath: "/opt/oracle/scripts/setup/", @@ -877,8 +879,8 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns } else { return "datafiles-vol" } - }(), - SubPath: "setup", + }(), + SubPath: "setup", }) } return mounts @@ -1275,7 +1277,7 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePVCforCustomScriptsVol err := r.Get(ctx, types.NamespacedName{Name: pvcName, Namespace: m.Namespace}, pvc) if err == nil { - if (m.Spec.Persistence.ScriptsVolumeName != "" && pvc.Spec.VolumeName != m.Spec.Persistence.ScriptsVolumeName) { + if m.Spec.Persistence.ScriptsVolumeName != "" && pvc.Spec.VolumeName != m.Spec.Persistence.ScriptsVolumeName { // call deletePods() with zero pods in avaiable and nil readyPod to delete all pods result, err := r.deletePods(ctx, req, m, []corev1.Pod{}, corev1.Pod{}, 0, 0) if result.Requeue { @@ -1313,15 +1315,15 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePVCforCustomScriptsVol Storage := int(volumeQty.Value()) StorageClass := "" - log.Info(fmt.Sprintf("PV storage: %v\n",Storage)) - log.Info(fmt.Sprintf("PV AccessMode: %v\n",AccessMode)) - + log.Info(fmt.Sprintf("PV storage: %v\n", Storage)) + log.Info(fmt.Sprintf("PV AccessMode: %v\n", AccessMode)) + pvc := &corev1.PersistentVolumeClaim{ TypeMeta: metav1.TypeMeta{ Kind: "PersistentVolumeClaim", }, ObjectMeta: metav1.ObjectMeta{ - Name: pvcName , + Name: pvcName, Namespace: m.Namespace, Labels: map[string]string{ "app": m.Name, @@ -1359,7 +1361,7 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePVCforCustomScriptsVol return requeueY, err } - return requeueN, nil + return requeueN, nil } // ############################################################################# From 877bcf56aef4053a95ebed5fdfa6e892a043b29c Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Wed, 14 Feb 2024 06:35:16 +0000 Subject: [PATCH 023/414] Enhancement/idesai/root permissions fix --- apis/database/v1alpha1/singleinstancedatabase_types.go | 1 + config/samples/sidb/singleinstancedatabase.yaml | 2 ++ controllers/database/singleinstancedatabase_controller.go | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index 4e36c22c..0d49aa6b 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -97,6 +97,7 @@ type SingleInstanceDatabasePersistence struct { DatafilesVolumeName string `json:"datafilesVolumeName,omitempty"` ScriptsVolumeName string `json:"scriptsVolumeName,omitempty"` VolumeClaimAnnotation string `json:"volumeClaimAnnotation,omitempty"` + SetWritePermissions *bool `json:"setWritePermissions,omitempty"` } // SingleInstanceDatabaseInitParams defines the Init Parameters diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index d130dbef..a6f67671 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -91,6 +91,8 @@ spec: ## if the storageClass supports volume expansion, patch the size attribute to expand the volume ## Shrinking volumes is not allowed size: 100Gi + ## set ownership/permissions for writing to datafiles volume. This is usually needed for NFS volumes. + setWritePermissions: true ## oci-bv applies to OCI block volumes. Use "standard" storageClass for dynamic provisioning in Minikube. Update as appropriate for other cloud services storageClass: "oci-bv" ## accessMode can only accept one of ReadWriteOnce, ReadWriteMany diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 8a79391e..946a1c4a 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -699,7 +699,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }}, InitContainers: func() []corev1.Container { initContainers := []corev1.Container{} - if m.Spec.Persistence.Size != "" { + if m.Spec.Persistence.Size != "" && m.Spec.Persistence.SetWritePermissions != nil && *m.Spec.Persistence.SetWritePermissions { initContainers = append(initContainers, corev1.Container{ Name: "init-permissions", Image: m.Spec.Image.PullFrom, @@ -1111,6 +1111,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns Name: m.Spec.Image.PullSecrets, }, }, + ServiceAccountName: m.Spec.ServiceAccountName, }, } From c7ee2ddd3016d77907af9b4c8554e0cbedbbd4eb Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Tue, 20 Feb 2024 22:33:38 +0000 Subject: [PATCH 024/414] Multinamespaced support for operator deployment --- Makefile | 2 +- .../v1alpha1/dataguardbroker_webhook.go | 31 +++++++---- .../v1alpha1/oraclerestdataservice_webhook.go | 12 +++- .../singleinstancedatabase_webhook.go | 9 +++ commons/database/utils.go | 14 +++++ config/manager/manager.yaml | 3 + main.go | 55 +++++++++++++++++-- 7 files changed, 110 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 67ea779f..675e2aa3 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ build: generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go -GOLANG_VERSION ?= 1.21.4 +GOLANG_VERSION ?= 1.22.0 ## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. ## Otherwise, use golang image from docker hub as the builder. ifeq ($(BUILD_INTERNAL), true) diff --git a/apis/database/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go index a0e3ccbe..0e211b37 100644 --- a/apis/database/v1alpha1/dataguardbroker_webhook.go +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -41,6 +41,7 @@ package v1alpha1 import ( "strings" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -70,21 +71,21 @@ var _ webhook.Defaulter = &DataguardBroker{} func (r *DataguardBroker) Default() { dataguardbrokerlog.Info("default", "name", r.Name) - if (r.Spec.LoadBalancer) { + if r.Spec.LoadBalancer { if r.Spec.ServiceAnnotations == nil { - r.Spec.ServiceAnnotations= make(map[string]string) + r.Spec.ServiceAnnotations = make(map[string]string) } - // Annotations required for a flexible load balancer on oci + // Annotations required for a flexible load balancer on oci _, ok := r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] - if(!ok) { + if !ok { r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] = "flexible" } - _,ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] - if(!ok) { + _, ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] + if !ok { r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] = "10" } - _,ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] - if(!ok) { + _, ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] + if !ok { r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] = "100" } } @@ -98,9 +99,19 @@ var _ webhook.Validator = &DataguardBroker{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *DataguardBroker) ValidateCreate() (admission.Warnings, error) { dataguardbrokerlog.Info("validate create", "name", r.Name) + var allErrs field.ErrorList + namespaces := dbcommons.GetWatchNamespaces() + _, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + if len(namespaces) == 0 && !containsNamespace { + allErrs = append(allErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } - // TODO(user): fill in your validation logic upon object creation. - return nil, nil + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "Dataguard"}, + r.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v1alpha1/oraclerestdataservice_webhook.go b/apis/database/v1alpha1/oraclerestdataservice_webhook.go index bad35f05..12f9270f 100644 --- a/apis/database/v1alpha1/oraclerestdataservice_webhook.go +++ b/apis/database/v1alpha1/oraclerestdataservice_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -92,6 +93,15 @@ func (r *OracleRestDataService) ValidateCreate() (admission.Warnings, error) { var allErrs field.ErrorList + namespaces := dbcommons.GetWatchNamespaces() + _, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + if len(namespaces) == 0 && !containsNamespace { + allErrs = append(allErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + // Persistence spec validation if r.Spec.Persistence.Size == "" && (r.Spec.Persistence.AccessMode != "" || r.Spec.Persistence.StorageClass != "" || r.Spec.Persistence.VolumeName != "") { @@ -117,7 +127,7 @@ func (r *OracleRestDataService) ValidateCreate() (admission.Warnings, error) { if r.Spec.DatabaseRef == r.Name { allErrs = append(allErrs, field.Forbidden(field.NewPath("Name"), - "cannot be same as DatabaseRef: " + r.Spec.DatabaseRef)) + "cannot be same as DatabaseRef: "+r.Spec.DatabaseRef)) } diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index f97fe5f4..8d3c27e9 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -152,6 +152,15 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { singleinstancedatabaselog.Info("validate create", "name", r.Name) var allErrs field.ErrorList + namespaces := dbcommons.GetWatchNamespaces() + _, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + if len(namespaces) == 0 && !containsNamespace { + allErrs = append(allErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + // Persistence spec validation if r.Spec.Persistence.Size == "" && (r.Spec.Persistence.AccessMode != "" || r.Spec.Persistence.StorageClass != "" || r.Spec.Persistence.DatafilesVolumeName != "") { diff --git a/commons/database/utils.go b/commons/database/utils.go index eedeeeb4..ec3a6fe4 100644 --- a/commons/database/utils.go +++ b/commons/database/utils.go @@ -45,6 +45,7 @@ import ( "errors" "fmt" "math/rand" + "os" "strconv" "strings" "time" @@ -785,3 +786,16 @@ func PatchService(config *rest.Config, namespace string, ctx context.Context, re _, err = client.CoreV1().Services(namespace).Patch(ctx, svcName, types.MergePatchType, []byte(payload), metav1.PatchOptions{}) return err } + +func GetWatchNamespaces() map[string]bool { + // Fetching the allowed namespaces from env variables + var watchNamespaceEnvVar = "WATCH_NAMESPACE" + ns, _ := os.LookupEnv(watchNamespaceEnvVar) + values := strings.Split(strings.TrimSpace(ns), ",") + namespaces := make(map[string]bool) + // put slice values into map + for _, s := range values { + namespaces[s] = true + } + return namespaces +} diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 90df4158..54340faf 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -41,4 +41,7 @@ spec: requests: cpu: 400m memory: 400Mi + env: + - name : WATCH_NAMESPACE + value : "" terminationGracePeriodSeconds: 10 diff --git a/main.go b/main.go index 8ecef6e6..5ff714be 100644 --- a/main.go +++ b/main.go @@ -41,17 +41,22 @@ package main import ( "context" "flag" - monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "fmt" "os" "strconv" + "strings" "time" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "go.uber.org/zap/zapcore" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" @@ -94,15 +99,25 @@ func main() { ctrl.SetLogger(zap.New(func(o *zap.Options) { *o = *options })) - // By default, a Manager will create a WebhookServer with port 9443 - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + watchNamespaces, err := getWatchNamespace() + if err != nil { + setupLog.Error(err, "Failed to get watch namespaces") + os.Exit(1) + } + opt := ctrl.Options{ Scheme: scheme, Metrics: metricsserver.Options{ BindAddress: metricsAddr, }, LeaderElection: enableLeaderElection, LeaderElectionID: "a9d608ea.oracle.com", - }) + NewCache: func(config *rest.Config, opts cache.Options) (cache.Cache, error) { + opts.DefaultNamespaces = watchNamespaces + return cache.New(config, opts) + }, + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), opt) if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1) @@ -298,3 +313,35 @@ func main() { os.Exit(1) } } + +func getWatchNamespace() (map[string]cache.Config, error) { + // WatchNamespaceEnvVar is the constant for env variable WATCH_NAMESPACE + // which specifies the Namespace to watch. + // An empty value means the operator is running with cluster scope. + + var watchNamespaceEnvVar = "WATCH_NAMESPACE" + var nsmap map[string]cache.Config + ns, found := os.LookupEnv(watchNamespaceEnvVar) + values := strings.Split(ns, ",") + if len(values) == 1 && values[0] == "" { + fmt.Printf(":CLUSTER SCOPED:\n") + return nil, nil + } + fmt.Printf(":NAMESPACE SCOPED:\n") + fmt.Printf("WATCH LIST=%s\n", values) + nsmap = make(map[string]cache.Config, len(values)) + if !found { + return nsmap, fmt.Errorf("%s must be set", watchNamespaceEnvVar) + } + + if ns == "" { + return nil, nil + } + + for _, ns := range values { + nsmap[ns] = cache.Config{} + } + + return nsmap, nil + +} From cd74587006d35cc83cddd0616b2462e8afdea7c1 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Fri, 23 Feb 2024 14:39:09 +0000 Subject: [PATCH 025/414] Enhancement namespace webhook logic + updating ords images + updating Readme --- README.md | 1 + apis/database/v1alpha1/dataguardbroker_webhook.go | 2 +- apis/database/v1alpha1/oraclerestdataservice_webhook.go | 2 +- apis/database/v1alpha1/singleinstancedatabase_webhook.go | 2 +- config/samples/sidb/oraclerestdataservice.yaml | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2adcb566..20f7b7da 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ This release of Oracle Database Operator for Kubernetes (the operator) supports * Oracle Base Database Cloud Service (BDBCS): provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License * Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration * Oracle Database Metrics Exporter: create, patch, delete +* Watch over a set of namespaces or all the namespaces in the cluster using the "WATCH_NAMESPACE" env variable of the operator deployment The upcoming releases will support new configurations, operations and capabilities. diff --git a/apis/database/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go index 0e211b37..bbafdc3d 100644 --- a/apis/database/v1alpha1/dataguardbroker_webhook.go +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -103,7 +103,7 @@ func (r *DataguardBroker) ValidateCreate() (admission.Warnings, error) { namespaces := dbcommons.GetWatchNamespaces() _, containsNamespace := namespaces[r.Namespace] // Check if the allowed namespaces maps contains the required namespace - if len(namespaces) == 0 && !containsNamespace { + if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, "Oracle database operator doesn't watch over this namespace")) diff --git a/apis/database/v1alpha1/oraclerestdataservice_webhook.go b/apis/database/v1alpha1/oraclerestdataservice_webhook.go index 12f9270f..bfe3208c 100644 --- a/apis/database/v1alpha1/oraclerestdataservice_webhook.go +++ b/apis/database/v1alpha1/oraclerestdataservice_webhook.go @@ -96,7 +96,7 @@ func (r *OracleRestDataService) ValidateCreate() (admission.Warnings, error) { namespaces := dbcommons.GetWatchNamespaces() _, containsNamespace := namespaces[r.Namespace] // Check if the allowed namespaces maps contains the required namespace - if len(namespaces) == 0 && !containsNamespace { + if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, "Oracle database operator doesn't watch over this namespace")) diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 8d3c27e9..89773040 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -155,7 +155,7 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { namespaces := dbcommons.GetWatchNamespaces() _, containsNamespace := namespaces[r.Namespace] // Check if the allowed namespaces maps contains the required namespace - if len(namespaces) == 0 && !containsNamespace { + if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, "Oracle database operator doesn't watch over this namespace")) diff --git a/config/samples/sidb/oraclerestdataservice.yaml b/config/samples/sidb/oraclerestdataservice.yaml index cef0276f..0cac834d 100644 --- a/config/samples/sidb/oraclerestdataservice.yaml +++ b/config/samples/sidb/oraclerestdataservice.yaml @@ -44,7 +44,7 @@ spec: ## Build the ORDS image following instructions at ## https://github.com/oracle/docker-images/tree/main/OracleRestDataServices image: - pullFrom: + pullFrom: container-registry.oracle.com/database/ords:21.4.2-gh pullSecrets: ## Dedicated persistent storage is optional. If not specified, ORDS will use persistent storage from .spec.databaseRef From feb24c5146881956676b2cea35ff5d07d4f3e5da Mon Sep 17 00:00:00 2001 From: psaini Date: Fri, 23 Feb 2024 18:16:48 +0000 Subject: [PATCH 026/414] Added support for user defined sharding --- .../v1alpha1/shardingdatabase_types.go | 136 +++++++-- commons/sharding/catalog.go | 74 +++-- commons/sharding/exec.go | 73 +++++ commons/sharding/gsm.go | 46 +-- commons/sharding/provstatus.go | 8 +- commons/sharding/scommon.go | 266 ++++++++++++----- commons/sharding/shard.go | 74 +++-- .../database/shardingdatabase_controller.go | 279 +++++++++++++++--- 8 files changed, 748 insertions(+), 208 deletions(-) diff --git a/apis/database/v1alpha1/shardingdatabase_types.go b/apis/database/v1alpha1/shardingdatabase_types.go index b2f7142e..3b4f9c17 100644 --- a/apis/database/v1alpha1/shardingdatabase_types.go +++ b/apis/database/v1alpha1/shardingdatabase_types.go @@ -58,26 +58,39 @@ import ( type ShardingDatabaseSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Shard []ShardSpec `json:"shard"` - Catalog []CatalogSpec `json:"catalog"` // The catalogSpes accept all the catalog parameters - Gsm []GsmSpec `json:"gsm"` // The GsmSpec will accept all the Gsm parameter - StorageClass string `json:"storageClass,omitempty"` // Optional Accept storage class name - DbImage string `json:"dbImage"` // Accept DB Image name - DbImagePullSecret string `json:"dbImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. - GsmImage string `json:"gsmImage"` // Acccept the GSM image name - GsmImagePullSecret string `json:"gsmImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. - Secret string `json:"secret"` // Secret Name to be used with Shard - StagePvcName string `json:"stagePvcName,omitempty"` // the Stagepvc for the backup of cluster - PortMappings []PortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least - Namespace string `json:"namespace,omitempty"` // Target namespace of the application. - IsDebug bool `json:"isDebug,omitempty"` // Optional parameter to enable logining - IsExternalSvc bool `json:"isExternalSvc,omitempty"` - IsClone bool `json:"isClone,omitempty"` - IsDataGuard bool `json:"isDataGuard,omitempty"` - ScriptsLocation string `json:"scriptsLocation,omitempty"` - NsConfigMap string `json:"nsConfigMap,omitempty"` - NsSecret string `json:"nsSecret,omitempty"` - IsDeleteOraPvc bool `json:"isDeleteOraPvc,omitempty"` + Shard []ShardSpec `json:"shard"` + Catalog []CatalogSpec `json:"catalog"` // The catalogSpes accept all the catalog parameters + Gsm []GsmSpec `json:"gsm"` // The GsmSpec will accept all the Gsm parameter + StorageClass string `json:"storageClass,omitempty"` // Optional Accept storage class name + DbImage string `json:"dbImage"` // Accept DB Image name + DbImagePullSecret string `json:"dbImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. + GsmImage string `json:"gsmImage"` // Acccept the GSM image name + GsmImagePullSecret string `json:"gsmImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. + StagePvcName string `json:"stagePvcName,omitempty"` // the Stagepvc for the backup of cluster + PortMappings []PortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least + Namespace string `json:"namespace,omitempty"` // Target namespace of the application. + IsDebug bool `json:"isDebug,omitempty"` // Optional parameter to enable logining + IsExternalSvc bool `json:"isExternalSvc,omitempty"` + IsClone bool `json:"isClone,omitempty"` + IsDataGuard bool `json:"isDataGuard,omitempty"` + ScriptsLocation string `json:"scriptsLocation,omitempty"` + IsDeleteOraPvc bool `json:"isDeleteOraPvc,omitempty"` + ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` + LivenessCheckPeriod int `json:"liveinessCheckPeriod,omitempty"` + ReplicationType string `json:"replicationType,omitempty"` + IsDownloadScripts bool `json:"isDownloadScripts,omitempty"` + InvitedNodeSubnetFlag string `json:"invitedNodeSubnetFlag,omitempty"` + InvitedNodeSubnet string `json:"InvitedNodeSubnet,omitempty"` + ShardingType string `json:"shardingType,omitempty"` + GsmShardSpace []GsmShardSpaceSpec `json:"gsmShardSpace,omitempty"` + GsmShardGroup []GsmShardGroupSpec `json:"gsmShardGroup,omitempty"` + ShardRegion []string `json:"shardRegion,omitempty"` + ShardBuddyRegion string `json:"shardBuddyRegion,omitempty"` + GsmService []GsmServiceSpec `json:"gsmService,omitempty"` + ShardConfigName string `json:"shardConfigName,omitempty"` + GsmDevMode string `json:"gsmDevMode,omitempty"` + DbSecret *SecretDetails `json:"dbSecret,omitempty"` // Secret Name to be used with Shard + IsTdeWallet bool `json:"isTdeWallet,omitempty"` } // To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 @@ -88,7 +101,8 @@ type ShardingDatabaseStatus struct { Shard map[string]string `json:"shards,omitempty"` Catalog map[string]string `json:"catalogs,omitempty"` - Gsm GsmStatus `json:"gsm,omitempty"` + + Gsm GsmStatus `json:"gsm,omitempty"` // +patchMergeKey=type // +patchStrategy=merge @@ -106,6 +120,12 @@ type GsmStatus struct { Services string `json:"services,omitempty"` } +type GsmShardDetails struct { + Name string `json:"name,omitempty"` + Available string `json:"available,omitempty"` + State string `json:"State,omitempty"` +} + type GsmStatusDetails struct { Name string `json:"name,omitempty"` K8sInternalSvc string `json:"k8sInternalSvc,omitempty"` @@ -118,8 +138,12 @@ type GsmStatusDetails struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status +//+kubebuilder:printcolumn:JSONPath=".status.gsm.state",name="Gsm State",type=string +//+kubebuilder:printcolumn:JSONPath=".status.gsm.services",name="Services",type=string +//+kubebuilder:printcolumn:JSONPath=".status.gsm.shards",name="shards",type=string,priority=1 // ShardingDatabase is the Schema for the shardingdatabases API +// +kubebuilder:resource:path=shardingdatabases,scope=Namespaced type ShardingDatabase struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -151,6 +175,10 @@ type ShardSpec struct { PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + ShardSpace string `json:"shardSpace,omitempty"` + ShardGroup string `json:"shardGroup,omitempty"` + ShardRegion string `json:"shardRegion,omitempty"` + DeployAs string `json:"deployAs,omitempty"` } // CatalogSpec defines the desired state of CatalogSpec @@ -174,7 +202,7 @@ type CatalogSpec struct { type GsmSpec struct { Name string `json:"name"` // Gsm name that will be used deploy StatefulSet - Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. + //Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. EnvVars []EnvironmentVariable `json:"envVars,omitempty"` //Optional Env variables for GSM StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` // This parameter will not be used if you use OraGsmPvcName Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` // Optional resource requirement for the container. @@ -184,6 +212,70 @@ type GsmSpec struct { NodeSelector map[string]string `json:"nodeSelector,omitempty"` PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + Region string `json:"region,omitempty"` + DirectorName string `json:"directorName,omitempty"` +} + +// ShardGroupSpec Specification + +type GsmShardGroupSpec struct { + Name string `json:"name"` // Name of the shardgroup. + Region string `json:"region,omitempty"` + DeployAs string `json:"deployAs,omitempty"` +} + +// ShardSpace Specs +type GsmShardSpaceSpec struct { + Name string `json:"name"` // Name of the shardSpace. + Chunks int `json:"chunks,omitempty"` //chunks is optional + ProtectionMode string `json:"protectionMode,omitempty"` // Data guard protection mode + ShardGroup string `json:"shardGroup,omitempty"` +} + +// Service Definition +type GsmServiceSpec struct { + Name string `json:"name"` // Name of the shardSpace. + Available string `json:"available,omitempty"` + ClbGoal string `json:"clbGoal,omitempty"` + CommitOutcome string `json:"commitOutcome,omitempty"` + DrainTimeout string `json:"drainTimeout,omitempty"` + Dtp string `json:"dtp,omitempty"` + Edition string `json:"edition,omitempty"` + FailoverPrimary string `json:"failoverPrimary,omitempty"` + FailoverRestore string `json:"failoverRestore,omitempty"` + FailoverDelay string `json:"failoverDelay,omitempty"` + FailoverMethod string `json:"failoverMethod,omitempty"` + FailoverRetry string `json:"failoverRetry,omitempty"` + FailoverType string `json:"failoverType,omitempty"` + GdsPool string `json:"gdsPool,omitempty"` + Role string `json:"role,omitempty"` + SessionState string `json:"sessionState,omitempty"` + Lag int `json:"lag,omitempty"` + Locality string `json:"locality,omitempty"` + Notification string `json:"notification,omitempty"` + PdbName string `json:"pdbName,omitempty"` + Policy string `json:"policy,omitempty"` + Preferrred string `json:"preferred,omitempty"` + PreferredAll string `json:"prferredAll,omitempty"` + RegionFailover string `json:"regionFailover,omitempty"` + StopOption string `json:"stopOption,omitempty"` + SqlTrasactionProfile string `json:"sqlTransactionProfile,omitempty"` + TableFamily string `json:"tableFamily,omitempty"` + Retention string `json:"retention,omitempty"` + TfaPolicy string `json:"tfaPolicy,omitempty"` +} + +// Secret Details +type SecretDetails struct { + Name string `json:"name"` // Name of the secret. + KeyFileName string `json:"keyFileName,omitempty"` // Name of the key. + NsConfigMap string `json:"nsConfigMap,omitempty"` + NsSecret string `json:"nsSecret,omitempty"` + PwdFileName string `json:"pwdFileName"` + PwdFileMountLocation string `json:"pwdFileMountLocation,omitempty"` + KeyFileMountLocation string `json:"keyFileMountLocation,omitempty"` + KeySecretName string `json:"keySecretName,omitempty"` + EncryptionType string `json:"encryptionType,omitempty"` } // EnvironmentVariable represents a named variable accessible for containers. diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index f87efea8..8a0019dd 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -141,10 +141,14 @@ func buildPodSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCata RunAsUser: &user, FSGroup: &group, }, - InitContainers: buildInitContainerSpecForCatalog(instance, OraCatalogSpex), - Containers: buildContainerSpecForCatalog(instance, OraCatalogSpex), - Volumes: buildVolumeSpecForCatalog(instance, OraCatalogSpex), + Containers: buildContainerSpecForCatalog(instance, OraCatalogSpex), + Volumes: buildVolumeSpecForCatalog(instance, OraCatalogSpex), } + + if (instance.Spec.IsDownloadScripts) && (instance.Spec.ScriptsLocation != "") { + spec.InitContainers = buildInitContainerSpecForCatalog(instance, OraCatalogSpex) + } + if len(instance.Spec.DbImagePullSecret) > 0 { spec.ImagePullSecrets = []corev1.LocalObjectReference{ { @@ -170,16 +174,10 @@ func buildVolumeSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraC Name: OraCatalogSpex.Name + "secretmap-vol3", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: instance.Spec.Secret, + SecretName: instance.Spec.DbSecret.Name, }, }, }, - { - Name: OraCatalogSpex.Name + "orascript-vol5", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, { Name: OraCatalogSpex.Name + "oradshm-vol6", VolumeSource: corev1.VolumeSource{ @@ -196,6 +194,10 @@ func buildVolumeSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraC result = append(result, corev1.Volume{Name: OraCatalogSpex.Name + "orastage-vol7", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: instance.Spec.StagePvcName}}}) } + if instance.Spec.IsDownloadScripts { + result = append(result, corev1.Volume{Name: OraCatalogSpex.Name + "orascript-vol5", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}) + } + return result } @@ -217,29 +219,49 @@ func buildContainerSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, O VolumeMounts: buildVolumeMountSpecForCatalog(instance, OraCatalogSpex), LivenessProbe: &corev1.Probe{ // TODO: Investigate if it's ok to call status every 10 seconds - FailureThreshold: int32(30), - PeriodSeconds: int32(240), - InitialDelaySeconds: int32(300), - TimeoutSeconds: int32(60), + FailureThreshold: int32(3), + InitialDelaySeconds: int32(30), + PeriodSeconds: func() int32 { + if instance.Spec.LivenessCheckPeriod > 0 { + return int32(instance.Spec.LivenessCheckPeriod) + } + return 60 + }(), + TimeoutSeconds: int32(30), ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: getLivenessCmd("CATALOG"), + Command: []string{"/bin/sh", "-c", "if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi "}, }, }, }, /** - // Disabling this because the pod is not reachable till the time startup probe completes and without network pod configuration cannot be completed. - StartupProbe: &corev1.Probe{ - // Initial delay should be big, because shard setup takes time - FailureThreshold: int32(30), - PeriodSeconds: int32(120), - Handler: corev1.Handler{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: getLivenessCmd("CATALOG"), + //Command: getReadinessCmd("CATALOG"), + Command: []string{"/bin/sh", "-c", "if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi "}, }, }, + InitialDelaySeconds: 20, + TimeoutSeconds: 20, + PeriodSeconds: func() int32 { + if instance.Spec.ReadinessCheckPeriod > 0 { + return int32(instance.Spec.ReadinessCheckPeriod) + } + return 60 + }(), }, **/ + StartupProbe: &corev1.Probe{ + FailureThreshold: int32(120), + PeriodSeconds: int32(40), + InitialDelaySeconds: int32(30), + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"/bin/sh", "-c", "if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi "}, + }, + }, + }, Env: buildEnvVarsSpec(instance, OraCatalogSpex.EnvVars, OraCatalogSpex.Name, "CATALOG", false, ""), } if instance.Spec.IsClone { @@ -256,7 +278,7 @@ func buildContainerSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, O return result } -//Function to build the init Container Spec +// Function to build the init Container Spec func buildInitContainerSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) []corev1.Container { var result []corev1.Container // building the init Container Spec @@ -297,7 +319,9 @@ func buildVolumeMountSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, var result []corev1.VolumeMount result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "secretmap-vol3", MountPath: oraSecretMount, ReadOnly: true}) result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "-oradata-vol4", MountPath: oraDataMount}) - result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "orascript-vol5", MountPath: oraScriptMount}) + if instance.Spec.IsDownloadScripts { + result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "orascript-vol5", MountPath: oraDbScriptMount}) + } result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "oradshm-vol6", MountPath: oraShm}) if len(instance.Spec.StagePvcName) != 0 { @@ -328,7 +352,7 @@ func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, corev1.ReadWriteOnce, }, StorageClassName: &instance.Spec.StorageClass, - Resources: corev1.ResourceRequirements{ + Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(strconv.FormatInt(int64(OraCatalogSpex.StorageSizeInGb), 10) + "Gi"), }, diff --git a/commons/sharding/exec.go b/commons/sharding/exec.go index 48fc18cf..c1921018 100644 --- a/commons/sharding/exec.go +++ b/commons/sharding/exec.go @@ -40,16 +40,24 @@ package commons import ( "bytes" + "fmt" "net/http" + "time" databasealphav1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/remotecommand" + "k8s.io/kubectl/pkg/cmd/cp" + "k8s.io/kubectl/pkg/cmd/util" ) // ExecCMDInContainer execute command in first container of a pod @@ -106,3 +114,68 @@ func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, return execOut.String(), execErr.String(), nil } + +func GetPodCopyConfig(kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasealphav1.ShardingDatabase, logger logr.Logger) (*rest.Config, *kubernetes.Clientset, error) { + + var clientSet *kubernetes.Clientset + config, err := kubeConfig.ClientConfig() + if err != nil { + return config, clientSet, err + } + clientSet, err = kubernetes.NewForConfig(config) + config.APIPath = "/api" + config.GroupVersion = &schema.GroupVersion{Version: "v1"} + config.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs} + + return config, clientSet, err + +} + +func KctlCopyFile(kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasealphav1.ShardingDatabase, restConfig *rest.Config, kclientset *kubernetes.Clientset, logger logr.Logger, src string, dst string, containername string) (*bytes.Buffer, *bytes.Buffer, *bytes.Buffer, error) { + + var in, out, errOut *bytes.Buffer + var ioStreams genericclioptions.IOStreams + for count := 0; ; count++ { + ioStreams, in, out, errOut = genericclioptions.NewTestIOStreams() + copyOptions := cp.NewCopyOptions(ioStreams) + copyOptions.ClientConfig = restConfig + if len(containername) != 0 { + copyOptions.Container = containername + } + configFlags := genericclioptions.NewConfigFlags(false) + f := util.NewFactory(configFlags) + cmd := cp.NewCmdCp(f, ioStreams) + err := copyOptions.Complete(f, cmd, []string{src, dst}) + if err != nil { + return nil, nil, nil, err + } + + c := rest.CopyConfig(restConfig) + cs, err := kubernetes.NewForConfig(c) + if err != nil { + return nil, nil, nil, err + } + + copyOptions.ClientConfig = c + copyOptions.Clientset = cs + + err = copyOptions.Run() + if err != nil { + if !shouldRetry(count, err) { + return nil, nil, nil, fmt.Errorf("could not run copy operation: %v. Stdout: %v, Stderr: %v", err, out.String(), errOut.String()) + } + time.Sleep(10 * time.Second) + continue + } + break + } + return in, out, errOut, nil + +} + +func shouldRetry(count int, err error) bool { + if count < connectFailureMaxTries { + return err.Error() == errorDialingBackendEOF + } + return false +} diff --git a/commons/sharding/gsm.go b/commons/sharding/gsm.go index b6c1f0a8..6ac1414f 100644 --- a/commons/sharding/gsm.go +++ b/commons/sharding/gsm.go @@ -121,6 +121,7 @@ func buildStatefulSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsm }, VolumeClaimTemplates: volumeClaimTemplatesForGsm(instance, OraGsmSpex), } + /** if OraGsmSpex.Replicas == 0 { OraGsmSpex.Replicas = 1 sfsetspec.Replicas = &OraGsmSpex.Replicas @@ -128,6 +129,7 @@ func buildStatefulSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsm OraGsmSpex.Replicas = 1 sfsetspec.Replicas = &OraGsmSpex.Replicas } + **/ return sfsetspec } @@ -143,10 +145,14 @@ func buildPodSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex RunAsUser: &user, FSGroup: &group, }, - InitContainers: buildInitContainerSpecForGsm(instance, OraGsmSpex), - Containers: buildContainerSpecForGsm(instance, OraGsmSpex), - Volumes: buildVolumeSpecForGsm(instance, OraGsmSpex), + Containers: buildContainerSpecForGsm(instance, OraGsmSpex), + Volumes: buildVolumeSpecForGsm(instance, OraGsmSpex), + } + + if (instance.Spec.IsDownloadScripts) && (instance.Spec.ScriptsLocation != "") { + spec.InitContainers = buildInitContainerSpecForGsm(instance, OraGsmSpex) } + if len(instance.Spec.GsmImagePullSecret) > 0 { spec.ImagePullSecrets = []corev1.LocalObjectReference{ { @@ -171,16 +177,10 @@ func buildVolumeSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSp Name: OraGsmSpex.Name + "secretmap-vol3", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: instance.Spec.Secret, + SecretName: instance.Spec.DbSecret.Name, }, }, }, - { - Name: OraGsmSpex.Name + "orascript-vol5", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, { Name: OraGsmSpex.Name + "oradshm-vol6", VolumeSource: corev1.VolumeSource{ @@ -197,6 +197,9 @@ func buildVolumeSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSp result = append(result, corev1.Volume{Name: OraGsmSpex.Name + "orastage-vol7", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: instance.Spec.StagePvcName}}}) } + if instance.Spec.IsDownloadScripts { + result = append(result, corev1.Volume{Name: OraGsmSpex.Name + "orascript-vol5", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}) + } return result } @@ -230,10 +233,15 @@ func buildContainerSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGs VolumeMounts: buildVolumeMountSpecForGsm(instance, OraGsmSpex), LivenessProbe: &corev1.Probe{ // TODO: Investigate if it's ok to call status every 10 seconds - FailureThreshold: int32(30), - PeriodSeconds: int32(240), - InitialDelaySeconds: int32(300), - TimeoutSeconds: int32(60), + FailureThreshold: int32(3), + InitialDelaySeconds: int32(30), + PeriodSeconds: func() int32 { + if instance.Spec.LivenessCheckPeriod > 0 { + return int32(instance.Spec.LivenessCheckPeriod) + } + return 60 + }(), + TimeoutSeconds: int32(20), ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ Command: getLivenessCmd("GSM"), @@ -263,7 +271,7 @@ func buildContainerSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGs return result } -//Function to build the init Container Spec +// Function to build the init Container Spec func buildInitContainerSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) []corev1.Container { var result []corev1.Container // building the init Container Spec @@ -305,7 +313,9 @@ func buildVolumeMountSpecForGsm(instance *databasev1alpha1.ShardingDatabase, Ora var result []corev1.VolumeMount result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "secretmap-vol3", MountPath: oraSecretMount, ReadOnly: true}) result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "-oradata-vol4", MountPath: oraGsmDataMount}) - result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "orascript-vol5", MountPath: oraScriptMount}) + if instance.Spec.IsDownloadScripts { + result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "orascript-vol5", MountPath: oraScriptMount}) + } result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "oradshm-vol6", MountPath: oraShm}) if len(instance.Spec.StagePvcName) != 0 { @@ -336,7 +346,7 @@ func volumeClaimTemplatesForGsm(instance *databasev1alpha1.ShardingDatabase, Ora corev1.ReadWriteOnce, }, StorageClassName: &instance.Spec.StorageClass, - Resources: corev1.ResourceRequirements{ + Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(strconv.FormatInt(int64(OraGsmSpex.StorageSizeInGb), 10) + "Gi"), }, @@ -443,7 +453,7 @@ func UpdateProvForGsm(instance *databasev1alpha1.ShardingDatabase, // Ensure deployment replicas match the desired state if sfSet.Spec.Replicas != nil { if *sfSet.Spec.Replicas != size { - msg = "Current StatefulSet replicas do not match configured Shard Replicas. Gsm is configured with only 1 but current replicas is set with " + strconv.FormatInt(int64(*sfSet.Spec.Replicas), 10) + msg = "Current StatefulSet replicas do not match configured GSM Replicas. Gsm is configured with only 1 but current replicas is set with " + strconv.FormatInt(int64(*sfSet.Spec.Replicas), 10) LogMessages("DEBUG", msg, nil, instance, logger) isUpdate = true } diff --git a/commons/sharding/provstatus.go b/commons/sharding/provstatus.go index 6c34b007..87796553 100644 --- a/commons/sharding/provstatus.go +++ b/commons/sharding/provstatus.go @@ -78,7 +78,7 @@ func UpdateGsmStatusData(instance *databasealphav1.ShardingDatabase, Specidx int K8sInternalSvcName := svcName + "." + getInstanceNs(instance) + ".svc.cluster.local" _, K8sInternalSvcIP, _ := GetSvcIp(instance.Spec.Gsm[Specidx].Name+"-0", K8sInternalSvcName, instance, kubeClient, kubeConfig, logger) _, K8sExternalSvcIP, _ := GetSvcIp(instance.Spec.Gsm[Specidx].Name+"-0", k8sExternalSvcName, instance, kubeClient, kubeConfig, logger) - DbPasswordSecret := instance.Spec.Secret + DbPasswordSecret := instance.Spec.DbSecret.Name instance.Status.Gsm.Services = GetGsmServices(instance.Spec.Gsm[Specidx].Name+"-0", instance, kubeClient, kubeConfig, logger) // externIp := strings.Replace(K8sInternalSvcIP, "/r/n", "", -1) @@ -125,7 +125,7 @@ func UpdateCatalogStatusData(instance *databasealphav1.ShardingDatabase, Specidx K8sInternalSvcName := svcName + "." + getInstanceNs(instance) + ".svc.cluster.local" _, K8sInternalSvcIP, _ := GetSvcIp(instance.Spec.Catalog[Specidx].Name+"-0", K8sInternalSvcName, instance, kubeClient, kubeConfig, logger) _, K8sExternalSvcIP, _ := GetSvcIp(instance.Spec.Catalog[Specidx].Name+"-0", k8sExternalSvcName, instance, kubeClient, kubeConfig, logger) - DbPasswordSecret := instance.Spec.Secret + DbPasswordSecret := instance.Spec.DbSecret.Name oracleSid := GetSidName(instance.Spec.Catalog[Specidx].EnvVars, instance.Spec.Catalog[Specidx].Name) oraclePdb := GetPdbName(instance.Spec.Catalog[Specidx].EnvVars, instance.Spec.Catalog[Specidx].Name) role := GetDbRole(instance.Spec.Catalog[Specidx].Name+"-0", instance, kubeClient, kubeConfig, logger) @@ -185,7 +185,7 @@ func UpdateShardStatusData(instance *databasealphav1.ShardingDatabase, Specidx i K8sInternalSvcName := svcName + "." + getInstanceNs(instance) + ".svc.cluster.local" _, K8sInternalSvcIP, _ := GetSvcIp(instance.Spec.Shard[Specidx].Name+"-0", K8sInternalSvcName, instance, kubeClient, kubeConfig, logger) _, K8sExternalSvcIP, _ := GetSvcIp(instance.Spec.Shard[Specidx].Name+"-0", k8sExternalSvcName, instance, kubeClient, kubeConfig, logger) - DbPasswordSecret := instance.Spec.Secret + DbPasswordSecret := instance.Spec.DbSecret.Name oracleSid := GetSidName(instance.Spec.Shard[Specidx].EnvVars, instance.Spec.Shard[Specidx].Name) oraclePdb := GetPdbName(instance.Spec.Shard[Specidx].EnvVars, instance.Spec.Shard[Specidx].Name) role := GetDbRole(instance.Spec.Shard[Specidx].Name+"-0", instance, kubeClient, kubeConfig, logger) @@ -347,7 +347,7 @@ func CheckGsmStatus(gname string, instance *databasealphav1.ShardingDatabase, ku return nil } -//============ Functiont o check the status of the Shard and catalog ========= +// ============ Functiont o check the status of the Shard and catalog ========= // ================================ Validate shard =========================== func ValidateDbSetup(podName string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 5a25a78d..7ed51fde 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -41,13 +41,17 @@ package commons import ( "context" "fmt" + "slices" databasealphav1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" "regexp" "strconv" "strings" + "os" + "github.com/go-logr/logr" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/ons" @@ -77,6 +81,7 @@ const ( oraRunAsUser = int64(54321) oraFsGroup = int64(54321) oraScriptMount = "/opt/oracle/scripts/sharding/scripts" + oraDbScriptMount = "/opt/oracle/scripts/sharding" oraDataMount = "/opt/oracle/oradata" oraGsmDataMount = "/opt/oracle/gsmdata" oraConfigMapMount = "/mnt/config-map" @@ -91,6 +96,9 @@ const ( oraLocalOnsPort = 6123 oraAgentPort = 8080 ShardingDatabaseFinalizer = "Shardingdb.oracle.com" + TmpLoc = "/var/tmp" + connectFailureMaxTries = 5 + errorDialingBackendEOF = "error dialing backend: EOF" ) // Function to build the env var specification @@ -98,13 +106,10 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da var result []corev1.EnvVar var varinfo string var sidFlag bool = false - var secretFlag bool = false - var pwdFileFLag bool = false - var pwdKeyFlag bool = false var pdbFlag bool = false var sDirectParam bool = false var sGroup1Params bool = false - var sGroup2Params bool = false + //var sGroup2Params bool = false var catalogParams bool = false var oldPdbFlag bool = false var oldSidFlag bool = false @@ -115,15 +120,6 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da if variable.Name == "ORACLE_SID" { sidFlag = true } - if variable.Name == "SECRET_VOLUME" { - secretFlag = true - } - if variable.Name == "COMMON_OS_PWD_FILE" { - pwdFileFLag = true - } - if variable.Name == "PWD_KEY" { - pwdKeyFlag = true - } if variable.Name == "ORACLE_PDB" { pdbFlag = true } @@ -133,9 +129,6 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da if variable.Name == "SHARD1_GROUP_PARAMS" { sGroup1Params = true } - if variable.Name == "SHARD2_GROUP_PARAMS" { - sGroup2Params = true - } if variable.Name == "CATALOG_PARAMS" { catalogParams = true } @@ -153,6 +146,7 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da } result = append(result, corev1.EnvVar{Name: variable.Name, Value: variable.Value}) } + if !shardSetupFlag { if restype == "SHARD" { result = append(result, corev1.EnvVar{Name: "SHARD_SETUP", Value: "true"}) @@ -172,7 +166,6 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da result = append(result, corev1.EnvVar{Name: "ENABLE_ARCHIVELOG", Value: "true"}) } } - if !sidFlag { if restype == "SHARD" { result = append(result, corev1.EnvVar{Name: "ORACLE_SID", Value: strings.ToUpper(name)}) @@ -189,29 +182,84 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da result = append(result, corev1.EnvVar{Name: "ORACLE_PDB", Value: strings.ToUpper(name) + "PDB"}) } } - if !secretFlag { - result = append(result, corev1.EnvVar{Name: "SECRET_VOLUME", Value: "/mnt/secrets"}) + // Secret Settings + + if strings.ToLower(instance.Spec.DbSecret.EncryptionType) != "base64" { + result = append(result, corev1.EnvVar{Name: "PWD_KEY", Value: instance.Spec.DbSecret.KeyFileName}) + result = append(result, corev1.EnvVar{Name: "COMMON_OS_PWD_FILE", Value: instance.Spec.DbSecret.PwdFileName}) + } else { + result = append(result, corev1.EnvVar{Name: "PASSWORD_FILE", Value: instance.Spec.DbSecret.PwdFileName}) } - if !pwdFileFLag { - result = append(result, corev1.EnvVar{Name: "COMMON_OS_PWD_FILE", Value: "common_os_pwdfile.enc"}) + if len(instance.Spec.DbSecret.PwdFileMountLocation) != 0 { + result = append(result, corev1.EnvVar{Name: "SECRET_VOLUME", Value: instance.Spec.DbSecret.PwdFileMountLocation}) + } else { + result = append(result, corev1.EnvVar{Name: "SECRET_VOLUME", Value: oraSecretMount}) } - if !pwdKeyFlag { - result = append(result, corev1.EnvVar{Name: "PWD_KEY", Value: "pwd.key"}) + if len(instance.Spec.DbSecret.KeyFileMountLocation) != 0 { + result = append(result, corev1.EnvVar{Name: "KEY_SECRET_VOLUME", Value: instance.Spec.DbSecret.KeyFileMountLocation}) + } else { + result = append(result, corev1.EnvVar{Name: "KEY_SECRET_VOLUME", Value: oraSecretMount}) } + if restype == "GSM" { if !sDirectParam { - // varinfo = "director_name=sharddirector" + sDirectorCounter + ";director_region=primary;director_port=1521" + //varinfo = "director_name=sharddirector" + sDirectorCounter + ";director_region=primary;director_port=1521" varinfo = directorParams result = append(result, corev1.EnvVar{Name: "SHARD_DIRECTOR_PARAMS", Value: varinfo}) } - if !sGroup1Params { - varinfo = "group_name=shardgroup1;deploy_as=primary;group_region=primary" - result = append(result, corev1.EnvVar{Name: "SHARD1_GROUP_PARAMS", Value: varinfo}) + if strings.ToUpper(instance.Spec.ShardingType) != "USER" { + if !sGroup1Params { + if len(instance.Spec.GsmShardGroup) > 0 { + for i := 0; i < len(instance.Spec.GsmShardGroup); i++ { + if strings.ToUpper(instance.Spec.GsmShardGroup[i].DeployAs) == "PRIMARY" { + group_name := instance.Spec.GsmShardGroup[i].Name + //deploy_as := instance.Spec.ShardGroup[i].DeployAs + region := instance.Spec.GsmShardGroup[i].Region + varinfo = "group_name=" + group_name + ";" + "deploy_as=primary;" + "group_region=" + region + result = append(result, corev1.EnvVar{Name: "SHARD1_GROUP_PARAMS", Value: varinfo}) + } + if strings.ToUpper(instance.Spec.GsmShardGroup[i].DeployAs) == "STANDBY" { + group_name := instance.Spec.GsmShardGroup[i].Name + //deploy_as := instance.Spec.ShardGroup[i].DeployAs + region := instance.Spec.GsmShardGroup[i].Region + varinfo = "group_name=" + group_name + ";" + "deploy_as=standby;" + "group_region=" + region + result = append(result, corev1.EnvVar{Name: "SHARD2_GROUP_PARAMS", Value: varinfo}) + } + } + } + } else { + varinfo = "group_name=shardgroup1;deploy_as=primary;group_region=primary" + result = append(result, corev1.EnvVar{Name: "SHARD1_GROUP_PARAMS", Value: varinfo}) + } + } + + if strings.ToUpper(instance.Spec.ShardingType) == "USER" { + result = append(result, corev1.EnvVar{Name: "SHARDING_TYPE", Value: "USER"}) + } + // SERVICE Params setting + var svc string + if len(instance.Spec.GsmService) > 0 { + svc = "" + for i := 0; i < len(instance.Spec.GsmService); i++ { + svc = svc + "service_name=" + instance.Spec.GsmService[i].Name + ";" + if len(instance.Spec.GsmService[i].Role) != 0 { + svc = svc + "service_role=" + instance.Spec.GsmService[i].Role + } else { + svc = svc + "service_role=primary" + } + result = append(result, corev1.EnvVar{Name: "SERVICE" + fmt.Sprint(i) + "_PARAMS", Value: svc}) + svc = "" + } + } + + if strings.ToUpper(instance.Spec.GsmDevMode) != "FALSE" { + result = append(result, corev1.EnvVar{Name: "DEV_MODE", Value: "TRUE"}) } - if instance.Spec.IsDataGuard { - if !sGroup2Params { - varinfo = "group_name=shardgroup2;deploy_as=standby;group_region=standby" - result = append(result, corev1.EnvVar{Name: "SHARD2_GROUP_PARAMS", Value: varinfo}) + + if strings.ToUpper(instance.Spec.InvitedNodeSubnetFlag) != "FALSE" { + result = append(result, corev1.EnvVar{Name: "INVITED_NODE_SUBNET_FLAG", Value: "TRUE"}) + if instance.Spec.InvitedNodeSubnet != "" { + result = append(result, corev1.EnvVar{Name: "INVITED_NODE_SUBNET", Value: instance.Spec.InvitedNodeSubnet}) } } if !catalogParams { @@ -625,17 +673,55 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { var sidFlag bool = false var pdbFlag bool = false var portFlag bool = false - var regionFlag bool = false var cnameFlag bool = false var chunksFlag bool = false var sidName string var pdbName string var cport string - var cregion string var cname string var catchunks string + var catalog_region, shard_space string result = "catalog_host=" + instance.Spec.Catalog[0].Name + "-0" + "." + instance.Spec.Catalog[0].Name + ";" + + //Checking if replcia type set to native + var sspace_arr []string + if strings.ToUpper(instance.Spec.ShardingType) == "USER" { + shard_space = "" + result = result + "sharding_type=user;" + for i := 0; i < len(instance.Spec.Shard); i++ { + sspace_arr = append(sspace_arr, instance.Spec.Shard[i].ShardSpace) + } + slices.Sort(sspace_arr) + sspace_arr = slices.Compact(sspace_arr) //[a b c d] + for i := 0; i < len(sspace_arr); i++ { + shard_space = shard_space + sspace_arr[i] + "," + } + shard_space = strings.TrimSuffix(shard_space, ",") + result = result + "shard_space=" + shard_space + ";" + } else if strings.ToUpper(instance.Spec.ReplicationType) == "NATIVE" { + result = result + "repl_type=native;" + } else { + fmt.Fprintln(os.Stdout, []any{""}...) + } + + var region_arr []string + for i := 0; i < len(instance.Spec.Shard); i++ { + region_arr = append(region_arr, instance.Spec.Shard[i].ShardRegion) + } + + slices.Sort(region_arr) + region_arr = slices.Compact(region_arr) //[a b c d] + for i := 0; i < len(region_arr); i++ { + catalog_region = catalog_region + region_arr[i] + "," + } + catalog_region = strings.TrimSuffix(catalog_region, ",") + result = result + "catalog_region=" + catalog_region + ";" + + if len(instance.Spec.ShardConfigName) != 0 { + result = result + "shard_configname=" + instance.Spec.ShardConfigName + ";" + } + for _, variable := range variables { if variable.Name == "ORACLE_SID" { sidFlag = true @@ -649,10 +735,6 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { portFlag = true cport = variable.Value } - if variable.Name == "CATALOG_REGION" { - regionFlag = true - cregion = variable.Value - } if variable.Name == "CATALOG_NAME" { cnameFlag = true cname = variable.Value @@ -661,6 +743,7 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { chunksFlag = true catchunks = variable.Value } + } if !sidFlag { @@ -697,15 +780,7 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { if chunksFlag { result = result + "catalog_chunks=" + catchunks + ";" } - - if !regionFlag { - varinfo = "catalog_region=primary,standby" - result = result + varinfo - } else { - varinfo = "catalog_region=" + cregion - result = result + varinfo - } - + result = strings.TrimSuffix(result, ";") return result } @@ -731,31 +806,40 @@ func buildDirectorParams(instance *databasealphav1.ShardingDatabase, oraGsmSpex result = result + varinfo } - switch idx { - case 0: - varinfo = "director_region=primary;" + if oraGsmSpex.Region != "" { + varinfo = "director_region=" + oraGsmSpex.Region + ";" result = result + varinfo - case 1: - varinfo = "director_region=standby;" + } else { + switch idx { + case 0: + varinfo = "director_region=primary;" + result = result + varinfo + case 1: + varinfo = "director_region=standby;" + result = result + varinfo + default: + // Do nothing + } result = result + varinfo - default: - // Do nothing } if !dportFlag { varinfo = "director_port=1522" result = result + varinfo } - + result = strings.TrimSuffix(result, ";") return result } -func BuildShardParams(sfSet *appsv1.StatefulSet) string { +func BuildShardParams(instance *databasealphav1.ShardingDatabase, sfSet *appsv1.StatefulSet, OraShardSpex databasev1alpha1.ShardSpec) string { var variables []corev1.EnvVar = sfSet.Spec.Template.Spec.Containers[0].Env var result string var varinfo string var isShardPort bool = false - var isShardGrp bool = false + //var isShardGrp bool = false + //var i int32 + //var isShardSpace bool = false + //var isShardRegion bool = false result = "shard_host=" + sfSet.Name + "-0" + "." + sfSet.Name + ";" for _, variable := range variables { @@ -772,23 +856,32 @@ func BuildShardParams(sfSet *appsv1.StatefulSet) string { result = result + varinfo isShardPort = true } - if variable.Name == "SHARD_GROUP" { - varinfo = "shard_group=" + variable.Value + ";" - result = result + varinfo - isShardGrp = true - } + + } + if OraShardSpex.ShardGroup != "" { + varinfo = "shard_group=" + OraShardSpex.ShardGroup + ";" + result = result + varinfo } - if !isShardPort { - varinfo = "shard_port=" + "1521" + ";" + if OraShardSpex.ShardSpace != "" { + varinfo = "shard_space=" + OraShardSpex.ShardSpace + ";" + result = result + varinfo + } + if OraShardSpex.ShardRegion != "" { + varinfo = "shard_region=" + OraShardSpex.ShardRegion + ";" result = result + varinfo } - if !isShardGrp { - varinfo = "shard_group=" + "shardgroup1" + if OraShardSpex.DeployAs != "" { + varinfo = "deploy_as=" + OraShardSpex.DeployAs + ";" result = result + varinfo } + if !isShardPort { + varinfo = "shard_port=" + "1521" + ";" + result = result + varinfo + } + result = strings.TrimSuffix(result, ";") return result } @@ -858,7 +951,7 @@ func getNoChunksCmd(sparamStr string) []string { func shardValidationCmd() []string { - var oraShardValidateCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkliveness=true ", "--optype=primaryshard"} + var oraShardValidateCmd = []string{oraDbScriptMount + "/cmdExec", "/bin/python", oraDbScriptMount + "/main.py ", "--checkliveness=true ", "--optype=primaryshard"} return oraShardValidateCmd } @@ -884,25 +977,47 @@ func getShardDelCmd(sparams string) []string { func getLivenessCmd(resType string) []string { var livenessCmd []string if resType == "SHARD" { - livenessCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkliveness=true", "--optype=primaryshard"} + livenessCmd = []string{oraDbScriptMount + "/cmdExec", "/bin/python", oraDbScriptMount + "/main.py ", "--checkliveness=true", "--optype=primaryshard"} } if resType == "CATALOG" { - livenessCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkliveness=true", "--optype=catalog"} + livenessCmd = []string{oraDbScriptMount + "/cmdExec", "/bin/python", oraDbScriptMount + "/main.py ", "--checkliveness=true", "--optype=catalog"} } if resType == "GSM" { livenessCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkliveness=true", "--optype=gsm"} } if resType == "STANDBY" { - livenessCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkliveness=true", "--optype=standbyshard"} + livenessCmd = []string{oraDbScriptMount + "/cmdExec", "/bin/python", oraDbScriptMount + "/main.py ", "--checkliveness=true", "--optype=standbyshard"} } return livenessCmd } +func getReadinessCmd(resType string) []string { + var readynessCmd []string + if resType == "SHARD" { + readynessCmd = []string{oraDbScriptMount + "/cmdExec", "/bin/python", oraDbScriptMount + "/main.py ", "--checkreadyness=true", "--optype=primaryshard"} + } + if resType == "CATALOG" { + readynessCmd = []string{oraDbScriptMount + "/cmdExec", "/bin/python", oraDbScriptMount + "/main.py ", "--checkreadyness=true", "--optype=catalog"} + } + if resType == "GSM" { + readynessCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkreadyness=true", "--optype=gsm"} + } + if resType == "STANDBY" { + readynessCmd = []string{oraDbScriptMount + "/cmdExec", "/bin/python", oraDbScriptMount + "/main.py ", "--checkreadyness=true", "--optype=standbyshard"} + } + return readynessCmd +} + func getGsmShardValidateCmd(shardName string) []string { var validateCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--validateshard=" + strconv.Quote(shardName), "--optype=gsm"} return validateCmd } +func GetTdeKeyLocCmd() []string { + var tdeKeyCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--gettdekey=true", "--optype=gsm"} + return tdeKeyCmd +} + func getOnlineShardCmd(sparamStr string) []string { var onlineCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkonlineshard=" + strconv.Quote(sparamStr), "--optype=gsm"} return onlineCmd @@ -927,9 +1042,9 @@ func getInitContainerCmd(resType string, name string, ) string { var initCmd string if resType == "WEB" { - initCmd = "chown -R 54321:54321 " + oraScriptMount + ";chmod 755 " + oraScriptMount + "/*;chown -R 54321:54321 /opt/oracle/oradata;chmod 750 /opt/oracle/oradata" + initCmd = "chown -R 54321:54321 " + oraDbScriptMount + ";chmod 755 " + oraDbScriptMount + "/*;chown -R 54321:54321 /opt/oracle/oradata;chmod 750 /opt/oracle/oradata" } else { - initCmd = resType + ";chown -R 54321:54321 " + oraScriptMount + ";chmod 755 " + oraScriptMount + "/*;chown -R 54321:54321 /opt/oracle/oradata;chmod 750 /opt/oracle/oradata" + initCmd = resType + ";chown -R 54321:54321 " + oraDbScriptMount + ";chmod 755 " + oraDbScriptMount + "/*;chown -R 54321:54321 /opt/oracle/oradata;chmod 750 /opt/oracle/oradata" } return initCmd } @@ -945,6 +1060,11 @@ func getGsmInitContainerCmd(resType string, name string, return initCmd } +func getResetPasswdCmd(sparamStr string) []string { + var resetPasswdCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--resetpassword=true"} + return resetPasswdCmd +} + func GetFmtStr(pstr string, ) string { return "[" + pstr + "]" @@ -1249,3 +1369,7 @@ func SendNotification(title string, body string, instance *databasealphav1.Shard LogMessages("DEBUG", msg, nil, instance, logger) } } + +func GetSecretMount() string { + return oraSecretMount +} diff --git a/commons/sharding/shard.go b/commons/sharding/shard.go index ecebf606..6dd7a6ea 100644 --- a/commons/sharding/shard.go +++ b/commons/sharding/shard.go @@ -141,10 +141,14 @@ func buildPodSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardS RunAsUser: &user, FSGroup: &group, }, - InitContainers: buildInitContainerSpecForShard(instance, OraShardSpex), - Containers: buildContainerSpecForShard(instance, OraShardSpex), - Volumes: buildVolumeSpecForShard(instance, OraShardSpex), + Containers: buildContainerSpecForShard(instance, OraShardSpex), + Volumes: buildVolumeSpecForShard(instance, OraShardSpex), } + + if (instance.Spec.IsDownloadScripts) && (instance.Spec.ScriptsLocation != "") { + spec.InitContainers = buildInitContainerSpecForShard(instance, OraShardSpex) + } + if len(instance.Spec.DbImagePullSecret) > 0 { spec.ImagePullSecrets = []corev1.LocalObjectReference{ { @@ -171,16 +175,10 @@ func buildVolumeSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraSha Name: OraShardSpex.Name + "secretmap-vol3", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: instance.Spec.Secret, + SecretName: instance.Spec.DbSecret.Name, }, }, }, - { - Name: OraShardSpex.Name + "orascript-vol5", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, { Name: OraShardSpex.Name + "oradshm-vol6", VolumeSource: corev1.VolumeSource{ @@ -196,7 +194,9 @@ func buildVolumeSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraSha if len(instance.Spec.StagePvcName) != 0 { result = append(result, corev1.Volume{Name: OraShardSpex.Name + "orastage-vol7", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: instance.Spec.StagePvcName}}}) } - + if instance.Spec.IsDownloadScripts { + result = append(result, corev1.Volume{Name: OraShardSpex.Name + "orascript-vol5", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}) + } return result } @@ -218,28 +218,50 @@ func buildContainerSpecForShard(instance *databasev1alpha1.ShardingDatabase, Ora VolumeMounts: buildVolumeMountSpecForShard(instance, OraShardSpex), LivenessProbe: &corev1.Probe{ // TODO: Investigate if it's ok to call status every 10 seconds - FailureThreshold: int32(30), - PeriodSeconds: int32(240), - InitialDelaySeconds: int32(300), - TimeoutSeconds: int32(120), + FailureThreshold: int32(3), + InitialDelaySeconds: int32(30), + PeriodSeconds: func() int32 { + if instance.Spec.LivenessCheckPeriod > 0 { + return int32(instance.Spec.LivenessCheckPeriod) + } + return 60 + }(), + TimeoutSeconds: int32(30), ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: getLivenessCmd("SHARD"), + Command: []string{"/bin/sh", "-c", "if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi "}, }, }, }, /** + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + //Command: getReadinessCmd("SHARD"), + Command: []string{"/bin/sh", "-c", "if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi "}, + }, + }, + InitialDelaySeconds: 20, + TimeoutSeconds: 20, + PeriodSeconds: func() int32 { + if instance.Spec.ReadinessCheckPeriod > 0 { + return int32(instance.Spec.ReadinessCheckPeriod) + } + return 60 + }(), + }, + **/ // Disabling this because ping stop working and sharding topologu never gets configured. StartupProbe: &corev1.Probe{ - FailureThreshold: int32(30), - PeriodSeconds: int32(180), - Handler: corev1.Handler{ + FailureThreshold: int32(30), + PeriodSeconds: int32(180), + InitialDelaySeconds: int32(30), + ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: getLivenessCmd("SHARD"), + Command: []string{"/bin/sh", "-c", "if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi "}, }, }, }, - **/ Env: buildEnvVarsSpec(instance, OraShardSpex.EnvVars, OraShardSpex.Name, "SHARD", false, "NONE"), } @@ -257,10 +279,10 @@ func buildContainerSpecForShard(instance *databasev1alpha1.ShardingDatabase, Ora return result } -//Function to build the init Container Spec +// Function to build the init Container Spec func buildInitContainerSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) []corev1.Container { var result []corev1.Container - privFlag := true + privFlag := false var uid int64 = 0 // building the init Container Spec @@ -300,7 +322,9 @@ func buildVolumeMountSpecForShard(instance *databasev1alpha1.ShardingDatabase, O var result []corev1.VolumeMount result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "secretmap-vol3", MountPath: oraSecretMount, ReadOnly: true}) result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "-oradata-vol4", MountPath: oraDataMount}) - result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "orascript-vol5", MountPath: oraScriptMount}) + if instance.Spec.IsDownloadScripts { + result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "orascript-vol5", MountPath: oraDbScriptMount}) + } result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "oradshm-vol6", MountPath: oraShm}) if len(instance.Spec.StagePvcName) != 0 { @@ -331,7 +355,7 @@ func volumeClaimTemplatesForShard(instance *databasev1alpha1.ShardingDatabase, O corev1.ReadWriteOnce, }, StorageClassName: &instance.Spec.StorageClass, - Resources: corev1.ResourceRequirements{ + Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(strconv.FormatInt(int64(OraShardSpex.StorageSizeInGb), 10) + "Gi"), }, diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index c76b7f45..06236c5f 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -44,6 +44,7 @@ import ( "fmt" "reflect" "strconv" + "strings" "time" "github.com/go-logr/logr" @@ -70,7 +71,7 @@ import ( shardingv1 "github.com/oracle/oracle-database-operator/commons/sharding" ) -//Sharding Topology +// Sharding Topology type ShardingTopology struct { topicid string Instance *databasev1alpha1.ShardingDatabase @@ -89,12 +90,14 @@ type ShardingDatabaseReconciler struct { kubeConfig clientcmd.ClientConfig Recorder record.EventRecorder osh []*ShardingTopology + InCluster bool + Namespace string } // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases/status,verbs=get;update;patch // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases/finalizers,verbs=get;create;update;patch;delete -// +kubebuilder:rbac:groups=core,resources=pods;pods/log;pods/exec;secrets;services;events;nodes;configmaps;persistentvolumeclaims;namespaces,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=pods;pods/log;pods/exec;secrets;containers;services;events;configmaps;persistentvolumeclaims;namespaces,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=pods/exec,verbs=create // +kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups='',resources=statefulsets/finalizers,verbs=get;list;watch;create;update;patch;delete @@ -184,7 +187,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req if !instFlag { //r.setCrdLifeCycleState(instance, &result, &err, stateType) result = resultNq - return result, fmt.Errorf("DId not fid the instance in checkProvInstance") + return result, fmt.Errorf("DId not find the instance in checkProvInstance") } // ================================ OCI Notification Provider =========== @@ -441,6 +444,7 @@ func (r *ShardingDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&appsv1.StatefulSet{}). Owns(&corev1.Service{}). Owns(&corev1.Pod{}). + Owns(&corev1.Secret{}). WithEventFilter(r.eventFilterPredicate()). WithOptions(controller.Options{MaxConcurrentReconciles: 50}). //MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1 Complete(r) @@ -454,6 +458,22 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate return true }, UpdateFunc: func(e event.UpdateEvent) bool { + if old, ok := e.ObjectOld.(*corev1.Secret); ok { + if new, ok := e.ObjectNew.(*corev1.Secret); ok { + for i := 0; i < len(r.osh); i++ { + oshInst := r.osh[i] + if (new.Name == oshInst.Instance.Spec.DbSecret.Name) && (new.Name == old.Name) { + _, ok := old.Data[oshInst.Instance.Spec.DbSecret.PwdFileName] + if ok { + if !reflect.DeepEqual(old.Data[oshInst.Instance.Spec.DbSecret.PwdFileName], new.Data[oshInst.Instance.Spec.DbSecret.PwdFileName]) { + shardingv1.LogMessages("INFO", "Secret Changed", nil, oshInst.Instance, r.Log) + } + } + shardingv1.LogMessages("INFO", "Secret update block", nil, oshInst.Instance, r.Log) + } + } + } + } return true }, DeleteFunc: func(e event.DeleteEvent) bool { @@ -495,13 +515,32 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate } } +// ================ Function to check secret update============= +func (r *ShardingDatabaseReconciler) UpdateSecret(instance *databasev1alpha1.ShardingDatabase, kClient client.Client, logger logr.Logger) (ctrl.Result, error) { + + sc := &corev1.Secret{} + //var err error + + // Reading a Secret + var err error = kClient.Get(context.TODO(), types.NamespacedName{ + Name: instance.Spec.DbSecret.Name, + Namespace: instance.Spec.Namespace, + }, sc) + + if err != nil { + return ctrl.Result{}, nil + } + + return ctrl.Result{}, nil +} + // ================== Function to get the Notification controller ============== func (r *ShardingDatabaseReconciler) getOnsConfigProvider(instance *databasev1alpha1.ShardingDatabase, idx int, ) { var err error - if instance.Spec.NsConfigMap != "" && instance.Spec.NsSecret != "" && r.osh[idx].onsProviderFlag != true { - cmName := instance.Spec.NsConfigMap - secName := instance.Spec.NsSecret + if instance.Spec.DbSecret.NsConfigMap != "" && instance.Spec.DbSecret.NsSecret != "" && r.osh[idx].onsProviderFlag != true { + cmName := instance.Spec.DbSecret.NsConfigMap + secName := instance.Spec.DbSecret.NsSecret shardingv1.LogMessages("DEBUG", "Received parameters are "+shardingv1.GetFmtStr(cmName)+","+shardingv1.GetFmtStr(secName), nil, instance, r.Log) region, user, tenancy, passphrase, fingerprint, topicid := shardingv1.ReadConfigMap(cmName, instance, r.Client, r.Log) privatekey := shardingv1.ReadSecret(secName, instance, r.Client, r.Log) @@ -831,6 +870,7 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.Sha var eventMsg string var eventErr string = "Spec Error" + var i int32 lastSuccSpec, err := instance.GetLastSuccessfulSpec() if err != nil { @@ -841,6 +881,53 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.Sha if lastSuccSpec == nil { // Logic to check if inital Spec is good or not + err = r.checkShardingType(instance, idx) + if err != nil { + return err + } + + if len(instance.Spec.Shard) > 0 { + for i = 0; i < int32(len(instance.Spec.Shard)); i++ { + OraShardSpex := instance.Spec.Shard[i] + if OraShardSpex.IsDelete != true { + err = r.checkShardSpace(instance, OraShardSpex) + if err != nil { + return err + } + err = r.checkShardGroup(instance, OraShardSpex) + if err != nil { + return err + } + } + } + } + + // Check Secret configuration + if instance.Spec.DbSecret == nil { + return fmt.Errorf("Secret specification cannot be null, you need to set secret details") + } else { + if len(instance.Spec.DbSecret.Name) == 0 { + return fmt.Errorf("instance.Spec.DbSecret.Name cannot be empty") + } + if len(instance.Spec.DbSecret.PwdFileName) == 0 { + return fmt.Errorf("instance.Spec.DbSecret.PwdFileName cannot be empty") + } + if strings.ToLower(instance.Spec.DbSecret.EncryptionType) != "base64" { + if strings.ToLower(instance.Spec.DbSecret.KeyFileName) == "" { + return fmt.Errorf("instance.Spec.DbSecret.KeyFileName cannot be empty") + } + } + if len(instance.Spec.DbSecret.PwdFileMountLocation) == 0 { + msg := "instance.Spec.DbSecret.PwdFileMountLocation is not set. Setting it to default " + shardingv1.GetSecretMount() + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + } + + if len(instance.Spec.DbSecret.KeyFileMountLocation) == 0 { + msg := "instance.Spec.DbSecret.KeyFileMountLocation is not set. Setting it to default " + shardingv1.GetSecretMount() + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + } + } + // Once the initial Spec is been validated then update the last Sucessful Spec err = instance.UpdateLastSuccessfulSpec(r.Client) if err != nil { @@ -890,6 +977,65 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.Sha return nil } +func (r *ShardingDatabaseReconciler) checkShardingType(instance *databasev1alpha1.ShardingDatabase, idx int) error { + var i, k int32 + var regionFlag bool + + for k = 0; k < int32(len(instance.Spec.Gsm)); k++ { + regionFlag = false + for i = 0; i < int32(len(instance.Spec.Shard)); i++ { + if instance.Spec.Gsm[k].Region == instance.Spec.Shard[i].ShardRegion { + regionFlag = true + } + } + if !regionFlag { + msg := instance.Spec.Gsm[k].Region + " does not match with any region with Shard region. Region will be created during shard director provisioning" + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + } + } + + return nil +} + +// Check the ShardGroups/ Shard Space and Shard group Name +// checkShrdGSR is Shardgroup/ShardSpace/ShardRegion + +func (r *ShardingDatabaseReconciler) checkShardSpace(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) error { + + if instance.Spec.ShardingType != "" { + // Check for the Sharding Type and if it is USER do following + if strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) == "USER" { + if len(OraShardSpex.ShardRegion) == 0 { + return fmt.Errorf("Shard region cannot be empty! ") + } + if len(OraShardSpex.ShardSpace) == 0 { + return fmt.Errorf("Shard Space in " + OraShardSpex.Name + " cannot be empty") + } + } + } + return nil +} + +// Check the ShardGroups/ Shard Space and Shard group Name +// checkShrdGSR is Shardgroup/ShardSpace/ShardRegion + +func (r *ShardingDatabaseReconciler) checkShardGroup(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) error { + + // We need to check Shard Region and Shard Group for ShardingType='SYSTEM' and 'NATIVE' + if strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) != "USER" { + if len(OraShardSpex.ShardRegion) == 0 { + return fmt.Errorf("Shard region cannot be empty! in " + OraShardSpex.Name) + } + if len(OraShardSpex.ShardGroup) == 0 { + return fmt.Errorf("Shard group in " + OraShardSpex.Name + " cannot be empty") + } + + // + + } + return nil +} + // Compare GSM Env Variables func (r *ShardingDatabaseReconciler) comapreGsmEnvVariables(instance *databasev1alpha1.ShardingDatabase, lastSuccSpec *databasev1alpha1.ShardingDatabaseSpec) bool { @@ -1029,6 +1175,7 @@ func (r *ShardingDatabaseReconciler) validateGsm(instance *databasev1alpha1.Shar if availableFlag != true { gsmSfSet = gsmSfSet1 gsmPod = gsmPod1 + // availableFlag = true availableFlag = true } } @@ -1102,6 +1249,7 @@ func (r *ShardingDatabaseReconciler) validateCatalog(instance *databasev1alpha1. if availlableFlag != true { catalogSfSet = catalogSfSet1 catalogPod = catalogPod1 + // availlableFlag = true availlableFlag = true } } @@ -1202,7 +1350,6 @@ func (r *ShardingDatabaseReconciler) validateShard(instance *databasev1alpha1.Sh } // This function updates the shard topology over all -// func (r *ShardingDatabaseReconciler) updateShardTopologyStatus(instance *databasev1alpha1.ShardingDatabase) error { //shardPod := &corev1.Pod{} //gsmSfSet := &appsv1.StatefulSet{} @@ -1237,7 +1384,7 @@ func (r *ShardingDatabaseReconciler) updateShardTopologyShardsInGsm(instance *da if err != nil { continue } else { - _ = r.verifyShards(instance, gsmPod, shardSfSet) + _ = r.verifyShards(instance, gsmPod, shardSfSet, OraShardSpex) } } @@ -1369,7 +1516,7 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 //gsmSfSet := &appsv1.StatefulSet{} gsmPod := &corev1.Pod{} var sparams1 string - var deployFlag = false + var deployFlag = true var errStr = false //var msg string @@ -1383,7 +1530,8 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex := instance.Spec.Shard[i] // stateStr := shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) - // !strings.Contains(stateStr, "DELETE") + // strings.Contains(stateStr, "DELETE") + if OraShardSpex.IsDelete != true { if setLifeCycleFlag != true { setLifeCycleFlag = true @@ -1395,52 +1543,88 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 shardSfSet, _, err = r.validateShard(instance, OraShardSpex, int(i)) if err != nil { errStr = true + deployFlag = false continue } // 2nd Step is to check if GSM is in good state if not then just return because you can't do anything _, gsmPod, err = r.validateGsm(instance) if err != nil { + deployFlag = false return err } // 3rd step to check if shard is in GSM if not then continue - sparams := shardingv1.BuildShardParams(shardSfSet) + sparams := shardingv1.BuildShardParams(instance, shardSfSet, OraShardSpex) sparams1 = sparams err = shardingv1.CheckShardInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err == nil { // if you are in this block then it means that shard already exist in the GSM and we do not need to anything continue } + + // Copy file from pod to FS + // configrest, kclientset, err := shardingv1.GetPodCopyConfig(r.kubeClient, r.kubeConfig, instance, r.Log) + // if err != nil { + // return fmt.Errorf("Error occurred in getting KubeConfig, cannot perform copy operation from the pod") + // } + + // _, _, err = shardingv1.ExecCommand(gsmPod.Name, shardingv1.GetTdeKeyLocCmd(), r.kubeClient, r.kubeConfig, instance, r.Log) + // if err != nil { + // fmt.Printf("Error occurred during the while getting the TDE key from the pod " + gsmPod.Name) + // //return err + // } + // fileName := "/tmp/tde_key" + // last := fileName[strings.LastIndex(fileName, "/")+1:] + // fileName1 := last + // fsLoc := shardingv1.TmpLoc + "/" + fileName1 + // _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fmt.Sprintf("%s/%s:/%s", instance.Spec.Namespace, gsmPod.Name, fileName), fsLoc, "") + // if err != nil { + // fmt.Printf("failed to copy file") + // //return err + // } + + // Copying it to Shard Pod + // _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fsLoc, fmt.Sprintf("%s/%s:/%s", instance.Spec.Namespace, OraShardSpex.Name+"-0", fsLoc), "") + // if err != nil { + // fmt.Printf("failed to copy file") + // //return err + /// } + // If the shard doesn't exist in GSM then just add the shard statefulset and update GSM shard status // ADD Shard in GSM + r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.AddingShardState)) - err := shardingv1.AddShardInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) + err = shardingv1.AddShardInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.AddingShardErrorState)) title = "Shard Addition Failure" message = "Error occurred during shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " addition." r.sendMessage(instance, title, message) - } else { - deployFlag = true + deployFlag = false } } } + if errStr == true { + shardingv1.LogMessages("INFO", "Some shards are still pending for addition. Requeue the reconcile loop.", nil, instance, r.Log) + return fmt.Errorf("shards are not ready for addition.") + } + // ======= Deploy Shard Logic ========= if deployFlag == true { _ = shardingv1.DeployShardInGsm(gsmPod.Name, sparams1, instance, r.kubeClient, r.kubeConfig, r.Log) r.updateShardTopologyShardsInGsm(instance, gsmPod) + } else { + shardingv1.LogMessages("INFO", "Shards are not added in GSM. Deploy operation will happen after shard addition. Requeue the reconcile loop.", nil, instance, r.Log) + return fmt.Errorf("shards addition are pending.") } } - if errStr == true { - shardingv1.LogMessages("INFO", "Some shards are still pending for addition. Requeue the reconcile loop.", nil, instance, r.Log) - return fmt.Errorf("Shard Addition is pending.") - } + shardingv1.LogMessages("INFO", "Completed the shard addition operation. For details, check the CRD resource status for GSM and Shards.", nil, instance, r.Log) return nil } // This function Check the online shard -func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev1alpha1.ShardingDatabase, gsmPod *corev1.Pod, shardSfSet *appsv1.StatefulSet) error { +func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev1alpha1.ShardingDatabase, gsmPod *corev1.Pod, shardSfSet *appsv1.StatefulSet, OraShardSpex databasev1alpha1.ShardSpec) error { //var result ctrl.Result //var i int32 var err error @@ -1448,13 +1632,15 @@ func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev1alpha1.Sha var message string // ================================ Check Shards ================== //veryify shard make shard state online and it must be executed to check shard state after every CRUD operation - sparams := shardingv1.BuildShardParams(shardSfSet) + sparams := shardingv1.BuildShardParams(instance, shardSfSet, OraShardSpex) err = shardingv1.CheckOnlineShardInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { // If the shard doesn't exist in GSM then just delete the shard statefulset and update GSM shard status /// Terminate state means we will remove teh shard entry from GSM shard status r.updateGsmShardStatus(instance, shardSfSet.Name, string(databasev1alpha1.ShardOnlineErrorState)) - shardingv1.CancelChunksInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) + if strings.ToUpper(instance.Spec.ReplicationType) != "NATIVE" { + shardingv1.CancelChunksInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) + } return err } oldStateStr := shardingv1.GetGsmShardStatus(instance, shardSfSet.Name) @@ -1490,6 +1676,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar shardingv1.LogMessages("DEBUG", "Starting shard deletion operation.", nil, instance, r.Log) // ================================ Shard Delete Logic =================== + if len(instance.Spec.Shard) > 0 { for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex := instance.Spec.Shard[i] @@ -1518,7 +1705,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar continue } // 3rd step to check if shard is in GSM if not then continue - sparams := shardingv1.BuildShardParams(shardSfSet) + sparams := shardingv1.BuildShardParams(instance, shardSfSet, OraShardSpex) err = shardingv1.CheckShardInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { // If the shard doesn't exist in GSM then just delete the shard statefulset and update GSM shard status @@ -1541,28 +1728,33 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar // 5th Step // Move the chunks before performing any Delete // If you are in this block then it means that shard is ONline and can be deleted - err = shardingv1.MoveChunks(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) - if err != nil { - r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.ChunkMoveError)) - title = "Chunk Movement Failure" - message = "Error occurred during chunk movement in shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " deletion." - r.sendMessage(instance, title, message) - continue - } - // 6th Step - // Check if Chunks has moved before performing actual delete - // This is a loop and will check unless there is a error or chunks has moved - // Validate if the chunks has moved before performing shard deletion - for { - err = shardingv1.VerifyChunks(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) - if err == nil { - break - } else { - msg = "Sleeping for 120 seconds and will check status again of chunks movement in gsm for shard: " + shardingv1.GetFmtStr(OraShardSpex.Name) - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - time.Sleep(120 * time.Second) + if strings.ToUpper(instance.Spec.ReplicationType) != "NATIVE" { + if len(instance.Spec.ReplicationType) == 0 { + err = shardingv1.MoveChunks(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) + if err != nil { + r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.ChunkMoveError)) + title = "Chunk Movement Failure" + message = "Error occurred during chunk movement in shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " deletion." + r.sendMessage(instance, title, message) + continue + } + // 6th Step + // Check if Chunks has moved before performing actual delete + // This is a loop and will check unless there is a error or chunks has moved + // Validate if the chunks has moved before performing shard deletion + for { + err = shardingv1.VerifyChunks(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) + if err == nil { + break + } else { + msg = "Sleeping for 120 seconds and will check status again of chunks movement in gsm for shard: " + shardingv1.GetFmtStr(OraShardSpex.Name) + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + time.Sleep(120 * time.Second) + } + } } } + // 7th Step remove the shards from the GSM // This steps will delete the shard entry from the GSM // It will delete CDB from catalog @@ -1574,6 +1766,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar r.updateShardStatus(instance, int(i), string(databasev1alpha1.ShardRemoveError)) continue } + // 8th Step // Delete the Statefulset as all the chunks has moved and Shard can be phyiscally deleted r.delShard(instance, shardSfSet.Name, shardSfSet, shardPod, int(i)) @@ -1645,7 +1838,7 @@ func (r *ShardingDatabaseReconciler) delShard(instance *databasev1alpha1.Shardin } } -//======== GSM Invited Node ========== +// ======== GSM Invited Node ========== // Remove and add GSM invited node func (r *ShardingDatabaseReconciler) gsmInvitedNodeOp(instance *databasev1alpha1.ShardingDatabase, objName string, ) { From d69eefdf02d33f6aba49a3b1e3dde9529cdda0ce Mon Sep 17 00:00:00 2001 From: psaini Date: Fri, 23 Feb 2024 18:41:59 +0000 Subject: [PATCH 027/414] Added fix --- .../v1alpha1/zz_generated.deepcopy.go | 109 +++- .../database.oracle.com_dataguardbrokers.yaml | 4 + ...database.oracle.com_shardingdatabases.yaml | 262 +++++++- ...se.oracle.com_singleinstancedatabases.yaml | 35 +- config/manager/kustomization.yaml | 4 +- config/rbac/role.yaml | 23 +- go.mod | 68 +- go.sum | 198 ++++-- oracle-database-operator.yaml | 604 +++++++++++++++++- 9 files changed, 1175 insertions(+), 132 deletions(-) diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 752d48b9..e5085e7f 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1259,6 +1259,66 @@ func (in *EnvironmentVariable) DeepCopy() *EnvironmentVariable { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmServiceSpec) DeepCopyInto(out *GsmServiceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmServiceSpec. +func (in *GsmServiceSpec) DeepCopy() *GsmServiceSpec { + if in == nil { + return nil + } + out := new(GsmServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmShardDetails) DeepCopyInto(out *GsmShardDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmShardDetails. +func (in *GsmShardDetails) DeepCopy() *GsmShardDetails { + if in == nil { + return nil + } + out := new(GsmShardDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmShardGroupSpec) DeepCopyInto(out *GsmShardGroupSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmShardGroupSpec. +func (in *GsmShardGroupSpec) DeepCopy() *GsmShardGroupSpec { + if in == nil { + return nil + } + out := new(GsmShardGroupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmShardSpaceSpec) DeepCopyInto(out *GsmShardSpaceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmShardSpaceSpec. +func (in *GsmShardSpaceSpec) DeepCopy() *GsmShardSpaceSpec { + if in == nil { + return nil + } + out := new(GsmShardSpaceSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GsmSpec) DeepCopyInto(out *GsmSpec) { *out = *in @@ -2041,6 +2101,21 @@ func (in *PrivateEndpointSpec) DeepCopy() *PrivateEndpointSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretDetails) DeepCopyInto(out *SecretDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretDetails. +func (in *SecretDetails) DeepCopy() *SecretDetails { + if in == nil { + return nil + } + out := new(SecretDetails) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ShardSpec) DeepCopyInto(out *ShardSpec) { *out = *in @@ -2180,6 +2255,31 @@ func (in *ShardingDatabaseSpec) DeepCopyInto(out *ShardingDatabaseSpec) { *out = make([]PortMapping, len(*in)) copy(*out, *in) } + if in.GsmShardSpace != nil { + in, out := &in.GsmShardSpace, &out.GsmShardSpace + *out = make([]GsmShardSpaceSpec, len(*in)) + copy(*out, *in) + } + if in.GsmShardGroup != nil { + in, out := &in.GsmShardGroup, &out.GsmShardGroup + *out = make([]GsmShardGroupSpec, len(*in)) + copy(*out, *in) + } + if in.ShardRegion != nil { + in, out := &in.ShardRegion, &out.ShardRegion + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.GsmService != nil { + in, out := &in.GsmService, &out.GsmService + *out = make([]GsmServiceSpec, len(*in)) + copy(*out, *in) + } + if in.DbSecret != nil { + in, out := &in.DbSecret, &out.DbSecret + *out = new(SecretDetails) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardingDatabaseSpec. @@ -2341,6 +2441,11 @@ func (in *SingleInstanceDatabaseList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SingleInstanceDatabasePersistence) DeepCopyInto(out *SingleInstanceDatabasePersistence) { *out = *in + if in.SetWritePermissions != nil { + in, out := &in.SetWritePermissions, &out.SetWritePermissions + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabasePersistence. @@ -2387,7 +2492,7 @@ func (in *SingleInstanceDatabaseSpec) DeepCopyInto(out *SingleInstanceDatabaseSp } in.AdminPassword.DeepCopyInto(&out.AdminPassword) out.Image = in.Image - out.Persistence = in.Persistence + in.Persistence.DeepCopyInto(&out.Persistence) if in.InitParams != nil { in, out := &in.InitParams, &out.InitParams *out = new(SingleInstanceDatabaseInitParams) @@ -2428,7 +2533,7 @@ func (in *SingleInstanceDatabaseStatus) DeepCopyInto(out *SingleInstanceDatabase } } out.InitParams = in.InitParams - out.Persistence = in.Persistence + in.Persistence.DeepCopyInto(&out.Persistence) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseStatus. diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index b5fc87c4..f19a3e22 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -88,6 +88,10 @@ spec: - MaxPerformance - MaxAvailability type: string + serviceAnnotations: + additionalProperties: + type: string + type: object setAsPrimaryDatabase: type: string standbyDatabaseRefs: diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 47432123..554ed506 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -16,7 +16,18 @@ spec: singular: shardingdatabase scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 schema: openAPIV3Schema: description: ShardingDatabase is the Schema for the shardingdatabases API @@ -36,6 +47,8 @@ spec: spec: description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: + InvitedNodeSubnet: + type: string catalog: items: description: CatalogSpec defines the desired state of CatalogSpec @@ -82,6 +95,28 @@ spec: description: ResourceRequirements describes the compute resource requirements. properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -102,8 +137,8 @@ spec: description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. More info: - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -117,11 +152,41 @@ spec: type: string dbImagePullSecret: type: string + dbSecret: + description: Secret Details + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object gsm: items: description: GsmSpec defines the desired state of GsmSpec properties: + directorName: + type: string envVars: + description: Replicas int32 `json:"replicas,omitempty"` // + Gsm Replicas. If you set OraGsmPvcName then it is set default + to 1. items: description: EnvironmentVariable represents a named variable accessible for containers. @@ -155,13 +220,34 @@ spec: type: object pvcName: type: string - replicas: - format: int32 - type: integer + region: + type: string resources: description: ResourceRequirements describes the compute resource requirements. properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -182,8 +268,8 @@ spec: description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. More info: - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -193,10 +279,109 @@ spec: - name type: object type: array + gsmDevMode: + type: string gsmImage: type: string gsmImagePullSecret: type: string + gsmService: + items: + description: Service Definition + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + description: ShardSpace Specs + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string isClone: type: boolean isDataGuard: @@ -205,14 +390,16 @@ spec: type: boolean isDeleteOraPvc: type: boolean + isDownloadScripts: + type: boolean isExternalSvc: type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer namespace: type: string - nsConfigMap: - type: string - nsSecret: - type: string portMappings: items: description: PortMapping is a specification of port mapping for @@ -233,9 +420,11 @@ spec: - targetPort type: object type: array - scriptsLocation: + readinessCheckPeriod: + type: integer + replicationType: type: string - secret: + scriptsLocation: type: string shard: description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster @@ -244,6 +433,8 @@ spec: description: ShardSpec is a specification of Shards for an application deployment. properties: + deployAs: + type: string envVars: items: description: EnvironmentVariable represents a named variable @@ -286,6 +477,28 @@ spec: description: ResourceRequirements describes the compute resource requirements. properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -306,10 +519,16 @@ spec: description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. More info: - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string storageSizeInGb: format: int32 type: integer @@ -317,6 +536,16 @@ spec: - name type: object type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string stagePvcName: type: string storageClass: @@ -326,7 +555,6 @@ spec: - dbImage - gsm - gsmImage - - secret - shard type: object status: diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index 83f6b3cf..acfa2bea 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -89,10 +89,12 @@ spec: type: boolean charset: type: string - cloneFrom: + createAs: + enum: + - primary + - standby + - clone type: string - createAsStandby: - type: boolean dgBrokerConfigured: type: boolean edition: @@ -100,6 +102,7 @@ spec: - standard - enterprise - express + - free type: string enableTCPS: type: boolean @@ -153,14 +156,18 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean size: type: string storageClass: type: string volumeClaimAnnotation: type: string - volumeName: - type: string type: object primaryDatabaseRef: type: string @@ -184,6 +191,8 @@ spec: type: string tcpsListenerPort: type: integer + tcpsTlsSecret: + type: string required: - image type: object @@ -203,8 +212,6 @@ spec: type: string clientWalletLoc: type: string - cloneFrom: - type: string clusterConnectString: type: string conditions: @@ -282,6 +289,8 @@ spec: x-kubernetes-list-type: map connectString: type: string + createdAs: + type: string datafilesCreated: default: "false" type: string @@ -336,14 +345,18 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean size: type: string storageClass: type: string volumeClaimAnnotation: type: string - volumeName: - type: string type: object prebuiltDB: type: boolean @@ -367,9 +380,13 @@ spec: type: string tcpsPdbConnectString: type: string + tcpsTlsSecret: + default: "" + type: string required: - isTcpsEnabled - persistence + - tcpsTlsSecret type: object type: object served: true diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index b8ec1f58..e7ed68ce 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: container-registry.oracle.com/database/operator - newTag: 0.1.0 + newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database + newTag: sharding-operator diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 1467c250..1fdcf261 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -72,26 +72,12 @@ rules: - "" resources: - events - - nodes - - persistentvolumeclaims - - persistentvolumes - - pods - - pods/exec - - pods/log - - services verbs: - create - - delete - - get - - list - patch - - update - - watch - apiGroups: - "" resources: - - events - - nodes - persistentvolumeclaims - pods - pods/exec @@ -105,6 +91,13 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list - apiGroups: - '''''' resources: @@ -169,9 +162,9 @@ rules: - "" resources: - configmaps + - containers - events - namespaces - - nodes - persistentvolumeclaims - pods - pods/exec diff --git a/go.mod b/go.mod index ddba3387..e298856c 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,18 @@ module github.com/oracle/oracle-database-operator go 1.21 require ( - github.com/go-logr/logr v1.2.4 - github.com/onsi/ginkgo/v2 v2.12.1 - github.com/onsi/gomega v1.28.0 + github.com/go-logr/logr v1.3.0 + github.com/onsi/ginkgo/v2 v2.13.0 + github.com/onsi/gomega v1.29.0 github.com/oracle/oci-go-sdk/v65 v65.49.3 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0 go.uber.org/zap v1.26.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.28.2 - k8s.io/apimachinery v0.28.2 - k8s.io/client-go v0.28.2 + k8s.io/api v0.29.2 + k8s.io/apimachinery v0.29.2 + k8s.io/cli-runtime v0.29.2 + k8s.io/client-go v0.29.2 + k8s.io/kubectl v0.29.2 sigs.k8s.io/controller-runtime v0.16.2 sigs.k8s.io/yaml v1.3.0 ) @@ -21,7 +23,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect @@ -31,7 +33,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -49,33 +51,59 @@ require ( github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.14.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/term v0.11.0 // indirect - golang.org/x/text v0.12.0 + golang.org/x/net v0.19.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 golang.org/x/time v0.3.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/apiextensions-apiserver v0.28.0 // indirect - k8s.io/component-base v0.28.1 // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect - k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + k8s.io/component-base v0.29.2 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) require ( + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/fatih/camelcase v1.0.0 // indirect + github.com/fvbommel/sortorder v1.1.0 // indirect + github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/zapr v1.2.4 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/google/btree v1.0.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/xlab/treeprint v1.2.0 // indirect + go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/tools v0.12.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/tools v0.16.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect ) diff --git a/go.sum b/go.sum index 1b323d94..5f306abd 100644 --- a/go.sum +++ b/go.sum @@ -1,28 +1,51 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= +github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -37,29 +60,55 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -75,25 +124,37 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= -github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= -github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/oracle/oci-go-sdk/v65 v65.49.3 h1:HHv+XMZiBYHtoU8Ac/fURdp9v1vJPPCpIbJAWeadREw= github.com/oracle/oci-go-sdk/v65 v65.49.3/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -103,6 +164,7 @@ github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0 h github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0/go.mod h1:j51242bf6LQwvJ1JPKWApzTnifmCwcQq0i1p29ylWiM= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= @@ -111,8 +173,14 @@ github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+Pymzi github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -127,9 +195,13 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= @@ -143,14 +215,19 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -158,15 +235,21 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -174,40 +257,63 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -221,27 +327,37 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= -k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= +k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= k8s.io/apiextensions-apiserver v0.28.0 h1:CszgmBL8CizEnj4sj7/PtLGey6Na3YgWyGCPONv7E9E= k8s.io/apiextensions-apiserver v0.28.0/go.mod h1:uRdYiwIuu0SyqJKriKmqEN2jThIJPhVmOWETm8ud1VE= -k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= -k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= -k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= -k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= -k8s.io/component-base v0.28.1 h1:LA4AujMlK2mr0tZbQDZkjWbdhTV5bRyEyAFe0TJxlWg= -k8s.io/component-base v0.28.1/go.mod h1:jI11OyhbX21Qtbav7JkhehyBsIRfnO8oEgoAR12ArIU= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= +k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= +k8s.io/cli-runtime v0.29.2 h1:smfsOcT4QujeghsNjECKN3lwyX9AwcFU0nvJ7sFN3ro= +k8s.io/cli-runtime v0.29.2/go.mod h1:KLisYYfoqeNfO+MkTWvpqIyb1wpJmmFJhioA0xd4MW8= +k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= +k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= +k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8= +k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kubectl v0.29.2 h1:uaDYaBhumvkwz0S2XHt36fK0v5IdNgL7HyUniwb2IUo= +k8s.io/kubectl v0.29.2/go.mod h1:BhizuYBGcKaHWyq+G7txGw2fXg576QbPrrnQdQDZgqI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.16.2 h1:mwXAVuEk3EQf478PQwQ48zGOXvW27UJc8NHktQVuIPU= sigs.k8s.io/controller-runtime v0.16.2/go.mod h1:vpMu3LpI5sYWtujJOa2uPK61nB5rbwlN7BAB8aSLvGU= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 70f026c1..fb7e4676 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -572,11 +572,59 @@ spec: - connectionStrings type: object type: array + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map lifecycleState: description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string timeCreated: type: string + walletExpiringDate: + type: string type: object type: object served: true @@ -853,6 +901,195 @@ status: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver + properties: + database: + description: DatabaseObserverDatabase defines the database details used for DatabaseObserver + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver + properties: + configuration: + properties: + configmap: + description: ConfigMapDetails defines the configmap name + properties: + configmapName: + type: string + key: + type: string + type: object + type: object + image: + type: string + service: + description: DatabaseObserverService defines the exporter service component of DatabaseObserver + properties: + port: + format: int32 + type: integer + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + description: PrometheusConfig defines the generated resources for Prometheus + properties: + labels: + additionalProperties: + type: string + type: object + port: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver + properties: + conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + required: + - conditions + - exporterConfig + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.6.1 @@ -1753,7 +1990,18 @@ spec: singular: shardingdatabase scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 schema: openAPIV3Schema: description: ShardingDatabase is the Schema for the shardingdatabases API @@ -1769,6 +2017,8 @@ spec: spec: description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: + InvitedNodeSubnet: + type: string catalog: items: description: CatalogSpec defines the desired state of CatalogSpec @@ -1812,6 +2062,21 @@ spec: resources: description: ResourceRequirements describes the compute resource requirements. properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1828,7 +2093,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -1842,11 +2107,39 @@ spec: type: string dbImagePullSecret: type: string + dbSecret: + description: Secret Details + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object gsm: items: description: GsmSpec defines the desired state of GsmSpec properties: + directorName: + type: string envVars: + description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. items: description: EnvironmentVariable represents a named variable accessible for containers. properties: @@ -1878,12 +2171,26 @@ spec: type: object pvcName: type: string - replicas: - format: int32 - type: integer + region: + type: string resources: description: ResourceRequirements describes the compute resource requirements. properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1900,7 +2207,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -1910,10 +2217,109 @@ spec: - name type: object type: array + gsmDevMode: + type: string gsmImage: type: string gsmImagePullSecret: type: string + gsmService: + items: + description: Service Definition + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + description: ShardSpace Specs + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string isClone: type: boolean isDataGuard: @@ -1922,14 +2328,16 @@ spec: type: boolean isDeleteOraPvc: type: boolean + isDownloadScripts: + type: boolean isExternalSvc: type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer namespace: type: string - nsConfigMap: - type: string - nsSecret: - type: string portMappings: items: description: PortMapping is a specification of port mapping for an application deployment. @@ -1949,15 +2357,19 @@ spec: - targetPort type: object type: array - scriptsLocation: + readinessCheckPeriod: + type: integer + replicationType: type: string - secret: + scriptsLocation: type: string shard: description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' items: description: ShardSpec is a specification of Shards for an application deployment. properties: + deployAs: + type: string envVars: items: description: EnvironmentVariable represents a named variable accessible for containers. @@ -1997,6 +2409,21 @@ spec: resources: description: ResourceRequirements describes the compute resource requirements. properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -2013,9 +2440,15 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string storageSizeInGb: format: int32 type: integer @@ -2023,6 +2456,16 @@ spec: - name type: object type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string stagePvcName: type: string storageClass: @@ -2032,7 +2475,6 @@ spec: - dbImage - gsm - gsmImage - - secret - shard type: object status: @@ -2207,10 +2649,12 @@ spec: type: boolean charset: type: string - cloneFrom: + createAs: + enum: + - primary + - standby + - clone type: string - createAsStandby: - type: boolean dgBrokerConfigured: type: boolean edition: @@ -2270,14 +2714,18 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean size: type: string storageClass: type: string volumeClaimAnnotation: type: string - volumeName: - type: string type: object primaryDatabaseRef: type: string @@ -2300,6 +2748,8 @@ spec: type: string tcpsListenerPort: type: integer + tcpsTlsSecret: + type: string required: - image type: object @@ -2318,8 +2768,6 @@ spec: type: string clientWalletLoc: type: string - cloneFrom: - type: string clusterConnectString: type: string conditions: @@ -2370,6 +2818,8 @@ spec: x-kubernetes-list-type: map connectString: type: string + createdAs: + type: string datafilesCreated: default: "false" type: string @@ -2423,14 +2873,18 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean size: type: string storageClass: type: string volumeClaimAnnotation: type: string - volumeName: - type: string type: object prebuiltDB: type: boolean @@ -2454,9 +2908,13 @@ spec: type: string tcpsPdbConnectString: type: string + tcpsTlsSecret: + default: "" + type: string required: - isTcpsEnabled - persistence + - tcpsTlsSecret type: object type: object served: true @@ -2525,6 +2983,23 @@ metadata: creationTimestamp: null name: oracle-database-operator-manager-role rules: +- apiGroups: + - "" + resources: + - configmaps + - deployments + - events + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -2574,7 +3049,12 @@ rules: - "" resources: - events - - nodes + verbs: + - create + - patch +- apiGroups: + - "" + resources: - persistentvolumeclaims - pods - pods/exec @@ -2588,6 +3068,13 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list - apiGroups: - '''''' resources: @@ -2600,6 +3087,21 @@ rules: - patch - update - watch +- apiGroups: + - apps + resources: + - configmaps + - deployments + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - apps resources: @@ -2637,9 +3139,9 @@ rules: - "" resources: - configmaps + - containers - events - namespaces - - nodes - persistentvolumeclaims - pods - pods/exec @@ -2947,6 +3449,53 @@ rules: - get - patch - update +- apiGroups: + - monitoring.coreos.com + resources: + - prometheusrules + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/finalizers + verbs: + - update +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get + - patch + - update +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -3435,7 +3984,10 @@ spec: - --enable-leader-election command: - /manager - image: container-registry.oracle.com/database/operator:1.0.0 + env: + - name: WATCH_NAMESPACE + value: "" + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator imagePullPolicy: Always name: manager ports: From 5d57f5e06a4b457cbed41788ed1a4b2e2ebd0cf0 Mon Sep 17 00:00:00 2001 From: psaini Date: Fri, 23 Feb 2024 18:43:48 +0000 Subject: [PATCH 028/414] Added fix --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 675e2aa3..909a6e59 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ build: generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go -GOLANG_VERSION ?= 1.22.0 +GOLANG_VERSION ?= 1.21.0 ## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. ## Otherwise, use golang image from docker hub as the builder. ifeq ($(BUILD_INTERNAL), true) From 642c5446429409048fdc2ad59ba697250fafebb2 Mon Sep 17 00:00:00 2001 From: psaini Date: Fri, 23 Feb 2024 19:28:48 +0000 Subject: [PATCH 029/414] Changing ResourceRequirements to VolumeResourceRequirement --- controllers/database/oraclerestdataservice_controller.go | 2 +- controllers/database/singleinstancedatabase_controller.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/database/oraclerestdataservice_controller.go b/controllers/database/oraclerestdataservice_controller.go index 1bb40ac7..783ae70c 100644 --- a/controllers/database/oraclerestdataservice_controller.go +++ b/controllers/database/oraclerestdataservice_controller.go @@ -786,7 +786,7 @@ func (r *OracleRestDataServiceReconciler) instantiatePVCSpec(m *dbapi.OracleRest accessMode = append(accessMode, corev1.PersistentVolumeAccessMode(m.Spec.Persistence.AccessMode)) return accessMode }(), - Resources: corev1.ResourceRequirements{ + Resources: corev1.VolumeResourceRequirements{ Requests: map[corev1.ResourceName]resource.Quantity{ // Requests describes the minimum amount of compute resources required "storage": resource.MustParse(m.Spec.Persistence.Size), diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 946a1c4a..b6c2049b 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -1223,7 +1223,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePVCSpec(m *dbapi.SingleIns accessMode = append(accessMode, corev1.PersistentVolumeAccessMode(m.Spec.Persistence.AccessMode)) return accessMode }(), - Resources: corev1.ResourceRequirements{ + Resources: corev1.VolumeResourceRequirements{ Requests: map[corev1.ResourceName]resource.Quantity{ // Requests describes the minimum amount of compute resources required "storage": resource.MustParse(m.Spec.Persistence.Size), @@ -1336,7 +1336,7 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePVCforCustomScriptsVol accessMode = append(accessMode, corev1.PersistentVolumeAccessMode(AccessMode)) return accessMode }(), - Resources: corev1.ResourceRequirements{ + Resources: corev1.VolumeResourceRequirements{ Requests: map[corev1.ResourceName]resource.Quantity{ // Requests describes the minimum amount of compute resources required "storage": *resource.NewQuantity(int64(Storage), resource.BinarySI), From 08a8f93626c7057e5732e3cc729251775536dfc1 Mon Sep 17 00:00:00 2001 From: psaini Date: Fri, 23 Feb 2024 20:41:26 +0000 Subject: [PATCH 030/414] Replace ClusterRoleBinding to RoleBinding --- config/rbac/role_binding.yaml | 2 +- oracle-database-operator.yaml | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 1abe0ec7..f2ccb566 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding +kind: RoleBinding metadata: name: oracle-database-operator-manager-rolebinding roleRef: diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index fb7e4676..f097732e 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -2978,10 +2978,11 @@ rules: - patch --- apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: Role metadata: creationTimestamp: null name: oracle-database-operator-manager-role + namespace: oracle-database-operator-system rules: - apiGroups: - "" @@ -3540,12 +3541,27 @@ subjects: namespace: oracle-database-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding +kind: RoleBinding metadata: name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: oracle-database-operator-system roleRef: apiGroup: rbac.authorization.k8s.io - kind: ClusterRole + kind: Role + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 + namespace: shns +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role name: oracle-database-operator-manager-role subjects: - kind: ServiceAccount @@ -3986,7 +4002,7 @@ spec: - /manager env: - name: WATCH_NAMESPACE - value: "" + value: "oracle-database-operator-system,shns" image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator imagePullPolicy: Always name: manager From 993994a90f23845ade4e78b4f1bb6450dd0fe1d4 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Sat, 24 Feb 2024 03:21:00 +0000 Subject: [PATCH 031/414] Added fix --- oracle-database-operator.yaml | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index f097732e..043fd215 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -2978,11 +2978,10 @@ rules: - patch --- apiVersion: rbac.authorization.k8s.io/v1 -kind: Role +kind: ClusterRole metadata: creationTimestamp: null name: oracle-database-operator-manager-role - namespace: oracle-database-operator-system rules: - apiGroups: - "" @@ -3547,21 +3546,7 @@ metadata: namespace: oracle-database-operator-system roleRef: apiGroup: rbac.authorization.k8s.io - kind: Role - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: shns -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role + kind: ClusterRole name: oracle-database-operator-manager-role subjects: - kind: ServiceAccount @@ -4002,7 +3987,7 @@ spec: - /manager env: - name: WATCH_NAMESPACE - value: "oracle-database-operator-system,shns" + value: "" image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator imagePullPolicy: Always name: manager From 5f3f7bedcd1d542e1008659ccc53312378ef9634 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Tue, 27 Feb 2024 13:44:55 +0000 Subject: [PATCH 032/414] GO version 1.21.7 --- .gitlab-ci.yml | 3 ++- Makefile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 170c87e3..b27445a0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,8 @@ build-operator: OP_YAML: oracle-database-operator.yaml BUILD_INTERNAL: "true" script: - - go version + - export GOROOT=$(go1.21.7 env GOROOT) + - export PATH="${GOROOT}/bin:${PATH}" - make docker-build IMG="$IMAGE" - docker push "$IMAGE" - docker rmi "$IMAGE" && docker system prune -f diff --git a/Makefile b/Makefile index 909a6e59..0dbe393b 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ build: generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go -GOLANG_VERSION ?= 1.21.0 +GOLANG_VERSION ?= 1.21.7 ## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. ## Otherwise, use golang image from docker hub as the builder. ifeq ($(BUILD_INTERNAL), true) From 02b198413b74008fedfcf303499d5b8ad7c08d0b Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 27 Feb 2024 21:28:44 +0000 Subject: [PATCH 033/414] Added fixes to handle cluster namescope --- .../database/shardingdatabase_controller.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 06236c5f..88823afe 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -194,14 +194,14 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req r.getOnsConfigProvider(instance, idx) // =============================== Checking Namespace ============== - if instance.Spec.Namespace != "" { - err = shardingv1.AddNamespace(instance, r.Client, r.Log) - if err != nil { - //r.setCrdLifeCycleState(instance, &result, &err, stateType) - result = resultNq - return result, err - } - } else { + if instance.Spec.Namespace == "" { + ///err = shardingv1.AddNamespace(instance, r.Client, r.Log) + //if err != nil { + // //r.setCrdLifeCycleState(instance, &result, &err, stateType) + // result = resultNq + // return result, err + // } + // } else { instance.Spec.Namespace = "default" } From f666a1832c21ac932268f093c850ea372514950a Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 27 Feb 2024 21:33:27 +0000 Subject: [PATCH 034/414] Added fixes for proxy role --- config/rbac/auth_proxy_role_binding.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml index 55665e32..6a702753 100644 --- a/config/rbac/auth_proxy_role_binding.yaml +++ b/config/rbac/auth_proxy_role_binding.yaml @@ -5,11 +5,11 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: oracle-database-operator-proxy-rolebinding + name: proxy-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: proxy-role + name: oracle-database-operator-proxy-role subjects: - kind: ServiceAccount name: default From 121dd2cbabea96ae4bfe027f65c245b69c4959be Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 27 Feb 2024 21:58:12 +0000 Subject: [PATCH 035/414] Generated new operator file --- oracle-database-operator.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 043fd215..ec713d8a 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -3556,11 +3556,11 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: oracle-database-operator-oracle-database-operator-proxy-rolebinding + name: oracle-database-operator-proxy-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: proxy-role + name: oracle-database-operator-oracle-database-operator-proxy-role subjects: - kind: ServiceAccount name: default From aad4f43b0a1d2ddd4a0768cef200c94b3643628d Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 27 Feb 2024 23:36:43 +0000 Subject: [PATCH 036/414] Added fix for GSM2 region --- commons/sharding/scommon.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 7ed51fde..d5d438b1 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -710,6 +710,10 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { region_arr = append(region_arr, instance.Spec.Shard[i].ShardRegion) } + for i := 0; i < len(instance.Spec.Gsm); i++ { + region_arr = append(region_arr, instance.Spec.Gsm[i].Region) + } + slices.Sort(region_arr) region_arr = slices.Compact(region_arr) //[a b c d] for i := 0; i < len(region_arr); i++ { From a292c165cac923bb270ac1530fa3d34dd201705d Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Wed, 28 Feb 2024 13:39:45 +0000 Subject: [PATCH 037/414] Node watch and list priv for operator manager --- controllers/database/dataguardbroker_controller.go | 1 + controllers/database/oraclerestdataservice_controller.go | 1 + controllers/database/singleinstancedatabase_controller.go | 1 + 3 files changed, 3 insertions(+) diff --git a/controllers/database/dataguardbroker_controller.go b/controllers/database/dataguardbroker_controller.go index 2a8fe163..27539082 100644 --- a/controllers/database/dataguardbroker_controller.go +++ b/controllers/database/dataguardbroker_controller.go @@ -78,6 +78,7 @@ const dataguardBrokerFinalizer = "database.oracle.com/dataguardbrokerfinalizer" //+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers/finalizers,verbs=update //+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch //+kubebuilder:rbac:groups="",resources=events,verbs=create;patch +//+kubebuilder:rbac:groups="",resources=nodes,verbs=watch;list // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. diff --git a/controllers/database/oraclerestdataservice_controller.go b/controllers/database/oraclerestdataservice_controller.go index 783ae70c..48469eab 100644 --- a/controllers/database/oraclerestdataservice_controller.go +++ b/controllers/database/oraclerestdataservice_controller.go @@ -82,6 +82,7 @@ type OracleRestDataServiceReconciler struct { //+kubebuilder:rbac:groups=database.oracle.com,resources=oraclerestdataservices/finalizers,verbs=update //+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch //+kubebuilder:rbac:groups="",resources=events,verbs=create;patch +//+kubebuilder:rbac:groups="",resources=nodes,verbs=watch;list // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index b6c2049b..42fb66ff 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -95,6 +95,7 @@ var oemExpressUrl string //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/finalizers,verbs=update //+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch //+kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get;list +//+kubebuilder:rbac:groups="",resources=nodes,verbs=watch;list //+kubebuilder:rbac:groups="",resources=events,verbs=create;patch //+kubebuilder:rbac:groups=storage.k8s.io,resources=storageclasses,verbs=get;list;watch From 3f2f3cd861867ffc83ed94ee1d27f264b1a78457 Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Wed, 28 Feb 2024 14:54:18 +0000 Subject: [PATCH 038/414] Documentation Bugs in OraOperator --- config/samples/sidb/singleinstancedatabase.yaml | 3 ++- docs/sidb/README.md | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index a6f67671..f1059494 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -56,7 +56,8 @@ spec: ## The following specified TLS certs will be used instead of self-signed tcpsTlsSecret: - ## TCPS Certificate Renewal Interval: The time after which TCPS certificate will be renewed if TCPS connections are enabled. + ## TCPS Certificate Renewal Interval: (Valid for Self-Signed Certificates) + ## The time after which TCPS certificate will be renewed if TCPS connections are enabled. ## tcpsCertRenewInterval can be in hours(h), minutes(m) and seconds(s); e.g. 4380h, 8760h etc. ## Maximum value is 8760h (1 year), Minimum value is 24h; Default value is 8760h (1 year) ## If this field is commented out/removed from the yaml, it will disable the auto-renewal feature for TCPS certificate diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 089f7fa2..00e1b9c6 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -580,7 +580,7 @@ true kubectl create secret tls my-tls-secret --cert=path/to/cert/tls.crt --key=path/to/key/tls.key ``` - `tls.crt` is a certificate chain in the order of client, followed by intermediate and then root certificate and `tls.key` is client key. -- Specify the secret created above (`my-tls-secret`) as the value for the attribute `tcpsTlsSecret` in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. +- Specify the secret created above (`my-tls-secret`) as the value for the attribute `tcpsTlsSecret` in the [config/samples/sidb/singleinstancedatabase_tcps.yaml](../../config/samples/sidb/singleinstancedatabase_tcps.yaml) file, and apply it. **Connecting to the Database using TCPS** - Download the wallet from the Persistent Volume (PV) attached with the database pod. The location of the wallet inside the pod is as `/opt/oracle/oradata/clientWallet/$ORACLE_SID`. Let us assume the `ORACLE_SID` is `ORCL1`, and singleinstance database resource name is `sidb-sample` for the upcoming example command. You can copy the wallet to the destination directory by the following command: @@ -820,7 +820,7 @@ $ kubectl delete singleinstancedatabase stdby-1 Custom scripts (sql and/or shell scripts) can be executed after the initial database setup and/or after each startup of the database. SQL scripts will be executed as sysdba, shell scripts will be executed as the current user. To ensure proper order it is recommended to prefix your scripts with a number. For example `01_users.sql`, `02_permissions.sql`, etc. Place all such scripts in setup and startup folders created in a persistent volume to execute them post setup and post startup respectively. -Create a persistent volume using [static provisioning](#static-persistence) and then specify the name of this volume with the `<.spec.persistence.scriptsVolumeName>` field which corresponds to the `scriptVolumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. +Create a persistent volume using [static provisioning](#static-persistence) and then specify the name of this volume with the `<.spec.persistence.scriptsVolumeName>` field which corresponds to the `scriptsVolumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. ## OracleRestDataService Resource From 49052bb2a07e5addde1a538c966ef5c0a3439183 Mon Sep 17 00:00:00 2001 From: Ishaan Date: Thu, 29 Feb 2024 15:08:30 +0530 Subject: [PATCH 039/414] fixing dg_webhook --- apis/database/v1alpha1/dataguardbroker_webhook.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apis/database/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go index bbafdc3d..a9d59286 100644 --- a/apis/database/v1alpha1/dataguardbroker_webhook.go +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -98,6 +98,7 @@ var _ webhook.Validator = &DataguardBroker{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *DataguardBroker) ValidateCreate() (admission.Warnings, error) { + dataguardbrokerlog.Info("validate create", "name", r.Name) var allErrs field.ErrorList namespaces := dbcommons.GetWatchNamespaces() @@ -109,6 +110,10 @@ func (r *DataguardBroker) ValidateCreate() (admission.Warnings, error) { "Oracle database operator doesn't watch over this namespace")) } + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "Dataguard"}, r.Name, allErrs) From 301350d219627c1d1e17103fcdcc0f45d11faecf Mon Sep 17 00:00:00 2001 From: Matteo Malvezzi Date: Mon, 4 Mar 2024 08:36:41 +0000 Subject: [PATCH 040/414] Pdb cdb multinamespace --- apis/database/v1alpha1/cdb_types.go | 4 +- apis/database/v1alpha1/cdb_webhook.go | 26 +- apis/database/v1alpha1/pdb_types.go | 19 + apis/database/v1alpha1/pdb_webhook.go | 43 +- .../v1alpha1/zz_generated.deepcopy.go | 34 ++ .../crd/bases/database.oracle.com_CDBS.yaml | 270 +++++++++++++ .../crd/bases/database.oracle.com_PDBS.yaml | 378 ++++++++++++++++++ .../crd/bases/database.oracle.com_pdbs.yaml | 38 ++ controllers/database/cdb_controller.go | 115 ++++-- controllers/database/pdb_controller.go | 81 ++-- docs/multitenant/NamespaceSeg.md | 14 + docs/multitenant/README.md | 9 + docs/multitenant/images/K8S_NAMESPACE_SEG.png | Bin 0 -> 269014 bytes docs/multitenant/usecase01/README.md | 5 + docs/multitenant/usecase01/cdb_create.yaml | 44 ++ docs/multitenant/usecase01/cdb_secret.yaml | 17 + docs/multitenant/usecase01/makefile | 51 ++- .../usecase01/oracle-database-operator.yaml | 1 + docs/multitenant/usecase01/pdb_close.yaml | 44 ++ docs/multitenant/usecase01/pdb_create.yaml | 46 +++ docs/multitenant/usecase01/pdb_map.yaml | 44 ++ docs/multitenant/usecase01/pdb_open.yaml | 43 ++ docs/multitenant/usecase01/pdb_secret.yaml | 16 + docs/multitenant/usecase01/tde_secret.yaml | 17 + docs/multitenant/usecase02/README.md | 35 +- docs/multitenant/usecase02/pdb_clone.yaml | 13 +- docs/multitenant/usecase02/pdb_plug.yaml | 12 +- docs/multitenant/usecase02/pdb_unplug.yaml | 12 +- docs/multitenant/usecase03/Dockerfile | 53 +++ .../usecase03/NamespaceSegregation.png | Bin 0 -> 270813 bytes docs/multitenant/usecase03/README.md | 266 ++++++++++++ docs/multitenant/usecase03/cdb_create.yaml | 44 ++ .../usecase03/cdb_creation_log.txt | 336 ++++++++++++++++ docs/multitenant/usecase03/cdb_secret.yaml | 17 + docs/multitenant/usecase03/gentlscert.sh | 23 ++ docs/multitenant/usecase03/makefile | 286 +++++++++++++ .../usecase03/operator_creation_log.txt | 27 ++ docs/multitenant/usecase03/pdb_create.yaml | 46 +++ .../usecase03/pdb_creation_log.txt | 6 + docs/multitenant/usecase03/pdb_secret.yaml | 16 + oracle-database-operator.yaml | 37 ++ 41 files changed, 2465 insertions(+), 123 deletions(-) create mode 100644 config/crd/bases/database.oracle.com_CDBS.yaml create mode 100644 config/crd/bases/database.oracle.com_PDBS.yaml create mode 100644 docs/multitenant/NamespaceSeg.md create mode 100644 docs/multitenant/images/K8S_NAMESPACE_SEG.png create mode 100644 docs/multitenant/usecase01/cdb_create.yaml create mode 100644 docs/multitenant/usecase01/cdb_secret.yaml create mode 120000 docs/multitenant/usecase01/oracle-database-operator.yaml create mode 100644 docs/multitenant/usecase01/pdb_close.yaml create mode 100644 docs/multitenant/usecase01/pdb_create.yaml create mode 100644 docs/multitenant/usecase01/pdb_map.yaml create mode 100644 docs/multitenant/usecase01/pdb_open.yaml create mode 100644 docs/multitenant/usecase01/pdb_secret.yaml create mode 100644 docs/multitenant/usecase01/tde_secret.yaml create mode 100644 docs/multitenant/usecase03/Dockerfile create mode 100644 docs/multitenant/usecase03/NamespaceSegregation.png create mode 100644 docs/multitenant/usecase03/README.md create mode 100644 docs/multitenant/usecase03/cdb_create.yaml create mode 100644 docs/multitenant/usecase03/cdb_creation_log.txt create mode 100644 docs/multitenant/usecase03/cdb_secret.yaml create mode 100644 docs/multitenant/usecase03/gentlscert.sh create mode 100644 docs/multitenant/usecase03/makefile create mode 100644 docs/multitenant/usecase03/operator_creation_log.txt create mode 100644 docs/multitenant/usecase03/pdb_create.yaml create mode 100644 docs/multitenant/usecase03/pdb_creation_log.txt create mode 100644 docs/multitenant/usecase03/pdb_secret.yaml diff --git a/apis/database/v1alpha1/cdb_types.go b/apis/database/v1alpha1/cdb_types.go index b5f39707..84babaee 100644 --- a/apis/database/v1alpha1/cdb_types.go +++ b/apis/database/v1alpha1/cdb_types.go @@ -52,7 +52,6 @@ type CDBSpec struct { // Name of the CDB Service ServiceName string `json:"serviceName,omitempty"` - // Password for the CDB System Administrator SysAdminPwd CDBSysAdminPassword `json:"sysAdminPwd,omitempty"` // User in the root container with sysdba priviledges to manage PDB lifecycle @@ -86,7 +85,7 @@ type CDBSpec struct { DBPort int `json:"dbPort,omitempty"` // Node Selector for running the Pod NodeSelector map[string]string `json:"nodeSelector,omitempty"` - DBTnsurl string `json:"dbTnsurl,omitempty"` + DBTnsurl string `json:"dbTnsurl,omitempty"` } // CDBSecret defines the secretName @@ -155,6 +154,7 @@ type CDBStatus struct { // +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="Replicas",type="integer",description="Replicas" // +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the CDB Resource" // +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" +// +kubebuilder:resource:path=CDBS,scope=Namespaced // CDB is the Schema for the cdbs API type CDB struct { diff --git a/apis/database/v1alpha1/cdb_webhook.go b/apis/database/v1alpha1/cdb_webhook.go index 7e48c82c..345b6f75 100644 --- a/apis/database/v1alpha1/cdb_webhook.go +++ b/apis/database/v1alpha1/cdb_webhook.go @@ -93,32 +93,32 @@ func (r *CDB) ValidateCreate() (admission.Warnings, error) { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("serviceName"), "Please specify CDB Service name")) } - - if reflect.ValueOf(r.Spec.CDBTlsKey).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbTlsKey"), "Please specify CDB Tls key(secret)")) - } - if reflect.ValueOf(r.Spec.CDBTlsCrt).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbTlsCrt"), "Please specify CDB Tls Certificate(secret)")) - } + if reflect.ValueOf(r.Spec.CDBTlsKey).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("cdbTlsKey"), "Please specify CDB Tls key(secret)")) + } + + if reflect.ValueOf(r.Spec.CDBTlsCrt).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("cdbTlsCrt"), "Please specify CDB Tls Certificate(secret)")) + } /*if r.Spec.SCANName == "" { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("scanName"), "Please specify SCAN Name for CDB")) }*/ - if ((r.Spec.DBServer == "" && r.Spec.DBTnsurl == "") || (r.Spec.DBServer != "" && r.Spec.DBTnsurl != "")) { + if (r.Spec.DBServer == "" && r.Spec.DBTnsurl == "") || (r.Spec.DBServer != "" && r.Spec.DBTnsurl != "") { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("dbServer"), "Please specify Database Server Name/IP Address or tnsalias string")) } - if r.Spec.DBTnsurl != "" && ( r.Spec.DBServer != "" || r.Spec.DBPort != 0 || r.Spec.ServiceName != "" ) { + if r.Spec.DBTnsurl != "" && (r.Spec.DBServer != "" || r.Spec.DBPort != 0 || r.Spec.ServiceName != "") { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("dbServer"), "DBtnsurl is orthogonal to (DBServer,DBport,Services)")) - } - + } + if r.Spec.DBPort == 0 && r.Spec.DBServer != "" { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("dbPort"), "Please specify DB Server Port")) diff --git a/apis/database/v1alpha1/pdb_types.go b/apis/database/v1alpha1/pdb_types.go index a73d3dd8..ed3cc3b3 100644 --- a/apis/database/v1alpha1/pdb_types.go +++ b/apis/database/v1alpha1/pdb_types.go @@ -51,6 +51,8 @@ type PDBSpec struct { PDBTlsCrt PDBTLSCRT `json:"pdbTlsCrt,omitempty"` PDBTlsCat PDBTLSCAT `json:"pdbTlsCat,omitempty"` + // CDB Namespace + CDBNamespace string `json:"cdbNamespace,omitempty"` // Name of the CDB Custom Resource that runs the ORDS container CDBResName string `json:"cdbResName,omitempty"` // Name of the CDB @@ -63,6 +65,10 @@ type PDBSpec struct { AdminName PDBAdminName `json:"adminName,omitempty"` // The administrator password for the new PDB. This property is required when the Action property is Create. AdminPwd PDBAdminPassword `json:"adminPwd,omitempty"` + // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + WebServerUsr WebServerUserPDB `json:"webServerUser,omitempty"` + // Password for the Web ServerPDB User + WebServerPwd WebServerPasswordPDB `json:"webServerPwd,omitempty"` // Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. FileNameConversions string `json:"fileNameConversions,omitempty"` // This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. @@ -130,6 +136,17 @@ type TDESecret struct { Secret PDBSecret `json:"secret"` } +// WebServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle + +type WebServerUserPDB struct { + Secret PDBSecret `json:"secret"` +} + +// WebServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle +type WebServerPasswordPDB struct { + Secret PDBSecret `json:"secret"` +} + // PDBSecret defines the secretName type PDBSecret struct { SecretName string `json:"secretName"` @@ -180,6 +197,8 @@ type PDBStatus struct { // +kubebuilder:printcolumn:JSONPath=".status.totalSize",name="PDB Size",type="string",description="Total Size of the PDB" // +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the PDB Resource" // +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" +// +kubebuilder:resource:path=PDBS,scope=Namespaced + // PDB is the Schema for the pdbs API type PDB struct { metav1.TypeMeta `json:",inline"` diff --git a/apis/database/v1alpha1/pdb_webhook.go b/apis/database/v1alpha1/pdb_webhook.go index 3051971e..1577198e 100644 --- a/apis/database/v1alpha1/pdb_webhook.go +++ b/apis/database/v1alpha1/pdb_webhook.go @@ -78,8 +78,8 @@ func (r *PDB) Default() { if action == "DELETE" { if r.Spec.DropAction == "" { - r.Spec.DropAction = "KEEP" - pdblog.Info(" - dropAction : KEEP") + r.Spec.DropAction = "INCLUDING" + pdblog.Info(" - dropAction : INCLUDING") } } else if action != "MODIFY" && action != "STATUS" { if r.Spec.ReuseTempFile == nil { @@ -148,21 +148,21 @@ func (r *PDB) validateAction(allErrs *field.ErrorList) { pdblog.Info("Valdiating PDB Resource Action : " + action) - if reflect.ValueOf(r.Spec.PDBTlsKey).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbTlsKey"), "Please specify PDB Tls Key(secret)")) - } - - if reflect.ValueOf(r.Spec.PDBTlsCrt).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbTlsCrt"), "Please specify PDB Tls Certificate(secret)")) - } - - if reflect.ValueOf(r.Spec.PDBTlsCat).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbTlsCat"), "Please specify PDB Tls Certificate Authority(secret)")) - } - + if reflect.ValueOf(r.Spec.PDBTlsKey).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("pdbTlsKey"), "Please specify PDB Tls Key(secret)")) + } + + if reflect.ValueOf(r.Spec.PDBTlsCrt).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("pdbTlsCrt"), "Please specify PDB Tls Certificate(secret)")) + } + + if reflect.ValueOf(r.Spec.PDBTlsCat).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("pdbTlsCat"), "Please specify PDB Tls Certificate Authority(secret)")) + } + switch action { case "CREATE": if reflect.ValueOf(r.Spec.AdminName).IsZero() { @@ -173,6 +173,15 @@ func (r *PDB) validateAction(allErrs *field.ErrorList) { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("adminPwd"), "Please specify PDB System Administrator Password")) } + if reflect.ValueOf(r.Spec.WebServerUsr).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("WebServerUser"), "Please specify the http webServerUser")) + } + if reflect.ValueOf(r.Spec.WebServerPwd).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("webServerPwd"), "Please specify the http webserverPassword")) + } + if r.Spec.FileNameConversions == "" { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("fileNameConversions"), "Please specify a value for fileNameConversions. Values can be a filename convert pattern or NONE")) diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index e5085e7f..89e58a33 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1912,6 +1912,8 @@ func (in *PDBSpec) DeepCopyInto(out *PDBSpec) { out.PDBTlsCat = in.PDBTlsCat out.AdminName = in.AdminName out.AdminPwd = in.AdminPwd + out.WebServerUsr = in.WebServerUsr + out.WebServerPwd = in.WebServerPwd if in.ReuseTempFile != nil { in, out := &in.ReuseTempFile, &out.ReuseTempFile *out = new(bool) @@ -2684,6 +2686,22 @@ func (in *WebServerPassword) DeepCopy() *WebServerPassword { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerPasswordPDB) DeepCopyInto(out *WebServerPasswordPDB) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPasswordPDB. +func (in *WebServerPasswordPDB) DeepCopy() *WebServerPasswordPDB { + if in == nil { + return nil + } + out := new(WebServerPasswordPDB) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WebServerUser) DeepCopyInto(out *WebServerUser) { *out = *in @@ -2699,3 +2717,19 @@ func (in *WebServerUser) DeepCopy() *WebServerUser { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerUserPDB) DeepCopyInto(out *WebServerUserPDB) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUserPDB. +func (in *WebServerUserPDB) DeepCopy() *WebServerUserPDB { + if in == nil { + return nil + } + out := new(WebServerUserPDB) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/database.oracle.com_CDBS.yaml b/config/crd/bases/database.oracle.com_CDBS.yaml new file mode 100644 index 00000000..f3ee22ee --- /dev/null +++ b/config/crd/bases/database.oracle.com_CDBS.yaml @@ -0,0 +1,270 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: CDBS.database.oracle.com +spec: + group: database.oracle.com + names: + kind: CDB + listKind: CDBList + plural: CDBS + singular: cdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: CDB is the Schema for the cdbs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: CDBSpec defines the desired state of CDB + properties: + cdbAdminPwd: + description: Password for the CDB Administrator to manage PDB lifecycle + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + description: User in the root container with sysdba priviledges to + manage PDB lifecycle + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + description: Name of the CDB + type: string + cdbTlsCrt: + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + description: DB server port + type: integer + dbServer: + description: Name of the DB server + type: string + dbTnsurl: + type: string + nodeSelector: + additionalProperties: + type: string + description: Node Selector for running the Pod + type: object + ordsImage: + description: ORDS Image Name + type: string + ordsImagePullPolicy: + description: ORDS Image Pull Policy + enum: + - Always + - Never + type: string + ordsImagePullSecret: + description: The name of the image pull secret in case of a private + docker repository. + type: string + ordsPort: + description: ORDS server port. For now, keep it as 8888. TO BE USED + IN FUTURE RELEASE. + type: integer + ordsPwd: + description: Password for user ORDS_PUBLIC_USER + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + description: Number of ORDS Containers to create + type: integer + serviceName: + description: Name of the CDB Service + type: string + sysAdminPwd: + description: Password for the CDB System Administrator + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + description: Password for the Web Server User + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow + us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + description: CDBStatus defines the observed state of CDB + properties: + msg: + description: Message + type: string + phase: + description: Phase of the CDB Resource + type: string + status: + description: CDB Resource Status + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_PDBS.yaml b/config/crd/bases/database.oracle.com_PDBS.yaml new file mode 100644 index 00000000..804c72aa --- /dev/null +++ b/config/crd/bases/database.oracle.com_PDBS.yaml @@ -0,0 +1,378 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: PDBS.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB + listKind: PDBList + plural: PDBS + singular: pdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: PDB is the Schema for the pdbs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PDBSpec defines the desired state of PDB + properties: + action: + description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. + Map is used to map a Databse PDB to a Kubernetes PDB CR.' + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + description: The administrator username for the new PDB. This property + is required when the Action property is Create. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + description: The administrator password for the new PDB. This property + is required when the Action property is Create. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + description: Indicate if 'AS CLONE' option should be used in the command + to plug in a PDB. This property is applicable when the Action property + is PLUG but not required. + type: boolean + cdbName: + description: Name of the CDB + type: string + cdbNamespace: + description: CDB Namespace + type: string + cdbResName: + description: Name of the CDB Custom Resource that runs the ORDS container + type: string + copyAction: + description: To copy files or not while cloning a PDB + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + description: Specify if datafiles should be removed or not. The value + can be INCLUDING or KEEP (default). + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + description: Relevant for Create and Plug operations. As defined in + the Oracle Multitenant Database documentation. Values can be a + filename convert pattern or NONE. + type: string + getScript: + description: Whether you need the script only or execute the script + type: boolean + modifyOption: + description: Extra options for opening and closing a PDB + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + description: The name of the new PDB. Relevant for both Create and + Plug Actions. + type: string + pdbState: + description: The target state of the PDB + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + description: Whether to reuse temp file + type: boolean + sourceFileNameConversions: + description: This property is required when the Action property is + Plug. As defined in the Oracle Multitenant Database documentation. + Values can be a source filename convert pattern or NONE. + type: string + sparseClonePath: + description: A Path specified for sparse clone snapshot copy. (Optional) + type: string + srcPdbName: + description: Name of the Source PDB from which to clone + type: string + tdeExport: + description: TDE export for unplug operations + type: boolean + tdeImport: + description: TDE import for plug operations + type: boolean + tdeKeystorePath: + description: TDE keystore path is required if the tdeImport or tdeExport + flag is set to true. Can be used in plug or unplug operations. + type: string + tdePassword: + description: TDE password if the tdeImport or tdeExport flag is set + to true. Can be used in create, plug or unplug operations + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + description: TDE secret is required if the tdeImport or tdeExport + flag is set to true. Can be used in plug or unplug operations. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + description: Relevant for Create and Clone operations. Total size + for temporary tablespace as defined in the Oracle Multitenant Database + documentation. See size_clause description in Database SQL Language + Reference documentation. + type: string + totalSize: + description: Relevant for create and plug operations. Total size as + defined in the Oracle Multitenant Database documentation. See size_clause + description in Database SQL Language Reference documentation. + type: string + unlimitedStorage: + description: Relevant for Create and Plug operations. True for unlimited + storage. Even when set to true, totalSize and tempSize MUST be specified + in the request if Action is Create. + type: boolean + webServerPwd: + description: Password for the Web ServerPDB User + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow + us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + description: XML metadata filename to be used for Plug or Unplug operations + type: string + required: + - action + type: object + status: + description: PDBStatus defines the observed state of PDB + properties: + action: + description: Last Completed Action + type: string + connString: + description: PDB Connect String + type: string + modifyOption: + description: Modify Option of the PDB + type: string + msg: + description: Message + type: string + openMode: + description: Open mode of the PDB + type: string + phase: + description: Phase of the PDB Resource + type: string + status: + description: PDB Resource Status + type: boolean + totalSize: + description: Total size of the PDB + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index 1bc8d905..8e6cb94f 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -122,6 +122,9 @@ spec: cdbName: description: Name of the CDB type: string + cdbNamespace: + description: CDB Namespace + type: string cdbResName: description: Name of the CDB Custom Resource that runs the ORDS container type: string @@ -290,6 +293,41 @@ spec: storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. type: boolean + webServerPwd: + description: Password for the Web ServerPDB User + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow + us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object xmlFileName: description: XML metadata filename to be used for Plug or Unplug operations type: string diff --git a/controllers/database/cdb_controller.go b/controllers/database/cdb_controller.go index 50778ccd..4c92edc1 100644 --- a/controllers/database/cdb_controller.go +++ b/controllers/database/cdb_controller.go @@ -220,9 +220,11 @@ func (r *CDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R return requeueN, nil } -/********************************************************** - * Create a ReplicaSet for pods based on the ORDS container - /********************************************************/ +/* +********************************************************* + - Create a ReplicaSet for pods based on the ORDS container + /******************************************************* +*/ func (r *CDBReconciler) createORDSInstances(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("createORDSInstances", req.NamespacedName) @@ -251,9 +253,11 @@ func (r *CDBReconciler) createORDSInstances(ctx context.Context, req ctrl.Reques return nil } -/************************************************* - * Validate ORDS Pod. Check if there are any errors - /************************************************/ +/* +************************************************ + - Validate ORDS Pod. Check if there are any errors + /*********************************************** +*/ func (r *CDBReconciler) validateORDSPods(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("validateORDSPod", req.NamespacedName) @@ -311,9 +315,12 @@ func (r *CDBReconciler) validateORDSPods(ctx context.Context, req ctrl.Request, return nil } -/************************ - * Create Pod spec -/************************/ +/* +*********************** + - Create Pod spec + +/*********************** +*/ func (r *CDBReconciler) createPodSpec(cdb *dbapi.CDB) corev1.PodSpec { podSpec := corev1.PodSpec{ @@ -531,9 +538,12 @@ func (r *CDBReconciler) createPodSpec(cdb *dbapi.CDB) corev1.PodSpec { return podSpec } -/************************ - * Create ReplicaSet spec -/************************/ +/* +*********************** + - Create ReplicaSet spec + +/*********************** +*/ func (r *CDBReconciler) createReplicaSetSpec(cdb *dbapi.CDB) *appsv1.ReplicaSet { replicas := int32(cdb.Spec.Replicas) @@ -570,9 +580,11 @@ func (r *CDBReconciler) createReplicaSetSpec(cdb *dbapi.CDB) *appsv1.ReplicaSet return replicaSet } -/********************************************************** - * Evaluate change in Spec post creation and instantiation - /********************************************************/ +/* +********************************************************* + - Evaluate change in Spec post creation and instantiation + /******************************************************* +*/ func (r *CDBReconciler) deleteReplicaSet(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("deleteReplicaSet", req.NamespacedName) @@ -596,9 +608,11 @@ func (r *CDBReconciler) deleteReplicaSet(ctx context.Context, req ctrl.Request, return nil } -/********************************************************** - * Evaluate change in Spec post creation and instantiation - /********************************************************/ +/* +********************************************************* + - Evaluate change in Spec post creation and instantiation + /******************************************************* +*/ func (r *CDBReconciler) evaluateSpecChange(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("evaluateSpecChange", req.NamespacedName) @@ -673,9 +687,11 @@ func (r *CDBReconciler) evaluateSpecChange(ctx context.Context, req ctrl.Request return nil } -/************************************************* - * Create a Cluster Service for ORDS CDB Pod - /************************************************/ +/* +************************************************ + - Create a Cluster Service for ORDS CDB Pod + /*********************************************** +*/ func (r *CDBReconciler) createORDSSVC(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("createORDSSVC", req.NamespacedName) @@ -701,9 +717,11 @@ func (r *CDBReconciler) createORDSSVC(ctx context.Context, req ctrl.Request, cdb return nil } -/************************ - * Create Service spec - /************************/ +/* +*********************** + - Create Service spec + /*********************** +*/ func (r *CDBReconciler) createSvcSpec(cdb *dbapi.CDB) *corev1.Service { svc := &corev1.Service{ @@ -726,9 +744,11 @@ func (r *CDBReconciler) createSvcSpec(cdb *dbapi.CDB) *corev1.Service { return svc } -/************************************************* - * Check CDB deletion - /************************************************/ +/* +************************************************ + - Check CDB deletion + /*********************************************** +*/ func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("manageCDBDeletion", req.NamespacedName) @@ -781,9 +801,12 @@ func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, return nil } -/************************************************* - * Delete CDB Resource -/************************************************/ +/* +************************************************ + - Delete CDB Resource + +/*********************************************** +*/ func (r *CDBReconciler) deleteCDBInstance(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("deleteCDBInstance", req.NamespacedName) @@ -824,9 +847,11 @@ func (r *CDBReconciler) deleteCDBInstance(ctx context.Context, req ctrl.Request, return nil } -/************************************************* - * Get Secret Key for a Secret Name - /************************************************/ +/* +************************************************ + - Get Secret Key for a Secret Name + /*********************************************** +*/ func (r *CDBReconciler) verifySecrets(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("verifySecrets", req.NamespacedName) @@ -855,9 +880,11 @@ func (r *CDBReconciler) verifySecrets(ctx context.Context, req ctrl.Request, cdb return nil } -/************************************************* - * Get Secret Key for a Secret Name - /************************************************/ +/* +************************************************ + - Get Secret Key for a Secret Name + /*********************************************** +*/ func (r *CDBReconciler) checkSecret(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB, secretName string) error { log := r.Log.WithValues("checkSecret", req.NamespacedName) @@ -877,9 +904,11 @@ func (r *CDBReconciler) checkSecret(ctx context.Context, req ctrl.Request, cdb * return nil } -/************************************************* - * Delete Secrets - /************************************************/ +/* +************************************************ + - Delete Secrets + /*********************************************** +*/ func (r *CDBReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) { log := r.Log.WithValues("deleteSecrets", req.NamespacedName) @@ -935,9 +964,11 @@ func (r *CDBReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, cdb } } -/************************************************************** - * SetupWithManager sets up the controller with the Manager. - /*************************************************************/ +/* +************************************************************* + - SetupWithManager sets up the controller with the Manager. + /************************************************************ +*/ func (r *CDBReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&dbapi.CDB{}). diff --git a/controllers/database/pdb_controller.go b/controllers/database/pdb_controller.go index 7a72e4f4..5c173740 100644 --- a/controllers/database/pdb_controller.go +++ b/controllers/database/pdb_controller.go @@ -128,6 +128,12 @@ var floodcontrol bool = false //+kubebuilder:rbac:groups=database.oracle.com,resources=pdbs/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=pdbs/finalizers,verbs=get;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=pods;pods/log;pods/exec;secrets;containers;services;events;configmaps;namespaces,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=pods/exec,verbs=create +// +kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups='',resources=statefulsets/finalizers,verbs=get;list;watch;create;update;patch;delete + + // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // TODO(user): Modify the Reconcile function to compare the state specified by @@ -137,6 +143,7 @@ var floodcontrol bool = false // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile + func (r *PDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithValues("multitenantoperator", req.NamespacedName) log.Info("Reconcile requested") @@ -291,23 +298,20 @@ func (r *PDBReconciler) validatePhase(ctx context.Context, req ctrl.Request, pdb */ func (r *PDBReconciler) checkDuplicatePDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - log := r.Log.WithValues("checkDuplicatePDB", req.NamespacedName) - // Name of the CDB CR that holds the ORDS container cdbResName := pdb.Spec.CDBResName - //cdbame := pdb.Spec.CDBName - // Name of the PDB resource + log := r.Log.WithValues("checkDuplicatePDB", pdb.Spec.CDBNamespace) pdbResName := pdb.Spec.PDBName pdbList := &dbapi.PDBList{} - listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingFields{"spec.pdbName": pdbResName}} + listOpts := []client.ListOption{client.InNamespace(pdb.Spec.CDBNamespace), client.MatchingFields{"spec.pdbName": pdbResName}} // List retrieves list of objects for a given namespace and list options. err := r.List(ctx, pdbList, listOpts...) if err != nil { - log.Info("Failed to list pdbs", "Namespace", req.Namespace, "Error", err) + log.Info("Failed to list pdbs", "Namespace", pdb.Spec.CDBNamespace, "Error", err) return err } @@ -342,15 +346,16 @@ func (r *PDBReconciler) getCDBResource(ctx context.Context, req ctrl.Request, pd // Name of the CDB CR that holds the ORDS container cdbResName := pdb.Spec.CDBResName + cdbNamespace := pdb.Spec.CDBNamespace // Get CDB CR corresponding to the CDB name specified in the PDB spec err := r.Get(context.Background(), client.ObjectKey{ - Namespace: req.Namespace, + Namespace: cdbNamespace, Name: cdbResName, }, &cdb) if err != nil { - log.Info("Failed to get CRD for CDB", "Name", cdbResName, "Namespace", req.Namespace, "Error", err.Error()) + log.Info("Failed to get CRD for CDB", "Name", cdbResName, "Namespace", pdb.Spec.CDBNamespace, "Error", err.Error()) pdb.Status.Msg = "Unable to get CRD for CDB : " + cdbResName r.Status().Update(ctx, pdb) return cdb, err @@ -376,12 +381,12 @@ func (r *PDBReconciler) getORDSPod(ctx context.Context, req ctrl.Request, pdb *d // Get ORDS Pod associated with the CDB Name specified in the PDB Spec err := r.Get(context.Background(), client.ObjectKey{ - Namespace: req.Namespace, + Namespace: pdb.Spec.CDBNamespace, Name: cdbResName + "-ords", }, &cdbPod) if err != nil { - log.Info("Failed to get Pod for CDB", "Name", cdbResName, "Namespace", req.Namespace, "Error", err.Error()) + log.Info("Failed to get Pod for CDB", "Name", cdbResName, "Namespace", pdb.Spec.CDBNamespace, "Error", err.Error()) pdb.Status.Msg = "Unable to get ORDS Pod for CDB : " + cdbResName return cdbPod, err } @@ -486,37 +491,37 @@ func (r *PDBReconciler) callAPI(ctx context.Context, req ctrl.Request, pdb *dbap log.Info("Issuing REST call", "URL", url, "Action", action) - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return "", err - } + /* + cdb, err := r.getCDBResource(ctx, req, pdb) + if err != nil { + return "", err + } + */ - // Get Web Server User - //secret := &corev1.Secret{} - err = r.Get(ctx, types.NamespacedName{Name: cdb.Spec.WebServerUser.Secret.SecretName, Namespace: cdb.Namespace}, secret) + err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.WebServerUsr.Secret.SecretName, Namespace: pdb.Namespace}, secret) if err != nil { if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + cdb.Spec.WebServerUser.Secret.SecretName) + log.Info("Secret not found:" + pdb.Spec.WebServerUsr.Secret.SecretName) return "", err } log.Error(err, "Unable to get the secret.") return "", err } - webUser := string(secret.Data[cdb.Spec.WebServerUser.Secret.Key]) + + webUser := string(secret.Data[pdb.Spec.WebServerUsr.Secret.Key]) webUser = strings.TrimSpace(webUser) - // Get Web Server User Password secret = &corev1.Secret{} - err = r.Get(ctx, types.NamespacedName{Name: cdb.Spec.WebServerPwd.Secret.SecretName, Namespace: cdb.Namespace}, secret) + err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.WebServerPwd.Secret.SecretName, Namespace: pdb.Namespace}, secret) if err != nil { if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + cdb.Spec.WebServerPwd.Secret.SecretName) + log.Info("Secret not found:" + pdb.Spec.WebServerPwd.Secret.SecretName) return "", err } log.Error(err, "Unable to get the secret.") return "", err } - webUserPwd := string(secret.Data[cdb.Spec.WebServerPwd.Secret.Key]) + webUserPwd := string(secret.Data[pdb.Spec.WebServerPwd.Secret.Key]) webUserPwd = strings.TrimSpace(webUserPwd) var httpreq *http.Request @@ -676,7 +681,8 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db values["tdeSecret"] = tdeSecret } - url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" pdb.Status.TotalSize = pdb.Spec.TotalSize pdb.Status.Phase = pdbPhaseCreate @@ -686,7 +692,7 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db } _, err = r.callAPI(ctx, req, pdb, url, values, "POST") if err != nil { - log.Error(err, "callAPI error", err.Error()) + log.Error(err, "callAPI error", "err", err.Error()) return err } @@ -757,7 +763,9 @@ func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dba values["tempSize"] = pdb.Spec.TempSize } - url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.SrcPDBName + "/" + //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.SrcPDBName + "/" + //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.SrcPDBName + "/" + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.SrcPDBName + "/" pdb.Status.Phase = pdbPhaseClone pdb.Status.Msg = "Waiting for PDB to be cloned" @@ -837,7 +845,9 @@ func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbap values["asClone"] = strconv.FormatBool(*(pdb.Spec.AsClone)) } - url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" pdb.Status.TotalSize = pdb.Spec.TotalSize pdb.Status.Phase = pdbPhasePlug @@ -905,7 +915,9 @@ func (r *PDBReconciler) unplugPDB(ctx context.Context, req ctrl.Request, pdb *db values["tdeExport"] = strconv.FormatBool(*(pdb.Spec.TDEExport)) } - url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.PDBName + "/" + //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.PDBName + "/" + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.PDBName + "/" + log.Info("CallAPI(url)", "url", url) pdb.Status.Phase = pdbPhaseUnplug @@ -999,7 +1011,8 @@ func (r *PDBReconciler) modifyPDB(ctx context.Context, req ctrl.Request, pdb *db log.Info("PDB STATUS OPENMODE", "pdb.Status.OpenMode=", pdb.Status.OpenMode) pdbName := pdb.Spec.PDBName - url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" + //url := "https://" + pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" pdb.Status.Phase = pdbPhaseModify pdb.Status.ModifyOption = pdb.Spec.PDBState + "-" + pdb.Spec.ModifyOption @@ -1042,7 +1055,9 @@ func (r *PDBReconciler) getPDBState(ctx context.Context, req ctrl.Request, pdb * } pdbName := pdb.Spec.PDBName - url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" + //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" + //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" pdb.Status.Msg = "Getting PDB state" if err := r.Status().Update(ctx, pdb); err != nil { @@ -1093,7 +1108,9 @@ func (r *PDBReconciler) mapPDB(ctx context.Context, req ctrl.Request, pdb *dbapi } pdbName := pdb.Spec.PDBName - url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" + //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" + //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" pdb.Status.Msg = "Mapping PDB" if err := r.Status().Update(ctx, pdb); err != nil { @@ -1242,7 +1259,7 @@ func (r *PDBReconciler) deletePDBInstance(req ctrl.Request, ctx context.Context, } pdbName := pdb.Spec.PDBName - url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" pdb.Status.Phase = pdbPhaseDelete pdb.Status.Msg = "Waiting for PDB to be deleted" diff --git a/docs/multitenant/NamespaceSeg.md b/docs/multitenant/NamespaceSeg.md new file mode 100644 index 00000000..6738fe56 --- /dev/null +++ b/docs/multitenant/NamespaceSeg.md @@ -0,0 +1,14 @@ + + +# Namespace segregation + +With the namespace segregation pdb controller and cdb controller run in different namespaces. The new functionality introduces a new parameter (the cdb namespace) in pdb crd definition. In case you don't need the namespace segregation you have to sepcify the namespace name that you are using for yours crd and pods anyway. Refer to usercase01 and usecase02 to see single namespace configuration. Refer to usecase03 to see examples of namespace segregation. + +# Secrets + +In order to use multiple namespace we need to create approriate secrets in each namespace. Tls certificate secrets must be created in all namespaces (db-ca db-tls). + +![general_schema](./images/K8S_NAMESPACE_SEG.png) + + + diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index 724c39e6..8f862508 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -6,6 +6,13 @@ > WARNING: Examples with https are located in the use case directories +Detailed examples can be found here + +- [Usecase01](./usecase01) pdb crd and cdb pod are running in the same namesaoce +- [Usecase02](./usecase02) unplug and plug operation examples +- [Usecase03](./usecase03) multiple namespace example cdb pod ,pdb crd and pod operator are running in different namespaces + + CDBs and PDBs are part of the Oracle Database [Multitenant Architecture](https://docs.oracle.com/en/database/oracle/oracle-database/21/multi/introduction-to-the-multitenant-architecture.html#GUID-AB84D6C9-4BBE-4D36-992F-2BB85739329F). The Multitenant Database Controller is a feature of Oracle DB Operator for Kubernetes (`OraOperator`), which helps to manage the lifecycle of Pluggable Databases (PDBs) in an Oracle Container Database (CDB). The target CDB for which PDB lifecycle management is needed can be running on a machine on-premises. To manage the PDBs of that target CDB, you can run the Oracle DB Operator on a Kubernetes system on-premises (For Example: [Oracle Linux Cloud Native Environment or OLCNE](https://docs.oracle.com/en/operating-systems/olcne/)). @@ -128,6 +135,8 @@ See this [provisioning example setup](./provisioning/example_setup_using_oci_oke Oracle DB Operator Multitenant Database Controller uses Kubernetes Secrets to store usernames and passwords that you must have to manage the lifecycle operations of a PDB in the target CDB. In addition, to use https protocol, all certificates need to be stored using Kubernetes Secret. + **Note** In multi namespace enviroment you have to create specific secrets for each namespaces + ### Secrets for CDB CRD Create a secret file as shown here: [config/samples/multitenant/cdb_secret.yaml](../../config/samples/multitenant/cdb_secret.yaml). Modify this file with the `base64` encoded values of the required passwords for CDB, and use this file to create the required secrets. diff --git a/docs/multitenant/images/K8S_NAMESPACE_SEG.png b/docs/multitenant/images/K8S_NAMESPACE_SEG.png new file mode 100644 index 0000000000000000000000000000000000000000..594471ed54a6c980c9e63ab2f538c40a32a5dcb8 GIT binary patch literal 269014 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 -n oracle-database-op ---- #### Apply cdb.yaml + +**note:** + Before creating the CDB pod make sure that all the pluggable databases in the container DB are open. + + + Create ords container ```bash diff --git a/docs/multitenant/usecase01/cdb_create.yaml b/docs/multitenant/usecase01/cdb_create.yaml new file mode 100644 index 00000000..01fc0a18 --- /dev/null +++ b/docs/multitenant/usecase01/cdb_create.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: CDB +metadata: + name: cdb-dev + namespace: oracle-database-operator-system +spec: + cdbName: "DB12" + ordsImage: ".............your registry............./ords-dboper:latest" + ordsImagePullPolicy: "Always" + dbTnsurl : "...Container tns alias....." + replicas: 1 + sysAdminPwd: + secret: + secretName: "cdb1-secret" + key: "sysadmin_pwd" + ordsPwd: + secret: + secretName: "cdb1-secret" + key: "ords_pwd" + cdbAdminUser: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_user" + cdbAdminPwd: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_pwd" + webServerUser: + secret: + secretName: "cdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "cdb1-secret" + key: "webserver_pwd" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + diff --git a/docs/multitenant/usecase01/cdb_secret.yaml b/docs/multitenant/usecase01/cdb_secret.yaml new file mode 100644 index 00000000..567b90a4 --- /dev/null +++ b/docs/multitenant/usecase01/cdb_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: cdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + ords_pwd: ".....base64 encoded password...." + sysadmin_pwd: ".....base64 encoded password...." + cdbadmin_user: ".....base64 encoded password...." + cdbadmin_pwd: ".....base64 encoded password...." + webserver_user: ".....base64 encoded password...." + webserver_pwd: ".....base64 encoded password...." diff --git a/docs/multitenant/usecase01/makefile b/docs/multitenant/usecase01/makefile index 0a96d1e7..1b4e32d8 100644 --- a/docs/multitenant/usecase01/makefile +++ b/docs/multitenant/usecase01/makefile @@ -42,10 +42,16 @@ # +-----------------------------+---------------------------------------------+ # |tde_secret.yaml | Secret file for the tablepsace enc. | # +-----------------------------+---------------------------------------------+ -# |cdb.yaml | Rest server pod creation | +# |cdb_create.yaml | Rest server pod creation | # +-----------------------------+---------------------------------------------+ -# |pdb.yaml | Pluggable database creation | +# |pdb_create.yaml | Pluggable database creation | # +-----------------------------+---------------------------------------------+ +# |pdb_close.yaml | Close pluggable database | +# +-----------------------------+---------------------------------------------+ +# |pdb_open.yaml | Open pluggable database | +# +-----------------------------+---------------------------------------------+ +# |pdb_map.yaml | Map an existing pdb | +# +-----------------------------+---------------------------------------------+ # |oracle-database-operator.yaml| Database operator | # +-----------------------------+---------------------------------------------+ # |Dockerfiles | Dockerfile for CBD | @@ -94,6 +100,15 @@ # | | +---------------------------------------------+ # | +---> checkpdb | Monitor PDB status | # +-----------------------------+---------------------------------------------+ +# |step11 | Close pluggable database | +# +-----------------------------+---------------------------------------------+ +# |step12 | Open pluggable database | +# +-----------------------------+---------------------------------------------+ +# |step13 | Map pluggable database | +# +-----------------------------+---------------------------------------------+ +# | Before testing step13 delete the crd: | +# | kubectl delete pdb pdb1 -n oracle-database-operator-system | +# +---------------------------------------------------------------------------+ # | DIAGNOSTIC TARGETS | # +-----------------------------+---------------------------------------------+ # | dump | Dump pods info into a file | @@ -120,16 +135,21 @@ URLPATH=/_/db-api/stable/database/pdbs/ OPENSSL=/usr/bin/openssl ORDSPORT=8888 MAKE=/usr/bin/make -DOCKERFILE=Dockerfile +DOCKERFILE=../../../ords/Dockerfile +RUNSCRIPT=../../../ords/runOrdsSSL.sh RM=/usr/bin/rm +CP=/usr/bin/cp ECHO=/usr/bin/echo NAMESPACE=oracle-database-operator-system CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml CDB_SECRET=cdb_secret.yaml PDB_SECRET=pdb_secret.yaml TDE_SECRET=tde_secret.yaml -CDB=cdb.yaml -PDB=pdb.yaml +CDB=cdb_create.yaml +PDB=pdb_create.yaml +PDB_CLOSE=pdb_close.yaml +PDB_OPEN=pdb_open.yaml +PDB_MAP=pdb_map.yaml SKEY=tls.key SCRT=tls.crt CART=ca.crt @@ -145,23 +165,29 @@ step7: tlssecret step8: dbsecret step9: cdb step10: pdb +step11: close +step12: open +step13: map checkstep9: checkcdb createimage: + $(CP) $(DOCKERFILE) . + $(CP) $(RUNSCRIPT) . @echo "BUILDING CDB IMAGES" @if [[ ! -f ./Dockerfile ]]; \ then\ echo "DOCKERFILE DOES NOT EXISTS";\ exit 1; \ fi; - @if [[ ! -f ../runOrdsSSL.sh ]]; \ + @if [[ ! -f ./runOrdsSSL.sh ]]; \ then\ echo "DOCKERFILE DOES NOT EXISTS";\ exit 1; \ fi; $(DOCKER) build -t $(IMAGE) . + $(RM) ./Dockerfile ./runOrdsSSL.sh tagimage: @echo "TAG IMAGE" @@ -183,8 +209,8 @@ tlscert: @echo "CREATING TLS CERTIFICATES" $(OPENSSL) genrsa -out ca.key 2048 $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER),DNS:www.example.com" > extfile.txt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(NAMESPACE)" -out server.csr + $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(NAMESPACE),DNS:www.example.com" > extfile.txt $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) tlssecret: @@ -208,6 +234,15 @@ checkcdb: pdb: $(KUBECTL) apply -f $(PDB) +close: + $(KUBECTL) apply -f $(PDB_CLOSE) + +open: + $(KUBECTL) apply -f $(PDB_OPEN) + +map: + $(KUBECTL) apply -f $(PDB_MAP) + checkpdb: $(KUBECTL) get pdbs -n $(NAMESPACE) diff --git a/docs/multitenant/usecase01/oracle-database-operator.yaml b/docs/multitenant/usecase01/oracle-database-operator.yaml new file mode 120000 index 00000000..d5bae7bc --- /dev/null +++ b/docs/multitenant/usecase01/oracle-database-operator.yaml @@ -0,0 +1 @@ +../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/usecase01/pdb_close.yaml b/docs/multitenant/usecase01/pdb_close.yaml new file mode 100644 index 00000000..5917d33a --- /dev/null +++ b/docs/multitenant/usecase01/pdb_close.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + diff --git a/docs/multitenant/usecase01/pdb_create.yaml b/docs/multitenant/usecase01/pdb_create.yaml new file mode 100644 index 00000000..7953118f --- /dev/null +++ b/docs/multitenant/usecase01/pdb_create.yaml @@ -0,0 +1,46 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + fileNameConversions: "NONE" + tdeImport: false + totalSize: "1G" + tempSize: "100M" + action: "Create" + diff --git a/docs/multitenant/usecase01/pdb_map.yaml b/docs/multitenant/usecase01/pdb_map.yaml new file mode 100644 index 00000000..cd6d4ffb --- /dev/null +++ b/docs/multitenant/usecase01/pdb_map.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" diff --git a/docs/multitenant/usecase01/pdb_open.yaml b/docs/multitenant/usecase01/pdb_open.yaml new file mode 100644 index 00000000..25fdccc4 --- /dev/null +++ b/docs/multitenant/usecase01/pdb_open.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" diff --git a/docs/multitenant/usecase01/pdb_secret.yaml b/docs/multitenant/usecase01/pdb_secret.yaml new file mode 100644 index 00000000..60d95d76 --- /dev/null +++ b/docs/multitenant/usecase01/pdb_secret.yaml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: pdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + sysadmin_user: ".....base64 encoded password...." + sysadmin_pwd: ".....base64 encoded password...." + webserver_user: ".....base64 encoded password...." + webserver_pwd: ".....base64 encoded password...." + diff --git a/docs/multitenant/usecase01/tde_secret.yaml b/docs/multitenant/usecase01/tde_secret.yaml new file mode 100644 index 00000000..7cf66c03 --- /dev/null +++ b/docs/multitenant/usecase01/tde_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: tde1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + tdepassword: "bW1hbHZlenoK" + tdesecret: "bW1hbHZlenoK" + + + + diff --git a/docs/multitenant/usecase02/README.md b/docs/multitenant/usecase02/README.md index f43d1f26..0b060df7 100644 --- a/docs/multitenant/usecase02/README.md +++ b/docs/multitenant/usecase02/README.md @@ -8,10 +8,11 @@ - [UNPLUG DATABASE](#unplug-database) - [PLUG DATABASE](#plug-database) - [CLONE PDB](#clone-pdb) - - [UNPLUG AND PLUG WITH TDE](#unplug-and-plug-with-tde) ### INTRODUCTION +> ☞ The examples of this folder are based on single namespace **oracle-database-operator-system** + This page explains how to plug and unplug database a pdb; it assumes that you have already configured a pluggable database (see usecase01) The following table reports the parameters required to configure and use oracle multi tenant controller for pluggable database lifecycle management. @@ -108,6 +109,9 @@ MSG=Success Prepare a new yaml file **pdb_unplug.yaml** to unplug the pdbdev database. Make sure that the path of the xml file is correct and check the existence of all the required secrets. ```yaml +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# #pdb_unplug.yaml apiVersion: database.oracle.com/v1alpha1 kind: PDB @@ -118,6 +122,7 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" xmlFileName: "/tmp/pdbunplug.xml" @@ -134,11 +139,23 @@ spec: secret: secretName: "db-ca" key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + ``` Close the pluggable database by applying the following yaml file **pdb_close.yaml** ```yaml +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# #pdb_close.yaml apiVersion: database.oracle.com/v1alpha1 kind: PDB @@ -149,6 +166,7 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" adminName: @@ -253,7 +271,9 @@ DATAFILE UNPROT COARSE JAN 03 14:00:00 Y system.353.1125061021 Prepare a new yaml file **pdb_plug.yaml** to plug the database back into the container. ```yaml - +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# # pdb_plug.yaml apiVersion: database.oracle.com/v1alpha1 kind: PDB @@ -264,6 +284,7 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" xmlFileName: "/tmp/pdbunplug.xml" @@ -362,6 +383,7 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdb2-clone" srcPdbName: "pdbdev" @@ -458,6 +480,13 @@ PDBDEV(3):Buffer Cache flush finished: 3 ``` ### UNPLUG AND PLUG WITH TDE + + + +> ⚠ __WARNING FOR THE TDE USERS__ ⚠ According to the [ords documentation](https://docs.oracle.com/en/database/oracle/oracle-database/21/dbrst/op-database-pdbs-pdb_name-post.html) the plug and unplug operation with tde is supported only if ords runs on the same host of the database which is not the case of operator where ords runs on an isolated pods. Do not use pdb controller for unplug and plug operation with tde in production environments. + + + You can use unplug and plug database with TDE; in order to do that you have to specify a key store path and create new kubernets secret for TDE using the following yaml file. **tde_secrete.yaml**. The procedure to unplug and plug database does not change apply the same file. ```yaml @@ -491,6 +520,7 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" adminName: @@ -541,6 +571,7 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" adminName: diff --git a/docs/multitenant/usecase02/pdb_clone.yaml b/docs/multitenant/usecase02/pdb_clone.yaml index befae41e..be22020b 100644 --- a/docs/multitenant/usecase02/pdb_clone.yaml +++ b/docs/multitenant/usecase02/pdb_clone.yaml @@ -2,7 +2,6 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # - apiVersion: database.oracle.com/v1alpha1 kind: PDB metadata: @@ -12,8 +11,9 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" - pdbName: "pdb2-clone" + pdbName: "pdb2_clone" srcPdbName: "pdbdev" fileNameConversions: "NONE" totalSize: "UNLIMITED" @@ -38,5 +38,12 @@ spec: secret: secretName: "db-ca" key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" action: "Clone" - diff --git a/docs/multitenant/usecase02/pdb_plug.yaml b/docs/multitenant/usecase02/pdb_plug.yaml index ac035623..0d0d3b37 100644 --- a/docs/multitenant/usecase02/pdb_plug.yaml +++ b/docs/multitenant/usecase02/pdb_plug.yaml @@ -2,7 +2,6 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # - apiVersion: database.oracle.com/v1alpha1 kind: PDB metadata: @@ -12,9 +11,10 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" - xmlFileName: "/tmp/pdbunplug.xml" + xmlFileName: "/tmp/pdb.xml" fileNameConversions: "NONE" sourceFileNameConversions: "NONE" copyAction: "MOVE" @@ -33,5 +33,13 @@ spec: secret: secretName: "db-ca" key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/usecase02/pdb_unplug.yaml b/docs/multitenant/usecase02/pdb_unplug.yaml index 6d995ac8..085d337e 100644 --- a/docs/multitenant/usecase02/pdb_unplug.yaml +++ b/docs/multitenant/usecase02/pdb_unplug.yaml @@ -2,7 +2,6 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # - apiVersion: database.oracle.com/v1alpha1 kind: PDB metadata: @@ -12,9 +11,10 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" - xmlFileName: "/tmp/pdbunplug.xml" + xmlFileName: "/tmp/pdb.xml" action: "Unplug" pdbTlsKey: secret: @@ -28,4 +28,12 @@ spec: secret: secretName: "db-ca" key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/usecase03/Dockerfile b/docs/multitenant/usecase03/Dockerfile new file mode 100644 index 00000000..5c27f11b --- /dev/null +++ b/docs/multitenant/usecase03/Dockerfile @@ -0,0 +1,53 @@ +#LICENSE UPL 1.0 +# +# Copyright (c) 1982-2017 Oracle and/or its affiliates. All rights reserved. +# +# ORACLE DOCKERFILES PROJECT +# -------------------------- +# This is the Dockerfile for Oracle Rest Data Services 22.2 +# +FROM container-registry.oracle.com/java/jdk:latest + +# Environment variables required for this build (do NOT change) +# ------------------------------------------------------------- +ENV ORDS_HOME=/opt/oracle/ords/ \ + RUN_FILE="runOrdsSSL.sh" + +#RUN_FILE_NOSSL="runOrdsNOSSL.sh" + +# Copy binaries +# ------------- +COPY $RUN_FILE $ORDS_HOME +#COPY $RUN_FILE_NOSSL $ORDS_HOME + +RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps && \ + yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && \ + yum -y install java-11-openjdk-devel && \ + yum -y install ords && \ + yum -y install iproute && \ + yum clean all + +# Setup filesystem and oracle user +# ------------------------------------------------------------ +RUN mkdir -p $ORDS_HOME/doc_root && \ + mkdir -p $ORDS_HOME/error && \ + mkdir -p $ORDS_HOME/secrets && \ + chmod ug+x $ORDS_HOME/*.sh && \ + groupadd -g 54322 dba && \ + usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && \ + chown -R oracle:dba $ORDS_HOME && \ + echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +# Finalize setup +# ------------------- +USER oracle +WORKDIR /home/oracle + +#VOLUME ["$ORDS_HOME/config/ords"] +EXPOSE 8888 + +# Define default command to start Ords Services +CMD $ORDS_HOME/$RUN_FILE + +## ONLY FOR DEVELOPMENT STAGE +#CMD ["/usr/sbin/init"] diff --git a/docs/multitenant/usecase03/NamespaceSegregation.png b/docs/multitenant/usecase03/NamespaceSegregation.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb0ae77818e87ed64bf248bc0c173a4aac28c73 GIT binary patch literal 270813 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≪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 S*V+lde;zJA&et8oss9JAh>QII literal 0 HcmV?d00001 diff --git a/docs/multitenant/usecase03/README.md b/docs/multitenant/usecase03/README.md new file mode 100644 index 00000000..3703a9ff --- /dev/null +++ b/docs/multitenant/usecase03/README.md @@ -0,0 +1,266 @@ + + + +# STEP BY STEP (NAMESPACE SEGREGATION) + +- [STEP BY STEP (NAMESPACE SEGREGATION)](#step-by-step-namespace-segregation) + - [INTRODUCTION](#introduction) + - [GIT CLONE ORACLE DATABASE OPERATOR PROJECT](#git-clone-oracle-database-operator-project) + - [NAMESPACE CREATION](#namespace-creation) + - [WEBHOOK CERTIFICATES](#webhook-certificates) + - [ORACLE DATABASE OPERATOR](#oracle-database-operator) + - [CREATE PDB AND CDB SECRETS](#create-pdb-and-cdb-secrets) + - [CREATE TLS CERTIFICATE](#create-tls-certificate) + - [REST SERVER IMAGE CREATION](#rest-server-image-creation) + - [CDB POD CREATION](#cdb-pod-creation) + - [PDB CREATION](#pdb-creation) + - [MAKEFILE](#makefile) + + +### INTRODUCTION + +> ☞ This folder contains the yaml files required to configure and manage cdb and pdb in different namespaces. The main change here is the possibility to specify the namespace where CDB will be created, this implies the introduction of a new parameter at PDB level in order to specify the CDB namespace. + +Tasks performed in the usecase03 are the same ones of the other usecases with the exception that controller pods cdb pods and pdb crd are running in different namespaces. You must be aware of the fact that secrets must be created in the proper namespaces; cdb secrets go into cdb namespace , pdb secrets go into pdbnamespace while certificate secrets need to be created in every namespace. + + +| yaml file parameters | value | description /ords parameter | +|-------------- |--------------------------- |-------------------------------------------------| +| ☞ cdbNamespace | | Cdb namespace | +| dbserver | or | [--db-hostname][1] | +| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | +| port | | [--db-port][2] | +| cdbName | | Container Name | +| name | | Ords podname prefix in cdb.yaml | +| name | | pdb resource in pdb.yaml | +| ordsImage | /ords-dboper:latest|My public container registry | +| pdbName | | Pluggable database name | +| servicename | | [--db-servicename][3] | +| sysadmin_user | | [--admin-user][adminuser] | +| sysadmin_pwd | | [--password-stdin][pwdstdin] | +| cdbadmin_user | | [db.cdb.adminUser][1] | +| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | +| webserver_user| | [https user][http] NOT A DB USER | +| webserver_pwd | | [http user password][http] | +| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | +| pdbTlsKey | | [standalone.https.cert.key][key] | +| pdbTlsCrt | | [standalone.https.cert][cr] | +| pdbTlsCat | | certificate authority | +| xmlFileName | | path for the unplug and plug operation | +| srcPdbName | | name of the database to be cloned | +| fileNameConversions | | used for database cloning | +| tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | +| tdeExport | | [tdeExport] | +| tdeSecret | | [tdeSecret][tdeSecret] | +| tdePassword | | [tdeSecret][tdeSecret] | + +![generla schema](./NamespaceSegregation.png) + +### GIT CLONE ORACLE DATABASE OPERATOR PROJECT + +```bash +git clone https://github.com/oracle/oracle-database-operator.git +cd oracle-database-operator/docs/multitenant/usecase03 +``` +### NAMESPACE CREATION + +We need first to create two different namespaces (**cdbnamespace**,**pdbnamespace**) using ns_pdb_namespace.yaml and ns_cdb_namespace.yaml + +```bash +kubectl apply -f ns_pdb_namespace.yaml +kubectl apply -f ns_cdb_namespace.yaml +``` + +### WEBHOOK CERTIFICATES +Create cert manager and verify the status + +```bash +kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml +``` + +```bash +kubectl get pods --namespace cert-manager +NAME READY STATUS RESTARTS AGE +cert-manager-75997f4b44-4nf5c 1/1 Running 1 9d +cert-manager-cainjector-769785cd7b-mzfq5 1/1 Running 1 9d +cert-manager-webhook-6bc9944d78-tslrp 1/1 Running 1 9d +``` + +### ORACLE DATABASE OPERATOR + +Create the oracle database operator using oracle-database-operator.yaml +```bash +cd oracle-database-operator +kubectl apply -f oracle-database-operator.yaml +cd - +``` + +[operator creation log](operator_creation_log.txt) +### CREATE PDB AND CDB SECRETS + +Update secrets files with your base64 encodede password. + +```bash +echo ImAdemoPassword | base64 +SW1BZGVtb1Bhc3N3b3JkCg== +``` +Apply the cdb_secret and pdb_secret yaml file to generate credential information in each namespace. + +``` +kubectl apply -f cdb_secret.yaml +kubectl apply -f pdb_secret.yaml +``` +> ☞ Note that https credential needs to be replicated in any secret file. It is possible to improve configuration by creating a dedicated namespace for https credential in order to specify this information only once. + +Namespace segregation enables the capability of deploying and manages pluggable database without the cdb administrative passwords. + +### CREATE TLS CERTIFICATE + +Here follow an example of script shell that can be used to create secret certificates in each namespace involved in the kubernets multi tenant architecture + +```bash +#!/bin/bash +export CDB_NAMESPACE=cdbnamespace +export PDB_NAMESPACE=pdbnamespace +export OPR_NAMESPACE=oracle-database-operator-system +export SKEY=tls.key +export SCRT=tls.crt +export CART=ca.crt +export COMPANY=oracle +export REST_SERVER=ords + +openssl genrsa -out ca.key 2048 +openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt +openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr +echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt +openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} + +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} +``` +after all secrets creation you shoud have the following pattern + +```bash +kubectl get secrets -n oracle-database-operator-system +NAME TYPE DATA AGE +db-ca Opaque 1 6d5h +db-tls kubernetes.io/tls 2 6d5h +webhook-server-cert kubernetes.io/tls 3 6d15h + + +kubectl get secrets -n cdbnamespace +NAME TYPE DATA AGE +cdb1-secret Opaque 6 6d15h +db-ca Opaque 1 6d6h +db-tls kubernetes.io/tls 2 6d6h + + +kubectl get secrets -n pdbnamespace +NAME TYPE DATA AGE +db-ca Opaque 1 6d6h +db-tls kubernetes.io/tls 2 6d6h +pdb1-secret Opaque 4 2d16h +tde1-secret Opaque 2 22h +``` +### REST SERVER IMAGE CREATION + +```bash +cd oracle-database-operator/ords +docker build -t oracle/ords-dboper:latest . +docker tag oracle/ords-dboper:latest [path_of_your_registry]/ords-dboper:latest +docker push [path_of_your_registry]/ords-dboper.latest +cd - +``` + +### CDB POD CREATION + +**note:** + Before creating the CDB pod make sure that all the pluggable databases in the container DB are open. + + + +Update the cdb_create.yaml with the path of the image generated before to create CDB pod + +```bash +kubectl apply -f cdb_create.yaml +``` + +Verify the status of the operation and cdb pod existence using the following commands + +```bash +## check the pod creation +kubectl get pods -n cdbnamespace + +## check the rest server log after pod creation +kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace + +##login to the pod for further debug and information gathering +kubectl exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash +``` + +[log cdb creation](./cdb_creation_log.txt) + +### PDB CREATION + +Apply the the pdb_create.yaml file to create a new pdb , after pdb creation you should be able to get pdb details using **kubectl get** command + +```bash +kubectl apply -f pdb_create.yaml +``` + +```bash +#!/bin/bash +#checkpdbs.sh +kubectl get pdbs -n pdbnamespace -o=jsonpath='{range .items[*]} +{"\n==================================================================\n"} +{"CDB="}{.metadata.labels.cdb} +{"K8SNAME="}{.metadata.name} +{"PDBNAME="}{.spec.pdbName} +{"OPENMODE="}{.status.openMode} +{"ACTION="}{.status.action} +{"MSG="}{.status.msg} +{"\n"}{end}' +``` + +```bash +./checkpdbs.sh +================================================================== +CDB=cdb-dev +K8SNAME=pdb1 +PDBNAME=pdbdev +OPENMODE=READ WRITE +ACTION=CREATE +MSG=Success + +``` +[pdb creation log](./pdb_creation_log.txt) + +### MAKEFILE + +In order to facilitate the command execution use the [makefile](./makefile) available target details are exposed in the following tables. + +|target |Action | +|-----------------------------|-------------------------------------| +|step1 | Build rest server images | +|step2 | Tag the immages | +|step3 | Push the image into the repository | +|step4 | Load webhook certmanager | +|step5 | Create the db operator | +|step6 | Create tls certificates | +|step7 | Create tls secret | +|step8 | Create database secrets | +|step9 | Create restserver pod | +|checkstep9 | Monitor the executions | +|step10 | Create pluggable database | +|checkpdb | Monitor PDB status | +|dump | Dump pods info into a file | +|reloadop | Reload the db operator | +|login | Login into cdb pod | + + + + diff --git a/docs/multitenant/usecase03/cdb_create.yaml b/docs/multitenant/usecase03/cdb_create.yaml new file mode 100644 index 00000000..09dd7f86 --- /dev/null +++ b/docs/multitenant/usecase03/cdb_create.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: CDB +metadata: + name: cdb-dev + namespace: cdbnamespace +spec: + cdbName: "DB12" + ordsImage: "lin.ocir.io/intsanjaysingh/mmalvezz/testppr/ords-dboper:latest" + ordsImagePullPolicy: "Always" + dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + replicas: 1 + sysAdminPwd: + secret: + secretName: "cdb1-secret" + key: "sysadmin_pwd" + ordsPwd: + secret: + secretName: "cdb1-secret" + key: "ords_pwd" + cdbAdminUser: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_user" + cdbAdminPwd: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_pwd" + webServerUser: + secret: + secretName: "cdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "cdb1-secret" + key: "webserver_pwd" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + diff --git a/docs/multitenant/usecase03/cdb_creation_log.txt b/docs/multitenant/usecase03/cdb_creation_log.txt new file mode 100644 index 00000000..8c7dc161 --- /dev/null +++ b/docs/multitenant/usecase03/cdb_creation_log.txt @@ -0,0 +1,336 @@ +kubectl get pods -n cdbnamespace +NAME READY STATUS RESTARTS AGE +cdb-dev-ords-rs-pgqqh 0/1 ContainerCreating 0 1s + +kubectl get pods -n cdbnamespace +NAME READY STATUS RESTARTS AGE +cdb-dev-ords-rs-pgqqh 1/1 Running 0 6s + +kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M +NOT_INSTALLED=2 + SETUP +==================================================== +CONFIG=/etc/ords/config +total 0 +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:20 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.connectionType was set to: customurl in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:21 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.customURL was set to: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:23 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: security.requestValidationFunction was set to: false in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:25 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: jdbc.MaxLimit was set to: 100 in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:27 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: jdbc.InitialLimit was set to: 50 in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:29 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: error.externalPath was set to: /opt/oracle/ords/error +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:31 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.access.log was set to: /home/oracle +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:32 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.https.port was set to: 8888 +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:34 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.https.cert was set to: /opt/oracle/ords//secrets/tls.crt +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:36 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.https.cert.key was set to: /opt/oracle/ords//secrets/tls.key +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:38 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: restEnabledSql.active was set to: true in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:40 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: security.verifySSL was set to: true +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:42 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: database.api.enabled was set to: true +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:43 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: plsql.gateway.mode was set to: disabled in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:45 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: database.api.management.services.disabled was set to: false +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:47 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: misc.pagination.maxRows was set to: 1000 in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:49 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.cdb.adminUser was set to: C##DBAPI_CDB_ADMIN AS SYSDBA in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:51 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.cdb.adminUser.password was set to: ****** in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:53 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +Created user welcome in file /etc/ords/config/global/credentials +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:55 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +Oracle REST Data Services - Non-Interactive Install + +Retrieving information.. +Completed verifying Oracle REST Data Services schema version 23.3.0.r2891830. +Connecting to database user: ORDS_PUBLIC_USER url: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) +The setting named: db.serviceNameSuffix was set to: in configuration: default +The setting named: db.username was set to: ORDS_PUBLIC_USER in configuration: default +The setting named: db.password was set to: ****** in configuration: default +The setting named: security.requestValidationFunction was set to: ords_util.authorize_plsql_gateway in configuration: default +2024-01-25T17:17:58.898Z INFO Oracle REST Data Services schema version 23.3.0.r2891830 is installed. +2024-01-25T17:17:58.900Z INFO To run in standalone mode, use the ords serve command: +2024-01-25T17:17:58.900Z INFO ords --config /etc/ords/config serve +2024-01-25T17:17:58.900Z INFO Visit the ORDS Documentation to access tutorials, developer guides and more to help you get started with the new ORDS Command Line Interface (http://oracle.com/rest). +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:18:00 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +2024-01-25T17:18:00.960Z INFO HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8080 +2024-01-25T17:18:00.963Z INFO HTTPS and HTTPS/2 listening on host: 0.0.0.0 port: 8888 +2024-01-25T17:18:00.980Z INFO Disabling document root because the specified folder does not exist: /etc/ords/config/global/doc_root +2024-01-25T17:18:00.981Z INFO Default forwarding from / to contextRoot configured. +2024-01-25T17:18:06.634Z INFO Configuration properties for: |default|lo| +db.serviceNameSuffix= +java.specification.version=21 +conf.use.wallet=true +database.api.management.services.disabled=false +sun.jnu.encoding=UTF-8 +user.region=US +java.class.path=/opt/oracle/ords/ords.war +java.vm.vendor=Oracle Corporation +standalone.https.cert.key=/opt/oracle/ords//secrets/tls.key +sun.arch.data.model=64 +nashorn.args=--no-deprecation-warning +java.vendor.url=https://java.oracle.com/ +resource.templates.enabled=false +user.timezone=UTC +java.vm.specification.version=21 +os.name=Linux +sun.java.launcher=SUN_STANDARD +user.country=US +sun.boot.library.path=/usr/java/jdk-21/lib +sun.java.command=/opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure +jdk.debug=release +sun.cpu.endian=little +user.home=/home/oracle +oracle.dbtools.launcher.executable.jar.path=/opt/oracle/ords/ords.war +user.language=en +db.cdb.adminUser.password=****** +java.specification.vendor=Oracle Corporation +java.version.date=2023-10-17 +database.api.enabled=true +java.home=/usr/java/jdk-21 +db.username=ORDS_PUBLIC_USER +file.separator=/ +java.vm.compressedOopsMode=32-bit +line.separator= + +restEnabledSql.active=true +java.specification.name=Java Platform API Specification +java.vm.specification.vendor=Oracle Corporation +java.awt.headless=true +standalone.https.cert=/opt/oracle/ords//secrets/tls.crt +db.password=****** +sun.management.compiler=HotSpot 64-Bit Tiered Compilers +security.requestValidationFunction=ords_util.authorize_plsql_gateway +misc.pagination.maxRows=1000 +java.runtime.version=21.0.1+12-LTS-29 +user.name=oracle +error.externalPath=/opt/oracle/ords/error +stdout.encoding=UTF-8 +path.separator=: +db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA +os.version=5.4.17-2136.323.8.1.el7uek.x86_64 +java.runtime.name=Java(TM) SE Runtime Environment +file.encoding=UTF-8 +plsql.gateway.mode=disabled +security.verifySSL=true +standalone.https.port=8888 +java.vm.name=Java HotSpot(TM) 64-Bit Server VM +java.vendor.url.bug=https://bugreport.java.com/bugreport/ +java.io.tmpdir=/tmp +oracle.dbtools.cmdline.ShellCommand=ords +java.version=21.0.1 +user.dir=/home/oracle/keystore +os.arch=amd64 +java.vm.specification.name=Java Virtual Machine Specification +jdbc.MaxLimit=100 +oracle.dbtools.cmdline.home=/opt/oracle/ords +native.encoding=UTF-8 +java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib +java.vendor=Oracle Corporation +java.vm.info=mixed mode, sharing +stderr.encoding=UTF-8 +java.vm.version=21.0.1+12-LTS-29 +sun.io.unicode.encoding=UnicodeLittle +jdbc.InitialLimit=50 +db.connectionType=customurl +java.class.version=65.0 +db.customURL=jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) +standalone.access.log=/home/oracle + +2024-01-25T17:18:10.381Z INFO + +Mapped local pools from /etc/ords/config/databases: + /ords/ => default => VALID + + +2024-01-25T17:18:10.532Z INFO Oracle REST Data Services initialized +Oracle REST Data Services version : 23.3.0.r2891830 +Oracle REST Data Services server info: jetty/10.0.17 +Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 21.0.1+12-LTS-29 + + +exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash +[oracle@cdb-dev-ords-rs-pgqqh ~]$ ps -ef|grep java +oracle 1147 1116 10 17:17 ? 00:00:21 /usr/java/jdk-21/bin/java -Doracle.dbtools.cmdline.home=/opt/oracle/ords -Duser.language=en -Duser.region=US -Dfile.encoding=UTF-8 -Djava.awt.headless=true -Dnashorn.args=--no-deprecation-warning -Doracle.dbtools.cmdline.ShellCommand=ords -Duser.timezone=UTC -jar /opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure +oracle 1227 1200 0 17:21 pts/0 00:00:00 grep --color=auto java diff --git a/docs/multitenant/usecase03/cdb_secret.yaml b/docs/multitenant/usecase03/cdb_secret.yaml new file mode 100644 index 00000000..8f1b6fc9 --- /dev/null +++ b/docs/multitenant/usecase03/cdb_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: cdb1-secret + namespace: cdbnamespace +type: Opaque +data: + ords_pwd: "[...base64 encoded password...]" + sysadmin_pwd: "[...base64 encoded password...]" + cdbadmin_user: "[...base64 encoded password...]" + cdbadmin_pwd: "[...base64 encoded password...]" + webserver_user: "[...base64 encoded password...]" + webserver_pwd: "[...base64 encoded password...]" diff --git a/docs/multitenant/usecase03/gentlscert.sh b/docs/multitenant/usecase03/gentlscert.sh new file mode 100644 index 00000000..49e29147 --- /dev/null +++ b/docs/multitenant/usecase03/gentlscert.sh @@ -0,0 +1,23 @@ +#!/bin/bash +export CDB_NAMESPACE=cdbnamespace +export PDB_NAMESPACE=pdbnamespace +export OPR_NAMESPACE=oracle-database-operator-system +export SKEY=tls.key +export SCRT=tls.crt +export CART=ca.crt +export COMPANY=oracle +export REST_SERVER=ords + +openssl genrsa -out ca.key 2048 +openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt +openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr +echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt +openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} + +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} + diff --git a/docs/multitenant/usecase03/makefile b/docs/multitenant/usecase03/makefile new file mode 100644 index 00000000..af52316f --- /dev/null +++ b/docs/multitenant/usecase03/makefile @@ -0,0 +1,286 @@ +# __ __ _ __ _ _ +# | \/ | __ _| | _____ / _(_) | ___ +# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ +# | | | | (_| | < __/ _| | | __/ +# |_| |_|\__,_|_|\_\___|_| |_|_|\___| +# +# ___ +# / _ \ _ __ _ __ _ __ ___ _ __ ___ +# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ +# | |_| | | | | |_) | | | __/ | | | | | +# \___/|_| |_| .__/|_| \___|_| |_| |_| +# |_| +# ____ _ _ _ +# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ +# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| +# | |__| (_) | | | | |_| | | (_) | | | __/ | +# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| +# +# +# This makefile helps to speed up the kubectl commands executions to deploy and test +# the mutlitenant operator. Although it has few functionality you can adapt to your needs +# by adding much more targets. +# +# Quick start: +# ~~~~~~~~~~~ +# +# - Copy files of tab.1 in the makefile directory. +# - Edit the secret files and other yaml files with the correct credential as +# specified in the documentation. +# - Edit makefile updating variables of tab.2 +# - Execute commands of tab.3 "make step1" "make step2" "make step3".... +# +# Tab.1 - List of required files +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# |oracle-database-operator.yaml| Opertaor yaml file | +# +-----------------------------+---------------------------------------------+ +# |cdb_secret.yaml | Secret file for the rest server pod | +# +-----------------------------+---------------------------------------------+ +# |pdb_secret.yaml | Secret file for the pdb creation | +# +-----------------------------+---------------------------------------------+ +# |cdb_create.yaml | Rest server pod creation | +# +-----------------------------+---------------------------------------------+ +# |pdb_create.yaml | Pluggable database creation | +# +-----------------------------+---------------------------------------------+ +# |oracle-database-operator.yaml| Database operator | +# +-----------------------------+---------------------------------------------+ +# |Dockerfiles | Dockerfile for CBD | +# +-----------------------------+---------------------------------------------+ +# |runOrdsSSL.sh | Init script executed by Dockerfile | +# +-----------------------------+---------------------------------------------+ +# +# Tab.2 - List of variables +# ~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# |OCIR | Your image registry | +# +-----------------------------+---------------------------------------------+ +# |OCIRPATH | Path of the image in your registry | +# +-----------------------------+---------------------------------------------+ +# +# Tab.3 - Execution steps +# ~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# | MAKEFILE TARGETS LIST | +# | ----- ooo ----- | +# | - TARGET - - DESCRIPTION - | +# +-----------------------------+-------------------------------------+-------+ +# |step1 | Build rest server images | | +# +-----------------------------+-------------------------------------+ REST | +# |step2 | Tag the immages | SRV | +# +-----------------------------+-------------------------------------+ IMG | +# |step3 | Push the image into the repository | | +# +-----------------------------+-------------------------------------+-------+ +# |step4 | Load webhook certmanager | DB | +# +-----------------------------+-------------------------------------+ OPER | +# |step5 | Create the db operator | | +# +-----------------------------+-------------------------------------+-------+ +# |step6 | Create tls certificates | T | +# +-----------------------------+-------------------------------------+ L | +# |step7 | Create tls secret | S | +# +-----------------------------+---------------------------------------------+ +# |step8 | Create database secrets | +# +-----------------------------+---------------------------------------------+ +# |step9 | Create restserver pod | +# | | +---------------------------------------------+ +# | +---> checkstep9 | Monitor the executions | +# +-----------------------------+---------------------------------------------+ +# |step10 | Create pluggable database | +# | | +---------------------------------------------+ +# | +---> checkpdb | Monitor PDB status | +# +-----------------------------+---------------------------------------------+ +# | DIAGNOSTIC TARGETS | +# +-----------------------------+---------------------------------------------+ +# | dump | Dump pods info into a file | +# +-----------------------------+---------------------------------------------+ +# | reloadop | Reload the db operator | +# +-----------------------------+---------------------------------------------+ +# | login | Login into cdb pod | +# +-----------------------------+---------------------------------------------+ + + +################ TAB 2 VARIABLES ############ +REST_SERVER=ords +ORDSVERSION=latest + +OCIR=[container registry] +OCIRPATH=$(REST_SERVER)-dboper:$(ORDSVERSION) + +#examples: +#OCIR=lin.ocir.io +#OCIRPATH=/sampletenancy/samplepath/sampledir/$(REST_SERVER)-dboper:$(ORDSVERSION) +############################################# +DOCKER=/usr/bin/docker +KUBECTL=/usr/bin/kubectl +ORDS=/usr/local/bin/ords +CONFIG=/etc/ords/config +IMAGE=oracle/$(REST_SERVER)-dboper:$(ORDSVERSION) +DBOPERATOR=oracle-database-operator.yaml +URLPATH=/_/db-api/stable/database/pdbs/ +OPENSSL=/usr/bin/openssl +ORDSPORT=8888 +MAKE=/usr/bin/make +DOCKERFILE=Dockerfile +RM=/usr/bin/rm +ECHO=/usr/bin/echo +CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml +CDB_SECRET_YAML=cdb_secret.yaml +PDB_SECRET_YAML=pdb_secret.yaml +TDE_SECRET_YAML=tde_secret.yaml +CDB_NAMESPACE_YAML=ns_namespace_cdb.yaml +PDB_NAMESPACE_YAML=ns_namespace_pdb.yaml +OPR_NAMESPACE=oracle-database-operator-system +PDB_NAMESPACE=$(shell grep namespace $(PDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') +CDB_NAMESPACE=$(shell grep namespace $(CDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') +CDB=cdb_create.yaml +PDB=pdb_create.yaml +SKEY=tls.key +SCRT=tls.crt +CART=ca.crt +COMPANY=oracle + +step1: createimage +step2: tagimage +step3: push +step4: certmanager +step5: dboperator +step6: tlscert +step7: tlssecret +step8: dbsecret +step9: cdb +step10: pdb + +checkstep9: checkcdb + + +createimage: + @echo "BUILDING CDB IMAGES" + @if [[ ! -f ./Dockerfile ]]; \ + then\ + echo "DOCKERFILE DOES NOT EXISTS";\ + exit 1; \ + fi; + @if [[ ! -f ../runOrdsSSL.sh ]]; \ + then\ + echo "DOCKERFILE DOES NOT EXISTS";\ + exit 1; \ + fi; + $(DOCKER) build -t $(IMAGE) . + +tagimage: + @echo "TAG IMAGE" + $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) + +push: + @echo "PUSH IMAGE INTO THE REGISTRY" + $(DOCKER) push $(OCIR)$(OCIRPATH) + +certmanager: + @echo "WEBHOOK CERT MANAGER" + $(KUBECTL) apply -f $(CERTMANAGER) + +dboperator: + @echo "ORACLE DATABASE OPERATOR" + $(KUBECTL) apply -f $(DBOPERATOR) + +namespace: + $(KUBECTL) get namespaces + $(KUBECTL) apply -f $(CDB_NAMESPACE_YAML) + $(KUBECTL) apply -f $(PDB_NAMESPACE_YAML) + $(KUBECTL) get namespaces + +tlscert: + @echo "CREATING TLS CERTIFICATES" + $(OPENSSL) genrsa -out ca.key 2048 + $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(CDB_NAMESPACE)" -out server.csr + $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(CDB_NAMESPACE),DNS:www.example.com" > extfile.txt + $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) + +tlssecret: + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDB_NAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(CDB_NAMESPACE) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDB_NAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDB_NAMESPACE) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPR_NAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPR_NAMESPACE) + + +dbsecret: + @echo "CREATING DB SECRETS" + $(KUBECTL) apply -f $(CDB_SECRET_YAML) + $(KUBECTL) apply -f $(PDB_SECRET_YAML) + $(KUBECTL) apply -f $(TDE_SECRET_YAML) + + +cdb: + @echo "CREATING REST SRV POD" + $(KUBECTL) apply -f $(CDB) + +checkcdb: + $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) + +pdb: + $(KUBECTL) apply -f $(PDB) + +checkpdb: + $(KUBECTL) get pdbs -n $(OPR_NAMESPACE) + +dump: + @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) + @$(eval DIAGFILE := ./opdmp.$(TMPSP)) + @>$(DIAGFILE) + @echo "OPERATOR DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) + @echo "CDB LOG DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs `$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) + @echo "SECRET DMP" >>$(DIAGFILE) + @echo "~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) get secrets -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) + @echo "CDB/PDB DMP" >> $(DIAGFILE) + $(KUBECTL) get pdbs -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) + $(KUBECTL) get cdb -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) + @echo "CLUSTER INFO" >> $(DIAGFILE) + $(KUBECTL) get nodes -o wide + $(KUBECTL) get svc --namespace=kube-system + +reloadop: + echo "RESTARTING OPERATOR" + $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) + $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) + $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) + $(KUBECTL) get pod $(OP1) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP2) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP3) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - + +login: + $(KUBECTL) exec -it `$(KUBECTL) get pods -n $(CDB_NAMESPACE) |grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) bash + +cdblog: + $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) + + + +xlog1: + $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) + +xlog2: + $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) + +xlog3: + $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) + +checkdep: + $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(OPR_NAMESPACE) + $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(CBD_NAMESPACE) + $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(PDB_NAMESPACE) + + + diff --git a/docs/multitenant/usecase03/operator_creation_log.txt b/docs/multitenant/usecase03/operator_creation_log.txt new file mode 100644 index 00000000..36ed02ac --- /dev/null +++ b/docs/multitenant/usecase03/operator_creation_log.txt @@ -0,0 +1,27 @@ +kubectl apply -f oracle-database-operator.yaml +namespace/oracle-database-operator-system created +customresourcedefinition.apiextensions.k8s.io/autonomouscontainerdatabases.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/autonomousdatabasebackups.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/autonomousdatabaserestores.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/autonomousdatabases.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/cdbs.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/dataguardbrokers.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/dbcssystems.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/oraclerestdataservices.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/pdbs.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/shardingdatabases.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/singleinstancedatabases.database.oracle.com configured +role.rbac.authorization.k8s.io/oracle-database-operator-leader-election-role created +clusterrole.rbac.authorization.k8s.io/oracle-database-operator-manager-role created +clusterrole.rbac.authorization.k8s.io/oracle-database-operator-metrics-reader created +clusterrole.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-role created +rolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-leader-election-rolebinding created +clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-manager-rolebinding created +clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-rolebinding created +service/oracle-database-operator-controller-manager-metrics-service created +service/oracle-database-operator-webhook-service created +certificate.cert-manager.io/oracle-database-operator-serving-cert created +issuer.cert-manager.io/oracle-database-operator-selfsigned-issuer created +mutatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-mutating-webhook-configuration created +validatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-validating-webhook-configuration created +deployment.apps/oracle-database-operator-controller-manager created diff --git a/docs/multitenant/usecase03/pdb_create.yaml b/docs/multitenant/usecase03/pdb_create.yaml new file mode 100644 index 00000000..200f3712 --- /dev/null +++ b/docs/multitenant/usecase03/pdb_create.yaml @@ -0,0 +1,46 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + fileNameConversions: "NONE" + tdeImport: false + totalSize: "1G" + tempSize: "100M" + action: "Create" + diff --git a/docs/multitenant/usecase03/pdb_creation_log.txt b/docs/multitenant/usecase03/pdb_creation_log.txt new file mode 100644 index 00000000..71d0eb4f --- /dev/null +++ b/docs/multitenant/usecase03/pdb_creation_log.txt @@ -0,0 +1,6 @@ +kubectl apply -f pdb_create.yaml +pdb.database.oracle.com/pdb1 created + +kubectl get pdbs -n pdbnamespace +NAME CONNECT_STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE +pdb1 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) DB12 pdbdev READ WRITE 0.78G Ready Success diff --git a/docs/multitenant/usecase03/pdb_secret.yaml b/docs/multitenant/usecase03/pdb_secret.yaml new file mode 100644 index 00000000..f1dfdac6 --- /dev/null +++ b/docs/multitenant/usecase03/pdb_secret.yaml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: pdb1-secret + namespace: pdbnamespace +type: Opaque +data: + sysadmin_user: "[...base64 encoded password...]" + sysadmin_pwd: "[...base64 encoded password...]" + webserver_user: "[...base64 encoded password...]" + webserver_pwd: "[...base64 encoded password...]" + diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index ec713d8a..1838ad9f 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -1773,6 +1773,9 @@ spec: cdbName: description: Name of the CDB type: string + cdbNamespace: + description: CDB Namespace + type: string cdbResName: description: Name of the CDB Custom Resource that runs the ORDS container type: string @@ -1925,6 +1928,40 @@ spec: unlimitedStorage: description: Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. type: boolean + webServerPwd: + description: Password for the Web ServerPDB User + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object xmlFileName: description: XML metadata filename to be used for Plug or Unplug operations type: string From 691e78b14172771c37f6e31732cf0b3ee4160fd1 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Wed, 6 Mar 2024 13:19:10 +0000 Subject: [PATCH 041/414] rm database.oracle.com_CDBS.yam and database.oracle.com_PDBS.yaml --- .../crd/bases/database.oracle.com_CDBS.yaml | 270 ------------- .../crd/bases/database.oracle.com_PDBS.yaml | 378 ------------------ 2 files changed, 648 deletions(-) delete mode 100644 config/crd/bases/database.oracle.com_CDBS.yaml delete mode 100644 config/crd/bases/database.oracle.com_PDBS.yaml diff --git a/config/crd/bases/database.oracle.com_CDBS.yaml b/config/crd/bases/database.oracle.com_CDBS.yaml deleted file mode 100644 index f3ee22ee..00000000 --- a/config/crd/bases/database.oracle.com_CDBS.yaml +++ /dev/null @@ -1,270 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: CDBS.database.oracle.com -spec: - group: database.oracle.com - names: - kind: CDB - listKind: CDBList - plural: CDBS - singular: cdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: CDB is the Schema for the cdbs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: CDBSpec defines the desired state of CDB - properties: - cdbAdminPwd: - description: Password for the CDB Administrator to manage PDB lifecycle - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - description: User in the root container with sysdba priviledges to - manage PDB lifecycle - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - description: Name of the CDB - type: string - cdbTlsCrt: - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - description: DB server port - type: integer - dbServer: - description: Name of the DB server - type: string - dbTnsurl: - type: string - nodeSelector: - additionalProperties: - type: string - description: Node Selector for running the Pod - type: object - ordsImage: - description: ORDS Image Name - type: string - ordsImagePullPolicy: - description: ORDS Image Pull Policy - enum: - - Always - - Never - type: string - ordsImagePullSecret: - description: The name of the image pull secret in case of a private - docker repository. - type: string - ordsPort: - description: ORDS server port. For now, keep it as 8888. TO BE USED - IN FUTURE RELEASE. - type: integer - ordsPwd: - description: Password for user ORDS_PUBLIC_USER - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - description: Number of ORDS Containers to create - type: integer - serviceName: - description: Name of the CDB Service - type: string - sysAdminPwd: - description: Password for the CDB System Administrator - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - description: Password for the Web Server User - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - description: CDBStatus defines the observed state of CDB - properties: - msg: - description: Message - type: string - phase: - description: Phase of the CDB Resource - type: string - status: - description: CDB Resource Status - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_PDBS.yaml b/config/crd/bases/database.oracle.com_PDBS.yaml deleted file mode 100644 index 804c72aa..00000000 --- a/config/crd/bases/database.oracle.com_PDBS.yaml +++ /dev/null @@ -1,378 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: PDBS.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB - listKind: PDBList - plural: PDBS - singular: pdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: PDB is the Schema for the pdbs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: PDBSpec defines the desired state of PDB - properties: - action: - description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. - Map is used to map a Databse PDB to a Kubernetes PDB CR.' - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - description: The administrator username for the new PDB. This property - is required when the Action property is Create. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - description: The administrator password for the new PDB. This property - is required when the Action property is Create. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - description: Indicate if 'AS CLONE' option should be used in the command - to plug in a PDB. This property is applicable when the Action property - is PLUG but not required. - type: boolean - cdbName: - description: Name of the CDB - type: string - cdbNamespace: - description: CDB Namespace - type: string - cdbResName: - description: Name of the CDB Custom Resource that runs the ORDS container - type: string - copyAction: - description: To copy files or not while cloning a PDB - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - description: Specify if datafiles should be removed or not. The value - can be INCLUDING or KEEP (default). - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - description: Relevant for Create and Plug operations. As defined in - the Oracle Multitenant Database documentation. Values can be a - filename convert pattern or NONE. - type: string - getScript: - description: Whether you need the script only or execute the script - type: boolean - modifyOption: - description: Extra options for opening and closing a PDB - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - description: The name of the new PDB. Relevant for both Create and - Plug Actions. - type: string - pdbState: - description: The target state of the PDB - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - description: Whether to reuse temp file - type: boolean - sourceFileNameConversions: - description: This property is required when the Action property is - Plug. As defined in the Oracle Multitenant Database documentation. - Values can be a source filename convert pattern or NONE. - type: string - sparseClonePath: - description: A Path specified for sparse clone snapshot copy. (Optional) - type: string - srcPdbName: - description: Name of the Source PDB from which to clone - type: string - tdeExport: - description: TDE export for unplug operations - type: boolean - tdeImport: - description: TDE import for plug operations - type: boolean - tdeKeystorePath: - description: TDE keystore path is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. - type: string - tdePassword: - description: TDE password if the tdeImport or tdeExport flag is set - to true. Can be used in create, plug or unplug operations - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - description: TDE secret is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - description: Relevant for Create and Clone operations. Total size - for temporary tablespace as defined in the Oracle Multitenant Database - documentation. See size_clause description in Database SQL Language - Reference documentation. - type: string - totalSize: - description: Relevant for create and plug operations. Total size as - defined in the Oracle Multitenant Database documentation. See size_clause - description in Database SQL Language Reference documentation. - type: string - unlimitedStorage: - description: Relevant for Create and Plug operations. True for unlimited - storage. Even when set to true, totalSize and tempSize MUST be specified - in the request if Action is Create. - type: boolean - webServerPwd: - description: Password for the Web ServerPDB User - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - description: XML metadata filename to be used for Plug or Unplug operations - type: string - required: - - action - type: object - status: - description: PDBStatus defines the observed state of PDB - properties: - action: - description: Last Completed Action - type: string - connString: - description: PDB Connect String - type: string - modifyOption: - description: Modify Option of the PDB - type: string - msg: - description: Message - type: string - openMode: - description: Open mode of the PDB - type: string - phase: - description: Phase of the PDB Resource - type: string - status: - description: PDB Resource Status - type: boolean - totalSize: - description: Total size of the PDB - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] From c6b17330cbdae58c814e408c2704b389691fd3ba Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Thu, 7 Mar 2024 06:15:05 +0000 Subject: [PATCH 042/414] Idesai bugfix release v1.1 --- .../singleinstancedatabase_webhook.go | 23 ++++--------------- commons/database/utils.go | 8 +++++-- .../sidb/singleinstancedatabase_free.yaml | 2 +- .../singleinstancedatabase_controller.go | 10 +++++--- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 89773040..9779b018 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -261,25 +261,10 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { field.Invalid(field.NewPath("spec").Child("pdbName"), r.Spec.Pdbname, "Free edition PDB must be FREEPDB1")) } - if r.Spec.InitParams.CpuCount != 0 { - allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("initParams").Child("cpuCount"), r.Spec.InitParams.CpuCount, - r.Spec.Edition+" edition does not support changing init parameter cpuCount.")) - } - if r.Spec.InitParams.Processes != 0 { - allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("initParams").Child("processes"), r.Spec.InitParams.Processes, - r.Spec.Edition+" edition does not support changing init parameter process.")) - } - if r.Spec.InitParams.SgaTarget != 0 { - allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("initParams").Child("sgaTarget"), r.Spec.InitParams.SgaTarget, - r.Spec.Edition+" edition does not support changing init parameter sgaTarget.")) - } - if r.Spec.InitParams.PgaAggregateTarget != 0 { + if r.Spec.InitParams != nil { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("initParams").Child("pgaAggregateTarget"), r.Spec.InitParams.PgaAggregateTarget, - r.Spec.Edition+" edition does not support changing init parameter pgaAggregateTarget.")) + field.Invalid(field.NewPath("spec").Child("initParams"), *r.Spec.InitParams, + r.Spec.Edition+" edition does not support changing init parameters")) } } else { if r.Spec.Sid == "XE" { @@ -294,7 +279,7 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { } } - if r.Spec.CreateAs != "clone" { + if r.Spec.CreateAs == "clone" { if r.Spec.Image.PrebuiltDB { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("createAs"), r.Spec.CreateAs, diff --git a/commons/database/utils.go b/commons/database/utils.go index ec3a6fe4..dd0e54a9 100644 --- a/commons/database/utils.go +++ b/commons/database/utils.go @@ -791,10 +791,14 @@ func GetWatchNamespaces() map[string]bool { // Fetching the allowed namespaces from env variables var watchNamespaceEnvVar = "WATCH_NAMESPACE" ns, _ := os.LookupEnv(watchNamespaceEnvVar) - values := strings.Split(strings.TrimSpace(ns), ",") + ns = strings.TrimSpace(ns) namespaces := make(map[string]bool) + if len(ns) == 0 { + return namespaces + } + namespacesArr := strings.Split(ns, ",") // put slice values into map - for _, s := range values { + for _, s := range namespacesArr { namespaces[s] = true } return namespaces diff --git a/config/samples/sidb/singleinstancedatabase_free.yaml b/config/samples/sidb/singleinstancedatabase_free.yaml index 558bd8f4..6dd0aa39 100644 --- a/config/samples/sidb/singleinstancedatabase_free.yaml +++ b/config/samples/sidb/singleinstancedatabase_free.yaml @@ -35,5 +35,5 @@ spec: storageClass: "oci-bv" accessMode: "ReadWriteOnce" - ## Count of Database Pods. Should be 1 for express edition. + ## Count of Database Pods. Should be 1 for free edition. replicas: 1 \ No newline at end of file diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 42fb66ff..d713ea8e 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -2863,15 +2863,19 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc // Needs to restart the Non Ready Pods ( Delete old ones and create new ones ) if m.Status.FlashBack == strconv.FormatBool(false) && flashBackStatus { - // call FindPods() to fetch pods all version/images of the same SIDB kind + // // call FindPods() to fetch pods all version/images of the same SIDB kind readyPod, replicasFound, available, _, err := dbcommons.FindPods(r, "", "", m.Name, m.Namespace, ctx, req) if err != nil { log.Error(err, err.Error()) return requeueY, err } - // delete non ready Pods as flashback needs restart of pods + // delete non ready Pods as flashback needs restart of pods to make sure failover works in sidbs with multiple replicas _, err = r.deletePods(ctx, req, m, available, readyPod, replicasFound, 1) - return requeueY, err + if err != nil { + log.Error(err, err.Error()) + return requeueY, err + } + return requeueN, err } m.Status.FlashBack = strconv.FormatBool(flashBackStatus) From 34634ac71a0d654a1ad1195695abbc73dd412631 Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Fri, 8 Mar 2024 19:59:37 +0000 Subject: [PATCH 043/414] Jyoti sharding docs changes --- README.md | 61 +++++++++++-- docs/sharding/README.md | 52 ++++++++--- .../create_kubernetes_secret_for_db_user.md | 62 +++++++++---- docs/sharding/provisioning/oraclesi.yaml | 27 +++--- .../provisioning/oraclesi_pvc_commented.yaml | 58 ++++++------ ..._persistent_volume_having_db_gold_image.md | 2 +- docs/sharding/provisioning/shard_prov.yaml | 46 ---------- .../provisioning/shard_prov_clone.yaml | 64 ------------- .../shard_prov_clone_across_ads.yaml | 72 --------------- .../shard_prov_send_notification.yaml | 75 ---------------- ..._cloning_db_from_gold_image_across_ads.md} | 25 +++--- ...ng_by_cloning_db_gold_image_in_same_ad.md} | 25 +++--- ...provisioning_with_control_on_resources.md} | 20 +++-- ...th_notification_using_oci_notification.md} | 26 +++--- ...ing_provisioning_without_db_gold_image.md} | 20 +++-- ...ding_scale_in_delete_an_existing_shard.md} | 20 +++-- .../ssharding_scale_out_add_shards.md} | 24 ++--- .../system_sharding/ssharding_shard_prov.yaml | 58 ++++++++++++ .../ssharding_shard_prov_clone.yaml | 82 +++++++++++++++++ ...ssharding_shard_prov_clone_across_ads.yaml | 82 +++++++++++++++++ .../ssharding_shard_prov_delshard.yaml | 68 ++++++++++++++ .../ssharding_shard_prov_extshard.yaml | 67 ++++++++++++++ .../ssharding_shard_prov_memory_cpu.yaml} | 51 +++++++---- ...sharding_shard_prov_send_notification.yaml | 85 ++++++++++++++++++ ...y_cloning_db_from_gold_image_across_ads.md | 53 +++++++++++ ...ing_by_cloning_db_gold_image_in_same_ad.md | 49 ++++++++++ ..._provisioning_with_control_on_resources.md | 42 +++++++++ ...ith_notification_using_oci_notification.md | 82 +++++++++++++++++ ...ding_provisioning_without_db_gold_image.md | 37 ++++++++ ...rding_scale_in_delete_an_existing_shard.md | 47 ++++++++++ .../udsharding_scale_out_add_shards.md | 34 +++++++ .../udsharding_shard_prov.yaml | 59 ++++++++++++ .../udsharding_shard_prov_clone.yaml | 83 +++++++++++++++++ ...dsharding_shard_prov_clone_across_ads.yaml | 83 +++++++++++++++++ .../udsharding_shard_prov_delshard.yaml} | 50 +++++++---- .../udsharding_shard_prov_extshard.yaml} | 49 ++++++---- .../udsharding_shard_prov_memory_cpu.yaml | 90 +++++++++++++++++++ ...sharding_shard_prov_send_notification.yaml | 85 ++++++++++++++++++ 38 files changed, 1557 insertions(+), 458 deletions(-) delete mode 100644 docs/sharding/provisioning/shard_prov.yaml delete mode 100644 docs/sharding/provisioning/shard_prov_clone.yaml delete mode 100644 docs/sharding/provisioning/shard_prov_clone_across_ads.yaml delete mode 100644 docs/sharding/provisioning/shard_prov_send_notification.yaml rename docs/sharding/provisioning/{provisioning_by_cloning_db_from_gold_image_across_ads.md => system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md} (57%) rename docs/sharding/provisioning/{provisioning_by_cloning_db_gold_image_in_same_ad.md => system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md} (54%) rename docs/sharding/provisioning/{provisioning_with_control_on_resources.md => system_sharding/ssharding_provisioning_with_control_on_resources.md} (58%) rename docs/sharding/provisioning/{provisioning_with_notification_using_oci_notification.md => system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md} (71%) rename docs/sharding/provisioning/{provisioning_without_db_gold_image.md => system_sharding/ssharding_provisioning_without_db_gold_image.md} (59%) rename docs/sharding/provisioning/{scale_in_delete_an_existing_shard.md => system_sharding/ssharding_scale_in_delete_an_existing_shard.md} (56%) rename docs/sharding/provisioning/{scale_out_add_shards.md => system_sharding/ssharding_scale_out_add_shards.md} (51%) create mode 100644 docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml create mode 100644 docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml create mode 100644 docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml create mode 100644 docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml create mode 100644 docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml rename docs/sharding/provisioning/{shard_prov_memory_cpu.yaml => system_sharding/ssharding_shard_prov_memory_cpu.yaml} (60%) create mode 100644 docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml rename docs/sharding/provisioning/{shard_prov_extshard.yaml => user-defined-sharding/udsharding_shard_prov_delshard.yaml} (51%) rename docs/sharding/provisioning/{shard_prov_delshard.yaml => user-defined-sharding/udsharding_shard_prov_extshard.yaml} (50%) create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml create mode 100644 docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml diff --git a/README.md b/README.md index 20f7b7da..81de9285 100644 --- a/README.md +++ b/README.md @@ -61,15 +61,64 @@ Oracle strongly recommends that you ensure your system meets the following [Prer kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml ``` -## Quick Install of the Operator +* ### Role Binding for access management - To install the operator in the cluster quickly, you can use a single [oracle-database-operator.yaml](https://github.com/oracle/oracle-database-operator/blob/main/oracle-database-operator.yaml) file. + When the Oracle DB Operator is dealing with multiple namespaces and access management is required, you need to complete additional steps: + - To have the Oralce DB Operator monitor only certain defined namespaces which you want it to monitor. + - To apply role binding for those namespaces for access control. - Run the following command + Follow the below steps: - ```sh - kubectl apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/main/oracle-database-operator.yaml - ``` + 1. Download the [oracle-database-operator.yaml](https://github.com/oracle/oracle-database-operator/blob/main/oracle-database-operator.yaml) file. + 2. Add the comma separated namespaces under `WATCH_NAMESPACE`. For example: + ```sh + - name: WATCH_NAMESPACE + value: "oracle-database-operator-system,shns" + ``` + This is needed when you want the DB Operator to monitor the namespaces `shns` and `oracle-database-operator-system`. + + If you are going to work with more namespaces, then you will need to add them under `WATCH_NAMESPACE`. + + Save the `oracle-database-operator-system.yaml` file after this change. + 3. Create a role binding file for the namespace you are going to work in. + + In this case, its the namespace `shns` and you need to create a file like below for it: + + ```sh + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 + namespace: shns + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role + subjects: + - kind: ServiceAccount + name: default + namespace: oracle-database-operator-system + ``` + + If you are testing with multiple namespaces, make sure you create that many binding files and apply them before applying `oracle-database-operator.yaml`. + + NOTE: You need to change the namespace in that binding file from `shns` and also change the binding name `oracle-database-operator-oracle-database-operator-manager-rolebinding1` to a new binding name. + + 4. Apply the role binding file like below: + ```sh + kubectl apply -f shns_binding.yaml + ``` + +## Install Oracle DB Operator + + Once the above prerequisite changes have been done, to install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from above step. + + Run the following command + + ```sh + kubectl apply -f oracle-database-operator.yaml + ``` Ensure that the operator pods are up and running. For high availability, Operator pod replicas are set to a default of 3. You can scale this setting up or down. diff --git a/docs/sharding/README.md b/docs/sharding/README.md index beb155d6..972dc55a 100644 --- a/docs/sharding/README.md +++ b/docs/sharding/README.md @@ -35,7 +35,7 @@ To create a Sharding Topology, complete the steps in the following sections belo ## Prerequsites for Running Oracle Sharding Database Controller -**IMPORTANT :** You must make the changes specified in this section before you proceed to the next section. +**IMPORTANT:** You must make the changes specified in this section before you proceed to the next section. ### 1. Kubernetes Cluster: To deploy Oracle Sharding database controller with Oracle Database Operator, you need a Kubernetes Cluster which can be one of the following: @@ -46,7 +46,9 @@ To use Oracle Sharding Database Controller, ensure that your system is provision ### 2. Deploy Oracle Database Operator -To deploy Oracle Database Operator in a Kubernetes cluster, go to the section [Quick Install of the Operator](../../README.md#oracle-database-kubernetes-operator-deployment) in the README, and complete the operator deployment before you proceed further. If you have already deployed the operator, then proceed to the next section. +To deploy Oracle Database Operator in a Kubernetes cluster, go to the section [Install Oracle DB Operator](../../README.md#install-oracle-db-operator) in the README, and complete the operator deployment before you proceed further. If you have already deployed the operator, then proceed to the next section. + +**IMPORTANT:** Make sure you have completed the steps for [Role Binding for access management](../../README.md#role-binding-for-access-management) as well before installing the Oracle DB Operator. ### 3. Oracle Database and Global Data Services Docker Images Choose one of the following deployment options: @@ -84,22 +86,46 @@ You can either download the images and push them to your Docker Images Repositor ### 5. Create a Kubernetes secret for the database installation owner for the database Sharding Deployment -Create a Kubernetes secret named `db-user-pass` using these steps: [Create Kubernetes Secret](./provisioning/create_kubernetes_secret_for_db_user.md) +Create a Kubernetes secret named `db-user-pass-rsa` using these steps: [Create Kubernetes Secret](./provisioning/create_kubernetes_secret_for_db_user.md) After you have the above prerequsites completed, you can proceed to the next section for your environment to provision the Oracle Database Sharding Topology. -## Provisioning Sharding Topology in a Cloud-Based Kubernetes Cluster (OKE in this case) +### 6. Provisioning a Persistent Volume having an Oracle Database Gold Image + +This step is needed when you want to provision a Persistent Volume having an Oracle Database Gold Image for Database Cloning. + +In case of an `OCI OKE` cluster, you can use this Persistent Volume during provisioning Shard Databases by cloning in the same Availability Domain or you can use a Full Backup of this Persistent Volume during provisioning Shard Databases by cloning in different Availability Domains. + +You can refer [here](./provisioning/provisioning_persistent_volume_having_db_gold_image.md) for the steps involved. + +## Provisioning Sharding Topology with System Sharding in a Cloud-Based Kubernetes Cluster + +Deploy Oracle Database Sharding Topology with `System Sharding` on your Cloud based Kubernetes cluster. + +In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database Sharding Topology covered by below examples: + +[1. Provisioning Oracle Sharded Database with System Sharding without Database Gold Image](./provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md) +[2. Provisioning Oracle Sharded Database with System Sharding with additional control on resources like Memory and CPU allocated to Pods](./provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md) +[3. Provisioning Oracle Sharded Database with System Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) +[4. Provisioning Oracle Sharded Database with System Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) +[5. Provisioning Oracle Sharded Database with System Sharding and send Notification using OCI Notification Service](./provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md) +[6. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System Sharding](./provisioning/system_sharding/ssharding_scale_out_add_shards.md) +[7. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System Sharding](./provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md) + + +## Provisioning Sharding Topology with User Defined Sharding in a Cloud-Based Kubernetes Cluster + +Deploy Oracle Database Sharding Topology with `User Defined Sharding` on your Cloud based Kubernetes cluster. -Deploy Oracle Database sharding topology on your Cloud based Kubernetes cluster. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database sharding topology. +In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database Sharding Topology covered by below examples: -[1. Provisioning Oracle Database sharding topology without Database Gold Image](./provisioning/provisioning_without_db_gold_image.md) -[2. Provisioning Oracle Database sharding topology with additional control on resources like Memory and CPU allocated to Pods](./provisioning/provisioning_with_control_on_resources.md) -[3. Provisioning a Persistent Volume having an Oracle Database Gold Image](./provisioning/provisioning_persistent_volume_having_db_gold_image.md) -[4. Provisioning Oracle Database sharding topology by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/provisioning_by_cloning_db_gold_image_in_same_ad.md) -[5. Provisioning Oracle Database sharding topology by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/provisioning_by_cloning_db_from_gold_image_across_ads.md) -[6. Provisioning Oracle Database sharding topology and send Notification using OCI Notification Service](./provisioning/provisioning_with_notification_using_oci_notification.md) -[7. Scale Out - Add Shards to an existing Oracle Database Sharding Topology](./provisioning/scale_out_add_shards.md) -[8. Scale In - Delete an existing Shard from a working Oracle Database sharding topology](./provisioning/scale_in_delete_an_existing_shard.md) +[1. Provisioning Oracle Sharded Database with User Defined Sharding without Database Gold Image](./provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md) +[2. Provisioning Oracle Sharded Database with User Defined Sharding with additional control on resources like Memory and CPU allocated to Pods](./provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md) +[3. Provisioning Oracle Sharded Database with User Defined Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) +[4. Provisioning Oracle Sharded Database with User Defined Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) +[5. Provisioning Oracle Sharded Database with User Defined Sharding and send Notification using OCI Notification Service](./provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md) +[6. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md) +[7. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md) ## Connecting to Shard Databases diff --git a/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md b/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md index 0d66f49b..b67a6120 100644 --- a/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md +++ b/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md @@ -1,25 +1,49 @@ # Create kubernetes secret for db user -Create a Kubernetes secret named "db-user-pass" using a password in a text file and then encrypt it using an `openssl` key. The text file will be removed after secret is created. +Below are the steps to create an encrypted file with a password for the DB User: -```sh -mkdir /tmp/.secrets/ - -# Generate a random openssl key -openssl rand -hex 64 -out /tmp/.secrets/pwd.key - -# Use a password you want and add it to a text file -echo ORacle_21c > /tmp/.secrets/common_os_pwdfile +- Create a text file which is having the password which you want to use for the DB user. +- Create an RSA key pair using `openssl`. +- Encrypt the text file with password using `openssl` with the RSA key pair generated earlier. +- Remove the initial text file. +- Create the Kubernetes secret named `db-user-pass-rsa` using the encrypted file. -# Encrypt the file with the password with the random openssl key generated above -openssl enc -aes-256-cbc -md md5 -salt -in /tmp/.secrets/common_os_pwdfile -out /tmp/.secrets/common_os_pwdfile.enc -pass file:/tmp/.secrets/pwd.key +Please refer the below example for the above steps: -# Remove the password text file -rm -f /tmp/.secrets/common_os_pwdfile - -# Create the Kubernetes secret in namespace "shns" -kubectl create secret generic db-user-pass --from-file=/tmp/.secrets/common_os_pwdfile.enc --from-file=/tmp/.secrets/pwd.key -n shns - -# Check the secret details -kubectl get secret -n shns +```sh +# Create a directory for files for the secret: +mkdir /tmp/.secret_loc/ + +# Create directories and initialize the variables +PDIR="/tmp/.secret_loc" +RSADIR="${PDIR}"/"rsakey" +rm -rf "${RSADIR}" +mkdir -p "${RSADIR}" +PRIVKEY="${RSADIR}"/"key.pem" +PUBKEY="${RSADIR}"/"key.pub" +NAMESPACE="shns" +PWDFILE="${RSADIR}"/"pwdfile.txt" +PWDFILE_ENC="${RSADIR}"/"pwdfile.enc" +SECRET_NAME="db-user-pass-rsa" + +# Generate the RSA Key +cd ${RSADIR} +openssl genrsa -out key.pem +openssl rsa -in key.pem -out key.pub -pubout + +# Create a text file with the password +rm -f $PWDFILE_ENC +echo ORacle_23c > ${RSADIR}/pwdfile.txt + +# Create encrypted file from the text file using the RSA key +openssl rsautl -in $PWDFILE -out $PWDFILE_ENC -pubin -inkey $PUBKEY -encrypt + +# Remove the initial text file: +rm -f $PWDFILE + +# Deleting the existing secret if existing +kubectl delete secret $SECRET_NAME -n $NAMESPACE + +# Create the Kubernetes secret in namespace "NAMESPACE" +kubectl create secret generic $SECRET_NAME --from-file=$PWDFILE_ENC --from-file=${PRIVKEY} -n $NAMESPACE ``` diff --git a/docs/sharding/provisioning/oraclesi.yaml b/docs/sharding/provisioning/oraclesi.yaml index 2e0607b7..cac70ffa 100644 --- a/docs/sharding/provisioning/oraclesi.yaml +++ b/docs/sharding/provisioning/oraclesi.yaml @@ -1,14 +1,14 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: oshard-gold-image-pvc19c + name: oshard-gold-image-pvc21c namespace: shns labels: - app: oshard19cdb-dep + app: oshard21cdb-dep spec: accessModes: - ReadWriteOnce @@ -18,28 +18,28 @@ spec: storageClassName: oci selector: matchLabels: - failure-domain.beta.kubernetes.io/zone: "EU-FRANKFURT-1-AD-1" + topology.kubernetes.io/zone: "PHX-AD-1" --- apiVersion: apps/v1 kind: StatefulSet metadata: - name: oshard19cdb + name: oshard21cdb namespace: shns labels: - app: oshard19cdb-dep + app: oshard21cdb-dep spec: selector: matchLabels: - app: oshard19cdb-dep + app: oshard21cdb-dep serviceName: gold-shard template: metadata: labels: - app: oshard19cdb-dep + app: oshard21cdb-dep spec: containers: - image: container-registry.oracle.com/database/enterprise:latest - name: oshard19cdb + name: oshard21cdb ports: - containerPort: 1521 name: db1-dbport @@ -68,17 +68,17 @@ spec: volumes: - name: data persistentVolumeClaim: - claimName: oshard-gold-image-pvc19c + claimName: oshard-gold-image-pvc21c - name: dshm emptyDir: medium: Memory nodeSelector: - failure-domain.beta.kubernetes.io/zone: "EU-FRANKFURT-1-AD-1" + topology.kubernetes.io/zone: "PHX-AD-1" --- apiVersion: v1 kind: Service metadata: - name: oshard19cdb + name: oshard21cdb namespace: shns spec: ports: @@ -95,5 +95,4 @@ spec: port: 6234 targetPort: db1-onsrport selector: - app: oshard19cdb-dep - + app: oshard21cdb-dep \ No newline at end of file diff --git a/docs/sharding/provisioning/oraclesi_pvc_commented.yaml b/docs/sharding/provisioning/oraclesi_pvc_commented.yaml index 89707a08..43b50a5e 100644 --- a/docs/sharding/provisioning/oraclesi_pvc_commented.yaml +++ b/docs/sharding/provisioning/oraclesi_pvc_commented.yaml @@ -1,46 +1,45 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # - -# apiVersion: v1 -# kind: PersistentVolumeClaim -# metadata: -# name: oshard-gold-image-pvc19c -# namespace: shns -# labels: -# app: oshard19cdb-dep -# spec: -# accessModes: -# - ReadWriteOnce -# resources: -# requests: -# storage: 50Gi -# storageClassName: oci -# selector: -# matchLabels: -# failure-domain.beta.kubernetes.io/zone: "EU-FRANKFURT-1-AD-1" -# --- +#apiVersion: v1 +#kind: PersistentVolumeClaim +#metadata: +# name: oshard-gold-image-pvc21c +# namespace: shns +# labels: +# app: oshard21cdb-dep +#spec: +# accessModes: +# - ReadWriteOnce +# resources: +# requests: +# storage: 50Gi +# storageClassName: oci +# selector: +# matchLabels: +# topology.kubernetes.io/zone: "PHX-AD-1" +#--- apiVersion: apps/v1 kind: StatefulSet metadata: - name: oshard19cdb + name: oshard21cdb namespace: shns labels: - app: oshard19cdb-dep + app: oshard21cdb-dep spec: selector: matchLabels: - app: oshard19cdb-dep + app: oshard21cdb-dep serviceName: gold-shard template: metadata: labels: - app: oshard19cdb-dep + app: oshard21cdb-dep spec: containers: - image: container-registry.oracle.com/database/enterprise:latest - name: oshard19cdb + name: oshard21cdb ports: - containerPort: 1521 name: db1-dbport @@ -69,17 +68,17 @@ spec: volumes: - name: data persistentVolumeClaim: - claimName: oshard-gold-image-pvc19c + claimName: oshard-gold-image-pvc21c - name: dshm emptyDir: medium: Memory nodeSelector: - failure-domain.beta.kubernetes.io/zone: "EU-FRANKFURT-1-AD-1" + topology.kubernetes.io/zone: "PHX-AD-1" --- apiVersion: v1 kind: Service metadata: - name: oshard19cdb + name: oshard21cdb namespace: shns spec: ports: @@ -96,5 +95,4 @@ spec: port: 6234 targetPort: db1-onsrport selector: - app: oshard19cdb-dep - + app: oshard21cdb-dep \ No newline at end of file diff --git a/docs/sharding/provisioning/provisioning_persistent_volume_having_db_gold_image.md b/docs/sharding/provisioning/provisioning_persistent_volume_having_db_gold_image.md index 581fc62c..7d3312a6 100644 --- a/docs/sharding/provisioning/provisioning_persistent_volume_having_db_gold_image.md +++ b/docs/sharding/provisioning/provisioning_persistent_volume_having_db_gold_image.md @@ -9,7 +9,7 @@ This example uses file `oraclesi.yaml` to provision a single instance Oracle Dat * A Persistent Volume Claim * Repository location for Database Docker Image: `image: container-registry.oracle.com/database/enterprise:latest` * Namespace: `shns` -* Tag `nodeSelector` to deploy the Single Oracle Database in AD `EU-FRANKFURT-1-AD-1` +* Tag `nodeSelector` to deploy the Single Oracle Database in AD `PHX-AD-1` In this example, we are using pre-built Oracle Database image available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above image from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. diff --git a/docs/sharding/provisioning/shard_prov.yaml b/docs/sharding/provisioning/shard_prov.yaml deleted file mode 100644 index 032abd68..00000000 --- a/docs/sharding/provisioning/shard_prov.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - - name: shard2 - storageSizeInGb: 50 - catalog: - - name: catalog - storageSizeInGb: 50 - gsm: - - name: gsm1 - storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" - - name: gsm2 - storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - scriptsLocation: "set -ex;curl https://codeload.github.com/oracle/db-sharding/tar.gz/master | tar -xz --strip=4 db-sharding-master/docker-based-sharding-deployment/dockerfiles/21.3.0/scripts; cp -i -r scripts/* /opt/oracle/scripts/sharding/scripts/;cp -i -r scripts/*py /opt/oracle/scripts/sharding" - secret: db-user-pass - isExternalSvc: false - isDeleteOraPvc: True - namespace: shns - diff --git a/docs/sharding/provisioning/shard_prov_clone.yaml b/docs/sharding/provisioning/shard_prov_clone.yaml deleted file mode 100644 index f9d3f826..00000000 --- a/docs/sharding/provisioning/shard_prov_clone.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.eu-frankfurt-1.abtheljtmwcwf7liuhaibzgdcoxqcwwfpsqiqlsumrjlzkin7y4zx3x2idua - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.eu-frankfurt-1.abtheljtmwcwf7liuhaibzgdcoxqcwwfpsqiqlsumrjlzkin7y4zx3x2idua - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.eu-frankfurt-1.abtheljtmwcwf7liuhaibzgdcoxqcwwfpsqiqlsumrjlzkin7y4zx3x2idua - gsm: - - name: gsm1 - storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" - - name: gsm2 - storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - scriptsLocation: "set -ex;curl https://codeload.github.com/oracle/db-sharding/tar.gz/master | tar -xz --strip=4 db-sharding-master/docker-based-sharding-deployment/dockerfiles/21.3.0/scripts; cp -i -r scripts/* /opt/oracle/scripts/sharding/scripts/;cp -i -r scripts/*py /opt/oracle/scripts/sharding" - secret: db-user-pass - isExternalSvc: false - isClone: True - isDeleteOraPvc: True - namespace: shns diff --git a/docs/sharding/provisioning/shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/shard_prov_clone_across_ads.yaml deleted file mode 100644 index 6b815541..00000000 --- a/docs/sharding/provisioning/shard_prov_clone_across_ads.yaml +++ /dev/null @@ -1,72 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.eu-frankfurt-1.abtheljtjlc7oce3sgq55vnskb4sjdip5sdaighm54hpmlcg7avgc76pjbea - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.eu-frankfurt-1.abtheljtjlc7oce3sgq55vnskb4sjdip5sdaighm54hpmlcg7avgc76pjbea - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.eu-frankfurt-1.abtheljtjlc7oce3sgq55vnskb4sjdip5sdaighm54hpmlcg7avgc76pjbea - gsm: - - name: gsm1 - storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - - name: gsm2 - storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-3" - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - scriptsLocation: "set -ex;curl https://codeload.github.com/oracle/db-sharding/tar.gz/master | tar -xz --strip=4 db-sharding-master/docker-based-sharding-deployment/dockerfiles/21.3.0/scripts; cp -i -r scripts/* /opt/oracle/scripts/sharding/scripts/;cp -i -r scripts/*py /opt/oracle/scripts/sharding" - secret: db-user-pass - isExternalSvc: false - isClone: True - isDeleteOraPvc: True - namespace: shns diff --git a/docs/sharding/provisioning/shard_prov_send_notification.yaml b/docs/sharding/provisioning/shard_prov_send_notification.yaml deleted file mode 100644 index 19f3c02c..00000000 --- a/docs/sharding/provisioning/shard_prov_send_notification.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.eu-frankfurt-1.abtheljtjlc7oce3sgq55vnskb4sjdip5sdaighm54hpmlcg7avgc76pjbea - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.eu-frankfurt-1.abtheljtjlc7oce3sgq55vnskb4sjdip5sdaighm54hpmlcg7avgc76pjbea - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.eu-frankfurt-1.abtheljtjlc7oce3sgq55vnskb4sjdip5sdaighm54hpmlcg7avgc76pjbea - gsm: - - name: gsm1 - storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-2" - - name: gsm2 - storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "EU-FRANKFURT-1-AD-3" - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - scriptsLocation: "set -ex;curl https://codeload.github.com/oracle/db-sharding/tar.gz/master | tar -xz --strip=4 db-sharding-master/docker-based-sharding-deployment/dockerfiles/21.3.0/scripts; cp -i -r scripts/* /opt/oracle/scripts/sharding/scripts/;cp -i -r scripts/*py /opt/oracle/scripts/sharding" - secret: db-user-pass - isExternalSvc: false - isClone: True - isDeleteOraPvc: True - namespace: shns - nsConfigMap: onsconfigmap - nsSecret: my-secret - diff --git a/docs/sharding/provisioning/provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md similarity index 57% rename from docs/sharding/provisioning/provisioning_by_cloning_db_from_gold_image_across_ads.md rename to docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md index 75a6f743..9ae05d50 100644 --- a/docs/sharding/provisioning/provisioning_by_cloning_db_from_gold_image_across_ads.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -1,6 +1,8 @@ -# Provisioning Oracle Database Sharding Topology by Cloning the Database from Your Own Database Gold Image Across Availability Domains (ADs) +# Provisioning Oracle Sharded Database with System Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs) -In this test case, you provision the Oracle Database sharding topology while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this test case, you provision the Oracle Database sharding topology with System Sharding while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. This use case applies when you want to provision the database Pods on a Kubernetes Node in any availability domain (AD), which can also be different from the availability domain (AD) of the Block Volume that has the Oracle Database Gold Image provisioned earlier. @@ -17,25 +19,28 @@ NOTE: ```sh kubectl get pv -n shns ``` -2. Create a Block Volume Backup for this Block Volume, and use the OCID of the Block Volume Backup in the next step. This example uses `shard_prov_clone_across_ads.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: +2. Create a Block Volume Backup for this Block Volume, and use the OCID of the Block Volume Backup in the next step. This example uses `ssharding_shard_prov_clone_across_ads.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: * Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Two sharding Pods: `shard1` and `shard2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` * One Catalog Pod: `catalog` * Namespace: `shns` * Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume which had the Gold Image. -* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.eu-frankfurt-1.abtheljtjlc7oce3sgq55vnskb4sjdip5sdaighm54hpmlcg7avgc76pjbea` +* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` + +NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned across multiple Availability Domains by cloning the database. In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `shard_prov_clone_across_ads.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_clone_across_ads.yaml`. + * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) -Use the file: [shard_prov_clone_across_ads.yaml](./shard_prov_clone_across_ads.yaml) for this use case as below: +Use the file: [ssharding_shard_prov_clone_across_ads.yaml](./ssharding_shard_prov_clone_across_ads.yaml) for this use case as below: -1. Deploy the `shard_prov_clone_across_ads.yaml` file: +1. Deploy the `ssharding_shard_prov_clone_across_ads.yaml` file: ```sh - kubectl apply -f shard_prov_clone_across_ads.yaml + kubectl apply -f ssharding_shard_prov_clone_across_ads.yaml ``` 2. Check the status of the deployment: ```sh diff --git a/docs/sharding/provisioning/provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md similarity index 54% rename from docs/sharding/provisioning/provisioning_by_cloning_db_gold_image_in_same_ad.md rename to docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md index 531c3839..cb67addb 100644 --- a/docs/sharding/provisioning/provisioning_by_cloning_db_gold_image_in_same_ad.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -1,4 +1,6 @@ -# Provisioning Oracle Database Sharding Topology by Cloning the Database from Your Own Database Gold Image in the same Availability Domain (AD) +# Provisioning Oracle Sharded Database with System Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD) + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. In this case, the database is created automatically by cloning from an existing Oracle Database Gold Image during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology is deployed using Oracle Sharding controller. @@ -8,30 +10,33 @@ Choosing this option takes substantially less time during the Oracle Database Sh **NOTE** For this step, the Persistent Volume that has the Oracle Database Gold Image is identified using its OCID. -1. Check the OCID of the Persistent Volume provisioned by above step using below command: +1. Check the OCID of the Persistent Volume provisioned earlier using below command: ```sh kubectl get pv -n shns ``` -2. This example uses `shard_prov_clone.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: +2. This example uses `ssharding_shard_prov_clone.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: * Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Two sharding Pods: `shard1` and `shard2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` * One Catalog Pod: `catalog` * Namespace: `shns` -* Database Cloning from the Database Gold Image present in Persistent Volume having OCID: `ocid1.volume.oc1.eu-frankfurt-1.abtheljtmwcwf7liuhaibzgdcoxqcwwfpsqiqlsumrjlzkin7y4zx3x2idua` +* Database Cloning from the Database Gold Image present in Persistent Volume having OCID: `ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq` + +NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned in the same Availability Domain `PHX-AD-1` by cloning the database. In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_clone.yaml`. + * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) -Use the file: [shard_prov_clone.yaml](./shard_prov_clone.yaml) for this use case as below: +Use the file: [ssharding_shard_prov_clone.yaml](./ssharding_shard_prov_clone.yaml) for this use case as below: -1. Deploy the `shard_prov_clone.yaml` file: +1. Deploy the `ssharding_shard_prov_clone.yaml` file: ```sh - kubectl apply -f shard_prov_clone.yaml + kubectl apply -f ssharding_shard_prov_clone.yaml ``` 2. Check the status of the deployment: ```sh diff --git a/docs/sharding/provisioning/provisioning_with_control_on_resources.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md similarity index 58% rename from docs/sharding/provisioning/provisioning_with_control_on_resources.md rename to docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md index 734cad89..e14e76a4 100644 --- a/docs/sharding/provisioning/provisioning_with_control_on_resources.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md @@ -1,11 +1,13 @@ -# Provisioning Oracle Database Sharding Topology with Additional Control on Resources Allocated to Pods +# Provisioning Oracle Sharded Database with System Sharding with additional control on resources like Memory and CPU allocated to Pods -In this use case, there are additional tags used to control resources such as CPU and Memory used by the different Pods when the Oracle Sharding topology is deployed using Oracle Sharding controller. +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. -This example uses `shard_prov_memory_cpu.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: +In this use case, there are additional tags used to control resources such as CPU and Memory used by the different Pods when the Oracle Sharding topology with System Sharding is deployed using Oracle Sharding controller. + +This example uses `ssharding_shard_prov_memory_cpu.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: * Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Two sharding Pods: `shard1` and `shard2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` * One Catalog Pod: `catalog` * Namespace: `shns` * Tags `memory` and `cpu` to control the Memory and CPU of the PODs @@ -13,15 +15,15 @@ This example uses `shard_prov_memory_cpu.yaml` to provision an Oracle Database s In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `shard_prov_memory_cpu.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_memory_cpu.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) -Use the YAML file [shard_prov_memory_cpu.yaml](./shard_prov_memory_cpu.yaml). +Use the YAML file [ssharding_shard_prov_memory_cpu.yaml](./ssharding_shard_prov_memory_cpu.yaml). -1. Deploy the `shard_prov_memory_cpu.yaml` file: +1. Deploy the `ssharding_shard_prov_memory_cpu.yaml` file: ```sh - kubectl apply -f shard_prov_memory_cpu.yaml + kubectl apply -f ssharding_shard_prov_memory_cpu.yaml ``` 1. Check the details of a POD. For example: To check the details of Pod `shard1-0`: diff --git a/docs/sharding/provisioning/provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md similarity index 71% rename from docs/sharding/provisioning/provisioning_with_notification_using_oci_notification.md rename to docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md index 7df7bc05..90a8f803 100644 --- a/docs/sharding/provisioning/provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md @@ -1,15 +1,17 @@ -# Provisioning Oracle Database Sharding Topology and Send Notification Using OCI Notification Service +# Provisioning Oracle Sharded Database with System Sharding and send Notification using OCI Notification Service -This use case demonstrates how to use a notification service like OCI Notification service to send an email notification when a particular operation is completed on an Oracle Database sharding topology provisioned using the Oracle Database sharding controller. +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. -This example uses `shard_prov_send_notification.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: +This use case demonstrates how to use a notification service like OCI Notification service to send an email notification when a particular operation is completed on an Oracle Database sharding topology provisioned using the Oracle Database sharding controller. + +This example uses `ssharding_shard_prov_send_notification.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: * Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Two sharding Pods: `shard1` and `shard2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` * One Catalog Pod: `catalog` * Namespace: `shns` * Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume that has the Database Gold Image created earlier. -* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.eu-frankfurt-1.abtheljtjlc7oce3sgq55vnskb4sjdip5sdaighm54hpmlcg7avgc76pjbea` +* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` * Configmap to send notification email when a particular operation is completed. For example: When a shard is added. **NOTE:** @@ -26,8 +28,8 @@ To do this: user=ocid1.user.oc1........fx7omxfq fingerprint=fa:18:98:...............:8a tenancy=ocid1.tenancy.oc1..aaaa.......orpn7inq - region=eu-frankfurt-1 - topicid=ocid1.onstopic.oc1.eu-frankfurt-1.aaa............6xrq + region=us-phoenix-1 + topicid=ocid1.onstopic.oc1.phx.aaa............6xrq ``` 2. Create a configmap using the below command using the file created above: ```sh @@ -61,14 +63,14 @@ To do this: In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_send_notification.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) -Use the file: [shard_prov_send_notification.yaml](./shard_prov_send_notification.yaml) for this use case as below: +Use the file: [ssharding_shard_prov_send_notification.yaml](./ssharding_shard_prov_send_notification.yaml) for this use case as below: -1. Deploy the `shard_prov_send_notification.yaml` file: +1. Deploy the `ssharding_shard_prov_send_notification.yaml` file: ```sh - kubectl apply -f shard_prov_send_notification.yaml + kubectl apply -f ssharding_shard_prov_send_notification.yaml ``` 2. Check the status of the deployment: ```sh diff --git a/docs/sharding/provisioning/provisioning_without_db_gold_image.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md similarity index 59% rename from docs/sharding/provisioning/provisioning_without_db_gold_image.md rename to docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md index 0a908b52..8f10fd8c 100644 --- a/docs/sharding/provisioning/provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md @@ -1,28 +1,30 @@ -# Provisioning Oracle Database Sharding Topology Without Database Gold Image +# Provisioning Oracle Sharded Database with System Sharding without Database Gold Image -In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology is deployed using Oracle Sharding controller. +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System Sharding is deployed using Oracle Sharding controller. **NOTE** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. -This example uses `shard_prov.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: +This example uses `ssharding_shard_prov.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: * Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Two sharding Pods: `shard1` and `shard2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` * One Catalog Pod: `catalog` * Namespace: `shns` In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) -Use the file: [shard_prov.yaml](./shard_prov.yaml) for this use case as below: +Use the file: [ssharding_shard_prov.yaml](./ssharding_shard_prov.yaml) for this use case as below: -1. Deploy the `shard_prov.yaml` file: +1. Deploy the `ssharding_shard_prov.yaml` file: ```sh - kubectl apply -f shard_prov.yaml + kubectl apply -f ssharding_shard_prov.yaml ``` 1. Check the status of the deployment: ```sh diff --git a/docs/sharding/provisioning/scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md similarity index 56% rename from docs/sharding/provisioning/scale_in_delete_an_existing_shard.md rename to docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md index 9334741b..4d5713d4 100644 --- a/docs/sharding/provisioning/scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md @@ -1,6 +1,8 @@ -# Scale In - Delete an existing Shard From a Working Oracle Database Sharding Topology +# Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System Sharding -This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology provisioned using Oracle Database Sharding controller. +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with System Sharding provisioned using Oracle Database Sharding controller. **NOTE** The deletion of a shard is done after verifying the Chunks have been moved out of that shard. @@ -13,25 +15,25 @@ In this use case, the existing database Sharding is having: In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_delshard.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) NOTE: Use tag `isDelete: true` to delete the shard you want. -This use case deletes the shard `shard2` from the above Sharding Topology. +This use case deletes the shard `shard4` from the above Sharding Topology. -Use the file: [shard_prov_delshard.yaml](./shard_prov_delshard.yaml) for this use case as below: +Use the file: [ssharding_shard_prov_delshard.yaml](./ssharding_shard_prov_delshard.yaml) for this use case as below: -1. Deploy the `shard_prov_delshard.yaml` file: +1. Deploy the `ssharding_shard_prov_delshard.yaml` file: ```sh - kubectl apply -f shard_prov_delshard.yaml + kubectl apply -f ssharding_shard_prov_delshard.yaml ``` 2. Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n shns -**NOTE:** After you apply `shard_prov_delshard.yaml`, the change may not be visible immediately. When the shard is removed, first the chunks will be moved out of that shard that is going to be deleted. +**NOTE:** After you apply `ssharding_shard_prov_delshard.yaml`, the change may not be visible immediately. When the shard is removed, first the chunks will be moved out of that shard that is going to be deleted. To monitor the chunk movement, use the following command: diff --git a/docs/sharding/provisioning/scale_out_add_shards.md b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md similarity index 51% rename from docs/sharding/provisioning/scale_out_add_shards.md rename to docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md index 174d8c6c..5c349847 100644 --- a/docs/sharding/provisioning/scale_out_add_shards.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md @@ -1,31 +1,33 @@ -# Scale Out - Add Shards to an existing Oracle Database Sharding Topology +# Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System Sharding -This use case demonstrates adding a new shard to an existing Oracle Database sharding topology provisioned earlier using Oracle Database Sharding controller. +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with System Sharding provisioned earlier using Oracle Database Sharding controller. In this use case, the existing Oracle Database sharding topology is having: * Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Two sharding Pods: `shard1` and `shard2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` * One Catalog Pod: `catalog` * Namespace: `shns` In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_extshard.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) -This use case adds three new shards `shard3`,`shard4`,`shard4` to above Sharding Topology. +This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. -Use the file: [shard_prov_extshard.yaml](./shard_prov_extshard.yaml) for this use case as below: +Use the file: [ssharding_shard_prov_extshard.yaml](./ssharding_shard_prov_extshard.yaml) for this use case as below: -1. Deploy the `shard_prov_extshard.yaml` file: +1. Deploy the `ssharding_shard_prov_extshard.yaml` file: ```sh - kubectl apply -f shard_prov_extshard.yaml + kubectl apply -f ssharding_shard_prov_extshard.yaml ``` 2. Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n shns - # Check the logs of a particular pod. For example, to check status of pod "shard3-0": - kubectl logs -f pod/shard3-0 -n shns + # Check the logs of a particular pod. For example, to check status of pod "shard4-0": + kubectl logs -f pod/shard4-0 -n shns diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml new file mode 100644 index 00000000..f803ba42 --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml @@ -0,0 +1,58 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns + diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml new file mode 100644 index 00000000..9a690626 --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml @@ -0,0 +1,82 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml new file mode 100644 index 00000000..d2ae0ba9 --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml @@ -0,0 +1,82 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml new file mode 100644 index 00000000..5f600d3f --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml @@ -0,0 +1,68 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard4 + isDelete: True + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard5 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml new file mode 100644 index 00000000..d25dc901 --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml @@ -0,0 +1,67 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard4 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard5 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml similarity index 60% rename from docs/sharding/provisioning/shard_prov_memory_cpu.yaml rename to docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml index 0e66c563..793a3e4d 100644 --- a/docs/sharding/provisioning/shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml @@ -2,6 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # +--- apiVersion: database.oracle.com/v1alpha1 kind: ShardingDatabase metadata: @@ -20,6 +21,9 @@ spec: value: "600" - name: "INIT_PGA_SIZE" value: "400" + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary - name: shard2 storageSizeInGb: 50 resources: @@ -31,6 +35,23 @@ spec: value: "600" - name: "INIT_PGA_SIZE" value: "400" + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary catalog: - name: catalog storageSizeInGb: 50 @@ -38,30 +59,30 @@ spec: requests: memory: "1000Mi" cpu: "1000m" + imagePullPolicy: "Always" gsm: - name: gsm1 + imagePullPolicy: "Always" storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" + region: primary - name: gsm2 + imagePullPolicy: "Always" storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" + region: standby storageClass: oci dbImage: container-registry.oracle.com/database/enterprise:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred - scriptsLocation: "set -ex;curl https://codeload.github.com/oracle/db-sharding/tar.gz/master | tar -xz --strip=4 db-sharding-master/docker-based-sharding-deployment/dockerfiles/21.3.0/scripts; cp -i -r scripts/* /opt/oracle/scripts/sharding/scripts/;cp -i -r scripts/*py /opt/oracle/scripts/sharding" - secret: db-user-pass - isExternalSvc: false + isExternalSvc: False isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary namespace: shns diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml new file mode 100644 index 00000000..96217b74 --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml @@ -0,0 +1,85 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + nsConfigMap: onsconfigmap + nsSecret: my-secret + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns + diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md new file mode 100644 index 00000000..0beecbca --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -0,0 +1,53 @@ +# Provisioning Oracle Sharded Database with User Defined Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs) + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this test case, you provision the Oracle Database sharding topology with User Defined Sharding while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. + +This use case applies when you want to provision the database Pods on a Kubernetes Node in any availability domain (AD), which can also be different from the availability domain (AD) of the Block Volume that has the Oracle Database Gold Image provisioned earlier. + +Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup across ADs. + +NOTE: + +* Cloning from Block Volume Backup in OCI enables the new Persistent Volumes to be created in other ADs. +* To specify the AD where you want to provision the database Pod, use the tag `nodeSelector` and the POD will be provisioned in a node running in that AD. +* To specify GSM containers, you can also use the tag `nodeSelector` to specify the AD. +* Before you can provision with the Gold Image, you need the OCID of the Persistent Volume that has the Oracle Database Gold Image. + +1. Check the OCID of the Persistent Volume provisioned for the Oracle Database Gold Image: + ```sh + kubectl get pv -n shns + ``` +2. Create a Block Volume Backup for this Block Volume, and use the OCID of the Block Volume Backup in the next step. This example uses `udsharding_shard_prov_clone_across_ads.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume which had the Gold Image. +* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` +* User Defined Sharding is specified using `shardingType: USER` + +NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned across multiple Availability Domains by cloning the database. + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_clone_across_ads.yaml`. + * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +Use the file: [udsharding_shard_prov_clone_across_ads.yaml](./udsharding_shard_prov_clone_across_ads.yaml) for this use case as below: + +1. Deploy the `udsharding_shard_prov_clone_across_ads.yaml` file: + ```sh + kubectl apply -f udsharding_shard_prov_clone_across_ads.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md new file mode 100644 index 00000000..445d0105 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -0,0 +1,49 @@ +# Provisioning Oracle Sharded Database with User Defined Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD) + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this case, the database is created automatically by cloning from an existing Oracle Database Gold Image during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology is deployed using Oracle Sharding controller. + +This use case applies when you are cloning from a Block Volume, and you can clone _only_ in the same availability domain (AD). The result is that the cloned shard database PODs can be created _only_ in the same AD where the Gold Image Block Volume is present. + +Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup. + +**NOTE** For this step, the Persistent Volume that has the Oracle Database Gold Image is identified using its OCID. + +1. Check the OCID of the Persistent Volume provisioned earlier using below command: + + ```sh + kubectl get pv -n shns + ``` + +2. This example uses `udsharding_shard_prov_clone.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Cloning from the Database Gold Image present in Persistent Volume having OCID: `ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq` +* User Defined Sharding is specified using `shardingType: USER` + +NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned in the same Availability Domain `PHX-AD-1` by cloning the database. + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_clone.yaml`. + * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +Use the file: [udsharding_shard_prov_clone.yaml](./udsharding_shard_prov_clone.yaml) for this use case as below: + +1. Deploy the `udsharding_shard_prov_clone.yaml` file: + ```sh + kubectl apply -f udsharding_shard_prov_clone.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md new file mode 100644 index 00000000..d37368f8 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md @@ -0,0 +1,42 @@ +# Provisioning Oracle Sharded Database with User Defined Sharding with additional control on resources like Memory and CPU allocated to Pods + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this use case, there are additional tags used to control resources such as CPU and Memory used by the different Pods when the Oracle Sharding topology with User Defined Sharding is deployed using Oracle Sharding controller. + +This example uses `udsharding_shard_prov_memory_cpu.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Tags `memory` and `cpu` to control the Memory and CPU of the PODs +* Additional tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level +* User Defined Sharding is specified using `shardingType: USER` + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_memory_cpu.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +Use the YAML file [udsharding_shard_prov_memory_cpu.yaml](./udsharding_shard_prov_memory_cpu.yaml). + +1. Deploy the `udsharding_shard_prov_memory_cpu.yaml` file: + + ```sh + kubectl apply -f udsharding_shard_prov_memory_cpu.yaml + ``` + +1. Check the details of a POD. For example: To check the details of Pod `shard1-0`: + + ```sh + kubectl describe pod/shard1-0 -n shns + ``` +3. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md new file mode 100644 index 00000000..c7da6aa5 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md @@ -0,0 +1,82 @@ +# Provisioning Oracle Sharded Database with User Defined Sharding and send Notification using OCI Notification Service + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates how to use a notification service like OCI Notification service to send an email notification when a particular operation is completed on an Oracle Database sharding topology provisioned using the Oracle Database sharding controller. + +This example uses `udsharding_shard_prov_send_notification.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume that has the Database Gold Image created earlier. +* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` +* Configmap to send notification email when a particular operation is completed. For example: When a shard is added. +* User Defined Sharding is specified using `shardingType: USER` + +**NOTE:** + +* The notification will be sent using a configmap created with the credentials of the OCI user account in this use case. + +We will create a topic in Notification Service of the OCI Console and use its OCID. + +To do this: + +1. Create a `configmap_data.txt` file, such as the following, which has the OCI User details that will be used to send notfication: + + ```sh + user=ocid1.user.oc1........fx7omxfq + fingerprint=fa:18:98:...............:8a + tenancy=ocid1.tenancy.oc1..aaaa.......orpn7inq + region=us-phoenix-1 + topicid=ocid1.onstopic.oc1.phx.aaa............6xrq + ``` +2. Create a configmap using the below command using the file created above: + ```sh + kubectl create configmap onsconfigmap --from-file=./configmap_data.txt -n shns + ``` + +3. Create a key file `priavatekey` having the PEM key of the OCI user being used to send notification: + ```sh + -----BEGIN PRIVATE KEY-G---- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXYxA0DJvEwtVR + +o4OxrunL3L2NZJRADTFR+TDHqrNF1JwbaFBizSdL+EXbxQW1faZs5lXZ/sVmQF9 + . + . + . + zn/xWC0FzXGRzfvYHhq8XT3omf6L47KqIzqo3jDKdgvVq4u+lb+fXJlhj6Rwi99y + QEp36HnZiUxAQnR331DacN+YSTE+vpzSwZ38OP49khAB1xQsbiv1adG7CbNpkxpI + nS7CkDLg4Hcs4b9bGLHYJVY= + -----END PRIVATE KEY----- + ``` +4. Use the key file `privatekey` to create a Kubernetes secret in namespace `shns`: + + ```sh + kubectl create secret generic my-secret --from-file=./privatekey -n shns + ``` + +5. Use this command to check details of the secret that you created: + + ```sh + kubectl describe secret my-secret -n shns + ``` + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_send_notification.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +Use the file: [udsharding_shard_prov_send_notification.yaml](./udsharding_shard_prov_send_notification.yaml) for this use case as below: + +1. Deploy the `udsharding_shard_prov_send_notification.yaml` file: + ```sh + kubectl apply -f udsharding_shard_prov_send_notification.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md new file mode 100644 index 00000000..a275155f --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md @@ -0,0 +1,37 @@ +# Provisioning Oracle Sharded Database with User Defined Sharding without Database Gold Image + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with User Defined Sharding is deployed using Oracle Sharding controller. + +**NOTE** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. + +This example uses `udsharding_shard_prov.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* User Defined Sharding is specified using `shardingType: USER` + + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + + +Use the file: [udsharding_shard_prov.yaml](./udsharding_shard_prov.yaml) for this use case as below: + +1. Deploy the `udsharding_shard_prov.yaml` file: + ```sh + kubectl apply -f udsharding_shard_prov.yaml + ``` +1. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md new file mode 100644 index 00000000..946a0ab9 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md @@ -0,0 +1,47 @@ +# Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with User Defined Sharding + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with User Defined Sharding provisioned using Oracle Database Sharding controller. + +**NOTE** The deletion of a shard is done after verifying the Chunks have been moved out of that shard. + +In this use case, the existing database Sharding is having: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Five sharding Pods: `shard1`,`shard2`,`shard3`,`shard4` and `shard5` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* User Defined Sharding is specified using `shardingType: USER` + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_delshard.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +NOTE: Use tag `isDelete: true` to delete the shard you want. + +This use case deletes the shard `shard4` from the above Sharding Topology. + +Use the file: [udsharding_shard_prov_delshard.yaml](./udsharding_shard_prov_delshard.yaml) for this use case as below: + +1. Deploy the `udsharding_shard_prov_delshard.yaml` file: + ```sh + kubectl apply -f udsharding_shard_prov_delshard.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + +**NOTE:** After you apply `udsharding_shard_prov_delshard.yaml`, the change may not be visible immediately. When the shard is removed, first the chunks will be moved out of that shard that is going to be deleted. + +To monitor the chunk movement, use the following command: + +```sh +# Switch to the primary GSM Container: +kubectl exec -i -t gsm1-0 -n shns /bin/bash + +# Check the status of the chunks and repeat to observe the chunk movement: +gdsctl config chunks +``` diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md new file mode 100644 index 00000000..e4200d72 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md @@ -0,0 +1,34 @@ +# Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with User Defined Sharding + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with User Defined Sharding provisioned earlier using Oracle Database Sharding controller. + +In this use case, the existing Oracle Database sharding topology is having: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* User Defined Sharding is specified using `shardingType: USER` + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_extshard.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. + +Use the file: [udsharding_shard_prov_extshard.yaml](./udsharding_shard_prov_extshard.yaml) for this use case as below: + +1. Deploy the `udsharding_shard_prov_extshard.yaml` file: + ```sh + kubectl apply -f udsharding_shard_prov_extshard.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard4-0": + kubectl logs -f pod/shard4-0 -n shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml new file mode 100644 index 00000000..019fc887 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml @@ -0,0 +1,59 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace2 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace3 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + shardingType: USER + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns + diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml new file mode 100644 index 00000000..adc2271f --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml @@ -0,0 +1,83 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + shardSpace: sspace1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + shardSpace: sspace2 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + shardSpace: sspace3 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + shardingType: USER + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml new file mode 100644 index 00000000..f2d10e23 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml @@ -0,0 +1,83 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + shardSpace: sspace1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + shardSpace: sspace2 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + shardSpace: sspace3 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + shardingType: USER + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns diff --git a/docs/sharding/provisioning/shard_prov_extshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml similarity index 51% rename from docs/sharding/provisioning/shard_prov_extshard.yaml rename to docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml index 449ba90b..51f1c292 100644 --- a/docs/sharding/provisioning/shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml @@ -2,6 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # +--- apiVersion: database.oracle.com/v1alpha1 kind: ShardingDatabase metadata: @@ -11,41 +12,58 @@ spec: shard: - name: shard1 storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace1 + shardRegion: primary - name: shard2 storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace2 + shardRegion: primary - name: shard3 storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace3 + shardRegion: primary - name: shard4 storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace4 + shardRegion: primary + isDelete: True - name: shard5 - storageSizeInGb: 50 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace5 + shardRegion: primary catalog: - name: catalog storageSizeInGb: 50 + imagePullPolicy: "Always" gsm: - name: gsm1 + imagePullPolicy: "Always" storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" + region: primary - name: gsm2 + imagePullPolicy: "Always" storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" + region: standby storageClass: oci dbImage: container-registry.oracle.com/database/enterprise:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred - scriptsLocation: "set -ex;curl https://codeload.github.com/oracle/db-sharding/tar.gz/master | tar -xz --strip=4 db-sharding-master/docker-based-sharding-deployment/dockerfiles/21.3.0/scripts; cp -i -r scripts/* /opt/oracle/scripts/sharding/scripts/;cp -i -r scripts/*py /opt/oracle/scripts/sharding" - secret: db-user-pass - isExternalSvc: false + shardingType: USER + isExternalSvc: False isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary namespace: shns diff --git a/docs/sharding/provisioning/shard_prov_delshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml similarity index 50% rename from docs/sharding/provisioning/shard_prov_delshard.yaml rename to docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml index 09cbd549..f45d421f 100644 --- a/docs/sharding/provisioning/shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml @@ -11,42 +11,57 @@ spec: shard: - name: shard1 storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace1 + shardRegion: primary - name: shard2 storageSizeInGb: 50 - isDelete: true + imagePullPolicy: "Always" + shardSpace: sspace2 + shardRegion: primary - name: shard3 storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace3 + shardRegion: primary - name: shard4 storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace4 + shardRegion: primary - name: shard5 - storageSizeInGb: 50 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace5 + shardRegion: primary catalog: - name: catalog storageSizeInGb: 50 + imagePullPolicy: "Always" gsm: - name: gsm1 + imagePullPolicy: "Always" storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" + region: primary - name: gsm2 + imagePullPolicy: "Always" storageSizeInGb: 50 - replicas: 1 - envVars: - - name: "SERVICE1_PARAMS" - value: "service_name=oltp_rw_svc;service_role=primary" - - name: "SERVICE2_PARAMS" - value: "service_name=oltp_ro_svc;service_role=primary" + region: standby storageClass: oci dbImage: container-registry.oracle.com/database/enterprise:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred - scriptsLocation: "set -ex;curl https://codeload.github.com/oracle/db-sharding/tar.gz/master | tar -xz --strip=4 db-sharding-master/docker-based-sharding-deployment/dockerfiles/21.3.0/scripts; cp -i -r scripts/* /opt/oracle/scripts/sharding/scripts/;cp -i -r scripts/*py /opt/oracle/scripts/sharding" - secret: db-user-pass - isExternalSvc: false + shardingType: USER + isExternalSvc: False isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml new file mode 100644 index 00000000..15022925 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml @@ -0,0 +1,90 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardSpace: sspace1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardSpace: sspace2 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardSpace: sspace3 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + shardingType: USER + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns + diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml new file mode 100644 index 00000000..afd951fe --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml @@ -0,0 +1,85 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + shardSpace: sspace1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + shardSpace: sspace2 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + shardSpace: sspace3 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + shardingType: USER + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + nsConfigMap: onsconfigmap + nsSecret: my-secret + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns From 6557e41ca538cf608c87d874d4abe4f87f150918 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Mon, 11 Mar 2024 14:56:53 +0000 Subject: [PATCH 044/414] add support for namspace scoped --- Dockerfile | 3 +- Makefile | 4 +-- .../v1alpha1/autonomousdatabase_webhook.go | 15 +++++++++- .../autonomousdatabasebackup_types.go | 3 +- .../autonomousdatabasebackup_webhook.go | 14 +++++++++ apis/database/v1alpha1/cdb_types.go | 2 +- apis/database/v1alpha1/pdb_types.go | 2 +- .../v1alpha1/zz_generated.deepcopy.go | 10 +++++++ commons/oci/database.go | 2 ++ ....oracle.com_autonomousdatabasebackups.yaml | 4 +++ config/rbac/role.yaml | 27 +++++++++++++++++ .../database/autonomousdatabase_controller.go | 29 +++++++++---------- 12 files changed, 92 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index 32db3fe0..11a56962 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,9 @@ ARG BUILDER_IMG FROM ${BUILDER_IMG} as builder # Download golang if BUILD_INTERNAL is set to true +ARG INSTALL_GO ARG GOLANG_VERSION -RUN if [ -n "$GOLANG_VERSION" ]; then \ +RUN if [ "$INSTALL_GO" = "true" ]; then \ curl -LJO https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ rm -rf /usr/local/go && tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ rm go${GOLANG_VERSION}.linux-amd64.tar.gz; \ diff --git a/Makefile b/Makefile index 0dbe393b..88e14843 100644 --- a/Makefile +++ b/Makefile @@ -76,10 +76,10 @@ GOLANG_VERSION ?= 1.21.7 ## Otherwise, use golang image from docker hub as the builder. ifeq ($(BUILD_INTERNAL), true) BUILDER_IMG = oraclelinux:8 -BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) +BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) --build-arg INSTALL_GO=true else BUILDER_IMG = golang:$(GOLANG_VERSION) -BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) +BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO=false endif docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast docker build --no-cache=true --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go index a7d492d7..7bd84b17 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -43,6 +43,7 @@ import ( "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -99,11 +100,23 @@ var _ webhook.Validator = &AutonomousDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type // ValidateCreate checks if the spec is valid for a provisioning or a binding operation func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { - var allErrs field.ErrorList autonomousdatabaselog.Info("validate create", "name", r.Name) + namespaces := dbcommons.GetWatchNamespaces() + _, hasEmptyString := namespaces[""] + isClusterScoped := len(namespaces) == 1 && hasEmptyString + if !isClusterScoped { + _, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + if len(namespaces) != 0 && !containsNamespace { + allErrs = append(allErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + } + if r.Spec.Details.AutonomousDatabaseOCID == nil { // provisioning operation allErrs = validateCommon(r, allErrs) allErrs = validateNetworkAccess(r, allErrs) diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_types.go b/apis/database/v1alpha1/autonomousdatabasebackup_types.go index d64e549b..876cb811 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_types.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_types.go @@ -55,7 +55,8 @@ type AutonomousDatabaseBackupSpec struct { Target TargetSpec `json:"target,omitempty"` DisplayName *string `json:"displayName,omitempty"` AutonomousDatabaseBackupOCID *string `json:"autonomousDatabaseBackupOCID,omitempty"` - + IsLongTermBackup *bool `json:"isLongTermBackup,omitempty"` + RetentionPeriodInDays *int `json:"retentionPeriodInDays,omitempty"` OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` } diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go index 42624c4a..99bf3815 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -82,6 +83,19 @@ func (r *AutonomousDatabaseBackup) ValidateCreate() (admission.Warnings, error) var allErrs field.ErrorList + namespaces := dbcommons.GetWatchNamespaces() + _, hasEmptyString := namespaces[""] + isClusterScoped := len(namespaces) == 1 && hasEmptyString + if !isClusterScoped { + _, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + if len(namespaces) != 0 && !containsNamespace { + allErrs = append(allErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + } + if r.Spec.Target.K8sADB.Name == nil && r.Spec.Target.OCIADB.OCID == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) diff --git a/apis/database/v1alpha1/cdb_types.go b/apis/database/v1alpha1/cdb_types.go index 84babaee..206781b2 100644 --- a/apis/database/v1alpha1/cdb_types.go +++ b/apis/database/v1alpha1/cdb_types.go @@ -154,7 +154,7 @@ type CDBStatus struct { // +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="Replicas",type="integer",description="Replicas" // +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the CDB Resource" // +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" -// +kubebuilder:resource:path=CDBS,scope=Namespaced +// +kubebuilder:resource:path=cdbs,scope=Namespaced // CDB is the Schema for the cdbs API type CDB struct { diff --git a/apis/database/v1alpha1/pdb_types.go b/apis/database/v1alpha1/pdb_types.go index ed3cc3b3..1e4da0a1 100644 --- a/apis/database/v1alpha1/pdb_types.go +++ b/apis/database/v1alpha1/pdb_types.go @@ -197,7 +197,7 @@ type PDBStatus struct { // +kubebuilder:printcolumn:JSONPath=".status.totalSize",name="PDB Size",type="string",description="Total Size of the PDB" // +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the PDB Resource" // +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" -// +kubebuilder:resource:path=PDBS,scope=Namespaced +// +kubebuilder:resource:path=pdbs,scope=Namespaced // PDB is the Schema for the pdbs API type PDB struct { diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 89e58a33..f1eaf503 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -288,6 +288,16 @@ func (in *AutonomousDatabaseBackupSpec) DeepCopyInto(out *AutonomousDatabaseBack *out = new(string) **out = **in } + if in.IsLongTermBackup != nil { + in, out := &in.IsLongTermBackup, &out.IsLongTermBackup + *out = new(bool) + **out = **in + } + if in.RetentionPeriodInDays != nil { + in, out := &in.RetentionPeriodInDays, &out.RetentionPeriodInDays + *out = new(int) + **out = **in + } in.OCIConfig.DeepCopyInto(&out.OCIConfig) } diff --git a/commons/oci/database.go b/commons/oci/database.go index 10d6c8ff..6196b9f5 100644 --- a/commons/oci/database.go +++ b/commons/oci/database.go @@ -408,6 +408,8 @@ func (d *databaseService) CreateAutonomousDatabaseBackup(adbBackup *dbv1alpha1.A createBackupRequest := database.CreateAutonomousDatabaseBackupRequest{ CreateAutonomousDatabaseBackupDetails: database.CreateAutonomousDatabaseBackupDetails{ AutonomousDatabaseId: common.String(adbOCID), + IsLongTermBackup: adbBackup.Spec.IsLongTermBackup, + RetentionPeriodInDays: adbBackup.Spec.RetentionPeriodInDays, }, } diff --git a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml index 0541b985..a5c37507 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml @@ -61,6 +61,8 @@ spec: type: string displayName: type: string + isLongTermBackup: + type: boolean ociConfig: description: "*********************** *\tOCI config ***********************" properties: @@ -69,6 +71,8 @@ spec: secretName: type: string type: object + retentionPeriodInDays: + type: integer target: description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 1fdcf261..fc3616e7 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -75,6 +75,13 @@ rules: verbs: - create - patch +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch - apiGroups: - "" resources: @@ -179,6 +186,26 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 35fc2f34..5323c0aa 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -93,32 +93,29 @@ func (r *AutonomousDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error For(&dbv1alpha1.AutonomousDatabase{}). Watches( &dbv1alpha1.AutonomousDatabaseBackup{}, - handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn()), + handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn), ). Watches( &dbv1alpha1.AutonomousDatabaseRestore{}, - handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn()), + handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn), ). WithEventFilter(predicate.And(r.eventFilterPredicate(), r.watchPredicate())). WithOptions(controller.Options{MaxConcurrentReconciles: 50}). // ReconcileHandler is never invoked concurrently with the same object. Complete(r) } +func (r *AutonomousDatabaseReconciler) enqueueMapFn(ctx context.Context, o client.Object) []reconcile.Request { + reqs := make([]reconcile.Request, len(o.GetOwnerReferences())) -func (r *AutonomousDatabaseReconciler) enqueueMapFn() handler.MapFunc { - return func(ctx context.Context, o client.Object) []reconcile.Request { - reqs := make([]reconcile.Request, len(o.GetOwnerReferences())) - - for _, owner := range o.GetOwnerReferences() { - reqs = append(reqs, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: owner.Name, - Namespace: o.GetNamespace(), - }, - }) - } - - return reqs + for _, owner := range o.GetOwnerReferences() { + reqs = append(reqs, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: owner.Name, + Namespace: o.GetNamespace(), + }, + }) } + + return reqs } func (r *AutonomousDatabaseReconciler) watchPredicate() predicate.Predicate { From 0ded045e840054acca67d8bf14720d842cf2eebf Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Tue, 12 Mar 2024 08:39:30 +0000 Subject: [PATCH 045/414] Rvats bugfixes --- commons/database/constants.go | 3 +++ .../singleinstancedatabase_controller.go | 21 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/commons/database/constants.go b/commons/database/constants.go index d655574f..47d2b8b9 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -521,6 +521,9 @@ const DisableTcpsCMD string = "$ORACLE_BASE/$CONFIG_TCPS_FILE disable" // Location of tls certs const TlsCertsLocation string = "/run/secrets/tls_secret" +// Check Mount in pods +const PodMountsCmd string = "awk '$2 == \"%s\" {print}' /proc/mounts" + // TCPS clientWallet update command const ClientWalletUpdate string = "sed -i -e 's/HOST.*$/HOST=%s)/g' -e 's/PORT.*$/PORT=%d)/g' ${ORACLE_BASE}/oradata/clientWallet/${ORACLE_SID}/tnsnames.ora" diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index d713ea8e..c9888b68 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -2438,10 +2438,23 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat if m.Spec.TcpsTlsSecret != "" { // case when tls secret is either added or changed TcpsCommand = "export TCPS_CERTS_LOCATION=" + dbcommons.TlsCertsLocation + " && " + dbcommons.EnableTcpsCMD - // call deletePods() with zero pods in avaiable and nil readyPod to delete all pods - result, err := r.deletePods(ctx, req, m, []corev1.Pod{}, corev1.Pod{}, 0, 0) - if result.Requeue { - return result, err + out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", + ctx, req, false, "bash", "-c", fmt.Sprintf(dbcommons.PodMountsCmd, dbcommons.TlsCertsLocation)) + r.Log.Info("Mount Check Output") + r.Log.Info(out) + if err != nil { + r.Log.Error(err, err.Error()) + return requeueY, nil + } + + if (m.Status.TcpsTlsSecret != "" ) || // case when TCPS Secret is changed + (!strings.Contains(out, dbcommons.TlsCertsLocation)) { // if mount is not there in pod + // call deletePods() with zero pods in avaiable and nil readyPod to delete all pods + result, err := r.deletePods(ctx, req, m, []corev1.Pod{}, corev1.Pod{}, 0, 0) + if result.Requeue { + return result, err + } + m.Status.TcpsTlsSecret = "" // to avoid reconciled pod deletions, in case of TCPS secret change and it fails } } From 9b4e95d4c4c5e80f38d552110ffdde999eed6fbb Mon Sep 17 00:00:00 2001 From: kuassi_mensah Date: Wed, 13 Mar 2024 13:52:12 +0000 Subject: [PATCH 046/414] Minor syntax fixes and renamed Metric Exporter. --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 81de9285..704c9e0b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released _Oracle Database Operator for Kubernetes_ (`OraOperator` or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating Oracle Database lifecycle management. -In this v1.0.0 production release, `OraOperator` supports the following database configurations and infrastructure: +In this v1.1.0 production release, `OraOperator` supports the following database configurations and infrastructure: * Oracle Autonomous Database: * Oracle Autonomous Database shared Oracle Cloud Infrastructure (OCI) (ADB-S) @@ -15,7 +15,7 @@ In this v1.0.0 production release, `OraOperator` supports the following database * Oracle Multitenant Databases (CDB/PDBs) * Oracle Base Database Cloud Service (BDBCS) * Oracle Data Guard (Preview status) -* Oracle Database Metrics Exporter (Preview status) +* Oracle Database Observability (Preview status) Oracle will continue to extend `OraOperator` to support additional Oracle Database configurations. @@ -30,7 +30,7 @@ This release of Oracle Database Operator for Kubernetes (the operator) supports * Oracle Multitenant Database: Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB * Oracle Base Database Cloud Service (BDBCS): provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License * Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration -* Oracle Database Metrics Exporter: create, patch, delete +* Oracle Database Observability: create, patch, delete * Watch over a set of namespaces or all the namespaces in the cluster using the "WATCH_NAMESPACE" env variable of the operator deployment The upcoming releases will support new configurations, operations and capabilities. @@ -63,9 +63,9 @@ Oracle strongly recommends that you ensure your system meets the following [Prer * ### Role Binding for access management - When the Oracle DB Operator is dealing with multiple namespaces and access management is required, you need to complete additional steps: - - To have the Oralce DB Operator monitor only certain defined namespaces which you want it to monitor. - - To apply role binding for those namespaces for access control. + When the Oracle DB Operator is operating with multiple namespaces and access management is required, you need to complete the following additional steps: + - Have the Oralce DB Operator monitor only certain defined namespaces. + - Apply role binding for those namespaces for access control. Follow the below steps: @@ -75,7 +75,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer - name: WATCH_NAMESPACE value: "oracle-database-operator-system,shns" ``` - This is needed when you want the DB Operator to monitor the namespaces `shns` and `oracle-database-operator-system`. + This is needed when you want the DB Operator to monitor `shns` and `oracle-database-operator-system` namespaces. If you are going to work with more namespaces, then you will need to add them under `WATCH_NAMESPACE`. @@ -151,7 +151,7 @@ The quickstarts are designed for specific database configurations: The quickstarts are designed for non-database configurations: -* [Oracle Database Metrics Exporter](./docs/observability/README.md) +* [Oracle Database Observability](./docs/observability/README.md) YAML file templates are available under [`/config/samples`](./config/samples/). You can copy and edit these template files to configure them for your use cases. From 2366b962c441d2878e051e2ed40d65f21779f9fc Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Wed, 13 Mar 2024 16:30:55 +0000 Subject: [PATCH 047/414] Fixing prebuilt sidb flow --- .../singleinstancedatabase_controller.go | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index c9888b68..9053d5aa 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -714,27 +714,27 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns Name: "datafiles-vol", }}, }) - if m.Spec.Image.PrebuiltDB { - initContainers = append(initContainers, corev1.Container{ - Name: "init-prebuiltdb", - Image: m.Spec.Image.PullFrom, - Command: []string{"/bin/sh", "-c", dbcommons.InitPrebuiltDbCMD}, - SecurityContext: &corev1.SecurityContext{ - RunAsUser: func() *int64 { i := int64(dbcommons.ORACLE_UID); return &i }(), - RunAsGroup: func() *int64 { i := int64(dbcommons.ORACLE_GUID); return &i }(), - }, - VolumeMounts: []corev1.VolumeMount{{ - MountPath: "/mnt/oradata", - Name: "datafiles-vol", - }}, - Env: []corev1.EnvVar{ - { - Name: "ORACLE_SID", - Value: strings.ToUpper(m.Spec.Sid), - }, + } + if m.Spec.Image.PrebuiltDB { + initContainers = append(initContainers, corev1.Container{ + Name: "init-prebuiltdb", + Image: m.Spec.Image.PullFrom, + Command: []string{"/bin/sh", "-c", dbcommons.InitPrebuiltDbCMD}, + SecurityContext: &corev1.SecurityContext{ + RunAsUser: func() *int64 { i := int64(dbcommons.ORACLE_UID); return &i }(), + RunAsGroup: func() *int64 { i := int64(dbcommons.ORACLE_GUID); return &i }(), + }, + VolumeMounts: []corev1.VolumeMount{{ + MountPath: "/mnt/oradata", + Name: "datafiles-vol", + }}, + Env: []corev1.EnvVar{ + { + Name: "ORACLE_SID", + Value: strings.ToUpper(m.Spec.Sid), }, - }) - } + }, + }) } /* Wallet only for edition barring express and free editions, non-prebuiltDB */ if (m.Spec.Edition != "express" && m.Spec.Edition != "free") && !m.Spec.Image.PrebuiltDB { @@ -2444,17 +2444,17 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat r.Log.Info(out) if err != nil { r.Log.Error(err, err.Error()) - return requeueY, nil + return requeueY, nil } - if (m.Status.TcpsTlsSecret != "" ) || // case when TCPS Secret is changed - (!strings.Contains(out, dbcommons.TlsCertsLocation)) { // if mount is not there in pod - // call deletePods() with zero pods in avaiable and nil readyPod to delete all pods + if (m.Status.TcpsTlsSecret != "") || // case when TCPS Secret is changed + (!strings.Contains(out, dbcommons.TlsCertsLocation)) { // if mount is not there in pod + // call deletePods() with zero pods in avaiable and nil readyPod to delete all pods result, err := r.deletePods(ctx, req, m, []corev1.Pod{}, corev1.Pod{}, 0, 0) if result.Requeue { return result, err } - m.Status.TcpsTlsSecret = "" // to avoid reconciled pod deletions, in case of TCPS secret change and it fails + m.Status.TcpsTlsSecret = "" // to avoid reconciled pod deletions, in case of TCPS secret change and it fails } } From 127f2b4cdc8422cef08c0ccf8a6518a0cc5e233e Mon Sep 17 00:00:00 2001 From: Jyoti Prakash Verma Date: Wed, 13 Mar 2024 20:01:30 -0400 Subject: [PATCH 048/414] pkeyutl secret change --- .../create_kubernetes_secret_for_db_user.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md b/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md index b67a6120..99620f04 100644 --- a/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md +++ b/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md @@ -12,13 +12,11 @@ Please refer the below example for the above steps: ```sh # Create a directory for files for the secret: -mkdir /tmp/.secret_loc/ +rm -rf /tmp/.secrets/ +mkdir /tmp/.secrets/ # Create directories and initialize the variables -PDIR="/tmp/.secret_loc" -RSADIR="${PDIR}"/"rsakey" -rm -rf "${RSADIR}" -mkdir -p "${RSADIR}" +RSADIR="/tmp/.secrets" PRIVKEY="${RSADIR}"/"key.pem" PUBKEY="${RSADIR}"/"key.pub" NAMESPACE="shns" @@ -27,16 +25,15 @@ PWDFILE_ENC="${RSADIR}"/"pwdfile.enc" SECRET_NAME="db-user-pass-rsa" # Generate the RSA Key -cd ${RSADIR} -openssl genrsa -out key.pem -openssl rsa -in key.pem -out key.pub -pubout +openssl genrsa -out "${RSADIR}"/key.pem +openssl rsa -in "${RSADIR}"/key.pem -out "${RSADIR}"/key.pub -pubout # Create a text file with the password rm -f $PWDFILE_ENC echo ORacle_23c > ${RSADIR}/pwdfile.txt # Create encrypted file from the text file using the RSA key -openssl rsautl -in $PWDFILE -out $PWDFILE_ENC -pubin -inkey $PUBKEY -encrypt +openssl pkeyutl -in $PWDFILE -out $PWDFILE_ENC -pubin -inkey $PUBKEY -encrypt # Remove the initial text file: rm -f $PWDFILE @@ -46,4 +43,4 @@ kubectl delete secret $SECRET_NAME -n $NAMESPACE # Create the Kubernetes secret in namespace "NAMESPACE" kubectl create secret generic $SECRET_NAME --from-file=$PWDFILE_ENC --from-file=${PRIVKEY} -n $NAMESPACE -``` +``` \ No newline at end of file From 7c0e4e4d938a3be8c58d64c0e6f9bfb3c1ce3449 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Thu, 14 Mar 2024 14:16:23 +0000 Subject: [PATCH 049/414] Bugs and Doc Fixes for operator release v1.1.0 --- README.md | 10 ++++++- .../samples/sidb/oraclerestdataservice.yaml | 3 +-- .../database/dataguardbroker_controller.go | 1 - .../oraclerestdataservice_controller.go | 1 - .../singleinstancedatabase_controller.go | 1 - docs/sidb/README.md | 6 +++-- oracle-database-operator-nodes-rbac.yaml | 27 +++++++++++++++++++ 7 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 oracle-database-operator-nodes-rbac.yaml diff --git a/README.md b/README.md index 704c9e0b..dac0c6fa 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer Follow the below steps: - 1. Download the [oracle-database-operator.yaml](https://github.com/oracle/oracle-database-operator/blob/main/oracle-database-operator.yaml) file. + 1. Download the [oracle-database-operator.yaml](./oracle-database-operator.yaml) file. 2. Add the comma separated namespaces under `WATCH_NAMESPACE`. For example: ```sh - name: WATCH_NAMESPACE @@ -109,6 +109,14 @@ Oracle strongly recommends that you ensure your system meets the following [Prer ```sh kubectl apply -f shns_binding.yaml ``` + +* ### ClusterRole and ClusterRoleBinding for NodePort services + + For exposing services on each Node's IP and port (the NodePort) apply the [oracle-database-operator-nodes-rbac.yaml](./oracle-database-operator-nodes-rbac.yaml). This is not required for LoadBalancer services. + + ```sh + kubectl apply -f oracle-database-operator-nodes-rbac.yaml + ``` ## Install Oracle DB Operator diff --git a/config/samples/sidb/oraclerestdataservice.yaml b/config/samples/sidb/oraclerestdataservice.yaml index 0cac834d..911a9b1e 100644 --- a/config/samples/sidb/oraclerestdataservice.yaml +++ b/config/samples/sidb/oraclerestdataservice.yaml @@ -41,8 +41,7 @@ spec: keepSecret: true ## ORDS image details - ## Build the ORDS image following instructions at - ## https://github.com/oracle/docker-images/tree/main/OracleRestDataServices + ## Supported ORDS image is container-registry.oracle.com/database/ords:21.4.2-gh image: pullFrom: container-registry.oracle.com/database/ords:21.4.2-gh pullSecrets: diff --git a/controllers/database/dataguardbroker_controller.go b/controllers/database/dataguardbroker_controller.go index 27539082..2a8fe163 100644 --- a/controllers/database/dataguardbroker_controller.go +++ b/controllers/database/dataguardbroker_controller.go @@ -78,7 +78,6 @@ const dataguardBrokerFinalizer = "database.oracle.com/dataguardbrokerfinalizer" //+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers/finalizers,verbs=update //+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch //+kubebuilder:rbac:groups="",resources=events,verbs=create;patch -//+kubebuilder:rbac:groups="",resources=nodes,verbs=watch;list // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. diff --git a/controllers/database/oraclerestdataservice_controller.go b/controllers/database/oraclerestdataservice_controller.go index 48469eab..783ae70c 100644 --- a/controllers/database/oraclerestdataservice_controller.go +++ b/controllers/database/oraclerestdataservice_controller.go @@ -82,7 +82,6 @@ type OracleRestDataServiceReconciler struct { //+kubebuilder:rbac:groups=database.oracle.com,resources=oraclerestdataservices/finalizers,verbs=update //+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch //+kubebuilder:rbac:groups="",resources=events,verbs=create;patch -//+kubebuilder:rbac:groups="",resources=nodes,verbs=watch;list // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 9053d5aa..1ec0a0f0 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -95,7 +95,6 @@ var oemExpressUrl string //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/finalizers,verbs=update //+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch //+kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get;list -//+kubebuilder:rbac:groups="",resources=nodes,verbs=watch;list //+kubebuilder:rbac:groups="",resources=events,verbs=create;patch //+kubebuilder:rbac:groups=storage.k8s.io,resources=storageclasses,verbs=get;list;watch diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 00e1b9c6..9a3c4d2a 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -161,10 +161,12 @@ To provision a new database instance on the Kubernetes cluster, use the example 2. If you have not already done so, create an image pull secret for the Oracle Container Registry: ```sh - $ kubectl create secret docker-registry oracle-container-registry-secret --docker-server=container-registry.oracle.com --docker-username='' --docker-password='' --docker-email='' + $ kubectl create secret docker-registry oracle-container-registry-secret --docker-server=container-registry.oracle.com --docker-username='' --docker-password='' --docker-email='' - secret/oracle-container-registry-secret created + secret/oracle-container-registry-secret created ``` + Note: Generate the auth token from user profile section on top right of the page after logging into container-registry.oracle.com + This secret can also be created from the docker config.json or from podman auth.json after a successful login ```sh docker login container-registry.oracle.com diff --git a/oracle-database-operator-nodes-rbac.yaml b/oracle-database-operator-nodes-rbac.yaml new file mode 100644 index 00000000..9acaf876 --- /dev/null +++ b/oracle-database-operator-nodes-rbac.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role-nodes +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-manager-role-clusterrolebindings +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role-nodes +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- From 131c4bad314d205102ed642fa2a009b5b556456a Mon Sep 17 00:00:00 2001 From: aberinnj Date: Thu, 14 Mar 2024 14:40:21 -0500 Subject: [PATCH 050/414] Fixed BUG-36350588: error handling on CR fetch --- commons/observability/constants.go | 1 + controllers/observability/databaseobserver_controller.go | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/commons/observability/constants.go b/commons/observability/constants.go index 886fdead..c7a15b18 100644 --- a/commons/observability/constants.go +++ b/commons/observability/constants.go @@ -120,6 +120,7 @@ const ( // Log Infos const ( LogCRStart = "Started DatabaseObserver instance reconciliation" + LogCREnd = "Ended DatabaseObserver instance reconciliation, resource must have been deleted." LogResourceCreated = "Created DatabaseObserver resource successfully" LogResourceUpdated = "Updated DatabaseObserver resource successfully" LogResourceFound = "Validated DatabaseObserver resource readiness" diff --git a/controllers/observability/databaseobserver_controller.go b/controllers/observability/databaseobserver_controller.go index 859540ef..b3a1b3df 100644 --- a/controllers/observability/databaseobserver_controller.go +++ b/controllers/observability/databaseobserver_controller.go @@ -91,6 +91,13 @@ func (r *DatabaseObserverReconciler) Reconcile(ctx context.Context, req ctrl.Req api := &apiv1.DatabaseObserver{} if e := r.Get(context.TODO(), req.NamespacedName, api); e != nil { + // if CR is not found or does not exist then + // consider either CR has been deleted + if apiError.IsNotFound(e) { + r.Log.WithName(constants.LogReconcile).Info(constants.LogCREnd) + return ctrl.Result{}, nil + } + r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorCRRetrieve) r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonFailedCRRetrieval, constants.EventMessageFailedCRRetrieval) return ctrl.Result{}, e From aa66dc0e98d1b4f76361fc41365cd4f2806d02ee Mon Sep 17 00:00:00 2001 From: aberinnj Date: Thu, 14 Mar 2024 14:50:34 -0500 Subject: [PATCH 051/414] Updated rbac for observability --- config/rbac/role.yaml | 27 ++++++++++--------- .../databaseobserver_controller.go | 8 +++--- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index fc3616e7..cb2974f6 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -10,10 +10,11 @@ rules: - "" resources: - configmaps - - deployments - events - pods - - secrets + - pods/exec + - pods/log + - replicasets - services verbs: - create @@ -27,12 +28,8 @@ rules: - "" resources: - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services + - namespaces + - secrets verbs: - create - delete @@ -45,7 +42,6 @@ rules: - "" resources: - configmaps - - namespaces - secrets verbs: - create @@ -58,8 +54,10 @@ rules: - apiGroups: - "" resources: - - configmaps - - secrets + - deployments + - events + - pods + - services verbs: - create - delete @@ -121,6 +119,12 @@ rules: - apps resources: - configmaps + verbs: + - get + - list +- apiGroups: + - apps + resources: - deployments - pods - services @@ -502,7 +506,6 @@ rules: - apiGroups: - monitoring.coreos.com resources: - - prometheusrules - servicemonitors verbs: - create diff --git a/controllers/observability/databaseobserver_controller.go b/controllers/observability/databaseobserver_controller.go index 859540ef..dc0a83cc 100644 --- a/controllers/observability/databaseobserver_controller.go +++ b/controllers/observability/databaseobserver_controller.go @@ -70,9 +70,11 @@ type DatabaseObserverReconciler struct { //+kubebuilder:rbac:groups=observability.oracle.com,resources=databaseobservers,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=observability.oracle.com,resources=databaseobservers/status,verbs=get;update;patch //+kubebuilder:rbac:groups=observability.oracle.com,resources=databaseobservers/finalizers,verbs=update -//+kubebuilder:rbac:groups=apps,resources=pods;deployments;services;configmaps,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups="",resources=pods;deployments;services;secrets;configmaps;events,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=monitoring.coreos.com,resources=prometheusrules;servicemonitors,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=apps,resources=pods;deployments;services,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=apps,resources=configmaps,verbs=get;list +//+kubebuilder:rbac:groups="",resources=pods;deployments;services;events,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="",resources=secrets;configmaps,verbs=get;list +//+kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. From 0cefe34d56fc939968142bf07334d6af1c4a8e93 Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Fri, 15 Mar 2024 13:54:43 +0000 Subject: [PATCH 052/414] Rvats bugfixes --- .../database/singleinstancedatabase_controller.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 1ec0a0f0..f7428ea6 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -2426,8 +2426,10 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat (m.Spec.TcpsTlsSecret == "" && m.Status.TcpsTlsSecret != "") || // TCPS Secret is removed in spec (m.Spec.TcpsTlsSecret != "" && m.Status.TcpsTlsSecret != "" && m.Spec.TcpsTlsSecret != m.Status.TcpsTlsSecret)) { //TCPS secret is changed - // Enable TCPS - m.Status.Status = dbcommons.StatusUpdating + // Set status to Updating, except when an error has been thrown from configTCPS script + if m.Status.Status != dbcommons.StatusError { + m.Status.Status = dbcommons.StatusUpdating + } r.Status().Update(ctx, m) eventMsg := "Enabling TCPS in the database..." @@ -2437,6 +2439,7 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat if m.Spec.TcpsTlsSecret != "" { // case when tls secret is either added or changed TcpsCommand = "export TCPS_CERTS_LOCATION=" + dbcommons.TlsCertsLocation + " && " + dbcommons.EnableTcpsCMD + // Checking for tls-secret mount in pods out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf(dbcommons.PodMountsCmd, dbcommons.TlsCertsLocation)) r.Log.Info("Mount Check Output") @@ -2457,12 +2460,15 @@ func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDat } } + // Enable TCPS out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", TcpsCommand) if err != nil { r.Log.Error(err, err.Error()) eventMsg = "Error encountered in enabling TCPS!" r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + m.Status.Status = dbcommons.StatusError + r.Status().Update(ctx, m) return requeueY, nil } r.Log.Info("enableTcps Output : \n" + out) From 088c42bf01d1aad5e21e01367d3aeae8ccc46abd Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 15 Mar 2024 14:59:16 +0000 Subject: [PATCH 053/414] Bug 36407635 - NULL POINTER IN SETDBCSSTATUS CAUSES OPERATOR TO CRASH --- apis/database/v1alpha1/dbcssystem_types.go | 5 +- commons/dbcssystem/dbcs_reconciler.go | 5 + .../bases/database.oracle.com_DbcsSystem.yaml | 240 ++++++++++++++++++ config/rbac/role.yaml | 7 - 4 files changed, 248 insertions(+), 9 deletions(-) create mode 100644 config/crd/bases/database.oracle.com_DbcsSystem.yaml diff --git a/apis/database/v1alpha1/dbcssystem_types.go b/apis/database/v1alpha1/dbcssystem_types.go index 28773fb2..37e80a6b 100644 --- a/apis/database/v1alpha1/dbcssystem_types.go +++ b/apis/database/v1alpha1/dbcssystem_types.go @@ -156,8 +156,9 @@ type VmNetworkDetails struct { NetworkSG string `json:"networkSG,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=DbcsSystem,scope=Namespaced // DbcsSystem is the Schema for the dbcssystems API type DbcsSystem struct { diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 34363bfe..70e50294 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -536,6 +536,11 @@ func GetResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id s func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { + if dbcs.Spec.Id == nil { + dbcs.Status.State = "FAILED" + return nil + } + dbcsId := *dbcs.Spec.Id dbcsReq := database.GetDbSystemRequest{ diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml new file mode 100644 index 00000000..e933d5a4 --- /dev/null +++ b/config/crd/bases/database.oracle.com_DbcsSystem.yaml @@ -0,0 +1,240 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: DbcsSystem.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: DbcsSystem + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: DbcsSystem is the Schema for the dbcssystems API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DbcsSystemSpec defines the desired state of DbcsSystem + properties: + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + description: DB Backup COnfig Network Struct + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - sshPublicKeys + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + ociConfigMap: + type: string + ociSecret: + type: string + required: + - ociConfigMap + type: object + status: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbEdition: + type: string + dbInfo: + items: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index fc3616e7..1b56ade8 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -75,13 +75,6 @@ rules: verbs: - create - patch -- apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch - apiGroups: - "" resources: From a25e5c66610ee1cabec227f4b02aa20eb37f409a Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 15 Mar 2024 18:16:33 +0000 Subject: [PATCH 054/414] dbcsdoc usecase01 --- docs/dbcs/usecase01/README.md | 199 ++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 docs/dbcs/usecase01/README.md diff --git a/docs/dbcs/usecase01/README.md b/docs/dbcs/usecase01/README.md new file mode 100644 index 00000000..4349e211 --- /dev/null +++ b/docs/dbcs/usecase01/README.md @@ -0,0 +1,199 @@ + +# Makefile for the dbcs automation creation + +This [makefile](#makefile) helps to speed up the **DBCS** creation. Edit all the credentials related to your tenancy in the configmap target section and update the **NAMESPACE** variable. Specify the oci pem key that you have created during ocicli configuration **OCIPEM** + +```makefile +[...] +ONAMESPACE=oracle-database-operator-system +NAMESPACE=[MY_NAMESPACE] +OCIPEN=[PATH_TO_OCI_API_KEY_PEM] +[...] +configmap: + $(KUBECTL) create configmap oci-cred \ + --from-literal=tenancy=[MY_TENANCY_ID] + --from-literal=user=[MY_USER_ID] \ + --from-literal=fingerprint=[MY_FINGER_PRINT] \ + --from-literal=region=[MY_REGION] -n $(NAMESPACE) + +[...] +``` +Specify the admin password and the tde password in adminpass and tdepass + +```makefile +adminpass: + echo "[SPECIFY_PASSWORD_HERE]" > ./admin-password + $(KUBECTL) create secret generic admin-password --from-file=./admin-password -n $(NAMESPACE) + $(RM) ./admin-password + +tdepass: + echo "[SPECIFY_PASSWORD_HERE]" > ./tde-password + $(KUBECTL) create secret generic tde-password --from-file=./tde-password -n $(NAMESPACE) + $(RM) ./tde-password +``` + +Execute the following targets step1 step2 step3 step4 step5 to setup secrets and certificates. + +```bash +make step1 +make step2 +make step3 +make step4 +make step5 +``` + +Create the file **dbcs_service_with_minimal_parameters.yaml** + +```yaml +apiVersion: database.oracle.com/v1alpha1 +kind: DbcsSystem +metadata: + name: dbcssystem-create + namespace: [MY_NAMESPACE] +spec: + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + dbSystem: + availabilityDomain: "OLou:EU-MILAN-1-AD-1" + compartmentId: "[MY_COMPARTMENT_ID]" + dbAdminPaswordSecret: "admin-password" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "testdb" + displayName: "dbsystem_example" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "19c" + dbWorkload: "OLTP" + hostName: "host_example_1205" + shape: "VM.Standard2.1" + domain: "example.com" + sshPublicKeys: + - "oci-publickey" + subnetId: "[MY_SUBNET_ID]" + +``` + +Execute the target make file create **make create** or apply directly the above yaml file **kubectl apply -f dbcs_service_with_minimal_parameters.yaml** to create DBCS . Verify the DBCS creation by executing **kubectl get DbcsSystem -n [MY_NAMESPACE]** + +``` +kubectl get DbcsSystem -n [MY_NAMESPACE] +NAME AGE +dbcssystem-create 52m +``` +Use the describe command to verify the status and the attributes of the dbcs system created + +```bash +kubectl describe DbcsSystem dbcssystem-create -n [...] +``` +```text +Name: dbcssystem-create +Namespace: pdbnamespace +Labels: +Annotations: kubectl.kubernetes.io/last-applied-configuration: + {"apiVersion":"database.oracle.com/v1alpha1","kind":"DbcsSystem","metadata":{"annotations":{},"name":"dbcssystem-create","namespace":"pdbn...}} + +API Version: database.oracle.com/v1alpha1 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2024-03-15T14:53:02Z + + Db System: + Availability Domain: OLou:EU-MILAN-1-AD-1 + Compartment Id: [MY_COMPARTMENT_ID] + Db Admin Pasword Secret: admin-password + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Name: testdb + Db Version: 19c + Db Workload: OLTP + Display Name: "dbsystem_example" + Domain: example.com + Host Name: host_example_1205 + License Model: BRING_YOUR_OWN_LICENSE + Shape: VM.Standard2.1 + Ssh Public Keys: + oci-publickey + Subnet Id: [MY_SUBNET_ID] + Oci Config Map: oci-cred + Oci Secret: oci-privatekey +Status: + Availability Domain: OLou:EU-MILAN-1-AD-1 + Cpu Core Count: 1 +``` +## makefile + +```Makefile +ONAMESPACE=oracle-database-operator-system +NAMESPACE=[MY_NAMESPACE] +OCIPEN=[PATH_TO_OCI_API_KEY_PEM] +KUBECTL=/usr/bin/kubectl +CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml +RM=/usr/bin/rm + +certmanager: + $(KUBECTL) apply -f $(CERTMANAGER) + +prereq: step1 step2 step3 step4 step5 + +step1: configmap +step2: ociprivkey +step3: adminpass +step4: tdepass +step5: ocipubkey + + +configmap: + $(KUBECTL) create configmap oci-cred \ + --from-literal=tenancy=[MY_TENANCY_ID] + --from-literal=user=[MY_USER_ID] \ + --from-literal=fingerprint=[MY_FINGER_PRINT] \ + --from-literal=region=[MY_REGION] -n $(NAMESPACE) + +ociprivkey: + $(KUBECTL) create secret generic oci-privatekey --from-file=privatekey=[PATH_TO_OCI_API_KEY_PEM] -n $(NAMESPACE) + +adminpass: + echo "WElcome_12##" > ./admin-password + $(KUBECTL) create secret generic admin-password --from-file=./admin-password -n $(NAMESPACE) + $(RM) ./admin-password + +tdepass: + echo "WElcome_12##" > ./tde-password + $(KUBECTL) create secret generic tde-password --from-file=./tde-password -n $(NAMESPACE) + $(RM) ./tde-password + +ocipubkey: + #ssh-keygen -N "" -C "DBCS_System"-`date +%Y%m` -P "" + $(KUBECTL) create secret generic oci-publickey --from-file=publickey=/home/oracle/.ssh/id_rsa.pub -n $(NAMESPACE) + +clean: delprivkey delpubkey deladminpass delconfigmap deltdepass + +delconfigmap: + $(KUBECTL) delete configmap oci-cred -n $(NAMESPACE) +delprivkey: + $(KUBECTL) delete secret oci-privatekey -n $(NAMESPACE) +delpubkey: + $(KUBECTL) delete secret oci-publickey -n $(NAMESPACE) +deltdepass: + $(KUBECTL) delete secret tde-password -n $(NAMESPACE) +deladminpass: + $(KUBECTL) delete secret admin-password -n $(NAMESPACE) +checkmap: + $(KUBECTL) get configmaps oci-cred -o yaml -n $(NAMESPACE) |grep -A 5 -B 2 "^data:" +checkdbcs: + $(KUBECTL) describe dbcssystems.database.oracle.com dbcssystem-create -n $(NAMESPACE) +getall: + $(KUBECTL) get all -n $(NAMESPACE) +getmaps: + $(KUBECTL) get configmaps oci-cred -n $(NAMESPACE) -o yaml +descdbcss: + $(KUBECTL) describe dbcssystems.database.oracle.com dbcssystem-create -n $(NAMESPACE) +getdbcs: + $(KUBECTL) get DbcsSystem -n $(NAMESPACE) +create: + $(KUBECTL) apply -f dbcs_service_with_minimal_parameters.yaml -n $(NAMESPACE) +xlog1: + $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(ONAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(ONAMESPACE) +xlog2: + $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(ONAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1` -n $(ONAMESPACE) +xlog3: + $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(ONAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(ONAMESPACE) +``` \ No newline at end of file From b075393619f732545e16973854e07e951d3ad27b Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Fri, 15 Mar 2024 20:23:19 +0000 Subject: [PATCH 055/414] Tinglwan adb multinamespace --- .../v1alpha1/autonomousdatabaserestore_webhook.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go index 3f24f37d..4f96bd7b 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -71,6 +72,19 @@ func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) var allErrs field.ErrorList + namespaces := dbcommons.GetWatchNamespaces() + _, hasEmptyString := namespaces[""] + isClusterScoped := len(namespaces) == 1 && hasEmptyString + if !isClusterScoped { + _, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + if len(namespaces) != 0 && !containsNamespace { + allErrs = append(allErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + } + // Validate the target ADB if r.Spec.Target.K8sADB.Name == nil && r.Spec.Target.OCIADB.OCID == nil { allErrs = append(allErrs, From b61f5401a6b8fa526e4b5d1408d0bfb315b7fa0f Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Tue, 19 Mar 2024 06:53:40 +0000 Subject: [PATCH 056/414] Dg tcps validation --- .../v1alpha1/singleinstancedatabase_webhook.go | 13 +++++++++---- docs/sidb/README.md | 3 ++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 9779b018..adf2d76e 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -467,10 +467,15 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) } } - // if Db is in a dataguard configuration. Restrict enabling Tcps on the Primary DB - if old.Status.DgBrokerConfigured && r.Spec.EnableTCPS { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("enableTCPS"), "cannot enable tcps as database is in a dataguard configuration")) + // if Db is in a dataguard configuration or referred by Standby databases then Restrict enabling Tcps on the Primary DB + if r.Spec.EnableTCPS { + if old.Status.DgBrokerConfigured { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("enableTCPS"), "cannot enable tcps as database is in a dataguard configuration")) + } else if len(old.Status.StandbyDatabases) != 0 { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("enableTCPS"), "cannot enable tcps as database is referred by one or more standby databases")) + } } if old.Status.DatafilesCreated == "true" && (old.Status.PrebuiltDB != r.Spec.Image.PrebuiltDB) { diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 9a3c4d2a..b6293801 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -619,7 +619,8 @@ In case of `NodePort` service, `listenerPort`, and `tcpsListenerPort` will be th ### Create a Standby Database #### Prerequisites -Before creating a standby, ArchiveLog, FlashBack, and ForceLog on primary Single Instance Database(`.spec.primaryDatabaseRef`) should be turned on. +- Before creating a Standby, ensure that ArchiveLog, FlashBack, and ForceLog on primary Single Instance Database(`.spec.primaryDatabaseRef`) are turned on. +- Standby database is not supported for TCPS enabled Primary databases. #### Template YAML To create a standby database, edit and apply the sample yaml file [config/samples/sidb/singleinstancedatabase_standby.yaml](../../config/samples/sidb/singleinstancedatabase_standby.yaml). From 4f17dcd9d6e4447a4f44ff4faa5bfd778f56c6d3 Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Wed, 20 Mar 2024 08:37:30 +0000 Subject: [PATCH 057/414] standby tcps validation --- apis/database/v1alpha1/singleinstancedatabase_webhook.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index adf2d76e..8cf6f75a 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -208,6 +208,12 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { field.Invalid(field.NewPath("spec").Child("persistence").Child("scriptsVolumeName"), r.Spec.Persistence.ScriptsVolumeName, "scriptsVolumeName cannot be specified for standby databases")) } + if r.Spec.EnableTCPS { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("enableTCPS"), + r.Spec.EnableTCPS, "enableTCPS cannot be specified for standby databases")) + } + } // Replica validation From 375a5878603bf63e627f0d00c9a551da745adf4a Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Wed, 20 Mar 2024 11:36:50 +0000 Subject: [PATCH 058/414] Documentation Changes for v1.1.0 release --- README.md | 90 +++++++++---------- docs/sidb/README.md | 22 ++++- rbac/cluster-role-binding.yaml | 15 ++++ rbac/default-ns-role-binding.yaml | 15 ++++ .../node-rbac.yaml | 6 +- rbac/persistent-volume-rbac.yaml | 27 ++++++ rbac/storage-class-rbac.yaml | 28 ++++++ 7 files changed, 149 insertions(+), 54 deletions(-) create mode 100644 rbac/cluster-role-binding.yaml create mode 100644 rbac/default-ns-role-binding.yaml rename oracle-database-operator-nodes-rbac.yaml => rbac/node-rbac.yaml (69%) create mode 100644 rbac/persistent-volume-rbac.yaml create mode 100644 rbac/storage-class-rbac.yaml diff --git a/README.md b/README.md index dac0c6fa..cb9e0fcf 100644 --- a/README.md +++ b/README.md @@ -63,59 +63,53 @@ Oracle strongly recommends that you ensure your system meets the following [Prer * ### Role Binding for access management - When the Oracle DB Operator is operating with multiple namespaces and access management is required, you need to complete the following additional steps: - - Have the Oralce DB Operator monitor only certain defined namespaces. - - Apply role binding for those namespaces for access control. - - Follow the below steps: - - 1. Download the [oracle-database-operator.yaml](./oracle-database-operator.yaml) file. - 2. Add the comma separated namespaces under `WATCH_NAMESPACE`. For example: - ```sh - - name: WATCH_NAMESPACE - value: "oracle-database-operator-system,shns" - ``` - This is needed when you want the DB Operator to monitor `shns` and `oracle-database-operator-system` namespaces. - - If you are going to work with more namespaces, then you will need to add them under `WATCH_NAMESPACE`. - - Save the `oracle-database-operator-system.yaml` file after this change. - 3. Create a role binding file for the namespace you are going to work in. - - In this case, its the namespace `shns` and you need to create a file like below for it: - - ```sh - --- - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: shns - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role - subjects: - - kind: ServiceAccount - name: default - namespace: oracle-database-operator-system - ``` - - If you are testing with multiple namespaces, make sure you create that many binding files and apply them before applying `oracle-database-operator.yaml`. - - NOTE: You need to change the namespace in that binding file from `shns` and also change the binding name `oracle-database-operator-oracle-database-operator-manager-rolebinding1` to a new binding name. - - 4. Apply the role binding file like below: - ```sh - kubectl apply -f shns_binding.yaml - ``` + OraOperator supports the following two modes of deployment: + ##### 1. Cluster Scope Deployment + + This is the default mode wherein OraOperator is deployed to operate in a cluster scope and watch all the namespaces cluster wide + + - Grant the `serviceaccount:oracle-database-operator-system:default` cluster wide access for the resources by applying [cluster-role-binding.yaml](./rbac/cluster-role-binding.yaml) + + ```sh + kubectl apply -f cluster-role-binding.yaml + ``` + + - Then apply the [oracle-database-operator.yaml](./oracle-database-operator.yaml) to deploy the Operator + + ```sh + kubectl apply -f oracle-database-operator.yaml + ``` + + ##### 2. Namespaced Scope Deployment + + In this mode OraOperator can be deployed to operate in a namespaced scope and watch one or many namespaces + + - Grant `serviceaccount:oracle-database-operator-system:default` service account with resource access in the required namespaces. For example, to watch only the default namespace, apply the [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) + + ```sh + kubectl apply -f default-ns-role-binding.yaml + ``` + For watching additional namespaces, create different role binding files for each namespace taking [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) as a template and changing the `metadata.name` and `metadata.namespace` fields + + - Now edit the [oracle-database-operator.yaml](./oracle-database-operator.yaml) to add the required namespaces under `WATCH_NAMESPACE`. Use comma separated values for multiple namespaces + + ```sh + - name: WATCH_NAMESPACE + value: "default" + ``` + - Then apply the edited [oracle-database-operator.yaml](./oracle-database-operator.yaml) to deploy the Operator + + ```sh + kubectl apply -f oracle-database-operator.yaml + ``` + * ### ClusterRole and ClusterRoleBinding for NodePort services - For exposing services on each Node's IP and port (the NodePort) apply the [oracle-database-operator-nodes-rbac.yaml](./oracle-database-operator-nodes-rbac.yaml). This is not required for LoadBalancer services. + For exposing services on each Node's IP and port (the NodePort) apply the [node-rbac.yaml](./rbac/node-rbac.yaml). This is not required for LoadBalancer services. ```sh - kubectl apply -f oracle-database-operator-nodes-rbac.yaml + kubectl apply -f manager-role-nodes-rbac.yaml ``` ## Install Oracle DB Operator diff --git a/docs/sidb/README.md b/docs/sidb/README.md index b6293801..3c4d0ed9 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -47,7 +47,20 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst ## Prerequisites -Oracle strongly recommends that you follow the [prerequisites](./PREREQUISITES.md). +* Oracle strongly recommends that you follow the [prerequisites](./PREREQUISITES.md) +* For managing the required levels of access, configure [role binding](../../README.md#role-binding-for-access-management) +* For exposing the database via Nodeport services, apply [RBAC](../../rbac/node-rbac.yaml) + ```sh + kubectl apply -f rbac/node-rbac.yaml + ``` +* For automatic storage expansion of block volumes, apply [RBAC](../../rbac/storage-class-rbac.yaml) + ```sh + kubectl apply -f rbac/storage-class-rbac.yaml + ``` +* For automatic execution of custom scripts post database setup or startup, apply [RBAC](../../rbac/persistent-volume-rbac.yaml) + ```sh + kubectl apply -f rbac/persistent-volume-rbac.yaml + ``` ## SingleInstanceDatabase Resource @@ -306,7 +319,8 @@ $ kubectl patch singleinstancedatabase sidb-sample -p '{"spec":{"persistence":{" ``` **Note:** -- For storage expansion to work, the storage class should have been configured to `allowVolumeExpansion:true` +- Storage expansion requires the storage class to be configured with `allowVolumeExpansion:true` +- Storage expansion requires read and watch access for storage account as mentioned in [prerequisites](#prerequisites) - User can only scale up a volume/storage and not scale down #### Static Persistence @@ -791,7 +805,7 @@ $ kubectl --type=merge -p '{"spec":{"setAsPrimaryDatabase":"ORCLS1"}}' patch dat ### Patch Primary and Standby databases in Data Guard configuration -Databases (both primary and standby) running in you cluster and managed by the Oracle Database operator can be patched or rolled back between release updates of the same major release. While patching databases configured with the dataguard broker you need to first patch the Primary database followed by seconday/standby databases in any order. +Databases (both primary and standby) running in you cluster and managed by the Oracle Database operator can be patched or rolled back between release updates of the same major release. While patching databases configured with the dataguard broker you need to first patch the Primary database followed by Standby databases in any order. To patch an existing database, edit and apply the **[config/samples/sidb/singleinstancedatabase_patch.yaml](../../config/samples/sidb/singleinstancedatabase_patch.yaml)** file of the database resource/object either by specifying a new release update for image attributes, or by running the following command: @@ -825,6 +839,8 @@ Custom scripts (sql and/or shell scripts) can be executed after the initial data Create a persistent volume using [static provisioning](#static-persistence) and then specify the name of this volume with the `<.spec.persistence.scriptsVolumeName>` field which corresponds to the `scriptsVolumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. +**Note:** +- Executing custom scripts requires read and list access for persistent volumes as mentioned in [prerequisites](#prerequisites) ## OracleRestDataService Resource diff --git a/rbac/cluster-role-binding.yaml b/rbac/cluster-role-binding.yaml new file mode 100644 index 00000000..1c609012 --- /dev/null +++ b/rbac/cluster-role-binding.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- diff --git a/rbac/default-ns-role-binding.yaml b/rbac/default-ns-role-binding.yaml new file mode 100644 index 00000000..b737e1f1 --- /dev/null +++ b/rbac/default-ns-role-binding.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- diff --git a/oracle-database-operator-nodes-rbac.yaml b/rbac/node-rbac.yaml similarity index 69% rename from oracle-database-operator-nodes-rbac.yaml rename to rbac/node-rbac.yaml index 9acaf876..ac474873 100644 --- a/oracle-database-operator-nodes-rbac.yaml +++ b/rbac/node-rbac.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: oracle-database-operator-manager-role-nodes + name: oracle-database-operator-manager-role-node rules: - apiGroups: - "" @@ -15,11 +15,11 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: oracle-database-operator-manager-role-clusterrolebindings + name: oracle-database-operator-manager-role-node-cluster-role-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: oracle-database-operator-manager-role-nodes + name: oracle-database-operator-manager-role-node subjects: - kind: ServiceAccount name: default diff --git a/rbac/persistent-volume-rbac.yaml b/rbac/persistent-volume-rbac.yaml new file mode 100644 index 00000000..fbf7cb98 --- /dev/null +++ b/rbac/persistent-volume-rbac.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role-persistent-volume +rules: +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-manager-role-persistent-volume-cluster-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role-persistent-volume +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- diff --git a/rbac/storage-class-rbac.yaml b/rbac/storage-class-rbac.yaml new file mode 100644 index 00000000..a34f67d4 --- /dev/null +++ b/rbac/storage-class-rbac.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role-storage-class +rules: +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-manager-role-storage-class-cluster-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role-storage-class +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- From 1a7ef86748fc303b2992b0398d59817ddeb0a26a Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Wed, 20 Mar 2024 12:22:19 +0000 Subject: [PATCH 059/414] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cb9e0fcf..a94c5b2f 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer - Grant the `serviceaccount:oracle-database-operator-system:default` cluster wide access for the resources by applying [cluster-role-binding.yaml](./rbac/cluster-role-binding.yaml) ```sh - kubectl apply -f cluster-role-binding.yaml + kubectl apply -f rbac/cluster-role-binding.yaml ``` - Then apply the [oracle-database-operator.yaml](./oracle-database-operator.yaml) to deploy the Operator @@ -87,7 +87,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer - Grant `serviceaccount:oracle-database-operator-system:default` service account with resource access in the required namespaces. For example, to watch only the default namespace, apply the [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) ```sh - kubectl apply -f default-ns-role-binding.yaml + kubectl apply -f rbac/default-ns-role-binding.yaml ``` For watching additional namespaces, create different role binding files for each namespace taking [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) as a template and changing the `metadata.name` and `metadata.namespace` fields @@ -109,7 +109,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer For exposing services on each Node's IP and port (the NodePort) apply the [node-rbac.yaml](./rbac/node-rbac.yaml). This is not required for LoadBalancer services. ```sh - kubectl apply -f manager-role-nodes-rbac.yaml + kubectl apply -f rbac/node-rbac.yaml ``` ## Install Oracle DB Operator From 14edae396a4328635877ba7e27647ee1a8742742 Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Thu, 21 Mar 2024 12:14:49 +0530 Subject: [PATCH 060/414] Fix PV RBAC --- controllers/database/singleinstancedatabase_controller.go | 2 +- rbac/persistent-volume-rbac.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index f7428ea6..61592622 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -94,7 +94,7 @@ var oemExpressUrl string //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/finalizers,verbs=update //+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch -//+kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get;list +//+kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get;list;watch //+kubebuilder:rbac:groups="",resources=events,verbs=create;patch //+kubebuilder:rbac:groups=storage.k8s.io,resources=storageclasses,verbs=get;list;watch diff --git a/rbac/persistent-volume-rbac.yaml b/rbac/persistent-volume-rbac.yaml index fbf7cb98..bce9733d 100644 --- a/rbac/persistent-volume-rbac.yaml +++ b/rbac/persistent-volume-rbac.yaml @@ -11,6 +11,7 @@ rules: verbs: - get - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding From e5274d35b29722fd155b1132dbca04c9b30d2d93 Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Thu, 21 Mar 2024 12:42:47 +0530 Subject: [PATCH 061/414] Same yaml fixes --- config/samples/sidb/singleinstancedatabase.yaml | 15 +++++++-------- .../sidb/singleinstancedatabase_clone.yaml | 2 +- .../sidb/singleinstancedatabase_standby.yaml | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index f1059494..60d02dfb 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -13,31 +13,30 @@ spec: sid: ORCL1 ## Type of database. - ## Valid values for createAs are primary, standby or clone + ## Valid values for createAs are primary, clone or standby createAs: primary ## Reference to a source primary database. ## Valid for standby and clone databases. - ## Format: 1. Intra-cluster: you can give name of the primary database or the database connect string in `:/` format - ## 2. Inter-cluster: Database connect string in `:/` format + ## The name of the primary database resource from the same namespace primaryDatabaseRef: "" - ## DB edition. N/A if cloning from a Source DB in current K8s cluster (if cloneFrom is set to a database ref) + ## DB edition. N/A for createAs clone or standby ## Valid values for edition are enterprise, standard or express edition: enterprise ## Secret containing SIDB password mapped to secretKey. secretKey defaults to oracle_pwd - ## Should refer to adminPassword of Source DB if cloning from a Source DB (i.e if cloneFrom is set) + ## Should refer to adminPassword of Source DB if createAs is standby or clone ## This secret will be deleted after creation of the database unless keepSecret is set to true which is the default adminPassword: secretName: secretKey: keepSecret: true - ## DB character set. N/A if cloning from a Source DB (if cloneFrom is set) + ## DB character set. N/A for createAs clone or standby charset: AL32UTF8 - ## PDB name. N/A if cloning from a Source DB (if cloneFrom is set) + ## PDB name. N/A for createAs clone or standby pdbName: orclpdb1 ## Enable/Disable Flashback @@ -63,7 +62,7 @@ spec: ## If this field is commented out/removed from the yaml, it will disable the auto-renewal feature for TCPS certificate tcpsCertRenewInterval: 8760h - ## NA if cloning from a SourceDB (cloneFrom is set) + ## N/A for createAs clone or standby ## Specify both sgaSize and pgaSize (in MB) or dont specify both ## Specify Non-Zero value to use ## You cannot change these initParams for Oracle Database Express (XE) edition diff --git a/config/samples/sidb/singleinstancedatabase_clone.yaml b/config/samples/sidb/singleinstancedatabase_clone.yaml index 9b60e4ba..eb91f5ed 100644 --- a/config/samples/sidb/singleinstancedatabase_clone.yaml +++ b/config/samples/sidb/singleinstancedatabase_clone.yaml @@ -13,7 +13,7 @@ spec: ## Use only alphanumeric characters for sid sid: ORCL2 - ## A source database ref to clone from. + ## The name of the source primary database resource to clone from the same namespace ## Make sure the source database has been created by applying singeinstancedatabase_create.yaml primaryDatabaseRef: sidb-sample diff --git a/config/samples/sidb/singleinstancedatabase_standby.yaml b/config/samples/sidb/singleinstancedatabase_standby.yaml index d5f2c04a..529e3e14 100644 --- a/config/samples/sidb/singleinstancedatabase_standby.yaml +++ b/config/samples/sidb/singleinstancedatabase_standby.yaml @@ -15,7 +15,7 @@ spec: ## Use only alphanumeric characters for sid sid: ORCLS - ## Reference to a source primary database name + ## The name of the source primary database resource from the same namespace primaryDatabaseRef: "sidb-sample" ## Intended type of database. From 23f18cfd92f15e17587c158ca5b6eaa8c15ec515 Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Thu, 21 Mar 2024 14:34:17 +0530 Subject: [PATCH 062/414] Fix comments --- config/samples/sidb/singleinstancedatabase.yaml | 6 +++--- config/samples/sidb/singleinstancedatabase_clone.yaml | 2 +- config/samples/sidb/singleinstancedatabase_standby.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 60d02dfb..c61f17fe 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -17,8 +17,8 @@ spec: createAs: primary ## Reference to a source primary database. - ## Valid for standby and clone databases. - ## The name of the primary database resource from the same namespace + ## Valid only for createAs clone or standby + ## The name of a source primary database resource from the same namespace primaryDatabaseRef: "" ## DB edition. N/A for createAs clone or standby @@ -26,7 +26,7 @@ spec: edition: enterprise ## Secret containing SIDB password mapped to secretKey. secretKey defaults to oracle_pwd - ## Should refer to adminPassword of Source DB if createAs is standby or clone + ## Should refer to adminPassword of Source DB if createAs is clone or standby ## This secret will be deleted after creation of the database unless keepSecret is set to true which is the default adminPassword: secretName: diff --git a/config/samples/sidb/singleinstancedatabase_clone.yaml b/config/samples/sidb/singleinstancedatabase_clone.yaml index eb91f5ed..f25484d9 100644 --- a/config/samples/sidb/singleinstancedatabase_clone.yaml +++ b/config/samples/sidb/singleinstancedatabase_clone.yaml @@ -13,7 +13,7 @@ spec: ## Use only alphanumeric characters for sid sid: ORCL2 - ## The name of the source primary database resource to clone from the same namespace + ## The name of a source primary database resource to clone from the same namespace ## Make sure the source database has been created by applying singeinstancedatabase_create.yaml primaryDatabaseRef: sidb-sample diff --git a/config/samples/sidb/singleinstancedatabase_standby.yaml b/config/samples/sidb/singleinstancedatabase_standby.yaml index 529e3e14..0674f838 100644 --- a/config/samples/sidb/singleinstancedatabase_standby.yaml +++ b/config/samples/sidb/singleinstancedatabase_standby.yaml @@ -15,7 +15,7 @@ spec: ## Use only alphanumeric characters for sid sid: ORCLS - ## The name of the source primary database resource from the same namespace + ## The name of a source primary database resource from the same namespace primaryDatabaseRef: "sidb-sample" ## Intended type of database. From 7639531b8df008b5dea8fb7009d901424ffcc027 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Thu, 21 Mar 2024 09:25:27 +0000 Subject: [PATCH 063/414] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a94c5b2f..c30fe4a8 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer Install the certificate manager with the following command: ```sh - kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml + kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml ``` * ### Role Binding for access management From 8391a574ef29e17f5b54f158b657238276b95066 Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Thu, 21 Mar 2024 17:56:51 +0000 Subject: [PATCH 064/414] Psaini sharding branch1 --- PROJECT | 8 + .../v1alpha1/shardingdatabase_webhook.go | 155 ++++++++++++++++++ commons/sharding/catalog.go | 3 +- config/samples/kustomization.yaml | 1 + .../database/shardingdatabase_controller.go | 26 --- docs/sharding/README.md | 17 ++ ...y_cloning_db_from_gold_image_across_ads.md | 55 +++++++ ...ing_by_cloning_db_gold_image_in_same_ad.md | 51 ++++++ ..._provisioning_with_control_on_resources.md | 44 +++++ ...ith_notification_using_oci_notification.md | 84 ++++++++++ ...ding_provisioning_without_db_gold_image.md | 39 +++++ ...rding_scale_in_delete_an_existing_shard.md | 49 ++++++ .../snr_ssharding_scale_out_add_shards.md | 36 ++++ .../snr_ssharding_shard_prov.yaml | 58 +++++++ .../snr_ssharding_shard_prov_clone.yaml | 83 ++++++++++ ...ssharding_shard_prov_clone_across_ads.yaml | 91 ++++++++++ .../snr_ssharding_shard_prov_delshard.yaml | 69 ++++++++ .../snr_ssharding_shard_prov_extshard.yaml | 68 ++++++++ .../snr_ssharding_shard_prov_memory_cpu.yaml | 89 ++++++++++ ...sharding_shard_prov_send_notification.yaml | 85 ++++++++++ ...ssharding_shard_prov_clone_across_ads.yaml | 8 + ...dsharding_shard_prov_clone_across_ads.yaml | 8 + main.go | 4 + 23 files changed, 1103 insertions(+), 28 deletions(-) create mode 100644 apis/database/v1alpha1/shardingdatabase_webhook.go create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml diff --git a/PROJECT b/PROJECT index 5cbb036c..d9d026ef 100644 --- a/PROJECT +++ b/PROJECT @@ -1,3 +1,7 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html domain: oracle.com layout: - go.kubebuilder.io/v2 @@ -67,6 +71,10 @@ resources: kind: ShardingDatabase path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1beta1 - api: crdVersion: v1 namespaced: true diff --git a/apis/database/v1alpha1/shardingdatabase_webhook.go b/apis/database/v1alpha1/shardingdatabase_webhook.go new file mode 100644 index 00000000..48b2f784 --- /dev/null +++ b/apis/database/v1alpha1/shardingdatabase_webhook.go @@ -0,0 +1,155 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1alpha1 + +import ( + "strings" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var shardingdatabaselog = logf.Log.WithName("shardingdatabase-resource") + +func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-shardingdatabase,mutating=true,failurePolicy=fail,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v1alpha1,name=mshardingdatabase.kb.io + +var _ webhook.Defaulter = &ShardingDatabase{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *ShardingDatabase) Default() { + shardingdatabaselog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. + if r.Spec.GsmDevMode != "" { + r.Spec.GsmDevMode = "dev" + } +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-shardingdatabase,mutating=false,failurePolicy=fail,groups=database.oracle.com,resources=shardingdatabases,versions=v1alpha1,name=vshardingdatabase.kb.io,admissionReviewVersions={v1,v1beta1} + +var _ webhook.Validator = &ShardingDatabase{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { + shardingdatabaselog.Info("validate create", "name", r.Name) + + // TODO(user): fill in your validation logic upon object creation. + // Check Secret configuration + var validationErr field.ErrorList + + //namespaces := db.GetWatchNamespaces() + //_, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + // if len(namespaces) != 0 && !containsNamespace { + // validationErr = append(validationErr, + // field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + // "Oracle database operator doesn't watch over this namespace")) + //} + + if r.Spec.DbSecret == nil { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret"), r.Spec.DbSecret, + "DbSecret cannot be set to nil")) + } else { + if len(r.Spec.DbSecret.Name) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("Name"), r.Spec.DbSecret.Name, + "Secret name cannot be set empty")) + } + if len(r.Spec.DbSecret.PwdFileName) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileName"), r.Spec.DbSecret.PwdFileName, + "Password file name cannot be set empty")) + } + if strings.ToLower(r.Spec.DbSecret.EncryptionType) != "base64" { + if strings.ToLower(r.Spec.DbSecret.KeyFileName) == "" { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileName"), r.Spec.DbSecret.KeyFileName, + "Key file name cannot be empty")) + } + } + if len(r.Spec.DbSecret.PwdFileMountLocation) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileMountLocation"), r.Spec.DbSecret.PwdFileMountLocation, + "Password file mount location cannot be empty")) + } + + if len(r.Spec.DbSecret.KeyFileMountLocation) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileMountLocation"), r.Spec.DbSecret.KeyFileMountLocation, + "KeyFileMountLocation file mount location cannot be empty")) + } + } + + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "ShardingDatabase"}, + r.Name, validationErr) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *ShardingDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + shardingdatabaselog.Info("validate update", "name", r.Name) + + // TODO(user): fill in your validation logic upon object update. + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *ShardingDatabase) ValidateDelete() (admission.Warnings, error) { + shardingdatabaselog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index 8a0019dd..0ff86649 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -43,9 +43,8 @@ import ( "reflect" "strconv" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - "github.com/go-logr/logr" + databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 3bf0eab5..fad43bc6 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -18,4 +18,5 @@ resources: - dbcs/database_v1alpha1_dbcssystem.yaml - database_v1alpha1_dataguardbroker.yaml - observability/databaseobserver.yaml +- database_v1alpha1_shardingdatabase.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 88823afe..32d25b76 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -902,32 +902,6 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.Sha } } - // Check Secret configuration - if instance.Spec.DbSecret == nil { - return fmt.Errorf("Secret specification cannot be null, you need to set secret details") - } else { - if len(instance.Spec.DbSecret.Name) == 0 { - return fmt.Errorf("instance.Spec.DbSecret.Name cannot be empty") - } - if len(instance.Spec.DbSecret.PwdFileName) == 0 { - return fmt.Errorf("instance.Spec.DbSecret.PwdFileName cannot be empty") - } - if strings.ToLower(instance.Spec.DbSecret.EncryptionType) != "base64" { - if strings.ToLower(instance.Spec.DbSecret.KeyFileName) == "" { - return fmt.Errorf("instance.Spec.DbSecret.KeyFileName cannot be empty") - } - } - if len(instance.Spec.DbSecret.PwdFileMountLocation) == 0 { - msg := "instance.Spec.DbSecret.PwdFileMountLocation is not set. Setting it to default " + shardingv1.GetSecretMount() - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - } - - if len(instance.Spec.DbSecret.KeyFileMountLocation) == 0 { - msg := "instance.Spec.DbSecret.KeyFileMountLocation is not set. Setting it to default " + shardingv1.GetSecretMount() - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - } - } - // Once the initial Spec is been validated then update the last Sucessful Spec err = instance.UpdateLastSuccessfulSpec(r.Client) if err != nil { diff --git a/docs/sharding/README.md b/docs/sharding/README.md index 972dc55a..06197092 100644 --- a/docs/sharding/README.md +++ b/docs/sharding/README.md @@ -127,6 +127,23 @@ In this example, the deployment uses the YAML file based on `OCI OKE` cluster. T [6. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md) [7. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md) + +## Provisioning Sharding Topology with System Sharding with SNR RAFT enabled in a Cloud-Based Kubernetes Cluster + +Deploy Oracle Database Sharding Topology with `System Sharding with SNR RAFT enabled` on your Cloud based Kubernetes cluster. + +**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database Sharding Topology covered by below examples: + +[1. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled without Database Gold Image](./provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md) +[2. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled with additional control on resources like Memory and CPU allocated to Pods](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md) +[3. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) +[4. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) +[5. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled and send Notification using OCI Notification Service](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md) +[6. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System Sharding with SNR RAFT enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md) +[7. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System Sharding with SNR RAFT enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md) + ## Connecting to Shard Databases After the Oracle Database Sharding Topology has been provisioned using the Sharding Controller in Oracle Database Kubernetes Operator, you can follow the steps in this document to connect to the Sharded Database or to the individual Shards: [Database Connectivity](./provisioning/database_connection.md) diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md new file mode 100644 index 00000000..c67f2504 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -0,0 +1,55 @@ +# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled by cloning database from your own Database Gold Image across Availability Domains(ADs) + +**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this test case, you provision the Oracle Database sharding topology with System Sharding with SNR RAFT enabled while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. + +This use case applies when you want to provision the database Pods on a Kubernetes Node in any availability domain (AD), which can also be different from the availability domain (AD) of the Block Volume that has the Oracle Database Gold Image provisioned earlier. + +Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup across ADs. + +NOTE: + +* Cloning from Block Volume Backup in OCI enables the new Persistent Volumes to be created in other ADs. +* To specify the AD where you want to provision the database Pod, use the tag `nodeSelector` and the POD will be provisioned in a node running in that AD. +* To specify GSM containers, you can also use the tag `nodeSelector` to specify the AD. +* Before you can provision with the Gold Image, you need the OCID of the Persistent Volume that has the Oracle Database Gold Image. + +1. Check the OCID of the Persistent Volume provisioned for the Oracle Database Gold Image: + ```sh + kubectl get pv -n shns + ``` +2. Create a Block Volume Backup for this Block Volume, and use the OCID of the Block Volume Backup in the next step. This example uses `snr_ssharding_shard_prov_clone_across_ads.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume which had the Gold Image. +* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` +* `SNR RAFT` enabled + +NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned across multiple Availability Domains by cloning the database. + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone_across_ads.yaml`. + * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +Use the file: [snr_ssharding_shard_prov_clone_across_ads.yaml](./snr_ssharding_shard_prov_clone_across_ads.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_clone_across_ads.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_clone_across_ads.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md new file mode 100644 index 00000000..330380cc --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -0,0 +1,51 @@ +# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD) + +**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this case, the database is created automatically by cloning from an existing Oracle Database Gold Image during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with SNR RAFT enabled is deployed using Oracle Sharding controller. + +This use case applies when you are cloning from a Block Volume, and you can clone _only_ in the same availability domain (AD). The result is that the cloned shard database PODs can be created _only_ in the same AD where the Gold Image Block Volume is present. + +Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup. + +**NOTE** For this step, the Persistent Volume that has the Oracle Database Gold Image is identified using its OCID. + +1. Check the OCID of the Persistent Volume provisioned earlier using below command: + + ```sh + kubectl get pv -n shns + ``` + +2. This example uses `snr_ssharding_shard_prov_clone.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Cloning from the Database Gold Image present in Persistent Volume having OCID: `ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq` +* `SNR RAFT` enabled + +NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned in the same Availability Domain `PHX-AD-1` by cloning the database. + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone.yaml`. + * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +Use the file: [snr_ssharding_shard_prov_clone.yaml](./snr_ssharding_shard_prov_clone.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_clone.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_clone.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md new file mode 100644 index 00000000..ce5eb34d --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md @@ -0,0 +1,44 @@ +# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT with additional control on resources like Memory and CPU allocated to Pods + +**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this use case, there are additional tags used to control resources such as CPU and Memory used by the different Pods when the Oracle Sharding topology with System Sharding is deployed using Oracle Sharding controller. + +This example uses `snr_ssharding_shard_prov_memory_cpu.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Tags `memory` and `cpu` to control the Memory and CPU of the PODs +* Additional tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level +* `SNR RAFT` enabled + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_memory_cpu.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +Use the YAML file [snr_ssharding_shard_prov_memory_cpu.yaml](./snr_ssharding_shard_prov_memory_cpu.yaml). + +1. Deploy the `snr_ssharding_shard_prov_memory_cpu.yaml` file: + + ```sh + kubectl apply -f snr_ssharding_shard_prov_memory_cpu.yaml + ``` + +1. Check the details of a POD. For example: To check the details of Pod `shard1-0`: + + ```sh + kubectl describe pod/shard1-0 -n shns + ``` +3. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md new file mode 100644 index 00000000..23db16b3 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md @@ -0,0 +1,84 @@ +# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT and send Notification using OCI Notification Service + +**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates how to use a notification service like OCI Notification service to send an email notification when a particular operation is completed on an Oracle Database sharding topology provisioned using the Oracle Database sharding controller. + +This example uses `snr_ssharding_shard_prov_send_notification.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume that has the Database Gold Image created earlier. +* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` +* Configmap to send notification email when a particular operation is completed. For example: When a shard is added. +* `SNR RAFT` enabled + +**NOTE:** + +* The notification will be sent using a configmap created with the credentials of the OCI user account in this use case. + +We will create a topic in Notification Service of the OCI Console and use its OCID. + +To do this: + +1. Create a `configmap_data.txt` file, such as the following, which has the OCI User details that will be used to send notfication: + + ```sh + user=ocid1.user.oc1........fx7omxfq + fingerprint=fa:18:98:...............:8a + tenancy=ocid1.tenancy.oc1..aaaa.......orpn7inq + region=us-phoenix-1 + topicid=ocid1.onstopic.oc1.phx.aaa............6xrq + ``` +2. Create a configmap using the below command using the file created above: + ```sh + kubectl create configmap onsconfigmap --from-file=./configmap_data.txt -n shns + ``` + +3. Create a key file `priavatekey` having the PEM key of the OCI user being used to send notification: + ```sh + -----BEGIN PRIVATE KEY-G---- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXYxA0DJvEwtVR + +o4OxrunL3L2NZJRADTFR+TDHqrNF1JwbaFBizSdL+EXbxQW1faZs5lXZ/sVmQF9 + . + . + . + zn/xWC0FzXGRzfvYHhq8XT3omf6L47KqIzqo3jDKdgvVq4u+lb+fXJlhj6Rwi99y + QEp36HnZiUxAQnR331DacN+YSTE+vpzSwZ38OP49khAB1xQsbiv1adG7CbNpkxpI + nS7CkDLg4Hcs4b9bGLHYJVY= + -----END PRIVATE KEY----- + ``` +4. Use the key file `privatekey` to create a Kubernetes secret in namespace `shns`: + + ```sh + kubectl create secret generic my-secret --from-file=./privatekey -n shns + ``` + +5. Use this command to check details of the secret that you created: + + ```sh + kubectl describe secret my-secret -n shns + ``` + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_send_notification.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +Use the file: [snr_ssharding_shard_prov_send_notification.yaml](./snr_ssharding_shard_prov_send_notification.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_send_notification.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_send_notification.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md new file mode 100644 index 00000000..4e17ff7f --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md @@ -0,0 +1,39 @@ +# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled without Database Gold Image + +**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System Sharding with SNR RAFT enabled is deployed using Oracle Sharding controller. + +**NOTE** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. + +This example uses `snr_ssharding_shard_prov.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* `SNR RAFT` enabled + + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + + +Use the file: [snr_ssharding_shard_prov.yaml](./snr_ssharding_shard_prov.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov.yaml + ``` +1. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md new file mode 100644 index 00000000..aa5dae75 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md @@ -0,0 +1,49 @@ +# Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System Sharding with SNR RAFT enabled + +**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with System Sharding with SNR RAFT enabled provisioned using Oracle Database Sharding controller. + +**NOTE** The deletion of a shard is done after verifying the Chunks have been moved out of that shard. + +In this use case, the existing database Sharding is having: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Five sharding Pods: `shard1`,`shard2`,`shard3`,`shard4` and `shard5` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* `SNR RAFT` enabled + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_delshard.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +NOTE: Use tag `isDelete: true` to delete the shard you want. + +This use case deletes the shard `shard4` from the above Sharding Topology. + +Use the file: [snr_ssharding_shard_prov_delshard.yaml](./snr_ssharding_shard_prov_delshard.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_delshard.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_delshard.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + +**NOTE:** After you apply `snr_ssharding_shard_prov_delshard.yaml`, the change may not be visible immediately. When the shard is removed, first the chunks will be moved out of that shard that is going to be deleted. + +To monitor the chunk movement, use the following command: + +```sh +# Switch to the primary GSM Container: +kubectl exec -i -t gsm1-0 -n shns /bin/bash + +# Check the status of the chunks and repeat to observe the chunk movement: +gdsctl config chunks +``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md new file mode 100644 index 00000000..a1002695 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md @@ -0,0 +1,36 @@ +# Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System Sharding with SNR RAFT enabled + +**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with System Sharding with SNR RAFT enabled provisioned earlier using Oracle Database Sharding controller. + +In this use case, the existing Oracle Database sharding topology is having: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* `SNR RAFT` enabled + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_extshard.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. + +Use the file: [snr_ssharding_shard_prov_extshard.yaml](./snr_ssharding_shard_prov_extshard.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_extshard.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_extshard.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard4-0": + kubectl logs -f pod/shard4-0 -n shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml new file mode 100644 index 00000000..efe3abec --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml @@ -0,0 +1,58 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml new file mode 100644 index 00000000..41fb758d --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml @@ -0,0 +1,83 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml new file mode 100644 index 00000000..4eb3954a --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml @@ -0,0 +1,91 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml new file mode 100644 index 00000000..43bf4074 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml @@ -0,0 +1,69 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard4 + isDelete: True + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard5 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml new file mode 100644 index 00000000..ea0c05a5 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml @@ -0,0 +1,68 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard4 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard5 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml new file mode 100644 index 00000000..7d5f2b45 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml @@ -0,0 +1,89 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml new file mode 100644 index 00000000..50c85443 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml @@ -0,0 +1,85 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + nsConfigMap: onsconfigmap + nsSecret: my-secret + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml index d2ae0ba9..45e1afe2 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml @@ -58,10 +58,18 @@ spec: imagePullPolicy: "Always" storageSizeInGb: 50 region: primary + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - name: gsm2 imagePullPolicy: "Always" storageSizeInGb: 50 region: standby + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" storageClass: oci dbImage: container-registry.oracle.com/database/enterprise:latest dbImagePullSecret: ocr-reg-cred diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml index f2d10e23..28f36608 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml @@ -58,10 +58,18 @@ spec: imagePullPolicy: "Always" storageSizeInGb: 50 region: primary + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - name: gsm2 imagePullPolicy: "Always" storageSizeInGb: 50 region: standby + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" storageClass: oci dbImage: container-registry.oracle.com/database/enterprise:latest dbImagePullSecret: ocr-reg-cred diff --git a/main.go b/main.go index 5ff714be..8c9b39a9 100644 --- a/main.go +++ b/main.go @@ -249,6 +249,10 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") os.Exit(1) } + if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + os.Exit(1) + } } // PDB Reconciler From 3ded387c7d3d37c9baf30bfc0980305d4dc42bc3 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 21 Mar 2024 18:06:02 +0000 Subject: [PATCH 065/414] Revert "Merge branch 'psaini_sharding_branch1' into 'master'" This reverts commit b1ef61565aea81c9cb4d3d9235f2a2674f63321d, reversing changes made to 7639531b8df008b5dea8fb7009d901424ffcc027. deleted: apis/database/v1alpha1/shardingdatabase_webhook.go modified: commons/sharding/catalog.go modified: config/samples/kustomization.yaml modified: controllers/database/shardingdatabase_controller.go modified: docs/sharding/README.md deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml deleted: docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml modified: docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml modified: docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml modified: main.go --- PROJECT | 8 - .../v1alpha1/shardingdatabase_webhook.go | 155 ------------------ commons/sharding/catalog.go | 3 +- config/samples/kustomization.yaml | 1 - .../database/shardingdatabase_controller.go | 26 +++ docs/sharding/README.md | 17 -- ...y_cloning_db_from_gold_image_across_ads.md | 55 ------- ...ing_by_cloning_db_gold_image_in_same_ad.md | 51 ------ ..._provisioning_with_control_on_resources.md | 44 ----- ...ith_notification_using_oci_notification.md | 84 ---------- ...ding_provisioning_without_db_gold_image.md | 39 ----- ...rding_scale_in_delete_an_existing_shard.md | 49 ------ .../snr_ssharding_scale_out_add_shards.md | 36 ---- .../snr_ssharding_shard_prov.yaml | 58 ------- .../snr_ssharding_shard_prov_clone.yaml | 83 ---------- ...ssharding_shard_prov_clone_across_ads.yaml | 91 ---------- .../snr_ssharding_shard_prov_delshard.yaml | 69 -------- .../snr_ssharding_shard_prov_extshard.yaml | 68 -------- .../snr_ssharding_shard_prov_memory_cpu.yaml | 89 ---------- ...sharding_shard_prov_send_notification.yaml | 85 ---------- ...ssharding_shard_prov_clone_across_ads.yaml | 8 - ...dsharding_shard_prov_clone_across_ads.yaml | 8 - main.go | 4 - 23 files changed, 28 insertions(+), 1103 deletions(-) delete mode 100644 apis/database/v1alpha1/shardingdatabase_webhook.go delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml diff --git a/PROJECT b/PROJECT index d9d026ef..5cbb036c 100644 --- a/PROJECT +++ b/PROJECT @@ -1,7 +1,3 @@ -# Code generated by tool. DO NOT EDIT. -# This file is used to track the info used to scaffold your project -# and allow the plugins properly work. -# More info: https://book.kubebuilder.io/reference/project-config.html domain: oracle.com layout: - go.kubebuilder.io/v2 @@ -71,10 +67,6 @@ resources: kind: ShardingDatabase path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 version: v1alpha1 - webhooks: - defaulting: true - validation: true - webhookVersion: v1beta1 - api: crdVersion: v1 namespaced: true diff --git a/apis/database/v1alpha1/shardingdatabase_webhook.go b/apis/database/v1alpha1/shardingdatabase_webhook.go deleted file mode 100644 index 48b2f784..00000000 --- a/apis/database/v1alpha1/shardingdatabase_webhook.go +++ /dev/null @@ -1,155 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v1alpha1 - -import ( - "strings" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// log is for logging in this package. -var shardingdatabaselog = logf.Log.WithName("shardingdatabase-resource") - -func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-shardingdatabase,mutating=true,failurePolicy=fail,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v1alpha1,name=mshardingdatabase.kb.io - -var _ webhook.Defaulter = &ShardingDatabase{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *ShardingDatabase) Default() { - shardingdatabaselog.Info("default", "name", r.Name) - - // TODO(user): fill in your defaulting logic. - if r.Spec.GsmDevMode != "" { - r.Spec.GsmDevMode = "dev" - } -} - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-shardingdatabase,mutating=false,failurePolicy=fail,groups=database.oracle.com,resources=shardingdatabases,versions=v1alpha1,name=vshardingdatabase.kb.io,admissionReviewVersions={v1,v1beta1} - -var _ webhook.Validator = &ShardingDatabase{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { - shardingdatabaselog.Info("validate create", "name", r.Name) - - // TODO(user): fill in your validation logic upon object creation. - // Check Secret configuration - var validationErr field.ErrorList - - //namespaces := db.GetWatchNamespaces() - //_, containsNamespace := namespaces[r.Namespace] - // Check if the allowed namespaces maps contains the required namespace - // if len(namespaces) != 0 && !containsNamespace { - // validationErr = append(validationErr, - // field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, - // "Oracle database operator doesn't watch over this namespace")) - //} - - if r.Spec.DbSecret == nil { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret"), r.Spec.DbSecret, - "DbSecret cannot be set to nil")) - } else { - if len(r.Spec.DbSecret.Name) == 0 { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("Name"), r.Spec.DbSecret.Name, - "Secret name cannot be set empty")) - } - if len(r.Spec.DbSecret.PwdFileName) == 0 { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileName"), r.Spec.DbSecret.PwdFileName, - "Password file name cannot be set empty")) - } - if strings.ToLower(r.Spec.DbSecret.EncryptionType) != "base64" { - if strings.ToLower(r.Spec.DbSecret.KeyFileName) == "" { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileName"), r.Spec.DbSecret.KeyFileName, - "Key file name cannot be empty")) - } - } - if len(r.Spec.DbSecret.PwdFileMountLocation) == 0 { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileMountLocation"), r.Spec.DbSecret.PwdFileMountLocation, - "Password file mount location cannot be empty")) - } - - if len(r.Spec.DbSecret.KeyFileMountLocation) == 0 { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileMountLocation"), r.Spec.DbSecret.KeyFileMountLocation, - "KeyFileMountLocation file mount location cannot be empty")) - } - } - - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "ShardingDatabase"}, - r.Name, validationErr) -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - shardingdatabaselog.Info("validate update", "name", r.Name) - - // TODO(user): fill in your validation logic upon object update. - return nil, nil -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateDelete() (admission.Warnings, error) { - shardingdatabaselog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. - return nil, nil -} diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index 0ff86649..8a0019dd 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -43,8 +43,9 @@ import ( "reflect" "strconv" - "github.com/go-logr/logr" databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + + "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index fad43bc6..3bf0eab5 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -18,5 +18,4 @@ resources: - dbcs/database_v1alpha1_dbcssystem.yaml - database_v1alpha1_dataguardbroker.yaml - observability/databaseobserver.yaml -- database_v1alpha1_shardingdatabase.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 32d25b76..88823afe 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -902,6 +902,32 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.Sha } } + // Check Secret configuration + if instance.Spec.DbSecret == nil { + return fmt.Errorf("Secret specification cannot be null, you need to set secret details") + } else { + if len(instance.Spec.DbSecret.Name) == 0 { + return fmt.Errorf("instance.Spec.DbSecret.Name cannot be empty") + } + if len(instance.Spec.DbSecret.PwdFileName) == 0 { + return fmt.Errorf("instance.Spec.DbSecret.PwdFileName cannot be empty") + } + if strings.ToLower(instance.Spec.DbSecret.EncryptionType) != "base64" { + if strings.ToLower(instance.Spec.DbSecret.KeyFileName) == "" { + return fmt.Errorf("instance.Spec.DbSecret.KeyFileName cannot be empty") + } + } + if len(instance.Spec.DbSecret.PwdFileMountLocation) == 0 { + msg := "instance.Spec.DbSecret.PwdFileMountLocation is not set. Setting it to default " + shardingv1.GetSecretMount() + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + } + + if len(instance.Spec.DbSecret.KeyFileMountLocation) == 0 { + msg := "instance.Spec.DbSecret.KeyFileMountLocation is not set. Setting it to default " + shardingv1.GetSecretMount() + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + } + } + // Once the initial Spec is been validated then update the last Sucessful Spec err = instance.UpdateLastSuccessfulSpec(r.Client) if err != nil { diff --git a/docs/sharding/README.md b/docs/sharding/README.md index 06197092..972dc55a 100644 --- a/docs/sharding/README.md +++ b/docs/sharding/README.md @@ -127,23 +127,6 @@ In this example, the deployment uses the YAML file based on `OCI OKE` cluster. T [6. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md) [7. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md) - -## Provisioning Sharding Topology with System Sharding with SNR RAFT enabled in a Cloud-Based Kubernetes Cluster - -Deploy Oracle Database Sharding Topology with `System Sharding with SNR RAFT enabled` on your Cloud based Kubernetes cluster. - -**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** - -In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database Sharding Topology covered by below examples: - -[1. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled without Database Gold Image](./provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md) -[2. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled with additional control on resources like Memory and CPU allocated to Pods](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md) -[3. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) -[4. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) -[5. Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled and send Notification using OCI Notification Service](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md) -[6. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System Sharding with SNR RAFT enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md) -[7. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System Sharding with SNR RAFT enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md) - ## Connecting to Shard Databases After the Oracle Database Sharding Topology has been provisioned using the Sharding Controller in Oracle Database Kubernetes Operator, you can follow the steps in this document to connect to the Sharded Database or to the individual Shards: [Database Connectivity](./provisioning/database_connection.md) diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md deleted file mode 100644 index c67f2504..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md +++ /dev/null @@ -1,55 +0,0 @@ -# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled by cloning database from your own Database Gold Image across Availability Domains(ADs) - -**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this test case, you provision the Oracle Database sharding topology with System Sharding with SNR RAFT enabled while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. - -This use case applies when you want to provision the database Pods on a Kubernetes Node in any availability domain (AD), which can also be different from the availability domain (AD) of the Block Volume that has the Oracle Database Gold Image provisioned earlier. - -Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup across ADs. - -NOTE: - -* Cloning from Block Volume Backup in OCI enables the new Persistent Volumes to be created in other ADs. -* To specify the AD where you want to provision the database Pod, use the tag `nodeSelector` and the POD will be provisioned in a node running in that AD. -* To specify GSM containers, you can also use the tag `nodeSelector` to specify the AD. -* Before you can provision with the Gold Image, you need the OCID of the Persistent Volume that has the Oracle Database Gold Image. - -1. Check the OCID of the Persistent Volume provisioned for the Oracle Database Gold Image: - ```sh - kubectl get pv -n shns - ``` -2. Create a Block Volume Backup for this Block Volume, and use the OCID of the Block Volume Backup in the next step. This example uses `snr_ssharding_shard_prov_clone_across_ads.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume which had the Gold Image. -* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` -* `SNR RAFT` enabled - -NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned across multiple Availability Domains by cloning the database. - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone_across_ads.yaml`. - * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - -Use the file: [snr_ssharding_shard_prov_clone_across_ads.yaml](./snr_ssharding_shard_prov_clone_across_ads.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_clone_across_ads.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_clone_across_ads.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md deleted file mode 100644 index 330380cc..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md +++ /dev/null @@ -1,51 +0,0 @@ -# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD) - -**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this case, the database is created automatically by cloning from an existing Oracle Database Gold Image during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with SNR RAFT enabled is deployed using Oracle Sharding controller. - -This use case applies when you are cloning from a Block Volume, and you can clone _only_ in the same availability domain (AD). The result is that the cloned shard database PODs can be created _only_ in the same AD where the Gold Image Block Volume is present. - -Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup. - -**NOTE** For this step, the Persistent Volume that has the Oracle Database Gold Image is identified using its OCID. - -1. Check the OCID of the Persistent Volume provisioned earlier using below command: - - ```sh - kubectl get pv -n shns - ``` - -2. This example uses `snr_ssharding_shard_prov_clone.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* Database Cloning from the Database Gold Image present in Persistent Volume having OCID: `ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq` -* `SNR RAFT` enabled - -NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned in the same Availability Domain `PHX-AD-1` by cloning the database. - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone.yaml`. - * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - -Use the file: [snr_ssharding_shard_prov_clone.yaml](./snr_ssharding_shard_prov_clone.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_clone.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_clone.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md deleted file mode 100644 index ce5eb34d..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md +++ /dev/null @@ -1,44 +0,0 @@ -# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT with additional control on resources like Memory and CPU allocated to Pods - -**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this use case, there are additional tags used to control resources such as CPU and Memory used by the different Pods when the Oracle Sharding topology with System Sharding is deployed using Oracle Sharding controller. - -This example uses `snr_ssharding_shard_prov_memory_cpu.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* Tags `memory` and `cpu` to control the Memory and CPU of the PODs -* Additional tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level -* `SNR RAFT` enabled - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_memory_cpu.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - -Use the YAML file [snr_ssharding_shard_prov_memory_cpu.yaml](./snr_ssharding_shard_prov_memory_cpu.yaml). - -1. Deploy the `snr_ssharding_shard_prov_memory_cpu.yaml` file: - - ```sh - kubectl apply -f snr_ssharding_shard_prov_memory_cpu.yaml - ``` - -1. Check the details of a POD. For example: To check the details of Pod `shard1-0`: - - ```sh - kubectl describe pod/shard1-0 -n shns - ``` -3. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md deleted file mode 100644 index 23db16b3..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md +++ /dev/null @@ -1,84 +0,0 @@ -# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT and send Notification using OCI Notification Service - -**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -This use case demonstrates how to use a notification service like OCI Notification service to send an email notification when a particular operation is completed on an Oracle Database sharding topology provisioned using the Oracle Database sharding controller. - -This example uses `snr_ssharding_shard_prov_send_notification.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume that has the Database Gold Image created earlier. -* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` -* Configmap to send notification email when a particular operation is completed. For example: When a shard is added. -* `SNR RAFT` enabled - -**NOTE:** - -* The notification will be sent using a configmap created with the credentials of the OCI user account in this use case. - -We will create a topic in Notification Service of the OCI Console and use its OCID. - -To do this: - -1. Create a `configmap_data.txt` file, such as the following, which has the OCI User details that will be used to send notfication: - - ```sh - user=ocid1.user.oc1........fx7omxfq - fingerprint=fa:18:98:...............:8a - tenancy=ocid1.tenancy.oc1..aaaa.......orpn7inq - region=us-phoenix-1 - topicid=ocid1.onstopic.oc1.phx.aaa............6xrq - ``` -2. Create a configmap using the below command using the file created above: - ```sh - kubectl create configmap onsconfigmap --from-file=./configmap_data.txt -n shns - ``` - -3. Create a key file `priavatekey` having the PEM key of the OCI user being used to send notification: - ```sh - -----BEGIN PRIVATE KEY-G---- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXYxA0DJvEwtVR - +o4OxrunL3L2NZJRADTFR+TDHqrNF1JwbaFBizSdL+EXbxQW1faZs5lXZ/sVmQF9 - . - . - . - zn/xWC0FzXGRzfvYHhq8XT3omf6L47KqIzqo3jDKdgvVq4u+lb+fXJlhj6Rwi99y - QEp36HnZiUxAQnR331DacN+YSTE+vpzSwZ38OP49khAB1xQsbiv1adG7CbNpkxpI - nS7CkDLg4Hcs4b9bGLHYJVY= - -----END PRIVATE KEY----- - ``` -4. Use the key file `privatekey` to create a Kubernetes secret in namespace `shns`: - - ```sh - kubectl create secret generic my-secret --from-file=./privatekey -n shns - ``` - -5. Use this command to check details of the secret that you created: - - ```sh - kubectl describe secret my-secret -n shns - ``` - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_send_notification.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - -Use the file: [snr_ssharding_shard_prov_send_notification.yaml](./snr_ssharding_shard_prov_send_notification.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_send_notification.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_send_notification.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md deleted file mode 100644 index 4e17ff7f..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md +++ /dev/null @@ -1,39 +0,0 @@ -# Provisioning Oracle Sharded Database with System Sharding with SNR RAFT enabled without Database Gold Image - -**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System Sharding with SNR RAFT enabled is deployed using Oracle Sharding controller. - -**NOTE** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. - -This example uses `snr_ssharding_shard_prov.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* `SNR RAFT` enabled - - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - - -Use the file: [snr_ssharding_shard_prov.yaml](./snr_ssharding_shard_prov.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov.yaml - ``` -1. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md deleted file mode 100644 index aa5dae75..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md +++ /dev/null @@ -1,49 +0,0 @@ -# Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System Sharding with SNR RAFT enabled - -**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with System Sharding with SNR RAFT enabled provisioned using Oracle Database Sharding controller. - -**NOTE** The deletion of a shard is done after verifying the Chunks have been moved out of that shard. - -In this use case, the existing database Sharding is having: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Five sharding Pods: `shard1`,`shard2`,`shard3`,`shard4` and `shard5` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* `SNR RAFT` enabled - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_delshard.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - -NOTE: Use tag `isDelete: true` to delete the shard you want. - -This use case deletes the shard `shard4` from the above Sharding Topology. - -Use the file: [snr_ssharding_shard_prov_delshard.yaml](./snr_ssharding_shard_prov_delshard.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_delshard.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_delshard.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - -**NOTE:** After you apply `snr_ssharding_shard_prov_delshard.yaml`, the change may not be visible immediately. When the shard is removed, first the chunks will be moved out of that shard that is going to be deleted. - -To monitor the chunk movement, use the following command: - -```sh -# Switch to the primary GSM Container: -kubectl exec -i -t gsm1-0 -n shns /bin/bash - -# Check the status of the chunks and repeat to observe the chunk movement: -gdsctl config chunks -``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md deleted file mode 100644 index a1002695..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md +++ /dev/null @@ -1,36 +0,0 @@ -# Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System Sharding with SNR RAFT enabled - -**NOTE: SNR RAFT Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with System Sharding with SNR RAFT enabled provisioned earlier using Oracle Database Sharding controller. - -In this use case, the existing Oracle Database sharding topology is having: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* `SNR RAFT` enabled - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_extshard.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - -This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. - -Use the file: [snr_ssharding_shard_prov_extshard.yaml](./snr_ssharding_shard_prov_extshard.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_extshard.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_extshard.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard4-0": - kubectl logs -f pod/shard4-0 -n shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml deleted file mode 100644 index efe3abec..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml +++ /dev/null @@ -1,58 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml deleted file mode 100644 index 41fb758d..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - isClone: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary - namespace: shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml deleted file mode 100644 index 4eb3954a..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml +++ /dev/null @@ -1,91 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - isClone: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml deleted file mode 100644 index 43bf4074..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard4 - isDelete: True - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard5 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml deleted file mode 100644 index ea0c05a5..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml +++ /dev/null @@ -1,68 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard4 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard5 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml deleted file mode 100644 index 7d5f2b45..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml +++ /dev/null @@ -1,89 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - resources: - requests: - memory: "1000Mi" - cpu: "1000m" - envVars: - - name: "INIT_SGA_SIZE" - value: "600" - - name: "INIT_PGA_SIZE" - value: "400" - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - resources: - requests: - memory: "1000Mi" - cpu: "1000m" - envVars: - - name: "INIT_SGA_SIZE" - value: "600" - - name: "INIT_PGA_SIZE" - value: "400" - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - resources: - requests: - memory: "1000Mi" - cpu: "1000m" - envVars: - - name: "INIT_SGA_SIZE" - value: "600" - - name: "INIT_PGA_SIZE" - value: "400" - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - resources: - requests: - memory: "1000Mi" - cpu: "1000m" - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary - namespace: shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml deleted file mode 100644 index 50c85443..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml +++ /dev/null @@ -1,85 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v1alpha1 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - isClone: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - nsConfigMap: onsconfigmap - nsSecret: my-secret - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml index 45e1afe2..d2ae0ba9 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml @@ -58,18 +58,10 @@ spec: imagePullPolicy: "Always" storageSizeInGb: 50 region: primary - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - name: gsm2 imagePullPolicy: "Always" storageSizeInGb: 50 region: standby - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" storageClass: oci dbImage: container-registry.oracle.com/database/enterprise:latest dbImagePullSecret: ocr-reg-cred diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml index 28f36608..f2d10e23 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml @@ -58,18 +58,10 @@ spec: imagePullPolicy: "Always" storageSizeInGb: 50 region: primary - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - name: gsm2 imagePullPolicy: "Always" storageSizeInGb: 50 region: standby - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" storageClass: oci dbImage: container-registry.oracle.com/database/enterprise:latest dbImagePullSecret: ocr-reg-cred diff --git a/main.go b/main.go index 8c9b39a9..5ff714be 100644 --- a/main.go +++ b/main.go @@ -249,10 +249,6 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") os.Exit(1) } - if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") - os.Exit(1) - } } // PDB Reconciler From 4fb8bdf68a825eb8cdb49ca21b5730a0fa4a1f89 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Fri, 22 Mar 2024 13:56:38 +0000 Subject: [PATCH 066/414] Enhancing event logs and sidb resource status updation --- commons/database/constants.go | 2 +- commons/database/utils.go | 8 ++- .../singleinstancedatabase_controller.go | 57 ++++++++++++------- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/commons/database/constants.go b/commons/database/constants.go index 47d2b8b9..3fa62b30 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -522,7 +522,7 @@ const DisableTcpsCMD string = "$ORACLE_BASE/$CONFIG_TCPS_FILE disable" const TlsCertsLocation string = "/run/secrets/tls_secret" // Check Mount in pods -const PodMountsCmd string = "awk '$2 == \"%s\" {print}' /proc/mounts" +const PodMountsCmd string = "awk '$2 == \"%s\" {print}' /proc/mounts" // TCPS clientWallet update command const ClientWalletUpdate string = "sed -i -e 's/HOST.*$/HOST=%s)/g' -e 's/PORT.*$/PORT=%d)/g' ${ORACLE_BASE}/oradata/clientWallet/${ORACLE_SID}/tnsnames.ora" diff --git a/commons/database/utils.go b/commons/database/utils.go index dd0e54a9..1723bc90 100644 --- a/commons/database/utils.go +++ b/commons/database/utils.go @@ -72,6 +72,8 @@ import ( var requeueY ctrl.Result = ctrl.Result{Requeue: true, RequeueAfter: 15 * time.Second} var requeueN ctrl.Result = ctrl.Result{} +var ErrNoReadyPod = errors.New("SingleInstanceDatabase has no ready pod currently") + // Filter events that trigger reconcilation func ResourceEventHandler() predicate.Predicate { return predicate.Funcs{ @@ -683,9 +685,9 @@ func GetSidPdbEdition(r client.Reader, config *rest.Config, ctx context.Context, splitstr := strings.Split((strings.TrimSpace(out)), ",") return splitstr[0], splitstr[1], splitstr[2], nil } - err = errors.New("ready pod name is nil") - log.Error(err, err.Error()) - return "", "", "", err + // err = errors.New("ready pod name is nil") + log.Error(err, ErrNoReadyPod.Error()) + return "", "", "", ErrNoReadyPod } // Get Datapatch Status diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 61592622..c7d94f3a 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -1740,6 +1740,9 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplaceSVC(ctx context.Contex r.Log.Info("Initiliazing database sid, pdb, edition for prebuilt database") var edition string sid, pdbName, edition, getSidPdbEditionErr = dbcommons.GetSidPdbEdition(r, r.Config, ctx, ctrl.Request{NamespacedName: types.NamespacedName{Namespace: m.Namespace, Name: m.Name}}) + if errors.Is(getSidPdbEditionErr, dbcommons.ErrNoReadyPod) { + return requeueN, nil + } if getSidPdbEditionErr != nil { return requeueY, getSidPdbEditionErr } @@ -2245,8 +2248,26 @@ func (r *SingleInstanceDatabaseReconciler) validateDBReadiness(sidb *dbapi.Singl eventMsg := "pod creation failed" r.Recorder.Eventf(sidb, corev1.EventTypeNormal, eventReason, eventMsg) } else if ok, _ := dbcommons.IsAnyPodWithStatus(available, corev1.PodRunning); ok { - log.Info("Database Creating....", "Name", sidb.Name) - sidb.Status.Status = dbcommons.StatusCreating + + out, err := dbcommons.ExecCommand(r, r.Config, available[0].Name, sidb.Namespace, "", + ctx, req, false, "bash", "-c", dbcommons.GetCheckpointFileCMD) + if err != nil { + r.Log.Info(err.Error()) + } + + if out != "" { + log.Info("Database initialzied") + eventReason := "Database Unhealthy" + eventMsg := "datafiles exists" + r.Recorder.Eventf(sidb, corev1.EventTypeNormal, eventReason, eventMsg) + sidb.Status.DatafilesCreated = "true" + sidb.Status.Status = dbcommons.StatusNotReady + r.updateORDSStatus(sidb, ctx, req) + } else { + log.Info("Database Creating....", "Name", sidb.Name) + sidb.Status.Status = dbcommons.StatusCreating + } + } else { log.Info("Database Pending....", "Name", sidb.Name) } @@ -2263,24 +2284,6 @@ func (r *SingleInstanceDatabaseReconciler) validateDBReadiness(sidb *dbapi.Singl r.Log.Info(err.Error()) } } - out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", - ctx, req, false, "bash", "-c", dbcommons.GetCheckpointFileCMD) - if err != nil { - r.Log.Info(err.Error()) - } - - if out != "" { - log.Info("Database initialzied") - eventReason := "Database Unhealthy" - eventMsg := "datafiles exists" - r.Recorder.Eventf(sidb, corev1.EventTypeNormal, eventReason, eventMsg) - sidb.Status.DatafilesCreated = "true" - sidb.Status.Status = dbcommons.StatusNotReady - r.updateORDSStatus(sidb, ctx, req) - } else { - log.Info("Database Creating....", "Name", sidb.Name) - return requeueY, sidbReadyPod, nil - } version, err := dbcommons.GetDatabaseVersion(sidbReadyPod, r, r.Config, ctx, req) if err != nil { @@ -3217,8 +3220,18 @@ func ValidatePrimaryDatabaseAdminPassword(r *SingleInstanceDatabaseReconciler, p // // ############################################################################# func ValidateDatabaseConfiguration(p *dbapi.SingleInstanceDatabase) error { + var missingModes []string + if p.Status.ArchiveLog == "false" { + missingModes = append(missingModes, "ArchiveLog") + } + if p.Status.FlashBack == "false" { + missingModes = append(missingModes, "FlashBack") + } + if p.Status.ForceLogging == "false" { + missingModes = append(missingModes, "ForceLogging") + } if p.Status.ArchiveLog == "false" || p.Status.FlashBack == "false" || p.Status.ForceLogging == "false" { - return fmt.Errorf("all of Archivelog, Flashback and ForceLogging modes are not enabled in the primary database %v", p.Name) + return fmt.Errorf("%v modes are not enabled in the primary database %v", strings.Join(missingModes, ","), p.Name) } return nil } @@ -3269,7 +3282,7 @@ func ValidatePrimaryDatabaseForStandbyCreation(r *SingleInstanceDatabaseReconcil log.Info(fmt.Sprintf("Validating primary database %s configuration...", primary.Name)) err = ValidateDatabaseConfiguration(primary) if err != nil { - r.Recorder.Eventf(stdby, corev1.EventTypeWarning, "Spec Error", "all of Archivelog, Flashback and ForceLogging modes are not enabled in the primary database "+primary.Name) + r.Recorder.Eventf(stdby, corev1.EventTypeWarning, "Spec Error", err.Error()) stdby.Status.Status = dbcommons.StatusError return err } From 3fdc2312dd8b5b7ed9a119943d5c493273adc108 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Mon, 25 Mar 2024 15:10:39 +0000 Subject: [PATCH 067/414] Including access and privileges that sidb controller requires in the readme --- docs/sidb/README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 3c4d0ed9..0299c464 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -3,6 +3,8 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Instance Database Controller, which enables provisioning, cloning, and patching of Oracle Single Instance Databases on Kubernetes. It also enables configuring the database for Oracle REST Data Services with Oracle APEX development platform. The following sections explain the setup and functionality of the operator * [Prerequisites](#prerequisites) + * [Mandatory roles and privileges requirements for Oracle Single Instance Database Controller](#mandatory-roles-and-privileges-requirements-for-oracle-single-instance-database-controller) + * [Options roles and privileges requirements for Oracle Single Instance Database Controller](#optional-roles-and-privileges-requirements-for-oracle-single-instance-database-controller) * [SingleInstanceDatabase Resource](#singleinstancedatabase-resource) * [Create a Database](#create-a-database) * [New Database](#new-database) @@ -62,6 +64,31 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst kubectl apply -f rbac/persistent-volume-rbac.yaml ``` + ### Mandatory roles and privileges requirements for Oracle Single Instance Database Controller + + Single Instance Database(sidb) controller uses Kubernetes objects such as :- + + | Resources | Verbs | + | --- | --- | + | Pods | create delete get list patch update watch | + | Containers | create delete get list patch update watch | + | PersistentVolumeClaims | create delete get list patch update watch | + | Services | create delete get list patch update watch | + | Secrets | create delete get list patch update watch | + | Events | create patch | + + ### Optional roles and privileges requirements for Oracle Single Instance Database Controller + + Single Instance Database(sidb) controller uses Kubernetes objects for some + features which can be given to the controller when using those features; + features and access are :- + + | Functionality | Resources | Verbs | + | --- | --- | --- | + | NodePort serivces | Nodes | list watch | + | Storage Expansion with block volumes | StorageClasses | get list watch | + | Custom scripts execution | PersistentVolumes | get list watch | + ## SingleInstanceDatabase Resource The Oracle Database Operator creates the `SingleInstanceDatabase` as a custom resource. Doing this enables Oracle Database to be managed as a native Kubernetes object. We will refer `SingleInstanceDatabase` resource as Database from now onwards. From d3b1aab076f0f46c9dd0c09893f78246f3c2eb6b Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Mon, 25 Mar 2024 21:58:31 +0000 Subject: [PATCH 068/414] Autonomous Database Controller doc changes --- .../database/autonomousdatabase_controller.go | 1 + docs/adb/README.md | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 5323c0aa..718ee499 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -187,6 +187,7 @@ func (r *AutonomousDatabaseReconciler) eventFilterPredicate() predicate.Predicat // +kubebuilder:rbac:groups=database.oracle.com,resources=autonomouscontainerdatabases,verbs=get;list // +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=create;get;list;update // +kubebuilder:rbac:groups="",resources=configmaps;secrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch // Reconcile is the funtion that the operator calls every time when the reconciliation loop is triggered. // It go to the beggining of the reconcile if an error is returned. We won't return a error if it is related diff --git a/docs/adb/README.md b/docs/adb/README.md index aa4bc60d..1b59c4d6 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -411,6 +411,18 @@ To delete the resource and terminate the Autonomous Database, complete these ste Now, you can verify that the database is in TERMINATING state on the Cloud Console. +## Roles and Privileges requirements for Oracle Autonomous Database Controller + +Autonomous Database controller uses Kubernetes objects such as: + + | Resources | Verbs | + | --- | --- | + | Configmaps | get list watch create update patch delete | + | Secrets | get list watch create update patch delete | + | Events | create patch | + +The defintion of all the Kubernetes Objects, which are to be used by the Oracle Autonomous Database Controller, comes from the `oracle-database-operator.yaml` file which is applied to deploy the **Oracle Database Operator**. + ## Debugging and troubleshooting ### Show the details of the resource From a1101f7104c3c3b4bb067f717e83121cebaa498e Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Mon, 25 Mar 2024 21:59:56 +0000 Subject: [PATCH 069/414] Updated README with security section --- docs/observability/README.md | 39 +++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/docs/observability/README.md b/docs/observability/README.md index e7cf2571..986b1885 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -1,7 +1,7 @@ # Managing Observability on Kubernetes for Oracle Databases Oracle Database Operator for Kubernetes (`OraOperator`) includes the -Oracle Database Metrics Exporter controller for Oracle Databases, which enables users to observe +Observability controller for Oracle Databases and adds the `DatabaseObserver` CRD, which enables users to observe Oracle Databases by scraping database metrics using SQL queries. The controller automates the deployment and maintenance of the metrics exporter container image, metrics exporter service and a Prometheus servicemonitor. @@ -17,26 +17,26 @@ of the controller. * [Get Status](#get-detailed-status) * [Update](#patch-resource) * [Delete](#delete-resource) +* [Mandatory Roles and Privileges](#mandatory-roles-and-privileges-requirements-for-observability-controller) * [Debugging and troubleshooting](#debugging-and-troubleshooting) ## Prerequisites The `DatabaseObserver` custom resource has the following pre-requisites: 1. Prometheus and its `servicemonitor` custom resource definition must be installed on the cluster. - - The Oracle Database metrics exporter controller creates multiple -2. Kubernetes resources that include + +- The Observability controller creates multiple Kubernetes resources that include a Prometheus `servicemonitor`. In order for the controller to create ServiceMonitors, the ServiceMonitor custom resource must exist. 2. A pre-existing Oracle Database and the proper database grants and privileges. - The Oracle Database metrics exporter exports metrics through SQL queries that the user can control +- The controller exports metrics through SQL queries that the user can control and specify through a _toml_ file. The necessary access privileges to the tables used in the queries are not provided and applied automatically. ### The DatabaseObserver Custom Resource -The Oracle Database Operator (__v1.1.0__) includes the Oracle Database Metrics Exporter controller which automates +The Oracle Database Operator (__v1.1.0__) includes the Oracle Database Observability controller which automates the deployment and setting up of the Oracle Database metrics exporter and the related resources to make Oracle databases observable. In the sample YAML file found in @@ -65,6 +65,9 @@ the databaseObserver custom resource offers the following properties to be confi | `spec.ociConfig.secretName` | string | - | Conditional | _oci-privatekey_ | + + + ### Configuration The `databaseObserver` custom resource has the following fields for all configurations that are required: * `spec.database.dbUser.secret` - secret containing the database username. The corresponding key can be any value but must match the key in the secret provided. @@ -173,7 +176,7 @@ value for every ConditionType. ### Patch Resource -The metrics exporter controller currently supports updates for most of the fields in the manifest. An example of patching the databaseObserver resource is as follows: +The Observability controller currently supports updates for most of the fields in the manifest. An example of patching the databaseObserver resource is as follows: ```bash kubectl --type=merge -p '{"spec":{"exporter":{"image":"container-registry.oracle.com/database/observability-exporter:latest"}}}' patch databaseobserver obs-sample ``` @@ -202,6 +205,28 @@ To delete the DatabaseObserver custom resource and all related resources: kubectl delete databaseobserver obs-sample ``` +## Mandatory roles and privileges requirements for Observability Controller + +The Observability controller issues the following policy rules for the following resources. Besides +databaseobserver resources, the controller manages its own service, deployment, pods and servicemonitor +and gets and lists configmaps and secrets. + +| Resources | Verbs | +|-------------------------------------------------------|-------------------------------------------| +| services | create delete get list patch update watch | +| deployments | create delete get list patch update watch | +| pods | create delete get list patch update watch | +| events | create delete get list patch update watch | +| services.apps | create delete get list patch update watch | +| deployments.apps | create delete get list patch update watch | +| pods.apps | create delete get list patch update watch | +| servicemonitors.monitoring.coreos.com | create delete get list patch update watch | +| databaseobservers.observability.oracle.com | create delete get list patch update watch | +| databaseobservers.observability.oracle.com/status | get patch update | +| configmaps | get list | +| secrets | get list | +| configmaps.apps | get list | +| databaseobservers.observability.oracle.com/finalizers | update | ## Debugging and troubleshooting From 76be3728a4b066d2ef083f0119670f657303fd8e Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Tue, 26 Mar 2024 17:49:10 +0530 Subject: [PATCH 070/414] ReadMe fixes --- config/samples/sidb/singleinstancedatabase.yaml | 9 +++++---- docs/sidb/README.md | 12 ++++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index c61f17fe..3fcf7d97 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -12,18 +12,19 @@ spec: ## Use only alphanumeric characters for sid up to a maximum of 8 characters sid: ORCL1 + ## DB edition. N/A for createAs clone or standby + ## Valid values for edition are enterprise, standard, express or free + edition: enterprise + ## Type of database. ## Valid values for createAs are primary, clone or standby + ## Valid only for enterprise and standard editions createAs: primary ## Reference to a source primary database. ## Valid only for createAs clone or standby ## The name of a source primary database resource from the same namespace primaryDatabaseRef: "" - - ## DB edition. N/A for createAs clone or standby - ## Valid values for edition are enterprise, standard or express - edition: enterprise ## Secret containing SIDB password mapped to secretKey. secretKey defaults to oracle_pwd ## Should refer to adminPassword of Source DB if createAs is clone or standby diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 0299c464..35bb7101 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -494,8 +494,6 @@ To create copies of your existing database quickly, you can use the cloning func To quickly clone the existing database sidb-sample created above, use the sample **[config/samples/sidb/singleinstancedatabase_clone.yaml](../../config/samples/sidb/singleinstancedatabase_clone.yaml)** file. -**Note**: To clone a database, the source database must have archiveLog mode set to true. - For example: ```sh @@ -505,7 +503,10 @@ $ kubectl apply -f singleinstancedatabase_clone.yaml singleinstancedatabase.database.oracle.com/sidb-sample-clone created ``` -**Note:** The clone database can specify a database image that is different from the source database. In such cases, cloning is supported only between databases of the same major release. +**Note:** +- To clone a database, the source database must have archiveLog mode set to true. +- The clone database can specify a database image that is different from the source database. In such cases, cloning is supported only between databases of the same major release. +- Only enterprise and standard editions support cloning. ### Patch a Database @@ -524,7 +525,10 @@ singleinstancedatabase.database.oracle.com/sidb-sample patched ``` -After patching is complete, the database pods are restarted with the new release update image. For minimum downtime, ensure that you have multiple replicas of the database pods running before you start the patch operation. +After patching is complete, the database pods are restarted with the new release update image. + +**Note:** +- Only enterprise and standard editions support patching. #### Patch after Cloning From 02626334e62a7ca3fb0a9956722bbdf753216432 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Tue, 26 Mar 2024 18:01:20 +0000 Subject: [PATCH 071/414] update README file security details --- docs/sharding/README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/sharding/README.md b/docs/sharding/README.md index 972dc55a..3d3320ee 100644 --- a/docs/sharding/README.md +++ b/docs/sharding/README.md @@ -26,14 +26,14 @@ The Oracle Sharding database controller provides end-to-end automation of Oracle To create a Sharding Topology, complete the steps in the following sections below: -1. [Prerequsites for running Oracle Sharding Database Controller](#prerequsites-for-running-oracle-sharding-database-controller) +1. [Prerequisites for running Oracle Sharding Database Controller](#prerequisites-for-running-oracle-sharding-database-controller) 2. [Provisioning Sharding Topology in a Cloud based Kubernetes Cluster (OKE in this case)](#provisioning-sharding-topology-in-a-cloud-based-kubernetes-cluster-oke-in-this-case) 3. [Connecting to Shard Databases](#connecting-to-shard-databases) 4. [Debugging and Troubleshooting](#debugging-and-troubleshooting) **Note** Before proceeding to the next section, you must complete the instructions given in each section, based on your enviornment, before proceeding to next section. -## Prerequsites for Running Oracle Sharding Database Controller +## Prerequisites for Running Oracle Sharding Database Controller **IMPORTANT:** You must make the changes specified in this section before you proceed to the next section. @@ -44,6 +44,19 @@ To create a Sharding Topology, complete the steps in the following sections belo To use Oracle Sharding Database Controller, ensure that your system is provisioned with a supported Kubernetes release. Refer to the [Release Status Section](../../README.md#release-status). +#### Mandatory roles and privileges requirements for Oracle Sharding Database Controller + + Oracle Sharding Database Controller uses Kubernetes objects such as :- + + | Resources | Verbs | + | --- | --- | + | Pods | create delete get list patch update watch | + | Containers | create delete get list patch update watch | + | PersistentVolumeClaims | create delete get list patch update watch | + | Services | create delete get list patch update watch | + | Secrets | create delete get list patch update watch | + | Events | create patch | + ### 2. Deploy Oracle Database Operator To deploy Oracle Database Operator in a Kubernetes cluster, go to the section [Install Oracle DB Operator](../../README.md#install-oracle-db-operator) in the README, and complete the operator deployment before you proceed further. If you have already deployed the operator, then proceed to the next section. From 8ec216b18c1d6ceb1fce9a6e24e6125533d44093 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Thu, 28 Mar 2024 11:17:04 +0000 Subject: [PATCH 072/414] ReadMe update --- docs/sidb/PREREQUISITES.md | 16 +++++++---- docs/sidb/README.md | 58 ++++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/docs/sidb/PREREQUISITES.md b/docs/sidb/PREREQUISITES.md index 6315d308..1b7edd31 100644 --- a/docs/sidb/PREREQUISITES.md +++ b/docs/sidb/PREREQUISITES.md @@ -1,17 +1,21 @@ -## Prerequisites for Oracle Docker Image Deployment -To deploy Oracle Database Operator for Kubernetes on Oracle Docker images, complete these steps. +## Deployment Prerequisites +To deploy Oracle Single Instance Database in Kubernetes using the OraOperator, complete these steps. -* ### Prepare Oracle Docker Images +* ### Prepare Oracle Container Images - Build Single Instance Database Docker Images from source, following the instructions at [https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance), or + Build Single Instance Database Container Images from source, following the instructions at [https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance), or use the pre-built images available at [https://container-registry.oracle.com](https://container-registry.oracle.com) by signing in and accepting the required license agreement. Oracle Database Releases Supported: Enterprise and Standard Edition for Oracle Database 19c, and later releases. Express Edition for Oracle Database 21.3.0 only. Oracle Database Free 23.2.0 and later Free releases - Build Oracle REST Data Service Docker Images from source following the instructions at [https://github.com/oracle/docker-images/tree/main/OracleRestDataServices](https://github.com/oracle/docker-images/tree/main/OracleRestDataServices). + Build Oracle REST Data Service Container Images from source following the instructions at [https://github.com/oracle/docker-images/tree/main/OracleRestDataServices](https://github.com/oracle/docker-images/tree/main/OracleRestDataServices). Supported Oracle REST Data Service version is 21.4.2 -* ### Set Up Kubernetes and Volumes +* ### Ensure Sufficient Disk Space in Kubernetes Workers Nodes + + Provision Kubernetes worker nodes with recommended 250 GiB or more of free disk space required for pulling the base and patched database container images. If deploying on cloud you may choose to increase the custom boot volume size of the worker nodes. + +* ### Set Up Kubernetes and Volumes for Database Persistence Set up an on-premises Kubernetes cluster, or subscribe to a managed Kubernetes service, such as Oracle Cloud Infrastructure Container Engine for Kubernetes. Use a dynamic volume provisioner or pre-provision static persistent volumes manually. These volumes are required for persistent storage of the database files. diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 35bb7101..7119099f 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -3,8 +3,8 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Instance Database Controller, which enables provisioning, cloning, and patching of Oracle Single Instance Databases on Kubernetes. It also enables configuring the database for Oracle REST Data Services with Oracle APEX development platform. The following sections explain the setup and functionality of the operator * [Prerequisites](#prerequisites) - * [Mandatory roles and privileges requirements for Oracle Single Instance Database Controller](#mandatory-roles-and-privileges-requirements-for-oracle-single-instance-database-controller) - * [Options roles and privileges requirements for Oracle Single Instance Database Controller](#optional-roles-and-privileges-requirements-for-oracle-single-instance-database-controller) + * [Mandatory Resource Privileges](#mandatory-resource-privileges) + * [Optional Resource Privileges](#optional-resource-privileges) * [SingleInstanceDatabase Resource](#singleinstancedatabase-resource) * [Create a Database](#create-a-database) * [New Database](#new-database) @@ -49,26 +49,13 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst ## Prerequisites -* Oracle strongly recommends that you follow the [prerequisites](./PREREQUISITES.md) -* For managing the required levels of access, configure [role binding](../../README.md#role-binding-for-access-management) -* For exposing the database via Nodeport services, apply [RBAC](../../rbac/node-rbac.yaml) - ```sh - kubectl apply -f rbac/node-rbac.yaml - ``` -* For automatic storage expansion of block volumes, apply [RBAC](../../rbac/storage-class-rbac.yaml) - ```sh - kubectl apply -f rbac/storage-class-rbac.yaml - ``` -* For automatic execution of custom scripts post database setup or startup, apply [RBAC](../../rbac/persistent-volume-rbac.yaml) - ```sh - kubectl apply -f rbac/persistent-volume-rbac.yaml - ``` +Oracle strongly recommends to comply with the [prerequisites](./PREREQUISITES.md) and the following requirements - ### Mandatory roles and privileges requirements for Oracle Single Instance Database Controller + ### Mandatory Resource Privileges - Single Instance Database(sidb) controller uses Kubernetes objects such as :- + Single Instance Database(sidb) controller mandatorily requires the following Kubernetes resource privileges: - | Resources | Verbs | + | Resources | Privileges | | --- | --- | | Pods | create delete get list patch update watch | | Containers | create delete get list patch update watch | @@ -77,17 +64,32 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst | Secrets | create delete get list patch update watch | | Events | create patch | - ### Optional roles and privileges requirements for Oracle Single Instance Database Controller + For managing the required levels of access, configure [role binding](../../README.md#role-binding-for-access-management) - Single Instance Database(sidb) controller uses Kubernetes objects for some - features which can be given to the controller when using those features; - features and access are :- - - | Functionality | Resources | Verbs | + ### Optional Resource Privileges + + Single Instance Database(sidb) controller optionally requires the following Kubernetes resource privileges depending on the functionality being used: + + | Functionality | Resources | Privileges | | --- | --- | --- | - | NodePort serivces | Nodes | list watch | + | NodePort Services | Nodes | list watch | | Storage Expansion with block volumes | StorageClasses | get list watch | - | Custom scripts execution | PersistentVolumes | get list watch | + | Custom Scripts Execution | PersistentVolumes | get list watch | + + + For exposing the database via Nodeport services, apply [RBAC](../../rbac/node-rbac.yaml) + ```sh + kubectl apply -f rbac/node-rbac.yaml + ``` + For automatic storage expansion of block volumes, apply [RBAC](../../rbac/storage-class-rbac.yaml) + ```sh + kubectl apply -f rbac/storage-class-rbac.yaml + ``` + For automatic execution of custom scripts post database setup or startup, apply [RBAC](../../rbac/persistent-volume-rbac.yaml) + ```sh + kubectl apply -f rbac/persistent-volume-rbac.yaml + ``` + ## SingleInstanceDatabase Resource @@ -215,7 +217,7 @@ To provision a new database instance on the Kubernetes cluster, use the example or ```sh podman login container-registry.oracle.com - podman create secret generic oracle-container-registry-secret --from-file=.dockerconfigjson=${XDG_RUNTIME_DIR}/containers/auth.json --type=kubernetes.io/dockerconfigjson + kubectl create secret generic oracle-container-registry-secret --from-file=.dockerconfigjson=${XDG_RUNTIME_DIR}/containers/auth.json --type=kubernetes.io/dockerconfigjson ``` 3. Provision a new database instance on the cluster by using the following command: From 9ad0784381e9e00ef77c867148c5e64e9bc6cc34 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Thu, 28 Mar 2024 11:23:47 +0000 Subject: [PATCH 073/414] Update PREREQUISITES.md --- docs/sidb/PREREQUISITES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sidb/PREREQUISITES.md b/docs/sidb/PREREQUISITES.md index 1b7edd31..b904f5da 100644 --- a/docs/sidb/PREREQUISITES.md +++ b/docs/sidb/PREREQUISITES.md @@ -11,7 +11,7 @@ To deploy Oracle Single Instance Database in Kubernetes using the OraOperator, c Build Oracle REST Data Service Container Images from source following the instructions at [https://github.com/oracle/docker-images/tree/main/OracleRestDataServices](https://github.com/oracle/docker-images/tree/main/OracleRestDataServices). Supported Oracle REST Data Service version is 21.4.2 -* ### Ensure Sufficient Disk Space in Kubernetes Workers Nodes +* ### Ensure Sufficient Disk Space in Kubernetes Worker Nodes Provision Kubernetes worker nodes with recommended 250 GiB or more of free disk space required for pulling the base and patched database container images. If deploying on cloud you may choose to increase the custom boot volume size of the worker nodes. From 53e5b0434b2c12e811c4237e0b86695de663ab7f Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Fri, 29 Mar 2024 11:35:00 +0000 Subject: [PATCH 074/414] patch instructions --- docs/sidb/README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 7119099f..ca0b6a50 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -579,7 +579,8 @@ The command above will delete the database pods and associated service. Some advanced database configuration scenarios are as follows: #### Run Database with Multiple Replicas -In multiple replicas mode, more than one pod is created for the database. The database is open and mounted by one of the replica pods. Other replica pods have instances started but not mounted, and serve to provide a quick cold fail-over in case the active pod goes down. Multiple replicas are also helpful in [patching](#patch-existing-database) operation. Ensure that you have multiple replicas of the database pods running before you start the patching operation for minimum downtime. +In multiple replicas mode, more than one pod is created for the database. Setting the replica count equal to or more than the number of worker nodes helps in distributing the replicas accross all the nodes that have access to the database persistent storage volume. +The database is open and mounted by one of the replica pods. Other replica pods have the database instance started but not mounted, and serve to provide a quick cold fail-over in case the active pod goes down. To enable multiple replicas, update the replica attribute in the `.yaml`, and apply by using the `kubectl apply` or `kubectl scale` commands. @@ -838,7 +839,7 @@ $ kubectl --type=merge -p '{"spec":{"setAsPrimaryDatabase":"ORCLS1"}}' patch dat ### Patch Primary and Standby databases in Data Guard configuration -Databases (both primary and standby) running in you cluster and managed by the Oracle Database operator can be patched or rolled back between release updates of the same major release. While patching databases configured with the dataguard broker you need to first patch the Primary database followed by Standby databases in any order. +Databases (both primary and standby) running in you cluster and managed by the Oracle Database operator can be patched between release updates of the same major release. To patch an existing database, edit and apply the **[config/samples/sidb/singleinstancedatabase_patch.yaml](../../config/samples/sidb/singleinstancedatabase_patch.yaml)** file of the database resource/object either by specifying a new release update for image attributes, or by running the following command: @@ -846,6 +847,15 @@ To patch an existing database, edit and apply the **[config/samples/sidb/singlei kubectl --type=merge -p '{"spec":{"image":{"pullFrom":"patched-image:tag","pullSecrets":"pull-secret"}}}' patch singleinstancedatabase ``` +Follow these steps for patching databases configured with the dataguard broker: +1. First patch all the standby databases by replacing the image with the new release update image +2. Perform switch over of the primary to one of the standby databases +3. Now patch the original primary database (currently standby after #2) + After #3 the software for primary and standby databases is at the same release update +4. Now bounce the current primary database by updating the replica count to 0 and then 1 + #4 will trigger a datapatch execution resulting in patching of the datafiles +5. Finally perform switch over of the current primary back to the original primary (current standby) + ### Delete the Data Guard Configuration From a6d393885337b95261507d3f1c709ba7d6883aab Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Fri, 29 Mar 2024 12:24:04 +0000 Subject: [PATCH 075/414] Patching Enhancements --- .../v1alpha1/singleinstancedatabase_webhook.go | 8 +------- commons/database/constants.go | 2 ++ .../sidb/singleinstancedatabase_standby.yaml | 2 ++ .../database/singleinstancedatabase_controller.go | 15 +++++++++------ 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 8cf6f75a..2ec64f88 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -134,12 +134,6 @@ func (r *SingleInstanceDatabase) Default() { r.Spec.Replicas = 1 } } - - if r.Spec.PrimaryDatabaseRef != "" && r.Spec.CreateAs == "standby" { - if r.Spec.Replicas == 0 { - r.Spec.Replicas = 1 - } - } } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. @@ -213,7 +207,7 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { field.Invalid(field.NewPath("spec").Child("enableTCPS"), r.Spec.EnableTCPS, "enableTCPS cannot be specified for standby databases")) } - + } // Replica validation diff --git a/commons/database/constants.go b/commons/database/constants.go index 3fa62b30..6f27750d 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -100,6 +100,8 @@ const StandbyDatabasePrerequisitesSQL string = "ALTER SYSTEM SET db_create_file_ "\nALTER DATABASE ADD STANDBY LOGFILE THREAD 1 SIZE 200M;" + "\nALTER DATABASE ADD STANDBY LOGFILE THREAD 1 SIZE 200M;" + "\nALTER SYSTEM SET STANDBY_FILE_MANAGEMENT=AUTO;" + + "\nALTER SYSTEM SET dg_broker_config_file1='/opt/oracle/oradata/dbconfig/dr1${ORACLE_SID}.dat' scope=both;" + + "\nALTER SYSTEM SET dg_broker_config_file2='/opt/oracle/oradata/dbconfig/dr2${ORACLE_SID}.dat';" + "\nALTER SYSTEM SET dg_broker_start=TRUE;" const GetDBOpenMode string = "select open_mode from v\\$database;" diff --git a/config/samples/sidb/singleinstancedatabase_standby.yaml b/config/samples/sidb/singleinstancedatabase_standby.yaml index 0674f838..644438b4 100644 --- a/config/samples/sidb/singleinstancedatabase_standby.yaml +++ b/config/samples/sidb/singleinstancedatabase_standby.yaml @@ -43,3 +43,5 @@ spec: ## if loadBalService : false, service type = "NodePort" else "LoadBalancer" loadBalancer: false + replicas: 1 + \ No newline at end of file diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index c7d94f3a..2f0e84f7 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -244,9 +244,11 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct } else { // Database is in role of standby - err = SetupStandbyDatabase(r, singleInstanceDatabase, referredPrimaryDatabase, ctx, req) - if err != nil { - return requeueY, err + if !singleInstanceDatabase.Status.DgBrokerConfigured { + err = SetupStandbyDatabase(r, singleInstanceDatabase, referredPrimaryDatabase, ctx, req) + if err != nil { + return requeueY, err + } } databaseOpenMode, err := dbcommons.GetDatabaseOpenMode(readyPod, r, r.Config, ctx, req, singleInstanceDatabase.Spec.Edition) @@ -257,7 +259,7 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct } r.Log.Info("DB openMode Output") r.Log.Info(databaseOpenMode) - if databaseOpenMode == "READ_ONLY" { + if databaseOpenMode == "READ_ONLY" || databaseOpenMode == "MOUNTED" { out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.ModifyStdbyDBOpenMode, dbcommons.SQLPlusCLI)) if err != nil { r.Log.Error(err, err.Error()) @@ -792,7 +794,7 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns Lifecycle: &corev1.Lifecycle{ PreStop: &corev1.LifecycleHandler{ Exec: &corev1.ExecAction{ - Command: []string{"/bin/sh", "-c", "/bin/echo -en 'shutdown abort;\n' | env ORACLE_SID=${ORACLE_SID^^} sqlplus -S / as sysdba"}, + Command: []string{"/bin/sh", "-c", "/bin/echo -en 'shutdown immediate;\n' | env ORACLE_SID=${ORACLE_SID^^} sqlplus -S / as sysdba"}, }, }, }, @@ -3400,7 +3402,7 @@ func SetupListenerPrimaryForDG(r *SingleInstanceDatabaseReconciler, p *dbapi.Sin func SetupInitParamsPrimaryForDG(r *SingleInstanceDatabaseReconciler, primaryReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) error { r.Log.Info("Running StandbyDatabasePrerequisitesSQL in the primary database") out, err := dbcommons.ExecCommand(r, r.Config, primaryReadyPod.Name, primaryReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | sqlplus -s / as sysdba", dbcommons.StandbyDatabasePrerequisitesSQL)) + fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.StandbyDatabasePrerequisitesSQL, dbcommons.SQLPlusCLI)) if err != nil { return fmt.Errorf("unable to run StandbyDatabasePrerequisitesSQL in primary database") } @@ -3465,6 +3467,7 @@ func GetAllPdbInDatabase(r *SingleInstanceDatabaseReconciler, dbReadyPod corev1. out, err := dbcommons.ExecCommand(r, r.Config, dbReadyPod.Name, dbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | sqlplus -s / as sysdba", dbcommons.GetPdbsSQL)) if err != nil { + r.Log.Error(err, err.Error()) return pdbs, err } r.Log.Info("GetPdbsSQL Output") From 8a0de28b063fad5f32ed278b726adfb57591e62a Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Fri, 29 Mar 2024 18:44:26 +0000 Subject: [PATCH 076/414] Tinglwan doc change --- .../samples/adb/autonomousdatabase_backup.yaml | 2 ++ ...tonomousdatabase_update_network_access.yaml | 2 +- ...EMAND_BACKUP.md => ADB_LONG_TERM_BACKUP.md} | 18 +++++++++--------- 3 files changed, 12 insertions(+), 10 deletions(-) rename docs/adb/{ADB_ON_DEMAND_BACKUP.md => ADB_LONG_TERM_BACKUP.md} (69%) diff --git a/config/samples/adb/autonomousdatabase_backup.yaml b/config/samples/adb/autonomousdatabase_backup.yaml index c134aaf6..0099a347 100644 --- a/config/samples/adb/autonomousdatabase_backup.yaml +++ b/config/samples/adb/autonomousdatabase_backup.yaml @@ -16,6 +16,8 @@ spec: # ociADB: # ocid: ocid1.autonomousdatabase... displayName: autonomousdatabasebackup-sample + isLongTermBackup: true + retentionPeriodInDays: 90 # minimum retention period is 90 days # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: diff --git a/config/samples/adb/autonomousdatabase_update_network_access.yaml b/config/samples/adb/autonomousdatabase_update_network_access.yaml index 0762efcb..f0e98806 100644 --- a/config/samples/adb/autonomousdatabase_update_network_access.yaml +++ b/config/samples/adb/autonomousdatabase_update_network_access.yaml @@ -32,7 +32,7 @@ spec: # accessType: PRIVATE # privateEndpoint: # subnetOCID: ocid1.subnet... - # nsgOCIDs: + # nsgOCIDs: # Optional # - ocid1.networksecuritygroup... # # Uncomment this block to configure the network access of an dedicated Autonomous Database (ADB-D) with an access control list. diff --git a/docs/adb/ADB_ON_DEMAND_BACKUP.md b/docs/adb/ADB_LONG_TERM_BACKUP.md similarity index 69% rename from docs/adb/ADB_ON_DEMAND_BACKUP.md rename to docs/adb/ADB_LONG_TERM_BACKUP.md index 046d62ca..4720697d 100644 --- a/docs/adb/ADB_ON_DEMAND_BACKUP.md +++ b/docs/adb/ADB_LONG_TERM_BACKUP.md @@ -1,23 +1,21 @@ -# Creating an On-Demand Backup of an Oracle Autonomous Database +# Creating Long-Term Backups of an Oracle Autonomous Database -To create on-demand backups of Autonomous Databases, use this procedure. +To create long-term backups of Autonomous Databases, use this procedure. -Oracle Cloud Infrastructure (OCI) automatically backs up your Autonomous Databases, and retains these backups for 60 days. You can restore and recover your database to any point-in-time in this retention period. Automatic backups are full backups taken every 60 days, with daily incremental backups. You can also create on-demand backups for your database. On-demand backups are stored in an Object Storage bucket that you create, and are retained for 60 days. Note that Oracle doesn not recommend create or use on-demand backups. For more information, please visit [Backing Up and Restoring Autonomous Database](https://docs.oracle.com/en-us/iaas/Content/Database/Tasks/adbbackingup.htm) and [Backup and Restore Notes](https://docs.oracle.com/en-us/iaas/autonomous-database-shared/doc/backup-restore-notes.html). +Oracle Cloud Infrastructure (OCI) automatically backs up your Autonomous Databases, and retains these backups for 60 days. You can restore and recover your database to any point-in-time in this retention period. Automatic backups are full backups taken every 60 days, with daily incremental backups. You can also create long-term backups for your database with a retention period between 3 months and up to 10 years. For more information, please visit [Create Long-Term Backups on Autonomous Database](https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/backup-long-term.html) and [Backup and Restore Notes](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/backup-restore-notes.html). -## Prerequisites - -To hold your Autonomous Database on-demand backups, you must create an Oracle Cloud Infrastructure Object Storage bucket, and configure your database to connect to it. To finish setting up on-demand backup storage, follow the steps in this page: [Setting Up a Bucket to Store Manual Backups](https://docs.oracle.com/en-us/iaas/Content/Database/Tasks/adbbackingup.htm#creatingbucket). Creating an Autonomous Database on-demand backup object storage bucket is a one-time operation. - -## Create On-Demand Backup +## Create Long-Term Backup To back up an Autonomous Database, complete this procedure. 1. Add the following fields to the AutonomousDatabaseBackup resource definition. An example `.yaml` file is available here: [`config/samples/adb/autonomousdatabase_backup.yaml`](./../../config/samples/adb/autonomousdatabase_backup.yaml) | Attribute | Type | Description | Required? | |----|----|----|----| + | `spec.displayName` | string | The user-friendly name for the backup. This name does not have to be unique. | Yes | + | `spec.isLongTermBackup` | boolean | Indicates whether the backup is long-term. | Yes | + | `spec.retentionPeriodInDays` | string | Retention period, in days, for long-term backups. Minimum retention period is 90 days. | Yes | | `spec.target.k8sADB.name` | string | The name of custom resource of the target Autonomous Database. Choose either the `spec.target.k8sADB.name` or the `spec.target.ociADB.ocid`, but not both. | Conditional | | `spec.target.ociADB.ocid` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the target AutonomousDatabase. Choose either the `spec.target.k8sADB.name` or the `spec.target.ociADB.ocid`, but not both. | Conditional | - | `spec.displayName` | string | The user-friendly name for the backup. This name does not have to be unique. | Yes | | `spec.ociConfig` | dictionary | Not required when the Operator is authorized with [Instance Principal](./ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from this section: [Authorized with API Key Authentication](./ADB_PREREQUISITES.md#authorized-with-api-key-authentication). | Conditional | | `spec.ociConfig.configMapName` | string | Name of the ConfigMap that holds the local OCI configuration | Conditional | | `spec.ociConfig.secretName`| string | Name of the Kubernetes (K8s) Secret that holds the private key value | Conditional | @@ -36,6 +34,8 @@ To back up an Autonomous Database, complete this procedure. # ociADB: # ocid: ocid1.autonomousdatabase... displayName: autonomousdatabasebackup-sample + isLongTermBackup: true + retentionPeriodInDays: 90 ociConfig: configMapName: oci-cred secretName: oci-privatekey From ca260cc26d94e3817cf61839e2063a84b3f2fbf3 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Tue, 2 Apr 2024 06:00:07 +0000 Subject: [PATCH 077/414] ReadMe update for uninstall --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c30fe4a8..f8e13d85 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,10 @@ Oracle strongly recommends that you ensure your system meets the following [Prer kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml ``` -* ### Role Binding for access management +* ### Create Role Bindings for Access Management OraOperator supports the following two modes of deployment: - ##### 1. Cluster Scope Deployment + ##### 1. Cluster Scoped Deployment This is the default mode wherein OraOperator is deployed to operate in a cluster scope and watch all the namespaces cluster wide @@ -80,7 +80,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer kubectl apply -f oracle-database-operator.yaml ``` - ##### 2. Namespaced Scope Deployment + ##### 2. Namespace Scoped Deployment In this mode OraOperator can be deployed to operate in a namespaced scope and watch one or many namespaces @@ -161,7 +161,7 @@ YAML file templates are available under [`/config/samples`](./config/samples/). To uninstall the operator, the final step consists of deciding whether you want to delete the custom resource definitions (CRDs) and Kubernetes APIServices introduced into the cluster by the operator. Choose one of the following options: -* ### Deleting the CRDs and APIServices +* ### Delete the CRDs and APIServices To delete all the CRD instances deployed to cluster by the operator, run the following commands, where is the namespace of the cluster object: @@ -180,6 +180,14 @@ YAML file templates are available under [`/config/samples`](./config/samples/). kubectl delete databaseobserver.observability.oracle.com --all -n ``` +* ### Delete the RBACs + + ```sh + cat rbac/* | kubectl delete -f - + ``` + +* ### Delete the Deployment + After all CRD instances are deleted, it is safe to remove the CRDs, APIServices and operator deployment. To remove these files, use the following command: ```sh @@ -227,5 +235,5 @@ See [Reporting security vulnerabilities](./SECURITY.md) ## License -Copyright (c) 2022, 2023 Oracle and/or its affiliates. +Copyright (c) 2022, 2024 Oracle and/or its affiliates. Released under the Universal Permissive License v1.0 as shown at [https://oss.oracle.com/licenses/upl/](https://oss.oracle.com/licenses/upl/) From c84fa7a9e1c64bd30e08d9a326bba4a36b512115 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Tue, 2 Apr 2024 10:34:18 +0000 Subject: [PATCH 078/414] Bugfix/mqureshi/36454509 --- apis/database/v1alpha1/singleinstancedatabase_webhook.go | 5 +++-- controllers/database/singleinstancedatabase_controller.go | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 2ec64f88..1a47207d 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -129,8 +129,9 @@ func (r *SingleInstanceDatabase) Default() { } if r.Spec.Edition == "express" || r.Spec.Edition == "free" { - if r.Status.Replicas == 1 { - // default the replicas for XE + // Allow zero replicas as a means to bounce the DB + if r.Status.Replicas == 1 && r.Spec.Replicas > 1 { + // If not zero, default the replicas to 1 r.Spec.Replicas = 1 } } diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 2f0e84f7..215c18e5 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -791,6 +791,12 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns Containers: []corev1.Container{{ Name: m.Name, Image: m.Spec.Image.PullFrom, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + // Allow priority elevation for DB processes + Add: []corev1.Capability{"SYS_NICE"}, + }, + }, Lifecycle: &corev1.Lifecycle{ PreStop: &corev1.LifecycleHandler{ Exec: &corev1.ExecAction{ From 9b80b7845a1ac314c9495ebe366ad315dc7311dd Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Tue, 2 Apr 2024 10:54:26 +0000 Subject: [PATCH 079/414] Dataguard Controller enhancements and bugfixes --- .../database/dataguardbroker_controller.go | 32 ++++++++++--------- .../singleinstancedatabase_controller.go | 1 + 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/controllers/database/dataguardbroker_controller.go b/controllers/database/dataguardbroker_controller.go index 2a8fe163..a362d57b 100644 --- a/controllers/database/dataguardbroker_controller.go +++ b/controllers/database/dataguardbroker_controller.go @@ -835,6 +835,22 @@ func (r *DataguardBrokerReconciler) SetAsPrimaryDatabase(sidbSid string, targetS return requeueY } + dbInDgConfig := false + for i := 0; i < len(databases); i++ { + splitstr := strings.Split(databases[i], ":") + if strings.ToUpper(splitstr[0]) == strings.ToUpper(targetSid) { + dbInDgConfig = true + break + } + } + + if !dbInDgConfig { + eventReason := "Cannot Switchover" + eventMsg := fmt.Sprintf("Database %s not a part of the dataguard configuration", targetSid) + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + return requeueN + } + // Fetch the current Primary database primarySid := dbcommons.GetPrimaryDatabase(databases) if strings.EqualFold(primarySid, targetSid) { @@ -1126,20 +1142,6 @@ func (r *DataguardBrokerReconciler) cleanupDataguardBroker(req ctrl.Request, ctx return result, nil } - // Get its Role - out, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err - } - // check if its PRIMARY database - if strings.ToUpper(out) != "PRIMARY" { - eventReason := "Deletion" - eventMsg := "DataGuard Broker cannot be deleted since primaryDatabaseRef is not in PRIMARY role" - r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) - return requeueY, errors.New(eventMsg) - } - // Get Primary database to remove dataguard configuration _, _, err = dbcommons.GetDatabasesInDgConfig(sidbReadyPod, r, r.Config, ctx, req) if err != nil { @@ -1149,7 +1151,7 @@ func (r *DataguardBrokerReconciler) cleanupDataguardBroker(req ctrl.Request, ctx //primarySid := dbcommons.GetPrimaryDatabase(databases) - out, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | dgmgrl / as sysdba ", dbcommons.RemoveDataguardConfiguration)) if err != nil { log.Error(err, err.Error()) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 215c18e5..ee9acd10 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -260,6 +260,7 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct r.Log.Info("DB openMode Output") r.Log.Info(databaseOpenMode) if databaseOpenMode == "READ_ONLY" || databaseOpenMode == "MOUNTED" { + // Changing the open mode for sidb to "READ ONLY WITH APPLY" out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.ModifyStdbyDBOpenMode, dbcommons.SQLPlusCLI)) if err != nil { r.Log.Error(err, err.Error()) From d85e312ca237675ca6b4ccb2d57c73150a3a7a92 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Wed, 3 Apr 2024 10:59:29 +0000 Subject: [PATCH 080/414] Bugfixes/mqureshi/1.1.0 --- config/samples/sidb/dataguardbroker.yaml | 2 +- .../database/singleinstancedatabase_controller.go | 9 ++++++++- docs/sidb/README.md | 1 - 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/config/samples/sidb/dataguardbroker.yaml b/config/samples/sidb/dataguardbroker.yaml index 26ab9d79..5425afb7 100644 --- a/config/samples/sidb/dataguardbroker.yaml +++ b/config/samples/sidb/dataguardbroker.yaml @@ -10,7 +10,7 @@ metadata: namespace: default spec: - ## Primary DB ref. This can be of kind SingleInstanceDatabase or CloneDB + ## Primary DB ref. This is of kind SingleInstanceDatabase primaryDatabaseRef: "sidb-sample" ## Standby DB pod CRD Metadata Name to add this DB to DG config diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index ee9acd10..44946256 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -801,7 +801,14 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns Lifecycle: &corev1.Lifecycle{ PreStop: &corev1.LifecycleHandler{ Exec: &corev1.ExecAction{ - Command: []string{"/bin/sh", "-c", "/bin/echo -en 'shutdown immediate;\n' | env ORACLE_SID=${ORACLE_SID^^} sqlplus -S / as sysdba"}, + Command: func() []string { + // To terminate any zombie instances left over due to forced termination + shutdown_mode := "abort" + if m.Spec.CreateAs == "standby" { + shutdown_mode = "immediate" + } + return []string{"/bin/sh", "-c", "/bin/echo -en 'shutdown " + shutdown_mode + ";\n' | env ORACLE_SID=${ORACLE_SID^^} sqlplus -S / as sysdba"} + }(), }, }, }, diff --git a/docs/sidb/README.md b/docs/sidb/README.md index ca0b6a50..8d7afe16 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -867,7 +867,6 @@ $ kubectl delete dataguardbroker dgbroker-sample dataguardbroker.database.oracle.com/dgbroker-sample deleted ``` -**Note:** Deleting of DataGuardBroker resource is allowed only when role of `.spec.primaryDatabaseRef` is PRIMARY #### Delete Standby Database ```sh From 8b7c6fe5a2e2ee6267db940cab78f2d11e8ec62f Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Wed, 3 Apr 2024 16:12:04 +0000 Subject: [PATCH 081/414] bugfix/idesai/36473360 --- controllers/database/dataguardbroker_controller.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/controllers/database/dataguardbroker_controller.go b/controllers/database/dataguardbroker_controller.go index a362d57b..9faaefd2 100644 --- a/controllers/database/dataguardbroker_controller.go +++ b/controllers/database/dataguardbroker_controller.go @@ -398,18 +398,18 @@ func (r *DataguardBrokerReconciler) setupDataguardBrokerConfiguration(m *dbapi.D log.Error(err, err.Error()) return requeueY } - _, ok := dbSet[standbyDatabase.Status.Sid] - if ok { - log.Info("A database with the same SID is already configured in the DG") - r.Recorder.Eventf(m, corev1.EventTypeWarning, "Spec Error", "A database with the same SID "+standbyDatabase.Status.Sid+" is already configured in the DG") - continue - } // Check if dataguard broker is already configured for the standby database if standbyDatabase.Status.DgBrokerConfigured { log.Info("Dataguard broker for standbyDatabase : " + standbyDatabase.Name + " is already configured") continue } + _, ok := dbSet[standbyDatabase.Status.Sid] + if ok { + log.Info("A database with the same SID is already configured in the DG") + r.Recorder.Eventf(m, corev1.EventTypeWarning, "Spec Error", "A database with the same SID "+standbyDatabase.Status.Sid+" is already configured in the DG") + continue + } m.Status.Status = dbcommons.StatusCreating r.Status().Update(ctx, m) From 1d10080bba20735154f76956a7584b306204d1ed Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Thu, 4 Apr 2024 13:10:26 +0530 Subject: [PATCH 082/414] Fix shutdown condition --- controllers/database/singleinstancedatabase_controller.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 44946256..0312f10f 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -802,10 +802,10 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns PreStop: &corev1.LifecycleHandler{ Exec: &corev1.ExecAction{ Command: func() []string { - // To terminate any zombie instances left over due to forced termination - shutdown_mode := "abort" - if m.Spec.CreateAs == "standby" { - shutdown_mode = "immediate" + shutdown_mode := "immediate" + if m.Spec.Edition == "express" || m.Spec.Edition == "free" { + // To terminate any zombie instances left over due to forced termination + shutdown_mode = "abort" } return []string{"/bin/sh", "-c", "/bin/echo -en 'shutdown " + shutdown_mode + ";\n' | env ORACLE_SID=${ORACLE_SID^^} sqlplus -S / as sysdba"} }(), From a4b5dcfe49c4c5f9c53fdaa05b4f3ad143d55570 Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Thu, 4 Apr 2024 14:48:44 +0530 Subject: [PATCH 083/414] Add comments --- controllers/database/singleinstancedatabase_controller.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 0312f10f..a0f88f0b 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -802,8 +802,10 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns PreStop: &corev1.LifecycleHandler{ Exec: &corev1.ExecAction{ Command: func() []string { + // For patching use cases shutdown immediate is needed especially for standby databases shutdown_mode := "immediate" if m.Spec.Edition == "express" || m.Spec.Edition == "free" { + // express/free do not support patching // To terminate any zombie instances left over due to forced termination shutdown_mode = "abort" } From d48609533014863631c1707c56ee10b17e5a98fc Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Thu, 4 Apr 2024 11:23:39 +0000 Subject: [PATCH 084/414] Fix standby validation --- controllers/database/singleinstancedatabase_controller.go | 4 ++-- docs/sidb/README.md | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index a0f88f0b..fa01ae7e 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -508,7 +508,7 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab m.Status.PrimaryDatabase = n.Name } - if m.Spec.CreateAs == "standby" { + if m.Spec.CreateAs == "standby" && m.Status.Role != "PRIMARY" { // Fetch the Primary database reference, required for all iterations err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: m.Spec.PrimaryDatabaseRef}, rp) @@ -545,7 +545,7 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab return requeueY, err } - r.Log.Info("Settingup Primary Database for standby creation...") + r.Log.Info("Setting up Primary Database for standby creation...") err = SetupPrimaryDatabase(r, m, rp, ctx, req) if err != nil { return requeueY, err diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 8d7afe16..57272c73 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -868,6 +868,8 @@ $ kubectl delete dataguardbroker dgbroker-sample dataguardbroker.database.oracle.com/dgbroker-sample deleted ``` +**Note:** If a switch over to standby was performed, make sure to switch back to the orginal primary database before deleting the dataguard broker resource + #### Delete Standby Database ```sh $ kubectl delete singleinstancedatabase stdby-1 @@ -881,8 +883,7 @@ Custom scripts (sql and/or shell scripts) can be executed after the initial data Create a persistent volume using [static provisioning](#static-persistence) and then specify the name of this volume with the `<.spec.persistence.scriptsVolumeName>` field which corresponds to the `scriptsVolumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. -**Note:** -- Executing custom scripts requires read and list access for persistent volumes as mentioned in [prerequisites](#prerequisites) +**Note:** Executing custom scripts requires read and list access for persistent volumes as mentioned in [prerequisites](#prerequisites) ## OracleRestDataService Resource From ca2aeddaa3c7d0ebfb2995c37aad219e9105efa5 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Fri, 5 Apr 2024 11:10:46 +0000 Subject: [PATCH 085/414] Update README.md --- docs/sidb/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 57272c73..546f8892 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -868,7 +868,7 @@ $ kubectl delete dataguardbroker dgbroker-sample dataguardbroker.database.oracle.com/dgbroker-sample deleted ``` -**Note:** If a switch over to standby was performed, make sure to switch back to the orginal primary database before deleting the dataguard broker resource +**Note:** If a switch over to standby was performed, make sure to switch back to the original primary database before deleting the dataguard broker resource #### Delete Standby Database ```sh From c2fc6eba7478c1315656c3efa920990409089a7a Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Fri, 5 Apr 2024 12:19:28 +0000 Subject: [PATCH 086/414] Failover matrix --- docs/sidb/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 57272c73..083734f7 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -584,7 +584,20 @@ The database is open and mounted by one of the replica pods. Other replica pods To enable multiple replicas, update the replica attribute in the `.yaml`, and apply by using the `kubectl apply` or `kubectl scale` commands. +The following table depicts the fail over matrix for any destructive operation to the primary replica pod + +| Pod Destructive Operation | Pod Restart/FailOver| + | --- | --- | + | Database instance crash | Yes | + | Force delete pod with zero grace period | Yes | + | Gracefully delete pod | Yes | + | Node running primary replica dies | Yes | + | Direct shutdown [All modes] | Yes | + | Maintenance shutdown [All modes] | No | + | PDB close | No | + **Note:** +- Maintence shutdown/startup can be executed using the scripts /home/oracle/shutDown.sh and /home/oracle/startUp.sh - This functionality requires the [k8s extension](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/k8s) extended images. The database image from the container registry `container-registry.oracle.com` includes the K8s extension. - Because Oracle Database Express Edition (XE) does not support [k8s extension](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/k8s), it does not support multiple replicas. - If the `ReadWriteOnce` access mode is used, all the replicas will be scheduled on the same node where the persistent volume would be mounted. From 92486d630ebb5be2163e040909fe8439002efc99 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Mon, 8 Apr 2024 06:18:49 +0000 Subject: [PATCH 087/414] Added databaseobserver webhook --- PROJECT | 4 + .../v1alpha1/databaseobserver_webhook.go | 185 ++++++++++++++++++ .../v1alpha1/zz_generated.deepcopy.go | 2 +- commons/observability/constants.go | 1 - config/webhook/manifests.yaml | 40 ++++ .../databaseobserver_controller.go | 8 - main.go | 5 + 7 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 apis/observability/v1alpha1/databaseobserver_webhook.go diff --git a/PROJECT b/PROJECT index 5cbb036c..7b3ec718 100644 --- a/PROJECT +++ b/PROJECT @@ -145,4 +145,8 @@ resources: kind: DatabaseObserver path: github.com/oracle/oracle-database-operator/apis/observability/v1alpha1 version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1beta1 version: "3" diff --git a/apis/observability/v1alpha1/databaseobserver_webhook.go b/apis/observability/v1alpha1/databaseobserver_webhook.go new file mode 100644 index 00000000..2ab9b732 --- /dev/null +++ b/apis/observability/v1alpha1/databaseobserver_webhook.go @@ -0,0 +1,185 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1alpha1 + +import ( + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "strings" +) + +// log is for logging in this package. +var databaseobserverlog = logf.Log.WithName("databaseobserver-resource") + +const ( + AllowedExporterImage = "container-registry.oracle.com/database/observability-exporter" + ErrorSpecValidationMissingConnString = "a required field for database connection string secret is missing or does not have a value" + ErrorSpecValidationMissingDBUser = "a required field for database user secret is missing or does not have a value" + ErrorSpecValidationMissingDBVaultField = "a field for the OCI vault has a value but the other required field is missing or does not have a value" + ErrorSpecValidationMissingOCIConfig = "a field(s) for the OCI Config is missing or does not have a value when fields for the OCI vault has values" + ErrorSpecValidationMissingDBPasswordSecret = "a required field for the database password secret is missing or does not have a value" + ErrorSpecExporterImageNotAllowed = "a different exporter image was found, only official database exporter container images are currently supported" +) + +func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +//+kubebuilder:webhook:path=/mutate-observability-oracle-com-v1alpha1-databaseobserver,mutating=true,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,verbs=create;update,versions=v1alpha1,name=mdatabaseobserver.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &DatabaseObserver{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *DatabaseObserver) Default() { + databaseobserverlog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:verbs=create;update,path=/validate-observability-oracle-com-v1alpha1-databaseobserver,mutating=false,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,versions=v1alpha1,name=vdatabaseobserver.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &DatabaseObserver{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { + databaseobserverlog.Info("validate create", "name", r.Name) + + var e field.ErrorList + ns := dbcommons.GetWatchNamespaces() + + // Check for namespace/cluster scope access + if _, isDesiredNamespaceWithinScope := ns[r.Namespace]; !isDesiredNamespaceWithinScope && len(ns) > 0 { + e = append(e, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + + // Check required secret for db user has value + if r.Spec.Database.DBUser.SecretName == "" { + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbUser").Child("secret"), r.Spec.Database.DBUser.SecretName, + ErrorSpecValidationMissingDBUser)) + } + + // Check required secret for db connection string has value + if r.Spec.Database.DBConnectionString.SecretName == "" { + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbConnectionString").Child("secret"), r.Spec.Database.DBConnectionString.SecretName, + ErrorSpecValidationMissingConnString)) + } + + // The other vault field must have value if one does + if (r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName == "") || + (r.Spec.Database.DBPassword.VaultSecretName != "" && r.Spec.Database.DBPassword.VaultOCID == "") { + + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword"), r.Spec.Database.DBPassword, + ErrorSpecValidationMissingDBVaultField)) + } + + // if vault fields have value, ociConfig must have values + if r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName != "" && + (r.Spec.OCIConfig.SecretName == "" || r.Spec.OCIConfig.ConfigMapName == "") { + + e = append(e, + field.Invalid(field.NewPath("spec").Child("ociConfig"), r.Spec.OCIConfig, + ErrorSpecValidationMissingOCIConfig)) + } + + // If all of {DB Password Secret Name and vaultOCID+vaultSecretName} have no value, then error out + if r.Spec.Database.DBPassword.SecretName == "" && + r.Spec.Database.DBPassword.VaultOCID == "" && + r.Spec.Database.DBPassword.VaultSecretName == "" { + + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword").Child("secret"), r.Spec.Database.DBPassword.SecretName, + ErrorSpecValidationMissingDBPasswordSecret)) + } + + // disallow usage of any other image than the observability-exporter + if r.Spec.Exporter.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.ExporterImage, AllowedExporterImage) { + e = append(e, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.ExporterImage, + ErrorSpecExporterImageNotAllowed)) + } + + // Return if any errors + if len(e) > 0 { + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + } + return nil, nil + +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + databaseobserverlog.Info("validate update", "name", r.Name) + var e field.ErrorList + + // disallow usage of any other image than the observability-exporter + if r.Spec.Exporter.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.ExporterImage, AllowedExporterImage) { + e = append(e, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.ExporterImage, + ErrorSpecExporterImageNotAllowed)) + } + // Return if any errors + if len(e) > 0 { + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateDelete() (admission.Warnings, error) { + databaseobserverlog.Info("validate delete", "name", r.Name) + + return nil, nil +} diff --git a/apis/observability/v1alpha1/zz_generated.deepcopy.go b/apis/observability/v1alpha1/zz_generated.deepcopy.go index d0444b63..39b438eb 100644 --- a/apis/observability/v1alpha1/zz_generated.deepcopy.go +++ b/apis/observability/v1alpha1/zz_generated.deepcopy.go @@ -45,7 +45,7 @@ package v1alpha1 import ( "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. diff --git a/commons/observability/constants.go b/commons/observability/constants.go index c7a15b18..a4d85b71 100644 --- a/commons/observability/constants.go +++ b/commons/observability/constants.go @@ -109,7 +109,6 @@ const ( const ( ErrorCRRetrieve = "an error occurred with retrieving the cr" ErrorStatusUpdate = "an error occurred with updating the cr status" - ErrorSpecValidationMissingDBPassword = "an error occurred with validating the spec due to missing dbpassword values" ErrorSpecValidationFailedDueToAnError = "an error occurred with validating the exporter deployment spec" ErrorDeploymentPodsFailure = "an error occurred with deploying exporter deployment pods" ErrorDeploymentUpdate = "an error occurred with updating exporter deployment" diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 28f50b79..175abd47 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -151,6 +151,26 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-observability-oracle-com-v1alpha1-databaseobserver + failurePolicy: Fail + name: mdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 @@ -345,3 +365,23 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-observability-oracle-com-v1alpha1-databaseobserver + failurePolicy: Fail + name: vdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None diff --git a/controllers/observability/databaseobserver_controller.go b/controllers/observability/databaseobserver_controller.go index 10299d63..bd58e71e 100644 --- a/controllers/observability/databaseobserver_controller.go +++ b/controllers/observability/databaseobserver_controller.go @@ -187,14 +187,6 @@ func (r *DatabaseObserverReconciler) initialize(ctx context.Context, api *apiv1. // validateSpecs method checks the values and secrets passed in the spec func (r *DatabaseObserverReconciler) validateSpecs(api *apiv1.DatabaseObserver) error { - // If all of {DB Password Secret Name and vaultOCID+vaultSecretName} have no value, then error out - if api.Spec.Database.DBPassword.SecretName == "" && - api.Spec.Database.DBPassword.VaultOCID == "" && - api.Spec.Database.DBPassword.VaultSecretName == "" { - r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPasswordMissing) - return errors.New(constants.ErrorSpecValidationMissingDBPassword) - } - // If either Vault Fields are empty, then assume a DBPassword secret is supplied. If the DBPassword secret not found, then error out if api.Spec.Database.DBPassword.VaultOCID == "" || api.Spec.Database.DBPassword.VaultSecretName == "" { dbSecret := &corev1.Secret{} diff --git a/main.go b/main.go index 5ff714be..acdd2719 100644 --- a/main.go +++ b/main.go @@ -249,6 +249,10 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") os.Exit(1) } + if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + os.Exit(1) + } } // PDB Reconciler @@ -296,6 +300,7 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "DatabaseObserver") os.Exit(1) } + // +kubebuilder:scaffold:builder // Add index for PDB CR to enable mgr to cache PDBs From 0d0a800dae0e4c45c672e9dc71941a74821d01fb Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 15 Apr 2024 23:15:53 +0000 Subject: [PATCH 088/414] Update README.md Minor style updates --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f8e13d85..b3d58e56 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer OraOperator supports the following two modes of deployment: ##### 1. Cluster Scoped Deployment - This is the default mode wherein OraOperator is deployed to operate in a cluster scope and watch all the namespaces cluster wide + This is the default mode, in which OraOperator is deployed to operate in a cluster, and to monitor all the namespaces in the cluster. - Grant the `serviceaccount:oracle-database-operator-system:default` cluster wide access for the resources by applying [cluster-role-binding.yaml](./rbac/cluster-role-binding.yaml) @@ -74,7 +74,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer kubectl apply -f rbac/cluster-role-binding.yaml ``` - - Then apply the [oracle-database-operator.yaml](./oracle-database-operator.yaml) to deploy the Operator + - Next, apply the [oracle-database-operator.yaml](./oracle-database-operator.yaml) to deploy the Operator ```sh kubectl apply -f oracle-database-operator.yaml @@ -82,22 +82,22 @@ Oracle strongly recommends that you ensure your system meets the following [Prer ##### 2. Namespace Scoped Deployment - In this mode OraOperator can be deployed to operate in a namespaced scope and watch one or many namespaces + In this mode, OraOperator can be deployed to operate in a namespace, and to monitor one or many namespaces. - - Grant `serviceaccount:oracle-database-operator-system:default` service account with resource access in the required namespaces. For example, to watch only the default namespace, apply the [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) + - Grant `serviceaccount:oracle-database-operator-system:default` service account with resource access in the required namespaces. For example, to monitor only the default namespace, apply the [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) ```sh kubectl apply -f rbac/default-ns-role-binding.yaml ``` - For watching additional namespaces, create different role binding files for each namespace taking [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) as a template and changing the `metadata.name` and `metadata.namespace` fields + To watch additional namespaces, create different role binding files for each namespace, using [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) as a template, and changing the `metadata.name` and `metadata.namespace` fields - - Now edit the [oracle-database-operator.yaml](./oracle-database-operator.yaml) to add the required namespaces under `WATCH_NAMESPACE`. Use comma separated values for multiple namespaces + - Next, edit the [oracle-database-operator.yaml](./oracle-database-operator.yaml) to add the required namespaces under `WATCH_NAMESPACE`. Use comma-delimited values for multiple namespaces. ```sh - name: WATCH_NAMESPACE value: "default" ``` - - Then apply the edited [oracle-database-operator.yaml](./oracle-database-operator.yaml) to deploy the Operator + - Finally, apply the edited [oracle-database-operator.yaml](./oracle-database-operator.yaml) to deploy the Operator ```sh kubectl apply -f oracle-database-operator.yaml @@ -106,7 +106,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer * ### ClusterRole and ClusterRoleBinding for NodePort services - For exposing services on each Node's IP and port (the NodePort) apply the [node-rbac.yaml](./rbac/node-rbac.yaml). This is not required for LoadBalancer services. + To expose services on each node's IP and port (the NodePort) apply the [node-rbac.yaml](./rbac/node-rbac.yaml). Note that this step is not required for LoadBalancer services. ```sh kubectl apply -f rbac/node-rbac.yaml @@ -114,7 +114,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer ## Install Oracle DB Operator - Once the above prerequisite changes have been done, to install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from above step. + After you have completed the preceding prerequisite changes, you can install the operator. To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the preceding step. Run the following command @@ -142,7 +142,7 @@ For more details, see [Oracle Database Operator Installation Instructions](./doc ## Getting Started with the Operator (Quickstart) -The quickstarts are designed for specific database configurations: +The following quickstarts are designed for specific database configurations: * [Oracle Autonomous Database](./docs/adb/README.md) * [Oracle Autonomous Container Database](./docs/adb/ACD.md) @@ -152,7 +152,7 @@ The quickstarts are designed for specific database configurations: * [Oracle Base Database Cloud Service (BDBCS)](./docs/dbcs/README.md) -The quickstarts are designed for non-database configurations: +The following quickstart is designed for non-database configurations: * [Oracle Database Observability](./docs/observability/README.md) YAML file templates are available under [`/config/samples`](./config/samples/). You can copy and edit these template files to configure them for your use cases. @@ -196,7 +196,7 @@ YAML file templates are available under [`/config/samples`](./config/samples/). Note: If the CRD instances are not deleted, and the operator is deleted by using the preceding command, then operator deployment and instance objects (pods, services, PVCs, and so on) are deleted. However, if that happens, then the CRD deletion stops responding. This is because the CRD instances have properties that prevent their deletion, and that can only be removed by the operator pod, which is deleted when the APIServices are deleted. -## Docs of the supported Oracle Database configurations +## Documentation for the supported Oracle Database configurations * [Oracle Autonomous Database](https://docs.oracle.com/en-us/iaas/Content/Database/Concepts/adboverview.htm) * [Components of Dedicated Autonomous Database](https://docs.oracle.com/en-us/iaas/autonomous-database/doc/components.html) @@ -210,7 +210,7 @@ See [Contributing to this Repository](./CONTRIBUTING.md) ## Support -You can submit a GitHub issue, and/or you file an [Oracle Support service](https://support.oracle.com/portal/) request, using this product ID: 14430. +You can submit a GitHub issue, oir submit an issue and then file an [Oracle Support service](https://support.oracle.com/portal/) request. To file an issue or a service request, use the following product ID: 14430. ## Security From e68e966d56ef593da8fb4b1175c6e92df1883b5d Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Wed, 24 Apr 2024 18:00:23 +0000 Subject: [PATCH 089/414] bug fix 36545873 --- config/rbac/role.yaml | 1 + controllers/database/cdb_controller.go | 4 +++- docs/multitenant/README.md | 10 +++++----- docs/multitenant/usecase01/makefile | 17 +++++++++++++---- docs/multitenant/usecase03/makefile | 11 ++++++++--- .../multitenant/usecase03/ns_namespace_cdb.yaml | 7 +++++++ .../multitenant/usecase03/ns_namespace_pdb.yaml | 7 +++++++ 7 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 docs/multitenant/usecase03/ns_namespace_cdb.yaml create mode 100644 docs/multitenant/usecase03/ns_namespace_pdb.yaml diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 73e389ec..2f33c915 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -96,6 +96,7 @@ rules: verbs: - get - list + - watch - apiGroups: - '''''' resources: diff --git a/controllers/database/cdb_controller.go b/controllers/database/cdb_controller.go index 4c92edc1..5e6c0aca 100644 --- a/controllers/database/cdb_controller.go +++ b/controllers/database/cdb_controller.go @@ -41,6 +41,7 @@ package controllers import ( "context" "errors" + //"fmt" "strconv" "strings" @@ -281,7 +282,8 @@ func (r *CDBReconciler) validateORDSPods(ctx context.Context, req ctrl.Request, return errors.New("Waiting for ORDS pods to start") } - getORDSStatus := " curl -sSkv -k -X GET https://localhost:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/stable/metadata-catalog/ " + /* /opt/oracle/ords/secrets/$TLSKEY /opt/oracle/ords/secrets/$TLSCRT */ + getORDSStatus := " curl --cert /opt/oracle/ords/secrets/tls.crt --key /opt/oracle/ords/secrets/tls.key -sSkv -k -X GET https://localhost:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/stable/metadata-catalog/ || curl --cert /opt/oracle/ords/secrets/tls.crt --key /opt/oracle/ords/secrets/tls.key -sSkv -X GET http://localhost:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/stable/metadata-catalog/ " readyPods := 0 for _, pod := range podList.Items { if pod.Status.Phase == corev1.PodRunning { diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index 8f862508..b20eaa4a 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -168,11 +168,11 @@ See this [provisioning example setup](./provisioning/example_setup_using_oci_oke Create the certificates and key on your local host, and use them to create the Kubernetes secret. -```bash -genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=cdb-dev-ords" -out server.csr -/usr/bin/echo "subjectAltName=DNS:cdb-dev-ords,DNS:www.example.com" > extfile.txt +```bash +openssl genrsa -out ca.key 2048 +openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost Root CA " -out ca.crt +openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost" -out server.csr +echo "subjectAltName=DNS:cdb-dev-ords,DNS:www.example.com" > extfile.txt openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt ``` diff --git a/docs/multitenant/usecase01/makefile b/docs/multitenant/usecase01/makefile index 1b4e32d8..e468ef62 100644 --- a/docs/multitenant/usecase01/makefile +++ b/docs/multitenant/usecase01/makefile @@ -154,6 +154,8 @@ SKEY=tls.key SCRT=tls.crt CART=ca.crt COMPANY=oracle +LOCALHOST=localhost +RESTPREFIX=cdb-dev step1: createimage step2: tagimage @@ -205,12 +207,19 @@ dboperator: @echo "ORACLE DATABASE OPERATOR" $(KUBECTL) apply -f $(DBOPERATOR) + +#C: Country +#ST: State +#L: locality (city) +#O: Organization Name Organization Unit +#CN: Common Name + tlscert: - @echo "CREATING TLS CERTIFICATES" + @echo "CREATING TLS CERTIFICATES" $(OPENSSL) genrsa -out ca.key 2048 - $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(NAMESPACE)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(NAMESPACE),DNS:www.example.com" > extfile.txt + $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER) /CN=$(LOCALHOST) Root CA " -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER) /CN=$(LOCALHOST)" -out server.csr + $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER),DNS:www.example.com" > extfile.txt $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) tlssecret: diff --git a/docs/multitenant/usecase03/makefile b/docs/multitenant/usecase03/makefile index af52316f..fc95cfa0 100644 --- a/docs/multitenant/usecase03/makefile +++ b/docs/multitenant/usecase03/makefile @@ -141,6 +141,9 @@ SKEY=tls.key SCRT=tls.crt CART=ca.crt COMPANY=oracle +LOCALHOST=localhost +RESTPREFIX=cdb-dev + step1: createimage step2: tagimage @@ -192,14 +195,16 @@ namespace: $(KUBECTL) apply -f $(PDB_NAMESPACE_YAML) $(KUBECTL) get namespaces + tlscert: @echo "CREATING TLS CERTIFICATES" $(OPENSSL) genrsa -out ca.key 2048 - $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(CDB_NAMESPACE)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(CDB_NAMESPACE),DNS:www.example.com" > extfile.txt + $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST)" -out server.csr + $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER),DNS:www.example.com" > extfile.txt $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) + tlssecret: $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDB_NAMESPACE) $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(CDB_NAMESPACE) diff --git a/docs/multitenant/usecase03/ns_namespace_cdb.yaml b/docs/multitenant/usecase03/ns_namespace_cdb.yaml new file mode 100644 index 00000000..f4c6d77b --- /dev/null +++ b/docs/multitenant/usecase03/ns_namespace_cdb.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: cdbnamespace + diff --git a/docs/multitenant/usecase03/ns_namespace_pdb.yaml b/docs/multitenant/usecase03/ns_namespace_pdb.yaml new file mode 100644 index 00000000..b22245f9 --- /dev/null +++ b/docs/multitenant/usecase03/ns_namespace_pdb.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: pdbnamespace + From b23f4869a59e11d2558f67a761519c479d18d513 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Wed, 1 May 2024 16:29:38 +0000 Subject: [PATCH 090/414] Tinglwan merge workload pr --- commons/oci/provider.go | 32 +++++++- .../adb/autonomousdatabase_create.yaml | 7 +- docs/adb/ADB_PREREQUISITES.md | 28 +++++++ go.mod | 75 +++++++++---------- go.sum | 5 +- 5 files changed, 100 insertions(+), 47 deletions(-) diff --git a/commons/oci/provider.go b/commons/oci/provider.go index e875969c..152f1efd 100644 --- a/commons/oci/provider.go +++ b/commons/oci/provider.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2022 Oracle and/or its affiliates. +** Copyright (c) 2022, 2024 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,6 +40,8 @@ package oci import ( "errors" + "fmt" + "os" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/common/auth" @@ -65,7 +67,9 @@ type APIKeyAuth struct { } func GetOCIProvider(kubeClient client.Client, authData APIKeyAuth) (common.ConfigurationProvider, error) { - if authData.ConfigMapName != nil && authData.SecretName != nil { + if authData.ConfigMapName != nil && authData.SecretName == nil { + return getWorkloadIdentityProvider(kubeClient, authData) + } else if authData.ConfigMapName != nil && authData.SecretName != nil { provider, err := getProviderWithAPIKey(kubeClient, authData) if err != nil { return nil, err @@ -80,6 +84,30 @@ func GetOCIProvider(kubeClient client.Client, authData APIKeyAuth) (common.Confi } } +func getWorkloadIdentityProvider(kubeClient client.Client, authData APIKeyAuth) (common.ConfigurationProvider, error) { + ociConfigMap, err := k8s.FetchConfigMap(kubeClient, authData.Namespace, *authData.ConfigMapName) + if err != nil { + return nil, err + } + // Ensure configmap is set with proper data + if len(ociConfigMap.Data) == 0 { + return nil, fmt.Errorf("OCI ConfigMap %s has no data", ociConfigMap.Name) + } + region, ok := ociConfigMap.Data[regionKey] + if !ok || len(region) == 0 { + return nil, fmt.Errorf("OCI Region Key %s missing from OCI ConfigMap %s", regionKey, ociConfigMap.Name) + } + // OCI SDK requires specific, dynamic environment variables for workload identity. + if err = os.Setenv(auth.ResourcePrincipalVersionEnvVar, auth.ResourcePrincipalVersion2_2); err != nil { + + return nil, fmt.Errorf("unable to set OCI SDK environment variable %s: %v", auth.ResourcePrincipalVersionEnvVar, err) + } + if err = os.Setenv(auth.ResourcePrincipalRegionEnvVar, region); err != nil { + return nil, fmt.Errorf("unable to set OCI SDK environment variable %s: %v", auth.ResourcePrincipalRegionEnvVar, err) + } + return auth.OkeWorkloadIdentityConfigurationProvider() +} + func getProviderWithAPIKey(kubeClient client.Client, authData APIKeyAuth) (common.ConfigurationProvider, error) { var region, fingerprint, user, tenancy, passphrase, privatekeyValue string diff --git a/config/samples/adb/autonomousdatabase_create.yaml b/config/samples/adb/autonomousdatabase_create.yaml index 3ecf966f..aa77a94c 100644 --- a/config/samples/adb/autonomousdatabase_create.yaml +++ b/config/samples/adb/autonomousdatabase_create.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2022, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # apiVersion: database.oracle.com/v1alpha1 @@ -30,7 +30,7 @@ spec: # # Uncomment this block to configure the network access type with the RESTRICTED option. # # This option lets you restrict access by defining access control rules in an Access Control List (ACL). - # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. + # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. # # Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs. # accessType: RESTRICTED # accessControlList: @@ -42,7 +42,7 @@ spec: # isMTLSConnectionRequired: true # # Uncomment this block to configure the network access type with the PRIVATE option. - # # This option assigns a private endpoint, private IP, and hostname to your database. + # # This option assigns a private endpoint, private IP, and hostname to your database. # # Specifying this option allows traffic only from the VCN you specify. # # This allows you to define security rules, ingress/egress, at the Network Security Group (NSG) level and to control traffic to your Autonomous Database. # accessType: PRIVATE @@ -61,4 +61,5 @@ spec: # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: configMapName: oci-cred + # Comment out secretName if using OKE workload identity secretName: oci-privatekey \ No newline at end of file diff --git a/docs/adb/ADB_PREREQUISITES.md b/docs/adb/ADB_PREREQUISITES.md index 2ddaaa0e..f8c04c4b 100644 --- a/docs/adb/ADB_PREREQUISITES.md +++ b/docs/adb/ADB_PREREQUISITES.md @@ -120,3 +120,31 @@ To set up the instance principals, you will have to: 3. To apply the policy, click Create. At this stage, the instances where the operator deploys have been granted sufficient permissions to call OCI services. You can now proceed to the installation. + +### Authorized with OKE Workload Identity + +OKE Workload Identity grants the operator pods policy-driven access to OCI resources using OCI Identity and Access Management (IAM). +When using OKE Workload Identity, only the region must be specified in the ConfigMap corresponding to the `ociConfigMap` attribute. The `ociSecret` attribute should not be specified in the `.yaml` file. + +To set up the OKE Workload Identity, you will have to: + +### Configure Cluster Region + +The operator reads the OCI region from a ConfigMap. + +```sh +kubectl create configmap oci-cred \ +--from-literal=region= +``` + +### Define Policies + +1. Get the compartment name where the database resides/will be created. +2. Get the OCID of the OKE Cluster where the Oracle Database Operator is running. +3. Create the following policy in OCI IAM, supplying your compartment name and OKE Cluster OCID: + +``` +Allow any-user to manage all-resources in compartment where all {request.principal.namespace='oracle-database-operator-system',request.principal.type='workload',request.principal.cluster_id='',request.principal.service_account='default'} +``` + +After creating the policy, operator pods will be granted sufficient permissions to call OCI services. You can now proceed to the installation. \ No newline at end of file diff --git a/go.mod b/go.mod index e298856c..1f30279f 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/oracle/oci-go-sdk/v65 v65.49.3 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0 go.uber.org/zap v1.26.0 + golang.org/x/text v0.14.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.29.2 k8s.io/apimachinery v0.29.2 @@ -20,90 +21,86 @@ require ( ) require ( + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/fatih/camelcase v1.0.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fvbommel/sortorder v1.1.0 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/zapr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sony/gobreaker v0.5.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/xlab/treeprint v1.2.0 // indirect + go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect - golang.org/x/text v0.14.0 golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.16.1 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.28.0 // indirect k8s.io/component-base v0.29.2 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect -) - -require ( - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/MakeNowJust/heredoc v1.0.0 // indirect - github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect - github.com/fatih/camelcase v1.0.0 // indirect - github.com/fvbommel/sortorder v1.1.0 // indirect - github.com/go-errors/errors v1.4.2 // indirect - github.com/go-logr/zapr v1.2.4 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect - github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect - github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/spf13/cobra v1.7.0 // indirect - github.com/xlab/treeprint v1.2.0 // indirect - go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/sync v0.5.0 // indirect - golang.org/x/tools v0.16.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/go.sum b/go.sum index 5f306abd..08185f3e 100644 --- a/go.sum +++ b/go.sum @@ -105,8 +105,8 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -321,7 +321,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 8461f68cd99534f28a72f338f42fc5d209b52f4a Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Wed, 1 May 2024 16:38:16 +0000 Subject: [PATCH 091/414] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3d58e56..1fc71146 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Oracle will continue to extend `OraOperator` to support additional Oracle Databa This release of Oracle Database Operator for Kubernetes (the operator) supports the following lifecycle operations: -* ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), on-demand backup, manual restore +* ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore * ACD: provision, bind, restart, terminate (soft/hard) * SIDB: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (a basic observability console), Oracle REST Data Service (ORDS) to support REST based SQL, PDB management, SQL Developer Web, and Application Express (Apex) * SHARDED: Provision/deploy sharded databases and the shard topology, Add a new shard, Delete an existing shard From 6b8438e1ce102dbe623c76abd2d0ba6fdd0049d6 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Wed, 1 May 2024 16:40:22 +0000 Subject: [PATCH 092/414] patch/databaseobserver Docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3d58e56..818a3115 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ This release of Oracle Database Operator for Kubernetes (the operator) supports * Oracle Multitenant Database: Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB * Oracle Base Database Cloud Service (BDBCS): provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License * Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration -* Oracle Database Observability: create, patch, delete +* Oracle Database Observability: create, patch, delete databaseObserver resources * Watch over a set of namespaces or all the namespaces in the cluster using the "WATCH_NAMESPACE" env variable of the operator deployment The upcoming releases will support new configurations, operations and capabilities. From fff2a749dbf3533b2fb9dbcdbc523f20e5180d13 Mon Sep 17 00:00:00 2001 From: kuassi_mensah Date: Thu, 2 May 2024 10:33:16 +0000 Subject: [PATCH 093/414] Update THIRD_PARTY_LICENSES.txt --- THIRD_PARTY_LICENSES.txt | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt index 74a065b6..d75f3946 100644 --- a/THIRD_PARTY_LICENSES.txt +++ b/THIRD_PARTY_LICENSES.txt @@ -1,5 +1,5 @@ ------------------------------------- -Operator SDK 1.26.1 +Operator SDK 1.32.0 https://github.com/operator-framework/operator-sdk Apache 2.0 @@ -208,7 +208,7 @@ Apache License: limitations under the License. ------------------------------ - GO lang + GO lang 1.21.4 https://github.com/golang @@ -241,18 +241,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------- -apimachinery 0.27.2 +apimachinery 0.28.4 https://github.com/kubernetes/apimachinery/tr Apache 2.0 ------------------------- -controller-runtime 0.15.0 -https://github.com/kubernetes-sigs/controller-runtime/tree/v0.14.6 +controller-runtime 0.16.3 +https://github.com/kubernetes-sigs/controller-runtime/releases/tag/v0.16.3 Apache 2.0 ------------------------- -golang 1.20.2 -https://github.com/golang/go/archive/refs/tags/go1.20.2.zip +golang 1.21.4 +https://github.com/golang/go/releases/tag/go1.21.4 BSD 2-clause or 3-clause @@ -1008,14 +1008,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. limitations under the License. -------------------------- -Logr +Logr 1.3.0 https://pkg.go.dev/github.com/go-logr/logr -https://github.com/go-logr/logr/tree/v1.2.4 +https://github.com/go-logr/logr/tree/v1.3.0 Apache 2.0 License ------------------------- -OCI Go SDK -github.com/oracle/oci-go-sdk/v43 +OCI Go SDK 65.53.0 +https://github.com/oracle/oci-go-sdk/releases/tag/v65.53.0 Dual-License: UPL + Apache 2.0 @@ -1065,8 +1065,8 @@ The Universal Permissive License (UPL), Version 1.0 ------------------------- -ginkgo 2.9.7 -https://github.com/onsi/ginkgo/tree/v2.9.7 +ginkgo 2.13.1 +https://github.com/onsi/ginkgo/releases/tag/v2.13.1 MIT ------------------------------------ Gomega @@ -1075,37 +1075,37 @@ MIT License Copyright (c) 2013-2014 Onsi Fakhouri ---------------------------- -gomega 1.27.7 +gomega 1.30.0 http://onsi.github.io/gomega/ MIT ------------------------- -Kubernetes api +Kubernetes api 0.28.4 https://pkg.go.dev/k8s.io/api Apache 2.0 ---------------------------------- -Kubernetes apimachinery +Kubernetes apimachinery 0.28.4 https://pkg.go.dev/k8s.io/apimachinery Apache 2.0 ----------------------------------- -Kubernetes client-go +Kubernetes client-go 0.28.4 https://pkg.go.dev/k8s.io/client-go Apache 2.0 ------------------------------------- -Kubernetes controller-runtime project +Kubernetes controller-runtime project 0.16.3 https://pkg.go.dev/sigs.k8s.io/controller-runtime Apache 2.0 ------------------------------------ -kubernetes-sigs/yaml 1.3.0 +kubernetes-sigs/yaml 1.4.0 https://github.com/kubernetes-sigs/yaml/tree/v1.3.0 MIT ------------------------- -OCI SDK for Go 65.37.1 +OCI SDK for Go 65.53.0 https://github.com/oracle/oci-go-sdk Multiple Licenses: Apache 2.0, UPL @@ -1135,8 +1135,8 @@ https://pkg.go.dev/sigs.k8s.io/yaml Dual license: BSD-3-Clause, MIT ------------------------------------ -zap 1.24.0 -https://github.com/uber-go/zap/tree/v1.24.0 +zap 1.26.0 +https://github.com/uber-go/zap/releases/tag/v1.26.0 MIT ------------------------------------ @@ -1192,7 +1192,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------ -Ginkgo +Ginkgo 2.13.1 github.com/onsi/ginkgo MIT License Copyright (c) 2013-2014 Onsi Fakhouri From 10afd196acee77b89f184fc799eb25dcadc7073e Mon Sep 17 00:00:00 2001 From: kuassi_mensah Date: Thu, 2 May 2024 11:17:17 +0000 Subject: [PATCH 094/414] Updated Readme with Free 23ai and OKE workload Identify --- README.md | 14 ++++++++++++++ image.png | Bin 0 -> 6913 bytes 2 files changed, 14 insertions(+) create mode 100644 image.png diff --git a/README.md b/README.md index 4a76aa35..af97d5bd 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,20 @@ In this v1.1.0 production release, `OraOperator` supports the following database Oracle will continue to extend `OraOperator` to support additional Oracle Database configurations. +## New in this Release +* Namespace scope deployment option +* Support for Oracle Database 23ai Free (with SIDB) +* Automatic Storage Expansion for SIDB and Sharded DB +* User-Defined Sharding +* TCPS support customer provided certs +* Execute custom scripts during DB setup/startup +* Long-term backup for Autonomous Databases (ADB) +* Patching for SIDB Primary/Standby in Data Guard +* Wallet expiry date for ADB +* Condition Metadata for ADB +* OKE workload Identify +* Database Observability (Preview - Metrics) + ## Features Summary This release of Oracle Database Operator for Kubernetes (the operator) supports the following lifecycle operations: diff --git a/image.png b/image.png new file mode 100644 index 0000000000000000000000000000000000000000..8bd1bbd5b33a7353cdc97863c2096670ce5f2f52 GIT binary patch literal 6913 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 Ovyz;uY?ZW0=zjql#Zw>v literal 0 HcmV?d00001 From ced238e33e5202ec9c696599bfe626e8bb3ad8e4 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Thu, 2 May 2024 11:24:41 +0000 Subject: [PATCH 095/414] changing 23c references to 23.3.0 --- docs/sidb/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sidb/README.md b/docs/sidb/README.md index a155bea1..ef4bce9b 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -267,10 +267,10 @@ To provision new Oracle Database Free database, use the sample **[config/samples This command pulls the Free image uploaded on the [Oracle Container Registry](https://container-registry.oracle.com/). **Note:** -- Provisioning Oracle Database Free is supported for release 23c (23.2.0) and later releases. +- Provisioning Oracle Database Free is supported for release 23.3.0 and later releases. - For Free database, only single replica mode (i.e. `replicas: 1`) is supported. - For Free database, you **cannot change** the init parameters i.e. `cpuCount, processes, sgaTarget or pgaAggregateTarget`. -- Oracle Enterprise Manager is not supported from release 23c and later release. +- Oracle Enterprise Manager is not supported from release 23.3.0 and later releases. #### Additional Information You are required to specify the database admin password secret in the corresponding YAML file. The default values mentioned in the `adminPassword.secretName` fields of [singleinstancedatabase_create.yaml](../../config/samples/sidb/singleinstancedatabase_create.yaml), [singleinstancedatabase_prebuiltdb.yaml](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml), [singleinstancedatabase_express.yaml](../../config/samples/sidb/singleinstancedatabase_express.yaml) and [singleinstancedatabse_free.yaml](../../config/samples/sidb/singleinstancedatabase_free.yaml) files are `db-admin-secret`, `prebuiltdb-admin-secret`, `xedb-admin-secret` and `free-admin-secret` respectively. You can create these secrets manually by using the sample command mentioned in the [Template YAML](#template-yaml) section. Alternatively, you can create these secrets by filling the passwords in the **[singleinstancedatabase_secrets.yaml](../../config/samples/sidb/singleinstancedatabase_secrets.yaml)** file and applying it using the command below: @@ -327,7 +327,7 @@ $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.oemExpres https://10.0.25.54:5500/em ``` -**Note:** OEM Express is not available for 23c and later releases +**Note:** OEM Express is not available for 23.3.0 and later releases ### Database Persistence (Storage) Configuration Options The database persistence can be achieved in the following two ways: From 959da6a6d509cf8d066f3970489850f9b1d5142c Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Thu, 2 May 2024 11:31:00 +0000 Subject: [PATCH 096/414] Idesai readme version fix --- docs/sidb/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sidb/README.md b/docs/sidb/README.md index ef4bce9b..81db03b8 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -270,7 +270,7 @@ This command pulls the Free image uploaded on the [Oracle Container Registry](ht - Provisioning Oracle Database Free is supported for release 23.3.0 and later releases. - For Free database, only single replica mode (i.e. `replicas: 1`) is supported. - For Free database, you **cannot change** the init parameters i.e. `cpuCount, processes, sgaTarget or pgaAggregateTarget`. -- Oracle Enterprise Manager is not supported from release 23.3.0 and later releases. +- Oracle Enterprise Manager Express (OEM Express) is not supported from release 23.3.0 and later releases. #### Additional Information You are required to specify the database admin password secret in the corresponding YAML file. The default values mentioned in the `adminPassword.secretName` fields of [singleinstancedatabase_create.yaml](../../config/samples/sidb/singleinstancedatabase_create.yaml), [singleinstancedatabase_prebuiltdb.yaml](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml), [singleinstancedatabase_express.yaml](../../config/samples/sidb/singleinstancedatabase_express.yaml) and [singleinstancedatabse_free.yaml](../../config/samples/sidb/singleinstancedatabase_free.yaml) files are `db-admin-secret`, `prebuiltdb-admin-secret`, `xedb-admin-secret` and `free-admin-secret` respectively. You can create these secrets manually by using the sample command mentioned in the [Template YAML](#template-yaml) section. Alternatively, you can create these secrets by filling the passwords in the **[singleinstancedatabase_secrets.yaml](../../config/samples/sidb/singleinstancedatabase_secrets.yaml)** file and applying it using the command below: From 71ac3d7c3049914b2115f33bef5e13a8eb96b5a0 Mon Sep 17 00:00:00 2001 From: kuassi_mensah Date: Thu, 2 May 2024 13:14:40 +0000 Subject: [PATCH 097/414] Update Readme.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index af97d5bd..cac8ba77 100644 --- a/README.md +++ b/README.md @@ -19,15 +19,15 @@ In this v1.1.0 production release, `OraOperator` supports the following database Oracle will continue to extend `OraOperator` to support additional Oracle Database configurations. -## New in this Release +## New in V1.1.0 Release * Namespace scope deployment option * Support for Oracle Database 23ai Free (with SIDB) * Automatic Storage Expansion for SIDB and Sharded DB * User-Defined Sharding * TCPS support customer provided certs * Execute custom scripts during DB setup/startup -* Long-term backup for Autonomous Databases (ADB) * Patching for SIDB Primary/Standby in Data Guard +* Long-term backup for Autonomous Databases (ADB) * Wallet expiry date for ADB * Condition Metadata for ADB * OKE workload Identify From 6f287d8e8be2e781a08fc8361b9cf89b127728e7 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Thu, 2 May 2024 20:47:53 +0000 Subject: [PATCH 098/414] Tinglwan bug 36349519 --- apis/database/v1alpha1/autonomousdatabase_types.go | 2 +- apis/database/v1alpha1/autonomousdatabase_webhook.go | 11 ++++++----- commons/oci/database.go | 4 ++-- controllers/database/autonomousdatabase_controller.go | 11 +++++++++-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/apis/database/v1alpha1/autonomousdatabase_types.go b/apis/database/v1alpha1/autonomousdatabase_types.go index 620c1d02..cd23b3f3 100644 --- a/apis/database/v1alpha1/autonomousdatabase_types.go +++ b/apis/database/v1alpha1/autonomousdatabase_types.go @@ -335,7 +335,7 @@ func (adb *AutonomousDatabase) UpdateFromOCIADB(ociObj database.AutonomousDataba if *ociObj.IsDedicated { adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate } else { - if ociObj.NsgIds != nil { + if ociObj.NsgIds != nil || ociObj.PrivateEndpoint != nil || ociObj.PrivateEndpointIp != nil || ociObj.PrivateEndpointLabel != nil { adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate } else if ociObj.WhitelistedIps != nil { adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypeRestricted diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go index 7bd84b17..b25e8104 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -240,12 +240,13 @@ func validateNetworkAccess(adb *AutonomousDatabase, allErrs field.ErrorList) fie field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("privateEndpoint").Child("subnetOCID"), fmt.Sprintf("subnetOCID cannot be empty when the network access type is %s", NetworkAccessTypePrivate))) } + } - if adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs == nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("privateEndpoint").Child("nsgOCIDs"), - fmt.Sprintf("nsgOCIDs cannot be empty when the network access type is %s", NetworkAccessTypePrivate))) - } + // NsgOCIDs only applies to PRIVATE accessType + if adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs != nil && adb.Spec.Details.NetworkAccess.AccessType != NetworkAccessTypePrivate { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("privateEndpoint").Child("nsgOCIDs"), + fmt.Sprintf("NsgOCIDs cannot only be applied when network access type is %s.", NetworkAccessTypePrivate))) } // IsAccessControlEnabled is not applicable to a shared database diff --git a/commons/oci/database.go b/commons/oci/database.go index 6196b9f5..9c3cd4d8 100644 --- a/commons/oci/database.go +++ b/commons/oci/database.go @@ -407,8 +407,8 @@ func (d *databaseService) ListAutonomousDatabaseBackups(adbOCID string) (databas func (d *databaseService) CreateAutonomousDatabaseBackup(adbBackup *dbv1alpha1.AutonomousDatabaseBackup, adbOCID string) (database.CreateAutonomousDatabaseBackupResponse, error) { createBackupRequest := database.CreateAutonomousDatabaseBackupRequest{ CreateAutonomousDatabaseBackupDetails: database.CreateAutonomousDatabaseBackupDetails{ - AutonomousDatabaseId: common.String(adbOCID), - IsLongTermBackup: adbBackup.Spec.IsLongTermBackup, + AutonomousDatabaseId: common.String(adbOCID), + IsLongTermBackup: adbBackup.Spec.IsLongTermBackup, RetentionPeriodInDays: adbBackup.Spec.RetentionPeriodInDays, }, } diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 718ee499..bf56bfe0 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -412,7 +412,7 @@ func updateCondition(adb *dbv1alpha1.AutonomousDatabase, err error) { // If error occurs, ReconcileComplete will be marked as true and the error message will still be listed // If the ADB lifecycleState is intermediate, then ReconcileComplete will be marked as false if err != nil { - condition = metav1.Condition { + condition = metav1.Condition{ Type: CONDITION_TYPE_COMPLETE, LastTransitionTime: metav1.Now(), ObservedGeneration: adb.GetGeneration(), @@ -421,7 +421,7 @@ func updateCondition(adb *dbv1alpha1.AutonomousDatabase, err error) { Status: metav1.ConditionTrue, } } else if dbv1alpha1.IsADBIntermediateState(adb.Status.LifecycleState) { - condition = metav1.Condition { + condition = metav1.Condition{ Type: CONDITION_TYPE_COMPLETE, LastTransitionTime: metav1.Now(), ObservedGeneration: adb.GetGeneration(), @@ -1006,6 +1006,7 @@ func (r *AutonomousDatabaseReconciler) validateDesiredLifecycleState( // c. to PRIVATE: // was PUBLIC: set subnetOCID and nsgOCIDs. Configure the IsMTLSConnectionRequired settings if it is set. // was RESTRICTED: set subnetOCID and nsgOCIDs. Configure the IsMTLSConnectionRequired settings if it is set. +// *Note: OCI requires nsgOCIDs to be an empty string rather than nil when we don't want the adb to be included in any network security group. // // Otherwise, if the network access type remains the same, apply the network configuration, and then set the IsMTLSConnectionRequired. // @@ -1220,6 +1221,12 @@ func (r *AutonomousDatabaseReconciler) validateNetworkAccess( l.Info("Sending request to OCI to configure network access options") + // When the network access type is set to PRIVATE, any nil type of nsgOCIDs needs to be set to an empty string, otherwise, OCI SDK returns a 400 error + if difADB.Spec.Details.NetworkAccess.AccessType == dbv1alpha1.NetworkAccessTypePrivate && + difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs == nil { + difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = []string{} + } + resp, err := r.dbService.UpdateNetworkAccess(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) if err != nil { return false, err From e57cfb97955af2fed8f537468a6bae59062663af Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Mon, 6 May 2024 14:44:39 +0000 Subject: [PATCH 099/414] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cac8ba77..ed684a8a 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ Oracle will continue to extend `OraOperator` to support additional Oracle Databa * TCPS support customer provided certs * Execute custom scripts during DB setup/startup * Patching for SIDB Primary/Standby in Data Guard -* Long-term backup for Autonomous Databases (ADB) -* Wallet expiry date for ADB -* Condition Metadata for ADB -* OKE workload Identify +* Long-term backup for Autonomous Databases (ADB): Moves to long-term backup and removes the deprecated mandatory backup +* Wallet expiry date for ADB: A user-freindly enhancement to display wallet expiry date in the status of assiciated ADB +* Wait-for-Completion option for ADB: Supports `kubectl wait` command that allows user to wait a specific condition on ADB +* OKE workload Identify: Supports OKE workload indentity authentication method. For more details, refer to [Oracle Autonomous Database (ADB) Prerequisites](docs/adb/ADB_PREREQUISITES.md#authorized-with-oke-workload-identity) * Database Observability (Preview - Metrics) ## Features Summary From 481563626a8211586b50c632faeb4e01245e715c Mon Sep 17 00:00:00 2001 From: aberinnj Date: Tue, 7 May 2024 17:00:02 -0500 Subject: [PATCH 100/414] Added fix for bug --- commons/observability/constants.go | 1 + controllers/observability/databaseobserver_resource.go | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/commons/observability/constants.go b/commons/observability/constants.go index a4d85b71..89ecb946 100644 --- a/commons/observability/constants.go +++ b/commons/observability/constants.go @@ -38,6 +38,7 @@ const ( DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:1.1.0" DefaultServicePort = 9161 + DefaultServiceTargetPort = 9161 DefaultPrometheusPort = "metrics" DefaultReplicaCount = 1 DefaultExporterConfigMountRootPath = "/oracle/observability" diff --git a/controllers/observability/databaseobserver_resource.go b/controllers/observability/databaseobserver_resource.go index 75e05330..8c20ebe5 100644 --- a/controllers/observability/databaseobserver_resource.go +++ b/controllers/observability/databaseobserver_resource.go @@ -10,6 +10,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -100,8 +101,9 @@ func (resource *ObservabilityServiceResource) generate(api *apiv1.DatabaseObserv Selector: rSelector, Ports: []corev1.ServicePort{ { - Name: "metrics", - Port: rPort, + Name: "metrics", + Port: rPort, + TargetPort: intstr.FromInt32(constants.DefaultServiceTargetPort), }, }, }, From 732fb0e2fcbd11449a5f397521db598e866b1f27 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Thu, 16 May 2024 13:46:45 +0000 Subject: [PATCH 101/414] use 23.4.0-8 --- docs/multitenant/usecase01/makefile | 14 +- docs/multitenant/usecase03/Dockerfile | 63 +++++--- docs/multitenant/usecase03/makefile | 18 +-- docs/multitenant/usecase03/runOrdsSSL.sh | 190 +++++++++++++++++++++++ ords/Dockerfile | 61 ++++++-- ords/runOrdsSSL.sh | 121 ++++++--------- 6 files changed, 336 insertions(+), 131 deletions(-) create mode 100644 docs/multitenant/usecase03/runOrdsSSL.sh diff --git a/docs/multitenant/usecase01/makefile b/docs/multitenant/usecase01/makefile index e468ef62..ae82778d 100644 --- a/docs/multitenant/usecase01/makefile +++ b/docs/multitenant/usecase01/makefile @@ -177,19 +177,7 @@ checkstep9: checkcdb createimage: $(CP) $(DOCKERFILE) . $(CP) $(RUNSCRIPT) . - @echo "BUILDING CDB IMAGES" - @if [[ ! -f ./Dockerfile ]]; \ - then\ - echo "DOCKERFILE DOES NOT EXISTS";\ - exit 1; \ - fi; - @if [[ ! -f ./runOrdsSSL.sh ]]; \ - then\ - echo "DOCKERFILE DOES NOT EXISTS";\ - exit 1; \ - fi; $(DOCKER) build -t $(IMAGE) . - $(RM) ./Dockerfile ./runOrdsSSL.sh tagimage: @echo "TAG IMAGE" @@ -219,7 +207,7 @@ tlscert: $(OPENSSL) genrsa -out ca.key 2048 $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER) /CN=$(LOCALHOST) Root CA " -out ca.crt $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER) /CN=$(LOCALHOST)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER),DNS:www.example.com" > extfile.txt + $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER),DNS:www.example.com" > extfile.txt $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) tlssecret: diff --git a/docs/multitenant/usecase03/Dockerfile b/docs/multitenant/usecase03/Dockerfile index 5c27f11b..772a7e6d 100644 --- a/docs/multitenant/usecase03/Dockerfile +++ b/docs/multitenant/usecase03/Dockerfile @@ -1,34 +1,63 @@ -#LICENSE UPL 1.0 -# -# Copyright (c) 1982-2017 Oracle and/or its affiliates. All rights reserved. -# -# ORACLE DOCKERFILES PROJECT -# -------------------------- -# This is the Dockerfile for Oracle Rest Data Services 22.2 -# +## Copyright (c) 2022 Oracle and/or its affiliates. +## +## The Universal Permissive License (UPL), Version 1.0 +## +## Subject to the condition set forth below, permission is hereby granted to any +## person obtaining a copy of this software, associated documentation and/or data +## (collectively the "Software"), free of charge and under any and all copyright +## rights in the Software, and any and all patent rights owned or freely +## licensable by each licensor hereunder covering either (i) the unmodified +## Software as contributed to or provided by such licensor, or (ii) the Larger +## Works (as defined below), to deal in both +## +## (a) the Software, and +## (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +## one is included with the Software (each a "Larger Work" to which the Software +## is contributed by such licensors), +## +## without restriction, including without limitation the rights to copy, create +## derivative works of, display, perform, and distribute the Software and make, +## use, sell, offer for sale, import, export, have made, and have sold the +## Software and the Larger Work(s), and to sublicense the foregoing rights on +## either these or other terms. +## +## This license is subject to the following condition: +## The above copyright notice and either this complete permission notice or at +## a minimum a reference to the UPL must be included in all copies or +## substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +## SOFTWARE. + FROM container-registry.oracle.com/java/jdk:latest # Environment variables required for this build (do NOT change) # ------------------------------------------------------------- ENV ORDS_HOME=/opt/oracle/ords/ \ - RUN_FILE="runOrdsSSL.sh" - -#RUN_FILE_NOSSL="runOrdsNOSSL.sh" + RUN_FILE="runOrdsSSL.sh" \ + ORDSVERSION=23.4.0-8 # Copy binaries # ------------- COPY $RUN_FILE $ORDS_HOME -#COPY $RUN_FILE_NOSSL $ORDS_HOME -RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps && \ +RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && \ yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && \ yum -y install java-11-openjdk-devel && \ - yum -y install ords && \ yum -y install iproute && \ yum clean all +RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm + +RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm + # Setup filesystem and oracle user -# ------------------------------------------------------------ +# -------------------------------- RUN mkdir -p $ORDS_HOME/doc_root && \ mkdir -p $ORDS_HOME/error && \ mkdir -p $ORDS_HOME/secrets && \ @@ -43,11 +72,9 @@ RUN mkdir -p $ORDS_HOME/doc_root && \ USER oracle WORKDIR /home/oracle -#VOLUME ["$ORDS_HOME/config/ords"] +VOLUME ["$ORDS_HOME/config/ords"] EXPOSE 8888 # Define default command to start Ords Services CMD $ORDS_HOME/$RUN_FILE -## ONLY FOR DEVELOPMENT STAGE -#CMD ["/usr/sbin/init"] diff --git a/docs/multitenant/usecase03/makefile b/docs/multitenant/usecase03/makefile index fc95cfa0..7270a5e0 100644 --- a/docs/multitenant/usecase03/makefile +++ b/docs/multitenant/usecase03/makefile @@ -123,8 +123,10 @@ URLPATH=/_/db-api/stable/database/pdbs/ OPENSSL=/usr/bin/openssl ORDSPORT=8888 MAKE=/usr/bin/make -DOCKERFILE=Dockerfile +DOCKERFILE=../../../ords/Dockerfile +RUNSCRIPT=../../../ords/runOrdsSSL.sh RM=/usr/bin/rm +CP=/bin/cp ECHO=/usr/bin/echo CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml CDB_SECRET_YAML=cdb_secret.yaml @@ -161,16 +163,8 @@ checkstep9: checkcdb createimage: @echo "BUILDING CDB IMAGES" - @if [[ ! -f ./Dockerfile ]]; \ - then\ - echo "DOCKERFILE DOES NOT EXISTS";\ - exit 1; \ - fi; - @if [[ ! -f ../runOrdsSSL.sh ]]; \ - then\ - echo "DOCKERFILE DOES NOT EXISTS";\ - exit 1; \ - fi; + $(CP) $(DOCKERFILE) . + $(CP) $(RUNSCRIPT) . $(DOCKER) build -t $(IMAGE) . tagimage: @@ -201,7 +195,7 @@ tlscert: $(OPENSSL) genrsa -out ca.key 2048 $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER),DNS:www.example.com" > extfile.txt + $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE),DNS:www.example.com" > extfile.txt $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) diff --git a/docs/multitenant/usecase03/runOrdsSSL.sh b/docs/multitenant/usecase03/runOrdsSSL.sh new file mode 100644 index 00000000..35f1b77b --- /dev/null +++ b/docs/multitenant/usecase03/runOrdsSSL.sh @@ -0,0 +1,190 @@ +#!/bin/bash + +cat <$TNSNAME + + +function SetParameter() { + ##ords config info <--- Use this command to get the list + +[[ ! -z "${ORACLE_HOST}" && -z "${DBTNSURL}" ]] && { + $ORDS --config ${CONFIG} config set db.hostname ${ORACLE_HOST:-racnode1} + $ORDS --config ${CONFIG} config set db.port ${ORACLE_PORT:-1521} + $ORDS --config ${CONFIG} config set db.servicename ${ORACLE_SERVICE:-TESTORDS} +} + +[[ -z "${ORACLE_HOST}" && ! -z "${DBTNSURL}" ]] && { + #$ORDS --config ${CONFIG} config set db.tnsAliasName ${TNSALIAS} + #$ORDS --config ${CONFIG} config set db.tnsDirectory ${TNS_ADMIN} + #$ORDS --config ${CONFIG} config set db.connectionType tns + + $ORDS --config ${CONFIG} config set db.connectionType customurl + $ORDS --config ${CONFIG} config set db.customURL jdbc:oracle:thin:@${DBTNSURL} +} + + $ORDS --config ${CONFIG} config set security.requestValidationFunction false + $ORDS --config ${CONFIG} config set jdbc.MaxLimit 100 + $ORDS --config ${CONFIG} config set jdbc.InitialLimit 50 + $ORDS --config ${CONFIG} config set error.externalPath ${ERRORFOLDER} + $ORDS --config ${CONFIG} config set standalone.access.log /home/oracle + $ORDS --config ${CONFIG} config set standalone.https.port 8888 + $ORDS --config ${CONFIG} config set standalone.https.cert ${CERTIFICATE} + $ORDS --config ${CONFIG} config set standalone.https.cert.key ${KEY} + $ORDS --config ${CONFIG} config set restEnabledSql.active true + $ORDS --config ${CONFIG} config set security.verifySSL true + $ORDS --config ${CONFIG} config set database.api.enabled true + $ORDS --config ${CONFIG} config set plsql.gateway.mode disabled + $ORDS --config ${CONFIG} config set database.api.management.services.disabled false + $ORDS --config ${CONFIG} config set misc.pagination.maxRows 1000 + $ORDS --config ${CONFIG} config set db.cdb.adminUser "${CDBADMIN_USER:-C##DBAPI_CDB_ADMIN} AS SYSDBA" + $ORDS --config ${CONFIG} config secret --password-stdin db.cdb.adminUser.password << EOF +${CDBADMIN_PWD:-PROVIDE_A_PASSWORD} +EOF + +$ORDS --config ${CONFIG} config user add --password-stdin ${WEBSERVER_USER:-ordspdbadmin} "SQL Administrator, System Administrator" <${CKF} 2>&1 +echo "checkfile" >> ${CKF} +NOT_INSTALLED=`cat ${CKF} | grep "INFO: The" |wc -l ` +echo NOT_INSTALLED=$NOT_INSTALLED + + +function StartUp () { + $ORDS --config $CONFIG serve --port 8888 --secure +} + +# Check whether ords is already setup +if [ $NOT_INSTALLED -ne 0 ] +then + echo " SETUP " + setupOrds; + StartUp; +fi + +if [ $NOT_INSTALLED -eq 0 ] +then + echo " STARTUP " + StartUp; +fi + + diff --git a/ords/Dockerfile b/ords/Dockerfile index d4e16b6c..772a7e6d 100644 --- a/ords/Dockerfile +++ b/ords/Dockerfile @@ -1,34 +1,63 @@ -#LICENSE UPL 1.0 -# -# Copyright (c) 1982-2017 Oracle and/or its affiliates. All rights reserved. -# -# ORACLE DOCKERFILES PROJECT -# -------------------------- -# This is the Dockerfile for Oracle Rest Data Services 22.2 -# +## Copyright (c) 2022 Oracle and/or its affiliates. +## +## The Universal Permissive License (UPL), Version 1.0 +## +## Subject to the condition set forth below, permission is hereby granted to any +## person obtaining a copy of this software, associated documentation and/or data +## (collectively the "Software"), free of charge and under any and all copyright +## rights in the Software, and any and all patent rights owned or freely +## licensable by each licensor hereunder covering either (i) the unmodified +## Software as contributed to or provided by such licensor, or (ii) the Larger +## Works (as defined below), to deal in both +## +## (a) the Software, and +## (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +## one is included with the Software (each a "Larger Work" to which the Software +## is contributed by such licensors), +## +## without restriction, including without limitation the rights to copy, create +## derivative works of, display, perform, and distribute the Software and make, +## use, sell, offer for sale, import, export, have made, and have sold the +## Software and the Larger Work(s), and to sublicense the foregoing rights on +## either these or other terms. +## +## This license is subject to the following condition: +## The above copyright notice and either this complete permission notice or at +## a minimum a reference to the UPL must be included in all copies or +## substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +## SOFTWARE. + FROM container-registry.oracle.com/java/jdk:latest # Environment variables required for this build (do NOT change) # ------------------------------------------------------------- ENV ORDS_HOME=/opt/oracle/ords/ \ - RUN_FILE="runOrdsSSL.sh" - -#RUN_FILE_NOSSL="runOrdsNOSSL.sh" + RUN_FILE="runOrdsSSL.sh" \ + ORDSVERSION=23.4.0-8 # Copy binaries # ------------- COPY $RUN_FILE $ORDS_HOME -#COPY $RUN_FILE_NOSSL $ORDS_HOME -RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps && \ +RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && \ yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && \ yum -y install java-11-openjdk-devel && \ - yum -y install ords && \ yum -y install iproute && \ yum clean all +RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm + +RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm + # Setup filesystem and oracle user -# ------------------------------------------------------------ +# -------------------------------- RUN mkdir -p $ORDS_HOME/doc_root && \ mkdir -p $ORDS_HOME/error && \ mkdir -p $ORDS_HOME/secrets && \ @@ -49,5 +78,3 @@ EXPOSE 8888 # Define default command to start Ords Services CMD $ORDS_HOME/$RUN_FILE -## ONLY FOR DEVELOPMENT STAGE -#CMD ["/usr/sbin/init"] diff --git a/ords/runOrdsSSL.sh b/ords/runOrdsSSL.sh index 23b99f1e..35f1b77b 100644 --- a/ords/runOrdsSSL.sh +++ b/ords/runOrdsSSL.sh @@ -1,16 +1,44 @@ #!/bin/bash -# -# Since: June, 2022 -# Author: matteo.malvezzi@oracle.com -# Description: Setup and runs Oracle Rest Data Services 22.2. -# -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. -# -# Copyright (c) 2014-2017 Oracle and/or its affiliates. All rights reserved. -# -# MODIFIED (DD-Mon-YY) -# mmalvezz 25-Jun-22 - Initial version -# mmalvezz 17-Oct-22 - db.customURL utilization + +cat <$TNSNAME - - function SetParameter() { ##ords config info <--- Use this command to get the list @@ -67,65 +91,16 @@ function SetParameter() { $ORDS --config ${CONFIG} config set misc.pagination.maxRows 1000 $ORDS --config ${CONFIG} config set db.cdb.adminUser "${CDBADMIN_USER:-C##DBAPI_CDB_ADMIN} AS SYSDBA" $ORDS --config ${CONFIG} config secret --password-stdin db.cdb.adminUser.password << EOF -${CDBADMIN_PWD:-WElcome_12##} +${CDBADMIN_PWD:-PROVIDE_A_PASSWORD} EOF -## $ORDS --config ${CONFIG} config set db.username "SYS AS SYSDBA" -## $ORDS --config ${CONFIG} config secret --password-stdin db.password <$PASSFILE -welcome1 -EOF - -## $JAVA_HOME/bin/keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks \ -## -dname "CN=${HN}, OU=Example Department, O=Example Company, L=Birmingham, ST=West Midlands, C=GB" \ -## -storepass welcome1 -validity 3600 -keysize 2048 -keypass welcome1 -## -## -## $JAVA_HOME/bin/keytool -importkeystore -srckeystore keystore.jks -srcalias selfsigned -srcstorepass welcome1 \ -## -destkeystore keystore.p12 -deststoretype PKCS12 -deststorepass welcome1 -destkeypass welcome1 -## -## -## ${OPENSSL} pkcs12 -in ${KEYSTORE}/keystore.p12 -nodes -nocerts -out ${KEYSTORE}/${HN}-key.pem -passin file:${PASSFILE} -## ${OPENSSL} pkcs12 -in ${KEYSTORE}/keystore.p12 -nokeys -out ${KEYSTORE}/${HN}.pem -passin file:${PASSFILE} -## ${OPENSSL} pkcs8 -topk8 -inform PEM -outform DER -in ${HN}-key.pem -out ${HN}-key.der -nocrypt -## ${OPENSSL} x509 -inform PEM -outform DER -in ${HN}.pem -out ${HN}.der - - - - - - - - -rm $PASSFILE -ls -ltr $KEYSTORE - - - -} - - function setupOrds() { echo "====================================================" @@ -163,7 +138,6 @@ export ORDS_LOGS=/tmp ORDS_PASSWORD=`cat $ORDS_HOME/secrets/$ORDS_PWD_KEY` } -setupHTTPS; SetParameter; $ORDS --config ${CONFIG} install \ @@ -173,8 +147,8 @@ $ORDS --config ${CONFIG} install \ --log-folder ${ORDS_LOGS} \ --proxy-user \ --password-stdin <${CKF} 2>&1 +echo "checkfile" >> ${CKF} +NOT_INSTALLED=`cat ${CKF} | grep "INFO: The" |wc -l ` echo NOT_INSTALLED=$NOT_INSTALLED + function StartUp () { $ORDS --config $CONFIG serve --port 8888 --secure } From 769809f9f97bdeb87cbddd2d60e96bfdfdcbed50 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Wed, 29 May 2024 20:18:56 +0000 Subject: [PATCH 102/414] update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed684a8a..c4b17b7d 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Oracle will continue to extend `OraOperator` to support additional Oracle Databa * TCPS support customer provided certs * Execute custom scripts during DB setup/startup * Patching for SIDB Primary/Standby in Data Guard -* Long-term backup for Autonomous Databases (ADB): Moves to long-term backup and removes the deprecated mandatory backup +* Long-term backup for Autonomous Databases (ADB): Support for [long-term retention backup](https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/backup-long-term.html) and removed support for the deprecated mandatory backup * Wallet expiry date for ADB: A user-freindly enhancement to display wallet expiry date in the status of assiciated ADB * Wait-for-Completion option for ADB: Supports `kubectl wait` command that allows user to wait a specific condition on ADB * OKE workload Identify: Supports OKE workload indentity authentication method. For more details, refer to [Oracle Autonomous Database (ADB) Prerequisites](docs/adb/ADB_PREREQUISITES.md#authorized-with-oke-workload-identity) From e9e76677cdfafd4a18410c2117a4034867ec963d Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Thu, 4 Jul 2024 06:20:51 +0000 Subject: [PATCH 103/414] Documentation Changes to include openshift pre req --- config/samples/sidb/openshift_rbac.yaml | 72 ++++++++++++------- .../samples/sidb/singleinstancedatabase.yaml | 4 +- docs/sidb/README.md | 33 +++++++-- 3 files changed, 77 insertions(+), 32 deletions(-) diff --git a/config/samples/sidb/openshift_rbac.yaml b/config/samples/sidb/openshift_rbac.yaml index 8c88f78e..2e0d0cb2 100644 --- a/config/samples/sidb/openshift_rbac.yaml +++ b/config/samples/sidb/openshift_rbac.yaml @@ -1,73 +1,93 @@ # -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # - --- # Create a Security Context Contraint kind: SecurityContextConstraints - apiVersion: v1 + apiVersion: security.openshift.io/v1 + metadata: + name: sidb-oracle-user-scc + allowPrivilegedContainer: false + allowedCapabilities: + - SYS_NICE + runAsUser: + type: MustRunAs + uid: 54321 + seLinuxContext: + type: RunAsAny + fsGroup: + type: MustRunAs + ranges: + - min: 54321 + max: 54321 + supplementalGroups: + type: MustRunAs + ranges: + - min: 54321 + max: 54321 +--- +# Create a Security Context Contraint + + kind: SecurityContextConstraints + apiVersion: security.openshift.io/v1 metadata: - name: sidb-scc - namespace: default + name: sidb-oracle-root-user-scc allowPrivilegedContainer: false - users: - - system:serviceaccount:default:sidb-sa - - system:serviceaccount:default:oracle-database-operator + allowedCapabilities: + - SYS_NICE runAsUser: type: MustRunAsRange uidRangeMin: 0 - uidRangeMax: 60000 + uidRangeMax: 54321 seLinuxContext: type: RunAsAny fsGroup: type: MustRunAs ranges: - min: 0 - max: 60000 + max: 54321 supplementalGroups: type: MustRunAs ranges: - min: 0 - max: 60000 - + max: 54321 --- -# Create Service Account - apiVersion: v1 kind: ServiceAccount metadata: name: sidb-sa - namespace: default + namespace: sidb-ns --- -# Create a rbac role - kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: use-sidb-scc - namespace: default + namespace: sidb-ns rules: - - apiGroups: ["security.openshift.io"] - resources: ["securitycontextconstraints"] - resourceNames: ["sidb-scc"] - verbs: ["use"] + - verbs: + - use + apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - oracle-user-scc + - oracle-root-scc --- -# Create a rbac role binding - kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: use-sidb-scc - namespace: default + namespace: sidb-ns subjects: - kind: ServiceAccount name: sidb-sa + namespace: sidb-ns roleRef: kind: Role name: use-sidb-scc apiGroup: rbac.authorization.k8s.io - \ No newline at end of file diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 3fcf7d97..746b09d0 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -64,9 +64,9 @@ spec: tcpsCertRenewInterval: 8760h ## N/A for createAs clone or standby - ## Specify both sgaSize and pgaSize (in MB) or dont specify both ## Specify Non-Zero value to use - ## You cannot change these initParams for Oracle Database Express (XE) edition + ## sgaTarget and pagAggregateTarget must be in MB + ## You cannot change these initParams for Oracle Database Express (XE) and Oracle Database Free edition initParams: cpuCount: 0 processes: 0 diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 81db03b8..895c26a9 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -5,6 +5,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [Prerequisites](#prerequisites) * [Mandatory Resource Privileges](#mandatory-resource-privileges) * [Optional Resource Privileges](#optional-resource-privileges) + * [OpenShift Security Context Constraints](#openshift-security-context-constraints) * [SingleInstanceDatabase Resource](#singleinstancedatabase-resource) * [Create a Database](#create-a-database) * [New Database](#new-database) @@ -47,6 +48,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [Maintenance Operations](#maintenance-operations) * [Additional Information](#additional-information) + ## Prerequisites Oracle strongly recommends to comply with the [prerequisites](./PREREQUISITES.md) and the following requirements @@ -89,7 +91,30 @@ Oracle strongly recommends to comply with the [prerequisites](./PREREQUISITES.md ```sh kubectl apply -f rbac/persistent-volume-rbac.yaml ``` + + ### OpenShift Security Context Constraints + + OpenShift requires additional Security Context Constraints (SCC) for deploying and managing the SingleInstanceDatabase resource. Follow these steps to create the appropriate SCCs before deploying the SingleInstanceDatabase resource. + + 1. Create a new project/namespace for deploying the SingleInstanceDatabase resource + + ```sh + oc new-project sidb-ns + ``` + + **Note:** OpenShift recommends not to deploy in namespaces starting with `kube`, `openshift` and the `default` namespace. + + 2. Apply the file [openshift_rbac.yaml](../../config/samples/sidb/openshift_rbac.yaml) with cluster-admin user privileges. + + ```sh + oc apply -f openshift-rbac.yaml + ``` + + This would result in creation of SCC (Security Context Constraints) and serviceaccount `sidb-sa` in the namespace `sidb-ns` which has access to the SCC. + + **Note:** The above config yaml file will bind the SCC to the serviceaccount `sidb-sa` in namespace `sidb-ns`. For any other project/namespace update the file appropriately with the namespace before applying. + 3. Set the `serviceAccountName` attribute to `sidb-sa` and the namespace to `sidb-ns` in **[config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)** before deploying the SingleInstanceDatabase resource. ## SingleInstanceDatabase Resource @@ -961,12 +986,12 @@ $ kubectl describe oraclerestdataservice ords-sample ### Template YAML -The template `.yaml` file for Oracle Rest Data Services (`OracleRestDataService` kind), including all the configurable options, is available at **[config/samples/sidb/oraclerestdataservice.yaml](config/samples/sidb/oraclerestdataservice.yaml)**. +The template `.yaml` file for Oracle Rest Data Services (`OracleRestDataService` kind), including all the configurable options, is available at **[config/samples/sidb/oraclerestdataservice.yaml](../../config/samples/sidb/oraclerestdataservice.yaml)**. **Note:** - The `adminPassword` and `ordsPassword` fields in the `oraclerestdataservice.yaml` file contains secrets for authenticating the Single Instance Database and the ORDS user with the following roles: `SQL Administrator, System Administrator, SQL Developer, oracle.dbtools.autorest.any.schema`. - To build the ORDS image, use the following instructions: [Building Oracle REST Data Services Install Images](https://github.com/oracle/docker-images/tree/main/OracleRestDataServices#building-oracle-rest-data-services-install-images). -- By default, ORDS uses self-signed certificates. To use certificates from the Certificate Authority, the ORDS image needs to be rebuilt after specifying the values of `ssl.cert` and `ssl.cert.key` in the [standalone.properties](https://github.com/oracle/docker-images/blob/main/OracleRestDataServices/dockerfiles/standalone.properties.tmpl) file. After you rebuild the ORDS image, use the rebuilt image in the **[config/samples/sidb/oraclerestdataservice.yaml](config/samples/sidb/oraclerestdataservice.yaml)** file. +- By default, ORDS uses self-signed certificates. To use certificates from the Certificate Authority, the ORDS image needs to be rebuilt after specifying the values of `ssl.cert` and `ssl.cert.key` in the [standalone.properties](https://github.com/oracle/docker-images/blob/main/OracleRestDataServices/dockerfiles/standalone.properties.tmpl) file. After you rebuild the ORDS image, use the rebuilt image in the **[config/samples/sidb/oraclerestdataservice.yaml](../../config/samples/sidb/oraclerestdataservice.yaml)** file. - If you want to install ORDS in a [prebuilt database](#provision-a-pre-built-database), make sure to attach the **database persistence** by uncommenting the `persistence` section in the **[config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml)** file, while provisioning the prebuilt database. ### REST Enable a Database @@ -1114,7 +1139,7 @@ Fetch all entries from 'DEPT' table by calling the following API Database Actions is a web-based interface that uses Oracle REST Data Services to provide development, data tools, administration and monitoring features for Oracle Database. * To use Database Actions, you must sign in as a database user whose schema has been REST-enabled. -* To enable a schema for REST, you can specify appropriate values for the `.spec.restEnableSchemas` attributes details in the sample `yaml` **[config/samples/sidb/oraclerestdataservice.yaml](config/samples/sidb/oraclerestdataservice.yaml)**, which are needed for authorizing Database Actions. +* To enable a schema for REST, you can specify appropriate values for the `.spec.restEnableSchemas` attributes details in the sample `yaml` **[config/samples/sidb/oraclerestdataservice.yaml](../../config/samples/sidb/oraclerestdataservice.yaml)**, which are needed for authorizing Database Actions. * Schema are created (if they exist) with the username as `.spec.restEnableSchema[].schema` and password as `.spec.ordsPassword.`. * UrlMapping `.spec.restEnableSchema[].urlMapping` is optional and is defaulted to `.spec.restEnableSchema[].schema`. @@ -1148,7 +1173,7 @@ Using APEX, developers can quickly develop and deploy compelling apps that solve The `OraOperator` facilitates installation of APEX in the database and also configures ORDS for it. The following section will explain installing APEX with configured ORDS: -* For quick provisioning, use the sample **[config/samples/sidb/oraclerestdataservice_apex.yaml](../../confi/samples/sidb/oraclerestdataservice_apex.yaml)** file. For example: +* For quick provisioning, use the sample **[config/samples/sidb/oraclerestdataservice_apex.yaml](../../config/samples/sidb/oraclerestdataservice_apex.yaml)** file. For example: kubectl apply -f oraclerestdataservice_apex.yaml From e953473c4594a09d4d0bbe997901f5f4da550ff2 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Mon, 8 Jul 2024 15:22:19 +0000 Subject: [PATCH 104/414] Documentation enhancements --- config/samples/sidb/openshift_rbac.yaml | 143 +++++++++--------- .../samples/sidb/singleinstancedatabase.yaml | 2 +- 2 files changed, 73 insertions(+), 72 deletions(-) diff --git a/config/samples/sidb/openshift_rbac.yaml b/config/samples/sidb/openshift_rbac.yaml index 2e0d0cb2..6dddb80d 100644 --- a/config/samples/sidb/openshift_rbac.yaml +++ b/config/samples/sidb/openshift_rbac.yaml @@ -3,91 +3,92 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -# Create a Security Context Contraint - kind: SecurityContextConstraints - apiVersion: security.openshift.io/v1 - metadata: - name: sidb-oracle-user-scc - allowPrivilegedContainer: false - allowedCapabilities: - - SYS_NICE - runAsUser: - type: MustRunAs - uid: 54321 - seLinuxContext: - type: RunAsAny - fsGroup: - type: MustRunAs - ranges: - - min: 54321 - max: 54321 - supplementalGroups: +# Create a Security Context Contraint +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + name: sidb-oracle-user-scc +allowPrivilegedContainer: false +allowedCapabilities: + - SYS_NICE +runAsUser: + type: MustRunAs + uid: 54321 +seLinuxContext: + type: RunAsAny +fsGroup: + type: MustRunAs + ranges: + - min: 54321 + max: 54321 +supplementalGroups: type: MustRunAs ranges: - min: 54321 max: 54321 --- -# Create a Security Context Contraint - kind: SecurityContextConstraints - apiVersion: security.openshift.io/v1 - metadata: - name: sidb-oracle-root-user-scc - allowPrivilegedContainer: false - allowedCapabilities: - - SYS_NICE - runAsUser: - type: MustRunAsRange - uidRangeMin: 0 - uidRangeMax: 54321 - seLinuxContext: - type: RunAsAny - fsGroup: - type: MustRunAs - ranges: - - min: 0 - max: 54321 - supplementalGroups: - type: MustRunAs - ranges: - - min: 0 - max: 54321 +# Create a Security Context Contraint +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + name: sidb-oracle-root-user-scc +allowPrivilegedContainer: false +allowedCapabilities: + - SYS_NICE +runAsUser: + type: MustRunAsRange + uidRangeMin: 0 + uidRangeMax: 54321 +seLinuxContext: + type: RunAsAny +fsGroup: + type: MustRunAs + ranges: + - min: 0 + max: 54321 +supplementalGroups: + type: MustRunAs + ranges: + - min: 0 + max: 5432 --- + apiVersion: v1 kind: ServiceAccount metadata: name: sidb-sa namespace: sidb-ns - --- - kind: Role - apiVersion: rbac.authorization.k8s.io/v1 - metadata: - name: use-sidb-scc - namespace: sidb-ns - rules: - - verbs: - - use - apiGroups: - - security.openshift.io + +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: use-sidb-scc + namespace: sidb-ns +rules: + - apiGroups: + - security.openshift.io + verbs: + - use resources: - - securitycontextconstraints + - securitycontextconstraints resourceNames: - - oracle-user-scc - - oracle-root-scc - + - sidb-oracle-user-scc + - sidb-oracle-root-user-scc --- - kind: RoleBinding - apiVersion: rbac.authorization.k8s.io/v1 - metadata: - name: use-sidb-scc - namespace: sidb-ns - subjects: - - kind: ServiceAccount - name: sidb-sa - namespace: sidb-ns - roleRef: - kind: Role - name: use-sidb-scc - apiGroup: rbac.authorization.k8s.io + +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: use-sidb-scc + namespace: sidb-ns +subjects: + - kind: ServiceAccount + name: sidb-sa + namespace: sidb-ns +roleRef: + kind: Role + name: use-sidb-scc + apiGroup: rbac.authorization.k8s.io diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 746b09d0..b66082e1 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -65,7 +65,7 @@ spec: ## N/A for createAs clone or standby ## Specify Non-Zero value to use - ## sgaTarget and pagAggregateTarget must be in MB + ## sgaTarget and pgaAggregateTarget must be in MB ## You cannot change these initParams for Oracle Database Express (XE) and Oracle Database Free edition initParams: cpuCount: 0 From 4d353dfb9969c2a19a685d7ed899d9581368ea6f Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Mon, 15 Jul 2024 10:22:19 +0000 Subject: [PATCH 105/414] Saurabh basedb --- apis/database/v1alpha1/dbcssystem_types.go | 25 +- .../v1alpha1/pdbconfig_connection_strings.go | 45 ++ apis/database/v1alpha1/pdbconfig_types.go | 72 +++ .../v1alpha1/zz_generated.deepcopy.go | 91 ++++ commons/dbcssystem/dbcs_reconciler.go | 33 +- .../bases/database.oracle.com_DbcsSystem.yaml | 52 ++- .../database.oracle.com_dbcssystems.yaml | 49 ++- controllers/database/dbcssystem_controller.go | 412 +++++++++++++++++- docs/dbcs/README.md | 64 +-- .../bind_to_existing_dbcs_system.md | 8 +- .../bind_to_existing_dbcs_system.yaml | 2 +- .../dbcs/provisioning/create_dbcs_with_pdb.md | 55 +++ docs/dbcs/provisioning/create_pdb.md | 55 +++ ...reatepdb_in_existing_dbcs_system_list.yaml | 28 ++ ...xisting_dbcs_system_list_sample_output.log | 17 + .../provisioning/dbcs_service_with_pdb.yaml | 38 ++ .../dbcs_service_with_pdb_sample_output.log | 103 +++++ docs/dbcs/provisioning/delete_pdb.md | 50 +++ ...eletepdb_in_existing_dbcs_system_list.yaml | 13 + ...xisting_dbcs_system_list_sample_output.log | 8 + oracle-database-operator.yaml | 129 +++++- 21 files changed, 1275 insertions(+), 74 deletions(-) create mode 100644 apis/database/v1alpha1/pdbconfig_connection_strings.go create mode 100644 apis/database/v1alpha1/pdbconfig_types.go create mode 100644 docs/dbcs/provisioning/create_dbcs_with_pdb.md create mode 100644 docs/dbcs/provisioning/create_pdb.md create mode 100644 docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml create mode 100644 docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log create mode 100644 docs/dbcs/provisioning/dbcs_service_with_pdb.yaml create mode 100644 docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log create mode 100644 docs/dbcs/provisioning/delete_pdb.md create mode 100644 docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml create mode 100644 docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log diff --git a/apis/database/v1alpha1/dbcssystem_types.go b/apis/database/v1alpha1/dbcssystem_types.go index 37e80a6b..a2ff3bfc 100644 --- a/apis/database/v1alpha1/dbcssystem_types.go +++ b/apis/database/v1alpha1/dbcssystem_types.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2022 Oracle and/or its affiliates. +** Copyright (c) 2022-2024 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -42,6 +42,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/go-logr/logr" dbcsv1 "github.com/oracle/oracle-database-operator/commons/annotations" "sigs.k8s.io/controller-runtime/pkg/client" @@ -57,6 +58,7 @@ type DbcsSystemSpec struct { OCIConfigMap string `json:"ociConfigMap"` OCISecret string `json:"ociSecret,omitempty"` HardLink bool `json:"hardLink,omitempty"` + PdbConfigs []PDBConfig `json:"pdbConfigs,omitempty"` } // DbSystemDetails Spec @@ -96,7 +98,7 @@ type DbSystemDetails struct { DbBackupConfig Backupconfig `json:"dbBackupConfig,omitempty"` } -// DB Backup COnfig Network Struct +// DB Backup Config Network Struct type Backupconfig struct { AutoBackupEnabled *bool `json:"autoBackupEnabled,omitempty"` RecoveryWindowsInDays *int `json:"recoveryWindowsInDays,omitempty"` @@ -208,6 +210,25 @@ func (dbcs *DbcsSystem) GetLastSuccessfulSpec() (*DbcsSystemSpec, error) { return &sucSpec, nil } +func (dbcs *DbcsSystem) GetLastSuccessfulSpecWithLog(log logr.Logger) (*DbcsSystemSpec, error) { + val, ok := dbcs.GetAnnotations()[lastSuccessfulSpec] + if !ok { + log.Info("No last successful spec annotation found") + return nil, nil + } + + specBytes := []byte(val) + sucSpec := DbcsSystemSpec{} + + err := json.Unmarshal(specBytes, &sucSpec) + if err != nil { + log.Error(err, "Failed to unmarshal last successful spec") + return nil, err + } + + log.Info("Successfully retrieved last successful spec", "spec", sucSpec) + return &sucSpec, nil +} // UpdateLastSuccessfulSpec updates lastSuccessfulSpec with the current spec. func (dbcs *DbcsSystem) UpdateLastSuccessfulSpec(kubeClient client.Client) error { diff --git a/apis/database/v1alpha1/pdbconfig_connection_strings.go b/apis/database/v1alpha1/pdbconfig_connection_strings.go new file mode 100644 index 00000000..9c3092ce --- /dev/null +++ b/apis/database/v1alpha1/pdbconfig_connection_strings.go @@ -0,0 +1,45 @@ +// Copyright (c) 2016, 2018, 2024, Oracle and/or its affiliates. All rights reserved. +// This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license. +// Code generated. DO NOT EDIT. + +// Database Service API +// +// The API for the Database Service. Use this API to manage resources such as databases and DB Systems. For more information, see Overview of the Database Service (https://docs.cloud.oracle.com/iaas/Content/Database/Concepts/databaseoverview.htm). +// + +package v1alpha1 + +import ( + "fmt" + "github.com/oracle/oci-go-sdk/v65/common" + "strings" +) + +// PDBConfigConnectionStrings Connection strings to connect to an Oracle Pluggable Database. +type PDBConfigConnectionStrings struct { + + // A host name-based PDB connection string. + PdbDefault *string `mandatory:"false" json:"pdbDefault"` + + // An IP-based PDB connection string. + PdbIpDefault *string `mandatory:"false" json:"pdbIpDefault"` + + // All connection strings to use to connect to the pluggable database. + AllConnectionStrings map[string]string `mandatory:"false" json:"allConnectionStrings"` +} + +func (m PDBConfigConnectionStrings) String() string { + return common.PointerString(m) +} + +// ValidateEnumValue returns an error when providing an unsupported enum value +// This function is being called during constructing API request process +// Not recommended for calling this function directly +func (m PDBConfigConnectionStrings) ValidateEnumValue() (bool, error) { + errMessage := []string{} + + if len(errMessage) > 0 { + return true, fmt.Errorf(strings.Join(errMessage, "\n")) + } + return false, nil +} \ No newline at end of file diff --git a/apis/database/v1alpha1/pdbconfig_types.go b/apis/database/v1alpha1/pdbconfig_types.go new file mode 100644 index 00000000..b5d7c501 --- /dev/null +++ b/apis/database/v1alpha1/pdbconfig_types.go @@ -0,0 +1,72 @@ +/* +** Copyright (c) 2022-2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ +package v1alpha1 + +// PDBConfig defines details of PDB struct for DBCS systems +type PDBConfig struct { + // The name for the pluggable database (PDB). The name is unique in the context of a Database. The name must begin with an alphabetic character and can contain a maximum of thirty alphanumeric characters. Special characters are not permitted. The pluggable database name should not be same as the container database name. + PdbName *string `mandatory:"true" json:"pdbName"` + + // The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the CDB + // ContainerDatabaseId *string `mandatory:"false" json:"containerDatabaseId"` + + // // A strong password for PDB Admin. The password must be at least nine characters and contain at least two uppercase, two lowercase, two numbers, and two special characters. The special characters must be _, \#, or -. + PdbAdminPassword *string `mandatory:"false" json:"pdbAdminPassword"` + + // // The existing TDE wallet password of the CDB. + TdeWalletPassword *string `mandatory:"false" json:"tdeWalletPassword"` + + // // The locked mode of the pluggable database admin account. If false, the user needs to provide the PDB Admin Password to connect to it. + // // If true, the pluggable database will be locked and user cannot login to it. + ShouldPdbAdminAccountBeLocked *bool `mandatory:"false" json:"shouldPdbAdminAccountBeLocked"` + + // // Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. + // // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). + // // Example: `{"Department": "Finance"}` + FreeformTags map[string]string `mandatory:"false" json:"freeformTags"` + + // // Defined tags for this resource. Each key is predefined and scoped to a namespace. + // // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). + // DefinedTags map[string]map[string]interface{} `mandatory:"false" json:"definedTags"` + + // To specify whether to delete the PDB + IsDelete *bool `json:"isDelete,omitempty"` + + // The OCID of the PDB for deletion purposes. + PluggableDatabaseId *string `mandatory:"false" json:"pluggableDatabaseId"` +} diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index f1eaf503..973083f2 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1187,6 +1187,13 @@ func (in *DbcsSystemSpec) DeepCopyInto(out *DbcsSystemSpec) { *out = new(string) **out = **in } + if in.PdbConfigs != nil { + in, out := &in.PdbConfigs, &out.PdbConfigs + *out = make([]PDBConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemSpec. @@ -1867,6 +1874,90 @@ func (in *PDBAdminPassword) DeepCopy() *PDBAdminPassword { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { + *out = *in + if in.PdbName != nil { + in, out := &in.PdbName, &out.PdbName + *out = new(string) + **out = **in + } + if in.PdbAdminPassword != nil { + in, out := &in.PdbAdminPassword, &out.PdbAdminPassword + *out = new(string) + **out = **in + } + if in.TdeWalletPassword != nil { + in, out := &in.TdeWalletPassword, &out.TdeWalletPassword + *out = new(string) + **out = **in + } + if in.ShouldPdbAdminAccountBeLocked != nil { + in, out := &in.ShouldPdbAdminAccountBeLocked, &out.ShouldPdbAdminAccountBeLocked + *out = new(bool) + **out = **in + } + if in.FreeformTags != nil { + in, out := &in.FreeformTags, &out.FreeformTags + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.IsDelete != nil { + in, out := &in.IsDelete, &out.IsDelete + *out = new(bool) + **out = **in + } + if in.PluggableDatabaseId != nil { + in, out := &in.PluggableDatabaseId, &out.PluggableDatabaseId + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBConfig. +func (in *PDBConfig) DeepCopy() *PDBConfig { + if in == nil { + return nil + } + out := new(PDBConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBConfigConnectionStrings) DeepCopyInto(out *PDBConfigConnectionStrings) { + *out = *in + if in.PdbDefault != nil { + in, out := &in.PdbDefault, &out.PdbDefault + *out = new(string) + **out = **in + } + if in.PdbIpDefault != nil { + in, out := &in.PdbIpDefault, &out.PdbIpDefault + *out = new(string) + **out = **in + } + if in.AllConnectionStrings != nil { + in, out := &in.AllConnectionStrings, &out.AllConnectionStrings + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBConfigConnectionStrings. +func (in *PDBConfigConnectionStrings) DeepCopy() *PDBConfigConnectionStrings { + if in == nil { + return nil + } + out := new(PDBConfigConnectionStrings) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBList) DeepCopyInto(out *PDBList) { *out = *in diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 70e50294..ea1bce37 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -94,7 +94,7 @@ func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient d dbcsDetails.CompartmentId = common.String(dbcs.Spec.DbSystem.CompartmentId) dbcsDetails.SubnetId = common.String(dbcs.Spec.DbSystem.SubnetId) dbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) - dbcsDetails.Domain = common.String(dbcs.Spec.DbSystem.Domain) + dbcsDetails.Domain = common.String(dbcs.Spec.DbSystem.Domain) if dbcs.Spec.DbSystem.DisplayName != "" { dbcsDetails.DisplayName = common.String(dbcs.Spec.DbSystem.DisplayName) } @@ -420,34 +420,51 @@ func GetListDatabaseRsp(logger logr.Logger, dbClient database.DatabaseClient, db func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, kubeClient client.Client, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { //logger := log.WithName("UpdateDbcsSystemInstance") + log.Info("DB System Getting Updated with func UpdateDbcsSystemIdInst") updateFlag := false updateDbcsDetails := database.UpdateDbSystemDetails{} - oldSpec, err := dbcs.GetLastSuccessfulSpec() + // Log annotations before retrieving the old spec + log.Info("Current annotations", "annotations", dbcs.GetAnnotations()) + // oldSpec, err := dbcs.GetLastSuccessfulSpecWithLog() + oldSpec, err := dbcs.GetLastSuccessfulSpecWithLog(log) // Use the new method if err != nil { + log.Error(err, "Failed to get last successful spec") return err } + if oldSpec == nil { + log.Info("oldSpec is nil") + } else { + log.Info("Details of oldSpec", "oldSpec", oldSpec) + } + // Log the entire oldSpec to see its contents + log.Info("Details of updateFlag -> " + fmt.Sprint(updateFlag)) + if dbcs.Spec.DbSystem.CpuCoreCount > 0 && dbcs.Spec.DbSystem.CpuCoreCount != oldSpec.DbSystem.CpuCoreCount { + log.Info("DB System cpu core count is: " + fmt.Sprint(dbcs.Spec.DbSystem.CpuCoreCount) + " DB System old cpu count is: " + fmt.Sprint(oldSpec.DbSystem.CpuCoreCount)) updateDbcsDetails.CpuCoreCount = common.Int(dbcs.Spec.DbSystem.CpuCoreCount) updateFlag = true } if dbcs.Spec.DbSystem.Shape != "" && dbcs.Spec.DbSystem.Shape != oldSpec.DbSystem.Shape { + log.Info("DB System desired shape is :" + string(dbcs.Spec.DbSystem.Shape) + "DB System old shape is " + string(oldSpec.DbSystem.Shape)) updateDbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) updateFlag = true } if dbcs.Spec.DbSystem.LicenseModel != "" && dbcs.Spec.DbSystem.LicenseModel != oldSpec.DbSystem.LicenseModel { licenceModel := getLicenceModel(dbcs) + log.Info("DB System desired License Model is :" + string(dbcs.Spec.DbSystem.LicenseModel) + "DB Sytsem old License Model is " + string(oldSpec.DbSystem.LicenseModel)) updateDbcsDetails.LicenseModel = database.UpdateDbSystemDetailsLicenseModelEnum(licenceModel) updateFlag = true } if dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != 0 && dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != oldSpec.DbSystem.InitialDataStorageSizeInGB { + log.Info("DB System desired Storage Size is :" + fmt.Sprint(dbcs.Spec.DbSystem.InitialDataStorageSizeInGB) + "DB System old Storage Size is " + fmt.Sprint(oldSpec.DbSystem.InitialDataStorageSizeInGB)) updateDbcsDetails.DataStorageSizeInGBs = &dbcs.Spec.DbSystem.InitialDataStorageSizeInGB updateFlag = true } - + log.Info("Details of updateFlag after validations is" + fmt.Sprint(updateFlag)) if updateFlag { updateDbcsRequest := database.UpdateDbSystemRequest{ DbSystemId: common.String(*dbcs.Spec.Id), @@ -536,10 +553,10 @@ func GetResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id s func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { - if dbcs.Spec.Id == nil { - dbcs.Status.State = "FAILED" - return nil - } + if dbcs.Spec.Id == nil { + dbcs.Status.State = "FAILED" + return nil + } dbcsId := *dbcs.Spec.Id @@ -768,4 +785,4 @@ func ValidateSpex(logger logr.Logger, kubeClient client.Client, dbClient databas return nil -} +} \ No newline at end of file diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml index e933d5a4..83890a3f 100644 --- a/config/crd/bases/database.oracle.com_DbcsSystem.yaml +++ b/config/crd/bases/database.oracle.com_DbcsSystem.yaml @@ -51,7 +51,7 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup COnfig Network Struct + description: DB Backup Config Network Struct properties: autoBackupEnabled: type: boolean @@ -135,6 +135,56 @@ spec: type: string ociSecret: type: string + pdbConfigs: + items: + description: PDBConfig defines details of PDB struct for DBCS systems + properties: + freeformTags: + additionalProperties: + type: string + description: '// Free-form tags for this resource. Each tag + is a simple key-value pair with no predefined name, type, + or namespace. // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). + // Example: `{"Department": "Finance"}`' + type: object + isDelete: + description: To specify whether to delete the PDB + type: boolean + pdbAdminPassword: + description: // A strong password for PDB Admin. The password + must be at least nine characters and contain at least two + uppercase, two lowercase, two numbers, and two special characters. + The special characters must be _, \#, or -. + type: string + pdbName: + description: The name for the pluggable database (PDB). The + name is unique in the context of a Database. The name must + begin with an alphabetic character and can contain a maximum + of thirty alphanumeric characters. Special characters are + not permitted. The pluggable database name should not be same + as the container database name. + type: string + pluggableDatabaseId: + description: The OCID of the PDB for deletion purposes. + type: string + shouldPdbAdminAccountBeLocked: + description: // The locked mode of the pluggable database admin + account. If false, the user needs to provide the PDB Admin + Password to connect to it. // If true, the pluggable database + will be locked and user cannot login to it. + type: boolean + tdeWalletPassword: + description: // The existing TDE wallet password of the CDB. + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - pluggableDatabaseId + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array required: - ociConfigMap type: object diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index 3f4b1c46..8e34f556 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -51,7 +51,7 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup COnfig Network Struct + description: DB Backup Config Network Struct properties: autoBackupEnabled: type: boolean @@ -135,6 +135,51 @@ spec: type: string ociSecret: type: string + pdbConfigs: + items: + description: PDBConfig defines details of PDB struct for DBCS systems + properties: + freeformTags: + additionalProperties: + type: string + description: '// Free-form tags for this resource. Each tag + is a simple key-value pair with no predefined name, type, + or namespace. // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). + // Example: `{"Department": "Finance"}`' + type: object + isDelete: + description: To specify whether to delete the PDB + type: boolean + pdbAdminPassword: + description: // A strong password for PDB Admin. The password + must be at least nine characters and contain at least two + uppercase, two lowercase, two numbers, and two special characters. + The special characters must be _, \#, or -. + type: string + pdbName: + description: The name for the pluggable database (PDB). The + name is unique in the context of a Database. The name must + begin with an alphabetic character and can contain a maximum + of thirty alphanumeric characters. Special characters are + not permitted. The pluggable database name should not be same + as the container database name. + type: string + pluggableDatabaseId: + description: The OCID of the PDB for deletion purposes. + type: string + shouldPdbAdminAccountBeLocked: + description: // The locked mode of the pluggable database admin + account. If false, the user needs to provide the PDB Admin + Password to connect to it. // If true, the pluggable database + will be locked and user cannot login to it. + type: boolean + tdeWalletPassword: + description: // The existing TDE wallet password of the CDB. + type: string + required: + - pdbName + type: object + type: array required: - ociConfigMap type: object @@ -237,4 +282,4 @@ status: kind: "" plural: "" conditions: [] - storedVersions: [] + storedVersions: [] \ No newline at end of file diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index 003be62a..dd50caae 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2022 Oracle and/or its affiliates. +** Copyright (c) 2022-2024 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,7 +40,10 @@ package controllers import ( "context" + "fmt" "reflect" + "strings" + "time" databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" dbcsv1 "github.com/oracle/oracle-database-operator/commons/dbcssystem" @@ -48,11 +51,14 @@ import ( "github.com/oracle/oracle-database-operator/commons/oci" "github.com/go-logr/logr" + "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/core" "github.com/oracle/oci-go-sdk/v65/database" "github.com/oracle/oci-go-sdk/v65/workrequests" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -91,12 +97,10 @@ type DbcsSystemReconciler struct { func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { r.Logger = log.FromContext(ctx) - // your logic here - - //r.Logger = r.Logv1.WithValues("Instance.Namespace", req.NamespacedName) var err error // Get the dbcs instance from the cluster dbcsInst := &databasev1alpha1.DbcsSystem{} + r.Logger.Info("Reconciling DbSystemDetails", "name", req.NamespacedName) if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, dbcsInst); err != nil { if !errors.IsNotFound(err) { @@ -127,10 +131,13 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } r.wrClient, err = workrequests.NewWorkRequestClientWithConfigurationProvider(provider) + if err != nil { + return ctrl.Result{}, err + } r.Logger.Info("OCI provider configured succesfully") /* - Using Finalizer for object deletion + Using Finalizer for object deletion */ if dbcsInst.ObjectMeta.DeletionTimestamp.IsZero() { @@ -154,6 +161,23 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue return ctrl.Result{}, nil } + + // Check if PDBConfig is defined + pdbConfigs := dbcsInst.Spec.PdbConfigs + for _, pdbConfig := range pdbConfigs { + if pdbConfig.PdbName != nil { + // Handle PDB deletion if PluggableDatabaseId is defined and isDelete is true + if pdbConfig.IsDelete != nil && pdbConfig.PluggableDatabaseId != nil && *pdbConfig.IsDelete { + // Call deletePluggableDatabase function + dbSystemId := *dbcsInst.Spec.Id + if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + } + } + // Remove the finalizer and update the object finalizer.Unregister(r.KubeClient, dbcsInst) r.Logger.Info("Finalizer unregistered successfully.") // Stop reconciliation as the item is being deleted @@ -161,7 +185,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } /* - Determine whether it's a provision or bind operation + Determine whether it's a provision or bind operation */ lastSucSpec, err := dbcsInst.GetLastSuccessfulSpec() if err != nil { @@ -218,10 +242,10 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - dbcsInstId := *dbcsInst.Spec.Id + dbSystemId := *dbcsInst.Spec.Id if err := dbcsv1.UpdateDbcsSystemId(r.KubeClient, dbcsInst); err != nil { // Change the status to Failed - assignDBCSID(dbcsInst, dbcsInstId) + assignDBCSID(dbcsInst, dbSystemId) if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Failed, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } @@ -230,11 +254,11 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("Sync information from remote DbcsSystem System successfully") - dbcsInstId = *dbcsInst.Spec.Id + dbSystemId = *dbcsInst.Spec.Id if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { return ctrl.Result{}, err } - assignDBCSID(dbcsInst, dbcsInstId) + assignDBCSID(dbcsInst, dbSystemId) } else { if dbcsInst.Spec.Id == nil { dbcsInst.Spec.Id = lastSucSpec.Id @@ -261,18 +285,382 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) //r.updateWalletSecret(dbcs) // Update the last succesful spec - dbcsInstId := *dbcsInst.Spec.Id + dbSystemId := *dbcsInst.Spec.Id if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { return ctrl.Result{}, err } //assignDBCSID(dbcsInst,dbcsI) // Change the phase to "Available" - assignDBCSID(dbcsInst, dbcsInstId) + assignDBCSID(dbcsInst, dbSystemId) if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Available, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } + r.Logger.Info("DBInst after assignment", "dbcsInst:->", dbcsInst) + // // Check if PDBConfig is defined and needs to be created or deleted + pdbConfigs := dbcsInst.Spec.PdbConfigs + + if pdbConfigs != nil { + for _, pdbConfig := range pdbConfigs { + if pdbConfig.PdbName != nil { + // Get database details + // Get DB Home ID by DB System ID + // Get Compartment ID by DB System ID + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + return ctrl.Result{}, err + } + dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, dbSystemId) + if err != nil { + fmt.Printf("Failed to get DB Home ID: %v\n", err) + return ctrl.Result{}, err + } + databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, dbSystemId, compartmentId, dbHomeId) + if err != nil { + fmt.Printf("Failed to get database IDs: %v\n", err) + return ctrl.Result{}, err + } + + // Now you can use dbDetails to access database attributes + r.Logger.Info("Database details fetched successfully", "DatabaseId", databaseIds) + + // Check if deletion is requested + if pdbConfig.IsDelete != nil && *pdbConfig.IsDelete { + // Call deletePluggableDatabase function + if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { + return ctrl.Result{}, err + } + // Continue to the next pdbConfig + continue + } else { + // Call the method to create the pluggable database + r.Logger.Info("Calling createPluggableDatabase", "ctx:->", ctx, "dbcsInst:->", dbcsInst, "databaseIds:->", databaseIds[0], "compartmentId:->", compartmentId) + err := r.createPluggableDatabase(ctx, dbcsInst, pdbConfig, databaseIds[0], compartmentId, dbSystemId) + if err != nil { + // Handle error if required + return ctrl.Result{}, err + } + } + } + } + } else { + r.Logger.Info("No PDB configurations given.") + } return ctrl.Result{}, nil + +} + +// getDbHomeIdByDbSystemID retrieves the DB Home ID associated with the given DB System ID +func (r *DbcsSystemReconciler) getDbHomeIdByDbSystemID(ctx context.Context, compartmentId, dbSystemId string) (string, error) { + listRequest := database.ListDbHomesRequest{ + CompartmentId: &compartmentId, + DbSystemId: &dbSystemId, + } + + listResponse, err := r.dbClient.ListDbHomes(ctx, listRequest) + if err != nil { + return "", fmt.Errorf("failed to list DB homes: %v", err) + } + + if len(listResponse.Items) == 0 { + return "", fmt.Errorf("no DB homes found for DB system ID: %s", dbSystemId) + } + + return *listResponse.Items[0].Id, nil +} +func (r *DbcsSystemReconciler) getCompartmentIDByDbSystemID(ctx context.Context, dbSystemId string) (string, error) { + // Construct the GetDbSystem request + getRequest := database.GetDbSystemRequest{ + DbSystemId: &dbSystemId, + } + + // Call GetDbSystem API using the existing dbClient + getResponse, err := r.dbClient.GetDbSystem(ctx, getRequest) + if err != nil { + return "", fmt.Errorf("failed to get DB system details: %v", err) + } + + // Extract the compartment ID from the DB system details + compartmentId := *getResponse.DbSystem.CompartmentId + + return compartmentId, nil +} +func (r *DbcsSystemReconciler) getDatabaseIDByDbSystemID(ctx context.Context, dbSystemId, compartmentId, dbHomeId string) ([]string, error) { + // Construct the ListDatabases request + request := database.ListDatabasesRequest{ + SystemId: &dbSystemId, + CompartmentId: &compartmentId, + DbHomeId: &dbHomeId, + } + + // Call ListDatabases API using the existing dbClient + response, err := r.dbClient.ListDatabases(ctx, request) + if err != nil { + return nil, fmt.Errorf("failed to list databases: %v", err) + } + + // Extract database IDs from the response + var databaseIds []string + for _, dbSummary := range response.Items { + databaseIds = append(databaseIds, *dbSummary.Id) + } + + return databaseIds, nil +} + +func (r *DbcsSystemReconciler) createPluggableDatabase(ctx context.Context, dbcs *databasev1alpha1.DbcsSystem, pdbConfig databasev1alpha1.PDBConfig, databaseId, compartmentId, dbSystemId string) error { + r.Logger.Info("Checking if the pluggable database exists", "PDBName", pdbConfig.PdbName) + + // Check if the pluggable database already exists + exists, pdbId, err := r.doesPluggableDatabaseExist(ctx, compartmentId, pdbConfig.PdbName) + if err != nil { + r.Logger.Error(err, "Failed to check if pluggable database exists", "PDBName", pdbConfig.PdbName) + return err + } + if exists { + // Set the PluggableDatabaseId in PDBConfig + pdbConfig.PluggableDatabaseId = pdbId + r.Logger.Info("Pluggable database already exists", "PDBName", pdbConfig.PdbName, "PluggableDatabaseId", *pdbConfig.PluggableDatabaseId) + return nil + } + + // Define the DatabaseExists method locally + databaseExists := func(dbSystemID string) (bool, error) { + req := database.GetDbSystemRequest{ + DbSystemId: &dbSystemID, + } + _, err := r.dbClient.GetDbSystem(ctx, req) + if err != nil { + if ociErr, ok := err.(common.ServiceError); ok && ociErr.GetHTTPStatusCode() == 404 { + return false, nil + } + return false, err + } + return true, nil + } + + exists, err = databaseExists(dbSystemId) + if err != nil { + r.Logger.Error(err, "Failed to check database existence") + return err + } + + if !exists { + errMsg := fmt.Sprintf("Database does not exist: %s", dbSystemId) + r.Logger.Error(fmt.Errorf(errMsg), "Database not found") + return fmt.Errorf(errMsg) + } + + // Fetch secrets for TdeWalletPassword and PdbAdminPassword + tdeWalletPassword, err := r.getSecret(ctx, dbcs.Namespace, *pdbConfig.TdeWalletPassword) + // Trim newline character from the password + tdeWalletPassword = strings.TrimSpace(tdeWalletPassword) + r.Logger.Info("TDE wallet password retrieved successfully") + if err != nil { + r.Logger.Error(err, "Failed to get TDE wallet password secret") + return err + } + + pdbAdminPassword, err := r.getSecret(ctx, dbcs.Namespace, *pdbConfig.PdbAdminPassword) + // Trim newline character from the password + pdbAdminPassword = strings.TrimSpace(pdbAdminPassword) + r.Logger.Info("PDB admin password retrieved successfully") + if err != nil { + r.Logger.Error(err, "Failed to get PDB admin password secret") + return err + } + + // Proceed with creating the pluggable database + r.Logger.Info("Creating pluggable database", "PDBName", pdbConfig.PdbName) + createPdbReq := database.CreatePluggableDatabaseRequest{ + CreatePluggableDatabaseDetails: database.CreatePluggableDatabaseDetails{ + PdbName: pdbConfig.PdbName, + ContainerDatabaseId: &databaseId, + ShouldPdbAdminAccountBeLocked: pdbConfig.ShouldPdbAdminAccountBeLocked, + PdbAdminPassword: common.String(pdbAdminPassword), + TdeWalletPassword: common.String(tdeWalletPassword), + FreeformTags: pdbConfig.FreeformTags, + }, + } + response, err := r.dbClient.CreatePluggableDatabase(ctx, createPdbReq) + if err != nil { + r.Logger.Error(err, "Failed to create pluggable database", "PDBName", pdbConfig.PdbName) + return err + } + // Set the PluggableDatabaseId in PDBConfig + // Set the PluggableDatabaseId in PDBConfig + pdbConfig.PluggableDatabaseId = response.PluggableDatabase.Id + + r.Logger.Info("Pluggable database creation initiated", "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) + + // Polling mechanism to check PDB status + const maxRetries = 120 // total 1 hour wait for creation of PDB + const retryInterval = 30 // in seconds + + for i := 0; i < maxRetries; i++ { + getPdbReq := database.GetPluggableDatabaseRequest{ + PluggableDatabaseId: pdbConfig.PluggableDatabaseId, + } + + getPdbResp, err := r.dbClient.GetPluggableDatabase(ctx, getPdbReq) + if err != nil { + r.Logger.Error(err, "Failed to get pluggable database status", "PDBID", *pdbConfig.PluggableDatabaseId) + return err + } + + pdbStatus := getPdbResp.PluggableDatabase.LifecycleState + r.Logger.Info("Checking pluggable database status", "PDBID", *pdbConfig.PluggableDatabaseId, "Status", pdbStatus) + + if pdbStatus == database.PluggableDatabaseLifecycleStateAvailable { + r.Logger.Info("Pluggable database successfully created", "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) + return nil + } + + if pdbStatus == database.PluggableDatabaseLifecycleStateFailed { + r.Logger.Error(fmt.Errorf("pluggable database creation failed"), "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) + return fmt.Errorf("pluggable database creation failed") + } + + time.Sleep(retryInterval * time.Second) + } + + r.Logger.Error(fmt.Errorf("timed out waiting for pluggable database to become available"), "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) + return fmt.Errorf("timed out waiting for pluggable database to become available") +} + +func (r *DbcsSystemReconciler) pluggableDatabaseExists(ctx context.Context, pluggableDatabaseId string) (bool, error) { + req := database.GetPluggableDatabaseRequest{ + PluggableDatabaseId: &pluggableDatabaseId, + } + _, err := r.dbClient.GetPluggableDatabase(ctx, req) + if err != nil { + if ociErr, ok := err.(common.ServiceError); ok && ociErr.GetHTTPStatusCode() == 404 { + // PDB does not exist + return false, nil + } + // Other error occurred + return false, err + } + // PDB exists + return true, nil +} + +func (r *DbcsSystemReconciler) deletePluggableDatabase(ctx context.Context, pdbConfig databasev1alpha1.PDBConfig, dbSystemId string) error { + if pdbConfig.PdbName == nil { + return fmt.Errorf("PDB name is not specified") + } + + r.Logger.Info("Deleting pluggable database", "PDBName", *pdbConfig.PdbName) + + if pdbConfig.PluggableDatabaseId == nil { + r.Logger.Info("PluggableDatabaseId is not specified, getting pluggable databaseID") + // Call a function to retrieve PluggableDatabaseId + pdbID, err := r.getPluggableDatabaseID(ctx, pdbConfig, dbSystemId) + if err != nil { + return fmt.Errorf("failed to get PluggableDatabaseId: %v", err) + } + pdbConfig.PluggableDatabaseId = &pdbID + } + + // Now pdbConfig.PluggableDatabaseId should not be nil + if pdbConfig.PluggableDatabaseId == nil { + return fmt.Errorf("PluggableDatabaseId is still nil after retrieval attempt. Nothing to delete") + } + + // Check if PluggableDatabaseId exists in the live system + exists, err := r.pluggableDatabaseExists(ctx, *pdbConfig.PluggableDatabaseId) + if err != nil { + r.Logger.Error(err, "Failed to check if pluggable database exists", "PluggableDatabaseId", *pdbConfig.PluggableDatabaseId) + return err + } + if !exists { + r.Logger.Info("PluggableDatabaseId does not exist in the live system, nothing to delete", "PluggableDatabaseId", *pdbConfig.PluggableDatabaseId) + return nil + } + + // Define the delete request + deleteReq := database.DeletePluggableDatabaseRequest{ + PluggableDatabaseId: pdbConfig.PluggableDatabaseId, + } + + // Call OCI SDK to delete the PDB + _, err = r.dbClient.DeletePluggableDatabase(ctx, deleteReq) + if err != nil { + r.Logger.Error(err, "Failed to delete pluggable database", "PDBName", *pdbConfig.PdbName) + return err + } + + r.Logger.Info("Successfully deleted pluggable database", "PDBName", *pdbConfig.PdbName) + return nil +} + +func (r *DbcsSystemReconciler) getPluggableDatabaseID(ctx context.Context, pdbConfig databasev1alpha1.PDBConfig, dbSystemId string) (string, error) { + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + return "", err + } + request := database.ListPluggableDatabasesRequest{ + CompartmentId: &compartmentId, + } + + response, err := r.dbClient.ListPluggableDatabases(ctx, request) + if err != nil { + return "", fmt.Errorf("failed to list Pluggable Databases: %v", err) + } + + var pdbID string + + for _, pdb := range response.Items { + if *pdb.PdbName == *pdbConfig.PdbName { + pdbID = *pdb.Id + break + } + } + + if pdbID == "" { + return "", fmt.Errorf("pluggable database '%s' not found", *pdbConfig.PdbName) + } + return pdbID, nil +} + +// doesPluggableDatabaseExist checks if a pluggable database with the given name exists +func (r *DbcsSystemReconciler) doesPluggableDatabaseExist(ctx context.Context, compartmentId string, pdbName *string) (bool, *string, error) { + if pdbName == nil { + return false, nil, fmt.Errorf("pdbName is nil") + } + + listPdbsReq := database.ListPluggableDatabasesRequest{ + CompartmentId: &compartmentId, + } + + resp, err := r.dbClient.ListPluggableDatabases(ctx, listPdbsReq) + if err != nil { + return false, nil, err + } + + for _, pdb := range resp.Items { + if pdb.PdbName != nil && *pdb.PdbName == *pdbName && pdb.LifecycleState != "TERMINATED" { + return true, pdb.Id, nil + } + } + + return false, nil, nil +} +func (r *DbcsSystemReconciler) getSecret(ctx context.Context, namespace, secretName string) (string, error) { + secret := &corev1.Secret{} + err := r.KubeClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: secretName}, secret) + if err != nil { + return "", err + } + + // Assume the secret contains only one key-value pair + for _, value := range secret.Data { + return string(value), nil + } + + return "", fmt.Errorf("secret %s is empty", secretName) } func assignDBCSID(dbcsInst *databasev1alpha1.DbcsSystem, dbcsID string) { diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md index c8b8d5d9..103f1b09 100644 --- a/docs/dbcs/README.md +++ b/docs/dbcs/README.md @@ -22,6 +22,7 @@ Two-node Oracle RAC DB systems require Oracle Enterprise Edition - Extreme Perfo For standard provisioning of DB systems (using Oracle Automatic Storage Management (ASM) as your storage management software), the following database releases are supported: +- Oracle Database 23ai - Oracle Database 21c - Oracle Database 19c - Oracle Database 18c (18.0) @@ -32,6 +33,7 @@ For standard provisioning of DB systems (using Oracle Automatic Storage Manageme For fast provisioning of single-node virtual machine database systems (using Logical Volume Manager as your storage management software), the following database releases are supported: +- Oracle Database 23ai - Oracle Database 21c - Oracle Database 19c - Oracle Database 18c @@ -43,34 +45,33 @@ For fast provisioning of single-node virtual machine database systems (using Log To deploy OraOperator, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. After the Oracle Database Operator is deployed, you can see the DB operator pods running in the Kubernetes Cluster. As part of the OraOperator deployment, the DBCS Controller is deployed as a CRD (Custom Resource Definition). The following screen output is an example of such a deployment: -``` +```bash [root@test-server oracle-database-operator]# kubectl get ns NAME STATUS AGE -cert-manager Active 2m5s -default Active 125d -kube-node-lease Active 125d -kube-public Active 125d -kube-system Active 125d -oracle-database-operator-system Active 17s <<<< namespace to deploy the Oracle Database Operator +cert-manager Active 33d +default Active 118d +kube-node-lease Active 118d +kube-public Active 118d +kube-system Active 118d +oracle-database-operator-system Active 10m <<<< namespace to deploy the Oracle Database Operator [root@test-server oracle-database-operator]# kubectl get all -n oracle-database-operator-system NAME READY STATUS RESTARTS AGE -pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 0 28s - +pod/oracle-database-operator-controller-manager-678f96f5f4-f4rhq 1/1 Running 0 10m +pod/oracle-database-operator-controller-manager-678f96f5f4-plxcp 1/1 Running 0 10m +pod/oracle-database-operator-controller-manager-678f96f5f4-qgcg8 1/1 Running 0 10m + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 29s -service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 29s - +service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.197.164 8443/TCP 11m +service/oracle-database-operator-webhook-service ClusterIP 10.96.35.62 443/TCP 11m + NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 29s - +deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 11m + NAME DESIRED CURRENT READY AGE -replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 29s -[root@docker-test-server oracle-database-operator]# - +replicaset.apps/oracle-database-operator-controller-manager-6657bfc664 0 0 0 11m +replicaset.apps/oracle-database-operator-controller-manager-678f96f5f4 3 3 3 10m [root@test-server oracle-database-operator]# kubectl get crd NAME CREATED AT @@ -97,19 +98,19 @@ Before you deploy a DBCS system in OCI using the Oracle DB Operator DBCS Control ## 1. Create a Kubernetes Configmap. For example: We are creating a Kubernetes Configmap named `oci-cred` using the OCI account we are using as below: -``` +```bash kubectl create configmap oci-cred \ ---from-literal=tenancy=ocid1.tenancy.oc1..................67iypsmea \ ---from-literal=user=ocid1.user.oc1..aaaaaaaaxw3i...............ce6qzdrnmq \ ---from-literal=fingerprint=b2:7c:a8:d5:44:f5.....................:9a:55 \ +--from-literal=tenancy= \ +--from-literal=user= \ +--from-literal=fingerprint= \ --from-literal=region=us-phoenix-1 ``` ## 2. Create a Kubernetes secret `oci-privatekey` using the OCI Pem key taken from OCI console for the account you are using: -``` --- assuming the OCI Pem key to be "/root/.oci/oci_api_key.pem" +```bash +#---assuming the OCI Pem key to be "/root/.oci/oci_api_key.pem" kubectl create secret generic oci-privatekey --from-file=privatekey=/root/.oci/oci_api_key.pem ``` @@ -118,8 +119,8 @@ kubectl create secret generic oci-privatekey --from-file=privatekey=/root/.oci/o ## 3. Create a Kubernetes secret named `admin-password`; This passward must meet the minimum passward requirements for the OCI BDBCS Service. For example: -``` --- assuming the passward has been added to a text file named "admin-password": +```bash +#-- assuming the passward has been added to a text file named "admin-password": kubectl create secret generic admin-password --from-file=./admin-password -n default ``` @@ -128,8 +129,8 @@ kubectl create secret generic admin-password --from-file=./admin-password -n def ## 4. Create a Kubernetes secret named `tde-password`; this passward must meet the minimum passward requirements for the OCI BDBCS Service. For example: -``` --- assuming the passward has been added to a text file named "tde-password": +```bash +# -- assuming the passward has been added to a text file named "tde-password": kubectl create secret generic tde-password --from-file=./tde-password -n default ``` @@ -137,7 +138,7 @@ kubectl create secret generic tde-password --from-file=./tde-password -n default ## 5. Create an ssh key pair, and use its public key to create a Kubernetes secret named `oci-publickey`; the private key for this public key can be used later to access the DBCS system's host machine using ssh: -``` +```bash [root@test-server DBCS]# ssh-keygen -N "" -C "DBCS_System"-`date +%Y%m` -P "" Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): @@ -179,6 +180,9 @@ For more informatoin about the multiple use cases available to you to deploy and [8. Create BDBCS with All Parameters with Storage Management as LVM](./provisioning/dbcs_service_with_all_parameters_lvm.md) [9. Create BDBCS with All Parameters with Storage Management as ASM](./provisioning/dbcs_service_with_all_parameters_asm.md) [10. Deploy a 2 Node RAC DB System using OCI BDBCS Service](./provisioning/dbcs_service_with_2_node_rac.md) +[11. Create PDB to an existing DBCS System already deployed in OCI Base DBCS Service](./provisioning/create_pdb.md) +[12. Create Base DBCS with PDB in OCI](./provisioning/create_dbcs_with_pdb.md) +[13. Delete PDB of an existing Base DBCS in OCI](./provisioning/delete_pdb.md) ## Connecting to OCI DBCS database deployed using Oracle DB Operator DBCS Controller diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md index 6fcff5de..c58f2ba9 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md @@ -14,16 +14,16 @@ This example uses `bind_to_existing_dbcs_system.yaml` to bind to an existing DBC Use the file: [bind_to_existing_dbcs_system.yaml](./bind_to_existing_dbcs_system.yaml) for this use case as below: 1. Deploy the .yaml file: -```sh -[root@docker-test-server DBCS]# kubectl apply -f bind_dbcs.yaml +```bash +[root@docker-test-server DBCS]# kubectl apply -f bind_to_existing_dbcs_system.yaml dbcssystem.database.oracle.com/dbcssystem-existing created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. +2. Monitor the Oracle DB Leader Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. -``` +```bash [root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml index 49647229..e6b02db7 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml @@ -3,6 +3,6 @@ kind: DbcsSystem metadata: name: dbcssystem-existing spec: - id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" + id: "ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza" ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" diff --git a/docs/dbcs/provisioning/create_dbcs_with_pdb.md b/docs/dbcs/provisioning/create_dbcs_with_pdb.md new file mode 100644 index 00000000..9deb0c77 --- /dev/null +++ b/docs/dbcs/provisioning/create_dbcs_with_pdb.md @@ -0,0 +1,55 @@ +# Deploy a DBCS DB System using OCI DBCS Service alongwith PDB + +In this use case, an OCI DBCS system is deployed using Oracle DB Operator DBCS controller along with PDB configuration + +**NOTE** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +Also, create a Kubernetes secret `pdb-password` using the file: + +```bash +#---assuming the PDB password is in ./pdb-password file" + +kubectl create secret generic pdb-password --from-file=./pdb-password -n default +``` + +This example uses `dbcs_service_with_pdb.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- Availability Domain for the DBCS VMDB as `OLou:US-ASHBURN-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` +- Database Admin Credential as `admin-password` +- Database Name as `dbsystem24` +- Oracle Database Software Image Version as `21c` +- Database Workload Type as Transaction Processing i.e. `OLTP` +- Database Hostname Prefix as `host24` +- Cpu Core Count as `1` +- Oracle VMDB Shape as `VM.Standard2.1` +- SSH Public key for the DBCS system being deployed as `oci-publickey` +- domain `subd215df3e6.k8stest.oraclevcn.com` +- OCID of the Subnet as `ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua` +- PDB Name as `pdb_sauahuja_11` +- TDE Wallet Password as `tde-password` +- PDB Admin Password as `pdb-password` + +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [dbcs_service_with_pdb.yaml](./dbcs_service_with_pdb.yaml) for this use case as below: + +1. Deploy the .yaml file: +```bash +[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_with_pdb.yaml +dbcssystem.database.oracle.com/dbcssystem-create-with-pdb created +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. + +NOTE: Check the DB Operator Pod name in your environment. + +```bash +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./dbcs_service_with_pdb_sample_output.log) is the sample output for a DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with PDB configurations. diff --git a/docs/dbcs/provisioning/create_pdb.md b/docs/dbcs/provisioning/create_pdb.md new file mode 100644 index 00000000..610ccd41 --- /dev/null +++ b/docs/dbcs/provisioning/create_pdb.md @@ -0,0 +1,55 @@ +# Create PDB to an existing DBCS System + +In this use case, an existing OCI DBCS system deployed earlier is going to have PDB/PDBs created. Its a 2 Step operation. + +In order to create PDBs to an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS Controller. +2. Apply the change to create PDBs. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +```bash +kubectl get dbcssystems +NAME AGE +dbcssystem-existing 3m33s +``` +Below proceeding further create PDB Admin Password which is going to used as name suggests. + +Create a Kubernetes secret `pdb-password` using the file: + +```bash +#---assuming the PDB password is in ./pdb-password file" + +kubectl create secret generic pdb-password --from-file=./pdb-password -n default +``` + +This example uses `createpdb_in_existing_dbcs_system_list.yaml` to scale up a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- TDE Wallet Password as `tde-password` +- PDB Admin Password as `pdb-password` +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [createpdb_in_existing_dbcs_system_list.yaml](./createpdb_in_existing_dbcs_system_list.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f createpdb_in_existing_dbcs_system_list.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./createpdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for creation of PDBs on an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml new file mode 100644 index 00000000..84b36902 --- /dev/null +++ b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml @@ -0,0 +1,28 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: DbcsSystem +metadata: + name: dbcssystem-existing + namespace: default +spec: + id: "ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza" + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + pdbConfigs: + - pdbName: "pdb_sauahuja_sdk_11" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "Finance" + - pdbName: "pdb_sauahuja_sdk_12" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "HR" + - pdbName: "pdb_sauahuja_sdk_13" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "IT" \ No newline at end of file diff --git a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log new file mode 100644 index 00000000..b527a68b --- /dev/null +++ b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log @@ -0,0 +1,17 @@ +2024-07-01T10:25:10Z INFO DBInst after assignment {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-existing"}} +2024-07-01T10:25:10Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a"]} +2024-07-01T10:25:10Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-existing"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} +2024-07-01T10:25:10Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBName": "pdb_sauahuja_sdk_11"} +2024-07-01T10:25:12Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c"} +2024-07-01T10:25:12Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c"} +2024-07-01T10:25:12Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBName": "pdb_sauahuja_sdk_11"} +2024-07-01T10:25:12Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBName": "pdb_sauahuja_sdk_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq"} +2024-07-01T10:25:13Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} +2024-07-01T10:25:43Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} +2024-07-01T10:26:13Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} +2024-07-01T10:26:43Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} +2024-07-01T10:27:13Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} +2024-07-01T10:27:44Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} +2024-07-01T10:28:14Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} +2024-07-01T10:28:44Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "AVAILABLE"} +2024-07-01T10:28:44Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBName": "pdb_sauahuja_sdk_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq"} \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml b/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml new file mode 100644 index 00000000..2ab9f2cf --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml @@ -0,0 +1,38 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: DbcsSystem +metadata: + name: dbcssystem-create-with-pdb + namespace: default +spec: + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + dbSystem: + availabilityDomain: "OLou:US-ASHBURN-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" + dbAdminPaswordSecret: "admin-password" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "dbsys" + displayName: "dbsystem24" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "21c" + dbWorkload: "OLTP" + hostName: "host24" + shape: "VM.Standard3.Flex" + cpuCoreCount: 1 + domain: "subd215df3e6.k8stest.oraclevcn.com" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua" + pdbConfigs: + - pdbName: "pdb_sauahuja_11" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "Finance" + - pdbName: "pdb_sauahuja_12" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "HR" \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log new file mode 100644 index 00000000..8932a87a --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log @@ -0,0 +1,103 @@ +2024-07-01T11:12:38Z INFO Reconciling DbSystemDetails {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "name": {"name":"dbcssystem-create-with-pdb","namespace":"default"}} +2024-07-01T11:12:38Z INFO OCI provider configured succesfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:12:39Z INFO DbcsSystem DBSystem provisioning {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:12:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:13:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:14:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:15:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:16:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:17:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:18:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:19:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:20:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:21:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:22:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:23:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:24:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:25:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:26:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:27:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:28:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:29:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:30:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:31:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:32:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:33:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:34:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:35:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:36:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:37:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:38:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:39:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:40:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:41:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:42:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:43:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:44:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:45:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:46:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:47:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:48:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:49:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:50:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:51:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:52:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:53:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:54:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:55:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:56:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:57:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:58:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T11:59:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:00:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:01:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:02:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:03:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:04:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:05:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:06:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:07:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:08:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:09:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:10:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:11:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:12:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:13:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:14:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:15:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:16:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:17:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:18:55Z INFO DbcsSystem system provisioned succesfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:18:56Z INFO DBInst after assignment {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-create-with-pdb"}} +2024-07-01T12:18:56Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyavvnsgu4rnbsquagb7y7egz3ixsl5yvss32353cfdwn6a"]} +2024-07-01T12:18:56Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-create-with-pdb"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htyavvnsgu4rnbsquagb7y7egz3ixsl5yvss32353cfdwn6a", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} +2024-07-01T12:18:56Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_11"} +2024-07-01T12:18:57Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:18:57Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:18:57Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_11"} +2024-07-01T12:18:58Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja"} +2024-07-01T12:18:58Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} +2024-07-01T12:19:28Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} +2024-07-01T12:19:59Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} +2024-07-01T12:20:29Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} +2024-07-01T12:20:59Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} +2024-07-01T12:21:29Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} +2024-07-01T12:22:00Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} +2024-07-01T12:22:30Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "AVAILABLE"} +2024-07-01T12:22:30Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja"} +2024-07-01T12:22:30Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyavvnsgu4rnbsquagb7y7egz3ixsl5yvss32353cfdwn6a"]} +2024-07-01T12:22:30Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-create-with-pdb"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htyavvnsgu4rnbsquagb7y7egz3ixsl5yvss32353cfdwn6a", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} +2024-07-01T12:22:30Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_12"} +2024-07-01T12:22:31Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:22:31Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} +2024-07-01T12:22:31Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_12"} +2024-07-01T12:22:32Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_12", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la"} +2024-07-01T12:22:32Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} +2024-07-01T12:23:02Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} +2024-07-01T12:23:32Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} +2024-07-01T12:24:03Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} +2024-07-01T12:24:33Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} +2024-07-01T12:25:03Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} +2024-07-01T12:25:33Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} +2024-07-01T12:26:03Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "AVAILABLE"} +2024-07-01T12:26:03Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_12", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la"} \ No newline at end of file diff --git a/docs/dbcs/provisioning/delete_pdb.md b/docs/dbcs/provisioning/delete_pdb.md new file mode 100644 index 00000000..84d676bc --- /dev/null +++ b/docs/dbcs/provisioning/delete_pdb.md @@ -0,0 +1,50 @@ +# Delete PDB of an existing DBCS System + +In this use case, an existing OCI DBCS system deployed earlier is going to have PDB/PDBs deleted. Its a 2 Step operation. + +In order to create PDBs to an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS Controller. +2. Apply the change to delete PDBs. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +```bash +kubectl get dbcssystems +NAME AGE +dbcssystem-existing 3m33s +``` + +This example uses `deletepdb_in_existing_dbcs_system_list.yaml` to delete PDBs of a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- PDB Name to be deleted e.g `pdb_sauahuja_11` and `pdb_sauahuja_12` +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [deletepdb_in_existing_dbcs_system_list.yaml](./deletepdb_in_existing_dbcs_system_list.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f deletepdb_in_existing_dbcs_system_list.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deletion of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +```bash +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +3. Remove DBCS Systems resource- +```bash +kubectl delete -f deletepdb_in_existing_dbcs_system_list.yaml +``` + +## Sample Output + +[Here](./deletepdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for deletion of PDBs from an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. \ No newline at end of file diff --git a/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml new file mode 100644 index 00000000..fed3ec6c --- /dev/null +++ b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml @@ -0,0 +1,13 @@ +kind: DbcsSystem +metadata: + name: dbcssystem-existing + namespace: default +spec: + id: "ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza" + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + pdbConfigs: + - pdbName: "pdb_sauahuja_11" + isDelete: true + - pdbName: "pdb_sauahuja_12" + isDelete: true \ No newline at end of file diff --git a/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log new file mode 100644 index 00000000..a4f75fa5 --- /dev/null +++ b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log @@ -0,0 +1,8 @@ +2024-07-01T12:34:44Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a"]} +2024-07-01T12:34:44Z INFO Deleting pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_11"} +2024-07-01T12:34:44Z INFO PluggableDatabaseId is not specified, getting pluggable databaseID {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808"} +2024-07-01T12:34:45Z INFO Successfully deleted pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_11"} +2024-07-01T12:34:46Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a"]} +2024-07-01T12:34:46Z INFO Deleting pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_12"} +2024-07-01T12:34:46Z INFO PluggableDatabaseId is not specified, getting pluggable databaseID {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808"} +2024-07-01T12:34:47Z INFO Successfully deleted pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_12"} \ No newline at end of file diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 1838ad9f..ce279486 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -165,6 +165,8 @@ spec: type: string displayName: type: string + isLongTermBackup: + type: boolean ociConfig: description: "*********************** *\tOCI config ***********************" properties: @@ -173,6 +175,8 @@ spec: secretName: type: string type: object + retentionPeriodInDays: + type: integer target: description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: @@ -1264,7 +1268,7 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup COnfig Network Struct + description: DB Backup Config Network Struct properties: autoBackupEnabled: type: boolean @@ -1348,6 +1352,39 @@ spec: type: string ociSecret: type: string + pdbConfig: + description: PDBConfig defines details of PDB struct for DBCS systems + properties: + containerDatabaseId: + description: The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the CDB + type: string + freeformTags: + additionalProperties: + type: string + description: '// Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). // Example: `{"Department": "Finance"}`' + type: object + isDelete: + description: // Whether to delete the PDB mentioned + type: boolean + pdbAdminPassword: + description: // A strong password for PDB Admin. The password must be at least nine characters and contain at least two uppercase, two lowercase, two numbers, and two special characters. The special characters must be _, \#, or -. + type: string + pdbName: + description: The name for the pluggable database (PDB). The name is unique in the context of a Database. The name must begin with an alphabetic character and can contain a maximum of thirty alphanumeric characters. Special characters are not permitted. The pluggable database name should not be same as the container database name. + type: string + pluggableDatabaseId: + description: The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the PDB + type: string + shouldPdbAdminAccountBeLocked: + description: // The locked mode of the pluggable database admin account. If false, the user needs to provide the PDB Admin Password to connect to it. // If true, the pluggable database will be locked and user cannot login to it. + type: boolean + tdeWalletPassword: + description: // The existing TDE wallet password of the CDB. + type: string + required: + - containerDatabaseId + - pdbName + type: object required: - ociConfigMap type: object @@ -3024,10 +3061,11 @@ rules: - "" resources: - configmaps - - deployments - events - pods - - secrets + - pods/exec + - pods/log + - replicasets - services verbs: - create @@ -3041,12 +3079,8 @@ rules: - "" resources: - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services + - namespaces + - secrets verbs: - create - delete @@ -3059,7 +3093,6 @@ rules: - "" resources: - configmaps - - namespaces - secrets verbs: - create @@ -3072,8 +3105,10 @@ rules: - apiGroups: - "" resources: - - configmaps - - secrets + - deployments + - events + - pods + - services verbs: - create - delete @@ -3112,6 +3147,7 @@ rules: verbs: - get - list + - watch - apiGroups: - '''''' resources: @@ -3128,6 +3164,12 @@ rules: - apps resources: - configmaps + verbs: + - get + - list +- apiGroups: + - apps + resources: - deployments - pods - services @@ -3193,6 +3235,26 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -3489,7 +3551,6 @@ rules: - apiGroups: - monitoring.coreos.com resources: - - prometheusrules - servicemonitors verbs: - create @@ -3805,6 +3866,26 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-observability-oracle-com-v1alpha1-databaseobserver + failurePolicy: Fail + name: mdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -3999,6 +4080,26 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-observability-oracle-com-v1alpha1-databaseobserver + failurePolicy: Fail + name: vdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None --- apiVersion: apps/v1 kind: Deployment @@ -4025,7 +4126,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa imagePullPolicy: Always name: manager ports: From aa36b612ab3fcdcb97ca5a589cd2087b976fc922 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Thu, 18 Jul 2024 10:52:44 +0000 Subject: [PATCH 106/414] Sidb resource management and node scheduling --- .../v1alpha1/singleinstancedatabase_types.go | 11 +++++++ .../samples/sidb/singleinstancedatabase.yaml | 15 +++++++++ .../singleinstancedatabase_controller.go | 32 +++++++++++++++++++ docs/sidb/README.md | 4 +++ 4 files changed, 62 insertions(+) diff --git a/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index 0d49aa6b..7c6c1ea5 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -86,6 +86,17 @@ type SingleInstanceDatabaseSpec struct { Image SingleInstanceDatabaseImage `json:"image"` Persistence SingleInstanceDatabasePersistence `json:"persistence,omitempty"` InitParams *SingleInstanceDatabaseInitParams `json:"initParams,omitempty"` + Resources SingleInstanceDatabaseResources `json:"resources,omitempty"` +} + +type SingleInstanceDatabaseResource struct { + Cpu string `json:"cpu,omitempty"` + Memory string `json:"memory,omitempty"` +} + +type SingleInstanceDatabaseResources struct { + Requests *SingleInstanceDatabaseResource `json:"requests,omitempty"` + Limits *SingleInstanceDatabaseResource `json:"limits,omitempty"` } // SingleInstanceDatabasePersistence defines the storage size and class for PVC diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index b66082e1..4425acea 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -103,6 +103,21 @@ spec: ## Optionally specify a volume containing scripts in 'setup' and 'startup' folders to be executed during database setup and startup respectively. scriptsVolumeName: "" + ## Database pod resource details + ## cpu can be expressed in terms of cpu units and can be a plain integer or fractional value + ## memory is measured in bytes and can be expressed in plain integer or as a fixed-point number + ## using one of these quantity suffixes: E, P, T, G, M, k. + ## You can also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki. + resources: + ## requests denotes minimum node resources required/to be utilized by the database pod + requests: + cpu: + memory: + ## limits specifies the maximum node resources that can be utilized by the database pod + limits: + cpu: + memory: + ## Type of service . Applicable on cloud enviroments only ## if loadBalService : false, service type = "NodePort" else "LoadBalancer" loadBalancer: false diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index fa01ae7e..a20fa1fd 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -1096,6 +1096,38 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns } }(), + + Resources: func() corev1.ResourceRequirements { + if m.Spec.Resources.Requests != nil && m.Spec.Resources.Limits != nil { + return corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": resource.MustParse(m.Spec.Resources.Requests.Cpu), + "memory": resource.MustParse(m.Spec.Resources.Requests.Memory), + }, + Limits: corev1.ResourceList{ + "cpu": resource.MustParse(m.Spec.Resources.Limits.Cpu), + "memory": resource.MustParse(m.Spec.Resources.Requests.Memory), + }, + } + } else if m.Spec.Resources.Requests != nil { + return corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": resource.MustParse(m.Spec.Resources.Requests.Cpu), + "memory": resource.MustParse(m.Spec.Resources.Requests.Memory), + }, + } + } else if m.Spec.Resources.Limits != nil { + return corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + "cpu": resource.MustParse(m.Spec.Resources.Limits.Cpu), + "memory": resource.MustParse(m.Spec.Resources.Requests.Memory), + }, + } + } else { + return corev1.ResourceRequirements{} + } + + }(), }}, TerminationGracePeriodSeconds: func() *int64 { i := int64(30); return &i }(), diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 895c26a9..ff357195 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -25,6 +25,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [Delete a Database](#delete-a-database) * [Advanced Database Configurations](#advanced-database-configurations) * [Run Database with Multiple Replicas](#run-database-with-multiple-replicas) + * [Database Pod Resource Management](#database-pod-resource-management) * [Setup Database with LoadBalancer](#setup-database-with-loadbalancer) * [Enabling TCPS Connections](#enabling-tcps-connections) * [Specifying Custom Ports](#specifying-custom-ports) @@ -628,6 +629,9 @@ The following table depicts the fail over matrix for any destructive operation t - If the `ReadWriteOnce` access mode is used, all the replicas will be scheduled on the same node where the persistent volume would be mounted. - If the `ReadWriteMany` access mode is used, all the replicas will be distributed on different nodes. So, it is recommended to have replicas more than or equal to the number of the nodes as the database image is downloaded on all those nodes. This is beneficial in quick cold fail-over scenario (when the active pod dies) as the image would already be available on that node. +#### Database Pod Resource Management +When creating a Single Instance Database you can specify the cpu and memory resources needed by the database pod. These specified resources are passed to the `kube-scheduler` so that the pod gets scheduled on one of the pods that has the required resources available. To use database pod resource management specify values for the `resources` attributes in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. + #### Setup Database with LoadBalancer For the Single Instance Database, the default service is the `NodePort` service. You can enable the `LoadBalancer` service by using `kubectl patch` command. From cac2a9a825f02196b36fb4d289cbdf4538a87701 Mon Sep 17 00:00:00 2001 From: racpack Date: Mon, 29 Jul 2024 15:50:41 +0000 Subject: [PATCH 107/414] Psaini sharding branch1 --- PROJECT | 8 + .../v1alpha1/shardingdatabase_types.go | 108 +++---- .../v1alpha1/shardingdatabase_webhook.go | 270 ++++++++++++++++++ commons/sharding/catalog.go | 53 +++- commons/sharding/gsm.go | 2 +- commons/sharding/scommon.go | 213 ++++++++++++-- commons/sharding/shard.go | 19 +- ...database.oracle.com_shardingdatabases.yaml | 21 +- config/samples/kustomization.yaml | 1 + config/webhook/manifests.yaml | 41 +++ .../database/shardingdatabase_controller.go | 191 ++++++++----- docs/sharding/README.md | 73 +++-- docs/sharding/provisioning/debugging.md | 7 + .../sharding_provisioning_with_db_events.md | 40 +++ .../sharding_provisioning_with_db_events.yaml | 69 +++++ .../sharding_provisioning_with_free_images.md | 40 +++ ...harding_provisioning_with_free_images.yaml | 58 ++++ ...y_cloning_db_from_gold_image_across_ads.md | 57 ++++ ...ing_by_cloning_db_gold_image_in_same_ad.md | 53 ++++ ...ding_provisioning_with_chunks_specified.md | 43 +++ ..._provisioning_with_control_on_resources.md | 47 +++ ...ith_notification_using_oci_notification.md | 87 ++++++ ...ding_provisioning_without_db_gold_image.md | 40 +++ ...rding_scale_in_delete_an_existing_shard.md | 50 ++++ .../snr_ssharding_scale_out_add_shards.md | 37 +++ .../snr_ssharding_shard_prov.yaml | 58 ++++ .../snr_ssharding_shard_prov_chunks.yaml | 61 ++++ .../snr_ssharding_shard_prov_clone.yaml | 83 ++++++ ...ssharding_shard_prov_clone_across_ads.yaml | 91 ++++++ .../snr_ssharding_shard_prov_delshard.yaml | 69 +++++ .../snr_ssharding_shard_prov_extshard.yaml | 68 +++++ .../snr_ssharding_shard_prov_memory_cpu.yaml | 89 ++++++ ...sharding_shard_prov_send_notification.yaml | 85 ++++++ ...y_cloning_db_from_gold_image_across_ads.md | 7 +- ...ing_by_cloning_db_gold_image_in_same_ad.md | 4 +- ...ding_provisioning_with_chunks_specified.md | 40 +++ ..._provisioning_with_control_on_resources.md | 7 +- ...ith_notification_using_oci_notification.md | 5 +- ...ding_provisioning_without_db_gold_image.md | 7 +- ...rding_scale_in_delete_an_existing_shard.md | 7 +- .../ssharding_scale_out_add_shards.md | 5 +- .../system_sharding/ssharding_shard_prov.yaml | 1 - .../ssharding_shard_prov_chunks.yaml | 60 ++++ ...ssharding_shard_prov_clone_across_ads.yaml | 10 +- .../ssharding_shard_prov_delshard.yaml | 2 +- ...y_cloning_db_from_gold_image_across_ads.md | 2 + ...ing_by_cloning_db_gold_image_in_same_ad.md | 2 + ..._provisioning_with_control_on_resources.md | 3 + ...ith_notification_using_oci_notification.md | 3 + ...ding_provisioning_without_db_gold_image.md | 2 +- ...rding_scale_in_delete_an_existing_shard.md | 44 ++- .../udsharding_scale_out_add_shards.md | 1 + .../udsharding_shard_prov.yaml | 3 +- .../udsharding_shard_prov_clone.yaml | 2 +- ...dsharding_shard_prov_clone_across_ads.yaml | 10 +- .../udsharding_shard_prov_delshard.yaml | 4 +- .../udsharding_shard_prov_extshard.yaml | 2 +- .../udsharding_shard_prov_memory_cpu.yaml | 3 +- ...sharding_shard_prov_send_notification.yaml | 2 +- main.go | 3 + oracle-database-operator.yaml | 62 +++- 61 files changed, 2304 insertions(+), 231 deletions(-) create mode 100644 apis/database/v1alpha1/shardingdatabase_webhook.go create mode 100644 docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.md create mode 100644 docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml create mode 100644 docs/sharding/provisioning/free/sharding_provisioning_with_free_images.md create mode 100644 docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml create mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml create mode 100644 docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md create mode 100644 docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml diff --git a/PROJECT b/PROJECT index 7b3ec718..fbf861db 100644 --- a/PROJECT +++ b/PROJECT @@ -1,3 +1,7 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html domain: oracle.com layout: - go.kubebuilder.io/v2 @@ -67,6 +71,10 @@ resources: kind: ShardingDatabase path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1beta1 - api: crdVersion: v1 namespaced: true diff --git a/apis/database/v1alpha1/shardingdatabase_types.go b/apis/database/v1alpha1/shardingdatabase_types.go index 3b4f9c17..bc759094 100644 --- a/apis/database/v1alpha1/shardingdatabase_types.go +++ b/apis/database/v1alpha1/shardingdatabase_types.go @@ -58,39 +58,43 @@ import ( type ShardingDatabaseSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Shard []ShardSpec `json:"shard"` - Catalog []CatalogSpec `json:"catalog"` // The catalogSpes accept all the catalog parameters - Gsm []GsmSpec `json:"gsm"` // The GsmSpec will accept all the Gsm parameter - StorageClass string `json:"storageClass,omitempty"` // Optional Accept storage class name - DbImage string `json:"dbImage"` // Accept DB Image name - DbImagePullSecret string `json:"dbImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. - GsmImage string `json:"gsmImage"` // Acccept the GSM image name - GsmImagePullSecret string `json:"gsmImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. - StagePvcName string `json:"stagePvcName,omitempty"` // the Stagepvc for the backup of cluster - PortMappings []PortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least - Namespace string `json:"namespace,omitempty"` // Target namespace of the application. - IsDebug bool `json:"isDebug,omitempty"` // Optional parameter to enable logining - IsExternalSvc bool `json:"isExternalSvc,omitempty"` - IsClone bool `json:"isClone,omitempty"` - IsDataGuard bool `json:"isDataGuard,omitempty"` - ScriptsLocation string `json:"scriptsLocation,omitempty"` - IsDeleteOraPvc bool `json:"isDeleteOraPvc,omitempty"` - ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` - LivenessCheckPeriod int `json:"liveinessCheckPeriod,omitempty"` - ReplicationType string `json:"replicationType,omitempty"` - IsDownloadScripts bool `json:"isDownloadScripts,omitempty"` - InvitedNodeSubnetFlag string `json:"invitedNodeSubnetFlag,omitempty"` - InvitedNodeSubnet string `json:"InvitedNodeSubnet,omitempty"` - ShardingType string `json:"shardingType,omitempty"` - GsmShardSpace []GsmShardSpaceSpec `json:"gsmShardSpace,omitempty"` - GsmShardGroup []GsmShardGroupSpec `json:"gsmShardGroup,omitempty"` - ShardRegion []string `json:"shardRegion,omitempty"` - ShardBuddyRegion string `json:"shardBuddyRegion,omitempty"` - GsmService []GsmServiceSpec `json:"gsmService,omitempty"` - ShardConfigName string `json:"shardConfigName,omitempty"` - GsmDevMode string `json:"gsmDevMode,omitempty"` - DbSecret *SecretDetails `json:"dbSecret,omitempty"` // Secret Name to be used with Shard - IsTdeWallet bool `json:"isTdeWallet,omitempty"` + Shard []ShardSpec `json:"shard"` + Catalog []CatalogSpec `json:"catalog"` // The catalogSpes accept all the catalog parameters + Gsm []GsmSpec `json:"gsm"` // The GsmSpec will accept all the Gsm parameter + StorageClass string `json:"storageClass,omitempty"` // Optional Accept storage class name + DbImage string `json:"dbImage"` // Accept DB Image name + DbImagePullSecret string `json:"dbImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. + GsmImage string `json:"gsmImage"` // Acccept the GSM image name + GsmImagePullSecret string `json:"gsmImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. + StagePvcName string `json:"stagePvcName,omitempty"` // the Stagepvc for the backup of cluster + PortMappings []PortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least + Namespace string `json:"namespace,omitempty"` // Target namespace of the application. + IsDebug bool `json:"isDebug,omitempty"` // Optional parameter to enable logining + IsExternalSvc bool `json:"isExternalSvc,omitempty"` + IsClone bool `json:"isClone,omitempty"` + IsDataGuard bool `json:"isDataGuard,omitempty"` + ScriptsLocation string `json:"scriptsLocation,omitempty"` + IsDeleteOraPvc bool `json:"isDeleteOraPvc,omitempty"` + ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` + LivenessCheckPeriod int `json:"liveinessCheckPeriod,omitempty"` + ReplicationType string `json:"replicationType,omitempty"` + IsDownloadScripts bool `json:"isDownloadScripts,omitempty"` + InvitedNodeSubnetFlag string `json:"invitedNodeSubnetFlag,omitempty"` + InvitedNodeSubnet string `json:"InvitedNodeSubnet,omitempty"` + ShardingType string `json:"shardingType,omitempty"` + GsmShardSpace []GsmShardSpaceSpec `json:"gsmShardSpace,omitempty"` + GsmShardGroup []GsmShardGroupSpec `json:"gsmShardGroup,omitempty"` + ShardRegion []string `json:"shardRegion,omitempty"` + ShardBuddyRegion string `json:"shardBuddyRegion,omitempty"` + GsmService []GsmServiceSpec `json:"gsmService,omitempty"` + ShardConfigName string `json:"shardConfigName,omitempty"` + GsmDevMode string `json:"gsmDevMode,omitempty"` + DbSecret *SecretDetails `json:"dbSecret,omitempty"` // Secret Name to be used with Shard + IsTdeWallet string `json:"isTdeWallet,omitempty"` + TdeWalletPvc string `json:"tdeWalletPvc,omitempty"` + FssStorageClass string `json:"fssStorageClass,omitempty"` + TdeWalletPvcMountLocation string `json:"tdeWalletPvcMountLocation,omitempty"` + DbEdition string `json:"dbEdition,omitempty"` } // To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 @@ -101,8 +105,7 @@ type ShardingDatabaseStatus struct { Shard map[string]string `json:"shards,omitempty"` Catalog map[string]string `json:"catalogs,omitempty"` - - Gsm GsmStatus `json:"gsm,omitempty"` + Gsm GsmStatus `json:"gsm,omitempty"` // +patchMergeKey=type // +patchStrategy=merge @@ -164,21 +167,22 @@ type ShardingDatabaseList struct { // ShardSpec is a specification of Shards for an application deployment. // +k8s:openapi-gen=true type ShardSpec struct { - Name string `json:"name"` // Shard name that will be used deploy StatefulSet - StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` // Optional Shard Storage Size - EnvVars []EnvironmentVariable `json:"envVars,omitempty"` //Optional Env variables for Shards - Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` //Optional resource requirement for the container. - PvcName string `json:"pvcName,omitempty"` - Label string `json:"label,omitempty"` - IsDelete bool `json:"isDelete,omitempty"` - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` - PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` - ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` - ShardSpace string `json:"shardSpace,omitempty"` - ShardGroup string `json:"shardGroup,omitempty"` - ShardRegion string `json:"shardRegion,omitempty"` - DeployAs string `json:"deployAs,omitempty"` + Name string `json:"name"` // Shard name that will be used deploy StatefulSet + StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` // Optional Shard Storage Size + EnvVars []EnvironmentVariable `json:"envVars,omitempty"` //Optional Env variables for Shards + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` //Optional resource requirement for the container. + PvcName string `json:"pvcName,omitempty"` + Label string `json:"label,omitempty"` + // +kubebuilder:validation:Enum=enable;disable;failed;force + IsDelete string `json:"isDelete,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` + PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` + ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + ShardSpace string `json:"shardSpace,omitempty"` + ShardGroup string `json:"shardGroup,omitempty"` + ShardRegion string `json:"shardRegion,omitempty"` + DeployAs string `json:"deployAs,omitempty"` } // CatalogSpec defines the desired state of CatalogSpec @@ -190,7 +194,7 @@ type CatalogSpec struct { Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` // Optional resource requirement for the container. PvcName string `json:"pvcName,omitempty"` Label string `json:"label,omitempty"` - IsDelete bool `json:"isDelete,omitempty"` + IsDelete string `json:"isDelete,omitempty"` NodeSelector map[string]string `json:"nodeSelector,omitempty"` PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` @@ -208,7 +212,7 @@ type GsmSpec struct { Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` // Optional resource requirement for the container. PvcName string `json:"pvcName,omitempty"` Label string `json:"label,omitempty"` // Optional GSM Label - IsDelete bool `json:"isDelete,omitempty"` + IsDelete string `json:"isDelete,omitempty"` NodeSelector map[string]string `json:"nodeSelector,omitempty"` PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` diff --git a/apis/database/v1alpha1/shardingdatabase_webhook.go b/apis/database/v1alpha1/shardingdatabase_webhook.go new file mode 100644 index 00000000..8b91fb0c --- /dev/null +++ b/apis/database/v1alpha1/shardingdatabase_webhook.go @@ -0,0 +1,270 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1alpha1 + +import ( + "strings" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var shardingdatabaselog = logf.Log.WithName("shardingdatabase-resource") + +func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v1alpha1,name=mshardingdatabase.kb.io,admissionReviewVersions={v1} + +var _ webhook.Defaulter = &ShardingDatabase{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *ShardingDatabase) Default() { + shardingdatabaselog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. + if r.Spec.GsmDevMode != "" { + r.Spec.GsmDevMode = "dev" + } + + if r.Spec.IsTdeWallet == "" { + r.Spec.IsTdeWallet = "disable" + } + for pindex := range r.Spec.Shard { + if strings.ToLower(r.Spec.Shard[pindex].IsDelete) == "" { + r.Spec.Shard[pindex].IsDelete = "disable" + } + } + +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v1alpha1-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v1alpha1,name=vshardingdatabase.kb.io,admissionReviewVersions={v1} + +var _ webhook.Validator = &ShardingDatabase{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { + shardingdatabaselog.Info("validate create", "name", r.Name) + + // TODO(user): fill in your validation logic upon object creation. + // Check Secret configuration + var validationErr field.ErrorList + var validationErrs1 field.ErrorList + + //namespaces := db.GetWatchNamespaces() + //_, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + // if len(namespaces) != 0 && !containsNamespace { + // validationErr = append(validationErr, + // field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + // "Oracle database operator doesn't watch over this namespace")) + //} + + if r.Spec.DbSecret == nil { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret"), r.Spec.DbSecret, + "DbSecret cannot be set to nil")) + } else { + if len(r.Spec.DbSecret.Name) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("Name"), r.Spec.DbSecret.Name, + "Secret name cannot be set empty")) + } + if len(r.Spec.DbSecret.PwdFileName) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileName"), r.Spec.DbSecret.PwdFileName, + "Password file name cannot be set empty")) + } + if strings.ToLower(r.Spec.DbSecret.EncryptionType) != "base64" { + if strings.ToLower(r.Spec.DbSecret.KeyFileName) == "" { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileName"), r.Spec.DbSecret.KeyFileName, + "Key file name cannot be empty")) + } + } + + /** + if len(r.Spec.DbSecret.PwdFileMountLocation) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileMountLocation"), r.Spec.DbSecret.PwdFileMountLocation, + "Password file mount location cannot be empty")) + } + + if len(r.Spec.DbSecret.KeyFileMountLocation) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileMountLocation"), r.Spec.DbSecret.KeyFileMountLocation, + "KeyFileMountLocation file mount location cannot be empty")) + } + **/ + } + + if r.Spec.IsTdeWallet == "enable" { + if (len(r.Spec.FssStorageClass) == 0) && (len(r.Spec.TdeWalletPvc) == 0) { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("FssStorageClass"), r.Spec.FssStorageClass, + "FssStorageClass or TdeWalletPvc cannot be set empty if isTdeWallet set to true")) + + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("TdeWalletPvc"), r.Spec.TdeWalletPvc, + "FssStorageClass or TdeWalletPvc cannot be set empty if isTdeWallet set to true")) + } + } + + if r.Spec.IsTdeWallet != "" { + if (strings.ToLower(strings.TrimSpace(r.Spec.IsTdeWallet)) != "enable") && (strings.ToLower(strings.TrimSpace(r.Spec.IsTdeWallet)) != "disable") { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("isTdeWallet"), r.Spec.IsTdeWallet, + "isTdeWallet can be set to only \"enable\" or \"disable\"")) + } + } + + validationErrs1 = r.validateShardIsDelete() + if validationErrs1 != nil { + validationErr = append(validationErr, validationErrs1...) + } + + validationErrs1 = r.validateFreeEdition() + if validationErrs1 != nil { + validationErr = append(validationErr, validationErrs1...) + } + + // TODO(user): fill in your validation logic upon object creation. + if len(validationErr) == 0 { + return nil, nil + } + + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "ShardingDatabase"}, + r.Name, validationErr) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *ShardingDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + shardingdatabaselog.Info("validate update", "name", r.Name) + + // TODO(user): fill in your validation logic upon object update. + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *ShardingDatabase) ValidateDelete() (admission.Warnings, error) { + shardingdatabaselog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// ###### Vlaidation Block ################# + +func (r *ShardingDatabase) validateShardIsDelete() field.ErrorList { + + var validationErrs field.ErrorList + + for pindex := range r.Spec.Shard { + if (strings.ToLower(strings.TrimSpace(r.Spec.Shard[pindex].IsDelete)) != "enable") && (strings.ToLower(strings.TrimSpace(r.Spec.Shard[pindex].IsDelete)) != "disable") && (strings.ToLower(strings.TrimSpace(r.Spec.Shard[pindex].IsDelete)) != "failed") { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("shard").Child("isDelete"), r.Spec.Shard[pindex].IsDelete, + "r.Spec.Shard[pindex].IsDelete can be set to only enable|disable|failed")) + } + } + + if len(validationErrs) > 0 { + return validationErrs + } + return nil +} + +func (r *ShardingDatabase) validateFreeEdition() field.ErrorList { + + var validationErrs field.ErrorList + if strings.ToLower(r.Spec.DbEdition) == "free" { + // Shard Spec Checks + for i := 0; i < len(r.Spec.Shard); i++ { + for index, variable := range r.Spec.Shard[i].EnvVars { + if variable.Name == "ORACLE_SID" { + if strings.ToLower(variable.Value) != "free" { + validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("shard").Child("EnvVars"), r.Spec.Shard[i].EnvVars[index].Name, + "r.Spec.Shard[i].EnvVars[index].Name ORACLE_SID value can only be set to free")) + } + } + if variable.Name == "ORACLE_PDB" { + if strings.ToLower(variable.Value) != "freepdb" { + validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("shard").Child("EnvVars"), r.Spec.Shard[i].EnvVars[index].Name, + "r.Spec.Shard[i].EnvVars[index].Name ORACLE_PDB value can only be set to freepdb")) + } + } + } + } + // Catalog Spec Checks + for i := 0; i < len(r.Spec.Catalog); i++ { + for index, variable := range r.Spec.Catalog[i].EnvVars { + if variable.Name == "ORACLE_SID" { + if strings.ToLower(variable.Value) != "free" { + validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("catalog").Child("EnvVars"), r.Spec.Catalog[i].EnvVars[index].Name, + "r.Spec.Catalog[i].EnvVars[index].Name ORACLE_SID value can only be set to free")) + } + } + if variable.Name == "ORACLE_PDB" { + if strings.ToLower(variable.Value) != "freepdb" { + validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("catalog").Child("EnvVars"), r.Spec.Catalog[i].EnvVars[index].Name, + "r.Spec.Catalog[i].EnvVars[index].Name ORACLE_PDB value can only be set to freepdb")) + } + } + } + } + } + + if len(validationErrs) > 0 { + return validationErrs + } + return nil +} diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index 8a0019dd..51af477b 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -43,9 +43,8 @@ import ( "reflect" "strconv" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - "github.com/go-logr/logr" + databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -198,6 +197,12 @@ func buildVolumeSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraC result = append(result, corev1.Volume{Name: OraCatalogSpex.Name + "orascript-vol5", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}) } + if checkTdeWalletFlag(instance) { + if len(instance.Spec.FssStorageClass) == 0 && len(instance.Spec.TdeWalletPvc) > 0 { + result = append(result, corev1.Volume{Name: OraCatalogSpex.Name + "shared-storage-vol8", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: instance.Spec.TdeWalletPvc}}}) + } + } + return result } @@ -210,7 +215,7 @@ func buildContainerSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, O Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ - Add: []corev1.Capability{"NET_RAW"}, + Add: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("SYS_NICE")}, }, }, Resources: corev1.ResourceRequirements{ @@ -328,6 +333,15 @@ func buildVolumeMountSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "orastage-vol7", MountPath: oraStage}) } + if checkTdeWalletFlag(instance) { + if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { + result = append(result, corev1.VolumeMount{Name: instance.Name + "shared-storage", MountPath: getTdeWalletMountLoc(instance)}) + } else { + if len(instance.Spec.FssStorageClass) == 0 && len(instance.Spec.TdeWalletPvc) > 0 { + result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "shared-storage-vol8", MountPath: getTdeWalletMountLoc(instance)}) + } + } + } return result } @@ -361,15 +375,32 @@ func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, }, } - if len(OraCatalogSpex.PvAnnotations) > 0 { - claims[0].ObjectMeta.Annotations = make(map[string]string) - for key, value := range OraCatalogSpex.PvAnnotations { - claims[0].ObjectMeta.Annotations[key] = value - } - } + if checkTdeWalletFlag(instance) { + if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { + { + pvcClaim := corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: instance.Name + "shared-storage", + Namespace: instance.Spec.Namespace, + OwnerReferences: getOwnerRef(instance), + Labels: buildLabelsForCatalog(instance, "sharding"), + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteMany, + }, + StorageClassName: &instance.Spec.FssStorageClass, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(strconv.FormatInt(int64(OraCatalogSpex.StorageSizeInGb), 10) + "Gi"), + }, + }, + }, + } - if len(OraCatalogSpex.PvMatchLabels) > 0 { - claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} + claims = append(claims, pvcClaim) + } + } } return claims diff --git a/commons/sharding/gsm.go b/commons/sharding/gsm.go index 6ac1414f..bcdc8866 100644 --- a/commons/sharding/gsm.go +++ b/commons/sharding/gsm.go @@ -466,7 +466,7 @@ func UpdateProvForGsm(instance *databasev1alpha1.ShardingDatabase, oraSpexRes := OraGsmSpex.Resources if !reflect.DeepEqual(shardContaineRes, oraSpexRes) { - isUpdate = true + isUpdate = false } } } diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index d5d438b1..8c70d3a0 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -40,6 +40,7 @@ package commons import ( "context" + "encoding/json" "fmt" "slices" @@ -106,6 +107,8 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da var result []corev1.EnvVar var varinfo string var sidFlag bool = false + //var sidValue string + var pdbValue string var pdbFlag bool = false var sDirectParam bool = false var sGroup1Params bool = false @@ -115,13 +118,17 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da var oldSidFlag bool = false var archiveLogFlag bool = false var shardSetupFlag bool = false + var dbUnameFlag bool = false + var ofreePdbFlag bool = false for _, variable := range variables { if variable.Name == "ORACLE_SID" { sidFlag = true + //sidValue = variable.Value } if variable.Name == "ORACLE_PDB" { pdbFlag = true + pdbValue = variable.Value } if variable.Name == "SHARD_DIRECTOR_PARAMS" { sDirectParam = true @@ -144,9 +151,32 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da if variable.Name == "OLD_ORACLE_PDB" { archiveLogFlag = true } + if variable.Name == "DB_UNIQUE_NAME" { + dbUnameFlag = true + } + if variable.Name == "ORACLE_FREE_PDB" { + ofreePdbFlag = true + } + result = append(result, corev1.EnvVar{Name: variable.Name, Value: variable.Value}) } + if !dbUnameFlag { + if strings.ToLower(instance.Spec.DbEdition) == "free" { + result = append(result, corev1.EnvVar{Name: "DB_UNIQUE_NAME", Value: strings.ToUpper(name)}) + } + } + + if !ofreePdbFlag { + if strings.ToLower(instance.Spec.DbEdition) == "free" { + if pdbFlag { + result = append(result, corev1.EnvVar{Name: "ORACLE_FREE_PDB", Value: pdbValue}) + } else { + result = append(result, corev1.EnvVar{Name: "ORACLE_FREE_PDB", Value: strings.ToUpper(name) + "PDB"}) + } + } + } + if !shardSetupFlag { if restype == "SHARD" { result = append(result, corev1.EnvVar{Name: "SHARD_SETUP", Value: "true"}) @@ -167,19 +197,27 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da } } if !sidFlag { - if restype == "SHARD" { - result = append(result, corev1.EnvVar{Name: "ORACLE_SID", Value: strings.ToUpper(name)}) - } - if restype == "CATALOG" { - result = append(result, corev1.EnvVar{Name: "ORACLE_SID", Value: strings.ToUpper(name)}) + if strings.ToLower(instance.Spec.DbEdition) == "free" { + result = append(result, corev1.EnvVar{Name: "ORACLE_SID", Value: "FREE"}) + } else { + if restype == "SHARD" { + result = append(result, corev1.EnvVar{Name: "ORACLE_SID", Value: strings.ToUpper(name)}) + } + if restype == "CATALOG" { + result = append(result, corev1.EnvVar{Name: "ORACLE_SID", Value: strings.ToUpper(name)}) + } } } if !pdbFlag { - if restype == "SHARD" { - result = append(result, corev1.EnvVar{Name: "ORACLE_PDB", Value: strings.ToUpper(name) + "PDB"}) - } - if restype == "CATALOG" { - result = append(result, corev1.EnvVar{Name: "ORACLE_PDB", Value: strings.ToUpper(name) + "PDB"}) + if strings.ToLower(instance.Spec.DbEdition) == "free" { + result = append(result, corev1.EnvVar{Name: "ORACLE_PDB", Value: "FREEPDB"}) + } else { + if restype == "SHARD" { + result = append(result, corev1.EnvVar{Name: "ORACLE_PDB", Value: strings.ToUpper(name) + "PDB"}) + } + if restype == "CATALOG" { + result = append(result, corev1.EnvVar{Name: "ORACLE_PDB", Value: strings.ToUpper(name) + "PDB"}) + } } } // Secret Settings @@ -727,13 +765,26 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { } for _, variable := range variables { - if variable.Name == "ORACLE_SID" { + if variable.Name == "DB_UNIQUE_NAME" { sidFlag = true sidName = variable.Value + } else { + if variable.Name == "ORACLE_SID" { + sidFlag = true + sidName = variable.Value + } } - if variable.Name == "ORACLE_PDB" { - pdbFlag = true - pdbName = variable.Value + if variable.Name == "ORACLE_FREE_PDB" { + if strings.ToLower(instance.Spec.DbEdition) == "free" { + pdbFlag = true + pdbName = variable.Value + } + } + if strings.ToLower(instance.Spec.DbEdition) != "free" { + if variable.Name == "ORACLE_PDB" { + pdbFlag = true + pdbName = variable.Value + } } if variable.Name == "CATALOG_PORT" { portFlag = true @@ -754,16 +805,26 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { varinfo = "catalog_db=" + strings.ToUpper(instance.Spec.Catalog[0].Name) + ";" result = result + varinfo } else { - varinfo = "catalog_db=" + strings.ToUpper(sidName) + ";" - result = result + varinfo + if strings.ToLower(instance.Spec.DbEdition) == "free" { + varinfo = "catalog_db=" + strings.ToUpper(instance.Spec.Catalog[0].Name) + ";" + result = result + varinfo + } else { + varinfo = "catalog_db=" + strings.ToUpper(sidName) + ";" + result = result + varinfo + } } if !pdbFlag { varinfo = "catalog_pdb=" + strings.ToUpper(instance.Spec.Catalog[0].Name) + "PDB" + ";" result = result + varinfo } else { - varinfo = "catalog_pdb=" + strings.ToUpper(pdbName) + ";" - result = result + varinfo + if strings.ToLower(instance.Spec.DbEdition) == "free" { + varinfo = "catalog_pdb=" + strings.ToUpper(instance.Spec.Catalog[0].Name) + "PDB" + ";" + result = result + varinfo + } else { + varinfo = "catalog_pdb=" + strings.ToUpper(pdbName) + ";" + result = result + varinfo + } } if !portFlag { @@ -781,8 +842,13 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { varinfo = "catalog_name=" + strings.ToUpper(cname) + ";" result = result + varinfo } + if chunksFlag { result = result + "catalog_chunks=" + catchunks + ";" + } else { + if strings.ToLower(instance.Spec.DbEdition) == "free" && strings.ToUpper(instance.Spec.ShardingType) != "USER" && strings.ToUpper(instance.Spec.ShardingType) != "NATIVE" { + result = result + "catalog_chunks=12;" + } } result = strings.TrimSuffix(result, ";") return result @@ -840,6 +906,15 @@ func BuildShardParams(instance *databasealphav1.ShardingDatabase, sfSet *appsv1. var result string var varinfo string var isShardPort bool = false + var freePdbFlag bool = false + var freePdbValue string + var pdbFlag bool = false + var pdbValue string + var dbUnameFlag bool = false + var sidFlag bool = false + var dbUname string + var sidName string + //var isShardGrp bool = false //var i int32 //var isShardSpace bool = false @@ -847,14 +922,25 @@ func BuildShardParams(instance *databasealphav1.ShardingDatabase, sfSet *appsv1. result = "shard_host=" + sfSet.Name + "-0" + "." + sfSet.Name + ";" for _, variable := range variables { - if variable.Name == "ORACLE_SID" { - varinfo = "shard_db=" + variable.Value + ";" - result = result + varinfo + if variable.Name == "DB_UNIQUE_NAME" { + dbUnameFlag = true + dbUname = variable.Value + } else { + if variable.Name == "ORACLE_SID" { + sidFlag = true + sidName = variable.Value + } + } + if variable.Name == "ORACLE_FREE_PDB" { + freePdbFlag = true + freePdbValue = variable.Value } + if variable.Name == "ORACLE_PDB" { - varinfo = "shard_pdb=" + variable.Value + ";" - result = result + varinfo + pdbFlag = true + pdbValue = variable.Value } + if variable.Name == "SHARD_PORT" { varinfo = "shard_port=" + variable.Value + ";" result = result + varinfo @@ -862,6 +948,41 @@ func BuildShardParams(instance *databasealphav1.ShardingDatabase, sfSet *appsv1. } } + + if dbUnameFlag { + varinfo = "shard_db=" + dbUname + ";" + result = result + varinfo + } + + if sidFlag && !dbUnameFlag { + if strings.ToLower(instance.Spec.DbEdition) != "free" { + varinfo = "shard_db=" + sidName + ";" + result = result + varinfo + } else { + varinfo = "shard_db=" + sfSet.Name + ";" + result = result + varinfo + } + } + + if !sidFlag && !dbUnameFlag { + if strings.ToLower(instance.Spec.DbEdition) != "free" { + varinfo = "shard_db=" + sfSet.Name + ";" + result = result + varinfo + } + } + + if freePdbFlag { + if strings.ToLower(instance.Spec.DbEdition) == "free" { + varinfo = "shard_pdb=" + freePdbValue + ";" + result = result + varinfo + } + } else { + if pdbFlag { + varinfo = "shard_pdb=" + pdbValue + ";" + result = result + varinfo + } + } + if OraShardSpex.ShardGroup != "" { varinfo = "shard_group=" + OraShardSpex.ShardGroup + ";" result = result + varinfo @@ -1355,6 +1476,28 @@ func SfsetLabelPatch(sfSetFound *appsv1.StatefulSet, sfSetPod *corev1.Pod, insta return nil } +func InstanceShardPatch(obj client.Object, instance *databasealphav1.ShardingDatabase, kClient client.Client, id int32, field string, value string, +) error { + + var err error + instSpec := instance.Spec + instSpec.Shard[id].IsDelete = "failed" + instshardM, _ := json.Marshal(struct { + Spec *databasealphav1.ShardingDatabaseSpec `json:"spec":` + }{ + Spec: &instSpec, + }) + + patch1 := client.RawPatch(types.MergePatchType, instshardM) + err = kClient.Patch(context.TODO(), obj, patch1) + + if err != nil { + return err + } + + return err +} + // Send Notification func SendNotification(title string, body string, instance *databasealphav1.ShardingDatabase, topicId string, rclient ons.NotificationDataPlaneClient, logger logr.Logger, @@ -1377,3 +1520,27 @@ func SendNotification(title string, body string, instance *databasealphav1.Shard func GetSecretMount() string { return oraSecretMount } + +func checkTdeWalletFlag(instance *databasev1alpha1.ShardingDatabase) bool { + if strings.ToLower(instance.Spec.IsTdeWallet) == "enable" { + return true + } + return false +} + +func CheckIsDeleteFlag(delStr string, instance *databasealphav1.ShardingDatabase, logger logr.Logger) bool { + if strings.ToLower(delStr) == "enable" { + return true + } + if strings.ToLower(delStr) == "failed" { + LogMessages("INFO", "manual intervention required", nil, instance, logger) + } + return false +} + +func getTdeWalletMountLoc(instance *databasev1alpha1.ShardingDatabase) string { + if len(instance.Spec.TdeWalletPvcMountLocation) > 0 { + return instance.Spec.TdeWalletPvcMountLocation + } + return "/tdewallet/" + instance.Name +} diff --git a/commons/sharding/shard.go b/commons/sharding/shard.go index 6dd7a6ea..5ef81d4a 100644 --- a/commons/sharding/shard.go +++ b/commons/sharding/shard.go @@ -197,6 +197,13 @@ func buildVolumeSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraSha if instance.Spec.IsDownloadScripts { result = append(result, corev1.Volume{Name: OraShardSpex.Name + "orascript-vol5", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}) } + + if checkTdeWalletFlag(instance) { + if len(instance.Spec.FssStorageClass) == 0 && len(instance.Spec.TdeWalletPvc) > 0 { + result = append(result, corev1.Volume{Name: OraShardSpex.Name + "shared-storage-vol8", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: instance.Spec.TdeWalletPvc}}}) + } + } + return result } @@ -209,7 +216,7 @@ func buildContainerSpecForShard(instance *databasev1alpha1.ShardingDatabase, Ora Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ - Add: []corev1.Capability{"NET_RAW"}, + Add: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("SYS_NICE")}, }, }, Resources: corev1.ResourceRequirements{ @@ -331,6 +338,16 @@ func buildVolumeMountSpecForShard(instance *databasev1alpha1.ShardingDatabase, O result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "orastage-vol7", MountPath: oraStage}) } + if checkTdeWalletFlag(instance) { + if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { + result = append(result, corev1.VolumeMount{Name: instance.Name + "shared-storage" + instance.Spec.Catalog[0].Name + "-0", MountPath: getTdeWalletMountLoc(instance)}) + } else { + if len(instance.Spec.FssStorageClass) == 0 && len(instance.Spec.TdeWalletPvc) > 0 { + result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "shared-storage-vol8", MountPath: getTdeWalletMountLoc(instance)}) + } + } + } + return result } diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 554ed506..641629a0 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -72,7 +72,7 @@ spec: a container image type: string isDelete: - type: boolean + type: string label: type: string name: @@ -148,6 +148,8 @@ spec: - name type: object type: array + dbEdition: + type: string dbImage: type: string dbImagePullSecret: @@ -177,6 +179,8 @@ spec: - name - pwdFileName type: object + fssStorageClass: + type: string gsm: items: description: GsmSpec defines the desired state of GsmSpec @@ -205,7 +209,7 @@ spec: a container image type: string isDelete: - type: boolean + type: string label: type: string name: @@ -395,7 +399,7 @@ spec: isExternalSvc: type: boolean isTdeWallet: - type: boolean + type: string liveinessCheckPeriod: type: integer namespace: @@ -454,7 +458,12 @@ spec: a container image type: string isDelete: - type: boolean + enum: + - enable + - disable + - failed + - force + type: string label: type: string name: @@ -550,6 +559,10 @@ spec: type: string storageClass: type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string required: - catalog - dbImage diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 3bf0eab5..fad43bc6 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -18,4 +18,5 @@ resources: - dbcs/database_v1alpha1_dbcssystem.yaml - database_v1alpha1_dataguardbroker.yaml - observability/databaseobserver.yaml +- database_v1alpha1_shardingdatabase.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 175abd47..c150867f 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -130,6 +130,26 @@ webhooks: resources: - pdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: mshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -343,6 +363,27 @@ webhooks: resources: - pdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: vshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 88823afe..205fc7fe 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -166,6 +166,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req r.osh = append(r.osh, osh) } defer r.setCrdLifeCycleState(instance, &result, &err, &stateType) + defer r.updateShardTopologyStatus(instance) // =============================== Check Deletion TimeStamp======== // Check if the ProvOShard instance is marked to be deleted, which is // // indicated by the deletion timestamp being set. @@ -286,7 +287,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // if user set replicasize greater than 1 but also set instance.Spec.OraDbPvcName then only one service will be created and one pod for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] - if OraShardSpex.IsDelete != true { + if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { result, err = r.createService(instance, shardingv1.BuildServiceDefForShard(instance, 0, OraShardSpex, "local")) if err != nil { result = resultNq @@ -306,7 +307,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req if len(instance.Spec.Shard) > 0 { for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] - if OraShardSpex.IsDelete != true { + if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { result, err = r.deployStatefulSet(instance, shardingv1.BuildStatefulSetForShard(instance, OraShardSpex), "SHARD") if err != nil { result = resultNq @@ -327,6 +328,13 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req return result, err } + err = r.checkShardState(instance) + if err != nil { + err = nilErr + result = resultQ + return result, err + } + //set the Waiting state for Reconcile loop // Loop will be requeued only if Shard Statefulset is not ready or not configured. // Till that time Reconcilation loop will remain in blocked state @@ -380,7 +388,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // ====================== Update Setup for Shard ============================== for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] - if OraShardSpex.IsDelete != true { + if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { sfSet, shardPod, err := r.validateShard(instance, OraShardSpex, int(i)) if err != nil { shardingv1.LogMessages("INFO", "Shard "+sfSet.Name+" is not in available state.", nil, instance, r.Log) @@ -413,18 +421,6 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req } } - // Calling updateShardTopology to update the entire sharding topology - // This is required because we just executed updateShard,updateCatalog and UpdateGsm - // If some state has changed it will update the topology - - err = r.updateShardTopologyStatus(instance) - if err != nil { - // time.Sleep(30 * time.Second) - result = resultQ - err = nilErr - return result, err - } - stateType = string(databasev1alpha1.CrdReconcileCompeleteState) // r.setCrdLifeCycleState(instance, &result, &err, stateType) // Set error to ni to avoid reconcilation state reconcilation error as we are passing err to setCrdLifeCycleState @@ -889,7 +885,7 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.Sha if len(instance.Spec.Shard) > 0 { for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex := instance.Spec.Shard[i] - if OraShardSpex.IsDelete != true { + if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { err = r.checkShardSpace(instance, OraShardSpex) if err != nil { return err @@ -902,32 +898,6 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.Sha } } - // Check Secret configuration - if instance.Spec.DbSecret == nil { - return fmt.Errorf("Secret specification cannot be null, you need to set secret details") - } else { - if len(instance.Spec.DbSecret.Name) == 0 { - return fmt.Errorf("instance.Spec.DbSecret.Name cannot be empty") - } - if len(instance.Spec.DbSecret.PwdFileName) == 0 { - return fmt.Errorf("instance.Spec.DbSecret.PwdFileName cannot be empty") - } - if strings.ToLower(instance.Spec.DbSecret.EncryptionType) != "base64" { - if strings.ToLower(instance.Spec.DbSecret.KeyFileName) == "" { - return fmt.Errorf("instance.Spec.DbSecret.KeyFileName cannot be empty") - } - } - if len(instance.Spec.DbSecret.PwdFileMountLocation) == 0 { - msg := "instance.Spec.DbSecret.PwdFileMountLocation is not set. Setting it to default " + shardingv1.GetSecretMount() - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - } - - if len(instance.Spec.DbSecret.KeyFileMountLocation) == 0 { - msg := "instance.Spec.DbSecret.KeyFileMountLocation is not set. Setting it to default " + shardingv1.GetSecretMount() - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - } - } - // Once the initial Spec is been validated then update the last Sucessful Spec err = instance.UpdateLastSuccessfulSpec(r.Client) if err != nil { @@ -1350,23 +1320,21 @@ func (r *ShardingDatabaseReconciler) validateShard(instance *databasev1alpha1.Sh } // This function updates the shard topology over all -func (r *ShardingDatabaseReconciler) updateShardTopologyStatus(instance *databasev1alpha1.ShardingDatabase) error { +func (r *ShardingDatabaseReconciler) updateShardTopologyStatus(instance *databasev1alpha1.ShardingDatabase) { //shardPod := &corev1.Pod{} //gsmSfSet := &appsv1.StatefulSet{} gsmPod := &corev1.Pod{} var err error _, _, err = r.validateCatalog(instance) if err != nil { - return err + } _, gsmPod, err = r.validateGsm(instance) if err != nil { - return err + } r.updateShardTopologyShardsInGsm(instance, gsmPod) - return nil - } func (r *ShardingDatabaseReconciler) updateShardTopologyShardsInGsm(instance *databasev1alpha1.ShardingDatabase, gsmPod *corev1.Pod) { @@ -1379,7 +1347,7 @@ func (r *ShardingDatabaseReconciler) updateShardTopologyShardsInGsm(instance *da for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex := instance.Spec.Shard[i] // stateStr := shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) - if OraShardSpex.IsDelete != true { + if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { shardSfSet, _, err = r.validateShard(instance, OraShardSpex, int(i)) if err != nil { continue @@ -1532,7 +1500,7 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 // stateStr := shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) // strings.Contains(stateStr, "DELETE") - if OraShardSpex.IsDelete != true { + if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { if setLifeCycleFlag != true { setLifeCycleFlag = true stateType := string(databasev1alpha1.CrdReconcileWaitingState) @@ -1561,33 +1529,36 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 continue } + /** // Copy file from pod to FS - // configrest, kclientset, err := shardingv1.GetPodCopyConfig(r.kubeClient, r.kubeConfig, instance, r.Log) - // if err != nil { - // return fmt.Errorf("Error occurred in getting KubeConfig, cannot perform copy operation from the pod") - // } - - // _, _, err = shardingv1.ExecCommand(gsmPod.Name, shardingv1.GetTdeKeyLocCmd(), r.kubeClient, r.kubeConfig, instance, r.Log) - // if err != nil { - // fmt.Printf("Error occurred during the while getting the TDE key from the pod " + gsmPod.Name) - // //return err - // } - // fileName := "/tmp/tde_key" - // last := fileName[strings.LastIndex(fileName, "/")+1:] - // fileName1 := last - // fsLoc := shardingv1.TmpLoc + "/" + fileName1 - // _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fmt.Sprintf("%s/%s:/%s", instance.Spec.Namespace, gsmPod.Name, fileName), fsLoc, "") - // if err != nil { - // fmt.Printf("failed to copy file") - // //return err - // } + configrest, kclientset, err := shardingv1.GetPodCopyConfig(r.kubeClient, r.kubeConfig, instance, r.Log) + if err != nil { + return fmt.Errorf("Error occurred in getting KubeConfig, cannot perform copy operation from the pod") + } + + _, _, err = shardingv1.ExecCommand(gsmPod.Name, shardingv1.GetTdeKeyLocCmd(), r.kubeClient, r.kubeConfig, instance, r.Log) + if err != nil { + fmt.Printf("Error occurred during the while getting the TDE key from the pod " + gsmPod.Name) + //return err + } + fileName := "/tmp/tde_key" + last := fileName[strings.LastIndex(fileName, "/")+1:] + fileName1 := last + fsLoc := shardingv1.TmpLoc + "/" + fileName1 + _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fmt.Sprintf("%s/%s:/%s", instance.Spec.Namespace, gsmPod.Name, fileName), fsLoc, "") + if err != nil { + fmt.Printf("failed to copy file") + //return err + } // Copying it to Shard Pod - // _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fsLoc, fmt.Sprintf("%s/%s:/%s", instance.Spec.Namespace, OraShardSpex.Name+"-0", fsLoc), "") - // if err != nil { - // fmt.Printf("failed to copy file") - // //return err - /// } + _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fsLoc, fmt.Sprintf("%s/%s:/%s", instance.Spec.Namespace, OraShardSpex.Name+"-0", fsLoc), "") + if err != nil { + fmt.Printf("failed to copy file") + //return err + } + + **/ // If the shard doesn't exist in GSM then just add the shard statefulset and update GSM shard status // ADD Shard in GSM @@ -1680,7 +1651,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar if len(instance.Spec.Shard) > 0 { for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex := instance.Spec.Shard[i] - if OraShardSpex.IsDelete == true { + if shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { if setLifeCycleFlag != true { setLifeCycleFlag = true stateType := string(databasev1alpha1.CrdReconcileWaitingState) @@ -1736,6 +1707,13 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar title = "Chunk Movement Failure" message = "Error occurred during chunk movement in shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " deletion." r.sendMessage(instance, title, message) + instance.Spec.Shard[i].IsDelete = "failed" + err = shardingv1.InstanceShardPatch(instance, instance, r.Client, i, "isDelete", "failed") + if err != nil { + msg = "Error occurred while changing the isDelete value to failed in Spec struct" + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + return err + } continue } // 6th Step @@ -1743,13 +1721,22 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar // This is a loop and will check unless there is a error or chunks has moved // Validate if the chunks has moved before performing shard deletion for { + msg = "Sleeping for 120 seconds and will check status again of chunks movement in gsm for shard: " + shardingv1.GetFmtStr(OraShardSpex.Name) + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + time.Sleep(120 * time.Second) err = shardingv1.VerifyChunks(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err == nil { break } else { - msg = "Sleeping for 120 seconds and will check status again of chunks movement in gsm for shard: " + shardingv1.GetFmtStr(OraShardSpex.Name) - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - time.Sleep(120 * time.Second) + instance.Spec.Shard[i].IsDelete = "failed" + err = shardingv1.InstanceShardPatch(instance, instance, r.Client, i, "isDelete", "failed") + if err != nil { + // r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.ChunkMoveError)) + msg = "Error occurred while changing the isDelete value to failed in Spec struct" + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + // return err + } + return err } } } @@ -1764,6 +1751,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar msg = "Error occurred during shard" + shardingv1.GetFmtStr(OraShardSpex.Name) + "removal from Gsm" shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateShardStatus(instance, int(i), string(databasev1alpha1.ShardRemoveError)) + instance.Spec.Shard[i].IsDelete = "failed" continue } @@ -1974,3 +1962,52 @@ func (r *ShardingDatabaseReconciler) deployStatefulSet(instance *databasev1alpha return ctrl.Result{}, nil } + +func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1.ShardingDatabase) error { + + var i int32 + var err error = nil + var OraShardSpex databasev1alpha1.ShardSpec + var currState string + var eventMsg string + + currState = "" + eventMsg = "" + if len(instance.Status.Gsm.Shards) > 0 { + for i = 0; i < int32(len(instance.Spec.Shard)); i++ { + OraShardSpex = instance.Spec.Shard[i] + currState = shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) + if currState == string(databasev1alpha1.AddingShardState) { + eventMsg = "Shard Addition in progress. Requeuing" + err = fmt.Errorf(eventMsg) + break + // } else if currState == string(databasev1alpha1.AddingShardErrorState) { + // eventMsg = "Shard Addition Error. Manual intervention required. Requeuing" + // err = fmt.Errorf(eventMsg) + // break + } else if currState == string(databasev1alpha1.DeletingState) { + eventMsg = "Shard Deletion in progress. Requeuing" + err = fmt.Errorf(eventMsg) + break + } else if OraShardSpex.IsDelete == "failed" { + eventMsg = "Shard Deletion failed. Manual intervention required. Requeuing" + err = fmt.Errorf(eventMsg) + break + } else if currState == string(databasev1alpha1.DeleteErrorState) { + eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" + err = fmt.Errorf(eventMsg) + break + } else if currState == string(databasev1alpha1.ShardRemoveError) { + eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" + err = fmt.Errorf(eventMsg) + break + } else { + eventMsg = "checkShardState() : Shard State=[" + currState + "]" + err = nil + } + } + r.publishEvents(instance, eventMsg, currState) + + } + return err +} diff --git a/docs/sharding/README.md b/docs/sharding/README.md index 3d3320ee..0c817467 100644 --- a/docs/sharding/README.md +++ b/docs/sharding/README.md @@ -22,18 +22,21 @@ The Sharding Database controller in Oracle Database Operator deploys Oracle Shar The Oracle Sharding database controller provides end-to-end automation of Oracle Database sharding topology deployment in Kubernetes clusters. -## Using Oracle Sharding Database Operator +## Using Oracle Database Operator Sharding Controller -To create a Sharding Topology, complete the steps in the following sections below: +Following sections provide the details for deploying Oracle Globally Distributed Database (Oracle Sharded Database) using Oracle Database Operator Sharding Controller with different use cases: -1. [Prerequisites for running Oracle Sharding Database Controller](#prerequisites-for-running-oracle-sharding-database-controller) -2. [Provisioning Sharding Topology in a Cloud based Kubernetes Cluster (OKE in this case)](#provisioning-sharding-topology-in-a-cloud-based-kubernetes-cluster-oke-in-this-case) -3. [Connecting to Shard Databases](#connecting-to-shard-databases) -4. [Debugging and Troubleshooting](#debugging-and-troubleshooting) +* [Prerequisites for running Oracle Sharding Database Controller](#prerequisites-for-running-oracle-sharding-database-controller) +* [Oracle Database 23ai Free](#oracle-database-23ai-free) +* [Provisioning Sharding Topology with System-Managed Sharding in a Cloud-Based Kubernetes Cluster](#provisioning-sharding-topology-with-system-managed-sharding-in-a-cloud-based-kubernetes-cluster) +* [Provisioning Sharding Topology with User Defined Sharding in a Cloud-Based Kubernetes Cluster](#provisioning-sharding-topology-with-user-defined-sharding-in-a-cloud-based-kubernetes-cluster) +* [Provisioning System-Managed Sharding Topology with Raft replication enabled in a Cloud-Based Kubernetes Cluster](#provisioning-system-managed-sharding-topology-with-raft-replication-enabled-in-a-cloud-based-kubernetes-cluster) +* [Connecting to Shard Databases](#connecting-to-shard-databases) +* [Debugging and Troubleshooting](#debugging-and-troubleshooting) **Note** Before proceeding to the next section, you must complete the instructions given in each section, based on your enviornment, before proceeding to next section. -## Prerequisites for Running Oracle Sharding Database Controller +## Prerequisites for running Oracle Sharding Database Controller **IMPORTANT:** You must make the changes specified in this section before you proceed to the next section. @@ -85,6 +88,8 @@ You can either download the images and push them to your Docker Images Repositor **Note**: In the sharding example yaml files, we are using GDS and database images available on [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::). +**Note:** In case you want to use the `Oracle Database 23ai Free` Image for Database and GSM, refer to section [Oracle Database 23ai Free](#oracle-database-23ai-free) for more details. + ### 4. Create a namespace for the Oracle DB Sharding Setup Create a Kubernetes namespace named `shns`. All the resources belonging to the Oracle Database Sharding Setup will be provisioned in this namespace named `shns`. For example: @@ -101,7 +106,7 @@ You can either download the images and push them to your Docker Images Repositor Create a Kubernetes secret named `db-user-pass-rsa` using these steps: [Create Kubernetes Secret](./provisioning/create_kubernetes_secret_for_db_user.md) -After you have the above prerequsites completed, you can proceed to the next section for your environment to provision the Oracle Database Sharding Topology. +After you have the above prerequisites completed, you can proceed to the next section for your environment to provision the Oracle Database Sharding Topology. ### 6. Provisioning a Persistent Volume having an Oracle Database Gold Image @@ -111,19 +116,35 @@ In case of an `OCI OKE` cluster, you can use this Persistent Volume during provi You can refer [here](./provisioning/provisioning_persistent_volume_having_db_gold_image.md) for the steps involved. -## Provisioning Sharding Topology with System Sharding in a Cloud-Based Kubernetes Cluster +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. So, this step will not be needed if you are deploying Oracle Sharded Database using Oracle 23ai Free Database and GSM Images. + +## Oracle Database 23ai Free + +Please refer to [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) documentation for more details. + +If you want to use Oracle Database 23ai Free Image for Database and GSM for deployment of the Sharded Database using Sharding Controller in Oracle Database Kubernetes Operator, you need to consider the below points: + +* To deploy using the FREE Database and GSM Image, you will need to add the additional parameter `dbEdition: "free"` to the .yaml file. +* Refer to [Sample Sharded Database Deployment using Oracle 23ai FREE Database and GSM Images](./provisioning/free/sharding_provisioning_with_free_images.md) for an example. +* For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. +* Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. +* Total number of chunks for FREE Database defaults to `12` if `CATALOG_CHUNKS` parameter is not specified. This default value is determined considering limitation of 12 GB of user data on disk for oracle free database. + + +## Provisioning Sharding Topology with System-Managed Sharding in a Cloud-Based Kubernetes Cluster -Deploy Oracle Database Sharding Topology with `System Sharding` on your Cloud based Kubernetes cluster. +Deploy Oracle Database Sharding Topology with `System-Managed Sharding` on your Cloud based Kubernetes cluster. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database Sharding Topology covered by below examples: -[1. Provisioning Oracle Sharded Database with System Sharding without Database Gold Image](./provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md) -[2. Provisioning Oracle Sharded Database with System Sharding with additional control on resources like Memory and CPU allocated to Pods](./provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md) -[3. Provisioning Oracle Sharded Database with System Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) -[4. Provisioning Oracle Sharded Database with System Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) -[5. Provisioning Oracle Sharded Database with System Sharding and send Notification using OCI Notification Service](./provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md) -[6. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System Sharding](./provisioning/system_sharding/ssharding_scale_out_add_shards.md) -[7. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System Sharding](./provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md) +[1. Provisioning Oracle Sharded Database with System-Managed Sharding without Database Gold Image](./provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md) +[2. Provisioning Oracle Sharded Database with System-Managed Sharding with number of chunks specified](./provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md) +[3. Provisioning Oracle Sharded Database with System-Managed Sharding with additional control on resources like Memory and CPU allocated to Pods](./provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md) +[4. Provisioning Oracle Sharded Database with System-Managed Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) +[5. Provisioning Oracle Sharded Database with System-Managed Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) +[6. Provisioning Oracle Sharded Database with System-Managed Sharding and send Notification using OCI Notification Service](./provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md) +[7. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding](./provisioning/system_sharding/ssharding_scale_out_add_shards.md) +[8. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding](./provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md) ## Provisioning Sharding Topology with User Defined Sharding in a Cloud-Based Kubernetes Cluster @@ -140,6 +161,24 @@ In this example, the deployment uses the YAML file based on `OCI OKE` cluster. T [6. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md) [7. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md) + +## Provisioning System-Managed Sharding Topology with Raft replication enabled in a Cloud-Based Kubernetes Cluster + +Deploy Oracle Database Sharding Topology with `System-Managed Sharding with SNR RAFT enabled` on your Cloud based Kubernetes cluster. + +**NOTE: SNR RAFT Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** + +In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database Sharding Topology covered by below examples: + +[1. Provisioning System-Managed Sharding Topology with Raft replication enabled without Database Gold Image](./provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md) +[2. Provisioning System-Managed Sharding Topology with Raft replication enabled with number of chunks specified](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md) +[3. Provisioning System-Managed Sharding Topology with Raft replication enabled with additional control on resources like Memory and CPU allocated to Pods](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md) +[4. Provisioning System-Managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) +[5. Provisioning System-Managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) +[6. Provisioning System-Managed Sharding Topology with Raft replication enabled and send Notification using OCI Notification Service](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md) +[7. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT replication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md) +[8. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT reolication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md) + ## Connecting to Shard Databases After the Oracle Database Sharding Topology has been provisioned using the Sharding Controller in Oracle Database Kubernetes Operator, you can follow the steps in this document to connect to the Sharded Database or to the individual Shards: [Database Connectivity](./provisioning/database_connection.md) diff --git a/docs/sharding/provisioning/debugging.md b/docs/sharding/provisioning/debugging.md index 545bf034..63e02b6a 100644 --- a/docs/sharding/provisioning/debugging.md +++ b/docs/sharding/provisioning/debugging.md @@ -41,3 +41,10 @@ kubectl exec -it catalog-0 -n shns /bin/bash ``` Now, you can troubleshooting the corresponding component using the alert log or the trace files etc just like a normal Sharding Database Deployment. Please refer to [Oracle Database Sharding Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/19/shard/sharding-troubleshooting.html#GUID-629262E5-7910-4690-A726-A565C59BA73E) for this purpose. + + +## Debugging using Database Events + +* You can enable database events as part of the Sharded Database Deployment +* This can be enabled using the `envVars` +* One example of enabling Database Events is [sharding_provisioning_with_db_events.md](./debugging/sharding_provisioning_with_db_events.md) \ No newline at end of file diff --git a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.md b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.md new file mode 100644 index 00000000..fa73920f --- /dev/null +++ b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.md @@ -0,0 +1,40 @@ +# Example of provisioning Oracle Sharded Database along with DB Events set at Database Level + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This example sets a Database Event at the Database Level for Catalog and Shard Databases. + +The sharded database in this example is deployed with System-Managed Sharding type. In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System-Managed Sharding is deployed using Oracle Sharding controller. + +**NOTE:** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. + +This example uses `sharding_provisioning_with_db_events.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Event: `10798 trace name context forever, level 7` set along with `GWM_TRACE level 263` + + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `sharding_provisioning_with_db_events.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + + +Use the file: [sharding_provisioning_with_db_events.yaml](./sharding_provisioning_with_db_events.yaml) for this use case as below: + +1. Deploy the `sharding_provisioning_with_db_events.yaml` file: + ```sh + kubectl apply -f sharding_provisioning_with_db_events.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` +3. You can confirm the Database event and the tracing enabled in the RDBMS alert log file of the Database. \ No newline at end of file diff --git a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml new file mode 100644 index 00000000..7d136d58 --- /dev/null +++ b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml @@ -0,0 +1,69 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + envVars: + - name: "DB_EVENTS" + value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + envVars: + - name: "DB_EVENTS" + value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + envVars: + - name: "DB_EVENTS" + value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + envVars: + - name: "DB_EVENTS" + value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns diff --git a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.md b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.md new file mode 100644 index 00000000..61641312 --- /dev/null +++ b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.md @@ -0,0 +1,40 @@ +# Example of provisioning Oracle Sharded Database with Oracle 23ai FREE Database and GSM Images + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This example uses the Oracle 23ai FREE Database and GSM Images. + +The sharded database in this example is deployed with System-Managed Sharding type. In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System-Managed Sharding is deployed using Oracle Sharding controller. + +**NOTE:** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. + +This example uses `sharding_provisioning_with_free_images.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` + + +To get the Oracle 23ai FREE Database and GSM Images: + * The Oracle 23ai FREE RDBMS Image used is `container-registry.oracle.com/database/free:latest`. Check [Oracle Database Free Get Started](https://www.oracle.com/database/free/get-started/?source=v0-DBFree-ChatCTA-j2032-20240709) for details. + * To pull the above image from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * Use the Oracle 23ai FREE GSM Binaries `LINUX.X64_234000_gsm.zip` as listed on page [Oracle Database Free Get Started](https://www.oracle.com/database/free/get-started/?source=v0-DBFree-ChatCTA-j2032-20240709) and prepare the GSM Container Image following [Oracle Global Data Services Image](https://github.com/oracle/db-sharding/tree/master/docker-based-sharding-deployment/dockerfiles) + * You need to change `dbImage` and `gsmImage` tag with the images you want to use in your enviornment in file `sharding_provisioning_with_free_images.yaml`. + + + +Use the file: [sharding_provisioning_with_free_images.yaml](./sharding_provisioning_with_free_images.yaml) for this use case as below: + +1. Deploy the `sharding_provisioning_with_free_images.yaml` file: + ```sh + kubectl apply -f sharding_provisioning_with_free_images.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` \ No newline at end of file diff --git a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml new file mode 100644 index 00000000..7e39b3b2 --- /dev/null +++ b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml @@ -0,0 +1,58 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/free:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: + gsmImagePullSecret: + dbEdition: "free" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md new file mode 100644 index 00000000..ba72be25 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -0,0 +1,57 @@ +# Provisioning System managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image across Availability Domains(ADs) + +**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this test case, you provision the System managed Sharding Topology with Raft replication enabled while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. + +This use case applies when you want to provision the database Pods on a Kubernetes Node in any availability domain (AD), which can also be different from the availability domain (AD) of the Block Volume that has the Oracle Database Gold Image provisioned earlier. + +Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup across ADs. + +NOTE: + +* Cloning from Block Volume Backup in OCI enables the new Persistent Volumes to be created in other ADs. +* To specify the AD where you want to provision the database Pod, use the tag `nodeSelector` and the POD will be provisioned in a node running in that AD. +* To specify GSM containers, you can also use the tag `nodeSelector` to specify the AD. +* Before you can provision with the Gold Image, you need the OCID of the Persistent Volume that has the Oracle Database Gold Image. + +1. Check the OCID of the Persistent Volume provisioned for the Oracle Database Gold Image: + ```sh + kubectl get pv -n shns + ``` +2. Create a Block Volume Backup for this Block Volume, and use the OCID of the Block Volume Backup in the next step. This example uses `snr_ssharding_shard_prov_clone_across_ads.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume which had the Gold Image. +* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` +* `RAFT Replication` enabled + +NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned across multiple Availability Domains by cloning the database. + +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone_across_ads.yaml`. + * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +Use the file: [snr_ssharding_shard_prov_clone_across_ads.yaml](./snr_ssharding_shard_prov_clone_across_ads.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_clone_across_ads.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_clone_across_ads.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md new file mode 100644 index 00000000..cf4240f7 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -0,0 +1,53 @@ +# Provisioning System managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD) + +**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this test case, you provision the System managed Sharding Topology with Raft replication enabled while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. + +This use case applies when you are cloning from a Block Volume, and you can clone _only_ in the same availability domain (AD). The result is that the cloned shard database PODs can be created _only_ in the same AD where the Gold Image Block Volume is present. + +Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup. + +**NOTE** For this step, the Persistent Volume that has the Oracle Database Gold Image is identified using its OCID. + +1. Check the OCID of the Persistent Volume provisioned earlier using below command: + + ```sh + kubectl get pv -n shns + ``` + +2. This example uses `snr_ssharding_shard_prov_clone.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Cloning from the Database Gold Image present in Persistent Volume having OCID: `ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq` +* `RAFT Replication` enabled + +NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned in the same Availability Domain `PHX-AD-1` by cloning the database. + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone.yaml`. + * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. + +Use the file: [snr_ssharding_shard_prov_clone.yaml](./snr_ssharding_shard_prov_clone.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_clone.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_clone.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md new file mode 100644 index 00000000..44972090 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md @@ -0,0 +1,43 @@ +# Provisioning System-Managed Sharding Topology with Raft replication enabled with number of chunks specified + +**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System-Managed with RAFT Replication enabled is deployed using Oracle Sharding controller. + +**NOTE** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. + +By default, the System-Managed with RAFT Replication deploys the Sharded Database with 360 chunks per Shard Database (because there are 3 chunks created for each replication unit). In this example, the Sharded Database will be deployed with non-default number of chunks specified using parameter `CATALOG_CHUNKS`. + +This example uses `snr_ssharding_shard_prov_chunks.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Total number of chunks as `120` specified by variable `CATALOG_CHUNKS` (it will be 120 chunks per shard) +* Namespace: `shns` +* `RAFT Replication` enabled + + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + + +Use the file: [snr_ssharding_shard_prov_chunks.yaml](./snr_ssharding_shard_prov_chunks.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_chunks.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_chunks.yaml + ``` +1. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md new file mode 100644 index 00000000..9cfd6afb --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md @@ -0,0 +1,47 @@ +# Provisioning System-Managed Sharding Topology with Raft replication enabled with additional control on resources like Memory and CPU allocated to Pods + +**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this use case, there are additional tags used to control resources such as CPU and Memory used by the different Pods when the Oracle Sharding topology with System-Managed with RAFT Replication is deployed using Oracle Sharding controller. + +This example uses `snr_ssharding_shard_prov_memory_cpu.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Tags `memory` and `cpu` to control the Memory and CPU of the PODs +* Additional tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level +* `RAFT Replication` enabled + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_memory_cpu.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + +**NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. + +Use the YAML file [snr_ssharding_shard_prov_memory_cpu.yaml](./snr_ssharding_shard_prov_memory_cpu.yaml). + +1. Deploy the `snr_ssharding_shard_prov_memory_cpu.yaml` file: + + ```sh + kubectl apply -f snr_ssharding_shard_prov_memory_cpu.yaml + ``` + +1. Check the details of a POD. For example: To check the details of Pod `shard1-0`: + + ```sh + kubectl describe pod/shard1-0 -n shns + ``` +3. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md new file mode 100644 index 00000000..d4cb11de --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md @@ -0,0 +1,87 @@ +# Provisioning System managed Sharding Topology with Raft replication enabled and send Notification using OCI Notification Service + +**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates how to use a notification service like OCI Notification service to send an email notification when a particular operation is completed on an Oracle Database sharding topology provisioned using the Oracle Database sharding controller. + +This example uses `snr_ssharding_shard_prov_send_notification.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume that has the Database Gold Image created earlier. +* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` +* Configmap to send notification email when a particular operation is completed. For example: When a shard is added. +* `RAFT Replication` enabled + +**NOTE:** + +* The notification will be sent using a configmap created with the credentials of the OCI user account in this use case. + +We will create a topic in Notification Service of the OCI Console and use its OCID. + +To do this: + +1. Create a `configmap_data.txt` file, such as the following, which has the OCI User details that will be used to send notfication: + + ```sh + user=ocid1.user.oc1........fx7omxfq + fingerprint=fa:18:98:...............:8a + tenancy=ocid1.tenancy.oc1..aaaa.......orpn7inq + region=us-phoenix-1 + topicid=ocid1.onstopic.oc1.phx.aaa............6xrq + ``` +2. Create a configmap using the below command using the file created above: + ```sh + kubectl create configmap onsconfigmap --from-file=./configmap_data.txt -n shns + ``` + +3. Create a key file `priavatekey` having the PEM key of the OCI user being used to send notification: + ```sh + -----BEGIN PRIVATE KEY-G---- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXYxA0DJvEwtVR + +o4OxrunL3L2NZJRADTFR+TDHqrNF1JwbaFBizSdL+EXbxQW1faZs5lXZ/sVmQF9 + . + . + . + zn/xWC0FzXGRzfvYHhq8XT3omf6L47KqIzqo3jDKdgvVq4u+lb+fXJlhj6Rwi99y + QEp36HnZiUxAQnR331DacN+YSTE+vpzSwZ38OP49khAB1xQsbiv1adG7CbNpkxpI + nS7CkDLg4Hcs4b9bGLHYJVY= + -----END PRIVATE KEY----- + ``` +4. Use the key file `privatekey` to create a Kubernetes secret in namespace `shns`: + + ```sh + kubectl create secret generic my-secret --from-file=./privatekey -n shns + ``` + +5. Use this command to check details of the secret that you created: + + ```sh + kubectl describe secret my-secret -n shns + ``` + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_send_notification.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. + +Use the file: [snr_ssharding_shard_prov_send_notification.yaml](./snr_ssharding_shard_prov_send_notification.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_send_notification.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_send_notification.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md new file mode 100644 index 00000000..892741a5 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md @@ -0,0 +1,40 @@ +# Provisioning System-Managed Sharding Topology with Raft replication enabled without Database Gold Image + +**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System-Managed with RAFT Replication enabled is deployed using Oracle Sharding controller. + +**NOTE** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. + +This example uses `snr_ssharding_shard_prov.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* `RAFT Replication` enabled + + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + + +Use the file: [snr_ssharding_shard_prov.yaml](./snr_ssharding_shard_prov.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov.yaml + ``` +1. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md new file mode 100644 index 00000000..fe3157ec --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md @@ -0,0 +1,50 @@ +# Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT reolication enabled + +**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with System-Managed with RAFT Replication enabled provisioned using Oracle Database Sharding controller. + +**NOTE** The deletion of a shard is done after verifying the Chunks have been moved out of that shard. + +In this use case, the existing database Sharding is having: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Five sharding Pods: `shard1`,`shard2`,`shard3`,`shard4` and `shard5` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* `RAFT Replication` enabled + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_delshard.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + +NOTE: Use tag `isDelete: enable` to delete the shard you want. + +This use case deletes the shard `shard4` from the above Sharding Topology. + +Use the file: [snr_ssharding_shard_prov_delshard.yaml](./snr_ssharding_shard_prov_delshard.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_delshard.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_delshard.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + +**NOTE:** After you apply `snr_ssharding_shard_prov_delshard.yaml`, the change may not be visible immediately. When the shard is removed, first the chunks will be moved out of that shard that is going to be deleted. + +To monitor the chunk movement, use the following command: + +```sh +# Switch to the primary GSM Container: +kubectl exec -i -t gsm1-0 -n shns /bin/bash + +# Check the status of the chunks and repeat to observe the chunk movement: +gdsctl config chunks +``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md new file mode 100644 index 00000000..03423e72 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md @@ -0,0 +1,37 @@ +# Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT replication enabled + +**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with System-Managed with RAFT Replication enabled provisioned earlier using Oracle Database Sharding controller. + +In this use case, the existing Oracle Database sharding topology is having: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Namespace: `shns` +* `RAFT Replication` enabled + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_extshard.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. + +This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. + +Use the file: [snr_ssharding_shard_prov_extshard.yaml](./snr_ssharding_shard_prov_extshard.yaml) for this use case as below: + +1. Deploy the `snr_ssharding_shard_prov_extshard.yaml` file: + ```sh + kubectl apply -f snr_ssharding_shard_prov_extshard.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard4-0": + kubectl logs -f pod/shard4-0 -n shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml new file mode 100644 index 00000000..efe3abec --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml @@ -0,0 +1,58 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml new file mode 100644 index 00000000..a79eafdc --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml @@ -0,0 +1,61 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + envVars: + - name: "CATALOG_CHUNKS" + value: "120" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml new file mode 100644 index 00000000..218fda0a --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml @@ -0,0 +1,83 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml new file mode 100644 index 00000000..4eb3954a --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml @@ -0,0 +1,91 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml new file mode 100644 index 00000000..145ef616 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml @@ -0,0 +1,69 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard4 + isDelete: enable + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard5 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml new file mode 100644 index 00000000..ea0c05a5 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml @@ -0,0 +1,68 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard4 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard5 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml new file mode 100644 index 00000000..5c15c724 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml @@ -0,0 +1,89 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + envVars: + - name: "INIT_SGA_SIZE" + value: "600" + - name: "INIT_PGA_SIZE" + value: "400" + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + resources: + requests: + memory: "1000Mi" + cpu: "1000m" + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml new file mode 100644 index 00000000..50c85443 --- /dev/null +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml @@ -0,0 +1,85 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" + pvAnnotations: + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + replicationType: "native" + isExternalSvc: False + isDeleteOraPvc: True + isClone: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + nsConfigMap: onsconfigmap + nsSecret: my-secret + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md index 9ae05d50..64e2f4eb 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -1,8 +1,8 @@ -# Provisioning Oracle Sharded Database with System Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs) +# Provisioning Oracle Sharded Database with System-Managed Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs) **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. -In this test case, you provision the Oracle Database sharding topology with System Sharding while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. +In this test case, you provision the Oracle Database sharding topology with System-Managed Sharding while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. This use case applies when you want to provision the database Pods on a Kubernetes Node in any availability domain (AD), which can also be different from the availability domain (AD) of the Block Volume that has the Oracle Database Gold Image provisioned earlier. @@ -30,6 +30,9 @@ NOTE: NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned across multiple Availability Domains by cloning the database. + +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. + In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_clone_across_ads.yaml`. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md index cb67addb..f7aef949 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -1,4 +1,4 @@ -# Provisioning Oracle Sharded Database with System Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD) +# Provisioning Oracle Sharded Database with System-Managed Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD) **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -24,6 +24,8 @@ Choosing this option takes substantially less time during the Oracle Database Sh * Namespace: `shns` * Database Cloning from the Database Gold Image present in Persistent Volume having OCID: `ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq` +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. + NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned in the same Availability Domain `PHX-AD-1` by cloning the database. In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md new file mode 100644 index 00000000..0c6ea8fe --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md @@ -0,0 +1,40 @@ +# Provisioning Oracle Sharded Database with System-Managed Sharding with number of chunks specified + +**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. + +In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System-Managed Sharding is deployed using Oracle Sharding controller. + +**NOTE:** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. + +By default, the System-Managed Sharding deploys the Sharded Database with 120 chunks per Shard Database. If, for example, we have three shards in the Sharded Database, it will be total of 360 chunks. In this example, the Sharded Database will be deployed with non-default number of chunks specified using parameter `CATALOG_CHUNKS`. + +This example uses `ssharding_shard_prov_chunks.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: + +* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` +* Three sharding Pods: `shard1`, `shard2` and `shard3` +* One Catalog Pod: `catalog` +* Total number of chunks as `120` specified by variable `CATALOG_CHUNKS` (it will be 40 chunks per shard) +* Namespace: `shns` + + +In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov.yaml`. + * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + + +Use the file: [ssharding_shard_prov_chunks.yaml](./ssharding_shard_prov_chunks.yaml) for this use case as below: + +1. Deploy the `ssharding_shard_prov_chunks.yaml` file: + ```sh + kubectl apply -f ssharding_shard_prov_chunks.yaml + ``` +1. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n shns + + # Check the logs of a particular pod. For example, to check status of pod "shard1-0": + kubectl logs -f pod/shard1-0 -n shns + ``` diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md index e14e76a4..c4f45a48 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md @@ -1,8 +1,8 @@ -# Provisioning Oracle Sharded Database with System Sharding with additional control on resources like Memory and CPU allocated to Pods +# Provisioning Oracle Sharded Database with System-Managed Sharding with additional control on resources like Memory and CPU allocated to Pods **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. -In this use case, there are additional tags used to control resources such as CPU and Memory used by the different Pods when the Oracle Sharding topology with System Sharding is deployed using Oracle Sharding controller. +In this use case, there are additional tags used to control resources such as CPU and Memory used by the different Pods when the Oracle Sharding topology with System-Managed Sharding is deployed using Oracle Sharding controller. This example uses `ssharding_shard_prov_memory_cpu.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: @@ -17,6 +17,9 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_memory_cpu.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + + **NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. Use the YAML file [ssharding_shard_prov_memory_cpu.yaml](./ssharding_shard_prov_memory_cpu.yaml). diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md index 90a8f803..1a6a1ee3 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md @@ -1,4 +1,4 @@ -# Provisioning Oracle Sharded Database with System Sharding and send Notification using OCI Notification Service +# Provisioning Oracle Sharded Database with System-Managed Sharding and send Notification using OCI Notification Service **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -65,6 +65,9 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_send_notification.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. Use the file: [ssharding_shard_prov_send_notification.yaml](./ssharding_shard_prov_send_notification.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md index 8f10fd8c..b9042c64 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md @@ -1,10 +1,10 @@ -# Provisioning Oracle Sharded Database with System Sharding without Database Gold Image +# Provisioning Oracle Sharded Database with System-Managed Sharding without Database Gold Image **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. -In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System Sharding is deployed using Oracle Sharding controller. +In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System-Managed Sharding is deployed using Oracle Sharding controller. -**NOTE** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. +**NOTE:** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. This example uses `ssharding_shard_prov.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: @@ -18,6 +18,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. Use the file: [ssharding_shard_prov.yaml](./ssharding_shard_prov.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md index 4d5713d4..bca34253 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md @@ -1,8 +1,8 @@ -# Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System Sharding +# Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. -This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with System Sharding provisioned using Oracle Database Sharding controller. +This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with System-Managed Sharding provisioned using Oracle Database Sharding controller. **NOTE** The deletion of a shard is done after verifying the Chunks have been moved out of that shard. @@ -17,8 +17,9 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_delshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. -NOTE: Use tag `isDelete: true` to delete the shard you want. +NOTE: Use tag `isDelete: enable` to delete the shard you want. This use case deletes the shard `shard4` from the above Sharding Topology. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md index 5c349847..1db8e6c3 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md @@ -1,8 +1,8 @@ -# Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System Sharding +# Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. -This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with System Sharding provisioned earlier using Oracle Database Sharding controller. +This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with System-Managed Sharding provisioned earlier using Oracle Database Sharding controller. In this use case, the existing Oracle Database sharding topology is having: @@ -15,6 +15,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_extshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml index f803ba42..7d4e16ec 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml @@ -55,4 +55,3 @@ spec: - name: oltp_ro_svc role: primary namespace: shns - diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml new file mode 100644 index 00000000..39e6a6ff --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml @@ -0,0 +1,60 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: database.oracle.com/v1alpha1 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample + namespace: shns +spec: + shard: + - name: shard1 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard2 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + - name: shard3 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardGroup: shardgroup1 + shardRegion: primary + catalog: + - name: catalog + storageSizeInGb: 50 + imagePullPolicy: "Always" + envVars: + - name: "CATALOG_CHUNKS" + value: "120" + gsm: + - name: gsm1 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: primary + - name: gsm2 + imagePullPolicy: "Always" + storageSizeInGb: 50 + region: standby + storageClass: oci + dbImage: container-registry.oracle.com/database/enterprise:latest + dbImagePullSecret: ocr-reg-cred + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred + isExternalSvc: False + isDeleteOraPvc: True + dbSecret: + name: db-user-pass-rsa + pwdFileName: pwdfile.enc + keyFileName: key.pem + gsmService: + - name: oltp_rw_svc + role: primary + - name: oltp_ro_svc + role: primary + namespace: shns diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml index d2ae0ba9..b7dd1397 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml @@ -58,10 +58,18 @@ spec: imagePullPolicy: "Always" storageSizeInGb: 50 region: primary + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - name: gsm2 imagePullPolicy: "Always" storageSizeInGb: 50 region: standby + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" storageClass: oci dbImage: container-registry.oracle.com/database/enterprise:latest dbImagePullSecret: ocr-reg-cred @@ -79,4 +87,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns \ No newline at end of file + namespace: shns diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml index 5f600d3f..75caca31 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml @@ -26,7 +26,7 @@ spec: shardGroup: shardgroup1 shardRegion: primary - name: shard4 - isDelete: True + isDelete: enable storageSizeInGb: 50 imagePullPolicy: "Always" shardGroup: shardgroup1 diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md index 0beecbca..9b2905e8 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -37,6 +37,8 @@ In this example, we are using pre-built Oracle Database and Global Data Services * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. + Use the file: [udsharding_shard_prov_clone_across_ads.yaml](./udsharding_shard_prov_clone_across_ads.yaml) for this use case as below: 1. Deploy the `udsharding_shard_prov_clone_across_ads.yaml` file: diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md index 445d0105..a4669667 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -33,6 +33,8 @@ In this example, we are using pre-built Oracle Database and Global Data Services * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. + Use the file: [udsharding_shard_prov_clone.yaml](./udsharding_shard_prov_clone.yaml) for this use case as below: 1. Deploy the `udsharding_shard_prov_clone.yaml` file: diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md index d37368f8..b52b8745 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md @@ -18,6 +18,9 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_memory_cpu.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + +**NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. Use the YAML file [udsharding_shard_prov_memory_cpu.yaml](./udsharding_shard_prov_memory_cpu.yaml). diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md index c7da6aa5..640301a2 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md @@ -66,6 +66,9 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_send_notification.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. Use the file: [udsharding_shard_prov_send_notification.yaml](./udsharding_shard_prov_send_notification.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md index a275155f..2be5ac9f 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md @@ -19,7 +19,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. Use the file: [udsharding_shard_prov.yaml](./udsharding_shard_prov.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md index 946a0ab9..3b93b284 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md @@ -4,8 +4,6 @@ This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with User Defined Sharding provisioned using Oracle Database Sharding controller. -**NOTE** The deletion of a shard is done after verifying the Chunks have been moved out of that shard. - In this use case, the existing database Sharding is having: * Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` @@ -18,30 +16,50 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_delshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. -NOTE: Use tag `isDelete: true` to delete the shard you want. +**NOTE:** Use tag `isDelete: enable` to delete the shard you want. This use case deletes the shard `shard4` from the above Sharding Topology. Use the file: [udsharding_shard_prov_delshard.yaml](./udsharding_shard_prov_delshard.yaml) for this use case as below: -1. Deploy the `udsharding_shard_prov_delshard.yaml` file: +1. Move out the chunks from the shard to be deleted to another shard. For example, in the current case, before deleting the `shard4`, if you want to move the chunks from `shard4` to `shard2`, then you can run the below `kubectl` command where `/u01/app/oracle/product/23ai/gsmhome_1` is the GSM HOME: + ```sh + kubectl exec -it pod/gsm1-0 -n shns -- /u01/app/oracle/product/23ai/gsmhome_1/bin/gdsctl "move chunk -chunk all -source shard4_shard4pdb -target shard4_shard4pdb" + ``` +2. Confirm the shard to be deleted (`shard4` in this case) is not having any chunk using below command: + ```sh + kubectl exec -it pod/gsm1-0 -n shns -- /u01/app/oracle/product/23ai/gsmhome_1/bin/gdsctl "config chunks" + ``` + If there is no chunk present in the shard to be deleted, you can move to the next step. + +3. Apply the `udsharding_shard_prov_delshard.yaml` file: ```sh kubectl apply -f udsharding_shard_prov_delshard.yaml ``` -2. Check the status of the deployment: +4. Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n shns + ``` -**NOTE:** After you apply `udsharding_shard_prov_delshard.yaml`, the change may not be visible immediately. When the shard is removed, first the chunks will be moved out of that shard that is going to be deleted. +**NOTE:** +- After you apply `udsharding_shard_prov_delshard.yaml`, the change may not be visible immediately and it may take some time for the delete operation to complete. +- If the shard, that you are trying to delete, is still having chunks, then the you will see message like below in the logs of the Oracle Database Operator Pod. + ```sh + INFO controllers.database.ShardingDatabase manual intervention required + ``` + In this case, you will need to first move out the chunks from the shard to be deleted using Step 2 above and then apply the file in Step 3 to delete that shard. -To monitor the chunk movement, use the following command: +To check the status, use the following command: + ```sh + # Switch to the primary GSM Container: + kubectl exec -i -t gsm1-0 -n shns /bin/bash -```sh -# Switch to the primary GSM Container: -kubectl exec -i -t gsm1-0 -n shns /bin/bash + # Check the status shards: + gdsctl config shard -# Check the status of the chunks and repeat to observe the chunk movement: -gdsctl config chunks -``` + # Check the status of the chunks: + gdsctl config chunks + ``` \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md index e4200d72..20f50b29 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md @@ -16,6 +16,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_extshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml index 019fc887..0692d04b 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml @@ -55,5 +55,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns - + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml index adc2271f..bfe8da53 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml @@ -80,4 +80,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml index f2d10e23..bcaaf457 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml @@ -58,10 +58,18 @@ spec: imagePullPolicy: "Always" storageSizeInGb: 50 region: primary + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - name: gsm2 imagePullPolicy: "Always" storageSizeInGb: 50 region: standby + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" storageClass: oci dbImage: container-registry.oracle.com/database/enterprise:latest dbImagePullSecret: ocr-reg-cred @@ -80,4 +88,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml index 51f1c292..2342dc55 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml @@ -30,7 +30,7 @@ spec: imagePullPolicy: "Always" shardSpace: sspace4 shardRegion: primary - isDelete: True + isDelete: enable - name: shard5 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -66,4 +66,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml index f45d421f..0609c4b4 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml @@ -64,4 +64,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml index 15022925..cfdb62ea 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml @@ -86,5 +86,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns - + namespace: shns \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml index afd951fe..0cf9c8c5 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml @@ -82,4 +82,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns + namespace: shns \ No newline at end of file diff --git a/main.go b/main.go index acdd2719..4174e97d 100644 --- a/main.go +++ b/main.go @@ -249,6 +249,9 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") os.Exit(1) } + if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + } if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") os.Exit(1) diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index ce279486..ae217350 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -2114,7 +2114,7 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: - type: boolean + type: string label: type: string name: @@ -2177,6 +2177,8 @@ spec: - name type: object type: array + dbEdition: + type: string dbImage: type: string dbImagePullSecret: @@ -2206,6 +2208,8 @@ spec: - name - pwdFileName type: object + fssStorageClass: + type: string gsm: items: description: GsmSpec defines the desired state of GsmSpec @@ -2230,7 +2234,7 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: - type: boolean + type: string label: type: string name: @@ -2407,7 +2411,7 @@ spec: isExternalSvc: type: boolean isTdeWallet: - type: boolean + type: string liveinessCheckPeriod: type: integer namespace: @@ -2461,7 +2465,12 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: - type: boolean + enum: + - enable + - disable + - failed + - force + type: string label: type: string name: @@ -2544,6 +2553,10 @@ spec: type: string storageClass: type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string required: - catalog - dbImage @@ -3845,6 +3858,26 @@ webhooks: resources: - pdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: mshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -4058,6 +4091,27 @@ webhooks: resources: - pdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: vshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 From c1b77737e158e30ef830675b13faa29285972ffc Mon Sep 17 00:00:00 2001 From: racpack Date: Tue, 30 Jul 2024 17:40:40 +0000 Subject: [PATCH 108/414] psaini_dbcs_branch1 --- apis/database/v1alpha1/dbcssystem_types.go | 25 +- .../v1alpha1/pdbconfig_connection_strings.go | 45 -- apis/database/v1alpha1/pdbconfig_types.go | 72 --- .../v1alpha1/zz_generated.deepcopy.go | 91 ---- commons/dbcssystem/dbcs_reconciler.go | 33 +- .../bases/database.oracle.com_DbcsSystem.yaml | 52 +-- .../database.oracle.com_dbcssystems.yaml | 49 +-- controllers/database/dbcssystem_controller.go | 412 +----------------- docs/dbcs/README.md | 64 ++- .../bind_to_existing_dbcs_system.md | 8 +- .../bind_to_existing_dbcs_system.yaml | 2 +- .../dbcs/provisioning/create_dbcs_with_pdb.md | 55 --- docs/dbcs/provisioning/create_pdb.md | 55 --- ...reatepdb_in_existing_dbcs_system_list.yaml | 28 -- ...xisting_dbcs_system_list_sample_output.log | 17 - .../provisioning/dbcs_service_with_pdb.yaml | 38 -- .../dbcs_service_with_pdb_sample_output.log | 103 ----- docs/dbcs/provisioning/delete_pdb.md | 50 --- ...eletepdb_in_existing_dbcs_system_list.yaml | 13 - ...xisting_dbcs_system_list_sample_output.log | 8 - oracle-database-operator.yaml | 129 +----- 21 files changed, 74 insertions(+), 1275 deletions(-) delete mode 100644 apis/database/v1alpha1/pdbconfig_connection_strings.go delete mode 100644 apis/database/v1alpha1/pdbconfig_types.go delete mode 100644 docs/dbcs/provisioning/create_dbcs_with_pdb.md delete mode 100644 docs/dbcs/provisioning/create_pdb.md delete mode 100644 docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml delete mode 100644 docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log delete mode 100644 docs/dbcs/provisioning/dbcs_service_with_pdb.yaml delete mode 100644 docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log delete mode 100644 docs/dbcs/provisioning/delete_pdb.md delete mode 100644 docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml delete mode 100644 docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log diff --git a/apis/database/v1alpha1/dbcssystem_types.go b/apis/database/v1alpha1/dbcssystem_types.go index a2ff3bfc..37e80a6b 100644 --- a/apis/database/v1alpha1/dbcssystem_types.go +++ b/apis/database/v1alpha1/dbcssystem_types.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2022-2024 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -42,7 +42,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/go-logr/logr" dbcsv1 "github.com/oracle/oracle-database-operator/commons/annotations" "sigs.k8s.io/controller-runtime/pkg/client" @@ -58,7 +57,6 @@ type DbcsSystemSpec struct { OCIConfigMap string `json:"ociConfigMap"` OCISecret string `json:"ociSecret,omitempty"` HardLink bool `json:"hardLink,omitempty"` - PdbConfigs []PDBConfig `json:"pdbConfigs,omitempty"` } // DbSystemDetails Spec @@ -98,7 +96,7 @@ type DbSystemDetails struct { DbBackupConfig Backupconfig `json:"dbBackupConfig,omitempty"` } -// DB Backup Config Network Struct +// DB Backup COnfig Network Struct type Backupconfig struct { AutoBackupEnabled *bool `json:"autoBackupEnabled,omitempty"` RecoveryWindowsInDays *int `json:"recoveryWindowsInDays,omitempty"` @@ -210,25 +208,6 @@ func (dbcs *DbcsSystem) GetLastSuccessfulSpec() (*DbcsSystemSpec, error) { return &sucSpec, nil } -func (dbcs *DbcsSystem) GetLastSuccessfulSpecWithLog(log logr.Logger) (*DbcsSystemSpec, error) { - val, ok := dbcs.GetAnnotations()[lastSuccessfulSpec] - if !ok { - log.Info("No last successful spec annotation found") - return nil, nil - } - - specBytes := []byte(val) - sucSpec := DbcsSystemSpec{} - - err := json.Unmarshal(specBytes, &sucSpec) - if err != nil { - log.Error(err, "Failed to unmarshal last successful spec") - return nil, err - } - - log.Info("Successfully retrieved last successful spec", "spec", sucSpec) - return &sucSpec, nil -} // UpdateLastSuccessfulSpec updates lastSuccessfulSpec with the current spec. func (dbcs *DbcsSystem) UpdateLastSuccessfulSpec(kubeClient client.Client) error { diff --git a/apis/database/v1alpha1/pdbconfig_connection_strings.go b/apis/database/v1alpha1/pdbconfig_connection_strings.go deleted file mode 100644 index 9c3092ce..00000000 --- a/apis/database/v1alpha1/pdbconfig_connection_strings.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2016, 2018, 2024, Oracle and/or its affiliates. All rights reserved. -// This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license. -// Code generated. DO NOT EDIT. - -// Database Service API -// -// The API for the Database Service. Use this API to manage resources such as databases and DB Systems. For more information, see Overview of the Database Service (https://docs.cloud.oracle.com/iaas/Content/Database/Concepts/databaseoverview.htm). -// - -package v1alpha1 - -import ( - "fmt" - "github.com/oracle/oci-go-sdk/v65/common" - "strings" -) - -// PDBConfigConnectionStrings Connection strings to connect to an Oracle Pluggable Database. -type PDBConfigConnectionStrings struct { - - // A host name-based PDB connection string. - PdbDefault *string `mandatory:"false" json:"pdbDefault"` - - // An IP-based PDB connection string. - PdbIpDefault *string `mandatory:"false" json:"pdbIpDefault"` - - // All connection strings to use to connect to the pluggable database. - AllConnectionStrings map[string]string `mandatory:"false" json:"allConnectionStrings"` -} - -func (m PDBConfigConnectionStrings) String() string { - return common.PointerString(m) -} - -// ValidateEnumValue returns an error when providing an unsupported enum value -// This function is being called during constructing API request process -// Not recommended for calling this function directly -func (m PDBConfigConnectionStrings) ValidateEnumValue() (bool, error) { - errMessage := []string{} - - if len(errMessage) > 0 { - return true, fmt.Errorf(strings.Join(errMessage, "\n")) - } - return false, nil -} \ No newline at end of file diff --git a/apis/database/v1alpha1/pdbconfig_types.go b/apis/database/v1alpha1/pdbconfig_types.go deleted file mode 100644 index b5d7c501..00000000 --- a/apis/database/v1alpha1/pdbconfig_types.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -** Copyright (c) 2022-2024 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ -package v1alpha1 - -// PDBConfig defines details of PDB struct for DBCS systems -type PDBConfig struct { - // The name for the pluggable database (PDB). The name is unique in the context of a Database. The name must begin with an alphabetic character and can contain a maximum of thirty alphanumeric characters. Special characters are not permitted. The pluggable database name should not be same as the container database name. - PdbName *string `mandatory:"true" json:"pdbName"` - - // The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the CDB - // ContainerDatabaseId *string `mandatory:"false" json:"containerDatabaseId"` - - // // A strong password for PDB Admin. The password must be at least nine characters and contain at least two uppercase, two lowercase, two numbers, and two special characters. The special characters must be _, \#, or -. - PdbAdminPassword *string `mandatory:"false" json:"pdbAdminPassword"` - - // // The existing TDE wallet password of the CDB. - TdeWalletPassword *string `mandatory:"false" json:"tdeWalletPassword"` - - // // The locked mode of the pluggable database admin account. If false, the user needs to provide the PDB Admin Password to connect to it. - // // If true, the pluggable database will be locked and user cannot login to it. - ShouldPdbAdminAccountBeLocked *bool `mandatory:"false" json:"shouldPdbAdminAccountBeLocked"` - - // // Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. - // // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). - // // Example: `{"Department": "Finance"}` - FreeformTags map[string]string `mandatory:"false" json:"freeformTags"` - - // // Defined tags for this resource. Each key is predefined and scoped to a namespace. - // // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). - // DefinedTags map[string]map[string]interface{} `mandatory:"false" json:"definedTags"` - - // To specify whether to delete the PDB - IsDelete *bool `json:"isDelete,omitempty"` - - // The OCID of the PDB for deletion purposes. - PluggableDatabaseId *string `mandatory:"false" json:"pluggableDatabaseId"` -} diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 973083f2..f1eaf503 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1187,13 +1187,6 @@ func (in *DbcsSystemSpec) DeepCopyInto(out *DbcsSystemSpec) { *out = new(string) **out = **in } - if in.PdbConfigs != nil { - in, out := &in.PdbConfigs, &out.PdbConfigs - *out = make([]PDBConfig, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemSpec. @@ -1874,90 +1867,6 @@ func (in *PDBAdminPassword) DeepCopy() *PDBAdminPassword { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { - *out = *in - if in.PdbName != nil { - in, out := &in.PdbName, &out.PdbName - *out = new(string) - **out = **in - } - if in.PdbAdminPassword != nil { - in, out := &in.PdbAdminPassword, &out.PdbAdminPassword - *out = new(string) - **out = **in - } - if in.TdeWalletPassword != nil { - in, out := &in.TdeWalletPassword, &out.TdeWalletPassword - *out = new(string) - **out = **in - } - if in.ShouldPdbAdminAccountBeLocked != nil { - in, out := &in.ShouldPdbAdminAccountBeLocked, &out.ShouldPdbAdminAccountBeLocked - *out = new(bool) - **out = **in - } - if in.FreeformTags != nil { - in, out := &in.FreeformTags, &out.FreeformTags - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.IsDelete != nil { - in, out := &in.IsDelete, &out.IsDelete - *out = new(bool) - **out = **in - } - if in.PluggableDatabaseId != nil { - in, out := &in.PluggableDatabaseId, &out.PluggableDatabaseId - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBConfig. -func (in *PDBConfig) DeepCopy() *PDBConfig { - if in == nil { - return nil - } - out := new(PDBConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBConfigConnectionStrings) DeepCopyInto(out *PDBConfigConnectionStrings) { - *out = *in - if in.PdbDefault != nil { - in, out := &in.PdbDefault, &out.PdbDefault - *out = new(string) - **out = **in - } - if in.PdbIpDefault != nil { - in, out := &in.PdbIpDefault, &out.PdbIpDefault - *out = new(string) - **out = **in - } - if in.AllConnectionStrings != nil { - in, out := &in.AllConnectionStrings, &out.AllConnectionStrings - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBConfigConnectionStrings. -func (in *PDBConfigConnectionStrings) DeepCopy() *PDBConfigConnectionStrings { - if in == nil { - return nil - } - out := new(PDBConfigConnectionStrings) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBList) DeepCopyInto(out *PDBList) { *out = *in diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index ea1bce37..70e50294 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -94,7 +94,7 @@ func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient d dbcsDetails.CompartmentId = common.String(dbcs.Spec.DbSystem.CompartmentId) dbcsDetails.SubnetId = common.String(dbcs.Spec.DbSystem.SubnetId) dbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) - dbcsDetails.Domain = common.String(dbcs.Spec.DbSystem.Domain) + dbcsDetails.Domain = common.String(dbcs.Spec.DbSystem.Domain) if dbcs.Spec.DbSystem.DisplayName != "" { dbcsDetails.DisplayName = common.String(dbcs.Spec.DbSystem.DisplayName) } @@ -420,51 +420,34 @@ func GetListDatabaseRsp(logger logr.Logger, dbClient database.DatabaseClient, db func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, kubeClient client.Client, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { //logger := log.WithName("UpdateDbcsSystemInstance") - log.Info("DB System Getting Updated with func UpdateDbcsSystemIdInst") updateFlag := false updateDbcsDetails := database.UpdateDbSystemDetails{} - // Log annotations before retrieving the old spec - log.Info("Current annotations", "annotations", dbcs.GetAnnotations()) - // oldSpec, err := dbcs.GetLastSuccessfulSpecWithLog() - oldSpec, err := dbcs.GetLastSuccessfulSpecWithLog(log) // Use the new method + oldSpec, err := dbcs.GetLastSuccessfulSpec() if err != nil { - log.Error(err, "Failed to get last successful spec") return err } - if oldSpec == nil { - log.Info("oldSpec is nil") - } else { - log.Info("Details of oldSpec", "oldSpec", oldSpec) - } - // Log the entire oldSpec to see its contents - log.Info("Details of updateFlag -> " + fmt.Sprint(updateFlag)) - if dbcs.Spec.DbSystem.CpuCoreCount > 0 && dbcs.Spec.DbSystem.CpuCoreCount != oldSpec.DbSystem.CpuCoreCount { - log.Info("DB System cpu core count is: " + fmt.Sprint(dbcs.Spec.DbSystem.CpuCoreCount) + " DB System old cpu count is: " + fmt.Sprint(oldSpec.DbSystem.CpuCoreCount)) updateDbcsDetails.CpuCoreCount = common.Int(dbcs.Spec.DbSystem.CpuCoreCount) updateFlag = true } if dbcs.Spec.DbSystem.Shape != "" && dbcs.Spec.DbSystem.Shape != oldSpec.DbSystem.Shape { - log.Info("DB System desired shape is :" + string(dbcs.Spec.DbSystem.Shape) + "DB System old shape is " + string(oldSpec.DbSystem.Shape)) updateDbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) updateFlag = true } if dbcs.Spec.DbSystem.LicenseModel != "" && dbcs.Spec.DbSystem.LicenseModel != oldSpec.DbSystem.LicenseModel { licenceModel := getLicenceModel(dbcs) - log.Info("DB System desired License Model is :" + string(dbcs.Spec.DbSystem.LicenseModel) + "DB Sytsem old License Model is " + string(oldSpec.DbSystem.LicenseModel)) updateDbcsDetails.LicenseModel = database.UpdateDbSystemDetailsLicenseModelEnum(licenceModel) updateFlag = true } if dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != 0 && dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != oldSpec.DbSystem.InitialDataStorageSizeInGB { - log.Info("DB System desired Storage Size is :" + fmt.Sprint(dbcs.Spec.DbSystem.InitialDataStorageSizeInGB) + "DB System old Storage Size is " + fmt.Sprint(oldSpec.DbSystem.InitialDataStorageSizeInGB)) updateDbcsDetails.DataStorageSizeInGBs = &dbcs.Spec.DbSystem.InitialDataStorageSizeInGB updateFlag = true } - log.Info("Details of updateFlag after validations is" + fmt.Sprint(updateFlag)) + if updateFlag { updateDbcsRequest := database.UpdateDbSystemRequest{ DbSystemId: common.String(*dbcs.Spec.Id), @@ -553,10 +536,10 @@ func GetResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id s func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { - if dbcs.Spec.Id == nil { - dbcs.Status.State = "FAILED" - return nil - } + if dbcs.Spec.Id == nil { + dbcs.Status.State = "FAILED" + return nil + } dbcsId := *dbcs.Spec.Id @@ -785,4 +768,4 @@ func ValidateSpex(logger logr.Logger, kubeClient client.Client, dbClient databas return nil -} \ No newline at end of file +} diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml index 83890a3f..e933d5a4 100644 --- a/config/crd/bases/database.oracle.com_DbcsSystem.yaml +++ b/config/crd/bases/database.oracle.com_DbcsSystem.yaml @@ -51,7 +51,7 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup Config Network Struct + description: DB Backup COnfig Network Struct properties: autoBackupEnabled: type: boolean @@ -135,56 +135,6 @@ spec: type: string ociSecret: type: string - pdbConfigs: - items: - description: PDBConfig defines details of PDB struct for DBCS systems - properties: - freeformTags: - additionalProperties: - type: string - description: '// Free-form tags for this resource. Each tag - is a simple key-value pair with no predefined name, type, - or namespace. // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). - // Example: `{"Department": "Finance"}`' - type: object - isDelete: - description: To specify whether to delete the PDB - type: boolean - pdbAdminPassword: - description: // A strong password for PDB Admin. The password - must be at least nine characters and contain at least two - uppercase, two lowercase, two numbers, and two special characters. - The special characters must be _, \#, or -. - type: string - pdbName: - description: The name for the pluggable database (PDB). The - name is unique in the context of a Database. The name must - begin with an alphabetic character and can contain a maximum - of thirty alphanumeric characters. Special characters are - not permitted. The pluggable database name should not be same - as the container database name. - type: string - pluggableDatabaseId: - description: The OCID of the PDB for deletion purposes. - type: string - shouldPdbAdminAccountBeLocked: - description: // The locked mode of the pluggable database admin - account. If false, the user needs to provide the PDB Admin - Password to connect to it. // If true, the pluggable database - will be locked and user cannot login to it. - type: boolean - tdeWalletPassword: - description: // The existing TDE wallet password of the CDB. - type: string - required: - - freeformTags - - pdbAdminPassword - - pdbName - - pluggableDatabaseId - - shouldPdbAdminAccountBeLocked - - tdeWalletPassword - type: object - type: array required: - ociConfigMap type: object diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index 8e34f556..3f4b1c46 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -51,7 +51,7 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup Config Network Struct + description: DB Backup COnfig Network Struct properties: autoBackupEnabled: type: boolean @@ -135,51 +135,6 @@ spec: type: string ociSecret: type: string - pdbConfigs: - items: - description: PDBConfig defines details of PDB struct for DBCS systems - properties: - freeformTags: - additionalProperties: - type: string - description: '// Free-form tags for this resource. Each tag - is a simple key-value pair with no predefined name, type, - or namespace. // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). - // Example: `{"Department": "Finance"}`' - type: object - isDelete: - description: To specify whether to delete the PDB - type: boolean - pdbAdminPassword: - description: // A strong password for PDB Admin. The password - must be at least nine characters and contain at least two - uppercase, two lowercase, two numbers, and two special characters. - The special characters must be _, \#, or -. - type: string - pdbName: - description: The name for the pluggable database (PDB). The - name is unique in the context of a Database. The name must - begin with an alphabetic character and can contain a maximum - of thirty alphanumeric characters. Special characters are - not permitted. The pluggable database name should not be same - as the container database name. - type: string - pluggableDatabaseId: - description: The OCID of the PDB for deletion purposes. - type: string - shouldPdbAdminAccountBeLocked: - description: // The locked mode of the pluggable database admin - account. If false, the user needs to provide the PDB Admin - Password to connect to it. // If true, the pluggable database - will be locked and user cannot login to it. - type: boolean - tdeWalletPassword: - description: // The existing TDE wallet password of the CDB. - type: string - required: - - pdbName - type: object - type: array required: - ociConfigMap type: object @@ -282,4 +237,4 @@ status: kind: "" plural: "" conditions: [] - storedVersions: [] \ No newline at end of file + storedVersions: [] diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index dd50caae..003be62a 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2022-2024 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,10 +40,7 @@ package controllers import ( "context" - "fmt" "reflect" - "strings" - "time" databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" dbcsv1 "github.com/oracle/oracle-database-operator/commons/dbcssystem" @@ -51,14 +48,11 @@ import ( "github.com/oracle/oracle-database-operator/commons/oci" "github.com/go-logr/logr" - "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/core" "github.com/oracle/oci-go-sdk/v65/database" "github.com/oracle/oci-go-sdk/v65/workrequests" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -97,10 +91,12 @@ type DbcsSystemReconciler struct { func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { r.Logger = log.FromContext(ctx) + // your logic here + + //r.Logger = r.Logv1.WithValues("Instance.Namespace", req.NamespacedName) var err error // Get the dbcs instance from the cluster dbcsInst := &databasev1alpha1.DbcsSystem{} - r.Logger.Info("Reconciling DbSystemDetails", "name", req.NamespacedName) if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, dbcsInst); err != nil { if !errors.IsNotFound(err) { @@ -131,13 +127,10 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } r.wrClient, err = workrequests.NewWorkRequestClientWithConfigurationProvider(provider) - if err != nil { - return ctrl.Result{}, err - } r.Logger.Info("OCI provider configured succesfully") /* - Using Finalizer for object deletion + Using Finalizer for object deletion */ if dbcsInst.ObjectMeta.DeletionTimestamp.IsZero() { @@ -161,23 +154,6 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue return ctrl.Result{}, nil } - - // Check if PDBConfig is defined - pdbConfigs := dbcsInst.Spec.PdbConfigs - for _, pdbConfig := range pdbConfigs { - if pdbConfig.PdbName != nil { - // Handle PDB deletion if PluggableDatabaseId is defined and isDelete is true - if pdbConfig.IsDelete != nil && pdbConfig.PluggableDatabaseId != nil && *pdbConfig.IsDelete { - // Call deletePluggableDatabase function - dbSystemId := *dbcsInst.Spec.Id - if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - } - } - // Remove the finalizer and update the object finalizer.Unregister(r.KubeClient, dbcsInst) r.Logger.Info("Finalizer unregistered successfully.") // Stop reconciliation as the item is being deleted @@ -185,7 +161,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } /* - Determine whether it's a provision or bind operation + Determine whether it's a provision or bind operation */ lastSucSpec, err := dbcsInst.GetLastSuccessfulSpec() if err != nil { @@ -242,10 +218,10 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - dbSystemId := *dbcsInst.Spec.Id + dbcsInstId := *dbcsInst.Spec.Id if err := dbcsv1.UpdateDbcsSystemId(r.KubeClient, dbcsInst); err != nil { // Change the status to Failed - assignDBCSID(dbcsInst, dbSystemId) + assignDBCSID(dbcsInst, dbcsInstId) if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Failed, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } @@ -254,11 +230,11 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("Sync information from remote DbcsSystem System successfully") - dbSystemId = *dbcsInst.Spec.Id + dbcsInstId = *dbcsInst.Spec.Id if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { return ctrl.Result{}, err } - assignDBCSID(dbcsInst, dbSystemId) + assignDBCSID(dbcsInst, dbcsInstId) } else { if dbcsInst.Spec.Id == nil { dbcsInst.Spec.Id = lastSucSpec.Id @@ -285,382 +261,18 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) //r.updateWalletSecret(dbcs) // Update the last succesful spec - dbSystemId := *dbcsInst.Spec.Id + dbcsInstId := *dbcsInst.Spec.Id if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { return ctrl.Result{}, err } //assignDBCSID(dbcsInst,dbcsI) // Change the phase to "Available" - assignDBCSID(dbcsInst, dbSystemId) + assignDBCSID(dbcsInst, dbcsInstId) if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Available, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } - r.Logger.Info("DBInst after assignment", "dbcsInst:->", dbcsInst) - // // Check if PDBConfig is defined and needs to be created or deleted - pdbConfigs := dbcsInst.Spec.PdbConfigs - - if pdbConfigs != nil { - for _, pdbConfig := range pdbConfigs { - if pdbConfig.PdbName != nil { - // Get database details - // Get DB Home ID by DB System ID - // Get Compartment ID by DB System ID - compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) - if err != nil { - fmt.Printf("Failed to get compartment ID: %v\n", err) - return ctrl.Result{}, err - } - dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, dbSystemId) - if err != nil { - fmt.Printf("Failed to get DB Home ID: %v\n", err) - return ctrl.Result{}, err - } - databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, dbSystemId, compartmentId, dbHomeId) - if err != nil { - fmt.Printf("Failed to get database IDs: %v\n", err) - return ctrl.Result{}, err - } - - // Now you can use dbDetails to access database attributes - r.Logger.Info("Database details fetched successfully", "DatabaseId", databaseIds) - - // Check if deletion is requested - if pdbConfig.IsDelete != nil && *pdbConfig.IsDelete { - // Call deletePluggableDatabase function - if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { - return ctrl.Result{}, err - } - // Continue to the next pdbConfig - continue - } else { - // Call the method to create the pluggable database - r.Logger.Info("Calling createPluggableDatabase", "ctx:->", ctx, "dbcsInst:->", dbcsInst, "databaseIds:->", databaseIds[0], "compartmentId:->", compartmentId) - err := r.createPluggableDatabase(ctx, dbcsInst, pdbConfig, databaseIds[0], compartmentId, dbSystemId) - if err != nil { - // Handle error if required - return ctrl.Result{}, err - } - } - } - } - } else { - r.Logger.Info("No PDB configurations given.") - } return ctrl.Result{}, nil - -} - -// getDbHomeIdByDbSystemID retrieves the DB Home ID associated with the given DB System ID -func (r *DbcsSystemReconciler) getDbHomeIdByDbSystemID(ctx context.Context, compartmentId, dbSystemId string) (string, error) { - listRequest := database.ListDbHomesRequest{ - CompartmentId: &compartmentId, - DbSystemId: &dbSystemId, - } - - listResponse, err := r.dbClient.ListDbHomes(ctx, listRequest) - if err != nil { - return "", fmt.Errorf("failed to list DB homes: %v", err) - } - - if len(listResponse.Items) == 0 { - return "", fmt.Errorf("no DB homes found for DB system ID: %s", dbSystemId) - } - - return *listResponse.Items[0].Id, nil -} -func (r *DbcsSystemReconciler) getCompartmentIDByDbSystemID(ctx context.Context, dbSystemId string) (string, error) { - // Construct the GetDbSystem request - getRequest := database.GetDbSystemRequest{ - DbSystemId: &dbSystemId, - } - - // Call GetDbSystem API using the existing dbClient - getResponse, err := r.dbClient.GetDbSystem(ctx, getRequest) - if err != nil { - return "", fmt.Errorf("failed to get DB system details: %v", err) - } - - // Extract the compartment ID from the DB system details - compartmentId := *getResponse.DbSystem.CompartmentId - - return compartmentId, nil -} -func (r *DbcsSystemReconciler) getDatabaseIDByDbSystemID(ctx context.Context, dbSystemId, compartmentId, dbHomeId string) ([]string, error) { - // Construct the ListDatabases request - request := database.ListDatabasesRequest{ - SystemId: &dbSystemId, - CompartmentId: &compartmentId, - DbHomeId: &dbHomeId, - } - - // Call ListDatabases API using the existing dbClient - response, err := r.dbClient.ListDatabases(ctx, request) - if err != nil { - return nil, fmt.Errorf("failed to list databases: %v", err) - } - - // Extract database IDs from the response - var databaseIds []string - for _, dbSummary := range response.Items { - databaseIds = append(databaseIds, *dbSummary.Id) - } - - return databaseIds, nil -} - -func (r *DbcsSystemReconciler) createPluggableDatabase(ctx context.Context, dbcs *databasev1alpha1.DbcsSystem, pdbConfig databasev1alpha1.PDBConfig, databaseId, compartmentId, dbSystemId string) error { - r.Logger.Info("Checking if the pluggable database exists", "PDBName", pdbConfig.PdbName) - - // Check if the pluggable database already exists - exists, pdbId, err := r.doesPluggableDatabaseExist(ctx, compartmentId, pdbConfig.PdbName) - if err != nil { - r.Logger.Error(err, "Failed to check if pluggable database exists", "PDBName", pdbConfig.PdbName) - return err - } - if exists { - // Set the PluggableDatabaseId in PDBConfig - pdbConfig.PluggableDatabaseId = pdbId - r.Logger.Info("Pluggable database already exists", "PDBName", pdbConfig.PdbName, "PluggableDatabaseId", *pdbConfig.PluggableDatabaseId) - return nil - } - - // Define the DatabaseExists method locally - databaseExists := func(dbSystemID string) (bool, error) { - req := database.GetDbSystemRequest{ - DbSystemId: &dbSystemID, - } - _, err := r.dbClient.GetDbSystem(ctx, req) - if err != nil { - if ociErr, ok := err.(common.ServiceError); ok && ociErr.GetHTTPStatusCode() == 404 { - return false, nil - } - return false, err - } - return true, nil - } - - exists, err = databaseExists(dbSystemId) - if err != nil { - r.Logger.Error(err, "Failed to check database existence") - return err - } - - if !exists { - errMsg := fmt.Sprintf("Database does not exist: %s", dbSystemId) - r.Logger.Error(fmt.Errorf(errMsg), "Database not found") - return fmt.Errorf(errMsg) - } - - // Fetch secrets for TdeWalletPassword and PdbAdminPassword - tdeWalletPassword, err := r.getSecret(ctx, dbcs.Namespace, *pdbConfig.TdeWalletPassword) - // Trim newline character from the password - tdeWalletPassword = strings.TrimSpace(tdeWalletPassword) - r.Logger.Info("TDE wallet password retrieved successfully") - if err != nil { - r.Logger.Error(err, "Failed to get TDE wallet password secret") - return err - } - - pdbAdminPassword, err := r.getSecret(ctx, dbcs.Namespace, *pdbConfig.PdbAdminPassword) - // Trim newline character from the password - pdbAdminPassword = strings.TrimSpace(pdbAdminPassword) - r.Logger.Info("PDB admin password retrieved successfully") - if err != nil { - r.Logger.Error(err, "Failed to get PDB admin password secret") - return err - } - - // Proceed with creating the pluggable database - r.Logger.Info("Creating pluggable database", "PDBName", pdbConfig.PdbName) - createPdbReq := database.CreatePluggableDatabaseRequest{ - CreatePluggableDatabaseDetails: database.CreatePluggableDatabaseDetails{ - PdbName: pdbConfig.PdbName, - ContainerDatabaseId: &databaseId, - ShouldPdbAdminAccountBeLocked: pdbConfig.ShouldPdbAdminAccountBeLocked, - PdbAdminPassword: common.String(pdbAdminPassword), - TdeWalletPassword: common.String(tdeWalletPassword), - FreeformTags: pdbConfig.FreeformTags, - }, - } - response, err := r.dbClient.CreatePluggableDatabase(ctx, createPdbReq) - if err != nil { - r.Logger.Error(err, "Failed to create pluggable database", "PDBName", pdbConfig.PdbName) - return err - } - // Set the PluggableDatabaseId in PDBConfig - // Set the PluggableDatabaseId in PDBConfig - pdbConfig.PluggableDatabaseId = response.PluggableDatabase.Id - - r.Logger.Info("Pluggable database creation initiated", "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) - - // Polling mechanism to check PDB status - const maxRetries = 120 // total 1 hour wait for creation of PDB - const retryInterval = 30 // in seconds - - for i := 0; i < maxRetries; i++ { - getPdbReq := database.GetPluggableDatabaseRequest{ - PluggableDatabaseId: pdbConfig.PluggableDatabaseId, - } - - getPdbResp, err := r.dbClient.GetPluggableDatabase(ctx, getPdbReq) - if err != nil { - r.Logger.Error(err, "Failed to get pluggable database status", "PDBID", *pdbConfig.PluggableDatabaseId) - return err - } - - pdbStatus := getPdbResp.PluggableDatabase.LifecycleState - r.Logger.Info("Checking pluggable database status", "PDBID", *pdbConfig.PluggableDatabaseId, "Status", pdbStatus) - - if pdbStatus == database.PluggableDatabaseLifecycleStateAvailable { - r.Logger.Info("Pluggable database successfully created", "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) - return nil - } - - if pdbStatus == database.PluggableDatabaseLifecycleStateFailed { - r.Logger.Error(fmt.Errorf("pluggable database creation failed"), "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) - return fmt.Errorf("pluggable database creation failed") - } - - time.Sleep(retryInterval * time.Second) - } - - r.Logger.Error(fmt.Errorf("timed out waiting for pluggable database to become available"), "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) - return fmt.Errorf("timed out waiting for pluggable database to become available") -} - -func (r *DbcsSystemReconciler) pluggableDatabaseExists(ctx context.Context, pluggableDatabaseId string) (bool, error) { - req := database.GetPluggableDatabaseRequest{ - PluggableDatabaseId: &pluggableDatabaseId, - } - _, err := r.dbClient.GetPluggableDatabase(ctx, req) - if err != nil { - if ociErr, ok := err.(common.ServiceError); ok && ociErr.GetHTTPStatusCode() == 404 { - // PDB does not exist - return false, nil - } - // Other error occurred - return false, err - } - // PDB exists - return true, nil -} - -func (r *DbcsSystemReconciler) deletePluggableDatabase(ctx context.Context, pdbConfig databasev1alpha1.PDBConfig, dbSystemId string) error { - if pdbConfig.PdbName == nil { - return fmt.Errorf("PDB name is not specified") - } - - r.Logger.Info("Deleting pluggable database", "PDBName", *pdbConfig.PdbName) - - if pdbConfig.PluggableDatabaseId == nil { - r.Logger.Info("PluggableDatabaseId is not specified, getting pluggable databaseID") - // Call a function to retrieve PluggableDatabaseId - pdbID, err := r.getPluggableDatabaseID(ctx, pdbConfig, dbSystemId) - if err != nil { - return fmt.Errorf("failed to get PluggableDatabaseId: %v", err) - } - pdbConfig.PluggableDatabaseId = &pdbID - } - - // Now pdbConfig.PluggableDatabaseId should not be nil - if pdbConfig.PluggableDatabaseId == nil { - return fmt.Errorf("PluggableDatabaseId is still nil after retrieval attempt. Nothing to delete") - } - - // Check if PluggableDatabaseId exists in the live system - exists, err := r.pluggableDatabaseExists(ctx, *pdbConfig.PluggableDatabaseId) - if err != nil { - r.Logger.Error(err, "Failed to check if pluggable database exists", "PluggableDatabaseId", *pdbConfig.PluggableDatabaseId) - return err - } - if !exists { - r.Logger.Info("PluggableDatabaseId does not exist in the live system, nothing to delete", "PluggableDatabaseId", *pdbConfig.PluggableDatabaseId) - return nil - } - - // Define the delete request - deleteReq := database.DeletePluggableDatabaseRequest{ - PluggableDatabaseId: pdbConfig.PluggableDatabaseId, - } - - // Call OCI SDK to delete the PDB - _, err = r.dbClient.DeletePluggableDatabase(ctx, deleteReq) - if err != nil { - r.Logger.Error(err, "Failed to delete pluggable database", "PDBName", *pdbConfig.PdbName) - return err - } - - r.Logger.Info("Successfully deleted pluggable database", "PDBName", *pdbConfig.PdbName) - return nil -} - -func (r *DbcsSystemReconciler) getPluggableDatabaseID(ctx context.Context, pdbConfig databasev1alpha1.PDBConfig, dbSystemId string) (string, error) { - compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) - if err != nil { - fmt.Printf("Failed to get compartment ID: %v\n", err) - return "", err - } - request := database.ListPluggableDatabasesRequest{ - CompartmentId: &compartmentId, - } - - response, err := r.dbClient.ListPluggableDatabases(ctx, request) - if err != nil { - return "", fmt.Errorf("failed to list Pluggable Databases: %v", err) - } - - var pdbID string - - for _, pdb := range response.Items { - if *pdb.PdbName == *pdbConfig.PdbName { - pdbID = *pdb.Id - break - } - } - - if pdbID == "" { - return "", fmt.Errorf("pluggable database '%s' not found", *pdbConfig.PdbName) - } - return pdbID, nil -} - -// doesPluggableDatabaseExist checks if a pluggable database with the given name exists -func (r *DbcsSystemReconciler) doesPluggableDatabaseExist(ctx context.Context, compartmentId string, pdbName *string) (bool, *string, error) { - if pdbName == nil { - return false, nil, fmt.Errorf("pdbName is nil") - } - - listPdbsReq := database.ListPluggableDatabasesRequest{ - CompartmentId: &compartmentId, - } - - resp, err := r.dbClient.ListPluggableDatabases(ctx, listPdbsReq) - if err != nil { - return false, nil, err - } - - for _, pdb := range resp.Items { - if pdb.PdbName != nil && *pdb.PdbName == *pdbName && pdb.LifecycleState != "TERMINATED" { - return true, pdb.Id, nil - } - } - - return false, nil, nil -} -func (r *DbcsSystemReconciler) getSecret(ctx context.Context, namespace, secretName string) (string, error) { - secret := &corev1.Secret{} - err := r.KubeClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: secretName}, secret) - if err != nil { - return "", err - } - - // Assume the secret contains only one key-value pair - for _, value := range secret.Data { - return string(value), nil - } - - return "", fmt.Errorf("secret %s is empty", secretName) } func assignDBCSID(dbcsInst *databasev1alpha1.DbcsSystem, dbcsID string) { diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md index 103f1b09..c8b8d5d9 100644 --- a/docs/dbcs/README.md +++ b/docs/dbcs/README.md @@ -22,7 +22,6 @@ Two-node Oracle RAC DB systems require Oracle Enterprise Edition - Extreme Perfo For standard provisioning of DB systems (using Oracle Automatic Storage Management (ASM) as your storage management software), the following database releases are supported: -- Oracle Database 23ai - Oracle Database 21c - Oracle Database 19c - Oracle Database 18c (18.0) @@ -33,7 +32,6 @@ For standard provisioning of DB systems (using Oracle Automatic Storage Manageme For fast provisioning of single-node virtual machine database systems (using Logical Volume Manager as your storage management software), the following database releases are supported: -- Oracle Database 23ai - Oracle Database 21c - Oracle Database 19c - Oracle Database 18c @@ -45,33 +43,34 @@ For fast provisioning of single-node virtual machine database systems (using Log To deploy OraOperator, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. After the Oracle Database Operator is deployed, you can see the DB operator pods running in the Kubernetes Cluster. As part of the OraOperator deployment, the DBCS Controller is deployed as a CRD (Custom Resource Definition). The following screen output is an example of such a deployment: -```bash +``` [root@test-server oracle-database-operator]# kubectl get ns NAME STATUS AGE -cert-manager Active 33d -default Active 118d -kube-node-lease Active 118d -kube-public Active 118d -kube-system Active 118d -oracle-database-operator-system Active 10m <<<< namespace to deploy the Oracle Database Operator +cert-manager Active 2m5s +default Active 125d +kube-node-lease Active 125d +kube-public Active 125d +kube-system Active 125d +oracle-database-operator-system Active 17s <<<< namespace to deploy the Oracle Database Operator [root@test-server oracle-database-operator]# kubectl get all -n oracle-database-operator-system NAME READY STATUS RESTARTS AGE -pod/oracle-database-operator-controller-manager-678f96f5f4-f4rhq 1/1 Running 0 10m -pod/oracle-database-operator-controller-manager-678f96f5f4-plxcp 1/1 Running 0 10m -pod/oracle-database-operator-controller-manager-678f96f5f4-qgcg8 1/1 Running 0 10m - +pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 0 28s +pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 0 28s +pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 0 28s + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.197.164 8443/TCP 11m -service/oracle-database-operator-webhook-service ClusterIP 10.96.35.62 443/TCP 11m - +service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 29s +service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 29s + NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 11m - +deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 29s + NAME DESIRED CURRENT READY AGE -replicaset.apps/oracle-database-operator-controller-manager-6657bfc664 0 0 0 11m -replicaset.apps/oracle-database-operator-controller-manager-678f96f5f4 3 3 3 10m +replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 29s +[root@docker-test-server oracle-database-operator]# + [root@test-server oracle-database-operator]# kubectl get crd NAME CREATED AT @@ -98,19 +97,19 @@ Before you deploy a DBCS system in OCI using the Oracle DB Operator DBCS Control ## 1. Create a Kubernetes Configmap. For example: We are creating a Kubernetes Configmap named `oci-cred` using the OCI account we are using as below: -```bash +``` kubectl create configmap oci-cred \ ---from-literal=tenancy= \ ---from-literal=user= \ ---from-literal=fingerprint= \ +--from-literal=tenancy=ocid1.tenancy.oc1..................67iypsmea \ +--from-literal=user=ocid1.user.oc1..aaaaaaaaxw3i...............ce6qzdrnmq \ +--from-literal=fingerprint=b2:7c:a8:d5:44:f5.....................:9a:55 \ --from-literal=region=us-phoenix-1 ``` ## 2. Create a Kubernetes secret `oci-privatekey` using the OCI Pem key taken from OCI console for the account you are using: -```bash -#---assuming the OCI Pem key to be "/root/.oci/oci_api_key.pem" +``` +-- assuming the OCI Pem key to be "/root/.oci/oci_api_key.pem" kubectl create secret generic oci-privatekey --from-file=privatekey=/root/.oci/oci_api_key.pem ``` @@ -119,8 +118,8 @@ kubectl create secret generic oci-privatekey --from-file=privatekey=/root/.oci/o ## 3. Create a Kubernetes secret named `admin-password`; This passward must meet the minimum passward requirements for the OCI BDBCS Service. For example: -```bash -#-- assuming the passward has been added to a text file named "admin-password": +``` +-- assuming the passward has been added to a text file named "admin-password": kubectl create secret generic admin-password --from-file=./admin-password -n default ``` @@ -129,8 +128,8 @@ kubectl create secret generic admin-password --from-file=./admin-password -n def ## 4. Create a Kubernetes secret named `tde-password`; this passward must meet the minimum passward requirements for the OCI BDBCS Service. For example: -```bash -# -- assuming the passward has been added to a text file named "tde-password": +``` +-- assuming the passward has been added to a text file named "tde-password": kubectl create secret generic tde-password --from-file=./tde-password -n default ``` @@ -138,7 +137,7 @@ kubectl create secret generic tde-password --from-file=./tde-password -n default ## 5. Create an ssh key pair, and use its public key to create a Kubernetes secret named `oci-publickey`; the private key for this public key can be used later to access the DBCS system's host machine using ssh: -```bash +``` [root@test-server DBCS]# ssh-keygen -N "" -C "DBCS_System"-`date +%Y%m` -P "" Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): @@ -180,9 +179,6 @@ For more informatoin about the multiple use cases available to you to deploy and [8. Create BDBCS with All Parameters with Storage Management as LVM](./provisioning/dbcs_service_with_all_parameters_lvm.md) [9. Create BDBCS with All Parameters with Storage Management as ASM](./provisioning/dbcs_service_with_all_parameters_asm.md) [10. Deploy a 2 Node RAC DB System using OCI BDBCS Service](./provisioning/dbcs_service_with_2_node_rac.md) -[11. Create PDB to an existing DBCS System already deployed in OCI Base DBCS Service](./provisioning/create_pdb.md) -[12. Create Base DBCS with PDB in OCI](./provisioning/create_dbcs_with_pdb.md) -[13. Delete PDB of an existing Base DBCS in OCI](./provisioning/delete_pdb.md) ## Connecting to OCI DBCS database deployed using Oracle DB Operator DBCS Controller diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md index c58f2ba9..6fcff5de 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md @@ -14,16 +14,16 @@ This example uses `bind_to_existing_dbcs_system.yaml` to bind to an existing DBC Use the file: [bind_to_existing_dbcs_system.yaml](./bind_to_existing_dbcs_system.yaml) for this use case as below: 1. Deploy the .yaml file: -```bash -[root@docker-test-server DBCS]# kubectl apply -f bind_to_existing_dbcs_system.yaml +```sh +[root@docker-test-server DBCS]# kubectl apply -f bind_dbcs.yaml dbcssystem.database.oracle.com/dbcssystem-existing created ``` -2. Monitor the Oracle DB Leader Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. -```bash +``` [root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml index e6b02db7..49647229 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml @@ -3,6 +3,6 @@ kind: DbcsSystem metadata: name: dbcssystem-existing spec: - id: "ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza" + id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" diff --git a/docs/dbcs/provisioning/create_dbcs_with_pdb.md b/docs/dbcs/provisioning/create_dbcs_with_pdb.md deleted file mode 100644 index 9deb0c77..00000000 --- a/docs/dbcs/provisioning/create_dbcs_with_pdb.md +++ /dev/null @@ -1,55 +0,0 @@ -# Deploy a DBCS DB System using OCI DBCS Service alongwith PDB - -In this use case, an OCI DBCS system is deployed using Oracle DB Operator DBCS controller along with PDB configuration - -**NOTE** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. - -Also, create a Kubernetes secret `pdb-password` using the file: - -```bash -#---assuming the PDB password is in ./pdb-password file" - -kubectl create secret generic pdb-password --from-file=./pdb-password -n default -``` - -This example uses `dbcs_service_with_pdb.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: - -- OCI Configmap as `oci-cred` -- OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:US-ASHBURN-AD-1` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` -- Database Admin Credential as `admin-password` -- Database Name as `dbsystem24` -- Oracle Database Software Image Version as `21c` -- Database Workload Type as Transaction Processing i.e. `OLTP` -- Database Hostname Prefix as `host24` -- Cpu Core Count as `1` -- Oracle VMDB Shape as `VM.Standard2.1` -- SSH Public key for the DBCS system being deployed as `oci-publickey` -- domain `subd215df3e6.k8stest.oraclevcn.com` -- OCID of the Subnet as `ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua` -- PDB Name as `pdb_sauahuja_11` -- TDE Wallet Password as `tde-password` -- PDB Admin Password as `pdb-password` - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). - -Use the file: [dbcs_service_with_pdb.yaml](./dbcs_service_with_pdb.yaml) for this use case as below: - -1. Deploy the .yaml file: -```bash -[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_with_pdb.yaml -dbcssystem.database.oracle.com/dbcssystem-create-with-pdb created -``` - -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. - -NOTE: Check the DB Operator Pod name in your environment. - -```bash -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./dbcs_service_with_pdb_sample_output.log) is the sample output for a DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with PDB configurations. diff --git a/docs/dbcs/provisioning/create_pdb.md b/docs/dbcs/provisioning/create_pdb.md deleted file mode 100644 index 610ccd41..00000000 --- a/docs/dbcs/provisioning/create_pdb.md +++ /dev/null @@ -1,55 +0,0 @@ -# Create PDB to an existing DBCS System - -In this use case, an existing OCI DBCS system deployed earlier is going to have PDB/PDBs created. Its a 2 Step operation. - -In order to create PDBs to an existing DBCS system, the steps will be: - -1. Bind the existing DBCS System to DBCS Controller. -2. Apply the change to create PDBs. - -**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. - -As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- -```bash -kubectl get dbcssystems -NAME AGE -dbcssystem-existing 3m33s -``` -Below proceeding further create PDB Admin Password which is going to used as name suggests. - -Create a Kubernetes secret `pdb-password` using the file: - -```bash -#---assuming the PDB password is in ./pdb-password file" - -kubectl create secret generic pdb-password --from-file=./pdb-password -n default -``` - -This example uses `createpdb_in_existing_dbcs_system_list.yaml` to scale up a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: - -- OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza` -- OCI Configmap as `oci-cred` -- OCI Secret as `oci-privatekey` -- TDE Wallet Password as `tde-password` -- PDB Admin Password as `pdb-password` -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). - -Use the file: [createpdb_in_existing_dbcs_system_list.yaml](./createpdb_in_existing_dbcs_system_list.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -[root@docker-test-server DBCS]# kubectl apply -f createpdb_in_existing_dbcs_system_list.yaml -dbcssystem.database.oracle.com/dbcssystem-existing configured -``` - -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. - -NOTE: Check the DB Operator Pod name in your environment. - -``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./createpdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for creation of PDBs on an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml deleted file mode 100644 index 84b36902..00000000 --- a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: DbcsSystem -metadata: - name: dbcssystem-existing - namespace: default -spec: - id: "ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza" - ociConfigMap: "oci-cred" - ociSecret: "oci-privatekey" - pdbConfigs: - - pdbName: "pdb_sauahuja_sdk_11" - tdeWalletPassword: "tde-password" - pdbAdminPassword: "pdb-password" - shouldPdbAdminAccountBeLocked: false - freeformTags: - Department: "Finance" - - pdbName: "pdb_sauahuja_sdk_12" - tdeWalletPassword: "tde-password" - pdbAdminPassword: "pdb-password" - shouldPdbAdminAccountBeLocked: false - freeformTags: - Department: "HR" - - pdbName: "pdb_sauahuja_sdk_13" - tdeWalletPassword: "tde-password" - pdbAdminPassword: "pdb-password" - shouldPdbAdminAccountBeLocked: false - freeformTags: - Department: "IT" \ No newline at end of file diff --git a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log deleted file mode 100644 index b527a68b..00000000 --- a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log +++ /dev/null @@ -1,17 +0,0 @@ -2024-07-01T10:25:10Z INFO DBInst after assignment {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-existing"}} -2024-07-01T10:25:10Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a"]} -2024-07-01T10:25:10Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-existing"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} -2024-07-01T10:25:10Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBName": "pdb_sauahuja_sdk_11"} -2024-07-01T10:25:12Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c"} -2024-07-01T10:25:12Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c"} -2024-07-01T10:25:12Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBName": "pdb_sauahuja_sdk_11"} -2024-07-01T10:25:12Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBName": "pdb_sauahuja_sdk_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq"} -2024-07-01T10:25:13Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} -2024-07-01T10:25:43Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} -2024-07-01T10:26:13Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} -2024-07-01T10:26:43Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} -2024-07-01T10:27:13Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} -2024-07-01T10:27:44Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} -2024-07-01T10:28:14Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "PROVISIONING"} -2024-07-01T10:28:44Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq", "Status": "AVAILABLE"} -2024-07-01T10:28:44Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "7c2742a5-2d7e-443b-b066-90194b7b307c", "PDBName": "pdb_sauahuja_sdk_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya3wf2q75salpwd5dgx3mdrl2mxqemjome56y5bny5k6lq"} \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml b/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml deleted file mode 100644 index 2ab9f2cf..00000000 --- a/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: DbcsSystem -metadata: - name: dbcssystem-create-with-pdb - namespace: default -spec: - ociConfigMap: "oci-cred" - ociSecret: "oci-privatekey" - dbSystem: - availabilityDomain: "OLou:US-ASHBURN-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" - dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" - dbName: "dbsys" - displayName: "dbsystem24" - licenseModel: "BRING_YOUR_OWN_LICENSE" - dbVersion: "21c" - dbWorkload: "OLTP" - hostName: "host24" - shape: "VM.Standard3.Flex" - cpuCoreCount: 1 - domain: "subd215df3e6.k8stest.oraclevcn.com" - sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua" - pdbConfigs: - - pdbName: "pdb_sauahuja_11" - tdeWalletPassword: "tde-password" - pdbAdminPassword: "pdb-password" - shouldPdbAdminAccountBeLocked: false - freeformTags: - Department: "Finance" - - pdbName: "pdb_sauahuja_12" - tdeWalletPassword: "tde-password" - pdbAdminPassword: "pdb-password" - shouldPdbAdminAccountBeLocked: false - freeformTags: - Department: "HR" \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log deleted file mode 100644 index 8932a87a..00000000 --- a/docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log +++ /dev/null @@ -1,103 +0,0 @@ -2024-07-01T11:12:38Z INFO Reconciling DbSystemDetails {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "name": {"name":"dbcssystem-create-with-pdb","namespace":"default"}} -2024-07-01T11:12:38Z INFO OCI provider configured succesfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:12:39Z INFO DbcsSystem DBSystem provisioning {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:12:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:13:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:14:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:15:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:16:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:17:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:18:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:19:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:20:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:21:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:22:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:23:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:24:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:25:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:26:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:27:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:28:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:29:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:30:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:31:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:32:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:33:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:34:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:35:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:36:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:37:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:38:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:39:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:40:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:41:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:42:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:43:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:44:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:45:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:46:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:47:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:48:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:49:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:50:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:51:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:52:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:53:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:54:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:55:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:56:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:57:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:58:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T11:59:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:00:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:01:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:02:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:03:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:04:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:05:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:06:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:07:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:08:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:09:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:10:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:11:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:12:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:13:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:14:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:15:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:16:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:17:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:18:55Z INFO DbcsSystem system provisioned succesfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:18:56Z INFO DBInst after assignment {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-create-with-pdb"}} -2024-07-01T12:18:56Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyavvnsgu4rnbsquagb7y7egz3ixsl5yvss32353cfdwn6a"]} -2024-07-01T12:18:56Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-create-with-pdb"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htyavvnsgu4rnbsquagb7y7egz3ixsl5yvss32353cfdwn6a", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} -2024-07-01T12:18:56Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_11"} -2024-07-01T12:18:57Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:18:57Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:18:57Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_11"} -2024-07-01T12:18:58Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja"} -2024-07-01T12:18:58Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} -2024-07-01T12:19:28Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} -2024-07-01T12:19:59Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} -2024-07-01T12:20:29Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} -2024-07-01T12:20:59Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} -2024-07-01T12:21:29Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} -2024-07-01T12:22:00Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "PROVISIONING"} -2024-07-01T12:22:30Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja", "Status": "AVAILABLE"} -2024-07-01T12:22:30Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyamut5sfjwzxrjvxdqfkmldczhau2yglw4p5h52wnsenja"} -2024-07-01T12:22:30Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyavvnsgu4rnbsquagb7y7egz3ixsl5yvss32353cfdwn6a"]} -2024-07-01T12:22:30Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-create-with-pdb"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htyavvnsgu4rnbsquagb7y7egz3ixsl5yvss32353cfdwn6a", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} -2024-07-01T12:22:30Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_12"} -2024-07-01T12:22:31Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:22:31Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9"} -2024-07-01T12:22:31Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_12"} -2024-07-01T12:22:32Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_12", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la"} -2024-07-01T12:22:32Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} -2024-07-01T12:23:02Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} -2024-07-01T12:23:32Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} -2024-07-01T12:24:03Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} -2024-07-01T12:24:33Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} -2024-07-01T12:25:03Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} -2024-07-01T12:25:33Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "PROVISIONING"} -2024-07-01T12:26:03Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la", "Status": "AVAILABLE"} -2024-07-01T12:26:03Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "027c87f6-a845-4dcb-8d33-57ce820bc0a9", "PDBName": "pdb_sauahuja_12", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaac5xbyxvptsx3wchariidlwmfpywruk2pa4cazhmk4la"} \ No newline at end of file diff --git a/docs/dbcs/provisioning/delete_pdb.md b/docs/dbcs/provisioning/delete_pdb.md deleted file mode 100644 index 84d676bc..00000000 --- a/docs/dbcs/provisioning/delete_pdb.md +++ /dev/null @@ -1,50 +0,0 @@ -# Delete PDB of an existing DBCS System - -In this use case, an existing OCI DBCS system deployed earlier is going to have PDB/PDBs deleted. Its a 2 Step operation. - -In order to create PDBs to an existing DBCS system, the steps will be: - -1. Bind the existing DBCS System to DBCS Controller. -2. Apply the change to delete PDBs. - -**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. - -As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- -```bash -kubectl get dbcssystems -NAME AGE -dbcssystem-existing 3m33s -``` - -This example uses `deletepdb_in_existing_dbcs_system_list.yaml` to delete PDBs of a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: - -- OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza` -- OCI Configmap as `oci-cred` -- OCI Secret as `oci-privatekey` -- PDB Name to be deleted e.g `pdb_sauahuja_11` and `pdb_sauahuja_12` -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). - -Use the file: [deletepdb_in_existing_dbcs_system_list.yaml](./deletepdb_in_existing_dbcs_system_list.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -[root@docker-test-server DBCS]# kubectl apply -f deletepdb_in_existing_dbcs_system_list.yaml -dbcssystem.database.oracle.com/dbcssystem-existing configured -``` - -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deletion of PDBs. - -NOTE: Check the DB Operator Pod name in your environment. - -```bash -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system -``` - -3. Remove DBCS Systems resource- -```bash -kubectl delete -f deletepdb_in_existing_dbcs_system_list.yaml -``` - -## Sample Output - -[Here](./deletepdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for deletion of PDBs from an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. \ No newline at end of file diff --git a/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml deleted file mode 100644 index fed3ec6c..00000000 --- a/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml +++ /dev/null @@ -1,13 +0,0 @@ -kind: DbcsSystem -metadata: - name: dbcssystem-existing - namespace: default -spec: - id: "ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza" - ociConfigMap: "oci-cred" - ociSecret: "oci-privatekey" - pdbConfigs: - - pdbName: "pdb_sauahuja_11" - isDelete: true - - pdbName: "pdb_sauahuja_12" - isDelete: true \ No newline at end of file diff --git a/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log deleted file mode 100644 index a4f75fa5..00000000 --- a/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log +++ /dev/null @@ -1,8 +0,0 @@ -2024-07-01T12:34:44Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a"]} -2024-07-01T12:34:44Z INFO Deleting pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_11"} -2024-07-01T12:34:44Z INFO PluggableDatabaseId is not specified, getting pluggable databaseID {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808"} -2024-07-01T12:34:45Z INFO Successfully deleted pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_11"} -2024-07-01T12:34:46Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a"]} -2024-07-01T12:34:46Z INFO Deleting pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_12"} -2024-07-01T12:34:46Z INFO PluggableDatabaseId is not specified, getting pluggable databaseID {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808"} -2024-07-01T12:34:47Z INFO Successfully deleted pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_12"} \ No newline at end of file diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index ae217350..5c0a56dd 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -165,8 +165,6 @@ spec: type: string displayName: type: string - isLongTermBackup: - type: boolean ociConfig: description: "*********************** *\tOCI config ***********************" properties: @@ -175,8 +173,6 @@ spec: secretName: type: string type: object - retentionPeriodInDays: - type: integer target: description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: @@ -1268,7 +1264,7 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup Config Network Struct + description: DB Backup COnfig Network Struct properties: autoBackupEnabled: type: boolean @@ -1352,39 +1348,6 @@ spec: type: string ociSecret: type: string - pdbConfig: - description: PDBConfig defines details of PDB struct for DBCS systems - properties: - containerDatabaseId: - description: The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the CDB - type: string - freeformTags: - additionalProperties: - type: string - description: '// Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). // Example: `{"Department": "Finance"}`' - type: object - isDelete: - description: // Whether to delete the PDB mentioned - type: boolean - pdbAdminPassword: - description: // A strong password for PDB Admin. The password must be at least nine characters and contain at least two uppercase, two lowercase, two numbers, and two special characters. The special characters must be _, \#, or -. - type: string - pdbName: - description: The name for the pluggable database (PDB). The name is unique in the context of a Database. The name must begin with an alphabetic character and can contain a maximum of thirty alphanumeric characters. Special characters are not permitted. The pluggable database name should not be same as the container database name. - type: string - pluggableDatabaseId: - description: The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the PDB - type: string - shouldPdbAdminAccountBeLocked: - description: // The locked mode of the pluggable database admin account. If false, the user needs to provide the PDB Admin Password to connect to it. // If true, the pluggable database will be locked and user cannot login to it. - type: boolean - tdeWalletPassword: - description: // The existing TDE wallet password of the CDB. - type: string - required: - - containerDatabaseId - - pdbName - type: object required: - ociConfigMap type: object @@ -3074,11 +3037,10 @@ rules: - "" resources: - configmaps + - deployments - events - pods - - pods/exec - - pods/log - - replicasets + - secrets - services verbs: - create @@ -3092,8 +3054,12 @@ rules: - "" resources: - configmaps - - namespaces - - secrets + - events + - pods + - pods/exec + - pods/log + - replicasets + - services verbs: - create - delete @@ -3106,6 +3072,7 @@ rules: - "" resources: - configmaps + - namespaces - secrets verbs: - create @@ -3118,10 +3085,8 @@ rules: - apiGroups: - "" resources: - - deployments - - events - - pods - - services + - configmaps + - secrets verbs: - create - delete @@ -3160,7 +3125,6 @@ rules: verbs: - get - list - - watch - apiGroups: - '''''' resources: @@ -3177,12 +3141,6 @@ rules: - apps resources: - configmaps - verbs: - - get - - list -- apiGroups: - - apps - resources: - deployments - pods - services @@ -3248,26 +3206,6 @@ rules: - patch - update - watch -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - "" resources: @@ -3564,6 +3502,7 @@ rules: - apiGroups: - monitoring.coreos.com resources: + - prometheusrules - servicemonitors verbs: - create @@ -3899,26 +3838,6 @@ webhooks: resources: - singleinstancedatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-observability-oracle-com-v1alpha1-databaseobserver - failurePolicy: Fail - name: mdatabaseobserver.kb.io - rules: - - apiGroups: - - observability.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - databaseobservers - sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -4134,26 +4053,6 @@ webhooks: resources: - singleinstancedatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-observability-oracle-com-v1alpha1-databaseobserver - failurePolicy: Fail - name: vdatabaseobserver.kb.io - rules: - - apiGroups: - - observability.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - databaseobservers - sideEffects: None --- apiVersion: apps/v1 kind: Deployment @@ -4180,7 +4079,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator imagePullPolicy: Always name: manager ports: From e9ea2bdf0b628a9045893fec394c4b38b0dee00f Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Wed, 31 Jul 2024 17:34:07 +0000 Subject: [PATCH 109/414] Update README.md --- .gitignore | 2 +- README.md | 11 ++++++----- apis/database/v1alpha1/shardingdatabase_types.go | 3 ++- docs/multitenant/usecase01/makefile | 1 + ...harding_provisioning_without_db_gold_image.md | 2 +- ...sharding_scale_in_delete_an_existing_shard.md | 2 +- .../udsharding_shard_prov.yaml | 2 +- .../udsharding_shard_prov_clone.yaml | 2 +- .../udsharding_shard_prov_clone_across_ads.yaml | 2 +- .../udsharding_shard_prov_extshard.yaml | 2 +- .../udsharding_shard_prov_memory_cpu.yaml | 2 +- .../udsharding_shard_prov_send_notification.yaml | 2 +- oracle-database-operator.yaml | 16 ++++++++++++++++ 13 files changed, 34 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 0af8ddc4..e1f4168e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,4 @@ ords/*zip # development .idea -.local \ No newline at end of file +.local diff --git a/README.md b/README.md index c4b17b7d..cad25dec 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ In this v1.1.0 production release, `OraOperator` supports the following database * Oracle Autonomous Database: * Oracle Autonomous Database shared Oracle Cloud Infrastructure (OCI) (ADB-S) * Oracle Autonomous Database on dedicated Cloud infrastructure (ADB-D) - * Oracle Autonomous Container Database (ACD) (infrastructure) the infrastructure for provisioning Autonomous Databases. + * Oracle Autonomous Container Database (ACD) (infrastructure) is the infrastructure for provisioning Autonomous Databases. * Containerized Single Instance databases (SIDB) deployed in the Oracle Kubernetes Engine (OKE) and any k8s where OraOperator is deployed * Containerized Sharded databases (SHARDED) deployed in OKE and any k8s where OraOperator is deployed * Oracle Multitenant Databases (CDB/PDBs) @@ -21,6 +21,7 @@ Oracle will continue to extend `OraOperator` to support additional Oracle Databa ## New in V1.1.0 Release * Namespace scope deployment option +* Enhanced security with namespace scope deployment option * Support for Oracle Database 23ai Free (with SIDB) * Automatic Storage Expansion for SIDB and Sharded DB * User-Defined Sharding @@ -28,9 +29,9 @@ Oracle will continue to extend `OraOperator` to support additional Oracle Databa * Execute custom scripts during DB setup/startup * Patching for SIDB Primary/Standby in Data Guard * Long-term backup for Autonomous Databases (ADB): Support for [long-term retention backup](https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/backup-long-term.html) and removed support for the deprecated mandatory backup -* Wallet expiry date for ADB: A user-freindly enhancement to display wallet expiry date in the status of assiciated ADB -* Wait-for-Completion option for ADB: Supports `kubectl wait` command that allows user to wait a specific condition on ADB -* OKE workload Identify: Supports OKE workload indentity authentication method. For more details, refer to [Oracle Autonomous Database (ADB) Prerequisites](docs/adb/ADB_PREREQUISITES.md#authorized-with-oke-workload-identity) +* Wallet expiry date for ADB: A user-friendly enhancement to display the wallet expiry date in the status of the associated ADB +* Wait-for-Completion option for ADB: Supports `kubectl wait` command that allows the user to wait for a specific condition on ADB +* OKE workload Identify: Supports OKE workload identity authentication method (i.e., uses OKE credentials). For more details, refer to [Oracle Autonomous Database (ADB) Prerequisites](docs/adb/ADB_PREREQUISITES.md#authorized-with-oke-workload-identity) * Database Observability (Preview - Metrics) ## Features Summary @@ -47,7 +48,7 @@ This release of Oracle Database Operator for Kubernetes (the operator) supports * Oracle Database Observability: create, patch, delete databaseObserver resources * Watch over a set of namespaces or all the namespaces in the cluster using the "WATCH_NAMESPACE" env variable of the operator deployment -The upcoming releases will support new configurations, operations and capabilities. +The upcoming releases will support new configurations, operations, and capabilities. ## Release Status diff --git a/apis/database/v1alpha1/shardingdatabase_types.go b/apis/database/v1alpha1/shardingdatabase_types.go index bc759094..ffc17ab0 100644 --- a/apis/database/v1alpha1/shardingdatabase_types.go +++ b/apis/database/v1alpha1/shardingdatabase_types.go @@ -105,7 +105,8 @@ type ShardingDatabaseStatus struct { Shard map[string]string `json:"shards,omitempty"` Catalog map[string]string `json:"catalogs,omitempty"` - Gsm GsmStatus `json:"gsm,omitempty"` + + Gsm GsmStatus `json:"gsm,omitempty"` // +patchMergeKey=type // +patchStrategy=merge diff --git a/docs/multitenant/usecase01/makefile b/docs/multitenant/usecase01/makefile index ae82778d..90020f04 100644 --- a/docs/multitenant/usecase01/makefile +++ b/docs/multitenant/usecase01/makefile @@ -178,6 +178,7 @@ createimage: $(CP) $(DOCKERFILE) . $(CP) $(RUNSCRIPT) . $(DOCKER) build -t $(IMAGE) . + $(RM) ./Dockerfile ./runOrdsSSL.sh tagimage: @echo "TAG IMAGE" diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md index b9042c64..b223d1af 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md @@ -34,4 +34,4 @@ Use the file: [ssharding_shard_prov.yaml](./ssharding_shard_prov.yaml) for this # Check the logs of a particular pod. For example, to check status of pod "shard1-0": kubectl logs -f pod/shard1-0 -n shns - ``` + ``` \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md index 3b93b284..2c4cbfc2 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md @@ -62,4 +62,4 @@ To check the status, use the following command: # Check the status of the chunks: gdsctl config chunks - ``` \ No newline at end of file + ``` diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml index 0692d04b..9b565b73 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml @@ -55,4 +55,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns \ No newline at end of file + namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml index bfe8da53..adc2271f 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml @@ -80,4 +80,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns \ No newline at end of file + namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml index bcaaf457..28f36608 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml @@ -88,4 +88,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns \ No newline at end of file + namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml index 0609c4b4..f45d421f 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml @@ -64,4 +64,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns \ No newline at end of file + namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml index cfdb62ea..e663aa65 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml @@ -86,4 +86,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns \ No newline at end of file + namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml index 0cf9c8c5..afd951fe 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml @@ -82,4 +82,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns \ No newline at end of file + namespace: shns diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 5c0a56dd..f9d718d6 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -3050,6 +3050,22 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - configmaps + - events + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: From a0986d1061960ee0d07614d488b93721abd79639 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Wed, 31 Jul 2024 19:17:48 +0000 Subject: [PATCH 110/414] Added fixes --- oracle-database-operator.yaml | 92 ++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index f9d718d6..1b0efafa 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -165,6 +165,8 @@ spec: type: string displayName: type: string + isLongTermBackup: + type: boolean ociConfig: description: "*********************** *\tOCI config ***********************" properties: @@ -173,6 +175,8 @@ spec: secretName: type: string type: object + retentionPeriodInDays: + type: integer target: description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: @@ -3037,7 +3041,6 @@ rules: - "" resources: - configmaps - - deployments - events - pods - secrets @@ -3056,7 +3059,9 @@ rules: - configmaps - events - pods - - secrets + - pods/exec + - pods/log + - replicasets - services verbs: - create @@ -3070,12 +3075,8 @@ rules: - "" resources: - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services + - namespaces + - secrets verbs: - create - delete @@ -3088,7 +3089,6 @@ rules: - "" resources: - configmaps - - namespaces - secrets verbs: - create @@ -3101,8 +3101,10 @@ rules: - apiGroups: - "" resources: - - configmaps - - secrets + - deployments + - events + - pods + - services verbs: - create - delete @@ -3141,6 +3143,7 @@ rules: verbs: - get - list + - watch - apiGroups: - '''''' resources: @@ -3157,6 +3160,12 @@ rules: - apps resources: - configmaps + verbs: + - get + - list +- apiGroups: + - apps + resources: - deployments - pods - services @@ -3222,6 +3231,26 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -3518,7 +3547,6 @@ rules: - apiGroups: - monitoring.coreos.com resources: - - prometheusrules - servicemonitors verbs: - create @@ -3854,6 +3882,26 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-observability-oracle-com-v1alpha1-databaseobserver + failurePolicy: Fail + name: mdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -4069,6 +4117,26 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-observability-oracle-com-v1alpha1-databaseobserver + failurePolicy: Fail + name: vdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None --- apiVersion: apps/v1 kind: Deployment From 72216713d7e410d45df63c9b0589163d3e0cce90 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Wed, 31 Jul 2024 19:29:26 +0000 Subject: [PATCH 111/414] Added fixes --- .../v1alpha1/zz_generated.deepcopy.go | 41 +++++++++++++++++++ ...se.oracle.com_singleinstancedatabases.yaml | 17 ++++++++ oracle-database-operator.yaml | 33 +++++++-------- 3 files changed, 75 insertions(+), 16 deletions(-) diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index f1eaf503..10b34ca7 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -2470,6 +2470,46 @@ func (in *SingleInstanceDatabasePersistence) DeepCopy() *SingleInstanceDatabaseP return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseResource) DeepCopyInto(out *SingleInstanceDatabaseResource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseResource. +func (in *SingleInstanceDatabaseResource) DeepCopy() *SingleInstanceDatabaseResource { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseResources) DeepCopyInto(out *SingleInstanceDatabaseResources) { + *out = *in + if in.Requests != nil { + in, out := &in.Requests, &out.Requests + *out = new(SingleInstanceDatabaseResource) + **out = **in + } + if in.Limits != nil { + in, out := &in.Limits, &out.Limits + *out = new(SingleInstanceDatabaseResource) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseResources. +func (in *SingleInstanceDatabaseResources) DeepCopy() *SingleInstanceDatabaseResources { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseResources) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SingleInstanceDatabaseSpec) DeepCopyInto(out *SingleInstanceDatabaseSpec) { *out = *in @@ -2510,6 +2550,7 @@ func (in *SingleInstanceDatabaseSpec) DeepCopyInto(out *SingleInstanceDatabaseSp *out = new(SingleInstanceDatabaseInitParams) **out = **in } + in.Resources.DeepCopyInto(&out.Resources) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseSpec. diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index acfa2bea..1c011e17 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -175,6 +175,23 @@ spec: type: integer replicas: type: integer + resources: + properties: + limits: + properties: + cpu: + type: string + memory: + type: string + type: object + requests: + properties: + cpu: + type: string + memory: + type: string + type: object + type: object serviceAccountName: type: string serviceAnnotations: diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 1b0efafa..5d3b43d3 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -2787,6 +2787,23 @@ spec: type: integer replicas: type: integer + resources: + properties: + limits: + properties: + cpu: + type: string + memory: + type: string + type: object + requests: + properties: + cpu: + type: string + memory: + type: string + type: object + type: object serviceAccountName: type: string serviceAnnotations: @@ -3037,22 +3054,6 @@ metadata: creationTimestamp: null name: oracle-database-operator-manager-role rules: -- apiGroups: - - "" - resources: - - configmaps - - events - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - "" resources: From d94909ca250e164282d9c377b41bee26ca34264e Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Tue, 13 Aug 2024 08:38:29 +0000 Subject: [PATCH 112/414] fix bug36752628 --- config/manager/kustomization.yaml | 4 +- controllers/database/pdb_controller.go | 6 +- docs/multitenant/provisioning/add_replica.log | 192 ------- docs/multitenant/provisioning/add_replica.md | 36 -- .../multitenant/provisioning/add_replica.yaml | 40 -- docs/multitenant/provisioning/cdb.log | 279 ---------- docs/multitenant/provisioning/cdb.yaml | 41 -- .../provisioning/cdb_crd_resource.md | 38 -- docs/multitenant/provisioning/cdb_secret.yaml | 17 - docs/multitenant/provisioning/clone_pdb.log | 137 ----- docs/multitenant/provisioning/clone_pdb.md | 38 -- docs/multitenant/provisioning/clone_pdb.yaml | 20 - docs/multitenant/provisioning/create_pdb.log | 139 ----- docs/multitenant/provisioning/create_pdb.md | 37 -- docs/multitenant/provisioning/create_pdb.yaml | 27 - docs/multitenant/provisioning/delete_pdb.log | 157 ------ docs/multitenant/provisioning/delete_pdb.md | 37 -- docs/multitenant/provisioning/delete_pdb.yaml | 16 - .../example_setup_using_oci_oke_cluster.md | 37 -- docs/multitenant/provisioning/known_issues.md | 49 -- docs/multitenant/provisioning/modify_pdb.log | 181 ------- docs/multitenant/provisioning/modify_pdb.md | 69 --- .../provisioning/modify_pdb_close.yaml | 18 - .../provisioning/modify_pdb_open.yaml | 18 - docs/multitenant/provisioning/ords_image.log | 503 ------------------ docs/multitenant/provisioning/ords_image.md | 64 --- docs/multitenant/provisioning/pdb.yaml | 27 - .../provisioning/pdb_crd_resource.md | 0 docs/multitenant/provisioning/pdb_secret.yaml | 13 - docs/multitenant/provisioning/plug_pdb.log | 100 ---- docs/multitenant/provisioning/plug_pdb.md | 44 -- docs/multitenant/provisioning/plug_pdb.yaml | 22 - docs/multitenant/provisioning/unplug_pdb.log | 165 ------ docs/multitenant/provisioning/unplug_pdb.md | 39 -- docs/multitenant/provisioning/unplug_pdb.yaml | 17 - .../provisioning/validation_error.md | 73 --- docs/multitenant/usecase01/ca.crt | 25 + docs/multitenant/usecase01/ca.key | 27 + docs/multitenant/usecase01/ca.srl | 1 + docs/multitenant/usecase01/extfile.txt | 1 + docs/multitenant/usecase01/makefile | 12 +- docs/multitenant/usecase01/pdb_delete.yaml | 34 ++ docs/multitenant/usecase01/server.csr | 18 + docs/multitenant/usecase01/tls.crt | 24 + docs/multitenant/usecase01/tls.key | 28 + oracle-database-operator.yaml | 2 +- 46 files changed, 175 insertions(+), 2697 deletions(-) delete mode 100644 docs/multitenant/provisioning/add_replica.log delete mode 100644 docs/multitenant/provisioning/add_replica.md delete mode 100644 docs/multitenant/provisioning/add_replica.yaml delete mode 100644 docs/multitenant/provisioning/cdb.log delete mode 100644 docs/multitenant/provisioning/cdb.yaml delete mode 100644 docs/multitenant/provisioning/cdb_crd_resource.md delete mode 100644 docs/multitenant/provisioning/cdb_secret.yaml delete mode 100644 docs/multitenant/provisioning/clone_pdb.log delete mode 100644 docs/multitenant/provisioning/clone_pdb.md delete mode 100644 docs/multitenant/provisioning/clone_pdb.yaml delete mode 100644 docs/multitenant/provisioning/create_pdb.log delete mode 100644 docs/multitenant/provisioning/create_pdb.md delete mode 100644 docs/multitenant/provisioning/create_pdb.yaml delete mode 100644 docs/multitenant/provisioning/delete_pdb.log delete mode 100644 docs/multitenant/provisioning/delete_pdb.md delete mode 100644 docs/multitenant/provisioning/delete_pdb.yaml delete mode 100644 docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md delete mode 100644 docs/multitenant/provisioning/known_issues.md delete mode 100644 docs/multitenant/provisioning/modify_pdb.log delete mode 100644 docs/multitenant/provisioning/modify_pdb.md delete mode 100644 docs/multitenant/provisioning/modify_pdb_close.yaml delete mode 100644 docs/multitenant/provisioning/modify_pdb_open.yaml delete mode 100644 docs/multitenant/provisioning/ords_image.log delete mode 100644 docs/multitenant/provisioning/ords_image.md delete mode 100644 docs/multitenant/provisioning/pdb.yaml delete mode 100644 docs/multitenant/provisioning/pdb_crd_resource.md delete mode 100644 docs/multitenant/provisioning/pdb_secret.yaml delete mode 100644 docs/multitenant/provisioning/plug_pdb.log delete mode 100644 docs/multitenant/provisioning/plug_pdb.md delete mode 100644 docs/multitenant/provisioning/plug_pdb.yaml delete mode 100644 docs/multitenant/provisioning/unplug_pdb.log delete mode 100644 docs/multitenant/provisioning/unplug_pdb.md delete mode 100644 docs/multitenant/provisioning/unplug_pdb.yaml delete mode 100644 docs/multitenant/provisioning/validation_error.md create mode 100644 docs/multitenant/usecase01/ca.crt create mode 100644 docs/multitenant/usecase01/ca.key create mode 100644 docs/multitenant/usecase01/ca.srl create mode 100644 docs/multitenant/usecase01/extfile.txt create mode 100644 docs/multitenant/usecase01/pdb_delete.yaml create mode 100644 docs/multitenant/usecase01/server.csr create mode 100644 docs/multitenant/usecase01/tls.crt create mode 100644 docs/multitenant/usecase01/tls.key diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index e7ed68ce..1a9d97d3 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: sharding-operator + newName: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/operatormntnns + newTag: latest diff --git a/controllers/database/pdb_controller.go b/controllers/database/pdb_controller.go index 5c173740..afd7f247 100644 --- a/controllers/database/pdb_controller.go +++ b/controllers/database/pdb_controller.go @@ -44,6 +44,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/json" + //"encoding/pem" "errors" "fmt" @@ -59,6 +60,7 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + //metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -133,7 +135,6 @@ var floodcontrol bool = false // +kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups='',resources=statefulsets/finalizers,verbs=get;list;watch;create;update;patch;delete - // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // TODO(user): Modify the Reconcile function to compare the state specified by @@ -652,6 +653,9 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db return nil } + pdbAdminName = strings.TrimSuffix(pdbAdminName, "\n") + pdbAdminPwd = strings.TrimSuffix(pdbAdminPwd, "\n") + values := map[string]string{ "method": "CREATE", "pdb_name": pdb.Spec.PDBName, diff --git a/docs/multitenant/provisioning/add_replica.log b/docs/multitenant/provisioning/add_replica.log deleted file mode 100644 index 53971443..00000000 --- a/docs/multitenant/provisioning/add_replica.log +++ /dev/null @@ -1,192 +0,0 @@ --- Check the status of CDB CRD Pod(s): - -% kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/cdb-dev-ords-rs-q2b68 1/1 Running 0 29s -pod/oracle-database-operator-controller-manager-76cb674c5c-4nrh8 1/1 Running 0 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd 1/1 Running 1 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-xsv9g 1/1 Running 2 4d10h - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.98.47 8443/TCP 5d1h -service/oracle-database-operator-webhook-service ClusterIP 10.96.166.163 443/TCP 5d1h - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 5d1h - -NAME DESIRED CURRENT READY AGE -replicaset.apps/cdb-dev-ords-rs 1 1 1 31s -replicaset.apps/oracle-database-operator-controller-manager-76cb674c5c 3 3 3 5d1h - - --- .yaml file for the add replica use case: - -% cat add_replica.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "goldcdb" - scanName: "goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com" - dbServer: "goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com" - ordsImage: phx.ocir.io//oracle/ords:21.4.3 - dbPort: 1521 - replicas: 2 - serviceName: "goldcdb_phx1pw.lbsub52b3b1cae.okecluster.oraclevcn.com" - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" - - - - --- Apply the .yaml file: - -% kubectl apply -f add_replica.yaml -cdb.database.oracle.com/cdb-dev configured - - - --- Check the status of the CDB CRD Pod(s): - -% kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/cdb-dev-ords-rs-5bztb 1/1 Running 0 21s << New Pod Added -pod/cdb-dev-ords-rs-q2b68 1/1 Running 0 7m40s -pod/oracle-database-operator-controller-manager-76cb674c5c-4nrh8 1/1 Running 0 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd 1/1 Running 1 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-xsv9g 1/1 Running 2 4d10h - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/cdb-dev-ords ClusterIP None 6m25s -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.98.47 8443/TCP 5d2h -service/oracle-database-operator-webhook-service ClusterIP 10.96.166.163 443/TCP 5d2h - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 5d2h - -NAME DESIRED CURRENT READY AGE -replicaset.apps/cdb-dev-ords-rs 2 2 2 7m42s -replicaset.apps/oracle-database-operator-controller-manager-76cb674c5c 3 3 3 5d2h - - - - - --- Logs from Oracle DB Operator Pod: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T03:24:34Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-cdb", "UID": "19a3fbb6-57e4-4ad2-92c9-a90bb66cefae", "kind": "database.oracle.com/v1alpha1, Kind=CDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"cdbs"}} -2022-06-27T03:24:34Z INFO cdb-webhook validate update {"name": "cdb-dev"} -2022-06-27T03:24:34Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-cdb", "code": 200, "reason": "", "UID": "19a3fbb6-57e4-4ad2-92c9-a90bb66cefae", "allowed": true} -2022-06-27T03:24:34Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:34Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "Ready", "Status": "true"} -2022-06-27T03:24:34Z INFO controllers.CDB Existing Replicas: 1, New Replicas: 2 {"evaluateSpecChange": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:34Z INFO controllers.CDB Current Phase:ValidatingPods {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:24:34Z INFO controllers.CDB Validating Pod creation for :cdb-dev {"validateORDSPod": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:34Z INFO controller.cdb Executing Command : {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:34Z INFO controller.cdb bash -c curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:8888/ords/_/db-api/stable/metadata-catalog/ {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:35Z INFO controllers.CDB Replicas: 2 {"validateORDSPod": "oracle-database-operator-system/cdb-dev", "Ready Pods: ": 1} -2022-06-27T03:24:35Z INFO controllers.CDB Reconcile queued {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:35Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:24:50Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:50Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:24:50Z INFO controllers.CDB Current Phase:ValidatingPods {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:24:50Z INFO controllers.CDB Validating Pod creation for :cdb-dev {"validateORDSPod": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:50Z INFO controller.cdb Executing Command : {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:50Z INFO controller.cdb bash -c curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:8888/ords/_/db-api/stable/metadata-catalog/ {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:50Z INFO controller.cdb Executing Command : {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:50Z INFO controller.cdb bash -c curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:8888/ords/_/db-api/stable/metadata-catalog/ {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:50Z INFO controllers.CDB Replicas: 2 {"validateORDSPod": "oracle-database-operator-system/cdb-dev", "Ready Pods: ": 1} -2022-06-27T03:24:50Z INFO controllers.CDB Reconcile queued {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:24:50Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:25:05Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:25:05Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:25:05Z INFO controllers.CDB Current Phase:ValidatingPods {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:25:05Z INFO controllers.CDB Validating Pod creation for :cdb-dev {"validateORDSPod": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:25:05Z INFO controller.cdb Executing Command : {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:25:05Z INFO controller.cdb bash -c curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:8888/ords/_/db-api/stable/metadata-catalog/ {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:25:05Z INFO controller.cdb Executing Command : {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:25:05Z INFO controller.cdb bash -c curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:8888/ords/_/db-api/stable/metadata-catalog/ {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:25:06Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "CreatingService", "Status": "false"} -2022-06-27T03:25:21Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:25:21Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "CreatingService", "Status": "false"} -2022-06-27T03:25:21Z INFO controllers.CDB Current Phase:CreatingService {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:25:21Z INFO controllers.CDB ORDS Cluster Service already exists {"createORDSSVC": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:25:21Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "Ready", "Status": "false"} -2022-06-27T03:25:36Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:25:36Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "Ready", "Status": "false"} -2022-06-27T03:25:36Z INFO controllers.CDB Current Phase:Ready {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:25:36Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "Ready", "Status": "true"} - - - --- Logs of the newly added CDB CRD Pod: - -% kubectl logs -f pod/cdb-dev-ords-rs-5bztb -n oracle-database-operator-system - -Retrieving information. -Requires to login with administrator privileges to verify Oracle REST Data Services schema. - -Connecting to database user: SYS AS SYSDBA url: jdbc:oracle:thin:@//goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/goldcdb_phx1pw.lbsub52b3b1cae.okecluster.oraclevcn.com - -Retrieving information.. -Completed verifying Oracle REST Data Services schema version 21.4.3.r1170405. -2022-06-27T03:24:40.351Z INFO reloaded pools: [] -2022-06-27T03:24:40.353Z INFO Oracle REST Data Services schema version 21.4.3.r1170405 is installed. -spawn java -jar /opt/oracle/ords/ords.war user sql_admin SQL Administrator -Enter a password for user sql_admin: -Confirm password for user sql_admin: -2022-06-27T03:24:42.034Z INFO Created user: sql_admin in file: /opt/oracle/ords/config/ords/credentials -2022-06-27T03:24:43.666Z INFO Modified: /opt/oracle/ords/config/ords/conf/apex_pu.xml, updated properties: database.api.admin.enabled, db.cdb.adminUser, db.cdb.adminUser.password -2022-06-27T03:24:45.455Z INFO HTTP and HTTP/2 cleartext listening on host: localhost port: 8888 -2022-06-27T03:24:45.520Z INFO The document root is serving static resources located in: /opt/oracle/ords/doc_root -2022-06-27T03:24:47.515Z INFO Configuration properties for: |apex|pu| -db.servicename=goldcdb_phx1pw.lbsub52b3b1cae.okecluster.oraclevcn.com -db.hostname=goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com -database.api.admin.enabled=true -db.password=****** -db.cdb.adminUser.password=****** -database.api.enabled=true -db.cdb.adminUser=C##DBAPI_CDB_ADMIN as SYSDBA -db.username=ORDS_PUBLIC_USER -restEnabledSql.active=true -resource.templates.enabled=true -db.port=1521 -feature.sdw=true -db.connectionType=basic - -2022-06-27T03:24:47.517Z WARNING *** jdbc.MaxLimit in configuration |apex|pu| is using a value of 10, this setting may not be sized adequately for a production environment *** -2022-06-27T03:24:47.517Z WARNING *** jdbc.InitialLimit in configuration |apex|pu| is using a value of 3, this setting may not be sized adequately for a production environment *** -2022-06-27T03:24:51.761Z INFO Oracle REST Data Services initialized -Oracle REST Data Services version : 21.4.3.r1170405 -Oracle REST Data Services server info: jetty/9.4.44.v20210927 diff --git a/docs/multitenant/provisioning/add_replica.md b/docs/multitenant/provisioning/add_replica.md deleted file mode 100644 index 1315dc9f..00000000 --- a/docs/multitenant/provisioning/add_replica.md +++ /dev/null @@ -1,36 +0,0 @@ -# Add a new replicate to an existing CDB CRD Resource using Oracle DB Operator On-Prem Controller - -In this use case, using the Oracle Database Operator On-Prem Controller, you will add a new replica to an existing CDB CRD resource. - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -This example uses `add_replica.yaml` with: - -- CDB CRD resource Name as `cdb-dev` -- Container Database (CDB) Name as `goldcdb` -- Scan Name as `goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com` -- Database Server Name as `goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com` -- ORDS Docker Image as `phx.ocir.io//oracle/ords:21.4.3` -- Database Listener Port as `1521` -- Number of replicas for CDB CRD Resource as 2. - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [config/crd/bases/database.oracle.com_cdbs.yaml](../../../config/crd/bases/database.oracle.com_cdbs.yaml) - -Use the file: [add_replica.yaml](./add_replica.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -[root@test-server oracle-database-operator]# kubectl apply -f add_replica.yaml -``` - -2. Monitor the Oracle DB Operator Pod for the progress of the CDB CRD Resource creation. - -NOTE: Check the DB Operator Pod name in your environment. - -``` -[root@test-server oracle-database-operator]# kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./add_replica.log) is the sample output for a PDB created using Oracle DB Operator On-Prem Controller using file [add_replica.yaml](./add_replica.yaml) diff --git a/docs/multitenant/provisioning/add_replica.yaml b/docs/multitenant/provisioning/add_replica.yaml deleted file mode 100644 index fac2d7ba..00000000 --- a/docs/multitenant/provisioning/add_replica.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "goldcdb" - dbServer: "goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com" - ordsImage: phx.ocir.io//oracle/ords:21.4.3 - dbPort: 1521 - replicas: 2 - serviceName: "goldcdb_phx1pw.lbsub52b3b1cae.okecluster.oraclevcn.com" - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" diff --git a/docs/multitenant/provisioning/cdb.log b/docs/multitenant/provisioning/cdb.log deleted file mode 100644 index 8c3cbdc5..00000000 --- a/docs/multitenant/provisioning/cdb.log +++ /dev/null @@ -1,279 +0,0 @@ --- Check the status of the Oracle DB Operator Pods: - -% kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/oracle-database-operator-controller-manager-76cb674c5c-4nrh8 1/1 Running 0 29h -pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd 1/1 Running 1 29h -pod/oracle-database-operator-controller-manager-76cb674c5c-xsv9g 1/1 Running 2 29h - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.98.47 8443/TCP 45h -service/oracle-database-operator-webhook-service ClusterIP 10.96.166.163 443/TCP 45h - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 45h - -NAME DESIRED CURRENT READY AGE -replicaset.apps/oracle-database-operator-controller-manager-76cb674c5c 3 3 3 45h - - - --- Get the base64 values for the required passwords as below: - -% echo -n "WElcome_21##" | base64 -V0VsY29tZV8yMSMj - -% echo -n "C##DBAPI_CDB_ADMIN" | base64 -QyMjREJBUElfQ0RCX0FETUlO - -% echo -n "sql_admin" | base64 -c3FsX2FkbWlu - -% echo -n "welcome1" | base64 -d2VsY29tZTE= - - --- Add the base64 encoded values against the required variables in cdb_secret.yaml file for this use case: - -% cat cdb_secret.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: "V0VsY29tZV8yMSMj" - sysadmin_pwd: "V0VsY29tZV8yMSMj" - cdbadmin_user: "QyMjREJBUElfQ0RCX0FETUlO" - cdbadmin_pwd: "V0VsY29tZV8yMSMj" - webserver_user: "c3FsX2FkbWlu" - webserver_pwd: "d2VsY29tZTE=" - - --- Check the contents of the cdb.yaml file: -% cat cdb.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "goldcdb" - scanName: "goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com" - dbServer: "goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com" - ordsImage: phx.ocir.io//oracle/ords:21.4.3 - ordsImagePullSecret: "container-registry-secret" - dbPort: 1521 - replicas: 1 - serviceName: "goldcdb_phx1pw.lbsub52b3b1cae.okecluster.oraclevcn.com" - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" - - - --- Apply the .yaml files: - -% kubectl apply -f cdb_secret.yaml -secret/cdb1-secret created - -% kubectl apply -f cdb.yaml -cdb.database.oracle.com/cdb-dev created - - - --- Monitor the Oracle DB Operator Pod during the period when the CDB CRD is getting deployed: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T03:16:44Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:16:44Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "", "Status": "false"} -2022-06-27T03:16:44Z INFO controllers.CDB Adding finalizer {"manageCDBDeletion": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:16:44Z INFO controllers.CDB Current Phase: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:16:44Z INFO controllers.CDB DEFAULT: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "", "Status": "false"} -2022-06-27T03:16:44Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "Initializing", "Status": "false"} -2022-06-27T03:17:00Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:00Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "Initializing", "Status": "false"} -2022-06-27T03:17:00Z INFO controllers.CDB Current Phase:Initializing {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:17:00Z INFO controllers.CDB Verified secrets successfully {"verifySecrets": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:00Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "CreatingPod", "Status": "false"} -2022-06-27T03:17:15Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:15Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "CreatingPod", "Status": "false"} -2022-06-27T03:17:15Z INFO controllers.CDB Current Phase:CreatingPod {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:17:15Z INFO controllers.CDB Creating ORDS Replicaset: cdb-dev-ords-rs {"createORDSInstances": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:15Z INFO controllers.CDB Created ORDS ReplicaSet successfully {"createORDSInstances": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:15Z DEBUG events Normal {"object": {"kind":"CDB","namespace":"oracle-database-operator-system","name":"cdb-dev","uid":"c36e8d5f-6103-4a70-a840-16c6683755ec","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101443216"}, "reason": "CreatedORDSReplicaSet", "message": "Created ORDS Replicaset (Replicas - 1) for cdb-dev"} -2022-06-27T03:17:15Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:17:30Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:30Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:17:30Z INFO controllers.CDB Current Phase:ValidatingPods {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:17:30Z INFO controllers.CDB Validating Pod creation for :cdb-dev {"validateORDSPod": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:30Z INFO controller.cdb Executing Command : {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:30Z INFO controller.cdb bash -c curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:8888/ords/_/db-api/stable/metadata-catalog/ {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:30Z INFO controllers.CDB Replicas: 1 {"validateORDSPod": "oracle-database-operator-system/cdb-dev", "Ready Pods: ": 0} -2022-06-27T03:17:30Z INFO controllers.CDB Reconcile queued {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:30Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:17:45Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:45Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:17:45Z INFO controllers.CDB Current Phase:ValidatingPods {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:17:45Z INFO controllers.CDB Validating Pod creation for :cdb-dev {"validateORDSPod": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:45Z INFO controller.cdb Executing Command : {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:45Z INFO controller.cdb bash -c curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:8888/ords/_/db-api/stable/metadata-catalog/ {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:45Z INFO controllers.CDB Replicas: 1 {"validateORDSPod": "oracle-database-operator-system/cdb-dev", "Ready Pods: ": 0} -2022-06-27T03:17:45Z INFO controllers.CDB Reconcile queued {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:17:45Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:18:00Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:00Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:18:00Z INFO controllers.CDB Current Phase:ValidatingPods {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:18:00Z INFO controllers.CDB Validating Pod creation for :cdb-dev {"validateORDSPod": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:00Z INFO controller.cdb Executing Command : {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:00Z INFO controller.cdb bash -c curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:8888/ords/_/db-api/stable/metadata-catalog/ {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:01Z INFO controllers.CDB Replicas: 1 {"validateORDSPod": "oracle-database-operator-system/cdb-dev", "Ready Pods: ": 0} -2022-06-27T03:18:01Z INFO controllers.CDB Reconcile queued {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:01Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:18:16Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:16Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "ValidatingPods", "Status": "false"} -2022-06-27T03:18:16Z INFO controllers.CDB Current Phase:ValidatingPods {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:18:16Z INFO controllers.CDB Validating Pod creation for :cdb-dev {"validateORDSPod": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:16Z INFO controller.cdb Executing Command : {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:16Z INFO controller.cdb bash -c curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ || curl -sSkv -X GET http://localhost:8888/ords/_/db-api/stable/metadata-catalog/ {"reconciler group": "database.oracle.com", "reconciler kind": "CDB", "name": "cdb-dev", "namespace": "oracle-database-operator-system", "ExecCommand": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:16Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "CreatingService", "Status": "false"} -2022-06-27T03:18:31Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:31Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "CreatingService", "Status": "false"} -2022-06-27T03:18:31Z INFO controllers.CDB Current Phase:CreatingService {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:18:31Z INFO controllers.CDB Creating a new Cluster Service for: cdb-dev {"createORDSSVC": "oracle-database-operator-system/cdb-dev", "Svc.Namespace": "oracle-database-operator-system", "Service.Name": "cdb-dev-ords"} -2022-06-27T03:18:31Z INFO controllers.CDB Created ORDS Cluster Service successfully {"createORDSSVC": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:31Z DEBUG events Normal {"object": {"kind":"CDB","namespace":"oracle-database-operator-system","name":"cdb-dev","uid":"c36e8d5f-6103-4a70-a840-16c6683755ec","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101443627"}, "reason": "CreatedORDSService", "message": "Created ORDS Service for cdb-dev"} -2022-06-27T03:18:31Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "Ready", "Status": "false"} -2022-06-27T03:18:46Z INFO controllers.CDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/cdb-dev"} -2022-06-27T03:18:46Z INFO controllers.CDB Res Status: {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "Ready", "Status": "false"} -2022-06-27T03:18:46Z INFO controllers.CDB Current Phase:Ready {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev"} -2022-06-27T03:18:46Z INFO controllers.CDB DEFER {"onpremdboperator": "oracle-database-operator-system/cdb-dev", "Name": "cdb-dev", "Phase": "Ready", "Status": "true"} - - - - --- Check the status of the CDB CRD Pod after few minutes of running above commands: - -% kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/cdb-dev-ords-rs-q2b68 1/1 Running 0 29s <<< CDB CRD Resource Pod -pod/oracle-database-operator-controller-manager-76cb674c5c-4nrh8 1/1 Running 0 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd 1/1 Running 1 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-xsv9g 1/1 Running 2 4d10h - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.98.47 8443/TCP 5d1h -service/oracle-database-operator-webhook-service ClusterIP 10.96.166.163 443/TCP 5d1h - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 5d1h - -NAME DESIRED CURRENT READY AGE -replicaset.apps/cdb-dev-ords-rs 1 1 1 31s -replicaset.apps/oracle-database-operator-controller-manager-76cb674c5c 3 3 3 5d1h - - - - --- Check the logs of the CDB CRD Pod created above: - -% kubectl logs -f pod/cdb-dev-ords-rs-q2b68 -n oracle-database-operator-system -Requires to login with administrator privileges to verify Oracle REST Data Services schema. - -Connecting to database user: SYS AS SYSDBA url: jdbc:oracle:thin:@//goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/goldcdb_phx1pw.lbsub52b3b1cae.okecluster.oraclevcn.com - -Retrieving information.. -Your database connection is to a CDB. ORDS common user ORDS_PUBLIC_USER will be created in the CDB. ORDS schema will be installed in the PDBs. -Root CDB$ROOT - create ORDS common user -PDB PDB$SEED - install ORDS 21.4.3.r1170405 - -2022-06-27T03:17:22.015Z INFO reloaded pools: [] -2022-06-27T03:17:22.024Z INFO - -2022-06-27T03:17:23.200Z INFO Installing Oracle REST Data Services version 21.4.3.r1170405 in CDB$ROOT -2022-06-27T03:17:23.234Z INFO ... Log file written to /home/oracle/ords_cdb_install_core_CDB_ROOT_2022-06-27_031723_00234.log -2022-06-27T03:17:24.352Z INFO ... Verified database prerequisites -2022-06-27T03:17:24.662Z INFO ... Created Oracle REST Data Services proxy user -2022-06-27T03:17:24.708Z INFO Completed installation for Oracle REST Data Services version 21.4.3.r1170405. Elapsed time: 00:00:01.504 - -2022-06-27T03:17:24.722Z INFO Installing Oracle REST Data Services version 21.4.3.r1170405 in PDB$SEED -2022-06-27T03:17:24.731Z INFO ... Log file written to /home/oracle/ords_cdb_install_core_PDB_SEED_2022-06-27_031724_00731.log -2022-06-27T03:17:24.863Z INFO ... Verified database prerequisites -2022-06-27T03:17:25.123Z INFO ... Created Oracle REST Data Services proxy user -2022-06-27T03:17:25.568Z INFO ... Created Oracle REST Data Services schema -2022-06-27T03:17:26.252Z INFO ... Granted privileges to Oracle REST Data Services -2022-06-27T03:17:34.493Z INFO ... Created Oracle REST Data Services database objects -2022-06-27T03:17:43.730Z INFO ... Log file written to /home/oracle/ords_cdb_install_datamodel_PDB_SEED_2022-06-27_031743_00730.log -2022-06-27T03:17:45.883Z INFO ... Log file written to /home/oracle/ords_cdb_install_scheduler_PDB_SEED_2022-06-27_031745_00883.log -2022-06-27T03:17:49.296Z INFO ... Log file written to /home/oracle/ords_cdb_install_apex_PDB_SEED_2022-06-27_031749_00296.log -2022-06-27T03:17:51.492Z INFO Completed installation for Oracle REST Data Services version 21.4.3.r1170405. Elapsed time: 00:00:26.768 - -2022-06-27T03:17:51.492Z INFO Completed CDB installation for Oracle REST Data Services version 21.4.3.r1170405. Total elapsed time: 00:00:28.297 - -spawn java -jar /opt/oracle/ords/ords.war user sql_admin SQL Administrator -Enter a password for user sql_admin: -Confirm password for user sql_admin: -2022-06-27T03:17:53.192Z INFO Created user: sql_admin in file: /opt/oracle/ords/config/ords/credentials -2022-06-27T03:17:54.816Z INFO Modified: /opt/oracle/ords/config/ords/conf/apex_pu.xml, updated properties: database.api.admin.enabled, db.cdb.adminUser, db.cdb.adminUser.password -2022-06-27T03:17:56.583Z INFO HTTP and HTTP/2 cleartext listening on host: localhost port: 8888 -2022-06-27T03:17:56.647Z INFO The document root is serving static resources located in: /opt/oracle/ords/doc_root -2022-06-27T03:17:58.593Z INFO Configuration properties for: |apex|pu| -db.servicename=goldcdb_phx1pw.lbsub52b3b1cae.okecluster.oraclevcn.com -db.hostname=goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com -database.api.admin.enabled=true -db.password=****** -db.cdb.adminUser.password=****** -database.api.enabled=true -db.cdb.adminUser=C##DBAPI_CDB_ADMIN as SYSDBA -db.username=ORDS_PUBLIC_USER -restEnabledSql.active=true -resource.templates.enabled=true -db.port=1521 -feature.sdw=true -db.connectionType=basic - -2022-06-27T03:17:58.595Z WARNING *** jdbc.MaxLimit in configuration |apex|pu| is using a value of 10, this setting may not be sized adequately for a production environment *** -2022-06-27T03:17:58.595Z WARNING *** jdbc.InitialLimit in configuration |apex|pu| is using a value of 3, this setting may not be sized adequately for a production environment *** -2022-06-27T03:18:02.803Z INFO Oracle REST Data Services initialized -Oracle REST Data Services version : 21.4.3.r1170405 -Oracle REST Data Services server info: jetty/9.4.44.v20210927 - - - --- Check the CDB CRD Resource and it should be in "Ready" status for a successful deployment: - -% kubectl get cdbs -A -NAMESPACE NAME CDB NAME DB SERVER DB PORT SCAN NAME REPLICAS STATUS MESSAGE -oracle-database-operator-system cdb-dev goldcdb goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com 1521 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com 1 Ready diff --git a/docs/multitenant/provisioning/cdb.yaml b/docs/multitenant/provisioning/cdb.yaml deleted file mode 100644 index 8e25a763..00000000 --- a/docs/multitenant/provisioning/cdb.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "goldcdb" - dbServer: "goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com" - ordsImage: phx.ocir.io//oracle/ords:21.4.3 - ordsImagePullSecret: "container-registry-secret" - dbPort: 1521 - replicas: 1 - serviceName: "goldcdb_phx1pw.lbsub52b3b1cae.okecluster.oraclevcn.com" - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" diff --git a/docs/multitenant/provisioning/cdb_crd_resource.md b/docs/multitenant/provisioning/cdb_crd_resource.md deleted file mode 100644 index f1f36404..00000000 --- a/docs/multitenant/provisioning/cdb_crd_resource.md +++ /dev/null @@ -1,38 +0,0 @@ -# Create a CDB CRD Resource using Oracle DB Operator On-Prem Controller - -In this use case, using the Oracle Database Operator On-Prem Controller, you will create the CDB kind as a custom resource that will model a CDB as a native Kubernetes object. - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -This example uses `create_cdb.yaml` with: - -- CDB CRD resource Name as `cdb-dev` -- Container Database (CDB) Name as `goldcdb` -- Scan Name as `goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com` -- Database Server Name as `goldhost1.lbsub52b3b1cae.okecluster.oraclevcn.com` -- ORDS Docker Image as `phx.ocir.io//oracle/ords:21.4.3` -- Image Pull Secret as `container-registry-secret` -- Database Listener Port as `1521` -- Database Service Name as `goldcdb_phx1pw.lbsub52b3b1cae.okecluster.oraclevcn.com` -- Number of replicas for CDB CRD Resource as 1 - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [config/crd/bases/database.oracle.com_cdbs.yaml](../../../config/crd/bases/database.oracle.com_cdbs.yaml) - -Use the file: [cdb.yaml](./cdb.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -[root@test-server oracle-database-operator]# kubectl apply -f cdb.yaml -``` - -2. Monitor the Oracle DB Operator Pod for the progress of the CDB CRD Resource creation. - -NOTE: Check the DB Operator Pod name in your environment. - -``` -[root@test-server oracle-database-operator]# kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./cdb.log) is the sample output for a PDB created using Oracle DB Operator On-Prem Controller using file [cdb.yaml](./cdb.yaml) diff --git a/docs/multitenant/provisioning/cdb_secret.yaml b/docs/multitenant/provisioning/cdb_secret.yaml deleted file mode 100644 index 4d03499e..00000000 --- a/docs/multitenant/provisioning/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: "[ base64 encode values ]" - sysadmin_pwd: "[ base64 encode values ]" - cdbadmin_user: "[ base64 encode values ]" - cdbadmin_pwd: "[ base64 encode values ]" - webserver_user: "[ base64 encode values ]" - webserver_pwd: "[base64 encode values ]" diff --git a/docs/multitenant/provisioning/clone_pdb.log b/docs/multitenant/provisioning/clone_pdb.log deleted file mode 100644 index 54b54ef6..00000000 --- a/docs/multitenant/provisioning/clone_pdb.log +++ /dev/null @@ -1,137 +0,0 @@ --- Check the Oracle DB Operator Pod and CDB CRD Pod status: - -% kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/cdb-dev-ords-rs-5bztb 1/1 Running 0 5m23s -pod/cdb-dev-ords-rs-q2b68 1/1 Running 0 12m -pod/oracle-database-operator-controller-manager-76cb674c5c-4nrh8 1/1 Running 0 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd 1/1 Running 1 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-xsv9g 1/1 Running 2 4d10h - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/cdb-dev-ords ClusterIP None 11m -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.98.47 8443/TCP 5d2h -service/oracle-database-operator-webhook-service ClusterIP 10.96.166.163 443/TCP 5d2h - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 5d2h - -NAME DESIRED CURRENT READY AGE -replicaset.apps/cdb-dev-ords-rs 2 2 2 12m -replicaset.apps/oracle-database-operator-controller-manager-76cb674c5c 3 3 3 5d2h - - --- Check the current PDB CRD resource: - -% kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew READ WRITE 1G Ready Success - - --- Check the current PDBs in the target CDB: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW READ WRITE NO - - - --- .yaml file used in this use case to clone a PDB: - -% cat clone_pdb.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1-clone - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnewclone" - srcPdbName: "pdbnew" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - action: "Clone" - - --- Apply the .yaml file: - -% kubectl apply -f clone_pdb.yaml -pdb.database.oracle.com/pdb1-clone created - - - -- Monitor the logs from the Oracle DB Operator Pod: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T03:37:21Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "UID": "7fbbd983-0309-4603-9c3e-77f7ffded000", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T03:37:21Z INFO pdb-webhook Setting default values in PDB spec for : pdb1-clone -2022-06-27T03:37:21Z INFO pdb-webhook - reuseTempFile : true -2022-06-27T03:37:21Z INFO pdb-webhook - unlimitedStorage : true -2022-06-27T03:37:21Z INFO pdb-webhook - tdeImport : false -2022-06-27T03:37:21Z INFO pdb-webhook - tdeExport : false -2022-06-27T03:37:21Z INFO pdb-webhook - asClone : false -2022-06-27T03:37:21Z INFO pdb-webhook - getScript : false -2022-06-27T03:37:21Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "7fbbd983-0309-4603-9c3e-77f7ffded000", "allowed": true} -2022-06-27T03:37:21Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "UID": "12aa43ea-df81-4931-b29b-d665c121590f", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T03:37:21Z INFO pdb-webhook ValidateCreate-Validating PDB spec for : pdb1-clone -2022-06-27T03:37:21Z INFO pdb-webhook validateCommon {"name": "pdb1-clone"} -2022-06-27T03:37:21Z INFO pdb-webhook Valdiating PDB Resource Action : CLONE -2022-06-27T03:37:21Z INFO pdb-webhook PDB Resource : pdb1-clone successfully validated for Action : CLONE -2022-06-27T03:37:21Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "12aa43ea-df81-4931-b29b-d665c121590f", "allowed": true} -2022-06-27T03:37:21Z INFO controllers.PDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T03:37:21Z INFO controllers.PDB Adding finalizer {"managePDBDeletion": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T03:37:21Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "UID": "b3bbaa0d-6865-4dd1-9ca9-d54f916a8d66", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T03:37:21Z INFO pdb-webhook Setting default values in PDB spec for : pdb1-clone -2022-06-27T03:37:21Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "b3bbaa0d-6865-4dd1-9ca9-d54f916a8d66", "allowed": true} -2022-06-27T03:37:21Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "UID": "5924d376-7a5c-4d4a-8ca9-040c585fb4b6", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T03:37:21Z INFO pdb-webhook ValidateUpdate-Validating PDB spec for : pdb1-clone -2022-06-27T03:37:21Z INFO pdb-webhook validateCommon {"name": "pdb1-clone"} -2022-06-27T03:37:21Z INFO pdb-webhook Valdiating PDB Resource Action : CLONE -2022-06-27T03:37:21Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "5924d376-7a5c-4d4a-8ca9-040c585fb4b6", "allowed": true} -2022-06-27T03:37:21Z INFO controllers.PDB Found PDB: pdb1-clone {"checkDuplicatePDB": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T03:37:21Z INFO controllers.PDB Validating PDB phase for: pdb1-clone {"validatePhase": "oracle-database-operator-system/pdb1-clone", "Action": "CLONE"} -2022-06-27T03:37:21Z INFO controllers.PDB Validation complete {"validatePhase": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T03:37:21Z INFO controllers.PDB PDB: {"onpremdboperator": "oracle-database-operator-system/pdb1-clone", "Name": "pdb1-clone", "Phase": "Cloning", "Status": "false"} -2022-06-27T03:37:21Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:37:21Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1-clone", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/", "Action": "POST"} -2022-06-27T03:37:21Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:38:01Z INFO controllers.PDB Cloned PDB successfully {"clonePDB": "oracle-database-operator-system/pdb1-clone", "Source PDB Name": "pdbnew", "Clone PDB Name": "pdbnewclone"} -2022-06-27T03:38:01Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:38:01Z DEBUG events Normal {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1-clone","uid":"16276c26-60a3-463b-bdd5-10bd328f9d43","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101449482"}, "reason": "Created", "message": "PDB 'pdbnewclone' cloned successfully"} -2022-06-27T03:38:01Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1-clone", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnewclone/status", "Action": "GET"} -2022-06-27T03:38:01Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:38:01Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1-clone", "PDB Name": "pdbnewclone", "State": "READ WRITE"} -2022-06-27T03:38:01Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1-clone"} - - - --- Check the status of the new PDB CRD resource created by cloning: - -% kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew READ WRITE 1G Ready Success -oracle-database-operator-system pdb1-clone goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnewclone goldcdb pdbnewclone READ WRITE Ready Success - - --- Verify the new PDB created from the target CDB: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW READ WRITE NO - 4 PDBNEWCLONE READ WRITE NO diff --git a/docs/multitenant/provisioning/clone_pdb.md b/docs/multitenant/provisioning/clone_pdb.md deleted file mode 100644 index b731d301..00000000 --- a/docs/multitenant/provisioning/clone_pdb.md +++ /dev/null @@ -1,38 +0,0 @@ -# Clone a PDB using Oracle DB Operator On-Prem Controller in a target CDB - -In this use case, a PDB is cloned using Oracle DB Operator On-Prem controller. - -To clone a PDB CRD Resource, a sample .yaml file is available here: [config/samples/onpremdb/pdb_clone.yaml](../../../config/samples/onpremdb/pdb_clone.yaml) - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -This example uses `clone_pdb.yaml` to clone a PDB using Oracle DB Operator On-Prem Controller with: - -- PDB CRD resource Name as `pdb1-clone` -- Pluggable Database (PDB) Name as `pdbnewclone` -- Total Size of the PDB as `UNLIMITED` -- Total size for temporary tablespace as `UNLIMITED` -- Target CDB CRD Resource Name as `cdb-dev` -- Target CDB name as `goldcdb` -- Source PDB Name as `pdbnew` - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -Use the file: [clone_pdb.yaml](./clone_pdb.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -[root@test-server oracle-database-operator]# kubectl apply -f clone_pdb.yaml -``` - -2. Monitor the Oracle DB Operator Pod for the progress of the PDB creation. - -NOTE: Check the DB Operator Pod name in your environment. - -``` -[root@test-server oracle-database-operator]# kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./clone_pdb.log) is the sample output for a PDB created using Oracle DB Operator On-Prem Controller using file [clone_pdb.yaml](./clone_pdb.yaml) diff --git a/docs/multitenant/provisioning/clone_pdb.yaml b/docs/multitenant/provisioning/clone_pdb.yaml deleted file mode 100644 index 7d3cfff9..00000000 --- a/docs/multitenant/provisioning/clone_pdb.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1-clone - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnewclone" - srcPdbName: "pdbnew" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - action: "Clone" diff --git a/docs/multitenant/provisioning/create_pdb.log b/docs/multitenant/provisioning/create_pdb.log deleted file mode 100644 index ebba9e66..00000000 --- a/docs/multitenant/provisioning/create_pdb.log +++ /dev/null @@ -1,139 +0,0 @@ --- Check the Oracle DB Operator Pod and CDB CRD Pod status: - -% kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/cdb-dev-ords-rs-5bztb 1/1 Running 0 5m23s -pod/cdb-dev-ords-rs-q2b68 1/1 Running 0 12m -pod/oracle-database-operator-controller-manager-76cb674c5c-4nrh8 1/1 Running 0 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd 1/1 Running 1 4d10h -pod/oracle-database-operator-controller-manager-76cb674c5c-xsv9g 1/1 Running 2 4d10h - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/cdb-dev-ords ClusterIP None 11m -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.98.47 8443/TCP 5d2h -service/oracle-database-operator-webhook-service ClusterIP 10.96.166.163 443/TCP 5d2h - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 5d2h - -NAME DESIRED CURRENT READY AGE -replicaset.apps/cdb-dev-ords-rs 2 2 2 12m -replicaset.apps/oracle-database-operator-controller-manager-76cb674c5c 3 3 3 5d2h - - - --- PDB secrets in this use case were created using the below file: - -% cat pdb_secret.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - sysadmin_user: "cGRiYWRtaW4=" - sysadmin_pwd: "V0VsY29tZV8yMSMj" - - --- This is the .yaml file used to create a PDB CRD resource in this use case: - -% cat pdb_create.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Create" - - - - --- Apply the .yaml files: - -% kubectl apply -f pdb_secret.yaml -secret/pdb1-secret created - -% kubectl apply -f pdb_create.yaml -pdb.database.oracle.com/pdb1 created - - --- Monitor the logs from the Oracle DB Operator Pod: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T03:28:30Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "UID": "6fca0e37-8fd9-4ccd-86ad-2edec604a28b", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T03:28:30Z INFO pdb-webhook Setting default values in PDB spec for : pdb1 -2022-06-27T03:28:30Z INFO pdb-webhook - reuseTempFile : true -2022-06-27T03:28:30Z INFO pdb-webhook - unlimitedStorage : true -2022-06-27T03:28:30Z INFO pdb-webhook - tdeImport : false -2022-06-27T03:28:30Z INFO pdb-webhook - tdeExport : false -2022-06-27T03:28:30Z INFO pdb-webhook - asClone : false -2022-06-27T03:28:30Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "6fca0e37-8fd9-4ccd-86ad-2edec604a28b", "allowed": true} -2022-06-27T03:28:30Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "UID": "6ae20043-fa1a-4eba-b943-a2266183da48", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T03:28:30Z INFO pdb-webhook ValidateUpdate-Validating PDB spec for : pdb1 -2022-06-27T03:28:30Z INFO pdb-webhook validateCommon {"name": "pdb1"} -2022-06-27T03:28:30Z INFO pdb-webhook Valdiating PDB Resource Action : CREATE -2022-06-27T03:28:30Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "6ae20043-fa1a-4eba-b943-a2266183da48", "allowed": true} -2022-06-27T03:28:30Z INFO controllers.PDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/pdb1"} -2022-06-27T03:28:30Z INFO controllers.PDB Validating PDB phase for: pdb1 {"validatePhase": "oracle-database-operator-system/pdb1", "Action": "CREATE"} -2022-06-27T03:28:30Z INFO controllers.PDB Validation complete {"validatePhase": "oracle-database-operator-system/pdb1"} -2022-06-27T03:28:30Z INFO controllers.PDB PDB: {"onpremdboperator": "oracle-database-operator-system/pdb1", "Name": "pdb1", "Phase": "Creating", "Status": "false"} -2022-06-27T03:28:30Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:28:30Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/", "Action": "POST"} -2022-06-27T03:28:30Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:29:04Z INFO controllers.PDB Created PDB Resource {"createPDB": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew"} -2022-06-27T03:29:04Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:29:04Z DEBUG events Normal {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1","uid":"81f2e686-6e1b-4e2c-8a2f-e20c2f99d6b9","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101446779"}, "reason": "Created", "message": "PDB 'pdbnew' created successfully"} -2022-06-27T03:29:04Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "GET"} -2022-06-27T03:29:04Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:29:04Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew", "State": "READ WRITE"} -2022-06-27T03:29:04Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1"} - - - - --- Check the status of the PDB CRD Resource created: - -% kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew READ WRITE 1G Ready Success - - --- The status as "Ready" and message as "Success" confirms that the resource has been created successfully. - - --- Verify that the PDB is created from CDB: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW READ WRITE NO diff --git a/docs/multitenant/provisioning/create_pdb.md b/docs/multitenant/provisioning/create_pdb.md deleted file mode 100644 index 4f6e57aa..00000000 --- a/docs/multitenant/provisioning/create_pdb.md +++ /dev/null @@ -1,37 +0,0 @@ -# Create a PDB using Oracle DB Operator On-Prem Controller in a target CDB - -The Oracle Database Operator On-Prem Controller creates the PDB kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -To create a PDB CRD Resource, a sample .yaml file is available here: [config/samples/onpremdb/pdb_create.yaml](../../../config/samples/onpremdb/pdb_create.yaml) - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -This example uses `create_pdb.yaml` to create a PDB using Oracle DB Operator On-Prem Controller with: - -- PDB CRD resource Name as `pdb1` -- Pluggable Database (PDB) Name as `pdbnew` -- Total Size of the PDB as `1GB` -- Total size for temporary tablespace as `100M` -- Target CDB CRD Resource Name as `cdb-dev` -- Target CDB name as `goldcdb` - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -Use the file: [create_pdb.yaml](./create_pdb.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -[root@test-server oracle-database-operator]# kubectl apply -f create_pdb.yaml -``` - -2. Monitor the Oracle DB Operator Pod for the progress of the PDB creation. - -NOTE: Check the DB Operator Pod name in your environment. - -``` -[root@test-server oracle-database-operator]# kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./create_pdb.log) is the sample output for a PDB created using Oracle DB Operator On-Prem Controller using file [create_pdb.yaml](./create_pdb.yaml) diff --git a/docs/multitenant/provisioning/create_pdb.yaml b/docs/multitenant/provisioning/create_pdb.yaml deleted file mode 100644 index 82941185..00000000 --- a/docs/multitenant/provisioning/create_pdb.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Create" diff --git a/docs/multitenant/provisioning/delete_pdb.log b/docs/multitenant/provisioning/delete_pdb.log deleted file mode 100644 index 7f361871..00000000 --- a/docs/multitenant/provisioning/delete_pdb.log +++ /dev/null @@ -1,157 +0,0 @@ --- Check the existing PDB CRD resources: - -% kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew READ WRITE 1G Ready Success -oracle-database-operator-system pdb1-clone goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnewclone goldcdb pdbnewclone READ WRITE Ready Success - - --- Also check from the database as well: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW READ WRITE NO - 4 PDBNEWCLONE READ WRITE NO - - - -% cat modify_pdb_close.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1-clone - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnewclone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - - -% kubectl apply -f modify_pdb_close.yaml -pdb.database.oracle.com/pdb1-clone configured - - - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T04:19:36Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "UID": "24842cc8-0047-46cc-86a5-2782a95e3e36", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T04:19:36Z INFO pdb-webhook Setting default values in PDB spec for : pdb1-clone -2022-06-27T04:19:36Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "24842cc8-0047-46cc-86a5-2782a95e3e36", "allowed": true} -2022-06-27T04:19:36Z INFO controllers.PDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T04:19:36Z INFO controllers.PDB Validating PDB phase for: pdb1-clone {"validatePhase": "oracle-database-operator-system/pdb1-clone", "Action": "MODIFY"} -2022-06-27T04:19:36Z INFO controllers.PDB Validation complete {"validatePhase": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T04:19:36Z INFO controllers.PDB PDB: {"onpremdboperator": "oracle-database-operator-system/pdb1-clone", "Name": "pdb1-clone", "Phase": "Modifying", "Status": "false"} -2022-06-27T04:19:36Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:19:36Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1-clone", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnewclone/status", "Action": "GET"} -2022-06-27T04:19:36Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:19:38Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1-clone", "PDB Name": "pdbnewclone", "State": "READ WRITE"} -2022-06-27T04:19:38Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:19:38Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1-clone", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnewclone/status", "Action": "POST"} -2022-06-27T04:19:38Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:19:39Z INFO controllers.PDB Successfully modified PDB state {"modifyPDB": "oracle-database-operator-system/pdb1-clone", "PDB Name": "pdbnewclone"} -2022-06-27T04:19:39Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:19:39Z DEBUG events Normal {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1-clone","uid":"309dd711-198b-45b6-a34b-da5069af70fb","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101462484"}, "reason": "Modified", "message": "PDB 'pdbnewclone' modified successfully"} -2022-06-27T04:19:39Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1-clone", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnewclone/status", "Action": "GET"} -2022-06-27T04:19:39Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:19:39Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1-clone", "PDB Name": "pdbnewclone", "State": "MOUNTED"} -2022-06-27T04:19:39Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1-clone"} - - - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW READ WRITE NO - 4 PDBNEWCLONE MOUNTED - - --- Check the .yaml file to be used to delete a PDB: - -% cat delete_pdb.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1-clone - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - pdbName: "pdbnewclone" - action: "Delete" - dropAction: "INCLUDING" - - - --- Apply the .yaml file: - -% kubectl apply -f delete_pdb.yaml -pdb.database.oracle.com/pdb1-clone configured - - --- Monitor the Oracle DB Operator Pod logs: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T04:21:37Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "UID": "64148dda-d0df-4e03-88e3-98b1ce7b7aaf", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T04:21:37Z INFO pdb-webhook ValidateUpdate-Validating PDB spec for : pdb1-clone -2022-06-27T04:21:37Z INFO pdb-webhook validateCommon {"name": "pdb1-clone"} -2022-06-27T04:21:37Z INFO pdb-webhook Valdiating PDB Resource Action : DELETE -2022-06-27T04:21:37Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "64148dda-d0df-4e03-88e3-98b1ce7b7aaf", "allowed": true} -2022-06-27T04:21:37Z INFO controllers.PDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T04:21:37Z INFO controllers.PDB Validating PDB phase for: pdb1-clone {"validatePhase": "oracle-database-operator-system/pdb1-clone", "Action": "DELETE"} -2022-06-27T04:21:37Z INFO controllers.PDB Validation complete {"validatePhase": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T04:21:37Z INFO controllers.PDB PDB: {"onpremdboperator": "oracle-database-operator-system/pdb1-clone", "Name": "pdb1-clone", "Phase": "Deleting", "Status": "false"} -2022-06-27T04:21:37Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:21:37Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1-clone", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnewclone/", "Action": "DELETE"} -2022-06-27T04:21:37Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1-clone", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:21:39Z INFO controllers.PDB Successfully dropped PDB {"deletePDBInstance": "oracle-database-operator-system/pdb1-clone", "PDB Name": "pdbnewclone"} -2022-06-27T04:21:39Z INFO controllers.PDB Removing finalizer {"deletePDB": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T04:21:39Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "UID": "15bd320b-3f9f-46a7-8493-c586310b7d84", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T04:21:39Z INFO pdb-webhook ValidateUpdate-Validating PDB spec for : pdb1-clone -2022-06-27T04:21:39Z INFO pdb-webhook validateCommon {"name": "pdb1-clone"} -2022-06-27T04:21:39Z INFO pdb-webhook Valdiating PDB Resource Action : DELETE -2022-06-27T04:21:39Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "15bd320b-3f9f-46a7-8493-c586310b7d84", "allowed": true} -2022-06-27T04:21:39Z INFO controllers.PDB Successfully deleted PDB resource {"deletePDB": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T04:21:39Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1-clone"} -2022-06-27T04:21:39Z DEBUG events Normal {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1-clone","uid":"309dd711-198b-45b6-a34b-da5069af70fb","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101463106"}, "reason": "Deleted", "message": "PDB 'pdbnewclone' dropped successfully"} - - - - - --- Check the PDB CRD resources: - -% kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew READ WRITE 1G Ready Success - - --- Verify from the CDB: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW READ WRITE NO diff --git a/docs/multitenant/provisioning/delete_pdb.md b/docs/multitenant/provisioning/delete_pdb.md deleted file mode 100644 index e36bb473..00000000 --- a/docs/multitenant/provisioning/delete_pdb.md +++ /dev/null @@ -1,37 +0,0 @@ -# Delete a PDB using Oracle DB Operator On-Prem Controller in a target CDB - -In this use case, a PDB is deleted using Oracle DB Operator On-Prem controller. - -To delete a PDB CRD Resource, a sample .yaml file is available here: [config/samples/onpremdb/pdb_delete.yaml](../../../config/samples/onpremdb/pdb_delete.yaml) - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -This example uses `delete_pdb.yaml` to delete a PDB using Oracle DB Operator On-Prem Controller with: - -- Pluggable Database (PDB) Name as `pdbnewclone` -- Target CDB CRD Resource Name as `cdb-dev` -- Action to be taken on the PDB as `Delete` -- Option to specify if datafiles should be removed as `INCLUDING` - -**NOTE:** You need to *modify* the PDB status to MOUNTED, as described earlier, on the target CDB before you want to delete that PDB. - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -Use the file: [delete_pdb.yaml](./delete_pdb.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -% kubectl apply -f delete_pdb.yaml -``` - -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the PDB deletion. - -NOTE: Check the DB Operator Pod name in your environment. - -```sh -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./delete_pdb.log) is the sample output for a PDB created using Oracle DB Operator On-Prem Controller using file [delete_pdb.yaml](./delete_pdb.yaml) diff --git a/docs/multitenant/provisioning/delete_pdb.yaml b/docs/multitenant/provisioning/delete_pdb.yaml deleted file mode 100644 index d16084bb..00000000 --- a/docs/multitenant/provisioning/delete_pdb.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1-clone - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - pdbName: "pdbnewclone" - action: "Delete" - dropAction: "INCLUDING" diff --git a/docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md b/docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md deleted file mode 100644 index f3ed2489..00000000 --- a/docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md +++ /dev/null @@ -1,37 +0,0 @@ -# Example of a working setup using OCI OKE(Kubernetes Cluster) and a CDB in Cloud (OCI Exadata Database Cluster) - -In this example, the target CDB (for which the PDB life cycle management is needed) is running in a Cloud environment (OCI's [Oracle Exadata Database Service](https://docs.oracle.com/en-us/iaas/exadatacloud/index.html)) and to manage its PDBs, the Oracle DB Operator is running on a Kubernetes Cluster running in cloud (OCI's [Container Engine for Kubernetes or OKE](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm#Overview_of_Container_Engine_for_Kubernetes)). - - -## High Level plans for this setup - -Below are the main steps that will be involved in this setup: - -- Setup VCN, Add security lists -- Setup OKE cluster with custom settings -- Install Oracle Database Operator on OKE Cluster -- Install ords controller definition -- Manager pdb life cycle management. - - -## OKE Cluster - -Check the [Oracle Documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengnetworkconfigexample.htm#example-privatek8sapi-privateworkers-publiclb) for the OKE rules settings. - -Create OKE cluster with CUSTOM option to use same VCN where ExaCS is provisioned. - -**NOTE:** Make sure you choose same VCN exaphxvcn where ExaCS is provisioned. - -After this, setup kubeconfig & validate cluster access as well as worker node access via ssh. - -For example, you should be able to check the available OKE nodes using "kubectl" as below: - -``` -% kubectl get nodes -o wide -NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME -192.168.194.163 Ready node 3d19h v1.23.4 192.168.194.163 XX.XX.XX.XX Oracle Linux Server 7.9 5.4.17-2136.306.1.3.el7uek.x86_64 cri-o://1.23.2 -192.168.194.169 Ready node 3d19h v1.23.4 192.168.194.169 XX.XX.XX.XX Oracle Linux Server 7.9 5.4.17-2136.306.1.3.el7uek.x86_64 cri-o://1.23.2 -192.168.194.241 Ready node 3d19h v1.23.4 192.168.194.241 XX.XX.XX.XX Oracle Linux Server 7.9 5.4.17-2136.306.1.3.el7uek.x86_64 cri-o://1.23.2 -``` - -Once this setup is ready, you can proceed with the installation of [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) to use the Oracle On-prem controller to manage PDBs in this CDB. diff --git a/docs/multitenant/provisioning/known_issues.md b/docs/multitenant/provisioning/known_issues.md deleted file mode 100644 index 3ab19897..00000000 --- a/docs/multitenant/provisioning/known_issues.md +++ /dev/null @@ -1,49 +0,0 @@ -# Known Issues - -Please refer to the below list of known issues related to Oracle DB Operator On-Prem Controller: - -1. **ORA-20002: ERROR: The user ORDS_PUBLIC_USER already exists in the logs of CDB CRD Pods.** - -This error is expected when you are deploying `2` replicas of CDB CRD during the deployment of the CDB CRD. Below is the snippet of a possible error: -``` -2022-06-22T20:06:32.616Z INFO Installing Oracle REST Data Services version 21.4.2.r0621806 in CDB$ROOT -2022-06-22T20:06:32.663Z INFO ... Log file written to /home/oracle/ords_cdb_install_core_CDB_ROOT_2022-06-22_200632_00662.log -2022-06-22T20:06:33.835Z INFO CDB restart file created in /home/oracle/ords_restart_2022-06-22_200633_00835.properties -2022-06-22T20:06:33.837Z SEVERE Error executing script: ords_prereq_env.sql Error: ORA-20002: ERROR: The user ORDS_PUBLIC_USER already exists. You must first uninstall ORDS using ords_uninstall.sql prior to running the install scripts. -ORA-06512: at line 8 -ORA-06512: at line 8 - - Refer to log file /home/oracle/ords_cdb_install_core_CDB_ROOT_2022-06-22_200632_00662.log for details - -java.io.IOException: Error executing script: ords_prereq_env.sql Error: ORA-20002: ERROR: The user ORDS_PUBLIC_USER already exists. You must first uninstall ORDS using ords_uninstall.sql prior to running the install scripts. -ORA-06512: at line 8 -ORA-06512: at line 8 -``` -This error is seen in the logs of one of the two CDB CRD pods. The other Pod `does not` show this error and the ORDS installation is done successfully. - -To avoid this error, you need to initially deploy the CDB CRD with a single replica and later add another replica as per need. - -2. **PDB create failure with error "Failed: Unauthorized"** - -It was observed that PDB creation fails with the below error when special characters like "_" or "#" were used in the password for user SQL_ADMIN: -``` -2022-06-22T20:10:09Z INFO controllers.PDB ORDS Error - HTTP Status Code :401 {"callAPI": "oracle-database-operator-system/pdb1", "Err": "\n{\n \"code\": \"Unauthorized\",\n \"message\": \"Unauthorized\",\n \"type\": \"tag:oracle.com,2020:error/Unauthorized\",\n \"instance\": \"tag:oracle.com,2020:ecid/OoqA0Zw3oBWdabzP8wUMcQ\"\n}"} -2022-06-22T20:10:09Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1"} -2022-06-22T20:10:09Z DEBUG events Warning {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1","uid":"19fc98b1-ca7f-4e63-a6c7-fdeb14b8c275","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"99558229"}, "reason": "ORDSError", "message": "Failed: Unauthorized"} -``` - -In testing, we have used the password `welcome1` for the user SQL_ADMIN. - -To avoid this error, please avoid password `welcome1` for SQL_ADMIN user. - - -3. **After cloning a PDB from another PDB, PDB SIZE field is show as empty even if the .yaml file used during the PDB cloning specifies the PDB size:** - -```sh -% kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew READ WRITE 1G Ready Success -oracle-database-operator-system pdb1-clone goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnewclone goldcdb pdbnewclone READ WRITE Ready Success -``` - -In the above example the PDB `pdbnewclone` is cloned from PDB `pdbnew` and is showing the size column as EMPTY. This will be fixed in future version. diff --git a/docs/multitenant/provisioning/modify_pdb.log b/docs/multitenant/provisioning/modify_pdb.log deleted file mode 100644 index 151393a7..00000000 --- a/docs/multitenant/provisioning/modify_pdb.log +++ /dev/null @@ -1,181 +0,0 @@ ------ Closing a PDB ------ - --- Check the existing PDB CRD resources - -jyotiprakashverma@jyotiprakashverma-mac onprem_test % kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew READ WRITE 1G Ready Success -oracle-database-operator-system pdb1-clone goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnewclone goldcdb pdbnewclone READ WRITE Ready Success - - - - --- Check the status of the PDBs in the CDB: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW READ WRITE NO - 5 PDBNEWCLONE READ WRITE NO - - - --- Check the file to modify the PDB state to CLOSE: - -% cat modify_pdb_close.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - - --- Apply the file: - -% kubectl apply -f modify_pdb_close.yaml -pdb.database.oracle.com/pdb1 configured - - --- Monitor the logs from the Oracle DB Operator Pod: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T03:44:36Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "UID": "08f95926-6bf1-4c70-b319-1b17015ce22a", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T03:44:36Z INFO pdb-webhook Setting default values in PDB spec for : pdb1 -2022-06-27T03:44:36Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "08f95926-6bf1-4c70-b319-1b17015ce22a", "allowed": true} -2022-06-27T03:44:36Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "UID": "64ebebe2-87f2-4237-8928-532365f3cca9", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T03:44:36Z INFO pdb-webhook ValidateUpdate-Validating PDB spec for : pdb1 -2022-06-27T03:44:36Z INFO pdb-webhook validateCommon {"name": "pdb1"} -2022-06-27T03:44:36Z INFO pdb-webhook Valdiating PDB Resource Action : MODIFY -2022-06-27T03:44:36Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "64ebebe2-87f2-4237-8928-532365f3cca9", "allowed": true} -2022-06-27T03:44:36Z INFO controllers.PDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/pdb1"} -2022-06-27T03:44:36Z INFO controllers.PDB Validating PDB phase for: pdb1 {"validatePhase": "oracle-database-operator-system/pdb1", "Action": "MODIFY"} -2022-06-27T03:44:36Z INFO controllers.PDB Validation complete {"validatePhase": "oracle-database-operator-system/pdb1"} -2022-06-27T03:44:36Z INFO controllers.PDB PDB: {"onpremdboperator": "oracle-database-operator-system/pdb1", "Name": "pdb1", "Phase": "Modifying", "Status": "false"} -2022-06-27T03:44:36Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:44:36Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "GET"} -2022-06-27T03:44:36Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:44:36Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew", "State": "READ WRITE"} -2022-06-27T03:44:36Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:44:36Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "POST"} -2022-06-27T03:44:36Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:44:38Z INFO controllers.PDB Successfully modified PDB state {"modifyPDB": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew"} -2022-06-27T03:44:38Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:44:38Z DEBUG events Normal {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1","uid":"81f2e686-6e1b-4e2c-8a2f-e20c2f99d6b9","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101451707"}, "reason": "Modified", "message": "PDB 'pdbnew' modified successfully"} -2022-06-27T03:44:38Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "GET"} -2022-06-27T03:44:38Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:44:38Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew", "State": "MOUNTED"} -2022-06-27T03:44:38Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1"} - - - - --- Check the status of PDB CRD resources - -jyotiprakashverma@jyotiprakashverma-mac onprem_test % kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew MOUNTED 1G Ready Success -oracle-database-operator-system pdb1-clone goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnewclone goldcdb pdbnewclone READ WRITE Ready Success - - - --- Confirm the status of the PDB in the CDB: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW MOUNTED - 4 PDBNEWCLONE READ WRITE NO - - - - - - - - ------ Opening a PDB ------ - --- Check the .yaml file to open the PDB: - -% cat modify_pdb_open.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - pdbState: "OPEN" - modifyOption: "READ WRITE" - action: "Modify" - - - --- Apply the file: - -% kubectl apply -f modify_pdb_open.yaml -pdb.database.oracle.com/pdb1 configured - - --- Monitor the logs from the Oracle DB Operator Pod: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T03:48:38Z INFO controllers.PDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/pdb1"} -2022-06-27T03:48:38Z INFO controllers.PDB Validating PDB phase for: pdb1 {"validatePhase": "oracle-database-operator-system/pdb1", "Action": "MODIFY"} -2022-06-27T03:48:38Z INFO controllers.PDB Validation complete {"validatePhase": "oracle-database-operator-system/pdb1"} -2022-06-27T03:48:38Z INFO controllers.PDB PDB: {"onpremdboperator": "oracle-database-operator-system/pdb1", "Name": "pdb1", "Phase": "Modifying", "Status": "false"} -2022-06-27T03:48:38Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:48:38Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "GET"} -2022-06-27T03:48:38Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:48:38Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew", "State": "MOUNTED"} -2022-06-27T03:48:38Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:48:38Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "POST"} -2022-06-27T03:48:38Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:48:41Z INFO controllers.PDB Successfully modified PDB state {"modifyPDB": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew"} -2022-06-27T03:48:41Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:48:41Z DEBUG events Normal {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1","uid":"81f2e686-6e1b-4e2c-8a2f-e20c2f99d6b9","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101452939"}, "reason": "Modified", "message": "PDB 'pdbnew' modified successfully"} -2022-06-27T03:48:41Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "GET"} -2022-06-27T03:48:41Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T03:48:41Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew", "State": "READ WRITE"} -2022-06-27T03:48:41Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1"} - - - --- Verify the status of the PDB: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW READ WRITE NO - 4 PDBNEWCLONE READ WRITE NO diff --git a/docs/multitenant/provisioning/modify_pdb.md b/docs/multitenant/provisioning/modify_pdb.md deleted file mode 100644 index a0432efb..00000000 --- a/docs/multitenant/provisioning/modify_pdb.md +++ /dev/null @@ -1,69 +0,0 @@ -# Modify a PDB using Oracle DB Operator On-Prem Controller in a target CDB - -In this use case, the state of an existing PDB is modified using Oracle DB Operator On-Prem controller. - -To modify a PDB CRD Resource, a sample .yaml file is available here: [config/samples/onpremdb/pdb_modify.yaml](../../../config/samples/onpremdb/pdb_modify.yaml) - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -Subcase 1: This example uses `modify_pdb_close.yaml` to close a PDB using Oracle DB Operator On-Prem Controller with: - -- PDB CRD resource Name as `pdb1` -- Pluggable Database (PDB) Name as `pdbnew` -- Target CDB CRD Resource Name as `cdb-dev` -- Target CDB name as `goldcdb` -- Action to be taken on the PDB as `MODIFY` -- Target state of the PDB as `CLOSE` -- Option to close the state (i.e. modify) as `IMMEDIATE` - - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -Use the file: [modify_pdb_close.yaml](./modify_pdb_close.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -% kubectl apply -f modify_pdb_close.yaml -pdb.database.oracle.com/pdb1 configured -``` - -2. Monitor the Oracle DB Operator Pod for the progress of the PDB creation. - -NOTE: Check the DB Operator Pod name in your environment. - -``` -[root@test-server oracle-database-operator]# kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -``` - -Subcase 2: This example uses `modify_pdb_open.yaml` to open a PDB using Oracle DB Operator On-Prem Controller with: - -- PDB CRD resource Name as `pdb1` -- Pluggable Database (PDB) Name as `pdbnew` -- Target CDB CRD Resource Name as `cdb-dev` -- Target CDB name as `goldcdb` -- Action to be taken on the PDB as `MODIFY` -- Target state of the PDB as `OPEN` -- Option to close the state (i.e. modify) as `READ WRITE` - - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -Use the file: [modify_pdb_open.yaml](./modify_pdb_open.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -% kubectl apply -f modify_pdb_open.yaml -pdb.database.oracle.com/pdb1 configured -``` - -2. Monitor the Oracle DB Operator Pod for the progress of the PDB creation. - -NOTE: Check the DB Operator Pod name in your environment. - -``` -[root@test-server oracle-database-operator]# kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./modify_pdb.log) is the sample output for a PDB created using Oracle DB Operator On-Prem Controller using file [modify_pdb_close.yaml](./modify_pdb_close.yaml) and [modify_pdb_open.yaml](./modify_pdb_open.yaml) diff --git a/docs/multitenant/provisioning/modify_pdb_close.yaml b/docs/multitenant/provisioning/modify_pdb_close.yaml deleted file mode 100644 index 8897b482..00000000 --- a/docs/multitenant/provisioning/modify_pdb_close.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" diff --git a/docs/multitenant/provisioning/modify_pdb_open.yaml b/docs/multitenant/provisioning/modify_pdb_open.yaml deleted file mode 100644 index 0ec640d4..00000000 --- a/docs/multitenant/provisioning/modify_pdb_open.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - pdbState: "OPEN" - modifyOption: "READ WRITE" - action: "Modify" diff --git a/docs/multitenant/provisioning/ords_image.log b/docs/multitenant/provisioning/ords_image.log deleted file mode 100644 index 771a22ca..00000000 --- a/docs/multitenant/provisioning/ords_image.log +++ /dev/null @@ -1,503 +0,0 @@ -/usr/bin/docker build -t oracle/ords-dboper:22.2.1 . -Sending build context to Docker daemon 280.6kB -Step 1/10 : FROM container-registry.oracle.com/java/jdk:latest - ---> 44b86e8925c4 -Step 2/10 : ENV ORDS_HOME=/opt/oracle/ords RUN_FILE="runOrdsSSL.sh" - ---> Running in e22c5a03b869 -Removing intermediate container e22c5a03b869 - ---> 1421497abef8 -Step 3/10 : COPY $RUN_FILE $ORDS_HOME/ - ---> d96ac1477d2d -Step 4/10 : RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps && yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && yum -y install java-11-openjdk-devel && yum -y install ords && yum -y install iproute && yum clean all - ---> Running in c08b8dac80a5 -Oracle Linux 8 BaseOS Latest (x86_64) 72 MB/s | 49 MB 00:00 -Oracle Linux 8 Application Stream (x86_64) 88 MB/s | 37 MB 00:00 -Last metadata expiration check: 0:00:07 ago on Mon 12 Sep 2022 03:23:32 PM UTC. -Package yum-utils-4.0.21-11.0.1.el8.noarch is already installed. -Package vim-minimal-2:8.0.1763-19.0.1.el8_6.2.x86_64 is already installed. -Package procps-ng-3.3.15-6.0.1.el8.x86_64 is already installed. -Dependencies resolved. -================================================================================ - Package Arch Version Repository Size -================================================================================ -Installing: - bind-utils x86_64 32:9.11.36-3.el8 ol8_appstream 452 k - expect x86_64 5.45.4-5.el8 ol8_baseos_latest 266 k - hostname x86_64 3.20-6.el8 ol8_baseos_latest 32 k - net-tools x86_64 2.0-0.52.20160912git.el8 ol8_baseos_latest 322 k - openssl x86_64 1:1.1.1k-7.el8_6 ol8_baseos_latest 709 k - sudo x86_64 1.8.29-8.el8 ol8_baseos_latest 925 k - tar x86_64 2:1.30-5.el8 ol8_baseos_latest 838 k - tree x86_64 1.7.0-15.el8 ol8_baseos_latest 59 k - unzip x86_64 6.0-46.0.1.el8 ol8_baseos_latest 196 k - wget x86_64 1.19.5-10.0.1.el8 ol8_appstream 734 k - which x86_64 2.21-17.el8 ol8_baseos_latest 49 k - zip x86_64 3.0-23.el8 ol8_baseos_latest 270 k -Upgrading: - openssl-libs x86_64 1:1.1.1k-7.el8_6 ol8_baseos_latest 1.5 M - vim-minimal x86_64 2:8.0.1763-19.0.1.el8_6.4 ol8_baseos_latest 575 k -Installing dependencies: - bind-libs x86_64 32:9.11.36-3.el8 ol8_appstream 175 k - bind-libs-lite x86_64 32:9.11.36-3.el8 ol8_appstream 1.2 M - bind-license noarch 32:9.11.36-3.el8 ol8_appstream 103 k - fstrm x86_64 0.6.1-2.el8 ol8_appstream 29 k - libmaxminddb x86_64 1.2.0-10.el8 ol8_appstream 33 k - libmetalink x86_64 0.1.3-7.el8 ol8_baseos_latest 32 k - protobuf-c x86_64 1.3.0-6.el8 ol8_appstream 37 k - python3-bind noarch 32:9.11.36-3.el8 ol8_appstream 150 k - python3-ply noarch 3.9-9.el8 ol8_baseos_latest 111 k - tcl x86_64 1:8.6.8-2.el8 ol8_baseos_latest 1.1 M - -Transaction Summary -================================================================================ -Install 22 Packages -Upgrade 2 Packages - -Total download size: 9.7 M -Downloading Packages: -(1/24): expect-5.45.4-5.el8.x86_64.rpm 158 kB/s | 266 kB 00:01 -(2/24): hostname-3.20-6.el8.x86_64.rpm 18 kB/s | 32 kB 00:01 -(3/24): libmetalink-0.1.3-7.el8.x86_64.rpm 18 kB/s | 32 kB 00:01 -(4/24): net-tools-2.0-0.52.20160912git.el8.x86_ 2.3 MB/s | 322 kB 00:00 -(5/24): openssl-1.1.1k-7.el8_6.x86_64.rpm 4.0 MB/s | 709 kB 00:00 -(6/24): python3-ply-3.9-9.el8.noarch.rpm 538 kB/s | 111 kB 00:00 -(7/24): sudo-1.8.29-8.el8.x86_64.rpm 5.0 MB/s | 925 kB 00:00 -(8/24): tar-1.30-5.el8.x86_64.rpm 4.2 MB/s | 838 kB 00:00 -(9/24): unzip-6.0-46.0.1.el8.x86_64.rpm 3.6 MB/s | 196 kB 00:00 -(10/24): tcl-8.6.8-2.el8.x86_64.rpm 4.1 MB/s | 1.1 MB 00:00 -(11/24): which-2.21-17.el8.x86_64.rpm 613 kB/s | 49 kB 00:00 -(12/24): tree-1.7.0-15.el8.x86_64.rpm 208 kB/s | 59 kB 00:00 -(13/24): bind-libs-9.11.36-3.el8.x86_64.rpm 1.3 MB/s | 175 kB 00:00 -(14/24): bind-license-9.11.36-3.el8.noarch.rpm 2.6 MB/s | 103 kB 00:00 -(15/24): bind-libs-lite-9.11.36-3.el8.x86_64.rp 6.8 MB/s | 1.2 MB 00:00 -(16/24): bind-utils-9.11.36-3.el8.x86_64.rpm 3.6 MB/s | 452 kB 00:00 -(17/24): zip-3.0-23.el8.x86_64.rpm 804 kB/s | 270 kB 00:00 -(18/24): libmaxminddb-1.2.0-10.el8.x86_64.rpm 529 kB/s | 33 kB 00:00 -(19/24): fstrm-0.6.1-2.el8.x86_64.rpm 161 kB/s | 29 kB 00:00 -(20/24): python3-bind-9.11.36-3.el8.noarch.rpm 2.0 MB/s | 150 kB 00:00 -(21/24): protobuf-c-1.3.0-6.el8.x86_64.rpm 351 kB/s | 37 kB 00:00 -(22/24): vim-minimal-8.0.1763-19.0.1.el8_6.4.x8 6.4 MB/s | 575 kB 00:00 -(23/24): wget-1.19.5-10.0.1.el8.x86_64.rpm 3.3 MB/s | 734 kB 00:00 -(24/24): openssl-libs-1.1.1k-7.el8_6.x86_64.rpm 6.8 MB/s | 1.5 MB 00:00 --------------------------------------------------------------------------------- -Total 3.3 MB/s | 9.7 MB 00:02 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Preparing : 1/1 - Upgrading : openssl-libs-1:1.1.1k-7.el8_6.x86_64 1/26 - Running scriptlet: openssl-libs-1:1.1.1k-7.el8_6.x86_64 1/26 - Installing : protobuf-c-1.3.0-6.el8.x86_64 2/26 - Installing : libmaxminddb-1.2.0-10.el8.x86_64 3/26 - Running scriptlet: libmaxminddb-1.2.0-10.el8.x86_64 3/26 - Installing : fstrm-0.6.1-2.el8.x86_64 4/26 - Installing : bind-license-32:9.11.36-3.el8.noarch 5/26 - Installing : bind-libs-lite-32:9.11.36-3.el8.x86_64 6/26 - Installing : bind-libs-32:9.11.36-3.el8.x86_64 7/26 - Upgrading : vim-minimal-2:8.0.1763-19.0.1.el8_6.4.x86_64 8/26 - Installing : unzip-6.0-46.0.1.el8.x86_64 9/26 - Installing : tcl-1:8.6.8-2.el8.x86_64 10/26 - Running scriptlet: tcl-1:8.6.8-2.el8.x86_64 10/26 - Installing : python3-ply-3.9-9.el8.noarch 11/26 - Installing : python3-bind-32:9.11.36-3.el8.noarch 12/26 - Installing : libmetalink-0.1.3-7.el8.x86_64 13/26 - Installing : wget-1.19.5-10.0.1.el8.x86_64 14/26 - Running scriptlet: wget-1.19.5-10.0.1.el8.x86_64 14/26 - Installing : bind-utils-32:9.11.36-3.el8.x86_64 15/26 - Installing : expect-5.45.4-5.el8.x86_64 16/26 - Installing : zip-3.0-23.el8.x86_64 17/26 - Installing : sudo-1.8.29-8.el8.x86_64 18/26 - Running scriptlet: sudo-1.8.29-8.el8.x86_64 18/26 - Installing : openssl-1:1.1.1k-7.el8_6.x86_64 19/26 - Installing : which-2.21-17.el8.x86_64 20/26 - Installing : tree-1.7.0-15.el8.x86_64 21/26 - Installing : tar-2:1.30-5.el8.x86_64 22/26 - Running scriptlet: tar-2:1.30-5.el8.x86_64 22/26 - Installing : net-tools-2.0-0.52.20160912git.el8.x86_64 23/26 - Running scriptlet: net-tools-2.0-0.52.20160912git.el8.x86_64 23/26 - Installing : hostname-3.20-6.el8.x86_64 24/26 - Running scriptlet: hostname-3.20-6.el8.x86_64 24/26 - Cleanup : vim-minimal-2:8.0.1763-19.0.1.el8_6.2.x86_64 25/26 - Cleanup : openssl-libs-1:1.1.1k-6.el8_5.x86_64 26/26 - Running scriptlet: openssl-libs-1:1.1.1k-6.el8_5.x86_64 26/26 - Verifying : expect-5.45.4-5.el8.x86_64 1/26 - Verifying : hostname-3.20-6.el8.x86_64 2/26 - Verifying : libmetalink-0.1.3-7.el8.x86_64 3/26 - Verifying : net-tools-2.0-0.52.20160912git.el8.x86_64 4/26 - Verifying : openssl-1:1.1.1k-7.el8_6.x86_64 5/26 - Verifying : python3-ply-3.9-9.el8.noarch 6/26 - Verifying : sudo-1.8.29-8.el8.x86_64 7/26 - Verifying : tar-2:1.30-5.el8.x86_64 8/26 - Verifying : tcl-1:8.6.8-2.el8.x86_64 9/26 - Verifying : tree-1.7.0-15.el8.x86_64 10/26 - Verifying : unzip-6.0-46.0.1.el8.x86_64 11/26 - Verifying : which-2.21-17.el8.x86_64 12/26 - Verifying : zip-3.0-23.el8.x86_64 13/26 - Verifying : bind-libs-32:9.11.36-3.el8.x86_64 14/26 - Verifying : bind-libs-lite-32:9.11.36-3.el8.x86_64 15/26 - Verifying : bind-license-32:9.11.36-3.el8.noarch 16/26 - Verifying : bind-utils-32:9.11.36-3.el8.x86_64 17/26 - Verifying : fstrm-0.6.1-2.el8.x86_64 18/26 - Verifying : libmaxminddb-1.2.0-10.el8.x86_64 19/26 - Verifying : protobuf-c-1.3.0-6.el8.x86_64 20/26 - Verifying : python3-bind-32:9.11.36-3.el8.noarch 21/26 - Verifying : wget-1.19.5-10.0.1.el8.x86_64 22/26 - Verifying : openssl-libs-1:1.1.1k-7.el8_6.x86_64 23/26 - Verifying : openssl-libs-1:1.1.1k-6.el8_5.x86_64 24/26 - Verifying : vim-minimal-2:8.0.1763-19.0.1.el8_6.4.x86_64 25/26 - Verifying : vim-minimal-2:8.0.1763-19.0.1.el8_6.2.x86_64 26/26 - -Upgraded: - openssl-libs-1:1.1.1k-7.el8_6.x86_64 - vim-minimal-2:8.0.1763-19.0.1.el8_6.4.x86_64 -Installed: - bind-libs-32:9.11.36-3.el8.x86_64 - bind-libs-lite-32:9.11.36-3.el8.x86_64 - bind-license-32:9.11.36-3.el8.noarch - bind-utils-32:9.11.36-3.el8.x86_64 - expect-5.45.4-5.el8.x86_64 - fstrm-0.6.1-2.el8.x86_64 - hostname-3.20-6.el8.x86_64 - libmaxminddb-1.2.0-10.el8.x86_64 - libmetalink-0.1.3-7.el8.x86_64 - net-tools-2.0-0.52.20160912git.el8.x86_64 - openssl-1:1.1.1k-7.el8_6.x86_64 - protobuf-c-1.3.0-6.el8.x86_64 - python3-bind-32:9.11.36-3.el8.noarch - python3-ply-3.9-9.el8.noarch - sudo-1.8.29-8.el8.x86_64 - tar-2:1.30-5.el8.x86_64 - tcl-1:8.6.8-2.el8.x86_64 - tree-1.7.0-15.el8.x86_64 - unzip-6.0-46.0.1.el8.x86_64 - wget-1.19.5-10.0.1.el8.x86_64 - which-2.21-17.el8.x86_64 - zip-3.0-23.el8.x86_64 - -Complete! -Adding repo from: http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 -created by dnf config-manager from http://yum.o 221 kB/s | 45 kB 00:00 -Dependencies resolved. -============================================================================================= - Package Arch Version Repository Size -============================================================================================= -Installing: - java-11-openjdk-devel x86_64 1:11.0.16.1.1-1.el8_6 ol8_appstream 3.4 M -Installing dependencies: - alsa-lib x86_64 1.2.6.1-3.el8 ol8_appstream 491 k - avahi-libs x86_64 0.7-20.el8 ol8_baseos_latest 62 k - copy-jdk-configs noarch 4.0-2.el8 ol8_appstream 30 k - crypto-policies-scripts noarch 20211116-1.gitae470d6.el8 ol8_baseos_latest 83 k - cups-libs x86_64 1:2.2.6-45.el8_6.2 ol8_baseos_latest 434 k - giflib x86_64 5.1.4-3.el8 ol8_appstream 51 k - graphite2 x86_64 1.3.10-10.el8 ol8_appstream 122 k - harfbuzz x86_64 1.7.5-3.el8 ol8_appstream 295 k - java-11-openjdk x86_64 1:11.0.16.1.1-1.el8_6 ol8_appstream 272 k - java-11-openjdk-headless x86_64 1:11.0.16.1.1-1.el8_6 ol8_appstream 40 M - javapackages-filesystem noarch 5.3.0-1.module+el8+5136+7ff78f74 ol8_appstream 30 k - lcms2 x86_64 2.9-2.el8 ol8_appstream 164 k - libX11 x86_64 1.6.8-5.el8 ol8_appstream 611 k - libX11-common noarch 1.6.8-5.el8 ol8_appstream 158 k - libXau x86_64 1.0.9-3.el8 ol8_appstream 37 k - libXcomposite x86_64 0.4.4-14.el8 ol8_appstream 28 k - libXext x86_64 1.3.4-1.el8 ol8_appstream 45 k - libXi x86_64 1.7.10-1.el8 ol8_appstream 49 k - libXrender x86_64 0.9.10-7.el8 ol8_appstream 33 k - libXtst x86_64 1.2.3-7.el8 ol8_appstream 22 k - libfontenc x86_64 1.1.3-8.el8 ol8_appstream 37 k - libjpeg-turbo x86_64 1.5.3-12.el8 ol8_appstream 157 k - libpkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 35 k - libxcb x86_64 1.13.1-1.el8 ol8_appstream 231 k - lksctp-tools x86_64 1.0.18-3.el8 ol8_baseos_latest 100 k - lua x86_64 5.3.4-12.el8 ol8_appstream 192 k - nspr x86_64 4.32.0-1.el8_4 ol8_appstream 142 k - nss x86_64 3.67.0-7.el8_5 ol8_appstream 741 k - nss-softokn x86_64 3.67.0-7.el8_5 ol8_appstream 487 k - nss-softokn-freebl x86_64 3.67.0-7.el8_5 ol8_appstream 395 k - nss-sysinit x86_64 3.67.0-7.el8_5 ol8_appstream 73 k - nss-util x86_64 3.67.0-7.el8_5 ol8_appstream 137 k - pkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 38 k - pkgconf-m4 noarch 1.4.2-1.el8 ol8_baseos_latest 17 k - pkgconf-pkg-config x86_64 1.4.2-1.el8 ol8_baseos_latest 15 k - ttmkfdir x86_64 3.0.9-54.el8 ol8_appstream 62 k - tzdata-java noarch 2022c-1.el8 ol8_appstream 186 k - xorg-x11-font-utils x86_64 1:7.5-41.el8 ol8_appstream 104 k - xorg-x11-fonts-Type1 noarch 7.5-19.el8 ol8_appstream 522 k -Enabling module streams: - javapackages-runtime 201801 - -Transaction Summary -============================================================================================= -Install 40 Packages - -Total download size: 50 M -Installed size: 194 M -Downloading Packages: -(1/40): crypto-policies-scripts-20211116-1.gita 1.3 MB/s | 83 kB 00:00 -(2/40): avahi-libs-0.7-20.el8.x86_64.rpm 952 kB/s | 62 kB 00:00 -(3/40): libpkgconf-1.4.2-1.el8.x86_64.rpm 2.2 MB/s | 35 kB 00:00 -(4/40): cups-libs-2.2.6-45.el8_6.2.x86_64.rpm 4.9 MB/s | 434 kB 00:00 -(5/40): lksctp-tools-1.0.18-3.el8.x86_64.rpm 3.9 MB/s | 100 kB 00:00 -(6/40): pkgconf-1.4.2-1.el8.x86_64.rpm 2.3 MB/s | 38 kB 00:00 -(7/40): pkgconf-m4-1.4.2-1.el8.noarch.rpm 1.1 MB/s | 17 kB 00:00 -(8/40): pkgconf-pkg-config-1.4.2-1.el8.x86_64.r 1.1 MB/s | 15 kB 00:00 -(9/40): copy-jdk-configs-4.0-2.el8.noarch.rpm 1.8 MB/s | 30 kB 00:00 -(10/40): giflib-5.1.4-3.el8.x86_64.rpm 3.0 MB/s | 51 kB 00:00 -(11/40): alsa-lib-1.2.6.1-3.el8.x86_64.rpm 12 MB/s | 491 kB 00:00 -(12/40): graphite2-1.3.10-10.el8.x86_64.rpm 5.9 MB/s | 122 kB 00:00 -(13/40): harfbuzz-1.7.5-3.el8.x86_64.rpm 13 MB/s | 295 kB 00:00 -(14/40): java-11-openjdk-11.0.16.1.1-1.el8_6.x8 15 MB/s | 272 kB 00:00 -(15/40): javapackages-filesystem-5.3.0-1.module 2.1 MB/s | 30 kB 00:00 -(16/40): lcms2-2.9-2.el8.x86_64.rpm 9.5 MB/s | 164 kB 00:00 -(17/40): libX11-1.6.8-5.el8.x86_64.rpm 24 MB/s | 611 kB 00:00 -(18/40): java-11-openjdk-devel-11.0.16.1.1-1.el 40 MB/s | 3.4 MB 00:00 -(19/40): libX11-common-1.6.8-5.el8.noarch.rpm 8.6 MB/s | 158 kB 00:00 -(20/40): libXau-1.0.9-3.el8.x86_64.rpm 2.6 MB/s | 37 kB 00:00 -(21/40): libXcomposite-0.4.4-14.el8.x86_64.rpm 2.2 MB/s | 28 kB 00:00 -(22/40): libXext-1.3.4-1.el8.x86_64.rpm 2.7 MB/s | 45 kB 00:00 -(23/40): libXi-1.7.10-1.el8.x86_64.rpm 2.8 MB/s | 49 kB 00:00 -(24/40): libXrender-0.9.10-7.el8.x86_64.rpm 2.4 MB/s | 33 kB 00:00 -(25/40): libXtst-1.2.3-7.el8.x86_64.rpm 1.6 MB/s | 22 kB 00:00 -(26/40): libfontenc-1.1.3-8.el8.x86_64.rpm 2.7 MB/s | 37 kB 00:00 -(27/40): libjpeg-turbo-1.5.3-12.el8.x86_64.rpm 9.6 MB/s | 157 kB 00:00 -(28/40): libxcb-1.13.1-1.el8.x86_64.rpm 13 MB/s | 231 kB 00:00 -(29/40): lua-5.3.4-12.el8.x86_64.rpm 11 MB/s | 192 kB 00:00 -(30/40): nspr-4.32.0-1.el8_4.x86_64.rpm 9.2 MB/s | 142 kB 00:00 -(31/40): nss-3.67.0-7.el8_5.x86_64.rpm 31 MB/s | 741 kB 00:00 -(32/40): nss-softokn-3.67.0-7.el8_5.x86_64.rpm 24 MB/s | 487 kB 00:00 -(33/40): nss-softokn-freebl-3.67.0-7.el8_5.x86_ 18 MB/s | 395 kB 00:00 -(34/40): nss-sysinit-3.67.0-7.el8_5.x86_64.rpm 4.3 MB/s | 73 kB 00:00 -(35/40): nss-util-3.67.0-7.el8_5.x86_64.rpm 8.7 MB/s | 137 kB 00:00 -(36/40): ttmkfdir-3.0.9-54.el8.x86_64.rpm 4.0 MB/s | 62 kB 00:00 -(37/40): tzdata-java-2022c-1.el8.noarch.rpm 12 MB/s | 186 kB 00:00 -(38/40): xorg-x11-font-utils-7.5-41.el8.x86_64. 6.0 MB/s | 104 kB 00:00 -(39/40): xorg-x11-fonts-Type1-7.5-19.el8.noarch 23 MB/s | 522 kB 00:00 -(40/40): java-11-openjdk-headless-11.0.16.1.1-1 73 MB/s | 40 MB 00:00 --------------------------------------------------------------------------------- -Total 71 MB/s | 50 MB 00:00 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 1/1 - Running scriptlet: java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86_6 1/1 - Preparing : 1/1 - Installing : nspr-4.32.0-1.el8_4.x86_64 1/40 - Running scriptlet: nspr-4.32.0-1.el8_4.x86_64 1/40 - Installing : nss-util-3.67.0-7.el8_5.x86_64 2/40 - Installing : libjpeg-turbo-1.5.3-12.el8.x86_64 3/40 - Installing : nss-softokn-freebl-3.67.0-7.el8_5.x86_64 4/40 - Installing : nss-softokn-3.67.0-7.el8_5.x86_64 5/40 - Installing : tzdata-java-2022c-1.el8.noarch 6/40 - Installing : ttmkfdir-3.0.9-54.el8.x86_64 7/40 - Installing : lua-5.3.4-12.el8.x86_64 8/40 - Installing : copy-jdk-configs-4.0-2.el8.noarch 9/40 - Installing : libfontenc-1.1.3-8.el8.x86_64 10/40 - Installing : libXau-1.0.9-3.el8.x86_64 11/40 - Installing : libxcb-1.13.1-1.el8.x86_64 12/40 - Installing : libX11-common-1.6.8-5.el8.noarch 13/40 - Installing : libX11-1.6.8-5.el8.x86_64 14/40 - Installing : libXext-1.3.4-1.el8.x86_64 15/40 - Installing : libXi-1.7.10-1.el8.x86_64 16/40 - Installing : libXtst-1.2.3-7.el8.x86_64 17/40 - Installing : libXcomposite-0.4.4-14.el8.x86_64 18/40 - Installing : libXrender-0.9.10-7.el8.x86_64 19/40 - Installing : lcms2-2.9-2.el8.x86_64 20/40 - Running scriptlet: lcms2-2.9-2.el8.x86_64 20/40 - Installing : javapackages-filesystem-5.3.0-1.module+el8+5136+7f 21/40 - Installing : graphite2-1.3.10-10.el8.x86_64 22/40 - Installing : harfbuzz-1.7.5-3.el8.x86_64 23/40 - Running scriptlet: harfbuzz-1.7.5-3.el8.x86_64 23/40 - Installing : giflib-5.1.4-3.el8.x86_64 24/40 - Installing : alsa-lib-1.2.6.1-3.el8.x86_64 25/40 - Running scriptlet: alsa-lib-1.2.6.1-3.el8.x86_64 25/40 - Installing : pkgconf-m4-1.4.2-1.el8.noarch 26/40 - Installing : lksctp-tools-1.0.18-3.el8.x86_64 27/40 - Running scriptlet: lksctp-tools-1.0.18-3.el8.x86_64 27/40 - Installing : libpkgconf-1.4.2-1.el8.x86_64 28/40 - Installing : pkgconf-1.4.2-1.el8.x86_64 29/40 - Installing : pkgconf-pkg-config-1.4.2-1.el8.x86_64 30/40 - Installing : xorg-x11-font-utils-1:7.5-41.el8.x86_64 31/40 - Installing : xorg-x11-fonts-Type1-7.5-19.el8.noarch 32/40 - Running scriptlet: xorg-x11-fonts-Type1-7.5-19.el8.noarch 32/40 - Installing : crypto-policies-scripts-20211116-1.gitae470d6.el8. 33/40 - Installing : nss-sysinit-3.67.0-7.el8_5.x86_64 34/40 - Installing : nss-3.67.0-7.el8_5.x86_64 35/40 - Installing : avahi-libs-0.7-20.el8.x86_64 36/40 - Installing : cups-libs-1:2.2.6-45.el8_6.2.x86_64 37/40 - Installing : java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86 38/40 - Running scriptlet: java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86 38/40 - Installing : java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 39/40 - Running scriptlet: java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 39/40 - Installing : java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 40/40 - Running scriptlet: java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 40/40 - Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 40/40 - Running scriptlet: crypto-policies-scripts-20211116-1.gitae470d6.el8. 40/40 - Running scriptlet: nss-3.67.0-7.el8_5.x86_64 40/40 - Running scriptlet: java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86 40/40 - Running scriptlet: java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 40/40 - Running scriptlet: java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 40/40 - Verifying : avahi-libs-0.7-20.el8.x86_64 1/40 - Verifying : crypto-policies-scripts-20211116-1.gitae470d6.el8. 2/40 - Verifying : cups-libs-1:2.2.6-45.el8_6.2.x86_64 3/40 - Verifying : libpkgconf-1.4.2-1.el8.x86_64 4/40 - Verifying : lksctp-tools-1.0.18-3.el8.x86_64 5/40 - Verifying : pkgconf-1.4.2-1.el8.x86_64 6/40 - Verifying : pkgconf-m4-1.4.2-1.el8.noarch 7/40 - Verifying : pkgconf-pkg-config-1.4.2-1.el8.x86_64 8/40 - Verifying : alsa-lib-1.2.6.1-3.el8.x86_64 9/40 - Verifying : copy-jdk-configs-4.0-2.el8.noarch 10/40 - Verifying : giflib-5.1.4-3.el8.x86_64 11/40 - Verifying : graphite2-1.3.10-10.el8.x86_64 12/40 - Verifying : harfbuzz-1.7.5-3.el8.x86_64 13/40 - Verifying : java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 14/40 - Verifying : java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 15/40 - Verifying : java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86 16/40 - Verifying : javapackages-filesystem-5.3.0-1.module+el8+5136+7f 17/40 - Verifying : lcms2-2.9-2.el8.x86_64 18/40 - Verifying : libX11-1.6.8-5.el8.x86_64 19/40 - Verifying : libX11-common-1.6.8-5.el8.noarch 20/40 - Verifying : libXau-1.0.9-3.el8.x86_64 21/40 - Verifying : libXcomposite-0.4.4-14.el8.x86_64 22/40 - Verifying : libXext-1.3.4-1.el8.x86_64 23/40 - Verifying : libXi-1.7.10-1.el8.x86_64 24/40 - Verifying : libXrender-0.9.10-7.el8.x86_64 25/40 - Verifying : libXtst-1.2.3-7.el8.x86_64 26/40 - Verifying : libfontenc-1.1.3-8.el8.x86_64 27/40 - Verifying : libjpeg-turbo-1.5.3-12.el8.x86_64 28/40 - Verifying : libxcb-1.13.1-1.el8.x86_64 29/40 - Verifying : lua-5.3.4-12.el8.x86_64 30/40 - Verifying : nspr-4.32.0-1.el8_4.x86_64 31/40 - Verifying : nss-3.67.0-7.el8_5.x86_64 32/40 - Verifying : nss-softokn-3.67.0-7.el8_5.x86_64 33/40 - Verifying : nss-softokn-freebl-3.67.0-7.el8_5.x86_64 34/40 - Verifying : nss-sysinit-3.67.0-7.el8_5.x86_64 35/40 - Verifying : nss-util-3.67.0-7.el8_5.x86_64 36/40 - Verifying : ttmkfdir-3.0.9-54.el8.x86_64 37/40 - Verifying : tzdata-java-2022c-1.el8.noarch 38/40 - Verifying : xorg-x11-font-utils-1:7.5-41.el8.x86_64 39/40 - Verifying : xorg-x11-fonts-Type1-7.5-19.el8.noarch 40/40 - -Installed: - alsa-lib-1.2.6.1-3.el8.x86_64 - avahi-libs-0.7-20.el8.x86_64 - copy-jdk-configs-4.0-2.el8.noarch - crypto-policies-scripts-20211116-1.gitae470d6.el8.noarch - cups-libs-1:2.2.6-45.el8_6.2.x86_64 - giflib-5.1.4-3.el8.x86_64 - graphite2-1.3.10-10.el8.x86_64 - harfbuzz-1.7.5-3.el8.x86_64 - java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 - java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 - java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86_64 - javapackages-filesystem-5.3.0-1.module+el8+5136+7ff78f74.noarch - lcms2-2.9-2.el8.x86_64 - libX11-1.6.8-5.el8.x86_64 - libX11-common-1.6.8-5.el8.noarch - libXau-1.0.9-3.el8.x86_64 - libXcomposite-0.4.4-14.el8.x86_64 - libXext-1.3.4-1.el8.x86_64 - libXi-1.7.10-1.el8.x86_64 - libXrender-0.9.10-7.el8.x86_64 - libXtst-1.2.3-7.el8.x86_64 - libfontenc-1.1.3-8.el8.x86_64 - libjpeg-turbo-1.5.3-12.el8.x86_64 - libpkgconf-1.4.2-1.el8.x86_64 - libxcb-1.13.1-1.el8.x86_64 - lksctp-tools-1.0.18-3.el8.x86_64 - lua-5.3.4-12.el8.x86_64 - nspr-4.32.0-1.el8_4.x86_64 - nss-3.67.0-7.el8_5.x86_64 - nss-softokn-3.67.0-7.el8_5.x86_64 - nss-softokn-freebl-3.67.0-7.el8_5.x86_64 - nss-sysinit-3.67.0-7.el8_5.x86_64 - nss-util-3.67.0-7.el8_5.x86_64 - pkgconf-1.4.2-1.el8.x86_64 - pkgconf-m4-1.4.2-1.el8.noarch - pkgconf-pkg-config-1.4.2-1.el8.x86_64 - ttmkfdir-3.0.9-54.el8.x86_64 - tzdata-java-2022c-1.el8.noarch - xorg-x11-font-utils-1:7.5-41.el8.x86_64 - xorg-x11-fonts-Type1-7.5-19.el8.noarch - -Complete! -Last metadata expiration check: 0:00:10 ago on Mon 12 Sep 2022 03:23:49 PM UTC. -Dependencies resolved. -============================================================================================== - Package - Arch Version Repository Size -============================================================================================== -Installing: - ords noarch 22.2.1-2.el8 yum.oracle.com_repo_OracleLinux_OL8_oracle_software_x86_64 83 M -Installing dependencies: - lsof x86_64 4.93.2-1.el8 ol8_baseos_latest 253 k - -Transaction Summary -============================================================================================== -Install 2 Packages - -Total download size: 83 M -Installed size: 87 M -Downloading Packages: -(1/2): lsof-4.93.2-1.el8.x86_64.rpm 3.0 MB/s | 253 kB 00:00 -(2/2): ords-22.2.1-2.el8.noarch.rpm 56 MB/s | 83 MB 00:01 --------------------------------------------------------------------------------- -Total 56 MB/s | 83 MB 00:01 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Preparing : 1/1 - Installing : lsof-4.93.2-1.el8.x86_64 1/2 - Running scriptlet: ords-22.2.1-2.el8.noarch 2/2 - Installing : ords-22.2.1-2.el8.noarch 2/2 - Running scriptlet: ords-22.2.1-2.el8.noarch 2/2 -INFO: Before starting ORDS service, run the below command as user oracle: - ords --config /etc/ords/config install - - Verifying : lsof-4.93.2-1.el8.x86_64 1/2 - Verifying : ords-22.2.1-2.el8.noarch 2/2 - -Installed: - lsof-4.93.2-1.el8.x86_64 ords-22.2.1-2.el8.noarch - -Complete! -Last metadata expiration check: 0:00:15 ago on Mon 12 Sep 2022 03:23:49 PM UTC. -Package iproute-5.15.0-4.el8.x86_64 is already installed. -Dependencies resolved. -Nothing to do. -Complete! -24 files removed -Removing intermediate container c08b8dac80a5 - ---> bb1a717f3e6e -Step 5/10 : RUN mkdir -p $ORDS_HOME/doc_root && mkdir -p $ORDS_HOME/error && mkdir -p $ORDS_HOME/secrets && chmod ug+x $ORDS_HOME/*.sh && groupadd -g 54322 dba && usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && chown -R oracle:dba $ORDS_HOME && echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - ---> Running in 0103c070f4b6 -Removing intermediate container 0103c070f4b6 - ---> 089d06d9b198 -Step 6/10 : USER oracle - ---> Running in 51b1846c8c6f -Removing intermediate container 51b1846c8c6f - ---> 6c7b115954a4 -Step 7/10 : WORKDIR /home/oracle - ---> Running in 5862e2bc8df9 -Removing intermediate container 5862e2bc8df9 - ---> 28543543a88c -Step 8/10 : VOLUME ["$ORDS_HOME/config/ords"] - ---> Running in 465398d6f2bb -Removing intermediate container 465398d6f2bb - ---> 4037eb7f2f12 -Step 9/10 : EXPOSE 8888 - ---> Running in 2813ab5473f6 -Removing intermediate container 2813ab5473f6 - ---> 3410f1be2fff -Step 10/10 : CMD $ORDS_HOME/$RUN_FILE - ---> Running in 0a9a72408177 -Removing intermediate container 0a9a72408177 - ---> 2ef5dc95701b -Successfully built 2ef5dc95701b -Successfully tagged oracle/ords-dboper:22.2.1 - diff --git a/docs/multitenant/provisioning/ords_image.md b/docs/multitenant/provisioning/ords_image.md deleted file mode 100644 index 21abcbab..00000000 --- a/docs/multitenant/provisioning/ords_image.md +++ /dev/null @@ -1,64 +0,0 @@ - - -# Build ORDS Docker Image - -In the below steps, we are building an ORDS Docker Image for ORDS Software. The image built can be later pushed to a local repository to be used later for a deployment. - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -1. Clone the software using git: -```sh - git clone git@orahub.oci.oraclecorp.com:rac-docker-dev/oracle-database-operator.git - cd oracle-database-operator/ords/ -``` - -2. Login to the registry: container-registry.oracle.com - -**NOTE:** To login to this registry, you will need to the URL https://container-registry.oracle.com , Sign in, then click on "Java" and then accept the agreement. - -```bash -docker login container-registry.oracle.com -``` - -3. Login to a repo where you want to push your docker image (if needed) to pull during deployment in your environment. - -```bash -docker login -``` - -4. Build the docker image by using below command: - -```bash -docker build -t oracle/ords-dboper:ords-latest . -``` - -5. Check the docker image details using: - -```bash -docker images -``` - -6. Tag and push the image to your docker repository. - -NOTE: We have the repo as `phx.ocir.io//oracle/ords:latest`. Please change as per your environment. - -```bash -docker tag oracle/ords-dboper:ords-latest phx.ocir.io//oracle/ords:latest -docker push phx.ocir.io//oracle/ords:latest -``` - -7. Verify the image pushed to your docker repository. - -You can refer to below sample output for above steps as well. - -8. Create a Kubernetes Secret for your docker repository to pull the image during deployment using the below command: - -```bash -kubectl create secret generic container-registry-secret --from-file=.dockerconfigjson=./.docker/config.json --type=kubernetes.io/dockerconfigjson -n oracle-database-operator-system -``` - -This Kubernetes secret will be provided in the .yaml file against the parameter `ordsImagePullSecret` to pull the ORDS Docker Image from your docker repository (if its a private repository). - -## Sample Output - -[Here](./ords_image.log) is the sample output for docker image created for ORDS latest version diff --git a/docs/multitenant/provisioning/pdb.yaml b/docs/multitenant/provisioning/pdb.yaml deleted file mode 100644 index 82941185..00000000 --- a/docs/multitenant/provisioning/pdb.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Create" diff --git a/docs/multitenant/provisioning/pdb_crd_resource.md b/docs/multitenant/provisioning/pdb_crd_resource.md deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/multitenant/provisioning/pdb_secret.yaml b/docs/multitenant/provisioning/pdb_secret.yaml deleted file mode 100644 index cf385dda..00000000 --- a/docs/multitenant/provisioning/pdb_secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - sysadmin_user: "[ base64 encode values ]" - sysadmin_pwd: "[base64 encode values ]" diff --git a/docs/multitenant/provisioning/plug_pdb.log b/docs/multitenant/provisioning/plug_pdb.log deleted file mode 100644 index 3a0be247..00000000 --- a/docs/multitenant/provisioning/plug_pdb.log +++ /dev/null @@ -1,100 +0,0 @@ --- Check the status of the PDB CRD Resources: - -% kubectl get pdbs -A -No resources found - - - --- Verify from the CDB: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - - - --- Confirm the availability of the required .xml file: - -[oracle@goldhost1 ~]$ ls -lrt /tmp/pdbnewclone.xml --rw-r--r-- 1 oracle asmadmin 9920 Jun 27 06:26 /tmp/pdbnewclone.xml - - --- Use the below .yaml file for the plug in operation: - -% cat plug_pdb.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - xmlFileName: "/tmp/pdbnewclone.xml" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - action: "Plug" - - - --- Apply the .yaml file: - -% kubectl apply -f plug_pdb.yaml -pdb.database.oracle.com/pdb1 created - - --- Monitor the logs from the Oracle DB Operator Pod: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T04:28:36Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "UID": "bfad69af-36be-4792-87e3-639323300167", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T04:28:36Z INFO pdb-webhook ValidateCreate-Validating PDB spec for : pdb1 -2022-06-27T04:28:36Z INFO pdb-webhook validateCommon {"name": "pdb1"} -2022-06-27T04:28:36Z INFO pdb-webhook Valdiating PDB Resource Action : PLUG -2022-06-27T04:28:36Z INFO pdb-webhook PDB Resource : pdb1 successfully validated for Action : PLUG -2022-06-27T04:28:36Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "bfad69af-36be-4792-87e3-639323300167", "allowed": true} -2022-06-27T04:28:36Z INFO controllers.PDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/pdb1"} -2022-06-27T04:28:36Z INFO controllers.PDB Adding finalizer {"managePDBDeletion": "oracle-database-operator-system/pdb1"} -2022-06-27T04:28:36Z INFO controllers.PDB Found PDB: pdb1 {"checkDuplicatePDB": "oracle-database-operator-system/pdb1"} -2022-06-27T04:28:36Z INFO controllers.PDB Validating PDB phase for: pdb1 {"validatePhase": "oracle-database-operator-system/pdb1", "Action": "PLUG"} -2022-06-27T04:28:36Z INFO controllers.PDB Validation complete {"validatePhase": "oracle-database-operator-system/pdb1"} -2022-06-27T04:28:36Z INFO controllers.PDB PDB: {"onpremdboperator": "oracle-database-operator-system/pdb1", "Name": "pdb1", "Phase": "Plugging", "Status": "false"} -2022-06-27T04:28:36Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:28:36Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/", "Action": "POST"} -2022-06-27T04:28:36Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:29:07Z INFO controllers.PDB Successfully plugged PDB {"plugPDB": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew"} -2022-06-27T04:29:07Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:29:07Z DEBUG events Normal {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1","uid":"dd9bef3c-e493-4d5a-ae82-b24cbf5d0be3","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101465242"}, "reason": "Created", "message": "PDB 'pdbnew' plugged successfully"} -2022-06-27T04:29:07Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "GET"} -2022-06-27T04:29:07Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:29:07Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew", "State": "READ WRITE"} -2022-06-27T04:29:07Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1"} - - - --- Confirm the PDB CRD resource has been created and the PDB has been plugged in to the target CDB: - -% kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew READ WRITE 1G Ready Success - - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 4 PDBNEW READ WRITE NO diff --git a/docs/multitenant/provisioning/plug_pdb.md b/docs/multitenant/provisioning/plug_pdb.md deleted file mode 100644 index 02645f52..00000000 --- a/docs/multitenant/provisioning/plug_pdb.md +++ /dev/null @@ -1,44 +0,0 @@ -# Plug in a PDB using Oracle DB Operator On-Prem Controller in a target CDB - -In this use case, a PDB is plugged in using Oracle DB Operator On-Prem controller using an existing .xml file which was generated when the PDB was unplugged from this target CDB or another CDB. - -To plug in a PDB CRD Resource, a sample .yaml file is available here: [config/samples/onpremdb/pdb_plug.yaml](../../../config/samples/onpremdb/pdb_plug.yaml) - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -This example uses `plug_pdb.yaml` to plug in a PDB to a target CDB using Oracle DB Operator On-Prem Controller with: - -- Pluggable Database CRD Resource Name as `pdb1` -- Pluggable Database (PDB) Name as `pdbnew` -- Target CDB CRD Resource Name as `cdb-dev` -- CDB Name as `goldcdb` -- Action to be taken on the PDB as `Plug` -- XML metadata filename as `/tmp/pdbnewclone.xml` -- Source File Name Conversion as `NONE` -- File Name Conversion as `NONE` -- Copy Action as `MOVE` -- PDB Size as `1G` -- Temporary tablespace Size as `100M` - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -**NOTE:** Before performing the plug inoperation, you will first need to confirm the availability of the .xml file and the PDB datafiles. - -Use the file: [plug_pdb.yaml](./plug_pdb.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -% kubectl apply -f plug_pdb.yaml -``` - -2. Monitor the Oracle DB Operator Pod for the progress of the PDB Unplug operation: - -NOTE: Check the DB Operator Pod name in your environment. - -```sh -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./plug_pdb.log) is the sample output for a PDB created using Oracle DB Operator On-Prem Controller using file [plug_pdb.yaml](./plug_pdb.yaml) diff --git a/docs/multitenant/provisioning/plug_pdb.yaml b/docs/multitenant/provisioning/plug_pdb.yaml deleted file mode 100644 index a27d3255..00000000 --- a/docs/multitenant/provisioning/plug_pdb.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - xmlFileName: "/tmp/pdbnewclone.xml" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - action: "Plug" diff --git a/docs/multitenant/provisioning/unplug_pdb.log b/docs/multitenant/provisioning/unplug_pdb.log deleted file mode 100644 index c0995f83..00000000 --- a/docs/multitenant/provisioning/unplug_pdb.log +++ /dev/null @@ -1,165 +0,0 @@ --- Check the status of the PDB CRD resource: - -% kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew READ WRITE 1G Ready Success - - --- Verify the status from the CDB: - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW READ WRITE NO - - --- Use the below .yaml file to close the PDB: - -% cat modify_pdb_close.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - - --- Apply the .yaml file: - -% kubectl apply -f modify_pdb_close.yaml -pdb.database.oracle.com/pdb1 configured - - --- Monitor the Oracle DB Operator Pod logs: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T04:25:00Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "UID": "1623a6e2-d7dc-4b0f-8aa8-efada76cac13", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T04:25:00Z INFO pdb-webhook Setting default values in PDB spec for : pdb1 -2022-06-27T04:25:00Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "1623a6e2-d7dc-4b0f-8aa8-efada76cac13", "allowed": true} -2022-06-27T04:25:00Z INFO controllers.PDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/pdb1"} -2022-06-27T04:25:00Z INFO controllers.PDB Validating PDB phase for: pdb1 {"validatePhase": "oracle-database-operator-system/pdb1", "Action": "MODIFY"} -2022-06-27T04:25:00Z INFO controllers.PDB Validation complete {"validatePhase": "oracle-database-operator-system/pdb1"} -2022-06-27T04:25:00Z INFO controllers.PDB PDB: {"onpremdboperator": "oracle-database-operator-system/pdb1", "Name": "pdb1", "Phase": "Modifying", "Status": "false"} -2022-06-27T04:25:00Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:25:00Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "GET"} -2022-06-27T04:25:00Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:25:00Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew", "State": "READ WRITE"} -2022-06-27T04:25:00Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:25:01Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "POST"} -2022-06-27T04:25:01Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:25:02Z INFO controllers.PDB Successfully modified PDB state {"modifyPDB": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew"} -2022-06-27T04:25:02Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:25:02Z DEBUG events Normal {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1","uid":"447346c7-cfb0-43ed-abb2-a0fac844a3e4","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101464133"}, "reason": "Modified", "message": "PDB 'pdbnew' modified successfully"} -2022-06-27T04:25:02Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/status", "Action": "GET"} -2022-06-27T04:25:02Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:25:02Z INFO controllers.PDB Successfully obtained PDB state {"getPDBState": "oracle-database-operator-system/pdb1", "PDB Name": "pdbnew", "State": "MOUNTED"} -2022-06-27T04:25:02Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1"} - - - --- Confirm the PDB is now in MOUNT status: - -% kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 goldhost-scan.lbsub52b3b1cae.okecluster.oraclevcn.com:1521/pdbnew goldcdb pdbnew MOUNTED 1G Ready Success - - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - 3 PDBNEW MOUNTED - - - - --- Use the below .yaml file to unplug the PDB: - -% cat unplug_pdb.yaml -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - xmlFileName: "/tmp/pdbnewclone.xml" - action: "Unplug" - - --- Apply the .yaml file: - -% kubectl apply -f unplug_pdb.yaml -pdb.database.oracle.com/pdb1 configured - - --- Monitor the Oracle DB Operator Pod logs: - -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -. -. -2022-06-27T04:26:10Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "UID": "0f292426-8839-46b6-ba30-b3ffeee7e644", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T04:26:10Z INFO pdb-webhook Setting default values in PDB spec for : pdb1 -2022-06-27T04:26:10Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "0f292426-8839-46b6-ba30-b3ffeee7e644", "allowed": true} -2022-06-27T04:26:10Z INFO controllers.PDB Reconcile requested {"onpremdboperator": "oracle-database-operator-system/pdb1"} -2022-06-27T04:26:10Z INFO controllers.PDB Validating PDB phase for: pdb1 {"validatePhase": "oracle-database-operator-system/pdb1", "Action": "UNPLUG"} -2022-06-27T04:26:10Z INFO controllers.PDB Validation complete {"validatePhase": "oracle-database-operator-system/pdb1"} -2022-06-27T04:26:10Z INFO controllers.PDB PDB: {"onpremdboperator": "oracle-database-operator-system/pdb1", "Name": "pdb1", "Phase": "Unplugging", "Status": "false"} -2022-06-27T04:26:10Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:26:10Z INFO controllers.PDB Issuing REST call {"callAPI": "oracle-database-operator-system/pdb1", "URL": "http://cdb-dev-ords:8888/ords/_/db-api/latest/database/pdbs/pdbnew/", "Action": "POST"} -2022-06-27T04:26:10Z INFO controllers.PDB Found CR for CDB {"getCDBResource": "oracle-database-operator-system/pdb1", "Name": "cdb-dev", "CR Name": "cdb-dev"} -2022-06-27T04:26:18Z INFO controllers.PDB Removing finalizer {"unplugPDB": "oracle-database-operator-system/pdb1"} -2022-06-27T04:26:19Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "UID": "ce9e1a52-a372-4e3b-b148-c1e31bcb26f8", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2022-06-27T04:26:19Z INFO pdb-webhook ValidateUpdate-Validating PDB spec for : pdb1 -2022-06-27T04:26:19Z INFO pdb-webhook validateCommon {"name": "pdb1"} -2022-06-27T04:26:19Z INFO pdb-webhook Valdiating PDB Resource Action : UNPLUG -2022-06-27T04:26:19Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "ce9e1a52-a372-4e3b-b148-c1e31bcb26f8", "allowed": true} -2022-06-27T04:26:19Z INFO controllers.PDB Successfully unplugged PDB resource {"unplugPDB": "oracle-database-operator-system/pdb1"} -2022-06-27T04:26:19Z INFO controllers.PDB Reconcile completed {"onpremdboperator": "oracle-database-operator-system/pdb1"} -2022-06-27T04:26:19Z DEBUG events Normal {"object": {"kind":"PDB","namespace":"oracle-database-operator-system","name":"pdb1","uid":"447346c7-cfb0-43ed-abb2-a0fac844a3e4","apiVersion":"database.oracle.com/v1alpha1","resourceVersion":"101464533"}, "reason": "Unplugged", "message": "PDB 'pdbnew' unplugged successfully"} - - - --- Confirm the PDB has been unplugged: - -% kubectl get pdbs -A -No resources found - - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ WRITE NO - - - --- Confirm the .xml file generated in the CDB host: - -[oracle@goldhost1 ~]$ ls -lrt /tmp/pdbnewclone.xml --rw-r--r-- 1 oracle asmadmin 9920 Jun 27 06:26 /tmp/pdbnewclone.xml diff --git a/docs/multitenant/provisioning/unplug_pdb.md b/docs/multitenant/provisioning/unplug_pdb.md deleted file mode 100644 index fb98fc8b..00000000 --- a/docs/multitenant/provisioning/unplug_pdb.md +++ /dev/null @@ -1,39 +0,0 @@ -# Unplug a PDB using Oracle DB Operator On-Prem Controller in a target CDB - -In this use case, a PDB is unplugged using Oracle DB Operator On-Prem controller. - -To unplug a PDB CRD Resource, a sample .yaml file is available here: [config/samples/onpremdb/pdb_unplug.yaml](../../../config/samples/onpremdb/pdb_unplug.yaml) - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -This example uses `unplug_pdb.yaml` to unplug a PDB from a target CDB using Oracle DB Operator On-Prem Controller with: - -- Pluggable Database CRD Resource Name as `pdb1` -- Pluggable Database (PDB) Name as `pdbnew` -- Target CDB CRD Resource Name as `cdb-dev` -- CDB Name as `goldcdb` -- Action to be taken on the PDB as `Unplug` -- XML metadata filename as `/tmp/pdbnewclone.xml` - -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -**NOTE:** Before performing the unplug operation on the PDB CRD Resource, you will first need to perform the Modify Operation on that PDB CRD resource to Close the the PDB. After that you will be able to perform the Unplug operation. Please refer to the use case to modify the PDB state to Close. - -Use the file: [unplug_pdb.yaml](./unplug_pdb.yaml) for this use case as below: - -1. Deploy the .yaml file: -```sh -% kubectl apply -f unplug_pdb.yaml -``` - -2. Monitor the Oracle DB Operator Pod for the progress of the PDB Unplug operation: - -NOTE: Check the DB Operator Pod name in your environment. - -```sh -% kubectl logs -f pod/oracle-database-operator-controller-manager-76cb674c5c-f9wsd -n oracle-database-operator-system -``` - -## Sample Output - -[Here](./unplug_pdb.log) is the sample output for a PDB created using Oracle DB Operator On-Prem Controller using file [unplug_pdb.yaml](./unplug_pdb.yaml) diff --git a/docs/multitenant/provisioning/unplug_pdb.yaml b/docs/multitenant/provisioning/unplug_pdb.yaml deleted file mode 100644 index c9915b28..00000000 --- a/docs/multitenant/provisioning/unplug_pdb.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "goldcdb" - pdbName: "pdbnew" - xmlFileName: "/tmp/pdbnewclone.xml" - action: "Unplug" diff --git a/docs/multitenant/provisioning/validation_error.md b/docs/multitenant/provisioning/validation_error.md deleted file mode 100644 index ec527cdb..00000000 --- a/docs/multitenant/provisioning/validation_error.md +++ /dev/null @@ -1,73 +0,0 @@ -#Validation and Errors - -## Kubernetes Events -You can check Kubernetes events for any errors or status updates as shown below: -```sh -$ kubectl get events -A -NAMESPACE LAST SEEN TYPE REASON OBJECT MESSAGE -oracle-database-operator-system 58m Warning Failed pod/cdb-dev-ords-qiigr Error: secret "cdb1-secret" not found -oracle-database-operator-system 56m Normal DeletedORDSPod cdb/cdb-dev Deleted ORDS Pod(s) for cdb-dev -oracle-database-operator-system 56m Normal DeletedORDSService cdb/cdb-dev Deleted ORDS Service for cdb-dev -... -oracle-database-operator-system 26m Warning OraError pdb/pdb1 ORA-65016: FILE_NAME_CONVERT must be specified... -oracle-database-operator-system 24m Warning OraError pdb/pdb2 ORA-65011: Pluggable database DEMOTEST does not exist. -... -oracle-database-operator-system 20m Normal Created pdb/pdb1 PDB 'demotest' created successfully -... -oracle-database-operator-system 17m Warning OraError pdb/pdb3 ORA-65012: Pluggable database DEMOTEST already exists... -``` - -In case of successfull operation, you can see messages like below: - -```sh -% kubectl get events -A -NAMESPACE LAST SEEN TYPE REASON OBJECT MESSAGE -kube-system 33s Warning BackOff pod/kube-apiserver Back-off restarting failed container -oracle-database-operator-system 59m Normal CreatedORDSService cdb/cdb-dev Created ORDS Service for cdb-dev -oracle-database-operator-system 51m Normal Created pdb/pdb1-clone PDB 'pdbnewclone' cloned successfully -oracle-database-operator-system 49m Normal Modified pdb/pdb1-clone PDB 'pdbnewclone' modified successfully -oracle-database-operator-system 47m Normal Deleted pdb/pdb1-clone PDB 'pdbnewclone' dropped successfully -oracle-database-operator-system 53m Normal Created pdb/pdb1 PDB 'pdbnew' created successfully -oracle-database-operator-system 44m Normal Modified pdb/pdb1 PDB 'pdbnew' modified successfully -oracle-database-operator-system 42m Normal Unplugged pdb/pdb1 PDB 'pdbnew' unplugged successfully -oracle-database-operator-system 39m Normal Created pdb/pdb1 PDB 'pdbnew' plugged successfully -``` - -## CDB Validation and Errors - -Validation is done at the time of CDB resource creation as shown below: -```sh -$ kubectl apply -f cdb1.yaml -The PDB "cdb-dev" is invalid: -* spec.dbServer: Required value: Please specify Database Server Name or IP Address -* spec.dbPort: Required value: Please specify DB Server Port -* spec.ordsImage: Required value: Please specify name of ORDS Image to be used -``` - -Apart from events, listing of CDBs will also show the possible reasons why a particular CDB CR could not be created as shown below: -```sh - $ kubectl get cdbs -A - - NAMESPACE NAME CDB NAME DB SERVER DB PORT SCAN NAME STATUS MESSAGE - oracle-database-operator-system cdb-dev devdb 172.17.0.4 1521 devdb Failed Secret not found:cdb1-secret -``` - -## PDB Validation and Errors - -Validation is done at the time of PDB resource creation as shown below: -```sh -$ kubectl apply -f pdb1.yaml -The PDB "pdb1" is invalid: -* spec.cdbResName: Required value: Please specify the name of the CDB Kubernetes resource to use for PDB operations -* spec.pdbName: Required value: Please specify name of the PDB to be created -* spec.adminPwd: Required value: Please specify PDB System Administrator Password -* spec.fileNameConversions: Required value: Please specify a value for fileNameConversions. Values can be a filename convert pattern or NONE -``` - -Similarly, for PDBs, listing of PDBs will also show the possible reasons why a particular PDB CR could not be created as shown below: -```sh -$ kubectl get pdbs -A -NAMESPACE NAME CONNECT STRING CDB NAME PDB NAME PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 democdb demotest1 Failed Secret not found:pdb12-secret -oracle-database-operator-system pdb2 democdb demotest2 Failed ORA-65016: FILE_NAME_CONVERT must be specified... -``` diff --git a/docs/multitenant/usecase01/ca.crt b/docs/multitenant/usecase01/ca.crt new file mode 100644 index 00000000..cc9aa8bb --- /dev/null +++ b/docs/multitenant/usecase01/ca.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEJTCCAw2gAwIBAgIUNXPtpnNEFBCMcnxRP5kJsBDpafcwDQYJKoZIhvcNAQEL +BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH +DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k +ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE +AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx +NTMyMzVaMIGhMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG +A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j +ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxHDAa +BgNVBAMME2xvY2FsaG9zdCAgUm9vdCBDQSAwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCmnGVApwUBF1kpqcyr2nYeED0VKvefpoHLtxHSP+vP0lWhW7NU +NJlb1YuUagjJ4/rpGRQmPxcVU51n3aAW3a5qHazIpNxNa3fvgB1rMOPFxGmdel2d +8lIt+u19q19DknX/GNgH9Mog8RcyZyPeA7d2icT8TBo74ognr+8p68O3CjBHQ8EM +SnRQR7/bh1c10Uia317ilKvs+I7oErTq5JFLeIuPDdAJ6UncaeblTf1XJ/1FrpHG +fSS7xmR8x0/MblBQlku4eImYmN35g+eRgf8bLDDwC+GPzDnAqqMLjx6h2N+btDxr +tnn05qyqmN9G08uUlP4d4BXi9ISb/toYypklAgMBAAGjUzBRMB0GA1UdDgQWBBS+ +a4X2XTmdPivdQtqDWNpfOtHypDAfBgNVHSMEGDAWgBS+a4X2XTmdPivdQtqDWNpf +OtHypDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZIrGBNdSw +pe+1agefHfaR8hjZQiXBxdwHM1gR2LWOaFzMS8Q/eRETHTO6+VwQ0/FNaXbAqgqk +G317gZMXS5ZmXuOi28fTpAQtuzokkEKpoK0puTnbXOKGA2QSbBlpSFPqb3aJXvVt +afXFQb5P/0mhr4kuVt7Ech82WM/o5ryFgObygDayDmLatTp+VaRmBZPksnSMhslq +3zPyS7bx2YhbPTLkDxq8Mfr/Msxme8LvSXUpFf4PpQ5zwp1RE32gekct6eRQLmqU +5LXY2aPtqpMF0fBpcwPWbqA9gOYCRKcvXXIr+u1x8hf6Er6grZegHkM9TQ8s0hJd +sxi5tK0lPMHJ +-----END CERTIFICATE----- diff --git a/docs/multitenant/usecase01/ca.key b/docs/multitenant/usecase01/ca.key new file mode 100644 index 00000000..1a0ef89d --- /dev/null +++ b/docs/multitenant/usecase01/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAppxlQKcFARdZKanMq9p2HhA9FSr3n6aBy7cR0j/rz9JVoVuz +VDSZW9WLlGoIyeP66RkUJj8XFVOdZ92gFt2uah2syKTcTWt374AdazDjxcRpnXpd +nfJSLfrtfatfQ5J1/xjYB/TKIPEXMmcj3gO3donE/EwaO+KIJ6/vKevDtwowR0PB +DEp0UEe/24dXNdFImt9e4pSr7PiO6BK06uSRS3iLjw3QCelJ3Gnm5U39Vyf9Ra6R +xn0ku8ZkfMdPzG5QUJZLuHiJmJjd+YPnkYH/Gyww8Avhj8w5wKqjC48eodjfm7Q8 +a7Z59OasqpjfRtPLlJT+HeAV4vSEm/7aGMqZJQIDAQABAoIBAGXRGYdjCgnarOBr +Jeq3vIsuvUVcVqs35AYMQFXOPltoXHAZTAPfiQC4BW6TRf+q1MDyVH/y+jZMPNsm +cxjGLDopHFgZd4/QZyDzmAbTf75yA2D7UI6fcV0sBUpRGgx/SqC0HADwtT1gWB6z +LRYWC13jX4AXOcjy7OXj/DIQJDCMivedt3dv0rDWJUcBCnVot5tr6zjycefxGKa8 +mG9LZQb3x71FxwpFUau3WLDSwOjtXCeMytaGXnGmIiofJmXnFi0KA4ApzKL7QV6I +cCBS1WBLLXeVM9vOfrtzKVLWGe0qADyLm35p5Fnl3j+vimkk8h/2DEvCZ75c987m +O3PEgdkCgYEA0Scg+KINTA78sdZL5v2+8fT4b+EfoCgUqfr10ReUPKrz3HfrVHcj +7Vf00RT52TkfmkL3mIdLyBUzQ9vzPgweo1o4yKCKNCpR9G3ydNW+KI5jSYnq2efz +Gpe3wTt+8YoyCgm9eUxNWjfO9fipS91sSotY0PovkBohj9aezfcWp1sCgYEAy+3n +MIvW/9PoYxCvQ9fDGLvx3B4/uy0ZYPh7j5edDuaRzwFd2YXUysXhJVuqTp0KT2tv +dRPFRE9Oq5N8e5ITIUiKLQ5PIRNBZm8CiAof+XS1fIuU+MTDaTfXwyGQo0xSg8MB +ITnJulmUlkcTWEtGyBi9sIjor5ve8kqvyrdAKX8CgYA9ZUUSd0978jJPad6iEf6J +PCXpgaYs91cJhre+BzPmkzA+mZ0lEEwlkdo1vfiRwWj7eYkA50Zhl4eS9e/zWM9t +mEBu9GFdasbf/55amZvWf+W5YpjkGmiMd9jjCjn7YVvLAozyHGngf91q6vGXaYou +X7VUsvxfSqxrcs7vGwc1XQKBgB0qaD80MMqj5v+MGlTsndWCw8OEe/7sI04QG7Pc +rjS8Wyws+NwsXNOnW1z5cDEQGrJjHiyzaCot4YV+cXZG3P+MnV52RnDnjRn2VHla +YVpPC8nFOMgfdAcvWmdo/IOuXbrEf/vdhPFm8G5Ruf2NvpDNoQuHeSfsdgVXEy89 +6CpHAoGBAMZInYD0XjcnZNqiQnQdcIJN3CqDIU76Z45OOpcUrYrvTos2xhGLrRI5 +qrk5Od/sovJfse+oUIIbgsABieqtyfxM03iu8fvbahIY6Un1iw2KN9t+mcPrSZJK +jTXKf7XxZ1+yN9kvohdLc65ySyXFSm++glDq8WGrmnOtLUlr0oMm +-----END RSA PRIVATE KEY----- diff --git a/docs/multitenant/usecase01/ca.srl b/docs/multitenant/usecase01/ca.srl new file mode 100644 index 00000000..7c9868bb --- /dev/null +++ b/docs/multitenant/usecase01/ca.srl @@ -0,0 +1 @@ +77D97AB4C4B6D5A9377B84B455D3E16348C6DE04 diff --git a/docs/multitenant/usecase01/extfile.txt b/docs/multitenant/usecase01/extfile.txt new file mode 100644 index 00000000..c51d22a3 --- /dev/null +++ b/docs/multitenant/usecase01/extfile.txt @@ -0,0 +1 @@ +subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com diff --git a/docs/multitenant/usecase01/makefile b/docs/multitenant/usecase01/makefile index 90020f04..202b312f 100644 --- a/docs/multitenant/usecase01/makefile +++ b/docs/multitenant/usecase01/makefile @@ -109,6 +109,8 @@ # | Before testing step13 delete the crd: | # | kubectl delete pdb pdb1 -n oracle-database-operator-system | # +---------------------------------------------------------------------------+ +# |step14 | delete pdb | +# +-----------------------------+---------------------------------------------+ # | DIAGNOSTIC TARGETS | # +-----------------------------+---------------------------------------------+ # | dump | Dump pods info into a file | @@ -170,6 +172,7 @@ step10: pdb step11: close step12: open step13: map +step14: delete checkstep9: checkcdb @@ -206,9 +209,9 @@ dboperator: tlscert: @echo "CREATING TLS CERTIFICATES" $(OPENSSL) genrsa -out ca.key 2048 - $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER) /CN=$(LOCALHOST) Root CA " -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER) /CN=$(LOCALHOST)" -out server.csr - $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER),DNS:www.example.com" > extfile.txt + $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE) /CN=$(LOCALHOST)" -out server.csr + $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE),DNS:www.example.com" > extfile.txt $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) tlssecret: @@ -244,6 +247,9 @@ map: checkpdb: $(KUBECTL) get pdbs -n $(NAMESPACE) +delete: + $(KUBECTL) apply -f pdb_delete.yaml + dump: @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) @$(eval DIAGFILE := ./opdmp.$(TMPSP)) diff --git a/docs/multitenant/usecase01/pdb_delete.yaml b/docs/multitenant/usecase01/pdb_delete.yaml new file mode 100644 index 00000000..c22b546a --- /dev/null +++ b/docs/multitenant/usecase01/pdb_delete.yaml @@ -0,0 +1,34 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + diff --git a/docs/multitenant/usecase01/server.csr b/docs/multitenant/usecase01/server.csr new file mode 100644 index 00000000..e308d301 --- /dev/null +++ b/docs/multitenant/usecase01/server.csr @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIC3TCCAcUCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh +MRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNV +BAMMLWNkYi1kZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVt +IDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAm9nlNSQNsPTVqH57MkWKZEyaVtzVKQ8Z3oDK6hWXfB24p0jVj6sTOJkf +NVAxnqmU8DpW3odpbU6qWe/n+B5vJpqdXUGdsq9NKyus2fGb/xf1UnskpA2FUuWZ +o3upyCFxDAOvE4eZUzlxIn+54XXaNAdQiU9E8VXPr5YxrvZ15T/xCXLtJPs/RCOF +cJ8+gvZGcjMbdP16auJDVWZzBaur3eKbiHN7LXNCCRzGO++dv0kGY8vH7MyFfgp3 +qYBiSHS3WDiFUJjYIvfa8lLfP1hnlCyHn8TnU9gjGjmd1YcccSKqWIAT24wPUKVU +Lme4n91jxDPp7g8nRtDw0Smj9gYCtQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEB +AGOG/9IJJRvT2JLcuzE5Arai1XHc6Jh65iuDRqXQav47Bz38FFF2gZNO69gzDmhq +6k7tie+5bPcAHuuJZ0dAa71a9SLjKl+XNkkI0vS6te6OK3DCVUoMqNCk5VdwrJw0 +RORbKUwgLEG6mu80Gc/6wCdeR/36hoYTMeNPjm6M9e+X5ppsXqxCNsgDxasJFT82 +FejuJE2sZ6RCradlDToUHNS1dMLoW0WAIISqOmrDvEI6snm9ZZr3Sxo1auEtpI6v +NllBM4AgEghy/2mAtke+By4WHCfXBpxEGv9S7ATqJHYrR5Qa3nwx0eojWW1vmn0/ +aEzslX1tAH6oz2jA6QZ0sNo= +-----END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/usecase01/tls.crt b/docs/multitenant/usecase01/tls.crt new file mode 100644 index 00000000..6bf8aef4 --- /dev/null +++ b/docs/multitenant/usecase01/tls.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEFDCCAvygAwIBAgIUd9l6tMS21ak3e4S0VdPhY0jG3gQwDQYJKoZIhvcNAQEL +BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH +DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k +ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE +AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx +NTMyMzVaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG +A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j +ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxEjAQ +BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AJvZ5TUkDbD01ah+ezJFimRMmlbc1SkPGd6AyuoVl3wduKdI1Y+rEziZHzVQMZ6p +lPA6Vt6HaW1Oqlnv5/gebyaanV1BnbKvTSsrrNnxm/8X9VJ7JKQNhVLlmaN7qcgh +cQwDrxOHmVM5cSJ/ueF12jQHUIlPRPFVz6+WMa72deU/8Qly7ST7P0QjhXCfPoL2 +RnIzG3T9emriQ1VmcwWrq93im4hzey1zQgkcxjvvnb9JBmPLx+zMhX4Kd6mAYkh0 +t1g4hVCY2CL32vJS3z9YZ5Qsh5/E51PYIxo5ndWHHHEiqliAE9uMD1ClVC5nuJ/d +Y8Qz6e4PJ0bQ8NEpo/YGArUCAwEAAaNMMEowSAYDVR0RBEEwP4IsY2RiLWRldi1v +cmRzLm9yYWNsZS1kYXRhYmFzZS1vcGVyYXRvci1zeXN0ZW2CD3d3dy5leGFtcGxl +LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAh7Lsu2ITS6Bc2q/Ef4No5Us0Vo9BWKoL +AlrfQPjsv1erMGsyEEyZ0Cg8l3QrXlscQ1ESvx0BnRGjoqZGE4+PoVZTEYSkokXP +aAr69epPzXQRyyAGCg5GeL6IFAj1AzqJGNnKOrPaLpcTri4MboiWmW+MHmgLdyPK +iwl8bNa8841nK/L/m6QET15BI+MIAvn7pgcpztum5jmkB+eceXzXnKUGg77TaFiX +bXqVBR4EvexC4DgUfQJI4zJLFdcH/GHxCpaaXNjbXeVz1ZK/qo2TCrXp2UXVrznU +9VTUuCaQA2VYZCitvAbupt+1OvMFYhWiIAroJSmzrvH4oK+IXgY6GA== +-----END CERTIFICATE----- diff --git a/docs/multitenant/usecase01/tls.key b/docs/multitenant/usecase01/tls.key new file mode 100644 index 00000000..666c5639 --- /dev/null +++ b/docs/multitenant/usecase01/tls.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb2eU1JA2w9NWo +fnsyRYpkTJpW3NUpDxnegMrqFZd8HbinSNWPqxM4mR81UDGeqZTwOlbeh2ltTqpZ +7+f4Hm8mmp1dQZ2yr00rK6zZ8Zv/F/VSeySkDYVS5Zmje6nIIXEMA68Th5lTOXEi +f7nhddo0B1CJT0TxVc+vljGu9nXlP/EJcu0k+z9EI4Vwnz6C9kZyMxt0/Xpq4kNV +ZnMFq6vd4puIc3stc0IJHMY7752/SQZjy8fszIV+CnepgGJIdLdYOIVQmNgi99ry +Ut8/WGeULIefxOdT2CMaOZ3VhxxxIqpYgBPbjA9QpVQuZ7if3WPEM+nuDydG0PDR +KaP2BgK1AgMBAAECggEAKUwl1l0FW7yk2Q8a6glPUKCTzSybN1QPEMyj+D9ccsEV +aw57uKQmZbr9cA0d+OMK2lU7K6BKKXLM5SQTHcZCwcH6rPl0JiMZmbTrCp1hLslU +clS7MtV6XKsGeTGNncBuyjY3sD8gO9NezTt3L+0gsuS1TI06wZBxhh+QbsJUHzjW +bC3mNjD4SqXree4Snp05nlFaT2s2isIjj25mKDwBu8IX0BN2VjsaSiQcjb8Dmzmu +42Xh7bcWBebns8Ehuq9TIl6ZjQht+pmVOMlB862baVpW/9CxkknzM+UQhIkXTSJk +Jt/mGeO89V4/Zh2N4ixIOE1hw87EvRFBoYh2VF58QQKBgQDMujXYblh+eEdsB1LG +kY0LerFHuQgdzifYmjPl0jtBsWDmh5i6q9PRUs2JZ/Fsq4QMQ8SLinGzaIBq5FKr +CL067X5blrFA9H0D6exJI3iHBTQpeMFwtqvu3j+zpCmgzonaUDQrczUpc0hxU7YI +/jhDe9LSWknPrzzMoWWKuy0sTQKBgQDC4g8F2krqm9Q5ug8bRKTAvMrY0skFIwrP +5LXBq9C8YCnLnT4S4tYQfbnWaBeG7YpkkmkZe30c9MUjsr1OHZbo+jlxHBU+oRYZ +e1j0UorVGt7FfNe/zjW0fLd72CBO741EDvV6pVeItkAwH6P5/cbRu085dwvyFbxv +JmOaYddECQKBgQCuid6YG1NE10SE3CV89uAZtktny18ZEgY0ixrNx5MPaaskPtw9 +4Xofjol+qOhR7lQQpMHu+WQAQYqiFvBHspapo4pDiVCrAQWIDamNnTkHW69h3/qD +HqmsZzxF6iI3X351akVf+cOMCCXtwCGEvz+2gN12ytT8w/iAuOS6BuP3TQKBgBlf +v57+diSn13EQtajSPjVOH4ctorjFgEHjQHsP+OSeDLMTLSLeYArTo9+zu+R4hz1j +BsYnmvmrMQPd4OIL3jtFYTdF9coqxSraMZHWMXdfwUOrZpf1rG5skqNQV5yPejAz +Vmj6oDQPrrnVVM9W6I0kO0N7KZYCmH9MW0mdlZ6pAoGAB60f2sk35VUBpvh7qzTY +70WDbNnCCU3I3KZ7LCUwUPWzGLQwMXRlAb5ZMheT/SGPChX4QXCNUCjXkR3Am3NO +yURHqZIRy0bwZRVjYnlCtc9YQ8pB0isZ1z2a9FXRD75o2WboFZ+VsG0FU81IE2ZO +gW802gT76NRnz851B7/nFNs= +-----END PRIVATE KEY----- diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 5d3b43d3..c41093c6 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -4164,7 +4164,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator + image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/operatormntnns:latest imagePullPolicy: Always name: manager ports: From 183a32199c8a400f865523bfd6f16ebbb73b7d21 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Mon, 19 Aug 2024 13:32:26 +0000 Subject: [PATCH 113/414] assertive approach on pdb deletion --- .../v1alpha1/adbfamily_common_utils.go | 6 +- .../autonomousdatabasebackup_types.go | 12 +- .../v1alpha1/dataguardbroker_types.go | 16 +- apis/database/v1alpha1/groupversion_info.go | 4 +- apis/database/v1alpha1/pdb_types.go | 4 + apis/database/v1alpha1/webhook_suite_test.go | 8 +- commons/dbcssystem/dbcs_reconciler.go | 10 +- commons/oci/wallet.go | 2 +- .../bases/database.oracle.com_DbcsSystem.yaml | 11 - ...acle.com_autonomouscontainerdatabases.yaml | 20 - ....oracle.com_autonomousdatabasebackups.yaml | 20 - ...oracle.com_autonomousdatabaserestores.yaml | 25 - ...tabase.oracle.com_autonomousdatabases.yaml | 64 -- .../crd/bases/database.oracle.com_cdbs.yaml | 40 - .../database.oracle.com_dataguardbrokers.yaml | 10 - ...ase.oracle.com_oraclerestdataservices.yaml | 28 - .../crd/bases/database.oracle.com_pdbs.yaml | 79 +- ...database.oracle.com_shardingdatabases.yaml | 127 ---- ...se.oracle.com_singleinstancedatabases.yaml | 57 -- ...vability.oracle.com_databaseobservers.yaml | 55 -- config/database.oracle.com_DbcsSystem.yaml | 240 ++++++ ...acle.com_autonomouscontainerdatabases.yaml | 117 +++ ....oracle.com_autonomousdatabasebackups.yaml | 138 ++++ ...oracle.com_autonomousdatabaserestores.yaml | 138 ++++ ...tabase.oracle.com_autonomousdatabases.yaml | 324 +++++++++ config/database.oracle.com_cdbs.yaml | 270 +++++++ .../database.oracle.com_dataguardbrokers.yaml | 134 ++++ ...ase.oracle.com_oraclerestdataservices.yaml | 224 ++++++ config/database.oracle.com_pdbs.yaml | 383 ++++++++++ ...database.oracle.com_shardingdatabases.yaml | 688 ++++++++++++++++++ ...se.oracle.com_singleinstancedatabases.yaml | 421 +++++++++++ ...vability.oracle.com_databaseobservers.yaml | 227 ++++++ controllers/database/pdb_controller.go | 126 ++-- docs/multitenant/usecase01/pdb_create.yaml | 1 + oracle-database-operator.yaml | 239 +----- 35 files changed, 3422 insertions(+), 846 deletions(-) create mode 100644 config/database.oracle.com_DbcsSystem.yaml create mode 100644 config/database.oracle.com_autonomouscontainerdatabases.yaml create mode 100644 config/database.oracle.com_autonomousdatabasebackups.yaml create mode 100644 config/database.oracle.com_autonomousdatabaserestores.yaml create mode 100644 config/database.oracle.com_autonomousdatabases.yaml create mode 100644 config/database.oracle.com_cdbs.yaml create mode 100644 config/database.oracle.com_dataguardbrokers.yaml create mode 100644 config/database.oracle.com_oraclerestdataservices.yaml create mode 100644 config/database.oracle.com_pdbs.yaml create mode 100644 config/database.oracle.com_shardingdatabases.yaml create mode 100644 config/database.oracle.com_singleinstancedatabases.yaml create mode 100644 config/observability.oracle.com_databaseobservers.yaml diff --git a/apis/database/v1alpha1/adbfamily_common_utils.go b/apis/database/v1alpha1/adbfamily_common_utils.go index e6691a12..d4d3ae9f 100644 --- a/apis/database/v1alpha1/adbfamily_common_utils.go +++ b/apis/database/v1alpha1/adbfamily_common_utils.go @@ -173,9 +173,9 @@ func traverse(lastSpec interface{}, curSpec interface{}) bool { return changed } -// 1. If the current field is with a zero value, then the field is unchanged. -// 2. If the current field is NOT with a zero value, then we want to comapre it with the last field. -// In this case if the last field is with a zero value, then the field is changed +// 1. If the current field is with a zero value, then the field is unchanged. +// 2. If the current field is NOT with a zero value, then we want to comapre it with the last field. +// In this case if the last field is with a zero value, then the field is changed func hasChanged(lastField reflect.Value, curField reflect.Value) bool { zero := reflect.Zero(lastField.Type()).Interface() lastFieldIsZero := reflect.DeepEqual(lastField.Interface(), zero) diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_types.go b/apis/database/v1alpha1/autonomousdatabasebackup_types.go index 876cb811..95c77560 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_types.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_types.go @@ -52,12 +52,12 @@ import ( type AutonomousDatabaseBackupSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Target TargetSpec `json:"target,omitempty"` - DisplayName *string `json:"displayName,omitempty"` - AutonomousDatabaseBackupOCID *string `json:"autonomousDatabaseBackupOCID,omitempty"` - IsLongTermBackup *bool `json:"isLongTermBackup,omitempty"` - RetentionPeriodInDays *int `json:"retentionPeriodInDays,omitempty"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + Target TargetSpec `json:"target,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + AutonomousDatabaseBackupOCID *string `json:"autonomousDatabaseBackupOCID,omitempty"` + IsLongTermBackup *bool `json:"isLongTermBackup,omitempty"` + RetentionPeriodInDays *int `json:"retentionPeriodInDays,omitempty"` + OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` } // AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup diff --git a/apis/database/v1alpha1/dataguardbroker_types.go b/apis/database/v1alpha1/dataguardbroker_types.go index 138e2bdb..37d71b92 100644 --- a/apis/database/v1alpha1/dataguardbroker_types.go +++ b/apis/database/v1alpha1/dataguardbroker_types.go @@ -50,15 +50,15 @@ type DataguardBrokerSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - PrimaryDatabaseRef string `json:"primaryDatabaseRef"` - StandbyDatabaseRefs []string `json:"standbyDatabaseRefs"` - SetAsPrimaryDatabase string `json:"setAsPrimaryDatabase,omitempty"` - LoadBalancer bool `json:"loadBalancer,omitempty"` - ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` + PrimaryDatabaseRef string `json:"primaryDatabaseRef"` + StandbyDatabaseRefs []string `json:"standbyDatabaseRefs"` + SetAsPrimaryDatabase string `json:"setAsPrimaryDatabase,omitempty"` + LoadBalancer bool `json:"loadBalancer,omitempty"` + ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` // +kubebuilder:validation:Enum=MaxPerformance;MaxAvailability - ProtectionMode string `json:"protectionMode"` - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - FastStartFailOver DataguardBrokerFastStartFailOver `json:"fastStartFailOver,omitempty"` + ProtectionMode string `json:"protectionMode"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + FastStartFailOver DataguardBrokerFastStartFailOver `json:"fastStartFailOver,omitempty"` } type DataguardBrokerFastStartFailOver struct { diff --git a/apis/database/v1alpha1/groupversion_info.go b/apis/database/v1alpha1/groupversion_info.go index d72ee908..3c4b1804 100644 --- a/apis/database/v1alpha1/groupversion_info.go +++ b/apis/database/v1alpha1/groupversion_info.go @@ -37,8 +37,8 @@ */ // Package v1alpha1 contains API Schema definitions for the database v1alpha1 API group -//+kubebuilder:object:generate=true -//+groupName=database.oracle.com +// +kubebuilder:object:generate=true +// +groupName=database.oracle.com package v1alpha1 import ( diff --git a/apis/database/v1alpha1/pdb_types.go b/apis/database/v1alpha1/pdb_types.go index 1e4da0a1..8de9db52 100644 --- a/apis/database/v1alpha1/pdb_types.go +++ b/apis/database/v1alpha1/pdb_types.go @@ -114,6 +114,10 @@ type PDBSpec struct { // The target state of the PDB // +kubebuilder:validation:Enum=OPEN;CLOSE PDBState string `json:"pdbState,omitempty"` + // turn on the assertive approach to delete pdb resource + // kubectl delete pdb ..... automatically triggers the pluggable database + // deletion + AssertivePdbDeletion bool `json:"assertivePdbDeletion,omitempty"` } // PDBAdminName defines the secret containing Sys Admin User mapped to key 'adminName' for PDB diff --git a/apis/database/v1alpha1/webhook_suite_test.go b/apis/database/v1alpha1/webhook_suite_test.go index 3f740a41..e28925e6 100644 --- a/apis/database/v1alpha1/webhook_suite_test.go +++ b/apis/database/v1alpha1/webhook_suite_test.go @@ -61,8 +61,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/webhook" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" ) // To avoid dot import @@ -137,14 +137,14 @@ var _ = BeforeSuite(func() { // start webhook server using Manager webhookInstallOptions := &testEnv.WebhookInstallOptions mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme, + Scheme: scheme, WebhookServer: webhook.NewServer(webhook.Options{ Port: webhookInstallOptions.LocalServingPort, Host: webhookInstallOptions.LocalServingHost, CertDir: webhookInstallOptions.LocalServingCertDir, }), - LeaderElection: false, - Metrics: metricsserver.Options{ + LeaderElection: false, + Metrics: metricsserver.Options{ BindAddress: "0", }, }) diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 70e50294..60905c76 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -94,7 +94,7 @@ func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient d dbcsDetails.CompartmentId = common.String(dbcs.Spec.DbSystem.CompartmentId) dbcsDetails.SubnetId = common.String(dbcs.Spec.DbSystem.SubnetId) dbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) - dbcsDetails.Domain = common.String(dbcs.Spec.DbSystem.Domain) + dbcsDetails.Domain = common.String(dbcs.Spec.DbSystem.Domain) if dbcs.Spec.DbSystem.DisplayName != "" { dbcsDetails.DisplayName = common.String(dbcs.Spec.DbSystem.DisplayName) } @@ -536,10 +536,10 @@ func GetResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id s func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { - if dbcs.Spec.Id == nil { - dbcs.Status.State = "FAILED" - return nil - } + if dbcs.Spec.Id == nil { + dbcs.Status.State = "FAILED" + return nil + } dbcsId := *dbcs.Spec.Id diff --git a/commons/oci/wallet.go b/commons/oci/wallet.go index a5f9235e..076460b1 100644 --- a/commons/oci/wallet.go +++ b/commons/oci/wallet.go @@ -107,4 +107,4 @@ func WalletExpiringDate(files map[string][]byte) string { line := data[strings.Index(data, "this wallet will expire on"):strings.Index(data, ".\nIn order to avoid")] return strings.TrimSpace(strings.TrimPrefix(line, "this wallet will expire on")) -} \ No newline at end of file +} diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml index e933d5a4..970954a1 100644 --- a/config/crd/bases/database.oracle.com_DbcsSystem.yaml +++ b/config/crd/bases/database.oracle.com_DbcsSystem.yaml @@ -19,22 +19,14 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: DbcsSystem is the Schema for the dbcssystems API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DbcsSystemSpec defines the desired state of DbcsSystem properties: dbSystem: properties: @@ -51,7 +43,6 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup COnfig Network Struct properties: autoBackupEnabled: type: boolean @@ -139,7 +130,6 @@ spec: - ociConfigMap type: object status: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: availabilityDomain: type: string @@ -153,7 +143,6 @@ spec: type: string dbInfo: items: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: dbHomeId: type: string diff --git a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml index bac3a28c..875eeaef 100644 --- a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml @@ -32,24 +32,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousContainerDatabaseSpec defines the desired state - of AutonomousContainerDatabase properties: action: enum: @@ -58,8 +48,6 @@ spec: - TERMINATE type: string autonomousContainerDatabaseOCID: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' type: string autonomousExadataVMClusterOCID: type: string @@ -75,7 +63,6 @@ spec: default: false type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -83,21 +70,14 @@ spec: type: string type: object patchModel: - description: 'AutonomousContainerDatabasePatchModelEnum Enum with - underlying type: string' enum: - RELEASE_UPDATES - RELEASE_UPDATE_REVISIONS type: string type: object status: - description: AutonomousContainerDatabaseStatus defines the observed state - of AutonomousContainerDatabase properties: lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string timeCreated: type: string diff --git a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml index a5c37507..71fdbfa4 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml @@ -38,24 +38,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseBackupSpec defines the desired state of - AutonomousDatabaseBackup properties: autonomousDatabaseBackupOCID: type: string @@ -64,7 +54,6 @@ spec: isLongTermBackup: type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -74,11 +63,8 @@ spec: retentionPeriodInDays: type: integer target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -91,8 +77,6 @@ spec: type: object type: object status: - description: AutonomousDatabaseBackupStatus defines the observed state - of AutonomousDatabaseBackup properties: autonomousDatabaseOCID: type: string @@ -105,16 +89,12 @@ spec: isAutomatic: type: boolean lifecycleState: - description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with - underlying type: string' type: string timeEnded: type: string timeStarted: type: string type: - description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying - type: string' type: string required: - autonomousDatabaseOCID diff --git a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml index 5e9f2c73..f6173b5b 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml @@ -32,27 +32,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseRestoreSpec defines the desired state of - AutonomousDatabaseRestore properties: ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -62,9 +51,6 @@ spec: source: properties: k8sADBBackup: - description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO - OWN! NOTE: json tags are required. Any new fields you add must - have json tags for the fields to be serialized.' properties: name: type: string @@ -72,17 +58,12 @@ spec: pointInTime: properties: timestamp: - description: 'The timestamp must follow this format: YYYY-MM-DD - HH:MM:SS GMT' type: string type: object type: object target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -98,18 +79,12 @@ spec: - target type: object status: - description: AutonomousDatabaseRestoreStatus defines the observed state - of AutonomousDatabaseRestore properties: dbName: type: string displayName: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string status: - description: 'WorkRequestStatusEnum Enum with underlying type: string' type: string timeAccepted: type: string diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index f77407f3..d494b5a6 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -47,33 +47,20 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabase is the Schema for the autonomousdatabases - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase - Important: Run "make" to regenerate code after modifying this file' properties: details: - description: AutonomousDatabaseDetails defines the detail information - of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase properties: adminPassword: properties: k8sSecret: - description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -85,12 +72,8 @@ spec: type: object type: object autonomousContainerDatabase: - description: ACDSpec defines the spec of the target for backup/restore - runs. The name could be the name of an AutonomousDatabase or - an AutonomousDatabaseBackup properties: k8sACD: - description: "*********************** *\tACD specs ***********************" properties: name: type: string @@ -114,8 +97,6 @@ spec: dbVersion: type: string dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying - type: string' enum: - OLTP - DW @@ -133,15 +114,11 @@ spec: isDedicated: type: boolean licenseModel: - description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying - type: string' enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying - type: string' type: string networkAccess: properties: @@ -179,8 +156,6 @@ spec: password: properties: k8sSecret: - description: "*********************** *\tSecret specs - ***********************" properties: name: type: string @@ -197,7 +172,6 @@ spec: default: false type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -208,7 +182,6 @@ spec: - details type: object status: - description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase properties: allConnectionStrings: items: @@ -230,63 +203,29 @@ spec: type: array conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -302,9 +241,6 @@ spec: - type x-kubernetes-list-type: map lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string timeCreated: type: string diff --git a/config/crd/bases/database.oracle.com_cdbs.yaml b/config/crd/bases/database.oracle.com_cdbs.yaml index 6b1c350c..3c564c85 100644 --- a/config/crd/bases/database.oracle.com_cdbs.yaml +++ b/config/crd/bases/database.oracle.com_cdbs.yaml @@ -48,28 +48,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: CDB is the Schema for the cdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: CDBSpec defines the desired state of CDB properties: cdbAdminPwd: - description: Password for the CDB Administrator to manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -83,11 +73,8 @@ spec: - secret type: object cdbAdminUser: - description: User in the root container with sysdba priviledges to - manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -101,12 +88,10 @@ spec: - secret type: object cdbName: - description: Name of the CDB type: string cdbTlsCrt: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -122,7 +107,6 @@ spec: cdbTlsKey: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -136,40 +120,29 @@ spec: - secret type: object dbPort: - description: DB server port type: integer dbServer: - description: Name of the DB server type: string dbTnsurl: type: string nodeSelector: additionalProperties: type: string - description: Node Selector for running the Pod type: object ordsImage: - description: ORDS Image Name type: string ordsImagePullPolicy: - description: ORDS Image Pull Policy enum: - Always - Never type: string ordsImagePullSecret: - description: The name of the image pull secret in case of a private - docker repository. type: string ordsPort: - description: ORDS server port. For now, keep it as 8888. TO BE USED - IN FUTURE RELEASE. type: integer ordsPwd: - description: Password for user ORDS_PUBLIC_USER properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -183,16 +156,12 @@ spec: - secret type: object replicas: - description: Number of ORDS Containers to create type: integer serviceName: - description: Name of the CDB Service type: string sysAdminPwd: - description: Password for the CDB System Administrator properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -206,10 +175,8 @@ spec: - secret type: object webServerPwd: - description: Password for the Web Server User properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -223,11 +190,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -242,16 +206,12 @@ spec: type: object type: object status: - description: CDBStatus defines the observed state of CDB properties: msg: - description: Message type: string phase: - description: Phase of the CDB Resource type: string status: - description: CDB Resource Status type: boolean required: - phase diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index f19a3e22..33bc2e2a 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -43,22 +43,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: DataguardBroker is the Schema for the dataguardbrokers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DataguardBrokerSpec defines the desired state of DataguardBroker properties: fastStartFailOver: properties: @@ -66,7 +58,6 @@ spec: type: boolean strategy: items: - description: FSFO strategy properties: sourceDatabaseRef: type: string @@ -104,7 +95,6 @@ spec: - standbyDatabaseRefs type: object status: - description: DataguardBrokerStatus defines the observed state of DataguardBroker properties: clusterConnectString: type: string diff --git a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml index 121383fd..8264ff93 100644 --- a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml @@ -35,27 +35,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: OracleRestDataService is the Schema for the oraclerestdataservices - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService properties: adminPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey properties: keepSecret: type: boolean @@ -68,8 +57,6 @@ spec: - secretName type: object apexPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey properties: keepSecret: type: boolean @@ -84,8 +71,6 @@ spec: databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and - pullSecrets for POD properties: pullFrom: type: string @@ -105,8 +90,6 @@ spec: oracleService: type: string ordsPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey properties: keepSecret: type: boolean @@ -121,8 +104,6 @@ spec: ordsUser: type: string persistence: - description: OracleRestDataServicePersistence defines the storage - releated params properties: accessMode: enum: @@ -141,8 +122,6 @@ spec: type: integer restEnableSchemas: items: - description: OracleRestDataServicePDBSchemas defines the PDB Schemas - to be ORDS Enabled properties: enable: type: boolean @@ -169,8 +148,6 @@ spec: - ordsPassword type: object status: - description: OracleRestDataServiceStatus defines the observed state of - OracleRestDataService properties: apexConfigured: type: boolean @@ -185,8 +162,6 @@ spec: databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and - pullSecrets for POD properties: pullFrom: type: string @@ -206,9 +181,6 @@ spec: serviceIP: type: string status: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string type: object type: object diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index 8e6cb94f..2a72da27 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -48,26 +48,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: PDB is the Schema for the pdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: PDBSpec defines the desired state of PDB properties: action: - description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. - Map is used to map a Databse PDB to a Kubernetes PDB CR.' enum: - Create - Clone @@ -79,11 +69,8 @@ spec: - Map type: string adminName: - description: The administrator username for the new PDB. This property - is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -97,11 +84,8 @@ spec: - secret type: object adminPwd: - description: The administrator password for the new PDB. This property - is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -115,43 +99,31 @@ spec: - secret type: object asClone: - description: Indicate if 'AS CLONE' option should be used in the command - to plug in a PDB. This property is applicable when the Action property - is PLUG but not required. + type: boolean + assertivePdbDeletion: type: boolean cdbName: - description: Name of the CDB type: string cdbNamespace: - description: CDB Namespace type: string cdbResName: - description: Name of the CDB Custom Resource that runs the ORDS container type: string copyAction: - description: To copy files or not while cloning a PDB enum: - COPY - NOCOPY - MOVE type: string dropAction: - description: Specify if datafiles should be removed or not. The value - can be INCLUDING or KEEP (default). enum: - INCLUDING - KEEP type: string fileNameConversions: - description: Relevant for Create and Plug operations. As defined in - the Oracle Multitenant Database documentation. Values can be a - filename convert pattern or NONE. type: string getScript: - description: Whether you need the script only or execute the script type: boolean modifyOption: - description: Extra options for opening and closing a PDB enum: - IMMEDIATE - NORMAL @@ -160,11 +132,8 @@ spec: - RESTRICTED type: string pdbName: - description: The name of the new PDB. Relevant for both Create and - Plug Actions. type: string pdbState: - description: The target state of the PDB enum: - OPEN - CLOSE @@ -172,7 +141,6 @@ spec: pdbTlsCat: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -188,7 +156,6 @@ spec: pdbTlsCrt: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -204,7 +171,6 @@ spec: pdbTlsKey: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -218,35 +184,22 @@ spec: - secret type: object reuseTempFile: - description: Whether to reuse temp file type: boolean sourceFileNameConversions: - description: This property is required when the Action property is - Plug. As defined in the Oracle Multitenant Database documentation. - Values can be a source filename convert pattern or NONE. type: string sparseClonePath: - description: A Path specified for sparse clone snapshot copy. (Optional) type: string srcPdbName: - description: Name of the Source PDB from which to clone type: string tdeExport: - description: TDE export for unplug operations type: boolean tdeImport: - description: TDE import for plug operations type: boolean tdeKeystorePath: - description: TDE keystore path is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. type: string tdePassword: - description: TDE password if the tdeImport or tdeExport flag is set - to true. Can be used in create, plug or unplug operations properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -260,11 +213,8 @@ spec: - secret type: object tdeSecret: - description: TDE secret is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -278,26 +228,14 @@ spec: - secret type: object tempSize: - description: Relevant for Create and Clone operations. Total size - for temporary tablespace as defined in the Oracle Multitenant Database - documentation. See size_clause description in Database SQL Language - Reference documentation. type: string totalSize: - description: Relevant for create and plug operations. Total size as - defined in the Oracle Multitenant Database documentation. See size_clause - description in Database SQL Language Reference documentation. type: string unlimitedStorage: - description: Relevant for Create and Plug operations. True for unlimited - storage. Even when set to true, totalSize and tempSize MUST be specified - in the request if Action is Create. type: boolean webServerPwd: - description: Password for the Web ServerPDB User properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -311,11 +249,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -329,37 +264,27 @@ spec: - secret type: object xmlFileName: - description: XML metadata filename to be used for Plug or Unplug operations type: string required: - action type: object status: - description: PDBStatus defines the observed state of PDB properties: action: - description: Last Completed Action type: string connString: - description: PDB Connect String type: string modifyOption: - description: Modify Option of the PDB type: string msg: - description: Message type: string openMode: - description: Open mode of the PDB type: string phase: - description: Phase of the PDB Resource type: string status: - description: PDB Resource Status type: boolean totalSize: - description: Total size of the PDB type: string required: - phase diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 641629a0..e0959a4c 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -30,33 +30,22 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: ShardingDatabase is the Schema for the shardingdatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: InvitedNodeSubnet: type: string catalog: items: - description: CatalogSpec defines the desired state of CatalogSpec properties: envVars: items: - description: EnvironmentVariable represents a named variable - accessible for containers. properties: name: type: string @@ -68,8 +57,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image type: string isDelete: type: string @@ -92,23 +79,11 @@ spec: pvcName: type: string resources: - description: ResourceRequirements describes the compute resource - requirements. properties: claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. type: string required: - name @@ -124,8 +99,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -134,11 +107,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -155,7 +123,6 @@ spec: dbImagePullSecret: type: string dbSecret: - description: Secret Details properties: encryptionType: type: string @@ -183,17 +150,11 @@ spec: type: string gsm: items: - description: GsmSpec defines the desired state of GsmSpec properties: directorName: type: string envVars: - description: Replicas int32 `json:"replicas,omitempty"` // - Gsm Replicas. If you set OraGsmPvcName then it is set default - to 1. items: - description: EnvironmentVariable represents a named variable - accessible for containers. properties: name: type: string @@ -205,8 +166,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image type: string isDelete: type: string @@ -227,23 +186,11 @@ spec: region: type: string resources: - description: ResourceRequirements describes the compute resource - requirements. properties: claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. type: string required: - name @@ -259,8 +206,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -269,11 +214,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -291,7 +231,6 @@ spec: type: string gsmService: items: - description: Service Definition properties: available: type: string @@ -370,7 +309,6 @@ spec: type: array gsmShardSpace: items: - description: ShardSpace Specs properties: chunks: type: integer @@ -406,8 +344,6 @@ spec: type: string portMappings: items: - description: PortMapping is a specification of port mapping for - an application deployment. properties: port: format: int32 @@ -431,18 +367,12 @@ spec: scriptsLocation: type: string shard: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' items: - description: ShardSpec is a specification of Shards for an application - deployment. properties: deployAs: type: string envVars: items: - description: EnvironmentVariable represents a named variable - accessible for containers. properties: name: type: string @@ -454,8 +384,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image type: string isDelete: enum: @@ -483,23 +411,11 @@ spec: pvcName: type: string resources: - description: ResourceRequirements describes the compute resource - requirements. properties: claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. type: string required: - name @@ -515,8 +431,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -525,11 +439,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object shardGroup: @@ -571,8 +480,6 @@ spec: - shard type: object status: - description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 - ShardingDatabaseStatus defines the observed state of ShardingDatabase properties: catalogs: additionalProperties: @@ -580,63 +487,29 @@ spec: type: object conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index 1c011e17..3a5d2ce4 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -53,27 +53,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase properties: adminPassword: - description: SingleInsatnceAdminPassword defines the secret containing - Admin Password mapped to secretKey for Database properties: keepSecret: type: boolean @@ -111,8 +100,6 @@ spec: forceLog: type: boolean image: - description: SingleInstanceDatabaseImage defines the Image source - and pullSecrets for POD properties: prebuiltDB: type: boolean @@ -126,7 +113,6 @@ spec: - pullFrom type: object initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -148,8 +134,6 @@ spec: pdbName: type: string persistence: - description: SingleInstanceDatabasePersistence defines the storage - size and class for PVC properties: accessMode: enum: @@ -199,8 +183,6 @@ spec: type: string type: object sid: - description: SID must be alphanumeric (no special characters, only - a-z, A-Z, 0-9), and no longer than 12 characters. maxLength: 12 pattern: ^[a-zA-Z0-9]+$ type: string @@ -214,8 +196,6 @@ spec: - image type: object status: - description: SingleInstanceDatabaseStatus defines the observed state of - SingleInstanceDatabase properties: apexInstalled: type: boolean @@ -233,63 +213,29 @@ spec: type: string conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -323,7 +269,6 @@ spec: forceLog: type: string initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -354,8 +299,6 @@ spec: pdbName: type: string persistence: - description: SingleInstanceDatabasePersistence defines the storage - size and class for PVC properties: accessMode: enum: diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index b0801738..d9aee464 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -26,26 +26,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: DatabaseObserver is the Schema for the databaseobservers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DatabaseObserverSpec defines the desired state of DatabaseObserver properties: database: - description: DatabaseObserverDatabase defines the database details - used for DatabaseObserver properties: dbConnectionString: properties: @@ -81,13 +71,10 @@ spec: type: object type: object exporter: - description: DatabaseObserverExporterConfig defines the configuration - details related to the exporters of DatabaseObserver properties: configuration: properties: configmap: - description: ConfigMapDetails defines the configmap name properties: configmapName: type: string @@ -98,8 +85,6 @@ spec: image: type: string service: - description: DatabaseObserverService defines the exporter service - component of DatabaseObserver properties: port: format: int32 @@ -114,8 +99,6 @@ spec: type: string type: object prometheus: - description: PrometheusConfig defines the generated resources for - Prometheus properties: labels: additionalProperties: @@ -129,70 +112,32 @@ spec: type: integer type: object status: - description: DatabaseObserverStatus defines the observed state of DatabaseObserver properties: conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/config/database.oracle.com_DbcsSystem.yaml b/config/database.oracle.com_DbcsSystem.yaml new file mode 100644 index 00000000..e933d5a4 --- /dev/null +++ b/config/database.oracle.com_DbcsSystem.yaml @@ -0,0 +1,240 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: DbcsSystem.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: DbcsSystem + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: DbcsSystem is the Schema for the dbcssystems API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DbcsSystemSpec defines the desired state of DbcsSystem + properties: + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + description: DB Backup COnfig Network Struct + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - sshPublicKeys + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + ociConfigMap: + type: string + ociSecret: + type: string + required: + - ociConfigMap + type: object + status: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbEdition: + type: string + dbInfo: + items: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_autonomouscontainerdatabases.yaml b/config/database.oracle.com_autonomouscontainerdatabases.yaml new file mode 100644 index 00000000..bac3a28c --- /dev/null +++ b/config/database.oracle.com_autonomouscontainerdatabases.yaml @@ -0,0 +1,117 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomouscontainerdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousContainerDatabase + listKind: AutonomousContainerDatabaseList + plural: autonomouscontainerdatabases + shortNames: + - acd + - acds + singular: autonomouscontainerdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AutonomousContainerDatabaseSpec defines the desired state + of AutonomousContainerDatabase + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + description: 'AutonomousContainerDatabasePatchModelEnum Enum with + underlying type: string' + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + description: AutonomousContainerDatabaseStatus defines the observed state + of AutonomousContainerDatabase + properties: + lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_autonomousdatabasebackups.yaml b/config/database.oracle.com_autonomousdatabasebackups.yaml new file mode 100644 index 00000000..a5c37507 --- /dev/null +++ b/config/database.oracle.com_autonomousdatabasebackups.yaml @@ -0,0 +1,138 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomousdatabasebackups.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseBackup + listKind: AutonomousDatabaseBackupList + plural: autonomousdatabasebackups + shortNames: + - adbbu + - adbbus + singular: autonomousdatabasebackup + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AutonomousDatabaseBackupSpec defines the desired state of + AutonomousDatabaseBackup + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + isLongTermBackup: + type: boolean + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + retentionPeriodInDays: + type: integer + target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + properties: + k8sADB: + description: "*********************** *\tADB spec ***********************" + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + description: AutonomousDatabaseBackupStatus defines the observed state + of AutonomousDatabaseBackup + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with + underlying type: string' + type: string + timeEnded: + type: string + timeStarted: + type: string + type: + description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying + type: string' + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_autonomousdatabaserestores.yaml b/config/database.oracle.com_autonomousdatabaserestores.yaml new file mode 100644 index 00000000..5e9f2c73 --- /dev/null +++ b/config/database.oracle.com_autonomousdatabaserestores.yaml @@ -0,0 +1,138 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomousdatabaserestores.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseRestore + listKind: AutonomousDatabaseRestoreList + plural: autonomousdatabaserestores + shortNames: + - adbr + - adbrs + singular: autonomousdatabaserestore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AutonomousDatabaseRestoreSpec defines the desired state of + AutonomousDatabaseRestore + properties: + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO + OWN! NOTE: json tags are required. Any new fields you add must + have json tags for the fields to be serialized.' + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + description: 'The timestamp must follow this format: YYYY-MM-DD + HH:MM:SS GMT' + type: string + type: object + type: object + target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + properties: + k8sADB: + description: "*********************** *\tADB spec ***********************" + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + description: AutonomousDatabaseRestoreStatus defines the observed state + of AutonomousDatabaseRestore + properties: + dbName: + type: string + displayName: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + status: + description: 'WorkRequestStatusEnum Enum with underlying type: string' + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_autonomousdatabases.yaml b/config/database.oracle.com_autonomousdatabases.yaml new file mode 100644 index 00000000..f77407f3 --- /dev/null +++ b/config/database.oracle.com_autonomousdatabases.yaml @@ -0,0 +1,324 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomousdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabase + listKind: AutonomousDatabaseList + plural: autonomousdatabases + shortNames: + - adb + - adbs + singular: autonomousdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousDatabase is the Schema for the autonomousdatabases + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase + Important: Run "make" to regenerate code after modifying this file' + properties: + details: + description: AutonomousDatabaseDetails defines the detail information + of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase + properties: + adminPassword: + properties: + k8sSecret: + description: "*********************** *\tSecret specs ***********************" + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + autonomousContainerDatabase: + description: ACDSpec defines the spec of the target for backup/restore + runs. The name could be the name of an AutonomousDatabase or + an AutonomousDatabaseBackup + properties: + k8sACD: + description: "*********************** *\tACD specs ***********************" + properties: + name: + type: string + type: object + ociACD: + properties: + ocid: + type: string + type: object + type: object + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying + type: string' + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + licenseModel: + description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying + type: string' + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + lifecycleState: + description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying + type: string' + type: string + networkAccess: + properties: + accessControlList: + items: + type: string + type: array + accessType: + enum: + - "" + - PUBLIC + - RESTRICTED + - PRIVATE + type: string + isAccessControlEnabled: + type: boolean + isMTLSConnectionRequired: + type: boolean + privateEndpoint: + properties: + hostnamePrefix: + type: string + nsgOCIDs: + items: + type: string + type: array + subnetOCID: + type: string + type: object + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + description: "*********************** *\tSecret specs + ***********************" + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + type: object + type: object + hardLink: + default: false + type: boolean + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + required: + - details + type: object + status: + description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_cdbs.yaml b/config/database.oracle.com_cdbs.yaml new file mode 100644 index 00000000..6b1c350c --- /dev/null +++ b/config/database.oracle.com_cdbs.yaml @@ -0,0 +1,270 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: cdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: CDB + listKind: CDBList + plural: cdbs + singular: cdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: CDB is the Schema for the cdbs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: CDBSpec defines the desired state of CDB + properties: + cdbAdminPwd: + description: Password for the CDB Administrator to manage PDB lifecycle + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + description: User in the root container with sysdba priviledges to + manage PDB lifecycle + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + description: Name of the CDB + type: string + cdbTlsCrt: + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + description: DB server port + type: integer + dbServer: + description: Name of the DB server + type: string + dbTnsurl: + type: string + nodeSelector: + additionalProperties: + type: string + description: Node Selector for running the Pod + type: object + ordsImage: + description: ORDS Image Name + type: string + ordsImagePullPolicy: + description: ORDS Image Pull Policy + enum: + - Always + - Never + type: string + ordsImagePullSecret: + description: The name of the image pull secret in case of a private + docker repository. + type: string + ordsPort: + description: ORDS server port. For now, keep it as 8888. TO BE USED + IN FUTURE RELEASE. + type: integer + ordsPwd: + description: Password for user ORDS_PUBLIC_USER + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + description: Number of ORDS Containers to create + type: integer + serviceName: + description: Name of the CDB Service + type: string + sysAdminPwd: + description: Password for the CDB System Administrator + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + description: Password for the Web Server User + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow + us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + description: CDBStatus defines the observed state of CDB + properties: + msg: + description: Message + type: string + phase: + description: Phase of the CDB Resource + type: string + status: + description: CDB Resource Status + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_dataguardbrokers.yaml b/config/database.oracle.com_dataguardbrokers.yaml new file mode 100644 index 00000000..f19a3e22 --- /dev/null +++ b/config/database.oracle.com_dataguardbrokers.yaml @@ -0,0 +1,134 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: dataguardbrokers.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + singular: dataguardbroker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: DataguardBroker is the Schema for the dataguardbrokers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DataguardBrokerSpec defines the desired state of DataguardBroker + properties: + fastStartFailOver: + properties: + enable: + type: boolean + strategy: + items: + description: FSFO strategy + properties: + sourceDatabaseRef: + type: string + targetDatabaseRefs: + type: string + type: object + type: array + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + description: DataguardBrokerStatus defines the observed state of DataguardBroker + properties: + clusterConnectString: + type: string + externalConnectString: + type: string + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_oraclerestdataservices.yaml b/config/database.oracle.com_oraclerestdataservices.yaml new file mode 100644 index 00000000..121383fd --- /dev/null +++ b/config/database.oracle.com_oraclerestdataservices.yaml @@ -0,0 +1,224 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: oraclerestdataservices.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestDataService + listKind: OracleRestDataServiceList + plural: oraclerestdataservices + singular: oraclerestdataservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: OracleRestDataService is the Schema for the oraclerestdataservices + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService + properties: + adminPassword: + description: OracleRestDataServicePassword defines the secret containing + Password mapped to secretKey + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + apexPassword: + description: OracleRestDataServicePassword defines the secret containing + Password mapped to secretKey + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + description: OracleRestDataServiceImage defines the Image source and + pullSecrets for POD + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + description: OracleRestDataServicePassword defines the secret containing + Password mapped to secretKey + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + description: OracleRestDataServicePersistence defines the storage + releated params + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + description: OracleRestDataServicePDBSchemas defines the PDB Schemas + to be ORDS Enabled + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + description: OracleRestDataServiceStatus defines the observed state of + OracleRestDataService + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + description: OracleRestDataServiceImage defines the Image source and + pullSecrets for POD + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_pdbs.yaml b/config/database.oracle.com_pdbs.yaml new file mode 100644 index 00000000..85af8c1b --- /dev/null +++ b/config/database.oracle.com_pdbs.yaml @@ -0,0 +1,383 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: pdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB + listKind: PDBList + plural: pdbs + singular: pdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: PDB is the Schema for the pdbs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PDBSpec defines the desired state of PDB + properties: + action: + description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. + Map is used to map a Databse PDB to a Kubernetes PDB CR.' + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + description: The administrator username for the new PDB. This property + is required when the Action property is Create. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + description: The administrator password for the new PDB. This property + is required when the Action property is Create. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + description: Indicate if 'AS CLONE' option should be used in the command + to plug in a PDB. This property is applicable when the Action property + is PLUG but not required. + type: boolean + assertivePdbDeletion: + description: turn on the assertive approach to delete pdb resource + kubectl delete pdb ..... automatically triggers the pluggable database + deletion + type: boolean + cdbName: + description: Name of the CDB + type: string + cdbNamespace: + description: CDB Namespace + type: string + cdbResName: + description: Name of the CDB Custom Resource that runs the ORDS container + type: string + copyAction: + description: To copy files or not while cloning a PDB + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + description: Specify if datafiles should be removed or not. The value + can be INCLUDING or KEEP (default). + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + description: Relevant for Create and Plug operations. As defined in + the Oracle Multitenant Database documentation. Values can be a + filename convert pattern or NONE. + type: string + getScript: + description: Whether you need the script only or execute the script + type: boolean + modifyOption: + description: Extra options for opening and closing a PDB + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + description: The name of the new PDB. Relevant for both Create and + Plug Actions. + type: string + pdbState: + description: The target state of the PDB + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + description: Whether to reuse temp file + type: boolean + sourceFileNameConversions: + description: This property is required when the Action property is + Plug. As defined in the Oracle Multitenant Database documentation. + Values can be a source filename convert pattern or NONE. + type: string + sparseClonePath: + description: A Path specified for sparse clone snapshot copy. (Optional) + type: string + srcPdbName: + description: Name of the Source PDB from which to clone + type: string + tdeExport: + description: TDE export for unplug operations + type: boolean + tdeImport: + description: TDE import for plug operations + type: boolean + tdeKeystorePath: + description: TDE keystore path is required if the tdeImport or tdeExport + flag is set to true. Can be used in plug or unplug operations. + type: string + tdePassword: + description: TDE password if the tdeImport or tdeExport flag is set + to true. Can be used in create, plug or unplug operations + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + description: TDE secret is required if the tdeImport or tdeExport + flag is set to true. Can be used in plug or unplug operations. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + description: Relevant for Create and Clone operations. Total size + for temporary tablespace as defined in the Oracle Multitenant Database + documentation. See size_clause description in Database SQL Language + Reference documentation. + type: string + totalSize: + description: Relevant for create and plug operations. Total size as + defined in the Oracle Multitenant Database documentation. See size_clause + description in Database SQL Language Reference documentation. + type: string + unlimitedStorage: + description: Relevant for Create and Plug operations. True for unlimited + storage. Even when set to true, totalSize and tempSize MUST be specified + in the request if Action is Create. + type: boolean + webServerPwd: + description: Password for the Web ServerPDB User + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow + us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + description: XML metadata filename to be used for Plug or Unplug operations + type: string + required: + - action + type: object + status: + description: PDBStatus defines the observed state of PDB + properties: + action: + description: Last Completed Action + type: string + connString: + description: PDB Connect String + type: string + modifyOption: + description: Modify Option of the PDB + type: string + msg: + description: Message + type: string + openMode: + description: Open mode of the PDB + type: string + phase: + description: Phase of the PDB Resource + type: string + status: + description: PDB Resource Status + type: boolean + totalSize: + description: Total size of the PDB + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_shardingdatabases.yaml b/config/database.oracle.com_shardingdatabases.yaml new file mode 100644 index 00000000..641629a0 --- /dev/null +++ b/config/database.oracle.com_shardingdatabases.yaml @@ -0,0 +1,688 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: shardingdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: ShardingDatabase + listKind: ShardingDatabaseList + plural: shardingdatabases + singular: shardingdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ShardingDatabase is the Schema for the shardingdatabases API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ShardingDatabaseSpec defines the desired state of ShardingDatabase + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + description: CatalogSpec defines the desired state of CatalogSpec + properties: + envVars: + items: + description: EnvironmentVariable represents a named variable + accessible for containers. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbEdition: + type: string + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + description: Secret Details + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + fssStorageClass: + type: string + gsm: + items: + description: GsmSpec defines the desired state of GsmSpec + properties: + directorName: + type: string + envVars: + description: Replicas int32 `json:"replicas,omitempty"` // + Gsm Replicas. If you set OraGsmPvcName then it is set default + to 1. + items: + description: EnvironmentVariable represents a named variable + accessible for containers. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + description: Service Definition + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + description: ShardSpace Specs + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: string + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + description: PortMapping is a specification of port mapping for + an application deployment. + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + items: + description: ShardSpec is a specification of Shards for an application + deployment. + properties: + deployAs: + type: string + envVars: + items: + description: EnvironmentVariable represents a named variable + accessible for containers. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + isDelete: + enum: + - enable + - disable + - failed + - force + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 + ShardingDatabaseStatus defines the observed state of ShardingDatabase + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/database.oracle.com_singleinstancedatabases.yaml b/config/database.oracle.com_singleinstancedatabases.yaml new file mode 100644 index 00000000..1c011e17 --- /dev/null +++ b/config/database.oracle.com_singleinstancedatabases.yaml @@ -0,0 +1,421 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: SingleInstanceDatabase is the Schema for the singleinstancedatabases + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase + properties: + adminPassword: + description: SingleInsatnceAdminPassword defines the secret containing + Admin Password mapped to secretKey for Database + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + createAs: + enum: + - primary + - standby + - clone + type: string + dgBrokerConfigured: + type: boolean + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + description: SingleInstanceDatabaseImage defines the Image source + and pullSecrets for POD + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + description: SingleInstanceDatabasePersistence defines the storage + size and class for PVC + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + resources: + properties: + limits: + properties: + cpu: + type: string + memory: + type: string + type: object + requests: + properties: + cpu: + type: string + memory: + type: string + type: object + type: object + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + description: SID must be alphanumeric (no special characters, only + a-z, A-Z, 0-9), and no longer than 12 characters. + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + required: + - image + type: object + status: + description: SingleInstanceDatabaseStatus defines the observed state of + SingleInstanceDatabase + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBrokerConfigured: + type: boolean + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + description: SingleInstanceDatabasePersistence defines the storage + size and class for PVC + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/observability.oracle.com_databaseobservers.yaml b/config/observability.oracle.com_databaseobservers.yaml new file mode 100644 index 00000000..b0801738 --- /dev/null +++ b/config/observability.oracle.com_databaseobservers.yaml @@ -0,0 +1,227 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver + properties: + database: + description: DatabaseObserverDatabase defines the database details + used for DatabaseObserver + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + description: DatabaseObserverExporterConfig defines the configuration + details related to the exporters of DatabaseObserver + properties: + configuration: + properties: + configmap: + description: ConfigMapDetails defines the configmap name + properties: + configmapName: + type: string + key: + type: string + type: object + type: object + image: + type: string + service: + description: DatabaseObserverService defines the exporter service + component of DatabaseObserver + properties: + port: + format: int32 + type: integer + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + description: PrometheusConfig defines the generated resources for + Prometheus + properties: + labels: + additionalProperties: + type: string + type: object + port: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver + properties: + conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + required: + - conditions + - exporterConfig + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/controllers/database/pdb_controller.go b/controllers/database/pdb_controller.go index afd7f247..f0b4fd46 100644 --- a/controllers/database/pdb_controller.go +++ b/controllers/database/pdb_controller.go @@ -121,10 +121,13 @@ var ( ) const PDBFinalizer = "database.oracle.com/PDBfinalizer" +const ONE = 1 +const ZERO = 0 var tdePassword string var tdeSecret string var floodcontrol bool = false +var assertivePdbDeletion bool = false /* Global variable for assertive pdb deletion */ //+kubebuilder:rbac:groups=database.oracle.com,resources=pdbs,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=database.oracle.com,resources=pdbs/status,verbs=get;update;patch @@ -184,9 +187,9 @@ func (r *PDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R } // Finalizer section - err = r.managePDBDeletion(ctx, req, pdb) + err = r.managePDBDeletion2(ctx, req, pdb) if err != nil { - log.Info("Reconcile queued") + log.Info("managePDBDeletion2 Error Deleting resource ") return requeueY, nil } @@ -685,7 +688,6 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db values["tdeSecret"] = tdeSecret } - //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" pdb.Status.TotalSize = pdb.Spec.TotalSize @@ -709,6 +711,10 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) } + assertivePdbDeletion = pdb.Spec.AssertivePdbDeletion + if pdb.Spec.AssertivePdbDeletion == true { + r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Created", "PDB '%s' assertive pdb deletion turned on", pdb.Spec.PDBName) + } log.Info("New connect strinng", "tnsurl", cdb.Spec.DBTnsurl) log.Info("Created PDB Resource", "PDB Name", pdb.Spec.PDBName) r.getPDBState(ctx, req, pdb) @@ -790,6 +796,11 @@ func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dba ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) } + assertivePdbDeletion = pdb.Spec.AssertivePdbDeletion + if pdb.Spec.AssertivePdbDeletion == true { + r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Clone", "PDB '%s' assertive pdb deletion turned on", pdb.Spec.PDBName) + } + log.Info("Cloned PDB successfully", "Source PDB Name", pdb.Spec.SrcPDBName, "Clone PDB Name", pdb.Spec.PDBName) r.getPDBState(ctx, req, pdb) return nil @@ -849,8 +860,6 @@ func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbap values["asClone"] = strconv.FormatBool(*(pdb.Spec.AsClone)) } - //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" - //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" pdb.Status.TotalSize = pdb.Spec.TotalSize @@ -872,6 +881,11 @@ func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbap pdb.Status.ConnString = cdb.Spec.DBTnsurl } + assertivePdbDeletion = pdb.Spec.AssertivePdbDeletion + if pdb.Spec.AssertivePdbDeletion == true { + r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Plugged", "PDB '%s' assertive pdb deletion turned on", pdb.Spec.PDBName) + } + log.Info("Successfully plugged PDB", "PDB Name", pdb.Spec.PDBName) r.getPDBState(ctx, req, pdb) return nil @@ -919,7 +933,6 @@ func (r *PDBReconciler) unplugPDB(ctx context.Context, req ctrl.Request, pdb *db values["tdeExport"] = strconv.FormatBool(*(pdb.Spec.TDEExport)) } - //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.PDBName + "/" url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.PDBName + "/" log.Info("CallAPI(url)", "url", url) @@ -1015,7 +1028,6 @@ func (r *PDBReconciler) modifyPDB(ctx context.Context, req ctrl.Request, pdb *db log.Info("PDB STATUS OPENMODE", "pdb.Status.OpenMode=", pdb.Status.OpenMode) pdbName := pdb.Spec.PDBName - //url := "https://" + pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" pdb.Status.Phase = pdbPhaseModify @@ -1059,8 +1071,6 @@ func (r *PDBReconciler) getPDBState(ctx context.Context, req ctrl.Request, pdb * } pdbName := pdb.Spec.PDBName - //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" - //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" pdb.Status.Msg = "Getting PDB state" @@ -1112,8 +1122,6 @@ func (r *PDBReconciler) mapPDB(ctx context.Context, req ctrl.Request, pdb *dbapi } pdbName := pdb.Spec.PDBName - //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" - //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" pdb.Status.Msg = "Mapping PDB" @@ -1133,12 +1141,15 @@ func (r *PDBReconciler) mapPDB(ctx context.Context, req ctrl.Request, pdb *dbapi log.Error(err, "Failed to get state of PDB :"+pdbName, "err", err.Error()) } - //fmt.Printf("%+v\n", objmap) totSizeInBytes := objmap["total_size"].(float64) totSizeInGB := totSizeInBytes / 1024 / 1024 / 1024 pdb.Status.OpenMode = objmap["open_mode"].(string) pdb.Status.TotalSize = fmt.Sprintf("%.2f", totSizeInGB) + "G" + assertivePdbDeletion = pdb.Spec.AssertivePdbDeletion + if pdb.Spec.AssertivePdbDeletion == true { + r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Mapped", "PDB '%s' assertive pdb deletion turned on", pdb.Spec.PDBName) + } if cdb.Spec.DBServer != "" { pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName @@ -1192,49 +1203,72 @@ func (r *PDBReconciler) deletePDB(ctx context.Context, req ctrl.Request, pdb *db return nil } -/* -************************************************ +/************************************************* - Check PDB deletion - /*********************************************** -*/ -func (r *PDBReconciler) managePDBDeletion(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - log := r.Log.WithValues("managePDBDeletion", req.NamespacedName) - - // Check if the PDB instance is marked to be deleted, which is - // indicated by the deletion timestamp being set. - isPDBMarkedToBeDeleted := pdb.GetDeletionTimestamp() != nil - if isPDBMarkedToBeDeleted { - log.Info("Marked to be deleted") - pdb.Status.Phase = pdbPhaseDelete - pdb.Status.Status = true - r.Status().Update(ctx, pdb) +**************************************************/ +func (r *PDBReconciler) managePDBDeletion2(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { + log := r.Log.WithValues("managePDBDeletion", req.NamespacedName) + if pdb.ObjectMeta.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(pdb, PDBFinalizer) { + controllerutil.AddFinalizer(pdb, PDBFinalizer) + if err := r.Update(ctx, pdb); err != nil { + return err + } + } + } else { + log.Info("Pdb marked to be delted") if controllerutil.ContainsFinalizer(pdb, PDBFinalizer) { - // Remove PDBFinalizer. Once all finalizers have been - // removed, the object will be deleted. - log.Info("Removing finalizer") + if assertivePdbDeletion == true { + log.Info("Deleting pdb CRD: Assertive approach is turned on ") + cdb, err := r.getCDBResource(ctx, req, pdb) + if err != nil { + log.Error(err, "Cannont find cdb resource ", "err", err.Error()) + return err + } + + pdbName := pdb.Spec.PDBName + if pdb.Status.OpenMode == "READ WRITE" { + valuesclose := map[string]string{ + "state": "CLOSE", + "modifyOption": "IMMEDIATE", + "getScript": "FALSE"} + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" + _, errclose := r.callAPI(ctx, req, pdb, url, valuesclose, "POST") + if errclose != nil { + log.Info("Warning error closing pdb continue anyway") + } + } + + valuesdrop := map[string]string{ + "action": "INCLUDING", + "getScript": "FALSE"} + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" + + log.Info("Call Delete()") + _, errdelete := r.callAPI(ctx, req, pdb, url, valuesdrop, "DELETE") + if errdelete != nil { + log.Error(errdelete, "Fail to delete pdb :"+pdb.Name, "err", err.Error()) + return errdelete + } + } /* END OF ASSERTIVE SECTION */ + + log.Info("Marked to be deleted") + pdb.Status.Phase = pdbPhaseDelete + pdb.Status.Status = true + r.Status().Update(ctx, pdb) + controllerutil.RemoveFinalizer(pdb, PDBFinalizer) - err := r.Update(ctx, pdb) - if err != nil { - log.Info("Could not remove finalizer", "err", err.Error()) + if err := r.Update(ctx, pdb); err != nil { + log.Info("Cannot remove finalizer") return err } - log.Info("Successfully removed PDB resource") - return nil - } - } - // Add finalizer for this CR - if !controllerutil.ContainsFinalizer(pdb, PDBFinalizer) { - log.Info("Adding finalizer") - controllerutil.AddFinalizer(pdb, PDBFinalizer) - err := r.Update(ctx, pdb) - if err != nil { - log.Info("Could not add finalizer", "err", err.Error()) - return err } - pdb.Status.Status = false + + return nil } + return nil } diff --git a/docs/multitenant/usecase01/pdb_create.yaml b/docs/multitenant/usecase01/pdb_create.yaml index 7953118f..be3581ad 100644 --- a/docs/multitenant/usecase01/pdb_create.yaml +++ b/docs/multitenant/usecase01/pdb_create.yaml @@ -43,4 +43,5 @@ spec: totalSize: "1G" tempSize: "100M" action: "Create" + assertivePdbDeletion: true diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index c41093c6..9185ab9c 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -37,18 +37,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase properties: action: enum: @@ -57,7 +53,6 @@ spec: - TERMINATE type: string autonomousContainerDatabaseOCID: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' type: string autonomousExadataVMClusterOCID: type: string @@ -73,7 +68,6 @@ spec: default: false type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -81,17 +75,14 @@ spec: type: string type: object patchModel: - description: 'AutonomousContainerDatabasePatchModelEnum Enum with underlying type: string' enum: - RELEASE_UPDATES - RELEASE_UPDATE_REVISIONS type: string type: object status: - description: AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase properties: lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string timeCreated: type: string @@ -148,18 +139,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseBackupSpec defines the desired state of AutonomousDatabaseBackup properties: autonomousDatabaseBackupOCID: type: string @@ -168,7 +155,6 @@ spec: isLongTermBackup: type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -178,10 +164,8 @@ spec: retentionPeriodInDays: type: integer target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -194,7 +178,6 @@ spec: type: object type: object status: - description: AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup properties: autonomousDatabaseOCID: type: string @@ -207,14 +190,12 @@ spec: isAutomatic: type: boolean lifecycleState: - description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with underlying type: string' type: string timeEnded: type: string timeStarted: type: string type: - description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying type: string' type: string required: - autonomousDatabaseOCID @@ -269,21 +250,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore properties: ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -293,7 +269,6 @@ spec: source: properties: k8sADBBackup: - description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.' properties: name: type: string @@ -301,15 +276,12 @@ spec: pointInTime: properties: timestamp: - description: 'The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT' type: string type: object type: object target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -325,15 +297,12 @@ spec: - target type: object status: - description: AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore properties: dbName: type: string displayName: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string status: - description: 'WorkRequestStatusEnum Enum with underlying type: string' type: string timeAccepted: type: string @@ -408,26 +377,20 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabase is the Schema for the autonomousdatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase Important: Run "make" to regenerate code after modifying this file' properties: details: - description: AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase properties: adminPassword: properties: k8sSecret: - description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -439,10 +402,8 @@ spec: type: object type: object autonomousContainerDatabase: - description: ACDSpec defines the spec of the target for backup/restore runs. The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup properties: k8sACD: - description: "*********************** *\tACD specs ***********************" properties: name: type: string @@ -466,7 +427,6 @@ spec: dbVersion: type: string dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying type: string' enum: - OLTP - DW @@ -484,13 +444,11 @@ spec: isDedicated: type: boolean licenseModel: - description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying type: string' enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' type: string networkAccess: properties: @@ -528,7 +486,6 @@ spec: password: properties: k8sSecret: - description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -545,7 +502,6 @@ spec: default: false type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -556,7 +512,6 @@ spec: - details type: object status: - description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase properties: allConnectionStrings: items: @@ -578,36 +533,29 @@ spec: type: array conditions: items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -623,7 +571,6 @@ spec: - type x-kubernetes-list-type: map lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string timeCreated: type: string @@ -690,24 +637,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: CDB is the Schema for the cdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: CDBSpec defines the desired state of CDB properties: cdbAdminPwd: - description: Password for the CDB Administrator to manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -721,10 +662,8 @@ spec: - secret type: object cdbAdminUser: - description: User in the root container with sysdba priviledges to manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -738,12 +677,10 @@ spec: - secret type: object cdbName: - description: Name of the CDB type: string cdbTlsCrt: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -759,7 +696,6 @@ spec: cdbTlsKey: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -773,38 +709,29 @@ spec: - secret type: object dbPort: - description: DB server port type: integer dbServer: - description: Name of the DB server type: string dbTnsurl: type: string nodeSelector: additionalProperties: type: string - description: Node Selector for running the Pod type: object ordsImage: - description: ORDS Image Name type: string ordsImagePullPolicy: - description: ORDS Image Pull Policy enum: - Always - Never type: string ordsImagePullSecret: - description: The name of the image pull secret in case of a private docker repository. type: string ordsPort: - description: ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. type: integer ordsPwd: - description: Password for user ORDS_PUBLIC_USER properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -818,16 +745,12 @@ spec: - secret type: object replicas: - description: Number of ORDS Containers to create type: integer serviceName: - description: Name of the CDB Service type: string sysAdminPwd: - description: Password for the CDB System Administrator properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -841,10 +764,8 @@ spec: - secret type: object webServerPwd: - description: Password for the Web Server User properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -858,10 +779,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -876,16 +795,12 @@ spec: type: object type: object status: - description: CDBStatus defines the observed state of CDB properties: msg: - description: Message type: string phase: - description: Phase of the CDB Resource type: string status: - description: CDB Resource Status type: boolean required: - phase @@ -929,21 +844,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: DatabaseObserver is the Schema for the databaseobservers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DatabaseObserverSpec defines the desired state of DatabaseObserver properties: database: - description: DatabaseObserverDatabase defines the database details used for DatabaseObserver properties: dbConnectionString: properties: @@ -979,12 +889,10 @@ spec: type: object type: object exporter: - description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver properties: configuration: properties: configmap: - description: ConfigMapDetails defines the configmap name properties: configmapName: type: string @@ -995,7 +903,6 @@ spec: image: type: string service: - description: DatabaseObserverService defines the exporter service component of DatabaseObserver properties: port: format: int32 @@ -1010,7 +917,6 @@ spec: type: string type: object prometheus: - description: PrometheusConfig defines the generated resources for Prometheus properties: labels: additionalProperties: @@ -1024,41 +930,32 @@ spec: type: integer type: object status: - description: DatabaseObserverStatus defines the observed state of DatabaseObserver properties: conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1135,18 +1032,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: DataguardBroker is the Schema for the dataguardbrokers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DataguardBrokerSpec defines the desired state of DataguardBroker properties: fastStartFailOver: properties: @@ -1154,7 +1047,6 @@ spec: type: boolean strategy: items: - description: FSFO strategy properties: sourceDatabaseRef: type: string @@ -1192,7 +1084,6 @@ spec: - standbyDatabaseRefs type: object status: - description: DataguardBrokerStatus defines the observed state of DataguardBroker properties: clusterConnectString: type: string @@ -1491,21 +1382,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: OracleRestDataService is the Schema for the oraclerestdataservices API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService properties: adminPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1518,7 +1404,6 @@ spec: - secretName type: object apexPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1533,7 +1418,6 @@ spec: databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD properties: pullFrom: type: string @@ -1553,7 +1437,6 @@ spec: oracleService: type: string ordsPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1568,7 +1451,6 @@ spec: ordsUser: type: string persistence: - description: OracleRestDataServicePersistence defines the storage releated params properties: accessMode: enum: @@ -1587,7 +1469,6 @@ spec: type: integer restEnableSchemas: items: - description: OracleRestDataServicePDBSchemas defines the PDB Schemas to be ORDS Enabled properties: enable: type: boolean @@ -1614,7 +1495,6 @@ spec: - ordsPassword type: object status: - description: OracleRestDataServiceStatus defines the observed state of OracleRestDataService properties: apexConfigured: type: boolean @@ -1629,7 +1509,6 @@ spec: databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD properties: pullFrom: type: string @@ -1649,7 +1528,6 @@ spec: serviceIP: type: string status: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string type: object type: object @@ -1712,21 +1590,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: PDB is the Schema for the pdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: PDBSpec defines the desired state of PDB properties: action: - description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR.' enum: - Create - Clone @@ -1738,10 +1611,8 @@ spec: - Map type: string adminName: - description: The administrator username for the new PDB. This property is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1755,10 +1626,8 @@ spec: - secret type: object adminPwd: - description: The administrator password for the new PDB. This property is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1772,38 +1641,31 @@ spec: - secret type: object asClone: - description: Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. + type: boolean + assertivePdbDeletion: type: boolean cdbName: - description: Name of the CDB type: string cdbNamespace: - description: CDB Namespace type: string cdbResName: - description: Name of the CDB Custom Resource that runs the ORDS container type: string copyAction: - description: To copy files or not while cloning a PDB enum: - COPY - NOCOPY - MOVE type: string dropAction: - description: Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). enum: - INCLUDING - KEEP type: string fileNameConversions: - description: Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. type: string getScript: - description: Whether you need the script only or execute the script type: boolean modifyOption: - description: Extra options for opening and closing a PDB enum: - IMMEDIATE - NORMAL @@ -1812,10 +1674,8 @@ spec: - RESTRICTED type: string pdbName: - description: The name of the new PDB. Relevant for both Create and Plug Actions. type: string pdbState: - description: The target state of the PDB enum: - OPEN - CLOSE @@ -1823,7 +1683,6 @@ spec: pdbTlsCat: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1839,7 +1698,6 @@ spec: pdbTlsCrt: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1855,7 +1713,6 @@ spec: pdbTlsKey: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1869,31 +1726,22 @@ spec: - secret type: object reuseTempFile: - description: Whether to reuse temp file type: boolean sourceFileNameConversions: - description: This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. type: string sparseClonePath: - description: A Path specified for sparse clone snapshot copy. (Optional) type: string srcPdbName: - description: Name of the Source PDB from which to clone type: string tdeExport: - description: TDE export for unplug operations type: boolean tdeImport: - description: TDE import for plug operations type: boolean tdeKeystorePath: - description: TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. type: string tdePassword: - description: TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1907,10 +1755,8 @@ spec: - secret type: object tdeSecret: - description: TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1924,19 +1770,14 @@ spec: - secret type: object tempSize: - description: Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. type: string totalSize: - description: Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. type: string unlimitedStorage: - description: Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. type: boolean webServerPwd: - description: Password for the Web ServerPDB User properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1950,10 +1791,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1967,37 +1806,27 @@ spec: - secret type: object xmlFileName: - description: XML metadata filename to be used for Plug or Unplug operations type: string required: - action type: object status: - description: PDBStatus defines the observed state of PDB properties: action: - description: Last Completed Action type: string connString: - description: PDB Connect String type: string modifyOption: - description: Modify Option of the PDB type: string msg: - description: Message type: string openMode: - description: Open mode of the PDB type: string phase: - description: Phase of the PDB Resource type: string status: - description: PDB Resource Status type: boolean totalSize: - description: Total size of the PDB type: string required: - phase @@ -2045,28 +1874,22 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: ShardingDatabase is the Schema for the shardingdatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: InvitedNodeSubnet: type: string catalog: items: - description: CatalogSpec defines the desired state of CatalogSpec properties: envVars: items: - description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2078,7 +1901,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: type: string @@ -2101,15 +1923,11 @@ spec: pvcName: type: string resources: - description: ResourceRequirements describes the compute resource requirements. properties: claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2125,7 +1943,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2134,7 +1951,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -2151,7 +1967,6 @@ spec: dbImagePullSecret: type: string dbSecret: - description: Secret Details properties: encryptionType: type: string @@ -2179,14 +1994,11 @@ spec: type: string gsm: items: - description: GsmSpec defines the desired state of GsmSpec properties: directorName: type: string envVars: - description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. items: - description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2198,7 +2010,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: type: string @@ -2219,15 +2030,11 @@ spec: region: type: string resources: - description: ResourceRequirements describes the compute resource requirements. properties: claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2243,7 +2050,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2252,7 +2058,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -2270,7 +2075,6 @@ spec: type: string gsmService: items: - description: Service Definition properties: available: type: string @@ -2349,7 +2153,6 @@ spec: type: array gsmShardSpace: items: - description: ShardSpace Specs properties: chunks: type: integer @@ -2385,7 +2188,6 @@ spec: type: string portMappings: items: - description: PortMapping is a specification of port mapping for an application deployment. properties: port: format: int32 @@ -2409,15 +2211,12 @@ spec: scriptsLocation: type: string shard: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' items: - description: ShardSpec is a specification of Shards for an application deployment. properties: deployAs: type: string envVars: items: - description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2429,7 +2228,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: enum: @@ -2457,15 +2255,11 @@ spec: pvcName: type: string resources: - description: ResourceRequirements describes the compute resource requirements. properties: claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2481,7 +2275,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2490,7 +2283,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object shardGroup: @@ -2532,7 +2324,6 @@ spec: - shard type: object status: - description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 ShardingDatabaseStatus defines the observed state of ShardingDatabase properties: catalogs: additionalProperties: @@ -2540,36 +2331,29 @@ spec: type: object conditions: items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2673,21 +2457,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase properties: adminPassword: - description: SingleInsatnceAdminPassword defines the secret containing Admin Password mapped to secretKey for Database properties: keepSecret: type: boolean @@ -2725,7 +2504,6 @@ spec: forceLog: type: boolean image: - description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD properties: prebuiltDB: type: boolean @@ -2739,7 +2517,6 @@ spec: - pullFrom type: object initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -2761,7 +2538,6 @@ spec: pdbName: type: string persistence: - description: SingleInstanceDatabasePersistence defines the storage size and class for PVC properties: accessMode: enum: @@ -2811,7 +2587,6 @@ spec: type: string type: object sid: - description: SID must be alphanumeric (no special characters, only a-z, A-Z, 0-9), and no longer than 12 characters. maxLength: 12 pattern: ^[a-zA-Z0-9]+$ type: string @@ -2825,7 +2600,6 @@ spec: - image type: object status: - description: SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase properties: apexInstalled: type: boolean @@ -2843,36 +2617,29 @@ spec: type: string conditions: items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2906,7 +2673,6 @@ spec: forceLog: type: string initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -2937,7 +2703,6 @@ spec: pdbName: type: string persistence: - description: SingleInstanceDatabasePersistence defines the storage size and class for PVC properties: accessMode: enum: From 56b3bd68e710c10587e131e115f97ceaf7bd7ef7 Mon Sep 17 00:00:00 2001 From: racpack Date: Tue, 20 Aug 2024 06:20:34 +0000 Subject: [PATCH 114/414] Review sharding changes --- commons/sharding/catalog.go | 2 +- commons/sharding/exec.go | 21 +++++++ commons/sharding/scommon.go | 4 +- commons/sharding/shard.go | 2 +- .../database/shardingdatabase_controller.go | 60 +++++++++++++++---- 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index 51af477b..e325ce6c 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -485,7 +485,7 @@ func UpdateProvForCatalog(instance *databasev1alpha1.ShardingDatabase, oraSpexRes := OraCatalogSpex.Resources if !reflect.DeepEqual(shardContaineRes, oraSpexRes) { - isUpdate = true + isUpdate = false } } } diff --git a/commons/sharding/exec.go b/commons/sharding/exec.go index c1921018..44f91e51 100644 --- a/commons/sharding/exec.go +++ b/commons/sharding/exec.go @@ -63,12 +63,33 @@ import ( // ExecCMDInContainer execute command in first container of a pod func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasealphav1.ShardingDatabase, logger logr.Logger) (string, string, error) { + var err1 error = nil var msg string var ( execOut bytes.Buffer execErr bytes.Buffer ) + for i := 0; i < 5; i++ { + if scheme.Scheme == nil { + time.Sleep(time.Second * 40) + } else { + break + } + } + + if kubeClient == nil { + msg = "ExecCommand() : kubeClient is nil" + err1 = fmt.Errorf(msg) + return "Error:","kubeClient is nil",err1 + } + if kubeConfig == nil { + msg = "ExecCommand() : kubeConfig is nil" + err1 = fmt.Errorf(msg) + return "Error:","kubeConfig is nil",err1 + } + + msg = "" req := kubeClient.CoreV1().RESTClient(). Post(). Namespace(instance.Spec.Namespace). diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 8c70d3a0..98892962 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -1326,7 +1326,7 @@ func CheckOnlineShardInGsm(gsmPodName string, sparams string, instance *database _, _, err := ExecCommand(gsmPodName, getOnlineShardCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { - msg := "Shard: " + GetFmtStr(sparams) + " is not onine in GSM." + msg := "Shard: " + GetFmtStr(sparams) + " is not online in GSM." LogMessages("INFO", msg, nil, instance, logger) return err } @@ -1533,7 +1533,7 @@ func CheckIsDeleteFlag(delStr string, instance *databasealphav1.ShardingDatabase return true } if strings.ToLower(delStr) == "failed" { - LogMessages("INFO", "manual intervention required", nil, instance, logger) + // LogMessages("INFO", "manual intervention required", nil, instance, logger) } return false } diff --git a/commons/sharding/shard.go b/commons/sharding/shard.go index 5ef81d4a..c76fc0e5 100644 --- a/commons/sharding/shard.go +++ b/commons/sharding/shard.go @@ -480,7 +480,7 @@ func UpdateProvForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpe oraSpexRes := OraShardSpex.Resources if !reflect.DeepEqual(shardContaineRes, oraSpexRes) { - isUpdate = true + isUpdate = false } } } diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 205fc7fe..b76de272 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -94,6 +94,9 @@ type ShardingDatabaseReconciler struct { Namespace string } +var sentFailMsg=make(map[string]bool) +var sentCompleteMsg=make(map[string]bool) + // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases/status,verbs=get;update;patch // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases/finalizers,verbs=get;create;update;patch;delete @@ -1346,6 +1349,9 @@ func (r *ShardingDatabaseReconciler) updateShardTopologyShardsInGsm(instance *da if len(instance.Spec.Shard) > 0 { for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex := instance.Spec.Shard[i] + if strings.ToLower(OraShardSpex.IsDelete) == "failed" { + continue + } // stateStr := shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { shardSfSet, _, err = r.validateShard(instance, OraShardSpex, int(i)) @@ -1569,7 +1575,12 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.AddingShardErrorState)) title = "Shard Addition Failure" message = "Error occurred during shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " addition." - r.sendMessage(instance, title, message) + shardingv1.LogMessages("INFO", title + ":" + message, nil, instance, r.Log) + if sentFailMsg[OraShardSpex.Name] != true { + r.sendMessage(instance, title, message) + } + sentFailMsg[OraShardSpex.Name]=true + sentCompleteMsg[OraShardSpex.Name]=false deployFlag = false } } @@ -1620,7 +1631,13 @@ func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev1alpha1.Sha if oldStateStr != string(databasev1alpha1.ShardOnlineState) { title = "Shard Addition Completed" message = "Shard addition completed for shard " + shardingv1.GetFmtStr(shardSfSet.Name) + " in GSM." - r.sendMessage(instance, title, message) + shardingv1.LogMessages("INFO", title + ":" + message, nil, instance, r.Log) + if sentCompleteMsg[shardSfSet.Name] != true { + r.sendMessage(instance, title, message) + } + + sentCompleteMsg[shardSfSet.Name] = true + sentFailMsg[shardSfSet.Name] = false } return nil } @@ -1721,13 +1738,17 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar // This is a loop and will check unless there is a error or chunks has moved // Validate if the chunks has moved before performing shard deletion for { - msg = "Sleeping for 120 seconds and will check status again of chunks movement in gsm for shard: " + shardingv1.GetFmtStr(OraShardSpex.Name) + msg = "Sleeping for 120 seconds and will check status again of chunks movement in gsm for shard: " + shardingv1.GetFmtStr(OraShardSpex.Name) + "ShardType=" + strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) time.Sleep(120 * time.Second) err = shardingv1.VerifyChunks(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err == nil { break } else { + if strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) != "USER" { + // If ShardingType is not "USER", do not perform the patching.. continue + continue + } instance.Spec.Shard[i].IsDelete = "failed" err = shardingv1.InstanceShardPatch(instance, instance, r.Client, i, "isDelete", "failed") if err != nil { @@ -1927,6 +1948,15 @@ func (r *ShardingDatabaseReconciler) deployStatefulSet(instance *databasev1alpha message := "Inside the deployStatefulSet function" shardingv1.LogMessages("DEBUG", message, nil, instance, r.Log) // See if StatefulSets already exists and create if it doesn't + // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) + // This happens during unit test cases + for i := 0; i < 5; i++ { + if r.Scheme == nil { + time.Sleep(time.Second * 40) + } else { + break + } + } controllerutil.SetControllerReference(instance, dep, r.Scheme) found := &appsv1.StatefulSet{} err := r.Client.Get(context.TODO(), types.NamespacedName{ @@ -1970,9 +2000,18 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. var OraShardSpex databasev1alpha1.ShardSpec var currState string var eventMsg string + var msg string currState = "" eventMsg = "" + + msg = "checkShardState():ShardType=" + strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) + shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + if strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) != "USER" { + // ShardingType is not "USER", so return + return err + } + if len(instance.Status.Gsm.Shards) > 0 { for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] @@ -1981,14 +2020,11 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. eventMsg = "Shard Addition in progress. Requeuing" err = fmt.Errorf(eventMsg) break - // } else if currState == string(databasev1alpha1.AddingShardErrorState) { - // eventMsg = "Shard Addition Error. Manual intervention required. Requeuing" - // err = fmt.Errorf(eventMsg) - // break } else if currState == string(databasev1alpha1.DeletingState) { - eventMsg = "Shard Deletion in progress. Requeuing" - err = fmt.Errorf(eventMsg) - break + eventMsg = "Shard Deletion in progress. Requeuing" + err = fmt.Errorf(eventMsg) + err = nil + break } else if OraShardSpex.IsDelete == "failed" { eventMsg = "Shard Deletion failed. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) @@ -2003,11 +2039,11 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. break } else { eventMsg = "checkShardState() : Shard State=[" + currState + "]" + shardingv1.LogMessages("INFO", eventMsg, nil, instance, r.Log) err = nil } } - r.publishEvents(instance, eventMsg, currState) - + r.publishEvents(instance, eventMsg, currState) } return err } From d4045a1df1562e0d3bd2567e6ae88590066c0096 Mon Sep 17 00:00:00 2001 From: matteo malvezzi Date: Tue, 20 Aug 2024 17:24:05 +0200 Subject: [PATCH 115/414] fix documentation broken links --- docs/multitenant/README.md | 142 +- .../example_setup_using_oci_oke_cluster.md | 38 + .../multinamespace/cdb_create.yaml | 44 + .../multinamespace/pdb_clone.yaml | 50 + .../multinamespace/pdb_close.yaml | 44 + .../multinamespace/pdb_create.yaml | 46 + .../multinamespace/pdb_delete.yaml | 34 + .../provisioning/multinamespace/pdb_open.yaml | 43 + .../provisioning/multinamespace/pdb_plug.yaml | 46 + .../multinamespace/pdb_unplug.yaml | 39 + docs/multitenant/provisioning/ords_image.md | 81 ++ .../provisioning/quickOKEcreation.md | 136 ++ .../singlenamespace/cdb_create.yaml | 44 + .../singlenamespace/cdb_secret.yaml | 17 + .../singlenamespace/pdb_clone.yaml | 50 + .../singlenamespace/pdb_close.yaml | 44 + .../singlenamespace/pdb_create.yaml | 47 + .../singlenamespace/pdb_delete.yaml | 34 + .../singlenamespace/pdb_open.yaml | 43 + .../singlenamespace/pdb_plug.yaml | 46 + .../singlenamespace/pdb_secret.yaml | 16 + .../singlenamespace/pdb_unplug.yaml | 39 + docs/multitenant/usecase01/README.md | 150 ++- .../usecase01/logfiles/BuildImage.log | 1199 +++++++++++------ .../usecase01/logfiles/cdb_creation.log | 357 +++++ .../usecase01/logfiles/openssl_execution.log | 27 +- .../usecase01/logfiles/ordsconfig.log | 58 +- .../usecase01/logfiles/tagandpush.log | 14 + .../usecase01/logfiles/testapi.log | 29 +- docs/multitenant/usecase01/makefile | 6 +- docs/multitenant/usecase01/pdb_map.yaml | 1 + docs/multitenant/usecase02/README.md | 10 +- docs/multitenant/usecase02/pdb_clone.yaml | 1 + docs/multitenant/usecase02/pdb_plug.yaml | 1 + docs/multitenant/usecase03/README.md | 8 +- docs/multitenant/usecase03/cdb_create.yaml | 4 +- 36 files changed, 2412 insertions(+), 576 deletions(-) create mode 100644 docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md create mode 100644 docs/multitenant/provisioning/multinamespace/cdb_create.yaml create mode 100644 docs/multitenant/provisioning/multinamespace/pdb_clone.yaml create mode 100644 docs/multitenant/provisioning/multinamespace/pdb_close.yaml create mode 100644 docs/multitenant/provisioning/multinamespace/pdb_create.yaml create mode 100644 docs/multitenant/provisioning/multinamespace/pdb_delete.yaml create mode 100644 docs/multitenant/provisioning/multinamespace/pdb_open.yaml create mode 100644 docs/multitenant/provisioning/multinamespace/pdb_plug.yaml create mode 100644 docs/multitenant/provisioning/multinamespace/pdb_unplug.yaml create mode 100644 docs/multitenant/provisioning/ords_image.md create mode 100644 docs/multitenant/provisioning/quickOKEcreation.md create mode 100644 docs/multitenant/provisioning/singlenamespace/cdb_create.yaml create mode 100644 docs/multitenant/provisioning/singlenamespace/cdb_secret.yaml create mode 100644 docs/multitenant/provisioning/singlenamespace/pdb_clone.yaml create mode 100644 docs/multitenant/provisioning/singlenamespace/pdb_close.yaml create mode 100644 docs/multitenant/provisioning/singlenamespace/pdb_create.yaml create mode 100644 docs/multitenant/provisioning/singlenamespace/pdb_delete.yaml create mode 100644 docs/multitenant/provisioning/singlenamespace/pdb_open.yaml create mode 100644 docs/multitenant/provisioning/singlenamespace/pdb_plug.yaml create mode 100644 docs/multitenant/provisioning/singlenamespace/pdb_secret.yaml create mode 100644 docs/multitenant/provisioning/singlenamespace/pdb_unplug.yaml create mode 100644 docs/multitenant/usecase01/logfiles/cdb_creation.log create mode 100644 docs/multitenant/usecase01/logfiles/tagandpush.log diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index b20eaa4a..6028cad0 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -1,31 +1,37 @@ +# Oracle Multitenant Database Controllers +The Oracle Database Operator for kubernetes uses two controllers to manage [Pluggable Database life cycle][oradocpdb] -# Oracle Multitenant Database Controller +- CDB controller +- PDB controller -> WARNING: Examples with https are located in the use case directories +By usigng CDB/PDB controllers you can perform the following actions **CREATE**,**MODIFY(OPEN/COSE)**,**DELETE**,**CLONE**,**PLUG** and **UNPLUG** -Detailed examples can be found here +This file examplains how to setup CDB and PDB controllers, additional details can be found in the README files under usecases directories.. -- [Usecase01](./usecase01) pdb crd and cdb pod are running in the same namesaoce -- [Usecase02](./usecase02) unplug and plug operation examples -- [Usecase03](./usecase03) multiple namespace example cdb pod ,pdb crd and pod operator are running in different namespaces +- [Usecase01][uc01] pdb crd and cdb pod are running in the same namesaoce +- [Usecase02][uc02] unplug and plug operation examples +- [Usecase03][uc03] multiple namespace example cdb pod ,pdb crd and pod operator are running in different namespaces. +> **NOTE** that there is no controller for Container Database Operations -CDBs and PDBs are part of the Oracle Database [Multitenant Architecture](https://docs.oracle.com/en/database/oracle/oracle-database/21/multi/introduction-to-the-multitenant-architecture.html#GUID-AB84D6C9-4BBE-4D36-992F-2BB85739329F). The Multitenant Database Controller is a feature of Oracle DB Operator for Kubernetes (`OraOperator`), which helps to manage the lifecycle of Pluggable Databases (PDBs) in an Oracle Container Database (CDB). +## Macro steps for setup -The target CDB for which PDB lifecycle management is needed can be running on a machine on-premises. To manage the PDBs of that target CDB, you can run the Oracle DB Operator on a Kubernetes system on-premises (For Example: [Oracle Linux Cloud Native Environment or OLCNE](https://docs.oracle.com/en/operating-systems/olcne/)). +- Deply the Oracle Database Operator +- Create Ords based image for CDB pod +- Container DB user creation +- Create secrets for credentials +- Create certificates for https connection +- Create CDB pod -NOTE: The target CDB can also run in a Cloud environment, such as an OCI [Oracle Base Database Service](https://docs.oracle.com/en-us/iaas/dbcs/doc/bare-metal-and-virtual-machine-db-systems.html)). To manage PDBs on the target CDB, the Oracle DB Operator can run on a Kubernetes Cluster running in the cloud, such as OCI's [Container Engine for Kubernetes or OKE](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm#Overview_of_Container_Engine_for_Kubernetes)) - - - -# Oracle DB Operator Multitenant Database Controller Deployment +## Oracle DB Operator Multitenant Database Controller Deployment To deploy OraOperator, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. After the Oracle Database Operator is deployed, you can see the DB Operator Pods running in the Kubernetes Cluster. As part of the `OraOperator` deployment, the multitenant Database Controller is deployed. You can see the CRDs (Custom Resource Definition) for the CDB and PDBs in the list of CRDs. The following output is an example of such a deployment: + ```bash [root@test-server oracle-database-operator]# kubectl get ns NAME STATUS AGE @@ -36,7 +42,6 @@ kube-public Active 245d kube-system Active 245d oracle-database-operator-system Active 24h <<<< namespace to deploy the Oracle Database Operator - [root@test-server oracle-database-operator]# kubectl get all -n oracle-database-operator-system NAME READY STATUS RESTARTS AGE pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 0 28s @@ -54,7 +59,6 @@ NAME DESIRED replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 29s [root@docker-test-server oracle-database-operator]# - [root@test-server oracle-database-operator]# kubectl get crd NAME CREATED AT autonomouscontainerdatabases.database.oracle.com 2022-06-22T01:21:36Z @@ -75,23 +79,18 @@ shardingdatabases.database.oracle.com 2022-06-22T01:21:39Z singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z ``` -The following sections explain the setup and functionality of this controller. - -# Prerequsites to manage PDB Life Cycle using Oracle DB Operator Multitenant Database Controller +## Prerequsites to manage PDB Life Cycle using Oracle DB Operator Multitenant Database Controller -**CAUTION :** You must complete the following steps before managing the lifecycle of a PDB in a CDB using the Oracle DB Operator Multitenant Database Controller. - -* [Prepare CDB for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) +* [Prepare the container database for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) * [Oracle REST Data Service or ORDS Image](#oracle-rest-data-service-ords-image) * [Kubernetes Secrets](#kubernetes-secrets) * [Kubernetes CRD for CDB](#kubernetes-crd-for-cdb) * [Kubernetes CRD for PDB](#kubernetes-crd-for-pdb) +## Prepare the container database for PDB Lifecycle Management (PDB-LM) -+ ## Prepare CDB for PDB Lifecycle Management (PDB-LM) - -Pluggable Database (PDB) management operations are performed in the Container Database (CDB). These operations include create, clone, plug, unplug, delete, modify and map operations. +Pluggable Database (PDB) management operations are performed in the Container Database (CDB). These operations include create, clone, plug, unplug, delete, modify and map pdb. You cannot have an ORDS-enabled schema in the container database. To perform the PDB lifecycle management operations, you must first use the following steps to define the default CDB administrator credentials on target CDBs: @@ -117,29 +116,27 @@ col account_status for a30 select username, account_status from dba_users where username in ('ORDS_PUBLIC_USER','C##DBAPI_CDB_ADMIN','APEX_PUBLIC_USER','APEX_REST_PUBLIC_USER'); ``` -### Reference Setup: Example of a setup using OCI OKE(Kubernetes Cluster) and a CDB in Cloud (OCI Exadata Database Cluster) +## OCI OKE(Kubernetes Cluster) -See this [provisioning example setup](./provisioning/example_setup_using_oci_oke_cluster.md) for steps to configure a Kubernetes Cluster and a CDB. This example uses an OCI OKE Cluster as the Kubernetes Cluster and a CDB in OCI Exadata Database service. +You can use an [OKE in Oracle Cloud Infrastructure][okelink] to configure the operator for PDB lifecycle management. **Note that there is no restriction about container database location; it can be anywhere (on cloud or premises , on any supported platform).** +To quickly create an OKE cluster in your OCI cloud environment you can use the following [link](./provisioning/quickOKEcreation.md). +In this setup example [provisioning example setup](./provisioning/example_setup_using_oci_oke_cluster.md), the Container database is running on a OCI Exadata Database Cluster. -+ ## Oracle REST Data Service (ORDS) Image + +## Oracle REST Data Service (ORDS) Image - Oracle DB Operator Multitenant Database controller requires that the Oracle REST Data Services (ORDS) image for PDB Lifecycle Management is present in the target CDB. + The PDB Database controllers require a pod running a dedicated rest server image based on [ORDS][ordsdoc]. Read the following [link](./provisioning/ords_image.md) to build the ords images. - You can build this image by using the ORDS [Dockerfile](../../../ords/Dockerfile) - - - For the steps to build the ORDS Docker image, see [ORDS_image](./provisioning/ords_image.md) - -+ ## Kubernetes Secrets +## Kubernetes Secrets - Oracle DB Operator Multitenant Database Controller uses Kubernetes Secrets to store usernames and passwords that you must have to manage the lifecycle operations of a PDB in the target CDB. In addition, to use https protocol, all certificates need to be stored using Kubernetes Secret. + Multitenant Controllers use Kubernetes Secrets to store the required credential. The https certificates are stored in Kubernetes Secrets as well. **Note** In multi namespace enviroment you have to create specific secrets for each namespaces ### Secrets for CDB CRD - Create a secret file as shown here: [config/samples/multitenant/cdb_secret.yaml](../../config/samples/multitenant/cdb_secret.yaml). Modify this file with the `base64` encoded values of the required passwords for CDB, and use this file to create the required secrets. + Create a secret file as shown here: [config/samples/multitenant/cdb_secret.yaml](../multitenant/provisioning/singlenamespace/cdb_create.yaml). Modify this file with the `base64` encoded values of the required passwords for CDB, and use this file to create the required secrets. ```bash kubectl apply -f cdb_secret.yaml @@ -150,17 +147,16 @@ See this [provisioning example setup](./provisioning/example_setup_using_oci_oke ```bash echo -n "" | base64 ``` - The value that is returned is the base64-encoded value for that password string. **Note:** After successful creation of the CDB Resource, the CDB secrets are deleted from the Kubernetes system . ### Secrets for PDB CRD - Create a secret file as shown here: [config/samples/multitenant/pdb_secret.yaml](../../config/samples/multitenant/pdb_secret.yaml). Modify this file with the `base64` encoded values of the required passwords for PDB and use it to create the required secrets. + + Create a secret file as shown here: [pdb_secret.yaml](../multitenant/provisioning/singlenamespace/pdb_secret.yaml). Edit the file using your base64 credential and apply it. ```bash kubectl apply -f pdb_secret.yaml ``` - **NOTE:** To encode the password using `base64`, see the command example in the preceding **Secrets for CDB CRD** section. **NOTE:** Don't leave plaintext files containing sensitive data on disk. After loading the Secret, remove the plaintext file or move it to secure storage. @@ -182,43 +178,73 @@ kubectl create secret generic db-ca --from-file=ca.crt -n oracle-database-operat ``` image_not_found + **Note:** On successful creation of the certificates secret creation remove files or move to secure storage . -+ ## Kubernetes CRD for CDB +## Kubernetes CRD for CDB -The Oracle Database Operator Multitenant Controller creates the CDB kind as a custom resource that models a target CDB as a native Kubernetes object. This kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. These CDB resources can be scaled, based on the expected load, using replicas. Each CDB resource follows the CDB CRD as defined here: [config/crd/bases/database.oracle.com_cdbs.yaml](../../config/crd/bases/database.oracle.com_cdbs.yaml) +The Oracle Database Operator Multitenant Controller creates the CDB kind as a custom resource that models a target CDB as a native Kubernetes object. This kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. Each CDB resource follows the CDB CRD as defined here: [config/crd/bases/database.oracle.com_cdbs.yaml](../../config/crd/bases/database.oracle.com_cdbs.yaml) -To create a CDB CRD, see this example `.yaml` file: [config/samples/multitenant/cdb.yaml](../../config/samples/multitenant/cdb.yaml) +To create a CDB CRD, see this example `.yaml` file: [cdb_create.yaml](../multitenant/provisioning/singlenamespace/cdb_create.yaml) **Note:** The password and username fields in this *cdb.yaml* Yaml are the Kubernetes Secrets created earlier in this procedure. For more information, see the section [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). To understand more about creating secrets for pulling images from a Docker private registry, see [Kubernetes Private Registry Documenation]( https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). -1. [Use Case: Create a CDB CRD Resource](./provisioning/cdb_crd_resource.md) -2. [Use Case: Add another replica to an existing CDB CRD Resource](./provisioning/add_replica.md) +Create a CDB CRD Resource example +```bash +kubectl apply -f cdb_create.yaml +``` -+ ## Kubernetes CRD for PDB +see [usecase01][uc01] and usecase03[uc03] for more information about file configuration -The Oracle Database Operator Multitenant Controller creates the PDB kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. You cannot have more than one Kubernetes resource for a target PDB. This PDB resource can be used to perform PDB-LM operations by specifying the action attribute in the PDB Specs. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) +## Kubernetes CRD for PDB -To create a PDB CRD Resource, a sample .yaml file is available here: [config/samples/multitenant/pdb_create.yaml](../../config/samples/multitenant/pdb_create.yaml) +The Oracle Database Operator Multitenant Controller creates the PDB kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. You cannot have more than one Kubernetes resource for a target PDB. This PDB resource can be used to perform PDB-LM operations by specifying the action attribute in the PDB Specs. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) -# Use Cases for PDB Lifecycle Management Operations using Oracle DB Operator Multitenant Controller +To create a PDB CRD Resource, a sample .yaml file is available here: [pdb_create.yaml](../multitenant/provisioning/singlenamespace/pdb_create.yaml) -Using the Oracle DB Operator Multitenant Controller, you can perform the following PDB-LM operations: CREATE, CLONE, MODIFY, DELETE, UNPLUG, PLUG. +```bash +kubectl apply -f cdb_create.yaml +``` -1. [Create PDB](./provisioning/create_pdb.md) -2. [Clone PDB](./provisioning/clone_pdb.md) -3. [Modify PDB](./provisioning/modify_pdb.md) -4. [Delete PDB](./provisioning/delete_pdb.md) -5. [Unplug PDB](./provisioning/unplug_pdb.md) -6. [Plug PDB](./provisioning/plug_pdb.md) +## Usecases files list +### Single Namespace -## Validation and Errors +1. [Create CDB](./provisioning/singlenamespace/cdb_create.yaml) +2. [Create PDB](./provisioning/singlenamespace/pdb_create.yaml) +3. [Clone PDB](./provisioning/singlenamespace/pdb_clone.yaml) +4. [Open PDB](./provisioning/singlenamespace/pdb_open.yaml) +4. [Close PDB](./provisioning/singlenamespace/pdb_close.yaml) +5. [Delete PDB](./provisioning/singlenamespace/pdb_delete.yaml) +6. [Unplug PDB](./provisioning/singlenamespace/pdb_unplug.yaml) +7. [Plug PDB](./provisioning/singlenamespace/pdb_plug.yaml) -To see how to look for any validation errors, see [validation_error](./provisioning/validation_error.md). +### Multiple namespace (cdbnamespace,dbnamespace) +1. [Create CDB](./provisioning/multinamespace/cdb_create.yaml) +2. [Create PDB](./provisioning/multinamespace/pdb_create.yaml) +3. [Clone PDB](./provisioning/multinamespace/pdb_clone.yaml) +4. [Open PDB](./provisioning/multinamespace/pdb_open.yaml) +4. [Close PDB](./provisioning/multinamespace/pdb_close.yaml) +5. [Delete PDB](./provisioning/multinamespace/pdb_delete.yaml) +6. [Unplug PDB](./provisioning/multinamespace/pdb_unplug.yaml) ## Known issues -To find out about known issue related to Oracle DB Operator Multitenant Controller, see [known_issues](./provisioning/known_issues.md). + - Ords installatian failure if pluaggable databases in the container db are not opened + + - Version 1.1.0: encoded password for https authentication may include carriege return as consequence the https request fails with http 404 error. W/A generate encoded password using **printf** instead of **echo**. + + - pdb controller authentication suddenly failes without any system change. Check the certificate expiration date **openssl .... -days 365** + + - Nothing happens after cdb yaml file applying: Make sure to have properly configure the WHATCH_NAMESPACE list in the operator yaml file + + [okelink]:https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm + [ordsdoc]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.1/index.html + [uc01]:../multitenant/usecase01/README.md + [uc02]:../multitenant/usecase02/README.md + [uc03]:../multitenant/usecase03/README.md + [oradocpdb]:https://docs.oracle.com/en/database/oracle/oracle-database/21/multi/introduction-to-the-multitenant-architecture.html#GUID-AB84D6C9-4BBE-4D36-992F-2BB85739329F + + \ No newline at end of file diff --git a/docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md b/docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md new file mode 100644 index 00000000..d56efacb --- /dev/null +++ b/docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md @@ -0,0 +1,38 @@ +# Example of a working setup using OCI OKE(Kubernetes Cluster) and a CDB in Cloud (OCI Exadata Database Cluster) + +In this example, the target CDB (for which the PDB life cycle management is needed) is running in a Cloud environment (OCI's [Oracle Exadata Database Service](https://docs.oracle.com/en-us/iaas/exadatacloud/index.html)) and to manage its PDBs, the Oracle DB Operator is running on a Kubernetes Cluster running in cloud (OCI's [Container Engine for Kubernetes or OKE](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm#Overview_of_Container_Engine_for_Kubernetes)). + + +## High Level plans for this setup + +Below are the main steps that will be involved in this setup: + +- Setup VCN, Add security lists +- Setup OKE cluster with custom settings +- Install Oracle Database Operator on OKE Cluster +- Install ords controller definition +- Manager pdb life cycle management. + + +## OKE Cluster + +Check the [Oracle Documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengnetworkconfigexample.htm#example-privatek8sapi-privateworkers-publiclb) for the OKE rules settings. + +Create OKE cluster with CUSTOM option to use same VCN where ExaCS is provisioned. + +**NOTE:** Make sure you choose same VCN exaphxvcn where ExaCS is provisioned. + +After this, setup kubeconfig & validate cluster access as well as worker node access via ssh. + +For example, you should be able to check the available OKE nodes using "kubectl" as below: + +``` +% kubectl get nodes -o wide +NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME +192.168.194.163 Ready node 3d19h v1.23.4 192.168.194.163 XX.XX.XX.XX Oracle Linux Server 7.9 5.4.17-2136.306.1.3.el7uek.x86_64 cri-o://1.23.2 +192.168.194.169 Ready node 3d19h v1.23.4 192.168.194.169 XX.XX.XX.XX Oracle Linux Server 7.9 5.4.17-2136.306.1.3.el7uek.x86_64 cri-o://1.23.2 +192.168.194.241 Ready node 3d19h v1.23.4 192.168.194.241 XX.XX.XX.XX Oracle Linux Server 7.9 5.4.17-2136.306.1.3.el7uek.x86_64 cri-o://1.23.2 +``` + +Once this setup is ready, you can proceed with the installation of [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) to use the Oracle On-prem controller to manage PDBs in this CDB. + diff --git a/docs/multitenant/provisioning/multinamespace/cdb_create.yaml b/docs/multitenant/provisioning/multinamespace/cdb_create.yaml new file mode 100644 index 00000000..d3b5e04f --- /dev/null +++ b/docs/multitenant/provisioning/multinamespace/cdb_create.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: CDB +metadata: + name: cdb-dev + namespace: cdbnamespace +spec: + cdbName: "DB12" + ordsImage: ".............your registry............./ords-dboper:latest" + ordsImagePullPolicy: "Always" + dbTnsurl : "...Container tns alias....." + replicas: 1 + sysAdminPwd: + secret: + secretName: "cdb1-secret" + key: "sysadmin_pwd" + ordsPwd: + secret: + secretName: "cdb1-secret" + key: "ords_pwd" + cdbAdminUser: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_user" + cdbAdminPwd: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_pwd" + webServerUser: + secret: + secretName: "cdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "cdb1-secret" + key: "webserver_pwd" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + diff --git a/docs/multitenant/provisioning/multinamespace/pdb_clone.yaml b/docs/multitenant/provisioning/multinamespace/pdb_clone.yaml new file mode 100644 index 00000000..b88fb71b --- /dev/null +++ b/docs/multitenant/provisioning/multinamespace/pdb_clone.yaml @@ -0,0 +1,50 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdb2_clone" + srcPdbName: "pdbdev" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + action: "Clone" diff --git a/docs/multitenant/provisioning/multinamespace/pdb_close.yaml b/docs/multitenant/provisioning/multinamespace/pdb_close.yaml new file mode 100644 index 00000000..a823f5d9 --- /dev/null +++ b/docs/multitenant/provisioning/multinamespace/pdb_close.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + diff --git a/docs/multitenant/provisioning/multinamespace/pdb_create.yaml b/docs/multitenant/provisioning/multinamespace/pdb_create.yaml new file mode 100644 index 00000000..200f3712 --- /dev/null +++ b/docs/multitenant/provisioning/multinamespace/pdb_create.yaml @@ -0,0 +1,46 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + fileNameConversions: "NONE" + tdeImport: false + totalSize: "1G" + tempSize: "100M" + action: "Create" + diff --git a/docs/multitenant/provisioning/multinamespace/pdb_delete.yaml b/docs/multitenant/provisioning/multinamespace/pdb_delete.yaml new file mode 100644 index 00000000..282885b0 --- /dev/null +++ b/docs/multitenant/provisioning/multinamespace/pdb_delete.yaml @@ -0,0 +1,34 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + diff --git a/docs/multitenant/provisioning/multinamespace/pdb_open.yaml b/docs/multitenant/provisioning/multinamespace/pdb_open.yaml new file mode 100644 index 00000000..85fb2ce4 --- /dev/null +++ b/docs/multitenant/provisioning/multinamespace/pdb_open.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" diff --git a/docs/multitenant/provisioning/multinamespace/pdb_plug.yaml b/docs/multitenant/provisioning/multinamespace/pdb_plug.yaml new file mode 100644 index 00000000..d9135f13 --- /dev/null +++ b/docs/multitenant/provisioning/multinamespace/pdb_plug.yaml @@ -0,0 +1,46 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + fileNameConversions: "NONE" + sourceFileNameConversions: "NONE" + copyAction: "MOVE" + totalSize: "1G" + tempSize: "100M" + action: "Plug" + assertivePdbDeletion: true + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + + diff --git a/docs/multitenant/provisioning/multinamespace/pdb_unplug.yaml b/docs/multitenant/provisioning/multinamespace/pdb_unplug.yaml new file mode 100644 index 00000000..f3667dad --- /dev/null +++ b/docs/multitenant/provisioning/multinamespace/pdb_unplug.yaml @@ -0,0 +1,39 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "Unplug" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + diff --git a/docs/multitenant/provisioning/ords_image.md b/docs/multitenant/provisioning/ords_image.md new file mode 100644 index 00000000..e2d1dcef --- /dev/null +++ b/docs/multitenant/provisioning/ords_image.md @@ -0,0 +1,81 @@ + + +# Build ORDS Docker Image + +This file contains the steps to create an ORDS based image to be used solely by the PDB life cycle multitentant controllers. + +**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. + +#### Clone the software using git: + +> Under directory ./oracle-database-operator/ords you will find the [Dockerfile](../../../ords/Dockerfile) and [runOrdsSSL.sh](../../../ords/runOrdsSSL.sh) required to build the image. + +```sh + git clone git@orahub.oci.oraclecorp.com:rac-docker-dev/oracle-database-operator.git + cd oracle-database-operator/ords/ +``` + +#### Login to the registry: container-registry.oracle.com + +**NOTE:** To login to this registry, you will need to the URL https://container-registry.oracle.com , Sign in, then click on "Java" and then accept the agreement. + +```bash +docker login container-registry.oracle.com +``` + +#### Login to the your container registry + +Login to a repo where you want to push your docker image (if needed) to pull during deployment in your environment. + +```bash +docker login +``` + +#### Build the image + +Build the docker image by using below command: + +```bash +docker build -t oracle/ords-dboper:latest . +``` +> If your are working behind a proxy mind to specify https_proxy and http_proxy during image creation + +Check the docker image details using: + +```bash +docker images +``` + +> OUTPUT EXAMPLE +```bash +REPOSITORY TAG IMAGE ID CREATED SIZE +oracle/ords-dboper latest fdb17aa242f8 4 hours ago 1.46GB + +``` + +#### Tag and push the image + +Tag and push the image to your image repository. + +NOTE: We have the repo as `phx.ocir.io//oracle/ords:latest`. Please change as per your environment. + +```bash +docker tag oracle/ords-dboper:ords-latest phx.ocir.io//oracle/ords:latest +docker push phx.ocir.io//oracle/ords:latest +``` + +#### In case of private image + +If you the image not be public then yuo need to create a secret containing the password of your image repository. +Create a Kubernetes Secret for your docker repository to pull the image during deployment using the below command: + +```bash +kubectl create secret generic container-registry-secret --from-file=.dockerconfigjson=./.docker/config.json --type=kubernetes.io/dockerconfigjson -n oracle-database-operator-system +``` + +Use the parameter `ordsImagePullSecret` to specify the container secrets in pod creation yaml file + +#### [Image createion example](../usecase01/logfiles/BuildImage.log) + + + diff --git a/docs/multitenant/provisioning/quickOKEcreation.md b/docs/multitenant/provisioning/quickOKEcreation.md new file mode 100644 index 00000000..19d9323e --- /dev/null +++ b/docs/multitenant/provisioning/quickOKEcreation.md @@ -0,0 +1,136 @@ + + +### Quick Oke creation script + +Use this script to create quickly an OKE cluster in your OCI. + +#### Prerequisties: +- ocicli is properly configured on your client +- make is installed on your client +- vnc is already configured +- ssh key is configured (public key available under directory ~/.ssh) +- edit make providing all the information about your compartment, vnc,subnet,lb subnet and nd subnet (exported variables in the header section) + + +#### Execution: + +```bash +make all +``` + +Monitor the OKE from OCI console + +#### Makefile +```makefile +.EXPORT_ALL_VARIABLES: + +export CMPID=[.... COMPARTMENT ID.............] +export VNCID=[.... VNC ID ....................] +export ENDID=[.... SUBNET END POINT ID .......] +export LBSID=[.....LB SUBNET ID...............] +export NDSID=[.....NODE SUBNET ID.............] + + +#ssh public key +export KEYFL=~/.ssh/id_rsa.pub + +#cluster version +export KSVER=v1.27.2 + +#cluster name +export CLUNM=myoke + +#pool name +export PLNAM=Pool1 + +#logfile +export LOGFILE=./clustoke.log + +#shape +export SHAPE=VM.Standard.E4.Flex + +OCI=/home/oracle/bin/oci +CUT=/usr/bin/cut +KUBECTL=/usr/bin/kubectl +CAT=/usr/bin/cat + +all: cluster waitcluster pool waitpool config desccluster + +cluster: + @echo " - CREATING CLUSTER " + @$(OCI) ce cluster create \ + --compartment-id $(CMPID) \ + --kubernetes-version $(KSVER) \ + --name $(CLUNM) \ + --vcn-id $(VNCID) \ + --endpoint-subnet-id $(ENDID) \ + --service-lb-subnet-ids '["'$(LBSID)'"]' \ + --endpoint-public-ip-enabled true \ + --persistent-volume-freeform-tags '{"$(CLUNM)" : "OKE"}' 1>$(LOGFILE) 2>&1 + +waitcluster: + @while [ `$(OCI) ce cluster list --compartment-id $(CMPID) \ + --name $(CLUNM) --lifecycle-state ACTIVE --query data[0].id \ + --raw-output |wc -l ` -eq 0 ] ; do sleep 5 ; done + @echo " - CLUSTER CREATED" + + +pool: + @echo " - CREATING POOL" + @$(eval PBKEY :=$(shell $(CAT) $(KEYFL)|grep -v " PUBLIC KEY")) + @$(OCI) ce node-pool create \ + --cluster-id `$(OCI) ce cluster list --compartment-id $(CMPID) \ + --name $(CLUNM) --lifecycle-state ACTIVE --query data[0].id --raw-output` \ + --compartment-id $(CMPID) \ + --kubernetes-version $(KSVER) \ + --name $(PLNAM) \ + --node-shape $(SHAPE) \ + --node-shape-config '{"memoryInGBs": 8.0, "ocpus": 1.0}' \ + --node-image-id `$(OCI) compute image list \ + --operating-system 'Oracle Linux' --operating-system-version 7.9 \ + --sort-by TIMECREATED --compartment-id $(CMPID) --shape $(SHAPE) \ + --query data[1].id --raw-output` \ + --node-boot-volume-size-in-gbs 50 \ + --ssh-public-key "$(PBKEY)" \ + --size 3 \ + --placement-configs '[{"availabilityDomain": "'`oci iam availability-domain list \ + --compartment-id $(CMPID) \ + --query data[0].name --raw-output`'", "subnetId": "'$(NDSID)'"}]' 1>>$(LOGFILE) 2>&1 + +waitpool: + $(eval CLSID :=$(shell $(OCI) ce cluster list --compartment-id $(CMPID) \ + --name $(CLUNM) --lifecycle-state ACTIVE --query data[0].id --raw-output)) + @while [ `$(OCI) ce node-pool list --compartment-id $(CMPID) \ + --lifecycle-state ACTIVE --cluster-id $(CLSID) \ + --query data[0].id --raw-output |wc -l ` -eq 0 ] ; do sleep 5 ; done + @sleep 10 + $(eval PLLID :=$(shell $(OCI) ce node-pool list --compartment-id $(CMPID) \ + --lifecycle-state ACTIVE --cluster-id $(CLSID) --query data[0].id --raw-output)) + @echo " - POOL CREATED" + +config: + @$(OCI) ce cluster create-kubeconfig --cluster-id \ + `$(OCI) ce cluster list \ + --compartment-id $(CMPID) --name $(CLUNM) --lifecycle-state ACTIVE \ + --query data[0].id --raw-output` \ + --file $(HOME)/.kube/config --region \ + `$(OCI) ce cluster list \ + --compartment-id $(CMPID) --name $(CLUNM) --lifecycle-state ACTIVE \ + --query data[0].id --raw-output|$(CUT) -f4 -d. ` \ + --token-version 2.0.0 --kube-endpoint PUBLIC_ENDPOINT + @echo " - KUBECTL PUBLIC ENDPOINT CONFIGURED" + + +desccluster: + @$(eval TMPSP := $(shell date "+%y/%m/%d:%H:%M" )) + $(KUBECTL) get nodes -o wide + $(KUBECTL) get storageclass + +checkvol: + $(OCI) bv volume list \ + --compartment-id $(CMPID) \ + --lifecycle-state AVAILABLE \ + --query 'data[?"freeform-tags".stackgres == '\''OKE'\''].id' +``` + + diff --git a/docs/multitenant/provisioning/singlenamespace/cdb_create.yaml b/docs/multitenant/provisioning/singlenamespace/cdb_create.yaml new file mode 100644 index 00000000..01fc0a18 --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/cdb_create.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: CDB +metadata: + name: cdb-dev + namespace: oracle-database-operator-system +spec: + cdbName: "DB12" + ordsImage: ".............your registry............./ords-dboper:latest" + ordsImagePullPolicy: "Always" + dbTnsurl : "...Container tns alias....." + replicas: 1 + sysAdminPwd: + secret: + secretName: "cdb1-secret" + key: "sysadmin_pwd" + ordsPwd: + secret: + secretName: "cdb1-secret" + key: "ords_pwd" + cdbAdminUser: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_user" + cdbAdminPwd: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_pwd" + webServerUser: + secret: + secretName: "cdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "cdb1-secret" + key: "webserver_pwd" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + diff --git a/docs/multitenant/provisioning/singlenamespace/cdb_secret.yaml b/docs/multitenant/provisioning/singlenamespace/cdb_secret.yaml new file mode 100644 index 00000000..567b90a4 --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/cdb_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: cdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + ords_pwd: ".....base64 encoded password...." + sysadmin_pwd: ".....base64 encoded password...." + cdbadmin_user: ".....base64 encoded password...." + cdbadmin_pwd: ".....base64 encoded password...." + webserver_user: ".....base64 encoded password...." + webserver_pwd: ".....base64 encoded password...." diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_clone.yaml b/docs/multitenant/provisioning/singlenamespace/pdb_clone.yaml new file mode 100644 index 00000000..0ecc3c70 --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/pdb_clone.yaml @@ -0,0 +1,50 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb2 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdb2_clone" + srcPdbName: "pdbdev" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + action: "Clone" diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_close.yaml b/docs/multitenant/provisioning/singlenamespace/pdb_close.yaml new file mode 100644 index 00000000..5917d33a --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/pdb_close.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_create.yaml b/docs/multitenant/provisioning/singlenamespace/pdb_create.yaml new file mode 100644 index 00000000..be3581ad --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/pdb_create.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + fileNameConversions: "NONE" + tdeImport: false + totalSize: "1G" + tempSize: "100M" + action: "Create" + assertivePdbDeletion: true + diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_delete.yaml b/docs/multitenant/provisioning/singlenamespace/pdb_delete.yaml new file mode 100644 index 00000000..c22b546a --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/pdb_delete.yaml @@ -0,0 +1,34 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_open.yaml b/docs/multitenant/provisioning/singlenamespace/pdb_open.yaml new file mode 100644 index 00000000..25fdccc4 --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/pdb_open.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_plug.yaml b/docs/multitenant/provisioning/singlenamespace/pdb_plug.yaml new file mode 100644 index 00000000..77c00b9c --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/pdb_plug.yaml @@ -0,0 +1,46 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + fileNameConversions: "NONE" + sourceFileNameConversions: "NONE" + copyAction: "MOVE" + totalSize: "1G" + tempSize: "100M" + action: "Plug" + assertivePdbDeletion: true + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + + diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_secret.yaml b/docs/multitenant/provisioning/singlenamespace/pdb_secret.yaml new file mode 100644 index 00000000..60d95d76 --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/pdb_secret.yaml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: pdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + sysadmin_user: ".....base64 encoded password...." + sysadmin_pwd: ".....base64 encoded password...." + webserver_user: ".....base64 encoded password...." + webserver_pwd: ".....base64 encoded password...." + diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_unplug.yaml b/docs/multitenant/provisioning/singlenamespace/pdb_unplug.yaml new file mode 100644 index 00000000..085d337e --- /dev/null +++ b/docs/multitenant/provisioning/singlenamespace/pdb_unplug.yaml @@ -0,0 +1,39 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "Unplug" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + diff --git a/docs/multitenant/usecase01/README.md b/docs/multitenant/usecase01/README.md index e8109110..7352257e 100644 --- a/docs/multitenant/usecase01/README.md +++ b/docs/multitenant/usecase01/README.md @@ -3,20 +3,23 @@ # STEP BY STEP USE CASE -- [INTRODUCTION](#introduction) -- [OPERATION STEPS ](#operation-steps) -- [Download latest version from github ](#download-latest-version-from-orahub-a-namedownloada) -- [Upload webhook certificates](#upload-webhook-certificates-a-namewebhooka) -- [Create the dboperator](#create-the-dboperator-a-namedboperatora) -- [Create Secret for container registry](#create-secret-for-container-registry) -- [Build ords immage ](#build-ords-immage-a-nameordsimagea) -- [Database Configuration](#database-configuration) -- [Create CDB secret ](#create-cdb-secret) -- [Create Certificates](#create-certificates) -- [Apply cdb.yaml](#apply-cdbyaml) -- [Logs and throuble shutting](#cdb---logs-and-throuble-shutting) -- [Create PDB secret](#create-pdb-secret) -- [Other action ](#other-actions) +- [STEP BY STEP USE CASE](#step-by-step-use-case) + - [INTRODUCTION](#introduction) + - [OPERATIONAL STEPS](#operational-steps) + - [Download latest version from github ](#download-latest-version-from-github-) + - [Upload webhook certificates ](#upload-webhook-certificates-) + - [Create the dboperator ](#create-the-dboperator-) + - [Create secret for container registry](#create-secret-for-container-registry) + - [Build ords immage ](#build-ords-immage-) + - [Database Configuration](#database-configuration) + - [Create CDB secret](#create-cdb-secret) + - [Create Certificates](#create-certificates) + - [Apply cdb.yaml](#apply-cdbyaml) + - [CDB - Logs and throuble shutting](#cdb---logs-and-throuble-shutting) + - [Create PDB secret](#create-pdb-secret) + - [Apply pdb yaml file to create pdb](#apply-pdb-yaml-file-to-create-pdb) + - [Other actions](#other-actions) + - [Imperative approach on pdb deletion - will be avilable in 1.2.0 ](#imperative-approach-on-pdb-deletion) @@ -47,6 +50,7 @@ The following table reports the parameters required to configure and use oracle | pdbTlsKey | | [standalone.https.cert.key][key] | | pdbTlsCrt | | [standalone.https.cert][cr] | | pdbTlsCat | | certificate authority | +| assertivePdbDeletion | boolean | [turn on imperative approach on crd deleteion][imperative] | > A [makfile](./makefile) is available to sped up the command execution for the multitenant setup and test. See the comments in the header of file @@ -78,6 +82,7 @@ make operator-yaml IMG=operator:latest > **NOTE:** If you are using oracle-container-registry make sure to accept the license agreement otherwise the operator image pull fails. ---- + #### Upload webhook certificates ```bash @@ -101,6 +106,7 @@ oracle-database-operator-controller-manager-557ff6c659-xpswv 1/1 Running ``` ---- + #### Create secret for container registry + Make sure to login to your container registry and then create the secret for you container registry. @@ -119,6 +125,7 @@ container-registry-secret kubernetes.io/dockerconfigjson 1 19s webhook-server-cert kubernetes.io/tls ``` ---- + #### Build ords immage + Build the ords image, downloading ords software is no longer needed; just build the image and push it to your repository @@ -128,16 +135,17 @@ cd oracle-database-operator/ords docker build -t oracle/ords-dboper:latest . ``` -[example of execution](./BuildImage.log) +[Example of execution](./logfiles/BuildImage.log) + Login to your container registry and push the ords image. ```bash docker tag /ords-dboper:latest docker push /ords-dboper:latest ``` -[example of execution](./ImagePush.log) +[Example of execution](./logfiles/tagandpush.log) ---- + #### Database Configuration + Configure Database @@ -153,6 +161,7 @@ GRANT SYSDBA TO CONTAINER = ALL; GRANT CREATE SESSION TO CONTAINER = ALL; ``` ---- + #### Create CDB secret + Create secret for CDB connection @@ -208,6 +217,7 @@ webhook-server-cert kubernetes.io/tls 3 4m55s >**TIPS:** Use the following commands to analyze contents of an existing secret ```bash kubectl get secret -o yaml -n ``` ---- + #### Create Certificates + Create certificates: At this stage we need to create certificates on our local machine and upload into kubernetes cluster by creating new secrets. @@ -258,10 +268,11 @@ kubectl create secret generic db-ca --from-file= -n oracle-database-op ``` -[example of execution:](./openssl_execution.log) +[Example of execution:](./logfiles/openssl_execution.log) ---- + #### Apply cdb.yaml @@ -272,56 +283,54 @@ kubectl create secret generic db-ca --from-file= -n oracle-database-op + Create ords container ```bash -/usr/bin/kubectl apply -f cdb.yaml -n oracle-database-operator-system +/usr/bin/kubectl apply -f cdb_create.yaml -n oracle-database-operator-system ``` -Example: **cdb.yaml** +Example: **cdb_create.yaml** ```yaml apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: +kind: CDB +metadata: + name: cdb-dev namespace: oracle-database-operator-system spec: - cdbName: "" - dbServer: "" or - dbPort: - ordsImage: "/ords-dboper:.latest" + cdbName: "DB12" + ordsImage: ".............your registry............./ords-dboper:latest" ordsImagePullPolicy: "Always" - serviceName: + dbTnsurl : "...Container tns alias....." replicas: 1 - sysAdminPwd: - secret: + sysAdminPwd: + secret: secretName: "cdb1-secret" key: "sysadmin_pwd" ordsPwd: - secret: + secret: secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: + key: "ords_pwd" + cdbAdminUser: + secret: secretName: "cdb1-secret" key: "cdbadmin_user" - cdbAdminPwd: - secret: + cdbAdminPwd: + secret: secretName: "cdb1-secret" key: "cdbadmin_pwd" - webServerUser: - secret: + webServerUser: + secret: secretName: "cdb1-secret" key: "webserver_user" - webServerPwd: - secret: + webServerPwd: + secret: secretName: "cdb1-secret" - key: "webserver_pwd" + key: "webserver_pwd" cdbTlsKey: secret: secretName: "db-tls" - key: "" + key: "tls.key" cdbTlsCrt: secret: secretName: "db-tls" - key: ":" + key: "tls.crt" ``` > **Note** if you are working in dataguard environment with multiple sites (AC/DR) specifying the host name (dbServer/dbPort/serviceName) may not be the suitable solution for this kind of configuration, use **dbTnsurl** instead. Specify the whole tns string which includes the hosts/scan list. @@ -342,9 +351,11 @@ spec: dbtnsurl:((DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(TRANS...... ``` -[example of cdb.yaml](./cdb.yaml) +[Example of cdb.yaml](./cdb_create.yaml) + ---- + #### CDB - Logs and throuble shutting + Check the status of ords container @@ -379,14 +390,14 @@ NAME CDB NAME DB SERVER DB PORT REPLICAS STATUS MESSAG ```bash /usr/bin/kubectl logs `/usr/bin/kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system ``` -[example of execution](./cdb.log) +[Example of cdb creation log](./logfiles/cdb_creation.log) + Test REST API from the pod. By querying the metadata catalog you can verify the status of https setting ```bash /usr/bin/kubectl exec -it `/usr/bin/kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system -i -t -- /usr/bin/curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ ``` -[example of execution](./testapi.log) +[Example of execution](./logfiles/testapi.log) + Verify the pod environment varaibles ```bash @@ -403,9 +414,10 @@ NAME CDB NAME DB SERVER DB PORT REPLICAS STATUS MESSAG ```bash /usr/bin/kubectl exec -it `/usr/bin/kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system -i -t -- /usr/local/bin/ords --config /etc/ords/config config list ``` -[Example of executions](./ordsconfig.log) +[Example of executions](./logfiles/ordsconfig.log) ----- + #### Create PDB secret @@ -439,30 +451,32 @@ pdb1-secret Opaque 2 79m <--- webhook-server-cert kubernetes.io/tls 3 79m ``` --- + #### Apply pdb yaml file to create pdb ```bash /usr/bin/kubectl apply -f pdb.yaml -n oracle-database-operator-system ``` -Example: **pdb.yaml** +Example: **pdb_create.yaml** ```yaml apiVersion: database.oracle.com/v1alpha1 kind: PDB metadata: - name: + name: pdb1 namespace: oracle-database-operator-system labels: - cdb: + cdb: cdb-dev spec: - cdbResName: "" - cdbName: "" - pdbName: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" adminName: secret: secretName: "pdb1-secret" - key: "sysadmin_user" + key: "sysadmin_user" adminPwd: secret: secretName: "pdb1-secret" @@ -470,19 +484,29 @@ spec: pdbTlsKey: secret: secretName: "db-tls" - key: "" + key: "tls.key" pdbTlsCrt: secret: secretName: "db-tls" - key: "" + key: "tls.crt" pdbTlsCat: secret: secretName: "db-ca" - key: "" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" fileNameConversions: "NONE" + tdeImport: false totalSize: "1G" tempSize: "100M" action: "Create" + assertivePdbDeletion: true ``` + Monitor the pdb creation status until message is success @@ -529,13 +553,17 @@ kubectl logs -f $(kubectl get pods -n oracle-database-operator-system|grep oracl ``` --- + #### Other actions -Configure and use other yaml files to perform pluggable database life cycle managment action **modify_pdb_open.yaml** **modify_pdb_close.yaml** +Configure and use other yaml files to perform pluggable database life cycle managment action **pdb_open.yaml** **pdb_close.yaml** -> **Note** sql command *"alter pluggable database open instances=all;"* acts only on closed databases, so you want get any oracle error in case of execution against an pluggable database already opened +> **Note** sql command *"alter pluggable database open instances=all;"* acts only on closed databases, so you don't get any oracle error in case of execution against an pluggable database already opened +#### Imperative approach on pdb deletion +If **assertivePdbDeletion** is true then the command execution **kubectl delete pdbs crd_pdb_name** automatically deletes the pluggable database on the container database. By default this option is disabled. You can use this option during **create**,**map**,**plug** and **clone** operation. If the option is disabled then **kubectl delete** only deletes the crd but not the pluggable on the container db. Database deletion uses the option **including datafiles**. +If you drop the CRD without dropping the pluggable database and you need to recreate the CRD then you can use the [pdb_map.yaml](./pdb_map.yaml) [1]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation @@ -558,4 +586,8 @@ Configure and use other yaml files to perform pluggable database life cycle mana [http]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-BEECC057-A8F5-4EAB-B88E-9828C2809CD8:~:text=Example%3A%20delete%20%5B%2D%2Dglobal%5D-,user%20add,-Add%20a%20user -[dbtnsurl]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22 \ No newline at end of file +[dbtnsurl]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22 + +[imperative]:https://kubernetes.io/docs/concepts/overview/working-with-objects/object-management/ + + diff --git a/docs/multitenant/usecase01/logfiles/BuildImage.log b/docs/multitenant/usecase01/logfiles/BuildImage.log index 4ee2fa05..f35c66d8 100644 --- a/docs/multitenant/usecase01/logfiles/BuildImage.log +++ b/docs/multitenant/usecase01/logfiles/BuildImage.log @@ -1,487 +1,896 @@ -/usr/bin/docker build -t oracle/ords-dboper:latest . -Sending build context to Docker daemon 92.38MB -Step 1/10 : FROM container-registry.oracle.com/java/jdk:latest -Trying to pull repository container-registry.oracle.com/java/jdk ... -latest: Pulling from container-registry.oracle.com/java/jdk -7cb069903b8a: Pull complete -a98ca67f4239: Pull complete -1b4060d1d804: Pull complete -Digest: sha256:8e7161bbd6a3a3beb77ee6f2d80c17ae4c80d88e0f5af667a19a0271c33f1b5e -Status: Downloaded newer image for container-registry.oracle.com/java/jdk:latest - ---> ad9ff1bbe92a -Step 2/10 : ENV ORDS_HOME=/opt/oracle/ords/ RUN_FILE="runOrdsSSL.sh" - ---> Running in e6f76deab66e -Removing intermediate container e6f76deab66e - ---> 0b26c489e4fd -Step 3/10 : COPY $RUN_FILE $ORDS_HOME - ---> ee472155adab -Step 4/10 : RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps && yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && yum -y install java-11-openjdk-devel && yum -y install ords && yum -y install iproute && yum clean all - ---> Running in d38a69d2cc70 -Oracle Linux 8 BaseOS Latest (x86_64) 105 MB/s | 50 MB 00:00 -Oracle Linux 8 Application Stream (x86_64) 90 MB/s | 38 MB 00:00 -Last metadata expiration check: 0:00:07 ago on Mon 10 Oct 2022 04:06:15 PM UTC. -Package yum-utils-4.0.21-11.0.1.el8.noarch is already installed. -Package tar-2:1.30-5.el8.x86_64 is already installed. +/usr/bin/docker build -t oracle/ords-dboper:latest ../../../ords +Sending build context to Docker daemon 13.82kB +Step 1/12 : FROM container-registry.oracle.com/java/jdk:latest + ---> b8457e2f0b73 +Step 2/12 : ENV ORDS_HOME=/opt/oracle/ords/ RUN_FILE="runOrdsSSL.sh" ORDSVERSION=23.4.0-8 + ---> Using cache + ---> 3317a16cd6f8 +Step 3/12 : COPY $RUN_FILE $ORDS_HOME + ---> 7995edec33cc +Step 4/12 : RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && yum -y install java-11-openjdk-devel && yum -y install iproute && yum clean all + ---> Running in fe168b01f3ad +Oracle Linux 8 BaseOS Latest (x86_64) 91 MB/s | 79 MB 00:00 +Oracle Linux 8 Application Stream (x86_64) 69 MB/s | 62 MB 00:00 +Last metadata expiration check: 0:00:12 ago on Tue 20 Aug 2024 08:54:50 AM UTC. +Package yum-utils-4.0.21-23.0.1.el8.noarch is already installed. +Package tar-2:1.30-9.el8.x86_64 is already installed. Package vim-minimal-2:8.0.1763-19.0.1.el8_6.4.x86_64 is already installed. -Package procps-ng-3.3.15-6.0.1.el8.x86_64 is already installed. +Package procps-ng-3.3.15-14.0.1.el8.x86_64 is already installed. +Package curl-7.61.1-33.el8_9.5.x86_64 is already installed. Dependencies resolved. ================================================================================ - Package Arch Version Repository Size + Package Arch Version Repository Size ================================================================================ Installing: - bind-utils x86_64 32:9.11.36-3.el8_6.1 ol8_appstream 452 k - expect x86_64 5.45.4-5.el8 ol8_baseos_latest 266 k - hostname x86_64 3.20-6.el8 ol8_baseos_latest 32 k - net-tools x86_64 2.0-0.52.20160912git.el8 ol8_baseos_latest 322 k - openssl x86_64 1:1.1.1k-7.el8_6 ol8_baseos_latest 709 k - sudo x86_64 1.8.29-8.el8 ol8_baseos_latest 925 k - tree x86_64 1.7.0-15.el8 ol8_baseos_latest 59 k - unzip x86_64 6.0-46.0.1.el8 ol8_baseos_latest 196 k - wget x86_64 1.19.5-10.0.1.el8 ol8_appstream 734 k - which x86_64 2.21-17.el8 ol8_baseos_latest 49 k - zip x86_64 3.0-23.el8 ol8_baseos_latest 270 k + bind-utils x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 453 k + expect x86_64 5.45.4-5.el8 ol8_baseos_latest 266 k + hostname x86_64 3.20-6.el8 ol8_baseos_latest 32 k + lsof x86_64 4.93.2-1.el8 ol8_baseos_latest 253 k + net-tools x86_64 2.0-0.52.20160912git.el8 ol8_baseos_latest 322 k + openssl x86_64 1:1.1.1k-12.el8_9 ol8_baseos_latest 710 k + sudo x86_64 1.9.5p2-1.el8_9 ol8_baseos_latest 1.0 M + tree x86_64 1.7.0-15.el8 ol8_baseos_latest 59 k + unzip x86_64 6.0-46.0.1.el8 ol8_baseos_latest 196 k + wget x86_64 1.19.5-12.0.1.el8_10 ol8_appstream 733 k + which x86_64 2.21-20.el8 ol8_baseos_latest 50 k + zip x86_64 3.0-23.el8 ol8_baseos_latest 270 k +Upgrading: + curl x86_64 7.61.1-34.el8 ol8_baseos_latest 352 k + dnf-plugins-core noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 76 k + libcurl x86_64 7.61.1-34.el8 ol8_baseos_latest 303 k + python3-dnf-plugins-core + noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 263 k + yum-utils noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 75 k Installing dependencies: - bind-libs x86_64 32:9.11.36-3.el8_6.1 ol8_appstream 175 k - bind-libs-lite x86_64 32:9.11.36-3.el8_6.1 ol8_appstream 1.2 M - bind-license noarch 32:9.11.36-3.el8_6.1 ol8_appstream 103 k - fstrm x86_64 0.6.1-2.el8 ol8_appstream 29 k - libmaxminddb x86_64 1.2.0-10.el8 ol8_appstream 33 k - libmetalink x86_64 0.1.3-7.el8 ol8_baseos_latest 32 k - protobuf-c x86_64 1.3.0-6.el8 ol8_appstream 37 k - python3-bind noarch 32:9.11.36-3.el8_6.1 ol8_appstream 150 k - python3-ply noarch 3.9-9.el8 ol8_baseos_latest 111 k - tcl x86_64 1:8.6.8-2.el8 ol8_baseos_latest 1.1 M + bind-libs x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 176 k + bind-libs-lite x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 1.2 M + bind-license noarch 32:9.11.36-16.el8_10.2 ol8_appstream 104 k + fstrm x86_64 0.6.1-3.el8 ol8_appstream 29 k + libmaxminddb x86_64 1.2.0-10.el8_9.1 ol8_appstream 32 k + libmetalink x86_64 0.1.3-7.el8 ol8_baseos_latest 32 k + protobuf-c x86_64 1.3.0-8.el8 ol8_appstream 37 k + python3-bind noarch 32:9.11.36-16.el8_10.2 ol8_appstream 151 k + python3-ply noarch 3.9-9.el8 ol8_baseos_latest 111 k + tcl x86_64 1:8.6.8-2.el8 ol8_baseos_latest 1.1 M +Installing weak dependencies: + geolite2-city noarch 20180605-1.el8 ol8_appstream 19 M + geolite2-country noarch 20180605-1.el8 ol8_appstream 1.0 M Transaction Summary ================================================================================ -Install 21 Packages +Install 24 Packages +Upgrade 5 Packages -Total download size: 6.9 M -Installed size: 20 M +Total download size: 28 M Downloading Packages: -(1/21): hostname-3.20-6.el8.x86_64.rpm 555 kB/s | 32 kB 00:00 -(2/21): libmetalink-0.1.3-7.el8.x86_64.rpm 492 kB/s | 32 kB 00:00 -(3/21): expect-5.45.4-5.el8.x86_64.rpm 3.2 MB/s | 266 kB 00:00 -(4/21): python3-ply-3.9-9.el8.noarch.rpm 5.5 MB/s | 111 kB 00:00 -(5/21): net-tools-2.0-0.52.20160912git.el8.x86_ 6.7 MB/s | 322 kB 00:00 -(6/21): openssl-1.1.1k-7.el8_6.x86_64.rpm 12 MB/s | 709 kB 00:00 -(7/21): tree-1.7.0-15.el8.x86_64.rpm 4.1 MB/s | 59 kB 00:00 -(8/21): sudo-1.8.29-8.el8.x86_64.rpm 19 MB/s | 925 kB 00:00 -(9/21): which-2.21-17.el8.x86_64.rpm 2.5 MB/s | 49 kB 00:00 -(10/21): unzip-6.0-46.0.1.el8.x86_64.rpm 5.9 MB/s | 196 kB 00:00 -(11/21): tcl-8.6.8-2.el8.x86_64.rpm 15 MB/s | 1.1 MB 00:00 -(12/21): zip-3.0-23.el8.x86_64.rpm 15 MB/s | 270 kB 00:00 -(13/21): bind-libs-9.11.36-3.el8_6.1.x86_64.rpm 7.9 MB/s | 175 kB 00:00 -(14/21): bind-license-9.11.36-3.el8_6.1.noarch. 4.9 MB/s | 103 kB 00:00 -(15/21): bind-utils-9.11.36-3.el8_6.1.x86_64.rp 21 MB/s | 452 kB 00:00 -(16/21): bind-libs-lite-9.11.36-3.el8_6.1.x86_6 28 MB/s | 1.2 MB 00:00 -(17/21): libmaxminddb-1.2.0-10.el8.x86_64.rpm 1.8 MB/s | 33 kB 00:00 -(18/21): fstrm-0.6.1-2.el8.x86_64.rpm 1.0 MB/s | 29 kB 00:00 -(19/21): protobuf-c-1.3.0-6.el8.x86_64.rpm 1.4 MB/s | 37 kB 00:00 -(20/21): python3-bind-9.11.36-3.el8_6.1.noarch. 9.2 MB/s | 150 kB 00:00 -(21/21): wget-1.19.5-10.0.1.el8.x86_64.rpm 7.5 MB/s | 734 kB 00:00 +(1/29): hostname-3.20-6.el8.x86_64.rpm 268 kB/s | 32 kB 00:00 +(2/29): libmetalink-0.1.3-7.el8.x86_64.rpm 257 kB/s | 32 kB 00:00 +(3/29): expect-5.45.4-5.el8.x86_64.rpm 1.4 MB/s | 266 kB 00:00 +(4/29): lsof-4.93.2-1.el8.x86_64.rpm 3.2 MB/s | 253 kB 00:00 +(5/29): net-tools-2.0-0.52.20160912git.el8.x86_ 3.6 MB/s | 322 kB 00:00 +(6/29): python3-ply-3.9-9.el8.noarch.rpm 2.7 MB/s | 111 kB 00:00 +(7/29): openssl-1.1.1k-12.el8_9.x86_64.rpm 10 MB/s | 710 kB 00:00 +(8/29): tree-1.7.0-15.el8.x86_64.rpm 2.2 MB/s | 59 kB 00:00 +(9/29): sudo-1.9.5p2-1.el8_9.x86_64.rpm 14 MB/s | 1.0 MB 00:00 +(10/29): unzip-6.0-46.0.1.el8.x86_64.rpm 6.8 MB/s | 196 kB 00:00 +(11/29): which-2.21-20.el8.x86_64.rpm 2.0 MB/s | 50 kB 00:00 +(12/29): tcl-8.6.8-2.el8.x86_64.rpm 13 MB/s | 1.1 MB 00:00 +(13/29): bind-libs-9.11.36-16.el8_10.2.x86_64.r 6.7 MB/s | 176 kB 00:00 +(14/29): zip-3.0-23.el8.x86_64.rpm 8.4 MB/s | 270 kB 00:00 +(15/29): bind-libs-lite-9.11.36-16.el8_10.2.x86 29 MB/s | 1.2 MB 00:00 +(16/29): bind-license-9.11.36-16.el8_10.2.noarc 3.3 MB/s | 104 kB 00:00 +(17/29): bind-utils-9.11.36-16.el8_10.2.x86_64. 13 MB/s | 453 kB 00:00 +(18/29): fstrm-0.6.1-3.el8.x86_64.rpm 1.2 MB/s | 29 kB 00:00 +(19/29): libmaxminddb-1.2.0-10.el8_9.1.x86_64.r 1.3 MB/s | 32 kB 00:00 +(20/29): geolite2-country-20180605-1.el8.noarch 17 MB/s | 1.0 MB 00:00 +(21/29): protobuf-c-1.3.0-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 +(22/29): python3-bind-9.11.36-16.el8_10.2.noarc 5.8 MB/s | 151 kB 00:00 +(23/29): wget-1.19.5-12.0.1.el8_10.x86_64.rpm 17 MB/s | 733 kB 00:00 +(24/29): curl-7.61.1-34.el8.x86_64.rpm 12 MB/s | 352 kB 00:00 +(25/29): dnf-plugins-core-4.0.21-25.0.1.el8.noa 2.4 MB/s | 76 kB 00:00 +(26/29): libcurl-7.61.1-34.el8.x86_64.rpm 8.6 MB/s | 303 kB 00:00 +(27/29): python3-dnf-plugins-core-4.0.21-25.0.1 9.8 MB/s | 263 kB 00:00 +(28/29): yum-utils-4.0.21-25.0.1.el8.noarch.rpm 3.0 MB/s | 75 kB 00:00 +(29/29): geolite2-city-20180605-1.el8.noarch.rp 66 MB/s | 19 MB 00:00 -------------------------------------------------------------------------------- -Total 20 MB/s | 6.9 MB 00:00 +Total 43 MB/s | 28 MB 00:00 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 - Installing : protobuf-c-1.3.0-6.el8.x86_64 1/21 - Installing : libmaxminddb-1.2.0-10.el8.x86_64 2/21 - Running scriptlet: libmaxminddb-1.2.0-10.el8.x86_64 2/21 - Installing : fstrm-0.6.1-2.el8.x86_64 3/21 - Installing : bind-license-32:9.11.36-3.el8_6.1.noarch 4/21 - Installing : bind-libs-lite-32:9.11.36-3.el8_6.1.x86_64 5/21 - Installing : bind-libs-32:9.11.36-3.el8_6.1.x86_64 6/21 - Installing : unzip-6.0-46.0.1.el8.x86_64 7/21 - Installing : tcl-1:8.6.8-2.el8.x86_64 8/21 - Running scriptlet: tcl-1:8.6.8-2.el8.x86_64 8/21 - Installing : python3-ply-3.9-9.el8.noarch 9/21 - Installing : python3-bind-32:9.11.36-3.el8_6.1.noarch 10/21 - Installing : libmetalink-0.1.3-7.el8.x86_64 11/21 - Installing : wget-1.19.5-10.0.1.el8.x86_64 12/21 - Running scriptlet: wget-1.19.5-10.0.1.el8.x86_64 12/21 - Installing : bind-utils-32:9.11.36-3.el8_6.1.x86_64 13/21 - Installing : expect-5.45.4-5.el8.x86_64 14/21 - Installing : zip-3.0-23.el8.x86_64 15/21 - Installing : which-2.21-17.el8.x86_64 16/21 - Installing : tree-1.7.0-15.el8.x86_64 17/21 - Installing : sudo-1.8.29-8.el8.x86_64 18/21 - Running scriptlet: sudo-1.8.29-8.el8.x86_64 18/21 - Installing : openssl-1:1.1.1k-7.el8_6.x86_64 19/21 - Installing : net-tools-2.0-0.52.20160912git.el8.x86_64 20/21 - Running scriptlet: net-tools-2.0-0.52.20160912git.el8.x86_64 20/21 - Installing : hostname-3.20-6.el8.x86_64 21/21 - Running scriptlet: hostname-3.20-6.el8.x86_64 21/21 - Verifying : expect-5.45.4-5.el8.x86_64 1/21 - Verifying : hostname-3.20-6.el8.x86_64 2/21 - Verifying : libmetalink-0.1.3-7.el8.x86_64 3/21 - Verifying : net-tools-2.0-0.52.20160912git.el8.x86_64 4/21 - Verifying : openssl-1:1.1.1k-7.el8_6.x86_64 5/21 - Verifying : python3-ply-3.9-9.el8.noarch 6/21 - Verifying : sudo-1.8.29-8.el8.x86_64 7/21 - Verifying : tcl-1:8.6.8-2.el8.x86_64 8/21 - Verifying : tree-1.7.0-15.el8.x86_64 9/21 - Verifying : unzip-6.0-46.0.1.el8.x86_64 10/21 - Verifying : which-2.21-17.el8.x86_64 11/21 - Verifying : zip-3.0-23.el8.x86_64 12/21 - Verifying : bind-libs-32:9.11.36-3.el8_6.1.x86_64 13/21 - Verifying : bind-libs-lite-32:9.11.36-3.el8_6.1.x86_64 14/21 - Verifying : bind-license-32:9.11.36-3.el8_6.1.noarch 15/21 - Verifying : bind-utils-32:9.11.36-3.el8_6.1.x86_64 16/21 - Verifying : fstrm-0.6.1-2.el8.x86_64 17/21 - Verifying : libmaxminddb-1.2.0-10.el8.x86_64 18/21 - Verifying : protobuf-c-1.3.0-6.el8.x86_64 19/21 - Verifying : python3-bind-32:9.11.36-3.el8_6.1.noarch 20/21 - Verifying : wget-1.19.5-10.0.1.el8.x86_64 21/21 + Running scriptlet: protobuf-c-1.3.0-8.el8.x86_64 1/1 + Installing : protobuf-c-1.3.0-8.el8.x86_64 1/34 + Installing : fstrm-0.6.1-3.el8.x86_64 2/34 + Installing : bind-license-32:9.11.36-16.el8_10.2.noarch 3/34 + Upgrading : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 4/34 + Upgrading : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 5/34 + Upgrading : libcurl-7.61.1-34.el8.x86_64 6/34 + Installing : geolite2-country-20180605-1.el8.noarch 7/34 + Installing : geolite2-city-20180605-1.el8.noarch 8/34 + Installing : libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 + Running scriptlet: libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 + Installing : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 10/34 + Installing : bind-libs-32:9.11.36-16.el8_10.2.x86_64 11/34 + Installing : unzip-6.0-46.0.1.el8.x86_64 12/34 + Installing : tcl-1:8.6.8-2.el8.x86_64 13/34 + Running scriptlet: tcl-1:8.6.8-2.el8.x86_64 13/34 + Installing : python3-ply-3.9-9.el8.noarch 14/34 + Installing : python3-bind-32:9.11.36-16.el8_10.2.noarch 15/34 + Installing : libmetalink-0.1.3-7.el8.x86_64 16/34 + Installing : wget-1.19.5-12.0.1.el8_10.x86_64 17/34 + Running scriptlet: wget-1.19.5-12.0.1.el8_10.x86_64 17/34 + Installing : bind-utils-32:9.11.36-16.el8_10.2.x86_64 18/34 + Installing : expect-5.45.4-5.el8.x86_64 19/34 + Installing : zip-3.0-23.el8.x86_64 20/34 + Upgrading : curl-7.61.1-34.el8.x86_64 21/34 + Upgrading : yum-utils-4.0.21-25.0.1.el8.noarch 22/34 + Installing : which-2.21-20.el8.x86_64 23/34 + Installing : tree-1.7.0-15.el8.x86_64 24/34 + Installing : sudo-1.9.5p2-1.el8_9.x86_64 25/34 + Running scriptlet: sudo-1.9.5p2-1.el8_9.x86_64 25/34 + Installing : openssl-1:1.1.1k-12.el8_9.x86_64 26/34 + Installing : net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 + Running scriptlet: net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 + Installing : lsof-4.93.2-1.el8.x86_64 28/34 + Installing : hostname-3.20-6.el8.x86_64 29/34 + Running scriptlet: hostname-3.20-6.el8.x86_64 29/34 + Cleanup : curl-7.61.1-33.el8_9.5.x86_64 30/34 + Cleanup : yum-utils-4.0.21-23.0.1.el8.noarch 31/34 + Cleanup : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 + Cleanup : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 33/34 + Cleanup : libcurl-7.61.1-33.el8_9.5.x86_64 34/34 + Running scriptlet: libcurl-7.61.1-33.el8_9.5.x86_64 34/34 + Verifying : expect-5.45.4-5.el8.x86_64 1/34 + Verifying : hostname-3.20-6.el8.x86_64 2/34 + Verifying : libmetalink-0.1.3-7.el8.x86_64 3/34 + Verifying : lsof-4.93.2-1.el8.x86_64 4/34 + Verifying : net-tools-2.0-0.52.20160912git.el8.x86_64 5/34 + Verifying : openssl-1:1.1.1k-12.el8_9.x86_64 6/34 + Verifying : python3-ply-3.9-9.el8.noarch 7/34 + Verifying : sudo-1.9.5p2-1.el8_9.x86_64 8/34 + Verifying : tcl-1:8.6.8-2.el8.x86_64 9/34 + Verifying : tree-1.7.0-15.el8.x86_64 10/34 + Verifying : unzip-6.0-46.0.1.el8.x86_64 11/34 + Verifying : which-2.21-20.el8.x86_64 12/34 + Verifying : zip-3.0-23.el8.x86_64 13/34 + Verifying : bind-libs-32:9.11.36-16.el8_10.2.x86_64 14/34 + Verifying : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 15/34 + Verifying : bind-license-32:9.11.36-16.el8_10.2.noarch 16/34 + Verifying : bind-utils-32:9.11.36-16.el8_10.2.x86_64 17/34 + Verifying : fstrm-0.6.1-3.el8.x86_64 18/34 + Verifying : geolite2-city-20180605-1.el8.noarch 19/34 + Verifying : geolite2-country-20180605-1.el8.noarch 20/34 + Verifying : libmaxminddb-1.2.0-10.el8_9.1.x86_64 21/34 + Verifying : protobuf-c-1.3.0-8.el8.x86_64 22/34 + Verifying : python3-bind-32:9.11.36-16.el8_10.2.noarch 23/34 + Verifying : wget-1.19.5-12.0.1.el8_10.x86_64 24/34 + Verifying : curl-7.61.1-34.el8.x86_64 25/34 + Verifying : curl-7.61.1-33.el8_9.5.x86_64 26/34 + Verifying : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 27/34 + Verifying : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 28/34 + Verifying : libcurl-7.61.1-34.el8.x86_64 29/34 + Verifying : libcurl-7.61.1-33.el8_9.5.x86_64 30/34 + Verifying : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 31/34 + Verifying : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 + Verifying : yum-utils-4.0.21-25.0.1.el8.noarch 33/34 + Verifying : yum-utils-4.0.21-23.0.1.el8.noarch 34/34 +Upgraded: + curl-7.61.1-34.el8.x86_64 + dnf-plugins-core-4.0.21-25.0.1.el8.noarch + libcurl-7.61.1-34.el8.x86_64 + python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch + yum-utils-4.0.21-25.0.1.el8.noarch Installed: - bind-libs-32:9.11.36-3.el8_6.1.x86_64 - bind-libs-lite-32:9.11.36-3.el8_6.1.x86_64 - bind-license-32:9.11.36-3.el8_6.1.noarch - bind-utils-32:9.11.36-3.el8_6.1.x86_64 + bind-libs-32:9.11.36-16.el8_10.2.x86_64 + bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 + bind-license-32:9.11.36-16.el8_10.2.noarch + bind-utils-32:9.11.36-16.el8_10.2.x86_64 expect-5.45.4-5.el8.x86_64 - fstrm-0.6.1-2.el8.x86_64 + fstrm-0.6.1-3.el8.x86_64 + geolite2-city-20180605-1.el8.noarch + geolite2-country-20180605-1.el8.noarch hostname-3.20-6.el8.x86_64 - libmaxminddb-1.2.0-10.el8.x86_64 + libmaxminddb-1.2.0-10.el8_9.1.x86_64 libmetalink-0.1.3-7.el8.x86_64 + lsof-4.93.2-1.el8.x86_64 net-tools-2.0-0.52.20160912git.el8.x86_64 - openssl-1:1.1.1k-7.el8_6.x86_64 - protobuf-c-1.3.0-6.el8.x86_64 - python3-bind-32:9.11.36-3.el8_6.1.noarch + openssl-1:1.1.1k-12.el8_9.x86_64 + protobuf-c-1.3.0-8.el8.x86_64 + python3-bind-32:9.11.36-16.el8_10.2.noarch python3-ply-3.9-9.el8.noarch - sudo-1.8.29-8.el8.x86_64 + sudo-1.9.5p2-1.el8_9.x86_64 tcl-1:8.6.8-2.el8.x86_64 tree-1.7.0-15.el8.x86_64 unzip-6.0-46.0.1.el8.x86_64 - wget-1.19.5-10.0.1.el8.x86_64 - which-2.21-17.el8.x86_64 + wget-1.19.5-12.0.1.el8_10.x86_64 + which-2.21-20.el8.x86_64 zip-3.0-23.el8.x86_64 Complete! Adding repo from: http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 -created by dnf config-manager from http://yum.o 194 kB/s | 49 kB 00:00 +created by dnf config-manager from http://yum.o 496 kB/s | 139 kB 00:00 +Last metadata expiration check: 0:00:01 ago on Tue 20 Aug 2024 08:55:14 AM UTC. Dependencies resolved. -============================================================================================= - Package Arch Version Repository Size -============================================================================================= +============================================================================================== + Package Arch Version Repository Size +============================================================================================== Installing: - java-11-openjdk-devel x86_64 1:11.0.16.1.1-1.el8_6 ol8_appstream 3.4 M + java-11-openjdk-devel x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 3.4 M Installing dependencies: - alsa-lib x86_64 1.2.6.1-3.el8 ol8_appstream 491 k - avahi-libs x86_64 0.7-20.el8 ol8_baseos_latest 62 k - copy-jdk-configs noarch 4.0-2.el8 ol8_appstream 30 k - crypto-policies-scripts noarch 20211116-1.gitae470d6.el8 ol8_baseos_latest 83 k - cups-libs x86_64 1:2.2.6-45.el8_6.2 ol8_baseos_latest 434 k - giflib x86_64 5.1.4-3.el8 ol8_appstream 51 k - graphite2 x86_64 1.3.10-10.el8 ol8_appstream 122 k - harfbuzz x86_64 1.7.5-3.el8 ol8_appstream 295 k - java-11-openjdk x86_64 1:11.0.16.1.1-1.el8_6 ol8_appstream 272 k - java-11-openjdk-headless x86_64 1:11.0.16.1.1-1.el8_6 ol8_appstream 40 M - javapackages-filesystem noarch 5.3.0-1.module+el8+5136+7ff78f74 ol8_appstream 30 k - lcms2 x86_64 2.9-2.el8 ol8_appstream 164 k - libX11 x86_64 1.6.8-5.el8 ol8_appstream 611 k - libX11-common noarch 1.6.8-5.el8 ol8_appstream 158 k - libXau x86_64 1.0.9-3.el8 ol8_appstream 37 k - libXcomposite x86_64 0.4.4-14.el8 ol8_appstream 28 k - libXext x86_64 1.3.4-1.el8 ol8_appstream 45 k - libXi x86_64 1.7.10-1.el8 ol8_appstream 49 k - libXrender x86_64 0.9.10-7.el8 ol8_appstream 33 k - libXtst x86_64 1.2.3-7.el8 ol8_appstream 22 k - libfontenc x86_64 1.1.3-8.el8 ol8_appstream 37 k - libjpeg-turbo x86_64 1.5.3-12.el8 ol8_appstream 157 k - libpkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 35 k - libxcb x86_64 1.13.1-1.el8 ol8_appstream 231 k - lksctp-tools x86_64 1.0.18-3.el8 ol8_baseos_latest 100 k - lua x86_64 5.3.4-12.el8 ol8_appstream 192 k - nspr x86_64 4.34.0-3.el8_6 ol8_appstream 143 k - nss x86_64 3.79.0-10.el8_6 ol8_appstream 747 k - nss-softokn x86_64 3.79.0-10.el8_6 ol8_appstream 1.2 M - nss-softokn-freebl x86_64 3.79.0-10.el8_6 ol8_appstream 398 k - nss-sysinit x86_64 3.79.0-10.el8_6 ol8_appstream 74 k - nss-util x86_64 3.79.0-10.el8_6 ol8_appstream 138 k - pkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 38 k - pkgconf-m4 noarch 1.4.2-1.el8 ol8_baseos_latest 17 k - pkgconf-pkg-config x86_64 1.4.2-1.el8 ol8_baseos_latest 15 k - ttmkfdir x86_64 3.0.9-54.el8 ol8_appstream 62 k - tzdata-java noarch 2022d-1.el8 ol8_appstream 186 k - xorg-x11-font-utils x86_64 1:7.5-41.el8 ol8_appstream 104 k - xorg-x11-fonts-Type1 noarch 7.5-19.el8 ol8_appstream 522 k + adwaita-cursor-theme noarch 3.28.0-3.el8 ol8_appstream 647 k + adwaita-icon-theme noarch 3.28.0-3.el8 ol8_appstream 11 M + alsa-lib x86_64 1.2.10-2.el8 ol8_appstream 500 k + at-spi2-atk x86_64 2.26.2-1.el8 ol8_appstream 89 k + at-spi2-core x86_64 2.28.0-1.el8 ol8_appstream 169 k + atk x86_64 2.28.1-1.el8 ol8_appstream 272 k + avahi-libs x86_64 0.7-27.el8 ol8_baseos_latest 61 k + cairo x86_64 1.15.12-6.el8 ol8_appstream 719 k + cairo-gobject x86_64 1.15.12-6.el8 ol8_appstream 33 k + colord-libs x86_64 1.4.2-1.el8 ol8_appstream 236 k + copy-jdk-configs noarch 4.0-2.el8 ol8_appstream 30 k + cpio x86_64 2.12-11.el8 ol8_baseos_latest 266 k + crypto-policies-scripts noarch 20230731-1.git3177e06.el8 ol8_baseos_latest 84 k + cups-libs x86_64 1:2.2.6-60.el8_10 ol8_baseos_latest 435 k + dracut x86_64 049-233.git20240115.0.1.el8 ol8_baseos_latest 382 k + file x86_64 5.33-25.el8 ol8_baseos_latest 77 k + fribidi x86_64 1.0.4-9.el8 ol8_appstream 89 k + gdk-pixbuf2 x86_64 2.36.12-6.el8_10 ol8_baseos_latest 465 k + gdk-pixbuf2-modules x86_64 2.36.12-6.el8_10 ol8_appstream 108 k + gettext x86_64 0.19.8.1-17.el8 ol8_baseos_latest 1.1 M + gettext-libs x86_64 0.19.8.1-17.el8 ol8_baseos_latest 312 k + glib-networking x86_64 2.56.1-1.1.el8 ol8_baseos_latest 155 k + graphite2 x86_64 1.3.10-10.el8 ol8_appstream 122 k + grub2-common noarch 1:2.02-156.0.2.el8 ol8_baseos_latest 897 k + grub2-tools x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 2.0 M + grub2-tools-minimal x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 215 k + gsettings-desktop-schemas x86_64 3.32.0-6.el8 ol8_baseos_latest 633 k + gtk-update-icon-cache x86_64 3.22.30-11.el8 ol8_appstream 32 k + harfbuzz x86_64 1.7.5-4.el8 ol8_appstream 295 k + hicolor-icon-theme noarch 0.17-2.el8 ol8_appstream 48 k + jasper-libs x86_64 2.0.14-5.el8 ol8_appstream 167 k + java-11-openjdk x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 475 k + java-11-openjdk-headless x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 42 M + javapackages-filesystem noarch 5.3.0-1.module+el8+5136+7ff78f74 ol8_appstream 30 k + jbigkit-libs x86_64 2.1-14.el8 ol8_appstream 55 k + json-glib x86_64 1.4.4-1.el8 ol8_baseos_latest 144 k + kbd-legacy noarch 2.0.4-11.el8 ol8_baseos_latest 481 k + kbd-misc noarch 2.0.4-11.el8 ol8_baseos_latest 1.5 M + lcms2 x86_64 2.9-2.el8 ol8_appstream 164 k + libX11 x86_64 1.6.8-8.el8 ol8_appstream 611 k + libX11-common noarch 1.6.8-8.el8 ol8_appstream 157 k + libXau x86_64 1.0.9-3.el8 ol8_appstream 37 k + libXcomposite x86_64 0.4.4-14.el8 ol8_appstream 28 k + libXcursor x86_64 1.1.15-3.el8 ol8_appstream 36 k + libXdamage x86_64 1.1.4-14.el8 ol8_appstream 27 k + libXext x86_64 1.3.4-1.el8 ol8_appstream 45 k + libXfixes x86_64 5.0.3-7.el8 ol8_appstream 25 k + libXft x86_64 2.3.3-1.el8 ol8_appstream 67 k + libXi x86_64 1.7.10-1.el8 ol8_appstream 49 k + libXinerama x86_64 1.1.4-1.el8 ol8_appstream 15 k + libXrandr x86_64 1.5.2-1.el8 ol8_appstream 34 k + libXrender x86_64 0.9.10-7.el8 ol8_appstream 33 k + libXtst x86_64 1.2.3-7.el8 ol8_appstream 22 k + libcroco x86_64 0.6.12-4.el8_2.1 ol8_baseos_latest 113 k + libdatrie x86_64 0.2.9-7.el8 ol8_appstream 33 k + libepoxy x86_64 1.5.8-1.el8 ol8_appstream 225 k + libfontenc x86_64 1.1.3-8.el8 ol8_appstream 37 k + libgomp x86_64 8.5.0-22.0.1.el8_10 ol8_baseos_latest 218 k + libgusb x86_64 0.3.0-1.el8 ol8_baseos_latest 49 k + libjpeg-turbo x86_64 1.5.3-12.el8 ol8_appstream 157 k + libkcapi x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 52 k + libkcapi-hmaccalc x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 31 k + libmodman x86_64 2.0.1-17.el8 ol8_baseos_latest 36 k + libpkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 35 k + libproxy x86_64 0.4.15-5.2.el8 ol8_baseos_latest 75 k + libsoup x86_64 2.62.3-5.el8 ol8_baseos_latest 424 k + libthai x86_64 0.1.27-2.el8 ol8_appstream 203 k + libtiff x86_64 4.0.9-32.el8_10 ol8_appstream 189 k + libwayland-client x86_64 1.21.0-1.el8 ol8_appstream 41 k + libwayland-cursor x86_64 1.21.0-1.el8 ol8_appstream 26 k + libwayland-egl x86_64 1.21.0-1.el8 ol8_appstream 19 k + libxcb x86_64 1.13.1-1.el8 ol8_appstream 231 k + libxkbcommon x86_64 0.9.1-1.el8 ol8_appstream 116 k + lksctp-tools x86_64 1.0.18-3.el8 ol8_baseos_latest 100 k + lua x86_64 5.3.4-12.el8 ol8_appstream 192 k + nspr x86_64 4.35.0-1.el8_8 ol8_appstream 143 k + nss x86_64 3.90.0-7.el8_10 ol8_appstream 750 k + nss-softokn x86_64 3.90.0-7.el8_10 ol8_appstream 1.2 M + nss-softokn-freebl x86_64 3.90.0-7.el8_10 ol8_appstream 375 k + nss-sysinit x86_64 3.90.0-7.el8_10 ol8_appstream 74 k + nss-util x86_64 3.90.0-7.el8_10 ol8_appstream 139 k + os-prober x86_64 1.74-9.0.1.el8 ol8_baseos_latest 51 k + pango x86_64 1.42.4-8.el8 ol8_appstream 297 k + pixman x86_64 0.38.4-4.el8 ol8_appstream 256 k + pkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 38 k + pkgconf-m4 noarch 1.4.2-1.el8 ol8_baseos_latest 17 k + pkgconf-pkg-config x86_64 1.4.2-1.el8 ol8_baseos_latest 15 k + rest x86_64 0.8.1-2.el8 ol8_appstream 70 k + shared-mime-info x86_64 1.9-4.el8 ol8_baseos_latest 328 k + systemd-udev x86_64 239-78.0.4.el8 ol8_baseos_latest 1.6 M + ttmkfdir x86_64 3.0.9-54.el8 ol8_appstream 62 k + tzdata-java noarch 2024a-1.0.1.el8 ol8_appstream 186 k + xkeyboard-config noarch 2.28-1.el8 ol8_appstream 782 k + xorg-x11-font-utils x86_64 1:7.5-41.el8 ol8_appstream 104 k + xorg-x11-fonts-Type1 noarch 7.5-19.el8 ol8_appstream 522 k + xz x86_64 5.2.4-4.el8_6 ol8_baseos_latest 153 k +Installing weak dependencies: + abattis-cantarell-fonts noarch 0.0.25-6.el8 ol8_appstream 155 k + dconf x86_64 0.28.0-4.0.1.el8 ol8_appstream 108 k + dejavu-sans-mono-fonts noarch 2.35-7.el8 ol8_baseos_latest 447 k + grubby x86_64 8.40-49.0.2.el8 ol8_baseos_latest 50 k + gtk3 x86_64 3.22.30-11.el8 ol8_appstream 4.5 M + hardlink x86_64 1:1.3-6.el8 ol8_baseos_latest 29 k + kbd x86_64 2.0.4-11.el8 ol8_baseos_latest 390 k + memstrack x86_64 0.2.5-2.el8 ol8_baseos_latest 51 k + pigz x86_64 2.4-4.el8 ol8_baseos_latest 80 k Enabling module streams: - javapackages-runtime 201801 + javapackages-runtime 201801 Transaction Summary -============================================================================================= -Install 40 Packages +============================================================================================== +Install 106 Packages -Total download size: 50 M -Installed size: 196 M +Total download size: 86 M +Installed size: 312 M Downloading Packages: -(1/40): crypto-policies-scripts-20211116-1.gita 1.3 MB/s | 83 kB 00:00 -(2/40): avahi-libs-0.7-20.el8.x86_64.rpm 879 kB/s | 62 kB 00:00 -(3/40): libpkgconf-1.4.2-1.el8.x86_64.rpm 2.0 MB/s | 35 kB 00:00 -(4/40): cups-libs-2.2.6-45.el8_6.2.x86_64.rpm 4.5 MB/s | 434 kB 00:00 -(5/40): lksctp-tools-1.0.18-3.el8.x86_64.rpm 3.7 MB/s | 100 kB 00:00 -(6/40): pkgconf-1.4.2-1.el8.x86_64.rpm 2.2 MB/s | 38 kB 00:00 -(7/40): pkgconf-m4-1.4.2-1.el8.noarch.rpm 1.2 MB/s | 17 kB 00:00 -(8/40): pkgconf-pkg-config-1.4.2-1.el8.x86_64.r 929 kB/s | 15 kB 00:00 -(9/40): copy-jdk-configs-4.0-2.el8.noarch.rpm 2.2 MB/s | 30 kB 00:00 -(10/40): giflib-5.1.4-3.el8.x86_64.rpm 3.3 MB/s | 51 kB 00:00 -(11/40): graphite2-1.3.10-10.el8.x86_64.rpm 7.7 MB/s | 122 kB 00:00 -(12/40): alsa-lib-1.2.6.1-3.el8.x86_64.rpm 12 MB/s | 491 kB 00:00 -(13/40): java-11-openjdk-11.0.16.1.1-1.el8_6.x8 14 MB/s | 272 kB 00:00 -(14/40): harfbuzz-1.7.5-3.el8.x86_64.rpm 8.7 MB/s | 295 kB 00:00 -(15/40): javapackages-filesystem-5.3.0-1.module 2.0 MB/s | 30 kB 00:00 -(16/40): lcms2-2.9-2.el8.x86_64.rpm 6.7 MB/s | 164 kB 00:00 -(17/40): java-11-openjdk-devel-11.0.16.1.1-1.el 46 MB/s | 3.4 MB 00:00 -(18/40): libX11-common-1.6.8-5.el8.noarch.rpm 8.4 MB/s | 158 kB 00:00 -(19/40): libX11-1.6.8-5.el8.x86_64.rpm 17 MB/s | 611 kB 00:00 -(20/40): libXau-1.0.9-3.el8.x86_64.rpm 2.6 MB/s | 37 kB 00:00 -(21/40): libXcomposite-0.4.4-14.el8.x86_64.rpm 2.0 MB/s | 28 kB 00:00 -(22/40): libXi-1.7.10-1.el8.x86_64.rpm 2.2 MB/s | 49 kB 00:00 -(23/40): libXext-1.3.4-1.el8.x86_64.rpm 1.6 MB/s | 45 kB 00:00 -(24/40): libXtst-1.2.3-7.el8.x86_64.rpm 1.1 MB/s | 22 kB 00:00 -(25/40): libXrender-0.9.10-7.el8.x86_64.rpm 1.3 MB/s | 33 kB 00:00 -(26/40): libfontenc-1.1.3-8.el8.x86_64.rpm 2.2 MB/s | 37 kB 00:00 -(27/40): libjpeg-turbo-1.5.3-12.el8.x86_64.rpm 8.6 MB/s | 157 kB 00:00 -(28/40): libxcb-1.13.1-1.el8.x86_64.rpm 13 MB/s | 231 kB 00:00 -(29/40): lua-5.3.4-12.el8.x86_64.rpm 11 MB/s | 192 kB 00:00 -(30/40): nspr-4.34.0-3.el8_6.x86_64.rpm 7.8 MB/s | 143 kB 00:00 -(31/40): nss-3.79.0-10.el8_6.x86_64.rpm 23 MB/s | 747 kB 00:00 -(32/40): nss-softokn-3.79.0-10.el8_6.x86_64.rpm 42 MB/s | 1.2 MB 00:00 -(33/40): nss-softokn-freebl-3.79.0-10.el8_6.x86 19 MB/s | 398 kB 00:00 -(34/40): nss-sysinit-3.79.0-10.el8_6.x86_64.rpm 5.3 MB/s | 74 kB 00:00 -(35/40): nss-util-3.79.0-10.el8_6.x86_64.rpm 8.7 MB/s | 138 kB 00:00 -(36/40): ttmkfdir-3.0.9-54.el8.x86_64.rpm 4.2 MB/s | 62 kB 00:00 -(37/40): tzdata-java-2022d-1.el8.noarch.rpm 11 MB/s | 186 kB 00:00 -(38/40): xorg-x11-font-utils-7.5-41.el8.x86_64. 6.7 MB/s | 104 kB 00:00 -(39/40): xorg-x11-fonts-Type1-7.5-19.el8.noarch 24 MB/s | 522 kB 00:00 -(40/40): java-11-openjdk-headless-11.0.16.1.1-1 77 MB/s | 40 MB 00:00 +(1/106): crypto-policies-scripts-20230731-1.git 862 kB/s | 84 kB 00:00 +(2/106): avahi-libs-0.7-27.el8.x86_64.rpm 602 kB/s | 61 kB 00:00 +(3/106): cpio-2.12-11.el8.x86_64.rpm 1.8 MB/s | 266 kB 00:00 +(4/106): cups-libs-2.2.6-60.el8_10.x86_64.rpm 5.7 MB/s | 435 kB 00:00 +(5/106): dejavu-sans-mono-fonts-2.35-7.el8.noar 5.1 MB/s | 447 kB 00:00 +(6/106): dracut-049-233.git20240115.0.1.el8.x86 7.0 MB/s | 382 kB 00:00 +(7/106): gdk-pixbuf2-2.36.12-6.el8_10.x86_64.rp 12 MB/s | 465 kB 00:00 +(8/106): gettext-libs-0.19.8.1-17.el8.x86_64.rp 9.3 MB/s | 312 kB 00:00 +(9/106): gettext-0.19.8.1-17.el8.x86_64.rpm 16 MB/s | 1.1 MB 00:00 +(10/106): glib-networking-2.56.1-1.1.el8.x86_64 6.0 MB/s | 155 kB 00:00 +(11/106): grub2-common-2.02-156.0.2.el8.noarch. 26 MB/s | 897 kB 00:00 +(12/106): grub2-tools-minimal-2.02-156.0.2.el8. 8.2 MB/s | 215 kB 00:00 +(13/106): grubby-8.40-49.0.2.el8.x86_64.rpm 2.1 MB/s | 50 kB 00:00 +(14/106): grub2-tools-2.02-156.0.2.el8.x86_64.r 26 MB/s | 2.0 MB 00:00 +(15/106): gsettings-desktop-schemas-3.32.0-6.el 19 MB/s | 633 kB 00:00 +(16/106): hardlink-1.3-6.el8.x86_64.rpm 1.1 MB/s | 29 kB 00:00 +(17/106): json-glib-1.4.4-1.el8.x86_64.rpm 5.9 MB/s | 144 kB 00:00 +(18/106): kbd-2.0.4-11.el8.x86_64.rpm 14 MB/s | 390 kB 00:00 +(19/106): kbd-legacy-2.0.4-11.el8.noarch.rpm 17 MB/s | 481 kB 00:00 +(20/106): kbd-misc-2.0.4-11.el8.noarch.rpm 41 MB/s | 1.5 MB 00:00 +(21/106): libcroco-0.6.12-4.el8_2.1.x86_64.rpm 4.7 MB/s | 113 kB 00:00 +(22/106): libgomp-8.5.0-22.0.1.el8_10.x86_64.rp 9.1 MB/s | 218 kB 00:00 +(23/106): libgusb-0.3.0-1.el8.x86_64.rpm 2.1 MB/s | 49 kB 00:00 +(24/106): libkcapi-1.4.0-2.0.1.el8.x86_64.rpm 1.6 MB/s | 52 kB 00:00 +(25/106): libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86 822 kB/s | 31 kB 00:00 +(26/106): libmodman-2.0.1-17.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 +(27/106): libpkgconf-1.4.2-1.el8.x86_64.rpm 1.2 MB/s | 35 kB 00:00 +(28/106): libproxy-0.4.15-5.2.el8.x86_64.rpm 3.0 MB/s | 75 kB 00:00 +(29/106): libsoup-2.62.3-5.el8.x86_64.rpm 15 MB/s | 424 kB 00:00 +(30/106): lksctp-tools-1.0.18-3.el8.x86_64.rpm 3.5 MB/s | 100 kB 00:00 +(31/106): memstrack-0.2.5-2.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 +(32/106): os-prober-1.74-9.0.1.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 +(33/106): pigz-2.4-4.el8.x86_64.rpm 3.5 MB/s | 80 kB 00:00 +(34/106): pkgconf-1.4.2-1.el8.x86_64.rpm 1.7 MB/s | 38 kB 00:00 +(35/106): pkgconf-m4-1.4.2-1.el8.noarch.rpm 761 kB/s | 17 kB 00:00 +(36/106): pkgconf-pkg-config-1.4.2-1.el8.x86_64 691 kB/s | 15 kB 00:00 +(37/106): shared-mime-info-1.9-4.el8.x86_64.rpm 13 MB/s | 328 kB 00:00 +(38/106): systemd-udev-239-78.0.4.el8.x86_64.rp 32 MB/s | 1.6 MB 00:00 +(39/106): xz-5.2.4-4.el8_6.x86_64.rpm 5.2 MB/s | 153 kB 00:00 +(40/106): abattis-cantarell-fonts-0.0.25-6.el8. 6.4 MB/s | 155 kB 00:00 +(41/106): adwaita-cursor-theme-3.28.0-3.el8.noa 22 MB/s | 647 kB 00:00 +(42/106): alsa-lib-1.2.10-2.el8.x86_64.rpm 18 MB/s | 500 kB 00:00 +(43/106): at-spi2-atk-2.26.2-1.el8.x86_64.rpm 3.8 MB/s | 89 kB 00:00 +(44/106): at-spi2-core-2.28.0-1.el8.x86_64.rpm 6.9 MB/s | 169 kB 00:00 +(45/106): atk-2.28.1-1.el8.x86_64.rpm 9.2 MB/s | 272 kB 00:00 +(46/106): cairo-1.15.12-6.el8.x86_64.rpm 24 MB/s | 719 kB 00:00 +(47/106): adwaita-icon-theme-3.28.0-3.el8.noarc 65 MB/s | 11 MB 00:00 +(48/106): cairo-gobject-1.15.12-6.el8.x86_64.rp 914 kB/s | 33 kB 00:00 +(49/106): colord-libs-1.4.2-1.el8.x86_64.rpm 9.5 MB/s | 236 kB 00:00 +(50/106): copy-jdk-configs-4.0-2.el8.noarch.rpm 1.1 MB/s | 30 kB 00:00 +(51/106): dconf-0.28.0-4.0.1.el8.x86_64.rpm 4.4 MB/s | 108 kB 00:00 +(52/106): fribidi-1.0.4-9.el8.x86_64.rpm 3.9 MB/s | 89 kB 00:00 +(53/106): graphite2-1.3.10-10.el8.x86_64.rpm 5.1 MB/s | 122 kB 00:00 +(54/106): gdk-pixbuf2-modules-2.36.12-6.el8_10. 3.6 MB/s | 108 kB 00:00 +(55/106): gtk-update-icon-cache-3.22.30-11.el8. 1.4 MB/s | 32 kB 00:00 +(56/106): harfbuzz-1.7.5-4.el8.x86_64.rpm 11 MB/s | 295 kB 00:00 +(57/106): gtk3-3.22.30-11.el8.x86_64.rpm 68 MB/s | 4.5 MB 00:00 +(58/106): hicolor-icon-theme-0.17-2.el8.noarch. 2.1 MB/s | 48 kB 00:00 +(59/106): java-11-openjdk-11.0.24.0.8-3.0.1.el8 17 MB/s | 475 kB 00:00 +(60/106): jasper-libs-2.0.14-5.el8.x86_64.rpm 5.0 MB/s | 167 kB 00:00 +(61/106): java-11-openjdk-devel-11.0.24.0.8-3.0 61 MB/s | 3.4 MB 00:00 +(62/106): javapackages-filesystem-5.3.0-1.modul 1.2 MB/s | 30 kB 00:00 +(63/106): jbigkit-libs-2.1-14.el8.x86_64.rpm 2.1 MB/s | 55 kB 00:00 +(64/106): lcms2-2.9-2.el8.x86_64.rpm 3.8 MB/s | 164 kB 00:00 +(65/106): libX11-1.6.8-8.el8.x86_64.rpm 20 MB/s | 611 kB 00:00 +(66/106): libX11-common-1.6.8-8.el8.noarch.rpm 6.8 MB/s | 157 kB 00:00 +(67/106): libXau-1.0.9-3.el8.x86_64.rpm 1.6 MB/s | 37 kB 00:00 +(68/106): libXcomposite-0.4.4-14.el8.x86_64.rpm 1.3 MB/s | 28 kB 00:00 +(69/106): libXcursor-1.1.15-3.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 +(70/106): libXdamage-1.1.4-14.el8.x86_64.rpm 1.2 MB/s | 27 kB 00:00 +(71/106): libXext-1.3.4-1.el8.x86_64.rpm 2.0 MB/s | 45 kB 00:00 +(72/106): libXfixes-5.0.3-7.el8.x86_64.rpm 1.1 MB/s | 25 kB 00:00 +(73/106): libXft-2.3.3-1.el8.x86_64.rpm 2.9 MB/s | 67 kB 00:00 +(74/106): libXi-1.7.10-1.el8.x86_64.rpm 2.2 MB/s | 49 kB 00:00 +(75/106): libXinerama-1.1.4-1.el8.x86_64.rpm 717 kB/s | 15 kB 00:00 +(76/106): libXrandr-1.5.2-1.el8.x86_64.rpm 1.5 MB/s | 34 kB 00:00 +(77/106): libXrender-0.9.10-7.el8.x86_64.rpm 1.4 MB/s | 33 kB 00:00 +(78/106): libXtst-1.2.3-7.el8.x86_64.rpm 957 kB/s | 22 kB 00:00 +(79/106): java-11-openjdk-headless-11.0.24.0.8- 71 MB/s | 42 MB 00:00 +(80/106): libdatrie-0.2.9-7.el8.x86_64.rpm 274 kB/s | 33 kB 00:00 +(81/106): libepoxy-1.5.8-1.el8.x86_64.rpm 9.1 MB/s | 225 kB 00:00 +(82/106): libfontenc-1.1.3-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 +(83/106): libthai-0.1.27-2.el8.x86_64.rpm 8.2 MB/s | 203 kB 00:00 +(84/106): libjpeg-turbo-1.5.3-12.el8.x86_64.rpm 5.1 MB/s | 157 kB 00:00 +(85/106): libtiff-4.0.9-32.el8_10.x86_64.rpm 7.8 MB/s | 189 kB 00:00 +(86/106): libwayland-client-1.21.0-1.el8.x86_64 1.7 MB/s | 41 kB 00:00 +(87/106): libwayland-cursor-1.21.0-1.el8.x86_64 1.2 MB/s | 26 kB 00:00 +(88/106): libwayland-egl-1.21.0-1.el8.x86_64.rp 801 kB/s | 19 kB 00:00 +(89/106): libxcb-1.13.1-1.el8.x86_64.rpm 9.7 MB/s | 231 kB 00:00 +(90/106): libxkbcommon-0.9.1-1.el8.x86_64.rpm 5.0 MB/s | 116 kB 00:00 +(91/106): nspr-4.35.0-1.el8_8.x86_64.rpm 6.0 MB/s | 143 kB 00:00 +(92/106): lua-5.3.4-12.el8.x86_64.rpm 5.9 MB/s | 192 kB 00:00 +(93/106): nss-softokn-3.90.0-7.el8_10.x86_64.rp 38 MB/s | 1.2 MB 00:00 +(94/106): nss-3.90.0-7.el8_10.x86_64.rpm 17 MB/s | 750 kB 00:00 +(95/106): nss-softokn-freebl-3.90.0-7.el8_10.x8 14 MB/s | 375 kB 00:00 +(96/106): nss-sysinit-3.90.0-7.el8_10.x86_64.rp 3.2 MB/s | 74 kB 00:00 +(97/106): nss-util-3.90.0-7.el8_10.x86_64.rpm 5.8 MB/s | 139 kB 00:00 +(98/106): pango-1.42.4-8.el8.x86_64.rpm 11 MB/s | 297 kB 00:00 +(99/106): pixman-0.38.4-4.el8.x86_64.rpm 10 MB/s | 256 kB 00:00 +(100/106): rest-0.8.1-2.el8.x86_64.rpm 3.1 MB/s | 70 kB 00:00 +(101/106): ttmkfdir-3.0.9-54.el8.x86_64.rpm 2.5 MB/s | 62 kB 00:00 +(102/106): tzdata-java-2024a-1.0.1.el8.noarch.r 7.4 MB/s | 186 kB 00:00 +(103/106): xkeyboard-config-2.28-1.el8.noarch.r 27 MB/s | 782 kB 00:00 +(104/106): xorg-x11-font-utils-7.5-41.el8.x86_6 3.9 MB/s | 104 kB 00:00 +(105/106): xorg-x11-fonts-Type1-7.5-19.el8.noar 1.3 MB/s | 522 kB 00:00 +(106/106): file-5.33-25.el8.x86_64.rpm 26 kB/s | 77 kB 00:02 -------------------------------------------------------------------------------- -Total 74 MB/s | 50 MB 00:00 +Total 27 MB/s | 86 MB 00:03 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 1/1 - Running scriptlet: java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86_6 1/1 + Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86 1/1 Preparing : 1/1 - Installing : nspr-4.34.0-3.el8_6.x86_64 1/40 - Running scriptlet: nspr-4.34.0-3.el8_6.x86_64 1/40 - Installing : nss-util-3.79.0-10.el8_6.x86_64 2/40 - Installing : libjpeg-turbo-1.5.3-12.el8.x86_64 3/40 - Installing : nss-softokn-freebl-3.79.0-10.el8_6.x86_64 4/40 - Installing : nss-softokn-3.79.0-10.el8_6.x86_64 5/40 - Installing : tzdata-java-2022d-1.el8.noarch 6/40 - Installing : ttmkfdir-3.0.9-54.el8.x86_64 7/40 - Installing : lua-5.3.4-12.el8.x86_64 8/40 - Installing : copy-jdk-configs-4.0-2.el8.noarch 9/40 - Installing : libfontenc-1.1.3-8.el8.x86_64 10/40 - Installing : libXau-1.0.9-3.el8.x86_64 11/40 - Installing : libxcb-1.13.1-1.el8.x86_64 12/40 - Installing : libX11-common-1.6.8-5.el8.noarch 13/40 - Installing : libX11-1.6.8-5.el8.x86_64 14/40 - Installing : libXext-1.3.4-1.el8.x86_64 15/40 - Installing : libXi-1.7.10-1.el8.x86_64 16/40 - Installing : libXtst-1.2.3-7.el8.x86_64 17/40 - Installing : libXcomposite-0.4.4-14.el8.x86_64 18/40 - Installing : libXrender-0.9.10-7.el8.x86_64 19/40 - Installing : lcms2-2.9-2.el8.x86_64 20/40 - Running scriptlet: lcms2-2.9-2.el8.x86_64 20/40 - Installing : javapackages-filesystem-5.3.0-1.module+el8+5136+7f 21/40 - Installing : graphite2-1.3.10-10.el8.x86_64 22/40 - Installing : harfbuzz-1.7.5-3.el8.x86_64 23/40 - Running scriptlet: harfbuzz-1.7.5-3.el8.x86_64 23/40 - Installing : giflib-5.1.4-3.el8.x86_64 24/40 - Installing : alsa-lib-1.2.6.1-3.el8.x86_64 25/40 - Running scriptlet: alsa-lib-1.2.6.1-3.el8.x86_64 25/40 - Installing : pkgconf-m4-1.4.2-1.el8.noarch 26/40 - Installing : lksctp-tools-1.0.18-3.el8.x86_64 27/40 - Running scriptlet: lksctp-tools-1.0.18-3.el8.x86_64 27/40 - Installing : libpkgconf-1.4.2-1.el8.x86_64 28/40 - Installing : pkgconf-1.4.2-1.el8.x86_64 29/40 - Installing : pkgconf-pkg-config-1.4.2-1.el8.x86_64 30/40 - Installing : xorg-x11-font-utils-1:7.5-41.el8.x86_64 31/40 - Installing : xorg-x11-fonts-Type1-7.5-19.el8.noarch 32/40 - Running scriptlet: xorg-x11-fonts-Type1-7.5-19.el8.noarch 32/40 - Installing : crypto-policies-scripts-20211116-1.gitae470d6.el8. 33/40 - Installing : nss-sysinit-3.79.0-10.el8_6.x86_64 34/40 - Installing : nss-3.79.0-10.el8_6.x86_64 35/40 - Installing : avahi-libs-0.7-20.el8.x86_64 36/40 - Installing : cups-libs-1:2.2.6-45.el8_6.2.x86_64 37/40 - Installing : java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86 38/40 - Running scriptlet: java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86 38/40 - Installing : java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 39/40 - Running scriptlet: java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 39/40 - Installing : java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 40/40 - Running scriptlet: java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 40/40 - Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 40/40 - Running scriptlet: crypto-policies-scripts-20211116-1.gitae470d6.el8. 40/40 - Running scriptlet: nss-3.79.0-10.el8_6.x86_64 40/40 - Running scriptlet: java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86 40/40 - Running scriptlet: java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 40/40 - Running scriptlet: java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 40/40 - Verifying : avahi-libs-0.7-20.el8.x86_64 1/40 - Verifying : crypto-policies-scripts-20211116-1.gitae470d6.el8. 2/40 - Verifying : cups-libs-1:2.2.6-45.el8_6.2.x86_64 3/40 - Verifying : libpkgconf-1.4.2-1.el8.x86_64 4/40 - Verifying : lksctp-tools-1.0.18-3.el8.x86_64 5/40 - Verifying : pkgconf-1.4.2-1.el8.x86_64 6/40 - Verifying : pkgconf-m4-1.4.2-1.el8.noarch 7/40 - Verifying : pkgconf-pkg-config-1.4.2-1.el8.x86_64 8/40 - Verifying : alsa-lib-1.2.6.1-3.el8.x86_64 9/40 - Verifying : copy-jdk-configs-4.0-2.el8.noarch 10/40 - Verifying : giflib-5.1.4-3.el8.x86_64 11/40 - Verifying : graphite2-1.3.10-10.el8.x86_64 12/40 - Verifying : harfbuzz-1.7.5-3.el8.x86_64 13/40 - Verifying : java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 14/40 - Verifying : java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 15/40 - Verifying : java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86 16/40 - Verifying : javapackages-filesystem-5.3.0-1.module+el8+5136+7f 17/40 - Verifying : lcms2-2.9-2.el8.x86_64 18/40 - Verifying : libX11-1.6.8-5.el8.x86_64 19/40 - Verifying : libX11-common-1.6.8-5.el8.noarch 20/40 - Verifying : libXau-1.0.9-3.el8.x86_64 21/40 - Verifying : libXcomposite-0.4.4-14.el8.x86_64 22/40 - Verifying : libXext-1.3.4-1.el8.x86_64 23/40 - Verifying : libXi-1.7.10-1.el8.x86_64 24/40 - Verifying : libXrender-0.9.10-7.el8.x86_64 25/40 - Verifying : libXtst-1.2.3-7.el8.x86_64 26/40 - Verifying : libfontenc-1.1.3-8.el8.x86_64 27/40 - Verifying : libjpeg-turbo-1.5.3-12.el8.x86_64 28/40 - Verifying : libxcb-1.13.1-1.el8.x86_64 29/40 - Verifying : lua-5.3.4-12.el8.x86_64 30/40 - Verifying : nspr-4.34.0-3.el8_6.x86_64 31/40 - Verifying : nss-3.79.0-10.el8_6.x86_64 32/40 - Verifying : nss-softokn-3.79.0-10.el8_6.x86_64 33/40 - Verifying : nss-softokn-freebl-3.79.0-10.el8_6.x86_64 34/40 - Verifying : nss-sysinit-3.79.0-10.el8_6.x86_64 35/40 - Verifying : nss-util-3.79.0-10.el8_6.x86_64 36/40 - Verifying : ttmkfdir-3.0.9-54.el8.x86_64 37/40 - Verifying : tzdata-java-2022d-1.el8.noarch 38/40 - Verifying : xorg-x11-font-utils-1:7.5-41.el8.x86_64 39/40 - Verifying : xorg-x11-fonts-Type1-7.5-19.el8.noarch 40/40 + Installing : nspr-4.35.0-1.el8_8.x86_64 1/106 + Running scriptlet: nspr-4.35.0-1.el8_8.x86_64 1/106 + Installing : nss-util-3.90.0-7.el8_10.x86_64 2/106 + Installing : libjpeg-turbo-1.5.3-12.el8.x86_64 3/106 + Installing : pixman-0.38.4-4.el8.x86_64 4/106 + Installing : libwayland-client-1.21.0-1.el8.x86_64 5/106 + Installing : atk-2.28.1-1.el8.x86_64 6/106 + Installing : libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 + Running scriptlet: libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 + Installing : libcroco-0.6.12-4.el8_2.1.x86_64 8/106 + Running scriptlet: libcroco-0.6.12-4.el8_2.1.x86_64 8/106 + Installing : grub2-common-1:2.02-156.0.2.el8.noarch 9/106 + Installing : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 + Installing : gettext-0.19.8.1-17.el8.x86_64 11/106 + Running scriptlet: gettext-0.19.8.1-17.el8.x86_64 11/106 + Installing : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 12/106 + Installing : libwayland-cursor-1.21.0-1.el8.x86_64 13/106 + Installing : jasper-libs-2.0.14-5.el8.x86_64 14/106 + Installing : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 15/106 + Installing : nss-softokn-3.90.0-7.el8_10.x86_64 16/106 + Installing : xkeyboard-config-2.28-1.el8.noarch 17/106 + Installing : libxkbcommon-0.9.1-1.el8.x86_64 18/106 + Installing : tzdata-java-2024a-1.0.1.el8.noarch 19/106 + Installing : ttmkfdir-3.0.9-54.el8.x86_64 20/106 + Installing : lua-5.3.4-12.el8.x86_64 21/106 + Installing : copy-jdk-configs-4.0-2.el8.noarch 22/106 + Installing : libwayland-egl-1.21.0-1.el8.x86_64 23/106 + Installing : libfontenc-1.1.3-8.el8.x86_64 24/106 + Installing : libepoxy-1.5.8-1.el8.x86_64 25/106 + Installing : libdatrie-0.2.9-7.el8.x86_64 26/106 + Running scriptlet: libdatrie-0.2.9-7.el8.x86_64 26/106 + Installing : libthai-0.1.27-2.el8.x86_64 27/106 + Running scriptlet: libthai-0.1.27-2.el8.x86_64 27/106 + Installing : libXau-1.0.9-3.el8.x86_64 28/106 + Installing : libxcb-1.13.1-1.el8.x86_64 29/106 + Installing : libX11-common-1.6.8-8.el8.noarch 30/106 + Installing : libX11-1.6.8-8.el8.x86_64 31/106 + Installing : libXext-1.3.4-1.el8.x86_64 32/106 + Installing : libXrender-0.9.10-7.el8.x86_64 33/106 + Installing : cairo-1.15.12-6.el8.x86_64 34/106 + Installing : libXi-1.7.10-1.el8.x86_64 35/106 + Installing : libXfixes-5.0.3-7.el8.x86_64 36/106 + Installing : libXtst-1.2.3-7.el8.x86_64 37/106 + Installing : libXcomposite-0.4.4-14.el8.x86_64 38/106 + Installing : at-spi2-core-2.28.0-1.el8.x86_64 39/106 + Running scriptlet: at-spi2-core-2.28.0-1.el8.x86_64 39/106 + Installing : at-spi2-atk-2.26.2-1.el8.x86_64 40/106 + Running scriptlet: at-spi2-atk-2.26.2-1.el8.x86_64 40/106 + Installing : libXcursor-1.1.15-3.el8.x86_64 41/106 + Installing : libXdamage-1.1.4-14.el8.x86_64 42/106 + Installing : cairo-gobject-1.15.12-6.el8.x86_64 43/106 + Installing : libXft-2.3.3-1.el8.x86_64 44/106 + Installing : libXrandr-1.5.2-1.el8.x86_64 45/106 + Installing : libXinerama-1.1.4-1.el8.x86_64 46/106 + Installing : lcms2-2.9-2.el8.x86_64 47/106 + Running scriptlet: lcms2-2.9-2.el8.x86_64 47/106 + Installing : jbigkit-libs-2.1-14.el8.x86_64 48/106 + Running scriptlet: jbigkit-libs-2.1-14.el8.x86_64 48/106 + Installing : libtiff-4.0.9-32.el8_10.x86_64 49/106 + Installing : javapackages-filesystem-5.3.0-1.module+el8+5136+ 50/106 + Installing : hicolor-icon-theme-0.17-2.el8.noarch 51/106 + Installing : graphite2-1.3.10-10.el8.x86_64 52/106 + Installing : harfbuzz-1.7.5-4.el8.x86_64 53/106 + Running scriptlet: harfbuzz-1.7.5-4.el8.x86_64 53/106 + Installing : fribidi-1.0.4-9.el8.x86_64 54/106 + Installing : pango-1.42.4-8.el8.x86_64 55/106 + Running scriptlet: pango-1.42.4-8.el8.x86_64 55/106 + Installing : dconf-0.28.0-4.0.1.el8.x86_64 56/106 + Installing : alsa-lib-1.2.10-2.el8.x86_64 57/106 + Running scriptlet: alsa-lib-1.2.10-2.el8.x86_64 57/106 + Installing : adwaita-cursor-theme-3.28.0-3.el8.noarch 58/106 + Installing : adwaita-icon-theme-3.28.0-3.el8.noarch 59/106 + Installing : abattis-cantarell-fonts-0.0.25-6.el8.noarch 60/106 + Installing : xz-5.2.4-4.el8_6.x86_64 61/106 + Installing : shared-mime-info-1.9-4.el8.x86_64 62/106 + Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 62/106 + Installing : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 + Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 + Installing : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 64/106 + Installing : gtk-update-icon-cache-3.22.30-11.el8.x86_64 65/106 + Installing : pkgconf-m4-1.4.2-1.el8.noarch 66/106 + Installing : pigz-2.4-4.el8.x86_64 67/106 + Installing : memstrack-0.2.5-2.el8.x86_64 68/106 + Installing : lksctp-tools-1.0.18-3.el8.x86_64 69/106 + Running scriptlet: lksctp-tools-1.0.18-3.el8.x86_64 69/106 + Installing : libpkgconf-1.4.2-1.el8.x86_64 70/106 + Installing : pkgconf-1.4.2-1.el8.x86_64 71/106 + Installing : pkgconf-pkg-config-1.4.2-1.el8.x86_64 72/106 + Installing : xorg-x11-font-utils-1:7.5-41.el8.x86_64 73/106 + Installing : xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 + Running scriptlet: xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 + Installing : libmodman-2.0.1-17.el8.x86_64 75/106 + Running scriptlet: libmodman-2.0.1-17.el8.x86_64 75/106 + Installing : libproxy-0.4.15-5.2.el8.x86_64 76/106 + Running scriptlet: libproxy-0.4.15-5.2.el8.x86_64 76/106 + Installing : libkcapi-1.4.0-2.0.1.el8.x86_64 77/106 + Installing : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 78/106 + Installing : libgusb-0.3.0-1.el8.x86_64 79/106 + Installing : colord-libs-1.4.2-1.el8.x86_64 80/106 + Installing : kbd-misc-2.0.4-11.el8.noarch 81/106 + Installing : kbd-legacy-2.0.4-11.el8.noarch 82/106 + Installing : kbd-2.0.4-11.el8.x86_64 83/106 + Installing : systemd-udev-239-78.0.4.el8.x86_64 84/106 + Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 84/106 + Installing : os-prober-1.74-9.0.1.el8.x86_64 85/106 + Installing : json-glib-1.4.4-1.el8.x86_64 86/106 + Installing : hardlink-1:1.3-6.el8.x86_64 87/106 + Installing : file-5.33-25.el8.x86_64 88/106 + Installing : dejavu-sans-mono-fonts-2.35-7.el8.noarch 89/106 + Installing : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 90/106 + Installing : glib-networking-2.56.1-1.1.el8.x86_64 91/106 + Installing : libsoup-2.62.3-5.el8.x86_64 92/106 + Installing : rest-0.8.1-2.el8.x86_64 93/106 + Running scriptlet: rest-0.8.1-2.el8.x86_64 93/106 + Installing : cpio-2.12-11.el8.x86_64 94/106 + Installing : dracut-049-233.git20240115.0.1.el8.x86_64 95/106 + Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 + Installing : grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 + Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 + Installing : grubby-8.40-49.0.2.el8.x86_64 97/106 + Installing : crypto-policies-scripts-20230731-1.git3177e06.el 98/106 + Installing : nss-sysinit-3.90.0-7.el8_10.x86_64 99/106 + Installing : nss-3.90.0-7.el8_10.x86_64 100/106 + Installing : avahi-libs-0.7-27.el8.x86_64 101/106 + Installing : cups-libs-1:2.2.6-60.el8_10.x86_64 102/106 + Installing : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 + Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 + Installing : gtk3-3.22.30-11.el8.x86_64 104/106 + Installing : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 + Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 + Installing : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 + Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 + Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 106/106 + Running scriptlet: dconf-0.28.0-4.0.1.el8.x86_64 106/106 + Running scriptlet: crypto-policies-scripts-20230731-1.git3177e06.el 106/106 + Running scriptlet: nss-3.90.0-7.el8_10.x86_64 106/106 + Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 106/106 + Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 106/106 + Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 + Running scriptlet: hicolor-icon-theme-0.17-2.el8.noarch 106/106 + Running scriptlet: adwaita-icon-theme-3.28.0-3.el8.noarch 106/106 + Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 106/106 + Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 106/106 + Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 106/106 + Verifying : avahi-libs-0.7-27.el8.x86_64 1/106 + Verifying : cpio-2.12-11.el8.x86_64 2/106 + Verifying : crypto-policies-scripts-20230731-1.git3177e06.el 3/106 + Verifying : cups-libs-1:2.2.6-60.el8_10.x86_64 4/106 + Verifying : dejavu-sans-mono-fonts-2.35-7.el8.noarch 5/106 + Verifying : dracut-049-233.git20240115.0.1.el8.x86_64 6/106 + Verifying : file-5.33-25.el8.x86_64 7/106 + Verifying : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 8/106 + Verifying : gettext-0.19.8.1-17.el8.x86_64 9/106 + Verifying : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 + Verifying : glib-networking-2.56.1-1.1.el8.x86_64 11/106 + Verifying : grub2-common-1:2.02-156.0.2.el8.noarch 12/106 + Verifying : grub2-tools-1:2.02-156.0.2.el8.x86_64 13/106 + Verifying : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 14/106 + Verifying : grubby-8.40-49.0.2.el8.x86_64 15/106 + Verifying : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 16/106 + Verifying : hardlink-1:1.3-6.el8.x86_64 17/106 + Verifying : json-glib-1.4.4-1.el8.x86_64 18/106 + Verifying : kbd-2.0.4-11.el8.x86_64 19/106 + Verifying : kbd-legacy-2.0.4-11.el8.noarch 20/106 + Verifying : kbd-misc-2.0.4-11.el8.noarch 21/106 + Verifying : libcroco-0.6.12-4.el8_2.1.x86_64 22/106 + Verifying : libgomp-8.5.0-22.0.1.el8_10.x86_64 23/106 + Verifying : libgusb-0.3.0-1.el8.x86_64 24/106 + Verifying : libkcapi-1.4.0-2.0.1.el8.x86_64 25/106 + Verifying : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 26/106 + Verifying : libmodman-2.0.1-17.el8.x86_64 27/106 + Verifying : libpkgconf-1.4.2-1.el8.x86_64 28/106 + Verifying : libproxy-0.4.15-5.2.el8.x86_64 29/106 + Verifying : libsoup-2.62.3-5.el8.x86_64 30/106 + Verifying : lksctp-tools-1.0.18-3.el8.x86_64 31/106 + Verifying : memstrack-0.2.5-2.el8.x86_64 32/106 + Verifying : os-prober-1.74-9.0.1.el8.x86_64 33/106 + Verifying : pigz-2.4-4.el8.x86_64 34/106 + Verifying : pkgconf-1.4.2-1.el8.x86_64 35/106 + Verifying : pkgconf-m4-1.4.2-1.el8.noarch 36/106 + Verifying : pkgconf-pkg-config-1.4.2-1.el8.x86_64 37/106 + Verifying : shared-mime-info-1.9-4.el8.x86_64 38/106 + Verifying : systemd-udev-239-78.0.4.el8.x86_64 39/106 + Verifying : xz-5.2.4-4.el8_6.x86_64 40/106 + Verifying : abattis-cantarell-fonts-0.0.25-6.el8.noarch 41/106 + Verifying : adwaita-cursor-theme-3.28.0-3.el8.noarch 42/106 + Verifying : adwaita-icon-theme-3.28.0-3.el8.noarch 43/106 + Verifying : alsa-lib-1.2.10-2.el8.x86_64 44/106 + Verifying : at-spi2-atk-2.26.2-1.el8.x86_64 45/106 + Verifying : at-spi2-core-2.28.0-1.el8.x86_64 46/106 + Verifying : atk-2.28.1-1.el8.x86_64 47/106 + Verifying : cairo-1.15.12-6.el8.x86_64 48/106 + Verifying : cairo-gobject-1.15.12-6.el8.x86_64 49/106 + Verifying : colord-libs-1.4.2-1.el8.x86_64 50/106 + Verifying : copy-jdk-configs-4.0-2.el8.noarch 51/106 + Verifying : dconf-0.28.0-4.0.1.el8.x86_64 52/106 + Verifying : fribidi-1.0.4-9.el8.x86_64 53/106 + Verifying : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 54/106 + Verifying : graphite2-1.3.10-10.el8.x86_64 55/106 + Verifying : gtk-update-icon-cache-3.22.30-11.el8.x86_64 56/106 + Verifying : gtk3-3.22.30-11.el8.x86_64 57/106 + Verifying : harfbuzz-1.7.5-4.el8.x86_64 58/106 + Verifying : hicolor-icon-theme-0.17-2.el8.noarch 59/106 + Verifying : jasper-libs-2.0.14-5.el8.x86_64 60/106 + Verifying : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 61/106 + Verifying : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 62/106 + Verifying : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 63/106 + Verifying : javapackages-filesystem-5.3.0-1.module+el8+5136+ 64/106 + Verifying : jbigkit-libs-2.1-14.el8.x86_64 65/106 + Verifying : lcms2-2.9-2.el8.x86_64 66/106 + Verifying : libX11-1.6.8-8.el8.x86_64 67/106 + Verifying : libX11-common-1.6.8-8.el8.noarch 68/106 + Verifying : libXau-1.0.9-3.el8.x86_64 69/106 + Verifying : libXcomposite-0.4.4-14.el8.x86_64 70/106 + Verifying : libXcursor-1.1.15-3.el8.x86_64 71/106 + Verifying : libXdamage-1.1.4-14.el8.x86_64 72/106 + Verifying : libXext-1.3.4-1.el8.x86_64 73/106 + Verifying : libXfixes-5.0.3-7.el8.x86_64 74/106 + Verifying : libXft-2.3.3-1.el8.x86_64 75/106 + Verifying : libXi-1.7.10-1.el8.x86_64 76/106 + Verifying : libXinerama-1.1.4-1.el8.x86_64 77/106 + Verifying : libXrandr-1.5.2-1.el8.x86_64 78/106 + Verifying : libXrender-0.9.10-7.el8.x86_64 79/106 + Verifying : libXtst-1.2.3-7.el8.x86_64 80/106 + Verifying : libdatrie-0.2.9-7.el8.x86_64 81/106 + Verifying : libepoxy-1.5.8-1.el8.x86_64 82/106 + Verifying : libfontenc-1.1.3-8.el8.x86_64 83/106 + Verifying : libjpeg-turbo-1.5.3-12.el8.x86_64 84/106 + Verifying : libthai-0.1.27-2.el8.x86_64 85/106 + Verifying : libtiff-4.0.9-32.el8_10.x86_64 86/106 + Verifying : libwayland-client-1.21.0-1.el8.x86_64 87/106 + Verifying : libwayland-cursor-1.21.0-1.el8.x86_64 88/106 + Verifying : libwayland-egl-1.21.0-1.el8.x86_64 89/106 + Verifying : libxcb-1.13.1-1.el8.x86_64 90/106 + Verifying : libxkbcommon-0.9.1-1.el8.x86_64 91/106 + Verifying : lua-5.3.4-12.el8.x86_64 92/106 + Verifying : nspr-4.35.0-1.el8_8.x86_64 93/106 + Verifying : nss-3.90.0-7.el8_10.x86_64 94/106 + Verifying : nss-softokn-3.90.0-7.el8_10.x86_64 95/106 + Verifying : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 96/106 + Verifying : nss-sysinit-3.90.0-7.el8_10.x86_64 97/106 + Verifying : nss-util-3.90.0-7.el8_10.x86_64 98/106 + Verifying : pango-1.42.4-8.el8.x86_64 99/106 + Verifying : pixman-0.38.4-4.el8.x86_64 100/106 + Verifying : rest-0.8.1-2.el8.x86_64 101/106 + Verifying : ttmkfdir-3.0.9-54.el8.x86_64 102/106 + Verifying : tzdata-java-2024a-1.0.1.el8.noarch 103/106 + Verifying : xkeyboard-config-2.28-1.el8.noarch 104/106 + Verifying : xorg-x11-font-utils-1:7.5-41.el8.x86_64 105/106 + Verifying : xorg-x11-fonts-Type1-7.5-19.el8.noarch 106/106 Installed: - alsa-lib-1.2.6.1-3.el8.x86_64 - avahi-libs-0.7-20.el8.x86_64 + abattis-cantarell-fonts-0.0.25-6.el8.noarch + adwaita-cursor-theme-3.28.0-3.el8.noarch + adwaita-icon-theme-3.28.0-3.el8.noarch + alsa-lib-1.2.10-2.el8.x86_64 + at-spi2-atk-2.26.2-1.el8.x86_64 + at-spi2-core-2.28.0-1.el8.x86_64 + atk-2.28.1-1.el8.x86_64 + avahi-libs-0.7-27.el8.x86_64 + cairo-1.15.12-6.el8.x86_64 + cairo-gobject-1.15.12-6.el8.x86_64 + colord-libs-1.4.2-1.el8.x86_64 copy-jdk-configs-4.0-2.el8.noarch - crypto-policies-scripts-20211116-1.gitae470d6.el8.noarch - cups-libs-1:2.2.6-45.el8_6.2.x86_64 - giflib-5.1.4-3.el8.x86_64 + cpio-2.12-11.el8.x86_64 + crypto-policies-scripts-20230731-1.git3177e06.el8.noarch + cups-libs-1:2.2.6-60.el8_10.x86_64 + dconf-0.28.0-4.0.1.el8.x86_64 + dejavu-sans-mono-fonts-2.35-7.el8.noarch + dracut-049-233.git20240115.0.1.el8.x86_64 + file-5.33-25.el8.x86_64 + fribidi-1.0.4-9.el8.x86_64 + gdk-pixbuf2-2.36.12-6.el8_10.x86_64 + gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 + gettext-0.19.8.1-17.el8.x86_64 + gettext-libs-0.19.8.1-17.el8.x86_64 + glib-networking-2.56.1-1.1.el8.x86_64 graphite2-1.3.10-10.el8.x86_64 - harfbuzz-1.7.5-3.el8.x86_64 - java-11-openjdk-1:11.0.16.1.1-1.el8_6.x86_64 - java-11-openjdk-devel-1:11.0.16.1.1-1.el8_6.x86_64 - java-11-openjdk-headless-1:11.0.16.1.1-1.el8_6.x86_64 + grub2-common-1:2.02-156.0.2.el8.noarch + grub2-tools-1:2.02-156.0.2.el8.x86_64 + grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 + grubby-8.40-49.0.2.el8.x86_64 + gsettings-desktop-schemas-3.32.0-6.el8.x86_64 + gtk-update-icon-cache-3.22.30-11.el8.x86_64 + gtk3-3.22.30-11.el8.x86_64 + hardlink-1:1.3-6.el8.x86_64 + harfbuzz-1.7.5-4.el8.x86_64 + hicolor-icon-theme-0.17-2.el8.noarch + jasper-libs-2.0.14-5.el8.x86_64 + java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 + java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x86_64 + java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86_64 javapackages-filesystem-5.3.0-1.module+el8+5136+7ff78f74.noarch + jbigkit-libs-2.1-14.el8.x86_64 + json-glib-1.4.4-1.el8.x86_64 + kbd-2.0.4-11.el8.x86_64 + kbd-legacy-2.0.4-11.el8.noarch + kbd-misc-2.0.4-11.el8.noarch lcms2-2.9-2.el8.x86_64 - libX11-1.6.8-5.el8.x86_64 - libX11-common-1.6.8-5.el8.noarch + libX11-1.6.8-8.el8.x86_64 + libX11-common-1.6.8-8.el8.noarch libXau-1.0.9-3.el8.x86_64 libXcomposite-0.4.4-14.el8.x86_64 + libXcursor-1.1.15-3.el8.x86_64 + libXdamage-1.1.4-14.el8.x86_64 libXext-1.3.4-1.el8.x86_64 + libXfixes-5.0.3-7.el8.x86_64 + libXft-2.3.3-1.el8.x86_64 libXi-1.7.10-1.el8.x86_64 + libXinerama-1.1.4-1.el8.x86_64 + libXrandr-1.5.2-1.el8.x86_64 libXrender-0.9.10-7.el8.x86_64 libXtst-1.2.3-7.el8.x86_64 + libcroco-0.6.12-4.el8_2.1.x86_64 + libdatrie-0.2.9-7.el8.x86_64 + libepoxy-1.5.8-1.el8.x86_64 libfontenc-1.1.3-8.el8.x86_64 + libgomp-8.5.0-22.0.1.el8_10.x86_64 + libgusb-0.3.0-1.el8.x86_64 libjpeg-turbo-1.5.3-12.el8.x86_64 + libkcapi-1.4.0-2.0.1.el8.x86_64 + libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 + libmodman-2.0.1-17.el8.x86_64 libpkgconf-1.4.2-1.el8.x86_64 + libproxy-0.4.15-5.2.el8.x86_64 + libsoup-2.62.3-5.el8.x86_64 + libthai-0.1.27-2.el8.x86_64 + libtiff-4.0.9-32.el8_10.x86_64 + libwayland-client-1.21.0-1.el8.x86_64 + libwayland-cursor-1.21.0-1.el8.x86_64 + libwayland-egl-1.21.0-1.el8.x86_64 libxcb-1.13.1-1.el8.x86_64 + libxkbcommon-0.9.1-1.el8.x86_64 lksctp-tools-1.0.18-3.el8.x86_64 lua-5.3.4-12.el8.x86_64 - nspr-4.34.0-3.el8_6.x86_64 - nss-3.79.0-10.el8_6.x86_64 - nss-softokn-3.79.0-10.el8_6.x86_64 - nss-softokn-freebl-3.79.0-10.el8_6.x86_64 - nss-sysinit-3.79.0-10.el8_6.x86_64 - nss-util-3.79.0-10.el8_6.x86_64 + memstrack-0.2.5-2.el8.x86_64 + nspr-4.35.0-1.el8_8.x86_64 + nss-3.90.0-7.el8_10.x86_64 + nss-softokn-3.90.0-7.el8_10.x86_64 + nss-softokn-freebl-3.90.0-7.el8_10.x86_64 + nss-sysinit-3.90.0-7.el8_10.x86_64 + nss-util-3.90.0-7.el8_10.x86_64 + os-prober-1.74-9.0.1.el8.x86_64 + pango-1.42.4-8.el8.x86_64 + pigz-2.4-4.el8.x86_64 + pixman-0.38.4-4.el8.x86_64 pkgconf-1.4.2-1.el8.x86_64 pkgconf-m4-1.4.2-1.el8.noarch pkgconf-pkg-config-1.4.2-1.el8.x86_64 + rest-0.8.1-2.el8.x86_64 + shared-mime-info-1.9-4.el8.x86_64 + systemd-udev-239-78.0.4.el8.x86_64 ttmkfdir-3.0.9-54.el8.x86_64 - tzdata-java-2022d-1.el8.noarch + tzdata-java-2024a-1.0.1.el8.noarch + xkeyboard-config-2.28-1.el8.noarch xorg-x11-font-utils-1:7.5-41.el8.x86_64 xorg-x11-fonts-Type1-7.5-19.el8.noarch + xz-5.2.4-4.el8_6.x86_64 Complete! -Last metadata expiration check: 0:00:10 ago on Mon 10 Oct 2022 04:06:28 PM UTC. +Last metadata expiration check: 0:00:23 ago on Tue 20 Aug 2024 08:55:14 AM UTC. +Package iproute-6.2.0-5.el8_9.x86_64 is already installed. Dependencies resolved. -============================================================================================== - Package - Arch Version Repository Size -============================================================================================== -Installing: - ords noarch 22.3.0-7.el8 yum.oracle.com_repo_OracleLinux_OL8_oracle_software_x86_64 87 M -Installing dependencies: - lsof x86_64 4.93.2-1.el8 ol8_baseos_latest 253 k +================================================================================ + Package Architecture Version Repository Size +================================================================================ +Upgrading: + iproute x86_64 6.2.0-6.el8_10 ol8_baseos_latest 853 k Transaction Summary -============================================================================================== -Install 2 Packages +================================================================================ +Upgrade 1 Package -Total download size: 87 M -Installed size: 92 M +Total download size: 853 k Downloading Packages: -(1/2): lsof-4.93.2-1.el8.x86_64.rpm 3.0 MB/s | 253 kB 00:00 -(2/2): ords-22.3.0-7.el8.noarch.rpm 66 MB/s | 87 MB 00:01 +iproute-6.2.0-6.el8_10.x86_64.rpm 4.2 MB/s | 853 kB 00:00 -------------------------------------------------------------------------------- -Total 66 MB/s | 87 MB 00:01 +Total 4.2 MB/s | 853 kB 00:00 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 - Installing : lsof-4.93.2-1.el8.x86_64 1/2 - Running scriptlet: ords-22.3.0-7.el8.noarch 2/2 - Installing : ords-22.3.0-7.el8.noarch 2/2 - Running scriptlet: ords-22.3.0-7.el8.noarch 2/2 -INFO: Before starting ORDS service, run the below command as user oracle: - ords --config /etc/ords/config install + Upgrading : iproute-6.2.0-6.el8_10.x86_64 1/2 + Cleanup : iproute-6.2.0-5.el8_9.x86_64 2/2 + Running scriptlet: iproute-6.2.0-5.el8_9.x86_64 2/2 + Verifying : iproute-6.2.0-6.el8_10.x86_64 1/2 + Verifying : iproute-6.2.0-5.el8_9.x86_64 2/2 - Verifying : lsof-4.93.2-1.el8.x86_64 1/2 - Verifying : ords-22.3.0-7.el8.noarch 2/2 - -Installed: - lsof-4.93.2-1.el8.x86_64 ords-22.3.0-7.el8.noarch +Upgraded: + iproute-6.2.0-6.el8_10.x86_64 -Complete! -Last metadata expiration check: 0:00:15 ago on Mon 10 Oct 2022 04:06:28 PM UTC. -Package iproute-5.15.0-4.el8_6.1.x86_64 is already installed. -Dependencies resolved. -Nothing to do. Complete! 24 files removed -Removing intermediate container d38a69d2cc70 - ---> 3a7b8edb327e -Step 5/10 : RUN mkdir -p $ORDS_HOME/doc_root && mkdir -p $ORDS_HOME/error && mkdir -p $ORDS_HOME/secrets && chmod ug+x $ORDS_HOME/*.sh && groupadd -g 54322 dba && usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && chown -R oracle:dba $ORDS_HOME && echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - ---> Running in 1d05951f8252 -Removing intermediate container 1d05951f8252 - ---> 265cb7ab4f2c -Step 6/10 : USER oracle - ---> Running in 180d432ae42d -Removing intermediate container 180d432ae42d - ---> a9caee3d9426 -Step 7/10 : WORKDIR /home/oracle - ---> Running in bf8ac95c724a -Removing intermediate container bf8ac95c724a - ---> 4623d696e603 -Step 8/10 : VOLUME ["$ORDS_HOME/config/ords"] - ---> Running in 3afce627e4c0 -Removing intermediate container 3afce627e4c0 - ---> 914d4ee42ede -Step 9/10 : EXPOSE 8888 - ---> Running in 13460b132c52 -Removing intermediate container 13460b132c52 - ---> 4c9edba5aade -Step 10/10 : CMD $ORDS_HOME/$RUN_FILE - ---> Running in f97b17d8cea4 -Removing intermediate container f97b17d8cea4 - ---> c8e95aadf5e3 -Successfully built c8e95aadf5e3 +Removing intermediate container fe168b01f3ad + ---> 791878694a50 +Step 5/12 : RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm + ---> Running in 59d7143da358 + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 108M 100 108M 0 0 1440k 0 0:01:16 0:01:16 --:--:-- 1578k +Removing intermediate container 59d7143da358 + ---> 17c4534293e5 +Step 6/12 : RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm + ---> Running in 84b1cbffdc51 +Verifying... ######################################## +Preparing... ######################################## +Updating / installing... +ords-23.4.0-8.el8 ######################################## +INFO: Before starting ORDS service, run the below command as user oracle: + ords --config /etc/ords/config install +Removing intermediate container 84b1cbffdc51 + ---> 6e7151b79588 +Step 7/12 : RUN mkdir -p $ORDS_HOME/doc_root && mkdir -p $ORDS_HOME/error && mkdir -p $ORDS_HOME/secrets && chmod ug+x $ORDS_HOME/*.sh && groupadd -g 54322 dba && usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && chown -R oracle:dba $ORDS_HOME && echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + ---> Running in 66e5db5f343f +Removing intermediate container 66e5db5f343f + ---> 0523dc897bf4 +Step 8/12 : USER oracle + ---> Running in ffda8495ac77 +Removing intermediate container ffda8495ac77 + ---> 162acd4d0b93 +Step 9/12 : WORKDIR /home/oracle + ---> Running in 8c14310ffbc7 +Removing intermediate container 8c14310ffbc7 + ---> c8dae809e772 +Step 10/12 : VOLUME ["$ORDS_HOME/config/ords"] + ---> Running in ed64548fd997 +Removing intermediate container ed64548fd997 + ---> 22e2c99247b0 +Step 11/12 : EXPOSE 8888 + ---> Running in 921f7c85d61d +Removing intermediate container 921f7c85d61d + ---> e5d503c92224 +Step 12/12 : CMD $ORDS_HOME/$RUN_FILE + ---> Running in cad487298d63 +Removing intermediate container cad487298d63 + ---> fdb17aa242f8 +Successfully built fdb17aa242f8 Successfully tagged oracle/ords-dboper:latest +08:57:18 oracle@mitk01:# diff --git a/docs/multitenant/usecase01/logfiles/cdb_creation.log b/docs/multitenant/usecase01/logfiles/cdb_creation.log new file mode 100644 index 00000000..b4602f54 --- /dev/null +++ b/docs/multitenant/usecase01/logfiles/cdb_creation.log @@ -0,0 +1,357 @@ +/usr/local/go/bin/kubectl logs -f `/usr/local/go/bin/kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. +ORDSVERSIN:23.4.0-8 +NOT_INSTALLED=2 + SETUP +==================================================== +CONFIG=/etc/ords/config +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:16 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.connectionType was set to: customurl in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:18 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.customURL was set to: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:20 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: security.requestValidationFunction was set to: false in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:22 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: jdbc.MaxLimit was set to: 100 in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:24 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: jdbc.InitialLimit was set to: 50 in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:25 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: error.externalPath was set to: /opt/oracle/ords/error +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:27 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.access.log was set to: /home/oracle +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:29 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.https.port was set to: 8888 +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:31 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.https.cert was set to: /opt/oracle/ords//secrets/tls.crt +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:33 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.https.cert.key was set to: /opt/oracle/ords//secrets/tls.key +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:35 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: restEnabledSql.active was set to: true in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:37 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: security.verifySSL was set to: true +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:39 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: database.api.enabled was set to: true +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:41 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: plsql.gateway.mode was set to: disabled in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:43 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: database.api.management.services.disabled was set to: false +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:45 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: misc.pagination.maxRows was set to: 1000 in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:47 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.cdb.adminUser was set to: C##DBAPI_CDB_ADMIN AS SYSDBA in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:49 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.cdb.adminUser.password was set to: ****** in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:51 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +Created user welcome in file /etc/ords/config/global/credentials +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:53 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +Oracle REST Data Services - Non-Interactive Install + +Retrieving information... +Completed verifying Oracle REST Data Services schema version 23.4.0.r3461619. +Connecting to database user: ORDS_PUBLIC_USER url: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) +The setting named: db.serviceNameSuffix was set to: in configuration: default +The setting named: db.username was set to: ORDS_PUBLIC_USER in configuration: default +The setting named: db.password was set to: ****** in configuration: default +The setting named: security.requestValidationFunction was set to: ords_util.authorize_plsql_gateway in configuration: default +2024-08-20T07:21:57.563Z INFO Oracle REST Data Services schema version 23.4.0.r3461619 is installed. +2024-08-20T07:21:57.565Z INFO To run in standalone mode, use the ords serve command: +2024-08-20T07:21:57.565Z INFO ords --config /etc/ords/config serve +2024-08-20T07:21:57.565Z INFO Visit the ORDS Documentation to access tutorials, developer guides and more to help you get started with the new ORDS Command Line Interface (http://oracle.com/rest). +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.4 Production on Tue Aug 20 07:21:59 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +2024-08-20T07:21:59.739Z INFO HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8080 +2024-08-20T07:21:59.741Z INFO HTTPS and HTTPS/2 listening on host: 0.0.0.0 port: 8888 +2024-08-20T07:21:59.765Z INFO Disabling document root because the specified folder does not exist: /etc/ords/config/global/doc_root +2024-08-20T07:21:59.765Z INFO Default forwarding from / to contextRoot configured. +2024-08-20T07:22:05.313Z INFO Configuration properties for: |default|lo| +db.serviceNameSuffix= +java.specification.version=22 +conf.use.wallet=true +database.api.management.services.disabled=false +sun.jnu.encoding=UTF-8 +user.region=US +java.class.path=/opt/oracle/ords/ords.war +java.vm.vendor=Oracle Corporation +standalone.https.cert.key=/opt/oracle/ords//secrets/tls.key +sun.arch.data.model=64 +nashorn.args=--no-deprecation-warning +java.vendor.url=https://java.oracle.com/ +resource.templates.enabled=false +user.timezone=UTC +java.vm.specification.version=22 +os.name=Linux +sun.java.launcher=SUN_STANDARD +user.country=US +sun.boot.library.path=/usr/java/jdk-22/lib +sun.java.command=/opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure +jdk.debug=release +sun.cpu.endian=little +user.home=/home/oracle +oracle.dbtools.launcher.executable.jar.path=/opt/oracle/ords/ords.war +user.language=en +db.cdb.adminUser.password=****** +java.specification.vendor=Oracle Corporation +java.version.date=2024-07-16 +database.api.enabled=true +java.home=/usr/java/jdk-22 +db.username=ORDS_PUBLIC_USER +file.separator=/ +java.vm.compressedOopsMode=32-bit +line.separator= + +restEnabledSql.active=true +java.specification.name=Java Platform API Specification +java.vm.specification.vendor=Oracle Corporation +java.awt.headless=true +standalone.https.cert=/opt/oracle/ords//secrets/tls.crt +db.password=****** +sun.management.compiler=HotSpot 64-Bit Tiered Compilers +security.requestValidationFunction=ords_util.authorize_plsql_gateway +misc.pagination.maxRows=1000 +java.runtime.version=22.0.2+9-70 +user.name=oracle +error.externalPath=/opt/oracle/ords/error +stdout.encoding=UTF-8 +path.separator=: +db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA +os.version=5.4.17-2136.329.3.1.el7uek.x86_64 +java.runtime.name=Java(TM) SE Runtime Environment +file.encoding=UTF-8 +plsql.gateway.mode=disabled +security.verifySSL=true +standalone.https.port=8888 +java.vm.name=Java HotSpot(TM) 64-Bit Server VM +java.vendor.url.bug=https://bugreport.java.com/bugreport/ +java.io.tmpdir=/tmp +oracle.dbtools.cmdline.ShellCommand=ords +java.version=22.0.2 +user.dir=/home/oracle +os.arch=amd64 +java.vm.specification.name=Java Virtual Machine Specification +jdbc.MaxLimit=100 +oracle.dbtools.cmdline.home=/opt/oracle/ords +native.encoding=UTF-8 +java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib +java.vendor=Oracle Corporation +java.vm.info=mixed mode, sharing +stderr.encoding=UTF-8 +java.vm.version=22.0.2+9-70 +sun.io.unicode.encoding=UnicodeLittle +jdbc.InitialLimit=50 +db.connectionType=customurl +java.class.version=66.0 +db.customURL=jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) +standalone.access.log=/home/oracle + +2024-08-20T07:22:09.268Z INFO + +Mapped local pools from /etc/ords/config/databases: + /ords/ => default => VALID + + +2024-08-20T07:22:09.414Z INFO Oracle REST Data Services initialized +Oracle REST Data Services version : 23.4.0.r3461619 +Oracle REST Data Services server info: jetty/10.0.18 +Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 22.0.2+9-70 + diff --git a/docs/multitenant/usecase01/logfiles/openssl_execution.log b/docs/multitenant/usecase01/logfiles/openssl_execution.log index 30a1c5d4..e3915a21 100644 --- a/docs/multitenant/usecase01/logfiles/openssl_execution.log +++ b/docs/multitenant/usecase01/logfiles/openssl_execution.log @@ -1,22 +1,19 @@ +CREATING TLS CERTIFICATES /usr/bin/openssl genrsa -out ca.key 2048 -Generating RSA private key, 2048 bit long modulus -......................................................................................................................................................................................+++ -...................................+++ -e is 65537 (0x10001) -/usr/bin/openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out ca.crt -/usr/bin/openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=cdb-dev-ords" -out server.csr -Generating a 2048 bit RSA private key -...................................+++ -........................................+++ +Generating RSA private key, 2048 bit long modulus (2 primes) +......................+++++ +..................................................+++++ +e is 65537 (0x010001) +/usr/bin/openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost Root CA " -out ca.crt +/usr/bin/openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost" -out server.csr +Generating a RSA private key +...........+++++ +...........................................+++++ writing new private key to 'tls.key' ----- -/usr/bin/echo "subjectAltName=DNS:cdb-dev-ords,DNS:www.example.com" > extfile.txt +/usr/bin/echo "subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com" > extfile.txt /usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt Signature ok -subject=/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=cdb-dev-ords +subject=C = US, ST = California, L = SanFrancisco, O = "oracle ", CN = "cdb-dev-ords.oracle-database-operator-system ", CN = localhost Getting CA Private Key -/usr/bin/kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system -secret/db-tls created -/usr/bin/kubectl create secret generic db-ca --from-file="ca.crt" -n oracle-database-operator-system -secret/db-ca created diff --git a/docs/multitenant/usecase01/logfiles/ordsconfig.log b/docs/multitenant/usecase01/logfiles/ordsconfig.log index ad5e7bab..b787b752 100644 --- a/docs/multitenant/usecase01/logfiles/ordsconfig.log +++ b/docs/multitenant/usecase01/logfiles/ordsconfig.log @@ -1,35 +1,39 @@ -: Release 22.3 Production on Tue Oct 11 12:51:50 2022 +ORDS: Release 23.4 Production on Tue Aug 20 07:48:44 2024 -Copyright (c) 2010, 2022, Oracle. +Copyright (c) 2010, 2024, Oracle. Configuration: /etc/ords/config/ Database pool: default -Setting Value Source ------------------------------------------ -------------------------------------- ----------- -database.api.enabled true Global -database.api.management.services.disabled false Global -db.cdb.adminUser C##DBAPI_CDB_ADMIN AS SYSDBA Pool -db.cdb.adminUser.password ****** Pool Wallet -db.connectionType basic Pool -db.hostname racnode1.testrac.com Pool -db.password ****** Pool Wallet -db.port 1521 Pool -db.serviceNameSuffix Pool -db.servicename TESTORDS Pool -db.username ORDS_PUBLIC_USER Pool -error.externalPath /opt/oracle/ords/error Global -jdbc.InitialLimit 50 Pool -jdbc.MaxLimit 100 Pool -misc.pagination.maxRows 1000 Pool -plsql.gateway.mode proxied Pool -restEnabledSql.active true Pool -security.requestValidationFunction wwv_flow_epg_include_modules.authorize Pool -security.verifySSL true Global -standalone.access.log /home/oracle Global -standalone.https.cert /opt/oracle/ords//secrets/tls.crt Global -standalone.https.cert.key /opt/oracle/ords//secrets/tls.key Global -standalone.https.port 8888 Global +Setting Value Source +----------------------------------------- -------------------------------------------------- ----------- +database.api.enabled true Global +database.api.management.services.disabled false Global +db.cdb.adminUser C##DBAPI_CDB_ADMIN AS SYSDBA Pool +db.cdb.adminUser.password ****** Pool Wallet +db.connectionType customurl Pool +db.customURL jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90 Pool + )(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNEC + T_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL= + TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONL + Y))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST= + scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNEC + T_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) +db.password ****** Pool Wallet +db.serviceNameSuffix Pool +db.username ORDS_PUBLIC_USER Pool +error.externalPath /opt/oracle/ords/error Global +jdbc.InitialLimit 50 Pool +jdbc.MaxLimit 100 Pool +misc.pagination.maxRows 1000 Pool +plsql.gateway.mode disabled Pool +restEnabledSql.active true Pool +security.requestValidationFunction ords_util.authorize_plsql_gateway Pool +security.verifySSL true Global +standalone.access.log /home/oracle Global +standalone.https.cert /opt/oracle/ords//secrets/tls.crt Global +standalone.https.cert.key /opt/oracle/ords//secrets/tls.key Global +standalone.https.port 8888 Global diff --git a/docs/multitenant/usecase01/logfiles/tagandpush.log b/docs/multitenant/usecase01/logfiles/tagandpush.log new file mode 100644 index 00000000..232d5bb2 --- /dev/null +++ b/docs/multitenant/usecase01/logfiles/tagandpush.log @@ -0,0 +1,14 @@ +/usr/bin/docker tag oracle/ords-dboper:latest [.......]/ords-dboper:latest + +/usr/bin/docker push [your container registry]/ords-dboper:latest +The push refers to repository [your container registry] +0405aac3af1c: Pushed +6be46e8e1e21: Pushed +c9884830a66d: Pushed +a46244557bb9: Pushing [===========================> ] 261.8MB/469.9MB +f988845e261e: Pushed +fe07ec0b1f5a: Layer already exists +2ac63de5f950: Layer already exists +386cd7a64c01: Layer already exists +826c69252b8b: Layer already exists + diff --git a/docs/multitenant/usecase01/logfiles/testapi.log b/docs/multitenant/usecase01/logfiles/testapi.log index 4c95b457..cb42ecc3 100644 --- a/docs/multitenant/usecase01/logfiles/testapi.log +++ b/docs/multitenant/usecase01/logfiles/testapi.log @@ -1,6 +1,7 @@ -* Trying 127.0.0.1... +kubectl exec -it `kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system -i -t -- /usr/bin/curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ +* Trying ::1... * TCP_NODELAY set -* Connected to localhost (127.0.0.1) port 8888 (#0) +* Connected to localhost (::1) port 8888 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: @@ -19,10 +20,10 @@ * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: -* subject: C=CN; ST=GD; L=SZ; O=oracle, Inc.; CN=cdb-dev-ords -* start date: Oct 11 07:44:38 2022 GMT -* expire date: Oct 11 07:44:38 2023 GMT -* issuer: C=CN; ST=GD; L=SZ; O=oracle, Inc.; CN=oracle Root CA +* subject: C=US; ST=California; L=SanFrancisco; O=oracle ; CN=cdb-dev-ords.oracle-database-operator-system ; CN=localhost +* start date: Aug 20 07:14:04 2024 GMT +* expire date: Aug 20 07:14:04 2025 GMT +* issuer: C=US; ST=California; L=SanFrancisco; O=oracle ; CN=cdb-dev-ords.oracle-database-operator-system ; CN=localhost Root CA * SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) @@ -30,7 +31,7 @@ * TLSv1.3 (OUT), TLS app data, [no content] (0): * TLSv1.3 (OUT), TLS app data, [no content] (0): * TLSv1.3 (OUT), TLS app data, [no content] (0): -* Using Stream ID: 1 (easy handle 0x564be7b0b970) +* Using Stream ID: 1 (easy handle 0x55d14a7dea90) * TLSv1.3 (OUT), TLS app data, [no content] (0): > GET /ords/_/db-api/stable/metadata-catalog/ HTTP/2 > Host: localhost:8888 @@ -46,4 +47,16 @@ * TLSv1.3 (OUT), TLS app data, [no content] (0): * TLSv1.3 (IN), TLS app data, [no content] (0): * TLSv1.3 (IN), TLS app data, [no content] (0): - +< HTTP/2 200 +< content-type: application/json +< +* TLSv1.3 (IN), TLS handshake, [no content] (0): +* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): +* TLSv1.3 (IN), TLS app data, [no content] (0): +* TLSv1.3 (IN), TLS app data, [no content] (0): +* TLSv1.3 (IN), TLS app data, [no content] (0): +* TLSv1.3 (IN), TLS app data, [no content] (0): +* TLSv1.3 (IN), TLS app data, [no content] (0): +* TLSv1.3 (IN), TLS app data, [no content] (0): +* Connection #0 to host localhost left intact +{"items":[{"name":"default","links":[{"rel":"canonical","href":"https://localhost:8888/ords/_/db-api/stable/metadata-catalog/openapi.json","mediaType":"application/vnd.oai.openapi+json;version=3.0"}]}],"links":[{"rel":"self","href":"https://localhost:8888/ords/_/db-api/stable/metadata-catalog/"},{"rel":"describes","href":"https://localhost:8888/ords/_/db-api/stable/"}]} diff --git a/docs/multitenant/usecase01/makefile b/docs/multitenant/usecase01/makefile index 202b312f..d4176c75 100644 --- a/docs/multitenant/usecase01/makefile +++ b/docs/multitenant/usecase01/makefile @@ -139,6 +139,7 @@ ORDSPORT=8888 MAKE=/usr/bin/make DOCKERFILE=../../../ords/Dockerfile RUNSCRIPT=../../../ords/runOrdsSSL.sh +ORDSIMGDIR=../../../ords RM=/usr/bin/rm CP=/usr/bin/cp ECHO=/usr/bin/echo @@ -178,10 +179,7 @@ checkstep9: checkcdb createimage: - $(CP) $(DOCKERFILE) . - $(CP) $(RUNSCRIPT) . - $(DOCKER) build -t $(IMAGE) . - $(RM) ./Dockerfile ./runOrdsSSL.sh + $(DOCKER) build -t $(IMAGE) $(ORDSIMGDIR) tagimage: @echo "TAG IMAGE" diff --git a/docs/multitenant/usecase01/pdb_map.yaml b/docs/multitenant/usecase01/pdb_map.yaml index cd6d4ffb..3300a7fa 100644 --- a/docs/multitenant/usecase01/pdb_map.yaml +++ b/docs/multitenant/usecase01/pdb_map.yaml @@ -42,3 +42,4 @@ spec: totalSize: "1G" tempSize: "100M" action: "Map" + assertivePdbDeletion: true diff --git a/docs/multitenant/usecase02/README.md b/docs/multitenant/usecase02/README.md index 0b060df7..c434271f 100644 --- a/docs/multitenant/usecase02/README.md +++ b/docs/multitenant/usecase02/README.md @@ -13,7 +13,7 @@ > ☞ The examples of this folder are based on single namespace **oracle-database-operator-system** -This page explains how to plug and unplug database a pdb; it assumes that you have already configured a pluggable database (see usecase01) +This page explains how to plug and unplug database a pdb; it assumes that you have already configured a pluggable database (see [usecase01](../usecase01/README.md)) The following table reports the parameters required to configure and use oracle multi tenant controller for pluggable database lifecycle management. | yaml file parameters | value | description /ords parameter | @@ -78,7 +78,7 @@ The following table reports the parameters required to configure and use oracle ### UNPLUG DATABASE -Use the following command to check kubernets pdb resources. Note that the output of the commands can be tailored to fit to your needs. Just check the structure of pdb resource **kubectl get pdbs -n oracle-database-operator-system -o=json** and modify the script accordingly. For the sake of simplicity put this command in a single script **checkpdbs.sh**. +Use the following command to check kubernets pdb resources. Note that the output of the commands can be tailored to meet your needs. Just check the structure of pdb resource **kubectl get pdbs -n oracle-database-operator-system -o=json** and modify the script accordingly. For the sake of simplicity put this command in a single script **checkpdbs.sh**. ```bash kubectl get pdbs -n oracle-database-operator-system -o=jsonpath='{range .items[*]} @@ -92,7 +92,7 @@ kubectl get pdbs -n oracle-database-operator-system -o=jsonpath='{range .items[* {"\n"}{end}' ``` -We assume that the pluggable database pdbdev is already configured on opened in read write mode +We assume that the pluggable database pdbdev is already configured and opened in read write mode ```bash ./checkpdbs.sh @@ -106,7 +106,7 @@ MSG=Success ``` -Prepare a new yaml file **pdb_unplug.yaml** to unplug the pdbdev database. Make sure that the path of the xml file is correct and check the existence of all the required secrets. +Prepare a new yaml file **pdb_unplug.yaml** to unplug the pdbdev database. Make sure that the path of the xml file is correct and check the existence of all the required secrets. Do not reuse an existing xml files. ```yaml # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. @@ -248,7 +248,7 @@ Completed: DROP PLUGGABLE DATABASE "pdbdev" KEEP DATAFILES ``` -login to the server and check xml file existence. Get the datafile path on the ASM filesystem. +login to the server and check xml file existence. Verify the datafile path on the ASM filesystem. ```bash ls -ltr /tmp/pdbunplug.xml diff --git a/docs/multitenant/usecase02/pdb_clone.yaml b/docs/multitenant/usecase02/pdb_clone.yaml index be22020b..0ecc3c70 100644 --- a/docs/multitenant/usecase02/pdb_clone.yaml +++ b/docs/multitenant/usecase02/pdb_clone.yaml @@ -18,6 +18,7 @@ spec: fileNameConversions: "NONE" totalSize: "UNLIMITED" tempSize: "UNLIMITED" + assertivePdbDeletion: true adminName: secret: secretName: "pdb1-secret" diff --git a/docs/multitenant/usecase02/pdb_plug.yaml b/docs/multitenant/usecase02/pdb_plug.yaml index 0d0d3b37..77c00b9c 100644 --- a/docs/multitenant/usecase02/pdb_plug.yaml +++ b/docs/multitenant/usecase02/pdb_plug.yaml @@ -21,6 +21,7 @@ spec: totalSize: "1G" tempSize: "100M" action: "Plug" + assertivePdbDeletion: true pdbTlsKey: secret: secretName: "db-tls" diff --git a/docs/multitenant/usecase03/README.md b/docs/multitenant/usecase03/README.md index 3703a9ff..c06368cd 100644 --- a/docs/multitenant/usecase03/README.md +++ b/docs/multitenant/usecase03/README.md @@ -19,9 +19,9 @@ ### INTRODUCTION -> ☞ This folder contains the yaml files required to configure and manage cdb and pdb in different namespaces. The main change here is the possibility to specify the namespace where CDB will be created, this implies the introduction of a new parameter at PDB level in order to specify the CDB namespace. +> ☞ This folder contains the yaml files required to configure and manage cdb and pdb in different namespaces. The main change here is the possibility to specify the namespace where CDB will be created, this implies the introduction of new parameter at PDB level in order to specify the CDB namespace. -Tasks performed in the usecase03 are the same ones of the other usecases with the exception that controller pods cdb pods and pdb crd are running in different namespaces. You must be aware of the fact that secrets must be created in the proper namespaces; cdb secrets go into cdb namespace , pdb secrets go into pdbnamespace while certificate secrets need to be created in every namespace. +Tasks performed in the usecase03 are the same ones of the other usecase01 with the exception that controller pods cdb pods and pdb crd are running in different namespaces. You must be aware of the fact that secrets must be created in the proper namespaces; cdb secrets go into cdb namespace , pdb secrets go into pdbnamespace while certificate secrets need to be created in every namespace. | yaml file parameters | value | description /ords parameter | @@ -53,6 +53,7 @@ Tasks performed in the usecase03 are the same ones of the other usecases with th | tdeExport | | [tdeExport] | | tdeSecret | | [tdeSecret][tdeSecret] | | tdePassword | | [tdeSecret][tdeSecret] | +| assertivePdbDeletion | boolean | [turn on imperative approach on crd deleteion][imperative] | ![generla schema](./NamespaceSegregation.png) @@ -261,6 +262,7 @@ In order to facilitate the command execution use the [makefile](./makefile) avai |reloadop | Reload the db operator | |login | Login into cdb pod | - +[imperative]:https://kubernetes.io/docs/concepts/overview/working-with-objects/object-management/ + diff --git a/docs/multitenant/usecase03/cdb_create.yaml b/docs/multitenant/usecase03/cdb_create.yaml index 09dd7f86..d3b5e04f 100644 --- a/docs/multitenant/usecase03/cdb_create.yaml +++ b/docs/multitenant/usecase03/cdb_create.yaml @@ -5,9 +5,9 @@ metadata: namespace: cdbnamespace spec: cdbName: "DB12" - ordsImage: "lin.ocir.io/intsanjaysingh/mmalvezz/testppr/ords-dboper:latest" + ordsImage: ".............your registry............./ords-dboper:latest" ordsImagePullPolicy: "Always" - dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + dbTnsurl : "...Container tns alias....." replicas: 1 sysAdminPwd: secret: From 23ebf0b381d2c907b132d1dc6e94be9679b516c5 Mon Sep 17 00:00:00 2001 From: Pablo Silberkasten Date: Tue, 27 Aug 2024 22:25:11 +0000 Subject: [PATCH 116/414] Changes in config to create bundle for operatorhub --- config/manager/kustomization.yaml | 2 +- ...tabase-operator.clusterserviceversion.yaml | 97 ++++++++++++++++--- config/samples/kustomization.yaml | 35 ++++--- 3 files changed, 109 insertions(+), 25 deletions(-) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 1a9d97d3..48efb61b 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/operatormntnns + newName: controller newTag: latest diff --git a/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml b/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml index 6ec37dd1..23cd7c00 100644 --- a/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml @@ -3,25 +3,98 @@ kind: ClusterServiceVersion metadata: annotations: alm-examples: '[]' - capabilities: Basic Install + capabilities: Seamless Upgrades operators.operatorframework.io/builder: operator-sdk-v1.2.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v2 - name: oracle-database-operator.v0.0.0 - namespace: placeholder + name: oracle-database-operator.v1.1.0 + namespace: oracle-database-operator-system spec: apiservicedefinitions: {} customresourcedefinitions: owned: + - description: DbcsSystem is the Schema for the dbcssystems API + displayName: Dbcs System + kind: DbcsSystem + name: DbcsSystem.database.oracle.com + version: v1alpha1 + - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases + API + displayName: Autonomous Container Database + kind: AutonomousContainerDatabase + name: autonomouscontainerdatabases.database.oracle.com + version: v1alpha1 + - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups + API + displayName: Autonomous Database Backup + kind: AutonomousDatabaseBackup + name: autonomousdatabasebackups.database.oracle.com + version: v1alpha1 + - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores + API + displayName: Autonomous Database Restore + kind: AutonomousDatabaseRestore + name: autonomousdatabaserestores.database.oracle.com + version: v1alpha1 - description: AutonomousDatabase is the Schema for the autonomousdatabases API displayName: Autonomous Database kind: AutonomousDatabase name: autonomousdatabases.database.oracle.com version: v1alpha1 - description: Operator to manage Oracle sharding - displayName: Oracle Sharding DB Operator + - description: CDB is the Schema for the cdbs API + displayName: CDB + kind: CDB + name: cdbs.database.oracle.com + version: v1alpha1 + - description: DatabaseObserver is the Schema for the databaseobservers API + displayName: Database Observer + kind: DatabaseObserver + name: databaseobservers.observability.oracle.com + version: v1alpha1 + - description: DataguardBroker is the Schema for the dataguardbrokers API + displayName: Dataguard Broker + kind: DataguardBroker + name: dataguardbrokers.database.oracle.com + version: v1alpha1 + - description: OracleRestDataService is the Schema for the oraclerestdataservices + API + displayName: Oracle Rest Data Service + kind: OracleRestDataService + name: oraclerestdataservices.database.oracle.com + version: v1alpha1 + - description: PDB is the Schema for the pdbs API + displayName: PDB + kind: PDB + name: pdbs.database.oracle.com + version: v1alpha1 + - description: ShardingDatabase is the Schema for the shardingdatabases API + displayName: Sharding Database + kind: ShardingDatabase + name: shardingdatabases.database.oracle.com + version: v1alpha1 + - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases + API + displayName: Single Instance Database + kind: SingleInstanceDatabase + name: singleinstancedatabases.database.oracle.com + version: v1alpha1 + description: | + As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released Oracle Database Operator for Kubernetes (OraOperator or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating Oracle Database lifecycle management. + In this v1.1.0 production release, OraOperator supports the following database configurations and infrastructure: + ## Oracle Autonomous Database: + * Oracle Autonomous Database shared Oracle Cloud Infrastructure (OCI) (ADB-S) + * Oracle Autonomous Database on dedicated Cloud infrastructure (ADB-D) + * Oracle Autonomous Container Database (ACD) (infrastructure) is the infrastructure for provisioning Autonomous Databases. + * Containerized Single Instance databases (SIDB) deployed in the Oracle Kubernetes Engine (OKE) and any k8s where OraOperator is deployed + * Containerized Sharded databases (SHARDED) deployed in OKE and any k8s where OraOperator is deployed + * Oracle Multitenant Databases (CDB/PDBs) + * Oracle Base Database Cloud Service (BDBCS) + * Oracle Data Guard (Preview status) + * Oracle Database Observability (Preview status) + * Oracle will continue to extend OraOperator to support additional Oracle Database configurations. + displayName: Oracle Database Operator icon: - - base64data: "" - mediatype: "" + - base64data: iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAJjUlEQVR42u3cfcwcRQHH8S9PH0BokZfCVBgpOgjyFjRoQIQQkLeA0PLWqgQMFDVgja9AChIKKCEKSgQEQVsQJGKxtNCAvAi2vJiCqAQMUpQRMKM4vFiCQEUo/jH7kOt19m7vbveK8fdJLukzMzuzczc7OzszWxAREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREZH/X2tVSRStmwi8B5gErN1nWS8DAVhmgl9ZsdwpVc+xoteKc/iTCf7VujKN1o0A+xef5cDPTfCP1XjeY+VsAWwFTATGDZjdPSb4F6J1U9sjTPA31n3uXeq1MfBe4F30376ADo0lWjcBOAa4EHhHzXW4FzgDuNsE/2aHc3gJmFBz2WMuBc4ywT87SCbRunWAB4APtEV92gR/zaAnGa2bBJwInFVz/SeY4F+O1q32/Zvg6+xEyuq1PnA08F1gg7ryHSkpbDrwEnAZ9TdmgD2BxcDj0bptG8i/is8DMVq394D5zGb1xgxwdbRum34zjdaNi9adAjxD/Y15PxP8yzXn2UvdDiPdsX9IjY0ZOHOVKzFaNw64Apgx5DoelrvNNdxDt9rDBP/rXg+K1u0EPNIhyWPADp3uQiX5bggsIX+hDOp6E/z0lrKG1kMXQ7OLgJkNZL8SWO+tHjpatxbwM4bfmAEWRuuOWAPljrmvGGJVVlz8d3ZJth1wXI/5jgeW0UxjhnRnGrqifV1FM40ZYDcT/GujLQGnAUd2OGAu6Qd8oY/CxgGTgWOB3UvSzI/W7WiCf7RLXgcNUOnJwOUlcScCF/SQ10mAqZBubrTuVhP837sljNZB6lQmdUh2AbCUdMvu1Ssm+Of6OK4OXyb9/mV+DNxOf+0LE/yDUDwURuu2A/5YknYWcLEJ/pU6ahWt2wq4FtgjE/08YMZmQUqGHHua4O8boPx1gNuAvTPRI1WGB0UdnsxEPQzsnAm/HTjQBN8t36nAwpLoTwDzTfBv9Fv3kjIbH3JE6xzwREn0bOA7dY3px4Ycl5bEH2CC/1ZdjRnABP8UsBfwg0z0RODgusoqKf810uxNzsbdjm/pRXOOIP1A7Q4ADu2S7yjljXl7E/y8uhvzEF1YEj7FBH9OnQ+oI9G6zYF9MnEzTfB3NFG7ogf+ApDrsr7XRJlt/lYSvmGFY6cDu2XCZ5ngnwC+XXLcTcXDXpmyodheTcxpD0u0bjNgSibqZBP8orrLGyH1HjlXNFnRorc5KhPlioWcJo2WhL/e6aBo3abke+eVFL2QCX4F+eEMpDnXMsdnwu4ywd/T8HfRtLLv4pImChsFDsmEn2aCf73XzPrwUEn4+4Gep9F68LGS8G4PTN8vCf9oMZQBwAS/JFp3PTCtLd2MaN3ckmeAXIP+ZoPfwbCUDSFXFMO3Om03QlrkaLd4GDUtHsByPd7WTZUZrdsLuDUTdX+n5fBo3b6k4Ua7K0zw92fCy6an7o3WVV2seqRiurez3QfPopI5Jvhlo+SniPqaOunT05mwjmPZaN2WwKPAmy0fOvw99u/NOmR7aofyxgO/7OU4E/yz0brjSHOv7c4oPt3U9jC+Bk0aPItKvgZpDJ27zVZ5OKrLFpmwlzodYIL/K2k+egPgncX5bghsRJqp2KT4TAQ2LT6dGvONJvi7O8R/oyR8qgn+xQ7HXUP+wffrxSpjN+v195W+rQxj3vuQsd9hhDRJ326P3vLrTzGGOjoT9Zdux5rg7yWtxA3qsZJzGDvHXYCvZKIWAzd1OceVlE/X3VmsNnayfQ31W9MeaDj/W0zwN4/9MQosYvUv/YJo3UVVt3kOYAfyO/4qTVOZ4JcVu9EeAjbvo/xLSNNH/85FRuvWBsp67guBKRUfbM4jrcSucvqk1caxp/3rgE+2pfkqaWfi/7JfkO8w1m5i4mGUtGrWbhzwKdKKXiOKtf0rM1HPAbFqPib4GK17H2lRYv8Kh7xIWkj6kQned0n7JWB8JnwmaSfiFgzm4mjdomKxaQ6rN+jDo3UfMsH/dsBy1qSy/S4zaGBqeMQE/zTwu0zcT6J1u/WaYRVFr3YOsGsmema3JeJ2xUrmQcDFHZKdU9R3IxP86d0ac7Rua+D8XBRpP0iVi6eKecXFXXYneDBaN7mmsoau2MOyJBN1eTHjVKuxpe/PlMQvjdadUGGsV1mxcjSP8qf8Bf3ka4J/wwT/RdImmJwzSbv6uj5oFQ1sYUn0fkVZjwJn1/CV7ApM77Ik/1S0rq4LaE0o2+G3JFp3UjG0q8Vb49do3fnAyR3SngvcQ3rFqFfjAEtaaJjWId2uJvjftJxTX5uTonWHAzeURQMf7LT7LVp3LHB1Jup8E/ypLenWBVb08X3kbEbanLWU/J0L0uzP2aQ76qBTeq+a4B/ObU4CPlJTnVodTOpUypxH6smXD1JIa4MeBX5FfqFlGGaY4FcZUw+y264YLi3tkGQXE/zvM8dNIr0lkjO+faNWtG5n0sXai1syYfNN8EdF6zYhNeymHWmCv6GkQdftKtIo4GbgwCYLan9jZV1gPvDxIVSy1fEm+KvaAwfdPlqMg//cIcnhJviFbccsIr8dYD8TfLcN/ZVE6y4HPpeJOsAEf0dxUf2BNH/ehMXAPiZ4htSgNzbBLy+27l5Lfg9PLVZ5p7CYvjqU4b3V8B/gw7nGXIdi99umlO/FXRCtmzU29RatO4h8Y15QV2MuzCoJvz1aN8EE/w9gS3p74aAXx/T64D2AKSb45fDW1t1plD+zDWrf1V6SNcG/aYK/jDSmm917npU8Tdo7vH7TU1Im+OeBnShfBDkP+Gm0bgPyQwFIb7PUeU7/pHwx59wizQoT/CmAI793vF+fNcGHlr+P7zun7m4jrXO01h0T/BzSKu7pNZZ1nQn+rqr/L8dkYFvSvGu/T6T/Ap4CHjfBV9orEq07IRN8pwn+yV4LL17Q7PTj3UV+F95zTfw/FcVMSvb9zeIHz53/1sA2pEWZfmeermxfMIvW7Uh6EB3pL8tSN5vgn+mWqNibsw3wbvpvXwuqtisREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREWnxX2ox1/vZSvwPAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI0LTA4LTEzVDE5OjUyOjMxKzAwOjAwsDIMcAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNC0wOC0xM1QxOTo1MjozMSswMDowMMFvtMwAAABVdEVYdHN2Zzpjb21tZW50ACBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyBFB1wTAAAAAElFTkSuQmCC + mediatype: png install: spec: deployments: null @@ -37,12 +110,12 @@ spec: type: AllNamespaces keywords: - Oracle - - sharding - - db + - Database + - Operator links: - name: Oracle Database Operator - url: https://oracle-database-operator.domain + url: https://github.com/oracle/oracle-database-operator maturity: alpha provider: - name: ShardingDatabase - version: 0.0.0 + name: Oracle + version: 1.2.0 diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index fad43bc6..907205ad 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -5,18 +5,29 @@ ## Append samples you want in your CSV to this file as resources ## resources: - - onpremdb/pdb.yaml - - onpremdb/cdb.yaml - - adb/autonomousdatabase_create.yaml + - multitenant/pdb_plug.yaml + - multitenant/cdb_secret.yaml + - multitenant/pdb_secret.yaml + - multitenant/pdb_clone.yaml + - multitenant/cdb.yaml + - sidb/singleinstancedatabase_patch.yaml + - sidb/oraclerestdataservice_apex.yaml + - sidb/singleinstancedatabase_express.yaml + - sidb/singleinstancedatabase_secrets.yaml + - sidb/singleinstancedatabase_clone.yaml + - sidb/singleinstancedatabase_prebuiltdb.yaml + - sidb/dataguardbroker.yaml + - sidb/oraclerestdataservice_secrets.yaml + - sidb/singleinstancedatabase_free.yaml + - sidb/singleinstancedatabase_standby.yaml + - sidb/openshift_rbac.yaml + - sharding/sharding_v1alpha1_provshard_clonespec1.yaml + - sharding/shardingdatabase.yaml + - sharding/sharding_v1alpha1_provshard_clonespec.yaml + - observability/databaseobserver_vault.yaml + - observability/databaseobserver_minimal.yaml - adb/autonomousdatabase_bind.yaml - adb/autonomousdatabase_backup.yaml - adb/autonomousdatabase_restore.yaml - - acd/autonomouscontainerdatabase_create.yaml - - sidb/singleinstancedatabase.yaml - - sharding/shardingdatabase.yaml - - sharding/sharding_v1alpha1_provshard.yaml - - dbcs/database_v1alpha1_dbcssystem.yaml - - database_v1alpha1_dataguardbroker.yaml - - observability/databaseobserver.yaml -- database_v1alpha1_shardingdatabase.yaml - # +kubebuilder:scaffold:manifestskustomizesamples + - acd/autonomouscontainerdatabase_restart_terminate.yaml + # +kubebuilder:scaffold:manifestskustomizesamples \ No newline at end of file From d411bb1ad198f4b681bfe488fe6f4236eb4081e5 Mon Sep 17 00:00:00 2001 From: thirumalai_thathachary Date: Wed, 28 Aug 2024 20:54:50 +0000 Subject: [PATCH 117/414] Set default as FALSE for InvitedNodeSubnetFlag --- commons/sharding/catalog.go | 11 + commons/sharding/scommon.go | 4 + .../bases/database.oracle.com_DbcsSystem.yaml | 11 + ...acle.com_autonomouscontainerdatabases.yaml | 20 ++ ....oracle.com_autonomousdatabasebackups.yaml | 20 ++ ...oracle.com_autonomousdatabaserestores.yaml | 25 ++ ...tabase.oracle.com_autonomousdatabases.yaml | 64 +++++ .../crd/bases/database.oracle.com_cdbs.yaml | 40 +++ .../database.oracle.com_dataguardbrokers.yaml | 10 + ...ase.oracle.com_oraclerestdataservices.yaml | 28 ++ .../crd/bases/database.oracle.com_pdbs.yaml | 80 ++++++ ...database.oracle.com_shardingdatabases.yaml | 127 +++++++++ ...se.oracle.com_singleinstancedatabases.yaml | 57 +++++ ...vability.oracle.com_databaseobservers.yaml | 55 ++++ oracle-database-operator.yaml | 240 +++++++++++++++++- 15 files changed, 791 insertions(+), 1 deletion(-) diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index e325ce6c..141ccca2 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -375,6 +375,17 @@ func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, }, } + if len(OraCatalogSpex.PvAnnotations) > 0 { + claims[0].ObjectMeta.Annotations = make(map[string]string) + for key, value := range OraCatalogSpex.PvAnnotations { + claims[0].ObjectMeta.Annotations[key] = value + } + } + + if len(OraCatalogSpex.PvMatchLabels) > 0 { + claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} + } + if checkTdeWalletFlag(instance) { if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { { diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 98892962..a88946d0 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -294,6 +294,10 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da result = append(result, corev1.EnvVar{Name: "DEV_MODE", Value: "TRUE"}) } + if instance.Spec.InvitedNodeSubnetFlag == "" { + instance.Spec.InvitedNodeSubnetFlag = "FALSE" + + } if strings.ToUpper(instance.Spec.InvitedNodeSubnetFlag) != "FALSE" { result = append(result, corev1.EnvVar{Name: "INVITED_NODE_SUBNET_FLAG", Value: "TRUE"}) if instance.Spec.InvitedNodeSubnet != "" { diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml index 970954a1..e933d5a4 100644 --- a/config/crd/bases/database.oracle.com_DbcsSystem.yaml +++ b/config/crd/bases/database.oracle.com_DbcsSystem.yaml @@ -19,14 +19,22 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: + description: DbcsSystem is the Schema for the dbcssystems API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DbcsSystemSpec defines the desired state of DbcsSystem properties: dbSystem: properties: @@ -43,6 +51,7 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: + description: DB Backup COnfig Network Struct properties: autoBackupEnabled: type: boolean @@ -130,6 +139,7 @@ spec: - ociConfigMap type: object status: + description: DbcsSystemStatus defines the observed state of DbcsSystem properties: availabilityDomain: type: string @@ -143,6 +153,7 @@ spec: type: string dbInfo: items: + description: DbcsSystemStatus defines the observed state of DbcsSystem properties: dbHomeId: type: string diff --git a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml index 875eeaef..bac3a28c 100644 --- a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml @@ -32,14 +32,24 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousContainerDatabaseSpec defines the desired state + of AutonomousContainerDatabase properties: action: enum: @@ -48,6 +58,8 @@ spec: - TERMINATE type: string autonomousContainerDatabaseOCID: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' type: string autonomousExadataVMClusterOCID: type: string @@ -63,6 +75,7 @@ spec: default: false type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -70,14 +83,21 @@ spec: type: string type: object patchModel: + description: 'AutonomousContainerDatabasePatchModelEnum Enum with + underlying type: string' enum: - RELEASE_UPDATES - RELEASE_UPDATE_REVISIONS type: string type: object status: + description: AutonomousContainerDatabaseStatus defines the observed state + of AutonomousContainerDatabase properties: lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' type: string timeCreated: type: string diff --git a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml index 71fdbfa4..a5c37507 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml @@ -38,14 +38,24 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousDatabaseBackupSpec defines the desired state of + AutonomousDatabaseBackup properties: autonomousDatabaseBackupOCID: type: string @@ -54,6 +64,7 @@ spec: isLongTermBackup: type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -63,8 +74,11 @@ spec: retentionPeriodInDays: type: integer target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: + description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -77,6 +91,8 @@ spec: type: object type: object status: + description: AutonomousDatabaseBackupStatus defines the observed state + of AutonomousDatabaseBackup properties: autonomousDatabaseOCID: type: string @@ -89,12 +105,16 @@ spec: isAutomatic: type: boolean lifecycleState: + description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with + underlying type: string' type: string timeEnded: type: string timeStarted: type: string type: + description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying + type: string' type: string required: - autonomousDatabaseOCID diff --git a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml index f6173b5b..5e9f2c73 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml @@ -32,16 +32,27 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousDatabaseRestoreSpec defines the desired state of + AutonomousDatabaseRestore properties: ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -51,6 +62,9 @@ spec: source: properties: k8sADBBackup: + description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO + OWN! NOTE: json tags are required. Any new fields you add must + have json tags for the fields to be serialized.' properties: name: type: string @@ -58,12 +72,17 @@ spec: pointInTime: properties: timestamp: + description: 'The timestamp must follow this format: YYYY-MM-DD + HH:MM:SS GMT' type: string type: object type: object target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: + description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -79,12 +98,18 @@ spec: - target type: object status: + description: AutonomousDatabaseRestoreStatus defines the observed state + of AutonomousDatabaseRestore properties: dbName: type: string displayName: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' type: string status: + description: 'WorkRequestStatusEnum Enum with underlying type: string' type: string timeAccepted: type: string diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index d494b5a6..f77407f3 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -47,20 +47,33 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabase is the Schema for the autonomousdatabases + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase + Important: Run "make" to regenerate code after modifying this file' properties: details: + description: AutonomousDatabaseDetails defines the detail information + of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase properties: adminPassword: properties: k8sSecret: + description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -72,8 +85,12 @@ spec: type: object type: object autonomousContainerDatabase: + description: ACDSpec defines the spec of the target for backup/restore + runs. The name could be the name of an AutonomousDatabase or + an AutonomousDatabaseBackup properties: k8sACD: + description: "*********************** *\tACD specs ***********************" properties: name: type: string @@ -97,6 +114,8 @@ spec: dbVersion: type: string dbWorkload: + description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying + type: string' enum: - OLTP - DW @@ -114,11 +133,15 @@ spec: isDedicated: type: boolean licenseModel: + description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying + type: string' enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string lifecycleState: + description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying + type: string' type: string networkAccess: properties: @@ -156,6 +179,8 @@ spec: password: properties: k8sSecret: + description: "*********************** *\tSecret specs + ***********************" properties: name: type: string @@ -172,6 +197,7 @@ spec: default: false type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -182,6 +208,7 @@ spec: - details type: object status: + description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase properties: allConnectionStrings: items: @@ -203,29 +230,63 @@ spec: type: array conditions: items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -241,6 +302,9 @@ spec: - type x-kubernetes-list-type: map lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' type: string timeCreated: type: string diff --git a/config/crd/bases/database.oracle.com_cdbs.yaml b/config/crd/bases/database.oracle.com_cdbs.yaml index 3c564c85..6b1c350c 100644 --- a/config/crd/bases/database.oracle.com_cdbs.yaml +++ b/config/crd/bases/database.oracle.com_cdbs.yaml @@ -48,18 +48,28 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: CDB is the Schema for the cdbs API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: CDBSpec defines the desired state of CDB properties: cdbAdminPwd: + description: Password for the CDB Administrator to manage PDB lifecycle properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -73,8 +83,11 @@ spec: - secret type: object cdbAdminUser: + description: User in the root container with sysdba priviledges to + manage PDB lifecycle properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -88,10 +101,12 @@ spec: - secret type: object cdbName: + description: Name of the CDB type: string cdbTlsCrt: properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -107,6 +122,7 @@ spec: cdbTlsKey: properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -120,29 +136,40 @@ spec: - secret type: object dbPort: + description: DB server port type: integer dbServer: + description: Name of the DB server type: string dbTnsurl: type: string nodeSelector: additionalProperties: type: string + description: Node Selector for running the Pod type: object ordsImage: + description: ORDS Image Name type: string ordsImagePullPolicy: + description: ORDS Image Pull Policy enum: - Always - Never type: string ordsImagePullSecret: + description: The name of the image pull secret in case of a private + docker repository. type: string ordsPort: + description: ORDS server port. For now, keep it as 8888. TO BE USED + IN FUTURE RELEASE. type: integer ordsPwd: + description: Password for user ORDS_PUBLIC_USER properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -156,12 +183,16 @@ spec: - secret type: object replicas: + description: Number of ORDS Containers to create type: integer serviceName: + description: Name of the CDB Service type: string sysAdminPwd: + description: Password for the CDB System Administrator properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -175,8 +206,10 @@ spec: - secret type: object webServerPwd: + description: Password for the Web Server User properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -190,8 +223,11 @@ spec: - secret type: object webServerUser: + description: Web Server User with SQL Administrator role to allow + us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -206,12 +242,16 @@ spec: type: object type: object status: + description: CDBStatus defines the observed state of CDB properties: msg: + description: Message type: string phase: + description: Phase of the CDB Resource type: string status: + description: CDB Resource Status type: boolean required: - phase diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index 33bc2e2a..f19a3e22 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -43,14 +43,22 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: DataguardBroker is the Schema for the dataguardbrokers API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DataguardBrokerSpec defines the desired state of DataguardBroker properties: fastStartFailOver: properties: @@ -58,6 +66,7 @@ spec: type: boolean strategy: items: + description: FSFO strategy properties: sourceDatabaseRef: type: string @@ -95,6 +104,7 @@ spec: - standbyDatabaseRefs type: object status: + description: DataguardBrokerStatus defines the observed state of DataguardBroker properties: clusterConnectString: type: string diff --git a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml index 8264ff93..121383fd 100644 --- a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml @@ -35,16 +35,27 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: OracleRestDataService is the Schema for the oraclerestdataservices + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService properties: adminPassword: + description: OracleRestDataServicePassword defines the secret containing + Password mapped to secretKey properties: keepSecret: type: boolean @@ -57,6 +68,8 @@ spec: - secretName type: object apexPassword: + description: OracleRestDataServicePassword defines the secret containing + Password mapped to secretKey properties: keepSecret: type: boolean @@ -71,6 +84,8 @@ spec: databaseRef: type: string image: + description: OracleRestDataServiceImage defines the Image source and + pullSecrets for POD properties: pullFrom: type: string @@ -90,6 +105,8 @@ spec: oracleService: type: string ordsPassword: + description: OracleRestDataServicePassword defines the secret containing + Password mapped to secretKey properties: keepSecret: type: boolean @@ -104,6 +121,8 @@ spec: ordsUser: type: string persistence: + description: OracleRestDataServicePersistence defines the storage + releated params properties: accessMode: enum: @@ -122,6 +141,8 @@ spec: type: integer restEnableSchemas: items: + description: OracleRestDataServicePDBSchemas defines the PDB Schemas + to be ORDS Enabled properties: enable: type: boolean @@ -148,6 +169,8 @@ spec: - ordsPassword type: object status: + description: OracleRestDataServiceStatus defines the observed state of + OracleRestDataService properties: apexConfigured: type: boolean @@ -162,6 +185,8 @@ spec: databaseRef: type: string image: + description: OracleRestDataServiceImage defines the Image source and + pullSecrets for POD properties: pullFrom: type: string @@ -181,6 +206,9 @@ spec: serviceIP: type: string status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' type: string type: object type: object diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index 2a72da27..85af8c1b 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -48,16 +48,26 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: PDB is the Schema for the pdbs API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: PDBSpec defines the desired state of PDB properties: action: + description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. + Map is used to map a Databse PDB to a Kubernetes PDB CR.' enum: - Create - Clone @@ -69,8 +79,11 @@ spec: - Map type: string adminName: + description: The administrator username for the new PDB. This property + is required when the Action property is Create. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -84,8 +97,11 @@ spec: - secret type: object adminPwd: + description: The administrator password for the new PDB. This property + is required when the Action property is Create. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -99,31 +115,48 @@ spec: - secret type: object asClone: + description: Indicate if 'AS CLONE' option should be used in the command + to plug in a PDB. This property is applicable when the Action property + is PLUG but not required. type: boolean assertivePdbDeletion: + description: turn on the assertive approach to delete pdb resource + kubectl delete pdb ..... automatically triggers the pluggable database + deletion type: boolean cdbName: + description: Name of the CDB type: string cdbNamespace: + description: CDB Namespace type: string cdbResName: + description: Name of the CDB Custom Resource that runs the ORDS container type: string copyAction: + description: To copy files or not while cloning a PDB enum: - COPY - NOCOPY - MOVE type: string dropAction: + description: Specify if datafiles should be removed or not. The value + can be INCLUDING or KEEP (default). enum: - INCLUDING - KEEP type: string fileNameConversions: + description: Relevant for Create and Plug operations. As defined in + the Oracle Multitenant Database documentation. Values can be a + filename convert pattern or NONE. type: string getScript: + description: Whether you need the script only or execute the script type: boolean modifyOption: + description: Extra options for opening and closing a PDB enum: - IMMEDIATE - NORMAL @@ -132,8 +165,11 @@ spec: - RESTRICTED type: string pdbName: + description: The name of the new PDB. Relevant for both Create and + Plug Actions. type: string pdbState: + description: The target state of the PDB enum: - OPEN - CLOSE @@ -141,6 +177,7 @@ spec: pdbTlsCat: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -156,6 +193,7 @@ spec: pdbTlsCrt: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -171,6 +209,7 @@ spec: pdbTlsKey: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -184,22 +223,35 @@ spec: - secret type: object reuseTempFile: + description: Whether to reuse temp file type: boolean sourceFileNameConversions: + description: This property is required when the Action property is + Plug. As defined in the Oracle Multitenant Database documentation. + Values can be a source filename convert pattern or NONE. type: string sparseClonePath: + description: A Path specified for sparse clone snapshot copy. (Optional) type: string srcPdbName: + description: Name of the Source PDB from which to clone type: string tdeExport: + description: TDE export for unplug operations type: boolean tdeImport: + description: TDE import for plug operations type: boolean tdeKeystorePath: + description: TDE keystore path is required if the tdeImport or tdeExport + flag is set to true. Can be used in plug or unplug operations. type: string tdePassword: + description: TDE password if the tdeImport or tdeExport flag is set + to true. Can be used in create, plug or unplug operations properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -213,8 +265,11 @@ spec: - secret type: object tdeSecret: + description: TDE secret is required if the tdeImport or tdeExport + flag is set to true. Can be used in plug or unplug operations. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -228,14 +283,26 @@ spec: - secret type: object tempSize: + description: Relevant for Create and Clone operations. Total size + for temporary tablespace as defined in the Oracle Multitenant Database + documentation. See size_clause description in Database SQL Language + Reference documentation. type: string totalSize: + description: Relevant for create and plug operations. Total size as + defined in the Oracle Multitenant Database documentation. See size_clause + description in Database SQL Language Reference documentation. type: string unlimitedStorage: + description: Relevant for Create and Plug operations. True for unlimited + storage. Even when set to true, totalSize and tempSize MUST be specified + in the request if Action is Create. type: boolean webServerPwd: + description: Password for the Web ServerPDB User properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -249,8 +316,11 @@ spec: - secret type: object webServerUser: + description: Web Server User with SQL Administrator role to allow + us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -264,27 +334,37 @@ spec: - secret type: object xmlFileName: + description: XML metadata filename to be used for Plug or Unplug operations type: string required: - action type: object status: + description: PDBStatus defines the observed state of PDB properties: action: + description: Last Completed Action type: string connString: + description: PDB Connect String type: string modifyOption: + description: Modify Option of the PDB type: string msg: + description: Message type: string openMode: + description: Open mode of the PDB type: string phase: + description: Phase of the PDB Resource type: string status: + description: PDB Resource Status type: boolean totalSize: + description: Total size of the PDB type: string required: - phase diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index e0959a4c..641629a0 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -30,22 +30,33 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: ShardingDatabase is the Schema for the shardingdatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: InvitedNodeSubnet: type: string catalog: items: + description: CatalogSpec defines the desired state of CatalogSpec properties: envVars: items: + description: EnvironmentVariable represents a named variable + accessible for containers. properties: name: type: string @@ -57,6 +68,8 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image type: string isDelete: type: string @@ -79,11 +92,23 @@ spec: pvcName: type: string resources: + description: ResourceRequirements describes the compute resource + requirements. properties: claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. type: string required: - name @@ -99,6 +124,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -107,6 +134,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -123,6 +155,7 @@ spec: dbImagePullSecret: type: string dbSecret: + description: Secret Details properties: encryptionType: type: string @@ -150,11 +183,17 @@ spec: type: string gsm: items: + description: GsmSpec defines the desired state of GsmSpec properties: directorName: type: string envVars: + description: Replicas int32 `json:"replicas,omitempty"` // + Gsm Replicas. If you set OraGsmPvcName then it is set default + to 1. items: + description: EnvironmentVariable represents a named variable + accessible for containers. properties: name: type: string @@ -166,6 +205,8 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image type: string isDelete: type: string @@ -186,11 +227,23 @@ spec: region: type: string resources: + description: ResourceRequirements describes the compute resource + requirements. properties: claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. type: string required: - name @@ -206,6 +259,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -214,6 +269,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -231,6 +291,7 @@ spec: type: string gsmService: items: + description: Service Definition properties: available: type: string @@ -309,6 +370,7 @@ spec: type: array gsmShardSpace: items: + description: ShardSpace Specs properties: chunks: type: integer @@ -344,6 +406,8 @@ spec: type: string portMappings: items: + description: PortMapping is a specification of port mapping for + an application deployment. properties: port: format: int32 @@ -367,12 +431,18 @@ spec: scriptsLocation: type: string shard: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' items: + description: ShardSpec is a specification of Shards for an application + deployment. properties: deployAs: type: string envVars: items: + description: EnvironmentVariable represents a named variable + accessible for containers. properties: name: type: string @@ -384,6 +454,8 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image type: string isDelete: enum: @@ -411,11 +483,23 @@ spec: pvcName: type: string resources: + description: ResourceRequirements describes the compute resource + requirements. properties: claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. type: string required: - name @@ -431,6 +515,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -439,6 +525,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object shardGroup: @@ -480,6 +571,8 @@ spec: - shard type: object status: + description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 + ShardingDatabaseStatus defines the observed state of ShardingDatabase properties: catalogs: additionalProperties: @@ -487,29 +580,63 @@ spec: type: object conditions: items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index 3a5d2ce4..1c011e17 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -53,16 +53,27 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: SingleInstanceDatabase is the Schema for the singleinstancedatabases + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase properties: adminPassword: + description: SingleInsatnceAdminPassword defines the secret containing + Admin Password mapped to secretKey for Database properties: keepSecret: type: boolean @@ -100,6 +111,8 @@ spec: forceLog: type: boolean image: + description: SingleInstanceDatabaseImage defines the Image source + and pullSecrets for POD properties: prebuiltDB: type: boolean @@ -113,6 +126,7 @@ spec: - pullFrom type: object initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -134,6 +148,8 @@ spec: pdbName: type: string persistence: + description: SingleInstanceDatabasePersistence defines the storage + size and class for PVC properties: accessMode: enum: @@ -183,6 +199,8 @@ spec: type: string type: object sid: + description: SID must be alphanumeric (no special characters, only + a-z, A-Z, 0-9), and no longer than 12 characters. maxLength: 12 pattern: ^[a-zA-Z0-9]+$ type: string @@ -196,6 +214,8 @@ spec: - image type: object status: + description: SingleInstanceDatabaseStatus defines the observed state of + SingleInstanceDatabase properties: apexInstalled: type: boolean @@ -213,29 +233,63 @@ spec: type: string conditions: items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -269,6 +323,7 @@ spec: forceLog: type: string initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -299,6 +354,8 @@ spec: pdbName: type: string persistence: + description: SingleInstanceDatabasePersistence defines the storage + size and class for PVC properties: accessMode: enum: diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index d9aee464..b0801738 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -26,16 +26,26 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver properties: database: + description: DatabaseObserverDatabase defines the database details + used for DatabaseObserver properties: dbConnectionString: properties: @@ -71,10 +81,13 @@ spec: type: object type: object exporter: + description: DatabaseObserverExporterConfig defines the configuration + details related to the exporters of DatabaseObserver properties: configuration: properties: configmap: + description: ConfigMapDetails defines the configmap name properties: configmapName: type: string @@ -85,6 +98,8 @@ spec: image: type: string service: + description: DatabaseObserverService defines the exporter service + component of DatabaseObserver properties: port: format: int32 @@ -99,6 +114,8 @@ spec: type: string type: object prometheus: + description: PrometheusConfig defines the generated resources for + Prometheus properties: labels: additionalProperties: @@ -112,32 +129,70 @@ spec: type: integer type: object status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver properties: conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 9185ab9c..cec36793 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -37,14 +37,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase properties: action: enum: @@ -53,6 +57,7 @@ spec: - TERMINATE type: string autonomousContainerDatabaseOCID: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' type: string autonomousExadataVMClusterOCID: type: string @@ -68,6 +73,7 @@ spec: default: false type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -75,14 +81,17 @@ spec: type: string type: object patchModel: + description: 'AutonomousContainerDatabasePatchModelEnum Enum with underlying type: string' enum: - RELEASE_UPDATES - RELEASE_UPDATE_REVISIONS type: string type: object status: + description: AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase properties: lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string timeCreated: type: string @@ -139,14 +148,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousDatabaseBackupSpec defines the desired state of AutonomousDatabaseBackup properties: autonomousDatabaseBackupOCID: type: string @@ -155,6 +168,7 @@ spec: isLongTermBackup: type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -164,8 +178,10 @@ spec: retentionPeriodInDays: type: integer target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: + description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -178,6 +194,7 @@ spec: type: object type: object status: + description: AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup properties: autonomousDatabaseOCID: type: string @@ -190,12 +207,14 @@ spec: isAutomatic: type: boolean lifecycleState: + description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with underlying type: string' type: string timeEnded: type: string timeStarted: type: string type: + description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying type: string' type: string required: - autonomousDatabaseOCID @@ -250,16 +269,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore properties: ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -269,6 +293,7 @@ spec: source: properties: k8sADBBackup: + description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.' properties: name: type: string @@ -276,12 +301,15 @@ spec: pointInTime: properties: timestamp: + description: 'The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT' type: string type: object type: object target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: + description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -297,12 +325,15 @@ spec: - target type: object status: + description: AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore properties: dbName: type: string displayName: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string status: + description: 'WorkRequestStatusEnum Enum with underlying type: string' type: string timeAccepted: type: string @@ -377,20 +408,26 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabase is the Schema for the autonomousdatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase Important: Run "make" to regenerate code after modifying this file' properties: details: + description: AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase properties: adminPassword: properties: k8sSecret: + description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -402,8 +439,10 @@ spec: type: object type: object autonomousContainerDatabase: + description: ACDSpec defines the spec of the target for backup/restore runs. The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup properties: k8sACD: + description: "*********************** *\tACD specs ***********************" properties: name: type: string @@ -427,6 +466,7 @@ spec: dbVersion: type: string dbWorkload: + description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying type: string' enum: - OLTP - DW @@ -444,11 +484,13 @@ spec: isDedicated: type: boolean licenseModel: + description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying type: string' enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string lifecycleState: + description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' type: string networkAccess: properties: @@ -486,6 +528,7 @@ spec: password: properties: k8sSecret: + description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -502,6 +545,7 @@ spec: default: false type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -512,6 +556,7 @@ spec: - details type: object status: + description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase properties: allConnectionStrings: items: @@ -533,29 +578,36 @@ spec: type: array conditions: items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -571,6 +623,7 @@ spec: - type x-kubernetes-list-type: map lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string timeCreated: type: string @@ -637,18 +690,24 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: CDB is the Schema for the cdbs API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: CDBSpec defines the desired state of CDB properties: cdbAdminPwd: + description: Password for the CDB Administrator to manage PDB lifecycle properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -662,8 +721,10 @@ spec: - secret type: object cdbAdminUser: + description: User in the root container with sysdba priviledges to manage PDB lifecycle properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -677,10 +738,12 @@ spec: - secret type: object cdbName: + description: Name of the CDB type: string cdbTlsCrt: properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -696,6 +759,7 @@ spec: cdbTlsKey: properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -709,29 +773,38 @@ spec: - secret type: object dbPort: + description: DB server port type: integer dbServer: + description: Name of the DB server type: string dbTnsurl: type: string nodeSelector: additionalProperties: type: string + description: Node Selector for running the Pod type: object ordsImage: + description: ORDS Image Name type: string ordsImagePullPolicy: + description: ORDS Image Pull Policy enum: - Always - Never type: string ordsImagePullSecret: + description: The name of the image pull secret in case of a private docker repository. type: string ordsPort: + description: ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. type: integer ordsPwd: + description: Password for user ORDS_PUBLIC_USER properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -745,12 +818,16 @@ spec: - secret type: object replicas: + description: Number of ORDS Containers to create type: integer serviceName: + description: Name of the CDB Service type: string sysAdminPwd: + description: Password for the CDB System Administrator properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -764,8 +841,10 @@ spec: - secret type: object webServerPwd: + description: Password for the Web Server User properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -779,8 +858,10 @@ spec: - secret type: object webServerUser: + description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -795,12 +876,16 @@ spec: type: object type: object status: + description: CDBStatus defines the observed state of CDB properties: msg: + description: Message type: string phase: + description: Phase of the CDB Resource type: string status: + description: CDB Resource Status type: boolean required: - phase @@ -844,16 +929,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver properties: database: + description: DatabaseObserverDatabase defines the database details used for DatabaseObserver properties: dbConnectionString: properties: @@ -889,10 +979,12 @@ spec: type: object type: object exporter: + description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver properties: configuration: properties: configmap: + description: ConfigMapDetails defines the configmap name properties: configmapName: type: string @@ -903,6 +995,7 @@ spec: image: type: string service: + description: DatabaseObserverService defines the exporter service component of DatabaseObserver properties: port: format: int32 @@ -917,6 +1010,7 @@ spec: type: string type: object prometheus: + description: PrometheusConfig defines the generated resources for Prometheus properties: labels: additionalProperties: @@ -930,32 +1024,41 @@ spec: type: integer type: object status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver properties: conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1032,14 +1135,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: DataguardBroker is the Schema for the dataguardbrokers API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DataguardBrokerSpec defines the desired state of DataguardBroker properties: fastStartFailOver: properties: @@ -1047,6 +1154,7 @@ spec: type: boolean strategy: items: + description: FSFO strategy properties: sourceDatabaseRef: type: string @@ -1084,6 +1192,7 @@ spec: - standbyDatabaseRefs type: object status: + description: DataguardBrokerStatus defines the observed state of DataguardBroker properties: clusterConnectString: type: string @@ -1382,16 +1491,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: OracleRestDataService is the Schema for the oraclerestdataservices API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService properties: adminPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1404,6 +1518,7 @@ spec: - secretName type: object apexPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1418,6 +1533,7 @@ spec: databaseRef: type: string image: + description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD properties: pullFrom: type: string @@ -1437,6 +1553,7 @@ spec: oracleService: type: string ordsPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1451,6 +1568,7 @@ spec: ordsUser: type: string persistence: + description: OracleRestDataServicePersistence defines the storage releated params properties: accessMode: enum: @@ -1469,6 +1587,7 @@ spec: type: integer restEnableSchemas: items: + description: OracleRestDataServicePDBSchemas defines the PDB Schemas to be ORDS Enabled properties: enable: type: boolean @@ -1495,6 +1614,7 @@ spec: - ordsPassword type: object status: + description: OracleRestDataServiceStatus defines the observed state of OracleRestDataService properties: apexConfigured: type: boolean @@ -1509,6 +1629,7 @@ spec: databaseRef: type: string image: + description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD properties: pullFrom: type: string @@ -1528,6 +1649,7 @@ spec: serviceIP: type: string status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string type: object type: object @@ -1590,16 +1712,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: PDB is the Schema for the pdbs API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: PDBSpec defines the desired state of PDB properties: action: + description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR.' enum: - Create - Clone @@ -1611,8 +1738,10 @@ spec: - Map type: string adminName: + description: The administrator username for the new PDB. This property is required when the Action property is Create. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1626,8 +1755,10 @@ spec: - secret type: object adminPwd: + description: The administrator password for the new PDB. This property is required when the Action property is Create. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1641,31 +1772,41 @@ spec: - secret type: object asClone: + description: Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. type: boolean assertivePdbDeletion: + description: turn on the assertive approach to delete pdb resource kubectl delete pdb ..... automatically triggers the pluggable database deletion type: boolean cdbName: + description: Name of the CDB type: string cdbNamespace: + description: CDB Namespace type: string cdbResName: + description: Name of the CDB Custom Resource that runs the ORDS container type: string copyAction: + description: To copy files or not while cloning a PDB enum: - COPY - NOCOPY - MOVE type: string dropAction: + description: Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). enum: - INCLUDING - KEEP type: string fileNameConversions: + description: Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. type: string getScript: + description: Whether you need the script only or execute the script type: boolean modifyOption: + description: Extra options for opening and closing a PDB enum: - IMMEDIATE - NORMAL @@ -1674,8 +1815,10 @@ spec: - RESTRICTED type: string pdbName: + description: The name of the new PDB. Relevant for both Create and Plug Actions. type: string pdbState: + description: The target state of the PDB enum: - OPEN - CLOSE @@ -1683,6 +1826,7 @@ spec: pdbTlsCat: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1698,6 +1842,7 @@ spec: pdbTlsCrt: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1713,6 +1858,7 @@ spec: pdbTlsKey: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1726,22 +1872,31 @@ spec: - secret type: object reuseTempFile: + description: Whether to reuse temp file type: boolean sourceFileNameConversions: + description: This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. type: string sparseClonePath: + description: A Path specified for sparse clone snapshot copy. (Optional) type: string srcPdbName: + description: Name of the Source PDB from which to clone type: string tdeExport: + description: TDE export for unplug operations type: boolean tdeImport: + description: TDE import for plug operations type: boolean tdeKeystorePath: + description: TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. type: string tdePassword: + description: TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1755,8 +1910,10 @@ spec: - secret type: object tdeSecret: + description: TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1770,14 +1927,19 @@ spec: - secret type: object tempSize: + description: Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. type: string totalSize: + description: Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. type: string unlimitedStorage: + description: Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. type: boolean webServerPwd: + description: Password for the Web ServerPDB User properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1791,8 +1953,10 @@ spec: - secret type: object webServerUser: + description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1806,27 +1970,37 @@ spec: - secret type: object xmlFileName: + description: XML metadata filename to be used for Plug or Unplug operations type: string required: - action type: object status: + description: PDBStatus defines the observed state of PDB properties: action: + description: Last Completed Action type: string connString: + description: PDB Connect String type: string modifyOption: + description: Modify Option of the PDB type: string msg: + description: Message type: string openMode: + description: Open mode of the PDB type: string phase: + description: Phase of the PDB Resource type: string status: + description: PDB Resource Status type: boolean totalSize: + description: Total size of the PDB type: string required: - phase @@ -1874,22 +2048,28 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: ShardingDatabase is the Schema for the shardingdatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: InvitedNodeSubnet: type: string catalog: items: + description: CatalogSpec defines the desired state of CatalogSpec properties: envVars: items: + description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -1901,6 +2081,7 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: type: string @@ -1923,11 +2104,15 @@ spec: pvcName: type: string resources: + description: ResourceRequirements describes the compute resource requirements. properties: claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -1943,6 +2128,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -1951,6 +2137,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -1967,6 +2154,7 @@ spec: dbImagePullSecret: type: string dbSecret: + description: Secret Details properties: encryptionType: type: string @@ -1994,11 +2182,14 @@ spec: type: string gsm: items: + description: GsmSpec defines the desired state of GsmSpec properties: directorName: type: string envVars: + description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. items: + description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2010,6 +2201,7 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: type: string @@ -2030,11 +2222,15 @@ spec: region: type: string resources: + description: ResourceRequirements describes the compute resource requirements. properties: claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2050,6 +2246,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2058,6 +2255,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -2075,6 +2273,7 @@ spec: type: string gsmService: items: + description: Service Definition properties: available: type: string @@ -2153,6 +2352,7 @@ spec: type: array gsmShardSpace: items: + description: ShardSpace Specs properties: chunks: type: integer @@ -2188,6 +2388,7 @@ spec: type: string portMappings: items: + description: PortMapping is a specification of port mapping for an application deployment. properties: port: format: int32 @@ -2211,12 +2412,15 @@ spec: scriptsLocation: type: string shard: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' items: + description: ShardSpec is a specification of Shards for an application deployment. properties: deployAs: type: string envVars: items: + description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2228,6 +2432,7 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: enum: @@ -2255,11 +2460,15 @@ spec: pvcName: type: string resources: + description: ResourceRequirements describes the compute resource requirements. properties: claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2275,6 +2484,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2283,6 +2493,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object shardGroup: @@ -2324,6 +2535,7 @@ spec: - shard type: object status: + description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 ShardingDatabaseStatus defines the observed state of ShardingDatabase properties: catalogs: additionalProperties: @@ -2331,29 +2543,36 @@ spec: type: object conditions: items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2457,16 +2676,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase properties: adminPassword: + description: SingleInsatnceAdminPassword defines the secret containing Admin Password mapped to secretKey for Database properties: keepSecret: type: boolean @@ -2504,6 +2728,7 @@ spec: forceLog: type: boolean image: + description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD properties: prebuiltDB: type: boolean @@ -2517,6 +2742,7 @@ spec: - pullFrom type: object initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -2538,6 +2764,7 @@ spec: pdbName: type: string persistence: + description: SingleInstanceDatabasePersistence defines the storage size and class for PVC properties: accessMode: enum: @@ -2587,6 +2814,7 @@ spec: type: string type: object sid: + description: SID must be alphanumeric (no special characters, only a-z, A-Z, 0-9), and no longer than 12 characters. maxLength: 12 pattern: ^[a-zA-Z0-9]+$ type: string @@ -2600,6 +2828,7 @@ spec: - image type: object status: + description: SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase properties: apexInstalled: type: boolean @@ -2617,29 +2846,36 @@ spec: type: string conditions: items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2673,6 +2909,7 @@ spec: forceLog: type: string initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -2703,6 +2940,7 @@ spec: pdbName: type: string persistence: + description: SingleInstanceDatabasePersistence defines the storage size and class for PVC properties: accessMode: enum: @@ -3929,7 +4167,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/operatormntnns:latest + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator-VV imagePullPolicy: Always name: manager ports: From 3cd442978a942780602b51e28b0452b5137a4be7 Mon Sep 17 00:00:00 2001 From: thirumalai_thathachary Date: Wed, 28 Aug 2024 20:54:54 +0000 Subject: [PATCH 118/414] Review bug36738203 fix --- commons/sharding/catalog.go | 12 + commons/sharding/scommon.go | 4 + .../bases/database.oracle.com_DbcsSystem.yaml | 11 + ...acle.com_autonomouscontainerdatabases.yaml | 20 ++ ....oracle.com_autonomousdatabasebackups.yaml | 20 ++ ...oracle.com_autonomousdatabaserestores.yaml | 25 ++ ...tabase.oracle.com_autonomousdatabases.yaml | 64 +++++ .../crd/bases/database.oracle.com_cdbs.yaml | 40 +++ .../database.oracle.com_dataguardbrokers.yaml | 10 + ...ase.oracle.com_oraclerestdataservices.yaml | 28 ++ .../crd/bases/database.oracle.com_pdbs.yaml | 80 ++++++ ...database.oracle.com_shardingdatabases.yaml | 127 +++++++++ ...se.oracle.com_singleinstancedatabases.yaml | 57 +++++ ...vability.oracle.com_databaseobservers.yaml | 55 ++++ oracle-database-operator.yaml | 240 +++++++++++++++++- 15 files changed, 792 insertions(+), 1 deletion(-) diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index e325ce6c..cb3a3c29 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -375,6 +375,17 @@ func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, }, } + if len(OraCatalogSpex.PvAnnotations) > 0 { + claims[0].ObjectMeta.Annotations = make(map[string]string) + for key, value := range OraCatalogSpex.PvAnnotations { + claims[0].ObjectMeta.Annotations[key] = value + } + } + + if len(OraCatalogSpex.PvMatchLabels) > 0 { + claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} + } + if checkTdeWalletFlag(instance) { if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { { @@ -515,3 +526,4 @@ func UpdateProvForCatalog(instance *databasev1alpha1.ShardingDatabase, return ctrl.Result{}, nil } + diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 98892962..a88946d0 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -294,6 +294,10 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da result = append(result, corev1.EnvVar{Name: "DEV_MODE", Value: "TRUE"}) } + if instance.Spec.InvitedNodeSubnetFlag == "" { + instance.Spec.InvitedNodeSubnetFlag = "FALSE" + + } if strings.ToUpper(instance.Spec.InvitedNodeSubnetFlag) != "FALSE" { result = append(result, corev1.EnvVar{Name: "INVITED_NODE_SUBNET_FLAG", Value: "TRUE"}) if instance.Spec.InvitedNodeSubnet != "" { diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml index 970954a1..e933d5a4 100644 --- a/config/crd/bases/database.oracle.com_DbcsSystem.yaml +++ b/config/crd/bases/database.oracle.com_DbcsSystem.yaml @@ -19,14 +19,22 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: + description: DbcsSystem is the Schema for the dbcssystems API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DbcsSystemSpec defines the desired state of DbcsSystem properties: dbSystem: properties: @@ -43,6 +51,7 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: + description: DB Backup COnfig Network Struct properties: autoBackupEnabled: type: boolean @@ -130,6 +139,7 @@ spec: - ociConfigMap type: object status: + description: DbcsSystemStatus defines the observed state of DbcsSystem properties: availabilityDomain: type: string @@ -143,6 +153,7 @@ spec: type: string dbInfo: items: + description: DbcsSystemStatus defines the observed state of DbcsSystem properties: dbHomeId: type: string diff --git a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml index 875eeaef..bac3a28c 100644 --- a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml @@ -32,14 +32,24 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousContainerDatabaseSpec defines the desired state + of AutonomousContainerDatabase properties: action: enum: @@ -48,6 +58,8 @@ spec: - TERMINATE type: string autonomousContainerDatabaseOCID: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' type: string autonomousExadataVMClusterOCID: type: string @@ -63,6 +75,7 @@ spec: default: false type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -70,14 +83,21 @@ spec: type: string type: object patchModel: + description: 'AutonomousContainerDatabasePatchModelEnum Enum with + underlying type: string' enum: - RELEASE_UPDATES - RELEASE_UPDATE_REVISIONS type: string type: object status: + description: AutonomousContainerDatabaseStatus defines the observed state + of AutonomousContainerDatabase properties: lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' type: string timeCreated: type: string diff --git a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml index 71fdbfa4..a5c37507 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml @@ -38,14 +38,24 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousDatabaseBackupSpec defines the desired state of + AutonomousDatabaseBackup properties: autonomousDatabaseBackupOCID: type: string @@ -54,6 +64,7 @@ spec: isLongTermBackup: type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -63,8 +74,11 @@ spec: retentionPeriodInDays: type: integer target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: + description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -77,6 +91,8 @@ spec: type: object type: object status: + description: AutonomousDatabaseBackupStatus defines the observed state + of AutonomousDatabaseBackup properties: autonomousDatabaseOCID: type: string @@ -89,12 +105,16 @@ spec: isAutomatic: type: boolean lifecycleState: + description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with + underlying type: string' type: string timeEnded: type: string timeStarted: type: string type: + description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying + type: string' type: string required: - autonomousDatabaseOCID diff --git a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml index f6173b5b..5e9f2c73 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml @@ -32,16 +32,27 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousDatabaseRestoreSpec defines the desired state of + AutonomousDatabaseRestore properties: ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -51,6 +62,9 @@ spec: source: properties: k8sADBBackup: + description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO + OWN! NOTE: json tags are required. Any new fields you add must + have json tags for the fields to be serialized.' properties: name: type: string @@ -58,12 +72,17 @@ spec: pointInTime: properties: timestamp: + description: 'The timestamp must follow this format: YYYY-MM-DD + HH:MM:SS GMT' type: string type: object type: object target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: + description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -79,12 +98,18 @@ spec: - target type: object status: + description: AutonomousDatabaseRestoreStatus defines the observed state + of AutonomousDatabaseRestore properties: dbName: type: string displayName: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' type: string status: + description: 'WorkRequestStatusEnum Enum with underlying type: string' type: string timeAccepted: type: string diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index d494b5a6..f77407f3 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -47,20 +47,33 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabase is the Schema for the autonomousdatabases + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase + Important: Run "make" to regenerate code after modifying this file' properties: details: + description: AutonomousDatabaseDetails defines the detail information + of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase properties: adminPassword: properties: k8sSecret: + description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -72,8 +85,12 @@ spec: type: object type: object autonomousContainerDatabase: + description: ACDSpec defines the spec of the target for backup/restore + runs. The name could be the name of an AutonomousDatabase or + an AutonomousDatabaseBackup properties: k8sACD: + description: "*********************** *\tACD specs ***********************" properties: name: type: string @@ -97,6 +114,8 @@ spec: dbVersion: type: string dbWorkload: + description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying + type: string' enum: - OLTP - DW @@ -114,11 +133,15 @@ spec: isDedicated: type: boolean licenseModel: + description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying + type: string' enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string lifecycleState: + description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying + type: string' type: string networkAccess: properties: @@ -156,6 +179,8 @@ spec: password: properties: k8sSecret: + description: "*********************** *\tSecret specs + ***********************" properties: name: type: string @@ -172,6 +197,7 @@ spec: default: false type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -182,6 +208,7 @@ spec: - details type: object status: + description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase properties: allConnectionStrings: items: @@ -203,29 +230,63 @@ spec: type: array conditions: items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -241,6 +302,9 @@ spec: - type x-kubernetes-list-type: map lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' type: string timeCreated: type: string diff --git a/config/crd/bases/database.oracle.com_cdbs.yaml b/config/crd/bases/database.oracle.com_cdbs.yaml index 3c564c85..6b1c350c 100644 --- a/config/crd/bases/database.oracle.com_cdbs.yaml +++ b/config/crd/bases/database.oracle.com_cdbs.yaml @@ -48,18 +48,28 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: CDB is the Schema for the cdbs API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: CDBSpec defines the desired state of CDB properties: cdbAdminPwd: + description: Password for the CDB Administrator to manage PDB lifecycle properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -73,8 +83,11 @@ spec: - secret type: object cdbAdminUser: + description: User in the root container with sysdba priviledges to + manage PDB lifecycle properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -88,10 +101,12 @@ spec: - secret type: object cdbName: + description: Name of the CDB type: string cdbTlsCrt: properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -107,6 +122,7 @@ spec: cdbTlsKey: properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -120,29 +136,40 @@ spec: - secret type: object dbPort: + description: DB server port type: integer dbServer: + description: Name of the DB server type: string dbTnsurl: type: string nodeSelector: additionalProperties: type: string + description: Node Selector for running the Pod type: object ordsImage: + description: ORDS Image Name type: string ordsImagePullPolicy: + description: ORDS Image Pull Policy enum: - Always - Never type: string ordsImagePullSecret: + description: The name of the image pull secret in case of a private + docker repository. type: string ordsPort: + description: ORDS server port. For now, keep it as 8888. TO BE USED + IN FUTURE RELEASE. type: integer ordsPwd: + description: Password for user ORDS_PUBLIC_USER properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -156,12 +183,16 @@ spec: - secret type: object replicas: + description: Number of ORDS Containers to create type: integer serviceName: + description: Name of the CDB Service type: string sysAdminPwd: + description: Password for the CDB System Administrator properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -175,8 +206,10 @@ spec: - secret type: object webServerPwd: + description: Password for the Web Server User properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -190,8 +223,11 @@ spec: - secret type: object webServerUser: + description: Web Server User with SQL Administrator role to allow + us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -206,12 +242,16 @@ spec: type: object type: object status: + description: CDBStatus defines the observed state of CDB properties: msg: + description: Message type: string phase: + description: Phase of the CDB Resource type: string status: + description: CDB Resource Status type: boolean required: - phase diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index 33bc2e2a..f19a3e22 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -43,14 +43,22 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: DataguardBroker is the Schema for the dataguardbrokers API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DataguardBrokerSpec defines the desired state of DataguardBroker properties: fastStartFailOver: properties: @@ -58,6 +66,7 @@ spec: type: boolean strategy: items: + description: FSFO strategy properties: sourceDatabaseRef: type: string @@ -95,6 +104,7 @@ spec: - standbyDatabaseRefs type: object status: + description: DataguardBrokerStatus defines the observed state of DataguardBroker properties: clusterConnectString: type: string diff --git a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml index 8264ff93..121383fd 100644 --- a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml @@ -35,16 +35,27 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: OracleRestDataService is the Schema for the oraclerestdataservices + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService properties: adminPassword: + description: OracleRestDataServicePassword defines the secret containing + Password mapped to secretKey properties: keepSecret: type: boolean @@ -57,6 +68,8 @@ spec: - secretName type: object apexPassword: + description: OracleRestDataServicePassword defines the secret containing + Password mapped to secretKey properties: keepSecret: type: boolean @@ -71,6 +84,8 @@ spec: databaseRef: type: string image: + description: OracleRestDataServiceImage defines the Image source and + pullSecrets for POD properties: pullFrom: type: string @@ -90,6 +105,8 @@ spec: oracleService: type: string ordsPassword: + description: OracleRestDataServicePassword defines the secret containing + Password mapped to secretKey properties: keepSecret: type: boolean @@ -104,6 +121,8 @@ spec: ordsUser: type: string persistence: + description: OracleRestDataServicePersistence defines the storage + releated params properties: accessMode: enum: @@ -122,6 +141,8 @@ spec: type: integer restEnableSchemas: items: + description: OracleRestDataServicePDBSchemas defines the PDB Schemas + to be ORDS Enabled properties: enable: type: boolean @@ -148,6 +169,8 @@ spec: - ordsPassword type: object status: + description: OracleRestDataServiceStatus defines the observed state of + OracleRestDataService properties: apexConfigured: type: boolean @@ -162,6 +185,8 @@ spec: databaseRef: type: string image: + description: OracleRestDataServiceImage defines the Image source and + pullSecrets for POD properties: pullFrom: type: string @@ -181,6 +206,9 @@ spec: serviceIP: type: string status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' type: string type: object type: object diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index 2a72da27..85af8c1b 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -48,16 +48,26 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: PDB is the Schema for the pdbs API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: PDBSpec defines the desired state of PDB properties: action: + description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. + Map is used to map a Databse PDB to a Kubernetes PDB CR.' enum: - Create - Clone @@ -69,8 +79,11 @@ spec: - Map type: string adminName: + description: The administrator username for the new PDB. This property + is required when the Action property is Create. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -84,8 +97,11 @@ spec: - secret type: object adminPwd: + description: The administrator password for the new PDB. This property + is required when the Action property is Create. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -99,31 +115,48 @@ spec: - secret type: object asClone: + description: Indicate if 'AS CLONE' option should be used in the command + to plug in a PDB. This property is applicable when the Action property + is PLUG but not required. type: boolean assertivePdbDeletion: + description: turn on the assertive approach to delete pdb resource + kubectl delete pdb ..... automatically triggers the pluggable database + deletion type: boolean cdbName: + description: Name of the CDB type: string cdbNamespace: + description: CDB Namespace type: string cdbResName: + description: Name of the CDB Custom Resource that runs the ORDS container type: string copyAction: + description: To copy files or not while cloning a PDB enum: - COPY - NOCOPY - MOVE type: string dropAction: + description: Specify if datafiles should be removed or not. The value + can be INCLUDING or KEEP (default). enum: - INCLUDING - KEEP type: string fileNameConversions: + description: Relevant for Create and Plug operations. As defined in + the Oracle Multitenant Database documentation. Values can be a + filename convert pattern or NONE. type: string getScript: + description: Whether you need the script only or execute the script type: boolean modifyOption: + description: Extra options for opening and closing a PDB enum: - IMMEDIATE - NORMAL @@ -132,8 +165,11 @@ spec: - RESTRICTED type: string pdbName: + description: The name of the new PDB. Relevant for both Create and + Plug Actions. type: string pdbState: + description: The target state of the PDB enum: - OPEN - CLOSE @@ -141,6 +177,7 @@ spec: pdbTlsCat: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -156,6 +193,7 @@ spec: pdbTlsCrt: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -171,6 +209,7 @@ spec: pdbTlsKey: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -184,22 +223,35 @@ spec: - secret type: object reuseTempFile: + description: Whether to reuse temp file type: boolean sourceFileNameConversions: + description: This property is required when the Action property is + Plug. As defined in the Oracle Multitenant Database documentation. + Values can be a source filename convert pattern or NONE. type: string sparseClonePath: + description: A Path specified for sparse clone snapshot copy. (Optional) type: string srcPdbName: + description: Name of the Source PDB from which to clone type: string tdeExport: + description: TDE export for unplug operations type: boolean tdeImport: + description: TDE import for plug operations type: boolean tdeKeystorePath: + description: TDE keystore path is required if the tdeImport or tdeExport + flag is set to true. Can be used in plug or unplug operations. type: string tdePassword: + description: TDE password if the tdeImport or tdeExport flag is set + to true. Can be used in create, plug or unplug operations properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -213,8 +265,11 @@ spec: - secret type: object tdeSecret: + description: TDE secret is required if the tdeImport or tdeExport + flag is set to true. Can be used in plug or unplug operations. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -228,14 +283,26 @@ spec: - secret type: object tempSize: + description: Relevant for Create and Clone operations. Total size + for temporary tablespace as defined in the Oracle Multitenant Database + documentation. See size_clause description in Database SQL Language + Reference documentation. type: string totalSize: + description: Relevant for create and plug operations. Total size as + defined in the Oracle Multitenant Database documentation. See size_clause + description in Database SQL Language Reference documentation. type: string unlimitedStorage: + description: Relevant for Create and Plug operations. True for unlimited + storage. Even when set to true, totalSize and tempSize MUST be specified + in the request if Action is Create. type: boolean webServerPwd: + description: Password for the Web ServerPDB User properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -249,8 +316,11 @@ spec: - secret type: object webServerUser: + description: Web Server User with SQL Administrator role to allow + us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -264,27 +334,37 @@ spec: - secret type: object xmlFileName: + description: XML metadata filename to be used for Plug or Unplug operations type: string required: - action type: object status: + description: PDBStatus defines the observed state of PDB properties: action: + description: Last Completed Action type: string connString: + description: PDB Connect String type: string modifyOption: + description: Modify Option of the PDB type: string msg: + description: Message type: string openMode: + description: Open mode of the PDB type: string phase: + description: Phase of the PDB Resource type: string status: + description: PDB Resource Status type: boolean totalSize: + description: Total size of the PDB type: string required: - phase diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index e0959a4c..641629a0 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -30,22 +30,33 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: ShardingDatabase is the Schema for the shardingdatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: InvitedNodeSubnet: type: string catalog: items: + description: CatalogSpec defines the desired state of CatalogSpec properties: envVars: items: + description: EnvironmentVariable represents a named variable + accessible for containers. properties: name: type: string @@ -57,6 +68,8 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image type: string isDelete: type: string @@ -79,11 +92,23 @@ spec: pvcName: type: string resources: + description: ResourceRequirements describes the compute resource + requirements. properties: claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. type: string required: - name @@ -99,6 +124,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -107,6 +134,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -123,6 +155,7 @@ spec: dbImagePullSecret: type: string dbSecret: + description: Secret Details properties: encryptionType: type: string @@ -150,11 +183,17 @@ spec: type: string gsm: items: + description: GsmSpec defines the desired state of GsmSpec properties: directorName: type: string envVars: + description: Replicas int32 `json:"replicas,omitempty"` // + Gsm Replicas. If you set OraGsmPvcName then it is set default + to 1. items: + description: EnvironmentVariable represents a named variable + accessible for containers. properties: name: type: string @@ -166,6 +205,8 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image type: string isDelete: type: string @@ -186,11 +227,23 @@ spec: region: type: string resources: + description: ResourceRequirements describes the compute resource + requirements. properties: claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. type: string required: - name @@ -206,6 +259,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -214,6 +269,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -231,6 +291,7 @@ spec: type: string gsmService: items: + description: Service Definition properties: available: type: string @@ -309,6 +370,7 @@ spec: type: array gsmShardSpace: items: + description: ShardSpace Specs properties: chunks: type: integer @@ -344,6 +406,8 @@ spec: type: string portMappings: items: + description: PortMapping is a specification of port mapping for + an application deployment. properties: port: format: int32 @@ -367,12 +431,18 @@ spec: scriptsLocation: type: string shard: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' items: + description: ShardSpec is a specification of Shards for an application + deployment. properties: deployAs: type: string envVars: items: + description: EnvironmentVariable represents a named variable + accessible for containers. properties: name: type: string @@ -384,6 +454,8 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image type: string isDelete: enum: @@ -411,11 +483,23 @@ spec: pvcName: type: string resources: + description: ResourceRequirements describes the compute resource + requirements. properties: claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. type: string required: - name @@ -431,6 +515,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -439,6 +525,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object shardGroup: @@ -480,6 +571,8 @@ spec: - shard type: object status: + description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 + ShardingDatabaseStatus defines the observed state of ShardingDatabase properties: catalogs: additionalProperties: @@ -487,29 +580,63 @@ spec: type: object conditions: items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index 3a5d2ce4..1c011e17 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -53,16 +53,27 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: SingleInstanceDatabase is the Schema for the singleinstancedatabases + API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase properties: adminPassword: + description: SingleInsatnceAdminPassword defines the secret containing + Admin Password mapped to secretKey for Database properties: keepSecret: type: boolean @@ -100,6 +111,8 @@ spec: forceLog: type: boolean image: + description: SingleInstanceDatabaseImage defines the Image source + and pullSecrets for POD properties: prebuiltDB: type: boolean @@ -113,6 +126,7 @@ spec: - pullFrom type: object initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -134,6 +148,8 @@ spec: pdbName: type: string persistence: + description: SingleInstanceDatabasePersistence defines the storage + size and class for PVC properties: accessMode: enum: @@ -183,6 +199,8 @@ spec: type: string type: object sid: + description: SID must be alphanumeric (no special characters, only + a-z, A-Z, 0-9), and no longer than 12 characters. maxLength: 12 pattern: ^[a-zA-Z0-9]+$ type: string @@ -196,6 +214,8 @@ spec: - image type: object status: + description: SingleInstanceDatabaseStatus defines the observed state of + SingleInstanceDatabase properties: apexInstalled: type: boolean @@ -213,29 +233,63 @@ spec: type: string conditions: items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -269,6 +323,7 @@ spec: forceLog: type: string initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -299,6 +354,8 @@ spec: pdbName: type: string persistence: + description: SingleInstanceDatabasePersistence defines the storage + size and class for PVC properties: accessMode: enum: diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index d9aee464..b0801738 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -26,16 +26,26 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver properties: database: + description: DatabaseObserverDatabase defines the database details + used for DatabaseObserver properties: dbConnectionString: properties: @@ -71,10 +81,13 @@ spec: type: object type: object exporter: + description: DatabaseObserverExporterConfig defines the configuration + details related to the exporters of DatabaseObserver properties: configuration: properties: configmap: + description: ConfigMapDetails defines the configmap name properties: configmapName: type: string @@ -85,6 +98,8 @@ spec: image: type: string service: + description: DatabaseObserverService defines the exporter service + component of DatabaseObserver properties: port: format: int32 @@ -99,6 +114,8 @@ spec: type: string type: object prometheus: + description: PrometheusConfig defines the generated resources for + Prometheus properties: labels: additionalProperties: @@ -112,32 +129,70 @@ spec: type: integer type: object status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver properties: conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 9185ab9c..cec36793 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -37,14 +37,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase properties: action: enum: @@ -53,6 +57,7 @@ spec: - TERMINATE type: string autonomousContainerDatabaseOCID: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' type: string autonomousExadataVMClusterOCID: type: string @@ -68,6 +73,7 @@ spec: default: false type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -75,14 +81,17 @@ spec: type: string type: object patchModel: + description: 'AutonomousContainerDatabasePatchModelEnum Enum with underlying type: string' enum: - RELEASE_UPDATES - RELEASE_UPDATE_REVISIONS type: string type: object status: + description: AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase properties: lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string timeCreated: type: string @@ -139,14 +148,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousDatabaseBackupSpec defines the desired state of AutonomousDatabaseBackup properties: autonomousDatabaseBackupOCID: type: string @@ -155,6 +168,7 @@ spec: isLongTermBackup: type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -164,8 +178,10 @@ spec: retentionPeriodInDays: type: integer target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: + description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -178,6 +194,7 @@ spec: type: object type: object status: + description: AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup properties: autonomousDatabaseOCID: type: string @@ -190,12 +207,14 @@ spec: isAutomatic: type: boolean lifecycleState: + description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with underlying type: string' type: string timeEnded: type: string timeStarted: type: string type: + description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying type: string' type: string required: - autonomousDatabaseOCID @@ -250,16 +269,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore properties: ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -269,6 +293,7 @@ spec: source: properties: k8sADBBackup: + description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.' properties: name: type: string @@ -276,12 +301,15 @@ spec: pointInTime: properties: timestamp: + description: 'The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT' type: string type: object type: object target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: + description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -297,12 +325,15 @@ spec: - target type: object status: + description: AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore properties: dbName: type: string displayName: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string status: + description: 'WorkRequestStatusEnum Enum with underlying type: string' type: string timeAccepted: type: string @@ -377,20 +408,26 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: AutonomousDatabase is the Schema for the autonomousdatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase Important: Run "make" to regenerate code after modifying this file' properties: details: + description: AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase properties: adminPassword: properties: k8sSecret: + description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -402,8 +439,10 @@ spec: type: object type: object autonomousContainerDatabase: + description: ACDSpec defines the spec of the target for backup/restore runs. The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup properties: k8sACD: + description: "*********************** *\tACD specs ***********************" properties: name: type: string @@ -427,6 +466,7 @@ spec: dbVersion: type: string dbWorkload: + description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying type: string' enum: - OLTP - DW @@ -444,11 +484,13 @@ spec: isDedicated: type: boolean licenseModel: + description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying type: string' enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string lifecycleState: + description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' type: string networkAccess: properties: @@ -486,6 +528,7 @@ spec: password: properties: k8sSecret: + description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -502,6 +545,7 @@ spec: default: false type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -512,6 +556,7 @@ spec: - details type: object status: + description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase properties: allConnectionStrings: items: @@ -533,29 +578,36 @@ spec: type: array conditions: items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -571,6 +623,7 @@ spec: - type x-kubernetes-list-type: map lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string timeCreated: type: string @@ -637,18 +690,24 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: CDB is the Schema for the cdbs API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: CDBSpec defines the desired state of CDB properties: cdbAdminPwd: + description: Password for the CDB Administrator to manage PDB lifecycle properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -662,8 +721,10 @@ spec: - secret type: object cdbAdminUser: + description: User in the root container with sysdba priviledges to manage PDB lifecycle properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -677,10 +738,12 @@ spec: - secret type: object cdbName: + description: Name of the CDB type: string cdbTlsCrt: properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -696,6 +759,7 @@ spec: cdbTlsKey: properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -709,29 +773,38 @@ spec: - secret type: object dbPort: + description: DB server port type: integer dbServer: + description: Name of the DB server type: string dbTnsurl: type: string nodeSelector: additionalProperties: type: string + description: Node Selector for running the Pod type: object ordsImage: + description: ORDS Image Name type: string ordsImagePullPolicy: + description: ORDS Image Pull Policy enum: - Always - Never type: string ordsImagePullSecret: + description: The name of the image pull secret in case of a private docker repository. type: string ordsPort: + description: ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. type: integer ordsPwd: + description: Password for user ORDS_PUBLIC_USER properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -745,12 +818,16 @@ spec: - secret type: object replicas: + description: Number of ORDS Containers to create type: integer serviceName: + description: Name of the CDB Service type: string sysAdminPwd: + description: Password for the CDB System Administrator properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -764,8 +841,10 @@ spec: - secret type: object webServerPwd: + description: Password for the Web Server User properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -779,8 +858,10 @@ spec: - secret type: object webServerUser: + description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: + description: CDBSecret defines the secretName properties: key: type: string @@ -795,12 +876,16 @@ spec: type: object type: object status: + description: CDBStatus defines the observed state of CDB properties: msg: + description: Message type: string phase: + description: Phase of the CDB Resource type: string status: + description: CDB Resource Status type: boolean required: - phase @@ -844,16 +929,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver properties: database: + description: DatabaseObserverDatabase defines the database details used for DatabaseObserver properties: dbConnectionString: properties: @@ -889,10 +979,12 @@ spec: type: object type: object exporter: + description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver properties: configuration: properties: configmap: + description: ConfigMapDetails defines the configmap name properties: configmapName: type: string @@ -903,6 +995,7 @@ spec: image: type: string service: + description: DatabaseObserverService defines the exporter service component of DatabaseObserver properties: port: format: int32 @@ -917,6 +1010,7 @@ spec: type: string type: object prometheus: + description: PrometheusConfig defines the generated resources for Prometheus properties: labels: additionalProperties: @@ -930,32 +1024,41 @@ spec: type: integer type: object status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver properties: conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1032,14 +1135,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: DataguardBroker is the Schema for the dataguardbrokers API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: DataguardBrokerSpec defines the desired state of DataguardBroker properties: fastStartFailOver: properties: @@ -1047,6 +1154,7 @@ spec: type: boolean strategy: items: + description: FSFO strategy properties: sourceDatabaseRef: type: string @@ -1084,6 +1192,7 @@ spec: - standbyDatabaseRefs type: object status: + description: DataguardBrokerStatus defines the observed state of DataguardBroker properties: clusterConnectString: type: string @@ -1382,16 +1491,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: OracleRestDataService is the Schema for the oraclerestdataservices API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService properties: adminPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1404,6 +1518,7 @@ spec: - secretName type: object apexPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1418,6 +1533,7 @@ spec: databaseRef: type: string image: + description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD properties: pullFrom: type: string @@ -1437,6 +1553,7 @@ spec: oracleService: type: string ordsPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1451,6 +1568,7 @@ spec: ordsUser: type: string persistence: + description: OracleRestDataServicePersistence defines the storage releated params properties: accessMode: enum: @@ -1469,6 +1587,7 @@ spec: type: integer restEnableSchemas: items: + description: OracleRestDataServicePDBSchemas defines the PDB Schemas to be ORDS Enabled properties: enable: type: boolean @@ -1495,6 +1614,7 @@ spec: - ordsPassword type: object status: + description: OracleRestDataServiceStatus defines the observed state of OracleRestDataService properties: apexConfigured: type: boolean @@ -1509,6 +1629,7 @@ spec: databaseRef: type: string image: + description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD properties: pullFrom: type: string @@ -1528,6 +1649,7 @@ spec: serviceIP: type: string status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string type: object type: object @@ -1590,16 +1712,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: PDB is the Schema for the pdbs API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: PDBSpec defines the desired state of PDB properties: action: + description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR.' enum: - Create - Clone @@ -1611,8 +1738,10 @@ spec: - Map type: string adminName: + description: The administrator username for the new PDB. This property is required when the Action property is Create. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1626,8 +1755,10 @@ spec: - secret type: object adminPwd: + description: The administrator password for the new PDB. This property is required when the Action property is Create. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1641,31 +1772,41 @@ spec: - secret type: object asClone: + description: Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. type: boolean assertivePdbDeletion: + description: turn on the assertive approach to delete pdb resource kubectl delete pdb ..... automatically triggers the pluggable database deletion type: boolean cdbName: + description: Name of the CDB type: string cdbNamespace: + description: CDB Namespace type: string cdbResName: + description: Name of the CDB Custom Resource that runs the ORDS container type: string copyAction: + description: To copy files or not while cloning a PDB enum: - COPY - NOCOPY - MOVE type: string dropAction: + description: Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). enum: - INCLUDING - KEEP type: string fileNameConversions: + description: Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. type: string getScript: + description: Whether you need the script only or execute the script type: boolean modifyOption: + description: Extra options for opening and closing a PDB enum: - IMMEDIATE - NORMAL @@ -1674,8 +1815,10 @@ spec: - RESTRICTED type: string pdbName: + description: The name of the new PDB. Relevant for both Create and Plug Actions. type: string pdbState: + description: The target state of the PDB enum: - OPEN - CLOSE @@ -1683,6 +1826,7 @@ spec: pdbTlsCat: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1698,6 +1842,7 @@ spec: pdbTlsCrt: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1713,6 +1858,7 @@ spec: pdbTlsKey: properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1726,22 +1872,31 @@ spec: - secret type: object reuseTempFile: + description: Whether to reuse temp file type: boolean sourceFileNameConversions: + description: This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. type: string sparseClonePath: + description: A Path specified for sparse clone snapshot copy. (Optional) type: string srcPdbName: + description: Name of the Source PDB from which to clone type: string tdeExport: + description: TDE export for unplug operations type: boolean tdeImport: + description: TDE import for plug operations type: boolean tdeKeystorePath: + description: TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. type: string tdePassword: + description: TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1755,8 +1910,10 @@ spec: - secret type: object tdeSecret: + description: TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1770,14 +1927,19 @@ spec: - secret type: object tempSize: + description: Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. type: string totalSize: + description: Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. type: string unlimitedStorage: + description: Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. type: boolean webServerPwd: + description: Password for the Web ServerPDB User properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1791,8 +1953,10 @@ spec: - secret type: object webServerUser: + description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: + description: PDBSecret defines the secretName properties: key: type: string @@ -1806,27 +1970,37 @@ spec: - secret type: object xmlFileName: + description: XML metadata filename to be used for Plug or Unplug operations type: string required: - action type: object status: + description: PDBStatus defines the observed state of PDB properties: action: + description: Last Completed Action type: string connString: + description: PDB Connect String type: string modifyOption: + description: Modify Option of the PDB type: string msg: + description: Message type: string openMode: + description: Open mode of the PDB type: string phase: + description: Phase of the PDB Resource type: string status: + description: PDB Resource Status type: boolean totalSize: + description: Total size of the PDB type: string required: - phase @@ -1874,22 +2048,28 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: ShardingDatabase is the Schema for the shardingdatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: InvitedNodeSubnet: type: string catalog: items: + description: CatalogSpec defines the desired state of CatalogSpec properties: envVars: items: + description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -1901,6 +2081,7 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: type: string @@ -1923,11 +2104,15 @@ spec: pvcName: type: string resources: + description: ResourceRequirements describes the compute resource requirements. properties: claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -1943,6 +2128,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -1951,6 +2137,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -1967,6 +2154,7 @@ spec: dbImagePullSecret: type: string dbSecret: + description: Secret Details properties: encryptionType: type: string @@ -1994,11 +2182,14 @@ spec: type: string gsm: items: + description: GsmSpec defines the desired state of GsmSpec properties: directorName: type: string envVars: + description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. items: + description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2010,6 +2201,7 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: type: string @@ -2030,11 +2222,15 @@ spec: region: type: string resources: + description: ResourceRequirements describes the compute resource requirements. properties: claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2050,6 +2246,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2058,6 +2255,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -2075,6 +2273,7 @@ spec: type: string gsmService: items: + description: Service Definition properties: available: type: string @@ -2153,6 +2352,7 @@ spec: type: array gsmShardSpace: items: + description: ShardSpace Specs properties: chunks: type: integer @@ -2188,6 +2388,7 @@ spec: type: string portMappings: items: + description: PortMapping is a specification of port mapping for an application deployment. properties: port: format: int32 @@ -2211,12 +2412,15 @@ spec: scriptsLocation: type: string shard: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' items: + description: ShardSpec is a specification of Shards for an application deployment. properties: deployAs: type: string envVars: items: + description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2228,6 +2432,7 @@ spec: type: object type: array imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: enum: @@ -2255,11 +2460,15 @@ spec: pvcName: type: string resources: + description: ResourceRequirements describes the compute resource requirements. properties: claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2275,6 +2484,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2283,6 +2493,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object shardGroup: @@ -2324,6 +2535,7 @@ spec: - shard type: object status: + description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 ShardingDatabaseStatus defines the observed state of ShardingDatabase properties: catalogs: additionalProperties: @@ -2331,29 +2543,36 @@ spec: type: object conditions: items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2457,16 +2676,21 @@ spec: name: v1alpha1 schema: openAPIV3Schema: + description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API properties: apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: + description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase properties: adminPassword: + description: SingleInsatnceAdminPassword defines the secret containing Admin Password mapped to secretKey for Database properties: keepSecret: type: boolean @@ -2504,6 +2728,7 @@ spec: forceLog: type: boolean image: + description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD properties: prebuiltDB: type: boolean @@ -2517,6 +2742,7 @@ spec: - pullFrom type: object initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -2538,6 +2764,7 @@ spec: pdbName: type: string persistence: + description: SingleInstanceDatabasePersistence defines the storage size and class for PVC properties: accessMode: enum: @@ -2587,6 +2814,7 @@ spec: type: string type: object sid: + description: SID must be alphanumeric (no special characters, only a-z, A-Z, 0-9), and no longer than 12 characters. maxLength: 12 pattern: ^[a-zA-Z0-9]+$ type: string @@ -2600,6 +2828,7 @@ spec: - image type: object status: + description: SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase properties: apexInstalled: type: boolean @@ -2617,29 +2846,36 @@ spec: type: string conditions: items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: + description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2673,6 +2909,7 @@ spec: forceLog: type: string initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -2703,6 +2940,7 @@ spec: pdbName: type: string persistence: + description: SingleInstanceDatabasePersistence defines the storage size and class for PVC properties: accessMode: enum: @@ -3929,7 +4167,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/operatormntnns:latest + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator-VV imagePullPolicy: Always name: manager ports: From df25575f525cb1f33b15f74e3e94f6f4bd6ac5cb Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Wed, 28 Aug 2024 22:01:37 +0000 Subject: [PATCH 119/414] Fix image repository --- config/manager/kustomization.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 48efb61b..2aed83d4 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: controller + newName: container-registry.oracle.com/database/operator newTag: latest From b8422c2008276937e5e4ce517f53e0af2591b644 Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Mon, 2 Sep 2024 18:31:03 +0000 Subject: [PATCH 120/414] Update README.md (#99) --- commons/sharding/catalog.go | 17 +++-- commons/sharding/scommon.go | 4 +- config/samples/kustomization.yaml | 2 +- .../database/shardingdatabase_controller.go | 62 +++++++++---------- docs/multitenant/README.md | 2 + oracle-database-operator.yaml | 19 +++++- 6 files changed, 62 insertions(+), 44 deletions(-) diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index cb3a3c29..58f07490 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -376,15 +376,15 @@ func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, } if len(OraCatalogSpex.PvAnnotations) > 0 { - claims[0].ObjectMeta.Annotations = make(map[string]string) - for key, value := range OraCatalogSpex.PvAnnotations { - claims[0].ObjectMeta.Annotations[key] = value - } - } + claims[0].ObjectMeta.Annotations = make(map[string]string) + for key, value := range OraCatalogSpex.PvAnnotations { + claims[0].ObjectMeta.Annotations[key] = value + } + } - if len(OraCatalogSpex.PvMatchLabels) > 0 { - claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} - } + if len(OraCatalogSpex.PvMatchLabels) > 0 { + claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} + } if checkTdeWalletFlag(instance) { if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { @@ -526,4 +526,3 @@ func UpdateProvForCatalog(instance *databasev1alpha1.ShardingDatabase, return ctrl.Result{}, nil } - diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index a88946d0..99987661 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -295,7 +295,7 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da } if instance.Spec.InvitedNodeSubnetFlag == "" { - instance.Spec.InvitedNodeSubnetFlag = "FALSE" + instance.Spec.InvitedNodeSubnetFlag = "FALSE" } if strings.ToUpper(instance.Spec.InvitedNodeSubnetFlag) != "FALSE" { @@ -1537,7 +1537,7 @@ func CheckIsDeleteFlag(delStr string, instance *databasealphav1.ShardingDatabase return true } if strings.ToLower(delStr) == "failed" { - // LogMessages("INFO", "manual intervention required", nil, instance, logger) + // LogMessages("INFO", "manual intervention required", nil, instance, logger) } return false } diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 907205ad..ac5e158f 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -30,4 +30,4 @@ resources: - adb/autonomousdatabase_backup.yaml - adb/autonomousdatabase_restore.yaml - acd/autonomouscontainerdatabase_restart_terminate.yaml - # +kubebuilder:scaffold:manifestskustomizesamples \ No newline at end of file + # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index b76de272..7fcaac2b 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -94,8 +94,8 @@ type ShardingDatabaseReconciler struct { Namespace string } -var sentFailMsg=make(map[string]bool) -var sentCompleteMsg=make(map[string]bool) +var sentFailMsg = make(map[string]bool) +var sentCompleteMsg = make(map[string]bool) // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases/status,verbs=get;update;patch @@ -1350,7 +1350,7 @@ func (r *ShardingDatabaseReconciler) updateShardTopologyShardsInGsm(instance *da for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex := instance.Spec.Shard[i] if strings.ToLower(OraShardSpex.IsDelete) == "failed" { - continue + continue } // stateStr := shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { @@ -1575,12 +1575,12 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.AddingShardErrorState)) title = "Shard Addition Failure" message = "Error occurred during shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " addition." - shardingv1.LogMessages("INFO", title + ":" + message, nil, instance, r.Log) + shardingv1.LogMessages("INFO", title+":"+message, nil, instance, r.Log) if sentFailMsg[OraShardSpex.Name] != true { - r.sendMessage(instance, title, message) + r.sendMessage(instance, title, message) } - sentFailMsg[OraShardSpex.Name]=true - sentCompleteMsg[OraShardSpex.Name]=false + sentFailMsg[OraShardSpex.Name] = true + sentCompleteMsg[OraShardSpex.Name] = false deployFlag = false } } @@ -1631,13 +1631,13 @@ func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev1alpha1.Sha if oldStateStr != string(databasev1alpha1.ShardOnlineState) { title = "Shard Addition Completed" message = "Shard addition completed for shard " + shardingv1.GetFmtStr(shardSfSet.Name) + " in GSM." - shardingv1.LogMessages("INFO", title + ":" + message, nil, instance, r.Log) + shardingv1.LogMessages("INFO", title+":"+message, nil, instance, r.Log) if sentCompleteMsg[shardSfSet.Name] != true { - r.sendMessage(instance, title, message) + r.sendMessage(instance, title, message) } - sentCompleteMsg[shardSfSet.Name] = true - sentFailMsg[shardSfSet.Name] = false + sentCompleteMsg[shardSfSet.Name] = true + sentFailMsg[shardSfSet.Name] = false } return nil } @@ -1745,10 +1745,10 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar if err == nil { break } else { - if strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) != "USER" { - // If ShardingType is not "USER", do not perform the patching.. continue - continue - } + if strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) != "USER" { + // If ShardingType is not "USER", do not perform the patching.. continue + continue + } instance.Spec.Shard[i].IsDelete = "failed" err = shardingv1.InstanceShardPatch(instance, instance, r.Client, i, "isDelete", "failed") if err != nil { @@ -1948,15 +1948,15 @@ func (r *ShardingDatabaseReconciler) deployStatefulSet(instance *databasev1alpha message := "Inside the deployStatefulSet function" shardingv1.LogMessages("DEBUG", message, nil, instance, r.Log) // See if StatefulSets already exists and create if it doesn't - // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) - // This happens during unit test cases - for i := 0; i < 5; i++ { - if r.Scheme == nil { - time.Sleep(time.Second * 40) - } else { - break - } - } + // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) + // This happens during unit test cases + for i := 0; i < 5; i++ { + if r.Scheme == nil { + time.Sleep(time.Second * 40) + } else { + break + } + } controllerutil.SetControllerReference(instance, dep, r.Scheme) found := &appsv1.StatefulSet{} err := r.Client.Get(context.TODO(), types.NamespacedName{ @@ -2008,8 +2008,8 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. msg = "checkShardState():ShardType=" + strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) if strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) != "USER" { - // ShardingType is not "USER", so return - return err + // ShardingType is not "USER", so return + return err } if len(instance.Status.Gsm.Shards) > 0 { @@ -2021,10 +2021,10 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. err = fmt.Errorf(eventMsg) break } else if currState == string(databasev1alpha1.DeletingState) { - eventMsg = "Shard Deletion in progress. Requeuing" - err = fmt.Errorf(eventMsg) + eventMsg = "Shard Deletion in progress. Requeuing" + err = fmt.Errorf(eventMsg) err = nil - break + break } else if OraShardSpex.IsDelete == "failed" { eventMsg = "Shard Deletion failed. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) @@ -2039,11 +2039,11 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. break } else { eventMsg = "checkShardState() : Shard State=[" + currState + "]" - shardingv1.LogMessages("INFO", eventMsg, nil, instance, r.Log) + shardingv1.LogMessages("INFO", eventMsg, nil, instance, r.Log) err = nil } } - r.publishEvents(instance, eventMsg, currState) + r.publishEvents(instance, eventMsg, currState) } return err } diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index 6028cad0..2b5b576d 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -134,6 +134,8 @@ In this setup example [provisioning example setup](./provisioning/example_setup_ **Note** In multi namespace enviroment you have to create specific secrets for each namespaces + **Note** In multi namespace enviroment you have to create specific secrets for each namespaces + ### Secrets for CDB CRD Create a secret file as shown here: [config/samples/multitenant/cdb_secret.yaml](../multitenant/provisioning/singlenamespace/cdb_create.yaml). Modify this file with the `base64` encoded values of the required passwords for CDB, and use this file to create the required secrets. diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index cec36793..504fc7cd 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -3057,6 +3057,23 @@ metadata: creationTimestamp: null name: oracle-database-operator-manager-role rules: +- apiGroups: + - "" + resources: + - configmaps + - deployments + - events + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -4167,7 +4184,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator-VV + image: container-registry.oracle.com/database/operator:latest imagePullPolicy: Always name: manager ports: From a8b5579773fa351c18e8b92c61d44220c1d569ba Mon Sep 17 00:00:00 2001 From: thirumalai_thathachary Date: Wed, 25 Sep 2024 19:02:16 +0000 Subject: [PATCH 121/414] Review bug fixes --- commons/sharding/catalog.go | 17 ++++---- commons/sharding/scommon.go | 2 +- .../database/shardingdatabase_controller.go | 42 +++++++++++-------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index 58f07490..cb3a3c29 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -376,15 +376,15 @@ func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, } if len(OraCatalogSpex.PvAnnotations) > 0 { - claims[0].ObjectMeta.Annotations = make(map[string]string) - for key, value := range OraCatalogSpex.PvAnnotations { - claims[0].ObjectMeta.Annotations[key] = value - } - } + claims[0].ObjectMeta.Annotations = make(map[string]string) + for key, value := range OraCatalogSpex.PvAnnotations { + claims[0].ObjectMeta.Annotations[key] = value + } + } - if len(OraCatalogSpex.PvMatchLabels) > 0 { - claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} - } + if len(OraCatalogSpex.PvMatchLabels) > 0 { + claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} + } if checkTdeWalletFlag(instance) { if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { @@ -526,3 +526,4 @@ func UpdateProvForCatalog(instance *databasev1alpha1.ShardingDatabase, return ctrl.Result{}, nil } + diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 99987661..e9df2add 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -295,7 +295,7 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da } if instance.Spec.InvitedNodeSubnetFlag == "" { - instance.Spec.InvitedNodeSubnetFlag = "FALSE" + instance.Spec.InvitedNodeSubnetFlag = "TRUE" } if strings.ToUpper(instance.Spec.InvitedNodeSubnetFlag) != "FALSE" { diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 7fcaac2b..9f210b03 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -133,6 +133,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req resultNq := ctrl.Result{Requeue: false} resultQ := ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second} var nilErr error = nil + var msg string // On every reconcile, we will call setCrdLifeCycleState // To understand this, please refer https://sdk.operatorframework.io/docs/building-operators/golang/advanced-topics/ @@ -239,6 +240,12 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req if len(instance.Spec.Catalog) > 0 { for i = 0; i < int32(len(instance.Spec.Catalog)); i++ { OraCatalogSpex = instance.Spec.Catalog[i] + if len(OraCatalogSpex.Name) > 9 { + msg = "Catalog Name cannot be greater than 9 characters." + err = fmt.Errorf(msg) + result = resultNq + return result, err + } // See if StatefulSets already exists and create if it doesn't result, err = r.deployStatefulSet(instance, shardingv1.BuildStatefulSetForCatalog(instance, OraCatalogSpex), "CATALOG") if err != nil { @@ -290,6 +297,12 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // if user set replicasize greater than 1 but also set instance.Spec.OraDbPvcName then only one service will be created and one pod for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] + if len(OraShardSpex.Name) > 9 { + msg = "Shard Name cannot be greater than 9 characters." + err = fmt.Errorf(msg) + result = resultNq + return result, err + } if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { result, err = r.createService(instance, shardingv1.BuildServiceDefForShard(instance, 0, OraShardSpex, "local")) if err != nil { @@ -2016,34 +2029,29 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] currState = shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) - if currState == string(databasev1alpha1.AddingShardState) { - eventMsg = "Shard Addition in progress. Requeuing" + if OraShardSpex.IsDelete == "failed" { + eventMsg = "Shard Deletion failed for [" + OraShardSpex.Name + "]. Retry shard deletion after manually moving the chunks. Requeuing" err = fmt.Errorf(eventMsg) - break - } else if currState == string(databasev1alpha1.DeletingState) { - eventMsg = "Shard Deletion in progress. Requeuing" + } else if currState == string(databasev1alpha1.AddingShardState) { + eventMsg = "Shard Addition in progress for [" + OraShardSpex.Name + "]. Requeuing" err = fmt.Errorf(eventMsg) + } else if currState == string(databasev1alpha1.DeletingState) { + eventMsg = "Shard Deletion in progress for [" + OraShardSpex.Name + "]. Requeuing" + err = fmt.Errorf(eventMsg) err = nil - break - } else if OraShardSpex.IsDelete == "failed" { - eventMsg = "Shard Deletion failed. Manual intervention required. Requeuing" - err = fmt.Errorf(eventMsg) - break } else if currState == string(databasev1alpha1.DeleteErrorState) { - eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" + eventMsg = "Shard Deletion Error for [" + OraShardSpex.Name + "]. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) - break } else if currState == string(databasev1alpha1.ShardRemoveError) { - eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" + eventMsg = "Shard Deletion Error for [" + OraShardSpex.Name + "]. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) - break } else { - eventMsg = "checkShardState() : Shard State=[" + currState + "]" - shardingv1.LogMessages("INFO", eventMsg, nil, instance, r.Log) + eventMsg = "checkShardState() : Shard State[" + OraShardSpex.Name + "]=[" + currState + "]" + shardingv1.LogMessages("INFO", eventMsg, nil, instance, r.Log) err = nil } + r.publishEvents(instance, eventMsg, currState) } - r.publishEvents(instance, eventMsg, currState) } return err } From 9f560824f24d63f277aa4861d5cc63f7c7ae356c Mon Sep 17 00:00:00 2001 From: racpack Date: Thu, 26 Sep 2024 17:15:45 +0000 Subject: [PATCH 122/414] Revert "Merge branch 'Review-BugFixes' into 'master'" --- commons/sharding/catalog.go | 17 ++++---- commons/sharding/scommon.go | 2 +- .../database/shardingdatabase_controller.go | 42 ++++++++----------- 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index cb3a3c29..58f07490 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -376,15 +376,15 @@ func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, } if len(OraCatalogSpex.PvAnnotations) > 0 { - claims[0].ObjectMeta.Annotations = make(map[string]string) - for key, value := range OraCatalogSpex.PvAnnotations { - claims[0].ObjectMeta.Annotations[key] = value - } - } + claims[0].ObjectMeta.Annotations = make(map[string]string) + for key, value := range OraCatalogSpex.PvAnnotations { + claims[0].ObjectMeta.Annotations[key] = value + } + } - if len(OraCatalogSpex.PvMatchLabels) > 0 { - claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} - } + if len(OraCatalogSpex.PvMatchLabels) > 0 { + claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} + } if checkTdeWalletFlag(instance) { if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { @@ -526,4 +526,3 @@ func UpdateProvForCatalog(instance *databasev1alpha1.ShardingDatabase, return ctrl.Result{}, nil } - diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index e9df2add..99987661 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -295,7 +295,7 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da } if instance.Spec.InvitedNodeSubnetFlag == "" { - instance.Spec.InvitedNodeSubnetFlag = "TRUE" + instance.Spec.InvitedNodeSubnetFlag = "FALSE" } if strings.ToUpper(instance.Spec.InvitedNodeSubnetFlag) != "FALSE" { diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 9f210b03..7fcaac2b 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -133,7 +133,6 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req resultNq := ctrl.Result{Requeue: false} resultQ := ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second} var nilErr error = nil - var msg string // On every reconcile, we will call setCrdLifeCycleState // To understand this, please refer https://sdk.operatorframework.io/docs/building-operators/golang/advanced-topics/ @@ -240,12 +239,6 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req if len(instance.Spec.Catalog) > 0 { for i = 0; i < int32(len(instance.Spec.Catalog)); i++ { OraCatalogSpex = instance.Spec.Catalog[i] - if len(OraCatalogSpex.Name) > 9 { - msg = "Catalog Name cannot be greater than 9 characters." - err = fmt.Errorf(msg) - result = resultNq - return result, err - } // See if StatefulSets already exists and create if it doesn't result, err = r.deployStatefulSet(instance, shardingv1.BuildStatefulSetForCatalog(instance, OraCatalogSpex), "CATALOG") if err != nil { @@ -297,12 +290,6 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // if user set replicasize greater than 1 but also set instance.Spec.OraDbPvcName then only one service will be created and one pod for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] - if len(OraShardSpex.Name) > 9 { - msg = "Shard Name cannot be greater than 9 characters." - err = fmt.Errorf(msg) - result = resultNq - return result, err - } if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { result, err = r.createService(instance, shardingv1.BuildServiceDefForShard(instance, 0, OraShardSpex, "local")) if err != nil { @@ -2029,29 +2016,34 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] currState = shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) - if OraShardSpex.IsDelete == "failed" { - eventMsg = "Shard Deletion failed for [" + OraShardSpex.Name + "]. Retry shard deletion after manually moving the chunks. Requeuing" - err = fmt.Errorf(eventMsg) - } else if currState == string(databasev1alpha1.AddingShardState) { - eventMsg = "Shard Addition in progress for [" + OraShardSpex.Name + "]. Requeuing" + if currState == string(databasev1alpha1.AddingShardState) { + eventMsg = "Shard Addition in progress. Requeuing" err = fmt.Errorf(eventMsg) + break } else if currState == string(databasev1alpha1.DeletingState) { - eventMsg = "Shard Deletion in progress for [" + OraShardSpex.Name + "]. Requeuing" - err = fmt.Errorf(eventMsg) + eventMsg = "Shard Deletion in progress. Requeuing" + err = fmt.Errorf(eventMsg) err = nil + break + } else if OraShardSpex.IsDelete == "failed" { + eventMsg = "Shard Deletion failed. Manual intervention required. Requeuing" + err = fmt.Errorf(eventMsg) + break } else if currState == string(databasev1alpha1.DeleteErrorState) { - eventMsg = "Shard Deletion Error for [" + OraShardSpex.Name + "]. Manual intervention required. Requeuing" + eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) + break } else if currState == string(databasev1alpha1.ShardRemoveError) { - eventMsg = "Shard Deletion Error for [" + OraShardSpex.Name + "]. Manual intervention required. Requeuing" + eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) + break } else { - eventMsg = "checkShardState() : Shard State[" + OraShardSpex.Name + "]=[" + currState + "]" - shardingv1.LogMessages("INFO", eventMsg, nil, instance, r.Log) + eventMsg = "checkShardState() : Shard State=[" + currState + "]" + shardingv1.LogMessages("INFO", eventMsg, nil, instance, r.Log) err = nil } - r.publishEvents(instance, eventMsg, currState) } + r.publishEvents(instance, eventMsg, currState) } return err } From b93c29741800c40b8e3772b7e0cc478e14e383c0 Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Thu, 3 Oct 2024 06:58:50 +0000 Subject: [PATCH 123/414] ords latest developer img --- .../v1alpha1/oraclerestdataservice_types.go | 27 +- .../v1alpha1/oraclerestdataservice_webhook.go | 3 - .../v1alpha1/zz_generated.deepcopy.go | 1 - commons/database/constants.go | 19 +- .../samples/sidb/oraclerestdataservice.yaml | 19 +- .../sidb/oraclerestdataservice_apex.yaml | 42 - .../sidb/oraclerestdataservice_create.yaml | 7 +- .../sidb/oraclerestdataservice_secrets.yaml | 18 - .../oraclerestdataservice_controller.go | 774 +++++++++--------- docs/sidb/README.md | 92 +-- 10 files changed, 464 insertions(+), 538 deletions(-) delete mode 100644 config/samples/sidb/oraclerestdataservice_apex.yaml diff --git a/apis/database/v1alpha1/oraclerestdataservice_types.go b/apis/database/v1alpha1/oraclerestdataservice_types.go index 37f61a8a..d653c11e 100644 --- a/apis/database/v1alpha1/oraclerestdataservice_types.go +++ b/apis/database/v1alpha1/oraclerestdataservice_types.go @@ -56,13 +56,13 @@ type OracleRestDataServiceSpec struct { NodeSelector map[string]string `json:"nodeSelector,omitempty"` Image OracleRestDataServiceImage `json:"image,omitempty"` OrdsPassword OracleRestDataServicePassword `json:"ordsPassword"` - ApexPassword OracleRestDataServicePassword `json:"apexPassword,omitempty"` AdminPassword OracleRestDataServicePassword `json:"adminPassword"` OrdsUser string `json:"ordsUser,omitempty"` RestEnableSchemas []OracleRestDataServiceRestEnableSchemas `json:"restEnableSchemas,omitempty"` OracleService string `json:"oracleService,omitempty"` ServiceAccountName string `json:"serviceAccountName,omitempty"` Persistence OracleRestDataServicePersistence `json:"persistence,omitempty"` + MongoDbApi bool `json:"mongoDbApi,omitempty"` // +k8s:openapi-gen=true // +kubebuilder:validation:Minimum=1 @@ -106,17 +106,19 @@ type OracleRestDataServiceRestEnableSchemas struct { type OracleRestDataServiceStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - Status string `json:"status,omitempty"` - DatabaseApiUrl string `json:"databaseApiUrl,omitempty"` - LoadBalancer string `json:"loadBalancer,omitempty"` - DatabaseRef string `json:"databaseRef,omitempty"` - ServiceIP string `json:"serviceIP,omitempty"` - DatabaseActionsUrl string `json:"databaseActionsUrl,omitempty"` - OrdsInstalled bool `json:"ordsInstalled,omitempty"` - ApexConfigured bool `json:"apexConfigured,omitempty"` - ApxeUrl string `json:"apexUrl,omitempty"` - CommonUsersCreated bool `json:"commonUsersCreated,omitempty"` - Replicas int `json:"replicas,omitempty"` + Status string `json:"status,omitempty"` + DatabaseApiUrl string `json:"databaseApiUrl,omitempty"` + LoadBalancer string `json:"loadBalancer,omitempty"` + DatabaseRef string `json:"databaseRef,omitempty"` + ServiceIP string `json:"serviceIP,omitempty"` + DatabaseActionsUrl string `json:"databaseActionsUrl,omitempty"` + MongoDbApiAccessUrl string `json:"mongoDbApiAccessUrl,omitempty"` + OrdsInstalled bool `json:"ordsInstalled,omitempty"` + ApexConfigured bool `json:"apexConfigured,omitempty"` + ApxeUrl string `json:"apexUrl,omitempty"` + MongoDbApi bool `json:"mongoDbApi,omitempty"` + CommonUsersCreated bool `json:"commonUsersCreated,omitempty"` + Replicas int `json:"replicas,omitempty"` Image OracleRestDataServiceImage `json:"image,omitempty"` } @@ -128,6 +130,7 @@ type OracleRestDataServiceStatus struct { // +kubebuilder:printcolumn:JSONPath=".status.databaseApiUrl",name="Database API URL",type="string" // +kubebuilder:printcolumn:JSONPath=".status.databaseActionsUrl",name="Database Actions URL",type="string" // +kubebuilder:printcolumn:JSONPath=".status.apexUrl",name="Apex URL",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.mongoDbApiAccessUrl",name="MongoDbApi Access URL",type="string" // OracleRestDataService is the Schema for the oraclerestdataservices API type OracleRestDataService struct { diff --git a/apis/database/v1alpha1/oraclerestdataservice_webhook.go b/apis/database/v1alpha1/oraclerestdataservice_webhook.go index bfe3208c..c5ecde1c 100644 --- a/apis/database/v1alpha1/oraclerestdataservice_webhook.go +++ b/apis/database/v1alpha1/oraclerestdataservice_webhook.go @@ -74,9 +74,6 @@ func (r *OracleRestDataService) Default() { if r.Spec.OrdsPassword.KeepSecret == nil { r.Spec.OrdsPassword.KeepSecret = &keepSecret } - if r.Spec.ApexPassword.KeepSecret == nil { - r.Spec.ApexPassword.KeepSecret = &keepSecret - } if r.Spec.AdminPassword.KeepSecret == nil { r.Spec.AdminPassword.KeepSecret = &keepSecret } diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 10b34ca7..4ed96cd8 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1772,7 +1772,6 @@ func (in *OracleRestDataServiceSpec) DeepCopyInto(out *OracleRestDataServiceSpec } out.Image = in.Image in.OrdsPassword.DeepCopyInto(&out.OrdsPassword) - in.ApexPassword.DeepCopyInto(&out.ApexPassword) in.AdminPassword.DeepCopyInto(&out.AdminPassword) if in.RestEnableSchemas != nil { in, out := &in.RestEnableSchemas, &out.RestEnableSchemas diff --git a/commons/database/constants.go b/commons/database/constants.go index 6f27750d..3abdeb94 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -50,6 +50,8 @@ const DBA_GUID int64 = 54322 const SQLPlusCLI string = "sqlplus -s / as sysdba" +const SQLCLI string = "sql -s / as sysdba" + const GetVersionSQL string = "SELECT VERSION_FULL FROM V\\$INSTANCE;" const CheckModesSQL string = "SELECT 'log_mode:' || log_mode AS log_mode ,'flashback_on:' || flashback_on AS flashback_on ,'force_logging:' || force_logging AS force_logging FROM v\\$database;" @@ -345,6 +347,8 @@ const InitORDSCMD string = "if [ -f $ORDS_HOME/config/ords/defaults.xml ]; then "\nrm -f sqladmin.passwd" + "\numask 022" +const DbConnectString string = "CONN_STRING=sys/%[1]s@%[2]s:1521/%[3]s" + const GetSessionInfoSQL string = "select s.sid || ',' || s.serial# as Info FROM v\\$session s, v\\$process p " + "WHERE (s.username = 'ORDS_PUBLIC_USER' or " + "s.username = 'APEX_PUBLIC_USER' or " + @@ -369,7 +373,7 @@ const UninstallORDSCMD string = "\numask 177" + "\nrm -rf /opt/oracle/ords/config/ords/standalone" + "\nrm -rf /opt/oracle/ords/config/ords/apex" -const GetORDSStatus string = "curl -sSkv -k -X GET https://localhost:8443/ords/_/db-api/stable/metadata-catalog/" +const GetORDSStatus string = "curl -sSkv -k -X GET http://localhost:8181/ords/_/db-api/stable/metadata-catalog/" const ValidateAdminPassword string = "conn sys/\\\"%s\\\"@${ORACLE_SID} as sysdba\nshow user" @@ -442,18 +446,18 @@ const ChownApex string = " chown oracle:oinstall /opt/oracle/oradata/${ORACLE_SI const InstallApex string = "if [ -f /opt/oracle/oradata/${ORACLE_SID^^}/apex/apexins.sql ]; then ( while true; do sleep 60; echo \"Installing Apex...\" ; done ) & " + " cd /opt/oracle/oradata/${ORACLE_SID^^}/apex && echo -e \"@apexins.sql SYSAUX SYSAUX TEMP /i/\" | %[1]s && kill -9 $!; else echo \"Apex Folder doesn't exist\" ; fi ;" -const InstallApexInContainer string = "cd ${ORDS_HOME}/config/apex/ && echo -e \"@apxsilentins.sql SYSAUX SYSAUX TEMP /i/ %[1]s %[1]s %[1]s %[1]s;\n" + +const InstallApexInContainer string = "cd ${APEX_HOME}/${APEX_VER} && echo -e \"@apxsilentins.sql SYSAUX SYSAUX TEMP /i/ %[1]s %[1]s %[1]s %[1]s;\n" + "@apex_rest_config_core.sql;\n" + "exec APEX_UTIL.set_workspace(p_workspace => 'INTERNAL');\n" + "exec APEX_UTIL.EDIT_USER(p_user_id => APEX_UTIL.GET_USER_ID('ADMIN'), p_user_name => 'ADMIN', p_change_password_on_first_use => 'Y');\n" + - "\" | sqlplus -s sys/%[2]s@${ORACLE_HOST}:${ORACLE_PORT}/%[3]s as sysdba;" + "\" | sql -s sys/%[2]s@${ORACLE_HOST}:${ORACLE_PORT}/%[3]s as sysdba;" const IsApexInstalled string = "echo -e \"select 'APEXVERSION:'||version as version FROM DBA_REGISTRY WHERE COMP_ID='APEX';\"" + - " | sqlplus -s sys/%[1]s@${ORACLE_HOST}:${ORACLE_PORT}/%[2]s as sysdba;" + " | sql -s sys/%[1]s@${ORACLE_HOST}:${ORACLE_PORT}/%[2]s as sysdba;" -const UninstallApex string = "cd ${ORDS_HOME}/config/apex/ && echo -e \"@apxremov.sql\n\" | sqlplus -s sys/%[1]s@${ORACLE_HOST}:${ORACLE_PORT}/%[2]s as sysdba;" +const UninstallApex string = "cd ${APEX_HOME}/${APEX_VER} && echo -e \"@apxremov.sql\n\" | sql -s sys/%[1]s@${ORACLE_HOST}:${ORACLE_PORT}/%[2]s as sysdba;" -const ConfigureApexRest string = "if [ -f ${ORDS_HOME}/config/apex/apex_rest_config.sql ]; then cd ${ORDS_HOME}/config/apex && " + +const ConfigureApexRest string = "if [ -f ${APEX_HOME}/${APEX_VER}/apex_rest_config.sql ]; then cd ${ORDS_HOME}/config/apex && " + "echo -e \"%[1]s\n%[1]s\" | %[2]s ; else echo \"Apex Folder doesn't exist\" ; fi ;" const AlterApexUsers string = "\nALTER SESSION SET CONTAINER=%[2]s;" + @@ -508,6 +512,9 @@ const SetApexUsers string = "\numask 177" + "\nrm -f apexPublicUser" + "\numask 022" +// Command to enable/disable MongoDB API support in ords pods +const ConfigMongoDb string = "ords config set mongo.enabled %[1]s" + // Get Sid, Pdbname, Edition for prebuilt db const GetSidPdbEditionCMD string = "echo $ORACLE_SID,$ORACLE_PDB,$ORACLE_EDITION;" diff --git a/config/samples/sidb/oraclerestdataservice.yaml b/config/samples/sidb/oraclerestdataservice.yaml index 911a9b1e..a8f1b9bb 100644 --- a/config/samples/sidb/oraclerestdataservice.yaml +++ b/config/samples/sidb/oraclerestdataservice.yaml @@ -26,24 +26,9 @@ spec: secretKey: keepSecret: true - ## To configure APEX with ORDS, specfiy the apexPassword secret details. Leave empty if Apex is not needed - ## This is a secret containing a common password for APEX_PUBLIC_USER, APEX_REST_PUBLIC_USER, APEX_LISTENER and Apex administrator (username: ADMIN) mapped to secretKey - ## This secret will be deleted after ORDS Installation unless keepSecret set to true. - ## This password should complete the following requirements: - ## 1. Contain at least 6 characters. - ## 2. Contain at least one numeric character (0123456789). - ## 3. Contain at least one punctuation character (!"#$%&()``*+,-/:;?_). - ## 4. Contain at least one uppercase alphabetic character. - - apexPassword: - secretName: - secretKey: - keepSecret: true - ## ORDS image details - ## Supported ORDS image is container-registry.oracle.com/database/ords:21.4.2-gh image: - pullFrom: container-registry.oracle.com/database/ords:21.4.2-gh + pullFrom: container-registry.oracle.com/database/ords-developer:latest pullSecrets: ## Dedicated persistent storage is optional. If not specified, ORDS will use persistent storage from .spec.databaseRef @@ -65,6 +50,8 @@ spec: #serviceAnnotations: # service.beta.kubernetes.io/oci-load-balancer-internal: "true" + ## Set this to true to enable MongoDB API + mongoDbApi: true ## Deploy only on nodes having required labels. Format label_name: label_value ## The same lables are applied to the created PVC diff --git a/config/samples/sidb/oraclerestdataservice_apex.yaml b/config/samples/sidb/oraclerestdataservice_apex.yaml deleted file mode 100644 index 6bdc9fb5..00000000 --- a/config/samples/sidb/oraclerestdataservice_apex.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# - -apiVersion: database.oracle.com/v1alpha1 -kind: OracleRestDataService -metadata: - name: ords-sample - namespace: default -spec: - - ## Database ref. This can be of kind SingleInstanceDatabase. - ## Make sure the source database has been created by applying singeinstancedatabase_express.yaml - databaseRef: "xedb-sample" - - ## Secret containing databaseRef password - adminPassword: - secretName: xedb-admin-secret - - ## Secret containing ORDS_PUBLIC_USER password - ordsPassword: - secretName: ords-secret - - ## To configure APEX with ORDS, specfiy the apexPassword secret details. Leave empty if Apex is not needed. - ## This is a secret containing a common password for APEX_PUBLIC_USER, APEX_REST_PUBLIC_USER, APEX_LISTENER and Apex administrator (username: ADMIN) - apexPassword: - secretName: apex-secret - - ## ORDS image details - image: - pullFrom: container-registry.oracle.com/database/ords:21.4.2-gh - - ## PDB Schemas to be ORDS Enabled. - ## Schema will be created (if not exists) with password as .spec.ordsPassword. - restEnableSchemas: - - schemaName: schema1 - enable: true - urlMapping: - - schemaName: schema2 - enable: true - urlMapping: myschema diff --git a/config/samples/sidb/oraclerestdataservice_create.yaml b/config/samples/sidb/oraclerestdataservice_create.yaml index 454abf37..8647592b 100644 --- a/config/samples/sidb/oraclerestdataservice_create.yaml +++ b/config/samples/sidb/oraclerestdataservice_create.yaml @@ -24,7 +24,10 @@ spec: ## ORDS image details image: - pullFrom: container-registry.oracle.com/database/ords:21.4.2-gh + pullFrom: container-registry.oracle.com/database/ords-developer:latest + + ## Set this to true to enable MongoDB API + mongoDbApi: true ## PDB Schemas to be ORDS Enabled. ## Schema will be created (if not exists) with password as .spec.ordsPassword. @@ -34,4 +37,4 @@ spec: urlMapping: - schemaName: schema2 enable: true - urlMapping: myschema + urlMapping: myschema \ No newline at end of file diff --git a/config/samples/sidb/oraclerestdataservice_secrets.yaml b/config/samples/sidb/oraclerestdataservice_secrets.yaml index 9e587960..aebca546 100644 --- a/config/samples/sidb/oraclerestdataservice_secrets.yaml +++ b/config/samples/sidb/oraclerestdataservice_secrets.yaml @@ -13,21 +13,3 @@ type: Opaque stringData: ## Specify your ORDS password here oracle_pwd: - ---- - -## APEX password secret -apiVersion: v1 -kind: Secret -metadata: - name: apex-secret - namespace: default -type: Opaque -stringData: - ## Specify your APEX password here - ## This password should complete the following requirements: - ## 1. Contain at least 6 characters. - ## 2. Contain at least one numeric character (0123456789). - ## 3. Contain at least one punctuation character (!"#$%&()``*+,-/:;?_). - ## 4. Contain at least one uppercase alphabetic character. - oracle_pwd: diff --git a/controllers/database/oraclerestdataservice_controller.go b/controllers/database/oraclerestdataservice_controller.go index 783ae70c..4e26676e 100644 --- a/controllers/database/oraclerestdataservice_controller.go +++ b/controllers/database/oraclerestdataservice_controller.go @@ -210,6 +210,13 @@ func (r *OracleRestDataServiceReconciler) Reconcile(ctx context.Context, req ctr return result, nil } + // Configure MongoDB + result = r.enableMongoDB(oracleRestDataService, singleInstanceDatabase, sidbReadyPod, ordsReadyPod, ctx, req) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + // Delete Secrets r.deleteSecrets(oracleRestDataService, ctx, req) @@ -263,37 +270,6 @@ func (r *OracleRestDataServiceReconciler) validate(m *dbapi.OracleRestDataServic eventMsgs = append(eventMsgs, "image patching is not available currently") } - // Validate the apex ADMIN password if it is specified - - if !m.Status.ApexConfigured && m.Spec.ApexPassword.SecretName != "" { - apexPasswordSecret := &corev1.Secret{} - err = r.Get(ctx, types.NamespacedName{Name: m.Spec.ApexPassword.SecretName, Namespace: m.Namespace}, apexPasswordSecret) - if err != nil { - if apierrors.IsNotFound(err) { - m.Status.Status = dbcommons.StatusError - eventReason := "Apex Password" - eventMsg := "password secret " + m.Spec.ApexPassword.SecretName + " not found, retrying..." - r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) - r.Log.Info(eventMsg) - return requeueY, nil - } - r.Log.Error(err, err.Error()) - return requeueY, err - } - // APEX_LISTENER , APEX_REST_PUBLIC_USER , APEX_PUBLIC_USER passwords - apexPassword := string(apexPasswordSecret.Data[m.Spec.ApexPassword.SecretKey]) - - // Validate apexPassword - if !dbcommons.ApexPasswordValidator(apexPassword) { - m.Status.Status = dbcommons.StatusError - eventReason := "Apex Password" - eventMsg := "password for Apex is invalid, it should contain at least 6 chars, at least one numeric character, at least one punctuation character (!\"#$%&()``*+,-/:;?_), at least one upper-case alphabet" - r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) - r.Log.Info("APEX password does not conform to the requirements") - return requeueY, nil - } - } - if len(eventMsgs) > 0 { m.Status.Status = dbcommons.StatusError r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, strings.Join(eventMsgs, ",")) @@ -480,13 +456,24 @@ func (r *OracleRestDataServiceReconciler) instantiateSVCSpec(m *dbapi.OracleRest }(), }, Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "client", - Port: 8443, - Protocol: corev1.ProtocolTCP, - }, - }, + Ports: func() []corev1.ServicePort { + ports := []corev1.ServicePort{ + { + Name: "client", + Port: 8181, + Protocol: corev1.ProtocolTCP, + }, + } + // Conditionally add MongoDB port if enabled + if m.Spec.MongoDbApi { + ports = append(ports, corev1.ServicePort{ + Name: "mongodb", + Port: 27017, + Protocol: corev1.ProtocolTCP, + }) + } + return ports + }(), Selector: map[string]string{ "app": m.Name, }, @@ -509,253 +496,179 @@ func (r *OracleRestDataServiceReconciler) instantiateSVCSpec(m *dbapi.OracleRest // // ############################################################################# func (r *OracleRestDataServiceReconciler) instantiatePodSpec(m *dbapi.OracleRestDataService, - n *dbapi.SingleInstanceDatabase) (*corev1.Pod, *corev1.Secret) { - - initSecret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: m.Name, - Namespace: m.Namespace, - Labels: map[string]string{ - "app": m.Name, + n *dbapi.SingleInstanceDatabase, req ctrl.Request) *corev1.Pod { + + pod := &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", }, - }, - Type: corev1.SecretTypeOpaque, - StringData: map[string]string{ - "init-cmd": dbcommons.InitORDSCMD, - }, - } - - pod := &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: m.Name + "-" + dbcommons.GenerateRandomString(5), - Namespace: m.Namespace, - Labels: map[string]string{ - "app": m.Name, - "version": m.Spec.Image.Version, + ObjectMeta: metav1.ObjectMeta{ + Name: m.Name + "-" + dbcommons.GenerateRandomString(5), + Namespace: m.Namespace, + Labels: map[string]string{ + "app": m.Name, + "version": m.Spec.Image.Version, + }, }, - }, - Spec: corev1.PodSpec{ - Affinity: func() *corev1.Affinity { - if m.Spec.Persistence.Size == "" && n.Spec.Persistence.AccessMode == "ReadWriteOnce" { - // Only allowing pods to be scheduled on the node where SIDB pods are running - return &corev1.Affinity{ - PodAffinity: &corev1.PodAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{{ - Key: "app", - Operator: metav1.LabelSelectorOpIn, - Values: []string{n.Name}, // Schedule on same host as DB Pod - }}, + Spec: corev1.PodSpec{ + Affinity: func() *corev1.Affinity { + if m.Spec.Persistence.Size == "" && n.Spec.Persistence.AccessMode == "ReadWriteOnce" { + // Only allowing pods to be scheduled on the node where SIDB pods are running + return &corev1.Affinity{ + PodAffinity: &corev1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "app", + Operator: metav1.LabelSelectorOpIn, + Values: []string{n.Name}, // Schedule on same host as DB Pod + }}, + }, + TopologyKey: "kubernetes.io/hostname", + }, }, - TopologyKey: "kubernetes.io/hostname", - }, }, - }, + } } - } - return nil - }(), - Volumes: []corev1.Volume{ - { - Name: "datamount", - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: func() string { - if m.Spec.Persistence.AccessMode != "" { - return m.Name - } - return n.Name - }(), - ReadOnly: false, + return nil + }(), + Volumes: []corev1.Volume{ + { + Name: "datamount", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: func() string { + if m.Spec.Persistence.AccessMode != "" { + return m.Name + } + return n.Name + }(), + ReadOnly: false, + }, }, }, - }, - { - Name: "init-ords-vol", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: m.Name, - Optional: func() *bool { i := true; return &i }(), - Items: []corev1.KeyToPath{{ - Key: "init-cmd", - Path: "init-cmd", - }}, + { + Name: "varmount", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, - }, - InitContainers: []corev1.Container{ - { - Name: "init-permissions", - Image: m.Spec.Image.PullFrom, - Command: []string{"/bin/sh", "-c", fmt.Sprintf("chown %d:%d /opt/oracle/ords/config/ords || true", int(dbcommons.ORACLE_UID), int(dbcommons.DBA_GUID))}, - SecurityContext: &corev1.SecurityContext{ - // User ID 0 means, root user - RunAsUser: func() *int64 { i := int64(0); return &i }(), + InitContainers: []corev1.Container{ + { + Name: "init-ords", + Image: m.Spec.Image.PullFrom, + Command: []string{"/bin/sh"}, + Args: []string{ + "-c", + fmt.Sprintf("while [ ! -f /opt/oracle/variables/%s ]; do sleep 0.5; done", "conn_string.txt"), + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/etc/ords/config/", + Name: "datamount", + }, + { + MountPath: "/opt/oracle/variables/", + Name: "varmount", + }, + }, }, - VolumeMounts: []corev1.VolumeMount{{ - MountPath: "/opt/oracle/ords/config/ords", - Name: "datamount", - SubPath: strings.ToUpper(n.Spec.Sid) + "_ORDS", - }}, }, - { - Name: "init-ords", - Image: m.Spec.Image.PullFrom, - Command: []string{"/bin/sh", "/run/secrets/init-cmd"}, - SecurityContext: &corev1.SecurityContext{ - RunAsUser: func() *int64 { i := int64(dbcommons.ORACLE_UID); return &i }(), - RunAsGroup: func() *int64 { i := int64(dbcommons.DBA_GUID); return &i }(), - }, + Containers: []corev1.Container{{ + Name: m.Name, + Image: m.Spec.Image.PullFrom, + Ports: func() []corev1.ContainerPort { + ports := []corev1.ContainerPort{ + { + ContainerPort: 8181, // Default application port + }, + } + if m.Spec.MongoDbApi { + ports = append(ports, corev1.ContainerPort{ + ContainerPort: 27017, // MongoDB port + }) + } + return ports + }(), VolumeMounts: []corev1.VolumeMount{ { - MountPath: "/opt/oracle/ords/config/ords", + MountPath: "/etc/ords/config/", Name: "datamount", - SubPath: strings.ToUpper(n.Spec.Sid) + "_ORDS", - }, - { - MountPath: "/run/secrets/init-cmd", - ReadOnly: true, - Name: "init-ords-vol", - SubPath: "init-cmd", - }, - }, - Env: []corev1.EnvVar{ - { - Name: "ORACLE_HOST", - Value: n.Name, }, { - Name: "ORACLE_PORT", - Value: "1521", + MountPath: "/opt/oracle/variables/", + Name: "varmount", }, - { - Name: "ORACLE_SERVICE", - Value: func() string { - if m.Spec.OracleService != "" { - return m.Spec.OracleService - } - return n.Spec.Sid - }(), - }, - { - Name: "ORDS_USER", - Value: func() string { - if m.Spec.OrdsUser != "" { - return m.Spec.OrdsUser - } - return "ORDS_PUBLIC_USER" - }(), - }, - { - Name: "ORDS_PWD", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: m.Spec.OrdsPassword.SecretName, - }, - Key: m.Spec.OrdsPassword.SecretKey, - }, + }, + Env: func() []corev1.EnvVar { + // After ORDS is Installed, we DELETE THE OLD ORDS Pod and create new ones ONLY USING BELOW ENV VARIABLES. + return []corev1.EnvVar{ + { + Name: "ORACLE_HOST", + Value: n.Name, }, - }, - { - Name: "ORACLE_PWD", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: m.Spec.AdminPassword.SecretName, - }, - Key: m.Spec.AdminPassword.SecretKey, - }, + { + Name: "ORACLE_PORT", + Value: "1521", }, - }, - }, - }, - }, - Containers: []corev1.Container{{ - Name: m.Name, - Image: m.Spec.Image.PullFrom, - Ports: []corev1.ContainerPort{{ContainerPort: 8443}}, - VolumeMounts: []corev1.VolumeMount{{ - MountPath: "/opt/oracle/ords/config/ords/", - Name: "datamount", - SubPath: strings.ToUpper(n.Spec.Sid) + "_ORDS", + { + Name: "ORACLE_SERVICE", + Value: func() string { + if m.Spec.OracleService != "" { + return m.Spec.OracleService + } + return n.Spec.Sid + }(), + }, + { + Name: "ORDS_USER", + Value: func() string { + if m.Spec.OrdsUser != "" { + return m.Spec.OrdsUser + } + return "ORDS_PUBLIC_USER" + }(), + }, + } + }(), }}, - Env: func() []corev1.EnvVar { - // After ORDS is Installed, we DELETE THE OLD ORDS Pod and create new ones ONLY USING BELOW ENV VARIABLES. - return []corev1.EnvVar{ - { - Name: "ORACLE_HOST", - Value: n.Name, - }, - { - Name: "ORACLE_PORT", - Value: "1521", - }, - { - Name: "ORACLE_SERVICE", - Value: func() string { - if m.Spec.OracleService != "" { - return m.Spec.OracleService - } - return n.Spec.Sid - }(), - }, - { - Name: "ORDS_USER", - Value: func() string { - if m.Spec.OrdsUser != "" { - return m.Spec.OrdsUser - } - return "ORDS_PUBLIC_USER" - }(), - }, + + TerminationGracePeriodSeconds: func() *int64 { i := int64(30); return &i }(), + + NodeSelector: func() map[string]string { + ns := make(map[string]string) + if len(m.Spec.NodeSelector) != 0 { + for key, value := range m.Spec.NodeSelector { + ns[key] = value + } } + return ns }(), - }}, - - TerminationGracePeriodSeconds: func() *int64 { i := int64(30); return &i }(), - - NodeSelector: func() map[string]string { - ns := make(map[string]string) - if len(m.Spec.NodeSelector) != 0 { - for key, value := range m.Spec.NodeSelector { - ns[key] = value + ServiceAccountName: func() string { + if m.Spec.ServiceAccountName != "" { + return m.Spec.ServiceAccountName } - } - return ns - }(), - ServiceAccountName: func() string { - if m.Spec.ServiceAccountName != "" { - return m.Spec.ServiceAccountName - } - return "default" - }(), - SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: func() *int64 { i := int64(dbcommons.ORACLE_UID); return &i }(), - RunAsGroup: func() *int64 { i := int64(dbcommons.DBA_GUID); return &i }(), - FSGroup: func() *int64 { i := int64(dbcommons.DBA_GUID); return &i }(), - }, - - ImagePullSecrets: []corev1.LocalObjectReference{ - { - Name: m.Spec.Image.PullSecrets, + return "default" + }(), + SecurityContext: &corev1.PodSecurityContext{ + RunAsUser: func() *int64 { i := int64(dbcommons.ORACLE_UID); return &i }(), + RunAsGroup: func() *int64 { i := int64(dbcommons.DBA_GUID); return &i }(), + FSGroup: func() *int64 { i := int64(dbcommons.DBA_GUID); return &i }(), + }, + + ImagePullSecrets: []corev1.LocalObjectReference{ + { + Name: m.Spec.Image.PullSecrets, + }, }, }, - }, - } - - // Set oracleRestDataService instance as the owner and controller - ctrl.SetControllerReference(m, initSecret, r.Scheme) - ctrl.SetControllerReference(m, pod, r.Scheme) - return pod, initSecret + } + + // Set oracleRestDataService instance as the owner and controller + // ctrl.SetControllerReference(m, initSecret, r.Scheme) + ctrl.SetControllerReference(m, pod, r.Scheme) + return pod } //############################################################################# @@ -878,14 +791,21 @@ func (r *OracleRestDataServiceReconciler) createSVC(ctx context.Context, req ctr if lbAddress == "" { lbAddress = svc.Status.LoadBalancer.Ingress[0].IP } - m.Status.DatabaseApiUrl = "https://" + lbAddress + ":" + - fmt.Sprint(svc.Spec.Ports[0].Port) + "/ords/" + n.Status.Pdbname + "/_/db-api/stable/" + m.Status.DatabaseApiUrl = "http://" + lbAddress + ":" + + fmt.Sprint(svc.Spec.Ports[0].Port) + "/ords/" + "{schema-name}" + "/_/db-api/stable/" m.Status.ServiceIP = lbAddress - m.Status.DatabaseActionsUrl = "https://" + lbAddress + ":" + + m.Status.DatabaseActionsUrl = "http://" + lbAddress + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/ords/sql-developer" if m.Status.ApexConfigured { - m.Status.ApxeUrl = "https://" + lbAddress + ":" + - fmt.Sprint(svc.Spec.Ports[0].Port) + "/ords/" + n.Status.Pdbname + "/apex" + m.Status.ApxeUrl = "http://" + lbAddress + ":" + + fmt.Sprint(svc.Spec.Ports[0].Port) + "/ords/apex" + } + if m.Status.MongoDbApi && len(svc.Spec.Ports) > 1 { + m.Status.MongoDbApiAccessUrl = "mongodb://[{user}:{password}@]"+ lbAddress + ":"+ + fmt.Sprint(svc.Spec.Ports[1].Port)+ "/{user}?" + + "authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true" + } else { + m.Status.MongoDbApiAccessUrl = "" } } return requeueN @@ -893,13 +813,19 @@ func (r *OracleRestDataServiceReconciler) createSVC(ctx context.Context, req ctr nodeip := dbcommons.GetNodeIp(r, ctx, req) if nodeip != "" { m.Status.ServiceIP = nodeip - m.Status.DatabaseApiUrl = "https://" + nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + - "/ords/" + n.Status.Pdbname + "/_/db-api/stable/" - m.Status.DatabaseActionsUrl = "https://" + nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + + m.Status.DatabaseApiUrl = "http://" + nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + + "/ords/" + "{schema-name}" + "/_/db-api/stable/" + m.Status.DatabaseActionsUrl = "http://" + nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/ords/sql-developer" if m.Status.ApexConfigured { - m.Status.ApxeUrl = "https://" + nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/ords/" + - n.Status.Pdbname + "/apex" + m.Status.ApxeUrl = "http://" + nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/ords/apex" + } + if m.Status.MongoDbApi && len(svc.Spec.Ports) > 1 { + m.Status.MongoDbApiAccessUrl = "mongodb://[{user}:{password}@]"+ nodeip + ":"+ + fmt.Sprint(svc.Spec.Ports[1].NodePort)+ "/{user}?" + + "authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true" + } else { + m.Status.MongoDbApiAccessUrl = "" } } return requeueN @@ -941,6 +867,98 @@ func (r *OracleRestDataServiceReconciler) createPVC(ctx context.Context, req ctr return requeueN, nil } + +// ############################################################################# +// +// Function for creating connection sting file +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) createConnectionString(m *dbapi.OracleRestDataService, + n *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + // Listing all the pods + readyPod, _, availableFinal, _, err := dbcommons.FindPods(r, m.Spec.Image.Version, + m.Spec.Image.PullFrom, m.Name, m.Namespace, ctx, req) + + if err != nil { + r.Log.Error(err, err.Error()) + return requeueY, nil + } + if readyPod.Name != "" { + return requeueN, nil + } + + if len(availableFinal) == 0 { + r.Log.Info("Pods are being created, currently no pods available") + return requeueY, nil + } + + // Iterate through the availableFinal (list of pods) to find out the pod whose status is updated about the init containers + // If no required pod found then requeue the reconcile request + var pod corev1.Pod + var podFound bool + for _, pod = range availableFinal { + // Check if pod status container is updated about init containers + if len(pod.Status.InitContainerStatuses) > 0 { + podFound = true + break + } + } + if !podFound { + r.Log.Info("No pod has its status updated about init containers. Requeueing...") + return requeueY, nil + } + + lastInitContIndex := len(pod.Status.InitContainerStatuses) - 1 + + // If InitContainerStatuses[].Ready is true, it means that the init container is successful + if pod.Status.InitContainerStatuses[lastInitContIndex].Ready { + // Init container named "init-ords" has completed it's execution, hence return and don't requeue + return requeueN, nil + } + + if pod.Status.InitContainerStatuses[lastInitContIndex].State.Running == nil { + // Init container named "init-ords" is not running, so waiting for it to come in running state requeueing the reconcile request + r.Log.Info("Waiting for init-ords to come in running state...") + return requeueY, nil + } + + r.Log.Info("Creating Connection String file...") + + // Querying the secret + r.Log.Info("Querying the database secret ...") + secret := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: m.Spec.AdminPassword.SecretName, Namespace: m.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + r.Log.Info("Secret not found") + m.Status.Status = dbcommons.StatusError + r.Status().Update(ctx, m) + return requeueY, nil + } + r.Log.Error(err, "Unable to get the secret. Requeueing..") + return requeueY, nil + } + + // Execing into the pods and creating the Connection String + adminPassword := string(secret.Data[m.Spec.AdminPassword.SecretKey]) + + _, err = dbcommons.ExecCommand(r, r.Config, pod.Name, pod.Namespace, "init-ords", + ctx, req, true, "bash", "-c", + fmt.Sprintf("mkdir -p /opt/oracle/variables && echo %[1]s > /opt/oracle/variables/%[2]s", + fmt.Sprintf(dbcommons.DbConnectString, adminPassword, n.Name, n.Status.Pdbname), + "conn_string.txt")) + + if err != nil { + r.Log.Error(err, err.Error()) + r.Log.Error(err, "Failed to create connection string in new "+m.Name+" POD", "pod.Namespace", pod.Namespace, "POD.Name", pod.Name) + return requeueY, nil + } + r.Log.Info("Succesfully Created connection string in new "+m.Name+" POD", "POD.NAME : ", pod.Name) + + return requeueN, nil +} + // ############################################################################# // // Create the requested POD replicas @@ -980,16 +998,24 @@ func (r *OracleRestDataServiceReconciler) createPods(m *dbapi.OracleRestDataServ } else if replicasFound < replicasReq { // Create New Pods , Name of Pods are generated Randomly for i := replicasFound; i < replicasReq; i++ { - pod, initSecret := r.instantiatePodSpec(m, n) - // Check if init-secret is present - err := r.Get(ctx, types.NamespacedName{Name: m.Name, Namespace: m.Namespace}, &corev1.Secret{}) - if err != nil && apierrors.IsNotFound(err) { - log.Info("Creating a new secret", "name", m.Name) - if err = r.Create(ctx, initSecret); err != nil { - log.Error(err, "Failed to create secret ", "Namespace", initSecret.Namespace, "Name", initSecret.Name) + // Obtain admin password of the referred database + adminPasswordSecret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: n.Spec.AdminPassword.SecretName, Namespace: n.Namespace}, adminPasswordSecret) + if err != nil { + if apierrors.IsNotFound(err) { + m.Status.Status = dbcommons.StatusError + eventReason := "Database Password" + eventMsg := "password secret " + m.Spec.AdminPassword.SecretName + " not found, retrying..." + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + r.Log.Info(eventMsg) return requeueY } + log.Error(err, err.Error()) + return requeueY } + + pod := r.instantiatePodSpec(m, n, req) + log.Info("Creating a new "+m.Name+" POD", "POD.Namespace", pod.Namespace, "POD.Name", pod.Name) err = r.Create(ctx, pod) if err != nil { @@ -1024,6 +1050,17 @@ func (r *OracleRestDataServiceReconciler) createPods(m *dbapi.OracleRestDataServ } } + // Creating conn string in pods + result, err := r.createConnectionString(m, n, ctx, req) + + if err != nil { + return requeueY + } + if result.Requeue { + log.Info("Requeued at connection string creation") + return requeueY + } + m.Status.Replicas = m.Spec.Replicas return requeueN @@ -1241,97 +1278,14 @@ func (r *OracleRestDataServiceReconciler) cleanupOracleRestDataService(req ctrl. // ############################################################################# func (r *OracleRestDataServiceReconciler) configureApex(m *dbapi.OracleRestDataService, n *dbapi.SingleInstanceDatabase, sidbReadyPod corev1.Pod, ordsReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) ctrl.Result { - log := r.Log.WithValues("configureApex", req.NamespacedName) + log := r.Log.WithValues("verifyApex", req.NamespacedName) - if m.Spec.ApexPassword.SecretName == "" { - m.Status.ApexConfigured = false - return requeueN - } if m.Status.ApexConfigured { return requeueN } - apexPasswordSecret := &corev1.Secret{} - err := r.Get(ctx, types.NamespacedName{Name: m.Spec.ApexPassword.SecretName, Namespace: m.Namespace}, apexPasswordSecret) - if err != nil { - if apierrors.IsNotFound(err) { - m.Status.Status = dbcommons.StatusError - eventReason := "Apex Password" - eventMsg := "password secret " + m.Spec.ApexPassword.SecretName + " not found, retrying..." - r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) - r.Log.Info(eventMsg) - return requeueY - } - log.Error(err, err.Error()) - return requeueY - } - // APEX_LISTENER , APEX_REST_PUBLIC_USER , APEX_PUBLIC_USER passwords - apexPassword := string(apexPasswordSecret.Data[m.Spec.ApexPassword.SecretKey]) - - if !n.Status.ApexInstalled { - m.Status.Status = dbcommons.StatusUpdating - result := r.installApex(m, n, ordsReadyPod, apexPassword, ctx, req) - if result.Requeue { - log.Info("Reconcile requeued because apex installation failed") - return result - } - } else { - // Alter Apex Users - log.Info("Alter APEX Users") - _, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", - ctx, req, true, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", - fmt.Sprintf(dbcommons.AlterApexUsers, apexPassword, n.Spec.Pdbname), dbcommons.SQLPlusCLI)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - } - - // Set Apex users in apex_rt,apex_al,apex files - out, err := dbcommons.ExecCommand(r, r.Config, ordsReadyPod.Name, ordsReadyPod.Namespace, "", ctx, req, true, "bash", "-c", - fmt.Sprintf(dbcommons.SetApexUsers, apexPassword)) - log.Info("SetApexUsers Output: \n" + out) - if strings.Contains(strings.ToUpper(out), "ERROR") { - return requeueY - } - if err != nil { - log.Info(err.Error()) - if strings.Contains(strings.ToUpper(err.Error()), "ERROR") { - return requeueY - } - } - - // ORDS needs to be restarted to configure APEX - r.Log.Info("Restarting ORDS Pod to complete APEX configuration: " + ordsReadyPod.Name) - var gracePeriodSeconds int64 = 0 - policy := metav1.DeletePropagationForeground - err = r.Delete(ctx, &ordsReadyPod, &client.DeleteOptions{ - GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy}) - if err != nil { - r.Log.Error(err, err.Error()) - } - - m.Status.ApexConfigured = true - r.Status().Update(ctx, m) - eventReason := "Apex Configuration" - eventMsg := "configuration of Apex completed!" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - log.Info(eventMsg) - - // Cannot return requeue as the secrets will be deleted if keepSecert is false, which cause problem in pod restart - return requeueY -} - -// ############################################################################# -// -// Install APEX in SIDB -// -// ############################################################################# -func (r *OracleRestDataServiceReconciler) installApex(m *dbapi.OracleRestDataService, n *dbapi.SingleInstanceDatabase, - ordsReadyPod corev1.Pod, apexPassword string, ctx context.Context, req ctrl.Request) ctrl.Result { - log := r.Log.WithValues("installApex", req.NamespacedName) - // Obtain admin password of the referred database + adminPasswordSecret := &corev1.Secret{} err := r.Get(ctx, types.NamespacedName{Name: m.Spec.AdminPassword.SecretName, Namespace: m.Namespace}, adminPasswordSecret) if err != nil { @@ -1348,23 +1302,8 @@ func (r *OracleRestDataServiceReconciler) installApex(m *dbapi.OracleRestDataSer } sidbPassword := string(adminPasswordSecret.Data[m.Spec.AdminPassword.SecretKey]) - // Status Updation - m.Status.Status = dbcommons.StatusUpdating - r.Status().Update(ctx, m) - eventReason := "Apex Installation" - eventMsg := "performing install of Apex in database " + m.Spec.DatabaseRef - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - - //Install Apex in SIDB ready pod - out, err := dbcommons.ExecCommand(r, r.Config, ordsReadyPod.Name, ordsReadyPod.Namespace, "", ctx, req, true, "bash", "-c", - fmt.Sprintf(dbcommons.InstallApexInContainer, apexPassword, sidbPassword, n.Status.Pdbname)) - if err != nil { - log.Info(err.Error()) - } - log.Info("Apex installation output : \n" + out) - // Checking if Apex is installed successfully or not - out, err = dbcommons.ExecCommand(r, r.Config, ordsReadyPod.Name, ordsReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + out, err := dbcommons.ExecCommand(r, r.Config, ordsReadyPod.Name, ordsReadyPod.Namespace, "", ctx, req, true, "bash", "-c", fmt.Sprintf(dbcommons.IsApexInstalled, sidbPassword, n.Status.Pdbname)) if err != nil { log.Error(err, err.Error()) @@ -1374,19 +1313,22 @@ func (r *OracleRestDataServiceReconciler) installApex(m *dbapi.OracleRestDataSer apexInstalled := "APEXVERSION:" if !strings.Contains(out, apexInstalled) { - eventReason = "Apex Installation" - eventMsg = "Unable to determine Apex version, retrying install..." + eventReason := "Apex Verification" + eventMsg := "Unable to determine Apex version, retrying..." r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) return requeueY } m.Status.Status = dbcommons.StatusReady - eventReason = "Apex Installation" + eventReason := "Apex Verification" outArr := strings.Split(out, apexInstalled) - eventMsg = "installation of Apex " + strings.TrimSpace(outArr[len(outArr)-1]) + " completed" + eventMsg := "Verification of Apex " + strings.TrimSpace(outArr[len(outArr)-1]) + " completed" r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) n.Status.ApexInstalled = true + m.Status.ApexConfigured = true r.Status().Update(ctx, n) + r.Status().Update(ctx, m) + return requeueN } @@ -1423,20 +1365,72 @@ func (r *OracleRestDataServiceReconciler) deleteSecrets(m *dbapi.OracleRestDataS } } } +} - if !*m.Spec.ApexPassword.KeepSecret { - // Fetch apexPassword Secret - apexPasswordSecret := &corev1.Secret{} - err := r.Get(ctx, types.NamespacedName{Name: m.Spec.ApexPassword.SecretName, Namespace: m.Namespace}, apexPasswordSecret) - if err == nil { - //Delete APEX Password Secret . - err := r.Delete(ctx, apexPasswordSecret, &client.DeleteOptions{}) - if err == nil { - log.Info("APEX password secret deleted : " + apexPasswordSecret.Name) + +// ############################################################################# +// +// Enable MongoDB API Support +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) enableMongoDB(m *dbapi.OracleRestDataService, n *dbapi.SingleInstanceDatabase, + sidbReadyPod corev1.Pod, ordsReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) ctrl.Result { + log := r.Log.WithValues("enableMongoDB", req.NamespacedName) + + if (m.Spec.MongoDbApi && !m.Status.MongoDbApi) || // setting MongoDbApi to true + (!m.Spec.MongoDbApi && m.Status.MongoDbApi) { // setting MongoDbApi to false + m.Status.Status = dbcommons.StatusUpdating + + out, err := dbcommons.ExecCommand(r, r.Config, ordsReadyPod.Name, ordsReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf(dbcommons.ConfigMongoDb, strconv.FormatBool(m.Spec.MongoDbApi)) ) + log.Info("configMongoDB Output: \n" + out) + + if strings.Contains(strings.ToUpper(out), "ERROR") { + return requeueY + } + if err != nil { + log.Info(err.Error()) + if strings.Contains(strings.ToUpper(err.Error()), "ERROR") { + return requeueY } } + + m.Status.MongoDbApi = m.Spec.MongoDbApi + m.Status.Status = dbcommons.StatusReady + r.Status().Update(ctx, m) + eventReason := "MongoDB-API Config" + eventMsg := "configuration of MongoDb API completed!" + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + log.Info(eventMsg) + + // ORDS service is resatrted + r.Log.Info("Restarting ORDS Service : " + m.Name) + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{Name: m.Name, Namespace: m.Namespace,}, + } + var gracePeriodSeconds int64 = 0 + policy := metav1.DeletePropagationForeground + err = r.Delete(ctx, svc, &client.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy,}) + if err != nil { + r.Log.Error(err, "Failed to delete ORDS service", "Service Name", m.Name) + return requeueY + } + + // ORDS needs to be restarted to configure MongoDB API + r.Log.Info("Restarting ORDS Pod after configuring MongoDb API : " + ordsReadyPod.Name) + err = r.Delete(ctx, &ordsReadyPod, &client.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy}) + if err != nil { + r.Log.Error(err, err.Error()) + } + return requeueY + + } else { + log.Info("MongoDB Already Configured") } + return requeueN } // ############################################################################# diff --git a/docs/sidb/README.md b/docs/sidb/README.md index ff357195..89c8a083 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -40,6 +40,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [REST Enable a Database](#rest-enable-a-database) * [Provision ORDS](#provision-ords) * [Database API](#database-api) + * [MongoDB API](#mongodb-api) * [Advanced Usages](#advanced-usages) * [Oracle Data Pump](#oracle-data-pump) * [REST Enabled SQL](#rest-enabled-sql) @@ -950,7 +951,7 @@ To obtain a quick status check of the ORDS service, use the following command: $ kubectl get oraclerestdataservice ords-sample NAME STATUS DATABASE DATABASE API URL DATABASE ACTIONS URL APEX URL -ords-sample Healthy sidb-sample https://10.0.25.54:8443/ords/ORCLPDB1/_/db-api/stable/ https://10.0.25.54:8443/ords/sql-developer https://10.0.25.54:8443/ords/ORCLPDB1/apex +ords-sample Healthy sidb-sample http://10.0.25.54:8181/ords/schema1/_/db-api/stable/ http://10.0.25.54:8181/ords/sql-developer http://10.0.25.54:8181/ords/apex ``` @@ -969,10 +970,10 @@ $ kubectl describe oraclerestdataservice ords-sample Metadata: ... Spec: ... Status: - Cluster Db API URL: https://ords21c-1.default:8443/ords/ORCLPDB1/_/db-api/stable/ - Database Actions URL: https://10.0.25.54:8443/ords/sql-developer - Database API URL: https://10.0.25.54:8443/ords/ORCLPDB1/_/db-api/stable/ - Apex URL: https://10.0.25.54:8443/ords/ORCLPDB1/apex + Cluster Db API URL: http://ords21c-1.default:8181/ords/schema1/_/db-api/stable/ + Database Actions URL: http://10.0.25.54:8181/ords/sql-developer + Database API URL: http://10.0.25.54:8181/ords/schema1/_/db-api/stable/ + Apex URL: http://10.0.25.54:8181/ords/apex Database Ref: sidb21c-1 Image: Pull From: ... @@ -994,8 +995,7 @@ The template `.yaml` file for Oracle Rest Data Services (`OracleRestDataService` **Note:** - The `adminPassword` and `ordsPassword` fields in the `oraclerestdataservice.yaml` file contains secrets for authenticating the Single Instance Database and the ORDS user with the following roles: `SQL Administrator, System Administrator, SQL Developer, oracle.dbtools.autorest.any.schema`. -- To build the ORDS image, use the following instructions: [Building Oracle REST Data Services Install Images](https://github.com/oracle/docker-images/tree/main/OracleRestDataServices#building-oracle-rest-data-services-install-images). -- By default, ORDS uses self-signed certificates. To use certificates from the Certificate Authority, the ORDS image needs to be rebuilt after specifying the values of `ssl.cert` and `ssl.cert.key` in the [standalone.properties](https://github.com/oracle/docker-images/blob/main/OracleRestDataServices/dockerfiles/standalone.properties.tmpl) file. After you rebuild the ORDS image, use the rebuilt image in the **[config/samples/sidb/oraclerestdataservice.yaml](../../config/samples/sidb/oraclerestdataservice.yaml)** file. + - If you want to install ORDS in a [prebuilt database](#provision-a-pre-built-database), make sure to attach the **database persistence** by uncommenting the `persistence` section in the **[config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml)** file, while provisioning the prebuilt database. ### REST Enable a Database @@ -1018,12 +1018,11 @@ You are required to specify the ORDS secret in the [oraclerestdataservice_create kubectl create secret generic ords-secret --from-literal=oracle_pwd= ``` -Alternatively, you can create this secret and the APEX secret by filling the passwords in the **[oraclerestdataservice_secrets.yaml](../../config/samples/sidb/oraclerestdataservice_secrets.yaml)** file and applying it using the command below: +Alternatively, you can create this secret by filling the passwords in the **[oraclerestdataservice_secrets.yaml](../../config/samples/sidb/oraclerestdataservice_secrets.yaml)** file and applying it using the command below: ```bash kubectl apply -f singleinstancedatabase_secrets.yaml ``` -The APEX secret created above, will be used while [installing APEX](#apex-installation). #### Creation Status @@ -1043,7 +1042,7 @@ Clients can access the REST Endpoints using `.status.databaseApiUrl` as shown in ```sh $ kubectl get oraclerestdataservice/ords-sample -o "jsonpath={.status.databaseApiUrl}" - https://10.0.25.54:8443/ords/ORCLPDB1/_/db-api/stable/ + http://10.0.25.54:8181/ords/schema1/_/db-api/stable/ ``` All the REST Endpoints can be found in [_REST APIs for Oracle Database_](https://docs.oracle.com/en/database/oracle/oracle-database/21/dbrst/rest-endpoints.html). @@ -1052,37 +1051,51 @@ There are two basic approaches for authentication to the REST Endpoints. Certain #### Database API -To call certain REST endpoints, you must use the ORDS_PUBLIC_USER with role `SQL Administrator`, and `.spec.ordsPassword` credentials. +To call certain REST endpoints, you must use the Schema User which is Rest Enabled with role `SQL Administrator`, and `.spec.ordsPassword` credentials. -The ORDS user also has the following additional roles: `System Administrator, SQL Developer, oracle.dbtools.autorest.any.schema`. +The Schema user also has the following additional roles: `System Administrator, SQL Developer`. -Use this ORDS user to authenticate the following: +Use this Schema user to authenticate the following: * Database APIs * Any Protected AutoRest Enabled Object APIs * Database Actions of any REST Enabled Schema ##### Examples -Some examples for the Database API usage are as follows: +Some examples for the Database API usage for REST Enabled schema1 are as follows: - **Get all Database Components** ```sh - curl -s -k -X GET -u 'ORDS_PUBLIC_USER:<.spec.ordsPassword>' https://10.0.25.54:8443/ords/ORCLPDB1/_/db-api/stable/database/components/ | python -m json.tool + curl -s -k -X GET -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' http://10.0.25.54:8181/ords/schema1/_/db-api/stable/database/components/ | python -m json.tool ``` - **Get all Database Users** ```sh - curl -s -k -X GET -u 'ORDS_PUBLIC_USER:<.spec.ordsPassword>' https://10.0.25.54:8443/ords/ORCLPDB1/_/db-api/stable/database/security/users/ | python -m json.tool + curl -s -k -X GET -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' http://10.0.25.54:8181/ords/schema1/_/db-api/stable/database/security/users/ | python -m json.tool ``` - **Get all Tablespaces** ```sh - curl -s -k -X GET -u 'ORDS_PUBLIC_USER:<.spec.ordsPassword>' https://10.0.25.54:8443/ords/ORCLPDB1/_/db-api/stable/database/storage/tablespaces/ | python -m json.tool + curl -s -k -X GET -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' http://10.0.25.54:8181/ords/schema1/_/db-api/stable/database/storage/tablespaces/ | python -m json.tool ``` - **Get all Database Parameters** ```sh - curl -s -k -X GET -u 'ORDS_PUBLIC_USER:<.spec.ordsPassword>' https://10.0.25.54:8443/ords/ORCLPDB1/_/db-api/stable/database/parameters/ | python -m json.tool + curl -s -k -X GET -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' http://10.0.25.54:8181/ords/schema1/_/db-api/stable/database/parameters/ | python -m json.tool ``` - **Get all Feature Usage Statistics** ```sh - curl -s -k -X GET -u 'ORDS_PUBLIC_USER:<.spec.ordsPassword>' https://10.0.25.54:8443/ords/ORCLPDB1/_/db-api/stable/database/feature_usage/ | python -m json.tool + curl -s -k -X GET -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' http://10.0.25.54:8181/ords/schema1/_/db-api/stable/database/feature_usage/ | python -m json.tool ``` + +#### MongoDB API + +To enable the Database API for MongoDB set `.spec.mongoDbApi` to `true`. Thus MongoDB applications would now be able to connect to Oracle Database using the MongoDB API Access URL. + +```sh +$ kubectl get oraclerestdataservice/ords-sample -o "jsonpath={.status.mongoDbApiAccessUrl}" + + mongodb://[{user}:{password}@]10.0.25.54:27017/{user}?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true +``` + +* Change [{user}:{password}@] to database username and password. Retain the @ symbol but remove all the brackets. +* Change the {user} later in the URL to database username as well. + #### Advanced Usages ##### Oracle Data Pump @@ -1119,7 +1132,7 @@ Create a file called "/tmp/table.sql" with the following contents. Run the following API to run the script created in the previous example: ```sh - curl -s -k -X "POST" "https://10.0.25.54:8443/ords/<.spec.restEnableSchemas[].pdbName>/<.spec.restEnableSchemas[].urlMapping>/_/sql" \ + curl -s -k -X "POST" "http://10.0.25.54:8181/ords/<.spec.restEnableSchemas[].urlMapping>/_/sql" \ -H "Content-Type: application/sql" \ -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' \ -d @/tmp/table.sql @@ -1130,7 +1143,7 @@ Run the following API to run the script created in the previous example: Fetch all entries from 'DEPT' table by calling the following API ```sh - curl -s -k -X "POST" "https://10.0.25.54:8443/ords/<.spec.restEnableSchemas[].pdbName>/<.spec.restEnableSchemas[].urlMapping>/_/sql" \ + curl -s -k -X "POST" "http://10.0.25.54:8181/ords/<.spec.restEnableSchemas[].urlMapping>/_/sql" \ -H "Content-Type: application/sql" \ -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' \ -d $'select * from dept;' | python -m json.tool @@ -1152,16 +1165,12 @@ Database Actions can be accessed with a browser by using `.status.databaseAction ```sh $ kubectl get oraclerestdataservice/ords-sample -o "jsonpath={.status.databaseActionsUrl}" - https://10.0.25.54:8443/ords/sql-developer + http://10.0.25.54:8181/ords/sql-developer ``` To access Database Actions, sign in by using the following code as a database user whose schema has been REST-enabled: -* First Page: \ -PDB Name: `.spec.restEnableSchemas[].pdbName` \ -Username: `.spec.restEnableSchemas[].urlMapping` - -* Second Page: \ +* Login Page: \ Username: `.spec.restEnableSchemas[].schemaName` \ Password: `.spec.ordsPassword` @@ -1175,21 +1184,9 @@ Oracle APEX is a low-code development platform that enables developers to build Using APEX, developers can quickly develop and deploy compelling apps that solve real problems and provide immediate value. Developers won't need to be an expert in a vast array of technologies to deliver sophisticated solutions. Focus on solving the problem and let APEX take care of the rest. -The `OraOperator` facilitates installation of APEX in the database and also configures ORDS for it. The following section will explain installing APEX with configured ORDS: - -* For quick provisioning, use the sample **[config/samples/sidb/oraclerestdataservice_apex.yaml](../../config/samples/sidb/oraclerestdataservice_apex.yaml)** file. For example: - - kubectl apply -f oraclerestdataservice_apex.yaml - -* The APEX Password is used as a common password for `APEX_PUBLIC_USER, APEX_REST_PUBLIC_USER, APEX_LISTENER` and Apex administrator (username: `ADMIN`) mapped to secretKey. You can create APEX secret using the following command: - - ```bash - kubectl create secret generic apex-secret --from-literal=oracle_pwd= - ``` - Please refer [this](#note) section for APEX secret creation using the **[oraclerestdataservice_secrets.yaml](../../config/samples/sidb/oraclerestdataservice_secrets.yaml)** file. - -* The status of ORDS turns to `Updating` during APEX configuration, and changes to `Healthy` after successful configuration. You can also check status by using the following command: +The `OraOperator` facilitates installation of APEX in the database and also configures ORDS for it. +* Status of APEX configuration can be checked using the following command: ```sh $ kubectl get oraclerestdataservice ords-sample -o "jsonpath={.status.apexConfigured}" @@ -1197,24 +1194,23 @@ The `OraOperator` facilitates installation of APEX in the database and also conf [true] ``` -* If you configure APEX after ORDS is installed, then ORDS pods will be deleted and recreated. - Application Express can be accessed via browser using `.status.apexUrl` in the following command. ```sh $ kubectl get oraclerestdataservice/ords-sample -o "jsonpath={.status.apexUrl}" - https://10.0.25.54:8443/ords/ORCLPDB1/apex + http://10.0.25.54:8181/ords/apex ``` -Sign in to Administration services using -workspace: `INTERNAL` -username: `ADMIN` -password: `.spec.apexPassword` +Sign in to Administration services using \ +workspace: `INTERNAL` \ +username: `ADMIN` \ +password: `Welcome_1` ![application-express-admin-home](/images/sidb/application-express-admin-home.png) **Note:** +- It is strongly recommend that default apex admin password is changed. - By default, the full development environment is initialized in APEX. After deployment, you can change it manually to the runtime environment. To change environments, run the script `apxdevrm.sql` after connecting to the primary database from the ORDS pod as the `SYS` user with `SYSDBA` privilege. For detailed instructions, see: [Converting a Full Development Environment to a Runtime Environment](https://docs.oracle.com/en/database/oracle/application-express/21.2/htmig/converting-between-runtime-and-full-development-environments.html#GUID-B0621B40-3441-44ED-9D86-29B058E26BE9). ### Delete ORDS From ebb28175379853fd130882f8f9af2356fe78bb27 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Fri, 11 Oct 2024 05:05:36 +0000 Subject: [PATCH 124/414] FSFO support sidb with dataguardbroker and DG controller refactor --- .../v1alpha1/dataguardbroker_types.go | 73 +- .../v1alpha1/dataguardbroker_webhook.go | 9 + apis/database/v1alpha1/dbcssystem_types.go | 4 +- .../singleinstancedatabase_webhook.go | 6 + .../v1alpha1/zz_generated.deepcopy.go | 45 +- commons/database/constants.go | 13 +- commons/database/podbuilder.go | 108 ++ commons/database/svcbuilder.go | 99 ++ commons/database/utils.go | 26 - config/samples/sidb/dataguardbroker.yaml | 6 +- .../database/dataguardbroker_controller.go | 1199 ----------------- .../singleinstancedatabase_controller.go | 60 +- controllers/dataguard/datagauard_errors.go | 47 + controllers/dataguard/dataguard_utils.go | 1008 ++++++++++++++ .../dataguard/dataguardbroker_controller.go | 497 +++++++ docs/sidb/README.md | 57 +- main.go | 5 +- 17 files changed, 1926 insertions(+), 1336 deletions(-) create mode 100644 commons/database/podbuilder.go create mode 100644 commons/database/svcbuilder.go delete mode 100644 controllers/database/dataguardbroker_controller.go create mode 100644 controllers/dataguard/datagauard_errors.go create mode 100644 controllers/dataguard/dataguard_utils.go create mode 100644 controllers/dataguard/dataguardbroker_controller.go diff --git a/apis/database/v1alpha1/dataguardbroker_types.go b/apis/database/v1alpha1/dataguardbroker_types.go index 37d71b92..416c2ce4 100644 --- a/apis/database/v1alpha1/dataguardbroker_types.go +++ b/apis/database/v1alpha1/dataguardbroker_types.go @@ -56,20 +56,10 @@ type DataguardBrokerSpec struct { LoadBalancer bool `json:"loadBalancer,omitempty"` ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` // +kubebuilder:validation:Enum=MaxPerformance;MaxAvailability - ProtectionMode string `json:"protectionMode"` - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - FastStartFailOver DataguardBrokerFastStartFailOver `json:"fastStartFailOver,omitempty"` -} - -type DataguardBrokerFastStartFailOver struct { - Enable bool `json:"enable,omitempty"` - Strategy []DataguardBrokerStrategy `json:"strategy,omitempty"` -} + ProtectionMode string `json:"protectionMode"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` -// FSFO strategy -type DataguardBrokerStrategy struct { - SourceDatabaseRef string `json:"sourceDatabaseRef,omitempty"` - TargetDatabaseRefs string `json:"targetDatabaseRefs,omitempty"` + FastStartFailover bool `json:"fastStartFailover,omitempty"` } // DataguardBrokerStatus defines the observed state of DataguardBroker @@ -84,10 +74,13 @@ type DataguardBrokerStatus struct { ExternalConnectString string `json:"externalConnectString,omitempty"` ClusterConnectString string `json:"clusterConnectString,omitempty"` Status string `json:"status,omitempty"` + + FastStartFailover bool `json:"fastStartFailover,omitempty"` + DatabasesInDataguardConfig map[string]string `json:"databasesInDataguardConfig,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status // +kubebuilder:printcolumn:JSONPath=".status.primaryDatabase",name="Primary",type="string" // +kubebuilder:printcolumn:JSONPath=".status.standbyDatabases",name="Standbys",type="string" // +kubebuilder:printcolumn:JSONPath=".spec.protectionMode",name="Protection Mode",type="string" @@ -95,6 +88,7 @@ type DataguardBrokerStatus struct { // +kubebuilder:printcolumn:JSONPath=".status.externalConnectString",name="Connect Str",type="string" // +kubebuilder:printcolumn:JSONPath=".spec.primaryDatabaseRef",name="Primary Database",type="string", priority=1 // +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.fastStartFailover",name="FSFO", type="string" // DataguardBroker is the Schema for the dataguardbrokers API type DataguardBroker struct { @@ -105,6 +99,55 @@ type DataguardBroker struct { Status DataguardBrokerStatus `json:"status,omitempty"` } +// ////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns the current primary database in the dataguard configuration from the resource status/spec +// ////////////////////////////////////////////////////////////////////////////////////////////////// +func (broker *DataguardBroker) GetCurrentPrimaryDatabase() string { + if broker.Status.PrimaryDatabase != "" { + return broker.Status.DatabasesInDataguardConfig[broker.Status.PrimaryDatabase] + } + return broker.Spec.PrimaryDatabaseRef +} + +// ////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns databases in Dataguard configuration from the resource status/spec +// ////////////////////////////////////////////////////////////////////////////////////////////////// +func (broker *DataguardBroker) GetDatabasesInDataGuardConfiguration() []string { + var databases []string + if len(broker.Status.DatabasesInDataguardConfig) > 0 { + for _, value := range broker.Status.DatabasesInDataguardConfig { + if value != "" { + databases = append(databases, value) + } + } + + return databases + } + + databases = append(databases, broker.Spec.PrimaryDatabaseRef) + databases = append(databases, broker.Spec.StandbyDatabaseRefs...) + return databases +} + +// ////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns standby databases in the dataguard configuration from the resource status/spec +// ////////////////////////////////////////////////////////////////////////////////////////////////// +func (broker *DataguardBroker) GetStandbyDatabasesInDgConfig() []string { + var databases []string + if len(broker.Status.DatabasesInDataguardConfig) > 0 { + for _, value := range broker.Status.DatabasesInDataguardConfig { + if value != "" && value != broker.Status.PrimaryDatabase { + databases = append(databases, value) + } + } + + return databases + } + + databases = append(databases, broker.Spec.StandbyDatabaseRefs...) + return databases +} + //+kubebuilder:object:root=true // DataguardBrokerList contains a list of DataguardBroker diff --git a/apis/database/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go index a9d59286..4b470665 100644 --- a/apis/database/v1alpha1/dataguardbroker_webhook.go +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -89,6 +89,10 @@ func (r *DataguardBroker) Default() { r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] = "100" } } + + if r.Spec.SetAsPrimaryDatabase != "" { + r.Spec.SetAsPrimaryDatabase = strings.ToUpper(r.Spec.SetAsPrimaryDatabase) + } } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. @@ -155,6 +159,11 @@ func (r *DataguardBroker) ValidateUpdate(old runtime.Object) (admission.Warnings field.Forbidden(field.NewPath("spec").Child("primaryDatabaseRef"), "cannot be changed")) } + if (oldObj.Status.FastStartFailover || r.Spec.FastStartFailover) && r.Spec.SetAsPrimaryDatabase != "" { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("setAsPrimaryDatabase"), "switchover not supported when fastStartFailover is true")) + } + if len(allErrs) == 0 { return nil, nil } diff --git a/apis/database/v1alpha1/dbcssystem_types.go b/apis/database/v1alpha1/dbcssystem_types.go index 37e80a6b..52d725b2 100644 --- a/apis/database/v1alpha1/dbcssystem_types.go +++ b/apis/database/v1alpha1/dbcssystem_types.go @@ -138,8 +138,8 @@ type DbStatus struct { } type DbWorkrequests struct { - OperationType *string `json:"operationType,omitmpty"` - OperationId *string `json:"operationId,omitemty"` + OperationType *string `json:"operationType,omitempty"` + OperationId *string `json:"operationId,omitempty"` PercentComplete string `json:"percentComplete,omitempty"` TimeAccepted string `json:"timeAccepted,omitempty"` TimeStarted string `json:"timeStarted,omitempty"` diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 1a47207d..1cbc57a8 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -509,6 +509,12 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("persistence"), "uninstall ORDS to change Persistence")) } + + if old.Status.Replicas != r.Spec.Replicas && old.Status.DgBrokerConfigured { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("replicas"), "cannot be updated for a database in a Data Guard configuration")) + } + if len(allErrs) == 0 { return nil, nil } diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 4ed96cd8..c315cbfa 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -898,7 +898,7 @@ func (in *DataguardBroker) DeepCopyInto(out *DataguardBroker) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBroker. @@ -919,26 +919,6 @@ func (in *DataguardBroker) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DataguardBrokerFastStartFailOver) DeepCopyInto(out *DataguardBrokerFastStartFailOver) { - *out = *in - if in.Strategy != nil { - in, out := &in.Strategy, &out.Strategy - *out = make([]DataguardBrokerStrategy, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBrokerFastStartFailOver. -func (in *DataguardBrokerFastStartFailOver) DeepCopy() *DataguardBrokerFastStartFailOver { - if in == nil { - return nil - } - out := new(DataguardBrokerFastStartFailOver) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DataguardBrokerList) DeepCopyInto(out *DataguardBrokerList) { *out = *in @@ -993,7 +973,6 @@ func (in *DataguardBrokerSpec) DeepCopyInto(out *DataguardBrokerSpec) { (*out)[key] = val } } - in.FastStartFailOver.DeepCopyInto(&out.FastStartFailOver) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBrokerSpec. @@ -1009,6 +988,13 @@ func (in *DataguardBrokerSpec) DeepCopy() *DataguardBrokerSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DataguardBrokerStatus) DeepCopyInto(out *DataguardBrokerStatus) { *out = *in + if in.DatabasesInDataguardConfig != nil { + in, out := &in.DatabasesInDataguardConfig, &out.DatabasesInDataguardConfig + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBrokerStatus. @@ -1021,21 +1007,6 @@ func (in *DataguardBrokerStatus) DeepCopy() *DataguardBrokerStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DataguardBrokerStrategy) DeepCopyInto(out *DataguardBrokerStrategy) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBrokerStrategy. -func (in *DataguardBrokerStrategy) DeepCopy() *DataguardBrokerStrategy { - if in == nil { - return nil - } - out := new(DataguardBrokerStrategy) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DbStatus) DeepCopyInto(out *DbStatus) { *out = *in diff --git a/commons/database/constants.go b/commons/database/constants.go index 3abdeb94..fd549ca6 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -184,10 +184,6 @@ const DataguardBrokerMaxPerformanceCMD string = "CREATE CONFIGURATION dg_config "\nADD DATABASE ${ORACLE_SID} AS CONNECT IDENTIFIER IS ${SVC_HOST}:1521/${ORACLE_SID} MAINTAINED AS PHYSICAL;" + "\nEDIT DATABASE ${PRIMARY_SID} SET PROPERTY LogXptMode='ASYNC';" + "\nEDIT DATABASE ${ORACLE_SID} SET PROPERTY LogXptMode='ASYNC';" + - "\nEDIT DATABASE ${PRIMARY_SID} SET PROPERTY STATICCONNECTIDENTIFIER='(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=${PRIMARY_IP})(PORT=1521))" + - "(CONNECT_DATA=(SERVICE_NAME=${PRIMARY_SID}_DGMGRL)(INSTANCE_NAME=${PRIMARY_SID})(SERVER=DEDICATED)))';" + - "\nEDIT DATABASE ${ORACLE_SID} SET PROPERTY STATICCONNECTIDENTIFIER='(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=${SVC_HOST})(PORT=1521))" + - "(CONNECT_DATA=(SERVICE_NAME=${ORACLE_SID}_DGMGRL)(INSTANCE_NAME=${ORACLE_SID})(SERVER=DEDICATED)))';" + "\nEDIT CONFIGURATION SET PROTECTION MODE AS MAXPERFORMANCE;" + "\nENABLE CONFIGURATION;" @@ -195,10 +191,6 @@ const DataguardBrokerMaxAvailabilityCMD string = "CREATE CONFIGURATION dg_config "\nADD DATABASE ${ORACLE_SID} AS CONNECT IDENTIFIER IS ${SVC_HOST}:1521/${ORACLE_SID} MAINTAINED AS PHYSICAL;" + "\nEDIT DATABASE ${PRIMARY_SID} SET PROPERTY LogXptMode='SYNC';" + "\nEDIT DATABASE ${ORACLE_SID} SET PROPERTY LogXptMode='SYNC';" + - "\nEDIT DATABASE ${PRIMARY_SID} SET PROPERTY STATICCONNECTIDENTIFIER='(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=${PRIMARY_IP})(PORT=1521))" + - "(CONNECT_DATA=(SERVICE_NAME=${PRIMARY_SID}_DGMGRL)(INSTANCE_NAME=${PRIMARY_SID})(SERVER=DEDICATED)))';" + - "\nEDIT DATABASE ${ORACLE_SID} SET PROPERTY STATICCONNECTIDENTIFIER='(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=${SVC_HOST})(PORT=1521))" + - "(CONNECT_DATA=(SERVICE_NAME=${ORACLE_SID}_DGMGRL)(INSTANCE_NAME=${ORACLE_SID})(SERVER=DEDICATED)))';" + "\nEDIT CONFIGURATION SET PROTECTION MODE AS MAXAVAILABILITY;" + "\nENABLE CONFIGURATION;" @@ -223,6 +215,9 @@ const DataguardBrokerGetDatabaseCMD string = "SELECT DATABASE || ':' || DATAGUAR const EnableFSFOCMD string = "ENABLE FAST_START FAILOVER;" +const DisableFSFOCMD string = "STOP OBSERVER %s" + + "\nDISABLE FAST_START FAILOVER;" + const RemoveDataguardConfiguration string = "DISABLE FAST_START FAILOVER;" + "\nEDIT CONFIGURATION SET PROTECTION MODE AS MAXPERFORMANCE;" + "\nREMOVE CONFIGURATION;" @@ -407,6 +402,8 @@ const StatusReady string = "Healthy" const StatusError string = "Error" +const StatusUnknown string = "Unknown" + const ValueUnavailable string = "Unavailable" const NoExternalIp string = "Node ExternalIP unavailable" diff --git a/commons/database/podbuilder.go b/commons/database/podbuilder.go new file mode 100644 index 00000000..c704c4fc --- /dev/null +++ b/commons/database/podbuilder.go @@ -0,0 +1,108 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +import ( + "k8s.io/apimachinery/pkg/types" + + corev1 "k8s.io/api/core/v1" +) + +type PodBuilder interface { + SetNamespacedName(types.NamespacedName) *PodBuilder + SetLabels(map[string]string) *PodBuilder + SetTerminationGracePeriodSeconds(int64) *PodBuilder + SetNodeSelector(map[string]string) *PodBuilder + SetSecurityContext(corev1.PodSecurityContext) *PodBuilder + SetImagePullSecrets(string) *PodBuilder + AppendContainers(corev1.Container) *PodBuilder + Build() corev1.Pod +} + +type RealPodBuilder struct { + pod corev1.Pod +} + +func (rpb *RealPodBuilder) SetNamespacedName(namespacedName types.NamespacedName) *RealPodBuilder { + rpb.pod.ObjectMeta.Name = namespacedName.Name + rpb.pod.ObjectMeta.Namespace = namespacedName.Namespace + return rpb +} + +func (rpb *RealPodBuilder) SetLabels(labels map[string]string) *RealPodBuilder { + rpb.pod.ObjectMeta.Labels = labels + return rpb +} + +func (rpb *RealPodBuilder) SetTerminationGracePeriodSeconds(terminationGracePeriod int64) *RealPodBuilder { + rpb.pod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriod + return rpb +} + +func (rpb *RealPodBuilder) SetNodeSelector(nsRule map[string]string) *RealPodBuilder { + rpb.pod.Spec.NodeSelector = nsRule + return rpb +} + +func (rpb *RealPodBuilder) SetSecurityContext(podSecurityContext corev1.PodSecurityContext) *RealPodBuilder { + rpb.pod.Spec.SecurityContext = &podSecurityContext + return rpb +} + +func (rpb *RealPodBuilder) SetImagePullSecrets(imagePullSecret string) *RealPodBuilder { + rpb.pod.Spec.ImagePullSecrets = []corev1.LocalObjectReference{ + { + Name: imagePullSecret, + }, + } + return rpb +} + +func (rpb *RealPodBuilder) AppendContainers(container corev1.Container) *RealPodBuilder { + rpb.pod.Spec.Containers = append(rpb.pod.Spec.Containers, container) + return rpb +} + +func (rpb *RealPodBuilder) Build() corev1.Pod { + return rpb.pod +} + +func NewRealPodBuilder() *RealPodBuilder { + return &RealPodBuilder{} +} diff --git a/commons/database/svcbuilder.go b/commons/database/svcbuilder.go new file mode 100644 index 00000000..8029c8ee --- /dev/null +++ b/commons/database/svcbuilder.go @@ -0,0 +1,99 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +import ( + corev1 "k8s.io/api/core/v1" +) + +type ServiceBuilder interface { + SetName(string) *ServiceBuilder + SetNamespace(string) *ServiceBuilder + SetLabels(map[string]string) *ServiceBuilder + SetAnnotation(map[string]string) *ServiceBuilder + SetPorts([]corev1.ServicePort) *ServiceBuilder + SetSelector(map[string]string) *ServiceBuilder + SetPublishNotReadyAddresses(bool) *ServiceBuilder + SetServiceType(corev1.ServiceType) *ServiceBuilder + Build() *corev1.Service +} + +type RealServiceBuilder struct { + service corev1.Service +} + +func (rsb *RealServiceBuilder) SetName(name string) *RealServiceBuilder { + rsb.service.ObjectMeta.Name = name + return rsb +} +func (rsb *RealServiceBuilder) SetNamespace(namespace string) *RealServiceBuilder { + rsb.service.ObjectMeta.Namespace = namespace + return rsb +} +func (rsb *RealServiceBuilder) SetLabels(labels map[string]string) *RealServiceBuilder { + rsb.service.ObjectMeta.Labels = labels + return rsb +} +func (rsb *RealServiceBuilder) SetAnnotation(annotations map[string]string) *RealServiceBuilder { + rsb.service.ObjectMeta.Annotations = annotations + return rsb +} +func (rsb *RealServiceBuilder) SetPorts(ports []corev1.ServicePort) *RealServiceBuilder { + rsb.service.Spec.Ports = ports + return rsb +} +func (rsb *RealServiceBuilder) SetSelector(selector map[string]string) *RealServiceBuilder { + rsb.service.Spec.Selector = selector + return rsb +} +func (rsb *RealServiceBuilder) SetPublishNotReadyAddresses(flag bool) *RealServiceBuilder { + rsb.service.Spec.PublishNotReadyAddresses = flag + return rsb +} +func (rsb *RealServiceBuilder) SetType(serviceType corev1.ServiceType) *RealServiceBuilder { + rsb.service.Spec.Type = serviceType + return rsb +} +func (rsb *RealServiceBuilder) Build() corev1.Service { + return rsb.service +} + +func NewRealServiceBuilder() *RealServiceBuilder { + return &RealServiceBuilder{} +} diff --git a/commons/database/utils.go b/commons/database/utils.go index 1723bc90..e0536642 100644 --- a/commons/database/utils.go +++ b/commons/database/utils.go @@ -502,32 +502,6 @@ func GetPrimaryDatabase(databases []string) string { return primary } -// Returns the databases in DG config . -func GetDatabasesInDgConfig(readyPod corev1.Pod, r client.Reader, - config *rest.Config, ctx context.Context, req ctrl.Request) ([]string, string, error) { - log := ctrllog.FromContext(ctx).WithValues("GetDatabasesInDgConfig", req.NamespacedName) - - // ## FIND DATABASES PRESENT IN DG CONFIGURATION - out, err := ExecCommand(r, config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | sqlplus -s / as sysdba ", DataguardBrokerGetDatabaseCMD)) - if err != nil { - return []string{}, "", err - } - log.Info("GetDatabasesInDgConfig Output") - log.Info(out) - - if !strings.Contains(out, "no rows selected") && !strings.Contains(out, "ORA-") { - out1 := strings.Replace(out, " ", "_", -1) - // filtering output and storing databses in dg configuration in "databases" slice - databases := strings.Fields(out1) - - // first 2 values in the slice will be column name(DATABASES) and a seperator(--------------) . so take the slice from position [2:] - databases = databases[2:] - return databases, out, nil - } - return []string{}, out, errors.New("databases in DG config is nil") -} - // Returns Database version func GetDatabaseVersion(readyPod corev1.Pod, r client.Reader, config *rest.Config, ctx context.Context, req ctrl.Request) (string, error) { diff --git a/config/samples/sidb/dataguardbroker.yaml b/config/samples/sidb/dataguardbroker.yaml index 5425afb7..214c6f86 100644 --- a/config/samples/sidb/dataguardbroker.yaml +++ b/config/samples/sidb/dataguardbroker.yaml @@ -25,5 +25,9 @@ spec: ## Protection Mode for dg configuration . MaxAvailability or MaxPerformance protectionMode: MaxAvailability - ## Manual Switchover to this database to make it primary(if not already), requires target Database SID . + ## Specify the database SID to switchover thereby making it the primary. + ## Switchover is not supported when fastStartFailover is true. setAsPrimaryDatabase: "" + + ## Enable/disable Fast-Start Failover for the dataguard configuration. + fastStartFailover: false diff --git a/controllers/database/dataguardbroker_controller.go b/controllers/database/dataguardbroker_controller.go deleted file mode 100644 index 9faaefd2..00000000 --- a/controllers/database/dataguardbroker_controller.go +++ /dev/null @@ -1,1199 +0,0 @@ -/* -** Copyright (c) 2023 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package controllers - -import ( - "context" - "errors" - "fmt" - "strings" - "time" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - dbcommons "github.com/oracle/oracle-database-operator/commons/database" -) - -// DataguardBrokerReconciler reconciles a DataguardBroker object -type DataguardBrokerReconciler struct { - client.Client - Log logr.Logger - Scheme *runtime.Scheme - Config *rest.Config - Recorder record.EventRecorder -} - -const dataguardBrokerFinalizer = "database.oracle.com/dataguardbrokerfinalizer" - -//+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch -//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the DataguardBroker object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile -func (r *DataguardBrokerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - - r.Log.Info("Reconcile requested") - - dataguardBroker := &dbapi.DataguardBroker{} - err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, dataguardBroker) - if err != nil { - if apierrors.IsNotFound(err) { - r.Log.Info("Resource deleted") - return requeueN, nil - } - return requeueN, err - } - - // Manage DataguardBroker Deletion - result, err := r.manageDataguardBrokerDeletion(req, ctx, dataguardBroker) - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, err - } - if err != nil { - r.Log.Error(err, err.Error()) - return result, err - } - - // Fetch Primary Database Reference - singleInstanceDatabase := &dbapi.SingleInstanceDatabase{} - err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: dataguardBroker.Spec.PrimaryDatabaseRef}, singleInstanceDatabase) - if err != nil { - if apierrors.IsNotFound(err) { - r.Log.Info("Resource deleted") - return requeueN, nil - } - return requeueN, err - } - - /* Initialize Status */ - if dataguardBroker.Status.Status == "" { - dataguardBroker.Status.Status = dbcommons.StatusCreating - dataguardBroker.Status.ExternalConnectString = dbcommons.ValueUnavailable - dataguardBroker.Status.ClusterConnectString = dbcommons.ValueUnavailable - r.Status().Update(ctx, dataguardBroker) - } - - // Always refresh status before a reconcile - defer r.Status().Update(ctx, dataguardBroker) - - // Create Service to point to primary database always - result = r.createSVC(ctx, req, dataguardBroker) - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, nil - } - - // Validate if Primary Database Reference is ready - result, sidbReadyPod, adminPassword := r.validateSidbReadiness(dataguardBroker, singleInstanceDatabase, ctx, req) - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, nil - } - - // Setup the DG Configuration - result = r.setupDataguardBrokerConfiguration(dataguardBroker, singleInstanceDatabase, sidbReadyPod, adminPassword, ctx, req) - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, nil - } - - // Set a particular database as primary - result = r.SetAsPrimaryDatabase(singleInstanceDatabase.Spec.Sid, dataguardBroker.Spec.SetAsPrimaryDatabase, dataguardBroker, - singleInstanceDatabase, adminPassword, ctx, req) - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, nil - } - - // If LoadBalancer = true , ensure Connect String is updated - if dataguardBroker.Status.ExternalConnectString == dbcommons.ValueUnavailable { - return requeueY, nil - } - - dataguardBroker.Status.Status = dbcommons.StatusReady - - r.Log.Info("Reconcile completed") - return ctrl.Result{}, nil - -} - -// ##################################################################################################### -// -// Validate Readiness of the primary DB specified -// -// ##################################################################################################### -func (r *DataguardBrokerReconciler) validateSidbReadiness(m *dbapi.DataguardBroker, - n *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (ctrl.Result, corev1.Pod, string) { - - log := r.Log.WithValues("validateSidbReadiness", req.NamespacedName) - adminPassword := "" - // ## FETCH THE SIDB REPLICAS . - sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, n.Spec.Image.Version, - n.Spec.Image.PullFrom, n.Name, n.Namespace, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY, sidbReadyPod, adminPassword - } - - if n.Status.Status != dbcommons.StatusReady { - - eventReason := "Waiting" - eventMsg := "Waiting for " + n.Name + " to be Ready" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - return requeueY, sidbReadyPod, adminPassword - } - - // Validate databaseRef Admin Password - adminPasswordSecret := &corev1.Secret{} - err = r.Get(ctx, types.NamespacedName{Name: n.Spec.AdminPassword.SecretName, Namespace: n.Namespace}, adminPasswordSecret) - if err != nil { - if apierrors.IsNotFound(err) { - //m.Status.Status = dbcommons.StatusError - eventReason := "Waiting" - eventMsg := "waiting for secret : " + n.Spec.AdminPassword.SecretName + " to get created" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - r.Log.Info("Secret " + n.Spec.AdminPassword.SecretName + " Not Found") - return requeueY, sidbReadyPod, adminPassword - } - log.Error(err, err.Error()) - return requeueY, sidbReadyPod, adminPassword - } - adminPassword = string(adminPasswordSecret.Data[n.Spec.AdminPassword.SecretKey]) - - out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | %s", fmt.Sprintf(dbcommons.ValidateAdminPassword, adminPassword), dbcommons.GetSqlClient(n.Spec.Edition))) - if err != nil { - log.Error(err, err.Error()) - return requeueY, sidbReadyPod, adminPassword - } - if strings.Contains(out, "USER is \"SYS\"") { - log.Info("validated Admin password successfully") - } else if strings.Contains(out, "ORA-01017") { - //m.Status.Status = dbcommons.StatusError - eventReason := "Logon denied" - eventMsg := "invalid databaseRef admin password. secret: " + n.Spec.AdminPassword.SecretName - r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) - return requeueY, sidbReadyPod, adminPassword - } else { - return requeueY, sidbReadyPod, adminPassword - } - - return requeueN, sidbReadyPod, adminPassword -} - -// ############################################################################# -// -// Instantiate Service spec from StandbyDatabase spec -// -// ############################################################################# -func (r *DataguardBrokerReconciler) instantiateSVCSpec(m *dbapi.DataguardBroker) *corev1.Service { - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: m.Name, - Namespace: m.Namespace, - Labels: map[string]string{ - "app": m.Name, - }, - Annotations: func() map[string]string { - annotations := make(map[string]string) - if len(m.Spec.ServiceAnnotations) != 0 { - for key, value := range m.Spec.ServiceAnnotations { - annotations[key] = value - } - } - return annotations - }(), - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "listener", - Port: 1521, - Protocol: corev1.ProtocolTCP, - }, - { - Name: "xmldb", - Port: 5500, - Protocol: corev1.ProtocolTCP, - }, - }, - Selector: map[string]string{ - "app": m.Name, - }, - Type: corev1.ServiceType(func() string { - if m.Spec.LoadBalancer { - return "LoadBalancer" - } - return "NodePort" - }()), - }, - } - // Set StandbyDatabase instance as the owner and controller - ctrl.SetControllerReference(m, svc, r.Scheme) - return svc -} - -// ############################################################################# -// -// Create a Service for StandbyDatabase -// -// ############################################################################# -func (r *DataguardBrokerReconciler) createSVC(ctx context.Context, req ctrl.Request, - m *dbapi.DataguardBroker) ctrl.Result { - - log := r.Log.WithValues("createSVC", req.NamespacedName) - // Check if the Service already exists, if not create a new one - svc := &corev1.Service{} - // Get retrieves an obj for the given object key from the Kubernetes Cluster. - // obj must be a struct pointer so that obj can be updated with the response returned by the Server. - // Here foundsvc is the struct pointer to corev1.Service{} - err := r.Get(ctx, types.NamespacedName{Name: m.Name, Namespace: m.Namespace}, svc) - if err != nil && apierrors.IsNotFound(err) { - // Define a new Service - svc = r.instantiateSVCSpec(m) - log.Info("Creating a new Service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) - err = r.Create(ctx, svc) - //err = r.Update(ctx, svc) - if err != nil { - log.Error(err, "Failed to create new Service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) - return requeueY - } else { - timeout := 30 - // Waiting for Service to get created as sometimes it takes some time to create a service . 30 seconds TImeout - err = dbcommons.WaitForStatusChange(r, svc.Name, m.Namespace, ctx, req, time.Duration(timeout)*time.Second, "svc", "creation") - if err != nil { - log.Error(err, "Error in Waiting for svc status for Creation", "svc.Namespace", svc.Namespace, "SVC.Name", svc.Name) - return requeueY - } - log.Info("Succesfully Created New Service ", "Service.Name : ", svc.Name) - } - time.Sleep(10 * time.Second) - - } else if err != nil { - log.Error(err, "Failed to get Service") - return requeueY - } else if err == nil { - log.Info(" ", "Found Existing Service ", svc.Name) - } - - // update service status - log.Info("Updating the service status...") - m.Status.ClusterConnectString = svc.Name + "." + svc.Namespace + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/DATAGUARD" - if m.Spec.LoadBalancer { - if len(svc.Status.LoadBalancer.Ingress) > 0 { - lbAddress := svc.Status.LoadBalancer.Ingress[0].Hostname - if lbAddress == "" { - lbAddress = svc.Status.LoadBalancer.Ingress[0].IP - } - m.Status.ExternalConnectString = lbAddress + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/DATAGUARD" - } - } else { - nodeip := dbcommons.GetNodeIp(r, ctx, req) - if nodeip != "" { - m.Status.ExternalConnectString = nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/DATAGUARD" - } - } - r.Status().Update(ctx, m) - - return requeueN -} - -// ############################################################################# -// -// Setup the requested DG Configuration -// -// ############################################################################# -func (r *DataguardBrokerReconciler) setupDataguardBrokerConfiguration(m *dbapi.DataguardBroker, n *dbapi.SingleInstanceDatabase, - sidbReadyPod corev1.Pod, adminPassword string, ctx context.Context, req ctrl.Request) ctrl.Result { - log := r.Log.WithValues("setupDataguardBrokerConfiguration", req.NamespacedName) - - databases, _, err := dbcommons.GetDatabasesInDgConfig(sidbReadyPod, r, r.Config, ctx, req) - dbSet := make(map[string]struct{}) - if err != nil { - if err.Error() != "databases in DG config is nil" { - return requeueY - } - } - if len(databases) > 0 { - log.Info("Databases in DG config are :") - for i := 0; i < len(databases); i++ { - log.Info(strings.Split(databases[i], ":")[0]) - dbSet[strings.ToUpper(strings.Split(databases[i], ":")[0])] = struct{}{} - } - } - - for i := 0; i < len(m.Spec.StandbyDatabaseRefs); i++ { - - standbyDatabase := &dbapi.SingleInstanceDatabase{} - err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: m.Spec.StandbyDatabaseRefs[i]}, standbyDatabase) - if err != nil { - if apierrors.IsNotFound(err) { - eventReason := "Warning" - eventMsg := m.Spec.StandbyDatabaseRefs[i] + "not found" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - continue - } - log.Error(err, err.Error()) - return requeueY - } - - // Check if dataguard broker is already configured for the standby database - if standbyDatabase.Status.DgBrokerConfigured { - log.Info("Dataguard broker for standbyDatabase : " + standbyDatabase.Name + " is already configured") - continue - } - _, ok := dbSet[standbyDatabase.Status.Sid] - if ok { - log.Info("A database with the same SID is already configured in the DG") - r.Recorder.Eventf(m, corev1.EventTypeWarning, "Spec Error", "A database with the same SID "+standbyDatabase.Status.Sid+" is already configured in the DG") - continue - } - - m.Status.Status = dbcommons.StatusCreating - r.Status().Update(ctx, m) - - // ## FETCH THE STANDBY REPLICAS . - standbyDatabaseReadyPod, _, _, _, err := dbcommons.FindPods(r, n.Spec.Image.Version, - n.Spec.Image.PullFrom, standbyDatabase.Name, standbyDatabase.Namespace, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - - if standbyDatabase.Status.Status != dbcommons.StatusReady { - - eventReason := "Waiting" - eventMsg := "Waiting for " + standbyDatabase.Name + " to be Ready" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - return requeueY - - } - - result := r.setupDataguardBrokerConfigurationForGivenDB(m, n, standbyDatabase, standbyDatabaseReadyPod, sidbReadyPod, ctx, req, adminPassword) - if result.Requeue { - return result - } - - // Update Databases - r.updateReconcileStatus(m, sidbReadyPod, ctx, req) - } - - eventReason := "DG Configuration up to date" - eventMsg := "" - - // Patch DataguardBroker Service to point selector to Current Primary Name - result := r.patchService(m, sidbReadyPod, n, ctx, req) - if result.Requeue { - return result - } - - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - - return requeueN -} - -// ############################################################################# -// -// Patch DataguardBroker Service to point selector to Current Primary Name -// -// ############################################################################# -func (r *DataguardBrokerReconciler) patchService(m *dbapi.DataguardBroker, sidbReadyPod corev1.Pod, n *dbapi.SingleInstanceDatabase, - ctx context.Context, req ctrl.Request) ctrl.Result { - log := r.Log.WithValues("patchService", req.NamespacedName) - databases, out, err := dbcommons.GetDatabasesInDgConfig(sidbReadyPod, r, r.Config, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - if !strings.Contains(out, "ORA-") { - primarySid := strings.ToUpper(dbcommons.GetPrimaryDatabase(databases)) - primaryName := n.Name - if primarySid != n.Spec.Sid { - primaryName = n.Status.StandbyDatabases[primarySid] - } - - // Patch DataguardBroker Service to point selector to Current Primary Name - svc := &corev1.Service{} - err = r.Get(ctx, types.NamespacedName{Name: req.Name, Namespace: req.Namespace}, svc) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - svc.Spec.Selector["app"] = primaryName - err = r.Update(ctx, svc) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - - m.Status.ClusterConnectString = svc.Name + "." + svc.Namespace + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/DATAGUARD" - if m.Spec.LoadBalancer { - if len(svc.Status.LoadBalancer.Ingress) > 0 { - lbAddress := svc.Status.LoadBalancer.Ingress[0].Hostname - if lbAddress == "" { - lbAddress = svc.Status.LoadBalancer.Ingress[0].IP - } - m.Status.ExternalConnectString = lbAddress + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/DATAGUARD" - } - } else { - nodeip := dbcommons.GetNodeIp(r, ctx, req) - if nodeip != "" { - m.Status.ExternalConnectString = nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/DATAGUARD" - } - } - } - return requeueN -} - -// ############################################################################# -// -// Set up DG Configuration for a given StandbyDatabase -// -// ############################################################################# -func (r *DataguardBrokerReconciler) setupDataguardBrokerConfigurationForGivenDB(m *dbapi.DataguardBroker, n *dbapi.SingleInstanceDatabase, standbyDatabase *dbapi.SingleInstanceDatabase, - standbyDatabaseReadyPod corev1.Pod, sidbReadyPod corev1.Pod, ctx context.Context, req ctrl.Request, adminPassword string) ctrl.Result { - - log := r.Log.WithValues("setupDataguardBrokerConfigurationForGivenDB", req.NamespacedName) - - if standbyDatabaseReadyPod.Name == "" || sidbReadyPod.Name == "" { - return requeueY - } - - // ## CHECK IF DG CONFIGURATION AVAILABLE IN PRIMARY DATABSE## - out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | dgmgrl / as sysdba ", dbcommons.DBShowConfigCMD)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("ShowConfiguration Output") - log.Info(out) - - if strings.Contains(out, "ORA-16525") { - log.Info("ORA-16525: The Oracle Data Guard broker is not yet available on Primary") - return requeueY - } - - _, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, true, "bash", "-c", - fmt.Sprintf(dbcommons.CreateAdminPasswordFile, adminPassword)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DB Admin pwd file created") - - // ORA-16532: Oracle Data Guard broker configuration does not exist , so create one - if strings.Contains(out, "ORA-16532") { - if m.Spec.ProtectionMode == "MaxPerformance" { - // Construct the password file and dgbroker command file - out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf(dbcommons.CreateDGMGRLScriptFile, dbcommons.DataguardBrokerMaxPerformanceCMD)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DGMGRL command file creation output") - log.Info(out) - - // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAXPERFORMANCE ## - out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - "dgmgrl sys@${PRIMARY_DB_CONN_STR} @dgmgrl.cmd < admin.pwd && rm -rf dgmgrl.cmd") - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DgConfigurationMaxPerformance Output") - log.Info(out) - } else if m.Spec.ProtectionMode == "MaxAvailability" { - // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAX AVAILABILITY ## - out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf(dbcommons.CreateDGMGRLScriptFile, dbcommons.DataguardBrokerMaxAvailabilityCMD)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DGMGRL command file creation output") - log.Info(out) - - // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAXPERFORMANCE ## - out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - "dgmgrl sys@${PRIMARY_DB_CONN_STR} @dgmgrl.cmd < admin.pwd && rm -rf dgmgrl.cmd") - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DgConfigurationMaxAvailability Output") - log.Info(out) - } else { - log.Info("SPECIFY correct Protection Mode . Either MaxAvailability or MaxPerformance") - return requeueY - } - - // ## SHOW CONFIGURATION DG - out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | dgmgrl / as sysdba ", dbcommons.DBShowConfigCMD)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } else { - log.Info("ShowConfiguration Output") - log.Info(out) - } - // Set DG Configured status to true for this standbyDatabase and primary Database. so that in next reconcilation, we dont configure this again - n.Status.DgBrokerConfigured = true - standbyDatabase.Status.DgBrokerConfigured = true - r.Status().Update(ctx, standbyDatabase) - r.Status().Update(ctx, n) - // Remove admin pwd file - _, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, true, "bash", "-c", - dbcommons.RemoveAdminPasswordFile) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DB Admin pwd file removed") - - return requeueN - } - - // DG Configuration Exists . So add the standbyDatabase to the existing DG Configuration - databases, _, err := dbcommons.GetDatabasesInDgConfig(sidbReadyPod, r, r.Config, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - - // ## ADD DATABASE TO DG CONFIG , IF NOT PRESENT - found, _ := dbcommons.IsDatabaseFound(standbyDatabase.Spec.Sid, databases, "") - if found { - return requeueN - } - primarySid := dbcommons.GetPrimaryDatabase(databases) - - // If user adds a new standby to a dg config when failover happened to one ot the standbys, we need to have current primary connect string - primaryConnectString := n.Name + ":1521/" + primarySid - if !strings.EqualFold(primarySid, n.Spec.Sid) { - primaryConnectString = n.Status.StandbyDatabases[primarySid] + ":1521/" + primarySid - } - - if m.Spec.ProtectionMode == "MaxPerformance" { - // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAXPERFORMANCE ## - out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf(dbcommons.CreateDGMGRLScriptFile, dbcommons.DataguardBrokerAddDBMaxPerformanceCMD)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DGMGRL command file creation output") - log.Info(out) - - out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("dgmgrl sys@%s @dgmgrl.cmd < admin.pwd && rm -rf dgmgrl.cmd ", primaryConnectString)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DgConfigurationMaxPerformance Output") - log.Info(out) - - } else if m.Spec.ProtectionMode == "MaxAvailability" { - // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAX AVAILABILITY ## - out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf(dbcommons.CreateDGMGRLScriptFile, dbcommons.DataguardBrokerAddDBMaxAvailabilityCMD)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DGMGRL command file creation output") - log.Info(out) - - out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("dgmgrl sys@%s @dgmgrl.cmd < admin.pwd && rm -rf dgmgrl.cmd ", primaryConnectString)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DgConfigurationMaxAvailability Output") - log.Info(out) - - } else { - log.Info("SPECIFY correct Protection Mode . Either MaxAvailability or MaxPerformance") - log.Error(err, err.Error()) - return requeueY - } - - databases, _, err = dbcommons.GetDatabasesInDgConfig(sidbReadyPod, r, r.Config, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - - // ## SET PROPERTY FASTSTARTFAILOVERTARGET FOR EACH DATABASE TO ALL OTHER DATABASES IN DG CONFIG . - if m.Spec.FastStartFailOver.Enable { - for i := 0; i < len(databases); i++ { - out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("dgmgrl sys@%s \"EDIT DATABASE %s SET PROPERTY FASTSTARTFAILOVERTARGET=%s\"< admin.pwd", primaryConnectString, - strings.Split(databases[i], ":")[0], getFSFOTargets(i, databases))) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("SETTING FSFO TARGET OUTPUT") - log.Info(out) - - out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("dgmgrl sys@%s \"SHOW DATABASE %s FASTSTARTFAILOVERTARGET\" < admin.pwd", primaryConnectString, strings.Split(databases[i], ":")[0])) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("FSFO TARGETS OF " + databases[i]) - log.Info(out) - - } - } - // Remove admin pwd file - _, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, true, "bash", "-c", - dbcommons.RemoveAdminPasswordFile) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DB Admin pwd file removed") - - // Set DG Configured status to true for this standbyDatabase. so that in next reconcilation, we dont configure this again - standbyDatabase.Status.DgBrokerConfigured = true - r.Status().Update(ctx, standbyDatabase) - - return requeueN -} - -// ############################################################################# -// -// Remove a Database from DG Configuration -// -// ############################################################################# -// -//lint:ignore U1000 deferred for next release -func (r *DataguardBrokerReconciler) removeDatabaseFromDGConfig(m *dbapi.DataguardBroker, n *dbapi.SingleInstanceDatabase, standbyDatabase *dbapi.SingleInstanceDatabase, standbyDatabaseReadyPod corev1.Pod, sidbReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) ctrl.Result { - log := r.Log.WithValues("removeDataguardBrokerConfigurationForGivenDB", req.NamespacedName) - - if standbyDatabaseReadyPod.Name == "" || sidbReadyPod.Name == "" { - return requeueY - } - - // ## CHECK IF DG CONFIGURATION IS AVAILABLE IN PRIMARY DATABASE ## - out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | dgmgrl / as sysdba", dbcommons.DBShowConfigCMD)) - - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("Showconfiguration Output") - log.Info(out) - - if strings.Contains(out, "ORA-16525") { - log.Info("ORA-16525: The Oracle Data Guard broker is not yet available on Primary") - return requeueY - } - - // ## REMOVING STANDBY DATABASE FROM DG CONFIGURATION ## - _, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf(dbcommons.CreateDGMGRLScriptFile, dbcommons.RemoveStandbyDBFromDGConfgCMD)) - - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - - // ## SHOW CONFIGURATION - _, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | dgmgrl / as sysdba", dbcommons.DBShowConfigCMD)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("Showconfiguration Output") - log.Info(out) - // Set DG Configured status to false for this standbyDatabase. so that in next reconcilation, we dont configure this again - standbyDatabase.Status.DgBrokerConfigured = false - r.Status().Update(ctx, standbyDatabase) - - return requeueN -} - -// ############################################################################# -// -// Return FSFO targets of each StandbyDatabase -// Concatenation of all strings in databases slice expecting that of index 1 -// -// ############################################################################# -func getFSFOTargets(index int, databases []string) string { - fsfotargets := "" - for i := 0; i < len(databases); i++ { - if i != index { - splitstr := strings.Split(databases[i], ":") - if fsfotargets == "" { - fsfotargets = splitstr[0] - } else { - fsfotargets = fsfotargets + "," + splitstr[0] - } - } - } - return fsfotargets -} - -// ##################################################################################################### -// -// Switchovers to 'sid' db to make 'sid' db primary -// -// ##################################################################################################### -func (r *DataguardBrokerReconciler) SetAsPrimaryDatabase(sidbSid string, targetSid string, m *dbapi.DataguardBroker, n *dbapi.SingleInstanceDatabase, - adminPassword string, ctx context.Context, req ctrl.Request) ctrl.Result { - - log := r.Log.WithValues("SetAsPrimaryDatabase", req.NamespacedName) - if targetSid == "" { - log.Info("Specified sid is nil") - return requeueN - } - - // Fetch the SIDB Ready Pod - sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, n.Spec.Image.Version, - (n.Spec.Image.PullFrom), n.Name, n.Namespace, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - - // Fetch databases in dataguard broker configuration - databases, _, err := dbcommons.GetDatabasesInDgConfig(sidbReadyPod, r, r.Config, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - - dbInDgConfig := false - for i := 0; i < len(databases); i++ { - splitstr := strings.Split(databases[i], ":") - if strings.ToUpper(splitstr[0]) == strings.ToUpper(targetSid) { - dbInDgConfig = true - break - } - } - - if !dbInDgConfig { - eventReason := "Cannot Switchover" - eventMsg := fmt.Sprintf("Database %s not a part of the dataguard configuration", targetSid) - r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) - return requeueN - } - - // Fetch the current Primary database - primarySid := dbcommons.GetPrimaryDatabase(databases) - if strings.EqualFold(primarySid, targetSid) { - log.Info(targetSid + " is already Primary") - return requeueN - } - - m.Status.Status = dbcommons.StatusUpdating - r.Status().Update(ctx, m) - - found, _ := dbcommons.IsDatabaseFound(targetSid, databases, "") - if !found { - log.Info(targetSid + " not yet set in DG config") - return requeueY - } - - // Fetch the PrimarySid Ready Pod to create chk file - var primaryReq ctrl.Request - var primaryReadyPod corev1.Pod - if !strings.EqualFold(primarySid, sidbSid) { - primaryReq = ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: req.Namespace, - Name: n.Status.StandbyDatabases[strings.ToUpper(primarySid)], - }, - } - primaryReadyPod, _, _, _, err = dbcommons.FindPods(r, "", "", primaryReq.Name, primaryReq.Namespace, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - } else { - primaryReadyPod = sidbReadyPod - } - - // Fetch the targetSid Ready Pod to create chk file - var targetReq ctrl.Request - var targetReadyPod corev1.Pod - if !strings.EqualFold(targetSid, sidbSid) { - targetReq = ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: req.Namespace, - Name: n.Status.StandbyDatabases[strings.ToUpper(targetSid)], - }, - } - targetReadyPod, _, _, _, err = dbcommons.FindPods(r, "", "", targetReq.Name, targetReq.Namespace, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - } else { - targetReadyPod = sidbReadyPod - } - - // Create a chk File so that no other pods take the lock during Switchover . - out, err := dbcommons.ExecCommand(r, r.Config, primaryReadyPod.Name, primaryReadyPod.Namespace, "", ctx, req, false, "bash", "-c", dbcommons.CreateChkFileCMD) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("Successfully Created chk file " + out) - out, err = dbcommons.ExecCommand(r, r.Config, targetReadyPod.Name, targetReadyPod.Namespace, "", ctx, req, false, "bash", "-c", dbcommons.CreateChkFileCMD) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("Successfully Created chk file " + out) - - eventReason := "Waiting" - eventMsg := "Switchover In Progress" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - - // Connect to 'primarySid' db using dgmgrl and switchover to 'targetSid' db to make 'targetSid' db primary - _, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", - fmt.Sprintf(dbcommons.CreateAdminPasswordFile, adminPassword)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DB Admin pwd file created") - - out, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("dgmgrl sys@%s \"SWITCHOVER TO %s\" < admin.pwd", primarySid, targetSid)) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("SWITCHOVER TO " + targetSid + " Output") - log.Info(out) - - //Delete pwd file - _, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", - dbcommons.RemoveAdminPasswordFile) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("DB Admin pwd file removed") - - eventReason = "Success" - eventMsg = "Switchover Completed Successfully" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - - // Remove the chk File . - _, err = dbcommons.ExecCommand(r, r.Config, primaryReadyPod.Name, primaryReadyPod.Namespace, "", ctx, req, false, "bash", "-c", dbcommons.RemoveChkFileCMD) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - out, err = dbcommons.ExecCommand(r, r.Config, targetReadyPod.Name, targetReadyPod.Namespace, "", ctx, req, false, "bash", "-c", dbcommons.RemoveChkFileCMD) - if err != nil { - log.Error(err, err.Error()) - return requeueY - } - log.Info("Successfully Removed chk file " + out) - - // Update Databases - r.updateReconcileStatus(m, sidbReadyPod, ctx, req) - - // Update status of Primary true/false on 'primary' db (From which switchover initiated) - if !strings.EqualFold(primarySid, sidbSid) { - - standbyDatabase := &dbapi.SingleInstanceDatabase{} - err = r.Get(ctx, primaryReq.NamespacedName, standbyDatabase) - if err != nil { - return requeueN - } - out, err := dbcommons.GetDatabaseRole(primaryReadyPod, r, r.Config, ctx, primaryReq) - if err == nil { - standbyDatabase.Status.Role = strings.ToUpper(out) - } - r.Status().Update(ctx, standbyDatabase) - - } else { - sidbReq := ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: req.Namespace, - Name: n.Name, - }, - } - out, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, sidbReq) - if err == nil { - n.Status.Role = strings.ToUpper(out) - } - r.Status().Update(ctx, n) - } - - // Update status of Primary true/false on 'sid' db (To which switchover initiated) - if !strings.EqualFold(targetSid, sidbSid) { - - standbyDatabase := &dbapi.SingleInstanceDatabase{} - err = r.Get(ctx, targetReq.NamespacedName, standbyDatabase) - if err != nil { - return requeueN - } - out, err := dbcommons.GetDatabaseRole(targetReadyPod, r, r.Config, ctx, targetReq) - if err == nil { - standbyDatabase.Status.Role = strings.ToUpper(out) - } - r.Status().Update(ctx, standbyDatabase) - - } else { - sidbReq := ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: req.Namespace, - Name: n.Name, - }, - } - out, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, sidbReq) - if err == nil { - n.Status.Role = strings.ToUpper(out) - } - r.Status().Update(ctx, n) - } - - // Patch DataguardBroker Service to point selector to Current Primary Name and updates client db connection strings on dataguardBroker - result := r.patchService(m, sidbReadyPod, n, ctx, req) - if result.Requeue { - return result - } - - return requeueN -} - -// ############################################################################# -// -// Update Reconcile Status -// -// ############################################################################# -func (r *DataguardBrokerReconciler) updateReconcileStatus(m *dbapi.DataguardBroker, sidbReadyPod corev1.Pod, - ctx context.Context, req ctrl.Request) (err error) { - - // ConnectStrings updated in PatchService() - var databases []string - databases, _, err = dbcommons.GetDatabasesInDgConfig(sidbReadyPod, r, r.Config, ctx, req) - if err == nil { - primaryDatabase := "" - standbyDatabases := "" - for i := 0; i < len(databases); i++ { - splitstr := strings.Split(databases[i], ":") - if strings.ToUpper(splitstr[1]) == "PRIMARY" { - primaryDatabase = strings.ToUpper(splitstr[0]) - } - if strings.ToUpper(splitstr[1]) == "PHYSICAL_STANDBY" { - if standbyDatabases != "" { - standbyDatabases += "," + strings.ToUpper(splitstr[0]) - } else { - standbyDatabases = strings.ToUpper(splitstr[0]) - } - } - } - m.Status.PrimaryDatabase = primaryDatabase - m.Status.StandbyDatabases = standbyDatabases - } - - m.Status.PrimaryDatabaseRef = m.Spec.PrimaryDatabaseRef - m.Status.ProtectionMode = m.Spec.ProtectionMode - return -} - -// ############################################################################# -// -// Manage Finalizer to cleanup before deletion of DataguardBroker -// -// ############################################################################# -func (r *DataguardBrokerReconciler) manageDataguardBrokerDeletion(req ctrl.Request, ctx context.Context, m *dbapi.DataguardBroker) (ctrl.Result, error) { - log := r.Log.WithValues("manageDataguardBrokerDeletion", req.NamespacedName) - - // Check if the DataguardBroker instance is marked to be deleted, which is - // indicated by the deletion timestamp being set. - isDataguardBrokerMarkedToBeDeleted := m.GetDeletionTimestamp() != nil - if isDataguardBrokerMarkedToBeDeleted { - if controllerutil.ContainsFinalizer(m, dataguardBrokerFinalizer) { - // Run finalization logic for dataguardBrokerFinalizer. If the - // finalization logic fails, don't remove the finalizer so - // that we can retry during the next reconciliation. - result, err := r.cleanupDataguardBroker(req, ctx, m) - if result.Requeue { - return result, err - } - - // Remove dataguardBrokerFinalizer. Once all finalizers have been - // removed, the object will be deleted. - controllerutil.RemoveFinalizer(m, dataguardBrokerFinalizer) - err = r.Update(ctx, m) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err - } - } - return requeueY, errors.New("deletion pending") - } - - // Add finalizer for this CR - if !controllerutil.ContainsFinalizer(m, dataguardBrokerFinalizer) { - controllerutil.AddFinalizer(m, dataguardBrokerFinalizer) - err := r.Update(ctx, m) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err - } - } - return requeueN, nil -} - -// ############################################################################# -// -// Finalization logic for DataguardBrokerFinalizer -// -// ############################################################################# -func (r *DataguardBrokerReconciler) cleanupDataguardBroker(req ctrl.Request, ctx context.Context, m *dbapi.DataguardBroker) (ctrl.Result, error) { - log := r.Log.WithValues("cleanupDataguardBroker", req.NamespacedName) - - // Fetch Primary Database Reference - singleInstanceDatabase := &dbapi.SingleInstanceDatabase{} - err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: m.Spec.PrimaryDatabaseRef}, singleInstanceDatabase) - if err != nil { - if apierrors.IsNotFound(err) { - r.Log.Info("Resource deleted. No need to remove dataguard configuration") - return requeueN, nil - } - return requeueY, err - } - - // Validate if Primary Database Reference is ready - result, sidbReadyPod, _ := r.validateSidbReadiness(m, singleInstanceDatabase, ctx, req) - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, nil - } - - // Get Primary database to remove dataguard configuration - _, _, err = dbcommons.GetDatabasesInDgConfig(sidbReadyPod, r, r.Config, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err - } - - //primarySid := dbcommons.GetPrimaryDatabase(databases) - - out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | dgmgrl / as sysdba ", dbcommons.RemoveDataguardConfiguration)) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err - } - log.Info("RemoveDataguardConfiguration Output") - log.Info(out) - - for i := 0; i < len(m.Spec.StandbyDatabaseRefs); i++ { - - standbyDatabase := &dbapi.SingleInstanceDatabase{} - err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: m.Spec.StandbyDatabaseRefs[i]}, standbyDatabase) - if err != nil { - if apierrors.IsNotFound(err) { - continue - } - log.Error(err, err.Error()) - return requeueY, err - } - - // Set DgBrokerConfigured to false - standbyDatabase.Status.DgBrokerConfigured = false - r.Status().Update(ctx, standbyDatabase) - } - - singleInstanceDatabase.Status.DgBrokerConfigured = false - r.Status().Update(ctx, singleInstanceDatabase) - - log.Info("Successfully cleaned up Dataguard Broker") - return requeueN, nil -} - -// ############################################################################# -// -// SetupWithManager sets up the controller with the Manager -// -// ############################################################################# -func (r *DataguardBrokerReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&dbapi.DataguardBroker{}). - Owns(&corev1.Pod{}). //Watch for deleted pods of DataguardBroker Owner - WithEventFilter(dbcommons.ResourceEventHandler()). - WithOptions(controller.Options{MaxConcurrentReconciles: 100}). //ReconcileHandler is never invoked concurrently with the same object. - Complete(r) -} diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index a20fa1fd..a15dfe2d 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -1205,39 +1205,35 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns // // ############################################################################# func (r *SingleInstanceDatabaseReconciler) instantiateSVCSpec(m *dbapi.SingleInstanceDatabase, - svcName string, ports []corev1.ServicePort, svcType corev1.ServiceType) *corev1.Service { - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: svcName, - Namespace: m.Namespace, - Labels: map[string]string{ + svcName string, ports []corev1.ServicePort, svcType corev1.ServiceType, publishNotReadyAddress bool) *corev1.Service { + svc := dbcommons.NewRealServiceBuilder(). + SetName(svcName). + SetNamespace(m.Namespace). + SetLabels(func() map[string]string { + return map[string]string{ "app": m.Name, - }, - Annotations: func() map[string]string { - annotations := make(map[string]string) - if len(m.Spec.ServiceAnnotations) != 0 { - for key, value := range m.Spec.ServiceAnnotations { - annotations[key] = value - } + } + }()). + SetAnnotation(func() map[string]string { + annotations := make(map[string]string) + if len(m.Spec.ServiceAnnotations) != 0 { + for key, value := range m.Spec.ServiceAnnotations { + annotations[key] = value } - return annotations - }(), - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{}, - Selector: map[string]string{ + } + return annotations + }()). + SetPorts(ports). + SetSelector(func() map[string]string { + return map[string]string{ "app": m.Name, - }, - Type: svcType, - }, - } - svc.Spec.Ports = ports - // Set SingleInstanceDatabase instance as the owner and controller - ctrl.SetControllerReference(m, svc, r.Scheme) - return svc + } + }()). + SetPublishNotReadyAddresses(publishNotReadyAddress). + SetType(svcType). + Build() + ctrl.SetControllerReference(m, &svc, r.Scheme) + return &svc } // ############################################################################# @@ -1572,7 +1568,7 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplaceSVC(ctx context.Contex if getClusterSvcErr != nil && apierrors.IsNotFound(getClusterSvcErr) { // Create a new ClusterIP service ports := []corev1.ServicePort{{Name: "listener", Port: dbcommons.CONTAINER_LISTENER_PORT, Protocol: corev1.ProtocolTCP}} - svc := r.instantiateSVCSpec(m, clusterSvcName, ports, corev1.ServiceType("ClusterIP")) + svc := r.instantiateSVCSpec(m, clusterSvcName, ports, corev1.ServiceType("ClusterIP"), true) log.Info("Creating a new service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) err := r.Create(ctx, svc) if err != nil { @@ -1774,7 +1770,7 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplaceSVC(ctx context.Contex } // Create the service - svc := r.instantiateSVCSpec(m, extSvcName, ports, extSvcType) + svc := r.instantiateSVCSpec(m, extSvcName, ports, extSvcType, false) log.Info("Creating a new service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) err := r.Create(ctx, svc) if err != nil { diff --git a/controllers/dataguard/datagauard_errors.go b/controllers/dataguard/datagauard_errors.go new file mode 100644 index 00000000..94b2b0ea --- /dev/null +++ b/controllers/dataguard/datagauard_errors.go @@ -0,0 +1,47 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "errors" +) + +var ErrSidbWithMutipleReplicas error = errors.New("SingleInstanceDatabase with multiple replicas is not supported") +var ErrCurrentPrimaryDatabaseNotReady error = errors.New("current primary database not ready") +var ErrCurrentPrimaryDatabaseNotFound error = errors.New("current primary database not found") diff --git a/controllers/dataguard/dataguard_utils.go b/controllers/dataguard/dataguard_utils.go new file mode 100644 index 00000000..213e33d2 --- /dev/null +++ b/controllers/dataguard/dataguard_utils.go @@ -0,0 +1,1008 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" +) + +// ############################################################################################################### +// +// Clean up necessary resources required prior to dataguardbroker resource deletion +// +// ############################################################################################################### +func cleanupDataguardBroker(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, req ctrl.Request, ctx context.Context) error { + log := ctrllog.FromContext(ctx).WithValues("cleanupDataguardBroker", req.NamespacedName) + + log.Info(fmt.Sprintf("Cleaning for dataguard broker %v deletion", broker.Name)) + + // Fetch Primary Database Reference + var sidb dbapi.SingleInstanceDatabase + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: broker.GetCurrentPrimaryDatabase()}, &sidb); err != nil { + if apierrors.IsNotFound(err) { + log.Info(fmt.Sprintf("SingleInstanceDatabase %s deleted.", broker.GetCurrentPrimaryDatabase())) + return err + } + return err + } + + log.Info(fmt.Sprintf("The current primary database is %v", sidb.Name)) + + // Validate if Primary Database Reference is ready + if err := validateSidbReadiness(r, broker, &sidb, ctx, req); err != nil { + log.Info("Reconcile queued") + return err + } + + log.Info(fmt.Sprintf("The current primary database %v is ready and healthy", sidb.Name)) + + sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, sidb.Spec.Image.Version, + sidb.Spec.Image.PullFrom, sidb.Name, sidb.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return err + } + + log.Info(fmt.Sprintf("Ready pod for the sidb %v is %v", sidb.Name, sidbReadyPod.Name)) + + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | dgmgrl / as sysdba ", dbcommons.RemoveDataguardConfiguration)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("RemoveDataguardConfiguration Output") + log.Info(out) + + for _, databaseRef := range broker.Status.DatabasesInDataguardConfig { + + var standbyDatabase dbapi.SingleInstanceDatabase + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: databaseRef}, &standbyDatabase); err != nil { + if apierrors.IsNotFound(err) { + continue + } + log.Error(err, err.Error()) + return err + } + + // Set DgBrokerConfigured to false + standbyDatabase.Status.DgBrokerConfigured = false + if err := r.Status().Update(ctx, &standbyDatabase); err != nil { + r.Recorder.Eventf(&standbyDatabase, corev1.EventTypeWarning, "Updating Status", "DgBrokerConfigured status updation failed") + log.Info(fmt.Sprintf("Status updation for sidb %s failed", standbyDatabase.Name)) + return err + } + } + + log.Info("Successfully cleaned up Dataguard Broker") + return nil +} + +// ##################################################################################################### +// +// Validate readiness of the primary singleinstancedatabase specified +// +// ##################################################################################################### +func validateSidbReadiness(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, sidb *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) error { + + log := r.Log.WithValues("validateSidbReadiness", req.NamespacedName) + + var adminPassword string + var sidbReadyPod corev1.Pod + + // Check if current primary singleinstancedatabase is "ready" + if sidb.Status.Status != dbcommons.StatusReady { + return ErrCurrentPrimaryDatabaseNotReady + } + + // ## FETCH THE SIDB REPLICAS . + sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, sidb.Spec.Image.Version, + sidb.Spec.Image.PullFrom, sidb.Name, sidb.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return err + } + + log.Info(fmt.Sprintf("Ready pod for the singleInstanceDatabase %s is %s", sidb.Name, sidbReadyPod.Name)) + + // Validate databaseRef Admin Password + var adminPasswordSecret corev1.Secret + err = r.Get(ctx, types.NamespacedName{Name: sidb.Spec.AdminPassword.SecretName, Namespace: sidb.Namespace}, &adminPasswordSecret) + if err != nil { + if apierrors.IsNotFound(err) { + //m.Status.Status = dbcommons.StatusError + eventReason := "Waiting" + eventMsg := "waiting for secret : " + sidb.Spec.AdminPassword.SecretName + " to get created" + r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) + r.Log.Info("Secret " + sidb.Spec.AdminPassword.SecretName + " Not Found") + return fmt.Errorf("adminPassword secret for singleinstancedatabase %v not found", sidb.Name) + } + log.Error(err, err.Error()) + return err + } + adminPassword = string(adminPasswordSecret.Data[sidb.Spec.AdminPassword.SecretKey]) + + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | %s", fmt.Sprintf(dbcommons.ValidateAdminPassword, adminPassword), dbcommons.GetSqlClient(sidb.Spec.Edition))) + if err != nil { + log.Error(err, err.Error()) + return err + } + + if strings.Contains(out, "USER is \"SYS\"") { + log.Info("validated Admin password successfully") + } else if strings.Contains(out, "ORA-01017") { + //m.Status.Status = dbcommons.StatusError + eventReason := "Logon denied" + eventMsg := "invalid databaseRef admin password. secret: " + sidb.Spec.AdminPassword.SecretName + r.Recorder.Eventf(broker, corev1.EventTypeWarning, eventReason, eventMsg) + return fmt.Errorf("logon denied for singleinstancedatabase %v", sidb.Name) + } else { + return fmt.Errorf("%v", out) + } + + return nil +} + +// ############################################################################# +// +// Setup the requested dataguard Configuration +// +// ############################################################################# +func setupDataguardBrokerConfiguration(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, sidb *dbapi.SingleInstanceDatabase, + ctx context.Context, req ctrl.Request) error { + + log := r.Log.WithValues("setupDataguardBrokerConfiguration", req.NamespacedName) + + // Get sidb ready pod for current primary database + sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, sidb.Spec.Image.Version, + sidb.Spec.Image.PullFrom, sidb.Name, sidb.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return err + } + + for _, database := range broker.Spec.StandbyDatabaseRefs { + + // Get the standby database resource + var standbyDatabase dbapi.SingleInstanceDatabase + err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: database}, &standbyDatabase) + if err != nil { + if apierrors.IsNotFound(err) { + eventReason := "Warning" + eventMsg := database + "not found" + r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) + continue + } + log.Error(err, err.Error()) + return err + } + + // validate standby database status + if standbyDatabase.Status.Status != dbcommons.StatusReady { + eventReason := "Waiting" + eventMsg := "Waiting for " + standbyDatabase.Name + " to be Ready" + r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) + log.Info(fmt.Sprintf("single instance database %s not ready yet", standbyDatabase.Name)) + continue + } + + // Check if dataguard broker is already configured for the standby database + if standbyDatabase.Status.DgBrokerConfigured { + log.Info("Dataguard broker for standbyDatabase : " + standbyDatabase.Name + " is already configured") + continue + } + + // Check if dataguard broker already has a database with the same SID + _, ok := broker.Status.DatabasesInDataguardConfig[strings.ToUpper(standbyDatabase.Status.Sid)] + if ok { + log.Info("A database with the same SID is already configured in the DG") + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "Spec Error", "A database with the same SID "+standbyDatabase.Status.Sid+" is already configured in the DG") + continue + } + + broker.Status.Status = dbcommons.StatusCreating + r.Status().Update(ctx, broker) + + // ## FETCH THE STANDBY REPLICAS . + standbyDatabaseReadyPod, _, _, _, err := dbcommons.FindPods(r, sidb.Spec.Image.Version, + sidb.Spec.Image.PullFrom, standbyDatabase.Name, standbyDatabase.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return err + } + + var adminPasswordSecret corev1.Secret + if err := r.Get(ctx, types.NamespacedName{Name: sidb.Spec.AdminPassword.SecretName, Namespace: sidb.Namespace}, &adminPasswordSecret); err != nil { + return err + } + var adminPassword string = string(adminPasswordSecret.Data[sidb.Spec.AdminPassword.SecretKey]) + if err := setupDataguardBrokerConfigurationForGivenDB(r, broker, sidb, &standbyDatabase, standbyDatabaseReadyPod, sidbReadyPod, ctx, req, adminPassword); err != nil { + log.Error(err, fmt.Sprintf(" Error while setting up DG broker for the Database %v:%v", standbyDatabase.Status.Sid, standbyDatabase.Name)) + return err + } + if len(broker.Status.DatabasesInDataguardConfig) == 0 { + log.Info("DatabasesInDataguardConfig is nil") + broker.Status.DatabasesInDataguardConfig = make(map[string]string) + } + log.Info(fmt.Sprintf("adding %v:%v to the map", standbyDatabase.Status.Sid, standbyDatabase.Name)) + broker.Status.DatabasesInDataguardConfig[standbyDatabase.Status.Sid] = standbyDatabase.Name + r.Status().Update(ctx, broker) + // Update Databases + } + if len(broker.Status.DatabasesInDataguardConfig) == 0 { + broker.Status.DatabasesInDataguardConfig = make(map[string]string) + } + log.Info(fmt.Sprintf("adding primary database %v:%v to the map", sidb.Status.Sid, sidb.Name)) + broker.Status.DatabasesInDataguardConfig[sidb.Status.Sid] = sidb.Name + + eventReason := "DG Configuration up to date" + eventMsg := "" + + // Patch DataguardBroker Service to point selector to Current Primary Name + if err := patchService(r, broker, ctx, req); err != nil { + log.Error(err, err.Error()) + return err + } + + r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) + + return nil +} + +// ############################################################################# +// +// Set up dataguard Configuration for a given StandbyDatabase +// +// ############################################################################# +func setupDataguardBrokerConfigurationForGivenDB(r *DataguardBrokerReconciler, m *dbapi.DataguardBroker, n *dbapi.SingleInstanceDatabase, standbyDatabase *dbapi.SingleInstanceDatabase, + standbyDatabaseReadyPod corev1.Pod, sidbReadyPod corev1.Pod, ctx context.Context, req ctrl.Request, adminPassword string) error { + + log := r.Log.WithValues("setupDataguardBrokerConfigurationForGivenDB", req.NamespacedName) + + if standbyDatabaseReadyPod.Name == "" || sidbReadyPod.Name == "" { + return errors.New("no ready Pod for the singleinstancedatabase") + } + + // ## CHECK IF DG CONFIGURATION AVAILABLE IN PRIMARY DATABSE## + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | dgmgrl / as sysdba ", dbcommons.DBShowConfigCMD)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("ShowConfiguration Output") + log.Info(out) + + if strings.Contains(out, "ORA-16525") { + log.Info("ORA-16525: The Oracle Data Guard broker is not yet available on Primary") + return fmt.Errorf("ORA-16525: The Oracle Data Guard broker is not yet available on Primary database %v", n.Name) + } + + _, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf(dbcommons.CreateAdminPasswordFile, adminPassword)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DB Admin pwd file created") + + // ORA-16532: Oracle Data Guard broker configuration does not exist , so create one + if strings.Contains(out, "ORA-16532") { + if m.Spec.ProtectionMode == "MaxPerformance" { + // Construct the password file and dgbroker command file + out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf(dbcommons.CreateDGMGRLScriptFile, dbcommons.DataguardBrokerMaxPerformanceCMD)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DGMGRL command file creation output") + log.Info(out) + + // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAXPERFORMANCE ## + out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + "dgmgrl sys@${PRIMARY_DB_CONN_STR} @dgmgrl.cmd < admin.pwd && rm -rf dgmgrl.cmd") + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DgConfigurationMaxPerformance Output") + log.Info(out) + } else if m.Spec.ProtectionMode == "MaxAvailability" { + // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAX AVAILABILITY ## + out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf(dbcommons.CreateDGMGRLScriptFile, dbcommons.DataguardBrokerMaxAvailabilityCMD)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DGMGRL command file creation output") + log.Info(out) + + // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAXPERFORMANCE ## + out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + "dgmgrl sys@${PRIMARY_DB_CONN_STR} @dgmgrl.cmd < admin.pwd && rm -rf dgmgrl.cmd") + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DgConfigurationMaxAvailability Output") + log.Info(out) + } else { + log.Info("SPECIFY correct Protection Mode . Either MaxAvailability or MaxPerformance") + return err + } + + // ## SHOW CONFIGURATION DG + out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | dgmgrl / as sysdba ", dbcommons.DBShowConfigCMD)) + if err != nil { + log.Error(err, err.Error()) + return err + } else { + log.Info("ShowConfiguration Output") + log.Info(out) + } + // Set DG Configured status to true for this standbyDatabase and primary Database. so that in next reconcilation, we dont configure this again + n.Status.DgBrokerConfigured = true + standbyDatabase.Status.DgBrokerConfigured = true + r.Status().Update(ctx, standbyDatabase) + r.Status().Update(ctx, n) + // Remove admin pwd file + _, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + dbcommons.RemoveAdminPasswordFile) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DB Admin pwd file removed") + + return err + } + + // DG Configuration Exists . So add the standbyDatabase to the existing DG Configuration + databases, err := GetDatabasesInDataGuardConfigurationWithRole(r, m, ctx, req) + if err != nil { + log.Info("Error while setting up the dataguard configuration") + log.Error(err, err.Error()) + return err + } + + // ## ADD DATABASE TO DG CONFIG , IF NOT PRESENT + found, _ := dbcommons.IsDatabaseFound(standbyDatabase.Spec.Sid, databases, "") + if found { + return err + } + primarySid := dbcommons.GetPrimaryDatabase(databases) + + // If user adds a new standby to a dg config when failover happened to one ot the standbys, we need to have current primary connect string + primaryConnectString := n.Name + ":1521/" + primarySid + if !strings.EqualFold(primarySid, n.Spec.Sid) { + primaryConnectString = m.Status.DatabasesInDataguardConfig[strings.ToUpper(primarySid)] + ":1521/" + primarySid + } + + if m.Spec.ProtectionMode == "MaxPerformance" { + // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAXPERFORMANCE ## + out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf(dbcommons.CreateDGMGRLScriptFile, dbcommons.DataguardBrokerAddDBMaxPerformanceCMD)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DGMGRL command file creation output") + log.Info(out) + + out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("dgmgrl sys@%s @dgmgrl.cmd < admin.pwd && rm -rf dgmgrl.cmd ", primaryConnectString)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DgConfigurationMaxPerformance Output") + log.Info(out) + + } else if m.Spec.ProtectionMode == "MaxAvailability" { + // ## DG CONFIGURATION FOR PRIMARY DB || MODE : MAX AVAILABILITY ## + out, err := dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf(dbcommons.CreateDGMGRLScriptFile, dbcommons.DataguardBrokerAddDBMaxAvailabilityCMD)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DGMGRL command file creation output") + log.Info(out) + + out, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("dgmgrl sys@%s @dgmgrl.cmd < admin.pwd && rm -rf dgmgrl.cmd ", primaryConnectString)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DgConfigurationMaxAvailability Output") + log.Info(out) + + } else { + log.Info("SPECIFY correct Protection Mode . Either MaxAvailability or MaxPerformance") + log.Error(err, err.Error()) + return err + } + + // Remove admin pwd file + _, err = dbcommons.ExecCommand(r, r.Config, standbyDatabaseReadyPod.Name, standbyDatabaseReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + dbcommons.RemoveAdminPasswordFile) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("DB Admin pwd file removed") + + // Set DG Configured status to true for this standbyDatabase. so that in next reconcilation, we dont configure this again + standbyDatabase.Status.DgBrokerConfigured = true + r.Status().Update(ctx, standbyDatabase) + + return nil +} + +// ########################################################################################################### +// +// Patch the service for dataguardbroker resource to point selector to current Primary Name +// +// ########################################################################################################### +func patchService(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) error { + log := r.Log.WithValues("patchService", req.NamespacedName) + + primaryDatabaseRef := broker.Status.DatabasesInDataguardConfig[broker.Status.PrimaryDatabase] + var svc *corev1.Service = &corev1.Service{} + + // fetch the k8s service for the dataguardbroker resource + err := r.Get(ctx, types.NamespacedName{Name: req.Name, Namespace: req.Namespace}, svc) + if err != nil { + return err + } + + log.Info(fmt.Sprintf("Patching Service %s to point to the currPrimaryDatabase %s", svc.Name, primaryDatabaseRef)) + + // updating service selector for the primary database pod to attach itself to the service + svc.Spec.Selector["app"] = primaryDatabaseRef + if err = r.Update(ctx, svc); err != nil { + return err + } + log.Info(fmt.Sprintf("Patching service %s successful ", svc.Name)) + + // updating the dataguardbroker resource connect strings + broker.Status.ClusterConnectString = svc.Name + "." + svc.Namespace + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/DATAGUARD" + if broker.Spec.LoadBalancer { + if len(svc.Status.LoadBalancer.Ingress) > 0 { + lbAddress := svc.Status.LoadBalancer.Ingress[0].Hostname + if lbAddress == "" { + lbAddress = svc.Status.LoadBalancer.Ingress[0].IP + } + broker.Status.ExternalConnectString = lbAddress + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/DATAGUARD" + } + } else { + nodeip := dbcommons.GetNodeIp(r, ctx, req) + if nodeip != "" { + broker.Status.ExternalConnectString = nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/DATAGUARD" + } + } + log.Info("Updated connect strings to the dataguard broker") + return nil +} + +// ########################################################################################################### +// +// Update Reconcile Status +// +// ########################################################################################################### +func updateReconcileStatus(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) (err error) { + + log := r.Log.WithValues("updateReconcileStatus", req.NamespacedName) + + // fetch the singleinstancedatabase (database sid) and their role in the dataguard configuration + var databases []string + databases, err = GetDatabasesInDataGuardConfigurationWithRole(r, broker, ctx, req) + if err != nil { + log.Info("Problem when retrieving the databases in dg config") + broker.Status.Status = dbcommons.StatusNotReady + r.Status().Update(ctx, broker) + return nil + } + + // loop over all the databases to update the status of the dataguardbroker and the singleinstancedatabase + var standbyDatabases string = "" + for i := 0; i < len(databases); i++ { + splitstr := strings.Split(databases[i], ":") + database := strings.ToUpper(splitstr[0]) + var singleInstanceDatabase dbapi.SingleInstanceDatabase + err := r.Get(ctx, types.NamespacedName{Name: broker.Status.DatabasesInDataguardConfig[database], Namespace: req.Namespace}, &singleInstanceDatabase) + if err != nil { + return err + } + log.Info(fmt.Sprintf("Checking current role of %v is %v and its status is %v", broker.Status.DatabasesInDataguardConfig[database], strings.ToUpper(splitstr[1]), singleInstanceDatabase.Status.Role)) + if singleInstanceDatabase.Status.Role != strings.ToUpper(splitstr[1]) { + singleInstanceDatabase.Status.Role = strings.ToUpper(splitstr[1]) + r.Status().Update(ctx, &singleInstanceDatabase) + } + if strings.ToUpper(splitstr[1]) == "PRIMARY" && strings.ToUpper(database) != strings.ToUpper(broker.Status.PrimaryDatabase) { + log.Info("primary Database is " + strings.ToUpper(database)) + broker.Status.PrimaryDatabase = strings.ToUpper(database) + // patch the service with the current primary + } + if strings.ToUpper(splitstr[1]) == "PHYSICAL_STANDBY" { + if standbyDatabases != "" { + standbyDatabases += "," + strings.ToUpper(splitstr[0]) + } else { + standbyDatabases = strings.ToUpper(splitstr[0]) + } + } + } + + broker.Status.StandbyDatabases = standbyDatabases + broker.Status.ProtectionMode = broker.Spec.ProtectionMode + r.Status().Update(ctx, broker) + + // patch the dataguardbroker resource service + if err := patchService(r, broker, ctx, req); err != nil { + return err + } + + return nil +} + +// ##################################################################################################### +// +// Get the avail FSFO targets for a given singleinstancedatabase sid +// +// ##################################################################################################### +func GetFSFOTargets(databaseSid string, databasesInDgConfig map[string]string) (string, error) { + if _, ok := databasesInDgConfig[databaseSid]; !ok { + return "", fmt.Errorf("database %s not in dataguard config", databasesInDgConfig[databaseSid]) + } + var fsfoTarget []string + for dbSid, _ := range databasesInDgConfig { + if strings.Compare(databaseSid, dbSid) != 0 { + fsfoTarget = append(fsfoTarget, dbSid) + } + } + return strings.Join(fsfoTarget, ","), nil +} + +// ##################################################################################################### +// +// Set faststartfailover targets accordingly to dataguard configuration +// +// ##################################################################################################### +func setFSFOTargets(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) error { + + log := r.Log.WithValues("setFSFOTargets", req.NamespacedName) + + // fetch the current primary singleinstancedatabase + var currentPrimaryDatabase dbapi.SingleInstanceDatabase + err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: broker.GetCurrentPrimaryDatabase()}, ¤tPrimaryDatabase) + if err != nil { + if apierrors.IsNotFound(err) { + r.Log.Info("Resource not found") + return nil + } + r.Log.Error(err, err.Error()) + return err + } + + log.Info(fmt.Sprintf("current primary database for the dg config is %s", currentPrimaryDatabase.Name)) + + // fetch the singleinstancedatabase ready pod + sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, currentPrimaryDatabase.Spec.Image.Version, + currentPrimaryDatabase.Spec.Image.PullFrom, currentPrimaryDatabase.Name, currentPrimaryDatabase.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return fmt.Errorf("error while fetching ready pod for %s", currentPrimaryDatabase.Name) + } + + log.Info(fmt.Sprintf("current primary database ready pod is %s", sidbReadyPod.Name)) + + // fetch singleinstancedatabase admin password + var adminPasswordSecret corev1.Secret + if err = r.Get(ctx, types.NamespacedName{Name: currentPrimaryDatabase.Spec.AdminPassword.SecretName, Namespace: currentPrimaryDatabase.Namespace}, &adminPasswordSecret); err != nil { + if apierrors.IsNotFound(err) { + //m.Status.Status = dbcommons.StatusError + eventReason := "Waiting" + eventMsg := "waiting for secret : " + currentPrimaryDatabase.Spec.AdminPassword.SecretName + " to get created" + r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) + r.Log.Info("Secret " + currentPrimaryDatabase.Spec.AdminPassword.SecretName + " Not Found") + return errors.New("admin password secret not found") + } + log.Error(err, err.Error()) + return err + } + adminPassword := string(adminPasswordSecret.Data[currentPrimaryDatabase.Spec.AdminPassword.SecretKey]) + + for databaseSid, databaseRef := range broker.Status.DatabasesInDataguardConfig { + // construct FSFO target for this database + fsfoTargets, err := GetFSFOTargets(databaseSid, broker.Status.DatabasesInDataguardConfig) + if err != nil { + return err + } + log.Info(fmt.Sprintf("Setting fast start failover target for the database %s to %s", databaseRef, fsfoTargets)) + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"EDIT DATABASE %s SET PROPERTY FASTSTARTFAILOVERTARGET=%s \" | dgmgrl sys/%s@%s ", + databaseSid, fsfoTargets, adminPassword, currentPrimaryDatabase.Status.Sid)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("SETTING FSFO TARGET OUTPUT") + log.Info(out) + + out, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"SHOW DATABASE %s FASTSTARTFAILOVERTARGET \" | dgmgrl sys/%s@%s ", databaseSid, adminPassword, currentPrimaryDatabase.Status.Sid)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("FSFO TARGETS OF " + databaseSid) + log.Info(out) + } + + // Set FSFO Targets according to the input yaml of broker + return nil +} + +// ############################################################################# +// +// Setup the requested dataguard configuration +// +// ############################################################################# +func createObserverPods(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) error { + + log := r.Log.WithValues("createObserverPods", req.NamespacedName) + + // fetch the current primary singleinstancedatabase resourcce + var currPrimaryDatabase dbapi.SingleInstanceDatabase + namespacedName := types.NamespacedName{ + Namespace: broker.Namespace, + Name: broker.GetCurrentPrimaryDatabase(), + } + if err := r.Get(ctx, namespacedName, &currPrimaryDatabase); err != nil { + if apierrors.IsNotFound(err) { + broker.Status.Status = dbcommons.StatusError + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "SingleInstanceDatabase Not Found", fmt.Sprintf("SingleInstanceDatabase %s not found", namespacedName.Name)) + r.Log.Info(fmt.Sprintf("singleinstancedatabase %s not found", namespacedName.Name)) + return ErrCurrentPrimaryDatabaseNotFound + } + return err + } + + // fetch the dataguardbroker observer replicas + _, brokerReplicasFound, _, _, err := dbcommons.FindPods(r, "", "", broker.Name, broker.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return err + } + + if brokerReplicasFound > 0 { + return nil + } + + // instantiate observer pod specification + pod := dbcommons.NewRealPodBuilder(). + SetNamespacedName(types.NamespacedName{ + Name: broker.Name + "-" + dbcommons.GenerateRandomString(5), + Namespace: broker.Namespace, + }). + SetLabels(map[string]string{ + "app": broker.Name, + "version": currPrimaryDatabase.Spec.Image.PullSecrets, + }). + SetTerminationGracePeriodSeconds(int64(30)). + SetNodeSelector(func() map[string]string { + var nsRule map[string]string = map[string]string{} + if len(broker.Spec.NodeSelector) != 0 { + for key, value := range broker.Spec.NodeSelector { + nsRule[key] = value + } + } + return nsRule + }()). + SetSecurityContext(corev1.PodSecurityContext{ + RunAsUser: func() *int64 { i := int64(54321); return &i }(), + FSGroup: func() *int64 { i := int64(54321); return &i }(), + }). + SetImagePullSecrets(currPrimaryDatabase.Spec.Image.PullSecrets). + AppendContainers(corev1.Container{ + Name: broker.Name, + Image: currPrimaryDatabase.Spec.Image.PullFrom, + Lifecycle: &corev1.Lifecycle{ + PreStop: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"/bin/sh", "-c", "/bin/echo -en 'shutdown abort;\n' | env ORACLE_SID=${ORACLE_SID^^} sqlplus -S / as sysdba"}, + }, + }, + }, + ImagePullPolicy: corev1.PullAlways, + Ports: []corev1.ContainerPort{{ContainerPort: 1521}, {ContainerPort: 5500}}, + + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"/bin/sh", "-c", "$ORACLE_BASE/checkDBLockStatus.sh"}, + }, + }, + InitialDelaySeconds: 20, + TimeoutSeconds: 20, + PeriodSeconds: 40, + }, + Env: []corev1.EnvVar{ + { + Name: "SVC_HOST", + Value: broker.Name, + }, + { + Name: "SVC_PORT", + Value: "1521", + }, + { + Name: "PRIMARY_DB_CONN_STR", + Value: currPrimaryDatabase.Name + ":1521/" + currPrimaryDatabase.Spec.Sid, + }, + { + Name: "DG_OBSERVER_ONLY", + Value: "true", + }, + { + Name: "DG_OBSERVER_NAME", + Value: broker.Name, + }, + { + // Sid used here only for Locking mechanism to work . + Name: "ORACLE_SID", + Value: "OBSRVR" + strings.ToUpper(currPrimaryDatabase.Spec.Sid), + }, + { + Name: "ORACLE_PWD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: currPrimaryDatabase.Spec.AdminPassword.SecretName, + }, + Key: currPrimaryDatabase.Spec.AdminPassword.SecretKey, + }, + }, + }, + }, + }). + Build() + + // set the ownership and lifecyle of the observer pod to the dataguardbroker resource + ctrl.SetControllerReference(broker, &pod, r.Scheme) + + log.Info("Creating a new POD", "POD.Namespace", pod.Namespace, "POD.Name", pod.Name) + if err = r.Create(ctx, &pod); err != nil { + log.Error(err, "Failed to create new POD", "pod.Namespace", pod.Namespace, "POD.Name", pod.Name) + return err + } + + // Waiting for Pod to get created as sometimes it takes some time to create a Pod . 30 seconds TImeout + timeout := 30 + err = dbcommons.WaitForStatusChange(r, pod.Name, broker.Namespace, ctx, req, time.Duration(timeout)*time.Second, "pod", "creation") + if err != nil { + log.Error(err, "Error in Waiting for Pod status for Creation", "pod.Namespace", pod.Namespace, "POD.Name", pod.Name) + return err + } + log.Info("Succesfully Created New Pod ", "POD.NAME : ", pod.Name) + + eventReason := "SUCCESS" + eventMsg := "" + r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) + + return nil +} + +// ############################################################################# +// +// Enable faststartfailover for the dataguard configuration +// +// ############################################################################# +func enableFSFOForDgConfig(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) error { + + log := r.Log.WithValues("enableFSFOForDgConfig", req.NamespacedName) + + // Get the current primary singleinstancedatabase resourcce + var sidb dbapi.SingleInstanceDatabase + namespacedName := types.NamespacedName{ + Namespace: broker.Namespace, + Name: broker.GetCurrentPrimaryDatabase(), + } + if err := r.Get(ctx, namespacedName, &sidb); err != nil { + if apierrors.IsNotFound(err) { + broker.Status.Status = dbcommons.StatusError + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "SingleInstanceDatabase Not Found", fmt.Sprintf("SingleInstanceDatabase %s not found", sidb.Name)) + log.Info(fmt.Sprintf("singleinstancedatabase %s not found", namespacedName.Name)) + return ErrCurrentPrimaryDatabaseNotFound + } + return err + } + + // fetch singleinstancedatabase ready pod + sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, sidb.Spec.Image.Version, + sidb.Spec.Image.PullFrom, sidb.Name, sidb.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return err + } + + // fetch singleinstancedatabase adminpassword secret + var adminPasswordSecret corev1.Secret + if err := r.Get(ctx, types.NamespacedName{Name: sidb.Spec.AdminPassword.SecretName, Namespace: sidb.Namespace}, &adminPasswordSecret); err != nil { + return err + } + var adminPassword string = string(adminPasswordSecret.Data[sidb.Spec.AdminPassword.SecretKey]) + + r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Enabling FastStartFailover", fmt.Sprintf("Enabling FastStartFailover for the dataguard broker %s", broker.Name)) + log.Info(fmt.Sprintf("Enabling FastStartFailover for the dataguard broker %s", broker.Name)) + + // enable faststartfailover for the dataguard configuration + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | dgmgrl sys/%s@%s ", dbcommons.EnableFSFOCMD, adminPassword, sidb.Status.Sid)) + if err != nil { + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "Enabling FastStartFailover failed", fmt.Sprintf("Enabling FastStartFailover for the dataguard broker %s failed", broker.Name)) + log.Error(err, err.Error()) + return err + } + log.Info("EnableFastStartFailover Output") + log.Info(out) + + r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Enabling FastStartFailover successful", fmt.Sprintf("Enabling FastStartFailover for the dataguard broker %s successful", broker.Name)) + + return nil +} + +// ############################################################################# +// +// Disable faststartfailover for the dataguard configuration +// +// ############################################################################# +func disableFSFOForDGConfig(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) error { + + log := r.Log.WithValues("disableFSFOForDGConfig", req.NamespacedName) + + // Get the current primary singleinstancedatabase resource + var sidb dbapi.SingleInstanceDatabase + namespacedName := types.NamespacedName{ + Namespace: broker.Namespace, + Name: broker.GetCurrentPrimaryDatabase(), + } + if err := r.Get(ctx, namespacedName, &sidb); err != nil { + if apierrors.IsNotFound(err) { + broker.Status.Status = dbcommons.StatusError + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "SingleInstanceDatabase Not Found", fmt.Sprintf("SingleInstanceDatabase %s not found", sidb.Name)) + log.Info(fmt.Sprintf("singleinstancedatabase %s not found", namespacedName.Name)) + return ErrCurrentPrimaryDatabaseNotFound + } + return err + } + + // fetch singleinstancedatabase ready pod + sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, sidb.Spec.Image.Version, + sidb.Spec.Image.PullFrom, sidb.Name, sidb.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return err + } + + // fetch admin password for the singleinstancedatabase + var adminPasswordSecret corev1.Secret + if err := r.Get(ctx, types.NamespacedName{Name: sidb.Spec.AdminPassword.SecretName, Namespace: sidb.Namespace}, &adminPasswordSecret); err != nil { + return err + } + var adminPassword string = string(adminPasswordSecret.Data[sidb.Spec.AdminPassword.SecretKey]) + + r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Disabling FastStartFailover", fmt.Sprintf("Disabling FastStartFailover for the dataguard broker %s", broker.Name)) + log.Info(fmt.Sprintf("Disabling FastStartFailover for the dataguard broker %s", broker.Name)) + + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | dgmgrl sys/%s@%s ", fmt.Sprintf(dbcommons.DisableFSFOCMD, broker.Name), adminPassword, sidb.Status.Sid)) + if err != nil { + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "Disabling FastStartFailover failed", fmt.Sprintf("Disabling FastStartFailover for the dataguard broker %s failed", broker.Name)) + log.Error(err, err.Error()) + return err + } + log.Info("DisableFastStartFailover Output") + log.Info(out) + + r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Disabling FastStartFailover", "faststartfailover disabled successfully") + log.Info("faststartfailover disabled successfully") + + return nil +} + +// ############################################################################# +// +// Get databases in dataguard configuration along with their roles +// +// ############################################################################# +func GetDatabasesInDataGuardConfigurationWithRole(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) ([]string, error) { + r.Log.Info(fmt.Sprintf("GetDatabasesInDataGuardConfiguration are %v", broker.GetDatabasesInDataGuardConfiguration())) + for _, database := range broker.GetDatabasesInDataGuardConfiguration() { + + var singleInstanceDatabase dbapi.SingleInstanceDatabase + if err := r.Get(context.TODO(), types.NamespacedName{Namespace: broker.Namespace, Name: database}, &singleInstanceDatabase); err != nil { + // log about the error while fetching the database + continue + } + + // Fetch the primary database ready pod + sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, singleInstanceDatabase.Spec.Image.Version, + singleInstanceDatabase.Spec.Image.PullFrom, singleInstanceDatabase.Name, singleInstanceDatabase.Namespace, ctx, req) + if err != nil || sidbReadyPod.Name == "" { + continue + } + + // try out + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | sqlplus -s / as sysdba ", dbcommons.DataguardBrokerGetDatabaseCMD)) + if err != nil || strings.Contains(out, "no rows selected") && strings.Contains(out, "ORA-") { + continue + } + + r.Log.Info(fmt.Sprintf("sidbReadyPod is %v \n output of the exec is %v \n and output contains ORA- is %v", sidbReadyPod.Name, out, strings.Contains(out, "ORA-"))) + + out1 := strings.Replace(out, " ", "_", -1) + // filtering output and storing databses in dg configuration in "databases" slice + databases := strings.Fields(out1) + + // first 2 values in the slice will be column name(DATABASES) and a seperator(--------------) . so take the slice from position [2:] + databases = databases[2:] + return databases, nil + } + + return []string{}, errors.New("cannot get databases in dataguard configuration") +} diff --git a/controllers/dataguard/dataguardbroker_controller.go b/controllers/dataguard/dataguardbroker_controller.go new file mode 100644 index 00000000..8090fc67 --- /dev/null +++ b/controllers/dataguard/dataguardbroker_controller.go @@ -0,0 +1,497 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/go-logr/logr" + dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +// DataguardBrokerReconciler reconciles a DataguardBroker object +type DataguardBrokerReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + Config *rest.Config + Recorder record.EventRecorder +} + +const dataguardBrokerFinalizer = "database.oracle.com/dataguardbrokerfinalizer" + +//+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=database.oracle.com,resources=dataguardbrokers/finalizers,verbs=update +//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;persistentvolumeclaims;services,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch + +func (r *DataguardBrokerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + log := r.Log.WithValues("reconciler", req.NamespacedName) + + log.Info("Reconcile requested") + + // Get the dataguardbroker resource if already exists + var dataguardBroker dbapi.DataguardBroker + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, &dataguardBroker); err != nil { + if apierrors.IsNotFound(err) { + log.Info("Resource deleted") + return ctrl.Result{Requeue: false}, nil + } + return ctrl.Result{Requeue: false}, err + } + + // Manage dataguardbroker deletion + if !dataguardBroker.DeletionTimestamp.IsZero() { + return r.manageDataguardBrokerDeletion(&dataguardBroker, ctx, req) + } + + // initialize the dataguardbroker resource status + if dataguardBroker.Status.Status == "" { + r.Recorder.Eventf(&dataguardBroker, corev1.EventTypeNormal, "Status Initialization", "initializing status fields for the resource") + log.Info("Initializing status fields") + dataguardBroker.Status.Status = dbcommons.StatusCreating + dataguardBroker.Status.ExternalConnectString = dbcommons.ValueUnavailable + dataguardBroker.Status.ClusterConnectString = dbcommons.ValueUnavailable + dataguardBroker.Status.FastStartFailover = false + if len(dataguardBroker.Status.DatabasesInDataguardConfig) == 0 { + dataguardBroker.Status.DatabasesInDataguardConfig = map[string]string{} + } + } + + // Always refresh status before a reconcile + defer r.Status().Update(ctx, &dataguardBroker) + + // Mange DataguardBroker Creation + result, err := r.manageDataguardBrokerCreation(&dataguardBroker, ctx, req) + if err != nil { + return ctrl.Result{Requeue: false}, err + } + if result.Requeue { + return result, nil + } + + // manage enabling and disabling faststartfailover + if dataguardBroker.Spec.FastStartFailover != dataguardBroker.Status.FastStartFailover { + if dataguardBroker.Spec.FastStartFailover { + + // set faststartfailover targets for all the singleinstancedatabases in the dataguard configuration + if err := setFSFOTargets(r, &dataguardBroker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } + + // enable faststartfailover in the dataguard configuration + if err := enableFSFOForDgConfig(r, &dataguardBroker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } + + // create Observer Pod + if err := createObserverPods(r, &dataguardBroker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } + + // set faststartfailover status to true + dataguardBroker.Status.FastStartFailover = true + + } else { + + // disable faststartfailover + if err := disableFSFOForDGConfig(r, &dataguardBroker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } + + // delete Observer Pod + observerReadyPod, _, _, _, err := dbcommons.FindPods(r, "", "", dataguardBroker.Name, dataguardBroker.Namespace, ctx, req) + if err != nil { + return ctrl.Result{Requeue: false}, err + } + if observerReadyPod.Name != "" { + if err := r.Delete(ctx, &observerReadyPod); err != nil { + return ctrl.Result{Requeue: false}, err + } + } + + r.Recorder.Eventf(&dataguardBroker, corev1.EventTypeNormal, "Observer Deleted", "database observer pod deleted") + log.Info("database observer deleted") + + // set faststartfailover status to false + dataguardBroker.Status.FastStartFailover = false + } + } + + // manage manual switchover + if dataguardBroker.Spec.SetAsPrimaryDatabase != "" && dataguardBroker.Spec.SetAsPrimaryDatabase != dataguardBroker.Status.PrimaryDatabase { + if _, ok := dataguardBroker.Status.DatabasesInDataguardConfig[dataguardBroker.Spec.SetAsPrimaryDatabase]; !ok { + r.Recorder.Eventf(&dataguardBroker, corev1.EventTypeWarning, "Cannot Switchover", fmt.Sprintf("database with SID %v not found in dataguardbroker configuration", dataguardBroker.Spec.SetAsPrimaryDatabase)) + log.Info(fmt.Sprintf("cannot perform switchover, database with SID %v not found in dataguardbroker configuration", dataguardBroker.Spec.SetAsPrimaryDatabase)) + return ctrl.Result{Requeue: false}, nil + } + r.Recorder.Eventf(&dataguardBroker, corev1.EventTypeWarning, "Manual Switchover", fmt.Sprintf("Switching over to %s database", dataguardBroker.Status.DatabasesInDataguardConfig[dataguardBroker.Spec.SetAsPrimaryDatabase])) + log.Info(fmt.Sprintf("switching over to %s database", dataguardBroker.Status.DatabasesInDataguardConfig[dataguardBroker.Spec.SetAsPrimaryDatabase])) + result, err := r.manageManualSwitchOver(dataguardBroker.Spec.SetAsPrimaryDatabase, &dataguardBroker, ctx, req) + if err != nil { + return ctrl.Result{Requeue: false}, err + } + if result.Requeue { + return result, nil + } + } + + // Update Status for broker and sidb resources + if err := updateReconcileStatus(r, &dataguardBroker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } + + dataguardBroker.Status.Status = dbcommons.StatusReady + log.Info("Reconcile Completed") + + if dataguardBroker.Spec.FastStartFailover { + return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, nil + } else { + return ctrl.Result{Requeue: false}, nil + } +} + +// ############################################################################################################################# +// +// Manage deletion and clean up of the dataguardBroker resource +// +// ############################################################################################################################# +func (r *DataguardBrokerReconciler) manageDataguardBrokerDeletion(broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + log := r.Log.WithValues("manageDataguardBrokerDeletion", req.NamespacedName) + + log.Info(fmt.Sprintf("Deleting dataguard broker %v", broker.Name)) + // Check if the DataguardBroker instance is marked to be deleted, which is + // indicated by the deletion timestamp being set. + if controllerutil.ContainsFinalizer(broker, dataguardBrokerFinalizer) { + // Run finalization logic for dataguardBrokerFinalizer. If the + // finalization logic fails, don't remove the finalizer so + // that we can retry during the next reconciliation. + if err := cleanupDataguardBroker(r, broker, req, ctx); err != nil { + // handle the errors + return ctrl.Result{Requeue: false}, err + } + + // Remove dataguardBrokerFinalizer. Once all finalizers have been + // removed, the object will be deleted. + controllerutil.RemoveFinalizer(broker, dataguardBrokerFinalizer) + if err := r.Update(ctx, broker); err != nil { + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "Updating Resource", "Error while removing resource finalizers") + log.Info("Error while removing resource finalizers") + return ctrl.Result{Requeue: false}, err + } + } + return ctrl.Result{Requeue: false}, nil +} + +// ############################################################################################################################# +// +// Manage validation of singleinstancedatabases and creation of the dataguard configuration +// +// ############################################################################################################################# +func (r *DataguardBrokerReconciler) manageDataguardBrokerCreation(broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + log := r.Log.WithValues("manageDataguardBrokerCreation", req.NamespacedName) + + // Add finalizer for this dataguardbroker resource + if !controllerutil.ContainsFinalizer(broker, dataguardBrokerFinalizer) { + r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Updating Resource", "Adding finalizers") + log.Info("Adding finalizer") + controllerutil.AddFinalizer(broker, dataguardBrokerFinalizer) + if err := r.Update(ctx, broker); err != nil { + return ctrl.Result{Requeue: false}, err + } + } + + // Check if a service for the dataguardbroker resources exists + var service corev1.Service + if err := r.Get(context.TODO(), types.NamespacedName{Name: broker.Name, Namespace: broker.Namespace}, &service); err != nil { + // check if the required service is not found then create the service + if apierrors.IsNotFound(err) { + r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Creating Service", "creating service for the resource") + log.Info("creating service for the dataguardbroker resource") + + // instantiate the service specification + svc := dbcommons.NewRealServiceBuilder(). + SetName(broker.Name). + SetNamespace(broker.Namespace). + SetLabels(map[string]string{ + "app": broker.Name, + }). + SetAnnotation(func() map[string]string { + annotations := make(map[string]string) + if len(broker.Spec.ServiceAnnotations) != 0 { + for key, value := range broker.Spec.ServiceAnnotations { + annotations[key] = value + } + } + return annotations + }()). + SetPorts([]corev1.ServicePort{ + { + Name: "listener", + Port: 1521, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "xmldb", + Port: 5500, + Protocol: corev1.ProtocolTCP, + }, + }). + SetSelector(map[string]string{ + "app": broker.Name, + }). + SetType(func() corev1.ServiceType { + if broker.Spec.LoadBalancer { + return corev1.ServiceType("LoadBalancer") + } + return corev1.ServiceType("NodePort") + }()). + Build() + + // Set the ownership of the service object to the dataguard broker resource object + ctrl.SetControllerReference(broker, &svc, r.Scheme) + + // create the service for dataguardbroker resource + if err = r.Create(ctx, &svc); err != nil { + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "Service Creation", "service creation failed") + log.Info("service creation failed") + return ctrl.Result{Requeue: false}, err + } else { + timeout := 30 + // Waiting for Service to get created as sometimes it takes some time to create a service . 30 seconds TImeout + err = dbcommons.WaitForStatusChange(r, svc.Name, broker.Namespace, ctx, req, time.Duration(timeout)*time.Second, "svc", "creation") + if err != nil { + log.Error(err, "Error in Waiting for svc status for Creation", "svc.Namespace", svc.Namespace, "SVC.Name", svc.Name) + return ctrl.Result{Requeue: false}, err + } + r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Service Created", fmt.Sprintf("Succesfully Created New Service %v", svc.Name)) + log.Info("Succesfully Created New Service ", "Service.Name : ", svc.Name) + } + time.Sleep(10 * time.Second) + } else { + return ctrl.Result{Requeue: false}, err + } + } + + log.Info(" ", "Found Existing Service ", service.Name) + + // validate if all the databases have only one replicas + for _, databaseRef := range broker.GetDatabasesInDataGuardConfiguration() { + var singleinstancedatabase dbapi.SingleInstanceDatabase + if err := r.Get(ctx, types.NamespacedName{Name: databaseRef, Namespace: broker.Namespace}, &singleinstancedatabase); err != nil { + if apierrors.IsNotFound(err) { + broker.Status.Status = dbcommons.StatusError + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "SingleInstanceDatabase Not Found", fmt.Sprintf("SingleInstanceDatabase %s not found", singleinstancedatabase.Name)) + log.Info(fmt.Sprintf("singleinstancedatabase %s not found", databaseRef)) + return ctrl.Result{Requeue: false}, nil + } + return ctrl.Result{Requeue: false}, err + } + if broker.Spec.FastStartFailover && singleinstancedatabase.Status.Replicas > 1 { + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "SIDB Not supported", "dataguardbroker doesn't support multiple replicas sidb in FastStartFailover mode") + log.Info("dataguardbroker doesn't support multiple replicas sidb in FastStartFailover mode") + broker.Status.Status = dbcommons.StatusError + return ctrl.Result{Requeue: false}, nil + } + } + + // Get the current primary singleinstancedatabase resourcce + var sidb dbapi.SingleInstanceDatabase + namespacedName := types.NamespacedName{ + Namespace: broker.Namespace, + Name: broker.GetCurrentPrimaryDatabase(), + } + if err := r.Get(ctx, namespacedName, &sidb); err != nil { + if apierrors.IsNotFound(err) { + broker.Status.Status = dbcommons.StatusError + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "SingleInstanceDatabase Not Found", fmt.Sprintf("SingleInstanceDatabase %s not found", sidb.Name)) + log.Info(fmt.Sprintf("singleinstancedatabase %s not found", namespacedName.Name)) + return ctrl.Result{Requeue: false}, nil + } + return ctrl.Result{Requeue: false}, err + } + if sidb.Status.Role != "PRIMARY" { + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "Spec Validation", fmt.Sprintf("singleInstanceDatabase %v not in primary role", sidb.Name)) + log.Info(fmt.Sprintf("singleinstancedatabase %s expected to be in primary role", sidb.Name)) + log.Info("updating database status to check for possible FSFO") + if err := updateReconcileStatus(r, broker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } + return ctrl.Result{Requeue: true, RequeueAfter: 60 * time.Second}, nil + } + + // validate current primary singleinstancedatabase readiness + log.Info(fmt.Sprintf("Validating readiness for singleinstancedatabase %v", sidb.Name)) + if err := validateSidbReadiness(r, broker, &sidb, ctx, req); err != nil { + if errors.Is(err, ErrCurrentPrimaryDatabaseNotReady) { + if broker.Status.Status != "" && broker.Status.FastStartFailover { + r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Possible Failover", "Primary db not in ready state after setting up DG configuration") + } + r.Recorder.Eventf(broker, corev1.EventTypeWarning, "Waiting", err.Error()) + return ctrl.Result{Requeue: true, RequeueAfter: 60 * time.Second}, nil + } + return ctrl.Result{Requeue: false}, err + } + + // setup dataguard configuration + log.Info(fmt.Sprintf("setup Dataguard configuration for primary database %v", sidb.Name)) + if err := setupDataguardBrokerConfiguration(r, broker, &sidb, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } + + return ctrl.Result{Requeue: false}, nil +} + +// ############################################################################################################################# +// +// Manange manual switchover to the target database +// +// ############################################################################################################################# +func (r *DataguardBrokerReconciler) manageManualSwitchOver(targetSidbSid string, broker *dbapi.DataguardBroker, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + log := r.Log.WithValues("SetAsPrimaryDatabase", req.NamespacedName) + + if _, ok := broker.Status.DatabasesInDataguardConfig[targetSidbSid]; !ok { + eventReason := "Cannot Switchover" + eventMsg := fmt.Sprintf("Database %s not a part of the dataguard configuration", targetSidbSid) + r.Recorder.Eventf(broker, corev1.EventTypeWarning, eventReason, eventMsg) + return ctrl.Result{Requeue: false}, nil + } + + // change broker status to updating to indicate manual switchover start + broker.Status.Status = dbcommons.StatusUpdating + r.Status().Update(ctx, broker) + + var sidb dbapi.SingleInstanceDatabase + if err := r.Get(context.TODO(), types.NamespacedName{Name: broker.GetCurrentPrimaryDatabase(), Namespace: broker.Namespace}, &sidb); err != nil { + return ctrl.Result{Requeue: false}, err + } + + // Fetch the primary database ready pod to create chk file + sidbReadyPod, _, _, _, err := dbcommons.FindPods(r, sidb.Spec.Image.Version, + sidb.Spec.Image.PullFrom, sidb.Name, sidb.Namespace, ctx, req) + if err != nil { + return ctrl.Result{Requeue: false}, err + } + + // Fetch the target database ready pod to create chk file + targetReadyPod, _, _, _, err := dbcommons.FindPods(r, "", "", broker.Status.DatabasesInDataguardConfig[targetSidbSid], req.Namespace, + ctx, ctrl.Request{NamespacedName: types.NamespacedName{Name: broker.Status.DatabasesInDataguardConfig[targetSidbSid], Namespace: req.Namespace}}) + if err != nil { + return ctrl.Result{Requeue: false}, err + } + + // Create a chk File so that no other pods take the lock during Switchover . + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", dbcommons.CreateChkFileCMD) + if err != nil { + log.Error(err, err.Error()) + return ctrl.Result{Requeue: false}, err + } + log.Info("Successfully Created chk file " + out) + + out, err = dbcommons.ExecCommand(r, r.Config, targetReadyPod.Name, targetReadyPod.Namespace, "", ctx, req, false, "bash", "-c", dbcommons.CreateChkFileCMD) + if err != nil { + log.Error(err, err.Error()) + return ctrl.Result{Requeue: false}, err + } + log.Info("Successfully Created chk file " + out) + + eventReason := "Waiting" + eventMsg := "Switchover In Progress" + r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) + + // Get Admin password for current primary database + var adminPasswordSecret corev1.Secret + if err := r.Get(context.TODO(), types.NamespacedName{Name: sidb.Spec.AdminPassword.SecretName, Namespace: sidb.Namespace}, &adminPasswordSecret); err != nil { + return ctrl.Result{Requeue: false}, err + } + var adminPassword string = string(adminPasswordSecret.Data[sidb.Spec.AdminPassword.SecretKey]) + + // Connect to 'primarySid' db using dgmgrl and switchover to 'targetSidbSid' db to make 'targetSidbSid' db primary + _, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf(dbcommons.CreateAdminPasswordFile, adminPassword)) + if err != nil { + log.Error(err, err.Error()) + return ctrl.Result{Requeue: false}, err + } + log.Info("DB Admin pwd file created") + + out, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("dgmgrl sys@%s \"SWITCHOVER TO %s\" < admin.pwd", broker.Status.PrimaryDatabase, targetSidbSid)) + if err != nil { + log.Error(err, err.Error()) + return ctrl.Result{Requeue: false}, err + } + log.Info("SWITCHOVER TO " + targetSidbSid + " Output") + log.Info(out) + + return ctrl.Result{Requeue: false}, nil +} + +// ############################################################################################################################# +// +// Setup the controller with the Manager +// +// ############################################################################################################################# +func (r *DataguardBrokerReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbapi.DataguardBroker{}). + Owns(&corev1.Pod{}). //Watch for deleted pods of DataguardBroker Owner + WithEventFilter(dbcommons.ResourceEventHandler()). + WithOptions(controller.Options{MaxConcurrentReconciles: 100}). //ReconcileHandler is never invoked concurrently with the same object. + Complete(r) +} diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 89c8a083..4044e84f 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -29,11 +29,13 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [Setup Database with LoadBalancer](#setup-database-with-loadbalancer) * [Enabling TCPS Connections](#enabling-tcps-connections) * [Specifying Custom Ports](#specifying-custom-ports) - * [Setup Data Guard Configuration for a Single Instance Database (Preview status)](#setup-data-guard-configuration-for-a-single-instance-database-preview-status) + * [Setup Data Guard Configuration for a Single Instance Database](#setup-data-guard-configuration-for-a-single-instance-database) * [Create a Standby Database](#create-a-standby-database) * [Create a Data Guard Configuration](#create-a-data-guard-configuration) * [Perform a Switchover](#perform-a-switchover) - * [Patch Primary and Standby databases in Data Guard configuration](#patch-primary-and-standby-databases-in-data-guard-configuration) + * [Enable Fast-Start Failover](#enable-fast-start-failover) + * [Static Data Guard Connect String](#static-data-guard-connect-string) + * [Patch Primary and Standby databases](#patch-primary-and-standby-databases) * [Delete the Data Guard Configuration](#delete-the-data-guard-configuration) * [Execute Custom Scripts](#execute-custom-scripts) * [OracleRestDataService Resource](#oraclerestdataservice-resource) @@ -705,7 +707,7 @@ In case of `NodePort` service, `listenerPort`, and `tcpsListenerPort` will be th - If TCPS connections are enabled, and `listenerPort` is commented/removed in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, only TCPS endpoint will be exposed. - If LoadBalancer is enabled, and either `listenerPort` or `tcpsListenerPort` is changed, then it takes some time to complete the work requests (drain existing backend sets and create new ones). In this time, the database connectivity is broken. Although, SingleInstanceDatabase and LoadBalancer remain in the healthy state, you can check the progress of the work requests by logging into the cloud provider's console and checking the corresponding LoadBalancer. -### Setup Data Guard Configuration for a Single Instance Database (Preview status) +### Setup Data Guard Configuration for a Single Instance Database ### Create a Standby Database @@ -828,8 +830,7 @@ To list the DataguardBroker resources, use the following command: Keep Secret: true Secret Key: oracle_pwd Secret Name: db-secret - Fast Start Fail Over: - Enable: true + Fast Start Failover: false Primary Database Ref: sidb-sample Protection Mode: MaxAvailability Set As Primary Database: @@ -839,6 +840,7 @@ To list the DataguardBroker resources, use the following command: Status: Cluster Connect String: dataguardbroker-sample.default:1521/DATAGUARD External Connect String: 10.0.25.85:31167/DATAGUARD + Fast Start Failover: false Primary Database: OR19E3 Standby Databases: OR19E3S1,OR19E3S2 Status: Healthy @@ -869,18 +871,41 @@ $ kubectl --type=merge -p '{"spec":{"setAsPrimaryDatabase":"ORCLS1"}}' patch dat dataguardbroker.database.oracle.com/dataguardbroker-sample patched ``` -#### Static Primary Database Connection String +### Enable Fast-Start Failover - External and internal (running in Kubernetes pods) clients can connect to the primary database using `.status.connectString` and `.status.clusterConnectString` of the DataguardBroker resource respectively. These connection strings are fixed for the DataguardBroker resource and will not change on switchover. They can be queried using the following command +Oracle Data Guard Fast-Start Failover (FSFO) monitors your Data Guard environments and initiates an automatic failover in the case of an outage. +To enable FSFO, make sure the primary database is in the primary role. Then set the attribute `.spec.fastStartFailover` to true in [datguardbroker.yaml](./../../config/samples/sidb/dataguardbroker.yaml) and apply it. + +```sh +$ kubectl apply -f dataguardbroker.yaml + + dataguardbroker.database.oracle.com/dataguardbroker-sample configured +``` + +Or use the patch command + +```sh +$ kubectl --type=merge -p '{"spec":{"fastStartFailover": true}}' patch dataguardbroker dataguardbroker-sample + + dataguardbroker.database.oracle.com/dataguardbroker-sample patched +``` + +This results in the creation of a pod running the Observer. The Observer is a component of the DGMGRL interface which monitors the availability of the primary database. + +**Note:** When the attribute fastStartFailover is true, performing a switchover by specifying setAsPrimaryDatabase is not allowed. + +### Static Data Guard Connect String + + External and internal (running in pods) applications can always connect to the database in the primary role using `.status.externalConnectString` and `.status.clusterConnectString` of the DataguardBroker resource respectively. These connect strings are fixed for the DataguardBroker resource and will not change on switchover or failover. The external connect string can be obtained using the following command ```sh $ kubectl get dataguardbroker dataguardbroker-sample -o "jsonpath={.status.externalConnectString}" 10.0.25.87:1521/DATAGUARD ``` - The above connection string will always automatically route to the Primary database not requiring clients to change the connection string after switchover + The above connect string will always automatically route to the database in the primary role. Client applications can be totally agnostic of the databases in the Data Guard configuration. Their number or host/IP details are not needed in the connect string. -### Patch Primary and Standby databases in Data Guard configuration +### Patch Primary and Standby databases Databases (both primary and standby) running in you cluster and managed by the Oracle Database operator can be patched between release updates of the same major release. @@ -891,13 +916,17 @@ kubectl --type=merge -p '{"spec":{"image":{"pullFrom":"patched-image:tag","pullS ``` Follow these steps for patching databases configured with the dataguard broker: -1. First patch all the standby databases by replacing the image with the new release update image -2. Perform switch over of the primary to one of the standby databases -3. Now patch the original primary database (currently standby after #2) +1. Ensure Fast-Start Failover is disabled by executing the following command +```sh + kubectl patch dataguardbroker dataguardbroker-sample -p '{"spec":{"fastStartFailover": false}}' --type=merge +``` +2. First patch all the standby databases by replacing the image with the new release update image +3. Perform switch over of the primary to one of the standby databases +4. Now patch the original primary database (currently standby after #2) After #3 the software for primary and standby databases is at the same release update -4. Now bounce the current primary database by updating the replica count to 0 and then 1 +5. Now bounce the current primary database by updating the replica count to 0 and then 1 #4 will trigger a datapatch execution resulting in patching of the datafiles -5. Finally perform switch over of the current primary back to the original primary (current standby) +6. Finally perform switch over of the current primary back to the original primary (current standby) ### Delete the Data Guard Configuration diff --git a/main.go b/main.go index 4174e97d..e65dde11 100644 --- a/main.go +++ b/main.go @@ -63,6 +63,7 @@ import ( databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" databasecontroller "github.com/oracle/oracle-database-operator/controllers/database" + dataguardcontroller "github.com/oracle/oracle-database-operator/controllers/dataguard" observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" observabilitycontroller "github.com/oracle/oracle-database-operator/controllers/observability" @@ -282,9 +283,9 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "CDB") os.Exit(1) } - if err = (&databasecontroller.DataguardBrokerReconciler{ + if err = (&dataguardcontroller.DataguardBrokerReconciler{ Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("database").WithName("DataguardBroker"), + Log: ctrl.Log.WithName("controllers").WithName("dataguard").WithName("DataguardBroker"), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), Recorder: mgr.GetEventRecorderFor("DataguardBroker"), From f45fa6482636da633fc3049cfbe3b61521f36077 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Mon, 28 Oct 2024 05:05:27 +0000 Subject: [PATCH 125/414] Readme changes for Oracle Database Free Lite support --- .../singleinstancedatabase_free-lite.yaml | 38 +++++++++++++++++++ docs/sidb/README.md | 14 +++++-- 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 config/samples/sidb/singleinstancedatabase_free-lite.yaml diff --git a/config/samples/sidb/singleinstancedatabase_free-lite.yaml b/config/samples/sidb/singleinstancedatabase_free-lite.yaml new file mode 100644 index 00000000..6641790f --- /dev/null +++ b/config/samples/sidb/singleinstancedatabase_free-lite.yaml @@ -0,0 +1,38 @@ +# +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# + +apiVersion: database.oracle.com/v1alpha1 +kind: SingleInstanceDatabase +metadata: + name: freedb-lite-sample + namespace: default +spec: + + ## Use only alphanumeric characters for sid + sid: FREE + + ## DB edition + edition: free + + ## Secret containing SIDB password mapped to secretKey + adminPassword: + secretName: freedb-admin-secret + + ## Database image details + image: + ## Oracle Database Free Lite is only supported from DB version 23.2 onwards + pullFrom: container-registry.oracle.com/database/free:latest-lite + + ## size is the required minimum size of the persistent volume + ## storageClass is specified for automatic volume provisioning + ## accessMode can only accept one of ReadWriteOnce, ReadWriteMany + persistence: + size: 50Gi + ## oci-bv applies to OCI block volumes. Use "standard" storageClass for dynamic provisioning in Minikube. Update as appropriate for other cloud service providers + storageClass: "oci-bv" + accessMode: "ReadWriteOnce" + + ## Count of Database Pods. Should be 1 for free edition. + replicas: 1 \ No newline at end of file diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 4044e84f..3f518671 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -12,6 +12,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [Pre-built Database](#pre-built-database) * [XE Database](#xe-database) * [Free Database](#free-database) + * [Free Lite Database](#free-lite-database) * [Connecting to Database](#connecting-to-database) * [Database Persistence (Storage) Configuration Options](#database-persistence-storage-configuration-options) * [Dynamic Persistence](#dynamic-persistence) @@ -281,7 +282,7 @@ To provision new Oracle Database Express Edition (XE) database, use the sample * kubectl apply -f singleinstancedatabase_express.yaml -This command pulls the XE image uploaded on the [Oracle Container Registry](https://container-registry.oracle.com/). +This command pulls the XE image available in [Oracle Container Registry](https://container-registry.oracle.com/). **Note:** - Provisioning Oracle Database express edition is supported for release 21c (21.3.0) only. @@ -289,11 +290,18 @@ This command pulls the XE image uploaded on the [Oracle Container Registry](http - For XE database, you **cannot change** the init parameters i.e. `cpuCount, processes, sgaTarget or pgaAggregateTarget`. #### Free Database -To provision new Oracle Database Free database, use the sample **[config/samples/sidb/singleinstancedatabase_free.yaml](../../config/samples/sidb/singleinstancedatabase_free.yaml)** file. For example: +To provision new Oracle Database Free, use the sample **[config/samples/sidb/singleinstancedatabase_free.yaml](../../config/samples/sidb/singleinstancedatabase_free.yaml)** file. For example: kubectl apply -f singleinstancedatabase_free.yaml -This command pulls the Free image uploaded on the [Oracle Container Registry](https://container-registry.oracle.com/). +This command pulls the Free image available in [Oracle Container Registry](https://container-registry.oracle.com/). + +#### Free Lite Database +To provision new Oracle Database Free Lite, use the sample **[config/samples/sidb/singleinstancedatabase_free-lite.yaml](../../config/samples/sidb/singleinstancedatabase_free-lite.yaml)** file. For example: + + kubectl apply -f singleinstancedatabase_free-lite.yaml + +This command pulls the Free lite image available in [Oracle Container Registry](https://container-registry.oracle.com/). **Note:** - Provisioning Oracle Database Free is supported for release 23.3.0 and later releases. From 6d081e630bdc6e25534ca930484d1ec5dfb0a437 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Thu, 14 Nov 2024 13:16:28 +0000 Subject: [PATCH 126/414] Supporting snapshot standby databases --- .../v1alpha1/singleinstancedatabase_types.go | 7 +- .../singleinstancedatabase_webhook.go | 4 +- .../samples/sidb/singleinstancedatabase.yaml | 4 + .../sidb/singleinstancedatabase_create.yaml | 2 +- .../sidb/singleinstancedatabase_standby.yaml | 2 +- .../singleinstancedatabase_controller.go | 212 +++++++++++++++++- controllers/dataguard/dataguard_utils.go | 10 +- .../dataguard/dataguardbroker_controller.go | 13 ++ docs/sidb/README.md | 21 ++ 9 files changed, 262 insertions(+), 13 deletions(-) diff --git a/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index 7c6c1ea5..810f2e10 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -70,7 +70,6 @@ type SingleInstanceDatabaseSpec struct { EnableTCPS bool `json:"enableTCPS,omitempty"` TcpsCertRenewInterval string `json:"tcpsCertRenewInterval,omitempty"` TcpsTlsSecret string `json:"tcpsTlsSecret,omitempty"` - DgBrokerConfigured bool `json:"dgBrokerConfigured,omitempty"` PrimaryDatabaseRef string `json:"primaryDatabaseRef,omitempty"` // +kubebuilder:validation:Enum=primary;standby;clone @@ -87,6 +86,8 @@ type SingleInstanceDatabaseSpec struct { Persistence SingleInstanceDatabasePersistence `json:"persistence,omitempty"` InitParams *SingleInstanceDatabaseInitParams `json:"initParams,omitempty"` Resources SingleInstanceDatabaseResources `json:"resources,omitempty"` + + ConvertToSnapshotStandby bool `json:"convertToSnapshotStandby,omitempty"` } type SingleInstanceDatabaseResource struct { @@ -145,6 +146,7 @@ type SingleInstanceDatabaseStatus struct { Status string `json:"status,omitempty"` Replicas int `json:"replicas,omitempty"` ReleaseUpdate string `json:"releaseUpdate,omitempty"` + DgBroker *string `json:"dgBroker,omitempty"` // +kubebuilder:default:="false" DatafilesPatched string `json:"datafilesPatched,omitempty"` ConnectString string `json:"connectString,omitempty"` @@ -175,7 +177,6 @@ type SingleInstanceDatabaseStatus struct { CertRenewInterval string `json:"certRenewInterval,omitempty"` ClientWalletLoc string `json:"clientWalletLoc,omitempty"` PrimaryDatabase string `json:"primaryDatabase,omitempty"` - DgBrokerConfigured bool `json:"dgBrokerConfigured,omitempty"` // +kubebuilder:default:="" TcpsTlsSecret string `json:"tcpsTlsSecret"` @@ -187,6 +188,8 @@ type SingleInstanceDatabaseStatus struct { InitParams SingleInstanceDatabaseInitParams `json:"initParams,omitempty"` Persistence SingleInstanceDatabasePersistence `json:"persistence"` + + ConvertToSnapshotStandby bool `json:"convertToSnapshotStandby,omitempty"` } //+kubebuilder:object:root=true diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 1cbc57a8..e5870a68 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -470,7 +470,7 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) // if Db is in a dataguard configuration or referred by Standby databases then Restrict enabling Tcps on the Primary DB if r.Spec.EnableTCPS { - if old.Status.DgBrokerConfigured { + if old.Status.DgBroker != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("enableTCPS"), "cannot enable tcps as database is in a dataguard configuration")) } else if len(old.Status.StandbyDatabases) != 0 { @@ -510,7 +510,7 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) field.Forbidden(field.NewPath("spec").Child("persistence"), "uninstall ORDS to change Persistence")) } - if old.Status.Replicas != r.Spec.Replicas && old.Status.DgBrokerConfigured { + if old.Status.Replicas != r.Spec.Replicas && old.Status.DgBroker != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("replicas"), "cannot be updated for a database in a Data Guard configuration")) } diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 4425acea..b24f517d 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -21,6 +21,10 @@ spec: ## Valid only for enterprise and standard editions createAs: primary + ## Specify true to convert this standby to a snapshot standby + ## Valid only if createAs is standby + convertToSnapshotStandby: false + ## Reference to a source primary database. ## Valid only for createAs clone or standby ## The name of a source primary database resource from the same namespace diff --git a/config/samples/sidb/singleinstancedatabase_create.yaml b/config/samples/sidb/singleinstancedatabase_create.yaml index d09d9c26..3989d6ac 100644 --- a/config/samples/sidb/singleinstancedatabase_create.yaml +++ b/config/samples/sidb/singleinstancedatabase_create.yaml @@ -33,7 +33,7 @@ spec: ## Database image details image: - pullFrom: container-registry.oracle.com/database/enterprise:latest + pullFrom: container-registry.oracle.com/database/enterprise_ru:19 pullSecrets: oracle-container-registry-secret ## size is the required minimum size of the persistent volume diff --git a/config/samples/sidb/singleinstancedatabase_standby.yaml b/config/samples/sidb/singleinstancedatabase_standby.yaml index 644438b4..dbc9c267 100644 --- a/config/samples/sidb/singleinstancedatabase_standby.yaml +++ b/config/samples/sidb/singleinstancedatabase_standby.yaml @@ -27,7 +27,7 @@ spec: ## Database image details image: - pullFrom: container-registry.oracle.com/database/enterprise:latest + pullFrom: container-registry.oracle.com/database/enterprise_ru:19 pullSecrets: oracle-container-registry-secret ## size is the required minimum size of the persistent volume diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index a15dfe2d..5f8902b2 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -90,6 +90,11 @@ const singleInstanceDatabaseFinalizer = "database.oracle.com/singleinstancedatab var oemExpressUrl string +var ErrNotPhysicalStandby error = errors.New("database not in PHYSICAL_STANDBY role") +var ErrDBNotConfiguredWithDG error = errors.New("database is not configured with a dataguard configuration") +var ErrFSFOEnabledForDGConfig error = errors.New("database is configured with dataguard and FSFO enabled") +var ErrAdminPasswordSecretNotFound error = errors.New("Admin password secret for the database not found") + //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=singleinstancedatabases/finalizers,verbs=update @@ -244,7 +249,7 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct } else { // Database is in role of standby - if !singleInstanceDatabase.Status.DgBrokerConfigured { + if singleInstanceDatabase.Status.DgBroker == nil { err = SetupStandbyDatabase(r, singleInstanceDatabase, referredPrimaryDatabase, ctx, req) if err != nil { return requeueY, err @@ -280,6 +285,17 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct } + // manage snapshot database creation + if singleInstanceDatabase.Spec.ConvertToSnapshotStandby != singleInstanceDatabase.Status.ConvertToSnapshotStandby { + result, err := r.manageConvPhysicalToSnapshot(ctx, req) + if err != nil { + return requeueN, err + } + if result.Requeue { + return requeueY, nil + } + } + // Run Datapatch if strings.ToUpper(singleInstanceDatabase.Status.Role) == "PRIMARY" && singleInstanceDatabase.Status.DatafilesPatched != "true" { // add a blocking reconcile condition @@ -3125,7 +3141,7 @@ func (r *SingleInstanceDatabaseReconciler) cleanupSingleInstanceDatabase(req ctr return requeueY, nil } - if m.Status.DgBrokerConfigured { + if m.Status.DgBroker != nil { eventReason := "Cannot Delete" eventMsg := "database cannot be deleted as it is present in a DataGuard Broker configuration" r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) @@ -3159,6 +3175,198 @@ func (r *SingleInstanceDatabaseReconciler) cleanupSingleInstanceDatabase(req ctr return requeueN, nil } +// ############################################################################################# +// +// Manage conversion of singleinstancedatabase from PHYSICAL_STANDBY To SNAPSHOT_STANDBY +// +// ############################################################################################# +func (r *SingleInstanceDatabaseReconciler) manageConvPhysicalToSnapshot(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.Log.WithValues("manageConvPhysicalToSnapshot", req.NamespacedName) + var singleInstanceDatabase dbapi.SingleInstanceDatabase + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, &singleInstanceDatabase); err != nil { + if apierrors.IsNotFound(err) { + log.Info("requested resource not found") + return requeueY, nil + } + log.Error(err, err.Error()) + return requeueY, err + } + + sidbReadyPod, err := GetDatabaseReadyPod(r, &singleInstanceDatabase, ctx, req) + if err != nil { + return requeueY, err + } + if sidbReadyPod.Name == "" { + log.Info("No ready Pod for the requested singleinstancedatabase") + return requeueY, nil + } + + if singleInstanceDatabase.Spec.ConvertToSnapshotStandby { + // Convert a PHYSICAL_STANDBY -> SNAPSHOT_STANDBY + singleInstanceDatabase.Status.Status = dbcommons.StatusUpdating + r.Status().Update(ctx, &singleInstanceDatabase) + if err := convertPhysicalStdToSnapshotStdDB(r, &singleInstanceDatabase, &sidbReadyPod, ctx, req); err != nil { + switch err { + case ErrNotPhysicalStandby: + r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Conversion to Snapshot Standby Not allowed", "Database not in physical standby role") + log.Info("Conversion to Snapshot Standby not allowed as database not in physical standby role") + return requeueY, nil + case ErrDBNotConfiguredWithDG: + // cannot convert to snapshot database + r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Conversion to Snapshot Standby Not allowed", "Database is not configured with dataguard") + log.Info("Conversion to Snapshot Standby not allowed as requested database is not configured with dataguard") + return requeueY, nil + case ErrFSFOEnabledForDGConfig: + r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Conversion to Snapshot Standby Not allowed", "Database is a FastStartFailover target") + log.Info("Conversion to Snapshot Standby Not allowed as database is a FastStartFailover target") + return requeueY, nil + case ErrAdminPasswordSecretNotFound: + r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Admin Password", "Database admin password secret not found") + log.Info("Database admin password secret not found") + return requeueY, nil + default: + log.Error(err, err.Error()) + return requeueY, nil + } + } + log.Info(fmt.Sprintf("Database %s converted to snapshot standby", singleInstanceDatabase.Name)) + singleInstanceDatabase.Status.ConvertToSnapshotStandby = true + singleInstanceDatabase.Status.Status = dbcommons.StatusReady + // Get database role and update the status + sidbRole, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, req) + if err != nil { + return requeueN, err + } + log.Info("Database "+singleInstanceDatabase.Name, "Database Role : ", sidbRole) + singleInstanceDatabase.Status.Role = sidbRole + r.Status().Update(ctx, &singleInstanceDatabase) + } else { + // Convert a SNAPSHOT_STANDBY -> PHYSICAL_STANDBY + singleInstanceDatabase.Status.Status = dbcommons.StatusUpdating + r.Status().Update(ctx, &singleInstanceDatabase) + if err := convertSnapshotStdToPhysicalStdDB(r, &singleInstanceDatabase, &sidbReadyPod, ctx, req); err != nil { + switch err { + default: + r.Log.Error(err, err.Error()) + return requeueY, nil + } + } + singleInstanceDatabase.Status.ConvertToSnapshotStandby = false + singleInstanceDatabase.Status.Status = dbcommons.StatusReady + // Get database role and update the status + sidbRole, err := dbcommons.GetDatabaseRole(sidbReadyPod, r, r.Config, ctx, req) + if err != nil { + return requeueN, err + } + log.Info("Database "+singleInstanceDatabase.Name, "Database Role : ", sidbRole) + singleInstanceDatabase.Status.Role = sidbRole + r.Status().Update(ctx, &singleInstanceDatabase) + } + + return requeueN, nil +} + +func convertPhysicalStdToSnapshotStdDB(r *SingleInstanceDatabaseReconciler, singleInstanceDatabase *dbapi.SingleInstanceDatabase, sidbReadyPod *corev1.Pod, ctx context.Context, req ctrl.Request) error { + log := r.Log.WithValues("convertPhysicalStdToSnapshotStdDB", req.NamespacedName) + log.Info(fmt.Sprintf("Checking the role %s database i.e %s", singleInstanceDatabase.Name, singleInstanceDatabase.Status.Role)) + if singleInstanceDatabase.Status.Role != "PHYSICAL_STANDBY" { + return ErrNotPhysicalStandby + } + + var dataguardBroker dbapi.DataguardBroker + log.Info(fmt.Sprintf("Checking if the database %s is configured with dgbroker or not ?", singleInstanceDatabase.Name)) + if singleInstanceDatabase.Status.DgBroker != nil { + if err := r.Get(ctx, types.NamespacedName{Namespace: singleInstanceDatabase.Namespace, Name: *singleInstanceDatabase.Status.DgBroker}, &dataguardBroker); err != nil { + if apierrors.IsNotFound(err) { + log.Info("Resource not found") + return errors.New("Dataguardbroker resource not found") + } + return err + } + log.Info(fmt.Sprintf("database %s is configured with dgbroker %s", singleInstanceDatabase.Name, *singleInstanceDatabase.Status.DgBroker)) + if dataguardBroker.Status.FastStartFailover { + // not allowed to convert to snapshot standby + return ErrFSFOEnabledForDGConfig + } + } else { + // cannot convert to snapshot database + return ErrDBNotConfiguredWithDG + } + + // get singleinstancedatabase ready pod + // execute the dgmgrl command for conversion to snapshot database + // Exception handling + // Get Admin password for current primary database + var adminPasswordSecret corev1.Secret + if err := r.Get(context.TODO(), types.NamespacedName{Name: singleInstanceDatabase.Spec.AdminPassword.SecretName, Namespace: singleInstanceDatabase.Namespace}, &adminPasswordSecret); err != nil { + return err + } + var adminPassword string = string(adminPasswordSecret.Data[singleInstanceDatabase.Spec.AdminPassword.SecretKey]) + + // Connect to 'primarySid' db using dgmgrl and switchover to 'targetSidbSid' db to make 'targetSidbSid' db primary + if _, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", fmt.Sprintf(dbcommons.CreateAdminPasswordFile, adminPassword)); err != nil { + return err + } + + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", fmt.Sprintf("dgmgrl sys@%s \"convert database %s to snapshot standby;\" < admin.pwd", dataguardBroker.Status.PrimaryDatabase, singleInstanceDatabase.Status.Sid)) + if err != nil { + return err + } + log.Info(fmt.Sprintf("Convert to snapshot standby command output \n %s", out)) + + out, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", fmt.Sprintf("echo -e \"alter pluggable database %s open;\" | %s", singleInstanceDatabase.Status.Pdbname, dbcommons.SQLPlusCLI)) + if err != nil { + return err + } + log.Info(fmt.Sprintf("Open pluggable databases output \n %s", out)) + + return nil +} + +func convertSnapshotStdToPhysicalStdDB(r *SingleInstanceDatabaseReconciler, singleInstanceDatabase *dbapi.SingleInstanceDatabase, sidbReadyPod *corev1.Pod, ctx context.Context, req ctrl.Request) error { + log := r.Log.WithValues("convertSnapshotStdToPhysicalStdDB", req.NamespacedName) + + var dataguardBroker dbapi.DataguardBroker + if err := r.Get(ctx, types.NamespacedName{Namespace: singleInstanceDatabase.Namespace, Name: *singleInstanceDatabase.Status.DgBroker}, &dataguardBroker); err != nil { + if apierrors.IsNotFound(err) { + return errors.New("dataguardbroker resource not found") + } + return err + } + + var adminPasswordSecret corev1.Secret + if err := r.Get(context.TODO(), types.NamespacedName{Name: singleInstanceDatabase.Spec.AdminPassword.SecretName, Namespace: singleInstanceDatabase.Namespace}, &adminPasswordSecret); err != nil { + if apierrors.IsNotFound(err) { + return ErrAdminPasswordSecretNotFound + } + return err + } + var adminPassword string = string(adminPasswordSecret.Data[singleInstanceDatabase.Spec.AdminPassword.SecretKey]) + + // Connect to 'primarySid' db using dgmgrl and switchover to 'targetSidbSid' db to make 'targetSidbSid' db primary + _, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf(dbcommons.CreateAdminPasswordFile, adminPassword)) + if err != nil { + return err + } + log.Info("Converting snapshot standby to physical standby") + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", fmt.Sprintf("dgmgrl sys@%s \"convert database %s to physical standby;\" < admin.pwd", dataguardBroker.Status.PrimaryDatabase, singleInstanceDatabase.Status.Sid)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info(fmt.Sprintf("Database %s converted to physical standby \n %s", singleInstanceDatabase.Name, out)) + log.Info("opening the PDB for the database") + out, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", fmt.Sprintf("echo -e \"alter pluggable database %s open;\" | %s", singleInstanceDatabase.Status.Pdbname, dbcommons.SQLPlusCLI)) + if err != nil { + r.Log.Error(err, err.Error()) + return err + } + log.Info(fmt.Sprintf("PDB open command output %s", out)) + + return nil +} + // ############################################################################# // // SetupWithManager sets up the controller with the Manager diff --git a/controllers/dataguard/dataguard_utils.go b/controllers/dataguard/dataguard_utils.go index 213e33d2..65efeb5d 100644 --- a/controllers/dataguard/dataguard_utils.go +++ b/controllers/dataguard/dataguard_utils.go @@ -114,7 +114,7 @@ func cleanupDataguardBroker(r *DataguardBrokerReconciler, broker *dbapi.Dataguar } // Set DgBrokerConfigured to false - standbyDatabase.Status.DgBrokerConfigured = false + standbyDatabase.Status.DgBroker = nil if err := r.Status().Update(ctx, &standbyDatabase); err != nil { r.Recorder.Eventf(&standbyDatabase, corev1.EventTypeWarning, "Updating Status", "DgBrokerConfigured status updation failed") log.Info(fmt.Sprintf("Status updation for sidb %s failed", standbyDatabase.Name)) @@ -236,7 +236,7 @@ func setupDataguardBrokerConfiguration(r *DataguardBrokerReconciler, broker *dba } // Check if dataguard broker is already configured for the standby database - if standbyDatabase.Status.DgBrokerConfigured { + if standbyDatabase.Status.DgBroker != nil { log.Info("Dataguard broker for standbyDatabase : " + standbyDatabase.Name + " is already configured") continue } @@ -393,8 +393,8 @@ func setupDataguardBrokerConfigurationForGivenDB(r *DataguardBrokerReconciler, m log.Info(out) } // Set DG Configured status to true for this standbyDatabase and primary Database. so that in next reconcilation, we dont configure this again - n.Status.DgBrokerConfigured = true - standbyDatabase.Status.DgBrokerConfigured = true + n.Status.DgBroker = &m.Name + standbyDatabase.Status.DgBroker = &m.Name r.Status().Update(ctx, standbyDatabase) r.Status().Update(ctx, n) // Remove admin pwd file @@ -486,7 +486,7 @@ func setupDataguardBrokerConfigurationForGivenDB(r *DataguardBrokerReconciler, m log.Info("DB Admin pwd file removed") // Set DG Configured status to true for this standbyDatabase. so that in next reconcilation, we dont configure this again - standbyDatabase.Status.DgBrokerConfigured = true + standbyDatabase.Status.DgBroker = &m.Name r.Status().Update(ctx, standbyDatabase) return nil diff --git a/controllers/dataguard/dataguardbroker_controller.go b/controllers/dataguard/dataguardbroker_controller.go index 8090fc67..f3878a05 100644 --- a/controllers/dataguard/dataguardbroker_controller.go +++ b/controllers/dataguard/dataguardbroker_controller.go @@ -126,6 +126,19 @@ func (r *DataguardBrokerReconciler) Reconcile(ctx context.Context, req ctrl.Requ if dataguardBroker.Spec.FastStartFailover != dataguardBroker.Status.FastStartFailover { if dataguardBroker.Spec.FastStartFailover { + for _, DbResource := range dataguardBroker.Status.DatabasesInDataguardConfig { + var singleInstanceDatabase dbapi.SingleInstanceDatabase + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: DbResource}, &singleInstanceDatabase); err != nil { + return ctrl.Result{Requeue: false}, err + } + r.Log.Info("Check the role for database", "database", singleInstanceDatabase.Name, "role", singleInstanceDatabase.Status.Role) + if singleInstanceDatabase.Status.Role == "SNAPSHOT_STANDBY" { + r.Recorder.Eventf(&dataguardBroker, corev1.EventTypeWarning, "Enabling FSFO failed", "database %s is a snapshot database", singleInstanceDatabase.Name) + r.Log.Info("Enabling FSFO failed, one of the database is a snapshot database", "snapshot database", singleInstanceDatabase.Name) + return ctrl.Result{Requeue: true}, nil + } + } + // set faststartfailover targets for all the singleinstancedatabases in the dataguard configuration if err := setFSFOTargets(r, &dataguardBroker, ctx, req); err != nil { return ctrl.Result{Requeue: false}, err diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 3f518671..1ac0c2fe 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -35,6 +35,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [Create a Data Guard Configuration](#create-a-data-guard-configuration) * [Perform a Switchover](#perform-a-switchover) * [Enable Fast-Start Failover](#enable-fast-start-failover) + * [Convert Standby to Snapshot Standby](#convert-standby-to-snapshot-standby) * [Static Data Guard Connect String](#static-data-guard-connect-string) * [Patch Primary and Standby databases](#patch-primary-and-standby-databases) * [Delete the Data Guard Configuration](#delete-the-data-guard-configuration) @@ -902,6 +903,26 @@ This results in the creation of a pod running the Observer. The Observer is a co **Note:** When the attribute fastStartFailover is true, performing a switchover by specifying setAsPrimaryDatabase is not allowed. +### Convert Standby to Snapshot Standby + +A snapshot standby is a fully updatable standby database that can be used development and testing. It receives and archives, but does not apply, redo data from a primary database. The redo data received from the primary database is applied once a snapshot standby database is converted back into a physical standby database, after discarding all local updates to the snapshot standby database. + +To convert a standby database to snapshot standby, make sure Fast-Start Failover is disabled, then set the attribute `.spec.convertToSnapshotStandby` to true in [singleinstancedatabase.yaml](./../../config/samples/sidb/singleinstancedatabase.yaml) before applying it. + +```sh +$ kubectl apply -f singleinstancedatabase.yaml + + singleinstancedatabase.database.oracle.com/sidb-sample configured +``` + +Or use the patch command + +```sh +$ kubectl --type=merge -p '{"spec":{"convertToSnapshotStandby":true}}' patch singleinstancedatabase sidb-sample + + singleinstancedatabase.database.oracle.com/sidb-sample patched +``` + ### Static Data Guard Connect String External and internal (running in pods) applications can always connect to the database in the primary role using `.status.externalConnectString` and `.status.clusterConnectString` of the DataguardBroker resource respectively. These connect strings are fixed for the DataguardBroker resource and will not change on switchover or failover. The external connect string can be obtained using the following command From 7bc8702ece9ca144d75533d512d9aeb8b98b3b83 Mon Sep 17 00:00:00 2001 From: racpack Date: Tue, 26 Nov 2024 13:52:59 +0000 Subject: [PATCH 127/414] Added support for golang 1.23.2 and multiarchiteture --- .gitlab-ci.yml | 6 +- Dockerfile | 17 +- Makefile | 103 +++-- .../v1alpha1/zz_generated.deepcopy.go | 6 +- .../v1alpha1/zz_generated.deepcopy.go | 1 - .../bases/database.oracle.com_DbcsSystem.yaml | 24 +- ...acle.com_autonomouscontainerdatabases.yaml | 30 +- ....oracle.com_autonomousdatabasebackups.yaml | 30 +- ...oracle.com_autonomousdatabaserestores.yaml | 35 +- ...tabase.oracle.com_autonomousdatabases.yaml | 74 +--- .../crd/bases/database.oracle.com_cdbs.yaml | 50 +-- .../database.oracle.com_dataguardbrokers.yaml | 45 +- ...ase.oracle.com_oraclerestdataservices.yaml | 59 +-- .../crd/bases/database.oracle.com_pdbs.yaml | 90 +--- ...database.oracle.com_shardingdatabases.yaml | 137 +----- ...se.oracle.com_singleinstancedatabases.yaml | 77 +--- ...vability.oracle.com_databaseobservers.yaml | 65 +-- config/manager/kustomization.yaml | 4 +- config/rbac/role.yaml | 2 - config/webhook/manifests.yaml | 4 - go.mod | 34 +- go.sum | 89 ++-- oracle-database-operator.yaml | 418 ++---------------- 23 files changed, 203 insertions(+), 1197 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b27445a0..ede6e88e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,11 +5,11 @@ build-operator: OP_YAML: oracle-database-operator.yaml BUILD_INTERNAL: "true" script: - - export GOROOT=$(go1.21.7 env GOROOT) + - export GOROOT=$(go1.23.3 env GOROOT) - export PATH="${GOROOT}/bin:${PATH}" - make docker-build IMG="$IMAGE" - - docker push "$IMAGE" - - docker rmi "$IMAGE" && docker system prune -f + - docker manifest push "$IMAGE" + - docker manifest rm "$IMAGE" && docker system prune -f - make operator-yaml IMG=$IMAGE - if [ "$CI_COMMIT_BRANCH" != "master" ]; then sed -i "s/\(replicas.\) 3/\1 1/g" ./$OP_YAML; fi - curl -s --netrc-file $HOME/.netrc_gitlab $ARTIFACTORY_REPO/$CI_COMMIT_BRANCH/$OP_YAML -T ./$OP_YAML diff --git a/Dockerfile b/Dockerfile index 11a56962..9ed6eabf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,13 +6,16 @@ ARG BUILDER_IMG FROM ${BUILDER_IMG} as builder -# Download golang if BUILD_INTERNAL is set to true +ARG TARGETARCH +# Download golang if INSTALL_GO is set to true ARG INSTALL_GO ARG GOLANG_VERSION RUN if [ "$INSTALL_GO" = "true" ]; then \ - curl -LJO https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ - rm -rf /usr/local/go && tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ - rm go${GOLANG_VERSION}.linux-amd64.tar.gz; \ + echo -e "\nCurrent Arch: $(arch), Downloading Go for linux/${TARGETARCH}" &&\ + curl -LJO https://go.dev/dl/go${GOLANG_VERSION}.linux-${TARGETARCH}.tar.gz &&\ + rm -rf /usr/local/go && tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-${TARGETARCH}.tar.gz &&\ + rm go${GOLANG_VERSION}.linux-${TARGETARCH}.tar.gz; \ + echo "Go Arch: $(/usr/local/go/bin/go env GOARCH)"; \ fi ENV PATH=${GOLANG_VERSION:+"${PATH}:/usr/local/go/bin"} @@ -33,10 +36,10 @@ COPY LICENSE.txt LICENSE.txt COPY THIRD_PARTY_LICENSES_DOCKER.txt THIRD_PARTY_LICENSES_DOCKER.txt # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} GO111MODULE=on go build -a -o manager main.go -# Use oraclelinux:8-slim as base image to package the manager binary -FROM oraclelinux:8-slim +# Use oraclelinux:9 as base image to package the manager binary +FROM oraclelinux:9 ARG CI_COMMIT_SHA ARG CI_COMMIT_BRANCH ENV COMMIT_SHA=${CI_COMMIT_SHA} \ diff --git a/Makefile b/Makefile index 88e14843..ead6e157 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # - + # Current Operator version VERSION ?= 0.0.1 # Default bundle image tag @@ -20,147 +20,145 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) IMG ?= controller:latest # Produce CRDs that work back to Kubernetes 1.11 (no version conversion) # API version has to be v1 to use defaulting (https://github.com/kubernetes-sigs/controller-tools/issues/478) -CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false" +CRD_OPTIONS ?= "crd:maxDescLen=0" # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.21 +ENVTEST_K8S_VERSION = 1.29.0 # Operator YAML file OPERATOR_YAML=$$(basename $$(pwd)).yaml - # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) GOBIN=$(shell go env GOPATH)/bin else GOBIN=$(shell go env GOBIN) endif - + # Setting SHELL to bash allows bash commands to be executed by recipes. # This is a requirement for 'setup-envtest.sh' in the test target. # Options are set to exit when a recipe line exits non-zero or a piped command fails. SHELL = /usr/bin/env bash -o pipefail .SHELLFLAGS = -ec - + all: build - ##@ Development - + manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases - + generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." - + fmt: ## Run go fmt against code. go fmt ./... - + vet: ## Run go vet against code. go vet ./... - + TEST ?= ./apis/database/v1alpha1 ./commons/... ./controllers/... test: manifests generate fmt vet envtest ## Run unit tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $(TEST) -coverprofile cover.out - + E2ETEST ?= ./test/e2e/ e2e: manifests generate fmt vet envtest ## Run e2e tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $(E2ETEST) -test.timeout 0 -test.v --ginkgo.fail-fast - + ##@ Build - + build: generate fmt vet ## Build manager binary. go build -o bin/manager main.go - + run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go - -GOLANG_VERSION ?= 1.21.7 + +GOLANG_VERSION ?= 1.23.3 ## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. ## Otherwise, use golang image from docker hub as the builder. ifeq ($(BUILD_INTERNAL), true) -BUILDER_IMG = oraclelinux:8 +BUILDER_IMG = oraclelinux:9 BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) --build-arg INSTALL_GO=true else BUILDER_IMG = golang:$(GOLANG_VERSION) -BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO=false +BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO="false" --build-arg GOLANG_VERSION=$(GOLANG_VERSION) endif docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast - docker build --no-cache=true --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ - --build-arg CI_COMMIT_SHA=$(CI_COMMIT_SHA) --build-arg CI_COMMIT_BRANCH=$(CI_COMMIT_BRANCH) \ - $(BUILD_ARGS) . -t $(IMG) - + docker build --no-cache=true --platform=linux/arm64,linux/amd64 --jobs=2 --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ + --build-arg CI_COMMIT_SHA=$(CI_COMMIT_SHA) --build-arg CI_COMMIT_BRANCH=$(CI_COMMIT_BRANCH) \ + $(BUILD_ARGS) --manifest $(IMG) . + docker-push: ## Push docker image with the manager. docker push $(IMG) - + ##@ Deployment - + install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/crd | kubectl apply -f - - + uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/crd | kubectl delete -f - - + deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) $(KUSTOMIZE) build config/default | kubectl apply -f - - + # Bug:34265574 -# Used sed to reposition the controller-manager Deployment after the certificate creation in the OPERATOR_YAML +# Used sed to reposition the controller-manager Deployment after the certificate creation in the OPERATOR_YAML operator-yaml: manifests kustomize cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) $(KUSTOMIZE) build config/default > "$(OPERATOR_YAML)" sed -i.bak -e '/^apiVersion: apps\/v1/,/---/d' "$(OPERATOR_YAML)" (echo --- && sed '/^apiVersion: apps\/v1/,/---/!d' "$(OPERATOR_YAML).bak") >> "$(OPERATOR_YAML)" rm "$(OPERATOR_YAML).bak" - + undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/default | kubectl delete -f - - + ##@ Build Dependencies - + ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin $(LOCALBIN): mkdir -p $(LOCALBIN) - + ## Tool Binaries KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest - + ## Tool Versions -KUSTOMIZE_VERSION ?= v3.8.7 -CONTROLLER_TOOLS_VERSION ?= v0.6.1 - +KUSTOMIZE_VERSION ?= v4.5.7 +CONTROLLER_TOOLS_VERSION ?= v0.16.5 + KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. $(KUSTOMIZE): $(LOCALBIN) curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN) - + .PHONY: controller-gen controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. $(CONTROLLER_GEN): $(LOCALBIN) GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) - + .PHONY: envtest envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. $(ENVTEST): $(LOCALBIN) GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest - - + + .PHONY: bundle bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files. operator-sdk generate kustomize manifests -q cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) operator-sdk bundle validate ./bundle - + .PHONY: bundle-build bundle-build: ## Build the bundle image. docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . - + .PHONY: bundle-push bundle-push: ## Push the bundle image. $(MAKE) docker-push IMG=$(BUNDLE_IMG) - + .PHONY: opm OPM = ./bin/opm opm: ## Download opm locally if necessary. @@ -172,33 +170,32 @@ ifeq (,$(shell which opm 2>/dev/null)) OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.1/$${OS}-$${ARCH}-opm ;\ chmod +x $(OPM) ;\ - } + } else OPM = $(shell which opm) endif endif - + # A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). # These images MUST exist in a registry and be pull-able. BUNDLE_IMGS ?= $(BUNDLE_IMG) - + # The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) - + # Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. ifneq ($(origin CATALOG_BASE_IMG), undefined) FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) endif - + # Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. # This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator .PHONY: catalog-build catalog-build: opm ## Build a catalog image. $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) - + # Push the catalog image. .PHONY: catalog-push catalog-push: ## Push a catalog image. $(MAKE) docker-push IMG=$(CATALOG_IMG) - diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index c315cbfa..a3779fe5 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* ** Copyright (c) 2022 Oracle and/or its affiliates. @@ -2541,6 +2540,11 @@ func (in *SingleInstanceDatabaseStatus) DeepCopyInto(out *SingleInstanceDatabase *out = make([]string, len(*in)) copy(*out, *in) } + if in.DgBroker != nil { + in, out := &in.DgBroker, &out.DgBroker + *out = new(string) + **out = **in + } if in.StandbyDatabases != nil { in, out := &in.StandbyDatabases, &out.StandbyDatabases *out = make(map[string]string, len(*in)) diff --git a/apis/observability/v1alpha1/zz_generated.deepcopy.go b/apis/observability/v1alpha1/zz_generated.deepcopy.go index 39b438eb..23a349c0 100644 --- a/apis/observability/v1alpha1/zz_generated.deepcopy.go +++ b/apis/observability/v1alpha1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* ** Copyright (c) 2022 Oracle and/or its affiliates. diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml index e933d5a4..b57daa1c 100644 --- a/config/crd/bases/database.oracle.com_DbcsSystem.yaml +++ b/config/crd/bases/database.oracle.com_DbcsSystem.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: DbcsSystem.database.oracle.com spec: group: database.oracle.com @@ -19,22 +17,14 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: DbcsSystem is the Schema for the dbcssystems API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DbcsSystemSpec defines the desired state of DbcsSystem properties: dbSystem: properties: @@ -51,7 +41,6 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup COnfig Network Struct properties: autoBackupEnabled: type: boolean @@ -139,7 +128,6 @@ spec: - ociConfigMap type: object status: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: availabilityDomain: type: string @@ -153,7 +141,6 @@ spec: type: string dbInfo: items: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: dbHomeId: type: string @@ -219,9 +206,6 @@ spec: type: string timeStarted: type: string - required: - - operationId - - operationType type: object type: array required: @@ -232,9 +216,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml index bac3a28c..4d88297c 100644 --- a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: autonomouscontainerdatabases.database.oracle.com spec: group: database.oracle.com @@ -32,24 +30,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousContainerDatabaseSpec defines the desired state - of AutonomousContainerDatabase properties: action: enum: @@ -58,8 +46,6 @@ spec: - TERMINATE type: string autonomousContainerDatabaseOCID: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' type: string autonomousExadataVMClusterOCID: type: string @@ -75,7 +61,6 @@ spec: default: false type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -83,21 +68,14 @@ spec: type: string type: object patchModel: - description: 'AutonomousContainerDatabasePatchModelEnum Enum with - underlying type: string' enum: - RELEASE_UPDATES - RELEASE_UPDATE_REVISIONS type: string type: object status: - description: AutonomousContainerDatabaseStatus defines the observed state - of AutonomousContainerDatabase properties: lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string timeCreated: type: string @@ -109,9 +87,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml index a5c37507..41993eba 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: autonomousdatabasebackups.database.oracle.com spec: group: database.oracle.com @@ -38,24 +36,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseBackupSpec defines the desired state of - AutonomousDatabaseBackup properties: autonomousDatabaseBackupOCID: type: string @@ -64,7 +52,6 @@ spec: isLongTermBackup: type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -74,11 +61,8 @@ spec: retentionPeriodInDays: type: integer target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -91,8 +75,6 @@ spec: type: object type: object status: - description: AutonomousDatabaseBackupStatus defines the observed state - of AutonomousDatabaseBackup properties: autonomousDatabaseOCID: type: string @@ -105,16 +87,12 @@ spec: isAutomatic: type: boolean lifecycleState: - description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with - underlying type: string' type: string timeEnded: type: string timeStarted: type: string type: - description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying - type: string' type: string required: - autonomousDatabaseOCID @@ -130,9 +108,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml index 5e9f2c73..c6973e00 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: autonomousdatabaserestores.database.oracle.com spec: group: database.oracle.com @@ -32,27 +30,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseRestoreSpec defines the desired state of - AutonomousDatabaseRestore properties: ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -62,9 +49,6 @@ spec: source: properties: k8sADBBackup: - description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO - OWN! NOTE: json tags are required. Any new fields you add must - have json tags for the fields to be serialized.' properties: name: type: string @@ -72,17 +56,12 @@ spec: pointInTime: properties: timestamp: - description: 'The timestamp must follow this format: YYYY-MM-DD - HH:MM:SS GMT' type: string type: object type: object target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -98,18 +77,12 @@ spec: - target type: object status: - description: AutonomousDatabaseRestoreStatus defines the observed state - of AutonomousDatabaseRestore properties: dbName: type: string displayName: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string status: - description: 'WorkRequestStatusEnum Enum with underlying type: string' type: string timeAccepted: type: string @@ -130,9 +103,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index f77407f3..a9bd37cd 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: autonomousdatabases.database.oracle.com spec: group: database.oracle.com @@ -47,33 +45,20 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabase is the Schema for the autonomousdatabases - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase - Important: Run "make" to regenerate code after modifying this file' properties: details: - description: AutonomousDatabaseDetails defines the detail information - of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase properties: adminPassword: properties: k8sSecret: - description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -85,12 +70,8 @@ spec: type: object type: object autonomousContainerDatabase: - description: ACDSpec defines the spec of the target for backup/restore - runs. The name could be the name of an AutonomousDatabase or - an AutonomousDatabaseBackup properties: k8sACD: - description: "*********************** *\tACD specs ***********************" properties: name: type: string @@ -114,8 +95,6 @@ spec: dbVersion: type: string dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying - type: string' enum: - OLTP - DW @@ -133,15 +112,11 @@ spec: isDedicated: type: boolean licenseModel: - description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying - type: string' enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying - type: string' type: string networkAccess: properties: @@ -179,8 +154,6 @@ spec: password: properties: k8sSecret: - description: "*********************** *\tSecret specs - ***********************" properties: name: type: string @@ -197,7 +170,6 @@ spec: default: false type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -208,7 +180,6 @@ spec: - details type: object status: - description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase properties: allConnectionStrings: items: @@ -230,63 +201,29 @@ spec: type: array conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -302,9 +239,6 @@ spec: - type x-kubernetes-list-type: map lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string timeCreated: type: string @@ -316,9 +250,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_cdbs.yaml b/config/crd/bases/database.oracle.com_cdbs.yaml index 6b1c350c..b47bd7f2 100644 --- a/config/crd/bases/database.oracle.com_cdbs.yaml +++ b/config/crd/bases/database.oracle.com_cdbs.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: cdbs.database.oracle.com spec: group: database.oracle.com @@ -48,28 +46,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: CDB is the Schema for the cdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: CDBSpec defines the desired state of CDB properties: cdbAdminPwd: - description: Password for the CDB Administrator to manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -83,11 +71,8 @@ spec: - secret type: object cdbAdminUser: - description: User in the root container with sysdba priviledges to - manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -101,12 +86,10 @@ spec: - secret type: object cdbName: - description: Name of the CDB type: string cdbTlsCrt: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -122,7 +105,6 @@ spec: cdbTlsKey: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -136,40 +118,29 @@ spec: - secret type: object dbPort: - description: DB server port type: integer dbServer: - description: Name of the DB server type: string dbTnsurl: type: string nodeSelector: additionalProperties: type: string - description: Node Selector for running the Pod type: object ordsImage: - description: ORDS Image Name type: string ordsImagePullPolicy: - description: ORDS Image Pull Policy enum: - Always - Never type: string ordsImagePullSecret: - description: The name of the image pull secret in case of a private - docker repository. type: string ordsPort: - description: ORDS server port. For now, keep it as 8888. TO BE USED - IN FUTURE RELEASE. type: integer ordsPwd: - description: Password for user ORDS_PUBLIC_USER properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -183,16 +154,12 @@ spec: - secret type: object replicas: - description: Number of ORDS Containers to create type: integer serviceName: - description: Name of the CDB Service type: string sysAdminPwd: - description: Password for the CDB System Administrator properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -206,10 +173,8 @@ spec: - secret type: object webServerPwd: - description: Password for the Web Server User properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -223,11 +188,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -242,16 +204,12 @@ spec: type: object type: object status: - description: CDBStatus defines the observed state of CDB properties: msg: - description: Message type: string phase: - description: Phase of the CDB Resource type: string status: - description: CDB Resource Status type: boolean required: - phase @@ -262,9 +220,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index f19a3e22..2e60c6db 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: dataguardbrokers.database.oracle.com spec: group: database.oracle.com @@ -40,41 +38,23 @@ spec: - jsonPath: .status.status name: Status type: string + - jsonPath: .status.fastStartFailover + name: FSFO + type: string name: v1alpha1 schema: openAPIV3Schema: - description: DataguardBroker is the Schema for the dataguardbrokers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DataguardBrokerSpec defines the desired state of DataguardBroker properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - description: FSFO strategy - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object + fastStartFailover: + type: boolean loadBalancer: type: boolean nodeSelector: @@ -104,12 +84,17 @@ spec: - standbyDatabaseRefs type: object status: - description: DataguardBrokerStatus defines the observed state of DataguardBroker properties: clusterConnectString: type: string + databasesInDataguardConfig: + additionalProperties: + type: string + type: object externalConnectString: type: string + fastStartFailover: + type: boolean primaryDatabase: type: string primaryDatabaseRef: @@ -126,9 +111,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml index 121383fd..ab912020 100644 --- a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: oraclerestdataservices.database.oracle.com spec: group: database.oracle.com @@ -32,44 +30,22 @@ spec: - jsonPath: .status.apexUrl name: Apex URL type: string + - jsonPath: .status.mongoDbApiAccessUrl + name: MongoDbApi Access URL + type: string name: v1alpha1 schema: openAPIV3Schema: - description: OracleRestDataService is the Schema for the oraclerestdataservices - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService properties: adminPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - apexPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey properties: keepSecret: type: boolean @@ -84,8 +60,6 @@ spec: databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and - pullSecrets for POD properties: pullFrom: type: string @@ -98,6 +72,8 @@ spec: type: object loadBalancer: type: boolean + mongoDbApi: + type: boolean nodeSelector: additionalProperties: type: string @@ -105,8 +81,6 @@ spec: oracleService: type: string ordsPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey properties: keepSecret: type: boolean @@ -121,8 +95,6 @@ spec: ordsUser: type: string persistence: - description: OracleRestDataServicePersistence defines the storage - releated params properties: accessMode: enum: @@ -141,8 +113,6 @@ spec: type: integer restEnableSchemas: items: - description: OracleRestDataServicePDBSchemas defines the PDB Schemas - to be ORDS Enabled properties: enable: type: boolean @@ -169,8 +139,6 @@ spec: - ordsPassword type: object status: - description: OracleRestDataServiceStatus defines the observed state of - OracleRestDataService properties: apexConfigured: type: boolean @@ -185,8 +153,6 @@ spec: databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and - pullSecrets for POD properties: pullFrom: type: string @@ -199,6 +165,10 @@ spec: type: object loadBalancer: type: string + mongoDbApi: + type: boolean + mongoDbApiAccessUrl: + type: string ordsInstalled: type: boolean replicas: @@ -206,9 +176,6 @@ spec: serviceIP: type: string status: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string type: object type: object @@ -216,9 +183,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index 85af8c1b..38a6f877 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: pdbs.database.oracle.com spec: group: database.oracle.com @@ -48,26 +46,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: PDB is the Schema for the pdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: PDBSpec defines the desired state of PDB properties: action: - description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. - Map is used to map a Databse PDB to a Kubernetes PDB CR.' enum: - Create - Clone @@ -79,11 +67,8 @@ spec: - Map type: string adminName: - description: The administrator username for the new PDB. This property - is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -97,11 +82,8 @@ spec: - secret type: object adminPwd: - description: The administrator password for the new PDB. This property - is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -115,48 +97,31 @@ spec: - secret type: object asClone: - description: Indicate if 'AS CLONE' option should be used in the command - to plug in a PDB. This property is applicable when the Action property - is PLUG but not required. type: boolean assertivePdbDeletion: - description: turn on the assertive approach to delete pdb resource - kubectl delete pdb ..... automatically triggers the pluggable database - deletion type: boolean cdbName: - description: Name of the CDB type: string cdbNamespace: - description: CDB Namespace type: string cdbResName: - description: Name of the CDB Custom Resource that runs the ORDS container type: string copyAction: - description: To copy files or not while cloning a PDB enum: - COPY - NOCOPY - MOVE type: string dropAction: - description: Specify if datafiles should be removed or not. The value - can be INCLUDING or KEEP (default). enum: - INCLUDING - KEEP type: string fileNameConversions: - description: Relevant for Create and Plug operations. As defined in - the Oracle Multitenant Database documentation. Values can be a - filename convert pattern or NONE. type: string getScript: - description: Whether you need the script only or execute the script type: boolean modifyOption: - description: Extra options for opening and closing a PDB enum: - IMMEDIATE - NORMAL @@ -165,11 +130,8 @@ spec: - RESTRICTED type: string pdbName: - description: The name of the new PDB. Relevant for both Create and - Plug Actions. type: string pdbState: - description: The target state of the PDB enum: - OPEN - CLOSE @@ -177,7 +139,6 @@ spec: pdbTlsCat: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -193,7 +154,6 @@ spec: pdbTlsCrt: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -209,7 +169,6 @@ spec: pdbTlsKey: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -223,35 +182,22 @@ spec: - secret type: object reuseTempFile: - description: Whether to reuse temp file type: boolean sourceFileNameConversions: - description: This property is required when the Action property is - Plug. As defined in the Oracle Multitenant Database documentation. - Values can be a source filename convert pattern or NONE. type: string sparseClonePath: - description: A Path specified for sparse clone snapshot copy. (Optional) type: string srcPdbName: - description: Name of the Source PDB from which to clone type: string tdeExport: - description: TDE export for unplug operations type: boolean tdeImport: - description: TDE import for plug operations type: boolean tdeKeystorePath: - description: TDE keystore path is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. type: string tdePassword: - description: TDE password if the tdeImport or tdeExport flag is set - to true. Can be used in create, plug or unplug operations properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -265,11 +211,8 @@ spec: - secret type: object tdeSecret: - description: TDE secret is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -283,26 +226,14 @@ spec: - secret type: object tempSize: - description: Relevant for Create and Clone operations. Total size - for temporary tablespace as defined in the Oracle Multitenant Database - documentation. See size_clause description in Database SQL Language - Reference documentation. type: string totalSize: - description: Relevant for create and plug operations. Total size as - defined in the Oracle Multitenant Database documentation. See size_clause - description in Database SQL Language Reference documentation. type: string unlimitedStorage: - description: Relevant for Create and Plug operations. True for unlimited - storage. Even when set to true, totalSize and tempSize MUST be specified - in the request if Action is Create. type: boolean webServerPwd: - description: Password for the Web ServerPDB User properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -316,11 +247,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -334,37 +262,27 @@ spec: - secret type: object xmlFileName: - description: XML metadata filename to be used for Plug or Unplug operations type: string required: - action type: object status: - description: PDBStatus defines the observed state of PDB properties: action: - description: Last Completed Action type: string connString: - description: PDB Connect String type: string modifyOption: - description: Modify Option of the PDB type: string msg: - description: Message type: string openMode: - description: Open mode of the PDB type: string phase: - description: Phase of the PDB Resource type: string status: - description: PDB Resource Status type: boolean totalSize: - description: Total size of the PDB type: string required: - phase @@ -375,9 +293,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 641629a0..f0fb0e6f 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: shardingdatabases.database.oracle.com spec: group: database.oracle.com @@ -30,33 +28,22 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: ShardingDatabase is the Schema for the shardingdatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: InvitedNodeSubnet: type: string catalog: items: - description: CatalogSpec defines the desired state of CatalogSpec properties: envVars: items: - description: EnvironmentVariable represents a named variable - accessible for containers. properties: name: type: string @@ -68,8 +55,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image type: string isDelete: type: string @@ -92,23 +77,11 @@ spec: pvcName: type: string resources: - description: ResourceRequirements describes the compute resource - requirements. properties: claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. type: string required: - name @@ -124,8 +97,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -134,11 +105,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -155,7 +121,6 @@ spec: dbImagePullSecret: type: string dbSecret: - description: Secret Details properties: encryptionType: type: string @@ -183,17 +148,11 @@ spec: type: string gsm: items: - description: GsmSpec defines the desired state of GsmSpec properties: directorName: type: string envVars: - description: Replicas int32 `json:"replicas,omitempty"` // - Gsm Replicas. If you set OraGsmPvcName then it is set default - to 1. items: - description: EnvironmentVariable represents a named variable - accessible for containers. properties: name: type: string @@ -205,8 +164,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image type: string isDelete: type: string @@ -227,23 +184,11 @@ spec: region: type: string resources: - description: ResourceRequirements describes the compute resource - requirements. properties: claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. type: string required: - name @@ -259,8 +204,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -269,11 +212,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -291,7 +229,6 @@ spec: type: string gsmService: items: - description: Service Definition properties: available: type: string @@ -370,7 +307,6 @@ spec: type: array gsmShardSpace: items: - description: ShardSpace Specs properties: chunks: type: integer @@ -406,8 +342,6 @@ spec: type: string portMappings: items: - description: PortMapping is a specification of port mapping for - an application deployment. properties: port: format: int32 @@ -431,18 +365,12 @@ spec: scriptsLocation: type: string shard: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' items: - description: ShardSpec is a specification of Shards for an application - deployment. properties: deployAs: type: string envVars: items: - description: EnvironmentVariable represents a named variable - accessible for containers. properties: name: type: string @@ -454,8 +382,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image type: string isDelete: enum: @@ -483,23 +409,11 @@ spec: pvcName: type: string resources: - description: ResourceRequirements describes the compute resource - requirements. properties: claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. type: string required: - name @@ -515,8 +429,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -525,11 +437,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object shardGroup: @@ -571,8 +478,6 @@ spec: - shard type: object status: - description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 - ShardingDatabaseStatus defines the observed state of ShardingDatabase properties: catalogs: additionalProperties: @@ -580,63 +485,29 @@ spec: type: object conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -680,9 +551,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index 1c011e17..836a55a4 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: singleinstancedatabases.database.oracle.com spec: group: database.oracle.com @@ -53,27 +51,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase properties: adminPassword: - description: SingleInsatnceAdminPassword defines the secret containing - Admin Password mapped to secretKey for Database properties: keepSecret: type: boolean @@ -89,14 +76,14 @@ spec: type: boolean charset: type: string + convertToSnapshotStandby: + type: boolean createAs: enum: - primary - standby - clone type: string - dgBrokerConfigured: - type: boolean edition: enum: - standard @@ -111,8 +98,6 @@ spec: forceLog: type: boolean image: - description: SingleInstanceDatabaseImage defines the Image source - and pullSecrets for POD properties: prebuiltDB: type: boolean @@ -126,7 +111,6 @@ spec: - pullFrom type: object initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -148,8 +132,6 @@ spec: pdbName: type: string persistence: - description: SingleInstanceDatabasePersistence defines the storage - size and class for PVC properties: accessMode: enum: @@ -199,8 +181,6 @@ spec: type: string type: object sid: - description: SID must be alphanumeric (no special characters, only - a-z, A-Z, 0-9), and no longer than 12 characters. maxLength: 12 pattern: ^[a-zA-Z0-9]+$ type: string @@ -214,8 +194,6 @@ spec: - image type: object status: - description: SingleInstanceDatabaseStatus defines the observed state of - SingleInstanceDatabase properties: apexInstalled: type: boolean @@ -233,63 +211,29 @@ spec: type: string conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -306,6 +250,8 @@ spec: x-kubernetes-list-type: map connectString: type: string + convertToSnapshotStandby: + type: boolean createdAs: type: string datafilesCreated: @@ -314,8 +260,8 @@ spec: datafilesPatched: default: "false" type: string - dgBrokerConfigured: - type: boolean + dgBroker: + type: string edition: type: string flashBack: @@ -323,7 +269,6 @@ spec: forceLog: type: string initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -354,8 +299,6 @@ spec: pdbName: type: string persistence: - description: SingleInstanceDatabasePersistence defines the storage - size and class for PVC properties: accessMode: enum: @@ -413,9 +356,3 @@ spec: specReplicasPath: .spec.replicas statusReplicasPath: .status.replicas status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index b0801738..d058f116 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: databaseobservers.observability.oracle.com spec: group: observability.oracle.com @@ -26,26 +24,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: DatabaseObserver is the Schema for the databaseobservers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DatabaseObserverSpec defines the desired state of DatabaseObserver properties: database: - description: DatabaseObserverDatabase defines the database details - used for DatabaseObserver properties: dbConnectionString: properties: @@ -81,13 +69,10 @@ spec: type: object type: object exporter: - description: DatabaseObserverExporterConfig defines the configuration - details related to the exporters of DatabaseObserver properties: configuration: properties: configmap: - description: ConfigMapDetails defines the configmap name properties: configmapName: type: string @@ -98,8 +83,6 @@ spec: image: type: string service: - description: DatabaseObserverService defines the exporter service - component of DatabaseObserver properties: port: format: int32 @@ -114,8 +97,6 @@ spec: type: string type: object prometheus: - description: PrometheusConfig defines the generated resources for - Prometheus properties: labels: additionalProperties: @@ -129,70 +110,32 @@ spec: type: integer type: object status: - description: DatabaseObserverStatus defines the observed state of DatabaseObserver properties: conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -219,9 +162,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2aed83d4..1acf74e3 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: container-registry.oracle.com/database/operator - newTag: latest + newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database + newTag: rac-operator diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 2f33c915..9b2f8f04 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -1,9 +1,7 @@ - --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index c150867f..db18b511 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -1,9 +1,7 @@ - --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -191,12 +189,10 @@ webhooks: resources: - databaseobservers sideEffects: None - --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: diff --git a/go.mod b/go.mod index 1f30279f..90a91797 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/oracle/oracle-database-operator -go 1.21 +go 1.23.3 require ( - github.com/go-logr/logr v1.3.0 - github.com/onsi/ginkgo/v2 v2.13.0 - github.com/onsi/gomega v1.29.0 + github.com/go-logr/logr v1.4.1 + github.com/onsi/ginkgo/v2 v2.14.0 + github.com/onsi/gomega v1.30.0 github.com/oracle/oci-go-sdk/v65 v65.49.3 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0 go.uber.org/zap v1.26.0 @@ -16,8 +16,8 @@ require ( k8s.io/cli-runtime v0.29.2 k8s.io/client-go v0.29.2 k8s.io/kubectl v0.29.2 - sigs.k8s.io/controller-runtime v0.16.2 - sigs.k8s.io/yaml v1.3.0 + sigs.k8s.io/controller-runtime v0.17.3 + sigs.k8s.io/yaml v1.4.0 ) require ( @@ -29,13 +29,13 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/evanphx/json-patch/v5 v5.8.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fvbommel/sortorder v1.1.0 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect @@ -59,7 +59,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect @@ -70,10 +70,10 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.16.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.10.1 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/cobra v1.7.0 // indirect @@ -83,9 +83,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/term v0.15.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.16.1 // indirect @@ -94,7 +94,7 @@ require ( google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.28.0 // indirect + k8s.io/apiextensions-apiserver v0.29.2 // indirect k8s.io/component-base v0.29.2 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect diff --git a/go.sum b/go.sum index 08185f3e..e05155ae 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,6 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -31,23 +30,23 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= +github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= -github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= @@ -109,14 +108,12 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -128,8 +125,8 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -147,30 +144,29 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= -github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY= +github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/oracle/oci-go-sdk/v65 v65.49.3 h1:HHv+XMZiBYHtoU8Ac/fURdp9v1vJPPCpIbJAWeadREw= github.com/oracle/oci-go-sdk/v65 v65.49.3/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0 h1:55138zTXw/yRYizPxZ672I/aDD7Yte3uYRAfUjWUu2M= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0/go.mod h1:j51242bf6LQwvJ1JPKWApzTnifmCwcQq0i1p29ylWiM= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= -github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -199,17 +195,12 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -221,10 +212,8 @@ golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2F golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -234,19 +223,16 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -254,17 +240,12 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= @@ -283,7 +264,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -315,7 +295,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -330,8 +309,8 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= -k8s.io/apiextensions-apiserver v0.28.0 h1:CszgmBL8CizEnj4sj7/PtLGey6Na3YgWyGCPONv7E9E= -k8s.io/apiextensions-apiserver v0.28.0/go.mod h1:uRdYiwIuu0SyqJKriKmqEN2jThIJPhVmOWETm8ud1VE= +k8s.io/apiextensions-apiserver v0.29.2 h1:UK3xB5lOWSnhaCk0RFZ0LUacPZz9RY4wi/yt2Iu+btg= +k8s.io/apiextensions-apiserver v0.29.2/go.mod h1:aLfYjpA5p3OwtqNXQFkhJ56TB+spV8Gc4wfMhUA3/b8= k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/cli-runtime v0.29.2 h1:smfsOcT4QujeghsNjECKN3lwyX9AwcFU0nvJ7sFN3ro= @@ -348,8 +327,8 @@ k8s.io/kubectl v0.29.2 h1:uaDYaBhumvkwz0S2XHt36fK0v5IdNgL7HyUniwb2IUo= k8s.io/kubectl v0.29.2/go.mod h1:BhizuYBGcKaHWyq+G7txGw2fXg576QbPrrnQdQDZgqI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.16.2 h1:mwXAVuEk3EQf478PQwQ48zGOXvW27UJc8NHktQVuIPU= -sigs.k8s.io/controller-runtime v0.16.2/go.mod h1:vpMu3LpI5sYWtujJOa2uPK61nB5rbwlN7BAB8aSLvGU= +sigs.k8s.io/controller-runtime v0.17.3 h1:65QmN7r3FWgTxDMz9fvGnO1kbf2nu+acg9p2R9oYYYk= +sigs.k8s.io/controller-runtime v0.17.3/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= @@ -358,5 +337,5 @@ sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 504fc7cd..1a476814 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -9,8 +9,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: autonomouscontainerdatabases.database.oracle.com spec: group: database.oracle.com @@ -37,18 +36,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase properties: action: enum: @@ -57,7 +52,6 @@ spec: - TERMINATE type: string autonomousContainerDatabaseOCID: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' type: string autonomousExadataVMClusterOCID: type: string @@ -73,7 +67,6 @@ spec: default: false type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -81,17 +74,14 @@ spec: type: string type: object patchModel: - description: 'AutonomousContainerDatabasePatchModelEnum Enum with underlying type: string' enum: - RELEASE_UPDATES - RELEASE_UPDATE_REVISIONS type: string type: object status: - description: AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase properties: lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string timeCreated: type: string @@ -103,19 +93,12 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: autonomousdatabasebackups.database.oracle.com spec: group: database.oracle.com @@ -148,18 +131,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseBackupSpec defines the desired state of AutonomousDatabaseBackup properties: autonomousDatabaseBackupOCID: type: string @@ -168,7 +147,6 @@ spec: isLongTermBackup: type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -178,10 +156,8 @@ spec: retentionPeriodInDays: type: integer target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -194,7 +170,6 @@ spec: type: object type: object status: - description: AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup properties: autonomousDatabaseOCID: type: string @@ -207,14 +182,12 @@ spec: isAutomatic: type: boolean lifecycleState: - description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with underlying type: string' type: string timeEnded: type: string timeStarted: type: string type: - description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying type: string' type: string required: - autonomousDatabaseOCID @@ -230,19 +203,12 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: autonomousdatabaserestores.database.oracle.com spec: group: database.oracle.com @@ -269,21 +235,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore properties: ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -293,7 +254,6 @@ spec: source: properties: k8sADBBackup: - description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.' properties: name: type: string @@ -301,15 +261,12 @@ spec: pointInTime: properties: timestamp: - description: 'The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT' type: string type: object type: object target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -325,15 +282,12 @@ spec: - target type: object status: - description: AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore properties: dbName: type: string displayName: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string status: - description: 'WorkRequestStatusEnum Enum with underlying type: string' type: string timeAccepted: type: string @@ -354,19 +308,12 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: autonomousdatabases.database.oracle.com spec: group: database.oracle.com @@ -408,26 +355,20 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabase is the Schema for the autonomousdatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase Important: Run "make" to regenerate code after modifying this file' properties: details: - description: AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase properties: adminPassword: properties: k8sSecret: - description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -439,10 +380,8 @@ spec: type: object type: object autonomousContainerDatabase: - description: ACDSpec defines the spec of the target for backup/restore runs. The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup properties: k8sACD: - description: "*********************** *\tACD specs ***********************" properties: name: type: string @@ -466,7 +405,6 @@ spec: dbVersion: type: string dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying type: string' enum: - OLTP - DW @@ -484,13 +422,11 @@ spec: isDedicated: type: boolean licenseModel: - description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying type: string' enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' type: string networkAccess: properties: @@ -528,7 +464,6 @@ spec: password: properties: k8sSecret: - description: "*********************** *\tSecret specs ***********************" properties: name: type: string @@ -545,7 +480,6 @@ spec: default: false type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -556,7 +490,6 @@ spec: - details type: object status: - description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase properties: allConnectionStrings: items: @@ -578,36 +511,29 @@ spec: type: array conditions: items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -623,7 +549,6 @@ spec: - type x-kubernetes-list-type: map lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string timeCreated: type: string @@ -635,19 +560,13 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.6.1 + controller-gen.kubebuilder.io/version: v0.14.0 name: cdbs.database.oracle.com spec: group: database.oracle.com @@ -690,24 +609,18 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: CDB is the Schema for the cdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: CDBSpec defines the desired state of CDB properties: cdbAdminPwd: - description: Password for the CDB Administrator to manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -721,10 +634,8 @@ spec: - secret type: object cdbAdminUser: - description: User in the root container with sysdba priviledges to manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -738,12 +649,10 @@ spec: - secret type: object cdbName: - description: Name of the CDB type: string cdbTlsCrt: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -759,7 +668,6 @@ spec: cdbTlsKey: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -773,38 +681,29 @@ spec: - secret type: object dbPort: - description: DB server port type: integer dbServer: - description: Name of the DB server type: string dbTnsurl: type: string nodeSelector: additionalProperties: type: string - description: Node Selector for running the Pod type: object ordsImage: - description: ORDS Image Name type: string ordsImagePullPolicy: - description: ORDS Image Pull Policy enum: - Always - Never type: string ordsImagePullSecret: - description: The name of the image pull secret in case of a private docker repository. type: string ordsPort: - description: ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. type: integer ordsPwd: - description: Password for user ORDS_PUBLIC_USER properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -818,16 +717,12 @@ spec: - secret type: object replicas: - description: Number of ORDS Containers to create type: integer serviceName: - description: Name of the CDB Service type: string sysAdminPwd: - description: Password for the CDB System Administrator properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -841,10 +736,8 @@ spec: - secret type: object webServerPwd: - description: Password for the Web Server User properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -858,10 +751,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -876,16 +767,12 @@ spec: type: object type: object status: - description: CDBStatus defines the observed state of CDB properties: msg: - description: Message type: string phase: - description: Phase of the CDB Resource type: string status: - description: CDB Resource Status type: boolean required: - phase @@ -896,19 +783,12 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: databaseobservers.observability.oracle.com spec: group: observability.oracle.com @@ -929,21 +809,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: DatabaseObserver is the Schema for the databaseobservers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DatabaseObserverSpec defines the desired state of DatabaseObserver properties: database: - description: DatabaseObserverDatabase defines the database details used for DatabaseObserver properties: dbConnectionString: properties: @@ -979,12 +854,10 @@ spec: type: object type: object exporter: - description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver properties: configuration: properties: configmap: - description: ConfigMapDetails defines the configmap name properties: configmapName: type: string @@ -995,7 +868,6 @@ spec: image: type: string service: - description: DatabaseObserverService defines the exporter service component of DatabaseObserver properties: port: format: int32 @@ -1010,7 +882,6 @@ spec: type: string type: object prometheus: - description: PrometheusConfig defines the generated resources for Prometheus properties: labels: additionalProperties: @@ -1024,41 +895,32 @@ spec: type: integer type: object status: - description: DatabaseObserverStatus defines the observed state of DatabaseObserver properties: conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1085,19 +947,12 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: dataguardbrokers.database.oracle.com spec: group: database.oracle.com @@ -1132,37 +987,23 @@ spec: - jsonPath: .status.status name: Status type: string + - jsonPath: .status.fastStartFailover + name: FSFO + type: string name: v1alpha1 schema: openAPIV3Schema: - description: DataguardBroker is the Schema for the dataguardbrokers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DataguardBrokerSpec defines the desired state of DataguardBroker properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - description: FSFO strategy - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object + fastStartFailover: + type: boolean loadBalancer: type: boolean nodeSelector: @@ -1192,12 +1033,17 @@ spec: - standbyDatabaseRefs type: object status: - description: DataguardBrokerStatus defines the observed state of DataguardBroker properties: clusterConnectString: type: string + databasesInDataguardConfig: + additionalProperties: + type: string + type: object externalConnectString: type: string + fastStartFailover: + type: boolean primaryDatabase: type: string primaryDatabaseRef: @@ -1214,12 +1060,6 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -1243,10 +1083,14 @@ spec: description: DbcsSystem is the Schema for the dbcssystems API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -1460,8 +1304,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: oraclerestdataservices.database.oracle.com spec: group: database.oracle.com @@ -1488,37 +1331,22 @@ spec: - jsonPath: .status.apexUrl name: Apex URL type: string + - jsonPath: .status.mongoDbApiAccessUrl + name: MongoDbApi Access URL + type: string name: v1alpha1 schema: openAPIV3Schema: - description: OracleRestDataService is the Schema for the oraclerestdataservices API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService properties: adminPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - apexPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1533,7 +1361,6 @@ spec: databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD properties: pullFrom: type: string @@ -1546,6 +1373,8 @@ spec: type: object loadBalancer: type: boolean + mongoDbApi: + type: boolean nodeSelector: additionalProperties: type: string @@ -1553,7 +1382,6 @@ spec: oracleService: type: string ordsPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey properties: keepSecret: type: boolean @@ -1568,7 +1396,6 @@ spec: ordsUser: type: string persistence: - description: OracleRestDataServicePersistence defines the storage releated params properties: accessMode: enum: @@ -1587,7 +1414,6 @@ spec: type: integer restEnableSchemas: items: - description: OracleRestDataServicePDBSchemas defines the PDB Schemas to be ORDS Enabled properties: enable: type: boolean @@ -1614,7 +1440,6 @@ spec: - ordsPassword type: object status: - description: OracleRestDataServiceStatus defines the observed state of OracleRestDataService properties: apexConfigured: type: boolean @@ -1629,7 +1454,6 @@ spec: databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD properties: pullFrom: type: string @@ -1642,6 +1466,10 @@ spec: type: object loadBalancer: type: string + mongoDbApi: + type: boolean + mongoDbApiAccessUrl: + type: string ordsInstalled: type: boolean replicas: @@ -1649,7 +1477,6 @@ spec: serviceIP: type: string status: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string type: object type: object @@ -1657,19 +1484,13 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.6.1 + controller-gen.kubebuilder.io/version: v0.14.0 name: pdbs.database.oracle.com spec: group: database.oracle.com @@ -1712,21 +1533,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: PDB is the Schema for the pdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: PDBSpec defines the desired state of PDB properties: action: - description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR.' enum: - Create - Clone @@ -1738,10 +1554,8 @@ spec: - Map type: string adminName: - description: The administrator username for the new PDB. This property is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1755,10 +1569,8 @@ spec: - secret type: object adminPwd: - description: The administrator password for the new PDB. This property is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1772,41 +1584,31 @@ spec: - secret type: object asClone: - description: Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. type: boolean assertivePdbDeletion: - description: turn on the assertive approach to delete pdb resource kubectl delete pdb ..... automatically triggers the pluggable database deletion type: boolean cdbName: - description: Name of the CDB type: string cdbNamespace: - description: CDB Namespace type: string cdbResName: - description: Name of the CDB Custom Resource that runs the ORDS container type: string copyAction: - description: To copy files or not while cloning a PDB enum: - COPY - NOCOPY - MOVE type: string dropAction: - description: Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). enum: - INCLUDING - KEEP type: string fileNameConversions: - description: Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. type: string getScript: - description: Whether you need the script only or execute the script type: boolean modifyOption: - description: Extra options for opening and closing a PDB enum: - IMMEDIATE - NORMAL @@ -1815,10 +1617,8 @@ spec: - RESTRICTED type: string pdbName: - description: The name of the new PDB. Relevant for both Create and Plug Actions. type: string pdbState: - description: The target state of the PDB enum: - OPEN - CLOSE @@ -1826,7 +1626,6 @@ spec: pdbTlsCat: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1842,7 +1641,6 @@ spec: pdbTlsCrt: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1858,7 +1656,6 @@ spec: pdbTlsKey: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1872,31 +1669,22 @@ spec: - secret type: object reuseTempFile: - description: Whether to reuse temp file type: boolean sourceFileNameConversions: - description: This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. type: string sparseClonePath: - description: A Path specified for sparse clone snapshot copy. (Optional) type: string srcPdbName: - description: Name of the Source PDB from which to clone type: string tdeExport: - description: TDE export for unplug operations type: boolean tdeImport: - description: TDE import for plug operations type: boolean tdeKeystorePath: - description: TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. type: string tdePassword: - description: TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1910,10 +1698,8 @@ spec: - secret type: object tdeSecret: - description: TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1927,19 +1713,14 @@ spec: - secret type: object tempSize: - description: Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. type: string totalSize: - description: Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. type: string unlimitedStorage: - description: Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. type: boolean webServerPwd: - description: Password for the Web ServerPDB User properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1953,10 +1734,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -1970,37 +1749,27 @@ spec: - secret type: object xmlFileName: - description: XML metadata filename to be used for Plug or Unplug operations type: string required: - action type: object status: - description: PDBStatus defines the observed state of PDB properties: action: - description: Last Completed Action type: string connString: - description: PDB Connect String type: string modifyOption: - description: Modify Option of the PDB type: string msg: - description: Message type: string openMode: - description: Open mode of the PDB type: string phase: - description: Phase of the PDB Resource type: string status: - description: PDB Resource Status type: boolean totalSize: - description: Total size of the PDB type: string required: - phase @@ -2011,19 +1780,12 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: shardingdatabases.database.oracle.com spec: group: database.oracle.com @@ -2048,28 +1810,22 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: ShardingDatabase is the Schema for the shardingdatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: ShardingDatabaseSpec defines the desired state of ShardingDatabase properties: InvitedNodeSubnet: type: string catalog: items: - description: CatalogSpec defines the desired state of CatalogSpec properties: envVars: items: - description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2081,7 +1837,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: type: string @@ -2104,15 +1859,11 @@ spec: pvcName: type: string resources: - description: ResourceRequirements describes the compute resource requirements. properties: claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2128,7 +1879,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2137,7 +1887,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -2154,7 +1903,6 @@ spec: dbImagePullSecret: type: string dbSecret: - description: Secret Details properties: encryptionType: type: string @@ -2182,14 +1930,11 @@ spec: type: string gsm: items: - description: GsmSpec defines the desired state of GsmSpec properties: directorName: type: string envVars: - description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. items: - description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2201,7 +1946,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: type: string @@ -2222,15 +1966,11 @@ spec: region: type: string resources: - description: ResourceRequirements describes the compute resource requirements. properties: claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2246,7 +1986,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2255,7 +1994,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object storageSizeInGb: @@ -2273,7 +2011,6 @@ spec: type: string gsmService: items: - description: Service Definition properties: available: type: string @@ -2352,7 +2089,6 @@ spec: type: array gsmShardSpace: items: - description: ShardSpace Specs properties: chunks: type: integer @@ -2388,7 +2124,6 @@ spec: type: string portMappings: items: - description: PortMapping is a specification of port mapping for an application deployment. properties: port: format: int32 @@ -2412,15 +2147,12 @@ spec: scriptsLocation: type: string shard: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' items: - description: ShardSpec is a specification of Shards for an application deployment. properties: deployAs: type: string envVars: items: - description: EnvironmentVariable represents a named variable accessible for containers. properties: name: type: string @@ -2432,7 +2164,6 @@ spec: type: object type: array imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image type: string isDelete: enum: @@ -2460,15 +2191,11 @@ spec: pvcName: type: string resources: - description: ResourceRequirements describes the compute resource requirements. properties: claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. type: string required: - name @@ -2484,7 +2211,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -2493,7 +2219,6 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object shardGroup: @@ -2535,7 +2260,6 @@ spec: - shard type: object status: - description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 ShardingDatabaseStatus defines the observed state of ShardingDatabase properties: catalogs: additionalProperties: @@ -2543,36 +2267,29 @@ spec: type: object conditions: items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2616,19 +2333,13 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.6.1 + controller-gen.kubebuilder.io/version: v0.14.0 name: singleinstancedatabases.database.oracle.com spec: group: database.oracle.com @@ -2676,21 +2387,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase properties: adminPassword: - description: SingleInsatnceAdminPassword defines the secret containing Admin Password mapped to secretKey for Database properties: keepSecret: type: boolean @@ -2706,14 +2412,14 @@ spec: type: boolean charset: type: string + convertToSnapshotStandby: + type: boolean createAs: enum: - primary - standby - clone type: string - dgBrokerConfigured: - type: boolean edition: enum: - standard @@ -2728,7 +2434,6 @@ spec: forceLog: type: boolean image: - description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD properties: prebuiltDB: type: boolean @@ -2742,7 +2447,6 @@ spec: - pullFrom type: object initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -2764,7 +2468,6 @@ spec: pdbName: type: string persistence: - description: SingleInstanceDatabasePersistence defines the storage size and class for PVC properties: accessMode: enum: @@ -2814,7 +2517,6 @@ spec: type: string type: object sid: - description: SID must be alphanumeric (no special characters, only a-z, A-Z, 0-9), and no longer than 12 characters. maxLength: 12 pattern: ^[a-zA-Z0-9]+$ type: string @@ -2828,7 +2530,6 @@ spec: - image type: object status: - description: SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase properties: apexInstalled: type: boolean @@ -2846,36 +2547,29 @@ spec: type: string conditions: items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2892,6 +2586,8 @@ spec: x-kubernetes-list-type: map connectString: type: string + convertToSnapshotStandby: + type: boolean createdAs: type: string datafilesCreated: @@ -2900,8 +2596,8 @@ spec: datafilesPatched: default: "false" type: string - dgBrokerConfigured: - type: boolean + dgBroker: + type: string edition: type: string flashBack: @@ -2909,7 +2605,6 @@ spec: forceLog: type: string initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters properties: cpuCount: type: integer @@ -2940,7 +2635,6 @@ spec: pdbName: type: string persistence: - description: SingleInstanceDatabasePersistence defines the storage size and class for PVC properties: accessMode: enum: @@ -2998,12 +2692,6 @@ spec: specReplicasPath: .spec.replicas statusReplicasPath: .status.replicas status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -3054,26 +2742,8 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: oracle-database-operator-manager-role rules: -- apiGroups: - - "" - resources: - - configmaps - - deployments - - events - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - "" resources: @@ -4184,7 +3854,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: container-registry.oracle.com/database/operator:latest + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:rac-operator imagePullPolicy: Always name: manager ports: From 14907b349a5ecf316ae03a6ee476ae2a256721fa Mon Sep 17 00:00:00 2001 From: racpack Date: Mon, 2 Dec 2024 21:53:29 +0000 Subject: [PATCH 128/414] Psaini v4api --- PROJECT | 19 + .../v1alpha1/dbcssystem_conversion.go | 14 + .../database/v1alpha1/dbcssystem_kms_types.go | 141 ++ .../v1alpha1/dbcssystem_pdbconfig_types.go | 83 + apis/database/v1alpha1/dbcssystem_types.go | 100 +- .../v1alpha1/shardingdatabase_conversion.go | 14 + .../v1alpha1/zz_generated.deepcopy.go | 229 +++ apis/database/v4/dbcssystem_conversion.go | 4 + apis/database/v4/dbcssystem_kms_types.go | 141 ++ .../database/v4/dbcssystem_pdbconfig_types.go | 83 + apis/database/v4/dbcssystem_types.go | 291 ++++ apis/database/v4/dbcssystem_webhook.go | 98 ++ apis/database/v4/groupversion_info.go | 58 + .../v4/shardingdatabase_conversion.go | 4 + apis/database/v4/shardingdatabase_types.go | 405 +++++ .../shardingdatabase_webhook.go | 6 +- apis/database/v4/zz_generated.deepcopy.go | 1023 +++++++++++++ commons/dbcssystem/dbcs_reconciler.go | 945 ++++++++++-- commons/dbcssystem/dcommon.go | 61 +- commons/sharding/catalog.go | 34 +- commons/sharding/exec.go | 8 +- commons/sharding/gsm.go | 38 +- commons/sharding/provstatus.go | 222 +-- commons/sharding/scommon.go | 103 +- commons/sharding/shard.go | 32 +- .../bases/database.oracle.com_DbcsSystem.yaml | 548 ++++++- ...acle.com_autonomouscontainerdatabases.yaml | 2 +- ....oracle.com_autonomousdatabasebackups.yaml | 2 +- ...oracle.com_autonomousdatabaserestores.yaml | 2 +- ...tabase.oracle.com_autonomousdatabases.yaml | 2 +- .../crd/bases/database.oracle.com_cdbs.yaml | 2 +- .../database.oracle.com_dataguardbrokers.yaml | 2 +- ...ase.oracle.com_oraclerestdataservices.yaml | 2 +- .../crd/bases/database.oracle.com_pdbs.yaml | 2 +- ...database.oracle.com_shardingdatabases.yaml | 539 ++++++- ...se.oracle.com_singleinstancedatabases.yaml | 2 +- ...vability.oracle.com_databaseobservers.yaml | 2 +- .../cainjection_in_database_dbcssystems.yaml | 8 + ...jection_in_database_shardingdatabases.yaml | 8 + config/rbac/role.yaml | 384 +---- config/samples/database_v4_dbcssystem.yaml | 6 + .../samples/database_v4_shardingdatabase.yaml | 6 + config/samples/kustomization.yaml | 2 + config/webhook/manifests.yaml | 52 +- controllers/database/dbcssystem_controller.go | 1338 +++++++++++++++-- .../database/shardingdatabase_controller.go | 190 +-- main.go | 8 +- 47 files changed, 6333 insertions(+), 932 deletions(-) create mode 100644 apis/database/v1alpha1/dbcssystem_conversion.go create mode 100644 apis/database/v1alpha1/dbcssystem_kms_types.go create mode 100644 apis/database/v1alpha1/dbcssystem_pdbconfig_types.go create mode 100644 apis/database/v1alpha1/shardingdatabase_conversion.go create mode 100644 apis/database/v4/dbcssystem_conversion.go create mode 100644 apis/database/v4/dbcssystem_kms_types.go create mode 100644 apis/database/v4/dbcssystem_pdbconfig_types.go create mode 100644 apis/database/v4/dbcssystem_types.go create mode 100644 apis/database/v4/dbcssystem_webhook.go create mode 100644 apis/database/v4/groupversion_info.go create mode 100644 apis/database/v4/shardingdatabase_conversion.go create mode 100644 apis/database/v4/shardingdatabase_types.go rename apis/database/{v1alpha1 => v4}/shardingdatabase_webhook.go (95%) create mode 100644 apis/database/v4/zz_generated.deepcopy.go create mode 100644 config/crd/patches/cainjection_in_database_dbcssystems.yaml create mode 100644 config/crd/patches/cainjection_in_database_shardingdatabases.yaml create mode 100644 config/samples/database_v4_dbcssystem.yaml create mode 100644 config/samples/database_v4_shardingdatabase.yaml diff --git a/PROJECT b/PROJECT index fbf861db..f4e75898 100644 --- a/PROJECT +++ b/PROJECT @@ -157,4 +157,23 @@ resources: defaulting: true validation: true webhookVersion: v1beta1 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: database + kind: ShardingDatabase + path: github.com/oracle/oracle-database-operator/apis/database/v4 + version: v4 + webhooks: + conversion: true + webhookVersion: v1beta1 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: database + kind: DbcsSystem + path: github.com/oracle/oracle-database-operator/apis/database/v4 + version: v4 version: "3" diff --git a/apis/database/v1alpha1/dbcssystem_conversion.go b/apis/database/v1alpha1/dbcssystem_conversion.go new file mode 100644 index 00000000..7e4a25c8 --- /dev/null +++ b/apis/database/v1alpha1/dbcssystem_conversion.go @@ -0,0 +1,14 @@ +package v1alpha1 + +import ( + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *DbcsSystem) ConvertTo(dst conversion.Hub) error { + return nil +} + +// ConvertFrom converts v1 to v1alpha1 +func (dst *DbcsSystem) ConvertFrom(src conversion.Hub) error { + return nil +} diff --git a/apis/database/v1alpha1/dbcssystem_kms_types.go b/apis/database/v1alpha1/dbcssystem_kms_types.go new file mode 100644 index 00000000..c90726e3 --- /dev/null +++ b/apis/database/v1alpha1/dbcssystem_kms_types.go @@ -0,0 +1,141 @@ +/* +** Copyright (c) 2022-2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ +package v1alpha1 + +import "encoding/json" + +type KMSConfig struct { + VaultName string `json:"vaultName,omitempty"` + CompartmentId string `json:"compartmentId,omitempty"` + KeyName string `json:"keyName,omitempty"` + EncryptionAlgo string `json:"encryptionAlgo,omitempty"` + VaultType string `json:"vaultType,omitempty"` +} +type KMSDetailsStatus struct { + VaultId string `json:"vaultId,omitempty"` + ManagementEndpoint string `json:"managementEndpoint,omitempty"` + KeyId string `json:"keyId,omitempty"` + VaultName string `json:"vaultName,omitempty"` + CompartmentId string `json:"compartmentId,omitempty"` + KeyName string `json:"keyName,omitempty"` + EncryptionAlgo string `json:"encryptionAlgo,omitempty"` + VaultType string `json:"vaultType,omitempty"` +} + +const ( + lastSuccessfulKMSConfig = "lastSuccessfulKMSConfig" + lastSuccessfulKMSStatus = "lastSuccessfulKMSStatus" +) + +// GetLastSuccessfulKMSConfig returns the KMS config from the last successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulKMSConfig. +func (dbcs *DbcsSystem) GetLastSuccessfulKMSConfig() (*KMSConfig, error) { + val, ok := dbcs.GetAnnotations()[lastSuccessfulKMSConfig] + if !ok { + return nil, nil + } + + configBytes := []byte(val) + kmsConfig := KMSConfig{} + + err := json.Unmarshal(configBytes, &kmsConfig) + if err != nil { + return nil, err + } + + return &kmsConfig, nil +} + +// GetLastSuccessfulKMSStatus returns the KMS status from the last successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulKMSStatus. +func (dbcs *DbcsSystem) GetLastSuccessfulKMSStatus() (*KMSDetailsStatus, error) { + val, ok := dbcs.GetAnnotations()[lastSuccessfulKMSStatus] + if !ok { + return nil, nil + } + + statusBytes := []byte(val) + kmsStatus := KMSDetailsStatus{} + + err := json.Unmarshal(statusBytes, &kmsStatus) + if err != nil { + return nil, err + } + + return &kmsStatus, nil +} + +// SetLastSuccessfulKMSConfig saves the given KMSConfig to the annotations. +func (dbcs *DbcsSystem) SetLastSuccessfulKMSConfig(kmsConfig *KMSConfig) error { + configBytes, err := json.Marshal(kmsConfig) + if err != nil { + return err + } + + annotations := dbcs.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations[lastSuccessfulKMSConfig] = string(configBytes) + dbcs.SetAnnotations(annotations) + return nil +} + +// SetLastSuccessfulKMSStatus saves the given KMSDetailsStatus to the annotations. +func (dbcs *DbcsSystem) SetLastSuccessfulKMSStatus(kmsStatus *KMSDetailsStatus) error { + statusBytes, err := json.Marshal(kmsStatus) + if err != nil { + return err + } + + annotations := dbcs.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations[lastSuccessfulKMSStatus] = string(statusBytes) + dbcs.SetAnnotations(annotations) + // Update KMSDetailsStatus in DbcsSystemStatus + dbcs.Status.KMSDetailsStatus = KMSDetailsStatus{ + VaultName: kmsStatus.VaultName, + CompartmentId: kmsStatus.CompartmentId, + KeyName: kmsStatus.KeyName, + EncryptionAlgo: kmsStatus.EncryptionAlgo, + VaultType: kmsStatus.VaultType, + } + return nil +} diff --git a/apis/database/v1alpha1/dbcssystem_pdbconfig_types.go b/apis/database/v1alpha1/dbcssystem_pdbconfig_types.go new file mode 100644 index 00000000..1b745e09 --- /dev/null +++ b/apis/database/v1alpha1/dbcssystem_pdbconfig_types.go @@ -0,0 +1,83 @@ +/* +** Copyright (c) 2022-2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ +package v1alpha1 + +// PDBConfig defines details of PDB struct for DBCS systems +type PDBConfig struct { + // The name for the pluggable database (PDB). The name is unique in the context of a Database. The name must begin with an alphabetic character and can contain a maximum of thirty alphanumeric characters. Special characters are not permitted. The pluggable database name should not be same as the container database name. + PdbName *string `mandatory:"true" json:"pdbName"` + + // The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the CDB + // ContainerDatabaseId *string `mandatory:"false" json:"containerDatabaseId"` + + // // A strong password for PDB Admin. The password must be at least nine characters and contain at least two uppercase, two lowercase, two numbers, and two special characters. The special characters must be _, \#, or -. + PdbAdminPassword *string `mandatory:"false" json:"pdbAdminPassword"` + + // // The existing TDE wallet password of the CDB. + TdeWalletPassword *string `mandatory:"false" json:"tdeWalletPassword"` + + // // The locked mode of the pluggable database admin account. If false, the user needs to provide the PDB Admin Password to connect to it. + // // If true, the pluggable database will be locked and user cannot login to it. + ShouldPdbAdminAccountBeLocked *bool `mandatory:"false" json:"shouldPdbAdminAccountBeLocked"` + + // // Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. + // // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). + // // Example: `{"Department": "Finance"}` + FreeformTags map[string]string `mandatory:"false" json:"freeformTags"` + + // // Defined tags for this resource. Each key is predefined and scoped to a namespace. + // // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). + // DefinedTags map[string]map[string]interface{} `mandatory:"false" json:"definedTags"` + + // To specify whether to delete the PDB + IsDelete *bool `mandatory:"false" json:"isDelete,omitempty"` + + // The OCID of the PDB for deletion purposes. + PluggableDatabaseId *string `mandatory:"false" json:"pluggableDatabaseId,omitempty"` +} + +type PDBConfigStatus struct { + PdbName *string `mandatory:"true" json:"pdbName"` + ShouldPdbAdminAccountBeLocked *bool `mandatory:"false" json:"shouldPdbAdminAccountBeLocked"` + FreeformTags map[string]string `mandatory:"false" json:"freeformTags"` + PluggableDatabaseId *string `mandatory:"false" json:"pluggableDatabaseId,omitempty"` + PdbLifecycleState LifecycleState `json:"pdbState,omitempty"` +} +type PDBDetailsStatus struct { + PDBConfigStatus []PDBConfigStatus `json:"pdbConfigStatus,omitempty"` +} diff --git a/apis/database/v1alpha1/dbcssystem_types.go b/apis/database/v1alpha1/dbcssystem_types.go index 52d725b2..a59ab8d6 100644 --- a/apis/database/v1alpha1/dbcssystem_types.go +++ b/apis/database/v1alpha1/dbcssystem_types.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2022 Oracle and/or its affiliates. +** Copyright (c) 2022-2024 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -42,6 +42,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/go-logr/logr" dbcsv1 "github.com/oracle/oracle-database-operator/commons/annotations" "sigs.k8s.io/controller-runtime/pkg/client" @@ -52,11 +53,17 @@ import ( // DbcsSystemSpec defines the desired state of DbcsSystem type DbcsSystemSpec struct { - DbSystem DbSystemDetails `json:"dbSystem,omitempty"` - Id *string `json:"id,omitempty"` - OCIConfigMap string `json:"ociConfigMap"` - OCISecret string `json:"ociSecret,omitempty"` - HardLink bool `json:"hardLink,omitempty"` + DbSystem DbSystemDetails `json:"dbSystem,omitempty"` + Id *string `json:"id,omitempty"` + OCIConfigMap *string `json:"ociConfigMap"` + OCISecret *string `json:"ociSecret,omitempty"` + DbClone *DbCloneConfig `json:"dbClone,omitempty"` + HardLink bool `json:"hardLink,omitempty"` + PdbConfigs []PDBConfig `json:"pdbConfigs,omitempty"` + SetupDBCloning bool `json:"setupDBCloning,omitempty"` + DbBackupId *string `json:"dbBackupId,omitempty"` + DatabaseId *string `json:"databaseId,omitempty"` + KMSConfig KMSConfig `json:"kmsConfig,omitempty"` } // DbSystemDetails Spec @@ -66,7 +73,7 @@ type DbSystemDetails struct { AvailabilityDomain string `json:"availabilityDomain"` SubnetId string `json:"subnetId"` Shape string `json:"shape"` - SshPublicKeys []string `json:"sshPublicKeys"` + SshPublicKeys []string `json:"sshPublicKeys,omitempty"` HostName string `json:"hostName"` CpuCoreCount int `json:"cpuCoreCount,omitempty"` FaultDomains []string `json:"faultDomains,omitempty"` @@ -78,8 +85,6 @@ type DbSystemDetails struct { Domain string `json:"domain,omitempty"` InitialDataStorageSizeInGB int `json:"initialDataStorageSizeInGB,omitempty"` ClusterName string `json:"clusterName,omitempty"` - KmsKeyId string `json:"kmsKeyId,omitempty"` - KmsKeyVersionId string `json:"kmsKeyVersionId,omitempty"` DbAdminPaswordSecret string `json:"dbAdminPaswordSecret"` DbName string `json:"dbName,omitempty"` PdbName string `json:"pdbName,omitempty"` @@ -94,9 +99,10 @@ type DbSystemDetails struct { TdeWalletPasswordSecret string `json:"tdeWalletPasswordSecret,omitempty"` Tags map[string]string `json:"tags,omitempty"` DbBackupConfig Backupconfig `json:"dbBackupConfig,omitempty"` + KMSConfig KMSConfig `json:"kmsConfig,omitempty"` } -// DB Backup COnfig Network Struct +// DB Backup Config Network Struct type Backupconfig struct { AutoBackupEnabled *bool `json:"autoBackupEnabled,omitempty"` RecoveryWindowsInDays *int `json:"recoveryWindowsInDays,omitempty"` @@ -121,11 +127,14 @@ type DbcsSystemStatus struct { DataStorageSizeInGBs *int `json:"dataStorageSizeInGBs,omitempty"` RecoStorageSizeInGB *int `json:"recoStorageSizeInGB,omitempty"` - Shape *string `json:"shape,omitempty"` - State LifecycleState `json:"state"` - DbInfo []DbStatus `json:"dbInfo,omitempty"` - Network VmNetworkDetails `json:"network,omitempty"` - WorkRequests []DbWorkrequests `json:"workRequests,omitempty"` + Shape *string `json:"shape,omitempty"` + State LifecycleState `json:"state"` + DbInfo []DbStatus `json:"dbInfo,omitempty"` + Network VmNetworkDetails `json:"network,omitempty"` + WorkRequests []DbWorkrequests `json:"workRequests,omitempty"` + KMSDetailsStatus KMSDetailsStatus `json:"kmsDetailsStatus,omitempty"` + DbCloneStatus DbCloneStatus `json:"dbCloneStatus,omitempty"` + PdbDetailsStatus []PDBDetailsStatus `json:"pdbDetailsStatus,omitempty"` } // DbcsSystemStatus defines the observed state of DbcsSystem @@ -138,8 +147,8 @@ type DbStatus struct { } type DbWorkrequests struct { - OperationType *string `json:"operationType,omitempty"` - OperationId *string `json:"operationId,omitempty"` + OperationType *string `json:"operationType,omitmpty"` + OperationId *string `json:"operationId,omitemty"` PercentComplete string `json:"percentComplete,omitempty"` TimeAccepted string `json:"timeAccepted,omitempty"` TimeStarted string `json:"timeStarted,omitempty"` @@ -156,6 +165,39 @@ type VmNetworkDetails struct { NetworkSG string `json:"networkSG,omitempty"` } +// DbCloneConfig defines the configuration for the database clone +type DbCloneConfig struct { + DbAdminPaswordSecret string `json:"dbAdminPaswordSecret,omitempty"` + TdeWalletPasswordSecret string `json:"tdeWalletPasswordSecret,omitempty"` + DbName string `json:"dbName"` + HostName string `json:"hostName"` + DbUniqueName string `json:"dbDbUniqueName"` + DisplayName string `json:"displayName"` + LicenseModel string `json:"licenseModel,omitempty"` + Domain string `json:"domain,omitempty"` + SshPublicKeys []string `json:"sshPublicKeys,omitempty"` + SubnetId string `json:"subnetId"` + SidPrefix string `json:"sidPrefix,omitempty"` + InitialDataStorageSizeInGB int `json:"initialDataStorageSizeInGB,omitempty"` + KmsKeyId string `json:"kmsKeyId,omitempty"` + KmsKeyVersionId string `json:"kmsKeyVersionId,omitempty"` + PrivateIp string `json:"privateIp,omitempty"` +} + +// DbCloneStatus defines the observed state of DbClone +type DbCloneStatus struct { + Id *string `json:"id,omitempty"` + DbAdminPaswordSecret string `json:"dbAdminPaswordSecret,omitempty"` + DbName string `json:"dbName,omitempty"` + HostName string `json:"hostName"` + DbUniqueName string `json:"dbDbUniqueName"` + DisplayName string `json:"displayName,omitempty"` + LicenseModel string `json:"licenseModel,omitempty"` + Domain string `json:"domain,omitempty"` + SshPublicKeys []string `json:"sshPublicKeys,omitempty"` + SubnetId string `json:"subnetId,omitempty"` +} + // +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:resource:path=DbcsSystem,scope=Namespaced @@ -164,9 +206,8 @@ type VmNetworkDetails struct { type DbcsSystem struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec DbcsSystemSpec `json:"spec,omitempty"` - Status DbcsSystemStatus `json:"status,omitempty"` + Spec DbcsSystemSpec `json:"spec,omitempty"` + Status DbcsSystemStatus `json:"status,omitempty"` } //+kubebuilder:object:root=true @@ -208,6 +249,25 @@ func (dbcs *DbcsSystem) GetLastSuccessfulSpec() (*DbcsSystemSpec, error) { return &sucSpec, nil } +func (dbcs *DbcsSystem) GetLastSuccessfulSpecWithLog(log logr.Logger) (*DbcsSystemSpec, error) { + val, ok := dbcs.GetAnnotations()[lastSuccessfulSpec] + if !ok { + log.Info("No last successful spec annotation found") + return nil, nil + } + + specBytes := []byte(val) + sucSpec := DbcsSystemSpec{} + + err := json.Unmarshal(specBytes, &sucSpec) + if err != nil { + log.Error(err, "Failed to unmarshal last successful spec") + return nil, err + } + + log.Info("Successfully retrieved last successful spec", "spec", sucSpec) + return &sucSpec, nil +} // UpdateLastSuccessfulSpec updates lastSuccessfulSpec with the current spec. func (dbcs *DbcsSystem) UpdateLastSuccessfulSpec(kubeClient client.Client) error { diff --git a/apis/database/v1alpha1/shardingdatabase_conversion.go b/apis/database/v1alpha1/shardingdatabase_conversion.go new file mode 100644 index 00000000..f3871221 --- /dev/null +++ b/apis/database/v1alpha1/shardingdatabase_conversion.go @@ -0,0 +1,14 @@ +package v1alpha1 + +import ( + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *ShardingDatabase) ConvertTo(dst conversion.Hub) error { + return nil +} + +// ConvertFrom converts v1 to v1alpha1 +func (dst *ShardingDatabase) ConvertFrom(src conversion.Hub) error { + return nil +} diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index a3779fe5..b79721e3 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1006,6 +1006,51 @@ func (in *DataguardBrokerStatus) DeepCopy() *DataguardBrokerStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbCloneConfig) DeepCopyInto(out *DbCloneConfig) { + *out = *in + if in.SshPublicKeys != nil { + in, out := &in.SshPublicKeys, &out.SshPublicKeys + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbCloneConfig. +func (in *DbCloneConfig) DeepCopy() *DbCloneConfig { + if in == nil { + return nil + } + out := new(DbCloneConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbCloneStatus) DeepCopyInto(out *DbCloneStatus) { + *out = *in + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) + **out = **in + } + if in.SshPublicKeys != nil { + in, out := &in.SshPublicKeys, &out.SshPublicKeys + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbCloneStatus. +func (in *DbCloneStatus) DeepCopy() *DbCloneStatus { + if in == nil { + return nil + } + out := new(DbCloneStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DbStatus) DeepCopyInto(out *DbStatus) { *out = *in @@ -1052,6 +1097,7 @@ func (in *DbSystemDetails) DeepCopyInto(out *DbSystemDetails) { } } in.DbBackupConfig.DeepCopyInto(&out.DbBackupConfig) + out.KMSConfig = in.KMSConfig } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbSystemDetails. @@ -1157,6 +1203,39 @@ func (in *DbcsSystemSpec) DeepCopyInto(out *DbcsSystemSpec) { *out = new(string) **out = **in } + if in.OCIConfigMap != nil { + in, out := &in.OCIConfigMap, &out.OCIConfigMap + *out = new(string) + **out = **in + } + if in.OCISecret != nil { + in, out := &in.OCISecret, &out.OCISecret + *out = new(string) + **out = **in + } + if in.DbClone != nil { + in, out := &in.DbClone, &out.DbClone + *out = new(DbCloneConfig) + (*in).DeepCopyInto(*out) + } + if in.PdbConfigs != nil { + in, out := &in.PdbConfigs, &out.PdbConfigs + *out = make([]PDBConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DbBackupId != nil { + in, out := &in.DbBackupId, &out.DbBackupId + *out = new(string) + **out = **in + } + if in.DatabaseId != nil { + in, out := &in.DatabaseId, &out.DatabaseId + *out = new(string) + **out = **in + } + out.KMSConfig = in.KMSConfig } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemSpec. @@ -1212,6 +1291,15 @@ func (in *DbcsSystemStatus) DeepCopyInto(out *DbcsSystemStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + out.KMSDetailsStatus = in.KMSDetailsStatus + in.DbCloneStatus.DeepCopyInto(&out.DbCloneStatus) + if in.PdbDetailsStatus != nil { + in, out := &in.PdbDetailsStatus, &out.PdbDetailsStatus + *out = make([]PDBDetailsStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemStatus. @@ -1467,6 +1555,36 @@ func (in *K8sSecretSpec) DeepCopy() *K8sSecretSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KMSConfig) DeepCopyInto(out *KMSConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KMSConfig. +func (in *KMSConfig) DeepCopy() *KMSConfig { + if in == nil { + return nil + } + out := new(KMSConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KMSDetailsStatus) DeepCopyInto(out *KMSDetailsStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KMSDetailsStatus. +func (in *KMSDetailsStatus) DeepCopy() *KMSDetailsStatus { + if in == nil { + return nil + } + out := new(KMSDetailsStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkAccessSpec) DeepCopyInto(out *NetworkAccessSpec) { *out = *in @@ -1836,6 +1954,117 @@ func (in *PDBAdminPassword) DeepCopy() *PDBAdminPassword { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { + *out = *in + if in.PdbName != nil { + in, out := &in.PdbName, &out.PdbName + *out = new(string) + **out = **in + } + if in.PdbAdminPassword != nil { + in, out := &in.PdbAdminPassword, &out.PdbAdminPassword + *out = new(string) + **out = **in + } + if in.TdeWalletPassword != nil { + in, out := &in.TdeWalletPassword, &out.TdeWalletPassword + *out = new(string) + **out = **in + } + if in.ShouldPdbAdminAccountBeLocked != nil { + in, out := &in.ShouldPdbAdminAccountBeLocked, &out.ShouldPdbAdminAccountBeLocked + *out = new(bool) + **out = **in + } + if in.FreeformTags != nil { + in, out := &in.FreeformTags, &out.FreeformTags + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.IsDelete != nil { + in, out := &in.IsDelete, &out.IsDelete + *out = new(bool) + **out = **in + } + if in.PluggableDatabaseId != nil { + in, out := &in.PluggableDatabaseId, &out.PluggableDatabaseId + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBConfig. +func (in *PDBConfig) DeepCopy() *PDBConfig { + if in == nil { + return nil + } + out := new(PDBConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBConfigStatus) DeepCopyInto(out *PDBConfigStatus) { + *out = *in + if in.PdbName != nil { + in, out := &in.PdbName, &out.PdbName + *out = new(string) + **out = **in + } + if in.ShouldPdbAdminAccountBeLocked != nil { + in, out := &in.ShouldPdbAdminAccountBeLocked, &out.ShouldPdbAdminAccountBeLocked + *out = new(bool) + **out = **in + } + if in.FreeformTags != nil { + in, out := &in.FreeformTags, &out.FreeformTags + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PluggableDatabaseId != nil { + in, out := &in.PluggableDatabaseId, &out.PluggableDatabaseId + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBConfigStatus. +func (in *PDBConfigStatus) DeepCopy() *PDBConfigStatus { + if in == nil { + return nil + } + out := new(PDBConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBDetailsStatus) DeepCopyInto(out *PDBDetailsStatus) { + *out = *in + if in.PDBConfigStatus != nil { + in, out := &in.PDBConfigStatus, &out.PDBConfigStatus + *out = make([]PDBConfigStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBDetailsStatus. +func (in *PDBDetailsStatus) DeepCopy() *PDBDetailsStatus { + if in == nil { + return nil + } + out := new(PDBDetailsStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBList) DeepCopyInto(out *PDBList) { *out = *in diff --git a/apis/database/v4/dbcssystem_conversion.go b/apis/database/v4/dbcssystem_conversion.go new file mode 100644 index 00000000..e5919f54 --- /dev/null +++ b/apis/database/v4/dbcssystem_conversion.go @@ -0,0 +1,4 @@ +package v4 + +// Hub defines v1 as the hub version +func (*DbcsSystem) Hub() {} diff --git a/apis/database/v4/dbcssystem_kms_types.go b/apis/database/v4/dbcssystem_kms_types.go new file mode 100644 index 00000000..8cbff504 --- /dev/null +++ b/apis/database/v4/dbcssystem_kms_types.go @@ -0,0 +1,141 @@ +/* +** Copyright (c) 2022-2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ +package v4 + +import "encoding/json" + +type KMSConfig struct { + VaultName string `json:"vaultName,omitempty"` + CompartmentId string `json:"compartmentId,omitempty"` + KeyName string `json:"keyName,omitempty"` + EncryptionAlgo string `json:"encryptionAlgo,omitempty"` + VaultType string `json:"vaultType,omitempty"` +} +type KMSDetailsStatus struct { + VaultId string `json:"vaultId,omitempty"` + ManagementEndpoint string `json:"managementEndpoint,omitempty"` + KeyId string `json:"keyId,omitempty"` + VaultName string `json:"vaultName,omitempty"` + CompartmentId string `json:"compartmentId,omitempty"` + KeyName string `json:"keyName,omitempty"` + EncryptionAlgo string `json:"encryptionAlgo,omitempty"` + VaultType string `json:"vaultType,omitempty"` +} + +const ( + lastSuccessfulKMSConfig = "lastSuccessfulKMSConfig" + lastSuccessfulKMSStatus = "lastSuccessfulKMSStatus" +) + +// GetLastSuccessfulKMSConfig returns the KMS config from the last successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulKMSConfig. +func (dbcs *DbcsSystem) GetLastSuccessfulKMSConfig() (*KMSConfig, error) { + val, ok := dbcs.GetAnnotations()[lastSuccessfulKMSConfig] + if !ok { + return nil, nil + } + + configBytes := []byte(val) + kmsConfig := KMSConfig{} + + err := json.Unmarshal(configBytes, &kmsConfig) + if err != nil { + return nil, err + } + + return &kmsConfig, nil +} + +// GetLastSuccessfulKMSStatus returns the KMS status from the last successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulKMSStatus. +func (dbcs *DbcsSystem) GetLastSuccessfulKMSStatus() (*KMSDetailsStatus, error) { + val, ok := dbcs.GetAnnotations()[lastSuccessfulKMSStatus] + if !ok { + return nil, nil + } + + statusBytes := []byte(val) + kmsStatus := KMSDetailsStatus{} + + err := json.Unmarshal(statusBytes, &kmsStatus) + if err != nil { + return nil, err + } + + return &kmsStatus, nil +} + +// SetLastSuccessfulKMSConfig saves the given KMSConfig to the annotations. +func (dbcs *DbcsSystem) SetLastSuccessfulKMSConfig(kmsConfig *KMSConfig) error { + configBytes, err := json.Marshal(kmsConfig) + if err != nil { + return err + } + + annotations := dbcs.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations[lastSuccessfulKMSConfig] = string(configBytes) + dbcs.SetAnnotations(annotations) + return nil +} + +// SetLastSuccessfulKMSStatus saves the given KMSDetailsStatus to the annotations. +func (dbcs *DbcsSystem) SetLastSuccessfulKMSStatus(kmsStatus *KMSDetailsStatus) error { + statusBytes, err := json.Marshal(kmsStatus) + if err != nil { + return err + } + + annotations := dbcs.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations[lastSuccessfulKMSStatus] = string(statusBytes) + dbcs.SetAnnotations(annotations) + // Update KMSDetailsStatus in DbcsSystemStatus + dbcs.Status.KMSDetailsStatus = KMSDetailsStatus{ + VaultName: kmsStatus.VaultName, + CompartmentId: kmsStatus.CompartmentId, + KeyName: kmsStatus.KeyName, + EncryptionAlgo: kmsStatus.EncryptionAlgo, + VaultType: kmsStatus.VaultType, + } + return nil +} diff --git a/apis/database/v4/dbcssystem_pdbconfig_types.go b/apis/database/v4/dbcssystem_pdbconfig_types.go new file mode 100644 index 00000000..f85a1855 --- /dev/null +++ b/apis/database/v4/dbcssystem_pdbconfig_types.go @@ -0,0 +1,83 @@ +/* +** Copyright (c) 2022-2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ +package v4 + +// PDBConfig defines details of PDB struct for DBCS systems +type PDBConfig struct { + // The name for the pluggable database (PDB). The name is unique in the context of a Database. The name must begin with an alphabetic character and can contain a maximum of thirty alphanumeric characters. Special characters are not permitted. The pluggable database name should not be same as the container database name. + PdbName *string `mandatory:"true" json:"pdbName"` + + // The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the CDB + // ContainerDatabaseId *string `mandatory:"false" json:"containerDatabaseId"` + + // // A strong password for PDB Admin. The password must be at least nine characters and contain at least two uppercase, two lowercase, two numbers, and two special characters. The special characters must be _, \#, or -. + PdbAdminPassword *string `mandatory:"false" json:"pdbAdminPassword"` + + // // The existing TDE wallet password of the CDB. + TdeWalletPassword *string `mandatory:"false" json:"tdeWalletPassword"` + + // // The locked mode of the pluggable database admin account. If false, the user needs to provide the PDB Admin Password to connect to it. + // // If true, the pluggable database will be locked and user cannot login to it. + ShouldPdbAdminAccountBeLocked *bool `mandatory:"false" json:"shouldPdbAdminAccountBeLocked"` + + // // Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. + // // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). + // // Example: `{"Department": "Finance"}` + FreeformTags map[string]string `mandatory:"false" json:"freeformTags"` + + // // Defined tags for this resource. Each key is predefined and scoped to a namespace. + // // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). + // DefinedTags map[string]map[string]interface{} `mandatory:"false" json:"definedTags"` + + // To specify whether to delete the PDB + IsDelete *bool `mandatory:"false" json:"isDelete,omitempty"` + + // The OCID of the PDB for deletion purposes. + PluggableDatabaseId *string `mandatory:"false" json:"pluggableDatabaseId,omitempty"` +} + +type PDBConfigStatus struct { + PdbName *string `mandatory:"true" json:"pdbName"` + ShouldPdbAdminAccountBeLocked *bool `mandatory:"false" json:"shouldPdbAdminAccountBeLocked"` + FreeformTags map[string]string `mandatory:"false" json:"freeformTags"` + PluggableDatabaseId *string `mandatory:"false" json:"pluggableDatabaseId,omitempty"` + PdbLifecycleState LifecycleState `json:"pdbState,omitempty"` +} +type PDBDetailsStatus struct { + PDBConfigStatus []PDBConfigStatus `json:"pdbConfigStatus,omitempty"` +} diff --git a/apis/database/v4/dbcssystem_types.go b/apis/database/v4/dbcssystem_types.go new file mode 100644 index 00000000..80501aaf --- /dev/null +++ b/apis/database/v4/dbcssystem_types.go @@ -0,0 +1,291 @@ +/* +** Copyright (c) 2022-2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ +package v4 + +import ( + "encoding/json" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/go-logr/logr" + dbcsv1 "github.com/oracle/oracle-database-operator/commons/annotations" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// DbcsSystemSpec defines the desired state of DbcsSystem +type DbcsSystemSpec struct { + DbSystem DbSystemDetails `json:"dbSystem,omitempty"` + Id *string `json:"id,omitempty"` + OCIConfigMap *string `json:"ociConfigMap"` + OCISecret *string `json:"ociSecret,omitempty"` + DbClone *DbCloneConfig `json:"dbClone,omitempty"` + HardLink bool `json:"hardLink,omitempty"` + PdbConfigs []PDBConfig `json:"pdbConfigs,omitempty"` + SetupDBCloning bool `json:"setupDBCloning,omitempty"` + DbBackupId *string `json:"dbBackupId,omitempty"` + DatabaseId *string `json:"databaseId,omitempty"` + KMSConfig KMSConfig `json:"kmsConfig,omitempty"` +} + +// DbSystemDetails Spec + +type DbSystemDetails struct { + CompartmentId string `json:"compartmentId"` + AvailabilityDomain string `json:"availabilityDomain"` + SubnetId string `json:"subnetId"` + Shape string `json:"shape"` + SshPublicKeys []string `json:"sshPublicKeys,omitempty"` + HostName string `json:"hostName"` + CpuCoreCount int `json:"cpuCoreCount,omitempty"` + FaultDomains []string `json:"faultDomains,omitempty"` + DisplayName string `json:"displayName,omitempty"` + BackupSubnetId string `json:"backupSubnetId,omitempty"` + TimeZone string `json:"timeZone,omitempty"` + NodeCount *int `json:"nodeCount,omitempty"` + PrivateIp string `json:"privateIp,omitempty"` + Domain string `json:"domain,omitempty"` + InitialDataStorageSizeInGB int `json:"initialDataStorageSizeInGB,omitempty"` + ClusterName string `json:"clusterName,omitempty"` + DbAdminPaswordSecret string `json:"dbAdminPaswordSecret"` + DbName string `json:"dbName,omitempty"` + PdbName string `json:"pdbName,omitempty"` + DbDomain string `json:"dbDomain,omitempty"` + DbUniqueName string `json:"dbUniqueName,omitempty"` + StorageManagement string `json:"storageManagement,omitempty"` + DbVersion string `json:"dbVersion,omitempty"` + DbEdition string `json:"dbEdition,omitempty"` + DiskRedundancy string `json:"diskRedundancy,omitempty"` + DbWorkload string `json:"dbWorkload,omitempty"` + LicenseModel string `json:"licenseModel,omitempty"` + TdeWalletPasswordSecret string `json:"tdeWalletPasswordSecret,omitempty"` + Tags map[string]string `json:"tags,omitempty"` + DbBackupConfig Backupconfig `json:"dbBackupConfig,omitempty"` + KMSConfig KMSConfig `json:"kmsConfig,omitempty"` +} + +// DB Backup Config Network Struct +type Backupconfig struct { + AutoBackupEnabled *bool `json:"autoBackupEnabled,omitempty"` + RecoveryWindowsInDays *int `json:"recoveryWindowsInDays,omitempty"` + AutoBackupWindow *string `json:"autoBackupWindow,omitempty"` + BackupDestinationDetails *string `json:"backupDestinationDetails,omitempty"` +} + +// DbcsSystemStatus defines the observed state of DbcsSystem +type DbcsSystemStatus struct { + Id *string `json:"id,omitempty"` + DisplayName string `json:"displayName,omitempty"` + AvailabilityDomain string `json:"availabilityDomain,omitempty"` + SubnetId string `json:"subnetId,omitempty"` + StorageManagement string `json:"storageManagement,omitempty"` + NodeCount int `json:"nodeCount,omitempty"` + CpuCoreCount int `json:"cpuCoreCount,omitempty"` + + DbEdition string `json:"dbEdition,omitempty"` + TimeZone string `json:"timeZone,omitempty"` + DataStoragePercentage *int `json:"dataStoragePercentage,omitempty"` + LicenseModel string `json:"licenseModel,omitempty"` + DataStorageSizeInGBs *int `json:"dataStorageSizeInGBs,omitempty"` + RecoStorageSizeInGB *int `json:"recoStorageSizeInGB,omitempty"` + + Shape *string `json:"shape,omitempty"` + State LifecycleState `json:"state"` + DbInfo []DbStatus `json:"dbInfo,omitempty"` + Network VmNetworkDetails `json:"network,omitempty"` + WorkRequests []DbWorkrequests `json:"workRequests,omitempty"` + KMSDetailsStatus KMSDetailsStatus `json:"kmsDetailsStatus,omitempty"` + DbCloneStatus DbCloneStatus `json:"dbCloneStatus,omitempty"` + PdbDetailsStatus []PDBDetailsStatus `json:"pdbDetailsStatus,omitempty"` +} + +// DbcsSystemStatus defines the observed state of DbcsSystem +type DbStatus struct { + Id *string `json:"id,omitempty"` + DbName string `json:"dbName,omitempty"` + DbUniqueName string `json:"dbUniqueName,omitempty"` + DbWorkload string `json:"dbWorkload,omitempty"` + DbHomeId string `json:"dbHomeId,omitempty"` +} + +type DbWorkrequests struct { + OperationType *string `json:"operationType,omitmpty"` + OperationId *string `json:"operationId,omitemty"` + PercentComplete string `json:"percentComplete,omitempty"` + TimeAccepted string `json:"timeAccepted,omitempty"` + TimeStarted string `json:"timeStarted,omitempty"` + TimeFinished string `json:"timeFinished,omitempty"` +} + +type VmNetworkDetails struct { + VcnName *string `json:"vcnName,omitempty"` + SubnetName *string `json:"clientSubnet,omitempty"` + ScanDnsName *string `json:"scanDnsName,omitempty"` + HostName string `json:"hostName,omitempty"` + DomainName string `json:"domainName,omitempty"` + ListenerPort *int `json:"listenerPort,omitempty"` + NetworkSG string `json:"networkSG,omitempty"` +} + +// DbCloneConfig defines the configuration for the database clone +type DbCloneConfig struct { + DbAdminPaswordSecret string `json:"dbAdminPaswordSecret,omitempty"` + TdeWalletPasswordSecret string `json:"tdeWalletPasswordSecret,omitempty"` + DbName string `json:"dbName"` + HostName string `json:"hostName"` + DbUniqueName string `json:"dbDbUniqueName"` + DisplayName string `json:"displayName"` + LicenseModel string `json:"licenseModel,omitempty"` + Domain string `json:"domain,omitempty"` + SshPublicKeys []string `json:"sshPublicKeys,omitempty"` + SubnetId string `json:"subnetId"` + SidPrefix string `json:"sidPrefix,omitempty"` + InitialDataStorageSizeInGB int `json:"initialDataStorageSizeInGB,omitempty"` + KmsKeyId string `json:"kmsKeyId,omitempty"` + KmsKeyVersionId string `json:"kmsKeyVersionId,omitempty"` + PrivateIp string `json:"privateIp,omitempty"` +} + +// DbCloneStatus defines the observed state of DbClone +type DbCloneStatus struct { + Id *string `json:"id,omitempty"` + DbAdminPaswordSecret string `json:"dbAdminPaswordSecret,omitempty"` + DbName string `json:"dbName,omitempty"` + HostName string `json:"hostName"` + DbUniqueName string `json:"dbDbUniqueName"` + DisplayName string `json:"displayName,omitempty"` + LicenseModel string `json:"licenseModel,omitempty"` + Domain string `json:"domain,omitempty"` + SshPublicKeys []string `json:"sshPublicKeys,omitempty"` + SubnetId string `json:"subnetId,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=DbcsSystem,scope=Namespaced + // +kubebuilder:storageversion + +// DbcsSystem is the Schema for the dbcssystems API +type DbcsSystem struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec DbcsSystemSpec `json:"spec,omitempty"` + Status DbcsSystemStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// DbcsSystemList contains a list of DbcsSystem +type DbcsSystemList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DbcsSystem `json:"items"` +} + +type LifecycleState string + +const ( + Available LifecycleState = "AVAILABLE" + Failed LifecycleState = "FAILED" + Update LifecycleState = "UPDATING" + Provision LifecycleState = "PROVISIONING" + Terminate LifecycleState = "TERMINATED" +) + +const lastSuccessfulSpec = "lastSuccessfulSpec" + +// GetLastSuccessfulSpec returns spec from the lass successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulSpec. +func (dbcs *DbcsSystem) GetLastSuccessfulSpec() (*DbcsSystemSpec, error) { + val, ok := dbcs.GetAnnotations()[lastSuccessfulSpec] + if !ok { + return nil, nil + } + + specBytes := []byte(val) + sucSpec := DbcsSystemSpec{} + + err := json.Unmarshal(specBytes, &sucSpec) + if err != nil { + return nil, err + } + + return &sucSpec, nil +} +func (dbcs *DbcsSystem) GetLastSuccessfulSpecWithLog(log logr.Logger) (*DbcsSystemSpec, error) { + val, ok := dbcs.GetAnnotations()[lastSuccessfulSpec] + if !ok { + log.Info("No last successful spec annotation found") + return nil, nil + } + + specBytes := []byte(val) + sucSpec := DbcsSystemSpec{} + + err := json.Unmarshal(specBytes, &sucSpec) + if err != nil { + log.Error(err, "Failed to unmarshal last successful spec") + return nil, err + } + + log.Info("Successfully retrieved last successful spec", "spec", sucSpec) + return &sucSpec, nil +} + +// UpdateLastSuccessfulSpec updates lastSuccessfulSpec with the current spec. +func (dbcs *DbcsSystem) UpdateLastSuccessfulSpec(kubeClient client.Client) error { + specBytes, err := json.Marshal(dbcs.Spec) + if err != nil { + return err + } + + anns := map[string]string{ + lastSuccessfulSpec: string(specBytes), + } + + // return dbcsv1.SetAnnotations(kubeClient, dbcs, anns) + return dbcsv1.PatchAnnotations(kubeClient, dbcs, anns) + +} + +func init() { + SchemeBuilder.Register(&DbcsSystem{}, &DbcsSystemList{}) +} diff --git a/apis/database/v4/dbcssystem_webhook.go b/apis/database/v4/dbcssystem_webhook.go new file mode 100644 index 00000000..68a0ab5a --- /dev/null +++ b/apis/database/v4/dbcssystem_webhook.go @@ -0,0 +1,98 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var dbcssystemlog = logf.Log.WithName("dbcssystem-resource") + +func (r *DbcsSystem) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-dbcssystem,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=dbcssystems,verbs=create;update,versions=v4,name=mdbcssystem.kb.io,admissionReviewVersions={v1} + +var _ webhook.Defaulter = &DbcsSystem{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *DbcsSystem) Default() { + dbcssystemlog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. + +// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystem.kb.io,admissionReviewVersions={v1} +var _ webhook.Validator = &DbcsSystem{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *DbcsSystem) ValidateCreate() (admission.Warnings, error) { + dbcssystemlog.Info("validate create", "name", r.Name) + + // // TODO(user): fill in your validation logic upon object creation. + return nil, nil +} + +// // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *DbcsSystem) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + dbcssystemlog.Info("validate update", "name", r.Name) + + // // TODO(user): fill in your validation logic upon object update. + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *DbcsSystem) ValidateDelete() (admission.Warnings, error) { + dbcssystemlog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} diff --git a/apis/database/v4/groupversion_info.go b/apis/database/v4/groupversion_info.go new file mode 100644 index 00000000..6644b93c --- /dev/null +++ b/apis/database/v4/groupversion_info.go @@ -0,0 +1,58 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Package v4 contains API Schema definitions for the database v4 API group +// +kubebuilder:object:generate=true +// +groupName=database.oracle.com +package v4 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "database.oracle.com", Version: "v4"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/database/v4/shardingdatabase_conversion.go b/apis/database/v4/shardingdatabase_conversion.go new file mode 100644 index 00000000..7b2c17ac --- /dev/null +++ b/apis/database/v4/shardingdatabase_conversion.go @@ -0,0 +1,4 @@ +package v4 + +// Hub defines v1 as the hub version +func (*ShardingDatabase) Hub() {} diff --git a/apis/database/v4/shardingdatabase_types.go b/apis/database/v4/shardingdatabase_types.go new file mode 100644 index 00000000..ee7b981e --- /dev/null +++ b/apis/database/v4/shardingdatabase_types.go @@ -0,0 +1,405 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "sync" + + "encoding/json" + + "sigs.k8s.io/controller-runtime/pkg/client" + + annsv1 "github.com/oracle/oracle-database-operator/commons/annotations" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// ShardingDatabaseSpec defines the desired state of ShardingDatabase +type ShardingDatabaseSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + Shard []ShardSpec `json:"shard"` + Catalog []CatalogSpec `json:"catalog"` // The catalogSpes accept all the catalog parameters + Gsm []GsmSpec `json:"gsm"` // The GsmSpec will accept all the Gsm parameter + StorageClass string `json:"storageClass,omitempty"` // Optional Accept storage class name + DbImage string `json:"dbImage"` // Accept DB Image name + DbImagePullSecret string `json:"dbImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. + GsmImage string `json:"gsmImage"` // Acccept the GSM image name + GsmImagePullSecret string `json:"gsmImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. + StagePvcName string `json:"stagePvcName,omitempty"` // the Stagepvc for the backup of cluster + PortMappings []PortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least + Namespace string `json:"namespace,omitempty"` // Target namespace of the application. + IsDebug bool `json:"isDebug,omitempty"` // Optional parameter to enable logining + IsExternalSvc bool `json:"isExternalSvc,omitempty"` + IsClone bool `json:"isClone,omitempty"` + IsDataGuard bool `json:"isDataGuard,omitempty"` + ScriptsLocation string `json:"scriptsLocation,omitempty"` + IsDeleteOraPvc bool `json:"isDeleteOraPvc,omitempty"` + ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` + LivenessCheckPeriod int `json:"liveinessCheckPeriod,omitempty"` + ReplicationType string `json:"replicationType,omitempty"` + IsDownloadScripts bool `json:"isDownloadScripts,omitempty"` + InvitedNodeSubnetFlag string `json:"invitedNodeSubnetFlag,omitempty"` + InvitedNodeSubnet string `json:"InvitedNodeSubnet,omitempty"` + ShardingType string `json:"shardingType,omitempty"` + GsmShardSpace []GsmShardSpaceSpec `json:"gsmShardSpace,omitempty"` + GsmShardGroup []GsmShardGroupSpec `json:"gsmShardGroup,omitempty"` + ShardRegion []string `json:"shardRegion,omitempty"` + ShardBuddyRegion string `json:"shardBuddyRegion,omitempty"` + GsmService []GsmServiceSpec `json:"gsmService,omitempty"` + ShardConfigName string `json:"shardConfigName,omitempty"` + GsmDevMode string `json:"gsmDevMode,omitempty"` + DbSecret *SecretDetails `json:"dbSecret,omitempty"` // Secret Name to be used with Shard + IsTdeWallet string `json:"isTdeWallet,omitempty"` + TdeWalletPvc string `json:"tdeWalletPvc,omitempty"` + FssStorageClass string `json:"fssStorageClass,omitempty"` + TdeWalletPvcMountLocation string `json:"tdeWalletPvcMountLocation,omitempty"` + DbEdition string `json:"dbEdition,omitempty"` +} + +// To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 +// ShardingDatabaseStatus defines the observed state of ShardingDatabase +type ShardingDatabaseStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + Shard map[string]string `json:"shards,omitempty"` + Catalog map[string]string `json:"catalogs,omitempty"` + + Gsm GsmStatus `json:"gsm,omitempty"` + + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + CrdStatus []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` +} + +type GsmStatus struct { + InternalconnectStr string `json:"internalConnectStr,omitempty"` + ExternalConnectStr string `json:"externalConnectStr,omitempty"` + State string `json:"state,omitempty"` + Shards map[string]string `json:"shards,omitempty"` + Details map[string]string `json:"details,omitempty"` + Services string `json:"services,omitempty"` +} + +type GsmShardDetails struct { + Name string `json:"name,omitempty"` + Available string `json:"available,omitempty"` + State string `json:"State,omitempty"` +} + +type GsmStatusDetails struct { + Name string `json:"name,omitempty"` + K8sInternalSvc string `json:"k8sInternalSvc,omitempty"` + K8sExternalSvc string `json:"k8sExternalSvc,omitempty"` + K8sInternalSvcIP string `json:"k8sInternalIP,omitempty"` + K8sExternalSvcIP string `json:"k8sExternalIP,omitempty"` + Role string `json:"role,omitempty"` + DbPasswordSecret string `json:"dbPasswordSecret"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:JSONPath=".status.gsm.state",name="Gsm State",type=string +//+kubebuilder:printcolumn:JSONPath=".status.gsm.services",name="Services",type=string +//+kubebuilder:printcolumn:JSONPath=".status.gsm.shards",name="shards",type=string,priority=1 + +// ShardingDatabase is the Schema for the shardingdatabases API +// +kubebuilder:resource:path=shardingdatabases,scope=Namespaced + // +kubebuilder:storageversion + // +kubebuilder:storageversion +type ShardingDatabase struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ShardingDatabaseSpec `json:"spec,omitempty"` + Status ShardingDatabaseStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// ShardingDatabaseList contains a list of ShardingDatabase +type ShardingDatabaseList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ShardingDatabase `json:"items"` +} + +// ShardSpec is a specification of Shards for an application deployment. +// +k8s:openapi-gen=true +type ShardSpec struct { + Name string `json:"name"` // Shard name that will be used deploy StatefulSet + StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` // Optional Shard Storage Size + EnvVars []EnvironmentVariable `json:"envVars,omitempty"` //Optional Env variables for Shards + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` //Optional resource requirement for the container. + PvcName string `json:"pvcName,omitempty"` + Label string `json:"label,omitempty"` + // +kubebuilder:validation:Enum=enable;disable;failed;force + IsDelete string `json:"isDelete,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` + PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` + ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + ShardSpace string `json:"shardSpace,omitempty"` + ShardGroup string `json:"shardGroup,omitempty"` + ShardRegion string `json:"shardRegion,omitempty"` + DeployAs string `json:"deployAs,omitempty"` +} + +// CatalogSpec defines the desired state of CatalogSpec +// +k8s:openapi-gen=true +type CatalogSpec struct { + Name string `json:"name"` // Catalog name that will be used deploy StatefulSet + StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` // Optional Catalog Storage Size and This parameter will not be used if you use PvcName + EnvVars []EnvironmentVariable `json:"envVars,omitempty"` //Optional Env variables for Catalog + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` // Optional resource requirement for the container. + PvcName string `json:"pvcName,omitempty"` + Label string `json:"label,omitempty"` + IsDelete string `json:"isDelete,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` + PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` + ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` +} + +// GsmSpec defines the desired state of GsmSpec +// +k8s:openapi-gen=true +type GsmSpec struct { + Name string `json:"name"` // Gsm name that will be used deploy StatefulSet + + //Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. + EnvVars []EnvironmentVariable `json:"envVars,omitempty"` //Optional Env variables for GSM + StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` // This parameter will not be used if you use OraGsmPvcName + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` // Optional resource requirement for the container. + PvcName string `json:"pvcName,omitempty"` + Label string `json:"label,omitempty"` // Optional GSM Label + IsDelete string `json:"isDelete,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` + ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + Region string `json:"region,omitempty"` + DirectorName string `json:"directorName,omitempty"` +} + +// ShardGroupSpec Specification + +type GsmShardGroupSpec struct { + Name string `json:"name"` // Name of the shardgroup. + Region string `json:"region,omitempty"` + DeployAs string `json:"deployAs,omitempty"` +} + +// ShardSpace Specs +type GsmShardSpaceSpec struct { + Name string `json:"name"` // Name of the shardSpace. + Chunks int `json:"chunks,omitempty"` //chunks is optional + ProtectionMode string `json:"protectionMode,omitempty"` // Data guard protection mode + ShardGroup string `json:"shardGroup,omitempty"` +} + +// Service Definition +type GsmServiceSpec struct { + Name string `json:"name"` // Name of the shardSpace. + Available string `json:"available,omitempty"` + ClbGoal string `json:"clbGoal,omitempty"` + CommitOutcome string `json:"commitOutcome,omitempty"` + DrainTimeout string `json:"drainTimeout,omitempty"` + Dtp string `json:"dtp,omitempty"` + Edition string `json:"edition,omitempty"` + FailoverPrimary string `json:"failoverPrimary,omitempty"` + FailoverRestore string `json:"failoverRestore,omitempty"` + FailoverDelay string `json:"failoverDelay,omitempty"` + FailoverMethod string `json:"failoverMethod,omitempty"` + FailoverRetry string `json:"failoverRetry,omitempty"` + FailoverType string `json:"failoverType,omitempty"` + GdsPool string `json:"gdsPool,omitempty"` + Role string `json:"role,omitempty"` + SessionState string `json:"sessionState,omitempty"` + Lag int `json:"lag,omitempty"` + Locality string `json:"locality,omitempty"` + Notification string `json:"notification,omitempty"` + PdbName string `json:"pdbName,omitempty"` + Policy string `json:"policy,omitempty"` + Preferrred string `json:"preferred,omitempty"` + PreferredAll string `json:"prferredAll,omitempty"` + RegionFailover string `json:"regionFailover,omitempty"` + StopOption string `json:"stopOption,omitempty"` + SqlTrasactionProfile string `json:"sqlTransactionProfile,omitempty"` + TableFamily string `json:"tableFamily,omitempty"` + Retention string `json:"retention,omitempty"` + TfaPolicy string `json:"tfaPolicy,omitempty"` +} + +// Secret Details +type SecretDetails struct { + Name string `json:"name"` // Name of the secret. + KeyFileName string `json:"keyFileName,omitempty"` // Name of the key. + NsConfigMap string `json:"nsConfigMap,omitempty"` + NsSecret string `json:"nsSecret,omitempty"` + PwdFileName string `json:"pwdFileName"` + PwdFileMountLocation string `json:"pwdFileMountLocation,omitempty"` + KeyFileMountLocation string `json:"keyFileMountLocation,omitempty"` + KeySecretName string `json:"keySecretName,omitempty"` + EncryptionType string `json:"encryptionType,omitempty"` +} + +// EnvironmentVariable represents a named variable accessible for containers. +// +k8s:openapi-gen=true +type EnvironmentVariable struct { + Name string `json:"name"` // Name of the variable. Must be a C_IDENTIFIER. + Value string `json:"value"` // Value of the variable, as defined in Kubernetes core API. +} + +// PortMapping is a specification of port mapping for an application deployment. +// +k8s:openapi-gen=true +type PortMapping struct { + Port int32 `json:"port"` // Port that will be exposed on the service. + TargetPort int32 `json:"targetPort"` // Docker image port for the application. + Protocol corev1.Protocol `json:"protocol"` // IP protocol for the mapping, e.g., "TCP" or "UDP". +} + +type SfsetLabel string + +const ( + ShardingDelLabelKey SfsetLabel = "sharding.oracle.com/delflag" + ShardingDelLabelTrueValue SfsetLabel = "true" + ShardingDelLabelFalseValue SfsetLabel = "false" +) + +type ShardStatusMapKeys string + +const ( + Name ShardStatusMapKeys = "Name" + K8sInternalSvc ShardStatusMapKeys = "K8sInternalSvc" + K8sExternalSvc ShardStatusMapKeys = "K8sExternalSvc" + K8sInternalSvcIP ShardStatusMapKeys = "K8sInternalSvcIP" + K8sExternalSvcIP ShardStatusMapKeys = "K8sExternalSvcIP" + OracleSid ShardStatusMapKeys = "OracleSid" + OraclePdb ShardStatusMapKeys = "OraclePdb" + Role ShardStatusMapKeys = "Role" + DbPasswordSecret ShardStatusMapKeys = "DbPasswordSecret" + State ShardStatusMapKeys = "State" + OpenMode ShardStatusMapKeys = "OpenMode" +) + +type ShardLifecycleState string + +const ( + AvailableState ShardLifecycleState = "AVAILABLE" + FailedState ShardLifecycleState = "FAILED" + UpdateState ShardLifecycleState = "UPDATING" + ProvisionState ShardLifecycleState = "PROVISIONING" + PodNotReadyState ShardLifecycleState = "PODNOTREADY" + PodFailureState ShardLifecycleState = "PODFAILURE" + PodNotFound ShardLifecycleState = "PODNOTFOUND" + StatefulSetFailure ShardLifecycleState = "STATEFULSETFAILURE" + StatefulSetNotFound ShardLifecycleState = "STATEFULSETNOTFOUND" + DeletingState ShardLifecycleState = "DELETING" + DeleteErrorState ShardLifecycleState = "DELETE_ERROR" + ChunkMoveError ShardLifecycleState = "CHUNK_MOVE_ERROR_IN_GSM" + Terminated ShardLifecycleState = "TERMINATED" + LabelPatchingError ShardLifecycleState = "LABELPATCHINGERROR" + DeletePVCError ShardLifecycleState = "DELETEPVCERROR" + AddingShardState ShardLifecycleState = "SHARD_ADDITION" + AddingShardErrorState ShardLifecycleState = "SHARD_ADDITION_ERROR_IN_GSM" + ShardOnlineErrorState ShardLifecycleState = "SHARD_ONLINE_ERROR_IN_GSM" + ShardOnlineState ShardLifecycleState = "ONLINE_SHARD" + ShardRemoveError ShardLifecycleState = "SHARD_DELETE_ERROR_FROM_GSM" +) + +type CrdReconcileState string + +const ( + CrdReconcileErrorState CrdReconcileState = "ReconcileError" + CrdReconcileErrorReason CrdReconcileState = "LastReconcileCycleFailed" + CrdReconcileQueuedState CrdReconcileState = "ReconcileQueued" + CrdReconcileQueuedReason CrdReconcileState = "LastReconcileCycleQueued" + CrdReconcileCompeleteState CrdReconcileState = "ReconcileComplete" + CrdReconcileCompleteReason CrdReconcileState = "LastReconcileCycleCompleted" + CrdReconcileWaitingState CrdReconcileState = "ReconcileWaiting" + CrdReconcileWaitingReason CrdReconcileState = "LastReconcileCycleWaiting" +) + +// var +var KubeConfigOnce sync.Once + +//const lastSuccessfulSpec = "lastSuccessfulSpec" + +// GetLastSuccessfulSpec returns spec from the lass successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulSpec. +func (shardingv1 *ShardingDatabase) GetLastSuccessfulSpec() (*ShardingDatabaseSpec, error) { + val, ok := shardingv1.GetAnnotations()[lastSuccessfulSpec] + if !ok { + return nil, nil + } + + specBytes := []byte(val) + sucSpec := ShardingDatabaseSpec{} + + err := json.Unmarshal(specBytes, &sucSpec) + if err != nil { + return nil, err + } + + return &sucSpec, nil +} + +// UpdateLastSuccessfulSpec updates lastSuccessfulSpec with the current spec. +func (shardingv1 *ShardingDatabase) UpdateLastSuccessfulSpec(kubeClient client.Client) error { + specBytes, err := json.Marshal(shardingv1.Spec) + if err != nil { + return err + } + + anns := map[string]string{ + lastSuccessfulSpec: string(specBytes), + } + + return annsv1.PatchAnnotations(kubeClient, shardingv1, anns) +} + +func init() { + SchemeBuilder.Register(&ShardingDatabase{}, &ShardingDatabaseList{}) +} diff --git a/apis/database/v1alpha1/shardingdatabase_webhook.go b/apis/database/v4/shardingdatabase_webhook.go similarity index 95% rename from apis/database/v1alpha1/shardingdatabase_webhook.go rename to apis/database/v4/shardingdatabase_webhook.go index 8b91fb0c..496567d0 100644 --- a/apis/database/v1alpha1/shardingdatabase_webhook.go +++ b/apis/database/v4/shardingdatabase_webhook.go @@ -36,7 +36,7 @@ ** SOFTWARE. */ -package v1alpha1 +package v4 import ( "strings" @@ -62,7 +62,7 @@ func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v1alpha1,name=mshardingdatabase.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v4,name=mshardingdatabase.kb.io,admissionReviewVersions={v1} var _ webhook.Defaulter = &ShardingDatabase{} @@ -87,7 +87,7 @@ func (r *ShardingDatabase) Default() { } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v1alpha1-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v1alpha1,name=vshardingdatabase.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v4,name=vshardingdatabase.kb.io,admissionReviewVersions={v1} var _ webhook.Validator = &ShardingDatabase{} diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go new file mode 100644 index 00000000..b03976a3 --- /dev/null +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -0,0 +1,1023 @@ +//go:build !ignore_autogenerated + +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package v4 + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Backupconfig) DeepCopyInto(out *Backupconfig) { + *out = *in + if in.AutoBackupEnabled != nil { + in, out := &in.AutoBackupEnabled, &out.AutoBackupEnabled + *out = new(bool) + **out = **in + } + if in.RecoveryWindowsInDays != nil { + in, out := &in.RecoveryWindowsInDays, &out.RecoveryWindowsInDays + *out = new(int) + **out = **in + } + if in.AutoBackupWindow != nil { + in, out := &in.AutoBackupWindow, &out.AutoBackupWindow + *out = new(string) + **out = **in + } + if in.BackupDestinationDetails != nil { + in, out := &in.BackupDestinationDetails, &out.BackupDestinationDetails + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backupconfig. +func (in *Backupconfig) DeepCopy() *Backupconfig { + if in == nil { + return nil + } + out := new(Backupconfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CatalogSpec) DeepCopyInto(out *CatalogSpec) { + *out = *in + if in.EnvVars != nil { + in, out := &in.EnvVars, &out.EnvVars + *out = make([]EnvironmentVariable, len(*in)) + copy(*out, *in) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PvAnnotations != nil { + in, out := &in.PvAnnotations, &out.PvAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PvMatchLabels != nil { + in, out := &in.PvMatchLabels, &out.PvMatchLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ImagePulllPolicy != nil { + in, out := &in.ImagePulllPolicy, &out.ImagePulllPolicy + *out = new(corev1.PullPolicy) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CatalogSpec. +func (in *CatalogSpec) DeepCopy() *CatalogSpec { + if in == nil { + return nil + } + out := new(CatalogSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbCloneConfig) DeepCopyInto(out *DbCloneConfig) { + *out = *in + if in.SshPublicKeys != nil { + in, out := &in.SshPublicKeys, &out.SshPublicKeys + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbCloneConfig. +func (in *DbCloneConfig) DeepCopy() *DbCloneConfig { + if in == nil { + return nil + } + out := new(DbCloneConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbCloneStatus) DeepCopyInto(out *DbCloneStatus) { + *out = *in + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) + **out = **in + } + if in.SshPublicKeys != nil { + in, out := &in.SshPublicKeys, &out.SshPublicKeys + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbCloneStatus. +func (in *DbCloneStatus) DeepCopy() *DbCloneStatus { + if in == nil { + return nil + } + out := new(DbCloneStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbStatus) DeepCopyInto(out *DbStatus) { + *out = *in + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbStatus. +func (in *DbStatus) DeepCopy() *DbStatus { + if in == nil { + return nil + } + out := new(DbStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbSystemDetails) DeepCopyInto(out *DbSystemDetails) { + *out = *in + if in.SshPublicKeys != nil { + in, out := &in.SshPublicKeys, &out.SshPublicKeys + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.FaultDomains != nil { + in, out := &in.FaultDomains, &out.FaultDomains + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.NodeCount != nil { + in, out := &in.NodeCount, &out.NodeCount + *out = new(int) + **out = **in + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.DbBackupConfig.DeepCopyInto(&out.DbBackupConfig) + out.KMSConfig = in.KMSConfig +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbSystemDetails. +func (in *DbSystemDetails) DeepCopy() *DbSystemDetails { + if in == nil { + return nil + } + out := new(DbSystemDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbWorkrequests) DeepCopyInto(out *DbWorkrequests) { + *out = *in + if in.OperationType != nil { + in, out := &in.OperationType, &out.OperationType + *out = new(string) + **out = **in + } + if in.OperationId != nil { + in, out := &in.OperationId, &out.OperationId + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbWorkrequests. +func (in *DbWorkrequests) DeepCopy() *DbWorkrequests { + if in == nil { + return nil + } + out := new(DbWorkrequests) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbcsSystem) DeepCopyInto(out *DbcsSystem) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystem. +func (in *DbcsSystem) DeepCopy() *DbcsSystem { + if in == nil { + return nil + } + out := new(DbcsSystem) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DbcsSystem) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbcsSystemList) DeepCopyInto(out *DbcsSystemList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DbcsSystem, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemList. +func (in *DbcsSystemList) DeepCopy() *DbcsSystemList { + if in == nil { + return nil + } + out := new(DbcsSystemList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DbcsSystemList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbcsSystemSpec) DeepCopyInto(out *DbcsSystemSpec) { + *out = *in + in.DbSystem.DeepCopyInto(&out.DbSystem) + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) + **out = **in + } + if in.OCIConfigMap != nil { + in, out := &in.OCIConfigMap, &out.OCIConfigMap + *out = new(string) + **out = **in + } + if in.OCISecret != nil { + in, out := &in.OCISecret, &out.OCISecret + *out = new(string) + **out = **in + } + if in.DbClone != nil { + in, out := &in.DbClone, &out.DbClone + *out = new(DbCloneConfig) + (*in).DeepCopyInto(*out) + } + if in.PdbConfigs != nil { + in, out := &in.PdbConfigs, &out.PdbConfigs + *out = make([]PDBConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DbBackupId != nil { + in, out := &in.DbBackupId, &out.DbBackupId + *out = new(string) + **out = **in + } + if in.DatabaseId != nil { + in, out := &in.DatabaseId, &out.DatabaseId + *out = new(string) + **out = **in + } + out.KMSConfig = in.KMSConfig +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemSpec. +func (in *DbcsSystemSpec) DeepCopy() *DbcsSystemSpec { + if in == nil { + return nil + } + out := new(DbcsSystemSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbcsSystemStatus) DeepCopyInto(out *DbcsSystemStatus) { + *out = *in + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) + **out = **in + } + if in.DataStoragePercentage != nil { + in, out := &in.DataStoragePercentage, &out.DataStoragePercentage + *out = new(int) + **out = **in + } + if in.DataStorageSizeInGBs != nil { + in, out := &in.DataStorageSizeInGBs, &out.DataStorageSizeInGBs + *out = new(int) + **out = **in + } + if in.RecoStorageSizeInGB != nil { + in, out := &in.RecoStorageSizeInGB, &out.RecoStorageSizeInGB + *out = new(int) + **out = **in + } + if in.Shape != nil { + in, out := &in.Shape, &out.Shape + *out = new(string) + **out = **in + } + if in.DbInfo != nil { + in, out := &in.DbInfo, &out.DbInfo + *out = make([]DbStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Network.DeepCopyInto(&out.Network) + if in.WorkRequests != nil { + in, out := &in.WorkRequests, &out.WorkRequests + *out = make([]DbWorkrequests, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.KMSDetailsStatus = in.KMSDetailsStatus + in.DbCloneStatus.DeepCopyInto(&out.DbCloneStatus) + if in.PdbDetailsStatus != nil { + in, out := &in.PdbDetailsStatus, &out.PdbDetailsStatus + *out = make([]PDBDetailsStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemStatus. +func (in *DbcsSystemStatus) DeepCopy() *DbcsSystemStatus { + if in == nil { + return nil + } + out := new(DbcsSystemStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentVariable) DeepCopyInto(out *EnvironmentVariable) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentVariable. +func (in *EnvironmentVariable) DeepCopy() *EnvironmentVariable { + if in == nil { + return nil + } + out := new(EnvironmentVariable) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmServiceSpec) DeepCopyInto(out *GsmServiceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmServiceSpec. +func (in *GsmServiceSpec) DeepCopy() *GsmServiceSpec { + if in == nil { + return nil + } + out := new(GsmServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmShardDetails) DeepCopyInto(out *GsmShardDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmShardDetails. +func (in *GsmShardDetails) DeepCopy() *GsmShardDetails { + if in == nil { + return nil + } + out := new(GsmShardDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmShardGroupSpec) DeepCopyInto(out *GsmShardGroupSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmShardGroupSpec. +func (in *GsmShardGroupSpec) DeepCopy() *GsmShardGroupSpec { + if in == nil { + return nil + } + out := new(GsmShardGroupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmShardSpaceSpec) DeepCopyInto(out *GsmShardSpaceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmShardSpaceSpec. +func (in *GsmShardSpaceSpec) DeepCopy() *GsmShardSpaceSpec { + if in == nil { + return nil + } + out := new(GsmShardSpaceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmSpec) DeepCopyInto(out *GsmSpec) { + *out = *in + if in.EnvVars != nil { + in, out := &in.EnvVars, &out.EnvVars + *out = make([]EnvironmentVariable, len(*in)) + copy(*out, *in) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PvMatchLabels != nil { + in, out := &in.PvMatchLabels, &out.PvMatchLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ImagePulllPolicy != nil { + in, out := &in.ImagePulllPolicy, &out.ImagePulllPolicy + *out = new(corev1.PullPolicy) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmSpec. +func (in *GsmSpec) DeepCopy() *GsmSpec { + if in == nil { + return nil + } + out := new(GsmSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmStatus) DeepCopyInto(out *GsmStatus) { + *out = *in + if in.Shards != nil { + in, out := &in.Shards, &out.Shards + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Details != nil { + in, out := &in.Details, &out.Details + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmStatus. +func (in *GsmStatus) DeepCopy() *GsmStatus { + if in == nil { + return nil + } + out := new(GsmStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GsmStatusDetails) DeepCopyInto(out *GsmStatusDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmStatusDetails. +func (in *GsmStatusDetails) DeepCopy() *GsmStatusDetails { + if in == nil { + return nil + } + out := new(GsmStatusDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KMSConfig) DeepCopyInto(out *KMSConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KMSConfig. +func (in *KMSConfig) DeepCopy() *KMSConfig { + if in == nil { + return nil + } + out := new(KMSConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KMSDetailsStatus) DeepCopyInto(out *KMSDetailsStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KMSDetailsStatus. +func (in *KMSDetailsStatus) DeepCopy() *KMSDetailsStatus { + if in == nil { + return nil + } + out := new(KMSDetailsStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { + *out = *in + if in.PdbName != nil { + in, out := &in.PdbName, &out.PdbName + *out = new(string) + **out = **in + } + if in.PdbAdminPassword != nil { + in, out := &in.PdbAdminPassword, &out.PdbAdminPassword + *out = new(string) + **out = **in + } + if in.TdeWalletPassword != nil { + in, out := &in.TdeWalletPassword, &out.TdeWalletPassword + *out = new(string) + **out = **in + } + if in.ShouldPdbAdminAccountBeLocked != nil { + in, out := &in.ShouldPdbAdminAccountBeLocked, &out.ShouldPdbAdminAccountBeLocked + *out = new(bool) + **out = **in + } + if in.FreeformTags != nil { + in, out := &in.FreeformTags, &out.FreeformTags + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.IsDelete != nil { + in, out := &in.IsDelete, &out.IsDelete + *out = new(bool) + **out = **in + } + if in.PluggableDatabaseId != nil { + in, out := &in.PluggableDatabaseId, &out.PluggableDatabaseId + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBConfig. +func (in *PDBConfig) DeepCopy() *PDBConfig { + if in == nil { + return nil + } + out := new(PDBConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBConfigStatus) DeepCopyInto(out *PDBConfigStatus) { + *out = *in + if in.PdbName != nil { + in, out := &in.PdbName, &out.PdbName + *out = new(string) + **out = **in + } + if in.ShouldPdbAdminAccountBeLocked != nil { + in, out := &in.ShouldPdbAdminAccountBeLocked, &out.ShouldPdbAdminAccountBeLocked + *out = new(bool) + **out = **in + } + if in.FreeformTags != nil { + in, out := &in.FreeformTags, &out.FreeformTags + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PluggableDatabaseId != nil { + in, out := &in.PluggableDatabaseId, &out.PluggableDatabaseId + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBConfigStatus. +func (in *PDBConfigStatus) DeepCopy() *PDBConfigStatus { + if in == nil { + return nil + } + out := new(PDBConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBDetailsStatus) DeepCopyInto(out *PDBDetailsStatus) { + *out = *in + if in.PDBConfigStatus != nil { + in, out := &in.PDBConfigStatus, &out.PDBConfigStatus + *out = make([]PDBConfigStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBDetailsStatus. +func (in *PDBDetailsStatus) DeepCopy() *PDBDetailsStatus { + if in == nil { + return nil + } + out := new(PDBDetailsStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PortMapping) DeepCopyInto(out *PortMapping) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortMapping. +func (in *PortMapping) DeepCopy() *PortMapping { + if in == nil { + return nil + } + out := new(PortMapping) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretDetails) DeepCopyInto(out *SecretDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretDetails. +func (in *SecretDetails) DeepCopy() *SecretDetails { + if in == nil { + return nil + } + out := new(SecretDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardSpec) DeepCopyInto(out *ShardSpec) { + *out = *in + if in.EnvVars != nil { + in, out := &in.EnvVars, &out.EnvVars + *out = make([]EnvironmentVariable, len(*in)) + copy(*out, *in) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PvAnnotations != nil { + in, out := &in.PvAnnotations, &out.PvAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PvMatchLabels != nil { + in, out := &in.PvMatchLabels, &out.PvMatchLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ImagePulllPolicy != nil { + in, out := &in.ImagePulllPolicy, &out.ImagePulllPolicy + *out = new(corev1.PullPolicy) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardSpec. +func (in *ShardSpec) DeepCopy() *ShardSpec { + if in == nil { + return nil + } + out := new(ShardSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardingDatabase) DeepCopyInto(out *ShardingDatabase) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardingDatabase. +func (in *ShardingDatabase) DeepCopy() *ShardingDatabase { + if in == nil { + return nil + } + out := new(ShardingDatabase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ShardingDatabase) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardingDatabaseList) DeepCopyInto(out *ShardingDatabaseList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ShardingDatabase, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardingDatabaseList. +func (in *ShardingDatabaseList) DeepCopy() *ShardingDatabaseList { + if in == nil { + return nil + } + out := new(ShardingDatabaseList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ShardingDatabaseList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardingDatabaseSpec) DeepCopyInto(out *ShardingDatabaseSpec) { + *out = *in + if in.Shard != nil { + in, out := &in.Shard, &out.Shard + *out = make([]ShardSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Catalog != nil { + in, out := &in.Catalog, &out.Catalog + *out = make([]CatalogSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Gsm != nil { + in, out := &in.Gsm, &out.Gsm + *out = make([]GsmSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.PortMappings != nil { + in, out := &in.PortMappings, &out.PortMappings + *out = make([]PortMapping, len(*in)) + copy(*out, *in) + } + if in.GsmShardSpace != nil { + in, out := &in.GsmShardSpace, &out.GsmShardSpace + *out = make([]GsmShardSpaceSpec, len(*in)) + copy(*out, *in) + } + if in.GsmShardGroup != nil { + in, out := &in.GsmShardGroup, &out.GsmShardGroup + *out = make([]GsmShardGroupSpec, len(*in)) + copy(*out, *in) + } + if in.ShardRegion != nil { + in, out := &in.ShardRegion, &out.ShardRegion + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.GsmService != nil { + in, out := &in.GsmService, &out.GsmService + *out = make([]GsmServiceSpec, len(*in)) + copy(*out, *in) + } + if in.DbSecret != nil { + in, out := &in.DbSecret, &out.DbSecret + *out = new(SecretDetails) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardingDatabaseSpec. +func (in *ShardingDatabaseSpec) DeepCopy() *ShardingDatabaseSpec { + if in == nil { + return nil + } + out := new(ShardingDatabaseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardingDatabaseStatus) DeepCopyInto(out *ShardingDatabaseStatus) { + *out = *in + if in.Shard != nil { + in, out := &in.Shard, &out.Shard + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Catalog != nil { + in, out := &in.Catalog, &out.Catalog + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Gsm.DeepCopyInto(&out.Gsm) + if in.CrdStatus != nil { + in, out := &in.CrdStatus, &out.CrdStatus + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardingDatabaseStatus. +func (in *ShardingDatabaseStatus) DeepCopy() *ShardingDatabaseStatus { + if in == nil { + return nil + } + out := new(ShardingDatabaseStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VmNetworkDetails) DeepCopyInto(out *VmNetworkDetails) { + *out = *in + if in.VcnName != nil { + in, out := &in.VcnName, &out.VcnName + *out = new(string) + **out = **in + } + if in.SubnetName != nil { + in, out := &in.SubnetName, &out.SubnetName + *out = new(string) + **out = **in + } + if in.ScanDnsName != nil { + in, out := &in.ScanDnsName, &out.ScanDnsName + *out = new(string) + **out = **in + } + if in.ListenerPort != nil { + in, out := &in.ListenerPort, &out.ListenerPort + *out = new(int) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VmNetworkDetails. +func (in *VmNetworkDetails) DeepCopy() *VmNetworkDetails { + if in == nil { + return nil + } + out := new(VmNetworkDetails) + in.DeepCopyInto(out) + return out +} diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 60905c76..48e51d5e 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -43,6 +43,8 @@ import ( "encoding/json" "errors" "fmt" + "reflect" + "strings" "time" "github.com/go-logr/logr" @@ -51,68 +53,102 @@ import ( "github.com/oracle/oci-go-sdk/v65/database" "github.com/oracle/oci-go-sdk/v65/workrequests" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/oracle/oracle-database-operator/commons/annotations" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" ) -func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) (string, error) { +const ( + checkInterval = 30 * time.Second + timeout = 15 * time.Minute +) + +func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient, kmsDetails *databasev4.KMSDetailsStatus) (string, error) { - //var provisionedDbcsSystemId string ctx := context.TODO() - // Get DB System Details - dbcsDetails := database.LaunchDbSystemDetails{} + // Check if DBCS system already exists using the displayName + listDbcsRequest := database.ListDbSystemsRequest{ + CompartmentId: common.String(dbcs.Spec.DbSystem.CompartmentId), + DisplayName: common.String(dbcs.Spec.DbSystem.DisplayName), + } + + listDbcsResponse, err := dbClient.ListDbSystems(ctx, listDbcsRequest) + if err != nil { + return "", err + } + + // Check if any DBCS system matches the display name + if len(listDbcsResponse.Items) > 0 { + for _, dbcsItem := range listDbcsResponse.Items { + if dbcsItem.DisplayName != nil && *dbcsItem.DisplayName == dbcs.Spec.DbSystem.DisplayName { + logger.Info("DBCS system already exists", "DBCS ID", *dbcsItem.Id) + return *dbcsItem.Id, nil + } + } + } + // Get the admin password from OCI key sshPublicKeys, err := getPublicSSHKey(kubeClient, dbcs) if err != nil { return "", err } - // Get Db SystemOption + + // Get DB SystemOptions dbSystemReq := GetDBSystemopts(dbcs) licenceModel := getLicenceModel(dbcs) - if dbcs.Spec.DbSystem.ClusterName != "" { - dbcsDetails.ClusterName = &dbcs.Spec.DbSystem.ClusterName - } - - if dbcs.Spec.DbSystem.TimeZone != "" { - dbcsDetails.TimeZone = &dbcs.Spec.DbSystem.TimeZone - } // Get DB Home Details dbHomeReq, err := GetDbHomeDetails(kubeClient, dbClient, dbcs) if err != nil { return "", err } - //tenancyOcid, _ := provider.TenancyOCID() - dbcsDetails.AvailabilityDomain = common.String(dbcs.Spec.DbSystem.AvailabilityDomain) - dbcsDetails.CompartmentId = common.String(dbcs.Spec.DbSystem.CompartmentId) - dbcsDetails.SubnetId = common.String(dbcs.Spec.DbSystem.SubnetId) - dbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) - dbcsDetails.Domain = common.String(dbcs.Spec.DbSystem.Domain) - if dbcs.Spec.DbSystem.DisplayName != "" { - dbcsDetails.DisplayName = common.String(dbcs.Spec.DbSystem.DisplayName) - } - dbcsDetails.SshPublicKeys = []string{sshPublicKeys} - dbcsDetails.Hostname = common.String(dbcs.Spec.DbSystem.HostName) - dbcsDetails.CpuCoreCount = common.Int(dbcs.Spec.DbSystem.CpuCoreCount) - //dbcsDetails.SourceDbSystemId = common.String(r.tenancyOcid) - dbcsDetails.NodeCount = common.Int(GetNodeCount(dbcs)) - dbcsDetails.InitialDataStorageSizeInGB = common.Int(GetInitialStorage(dbcs)) - dbcsDetails.DbSystemOptions = &dbSystemReq - dbcsDetails.DbHome = &dbHomeReq - dbcsDetails.DatabaseEdition = GetDBEdition(dbcs) - dbcsDetails.DiskRedundancy = GetDBbDiskRedundancy(dbcs) - dbcsDetails.LicenseModel = database.LaunchDbSystemDetailsLicenseModelEnum(licenceModel) + + // Determine CpuCoreCount + cpuCoreCount := 2 // default value + if dbcs.Spec.DbSystem.CpuCoreCount > 0 { + cpuCoreCount = dbcs.Spec.DbSystem.CpuCoreCount + } + + // Set up DB system details + dbcsDetails := database.LaunchDbSystemDetails{ + AvailabilityDomain: common.String(dbcs.Spec.DbSystem.AvailabilityDomain), + CompartmentId: common.String(dbcs.Spec.DbSystem.CompartmentId), + SubnetId: common.String(dbcs.Spec.DbSystem.SubnetId), + Shape: common.String(dbcs.Spec.DbSystem.Shape), + Domain: common.String(dbcs.Spec.DbSystem.Domain), + DisplayName: common.String(dbcs.Spec.DbSystem.DisplayName), + SshPublicKeys: []string{sshPublicKeys}, + Hostname: common.String(dbcs.Spec.DbSystem.HostName), + CpuCoreCount: common.Int(cpuCoreCount), + NodeCount: common.Int(GetNodeCount(dbcs)), + InitialDataStorageSizeInGB: common.Int(GetInitialStorage(dbcs)), + DbSystemOptions: &dbSystemReq, + DbHome: &dbHomeReq, + DatabaseEdition: GetDBEdition(dbcs), + DiskRedundancy: GetDBbDiskRedundancy(dbcs), + LicenseModel: database.LaunchDbSystemDetailsLicenseModelEnum(licenceModel), + } + if len(dbcs.Spec.DbSystem.Tags) != 0 { dbcsDetails.FreeformTags = dbcs.Spec.DbSystem.Tags } + // Add KMS details if available + if kmsDetails != nil && kmsDetails.VaultId != "" { + dbcsDetails.KmsKeyId = common.String(kmsDetails.KeyId) + dbcsDetails.DbHome.Database.KmsKeyId = common.String(kmsDetails.KeyId) + dbcsDetails.DbHome.Database.VaultId = common.String(kmsDetails.VaultId) + } + + // Log dbcsDetails for debugging + logger.Info("Launching DB System with details", "dbcsDetails", dbcsDetails) + req := database.LaunchDbSystemRequest{LaunchDbSystemDetails: dbcsDetails} // Send the request using the service client @@ -122,12 +158,14 @@ func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient d } dbcs.Spec.Id = resp.DbSystem.Id + // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Provision, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { return "", statusErr } + // Check the State - _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev1alpha1.Provision), string(databasev1alpha1.Available)) + _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev4.Provision), string(databasev4.Available)) if err != nil { return "", err } @@ -135,10 +173,438 @@ func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient d return *resp.DbSystem.Id, nil } +func parseLicenseModel(licenseModelStr string) (database.DbSystemLicenseModelEnum, error) { + switch licenseModelStr { + case "LICENSE_INCLUDED": + return database.DbSystemLicenseModelLicenseIncluded, nil + case "BRING_YOUR_OWN_LICENSE": + return database.DbSystemLicenseModelBringYourOwnLicense, nil + default: + return "", fmt.Errorf("invalid license model: %s", licenseModelStr) + } +} +func convertLicenseModel(licenseModel database.DbSystemLicenseModelEnum) (database.LaunchDbSystemFromDbSystemDetailsLicenseModelEnum, error) { + switch licenseModel { + case database.DbSystemLicenseModelLicenseIncluded: + return database.LaunchDbSystemFromDbSystemDetailsLicenseModelLicenseIncluded, nil + case database.DbSystemLicenseModelBringYourOwnLicense: + return database.LaunchDbSystemFromDbSystemDetailsLicenseModelBringYourOwnLicense, nil + default: + return "", fmt.Errorf("unsupported license model: %s", licenseModel) + } +} + +func CloneAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) (string, error) { + ctx := context.TODO() + var err error + dbAdminPassword := "" + // tdePassword := "" + logger.Info("Starting the clone process for DBCS", "dbcs", dbcs) + // Get the admin password from Kubernetes secret + if dbcs.Spec.DbClone.DbAdminPaswordSecret != "" { + dbAdminPassword, err = GetCloningAdminPassword(kubeClient, dbcs) + if err != nil { + logger.Error(err, "Failed to get DB Admin password") + } + // logger.Info(dbAdminPassword) + } + // // Log retrieved passwords + logger.Info("Retrieved passwords from Kubernetes secrets") + + // // // Retrieve the TDE wallet password from Kubernetes secrets + // // tdePassword, err := GetTdePassword(kubeClient, dbcs.Namespace, dbcs.Spec.TdeWalletPasswordSecretName) + // // if err != nil { + // // logger.Error(err, "Failed to get TDE wallet password from Kubernetes secret", "namespace", dbcs.Namespace, "secretName", dbcs.Spec.TdeWalletPasswordSecretName) + // // return "", err + // // } + sshPublicKeys, err := getCloningPublicSSHKey(kubeClient, dbcs) + if err != nil { + logger.Error(err, "failed to get SSH public key") + } + + // Fetch the existing DB system details + existingDbSystem, err := dbClient.GetDbSystem(ctx, database.GetDbSystemRequest{ + DbSystemId: dbcs.Spec.Id, + }) + if err != nil { + return "", err + } + logger.Info("Retrieved existing Db System Details from OCI using Spec.Id") + + // // Create the clone request payload + // // Create the DbHome details + // Prepare CreateDatabaseFromDbSystemDetails + databaseDetails := &database.CreateDatabaseFromDbSystemDetails{ + AdminPassword: &dbAdminPassword, + DbName: &dbcs.Spec.DbClone.DbName, + DbDomain: existingDbSystem.DbSystem.Domain, + DbUniqueName: &dbcs.Spec.DbClone.DbUniqueName, + FreeformTags: existingDbSystem.DbSystem.FreeformTags, + DefinedTags: existingDbSystem.DbSystem.DefinedTags, + } + licenseModelEnum, err := parseLicenseModel(dbcs.Spec.DbClone.LicenseModel) + if err != nil { + return "", err + } + launchLicenseModel, err := convertLicenseModel(licenseModelEnum) + if err != nil { + return "", err + } + + cloneRequest := database.LaunchDbSystemFromDbSystemDetails{ + CompartmentId: existingDbSystem.DbSystem.CompartmentId, + AvailabilityDomain: existingDbSystem.DbSystem.AvailabilityDomain, + SubnetId: &dbcs.Spec.DbClone.SubnetId, + Shape: existingDbSystem.DbSystem.Shape, + SshPublicKeys: []string{sshPublicKeys}, + Hostname: &dbcs.Spec.DbClone.HostName, + CpuCoreCount: existingDbSystem.DbSystem.CpuCoreCount, + SourceDbSystemId: existingDbSystem.DbSystem.Id, + DbHome: &database.CreateDbHomeFromDbSystemDetails{ + Database: databaseDetails, + DisplayName: existingDbSystem.DbSystem.DisplayName, + FreeformTags: existingDbSystem.DbSystem.FreeformTags, + DefinedTags: existingDbSystem.DbSystem.DefinedTags, + }, + FaultDomains: existingDbSystem.DbSystem.FaultDomains, + DisplayName: &dbcs.Spec.DbClone.DisplayName, + BackupSubnetId: existingDbSystem.DbSystem.BackupSubnetId, + NsgIds: existingDbSystem.DbSystem.NsgIds, + BackupNetworkNsgIds: existingDbSystem.DbSystem.BackupNetworkNsgIds, + TimeZone: existingDbSystem.DbSystem.TimeZone, + DbSystemOptions: existingDbSystem.DbSystem.DbSystemOptions, + SparseDiskgroup: existingDbSystem.DbSystem.SparseDiskgroup, + Domain: &dbcs.Spec.DbClone.Domain, + ClusterName: existingDbSystem.DbSystem.ClusterName, + DataStoragePercentage: existingDbSystem.DbSystem.DataStoragePercentage, + // KmsKeyId: existingDbSystem.DbSystem.KmsKeyId, + // KmsKeyVersionId: existingDbSystem.DbSystem.KmsKeyVersionId, + NodeCount: existingDbSystem.DbSystem.NodeCount, + FreeformTags: existingDbSystem.DbSystem.FreeformTags, + DefinedTags: existingDbSystem.DbSystem.DefinedTags, + DataCollectionOptions: existingDbSystem.DbSystem.DataCollectionOptions, + LicenseModel: launchLicenseModel, + } + + // Execute the clone request + response, err := dbClient.LaunchDbSystem(ctx, database.LaunchDbSystemRequest{ + LaunchDbSystemDetails: cloneRequest, + }) + if err != nil { + return "", err + } + + dbcs.Status.DbCloneStatus.Id = response.DbSystem.Id + + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + return "", statusErr + } + + // Check the state + _, err = CheckResourceState(logger, dbClient, *response.DbSystem.Id, string(databasev4.Provision), string(databasev4.Available)) + if err != nil { + return "", err + } + + return *response.DbSystem.Id, nil + // return "", nil +} + +// CloneFromBackupAndGetDbcsId clones a DB system from a backup and returns the new DB system's OCID. +func CloneFromBackupAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) (string, error) { + ctx := context.TODO() + + var err error + var dbAdminPassword string + var tdePassword string + logger.Info("Starting the clone process for DBCS from backup", "dbcs", dbcs) + backupResp, err := dbClient.GetBackup(ctx, database.GetBackupRequest{ + BackupId: dbcs.Spec.DbBackupId, + }) + + if err != nil { + fmt.Println("Error getting backup details:", err) + return "", err + } + databaseId := backupResp.Backup.DatabaseId + // Fetch the existing Database details + existingDatabase, err := dbClient.GetDatabase(ctx, database.GetDatabaseRequest{ + DatabaseId: databaseId, + }) + if err != nil { + logger.Error(err, "Failed to retrieve existing Database details") + return "", err + } + // Check if DbSystemId is available + dbSystemId := existingDatabase.DbSystemId + if dbSystemId == nil { + // handle the case where DbSystemId is not available + logger.Error(err, "DBSystemId not found") + return "", err + } + + // Fetch the existing DB system details + existingDbSystem, err := dbClient.GetDbSystem(ctx, database.GetDbSystemRequest{ + DbSystemId: dbSystemId, + }) + if err != nil { + return "", err + } + // Get the admin password from Kubernetes secret + if dbcs.Spec.DbClone.DbAdminPaswordSecret != "" { + dbAdminPassword, err = GetCloningAdminPassword(kubeClient, dbcs) + if err != nil { + logger.Error(err, "Failed to get DB Admin password") + } + // logger.Info(dbAdminPassword) + } + // // // Retrieve the TDE wallet password from Kubernetes secrets to open backup DB using TDE Wallet + if dbcs.Spec.DbClone.TdeWalletPasswordSecret != "" { + tdePassword, err = GetCloningTdePassword(kubeClient, dbcs) + if err != nil { + logger.Error(err, "Failed to get TDE wallet password from Kubernetes secret") + return "", err + } + } + + sshPublicKeys, err := getCloningPublicSSHKey(kubeClient, dbcs) + if err != nil { + logger.Error(err, "failed to get SSH public key") + return "", err + } + + // Create the clone request payload + cloneRequest := database.LaunchDbSystemFromBackupDetails{ + CompartmentId: existingDbSystem.DbSystem.CompartmentId, + AvailabilityDomain: existingDbSystem.DbSystem.AvailabilityDomain, + SubnetId: &dbcs.Spec.DbClone.SubnetId, + Shape: existingDbSystem.DbSystem.Shape, + SshPublicKeys: []string{sshPublicKeys}, + Hostname: &dbcs.Spec.DbClone.HostName, + CpuCoreCount: existingDbSystem.DbSystem.CpuCoreCount, + DbHome: &database.CreateDbHomeFromBackupDetails{ + Database: &database.CreateDatabaseFromBackupDetails{ // Corrected type here + BackupId: dbcs.Spec.DbBackupId, + AdminPassword: &dbAdminPassword, + BackupTDEPassword: &tdePassword, + DbName: &dbcs.Spec.DbClone.DbName, + // DbDomain: existingDbSystem.DbSystem.Domain, + DbUniqueName: &dbcs.Spec.DbClone.DbUniqueName, + // FreeformTags: existingDbSystem.DbSystem.FreeformTags, + // DefinedTags: existingDbSystem.DbSystem.DefinedTags, + SidPrefix: &dbcs.Spec.DbClone.SidPrefix, + }, + DisplayName: existingDbSystem.DbSystem.DisplayName, + FreeformTags: existingDbSystem.DbSystem.FreeformTags, + DefinedTags: existingDbSystem.DbSystem.DefinedTags, + }, + FaultDomains: existingDbSystem.DbSystem.FaultDomains, + DisplayName: &dbcs.Spec.DbClone.DisplayName, + BackupSubnetId: existingDbSystem.DbSystem.BackupSubnetId, + NsgIds: existingDbSystem.DbSystem.NsgIds, + BackupNetworkNsgIds: existingDbSystem.DbSystem.BackupNetworkNsgIds, + TimeZone: existingDbSystem.DbSystem.TimeZone, + DbSystemOptions: existingDbSystem.DbSystem.DbSystemOptions, + SparseDiskgroup: existingDbSystem.DbSystem.SparseDiskgroup, + Domain: &dbcs.Spec.DbClone.Domain, + ClusterName: existingDbSystem.DbSystem.ClusterName, + DataStoragePercentage: existingDbSystem.DbSystem.DataStoragePercentage, + InitialDataStorageSizeInGB: &dbcs.Spec.DbClone.InitialDataStorageSizeInGB, + KmsKeyId: &dbcs.Spec.DbClone.KmsKeyId, + KmsKeyVersionId: &dbcs.Spec.DbClone.KmsKeyVersionId, + NodeCount: existingDbSystem.DbSystem.NodeCount, + FreeformTags: existingDbSystem.DbSystem.FreeformTags, + DefinedTags: existingDbSystem.DbSystem.DefinedTags, + DataCollectionOptions: existingDbSystem.DbSystem.DataCollectionOptions, + DatabaseEdition: database.LaunchDbSystemFromBackupDetailsDatabaseEditionEnum(existingDbSystem.DbSystem.DatabaseEdition), + LicenseModel: database.LaunchDbSystemFromBackupDetailsLicenseModelEnum(existingDbSystem.DbSystem.LicenseModel), + StorageVolumePerformanceMode: database.LaunchDbSystemBaseStorageVolumePerformanceModeEnum(existingDbSystem.DbSystem.StorageVolumePerformanceMode), + } + + // Execute the clone request + response, err := dbClient.LaunchDbSystem(ctx, database.LaunchDbSystemRequest{ + LaunchDbSystemDetails: cloneRequest, + }) + if err != nil { + return "", err + } + + dbcs.Status.DbCloneStatus.Id = response.DbSystem.Id + + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + return "", statusErr + } + + // Check the state + _, err = CheckResourceState(logger, dbClient, *response.DbSystem.Id, string(databasev4.Provision), string(databasev4.Available)) + if err != nil { + return "", err + } + + return *response.DbSystem.Id, nil +} + // Sync the DbcsSystem Database details +func CloneFromDatabaseAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) (string, error) { + ctx := context.TODO() + var err error + dbAdminPassword := "" + tdePassword := "" + logger.Info("Starting the clone process for Database", "dbcs", dbcs) + + // Get the admin password from Kubernetes secret + if dbcs.Spec.DbClone.DbAdminPaswordSecret != "" { + dbAdminPassword, err = GetCloningAdminPassword(kubeClient, dbcs) + if err != nil { + logger.Error(err, "Failed to get DB Admin password") + return "", err + } + } + // // // Retrieve the TDE wallet password from Kubernetes secrets to open backup DB using TDE Wallet + if dbcs.Spec.DbClone.TdeWalletPasswordSecret != "" { + tdePassword, err = GetCloningTdePassword(kubeClient, dbcs) + if err != nil { + logger.Error(err, "Failed to get TDE wallet password from Kubernetes secret") + return "", err + } + } + + logger.Info("Retrieved passwords from Kubernetes secrets") + + // Fetch the existing Database details + existingDatabase, err := dbClient.GetDatabase(ctx, database.GetDatabaseRequest{ + DatabaseId: dbcs.Spec.DatabaseId, + }) + if err != nil { + logger.Error(err, "Failed to retrieve existing Database details") + return "", err + } + // Check if DbSystemId is available + dbSystemId := existingDatabase.DbSystemId + if dbSystemId == nil { + // handle the case where DbSystemId is not available + logger.Error(err, "DBSystemId not found") + return "", err + } + + // Fetch the existing DB system details + existingDbSystem, err := dbClient.GetDbSystem(ctx, database.GetDbSystemRequest{ + DbSystemId: dbSystemId, + }) + if err != nil { + return "", err + } + logger.Info("Retrieved existing Database details from OCI", "DatabaseId", dbcs.Spec.DatabaseId) + + // Get SSH public key + sshPublicKeys, err := getCloningPublicSSHKey(kubeClient, dbcs) + if err != nil { + logger.Error(err, "Failed to get SSH public key") + return "", err + } + + // Create the clone request payload + cloneRequest := database.LaunchDbSystemFromDatabaseDetails{ + CompartmentId: existingDatabase.CompartmentId, + AvailabilityDomain: existingDbSystem.DbSystem.AvailabilityDomain, + SubnetId: existingDbSystem.DbSystem.SubnetId, + Shape: existingDbSystem.DbSystem.Shape, + SshPublicKeys: []string{sshPublicKeys}, + Hostname: &dbcs.Spec.DbClone.HostName, + CpuCoreCount: existingDbSystem.DbSystem.CpuCoreCount, + DatabaseEdition: database.LaunchDbSystemFromDatabaseDetailsDatabaseEditionEnum(existingDbSystem.DbSystem.DatabaseEdition), + DbHome: &database.CreateDbHomeFromDatabaseDetails{ + Database: &database.CreateDatabaseFromAnotherDatabaseDetails{ + // Mandatory fields + DatabaseId: dbcs.Spec.DatabaseId, // Source database ID + // Optionally fill in other fields if needed + DbName: &dbcs.Spec.DbClone.DbName, + AdminPassword: &dbAdminPassword, // Admin password for the new database + // The password to open the TDE wallet. + BackupTDEPassword: &tdePassword, + + DbUniqueName: &dbcs.Spec.DbClone.DbUniqueName, + }, + + // Provide a display name for the new Database Home + DisplayName: existingDbSystem.DbSystem.DisplayName, + FreeformTags: existingDbSystem.DbSystem.FreeformTags, + DefinedTags: existingDbSystem.DbSystem.DefinedTags, + }, + + FaultDomains: existingDbSystem.DbSystem.FaultDomains, + DisplayName: &dbcs.Spec.DbClone.DisplayName, + BackupSubnetId: existingDbSystem.DbSystem.BackupSubnetId, + NsgIds: existingDbSystem.DbSystem.NsgIds, + BackupNetworkNsgIds: existingDbSystem.DbSystem.BackupNetworkNsgIds, + TimeZone: existingDbSystem.DbSystem.TimeZone, + KmsKeyId: &dbcs.Spec.DbClone.KmsKeyId, + KmsKeyVersionId: &dbcs.Spec.DbClone.KmsKeyVersionId, + NodeCount: existingDbSystem.DbSystem.NodeCount, + FreeformTags: existingDbSystem.DbSystem.FreeformTags, + DefinedTags: existingDbSystem.DbSystem.DefinedTags, + // PrivateIp: &dbcs.Spec.DbClone.PrivateIp, + InitialDataStorageSizeInGB: &dbcs.Spec.DbClone.InitialDataStorageSizeInGB, + LicenseModel: database.LaunchDbSystemFromDatabaseDetailsLicenseModelEnum(existingDbSystem.DbSystem.LicenseModel), + StorageVolumePerformanceMode: database.LaunchDbSystemBaseStorageVolumePerformanceModeEnum(existingDbSystem.DbSystem.StorageVolumePerformanceMode), + } + + // logger.Info("Launching database clone", "cloneRequest", cloneRequest) + + // Execute the clone request + response, err := dbClient.LaunchDbSystem(ctx, database.LaunchDbSystemRequest{ + LaunchDbSystemDetails: cloneRequest, + }) + if err != nil { + return "", err + } + + dbcs.Status.DbCloneStatus.Id = response.DbSystem.Id + + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + return "", statusErr + } + + // Check the state + _, err = CheckResourceState(logger, dbClient, *response.DbSystem.Id, string(databasev4.Provision), string(databasev4.Available)) + if err != nil { + return "", err + } + + return *response.DbSystem.Id, nil +} // Get admin password from Secret then OCI valut secret -func GetAdminPassword(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (string, error) { +func GetCloningAdminPassword(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (string, error) { + if dbcs.Spec.DbClone.DbAdminPaswordSecret != "" { + // Get the Admin Secret + adminSecret := &corev1.Secret{} + err := kubeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: dbcs.GetNamespace(), + Name: dbcs.Spec.DbClone.DbAdminPaswordSecret, + }, adminSecret) + + if err != nil { + return "", err + } + + // Get the admin password + key := "admin-password" + if val, ok := adminSecret.Data[key]; ok { + return strings.TrimSpace(string(val)), nil + } else { + msg := "secret item not found: admin-password" + return "", errors.New(msg) + } + } + return "", errors.New("should provide either a Secret name or a Valut Secret ID") +} + +// Get admin password from Secret then OCI valut secret +func GetAdminPassword(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (string, error) { if dbcs.Spec.DbSystem.DbAdminPaswordSecret != "" { // Get the Admin Secret adminSecret := &corev1.Secret{} @@ -154,7 +620,7 @@ func GetAdminPassword(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSyste // Get the admin password key := "admin-password" if val, ok := adminSecret.Data[key]; ok { - return string(val), nil + return strings.TrimSpace(string(val)), nil } else { msg := "secret item not found: admin-password" return "", errors.New(msg) @@ -164,7 +630,7 @@ func GetAdminPassword(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSyste } // Get admin password from Secret then OCI valut secret -func GetTdePassword(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (string, error) { +func GetTdePassword(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (string, error) { if dbcs.Spec.DbSystem.TdeWalletPasswordSecret != "" { // Get the Admin Secret tdeSecret := &corev1.Secret{} @@ -180,7 +646,33 @@ func GetTdePassword(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) // Get the admin password key := "tde-password" if val, ok := tdeSecret.Data[key]; ok { - return string(val), nil + return strings.TrimSpace(string(val)), nil + } else { + msg := "secret item not found: tde-password" + return "", errors.New(msg) + } + } + return "", errors.New("should provide either a Secret name or a Valut Secret ID") +} + +// Get admin password from Secret then OCI valut secret +func GetCloningTdePassword(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (string, error) { + if dbcs.Spec.DbClone.TdeWalletPasswordSecret != "" { + // Get the Admin Secret + tdeSecret := &corev1.Secret{} + err := kubeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: dbcs.GetNamespace(), + Name: dbcs.Spec.DbClone.TdeWalletPasswordSecret, + }, tdeSecret) + + if err != nil { + return "", err + } + + // Get the admin password + key := "tde-password" + if val, ok := tdeSecret.Data[key]; ok { + return strings.TrimSpace(string(val)), nil } else { msg := "secret item not found: tde-password" return "", errors.New(msg) @@ -190,7 +682,7 @@ func GetTdePassword(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) } // Get admin password from Secret then OCI valut secret -func getPublicSSHKey(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (string, error) { +func getPublicSSHKey(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (string, error) { if dbcs.Spec.DbSystem.SshPublicKeys[0] != "" { // Get the Admin Secret sshkeysecret := &corev1.Secret{} @@ -215,6 +707,32 @@ func getPublicSSHKey(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem return "", errors.New("should provide either a Secret name or a Valut Secret ID") } +// Get admin password from Secret then OCI valut secret +func getCloningPublicSSHKey(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (string, error) { + if dbcs.Spec.DbClone.SshPublicKeys[0] != "" { + // Get the Admin Secret + sshkeysecret := &corev1.Secret{} + err := kubeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: dbcs.GetNamespace(), + Name: dbcs.Spec.DbClone.SshPublicKeys[0], + }, sshkeysecret) + + if err != nil { + return "", err + } + + // Get the admin password` + key := "publickey" + if val, ok := sshkeysecret.Data[key]; ok { + return string(val), nil + } else { + msg := "secret item not found: " + return "", errors.New(msg) + } + } + return "", errors.New("should provide either a Secret name or a Valut Secret ID") +} + // Delete DbcsSystem System func DeleteDbcsSystemSystem(dbClient database.DatabaseClient, Id string) error { @@ -233,24 +751,142 @@ func DeleteDbcsSystemSystem(dbClient database.DatabaseClient, Id string) error { } // SetLifecycleState set status.state of the reosurce. -func SetLifecycleState(kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, state databasev1alpha1.LifecycleState, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { - return retry.RetryOnConflict(retry.DefaultRetry, func() error { - dbcs.Status.State = state - // Set the status +func SetLifecycleState(kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, state databasev4.LifecycleState, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { + maxRetries := 5 + retryDelay := time.Second * 2 + + for attempt := 0; attempt < maxRetries; attempt++ { + // Fetch the latest version of the object + latestInstance := &databasev4.DbcsSystem{} + err := kubeClient.Get(context.TODO(), client.ObjectKeyFromObject(dbcs), latestInstance) + if err != nil { + // Log and return error if fetching the latest version fails + return fmt.Errorf("failed to fetch the latest version of DBCS instance: %w", err) + } + + // Merge the instance fields into latestInstance + err = mergeInstancesFromLatest(dbcs, latestInstance) + if err != nil { + return fmt.Errorf("failed to merge instances: %w", err) + } + + // Set the status using the dbcs object if statusErr := SetDBCSStatus(dbClient, dbcs, nwClient, wrClient); statusErr != nil { return statusErr } - if err := kubeClient.Status().Update(context.TODO(), dbcs); err != nil { - return err + + // Update the ResourceVersion of dbcs from latestInstance to avoid conflict + dbcs.ResourceVersion = latestInstance.ResourceVersion + + // Attempt to patch the status of the instance + err = kubeClient.Status().Patch(context.TODO(), dbcs, client.MergeFrom(latestInstance)) + if err != nil { + if apierrors.IsConflict(err) { + // Handle the conflict and retry + time.Sleep(retryDelay) + continue + } + // For other errors, log and return the error + return fmt.Errorf("failed to update the DBCS instance status: %w", err) } - return nil - }) + // If no error, break the loop + break + } + + return nil +} +func mergeInstancesFromLatest(instance, latestInstance *databasev4.DbcsSystem) error { + instanceVal := reflect.ValueOf(instance).Elem() + latestVal := reflect.ValueOf(latestInstance).Elem() + + // Fields to exclude from merging + excludeFields := map[string]bool{ + "ReleaseUpdate": true, + "AsmStorageStatus": true, + } + + // Loop through the fields in instance + for i := 0; i < instanceVal.NumField(); i++ { + field := instanceVal.Type().Field(i) + instanceField := instanceVal.Field(i) + latestField := latestVal.FieldByName(field.Name) + + // Skip unexported fields + if !isExported(field) { + continue + } + + // Ensure latestField is valid + if !latestField.IsValid() || !instanceField.CanSet() { + continue + } + + // Skip fields that are in the exclusion list + if excludeFields[field.Name] { + continue + } + + // Handle pointer fields + if latestField.Kind() == reflect.Ptr { + if !latestField.IsNil() && instanceField.IsNil() { + // If instance's field is nil and latest's field is not nil, set the latest's field value + instanceField.Set(latestField) + } + // If instance's field is not nil, do not overwrite + } else if latestField.Kind() == reflect.String { + if latestField.String() != "" && latestField.String() != "NOT_DEFINED" && instanceField.String() == "" { + // If latest's string field is non-empty and not "NOT_DEFINED", and instance's string field is empty, set the value + instanceField.Set(latestField) + } + } else if latestField.Kind() == reflect.Struct { + // Handle struct types recursively + mergeStructFields(instanceField, latestField) + } else { + // Handle other types if instance's field is zero value + if reflect.DeepEqual(instanceField.Interface(), reflect.Zero(instanceField.Type()).Interface()) { + instanceField.Set(latestField) + } + } + } + return nil +} + +func mergeStructFields(instanceField, latestField reflect.Value) { + for i := 0; i < instanceField.NumField(); i++ { + subField := instanceField.Type().Field(i) + instanceSubField := instanceField.Field(i) + latestSubField := latestField.Field(i) + + if !isExported(subField) || !instanceSubField.CanSet() { + continue + } + + if latestSubField.Kind() == reflect.Ptr { + if !latestSubField.IsNil() && instanceSubField.IsNil() { + instanceSubField.Set(latestSubField) + } + } else if latestSubField.Kind() == reflect.String { + if latestSubField.String() != "" && latestSubField.String() != "NOT_DEFINED" && instanceSubField.String() == "" { + instanceSubField.Set(latestSubField) + } + } else if latestSubField.Kind() == reflect.Struct { + mergeStructFields(instanceSubField, latestSubField) + } else { + if reflect.DeepEqual(instanceSubField.Interface(), reflect.Zero(instanceSubField.Type()).Interface()) { + instanceSubField.Set(latestSubField) + } + } + } +} + +func isExported(field reflect.StructField) bool { + return field.PkgPath == "" } // SetDBCSSystem LifeCycle state when state is provisioning -func SetDBCSDatabaseLifecycleState(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { +func SetDBCSDatabaseLifecycleState(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { dbcsId := *dbcs.Spec.Id @@ -266,47 +902,47 @@ func SetDBCSDatabaseLifecycleState(logger logr.Logger, kubeClient client.Client, // Return if the desired lifecycle state is the same as the current lifecycle state if string(dbcs.Status.State) == string(resp.LifecycleState) { return nil - } else if string(resp.LifecycleState) == string(databasev1alpha1.Available) { + } else if string(resp.LifecycleState) == string(databasev4.Available) { // Change the phase to "Available" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Available, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Available, nwClient, wrClient); statusErr != nil { return statusErr } - } else if string(resp.LifecycleState) == string(databasev1alpha1.Provision) { + } else if string(resp.LifecycleState) == string(databasev4.Provision) { // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Provision, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { return statusErr } // Check the State - _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev1alpha1.Provision), string(databasev1alpha1.Available)) + _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev4.Provision), string(databasev4.Available)) if err != nil { return err } - } else if string(resp.LifecycleState) == string(databasev1alpha1.Update) { + } else if string(resp.LifecycleState) == string(databasev4.Update) { // Change the phase to "Updating" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Update, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { return statusErr } // Check the State - _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev1alpha1.Update), string(databasev1alpha1.Available)) + _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev4.Update), string(databasev4.Available)) if err != nil { return err } - } else if string(resp.LifecycleState) == string(databasev1alpha1.Failed) { + } else if string(resp.LifecycleState) == string(databasev4.Failed) { // Change the phase to "Updating" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Failed, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Failed, nwClient, wrClient); statusErr != nil { return statusErr } return fmt.Errorf("DbSystem is in Failed State") - } else if string(resp.LifecycleState) == string(databasev1alpha1.Terminated) { + } else if string(resp.LifecycleState) == string(databasev4.Terminated) { // Change the phase to "Terminated" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Terminate, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Terminate, nwClient, wrClient); statusErr != nil { return statusErr } } return nil } -func GetDbSystemId(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) error { +func GetDbSystemId(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem) error { dbcsId := *dbcs.Spec.Id dbcsReq := database.GetDbSystemRequest{ @@ -353,7 +989,9 @@ func GetDbSystemId(logger logr.Logger, dbClient database.DatabaseClient, dbcs *d } dbcs.Spec.DbSystem.SubnetId = *response.SubnetId dbcs.Spec.DbSystem.AvailabilityDomain = *response.AvailabilityDomain - + if response.KmsKeyId != nil { + dbcs.Status.KMSDetailsStatus.KeyId = *response.KmsKeyId + } err = PopulateDBDetails(logger, dbClient, dbcs) if err != nil { logger.Info("Error Occurred while collecting the DB details") @@ -362,7 +1000,7 @@ func GetDbSystemId(logger logr.Logger, dbClient database.DatabaseClient, dbcs *d return nil } -func PopulateDBDetails(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) error { +func PopulateDBDetails(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem) error { listDbHomeRsp, err := GetListDbHomeRsp(logger, dbClient, dbcs) if err != nil { @@ -383,7 +1021,7 @@ func PopulateDBDetails(logger logr.Logger, dbClient database.DatabaseClient, dbc return nil } -func GetListDbHomeRsp(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) (database.ListDbHomesResponse, error) { +func GetListDbHomeRsp(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem) (database.ListDbHomesResponse, error) { dbcsId := *dbcs.Spec.Id CompartmentId := dbcs.Spec.DbSystem.CompartmentId @@ -401,7 +1039,7 @@ func GetListDbHomeRsp(logger logr.Logger, dbClient database.DatabaseClient, dbcs return response, nil } -func GetListDatabaseRsp(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, dbHomeId string) (database.ListDatabasesResponse, error) { +func GetListDatabaseRsp(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, dbHomeId string) (database.ListDatabasesResponse, error) { CompartmentId := dbcs.Spec.DbSystem.CompartmentId @@ -418,36 +1056,132 @@ func GetListDatabaseRsp(logger logr.Logger, dbClient database.DatabaseClient, db return response, nil } -func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, kubeClient client.Client, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { - //logger := log.WithName("UpdateDbcsSystemInstance") - +func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, kubeClient client.Client, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient, databaseID string) error { + log.Info("Existing DB System Getting Updated with new details in UpdateDbcsSystemIdInst") + var err error updateFlag := false updateDbcsDetails := database.UpdateDbSystemDetails{} - oldSpec, err := dbcs.GetLastSuccessfulSpec() + // log.Info("Current annotations", "annotations", dbcs.GetAnnotations()) + oldSpec, err := dbcs.GetLastSuccessfulSpecWithLog(log) // Use the new method if err != nil { + log.Error(err, "Failed to get last successful spec") return err } + if oldSpec == nil { + log.Info("oldSpec is nil") + } else { + log.Info("Details of oldSpec", "oldSpec", oldSpec) + } + log.Info("Details of updateFlag -> " + fmt.Sprint(updateFlag)) + if dbcs.Spec.DbSystem.CpuCoreCount > 0 && dbcs.Spec.DbSystem.CpuCoreCount != oldSpec.DbSystem.CpuCoreCount { + log.Info("DB System cpu core count is: " + fmt.Sprint(dbcs.Spec.DbSystem.CpuCoreCount) + " DB System old cpu count is: " + fmt.Sprint(oldSpec.DbSystem.CpuCoreCount)) updateDbcsDetails.CpuCoreCount = common.Int(dbcs.Spec.DbSystem.CpuCoreCount) updateFlag = true } if dbcs.Spec.DbSystem.Shape != "" && dbcs.Spec.DbSystem.Shape != oldSpec.DbSystem.Shape { + // log.Info("DB System desired shape is :" + string(dbcs.Spec.DbSystem.Shape) + "DB System old shape is " + string(oldSpec.DbSystem.Shape)) updateDbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) updateFlag = true } if dbcs.Spec.DbSystem.LicenseModel != "" && dbcs.Spec.DbSystem.LicenseModel != oldSpec.DbSystem.LicenseModel { licenceModel := getLicenceModel(dbcs) + // log.Info("DB System desired License Model is :" + string(dbcs.Spec.DbSystem.LicenseModel) + "DB Sytsem old License Model is " + string(oldSpec.DbSystem.LicenseModel)) updateDbcsDetails.LicenseModel = database.UpdateDbSystemDetailsLicenseModelEnum(licenceModel) updateFlag = true } if dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != 0 && dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != oldSpec.DbSystem.InitialDataStorageSizeInGB { + // log.Info("DB System desired Storage Size is :" + fmt.Sprint(dbcs.Spec.DbSystem.InitialDataStorageSizeInGB) + "DB System old Storage Size is " + fmt.Sprint(oldSpec.DbSystem.InitialDataStorageSizeInGB)) updateDbcsDetails.DataStorageSizeInGBs = &dbcs.Spec.DbSystem.InitialDataStorageSizeInGB updateFlag = true } + // // Check and update KMS details if necessary + if (dbcs.Spec.KMSConfig != databasev4.KMSConfig{}) { + if dbcs.Spec.KMSConfig != oldSpec.DbSystem.KMSConfig { + log.Info("Updating KMS details in Existing Database") + + kmsKeyID := dbcs.Status.KMSDetailsStatus.KeyId + vaultID := dbcs.Status.KMSDetailsStatus.VaultId + tdeWalletPassword := "" + if dbcs.Spec.DbSystem.TdeWalletPasswordSecret != "" { + tdeWalletPassword, err = GetTdePassword(kubeClient, dbcs) + if err != nil { + log.Error(err, "Failed to get TDE wallet password") + } + } else { + log.Info("Its mandatory to define Tde wallet password when KMS Vault is defined. Not updating existing database") + return nil + } + dbAdminPassword := "" + if dbcs.Spec.DbSystem.DbAdminPaswordSecret != "" { + dbAdminPassword, err = GetAdminPassword(kubeClient, dbcs) + if err != nil { + log.Error(err, "Failed to get DB Admin password") + } + } + + // Assign all available fields to KMSConfig + dbcs.Spec.DbSystem.KMSConfig = databasev4.KMSConfig{ + VaultName: dbcs.Spec.KMSConfig.VaultName, + CompartmentId: dbcs.Spec.KMSConfig.CompartmentId, + KeyName: dbcs.Spec.KMSConfig.KeyName, + EncryptionAlgo: dbcs.Spec.KMSConfig.EncryptionAlgo, + VaultType: dbcs.Spec.KMSConfig.VaultType, + } + + // Create the migrate vault key request + migrateRequest := database.MigrateVaultKeyRequest{ + DatabaseId: common.String(databaseID), + MigrateVaultKeyDetails: database.MigrateVaultKeyDetails{ + KmsKeyId: common.String(kmsKeyID), + VaultId: common.String(vaultID), + }, + } + if tdeWalletPassword != "" { + migrateRequest.TdeWalletPassword = common.String(tdeWalletPassword) + } + if dbAdminPassword != "" { + migrateRequest.AdminPassword = common.String(dbAdminPassword) + } + // // Wait for the database to reach the desired state after migration + // err = WaitForDatabaseState(log, dbClient, databaseID, "AVAILABLE") + // if err != nil { + // log.Error(err, "Database did not reach the desired state after migration") + // return err + // } + + // Send the request + migrateResponse, err := dbClient.MigrateVaultKey(context.TODO(), migrateRequest) + if err != nil { + log.Error(err, "Failed to migrate vault key") + return err + } + + // // Check for additional response details (if any) + if migrateResponse.RawResponse.StatusCode != 200 { + log.Error(fmt.Errorf("unexpected status code"), "Migrate vault key request failed", "StatusCode", migrateResponse.RawResponse.StatusCode) + return fmt.Errorf("MigrateVaultKey request failed with status code %d", migrateResponse.RawResponse.StatusCode) + } + + log.Info("MigrateVaultKey request succeeded, waiting for database to reach the desired state") + + // // Wait for the database to reach the desired state after migration + // err = WaitForDatabaseState(log, dbClient, databaseID, "AVAILABLE") + // if err != nil { + // log.Error(err, "Database did not reach the desired state after migration") + // return err + // } + + log.Info("KMS migration process completed successfully") + updateFlag = true + } + } + + log.Info("Details of updateFlag after validations is " + fmt.Sprint(updateFlag)) if updateFlag { updateDbcsRequest := database.UpdateDbSystemRequest{ DbSystemId: common.String(*dbcs.Spec.Id), @@ -459,21 +1193,52 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d } // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Update, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { return statusErr } - // Check the State - _, err = CheckResourceState(log, dbClient, *dbcs.Spec.Id, "UPDATING", "AVAILABLE") + // // Check the State + // _, err = CheckResourceState(log, dbClient, *dbcs.Spec.Id, "UPDATING", "AVAILABLE") + // if err != nil { + // return err + // } + } + + return nil +} + +func WaitForDatabaseState(log logr.Logger, dbClient database.DatabaseClient, dbHomeID string, desiredState database.DbHomeLifecycleStateEnum) error { + deadline := time.Now().Add(timeout) + client, err := database.NewDatabaseClientWithConfigurationProvider(common.DefaultConfigProvider()) + if err != nil { + log.Error(err, "Failed to get DBHome") + return err + } + for time.Now().Before(deadline) { + dbHomeReq := database.GetDbHomeRequest{ + DbHomeId: common.String(dbHomeID), + } + + log.Info("Sending GetDbHome request", "dbHomeID", dbHomeID) + + dbHomeResp, err := client.GetDbHome(context.TODO(), dbHomeReq) if err != nil { + log.Error(err, "Failed to get DBHome") return err } + if dbHomeResp.DbHome.LifecycleState == desiredState { + log.Info("DBHome reached desired state", "DBHomeID", dbHomeID, "State", desiredState) + return nil + } + + log.Info("Waiting for DBHome to reach desired state", "DBHomeID", dbHomeID, "CurrentState", dbHomeResp.DbHome.LifecycleState, "DesiredState", desiredState) + time.Sleep(checkInterval) } + return fmt.Errorf("timed out waiting for DBHome to reach desired state: %s", desiredState) - return nil } -func UpdateDbcsSystemId(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) error { +func UpdateDbcsSystemId(kubeClient client.Client, dbcs *databasev4.DbcsSystem) error { payload := []annotations.PatchValue{{ Op: "replace", Path: "/spec/details", @@ -491,7 +1256,6 @@ func UpdateDbcsSystemId(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSys func CheckResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id string, currentState string, expectedState string) (string, error) { // The database OCID is not available when the provisioning is onging. // Retry until the new DbcsSystem is ready. - // Retry up to 18 times every 10 seconds. var state string var err error @@ -534,7 +1298,7 @@ func GetResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id s return state, nil } -func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { +func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { if dbcs.Spec.Id == nil { dbcs.Status.State = "FAILED" @@ -571,6 +1335,15 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.Dbcs dbcs.Status.Network.ListenerPort = resp.ListenerPort dbcs.Status.Network.HostName = *resp.Hostname dbcs.Status.Network.DomainName = *resp.Domain + if dbcs.Spec.KMSConfig.CompartmentId != "" { + dbcs.Status.KMSDetailsStatus.CompartmentId = dbcs.Spec.KMSConfig.CompartmentId + dbcs.Status.KMSDetailsStatus.VaultName = dbcs.Spec.KMSConfig.VaultName + } + dbcs.Status.State = databasev4.LifecycleState(resp.LifecycleState) + if dbcs.Spec.KMSConfig.CompartmentId != "" { + dbcs.Status.KMSDetailsStatus.CompartmentId = dbcs.Spec.KMSConfig.CompartmentId + dbcs.Status.KMSDetailsStatus.VaultName = dbcs.Spec.KMSConfig.VaultName + } sname, vcnId, err := getSubnetName(*resp.SubnetId, nwClient) @@ -585,7 +1358,7 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.Dbcs } // Work Request Ststaus - dbWorkRequest := databasev1alpha1.DbWorkrequests{} + dbWorkRequest := databasev4.DbWorkrequests{} dbWorks, err := getWorkRequest(*resp.OpcRequestId, wrClient, dbcs) if err == nil { @@ -605,11 +1378,11 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.Dbcs dbWorkRequest.TimeStarted = dbWork.TimeStarted.String() } - if dbWorkRequest != (databasev1alpha1.DbWorkrequests{}) { + if dbWorkRequest != (databasev4.DbWorkrequests{}) { status := checkValue(dbcs, dbWork.Id) if status == 0 { dbcs.Status.WorkRequests = append(dbcs.Status.WorkRequests, dbWorkRequest) - dbWorkRequest = databasev1alpha1.DbWorkrequests{} + dbWorkRequest = databasev4.DbWorkrequests{} } else { setValue(dbcs, dbWorkRequest) } @@ -620,7 +1393,7 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.Dbcs // DB Home Status dbcs.Status.DbInfo = dbcs.Status.DbInfo[:0] - dbStatus := databasev1alpha1.DbStatus{} + dbStatus := databasev4.DbStatus{} dbHomes, err := getDbHomeList(dbClient, dbcs) @@ -636,14 +1409,14 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev1alpha1.Dbcs dbStatus.DbWorkload = *dbDetail.DbWorkload } dbcs.Status.DbInfo = append(dbcs.Status.DbInfo, dbStatus) - dbStatus = databasev1alpha1.DbStatus{} + dbStatus = databasev4.DbStatus{} } } } return nil } -func getDbHomeList(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) ([]database.DbHomeSummary, error) { +func getDbHomeList(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem) ([]database.DbHomeSummary, error) { var items []database.DbHomeSummary dbcsId := *dbcs.Spec.Id @@ -661,7 +1434,7 @@ func getDbHomeList(dbClient database.DatabaseClient, dbcs *databasev1alpha1.Dbcs return resp.Items, nil } -func getDList(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, dbHomeId *string) ([]database.DatabaseSummary, error) { +func getDList(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, dbHomeId *string) ([]database.DatabaseSummary, error) { dbcsId := *dbcs.Spec.Id var items []database.DatabaseSummary @@ -710,7 +1483,7 @@ func getVcnName(vcnId *string, nwClient core.VirtualNetworkClient) (*string, err } // =========== validate Specs ============ -func ValidateSpex(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, nwClient core.VirtualNetworkClient, eRecord record.EventRecorder) error { +func ValidateSpex(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, eRecord record.EventRecorder) error { //var str1 string var eventMsg string diff --git a/commons/dbcssystem/dcommon.go b/commons/dbcssystem/dcommon.go index 3af3a6b4..beaa7c38 100644 --- a/commons/dbcssystem/dcommon.go +++ b/commons/dbcssystem/dcommon.go @@ -49,10 +49,10 @@ import ( "github.com/oracle/oci-go-sdk/v65/workrequests" "sigs.k8s.io/controller-runtime/pkg/client" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" ) -func GetDbHomeDetails(kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) (database.CreateDbHomeDetails, error) { +func GetDbHomeDetails(kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem) (database.CreateDbHomeDetails, error) { dbHomeDetails := database.CreateDbHomeDetails{} @@ -72,7 +72,7 @@ func GetDbHomeDetails(kubeClient client.Client, dbClient database.DatabaseClient return dbHomeDetails, nil } -func GetDbLatestVersion(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, dbSystemId string) (string, error) { +func GetDbLatestVersion(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, dbSystemId string) (string, error) { //var provisionedDbcsSystemId string ctx := context.TODO() @@ -105,27 +105,34 @@ func GetDbLatestVersion(dbClient database.DatabaseClient, dbcs *databasev1alpha1 s2 := getStr(dbcs.Spec.DbSystem.DbVersion, 2) if strings.EqualFold(s1, s2) { val, _ = strconv.Atoi(s1) - if val >= 18 { + if val >= 18 && val <= 21 { s3 := s1 + "c" if strings.EqualFold(s3, dbcs.Spec.DbSystem.DbVersion) { sFlag = 1 break } + } else if val >= 23 { + s3 := s1 + "ai" + if strings.EqualFold(s3, dbcs.Spec.DbSystem.DbVersion) { + sFlag = 1 + break + } + } else if val < 18 && val >= 11 { + s4 := getStr(*version.Version, 4) + if strings.EqualFold(s4, dbcs.Spec.DbSystem.DbVersion) { + sFlag = 1 + break + } } - } else if val < 18 && val >= 11 { - s4 := getStr(*version.Version, 4) - if strings.EqualFold(s4, dbcs.Spec.DbSystem.DbVersion) { - sFlag = 1 - break - } - } + } } } if sFlag == 1 { return *version.Version, nil } + return *version.Version, fmt.Errorf("no database version matched") } @@ -133,7 +140,7 @@ func getStr(str1 string, num int) string { return str1[0:num] } -func GetDBDetails(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (database.CreateDatabaseDetails, error) { +func GetDBDetails(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (database.CreateDatabaseDetails, error) { dbDetails := database.CreateDatabaseDetails{} var val database.CreateDatabaseDetailsDbWorkloadEnum @@ -188,7 +195,7 @@ func GetDBDetails(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) ( return dbDetails, nil } -func getBackupConfig(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (database.DbBackupConfig, error) { +func getBackupConfig(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (database.DbBackupConfig, error) { backupConfig := database.DbBackupConfig{} if dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupEnabled != nil { @@ -216,7 +223,7 @@ func getBackupConfig(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem return backupConfig, nil } -func getBackupWindowEnum(dbcs *databasev1alpha1.DbcsSystem) (database.DbBackupConfigAutoBackupWindowEnum, error) { +func getBackupWindowEnum(dbcs *databasev4.DbcsSystem) (database.DbBackupConfigAutoBackupWindowEnum, error) { if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_ONE" { return database.DbBackupConfigAutoBackupWindowOne, nil @@ -251,7 +258,7 @@ func getBackupWindowEnum(dbcs *databasev1alpha1.DbcsSystem) (database.DbBackupCo //return database.DbBackupConfigAutoBackupWindowEight, fmt.Errorf("AutoBackupWindow values can be SLOT_ONE|SLOT_TWO|SLOT_THREE|SLOT_FOUR|SLOT_FIVE|SLOT_SIX|SLOT_SEVEN|SLOT_EIGHT|SLOT_NINE|SLOT_TEN|SLOT_ELEVEN|SLOT_TWELEVE. The current value set to " + *dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) } -func getRecoveryWindowsInDays(dbcs *databasev1alpha1.DbcsSystem) (int, error) { +func getRecoveryWindowsInDays(dbcs *databasev4.DbcsSystem) (int, error) { var days int @@ -274,7 +281,7 @@ func getRecoveryWindowsInDays(dbcs *databasev1alpha1.DbcsSystem) (int, error) { } func GetDBSystemopts( - dbcs *databasev1alpha1.DbcsSystem) database.DbSystemOptions { + dbcs *databasev4.DbcsSystem) database.DbSystemOptions { dbSystemOpt := database.DbSystemOptions{} @@ -294,7 +301,7 @@ func GetDBSystemopts( return dbSystemOpt } -func getLicenceModel(dbcs *databasev1alpha1.DbcsSystem) database.DbSystemLicenseModelEnum { +func getLicenceModel(dbcs *databasev4.DbcsSystem) database.DbSystemLicenseModelEnum { if dbcs.Spec.DbSystem.LicenseModel == "BRING_YOUR_OWN_LICENSE" { return database.DbSystemLicenseModelBringYourOwnLicense @@ -302,7 +309,7 @@ func getLicenceModel(dbcs *databasev1alpha1.DbcsSystem) database.DbSystemLicense return database.DbSystemLicenseModelLicenseIncluded } -func getDbWorkLoadType(dbcs *databasev1alpha1.DbcsSystem) (database.CreateDatabaseDetailsDbWorkloadEnum, error) { +func getDbWorkLoadType(dbcs *databasev4.DbcsSystem) (database.CreateDatabaseDetailsDbWorkloadEnum, error) { if strings.ToUpper(dbcs.Spec.DbSystem.DbWorkload) == "OLTP" { @@ -317,7 +324,7 @@ func getDbWorkLoadType(dbcs *databasev1alpha1.DbcsSystem) (database.CreateDataba } func GetNodeCount( - dbcs *databasev1alpha1.DbcsSystem) int { + dbcs *databasev4.DbcsSystem) int { if dbcs.Spec.DbSystem.NodeCount != nil { return *dbcs.Spec.DbSystem.NodeCount @@ -327,7 +334,7 @@ func GetNodeCount( } func GetInitialStorage( - dbcs *databasev1alpha1.DbcsSystem) int { + dbcs *databasev4.DbcsSystem) int { if dbcs.Spec.DbSystem.InitialDataStorageSizeInGB > 0 { return dbcs.Spec.DbSystem.InitialDataStorageSizeInGB @@ -335,7 +342,7 @@ func GetInitialStorage( return 256 } -func GetDBEdition(dbcs *databasev1alpha1.DbcsSystem) database.LaunchDbSystemDetailsDatabaseEditionEnum { +func GetDBEdition(dbcs *databasev4.DbcsSystem) database.LaunchDbSystemDetailsDatabaseEditionEnum { if dbcs.Spec.DbSystem.ClusterName != "" { return database.LaunchDbSystemDetailsDatabaseEditionEnterpriseEditionExtremePerformance @@ -360,7 +367,7 @@ func GetDBEdition(dbcs *databasev1alpha1.DbcsSystem) database.LaunchDbSystemDeta } func GetDBbDiskRedundancy( - dbcs *databasev1alpha1.DbcsSystem) database.LaunchDbSystemDetailsDiskRedundancyEnum { + dbcs *databasev4.DbcsSystem) database.LaunchDbSystemDetailsDiskRedundancyEnum { if dbcs.Spec.DbSystem.ClusterName != "" { return database.LaunchDbSystemDetailsDiskRedundancyHigh @@ -376,7 +383,7 @@ func GetDBbDiskRedundancy( return database.LaunchDbSystemDetailsDiskRedundancyNormal } -func getWorkRequest(workId string, wrClient workrequests.WorkRequestClient, dbcs *databasev1alpha1.DbcsSystem) ([]workrequests.WorkRequestSummary, error) { +func getWorkRequest(workId string, wrClient workrequests.WorkRequestClient, dbcs *databasev4.DbcsSystem) ([]workrequests.WorkRequestSummary, error) { var workReq []workrequests.WorkRequestSummary req := workrequests.ListWorkRequestsRequest{CompartmentId: &dbcs.Spec.DbSystem.CompartmentId, OpcRequestId: &workId, ResourceId: dbcs.Spec.Id} @@ -405,10 +412,10 @@ func GetFmtStr(pstr string) string { return "[" + pstr + "]" } -func checkValue(dbcs *databasev1alpha1.DbcsSystem, workId *string) int { +func checkValue(dbcs *databasev4.DbcsSystem, workId *string) int { var status int = 0 - //dbWorkRequest := databasev1alpha1.DbWorkrequests{} + //dbWorkRequest := databasev4.DbWorkrequests{} if len(dbcs.Status.WorkRequests) > 0 { for _, v := range dbcs.Status.WorkRequests { @@ -420,10 +427,10 @@ func checkValue(dbcs *databasev1alpha1.DbcsSystem, workId *string) int { return status } -func setValue(dbcs *databasev1alpha1.DbcsSystem, dbWorkRequest databasev1alpha1.DbWorkrequests) { +func setValue(dbcs *databasev4.DbcsSystem, dbWorkRequest databasev4.DbWorkrequests) { //var status int = 1 - //dbWorkRequest := databasev1alpha1.DbWorkrequests{} + //dbWorkRequest := databasev4.DbWorkrequests{} var counter int = 0 if len(dbcs.Status.WorkRequests) > 0 { for _, v := range dbcs.Status.WorkRequests { diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index 58f07490..96190250 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -44,7 +44,7 @@ import ( "strconv" "github.com/go-logr/logr" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -53,7 +53,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func buildLabelsForCatalog(instance *databasev1alpha1.ShardingDatabase, label string) map[string]string { +func buildLabelsForCatalog(instance *databasev4.ShardingDatabase, label string) map[string]string { return map[string]string{ "app": "OracleSharding", "type": "Catalog", @@ -61,7 +61,7 @@ func buildLabelsForCatalog(instance *databasev1alpha1.ShardingDatabase, label st } } -func getLabelForCatalog(instance *databasev1alpha1.ShardingDatabase) string { +func getLabelForCatalog(instance *databasev4.ShardingDatabase) string { // if len(OraCatalogSpex.Label) !=0 { // return OraCatalogSpex.Label @@ -70,7 +70,7 @@ func getLabelForCatalog(instance *databasev1alpha1.ShardingDatabase) string { return instance.Name } -func BuildStatefulSetForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) *appsv1.StatefulSet { +func BuildStatefulSetForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) *appsv1.StatefulSet { sfset := &appsv1.StatefulSet{ TypeMeta: buildTypeMetaForCatalog(), ObjectMeta: builObjectMetaForCatalog(instance, OraCatalogSpex), @@ -91,7 +91,7 @@ func buildTypeMetaForCatalog() metav1.TypeMeta { } // Function to build ObjectMeta -func builObjectMetaForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) metav1.ObjectMeta { +func builObjectMetaForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) metav1.ObjectMeta { // building objectMeta objmeta := metav1.ObjectMeta{ Name: OraCatalogSpex.Name, @@ -103,7 +103,7 @@ func builObjectMetaForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCa } // Function to build Stateful Specs -func buildStatefulSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) *appsv1.StatefulSetSpec { +func buildStatefulSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) *appsv1.StatefulSetSpec { // building Stateful set Specs sfsetspec := &appsv1.StatefulSetSpec{ @@ -131,7 +131,7 @@ func buildStatefulSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, Or // Function to build PodSpec -func buildPodSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) *corev1.PodSpec { +func buildPodSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) *corev1.PodSpec { user := oraRunAsUser group := oraFsGroup @@ -166,7 +166,7 @@ func buildPodSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCata } // Function to build Volume Spec -func buildVolumeSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) []corev1.Volume { +func buildVolumeSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) []corev1.Volume { var result []corev1.Volume result = []corev1.Volume{ { @@ -207,7 +207,7 @@ func buildVolumeSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraC } // Function to build the container Specification -func buildContainerSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) []corev1.Container { +func buildContainerSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) []corev1.Container { // building Continer spec var result []corev1.Container containerSpec := corev1.Container{ @@ -284,7 +284,7 @@ func buildContainerSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, O } // Function to build the init Container Spec -func buildInitContainerSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) []corev1.Container { +func buildInitContainerSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) []corev1.Container { var result []corev1.Container // building the init Container Spec privFlag := true @@ -320,7 +320,7 @@ func buildInitContainerSpecForCatalog(instance *databasev1alpha1.ShardingDatabas return result } -func buildVolumeMountSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) []corev1.VolumeMount { +func buildVolumeMountSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) []corev1.VolumeMount { var result []corev1.VolumeMount result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "secretmap-vol3", MountPath: oraSecretMount, ReadOnly: true}) result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "-oradata-vol4", MountPath: oraDataMount}) @@ -345,7 +345,7 @@ func buildVolumeMountSpecForCatalog(instance *databasev1alpha1.ShardingDatabase, return result } -func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec) []corev1.PersistentVolumeClaim { +func volumeClaimTemplatesForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) []corev1.PersistentVolumeClaim { var claims []corev1.PersistentVolumeClaim @@ -417,7 +417,7 @@ func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, return claims } -func BuildServiceDefForCatalog(instance *databasev1alpha1.ShardingDatabase, replicaCount int32, OraCatalogSpex databasev1alpha1.CatalogSpec, svctype string) *corev1.Service { +func BuildServiceDefForCatalog(instance *databasev4.ShardingDatabase, replicaCount int32, OraCatalogSpex databasev4.CatalogSpec, svctype string) *corev1.Service { //service := &corev1.Service{} service := &corev1.Service{ ObjectMeta: buildSvcObjectMetaForCatalog(instance, replicaCount, OraCatalogSpex, svctype), @@ -441,7 +441,7 @@ func BuildServiceDefForCatalog(instance *databasev1alpha1.ShardingDatabase, repl } // Function to build Service ObjectMeta -func buildSvcObjectMetaForCatalog(instance *databasev1alpha1.ShardingDatabase, replicaCount int32, OraCatalogSpex databasev1alpha1.CatalogSpec, svctype string) metav1.ObjectMeta { +func buildSvcObjectMetaForCatalog(instance *databasev4.ShardingDatabase, replicaCount int32, OraCatalogSpex databasev4.CatalogSpec, svctype string) metav1.ObjectMeta { // building objectMeta var svcName string if svctype == "local" { @@ -461,7 +461,7 @@ func buildSvcObjectMetaForCatalog(instance *databasev1alpha1.ShardingDatabase, r return objmeta } -func getSvcLabelsForCatalog(replicaCount int32, OraCatalogSpex databasev1alpha1.CatalogSpec) map[string]string { +func getSvcLabelsForCatalog(replicaCount int32, OraCatalogSpex databasev4.CatalogSpec) map[string]string { var labelStr map[string]string = make(map[string]string) if replicaCount == -1 { @@ -475,8 +475,8 @@ func getSvcLabelsForCatalog(replicaCount int32, OraCatalogSpex databasev1alpha1. } // ======================== update Section ======================== -func UpdateProvForCatalog(instance *databasev1alpha1.ShardingDatabase, - OraCatalogSpex databasev1alpha1.CatalogSpec, kClient client.Client, sfSet *appsv1.StatefulSet, catalogPod *corev1.Pod, logger logr.Logger, +func UpdateProvForCatalog(instance *databasev4.ShardingDatabase, + OraCatalogSpex databasev4.CatalogSpec, kClient client.Client, sfSet *appsv1.StatefulSet, catalogPod *corev1.Pod, logger logr.Logger, ) (ctrl.Result, error) { var isUpdate bool = false diff --git a/commons/sharding/exec.go b/commons/sharding/exec.go index 44f91e51..7d575320 100644 --- a/commons/sharding/exec.go +++ b/commons/sharding/exec.go @@ -44,7 +44,7 @@ import ( "net/http" "time" - databasealphav1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" @@ -61,7 +61,7 @@ import ( ) // ExecCMDInContainer execute command in first container of a pod -func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasealphav1.ShardingDatabase, logger logr.Logger) (string, string, error) { +func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasev4.ShardingDatabase, logger logr.Logger) (string, string, error) { var err1 error = nil var msg string @@ -136,7 +136,7 @@ func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, return execOut.String(), execErr.String(), nil } -func GetPodCopyConfig(kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasealphav1.ShardingDatabase, logger logr.Logger) (*rest.Config, *kubernetes.Clientset, error) { +func GetPodCopyConfig(kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasev4.ShardingDatabase, logger logr.Logger) (*rest.Config, *kubernetes.Clientset, error) { var clientSet *kubernetes.Clientset config, err := kubeConfig.ClientConfig() @@ -152,7 +152,7 @@ func GetPodCopyConfig(kubeClient kubernetes.Interface, kubeConfig clientcmd.Clie } -func KctlCopyFile(kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasealphav1.ShardingDatabase, restConfig *rest.Config, kclientset *kubernetes.Clientset, logger logr.Logger, src string, dst string, containername string) (*bytes.Buffer, *bytes.Buffer, *bytes.Buffer, error) { +func KctlCopyFile(kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasev4.ShardingDatabase, restConfig *rest.Config, kclientset *kubernetes.Clientset, logger logr.Logger, src string, dst string, containername string) (*bytes.Buffer, *bytes.Buffer, *bytes.Buffer, error) { var in, out, errOut *bytes.Buffer var ioStreams genericclioptions.IOStreams diff --git a/commons/sharding/gsm.go b/commons/sharding/gsm.go index bcdc8866..33f76ece 100644 --- a/commons/sharding/gsm.go +++ b/commons/sharding/gsm.go @@ -44,7 +44,7 @@ import ( "reflect" "strconv" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" @@ -56,7 +56,7 @@ import ( ) // Constants for hello-stateful StatefulSet & Volumes -func buildLabelsForGsm(instance *databasev1alpha1.ShardingDatabase, label string) map[string]string { +func buildLabelsForGsm(instance *databasev4.ShardingDatabase, label string) map[string]string { return map[string]string{ "app": "OracleGsming", "shard_name": "Gsm", @@ -64,7 +64,7 @@ func buildLabelsForGsm(instance *databasev1alpha1.ShardingDatabase, label string } } -func getLabelForGsm(instance *databasev1alpha1.ShardingDatabase) string { +func getLabelForGsm(instance *databasev4.ShardingDatabase) string { // if len(OraGsmSpex.Label) !=0 { // return OraGsmSpex.Label @@ -73,7 +73,7 @@ func getLabelForGsm(instance *databasev1alpha1.ShardingDatabase) string { return instance.Name } -func BuildStatefulSetForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) *appsv1.StatefulSet { +func BuildStatefulSetForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec) *appsv1.StatefulSet { sfset := &appsv1.StatefulSet{ TypeMeta: buildTypeMetaForGsm(), ObjectMeta: builObjectMetaForGsm(instance, OraGsmSpex), @@ -93,7 +93,7 @@ func buildTypeMetaForGsm() metav1.TypeMeta { } // Function to build ObjectMeta -func builObjectMetaForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) metav1.ObjectMeta { +func builObjectMetaForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec) metav1.ObjectMeta { // building objectMeta objmeta := metav1.ObjectMeta{ Name: OraGsmSpex.Name, @@ -105,7 +105,7 @@ func builObjectMetaForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpe } // Function to build Stateful Specs -func buildStatefulSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) *appsv1.StatefulSetSpec { +func buildStatefulSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec) *appsv1.StatefulSetSpec { // building Stateful set Specs sfsetspec := &appsv1.StatefulSetSpec{ @@ -136,7 +136,7 @@ func buildStatefulSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsm // Function to build PodSpec -func buildPodSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) *corev1.PodSpec { +func buildPodSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec) *corev1.PodSpec { user := oraRunAsUser group := oraFsGroup @@ -170,7 +170,7 @@ func buildPodSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex } // Function to build Volume Spec -func buildVolumeSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) []corev1.Volume { +func buildVolumeSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec) []corev1.Volume { var result []corev1.Volume result = []corev1.Volume{ { @@ -204,7 +204,7 @@ func buildVolumeSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSp } // Function to build the container Specification -func buildContainerSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) []corev1.Container { +func buildContainerSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec) []corev1.Container { // building Continer spec var result []corev1.Container var masterGsmFlag = false @@ -272,7 +272,7 @@ func buildContainerSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGs } // Function to build the init Container Spec -func buildInitContainerSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) []corev1.Container { +func buildInitContainerSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec) []corev1.Container { var result []corev1.Container // building the init Container Spec privFlag := true @@ -309,7 +309,7 @@ func buildInitContainerSpecForGsm(instance *databasev1alpha1.ShardingDatabase, O return result } -func buildVolumeMountSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) []corev1.VolumeMount { +func buildVolumeMountSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec) []corev1.VolumeMount { var result []corev1.VolumeMount result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "secretmap-vol3", MountPath: oraSecretMount, ReadOnly: true}) result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "-oradata-vol4", MountPath: oraGsmDataMount}) @@ -325,7 +325,7 @@ func buildVolumeMountSpecForGsm(instance *databasev1alpha1.ShardingDatabase, Ora return result } -func volumeClaimTemplatesForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec) []corev1.PersistentVolumeClaim { +func volumeClaimTemplatesForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec) []corev1.PersistentVolumeClaim { var claims []corev1.PersistentVolumeClaim @@ -361,7 +361,7 @@ func volumeClaimTemplatesForGsm(instance *databasev1alpha1.ShardingDatabase, Ora return claims } -func BuildServiceDefForGsm(instance *databasev1alpha1.ShardingDatabase, replicaCount int32, OraGsmSpex databasev1alpha1.GsmSpec, svctype string) *corev1.Service { +func BuildServiceDefForGsm(instance *databasev4.ShardingDatabase, replicaCount int32, OraGsmSpex databasev4.GsmSpec, svctype string) *corev1.Service { //service := &corev1.Service{} service := &corev1.Service{ ObjectMeta: buildSvcObjectMetaForGsm(instance, replicaCount, OraGsmSpex, svctype), @@ -385,7 +385,7 @@ func BuildServiceDefForGsm(instance *databasev1alpha1.ShardingDatabase, replicaC } // Function to build Service ObjectMeta -func buildSvcObjectMetaForGsm(instance *databasev1alpha1.ShardingDatabase, replicaCount int32, OraGsmSpex databasev1alpha1.GsmSpec, svctype string) metav1.ObjectMeta { +func buildSvcObjectMetaForGsm(instance *databasev4.ShardingDatabase, replicaCount int32, OraGsmSpex databasev4.GsmSpec, svctype string) metav1.ObjectMeta { // building objectMeta var svcName string if svctype == "local" { @@ -405,7 +405,7 @@ func buildSvcObjectMetaForGsm(instance *databasev1alpha1.ShardingDatabase, repli return objmeta } -func getSvcLabelsForGsm(replicaCount int32, OraGsmSpex databasev1alpha1.GsmSpec) map[string]string { +func getSvcLabelsForGsm(replicaCount int32, OraGsmSpex databasev4.GsmSpec) map[string]string { var labelStr map[string]string = make(map[string]string) if replicaCount == -1 { @@ -419,8 +419,8 @@ func getSvcLabelsForGsm(replicaCount int32, OraGsmSpex databasev1alpha1.GsmSpec) } // This function cleanup the shard from GSM -func OraCleanupForGsm(instance *databasev1alpha1.ShardingDatabase, - OraGsmSpex databasev1alpha1.GsmSpec, +func OraCleanupForGsm(instance *databasev4.ShardingDatabase, + OraGsmSpex databasev4.GsmSpec, oldReplicaSize int32, newReplicaSize int32, ) string { @@ -435,8 +435,8 @@ func OraCleanupForGsm(instance *databasev1alpha1.ShardingDatabase, return err1 } -func UpdateProvForGsm(instance *databasev1alpha1.ShardingDatabase, - OraGsmSpex databasev1alpha1.GsmSpec, kClient client.Client, sfSet *appsv1.StatefulSet, gsmPod *corev1.Pod, logger logr.Logger, +func UpdateProvForGsm(instance *databasev4.ShardingDatabase, + OraGsmSpex databasev4.GsmSpec, kClient client.Client, sfSet *appsv1.StatefulSet, gsmPod *corev1.Pod, logger logr.Logger, ) (ctrl.Result, error) { var msg string diff --git a/commons/sharding/provstatus.go b/commons/sharding/provstatus.go index 87796553..f5bf6767 100644 --- a/commons/sharding/provstatus.go +++ b/commons/sharding/provstatus.go @@ -42,7 +42,7 @@ import ( "fmt" "strconv" - databasealphav1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/go-logr/logr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -52,7 +52,7 @@ import ( ) // CHeck if record exist in a struct -func CheckGsmStatusInst(instSpex []databasealphav1.GsmStatusDetails, name string, +func CheckGsmStatusInst(instSpex []databasev4.GsmStatusDetails, name string, ) (int, bool) { var status bool = false @@ -69,9 +69,9 @@ func CheckGsmStatusInst(instSpex []databasealphav1.GsmStatusDetails, name string return idx, status } -func UpdateGsmStatusData(instance *databasealphav1.ShardingDatabase, Specidx int, state string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +func UpdateGsmStatusData(instance *databasev4.ShardingDatabase, Specidx int, state string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, ) { - if state == string(databasealphav1.AvailableState) { + if state == string(databasev4.AvailableState) { // Evaluate following values only if state is set to available svcName := instance.Spec.Gsm[Specidx].Name + "-0." + instance.Spec.Gsm[Specidx].Name k8sExternalSvcName := svcName + strconv.FormatInt(int64(0), 10) + "-svc." + getInstanceNs(instance) + ".svc.cluster.local" @@ -85,40 +85,40 @@ func UpdateGsmStatusData(instance *databasealphav1.ShardingDatabase, Specidx int // internIp := strings.Replace(K8sExternalSvcIP, "/r/n", "", -1) // Populate the Maps - insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.Name), instance.Spec.Gsm[Specidx].Name) - insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.DbPasswordSecret), DbPasswordSecret) + insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.Name), instance.Spec.Gsm[Specidx].Name) + insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.DbPasswordSecret), DbPasswordSecret) if instance.Spec.IsExternalSvc == true { - insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sExternalSvc), k8sExternalSvcName) - insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sExternalSvcIP), K8sExternalSvcIP) + insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sExternalSvc), k8sExternalSvcName) + insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sExternalSvcIP), K8sExternalSvcIP) } - insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sInternalSvc), K8sInternalSvcName) - insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sInternalSvcIP), K8sInternalSvcIP) - insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.State), state) - } else if state == string(databasealphav1.Terminated) { - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.Name)) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sInternalSvc)) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sExternalSvc)) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sExternalSvcIP)) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sInternalSvcIP)) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.Role)) + insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sInternalSvc), K8sInternalSvcName) + insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sInternalSvcIP), K8sInternalSvcIP) + insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.State), state) + } else if state == string(databasev4.Terminated) { + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.Name)) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sInternalSvc)) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sExternalSvc)) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sExternalSvcIP)) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sInternalSvcIP)) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.Role)) instance.Status.Gsm.Services = "" } else { - insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.Name), instance.Spec.Gsm[Specidx].Name) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sInternalSvc)) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sExternalSvc)) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sExternalSvcIP)) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.K8sInternalSvcIP)) - removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasealphav1.Role)) + insertOrUpdateGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.Name), instance.Spec.Gsm[Specidx].Name) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sInternalSvc)) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sExternalSvc)) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sExternalSvcIP)) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.K8sInternalSvcIP)) + removeGsmKeys(instance, instance.Spec.Gsm[Specidx].Name, string(databasev4.Role)) instance.Status.Gsm.Services = "" } } -func UpdateCatalogStatusData(instance *databasealphav1.ShardingDatabase, Specidx int, state string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +func UpdateCatalogStatusData(instance *databasev4.ShardingDatabase, Specidx int, state string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, ) { mode := GetDbOpenMode(instance.Spec.Catalog[Specidx].Name+"-0", instance, kubeClient, kubeConfig, logger) - if state == string(databasealphav1.AvailableState) { + if state == string(databasev4.AvailableState) { // Evaluate following values only if state is set to available svcName := instance.Spec.Catalog[Specidx].Name + "-0." + instance.Spec.Catalog[Specidx].Name k8sExternalSvcName := svcName + strconv.FormatInt(int64(0), 10) + "-svc." + getInstanceNs(instance) + ".svc.cluster.local" @@ -133,52 +133,52 @@ func UpdateCatalogStatusData(instance *databasealphav1.ShardingDatabase, Specidx // internIp := strings.Replace(K8sExternalSvcIP, "/r/n", "", -1) // Populate the Maps - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.Name), instance.Spec.Catalog[Specidx].Name) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.DbPasswordSecret), DbPasswordSecret) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.Name), instance.Spec.Catalog[Specidx].Name) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.DbPasswordSecret), DbPasswordSecret) if instance.Spec.IsExternalSvc == true { - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sExternalSvc), k8sExternalSvcName) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sExternalSvcIP), K8sExternalSvcIP) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sExternalSvc), k8sExternalSvcName) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sExternalSvcIP), K8sExternalSvcIP) } - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sInternalSvc), K8sInternalSvcName) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sInternalSvcIP), K8sInternalSvcIP) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.State), state) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.OracleSid), oracleSid) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.OraclePdb), oraclePdb) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.Role), role) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.OpenMode), mode) - } else if state == string(databasealphav1.Terminated) { - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.State)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.Name)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sInternalSvc)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sExternalSvc)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sExternalSvcIP)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sInternalSvcIP)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.Role)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.OraclePdb)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.OracleSid)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.Role)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.OpenMode)) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sInternalSvc), K8sInternalSvcName) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sInternalSvcIP), K8sInternalSvcIP) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.State), state) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.OracleSid), oracleSid) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.OraclePdb), oraclePdb) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.Role), role) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.OpenMode), mode) + } else if state == string(databasev4.Terminated) { + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.State)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.Name)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sInternalSvc)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sExternalSvc)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sExternalSvcIP)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sInternalSvcIP)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.Role)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.OraclePdb)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.OracleSid)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.Role)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.OpenMode)) } else { - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.State), state) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.Name), instance.Spec.Catalog[Specidx].Name) - insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.OpenMode), mode) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sInternalSvc)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sExternalSvc)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sExternalSvcIP)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.K8sInternalSvcIP)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.Role)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.OraclePdb)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.OracleSid)) - removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasealphav1.Role)) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.State), state) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.Name), instance.Spec.Catalog[Specidx].Name) + insertOrUpdateCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.OpenMode), mode) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sInternalSvc)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sExternalSvc)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sExternalSvcIP)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.K8sInternalSvcIP)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.Role)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.OraclePdb)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.OracleSid)) + removeCatalogKeys(instance, instance.Spec.Catalog[Specidx].Name, string(databasev4.Role)) } } -func UpdateShardStatusData(instance *databasealphav1.ShardingDatabase, Specidx int, state string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +func UpdateShardStatusData(instance *databasev4.ShardingDatabase, Specidx int, state string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, ) { mode := GetDbOpenMode(instance.Spec.Shard[Specidx].Name+"-0", instance, kubeClient, kubeConfig, logger) - if state == string(databasealphav1.AvailableState) { + if state == string(databasev4.AvailableState) { // Evaluate following values only if state is set to available svcName := instance.Spec.Shard[Specidx].Name + "-0." + instance.Spec.Shard[Specidx].Name k8sExternalSvcName := svcName + strconv.FormatInt(int64(0), 10) + "-svc." + getInstanceNs(instance) + ".svc.cluster.local" @@ -191,49 +191,49 @@ func UpdateShardStatusData(instance *databasealphav1.ShardingDatabase, Specidx i role := GetDbRole(instance.Spec.Shard[Specidx].Name+"-0", instance, kubeClient, kubeConfig, logger) // Populate the Maps - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.Name), instance.Spec.Shard[Specidx].Name) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.DbPasswordSecret), DbPasswordSecret) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.Name), instance.Spec.Shard[Specidx].Name) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.DbPasswordSecret), DbPasswordSecret) if instance.Spec.IsExternalSvc == true { - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sExternalSvc), k8sExternalSvcName) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sExternalSvcIP), K8sExternalSvcIP) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sExternalSvc), k8sExternalSvcName) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sExternalSvcIP), K8sExternalSvcIP) } - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sInternalSvc), K8sInternalSvcName) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sInternalSvcIP), K8sInternalSvcIP) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.State), state) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.OracleSid), oracleSid) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.OraclePdb), oraclePdb) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.Role), role) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.OpenMode), mode) - } else if state == string(databasealphav1.Terminated) { - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.State)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.Name)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sInternalSvc)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sExternalSvc)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sExternalSvcIP)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sInternalSvcIP)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.Role)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.OraclePdb)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.OracleSid)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.Role)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.OpenMode)) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sInternalSvc), K8sInternalSvcName) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sInternalSvcIP), K8sInternalSvcIP) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.State), state) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.OracleSid), oracleSid) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.OraclePdb), oraclePdb) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.Role), role) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.OpenMode), mode) + } else if state == string(databasev4.Terminated) { + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.State)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.Name)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sInternalSvc)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sExternalSvc)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sExternalSvcIP)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sInternalSvcIP)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.Role)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.OraclePdb)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.OracleSid)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.Role)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.OpenMode)) } else { - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.State), state) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.Name), instance.Spec.Shard[Specidx].Name) - insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.OpenMode), mode) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sInternalSvc)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sExternalSvc)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sExternalSvcIP)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.K8sInternalSvcIP)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.Role)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.OraclePdb)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.OracleSid)) - removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasealphav1.Role)) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.State), state) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.Name), instance.Spec.Shard[Specidx].Name) + insertOrUpdateShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.OpenMode), mode) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sInternalSvc)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sExternalSvc)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sExternalSvcIP)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.K8sInternalSvcIP)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.Role)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.OraclePdb)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.OracleSid)) + removeShardKeys(instance, instance.Spec.Shard[Specidx].Name, string(databasev4.Role)) } } -func insertOrUpdateShardKeys(instance *databasealphav1.ShardingDatabase, name string, key string, value string) { +func insertOrUpdateShardKeys(instance *databasev4.ShardingDatabase, name string, key string, value string) { newKey := name + "_" + key if len(instance.Status.Shard) > 0 { if _, ok := instance.Status.Shard[newKey]; ok { @@ -248,7 +248,7 @@ func insertOrUpdateShardKeys(instance *databasealphav1.ShardingDatabase, name st } -func removeShardKeys(instance *databasealphav1.ShardingDatabase, name string, key string) { +func removeShardKeys(instance *databasev4.ShardingDatabase, name string, key string) { newKey := name + "_" + key if len(instance.Status.Shard) > 0 { if _, ok := instance.Status.Shard[newKey]; ok { @@ -258,7 +258,7 @@ func removeShardKeys(instance *databasealphav1.ShardingDatabase, name string, ke } } -func insertOrUpdateCatalogKeys(instance *databasealphav1.ShardingDatabase, name string, key string, value string) { +func insertOrUpdateCatalogKeys(instance *databasev4.ShardingDatabase, name string, key string, value string) { newKey := name + "_" + key if len(instance.Status.Catalog) > 0 { if _, ok := instance.Status.Catalog[newKey]; ok { @@ -273,7 +273,7 @@ func insertOrUpdateCatalogKeys(instance *databasealphav1.ShardingDatabase, name } -func removeCatalogKeys(instance *databasealphav1.ShardingDatabase, name string, key string) { +func removeCatalogKeys(instance *databasev4.ShardingDatabase, name string, key string) { newKey := name + "_" + key if len(instance.Status.Catalog) > 0 { if _, ok := instance.Status.Catalog[newKey]; ok { @@ -283,7 +283,7 @@ func removeCatalogKeys(instance *databasealphav1.ShardingDatabase, name string, } } -func insertOrUpdateGsmKeys(instance *databasealphav1.ShardingDatabase, name string, key string, value string) { +func insertOrUpdateGsmKeys(instance *databasev4.ShardingDatabase, name string, key string, value string) { newKey := name + "_" + key if len(instance.Status.Gsm.Details) > 0 { if _, ok := instance.Status.Gsm.Details[newKey]; ok { @@ -298,7 +298,7 @@ func insertOrUpdateGsmKeys(instance *databasealphav1.ShardingDatabase, name stri } -func removeGsmKeys(instance *databasealphav1.ShardingDatabase, name string, key string) { +func removeGsmKeys(instance *databasev4.ShardingDatabase, name string, key string) { newKey := name + "_" + key if len(instance.Status.Gsm.Details) > 0 { if _, ok := instance.Status.Gsm.Details[newKey]; ok { @@ -308,7 +308,7 @@ func removeGsmKeys(instance *databasealphav1.ShardingDatabase, name string, key } } -func getInstanceNs(instance *databasealphav1.ShardingDatabase) string { +func getInstanceNs(instance *databasev4.ShardingDatabase) string { var namespace string if instance.Spec.Namespace == "" { namespace = "default" @@ -319,7 +319,7 @@ func getInstanceNs(instance *databasealphav1.ShardingDatabase) string { } // File the meta condition and return the meta view -func GetMetaCondition(instance *databasealphav1.ShardingDatabase, result *ctrl.Result, err *error, stateType string, stateMsg string) metav1.Condition { +func GetMetaCondition(instance *databasev4.ShardingDatabase, result *ctrl.Result, err *error, stateType string, stateMsg string) metav1.Condition { return metav1.Condition{ Type: stateType, @@ -332,7 +332,7 @@ func GetMetaCondition(instance *databasealphav1.ShardingDatabase, result *ctrl.R } // ======================= CHeck GSM Director Status ============== -func CheckGsmStatus(gname string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func CheckGsmStatus(gname string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { var err error var msg string = "Inside the checkGsmStatus. Checking GSM director in " + GetFmtStr(gname) + " pod." @@ -349,7 +349,7 @@ func CheckGsmStatus(gname string, instance *databasealphav1.ShardingDatabase, ku // ============ Functiont o check the status of the Shard and catalog ========= // ================================ Validate shard =========================== -func ValidateDbSetup(podName string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func ValidateDbSetup(podName string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { _, _, err := ExecCommand(podName, shardValidationCmd(), kubeClient, kubeconfig, instance, logger) @@ -359,7 +359,7 @@ func ValidateDbSetup(podName string, instance *databasealphav1.ShardingDatabase, return nil } -func UpdateGsmShardStatus(instance *databasealphav1.ShardingDatabase, name string, state string) { +func UpdateGsmShardStatus(instance *databasev4.ShardingDatabase, name string, state string) { //smap := make(map[string]string) if _, ok := instance.Status.Gsm.Shards[name]; ok { instance.Status.Gsm.Shards[name] = state @@ -383,7 +383,7 @@ func UpdateGsmShardStatus(instance *databasealphav1.ShardingDatabase, name strin } -func GetGsmShardStatus(instance *databasealphav1.ShardingDatabase, name string) string { +func GetGsmShardStatus(instance *databasev4.ShardingDatabase, name string) string { if _, ok := instance.Status.Gsm.Shards[name]; ok { return instance.Status.Gsm.Shards[name] @@ -392,7 +392,7 @@ func GetGsmShardStatus(instance *databasealphav1.ShardingDatabase, name string) } -func GetGsmShardStatusKey(instance *databasealphav1.ShardingDatabase, key string) string { +func GetGsmShardStatusKey(instance *databasev4.ShardingDatabase, key string) string { if _, ok := instance.Status.Shard[key]; ok { return instance.Status.Shard[key] @@ -401,7 +401,7 @@ func GetGsmShardStatusKey(instance *databasealphav1.ShardingDatabase, key string } -func GetGsmCatalogStatusKey(instance *databasealphav1.ShardingDatabase, key string) string { +func GetGsmCatalogStatusKey(instance *databasev4.ShardingDatabase, key string) string { if _, ok := instance.Status.Catalog[key]; ok { return instance.Status.Catalog[key] @@ -410,7 +410,7 @@ func GetGsmCatalogStatusKey(instance *databasealphav1.ShardingDatabase, key stri } -func GetGsmDetailsSttausKey(instance *databasealphav1.ShardingDatabase, key string) string { +func GetGsmDetailsSttausKey(instance *databasev4.ShardingDatabase, key string) string { if _, ok := instance.Status.Gsm.Details[key]; ok { return instance.Status.Gsm.Details[key] diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 99987661..b5cb0749 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -44,8 +44,7 @@ import ( "fmt" "slices" - databasealphav1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" "regexp" "strconv" @@ -103,7 +102,7 @@ const ( ) // Function to build the env var specification -func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []databasealphav1.EnvironmentVariable, name string, restype string, masterFlag bool, directorParams string) []corev1.EnvVar { +func buildEnvVarsSpec(instance *databasev4.ShardingDatabase, variables []databasev4.EnvironmentVariable, name string, restype string, masterFlag bool, directorParams string) []corev1.EnvVar { var result []corev1.EnvVar var varinfo string var sidFlag bool = false @@ -351,7 +350,7 @@ func buildEnvVarsSpec(instance *databasealphav1.ShardingDatabase, variables []da } // FUnction to build the svc definition for catalog/shard and GSM -func buildSvcPortsDef(instance *databasealphav1.ShardingDatabase, resType string) []corev1.ServicePort { +func buildSvcPortsDef(instance *databasev4.ShardingDatabase, resType string) []corev1.ServicePort { var result []corev1.ServicePort if len(instance.Spec.PortMappings) > 0 { for _, portMapping := range instance.Spec.PortMappings { @@ -393,12 +392,12 @@ func generateName(base string) string { } // Function to generate the port mapping -func generatePortMapping(portMapping databasealphav1.PortMapping) string { +func generatePortMapping(portMapping databasev4.PortMapping) string { return generateName(fmt.Sprintf("%s-%d-%d-", "tcp", portMapping.Port, portMapping.TargetPort)) } -func LogMessages(msgtype string, msg string, err error, instance *databasealphav1.ShardingDatabase, logger logr.Logger) { +func LogMessages(msgtype string, msg string, err error, instance *databasev4.ShardingDatabase, logger logr.Logger) { // setting logrus formatter //logrus.SetFormatter(&logrus.JSONFormatter{}) //logrus.SetOutput(os.Stdout) @@ -419,7 +418,7 @@ func GetGsmPodName(gsmName string) string { return podName } -func GetSidName(variables []databasealphav1.EnvironmentVariable, name string) string { +func GetSidName(variables []databasev4.EnvironmentVariable, name string) string { var result string for _, variable := range variables { @@ -433,7 +432,7 @@ func GetSidName(variables []databasealphav1.EnvironmentVariable, name string) st return result } -func GetPdbName(variables []databasealphav1.EnvironmentVariable, name string) string { +func GetPdbName(variables []databasev4.EnvironmentVariable, name string) string { var result string for _, variable := range variables { @@ -447,19 +446,19 @@ func GetPdbName(variables []databasealphav1.EnvironmentVariable, name string) st return result } -func getlabelsForGsm(instance *databasealphav1.ShardingDatabase) map[string]string { +func getlabelsForGsm(instance *databasev4.ShardingDatabase) map[string]string { return buildLabelsForGsm(instance, "sharding") } -func getlabelsForShard(instance *databasealphav1.ShardingDatabase) map[string]string { +func getlabelsForShard(instance *databasev4.ShardingDatabase) map[string]string { return buildLabelsForShard(instance, "sharding") } -func getlabelsForCatalog(instance *databasealphav1.ShardingDatabase) map[string]string { +func getlabelsForCatalog(instance *databasev4.ShardingDatabase) map[string]string { return buildLabelsForCatalog(instance, "sharding") } -func LabelsForProvShardKind(instance *databasealphav1.ShardingDatabase, sftype string, +func LabelsForProvShardKind(instance *databasev4.ShardingDatabase, sftype string, ) map[string]string { if sftype == "shard" { @@ -470,7 +469,7 @@ func LabelsForProvShardKind(instance *databasealphav1.ShardingDatabase, sftype s } -func CheckSfset(sfsetName string, instance *databasealphav1.ShardingDatabase, kClient client.Client) (*appsv1.StatefulSet, error) { +func CheckSfset(sfsetName string, instance *databasev4.ShardingDatabase, kClient client.Client) (*appsv1.StatefulSet, error) { sfSetFound := &appsv1.StatefulSet{} err := kClient.Get(context.TODO(), types.NamespacedName{ Name: sfsetName, @@ -482,7 +481,7 @@ func CheckSfset(sfsetName string, instance *databasealphav1.ShardingDatabase, kC return sfSetFound, nil } -func checkPvc(pvcName string, instance *databasealphav1.ShardingDatabase, kClient client.Client) (*corev1.PersistentVolumeClaim, error) { +func checkPvc(pvcName string, instance *databasev4.ShardingDatabase, kClient client.Client) (*corev1.PersistentVolumeClaim, error) { pvcFound := &corev1.PersistentVolumeClaim{} err := kClient.Get(context.TODO(), types.NamespacedName{ Name: pvcName, @@ -494,7 +493,7 @@ func checkPvc(pvcName string, instance *databasealphav1.ShardingDatabase, kClien return pvcFound, nil } -func DelPvc(pvcName string, instance *databasealphav1.ShardingDatabase, kClient client.Client, logger logr.Logger) error { +func DelPvc(pvcName string, instance *databasev4.ShardingDatabase, kClient client.Client, logger logr.Logger) error { LogMessages("DEBUG", "Inside the delPvc and received param: "+GetFmtStr(pvcName), nil, instance, logger) pvcFound, err := checkPvc(pvcName, instance, kClient) @@ -510,7 +509,7 @@ func DelPvc(pvcName string, instance *databasealphav1.ShardingDatabase, kClient return nil } -func DelSvc(pvcName string, instance *databasealphav1.ShardingDatabase, kClient client.Client, logger logr.Logger) error { +func DelSvc(pvcName string, instance *databasev4.ShardingDatabase, kClient client.Client, logger logr.Logger) error { LogMessages("DEBUG", "Inside the delPvc and received param: "+GetFmtStr(pvcName), nil, instance, logger) pvcFound, err := checkPvc(pvcName, instance, kClient) @@ -526,7 +525,7 @@ func DelSvc(pvcName string, instance *databasealphav1.ShardingDatabase, kClient return nil } -func CheckSvc(svcName string, instance *databasealphav1.ShardingDatabase, kClient client.Client) (*corev1.Service, error) { +func CheckSvc(svcName string, instance *databasev4.ShardingDatabase, kClient client.Client) (*corev1.Service, error) { svcFound := &corev1.Service{} err := kClient.Get(context.TODO(), types.NamespacedName{ Name: svcName, @@ -538,7 +537,7 @@ func CheckSvc(svcName string, instance *databasealphav1.ShardingDatabase, kClien return svcFound, nil } -func PodListValidation(podList *corev1.PodList, sfName string, instance *databasealphav1.ShardingDatabase, kClient client.Client, +func PodListValidation(podList *corev1.PodList, sfName string, instance *databasev4.ShardingDatabase, kClient client.Client, ) (bool, *corev1.Pod) { var isPodExist bool = false @@ -574,7 +573,7 @@ func PodListValidation(podList *corev1.PodList, sfName string, instance *databas return isPodExist, podInfo } -func GetPodList(sfsetName string, resType string, instance *databasealphav1.ShardingDatabase, kClient client.Client, +func GetPodList(sfsetName string, resType string, instance *databasev4.ShardingDatabase, kClient client.Client, ) (*corev1.PodList, error) { podList := &corev1.PodList{} //labelSelector := labels.SelectorFromSet(getlabelsForGsm(instance)) @@ -604,7 +603,7 @@ func GetPodList(sfsetName string, resType string, instance *databasealphav1.Shar return podList, nil } -func checkPod(instance *databasealphav1.ShardingDatabase, pod *corev1.Pod, kClient client.Client, +func checkPod(instance *databasev4.ShardingDatabase, pod *corev1.Pod, kClient client.Client, ) error { err := kClient.Get(context.TODO(), types.NamespacedName{ Name: pod.Name, @@ -664,7 +663,7 @@ func checkContainerStatus(pod *corev1.Pod, kClient client.Client, // Namespace related function -func AddNamespace(instance *databasealphav1.ShardingDatabase, kClient client.Client, logger logr.Logger, +func AddNamespace(instance *databasev4.ShardingDatabase, kClient client.Client, logger logr.Logger, ) error { var msg string ns := &corev1.Namespace{} @@ -700,7 +699,7 @@ func NewNamespace(name string) *corev1.Namespace { } } -func getOwnerRef(instance *databasealphav1.ShardingDatabase, +func getOwnerRef(instance *databasev4.ShardingDatabase, ) []metav1.OwnerReference { var ownerRef []metav1.OwnerReference @@ -708,8 +707,8 @@ func getOwnerRef(instance *databasealphav1.ShardingDatabase, return ownerRef } -func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { - var variables []databasealphav1.EnvironmentVariable = instance.Spec.Catalog[0].EnvVars +func buildCatalogParams(instance *databasev4.ShardingDatabase) string { + var variables []databasev4.EnvironmentVariable = instance.Spec.Catalog[0].EnvVars var result string var varinfo string var sidFlag bool = false @@ -858,8 +857,8 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { return result } -func buildDirectorParams(instance *databasealphav1.ShardingDatabase, oraGsmSpex databasealphav1.GsmSpec, idx int) string { - var variables []databasealphav1.EnvironmentVariable +func buildDirectorParams(instance *databasev4.ShardingDatabase, oraGsmSpex databasev4.GsmSpec, idx int) string { + var variables []databasev4.EnvironmentVariable var result string var varinfo string var dnameFlag bool = false @@ -905,7 +904,7 @@ func buildDirectorParams(instance *databasealphav1.ShardingDatabase, oraGsmSpex return result } -func BuildShardParams(instance *databasealphav1.ShardingDatabase, sfSet *appsv1.StatefulSet, OraShardSpex databasev1alpha1.ShardSpec) string { +func BuildShardParams(instance *databasev4.ShardingDatabase, sfSet *appsv1.StatefulSet, OraShardSpex databasev4.ShardSpec) string { var variables []corev1.EnvVar = sfSet.Spec.Template.Spec.Containers[0].Env var result string var varinfo string @@ -1014,7 +1013,7 @@ func BuildShardParams(instance *databasealphav1.ShardingDatabase, sfSet *appsv1. return result } -func labelsForShardingDatabaseKind(instance *databasealphav1.ShardingDatabase, sftype string, +func labelsForShardingDatabaseKind(instance *databasev4.ShardingDatabase, sftype string, ) map[string]string { if sftype == "shard" { @@ -1199,7 +1198,7 @@ func GetFmtStr(pstr string, return "[" + pstr + "]" } -func ReadConfigMap(cmName string, instance *databasealphav1.ShardingDatabase, kClient client.Client, logger logr.Logger, +func ReadConfigMap(cmName string, instance *databasev4.ShardingDatabase, kClient client.Client, logger logr.Logger, ) (string, string, string, string, string, string) { var region, fingerprint, user, tenancy, passphrase, str1, topicid, k, value string @@ -1253,7 +1252,7 @@ func ReadConfigMap(cmName string, instance *databasealphav1.ShardingDatabase, kC return region, user, tenancy, passphrase, fingerprint, topicid } -func ReadSecret(secName string, instance *databasealphav1.ShardingDatabase, kClient client.Client, logger logr.Logger, +func ReadSecret(secName string, instance *databasev4.ShardingDatabase, kClient client.Client, logger logr.Logger, ) string { var value string @@ -1285,7 +1284,7 @@ func GetK8sClientConfig(kClient client.Client) (clientcmd.ClientConfig, kubernet var kubeConfig clientcmd.ClientConfig var kubeClient kubernetes.Interface - databasealphav1.KubeConfigOnce.Do(func() { + databasev4.KubeConfigOnce.Do(func() { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() configOverrides := &clientcmd.ConfigOverrides{} kubeConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) @@ -1312,7 +1311,7 @@ func Contains(list []string, s string) bool { } // Function to check shadrd in GSM -func CheckShardInGsm(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func CheckShardInGsm(gsmPodName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { _, _, err := ExecCommand(gsmPodName, getShardCheckCmd(sparams), kubeClient, kubeconfig, instance, logger) @@ -1325,7 +1324,7 @@ func CheckShardInGsm(gsmPodName string, sparams string, instance *databasealphav } // Function to check the online Shard -func CheckOnlineShardInGsm(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func CheckOnlineShardInGsm(gsmPodName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { _, _, err := ExecCommand(gsmPodName, getOnlineShardCmd(sparams), kubeClient, kubeconfig, instance, logger) @@ -1338,7 +1337,7 @@ func CheckOnlineShardInGsm(gsmPodName string, sparams string, instance *database } // Function to move the chunks -func MoveChunks(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func MoveChunks(gsmPodName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { _, _, err := ExecCommand(gsmPodName, getMoveChunksCmd(sparams), kubeClient, kubeconfig, instance, logger) @@ -1351,7 +1350,7 @@ func MoveChunks(gsmPodName string, sparams string, instance *databasealphav1.Sha } // Function to verify the chunks -func VerifyChunks(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func VerifyChunks(gsmPodName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { _, _, err := ExecCommand(gsmPodName, getNoChunksCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { @@ -1363,7 +1362,7 @@ func VerifyChunks(gsmPodName string, sparams string, instance *databasealphav1.S } // Function to verify the chunks -func AddShardInGsm(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func AddShardInGsm(gsmPodName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { _, _, err := ExecCommand(gsmPodName, getShardAddCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { @@ -1375,7 +1374,7 @@ func AddShardInGsm(gsmPodName string, sparams string, instance *databasealphav1. } // Function to deploy the Shards -func DeployShardInGsm(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func DeployShardInGsm(gsmPodName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { _, _, err := ExecCommand(gsmPodName, getdeployShardCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { @@ -1387,7 +1386,7 @@ func DeployShardInGsm(gsmPodName string, sparams string, instance *databasealpha } // Function to verify the chunks -func CancelChunksInGsm(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func CancelChunksInGsm(gsmPodName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { _, _, err := ExecCommand(gsmPodName, getCancelChunksCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { @@ -1399,7 +1398,7 @@ func CancelChunksInGsm(gsmPodName string, sparams string, instance *databasealph } // Function to delete the shard -func RemoveShardFromGsm(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func RemoveShardFromGsm(gsmPodName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { _, _, err := ExecCommand(gsmPodName, getShardDelCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { @@ -1410,7 +1409,7 @@ func RemoveShardFromGsm(gsmPodName string, sparams string, instance *databasealp return nil } -func GetSvcIp(PodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func GetSvcIp(PodName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) (string, string, error) { stdoutput, stderror, err := ExecCommand(PodName, GetIpCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { @@ -1421,7 +1420,7 @@ func GetSvcIp(PodName string, sparams string, instance *databasealphav1.Sharding return strings.Replace(stdoutput, "\r\n", "", -1), strings.Replace(stderror, "/r/n", "", -1), nil } -func GetGsmServices(PodName string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func GetGsmServices(PodName string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) string { stdoutput, _, err := ExecCommand(PodName, getGsmSvcCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { @@ -1432,7 +1431,7 @@ func GetGsmServices(PodName string, instance *databasealphav1.ShardingDatabase, return stdoutput } -func GetDbRole(PodName string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func GetDbRole(PodName string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) string { stdoutput, _, err := ExecCommand(PodName, getDbRoleCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { @@ -1443,7 +1442,7 @@ func GetDbRole(PodName string, instance *databasealphav1.ShardingDatabase, kubeC return strings.TrimSpace(stdoutput) } -func GetDbOpenMode(PodName string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +func GetDbOpenMode(PodName string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) string { stdoutput, _, err := ExecCommand(PodName, getDbModeCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { @@ -1454,7 +1453,7 @@ func GetDbOpenMode(PodName string, instance *databasealphav1.ShardingDatabase, k return strings.TrimSpace(stdoutput) } -func SfsetLabelPatch(sfSetFound *appsv1.StatefulSet, sfSetPod *corev1.Pod, instance *databasealphav1.ShardingDatabase, kClient client.Client, +func SfsetLabelPatch(sfSetFound *appsv1.StatefulSet, sfSetPod *corev1.Pod, instance *databasev4.ShardingDatabase, kClient client.Client, ) error { //var msg string @@ -1462,7 +1461,7 @@ func SfsetLabelPatch(sfSetFound *appsv1.StatefulSet, sfSetPod *corev1.Pod, insta var err error sfsetCopy := sfSetFound.DeepCopy() - sfsetCopy.Labels[string(databasealphav1.ShardingDelLabelKey)] = string(databasealphav1.ShardingDelLabelTrueValue) + sfsetCopy.Labels[string(databasev4.ShardingDelLabelKey)] = string(databasev4.ShardingDelLabelTrueValue) patch := client.MergeFrom(sfSetFound) err = kClient.Patch(context.Background(), sfsetCopy, patch) if err != nil { @@ -1470,7 +1469,7 @@ func SfsetLabelPatch(sfSetFound *appsv1.StatefulSet, sfSetPod *corev1.Pod, insta } podCopy := sfSetPod.DeepCopy() - podCopy.Labels[string(databasealphav1.ShardingDelLabelKey)] = string(databasealphav1.ShardingDelLabelTrueValue) + podCopy.Labels[string(databasev4.ShardingDelLabelKey)] = string(databasev4.ShardingDelLabelTrueValue) podPatch := client.MergeFrom(sfSetPod.DeepCopy()) err = kClient.Patch(context.Background(), podCopy, podPatch) if err != nil { @@ -1480,14 +1479,14 @@ func SfsetLabelPatch(sfSetFound *appsv1.StatefulSet, sfSetPod *corev1.Pod, insta return nil } -func InstanceShardPatch(obj client.Object, instance *databasealphav1.ShardingDatabase, kClient client.Client, id int32, field string, value string, +func InstanceShardPatch(obj client.Object, instance *databasev4.ShardingDatabase, kClient client.Client, id int32, field string, value string, ) error { var err error instSpec := instance.Spec instSpec.Shard[id].IsDelete = "failed" instshardM, _ := json.Marshal(struct { - Spec *databasealphav1.ShardingDatabaseSpec `json:"spec":` + Spec *databasev4.ShardingDatabaseSpec `json:"spec":` }{ Spec: &instSpec, }) @@ -1504,7 +1503,7 @@ func InstanceShardPatch(obj client.Object, instance *databasealphav1.ShardingDat // Send Notification -func SendNotification(title string, body string, instance *databasealphav1.ShardingDatabase, topicId string, rclient ons.NotificationDataPlaneClient, logger logr.Logger, +func SendNotification(title string, body string, instance *databasev4.ShardingDatabase, topicId string, rclient ons.NotificationDataPlaneClient, logger logr.Logger, ) { var msg string req := ons.PublishMessageRequest{TopicId: common.String(topicId), @@ -1525,14 +1524,14 @@ func GetSecretMount() string { return oraSecretMount } -func checkTdeWalletFlag(instance *databasev1alpha1.ShardingDatabase) bool { +func checkTdeWalletFlag(instance *databasev4.ShardingDatabase) bool { if strings.ToLower(instance.Spec.IsTdeWallet) == "enable" { return true } return false } -func CheckIsDeleteFlag(delStr string, instance *databasealphav1.ShardingDatabase, logger logr.Logger) bool { +func CheckIsDeleteFlag(delStr string, instance *databasev4.ShardingDatabase, logger logr.Logger) bool { if strings.ToLower(delStr) == "enable" { return true } @@ -1542,7 +1541,7 @@ func CheckIsDeleteFlag(delStr string, instance *databasealphav1.ShardingDatabase return false } -func getTdeWalletMountLoc(instance *databasev1alpha1.ShardingDatabase) string { +func getTdeWalletMountLoc(instance *databasev4.ShardingDatabase) string { if len(instance.Spec.TdeWalletPvcMountLocation) > 0 { return instance.Spec.TdeWalletPvcMountLocation } diff --git a/commons/sharding/shard.go b/commons/sharding/shard.go index c76fc0e5..49031732 100644 --- a/commons/sharding/shard.go +++ b/commons/sharding/shard.go @@ -43,7 +43,7 @@ import ( "reflect" "strconv" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" @@ -54,7 +54,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func buildLabelsForShard(instance *databasev1alpha1.ShardingDatabase, label string) map[string]string { +func buildLabelsForShard(instance *databasev4.ShardingDatabase, label string) map[string]string { return map[string]string{ "app": "OracleSharding", "type": "Shard", @@ -62,7 +62,7 @@ func buildLabelsForShard(instance *databasev1alpha1.ShardingDatabase, label stri } } -func getLabelForShard(instance *databasev1alpha1.ShardingDatabase) string { +func getLabelForShard(instance *databasev4.ShardingDatabase) string { // if len(OraShardSpex.Label) !=0 { // return OraShardSpex.Label @@ -71,7 +71,7 @@ func getLabelForShard(instance *databasev1alpha1.ShardingDatabase) string { return instance.Name } -func BuildStatefulSetForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) *appsv1.StatefulSet { +func BuildStatefulSetForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) *appsv1.StatefulSet { sfset := &appsv1.StatefulSet{ TypeMeta: buildTypeMetaForShard(), ObjectMeta: builObjectMetaForShard(instance, OraShardSpex), @@ -92,7 +92,7 @@ func buildTypeMetaForShard() metav1.TypeMeta { } // Function to build ObjectMeta -func builObjectMetaForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) metav1.ObjectMeta { +func builObjectMetaForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) metav1.ObjectMeta { // building objectMeta objmeta := metav1.ObjectMeta{ Name: OraShardSpex.Name, @@ -104,7 +104,7 @@ func builObjectMetaForShard(instance *databasev1alpha1.ShardingDatabase, OraShar } // Function to build Stateful Specs -func buildStatefulSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) *appsv1.StatefulSetSpec { +func buildStatefulSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) *appsv1.StatefulSetSpec { // building Stateful set Specs var size int32 = 1 sfsetspec := &appsv1.StatefulSetSpec{ @@ -132,7 +132,7 @@ func buildStatefulSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraS // Function to build PodSpec -func buildPodSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) *corev1.PodSpec { +func buildPodSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) *corev1.PodSpec { user := oraRunAsUser group := oraFsGroup @@ -168,7 +168,7 @@ func buildPodSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardS } // Function to build Volume Spec -func buildVolumeSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) []corev1.Volume { +func buildVolumeSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) []corev1.Volume { var result []corev1.Volume result = []corev1.Volume{ { @@ -208,7 +208,7 @@ func buildVolumeSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraSha } // Function to build the container Specification -func buildContainerSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) []corev1.Container { +func buildContainerSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) []corev1.Container { // building Continer spec var result []corev1.Container containerSpec := corev1.Container{ @@ -287,7 +287,7 @@ func buildContainerSpecForShard(instance *databasev1alpha1.ShardingDatabase, Ora } // Function to build the init Container Spec -func buildInitContainerSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) []corev1.Container { +func buildInitContainerSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) []corev1.Container { var result []corev1.Container privFlag := false var uid int64 = 0 @@ -325,7 +325,7 @@ func buildInitContainerSpecForShard(instance *databasev1alpha1.ShardingDatabase, return result } -func buildVolumeMountSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) []corev1.VolumeMount { +func buildVolumeMountSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) []corev1.VolumeMount { var result []corev1.VolumeMount result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "secretmap-vol3", MountPath: oraSecretMount, ReadOnly: true}) result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "-oradata-vol4", MountPath: oraDataMount}) @@ -351,7 +351,7 @@ func buildVolumeMountSpecForShard(instance *databasev1alpha1.ShardingDatabase, O return result } -func volumeClaimTemplatesForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) []corev1.PersistentVolumeClaim { +func volumeClaimTemplatesForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) []corev1.PersistentVolumeClaim { var claims []corev1.PersistentVolumeClaim @@ -395,7 +395,7 @@ func volumeClaimTemplatesForShard(instance *databasev1alpha1.ShardingDatabase, O return claims } -func BuildServiceDefForShard(instance *databasev1alpha1.ShardingDatabase, replicaCount int32, OraShardSpex databasev1alpha1.ShardSpec, svctype string) *corev1.Service { +func BuildServiceDefForShard(instance *databasev4.ShardingDatabase, replicaCount int32, OraShardSpex databasev4.ShardSpec, svctype string) *corev1.Service { //service := &corev1.Service{} service := &corev1.Service{ ObjectMeta: buildSvcObjectMetaForShard(instance, replicaCount, OraShardSpex, svctype), @@ -419,7 +419,7 @@ func BuildServiceDefForShard(instance *databasev1alpha1.ShardingDatabase, replic } // Function to build Service ObjectMeta -func buildSvcObjectMetaForShard(instance *databasev1alpha1.ShardingDatabase, replicaCount int32, OraShardSpex databasev1alpha1.ShardSpec, svctype string) metav1.ObjectMeta { +func buildSvcObjectMetaForShard(instance *databasev4.ShardingDatabase, replicaCount int32, OraShardSpex databasev4.ShardSpec, svctype string) metav1.ObjectMeta { // building objectMeta var svcName string @@ -441,7 +441,7 @@ func buildSvcObjectMetaForShard(instance *databasev1alpha1.ShardingDatabase, rep return objmeta } -func getSvcLabelsForShard(replicaCount int32, OraShardSpex databasev1alpha1.ShardSpec) map[string]string { +func getSvcLabelsForShard(replicaCount int32, OraShardSpex databasev4.ShardSpec) map[string]string { var labelStr map[string]string = make(map[string]string) if replicaCount == -1 { @@ -455,7 +455,7 @@ func getSvcLabelsForShard(replicaCount int32, OraShardSpex databasev1alpha1.Shar } // ======================== Update Section ======================== -func UpdateProvForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec, kClient client.Client, sfSet *appsv1.StatefulSet, shardPod *corev1.Pod, logger logr.Logger, +func UpdateProvForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec, kClient client.Client, sfSet *appsv1.StatefulSet, shardPod *corev1.Pod, logger logr.Logger, ) (ctrl.Result, error) { var msg string var size int32 = 1 diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml index b57daa1c..b4d696d3 100644 --- a/config/crd/bases/database.oracle.com_DbcsSystem.yaml +++ b/config/crd/bases/database.oracle.com_DbcsSystem.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: DbcsSystem.database.oracle.com spec: group: database.oracle.com @@ -26,6 +26,51 @@ spec: type: object spec: properties: + databaseId: + type: string + dbBackupId: + type: string + dbClone: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + tdeWalletPasswordSecret: + type: string + required: + - dbDbUniqueName + - dbName + - displayName + - hostName + - subnetId + type: object dbSystem: properties: availabilityDomain: @@ -77,12 +122,394 @@ spec: type: string initialDataStorageSizeInGB: type: integer + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + ociConfigMap: + type: string + ociSecret: + type: string + pdbConfigs: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + isDelete: + type: boolean + pdbAdminPassword: + type: string + pdbName: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + tdeWalletPassword: + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean + required: + - ociConfigMap + type: object + status: + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbCloneStatus: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + id: + type: string + licenseModel: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + required: + - dbDbUniqueName + - hostName + type: object + dbEdition: + type: string + dbInfo: + items: + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + kmsDetailsStatus: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyId: + type: string + keyName: + type: string + managementEndpoint: + type: string + vaultId: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + required: + - freeformTags + - pdbName + - shouldPdbAdminAccountBeLocked + type: object + type: array + type: object + type: array + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + databaseId: + type: string + dbBackupId: + type: string + dbClone: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + initialDataStorageSizeInGB: + type: integer kmsKeyId: type: string kmsKeyVersionId: type: string licenseModel: type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + tdeWalletPasswordSecret: + type: string + required: + - dbDbUniqueName + - dbName + - displayName + - hostName + - subnetId + type: object + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string nodeCount: type: integer pdbName: @@ -113,17 +540,58 @@ spec: - dbAdminPaswordSecret - hostName - shape - - sshPublicKeys - subnetId type: object hardLink: type: boolean id: type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object ociConfigMap: type: string ociSecret: type: string + pdbConfigs: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + isDelete: + type: boolean + pdbAdminPassword: + type: string + pdbName: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + tdeWalletPassword: + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean required: - ociConfigMap type: object @@ -137,6 +605,34 @@ spec: type: integer dataStorageSizeInGBs: type: integer + dbCloneStatus: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + id: + type: string + licenseModel: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + required: + - dbDbUniqueName + - hostName + type: object dbEdition: type: string dbInfo: @@ -158,6 +654,25 @@ spec: type: string id: type: string + kmsDetailsStatus: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyId: + type: string + keyName: + type: string + managementEndpoint: + type: string + vaultId: + type: string + vaultName: + type: string + vaultType: + type: string + type: object licenseModel: type: string network: @@ -179,6 +694,32 @@ spec: type: object nodeCount: type: integer + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + required: + - freeformTags + - pdbName + - shouldPdbAdminAccountBeLocked + type: object + type: array + type: object + type: array recoStorageSizeInGB: type: integer shape: @@ -206,6 +747,9 @@ spec: type: string timeStarted: type: string + required: + - operationId + - operationType type: object type: array required: diff --git a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml index 4d88297c..bd768828 100644 --- a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: autonomouscontainerdatabases.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml index 41993eba..a522712a 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: autonomousdatabasebackups.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml index c6973e00..86c55a51 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: autonomousdatabaserestores.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index a9bd37cd..394ddcb3 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: autonomousdatabases.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_cdbs.yaml b/config/crd/bases/database.oracle.com_cdbs.yaml index b47bd7f2..81a33809 100644 --- a/config/crd/bases/database.oracle.com_cdbs.yaml +++ b/config/crd/bases/database.oracle.com_cdbs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: cdbs.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index 2e60c6db..8fa583b5 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: dataguardbrokers.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml index ab912020..534091b4 100644 --- a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: oraclerestdataservices.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index 38a6f877..f7a39cd8 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: pdbs.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index f0fb0e6f..779acea1 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: shardingdatabases.database.oracle.com spec: group: database.oracle.com @@ -347,7 +347,542 @@ spec: format: int32 type: integer protocol: - default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + enum: + - enable + - disable + - failed + - force + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbEdition: + type: string + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + fssStorageClass: + type: string + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: string + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: type: string targetPort: format: int32 diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index 836a55a4..2f17fa23 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: singleinstancedatabases.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index d058f116..c5d36d54 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: databaseobservers.observability.oracle.com spec: group: observability.oracle.com diff --git a/config/crd/patches/cainjection_in_database_dbcssystems.yaml b/config/crd/patches/cainjection_in_database_dbcssystems.yaml new file mode 100644 index 00000000..9d8521ac --- /dev/null +++ b/config/crd/patches/cainjection_in_database_dbcssystems.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: dbcssystems.database.oracle.com diff --git a/config/crd/patches/cainjection_in_database_shardingdatabases.yaml b/config/crd/patches/cainjection_in_database_shardingdatabases.yaml new file mode 100644 index 00000000..9817df6f --- /dev/null +++ b/config/crd/patches/cainjection_in_database_shardingdatabases.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: shardingdatabases.database.oracle.com diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 9b2f8f04..e904f893 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -8,76 +8,16 @@ rules: - "" resources: - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: + - containers - deployments - events - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: + - namespaces - persistentvolumeclaims - pods - pods/exec - pods/log + - replicasets + - secrets - services verbs: - create @@ -119,30 +59,8 @@ rules: resources: - deployments - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - replicasets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: + - services - statefulsets verbs: - create @@ -161,73 +79,17 @@ rules: - get - list - update -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods/exec - verbs: - - create - apiGroups: - database.oracle.com resources: - autonomouscontainerdatabases + - autonomousdatabases + - cdbs + - dataguardbrokers + - oraclerestdataservices + - pdbs + - shardingdatabases + - singleinstancedatabases verbs: - create - delete @@ -240,44 +102,14 @@ rules: - database.oracle.com resources: - autonomouscontainerdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - autonomousdatabasebackups/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - autonomousdatabaserestores/status + - cdbs/status + - dataguardbrokers/status + - oraclerestdataservices/status + - pdbs/status + - shardingdatabases/status + - singleinstancedatabases/status verbs: - get - patch @@ -285,13 +117,13 @@ rules: - apiGroups: - database.oracle.com resources: - - autonomousdatabases + - autonomousdatabasebackups + - autonomousdatabaserestores verbs: - create - delete - get - list - - patch - update - watch - apiGroups: @@ -301,159 +133,19 @@ rules: verbs: - patch - update -- apiGroups: - - database.oracle.com - resources: - - cdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - database.oracle.com resources: - cdbs/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - dataguardbrokers/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - oraclerestdataservices/finalizers + - singleinstancedatabases/finalizers verbs: - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - database.oracle.com resources: - pdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - shardingdatabases/finalizers verbs: - create @@ -461,40 +153,6 @@ rules: - get - patch - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/status - verbs: - - get - - patch - - update - apiGroups: - monitoring.coreos.com resources: diff --git a/config/samples/database_v4_dbcssystem.yaml b/config/samples/database_v4_dbcssystem.yaml new file mode 100644 index 00000000..6c75917c --- /dev/null +++ b/config/samples/database_v4_dbcssystem.yaml @@ -0,0 +1,6 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/database_v4_shardingdatabase.yaml b/config/samples/database_v4_shardingdatabase.yaml new file mode 100644 index 00000000..095206b1 --- /dev/null +++ b/config/samples/database_v4_shardingdatabase.yaml @@ -0,0 +1,6 @@ +apiVersion: database.oracle.com/v4 +kind: ShardingDatabase +metadata: + name: shardingdatabase-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index ac5e158f..5b466084 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -30,4 +30,6 @@ resources: - adb/autonomousdatabase_backup.yaml - adb/autonomousdatabase_restore.yaml - acd/autonomouscontainerdatabase_restart_terminate.yaml +- database_v4_shardingdatabase.yaml +- database_v4_dbcssystem.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index db18b511..9ab6522a 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -130,13 +130,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v1alpha1-shardingdatabase + path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase failurePolicy: Fail - name: mshardingdatabase.kb.io + name: msingleinstancedatabase.kb.io rules: - apiGroups: - database.oracle.com @@ -146,28 +147,47 @@ webhooks: - CREATE - UPDATE resources: - - shardingdatabases + - singleinstancedatabases sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase + path: /mutate-database-oracle-com-v4-dbcssystem failurePolicy: Fail - name: msingleinstancedatabase.kb.io + name: mdbcssystem.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - singleinstancedatabases + - dbcssystems + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: mshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -361,13 +381,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-shardingdatabase + path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase failurePolicy: Fail - name: vshardingdatabase.kb.io + name: vsingleinstancedatabase.kb.io rules: - apiGroups: - database.oracle.com @@ -378,29 +399,28 @@ webhooks: - UPDATE - DELETE resources: - - shardingdatabases + - singleinstancedatabases sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase + path: /validate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: vsingleinstancedatabase.kb.io + name: vshardingdatabase.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE - DELETE resources: - - singleinstancedatabases + - shardingdatabases sideEffects: None - admissionReviewVersions: - v1 diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index 003be62a..3fe3633e 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2022 Oracle and/or its affiliates. +** Copyright (c) 2022-2024 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,26 +40,35 @@ package controllers import ( "context" + "fmt" "reflect" + "strings" + "time" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" dbcsv1 "github.com/oracle/oracle-database-operator/commons/dbcssystem" "github.com/oracle/oracle-database-operator/commons/finalizer" "github.com/oracle/oracle-database-operator/commons/oci" "github.com/go-logr/logr" + "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/core" "github.com/oracle/oci-go-sdk/v65/database" + "github.com/oracle/oci-go-sdk/v65/keymanagement" "github.com/oracle/oci-go-sdk/v65/workrequests" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/retry" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" ) // DbcsSystemReconciler reconciles a DbcsSystem object @@ -74,11 +83,11 @@ type DbcsSystemReconciler struct { Recorder record.EventRecorder } -//+kubebuilder:rbac:groups=database.oracle.com,resources=dbcssystems,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=database.oracle.com,resources=dbcssystems,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=database.oracle.com,resources=dbcssystems/status,verbs=get;update;patch // +kubebuilder:rbac:groups=database.oracle.com,resources=dbcssystems/finalizers,verbs=get;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=configmaps;secrets;namespaces,verbs=get;list;watch;create;update;patch;delete - +// +kubebuilder:object:root=true // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // TODO(user): Modify the Reconcile function to compare the state specified by @@ -91,12 +100,13 @@ type DbcsSystemReconciler struct { func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { r.Logger = log.FromContext(ctx) - // your logic here - - //r.Logger = r.Logv1.WithValues("Instance.Namespace", req.NamespacedName) var err error + resultNq := ctrl.Result{Requeue: false} + resultQ := ctrl.Result{Requeue: true, RequeueAfter: 60 * time.Second} + // Get the dbcs instance from the cluster - dbcsInst := &databasev1alpha1.DbcsSystem{} + dbcsInst := &databasev4.DbcsSystem{} + r.Logger.Info("Reconciling DbSystemDetails", "name", req.NamespacedName) if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, dbcsInst); err != nil { if !errors.IsNotFound(err) { @@ -106,31 +116,38 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Create oci-go-sdk client authData := oci.APIKeyAuth{ - ConfigMapName: &dbcsInst.Spec.OCIConfigMap, - SecretName: &dbcsInst.Spec.OCISecret, + ConfigMapName: dbcsInst.Spec.OCIConfigMap, + SecretName: dbcsInst.Spec.OCISecret, Namespace: dbcsInst.GetNamespace(), } provider, err := oci.GetOCIProvider(r.KubeClient, authData) if err != nil { - return ctrl.Result{}, err + result := resultNq + return result, err } r.dbClient, err = database.NewDatabaseClientWithConfigurationProvider(provider) if err != nil { - return ctrl.Result{}, err + result := resultNq + return result, err } r.nwClient, err = core.NewVirtualNetworkClientWithConfigurationProvider(provider) if err != nil { - return ctrl.Result{}, err + result := resultNq + return result, err } r.wrClient, err = workrequests.NewWorkRequestClientWithConfigurationProvider(provider) + if err != nil { + result := resultNq + return result, err + } r.Logger.Info("OCI provider configured succesfully") /* - Using Finalizer for object deletion + Using Finalizer for object deletion */ if dbcsInst.ObjectMeta.DeletionTimestamp.IsZero() { @@ -148,111 +165,363 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) if err := dbcsv1.DeleteDbcsSystemSystem(r.dbClient, *dbcsInst.Spec.Id); err != nil { r.Logger.Error(err, "Fail to terminate DbcsSystem Instance") // Change the status to Failed - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Terminate, r.nwClient, r.wrClient); statusErr != nil { - return ctrl.Result{}, statusErr + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Terminate, r.nwClient, r.wrClient); statusErr != nil { + result := resultNq + return result, err } // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue - return ctrl.Result{}, nil + result := resultNq + return result, err + } + + // Check if PDBConfig is defined + pdbConfigs := dbcsInst.Spec.PdbConfigs + for _, pdbConfig := range pdbConfigs { + if pdbConfig.PdbName != nil { + // Handle PDB deletion if PluggableDatabaseId is defined and isDelete is true + if pdbConfig.IsDelete != nil && pdbConfig.PluggableDatabaseId != nil && *pdbConfig.IsDelete { + // Call deletePluggableDatabase function + dbSystemId := *dbcsInst.Spec.Id + if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { + result := resultNq + return result, err + } + result := resultNq + return result, err + } + } } + // Remove the finalizer and update the object finalizer.Unregister(r.KubeClient, dbcsInst) r.Logger.Info("Finalizer unregistered successfully.") // Stop reconciliation as the item is being deleted - return ctrl.Result{}, nil + result := resultNq + return result, err } /* - Determine whether it's a provision or bind operation + Determine whether it's a provision or bind operation */ - lastSucSpec, err := dbcsInst.GetLastSuccessfulSpec() + lastSuccessfullSpec, err := dbcsInst.GetLastSuccessfulSpec() + if err != nil { + return ctrl.Result{}, err + } + lastSuccessfullKMSConfig, err := dbcsInst.GetLastSuccessfulKMSConfig() + if err != nil { + return ctrl.Result{}, err + } + lastSuccessfullKMSStatus, err := dbcsInst.GetLastSuccessfulKMSStatus() if err != nil { return ctrl.Result{}, err } - if dbcsInst.Spec.Id == nil && lastSucSpec == nil { - // If no DbcsSystem ID specified, create a DB System - // ======================== Validate Specs ============== - err = dbcsv1.ValidateSpex(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.Recorder) - if err != nil { - return ctrl.Result{}, err - } - r.Logger.Info("DbcsSystem DBSystem provisioning") - dbcsID, err := dbcsv1.CreateAndGetDbcsId(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) - if err != nil { - r.Logger.Error(err, "Fail to provision and get DbcsSystem System ID") + if lastSuccessfullKMSConfig == nil && lastSuccessfullKMSStatus == nil { - // Change the status to Failed - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Failed, r.nwClient, r.wrClient); statusErr != nil { - return ctrl.Result{}, statusErr + if dbcsInst.Spec.KMSConfig.KeyName != "" { + + kmsVaultClient, err := keymanagement.NewKmsVaultClientWithConfigurationProvider(provider) + + if err != nil { + return ctrl.Result{}, err } - // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue - return ctrl.Result{}, nil - } - assignDBCSID(dbcsInst, dbcsID) - if err := dbcsv1.UpdateDbcsSystemId(r.KubeClient, dbcsInst); err != nil { - // Change the status to Failed - assignDBCSID(dbcsInst, dbcsID) - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Failed, r.nwClient, r.wrClient); statusErr != nil { - return ctrl.Result{}, statusErr + // Determine the criteria to identify or locate the vault based on provided information + // Example: Using displayName as a unique identifier (assumed to be unique in this context) + displayName := dbcsInst.Spec.KMSConfig.VaultName + + // Check if a vault with the given displayName exists + getVaultReq := keymanagement.ListVaultsRequest{ + CompartmentId: &dbcsInst.Spec.KMSConfig.CompartmentId, // Assuming compartment ID is known or provided } - return ctrl.Result{}, err - } - r.Logger.Info("DbcsSystem system provisioned succesfully") - assignDBCSID(dbcsInst, dbcsID) - if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { - return ctrl.Result{}, err + listResp, err := kmsVaultClient.ListVaults(ctx, getVaultReq) + if err != nil { + return ctrl.Result{}, fmt.Errorf("error listing vaults: %v", err) + } + + var existingVaultId *string + var existingVaultManagementEndpoint *string + var kmsClient keymanagement.KmsManagementClient + // Find the first active vault with matching displayName + for _, vault := range listResp.Items { + if vault.LifecycleState == keymanagement.VaultSummaryLifecycleStateActive && *vault.DisplayName == displayName { + existingVaultId = vault.Id + existingVaultManagementEndpoint = vault.ManagementEndpoint + // Create KMS Management client + kmsClient, err = keymanagement.NewKmsManagementClientWithConfigurationProvider(provider, *existingVaultManagementEndpoint) + if err != nil { + return ctrl.Result{}, err + } + break + } + } + + // If no active vault found, create a new one + if existingVaultId == nil { + + // Create the KMS vault + createResp, err := r.createKMSVault(ctx, &dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) + if err != nil { + return ctrl.Result{}, fmt.Errorf("error creating vault: %v", err) + } + existingVaultId = createResp.Id + r.Logger.Info("Created vault Id", existingVaultId) + } else { + // Optionally, perform additional checks or operations if needed + r.Logger.Info("Found existing active vault with displayName to migrate existing database", "DisplayName", displayName, "VaultId", *existingVaultId) + dbcsInst.Status.KMSDetailsStatus.VaultId = *existingVaultId + dbcsInst.Status.KMSDetailsStatus.ManagementEndpoint = *existingVaultManagementEndpoint + } + if existingVaultId != nil { + + // Find the key ID based on compartmentID in the existing vault + + listKeysReq := keymanagement.ListKeysRequest{ + CompartmentId: &dbcsInst.Spec.KMSConfig.CompartmentId, + } + + var keyId *string + var keyName *string + + // Make a single request to list keys + listKeysResp, err := kmsClient.ListKeys(ctx, listKeysReq) + if err != nil { + r.Logger.Error(err, "Error listing keys in existing vault") + return ctrl.Result{}, err + } + + // Iterate over the keys to find the desired key + for _, key := range listKeysResp.Items { + if key.DisplayName != nil && *key.DisplayName == dbcsInst.Spec.KMSConfig.KeyName { + keyId = key.Id + keyName = key.DisplayName + dbcsInst.Status.KMSDetailsStatus.KeyId = *key.Id + dbcsInst.Status.KMSDetailsStatus.KeyName = *key.DisplayName + break + } + } + + if keyId == nil { + r.Logger.Info("Master key not found in existing vault, creating new key") + + // Create the KMS key in the existing vault + keyResponse, err := r.createKMSKey(ctx, &dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) + if err != nil { + return ctrl.Result{}, err + } + + // Update the DbSystem with the encryption key ID + dbcsInst.Status.KMSDetailsStatus.KeyId = *keyResponse.Key.Id + dbcsInst.Status.KMSDetailsStatus.KeyName = *keyResponse.Key.DisplayName + } else { + r.Logger.Info("Found existing master key in vault", "KeyName", dbcsInst.Spec.KMSConfig.KeyName, "KeyId", *keyId) + + // Update the DbSystem with the existing encryption key ID + dbcsInst.Status.KMSDetailsStatus.KeyId = *keyId + dbcsInst.Status.KMSDetailsStatus.KeyName = *keyName + } + } else { + r.Logger.Info("Creating new vault") + + // Create the new vault + vaultResponse, err := r.createKMSVault(ctx, &dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) + if err != nil { + return ctrl.Result{}, err + } + dbcsInst.Status.KMSDetailsStatus.VaultId = *vaultResponse.Id + dbcsInst.Status.KMSDetailsStatus.ManagementEndpoint = *vaultResponse.ManagementEndpoint + // Create the KMS key in the newly created vault + keyResponse, err := r.createKMSKey(ctx, &dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) + if err != nil { + return ctrl.Result{}, err + } + + // Update the DbSystem with the encryption key ID + dbcsInst.Status.KMSDetailsStatus.KeyId = *keyResponse.Key.Id + dbcsInst.Status.KMSDetailsStatus.KeyName = *keyResponse.Key.DisplayName + + } } - assignDBCSID(dbcsInst, dbcsID) + } + //debugging + // lastSuccessfullSpec = nil + // r.ensureDBSystemSpec(&dbcsInst.Spec.DbSystem) + // Check if cloning is needed, debugging + // *dbcsInst.Status.DbCloneStatus.Id = "" + setupCloning := false + if dbcsInst.Spec.SetupDBCloning && (dbcsInst.Spec.Id != nil || dbcsInst.Spec.DbBackupId != nil || dbcsInst.Spec.DatabaseId != nil) { + setupCloning = true } else { - if lastSucSpec == nil { - if err := dbcsv1.GetDbSystemId(r.Logger, r.dbClient, dbcsInst); err != nil { - // Change the status to Failed - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Failed, r.nwClient, r.wrClient); statusErr != nil { + r.Logger.Error(err, "SetupDBCloning is defined but other necessary details are not present. Refer README.md file for instructions.") + return ctrl.Result{}, nil + } + var dbSystemId string + // Executing DB Cloning Process, if defined. Do not repeat cloning again when Status has Id present. + if setupCloning && dbcsInst.Status.DbCloneStatus.Id == nil { + switch { + + case dbcsInst.Spec.SetupDBCloning && dbcsInst.Spec.DbBackupId != nil: + dbSystemId, err = dbcsv1.CloneFromBackupAndGetDbcsId(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) + if err != nil { + r.Logger.Error(err, "Fail to clone db system from backup and get DbcsSystem System ID") + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } - return ctrl.Result{}, err + + return ctrl.Result{}, nil } - if err := dbcsv1.SetDBCSDatabaseLifecycleState(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient); err != nil { - // Change the status to required state + r.Logger.Info("DB Cloning completed successfully from provided backup DB system") + + case dbcsInst.Spec.SetupDBCloning && dbcsInst.Spec.DatabaseId != nil: + dbSystemId, err = dbcsv1.CloneFromDatabaseAndGetDbcsId(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) + if err != nil { + r.Logger.Error(err, "Fail to clone db system from DatabaseID provided") + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + return ctrl.Result{}, statusErr + } + + return ctrl.Result{}, nil + } + r.Logger.Info("DB Cloning completed successfully from provided databaseId") + + case dbcsInst.Spec.SetupDBCloning && dbcsInst.Spec.DbBackupId == nil && dbcsInst.Spec.DatabaseId == nil: + dbSystemId, err = dbcsv1.CloneAndGetDbcsId(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) + if err != nil { + r.Logger.Error(err, "Fail to clone db system and get DbcsSystem System ID") + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + return ctrl.Result{}, statusErr + } + return ctrl.Result{}, nil + } + r.Logger.Info("DB Cloning completed successfully from provided db system") + } + } else if !setupCloning { + if dbcsInst.Spec.Id == nil && lastSuccessfullSpec == nil { + // If no DbcsSystem ID specified, create a DB System + // ======================== Validate Specs ============== + err = dbcsv1.ValidateSpex(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.Recorder) + if err != nil { return ctrl.Result{}, err } + r.Logger.Info("DbcsSystem DBSystem provisioning") + dbcsID, err := dbcsv1.CreateAndGetDbcsId(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient, &dbcsInst.Status.KMSDetailsStatus) + if err != nil { + r.Logger.Error(err, "Fail to provision and get DbcsSystem System ID") + + // Change the status to Failed + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + return ctrl.Result{}, statusErr + } + // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue + return ctrl.Result{}, nil + } - dbcsInstId := *dbcsInst.Spec.Id + assignDBCSID(dbcsInst, dbcsID) + // Check if KMSConfig is specified + kmsConfig := dbcsInst.Spec.KMSConfig + if kmsConfig != (databasev4.KMSConfig{}) { + // Check if KMSDetailsStatus is uninitialized (zero value) + if dbcsInst.Spec.DbSystem.KMSConfig != dbcsInst.Spec.KMSConfig { + dbcsInst.Spec.DbSystem.KMSConfig = dbcsInst.Spec.KMSConfig + } + } if err := dbcsv1.UpdateDbcsSystemId(r.KubeClient, dbcsInst); err != nil { // Change the status to Failed - assignDBCSID(dbcsInst, dbcsInstId) - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Failed, r.nwClient, r.wrClient); statusErr != nil { + assignDBCSID(dbcsInst, dbcsID) + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } return ctrl.Result{}, err } - r.Logger.Info("Sync information from remote DbcsSystem System successfully") - - dbcsInstId = *dbcsInst.Spec.Id + r.Logger.Info("DbcsSystem system provisioned succesfully") + assignDBCSID(dbcsInst, dbcsID) if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { return ctrl.Result{}, err } - assignDBCSID(dbcsInst, dbcsInstId) + assignDBCSID(dbcsInst, dbcsID) } else { - if dbcsInst.Spec.Id == nil { - dbcsInst.Spec.Id = lastSucSpec.Id - } + if lastSuccessfullSpec == nil { + if err := dbcsv1.GetDbSystemId(r.Logger, r.dbClient, dbcsInst); err != nil { + // Change the status to Failed + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + return ctrl.Result{}, statusErr + } + return ctrl.Result{}, err + } + if err := dbcsv1.SetDBCSDatabaseLifecycleState(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient); err != nil { + // Change the status to required state + return ctrl.Result{}, err + } - if err := dbcsv1.UpdateDbcsSystemIdInst(r.Logger, r.dbClient, dbcsInst, r.KubeClient, r.nwClient, r.wrClient); err != nil { - r.Logger.Error(err, "Fail to update DbcsSystem Id") + dbSystemId := *dbcsInst.Spec.Id + if err := dbcsv1.UpdateDbcsSystemId(r.KubeClient, dbcsInst); err != nil { + // Change the status to Failed + assignDBCSID(dbcsInst, dbSystemId) + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + return ctrl.Result{}, statusErr + } + return ctrl.Result{}, err + } - // Change the status to Failed - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Failed, r.nwClient, r.wrClient); statusErr != nil { - return ctrl.Result{}, statusErr + r.Logger.Info("Sync information from remote DbcsSystem System successfully") + + dbSystemId = *dbcsInst.Spec.Id + if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { + return ctrl.Result{}, err + } + assignDBCSID(dbcsInst, dbSystemId) + } else { + dbSystemId := "" + if dbcsInst.Spec.Id == nil { + dbcsInst.Spec.Id = lastSuccessfullSpec.Id + dbSystemId = *dbcsInst.Spec.Id + } else { + dbSystemId = *dbcsInst.Spec.Id + } + //debugging + // *dbcsInst.Spec.Id = "ocid1.dbsystem.oc1.iad.anuwcljsabf7htya55wz5vfil7ul3pkzpubnymp6zrp3fhgomv3fcdr2vtiq" + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + return ctrl.Result{}, err + } + dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get DB Home ID: %v\n", err) + return ctrl.Result{}, err + } + + databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, *dbcsInst.Spec.Id, compartmentId, dbHomeId) + if err != nil { + fmt.Printf("Failed to get database IDs: %v\n", err) + return ctrl.Result{}, err + } + err = r.getPluggableDatabaseDetails(ctx, dbcsInst, *dbcsInst.Spec.Id, databaseIds) + if err != nil { + fmt.Printf("Failed to get pluggable database details: %v\n", err) + return ctrl.Result{}, err + } + + if err := dbcsv1.UpdateDbcsSystemIdInst(r.Logger, r.dbClient, dbcsInst, r.KubeClient, r.nwClient, r.wrClient, databaseIds[0]); err != nil { + r.Logger.Error(err, "Fail to update DbcsSystem Id") + + // Change the status to Failed + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + return ctrl.Result{}, statusErr + } + // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue + return ctrl.Result{}, nil + } + if err := dbcsv1.SetDBCSDatabaseLifecycleState(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient); err != nil { + // Change the status to required state + return ctrl.Result{}, err + } + // Update Spec and Status + result, err := r.updateSpecsAndStatus(ctx, dbcsInst, dbSystemId) + if err != nil { + return result, err } - // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue - return ctrl.Result{}, nil - } - if err := dbcsv1.SetDBCSDatabaseLifecycleState(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient); err != nil { - // Change the status to required state - return ctrl.Result{}, err } } } @@ -261,21 +530,914 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) //r.updateWalletSecret(dbcs) // Update the last succesful spec - dbcsInstId := *dbcsInst.Spec.Id - if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { - return ctrl.Result{}, err + if dbcsInst.Spec.Id != nil { + dbSystemId = *dbcsInst.Spec.Id + + if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { + return ctrl.Result{}, err + } + } else if dbcsInst.Status.DbCloneStatus.Id != nil { + dbSystemId = *dbcsInst.Status.DbCloneStatus.Id } //assignDBCSID(dbcsInst,dbcsI) // Change the phase to "Available" - assignDBCSID(dbcsInst, dbcsInstId) - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Available, r.nwClient, r.wrClient); statusErr != nil { + assignDBCSID(dbcsInst, dbSystemId) + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Available, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } - return ctrl.Result{}, nil + r.Logger.Info("DBInst after assignment", "dbcsInst:->", dbcsInst) + + // Check if specified PDB exists or needs to be created + exists, err := r.validatePDBExistence(dbcsInst) + if err != nil { + fmt.Printf("Failed to get PDB Details: %v\n", err) + return ctrl.Result{}, err + } + if dbcsInst.Spec.PdbConfigs != nil { + if !exists { + for _, pdbConfig := range dbcsInst.Spec.PdbConfigs { + if pdbConfig.PdbName != nil { + // Get database details + // Get DB Home ID by DB System ID + // Get Compartment ID by DB System ID + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + return ctrl.Result{}, err + } + dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, dbSystemId) + if err != nil { + fmt.Printf("Failed to get DB Home ID: %v\n", err) + return ctrl.Result{}, err + } + databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, dbSystemId, compartmentId, dbHomeId) + if err != nil { + fmt.Printf("Failed to get database IDs: %v\n", err) + return ctrl.Result{}, err + } + + // Now you can use dbDetails to access database attributes + r.Logger.Info("Database details fetched successfully", "DatabaseId", databaseIds) + + // Check if deletion is requested + if pdbConfig.IsDelete != nil && *pdbConfig.IsDelete { + // Call deletePluggableDatabase function + if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { + return ctrl.Result{}, err + } + // Continue to the next pdbConfig + continue + } else { + // Call the method to create the pluggable database + r.Logger.Info("Calling createPluggableDatabase", "ctx:->", ctx, "dbcsInst:->", dbcsInst, "databaseIds:->", databaseIds[0], "compartmentId:->", compartmentId) + pdbId, err := r.createPluggableDatabase(ctx, dbcsInst, pdbConfig, databaseIds[0], compartmentId, dbSystemId) + if err != nil { + // Handle error if required + return ctrl.Result{}, err + } + + // Create or update the PDBConfigStatus in DbcsSystemStatus + pdbConfigStatus := databasev4.PDBConfigStatus{ + PdbName: pdbConfig.PdbName, + ShouldPdbAdminAccountBeLocked: pdbConfig.ShouldPdbAdminAccountBeLocked, + PdbLifecycleState: databasev4.Available, + FreeformTags: pdbConfig.FreeformTags, + PluggableDatabaseId: &pdbId, + } + + // Create a map to track existing PDBConfigStatus by PdbName + pdbDetailsMap := make(map[string]databasev4.PDBConfigStatus) + + // Populate the map with existing PDBConfigStatus from dbcsInst.Status.PdbDetailsStatus + for _, pdbDetails := range dbcsInst.Status.PdbDetailsStatus { + for _, existingPdbConfig := range pdbDetails.PDBConfigStatus { + pdbDetailsMap[*existingPdbConfig.PdbName] = existingPdbConfig + } + } + + // Update the map with the new or updated PDBConfigStatus + pdbDetailsMap[*pdbConfig.PdbName] = pdbConfigStatus + + // Convert the map back to a slice of PDBDetailsStatus + var updatedPdbDetailsStatus []databasev4.PDBDetailsStatus + for _, pdbConfigStatus := range pdbDetailsMap { + updatedPdbDetailsStatus = append(updatedPdbDetailsStatus, databasev4.PDBDetailsStatus{ + PDBConfigStatus: []databasev4.PDBConfigStatus{pdbConfigStatus}, + }) + } + + // Assign the updated slice to dbcsInst.Status.PdbDetailsStatus + dbcsInst.Status.PdbDetailsStatus = updatedPdbDetailsStatus + // Update the status in Kubernetes + // Update the status subresource + err = r.KubeClient.Status().Update(ctx, dbcsInst) + if err != nil { + r.Logger.Error(err, "Failed to update DB status") + return reconcile.Result{}, err + } + + } + } + } + } else { + r.Logger.Info("No change in PDB configurations or, already existed PDB Status.") + } + } + // } else { + // r.Logger.Info("No PDB configurations given.") + // } + // r.Logger.Info("DBInst after assignment", "dbcsInst:->", dbcsInst) + // // Check if PDBConfig is defined and needs to be created or deleted + pdbConfigs := dbcsInst.Spec.PdbConfigs + if pdbConfigs != nil { + for _, pdbConfig := range pdbConfigs { + if pdbConfig.PdbName != nil { + // Get database details + // Get DB Home ID by DB System ID + // Get Compartment ID by DB System ID + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + return ctrl.Result{}, err + } + dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, dbSystemId) + if err != nil { + fmt.Printf("Failed to get DB Home ID: %v\n", err) + return ctrl.Result{}, err + } + databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, dbSystemId, compartmentId, dbHomeId) + if err != nil { + fmt.Printf("Failed to get database IDs: %v\n", err) + return ctrl.Result{}, err + } + + // Now you can use dbDetails to access database attributes + r.Logger.Info("Database details fetched successfully", "DatabaseId", databaseIds) + + // Check if deletion is requested + if pdbConfig.IsDelete != nil && *pdbConfig.IsDelete { + // Call deletePluggableDatabase function + if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { + return ctrl.Result{}, err + } + // Continue to the next pdbConfig + continue + } else { + // Call the method to create the pluggable database + r.Logger.Info("Calling createPluggableDatabase", "ctx:->", ctx, "dbcsInst:->", dbcsInst, "databaseIds:->", databaseIds[0], "compartmentId:->", compartmentId) + _, err := r.createPluggableDatabase(ctx, dbcsInst, pdbConfig, databaseIds[0], compartmentId, dbSystemId) + if err != nil { + // Handle error if required + return ctrl.Result{}, err + } + } + } + } + } + + return resultQ, nil + +} +func (r *DbcsSystemReconciler) updateSpecsAndStatus(ctx context.Context, dbcsInst *databasev4.DbcsSystem, dbSystemId string) (reconcile.Result, error) { + + // Retry mechanism for handling resource version conflicts + retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { + // Fetch the latest version of the resource + latestDbcsInst := &databasev4.DbcsSystem{} + err := r.KubeClient.Get(ctx, types.NamespacedName{ + Name: dbcsInst.Name, + Namespace: dbcsInst.Namespace, + }, latestDbcsInst) + if err != nil { + r.Logger.Error(err, "Failed to fetch the latest DB resource") + return err + } + + // Update the Spec subresource + latestDbcsInst.Spec.Id = &dbSystemId + err = r.KubeClient.Update(ctx, latestDbcsInst) + if err != nil { + r.Logger.Error(err, "Failed to update DB Spec") + return err + } + + // Update the Status subresource + + // Update the Status subresource + originalStatus := reflect.ValueOf(&dbcsInst.Status).Elem() + latestStatus := reflect.ValueOf(&latestDbcsInst.Status).Elem() + + // Iterate over all fields in the Status struct and update them + for i := 0; i < originalStatus.NumField(); i++ { + fieldName := originalStatus.Type().Field(i).Name + latestStatus.FieldByName(fieldName).Set(originalStatus.Field(i)) + } + + err = r.KubeClient.Status().Update(ctx, latestDbcsInst) + if err != nil { + r.Logger.Error(err, "Failed to update DB status") + return err + } + + return nil + }) + + if retryErr != nil { + r.Logger.Error(retryErr, "Failed to update DB Spec and Status after retries") + return reconcile.Result{}, retryErr + } + + r.Logger.Info("Successfully updated Spec and Status") + return reconcile.Result{}, nil +} + +// getDbHomeIdByDbSystemID retrieves the DB Home ID associated with the given DB System ID +func (r *DbcsSystemReconciler) getDbHomeIdByDbSystemID(ctx context.Context, compartmentId, dbSystemId string) (string, error) { + listRequest := database.ListDbHomesRequest{ + CompartmentId: &compartmentId, + DbSystemId: &dbSystemId, + } + + listResponse, err := r.dbClient.ListDbHomes(ctx, listRequest) + if err != nil { + return "", fmt.Errorf("failed to list DB homes: %v", err) + } + + if len(listResponse.Items) == 0 { + return "", fmt.Errorf("no DB homes found for DB system ID: %s", dbSystemId) + } + + return *listResponse.Items[0].Id, nil +} +func (r *DbcsSystemReconciler) getCompartmentIDByDbSystemID(ctx context.Context, dbSystemId string) (string, error) { + // Construct the GetDbSystem request + getRequest := database.GetDbSystemRequest{ + DbSystemId: &dbSystemId, + } + + // Call GetDbSystem API using the existing dbClient + getResponse, err := r.dbClient.GetDbSystem(ctx, getRequest) + if err != nil { + return "", fmt.Errorf("failed to get DB system details: %v", err) + } + + // Extract the compartment ID from the DB system details + compartmentId := *getResponse.DbSystem.CompartmentId + + return compartmentId, nil +} +func (r *DbcsSystemReconciler) getDatabaseIDByDbSystemID(ctx context.Context, dbSystemId, compartmentId, dbHomeId string) ([]string, error) { + // Construct the ListDatabases request + request := database.ListDatabasesRequest{ + SystemId: &dbSystemId, + CompartmentId: &compartmentId, + DbHomeId: &dbHomeId, + } + + // Call ListDatabases API using the existing dbClient + response, err := r.dbClient.ListDatabases(ctx, request) + if err != nil { + return nil, fmt.Errorf("failed to list databases: %v", err) + } + + // Extract database IDs from the response + var databaseIds []string + for _, dbSummary := range response.Items { + databaseIds = append(databaseIds, *dbSummary.Id) + } + + return databaseIds, nil +} +func (r *DbcsSystemReconciler) validatePDBExistence(dbcs *databasev4.DbcsSystem) (bool, error) { + r.Logger.Info("Validating PDB existence for all provided PDBs") + + // Iterate over each PDBConfig in Spec.PdbConfigs + for _, pdbConfig := range dbcs.Spec.PdbConfigs { + pdbName := pdbConfig.PdbName + r.Logger.Info("Checking PDB existence in Status", "PDBName", *pdbName) + + found := false + + // Check if the PDB exists in Status.PdbDetailsStatus with a state of "Available" + for _, pdbDetailsStatus := range dbcs.Status.PdbDetailsStatus { + for _, pdbStatus := range pdbDetailsStatus.PDBConfigStatus { + if pdbStatus.PdbName != nil && *pdbStatus.PdbName == *pdbName && pdbStatus.PdbLifecycleState == "AVAILABLE" { + found = true + break + } + } + if found { + break + } + } + + if !found { + r.Logger.Info("Pluggable database does not exist or is not available in Status.PdbDetailsStatus", "PDBName", *pdbName) + return false, nil + } + } + + // If all PDBs are found and available + r.Logger.Info("All specified PDBs are available") + return true, nil +} +func (r *DbcsSystemReconciler) createPluggableDatabase(ctx context.Context, dbcs *databasev4.DbcsSystem, pdbConfig databasev4.PDBConfig, databaseId, compartmentId, dbSystemId string) (string, error) { + r.Logger.Info("Checking if the pluggable database exists", "PDBName", pdbConfig.PdbName) + + // Check if the pluggable database already exists + exists, pdbId, err := r.doesPluggableDatabaseExist(ctx, compartmentId, pdbConfig.PdbName, databaseId) + if err != nil { + r.Logger.Error(err, "Failed to check if pluggable database exists", "PDBName", pdbConfig.PdbName) + return "", err + } + if exists { + // Set the PluggableDatabaseId in PDBConfig + pdbConfig.PluggableDatabaseId = pdbId + r.Logger.Info("Pluggable database already exists", "PDBName", pdbConfig.PdbName, "PluggableDatabaseId", *pdbConfig.PluggableDatabaseId) + return *pdbId, nil + } + + // Define the DatabaseExists method locally + databaseExists := func(dbSystemID string) (bool, error) { + req := database.GetDbSystemRequest{ + DbSystemId: &dbSystemID, + } + _, err := r.dbClient.GetDbSystem(ctx, req) + if err != nil { + if ociErr, ok := err.(common.ServiceError); ok && ociErr.GetHTTPStatusCode() == 404 { + return false, nil + } + return false, err + } + return true, nil + } + + exists, err = databaseExists(dbSystemId) + if err != nil { + r.Logger.Error(err, "Failed to check database existence") + return "", err + } + + if !exists { + errMsg := fmt.Sprintf("Database does not exist: %s", dbSystemId) + r.Logger.Error(fmt.Errorf(errMsg), "Database not found") + return "", fmt.Errorf(errMsg) + } + + // Fetch secrets for TdeWalletPassword and PdbAdminPassword + tdeWalletPassword, err := r.getSecret(ctx, dbcs.Namespace, *pdbConfig.TdeWalletPassword) + // Trim newline character from the password + tdeWalletPassword = strings.TrimSpace(tdeWalletPassword) + r.Logger.Info("TDE wallet password retrieved successfully") + if err != nil { + r.Logger.Error(err, "Failed to get TDE wallet password secret") + return "", err + } + + pdbAdminPassword, err := r.getSecret(ctx, dbcs.Namespace, *pdbConfig.PdbAdminPassword) + // Trim newline character from the password + pdbAdminPassword = strings.TrimSpace(pdbAdminPassword) + r.Logger.Info("PDB admin password retrieved successfully") + if err != nil { + r.Logger.Error(err, "Failed to get PDB admin password secret") + return "", err + } + // Change the status to Provisioning + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcs, databasev4.Provision, r.nwClient, r.wrClient); statusErr != nil { + r.Logger.Error(err, "Failed to set DBCS LifeCycle State to Provisioning") + return "", statusErr + } + r.Logger.Info("Updated DBCS LifeCycle State to Provisioning") + // Proceed with creating the pluggable database + r.Logger.Info("Creating pluggable database", "PDBName", pdbConfig.PdbName) + createPdbReq := database.CreatePluggableDatabaseRequest{ + CreatePluggableDatabaseDetails: database.CreatePluggableDatabaseDetails{ + PdbName: pdbConfig.PdbName, + ContainerDatabaseId: &databaseId, + ShouldPdbAdminAccountBeLocked: pdbConfig.ShouldPdbAdminAccountBeLocked, + PdbAdminPassword: common.String(pdbAdminPassword), + TdeWalletPassword: common.String(tdeWalletPassword), + FreeformTags: pdbConfig.FreeformTags, + }, + } + response, err := r.dbClient.CreatePluggableDatabase(ctx, createPdbReq) + if err != nil { + r.Logger.Error(err, "Failed to create pluggable database", "PDBName", pdbConfig.PdbName) + return "", err + } + // Set the PluggableDatabaseId in PDBConfig + pdbConfig.PluggableDatabaseId = response.PluggableDatabase.Id + + r.Logger.Info("Pluggable database creation initiated", "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) + + // Polling mechanism to check PDB status + const maxRetries = 120 // total 1 hour wait for creation of PDB + const retryInterval = 30 // in seconds + + for i := 0; i < maxRetries; i++ { + getPdbReq := database.GetPluggableDatabaseRequest{ + PluggableDatabaseId: pdbConfig.PluggableDatabaseId, + } + + getPdbResp, err := r.dbClient.GetPluggableDatabase(ctx, getPdbReq) + if err != nil { + r.Logger.Error(err, "Failed to get pluggable database status", "PDBID", *pdbConfig.PluggableDatabaseId) + return "", err + } + + pdbStatus := getPdbResp.PluggableDatabase.LifecycleState + r.Logger.Info("Checking pluggable database status", "PDBID", *pdbConfig.PluggableDatabaseId, "Status", pdbStatus) + + if pdbStatus == database.PluggableDatabaseLifecycleStateAvailable { + r.Logger.Info("Pluggable database successfully created", "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) + // Change the status to Available + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcs, databasev4.Available, r.nwClient, r.wrClient); statusErr != nil { + return "", statusErr + } + return *response.PluggableDatabase.Id, nil + } + + if pdbStatus == database.PluggableDatabaseLifecycleStateFailed { + r.Logger.Error(fmt.Errorf("pluggable database creation failed"), "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) + // Change the status to Failed + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcs, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + return "", statusErr + } + return "", fmt.Errorf("pluggable database creation failed") + } + + time.Sleep(retryInterval * time.Second) + } + + r.Logger.Error(fmt.Errorf("timed out waiting for pluggable database to become available"), "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) + return "", fmt.Errorf("timed out waiting for pluggable database to become available") +} + +func (r *DbcsSystemReconciler) pluggableDatabaseExists(ctx context.Context, pluggableDatabaseId string) (bool, error) { + req := database.GetPluggableDatabaseRequest{ + PluggableDatabaseId: &pluggableDatabaseId, + } + _, err := r.dbClient.GetPluggableDatabase(ctx, req) + if err != nil { + if ociErr, ok := err.(common.ServiceError); ok && ociErr.GetHTTPStatusCode() == 404 { + // PDB does not exist + return false, nil + } + // Other error occurred + return false, err + } + // PDB exists + return true, nil +} + +func (r *DbcsSystemReconciler) deletePluggableDatabase(ctx context.Context, pdbConfig databasev4.PDBConfig, dbSystemId string) error { + if pdbConfig.PdbName == nil { + return fmt.Errorf("PDB name is not specified") + } + + r.Logger.Info("Deleting pluggable database", "PDBName", *pdbConfig.PdbName) + + if pdbConfig.PluggableDatabaseId == nil { + r.Logger.Info("PluggableDatabaseId is not specified, getting pluggable databaseID") + // Call a function to retrieve PluggableDatabaseId + pdbID, err := r.getPluggableDatabaseID(ctx, pdbConfig, dbSystemId) + if err != nil { + return fmt.Errorf("failed to get PluggableDatabaseId: %v", err) + } + pdbConfig.PluggableDatabaseId = &pdbID + } + + // Now pdbConfig.PluggableDatabaseId should not be nil + if pdbConfig.PluggableDatabaseId == nil { + return fmt.Errorf("PluggableDatabaseId is still nil after retrieval attempt. Nothing to delete") + } + + // Check if PluggableDatabaseId exists in the live system + exists, err := r.pluggableDatabaseExists(ctx, *pdbConfig.PluggableDatabaseId) + if err != nil { + r.Logger.Error(err, "Failed to check if pluggable database exists", "PluggableDatabaseId", *pdbConfig.PluggableDatabaseId) + return err + } + if !exists { + r.Logger.Info("PluggableDatabaseId does not exist in the live system, nothing to delete", "PluggableDatabaseId", *pdbConfig.PluggableDatabaseId) + return nil + } + + // Define the delete request + deleteReq := database.DeletePluggableDatabaseRequest{ + PluggableDatabaseId: pdbConfig.PluggableDatabaseId, + } + + // Call OCI SDK to delete the PDB + _, err = r.dbClient.DeletePluggableDatabase(ctx, deleteReq) + if err != nil { + r.Logger.Error(err, "Failed to delete pluggable database", "PDBName", *pdbConfig.PdbName) + return err + } + + r.Logger.Info("Successfully deleted pluggable database", "PDBName", *pdbConfig.PdbName) + return nil +} + +func (r *DbcsSystemReconciler) getPluggableDatabaseID(ctx context.Context, pdbConfig databasev4.PDBConfig, dbSystemId string) (string, error) { + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + return "", err + } + request := database.ListPluggableDatabasesRequest{ + CompartmentId: &compartmentId, + } + + response, err := r.dbClient.ListPluggableDatabases(ctx, request) + if err != nil { + return "", fmt.Errorf("failed to list Pluggable Databases: %v", err) + } + + var pdbID string + + for _, pdb := range response.Items { + if *pdb.PdbName == *pdbConfig.PdbName { + pdbID = *pdb.Id + break + } + } + + if pdbID == "" { + return "", fmt.Errorf("pluggable database '%s' not found", *pdbConfig.PdbName) + } + return pdbID, nil +} + +func (r *DbcsSystemReconciler) getPluggableDatabaseDetails(ctx context.Context, dbcsInst *databasev4.DbcsSystem, dbSystemId string, databaseIds []string) error { + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + return err + } + request := database.ListPluggableDatabasesRequest{ + CompartmentId: &compartmentId, + } + + response, err := r.dbClient.ListPluggableDatabases(ctx, request) + if err != nil { + return fmt.Errorf("failed to list Pluggable Databases: %v", err) + } + + // Create a map to track existing PDBDetailsStatus by PdbName + pdbDetailsMap := make(map[string]databasev4.PDBConfigStatus) + + // Populate the map with existing PDBDetailsStatus from dbcsInst.Status.PdbDetailsStatus + // for _, existingPdbDetails := range dbcsInst.Status.PdbDetailsStatus { + // for _, existingPdbConfig := range existingPdbDetails.PDBConfigStatus { + // pdbDetailsMap[*existingPdbConfig.PdbName] = existingPdbConfig + // } + // } + // Convert databaseIds array to a set for quick lookup + databaseIdsSet := make(map[string]struct{}) + for _, id := range databaseIds { + databaseIdsSet[id] = struct{}{} + } + // Update the map with new PDB details from the response + for _, pdb := range response.Items { + if pdb.ContainerDatabaseId != nil { + // Check if the ContainerDatabaseId is in the set of databaseIds + if _, exists := databaseIdsSet[*pdb.ContainerDatabaseId]; exists { + pdbConfigStatus := databasev4.PDBConfigStatus{ + PdbName: pdb.PdbName, + ShouldPdbAdminAccountBeLocked: pdb.IsRestricted, + FreeformTags: pdb.FreeformTags, + PluggableDatabaseId: pdb.Id, + PdbLifecycleState: convertLifecycleState(pdb.LifecycleState), + } + + // Update the map with the new or updated PDBConfigStatus + pdbDetailsMap[*pdb.PdbName] = pdbConfigStatus + } + } + } + + // Convert the map back to a slice of PDBDetailsStatus + var updatedPdbDetailsStatus []databasev4.PDBDetailsStatus + for _, pdbConfigStatus := range pdbDetailsMap { + updatedPdbDetailsStatus = append(updatedPdbDetailsStatus, databasev4.PDBDetailsStatus{ + PDBConfigStatus: []databasev4.PDBConfigStatus{pdbConfigStatus}, + }) + } + + // Assign the updated slice to dbcsInst.Status.PdbDetailsStatus + dbcsInst.Status.PdbDetailsStatus = updatedPdbDetailsStatus + + return nil +} + +func convertLifecycleState(state database.PluggableDatabaseSummaryLifecycleStateEnum) databasev4.LifecycleState { + switch state { + case database.PluggableDatabaseSummaryLifecycleStateProvisioning: + return databasev4.Provision + case database.PluggableDatabaseSummaryLifecycleStateAvailable: + return databasev4.Available + case database.PluggableDatabaseSummaryLifecycleStateTerminating: + return databasev4.Terminate + case database.PluggableDatabaseSummaryLifecycleStateTerminated: + return databasev4.LifecycleState(databasev4.Terminated) + case database.PluggableDatabaseSummaryLifecycleStateUpdating: + return databasev4.Update + case database.PluggableDatabaseSummaryLifecycleStateFailed: + return databasev4.Failed + default: + return databasev4.Failed + } +} + +// doesPluggableDatabaseExist checks if a pluggable database with the given name exists +func (r *DbcsSystemReconciler) doesPluggableDatabaseExist(ctx context.Context, compartmentId string, pdbName *string, databaseId string) (bool, *string, error) { + if pdbName == nil { + return false, nil, fmt.Errorf("pdbName is nil") + } + + listPdbsReq := database.ListPluggableDatabasesRequest{ + CompartmentId: &compartmentId, + } + + resp, err := r.dbClient.ListPluggableDatabases(ctx, listPdbsReq) + if err != nil { + return false, nil, err + } + + for _, pdb := range resp.Items { + if pdb.ContainerDatabaseId != nil { + if pdb.PdbName != nil && *pdb.PdbName == *pdbName && pdb.LifecycleState != "TERMINATED" && *pdb.ContainerDatabaseId == databaseId { + return true, pdb.Id, nil + } + } + } + + return false, nil, nil +} + +// Function to create KMS vault +func (r *DbcsSystemReconciler) createKMSVault(ctx context.Context, kmsConfig *databasev4.KMSConfig, kmsClient keymanagement.KmsManagementClient, kmsInst *databasev4.KMSDetailsStatus) (*keymanagement.CreateVaultResponse, error) { + // Dereference the ConfigurationProvider pointer + configProvider := *kmsClient.ConfigurationProvider() + + kmsVaultClient, err := keymanagement.NewKmsVaultClientWithConfigurationProvider(configProvider) + if err != nil { + r.Logger.Error(err, "Error creating KMS vault client") + return nil, err + } + var vaultType keymanagement.CreateVaultDetailsVaultTypeEnum + + if kmsConfig.VaultType != "" { + switch kmsConfig.VaultType { + case "VIRTUAL_PRIVATE": + vaultType = keymanagement.CreateVaultDetailsVaultTypeVirtualPrivate + case "EXTERNAL": + vaultType = keymanagement.CreateVaultDetailsVaultTypeExternal + case "DEFAULT": + vaultType = keymanagement.CreateVaultDetailsVaultTypeDefault + default: + err := fmt.Errorf("unsupported VaultType specified: %s", kmsConfig.VaultType) + r.Logger.Error(err, "unsupported VaultType specified") + return nil, err + } + } else { + // Default to DEFAULT if kmsConfig.VaultType is not defined + vaultType = keymanagement.CreateVaultDetailsVaultTypeDefault + } + + createVaultReq := keymanagement.CreateVaultRequest{ + CreateVaultDetails: keymanagement.CreateVaultDetails{ + CompartmentId: common.String(kmsConfig.CompartmentId), + DisplayName: common.String(kmsConfig.VaultName), + VaultType: vaultType, + }, + } + + resp, err := kmsVaultClient.CreateVault(ctx, createVaultReq) + if err != nil { + r.Logger.Error(err, "Error creating KMS vault") + return nil, err + } + // Wait until vault becomes active or timeout + timeout := time.After(5 * time.Minute) // Example timeout: 5 minutes + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + for { + select { + case <-timeout: + r.Logger.Error(err, "timed out waiting for vault to become active") + case <-ticker.C: + getVaultReq := keymanagement.GetVaultRequest{ + VaultId: resp.Id, + } + + getResp, err := kmsVaultClient.GetVault(ctx, getVaultReq) + if err != nil { + r.Logger.Error(err, "Error getting vault status") + return nil, err + } + + if getResp.LifecycleState == keymanagement.VaultLifecycleStateActive { + r.Logger.Info("KMS vault created successfully and active") + // Save the vault details into KMSConfig + kmsInst.VaultId = *getResp.Vault.Id + kmsInst.ManagementEndpoint = *getResp.Vault.ManagementEndpoint + kmsInst.VaultName = *getResp.DisplayName + kmsInst.CompartmentId = *getResp.CompartmentId + kmsInst.VaultType = kmsConfig.VaultType + return &keymanagement.CreateVaultResponse{}, err + } + + r.Logger.Info(fmt.Sprintf("Vault state: %s, waiting for active state...", string(getResp.LifecycleState))) + } + } +} + +// Function to create KMS key +func (r *DbcsSystemReconciler) createKMSKey(ctx context.Context, kmsConfig *databasev4.KMSConfig, kmsClient keymanagement.KmsManagementClient, kmsInst *databasev4.KMSDetailsStatus) (*keymanagement.CreateKeyResponse, error) { + // Determine the KeyShape based on the encryption algorithm + var algorithm keymanagement.KeyShapeAlgorithmEnum + var keyLength int + switch kmsConfig.EncryptionAlgo { + case "AES": + algorithm = keymanagement.KeyShapeAlgorithmAes + keyLength = 32 + case "RSA": + algorithm = keymanagement.KeyShapeAlgorithmRsa + keyLength = 512 + default: + // Default to AES if the provided algorithm is unsupported + algorithm = keymanagement.KeyShapeAlgorithmAes + keyLength = 32 + r.Logger.Info("Unsupported encryption algorithm. Defaulting to AES.") + } + + // Create the key shape with the algorithm + keyShape := keymanagement.KeyShape{ + Algorithm: algorithm, + Length: common.Int(keyLength), + } + + createKeyReq := keymanagement.CreateKeyRequest{ + CreateKeyDetails: keymanagement.CreateKeyDetails{ + CompartmentId: common.String(kmsConfig.CompartmentId), + DisplayName: common.String(kmsConfig.KeyName), + KeyShape: &keyShape, + }, + RequestMetadata: common.RequestMetadata{}, + } + + // Call CreateKey without vaultID + resp, err := kmsClient.CreateKey(ctx, createKeyReq) + if err != nil { + r.Logger.Error(err, "Error creating KMS key:") + return nil, err + } + + r.Logger.Info("KMS key created successfully:", resp) + kmsInst.KeyId = *resp.Key.Id + kmsInst.EncryptionAlgo = string(algorithm) + return &resp, nil +} + +func (r *DbcsSystemReconciler) getSecret(ctx context.Context, namespace, secretName string) (string, error) { + secret := &corev1.Secret{} + err := r.KubeClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: secretName}, secret) + if err != nil { + return "", err + } + + // Assume the secret contains only one key-value pair + for _, value := range secret.Data { + return string(value), nil + } + + return "", fmt.Errorf("secret %s is empty", secretName) +} + +// func (r *DbcsSystemReconciler) cloneDbSystem(ctx context.Context, dbcsInst *databasev4.DbcsSystem, provider common.ConfigurationProvider) error { + +// // Initialize OCI clients +// dbClient, err := database.NewDatabaseClientWithConfigurationProvider(provider) +// if err != nil { +// return fmt.Errorf("failed to create OCI database client: %v", err) +// } + +// // Get DB System details +// compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Status.Id) +// if err != nil { +// fmt.Printf("Failed to get compartment ID: %v\n", err) +// return err +// } + +// dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, *dbcsInst.Status.Id) +// if err != nil { +// fmt.Printf("Failed to get DB Home ID: %v\n", err) +// return err +// } + +// databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, *dbcsInst.Status.Id, compartmentId, dbHomeId) +// if err != nil { +// fmt.Printf("Failed to get database IDs: %v\n", err) +// return err +// } + +// // Use the first database ID for cloning +// if len(databaseIds) == 0 { +// return fmt.Errorf("no databases found in the DB system") +// } + +// // Retrieve details of the database to clone +// sourceDatabaseId := databaseIds[0] +// _, err = dbClient.GetDatabase(ctx, database.GetDatabaseRequest{ +// DatabaseId: common.String(sourceDatabaseId), +// }) +// if err != nil { +// return fmt.Errorf("failed to get source database details: %v", err) +// } + +// // adminPassword, err := dbcsv1.GetAdminPassword(kubeClient, dbcsInstance) +// // if err != nil { +// // log.Fatalf("Error getting admin password: %v", err) +// // } + +// // tdePassword, err := GetTdePassword(kubeClient, dbcsInstance) +// // if err != nil { +// // log.Fatalf("Error getting TDE password: %v", err) +// // } + +// // Define the details for creating the database from the existing DB system +// // createDatabaseDetails := CreateDatabaseBaseWrapper{ +// // CreateDatabaseFromDbSystemDetails: database.CreateDatabaseFromDbSystemDetails{ +// // AdminPassword: common.String(adminPassword), // Replace with actual admin password +// // DbName: common.String(dbcsInst.Spec.DbSystem.DbName), // Use the dbName from DbcsSystemSpec +// // DbDomain: common.String(dbcsInst.Spec.DbSystem.DbDomain), // Use the dbDomain from DbcsSystemSpec +// // DbUniqueName: common.String(dbcsInst.Spec.DbSystem.DbUniqueName), // Use the dbUniqueName from DbcsSystemSpec +// // DbBackupConfig: &database.DbBackupConfig{ +// // AutoBackupEnabled: dbcsInst.Spec.DbSystem.DbBackupConfig.AutoBackupEnabled, +// // RecoveryWindowInDays: dbcsInst.Spec.DbSystem.DbBackupConfig.RecoveryWindowsInDays, +// // }, +// // FreeformTags: dbcsInst.Spec.DbSystem.Tags, +// // DefinedTags: map[string]map[string]interface{}{ +// // "Namespace": { +// // "TagKey": "TagValue", // Replace with actual defined tags if needed +// // }, +// // }, +// // }, +// // } +// // createDatabaseRequest := database.CreateDatabaseRequest{ +// // CreateNewDatabaseDetails: &createDatabaseDetails, +// // } + +// // createDatabaseResponse, err := dbClient.CreateDatabase(ctx, createDatabaseRequest) +// // if err != nil { +// // return fmt.Errorf("failed to create database from DB system: %v", err) +// // } + +// // // Update instance status with the new database ID +// // dbcsInst.Status.DbInfo = append(dbcsInst.Status.DbInfo, databasev4.DbStatus{ +// // Id: createDatabaseResponse.Database.Id, +// // DbName: dbcsInst.Spec.DbSystem.DbName, +// // DbUniqueName: dbcsInst.Spec.DbSystem.DbUniqueName, +// // }) + +// // err = r.KubeClient.Status().Update(ctx, dbcsInst) +// // if err != nil { +// // return fmt.Errorf("failed to update instance status with database ID: %v", err) +// // } + +// return nil +// } + +// Convert DbBackupConfigAutoBackupWindowEnum to *string +func autoBackupWindowEnumToStringPtr(enum *database.DbBackupConfigAutoBackupWindowEnum) *string { + if enum == nil { + return nil + } + value := string(*enum) + return &value +} +func (r *DbcsSystemReconciler) stringToDbBackupConfigAutoBackupWindowEnum(value *string) (database.DbBackupConfigAutoBackupWindowEnum, error) { + // Define a default value + // Define a default value + const defaultAutoBackupWindow = database.DbBackupConfigAutoBackupWindowOne + + if value == nil { + return defaultAutoBackupWindow, nil // Return the default value + } + + // Convert to enum + enum, ok := database.GetMappingDbBackupConfigAutoBackupWindowEnum(*value) + if !ok { + return "", fmt.Errorf("invalid value for AutoBackupWindow: %s", *value) + } + return enum, nil } -func assignDBCSID(dbcsInst *databasev1alpha1.DbcsSystem, dbcsID string) { +func assignDBCSID(dbcsInst *databasev4.DbcsSystem, dbcsID string) { dbcsInst.Spec.Id = &dbcsID } @@ -286,8 +1448,8 @@ func (r *DbcsSystemReconciler) eventFilterPredicate() predicate.Predicate { }, UpdateFunc: func(e event.UpdateEvent) bool { // Get the dbName as old dbName when an update event happens - oldObject := e.ObjectOld.DeepCopyObject().(*databasev1alpha1.DbcsSystem) - newObject := e.ObjectNew.DeepCopyObject().(*databasev1alpha1.DbcsSystem) + oldObject := e.ObjectOld.DeepCopyObject().(*databasev4.DbcsSystem) + newObject := e.ObjectNew.DeepCopyObject().(*databasev4.DbcsSystem) specObject := !reflect.DeepEqual(oldObject.Spec, newObject.Spec) deletionTimeStamp := !reflect.DeepEqual(oldObject.GetDeletionTimestamp(), newObject.GetDeletionTimestamp()) @@ -307,7 +1469,7 @@ func (r *DbcsSystemReconciler) eventFilterPredicate() predicate.Predicate { // SetupWithManager sets up the controller with the Manager. func (r *DbcsSystemReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&databasev1alpha1.DbcsSystem{}). + For(&databasev4.DbcsSystem{}). WithEventFilter(r.eventFilterPredicate()). WithOptions(controller.Options{MaxConcurrentReconciles: 50}). Complete(r) diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 7fcaac2b..ad676133 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -67,14 +67,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" shardingv1 "github.com/oracle/oracle-database-operator/commons/sharding" ) // Sharding Topology type ShardingTopology struct { topicid string - Instance *databasev1alpha1.ShardingDatabase + Instance *databasev4.ShardingDatabase deltopology bool onsProvider common.ConfigurationProvider onsProviderFlag bool @@ -120,10 +120,10 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // your logic here var i int32 - //var ShardImageLatest []databasev1alpha1.ShardSpec - var OraCatalogSpex databasev1alpha1.CatalogSpec - var OraShardSpex databasev1alpha1.ShardSpec - var OraGsmSpex databasev1alpha1.GsmSpec + //var ShardImageLatest []databasev4.ShardSpec + var OraCatalogSpex databasev4.CatalogSpec + var OraShardSpex databasev4.ShardSpec + var OraGsmSpex databasev4.GsmSpec var result ctrl.Result var isShardTopologyDeleteTrue bool = false //var msg string @@ -146,7 +146,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req } } // Fetch the ProvShard instance - instance := &databasev1alpha1.ShardingDatabase{} + instance := &databasev4.ShardingDatabase{} err = r.Client.Get(context.TODO(), req.NamespacedName, instance) if err != nil { if errors.IsNotFound(err) { @@ -424,7 +424,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req } } - stateType = string(databasev1alpha1.CrdReconcileCompeleteState) + stateType = string(databasev4.CrdReconcileCompeleteState) // r.setCrdLifeCycleState(instance, &result, &err, stateType) // Set error to ni to avoid reconcilation state reconcilation error as we are passing err to setCrdLifeCycleState @@ -439,7 +439,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // Check https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options to under MaxConcurrentReconciles func (r *ShardingDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&databasev1alpha1.ShardingDatabase{}). + For(&databasev4.ShardingDatabase{}). Owns(&appsv1.StatefulSet{}). Owns(&corev1.Service{}). Owns(&corev1.Pod{}). @@ -484,7 +484,7 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate break } - if e.Object.GetLabels()[string(databasev1alpha1.ShardingDelLabelKey)] == string(databasev1alpha1.ShardingDelLabelTrueValue) { + if e.Object.GetLabels()[string(databasev4.ShardingDelLabelKey)] == string(databasev4.ShardingDelLabelTrueValue) { break } @@ -515,7 +515,7 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate } // ================ Function to check secret update============= -func (r *ShardingDatabaseReconciler) UpdateSecret(instance *databasev1alpha1.ShardingDatabase, kClient client.Client, logger logr.Logger) (ctrl.Result, error) { +func (r *ShardingDatabaseReconciler) UpdateSecret(instance *databasev4.ShardingDatabase, kClient client.Client, logger logr.Logger) (ctrl.Result, error) { sc := &corev1.Secret{} //var err error @@ -534,7 +534,7 @@ func (r *ShardingDatabaseReconciler) UpdateSecret(instance *databasev1alpha1.Sha } // ================== Function to get the Notification controller ============== -func (r *ShardingDatabaseReconciler) getOnsConfigProvider(instance *databasev1alpha1.ShardingDatabase, idx int, +func (r *ShardingDatabaseReconciler) getOnsConfigProvider(instance *databasev4.ShardingDatabase, idx int, ) { var err error if instance.Spec.DbSecret.NsConfigMap != "" && instance.Spec.DbSecret.NsSecret != "" && r.osh[idx].onsProviderFlag != true { @@ -559,7 +559,7 @@ func (r *ShardingDatabaseReconciler) getOnsConfigProvider(instance *databasev1al } // ================== Function the Message ============== -func (r *ShardingDatabaseReconciler) sendMessage(instance *databasev1alpha1.ShardingDatabase, title string, body string) { +func (r *ShardingDatabaseReconciler) sendMessage(instance *databasev4.ShardingDatabase, title string, body string) { idx, instFlag := r.checkProvInstance(instance) if instFlag { if r.osh[idx].onsProviderFlag { @@ -568,9 +568,9 @@ func (r *ShardingDatabaseReconciler) sendMessage(instance *databasev1alpha1.Shar } } -func (r *ShardingDatabaseReconciler) publishEvents(instance *databasev1alpha1.ShardingDatabase, eventMsg string, state string) { +func (r *ShardingDatabaseReconciler) publishEvents(instance *databasev4.ShardingDatabase, eventMsg string, state string) { - if state == string(databasev1alpha1.AvailableState) || state == string(databasev1alpha1.AddingShardState) || state == string(databasev1alpha1.ShardOnlineState) || state == string(databasev1alpha1.ProvisionState) || state == string(databasev1alpha1.DeletingState) || state == string(databasev1alpha1.Terminated) { + if state == string(databasev4.AvailableState) || state == string(databasev4.AddingShardState) || state == string(databasev4.ShardOnlineState) || state == string(databasev4.ProvisionState) || state == string(databasev4.DeletingState) || state == string(databasev4.Terminated) { r.Recorder.Eventf(instance, corev1.EventTypeNormal, "State Change", eventMsg) } else { r.Recorder.Eventf(instance, corev1.EventTypeWarning, "State Change", eventMsg) @@ -580,7 +580,7 @@ func (r *ShardingDatabaseReconciler) publishEvents(instance *databasev1alpha1.Sh } // ================== Function to check insytance deletion timestamp and activate the finalizer code ======== -func (r *ShardingDatabaseReconciler) finalizerShardingDatabaseInstance(instance *databasev1alpha1.ShardingDatabase, +func (r *ShardingDatabaseReconciler) finalizerShardingDatabaseInstance(instance *databasev4.ShardingDatabase, ) (error, bool) { isProvOShardToBeDeleted := instance.GetDeletionTimestamp() != nil @@ -619,7 +619,7 @@ func (r *ShardingDatabaseReconciler) finalizerShardingDatabaseInstance(instance } // ========================== FInalizer Section =================== -func (r *ShardingDatabaseReconciler) addFinalizer(instance *databasev1alpha1.ShardingDatabase) error { +func (r *ShardingDatabaseReconciler) addFinalizer(instance *databasev4.ShardingDatabase) error { reqLogger := r.Log.WithValues("instance.Spec.Namespace", instance.Spec.Namespace, "instance.Name", instance.Name) controllerutil.AddFinalizer(instance, shardingv1.ShardingDatabaseFinalizer) @@ -632,7 +632,7 @@ func (r *ShardingDatabaseReconciler) addFinalizer(instance *databasev1alpha1.Sha return nil } -func (r *ShardingDatabaseReconciler) finalizeShardingDatabase(instance *databasev1alpha1.ShardingDatabase) error { +func (r *ShardingDatabaseReconciler) finalizeShardingDatabase(instance *databasev4.ShardingDatabase) error { // TODO(user): Add the cleanup steps that the operator needs to do before the CR // can be deleted. Examples of finalizers include performing backups and deleting // resources that are not owned by this CR, like a PVC. @@ -834,8 +834,8 @@ func (r *ShardingDatabaseReconciler) finalizeShardingDatabase(instance *database r.osh[idx].deltopology = false //r.osh[idx].addSem.Release(1) //r.osh[idx].delSem.Release(1) - //instance1 := &shardingv1alpha1.ProvShard{} - r.osh[idx].Instance = &databasev1alpha1.ShardingDatabase{} + //instance1 := &shardingv4.ProvShard{} + r.osh[idx].Instance = &databasev4.ShardingDatabase{} //r.osh[idx] = nil @@ -845,7 +845,7 @@ func (r *ShardingDatabaseReconciler) finalizeShardingDatabase(instance *database //============== // Get the current instance -func (r *ShardingDatabaseReconciler) checkProvInstance(instance *databasev1alpha1.ShardingDatabase, +func (r *ShardingDatabaseReconciler) checkProvInstance(instance *databasev4.ShardingDatabase, ) (int, bool) { var status bool = false @@ -865,7 +865,7 @@ func (r *ShardingDatabaseReconciler) checkProvInstance(instance *databasev1alpha } // =========== validate Specs ============ -func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev4.ShardingDatabase, idx int) error { var eventMsg string var eventErr string = "Spec Error" @@ -950,7 +950,7 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.Sha return nil } -func (r *ShardingDatabaseReconciler) checkShardingType(instance *databasev1alpha1.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) checkShardingType(instance *databasev4.ShardingDatabase, idx int) error { var i, k int32 var regionFlag bool @@ -973,7 +973,7 @@ func (r *ShardingDatabaseReconciler) checkShardingType(instance *databasev1alpha // Check the ShardGroups/ Shard Space and Shard group Name // checkShrdGSR is Shardgroup/ShardSpace/ShardRegion -func (r *ShardingDatabaseReconciler) checkShardSpace(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) error { +func (r *ShardingDatabaseReconciler) checkShardSpace(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) error { if instance.Spec.ShardingType != "" { // Check for the Sharding Type and if it is USER do following @@ -992,7 +992,7 @@ func (r *ShardingDatabaseReconciler) checkShardSpace(instance *databasev1alpha1. // Check the ShardGroups/ Shard Space and Shard group Name // checkShrdGSR is Shardgroup/ShardSpace/ShardRegion -func (r *ShardingDatabaseReconciler) checkShardGroup(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) error { +func (r *ShardingDatabaseReconciler) checkShardGroup(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) error { // We need to check Shard Region and Shard Group for ShardingType='SYSTEM' and 'NATIVE' if strings.TrimSpace(strings.ToUpper(instance.Spec.ShardingType)) != "USER" { @@ -1011,7 +1011,7 @@ func (r *ShardingDatabaseReconciler) checkShardGroup(instance *databasev1alpha1. // Compare GSM Env Variables -func (r *ShardingDatabaseReconciler) comapreGsmEnvVariables(instance *databasev1alpha1.ShardingDatabase, lastSuccSpec *databasev1alpha1.ShardingDatabaseSpec) bool { +func (r *ShardingDatabaseReconciler) comapreGsmEnvVariables(instance *databasev4.ShardingDatabase, lastSuccSpec *databasev4.ShardingDatabaseSpec) bool { var eventMsg string var eventErr string = "Spec Error" var i, j int32 @@ -1036,7 +1036,7 @@ func (r *ShardingDatabaseReconciler) comapreGsmEnvVariables(instance *databasev1 return true } -func (r *ShardingDatabaseReconciler) comapreCatalogEnvVariables(instance *databasev1alpha1.ShardingDatabase, lastSuccSpec *databasev1alpha1.ShardingDatabaseSpec) bool { +func (r *ShardingDatabaseReconciler) comapreCatalogEnvVariables(instance *databasev4.ShardingDatabase, lastSuccSpec *databasev4.ShardingDatabaseSpec) bool { var eventMsg string var eventErr string = "Spec Error" var i, j int32 @@ -1061,7 +1061,7 @@ func (r *ShardingDatabaseReconciler) comapreCatalogEnvVariables(instance *databa return true } -func (r *ShardingDatabaseReconciler) comapreShardEnvVariables(instance *databasev1alpha1.ShardingDatabase, lastSuccSpec *databasev1alpha1.ShardingDatabaseSpec) bool { +func (r *ShardingDatabaseReconciler) comapreShardEnvVariables(instance *databasev4.ShardingDatabase, lastSuccSpec *databasev4.ShardingDatabaseSpec) bool { var eventMsg string var eventErr string = "Spec Error" var i, j int32 @@ -1088,21 +1088,21 @@ func (r *ShardingDatabaseReconciler) comapreShardEnvVariables(instance *database //===== Set the CRD resource life cycle state ======== -func (r *ShardingDatabaseReconciler) setCrdLifeCycleState(instance *databasev1alpha1.ShardingDatabase, result *ctrl.Result, err *error, stateType *string) { +func (r *ShardingDatabaseReconciler) setCrdLifeCycleState(instance *databasev4.ShardingDatabase, result *ctrl.Result, err *error, stateType *string) { var metaCondition metav1.Condition var updateFlag = false if *stateType == "ReconcileWaiting" { - metaCondition = shardingv1.GetMetaCondition(instance, result, err, *stateType, string(databasev1alpha1.CrdReconcileWaitingReason)) + metaCondition = shardingv1.GetMetaCondition(instance, result, err, *stateType, string(databasev4.CrdReconcileWaitingReason)) updateFlag = true } else if *stateType == "ReconcileComplete" { - metaCondition = shardingv1.GetMetaCondition(instance, result, err, *stateType, string(databasev1alpha1.CrdReconcileCompleteReason)) + metaCondition = shardingv1.GetMetaCondition(instance, result, err, *stateType, string(databasev4.CrdReconcileCompleteReason)) updateFlag = true } else if result.Requeue { - metaCondition = shardingv1.GetMetaCondition(instance, result, err, string(databasev1alpha1.CrdReconcileQueuedState), string(databasev1alpha1.CrdReconcileQueuedReason)) + metaCondition = shardingv1.GetMetaCondition(instance, result, err, string(databasev4.CrdReconcileQueuedState), string(databasev4.CrdReconcileQueuedReason)) updateFlag = true } else if *err != nil { - metaCondition = shardingv1.GetMetaCondition(instance, result, err, string(databasev1alpha1.CrdReconcileErrorState), string(databasev1alpha1.CrdReconcileErrorReason)) + metaCondition = shardingv1.GetMetaCondition(instance, result, err, string(databasev4.CrdReconcileErrorState), string(databasev4.CrdReconcileErrorReason)) updateFlag = true } else { @@ -1119,7 +1119,7 @@ func (r *ShardingDatabaseReconciler) setCrdLifeCycleState(instance *databasev1al } -func (r *ShardingDatabaseReconciler) validateGsmnCatalog(instance *databasev1alpha1.ShardingDatabase) error { +func (r *ShardingDatabaseReconciler) validateGsmnCatalog(instance *databasev4.ShardingDatabase) error { var err error _, _, err = r.validateCatalog(instance) if err != nil { @@ -1132,7 +1132,7 @@ func (r *ShardingDatabaseReconciler) validateGsmnCatalog(instance *databasev1alp return nil } -func (r *ShardingDatabaseReconciler) validateGsm(instance *databasev1alpha1.ShardingDatabase, +func (r *ShardingDatabaseReconciler) validateGsm(instance *databasev4.ShardingDatabase, ) (*appsv1.StatefulSet, *corev1.Pod, error) { //var err error var i int32 @@ -1160,7 +1160,7 @@ func (r *ShardingDatabaseReconciler) validateGsm(instance *databasev1alpha1.Shar return gsmSfSet, gsmPod, fmt.Errorf("GSM is not ready") } -func (r *ShardingDatabaseReconciler) validateInvidualGsm(instance *databasev1alpha1.ShardingDatabase, OraGsmSpex databasev1alpha1.GsmSpec, specId int, +func (r *ShardingDatabaseReconciler) validateInvidualGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databasev4.GsmSpec, specId int, ) (*appsv1.StatefulSet, *corev1.Pod, error) { //var err error var i int32 @@ -1176,7 +1176,7 @@ func (r *ShardingDatabaseReconciler) validateInvidualGsm(instance *databasev1alp if err != nil { msg = "Unable to find GSM statefulset " + shardingv1.GetFmtStr(OraGsmSpex.Name) + "." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateGsmStatus(instance, int(i), string(databasev1alpha1.StatefulSetNotFound)) + r.updateGsmStatus(instance, int(i), string(databasev4.StatefulSetNotFound)) return gsmSfSet, gsmPod, err } @@ -1184,7 +1184,7 @@ func (r *ShardingDatabaseReconciler) validateInvidualGsm(instance *databasev1alp if err != nil { msg = "Unable to find any pod in statefulset " + shardingv1.GetFmtStr(gsmSfSet.Name) + "." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateGsmStatus(instance, int(i), string(databasev1alpha1.PodNotFound)) + r.updateGsmStatus(instance, int(i), string(databasev4.PodNotFound)) return gsmSfSet, gsmPod, err } @@ -1192,22 +1192,22 @@ func (r *ShardingDatabaseReconciler) validateInvidualGsm(instance *databasev1alp if !isPodExist { msg = "Unable to validate GSM " + shardingv1.GetFmtStr(gsmPod.Name) + " pod. GSM pod doesn't seems to be ready to accept the commands." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateGsmStatus(instance, int(i), string(databasev1alpha1.PodNotReadyState)) + r.updateGsmStatus(instance, int(i), string(databasev4.PodNotReadyState)) return gsmSfSet, gsmPod, fmt.Errorf("pod doesn't exist") } err = shardingv1.CheckGsmStatus(gsmPod.Name, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { msg = "Unable to validate GSM director. GSM director doesn't seems to be ready to accept the commands." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateGsmStatus(instance, int(i), string(databasev1alpha1.ProvisionState)) + r.updateGsmStatus(instance, int(i), string(databasev4.ProvisionState)) return gsmSfSet, gsmPod, err } - r.updateGsmStatus(instance, specId, string(databasev1alpha1.AvailableState)) + r.updateGsmStatus(instance, specId, string(databasev4.AvailableState)) return gsmSfSet, gsmPod, nil } -func (r *ShardingDatabaseReconciler) validateCatalog(instance *databasev1alpha1.ShardingDatabase, +func (r *ShardingDatabaseReconciler) validateCatalog(instance *databasev4.ShardingDatabase, ) (*appsv1.StatefulSet, *corev1.Pod, error) { catalogSfSet := &appsv1.StatefulSet{} @@ -1236,7 +1236,7 @@ func (r *ShardingDatabaseReconciler) validateCatalog(instance *databasev1alpha1. } // === Validate Individual Catalog -func (r *ShardingDatabaseReconciler) validateInvidualCatalog(instance *databasev1alpha1.ShardingDatabase, OraCatalogSpex databasev1alpha1.CatalogSpec, specId int, +func (r *ShardingDatabaseReconciler) validateInvidualCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec, specId int, ) (*appsv1.StatefulSet, *corev1.Pod, error) { var err error @@ -1249,7 +1249,7 @@ func (r *ShardingDatabaseReconciler) validateInvidualCatalog(instance *databasev if err != nil { msg := "Unable to find Catalog statefulset " + shardingv1.GetFmtStr(OraCatalogSpex.Name) + "." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateCatalogStatus(instance, specId, string(databasev1alpha1.StatefulSetNotFound)) + r.updateCatalogStatus(instance, specId, string(databasev4.StatefulSetNotFound)) return catalogSfSet, catalogPod, err } @@ -1257,31 +1257,31 @@ func (r *ShardingDatabaseReconciler) validateInvidualCatalog(instance *databasev if err != nil { msg := "Unable to find any pod in statefulset " + shardingv1.GetFmtStr(catalogSfSet.Name) + "." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateCatalogStatus(instance, specId, string(databasev1alpha1.PodNotFound)) + r.updateCatalogStatus(instance, specId, string(databasev4.PodNotFound)) return catalogSfSet, catalogPod, err } isPodExist, catalogPod = shardingv1.PodListValidation(podList, catalogSfSet.Name, instance, r.Client) if !isPodExist { msg := "Unable to validate Catalog " + shardingv1.GetFmtStr(catalogSfSet.Name) + " pod. Catalog pod doesn't seems to be ready to accept the commands." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateCatalogStatus(instance, specId, string(databasev1alpha1.PodNotReadyState)) + r.updateCatalogStatus(instance, specId, string(databasev4.PodNotReadyState)) return catalogSfSet, catalogPod, fmt.Errorf("Pod doesn't exist") } err = shardingv1.ValidateDbSetup(catalogPod.Name, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { msg := "Unable to validate Catalog. Catalog doesn't seems to be ready to accept the commands." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateCatalogStatus(instance, specId, string(databasev1alpha1.ProvisionState)) + r.updateCatalogStatus(instance, specId, string(databasev4.ProvisionState)) return catalogSfSet, catalogPod, err } - r.updateCatalogStatus(instance, specId, string(databasev1alpha1.AvailableState)) + r.updateCatalogStatus(instance, specId, string(databasev4.AvailableState)) return catalogSfSet, catalogPod, nil } // ======= Function to validate Shard -func (r *ShardingDatabaseReconciler) validateShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec, specId int, +func (r *ShardingDatabaseReconciler) validateShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec, specId int, ) (*appsv1.StatefulSet, *corev1.Pod, error) { var err error @@ -1292,7 +1292,7 @@ func (r *ShardingDatabaseReconciler) validateShard(instance *databasev1alpha1.Sh if err != nil { msg := "Unable to find Shard statefulset " + shardingv1.GetFmtStr(OraShardSpex.Name) + "." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateShardStatus(instance, specId, string(databasev1alpha1.StatefulSetNotFound)) + r.updateShardStatus(instance, specId, string(databasev4.StatefulSetNotFound)) return shardSfSet, shardPod, err } @@ -1300,30 +1300,30 @@ func (r *ShardingDatabaseReconciler) validateShard(instance *databasev1alpha1.Sh if err != nil { msg := "Unable to find any pod in statefulset " + shardingv1.GetFmtStr(shardSfSet.Name) + "." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateShardStatus(instance, specId, string(databasev1alpha1.PodNotFound)) + r.updateShardStatus(instance, specId, string(databasev4.PodNotFound)) return shardSfSet, shardPod, err } isPodExist, shardPod := shardingv1.PodListValidation(podList, shardSfSet.Name, instance, r.Client) if !isPodExist { msg := "Unable to validate Shard " + shardingv1.GetFmtStr(shardPod.Name) + " pod. Shard pod doesn't seems to be ready to accept the commands." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateShardStatus(instance, specId, string(databasev1alpha1.PodNotReadyState)) + r.updateShardStatus(instance, specId, string(databasev4.PodNotReadyState)) return shardSfSet, shardPod, err } err = shardingv1.ValidateDbSetup(shardPod.Name, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { msg := "Unable to validate shard. Shard doesn't seems to be ready to accept the commands." shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) - r.updateShardStatus(instance, specId, string(databasev1alpha1.ProvisionState)) + r.updateShardStatus(instance, specId, string(databasev4.ProvisionState)) return shardSfSet, shardPod, err } - r.updateShardStatus(instance, specId, string(databasev1alpha1.AvailableState)) + r.updateShardStatus(instance, specId, string(databasev4.AvailableState)) return shardSfSet, shardPod, nil } // This function updates the shard topology over all -func (r *ShardingDatabaseReconciler) updateShardTopologyStatus(instance *databasev1alpha1.ShardingDatabase) { +func (r *ShardingDatabaseReconciler) updateShardTopologyStatus(instance *databasev4.ShardingDatabase) { //shardPod := &corev1.Pod{} //gsmSfSet := &appsv1.StatefulSet{} gsmPod := &corev1.Pod{} @@ -1340,7 +1340,7 @@ func (r *ShardingDatabaseReconciler) updateShardTopologyStatus(instance *databas } -func (r *ShardingDatabaseReconciler) updateShardTopologyShardsInGsm(instance *databasev1alpha1.ShardingDatabase, gsmPod *corev1.Pod) { +func (r *ShardingDatabaseReconciler) updateShardTopologyShardsInGsm(instance *databasev4.ShardingDatabase, gsmPod *corev1.Pod) { shardSfSet := &appsv1.StatefulSet{} //shardPod := &corev1.Pod{} //gsmSfSet := &appsv1.StatefulSet{} @@ -1366,7 +1366,7 @@ func (r *ShardingDatabaseReconciler) updateShardTopologyShardsInGsm(instance *da } } -func (r *ShardingDatabaseReconciler) updateGsmStatus(instance *databasev1alpha1.ShardingDatabase, specIdx int, state string) { +func (r *ShardingDatabaseReconciler) updateGsmStatus(instance *databasev4.ShardingDatabase, specIdx int, state string) { var currState string var eventMsg string @@ -1396,7 +1396,7 @@ func (r *ShardingDatabaseReconciler) updateGsmStatus(instance *databasev1alpha1. } } -func (r *ShardingDatabaseReconciler) updateCatalogStatus(instance *databasev1alpha1.ShardingDatabase, specIdx int, state string) { +func (r *ShardingDatabaseReconciler) updateCatalogStatus(instance *databasev4.ShardingDatabase, specIdx int, state string) { var eventMsg string var currState string var eventMsgFlag = true @@ -1404,7 +1404,7 @@ func (r *ShardingDatabaseReconciler) updateCatalogStatus(instance *databasev1alp name := instance.Spec.Catalog[specIdx].Name if len(instance.Status.Catalog) > 0 { - currState = shardingv1.GetGsmCatalogStatusKey(instance, name+"_"+string(databasev1alpha1.State)) + currState = shardingv1.GetGsmCatalogStatusKey(instance, name+"_"+string(databasev4.State)) if currState == state { eventMsgFlag = false } @@ -1423,14 +1423,14 @@ func (r *ShardingDatabaseReconciler) updateCatalogStatus(instance *databasev1alp } } -func (r *ShardingDatabaseReconciler) updateShardStatus(instance *databasev1alpha1.ShardingDatabase, specIdx int, state string) { +func (r *ShardingDatabaseReconciler) updateShardStatus(instance *databasev4.ShardingDatabase, specIdx int, state string) { var eventMsg string var currState string var eventMsgFlag = true name := instance.Spec.Shard[specIdx].Name if len(instance.Status.Shard) > 0 { - currState = shardingv1.GetGsmShardStatusKey(instance, name+"_"+string(databasev1alpha1.State)) + currState = shardingv1.GetGsmShardStatusKey(instance, name+"_"+string(databasev4.State)) if currState == state { eventMsgFlag = false } @@ -1449,7 +1449,7 @@ func (r *ShardingDatabaseReconciler) updateShardStatus(instance *databasev1alpha } } -func (r *ShardingDatabaseReconciler) updateGsmShardStatus(instance *databasev1alpha1.ShardingDatabase, name string, state string) { +func (r *ShardingDatabaseReconciler) updateGsmShardStatus(instance *databasev4.ShardingDatabase, name string, state string) { var eventMsg string var currState string var eventMsgFlag = true @@ -1480,7 +1480,7 @@ func (r *ShardingDatabaseReconciler) updateGsmShardStatus(instance *databasev1al } // This function add the Primary Shards in GSM -func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev4.ShardingDatabase, idx int) error { //var result ctrl.Result var result ctrl.Result var i int32 @@ -1509,7 +1509,7 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { if setLifeCycleFlag != true { setLifeCycleFlag = true - stateType := string(databasev1alpha1.CrdReconcileWaitingState) + stateType := string(databasev4.CrdReconcileWaitingState) r.setCrdLifeCycleState(instance, &result, &err, &stateType) } // 1st Step is to check if Shard is in good state if not then just continue @@ -1569,10 +1569,10 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 // If the shard doesn't exist in GSM then just add the shard statefulset and update GSM shard status // ADD Shard in GSM - r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.AddingShardState)) + r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.AddingShardState)) err = shardingv1.AddShardInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { - r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.AddingShardErrorState)) + r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.AddingShardErrorState)) title = "Shard Addition Failure" message = "Error occurred during shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " addition." shardingv1.LogMessages("INFO", title+":"+message, nil, instance, r.Log) @@ -1606,7 +1606,7 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev1alpha1 } // This function Check the online shard -func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev1alpha1.ShardingDatabase, gsmPod *corev1.Pod, shardSfSet *appsv1.StatefulSet, OraShardSpex databasev1alpha1.ShardSpec) error { +func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev4.ShardingDatabase, gsmPod *corev1.Pod, shardSfSet *appsv1.StatefulSet, OraShardSpex databasev4.ShardSpec) error { //var result ctrl.Result //var i int32 var err error @@ -1619,16 +1619,16 @@ func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev1alpha1.Sha if err != nil { // If the shard doesn't exist in GSM then just delete the shard statefulset and update GSM shard status /// Terminate state means we will remove teh shard entry from GSM shard status - r.updateGsmShardStatus(instance, shardSfSet.Name, string(databasev1alpha1.ShardOnlineErrorState)) + r.updateGsmShardStatus(instance, shardSfSet.Name, string(databasev4.ShardOnlineErrorState)) if strings.ToUpper(instance.Spec.ReplicationType) != "NATIVE" { shardingv1.CancelChunksInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) } return err } oldStateStr := shardingv1.GetGsmShardStatus(instance, shardSfSet.Name) - r.updateGsmShardStatus(instance, shardSfSet.Name, string(databasev1alpha1.ShardOnlineState)) + r.updateGsmShardStatus(instance, shardSfSet.Name, string(databasev4.ShardOnlineState)) // Following logic will sent a email only once - if oldStateStr != string(databasev1alpha1.ShardOnlineState) { + if oldStateStr != string(databasev4.ShardOnlineState) { title = "Shard Addition Completed" message = "Shard addition completed for shard " + shardingv1.GetFmtStr(shardSfSet.Name) + " in GSM." shardingv1.LogMessages("INFO", title+":"+message, nil, instance, r.Log) @@ -1642,14 +1642,14 @@ func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev1alpha1.Sha return nil } -func (r *ShardingDatabaseReconciler) addStandbyShards(instance *databasev1alpha1.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) addStandbyShards(instance *databasev4.ShardingDatabase, idx int) error { //var result ctrl.Result return nil } // ========== Delete Shard Section==================== -func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev4.ShardingDatabase, idx int) error { var result ctrl.Result var i int32 var err error @@ -1671,7 +1671,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar if shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { if setLifeCycleFlag != true { setLifeCycleFlag = true - stateType := string(databasev1alpha1.CrdReconcileWaitingState) + stateType := string(databasev4.CrdReconcileWaitingState) r.setCrdLifeCycleState(instance, &result, &err, &stateType) } // Step 1st to check if GSM is in good state if not then just return because you can't do anything @@ -1699,18 +1699,18 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar // If the shard doesn't exist in GSM then just delete the shard statefulset and update GSM shard status /// Terminate state means we will remove teh shard entry from GSM shard status r.delShard(instance, shardSfSet.Name, shardSfSet, shardPod, int(i)) - r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.Terminated)) - r.updateShardStatus(instance, int(i), string(databasev1alpha1.Terminated)) + r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.Terminated)) + r.updateShardStatus(instance, int(i), string(databasev4.Terminated)) continue } // 4th step to check if shard is in GSM and shard is online if not then continue // CHeck before deletion if GSM is not ready set the Shard State to Delete Error - r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.DeletingState)) + r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.DeletingState)) err = shardingv1.CheckOnlineShardInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { // If the shard doesn't exist in GSM then just delete the shard statefulset and update GSM shard status /// Terminate state means we will remove teh shard entry from GSM shard status - r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.DeleteErrorState)) + r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.DeleteErrorState)) continue } // 5th Step @@ -1720,7 +1720,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar if len(instance.Spec.ReplicationType) == 0 { err = shardingv1.MoveChunks(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { - r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.ChunkMoveError)) + r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.ChunkMoveError)) title = "Chunk Movement Failure" message = "Error occurred during chunk movement in shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " deletion." r.sendMessage(instance, title, message) @@ -1752,7 +1752,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar instance.Spec.Shard[i].IsDelete = "failed" err = shardingv1.InstanceShardPatch(instance, instance, r.Client, i, "isDelete", "failed") if err != nil { - // r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.ChunkMoveError)) + // r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.ChunkMoveError)) msg = "Error occurred while changing the isDelete value to failed in Spec struct" shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) // return err @@ -1771,7 +1771,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar if err != nil { msg = "Error occurred during shard" + shardingv1.GetFmtStr(OraShardSpex.Name) + "removal from Gsm" shardingv1.LogMessages("Error", msg, nil, instance, r.Log) - r.updateShardStatus(instance, int(i), string(databasev1alpha1.ShardRemoveError)) + r.updateShardStatus(instance, int(i), string(databasev4.ShardRemoveError)) instance.Spec.Shard[i].IsDelete = "failed" continue } @@ -1779,8 +1779,8 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar // 8th Step // Delete the Statefulset as all the chunks has moved and Shard can be phyiscally deleted r.delShard(instance, shardSfSet.Name, shardSfSet, shardPod, int(i)) - r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev1alpha1.Terminated)) - r.updateShardStatus(instance, int(i), string(databasev1alpha1.Terminated)) + r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.Terminated)) + r.updateShardStatus(instance, int(i), string(databasev4.Terminated)) title = "Shard Deletion Completed" message = "Shard deletion completed for shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " in GSM." r.sendMessage(instance, title, message) @@ -1792,7 +1792,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev1alpha1.Shar } // This function delete the physical shard -func (r *ShardingDatabaseReconciler) delShard(instance *databasev1alpha1.ShardingDatabase, sfSetName string, sfSetFound *appsv1.StatefulSet, sfsetPod *corev1.Pod, specIdx int) { +func (r *ShardingDatabaseReconciler) delShard(instance *databasev4.ShardingDatabase, sfSetName string, sfSetFound *appsv1.StatefulSet, sfsetPod *corev1.Pod, specIdx int) { //var status bool var err error @@ -1803,7 +1803,7 @@ func (r *ShardingDatabaseReconciler) delShard(instance *databasev1alpha1.Shardin if err != nil { msg := "Failed to patch the Shard StatefulSet: " + sfSetFound.Name shardingv1.LogMessages("DEBUG", msg, err, instance, r.Log) - r.updateShardStatus(instance, specIdx, string(databasev1alpha1.LabelPatchingError)) + r.updateShardStatus(instance, specIdx, string(databasev4.LabelPatchingError)) return } @@ -1811,7 +1811,7 @@ func (r *ShardingDatabaseReconciler) delShard(instance *databasev1alpha1.Shardin if err != nil { msg = "Failed to delete Shard StatefulSet: " + shardingv1.GetFmtStr(sfSetFound.Name) shardingv1.LogMessages("DEBUG", msg, err, instance, r.Log) - r.updateShardStatus(instance, specIdx, string(databasev1alpha1.DeleteErrorState)) + r.updateShardStatus(instance, specIdx, string(databasev4.DeleteErrorState)) return } /// Delete External Service @@ -1842,14 +1842,14 @@ func (r *ShardingDatabaseReconciler) delShard(instance *databasev1alpha1.Shardin if err != nil { msg = "Failed to delete Shard pvc claim " + shardingv1.GetFmtStr(pvcName) shardingv1.LogMessages("DEBUG", msg, err, instance, r.Log) - r.updateShardStatus(instance, specIdx, string(databasev1alpha1.DeletePVCError)) + r.updateShardStatus(instance, specIdx, string(databasev4.DeletePVCError)) } } } // ======== GSM Invited Node ========== // Remove and add GSM invited node -func (r *ShardingDatabaseReconciler) gsmInvitedNodeOp(instance *databasev1alpha1.ShardingDatabase, objName string, +func (r *ShardingDatabaseReconciler) gsmInvitedNodeOp(instance *databasev4.ShardingDatabase, objName string, ) { var msg string @@ -1892,7 +1892,7 @@ func (r *ShardingDatabaseReconciler) gsmInvitedNodeOp(instance *databasev1alpha1 // ================================== CREATE FUNCTIONS ============================= // This function create a service based isExtern parameter set in the yaml file -func (r *ShardingDatabaseReconciler) createService(instance *databasev1alpha1.ShardingDatabase, +func (r *ShardingDatabaseReconciler) createService(instance *databasev4.ShardingDatabase, dep *corev1.Service, ) (ctrl.Result, error) { reqLogger := r.Log.WithValues("Instance.Namespace", instance.Spec.Namespace, "Instance.Name", instance.Name) @@ -1939,7 +1939,7 @@ func (r *ShardingDatabaseReconciler) createService(instance *databasev1alpha1.Sh } // This function deploy the statefulset -func (r *ShardingDatabaseReconciler) deployStatefulSet(instance *databasev1alpha1.ShardingDatabase, +func (r *ShardingDatabaseReconciler) deployStatefulSet(instance *databasev4.ShardingDatabase, dep *appsv1.StatefulSet, resType string, ) (ctrl.Result, error) { @@ -1993,11 +1993,11 @@ func (r *ShardingDatabaseReconciler) deployStatefulSet(instance *databasev1alpha return ctrl.Result{}, nil } -func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1.ShardingDatabase) error { +func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev4.ShardingDatabase) error { var i int32 var err error = nil - var OraShardSpex databasev1alpha1.ShardSpec + var OraShardSpex databasev4.ShardSpec var currState string var eventMsg string var msg string @@ -2016,11 +2016,11 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] currState = shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) - if currState == string(databasev1alpha1.AddingShardState) { + if currState == string(databasev4.AddingShardState) { eventMsg = "Shard Addition in progress. Requeuing" err = fmt.Errorf(eventMsg) break - } else if currState == string(databasev1alpha1.DeletingState) { + } else if currState == string(databasev4.DeletingState) { eventMsg = "Shard Deletion in progress. Requeuing" err = fmt.Errorf(eventMsg) err = nil @@ -2029,11 +2029,11 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev1alpha1. eventMsg = "Shard Deletion failed. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) break - } else if currState == string(databasev1alpha1.DeleteErrorState) { + } else if currState == string(databasev4.DeleteErrorState) { eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) break - } else if currState == string(databasev1alpha1.ShardRemoveError) { + } else if currState == string(databasev4.ShardRemoveError) { eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) break diff --git a/main.go b/main.go index e65dde11..ff2c6275 100644 --- a/main.go +++ b/main.go @@ -65,6 +65,7 @@ import ( databasecontroller "github.com/oracle/oracle-database-operator/controllers/database" dataguardcontroller "github.com/oracle/oracle-database-operator/controllers/dataguard" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" observabilitycontroller "github.com/oracle/oracle-database-operator/controllers/observability" // +kubebuilder:scaffold:imports @@ -80,6 +81,7 @@ func init() { utilruntime.Must(observabilityv1alpha1.AddToScheme(scheme)) utilruntime.Must(monitorv1.AddToScheme(scheme)) utilruntime.Must(databasev1alpha1.AddToScheme(scheme)) + utilruntime.Must(databasev4.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -250,7 +252,7 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") os.Exit(1) } - if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") } if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { @@ -305,6 +307,10 @@ func main() { os.Exit(1) } + if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + os.Exit(1) + } // +kubebuilder:scaffold:builder // Add index for PDB CR to enable mgr to cache PDBs From 9ad5779a3cbb837940a8a7c376c5b6f5748b9e41 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Thu, 5 Dec 2024 21:29:38 +0000 Subject: [PATCH 129/414] Tinglwan upgrade v4 --- .../autonomouscontainerdatabase_types.go | 11 + .../autonomouscontainerdatabase_webhook.go | 4 +- .../v1alpha1/autonomousdatabase_conversion.go | 313 +++++++ .../v1alpha1/autonomousdatabase_types.go | 23 + .../v1alpha1/autonomousdatabase_webhook.go | 6 +- .../autonomousdatabasebackup_webhook.go | 9 +- .../autonomousdatabaserestore_webhook.go | 5 +- apis/database/v4/adbfamily_common_utils.go | 328 +++++++ .../v4/autonomouscontainerdatabase_types.go | 226 +++++ .../v4/autonomouscontainerdatabase_webhook.go | 110 +++ apis/database/v4/autonomousdatabase_types.go | 424 +++++++++ .../database/v4/autonomousdatabase_webhook.go | 291 ++++++ .../v4/autonomousdatabasebackup_types.go | 129 +++ .../v4/autonomousdatabasebackup_webhook.go | 158 ++++ .../v4/autonomousdatabaserestore_types.go | 142 +++ .../v4/autonomousdatabaserestore_webhook.go | 146 +++ apis/database/v4/zz_generated.deepcopy.go | 876 ++++++++++++++++++ commons/adb_family/utils.go | 8 +- commons/k8s/create.go | 14 +- commons/k8s/fetch.go | 12 +- commons/oci/containerdatabase.go | 6 +- commons/oci/database.go | 60 +- ...acle.com_autonomouscontainerdatabases.yaml | 70 ++ ....oracle.com_autonomousdatabasebackups.yaml | 91 ++ ...oracle.com_autonomousdatabaserestores.yaml | 86 ++ ...tabase.oracle.com_autonomousdatabases.yaml | 233 +++++ config/crd/kustomization.yaml | 12 +- ...ction_in_autonomouscontainerdatabases.yaml | 2 +- ...njection_in_autonomousdatabasebackups.yaml | 2 +- ...jection_in_autonomousdatabaserestores.yaml | 2 +- ...bhook_in_autonomouscontainerdatabases.yaml | 20 +- .../webhook_in_autonomousdatabasebackups.yaml | 20 +- ...webhook_in_autonomousdatabaserestores.yaml | 20 +- .../webhook_in_autonomousdatabases.yaml | 18 +- config/webhook/manifests.yaml | 222 ++++- .../autonomouscontainerdatabase_controller.go | 88 +- .../database/autonomousdatabase_controller.go | 170 ++-- .../autonomousdatabasebackup_controller.go | 18 +- .../autonomousdatabaserestore_controller.go | 22 +- main.go | 16 + 40 files changed, 4104 insertions(+), 309 deletions(-) create mode 100644 apis/database/v1alpha1/autonomousdatabase_conversion.go create mode 100644 apis/database/v4/adbfamily_common_utils.go create mode 100644 apis/database/v4/autonomouscontainerdatabase_types.go create mode 100644 apis/database/v4/autonomouscontainerdatabase_webhook.go create mode 100644 apis/database/v4/autonomousdatabase_types.go create mode 100644 apis/database/v4/autonomousdatabase_webhook.go create mode 100644 apis/database/v4/autonomousdatabasebackup_types.go create mode 100644 apis/database/v4/autonomousdatabasebackup_webhook.go create mode 100644 apis/database/v4/autonomousdatabaserestore_types.go create mode 100644 apis/database/v4/autonomousdatabaserestore_webhook.go diff --git a/apis/database/v1alpha1/autonomouscontainerdatabase_types.go b/apis/database/v1alpha1/autonomouscontainerdatabase_types.go index abf0ee0d..a1350dda 100644 --- a/apis/database/v1alpha1/autonomouscontainerdatabase_types.go +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_types.go @@ -60,6 +60,17 @@ const ( AcdActionTerminate AcdActionEnum = "TERMINATE" ) +func GetAcdActionEnumFromString(val string) (AcdActionEnum, bool) { + var mappingAcdActionEnum = map[string]AcdActionEnum{ + "RESTART": AcdActionRestart, + "TERMINATE": AcdActionTerminate, + "": AcdActionBlank, + } + + enum, ok := mappingAcdActionEnum[val] + return enum, ok +} + // AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase type AutonomousContainerDatabaseSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster diff --git a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go index 37e1d819..baabda54 100644 --- a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go @@ -58,15 +58,13 @@ func (r *AutonomousContainerDatabase) SetupWebhookWithManager(mgr ctrl.Manager) Complete() } -//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomouscontainerdatabases,versions=v1alpha1,name=vautonomouscontainerdatabase.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomouscontainerdatabases,versions=v1alpha1,name=vautonomouscontainerdatabasev1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &AutonomousContainerDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousContainerDatabase) ValidateCreate() (admission.Warnings, error) { autonomouscontainerdatabaselog.Info("validate create", "name", r.Name) - - // TODO(user): fill in your validation logic upon object creation. return nil, nil } diff --git a/apis/database/v1alpha1/autonomousdatabase_conversion.go b/apis/database/v1alpha1/autonomousdatabase_conversion.go new file mode 100644 index 00000000..9afa8113 --- /dev/null +++ b/apis/database/v1alpha1/autonomousdatabase_conversion.go @@ -0,0 +1,313 @@ +package v1alpha1 + +import ( + "errors" + + v4 "github.com/oracle/oracle-database-operator/apis/database/v4" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +// ConvertTo converts this AutonomousDatabase to the Hub version (v4). +func (src *AutonomousDatabase) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v4.AutonomousDatabase) + // Convert the Spec + dst.Spec.Details.AutonomousDatabaseOCID = src.Spec.Details.AutonomousDatabaseOCID + dst.Spec.Details.CompartmentOCID = src.Spec.Details.CompartmentOCID + dst.Spec.Details.AutonomousContainerDatabase.K8sACD.Name = src.Spec.Details.AutonomousContainerDatabase.K8sACD.Name + dst.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID = src.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID + dst.Spec.Details.DisplayName = src.Spec.Details.DisplayName + dst.Spec.Details.DbName = src.Spec.Details.DbName + dst.Spec.Details.DbWorkload = src.Spec.Details.DbWorkload + dst.Spec.Details.LicenseModel = src.Spec.Details.LicenseModel + dst.Spec.Details.DbVersion = src.Spec.Details.DbVersion + dst.Spec.Details.DataStorageSizeInTBs = src.Spec.Details.DataStorageSizeInTBs + dst.Spec.Details.CPUCoreCount = src.Spec.Details.CPUCoreCount + dst.Spec.Details.AdminPassword.K8sSecret.Name = src.Spec.Details.AdminPassword.K8sSecret.Name + dst.Spec.Details.AdminPassword.OCISecret.OCID = src.Spec.Details.AdminPassword.OCISecret.OCID + dst.Spec.Details.IsAutoScalingEnabled = src.Spec.Details.IsAutoScalingEnabled + dst.Spec.Details.IsDedicated = src.Spec.Details.IsDedicated + dst.Spec.Details.LifecycleState = src.Spec.Details.LifecycleState + + if val, ok := v4.GetNetworkAccessTypeFromString(string(src.Spec.Details.NetworkAccess.AccessType)); !ok { + return errors.New("Unable to convert to NetworkAccessTypeEnum: " + string(src.Spec.Details.NetworkAccess.AccessType)) + } else { + dst.Spec.Details.NetworkAccess.AccessType = val + } + dst.Spec.Details.NetworkAccess.IsAccessControlEnabled = src.Spec.Details.NetworkAccess.IsAccessControlEnabled + dst.Spec.Details.NetworkAccess.AccessControlList = src.Spec.Details.NetworkAccess.AccessControlList + dst.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = src.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID + dst.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = src.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs + dst.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = src.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix + dst.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = src.Spec.Details.NetworkAccess.IsMTLSConnectionRequired + + dst.Spec.Details.FreeformTags = src.Spec.Details.FreeformTags + dst.Spec.Details.Wallet.Name = src.Spec.Details.Wallet.Name + dst.Spec.Details.Wallet.Password.K8sSecret.Name = src.Spec.Details.Wallet.Password.K8sSecret.Name + dst.Spec.Details.Wallet.Password.OCISecret.OCID = src.Spec.Details.Wallet.Password.OCISecret.OCID + + dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName + dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + + dst.Spec.HardLink = src.Spec.HardLink + + // Convert the Status + dst.Status.LifecycleState = src.Status.LifecycleState + dst.Status.TimeCreated = src.Status.TimeCreated + dst.Status.WalletExpiringDate = src.Status.WalletExpiringDate + + // convert status.allConnectionStrings + if src.Status.AllConnectionStrings != nil { + for _, srcProfile := range src.Status.AllConnectionStrings { + dstProfile := v4.ConnectionStringProfile{} + + // convert status.allConnectionStrings[i].tlsAuthentication + if val, ok := v4.GetTLSAuthenticationEnumFromString(string(srcProfile.TLSAuthentication)); !ok { + return errors.New("Unable to convert to TLSAuthenticationEnum: " + string(srcProfile.TLSAuthentication)) + } else { + dstProfile.TLSAuthentication = val + } + + // convert status.allConnectionStrings[i].connectionStrings + dstProfile.ConnectionStrings = make([]v4.ConnectionStringSpec, len(srcProfile.ConnectionStrings)) + for i, v := range srcProfile.ConnectionStrings { + dstProfile.ConnectionStrings[i].TNSName = v.TNSName + dstProfile.ConnectionStrings[i].ConnectionString = v.ConnectionString + } + + dst.Status.AllConnectionStrings = append(dst.Status.AllConnectionStrings, dstProfile) + } + } + + dst.Status.Conditions = src.Status.Conditions + + dst.ObjectMeta = src.ObjectMeta + return nil +} + +// ConvertFrom converts from the Hub version (v4) to v1alpha1 +func (dst *AutonomousDatabase) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v4.AutonomousDatabase) + + // Convert the Spec + dst.Spec.Details.AutonomousDatabaseOCID = src.Spec.Details.AutonomousDatabaseOCID + dst.Spec.Details.CompartmentOCID = src.Spec.Details.CompartmentOCID + dst.Spec.Details.AutonomousContainerDatabase.K8sACD.Name = src.Spec.Details.AutonomousContainerDatabase.K8sACD.Name + dst.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID = src.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID + dst.Spec.Details.DisplayName = src.Spec.Details.DisplayName + dst.Spec.Details.DbName = src.Spec.Details.DbName + dst.Spec.Details.DbWorkload = src.Spec.Details.DbWorkload + dst.Spec.Details.LicenseModel = src.Spec.Details.LicenseModel + dst.Spec.Details.DbVersion = src.Spec.Details.DbVersion + dst.Spec.Details.DataStorageSizeInTBs = src.Spec.Details.DataStorageSizeInTBs + dst.Spec.Details.CPUCoreCount = src.Spec.Details.CPUCoreCount + dst.Spec.Details.AdminPassword.K8sSecret.Name = src.Spec.Details.AdminPassword.K8sSecret.Name + dst.Spec.Details.AdminPassword.OCISecret.OCID = src.Spec.Details.AdminPassword.OCISecret.OCID + dst.Spec.Details.IsAutoScalingEnabled = src.Spec.Details.IsAutoScalingEnabled + dst.Spec.Details.IsDedicated = src.Spec.Details.IsDedicated + dst.Spec.Details.LifecycleState = src.Spec.Details.LifecycleState + + if val, ok := GetNetworkAccessTypeFromString(string(src.Spec.Details.NetworkAccess.AccessType)); !ok { + return errors.New("Unable to convert to NetworkAccessTypeEnum: " + string(src.Spec.Details.NetworkAccess.AccessType)) + } else { + dst.Spec.Details.NetworkAccess.AccessType = val + } + dst.Spec.Details.NetworkAccess.IsAccessControlEnabled = src.Spec.Details.NetworkAccess.IsAccessControlEnabled + dst.Spec.Details.NetworkAccess.AccessControlList = src.Spec.Details.NetworkAccess.AccessControlList + dst.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = src.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID + dst.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = src.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs + dst.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = src.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix + dst.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = src.Spec.Details.NetworkAccess.IsMTLSConnectionRequired + + dst.Spec.Details.FreeformTags = src.Spec.Details.FreeformTags + dst.Spec.Details.Wallet.Name = src.Spec.Details.Wallet.Name + dst.Spec.Details.Wallet.Password.K8sSecret.Name = src.Spec.Details.Wallet.Password.K8sSecret.Name + dst.Spec.Details.Wallet.Password.OCISecret.OCID = src.Spec.Details.Wallet.Password.OCISecret.OCID + + dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName + dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + + dst.Spec.HardLink = src.Spec.HardLink + + // Convert the Status + dst.Status.LifecycleState = src.Status.LifecycleState + dst.Status.TimeCreated = src.Status.TimeCreated + dst.Status.WalletExpiringDate = src.Status.WalletExpiringDate + + // convert status.allConnectionStrings + if src.Status.AllConnectionStrings != nil { + for _, srcProfile := range src.Status.AllConnectionStrings { + dstProfile := ConnectionStringProfile{} + + // convert status.allConnectionStrings[i].tlsAuthentication + if val, ok := GetTLSAuthenticationEnumFromString(string(srcProfile.TLSAuthentication)); !ok { + return errors.New("Unable to convert to TLSAuthenticationEnum: " + string(srcProfile.TLSAuthentication)) + } else { + dstProfile.TLSAuthentication = val + } + + // convert status.allConnectionStrings[i].connectionStrings + dstProfile.ConnectionStrings = make([]ConnectionStringSpec, len(srcProfile.ConnectionStrings)) + for i, v := range srcProfile.ConnectionStrings { + dstProfile.ConnectionStrings[i].TNSName = v.TNSName + dstProfile.ConnectionStrings[i].ConnectionString = v.ConnectionString + } + + dst.Status.AllConnectionStrings = append(dst.Status.AllConnectionStrings, dstProfile) + } + } + + dst.Status.Conditions = src.Status.Conditions + + dst.ObjectMeta = src.ObjectMeta + return nil +} + +func (src *AutonomousDatabaseBackup) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v4.AutonomousDatabaseBackup) + + dst.Spec.Target.K8sADB.Name = src.Spec.Target.K8sADB.Name + dst.Spec.Target.OCIADB.OCID = src.Spec.Target.OCIADB.OCID + dst.Spec.DisplayName = src.Spec.DisplayName + dst.Spec.AutonomousDatabaseBackupOCID = src.Spec.AutonomousDatabaseBackupOCID + dst.Spec.IsLongTermBackup = src.Spec.IsLongTermBackup + dst.Spec.RetentionPeriodInDays = src.Spec.RetentionPeriodInDays + dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName + dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + + dst.Status.LifecycleState = src.Status.LifecycleState + dst.Status.Type = src.Status.Type + dst.Status.IsAutomatic = src.Status.IsAutomatic + dst.Status.TimeStarted = src.Status.TimeStarted + dst.Status.TimeEnded = src.Status.TimeEnded + dst.Status.AutonomousDatabaseOCID = src.Status.AutonomousDatabaseOCID + dst.Status.CompartmentOCID = src.Status.CompartmentOCID + dst.Status.DBName = src.Status.DBName + dst.Status.DBDisplayName = src.Status.DBDisplayName + + dst.ObjectMeta = src.ObjectMeta + return nil +} + +func (dst *AutonomousDatabaseBackup) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v4.AutonomousDatabaseBackup) + + dst.Spec.Target.K8sADB.Name = src.Spec.Target.K8sADB.Name + dst.Spec.Target.OCIADB.OCID = src.Spec.Target.OCIADB.OCID + dst.Spec.DisplayName = src.Spec.DisplayName + dst.Spec.AutonomousDatabaseBackupOCID = src.Spec.AutonomousDatabaseBackupOCID + dst.Spec.IsLongTermBackup = src.Spec.IsLongTermBackup + dst.Spec.RetentionPeriodInDays = src.Spec.RetentionPeriodInDays + dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName + dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + + dst.Status.LifecycleState = src.Status.LifecycleState + dst.Status.Type = src.Status.Type + dst.Status.IsAutomatic = src.Status.IsAutomatic + dst.Status.TimeStarted = src.Status.TimeStarted + dst.Status.TimeEnded = src.Status.TimeEnded + dst.Status.AutonomousDatabaseOCID = src.Status.AutonomousDatabaseOCID + dst.Status.CompartmentOCID = src.Status.CompartmentOCID + dst.Status.DBName = src.Status.DBName + dst.Status.DBDisplayName = src.Status.DBDisplayName + + dst.ObjectMeta = src.ObjectMeta + return nil +} + +func (src *AutonomousDatabaseRestore) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v4.AutonomousDatabaseRestore) + + dst.Spec.Target.K8sADB.Name = src.Spec.Target.K8sADB.Name + dst.Spec.Target.OCIADB.OCID = src.Spec.Target.OCIADB.OCID + dst.Spec.Source.K8sADBBackup.Name = src.Spec.Source.K8sADBBackup.Name + dst.Spec.Source.PointInTime.Timestamp = src.Spec.Source.PointInTime.Timestamp + dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName + dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + + dst.Status.DisplayName = src.Status.DisplayName + dst.Status.TimeAccepted = src.Status.TimeAccepted + dst.Status.TimeStarted = src.Status.TimeStarted + dst.Status.TimeEnded = src.Status.TimeEnded + dst.Status.DbName = src.Status.DbName + dst.Status.WorkRequestOCID = src.Status.WorkRequestOCID + dst.Status.Status = src.Status.Status + + dst.ObjectMeta = src.ObjectMeta + return nil +} + +func (dst *AutonomousDatabaseRestore) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v4.AutonomousDatabaseRestore) + + dst.Spec.Target.K8sADB.Name = src.Spec.Target.K8sADB.Name + dst.Spec.Target.OCIADB.OCID = src.Spec.Target.OCIADB.OCID + dst.Spec.Source.K8sADBBackup.Name = src.Spec.Source.K8sADBBackup.Name + dst.Spec.Source.PointInTime.Timestamp = src.Spec.Source.PointInTime.Timestamp + dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName + dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + + dst.Status.DisplayName = src.Status.DisplayName + dst.Status.TimeAccepted = src.Status.TimeAccepted + dst.Status.TimeStarted = src.Status.TimeStarted + dst.Status.TimeEnded = src.Status.TimeEnded + dst.Status.DbName = src.Status.DbName + dst.Status.WorkRequestOCID = src.Status.WorkRequestOCID + dst.Status.Status = src.Status.Status + + dst.ObjectMeta = src.ObjectMeta + return nil +} + +func (src *AutonomousContainerDatabase) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v4.AutonomousContainerDatabase) + + dst.Spec.AutonomousContainerDatabaseOCID = src.Spec.AutonomousContainerDatabaseOCID + dst.Spec.CompartmentOCID = src.Spec.CompartmentOCID + dst.Spec.DisplayName = src.Spec.DisplayName + dst.Spec.AutonomousExadataVMClusterOCID = src.Spec.AutonomousExadataVMClusterOCID + dst.Spec.PatchModel = src.Spec.PatchModel + + if val, ok := v4.GetAcdActionEnumFromString(string(src.Spec.Action)); !ok { + return errors.New("Unable to convert to AcdActionEnum: " + string(src.Spec.Action)) + } else { + dst.Spec.Action = val + } + + dst.Spec.FreeformTags = src.Spec.FreeformTags + dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName + dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + dst.Spec.HardLink = src.Spec.HardLink + + dst.Status.LifecycleState = src.Status.LifecycleState + dst.Status.TimeCreated = src.Status.TimeCreated + + dst.ObjectMeta = src.ObjectMeta + return nil +} + +func (dst *AutonomousContainerDatabase) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v4.AutonomousContainerDatabase) + + dst.Spec.AutonomousContainerDatabaseOCID = src.Spec.AutonomousContainerDatabaseOCID + dst.Spec.CompartmentOCID = src.Spec.CompartmentOCID + dst.Spec.DisplayName = src.Spec.DisplayName + dst.Spec.AutonomousExadataVMClusterOCID = src.Spec.AutonomousExadataVMClusterOCID + dst.Spec.PatchModel = src.Spec.PatchModel + + if val, ok := GetAcdActionEnumFromString(string(src.Spec.Action)); !ok { + return errors.New("Unable to convert to AcdActionEnum: " + string(src.Spec.Action)) + } else { + dst.Spec.Action = val + } + + dst.Spec.FreeformTags = src.Spec.FreeformTags + dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName + dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + dst.Spec.HardLink = src.Spec.HardLink + + dst.Status.LifecycleState = src.Status.LifecycleState + dst.Status.TimeCreated = src.Status.TimeCreated + + dst.ObjectMeta = src.ObjectMeta + return nil +} diff --git a/apis/database/v1alpha1/autonomousdatabase_types.go b/apis/database/v1alpha1/autonomousdatabase_types.go index cd23b3f3..34abe59e 100644 --- a/apis/database/v1alpha1/autonomousdatabase_types.go +++ b/apis/database/v1alpha1/autonomousdatabase_types.go @@ -110,8 +110,21 @@ const ( NetworkAccessTypePublic NetworkAccessTypeEnum = "PUBLIC" NetworkAccessTypeRestricted NetworkAccessTypeEnum = "RESTRICTED" NetworkAccessTypePrivate NetworkAccessTypeEnum = "PRIVATE" + NetworkAccessTypeEmpty NetworkAccessTypeEnum = "" ) +func GetNetworkAccessTypeFromString(val string) (NetworkAccessTypeEnum, bool) { + var mappingNetworkAccessTypeEnum = map[string]NetworkAccessTypeEnum{ + "PUBLIC": NetworkAccessTypePublic, + "RESTRICTED": NetworkAccessTypeRestricted, + "PRIVATE": NetworkAccessTypePrivate, + "": NetworkAccessTypeEmpty, + } + + enum, ok := mappingNetworkAccessTypeEnum[val] + return enum, ok +} + type NetworkAccessSpec struct { // +kubebuilder:validation:Enum:="";"PUBLIC";"RESTRICTED";"PRIVATE" AccessType NetworkAccessTypeEnum `json:"accessType,omitempty"` @@ -175,6 +188,16 @@ const ( tlsAuthenticationMTLS TLSAuthenticationEnum = "Mutual TLS" ) +func GetTLSAuthenticationEnumFromString(val string) (TLSAuthenticationEnum, bool) { + var mappingTLSAuthenticationEnum = map[string]TLSAuthenticationEnum{ + "TLS": tlsAuthenticationTLS, + "Mutual TLS": tlsAuthenticationMTLS, + } + + enum, ok := mappingTLSAuthenticationEnum[val] + return enum, ok +} + type ConnectionStringProfile struct { TLSAuthentication TLSAuthenticationEnum `json:"tlsAuthentication,omitempty"` ConnectionStrings []ConnectionStringSpec `json:"connectionStrings"` diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go index b25e8104..27a0df29 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -63,7 +63,7 @@ func (r *AutonomousDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -//+kubebuilder:webhook:verbs=create;update,path=/mutate-database-oracle-com-v1alpha1-autonomousdatabase,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v1alpha1,name=mautonomousdatabase.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:verbs=create;update,path=/mutate-database-oracle-com-v1alpha1-autonomousdatabase,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v1alpha1,name=mautonomousdatabasev1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Defaulter = &AutonomousDatabase{} @@ -93,7 +93,7 @@ func (r *AutonomousDatabase) Default() { } -//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v1alpha1,name=vautonomousdatabase.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v1alpha1,name=vautonomousdatabasev1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &AutonomousDatabase{} @@ -280,8 +280,6 @@ func validateNetworkAccess(adb *AutonomousDatabase, allErrs field.ErrorList) fie // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabase) ValidateDelete() (admission.Warnings, error) { autonomousdatabaselog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. return nil, nil } diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go index 99bf3815..e48c9d3c 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go @@ -59,21 +59,16 @@ func (r *AutonomousDatabaseBackup) SetupWebhookWithManager(mgr ctrl.Manager) err Complete() } -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,verbs=create;update,versions=v1alpha1,name=mautonomousdatabasebackup.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,verbs=create;update,versions=v1alpha1,name=mautonomousdatabasebackupv1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Defaulter = &AutonomousDatabaseBackup{} // Default implements webhook.Defaulter so a webhook will be registered for the type func (r *AutonomousDatabaseBackup) Default() { autonomousdatabasebackuplog.Info("default", "name", r.Name) - - // TODO(user): fill in your defaulting logic. } -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabasebackup,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,versions=v1alpha1,name=vautonomousdatabasebackup.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabasebackup,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,versions=v1alpha1,name=vautonomousdatabasebackupv1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &AutonomousDatabaseBackup{} diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go index 4f96bd7b..2784d8dc 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go @@ -59,10 +59,7 @@ func (r *AutonomousDatabaseRestore) SetupWebhookWithManager(mgr ctrl.Manager) er Complete() } -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabaserestore,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabaserestores,versions=v1alpha1,name=vautonomousdatabaserestore.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabaserestore,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabaserestores,versions=v1alpha1,name=vautonomousdatabaserestorev1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &AutonomousDatabaseRestore{} diff --git a/apis/database/v4/adbfamily_common_utils.go b/apis/database/v4/adbfamily_common_utils.go new file mode 100644 index 00000000..da432662 --- /dev/null +++ b/apis/database/v4/adbfamily_common_utils.go @@ -0,0 +1,328 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "errors" + "reflect" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + "github.com/oracle/oci-go-sdk/v65/workrequests" +) + +// LastSuccessfulSpec is an annotation key which maps to the value of last successful spec +const LastSuccessfulSpec string = "lastSuccessfulSpec" + +// File the meta condition and return the meta view +func CreateMetaCondition(obj client.Object, err error, lifecycleState string, stateMsg string) metav1.Condition { + + return metav1.Condition{ + Type: lifecycleState, + LastTransitionTime: metav1.Now(), + ObservedGeneration: obj.GetGeneration(), + Reason: stateMsg, + Message: err.Error(), + Status: metav1.ConditionTrue, + } +} + +/************************ +* OCI config +************************/ +type OCIConfigSpec struct { + ConfigMapName *string `json:"configMapName,omitempty"` + SecretName *string `json:"secretName,omitempty"` +} + +/************************ +* ADB spec +************************/ +type K8sADBSpec struct { + Name *string `json:"name,omitempty"` +} + +type OCIADBSpec struct { + OCID *string `json:"ocid,omitempty"` +} + +// TargetSpec defines the spec of the target for backup/restore runs. +type TargetSpec struct { + K8sADB K8sADBSpec `json:"k8sADB,omitempty"` + OCIADB OCIADBSpec `json:"ociADB,omitempty"` +} + +/************************** +* Remove Unchanged Fields +**************************/ + +// removeUnchangedFields removes the unchanged fields in the struct and returns if the struct is changed. +// lastSpec should be a derefereced struct that is the last successful spec, e.g. AutonomousDatabaseSpec. +// curSpec should be a pointer pointing to the struct that is being proccessed, e.g., *AutonomousDatabaseSpec. +func removeUnchangedFields(lastSpec interface{}, curSpec interface{}) (bool, error) { + if reflect.ValueOf(lastSpec).Kind() != reflect.Struct { + return false, errors.New("lastSpec should be a struct") + } + + if reflect.ValueOf(curSpec).Kind() != reflect.Ptr || reflect.ValueOf(curSpec).Elem().Kind() != reflect.Struct { + return false, errors.New("curSpec should be a struct pointer") + } + + if reflect.ValueOf(lastSpec).Type() != reflect.ValueOf(curSpec).Elem().Type() { + return false, errors.New("the referenced type of curSpec should be the same as the type of lastSpec") + } + + return traverse(lastSpec, curSpec), nil +} + +// Traverse and compare each fields in the lastSpec and the the curSpec. +// If unchanged, set the field in curSpec to a zero value. +// lastSpec should be a derefereced struct that is the last successful spec, e.g. AutonomousDatabaseSpec. +// curSpec should be a pointer pointing to the struct that is being proccessed, e.g., *AutonomousDatabaseSpec. +func traverse(lastSpec interface{}, curSpec interface{}) bool { + var changed bool = false + + fields := reflect.VisibleFields(reflect.TypeOf(lastSpec)) + + lastSpecValue := reflect.ValueOf(lastSpec) + curSpecValue := reflect.ValueOf(curSpec).Elem() // deref the struct + + for _, field := range fields { + lastField := lastSpecValue.FieldByName(field.Name) + curField := curSpecValue.FieldByName(field.Name) + + // call traverse() if the current field is a struct + if field.Type.Kind() == reflect.Struct { + childrenChanged := traverse(lastField.Interface(), curField.Addr().Interface()) + if childrenChanged && !changed { + changed = true + } + } else { + fieldChanged := hasChanged(lastField, curField) + + // if fieldChanged { + // if curField.Kind() == reflect.Ptr { + // fmt.Printf("== field %s changed\n", field.Name) + // if lastField.IsZero() { + // fmt.Printf("=== lastField is nil\n") + // } else { + // fmt.Printf("=== lastField = %v\n", lastField.Elem().Interface()) + // } + // if curField.IsZero() { + // fmt.Printf("===== curField is nil\n") + // } else { + // fmt.Printf("===== curField = %v\n", curField.Elem().Interface()) + // } + // } else { + // fmt.Printf("=== lastField = %v\n", lastField.Interface()) + // fmt.Printf("===== curField = %v\n", curField.Interface()) + // } + // } + + if fieldChanged && !changed { + changed = true + } + + // Set the field to zero value if unchanged + if !fieldChanged { + curField.Set(reflect.Zero(curField.Type())) + } + } + } + + return changed +} + +// 1. If the current field is with a zero value, then the field is unchanged. +// 2. If the current field is NOT with a zero value, then we want to comapre it with the last field. +// In this case if the last field is with a zero value, then the field is changed +func hasChanged(lastField reflect.Value, curField reflect.Value) bool { + zero := reflect.Zero(lastField.Type()).Interface() + lastFieldIsZero := reflect.DeepEqual(lastField.Interface(), zero) + curFieldIsZero := reflect.DeepEqual(curField.Interface(), zero) + + if curFieldIsZero { + return false + } else if !lastFieldIsZero { + var lastIntrf interface{} + var curIntrf interface{} + + if curField.Kind() == reflect.Ptr { + lastIntrf = lastField.Elem().Interface() + curIntrf = curField.Elem().Interface() + } else { + lastIntrf = lastField.Interface() + curIntrf = curField.Interface() + } + + return !reflect.DeepEqual(lastIntrf, curIntrf) + } + + return true +} + +/************************ +* SDKTime format +************************/ + +// Follow the format of the display time +const displayFormat = "2006-01-02 15:04:05 MST" + +func FormatSDKTime(sdkTime *common.SDKTime) string { + if sdkTime == nil { + return "" + } + + time := sdkTime.Time + return time.Format(displayFormat) +} + +func parseDisplayTime(val string) (*common.SDKTime, error) { + parsedTime, err := time.Parse(displayFormat, val) + if err != nil { + return nil, err + } + sdkTime := common.SDKTime{Time: parsedTime} + return &sdkTime, nil +} + +/************************ +* LifecycleState check +************************/ +func IsADBIntermediateState(state database.AutonomousDatabaseLifecycleStateEnum) bool { + if state == database.AutonomousDatabaseLifecycleStateProvisioning || + state == database.AutonomousDatabaseLifecycleStateUpdating || + state == database.AutonomousDatabaseLifecycleStateScaleInProgress || + state == database.AutonomousDatabaseLifecycleStateStarting || + state == database.AutonomousDatabaseLifecycleStateStopping || + state == database.AutonomousDatabaseLifecycleStateTerminating || + state == database.AutonomousDatabaseLifecycleStateRestoreInProgress || + state == database.AutonomousDatabaseLifecycleStateBackupInProgress || + state == database.AutonomousDatabaseLifecycleStateMaintenanceInProgress || + state == database.AutonomousDatabaseLifecycleStateRestarting || + state == database.AutonomousDatabaseLifecycleStateRecreating || + state == database.AutonomousDatabaseLifecycleStateRoleChangeInProgress || + state == database.AutonomousDatabaseLifecycleStateUpgrading { + return true + } + return false +} + +func ValidADBTerminateState(state database.AutonomousDatabaseLifecycleStateEnum) bool { + if state == database.AutonomousDatabaseLifecycleStateProvisioning || + state == database.AutonomousDatabaseLifecycleStateAvailable || + state == database.AutonomousDatabaseLifecycleStateStopped || + state == database.AutonomousDatabaseLifecycleStateUnavailable || + state == database.AutonomousDatabaseLifecycleStateRestoreInProgress || + state == database.AutonomousDatabaseLifecycleStateRestoreFailed || + state == database.AutonomousDatabaseLifecycleStateBackupInProgress || + state == database.AutonomousDatabaseLifecycleStateScaleInProgress || + state == database.AutonomousDatabaseLifecycleStateAvailableNeedsAttention || + state == database.AutonomousDatabaseLifecycleStateUpdating || + state == database.AutonomousDatabaseLifecycleStateMaintenanceInProgress || + state == database.AutonomousDatabaseLifecycleStateRoleChangeInProgress || + state == database.AutonomousDatabaseLifecycleStateUpgrading { + return true + } + return false +} + +// NextADBStableState returns the next stable state if it's an intermediate state. +// Otherwise returns the same state. +func NextADBStableState(state database.AutonomousDatabaseLifecycleStateEnum) database.AutonomousDatabaseLifecycleStateEnum { + if state == database.AutonomousDatabaseLifecycleStateProvisioning || + state == database.AutonomousDatabaseLifecycleStateStarting || + state == database.AutonomousDatabaseLifecycleStateRestoreInProgress || + state == database.AutonomousDatabaseLifecycleStateBackupInProgress || + state == database.AutonomousDatabaseLifecycleStateScaleInProgress || + state == database.AutonomousDatabaseLifecycleStateUpdating || + state == database.AutonomousDatabaseLifecycleStateMaintenanceInProgress || + state == database.AutonomousDatabaseLifecycleStateRestarting || + state == database.AutonomousDatabaseLifecycleStateRecreating || + state == database.AutonomousDatabaseLifecycleStateRoleChangeInProgress || + state == database.AutonomousDatabaseLifecycleStateUpgrading { + + return database.AutonomousDatabaseLifecycleStateAvailable + } + + if state == database.AutonomousDatabaseLifecycleStateStopping { + return database.AutonomousDatabaseLifecycleStateStopped + } + + if state == database.AutonomousDatabaseLifecycleStateTerminating { + return database.AutonomousDatabaseLifecycleStateTerminated + } + + return state +} + +func IsBackupIntermediateState(state database.AutonomousDatabaseBackupLifecycleStateEnum) bool { + if state == database.AutonomousDatabaseBackupLifecycleStateCreating || + state == database.AutonomousDatabaseBackupLifecycleStateDeleting { + return true + } + return false +} + +func IsRestoreIntermediateState(state workrequests.WorkRequestStatusEnum) bool { + if state == workrequests.WorkRequestStatusAccepted || + state == workrequests.WorkRequestStatusInProgress || + state == workrequests.WorkRequestStatusCanceling { + return true + } + return false +} + +func IsACDIntermediateState(state database.AutonomousContainerDatabaseLifecycleStateEnum) bool { + if state == database.AutonomousContainerDatabaseLifecycleStateProvisioning || + state == database.AutonomousContainerDatabaseLifecycleStateUpdating || + state == database.AutonomousContainerDatabaseLifecycleStateTerminating || + state == database.AutonomousContainerDatabaseLifecycleStateBackupInProgress || + state == database.AutonomousContainerDatabaseLifecycleStateRestoring || + state == database.AutonomousContainerDatabaseLifecycleStateRestarting || + state == database.AutonomousContainerDatabaseLifecycleStateMaintenanceInProgress || + state == database.AutonomousContainerDatabaseLifecycleStateRoleChangeInProgress { + return true + } + return false +} diff --git a/apis/database/v4/autonomouscontainerdatabase_types.go b/apis/database/v4/autonomouscontainerdatabase_types.go new file mode 100644 index 00000000..d6a7e161 --- /dev/null +++ b/apis/database/v4/autonomouscontainerdatabase_types.go @@ -0,0 +1,226 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "encoding/json" + "reflect" + + "github.com/oracle/oci-go-sdk/v65/database" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// name of our custom finalizer +const ACDFinalizer = "database.oracle.com/acd-finalizer" + +type AcdActionEnum string + +const ( + AcdActionBlank AcdActionEnum = "" + AcdActionRestart AcdActionEnum = "RESTART" + AcdActionTerminate AcdActionEnum = "TERMINATE" +) + +func GetAcdActionEnumFromString(val string) (AcdActionEnum, bool) { + var mappingAcdActionEnum = map[string]AcdActionEnum{ + "RESTART": AcdActionRestart, + "TERMINATE": AcdActionTerminate, + "": AcdActionBlank, + } + + enum, ok := mappingAcdActionEnum[val] + return enum, ok +} + +// AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase +type AutonomousContainerDatabaseSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + AutonomousContainerDatabaseOCID *string `json:"autonomousContainerDatabaseOCID,omitempty"` + CompartmentOCID *string `json:"compartmentOCID,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + AutonomousExadataVMClusterOCID *string `json:"autonomousExadataVMClusterOCID,omitempty"` + // +kubebuilder:validation:Enum:="RELEASE_UPDATES";"RELEASE_UPDATE_REVISIONS" + PatchModel database.AutonomousContainerDatabasePatchModelEnum `json:"patchModel,omitempty"` + // +kubebuilder:validation:Enum:="SYNC";"RESTART";"TERMINATE" + Action AcdActionEnum `json:"action,omitempty"` + FreeformTags map[string]string `json:"freeformTags,omitempty"` + + OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + // +kubebuilder:default:=false + HardLink *bool `json:"hardLink,omitempty"` +} + +// AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase +type AutonomousContainerDatabaseStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + LifecycleState database.AutonomousContainerDatabaseLifecycleStateEnum `json:"lifecycleState"` + TimeCreated string `json:"timeCreated,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +// +kubebuilder:resource:shortName="acd";"acds" +// +kubebuilder:printcolumn:JSONPath=".spec.displayName",name="DisplayName",type=string +// +kubebuilder:printcolumn:JSONPath=".status.lifecycleState",name="State",type=string +// +kubebuilder:printcolumn:JSONPath=".status.timeCreated",name="Created",type=string +// +kubebuilder:storageversion + +// AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API +type AutonomousContainerDatabase struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AutonomousContainerDatabaseSpec `json:"spec,omitempty"` + Status AutonomousContainerDatabaseStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// AutonomousContainerDatabaseList contains a list of AutonomousContainerDatabase +type AutonomousContainerDatabaseList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []AutonomousContainerDatabase `json:"items"` +} + +func init() { + SchemeBuilder.Register(&AutonomousContainerDatabase{}, &AutonomousContainerDatabaseList{}) +} + +// Implement conversion.Hub interface, which means any resource version can convert into v4 +func (*AutonomousContainerDatabase) Hub() {} + +// GetLastSuccessfulSpec returns spec from the lass successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulSpec. +func (acd *AutonomousContainerDatabase) GetLastSuccessfulSpec() (*AutonomousContainerDatabaseSpec, error) { + val, ok := acd.GetAnnotations()[LastSuccessfulSpec] + if !ok { + return nil, nil + } + + specBytes := []byte(val) + sucSpec := AutonomousContainerDatabaseSpec{} + + err := json.Unmarshal(specBytes, &sucSpec) + if err != nil { + return nil, err + } + + return &sucSpec, nil +} + +func (acd *AutonomousContainerDatabase) UpdateLastSuccessfulSpec() error { + specBytes, err := json.Marshal(acd.Spec) + if err != nil { + return err + } + + anns := acd.GetAnnotations() + + if anns == nil { + anns = map[string]string{ + LastSuccessfulSpec: string(specBytes), + } + } else { + anns[LastSuccessfulSpec] = string(specBytes) + } + + acd.SetAnnotations(anns) + + return nil +} + +// UpdateStatusFromOCIACD updates the status subresource +func (acd *AutonomousContainerDatabase) UpdateStatusFromOCIACD(ociObj database.AutonomousContainerDatabase) { + acd.Status.LifecycleState = ociObj.LifecycleState + acd.Status.TimeCreated = FormatSDKTime(ociObj.TimeCreated) +} + +// UpdateFromOCIADB updates the attributes using database.AutonomousContainerDatabase object +func (acd *AutonomousContainerDatabase) UpdateFromOCIACD(ociObj database.AutonomousContainerDatabase) (specChanged bool) { + oldACD := acd.DeepCopy() + + /*********************************** + * update the spec + ***********************************/ + acd.Spec.Action = AcdActionBlank + acd.Spec.AutonomousContainerDatabaseOCID = ociObj.Id + acd.Spec.CompartmentOCID = ociObj.CompartmentId + acd.Spec.DisplayName = ociObj.DisplayName + acd.Spec.AutonomousExadataVMClusterOCID = ociObj.CloudAutonomousVmClusterId + acd.Spec.PatchModel = ociObj.PatchModel + + // special case: an emtpy map will be nil after unmarshalling while the OCI always returns an emty map. + if len(ociObj.FreeformTags) != 0 { + acd.Spec.FreeformTags = ociObj.FreeformTags + } else { + acd.Spec.FreeformTags = nil + } + + /*********************************** + * update the status subresource + ***********************************/ + acd.UpdateStatusFromOCIACD(ociObj) + + return !reflect.DeepEqual(oldACD.Spec, acd.Spec) +} + +// RemoveUnchangedSpec removes the unchanged fields in spec, and returns if the spec has been changed. +func (acd *AutonomousContainerDatabase) RemoveUnchangedSpec(prevSpec AutonomousContainerDatabaseSpec) (bool, error) { + changed, err := removeUnchangedFields(prevSpec, &acd.Spec) + if err != nil { + return changed, err + } + + return changed, nil +} + +// A helper function which is useful for debugging. The function prints out a structural JSON format. +func (acd *AutonomousContainerDatabase) String() (string, error) { + out, err := json.MarshalIndent(acd, "", " ") + if err != nil { + return "", err + } + return string(out), nil +} diff --git a/apis/database/v4/autonomouscontainerdatabase_webhook.go b/apis/database/v4/autonomouscontainerdatabase_webhook.go new file mode 100644 index 00000000..f18df608 --- /dev/null +++ b/apis/database/v4/autonomouscontainerdatabase_webhook.go @@ -0,0 +1,110 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var autonomouscontainerdatabaselog = logf.Log.WithName("autonomouscontainerdatabase-resource") + +func (r *AutonomousContainerDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomouscontainerdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomouscontainerdatabases,versions=v4,name=vautonomouscontainerdatabasev4.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &AutonomousContainerDatabase{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousContainerDatabase) ValidateCreate() (admission.Warnings, error) { + autonomouscontainerdatabaselog.Info("validate create", "name", r.Name) + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + var allErrs field.ErrorList + var oldACD *AutonomousContainerDatabase = old.(*AutonomousContainerDatabase) + + autonomouscontainerdatabaselog.Info("validate update", "name", r.Name) + + // skip the update of adding ADB OCID or binding + if oldACD.Status.LifecycleState == "" { + return nil, nil + } + + // cannot update when the old state is in intermediate state, except for the terminate operatrion + var copiedSpec *AutonomousContainerDatabaseSpec = r.Spec.DeepCopy() + changed, err := removeUnchangedFields(oldACD.Spec, copiedSpec) + if err != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec"), err.Error())) + } + if IsACDIntermediateState(oldACD.Status.LifecycleState) && changed { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec"), + "cannot change the spec when the lifecycleState is in an intermdeiate state")) + } + + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousContainerDatabase"}, + r.Name, allErrs) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousContainerDatabase) ValidateDelete() (admission.Warnings, error) { + autonomouscontainerdatabaselog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} diff --git a/apis/database/v4/autonomousdatabase_types.go b/apis/database/v4/autonomousdatabase_types.go new file mode 100644 index 00000000..d0899b0b --- /dev/null +++ b/apis/database/v4/autonomousdatabase_types.go @@ -0,0 +1,424 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "encoding/json" + "reflect" + + "github.com/oracle/oci-go-sdk/v65/database" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// name of our custom finalizer +const ADB_FINALIZER = "database.oracle.com/adb-finalizer" + +// AutonomousDatabaseSpec defines the desired state of AutonomousDatabase +// Important: Run "make" to regenerate code after modifying this file +type AutonomousDatabaseSpec struct { + Details AutonomousDatabaseDetails `json:"details"` + OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + // +kubebuilder:default:=false + HardLink *bool `json:"hardLink,omitempty"` +} + +/************************ +* ACD specs +************************/ +type K8sACDSpec struct { + Name *string `json:"name,omitempty"` +} + +type OCIACDSpec struct { + OCID *string `json:"ocid,omitempty"` +} + +// ACDSpec defines the spec of the target for backup/restore runs. +// The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup +type ACDSpec struct { + K8sACD K8sACDSpec `json:"k8sACD,omitempty"` + OCIACD OCIACDSpec `json:"ociACD,omitempty"` +} + +/************************ +* Secret specs +************************/ +type K8sSecretSpec struct { + Name *string `json:"name,omitempty"` +} + +type OCISecretSpec struct { + OCID *string `json:"ocid,omitempty"` +} + +type PasswordSpec struct { + K8sSecret K8sSecretSpec `json:"k8sSecret,omitempty"` + OCISecret OCISecretSpec `json:"ociSecret,omitempty"` +} + +type WalletSpec struct { + Name *string `json:"name,omitempty"` + Password PasswordSpec `json:"password,omitempty"` +} + +/************************ +* Network Access specs +************************/ + +type NetworkAccessTypeEnum string + +const ( + NetworkAccessTypePublic NetworkAccessTypeEnum = "PUBLIC" + NetworkAccessTypeRestricted NetworkAccessTypeEnum = "RESTRICTED" + NetworkAccessTypePrivate NetworkAccessTypeEnum = "PRIVATE" + NetworkAccessTypeEmpty NetworkAccessTypeEnum = "" +) + +func GetNetworkAccessTypeFromString(val string) (NetworkAccessTypeEnum, bool) { + var mappingNetworkAccessTypeEnum = map[string]NetworkAccessTypeEnum{ + "PUBLIC": NetworkAccessTypePublic, + "RESTRICTED": NetworkAccessTypeRestricted, + "PRIVATE": NetworkAccessTypePrivate, + "": NetworkAccessTypeEmpty, + } + + enum, ok := mappingNetworkAccessTypeEnum[val] + return enum, ok +} + +type NetworkAccessSpec struct { + // +kubebuilder:validation:Enum:="";"PUBLIC";"RESTRICTED";"PRIVATE" + AccessType NetworkAccessTypeEnum `json:"accessType,omitempty"` + IsAccessControlEnabled *bool `json:"isAccessControlEnabled,omitempty"` + AccessControlList []string `json:"accessControlList,omitempty"` + PrivateEndpoint PrivateEndpointSpec `json:"privateEndpoint,omitempty"` + IsMTLSConnectionRequired *bool `json:"isMTLSConnectionRequired,omitempty"` +} + +type PrivateEndpointSpec struct { + SubnetOCID *string `json:"subnetOCID,omitempty"` + NsgOCIDs []string `json:"nsgOCIDs,omitempty"` + HostnamePrefix *string `json:"hostnamePrefix,omitempty"` +} + +// AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase +type AutonomousDatabaseDetails struct { + AutonomousDatabaseOCID *string `json:"autonomousDatabaseOCID,omitempty"` + CompartmentOCID *string `json:"compartmentOCID,omitempty"` + AutonomousContainerDatabase ACDSpec `json:"autonomousContainerDatabase,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + DbName *string `json:"dbName,omitempty"` + // +kubebuilder:validation:Enum:="OLTP";"DW";"AJD";"APEX" + DbWorkload database.AutonomousDatabaseDbWorkloadEnum `json:"dbWorkload,omitempty"` + // +kubebuilder:validation:Enum:="LICENSE_INCLUDED";"BRING_YOUR_OWN_LICENSE" + LicenseModel database.AutonomousDatabaseLicenseModelEnum `json:"licenseModel,omitempty"` + DbVersion *string `json:"dbVersion,omitempty"` + DataStorageSizeInTBs *int `json:"dataStorageSizeInTBs,omitempty"` + CPUCoreCount *int `json:"cpuCoreCount,omitempty"` + AdminPassword PasswordSpec `json:"adminPassword,omitempty"` + IsAutoScalingEnabled *bool `json:"isAutoScalingEnabled,omitempty"` + IsDedicated *bool `json:"isDedicated,omitempty"` + LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` + + NetworkAccess NetworkAccessSpec `json:"networkAccess,omitempty"` + + FreeformTags map[string]string `json:"freeformTags,omitempty"` + + Wallet WalletSpec `json:"wallet,omitempty"` +} + +// AutonomousDatabaseStatus defines the observed state of AutonomousDatabase +type AutonomousDatabaseStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` + TimeCreated string `json:"timeCreated,omitempty"` + WalletExpiringDate string `json:"walletExpiringDate,omitempty"` + AllConnectionStrings []ConnectionStringProfile `json:"allConnectionStrings,omitempty"` + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +type TLSAuthenticationEnum string + +const ( + tlsAuthenticationTLS TLSAuthenticationEnum = "TLS" + tlsAuthenticationMTLS TLSAuthenticationEnum = "Mutual TLS" +) + +func GetTLSAuthenticationEnumFromString(val string) (TLSAuthenticationEnum, bool) { + var mappingTLSAuthenticationEnum = map[string]TLSAuthenticationEnum{ + "TLS": tlsAuthenticationTLS, + "Mutual TLS": tlsAuthenticationMTLS, + } + + enum, ok := mappingTLSAuthenticationEnum[val] + return enum, ok +} + +type ConnectionStringProfile struct { + TLSAuthentication TLSAuthenticationEnum `json:"tlsAuthentication,omitempty"` + ConnectionStrings []ConnectionStringSpec `json:"connectionStrings"` +} + +type ConnectionStringSpec struct { + TNSName string `json:"tnsName,omitempty"` + ConnectionString string `json:"connectionString,omitempty"` +} + +// AutonomousDatabase is the Schema for the autonomousdatabases API +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName="adb";"adbs" +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=".spec.details.displayName",name="Display Name",type=string +// +kubebuilder:printcolumn:JSONPath=".spec.details.dbName",name="Db Name",type=string +// +kubebuilder:printcolumn:JSONPath=".status.lifecycleState",name="State",type=string +// +kubebuilder:printcolumn:JSONPath=".spec.details.isDedicated",name="Dedicated",type=string +// +kubebuilder:printcolumn:JSONPath=".spec.details.cpuCoreCount",name="OCPUs",type=integer +// +kubebuilder:printcolumn:JSONPath=".spec.details.dataStorageSizeInTBs",name="Storage (TB)",type=integer +// +kubebuilder:printcolumn:JSONPath=".spec.details.dbWorkload",name="Workload Type",type=string +// +kubebuilder:printcolumn:JSONPath=".status.timeCreated",name="Created",type=string +// +kubebuilder:storageversion +type AutonomousDatabase struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AutonomousDatabaseSpec `json:"spec,omitempty"` + Status AutonomousDatabaseStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// AutonomousDatabaseList contains a list of AutonomousDatabase +type AutonomousDatabaseList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []AutonomousDatabase `json:"items"` +} + +func init() { + SchemeBuilder.Register(&AutonomousDatabase{}, &AutonomousDatabaseList{}) +} + +// Implement conversion.Hub interface, which means any resource version can convert into v4 +func (*AutonomousDatabase) Hub() {} + +// GetLastSuccessfulSpec returns spec from the lass successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulSpec. +func (adb *AutonomousDatabase) GetLastSuccessfulSpec() (*AutonomousDatabaseSpec, error) { + val, ok := adb.GetAnnotations()[LastSuccessfulSpec] + if !ok { + return nil, nil + } + + specBytes := []byte(val) + sucSpec := AutonomousDatabaseSpec{} + + err := json.Unmarshal(specBytes, &sucSpec) + if err != nil { + return nil, err + } + + return &sucSpec, nil +} + +func (adb *AutonomousDatabase) UpdateLastSuccessfulSpec() error { + specBytes, err := json.Marshal(adb.Spec) + if err != nil { + return err + } + + anns := adb.GetAnnotations() + + if anns == nil { + anns = map[string]string{ + LastSuccessfulSpec: string(specBytes), + } + } else { + anns[LastSuccessfulSpec] = string(specBytes) + } + + adb.SetAnnotations(anns) + + return nil +} + +// UpdateStatusFromOCIADB updates the status subresource +func (adb *AutonomousDatabase) UpdateStatusFromOCIADB(ociObj database.AutonomousDatabase) { + adb.Status.LifecycleState = ociObj.LifecycleState + adb.Status.TimeCreated = FormatSDKTime(ociObj.TimeCreated) + + if *ociObj.IsDedicated { + conns := make([]ConnectionStringSpec, len(ociObj.ConnectionStrings.AllConnectionStrings)) + for key, val := range ociObj.ConnectionStrings.AllConnectionStrings { + conns = append(conns, ConnectionStringSpec{TNSName: key, ConnectionString: val}) + } + + adb.Status.AllConnectionStrings = []ConnectionStringProfile{ + {ConnectionStrings: conns}, + } + } else { + var mTLSConns []ConnectionStringSpec + var tlsConns []ConnectionStringSpec + + var conns []ConnectionStringProfile + + for _, profile := range ociObj.ConnectionStrings.Profiles { + if profile.TlsAuthentication == database.DatabaseConnectionStringProfileTlsAuthenticationMutual { + mTLSConns = append(mTLSConns, ConnectionStringSpec{TNSName: *profile.DisplayName, ConnectionString: *profile.Value}) + } else { + tlsConns = append(tlsConns, ConnectionStringSpec{TNSName: *profile.DisplayName, ConnectionString: *profile.Value}) + } + } + + if len(mTLSConns) > 0 { + conns = append(conns, ConnectionStringProfile{ + TLSAuthentication: tlsAuthenticationMTLS, + ConnectionStrings: mTLSConns, + }) + } + + if len(tlsConns) > 0 { + conns = append(conns, ConnectionStringProfile{ + TLSAuthentication: tlsAuthenticationTLS, + ConnectionStrings: tlsConns, + }) + } + + adb.Status.AllConnectionStrings = conns + } +} + +// UpdateFromOCIADB updates the attributes using database.AutonomousDatabase object +func (adb *AutonomousDatabase) UpdateFromOCIADB(ociObj database.AutonomousDatabase) (specChanged bool) { + oldADB := adb.DeepCopy() + + /*********************************** + * update the spec + ***********************************/ + adb.Spec.Details.AutonomousDatabaseOCID = ociObj.Id + adb.Spec.Details.CompartmentOCID = ociObj.CompartmentId + adb.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID = ociObj.AutonomousContainerDatabaseId + adb.Spec.Details.DisplayName = ociObj.DisplayName + adb.Spec.Details.DbName = ociObj.DbName + adb.Spec.Details.DbWorkload = ociObj.DbWorkload + adb.Spec.Details.LicenseModel = ociObj.LicenseModel + adb.Spec.Details.DbVersion = ociObj.DbVersion + adb.Spec.Details.DataStorageSizeInTBs = ociObj.DataStorageSizeInTBs + adb.Spec.Details.CPUCoreCount = ociObj.CpuCoreCount + adb.Spec.Details.IsAutoScalingEnabled = ociObj.IsAutoScalingEnabled + adb.Spec.Details.IsDedicated = ociObj.IsDedicated + adb.Spec.Details.LifecycleState = NextADBStableState(ociObj.LifecycleState) + // Special case: an emtpy map will be nil after unmarshalling while the OCI always returns an emty map. + if len(ociObj.FreeformTags) != 0 { + adb.Spec.Details.FreeformTags = ociObj.FreeformTags + } else { + adb.Spec.Details.FreeformTags = nil + } + + // Determine network.accessType + if *ociObj.IsDedicated { + adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate + } else { + if ociObj.NsgIds != nil || ociObj.PrivateEndpoint != nil || ociObj.PrivateEndpointIp != nil || ociObj.PrivateEndpointLabel != nil { + adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate + } else if ociObj.WhitelistedIps != nil { + adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypeRestricted + } else { + adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePublic + } + } + + adb.Spec.Details.NetworkAccess.IsAccessControlEnabled = ociObj.IsAccessControlEnabled + if len(ociObj.WhitelistedIps) != 0 { + adb.Spec.Details.NetworkAccess.AccessControlList = ociObj.WhitelistedIps + } else { + adb.Spec.Details.NetworkAccess.AccessControlList = nil + } + adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = ociObj.IsMtlsConnectionRequired + adb.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = ociObj.SubnetId + if len(ociObj.NsgIds) != 0 { + adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = ociObj.NsgIds + } else { + adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil + } + adb.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = ociObj.PrivateEndpointLabel + + // The admin password is not going to be updated in a bind operation. Erase the field if the lastSucSpec is nil. + // Leave the wallet field as is because the download wallet operation is independent from the update operation. + lastSucSpec, _ := adb.GetLastSuccessfulSpec() + if lastSucSpec == nil { + adb.Spec.Details.AdminPassword = PasswordSpec{} + } else { + adb.Spec.Details.AdminPassword = lastSucSpec.Details.AdminPassword + } + + /*********************************** + * update the status subresource + ***********************************/ + adb.UpdateStatusFromOCIADB(ociObj) + + return !reflect.DeepEqual(oldADB.Spec, adb.Spec) +} + +// RemoveUnchangedDetails removes the unchanged fields in spec.details, and returns if the details has been changed. +func (adb *AutonomousDatabase) RemoveUnchangedDetails(prevSpec AutonomousDatabaseSpec) (bool, error) { + + changed, err := removeUnchangedFields(prevSpec.Details, &adb.Spec.Details) + if err != nil { + return changed, err + } + + return changed, nil +} + +// A helper function which is useful for debugging. The function prints out a structural JSON format. +func (adb *AutonomousDatabase) String() (string, error) { + out, err := json.MarshalIndent(adb, "", " ") + if err != nil { + return "", err + } + return string(out), nil +} diff --git a/apis/database/v4/autonomousdatabase_webhook.go b/apis/database/v4/autonomousdatabase_webhook.go new file mode 100644 index 00000000..82c56d34 --- /dev/null +++ b/apis/database/v4/autonomousdatabase_webhook.go @@ -0,0 +1,291 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "fmt" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var autonomousdatabaselog = logf.Log.WithName("autonomousdatabase-resource") + +func (r *AutonomousDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:verbs=create;update,path=/mutate-database-oracle-com-v4-autonomousdatabase,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v4,name=mautonomousdatabasev4.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &AutonomousDatabase{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *AutonomousDatabase) Default() { + autonomousdatabaselog.Info("default", "name", r.Name) + + if !isDedicated(r) { // Shared database + // AccessType is PUBLIC by default + if r.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypePublic { + r.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = common.Bool(true) + r.Spec.Details.NetworkAccess.AccessControlList = nil + r.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = nil + r.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil + r.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = nil + } else if r.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypeRestricted { + r.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = nil + r.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil + r.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = nil + } else if r.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypePrivate { + r.Spec.Details.NetworkAccess.AccessControlList = nil + } + } else { // Dedicated database + // AccessType can only be PRIVATE for a dedicated database + r.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate + } + +} + +//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomousdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v4,name=vautonomousdatabasev4.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &AutonomousDatabase{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +// ValidateCreate checks if the spec is valid for a provisioning or a binding operation +func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { + var allErrs field.ErrorList + + autonomousdatabaselog.Info("validate create", "name", r.Name) + + namespaces := dbcommons.GetWatchNamespaces() + _, hasEmptyString := namespaces[""] + isClusterScoped := len(namespaces) == 1 && hasEmptyString + if !isClusterScoped { + _, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + if len(namespaces) != 0 && !containsNamespace { + allErrs = append(allErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + } + + if r.Spec.Details.AutonomousDatabaseOCID == nil { // provisioning operation + allErrs = validateCommon(r, allErrs) + allErrs = validateNetworkAccess(r, allErrs) + + if r.Spec.Details.LifecycleState != "" { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("lifecycleState"), + "cannot apply lifecycleState to a provision operation")) + } + } + + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabase"}, + r.Name, allErrs) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + var allErrs field.ErrorList + var oldADB *AutonomousDatabase = old.(*AutonomousDatabase) + + autonomousdatabaselog.Info("validate update", "name", r.Name) + + // skip the update of adding ADB OCID or binding + if oldADB.Status.LifecycleState == "" { + return nil, nil + } + + // cannot update when the old state is in intermediate, except for the change to the hardLink or the terminate operatrion during valid lifecycleState + var copySpec *AutonomousDatabaseSpec = r.Spec.DeepCopy() + specChanged, err := removeUnchangedFields(oldADB.Spec, copySpec) + if err != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec"), err.Error())) + } + + hardLinkChanged := copySpec.HardLink != nil + + terminateOp := ValidADBTerminateState(oldADB.Status.LifecycleState) && copySpec.Details.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated + + if specChanged && IsADBIntermediateState(oldADB.Status.LifecycleState) && !terminateOp && !hardLinkChanged { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec"), + "cannot change the spec when the lifecycleState is in an intermdeiate state")) + } + + // cannot modify autonomousDatabaseOCID + if r.Spec.Details.AutonomousDatabaseOCID != nil && + oldADB.Spec.Details.AutonomousDatabaseOCID != nil && + *r.Spec.Details.AutonomousDatabaseOCID != *oldADB.Spec.Details.AutonomousDatabaseOCID { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("autonomousDatabaseOCID"), + "autonomousDatabaseOCID cannot be modified")) + } + + // cannot change lifecycleState with other fields together (except the oci config) + var lifecycleChanged, otherFieldsChanged bool + + lifecycleChanged = oldADB.Spec.Details.LifecycleState != "" && + r.Spec.Details.LifecycleState != "" && + oldADB.Spec.Details.LifecycleState != r.Spec.Details.LifecycleState + var copiedADB *AutonomousDatabaseSpec = r.Spec.DeepCopy() + copiedADB.Details.LifecycleState = oldADB.Spec.Details.LifecycleState + copiedADB.OCIConfig = oldADB.Spec.OCIConfig + + otherFieldsChanged, err = removeUnchangedFields(oldADB.Spec, copiedADB) + if err != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec"), err.Error())) + } + + if lifecycleChanged && otherFieldsChanged { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("LifecycleState"), + "cannot change lifecycleState with other spec attributes at the same time")) + } + + allErrs = validateCommon(r, allErrs) + allErrs = validateNetworkAccess(r, allErrs) + + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabase"}, + r.Name, allErrs) +} + +func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.ErrorList { + // password + if adb.Spec.Details.AdminPassword.K8sSecret.Name != nil && adb.Spec.Details.AdminPassword.OCISecret.OCID != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("adminPassword"), + "cannot apply k8sSecret.name and ociSecret.ocid at the same time")) + } + + if adb.Spec.Details.Wallet.Password.K8sSecret.Name != nil && adb.Spec.Details.Wallet.Password.OCISecret.OCID != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("wallet").Child("password"), + "cannot apply k8sSecret.name and ociSecret.ocid at the same time")) + } + + return allErrs +} + +func validateNetworkAccess(adb *AutonomousDatabase, allErrs field.ErrorList) field.ErrorList { + if !isDedicated(adb) { + // Shared database + if adb.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypeRestricted { + if adb.Spec.Details.NetworkAccess.AccessControlList == nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("accessControlList"), + fmt.Sprintf("accessControlList cannot be empty when the network access type is %s", NetworkAccessTypeRestricted))) + } + } else if adb.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypePrivate { // the accessType is PRIVATE + if adb.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID == nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("privateEndpoint").Child("subnetOCID"), + fmt.Sprintf("subnetOCID cannot be empty when the network access type is %s", NetworkAccessTypePrivate))) + } + } + + // NsgOCIDs only applies to PRIVATE accessType + if adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs != nil && adb.Spec.Details.NetworkAccess.AccessType != NetworkAccessTypePrivate { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("privateEndpoint").Child("nsgOCIDs"), + fmt.Sprintf("NsgOCIDs cannot only be applied when network access type is %s.", NetworkAccessTypePrivate))) + } + + // IsAccessControlEnabled is not applicable to a shared database + if adb.Spec.Details.NetworkAccess.IsAccessControlEnabled != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("IsAccessControlEnabled"), + "isAccessControlEnabled is not applicable on a shared Autonomous Database")) + } + } else { + // Dedicated database + + // accessControlList cannot be provided when Autonomous Database's access control is disabled + if adb.Spec.Details.NetworkAccess.AccessControlList != nil && + (adb.Spec.Details.NetworkAccess.IsAccessControlEnabled == nil || !*adb.Spec.Details.NetworkAccess.IsAccessControlEnabled) { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("accessControlList"), + "access control list cannot be provided when Autonomous Database's access control is disabled")) + } + + // IsMTLSConnectionRequired is not supported by dedicated database + if adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("isMTLSConnectionRequired"), + "isMTLSConnectionRequired is not supported on a dedicated database")) + } + } + + return allErrs +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousDatabase) ValidateDelete() (admission.Warnings, error) { + autonomousdatabaselog.Info("validate delete", "name", r.Name) + return nil, nil +} + +// Returns true if AutonomousContainerDatabaseOCID has value. +// We don't use Details.IsDedicated because the parameter might be null when it's a provision operation. +func isDedicated(adb *AutonomousDatabase) bool { + return adb.Spec.Details.AutonomousContainerDatabase.K8sACD.Name != nil || + adb.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID != nil +} diff --git a/apis/database/v4/autonomousdatabasebackup_types.go b/apis/database/v4/autonomousdatabasebackup_types.go new file mode 100644 index 00000000..798c8acf --- /dev/null +++ b/apis/database/v4/autonomousdatabasebackup_types.go @@ -0,0 +1,129 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// AutonomousDatabaseBackupSpec defines the desired state of AutonomousDatabaseBackup +type AutonomousDatabaseBackupSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + Target TargetSpec `json:"target,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + AutonomousDatabaseBackupOCID *string `json:"autonomousDatabaseBackupOCID,omitempty"` + IsLongTermBackup *bool `json:"isLongTermBackup,omitempty"` + RetentionPeriodInDays *int `json:"retentionPeriodInDays,omitempty"` + OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` +} + +// AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup +type AutonomousDatabaseBackupStatus struct { + LifecycleState database.AutonomousDatabaseBackupLifecycleStateEnum `json:"lifecycleState"` + Type database.AutonomousDatabaseBackupTypeEnum `json:"type"` + IsAutomatic bool `json:"isAutomatic"` + TimeStarted string `json:"timeStarted,omitempty"` + TimeEnded string `json:"timeEnded,omitempty"` + AutonomousDatabaseOCID string `json:"autonomousDatabaseOCID"` + CompartmentOCID string `json:"compartmentOCID"` + DBName string `json:"dbName"` + DBDisplayName string `json:"dbDisplayName"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:shortName="adbbu";"adbbus" +//+kubebuilder:printcolumn:JSONPath=".status.lifecycleState",name="State",type=string +//+kubebuilder:printcolumn:JSONPath=".status.dbDisplayName",name="DB DisplayName",type=string +//+kubebuilder:printcolumn:JSONPath=".status.type",name="Type",type=string +//+kubebuilder:printcolumn:JSONPath=".status.timeStarted",name="Started",type=string +//+kubebuilder:printcolumn:JSONPath=".status.timeEnded",name="Ended",type=string +// +kubebuilder:storageversion + +// AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API +type AutonomousDatabaseBackup struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AutonomousDatabaseBackupSpec `json:"spec,omitempty"` + Status AutonomousDatabaseBackupStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// AutonomousDatabaseBackupList contains a list of AutonomousDatabaseBackup +type AutonomousDatabaseBackupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []AutonomousDatabaseBackup `json:"items"` +} + +func init() { + SchemeBuilder.Register(&AutonomousDatabaseBackup{}, &AutonomousDatabaseBackupList{}) +} + +// Implement conversion.Hub interface, which means any resource version can convert into v4 +func (*AutonomousDatabaseBackup) Hub() {} + +func (b *AutonomousDatabaseBackup) UpdateStatusFromOCIBackup(ociBackup database.AutonomousDatabaseBackup, ociADB database.AutonomousDatabase) { + b.Status.AutonomousDatabaseOCID = *ociBackup.AutonomousDatabaseId + b.Status.CompartmentOCID = *ociBackup.CompartmentId + b.Status.Type = ociBackup.Type + b.Status.IsAutomatic = *ociBackup.IsAutomatic + + b.Status.LifecycleState = ociBackup.LifecycleState + + b.Status.TimeStarted = FormatSDKTime(ociBackup.TimeStarted) + b.Status.TimeEnded = FormatSDKTime(ociBackup.TimeEnded) + + b.Status.DBDisplayName = *ociADB.DisplayName + b.Status.DBName = *ociADB.DbName +} + +// GetTimeEnded returns the status.timeEnded in SDKTime format +func (b *AutonomousDatabaseBackup) GetTimeEnded() (*common.SDKTime, error) { + return parseDisplayTime(b.Status.TimeEnded) +} diff --git a/apis/database/v4/autonomousdatabasebackup_webhook.go b/apis/database/v4/autonomousdatabasebackup_webhook.go new file mode 100644 index 00000000..49f82542 --- /dev/null +++ b/apis/database/v4/autonomousdatabasebackup_webhook.go @@ -0,0 +1,158 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var autonomousdatabasebackuplog = logf.Log.WithName("autonomousdatabasebackup-resource") + +func (r *AutonomousDatabaseBackup) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-autonomousdatabasebackup,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,verbs=create;update,versions=v4,name=mautonomousdatabasebackupv4.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &AutonomousDatabaseBackup{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *AutonomousDatabaseBackup) Default() { + autonomousdatabasebackuplog.Info("default", "name", r.Name) +} + +//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomousdatabasebackup,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,versions=v4,name=vautonomousdatabasebackupv4.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &AutonomousDatabaseBackup{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousDatabaseBackup) ValidateCreate() (admission.Warnings, error) { + autonomousdatabasebackuplog.Info("validate create", "name", r.Name) + + var allErrs field.ErrorList + + namespaces := dbcommons.GetWatchNamespaces() + _, hasEmptyString := namespaces[""] + isClusterScoped := len(namespaces) == 1 && hasEmptyString + if !isClusterScoped { + _, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + if len(namespaces) != 0 && !containsNamespace { + allErrs = append(allErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + } + + if r.Spec.Target.K8sADB.Name == nil && r.Spec.Target.OCIADB.OCID == nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) + } + + if r.Spec.Target.K8sADB.Name != nil && r.Spec.Target.OCIADB.OCID != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB or ociADB, but not both")) + } + + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseBackup"}, + r.Name, allErrs) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + autonomousdatabasebackuplog.Info("validate update", "name", r.Name) + + var allErrs field.ErrorList + oldBackup := old.(*AutonomousDatabaseBackup) + + if oldBackup.Spec.AutonomousDatabaseBackupOCID != nil && r.Spec.AutonomousDatabaseBackupOCID != nil && + *oldBackup.Spec.AutonomousDatabaseBackupOCID != *r.Spec.AutonomousDatabaseBackupOCID { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("autonomousDatabaseBackupOCID"), + "cannot assign a new autonomousDatabaseBackupOCID to this backup")) + } + + if oldBackup.Spec.Target.K8sADB.Name != nil && r.Spec.Target.K8sADB.Name != nil && + *oldBackup.Spec.Target.K8sADB.Name != *r.Spec.Target.K8sADB.Name { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("target").Child("k8sADB").Child("name"), "cannot assign a new name to the target")) + } + + if oldBackup.Spec.Target.OCIADB.OCID != nil && r.Spec.Target.OCIADB.OCID != nil && + *oldBackup.Spec.Target.OCIADB.OCID != *r.Spec.Target.OCIADB.OCID { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("target").Child("ociADB").Child("ocid"), "cannot assign a new ocid to the target")) + } + + if oldBackup.Spec.DisplayName != nil && r.Spec.DisplayName != nil && + *oldBackup.Spec.DisplayName != *r.Spec.DisplayName { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("displayName"), "cannot assign a new displayName to this backup")) + } + + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseBackup"}, + r.Name, allErrs) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousDatabaseBackup) ValidateDelete() (admission.Warnings, error) { + autonomousdatabasebackuplog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} diff --git a/apis/database/v4/autonomousdatabaserestore_types.go b/apis/database/v4/autonomousdatabaserestore_types.go new file mode 100644 index 00000000..2589ce62 --- /dev/null +++ b/apis/database/v4/autonomousdatabaserestore_types.go @@ -0,0 +1,142 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "errors" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + "github.com/oracle/oci-go-sdk/v65/workrequests" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. +type K8sADBBackupSpec struct { + Name *string `json:"name,omitempty"` +} + +type PITSpec struct { + // The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT + Timestamp *string `json:"timestamp,omitempty"` +} + +type SourceSpec struct { + K8sADBBackup K8sADBBackupSpec `json:"k8sADBBackup,omitempty"` + PointInTime PITSpec `json:"pointInTime,omitempty"` +} + +// AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore +type AutonomousDatabaseRestoreSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + Target TargetSpec `json:"target"` + Source SourceSpec `json:"source"` + OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` +} + +// AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore +type AutonomousDatabaseRestoreStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + DisplayName string `json:"displayName"` + TimeAccepted string `json:"timeAccepted,omitempty"` + TimeStarted string `json:"timeStarted,omitempty"` + TimeEnded string `json:"timeEnded,omitempty"` + DbName string `json:"dbName"` + WorkRequestOCID string `json:"workRequestOCID"` + Status workrequests.WorkRequestStatusEnum `json:"status"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:shortName="adbr";"adbrs" +// +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string +// +kubebuilder:printcolumn:JSONPath=".status.displayName",name="DbDisplayName",type=string +// +kubebuilder:printcolumn:JSONPath=".status.dbName",name="DbName",type=string +// +kubebuilder:storageversion + +// AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API +type AutonomousDatabaseRestore struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AutonomousDatabaseRestoreSpec `json:"spec,omitempty"` + Status AutonomousDatabaseRestoreStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// AutonomousDatabaseRestoreList contains a list of AutonomousDatabaseRestore +type AutonomousDatabaseRestoreList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []AutonomousDatabaseRestore `json:"items"` +} + +func init() { + SchemeBuilder.Register(&AutonomousDatabaseRestore{}, &AutonomousDatabaseRestoreList{}) +} + +// Implement conversion.Hub interface, which means any resource version can convert into v4 +func (*AutonomousDatabaseRestore) Hub() {} + +// GetPIT returns the spec.pointInTime.timeStamp in SDKTime format +func (r *AutonomousDatabaseRestore) GetPIT() (*common.SDKTime, error) { + if r.Spec.Source.PointInTime.Timestamp == nil { + return nil, errors.New("the timestamp is empty") + } + return parseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) +} + +func (r *AutonomousDatabaseRestore) UpdateStatus( + adb database.AutonomousDatabase, + workResp workrequests.GetWorkRequestResponse) { + + r.Status.DisplayName = *adb.DisplayName + r.Status.DbName = *adb.DbName + + r.Status.WorkRequestOCID = *workResp.Id + r.Status.Status = workResp.Status + r.Status.TimeAccepted = FormatSDKTime(workResp.TimeAccepted) + r.Status.TimeStarted = FormatSDKTime(workResp.TimeStarted) + r.Status.TimeEnded = FormatSDKTime(workResp.TimeFinished) +} diff --git a/apis/database/v4/autonomousdatabaserestore_webhook.go b/apis/database/v4/autonomousdatabaserestore_webhook.go new file mode 100644 index 00000000..98e004ba --- /dev/null +++ b/apis/database/v4/autonomousdatabaserestore_webhook.go @@ -0,0 +1,146 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var autonomousdatabaserestorelog = logf.Log.WithName("autonomousdatabaserestore-resource") + +func (r *AutonomousDatabaseRestore) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomousdatabaserestore,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabaserestores,versions=v4,name=vautonomousdatabaserestorev4.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &AutonomousDatabaseRestore{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) { + autonomousdatabaserestorelog.Info("validate create", "name", r.Name) + + var allErrs field.ErrorList + + namespaces := dbcommons.GetWatchNamespaces() + _, hasEmptyString := namespaces[""] + isClusterScoped := len(namespaces) == 1 && hasEmptyString + if !isClusterScoped { + _, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + if len(namespaces) != 0 && !containsNamespace { + allErrs = append(allErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + } + + // Validate the target ADB + if r.Spec.Target.K8sADB.Name == nil && r.Spec.Target.OCIADB.OCID == nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) + } + + if r.Spec.Target.K8sADB.Name != nil && r.Spec.Target.OCIADB.OCID != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB.name or ociADB.ocid, but not both")) + } + + // Validate the restore source + if r.Spec.Source.K8sADBBackup.Name == nil && + r.Spec.Source.PointInTime.Timestamp == nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("source"), "retore source is empty")) + } + + if r.Spec.Source.K8sADBBackup.Name != nil && + r.Spec.Source.PointInTime.Timestamp != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("source"), "cannot apply backupName and the PITR parameters at the same time")) + } + + // Verify the timestamp format if it's PITR + if r.Spec.Source.PointInTime.Timestamp != nil { + _, err := parseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) + if err != nil { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("source").Child("pointInTime").Child("timestamp"), "invalid timestamp format")) + } + } + + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseRestore"}, + r.Name, allErrs) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousDatabaseRestore) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + autonomousdatabaserestorelog.Info("validate update", "name", r.Name) + + var allErrs field.ErrorList + + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseRestore"}, + r.Name, allErrs) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *AutonomousDatabaseRestore) ValidateDelete() (admission.Warnings, error) { + autonomousdatabaserestorelog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index b03976a3..b3b4e964 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -48,6 +48,529 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ACDSpec) DeepCopyInto(out *ACDSpec) { + *out = *in + in.K8sACD.DeepCopyInto(&out.K8sACD) + in.OCIACD.DeepCopyInto(&out.OCIACD) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACDSpec. +func (in *ACDSpec) DeepCopy() *ACDSpec { + if in == nil { + return nil + } + out := new(ACDSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousContainerDatabase) DeepCopyInto(out *AutonomousContainerDatabase) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousContainerDatabase. +func (in *AutonomousContainerDatabase) DeepCopy() *AutonomousContainerDatabase { + if in == nil { + return nil + } + out := new(AutonomousContainerDatabase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AutonomousContainerDatabase) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousContainerDatabaseList) DeepCopyInto(out *AutonomousContainerDatabaseList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AutonomousContainerDatabase, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousContainerDatabaseList. +func (in *AutonomousContainerDatabaseList) DeepCopy() *AutonomousContainerDatabaseList { + if in == nil { + return nil + } + out := new(AutonomousContainerDatabaseList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AutonomousContainerDatabaseList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousContainerDatabaseSpec) DeepCopyInto(out *AutonomousContainerDatabaseSpec) { + *out = *in + if in.AutonomousContainerDatabaseOCID != nil { + in, out := &in.AutonomousContainerDatabaseOCID, &out.AutonomousContainerDatabaseOCID + *out = new(string) + **out = **in + } + if in.CompartmentOCID != nil { + in, out := &in.CompartmentOCID, &out.CompartmentOCID + *out = new(string) + **out = **in + } + if in.DisplayName != nil { + in, out := &in.DisplayName, &out.DisplayName + *out = new(string) + **out = **in + } + if in.AutonomousExadataVMClusterOCID != nil { + in, out := &in.AutonomousExadataVMClusterOCID, &out.AutonomousExadataVMClusterOCID + *out = new(string) + **out = **in + } + if in.FreeformTags != nil { + in, out := &in.FreeformTags, &out.FreeformTags + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.OCIConfig.DeepCopyInto(&out.OCIConfig) + if in.HardLink != nil { + in, out := &in.HardLink, &out.HardLink + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousContainerDatabaseSpec. +func (in *AutonomousContainerDatabaseSpec) DeepCopy() *AutonomousContainerDatabaseSpec { + if in == nil { + return nil + } + out := new(AutonomousContainerDatabaseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousContainerDatabaseStatus) DeepCopyInto(out *AutonomousContainerDatabaseStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousContainerDatabaseStatus. +func (in *AutonomousContainerDatabaseStatus) DeepCopy() *AutonomousContainerDatabaseStatus { + if in == nil { + return nil + } + out := new(AutonomousContainerDatabaseStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabase) DeepCopyInto(out *AutonomousDatabase) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabase. +func (in *AutonomousDatabase) DeepCopy() *AutonomousDatabase { + if in == nil { + return nil + } + out := new(AutonomousDatabase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AutonomousDatabase) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseBackup) DeepCopyInto(out *AutonomousDatabaseBackup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseBackup. +func (in *AutonomousDatabaseBackup) DeepCopy() *AutonomousDatabaseBackup { + if in == nil { + return nil + } + out := new(AutonomousDatabaseBackup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AutonomousDatabaseBackup) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseBackupList) DeepCopyInto(out *AutonomousDatabaseBackupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AutonomousDatabaseBackup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseBackupList. +func (in *AutonomousDatabaseBackupList) DeepCopy() *AutonomousDatabaseBackupList { + if in == nil { + return nil + } + out := new(AutonomousDatabaseBackupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AutonomousDatabaseBackupList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseBackupSpec) DeepCopyInto(out *AutonomousDatabaseBackupSpec) { + *out = *in + in.Target.DeepCopyInto(&out.Target) + if in.DisplayName != nil { + in, out := &in.DisplayName, &out.DisplayName + *out = new(string) + **out = **in + } + if in.AutonomousDatabaseBackupOCID != nil { + in, out := &in.AutonomousDatabaseBackupOCID, &out.AutonomousDatabaseBackupOCID + *out = new(string) + **out = **in + } + if in.IsLongTermBackup != nil { + in, out := &in.IsLongTermBackup, &out.IsLongTermBackup + *out = new(bool) + **out = **in + } + if in.RetentionPeriodInDays != nil { + in, out := &in.RetentionPeriodInDays, &out.RetentionPeriodInDays + *out = new(int) + **out = **in + } + in.OCIConfig.DeepCopyInto(&out.OCIConfig) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseBackupSpec. +func (in *AutonomousDatabaseBackupSpec) DeepCopy() *AutonomousDatabaseBackupSpec { + if in == nil { + return nil + } + out := new(AutonomousDatabaseBackupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseBackupStatus) DeepCopyInto(out *AutonomousDatabaseBackupStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseBackupStatus. +func (in *AutonomousDatabaseBackupStatus) DeepCopy() *AutonomousDatabaseBackupStatus { + if in == nil { + return nil + } + out := new(AutonomousDatabaseBackupStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails) { + *out = *in + if in.AutonomousDatabaseOCID != nil { + in, out := &in.AutonomousDatabaseOCID, &out.AutonomousDatabaseOCID + *out = new(string) + **out = **in + } + if in.CompartmentOCID != nil { + in, out := &in.CompartmentOCID, &out.CompartmentOCID + *out = new(string) + **out = **in + } + in.AutonomousContainerDatabase.DeepCopyInto(&out.AutonomousContainerDatabase) + if in.DisplayName != nil { + in, out := &in.DisplayName, &out.DisplayName + *out = new(string) + **out = **in + } + if in.DbName != nil { + in, out := &in.DbName, &out.DbName + *out = new(string) + **out = **in + } + if in.DbVersion != nil { + in, out := &in.DbVersion, &out.DbVersion + *out = new(string) + **out = **in + } + if in.DataStorageSizeInTBs != nil { + in, out := &in.DataStorageSizeInTBs, &out.DataStorageSizeInTBs + *out = new(int) + **out = **in + } + if in.CPUCoreCount != nil { + in, out := &in.CPUCoreCount, &out.CPUCoreCount + *out = new(int) + **out = **in + } + in.AdminPassword.DeepCopyInto(&out.AdminPassword) + if in.IsAutoScalingEnabled != nil { + in, out := &in.IsAutoScalingEnabled, &out.IsAutoScalingEnabled + *out = new(bool) + **out = **in + } + if in.IsDedicated != nil { + in, out := &in.IsDedicated, &out.IsDedicated + *out = new(bool) + **out = **in + } + in.NetworkAccess.DeepCopyInto(&out.NetworkAccess) + if in.FreeformTags != nil { + in, out := &in.FreeformTags, &out.FreeformTags + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Wallet.DeepCopyInto(&out.Wallet) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseDetails. +func (in *AutonomousDatabaseDetails) DeepCopy() *AutonomousDatabaseDetails { + if in == nil { + return nil + } + out := new(AutonomousDatabaseDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseList) DeepCopyInto(out *AutonomousDatabaseList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AutonomousDatabase, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseList. +func (in *AutonomousDatabaseList) DeepCopy() *AutonomousDatabaseList { + if in == nil { + return nil + } + out := new(AutonomousDatabaseList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AutonomousDatabaseList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseRestore) DeepCopyInto(out *AutonomousDatabaseRestore) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseRestore. +func (in *AutonomousDatabaseRestore) DeepCopy() *AutonomousDatabaseRestore { + if in == nil { + return nil + } + out := new(AutonomousDatabaseRestore) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AutonomousDatabaseRestore) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseRestoreList) DeepCopyInto(out *AutonomousDatabaseRestoreList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AutonomousDatabaseRestore, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseRestoreList. +func (in *AutonomousDatabaseRestoreList) DeepCopy() *AutonomousDatabaseRestoreList { + if in == nil { + return nil + } + out := new(AutonomousDatabaseRestoreList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AutonomousDatabaseRestoreList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseRestoreSpec) DeepCopyInto(out *AutonomousDatabaseRestoreSpec) { + *out = *in + in.Target.DeepCopyInto(&out.Target) + in.Source.DeepCopyInto(&out.Source) + in.OCIConfig.DeepCopyInto(&out.OCIConfig) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseRestoreSpec. +func (in *AutonomousDatabaseRestoreSpec) DeepCopy() *AutonomousDatabaseRestoreSpec { + if in == nil { + return nil + } + out := new(AutonomousDatabaseRestoreSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseRestoreStatus) DeepCopyInto(out *AutonomousDatabaseRestoreStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseRestoreStatus. +func (in *AutonomousDatabaseRestoreStatus) DeepCopy() *AutonomousDatabaseRestoreStatus { + if in == nil { + return nil + } + out := new(AutonomousDatabaseRestoreStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseSpec) DeepCopyInto(out *AutonomousDatabaseSpec) { + *out = *in + in.Details.DeepCopyInto(&out.Details) + in.OCIConfig.DeepCopyInto(&out.OCIConfig) + if in.HardLink != nil { + in, out := &in.HardLink, &out.HardLink + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseSpec. +func (in *AutonomousDatabaseSpec) DeepCopy() *AutonomousDatabaseSpec { + if in == nil { + return nil + } + out := new(AutonomousDatabaseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseStatus) DeepCopyInto(out *AutonomousDatabaseStatus) { + *out = *in + if in.AllConnectionStrings != nil { + in, out := &in.AllConnectionStrings, &out.AllConnectionStrings + *out = make([]ConnectionStringProfile, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseStatus. +func (in *AutonomousDatabaseStatus) DeepCopy() *AutonomousDatabaseStatus { + if in == nil { + return nil + } + out := new(AutonomousDatabaseStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Backupconfig) DeepCopyInto(out *Backupconfig) { *out = *in @@ -134,6 +657,41 @@ func (in *CatalogSpec) DeepCopy() *CatalogSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConnectionStringProfile) DeepCopyInto(out *ConnectionStringProfile) { + *out = *in + if in.ConnectionStrings != nil { + in, out := &in.ConnectionStrings, &out.ConnectionStrings + *out = make([]ConnectionStringSpec, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectionStringProfile. +func (in *ConnectionStringProfile) DeepCopy() *ConnectionStringProfile { + if in == nil { + return nil + } + out := new(ConnectionStringProfile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConnectionStringSpec) DeepCopyInto(out *ConnectionStringSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectionStringSpec. +func (in *ConnectionStringSpec) DeepCopy() *ConnectionStringSpec { + if in == nil { + return nil + } + out := new(ConnectionStringSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DbCloneConfig) DeepCopyInto(out *DbCloneConfig) { *out = *in @@ -603,6 +1161,86 @@ func (in *GsmStatusDetails) DeepCopy() *GsmStatusDetails { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *K8sACDSpec) DeepCopyInto(out *K8sACDSpec) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sACDSpec. +func (in *K8sACDSpec) DeepCopy() *K8sACDSpec { + if in == nil { + return nil + } + out := new(K8sACDSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *K8sADBBackupSpec) DeepCopyInto(out *K8sADBBackupSpec) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sADBBackupSpec. +func (in *K8sADBBackupSpec) DeepCopy() *K8sADBBackupSpec { + if in == nil { + return nil + } + out := new(K8sADBBackupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *K8sADBSpec) DeepCopyInto(out *K8sADBSpec) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sADBSpec. +func (in *K8sADBSpec) DeepCopy() *K8sADBSpec { + if in == nil { + return nil + } + out := new(K8sADBSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *K8sSecretSpec) DeepCopyInto(out *K8sSecretSpec) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sSecretSpec. +func (in *K8sSecretSpec) DeepCopy() *K8sSecretSpec { + if in == nil { + return nil + } + out := new(K8sSecretSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KMSConfig) DeepCopyInto(out *KMSConfig) { *out = *in @@ -633,6 +1271,122 @@ func (in *KMSDetailsStatus) DeepCopy() *KMSDetailsStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkAccessSpec) DeepCopyInto(out *NetworkAccessSpec) { + *out = *in + if in.IsAccessControlEnabled != nil { + in, out := &in.IsAccessControlEnabled, &out.IsAccessControlEnabled + *out = new(bool) + **out = **in + } + if in.AccessControlList != nil { + in, out := &in.AccessControlList, &out.AccessControlList + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.PrivateEndpoint.DeepCopyInto(&out.PrivateEndpoint) + if in.IsMTLSConnectionRequired != nil { + in, out := &in.IsMTLSConnectionRequired, &out.IsMTLSConnectionRequired + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkAccessSpec. +func (in *NetworkAccessSpec) DeepCopy() *NetworkAccessSpec { + if in == nil { + return nil + } + out := new(NetworkAccessSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIACDSpec) DeepCopyInto(out *OCIACDSpec) { + *out = *in + if in.OCID != nil { + in, out := &in.OCID, &out.OCID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIACDSpec. +func (in *OCIACDSpec) DeepCopy() *OCIACDSpec { + if in == nil { + return nil + } + out := new(OCIACDSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIADBSpec) DeepCopyInto(out *OCIADBSpec) { + *out = *in + if in.OCID != nil { + in, out := &in.OCID, &out.OCID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIADBSpec. +func (in *OCIADBSpec) DeepCopy() *OCIADBSpec { + if in == nil { + return nil + } + out := new(OCIADBSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { + *out = *in + if in.ConfigMapName != nil { + in, out := &in.ConfigMapName, &out.ConfigMapName + *out = new(string) + **out = **in + } + if in.SecretName != nil { + in, out := &in.SecretName, &out.SecretName + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfigSpec. +func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { + if in == nil { + return nil + } + out := new(OCIConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCISecretSpec) DeepCopyInto(out *OCISecretSpec) { + *out = *in + if in.OCID != nil { + in, out := &in.OCID, &out.OCID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCISecretSpec. +func (in *OCISecretSpec) DeepCopy() *OCISecretSpec { + if in == nil { + return nil + } + out := new(OCISecretSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { *out = *in @@ -744,6 +1498,43 @@ func (in *PDBDetailsStatus) DeepCopy() *PDBDetailsStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PITSpec) DeepCopyInto(out *PITSpec) { + *out = *in + if in.Timestamp != nil { + in, out := &in.Timestamp, &out.Timestamp + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PITSpec. +func (in *PITSpec) DeepCopy() *PITSpec { + if in == nil { + return nil + } + out := new(PITSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PasswordSpec) DeepCopyInto(out *PasswordSpec) { + *out = *in + in.K8sSecret.DeepCopyInto(&out.K8sSecret) + in.OCISecret.DeepCopyInto(&out.OCISecret) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswordSpec. +func (in *PasswordSpec) DeepCopy() *PasswordSpec { + if in == nil { + return nil + } + out := new(PasswordSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PortMapping) DeepCopyInto(out *PortMapping) { *out = *in @@ -759,6 +1550,36 @@ func (in *PortMapping) DeepCopy() *PortMapping { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrivateEndpointSpec) DeepCopyInto(out *PrivateEndpointSpec) { + *out = *in + if in.SubnetOCID != nil { + in, out := &in.SubnetOCID, &out.SubnetOCID + *out = new(string) + **out = **in + } + if in.NsgOCIDs != nil { + in, out := &in.NsgOCIDs, &out.NsgOCIDs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.HostnamePrefix != nil { + in, out := &in.HostnamePrefix, &out.HostnamePrefix + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateEndpointSpec. +func (in *PrivateEndpointSpec) DeepCopy() *PrivateEndpointSpec { + if in == nil { + return nil + } + out := new(PrivateEndpointSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretDetails) DeepCopyInto(out *SecretDetails) { *out = *in @@ -987,6 +1808,40 @@ func (in *ShardingDatabaseStatus) DeepCopy() *ShardingDatabaseStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { + *out = *in + in.K8sADBBackup.DeepCopyInto(&out.K8sADBBackup) + in.PointInTime.DeepCopyInto(&out.PointInTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSpec. +func (in *SourceSpec) DeepCopy() *SourceSpec { + if in == nil { + return nil + } + out := new(SourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { + *out = *in + in.K8sADB.DeepCopyInto(&out.K8sADB) + in.OCIADB.DeepCopyInto(&out.OCIADB) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSpec. +func (in *TargetSpec) DeepCopy() *TargetSpec { + if in == nil { + return nil + } + out := new(TargetSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VmNetworkDetails) DeepCopyInto(out *VmNetworkDetails) { *out = *in @@ -1021,3 +1876,24 @@ func (in *VmNetworkDetails) DeepCopy() *VmNetworkDetails { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WalletSpec) DeepCopyInto(out *WalletSpec) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + in.Password.DeepCopyInto(&out.Password) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WalletSpec. +func (in *WalletSpec) DeepCopy() *WalletSpec { + if in == nil { + return nil + } + out := new(WalletSpec) + in.DeepCopyInto(out) + return out +} diff --git a/commons/adb_family/utils.go b/commons/adb_family/utils.go index 8218502e..93994458 100644 --- a/commons/adb_family/utils.go +++ b/commons/adb_family/utils.go @@ -39,7 +39,7 @@ package adbfamily import ( - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/oracle/oracle-database-operator/commons/k8s" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -48,14 +48,14 @@ import ( // The function returns two values in the following order: // ocid: the OCID of the target ADB. An empty string is returned if the ocid is nil. // ownerADB: the resource of the targetADB if it's found in the cluster -func VerifyTargetADB(kubeClient client.Client, target dbv1alpha1.TargetSpec, namespace string) (*dbv1alpha1.AutonomousDatabase, error) { +func VerifyTargetADB(kubeClient client.Client, target dbv4.TargetSpec, namespace string) (*dbv4.AutonomousDatabase, error) { var err error - var ownerADB *dbv1alpha1.AutonomousDatabase + var ownerADB *dbv4.AutonomousDatabase // Get the target ADB OCID if target.K8sADB.Name != nil { // Find the target ADB using the name of the k8s ADB - ownerADB = &dbv1alpha1.AutonomousDatabase{} + ownerADB = &dbv4.AutonomousDatabase{} if err := k8s.FetchResource(kubeClient, namespace, *target.K8sADB.Name, ownerADB); err != nil { return nil, err } diff --git a/commons/k8s/create.go b/commons/k8s/create.go index 5055bc0e..9f161968 100644 --- a/commons/k8s/create.go +++ b/commons/k8s/create.go @@ -41,7 +41,7 @@ package k8s import ( "context" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" @@ -78,23 +78,23 @@ func CreateSecret(kubeClient client.Client, namespace string, name string, data func CreateAutonomousBackup(kubeClient client.Client, backupName string, backupSummary database.AutonomousDatabaseBackupSummary, - ownerADB *dbv1alpha1.AutonomousDatabase) error { + ownerADB *dbv4.AutonomousDatabase) error { - backup := &dbv1alpha1.AutonomousDatabaseBackup{ + backup := &dbv4.AutonomousDatabaseBackup{ ObjectMeta: metav1.ObjectMeta{ Namespace: ownerADB.GetNamespace(), Name: backupName, OwnerReferences: NewOwnerReference(ownerADB), }, - Spec: dbv1alpha1.AutonomousDatabaseBackupSpec{ - Target: dbv1alpha1.TargetSpec{ - K8sADB: dbv1alpha1.K8sADBSpec{ + Spec: dbv4.AutonomousDatabaseBackupSpec{ + Target: dbv4.TargetSpec{ + K8sADB: dbv4.K8sADBSpec{ Name: common.String(ownerADB.Name), }, }, DisplayName: backupSummary.DisplayName, AutonomousDatabaseBackupOCID: backupSummary.Id, - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OCIConfig: dbv4.OCIConfigSpec{ ConfigMapName: ownerADB.Spec.OCIConfig.ConfigMapName, SecretName: ownerADB.Spec.OCIConfig.SecretName, }, diff --git a/commons/k8s/fetch.go b/commons/k8s/fetch.go index 05792cad..15087f28 100644 --- a/commons/k8s/fetch.go +++ b/commons/k8s/fetch.go @@ -47,7 +47,7 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" ) func FetchResource(kubeClient client.Client, namespace string, name string, object client.Object) error { @@ -66,7 +66,7 @@ func FetchResource(kubeClient client.Client, namespace string, name string, obje // Sometimes the AutonomousDatabase doesn't exist. It could happen if a user simply want to restore or // backup the ADB without creating an ADB rersource in the cluster. // If there isn't an AutonomousDatabase with the same OCID, a nil is returned. -func FetchAutonomousDatabaseWithOCID(kubeClient client.Client, namespace string, ocid string) (*dbv1alpha1.AutonomousDatabase, error) { +func FetchAutonomousDatabaseWithOCID(kubeClient client.Client, namespace string, ocid string) (*dbv4.AutonomousDatabase, error) { adbList, err := fetchAutonomousDatabases(kubeClient, namespace) if err != nil { return nil, err @@ -81,9 +81,9 @@ func FetchAutonomousDatabaseWithOCID(kubeClient client.Client, namespace string, return nil, nil } -func fetchAutonomousDatabases(kubeClient client.Client, namespace string) (*dbv1alpha1.AutonomousDatabaseList, error) { +func fetchAutonomousDatabases(kubeClient client.Client, namespace string) (*dbv4.AutonomousDatabaseList, error) { // Get the list of AutonomousDatabaseBackupOCID in the same namespace - adbList := &dbv1alpha1.AutonomousDatabaseList{} + adbList := &dbv4.AutonomousDatabaseList{} if err := kubeClient.List(context.TODO(), adbList, &client.ListOptions{Namespace: namespace}); err != nil { // Ignore not-found errors, since they can't be fixed by an immediate requeue. @@ -96,9 +96,9 @@ func fetchAutonomousDatabases(kubeClient client.Client, namespace string) (*dbv1 return adbList, nil } -func FetchAutonomousDatabaseBackups(kubeClient client.Client, namespace string) (*dbv1alpha1.AutonomousDatabaseBackupList, error) { +func FetchAutonomousDatabaseBackups(kubeClient client.Client, namespace string) (*dbv4.AutonomousDatabaseBackupList, error) { // Get the list of AutonomousDatabaseBackupOCID in the same namespace - backupList := &dbv1alpha1.AutonomousDatabaseBackupList{} + backupList := &dbv4.AutonomousDatabaseBackupList{} if err := kubeClient.List(context.TODO(), backupList, &client.ListOptions{Namespace: namespace}); err != nil { // Ignore not-found errors, since they can't be fixed by an immediate requeue. diff --git a/commons/oci/containerdatabase.go b/commons/oci/containerdatabase.go index a5313d41..da49977d 100644 --- a/commons/oci/containerdatabase.go +++ b/commons/oci/containerdatabase.go @@ -44,13 +44,13 @@ import ( "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" ) /******************************** * Autonomous Container Database *******************************/ -func (d *databaseService) CreateAutonomousContainerDatabase(acd *dbv1alpha1.AutonomousContainerDatabase) (database.CreateAutonomousContainerDatabaseResponse, error) { +func (d *databaseService) CreateAutonomousContainerDatabase(acd *dbv4.AutonomousContainerDatabase) (database.CreateAutonomousContainerDatabaseResponse, error) { createAutonomousContainerDatabaseRequest := database.CreateAutonomousContainerDatabaseRequest{ CreateAutonomousContainerDatabaseDetails: database.CreateAutonomousContainerDatabaseDetails{ CompartmentId: acd.Spec.CompartmentOCID, @@ -71,7 +71,7 @@ func (d *databaseService) GetAutonomousContainerDatabase(acdOCID string) (databa return d.dbClient.GetAutonomousContainerDatabase(context.TODO(), getAutonomousContainerDatabaseRequest) } -func (d *databaseService) UpdateAutonomousContainerDatabase(acdOCID string, difACD *dbv1alpha1.AutonomousContainerDatabase) (database.UpdateAutonomousContainerDatabaseResponse, error) { +func (d *databaseService) UpdateAutonomousContainerDatabase(acdOCID string, difACD *dbv4.AutonomousContainerDatabase) (database.UpdateAutonomousContainerDatabaseResponse, error) { updateAutonomousContainerDatabaseRequest := database.UpdateAutonomousContainerDatabaseRequest{ AutonomousContainerDatabaseId: common.String(acdOCID), UpdateAutonomousContainerDatabaseDetails: database.UpdateAutonomousContainerDatabaseDetails{ diff --git a/commons/oci/database.go b/commons/oci/database.go index 9c3cd4d8..8e2fd2b1 100644 --- a/commons/oci/database.go +++ b/commons/oci/database.go @@ -47,33 +47,33 @@ import ( "github.com/oracle/oci-go-sdk/v65/database" "sigs.k8s.io/controller-runtime/pkg/client" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/oracle/oracle-database-operator/commons/k8s" ) type DatabaseService interface { - CreateAutonomousDatabase(adb *dbv1alpha1.AutonomousDatabase) (database.CreateAutonomousDatabaseResponse, error) + CreateAutonomousDatabase(adb *dbv4.AutonomousDatabase) (database.CreateAutonomousDatabaseResponse, error) GetAutonomousDatabase(adbOCID string) (database.GetAutonomousDatabaseResponse, error) - UpdateAutonomousDatabaseGeneralFields(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateAutonomousDatabaseDBWorkload(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateAutonomousDatabaseLicenseModel(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateAutonomousDatabaseAdminPassword(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateAutonomousDatabaseScalingFields(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) + UpdateAutonomousDatabaseGeneralFields(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) + UpdateAutonomousDatabaseDBWorkload(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) + UpdateAutonomousDatabaseLicenseModel(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) + UpdateAutonomousDatabaseAdminPassword(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) + UpdateAutonomousDatabaseScalingFields(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) UpdateNetworkAccessMTLSRequired(adbOCID string) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateNetworkAccessMTLS(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateNetworkAccessPublic(lastAccessType dbv1alpha1.NetworkAccessTypeEnum, adbOCID string) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateNetworkAccess(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) + UpdateNetworkAccessMTLS(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) + UpdateNetworkAccessPublic(lastAccessType dbv4.NetworkAccessTypeEnum, adbOCID string) (resp database.UpdateAutonomousDatabaseResponse, err error) + UpdateNetworkAccess(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) StartAutonomousDatabase(adbOCID string) (database.StartAutonomousDatabaseResponse, error) StopAutonomousDatabase(adbOCID string) (database.StopAutonomousDatabaseResponse, error) DeleteAutonomousDatabase(adbOCID string) (database.DeleteAutonomousDatabaseResponse, error) - DownloadWallet(adb *dbv1alpha1.AutonomousDatabase) (database.GenerateAutonomousDatabaseWalletResponse, error) + DownloadWallet(adb *dbv4.AutonomousDatabase) (database.GenerateAutonomousDatabaseWalletResponse, error) RestoreAutonomousDatabase(adbOCID string, sdkTime common.SDKTime) (database.RestoreAutonomousDatabaseResponse, error) ListAutonomousDatabaseBackups(adbOCID string) (database.ListAutonomousDatabaseBackupsResponse, error) - CreateAutonomousDatabaseBackup(adbBackup *dbv1alpha1.AutonomousDatabaseBackup, adbOCID string) (database.CreateAutonomousDatabaseBackupResponse, error) + CreateAutonomousDatabaseBackup(adbBackup *dbv4.AutonomousDatabaseBackup, adbOCID string) (database.CreateAutonomousDatabaseBackupResponse, error) GetAutonomousDatabaseBackup(backupOCID string) (database.GetAutonomousDatabaseBackupResponse, error) - CreateAutonomousContainerDatabase(acd *dbv1alpha1.AutonomousContainerDatabase) (database.CreateAutonomousContainerDatabaseResponse, error) + CreateAutonomousContainerDatabase(acd *dbv4.AutonomousContainerDatabase) (database.CreateAutonomousContainerDatabaseResponse, error) GetAutonomousContainerDatabase(acdOCID string) (database.GetAutonomousContainerDatabaseResponse, error) - UpdateAutonomousContainerDatabase(acdOCID string, difACD *dbv1alpha1.AutonomousContainerDatabase) (database.UpdateAutonomousContainerDatabaseResponse, error) + UpdateAutonomousContainerDatabase(acdOCID string, difACD *dbv4.AutonomousContainerDatabase) (database.UpdateAutonomousContainerDatabaseResponse, error) RestartAutonomousContainerDatabase(acdOCID string) (database.RestartAutonomousContainerDatabaseResponse, error) TerminateAutonomousContainerDatabase(acdOCID string) (database.TerminateAutonomousContainerDatabaseResponse, error) } @@ -114,7 +114,7 @@ func NewDatabaseService( // ReadPassword reads the password from passwordSpec, and returns the pointer to the read password string. // The function returns a nil if nothing is read -func (d *databaseService) readPassword(namespace string, passwordSpec dbv1alpha1.PasswordSpec) (*string, error) { +func (d *databaseService) readPassword(namespace string, passwordSpec dbv4.PasswordSpec) (*string, error) { logger := d.logger.WithName("readPassword") if passwordSpec.K8sSecret.Name != nil { @@ -142,13 +142,13 @@ func (d *databaseService) readPassword(namespace string, passwordSpec dbv1alpha1 return nil, nil } -func (d *databaseService) readACD_OCID(acd *dbv1alpha1.ACDSpec, namespace string) (*string, error) { +func (d *databaseService) readACD_OCID(acd *dbv4.ACDSpec, namespace string) (*string, error) { if acd.OCIACD.OCID != nil { return acd.OCIACD.OCID, nil } if acd.K8sACD.Name != nil { - fetchedACD := &dbv1alpha1.AutonomousContainerDatabase{} + fetchedACD := &dbv4.AutonomousContainerDatabase{} if err := k8s.FetchResource(d.kubeClient, namespace, *acd.K8sACD.Name, fetchedACD); err != nil { return nil, err } @@ -160,7 +160,7 @@ func (d *databaseService) readACD_OCID(acd *dbv1alpha1.ACDSpec, namespace string } // CreateAutonomousDatabase sends a request to OCI to provision a database and returns the AutonomousDatabase OCID. -func (d *databaseService) CreateAutonomousDatabase(adb *dbv1alpha1.AutonomousDatabase) (resp database.CreateAutonomousDatabaseResponse, err error) { +func (d *databaseService) CreateAutonomousDatabase(adb *dbv4.AutonomousDatabase) (resp database.CreateAutonomousDatabaseResponse, err error) { adminPassword, err := d.readPassword(adb.Namespace, adb.Spec.Details.AdminPassword) if err != nil { return resp, err @@ -215,7 +215,7 @@ func (d *databaseService) GetAutonomousDatabase(adbOCID string) (database.GetAut return d.dbClient.GetAutonomousDatabase(context.TODO(), getAutonomousDatabaseRequest) } -func (d *databaseService) UpdateAutonomousDatabaseGeneralFields(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { +func (d *databaseService) UpdateAutonomousDatabaseGeneralFields(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ @@ -228,7 +228,7 @@ func (d *databaseService) UpdateAutonomousDatabaseGeneralFields(adbOCID string, return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -func (d *databaseService) UpdateAutonomousDatabaseDBWorkload(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { +func (d *databaseService) UpdateAutonomousDatabaseDBWorkload(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ @@ -238,7 +238,7 @@ func (d *databaseService) UpdateAutonomousDatabaseDBWorkload(adbOCID string, dif return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -func (d *databaseService) UpdateAutonomousDatabaseLicenseModel(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { +func (d *databaseService) UpdateAutonomousDatabaseLicenseModel(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ @@ -248,7 +248,7 @@ func (d *databaseService) UpdateAutonomousDatabaseLicenseModel(adbOCID string, d return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -func (d *databaseService) UpdateAutonomousDatabaseAdminPassword(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { +func (d *databaseService) UpdateAutonomousDatabaseAdminPassword(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { adminPassword, err := d.readPassword(difADB.Namespace, difADB.Spec.Details.AdminPassword) if err != nil { return resp, err @@ -263,7 +263,7 @@ func (d *databaseService) UpdateAutonomousDatabaseAdminPassword(adbOCID string, return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -func (d *databaseService) UpdateAutonomousDatabaseScalingFields(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { +func (d *databaseService) UpdateAutonomousDatabaseScalingFields(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ @@ -285,7 +285,7 @@ func (d *databaseService) UpdateNetworkAccessMTLSRequired(adbOCID string) (resp return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -func (d *databaseService) UpdateNetworkAccessMTLS(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { +func (d *databaseService) UpdateNetworkAccessMTLS(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ @@ -296,14 +296,14 @@ func (d *databaseService) UpdateNetworkAccessMTLS(adbOCID string, difADB *dbv1al } func (d *databaseService) UpdateNetworkAccessPublic( - lastAccessType dbv1alpha1.NetworkAccessTypeEnum, + lastAccessType dbv4.NetworkAccessTypeEnum, adbOCID string) (resp database.UpdateAutonomousDatabaseResponse, err error) { updateAutonomousDatabaseDetails := database.UpdateAutonomousDatabaseDetails{} - if lastAccessType == dbv1alpha1.NetworkAccessTypeRestricted { + if lastAccessType == dbv4.NetworkAccessTypeRestricted { updateAutonomousDatabaseDetails.WhitelistedIps = []string{""} - } else if lastAccessType == dbv1alpha1.NetworkAccessTypePrivate { + } else if lastAccessType == dbv4.NetworkAccessTypePrivate { updateAutonomousDatabaseDetails.PrivateEndpointLabel = common.String("") } @@ -315,7 +315,7 @@ func (d *databaseService) UpdateNetworkAccessPublic( return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -func (d *databaseService) UpdateNetworkAccess(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { +func (d *databaseService) UpdateNetworkAccess(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ @@ -354,7 +354,7 @@ func (d *databaseService) DeleteAutonomousDatabase(adbOCID string) (database.Del return d.dbClient.DeleteAutonomousDatabase(context.TODO(), deleteRequest) } -func (d *databaseService) DownloadWallet(adb *dbv1alpha1.AutonomousDatabase) (resp database.GenerateAutonomousDatabaseWalletResponse, err error) { +func (d *databaseService) DownloadWallet(adb *dbv4.AutonomousDatabase) (resp database.GenerateAutonomousDatabaseWalletResponse, err error) { // Prepare wallet password walletPassword, err := d.readPassword(adb.Namespace, adb.Spec.Details.Wallet.Password) if err != nil { @@ -404,7 +404,7 @@ func (d *databaseService) ListAutonomousDatabaseBackups(adbOCID string) (databas return d.dbClient.ListAutonomousDatabaseBackups(context.TODO(), listBackupRequest) } -func (d *databaseService) CreateAutonomousDatabaseBackup(adbBackup *dbv1alpha1.AutonomousDatabaseBackup, adbOCID string) (database.CreateAutonomousDatabaseBackupResponse, error) { +func (d *databaseService) CreateAutonomousDatabaseBackup(adbBackup *dbv4.AutonomousDatabaseBackup, adbOCID string) (database.CreateAutonomousDatabaseBackupResponse, error) { createBackupRequest := database.CreateAutonomousDatabaseBackupRequest{ CreateAutonomousDatabaseBackupDetails: database.CreateAutonomousDatabaseBackupDetails{ AutonomousDatabaseId: common.String(adbOCID), diff --git a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml index bd768828..1e078b63 100644 --- a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml @@ -84,6 +84,76 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + properties: + lifecycleState: + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml index a522712a..b0d6f8ed 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml @@ -105,6 +105,97 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + isLongTermBackup: + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + retentionPeriodInDays: + type: integer + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + type: string + timeEnded: + type: string + timeStarted: + type: string + type: + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml index 86c55a51..3bfc5a4e 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml @@ -100,6 +100,92 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + type: string + type: object + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + properties: + dbName: + type: string + displayName: + type: string + status: + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index 394ddcb3..cb22180a 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -247,6 +247,239 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + details: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sACD: + properties: + name: + type: string + type: object + ociACD: + properties: + ocid: + type: string + type: object + type: object + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + lifecycleState: + type: string + networkAccess: + properties: + accessControlList: + items: + type: string + type: array + accessType: + enum: + - "" + - PUBLIC + - RESTRICTED + - PRIVATE + type: string + isAccessControlEnabled: + type: boolean + isMTLSConnectionRequired: + type: boolean + privateEndpoint: + properties: + hostnamePrefix: + type: string + nsgOCIDs: + items: + type: string + type: array + subnetOCID: + type: string + type: object + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + type: object + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + required: + - details + type: object + status: + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index b9f3aa8c..ca4307d3 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -24,31 +24,35 @@ patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_provshards.yaml -#- patches/webhook_in_autonomousdatabases.yaml #- patches/webhook_in_singleinstancedatabases.yaml #- patches/webhook_in_shardingdatabases.yaml #- patches/webhook_in_pdbs.yaml #- patches/webhook_in_cdbs.yaml #- patches/webhook_in_oraclerestdataservices.yaml -#- patches/webhook_in_autonomouscontainerdatabases.yaml #- patches/webhook_in_dbcssystems.yaml #- patches/webhook_in_dataguardbrokers.yaml #- patches/webhook_in_databaseobservers.yaml +- patches/webhook_in_autonomousdatabases.yaml +- patches/webhook_in_autonomousdatabasebackups.yaml +- patches/webhook_in_autonomousdatabaserestores.yaml +- patches/webhook_in_autonomouscontainerdatabases.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_provshards.yaml -#- patches/cainjection_in_autonomousdatabases.yaml - patches/cainjection_in_singleinstancedatabases.yaml #- patches/cainjection_in_shardingdatabases.yaml - patches/cainjection_in_pdbs.yaml - patches/cainjection_in_cdbs.yaml #- patches/cainjection_in_oraclerestdataservices.yaml -#- patches/cainjection_in_autonomouscontainerdatabases.yaml #- patches/cainjection_in_dbcssystems.yaml #- patches/cainjection_in_dataguardbrokers.yaml #- patches/cainjection_in_databaseobservers.yaml +- patches/cainjection_in_autonomousdatabases.yaml +- patches/cainjection_in_autonomousdatabasebackups.yaml +- patches/cainjection_in_autonomousdatabaserestores.yaml +- patches/cainjection_in_autonomouscontainerdatabases.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml b/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml index 3985a5ae..734407bc 100644 --- a/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml +++ b/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml @@ -1,6 +1,6 @@ # The following patch adds a directive for certmanager to inject CA into the CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: diff --git a/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml b/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml index 78280137..9468569d 100644 --- a/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml +++ b/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml @@ -1,6 +1,6 @@ # The following patch adds a directive for certmanager to inject CA into the CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: diff --git a/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml b/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml index 75894cbb..cfc941f8 100644 --- a/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml +++ b/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml @@ -1,6 +1,6 @@ # The following patch adds a directive for certmanager to inject CA into the CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: diff --git a/config/crd/patches/webhook_in_autonomouscontainerdatabases.yaml b/config/crd/patches/webhook_in_autonomouscontainerdatabases.yaml index 03a73384..6ef8f0a6 100644 --- a/config/crd/patches/webhook_in_autonomouscontainerdatabases.yaml +++ b/config/crd/patches/webhook_in_autonomouscontainerdatabases.yaml @@ -1,17 +1,19 @@ # The following patch enables conversion webhook for CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: autonomouscontainerdatabases.database.oracle.com spec: conversion: strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert + webhook: + clientConfig: + service: + namespace: oracle-database-operator-system + name: oracle-database-operator-webhook-service + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 diff --git a/config/crd/patches/webhook_in_autonomousdatabasebackups.yaml b/config/crd/patches/webhook_in_autonomousdatabasebackups.yaml index 1a4eacb6..ee363f8f 100644 --- a/config/crd/patches/webhook_in_autonomousdatabasebackups.yaml +++ b/config/crd/patches/webhook_in_autonomousdatabasebackups.yaml @@ -1,17 +1,19 @@ # The following patch enables conversion webhook for CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: autonomousdatabasebackups.database.oracle.com spec: conversion: strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert + webhook: + clientConfig: + service: + namespace: oracle-database-operator-system + name: oracle-database-operator-webhook-service + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 \ No newline at end of file diff --git a/config/crd/patches/webhook_in_autonomousdatabaserestores.yaml b/config/crd/patches/webhook_in_autonomousdatabaserestores.yaml index 0a0ed4ad..33329655 100644 --- a/config/crd/patches/webhook_in_autonomousdatabaserestores.yaml +++ b/config/crd/patches/webhook_in_autonomousdatabaserestores.yaml @@ -1,17 +1,19 @@ # The following patch enables conversion webhook for CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: autonomousdatabaserestores.database.oracle.com spec: conversion: strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert + webhook: + clientConfig: + service: + namespace: oracle-database-operator-system + name: oracle-database-operator-webhook-service + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 \ No newline at end of file diff --git a/config/crd/patches/webhook_in_autonomousdatabases.yaml b/config/crd/patches/webhook_in_autonomousdatabases.yaml index 230f9f68..c7ec554f 100644 --- a/config/crd/patches/webhook_in_autonomousdatabases.yaml +++ b/config/crd/patches/webhook_in_autonomousdatabases.yaml @@ -11,11 +11,13 @@ metadata: spec: conversion: strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert + webhook: + clientConfig: + service: + namespace: oracle-database-operator-system + name: oracle-database-operator-webhook-service + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 9ab6522a..1ff969da 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -10,20 +10,80 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + path: /mutate-database-oracle-com-v4-autonomousdatabasebackup failurePolicy: Fail - name: mautonomousdatabase.kb.io + name: mautonomousdatabasebackupv4.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 operations: - CREATE - UPDATE resources: - autonomousdatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-dbcssystem + failurePolicy: Fail + name: mdbcssystem.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - dbcssystems + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: mshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -32,7 +92,7 @@ webhooks: namespace: system path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup failurePolicy: Fail - name: mautonomousdatabasebackup.kb.io + name: mautonomousdatabasebackupv1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -44,6 +104,26 @@ webhooks: resources: - autonomousdatabasebackups sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabasev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -155,9 +235,35 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v4-dbcssystem + path: /mutate-observability-oracle-com-v1alpha1-databaseobserver failurePolicy: Fail - name: mdbcssystem.kb.io + name: mdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v4-autonomouscontainerdatabase + failurePolicy: Fail + name: vautonomouscontainerdatabasev4.kb.io rules: - apiGroups: - database.oracle.com @@ -167,7 +273,7 @@ webhooks: - CREATE - UPDATE resources: - - dbcssystems + - autonomouscontainerdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -175,9 +281,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v4-shardingdatabase + path: /validate-database-oracle-com-v4-autonomousdatabasebackup failurePolicy: Fail - name: mshardingdatabase.kb.io + name: vautonomousdatabasebackupv4.kb.io rules: - apiGroups: - database.oracle.com @@ -187,7 +293,7 @@ webhooks: - CREATE - UPDATE resources: - - shardingdatabases + - autonomousdatabasebackups sideEffects: None - admissionReviewVersions: - v1 @@ -195,45 +301,39 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-observability-oracle-com-v1alpha1-databaseobserver + path: /validate-database-oracle-com-v4-autonomousdatabaserestore failurePolicy: Fail - name: mdatabaseobserver.kb.io + name: vautonomousdatabaserestorev4.kb.io rules: - apiGroups: - - observability.oracle.com + - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - databaseobservers + - autonomousdatabaserestores sideEffects: None ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: validating-webhook-configuration -webhooks: - admissionReviewVersions: - v1 clientConfig: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + path: /validate-database-oracle-com-v4-autonomousdatabase failurePolicy: Fail - name: vautonomouscontainerdatabase.kb.io + name: vautonomousdatabasev4.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - autonomouscontainerdatabases + - autonomousdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -241,9 +341,30 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + path: /validate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: vautonomousdatabase.kb.io + name: vshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + failurePolicy: Fail + name: vautonomouscontainerdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -253,7 +374,7 @@ webhooks: - CREATE - UPDATE resources: - - autonomousdatabases + - autonomouscontainerdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -263,7 +384,7 @@ webhooks: namespace: system path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup failurePolicy: Fail - name: vautonomousdatabasebackup.kb.io + name: vautonomousdatabasebackupv1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -283,7 +404,7 @@ webhooks: namespace: system path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore failurePolicy: Fail - name: vautonomousdatabaserestore.kb.io + name: vautonomousdatabaserestorev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -297,14 +418,13 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-cdb + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase failurePolicy: Fail - name: vcdb.kb.io + name: vautonomousdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -314,7 +434,7 @@ webhooks: - CREATE - UPDATE resources: - - cdbs + - autonomousdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -323,9 +443,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-dataguardbroker + path: /validate-database-oracle-com-v1alpha1-cdb failurePolicy: Fail - name: vdataguardbroker.kb.io + name: vcdb.kb.io rules: - apiGroups: - database.oracle.com @@ -335,7 +455,7 @@ webhooks: - CREATE - UPDATE resources: - - dataguardbrokers + - cdbs sideEffects: None - admissionReviewVersions: - v1 @@ -344,9 +464,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + path: /validate-database-oracle-com-v1alpha1-dataguardbroker failurePolicy: Fail - name: voraclerestdataservice.kb.io + name: vdataguardbroker.kb.io rules: - apiGroups: - database.oracle.com @@ -356,7 +476,7 @@ webhooks: - CREATE - UPDATE resources: - - oraclerestdataservices + - dataguardbrokers sideEffects: None - admissionReviewVersions: - v1 @@ -365,9 +485,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-pdb + path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice failurePolicy: Fail - name: vpdb.kb.io + name: voraclerestdataservice.kb.io rules: - apiGroups: - database.oracle.com @@ -377,7 +497,7 @@ webhooks: - CREATE - UPDATE resources: - - pdbs + - oraclerestdataservices sideEffects: None - admissionReviewVersions: - v1 @@ -386,9 +506,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase + path: /validate-database-oracle-com-v1alpha1-pdb failurePolicy: Fail - name: vsingleinstancedatabase.kb.io + name: vpdb.kb.io rules: - apiGroups: - database.oracle.com @@ -397,30 +517,30 @@ webhooks: operations: - CREATE - UPDATE - - DELETE resources: - - singleinstancedatabases + - pdbs sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v4-shardingdatabase + path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase failurePolicy: Fail - name: vshardingdatabase.kb.io + name: vsingleinstancedatabase.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v4 + - v1alpha1 operations: - CREATE - UPDATE - DELETE resources: - - shardingdatabases + - singleinstancedatabases sideEffects: None - admissionReviewVersions: - v1 diff --git a/controllers/database/autonomouscontainerdatabase_controller.go b/controllers/database/autonomouscontainerdatabase_controller.go index 3845a03f..712d2399 100644 --- a/controllers/database/autonomouscontainerdatabase_controller.go +++ b/controllers/database/autonomouscontainerdatabase_controller.go @@ -58,7 +58,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/oracle/oracle-database-operator/commons/annotations" "github.com/oracle/oracle-database-operator/commons/k8s" "github.com/oracle/oracle-database-operator/commons/oci" @@ -77,7 +77,7 @@ type AutonomousContainerDatabaseReconciler struct { // SetupWithManager sets up the controller with the Manager. func (r *AutonomousContainerDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&dbv1alpha1.AutonomousContainerDatabase{}). + For(&dbv4.AutonomousContainerDatabase{}). WithEventFilter(r.eventFilterPredicate()). WithOptions(controller.Options{MaxConcurrentReconciles: 5}). Complete(r) @@ -86,13 +86,13 @@ func (r *AutonomousContainerDatabaseReconciler) SetupWithManager(mgr ctrl.Manage func (r *AutonomousContainerDatabaseReconciler) eventFilterPredicate() predicate.Predicate { pred := predicate.Funcs{ UpdateFunc: func(e event.UpdateEvent) bool { - desiredACD, acdOk := e.ObjectNew.(*dbv1alpha1.AutonomousContainerDatabase) + desiredACD, acdOk := e.ObjectNew.(*dbv4.AutonomousContainerDatabase) if acdOk { - oldACD := e.ObjectOld.(*dbv1alpha1.AutonomousContainerDatabase) + oldACD := e.ObjectOld.(*dbv4.AutonomousContainerDatabase) if !reflect.DeepEqual(oldACD.Status, desiredACD.Status) || - (controllerutil.ContainsFinalizer(oldACD, dbv1alpha1.LastSuccessfulSpec) != controllerutil.ContainsFinalizer(desiredACD, dbv1alpha1.LastSuccessfulSpec)) || - (controllerutil.ContainsFinalizer(oldACD, dbv1alpha1.ACDFinalizer) != controllerutil.ContainsFinalizer(desiredACD, dbv1alpha1.ACDFinalizer)) { + (controllerutil.ContainsFinalizer(oldACD, dbv4.LastSuccessfulSpec) != controllerutil.ContainsFinalizer(desiredACD, dbv4.LastSuccessfulSpec)) || + (controllerutil.ContainsFinalizer(oldACD, dbv4.ACDFinalizer) != controllerutil.ContainsFinalizer(desiredACD, dbv4.ACDFinalizer)) { // Don't enqueue if the status, lastSucSpec, or the finalizler changes return false } @@ -103,7 +103,7 @@ func (r *AutonomousContainerDatabaseReconciler) eventFilterPredicate() predicate }, DeleteFunc: func(e event.DeleteEvent) bool { // Do not trigger reconciliation when the object is deleted from the cluster. - _, acdOk := e.Object.(*dbv1alpha1.AutonomousContainerDatabase) + _, acdOk := e.Object.(*dbv4.AutonomousContainerDatabase) return !acdOk }, } @@ -124,10 +124,10 @@ func (r *AutonomousContainerDatabaseReconciler) Reconcile(ctx context.Context, r logger := r.Log.WithValues("Namespace/Name", req.NamespacedName) var err error - var ociACD *dbv1alpha1.AutonomousContainerDatabase + var ociACD *dbv4.AutonomousContainerDatabase // Get the autonomousdatabase instance from the cluster - acd := &dbv1alpha1.AutonomousContainerDatabase{} + acd := &dbv4.AutonomousContainerDatabase{} if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, acd); err != nil { // Ignore not-found errors, since they can't be fixed by an immediate requeue. // No need to change the since we don't know if we obtain the object. @@ -159,7 +159,7 @@ func (r *AutonomousContainerDatabaseReconciler) Reconcile(ctx context.Context, r return r.manageError(logger, acd, err) } - ociACD = &dbv1alpha1.AutonomousContainerDatabase{} + ociACD = &dbv4.AutonomousContainerDatabase{} ociACD.UpdateFromOCIACD(resp.AutonomousContainerDatabase) } @@ -218,7 +218,7 @@ func (r *AutonomousContainerDatabaseReconciler) Reconcile(ctx context.Context, r return r.manageError(logger, acd, err) } - if dbv1alpha1.IsACDIntermediateState(acd.Status.LifecycleState) { + if dbv4.IsACDIntermediateState(acd.Status.LifecycleState) { logger.WithName("IsIntermediateState").Info("Current lifecycleState is " + string(acd.Status.LifecycleState) + "; reconcile queued") return requeueResult, nil } @@ -232,7 +232,7 @@ func (r *AutonomousContainerDatabaseReconciler) Reconcile(ctx context.Context, r return emptyResult, nil } -func (r *AutonomousContainerDatabaseReconciler) setupOCIClients(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase) error { +func (r *AutonomousContainerDatabaseReconciler) setupOCIClients(logger logr.Logger, acd *dbv4.AutonomousContainerDatabase) error { var err error authData := oci.APIKeyAuth{ @@ -254,7 +254,7 @@ func (r *AutonomousContainerDatabaseReconciler) setupOCIClients(logger logr.Logg return nil } -func (r *AutonomousContainerDatabaseReconciler) manageError(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase, issue error) (ctrl.Result, error) { +func (r *AutonomousContainerDatabaseReconciler) manageError(logger logr.Logger, acd *dbv4.AutonomousContainerDatabase, issue error) (ctrl.Result, error) { l := logger.WithName("manageError") // Has synced at least once @@ -290,7 +290,7 @@ func (r *AutonomousContainerDatabaseReconciler) manageError(logger logr.Logger, } // validateLifecycleState gets and validates the current lifecycleState -func (r *AutonomousContainerDatabaseReconciler) validateLifecycleState(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase, ociACD *dbv1alpha1.AutonomousContainerDatabase) (needsRequeue bool, err error) { +func (r *AutonomousContainerDatabaseReconciler) validateLifecycleState(logger logr.Logger, acd *dbv4.AutonomousContainerDatabase, ociACD *dbv4.AutonomousContainerDatabase) (needsRequeue bool, err error) { if ociACD == nil { return false, nil } @@ -313,7 +313,7 @@ func (r *AutonomousContainerDatabaseReconciler) validateLifecycleState(logger lo return false, err } - if dbv1alpha1.IsACDIntermediateState(ociACD.Status.LifecycleState) { + if dbv4.IsACDIntermediateState(ociACD.Status.LifecycleState) { l.Info("LifecycleState is " + string(acd.Status.LifecycleState) + "; reconcile queued") return true, nil } @@ -321,7 +321,7 @@ func (r *AutonomousContainerDatabaseReconciler) validateLifecycleState(logger lo return false, nil } -func (r *AutonomousContainerDatabaseReconciler) validateCleanup(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase) (exitReconcile bool, err error) { +func (r *AutonomousContainerDatabaseReconciler) validateCleanup(logger logr.Logger, acd *dbv4.AutonomousContainerDatabase) (exitReconcile bool, err error) { l := logger.WithName("validateCleanup") isACDToBeDeleted := acd.GetDeletionTimestamp() != nil @@ -330,7 +330,7 @@ func (r *AutonomousContainerDatabaseReconciler) validateCleanup(logger logr.Logg return false, nil } - if controllerutil.ContainsFinalizer(acd, dbv1alpha1.ACDFinalizer) { + if controllerutil.ContainsFinalizer(acd, dbv4.ACDFinalizer) { if acd.Status.LifecycleState == database.AutonomousContainerDatabaseLifecycleStateTerminating { l.Info("Resource is already in TERMINATING state") // Delete in progress, continue with the reconcile logic @@ -341,7 +341,7 @@ func (r *AutonomousContainerDatabaseReconciler) validateCleanup(logger logr.Logg // The acd has been deleted. Remove the finalizer and exit the reconcile. // Once all finalizers have been removed, the object will be deleted. l.Info("Resource is already in TERMINATED state; remove the finalizer") - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, acd, dbv1alpha1.ACDFinalizer); err != nil { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, acd, dbv4.ACDFinalizer); err != nil { return false, err } return true, nil @@ -350,17 +350,17 @@ func (r *AutonomousContainerDatabaseReconciler) validateCleanup(logger logr.Logg if acd.Spec.AutonomousContainerDatabaseOCID == nil { l.Info("Missing AutonomousContainerDatabaseOCID to terminate Autonomous Container Database; remove the finalizer anyway", "Name", acd.Name, "Namespace", acd.Namespace) // Remove finalizer anyway. - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, acd, dbv1alpha1.ACDFinalizer); err != nil { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, acd, dbv4.ACDFinalizer); err != nil { return false, err } return true, nil } - if acd.Spec.Action != dbv1alpha1.AcdActionTerminate { + if acd.Spec.Action != dbv4.AcdActionTerminate { // Run finalization logic for finalizer. If the finalization logic fails, don't remove the finalizer so // that we can retry during the next reconciliation. l.Info("Terminating Autonomous Container Database") - acd.Spec.Action = dbv1alpha1.AcdActionTerminate + acd.Spec.Action = dbv4.AcdActionTerminate if err := r.KubeClient.Update(context.TODO(), acd); err != nil { return false, err } @@ -376,15 +376,15 @@ func (r *AutonomousContainerDatabaseReconciler) validateCleanup(logger logr.Logg return true, nil } -func (r *AutonomousContainerDatabaseReconciler) validateFinalizer(acd *dbv1alpha1.AutonomousContainerDatabase) error { +func (r *AutonomousContainerDatabaseReconciler) validateFinalizer(acd *dbv4.AutonomousContainerDatabase) error { // Delete is not schduled. Update the finalizer for this CR if hardLink is present if acd.Spec.HardLink != nil { - if *acd.Spec.HardLink && !controllerutil.ContainsFinalizer(acd, dbv1alpha1.ACDFinalizer) { - if err := k8s.AddFinalizerAndPatch(r.KubeClient, acd, dbv1alpha1.ACDFinalizer); err != nil { + if *acd.Spec.HardLink && !controllerutil.ContainsFinalizer(acd, dbv4.ACDFinalizer) { + if err := k8s.AddFinalizerAndPatch(r.KubeClient, acd, dbv4.ACDFinalizer); err != nil { return err } - } else if !*acd.Spec.HardLink && controllerutil.ContainsFinalizer(acd, dbv1alpha1.ACDFinalizer) { - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, acd, dbv1alpha1.ACDFinalizer); err != nil { + } else if !*acd.Spec.HardLink && controllerutil.ContainsFinalizer(acd, dbv4.ACDFinalizer) { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, acd, dbv4.ACDFinalizer); err != nil { return err } } @@ -395,8 +395,8 @@ func (r *AutonomousContainerDatabaseReconciler) validateFinalizer(acd *dbv1alpha func (r *AutonomousContainerDatabaseReconciler) validateOperation( logger logr.Logger, - acd *dbv1alpha1.AutonomousContainerDatabase, - ociACD *dbv1alpha1.AutonomousContainerDatabase) (exitReconcile bool, result ctrl.Result, err error) { + acd *dbv4.AutonomousContainerDatabase, + ociACD *dbv4.AutonomousContainerDatabase) (exitReconcile bool, result ctrl.Result, err error) { l := logger.WithName("validateOperation") @@ -511,7 +511,7 @@ func (r *AutonomousContainerDatabaseReconciler) validateOperation( } } -func (r *AutonomousContainerDatabaseReconciler) updateCR(acd *dbv1alpha1.AutonomousContainerDatabase) error { +func (r *AutonomousContainerDatabaseReconciler) updateCR(acd *dbv4.AutonomousContainerDatabase) error { // Update the lastSucSpec if err := acd.UpdateLastSuccessfulSpec(); err != nil { return err @@ -523,14 +523,14 @@ func (r *AutonomousContainerDatabaseReconciler) updateCR(acd *dbv1alpha1.Autonom return nil } -func (r *AutonomousContainerDatabaseReconciler) patchLastSuccessfulSpec(acd *dbv1alpha1.AutonomousContainerDatabase) error { +func (r *AutonomousContainerDatabaseReconciler) patchLastSuccessfulSpec(acd *dbv4.AutonomousContainerDatabase) error { specBytes, err := json.Marshal(acd.Spec) if err != nil { return err } anns := map[string]string{ - dbv1alpha1.LastSuccessfulSpec: string(specBytes), + dbv4.LastSuccessfulSpec: string(specBytes), } annotations.PatchAnnotations(r.KubeClient, acd, anns) @@ -538,7 +538,7 @@ func (r *AutonomousContainerDatabaseReconciler) patchLastSuccessfulSpec(acd *dbv return nil } -func (r *AutonomousContainerDatabaseReconciler) createACD(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase) error { +func (r *AutonomousContainerDatabaseReconciler) createACD(logger logr.Logger, acd *dbv4.AutonomousContainerDatabase) error { logger.WithName("createACD").Info("Sending CreateAutonomousContainerDatabase request to OCI") resp, err := r.dbService.CreateAutonomousContainerDatabase(acd) @@ -551,7 +551,7 @@ func (r *AutonomousContainerDatabaseReconciler) createACD(logger logr.Logger, ac return nil } -func (r *AutonomousContainerDatabaseReconciler) getACD(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase) (bool, error) { +func (r *AutonomousContainerDatabaseReconciler) getACD(logger logr.Logger, acd *dbv4.AutonomousContainerDatabase) (bool, error) { if acd == nil { return false, errors.New("AutonomousContainerDatabase OCID is missing") } @@ -573,10 +573,10 @@ func (r *AutonomousContainerDatabaseReconciler) getACD(logger logr.Logger, acd * // The AutonomousContainerDatabase is updated with the returned object from the OCI requests. func (r *AutonomousContainerDatabaseReconciler) updateACD( logger logr.Logger, - acd *dbv1alpha1.AutonomousContainerDatabase, - difACD *dbv1alpha1.AutonomousContainerDatabase) (ociReqSent bool, specChanged bool, err error) { + acd *dbv4.AutonomousContainerDatabase, + difACD *dbv4.AutonomousContainerDatabase) (ociReqSent bool, specChanged bool, err error) { - validations := []func(logr.Logger, *dbv1alpha1.AutonomousContainerDatabase, *dbv1alpha1.AutonomousContainerDatabase) (bool, bool, error){ + validations := []func(logr.Logger, *dbv4.AutonomousContainerDatabase, *dbv4.AutonomousContainerDatabase) (bool, bool, error){ r.validateGeneralFields, r.validateDesiredLifecycleState, } @@ -597,8 +597,8 @@ func (r *AutonomousContainerDatabaseReconciler) updateACD( func (r *AutonomousContainerDatabaseReconciler) validateGeneralFields( logger logr.Logger, - acd *dbv1alpha1.AutonomousContainerDatabase, - difACD *dbv1alpha1.AutonomousContainerDatabase) (sent bool, requeue bool, err error) { + acd *dbv4.AutonomousContainerDatabase, + difACD *dbv4.AutonomousContainerDatabase) (sent bool, requeue bool, err error) { if difACD.Spec.DisplayName == nil && difACD.Spec.PatchModel == "" && @@ -620,17 +620,17 @@ func (r *AutonomousContainerDatabaseReconciler) validateGeneralFields( func (r *AutonomousContainerDatabaseReconciler) validateDesiredLifecycleState( logger logr.Logger, - acd *dbv1alpha1.AutonomousContainerDatabase, - difACD *dbv1alpha1.AutonomousContainerDatabase) (sent bool, specChanged bool, err error) { + acd *dbv4.AutonomousContainerDatabase, + difACD *dbv4.AutonomousContainerDatabase) (sent bool, specChanged bool, err error) { - if difACD.Spec.Action == dbv1alpha1.AcdActionBlank { + if difACD.Spec.Action == dbv4.AcdActionBlank { return false, false, nil } l := logger.WithName("validateDesiredLifecycleState") switch difACD.Spec.Action { - case dbv1alpha1.AcdActionRestart: + case dbv4.AcdActionRestart: l.Info("Sending RestartAutonomousContainerDatabase request to OCI") resp, err := r.dbService.RestartAutonomousContainerDatabase(*acd.Spec.AutonomousContainerDatabaseOCID) @@ -639,7 +639,7 @@ func (r *AutonomousContainerDatabaseReconciler) validateDesiredLifecycleState( } acd.Status.LifecycleState = resp.LifecycleState - case dbv1alpha1.AcdActionTerminate: + case dbv4.AcdActionTerminate: l.Info("Sending TerminateAutonomousContainerDatabase request to OCI") _, err := r.dbService.TerminateAutonomousContainerDatabase(*acd.Spec.AutonomousContainerDatabaseOCID) @@ -652,7 +652,7 @@ func (r *AutonomousContainerDatabaseReconciler) validateDesiredLifecycleState( return false, false, errors.New("unknown lifecycleState") } - acd.Spec.Action = dbv1alpha1.AcdActionBlank + acd.Spec.Action = dbv4.AcdActionBlank return true, true, nil } diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index bf56bfe0..d6b798ab 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -68,7 +68,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/oracle/oracle-database-operator/commons/annotations" "github.com/oracle/oracle-database-operator/commons/k8s" "github.com/oracle/oracle-database-operator/commons/oci" @@ -90,13 +90,13 @@ type AutonomousDatabaseReconciler struct { // SetupWithManager function func (r *AutonomousDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&dbv1alpha1.AutonomousDatabase{}). + For(&dbv4.AutonomousDatabase{}). Watches( - &dbv1alpha1.AutonomousDatabaseBackup{}, + &dbv4.AutonomousDatabaseBackup{}, handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn), ). Watches( - &dbv1alpha1.AutonomousDatabaseRestore{}, + &dbv4.AutonomousDatabaseRestore{}, handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn), ). WithEventFilter(predicate.And(r.eventFilterPredicate(), r.watchPredicate())). @@ -121,22 +121,22 @@ func (r *AutonomousDatabaseReconciler) enqueueMapFn(ctx context.Context, o clien func (r *AutonomousDatabaseReconciler) watchPredicate() predicate.Predicate { return predicate.Funcs{ CreateFunc: func(e event.CreateEvent) bool { - _, backupOk := e.Object.(*dbv1alpha1.AutonomousDatabaseBackup) - _, restoreOk := e.Object.(*dbv1alpha1.AutonomousDatabaseRestore) + _, backupOk := e.Object.(*dbv4.AutonomousDatabaseBackup) + _, restoreOk := e.Object.(*dbv4.AutonomousDatabaseRestore) // Don't enqueue if the event is from Backup or Restore return !(backupOk || restoreOk) }, UpdateFunc: func(e event.UpdateEvent) bool { // Enqueue the update event only when the status changes the first time - desiredBackup, backupOk := e.ObjectNew.(*dbv1alpha1.AutonomousDatabaseBackup) + desiredBackup, backupOk := e.ObjectNew.(*dbv4.AutonomousDatabaseBackup) if backupOk { - oldBackup := e.ObjectOld.(*dbv1alpha1.AutonomousDatabaseBackup) + oldBackup := e.ObjectOld.(*dbv4.AutonomousDatabaseBackup) return oldBackup.Status.LifecycleState == "" && desiredBackup.Status.LifecycleState != "" } - desiredRestore, restoreOk := e.ObjectNew.(*dbv1alpha1.AutonomousDatabaseRestore) + desiredRestore, restoreOk := e.ObjectNew.(*dbv4.AutonomousDatabaseRestore) if restoreOk { - oldRestore := e.ObjectOld.(*dbv1alpha1.AutonomousDatabaseRestore) + oldRestore := e.ObjectOld.(*dbv4.AutonomousDatabaseRestore) return oldRestore.Status.Status == "" && desiredRestore.Status.Status != "" } @@ -150,19 +150,19 @@ func (r *AutonomousDatabaseReconciler) eventFilterPredicate() predicate.Predicat return predicate.Funcs{ UpdateFunc: func(e event.UpdateEvent) bool { // source object can be AutonomousDatabase, AutonomousDatabaseBackup, or AutonomousDatabaseRestore - desiredADB, adbOk := e.ObjectNew.(*dbv1alpha1.AutonomousDatabase) + desiredADB, adbOk := e.ObjectNew.(*dbv4.AutonomousDatabase) if adbOk { - oldADB := e.ObjectOld.(*dbv1alpha1.AutonomousDatabase) + oldADB := e.ObjectOld.(*dbv4.AutonomousDatabase) specChanged := !reflect.DeepEqual(oldADB.Spec, desiredADB.Spec) statusChanged := !reflect.DeepEqual(oldADB.Status, desiredADB.Status) - oldLastSucSpec := oldADB.GetAnnotations()[dbv1alpha1.LastSuccessfulSpec] - desiredLastSucSpec := desiredADB.GetAnnotations()[dbv1alpha1.LastSuccessfulSpec] + oldLastSucSpec := oldADB.GetAnnotations()[dbv4.LastSuccessfulSpec] + desiredLastSucSpec := desiredADB.GetAnnotations()[dbv4.LastSuccessfulSpec] lastSucSpecChanged := oldLastSucSpec != desiredLastSucSpec if (!specChanged && statusChanged) || lastSucSpecChanged || - (controllerutil.ContainsFinalizer(oldADB, dbv1alpha1.ADB_FINALIZER) != controllerutil.ContainsFinalizer(desiredADB, dbv1alpha1.ADB_FINALIZER)) { + (controllerutil.ContainsFinalizer(oldADB, dbv4.ADB_FINALIZER) != controllerutil.ContainsFinalizer(desiredADB, dbv4.ADB_FINALIZER)) { // Don't enqueue in the folowing condition: // 1. only status changes 2. lastSucSpec changes 3. ADB_FINALIZER changes return false @@ -174,7 +174,7 @@ func (r *AutonomousDatabaseReconciler) eventFilterPredicate() predicate.Predicat }, DeleteFunc: func(e event.DeleteEvent) bool { // Do not trigger reconciliation when the object is deleted from the cluster. - _, adbOk := e.Object.(*dbv1alpha1.AutonomousDatabase) + _, adbOk := e.Object.(*dbv4.AutonomousDatabase) return !adbOk }, } @@ -196,10 +196,10 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R logger := r.Log.WithValues("Namespace/Name", req.NamespacedName) var err error - var ociADB *dbv1alpha1.AutonomousDatabase + var ociADB *dbv4.AutonomousDatabase // Get the autonomousdatabase instance from the cluster - desiredADB := &dbv1alpha1.AutonomousDatabase{} + desiredADB := &dbv4.AutonomousDatabase{} if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, desiredADB); err != nil { // Ignore not-found errors, since they can't be fixed by an immediate requeue. // No need to change the since we don't know if we obtain the object. @@ -298,7 +298,7 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R } modifiedADB.Spec = copiedADB.Spec - if dbv1alpha1.IsADBIntermediateState(modifiedADB.Status.LifecycleState) { + if dbv4.IsADBIntermediateState(modifiedADB.Status.LifecycleState) { logger.WithName("IsADBIntermediateState").Info("LifecycleState is " + string(modifiedADB.Status.LifecycleState) + "; reconcile queued") return requeueResult, nil } @@ -311,7 +311,7 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R var requeue bool = false if modifiedADB.GetDeletionTimestamp() != nil && - controllerutil.ContainsFinalizer(modifiedADB, dbv1alpha1.ADB_FINALIZER) && + controllerutil.ContainsFinalizer(modifiedADB, dbv4.ADB_FINALIZER) && modifiedADB.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated { logger.Info("The ADB is TERMINATED. The CR is to be deleted but finalizer is not yet removed; reconcile queued") requeue = true @@ -331,7 +331,7 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R } } -func (r *AutonomousDatabaseReconciler) setupOCIClients(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) error { +func (r *AutonomousDatabaseReconciler) setupOCIClients(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { var err error authData := oci.APIKeyAuth{ @@ -353,7 +353,7 @@ func (r *AutonomousDatabaseReconciler) setupOCIClients(logger logr.Logger, adb * return nil } -func (r *AutonomousDatabaseReconciler) manageError(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase, err error) (ctrl.Result, error) { +func (r *AutonomousDatabaseReconciler) manageError(logger logr.Logger, adb *dbv4.AutonomousDatabase, err error) (ctrl.Result, error) { l := logger.WithName("manageError") if adb.Status.LifecycleState == "" { // First time entering reconcile @@ -399,7 +399,7 @@ func (r *AutonomousDatabaseReconciler) manageError(logger logr.Logger, adb *dbv1 const CONDITION_TYPE_COMPLETE = "Complete" const CONDITION_REASON_COMPLETE = "ReconcileComplete" -func updateCondition(adb *dbv1alpha1.AutonomousDatabase, err error) { +func updateCondition(adb *dbv4.AutonomousDatabase, err error) { var condition metav1.Condition errMsg := func() string { @@ -420,7 +420,7 @@ func updateCondition(adb *dbv1alpha1.AutonomousDatabase, err error) { Message: errMsg, Status: metav1.ConditionTrue, } - } else if dbv1alpha1.IsADBIntermediateState(adb.Status.LifecycleState) { + } else if dbv4.IsADBIntermediateState(adb.Status.LifecycleState) { condition = metav1.Condition{ Type: CONDITION_TYPE_COMPLETE, LastTransitionTime: metav1.Now(), @@ -448,8 +448,8 @@ func updateCondition(adb *dbv1alpha1.AutonomousDatabase, err error) { func (r *AutonomousDatabaseReconciler) validateOperation( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (exit bool, result ctrl.Result, err error) { + adb *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (exit bool, result ctrl.Result, err error) { lastSucSpec, err := adb.GetLastSuccessfulSpec() if err != nil { @@ -546,7 +546,7 @@ func (r *AutonomousDatabaseReconciler) validateOperation( } } -func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) (exitReconcile bool, err error) { +func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb *dbv4.AutonomousDatabase) (exitReconcile bool, err error) { l := logger.WithName("validateCleanup") isADBToBeDeleted := adb.GetDeletionTimestamp() != nil @@ -555,7 +555,7 @@ func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb * return false, nil } - if controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADB_FINALIZER) { + if controllerutil.ContainsFinalizer(adb, dbv4.ADB_FINALIZER) { if adb.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminating { // Delete in progress, continue with the reconcile logic return false, nil @@ -565,7 +565,7 @@ func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb * // The adb has been deleted. Remove the finalizer and exit the reconcile. // Once all finalizers have been removed, the object will be deleted. l.Info("Resource is in TERMINATED state; remove the finalizer") - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv4.ADB_FINALIZER); err != nil { return false, err } return true, nil @@ -574,7 +574,7 @@ func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb * if adb.Spec.Details.AutonomousDatabaseOCID == nil { l.Info("Missing AutonomousDatabaseOCID to terminate Autonomous Database; remove the finalizer anyway", "Name", adb.Name, "Namespace", adb.Namespace) // Remove finalizer anyway. - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv4.ADB_FINALIZER); err != nil { return false, err } return true, nil @@ -600,24 +600,24 @@ func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb * return true, nil } -func (r *AutonomousDatabaseReconciler) validateFinalizer(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) (exit bool, err error) { +func (r *AutonomousDatabaseReconciler) validateFinalizer(logger logr.Logger, adb *dbv4.AutonomousDatabase) (exit bool, err error) { l := logger.WithName("validateFinalizer") // Delete is not schduled. Update the finalizer for this CR if hardLink is present var finalizerChanged = false if adb.Spec.HardLink != nil { - if *adb.Spec.HardLink && !controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADB_FINALIZER) { + if *adb.Spec.HardLink && !controllerutil.ContainsFinalizer(adb, dbv4.ADB_FINALIZER) { l.Info("Finalizer added") - if err := k8s.AddFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { + if err := k8s.AddFinalizerAndPatch(r.KubeClient, adb, dbv4.ADB_FINALIZER); err != nil { return false, err } finalizerChanged = true - } else if !*adb.Spec.HardLink && controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADB_FINALIZER) { + } else if !*adb.Spec.HardLink && controllerutil.ContainsFinalizer(adb, dbv4.ADB_FINALIZER) { l.Info("Finalizer removed") - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv4.ADB_FINALIZER); err != nil { return false, err } @@ -628,7 +628,7 @@ func (r *AutonomousDatabaseReconciler) validateFinalizer(logger logr.Logger, adb // If the finalizer is changed during an intermediate state, e.g. set hardLink to true and // delete the resource, then there must be another ongoing reconcile. In this case we should // exit the reconcile. - if finalizerChanged && dbv1alpha1.IsADBIntermediateState(adb.Status.LifecycleState) { + if finalizerChanged && dbv4.IsADBIntermediateState(adb.Status.LifecycleState) { l.Info("Finalizer changed during an intermediate state, exit the reconcile") return true, nil } @@ -637,7 +637,7 @@ func (r *AutonomousDatabaseReconciler) validateFinalizer(logger logr.Logger, adb } // updateCR updates the lastSucSpec and the CR -func (r *AutonomousDatabaseReconciler) updateCR(adb *dbv1alpha1.AutonomousDatabase) error { +func (r *AutonomousDatabaseReconciler) updateCR(adb *dbv4.AutonomousDatabase) error { // Update the lastSucSpec // Should patch the lastSuccessfulSpec first, otherwise, the update event will be // filtered out by predicate since the lastSuccessfulSpec is changed. @@ -651,7 +651,7 @@ func (r *AutonomousDatabaseReconciler) updateCR(adb *dbv1alpha1.AutonomousDataba return nil } -func (r *AutonomousDatabaseReconciler) patchLastSuccessfulSpec(adb *dbv1alpha1.AutonomousDatabase) error { +func (r *AutonomousDatabaseReconciler) patchLastSuccessfulSpec(adb *dbv4.AutonomousDatabase) error { copyADB := adb.DeepCopy() specBytes, err := json.Marshal(adb.Spec) @@ -659,8 +659,10 @@ func (r *AutonomousDatabaseReconciler) patchLastSuccessfulSpec(adb *dbv1alpha1.A return err } + specBytesString := string(specBytes) + anns := map[string]string{ - dbv1alpha1.LastSuccessfulSpec: string(specBytes), + dbv4.LastSuccessfulSpec: specBytesString, } annotations.PatchAnnotations(r.KubeClient, adb, anns) @@ -671,7 +673,7 @@ func (r *AutonomousDatabaseReconciler) patchLastSuccessfulSpec(adb *dbv1alpha1.A return nil } -func (r *AutonomousDatabaseReconciler) createADB(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) error { +func (r *AutonomousDatabaseReconciler) createADB(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { logger.WithName("createADB").Info("Sending CreateAutonomousDatabase request to OCI") resp, err := r.dbService.CreateAutonomousDatabase(adb) if err != nil { @@ -687,7 +689,7 @@ func (r *AutonomousDatabaseReconciler) createADB(logger logr.Logger, adb *dbv1al } // getADB gets the information from OCI and overwrites the spec and the status, but not update the CR in the cluster -func (r *AutonomousDatabaseReconciler) getADB(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) (bool, error) { +func (r *AutonomousDatabaseReconciler) getADB(logger logr.Logger, adb *dbv4.AutonomousDatabase) (bool, error) { if adb == nil { return false, errors.New("AutonomousDatabase OCID is missing") } @@ -710,7 +712,7 @@ func (r *AutonomousDatabaseReconciler) getADB(logger logr.Logger, adb *dbv1alpha // The AutonomousDatabase is updated with the returned object from the OCI requests. func (r *AutonomousDatabaseReconciler) updateADB( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase) (exit bool, err error) { + adb *dbv4.AutonomousDatabase) (exit bool, err error) { l := logger.WithName("updateADB") @@ -761,7 +763,7 @@ func (r *AutonomousDatabaseReconciler) updateADB( return exit, nil } - validations := []func(logr.Logger, *dbv1alpha1.AutonomousDatabase, *dbv1alpha1.AutonomousDatabase, *dbv1alpha1.AutonomousDatabase) (bool, error){ + validations := []func(logr.Logger, *dbv4.AutonomousDatabase, *dbv4.AutonomousDatabase, *dbv4.AutonomousDatabase) (bool, error){ r.validateGeneralFields, r.validateAdminPassword, r.validateDbWorkload, @@ -787,9 +789,9 @@ func (r *AutonomousDatabaseReconciler) updateADB( func (r *AutonomousDatabaseReconciler) validateGeneralFields( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - difADB *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + adb *dbv4.AutonomousDatabase, + difADB *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { if difADB.Spec.Details.DisplayName == nil && difADB.Spec.Details.DbName == nil && @@ -818,9 +820,9 @@ func (r *AutonomousDatabaseReconciler) validateGeneralFields( // Special case: compare with lastSpec but not ociSpec func (r *AutonomousDatabaseReconciler) validateAdminPassword( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - difADB *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + adb *dbv4.AutonomousDatabase, + difADB *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { if difADB.Spec.Details.AdminPassword.K8sSecret.Name == nil && difADB.Spec.Details.AdminPassword.OCISecret.OCID == nil { @@ -848,9 +850,9 @@ func (r *AutonomousDatabaseReconciler) validateAdminPassword( func (r *AutonomousDatabaseReconciler) validateDbWorkload( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - difADB *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + adb *dbv4.AutonomousDatabase, + difADB *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { if difADB.Spec.Details.DbWorkload == "" { return false, nil @@ -875,9 +877,9 @@ func (r *AutonomousDatabaseReconciler) validateDbWorkload( func (r *AutonomousDatabaseReconciler) validateLicenseModel( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - difADB *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + adb *dbv4.AutonomousDatabase, + difADB *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { if difADB.Spec.Details.LicenseModel == "" { return false, nil @@ -902,9 +904,9 @@ func (r *AutonomousDatabaseReconciler) validateLicenseModel( func (r *AutonomousDatabaseReconciler) validateScalingFields( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - difADB *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + adb *dbv4.AutonomousDatabase, + difADB *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { if difADB.Spec.Details.DataStorageSizeInTBs == nil && difADB.Spec.Details.CPUCoreCount == nil && @@ -931,9 +933,9 @@ func (r *AutonomousDatabaseReconciler) validateScalingFields( func (r *AutonomousDatabaseReconciler) validateDesiredLifecycleState( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - difADB *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, exit bool, err error) { + adb *dbv4.AutonomousDatabase, + difADB *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (sent bool, exit bool, err error) { if difADB.Spec.Details.LifecycleState == "" { return false, false, nil @@ -941,10 +943,10 @@ func (r *AutonomousDatabaseReconciler) validateDesiredLifecycleState( if difADB.Spec.Details.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated { // OCI only allows terminate operation when the ADB is in an valid state, otherwise requeue the reconcile. - if !dbv1alpha1.ValidADBTerminateState(adb.Status.LifecycleState) { + if !dbv4.ValidADBTerminateState(adb.Status.LifecycleState) { return false, false, nil } - } else if dbv1alpha1.IsADBIntermediateState(ociADB.Status.LifecycleState) { + } else if dbv4.IsADBIntermediateState(ociADB.Status.LifecycleState) { // Other lifecycle management operation; requeue the reconcile if it's in an intermediate state return false, false, nil } @@ -982,7 +984,7 @@ func (r *AutonomousDatabaseReconciler) validateDesiredLifecycleState( // The controller allows terminate during some intermediate states. // Exit the reconcile because there is already another ongoing reconcile. - if dbv1alpha1.IsADBIntermediateState(ociADB.Status.LifecycleState) { + if dbv4.IsADBIntermediateState(ociADB.Status.LifecycleState) { l.Info("Terminating an ADB which is in an intermediate state; exit reconcile") return true, true, nil } @@ -1014,9 +1016,9 @@ func (r *AutonomousDatabaseReconciler) validateDesiredLifecycleState( // Apply the configs directly func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - difADB *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + adb *dbv4.AutonomousDatabase, + difADB *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { if difADB.Spec.Details.NetworkAccess.AccessType == "" && difADB.Spec.Details.NetworkAccess.IsAccessControlEnabled == nil && @@ -1040,7 +1042,7 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( if difAccessType != "" { switch difAccessType { - case dbv1alpha1.NetworkAccessTypePublic: + case dbv4.NetworkAccessTypePublic: l.Info("Configuring network access type to PUBLIC") // OCI validation requires IsMTLSConnectionRequired to be enabled before changing the network access type to PUBLIC if !*ociADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired { @@ -1054,12 +1056,12 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( return false, err } return true, nil - case dbv1alpha1.NetworkAccessTypeRestricted: + case dbv4.NetworkAccessTypeRestricted: l.Info("Configuring network access type to RESTRICTED") // If the access type was PRIVATE, then OCI validation requires IsMTLSConnectionRequired // to be enabled before setting ACL. Also, we can only change the network access type from // PRIVATE to PUBLIC, so the steps are PRIVATE->(requeue)->PUBLIC->(requeue)->RESTRICTED. - if lastAccessType == dbv1alpha1.NetworkAccessTypePrivate { + if lastAccessType == dbv4.NetworkAccessTypePrivate { if !*ociADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired { if err := r.setMTLSRequired(logger, adb); err != nil { return false, err @@ -1088,7 +1090,7 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( if sent { return true, nil } - case dbv1alpha1.NetworkAccessTypePrivate: + case dbv4.NetworkAccessTypePrivate: l.Info("Configuring network access type to PRIVATE") sent, err := r.validateNetworkAccess(logger, adb, difADB, ociADB) @@ -1140,7 +1142,7 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( } // Set the mTLS to true but not changing the spec -func (r *AutonomousDatabaseReconciler) setMTLSRequired(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) error { +func (r *AutonomousDatabaseReconciler) setMTLSRequired(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { l := logger.WithName("setMTLSRequired") l.Info("Sending request to OCI to set IsMtlsConnectionRequired to true") @@ -1159,9 +1161,9 @@ func (r *AutonomousDatabaseReconciler) setMTLSRequired(logger logr.Logger, adb * func (r *AutonomousDatabaseReconciler) validateMTLS( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - difADB *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + adb *dbv4.AutonomousDatabase, + difADB *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { if difADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired == nil { return false, nil @@ -1181,8 +1183,8 @@ func (r *AutonomousDatabaseReconciler) validateMTLS( return true, nil } -func (r *AutonomousDatabaseReconciler) setNetworkAccessPublic(logger logr.Logger, lastAcessType dbv1alpha1.NetworkAccessTypeEnum, adb *dbv1alpha1.AutonomousDatabase) error { - adb.Spec.Details.NetworkAccess.AccessType = dbv1alpha1.NetworkAccessTypePublic +func (r *AutonomousDatabaseReconciler) setNetworkAccessPublic(logger logr.Logger, lastAcessType dbv4.NetworkAccessTypeEnum, adb *dbv4.AutonomousDatabase) error { + adb.Spec.Details.NetworkAccess.AccessType = dbv4.NetworkAccessTypePublic adb.Spec.Details.NetworkAccess.AccessControlList = nil adb.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = common.String("") adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil @@ -1204,9 +1206,9 @@ func (r *AutonomousDatabaseReconciler) setNetworkAccessPublic(logger logr.Logger func (r *AutonomousDatabaseReconciler) validateNetworkAccess( logger logr.Logger, - adb *dbv1alpha1.AutonomousDatabase, - difADB *dbv1alpha1.AutonomousDatabase, - ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + adb *dbv4.AutonomousDatabase, + difADB *dbv4.AutonomousDatabase, + ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { if difADB.Spec.Details.NetworkAccess.AccessType == "" && difADB.Spec.Details.NetworkAccess.IsAccessControlEnabled == nil && @@ -1222,7 +1224,7 @@ func (r *AutonomousDatabaseReconciler) validateNetworkAccess( l.Info("Sending request to OCI to configure network access options") // When the network access type is set to PRIVATE, any nil type of nsgOCIDs needs to be set to an empty string, otherwise, OCI SDK returns a 400 error - if difADB.Spec.Details.NetworkAccess.AccessType == dbv1alpha1.NetworkAccessTypePrivate && + if difADB.Spec.Details.NetworkAccess.AccessType == dbv4.NetworkAccessTypePrivate && difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs == nil { difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = []string{} } @@ -1237,7 +1239,7 @@ func (r *AutonomousDatabaseReconciler) validateNetworkAccess( return true, nil } -func (r *AutonomousDatabaseReconciler) validateWallet(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) error { +func (r *AutonomousDatabaseReconciler) validateWallet(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { if adb.Spec.Details.Wallet.Name == nil && adb.Spec.Details.Wallet.Password.K8sSecret.Name == nil && adb.Spec.Details.Wallet.Password.OCISecret.OCID == nil { @@ -1297,7 +1299,7 @@ func (r *AutonomousDatabaseReconciler) validateWallet(logger logr.Logger, adb *d // updateBackupResources get the list of AutonomousDatabasBackups and // create a backup object if it's not found in the same namespace -func (r *AutonomousDatabaseReconciler) syncBackupResources(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) error { +func (r *AutonomousDatabaseReconciler) syncBackupResources(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { l := logger.WithName("syncBackupResources") // Get the list of AutonomousDatabaseBackupOCID in the same namespace @@ -1370,7 +1372,7 @@ func (r *AutonomousDatabaseReconciler) getValidBackupName(displayName string, us return finalName, nil } -func (r *AutonomousDatabaseReconciler) ifBackupExists(backupSummary database.AutonomousDatabaseBackupSummary, curBackupOCIDs map[string]bool, backupList *dbv1alpha1.AutonomousDatabaseBackupList) bool { +func (r *AutonomousDatabaseReconciler) ifBackupExists(backupSummary database.AutonomousDatabaseBackupSummary, curBackupOCIDs map[string]bool, backupList *dbv4.AutonomousDatabaseBackupList) bool { _, ok := curBackupOCIDs[*backupSummary.Id] if ok { return true diff --git a/controllers/database/autonomousdatabasebackup_controller.go b/controllers/database/autonomousdatabasebackup_controller.go index 7fac9e04..0bd98067 100644 --- a/controllers/database/autonomousdatabasebackup_controller.go +++ b/controllers/database/autonomousdatabasebackup_controller.go @@ -54,7 +54,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/predicate" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/oracle/oracle-database-operator/commons/adb_family" "github.com/oracle/oracle-database-operator/commons/oci" ) @@ -72,7 +72,7 @@ type AutonomousDatabaseBackupReconciler struct { // SetupWithManager sets up the controller with the Manager. func (r *AutonomousDatabaseBackupReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&dbv1alpha1.AutonomousDatabaseBackup{}). + For(&dbv4.AutonomousDatabaseBackup{}). WithEventFilter(predicate.GenerationChangedPredicate{}). WithOptions(controller.Options{MaxConcurrentReconciles: 100}). // ReconcileHandler is never invoked concurrently with the same object. Complete(r) @@ -85,7 +85,7 @@ func (r *AutonomousDatabaseBackupReconciler) SetupWithManager(mgr ctrl.Manager) func (r *AutonomousDatabaseBackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := r.Log.WithValues("Namespace/Name", req.NamespacedName) - backup := &dbv1alpha1.AutonomousDatabaseBackup{} + backup := &dbv4.AutonomousDatabaseBackup{} if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, backup); err != nil { // Ignore not-found errors, since they can't be fixed by an immediate requeue. // No need to change the since we don't know if we obtain the object. @@ -136,7 +136,7 @@ func (r *AutonomousDatabaseBackupReconciler) Reconcile(ctx context.Context, req * No-op if the ADB OCID is nil * To get the latest status, execute before all the reconcile logic ******************************************************************/ - if dbv1alpha1.IsBackupIntermediateState(backup.Status.LifecycleState) { + if dbv4.IsBackupIntermediateState(backup.Status.LifecycleState) { logger.WithName("IsIntermediateState").Info("Current lifecycleState is " + string(backup.Status.LifecycleState) + "; reconcile queued") return requeueResult, nil } @@ -187,7 +187,7 @@ func (r *AutonomousDatabaseBackupReconciler) Reconcile(ctx context.Context, req return r.manageError(backup, err) } - if dbv1alpha1.IsBackupIntermediateState(backup.Status.LifecycleState) { + if dbv4.IsBackupIntermediateState(backup.Status.LifecycleState) { logger.WithName("IsIntermediateState").Info("Reconcile queued") return requeueResult, nil } @@ -198,7 +198,7 @@ func (r *AutonomousDatabaseBackupReconciler) Reconcile(ctx context.Context, req } // setOwnerAutonomousDatabase sets the owner of the AutonomousDatabaseBackup if the AutonomousDatabase resource with the same database OCID is found -func (r *AutonomousDatabaseBackupReconciler) setOwnerAutonomousDatabase(backup *dbv1alpha1.AutonomousDatabaseBackup, adb *dbv1alpha1.AutonomousDatabase) error { +func (r *AutonomousDatabaseBackupReconciler) setOwnerAutonomousDatabase(backup *dbv4.AutonomousDatabaseBackup, adb *dbv4.AutonomousDatabase) error { logger := r.Log.WithName("set-owner-reference") controllerutil.SetOwnerReference(adb, backup, r.Scheme) @@ -212,7 +212,7 @@ func (r *AutonomousDatabaseBackupReconciler) setOwnerAutonomousDatabase(backup * // verifyTargetADB searches if the target ADB is in the cluster, and set the owner reference to the ADB if it exists. // The function returns the OCID of the target ADB. -func (r *AutonomousDatabaseBackupReconciler) verifyTargetADB(backup *dbv1alpha1.AutonomousDatabaseBackup) (string, error) { +func (r *AutonomousDatabaseBackupReconciler) verifyTargetADB(backup *dbv4.AutonomousDatabaseBackup) (string, error) { // Get the target ADB OCID and the ADB resource ownerADB, err := adbfamily.VerifyTargetADB(r.KubeClient, backup.Spec.Target, backup.Namespace) @@ -237,7 +237,7 @@ func (r *AutonomousDatabaseBackupReconciler) verifyTargetADB(backup *dbv1alpha1. return "", errors.New("cannot get the OCID of the targetADB") } -func (r *AutonomousDatabaseBackupReconciler) setupOCIClients(backup *dbv1alpha1.AutonomousDatabaseBackup) error { +func (r *AutonomousDatabaseBackupReconciler) setupOCIClients(backup *dbv4.AutonomousDatabaseBackup) error { var err error authData := oci.APIKeyAuth{ @@ -259,7 +259,7 @@ func (r *AutonomousDatabaseBackupReconciler) setupOCIClients(backup *dbv1alpha1. return nil } -func (r *AutonomousDatabaseBackupReconciler) manageError(backup *dbv1alpha1.AutonomousDatabaseBackup, issue error) (ctrl.Result, error) { +func (r *AutonomousDatabaseBackupReconciler) manageError(backup *dbv4.AutonomousDatabaseBackup, issue error) (ctrl.Result, error) { // Send event r.Recorder.Event(backup, corev1.EventTypeWarning, "ReconcileFailed", issue.Error()) diff --git a/controllers/database/autonomousdatabaserestore_controller.go b/controllers/database/autonomousdatabaserestore_controller.go index 254731bb..f4579475 100644 --- a/controllers/database/autonomousdatabaserestore_controller.go +++ b/controllers/database/autonomousdatabaserestore_controller.go @@ -54,8 +54,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "github.com/oracle/oci-go-sdk/v65/common" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - "github.com/oracle/oracle-database-operator/commons/adb_family" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" + adbfamily "github.com/oracle/oracle-database-operator/commons/adb_family" "github.com/oracle/oracle-database-operator/commons/k8s" "github.com/oracle/oracle-database-operator/commons/oci" ) @@ -74,7 +74,7 @@ type AutonomousDatabaseRestoreReconciler struct { // SetupWithManager sets up the controller with the Manager. func (r *AutonomousDatabaseRestoreReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&dbv1alpha1.AutonomousDatabaseRestore{}). + For(&dbv4.AutonomousDatabaseRestore{}). WithEventFilter(predicate.GenerationChangedPredicate{}). Complete(r) } @@ -95,7 +95,7 @@ func (r *AutonomousDatabaseRestoreReconciler) SetupWithManager(mgr ctrl.Manager) func (r *AutonomousDatabaseRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := r.Log.WithValues("Namespace/Name", req.NamespacedName) - restore := &dbv1alpha1.AutonomousDatabaseRestore{} + restore := &dbv4.AutonomousDatabaseRestore{} if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, restore); err != nil { // Ignore not-found errors, since they can't be fixed by an immediate requeue. // No need to change since we don't know if we obtain the object. @@ -172,7 +172,7 @@ func (r *AutonomousDatabaseRestoreReconciler) Reconcile(ctx context.Context, req } // Requeue if it's in intermediate state - if dbv1alpha1.IsRestoreIntermediateState(restore.Status.Status) { + if dbv4.IsRestoreIntermediateState(restore.Status.Status) { logger.WithName("IsIntermediateState").Info("Current status is " + string(restore.Status.Status) + "; reconcile queued") return requeueResult, nil } @@ -182,9 +182,9 @@ func (r *AutonomousDatabaseRestoreReconciler) Reconcile(ctx context.Context, req return emptyResult, nil } -func (r *AutonomousDatabaseRestoreReconciler) getRestoreSDKTime(restore *dbv1alpha1.AutonomousDatabaseRestore) (*common.SDKTime, error) { +func (r *AutonomousDatabaseRestoreReconciler) getRestoreSDKTime(restore *dbv4.AutonomousDatabaseRestore) (*common.SDKTime, error) { if restore.Spec.Source.K8sADBBackup.Name != nil { // restore using backupName - backup := &dbv1alpha1.AutonomousDatabaseBackup{} + backup := &dbv4.AutonomousDatabaseBackup{} if err := k8s.FetchResource(r.KubeClient, restore.Namespace, *restore.Spec.Source.K8sADBBackup.Name, backup); err != nil { return nil, err } @@ -207,7 +207,7 @@ func (r *AutonomousDatabaseRestoreReconciler) getRestoreSDKTime(restore *dbv1alp } // setOwnerAutonomousDatabase sets the owner of the AutonomousDatabaseBackup if the AutonomousDatabase resource with the same database OCID is found -func (r *AutonomousDatabaseRestoreReconciler) setOwnerAutonomousDatabase(restore *dbv1alpha1.AutonomousDatabaseRestore, adb *dbv1alpha1.AutonomousDatabase) error { +func (r *AutonomousDatabaseRestoreReconciler) setOwnerAutonomousDatabase(restore *dbv4.AutonomousDatabaseRestore, adb *dbv4.AutonomousDatabase) error { logger := r.Log.WithName("set-owner-reference") controllerutil.SetOwnerReference(adb, restore, r.Scheme) @@ -221,7 +221,7 @@ func (r *AutonomousDatabaseRestoreReconciler) setOwnerAutonomousDatabase(restore // verifyTargetADB searches if the target ADB is in the cluster, and set the owner reference to the ADB if it exists. // The function returns the OCID of the target ADB. -func (r *AutonomousDatabaseRestoreReconciler) verifyTargetADB(restore *dbv1alpha1.AutonomousDatabaseRestore) (string, error) { +func (r *AutonomousDatabaseRestoreReconciler) verifyTargetADB(restore *dbv4.AutonomousDatabaseRestore) (string, error) { // Get the target ADB OCID and the ADB resource ownerADB, err := adbfamily.VerifyTargetADB(r.KubeClient, restore.Spec.Target, restore.Namespace) @@ -246,7 +246,7 @@ func (r *AutonomousDatabaseRestoreReconciler) verifyTargetADB(restore *dbv1alpha return "", errors.New("cannot get the OCID of the targetADB") } -func (r *AutonomousDatabaseRestoreReconciler) setupOCIClients(restore *dbv1alpha1.AutonomousDatabaseRestore) error { +func (r *AutonomousDatabaseRestoreReconciler) setupOCIClients(restore *dbv4.AutonomousDatabaseRestore) error { var err error authData := oci.APIKeyAuth{ @@ -274,7 +274,7 @@ func (r *AutonomousDatabaseRestoreReconciler) setupOCIClients(restore *dbv1alpha } // manageError doesn't return the error so that the request won't be requeued -func (r *AutonomousDatabaseRestoreReconciler) manageError(restore *dbv1alpha1.AutonomousDatabaseRestore, issue error) (ctrl.Result, error) { +func (r *AutonomousDatabaseRestoreReconciler) manageError(restore *dbv4.AutonomousDatabaseRestore, issue error) (ctrl.Result, error) { // Send event r.Recorder.Event(restore, corev1.EventTypeWarning, "ReconcileFailed", issue.Error()) diff --git a/main.go b/main.go index ff2c6275..94b6458e 100644 --- a/main.go +++ b/main.go @@ -248,6 +248,22 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") os.Exit(1) } + if err = (&databasev4.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") + os.Exit(1) + } + if err = (&databasev4.AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseBackup") + os.Exit(1) + } + if err = (&databasev4.AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseRestore") + os.Exit(1) + } + if err = (&databasev4.AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") + os.Exit(1) + } if err = (&databasev1alpha1.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") os.Exit(1) From 36103664ec63dd3fae81942095045d272256e457 Mon Sep 17 00:00:00 2001 From: racpack Date: Fri, 6 Dec 2024 00:58:58 +0000 Subject: [PATCH 130/414] Psaini v4api1 --- Makefile | 3 +- apis/database/v1alpha1/dbcssystem_types.go | 2 +- apis/database/v1alpha1/dbcssystem_webhook.go | 98 + .../v1alpha1/shardingdatabase_webhook.go | 270 ++ apis/database/v4/dbcssystem_types.go | 2 +- apis/database/v4/dbcssystem_webhook.go | 4 +- apis/database/v4/shardingdatabase_webhook.go | 4 +- .../bases/database.oracle.com_DbcsSystem.yaml | 762 ----- .../database.oracle.com_dbcssystems.yaml | 564 +++- config/crd/kustomization.yaml | 5 +- .../cainjection_in_database_dbcssystems.yaml | 8 - ...jection_in_database_shardingdatabases.yaml | 8 - .../patches/cainjection_in_dbcssystems.yaml | 2 +- config/database.oracle.com_DbcsSystem.yaml | 4 +- ...database.oracle.com_shardingdatabases.yaml | 4 +- ...tabase-operator.clusterserviceversion.yaml | 4 +- config/rbac/role.yaml | 3 + config/samples/database_v4_dbcssystem.yaml | 6 - .../samples/database_v4_shardingdatabase.yaml | 6 - config/samples/kustomization.yaml | 4 +- config/webhook/manifests.yaml | 122 + controllers/database/dbcssystem_controller.go | 18 +- main.go | 8 +- oracle-database-operator.yaml | 2618 +++++++++++------ 24 files changed, 2784 insertions(+), 1745 deletions(-) create mode 100644 apis/database/v1alpha1/dbcssystem_webhook.go create mode 100644 apis/database/v1alpha1/shardingdatabase_webhook.go delete mode 100644 config/crd/bases/database.oracle.com_DbcsSystem.yaml delete mode 100644 config/crd/patches/cainjection_in_database_dbcssystems.yaml delete mode 100644 config/crd/patches/cainjection_in_database_shardingdatabases.yaml delete mode 100644 config/samples/database_v4_dbcssystem.yaml delete mode 100644 config/samples/database_v4_shardingdatabase.yaml diff --git a/Makefile b/Makefile index ead6e157..d58d6525 100644 --- a/Makefile +++ b/Makefile @@ -77,10 +77,11 @@ BUILDER_IMG = oraclelinux:9 BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) --build-arg INSTALL_GO=true else BUILDER_IMG = golang:$(GOLANG_VERSION) +#BUILDER_IMG = phx.ocir.io/intsanjaysingh/db-repo/oracle/golang:1.23.3 BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO="false" --build-arg GOLANG_VERSION=$(GOLANG_VERSION) endif docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast - docker build --no-cache=true --platform=linux/arm64,linux/amd64 --jobs=2 --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ + docker build --no-cache=true --platform=linux/amd64 --jobs=2 --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ --build-arg CI_COMMIT_SHA=$(CI_COMMIT_SHA) --build-arg CI_COMMIT_BRANCH=$(CI_COMMIT_BRANCH) \ $(BUILD_ARGS) --manifest $(IMG) . diff --git a/apis/database/v1alpha1/dbcssystem_types.go b/apis/database/v1alpha1/dbcssystem_types.go index a59ab8d6..d49fde8c 100644 --- a/apis/database/v1alpha1/dbcssystem_types.go +++ b/apis/database/v1alpha1/dbcssystem_types.go @@ -200,7 +200,7 @@ type DbCloneStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status -// +kubebuilder:resource:path=DbcsSystem,scope=Namespaced +// +kubebuilder:resource:path=dbcssystems,scope=Namespaced // DbcsSystem is the Schema for the dbcssystems API type DbcsSystem struct { diff --git a/apis/database/v1alpha1/dbcssystem_webhook.go b/apis/database/v1alpha1/dbcssystem_webhook.go new file mode 100644 index 00000000..7c8f8957 --- /dev/null +++ b/apis/database/v1alpha1/dbcssystem_webhook.go @@ -0,0 +1,98 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var dbcssystemlog = logf.Log.WithName("dbcssystem-resource") + +func (r *DbcsSystem) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-dbcssystem,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=dbcssystems,verbs=create;update,versions=v4,name=mdbcssystemv1alpha1.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &DbcsSystem{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *DbcsSystem) Default() { + dbcssystemlog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. + +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv1alpha1.kb.io,admissionReviewVersions=v1 +var _ webhook.Validator = &DbcsSystem{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *DbcsSystem) ValidateCreate() (admission.Warnings, error) { + dbcssystemlog.Info("validate create", "name", r.Name) + + // // TODO(user): fill in your validation logic upon object creation. + return nil, nil +} + +// // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *DbcsSystem) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + dbcssystemlog.Info("validate update", "name", r.Name) + + // // TODO(user): fill in your validation logic upon object update. + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *DbcsSystem) ValidateDelete() (admission.Warnings, error) { + dbcssystemlog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} diff --git a/apis/database/v1alpha1/shardingdatabase_webhook.go b/apis/database/v1alpha1/shardingdatabase_webhook.go new file mode 100644 index 00000000..eb2e0145 --- /dev/null +++ b/apis/database/v1alpha1/shardingdatabase_webhook.go @@ -0,0 +1,270 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1alpha1 + +import ( + "strings" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var shardingdatabaselog = logf.Log.WithName("shardingdatabase-resource") + +func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v4,name=mshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &ShardingDatabase{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *ShardingDatabase) Default() { + shardingdatabaselog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. + if r.Spec.GsmDevMode != "" { + r.Spec.GsmDevMode = "dev" + } + + if r.Spec.IsTdeWallet == "" { + r.Spec.IsTdeWallet = "disable" + } + for pindex := range r.Spec.Shard { + if strings.ToLower(r.Spec.Shard[pindex].IsDelete) == "" { + r.Spec.Shard[pindex].IsDelete = "disable" + } + } + +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v4,name=vshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &ShardingDatabase{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { + shardingdatabaselog.Info("validate create", "name", r.Name) + + // TODO(user): fill in your validation logic upon object creation. + // Check Secret configuration + var validationErr field.ErrorList + var validationErrs1 field.ErrorList + + //namespaces := db.GetWatchNamespaces() + //_, containsNamespace := namespaces[r.Namespace] + // Check if the allowed namespaces maps contains the required namespace + // if len(namespaces) != 0 && !containsNamespace { + // validationErr = append(validationErr, + // field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + // "Oracle database operator doesn't watch over this namespace")) + //} + + if r.Spec.DbSecret == nil { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret"), r.Spec.DbSecret, + "DbSecret cannot be set to nil")) + } else { + if len(r.Spec.DbSecret.Name) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("Name"), r.Spec.DbSecret.Name, + "Secret name cannot be set empty")) + } + if len(r.Spec.DbSecret.PwdFileName) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileName"), r.Spec.DbSecret.PwdFileName, + "Password file name cannot be set empty")) + } + if strings.ToLower(r.Spec.DbSecret.EncryptionType) != "base64" { + if strings.ToLower(r.Spec.DbSecret.KeyFileName) == "" { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileName"), r.Spec.DbSecret.KeyFileName, + "Key file name cannot be empty")) + } + } + + /** + if len(r.Spec.DbSecret.PwdFileMountLocation) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileMountLocation"), r.Spec.DbSecret.PwdFileMountLocation, + "Password file mount location cannot be empty")) + } + + if len(r.Spec.DbSecret.KeyFileMountLocation) == 0 { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileMountLocation"), r.Spec.DbSecret.KeyFileMountLocation, + "KeyFileMountLocation file mount location cannot be empty")) + } + **/ + } + + if r.Spec.IsTdeWallet == "enable" { + if (len(r.Spec.FssStorageClass) == 0) && (len(r.Spec.TdeWalletPvc) == 0) { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("FssStorageClass"), r.Spec.FssStorageClass, + "FssStorageClass or TdeWalletPvc cannot be set empty if isTdeWallet set to true")) + + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("TdeWalletPvc"), r.Spec.TdeWalletPvc, + "FssStorageClass or TdeWalletPvc cannot be set empty if isTdeWallet set to true")) + } + } + + if r.Spec.IsTdeWallet != "" { + if (strings.ToLower(strings.TrimSpace(r.Spec.IsTdeWallet)) != "enable") && (strings.ToLower(strings.TrimSpace(r.Spec.IsTdeWallet)) != "disable") { + validationErr = append(validationErr, + field.Invalid(field.NewPath("spec").Child("isTdeWallet"), r.Spec.IsTdeWallet, + "isTdeWallet can be set to only \"enable\" or \"disable\"")) + } + } + + validationErrs1 = r.validateShardIsDelete() + if validationErrs1 != nil { + validationErr = append(validationErr, validationErrs1...) + } + + validationErrs1 = r.validateFreeEdition() + if validationErrs1 != nil { + validationErr = append(validationErr, validationErrs1...) + } + + // TODO(user): fill in your validation logic upon object creation. + if len(validationErr) == 0 { + return nil, nil + } + + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "ShardingDatabase"}, + r.Name, validationErr) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *ShardingDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + shardingdatabaselog.Info("validate update", "name", r.Name) + + // TODO(user): fill in your validation logic upon object update. + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *ShardingDatabase) ValidateDelete() (admission.Warnings, error) { + shardingdatabaselog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// ###### Vlaidation Block ################# + +func (r *ShardingDatabase) validateShardIsDelete() field.ErrorList { + + var validationErrs field.ErrorList + + for pindex := range r.Spec.Shard { + if (strings.ToLower(strings.TrimSpace(r.Spec.Shard[pindex].IsDelete)) != "enable") && (strings.ToLower(strings.TrimSpace(r.Spec.Shard[pindex].IsDelete)) != "disable") && (strings.ToLower(strings.TrimSpace(r.Spec.Shard[pindex].IsDelete)) != "failed") { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("shard").Child("isDelete"), r.Spec.Shard[pindex].IsDelete, + "r.Spec.Shard[pindex].IsDelete can be set to only enable|disable|failed")) + } + } + + if len(validationErrs) > 0 { + return validationErrs + } + return nil +} + +func (r *ShardingDatabase) validateFreeEdition() field.ErrorList { + + var validationErrs field.ErrorList + if strings.ToLower(r.Spec.DbEdition) == "free" { + // Shard Spec Checks + for i := 0; i < len(r.Spec.Shard); i++ { + for index, variable := range r.Spec.Shard[i].EnvVars { + if variable.Name == "ORACLE_SID" { + if strings.ToLower(variable.Value) != "free" { + validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("shard").Child("EnvVars"), r.Spec.Shard[i].EnvVars[index].Name, + "r.Spec.Shard[i].EnvVars[index].Name ORACLE_SID value can only be set to free")) + } + } + if variable.Name == "ORACLE_PDB" { + if strings.ToLower(variable.Value) != "freepdb" { + validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("shard").Child("EnvVars"), r.Spec.Shard[i].EnvVars[index].Name, + "r.Spec.Shard[i].EnvVars[index].Name ORACLE_PDB value can only be set to freepdb")) + } + } + } + } + // Catalog Spec Checks + for i := 0; i < len(r.Spec.Catalog); i++ { + for index, variable := range r.Spec.Catalog[i].EnvVars { + if variable.Name == "ORACLE_SID" { + if strings.ToLower(variable.Value) != "free" { + validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("catalog").Child("EnvVars"), r.Spec.Catalog[i].EnvVars[index].Name, + "r.Spec.Catalog[i].EnvVars[index].Name ORACLE_SID value can only be set to free")) + } + } + if variable.Name == "ORACLE_PDB" { + if strings.ToLower(variable.Value) != "freepdb" { + validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("catalog").Child("EnvVars"), r.Spec.Catalog[i].EnvVars[index].Name, + "r.Spec.Catalog[i].EnvVars[index].Name ORACLE_PDB value can only be set to freepdb")) + } + } + } + } + } + + if len(validationErrs) > 0 { + return validationErrs + } + return nil +} diff --git a/apis/database/v4/dbcssystem_types.go b/apis/database/v4/dbcssystem_types.go index 80501aaf..c51ea442 100644 --- a/apis/database/v4/dbcssystem_types.go +++ b/apis/database/v4/dbcssystem_types.go @@ -200,7 +200,7 @@ type DbCloneStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status -// +kubebuilder:resource:path=DbcsSystem,scope=Namespaced +// +kubebuilder:resource:path=dbcssystems,scope=Namespaced // +kubebuilder:storageversion // DbcsSystem is the Schema for the dbcssystems API diff --git a/apis/database/v4/dbcssystem_webhook.go b/apis/database/v4/dbcssystem_webhook.go index 68a0ab5a..1526deef 100644 --- a/apis/database/v4/dbcssystem_webhook.go +++ b/apis/database/v4/dbcssystem_webhook.go @@ -57,7 +57,7 @@ func (r *DbcsSystem) SetupWebhookWithManager(mgr ctrl.Manager) error { // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-dbcssystem,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=dbcssystems,verbs=create;update,versions=v4,name=mdbcssystem.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-dbcssystem,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=dbcssystems,verbs=create;update,versions=v4,name=mdbcssystemv4.kb.io,admissionReviewVersions=v1 var _ webhook.Defaulter = &DbcsSystem{} @@ -70,7 +70,7 @@ func (r *DbcsSystem) Default() { // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystem.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv4.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &DbcsSystem{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v4/shardingdatabase_webhook.go b/apis/database/v4/shardingdatabase_webhook.go index 496567d0..6669bb39 100644 --- a/apis/database/v4/shardingdatabase_webhook.go +++ b/apis/database/v4/shardingdatabase_webhook.go @@ -62,7 +62,7 @@ func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v4,name=mshardingdatabase.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v4,name=mshardingdatabasev4.kb.io,admissionReviewVersions=v1 var _ webhook.Defaulter = &ShardingDatabase{} @@ -87,7 +87,7 @@ func (r *ShardingDatabase) Default() { } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v4,name=vshardingdatabase.kb.io,admissionReviewVersions={v1} +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v4,name=vshardingdatabasev4.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &ShardingDatabase{} diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml deleted file mode 100644 index b4d696d3..00000000 --- a/config/crd/bases/database.oracle.com_DbcsSystem.yaml +++ /dev/null @@ -1,762 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - name: DbcsSystem.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DbcsSystem - listKind: DbcsSystemList - plural: DbcsSystem - singular: dbcssystem - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - databaseId: - type: string - dbBackupId: - type: string - dbClone: - properties: - dbAdminPaswordSecret: - type: string - dbDbUniqueName: - type: string - dbName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - privateIp: - type: string - sidPrefix: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - tdeWalletPasswordSecret: - type: string - required: - - dbDbUniqueName - - dbName - - displayName - - hostName - - subnetId - type: object - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - ociConfigMap: - type: string - ociSecret: - type: string - pdbConfigs: - items: - properties: - freeformTags: - additionalProperties: - type: string - type: object - isDelete: - type: boolean - pdbAdminPassword: - type: string - pdbName: - type: string - pluggableDatabaseId: - type: string - shouldPdbAdminAccountBeLocked: - type: boolean - tdeWalletPassword: - type: string - required: - - freeformTags - - pdbAdminPassword - - pdbName - - shouldPdbAdminAccountBeLocked - - tdeWalletPassword - type: object - type: array - setupDBCloning: - type: boolean - required: - - ociConfigMap - type: object - status: - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbCloneStatus: - properties: - dbAdminPaswordSecret: - type: string - dbDbUniqueName: - type: string - dbName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - id: - type: string - licenseModel: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - required: - - dbDbUniqueName - - hostName - type: object - dbEdition: - type: string - dbInfo: - items: - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - kmsDetailsStatus: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyId: - type: string - keyName: - type: string - managementEndpoint: - type: string - vaultId: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - pdbDetailsStatus: - items: - properties: - pdbConfigStatus: - items: - properties: - freeformTags: - additionalProperties: - type: string - type: object - pdbName: - type: string - pdbState: - type: string - pluggableDatabaseId: - type: string - shouldPdbAdminAccountBeLocked: - type: boolean - required: - - freeformTags - - pdbName - - shouldPdbAdminAccountBeLocked - type: object - type: array - type: object - type: array - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: false - subresources: - status: {} - - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - databaseId: - type: string - dbBackupId: - type: string - dbClone: - properties: - dbAdminPaswordSecret: - type: string - dbDbUniqueName: - type: string - dbName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - privateIp: - type: string - sidPrefix: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - tdeWalletPasswordSecret: - type: string - required: - - dbDbUniqueName - - dbName - - displayName - - hostName - - subnetId - type: object - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - ociConfigMap: - type: string - ociSecret: - type: string - pdbConfigs: - items: - properties: - freeformTags: - additionalProperties: - type: string - type: object - isDelete: - type: boolean - pdbAdminPassword: - type: string - pdbName: - type: string - pluggableDatabaseId: - type: string - shouldPdbAdminAccountBeLocked: - type: boolean - tdeWalletPassword: - type: string - required: - - freeformTags - - pdbAdminPassword - - pdbName - - shouldPdbAdminAccountBeLocked - - tdeWalletPassword - type: object - type: array - setupDBCloning: - type: boolean - required: - - ociConfigMap - type: object - status: - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbCloneStatus: - properties: - dbAdminPaswordSecret: - type: string - dbDbUniqueName: - type: string - dbName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - id: - type: string - licenseModel: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - required: - - dbDbUniqueName - - hostName - type: object - dbEdition: - type: string - dbInfo: - items: - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - kmsDetailsStatus: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyId: - type: string - keyName: - type: string - managementEndpoint: - type: string - vaultId: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - pdbDetailsStatus: - items: - properties: - pdbConfigStatus: - items: - properties: - freeformTags: - additionalProperties: - type: string - type: object - pdbName: - type: string - pdbState: - type: string - pluggableDatabaseId: - type: string - shouldPdbAdminAccountBeLocked: - type: boolean - required: - - freeformTags - - pdbName - - shouldPdbAdminAccountBeLocked - type: object - type: array - type: object - type: array - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index 3f4b1c46..2e656ce5 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -1,11 +1,9 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: dbcssystems.database.oracle.com spec: group: database.oracle.com @@ -19,23 +17,60 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: DbcsSystem is the Schema for the dbcssystems API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DbcsSystemSpec defines the desired state of DbcsSystem properties: + databaseId: + type: string + dbBackupId: + type: string + dbClone: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + tdeWalletPasswordSecret: + type: string + required: + - dbDbUniqueName + - dbName + - displayName + - hostName + - subnetId + type: object dbSystem: properties: availabilityDomain: @@ -51,7 +86,6 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup COnfig Network Struct properties: autoBackupEnabled: type: boolean @@ -88,12 +122,394 @@ spec: type: string initialDataStorageSizeInGB: type: integer + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + ociConfigMap: + type: string + ociSecret: + type: string + pdbConfigs: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + isDelete: + type: boolean + pdbAdminPassword: + type: string + pdbName: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + tdeWalletPassword: + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean + required: + - ociConfigMap + type: object + status: + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbCloneStatus: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + id: + type: string + licenseModel: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + required: + - dbDbUniqueName + - hostName + type: object + dbEdition: + type: string + dbInfo: + items: + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + kmsDetailsStatus: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyId: + type: string + keyName: + type: string + managementEndpoint: + type: string + vaultId: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + required: + - freeformTags + - pdbName + - shouldPdbAdminAccountBeLocked + type: object + type: array + type: object + type: array + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + databaseId: + type: string + dbBackupId: + type: string + dbClone: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + initialDataStorageSizeInGB: + type: integer kmsKeyId: type: string kmsKeyVersionId: type: string licenseModel: type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + tdeWalletPasswordSecret: + type: string + required: + - dbDbUniqueName + - dbName + - displayName + - hostName + - subnetId + type: object + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string nodeCount: type: integer pdbName: @@ -124,22 +540,62 @@ spec: - dbAdminPaswordSecret - hostName - shape - - sshPublicKeys - subnetId type: object hardLink: type: boolean id: type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object ociConfigMap: type: string ociSecret: type: string + pdbConfigs: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + isDelete: + type: boolean + pdbAdminPassword: + type: string + pdbName: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + tdeWalletPassword: + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean required: - ociConfigMap type: object status: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: availabilityDomain: type: string @@ -149,11 +605,38 @@ spec: type: integer dataStorageSizeInGBs: type: integer + dbCloneStatus: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + id: + type: string + licenseModel: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + required: + - dbDbUniqueName + - hostName + type: object dbEdition: type: string dbInfo: items: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: dbHomeId: type: string @@ -171,6 +654,25 @@ spec: type: string id: type: string + kmsDetailsStatus: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyId: + type: string + keyName: + type: string + managementEndpoint: + type: string + vaultId: + type: string + vaultName: + type: string + vaultType: + type: string + type: object licenseModel: type: string network: @@ -192,6 +694,32 @@ spec: type: object nodeCount: type: integer + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + required: + - freeformTags + - pdbName + - shouldPdbAdminAccountBeLocked + type: object + type: array + type: object + type: array recoStorageSizeInGB: type: integer shape: @@ -232,9 +760,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index ca4307d3..c42e9a76 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -42,11 +42,12 @@ patchesStrategicMerge: # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_provshards.yaml - patches/cainjection_in_singleinstancedatabases.yaml -#- patches/cainjection_in_shardingdatabases.yaml +- patches/cainjection_in_shardingdatabases.yaml - patches/cainjection_in_pdbs.yaml - patches/cainjection_in_cdbs.yaml #- patches/cainjection_in_oraclerestdataservices.yaml -#- patches/cainjection_in_dbcssystems.yaml +#- patches/cainjection_in_autonomouscontainerdatabases.yaml +- patches/cainjection_in_dbcssystems.yaml #- patches/cainjection_in_dataguardbrokers.yaml #- patches/cainjection_in_databaseobservers.yaml - patches/cainjection_in_autonomousdatabases.yaml diff --git a/config/crd/patches/cainjection_in_database_dbcssystems.yaml b/config/crd/patches/cainjection_in_database_dbcssystems.yaml deleted file mode 100644 index 9d8521ac..00000000 --- a/config/crd/patches/cainjection_in_database_dbcssystems.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: dbcssystems.database.oracle.com diff --git a/config/crd/patches/cainjection_in_database_shardingdatabases.yaml b/config/crd/patches/cainjection_in_database_shardingdatabases.yaml deleted file mode 100644 index 9817df6f..00000000 --- a/config/crd/patches/cainjection_in_database_shardingdatabases.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: shardingdatabases.database.oracle.com diff --git a/config/crd/patches/cainjection_in_dbcssystems.yaml b/config/crd/patches/cainjection_in_dbcssystems.yaml index 9d8521ac..1c14e1fd 100644 --- a/config/crd/patches/cainjection_in_dbcssystems.yaml +++ b/config/crd/patches/cainjection_in_dbcssystems.yaml @@ -1,6 +1,6 @@ # The following patch adds a directive for certmanager to inject CA into the CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: diff --git a/config/database.oracle.com_DbcsSystem.yaml b/config/database.oracle.com_DbcsSystem.yaml index e933d5a4..bde8744d 100644 --- a/config/database.oracle.com_DbcsSystem.yaml +++ b/config/database.oracle.com_DbcsSystem.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: DbcsSystem.database.oracle.com spec: @@ -16,7 +16,7 @@ spec: singular: dbcssystem scope: Namespaced versions: - - name: v1alpha1 + - name: v4 schema: openAPIV3Schema: description: DbcsSystem is the Schema for the dbcssystems API diff --git a/config/database.oracle.com_shardingdatabases.yaml b/config/database.oracle.com_shardingdatabases.yaml index 641629a0..bb9bbd38 100644 --- a/config/database.oracle.com_shardingdatabases.yaml +++ b/config/database.oracle.com_shardingdatabases.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: shardingdatabases.database.oracle.com spec: @@ -27,7 +27,7 @@ spec: name: shards priority: 1 type: string - name: v1alpha1 + name: v4 schema: openAPIV3Schema: description: ShardingDatabase is the Schema for the shardingdatabases API diff --git a/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml b/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml index 23cd7c00..933a2bfa 100644 --- a/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml @@ -16,7 +16,7 @@ spec: displayName: Dbcs System kind: DbcsSystem name: DbcsSystem.database.oracle.com - version: v1alpha1 + version: v4 - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API displayName: Autonomous Container Database @@ -70,7 +70,7 @@ spec: displayName: Sharding Database kind: ShardingDatabase name: shardingdatabases.database.oracle.com - version: v1alpha1 + version: v4 - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API displayName: Single Instance Database diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e904f893..ec3da481 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -86,6 +86,7 @@ rules: - autonomousdatabases - cdbs - dataguardbrokers + - dbcssystems - oraclerestdataservices - pdbs - shardingdatabases @@ -106,6 +107,7 @@ rules: - autonomousdatabaserestores/status - cdbs/status - dataguardbrokers/status + - dbcssystems/status - oraclerestdataservices/status - pdbs/status - shardingdatabases/status @@ -145,6 +147,7 @@ rules: - apiGroups: - database.oracle.com resources: + - dbcssystems/finalizers - pdbs/finalizers - shardingdatabases/finalizers verbs: diff --git a/config/samples/database_v4_dbcssystem.yaml b/config/samples/database_v4_dbcssystem.yaml deleted file mode 100644 index 6c75917c..00000000 --- a/config/samples/database_v4_dbcssystem.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: DbcsSystem -metadata: - name: dbcssystem-sample -spec: - # TODO(user): Add fields here diff --git a/config/samples/database_v4_shardingdatabase.yaml b/config/samples/database_v4_shardingdatabase.yaml deleted file mode 100644 index 095206b1..00000000 --- a/config/samples/database_v4_shardingdatabase.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample -spec: - # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 5b466084..1be7107d 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -30,6 +30,6 @@ resources: - adb/autonomousdatabase_backup.yaml - adb/autonomousdatabase_restore.yaml - acd/autonomouscontainerdatabase_restart_terminate.yaml -- database_v4_shardingdatabase.yaml -- database_v4_dbcssystem.yaml + - database_v4_shardingdatabase.yaml + - database_v4_dbcssystem.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 1ff969da..020a3bdb 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -166,6 +166,26 @@ webhooks: resources: - dataguardbrokers sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-dbcssystem + failurePolicy: Fail + name: mdbcssystemv1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - dbcssystems + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -208,6 +228,26 @@ webhooks: resources: - pdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: mshardingdatabasev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -229,6 +269,46 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-dbcssystem + failurePolicy: Fail + name: mdbcssystemv4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - dbcssystems + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: mshardingdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -520,6 +600,27 @@ webhooks: resources: - pdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: vshardingdatabasev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -542,6 +643,27 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: vshardingdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index 3fe3633e..7d9b646f 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -87,7 +87,8 @@ type DbcsSystemReconciler struct { // +kubebuilder:rbac:groups=database.oracle.com,resources=dbcssystems/status,verbs=get;update;patch // +kubebuilder:rbac:groups=database.oracle.com,resources=dbcssystems/finalizers,verbs=get;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=configmaps;secrets;namespaces,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:object:root=true + + // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // TODO(user): Modify the Reconcile function to compare the state specified by @@ -349,12 +350,21 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Check if cloning is needed, debugging // *dbcsInst.Status.DbCloneStatus.Id = "" setupCloning := false - if dbcsInst.Spec.SetupDBCloning && (dbcsInst.Spec.Id != nil || dbcsInst.Spec.DbBackupId != nil || dbcsInst.Spec.DatabaseId != nil) { + // Check if SetupDBCloning is true and ensure one of the required fields is provided + if dbcsInst.Spec.SetupDBCloning { + // If SetupDBCloning is true, at least one of Id, DbBackupId, or DatabaseId must be non-nil + if dbcsInst.Spec.Id == nil && dbcsInst.Spec.DbBackupId == nil && dbcsInst.Spec.DatabaseId == nil { + // If none of the required fields are set, log an error and exit the function + r.Logger.Error(err, "SetupDBCloning is defined but other necessary details (Id, DbBackupId, DatabaseId) are not present. Refer README.md file for instructions.") + return ctrl.Result{}, nil + } + // If the condition is met, proceed with cloning setup setupCloning = true } else { - r.Logger.Error(err, "SetupDBCloning is defined but other necessary details are not present. Refer README.md file for instructions.") - return ctrl.Result{}, nil + // If SetupDBCloning is false, continue as usual without cloning + setupCloning = false } + var dbSystemId string // Executing DB Cloning Process, if defined. Do not repeat cloning again when Status has Id present. if setupCloning && dbcsInst.Status.DbCloneStatus.Id == nil { diff --git a/main.go b/main.go index 94b6458e..35a9d5ae 100644 --- a/main.go +++ b/main.go @@ -275,6 +275,10 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") os.Exit(1) } + if err = (&databasev4.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } } // PDB Reconciler @@ -323,10 +327,6 @@ func main() { os.Exit(1) } - if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") - os.Exit(1) - } // +kubebuilder:scaffold:builder // Add index for PDB CR to enable mgr to cache PDBs diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 1a476814..b6e48604 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -9,7 +9,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: autonomouscontainerdatabases.database.oracle.com spec: group: database.oracle.com @@ -98,7 +98,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: autonomousdatabasebackups.database.oracle.com spec: group: database.oracle.com @@ -208,7 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: autonomousdatabaserestores.database.oracle.com spec: group: database.oracle.com @@ -313,7 +313,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: autonomousdatabases.database.oracle.com spec: group: database.oracle.com @@ -566,7 +566,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: cdbs.database.oracle.com spec: group: database.oracle.com @@ -788,7 +788,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: databaseobservers.observability.oracle.com spec: group: observability.oracle.com @@ -952,7 +952,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: dataguardbrokers.database.oracle.com spec: group: database.oracle.com @@ -1065,8 +1065,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 name: dbcssystems.database.oracle.com spec: group: database.oracle.com @@ -1080,23 +1080,60 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: DbcsSystem is the Schema for the dbcssystems API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DbcsSystemSpec defines the desired state of DbcsSystem properties: + databaseId: + type: string + dbBackupId: + type: string + dbClone: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + tdeWalletPasswordSecret: + type: string + required: + - dbDbUniqueName + - dbName + - displayName + - hostName + - subnetId + type: object dbSystem: properties: availabilityDomain: @@ -1112,7 +1149,6 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup COnfig Network Struct properties: autoBackupEnabled: type: boolean @@ -1149,10 +1185,19 @@ spec: type: string initialDataStorageSizeInGB: type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object licenseModel: type: string nodeCount: @@ -1185,22 +1230,62 @@ spec: - dbAdminPaswordSecret - hostName - shape - - sshPublicKeys - subnetId type: object hardLink: type: boolean id: type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object ociConfigMap: type: string ociSecret: type: string + pdbConfigs: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + isDelete: + type: boolean + pdbAdminPassword: + type: string + pdbName: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + tdeWalletPassword: + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean required: - ociConfigMap type: object status: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: availabilityDomain: type: string @@ -1210,11 +1295,38 @@ spec: type: integer dataStorageSizeInGBs: type: integer + dbCloneStatus: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + id: + type: string + licenseModel: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + required: + - dbDbUniqueName + - hostName + type: object dbEdition: type: string dbInfo: items: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: dbHomeId: type: string @@ -1232,6 +1344,25 @@ spec: type: string id: type: string + kmsDetailsStatus: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyId: + type: string + keyName: + type: string + managementEndpoint: + type: string + vaultId: + type: string + vaultName: + type: string + vaultType: + type: string + type: object licenseModel: type: string network: @@ -1253,6 +1384,32 @@ spec: type: object nodeCount: type: integer + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + required: + - freeformTags + - pdbName + - shouldPdbAdminAccountBeLocked + type: object + type: array + type: object + type: array recoStorageSizeInGB: type: integer shape: @@ -1290,51 +1447,10 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: oraclerestdataservices.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestDataService - listKind: OracleRestDataServiceList - plural: oraclerestdataservices - singular: oraclerestdataservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - - jsonPath: .status.mongoDbApiAccessUrl - name: MongoDbApi Access URL - type: string - name: v1alpha1 + - name: v4 schema: openAPIV3Schema: properties: @@ -1346,156 +1462,564 @@ spec: type: object spec: properties: - adminPassword: + databaseId: + type: string + dbBackupId: + type: string + dbClone: properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd + dbAdminPaswordSecret: type: string - secretName: + dbDbUniqueName: type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - properties: - pullFrom: + dbName: type: string - pullSecrets: + displayName: type: string - version: + domain: type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - mongoDbApi: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - oracleService: - type: string - ordsPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd + hostName: type: string - secretName: + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + tdeWalletPasswordSecret: type: string required: - - secretName + - dbDbUniqueName + - dbName + - displayName + - hostName + - subnetId type: object - ordsUser: - type: string - persistence: + dbSystem: properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany + availabilityDomain: type: string - size: + backupSubnetId: type: string - storageClass: + clusterName: type: string - volumeName: + compartmentId: type: string - type: object - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: - type: string - urlMapping: - type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - subnetId + type: object + hardLink: + type: boolean + id: type: string - serviceAnnotations: - additionalProperties: - type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string type: object + ociConfigMap: + type: string + ociSecret: + type: string + pdbConfigs: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + isDelete: + type: boolean + pdbAdminPassword: + type: string + pdbName: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + tdeWalletPassword: + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean required: - - adminPassword - - databaseRef - - ordsPassword + - ociConfigMap type: object status: properties: - apexConfigured: - type: boolean - apexUrl: + availabilityDomain: type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbCloneStatus: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + id: + type: string + licenseModel: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + required: + - dbDbUniqueName + - hostName + type: object + dbEdition: type: string - databaseApiUrl: + dbInfo: + items: + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: type: string - databaseRef: + id: type: string - image: + kmsDetailsStatus: properties: - pullFrom: + compartmentId: type: string - pullSecrets: + encryptionAlgo: type: string - version: + keyId: + type: string + keyName: + type: string + managementEndpoint: + type: string + vaultId: + type: string + vaultName: + type: string + vaultType: type: string - required: - - pullFrom type: object - loadBalancer: - type: string - mongoDbApi: - type: boolean - mongoDbApiAccessUrl: + licenseModel: type: string - ordsInstalled: - type: boolean - replicas: + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: type: integer - serviceIP: + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + required: + - freeformTags + - pdbName + - shouldPdbAdminAccountBeLocked + type: object + type: array + type: object + type: array + recoStorageSizeInGB: + type: integer + shape: type: string - status: + state: type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: pdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: oraclerestdataservices.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestDataService + listKind: OracleRestDataServiceList + plural: oraclerestdataservices + singular: oraclerestdataservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + - jsonPath: .status.mongoDbApiAccessUrl + name: MongoDbApi Access URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + mongoDbApi: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + mongoDbApi: + type: boolean + mongoDbApiAccessUrl: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: pdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB listKind: PDBList plural: pdbs singular: pdb @@ -1785,7 +2309,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 name: shardingdatabases.database.oracle.com spec: group: database.oracle.com @@ -2129,7 +2654,6 @@ spec: format: int32 type: integer protocol: - default: TCP type: string targetPort: format: int32 @@ -2330,61 +2854,21 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - additionalPrinterColumns: - - jsonPath: .status.edition - name: Edition + - jsonPath: .status.gsm.state + name: Gsm State type: string - - jsonPath: .status.sid - name: Sid - priority: 1 + - jsonPath: .status.gsm.services + name: Services type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version - type: string - - jsonPath: .status.connectString - name: Connect Str - type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.tcpsConnectString - name: TCPS Connect Str - type: string - - jsonPath: .status.tcpsPdbConnectString - name: TCPS Pdb Connect Str + - jsonPath: .status.gsm.shards + name: shards priority: 1 type: string - - jsonPath: .status.oemExpressUrl - name: Oem Express Url - type: string - name: v1alpha1 + name: v4 schema: openAPIV3Schema: properties: @@ -2396,681 +2880,945 @@ spec: type: object spec: properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - archiveLog: - type: boolean - charset: + InvitedNodeSubnet: type: string - convertToSnapshotStandby: - type: boolean - createAs: - enum: - - primary - - standby - - clone + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbEdition: type: string - edition: - enum: - - standard - - enterprise - - express - - free + dbImage: type: string - enableTCPS: - type: boolean - flashBack: - type: boolean - forceLog: - type: boolean - image: + dbImagePullSecret: + type: string + dbSecret: properties: - prebuiltDB: - type: boolean - pullFrom: + encryptionType: type: string - pullSecrets: + keyFileMountLocation: type: string - version: + keyFileName: type: string - required: - - pullFrom - type: object - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - listenerPort: - type: integer - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - pdbName: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany + keySecretName: type: string - datafilesVolumeName: + name: type: string - scriptsVolumeName: + nsConfigMap: type: string - setWritePermissions: - type: boolean - size: + nsSecret: type: string - storageClass: + pwdFileMountLocation: type: string - volumeClaimAnnotation: + pwdFileName: type: string + required: + - name + - pwdFileName type: object - primaryDatabaseRef: + fssStorageClass: type: string - readinessCheckPeriod: - type: integer - replicas: - type: integer - resources: - properties: - limits: - properties: - cpu: - type: string - memory: - type: string - type: object - requests: - properties: - cpu: + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: type: string - memory: + type: object + pvMatchLabels: + additionalProperties: type: string - type: object - type: object - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - sid: - maxLength: 12 - pattern: ^[a-zA-Z0-9]+$ - type: string - tcpsCertRenewInterval: - type: string - tcpsListenerPort: - type: integer - tcpsTlsSecret: - type: string - required: - - image - type: object - status: - properties: - apexInstalled: - type: boolean - archiveLog: - type: string - certCreationTimestamp: - type: string - certRenewInterval: - type: string - charset: + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: type: string - clientWalletLoc: + gsmImage: type: string - clusterConnectString: + gsmImagePullSecret: type: string - conditions: + gsmService: items: properties: - lastTransitionTime: - format: date-time + available: type: string - message: - maxLength: 32768 + clbGoal: type: string - observedGeneration: - format: int64 - minimum: 0 + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + locality: type: string - status: - enum: - - "True" - - "False" - - Unknown + name: type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: type: string required: - - lastTransitionTime - - message - - reason - - status - - type + - name type: object type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - connectString: + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: type: string - convertToSnapshotStandby: + isClone: type: boolean - createdAs: - type: string - datafilesCreated: - default: "false" - type: string - datafilesPatched: - default: "false" - type: string - dgBroker: - type: string - edition: - type: string - flashBack: - type: string - forceLog: + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: type: string - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - initPgaSize: - type: integer - initSgaSize: + liveinessCheckPeriod: type: integer - isTcpsEnabled: - default: false - type: boolean - nodes: + namespace: + type: string + portMappings: items: - type: string + properties: + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object type: array - oemExpressUrl: - type: string - ordsReference: + readinessCheckPeriod: + type: integer + replicationType: type: string - pdbConnectString: + scriptsLocation: type: string - pdbName: + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + enum: + - enable + - disable + - failed + - force + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: type: string - persistence: + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: + details: + additionalProperties: + type: string + type: object + externalConnectStr: type: string - setWritePermissions: - type: boolean - size: + internalConnectStr: type: string - storageClass: + services: type: string - volumeClaimAnnotation: + shards: + additionalProperties: + type: string + type: object + state: type: string type: object - prebuiltDB: - type: boolean - primaryDatabase: - type: string - releaseUpdate: - type: string - replicas: - type: integer - role: - type: string - sid: - type: string - standbyDatabases: + shards: additionalProperties: type: string type: object - status: - type: string - tcpsConnectString: - type: string - tcpsPdbConnectString: - type: string - tcpsTlsSecret: - default: "" - type: string - required: - - isTcpsEnabled - - persistence - - tcpsTlsSecret type: object type: object served: true storage: true subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.replicas status: {} --- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: - name: oracle-database-operator-leader-election-role - namespace: oracle-database-operator-system -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - deployments - - events - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch -- apiGroups: - - '''''' - resources: - - statefulsets/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - configmaps - verbs: - - get - - list -- apiGroups: - - apps - resources: - - deployments - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - replicasets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - list - - update -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods/exec - verbs: - - create -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores/status - verbs: - - get - - patch - - update + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + convertToSnapshotStandby: + type: boolean + createAs: + enum: + - primary + - standby + - clone + type: string + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + resources: + properties: + limits: + properties: + cpu: + type: string + memory: + type: string + type: object + requests: + properties: + cpu: + type: string + memory: + type: string + type: object + type: object + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + required: + - image + type: object + status: + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + convertToSnapshotStandby: + type: boolean + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBroker: + type: string + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: oracle-database-operator-leader-election-role + namespace: oracle-database-operator-system +rules: - apiGroups: - - database.oracle.com + - "" resources: - - autonomousdatabases + - configmaps verbs: - - create - - delete - get - list - - patch - - update - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases/status - verbs: - - patch + - create - update + - patch + - delete - apiGroups: - - database.oracle.com + - coordination.k8s.io resources: - - cdbs + - leases verbs: - - create - - delete - get - list - - patch - - update - watch + - create + - update + - patch + - delete - apiGroups: - - database.oracle.com + - "" resources: - - cdbs/finalizers + - configmaps/status verbs: + - get - update + - patch - apiGroups: - - database.oracle.com + - "" resources: - - cdbs/status + - events verbs: - - get + - create - patch - - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role +rules: - apiGroups: - - database.oracle.com + - "" resources: - - dataguardbrokers + - configmaps + - containers + - deployments + - events + - namespaces + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - replicasets + - secrets + - services verbs: - create - delete @@ -3080,53 +3828,40 @@ rules: - update - watch - apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com + - "" resources: - - dbcssystems + - persistentvolumes verbs: - - create - - delete - get - list - - patch - - update - watch - apiGroups: - - database.oracle.com + - '''''' resources: - - dbcssystems/finalizers + - statefulsets/finalizers verbs: - create - delete - get + - list - patch - update + - watch - apiGroups: - - database.oracle.com + - apps resources: - - dbcssystems/status + - configmaps verbs: - get - - patch - - update + - list - apiGroups: - - database.oracle.com + - apps resources: - - oraclerestdataservices + - deployments + - pods + - replicasets + - services + - statefulsets verbs: - create - delete @@ -3136,23 +3871,26 @@ rules: - update - watch - apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com + - coordination.k8s.io resources: - - oraclerestdataservices/status + - leases verbs: + - create - get - - patch + - list - update - apiGroups: - database.oracle.com resources: + - autonomouscontainerdatabases + - autonomousdatabases + - cdbs + - dataguardbrokers + - dbcssystems + - oraclerestdataservices - pdbs + - shardingdatabases + - singleinstancedatabases verbs: - create - delete @@ -3164,17 +3902,16 @@ rules: - apiGroups: - database.oracle.com resources: - - pdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: + - autonomouscontainerdatabases/status + - autonomousdatabasebackups/status + - autonomousdatabaserestores/status + - cdbs/status + - dataguardbrokers/status + - dbcssystems/status + - oraclerestdataservices/status - pdbs/status + - shardingdatabases/status + - singleinstancedatabases/status verbs: - get - patch @@ -3182,57 +3919,41 @@ rules: - apiGroups: - database.oracle.com resources: - - shardingdatabases + - autonomousdatabasebackups + - autonomousdatabaserestores verbs: - create - delete - get - list - - patch - update - watch - apiGroups: - database.oracle.com resources: - - shardingdatabases/finalizers + - autonomousdatabases/status verbs: - - create - - delete - - get - patch - update - apiGroups: - database.oracle.com resources: - - shardingdatabases/status + - cdbs/finalizers + - dataguardbrokers/finalizers + - oraclerestdataservices/finalizers + - singleinstancedatabases/finalizers verbs: - - get - - patch - update - apiGroups: - database.oracle.com resources: - - singleinstancedatabases + - dbcssystems/finalizers + - pdbs/finalizers + - shardingdatabases/finalizers verbs: - create - delete - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/status - verbs: - - get - patch - update - apiGroups: @@ -3490,6 +4211,26 @@ webhooks: resources: - dataguardbrokers sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-dbcssystem + failurePolicy: Fail + name: mdbcssystemv1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - dbcssystems + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -3538,14 +4279,14 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-shardingdatabase + path: /mutate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: mshardingdatabase.kb.io + name: mshardingdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE @@ -3573,6 +4314,46 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-dbcssystem + failurePolicy: Fail + name: mdbcssystemv4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - dbcssystems + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: mshardingdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -3771,14 +4552,14 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-shardingdatabase + path: /validate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: vshardingdatabase.kb.io + name: vshardingdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE @@ -3808,6 +4589,27 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: vshardingdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 clientConfig: From dd271e5509dfd5ad8ee33d860451a54ebaa9d7b4 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Fri, 6 Dec 2024 03:04:53 +0000 Subject: [PATCH 131/414] Added fixes for sharding --- .../v1alpha1/shardingdatabase_types.go | 25 +- .../v1alpha1/shardingdatabase_webhook.go | 48 ++- apis/database/v4/dbcssystem_types.go | 1 + apis/database/v4/shardingdatabase_types.go | 28 +- apis/database/v4/shardingdatabase_webhook.go | 46 ++- commons/sharding/catalog.go | 43 +- commons/sharding/exec.go | 32 +- commons/sharding/gsm.go | 28 +- commons/sharding/provstatus.go | 5 +- commons/sharding/scommon.go | 61 ++- commons/sharding/shard.go | 20 +- .../database/shardingdatabase_controller.go | 366 +++++++++--------- 12 files changed, 419 insertions(+), 284 deletions(-) diff --git a/apis/database/v1alpha1/shardingdatabase_types.go b/apis/database/v1alpha1/shardingdatabase_types.go index ffc17ab0..ae9066fc 100644 --- a/apis/database/v1alpha1/shardingdatabase_types.go +++ b/apis/database/v1alpha1/shardingdatabase_types.go @@ -68,7 +68,6 @@ type ShardingDatabaseSpec struct { GsmImagePullSecret string `json:"gsmImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. StagePvcName string `json:"stagePvcName,omitempty"` // the Stagepvc for the backup of cluster PortMappings []PortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least - Namespace string `json:"namespace,omitempty"` // Target namespace of the application. IsDebug bool `json:"isDebug,omitempty"` // Optional parameter to enable logining IsExternalSvc bool `json:"isExternalSvc,omitempty"` IsClone bool `json:"isClone,omitempty"` @@ -95,6 +94,7 @@ type ShardingDatabaseSpec struct { FssStorageClass string `json:"fssStorageClass,omitempty"` TdeWalletPvcMountLocation string `json:"tdeWalletPvcMountLocation,omitempty"` DbEdition string `json:"dbEdition,omitempty"` + TopicId string `json:"topicId,omitempty"` } // To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 @@ -215,6 +215,7 @@ type GsmSpec struct { Label string `json:"label,omitempty"` // Optional GSM Label IsDelete string `json:"isDelete,omitempty"` NodeSelector map[string]string `json:"nodeSelector,omitempty"` + PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` Region string `json:"region,omitempty"` @@ -364,6 +365,7 @@ const ( var KubeConfigOnce sync.Once // #const lastSuccessfulSpec = "lastSuccessfulSpec" +const lastSuccessfulSpecOnsInfo = "lastSuccessfulSpeOnsInfo" // GetLastSuccessfulSpec returns spec from the lass successful reconciliation. // Returns nil, nil if there is no lastSuccessfulSpec. @@ -398,6 +400,27 @@ func (shardingv1 *ShardingDatabase) UpdateLastSuccessfulSpec(kubeClient client.C return annsv1.PatchAnnotations(kubeClient, shardingv1, anns) } +// GetLastSuccessfulOnsInfo returns spec from the lass successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulSpec. +func (shardingv1 *ShardingDatabase) GetLastSuccessfulOnsInfo() ([]byte, error) { + val, ok := shardingv1.GetAnnotations()[lastSuccessfulSpecOnsInfo] + if !ok { + return nil, nil + } + specBytes := []byte(val) + return specBytes, nil +} + +// UpdateLastSuccessfulSpec updates lastSuccessfulSpec with the current spec. +func (shardingv1 *ShardingDatabase) UpdateLastSuccessfulSpecOnsInfo(kubeClient client.Client, specBytes []byte) error { + + anns := map[string]string{ + lastSuccessfulSpecOnsInfo: string(specBytes), + } + + return annsv1.PatchAnnotations(kubeClient, shardingv1, anns) +} + func init() { SchemeBuilder.Register(&ShardingDatabase{}, &ShardingDatabaseList{}) } diff --git a/apis/database/v1alpha1/shardingdatabase_webhook.go b/apis/database/v1alpha1/shardingdatabase_webhook.go index eb2e0145..4e7ea2e7 100644 --- a/apis/database/v1alpha1/shardingdatabase_webhook.go +++ b/apis/database/v1alpha1/shardingdatabase_webhook.go @@ -62,7 +62,7 @@ func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v4,name=mshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v1alpha1,name=mshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Defaulter = &ShardingDatabase{} @@ -87,7 +87,7 @@ func (r *ShardingDatabase) Default() { } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v4,name=vshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v1alpha1-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v1alpha1,name=vshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &ShardingDatabase{} @@ -177,6 +177,16 @@ func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { validationErr = append(validationErr, validationErrs1...) } + validationErrs1 = r.validateCatalogName() + if validationErrs1 != nil { + validationErr = append(validationErr, validationErrs1...) + } + + validationErrs1 = r.validateShardName() + if validationErrs1 != nil { + validationErr = append(validationErr, validationErrs1...) + } + // TODO(user): fill in your validation logic upon object creation. if len(validationErr) == 0 { return nil, nil @@ -268,3 +278,37 @@ func (r *ShardingDatabase) validateFreeEdition() field.ErrorList { } return nil } + +func (r *ShardingDatabase) validateShardName() field.ErrorList { + var validationErrs field.ErrorList + + for pindex := range r.Spec.Shard { + if len(r.Spec.Shard[pindex].Name) > 9 { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("shard").Child("Name"), r.Spec.Shard[pindex].Name, + "Shard Name cannot be greater than 9 characters.")) + } + } + + if len(validationErrs) > 0 { + return validationErrs + } + return nil +} + +func (r *ShardingDatabase) validateCatalogName() field.ErrorList { + var validationErrs field.ErrorList + + for pindex := range r.Spec.Catalog { + if len(r.Spec.Catalog[pindex].Name) > 9 { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("catalog").Child("Name"), r.Spec.Catalog[pindex].Name, + "Catalog Name cannot be greater than 9 characters.")) + } + } + + if len(validationErrs) > 0 { + return validationErrs + } + return nil +} diff --git a/apis/database/v4/dbcssystem_types.go b/apis/database/v4/dbcssystem_types.go index c51ea442..c8b6d632 100644 --- a/apis/database/v4/dbcssystem_types.go +++ b/apis/database/v4/dbcssystem_types.go @@ -202,6 +202,7 @@ type DbCloneStatus struct { // +kubebuilder:subresource:status // +kubebuilder:resource:path=dbcssystems,scope=Namespaced // +kubebuilder:storageversion + // +kubebuilder:storageversion // DbcsSystem is the Schema for the dbcssystems API type DbcsSystem struct { diff --git a/apis/database/v4/shardingdatabase_types.go b/apis/database/v4/shardingdatabase_types.go index ee7b981e..c312f419 100644 --- a/apis/database/v4/shardingdatabase_types.go +++ b/apis/database/v4/shardingdatabase_types.go @@ -68,7 +68,6 @@ type ShardingDatabaseSpec struct { GsmImagePullSecret string `json:"gsmImagePullSecret,omitempty"` // Optional The name of an image pull secret in case of a private docker repository. StagePvcName string `json:"stagePvcName,omitempty"` // the Stagepvc for the backup of cluster PortMappings []PortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least - Namespace string `json:"namespace,omitempty"` // Target namespace of the application. IsDebug bool `json:"isDebug,omitempty"` // Optional parameter to enable logining IsExternalSvc bool `json:"isExternalSvc,omitempty"` IsClone bool `json:"isClone,omitempty"` @@ -95,6 +94,7 @@ type ShardingDatabaseSpec struct { FssStorageClass string `json:"fssStorageClass,omitempty"` TdeWalletPvcMountLocation string `json:"tdeWalletPvcMountLocation,omitempty"` DbEdition string `json:"dbEdition,omitempty"` + TopicId string `json:"topicId,omitempty"` } // To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 @@ -149,7 +149,6 @@ type GsmStatusDetails struct { // ShardingDatabase is the Schema for the shardingdatabases API // +kubebuilder:resource:path=shardingdatabases,scope=Namespaced // +kubebuilder:storageversion - // +kubebuilder:storageversion type ShardingDatabase struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -217,6 +216,7 @@ type GsmSpec struct { Label string `json:"label,omitempty"` // Optional GSM Label IsDelete string `json:"isDelete,omitempty"` NodeSelector map[string]string `json:"nodeSelector,omitempty"` + PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` Region string `json:"region,omitempty"` @@ -365,7 +365,8 @@ const ( // var var KubeConfigOnce sync.Once -//const lastSuccessfulSpec = "lastSuccessfulSpec" +// #const lastSuccessfulSpec = "lastSuccessfulSpec" +const lastSuccessfulSpecOnsInfo = "lastSuccessfulSpeOnsInfo" // GetLastSuccessfulSpec returns spec from the lass successful reconciliation. // Returns nil, nil if there is no lastSuccessfulSpec. @@ -400,6 +401,27 @@ func (shardingv1 *ShardingDatabase) UpdateLastSuccessfulSpec(kubeClient client.C return annsv1.PatchAnnotations(kubeClient, shardingv1, anns) } +// GetLastSuccessfulOnsInfo returns spec from the lass successful reconciliation. +// Returns nil, nil if there is no lastSuccessfulSpec. +func (shardingv1 *ShardingDatabase) GetLastSuccessfulOnsInfo() ([]byte, error) { + val, ok := shardingv1.GetAnnotations()[lastSuccessfulSpecOnsInfo] + if !ok { + return nil, nil + } + specBytes := []byte(val) + return specBytes, nil +} + +// UpdateLastSuccessfulSpec updates lastSuccessfulSpec with the current spec. +func (shardingv1 *ShardingDatabase) UpdateLastSuccessfulSpecOnsInfo(kubeClient client.Client, specBytes []byte) error { + + anns := map[string]string{ + lastSuccessfulSpecOnsInfo: string(specBytes), + } + + return annsv1.PatchAnnotations(kubeClient, shardingv1, anns) +} + func init() { SchemeBuilder.Register(&ShardingDatabase{}, &ShardingDatabaseList{}) } diff --git a/apis/database/v4/shardingdatabase_webhook.go b/apis/database/v4/shardingdatabase_webhook.go index 6669bb39..1ac74d08 100644 --- a/apis/database/v4/shardingdatabase_webhook.go +++ b/apis/database/v4/shardingdatabase_webhook.go @@ -87,7 +87,7 @@ func (r *ShardingDatabase) Default() { } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v4,name=vshardingdatabasev4.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v4,name=vshardingdatabasev4.kb.io,admissionReviewVersions={v1} var _ webhook.Validator = &ShardingDatabase{} @@ -177,6 +177,16 @@ func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { validationErr = append(validationErr, validationErrs1...) } + validationErrs1 = r.validateCatalogName() + if validationErrs1 != nil { + validationErr = append(validationErr, validationErrs1...) + } + + validationErrs1 = r.validateShardName() + if validationErrs1 != nil { + validationErr = append(validationErr, validationErrs1...) + } + // TODO(user): fill in your validation logic upon object creation. if len(validationErr) == 0 { return nil, nil @@ -268,3 +278,37 @@ func (r *ShardingDatabase) validateFreeEdition() field.ErrorList { } return nil } + +func (r *ShardingDatabase) validateShardName() field.ErrorList { + var validationErrs field.ErrorList + + for pindex := range r.Spec.Shard { + if len(r.Spec.Shard[pindex].Name) > 9 { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("shard").Child("Name"), r.Spec.Shard[pindex].Name, + "Shard Name cannot be greater than 9 characters.")) + } + } + + if len(validationErrs) > 0 { + return validationErrs + } + return nil +} + +func (r *ShardingDatabase) validateCatalogName() field.ErrorList { + var validationErrs field.ErrorList + + for pindex := range r.Spec.Catalog { + if len(r.Spec.Catalog[pindex].Name) > 9 { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("catalog").Child("Name"), r.Spec.Catalog[pindex].Name, + "Catalog Name cannot be greater than 9 characters.")) + } + } + + if len(validationErrs) > 0 { + return validationErrs + } + return nil +} diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index 96190250..063dc13f 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -53,7 +53,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func buildLabelsForCatalog(instance *databasev4.ShardingDatabase, label string) map[string]string { +func buildLabelsForCatalog(instance *databasev4.ShardingDatabase, label string, catalogName string) map[string]string { return map[string]string{ "app": "OracleSharding", "type": "Catalog", @@ -95,9 +95,9 @@ func builObjectMetaForCatalog(instance *databasev4.ShardingDatabase, OraCatalogS // building objectMeta objmeta := metav1.ObjectMeta{ Name: OraCatalogSpex.Name, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, OwnerReferences: getOwnerRef(instance), - Labels: buildLabelsForCatalog(instance, "sharding"), + Labels: buildLabelsForCatalog(instance, "sharding", OraCatalogSpex.Name), } return objmeta } @@ -109,11 +109,11 @@ func buildStatefulSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatal sfsetspec := &appsv1.StatefulSetSpec{ ServiceName: OraCatalogSpex.Name, Selector: &metav1.LabelSelector{ - MatchLabels: buildLabelsForCatalog(instance, "sharding"), + MatchLabels: buildLabelsForCatalog(instance, "sharding", OraCatalogSpex.Name), }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: buildLabelsForCatalog(instance, "sharding"), + Labels: buildLabelsForCatalog(instance, "sharding", OraCatalogSpex.Name), }, Spec: *buildPodSpecForCatalog(instance, OraCatalogSpex), }, @@ -357,9 +357,9 @@ func volumeClaimTemplatesForCatalog(instance *databasev4.ShardingDatabase, OraCa { ObjectMeta: metav1.ObjectMeta{ Name: OraCatalogSpex.Name + "-oradata-vol4", - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, OwnerReferences: getOwnerRef(instance), - Labels: buildLabelsForCatalog(instance, "sharding"), + Labels: buildLabelsForCatalog(instance, "sharding", OraCatalogSpex.Name), }, Spec: corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ @@ -376,15 +376,15 @@ func volumeClaimTemplatesForCatalog(instance *databasev4.ShardingDatabase, OraCa } if len(OraCatalogSpex.PvAnnotations) > 0 { - claims[0].ObjectMeta.Annotations = make(map[string]string) - for key, value := range OraCatalogSpex.PvAnnotations { - claims[0].ObjectMeta.Annotations[key] = value - } - } + claims[0].ObjectMeta.Annotations = make(map[string]string) + for key, value := range OraCatalogSpex.PvAnnotations { + claims[0].ObjectMeta.Annotations[key] = value + } + } - if len(OraCatalogSpex.PvMatchLabels) > 0 { - claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} - } + if len(OraCatalogSpex.PvMatchLabels) > 0 { + claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} + } if checkTdeWalletFlag(instance) { if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { @@ -392,9 +392,9 @@ func volumeClaimTemplatesForCatalog(instance *databasev4.ShardingDatabase, OraCa pvcClaim := corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Name: instance.Name + "shared-storage", - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, OwnerReferences: getOwnerRef(instance), - Labels: buildLabelsForCatalog(instance, "sharding"), + Labels: buildLabelsForCatalog(instance, "sharding", OraCatalogSpex.Name), }, Spec: corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ @@ -432,7 +432,7 @@ func BuildServiceDefForCatalog(instance *databasev4.ShardingDatabase, replicaCou if svctype == "local" { service.Spec.ClusterIP = corev1.ClusterIPNone - service.Spec.Selector = buildLabelsForCatalog(instance, "sharding") + service.Spec.Selector = getSvcLabelsForCatalog(replicaCount, OraCatalogSpex) } // build Service Ports Specs to be exposed. If the PortMappings is not set then default ports will be exposed. @@ -454,9 +454,9 @@ func buildSvcObjectMetaForCatalog(instance *databasev4.ShardingDatabase, replica objmeta := metav1.ObjectMeta{ Name: svcName, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, OwnerReferences: getOwnerRef(instance), - Labels: buildLabelsForCatalog(instance, "sharding"), + Labels: buildLabelsForCatalog(instance, "sharding", OraCatalogSpex.Name), } return objmeta } @@ -485,7 +485,7 @@ func UpdateProvForCatalog(instance *databasev4.ShardingDatabase, var msg string //msg = "Inside the updateProvForCatalog" - //reqLogger := r.Log.WithValues("Instance.Namespace", instance.Spec.Namespace, "Instance.Name", instance.Name) + //reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) LogMessages("DEBUG", msg, nil, instance, logger) // Memory Check @@ -526,3 +526,4 @@ func UpdateProvForCatalog(instance *databasev4.ShardingDatabase, return ctrl.Result{}, nil } + diff --git a/commons/sharding/exec.go b/commons/sharding/exec.go index 7d575320..00caa995 100644 --- a/commons/sharding/exec.go +++ b/commons/sharding/exec.go @@ -63,7 +63,7 @@ import ( // ExecCMDInContainer execute command in first container of a pod func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasev4.ShardingDatabase, logger logr.Logger) (string, string, error) { - var err1 error = nil + var err1 error = nil var msg string var ( execOut bytes.Buffer @@ -71,28 +71,28 @@ func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, ) for i := 0; i < 5; i++ { - if scheme.Scheme == nil { - time.Sleep(time.Second * 40) - } else { - break - } - } + if scheme.Scheme == nil { + time.Sleep(time.Second * 40) + } else { + break + } + } if kubeClient == nil { - msg = "ExecCommand() : kubeClient is nil" - err1 = fmt.Errorf(msg) - return "Error:","kubeClient is nil",err1 - } + msg = "ExecCommand() : kubeClient is nil" + err1 = fmt.Errorf(msg) + return "Error:", "kubeClient is nil", err1 + } if kubeConfig == nil { - msg = "ExecCommand() : kubeConfig is nil" - err1 = fmt.Errorf(msg) - return "Error:","kubeConfig is nil",err1 + msg = "ExecCommand() : kubeConfig is nil" + err1 = fmt.Errorf(msg) + return "Error:", "kubeConfig is nil", err1 } msg = "" req := kubeClient.CoreV1().RESTClient(). Post(). - Namespace(instance.Spec.Namespace). + Namespace(instance.Namespace). Resource("pods"). Name(podName). SubResource("exec"). @@ -105,6 +105,8 @@ func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, config, err := kubeConfig.ClientConfig() if err != nil { + msg = "Error after executing kubeConfig.ClientConfig" + LogMessages("Error", msg, err, instance, logger) return "Error Occurred", "Error Occurred", err } diff --git a/commons/sharding/gsm.go b/commons/sharding/gsm.go index 33f76ece..e6be8770 100644 --- a/commons/sharding/gsm.go +++ b/commons/sharding/gsm.go @@ -56,7 +56,7 @@ import ( ) // Constants for hello-stateful StatefulSet & Volumes -func buildLabelsForGsm(instance *databasev4.ShardingDatabase, label string) map[string]string { +func buildLabelsForGsm(instance *databasev4.ShardingDatabase, label string, gsmName string) map[string]string { return map[string]string{ "app": "OracleGsming", "shard_name": "Gsm", @@ -97,8 +97,8 @@ func builObjectMetaForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex data // building objectMeta objmeta := metav1.ObjectMeta{ Name: OraGsmSpex.Name, - Namespace: instance.Spec.Namespace, - Labels: buildLabelsForGsm(instance, "sharding"), + Namespace: instance.Namespace, + Labels: buildLabelsForGsm(instance, "sharding", OraGsmSpex.Name), OwnerReferences: getOwnerRef(instance), } return objmeta @@ -111,11 +111,11 @@ func buildStatefulSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex d sfsetspec := &appsv1.StatefulSetSpec{ ServiceName: OraGsmSpex.Name, Selector: &metav1.LabelSelector{ - MatchLabels: buildLabelsForGsm(instance, "sharding"), + MatchLabels: buildLabelsForGsm(instance, "sharding", OraGsmSpex.Name), }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: buildLabelsForGsm(instance, "sharding"), + Labels: buildLabelsForGsm(instance, "sharding", OraGsmSpex.Name), }, Spec: *buildPodSpecForGsm(instance, OraGsmSpex), }, @@ -337,8 +337,8 @@ func volumeClaimTemplatesForGsm(instance *databasev4.ShardingDatabase, OraGsmSpe { ObjectMeta: metav1.ObjectMeta{ Name: OraGsmSpex.Name + "-oradata-vol4", - Namespace: instance.Spec.Namespace, - Labels: buildLabelsForGsm(instance, "sharding"), + Namespace: instance.Namespace, + Labels: buildLabelsForGsm(instance, "sharding", OraGsmSpex.Name), OwnerReferences: getOwnerRef(instance), }, Spec: corev1.PersistentVolumeClaimSpec{ @@ -354,6 +354,14 @@ func volumeClaimTemplatesForGsm(instance *databasev4.ShardingDatabase, OraGsmSpe }, }, } + + if len(OraGsmSpex.PvAnnotations) > 0 { + claims[0].ObjectMeta.Annotations = make(map[string]string) + for key, value := range OraGsmSpex.PvAnnotations { + claims[0].ObjectMeta.Annotations[key] = value + } + } + if len(OraGsmSpex.PvMatchLabels) > 0 { claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraGsmSpex.PvMatchLabels} } @@ -376,7 +384,7 @@ func BuildServiceDefForGsm(instance *databasev4.ShardingDatabase, replicaCount i if svctype == "local" { service.Spec.ClusterIP = corev1.ClusterIPNone - service.Spec.Selector = buildLabelsForGsm(instance, "sharding") + service.Spec.Selector = getSvcLabelsForGsm(replicaCount, OraGsmSpex) } // build Service Ports Specs to be exposed. If the PortMappings is not set then default ports will be exposed. @@ -398,8 +406,8 @@ func buildSvcObjectMetaForGsm(instance *databasev4.ShardingDatabase, replicaCoun objmeta := metav1.ObjectMeta{ Name: svcName, - Namespace: instance.Spec.Namespace, - Labels: buildLabelsForGsm(instance, "sharding"), + Namespace: instance.Namespace, + Labels: buildLabelsForGsm(instance, "sharding", OraGsmSpex.Name), OwnerReferences: getOwnerRef(instance), } return objmeta diff --git a/commons/sharding/provstatus.go b/commons/sharding/provstatus.go index f5bf6767..44544c60 100644 --- a/commons/sharding/provstatus.go +++ b/commons/sharding/provstatus.go @@ -310,10 +310,10 @@ func removeGsmKeys(instance *databasev4.ShardingDatabase, name string, key strin func getInstanceNs(instance *databasev4.ShardingDatabase) string { var namespace string - if instance.Spec.Namespace == "" { + if instance.Namespace == "" { namespace = "default" } else { - namespace = instance.Spec.Namespace + namespace = instance.Namespace } return namespace } @@ -354,6 +354,7 @@ func ValidateDbSetup(podName string, instance *databasev4.ShardingDatabase, kube _, _, err := ExecCommand(podName, shardValidationCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { + return fmt.Errorf("error ocurred while validating the DB Setup") } return nil diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index b5cb0749..bcef0763 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -294,7 +294,7 @@ func buildEnvVarsSpec(instance *databasev4.ShardingDatabase, variables []databas } if instance.Spec.InvitedNodeSubnetFlag == "" { - instance.Spec.InvitedNodeSubnetFlag = "FALSE" + instance.Spec.InvitedNodeSubnetFlag = "TRUE" } if strings.ToUpper(instance.Spec.InvitedNodeSubnetFlag) != "FALSE" { @@ -410,7 +410,9 @@ func LogMessages(msgtype string, msg string, err error, instance *databasev4.Sha } } else if msgtype == "INFO" { logger.Info(msg) - } + } else if msgtype == "Error" { + logger.Error(err, msg) + } } func GetGsmPodName(gsmName string) string { @@ -447,22 +449,22 @@ func GetPdbName(variables []databasev4.EnvironmentVariable, name string) string } func getlabelsForGsm(instance *databasev4.ShardingDatabase) map[string]string { - return buildLabelsForGsm(instance, "sharding") + return buildLabelsForGsm(instance, "sharding", "gsm") } func getlabelsForShard(instance *databasev4.ShardingDatabase) map[string]string { - return buildLabelsForShard(instance, "sharding") + return buildLabelsForShard(instance, "sharding", "shard") } func getlabelsForCatalog(instance *databasev4.ShardingDatabase) map[string]string { - return buildLabelsForCatalog(instance, "sharding") + return buildLabelsForCatalog(instance, "sharding", "catalog") } func LabelsForProvShardKind(instance *databasev4.ShardingDatabase, sftype string, ) map[string]string { if sftype == "shard" { - return buildLabelsForShard(instance, "sharding") + return buildLabelsForShard(instance, "sharding", "shard") } return nil @@ -473,7 +475,7 @@ func CheckSfset(sfsetName string, instance *databasev4.ShardingDatabase, kClient sfSetFound := &appsv1.StatefulSet{} err := kClient.Get(context.TODO(), types.NamespacedName{ Name: sfsetName, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, }, sfSetFound) if err != nil { return sfSetFound, err @@ -485,7 +487,7 @@ func checkPvc(pvcName string, instance *databasev4.ShardingDatabase, kClient cli pvcFound := &corev1.PersistentVolumeClaim{} err := kClient.Get(context.TODO(), types.NamespacedName{ Name: pvcName, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, }, pvcFound) if err != nil { return pvcFound, err @@ -529,7 +531,7 @@ func CheckSvc(svcName string, instance *databasev4.ShardingDatabase, kClient cli svcFound := &corev1.Service{} err := kClient.Get(context.TODO(), types.NamespacedName{ Name: svcName, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, }, svcFound) if err != nil { return svcFound, err @@ -594,7 +596,7 @@ func GetPodList(sfsetName string, resType string, instance *databasev4.ShardingD return nil, err1 } - listOps := &client.ListOptions{Namespace: instance.Spec.Namespace, LabelSelector: labelSelector} + listOps := &client.ListOptions{Namespace: instance.Namespace, LabelSelector: labelSelector} err := kClient.List(context.TODO(), podList, listOps) if err != nil { @@ -607,7 +609,7 @@ func checkPod(instance *databasev4.ShardingDatabase, pod *corev1.Pod, kClient cl ) error { err := kClient.Get(context.TODO(), types.NamespacedName{ Name: pod.Name, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, }, pod) if err != nil { @@ -667,11 +669,11 @@ func AddNamespace(instance *databasev4.ShardingDatabase, kClient client.Client, ) error { var msg string ns := &corev1.Namespace{} - err := kClient.Get(context.TODO(), types.NamespacedName{Name: instance.Spec.Namespace}, ns) + err := kClient.Get(context.TODO(), types.NamespacedName{Name: instance.Namespace}, ns) if err != nil { - //msg = "Namespace " + instance.Spec.Namespace + " doesn't exist! creating namespace" + //msg = "Namespace " + instance.Namespace + " doesn't exist! creating namespace" if errors.IsNotFound(err) { - err = kClient.Create(context.TODO(), NewNamespace(instance.Spec.Namespace)) + err = kClient.Create(context.TODO(), NewNamespace(instance.Namespace)) if err != nil { msg = "Error in creating namespace!" LogMessages("Error", msg, nil, instance, logger) @@ -1017,7 +1019,7 @@ func labelsForShardingDatabaseKind(instance *databasev4.ShardingDatabase, sftype ) map[string]string { if sftype == "shard" { - return buildLabelsForShard(instance, "sharding") + return buildLabelsForShard(instance, "sharding", "shard") } return nil @@ -1209,7 +1211,7 @@ func ReadConfigMap(cmName string, instance *databasev4.ShardingDatabase, kClient // Reding a config map err = kClient.Get(context.TODO(), types.NamespacedName{ Name: cmName, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, }, cm) if err != nil { @@ -1262,7 +1264,7 @@ func ReadSecret(secName string, instance *databasev4.ShardingDatabase, kClient c // Reading a Secret var err error = kClient.Get(context.TODO(), types.NamespacedName{ Name: secName, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, }, sc) if err != nil { @@ -1284,20 +1286,17 @@ func GetK8sClientConfig(kClient client.Client) (clientcmd.ClientConfig, kubernet var kubeConfig clientcmd.ClientConfig var kubeClient kubernetes.Interface - databasev4.KubeConfigOnce.Do(func() { - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - configOverrides := &clientcmd.ConfigOverrides{} - kubeConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) - config, err := kubeConfig.ClientConfig() - if err != nil { - err1 = err - } - kubeClient, err = kubernetes.NewForConfig(config) - if err != nil { - err1 = err - } - - }) + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + kubeConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + config, err := kubeConfig.ClientConfig() + if err != nil { + err1 = err + } + kubeClient, err = kubernetes.NewForConfig(config) + if err != nil { + err1 = err + } return kubeConfig, kubeClient, err1 } diff --git a/commons/sharding/shard.go b/commons/sharding/shard.go index 49031732..e48b56dd 100644 --- a/commons/sharding/shard.go +++ b/commons/sharding/shard.go @@ -54,7 +54,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func buildLabelsForShard(instance *databasev4.ShardingDatabase, label string) map[string]string { +func buildLabelsForShard(instance *databasev4.ShardingDatabase, label string, shardName string) map[string]string { return map[string]string{ "app": "OracleSharding", "type": "Shard", @@ -96,9 +96,9 @@ func builObjectMetaForShard(instance *databasev4.ShardingDatabase, OraShardSpex // building objectMeta objmeta := metav1.ObjectMeta{ Name: OraShardSpex.Name, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, OwnerReferences: getOwnerRef(instance), - Labels: buildLabelsForShard(instance, "sharding"), + Labels: buildLabelsForShard(instance, "sharding", OraShardSpex.Name), } return objmeta } @@ -110,11 +110,11 @@ func buildStatefulSpecForShard(instance *databasev4.ShardingDatabase, OraShardSp sfsetspec := &appsv1.StatefulSetSpec{ ServiceName: OraShardSpex.Name, Selector: &metav1.LabelSelector{ - MatchLabels: buildLabelsForShard(instance, "sharding"), + MatchLabels: buildLabelsForShard(instance, "sharding", OraShardSpex.Name), }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: buildLabelsForShard(instance, "sharding"), + Labels: buildLabelsForShard(instance, "sharding", OraShardSpex.Name), }, Spec: *buildPodSpecForShard(instance, OraShardSpex), }, @@ -363,9 +363,9 @@ func volumeClaimTemplatesForShard(instance *databasev4.ShardingDatabase, OraShar { ObjectMeta: metav1.ObjectMeta{ Name: OraShardSpex.Name + "-oradata-vol4", - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, OwnerReferences: getOwnerRef(instance), - Labels: buildLabelsForShard(instance, "sharding"), + Labels: buildLabelsForShard(instance, "sharding", OraShardSpex.Name), }, Spec: corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ @@ -410,7 +410,7 @@ func BuildServiceDefForShard(instance *databasev4.ShardingDatabase, replicaCount if svctype == "local" { service.Spec.ClusterIP = corev1.ClusterIPNone - service.Spec.Selector = buildLabelsForShard(instance, "sharding") + service.Spec.Selector = getSvcLabelsForShard(replicaCount, OraShardSpex) } // build Service Ports Specs to be exposed. If the PortMappings is not set then default ports will be exposed. @@ -434,8 +434,8 @@ func buildSvcObjectMetaForShard(instance *databasev4.ShardingDatabase, replicaCo objmeta := metav1.ObjectMeta{ Name: svcName, - Namespace: instance.Spec.Namespace, - Labels: buildLabelsForShard(instance, "sharding"), + Namespace: instance.Namespace, + Labels: buildLabelsForShard(instance, "sharding", OraShardSpex.Name), OwnerReferences: getOwnerRef(instance), } return objmeta diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index ad676133..2c227c14 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -71,14 +71,13 @@ import ( shardingv1 "github.com/oracle/oracle-database-operator/commons/sharding" ) -// Sharding Topology -type ShardingTopology struct { - topicid string - Instance *databasev4.ShardingDatabase - deltopology bool - onsProvider common.ConfigurationProvider - onsProviderFlag bool - rclient ons.NotificationDataPlaneClient +// Struct keeping Oracle Notification Server Info +type OnsStatus struct { + Topicid string `json:"topicid,omitempty"` + Instance *databasev4.ShardingDatabase `json:"instance,omitempty"` + OnsProvider common.ConfigurationProvider `json:"onsProvider,omitempty"` + OnsProviderFlag bool `json:"onsProviderFlag,omitempty"` + Rclient ons.NotificationDataPlaneClient `json:"rclient,omitempty"` } // ShardingDatabaseReconciler reconciles a ShardingDatabase object @@ -89,14 +88,15 @@ type ShardingDatabaseReconciler struct { kubeClient kubernetes.Interface kubeConfig clientcmd.ClientConfig Recorder record.EventRecorder - osh []*ShardingTopology - InCluster bool - Namespace string + InCluster bool + Namespace string } var sentFailMsg = make(map[string]bool) var sentCompleteMsg = make(map[string]bool) +var oshMap=make(map[string]*OnsStatus) + // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases/status,verbs=get;update;patch // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases/finalizers,verbs=get;create;update;patch;delete @@ -128,11 +128,11 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req var isShardTopologyDeleteTrue bool = false //var msg string var err error - var idx int var stateType string resultNq := ctrl.Result{Requeue: false} resultQ := ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second} var nilErr error = nil + var msg string // On every reconcile, we will call setCrdLifeCycleState // To understand this, please refer https://sdk.operatorframework.io/docs/building-operators/golang/advanced-topics/ @@ -159,14 +159,10 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, err } - _, instFlag := r.checkProvInstance(instance) - // assinging osh instance + instFlag := r.checkProvInstance(instance) if !instFlag { - // Sharding Topolgy Struct Assignment - // ====================================== - osh := &ShardingTopology{} - osh.Instance = instance - r.osh = append(r.osh, osh) + oshMap[instance.Name] = &OnsStatus{} + oshMap[instance.Name].Instance = instance } defer r.setCrdLifeCycleState(instance, &result, &err, &stateType) defer r.updateShardTopologyStatus(instance) @@ -187,30 +183,20 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req } // ======== Setting the flag and Index to be used later in this function ======== - idx, instFlag = r.checkProvInstance(instance) - if !instFlag { - //r.setCrdLifeCycleState(instance, &result, &err, stateType) - result = resultNq - return result, fmt.Errorf("DId not find the instance in checkProvInstance") - } + // instFlag = r.checkProvInstance(instance) + // if !instFlag { + //r.setCrdLifeCycleState(instance, &result, &err, stateType) + //// result = resultNq + // return result, fmt.Errorf("DId not find the instance in checkProvInstance") + // } // ================================ OCI Notification Provider =========== - r.getOnsConfigProvider(instance, idx) + r.getOnsConfigProvider(instance) // =============================== Checking Namespace ============== - if instance.Spec.Namespace == "" { - ///err = shardingv1.AddNamespace(instance, r.Client, r.Log) - //if err != nil { - // //r.setCrdLifeCycleState(instance, &result, &err, stateType) - // result = resultNq - // return result, err - // } - // } else { - instance.Spec.Namespace = "default" - } // ======================== Validate Specs ============== - err = r.validateSpex(instance, idx) + err = r.validateSpex(instance) if err != nil { //r.setCrdLifeCycleState(instance, &result, &err, stateType) result = resultNq @@ -239,6 +225,12 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req if len(instance.Spec.Catalog) > 0 { for i = 0; i < int32(len(instance.Spec.Catalog)); i++ { OraCatalogSpex = instance.Spec.Catalog[i] + if len(OraCatalogSpex.Name) > 9 { + msg = "Catalog Name cannot be greater than 9 characters." + err = fmt.Errorf(msg) + result = resultNq + return result, err + } // See if StatefulSets already exists and create if it doesn't result, err = r.deployStatefulSet(instance, shardingv1.BuildStatefulSetForCatalog(instance, OraCatalogSpex), "CATALOG") if err != nil { @@ -290,6 +282,12 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // if user set replicasize greater than 1 but also set instance.Spec.OraDbPvcName then only one service will be created and one pod for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] + if len(OraShardSpex.Name) > 9 { + msg = "Shard Name cannot be greater than 9 characters." + err = fmt.Errorf(msg) + result = resultNq + return result, err + } if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { result, err = r.createService(instance, shardingv1.BuildServiceDefForShard(instance, 0, OraShardSpex, "local")) if err != nil { @@ -342,7 +340,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // Loop will be requeued only if Shard Statefulset is not ready or not configured. // Till that time Reconcilation loop will remain in blocked state // if the err is return because of Shard is not ready then blocked state is rmeoved and reconcilation state is set - err = r.addPrimaryShards(instance, idx) + err = r.addPrimaryShards(instance) if err != nil { // time.Sleep(30 * time.Second) err = nilErr @@ -353,7 +351,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // Loop will be requeued only if Standby Shard Statefulset is not ready or not configured. // Till that time Reconcilation loop will remain in blocked state // if the err is return because of Shard is not ready then blocked state is rmeoved and reconcilation state is - err = r.addStandbyShards(instance, idx) + err = r.addStandbyShards(instance) if err != nil { // time.Sleep(30 * time.Second) err = nilErr @@ -363,7 +361,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // we don't need to run the requeue loop but still putting this condition to address any unkown situation // delShard function set the state to blocked and we do not allow any other operationn while delete is going on - err = r.delGsmShard(instance, idx) + err = r.delGsmShard(instance) if err != nil { // time.Sleep(30 * time.Second) err = nilErr @@ -376,13 +374,13 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req OraCatalogSpex = instance.Spec.Catalog[i] sfSet, catalogPod, err := r.validateInvidualCatalog(instance, OraCatalogSpex, int(i)) if err != nil { - shardingv1.LogMessages("INFO", "Catalog "+sfSet.Name+" is not in available state.", nil, instance, r.Log) + shardingv1.LogMessages("Error", "Catalog "+sfSet.Name+" is not in available state.", nil, instance, r.Log) result = resultNq return result, err } result, err = shardingv1.UpdateProvForCatalog(instance, OraCatalogSpex, r.Client, sfSet, catalogPod, r.Log) if err != nil { - shardingv1.LogMessages("INFO", "Error Occurred during catalog update operation.", nil, instance, r.Log) + shardingv1.LogMessages("Error", "Error Occurred during catalog update operation.", nil, instance, r.Log) result = resultNq return result, err } @@ -394,13 +392,13 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { sfSet, shardPod, err := r.validateShard(instance, OraShardSpex, int(i)) if err != nil { - shardingv1.LogMessages("INFO", "Shard "+sfSet.Name+" is not in available state.", nil, instance, r.Log) + shardingv1.LogMessages("Error", "Shard "+sfSet.Name+" is not in available state.", nil, instance, r.Log) result = resultNq return result, err } result, err = shardingv1.UpdateProvForShard(instance, OraShardSpex, r.Client, sfSet, shardPod, r.Log) if err != nil { - shardingv1.LogMessages("INFO", "Error Occurred during shard update operation..", nil, instance, r.Log) + shardingv1.LogMessages("Error", "Error Occurred during shard update operation..", nil, instance, r.Log) result = resultNq return result, err } @@ -412,13 +410,13 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req OraGsmSpex = instance.Spec.Gsm[i] sfSet, gsmPod, err := r.validateInvidualGsm(instance, OraGsmSpex, int(i)) if err != nil { - shardingv1.LogMessages("INFO", "Gsm "+sfSet.Name+" is not in available state.", nil, instance, r.Log) + shardingv1.LogMessages("Error", "Gsm "+sfSet.Name+" is not in available state.", nil, instance, r.Log) result = resultNq return result, err } result, err = shardingv1.UpdateProvForGsm(instance, OraGsmSpex, r.Client, sfSet, gsmPod, r.Log) if err != nil { - shardingv1.LogMessages("INFO", "Error Occurred during GSM update operation.", nil, instance, r.Log) + shardingv1.LogMessages("Error", "Error Occurred during GSM update operation.", nil, instance, r.Log) result = resultNq return result, err } @@ -457,58 +455,51 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate return true }, UpdateFunc: func(e event.UpdateEvent) bool { + instance := &databasev4.ShardingDatabase{} if old, ok := e.ObjectOld.(*corev1.Secret); ok { if new, ok := e.ObjectNew.(*corev1.Secret); ok { - for i := 0; i < len(r.osh); i++ { - oshInst := r.osh[i] - if (new.Name == oshInst.Instance.Spec.DbSecret.Name) && (new.Name == old.Name) { - _, ok := old.Data[oshInst.Instance.Spec.DbSecret.PwdFileName] - if ok { - if !reflect.DeepEqual(old.Data[oshInst.Instance.Spec.DbSecret.PwdFileName], new.Data[oshInst.Instance.Spec.DbSecret.PwdFileName]) { - shardingv1.LogMessages("INFO", "Secret Changed", nil, oshInst.Instance, r.Log) - } + oshInst := instance + if (new.Name == oshInst.Spec.DbSecret.Name) && (new.Name == old.Name) { + _, ok := old.Data[oshInst.Spec.DbSecret.PwdFileName] + if ok { + if !reflect.DeepEqual(old.Data[oshInst.Spec.DbSecret.PwdFileName], new.Data[oshInst.Spec.DbSecret.PwdFileName]) { + shardingv1.LogMessages("INFO", "Secret Changed", nil, oshInst, r.Log) } - shardingv1.LogMessages("INFO", "Secret update block", nil, oshInst.Instance, r.Log) } + shardingv1.LogMessages("INFO", "Secret update block", nil, oshInst, r.Log) } } } return true }, DeleteFunc: func(e event.DeleteEvent) bool { + instance := &databasev4.ShardingDatabase{} _, podOk := e.Object.GetLabels()["statefulset.kubernetes.io/pod-name"] - for i := 0; i < len(r.osh); i++ { - if r.osh[i] != nil { - oshInst := r.osh[i] - if oshInst.deltopology == true { - break - - } - if e.Object.GetLabels()[string(databasev4.ShardingDelLabelKey)] == string(databasev4.ShardingDelLabelTrueValue) { - break - } - - if podOk { - delObj := e.Object.(*corev1.Pod) - if e.Object.GetLabels()["type"] == "Shard" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Instance.Name { - - if delObj.DeletionTimestamp != nil { - go r.gsmInvitedNodeOp(oshInst.Instance, delObj.Name) - } - } - - if e.Object.GetLabels()["type"] == "Catalog" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Instance.Name { - - if delObj.DeletionTimestamp != nil { - go r.gsmInvitedNodeOp(oshInst.Instance, delObj.Name) - } - } - - } - - } - } - + if oshMap[instance.Name] != nil { + oshInst := instance + if instance.DeletionTimestamp == nil { + + if e.Object.GetLabels()[string(databasev4.ShardingDelLabelKey)] == string(databasev4.ShardingDelLabelTrueValue) { + } + + if podOk { + delObj := e.Object.(*corev1.Pod) + if e.Object.GetLabels()["type"] == "Shard" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Name { + + if delObj.DeletionTimestamp != nil { + go r.gsmInvitedNodeOp(oshInst, delObj.Name) + } + } + + if e.Object.GetLabels()["type"] == "Catalog" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Name { + + if delObj.DeletionTimestamp != nil { + go r.gsmInvitedNodeOp(oshInst, delObj.Name) + } + } + } + } + } return true }, } @@ -523,7 +514,7 @@ func (r *ShardingDatabaseReconciler) UpdateSecret(instance *databasev4.ShardingD // Reading a Secret var err error = kClient.Get(context.TODO(), types.NamespacedName{ Name: instance.Spec.DbSecret.Name, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, }, sc) if err != nil { @@ -534,38 +525,56 @@ func (r *ShardingDatabaseReconciler) UpdateSecret(instance *databasev4.ShardingD } // ================== Function to get the Notification controller ============== -func (r *ShardingDatabaseReconciler) getOnsConfigProvider(instance *databasev4.ShardingDatabase, idx int, -) { +func (r *ShardingDatabaseReconciler) getOnsConfigProvider(instance *databasev4.ShardingDatabase) { var err error - if instance.Spec.DbSecret.NsConfigMap != "" && instance.Spec.DbSecret.NsSecret != "" && r.osh[idx].onsProviderFlag != true { + if instance.Spec.DbSecret.NsConfigMap != "" && instance.Spec.DbSecret.NsSecret != "" && oshMap[instance.Name].OnsProviderFlag != true { cmName := instance.Spec.DbSecret.NsConfigMap secName := instance.Spec.DbSecret.NsSecret shardingv1.LogMessages("DEBUG", "Received parameters are "+shardingv1.GetFmtStr(cmName)+","+shardingv1.GetFmtStr(secName), nil, instance, r.Log) region, user, tenancy, passphrase, fingerprint, topicid := shardingv1.ReadConfigMap(cmName, instance, r.Client, r.Log) privatekey := shardingv1.ReadSecret(secName, instance, r.Client, r.Log) - r.osh[idx].topicid = topicid - r.osh[idx].onsProvider = common.NewRawConfigurationProvider(tenancy, user, region, fingerprint, privatekey, &passphrase) - r.osh[idx].rclient, err = ons.NewNotificationDataPlaneClientWithConfigurationProvider(r.osh[idx].onsProvider) + + oshMap[instance.Name].Topicid = topicid + oshMap[instance.Name].OnsProvider = common.NewRawConfigurationProvider(tenancy, user, region, fingerprint, privatekey, &passphrase) +//VV instance.Spec.TopicId = topicid + oshMap[instance.Name].Rclient, err = ons.NewNotificationDataPlaneClientWithConfigurationProvider(oshMap[instance.Name].OnsProvider) + if err != nil { + msg := "Error occurred in getting the OCI notification service based client." + oshMap[instance.Name].OnsProviderFlag = false + r.Log.Error(err, msg) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) + } else { + oshMap[instance.Name].OnsProviderFlag = true + } + } +} + +func (r ShardingDatabaseReconciler) marshalOnsInfo(instance *databasev4.ShardingDatabase) (OnsStatus, error) { + onsData := OnsStatus{} + specBytes, err := instance.GetLastSuccessfulOnsInfo() + if err != nil { + shardingv1.LogMessages("Error", "error occurred while getting the data from getLastSuccessfulOnsInfo", nil, instance, r.Log) + return onsData, err + } else { + shardingv1.LogMessages("Error", "error occurred while getting the data from getLastSuccessfulOnsInfo and unmarshaling the object", nil, instance, r.Log) + err := json.Unmarshal(specBytes, &onsData) if err != nil { - msg := "Error occurred in getting the OCI notification service based client." - r.osh[idx].onsProviderFlag = false - r.Log.Error(err, msg) - shardingv1.LogMessages("Error", msg, nil, instance, r.Log) - } else { - r.osh[idx].onsProviderFlag = true + return onsData, err } - } + return onsData, nil } // ================== Function the Message ============== func (r *ShardingDatabaseReconciler) sendMessage(instance *databasev4.ShardingDatabase, title string, body string) { - idx, instFlag := r.checkProvInstance(instance) - if instFlag { - if r.osh[idx].onsProviderFlag { - shardingv1.SendNotification(title, body, instance, r.osh[idx].topicid, r.osh[idx].rclient, r.Log) - } - } + instFlag := r.checkProvInstance(instance) + if instFlag { + shardingv1.LogMessages("INFO", "sendMessage():instFlag true", nil, instance, r.Log) + if oshMap[instance.Name].OnsProviderFlag { + shardingv1.LogMessages("INFO", "sendMessage():OnsProviderFlag true", nil, instance, r.Log) + shardingv1.SendNotification(title, body, instance, oshMap[instance.Name].Topicid, oshMap[instance.Name].Rclient, r.Log) + } + } } func (r *ShardingDatabaseReconciler) publishEvents(instance *databasev4.ShardingDatabase, eventMsg string, state string) { @@ -620,7 +629,7 @@ func (r *ShardingDatabaseReconciler) finalizerShardingDatabaseInstance(instance // ========================== FInalizer Section =================== func (r *ShardingDatabaseReconciler) addFinalizer(instance *databasev4.ShardingDatabase) error { - reqLogger := r.Log.WithValues("instance.Spec.Namespace", instance.Spec.Namespace, "instance.Name", instance.Name) + reqLogger := r.Log.WithValues("instance.Namespace", instance.Namespace, "instance.Name", instance.Name) controllerutil.AddFinalizer(instance, shardingv1.ShardingDatabaseFinalizer) // Update CR @@ -641,10 +650,9 @@ func (r *ShardingDatabaseReconciler) finalizeShardingDatabase(instance *database var err error var pvcName string - idx, _ := r.checkProvInstance(instance) + r.checkProvInstance(instance) sfSetFound := &appsv1.StatefulSet{} svcFound := &corev1.Service{} - r.osh[idx].deltopology = true if len(instance.Spec.Shard) > 0 { for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex := instance.Spec.Shard[i] @@ -831,41 +839,29 @@ func (r *ShardingDatabaseReconciler) finalizeShardingDatabase(instance *database } } - r.osh[idx].deltopology = false - //r.osh[idx].addSem.Release(1) - //r.osh[idx].delSem.Release(1) - //instance1 := &shardingv4.ProvShard{} - r.osh[idx].Instance = &databasev4.ShardingDatabase{} - - //r.osh[idx] = nil + oshMap[instance.Name].Instance = &databasev4.ShardingDatabase{} return nil } -//============== - // Get the current instance func (r *ShardingDatabaseReconciler) checkProvInstance(instance *databasev4.ShardingDatabase, -) (int, bool) { +) bool { var status bool = false - var idx int - for i := 0; i < len(r.osh); i++ { - idx = i - if r.osh[i] != nil { - if !r.osh[i].deltopology { - if r.osh[i].Instance.Name == instance.Name { - status = true - break - } - } + if oshMap[instance.Name] != nil { + title := "checkProvInstance()" + message := "oshMap.Instance.Name=[" + oshMap[instance.Name].Instance.Name + "]. instance.Name=[" + instance.Name + "]." + shardingv1.LogMessages("INFO", title+":"+message, nil, instance, r.Log) + if oshMap[instance.Name].Instance.Name == instance.Name { + status = true } } - return idx, status + return status } // =========== validate Specs ============ -func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev4.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev4.ShardingDatabase) error { var eventMsg string var eventErr string = "Spec Error" @@ -880,7 +876,7 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev4.ShardingD if lastSuccSpec == nil { // Logic to check if inital Spec is good or not - err = r.checkShardingType(instance, idx) + err = r.checkShardingType(instance) if err != nil { return err } @@ -909,11 +905,6 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev4.ShardingD } else { // if the last sucessful spec is not nil // check the parameters which cannot be changed - if lastSuccSpec.Namespace != instance.Spec.Namespace { - eventMsg = "ShardingDatabase CRD resource " + shardingv1.GetFmtStr(instance.Name) + " namespace changed from " + shardingv1.GetFmtStr(lastSuccSpec.Namespace) + " to " + shardingv1.GetFmtStr(instance.Spec.Namespace) + ". This change is not allowed." - r.Recorder.Eventf(instance, corev1.EventTypeWarning, eventErr, eventMsg) - return fmt.Errorf("instance spec has changed and namespace change is not supported") - } if lastSuccSpec.DbImage != instance.Spec.DbImage { eventMsg = "ShardingDatabase CRD resource " + shardingv1.GetFmtStr(instance.Name) + " DBImage changed from " + shardingv1.GetFmtStr(lastSuccSpec.DbImage) + " to " + shardingv1.GetFmtStr(instance.Spec.DbImage) + ". This change is not allowed." @@ -950,7 +941,7 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev4.ShardingD return nil } -func (r *ShardingDatabaseReconciler) checkShardingType(instance *databasev4.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) checkShardingType(instance *databasev4.ShardingDatabase) error { var i, k int32 var regionFlag bool @@ -1172,10 +1163,12 @@ func (r *ShardingDatabaseReconciler) validateInvidualGsm(instance *databasev4.Sh podList := &corev1.PodList{} var isPodExist bool + // VV : uninitialised variable 'i' being used. + i = int32(specId) gsmSfSet, err = shardingv1.CheckSfset(OraGsmSpex.Name, instance, r.Client) if err != nil { msg = "Unable to find GSM statefulset " + shardingv1.GetFmtStr(OraGsmSpex.Name) + "." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateGsmStatus(instance, int(i), string(databasev4.StatefulSetNotFound)) return gsmSfSet, gsmPod, err } @@ -1183,7 +1176,7 @@ func (r *ShardingDatabaseReconciler) validateInvidualGsm(instance *databasev4.Sh podList, err = shardingv1.GetPodList(gsmSfSet.Name, "GSM", instance, r.Client) if err != nil { msg = "Unable to find any pod in statefulset " + shardingv1.GetFmtStr(gsmSfSet.Name) + "." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateGsmStatus(instance, int(i), string(databasev4.PodNotFound)) return gsmSfSet, gsmPod, err } @@ -1191,14 +1184,14 @@ func (r *ShardingDatabaseReconciler) validateInvidualGsm(instance *databasev4.Sh isPodExist, gsmPod = shardingv1.PodListValidation(podList, gsmSfSet.Name, instance, r.Client) if !isPodExist { msg = "Unable to validate GSM " + shardingv1.GetFmtStr(gsmPod.Name) + " pod. GSM pod doesn't seems to be ready to accept the commands." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateGsmStatus(instance, int(i), string(databasev4.PodNotReadyState)) return gsmSfSet, gsmPod, fmt.Errorf("pod doesn't exist") } err = shardingv1.CheckGsmStatus(gsmPod.Name, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { msg = "Unable to validate GSM director. GSM director doesn't seems to be ready to accept the commands." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateGsmStatus(instance, int(i), string(databasev4.ProvisionState)) return gsmSfSet, gsmPod, err } @@ -1248,7 +1241,7 @@ func (r *ShardingDatabaseReconciler) validateInvidualCatalog(instance *databasev catalogSfSet, err = shardingv1.CheckSfset(OraCatalogSpex.Name, instance, r.Client) if err != nil { msg := "Unable to find Catalog statefulset " + shardingv1.GetFmtStr(OraCatalogSpex.Name) + "." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateCatalogStatus(instance, specId, string(databasev4.StatefulSetNotFound)) return catalogSfSet, catalogPod, err } @@ -1256,21 +1249,21 @@ func (r *ShardingDatabaseReconciler) validateInvidualCatalog(instance *databasev podList, err = shardingv1.GetPodList(catalogSfSet.Name, "CATALOG", instance, r.Client) if err != nil { msg := "Unable to find any pod in statefulset " + shardingv1.GetFmtStr(catalogSfSet.Name) + "." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateCatalogStatus(instance, specId, string(databasev4.PodNotFound)) return catalogSfSet, catalogPod, err } isPodExist, catalogPod = shardingv1.PodListValidation(podList, catalogSfSet.Name, instance, r.Client) if !isPodExist { msg := "Unable to validate Catalog " + shardingv1.GetFmtStr(catalogSfSet.Name) + " pod. Catalog pod doesn't seems to be ready to accept the commands." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateCatalogStatus(instance, specId, string(databasev4.PodNotReadyState)) return catalogSfSet, catalogPod, fmt.Errorf("Pod doesn't exist") } err = shardingv1.ValidateDbSetup(catalogPod.Name, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { msg := "Unable to validate Catalog. Catalog doesn't seems to be ready to accept the commands." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateCatalogStatus(instance, specId, string(databasev4.ProvisionState)) return catalogSfSet, catalogPod, err } @@ -1291,7 +1284,7 @@ func (r *ShardingDatabaseReconciler) validateShard(instance *databasev4.Sharding shardSfSet, err = shardingv1.CheckSfset(OraShardSpex.Name, instance, r.Client) if err != nil { msg := "Unable to find Shard statefulset " + shardingv1.GetFmtStr(OraShardSpex.Name) + "." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateShardStatus(instance, specId, string(databasev4.StatefulSetNotFound)) return shardSfSet, shardPod, err } @@ -1299,21 +1292,21 @@ func (r *ShardingDatabaseReconciler) validateShard(instance *databasev4.Sharding podList, err := shardingv1.GetPodList(shardSfSet.Name, "SHARD", instance, r.Client) if err != nil { msg := "Unable to find any pod in statefulset " + shardingv1.GetFmtStr(shardSfSet.Name) + "." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateShardStatus(instance, specId, string(databasev4.PodNotFound)) return shardSfSet, shardPod, err } isPodExist, shardPod := shardingv1.PodListValidation(podList, shardSfSet.Name, instance, r.Client) if !isPodExist { msg := "Unable to validate Shard " + shardingv1.GetFmtStr(shardPod.Name) + " pod. Shard pod doesn't seems to be ready to accept the commands." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateShardStatus(instance, specId, string(databasev4.PodNotReadyState)) return shardSfSet, shardPod, err } err = shardingv1.ValidateDbSetup(shardPod.Name, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { msg := "Unable to validate shard. Shard doesn't seems to be ready to accept the commands." - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) r.updateShardStatus(instance, specId, string(databasev4.ProvisionState)) return shardSfSet, shardPod, err } @@ -1480,7 +1473,7 @@ func (r *ShardingDatabaseReconciler) updateGsmShardStatus(instance *databasev4.S } // This function add the Primary Shards in GSM -func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev4.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev4.ShardingDatabase) error { //var result ctrl.Result var result ctrl.Result var i int32 @@ -1551,14 +1544,14 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev4.Shard last := fileName[strings.LastIndex(fileName, "/")+1:] fileName1 := last fsLoc := shardingv1.TmpLoc + "/" + fileName1 - _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fmt.Sprintf("%s/%s:/%s", instance.Spec.Namespace, gsmPod.Name, fileName), fsLoc, "") + _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fmt.Sprintf("%s/%s:/%s", instance.Namespace, gsmPod.Name, fileName), fsLoc, "") if err != nil { fmt.Printf("failed to copy file") //return err } // Copying it to Shard Pod - _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fsLoc, fmt.Sprintf("%s/%s:/%s", instance.Spec.Namespace, OraShardSpex.Name+"-0", fsLoc), "") + _, _, _, err = shardingv1.KctlCopyFile(r.kubeClient, r.kubeConfig, instance, configrest, kclientset, r.Log, fsLoc, fmt.Sprintf("%s/%s:/%s", instance.Namespace, OraShardSpex.Name+"-0", fsLoc), "") if err != nil { fmt.Printf("failed to copy file") //return err @@ -1573,14 +1566,15 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev4.Shard err = shardingv1.AddShardInGsm(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.AddingShardErrorState)) - title = "Shard Addition Failure" - message = "Error occurred during shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " addition." - shardingv1.LogMessages("INFO", title+":"+message, nil, instance, r.Log) - if sentFailMsg[OraShardSpex.Name] != true { + title = instance.Namespace + ":Shard Addition Failure" + message = "TopicId:" + oshMap[instance.Name].Topicid + ":Error occurred during shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " addition." + shardingv1.LogMessages("Error", title+":"+message, nil, instance, r.Log) + msgKey := instance.Namespace + "-" + OraShardSpex.Name + if sentFailMsg[msgKey] != true { r.sendMessage(instance, title, message) } - sentFailMsg[OraShardSpex.Name] = true - sentCompleteMsg[OraShardSpex.Name] = false + sentFailMsg[msgKey] = true + sentCompleteMsg[msgKey] = false deployFlag = false } } @@ -1629,27 +1623,28 @@ func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev4.ShardingD r.updateGsmShardStatus(instance, shardSfSet.Name, string(databasev4.ShardOnlineState)) // Following logic will sent a email only once if oldStateStr != string(databasev4.ShardOnlineState) { - title = "Shard Addition Completed" - message = "Shard addition completed for shard " + shardingv1.GetFmtStr(shardSfSet.Name) + " in GSM." + title = instance.Namespace + ":Shard Addition Completed" + message = "TopicId:" + oshMap[instance.Name].Topicid + ":Shard addition completed for shard " + shardingv1.GetFmtStr(shardSfSet.Name) + " in GSM." shardingv1.LogMessages("INFO", title+":"+message, nil, instance, r.Log) - if sentCompleteMsg[shardSfSet.Name] != true { + msgKey := instance.Namespace + "-" + shardSfSet.Name + if sentCompleteMsg[msgKey] != true { r.sendMessage(instance, title, message) } - sentCompleteMsg[shardSfSet.Name] = true - sentFailMsg[shardSfSet.Name] = false + sentCompleteMsg[msgKey] = true + sentFailMsg[msgKey] = false } return nil } -func (r *ShardingDatabaseReconciler) addStandbyShards(instance *databasev4.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) addStandbyShards(instance *databasev4.ShardingDatabase) error { //var result ctrl.Result return nil } // ========== Delete Shard Section==================== -func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev4.ShardingDatabase, idx int) error { +func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev4.ShardingDatabase) error { var result ctrl.Result var i int32 var err error @@ -1728,7 +1723,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev4.ShardingDa err = shardingv1.InstanceShardPatch(instance, instance, r.Client, i, "isDelete", "failed") if err != nil { msg = "Error occurred while changing the isDelete value to failed in Spec struct" - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) return err } continue @@ -1754,7 +1749,7 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev4.ShardingDa if err != nil { // r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.ChunkMoveError)) msg = "Error occurred while changing the isDelete value to failed in Spec struct" - shardingv1.LogMessages("INFO", msg, nil, instance, r.Log) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) // return err } return err @@ -1895,7 +1890,7 @@ func (r *ShardingDatabaseReconciler) gsmInvitedNodeOp(instance *databasev4.Shard func (r *ShardingDatabaseReconciler) createService(instance *databasev4.ShardingDatabase, dep *corev1.Service, ) (ctrl.Result, error) { - reqLogger := r.Log.WithValues("Instance.Namespace", instance.Spec.Namespace, "Instance.Name", instance.Name) + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) // See if Service already exists and create if it doesn't // We are getting error on nil pointer segment when r.scheme is null // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) @@ -1912,7 +1907,7 @@ func (r *ShardingDatabaseReconciler) createService(instance *databasev4.Sharding err := r.Client.Get(context.TODO(), types.NamespacedName{ Name: dep.Name, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, }, found) jsn, _ := json.Marshal(dep) @@ -1944,7 +1939,7 @@ func (r *ShardingDatabaseReconciler) deployStatefulSet(instance *databasev4.Shar resType string, ) (ctrl.Result, error) { - reqLogger := r.Log.WithValues("Instance.Namespace", instance.Spec.Namespace, "Instance.Name", instance.Name) + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) message := "Inside the deployStatefulSet function" shardingv1.LogMessages("DEBUG", message, nil, instance, r.Log) // See if StatefulSets already exists and create if it doesn't @@ -1961,7 +1956,7 @@ func (r *ShardingDatabaseReconciler) deployStatefulSet(instance *databasev4.Shar found := &appsv1.StatefulSet{} err := r.Client.Get(context.TODO(), types.NamespacedName{ Name: dep.Name, - Namespace: instance.Spec.Namespace, + Namespace: instance.Namespace, }, found) jsn, _ := json.Marshal(dep) shardingv1.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) @@ -2016,34 +2011,29 @@ func (r *ShardingDatabaseReconciler) checkShardState(instance *databasev4.Shardi for i = 0; i < int32(len(instance.Spec.Shard)); i++ { OraShardSpex = instance.Spec.Shard[i] currState = shardingv1.GetGsmShardStatus(instance, OraShardSpex.Name) - if currState == string(databasev4.AddingShardState) { - eventMsg = "Shard Addition in progress. Requeuing" + if OraShardSpex.IsDelete == "failed" { + eventMsg = "Shard Deletion failed for [" + OraShardSpex.Name + "]. Retry shard deletion after manually moving the chunks. Requeuing" + err = fmt.Errorf(eventMsg) + } else if currState == string(databasev4.AddingShardState) { + eventMsg = "Shard Addition in progress for [" + OraShardSpex.Name + "]. Requeuing" err = fmt.Errorf(eventMsg) - break } else if currState == string(databasev4.DeletingState) { - eventMsg = "Shard Deletion in progress. Requeuing" + eventMsg = "Shard Deletion in progress for [" + OraShardSpex.Name + "]. Requeuing" err = fmt.Errorf(eventMsg) err = nil - break - } else if OraShardSpex.IsDelete == "failed" { - eventMsg = "Shard Deletion failed. Manual intervention required. Requeuing" - err = fmt.Errorf(eventMsg) - break } else if currState == string(databasev4.DeleteErrorState) { - eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" + eventMsg = "Shard Deletion Error for [" + OraShardSpex.Name + "]. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) - break } else if currState == string(databasev4.ShardRemoveError) { - eventMsg = "Shard Deletion Error. Manual intervention required. Requeuing" + eventMsg = "Shard Deletion Error for [" + OraShardSpex.Name + "]. Manual intervention required. Requeuing" err = fmt.Errorf(eventMsg) - break } else { - eventMsg = "checkShardState() : Shard State=[" + currState + "]" + eventMsg = "checkShardState() : Shard State[" + OraShardSpex.Name + "]=[" + currState + "]" shardingv1.LogMessages("INFO", eventMsg, nil, instance, r.Log) err = nil } + r.publishEvents(instance, eventMsg, currState) } - r.publishEvents(instance, eventMsg, currState) } return err } From 003f4e4e8dd3e5e6d8e8cfa8c017b94a44e7aa87 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Fri, 6 Dec 2024 03:26:50 +0000 Subject: [PATCH 132/414] Added fixes --- .../v1alpha1/zz_generated.deepcopy.go | 7 + apis/database/v4/zz_generated.deepcopy.go | 7 + ...database.oracle.com_shardingdatabases.yaml | 16 +- config/webhook/manifests.yaml | 75 +- oracle-database-operator.yaml | 838 ++++++++++++++++-- 5 files changed, 782 insertions(+), 161 deletions(-) diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index b79721e3..7d5313c5 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1407,6 +1407,13 @@ func (in *GsmSpec) DeepCopyInto(out *GsmSpec) { (*out)[key] = val } } + if in.PvAnnotations != nil { + in, out := &in.PvAnnotations, &out.PvAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.PvMatchLabels != nil { in, out := &in.PvMatchLabels, &out.PvMatchLabels *out = make(map[string]string, len(*in)) diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index b3b4e964..c79f6818 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -1093,6 +1093,13 @@ func (in *GsmSpec) DeepCopyInto(out *GsmSpec) { (*out)[key] = val } } + if in.PvAnnotations != nil { + in, out := &in.PvAnnotations, &out.PvAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.PvMatchLabels != nil { in, out := &in.PvMatchLabels, &out.PvMatchLabels *out = make(map[string]string, len(*in)) diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 779acea1..446c2393 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -175,6 +175,10 @@ spec: additionalProperties: type: string type: object + pvAnnotations: + additionalProperties: + type: string + type: object pvMatchLabels: additionalProperties: type: string @@ -338,8 +342,6 @@ spec: type: string liveinessCheckPeriod: type: integer - namespace: - type: string portMappings: items: properties: @@ -469,6 +471,8 @@ spec: type: string tdeWalletPvcMountLocation: type: string + topicId: + type: string required: - catalog - dbImage @@ -711,6 +715,10 @@ spec: additionalProperties: type: string type: object + pvAnnotations: + additionalProperties: + type: string + type: object pvMatchLabels: additionalProperties: type: string @@ -874,8 +882,6 @@ spec: type: string liveinessCheckPeriod: type: integer - namespace: - type: string portMappings: items: properties: @@ -1005,6 +1011,8 @@ spec: type: string tdeWalletPvcMountLocation: type: string + topicId: + type: string required: - catalog - dbImage diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 020a3bdb..dc3d56b1 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -52,7 +52,7 @@ webhooks: namespace: system path: /mutate-database-oracle-com-v4-dbcssystem failurePolicy: Fail - name: mdbcssystem.kb.io + name: mdbcssystemv4.kb.io rules: - apiGroups: - database.oracle.com @@ -72,7 +72,7 @@ webhooks: namespace: system path: /mutate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: mshardingdatabase.kb.io + name: mshardingdatabasev4.kb.io rules: - apiGroups: - database.oracle.com @@ -234,14 +234,14 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v4-shardingdatabase + path: /mutate-database-oracle-com-v1alpha1-shardingdatabase failurePolicy: Fail name: mshardingdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v4 + - v1alpha1 operations: - CREATE - UPDATE @@ -269,46 +269,6 @@ webhooks: resources: - singleinstancedatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v4-dbcssystem - failurePolicy: Fail - name: mdbcssystemv4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - dbcssystems - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v4-shardingdatabase - failurePolicy: Fail - name: mshardingdatabasev4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -423,7 +383,7 @@ webhooks: namespace: system path: /validate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: vshardingdatabase.kb.io + name: vshardingdatabasev4.kb.io rules: - apiGroups: - database.oracle.com @@ -606,14 +566,14 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v4-shardingdatabase + path: /validate-database-oracle-com-v1alpha1-shardingdatabase failurePolicy: Fail name: vshardingdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v4 + - v1alpha1 operations: - CREATE - UPDATE @@ -643,27 +603,6 @@ webhooks: resources: - singleinstancedatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /validate-database-oracle-com-v4-shardingdatabase - failurePolicy: Fail - name: vshardingdatabasev4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index b6e48604..db486ca7 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -9,9 +9,22 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: autonomouscontainerdatabases.database.oracle.com spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 group: database.oracle.com names: kind: AutonomousContainerDatabase @@ -90,6 +103,76 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + properties: + lifecycleState: + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true storage: true subresources: status: {} @@ -98,9 +181,22 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: autonomousdatabasebackups.database.oracle.com spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 group: database.oracle.com names: kind: AutonomousDatabaseBackup @@ -200,6 +296,97 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + isLongTermBackup: + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + retentionPeriodInDays: + type: integer + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + type: string + timeEnded: + type: string + timeStarted: + type: string + type: + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true storage: true subresources: status: {} @@ -208,9 +395,22 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: autonomousdatabaserestores.database.oracle.com spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 group: database.oracle.com names: kind: AutonomousDatabaseRestore @@ -293,40 +493,372 @@ spec: type: string timeEnded: type: string - timeStarted: + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + type: string + type: object + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + properties: + dbName: + type: string + displayName: + type: string + status: + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: autonomousdatabases.database.oracle.com +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 + group: database.oracle.com + names: + kind: AutonomousDatabase + listKind: AutonomousDatabaseList + plural: autonomousdatabases + shortNames: + - adb + - adbs + singular: autonomousdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + details: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sACD: + properties: + name: + type: string + type: object + ociACD: + properties: + ocid: + type: string + type: object + type: object + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + lifecycleState: + type: string + networkAccess: + properties: + accessControlList: + items: + type: string + type: array + accessType: + enum: + - "" + - PUBLIC + - RESTRICTED + - PRIVATE + type: string + isAccessControlEnabled: + type: boolean + isMTLSConnectionRequired: + type: boolean + privateEndpoint: + properties: + hostnamePrefix: + type: string + nsgOCIDs: + items: + type: string + type: array + subnetOCID: + type: string + type: object + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + type: object + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + required: + - details + type: object + status: + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + type: string + timeCreated: type: string - workRequestOCID: + walletExpiringDate: type: string - required: - - dbName - - displayName - - status - - workRequestOCID type: object type: object served: true - storage: true + storage: false subresources: status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - name: autonomousdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases - shortNames: - - adb - - adbs - singular: autonomousdatabase - scope: Namespaced - versions: - additionalPrinterColumns: - jsonPath: .spec.details.displayName name: Display Name @@ -352,7 +884,7 @@ spec: - jsonPath: .status.timeCreated name: Created type: string - name: v1alpha1 + name: v4 schema: openAPIV3Schema: properties: @@ -2482,6 +3014,10 @@ spec: additionalProperties: type: string type: object + pvAnnotations: + additionalProperties: + type: string + type: object pvMatchLabels: additionalProperties: type: string @@ -2645,8 +3181,6 @@ spec: type: string liveinessCheckPeriod: type: integer - namespace: - type: string portMappings: items: properties: @@ -2776,6 +3310,8 @@ spec: type: string tdeWalletPvcMountLocation: type: string + topicId: + type: string required: - catalog - dbImage @@ -3018,6 +3554,10 @@ spec: additionalProperties: type: string type: object + pvAnnotations: + additionalProperties: + type: string + type: object pvMatchLabels: additionalProperties: type: string @@ -3181,8 +3721,6 @@ spec: type: string liveinessCheckPeriod: type: integer - namespace: - type: string portMappings: items: properties: @@ -3312,6 +3850,8 @@ spec: type: string tdeWalletPvcMountLocation: type: string + topicId: + type: string required: - catalog - dbImage @@ -4135,20 +4675,80 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + path: /mutate-database-oracle-com-v4-autonomousdatabasebackup failurePolicy: Fail - name: mautonomousdatabase.kb.io + name: mautonomousdatabasebackupv4.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 operations: - CREATE - UPDATE resources: - autonomousdatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-dbcssystem + failurePolicy: Fail + name: mdbcssystemv4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - dbcssystems + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: mshardingdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -4157,7 +4757,7 @@ webhooks: namespace: oracle-database-operator-system path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup failurePolicy: Fail - name: mautonomousdatabasebackup.kb.io + name: mautonomousdatabasebackupv1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -4169,6 +4769,26 @@ webhooks: resources: - autonomousdatabasebackups sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabasev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -4279,14 +4899,14 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-shardingdatabase + path: /mutate-database-oracle-com-v1alpha1-shardingdatabase failurePolicy: Fail name: mshardingdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v4 + - v1alpha1 operations: - CREATE - UPDATE @@ -4320,9 +4940,37 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-dbcssystem + path: /mutate-observability-oracle-com-v1alpha1-databaseobserver failurePolicy: Fail - name: mdbcssystemv4.kb.io + name: mdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-autonomouscontainerdatabase + failurePolicy: Fail + name: vautonomouscontainerdatabasev4.kb.io rules: - apiGroups: - database.oracle.com @@ -4332,7 +4980,7 @@ webhooks: - CREATE - UPDATE resources: - - dbcssystems + - autonomouscontainerdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -4340,9 +4988,9 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-shardingdatabase + path: /validate-database-oracle-com-v4-autonomousdatabasebackup failurePolicy: Fail - name: mshardingdatabasev4.kb.io + name: vautonomousdatabasebackupv4.kb.io rules: - apiGroups: - database.oracle.com @@ -4352,7 +5000,7 @@ webhooks: - CREATE - UPDATE resources: - - shardingdatabases + - autonomousdatabasebackups sideEffects: None - admissionReviewVersions: - v1 @@ -4360,47 +5008,39 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-observability-oracle-com-v1alpha1-databaseobserver + path: /validate-database-oracle-com-v4-autonomousdatabaserestore failurePolicy: Fail - name: mdatabaseobserver.kb.io + name: vautonomousdatabaserestorev4.kb.io rules: - apiGroups: - - observability.oracle.com + - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - databaseobservers + - autonomousdatabaserestores sideEffects: None ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-validating-webhook-configuration -webhooks: - admissionReviewVersions: - v1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + path: /validate-database-oracle-com-v4-autonomousdatabase failurePolicy: Fail - name: vautonomouscontainerdatabase.kb.io + name: vautonomousdatabasev4.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - autonomouscontainerdatabases + - autonomousdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -4408,9 +5048,30 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + path: /validate-database-oracle-com-v4-shardingdatabase + failurePolicy: Fail + name: vshardingdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase failurePolicy: Fail - name: vautonomousdatabase.kb.io + name: vautonomouscontainerdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -4420,7 +5081,7 @@ webhooks: - CREATE - UPDATE resources: - - autonomousdatabases + - autonomouscontainerdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -4430,7 +5091,7 @@ webhooks: namespace: oracle-database-operator-system path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup failurePolicy: Fail - name: vautonomousdatabasebackup.kb.io + name: vautonomousdatabasebackupv1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -4450,7 +5111,7 @@ webhooks: namespace: oracle-database-operator-system path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore failurePolicy: Fail - name: vautonomousdatabaserestore.kb.io + name: vautonomousdatabaserestorev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -4462,6 +5123,26 @@ webhooks: resources: - autonomousdatabaserestores sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabasev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -4552,14 +5233,14 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-shardingdatabase + path: /validate-database-oracle-com-v1alpha1-shardingdatabase failurePolicy: Fail name: vshardingdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v4 + - v1alpha1 operations: - CREATE - UPDATE @@ -4589,27 +5270,6 @@ webhooks: resources: - singleinstancedatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-shardingdatabase - failurePolicy: Fail - name: vshardingdatabasev4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 clientConfig: From be9e575891eb5dd9eb7989d14a0bb1a11c9fb9be Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Fri, 6 Dec 2024 04:33:46 +0000 Subject: [PATCH 133/414] Added fixes for webhook --- Makefile | 2 +- main.go | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d58d6525..f52c9ad6 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ BUILDER_IMG = golang:$(GOLANG_VERSION) BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO="false" --build-arg GOLANG_VERSION=$(GOLANG_VERSION) endif docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast - docker build --no-cache=true --platform=linux/amd64 --jobs=2 --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ + docker build --no-cache=true --platform=linux/arm64,linux/amd64 --jobs=2 --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ --build-arg CI_COMMIT_SHA=$(CI_COMMIT_SHA) --build-arg CI_COMMIT_BRANCH=$(CI_COMMIT_BRANCH) \ $(BUILD_ARGS) --manifest $(IMG) . diff --git a/main.go b/main.go index 35a9d5ae..3b40ae4d 100644 --- a/main.go +++ b/main.go @@ -268,13 +268,20 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") os.Exit(1) } - if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") } + if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + } if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") os.Exit(1) } + if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } if err = (&databasev4.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") os.Exit(1) From 242a9f1e6c16bc3440d4ce697c13562bf63c440f Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Tue, 10 Dec 2024 17:01:50 +0000 Subject: [PATCH 134/414] V4 --- PROJECT | 4 + apis/database/v1alpha1/dbcssystem_webhook.go | 2 +- .../database/v4/dbcssystem_pdbconfig_types.go | 6 +- commons/dbcssystem/dbcs_reconciler.go | 18 +- .../database.oracle.com_dbcssystems.yaml | 4 - config/database.oracle.com_DbcsSystem.yaml | 205 +++++++- config/manager/kustomization.yaml | 2 +- controllers/database/dbcssystem_controller.go | 9 +- docs/dbcs/README.md | 72 +-- .../bind_to_existing_dbcs_system.md | 12 +- .../bind_to_existing_dbcs_system.yaml | 6 +- ..._to_existing_dbcs_system_sample_output.log | 141 ++---- docs/dbcs/provisioning/clone_dbcs_system.yaml | 20 + .../clone_dbcs_system_from_backup.yaml | 22 + ..._dbcs_system_from_backup_sample_output.log | 75 +++ .../clone_dbcs_system_from_database.yaml | 22 + ...bcs_system_from_database_sample_output.log | 39 ++ .../clone_dbcs_system_sample_output.log | 60 +++ .../provisioning/clone_from_backup_dbcs.md | 36 ++ docs/dbcs/provisioning/clone_from_database.md | 35 ++ .../provisioning/clone_from_existing_dbcs.md | 36 ++ .../dbcs/provisioning/create_dbcs_with_kms.md | 47 ++ .../dbcs/provisioning/create_dbcs_with_pdb.md | 55 +++ docs/dbcs/provisioning/create_kms.md | 50 ++ docs/dbcs/provisioning/create_pdb.md | 55 +++ .../create_pdb_to_existing_dbcs_system.md | 55 +++ ..._in_existing_dbcs_system_sample_output.log | 1 + ...reatepdb_in_existing_dbcs_system_list.yaml | 27 ++ ...xisting_dbcs_system_list_sample_output.log | 185 ++++++++ .../dbcs_service_migrate_to_kms.yaml | 29 ++ .../dbcs_service_with_2_node_rac.md | 22 +- .../dbcs_service_with_2_node_rac.yaml | 21 +- .../dbcs_service_with_all_parameters_asm.md | 19 +- .../dbcs_service_with_all_parameters_asm.yaml | 26 +- ..._with_all_parameters_asm_sample_output.log | 225 +++------ .../dbcs_service_with_all_parameters_lvm.md | 20 +- .../dbcs_service_with_all_parameters_lvm.yaml | 27 +- .../provisioning/dbcs_service_with_kms.yaml | 28 ++ .../dbcs_service_with_kms_sample_output.log | 1 + .../dbcs_service_with_minimal_parameters.md | 10 +- .../dbcs_service_with_minimal_parameters.yaml | 22 +- ..._with_minimal_parameters_sample_output.log | 177 +++---- .../provisioning/dbcs_service_with_pdb.yaml | 38 ++ .../dbcs_service_with_pdb_sample_output.log | 137 ++++++ docs/dbcs/provisioning/delete_pdb.md | 50 ++ ...eletepdb_in_existing_dbcs_system_list.yaml | 13 + ...xisting_dbcs_system_list_sample_output.log | 8 + docs/dbcs/provisioning/migrate_to_kms.md | 50 ++ .../scale_down_dbcs_system_shape.md | 12 +- .../scale_down_dbcs_system_shape.yaml | 17 +- ...e_down_dbcs_system_shape_sample_output.log | 371 +++------------ .../scale_up_dbcs_system_shape.md | 14 +- .../scale_up_dbcs_system_shape.yaml | 17 +- docs/dbcs/provisioning/scale_up_storage.md | 12 +- docs/dbcs/provisioning/scale_up_storage.yaml | 19 +- .../scale_up_storage_sample_output.log | 440 ++++-------------- .../provisioning/terminate_dbcs_system.yaml | 2 +- .../terminate_dbcs_system_sample_output.log | 8 +- docs/dbcs/provisioning/update_license.md | 14 +- docs/dbcs/provisioning/update_license.yaml | 38 +- main.go | 5 + oracle-database-operator.yaml | 6 +- 62 files changed, 1944 insertions(+), 1255 deletions(-) create mode 100644 docs/dbcs/provisioning/clone_dbcs_system.yaml create mode 100644 docs/dbcs/provisioning/clone_dbcs_system_from_backup.yaml create mode 100644 docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log create mode 100644 docs/dbcs/provisioning/clone_dbcs_system_from_database.yaml create mode 100644 docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log create mode 100644 docs/dbcs/provisioning/clone_dbcs_system_sample_output.log create mode 100644 docs/dbcs/provisioning/clone_from_backup_dbcs.md create mode 100644 docs/dbcs/provisioning/clone_from_database.md create mode 100644 docs/dbcs/provisioning/clone_from_existing_dbcs.md create mode 100644 docs/dbcs/provisioning/create_dbcs_with_kms.md create mode 100644 docs/dbcs/provisioning/create_dbcs_with_pdb.md create mode 100644 docs/dbcs/provisioning/create_kms.md create mode 100644 docs/dbcs/provisioning/create_pdb.md create mode 100644 docs/dbcs/provisioning/create_pdb_to_existing_dbcs_system.md create mode 100644 docs/dbcs/provisioning/createkms_in_existing_dbcs_system_sample_output.log create mode 100644 docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml create mode 100644 docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log create mode 100644 docs/dbcs/provisioning/dbcs_service_migrate_to_kms.yaml create mode 100644 docs/dbcs/provisioning/dbcs_service_with_kms.yaml create mode 100644 docs/dbcs/provisioning/dbcs_service_with_kms_sample_output.log create mode 100644 docs/dbcs/provisioning/dbcs_service_with_pdb.yaml create mode 100644 docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log create mode 100644 docs/dbcs/provisioning/delete_pdb.md create mode 100644 docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml create mode 100644 docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log create mode 100644 docs/dbcs/provisioning/migrate_to_kms.md diff --git a/PROJECT b/PROJECT index f4e75898..ea420230 100644 --- a/PROJECT +++ b/PROJECT @@ -131,6 +131,10 @@ resources: kind: DbcsSystem path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1beta1 - api: crdVersion: v1beta1 namespaced: true diff --git a/apis/database/v1alpha1/dbcssystem_webhook.go b/apis/database/v1alpha1/dbcssystem_webhook.go index 7c8f8957..dc9f8934 100644 --- a/apis/database/v1alpha1/dbcssystem_webhook.go +++ b/apis/database/v1alpha1/dbcssystem_webhook.go @@ -70,7 +70,7 @@ func (r *DbcsSystem) Default() { // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv1alpha1.kb.io,admissionReviewVersions=v1 +// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &DbcsSystem{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v4/dbcssystem_pdbconfig_types.go b/apis/database/v4/dbcssystem_pdbconfig_types.go index f85a1855..2ae361e5 100644 --- a/apis/database/v4/dbcssystem_pdbconfig_types.go +++ b/apis/database/v4/dbcssystem_pdbconfig_types.go @@ -72,9 +72,9 @@ type PDBConfig struct { } type PDBConfigStatus struct { - PdbName *string `mandatory:"true" json:"pdbName"` - ShouldPdbAdminAccountBeLocked *bool `mandatory:"false" json:"shouldPdbAdminAccountBeLocked"` - FreeformTags map[string]string `mandatory:"false" json:"freeformTags"` + PdbName *string `mandatory:"false" json:"pdbName,omitempty"` + ShouldPdbAdminAccountBeLocked *bool `mandatory:"false" json:"shouldPdbAdminAccountBeLocked,omitempty"` + FreeformTags map[string]string `mandatory:"false" json:"freeformTags,omitempty"` PluggableDatabaseId *string `mandatory:"false" json:"pluggableDatabaseId,omitempty"` PdbLifecycleState LifecycleState `json:"pdbState,omitempty"` } diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 48e51d5e..152d4f6a 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -1057,7 +1057,7 @@ func GetListDatabaseRsp(logger logr.Logger, dbClient database.DatabaseClient, db } func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, kubeClient client.Client, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient, databaseID string) error { - log.Info("Existing DB System Getting Updated with new details in UpdateDbcsSystemIdInst") + // log.Info("Existing DB System Getting Updated with new details in UpdateDbcsSystemIdInst") var err error updateFlag := false updateDbcsDetails := database.UpdateDbSystemDetails{} @@ -1075,18 +1075,18 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d } log.Info("Details of updateFlag -> " + fmt.Sprint(updateFlag)) - if dbcs.Spec.DbSystem.CpuCoreCount > 0 && dbcs.Spec.DbSystem.CpuCoreCount != oldSpec.DbSystem.CpuCoreCount { + if dbcs.Spec.DbSystem.CpuCoreCount > 0 && ((dbcs.Spec.DbSystem.CpuCoreCount != oldSpec.DbSystem.CpuCoreCount) || (dbcs.Spec.DbSystem.CpuCoreCount != *&dbcs.Status.CpuCoreCount)) { log.Info("DB System cpu core count is: " + fmt.Sprint(dbcs.Spec.DbSystem.CpuCoreCount) + " DB System old cpu count is: " + fmt.Sprint(oldSpec.DbSystem.CpuCoreCount)) updateDbcsDetails.CpuCoreCount = common.Int(dbcs.Spec.DbSystem.CpuCoreCount) updateFlag = true } - if dbcs.Spec.DbSystem.Shape != "" && dbcs.Spec.DbSystem.Shape != oldSpec.DbSystem.Shape { + if dbcs.Spec.DbSystem.Shape != "" && ((dbcs.Spec.DbSystem.Shape != oldSpec.DbSystem.Shape) || (dbcs.Spec.DbSystem.Shape != *dbcs.Status.Shape)) { // log.Info("DB System desired shape is :" + string(dbcs.Spec.DbSystem.Shape) + "DB System old shape is " + string(oldSpec.DbSystem.Shape)) updateDbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) updateFlag = true } - if dbcs.Spec.DbSystem.LicenseModel != "" && dbcs.Spec.DbSystem.LicenseModel != oldSpec.DbSystem.LicenseModel { + if dbcs.Spec.DbSystem.LicenseModel != "" && ((dbcs.Spec.DbSystem.LicenseModel != oldSpec.DbSystem.LicenseModel) || (dbcs.Spec.DbSystem.LicenseModel != *&dbcs.Status.LicenseModel)) { licenceModel := getLicenceModel(dbcs) // log.Info("DB System desired License Model is :" + string(dbcs.Spec.DbSystem.LicenseModel) + "DB Sytsem old License Model is " + string(oldSpec.DbSystem.LicenseModel)) updateDbcsDetails.LicenseModel = database.UpdateDbSystemDetailsLicenseModelEnum(licenceModel) @@ -1196,11 +1196,11 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { return statusErr } - // // Check the State - // _, err = CheckResourceState(log, dbClient, *dbcs.Spec.Id, "UPDATING", "AVAILABLE") - // if err != nil { - // return err - // } + // Check the State + _, err = CheckResourceState(log, dbClient, *dbcs.Spec.Id, "UPDATING", "AVAILABLE") + if err != nil { + return err + } } return nil diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index 2e656ce5..a8a9e6c9 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -712,10 +712,6 @@ spec: type: string shouldPdbAdminAccountBeLocked: type: boolean - required: - - freeformTags - - pdbName - - shouldPdbAdminAccountBeLocked type: object type: array type: object diff --git a/config/database.oracle.com_DbcsSystem.yaml b/config/database.oracle.com_DbcsSystem.yaml index bde8744d..c342c363 100644 --- a/config/database.oracle.com_DbcsSystem.yaml +++ b/config/database.oracle.com_DbcsSystem.yaml @@ -36,6 +36,53 @@ spec: spec: description: DbcsSystemSpec defines the desired state of DbcsSystem properties: + databaseId: + type: string + dbBackupId: + type: string + dbClone: + description: DbCloneConfig defines the configuration for the database + clone + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + tdeWalletPasswordSecret: + type: string + required: + - dbDbUniqueName + - dbName + - displayName + - hostName + - subnetId + type: object dbSystem: properties: availabilityDomain: @@ -51,7 +98,7 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup COnfig Network Struct + description: DB Backup Config Network Struct properties: autoBackupEnabled: type: boolean @@ -88,10 +135,19 @@ spec: type: string initialDataStorageSizeInGB: type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object licenseModel: type: string nodeCount: @@ -124,17 +180,80 @@ spec: - dbAdminPaswordSecret - hostName - shape - - sshPublicKeys - subnetId type: object hardLink: type: boolean id: type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object ociConfigMap: type: string ociSecret: type: string + pdbConfigs: + items: + description: PDBConfig defines details of PDB struct for DBCS systems + properties: + freeformTags: + additionalProperties: + type: string + description: '// Free-form tags for this resource. Each tag + is a simple key-value pair with no predefined name, type, + or namespace. // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). + // Example: `{"Department": "Finance"}`' + type: object + isDelete: + description: To specify whether to delete the PDB + type: boolean + pdbAdminPassword: + description: // A strong password for PDB Admin. The password + must be at least nine characters and contain at least two + uppercase, two lowercase, two numbers, and two special characters. + The special characters must be _, \#, or -. + type: string + pdbName: + description: The name for the pluggable database (PDB). The + name is unique in the context of a Database. The name must + begin with an alphabetic character and can contain a maximum + of thirty alphanumeric characters. Special characters are + not permitted. The pluggable database name should not be same + as the container database name. + type: string + pluggableDatabaseId: + description: The OCID of the PDB for deletion purposes. + type: string + shouldPdbAdminAccountBeLocked: + description: // The locked mode of the pluggable database admin + account. If false, the user needs to provide the PDB Admin + Password to connect to it. // If true, the pluggable database + will be locked and user cannot login to it. + type: boolean + tdeWalletPassword: + description: // The existing TDE wallet password of the CDB. + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean required: - ociConfigMap type: object @@ -149,6 +268,35 @@ spec: type: integer dataStorageSizeInGBs: type: integer + dbCloneStatus: + description: DbCloneStatus defines the observed state of DbClone + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + id: + type: string + licenseModel: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + required: + - dbDbUniqueName + - hostName + type: object dbEdition: type: string dbInfo: @@ -171,6 +319,25 @@ spec: type: string id: type: string + kmsDetailsStatus: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyId: + type: string + keyName: + type: string + managementEndpoint: + type: string + vaultId: + type: string + vaultName: + type: string + vaultType: + type: string + type: object licenseModel: type: string network: @@ -192,6 +359,32 @@ spec: type: object nodeCount: type: integer + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + required: + - freeformTags + - pdbName + - shouldPdbAdminAccountBeLocked + type: object + type: array + type: object + type: array recoStorageSizeInGB: type: integer shape: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 1acf74e3..74303bea 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: rac-operator + newTag: basedb-operator-sa diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index 7d9b646f..a746d7ba 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -88,7 +88,6 @@ type DbcsSystemReconciler struct { // +kubebuilder:rbac:groups=database.oracle.com,resources=dbcssystems/finalizers,verbs=get;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=configmaps;secrets;namespaces,verbs=get;list;watch;create;update;patch;delete - // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // TODO(user): Modify the Reconcile function to compare the state specified by @@ -148,7 +147,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("OCI provider configured succesfully") /* - Using Finalizer for object deletion + Using Finalizer for object deletion */ if dbcsInst.ObjectMeta.DeletionTimestamp.IsZero() { @@ -201,7 +200,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } /* - Determine whether it's a provision or bind operation + Determine whether it's a provision or bind operation */ lastSuccessfullSpec, err := dbcsInst.GetLastSuccessfulSpec() if err != nil { @@ -407,7 +406,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } } else if !setupCloning { if dbcsInst.Spec.Id == nil && lastSuccessfullSpec == nil { - // If no DbcsSystem ID specified, create a DB System + // If no DbcsSystem ID specified, create a new DB System // ======================== Validate Specs ============== err = dbcsv1.ValidateSpex(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.Recorder) if err != nil { @@ -451,7 +450,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } assignDBCSID(dbcsInst, dbcsID) } else { - if lastSuccessfullSpec == nil { + if lastSuccessfullSpec == nil { // first time after creation of DB if err := dbcsv1.GetDbSystemId(r.Logger, r.dbClient, dbcsInst); err != nil { // Change the status to Failed if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md index c8b8d5d9..d5b6f4dd 100644 --- a/docs/dbcs/README.md +++ b/docs/dbcs/README.md @@ -22,7 +22,7 @@ Two-node Oracle RAC DB systems require Oracle Enterprise Edition - Extreme Perfo For standard provisioning of DB systems (using Oracle Automatic Storage Management (ASM) as your storage management software), the following database releases are supported: -- Oracle Database 21c +- Oracle Database 23ai - Oracle Database 19c - Oracle Database 18c (18.0) - Oracle Database 12c Release 2 (12.2) @@ -32,7 +32,7 @@ For standard provisioning of DB systems (using Oracle Automatic Storage Manageme For fast provisioning of single-node virtual machine database systems (using Logical Volume Manager as your storage management software), the following database releases are supported: -- Oracle Database 21c +- Oracle Database 23ai - Oracle Database 19c - Oracle Database 18c - Oracle Database 12c Release 2 (12.2) @@ -43,34 +43,33 @@ For fast provisioning of single-node virtual machine database systems (using Log To deploy OraOperator, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. After the Oracle Database Operator is deployed, you can see the DB operator pods running in the Kubernetes Cluster. As part of the OraOperator deployment, the DBCS Controller is deployed as a CRD (Custom Resource Definition). The following screen output is an example of such a deployment: -``` +```bash [root@test-server oracle-database-operator]# kubectl get ns NAME STATUS AGE -cert-manager Active 2m5s -default Active 125d -kube-node-lease Active 125d -kube-public Active 125d -kube-system Active 125d -oracle-database-operator-system Active 17s <<<< namespace to deploy the Oracle Database Operator +cert-manager Active 33d +default Active 118d +kube-node-lease Active 118d +kube-public Active 118d +kube-system Active 118d +oracle-database-operator-system Active 10m <<<< namespace to deploy the Oracle Database Operator [root@test-server oracle-database-operator]# kubectl get all -n oracle-database-operator-system NAME READY STATUS RESTARTS AGE -pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 0 28s - +pod/oracle-database-operator-controller-manager-678f96f5f4-f4rhq 1/1 Running 0 10m +pod/oracle-database-operator-controller-manager-678f96f5f4-plxcp 1/1 Running 0 10m +pod/oracle-database-operator-controller-manager-678f96f5f4-qgcg8 1/1 Running 0 10m + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 29s -service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 29s - +service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.197.164 8443/TCP 11m +service/oracle-database-operator-webhook-service ClusterIP 10.96.35.62 443/TCP 11m + NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 29s - +deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 11m + NAME DESIRED CURRENT READY AGE -replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 29s -[root@docker-test-server oracle-database-operator]# - +replicaset.apps/oracle-database-operator-controller-manager-6657bfc664 0 0 0 11m +replicaset.apps/oracle-database-operator-controller-manager-678f96f5f4 3 3 3 10m [root@test-server oracle-database-operator]# kubectl get crd NAME CREATED AT @@ -97,19 +96,19 @@ Before you deploy a DBCS system in OCI using the Oracle DB Operator DBCS Control ## 1. Create a Kubernetes Configmap. For example: We are creating a Kubernetes Configmap named `oci-cred` using the OCI account we are using as below: -``` +```bash kubectl create configmap oci-cred \ ---from-literal=tenancy=ocid1.tenancy.oc1..................67iypsmea \ ---from-literal=user=ocid1.user.oc1..aaaaaaaaxw3i...............ce6qzdrnmq \ ---from-literal=fingerprint=b2:7c:a8:d5:44:f5.....................:9a:55 \ +--from-literal=tenancy= \ +--from-literal=user= \ +--from-literal=fingerprint= \ --from-literal=region=us-phoenix-1 ``` ## 2. Create a Kubernetes secret `oci-privatekey` using the OCI Pem key taken from OCI console for the account you are using: -``` --- assuming the OCI Pem key to be "/root/.oci/oci_api_key.pem" +```bash +#---assuming the OCI Pem key to be "/root/.oci/oci_api_key.pem" kubectl create secret generic oci-privatekey --from-file=privatekey=/root/.oci/oci_api_key.pem ``` @@ -118,8 +117,8 @@ kubectl create secret generic oci-privatekey --from-file=privatekey=/root/.oci/o ## 3. Create a Kubernetes secret named `admin-password`; This passward must meet the minimum passward requirements for the OCI BDBCS Service. For example: -``` --- assuming the passward has been added to a text file named "admin-password": +```bash +#-- assuming the passward has been added to a text file named "admin-password": kubectl create secret generic admin-password --from-file=./admin-password -n default ``` @@ -128,8 +127,8 @@ kubectl create secret generic admin-password --from-file=./admin-password -n def ## 4. Create a Kubernetes secret named `tde-password`; this passward must meet the minimum passward requirements for the OCI BDBCS Service. For example: -``` --- assuming the passward has been added to a text file named "tde-password": +```bash +# -- assuming the passward has been added to a text file named "tde-password": kubectl create secret generic tde-password --from-file=./tde-password -n default ``` @@ -137,7 +136,7 @@ kubectl create secret generic tde-password --from-file=./tde-password -n default ## 5. Create an ssh key pair, and use its public key to create a Kubernetes secret named `oci-publickey`; the private key for this public key can be used later to access the DBCS system's host machine using ssh: -``` +```bash [root@test-server DBCS]# ssh-keygen -N "" -C "DBCS_System"-`date +%Y%m` -P "" Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): @@ -178,7 +177,14 @@ For more informatoin about the multiple use cases available to you to deploy and [7. Terminate an existing BDBCS System](./provisioning/terminate_dbcs_system.md) [8. Create BDBCS with All Parameters with Storage Management as LVM](./provisioning/dbcs_service_with_all_parameters_lvm.md) [9. Create BDBCS with All Parameters with Storage Management as ASM](./provisioning/dbcs_service_with_all_parameters_asm.md) -[10. Deploy a 2 Node RAC DB System using OCI BDBCS Service](./provisioning/dbcs_service_with_2_node_rac.md) +[10. Deploy a 2 Node RAC DB System using OCI BDBCS Service](./provisioning/dbcs_service_with_2_node_rac.md) +[11. Create PDB to an existing DBCS System already deployed in OCI Base DBCS Service](./provisioning/create_pdb_to_existing_dbcs_system.md) +[12. Create Base DBCS with PDB in OCI](./provisioning/create_dbcs_with_pdb.md) +[13. Create Base DBCS with KMS Vault Encryption in OCI](./provisioning/create_dbcs_with_kms.md) +[14. Migrate to KMS vault from TDE Wallet password encryption of an existing DBCS System already deployed in OCI Base DBCS Service](./provisioning/migrate_to_kms.md) +[15. Clone DB System from Existing DB System in OCI Base DBCS Service](./provisioning/clone_from_existing_dbcs.md) +[16. Clone DB System from Backup of Existing DB System in OCI Base DBCS Service](./provisioning/clone_from_backup_dbcs.md) +[17. Clone DB System from Existing Database of DB System in OCI Base DBCS Service](./provisioning/clone_from_database.md) ## Connecting to OCI DBCS database deployed using Oracle DB Operator DBCS Controller diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md index 6fcff5de..099902bc 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md @@ -6,24 +6,24 @@ In this use case, we bind the Oracle DB Operator DBCS Controller to an existing This example uses `bind_to_existing_dbcs_system.yaml` to bind to an existing DBCS VMDB using Oracle DB Operator DBCS Controller with: -- OCI Configmap as `oci-cred` +- OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` -- OCID of the existing DBCS System as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` +- OCID of the existing DBCS System as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` Use the file: [bind_to_existing_dbcs_system.yaml](./bind_to_existing_dbcs_system.yaml) for this use case as below: 1. Deploy the .yaml file: -```sh -[root@docker-test-server DBCS]# kubectl apply -f bind_dbcs.yaml +```bash +kubectl apply -f bind_to_existing_dbcs_system.yaml dbcssystem.database.oracle.com/dbcssystem-existing created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. +2. Monitor the Oracle DB Leader Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. -``` +```bash [root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml index 49647229..9fd60a17 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml @@ -1,8 +1,8 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-existing spec: - id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" - ociConfigMap: "oci-cred" + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" + ociConfigMap: "oci-cred-mumbai" ociSecret: "oci-privatekey" diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system_sample_output.log b/docs/dbcs/provisioning/bind_to_existing_dbcs_system_sample_output.log index 454a4452..f6505337 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system_sample_output.log +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system_sample_output.log @@ -48,130 +48,61 @@ replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 2022-03-08T23:27:48.625Z INFO controller-runtime.manager.controller.dbcssystem OCI provider configured succesfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} 2022-03-08T23:27:52.513Z INFO controller-runtime.manager.controller.dbcssystem Sync information from remote DbcsSystem System successfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -[root@docker-test-server test]# kubectl describe dbcssystems.database.oracle.com dbcssystem-existing +[root@docker-test-server test]# kubectl describe dbcssystems.database.oracle.com dbcssystem-existing Name: dbcssystem-existing Namespace: default Labels: Annotations: lastSuccessfulSpec: - {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya","availabilityDomain":"O... -API Version: database.oracle.com/v1alpha1 + {"dbSystem":{"compartmentId":"","availabilityDomain":"","subnetId":"","shape":"","hostName":"","dbAdminPaswordSecret":"","dbBackupConfig":... +API Version: database.oracle.com/v4 Kind: DbcsSystem Metadata: - Creation Timestamp: 2022-03-08T23:27:48Z + Creation Timestamp: 2024-12-06T15:16:07Z Generation: 1 - Managed Fields: - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - .: - f:kubectl.kubernetes.io/last-applied-configuration: - f:spec: - .: - f:id: - f:ociConfigMap: - f:ociSecret: - Manager: kubectl-client-side-apply - Operation: Update - Time: 2022-03-08T23:27:48Z - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - f:lastSuccessfulSpec: - f:spec: - f:dbSystem: - .: - f:availabilityDomain: - f:compartmentId: - f:cpuCoreCount: - f:dbAdminPaswordSecret: - f:dbBackupConfig: - f:dbEdition: - f:dbName: - f:dbUniqueName: - f:dbVersion: - f:diskRedundancy: - f:displayName: - f:faultDomains: - f:hostName: - f:nodeCount: - f:shape: - f:sshPublicKeys: - f:subnetId: - f:status: - .: - f:availabilityDomain: - f:cpuCoreCount: - f:dataStoragePercentage: - f:dataStorageSizeInGBs: - f:dbEdition: - f:dbInfo: - f:displayName: - f:id: - f:licenseModel: - f:network: - .: - f:clientSubnet: - f:domainName: - f:hostName: - f:listenerPort: - f:scanDnsName: - f:vcnName: - f:nodeCount: - f:recoStorageSizeInGB: - f:shape: - f:state: - f:storageManagement: - f:subnetId: - f:timeZone: - f:workRequests: - Manager: manager - Operation: Update - Time: 2022-03-08T23:27:52Z - Resource Version: 55191827 - UID: 96d7bc49-33e9-42cc-8dd0-ada9a5a4c7e5 + Resource Version: 116146012 + UID: 375b1bea-9b69-4b86-a2b1-fe7750608913 Spec: - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - Oci Config Map: oci-cred + Db System: + Availability Domain: + Compartment Id: + Db Admin Pasword Secret: + Db Backup Config: + Host Name: + Kms Config: + Shape: + Subnet Id: + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htya6crmqdnyz5h7ngpi4azbhndm6ssdmyn7yxk2uhbvxala + Kms Config: + Oci Config Map: oci-cred-mumbai Oci Secret: oci-privatekey Status: - Availability Domain: OLou:PHX-AD-1 - Cpu Core Count: 1 + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 2 Data Storage Percentage: 80 Data Storage Size In G Bs: 256 - Db Edition: ENTERPRISE_EDITION - Db Info: - Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqat52milqwt3gq6lwohhacwg5yi4mtzq7c7hag53lrkugq - Db Name: db0130 - Db Unique Name: db0130_phx1zn - Db Workload: OLTP - Id: ocid1.database.oc1.phx.anyhqljrabf7htyackgmsaqjfexoqgrzuuk33ju2q25z2al43tnd5mhhvkra - Display Name: dbsystem20220308221302 - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - License Model: LICENSE_INCLUDED + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Display Name: dbsystem1234 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htya6crmqdnyz5h7ngpi4azbhndm6ssdmyn7yxk2uhbvxala + License Model: BRING_YOUR_OWN_LICENSE Network: - Client Subnet: k8test-pubvcn - Domain Name: k8testpubvcn.k8test.oraclevcn.com - Host Name: host0130 + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 Listener Port: 1521 - Scan Dns Name: host0130-scan.k8testpubvcn.k8test.oraclevcn.com - Vcn Name: k8test + Scan Dns Name: host1234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db Node Count: 1 Reco Storage Size In GB: 256 - Shape: VM.Standard2.1 + Shape: VM.Standard.E5.Flex State: AVAILABLE Storage Management: ASM - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq Time Zone: UTC Work Requests: - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrxivzvgzel47zuoyke5yk36o7mrgjl27vscd5z3bqptmyh3rxwbqq + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljro3fhuxevjwxlue5gqq63q7rd7uhub2ru6gd6ay6k35f4hdeqqxkq Operation Type: Create DB System Percent Complete: 100 - Time Accepted: 2022-03-08 22:13:02.999 +0000 UTC - Time Finished: 2022-03-08 23:11:50.46 +0000 UTC - Time Started: 2022-03-08 22:13:16.995 +0000 UTC -Events: -[root@docker-test-server test]# \ No newline at end of file + Time Accepted: 2024-12-06 12:12:04.031 +0000 UTC + Time Finished: 2024-12-06 13:01:20.457 +0000 UTC + Time Started: 2024-12-06 12:12:11.041 +0000 UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system.yaml b/docs/dbcs/provisioning/clone_dbcs_system.yaml new file mode 100644 index 00000000..5f52d8e2 --- /dev/null +++ b/docs/dbcs/provisioning/clone_dbcs_system.yaml @@ -0,0 +1,20 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-clone + namespace: default +spec: + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyaqui4hoqdyzmzl65jwkncyp3bnohengniqienetsdzw2q" + ociConfigMap: "oci-cred-mumbai" + ociSecret: "oci-privatekey" + setupDBCloning: true + dbClone: + dbAdminPaswordSecret: "admin-password" + dbName: "db1212" + hostName: "host1213" + displayName: "dbsystem01312" + licenseModel: "BRING_YOUR_OWN_LICENSE" + domain: "subdda0b5eaa.cluster1.oraclevcn.com" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_backup.yaml b/docs/dbcs/provisioning/clone_dbcs_system_from_backup.yaml new file mode 100644 index 00000000..f5d9a393 --- /dev/null +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_backup.yaml @@ -0,0 +1,22 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-clone + namespace: default +spec: + ociConfigMap: "oci-cred-mumbai" + ociSecret: "oci-privatekey" + setupDBCloning: true + dbBackupId: "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyaae3fmnpacavkuwt2zqaj5q3gol2g6m6tirriveytoarq" + dbClone: + dbAdminPaswordSecret: "admin-password" + tdeWalletPasswordSecret: "tde-password" + dbName: "db1212" + hostName: "host1213" + displayName: "dbsystem01312" + licenseModel: "BRING_YOUR_OWN_LICENSE" + domain: "subdda0b5eaa.cluster1.oraclevcn.com" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" + initialDataStorageSizeInGB: 256 \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log b/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log new file mode 100644 index 00000000..82531993 --- /dev/null +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log @@ -0,0 +1,75 @@ +2024-09-18T12:55:33Z INFO Starting the clone process for DBCS from backup {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df", "dbcs": {"apiVersion": "database.oracle.com/v4", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-clone"}} +2024-09-18T12:55:33Z INFO Retrieved existing Db System Details from OCI using Spec.Id {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T12:55:41Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T12:56:42Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T12:57:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T12:58:44Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T12:59:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:00:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:01:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:02:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:03:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:04:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:05:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:06:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:07:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:08:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:09:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:10:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:11:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:12:56Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:13:57Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:14:58Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:15:59Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:17:00Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:18:01Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:19:02Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:20:02Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:21:03Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:22:05Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:23:05Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:24:06Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:25:08Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:26:08Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:27:09Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:28:10Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:29:11Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:30:12Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:31:13Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:32:14Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:33:15Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:34:16Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:35:16Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:36:17Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:37:18Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:38:19Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:39:20Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:40:21Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:41:22Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:42:23Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:43:23Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:44:24Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:45:25Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:46:26Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:47:27Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:48:28Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:49:29Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:50:30Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:51:31Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:52:32Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:53:32Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:54:33Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:55:34Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:56:35Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:57:36Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:58:37Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T13:59:38Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T14:00:39Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T14:01:40Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T14:02:41Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T14:03:42Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T14:04:42Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T14:05:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T14:06:44Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T14:07:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} +2024-09-18T14:08:46Z INFO DB Cloning completed successfully from provided backup DB system. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_database.yaml b/docs/dbcs/provisioning/clone_dbcs_system_from_database.yaml new file mode 100644 index 00000000..53e4bf83 --- /dev/null +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_database.yaml @@ -0,0 +1,22 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-clone + namespace: default +spec: + databaseId: "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyapxtsgw6hy3kyosmrawefq2csm4kjv4d5au7biuiaabsq" + ociConfigMap: "oci-cred-mumbai" + ociSecret: "oci-privatekey" + setupDBCloning: true + dbClone: + dbAdminPaswordSecret: "admin-password" + tdeWalletPasswordSecret: "tde-password" + dbName: "db1212" + hostName: "host1213" + displayName: "dbsystem01312" + licenseModel: "BRING_YOUR_OWN_LICENSE" + domain: "subdda0b5eaa.cluster1.oraclevcn.com" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" + initialDataStorageSizeInGB: 256 \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log b/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log new file mode 100644 index 00000000..2881051d --- /dev/null +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log @@ -0,0 +1,39 @@ +2024-09-19T19:23:08Z INFO Starting the clone process for Database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "4c2b2567-052a-4a27-ae96-e18f655577d1", "dbcs": {"apiVersion": "database.oracle.com/v4", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-clone"}} +2024-09-19T19:23:08Z INFO Retrieved passwords from Kubernetes secrets {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "4c2b2567-052a-4a27-ae96-e18f655577d1"} +2024-09-19T19:23:09Z INFO Retrieved existing Database details from OCI {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "4c2b2567-052a-4a27-ae96-e18f655577d1", "DatabaseId": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyapxtsgw6hy3kyosmrawefq2csm4kjv4d5au7biuiaabsq"} +2024-09-20T08:51:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T08:52:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T08:53:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T08:54:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T08:55:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T08:56:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T08:57:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T08:58:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database +.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone" +, "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T08:59:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:00:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database +.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone" +, "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:01:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:02:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:03:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:04:56Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:52:39Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:53:40Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database +.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone" +, "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:54:41Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:55:41Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database +.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone" +, "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:56:42Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:57:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database +.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone" +, "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:58:44Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T09:59:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database +.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone" +, "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T10:00:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} +2024-09-20T10:01:47Z INFO DB Cloning completed successfully from provided backup DB system {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log b/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log new file mode 100644 index 00000000..22d86e1e --- /dev/null +++ b/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log @@ -0,0 +1,60 @@ +2024-09-17T11:40:26Z INFO Starting the clone process for DBCS {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3", "dbcs": {"apiVersion": "database.oracle.com/v4", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-clone.yaml"}} +2024-09-17T11:40:26Z INFO Retrieved passwords from Kubernetes secrets {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:40:26Z INFO Retrieved existing Db System Details from OCI using Spec.Id {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:40:33Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:41:33Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:42:34Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:43:35Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:44:36Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:45:37Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:46:38Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:47:39Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:48:40Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:49:41Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:50:42Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:51:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:52:44Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:53:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:54:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:55:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:56:47Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:57:48Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:58:49Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T11:59:50Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:00:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:01:51Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:02:52Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:03:53Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:04:54Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:05:55Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:06:56Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:07:57Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:08:58Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:09:59Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:11:00Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:12:01Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:13:01Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:14:02Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:15:03Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:16:04Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:17:05Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:18:06Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:19:07Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:20:08Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:21:08Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:22:09Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:23:10Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:24:11Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:25:12Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:26:13Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:27:14Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:28:15Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:29:16Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:30:16Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:31:17Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:32:18Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:33:19Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:34:20Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:35:21Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:36:22Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} +2024-09-17T12:36:22Z INFO DB Cloning completed successfully from provided db system {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} diff --git a/docs/dbcs/provisioning/clone_from_backup_dbcs.md b/docs/dbcs/provisioning/clone_from_backup_dbcs.md new file mode 100644 index 00000000..b511e153 --- /dev/null +++ b/docs/dbcs/provisioning/clone_from_backup_dbcs.md @@ -0,0 +1,36 @@ +# Clone DB System from Backup of Existing DB System in OCI Base DBCS Service + +In this use case, an existing OCI DBCS system deployed earlier with the Backup is going to be cloned. + +In order to clone DBCS to an existing DBCS system using Backup, get the details of OCID of backup in OCI DBCS. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +This example uses `clone_dbcs_system_from_backup.yaml` to clone a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- setupDBCloning: as `true` +- OCID of Backup DB as `dbBackupId` of existing DBCS system. +- Specification for DB Cloning as `dbClone`-> `dbAdminPaswordSecret`,`tdeWalletPasswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`, `initialDataStorageSizeInGB` +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [clone_dbcs_system_from_backup.yaml](./clone_dbcs_system_from_backup.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f clone_dbcs_system_from_backup.yaml +dbcssystem.database.oracle.com/dbcssystem-clone created +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./clone_dbcs_system_from_backup_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/clone_from_database.md b/docs/dbcs/provisioning/clone_from_database.md new file mode 100644 index 00000000..049593fc --- /dev/null +++ b/docs/dbcs/provisioning/clone_from_database.md @@ -0,0 +1,35 @@ +# Clone DB System from Existing Database of DB System in OCI Base DBCS Service + +In this use case, an existing OCI DBCS system deployed earlier with existing Database is going to be cloned in OCI Base DBCS Service using existing Database ID. + +As an pre-requisite, get the details of OCID of database of an existing DBCS System which you want to clone. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +This example uses `clone_dbcs_system_from_database.yaml` to clone a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +- OCID of existing as `databaseId` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- setupDBCloning: as `true` +- Specification of dbClone as - Details of new DB system for cloning `dbAdminPaswordSecret`,`tdeWalletPasswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`, `initialDataStorageSizeInGB` +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [clone_dbcs_system_from_database.yaml](./clone_dbcs_system_from_database.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f clone_dbcs_system_from_database.yaml +dbcssystem.database.oracle.com/dbcssystem-clone created +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./clone_dbcs_system_from_database_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/clone_from_existing_dbcs.md b/docs/dbcs/provisioning/clone_from_existing_dbcs.md new file mode 100644 index 00000000..383c9887 --- /dev/null +++ b/docs/dbcs/provisioning/clone_from_existing_dbcs.md @@ -0,0 +1,36 @@ +# Clone DB System from Existing DB System in OCI Base DBCS Service + +In this use case, an existing OCI DBCS system deployed earlier is going to be cloned in OCI Base DBCS Service. Its a 2 Step operation. + +In order to clone DBCS to an existing DBCS system, get the OCID of DB System ID you want to clone. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +This example uses `clone_dbcs_system.yaml` to clone a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCID of existing VMDB as `id` to be cloned. +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- setupDBCloning: as `true` +- Specification of DB System been cloned as `dbClone` -> `dbAdminPaswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`. These must be unique and new details for new cloned DB system to be created. +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [clone_dbcs_system.yaml](./clone_dbcs_system.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f clone_dbcs_system.yaml +dbcssystem.database.oracle.com/dbcssystem-clone created +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./clone_dbcs_system_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/create_dbcs_with_kms.md b/docs/dbcs/provisioning/create_dbcs_with_kms.md new file mode 100644 index 00000000..42d00a66 --- /dev/null +++ b/docs/dbcs/provisioning/create_dbcs_with_kms.md @@ -0,0 +1,47 @@ +# Deploy a DBCS DB System alongwith KMS Vault Encryption in OCI + +In this use case, an OCI DBCS system is deployed using Oracle DB Operator DBCS controller along with KMS Vault configuration + +**NOTE** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + + +This example uses `dbcs_service_with_kms.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- Availability Domain for the DBCS VMDB as `OLou:US-ASHBURN-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` +- Database Admin Credential as `admin-password` +- Database Name as `dbsystem0130` +- Oracle Database Software Image Version as `21c` +- Database Workload Type as Transaction Processing i.e. `OLTP` +- Database Hostname Prefix as `host1205` +- Oracle VMDB Shape as `VM.Standard2.2` +- SSH Public key for the DBCS system being deployed as `oci-publickey` +- domain `subd215df3e6.k8stest.oraclevcn.com` +- OCID of the Subnet as `ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua` +- KMS Vault Name as `basdbvault` +- KMS Compartment Id as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` +- KMS Key Name as `dbvaultkey` + +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). While giving KMS Vault make sure not to pass TDE wallet password in DB creation as either of them can be only used for encryption. + +Use the file: [dbcs_service_with_kms.yaml](./dbcs_service_with_kms.yaml) for this use case as below: + +1. Deploy the .yaml file: +```bash +[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_with_kms.yaml +dbcssystem.database.oracle.com/dbcssystem-create configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. + +NOTE: Check the DB Operator Pod name in your environment. + +```bash +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./dbcs_service_with_kms_sample_output.log) is the sample output for a DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with KMS configurations. diff --git a/docs/dbcs/provisioning/create_dbcs_with_pdb.md b/docs/dbcs/provisioning/create_dbcs_with_pdb.md new file mode 100644 index 00000000..cb49ec51 --- /dev/null +++ b/docs/dbcs/provisioning/create_dbcs_with_pdb.md @@ -0,0 +1,55 @@ +# Deploy a DBCS DB System using OCI DBCS Service alongwith PDB + +In this use case, an OCI DBCS system is deployed using Oracle DB Operator DBCS controller along with PDB configuration + +**NOTE** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +Also, create a Kubernetes secret `pdb-password` using the file: + +```bash +#---assuming the PDB password is in ./pdb-password file" + +kubectl create secret generic pdb-password --from-file=./pdb-password -n default +``` + +This example uses `dbcs_service_with_pdb.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- Availability Domain for the DBCS VMDB as `OLou:US-ASHBURN-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` +- Database Admin Credential as `admin-password` +- Database Name as `dbsystem24` +- Oracle Database Software Image Version as `21c` +- Database Workload Type as Transaction Processing i.e. `OLTP` +- Database Hostname Prefix as `host24` +- Cpu Core Count as `1` +- Oracle VMDB Shape as `VM.Standard2.1` +- SSH Public key for the DBCS system being deployed as `oci-publickey` +- domain `subd215df3e6.k8stest.oraclevcn.com` +- OCID of the Subnet as `ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua` +- PDB Name as `pdb_sauahuja_11` +- TDE Wallet Password as `tde-password` +- PDB Admin Password as `pdb-password` + +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [dbcs_service_with_pdb.yaml](./dbcs_service_with_pdb.yaml) for this use case as below: + +1. Deploy the .yaml file: +```bash +[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_with_pdb.yaml +dbcssystem.database.oracle.com/dbcssystem-create-with-pdb created +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. + +NOTE: Check the DB Operator Pod name in your environment. + +```bash +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./dbcs_service_with_pdb_sample_output.log) is the sample output for a DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with PDB configurations. diff --git a/docs/dbcs/provisioning/create_kms.md b/docs/dbcs/provisioning/create_kms.md new file mode 100644 index 00000000..43db7037 --- /dev/null +++ b/docs/dbcs/provisioning/create_kms.md @@ -0,0 +1,50 @@ +# Create and update KMS vault to an existing DBCS System already deployed in OCI Base DBCS Service + +In this use case, an existing OCI DBCS system deployed earlier is going to have KMS Vault created and update DBCS System in OCI. Its a 2 Step operation. + +In order to create KMS Vaults to an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS Controller. +2. Apply the change to create KMS Vaults. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +```bash +kubectl get dbcssystems +NAME AGE +dbcssystem-existing 3m33s +``` +Below proceeding further create PDB Admin Password which is going to used as name suggests. + + +This example uses `dbcs_service_with_kms.yaml` to create KMS Vault to existing DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- Existing `dbSystem` used before to create DBCS system. +- kmsConfig - vaultName as "basdbvault" as an example. +- kmsConfig - keyName as "dbvaultkey" as an example. +- kmsConfig - compartmentId as "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" as an example. +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [dbcs_service_with_kms.yaml](./dbcs_service_with_kms.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f createpdb_in_existing_dbcs_system_list.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of KMS Vaults. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./createkms_in_existing_dbcs_system_sample_output.log) is the sample output for creation of KMS Vaults on an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/create_pdb.md b/docs/dbcs/provisioning/create_pdb.md new file mode 100644 index 00000000..610ccd41 --- /dev/null +++ b/docs/dbcs/provisioning/create_pdb.md @@ -0,0 +1,55 @@ +# Create PDB to an existing DBCS System + +In this use case, an existing OCI DBCS system deployed earlier is going to have PDB/PDBs created. Its a 2 Step operation. + +In order to create PDBs to an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS Controller. +2. Apply the change to create PDBs. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +```bash +kubectl get dbcssystems +NAME AGE +dbcssystem-existing 3m33s +``` +Below proceeding further create PDB Admin Password which is going to used as name suggests. + +Create a Kubernetes secret `pdb-password` using the file: + +```bash +#---assuming the PDB password is in ./pdb-password file" + +kubectl create secret generic pdb-password --from-file=./pdb-password -n default +``` + +This example uses `createpdb_in_existing_dbcs_system_list.yaml` to scale up a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- TDE Wallet Password as `tde-password` +- PDB Admin Password as `pdb-password` +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [createpdb_in_existing_dbcs_system_list.yaml](./createpdb_in_existing_dbcs_system_list.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f createpdb_in_existing_dbcs_system_list.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./createpdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for creation of PDBs on an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/create_pdb_to_existing_dbcs_system.md b/docs/dbcs/provisioning/create_pdb_to_existing_dbcs_system.md new file mode 100644 index 00000000..37c3fb49 --- /dev/null +++ b/docs/dbcs/provisioning/create_pdb_to_existing_dbcs_system.md @@ -0,0 +1,55 @@ +# Create PDB to an existing DBCS System + +In this use case, an existing OCI DBCS system deployed earlier is going to have a PDB/many PDBs created. Its a 2 Step operation. + +In order to create PDBs to an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS Controller. +2. Apply the change to create PDBs. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +```bash +kubectl get dbcssystems +NAME AGE +dbcssystem-existing 3m33s +``` +Below proceeding further create PDB Admin Password which is going to used as name suggests. + +Create a Kubernetes secret `pdb-password` using the file: + +```bash +#---assuming the PDB password is in ./pdb-password file" + +kubectl create secret generic pdb-password --from-file=./pdb-password -n default +``` + +This example uses `createpdb_in_existing_dbcs_system_list.yaml` to scale up a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htya55wz5vfil7ul3pkzpubnymp6zrp3fhgomv3fcdr2vtiq` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- TDE Wallet Password as `tde-password` +- PDB Admin Password as `pdb-password` +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [createpdb_in_existing_dbcs_system_list.yaml](./createpdb_in_existing_dbcs_system_list.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f createpdb_in_existing_dbcs_system_list.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./createpdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for creation of PDBs on an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/createkms_in_existing_dbcs_system_sample_output.log b/docs/dbcs/provisioning/createkms_in_existing_dbcs_system_sample_output.log new file mode 100644 index 00000000..18ac916e --- /dev/null +++ b/docs/dbcs/provisioning/createkms_in_existing_dbcs_system_sample_output.log @@ -0,0 +1 @@ +# To be added \ No newline at end of file diff --git a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml new file mode 100644 index 00000000..24f9423e --- /dev/null +++ b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml @@ -0,0 +1,27 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-existing +spec: + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" + ociConfigMap: "oci-cred-mumbai" + ociSecret: "oci-privatekey" + pdbConfigs: + - pdbName: "pdb_sauahuja_sdk_13" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "Finance" + - pdbName: "pdb_sauahuja_sdk_14" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "HR" + - pdbName: "pdb_sauahuja_sdk_15" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "IT" \ No newline at end of file diff --git a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log new file mode 100644 index 00000000..9bee73c8 --- /dev/null +++ b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list_sample_output.log @@ -0,0 +1,185 @@ +2024-08-15T14:14:55Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htya5c2ttar7axxqq6qej3allfz23nvrtx6ilka4stdmrpga"]} +2024-08-15T14:14:55Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v4", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-existing"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htya5c2ttar7axxqq6qej3allfz23nvrtx6ilka4stdmrpga", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} +2024-08-15T14:14:55Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_13"} +2024-08-15T14:14:55Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4"} +2024-08-15T14:14:55Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4"} +2024-08-15T14:14:55Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_13"} +2024-08-15T14:14:56Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_13", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahtowm4kb7rwjemwjnyyxy2nv525qqqpmjue2lua3rihq"} +2024-08-15T14:14:56Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahtowm4kb7rwjemwjnyyxy2nv525qqqpmjue2lua3rihq", "Status": "PROVISIONING"} +2024-08-15T14:15:26Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahtowm4kb7rwjemwjnyyxy2nv525qqqpmjue2lua3rihq", "Status": "PROVISIONING"} +2024-08-15T14:15:57Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahtowm4kb7rwjemwjnyyxy2nv525qqqpmjue2lua3rihq", "Status": "PROVISIONING"} +2024-08-15T14:16:27Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahtowm4kb7rwjemwjnyyxy2nv525qqqpmjue2lua3rihq", "Status": "PROVISIONING"} +2024-08-15T14:16:57Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahtowm4kb7rwjemwjnyyxy2nv525qqqpmjue2lua3rihq", "Status": "PROVISIONING"} +2024-08-15T14:17:27Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahtowm4kb7rwjemwjnyyxy2nv525qqqpmjue2lua3rihq", "Status": "PROVISIONING"} +2024-08-15T14:17:57Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahtowm4kb7rwjemwjnyyxy2nv525qqqpmjue2lua3rihq", "Status": "AVAILABLE"} +2024-08-15T14:17:57Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_13", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahtowm4kb7rwjemwjnyyxy2nv525qqqpmjue2lua3rihq"} +2024-08-15T14:17:59Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htya5c2ttar7axxqq6qej3allfz23nvrtx6ilka4stdmrpga"]} +2024-08-15T14:17:59Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v4", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-existing"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htya5c2ttar7axxqq6qej3allfz23nvrtx6ilka4stdmrpga", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} +2024-08-15T14:17:59Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_14"} +2024-08-15T14:17:59Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4"} +2024-08-15T14:17:59Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4"} +2024-08-15T14:18:00Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_14"} +2024-08-15T14:18:00Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_14", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyajgpwlaeyxj72m773xrqm6a4masvm5symlmine6v47llq"} +2024-08-15T14:18:01Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyajgpwlaeyxj72m773xrqm6a4masvm5symlmine6v47llq", "Status": "PROVISIONING"} +2024-08-15T14:18:31Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyajgpwlaeyxj72m773xrqm6a4masvm5symlmine6v47llq", "Status": "PROVISIONING"} +2024-08-15T14:19:01Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyajgpwlaeyxj72m773xrqm6a4masvm5symlmine6v47llq", "Status": "PROVISIONING"} +2024-08-15T14:19:31Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyajgpwlaeyxj72m773xrqm6a4masvm5symlmine6v47llq", "Status": "PROVISIONING"} +2024-08-15T14:20:01Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyajgpwlaeyxj72m773xrqm6a4masvm5symlmine6v47llq", "Status": "PROVISIONING"} +2024-08-15T14:20:32Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyajgpwlaeyxj72m773xrqm6a4masvm5symlmine6v47llq", "Status": "PROVISIONING"} +2024-08-15T14:21:02Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyajgpwlaeyxj72m773xrqm6a4masvm5symlmine6v47llq", "Status": "AVAILABLE"} +2024-08-15T14:21:02Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_14", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyajgpwlaeyxj72m773xrqm6a4masvm5symlmine6v47llq"} +2024-08-15T14:21:03Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htya5c2ttar7axxqq6qej3allfz23nvrtx6ilka4stdmrpga"]} +2024-08-15T14:21:03Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v4", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-existing"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htya5c2ttar7axxqq6qej3allfz23nvrtx6ilka4stdmrpga", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} +2024-08-15T14:21:03Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_15"} +2024-08-15T14:21:03Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4"} +2024-08-15T14:21:03Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4"} +2024-08-15T14:21:04Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_15"} +2024-08-15T14:21:05Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_15", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaq2s5xfn6jpkehpcoclcrzgksbxsj426dynsqyq7ajhla"} +2024-08-15T14:21:05Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaq2s5xfn6jpkehpcoclcrzgksbxsj426dynsqyq7ajhla", "Status": "PROVISIONING"} +2024-08-15T14:21:35Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaq2s5xfn6jpkehpcoclcrzgksbxsj426dynsqyq7ajhla", "Status": "PROVISIONING"} +2024-08-15T14:22:05Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaq2s5xfn6jpkehpcoclcrzgksbxsj426dynsqyq7ajhla", "Status": "PROVISIONING"} +2024-08-15T14:22:36Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaq2s5xfn6jpkehpcoclcrzgksbxsj426dynsqyq7ajhla", "Status": "PROVISIONING"} +2024-08-15T14:23:06Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaq2s5xfn6jpkehpcoclcrzgksbxsj426dynsqyq7ajhla", "Status": "AVAILABLE"} +2024-08-15T14:23:06Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "3ed0d8d1-0d9c-4163-8c71-347c441420b4", "PDBName": "pdb_sauahuja_sdk_15", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaq2s5xfn6jpkehpcoclcrzgksbxsj426dynsqyq7ajhla"} + + +# kubectl describe dbcssystems.database.oracle.com +Name: dbcssystem-existing +Namespace: default +Labels: +Annotations: lastSuccessfulSpec: + {"dbSystem":{"compartmentId":"","availabilityDomain":"","subnetId":"","shape":"","hostName":"","dbAdminPaswordSecret":"","dbBackupConfig":... +API Version: database.oracle.com/v4 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2024-12-10T10:54:17Z + Generation: 4 + Resource Version: 117823935 + UID: c9da1245-3582-4926-b311-c24d75e75003 +Spec: + Db System: + Availability Domain: + Compartment Id: + Db Admin Pasword Secret: + Db Backup Config: + Host Name: + Kms Config: + Shape: + Subnet Id: + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Config: + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey + Pdb Configs: + Freeform Tags: + Department: Finance + Pdb Admin Password: pdb-password + Pdb Name: pdb_sauahuja_sdk_13 + Should Pdb Admin Account Be Locked: false + Tde Wallet Password: tde-password + Freeform Tags: + Department: HR + Pdb Admin Password: pdb-password + Pdb Name: pdb_sauahuja_sdk_14 + Should Pdb Admin Account Be Locked: false + Tde Wallet Password: tde-password + Freeform Tags: + Department: IT + Pdb Admin Password: pdb-password + Pdb Name: pdb_sauahuja_sdk_15 + Should Pdb Admin Account Be Locked: false + Tde Wallet Password: tde-password +Status: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 1 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 512 + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Display Name: dbsystem1234 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Details Status: + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Listener Port: 1521 + Scan Dns Name: host1234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Pdb Details Status: + Pdb Config Status: + Freeform Tags: + Department: IT + Pdb Name: pdb_sauahuja_sdk_15 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyazfddwgjlmpm3tctcnmqe7zwefzghr4wttij6u4lhh7bq + Pdb Config Status: + Pdb Name: cdb1_pdb1 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyakgj4wuabus6z5kmalvob6r6b7vivkbsmmh7bjprzbuwa + Pdb Config Status: + Freeform Tags: + Department: Finance + Pdb Name: pdb_sauahuja_sdk_13 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyakkcrbhf6cit3z2hbcvded5g2rc7r5obbxeax7dv527xq + Pdb Config Status: + Freeform Tags: + Department: HR + Pdb Name: pdb_sauahuja_sdk_14 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnht5ctcopuntaj74ptum27tbdk5rouvnfq5f2y3eyna + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrtpnjzjidageolva6ytlzjfb2lqhbbrivm4lsb67xyjzyyke6bt4a + Operation Type: Update Shape + Percent Complete: 100 + Time Accepted: 2024-12-10 08:57:53.547 +0000 UTC + Time Finished: 2024-12-10 09:14:04.572 +0000 UTC + Time Started: 2024-12-10 08:57:57.588 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrxg7gov22vlcbqbnxrkl7t7xkcfya6w6gvck344jdf5vtqgw5wzgq + Operation Type: Update DB System + Percent Complete: 100 + Time Accepted: 2024-12-10 08:57:43.701 +0000 UTC + Time Finished: 2024-12-10 09:14:22.705 +0000 UTC + Time Started: 2024-12-10 08:57:53.873 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrk2efvqjda2t7k5iaerahw7wcyz5dq2zev2k55gmq2gvsjkui7hxq + Operation Type: Create DB System + Percent Complete: 100 + Time Accepted: 2024-12-10 05:19:52.499 +0000 UTC + Time Finished: 2024-12-10 07:59:19.083 +0000 UTC + Time Started: 2024-12-10 05:19:55.747 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr4qmf6rdtcbrc5p2q7bev3igugtpgfbwc2laht22yyjzr2srrg7vq + Operation Type: Update DB System + Percent Complete: 100 + Time Accepted: 2024-12-10 10:57:27.313 +0000 UTC + Time Finished: 2024-12-10 11:15:50.597 +0000 UTC + Time Started: 2024-12-10 10:57:45.242 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr2vehqv3vgrxr5mrmd6hoqxg2zr6m5eaunv3ip6bcrubcpvhudmia + Operation Type: Update Shape + Percent Complete: 100 + Time Accepted: 2024-12-10 10:57:44.95 +0000 UTC + Time Finished: 2024-12-10 11:15:40.364 +0000 UTC + Time Started: 2024-12-10 10:57:54.082 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr36bt7ot5oq3otch4bu2axn3azkicot4zuwgwmxeupxr4siisydja + Operation Type: Scale Storage + Percent Complete: 100 + Time Accepted: 2024-12-10 11:44:49.369 +0000 UTC + Time Finished: 2024-12-10 11:58:45.01 +0000 UTC + Time Started: 2024-12-10 11:44:55.544 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrxdpmmaipuqke5yx3szyfnf2zwkfptz3jevlq3coicecfjihnm4kq + Operation Type: Scale Storage + Percent Complete: 100 + Time Accepted: 2024-12-10 11:44:55.255 +0000 UTC + Time Finished: 2024-12-10 11:58:25.229 +0000 UTC + Time Started: 2024-12-10 11:44:57.743 +0000 UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.yaml b/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.yaml new file mode 100644 index 00000000..acbbd89b --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.yaml @@ -0,0 +1,29 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-existing +spec: + id: "ocid1.dbsystem.oc1.iad.anuwcljsabf7htyaoja4v2kx5rcfe5w2onndjfpqjhjoakxgwxo2sbgei5iq" + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + dbSystem: + availabilityDomain: "OLou:US-ASHBURN-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" + dbAdminPaswordSecret: "admin-password" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "testdb1" + displayName: "dbsystem0131" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "21c" + dbWorkload: "OLTP" + hostName: "host1206" + shape: "VM.Standard2.2" + domain: "subd215df3e6.k8stest.oraclevcn.com" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua" + tdeWalletPasswordSecret: "tde-password" + kmsConfig: + vaultName: "basdbvault" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" + keyName: "dbvaultkey" \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md index 1cfbe006..f7b1e319 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md +++ b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md @@ -6,31 +6,31 @@ In this use case, a 2 Node RAC OCI DBCS system is deployed using Oracle DB Opera This example uses `dbcs_service_with_2_node_rac.yaml` to deploy a 2 Node RAC VMDB using Oracle DB Operator DBCS Controller with: -- OCI Configmap as `oci-cred` +- OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:PHX-AD-1` - Cluster Name as `maa-cluster` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya` +- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` - Enable flag for Automatic Backup for DBCS Database as `True` - Auto Backup Window for DBCS Database as `SLOT_FOUR` - Recovery Windows for Backup retention in days as `15` -- Oracle Database Edition as `ENTERPRISE_EDITION_EXTREME_PERFORMANCE` +- Oracle Database Edition as `STANDARD_EDITION` - Database Name as `db0130` -- Oracle Database Software Image Version as `21c` +- Oracle Database Software Image Version as `19c` - Database Workload Type as Transaction Processing i.e. `OLTP` - Redundancy of the ASM Disks as `EXTERNAL` -- Display Name for the DBCS System as `dbsystem0130` -- Database Hostname Prefix as `host0130` +- Display Name for the DBCS System as `dbsys123` +- Database Hostname Prefix as `host01234` - Initial Size of the DATA Storage in GB as `256` - License Model as `BRING_YOUR_OWN_LICENSE` -- Node count as `2` -- Name of the PDB to be created as `PDB0130` +- Name of the PDB to be created as `PDB0123` - Private IP explicitly assigned to be `10.0.1.99` -- Oracle VMDB Shape as `VM.Standard2.2` +- Node count as `2` +- Oracle VMDB Shape as `VM.Standard2.1` - SSH Public key for the DBCS system being deployed as `oci-publickey` - Storage Management type as `ASM` -- OCID of the Subnet as `ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a` +- OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbv` - Tag the DBCS system with two key value pairs as `"TEST": "test_case_provision"` and `"CreatedBy": "MAA_TEAM"` - TDE Wallet Secret as `tde-password` - Time Zone for the DBCS System as `Europe/Berlin` diff --git a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml index 168cb427..01d80595 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml @@ -1,26 +1,27 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-create spec: - ociConfigMap: "oci-cred" + ociConfigMap: "oci-cred-mumbai" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:PHX-AD-1" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" clusterName: "maa-cluster" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPaswordSecret: "admin-password" dbBackupConfig: autoBackupEnabled: True autoBackupWindow: "SLOT_FOUR" recoveryWindowsInDays: 15 - dbEdition: "ENTERPRISE_EDITION_EXTREME_PERFORMANCE" - dbName: "db0130" - dbVersion: "21c" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "cdb12" + displayName: "dbsys123" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "19c" dbWorkload: "OLTP" diskRedundancy: "EXTERNAL" - displayName: "dbsystem0130" - hostName: "host0130" + hostName: "host01234" initialDataStorageSizeInGB: 256 licenseModel: "BRING_YOUR_OWN_LICENSE" nodeCount: 2 @@ -30,7 +31,7 @@ spec: sshPublicKeys: - "oci-publickey" storageManagement: "ASM" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" tags: "TEST": "test_case_provision" "CreatedBy": "MAA_TEAM" diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md index 5ef94c30..64a24cf9 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md @@ -6,30 +6,29 @@ In this use case, the an OCI DBCS system is deployed using Oracle DB Operator DB This example uses `dbcs_service_with_all_parameters_asm.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: -- OCI Configmap as `oci-cred` +- OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:PHX-AD-1` -- Cluster Name as `maa-cluster` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya` +- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` - Enable flag for Automatic Backup for DBCS Database as `True` - Auto Backup Window for DBCS Database as `SLOT_FOUR` - Recovery Windows for Backup retention in days as `15` - Oracle Database Edition as `STANDARD_EDITION` - Database Name as `db0130` -- Oracle Database Software Image Version as `21c` +- Oracle Database Software Image Version as `19c` - Database Workload Type as Transaction Processing i.e. `OLTP` - Redundancy of the ASM Disks as `EXTERNAL` -- Display Name for the DBCS System as `dbsystem0130` -- Database Hostname Prefix as `host0130` +- Display Name for the DBCS System as `dbsys123` +- Database Hostname Prefix as `host01234` - Initial Size of the DATA Storage in GB as `256` - License Model as `BRING_YOUR_OWN_LICENSE` -- Name of the PDB to be created as `PDB0130` +- Name of the PDB to be created as `PDB0123` - Private IP explicitly assigned to be `10.0.1.99` -- Oracle VMDB Shape as `VM.Standard2.1` +- Oracle VMDB Shape as `VM.Standard2.1` - SSH Public key for the DBCS system being deployed as `oci-publickey` - Storage Management type as `ASM` -- OCID of the Subnet as `ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a` +- OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbv` - Tag the DBCS system with two key value pairs as `"TEST": "test_case_provision"` and `"CreatedBy": "MAA_TEAM"` - TDE Wallet Secret as `tde-password` - Time Zone for the DBCS System as `Europe/Berlin` diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml index 1dcec54c..7146d1f5 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml @@ -1,34 +1,36 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-create spec: - ociConfigMap: "oci-cred" + ociConfigMap: "oci-cred-mumbai" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" + clusterName: "maa-cluster" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPaswordSecret: "admin-password" dbBackupConfig: autoBackupEnabled: True autoBackupWindow: "SLOT_FOUR" recoveryWindowsInDays: 15 - dbEdition: "STANDARD_EDITION" - dbName: "db0130" - dbVersion: "21c" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "cdb12" + displayName: "dbsys123" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "19c" dbWorkload: "OLTP" diskRedundancy: "EXTERNAL" - displayName: "dbsystem0130" - hostName: "host0130" + hostName: "host01234" initialDataStorageSizeInGB: 256 licenseModel: "BRING_YOUR_OWN_LICENSE" - pdbName: "PDB0130" + pdbName: "PDB0123" privateIp: "10.0.1.99" - shape: "VM.Standard2.1" + shape: "VM.Standard2.2" sshPublicKeys: - "oci-publickey" storageManagement: "ASM" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" tags: "TEST": "test_case_provision" "CreatedBy": "MAA_TEAM" diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm_sample_output.log index a2fc6690..eec26016 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm_sample_output.log +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm_sample_output.log @@ -1,35 +1,36 @@ [root@docker-test-server test]# cat dbcs_service_with_all_parameters_asm.yaml -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-create spec: - ociConfigMap: "oci-cred" + ociConfigMap: "oci-cred-mumbai" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPaswordSecret: "admin-password" dbBackupConfig: autoBackupEnabled: True autoBackupWindow: "SLOT_FOUR" recoveryWindowsInDays: 15 - dbEdition: "STANDARD_EDITION" - dbName: "db0130" - dbVersion: "21c" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "cdb12" + displayName: "dbsys123" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "19c" dbWorkload: "OLTP" diskRedundancy: "EXTERNAL" - displayName: "dbsystem0130" - hostName: "host0130" + hostName: "host01234" initialDataStorageSizeInGB: 256 licenseModel: "BRING_YOUR_OWN_LICENSE" - pdbName: "PDB0130" + pdbName: "PDB0123" privateIp: "10.0.1.99" - shape: "VM.Standard2.1" + shape: "VM.Standard.E5.Flex" sshPublicKeys: - "oci-publickey" storageManagement: "ASM" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" tags: "TEST": "test_case_provision" "CreatedBy": "MAA_TEAM" @@ -41,9 +42,6 @@ spec: dbcssystem.database.oracle.com/dbcssystem-create created - - - [root@docker-test-server test]# kubectl get ns kubectl get allNAME STATUS AGE @@ -145,169 +143,100 @@ replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 [root@docker-test-server test]# kubectl describe dbcssystems.database.oracle.com dbcssystem-create +user/ $ k describe dbcssystems/dbcssystem-create Name: dbcssystem-create Namespace: default Labels: Annotations: lastSuccessfulSpec: - {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya","availabilityDomain":"O... -API Version: database.oracle.com/v1alpha1 + {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a","availabilityDomain":"O... +API Version: database.oracle.com/v4 Kind: DbcsSystem Metadata: - Creation Timestamp: 2022-03-09T02:59:43Z - Generation: 1 - Managed Fields: - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - .: - f:kubectl.kubernetes.io/last-applied-configuration: - f:spec: - .: - f:dbSystem: - .: - f:availabilityDomain: - f:compartmentId: - f:dbAdminPaswordSecret: - f:dbBackupConfig: - .: - f:autoBackupEnabled: - f:autoBackupWindow: - f:recoveryWindowsInDays: - f:dbEdition: - f:dbName: - f:dbVersion: - f:dbWorkload: - f:diskRedundancy: - f:displayName: - f:hostName: - f:initialDataStorageSizeInGB: - f:licenseModel: - f:pdbName: - f:privateIp: - f:shape: - f:sshPublicKeys: - f:storageManagement: - f:subnetId: - f:tags: - .: - f:CreatedBy: - f:TEST: - f:tdeWalletPasswordSecret: - f:timeZone: - f:ociConfigMap: - f:ociSecret: - Manager: kubectl-client-side-apply - Operation: Update - Time: 2022-03-09T02:59:43Z - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - f:lastSuccessfulSpec: - f:spec: - f:id: - f:status: - .: - f:availabilityDomain: - f:cpuCoreCount: - f:dataStoragePercentage: - f:dataStorageSizeInGBs: - f:dbEdition: - f:dbInfo: - f:displayName: - f:id: - f:licenseModel: - f:network: - .: - f:clientSubnet: - f:domainName: - f:hostName: - f:listenerPort: - f:scanDnsName: - f:vcnName: - f:nodeCount: - f:recoStorageSizeInGB: - f:shape: - f:state: - f:storageManagement: - f:subnetId: - f:timeZone: - f:workRequests: - Manager: manager - Operation: Update - Time: 2022-03-09T03:59:22Z - Resource Version: 55276756 - UID: e7d874e7-3cd7-4b8b-8cd1-32d68795a38c + Creation Timestamp: 2024-12-09T09:42:08Z + Generation: 2 + Resource Version: 117337682 + UID: cc31eb51-56bc-48f5-926b-2453710b1592 Spec: Db System: - Availability Domain: OLou:PHX-AD-1 - Compartment Id: ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a Db Admin Pasword Secret: admin-password Db Backup Config: Auto Backup Enabled: true Auto Backup Window: SLOT_FOUR Recovery Windows In Days: 15 - Db Edition: STANDARD_EDITION - Db Name: db0130 - Db Version: 21c + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Name: cdb12 + Db Version: 19c Db Workload: OLTP Disk Redundancy: EXTERNAL - Display Name: dbsystem0130 - Host Name: host0130 + Display Name: dbsys123 + Host Name: host01234 Initial Data Storage Size In GB: 256 - License Model: BRING_YOUR_OWN_LICENSE - Pdb Name: PDB0130 - Private Ip: 10.0.1.99 - Shape: VM.Standard2.1 + Kms Config: + License Model: BRING_YOUR_OWN_LICENSE + Pdb Name: PDB0123 + Private Ip: 10.0.1.99 + Shape: VM.Standard.E5.Flex Ssh Public Keys: oci-publickey Storage Management: ASM - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq Tags: Created By: MAA_TEAM TEST: test_case_provision Tde Wallet Password Secret: tde-password Time Zone: Europe/Berlin - Oci Config Map: oci-cred - Oci Secret: oci-privatekey + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyap33onviyojoimevpazf4wtbnfsi5v5izah2s365wmyka + Kms Config: + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey Status: - Availability Domain: OLou:PHX-AD-1 - Cpu Core Count: 1 + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 2 Data Storage Percentage: 80 Data Storage Size In G Bs: 256 - Db Edition: STANDARD_EDITION + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE Db Info: - Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqaubltt77vlwmsx7w5d5dvq6be7isglwbpqijfi5gflh5a - Db Name: db0130 - Db Unique Name: db0130_phx1sw + Db Home Id: ocid1.dbhome.oc1.ap-mumbai-1.anrg6ljrqlb5nxiav4nm27oy6tfbqqyukvcgba7nalyozrgwfvkt5f25fazq + Db Name: cdb12 + Db Unique Name: cdb12_z4b_bom Db Workload: OLTP - Id: ocid1.database.oc1.phx.anyhqljrabf7htya5bzvoxrrc2qu6yjw6c27hcsx32bp7c76vzy35kesa2nq - Display Name: dbsystem0130 - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyaz42sxinatef6xieeppxmwg3bwlw5chpefc52s4joraxq - License Model: BRING_YOUR_OWN_LICENSE + Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyanuajzgrh6u5qtvlui4e7jtfwbcnx7lcplw36dy4u4fza + Display Name: dbsys123 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyap33onviyojoimevpazf4wtbnfsi5v5izah2s365wmyka + Kms Details Status: + License Model: BRING_YOUR_OWN_LICENSE Network: - Client Subnet: k8test-pubvcn - Domain Name: k8testpubvcn.k8test.oraclevcn.com - Host Name: host0130 - Listener Port: 1521 - Scan Dns Name: host0130-scan.k8testpubvcn.k8test.oraclevcn.com - Vcn Name: k8test - Node Count: 1 - Reco Storage Size In GB: 256 - Shape: VM.Standard2.1 - State: AVAILABLE - Storage Management: ASM - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a - Time Zone: Europe/Berlin + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host01234 + Listener Port: 1521 + Scan Dns Name: host01234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Pdb Details Status: + Pdb Config Status: + Freeform Tags: + Created By: MAA_TEAM + TEST: test_case_provision + Pdb Name: PDB0123 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htya6wvrskrlk2fazy4pa25jcbinks7vsjdv4kxf5t6nxcxq + Reco Storage Size In GB: 256 + Shape: VM.Standard.E5.Flex + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC Work Requests: - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrlpxe723pq3z5fkeyfgbu4ewsysjcdrxiyxigponwosy44uhcpcsq + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrq63rk37tfqyu64lwason4rczllxmd5nk5iovdzbqkkk2d4nwp5ka Operation Type: Create DB System Percent Complete: 100 - Time Accepted: 2022-03-09 02:59:48.969 +0000 UTC - Time Finished: 2022-03-09 03:56:52.77 +0000 UTC - Time Started: 2022-03-09 02:59:56.287 +0000 UTC -Events: -[root@docker-test-server test]# \ No newline at end of file + Time Accepted: 2024-12-09 09:42:14.521 +0000 UTC + Time Finished: 2024-12-09 10:32:30.77 +0000 UTC + Time Started: 2024-12-09 09:42:21.084 +0000 UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md index d6beeb16..408e1aa9 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md @@ -6,27 +6,29 @@ In this use case, the an OCI DBCS system is deployed using Oracle DB Operator DB This example uses `dbcs_service_with_all_parameters_lvm.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: -- OCI Configmap as `oci-cred` +- OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:PHX-AD-1` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya` +- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` - Enable flag for Automatic Backup for DBCS Database as `True` - Auto Backup Window for DBCS Database as `SLOT_FOUR` - Recovery Windows for Backup retention in days as `15` - Oracle Database Edition as `STANDARD_EDITION` - Database Name as `db0130` -- Oracle Database Software Image Version as `21c` +- Oracle Database Software Image Version as `19c` - Database Workload Type as Transaction Processing i.e. `OLTP` -- Display Name for the DBCS System as `dbsystem0130` -- Database Hostname Prefix as `host0130` +- Redundancy of the ASM Disks as `EXTERNAL` +- Display Name for the DBCS System as `dbsys123` +- Database Hostname Prefix as `host01234` - Initial Size of the DATA Storage in GB as `256` - License Model as `BRING_YOUR_OWN_LICENSE` -- Name of the PDB to be created as `PDB0130` -- Oracle VMDB Shape as `VM.Standard2.1` +- Name of the PDB to be created as `PDB0123` +- Private IP explicitly assigned to be `10.0.1.99` +- Oracle VMDB Shape as `VM.Standard2.1` - SSH Public key for the DBCS system being deployed as `oci-publickey` - Storage Management type as `LVM` -- OCID of the Subnet as `ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a` +- OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbv` - Tag the DBCS system with two key value pairs as `"TEST": "test_case_provision"` and `"CreatedBy": "MAA_TEAM"` - TDE Wallet Secret as `tde-password` - Time Zone for the DBCS System as `Europe/Berlin` diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml index 73208317..afc976d5 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml @@ -1,32 +1,35 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-create spec: - ociConfigMap: "oci-cred" + ociConfigMap: "oci-cred-mumbai" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPaswordSecret: "admin-password" dbBackupConfig: autoBackupEnabled: True autoBackupWindow: "SLOT_FOUR" recoveryWindowsInDays: 15 - dbEdition: "STANDARD_EDITION" - dbName: "db0130" - dbVersion: "21c" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "cdb12" + displayName: "dbsys123" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "19c" dbWorkload: "OLTP" - displayName: "dbsystem0130" - hostName: "host0130" + diskRedundancy: "EXTERNAL" + hostName: "host01234" initialDataStorageSizeInGB: 256 licenseModel: "BRING_YOUR_OWN_LICENSE" - pdbName: "PDB0130" - shape: "VM.Standard2.1" + pdbName: "PDB0123" + privateIp: "10.0.1.99" + shape: "VM.Standard.E5.Flex" sshPublicKeys: - "oci-publickey" storageManagement: "LVM" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" tags: "TEST": "test_case_provision" "CreatedBy": "MAA_TEAM" diff --git a/docs/dbcs/provisioning/dbcs_service_with_kms.yaml b/docs/dbcs/provisioning/dbcs_service_with_kms.yaml new file mode 100644 index 00000000..6d9b716b --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_kms.yaml @@ -0,0 +1,28 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-create + namespace: default +spec: + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + dbSystem: + availabilityDomain: "OLou:US-ASHBURN-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" + dbAdminPaswordSecret: "admin-password" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "testdb" + displayName: "dbsystem0130" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "21c" + dbWorkload: "OLTP" + hostName: "host1205" + shape: "VM.Standard2.2" + domain: "subd215df3e6.k8stest.oraclevcn.com" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua" + kmsConfig: + vaultName: "basdbvault" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" + keyName: "dbvaultkey" \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_kms_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_kms_sample_output.log new file mode 100644 index 00000000..82ac7e05 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_kms_sample_output.log @@ -0,0 +1 @@ +# To be Added \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md index f6bc8185..b2ba70a1 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md @@ -8,17 +8,17 @@ This example uses `dbcs_service_with_minimal_parameters.yaml` to deploy a Single - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:EU-MILAN-1-AD-1` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaaks5baeqlvv4kyj2jiwnrbxgzm3gsumcfy4c6ntj2ro5i3a5gzhhq` +- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Compartment OCID as `cid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` -- Database Name as `dbsystem0130` +- Database Name as `dbsystem1234` - Oracle Database Software Image Version as `19c` - Database Workload Type as Transaction Processing i.e. `OLTP` -- Database Hostname Prefix as `host1205` +- Database Hostname Prefix as `host1234` - Oracle VMDB Shape as `VM.Standard2.1` - SSH Public key for the DBCS system being deployed as `oci-publickey` - domain `vcndns.oraclevcn.com` -- OCID of the Subnet as `ocid1.subnet.oc1.eu-milan-1.aaaaaaaaeiy3tvcsnyg6upfp3ydtu7jmfnmoyifq2ax6y45b5qpdbpide5xa` +- OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml index dc02cfa3..52cb0a68 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml @@ -1,25 +1,23 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-create spec: - ociConfigMap: "oci-cred" + ociConfigMap: "oci-cred-mumbai" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:EU-MILAN-1-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaaks5baeqlvv4kyj2jiwnrbxgzm3gsumcfy4c6ntj2ro5i3a5gzhhq" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPaswordSecret: "admin-password" dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" - dbName: "testdb" - displayName: "dbsystem0130" + dbName: "cdb1" + displayName: "dbsystem1234" licenseModel: "BRING_YOUR_OWN_LICENSE" dbVersion: "19c" dbWorkload: "OLTP" - hostName: "host1205" + hostName: "host1234" shape: "VM.Standard2.1" - domain: "vcndns.oraclevcn.com" - sshPublicKeys: + domain: "subdda0b5eaa.cluster1.oraclevcn.com" + sshPublicKeys: - "oci-publickey" - subnetId: "ocid1.subnet.oc1.eu-milan-1.aaaaaaaaeiy3tvcsnyg6upfp3ydtu7jmfnmoyifq2ax6y45b5qpdbpide5xa" - - + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log index 133f109c..5135daf7 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log @@ -1,135 +1,82 @@ -/usr/bin/kubectl describe dbcssystems.database.oracle.com dbcssystem-create +kubectl describe dbcssystems/dbcssystem-create Name: dbcssystem-create Namespace: default Labels: -Annotations: kubectl.kubernetes.io/last-applied-configuration: - {"apiVersion":"database.oracle.com/v1alpha1","kind":"DbcsSystem","metadata":{"annotations":{},"name":"dbcssystem-create","namespace":"defa... -API Version: database.oracle.com/v1alpha1 +Annotations: lastSuccessfulSpec: + {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a","availabilityDomain":"O... +API Version: database.oracle.com/v4 Kind: DbcsSystem Metadata: - Creation Timestamp: 2023-12-12T12:59:58Z - Generation: 1 - Managed Fields: - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - Fields V 1: - F : Metadata: - F : Annotations: - .: - F : Kubectl . Kubernetes . Io / Last - Applied - Configuration: - F : Spec: - .: - F : Db System: - .: - F : Availability Domain: - F : Compartment Id: - F : Db Admin Pasword Secret: - F : Db Edition: - F : Db Name: - F : Db Version: - F : Db Workload: - F : Display Name: - F : Domain: - F : Host Name: - F : License Model: - F : Shape: - F : Ssh Public Keys: - F : Subnet Id: - F : Oci Config Map: - F : Oci Secret: - Manager: kubectl - Operation: Update - Time: 2023-12-12T12:59:58Z - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - Fields V 1: - F : Status: - .: - F : Availability Domain: - F : Cpu Core Count: - F : Data Storage Percentage: - F : Data Storage Size In G Bs: - F : Db Edition: - F : Db Info: - F : Display Name: - F : Id: - F : License Model: - F : Network: - .: - F : Client Subnet: - F : Domain Name: - F : Host Name: - F : Listener Port: - F : Vcn Name: - F : Node Count: - F : Reco Storage Size In GB: - F : Shape: - F : State: - F : Storage Management: - F : Subnet Id: - F : Time Zone: - F : Work Requests: - Manager: manager - Operation: Update - Subresource: status - Time: 2023-12-12T14:12:36Z - Resource Version: 1571919 - UID: e11353f3-1334-4ca8-af31-4b638442f429 + Creation Timestamp: 2024-12-10T05:19:46Z + Generation: 2 + Resource Version: 117717259 + UID: 3ff13686-50ec-41e3-81c8-77bb6b5a8afa Spec: Db System: - Availability Domain: OLou:EU-MILAN-1-AD-1 - Compartment Id: ocid1.compartment.oc1..aaaaaaaaks5baeqlvv4kyj2jiwnrbxgzm3gsumcfy4c6ntj2ro5i3a5gzhhq + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a Db Admin Pasword Secret: admin-password - Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE - Db Name: testdb - Db Version: 19c - Db Workload: OLTP - Display Name: dbsystem0130 - Domain: vcndns.oraclevcn.com - Host Name: host1205 - License Model: BRING_YOUR_OWN_LICENSE - Shape: VM.Standard2.1 + Db Backup Config: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Name: cdb1 + Db Version: 19c + Db Workload: OLTP + Display Name: dbsystem1234 + Domain: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Kms Config: + License Model: BRING_YOUR_OWN_LICENSE + Shape: VM.Standard2.1 Ssh Public Keys: oci-publickey - Subnet Id: ocid1.subnet.oc1.eu-milan-1.aaaaaaaaeiy3tvcsnyg6upfp3ydtu7jmfnmoyifq2ax6y45b5qpdbpide5xa - Oci Config Map: oci-cred + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Config: + Oci Config Map: oci-cred-mumbai Oci Secret: oci-privatekey Status: - Availability Domain: OLou:EU-MILAN-1-AD-1 + Availability Domain: OLou:AP-MUMBAI-1-AD-1 Cpu Core Count: 1 Data Storage Percentage: 80 Data Storage Size In G Bs: 256 - Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE Db Info: - Db Home Id: ocid1.dbhome.oc1.eu-milan-1.anwgsljrx2vveliazumnbyvudq3rwkbc4brtamgqzyrjuwfbtx5k7hlqwx2a - Db Name: testdb - Db Unique Name: testdb_fg4_lin + Db Home Id: ocid1.dbhome.oc1.ap-mumbai-1.anrg6ljrqlb5nxiaoqqlaxhx4urdwmefw4il5efzekneuru4bpfv57i7iy6a + Db Name: cdb1 + Db Unique Name: cdb1_tkf_bom Db Workload: OLTP - Id: ocid1.database.oc1.eu-milan-1.anwgsljrabf7htyasoe7b7mtfecc7tdfkp6w5knvvufxmk3phztxfktf6naq - Display Name: dbsystem0130 - Id: ocid1.dbsystem.oc1.eu-milan-1.anwgsljrabf7htyat3fsgcftfilt45bgbrfgawroa2oasamavsluwqyr5aya - License Model: BRING_YOUR_OWN_LICENSE + Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyalxin4xpiggjh4nxlta6o6iq56hjrlh4of2cq6c4qgrqa + Display Name: dbsystem1234 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Details Status: + License Model: BRING_YOUR_OWN_LICENSE Network: - Client Subnet: vcnsbn - Domain Name: vcndns.oraclevcn.com - Host Name: host1205 - Listener Port: 1521 - Vcn Name: vcnnet - Node Count: 1 - Reco Storage Size In GB: 256 - Shape: VM.Standard2.1 - State: PROVISIONING - Storage Management: ASM - Subnet Id: ocid1.subnet.oc1.eu-milan-1.aaaaaaaaeiy3tvcsnyg6upfp3ydtu7jmfnmoyifq2ax6y45b5qpdbpide5xa - Time Zone: UTC + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Listener Port: 1521 + Scan Dns Name: host1234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Pdb Details Status: + Pdb Config Status: + Pdb Name: cdb1_pdb1 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyakgj4wuabus6z5kmalvob6r6b7vivkbsmmh7bjprzbuwa + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC Work Requests: - Operation Id: ocid1.coreservicesworkrequest.oc1.eu-milan-1.abwgsljrkv6jwqtepnxyhnxgtzolw74bqfh5oqlwskq72dqgjpfs5rxu66wa + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrk2efvqjda2t7k5iaerahw7wcyz5dq2zev2k55gmq2gvsjkui7hxq Operation Type: Create DB System - Percent Complete: 0 - Time Accepted: 2023-12-12 13:00:03.156 +0000 UTC - Operation Id: ocid1.coreservicesworkrequest.oc1.eu-milan-1.abwgsljrxudjz2qivun6ypupqhytam4axv4wago7nuauceqyapbceysjukfq - Operation Type: Create DB System - Percent Complete: 0 - Time Accepted: 2023-12-12 14:12:36.047 +0000 UTC -Events: - + Percent Complete: 100 + Time Accepted: 2024-12-10 05:19:52.499 +0000 UTC + Time Finished: 2024-12-10 07:59:19.083 +0000 UTC + Time Started: 2024-12-10 05:19:55.747 +0000 UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml b/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml new file mode 100644 index 00000000..0d2e12a2 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml @@ -0,0 +1,38 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-create-with-pdb + namespace: default +spec: + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + dbSystem: + availabilityDomain: "OLou:US-ASHBURN-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" + dbAdminPaswordSecret: "admin-password" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "dbsys" + displayName: "dbsystem24" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "21c" + dbWorkload: "OLTP" + hostName: "host24" + shape: "VM.Standard3.Flex" + cpuCoreCount: 1 + domain: "subd215df3e6.k8stest.oraclevcn.com" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua" + pdbConfigs: + - pdbName: "pdb_sauahuja_11" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "Finance" + - pdbName: "pdb_sauahuja_12" + tdeWalletPassword: "tde-password" + pdbAdminPassword: "pdb-password" + shouldPdbAdminAccountBeLocked: false + freeformTags: + Department: "HR" \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log new file mode 100644 index 00000000..15946a43 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_pdb_sample_output.log @@ -0,0 +1,137 @@ +2024-08-14T13:59:34Z INFO DbcsSystem system provisioned succesfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267"} +2024-08-14T13:59:35Z INFO DBInst after assignment {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-create-with-pdb"}} +2024-08-14T13:59:36Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyarsik3zmfezgl5tvvnmtf7wqm2n3cnvhyx5oo3nk5f6lq"]} +2024-08-14T13:59:36Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-create-with-pdb"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htyarsik3zmfezgl5tvvnmtf7wqm2n3cnvhyx5oo3nk5f6lq", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} +2024-08-14T13:59:36Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBName": "pdb_sauahuja_11"} +2024-08-14T13:59:36Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267"} +2024-08-14T13:59:36Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267"} +2024-08-14T13:59:36Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBName": "pdb_sauahuja_11"} +2024-08-14T13:59:37Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBName": "pdb_sauahuja_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4ydcdycxirop2jjf4htjcq6mnzavf6yyqfsuo74miviq"} +2024-08-14T13:59:37Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4ydcdycxirop2jjf4htjcq6mnzavf6yyqfsuo74miviq", "Status": "PROVISIONING"} +2024-08-14T14:00:07Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4ydcdycxirop2jjf4htjcq6mnzavf6yyqfsuo74miviq", "Status": "PROVISIONING"} +2024-08-14T14:00:38Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4ydcdycxirop2jjf4htjcq6mnzavf6yyqfsuo74miviq", "Status": "PROVISIONING"} +2024-08-14T14:01:08Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4ydcdycxirop2jjf4htjcq6mnzavf6yyqfsuo74miviq", "Status": "PROVISIONING"} +2024-08-14T14:01:38Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4ydcdycxirop2jjf4htjcq6mnzavf6yyqfsuo74miviq", "Status": "PROVISIONING"} +2024-08-14T14:02:08Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4ydcdycxirop2jjf4htjcq6mnzavf6yyqfsuo74miviq", "Status": "PROVISIONING"} +2024-08-14T14:02:38Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4ydcdycxirop2jjf4htjcq6mnzavf6yyqfsuo74miviq", "Status": "AVAILABLE"} +2024-08-14T14:02:38Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBName": "pdb_sauahuja_11", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4ydcdycxirop2jjf4htjcq6mnzavf6yyqfsuo74miviq"} +2024-08-14T14:02:39Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyarsik3zmfezgl5tvvnmtf7wqm2n3cnvhyx5oo3nk5f6lq"]} +2024-08-14T14:02:39Z INFO Calling createPluggableDatabase {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "ctx:->": "context.Background.WithCancel.WithValue(type logr.contextKey, val ).WithValue(type controller.reconcileIDKey, val )", "dbcsInst:->": {"apiVersion": "database.oracle.com/v1alpha1", "kind": "DbcsSystem", "namespace": "default", "name": "dbcssystem-create-with-pdb"}, "databaseIds:->": "ocid1.database.oc1.iad.anuwcljsabf7htyarsik3zmfezgl5tvvnmtf7wqm2n3cnvhyx5oo3nk5f6lq", "compartmentId:->": "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a"} +2024-08-14T14:02:39Z INFO Checking if the pluggable database exists {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBName": "pdb_sauahuja_12"} +2024-08-14T14:02:39Z INFO TDE wallet password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267"} +2024-08-14T14:02:39Z INFO PDB admin password retrieved successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267"} +2024-08-14T14:02:39Z INFO Creating pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBName": "pdb_sauahuja_12"} +2024-08-14T14:02:40Z INFO Pluggable database creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBName": "pdb_sauahuja_12", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q"} +2024-08-14T14:02:40Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q", "Status": "PROVISIONING"} +2024-08-14T14:03:11Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q", "Status": "PROVISIONING"} +2024-08-14T14:03:41Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q", "Status": "PROVISIONING"} +2024-08-14T14:04:11Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q", "Status": "PROVISIONING"} +2024-08-14T14:04:41Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q", "Status": "PROVISIONING"} +2024-08-14T14:05:11Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q", "Status": "PROVISIONING"} +2024-08-14T14:05:42Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q", "Status": "PROVISIONING"} +2024-08-14T14:06:12Z INFO Checking pluggable database status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q", "Status": "AVAILABLE"} +2024-08-14T14:06:12Z INFO Pluggable database successfully created {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-create-with-pdb","namespace":"default"}, "namespace": "default", "name": "dbcssystem-create-with-pdb", "reconcileID": "4a257c84-d61d-4373-aec5-1bca6abf8267", "PDBName": "pdb_sauahuja_12", "PDBID": "ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyahbbg3hf56qhw55cou7465zmuukgv7hh46niu3dsoug3q"} + + +#kubectl describe dbcssystems.database.oracle.com +Name: dbcssystem-create-with-pdb +Namespace: default +Labels: +Annotations: lastSuccessfulSpec: + {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a","availabilityDomain":"O... +API Version: database.oracle.com/v1alpha1 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2024-08-16T09:26:08Z + Generation: 1 + Resource Version: 68483815 + UID: 9dd15628-e47b-4d9c-8bc6-2388e51cba30 +Spec: + Db System: + Availability Domain: OLou:US-ASHBURN-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a + Cpu Core Count: 1 + Db Admin Pasword Secret: admin-password + Db Backup Config: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Name: dbsys + Db Version: 21c + Db Workload: OLTP + Display Name: dbsystem24 + Domain: subd215df3e6.k8stest.oraclevcn.com + Host Name: host24 + Kms Config: + License Model: BRING_YOUR_OWN_LICENSE + Shape: VM.Standard3.Flex + Ssh Public Keys: + oci-publickey + Subnet Id: ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua + Kms Config: + Oci Config Map: oci-cred + Oci Secret: oci-privatekey + Pdb Configs: + Freeform Tags: + Department: Finance + Pdb Admin Password: pdb-password + Pdb Name: pdb_sauahuja_11 + Should Pdb Admin Account Be Locked: false + Tde Wallet Password: tde-password + Freeform Tags: + Department: HR + Pdb Admin Password: pdb-password + Pdb Name: pdb_sauahuja_12 + Should Pdb Admin Account Be Locked: false + Tde Wallet Password: tde-password +Status: + Availability Domain: OLou:US-ASHBURN-AD-1 + Cpu Core Count: 1 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 256 + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Info: + Db Home Id: ocid1.dbhome.oc1.iad.anuwcljsqlb5nxiaqfh3twuegmxxci5boocmowxd6kcczeq6e7jwqezfmbwq + Db Name: dbsys + Db Unique Name: dbsys_dss_iad + Db Workload: OLTP + Id: ocid1.database.oc1.iad.anuwcljsabf7htya5c2ttar7axxqq6qej3allfz23nvrtx6ilka4stdmrpga + Display Name: dbsystem24 + Id: ocid1.dbsystem.oc1.iad.anuwcljsabf7htya55wz5vfil7ul3pkzpubnymp6zrp3fhgomv3fcdr2vtiq + Kms Details Status: + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-nodesubnet-quick-k8s-test-ae2addeb0-regional + Domain Name: subd215df3e6.k8stest.oraclevcn.com + Host Name: host24 + Listener Port: 1521 + Scan Dns Name: host24-scan.subd215df3e6.k8stest.oraclevcn.com + Vcn Name: oke-vcn-quick-k8s-test-ae2addeb0 + Node Count: 1 + Pdb Details Status: + Pdb Config Status: + Freeform Tags: + Department: Finance + Pdb Name: pdb_sauahuja_11 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htya4arzakcgum6mv7h6cqmxhepyrjzfs77mxhqt4f3gylxq + Should Pdb Admin Account Be Locked: false + Pdb Config Status: + Freeform Tags: + Department: HR + Pdb Name: pdb_sauahuja_12 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.iad.anuwcljsabf7htyaiq6cyhxwqr4ad3pfn7g6e6nd2myiibj54tbg7vc27hfa + Should Pdb Admin Account Be Locked: false + Reco Storage Size In GB: 256 + Shape: VM.Standard3.Flex + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua + Time Zone: UTC + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.iad.abuwcljscvomyvuthyc5bnmgi4myo565mbaghtjbhscgvabiy4tyzahjtiba + Operation Type: Create DB System + Percent Complete: 100 + Time Accepted: 2024-08-14 18:28:36.996 +0000 UTC + Time Finished: 2024-08-14 19:44:28.607 +0000 UTC + Time Started: 2024-08-14 18:28:45.134 +0000 UTC +Events: diff --git a/docs/dbcs/provisioning/delete_pdb.md b/docs/dbcs/provisioning/delete_pdb.md new file mode 100644 index 00000000..84d676bc --- /dev/null +++ b/docs/dbcs/provisioning/delete_pdb.md @@ -0,0 +1,50 @@ +# Delete PDB of an existing DBCS System + +In this use case, an existing OCI DBCS system deployed earlier is going to have PDB/PDBs deleted. Its a 2 Step operation. + +In order to create PDBs to an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS Controller. +2. Apply the change to delete PDBs. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +```bash +kubectl get dbcssystems +NAME AGE +dbcssystem-existing 3m33s +``` + +This example uses `deletepdb_in_existing_dbcs_system_list.yaml` to delete PDBs of a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- PDB Name to be deleted e.g `pdb_sauahuja_11` and `pdb_sauahuja_12` +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [deletepdb_in_existing_dbcs_system_list.yaml](./deletepdb_in_existing_dbcs_system_list.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f deletepdb_in_existing_dbcs_system_list.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deletion of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +```bash +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +3. Remove DBCS Systems resource- +```bash +kubectl delete -f deletepdb_in_existing_dbcs_system_list.yaml +``` + +## Sample Output + +[Here](./deletepdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for deletion of PDBs from an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. \ No newline at end of file diff --git a/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml new file mode 100644 index 00000000..fed3ec6c --- /dev/null +++ b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list.yaml @@ -0,0 +1,13 @@ +kind: DbcsSystem +metadata: + name: dbcssystem-existing + namespace: default +spec: + id: "ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza" + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + pdbConfigs: + - pdbName: "pdb_sauahuja_11" + isDelete: true + - pdbName: "pdb_sauahuja_12" + isDelete: true \ No newline at end of file diff --git a/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log new file mode 100644 index 00000000..a4f75fa5 --- /dev/null +++ b/docs/dbcs/provisioning/deletepdb_in_existing_dbcs_system_list_sample_output.log @@ -0,0 +1,8 @@ +2024-07-01T12:34:44Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a"]} +2024-07-01T12:34:44Z INFO Deleting pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_11"} +2024-07-01T12:34:44Z INFO PluggableDatabaseId is not specified, getting pluggable databaseID {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808"} +2024-07-01T12:34:45Z INFO Successfully deleted pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_11"} +2024-07-01T12:34:46Z INFO Database details fetched successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "DatabaseId": ["ocid1.database.oc1.iad.anuwcljsabf7htyaxx3o46rynl5vyduxilwxeeafndy4cwqtkywkhcws435a"]} +2024-07-01T12:34:46Z INFO Deleting pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_12"} +2024-07-01T12:34:46Z INFO PluggableDatabaseId is not specified, getting pluggable databaseID {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808"} +2024-07-01T12:34:47Z INFO Successfully deleted pluggable database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "e9cdd6c8-a381-40c2-b621-5c97467f6808", "PDBName": "pdb_sauahuja_12"} \ No newline at end of file diff --git a/docs/dbcs/provisioning/migrate_to_kms.md b/docs/dbcs/provisioning/migrate_to_kms.md new file mode 100644 index 00000000..b1022d61 --- /dev/null +++ b/docs/dbcs/provisioning/migrate_to_kms.md @@ -0,0 +1,50 @@ +# Create and update KMS vault to an existing DBCS System already deployed in OCI Base DBCS Service + +In this use case, an existing OCI DBCS system deployed earlier having encryption with TDE Wallet Password, will be migrated to have KMS Vault created and update DBCS System in OCI. Its a 2 Step operation. + +In order to create KMS Vaults to an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System (having encryption enabled with TDE Wallet password) to the DBCS Controller. +2. Apply the change to create KMS Vaults. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +```bash +kubectl get dbcssystems +NAME AGE +dbcssystem-create-tde 3m33s +``` +Below proceeding further create PDB Admin Password which is going to used as name suggests. + + +This example uses `dbcs_service_migrate_to_kms.yaml` to create KMS Vault to existing DBCS VMDB having encryption already enabled earlier with TDE Wallet using Oracle DB Operator DBCS Controller with: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyaoja4v2kx5rcfe5w2onndjfpqjhjoakxgwxo2sbgei5iq` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- Existing `dbSystem` used before to create DBCS system. +- kmsConfig - vaultName as `basdbvault` as an example. +- kmsConfig - keyName as `dbvaultkey` as an example. +- kmsConfig - compartmentId as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` as an example. +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [dbcs_service_migrate_to_kms.yaml](./dbcs_service_migrate_to_kms.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_migrate_to_kms.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of KMS Vaults. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./dbcs_service_migrate_to_kms.log) is the sample output for creation of KMS Vaults on an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md index abe98eea..c7c5ba28 100644 --- a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md @@ -11,16 +11,16 @@ In order to scale down an existing DBCS system, the steps will be: This example uses `scale_down_dbcs_system_shape.yaml` to scale down a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: -- OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` -- OCI Configmap as `oci-cred` +- OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` +- OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:PHX-AD-1` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya` +- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72` - Database Admin Credential as `admin-password` -- Database Hostname Prefix as `host0130` +- Database Hostname Prefix as `host1234` - Oracle VMDB target Shape as `VM.Standard2.1` - SSH Public key for the DBCS system being deployed as `oci-publickey` -- OCID of the Subnet as `ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a` +- OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). diff --git a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml index 5e2cfb3f..110c1fd4 100644 --- a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml @@ -1,17 +1,18 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-existing spec: - id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" - ociConfigMap: "oci-cred" + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" + ociConfigMap: "oci-cred-mumbai" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPaswordSecret: "admin-password" - hostName: "host0130" + hostName: "host1234" shape: "VM.Standard2.1" + domain: "subdda0b5eaa.cluster1.oraclevcn.com" sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" diff --git a/docs/dbcs/provisioning/scale_down_dbcs_system_shape_sample_output.log b/docs/dbcs/provisioning/scale_down_dbcs_system_shape_sample_output.log index acc45208..32e0a318 100644 --- a/docs/dbcs/provisioning/scale_down_dbcs_system_shape_sample_output.log +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape_sample_output.log @@ -1,176 +1,6 @@ -[root@docker-test-server test]# kubectl describe dbcssystems.database.oracle.com dbcssystem-existing -Name: dbcssystem-existing -Namespace: default -Labels: -Annotations: lastSuccessfulSpec: - {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya","availabilityDomain":"O... -API Version: database.oracle.com/v1alpha1 -Kind: DbcsSystem -Metadata: - Creation Timestamp: 2022-03-08T23:27:48Z - Generation: 2 - Managed Fields: - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - .: - f:kubectl.kubernetes.io/last-applied-configuration: - f:spec: - .: - f:dbSystem: - .: - f:availabilityDomain: - f:compartmentId: - f:dbAdminPaswordSecret: - f:hostName: - f:shape: - f:sshPublicKeys: - f:subnetId: - f:id: - f:ociConfigMap: - f:ociSecret: - Manager: kubectl-client-side-apply - Operation: Update - Time: 2022-03-08T23:32:50Z - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - f:lastSuccessfulSpec: - f:spec: - f:dbSystem: - f:cpuCoreCount: - f:dbBackupConfig: - f:dbEdition: - f:dbName: - f:dbUniqueName: - f:dbVersion: - f:diskRedundancy: - f:displayName: - f:faultDomains: - f:nodeCount: - f:status: - .: - f:availabilityDomain: - f:cpuCoreCount: - f:dataStoragePercentage: - f:dataStorageSizeInGBs: - f:dbEdition: - f:dbInfo: - f:displayName: - f:id: - f:licenseModel: - f:network: - .: - f:clientSubnet: - f:domainName: - f:hostName: - f:listenerPort: - f:scanDnsName: - f:vcnName: - f:nodeCount: - f:recoStorageSizeInGB: - f:shape: - f:state: - f:storageManagement: - f:subnetId: - f:timeZone: - f:workRequests: - Manager: manager - Operation: Update - Time: 2022-03-08T23:32:55Z - Resource Version: 55197836 - UID: 96d7bc49-33e9-42cc-8dd0-ada9a5a4c7e5 -Spec: - Db System: - Availability Domain: OLou:PHX-AD-1 - Compartment Id: ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya - Db Admin Pasword Secret: admin-password - Host Name: host0130 - Shape: VM.Standard2.2 - Ssh Public Keys: - oci-publickey - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - Oci Config Map: oci-cred - Oci Secret: oci-privatekey -Status: - Availability Domain: OLou:PHX-AD-1 - Cpu Core Count: 2 - Data Storage Percentage: 80 - Data Storage Size In G Bs: 256 - Db Edition: ENTERPRISE_EDITION - Db Info: - Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqat52milqwt3gq6lwohhacwg5yi4mtzq7c7hag53lrkugq - Db Name: db0130 - Db Unique Name: db0130_phx1zn - Db Workload: OLTP - Id: ocid1.database.oc1.phx.anyhqljrabf7htyackgmsaqjfexoqgrzuuk33ju2q25z2al43tnd5mhhvkra - Display Name: dbsystem20220308221302 - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - License Model: LICENSE_INCLUDED - Network: - Client Subnet: k8test-pubvcn - Domain Name: k8testpubvcn.k8test.oraclevcn.com - Host Name: host0130 - Listener Port: 1521 - Scan Dns Name: host0130-scan.k8testpubvcn.k8test.oraclevcn.com - Vcn Name: k8test - Node Count: 1 - Reco Storage Size In GB: 256 - Shape: VM.Standard2.2 - State: AVAILABLE - Storage Management: ASM - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a - Time Zone: UTC - Work Requests: - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrxivzvgzel47zuoyke5yk36o7mrgjl27vscd5z3bqptmyh3rxwbqq - Operation Type: Create DB System - Percent Complete: 100 - Time Accepted: 2022-03-08 22:13:02.999 +0000 UTC - Time Finished: 2022-03-08 23:11:50.46 +0000 UTC - Time Started: 2022-03-08 22:13:16.995 +0000 UTC - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrc3fx6kwq4yxerk3ngztdbbngm7w4dnlddcdhxqxjn6e4kcyux5ca - Operation Type: Update Shape - Percent Complete: 100 - Time Accepted: 2022-03-08 23:33:42.807 +0000 UTC - Time Finished: 2022-03-08 23:46:21.126 +0000 UTC - Time Started: 2022-03-08 23:33:52.109 +0000 UTC -Events: -[root@docker-test-server test]# - - - - -[root@docker-test-server test]# cat scale_down_dbcs_system_shape.yaml -apiVersion: database.oracle.com/v1alpha1 -kind: DbcsSystem -metadata: - name: dbcssystem-existing -spec: - id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" - ociConfigMap: "oci-cred" - ociSecret: "oci-privatekey" - dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" - dbAdminPaswordSecret: "admin-password" - hostName: "host0130" - shape: "VM.Standard2.1" - sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" -[root@docker-test-server test]# -[root@docker-test-server test]# [root@docker-test-server test]# kubectl apply -f scale_down_dbcs_system_shape.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured - - - [root@docker-test-server test]# kubectl get ns kubectl get allNAME STATUS AGE @@ -221,158 +51,103 @@ replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 2022-03-09T00:38:18.344Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} - - - - - - - [root@docker-test-server test]# kubectl describe dbcssystems.database.oracle.com dbcssystem-existing Name: dbcssystem-existing Namespace: default Labels: Annotations: lastSuccessfulSpec: - {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya","availabilityDomain":"O... -API Version: database.oracle.com/v1alpha1 + {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a","availabilityDomain":"O... +API Version: database.oracle.com/v4 Kind: DbcsSystem Metadata: - Creation Timestamp: 2022-03-08T23:27:48Z - Generation: 3 - Managed Fields: - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - .: - f:kubectl.kubernetes.io/last-applied-configuration: - f:spec: - .: - f:dbSystem: - .: - f:availabilityDomain: - f:compartmentId: - f:dbAdminPaswordSecret: - f:hostName: - f:shape: - f:sshPublicKeys: - f:subnetId: - f:id: - f:ociConfigMap: - f:ociSecret: - Manager: kubectl-client-side-apply - Operation: Update - Time: 2022-03-08T23:32:50Z - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - f:lastSuccessfulSpec: - f:spec: - f:dbSystem: - f:cpuCoreCount: - f:dbBackupConfig: - f:dbEdition: - f:dbName: - f:dbUniqueName: - f:dbVersion: - f:diskRedundancy: - f:displayName: - f:faultDomains: - f:nodeCount: - f:status: - .: - f:availabilityDomain: - f:cpuCoreCount: - f:dataStoragePercentage: - f:dataStorageSizeInGBs: - f:dbEdition: - f:dbInfo: - f:displayName: - f:id: - f:licenseModel: - f:network: - .: - f:clientSubnet: - f:domainName: - f:hostName: - f:listenerPort: - f:scanDnsName: - f:vcnName: - f:nodeCount: - f:recoStorageSizeInGB: - f:shape: - f:state: - f:storageManagement: - f:subnetId: - f:timeZone: - f:workRequests: - Manager: manager - Operation: Update - Time: 2022-03-08T23:32:55Z - Resource Version: 55214174 - UID: 96d7bc49-33e9-42cc-8dd0-ada9a5a4c7e5 + Creation Timestamp: 2024-12-10T10:54:17Z + Generation: 2 + Resource Version: 117775637 + UID: c9da1245-3582-4926-b311-c24d75e75003 Spec: Db System: - Availability Domain: OLou:PHX-AD-1 - Compartment Id: ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a Db Admin Pasword Secret: admin-password - Host Name: host0130 - Shape: VM.Standard2.1 + Db Backup Config: + Domain: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Kms Config: + Shape: VM.Standard2.1 Ssh Public Keys: oci-publickey - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - Oci Config Map: oci-cred + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Config: + Oci Config Map: oci-cred-mumbai Oci Secret: oci-privatekey Status: - Availability Domain: OLou:PHX-AD-1 + Availability Domain: OLou:AP-MUMBAI-1-AD-1 Cpu Core Count: 1 Data Storage Percentage: 80 Data Storage Size In G Bs: 256 - Db Edition: ENTERPRISE_EDITION + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE Db Info: - Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqat52milqwt3gq6lwohhacwg5yi4mtzq7c7hag53lrkugq - Db Name: db0130 - Db Unique Name: db0130_phx1zn + Db Home Id: ocid1.dbhome.oc1.ap-mumbai-1.anrg6ljrqlb5nxiaoqqlaxhx4urdwmefw4il5efzekneuru4bpfv57i7iy6a + Db Name: cdb1 + Db Unique Name: cdb1_tkf_bom Db Workload: OLTP - Id: ocid1.database.oc1.phx.anyhqljrabf7htyackgmsaqjfexoqgrzuuk33ju2q25z2al43tnd5mhhvkra - Display Name: dbsystem20220308221302 - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - License Model: LICENSE_INCLUDED + Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyalxin4xpiggjh4nxlta6o6iq56hjrlh4of2cq6c4qgrqa + Display Name: dbsystem1234 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Details Status: + License Model: BRING_YOUR_OWN_LICENSE Network: - Client Subnet: k8test-pubvcn - Domain Name: k8testpubvcn.k8test.oraclevcn.com - Host Name: host0130 - Listener Port: 1521 - Scan Dns Name: host0130-scan.k8testpubvcn.k8test.oraclevcn.com - Vcn Name: k8test - Node Count: 1 - Reco Storage Size In GB: 256 - Shape: VM.Standard2.1 - State: AVAILABLE - Storage Management: ASM - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a - Time Zone: UTC + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Listener Port: 1521 + Scan Dns Name: host1234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Pdb Details Status: + Pdb Config Status: + Pdb Name: cdb1_pdb1 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyakgj4wuabus6z5kmalvob6r6b7vivkbsmmh7bjprzbuwa + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC Work Requests: - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrxivzvgzel47zuoyke5yk36o7mrgjl27vscd5z3bqptmyh3rxwbqq + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrtpnjzjidageolva6ytlzjfb2lqhbbrivm4lsb67xyjzyyke6bt4a + Operation Type: Update Shape + Percent Complete: 100 + Time Accepted: 2024-12-10 08:57:53.547 +0000 UTC + Time Finished: 2024-12-10 09:14:04.572 +0000 UTC + Time Started: 2024-12-10 08:57:57.588 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrxg7gov22vlcbqbnxrkl7t7xkcfya6w6gvck344jdf5vtqgw5wzgq + Operation Type: Update DB System + Percent Complete: 100 + Time Accepted: 2024-12-10 08:57:43.701 +0000 UTC + Time Finished: 2024-12-10 09:14:22.705 +0000 UTC + Time Started: 2024-12-10 08:57:53.873 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrk2efvqjda2t7k5iaerahw7wcyz5dq2zev2k55gmq2gvsjkui7hxq Operation Type: Create DB System Percent Complete: 100 - Time Accepted: 2022-03-08 22:13:02.999 +0000 UTC - Time Finished: 2022-03-08 23:11:50.46 +0000 UTC - Time Started: 2022-03-08 22:13:16.995 +0000 UTC - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrc3fx6kwq4yxerk3ngztdbbngm7w4dnlddcdhxqxjn6e4kcyux5ca - Operation Type: Update Shape + Time Accepted: 2024-12-10 05:19:52.499 +0000 UTC + Time Finished: 2024-12-10 07:59:19.083 +0000 UTC + Time Started: 2024-12-10 05:19:55.747 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr4qmf6rdtcbrc5p2q7bev3igugtpgfbwc2laht22yyjzr2srrg7vq + Operation Type: Update DB System Percent Complete: 100 - Time Accepted: 2022-03-08 23:33:42.807 +0000 UTC - Time Finished: 2022-03-08 23:46:21.126 +0000 UTC - Time Started: 2022-03-08 23:33:52.109 +0000 UTC - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljr5sveun3f6k3zuz23py7mm7jncmpq5vwyajbo5ezhc765347defwq + Time Accepted: 2024-12-10 10:57:27.313 +0000 UTC + Time Finished: 2024-12-10 11:15:50.597 +0000 UTC + Time Started: 2024-12-10 10:57:45.242 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr2vehqv3vgrxr5mrmd6hoqxg2zr6m5eaunv3ip6bcrubcpvhudmia Operation Type: Update Shape Percent Complete: 100 - Time Accepted: 2022-03-09 00:25:03.644 +0000 UTC - Time Finished: 2022-03-09 00:38:59.526 +0000 UTC - Time Started: 2022-03-09 00:25:15.578 +0000 UTC + Time Accepted: 2024-12-10 10:57:44.95 +0000 UTC + Time Finished: 2024-12-10 11:15:40.364 +0000 UTC + Time Started: 2024-12-10 10:57:54.082 +0000 UTC Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md index 8efccb5f..19641629 100644 --- a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md @@ -11,16 +11,16 @@ In order to scale up an existing DBCS system, the steps will be: This example uses `scale_up_dbcs_system_shape.yaml` to scale up a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: -- OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` -- OCI Configmap as `oci-cred` +- OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` +- OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:PHX-AD-1` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya` +- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72` - Database Admin Credential as `admin-password` -- Database Hostname Prefix as `host0130` -- Oracle VMDB Shape as `VM.Standard2.2` +- Database Hostname Prefix as `host1234` +- Oracle VMDB target Shape as `VM.Standard2.2` - SSH Public key for the DBCS system being deployed as `oci-publickey` -- OCID of the Subnet as `ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a` +- OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml index d1c2b95d..7762a0cd 100644 --- a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml @@ -1,17 +1,18 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-existing spec: - id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" - ociConfigMap: "oci-cred" + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" + ociConfigMap: "oci-cred-mumbai" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPaswordSecret: "admin-password" - hostName: "host0130" + hostName: "host1234" shape: "VM.Standard2.2" + domain: "subdda0b5eaa.cluster1.oraclevcn.com" sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" diff --git a/docs/dbcs/provisioning/scale_up_storage.md b/docs/dbcs/provisioning/scale_up_storage.md index 64514025..8f9059e8 100644 --- a/docs/dbcs/provisioning/scale_up_storage.md +++ b/docs/dbcs/provisioning/scale_up_storage.md @@ -11,17 +11,17 @@ In order to scale up storage of an existing DBCS system, the steps will be: This example uses `scale_up_storage.yaml` to scale up storage of an existing Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: -- OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` -- OCI Configmap as `oci-cred` +- OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` +- OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:PHX-AD-1` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya` +- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` -- Database Hostname Prefix as `host0130` +- Database Hostname Prefix as `host1234` - Target Data Storage Size in GBs as `512` - Oracle VMDB Shape as `VM.Standard2.1` - SSH Public key for the DBCS system being deployed as `oci-publickey` -- OCID of the Subnet as `ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a` +- OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` Use the file: [scale_up_storage.yaml](./scale_up_storage.yaml) for this use case as below: diff --git a/docs/dbcs/provisioning/scale_up_storage.yaml b/docs/dbcs/provisioning/scale_up_storage.yaml index 34e64b5e..a757da42 100644 --- a/docs/dbcs/provisioning/scale_up_storage.yaml +++ b/docs/dbcs/provisioning/scale_up_storage.yaml @@ -1,18 +1,19 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-existing spec: - id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" - ociConfigMap: "oci-cred" + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" + ociConfigMap: "oci-cred-mumbai" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPaswordSecret: "admin-password" - hostName: "host0130" - initialDataStorageSizeInGB: 512 + hostName: "host1234" shape: "VM.Standard2.1" + domain: "subdda0b5eaa.cluster1.oraclevcn.com" + initialDataStorageSizeInGB: 512 sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" \ No newline at end of file diff --git a/docs/dbcs/provisioning/scale_up_storage_sample_output.log b/docs/dbcs/provisioning/scale_up_storage_sample_output.log index 667667b8..e703391e 100644 --- a/docs/dbcs/provisioning/scale_up_storage_sample_output.log +++ b/docs/dbcs/provisioning/scale_up_storage_sample_output.log @@ -3,387 +3,111 @@ Name: dbcssystem-existing Namespace: default Labels: Annotations: lastSuccessfulSpec: - {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya","availabilityDomain":"O... -API Version: database.oracle.com/v1alpha1 + {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a","availabilityDomain":"O... +API Version: database.oracle.com/v4 Kind: DbcsSystem Metadata: - Creation Timestamp: 2022-03-08T23:27:48Z + Creation Timestamp: 2024-12-10T10:54:17Z Generation: 3 - Managed Fields: - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - .: - f:kubectl.kubernetes.io/last-applied-configuration: - f:spec: - .: - f:dbSystem: - .: - f:availabilityDomain: - f:compartmentId: - f:dbAdminPaswordSecret: - f:hostName: - f:shape: - f:sshPublicKeys: - f:subnetId: - f:id: - f:ociConfigMap: - f:ociSecret: - Manager: kubectl-client-side-apply - Operation: Update - Time: 2022-03-08T23:32:50Z - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - f:lastSuccessfulSpec: - f:spec: - f:dbSystem: - f:cpuCoreCount: - f:dbBackupConfig: - f:dbEdition: - f:dbName: - f:dbUniqueName: - f:dbVersion: - f:diskRedundancy: - f:displayName: - f:faultDomains: - f:nodeCount: - f:status: - .: - f:availabilityDomain: - f:cpuCoreCount: - f:dataStoragePercentage: - f:dataStorageSizeInGBs: - f:dbEdition: - f:dbInfo: - f:displayName: - f:id: - f:licenseModel: - f:network: - .: - f:clientSubnet: - f:domainName: - f:hostName: - f:listenerPort: - f:scanDnsName: - f:vcnName: - f:nodeCount: - f:recoStorageSizeInGB: - f:shape: - f:state: - f:storageManagement: - f:subnetId: - f:timeZone: - f:workRequests: - Manager: manager - Operation: Update - Time: 2022-03-08T23:32:55Z - Resource Version: 55214174 - UID: 96d7bc49-33e9-42cc-8dd0-ada9a5a4c7e5 + Resource Version: 117788129 + UID: c9da1245-3582-4926-b311-c24d75e75003 Spec: Db System: - Availability Domain: OLou:PHX-AD-1 - Compartment Id: ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a Db Admin Pasword Secret: admin-password - Host Name: host0130 - Shape: VM.Standard2.1 + Db Backup Config: + Domain: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Initial Data Storage Size In GB: 512 + Kms Config: + Shape: VM.Standard2.1 Ssh Public Keys: oci-publickey - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - Oci Config Map: oci-cred + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Config: + Oci Config Map: oci-cred-mumbai Oci Secret: oci-privatekey Status: - Availability Domain: OLou:PHX-AD-1 + Availability Domain: OLou:AP-MUMBAI-1-AD-1 Cpu Core Count: 1 Data Storage Percentage: 80 - Data Storage Size In G Bs: 256 - Db Edition: ENTERPRISE_EDITION + Data Storage Size In G Bs: 512 + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE Db Info: - Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqat52milqwt3gq6lwohhacwg5yi4mtzq7c7hag53lrkugq - Db Name: db0130 - Db Unique Name: db0130_phx1zn + Db Home Id: ocid1.dbhome.oc1.ap-mumbai-1.anrg6ljrqlb5nxiaoqqlaxhx4urdwmefw4il5efzekneuru4bpfv57i7iy6a + Db Name: cdb1 + Db Unique Name: cdb1_tkf_bom Db Workload: OLTP - Id: ocid1.database.oc1.phx.anyhqljrabf7htyackgmsaqjfexoqgrzuuk33ju2q25z2al43tnd5mhhvkra - Display Name: dbsystem20220308221302 - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - License Model: LICENSE_INCLUDED + Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyalxin4xpiggjh4nxlta6o6iq56hjrlh4of2cq6c4qgrqa + Display Name: dbsystem1234 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Details Status: + License Model: BRING_YOUR_OWN_LICENSE Network: - Client Subnet: k8test-pubvcn - Domain Name: k8testpubvcn.k8test.oraclevcn.com - Host Name: host0130 - Listener Port: 1521 - Scan Dns Name: host0130-scan.k8testpubvcn.k8test.oraclevcn.com - Vcn Name: k8test - Node Count: 1 - Reco Storage Size In GB: 256 - Shape: VM.Standard2.1 - State: AVAILABLE - Storage Management: ASM - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a - Time Zone: UTC + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Listener Port: 1521 + Scan Dns Name: host1234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Pdb Details Status: + Pdb Config Status: + Pdb Name: cdb1_pdb1 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyakgj4wuabus6z5kmalvob6r6b7vivkbsmmh7bjprzbuwa + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC Work Requests: - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrxivzvgzel47zuoyke5yk36o7mrgjl27vscd5z3bqptmyh3rxwbqq - Operation Type: Create DB System - Percent Complete: 100 - Time Accepted: 2022-03-08 22:13:02.999 +0000 UTC - Time Finished: 2022-03-08 23:11:50.46 +0000 UTC - Time Started: 2022-03-08 22:13:16.995 +0000 UTC - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrc3fx6kwq4yxerk3ngztdbbngm7w4dnlddcdhxqxjn6e4kcyux5ca + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrtpnjzjidageolva6ytlzjfb2lqhbbrivm4lsb67xyjzyyke6bt4a Operation Type: Update Shape Percent Complete: 100 - Time Accepted: 2022-03-08 23:33:42.807 +0000 UTC - Time Finished: 2022-03-08 23:46:21.126 +0000 UTC - Time Started: 2022-03-08 23:33:52.109 +0000 UTC - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljr5sveun3f6k3zuz23py7mm7jncmpq5vwyajbo5ezhc765347defwq - Operation Type: Update Shape + Time Accepted: 2024-12-10 08:57:53.547 +0000 UTC + Time Finished: 2024-12-10 09:14:04.572 +0000 UTC + Time Started: 2024-12-10 08:57:57.588 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrxg7gov22vlcbqbnxrkl7t7xkcfya6w6gvck344jdf5vtqgw5wzgq + Operation Type: Update DB System Percent Complete: 100 - Time Accepted: 2022-03-09 00:25:03.644 +0000 UTC - Time Finished: 2022-03-09 00:38:59.526 +0000 UTC - Time Started: 2022-03-09 00:25:15.578 +0000 UTC -Events: -[root@docker-test-server test]# - - - -[root@docker-test-server test]# cat scale_up_storage.yaml -apiVersion: database.oracle.com/v1alpha1 -kind: DbcsSystem -metadata: - name: dbcssystem-existing -spec: - id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" - ociConfigMap: "oci-cred" - ociSecret: "oci-privatekey" - dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" - dbAdminPaswordSecret: "admin-password" - hostName: "host0130" - initialDataStorageSizeInGB: 512 - shape: "VM.Standard2.1" - sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" -[root@docker-test-server test]# -[root@docker-test-server test]# kubectl apply -f scale_up_storage.yaml -dbcssystem.database.oracle.com/dbcssystem-existing configured -[root@docker-test-server test]# - - - -[root@docker-test-server test]# kubectl get ns - -kubectl get allNAME STATUS AGE -cert-manager Active 13d -default Active 139d -kube-node-lease Active 139d -kube-public Active 139d -kube-system Active 139d -oracle-database-operator-system Active 13d -shns Active 88d -[root@docker-test-server test]# -[root@docker-test-server test]# kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 3 13d -pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 3 13d -pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 4 13d - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 13d -service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 13d - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 13d - -NAME DESIRED CURRENT READY AGE -replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 13d -[root@docker-test-server test]# - - -[root@docker-test-server test]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-dlhls -n oracle-database-operator-system -. -. -2022-03-09T00:48:11.373Z INFO controller-runtime.manager.controller.dbcssystem OCI provider configured succesfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:48:15.961Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:49:16.273Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:50:16.557Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:51:16.910Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:52:17.277Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:53:17.600Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:54:18.189Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:55:18.506Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:56:18.862Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:57:19.180Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:58:19.544Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T00:59:19.870Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T01:00:20.230Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T01:01:20.663Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T01:02:21.303Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} -2022-03-09T01:03:21.690Z INFO controller-runtime.manager.controller.dbcssystem DB System current state is still:UPDATING. Sleeping for 60 seconds. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-existing", "namespace": "default"} - - - - - -[root@docker-test-server test]# kubectl describe dbcssystems.database.oracle.com dbcssystem-existing -Name: dbcssystem-existing -Namespace: default -Labels: -Annotations: lastSuccessfulSpec: - {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya","availabilityDomain":"O... -API Version: database.oracle.com/v1alpha1 -Kind: DbcsSystem -Metadata: - Creation Timestamp: 2022-03-08T23:27:48Z - Generation: 4 - Managed Fields: - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - f:lastSuccessfulSpec: - f:spec: - f:dbSystem: - f:cpuCoreCount: - f:dbBackupConfig: - f:dbEdition: - f:dbName: - f:dbUniqueName: - f:dbVersion: - f:diskRedundancy: - f:displayName: - f:faultDomains: - f:nodeCount: - f:status: - .: - f:availabilityDomain: - f:cpuCoreCount: - f:dataStoragePercentage: - f:dataStorageSizeInGBs: - f:dbEdition: - f:dbInfo: - f:displayName: - f:id: - f:licenseModel: - f:network: - .: - f:clientSubnet: - f:domainName: - f:hostName: - f:listenerPort: - f:scanDnsName: - f:vcnName: - f:nodeCount: - f:recoStorageSizeInGB: - f:shape: - f:state: - f:storageManagement: - f:subnetId: - f:timeZone: - f:workRequests: - Manager: manager - Operation: Update - Time: 2022-03-08T23:32:55Z - API Version: database.oracle.com/v1alpha1 - Fields Type: FieldsV1 - fieldsV1: - f:metadata: - f:annotations: - .: - f:kubectl.kubernetes.io/last-applied-configuration: - f:spec: - .: - f:dbSystem: - .: - f:availabilityDomain: - f:compartmentId: - f:dbAdminPaswordSecret: - f:hostName: - f:initialDataStorageSizeInGB: - f:shape: - f:sshPublicKeys: - f:subnetId: - f:id: - f:ociConfigMap: - f:ociSecret: - Manager: kubectl-client-side-apply - Operation: Update - Time: 2022-03-09T00:48:11Z - Resource Version: 55222013 - UID: 96d7bc49-33e9-42cc-8dd0-ada9a5a4c7e5 -Spec: - Db System: - Availability Domain: OLou:PHX-AD-1 - Compartment Id: ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya - Db Admin Pasword Secret: admin-password - Host Name: host0130 - Initial Data Storage Size In GB: 512 - Shape: VM.Standard2.1 - Ssh Public Keys: - oci-publickey - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - Oci Config Map: oci-cred - Oci Secret: oci-privatekey -Status: - Availability Domain: OLou:PHX-AD-1 - Cpu Core Count: 1 - Data Storage Percentage: 80 - Data Storage Size In G Bs: 512 - Db Edition: ENTERPRISE_EDITION - Db Info: - Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqat52milqwt3gq6lwohhacwg5yi4mtzq7c7hag53lrkugq - Db Name: db0130 - Db Unique Name: db0130_phx1zn - Db Workload: OLTP - Id: ocid1.database.oc1.phx.anyhqljrabf7htyackgmsaqjfexoqgrzuuk33ju2q25z2al43tnd5mhhvkra - Display Name: dbsystem20220308221302 - Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa - License Model: LICENSE_INCLUDED - Network: - Client Subnet: k8test-pubvcn - Domain Name: k8testpubvcn.k8test.oraclevcn.com - Host Name: host0130 - Listener Port: 1521 - Scan Dns Name: host0130-scan.k8testpubvcn.k8test.oraclevcn.com - Vcn Name: k8test - Node Count: 1 - Reco Storage Size In GB: 256 - Shape: VM.Standard2.1 - State: AVAILABLE - Storage Management: ASM - Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a - Time Zone: UTC - Work Requests: - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrxivzvgzel47zuoyke5yk36o7mrgjl27vscd5z3bqptmyh3rxwbqq + Time Accepted: 2024-12-10 08:57:43.701 +0000 UTC + Time Finished: 2024-12-10 09:14:22.705 +0000 UTC + Time Started: 2024-12-10 08:57:53.873 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrk2efvqjda2t7k5iaerahw7wcyz5dq2zev2k55gmq2gvsjkui7hxq Operation Type: Create DB System Percent Complete: 100 - Time Accepted: 2022-03-08 22:13:02.999 +0000 UTC - Time Finished: 2022-03-08 23:11:50.46 +0000 UTC - Time Started: 2022-03-08 22:13:16.995 +0000 UTC - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrc3fx6kwq4yxerk3ngztdbbngm7w4dnlddcdhxqxjn6e4kcyux5ca - Operation Type: Update Shape + Time Accepted: 2024-12-10 05:19:52.499 +0000 UTC + Time Finished: 2024-12-10 07:59:19.083 +0000 UTC + Time Started: 2024-12-10 05:19:55.747 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr4qmf6rdtcbrc5p2q7bev3igugtpgfbwc2laht22yyjzr2srrg7vq + Operation Type: Update DB System Percent Complete: 100 - Time Accepted: 2022-03-08 23:33:42.807 +0000 UTC - Time Finished: 2022-03-08 23:46:21.126 +0000 UTC - Time Started: 2022-03-08 23:33:52.109 +0000 UTC - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljr5sveun3f6k3zuz23py7mm7jncmpq5vwyajbo5ezhc765347defwq + Time Accepted: 2024-12-10 10:57:27.313 +0000 UTC + Time Finished: 2024-12-10 11:15:50.597 +0000 UTC + Time Started: 2024-12-10 10:57:45.242 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr2vehqv3vgrxr5mrmd6hoqxg2zr6m5eaunv3ip6bcrubcpvhudmia Operation Type: Update Shape Percent Complete: 100 - Time Accepted: 2022-03-09 00:25:03.644 +0000 UTC - Time Finished: 2022-03-09 00:38:59.526 +0000 UTC - Time Started: 2022-03-09 00:25:15.578 +0000 UTC - Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrbaqah6qktukvdlnx66fp2hlevegryfuppsshkqemfcdjtwfwaq3q + Time Accepted: 2024-12-10 10:57:44.95 +0000 UTC + Time Finished: 2024-12-10 11:15:40.364 +0000 UTC + Time Started: 2024-12-10 10:57:54.082 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr36bt7ot5oq3otch4bu2axn3azkicot4zuwgwmxeupxr4siisydja + Operation Type: Scale Storage + Percent Complete: 100 + Time Accepted: 2024-12-10 11:44:49.369 +0000 UTC + Time Finished: 2024-12-10 11:58:45.01 +0000 UTC + Time Started: 2024-12-10 11:44:55.544 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrxdpmmaipuqke5yx3szyfnf2zwkfptz3jevlq3coicecfjihnm4kq Operation Type: Scale Storage Percent Complete: 100 - Time Accepted: 2022-03-09 00:48:54.849 +0000 UTC - Time Finished: 2022-03-09 01:03:10.885 +0000 UTC - Time Started: 2022-03-09 00:49:05.911 +0000 UTC -Events: -[root@docker-test-server test]# \ No newline at end of file + Time Accepted: 2024-12-10 11:44:55.255 +0000 UTC + Time Finished: 2024-12-10 11:58:25.229 +0000 UTC + Time Started: 2024-12-10 11:44:57.743 +0000 UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/terminate_dbcs_system.yaml b/docs/dbcs/provisioning/terminate_dbcs_system.yaml index 075ce54e..a4a2f105 100644 --- a/docs/dbcs/provisioning/terminate_dbcs_system.yaml +++ b/docs/dbcs/provisioning/terminate_dbcs_system.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-terminate diff --git a/docs/dbcs/provisioning/terminate_dbcs_system_sample_output.log b/docs/dbcs/provisioning/terminate_dbcs_system_sample_output.log index ff8afc96..383f823c 100644 --- a/docs/dbcs/provisioning/terminate_dbcs_system_sample_output.log +++ b/docs/dbcs/provisioning/terminate_dbcs_system_sample_output.log @@ -4,13 +4,13 @@ Namespace: default Labels: Annotations: lastSuccessfulSpec: {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya","availabilityDomain":"O... -API Version: database.oracle.com/v1alpha1 +API Version: database.oracle.com/v4 Kind: DbcsSystem Metadata: Creation Timestamp: 2022-03-08T23:27:48Z Generation: 5 Managed Fields: - API Version: database.oracle.com/v1alpha1 + API Version: database.oracle.com/v4 Fields Type: FieldsV1 fieldsV1: f:metadata: @@ -58,7 +58,7 @@ Metadata: Manager: manager Operation: Update Time: 2022-03-08T23:32:55Z - API Version: database.oracle.com/v1alpha1 + API Version: database.oracle.com/v4 Fields Type: FieldsV1 fieldsV1: f:metadata: @@ -164,7 +164,7 @@ Events: [root@docker-test-server test]# cat terminate_dbcs_system.yaml -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-terminate diff --git a/docs/dbcs/provisioning/update_license.md b/docs/dbcs/provisioning/update_license.md index 7c7c43b7..f77f0dd8 100644 --- a/docs/dbcs/provisioning/update_license.md +++ b/docs/dbcs/provisioning/update_license.md @@ -11,17 +11,17 @@ In order to update the license type an existing DBCS system, the steps will be: This example uses `update_license.yaml` to change the license type of a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: -- OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` -- OCI Configmap as `oci-cred` +- OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` +- OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:PHX-AD-1` -- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya` -- Database Admin Credential as `admin-password` -- Database Hostname Prefix as `host0130` +- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Target license model as `BRING_YOUR_OWN_LICENSE` +- Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` +- Database Admin Credential as `admin-password` +- Database Hostname Prefix as `host1234` - Oracle VMDB Shape as `VM.Standard2.1` - SSH Public key for the DBCS system being deployed as `oci-publickey` -- OCID of the Subnet as `ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a` +- OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). diff --git a/docs/dbcs/provisioning/update_license.yaml b/docs/dbcs/provisioning/update_license.yaml index 1fb54a64..b97e16fd 100644 --- a/docs/dbcs/provisioning/update_license.yaml +++ b/docs/dbcs/provisioning/update_license.yaml @@ -1,18 +1,20 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: DbcsSystem -metadata: - name: dbcssystem-existing -spec: - id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" - dbSystem: - availabilityDomain: "OLou:PHX-AD-1" - compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" - dbAdminPaswordSecret: "admin-password" - hostName: "host0130" - licenseModel: "BRING_YOUR_OWN_LICENSE" - shape: "VM.Standard2.1" - sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" - ociConfigMap: "oci-cred" - ociSecret: "oci-privatekey" + apiVersion: database.oracle.com/v4 + kind: DbcsSystem + metadata: + name: dbcssystem-existing + spec: + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" + ociConfigMap: "oci-cred-mumbai" + ociSecret: "oci-privatekey" + dbSystem: + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" + dbAdminPaswordSecret: "admin-password" + hostName: "host1234" + licenseModel: "BRING_YOUR_OWN_LICENSE" + shape: "VM.Standard2.1" + domain: "subdda0b5eaa.cluster1.oraclevcn.com" + initialDataStorageSizeInGB: 512 + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" diff --git a/main.go b/main.go index 3b40ae4d..c9feef64 100644 --- a/main.go +++ b/main.go @@ -270,6 +270,11 @@ func main() { } if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + os.Exit(1) + } + if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) } if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index db486ca7..d300555d 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -2307,10 +2307,6 @@ spec: type: string shouldPdbAdminAccountBeLocked: type: boolean - required: - - freeformTags - - pdbName - - shouldPdbAdminAccountBeLocked type: object type: array type: object @@ -5316,7 +5312,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:rac-operator + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa imagePullPolicy: Always name: manager ports: From 7c14b791e2df8cf1015fd791142a4c04dfa220c2 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Wed, 11 Dec 2024 15:02:36 +0000 Subject: [PATCH 135/414] Enhancement/mqureshi/pipeline --- .gitlab-ci.yml | 13 +++++++++---- Makefile | 28 +++++++++++++++++++++++----- config/manager/kustomization.yaml | 4 ++-- oracle-database-operator.yaml | 2 +- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ede6e88e..cfbc829a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,11 +7,16 @@ build-operator: script: - export GOROOT=$(go1.23.3 env GOROOT) - export PATH="${GOROOT}/bin:${PATH}" - - make docker-build IMG="$IMAGE" - - docker manifest push "$IMAGE" - - docker manifest rm "$IMAGE" && docker system prune -f - make operator-yaml IMG=$IMAGE - - if [ "$CI_COMMIT_BRANCH" != "master" ]; then sed -i "s/\(replicas.\) 3/\1 1/g" ./$OP_YAML; fi + - if [ "$CI_COMMIT_BRANCH" = "master" ]; then + make docker-build docker-push IMG="$IMAGE" BUILD_MANIFEST=true; + docker manifest rm "$IMAGE"; + else + make docker-build docker-push IMG="$IMAGE"; + docker rmi "$IMAGE"; + sed -i "s/\(replicas.\) 3/\1 1/g" ./$OP_YAML; + fi + - docker system prune -f - curl -s --netrc-file $HOME/.netrc_gitlab $ARTIFACTORY_REPO/$CI_COMMIT_BRANCH/$OP_YAML -T ./$OP_YAML only: variables: diff --git a/Makefile b/Makefile index f52c9ad6..a388f472 100644 --- a/Makefile +++ b/Makefile @@ -77,17 +77,27 @@ BUILDER_IMG = oraclelinux:9 BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) --build-arg INSTALL_GO=true else BUILDER_IMG = golang:$(GOLANG_VERSION) -#BUILDER_IMG = phx.ocir.io/intsanjaysingh/db-repo/oracle/golang:1.23.3 BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO="false" --build-arg GOLANG_VERSION=$(GOLANG_VERSION) endif +ifeq ($(BUILD_MANIFEST), true) +BUILD_ARGS := $(BUILD_ARGS) --platform=linux/arm64,linux/amd64 --jobs=2 --manifest +PUSH_ARGS := manifest +else +BUILD_ARGS := $(BUILD_ARGS) --tag +endif docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast - docker build --no-cache=true --platform=linux/arm64,linux/amd64 --jobs=2 --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ + docker build --no-cache=true --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ --build-arg CI_COMMIT_SHA=$(CI_COMMIT_SHA) --build-arg CI_COMMIT_BRANCH=$(CI_COMMIT_BRANCH) \ - $(BUILD_ARGS) --manifest $(IMG) . + $(BUILD_ARGS) $(IMG) . docker-push: ## Push docker image with the manager. - docker push $(IMG) - + docker $(PUSH_ARGS) push $(IMG) + +# Push to minikube's local registry enabled by registry add-on +minikube-push: + docker tag $(IMG) $$(minikube ip):5000/$(IMG) + docker push --tls-verify=false $$(minikube ip):5000/$(IMG) + ##@ Deployment install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. @@ -100,6 +110,9 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) $(KUSTOMIZE) build config/default | kubectl apply -f - +minikube-deploy: minikube-operator-yaml minikube-push + kubectl apply -f $(OPERATOR_YAML) + # Bug:34265574 # Used sed to reposition the controller-manager Deployment after the certificate creation in the OPERATOR_YAML operator-yaml: manifests kustomize @@ -109,6 +122,11 @@ operator-yaml: manifests kustomize (echo --- && sed '/^apiVersion: apps\/v1/,/---/!d' "$(OPERATOR_YAML).bak") >> "$(OPERATOR_YAML)" rm "$(OPERATOR_YAML).bak" +minikube-operator-yaml: IMG:=localhost:5000/$(IMG) +minikube-operator-yaml: operator-yaml + sed -i.bak 's/\(replicas.\) 3/\1 1/g' "$(OPERATOR_YAML)" + rm "$(OPERATOR_YAML).bak" + undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/default | kubectl delete -f - diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 74303bea..84042a15 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: basedb-operator-sa + newName: container-registry.oracle.com/database/operator:latest + newTag: latest diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index d300555d..09d4c5c3 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -5312,7 +5312,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa + image: container-registry.oracle.com/database/operator:latest imagePullPolicy: Always name: manager ports: From 664b8d5a6b33c6709e5b3350512f2ed02cccccb7 Mon Sep 17 00:00:00 2001 From: Matteo Malvezzi Date: Wed, 11 Dec 2024 15:20:41 +0000 Subject: [PATCH 136/414] V4 pdb cdb controllers --- Makefile.orig | 202 +++++++ .../v1alpha1/zz_generated.deepcopy.go | 534 ------------------ apis/database/{v1alpha1 => v4}/cdb_types.go | 10 +- apis/database/{v1alpha1 => v4}/cdb_webhook.go | 6 +- apis/database/{v1alpha1 => v4}/pdb_types.go | 5 +- apis/database/{v1alpha1 => v4}/pdb_webhook.go | 36 +- apis/database/v4/zz_generated.deepcopy.go | 534 ++++++++++++++++++ .../crd/bases/database.oracle.com_cdbs.yaml | 12 +- .../crd/bases/database.oracle.com_pdbs.yaml | 10 +- config/webhook/manifests.yaml | 132 ++--- controllers/database/cdb_controller.go | 149 ++++- controllers/database/pdb_controller.go | 281 +++++++-- main.go | 8 +- oracle-database-operator.yaml | 154 ++--- 14 files changed, 1330 insertions(+), 743 deletions(-) create mode 100644 Makefile.orig rename apis/database/{v1alpha1 => v4}/cdb_types.go (96%) rename apis/database/{v1alpha1 => v4}/cdb_webhook.go (93%) rename apis/database/{v1alpha1 => v4}/pdb_types.go (99%) rename apis/database/{v1alpha1 => v4}/pdb_webhook.go (86%) diff --git a/Makefile.orig b/Makefile.orig new file mode 100644 index 00000000..f52c9ad6 --- /dev/null +++ b/Makefile.orig @@ -0,0 +1,202 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# + +# Current Operator version +VERSION ?= 0.0.1 +# Default bundle image tag +BUNDLE_IMG ?= controller-bundle:$(VERSION) +# Options for 'bundle-build' +ifneq ($(origin CHANNELS), undefined) +BUNDLE_CHANNELS := --channels=$(CHANNELS) +endif +ifneq ($(origin DEFAULT_CHANNEL), undefined) +BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) +endif +BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) + +# Image URL to use all building/pushing image targets +IMG ?= controller:latest +# Produce CRDs that work back to Kubernetes 1.11 (no version conversion) +# API version has to be v1 to use defaulting (https://github.com/kubernetes-sigs/controller-tools/issues/478) +CRD_OPTIONS ?= "crd:maxDescLen=0" +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.29.0 +# Operator YAML file +OPERATOR_YAML=$$(basename $$(pwd)).yaml +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# This is a requirement for 'setup-envtest.sh' in the test target. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +all: build +##@ Development + +manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +fmt: ## Run go fmt against code. + go fmt ./... + +vet: ## Run go vet against code. + go vet ./... + +TEST ?= ./apis/database/v1alpha1 ./commons/... ./controllers/... +test: manifests generate fmt vet envtest ## Run unit tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $(TEST) -coverprofile cover.out + +E2ETEST ?= ./test/e2e/ +e2e: manifests generate fmt vet envtest ## Run e2e tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $(E2ETEST) -test.timeout 0 -test.v --ginkgo.fail-fast + +##@ Build + +build: generate fmt vet ## Build manager binary. + go build -o bin/manager main.go + +run: manifests generate fmt vet ## Run a controller from your host. + go run ./main.go + +GOLANG_VERSION ?= 1.23.3 +## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. +## Otherwise, use golang image from docker hub as the builder. +ifeq ($(BUILD_INTERNAL), true) +BUILDER_IMG = oraclelinux:9 +BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) --build-arg INSTALL_GO=true +else +BUILDER_IMG = golang:$(GOLANG_VERSION) +#BUILDER_IMG = phx.ocir.io/intsanjaysingh/db-repo/oracle/golang:1.23.3 +BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO="false" --build-arg GOLANG_VERSION=$(GOLANG_VERSION) +endif +docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast + docker build --no-cache=true --platform=linux/arm64,linux/amd64 --jobs=2 --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ + --build-arg CI_COMMIT_SHA=$(CI_COMMIT_SHA) --build-arg CI_COMMIT_BRANCH=$(CI_COMMIT_BRANCH) \ + $(BUILD_ARGS) --manifest $(IMG) . + +docker-push: ## Push docker image with the manager. + docker push $(IMG) + +##@ Deployment + +install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | kubectl apply -f - + +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | kubectl delete -f - + +deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) + $(KUSTOMIZE) build config/default | kubectl apply -f - + +# Bug:34265574 +# Used sed to reposition the controller-manager Deployment after the certificate creation in the OPERATOR_YAML +operator-yaml: manifests kustomize + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) + $(KUSTOMIZE) build config/default > "$(OPERATOR_YAML)" + sed -i.bak -e '/^apiVersion: apps\/v1/,/---/d' "$(OPERATOR_YAML)" + (echo --- && sed '/^apiVersion: apps\/v1/,/---/!d' "$(OPERATOR_YAML).bak") >> "$(OPERATOR_YAML)" + rm "$(OPERATOR_YAML).bak" + +undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/default | kubectl delete -f - + +##@ Build Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Tool Binaries +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest + +## Tool Versions +KUSTOMIZE_VERSION ?= v4.5.7 +CONTROLLER_TOOLS_VERSION ?= v0.16.5 + +KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" +.PHONY: kustomize +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. +$(KUSTOMIZE): $(LOCALBIN) + curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN) + +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. +$(CONTROLLER_GEN): $(LOCALBIN) + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + +.PHONY: envtest +envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. +$(ENVTEST): $(LOCALBIN) + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + + +.PHONY: bundle +bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files. + operator-sdk generate kustomize manifests -q + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) + $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) + operator-sdk bundle validate ./bundle + +.PHONY: bundle-build +bundle-build: ## Build the bundle image. + docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . + +.PHONY: bundle-push +bundle-push: ## Push the bundle image. + $(MAKE) docker-push IMG=$(BUNDLE_IMG) + +.PHONY: opm +OPM = ./bin/opm +opm: ## Download opm locally if necessary. +ifeq (,$(wildcard $(OPM))) +ifeq (,$(shell which opm 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(OPM)) ;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.1/$${OS}-$${ARCH}-opm ;\ + chmod +x $(OPM) ;\ + } +else +OPM = $(shell which opm) +endif +endif + +# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). +# These images MUST exist in a registry and be pull-able. +BUNDLE_IMGS ?= $(BUNDLE_IMG) + +# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). +CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) + +# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. +ifneq ($(origin CATALOG_BASE_IMG), undefined) +FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) +endif + +# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. +# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: +# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator +.PHONY: catalog-build +catalog-build: opm ## Build a catalog image. + $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) + +# Push the catalog image. +.PHONY: catalog-push +catalog-push: ## Push a catalog image. + $(MAKE) docker-push IMG=$(CATALOG_IMG) diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 7d5313c5..36c26a7c 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -606,205 +606,6 @@ func (in *Backupconfig) DeepCopy() *Backupconfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDB) DeepCopyInto(out *CDB) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDB. -func (in *CDB) DeepCopy() *CDB { - if in == nil { - return nil - } - out := new(CDB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CDB) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBAdminPassword) DeepCopyInto(out *CDBAdminPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminPassword. -func (in *CDBAdminPassword) DeepCopy() *CDBAdminPassword { - if in == nil { - return nil - } - out := new(CDBAdminPassword) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBAdminUser) DeepCopyInto(out *CDBAdminUser) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminUser. -func (in *CDBAdminUser) DeepCopy() *CDBAdminUser { - if in == nil { - return nil - } - out := new(CDBAdminUser) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBList) DeepCopyInto(out *CDBList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]CDB, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBList. -func (in *CDBList) DeepCopy() *CDBList { - if in == nil { - return nil - } - out := new(CDBList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CDBList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBSecret) DeepCopyInto(out *CDBSecret) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSecret. -func (in *CDBSecret) DeepCopy() *CDBSecret { - if in == nil { - return nil - } - out := new(CDBSecret) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBSpec) DeepCopyInto(out *CDBSpec) { - *out = *in - out.SysAdminPwd = in.SysAdminPwd - out.CDBAdminUser = in.CDBAdminUser - out.CDBAdminPwd = in.CDBAdminPwd - out.CDBTlsKey = in.CDBTlsKey - out.CDBTlsCrt = in.CDBTlsCrt - out.ORDSPwd = in.ORDSPwd - out.WebServerUser = in.WebServerUser - out.WebServerPwd = in.WebServerPwd - if in.NodeSelector != nil { - in, out := &in.NodeSelector, &out.NodeSelector - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSpec. -func (in *CDBSpec) DeepCopy() *CDBSpec { - if in == nil { - return nil - } - out := new(CDBSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBStatus) DeepCopyInto(out *CDBStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBStatus. -func (in *CDBStatus) DeepCopy() *CDBStatus { - if in == nil { - return nil - } - out := new(CDBStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBSysAdminPassword) DeepCopyInto(out *CDBSysAdminPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSysAdminPassword. -func (in *CDBSysAdminPassword) DeepCopy() *CDBSysAdminPassword { - if in == nil { - return nil - } - out := new(CDBSysAdminPassword) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBTLSCRT) DeepCopyInto(out *CDBTLSCRT) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSCRT. -func (in *CDBTLSCRT) DeepCopy() *CDBTLSCRT { - if in == nil { - return nil - } - out := new(CDBTLSCRT) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBTLSKEY) DeepCopyInto(out *CDBTLSKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSKEY. -func (in *CDBTLSKEY) DeepCopy() *CDBTLSKEY { - if in == nil { - return nil - } - out := new(CDBTLSKEY) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CatalogSpec) DeepCopyInto(out *CatalogSpec) { *out = *in @@ -1708,22 +1509,6 @@ func (in *OCISecretSpec) DeepCopy() *OCISecretSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ORDSPassword. -func (in *ORDSPassword) DeepCopy() *ORDSPassword { - if in == nil { - return nil - } - out := new(ORDSPassword) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OracleRestDataService) DeepCopyInto(out *OracleRestDataService) { *out = *in @@ -1902,65 +1687,6 @@ func (in *OracleRestDataServiceStatus) DeepCopy() *OracleRestDataServiceStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDB) DeepCopyInto(out *PDB) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDB. -func (in *PDB) DeepCopy() *PDB { - if in == nil { - return nil - } - out := new(PDB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PDB) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBAdminName) DeepCopyInto(out *PDBAdminName) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminName. -func (in *PDBAdminName) DeepCopy() *PDBAdminName { - if in == nil { - return nil - } - out := new(PDBAdminName) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBAdminPassword) DeepCopyInto(out *PDBAdminPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminPassword. -func (in *PDBAdminPassword) DeepCopy() *PDBAdminPassword { - if in == nil { - return nil - } - out := new(PDBAdminPassword) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { *out = *in @@ -2072,170 +1798,6 @@ func (in *PDBDetailsStatus) DeepCopy() *PDBDetailsStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBList) DeepCopyInto(out *PDBList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PDB, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBList. -func (in *PDBList) DeepCopy() *PDBList { - if in == nil { - return nil - } - out := new(PDBList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PDBList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBSecret) DeepCopyInto(out *PDBSecret) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSecret. -func (in *PDBSecret) DeepCopy() *PDBSecret { - if in == nil { - return nil - } - out := new(PDBSecret) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBSpec) DeepCopyInto(out *PDBSpec) { - *out = *in - out.PDBTlsKey = in.PDBTlsKey - out.PDBTlsCrt = in.PDBTlsCrt - out.PDBTlsCat = in.PDBTlsCat - out.AdminName = in.AdminName - out.AdminPwd = in.AdminPwd - out.WebServerUsr = in.WebServerUsr - out.WebServerPwd = in.WebServerPwd - if in.ReuseTempFile != nil { - in, out := &in.ReuseTempFile, &out.ReuseTempFile - *out = new(bool) - **out = **in - } - if in.UnlimitedStorage != nil { - in, out := &in.UnlimitedStorage, &out.UnlimitedStorage - *out = new(bool) - **out = **in - } - if in.AsClone != nil { - in, out := &in.AsClone, &out.AsClone - *out = new(bool) - **out = **in - } - if in.TDEImport != nil { - in, out := &in.TDEImport, &out.TDEImport - *out = new(bool) - **out = **in - } - if in.TDEExport != nil { - in, out := &in.TDEExport, &out.TDEExport - *out = new(bool) - **out = **in - } - out.TDEPassword = in.TDEPassword - out.TDESecret = in.TDESecret - if in.GetScript != nil { - in, out := &in.GetScript, &out.GetScript - *out = new(bool) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSpec. -func (in *PDBSpec) DeepCopy() *PDBSpec { - if in == nil { - return nil - } - out := new(PDBSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBStatus) DeepCopyInto(out *PDBStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBStatus. -func (in *PDBStatus) DeepCopy() *PDBStatus { - if in == nil { - return nil - } - out := new(PDBStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBTLSCAT) DeepCopyInto(out *PDBTLSCAT) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCAT. -func (in *PDBTLSCAT) DeepCopy() *PDBTLSCAT { - if in == nil { - return nil - } - out := new(PDBTLSCAT) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBTLSCRT) DeepCopyInto(out *PDBTLSCRT) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCRT. -func (in *PDBTLSCRT) DeepCopy() *PDBTLSCRT { - if in == nil { - return nil - } - out := new(PDBTLSCRT) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBTLSKEY) DeepCopyInto(out *PDBTLSKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSKEY. -func (in *PDBTLSKEY) DeepCopy() *PDBTLSKEY { - if in == nil { - return nil - } - out := new(PDBTLSKEY) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PITSpec) DeepCopyInto(out *PITSpec) { *out = *in @@ -2826,38 +2388,6 @@ func (in *SourceSpec) DeepCopy() *SourceSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TDEPwd) DeepCopyInto(out *TDEPwd) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDEPwd. -func (in *TDEPwd) DeepCopy() *TDEPwd { - if in == nil { - return nil - } - out := new(TDEPwd) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TDESecret) DeepCopyInto(out *TDESecret) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDESecret. -func (in *TDESecret) DeepCopy() *TDESecret { - if in == nil { - return nil - } - out := new(TDESecret) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in @@ -2930,67 +2460,3 @@ func (in *WalletSpec) DeepCopy() *WalletSpec { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerPassword) DeepCopyInto(out *WebServerPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPassword. -func (in *WebServerPassword) DeepCopy() *WebServerPassword { - if in == nil { - return nil - } - out := new(WebServerPassword) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerPasswordPDB) DeepCopyInto(out *WebServerPasswordPDB) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPasswordPDB. -func (in *WebServerPasswordPDB) DeepCopy() *WebServerPasswordPDB { - if in == nil { - return nil - } - out := new(WebServerPasswordPDB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerUser) DeepCopyInto(out *WebServerUser) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUser. -func (in *WebServerUser) DeepCopy() *WebServerUser { - if in == nil { - return nil - } - out := new(WebServerUser) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerUserPDB) DeepCopyInto(out *WebServerUserPDB) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUserPDB. -func (in *WebServerUserPDB) DeepCopy() *WebServerUserPDB { - if in == nil { - return nil - } - out := new(WebServerUserPDB) - in.DeepCopyInto(out) - return out -} diff --git a/apis/database/v1alpha1/cdb_types.go b/apis/database/v4/cdb_types.go similarity index 96% rename from apis/database/v1alpha1/cdb_types.go rename to apis/database/v4/cdb_types.go index 206781b2..7bb4cbd3 100644 --- a/apis/database/v1alpha1/cdb_types.go +++ b/apis/database/v4/cdb_types.go @@ -36,7 +36,7 @@ ** SOFTWARE. */ -package v1alpha1 +package v4 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -84,8 +84,9 @@ type CDBSpec struct { // DB server port DBPort int `json:"dbPort,omitempty"` // Node Selector for running the Pod - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - DBTnsurl string `json:"dbTnsurl,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + DeletePDBCascade bool `json:"deletePdbCascade,omitempty"` + DBTnsurl string `json:"dbTnsurl,omitempty"` } // CDBSecret defines the secretName @@ -150,11 +151,12 @@ type CDBStatus struct { // +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" // +kubebuilder:printcolumn:JSONPath=".spec.dbServer",name="DB Server",type="string",description=" Name of the DB Server" // +kubebuilder:printcolumn:JSONPath=".spec.dbPort",name="DB Port",type="integer",description="DB server port" -// +kubebuilder:printcolumn:JSONPath=".spec.dbTnsurl",name="TNS STRING",type="string",description=" string of the tnsalias" // +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="Replicas",type="integer",description="Replicas" // +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the CDB Resource" // +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" +// +kubebuilder:printcolumn:JSONPath=".spec.dbTnsurl",name="TNS STRING",type="string",description=" string of the tnsalias" // +kubebuilder:resource:path=cdbs,scope=Namespaced +// +kubebuilder:storageversion // CDB is the Schema for the cdbs API type CDB struct { diff --git a/apis/database/v1alpha1/cdb_webhook.go b/apis/database/v4/cdb_webhook.go similarity index 93% rename from apis/database/v1alpha1/cdb_webhook.go rename to apis/database/v4/cdb_webhook.go index 345b6f75..98dcf823 100644 --- a/apis/database/v1alpha1/cdb_webhook.go +++ b/apis/database/v4/cdb_webhook.go @@ -36,7 +36,7 @@ ** SOFTWARE. */ -package v1alpha1 +package v4 import ( "reflect" @@ -61,7 +61,7 @@ func (r *CDB) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-cdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v1alpha1,name=mcdb.kb.io,admissionReviewVersions={v1,v1beta1} +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-cdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v4,name=mcdb.kb.io,admissionReviewVersions={v1,v1beta1} var _ webhook.Defaulter = &CDB{} @@ -79,7 +79,7 @@ func (r *CDB) Default() { } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:path=/validate-database-oracle-com-v1alpha1-cdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v1alpha1,name=vcdb.kb.io,admissionReviewVersions={v1,v1beta1} +//+kubebuilder:webhook:path=/validate-database-oracle-com-v4-cdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v4,name=vcdb.kb.io,admissionReviewVersions={v1,v1beta1} var _ webhook.Validator = &CDB{} diff --git a/apis/database/v1alpha1/pdb_types.go b/apis/database/v4/pdb_types.go similarity index 99% rename from apis/database/v1alpha1/pdb_types.go rename to apis/database/v4/pdb_types.go index 8de9db52..e621fb53 100644 --- a/apis/database/v1alpha1/pdb_types.go +++ b/apis/database/v4/pdb_types.go @@ -36,7 +36,7 @@ ** SOFTWARE. */ -package v1alpha1 +package v4 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -194,14 +194,15 @@ type PDBStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status -// +kubebuilder:printcolumn:JSONPath=".status.connString",name="Connect_String",type="string",description="The connect string to be used" // +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" // +kubebuilder:printcolumn:JSONPath=".spec.pdbName",name="PDB Name",type="string",description="Name of the PDB" // +kubebuilder:printcolumn:JSONPath=".status.openMode",name="PDB State",type="string",description="PDB Open Mode" // +kubebuilder:printcolumn:JSONPath=".status.totalSize",name="PDB Size",type="string",description="Total Size of the PDB" // +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the PDB Resource" // +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" +// +kubebuilder:printcolumn:JSONPath=".status.connString",name="Connect_String",type="string",description="The connect string to be used" // +kubebuilder:resource:path=pdbs,scope=Namespaced +// +kubebuilder:storageversion // PDB is the Schema for the pdbs API type PDB struct { diff --git a/apis/database/v1alpha1/pdb_webhook.go b/apis/database/v4/pdb_webhook.go similarity index 86% rename from apis/database/v1alpha1/pdb_webhook.go rename to apis/database/v4/pdb_webhook.go index 1577198e..896e6dc7 100644 --- a/apis/database/v1alpha1/pdb_webhook.go +++ b/apis/database/v4/pdb_webhook.go @@ -40,7 +40,7 @@ ** rcitton 07/14/22 - 33822886 */ -package v1alpha1 +package v4 import ( "reflect" @@ -66,7 +66,7 @@ func (r *PDB) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-pdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v1alpha1,name=mpdb.kb.io,admissionReviewVersions={v1,v1beta1} +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-pdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v4,name=mpdb.kb.io,admissionReviewVersions={v1,v1beta1} var _ webhook.Defaulter = &PDB{} @@ -107,6 +107,7 @@ func (r *PDB) Default() { *r.Spec.AsClone = false pdblog.Info(" - asClone : " + strconv.FormatBool(*(r.Spec.AsClone))) } + } if r.Spec.GetScript == nil { @@ -117,7 +118,7 @@ func (r *PDB) Default() { } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:path=/validate-database-oracle-com-v1alpha1-pdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v1alpha1,name=vpdb.kb.io,admissionReviewVersions={v1,v1beta1} +//+kubebuilder:webhook:path=/validate-database-oracle-com-v4-pdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v4,name=vpdb.kb.io,admissionReviewVersions={v1,v1beta1} var _ webhook.Validator = &PDB{} @@ -164,6 +165,13 @@ func (r *PDB) validateAction(allErrs *field.ErrorList) { } switch action { + case "DELETE": + /* BUG 36752336 - LREST OPERATOR - DELETE NON-EXISTENT PDB SHOWS LRPDB CREATED MESSAGE */ + if r.Status.OpenMode == "READ WRITE" { + pdblog.Info("Cannot delete: pdb is open ") + *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+r.Spec.PDBName+" "+r.Status.OpenMode)) + } + r.CheckObjExistence("DELETE", allErrs, r) case "CREATE": if reflect.ValueOf(r.Spec.AdminName).IsZero() { *allErrs = append(*allErrs, @@ -211,6 +219,13 @@ func (r *PDB) validateAction(allErrs *field.ErrorList) { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) } + /* We don't need this check as ords open the pdb before cloninig */ + /* + if r.Status.OpenMode == "MOUNTED" { + pdblog.Info("Cannot clone: pdb is mount ") + *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+r.Spec.PDBName+" "+r.Status.OpenMode)) + } + */ case "PLUG": if r.Spec.XMLFileName == "" { *allErrs = append(*allErrs, @@ -239,6 +254,11 @@ func (r *PDB) validateAction(allErrs *field.ErrorList) { if *(r.Spec.TDEExport) { r.validateTDEInfo(allErrs) } + if r.Status.OpenMode == "READ WRITE" { + pdblog.Info("Cannot unplug: pdb is open ") + *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+r.Spec.PDBName+" "+r.Status.OpenMode)) + } + r.CheckObjExistence("UNPLUG", allErrs, r) case "MODIFY": if r.Spec.PDBState == "" { *allErrs = append(*allErrs, @@ -248,6 +268,7 @@ func (r *PDB) validateAction(allErrs *field.ErrorList) { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("modifyOption"), "Please specify an option for opening/closing a PDB")) } + r.CheckObjExistence("MODIY", allErrs, r) } } @@ -333,3 +354,12 @@ func (r *PDB) validateTDEInfo(allErrs *field.ErrorList) { } } + +func (r *PDB) CheckObjExistence(action string, allErrs *field.ErrorList, pdb *PDB) { + /* BUG 36752465 - lrest operator - open non-existent pdb creates a lrpdb with status failed */ + pdblog.Info("Action [" + action + "] checkin " + pdb.Spec.PDBName + " existence") + if pdb.Status.OpenMode == "" { + *allErrs = append(*allErrs, field.NotFound(field.NewPath("Spec").Child("PDBName"), " "+pdb.Spec.PDBName+" does not exist : action "+action+" failure")) + + } +} diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index c79f6818..557cefc7 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -606,6 +606,205 @@ func (in *Backupconfig) DeepCopy() *Backupconfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDB) DeepCopyInto(out *CDB) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDB. +func (in *CDB) DeepCopy() *CDB { + if in == nil { + return nil + } + out := new(CDB) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CDB) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBAdminPassword) DeepCopyInto(out *CDBAdminPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminPassword. +func (in *CDBAdminPassword) DeepCopy() *CDBAdminPassword { + if in == nil { + return nil + } + out := new(CDBAdminPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBAdminUser) DeepCopyInto(out *CDBAdminUser) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminUser. +func (in *CDBAdminUser) DeepCopy() *CDBAdminUser { + if in == nil { + return nil + } + out := new(CDBAdminUser) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBList) DeepCopyInto(out *CDBList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CDB, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBList. +func (in *CDBList) DeepCopy() *CDBList { + if in == nil { + return nil + } + out := new(CDBList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CDBList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBSecret) DeepCopyInto(out *CDBSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSecret. +func (in *CDBSecret) DeepCopy() *CDBSecret { + if in == nil { + return nil + } + out := new(CDBSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBSpec) DeepCopyInto(out *CDBSpec) { + *out = *in + out.SysAdminPwd = in.SysAdminPwd + out.CDBAdminUser = in.CDBAdminUser + out.CDBAdminPwd = in.CDBAdminPwd + out.CDBTlsKey = in.CDBTlsKey + out.CDBTlsCrt = in.CDBTlsCrt + out.ORDSPwd = in.ORDSPwd + out.WebServerUser = in.WebServerUser + out.WebServerPwd = in.WebServerPwd + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSpec. +func (in *CDBSpec) DeepCopy() *CDBSpec { + if in == nil { + return nil + } + out := new(CDBSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBStatus) DeepCopyInto(out *CDBStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBStatus. +func (in *CDBStatus) DeepCopy() *CDBStatus { + if in == nil { + return nil + } + out := new(CDBStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBSysAdminPassword) DeepCopyInto(out *CDBSysAdminPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSysAdminPassword. +func (in *CDBSysAdminPassword) DeepCopy() *CDBSysAdminPassword { + if in == nil { + return nil + } + out := new(CDBSysAdminPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBTLSCRT) DeepCopyInto(out *CDBTLSCRT) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSCRT. +func (in *CDBTLSCRT) DeepCopy() *CDBTLSCRT { + if in == nil { + return nil + } + out := new(CDBTLSCRT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBTLSKEY) DeepCopyInto(out *CDBTLSKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSKEY. +func (in *CDBTLSKEY) DeepCopy() *CDBTLSKEY { + if in == nil { + return nil + } + out := new(CDBTLSKEY) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CatalogSpec) DeepCopyInto(out *CatalogSpec) { *out = *in @@ -1394,6 +1593,81 @@ func (in *OCISecretSpec) DeepCopy() *OCISecretSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ORDSPassword. +func (in *ORDSPassword) DeepCopy() *ORDSPassword { + if in == nil { + return nil + } + out := new(ORDSPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDB) DeepCopyInto(out *PDB) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDB. +func (in *PDB) DeepCopy() *PDB { + if in == nil { + return nil + } + out := new(PDB) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PDB) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBAdminName) DeepCopyInto(out *PDBAdminName) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminName. +func (in *PDBAdminName) DeepCopy() *PDBAdminName { + if in == nil { + return nil + } + out := new(PDBAdminName) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBAdminPassword) DeepCopyInto(out *PDBAdminPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminPassword. +func (in *PDBAdminPassword) DeepCopy() *PDBAdminPassword { + if in == nil { + return nil + } + out := new(PDBAdminPassword) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { *out = *in @@ -1505,6 +1779,170 @@ func (in *PDBDetailsStatus) DeepCopy() *PDBDetailsStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBList) DeepCopyInto(out *PDBList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PDB, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBList. +func (in *PDBList) DeepCopy() *PDBList { + if in == nil { + return nil + } + out := new(PDBList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PDBList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBSecret) DeepCopyInto(out *PDBSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSecret. +func (in *PDBSecret) DeepCopy() *PDBSecret { + if in == nil { + return nil + } + out := new(PDBSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBSpec) DeepCopyInto(out *PDBSpec) { + *out = *in + out.PDBTlsKey = in.PDBTlsKey + out.PDBTlsCrt = in.PDBTlsCrt + out.PDBTlsCat = in.PDBTlsCat + out.AdminName = in.AdminName + out.AdminPwd = in.AdminPwd + out.WebServerUsr = in.WebServerUsr + out.WebServerPwd = in.WebServerPwd + if in.ReuseTempFile != nil { + in, out := &in.ReuseTempFile, &out.ReuseTempFile + *out = new(bool) + **out = **in + } + if in.UnlimitedStorage != nil { + in, out := &in.UnlimitedStorage, &out.UnlimitedStorage + *out = new(bool) + **out = **in + } + if in.AsClone != nil { + in, out := &in.AsClone, &out.AsClone + *out = new(bool) + **out = **in + } + if in.TDEImport != nil { + in, out := &in.TDEImport, &out.TDEImport + *out = new(bool) + **out = **in + } + if in.TDEExport != nil { + in, out := &in.TDEExport, &out.TDEExport + *out = new(bool) + **out = **in + } + out.TDEPassword = in.TDEPassword + out.TDESecret = in.TDESecret + if in.GetScript != nil { + in, out := &in.GetScript, &out.GetScript + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSpec. +func (in *PDBSpec) DeepCopy() *PDBSpec { + if in == nil { + return nil + } + out := new(PDBSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBStatus) DeepCopyInto(out *PDBStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBStatus. +func (in *PDBStatus) DeepCopy() *PDBStatus { + if in == nil { + return nil + } + out := new(PDBStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBTLSCAT) DeepCopyInto(out *PDBTLSCAT) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCAT. +func (in *PDBTLSCAT) DeepCopy() *PDBTLSCAT { + if in == nil { + return nil + } + out := new(PDBTLSCAT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBTLSCRT) DeepCopyInto(out *PDBTLSCRT) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCRT. +func (in *PDBTLSCRT) DeepCopy() *PDBTLSCRT { + if in == nil { + return nil + } + out := new(PDBTLSCRT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBTLSKEY) DeepCopyInto(out *PDBTLSKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSKEY. +func (in *PDBTLSKEY) DeepCopy() *PDBTLSKEY { + if in == nil { + return nil + } + out := new(PDBTLSKEY) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PITSpec) DeepCopyInto(out *PITSpec) { *out = *in @@ -1832,6 +2270,38 @@ func (in *SourceSpec) DeepCopy() *SourceSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TDEPwd) DeepCopyInto(out *TDEPwd) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDEPwd. +func (in *TDEPwd) DeepCopy() *TDEPwd { + if in == nil { + return nil + } + out := new(TDEPwd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TDESecret) DeepCopyInto(out *TDESecret) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDESecret. +func (in *TDESecret) DeepCopy() *TDESecret { + if in == nil { + return nil + } + out := new(TDESecret) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in @@ -1904,3 +2374,67 @@ func (in *WalletSpec) DeepCopy() *WalletSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerPassword) DeepCopyInto(out *WebServerPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPassword. +func (in *WebServerPassword) DeepCopy() *WebServerPassword { + if in == nil { + return nil + } + out := new(WebServerPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerPasswordPDB) DeepCopyInto(out *WebServerPasswordPDB) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPasswordPDB. +func (in *WebServerPasswordPDB) DeepCopy() *WebServerPasswordPDB { + if in == nil { + return nil + } + out := new(WebServerPasswordPDB) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerUser) DeepCopyInto(out *WebServerUser) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUser. +func (in *WebServerUser) DeepCopy() *WebServerUser { + if in == nil { + return nil + } + out := new(WebServerUser) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerUserPDB) DeepCopyInto(out *WebServerUserPDB) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUserPDB. +func (in *WebServerUserPDB) DeepCopy() *WebServerUserPDB { + if in == nil { + return nil + } + out := new(WebServerUserPDB) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/database.oracle.com_cdbs.yaml b/config/crd/bases/database.oracle.com_cdbs.yaml index 81a33809..4308600b 100644 --- a/config/crd/bases/database.oracle.com_cdbs.yaml +++ b/config/crd/bases/database.oracle.com_cdbs.yaml @@ -27,10 +27,6 @@ spec: jsonPath: .spec.dbPort name: DB Port type: integer - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - description: Replicas jsonPath: .spec.replicas name: Replicas @@ -43,7 +39,11 @@ spec: jsonPath: .status.msg name: Message type: string - name: v1alpha1 + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 schema: openAPIV3Schema: properties: @@ -123,6 +123,8 @@ spec: type: string dbTnsurl: type: string + deletePdbCascade: + type: boolean nodeSelector: additionalProperties: type: string diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index f7a39cd8..c51ab4e7 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -15,10 +15,6 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - description: Name of the CDB jsonPath: .spec.cdbName name: CDB Name @@ -43,7 +39,11 @@ spec: jsonPath: .status.msg name: Message type: string - name: v1alpha1 + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 schema: openAPIV3Schema: properties: diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index dc3d56b1..3c6f3e22 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -46,13 +46,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v4-dbcssystem + path: /mutate-database-oracle-com-v4-cdb failurePolicy: Fail - name: mdbcssystemv4.kb.io + name: mcdb.kb.io rules: - apiGroups: - database.oracle.com @@ -62,7 +63,7 @@ webhooks: - CREATE - UPDATE resources: - - dbcssystems + - cdbs sideEffects: None - admissionReviewVersions: - v1 @@ -70,9 +71,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v4-shardingdatabase + path: /mutate-database-oracle-com-v4-dbcssystem failurePolicy: Fail - name: mshardingdatabasev4.kb.io + name: mdbcssystemv4.kb.io rules: - apiGroups: - database.oracle.com @@ -82,27 +83,28 @@ webhooks: - CREATE - UPDATE resources: - - shardingdatabases + - dbcssystems sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup + path: /mutate-database-oracle-com-v4-pdb failurePolicy: Fail - name: mautonomousdatabasebackupv1alpha1.kb.io + name: mpdb.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - autonomousdatabasebackups + - pdbs sideEffects: None - admissionReviewVersions: - v1 @@ -110,30 +112,29 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + path: /mutate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: mautonomousdatabasev1alpha1.kb.io + name: mshardingdatabasev4.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - autonomousdatabases + - shardingdatabases sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v1alpha1-cdb + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup failurePolicy: Fail - name: mcdb.kb.io + name: mautonomousdatabasebackupv1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -143,18 +144,17 @@ webhooks: - CREATE - UPDATE resources: - - cdbs + - autonomousdatabasebackups sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v1alpha1-dataguardbroker + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase failurePolicy: Fail - name: mdataguardbroker.kb.io + name: mautonomousdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -164,48 +164,48 @@ webhooks: - CREATE - UPDATE resources: - - dataguardbrokers + - autonomousdatabases sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v4-dbcssystem + path: /mutate-database-oracle-com-v1alpha1-dataguardbroker failurePolicy: Fail - name: mdbcssystemv1alpha1.kb.io + name: mdataguardbroker.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v4 + - v1alpha1 operations: - CREATE - UPDATE resources: - - dbcssystems + - dataguardbrokers sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice + path: /mutate-database-oracle-com-v4-dbcssystem failurePolicy: Fail - name: moraclerestdataservice.kb.io + name: mdbcssystemv1alpha1.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - oraclerestdataservices + - dbcssystems sideEffects: None - admissionReviewVersions: - v1 @@ -214,9 +214,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-database-oracle-com-v1alpha1-pdb + path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice failurePolicy: Fail - name: mpdb.kb.io + name: moraclerestdataservice.kb.io rules: - apiGroups: - database.oracle.com @@ -226,7 +226,7 @@ webhooks: - CREATE - UPDATE resources: - - pdbs + - oraclerestdataservices sideEffects: None - admissionReviewVersions: - v1 @@ -377,13 +377,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v4-shardingdatabase + path: /validate-database-oracle-com-v4-cdb failurePolicy: Fail - name: vshardingdatabasev4.kb.io + name: vcdb.kb.io rules: - apiGroups: - database.oracle.com @@ -392,29 +393,29 @@ webhooks: operations: - CREATE - UPDATE - - DELETE resources: - - shardingdatabases + - cdbs sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + path: /validate-database-oracle-com-v4-pdb failurePolicy: Fail - name: vautonomouscontainerdatabasev1alpha1.kb.io + name: vpdb.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - autonomouscontainerdatabases + - pdbs sideEffects: None - admissionReviewVersions: - v1 @@ -422,19 +423,20 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup + path: /validate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: vautonomousdatabasebackupv1alpha1.kb.io + name: vshardingdatabasev4.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE + - DELETE resources: - - autonomousdatabasebackups + - shardingdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -442,9 +444,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase failurePolicy: Fail - name: vautonomousdatabaserestorev1alpha1.kb.io + name: vautonomouscontainerdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -454,7 +456,7 @@ webhooks: - CREATE - UPDATE resources: - - autonomousdatabaserestores + - autonomouscontainerdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -462,9 +464,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup failurePolicy: Fail - name: vautonomousdatabasev1alpha1.kb.io + name: vautonomousdatabasebackupv1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -474,18 +476,17 @@ webhooks: - CREATE - UPDATE resources: - - autonomousdatabases + - autonomousdatabasebackups sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-cdb + path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore failurePolicy: Fail - name: vcdb.kb.io + name: vautonomousdatabaserestorev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -495,18 +496,17 @@ webhooks: - CREATE - UPDATE resources: - - cdbs + - autonomousdatabaserestores sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-dataguardbroker + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase failurePolicy: Fail - name: vdataguardbroker.kb.io + name: vautonomousdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -516,7 +516,7 @@ webhooks: - CREATE - UPDATE resources: - - dataguardbrokers + - autonomousdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -525,9 +525,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + path: /validate-database-oracle-com-v1alpha1-dataguardbroker failurePolicy: Fail - name: voraclerestdataservice.kb.io + name: vdataguardbroker.kb.io rules: - apiGroups: - database.oracle.com @@ -537,7 +537,7 @@ webhooks: - CREATE - UPDATE resources: - - oraclerestdataservices + - dataguardbrokers sideEffects: None - admissionReviewVersions: - v1 @@ -546,9 +546,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-database-oracle-com-v1alpha1-pdb + path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice failurePolicy: Fail - name: vpdb.kb.io + name: voraclerestdataservice.kb.io rules: - apiGroups: - database.oracle.com @@ -558,7 +558,7 @@ webhooks: - CREATE - UPDATE resources: - - pdbs + - oraclerestdataservices sideEffects: None - admissionReviewVersions: - v1 diff --git a/controllers/database/cdb_controller.go b/controllers/database/cdb_controller.go index 5e6c0aca..e5f056c2 100644 --- a/controllers/database/cdb_controller.go +++ b/controllers/database/cdb_controller.go @@ -40,7 +40,9 @@ package controllers import ( "context" + "encoding/json" "errors" + "fmt" //"fmt" "strconv" @@ -64,7 +66,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" - dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" dbcommons "github.com/oracle/oracle-database-operator/commons/database" ) @@ -751,11 +753,11 @@ func (r *CDBReconciler) createSvcSpec(cdb *dbapi.CDB) *corev1.Service { - Check CDB deletion /*********************************************** */ + +/* func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("manageCDBDeletion", req.NamespacedName) - // Check if the PDB instance is marked to be deleted, which is - // indicated by the deletion timestamp being set. isCDBMarkedToBeDeleted := cdb.GetDeletionTimestamp() != nil if isCDBMarkedToBeDeleted { log.Info("Marked to be deleted") @@ -763,17 +765,12 @@ func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, cdb.Status.Status = true r.Status().Update(ctx, cdb) if controllerutil.ContainsFinalizer(cdb, CDBFinalizer) { - // Run finalization logic for CDBFinalizer. If the - // finalization logic fails, don't remove the finalizer so - // that we can retry during the next reconciliation. err := r.deleteCDBInstance(ctx, req, cdb) if err != nil { log.Info("Could not delete CDB Resource", "CDB Name", cdb.Spec.CDBName, "err", err.Error()) return err } - // Remove CDBFinalizer. Once all finalizers have been - // removed, the object will be deleted. log.Info("Removing finalizer") controllerutil.RemoveFinalizer(cdb, CDBFinalizer) err = r.Update(ctx, cdb) @@ -787,7 +784,6 @@ func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, } } - // Add finalizer for this CR if !controllerutil.ContainsFinalizer(cdb, CDBFinalizer) { log.Info("Adding finalizer") @@ -802,6 +798,48 @@ func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, } return nil } +*/ + +func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { + log := r.Log.WithValues("manageCDBDeletion", req.NamespacedName) + + /* REGISTER FINALIZER */ + if cdb.ObjectMeta.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(cdb, CDBFinalizer) { + controllerutil.AddFinalizer(cdb, CDBFinalizer) + if err := r.Update(ctx, cdb); err != nil { + return err + } + } + + } else { + log.Info("cdb mark to be delited") + cdb.Status.Phase = cdbPhaseDelete + cdb.Status.Status = true + r.Status().Update(ctx, cdb) + + if controllerutil.ContainsFinalizer(cdb, CDBFinalizer) { + + if err := r.DeletePDBS(ctx, req, cdb); err != nil { + log.Info("Cannot delete pdbs") + return err + } + + controllerutil.RemoveFinalizer(cdb, CDBFinalizer) + if err := r.Update(ctx, cdb); err != nil { + return err + } + } + + err := r.deleteCDBInstance(ctx, req, cdb) + if err != nil { + log.Info("Could not delete CDB Resource", "CDB Name", cdb.Spec.CDBName, "err", err.Error()) + return err + } + + } + return nil +} /* ************************************************ @@ -966,11 +1004,104 @@ func (r *CDBReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, cdb } } +/* Delete cascade option */ + /* ************************************************************* - SetupWithManager sets up the controller with the Manager. /************************************************************ */ + +func (r *CDBReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { + log := r.Log.WithValues("DeletePDBS", req.NamespacedName) + + /* =================== DELETE CASCADE ================ */ + if cdb.Spec.DeletePDBCascade == true { + log.Info("DELETE PDB CASCADE OPTION") + pdbList := &dbapi.PDBList{} + listOpts := []client.ListOption{} + err := r.List(ctx, pdbList, listOpts...) + if err != nil { + log.Info("Failed to get the list of pdbs") + } + + var url string + if err == nil { + for _, pdbitem := range pdbList.Items { + log.Info("pdbitem.Spec.CDBName : " + pdbitem.Spec.CDBName) + log.Info("pdbitem.Spec.CDBNamespace: " + pdbitem.Spec.CDBNamespace) + log.Info("cdb.Spec.CDBName : " + cdb.Spec.CDBName) + log.Info("cdb.Namespace : " + cdb.Namespace) + if pdbitem.Spec.CDBName == cdb.Spec.CDBName && pdbitem.Spec.CDBNamespace == cdb.Namespace { + fmt.Printf("DeletePDBS Call Delete function for %s %s\n", pdbitem.Name, pdbitem.Spec.PDBName) + + var objmap map[string]interface{} /* Used for the return payload */ + values := map[string]string{ + "state": "CLOSE", + "modifyOption": "IMMEDIATE", + "getScript": "FALSE", + } + + //url := "https://" + pdbitem.Spec.CDBResName + "-cdb." + pdbitem.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/database/pdbs/" + pdbitem.Spec.PDBName + url = "https://" + pdbitem.Spec.CDBResName + "-ords." + pdbitem.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbitem.Spec.PDBName + "/status" + + log.Info("callAPI(URL):" + url) + log.Info("pdbitem.Status.OpenMode" + pdbitem.Status.OpenMode) + + if pdbitem.Status.OpenMode != "MOUNTED" { + + log.Info("Force pdb closure") + respData, errapi := NewCallApi(r, ctx, req, &pdbitem, url, values, "POST") + + fmt.Printf("Debug NEWCALL:%s\n", respData) + if err := json.Unmarshal([]byte(respData), &objmap); err != nil { + log.Error(err, "failed to get respData from callAPI", "err", err.Error()) + return err + } + + if errapi != nil { + log.Error(err, "callAPI cannot close pdb "+pdbitem.Spec.PDBName, "err", err.Error()) + return err + } + + r.Recorder.Eventf(cdb, corev1.EventTypeNormal, "close pdb", "pdbname=%s", pdbitem.Spec.PDBName) + } + + /* start dropping pdb */ + log.Info("Drop pluggable database") + values = map[string]string{ + "action": "INCLUDING", + "getScript": "FALSE", + } + url = "https://" + pdbitem.Spec.CDBResName + "-ords." + pdbitem.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbitem.Spec.PDBName + "/" + respData, errapi := NewCallApi(r, ctx, req, &pdbitem, url, values, "DELETE") + + if err := json.Unmarshal([]byte(respData), &objmap); err != nil { + log.Error(err, "failed to get respData from callAPI", "err", err.Error()) + return err + } + + if errapi != nil { + log.Error(err, "callAPI cannot drop pdb "+pdbitem.Spec.PDBName, "err", err.Error()) + return err + } + r.Recorder.Eventf(cdb, corev1.EventTypeNormal, "drop pdb", "pdbname=%s", pdbitem.Spec.PDBName) + + err = r.Delete(context.Background(), &pdbitem, client.GracePeriodSeconds(1)) + if err != nil { + log.Info("Could not delete PDB resource", "err", err.Error()) + return err + } + + } /* check pdb name */ + } /* end of loop */ + } + + } + /* ================================================ */ + return nil +} + func (r *CDBReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&dbapi.CDB{}). diff --git a/controllers/database/pdb_controller.go b/controllers/database/pdb_controller.go index f0b4fd46..1fd11c10 100644 --- a/controllers/database/pdb_controller.go +++ b/controllers/database/pdb_controller.go @@ -55,7 +55,7 @@ import ( "strings" "time" - dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" @@ -82,6 +82,11 @@ type PDBReconciler struct { Recorder record.EventRecorder } +type controllers struct { + Pdbc PDBReconciler + Cdbc CDBReconciler +} + type RESTSQLCollection struct { Env struct { DefaultTimeZone string `json:"defaultTimeZone,omitempty"` @@ -426,7 +431,8 @@ func (r *PDBReconciler) getSecret(ctx context.Context, req ctrl.Request, pdb *db /* ************************************************ - Issue a REST API Call to the ORDS container - /*********************************************** + +*********************************************** */ func (r *PDBReconciler) callAPI(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB, url string, payload map[string]string, action string) (string, error) { log := r.Log.WithValues("callAPI", req.NamespacedName) @@ -472,11 +478,6 @@ func (r *PDBReconciler) callAPI(ctx context.Context, req ctrl.Request, pdb *dbap } caCert := secret.Data[pdb.Spec.PDBTlsCat.Secret.Key] - /* - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSINFO", string(rsaKeyPEM)) - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSINFO", string(rsaCertPEM)) - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSINFO", string(caCert)) - */ certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) if err != nil { @@ -620,7 +621,8 @@ func (r *PDBReconciler) callAPI(ctx context.Context, req ctrl.Request, pdb *dbap /* ************************************************ - Create a PDB - /*********************************************** + +*********************************************** */ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { @@ -696,7 +698,7 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db if err := r.Status().Update(ctx, pdb); err != nil { log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) } - _, err = r.callAPI(ctx, req, pdb, url, values, "POST") + _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") if err != nil { log.Error(err, "callAPI error", "err", err.Error()) return err @@ -724,7 +726,8 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db /* ************************************************ - Clone a PDB - /*********************************************** + +*********************************************** */ func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { @@ -773,8 +776,6 @@ func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dba values["tempSize"] = pdb.Spec.TempSize } - //url := "https://"+ pdb.Spec.CDBNamespace + "." + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.SrcPDBName + "/" - //url := "https://" + pdb.Spec.CDBResName + "-ords:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.SrcPDBName + "/" url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.SrcPDBName + "/" pdb.Status.Phase = pdbPhaseClone @@ -782,7 +783,7 @@ func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dba if err := r.Status().Update(ctx, pdb); err != nil { log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) } - _, err = r.callAPI(ctx, req, pdb, url, values, "POST") + _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") if err != nil { return err } @@ -809,7 +810,8 @@ func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dba /* ************************************************ - Plug a PDB - /*********************************************** + +*********************************************** */ func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { @@ -868,7 +870,7 @@ func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbap if err := r.Status().Update(ctx, pdb); err != nil { log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) } - _, err = r.callAPI(ctx, req, pdb, url, values, "POST") + _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") if err != nil { return err } @@ -879,6 +881,7 @@ func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbap pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName } else { pdb.Status.ConnString = cdb.Spec.DBTnsurl + ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) } assertivePdbDeletion = pdb.Spec.AssertivePdbDeletion @@ -894,7 +897,8 @@ func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbap /* ************************************************ - Unplug a PDB - /*********************************************** + +*********************************************** */ func (r *PDBReconciler) unplugPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { @@ -950,7 +954,7 @@ func (r *PDBReconciler) unplugPDB(ctx context.Context, req ctrl.Request, pdb *db if err := r.Status().Update(ctx, pdb); err != nil { log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) } - _, err = r.callAPI(ctx, req, pdb, url, values, "POST") + _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") if err != nil { return err } @@ -1036,7 +1040,7 @@ func (r *PDBReconciler) modifyPDB(ctx context.Context, req ctrl.Request, pdb *db if err := r.Status().Update(ctx, pdb); err != nil { log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) } - _, err = r.callAPI(ctx, req, pdb, url, values, "POST") + _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") if err != nil { return err } @@ -1078,7 +1082,7 @@ func (r *PDBReconciler) getPDBState(ctx context.Context, req ctrl.Request, pdb * log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) } - respData, err := r.callAPI(ctx, req, pdb, url, nil, "GET") + respData, err := NewCallApi(r, ctx, req, pdb, url, nil, "GET") if err != nil { pdb.Status.OpenMode = "UNKNOWN" @@ -1129,7 +1133,7 @@ func (r *PDBReconciler) mapPDB(ctx context.Context, req ctrl.Request, pdb *dbapi log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) } - respData, err := r.callAPI(ctx, req, pdb, url, nil, "GET") + respData, err := NewCallApi(r, ctx, req, pdb, url, nil, "GET") if err != nil { pdb.Status.OpenMode = "UNKNOWN" @@ -1227,6 +1231,7 @@ func (r *PDBReconciler) managePDBDeletion2(ctx context.Context, req ctrl.Request return err } + var errclose error pdbName := pdb.Spec.PDBName if pdb.Status.OpenMode == "READ WRITE" { valuesclose := map[string]string{ @@ -1234,23 +1239,26 @@ func (r *PDBReconciler) managePDBDeletion2(ctx context.Context, req ctrl.Request "modifyOption": "IMMEDIATE", "getScript": "FALSE"} url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" - _, errclose := r.callAPI(ctx, req, pdb, url, valuesclose, "POST") + _, errclose = NewCallApi(r, ctx, req, pdb, url, valuesclose, "POST") if errclose != nil { log.Info("Warning error closing pdb continue anyway") } } - valuesdrop := map[string]string{ - "action": "INCLUDING", - "getScript": "FALSE"} - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" - - log.Info("Call Delete()") - _, errdelete := r.callAPI(ctx, req, pdb, url, valuesdrop, "DELETE") - if errdelete != nil { - log.Error(errdelete, "Fail to delete pdb :"+pdb.Name, "err", err.Error()) - return errdelete + if errclose == nil { + valuesdrop := map[string]string{ + "action": "INCLUDING", + "getScript": "FALSE"} + url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" + + log.Info("Call Delete()") + _, errdelete := NewCallApi(r, ctx, req, pdb, url, valuesdrop, "DELETE") + if errdelete != nil { + log.Error(errdelete, "Fail to delete pdb :"+pdb.Name, "err", errdelete.Error()) + return errdelete + } } + } /* END OF ASSERTIVE SECTION */ log.Info("Marked to be deleted") @@ -1304,7 +1312,7 @@ func (r *PDBReconciler) deletePDBInstance(req ctrl.Request, ctx context.Context, if err := r.Status().Update(ctx, pdb); err != nil { log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) } - _, err = r.callAPI(ctx, req, pdb, url, values, "DELETE") + _, err = NewCallApi(r, ctx, req, pdb, url, values, "DELETE") if err != nil { pdb.Status.ConnString = "" return err @@ -1363,3 +1371,212 @@ func ParseTnsAlias(tns *string, pdbsrv *string) { fmt.Printf("Newstring [%s]\n", *tns) } + +func NewCallApi(intr interface{}, ctx context.Context, req ctrl.Request, pdb *dbapi.PDB, url string, payload map[string]string, action string) (string, error) { + + var c client.Client + var r logr.Logger + var e record.EventRecorder + var err error + + recpdb, ok1 := intr.(*PDBReconciler) + if ok1 { + fmt.Printf("func NewCallApi ((*PDBReconciler),......)\n") + c = recpdb.Client + e = recpdb.Recorder + r = recpdb.Log + } + + reccdb, ok2 := intr.(*CDBReconciler) + if ok2 { + fmt.Printf("func NewCallApi ((*CDBReconciler),......)\n") + c = reccdb.Client + e = reccdb.Recorder + r = reccdb.Log + } + + secret := &corev1.Secret{} + + log := r.WithValues("NewCallApi", req.NamespacedName) + log.Info("Call c.Get") + err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBTlsKey.Secret.SecretName, Namespace: pdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + pdb.Spec.PDBTlsKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + rsaKeyPEM := secret.Data[pdb.Spec.PDBTlsKey.Secret.Key] + + err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBTlsCrt.Secret.SecretName, Namespace: pdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + pdb.Spec.PDBTlsCrt.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + rsaCertPEM := secret.Data[pdb.Spec.PDBTlsCrt.Secret.Key] + + err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBTlsCat.Secret.SecretName, Namespace: pdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + pdb.Spec.PDBTlsCat.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + caCert := secret.Data[pdb.Spec.PDBTlsCat.Secret.Key] + /* + r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSINFO", string(rsaKeyPEM)) + r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSINFO", string(rsaCertPEM)) + r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSINFO", string(caCert)) + */ + + certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) + if err != nil { + pdb.Status.Msg = "Error tls.X509KeyPair" + return "", err + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, RootCAs: caCertPool} + + tr := &http.Transport{TLSClientConfig: tlsConf} + + httpclient := &http.Client{Transport: tr} + + log.Info("Issuing REST call", "URL", url, "Action", action) + + /* + cdb, err := r.getCDBResource(ctx, req, pdb) + if err != nil { + return "", err + } + */ + + err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.WebServerUsr.Secret.SecretName, Namespace: pdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + pdb.Spec.WebServerUsr.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + webUser := string(secret.Data[pdb.Spec.WebServerUsr.Secret.Key]) + webUser = strings.TrimSpace(webUser) + + secret = &corev1.Secret{} + err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.WebServerPwd.Secret.SecretName, Namespace: pdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + pdb.Spec.WebServerPwd.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + webUserPwd := string(secret.Data[pdb.Spec.WebServerPwd.Secret.Key]) + webUserPwd = strings.TrimSpace(webUserPwd) + + var httpreq *http.Request + if action == "GET" { + httpreq, err = http.NewRequest(action, url, nil) + } else { + jsonValue, _ := json.Marshal(payload) + httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) + } + + if err != nil { + log.Info("Unable to create HTTP Request for PDB : "+pdb.Name, "err", err.Error()) + return "", err + } + + httpreq.Header.Add("Accept", "application/json") + httpreq.Header.Add("Content-Type", "application/json") + httpreq.SetBasicAuth(webUser, webUserPwd) + + resp, err := httpclient.Do(httpreq) + if err != nil { + errmsg := err.Error() + log.Error(err, "Failed - Could not connect to ORDS Pod", "err", err.Error()) + pdb.Status.Msg = "Error: Could not connect to ORDS Pod" + e.Eventf(pdb, corev1.EventTypeWarning, "ORDSError", errmsg) + return "", err + } + + e.Eventf(pdb, corev1.EventTypeWarning, "Done", pdb.Spec.CDBResName) + if resp.StatusCode != http.StatusOK { + bb, _ := ioutil.ReadAll(resp.Body) + + if resp.StatusCode == 404 { + pdb.Status.ConnString = "" + pdb.Status.Msg = pdb.Spec.PDBName + " not found" + + } else { + if floodcontrol == false { + pdb.Status.Msg = "ORDS Error - HTTP Status Code:" + strconv.Itoa(resp.StatusCode) + } + } + + if floodcontrol == false { + log.Info("ORDS Error - HTTP Status Code :"+strconv.Itoa(resp.StatusCode), "Err", string(bb)) + } + + var apiErr ORDSError + json.Unmarshal([]byte(bb), &apiErr) + if floodcontrol == false { + e.Eventf(pdb, corev1.EventTypeWarning, "ORDSError", "Failed: %s", apiErr.Message) + } + //fmt.Printf("%+v", apiErr) + //fmt.Println(string(bb)) + floodcontrol = true + return "", errors.New("ORDS Error") + } + floodcontrol = false + + defer resp.Body.Close() + + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Print(err.Error()) + } + respData := string(bodyBytes) + //fmt.Println(string(bodyBytes)) + + var apiResponse RESTSQLCollection + json.Unmarshal([]byte(bodyBytes), &apiResponse) + //fmt.Printf("%#v", apiResponse) + //fmt.Printf("%+v", apiResponse) + + errFound := false + for _, sqlItem := range apiResponse.Items { + if sqlItem.ErrorDetails != "" { + log.Info("ORDS Error - Oracle Error Code :" + strconv.Itoa(sqlItem.ErrorCode)) + if !errFound { + pdb.Status.Msg = sqlItem.ErrorDetails + } + e.Eventf(pdb, corev1.EventTypeWarning, "OraError", "%s", sqlItem.ErrorDetails) + errFound = true + } + } + + if errFound { + return "", errors.New("Oracle Error") + } + + return respData, nil +} diff --git a/main.go b/main.go index c9feef64..3b6f957d 100644 --- a/main.go +++ b/main.go @@ -224,11 +224,11 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") os.Exit(1) } - if err = (&databasev1alpha1.PDB{}).SetupWebhookWithManager(mgr); err != nil { + if err = (&databasev4.PDB{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "PDB") os.Exit(1) } - if err = (&databasev1alpha1.CDB{}).SetupWebhookWithManager(mgr); err != nil { + if err = (&databasev4.CDB{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "CDB") os.Exit(1) } @@ -343,9 +343,9 @@ func main() { // Add index for PDB CR to enable mgr to cache PDBs indexFunc := func(obj client.Object) []string { - return []string{obj.(*databasev1alpha1.PDB).Spec.PDBName} + return []string{obj.(*databasev4.PDB).Spec.PDBName} } - if err = cache.IndexField(context.TODO(), &databasev1alpha1.PDB{}, "spec.pdbName", indexFunc); err != nil { + if err = cache.IndexField(context.TODO(), &databasev4.PDB{}, "spec.pdbName", indexFunc); err != nil { setupLog.Error(err, "unable to create index function for ", "controller", "PDB") os.Exit(1) } diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 09d4c5c3..ae9d93fe 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -1122,10 +1122,6 @@ spec: jsonPath: .spec.dbPort name: DB Port type: integer - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - description: Replicas jsonPath: .spec.replicas name: Replicas @@ -1138,7 +1134,11 @@ spec: jsonPath: .status.msg name: Message type: string - name: v1alpha1 + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 schema: openAPIV3Schema: properties: @@ -1218,6 +1218,8 @@ spec: type: string dbTnsurl: type: string + deletePdbCascade: + type: boolean nodeSelector: additionalProperties: type: string @@ -2554,10 +2556,6 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - description: Name of the CDB jsonPath: .spec.cdbName name: CDB Name @@ -2582,7 +2580,11 @@ spec: jsonPath: .status.msg name: Message type: string - name: v1alpha1 + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 schema: openAPIV3Schema: properties: @@ -4707,13 +4709,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-dbcssystem + path: /mutate-database-oracle-com-v4-cdb failurePolicy: Fail - name: mdbcssystemv4.kb.io + name: mcdb.kb.io rules: - apiGroups: - database.oracle.com @@ -4723,7 +4726,7 @@ webhooks: - CREATE - UPDATE resources: - - dbcssystems + - cdbs sideEffects: None - admissionReviewVersions: - v1 @@ -4731,9 +4734,9 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-shardingdatabase + path: /mutate-database-oracle-com-v4-dbcssystem failurePolicy: Fail - name: mshardingdatabasev4.kb.io + name: mdbcssystemv4.kb.io rules: - apiGroups: - database.oracle.com @@ -4743,27 +4746,28 @@ webhooks: - CREATE - UPDATE resources: - - shardingdatabases + - dbcssystems sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup + path: /mutate-database-oracle-com-v4-pdb failurePolicy: Fail - name: mautonomousdatabasebackupv1alpha1.kb.io + name: mpdb.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - autonomousdatabasebackups + - pdbs sideEffects: None - admissionReviewVersions: - v1 @@ -4771,30 +4775,29 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + path: /mutate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: mautonomousdatabasev1alpha1.kb.io + name: mshardingdatabasev4.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - autonomousdatabases + - shardingdatabases sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-cdb + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup failurePolicy: Fail - name: mcdb.kb.io + name: mautonomousdatabasebackupv1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -4804,18 +4807,17 @@ webhooks: - CREATE - UPDATE resources: - - cdbs + - autonomousdatabasebackups sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-dataguardbroker + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase failurePolicy: Fail - name: mdataguardbroker.kb.io + name: mautonomousdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -4825,48 +4827,48 @@ webhooks: - CREATE - UPDATE resources: - - dataguardbrokers + - autonomousdatabases sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-dbcssystem + path: /mutate-database-oracle-com-v1alpha1-dataguardbroker failurePolicy: Fail - name: mdbcssystemv1alpha1.kb.io + name: mdataguardbroker.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v4 + - v1alpha1 operations: - CREATE - UPDATE resources: - - dbcssystems + - dataguardbrokers sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice + path: /mutate-database-oracle-com-v4-dbcssystem failurePolicy: Fail - name: moraclerestdataservice.kb.io + name: mdbcssystemv1alpha1.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - oraclerestdataservices + - dbcssystems sideEffects: None - admissionReviewVersions: - v1 @@ -4875,9 +4877,9 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-pdb + path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice failurePolicy: Fail - name: mpdb.kb.io + name: moraclerestdataservice.kb.io rules: - apiGroups: - database.oracle.com @@ -4887,7 +4889,7 @@ webhooks: - CREATE - UPDATE resources: - - pdbs + - oraclerestdataservices sideEffects: None - admissionReviewVersions: - v1 @@ -5040,13 +5042,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-shardingdatabase + path: /validate-database-oracle-com-v4-cdb failurePolicy: Fail - name: vshardingdatabasev4.kb.io + name: vcdb.kb.io rules: - apiGroups: - database.oracle.com @@ -5055,29 +5058,29 @@ webhooks: operations: - CREATE - UPDATE - - DELETE resources: - - shardingdatabases + - cdbs sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + path: /validate-database-oracle-com-v4-pdb failurePolicy: Fail - name: vautonomouscontainerdatabasev1alpha1.kb.io + name: vpdb.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE resources: - - autonomouscontainerdatabases + - pdbs sideEffects: None - admissionReviewVersions: - v1 @@ -5085,19 +5088,20 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup + path: /validate-database-oracle-com-v4-shardingdatabase failurePolicy: Fail - name: vautonomousdatabasebackupv1alpha1.kb.io + name: vshardingdatabasev4.kb.io rules: - apiGroups: - database.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE + - DELETE resources: - - autonomousdatabasebackups + - shardingdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -5105,9 +5109,9 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase failurePolicy: Fail - name: vautonomousdatabaserestorev1alpha1.kb.io + name: vautonomouscontainerdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -5117,7 +5121,7 @@ webhooks: - CREATE - UPDATE resources: - - autonomousdatabaserestores + - autonomouscontainerdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -5125,9 +5129,9 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup failurePolicy: Fail - name: vautonomousdatabasev1alpha1.kb.io + name: vautonomousdatabasebackupv1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -5137,18 +5141,17 @@ webhooks: - CREATE - UPDATE resources: - - autonomousdatabases + - autonomousdatabasebackups sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-cdb + path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore failurePolicy: Fail - name: vcdb.kb.io + name: vautonomousdatabaserestorev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -5158,18 +5161,17 @@ webhooks: - CREATE - UPDATE resources: - - cdbs + - autonomousdatabaserestores sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-dataguardbroker + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase failurePolicy: Fail - name: vdataguardbroker.kb.io + name: vautonomousdatabasev1alpha1.kb.io rules: - apiGroups: - database.oracle.com @@ -5179,7 +5181,7 @@ webhooks: - CREATE - UPDATE resources: - - dataguardbrokers + - autonomousdatabases sideEffects: None - admissionReviewVersions: - v1 @@ -5188,9 +5190,9 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + path: /validate-database-oracle-com-v1alpha1-dataguardbroker failurePolicy: Fail - name: voraclerestdataservice.kb.io + name: vdataguardbroker.kb.io rules: - apiGroups: - database.oracle.com @@ -5200,7 +5202,7 @@ webhooks: - CREATE - UPDATE resources: - - oraclerestdataservices + - dataguardbrokers sideEffects: None - admissionReviewVersions: - v1 @@ -5209,9 +5211,9 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-pdb + path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice failurePolicy: Fail - name: vpdb.kb.io + name: voraclerestdataservice.kb.io rules: - apiGroups: - database.oracle.com @@ -5221,7 +5223,7 @@ webhooks: - CREATE - UPDATE resources: - - pdbs + - oraclerestdataservices sideEffects: None - admissionReviewVersions: - v1 From 1b5597f595b5710162432a5fdc22f93e11a6b3f7 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Wed, 11 Dec 2024 16:14:41 +0000 Subject: [PATCH 137/414] Update KUSTOMIZE Version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a388f472..684f3259 100644 --- a/Makefile +++ b/Makefile @@ -143,7 +143,7 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions -KUSTOMIZE_VERSION ?= v4.5.7 +KUSTOMIZE_VERSION ?= v5.3.0 CONTROLLER_TOOLS_VERSION ?= v0.16.5 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" From 54d6d65e4799841922dcba4fa6d672dcaf276ebb Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Thu, 12 Dec 2024 07:48:03 +0000 Subject: [PATCH 138/414] v4_lrest_lrpdb --- PROJECT | 16 + apis/database/v4/lrest_types.go | 191 ++ apis/database/v4/lrest_webhook.go | 219 ++ apis/database/v4/lrpdb_types.go | 256 ++ apis/database/v4/lrpdb_webhook.go | 370 +++ apis/database/v4/zz_generated.deepcopy.go | 619 +++++ commons/multitenant/lrest/common.go | 113 + .../crd/bases/database.oracle.com_lrests.yaml | 254 ++ .../crd/bases/database.oracle.com_lrpdbs.yaml | 369 +++ config/crd/kustomization.yaml | 6 + .../cainjection_in_database_lrests.yaml | 8 + .../cainjection_in_database_lrpdbs.yaml | 8 + config/crd/patches/webhook_in_lrests.yaml | 17 + config/crd/patches/webhook_in_lrpdbs.yaml | 17 + config/manager/kustomization.yaml | 2 +- config/rbac/lrest_editor_role.yaml | 24 + config/rbac/lrest_viewer_role.yaml | 20 + config/rbac/lrpdb_editor_role.yaml | 24 + config/rbac/lrpdb_viewer_role.yaml | 20 + config/rbac/role.yaml | 7 + config/samples/database_v4_lrest.yaml | 6 + config/samples/database_v4_lrpdb.yaml | 6 + config/samples/kustomization.yaml | 2 + config/webhook/manifests.yaml | 84 + controllers/database/lrest_controller.go | 1105 ++++++++ controllers/database/lrpdb_controller.go | 2381 +++++++++++++++++ main.go | 64 +- oracle-database-operator.yaml | 716 ++++- 28 files changed, 6911 insertions(+), 13 deletions(-) create mode 100644 apis/database/v4/lrest_types.go create mode 100644 apis/database/v4/lrest_webhook.go create mode 100644 apis/database/v4/lrpdb_types.go create mode 100644 apis/database/v4/lrpdb_webhook.go create mode 100644 commons/multitenant/lrest/common.go create mode 100644 config/crd/bases/database.oracle.com_lrests.yaml create mode 100644 config/crd/bases/database.oracle.com_lrpdbs.yaml create mode 100644 config/crd/patches/cainjection_in_database_lrests.yaml create mode 100644 config/crd/patches/cainjection_in_database_lrpdbs.yaml create mode 100644 config/crd/patches/webhook_in_lrests.yaml create mode 100644 config/crd/patches/webhook_in_lrpdbs.yaml create mode 100644 config/rbac/lrest_editor_role.yaml create mode 100644 config/rbac/lrest_viewer_role.yaml create mode 100644 config/rbac/lrpdb_editor_role.yaml create mode 100644 config/rbac/lrpdb_viewer_role.yaml create mode 100644 config/samples/database_v4_lrest.yaml create mode 100644 config/samples/database_v4_lrpdb.yaml create mode 100644 controllers/database/lrest_controller.go create mode 100644 controllers/database/lrpdb_controller.go diff --git a/PROJECT b/PROJECT index ea420230..c4cd1bef 100644 --- a/PROJECT +++ b/PROJECT @@ -180,4 +180,20 @@ resources: kind: DbcsSystem path: github.com/oracle/oracle-database-operator/apis/database/v4 version: v4 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: database + kind: LREST + path: github.com/oracle/oracle-database-operator/apis/database/v4 + version: v4 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: database + kind: LRPDB + path: github.com/oracle/oracle-database-operator/apis/database/v4 + version: v4 version: "3" diff --git a/apis/database/v4/lrest_types.go b/apis/database/v4/lrest_types.go new file mode 100644 index 00000000..421a3ea1 --- /dev/null +++ b/apis/database/v4/lrest_types.go @@ -0,0 +1,191 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// LRESTSpec defines the desired state of LREST +type LRESTSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Name of the LREST + LRESTName string `json:"cdbName,omitempty"` + // Name of the LREST Service + ServiceName string `json:"serviceName,omitempty"` + + // Password for the LREST System Administrator + SysAdminPwd LRESTSysAdminPassword `json:"sysAdminPwd,omitempty"` + // User in the root container with sysdba priviledges to manage PDB lifecycle + LRESTAdminUser LRESTAdminUser `json:"cdbAdminUser,omitempty"` + // Password for the LREST Administrator to manage PDB lifecycle + LRESTAdminPwd LRESTAdminPassword `json:"cdbAdminPwd,omitempty"` + + LRESTTlsKey LRESTTLSKEY `json:"cdbTlsKey,omitempty"` + LRESTTlsCrt LRESTTLSCRT `json:"cdbTlsCrt,omitempty"` + LRESTPubKey LRESTPUBKEY `json:"cdbPubKey,omitempty"` + LRESTPriKey LRESTPRVKEY `json:"cdbPrvKey,omitempty"` + + // Password for user LREST_PUBLIC_USER + LRESTPwd LRESTPassword `json:"lrestPwd,omitempty"` + // LREST server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. + LRESTPort int `json:"lrestPort,omitempty"` + // LREST Image Name + LRESTImage string `json:"lrestImage,omitempty"` + // The name of the image pull secret in case of a private docker repository. + LRESTImagePullSecret string `json:"lrestImagePullSecret,omitempty"` + // LREST Image Pull Policy + // +kubebuilder:validation:Enum=Always;Never + LRESTImagePullPolicy string `json:"lrestImagePullPolicy,omitempty"` + // Number of LREST Containers to create + Replicas int `json:"replicas,omitempty"` + // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + WebLrestServerUser WebLrestServerUser `json:"webServerUser,omitempty"` + // Password for the Web Server User + WebLrestServerPwd WebLrestServerPassword `json:"webServerPwd,omitempty"` + // Name of the DB server + DBServer string `json:"dbServer,omitempty"` + // DB server port + DBPort int `json:"dbPort,omitempty"` + // Node Selector for running the Pod + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + DBTnsurl string `json:"dbTnsurl,omitempty"` + DeletePDBCascade bool `json:"deletePdbCascade,omitempty"` +} + +// LRESTSecret defines the secretName +type LRESTSecret struct { + SecretName string `json:"secretName"` + Key string `json:"key"` +} + +// LRESTSysAdminPassword defines the secret containing SysAdmin Password mapped to key 'sysAdminPwd' for LREST +type LRESTSysAdminPassword struct { + Secret LRESTSecret `json:"secret"` +} + +// LRESTAdminUser defines the secret containing LREST Administrator User mapped to key 'lrestAdminUser' to manage PDB lifecycle +type LRESTAdminUser struct { + Secret LRESTSecret `json:"secret"` +} + +// LRESTAdminPassword defines the secret containing LREST Administrator Password mapped to key 'lrestAdminPwd' to manage PDB lifecycle +type LRESTAdminPassword struct { + Secret LRESTSecret `json:"secret"` +} + +// LRESTPassword defines the secret containing LREST_PUBLIC_USER Password mapped to key 'ordsPwd' +type LRESTPassword struct { + Secret LRESTSecret `json:"secret"` +} + +// WebLrestServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle +type WebLrestServerUser struct { + Secret LRESTSecret `json:"secret"` +} + +// WebLrestServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle +type WebLrestServerPassword struct { + Secret LRESTSecret `json:"secret"` +} + +type LRESTTLSKEY struct { + Secret LRESTSecret `json:"secret"` +} + +type LRESTTLSCRT struct { + Secret LRESTSecret `json:"secret"` +} + +type LRESTPUBKEY struct { + Secret LRESTSecret `json:"secret"` +} + +type LRESTPRVKEY struct { + Secret LRESTSecret `json:"secret"` +} + +// LRESTStatus defines the observed state of LREST +type LRESTStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Phase of the LREST Resource + Phase string `json:"phase"` + // LREST Resource Status + Status bool `json:"status"` + // Message + Msg string `json:"msg,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB NAME",type="string",description="Name of the LREST" +// +kubebuilder:printcolumn:JSONPath=".spec.dbServer",name="DB Server",type="string",description=" Name of the DB Server" +// +kubebuilder:printcolumn:JSONPath=".spec.dbPort",name="DB Port",type="integer",description="DB server port" +// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="Replicas",type="integer",description="Replicas" +// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the LREST Resource" +// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message if any" +// +kubebuilder:printcolumn:JSONPath=".spec.dbTnsurl",name="TNS STRING",type="string",description="string of the tnsalias" +// +kubebuilder:resource:path=lrests,scope=Namespaced +// +kubebuilder:storageversion + +// LREST is the Schema for the lrests API +type LREST struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec LRESTSpec `json:"spec,omitempty"` + Status LRESTStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// LRESTList contains a list of LREST +type LRESTList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []LREST `json:"items"` +} + +func init() { + SchemeBuilder.Register(&LREST{}, &LRESTList{}) +} diff --git a/apis/database/v4/lrest_webhook.go b/apis/database/v4/lrest_webhook.go new file mode 100644 index 00000000..9d65a1d6 --- /dev/null +++ b/apis/database/v4/lrest_webhook.go @@ -0,0 +1,219 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "reflect" + "strings" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var lrestlog = logf.Log.WithName("lrest-webhook") + +func (r *LREST) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-lrest,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=lrests,verbs=create;update,versions=v4,name=mlrest.kb.io,admissionReviewVersions={v4,v1beta1} + +var _ webhook.Defaulter = &LREST{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *LREST) Default() { + lrestlog.Info("Setting default values in LREST spec for : " + r.Name) + + if r.Spec.LRESTPort == 0 { + r.Spec.LRESTPort = 8888 + } + + if r.Spec.Replicas == 0 { + r.Spec.Replicas = 1 + } +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:path=/validate-database-oracle-com-v4-lrest,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=lrests,verbs=create;update,versions=v4,name=vlrest.kb.io,admissionReviewVersions={v4,v1beta1} + +var _ webhook.Validator = &LREST{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *LREST) ValidateCreate() (admission.Warnings, error) { + lrestlog.Info("ValidateCreate", "name", r.Name) + + var allErrs field.ErrorList + + if r.Spec.ServiceName == "" && r.Spec.DBServer != "" { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("serviceName"), "Please specify LREST Service name")) + } + + if reflect.ValueOf(r.Spec.LRESTTlsKey).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("lrestTlsKey"), "Please specify LREST Tls key(secret)")) + } + + if reflect.ValueOf(r.Spec.LRESTTlsCrt).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("lrestTlsCrt"), "Please specify LREST Tls Certificate(secret)")) + } + + /*if r.Spec.SCANName == "" { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("scanName"), "Please specify SCAN Name for LREST")) + }*/ + + if (r.Spec.DBServer == "" && r.Spec.DBTnsurl == "") || (r.Spec.DBServer != "" && r.Spec.DBTnsurl != "") { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("dbServer"), "Please specify Database Server Name/IP Address or tnsalias string")) + } + + if r.Spec.DBTnsurl != "" && (r.Spec.DBServer != "" || r.Spec.DBPort != 0 || r.Spec.ServiceName != "") { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("dbServer"), "DBtnsurl is orthogonal to (DBServer,DBport,Services)")) + } + + if r.Spec.DBPort == 0 && r.Spec.DBServer != "" { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("dbPort"), "Please specify DB Server Port")) + } + if r.Spec.DBPort < 0 && r.Spec.DBServer != "" { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("dbPort"), "Please specify a valid DB Server Port")) + } + if r.Spec.LRESTPort < 0 { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("ordsPort"), "Please specify a valid LREST Port")) + } + if r.Spec.Replicas < 0 { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("replicas"), "Please specify a valid value for Replicas")) + } + if r.Spec.LRESTImage == "" { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("ordsImage"), "Please specify name of LREST Image to be used")) + } + if reflect.ValueOf(r.Spec.LRESTAdminUser).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("lrestAdminUser"), "Please specify user in the root container with sysdba priviledges to manage PDB lifecycle")) + } + if reflect.ValueOf(r.Spec.LRESTAdminPwd).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("lrestAdminPwd"), "Please specify password for the LREST Administrator to manage PDB lifecycle")) + } + /* if reflect.ValueOf(r.Spec.LRESTPwd).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("ordsPwd"), "Please specify password for user LREST_PUBLIC_USER")) + } */ + if reflect.ValueOf(r.Spec.WebLrestServerUser).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("webLrestServerUser"), "Please specify the Web Server User having SQL Administrator role")) + } + if reflect.ValueOf(r.Spec.WebLrestServerPwd).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("webServerPwd"), "Please specify password for the Web Server User having SQL Administrator role")) + } + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "LREST"}, + r.Name, allErrs) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *LREST) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + lrestlog.Info("validate update", "name", r.Name) + + isLRESTMarkedToBeDeleted := r.GetDeletionTimestamp() != nil + if isLRESTMarkedToBeDeleted { + return nil, nil + } + + var allErrs field.ErrorList + + // Check for updation errors + oldLREST, ok := old.(*LREST) + if !ok { + return nil, nil + } + + if r.Spec.DBPort < 0 { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("dbPort"), "Please specify a valid DB Server Port")) + } + if r.Spec.LRESTPort < 0 { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("ordsPort"), "Please specify a valid LREST Port")) + } + if r.Spec.Replicas < 0 { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("replicas"), "Please specify a valid value for Replicas")) + } + if !strings.EqualFold(oldLREST.Spec.ServiceName, r.Spec.ServiceName) { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("replicas"), "cannot be changed")) + } + + if len(allErrs) == 0 { + return nil, nil + } + + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "LREST"}, + r.Name, allErrs) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *LREST) ValidateDelete() (admission.Warnings, error) { + lrestlog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} diff --git a/apis/database/v4/lrpdb_types.go b/apis/database/v4/lrpdb_types.go new file mode 100644 index 00000000..d37bebdc --- /dev/null +++ b/apis/database/v4/lrpdb_types.go @@ -0,0 +1,256 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// LRPDBSpec defines the desired state of LRPDB +type LRPDBSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + LRPDBTlsKey LRPDBTLSKEY `json:"lrpdbTlsKey,omitempty"` + LRPDBTlsCrt LRPDBTLSCRT `json:"lrpdbTlsCrt,omitempty"` + LRPDBTlsCat LRPDBTLSCAT `json:"lrpdbTlsCat,omitempty"` + LRPDBPriKey LRPDBPRVKEY `json:"cdbPrvKey,omitempty"` + + // Namespace of the rest server + CDBNamespace string `json:"cdbNamespace,omitempty"` + // Name of the CDB Custom Resource that runs the LREST container + CDBResName string `json:"cdbResName,omitempty"` + // Name of the CDB + CDBName string `json:"cdbName,omitempty"` + // The name of the new LRPDB. Relevant for both Create and Plug Actions. + LRPDBName string `json:"pdbName,omitempty"` + // Name of the Source LRPDB from which to clone + SrcLRPDBName string `json:"srcPdbName,omitempty"` + // The administrator username for the new LRPDB. This property is required when the Action property is Create. + AdminName LRPDBAdminName `json:"adminName,omitempty"` + // The administrator password for the new LRPDB. This property is required when the Action property is Create. + AdminPwd LRPDBAdminPassword `json:"adminPwd,omitempty"` + // Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. + AdminpdbUser AdminpdbUser `json:"adminpdbUser,omitempty"` + AdminpdbPass AdminpdbPass `json:"adminpdbPass,omitempty"` + + FileNameConversions string `json:"fileNameConversions,omitempty"` + // This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. + SourceFileNameConversions string `json:"sourceFileNameConversions,omitempty"` + // XML metadata filename to be used for Plug or Unplug operations + XMLFileName string `json:"xmlFileName,omitempty"` + // To copy files or not while cloning a LRPDB + // +kubebuilder:validation:Enum=COPY;NOCOPY;MOVE + CopyAction string `json:"copyAction,omitempty"` + // Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). + // +kubebuilder:validation:Enum=INCLUDING;KEEP + DropAction string `json:"dropAction,omitempty"` + // A Path specified for sparse clone snapshot copy. (Optional) + SparseClonePath string `json:"sparseClonePath,omitempty"` + // Whether to reuse temp file + ReuseTempFile *bool `json:"reuseTempFile,omitempty"` + // Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. + UnlimitedStorage *bool `json:"unlimitedStorage,omitempty"` + // Indicate if 'AS CLONE' option should be used in the command to plug in a LRPDB. This property is applicable when the Action property is PLUG but not required. + AsClone *bool `json:"asClone,omitempty"` + // Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. + TotalSize string `json:"totalSize,omitempty"` + // Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. + TempSize string `json:"tempSize,omitempty"` + // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + WebLrpdbServerUser WebLrpdbServerUser `json:"webServerUser,omitempty"` + // Password for the Web Server User + WebLrpdbServerPwd WebLrpdbServerPassword `json:"webServerPwd,omitempt"` + // TDE import for plug operations + LTDEImport *bool `json:"tdeImport,omitempty"` + // LTDE export for unplug operations + LTDEExport *bool `json:"tdeExport,omitempty"` + // TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations + LTDEPassword LTDEPwd `json:"tdePassword,omitempty"` + // LTDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + LTDEKeystorePath string `json:"tdeKeystorePath,omitempty"` + // LTDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + LTDESecret LTDESecret `json:"tdeSecret,omitempty"` + // Whether you need the script only or execute the script + GetScript *bool `json:"getScript,omitempty"` + // Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map/Alter. Map is used to map a Databse LRPDB to a Kubernetes LRPDB CR. + // +kubebuilder:validation:Enum=Create;Clone;Plug;Unplug;Delete;Modify;Status;Map;Alter;Noaction + Action string `json:"action"` + // Extra options for opening and closing a LRPDB + // +kubebuilder:validation:Enum=IMMEDIATE;NORMAL;READ ONLY;READ WRITE;RESTRICTED + ModifyOption string `json:"modifyOption,omitempty"` + // to be used with ALTER option - obsolete do not use + AlterSystem string `json:"alterSystem,omitempty"` + // to be used with ALTER option - the name of the parameter + AlterSystemParameter string `json:"alterSystemParameter"` + // to be used with ALTER option - the value of the parameter + AlterSystemValue string `json:"alterSystemValue"` + // parameter scope + ParameterScope string `json:"parameterScope,omitempty"` + // The target state of the LRPDB + // +kubebuilder:validation:Enum=OPEN;CLOSE;ALTER + LRPDBState string `json:"pdbState,omitempty"` + // turn on the assertive approach to delete pdb resource + // kubectl delete pdb ..... automatically triggers the pluggable database + // deletion + AssertiveLrpdbDeletion bool `json:"assertiveLrpdbDeletion,omitempty"` + PDBConfigMap string `json:"pdbconfigmap,omitempty"` +} + +// LRPDBAdminName defines the secret containing Sys Admin User mapped to key 'adminName' for LRPDB +type LRPDBAdminName struct { + Secret LRPDBSecret `json:"secret"` +} + +// LRPDBAdminPassword defines the secret containing Sys Admin Password mapped to key 'adminPwd' for LRPDB +type LRPDBAdminPassword struct { + Secret LRPDBSecret `json:"secret"` +} + +// TDEPwd defines the secret containing TDE Wallet Password mapped to key 'tdePassword' for LRPDB +type LTDEPwd struct { + Secret LRPDBSecret `json:"secret"` +} + +// TDESecret defines the secret containing TDE Secret to key 'tdeSecret' for LRPDB +type LTDESecret struct { + Secret LRPDBSecret `json:"secret"` +} + +type WebLrpdbServerUser struct { + Secret LRPDBSecret `json:"secret"` +} + +type WebLrpdbServerPassword struct { + Secret LRPDBSecret `json:"secret"` +} + +type AdminpdbUser struct { + Secret LRPDBSecret `json:"secret"` +} + +type AdminpdbPass struct { + Secret LRPDBSecret `json:"secret"` +} + +// LRPDBSecret defines the secretName +type LRPDBSecret struct { + SecretName string `json:"secretName"` + Key string `json:"key"` +} + +type LRPDBTLSKEY struct { + Secret LRPDBSecret `json:"secret"` +} + +type LRPDBTLSCRT struct { + Secret LRPDBSecret `json:"secret"` +} + +type LRPDBTLSCAT struct { + Secret LRPDBSecret `json:"secret"` +} + +type LRPDBPRVKEY struct { + Secret LRPDBSecret `json:"secret"` +} + +// LRPDBStatus defines the observed state of LRPDB +type LRPDBStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // LRPDB Connect String + ConnString string `json:"connString,omitempty"` + // Phase of the LRPDB Resource + Phase string `json:"phase"` + // LRPDB Resource Status + Status bool `json:"status"` + // Total size of the LRPDB + TotalSize string `json:"totalSize,omitempty"` + // Open mode of the LRPDB + OpenMode string `json:"openMode,omitempty"` + // Modify Option of the LRPDB + ModifyOption string `json:"modifyOption,omitempty"` + // Message + Msg string `json:"msg,omitempty"` + // Last Completed Action + Action string `json:"action,omitempty"` + // Last Completed alter system + AlterSystem string `json:"alterSystem,omitempty"` + // Last ORA- + SqlCode int `json:"sqlCode"` + Bitstat int `json:"bitstat,omitempty"` /* Bitmask */ + BitStatStr string `json:"bitstatstr,omitempty"` /* Decoded bitmask */ +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" +// +kubebuilder:printcolumn:JSONPath=".spec.pdbName",name="PDB Name",type="string",description="Name of the PDB" +// +kubebuilder:printcolumn:JSONPath=".status.openMode",name="PDB State",type="string",description="PDB Open Mode" +// +kubebuilder:printcolumn:JSONPath=".status.totalSize",name="PDB Size",type="string",description="Total Size of the PDB" +// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the LRPDB Resource" +// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" +// +kubebuilder:printcolumn:JSONPath=".status.sqlCode",name="last sqlcode",type="integer",description="last sqlcode" +// +kubebuilder:printcolumn:JSONPath=".status.connString",name="Connect_String",type="string",description="The connect string to be used" +// +kubebuilder:resource:path=lrpdbs,scope=Namespaced +// +kubebuilder:storageversion + +// LRPDB is the Schema for the pdbs API +type LRPDB struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec LRPDBSpec `json:"spec,omitempty"` + Status LRPDBStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// LRPDBList contains a list of LRPDB +type LRPDBList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []LRPDB `json:"items"` +} + +func init() { + SchemeBuilder.Register(&LRPDB{}, &LRPDBList{}) +} diff --git a/apis/database/v4/lrpdb_webhook.go b/apis/database/v4/lrpdb_webhook.go new file mode 100644 index 00000000..d6807926 --- /dev/null +++ b/apis/database/v4/lrpdb_webhook.go @@ -0,0 +1,370 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +/* MODIFIED (MM/DD/YY) +** rcitton 07/14/22 - 33822886 + */ + +package v4 + +import ( + "context" + "fmt" + "reflect" + "strconv" + "strings" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var lrpdblog = logf.Log.WithName("lrpdb-webhook") + +func (r *LRPDB) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + WithValidator(&LRPDB{}). + WithDefaulter(&LRPDB{}). + For(r). + Complete() +} + +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-lrpdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=lrpdbs,verbs=create;update,versions=v4,name=mlrpdb.kb.io,admissionReviewVersions={v4,v1beta1} + +var _ webhook.CustomDefaulter = &LRPDB{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *LRPDB) Default(ctx context.Context, obj runtime.Object) error { + pdb, ok := obj.(*LRPDB) + if !ok { + return fmt.Errorf("expected an LRPDB object but got %T", obj) + } + lrpdblog.Info("Setting default values in LRPDB spec for : " + pdb.Name) + + action := strings.ToUpper(pdb.Spec.Action) + + if action == "DELETE" { + if pdb.Spec.DropAction == "" { + pdb.Spec.DropAction = "KEEP" + lrpdblog.Info(" - dropAction : KEEP") + } + } else if action != "MODIFY" && action != "STATUS" { + if pdb.Spec.ReuseTempFile == nil { + pdb.Spec.ReuseTempFile = new(bool) + *pdb.Spec.ReuseTempFile = true + lrpdblog.Info(" - reuseTempFile : " + strconv.FormatBool(*(pdb.Spec.ReuseTempFile))) + } + if pdb.Spec.UnlimitedStorage == nil { + pdb.Spec.UnlimitedStorage = new(bool) + *pdb.Spec.UnlimitedStorage = true + lrpdblog.Info(" - unlimitedStorage : " + strconv.FormatBool(*(pdb.Spec.UnlimitedStorage))) + } + if pdb.Spec.LTDEImport == nil { + pdb.Spec.LTDEImport = new(bool) + *pdb.Spec.LTDEImport = false + lrpdblog.Info(" - tdeImport : " + strconv.FormatBool(*(pdb.Spec.LTDEImport))) + } + if pdb.Spec.LTDEExport == nil { + pdb.Spec.LTDEExport = new(bool) + *pdb.Spec.LTDEExport = false + lrpdblog.Info(" - tdeExport : " + strconv.FormatBool(*(pdb.Spec.LTDEExport))) + } + if pdb.Spec.AsClone == nil { + pdb.Spec.AsClone = new(bool) + *pdb.Spec.AsClone = false + lrpdblog.Info(" - asClone : " + strconv.FormatBool(*(pdb.Spec.AsClone))) + } + } + + if pdb.Spec.GetScript == nil { + pdb.Spec.GetScript = new(bool) + *pdb.Spec.GetScript = false + lrpdblog.Info(" - getScript : " + strconv.FormatBool(*(pdb.Spec.GetScript))) + } + return nil +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:path=/validate-database-oracle-com-v4-lrpdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=lrpdbs,verbs=create;update,versions=v4,name=vlrpdb.kb.io,admissionReviewVersions={v4,v1beta1} + +var _ webhook.CustomValidator = &LRPDB{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *LRPDB) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + lrpdblog.Info("ValidateCreate-Validating LRPDB spec for : " + r.Name) + pdb := obj.(*LRPDB) + + var allErrs field.ErrorList + + r.validateCommon(&allErrs, ctx, *pdb) + + r.validateAction(&allErrs, ctx, *pdb) + + action := strings.ToUpper(pdb.Spec.Action) + + if len(allErrs) == 0 { + lrpdblog.Info("LRPDB Resource : " + r.Name + " successfully validated for Action : " + action) + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "LRPDB"}, + r.Name, allErrs) + return nil, nil +} + +// Validate Action for required parameters +func (r *LRPDB) validateAction(allErrs *field.ErrorList, ctx context.Context, pdb LRPDB) { + action := strings.ToUpper(pdb.Spec.Action) + + lrpdblog.Info("Valdiating LRPDB Resource Action : " + action) + + if reflect.ValueOf(pdb.Spec.LRPDBTlsKey).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("lrpdbTlsKey"), "Please specify LRPDB Tls Key(secret)")) + } + + if reflect.ValueOf(pdb.Spec.LRPDBTlsCrt).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("lrpdbTlsCrt"), "Please specify LRPDB Tls Certificate(secret)")) + } + + if reflect.ValueOf(pdb.Spec.LRPDBTlsCat).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("lrpdbTlsCat"), "Please specify LRPDB Tls Certificate Authority(secret)")) + } + + switch action { + case "DELETE": + /* BUG 36752336 - LREST OPERATOR - DELETE NON-EXISTENT PDB SHOWS LRPDB CREATED MESSAGE */ + if pdb.Status.OpenMode == "READ WRITE" { + lrpdblog.Info("Cannot delete: pdb is open ") + *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+pdb.Spec.LRPDBName+" "+pdb.Status.OpenMode)) + } + r.CheckObjExistence("DELETE", allErrs, ctx, pdb) + case "CREATE": + if reflect.ValueOf(pdb.Spec.AdminpdbUser).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("adminpdbUser"), "Please specify LRPDB System Administrator user")) + } + if reflect.ValueOf(pdb.Spec.AdminpdbPass).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("adminpdbPass"), "Please specify LRPDB System Administrator Password")) + } + if pdb.Spec.FileNameConversions == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("fileNameConversions"), "Please specify a value for fileNameConversions. Values can be a filename convert pattern or NONE")) + } + if pdb.Spec.TotalSize == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("totalSize"), "When the storage is not UNLIMITED the Total Size must be specified")) + } + if pdb.Spec.TempSize == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) + } + if *(pdb.Spec.LTDEImport) { + r.validateTDEInfo(allErrs, ctx, pdb) + } + case "CLONE": + // Sample Err: The LRPDB "lrpdb1-clone" is invalid: spec.srcPdbName: Required value: Please specify source LRPDB for Cloning + if pdb.Spec.SrcLRPDBName == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("srcPdbName"), "Please specify source LRPDB name for Cloning")) + } + if pdb.Spec.TotalSize == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("totalSize"), "When the storage is not UNLIMITED the Total Size must be specified")) + } + if pdb.Spec.TempSize == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) + } + if pdb.Status.OpenMode == "MOUNT" { + lrpdblog.Info("Cannot clone: pdb is mount ") + *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+pdb.Spec.LRPDBName+" "+pdb.Status.OpenMode)) + } + case "PLUG": + if pdb.Spec.XMLFileName == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("xmlFileName"), "Please specify XML metadata filename")) + } + if pdb.Spec.FileNameConversions == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("fileNameConversions"), "Please specify a value for fileNameConversions. Values can be a filename convert pattern or NONE")) + } + if pdb.Spec.SourceFileNameConversions == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("sourceFileNameConversions"), "Please specify a value for sourceFileNameConversions. Values can be a filename convert pattern or NONE")) + } + if pdb.Spec.CopyAction == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("copyAction"), "Please specify a value for copyAction. Values can be COPY, NOCOPY or MOVE")) + } + if *(pdb.Spec.LTDEImport) { + r.validateTDEInfo(allErrs, ctx, pdb) + } + case "UNPLUG": + if pdb.Spec.XMLFileName == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("xmlFileName"), "Please specify XML metadata filename")) + } + if *(pdb.Spec.LTDEExport) { + r.validateTDEInfo(allErrs, ctx, pdb) + } + if pdb.Status.OpenMode == "READ WRITE" { + lrpdblog.Info("Cannot unplug: pdb is open ") + *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+pdb.Spec.LRPDBName+" "+pdb.Status.OpenMode)) + } + r.CheckObjExistence("UNPLUG", allErrs, ctx, pdb) + case "MODIFY": + + if pdb.Spec.LRPDBState == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("lrpdbState"), "Please specify target state of LRPDB")) + } + if pdb.Spec.ModifyOption == "" && pdb.Spec.AlterSystem == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("modifyOption"), "Please specify an option for opening/closing a LRPDB or alter system parameter")) + } + r.CheckObjExistence("MODIFY", allErrs, ctx, pdb) + } +} + +func (r *LRPDB) CheckObjExistence(action string, allErrs *field.ErrorList, ctx context.Context, pdb LRPDB) { + /* BUG 36752465 - lrest operator - open non-existent pdb creates a lrpdb with status failed */ + lrpdblog.Info("Action [" + action + "] checkin " + pdb.Spec.LRPDBName + " existence") + if pdb.Status.OpenMode == "" { + *allErrs = append(*allErrs, field.NotFound(field.NewPath("Spec").Child("LRPDBName"), " "+pdb.Spec.LRPDBName+" does not exist : action "+action+" failure")) + + } +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *LRPDB) ValidateUpdate(ctx context.Context, obj runtime.Object, old runtime.Object) (admission.Warnings, error) { + lrpdblog.Info("ValidateUpdate-Validating LRPDB spec for : " + r.Name) + pdb := old.(*LRPDB) + + isLRPDBMarkedToBeDeleted := r.GetDeletionTimestamp() != nil + if isLRPDBMarkedToBeDeleted { + return nil, nil + } + + var allErrs field.ErrorList + action := strings.ToUpper(pdb.Spec.Action) + + // If LRPDB CR has been created and in Ready state, only allow updates if the "action" value has changed as well + if (pdb.Status.Phase == "Ready") && (pdb.Status.Action != "MODIFY") && (pdb.Status.Action != "STATUS") && (pdb.Status.Action != "NOACTION") && (pdb.Status.Action == action) { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("action"), "New action also needs to be specified after LRPDB is in Ready state")) + } else { + + // Check Common Validations + r.validateCommon(&allErrs, ctx, *pdb) + + // Validate required parameters for Action specified + r.validateAction(&allErrs, ctx, *pdb) + + // Check TDE requirements + if (action != "DELETE") && (action != "MODIFY") && (action != "STATUS") && (*(pdb.Spec.LTDEImport) || *(pdb.Spec.LTDEExport)) { + r.validateTDEInfo(&allErrs, ctx, *pdb) + } + } + + if len(allErrs) == 0 { + return nil, nil + } + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "LRPDB"}, + r.Name, allErrs) + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *LRPDB) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + lrpdblog.Info("ValidateDelete-Validating LRPDB spec for : " + r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// Validate common specs needed for all LRPDB Actions +func (r *LRPDB) validateCommon(allErrs *field.ErrorList, ctx context.Context, pdb LRPDB) { + lrpdblog.Info("validateCommon", "name", pdb.Name) + + if pdb.Spec.Action == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("action"), "Please specify LRPDB operation to be performed")) + } + if pdb.Spec.CDBResName == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("cdbResName"), "Please specify the name of the CDB Kubernetes resource to use for LRPDB operations")) + } + if pdb.Spec.CDBNamespace == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("cdbNamespace"), "Please specify the namespace of the rest server to use for LRPDB operations")) + } + if pdb.Spec.LRPDBName == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("lrpdbName"), "Please specify name of the LRPDB to be created")) + } +} + +// Validate TDE information for Create, Plug and Unplug Actions +func (r *LRPDB) validateTDEInfo(allErrs *field.ErrorList, ctx context.Context, pdb LRPDB) { + lrpdblog.Info("validateTDEInfo", "name", r.Name) + + if reflect.ValueOf(pdb.Spec.LTDEPassword).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("tdePassword"), "Please specify a value for tdePassword.")) + } + if pdb.Spec.LTDEKeystorePath == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("tdeKeystorePath"), "Please specify a value for tdeKeystorePath.")) + } + if reflect.ValueOf(pdb.Spec.LTDESecret).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("tdeSecret"), "Please specify a value for tdeSecret.")) + } + +} diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 557cefc7..f1ced358 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -65,6 +65,38 @@ func (in *ACDSpec) DeepCopy() *ACDSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AdminpdbPass) DeepCopyInto(out *AdminpdbPass) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdminpdbPass. +func (in *AdminpdbPass) DeepCopy() *AdminpdbPass { + if in == nil { + return nil + } + out := new(AdminpdbPass) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AdminpdbUser) DeepCopyInto(out *AdminpdbUser) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdminpdbUser. +func (in *AdminpdbUser) DeepCopy() *AdminpdbUser { + if in == nil { + return nil + } + out := new(AdminpdbUser) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AutonomousContainerDatabase) DeepCopyInto(out *AutonomousContainerDatabase) { *out = *in @@ -1477,6 +1509,529 @@ func (in *KMSDetailsStatus) DeepCopy() *KMSDetailsStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LREST) DeepCopyInto(out *LREST) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LREST. +func (in *LREST) DeepCopy() *LREST { + if in == nil { + return nil + } + out := new(LREST) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *LREST) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTAdminPassword) DeepCopyInto(out *LRESTAdminPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTAdminPassword. +func (in *LRESTAdminPassword) DeepCopy() *LRESTAdminPassword { + if in == nil { + return nil + } + out := new(LRESTAdminPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTAdminUser) DeepCopyInto(out *LRESTAdminUser) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTAdminUser. +func (in *LRESTAdminUser) DeepCopy() *LRESTAdminUser { + if in == nil { + return nil + } + out := new(LRESTAdminUser) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTList) DeepCopyInto(out *LRESTList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]LREST, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTList. +func (in *LRESTList) DeepCopy() *LRESTList { + if in == nil { + return nil + } + out := new(LRESTList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *LRESTList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTPRVKEY) DeepCopyInto(out *LRESTPRVKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTPRVKEY. +func (in *LRESTPRVKEY) DeepCopy() *LRESTPRVKEY { + if in == nil { + return nil + } + out := new(LRESTPRVKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTPUBKEY) DeepCopyInto(out *LRESTPUBKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTPUBKEY. +func (in *LRESTPUBKEY) DeepCopy() *LRESTPUBKEY { + if in == nil { + return nil + } + out := new(LRESTPUBKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTPassword) DeepCopyInto(out *LRESTPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTPassword. +func (in *LRESTPassword) DeepCopy() *LRESTPassword { + if in == nil { + return nil + } + out := new(LRESTPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTSecret) DeepCopyInto(out *LRESTSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTSecret. +func (in *LRESTSecret) DeepCopy() *LRESTSecret { + if in == nil { + return nil + } + out := new(LRESTSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTSpec) DeepCopyInto(out *LRESTSpec) { + *out = *in + out.SysAdminPwd = in.SysAdminPwd + out.LRESTAdminUser = in.LRESTAdminUser + out.LRESTAdminPwd = in.LRESTAdminPwd + out.LRESTTlsKey = in.LRESTTlsKey + out.LRESTTlsCrt = in.LRESTTlsCrt + out.LRESTPubKey = in.LRESTPubKey + out.LRESTPriKey = in.LRESTPriKey + out.LRESTPwd = in.LRESTPwd + out.WebLrestServerUser = in.WebLrestServerUser + out.WebLrestServerPwd = in.WebLrestServerPwd + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTSpec. +func (in *LRESTSpec) DeepCopy() *LRESTSpec { + if in == nil { + return nil + } + out := new(LRESTSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTStatus) DeepCopyInto(out *LRESTStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTStatus. +func (in *LRESTStatus) DeepCopy() *LRESTStatus { + if in == nil { + return nil + } + out := new(LRESTStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTSysAdminPassword) DeepCopyInto(out *LRESTSysAdminPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTSysAdminPassword. +func (in *LRESTSysAdminPassword) DeepCopy() *LRESTSysAdminPassword { + if in == nil { + return nil + } + out := new(LRESTSysAdminPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTTLSCRT) DeepCopyInto(out *LRESTTLSCRT) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTTLSCRT. +func (in *LRESTTLSCRT) DeepCopy() *LRESTTLSCRT { + if in == nil { + return nil + } + out := new(LRESTTLSCRT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRESTTLSKEY) DeepCopyInto(out *LRESTTLSKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRESTTLSKEY. +func (in *LRESTTLSKEY) DeepCopy() *LRESTTLSKEY { + if in == nil { + return nil + } + out := new(LRESTTLSKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDB) DeepCopyInto(out *LRPDB) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDB. +func (in *LRPDB) DeepCopy() *LRPDB { + if in == nil { + return nil + } + out := new(LRPDB) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *LRPDB) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBAdminName) DeepCopyInto(out *LRPDBAdminName) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBAdminName. +func (in *LRPDBAdminName) DeepCopy() *LRPDBAdminName { + if in == nil { + return nil + } + out := new(LRPDBAdminName) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBAdminPassword) DeepCopyInto(out *LRPDBAdminPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBAdminPassword. +func (in *LRPDBAdminPassword) DeepCopy() *LRPDBAdminPassword { + if in == nil { + return nil + } + out := new(LRPDBAdminPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBList) DeepCopyInto(out *LRPDBList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]LRPDB, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBList. +func (in *LRPDBList) DeepCopy() *LRPDBList { + if in == nil { + return nil + } + out := new(LRPDBList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *LRPDBList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBPRVKEY) DeepCopyInto(out *LRPDBPRVKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBPRVKEY. +func (in *LRPDBPRVKEY) DeepCopy() *LRPDBPRVKEY { + if in == nil { + return nil + } + out := new(LRPDBPRVKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBSecret) DeepCopyInto(out *LRPDBSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBSecret. +func (in *LRPDBSecret) DeepCopy() *LRPDBSecret { + if in == nil { + return nil + } + out := new(LRPDBSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBSpec) DeepCopyInto(out *LRPDBSpec) { + *out = *in + out.LRPDBTlsKey = in.LRPDBTlsKey + out.LRPDBTlsCrt = in.LRPDBTlsCrt + out.LRPDBTlsCat = in.LRPDBTlsCat + out.LRPDBPriKey = in.LRPDBPriKey + out.AdminName = in.AdminName + out.AdminPwd = in.AdminPwd + out.AdminpdbUser = in.AdminpdbUser + out.AdminpdbPass = in.AdminpdbPass + if in.ReuseTempFile != nil { + in, out := &in.ReuseTempFile, &out.ReuseTempFile + *out = new(bool) + **out = **in + } + if in.UnlimitedStorage != nil { + in, out := &in.UnlimitedStorage, &out.UnlimitedStorage + *out = new(bool) + **out = **in + } + if in.AsClone != nil { + in, out := &in.AsClone, &out.AsClone + *out = new(bool) + **out = **in + } + out.WebLrpdbServerUser = in.WebLrpdbServerUser + out.WebLrpdbServerPwd = in.WebLrpdbServerPwd + if in.LTDEImport != nil { + in, out := &in.LTDEImport, &out.LTDEImport + *out = new(bool) + **out = **in + } + if in.LTDEExport != nil { + in, out := &in.LTDEExport, &out.LTDEExport + *out = new(bool) + **out = **in + } + out.LTDEPassword = in.LTDEPassword + out.LTDESecret = in.LTDESecret + if in.GetScript != nil { + in, out := &in.GetScript, &out.GetScript + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBSpec. +func (in *LRPDBSpec) DeepCopy() *LRPDBSpec { + if in == nil { + return nil + } + out := new(LRPDBSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBStatus) DeepCopyInto(out *LRPDBStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBStatus. +func (in *LRPDBStatus) DeepCopy() *LRPDBStatus { + if in == nil { + return nil + } + out := new(LRPDBStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBTLSCAT) DeepCopyInto(out *LRPDBTLSCAT) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBTLSCAT. +func (in *LRPDBTLSCAT) DeepCopy() *LRPDBTLSCAT { + if in == nil { + return nil + } + out := new(LRPDBTLSCAT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBTLSCRT) DeepCopyInto(out *LRPDBTLSCRT) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBTLSCRT. +func (in *LRPDBTLSCRT) DeepCopy() *LRPDBTLSCRT { + if in == nil { + return nil + } + out := new(LRPDBTLSCRT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LRPDBTLSKEY) DeepCopyInto(out *LRPDBTLSKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LRPDBTLSKEY. +func (in *LRPDBTLSKEY) DeepCopy() *LRPDBTLSKEY { + if in == nil { + return nil + } + out := new(LRPDBTLSKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LTDEPwd) DeepCopyInto(out *LTDEPwd) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LTDEPwd. +func (in *LTDEPwd) DeepCopy() *LTDEPwd { + if in == nil { + return nil + } + out := new(LTDEPwd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LTDESecret) DeepCopyInto(out *LTDESecret) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LTDESecret. +func (in *LTDESecret) DeepCopy() *LTDESecret { + if in == nil { + return nil + } + out := new(LTDESecret) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkAccessSpec) DeepCopyInto(out *NetworkAccessSpec) { *out = *in @@ -2375,6 +2930,70 @@ func (in *WalletSpec) DeepCopy() *WalletSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebLrestServerPassword) DeepCopyInto(out *WebLrestServerPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebLrestServerPassword. +func (in *WebLrestServerPassword) DeepCopy() *WebLrestServerPassword { + if in == nil { + return nil + } + out := new(WebLrestServerPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebLrestServerUser) DeepCopyInto(out *WebLrestServerUser) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebLrestServerUser. +func (in *WebLrestServerUser) DeepCopy() *WebLrestServerUser { + if in == nil { + return nil + } + out := new(WebLrestServerUser) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebLrpdbServerPassword) DeepCopyInto(out *WebLrpdbServerPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebLrpdbServerPassword. +func (in *WebLrpdbServerPassword) DeepCopy() *WebLrpdbServerPassword { + if in == nil { + return nil + } + out := new(WebLrpdbServerPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebLrpdbServerUser) DeepCopyInto(out *WebLrpdbServerUser) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebLrpdbServerUser. +func (in *WebLrpdbServerUser) DeepCopy() *WebLrpdbServerUser { + if in == nil { + return nil + } + out := new(WebLrpdbServerUser) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WebServerPassword) DeepCopyInto(out *WebServerPassword) { *out = *in diff --git a/commons/multitenant/lrest/common.go b/commons/multitenant/lrest/common.go new file mode 100644 index 00000000..e72e85b0 --- /dev/null +++ b/commons/multitenant/lrest/common.go @@ -0,0 +1,113 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. +*/ + +package lrest + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "regexp" + "strings" + + corev1 "k8s.io/api/core/v1" + + ctrl "sigs.k8s.io/controller-runtime" +) + +func CommonDecryptWithPrivKey(Key string, Buffer string, req ctrl.Request) (string, error) { + + Debug := 0 + block, _ := pem.Decode([]byte(Key)) + pkcs8PrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + fmt.Printf("Failed to parse private key %s \n", err.Error()) + return "", err + } + if Debug == 1 { + fmt.Printf("======================================\n") + fmt.Printf("%s\n", Key) + fmt.Printf("======================================\n") + } + + encString64, err := base64.StdEncoding.DecodeString(string(Buffer)) + if err != nil { + fmt.Printf("Failed to decode encrypted string to base64: %s\n", err.Error()) + return "", err + } + + decryptedB, err := rsa.DecryptPKCS1v15(nil, pkcs8PrivateKey.(*rsa.PrivateKey), encString64) + if err != nil { + fmt.Printf("Failed to decrypt string %s\n", err.Error()) + return "", err + } + if Debug == 1 { + fmt.Printf("[%s]\n", string(decryptedB)) + } + return strings.TrimSpace(string(decryptedB)), err + +} + +func ParseConfigMapData(cfgmap *corev1.ConfigMap) []string { + + var tokens []string + for Key, Value := range cfgmap.Data { + fmt.Printf("KEY:%s\n", Key) + re0 := regexp.MustCompile("\\n") + re1 := regexp.MustCompile(";") + re2 := regexp.MustCompile(",") /* Additional separator for future use */ + + Value = re0.ReplaceAllString(Value, " ") + tokens = strings.Split(Value, " ") + + for cnt := range tokens { + if len(tokens[cnt]) != 0 { + tokens[cnt] = re1.ReplaceAllString(tokens[cnt], " ") + tokens[cnt] = re2.ReplaceAllString(tokens[cnt], " ") + + } + + } + + } + + return tokens + +} diff --git a/config/crd/bases/database.oracle.com_lrests.yaml b/config/crd/bases/database.oracle.com_lrests.yaml new file mode 100644 index 00000000..c20356e7 --- /dev/null +++ b/config/crd/bases/database.oracle.com_lrests.yaml @@ -0,0 +1,254 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: lrests.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LREST + listKind: LRESTList + plural: lrests + singular: lrest + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the LREST + jsonPath: .spec.cdbName + name: CDB NAME + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the LREST Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message if any + jsonPath: .status.msg + name: Message + type: string + - description: string of the tnsalias + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + lrestImage: + type: string + lrestImagePullPolicy: + enum: + - Always + - Never + type: string + lrestImagePullSecret: + type: string + lrestPort: + type: integer + lrestPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + nodeSelector: + additionalProperties: + type: string + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/database.oracle.com_lrpdbs.yaml b/config/crd/bases/database.oracle.com_lrpdbs.yaml new file mode 100644 index 00000000..14ad7f29 --- /dev/null +++ b/config/crd/bases/database.oracle.com_lrpdbs.yaml @@ -0,0 +1,369 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: lrpdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LRPDB + listKind: LRPDBList + plural: lrpdbs + singular: lrpdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the LRPDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: last sqlcode + jsonPath: .status.sqlCode + name: last sqlcode + type: integer + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + - Alter + - Noaction + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbPass: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + alterSystem: + type: string + alterSystemParameter: + type: string + alterSystemValue: + type: string + asClone: + type: boolean + assertiveLrpdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + lrpdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + parameterScope: + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + - ALTER + type: string + pdbconfigmap: + type: string + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + - alterSystemParameter + - alterSystemValue + - webServerPwd + type: object + status: + properties: + action: + type: string + alterSystem: + type: string + bitstat: + type: integer + bitstatstr: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + sqlCode: + type: integer + status: + type: boolean + totalSize: + type: string + required: + - phase + - sqlCode + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index c42e9a76..66e53ac4 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -18,6 +18,8 @@ resources: - bases/database.oracle.com_dbcssystems.yaml - bases/database.oracle.com_dataguardbrokers.yaml - bases/observability.oracle.com_databaseobservers.yaml +- bases/database.oracle.com_lrests.yaml +- bases/database.oracle.com_lrpdbs.yaml # +kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -36,6 +38,8 @@ patchesStrategicMerge: - patches/webhook_in_autonomousdatabasebackups.yaml - patches/webhook_in_autonomousdatabaserestores.yaml - patches/webhook_in_autonomouscontainerdatabases.yaml +#- patches/webhook_in_lrests.yaml +#- patches/webhook_in_lrpdbs.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. @@ -54,6 +58,8 @@ patchesStrategicMerge: - patches/cainjection_in_autonomousdatabasebackups.yaml - patches/cainjection_in_autonomousdatabaserestores.yaml - patches/cainjection_in_autonomouscontainerdatabases.yaml +#- patches/cainjection_in_lrests.yaml +#- patches/cainjection_in_lrpdbs.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_database_lrests.yaml b/config/crd/patches/cainjection_in_database_lrests.yaml new file mode 100644 index 00000000..22f4b410 --- /dev/null +++ b/config/crd/patches/cainjection_in_database_lrests.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: lrests.database.oracle.com diff --git a/config/crd/patches/cainjection_in_database_lrpdbs.yaml b/config/crd/patches/cainjection_in_database_lrpdbs.yaml new file mode 100644 index 00000000..f6f21f4c --- /dev/null +++ b/config/crd/patches/cainjection_in_database_lrpdbs.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: lrpdbs.database.oracle.com diff --git a/config/crd/patches/webhook_in_lrests.yaml b/config/crd/patches/webhook_in_lrests.yaml new file mode 100644 index 00000000..01afd4b5 --- /dev/null +++ b/config/crd/patches/webhook_in_lrests.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: lrests.database.oracle.com +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/crd/patches/webhook_in_lrpdbs.yaml b/config/crd/patches/webhook_in_lrpdbs.yaml new file mode 100644 index 00000000..4120e72f --- /dev/null +++ b/config/crd/patches/webhook_in_lrpdbs.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: lrpdbs.database.oracle.com +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 84042a15..1a9d97d3 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: container-registry.oracle.com/database/operator:latest + newName: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/operatormntnns newTag: latest diff --git a/config/rbac/lrest_editor_role.yaml b/config/rbac/lrest_editor_role.yaml new file mode 100644 index 00000000..7f5b2f01 --- /dev/null +++ b/config/rbac/lrest_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit lrests. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: lrest-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - lrests + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests/status + verbs: + - get diff --git a/config/rbac/lrest_viewer_role.yaml b/config/rbac/lrest_viewer_role.yaml new file mode 100644 index 00000000..d74bc977 --- /dev/null +++ b/config/rbac/lrest_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view lrests. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: lrest-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - lrests + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests/status + verbs: + - get diff --git a/config/rbac/lrpdb_editor_role.yaml b/config/rbac/lrpdb_editor_role.yaml new file mode 100644 index 00000000..20ae714a --- /dev/null +++ b/config/rbac/lrpdb_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit lrpdbs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: lrpdb-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - lrpdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/status + verbs: + - get diff --git a/config/rbac/lrpdb_viewer_role.yaml b/config/rbac/lrpdb_viewer_role.yaml new file mode 100644 index 00000000..95bcaab5 --- /dev/null +++ b/config/rbac/lrpdb_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view lrpdbs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: lrpdb-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - lrpdbs + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index ec3da481..9910f504 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -87,6 +87,9 @@ rules: - cdbs - dataguardbrokers - dbcssystems + - events + - lrests + - lrpdbs - oraclerestdataservices - pdbs - shardingdatabases @@ -108,6 +111,8 @@ rules: - cdbs/status - dataguardbrokers/status - dbcssystems/status + - lrests/status + - lrpdbs/status - oraclerestdataservices/status - pdbs/status - shardingdatabases/status @@ -140,6 +145,7 @@ rules: resources: - cdbs/finalizers - dataguardbrokers/finalizers + - lrests/finalizers - oraclerestdataservices/finalizers - singleinstancedatabases/finalizers verbs: @@ -148,6 +154,7 @@ rules: - database.oracle.com resources: - dbcssystems/finalizers + - lrpdbs/finalizers - pdbs/finalizers - shardingdatabases/finalizers verbs: diff --git a/config/samples/database_v4_lrest.yaml b/config/samples/database_v4_lrest.yaml new file mode 100644 index 00000000..8b8f9d16 --- /dev/null +++ b/config/samples/database_v4_lrest.yaml @@ -0,0 +1,6 @@ +apiVersion: database.oracle.com/v4 +kind: LREST +metadata: + name: lrest-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/database_v4_lrpdb.yaml b/config/samples/database_v4_lrpdb.yaml new file mode 100644 index 00000000..c674b264 --- /dev/null +++ b/config/samples/database_v4_lrpdb.yaml @@ -0,0 +1,6 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 1be7107d..2dbdb7f6 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -32,4 +32,6 @@ resources: - acd/autonomouscontainerdatabase_restart_terminate.yaml - database_v4_shardingdatabase.yaml - database_v4_dbcssystem.yaml +- database_v4_lrest.yaml +- database_v4_lrpdb.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 3c6f3e22..3c219e67 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -85,6 +85,48 @@ webhooks: resources: - dbcssystems sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: mlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: mlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -396,6 +438,48 @@ webhooks: resources: - cdbs sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: vlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: vlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/controllers/database/lrest_controller.go b/controllers/database/lrest_controller.go new file mode 100644 index 00000000..5ce90053 --- /dev/null +++ b/controllers/database/lrest_controller.go @@ -0,0 +1,1105 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "context" + "encoding/json" + "errors" + "fmt" + + //"fmt" + "strconv" + "strings" + "time" + + "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + //lrcommons "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" +) + +// LRESTReconciler reconciles a LREST object +type LRESTReconciler struct { + client.Client + Scheme *runtime.Scheme + Config *rest.Config + Log logr.Logger + Interval time.Duration + Recorder record.EventRecorder +} + +var ( + lrestPhaseInit = "Initializing" + lrestPhasePod = "CreatingPod" + lrestPhaseValPod = "ValidatingPods" + lrestPhaseService = "CreatingService" + lrestPhaseSecrets = "DeletingSecrets" + lrestPhaseReady = "Ready" + lrestPhaseDelete = "Deleting" + lrestPhaseFail = "Failed" +) + +const LRESTFinalizer = "database.oracle.com/LRESTfinalizer" + +//+kubebuilder:rbac:groups=database.oracle.com,resources=lrests,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=database.oracle.com,resources=lrests/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=database.oracle.com,resources=lrests/finalizers,verbs=update +//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;services;configmaps;events;replicasets,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups=core,resources=pods;secrets;services;configmaps;namespaces,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=apps,resources=replicasets,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the LREST object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile +func (r *LRESTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + log := r.Log.WithValues("multitenantoperator", req.NamespacedName) + log.Info("Reconcile requested") + + reconcilePeriod := r.Interval * time.Second + requeueY := ctrl.Result{Requeue: true, RequeueAfter: reconcilePeriod} + requeueN := ctrl.Result{} + + var err error + lrest := &dbapi.LREST{} + + // Execute for every reconcile + defer func() { + log.Info("DEFER", "Name", lrest.Name, "Phase", lrest.Status.Phase, "Status", strconv.FormatBool(lrest.Status.Status)) + if !lrest.Status.Status { + if err := r.Status().Update(ctx, lrest); err != nil { + log.Error(err, "Failed to update status for :"+lrest.Name, "err", err.Error()) + } + } + }() + + err = r.Client.Get(context.TODO(), req.NamespacedName, lrest) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("LREST Resource Not found", "Name", lrest.Name) + // Request object not found, could have been deleted after reconcile req. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + lrest.Status.Status = true + return requeueN, nil + } + // Error reading the object - requeue the req. + return requeueY, err + } + + log.Info("Res Status:", "Name", lrest.Name, "Phase", lrest.Status.Phase, "Status", strconv.FormatBool(lrest.Status.Status)) + + // Finalizer section + err = r.manageLRESTDeletion(ctx, req, lrest) + if err != nil { + log.Info("Reconcile queued") + return requeueY, nil + } + + // If post-creation, LREST spec is changed, check and take appropriate action + if (lrest.Status.Phase == lrestPhaseReady) && lrest.Status.Status { + r.evaluateSpecChange(ctx, req, lrest) + } + + if !lrest.Status.Status { + phase := lrest.Status.Phase + log.Info("Current Phase:"+phase, "Name", lrest.Name) + + switch phase { + case lrestPhaseInit: + err = r.verifySecrets(ctx, req, lrest) + if err != nil { + lrest.Status.Phase = lrestPhaseFail + return requeueN, nil + } + lrest.Status.Phase = lrestPhasePod + case lrestPhasePod: + // Create LREST PODs + err = r.createLRESTInstances(ctx, req, lrest) + if err != nil { + log.Info("Reconcile queued") + return requeueY, nil + } + lrest.Status.Phase = lrestPhaseValPod + case lrestPhaseValPod: + // Validate LREST PODs + err = r.validateLRESTPods(ctx, req, lrest) + if err != nil { + if lrest.Status.Phase == lrestPhaseFail { + return requeueN, nil + } + log.Info("Reconcile queued") + return requeueY, nil + } + lrest.Status.Phase = lrestPhaseService + case lrestPhaseService: + // Create LREST Service + err = r.createLRESTSVC(ctx, req, lrest) + if err != nil { + log.Info("Reconcile queued") + return requeueY, nil + } + //lrest.Status.Phase = lrestPhaseSecrets + lrest.Status.Phase = lrestPhaseReady + case lrestPhaseSecrets: + // Delete LREST Secrets + //r.deleteSecrets(ctx, req, lrest) + lrest.Status.Phase = lrestPhaseReady + lrest.Status.Msg = "Success" + case lrestPhaseReady: + lrest.Status.Status = true + r.Status().Update(ctx, lrest) + return requeueN, nil + default: + lrest.Status.Phase = lrestPhaseInit + log.Info("DEFAULT:", "Name", lrest.Name, "Phase", phase, "Status", strconv.FormatBool(lrest.Status.Status)) + } + + if err := r.Status().Update(ctx, lrest); err != nil { + log.Error(err, "Failed to update status for :"+lrest.Name, "err", err.Error()) + } + return requeueY, nil + } + + log.Info("Reconcile completed") + return requeueN, nil +} + +/* +********************************************************* + - Create a ReplicaSet for pods based on the LREST container + /******************************************************* +*/ +func (r *LRESTReconciler) createLRESTInstances(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + + log := r.Log.WithValues("createLRESTInstances", req.NamespacedName) + + replicaSet := r.createReplicaSetSpec(lrest) + + foundRS := &appsv1.ReplicaSet{} + err := r.Get(context.TODO(), types.NamespacedName{Name: replicaSet.Name, Namespace: lrest.Namespace}, foundRS) + if err != nil && apierrors.IsNotFound(err) { + log.Info("Creating LREST Replicaset: " + replicaSet.Name) + err = r.Create(ctx, replicaSet) + if err != nil { + log.Error(err, "Failed to create ReplicaSet for :"+lrest.Name, "Namespace", replicaSet.Namespace, "Name", replicaSet.Name) + return err + } + } else if err != nil { + log.Error(err, "Replicaset : "+replicaSet.Name+" already exists.") + return err + } + + // Set LREST instance as the owner and controller + ctrl.SetControllerReference(lrest, replicaSet, r.Scheme) + + log.Info("Created LREST ReplicaSet successfully") + r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "CreatedLRESTReplicaSet", "Created LREST Replicaset (Replicas - %s) for %s", strconv.Itoa(lrest.Spec.Replicas), lrest.Name) + return nil +} + +/* +************************************************ + - Validate LREST Pod. Check if there are any errors + /*********************************************** +*/ +func (r *LRESTReconciler) validateLRESTPods(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + + log := r.Log.WithValues("validateLRESTPod", req.NamespacedName) + + log.Info("Validating Pod creation for :" + lrest.Name) + + podName := lrest.Name + "-lrest" + podList := &corev1.PodList{} + listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingLabels{"name": podName}} + + // List retrieves list of objects for a given namespace and list options. + err := r.List(ctx, podList, listOpts...) + if err != nil { + log.Info("Failed to list pods of: "+podName, "Namespace", req.Namespace) + return err + } + + if len(podList.Items) == 0 { + log.Info("No pods found for: "+podName, "Namespace", req.Namespace) + lrest.Status.Msg = "Waiting for LREST Pod(s) to start" + return errors.New("Waiting for LREST pods to start") + } + + getLRESTStatus := " curl --cert /opt/oracle/lrest/certificates/tls.crt --cacert /opt/oracle/lrest/certificates/ca.crt --key /opt/oracle/lrest/certificates/tls.key -u `cat /opt/oracle/lrest/certificates/webserver_user`:`cat /opt/oracle/lrest/certificates/webserver_pwd` -sSkv -k -X GET https://localhost:" + strconv.Itoa(lrest.Spec.LRESTPort) + "/database/pdbs/" + readyPods := 0 + for _, pod := range podList.Items { + if pod.Status.Phase == corev1.PodRunning { + // Get LREST Status + out, err := dbcommons.ExecCommand(r, r.Config, pod.Name, pod.Namespace, "", ctx, req, false, "bash", "-c", getLRESTStatus) + if strings.Contains(out, "HTTP/1.1 200 OK") || strings.Contains(strings.ToUpper(err.Error()), "HTTP/1.1 200 OK") || + strings.Contains(out, "HTTP/2") || strings.Contains(strings.ToUpper(err.Error()), " HTTP/2") { + readyPods++ + } else if strings.Contains(out, "HTTP/1.1 404 Not Found") || strings.Contains(strings.ToUpper(err.Error()), "HTTP/1.1 404 NOT FOUND") || strings.Contains(strings.ToUpper(err.Error()), "HTTP/2 404") || strings.Contains(strings.ToUpper(err.Error()), "Failed to connect to localhost") { + // Check if DB connection parameters are correct + getLRESTInstallStatus := " grep -q 'Failed to' /tmp/lrest_install.log; echo $?;" + out, _ := dbcommons.ExecCommand(r, r.Config, pod.Name, pod.Namespace, "", ctx, req, false, "bash", "-c", getLRESTInstallStatus) + if strings.TrimSpace(out) == "0" { + lrest.Status.Msg = "Check DB connection parameters" + lrest.Status.Phase = lrestPhaseFail + // Delete existing ReplicaSet + r.deleteReplicaSet(ctx, req, lrest) + return errors.New("Check DB connection parameters") + } + } + } + } + + if readyPods != lrest.Spec.Replicas { + log.Info("Replicas: "+strconv.Itoa(lrest.Spec.Replicas), "Ready Pods: ", readyPods) + lrest.Status.Msg = "Waiting for LREST Pod(s) to be ready" + return errors.New("Waiting for LREST pods to be ready") + } + + lrest.Status.Msg = "" + return nil +} + +/* +*********************** + - Create Pod spec + +/*********************** +*/ +func (r *LRESTReconciler) createPodSpec(lrest *dbapi.LREST) corev1.PodSpec { + + podSpec := corev1.PodSpec{ + Volumes: []corev1.Volume{{ + Name: "secrets", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + DefaultMode: func() *int32 { i := int32(0666); return &i }(), + Sources: []corev1.VolumeProjection{ + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTPubKey.Secret.SecretName, + }, + Items: []corev1.KeyToPath{ + { + Key: lrest.Spec.LRESTPubKey.Secret.Key, + Path: lrest.Spec.LRESTPubKey.Secret.Key, + }, + }, + }, + }, + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTPriKey.Secret.SecretName, + }, + Items: []corev1.KeyToPath{ + { + Key: lrest.Spec.LRESTPriKey.Secret.Key, + Path: lrest.Spec.LRESTPriKey.Secret.Key, + }, + }, + }, + }, + + /***/ + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTTlsKey.Secret.SecretName, + }, + Items: []corev1.KeyToPath{ + { + Key: lrest.Spec.LRESTTlsKey.Secret.Key, + Path: lrest.Spec.LRESTTlsKey.Secret.Key, + }, + }, + }, + }, + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTTlsCrt.Secret.SecretName, + }, + Items: []corev1.KeyToPath{ + { + Key: lrest.Spec.LRESTTlsCrt.Secret.Key, + Path: lrest.Spec.LRESTTlsCrt.Secret.Key, + }, + }, + }, + }, + }, + }, + }, + }}, + SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: &[]bool{true}[0], + FSGroup: &[]int64{54321}[0], + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + }, + /*InitContainers: []corev1.Container{{ + Image: lrest.Spec.LRESTImage, + Name: lrest.Name + "-init", + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: securityContextDefine(), + Command: []string{"echo test > /opt/oracle/lrest/certificates/tests"}, + Env: func() []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: "ORACLE_HOST", + Value: lrest.Spec.DBTnsurl, + }} + }(), + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/opt/oracle/lrest/certificates", + Name: "secrets", + ReadOnly: false, + }}, + }},*/ + Containers: []corev1.Container{{ + Image: lrest.Spec.LRESTImage, + Name: lrest.Name + "-lrest", + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: securityContextDefine(), + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/opt/oracle/lrest/certificates", + Name: "secrets", + ReadOnly: true, + }, + }, + Env: func() []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: "ORACLE_HOST", + Value: lrest.Spec.DBServer, + }, + { + Name: "DBTNSURL", + Value: lrest.Spec.DBTnsurl, + }, + { + Name: "TLSCRT", + Value: lrest.Spec.LRESTTlsCrt.Secret.Key, + }, + { + Name: "TLSKEY", + Value: lrest.Spec.LRESTTlsKey.Secret.Key, + }, + { + Name: "PUBKEY", + Value: lrest.Spec.LRESTPubKey.Secret.Key, + }, + { + Name: "PRVKEY", + Value: lrest.Spec.LRESTPriKey.Secret.Key, + }, + { + Name: "ORACLE_PORT", + Value: strconv.Itoa(lrest.Spec.DBPort), + }, + { + Name: "LREST_PORT", + Value: strconv.Itoa(lrest.Spec.LRESTPort), + }, + { + Name: "ORACLE_SERVICE", + Value: lrest.Spec.ServiceName, + }, + { + Name: "R1", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTAdminUser.Secret.SecretName, + }, + Key: lrest.Spec.LRESTAdminUser.Secret.Key, + }, + }, + }, + { + Name: "R2", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTAdminPwd.Secret.SecretName, + }, + Key: lrest.Spec.LRESTAdminPwd.Secret.Key, + }, + }, + }, + { + Name: "R3", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.WebLrestServerUser.Secret.SecretName, + }, + Key: lrest.Spec.WebLrestServerUser.Secret.Key, + }, + }, + }, + { + Name: "R4", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.WebLrestServerPwd.Secret.SecretName, + }, + Key: lrest.Spec.WebLrestServerPwd.Secret.Key, + }, + }, + }, + } + }(), + }}, + + NodeSelector: func() map[string]string { + ns := make(map[string]string) + if len(lrest.Spec.NodeSelector) != 0 { + for key, value := range lrest.Spec.NodeSelector { + ns[key] = value + } + } + return ns + }(), + } + + if len(lrest.Spec.LRESTImagePullSecret) > 0 { + podSpec.ImagePullSecrets = []corev1.LocalObjectReference{ + { + Name: lrest.Spec.LRESTImagePullSecret, + }, + } + } + + podSpec.Containers[0].ImagePullPolicy = corev1.PullAlways + + if len(lrest.Spec.LRESTImagePullPolicy) > 0 { + if strings.ToUpper(lrest.Spec.LRESTImagePullPolicy) == "NEVER" { + podSpec.Containers[0].ImagePullPolicy = corev1.PullNever + } + } + + return podSpec +} + +/* +*********************** + - Create ReplicaSet spec + +/*********************** +*/ +func (r *LRESTReconciler) createReplicaSetSpec(lrest *dbapi.LREST) *appsv1.ReplicaSet { + + replicas := int32(lrest.Spec.Replicas) + podSpec := r.createPodSpec(lrest) + + replicaSet := &appsv1.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: lrest.Name + "-lrest-rs", + Namespace: lrest.Namespace, + Labels: map[string]string{ + "name": lrest.Name + "-lrest-rs", + }, + }, + Spec: appsv1.ReplicaSetSpec{ + Replicas: &replicas, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: lrest.Name + "-lrest", + Namespace: lrest.Namespace, + Labels: map[string]string{ + "name": lrest.Name + "-lrest", + }, + }, + Spec: podSpec, + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "name": lrest.Name + "-lrest", + }, + }, + }, + } + + return replicaSet +} + +/* +********************************************************* + - Evaluate change in Spec post creation and instantiation + /******************************************************* +*/ +func (r *LRESTReconciler) deleteReplicaSet(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + log := r.Log.WithValues("deleteReplicaSet", req.NamespacedName) + + k_client, err := kubernetes.NewForConfig(r.Config) + if err != nil { + log.Error(err, "Kubernetes Config Error") + return err + } + + replicaSetName := lrest.Name + "-lrest-rs" + err = k_client.AppsV1().ReplicaSets(lrest.Namespace).Delete(context.TODO(), replicaSetName, metav1.DeleteOptions{}) + if err != nil { + log.Info("Could not delete ReplicaSet", "RS Name", replicaSetName, "err", err.Error()) + if !strings.Contains(strings.ToUpper(err.Error()), "NOT FOUND") { + return err + } + } else { + log.Info("Successfully deleted LREST ReplicaSet", "RS Name", replicaSetName) + } + + return nil +} + +/* +********************************************************* + - Evaluate change in Spec post creation and instantiation + /******************************************************* +*/ +func (r *LRESTReconciler) evaluateSpecChange(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + log := r.Log.WithValues("evaluateSpecChange", req.NamespacedName) + + // List the Pods matching the PodTemplate Labels + podName := lrest.Name + "-lrest" + podList := &corev1.PodList{} + listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingLabels{"name": podName}} + + // List retrieves list of objects for a given namespace and list options. + err := r.List(ctx, podList, listOpts...) + if err != nil { + log.Info("Failed to list pods of: "+podName, "Namespace", req.Namespace) + return err + } + + var foundPod corev1.Pod + for _, pod := range podList.Items { + foundPod = pod + break + } + + lrestSpecChange := false + for _, envVar := range foundPod.Spec.Containers[0].Env { + if envVar.Name == "ORACLE_HOST" && envVar.Value != lrest.Spec.DBServer { + lrestSpecChange = true + } else if envVar.Name == "ORACLE_PORT" && envVar.Value != strconv.Itoa(lrest.Spec.DBPort) { + lrestSpecChange = true + } else if envVar.Name == "LREST_PORT" && envVar.Value != strconv.Itoa(lrest.Spec.LRESTPort) { + lrestSpecChange = true + } else if envVar.Name == "ORACLE_SERVICE" && envVar.Value != lrest.Spec.ServiceName { + lrestSpecChange = true + } + } + + if lrestSpecChange { + // Delete existing ReplicaSet + err = r.deleteReplicaSet(ctx, req, lrest) + if err != nil { + return err + } + + lrest.Status.Phase = lrestPhaseInit + lrest.Status.Status = false + r.Status().Update(ctx, lrest) + } else { + // Update the RS if the value of "replicas" is changed + replicaSetName := lrest.Name + "-lrest-rs" + + foundRS := &appsv1.ReplicaSet{} + err := r.Get(context.TODO(), types.NamespacedName{Name: replicaSetName, Namespace: lrest.Namespace}, foundRS) + if err != nil { + log.Error(err, "Unable to get LREST Replicaset: "+replicaSetName) + return err + } + + // Check if number of replicas have changed + replicas := int32(lrest.Spec.Replicas) + if lrest.Spec.Replicas != int(*(foundRS.Spec.Replicas)) { + log.Info("Existing Replicas: " + strconv.Itoa(int(*(foundRS.Spec.Replicas))) + ", New Replicas: " + strconv.Itoa(lrest.Spec.Replicas)) + foundRS.Spec.Replicas = &replicas + err = r.Update(ctx, foundRS) + if err != nil { + log.Error(err, "Failed to update ReplicaSet for :"+lrest.Name, "Namespace", lrest.Namespace, "Name", replicaSetName) + return err + } + lrest.Status.Phase = lrestPhaseValPod + lrest.Status.Status = false + r.Status().Update(ctx, lrest) + } + } + + return nil +} + +/* +************************************************ + - Create a Cluster Service for LREST LREST Pod + /*********************************************** +*/ +func (r *LRESTReconciler) createLRESTSVC(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + + log := r.Log.WithValues("createLRESTSVC", req.NamespacedName) + + foundSvc := &corev1.Service{} + err := r.Get(context.TODO(), types.NamespacedName{Name: lrest.Name + "-lrest", Namespace: lrest.Namespace}, foundSvc) + if err != nil && apierrors.IsNotFound(err) { + svc := r.createSvcSpec(lrest) + + log.Info("Creating a new Cluster Service for: "+lrest.Name, "Svc.Namespace", svc.Namespace, "Service.Name", svc.Name) + err := r.Create(ctx, svc) + if err != nil { + log.Error(err, "Failed to create new Cluster Service for: "+lrest.Name, "Svc.Namespace", svc.Namespace, "Service.Name", svc.Name) + return err + } + + log.Info("Created LREST Cluster Service successfully") + r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "CreatedLRESTService", "Created LREST Service for %s", lrest.Name) + } else { + log.Info("LREST Cluster Service already exists") + } + + return nil +} + +/* +*********************** + - Create Service spec + /*********************** +*/ +func (r *LRESTReconciler) createSvcSpec(lrest *dbapi.LREST) *corev1.Service { + + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: lrest.Name + "-lrest", + Namespace: lrest.Namespace, + }, + Spec: corev1.ServiceSpec{ + Selector: map[string]string{ + "name": lrest.Name + "-lrest", + }, + ClusterIP: corev1.ClusterIPNone, + }, + } + // Set LREST instance as the owner and controller + ctrl.SetControllerReference(lrest, svc, r.Scheme) + return svc +} + +/* +************************************************ + - Check LREST deletion + /*********************************************** +*/ + +func (r *LRESTReconciler) manageLRESTDeletion(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + log := r.Log.WithValues("manageLRESTDeletion", req.NamespacedName) + + /* REGISTER FINALIZER */ + if lrest.ObjectMeta.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(lrest, LRESTFinalizer) { + controllerutil.AddFinalizer(lrest, LRESTFinalizer) + if err := r.Update(ctx, lrest); err != nil { + return err + } + } + + } else { + log.Info("lrest mark to be delited") + lrest.Status.Phase = lrestPhaseDelete + lrest.Status.Status = true + r.Status().Update(ctx, lrest) + + if controllerutil.ContainsFinalizer(lrest, LRESTFinalizer) { + + if err := r.DeletePDBS(ctx, req, lrest); err != nil { + log.Info("Cannot delete lrpdbs") + return err + } + + controllerutil.RemoveFinalizer(lrest, LRESTFinalizer) + if err := r.Update(ctx, lrest); err != nil { + return err + } + } + + err := r.deleteLRESTInstance(ctx, req, lrest) + if err != nil { + log.Info("Could not delete LREST Resource", "LREST Name", lrest.Spec.LRESTName, "err", err.Error()) + return err + } + + } + return nil +} + +/* +************************************************ + - Delete LREST Resource + +/*********************************************** +*/ +func (r *LRESTReconciler) deleteLRESTInstance(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + + log := r.Log.WithValues("deleteLRESTInstance", req.NamespacedName) + + k_client, err := kubernetes.NewForConfig(r.Config) + if err != nil { + log.Error(err, "Kubernetes Config Error") + } + + replicaSetName := lrest.Name + "-lrest-rs" + + err = k_client.AppsV1().ReplicaSets(lrest.Namespace).Delete(context.TODO(), replicaSetName, metav1.DeleteOptions{}) + if err != nil { + log.Info("Could not delete ReplicaSet", "RS Name", replicaSetName, "err", err.Error()) + if !strings.Contains(strings.ToUpper(err.Error()), "NOT FOUND") { + return err + } + } else { + log.Info("Successfully deleted LREST ReplicaSet", "RS Name", replicaSetName) + } + + r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "DeletedLRESTReplicaSet", "Deleted LREST ReplicaSet for %s", lrest.Name) + + svcName := lrest.Name + "-lrest" + + err = k_client.CoreV1().Services(lrest.Namespace).Delete(context.TODO(), svcName, metav1.DeleteOptions{}) + if err != nil { + log.Info("Could not delete Service", "Service Name", svcName, "err", err.Error()) + if !strings.Contains(strings.ToUpper(err.Error()), "NOT FOUND") { + return err + } + } else { + r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "DeletedLRESTService", "Deleted LREST Service for %s", lrest.Name) + log.Info("Successfully deleted LREST Service", "Service Name", svcName) + } + + log.Info("Successfully deleted LREST resource", "LREST Name", lrest.Spec.LRESTName) + return nil +} + +/* +************************************************ + - Get Secret Key for a Secret Name + /*********************************************** +*/ +func (r *LRESTReconciler) verifySecrets(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + + log := r.Log.WithValues("verifySecrets", req.NamespacedName) + /* + if err := r.checkSecret(ctx, req, lrest, lrest.Spec.SysAdminPwd.Secret.SecretName); err != nil { + return err + }*/ + if err := r.checkSecret(ctx, req, lrest, lrest.Spec.LRESTAdminUser.Secret.SecretName); err != nil { + return err + } + if err := r.checkSecret(ctx, req, lrest, lrest.Spec.LRESTAdminPwd.Secret.SecretName); err != nil { + return err + } + /* + if err := r.checkSecret(ctx, req, lrest, lrest.Spec.LRESTPwd.Secret.SecretName); err != nil { + return err + }*/ + if err := r.checkSecret(ctx, req, lrest, lrest.Spec.WebLrestServerUser.Secret.SecretName); err != nil { + return err + } + if err := r.checkSecret(ctx, req, lrest, lrest.Spec.WebLrestServerPwd.Secret.SecretName); err != nil { + return err + } + + lrest.Status.Msg = "" + log.Info("Verified secrets successfully") + return nil +} + +/* +************************************************ + - Get Secret Key for a Secret Name + /*********************************************** +*/ +func (r *LRESTReconciler) checkSecret(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST, secretName string) error { + + log := r.Log.WithValues("checkSecret", req.NamespacedName) + + secret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: secretName, Namespace: lrest.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + secretName) + lrest.Status.Msg = "Secret not found:" + secretName + return err + } + log.Error(err, "Unable to get the secret.") + return err + } + + return nil +} + +/* +************************************************ + - Delete Secrets + /*********************************************** +*/ +func (r *LRESTReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) { + + log := r.Log.WithValues("deleteSecrets", req.NamespacedName) + + log.Info("Deleting LREST secrets") + secret := &corev1.Secret{} + /* + err := r.Get(ctx, types.NamespacedName{Name: lrest.Spec.SysAdminPwd.Secret.SecretName, Namespace: lrest.Namespace}, secret) + if err == nil { + err := r.Delete(ctx, secret) + if err == nil { + log.Info("Deleted the secret : " + lrest.Spec.SysAdminPwd.Secret.SecretName) + } + } + */ + + err := r.Get(ctx, types.NamespacedName{Name: lrest.Spec.LRESTAdminUser.Secret.SecretName, Namespace: lrest.Namespace}, secret) + if err == nil { + err := r.Delete(ctx, secret) + if err == nil { + log.Info("Deleted the secret : " + lrest.Spec.LRESTAdminUser.Secret.SecretName) + } + } + + err = r.Get(ctx, types.NamespacedName{Name: lrest.Spec.LRESTAdminPwd.Secret.SecretName, Namespace: lrest.Namespace}, secret) + if err == nil { + err := r.Delete(ctx, secret) + if err == nil { + log.Info("Deleted the secret : " + lrest.Spec.LRESTAdminPwd.Secret.SecretName) + } + } + /* + err = r.Get(ctx, types.NamespacedName{Name: lrest.Spec.LRESTPwd.Secret.SecretName, Namespace: lrest.Namespace}, secret) + if err == nil { + err := r.Delete(ctx, secret) + if err == nil { + log.Info("Deleted the secret : " + lrest.Spec.LRESTPwd.Secret.SecretName) + } + } + */ + + err = r.Get(ctx, types.NamespacedName{Name: lrest.Spec.WebLrestServerUser.Secret.SecretName, Namespace: lrest.Namespace}, secret) + if err == nil { + err := r.Delete(ctx, secret) + if err == nil { + log.Info("Deleted the secret : " + lrest.Spec.WebLrestServerUser.Secret.SecretName) + } + } + + err = r.Get(ctx, types.NamespacedName{Name: lrest.Spec.WebLrestServerPwd.Secret.SecretName, Namespace: lrest.Namespace}, secret) + if err == nil { + err := r.Delete(ctx, secret) + if err == nil { + log.Info("Deleted the secret : " + lrest.Spec.WebLrestServerPwd.Secret.SecretName) + } + } +} + +/* +************************************************************* + - SetupWithManager sets up the controller with the Manager. + /************************************************************ +*/ +func (r *LRESTReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbapi.LREST{}). + Owns(&appsv1.ReplicaSet{}). //Watch for deleted RS owned by this controller + WithEventFilter(predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + // Ignore updates to CR status in which case metadata.Generation does not change + return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() + }, + DeleteFunc: func(e event.DeleteEvent) bool { + // Evaluates to false if the object has been confirmed deleted. + //return !e.DeleteStateUnknown + return false + }, + }). + WithOptions(controller.Options{MaxConcurrentReconciles: 100}). + Complete(r) +} + +func securityContextDefine() *corev1.SecurityContext { + return &corev1.SecurityContext{ + RunAsNonRoot: &[]bool{true}[0], + RunAsUser: &[]int64{54321}[0], + AllowPrivilegeEscalation: &[]bool{false}[0], + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{ + "ALL", + }, + }, + } +} + +func (r *LRESTReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + log := r.Log.WithValues("DeletePDBS", req.NamespacedName) + + /* =================== DELETE CASCADE ================ */ + if lrest.Spec.DeletePDBCascade == true { + log.Info("DELETE PDB CASCADE OPTION") + lrpdbList := &dbapi.LRPDBList{} + listOpts := []client.ListOption{} + err := r.List(ctx, lrpdbList, listOpts...) + if err != nil { + log.Info("Failed to get the list of pdbs") + } + + if err == nil { + for _, pdbitem := range lrpdbList.Items { + log.Info("pdbitem.Spec.CDBName:" + pdbitem.Spec.CDBName) + log.Info("lrest.Spec.LRESTName:" + lrest.Spec.LRESTName) + if pdbitem.Spec.CDBName == lrest.Spec.LRESTName { + fmt.Printf("DEVPHASE: Call Delete function for %s %s\n", pdbitem.Name, pdbitem.Spec.LRPDBName) + + var objmap map[string]interface{} /* Used for the return payload */ + values := map[string]string{ + "state": "CLOSE", + "modifyOption": "ABORT", + } + + url := "https://" + pdbitem.Spec.CDBResName + "-lrest." + pdbitem.Spec.CDBNamespace + ":" + strconv.Itoa(lrest.Spec.LRESTPort) + "/database/pdbs/" + pdbitem.Spec.LRPDBName + + log.Info("callAPI(URL):" + url) + log.Info("pdbitem.Status.OpenMode" + pdbitem.Status.OpenMode) + + if pdbitem.Status.OpenMode != "MOUNTED" { + + log.Info("Force pdb closure") + respData, errapi := NewCallLAPI(r, ctx, req, &pdbitem, url, values, "POST") + + if err := json.Unmarshal([]byte(respData), &objmap); err != nil { + log.Error(err, "failed to get respData from callAPI", "err", err.Error()) + return err + } + + pdbitem.Status.SqlCode = int(objmap["sqlcode"].(float64)) + log.Info("pdb closuer.......:", "sqlcode", pdbitem.Status.SqlCode) + + if errapi != nil { + log.Error(err, "callAPI cannot close pdb "+pdbitem.Spec.LRPDBName, "err", err.Error()) + return err + } + + r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "close pdb", "pdbname=%s", pdbitem.Spec.LRPDBName) + } + + /* start dropping pdb */ + log.Info("Drop pluggable database") + values = map[string]string{ + "action": "INCLUDING", + } + respData, errapi := NewCallLAPI(r, ctx, req, &pdbitem, url, values, "DELETE") + + if err := json.Unmarshal([]byte(respData), &objmap); err != nil { + log.Error(err, "failed to get respData from callAPI", "err", err.Error()) + return err + } + + pdbitem.Status.SqlCode = int(objmap["sqlcode"].(float64)) + log.Info(".......:", "sqlcode", pdbitem.Status.SqlCode) + + if errapi != nil { + log.Error(err, "callAPI cannot drop pdb "+pdbitem.Spec.LRPDBName, "err", err.Error()) + return err + } + r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "drop pdb", "pdbname=%s", pdbitem.Spec.LRPDBName) + + /* + if controllerutil.ContainsFinalizer(&pdbitem, LRPDBFinalizer) { + log.Info("Removing finalizer") + controllerutil.RemoveFinalizer(&pdbitem, LRPDBFinalizer) + err = r.Update(ctx, &pdbitem) + if err != nil { + log.Info("Could not remove finalizer", "err", err.Error()) + return err + } + } + */ + + err = r.Delete(context.Background(), &pdbitem, client.GracePeriodSeconds(1)) + if err != nil { + log.Info("Could not delete LRPDB resource", "err", err.Error()) + return err + } + + } /* check pdb name */ + } /* end of loop */ + } + + } + /* ================================================ */ + return nil +} diff --git a/controllers/database/lrpdb_controller.go b/controllers/database/lrpdb_controller.go new file mode 100644 index 00000000..1aadf65b --- /dev/null +++ b/controllers/database/lrpdb_controller.go @@ -0,0 +1,2381 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "bytes" + "context" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + + //"encoding/pem" + "errors" + "fmt" + "io/ioutil" + "net/http" + "regexp" + "strconv" + "strings" + "time" + + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" + "github.com/oracle/oracle-database-operator/commons/k8s" + lrcommons "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" + + //metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +// Bitmask functions +const ( + MPAPPL = 0x00000001 /* The map config has been applyed */ + MPSYNC = 0x00000002 /* The map config is in sync with v$parameters where is default=flase */ + MPEMPT = 0x00000004 /* The map is empty - not specify */ + MPWARN = 0x00000008 /* Map applied with warnings */ + MPINIT = 0x00000010 /* Config map init */ + SPARE3 = 0x00000020 +) + +func bis(bitmask int, bitval int) int { + bitmask = ((bitmask) | (bitval)) + return bitmask +} + +func bit(bitmask int, bitval int) bool { + if bitmask&bitval != 0 { + return true + } else { + return false + } +} + +func bid(bitmask int, bitval int) int { + bitmask ^= ((bitval) & (bitmask)) + return bitmask +} + +func bitmaskprint(bitmask int) string { + BitRead := "|" + if bit(bitmask, MPAPPL) { + BitRead = strings.Join([]string{BitRead, "MPAPPL|"}, "") + } + if bit(bitmask, MPSYNC) { + BitRead = strings.Join([]string{BitRead, "MPSYNC|"}, "") + } + if bit(bitmask, MPEMPT) { + BitRead = strings.Join([]string{BitRead, "MPEMPT|"}, "") + } + if bit(bitmask, MPWARN) { + BitRead = strings.Join([]string{BitRead, "MPWARN|"}, "") + } + if bit(bitmask, MPINIT) { + BitRead = strings.Join([]string{BitRead, "MPINIT|"}, "") + } + if bit(bitmask, SPARE3) { + BitRead = strings.Join([]string{BitRead, "SPARE3|"}, "") + } + + return BitRead +} + +// LRPDBReconciler reconciles a LRPDB object +type LRPDBReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + Interval time.Duration + Recorder record.EventRecorder +} + +type restSQLCollection struct { + Env struct { + DefaultTimeZone string `json:"defaultTimeZone,omitempty"` + } `json:"env"` + Items []SQL_Item `json:"items"` +} + +type SQL_Item struct { + StatementId int `json:"statementId,omitempty"` + Response []string `json:"response"` + ErrorCode int `json:"errorCode,omitempty"` + ErrorLine int `json:"errorLine,omitempty"` + ErrorColumn int `json:"errorColumn,omitempty"` + ErrorDetails string `json:"errorDetails,omitempty"` + Result int `json:"result,omitempty"` +} + +type LRESTError struct { + Code string `json:"code,omitempty"` + Message string `json:"message,omitempty"` + Type string `json:"type,omitempty"` + Instance string `json:"instance,omitempty"` +} + +var ( + lrpdbPhaseCreate = "Creating" + lrpdbPhasePlug = "Plugging" + lrpdbPhaseUnplug = "Unplugging" + lrpdbPhaseClone = "Cloning" + lrpdbPhaseFinish = "Finishing" + lrpdbPhaseReady = "Ready" + lrpdbPhaseDelete = "Deleting" + lrpdbPhaseModify = "Modifying" + lrpdbPhaseMap = "Mapping" + lrpdbPhaseStatus = "CheckingState" + lrpdbPhaseFail = "Failed" + lrpdbPhaseAlterPlug = "AlterPlugDb" + lrpdbPhaseSpare = "NoAction" +) + +const LRPDBFinalizer = "database.oracle.com/LRPDBfinalizer" + +var tde_Password string +var tde_Secret string +var flood_control bool = false +var assertiveLpdbDeletion bool = false /* Global variable for assertive pdb deletion */ +/* + We need to record the config map name after pdb creation + in order to use it during open and clone op if config map + name is not set the open and clone yaml file +*/ +var globalconfigmap string +var globalsqlcode int + +/* mind https://github.com/kubernetes-sigs/kubebuilder/issues/549 */ +//+kubebuilder:rbac:groups=database.oracle.com,resources=lrpdbs,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=database.oracle.com,resources=events,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=database.oracle.com,resources=lrpdbs/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=database.oracle.com,resources=lrpdbs/finalizers,verbs=get;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the LRPDB object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile +func (r *LRPDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.Log.WithValues("multitenantoperator", req.NamespacedName) + log.Info("Reconcile requested") + + reconcilePeriod := r.Interval * time.Second + requeueY := ctrl.Result{Requeue: true, RequeueAfter: reconcilePeriod} + requeueN := ctrl.Result{} + + var err error + lrpdb := &dbapi.LRPDB{} + + // Execute for every reconcile + defer func() { + //log.Info("DEFER LRPDB", "Name", lrpdb.Name, "Phase", lrpdb.Status.Phase, "Status", strconv.FormatBool(lrpdb.Status.Status)) + if !lrpdb.Status.Status { + if lrpdb.Status.Phase == lrpdbPhaseReady { + lrpdb.Status.Status = true + } + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + } + }() + + err = r.Client.Get(context.TODO(), req.NamespacedName, lrpdb) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("LRPDB Resource Not found", "Name", lrpdb.Name) + // Request object not found, could have been deleted after reconcile req. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + lrpdb.Status.Status = true + return requeueN, nil + } + // Error reading the object - requeue the req. + return requeueY, err + } + + // Finalizer section + err = r.manageLRPDBDeletion2(ctx, req, lrpdb) + if err != nil { + log.Info("Reconcile queued") + return requeueY, nil + } + + // Check for Duplicate LRPDB + if !lrpdb.Status.Status { + err = r.checkDuplicateLRPDB(ctx, req, lrpdb) + if err != nil { + return requeueN, nil + } + } + + action := strings.ToUpper(lrpdb.Spec.Action) + /* + Bug 36714702 - LREST OPERATOR - POST ALTER PDB OPTION LRPDB STATUS INTERMITTENTLY + SHOWS "WAITING FOR LRPDB PARAMETER TO BE MODIFIED" + introducing additional check to avoid alter system repetition during + reconciliation loop + */ + if lrpdb.Status.Phase == lrpdbPhaseReady { + if (lrpdb.Status.Action != "" || action != "NOACTION") && (action == "ALTER" || action == "MODIFY" || action == "STATUS" || lrpdb.Status.Action != action) { + lrpdb.Status.Status = false + } else { + err = r.getLRPDBState(ctx, req, lrpdb) + if err != nil { + lrpdb.Status.Phase = lrpdbPhaseFail + } else { + lrpdb.Status.Phase = lrpdbPhaseReady + lrpdb.Status.Msg = "Success" + } + r.Status().Update(ctx, lrpdb) + } + } + + if !lrpdb.Status.Status { + r.validatePhase(ctx, req, lrpdb) + phase := lrpdb.Status.Phase + log.Info("LRPDB:", "Name", lrpdb.Name, "Phase", phase, "Status", strconv.FormatBool(lrpdb.Status.Status)) + + switch phase { + case lrpdbPhaseCreate: + err = r.createLRPDB(ctx, req, lrpdb) + case lrpdbPhaseClone: + err = r.cloneLRPDB(ctx, req, lrpdb) + case lrpdbPhasePlug: + err = r.plugLRPDB(ctx, req, lrpdb) + case lrpdbPhaseUnplug: + err = r.unplugLRPDB(ctx, req, lrpdb) + case lrpdbPhaseModify: + err = r.modifyLRPDB(ctx, req, lrpdb) + case lrpdbPhaseDelete: + err = r.deleteLRPDB(ctx, req, lrpdb) + case lrpdbPhaseStatus: + err = r.getLRPDBState(ctx, req, lrpdb) + case lrpdbPhaseMap: + err = r.mapLRPDB(ctx, req, lrpdb) + case lrpdbPhaseFail: + err = r.mapLRPDB(ctx, req, lrpdb) + case lrpdbPhaseAlterPlug: + err = r.alterSystemLRPDB(ctx, req, lrpdb) + default: + log.Info("DEFAULT:", "Name", lrpdb.Name, "Phase", phase, "Status", strconv.FormatBool(lrpdb.Status.Status)) + return requeueN, nil + } + lrpdb.Status.Action = strings.ToUpper(lrpdb.Spec.Action) + if err != nil { + lrpdb.Status.Phase = lrpdbPhaseFail + lrpdb.Status.SqlCode = globalsqlcode + } else { + lrpdb.Status.Phase = lrpdbPhaseReady + lrpdb.Status.Msg = "Success" + } + } + + r.ManageConfigMapForCloningAndPlugin(ctx, req, lrpdb) + lrpdb.Status.BitStatStr = bitmaskprint(lrpdb.Status.Bitstat) + + log.Info("Reconcile completed") + return requeueY, nil +} + +/* +************************************************ + - Validate the LRPDB Spec + /*********************************************** +*/ +func (r *LRPDBReconciler) validatePhase(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) { + + log := r.Log.WithValues("validatePhase", req.NamespacedName) + + action := strings.ToUpper(lrpdb.Spec.Action) + + log.Info("Validating LRPDB phase for: "+lrpdb.Name, "Action", action) + + switch action { + case "CREATE": + lrpdb.Status.Phase = lrpdbPhaseCreate + case "CLONE": + lrpdb.Status.Phase = lrpdbPhaseClone + case "PLUG": + lrpdb.Status.Phase = lrpdbPhasePlug + case "UNPLUG": + lrpdb.Status.Phase = lrpdbPhaseUnplug + case "MODIFY": + lrpdb.Status.Phase = lrpdbPhaseModify + case "DELETE": + lrpdb.Status.Phase = lrpdbPhaseDelete + case "STATUS": + lrpdb.Status.Phase = lrpdbPhaseStatus + case "MAP": + lrpdb.Status.Phase = lrpdbPhaseMap + case "ALTER": + lrpdb.Status.Phase = lrpdbPhaseAlterPlug + case "NOACTION": + lrpdb.Status.Phase = lrpdbPhaseStatus + + } + + log.Info("Validation complete") +} + +/* + This function scans the list of crd + pdb to verify the existence of the + pdb (crd) that we want to clone. + Bug 36752925 - LREST OPERATOR - CLONE NON-EXISTENT + PDB CREATES A LRPDB WITH STATUS FAILED + + return 1 - CRD found + return 0 - CRD not found / Stop clone process + + Bug 36753107 - LREST OPERATOR - CLONE + CLOSED PDB SUCCESSFULLY CLONES + +*/ + +func (r *LRPDBReconciler) checkPDBforCloninig(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, targetPdbName string) (int, error) { + log := r.Log.WithValues("checkDuplicateLRPDB", req.NamespacedName) + var pdbCounter int + pdbCounter = 0 + + lrpdbList := &dbapi.LRPDBList{} + listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingFields{"spec.pdbName": targetPdbName}} + err := r.List(ctx, lrpdbList, listOpts...) + if err != nil { + log.Info("Failed to list lrpdbs", "Namespace", req.Namespace, "Error", err) + return 0, err + } + if len(lrpdbList.Items) == 0 { + log.Info("No pdbs available") + return pdbCounter, err + } + + for _, p := range lrpdbList.Items { + fmt.Printf("DEBUGCLONE %s %s %i\n", p.Spec.LRPDBName, targetPdbName, pdbCounter) + if p.Spec.LRPDBName == targetPdbName { + log.Info("Found " + targetPdbName + " in the crd list") + if p.Status.OpenMode == "MOUNTED" { + log.Info("Cannot clone a mounted pdb") + return pdbCounter, err + } + pdbCounter++ + fmt.Printf("DEBUGCLONE %s %s %i\n", p.Spec.LRPDBName, targetPdbName, pdbCounter) + return pdbCounter, err + } + + } + return pdbCounter, err +} + +/* +*************************************************************** + - Check for Duplicate LRPDB. Same LRPDB name on the same LREST resource. + +/************************************************************** +*/ +func (r *LRPDBReconciler) checkDuplicateLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("checkDuplicateLRPDB", req.NamespacedName) + + // Name of the LREST CR that holds the LREST container + lrestResName := lrpdb.Spec.CDBResName + //lrestame := lrpdb.Spec.LRESTName + + // Name of the LRPDB resource + lrpdbResName := lrpdb.Spec.LRPDBName + + lrpdbList := &dbapi.LRPDBList{} + + listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingFields{"spec.pdbName": lrpdbResName}} + + // List retrieves list of objects for a given namespace and list options. + err := r.List(ctx, lrpdbList, listOpts...) + if err != nil { + log.Info("Failed to list lrpdbs", "Namespace", req.Namespace, "Error", err) + return err + } + + if len(lrpdbList.Items) == 0 { + log.Info("No lrpdbs found for LRPDBName: "+lrpdbResName, "CDBResName", lrestResName) + return nil + } + + for _, p := range lrpdbList.Items { + log.Info("Found LRPDB: " + p.Name) + if (p.Name != lrpdb.Name) && (p.Spec.CDBResName == lrestResName) { + log.Info("Duplicate LRPDB found") + lrpdb.Status.Msg = "LRPDB Resource already exists" + lrpdb.Status.Status = false + lrpdb.Status.Phase = lrpdbPhaseFail + return errors.New("Duplicate LRPDB found") + } + } + return nil +} + +/* +*************************************************************** + - Get the Custom Resource for the LREST mentioned in the LRPDB Spec + /************************************************************** +*/ +func (r *LRPDBReconciler) getLRESTResource(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (dbapi.LREST, error) { + + log := r.Log.WithValues("getLRESTResource", req.NamespacedName) + + var lrest dbapi.LREST // LREST CR corresponding to the LREST name specified in the LRPDB spec + + // Name of the LREST CR that holds the LREST container + lrestResName := lrpdb.Spec.CDBResName + lrestNamespace := lrpdb.Spec.CDBNamespace + + log.Info("lrestResName...........:" + lrestResName) + log.Info("lrestNamespace.........:" + lrestNamespace) + + // Get LREST CR corresponding to the LREST name specified in the LRPDB spec + err := r.Get(context.Background(), client.ObjectKey{ + Namespace: lrestNamespace, + Name: lrestResName, + }, &lrest) + + if err != nil { + log.Info("Failed to get CRD for LREST", "Name", lrestResName, "Namespace", lrestNamespace, "Error", err.Error()) + lrpdb.Status.Msg = "Unable to get CRD for LREST : " + lrestResName + r.Status().Update(ctx, lrpdb) + return lrest, err + } + + log.Info("Found CR for LREST", "Name", lrestResName, "CR Name", lrest.Name) + return lrest, nil +} + +/* +*************************************************************** + - Get the LREST Pod for the LREST mentioned in the LRPDB Spec + /************************************************************** +*/ +func (r *LRPDBReconciler) getLRESTPod(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (corev1.Pod, error) { + + log := r.Log.WithValues("getLRESTPod", req.NamespacedName) + + var lrestPod corev1.Pod // LREST Pod container with connection to the concerned LREST + + // Name of the LREST CR that holds the LREST container + lrestResName := lrpdb.Spec.CDBResName + + // Get LREST Pod associated with the LREST Name specified in the LRPDB Spec + err := r.Get(context.Background(), client.ObjectKey{ + Namespace: req.Namespace, + Name: lrestResName + "-lrest", + }, &lrestPod) + + if err != nil { + log.Info("Failed to get Pod for LREST", "Name", lrestResName, "Namespace", req.Namespace, "Error", err.Error()) + lrpdb.Status.Msg = "Unable to get LREST Pod for LREST : " + lrestResName + return lrestPod, err + } + + log.Info("Found LREST Pod for LREST", "Name", lrestResName, "Pod Name", lrestPod.Name, "LREST Container hostname", lrestPod.Spec.Hostname) + return lrestPod, nil +} + +/* +************************************************ + - Get Secret Key for a Secret Name + /*********************************************** +*/ +func (r *LRPDBReconciler) getSecret(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, secretName string, keyName string) (string, error) { + + log := r.Log.WithValues("getSecret", req.NamespacedName) + + secret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: secretName, Namespace: lrpdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + secretName) + lrpdb.Status.Msg = "Secret not found:" + secretName + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + return string(secret.Data[keyName]), nil +} + +/* +************************************************ + - Issue a REST API Call to the LREST container + /*********************************************** +*/ +func (r *LRPDBReconciler) callAPI(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, url string, payload map[string]string, action string) (string, error) { + log := r.Log.WithValues("callAPI", req.NamespacedName) + + var err error + + secret := &corev1.Secret{} + + err = r.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsKey.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + rsaKeyPEM := secret.Data[lrpdb.Spec.LRPDBTlsKey.Secret.Key] + + err = r.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + rsaCertPEM := secret.Data[lrpdb.Spec.LRPDBTlsCrt.Secret.Key] + + err = r.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCat.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCat.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + caCert := secret.Data[lrpdb.Spec.LRPDBTlsCat.Secret.Key] + /* + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaKeyPEM)) + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaCertPEM)) + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(caCert)) + */ + + certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) + if err != nil { + lrpdb.Status.Msg = "Error tls.X509KeyPair" + return "", err + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + /* + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool} + */ + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool, + //MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + PreferServerCipherSuites: true, + CipherSuites: []uint16{ + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_RSA_WITH_AES_256_CBC_SHA, + }, + } + + tr := &http.Transport{TLSClientConfig: tlsConf} + + httpclient := &http.Client{Transport: tr} + + log.Info("Issuing REST call", "URL", url, "Action", action) + + webUser, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.WebLrpdbServerUser.Secret.SecretName, lrpdb.Spec.WebLrpdbServerUser.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) + if err != nil { + log.Error(err, "Unable to get webuser account name ") + return "", err + } + + webUserPwd, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.WebLrpdbServerPwd.Secret.SecretName, lrpdb.Spec.WebLrpdbServerPwd.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) + if err != nil { + log.Error(err, "Unable to get webuser account password ") + return "", err + } + + var httpreq *http.Request + if action == "GET" { + httpreq, err = http.NewRequest(action, url, nil) + } else { + jsonValue, _ := json.Marshal(payload) + httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) + } + + if err != nil { + log.Info("Unable to create HTTP Request for LRPDB : "+lrpdb.Name, "err", err.Error()) + return "", err + } + + httpreq.Header.Add("Accept", "application/json") + httpreq.Header.Add("Content-Type", "application/json") + httpreq.SetBasicAuth(webUser, webUserPwd) + + resp, err := httpclient.Do(httpreq) + if err != nil { + errmsg := err.Error() + log.Error(err, "Failed - Could not connect to LREST Pod", "err", err.Error()) + lrpdb.Status.Msg = "Error: Could not connect to LREST Pod" + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", errmsg) + return "", err + } + + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "Done", lrpdb.Spec.CDBResName) + if resp.StatusCode != http.StatusOK { + bb, _ := ioutil.ReadAll(resp.Body) + + if resp.StatusCode == 404 { + lrpdb.Status.ConnString = "" + lrpdb.Status.Msg = lrpdb.Spec.LRPDBName + " not found" + + } else { + if flood_control == false { + lrpdb.Status.Msg = "LREST Error - HTTP Status Code:" + strconv.Itoa(resp.StatusCode) + } + } + + if flood_control == false { + log.Info("LREST Error - HTTP Status Code :"+strconv.Itoa(resp.StatusCode), "Err", string(bb)) + } + + var apiErr LRESTError + json.Unmarshal([]byte(bb), &apiErr) + if flood_control == false { + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", "Failed: %s", apiErr.Message) + } + fmt.Printf("\n================== APIERR ======================\n") + fmt.Printf("%+v \n", apiErr) + fmt.Printf(string(bb)) + fmt.Printf("URL=%s\n", url) + fmt.Printf("resp.StatusCode=%s\n", strconv.Itoa(resp.StatusCode)) + fmt.Printf("\n================== APIERR ======================\n") + flood_control = true + return "", errors.New("LREST Error") + } + flood_control = false + + defer resp.Body.Close() + + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Print(err.Error()) + } + respData := string(bodyBytes) + fmt.Print("CALL API return msg.....:") + fmt.Println(string(bodyBytes)) + + var apiResponse restSQLCollection + json.Unmarshal([]byte(bodyBytes), &apiResponse) + fmt.Printf("===> %#v\n", apiResponse) + fmt.Printf("===> %+v\n", apiResponse) + + errFound := false + for _, sqlItem := range apiResponse.Items { + if sqlItem.ErrorDetails != "" { + log.Info("LREST Error - Oracle Error Code :" + strconv.Itoa(sqlItem.ErrorCode)) + if !errFound { + lrpdb.Status.Msg = sqlItem.ErrorDetails + } + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "OraError", "%s", sqlItem.ErrorDetails) + errFound = true + } + } + + if errFound { + return "", errors.New("Oracle Error") + } + + return respData, nil +} + +/* +************************************************ + - Create a LRPDB + +*********************************************** +*/ +func (r *LRPDBReconciler) createLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("createLRPDB", req.NamespacedName) + + var err error + var tde_Password string + var tde_Secret string + + log.Info("call getLRESTResource \n") + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err + } + + lrpdbAdminName, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.AdminpdbUser.Secret.SecretName, lrpdb.Spec.AdminpdbUser.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) + if err != nil { + log.Error(err, "Unable to find pdb admin user ") + return err + } + + lrpdbAdminPwd, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.AdminpdbPass.Secret.SecretName, lrpdb.Spec.AdminpdbPass.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) + + if err != nil { + log.Error(err, "Unable to find pdb admin password ") + return err + } + + err = r.getLRPDBState(ctx, req, lrpdb) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Check LRPDB not existence completed", "LRPDB Name", lrpdb.Spec.LRPDBName) + } + + } else { + + lrpdb.Status.Phase = lrpdbPhaseFail + lrpdb.Status.Msg = "PDB " + lrpdb.Spec.LRPDBName + " already exists " + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + log.Info("Database already exists ", "LRPDB Name", lrpdb.Spec.LRPDBName) + err := fmt.Errorf("%v", 65012) + return err + } + + values := map[string]string{ + "method": "CREATE", + "pdb_name": lrpdb.Spec.LRPDBName, + "adminName": lrpdbAdminName, + "adminPwd": lrpdbAdminPwd, + "fileNameConversions": lrpdb.Spec.FileNameConversions, + "reuseTempFile": strconv.FormatBool(*(lrpdb.Spec.ReuseTempFile)), + "unlimitedStorage": strconv.FormatBool(*(lrpdb.Spec.UnlimitedStorage)), + "totalSize": lrpdb.Spec.TotalSize, + "tempSize": lrpdb.Spec.TempSize, + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + + fmt.Printf("===== PAYLOAD ===\n") + fmt.Print(" method ", values["method"], "\n") + fmt.Print(" pdb_name ", values["pdb_name"], "\n") + fmt.Print(" adminName ", values["adminName"], "\n") + fmt.Print(" adminPwd --------------\n") + fmt.Print(" fileNameConversions ", values["fileNameConversions"], "\n") + fmt.Print(" unlimitedStorage ", values["unlimitedStorage"], "\n") + fmt.Print(" reuseTempFile ", values["reuseTempFile"], "\n") + fmt.Print(" tempSize ", values["tempSize"], "\n") + fmt.Print(" totalSize ", values["totalSize"], "\n") + fmt.Print(" getScript ", values["getScript"], "\n") + + if *(lrpdb.Spec.LTDEImport) { + tde_Password, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDEPassword.Secret.SecretName, lrpdb.Spec.LTDEPassword.Secret.Key) + if err != nil { + return err + } + tde_Secret, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDESecret.Secret.SecretName, lrpdb.Spec.LTDESecret.Secret.Key) + if err != nil { + return err + } + + tde_Secret = tde_Secret[:len(tde_Secret)-1] + tde_Password = tde_Secret[:len(tde_Password)-1] + values["tde_Password"] = tde_Password + values["tdeKeystorePath"] = lrpdb.Spec.LTDEKeystorePath + values["tde_Secret"] = tde_Secret + } + + //url := "https://" + lrpdb.Spec.CDBResName + "-lrest:" + strconv.Itoa(lrest.Spec.LRESTPort) + "/database/pdbs/" + url := r.BaseUrl(ctx, req, lrpdb, lrest) + fmt.Print("============================================================\n") + fmt.Print(url) + fmt.Print("\n============================================================\n") + lrpdb.Status.TotalSize = lrpdb.Spec.TotalSize + lrpdb.Status.Phase = lrpdbPhaseCreate + lrpdb.Status.Msg = "Waiting for LRPDB to be created" + + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + if err != nil { + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + if lrpdb.Status.SqlCode != 0 { + err := fmt.Errorf("%v", lrpdb.Status.SqlCode) + return err + } + + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, + "Created", "LRPDB '%s' created successfully", lrpdb.Spec.LRPDBName) + + if lrest.Spec.DBServer != "" { + lrpdb.Status.ConnString = + lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName + } else { + log.Info("Parsing connectstring") + lrpdb.Status.ConnString = lrest.Spec.DBTnsurl + parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) + } + + assertiveLpdbDeletion = lrpdb.Spec.AssertiveLrpdbDeletion + if lrpdb.Spec.AssertiveLrpdbDeletion == true { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Created", "PDB '%s' assertive pdb deletion turned on", lrpdb.Spec.LRPDBName) + } + + r.getLRPDBState(ctx, req, lrpdb) + log.Info("Created LRPDB Resource", "LRPDB Name", lrpdb.Spec.LRPDBName) + + if bit(lrpdb.Status.Bitstat, MPINIT) == false { + r.InitConfigMap(ctx, req, lrpdb) + Cardinality, _ := r.ApplyConfigMap(ctx, req, lrpdb) + log.Info("Config Map Cardinality " + strconv.Itoa(int(Cardinality))) + } + + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + return nil +} + +/* +************************************************ + - Clone a LRPDB + /*********************************************** +*/ +func (r *LRPDBReconciler) cloneLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + if lrpdb.Spec.LRPDBName == lrpdb.Spec.SrcLRPDBName { + return nil + } + + log := r.Log.WithValues("cloneLRPDB", req.NamespacedName) + + globalsqlcode = 0 + var err error + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err + } + + /* Prevent cloning an existing lrpdb */ + err = r.getLRPDBState(ctx, req, lrpdb) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Check LRPDB not existence completed", "LRPDB Name", lrpdb.Spec.LRPDBName) + } + + } else { + log.Info("Database already exists ", "LRPDB Name", lrpdb.Spec.LRPDBName) + return nil + } + + values := map[string]string{ + "method": "CLONE", + "pdb_name": lrpdb.Spec.LRPDBName, + "srcPdbName": lrpdb.Spec.SrcLRPDBName, + "reuseTempFile": strconv.FormatBool(*(lrpdb.Spec.ReuseTempFile)), + "unlimitedStorage": strconv.FormatBool(*(lrpdb.Spec.UnlimitedStorage)), + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + + //* check the existence of lrpdb.Spec.SrcLRPDBName // + var allErrs field.ErrorList + pdbCounter, _ := r.checkPDBforCloninig(ctx, req, lrpdb, lrpdb.Spec.SrcLRPDBName) + if pdbCounter == 0 { + log.Info("target pdb " + lrpdb.Spec.SrcLRPDBName + " does not exists or is not open") + allErrs = append(allErrs, field.NotFound(field.NewPath("Spec").Child("LRPDBName"), " "+lrpdb.Spec.LRPDBName+" does not exist : failure")) + r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + return nil + } + + if lrpdb.Spec.SparseClonePath != "" { + values["sparseClonePath"] = lrpdb.Spec.SparseClonePath + } + if lrpdb.Spec.FileNameConversions != "" { + values["fileNameConversions"] = lrpdb.Spec.FileNameConversions + } + if lrpdb.Spec.TotalSize != "" { + values["totalSize"] = lrpdb.Spec.TotalSize + } + if lrpdb.Spec.TempSize != "" { + values["tempSize"] = lrpdb.Spec.TempSize + } + + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdb.Spec.LRPDBName + "/" + + lrpdb.Status.Phase = lrpdbPhaseClone + lrpdb.Status.Msg = "Waiting for LRPDB to be cloned" + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + if err != nil { + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + + if lrpdb.Status.SqlCode != 0 { + errclone := errors.New("Cannot clone database: ora-" + strconv.Itoa(lrpdb.Status.SqlCode)) + log.Info("Cannot clone database ora-" + strconv.Itoa(lrpdb.Status.SqlCode)) + lrpdb.Status.Msg = lrpdb.Spec.SrcLRPDBName + " is open in mount cannot clone " + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + return errclone + } + + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Created", "LRPDB '%s' cloned successfully", lrpdb.Spec.LRPDBName) + + if lrest.Spec.DBServer != "" { + lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName + } else { + lrpdb.Status.ConnString = lrest.Spec.DBTnsurl + parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) + + } + assertiveLpdbDeletion = lrpdb.Spec.AssertiveLrpdbDeletion + if lrpdb.Spec.AssertiveLrpdbDeletion == true { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Clone", "PDB '%s' assertive pdb deletion turned on", lrpdb.Spec.LRPDBName) + } + + log.Info("Cloned LRPDB successfully", "Source LRPDB Name", lrpdb.Spec.SrcLRPDBName, "Clone LRPDB Name", lrpdb.Spec.LRPDBName) + r.getLRPDBState(ctx, req, lrpdb) + return nil +} + +/* +************************************************ + - Plug a LRPDB + +*********************************************** +*/ +func (r *LRPDBReconciler) plugLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("plugLRPDB", req.NamespacedName) + globalsqlcode = 0 + + var err error + var tde_Password string + var tde_Secret string + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err + } + + values := map[string]string{ + "method": "PLUG", + "xmlFileName": lrpdb.Spec.XMLFileName, + "pdb_name": lrpdb.Spec.LRPDBName, + "sourceFileNameConversions": lrpdb.Spec.SourceFileNameConversions, + "copyAction": lrpdb.Spec.CopyAction, + "fileNameConversions": lrpdb.Spec.FileNameConversions, + "unlimitedStorage": strconv.FormatBool(*(lrpdb.Spec.UnlimitedStorage)), + "reuseTempFile": strconv.FormatBool(*(lrpdb.Spec.ReuseTempFile)), + "totalSize": lrpdb.Spec.TotalSize, + "tempSize": lrpdb.Spec.TempSize, + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + + if *(lrpdb.Spec.LTDEImport) { + tde_Password, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDEPassword.Secret.SecretName, lrpdb.Spec.LTDEPassword.Secret.Key) + if err != nil { + return err + } + tde_Secret, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDESecret.Secret.SecretName, lrpdb.Spec.LTDESecret.Secret.Key) + if err != nil { + return err + } + + tde_Secret = tde_Secret[:len(tde_Secret)-1] + tde_Password = tde_Secret[:len(tde_Password)-1] + values["tde_Password"] = tde_Password + values["tdeKeystorePath"] = lrpdb.Spec.LTDEKeystorePath + values["tde_Secret"] = tde_Secret + values["tdeImport"] = strconv.FormatBool(*(lrpdb.Spec.LTDEImport)) + } + if *(lrpdb.Spec.AsClone) { + values["asClone"] = strconv.FormatBool(*(lrpdb.Spec.AsClone)) + } + + url := r.BaseUrl(ctx, req, lrpdb, lrest) + + lrpdb.Status.TotalSize = lrpdb.Spec.TotalSize + lrpdb.Status.Phase = lrpdbPhasePlug + lrpdb.Status.Msg = "Waiting for LRPDB to be plugged" + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + if err != nil { + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + + if lrpdb.Status.SqlCode != 0 { + log.Info("Plug database failure........:" + strconv.Itoa(lrpdb.Status.SqlCode)) + err = fmt.Errorf("%v", lrpdb.Status.SqlCode) + return err + } + + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Created", "LRPDB '%s' plugged successfully", lrpdb.Spec.LRPDBName) + + if lrest.Spec.DBServer != "" { + lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName + } else { + log.Info("Parsing connectstring") + lrpdb.Status.ConnString = lrest.Spec.DBTnsurl + parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) + } + + assertiveLpdbDeletion = lrpdb.Spec.AssertiveLrpdbDeletion + if lrpdb.Spec.AssertiveLrpdbDeletion == true { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Plug", "PDB '%s' assertive pdb deletion turned on", lrpdb.Spec.LRPDBName) + } + + log.Info("Successfully plugged LRPDB", "LRPDB Name", lrpdb.Spec.LRPDBName) + r.getLRPDBState(ctx, req, lrpdb) + return nil +} + +/* +************************************************ + - Unplug a LRPDB + +*********************************************** +*/ +func (r *LRPDBReconciler) unplugLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("unplugLRPDB", req.NamespacedName) + globalsqlcode = 0 + + var err error + var tde_Password string + var tde_Secret string + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err + } + + values := map[string]string{ + "method": "UNPLUG", + "xmlFileName": lrpdb.Spec.XMLFileName, + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + + if *(lrpdb.Spec.LTDEExport) { + // Get the TDE Password + tde_Password, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDEPassword.Secret.SecretName, lrpdb.Spec.LTDEPassword.Secret.Key) + if err != nil { + return err + } + tde_Secret, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDESecret.Secret.SecretName, lrpdb.Spec.LTDESecret.Secret.Key) + if err != nil { + return err + } + + tde_Secret = tde_Secret[:len(tde_Secret)-1] + tde_Password = tde_Secret[:len(tde_Password)-1] + values["tde_Password"] = tde_Password + values["tdeKeystorePath"] = lrpdb.Spec.LTDEKeystorePath + values["tde_Secret"] = tde_Secret + values["tdeExport"] = strconv.FormatBool(*(lrpdb.Spec.LTDEExport)) + } + + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdb.Spec.LRPDBName + "/" + + log.Info("CallAPI(url)", "url", url) + lrpdb.Status.Phase = lrpdbPhaseUnplug + lrpdb.Status.Msg = "Waiting for LRPDB to be unplugged" + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + if err != nil { + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + + if lrpdb.Status.SqlCode != 0 { + globalsqlcode = lrpdb.Status.SqlCode + + lrpdb.Status.Msg = lrpdb.Spec.LRPDBName + " database cannot be unplugged " + log.Info(lrpdb.Spec.LRPDBName + " database cannot be unplugged ") + if lrpdb.Status.SqlCode == 65170 { + log.Info(lrpdb.Spec.XMLFileName + " xml file already exists ") + } + + /* + err := r.Update(ctx, lrpdb) + if err != nil { + log.Info("Fail to update crd", "err", err.Error()) + return err + } + + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status"+lrpdb.Name, "err", err.Error()) + return err + } + */ + + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Unplugged", " ORA-%s ", strconv.Itoa(lrpdb.Status.SqlCode)) + err = fmt.Errorf("%v", lrpdb.Status.SqlCode) + return err + } + + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + log.Info("Removing finalizer") + controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) + err = r.Update(ctx, lrpdb) + if err != nil { + log.Info("Could not remove finalizer", "err", err.Error()) + return err + } + lrpdb.Status.Status = true + err = r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + if err != nil { + log.Info("Could not delete LRPDB resource", "err", err.Error()) + return err + } + } + + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Unplugged", "LRPDB '%s' unplugged successfully", lrpdb.Spec.LRPDBName) + globalsqlcode = 0 + log.Info("Successfully unplugged LRPDB resource") + return nil +} + +/************************************************** +Alter system LRPDB +**************************************************/ + +/**just push the trasnsaction **/ +func (r *LRPDBReconciler) alterSystemLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("alterSystemLRPDB", req.NamespacedName) + globalsqlcode = 0 + + var err error + err = r.getLRPDBState(ctx, req, lrpdb) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Warning LRPDB does not exist", "LRPDB Name", lrpdb.Spec.LRPDBName) + return nil + } + return err + } + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + log.Info("Cannot find LREST server") + return err + } + + /* alter system payload */ + + values := map[string]string{ + "state": "ALTER", + "alterSystemParameter": lrpdb.Spec.AlterSystemParameter, + "alterSystemValue": lrpdb.Spec.AlterSystemValue, + "parameterScope": lrpdb.Spec.ParameterScope, + } + + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + log.Info("alter system payload...:", "lrpdb.Spec.AlterSystemValue=", lrpdb.Spec.AlterSystemValue) + log.Info("alter system payload...:", "lrpdb.Spec.AlterSystemParameter=", lrpdb.Spec.AlterSystemParameter) + log.Info("alter system payload...:", "lrpdb.Spec.ParameterScope=", lrpdb.Spec.ParameterScope) + log.Info("alter system path.......:", "url=", url) + + lrpdb.Status.Phase = lrpdbPhaseAlterPlug + lrpdb.Status.ModifyOption = lrpdb.Spec.AlterSystem + " " + lrpdb.Spec.ParameterScope + lrpdb.Status.Msg = "Waiting for LRPDB parameter to be modified" + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update lrpdb parameter :"+lrpdb.Name, "err", err.Error()) + return err + } + + respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + if err != nil { + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + + if lrpdb.Status.SqlCode == 0 { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Altered", "LRPDB(name,cmd,sqlcode) '%s %s %d' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.AlterSystem, lrpdb.Status.SqlCode) + lrpdb.Status.Phase = lrpdbPhaseReady + lrpdb.Spec.Action = "Noaction" + lrpdb.Status.Action = "Noaction" + lrpdb.Status.Status = true + + if err := r.Update(ctx, lrpdb); err != nil { + log.Error(err, "Cannot rest lrpdb Spec :"+lrpdb.Name, "err", err.Error()) + return err + } + + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update lrpdb parameter :"+lrpdb.Name, "err", err.Error()) + return err + } + return nil + + } + + if lrpdb.Status.SqlCode != 0 { + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "alter system failure", "LRPDB(name,cmd,sqlcode) '%s %s %d' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.AlterSystem, lrpdb.Status.SqlCode) + erralter := errors.New("Error: cannot modify parameter") + + lrpdb.Status.ModifyOption = lrpdb.Spec.AlterSystem + " " + lrpdb.Spec.ParameterScope + lrpdb.Status.Msg = "Failed: cannot modify system parameter" + lrpdb.Status.Phase = lrpdbPhaseStatus + lrpdb.Spec.AlterSystem = "" + lrpdb.Spec.ParameterScope = "" + lrpdb.Spec.Action = "Noaction" + if err := r.Update(ctx, lrpdb); err != nil { + log.Error(err, "Cannot rest lrpdb Spec :"+lrpdb.Name, "err", err.Error()) + return err + } + + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update lrpdb parameter :"+lrpdb.Name, "err", err.Error()) + return err + } + return erralter + } + + lrpdb.Status.Status = false + + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update lrpdb parameter :"+lrpdb.Name, "err", err.Error()) + return err + } + return nil +} + +/************************************************* + * Modify a LRPDB state + ***********************************************/ +func (r *LRPDBReconciler) modifyLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("modifyLRPDB", req.NamespacedName) + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Modify", "Info:'%s %s %s' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.LRPDBState, lrpdb.Status.ModifyOption) + + var err error + err = r.getLRPDBState(ctx, req, lrpdb) + if err != nil { + if lrpdb.Status.SqlCode == 1403 { + // BUG 36752465 + // We have to handle to verify a non existings results using both + log.Info("Database does not exists ") + r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + return nil + } + if apierrors.IsNotFound(err) { + log.Info("Warning LRPDB does not exist", "LRPDB Name", lrpdb.Spec.LRPDBName) + r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + return nil + } + return err + } + + /* This scenario is managed by webhook acceptance test ... leave it here anyway */ + if lrpdb.Status.OpenMode == "READ WRITE" && lrpdb.Spec.LRPDBState == "OPEN" && lrpdb.Spec.ModifyOption == "READ WRITE" { + /* Database is already open no action required */ + return nil + } + + if lrpdb.Status.OpenMode == "MOUNTED" && lrpdb.Spec.LRPDBState == "CLOSE" && lrpdb.Spec.ModifyOption == "IMMEDIATE" { + /* Database is already close no action required */ + return nil + } + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err + } + + values := map[string]string{} + if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { + values = map[string]string{ + "state": lrpdb.Spec.LRPDBState, + "modifyOption": lrpdb.Spec.ModifyOption, + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { + log.Info("MODIFY LRPDB", "lrpdb.Spec.LRPDBState=", lrpdb.Spec.LRPDBState, "lrpdb.Spec.ModifyOption=", lrpdb.Spec.ModifyOption) + log.Info("LRPDB STATUS OPENMODE", "lrpdb.Status.OpenMode=", lrpdb.Status.OpenMode) + } + } + + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" + + lrpdb.Status.Phase = lrpdbPhaseModify + if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { + lrpdb.Status.ModifyOption = lrpdb.Spec.LRPDBState + "-" + lrpdb.Spec.ModifyOption + } + + lrpdb.Status.Msg = "Waiting for LRPDB to be modified" + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + if err != nil { + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + + if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Modified", " '%s' modified successfully '%s'", lrpdb.Spec.LRPDBName, lrpdb.Spec.LRPDBState) + } + + if lrest.Spec.DBServer != "" { + lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName + } else { + lrpdb.Status.ConnString = lrest.Spec.DBTnsurl + parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) + + } + + lrpdb.Status.Msg = "alter lrpdb completed" + lrpdb.Status.Status = false + lrpdb.Status.Phase = lrpdbPhaseReady + + log.Info("Successfully modified LRPDB state", "LRPDB Name", lrpdb.Spec.LRPDBName) + + /* After database openining we reapply the config map if warning is present */ + if lrpdb.Spec.LRPDBState == "OPEN" { + if bit(lrpdb.Status.Bitstat, MPWARN|MPINIT) { + log.Info("re-apply config map") + r.ApplyConfigMap(ctx, req, lrpdb) + + } + } + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + //r.getLRPDBState(ctx, req, lrpdb) + return nil +} + +/* +************************************************ + - Get LRPDB State + /*********************************************** +*/ +func (r *LRPDBReconciler) getLRPDBState(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("getLRPDBState", req.NamespacedName) + + var err error + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err + } + + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" + + lrpdb.Status.Msg = "Getting LRPDB state" + fmt.Print("============================\n") + fmt.Println(lrpdb.Status) + fmt.Print("============================\n") + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, nil, "GET") + if err != nil { + log.Info("Begin respData") + log.Info(respData) + log.Info("End respData") + lrpdb.Status.Msg = "getLRPDBState failure : check lrpdb status" + lrpdb.Status.Status = false + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + + if lrpdb.Status.SqlCode == 1403 { + lrpdb.Status.OpenMode = "unknown" + lrpdb.Status.Msg = "check lrpdb status" + lrpdb.Status.Status = false + return errors.New("NO_DATA_FOUND") + } + + var objmap map[string]interface{} + if err := json.Unmarshal([]byte(respData), &objmap); err != nil { + log.Error(err, "Failed to get state of LRPDB :"+lrpdbName, "err", err.Error()) + } + lrpdb.Status.OpenMode = objmap["open_mode"].(string) + + /* if lrpdb.Status.Phase == lrpdbPhaseCreate && sqlcode == 1403 { + + if lrpdb.Status.OpenMode == "READ WRITE" { + err := r.mapLRPDB(ctx, req, lrpdb) + if err != nil { + log.Info("Fail to Map resource getting LRPDB state") + } + } + + if lrpdb.Status.OpenMode == "MOUNTED" { + err := r.mapLRPDB(ctx, req, lrpdb) + if err != nil { + log.Info("Fail to Map resource getting LRPDB state") + } + } + }*/ + + lrpdb.Status.Msg = "check lrpdb ok" + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + log.Info("Successfully obtained LRPDB state", "LRPDB Name", lrpdb.Spec.LRPDBName, "State", objmap["open_mode"].(string)) + return nil +} + +/* +************************************************ + - Map Database LRPDB to Kubernetes LRPDB CR + +/*********************************************** +*/ +func (r *LRPDBReconciler) mapLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("mapLRPDB", req.NamespacedName) + + var err error + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err + } + + log.Info("callapi get to map lrpdb") + + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + log.Info("DEBUG NEW URL " + url) + + respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, nil, "GET") + if err != nil { + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + return err + } + + var objmap map[string]interface{} + if err := json.Unmarshal([]byte(respData), &objmap); err != nil { + log.Error(err, "Failed json.Unmarshal :"+lrpdbName, "err", err.Error()) + } + + //fmt.Printf("%+v\n", objmap) + totSizeInBytes := objmap["total_size"].(float64) + totSizeInGB := totSizeInBytes / 1024 / 1024 / 1024 + + lrpdb.Status.OpenMode = objmap["open_mode"].(string) + lrpdb.Status.TotalSize = fmt.Sprintf("%4.2f", totSizeInGB) + "G" + assertiveLpdbDeletion = lrpdb.Spec.AssertiveLrpdbDeletion + if lrpdb.Spec.AssertiveLrpdbDeletion == true { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Map", "PDB '%s' assertive pdb deletion turned on", lrpdb.Spec.LRPDBName) + } + + if lrest.Spec.DBServer != "" { + lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName + } else { + lrpdb.Status.ConnString = lrest.Spec.DBTnsurl + parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) + } + + lrpdb.Status.Phase = lrpdbPhaseReady + + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + log.Info("Successfully mapped LRPDB to Kubernetes resource", "LRPDB Name", lrpdb.Spec.LRPDBName) + lrpdb.Status.Status = true + return nil +} + +/* +************************************************ + - Delete a LRPDB + /*********************************************** +*/ +func (r *LRPDBReconciler) deleteLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("deleteLRPDB", req.NamespacedName) + + errstate := r.getLRPDBState(ctx, req, lrpdb) + if errstate != nil { + if lrpdb.Status.SqlCode == 1403 { + // BUG 36752336: + log.Info("Database does not exists ") + r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + return nil + } + if apierrors.IsNotFound(errstate) { + log.Info("Warning LRPDB does not exist", "LRPDB Name", lrpdb.Spec.LRPDBName) + r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + return nil + } + log.Error(errstate, "Failed to update status for :"+lrpdb.Name, "err", errstate.Error()) + return errstate + //* if the pdb does not exists delete the crd *// + + } + + if lrpdb.Status.OpenMode == "READ WRITE" { + + errdel := errors.New("pdb is open cannot delete it") + log.Info("LRPDB is open in read write cannot drop ") + lrpdb.Status.Msg = "LRPDB is open in read write cannot drop " + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + return errdel + } + + err := r.deleteLRPDBInstance(req, ctx, lrpdb) + if err != nil { + log.Info("Could not delete LRPDB", "LRPDB Name", lrpdb.Spec.LRPDBName, "err", err.Error()) + return err + } + + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + log.Info("Removing finalizer") + controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) + err := r.Update(ctx, lrpdb) + if err != nil { + log.Info("Could not remove finalizer", "err", err.Error()) + return err + } + lrpdb.Status.Status = true + err = r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + if err != nil { + log.Info("Could not delete LRPDB resource", "err", err.Error()) + return err + } + } + + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Deleted", "LRPDB '%s' dropped successfully", lrpdb.Spec.LRPDBName) + + log.Info("Successfully deleted LRPDB resource") + return nil +} + +/* +************************************************ + - Check LRPDB deletion + /*********************************************** +*/ +func (r *LRPDBReconciler) manageLRPDBDeletion(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("manageLRPDBDeletion", req.NamespacedName) + + // Check if the LRPDB instance is marked to be deleted, which is + // indicated by the deletion timestamp being set. + isLRPDBMarkedToBeDeleted := lrpdb.GetDeletionTimestamp() != nil + if isLRPDBMarkedToBeDeleted { + log.Info("Marked to be deleted") + lrpdb.Status.Phase = lrpdbPhaseDelete + lrpdb.Status.Status = true + r.Status().Update(ctx, lrpdb) + + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + // Remove LRPDBFinalizer. Once all finalizers have been + // removed, the object will be deleted. + log.Info("Removing finalizer") + controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) + err := r.Update(ctx, lrpdb) + if err != nil { + log.Info("Could not remove finalizer", "err", err.Error()) + return err + } + log.Info("Successfully removed LRPDB resource") + return nil + } + } + + // Add finalizer for this CR + if !controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + log.Info("Adding finalizer") + controllerutil.AddFinalizer(lrpdb, LRPDBFinalizer) + err := r.Update(ctx, lrpdb) + if err != nil { + log.Info("Could not add finalizer", "err", err.Error()) + return err + } + lrpdb.Status.Status = false + } + return nil +} + +/* +************************************************ + - Finalization logic for LRPDBFinalizer + +*********************************************** +*/ +func (r *LRPDBReconciler) deleteLRPDBInstance(req ctrl.Request, ctx context.Context, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("deleteLRPDBInstance", req.NamespacedName) + + var err error + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err + } + + values := map[string]string{ + "action": "KEEP", + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + + if lrpdb.Spec.DropAction != "" { + values["action"] = lrpdb.Spec.DropAction + } + + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/" + + lrpdb.Status.Phase = lrpdbPhaseDelete + lrpdb.Status.Msg = "Waiting for LRPDB to be deleted" + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "DELETE") + if err != nil { + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + + log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdbName) + return nil +} + +/* +*********************************************************** + - SetupWithManager sets up the controller with the Manager + +************************************************************ +*/ +func (r *LRPDBReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbapi.LRPDB{}). + WithEventFilter(predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + // Ignore updates to CR status in which case metadata.Generation does not change + return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() + }, + DeleteFunc: func(e event.DeleteEvent) bool { + // Evaluates to false if the object has been confirmed deleted. + //return !e.DeleteStateUnknown + return false + }, + }). + WithOptions(controller.Options{MaxConcurrentReconciles: 100}). + Complete(r) +} + +/************************************************************* +Enh 35357707 - PROVIDE THE LRPDB TNSALIAS INFORMATION +**************************************************************/ + +func parseTnsAlias(tns *string, lrpdbsrv *string) { + fmt.Printf("Analyzing string [%s]\n", *tns) + fmt.Printf("Relacing srv [%s]\n", *lrpdbsrv) + var swaptns string + + if strings.Contains(strings.ToUpper(*tns), "SERVICE_NAME") == false { + fmt.Print("Cannot generate tns alias for lrpdb") + return + } + + if strings.Contains(strings.ToUpper(*tns), "ORACLE_SID") == true { + fmt.Print("Cannot generate tns alias for lrpdb") + return + } + + swaptns = fmt.Sprintf("SERVICE_NAME=%s", *lrpdbsrv) + tnsreg := regexp.MustCompile(`SERVICE_NAME=\w+`) + *tns = tnsreg.ReplaceAllString(*tns, swaptns) + + fmt.Printf("Newstring [%s]\n", *tns) + +} + +// Compose url +func (r *LRPDBReconciler) BaseUrl(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, lrest dbapi.LREST) string { + log := r.Log.WithValues("BaseUrl", req.NamespacedName) + baseurl := "https://" + lrpdb.Spec.CDBResName + "-lrest." + lrpdb.Spec.CDBNamespace + ":" + strconv.Itoa(lrest.Spec.LRESTPort) + "/database/pdbs/" + log.Info("Baseurl:" + baseurl) + return baseurl +} + +func (r *LRPDBReconciler) DecryptWithPrivKey(Key string, Buffer string, req ctrl.Request) (string, error) { + log := r.Log.WithValues("DecryptWithPrivKey", req.NamespacedName) + Debug := 0 + block, _ := pem.Decode([]byte(Key)) + pkcs8PrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + log.Error(err, "Failed to parse private key - "+err.Error()) + return "", err + } + if Debug == 1 { + fmt.Printf("======================================\n") + fmt.Printf("%s\n", Key) + fmt.Printf("======================================\n") + } + + encString64, err := base64.StdEncoding.DecodeString(string(Buffer)) + if err != nil { + log.Error(err, "Failed to decode encrypted string to base64 - "+err.Error()) + return "", err + } + + decryptedB, err := rsa.DecryptPKCS1v15(nil, pkcs8PrivateKey.(*rsa.PrivateKey), encString64) + if err != nil { + log.Error(err, "Failed to decrypt string - "+err.Error()) + return "", err + } + if Debug == 1 { + fmt.Printf("[%s]\n", string(decryptedB)) + } + return strings.TrimSpace(string(decryptedB)), err + +} + +// New function to decrypt credential using private key +func (r *LRPDBReconciler) getEncriptedSecret(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, secretName string, keyName string, secretNamePk string, keyNamePk string) (string, error) { + + log := r.Log.WithValues("getEncriptedSecret", req.NamespacedName) + + log.Info("getEncriptedSecret :" + secretName) + secret1 := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: secretName, Namespace: lrpdb.Namespace}, secret1) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + secretName) + lrpdb.Status.Msg = "Secret not found:" + secretName + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + secret2 := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: secretNamePk, Namespace: lrpdb.Namespace}, secret2) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + secretNamePk) + lrpdb.Status.Msg = "Secret not found:" + secretNamePk + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + Encval := string(secret1.Data[keyName]) + Encval = strings.TrimSpace(Encval) + + privKey := string(secret2.Data[keyNamePk]) + privKey = strings.TrimSpace(privKey) + + /* Debuug info for dev phase + fmt.Printf("DEBUG Secretename:secretName :%s\n", secretName) + fmt.Printf("DEBUG privKey :%s\n", privKey) + fmt.Printf("DEBUG Encval :%s\n", Encval) + */ + + DecVal, err := r.DecryptWithPrivKey(privKey, Encval, req) + if err != nil { + log.Error(err, "Fail to decrypt secret:"+secretName) + lrpdb.Status.Msg = " Fail to decrypt secret:" + secretName + return "", err + } + return DecVal, nil +} + +func (r *LRPDBReconciler) manageLRPDBDeletion2(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("manageLRPDBDeletion", req.NamespacedName) + if lrpdb.ObjectMeta.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + controllerutil.AddFinalizer(lrpdb, LRPDBFinalizer) + if err := r.Update(ctx, lrpdb); err != nil { + return err + } + } + } else { + log.Info("Pdb marked to be delted") + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + if assertiveLpdbDeletion == true { + log.Info("Deleting lrpdb CRD: Assertive approach is turned on ") + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + log.Error(err, "Cannont find cdb resource ", "err", err.Error()) + return err + } + + lrpdbName := lrpdb.Spec.LRPDBName + if lrpdb.Status.OpenMode == "READ WRITE" { + valuesclose := map[string]string{ + "state": "CLOSE", + "modifyOption": "IMMEDIATE", + "getScript": "FALSE"} + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" + _, errclose := r.callAPI(ctx, req, lrpdb, url, valuesclose, "POST") + if errclose != nil { + log.Info("Warning error closing lrpdb continue anyway") + } + } + + valuesdrop := map[string]string{ + "action": "INCLUDING", + "getScript": "FALSE"} + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/" + + log.Info("Call Delete()") + _, errdelete := r.callAPI(ctx, req, lrpdb, url, valuesdrop, "DELETE") + if errdelete != nil { + log.Error(errdelete, "Fail to delete lrpdb :"+lrpdb.Name, "err", err.Error()) + return errdelete + } + } /* END OF ASSERTIVE SECTION */ + + log.Info("Marked to be deleted") + lrpdb.Status.Phase = lrpdbPhaseDelete + lrpdb.Status.Status = true + r.Status().Update(ctx, lrpdb) + + controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) + if err := r.Update(ctx, lrpdb); err != nil { + log.Info("Cannot remove finalizer") + return err + } + + } + + return nil + } + + return nil +} + +func (r *LRPDBReconciler) InitConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) *corev1.ConfigMap { + log := r.Log.WithValues("InitConfigMap", req.NamespacedName) + log.Info("ConfigMap..............:" + "ConfigMap" + lrpdb.Name) + log.Info("ConfigMap nmsp.........:" + lrpdb.Namespace) + /* + * PDB SYSTEM PARAMETER + * record [name,value=[paramete_val|reset],level=[session|system]] + */ + + if lrpdb.Spec.PDBConfigMap == "" { + /* if users does not specify a config map + we generate an empty new one for possible + future pdb parameter modification */ + + var SystemParameters map[string]string + + log.Info("Generating an empty configmap") + globalconfigmap = "configmap-" + lrpdb.Spec.LRPDBName + "-default" + DbParameters := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "configmap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: globalconfigmap, + Namespace: lrpdb.Namespace, + }, + Data: SystemParameters, + } + + if err := ctrl.SetControllerReference(lrpdb, DbParameters, r.Scheme); err != nil { + log.Error(err, "Fail to set SetControllerReference", "err", err.Error()) + return nil + } + + /* Update Spec.PDBConfigMap */ + lrpdb.Spec.PDBConfigMap = "configmap" + lrpdb.Spec.LRPDBName + "default" + if err := r.Update(ctx, lrpdb); err != nil { + log.Error(err, "Failure updating Spec.PDBConfigMap ", "err", err.Error()) + return nil + } + lrpdb.Status.Bitstat = bis(lrpdb.Status.Bitstat, MPEMPT) + return DbParameters + + } else { + + lrpdb.Status.Bitstat = bis(lrpdb.Status.Bitstat, MPINIT) + globalconfigmap = lrpdb.Spec.PDBConfigMap + DbParameters, err := r.GetConfigMap(ctx, req, lrpdb) + if err != nil { + log.Error(err, "Fail to fetch configmap ", "err", err.Error()) + return nil + } + + //ParseConfigMapData(DbParameters) + + return DbParameters + } + + return nil +} + +func (r *LRPDBReconciler) GetConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (*corev1.ConfigMap, error) { + log := r.Log.WithValues("GetConfigMap", req.NamespacedName) + log.Info("ConfigMapGlobal.............:" + globalconfigmap) + DbParameters, err := k8s.FetchConfigMap(r.Client, lrpdb.Namespace, globalconfigmap) + if err != nil { + log.Error(err, "Fail to fetch configmap", "err", err.Error()) + return nil, err + } + + return DbParameters, nil +} + +func (r *LRPDBReconciler) ApplyConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (int32, error) { + log := r.Log.WithValues("ApplyConfigMap", req.NamespacedName) + /* We read the config map and apply the setting to the pdb */ + + log.Info("Starting Apply Config Map Process") + configmap, err := r.GetConfigMap(ctx, req, lrpdb) + if err != nil { + log.Info("Cannot get config map in the open yaml file") + return 0, nil + } + Cardinality := int32(len(configmap.Data)) + if Cardinality == 0 { + log.Info("Empty config map... nothing to do ") + return 0, nil + } + log.Info("GetConfigMap completed") + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + log.Info("Cannot find lrest server") + return 0, nil + } + tokens := lrcommons.ParseConfigMapData(configmap) + for cnt := range tokens { + if len(tokens[cnt]) != 0 { + /* avoid null token and check malformed value */ + fmt.Printf("token=[%s]\n", tokens[cnt]) + Parameter := strings.Split(tokens[cnt], " ") + if len(Parameter) != 3 { + log.Info("WARNING malformed value in the configmap") + } else { + fmt.Printf("alter system set %s=%s scope=%s instances=all\n", Parameter[0], Parameter[1], Parameter[2]) + /* Preparing PayLoad + ----------------- + WARNING: event setting is not yet supported. It will be implemented in future release + */ + AlterSystemPayload := map[string]string{ + "state": "ALTER", + "alterSystemParameter": Parameter[0], + "alterSystemValue": Parameter[1], + "parameterScope": Parameter[2], + } + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdb.Spec.LRPDBName + respData, err := r.callAPI(ctx, req, lrpdb, url, AlterSystemPayload, "POST") + if err != nil { + log.Error(err, "callAPI failure durring Apply Config Map", "err", err.Error()) + return 0, err + } + /* check sql code execution */ + var retJson map[string]interface{} + if err := json.Unmarshal([]byte(respData), &retJson); err != nil { + log.Error(err, "failed to get Data from callAPI", "err", err.Error()) + return 0, err + } + /* We do not the execution if something goes wrong for a single parameter + just report the error in the event queue */ + SqlCode := strconv.Itoa(int(retJson["sqlcode"].(float64))) + AlterMsg := fmt.Sprintf("pdb=%s:%s:%s:%s:%s", lrpdb.Spec.LRPDBName, Parameter[0], Parameter[1], Parameter[2], SqlCode) + log.Info("Config Map Apply:......." + AlterMsg) + + if SqlCode != "0" { + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", AlterMsg) + lrpdb.Status.Bitstat = bis(lrpdb.Status.Bitstat, MPWARN) + } + + } + } + + } + + lrpdb.Status.Bitstat = bis(lrpdb.Status.Bitstat, MPAPPL) + + return Cardinality, nil +} + +func (r *LRPDBReconciler) ManageConfigMapForCloningAndPlugin(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("ManageConfigMapForCloningAndPlugin", req.NamespacedName) + log.Info("Frame:") + /* + If configmap parameter is set and init flag is not set + then we need to iniialized the init mask. This is the case for + pdb generated by clone and plug action + */ + if lrpdb.Spec.Action != "CREATE" && lrpdb.Spec.PDBConfigMap != "" && bit(lrpdb.Status.Bitstat, MPINIT) == false { + if r.InitConfigMap(ctx, req, lrpdb) == nil { + log.Info("Cannot initialize config map for pdb.........:" + lrpdb.Spec.LRPDBName) + return nil + } + log.Info("Call...........:ApplyConfigMap(ctx, req, lrpdb)") + Cardinality, _ := r.ApplyConfigMap(ctx, req, lrpdb) + log.Info("Cardnality:....:" + strconv.Itoa(int(Cardinality))) + if Cardinality == 0 { + return nil + } + + } + return nil +} + +func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, url string, payload map[string]string, action string) (string, error) { + var c client.Client + var r logr.Logger + var e record.EventRecorder + var err error + + recpdb, ok1 := intr.(*LRPDBReconciler) + if ok1 { + fmt.Printf("func NewCallLApi ((*PDBReconciler),......)\n") + c = recpdb.Client + e = recpdb.Recorder + r = recpdb.Log + } + + reccdb, ok2 := intr.(*LRESTReconciler) + if ok2 { + fmt.Printf("func NewCallLApi ((*CDBReconciler),......)\n") + c = reccdb.Client + e = reccdb.Recorder + r = reccdb.Log + } + + log := r.WithValues("NewCallLAPI", req.NamespacedName) + + secret := &corev1.Secret{} + + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsKey.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + rsaKeyPEM := secret.Data[lrpdb.Spec.LRPDBTlsKey.Secret.Key] + + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + rsaCertPEM := secret.Data[lrpdb.Spec.LRPDBTlsCrt.Secret.Key] + + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCat.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCat.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + caCert := secret.Data[lrpdb.Spec.LRPDBTlsCat.Secret.Key] + /* + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaKeyPEM)) + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaCertPEM)) + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(caCert)) + */ + + certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) + if err != nil { + lrpdb.Status.Msg = "Error tls.X509KeyPair" + return "", err + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + /* + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool} + */ + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool, + //MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + PreferServerCipherSuites: true, + CipherSuites: []uint16{ + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_RSA_WITH_AES_256_CBC_SHA, + }, + } + + tr := &http.Transport{TLSClientConfig: tlsConf} + + httpclient := &http.Client{Transport: tr} + + log.Info("Issuing REST call", "URL", url, "Action", action) + + // Get Web Server User + //secret := &corev1.Secret{} + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.WebLrpdbServerUser.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.WebLrpdbServerUser.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + webUserEnc := string(secret.Data[lrpdb.Spec.WebLrpdbServerUser.Secret.Key]) + webUserEnc = strings.TrimSpace(webUserEnc) + + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBPriKey.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBPriKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + privKey := string(secret.Data[lrpdb.Spec.LRPDBPriKey.Secret.Key]) + webUser, err := lrcommons.CommonDecryptWithPrivKey(privKey, webUserEnc, req) + + // Get Web Server User Password + secret = &corev1.Secret{} + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.WebLrpdbServerPwd.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.WebLrpdbServerPwd.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + webUserPwdEnc := string(secret.Data[lrpdb.Spec.WebLrpdbServerPwd.Secret.Key]) + webUserPwdEnc = strings.TrimSpace(webUserPwdEnc) + webUserPwd, err := lrcommons.CommonDecryptWithPrivKey(privKey, webUserPwdEnc, req) + + var httpreq *http.Request + if action == "GET" { + httpreq, err = http.NewRequest(action, url, nil) + } else { + jsonValue, _ := json.Marshal(payload) + httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) + } + + if err != nil { + log.Info("Unable to create HTTP Request for LRPDB : "+lrpdb.Name, "err", err.Error()) + return "", err + } + + httpreq.Header.Add("Accept", "application/json") + httpreq.Header.Add("Content-Type", "application/json") + httpreq.SetBasicAuth(webUser, webUserPwd) + + resp, err := httpclient.Do(httpreq) + if err != nil { + errmsg := err.Error() + log.Error(err, "Failed - Could not connect to LREST Pod", "err", err.Error()) + lrpdb.Status.Msg = "Error: Could not connect to LREST Pod" + e.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", errmsg) + return "", err + } + + e.Eventf(lrpdb, corev1.EventTypeWarning, "Done", lrpdb.Spec.CDBResName) + if resp.StatusCode != http.StatusOK { + bb, _ := ioutil.ReadAll(resp.Body) + + if resp.StatusCode == 404 { + lrpdb.Status.ConnString = "" + lrpdb.Status.Msg = lrpdb.Spec.LRPDBName + " not found" + + } else { + if flood_control == false { + lrpdb.Status.Msg = "LREST Error - HTTP Status Code:" + strconv.Itoa(resp.StatusCode) + } + } + + if flood_control == false { + log.Info("LREST Error - HTTP Status Code :"+strconv.Itoa(resp.StatusCode), "Err", string(bb)) + } + + var apiErr LRESTError + json.Unmarshal([]byte(bb), &apiErr) + if flood_control == false { + e.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", "Failed: %s", apiErr.Message) + } + fmt.Printf("\n================== APIERR ======================\n") + fmt.Printf("%+v \n", apiErr) + fmt.Printf(string(bb)) + fmt.Printf("URL=%s\n", url) + fmt.Printf("resp.StatusCode=%s\n", strconv.Itoa(resp.StatusCode)) + fmt.Printf("\n================== APIERR ======================\n") + flood_control = true + return "", errors.New("LREST Error") + } + flood_control = false + + defer resp.Body.Close() + + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Print(err.Error()) + } + respData := string(bodyBytes) + fmt.Print("CALL API return msg.....:") + fmt.Println(string(bodyBytes)) + + var apiResponse restSQLCollection + json.Unmarshal([]byte(bodyBytes), &apiResponse) + fmt.Printf("===> %#v\n", apiResponse) + fmt.Printf("===> %+v\n", apiResponse) + + errFound := false + for _, sqlItem := range apiResponse.Items { + if sqlItem.ErrorDetails != "" { + log.Info("LREST Error - Oracle Error Code :" + strconv.Itoa(sqlItem.ErrorCode)) + if !errFound { + lrpdb.Status.Msg = sqlItem.ErrorDetails + } + e.Eventf(lrpdb, corev1.EventTypeWarning, "OraError", "%s", sqlItem.ErrorDetails) + errFound = true + } + } + + if errFound { + return "", errors.New("Oracle Error") + } + + return respData, nil +} + +func (r *LRPDBReconciler) GetSqlCode(rsp string, sqlcode *int) error { + log := r.Log.WithValues("GetSqlCode", "callAPI(...)") + + var objmap map[string]interface{} + if err := json.Unmarshal([]byte(rsp), &objmap); err != nil { + log.Error(err, "failed to get respData from callAPI", "err", err.Error()) + return err + } + + *sqlcode = int(objmap["sqlcode"].(float64)) + log.Info("sqlcode.......:ora-" + strconv.Itoa(*sqlcode)) + if *sqlcode != 0 { + switch strconv.Itoa(*sqlcode) { + case "65019": /* already open */ + return nil + case "65020": /* already closed */ + return nil + } + err := fmt.Errorf("%v", sqlcode) + return err + } + return nil +} diff --git a/main.go b/main.go index 3b6f957d..9e71967a 100644 --- a/main.go +++ b/main.go @@ -228,10 +228,18 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "PDB") os.Exit(1) } + if err = (&databasev4.LRPDB{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "LRPDB") + os.Exit(1) + } if err = (&databasev4.CDB{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "CDB") os.Exit(1) } + if err = (&databasev4.LREST{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "LREST") + os.Exit(1) + } if err = (&databasev1alpha1.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") os.Exit(1) @@ -276,21 +284,21 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") os.Exit(1) } - if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") - } + if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + } if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") os.Exit(1) } - if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") - os.Exit(1) - } - if err = (&databasev4.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") - os.Exit(1) - } + if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } + if err = (&databasev4.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } } // PDB Reconciler @@ -305,6 +313,18 @@ func main() { os.Exit(1) } + // LRPDBR Reconciler + if err = (&databasecontroller.LRPDBReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.Log.WithName("controllers").WithName("LRPDB"), + Interval: time.Duration(i), + Recorder: mgr.GetEventRecorderFor("LRPDB"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "LRPDB") + os.Exit(1) + } + // CDB Reconciler if err = (&databasecontroller.CDBReconciler{ Client: mgr.GetClient(), @@ -317,6 +337,20 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "CDB") os.Exit(1) } + + // LREST Reconciler + if err = (&databasecontroller.LRESTReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Config: mgr.GetConfig(), + Log: ctrl.Log.WithName("controllers").WithName("LREST"), + Interval: time.Duration(i), + Recorder: mgr.GetEventRecorderFor("LREST"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "LREST") + os.Exit(1) + } + if err = (&dataguardcontroller.DataguardBrokerReconciler{ Client: mgr.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("dataguard").WithName("DataguardBroker"), @@ -350,6 +384,14 @@ func main() { os.Exit(1) } + indexFunc2 := func(obj client.Object) []string { + return []string{obj.(*databasev4.LRPDB).Spec.LRPDBName} + } + if err = cache.IndexField(context.TODO(), &databasev4.LRPDB{}, "spec.pdbName", indexFunc2); err != nil { + setupLog.Error(err, "unable to create index function for ", "controller", "LRPDB") + os.Exit(1) + } + setupLog.Info("starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { setupLog.Error(err, "problem running manager") diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index ae9d93fe..ad8bb9c0 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -2356,6 +2356,629 @@ spec: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: lrests.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LREST + listKind: LRESTList + plural: lrests + singular: lrest + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the LREST + jsonPath: .spec.cdbName + name: CDB NAME + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the LREST Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message if any + jsonPath: .status.msg + name: Message + type: string + - description: string of the tnsalias + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + lrestImage: + type: string + lrestImagePullPolicy: + enum: + - Always + - Never + type: string + lrestImagePullSecret: + type: string + lrestPort: + type: integer + lrestPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + nodeSelector: + additionalProperties: + type: string + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: lrpdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LRPDB + listKind: LRPDBList + plural: lrpdbs + singular: lrpdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the LRPDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: last sqlcode + jsonPath: .status.sqlCode + name: last sqlcode + type: integer + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + - Alter + - Noaction + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbPass: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + alterSystem: + type: string + alterSystemParameter: + type: string + alterSystemValue: + type: string + asClone: + type: boolean + assertiveLrpdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + lrpdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + parameterScope: + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + - ALTER + type: string + pdbconfigmap: + type: string + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + - alterSystemParameter + - alterSystemValue + - webServerPwd + type: object + status: + properties: + action: + type: string + alterSystem: + type: string + bitstat: + type: integer + bitstatstr: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + sqlCode: + type: integer + status: + type: boolean + totalSize: + type: string + required: + - phase + - sqlCode + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.16.5 @@ -4425,6 +5048,9 @@ rules: - cdbs - dataguardbrokers - dbcssystems + - events + - lrests + - lrpdbs - oraclerestdataservices - pdbs - shardingdatabases @@ -4446,6 +5072,8 @@ rules: - cdbs/status - dataguardbrokers/status - dbcssystems/status + - lrests/status + - lrpdbs/status - oraclerestdataservices/status - pdbs/status - shardingdatabases/status @@ -4478,6 +5106,7 @@ rules: resources: - cdbs/finalizers - dataguardbrokers/finalizers + - lrests/finalizers - oraclerestdataservices/finalizers - singleinstancedatabases/finalizers verbs: @@ -4486,6 +5115,7 @@ rules: - database.oracle.com resources: - dbcssystems/finalizers + - lrpdbs/finalizers - pdbs/finalizers - shardingdatabases/finalizers verbs: @@ -4748,6 +5378,48 @@ webhooks: resources: - dbcssystems sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: mlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: mlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -5061,6 +5733,48 @@ webhooks: resources: - cdbs sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: vlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: vlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -5314,7 +6028,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: container-registry.oracle.com/database/operator:latest + image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/operatormntnns:latest imagePullPolicy: Always name: manager ports: From c1bec31f1164242900628a6bfefced9fee93eeee Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 13 Dec 2024 14:34:44 +0000 Subject: [PATCH 139/414] v4_ordsservice --- Dockerfile | 1 + PROJECT | 8 + apis/database/v4/ordssrvs_types.go | 686 +++++++++++ apis/database/v4/zz_generated.deepcopy.go | 487 ++++++++ .../bases/database.oracle.com_ordssrvs.yaml | 478 ++++++++ config/crd/kustomization.yaml | 3 + .../cainjection_in_database_ordssrvs.yaml | 8 + config/crd/patches/webhook_in_ordssrvs.yaml | 17 + config/rbac/ordssrvs_editor_role.yaml | 24 + config/rbac/ordssrvs_viewer_role.yaml | 20 + config/rbac/role.yaml | 22 + config/samples/database_v4_ordssrvs.yaml | 6 + config/samples/kustomization.yaml | 1 + controllers/database/lrest_controller.go | 6 +- controllers/database/ordssrvs_controller.go | 1055 +++++++++++++++++ controllers/database/ordssrvs_ordsconfig.go | 258 ++++ main.go | 9 + oracle-database-operator.yaml | 500 ++++++++ ords/ords_init.sh | 484 ++++++++ 19 files changed, 4070 insertions(+), 3 deletions(-) create mode 100644 apis/database/v4/ordssrvs_types.go create mode 100644 config/crd/bases/database.oracle.com_ordssrvs.yaml create mode 100644 config/crd/patches/cainjection_in_database_ordssrvs.yaml create mode 100644 config/crd/patches/webhook_in_ordssrvs.yaml create mode 100644 config/rbac/ordssrvs_editor_role.yaml create mode 100644 config/rbac/ordssrvs_viewer_role.yaml create mode 100644 config/samples/database_v4_ordssrvs.yaml create mode 100644 controllers/database/ordssrvs_controller.go create mode 100644 controllers/database/ordssrvs_ordsconfig.go create mode 100644 ords/ords_init.sh diff --git a/Dockerfile b/Dockerfile index 9ed6eabf..f444d508 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,6 +46,7 @@ ENV COMMIT_SHA=${CI_COMMIT_SHA} \ COMMIT_BRANCH=${CI_COMMIT_BRANCH} WORKDIR / COPY --from=builder /workspace/manager . +COPY ords/ords_init.sh . RUN useradd -u 1002 nonroot USER nonroot diff --git a/PROJECT b/PROJECT index c4cd1bef..f90ecb57 100644 --- a/PROJECT +++ b/PROJECT @@ -196,4 +196,12 @@ resources: kind: LRPDB path: github.com/oracle/oracle-database-operator/apis/database/v4 version: v4 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: database + kind: OrdsSrvs + path: github.com/oracle/oracle-database-operator/apis/database/v4 + version: v4 version: "3" diff --git a/apis/database/v4/ordssrvs_types.go b/apis/database/v4/ordssrvs_types.go new file mode 100644 index 00000000..cd4e28c6 --- /dev/null +++ b/apis/database/v4/ordssrvs_types.go @@ -0,0 +1,686 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// OrdsSrvsSpec defines the desired state of OrdsSrvs +// +kubebuilder:resource:shortName="ords" +type OrdsSrvsSpec struct { + // Specifies the desired Kubernetes Workload + //+kubebuilder:validation:Enum=Deployment;StatefulSet;DaemonSet + //+kubebuilder:default=Deployment + WorkloadType string `json:"workloadType,omitempty"` + // Defines the number of desired Replicas when workloadType is Deployment or StatefulSet + //+kubebuilder:validation:Minimum=1 + //+kubebuilder:default=1 + Replicas int32 `json:"replicas,omitempty"` + // Specifies whether to restart pods when Global or Pool configurations change + ForceRestart bool `json:"forceRestart,omitempty"` + // Specifies the ORDS container image + //+kubecbuilder:default=container-registry.oracle.com/database/ords:latest + Image string `json:"image"` + // Specifies the ORDS container image pull policy + //+kubebuilder:validation:Enum=IfNotPresent;Always;Never + //+kubebuilder:default=IfNotPresent + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + // Specifies the Secret Name for pulling the ORDS container image + ImagePullSecrets string `json:"imagePullSecrets,omitempty"` + // Contains settings that are configured across the entire ORDS instance. + GlobalSettings GlobalSettings `json:"globalSettings"` + // Contains settings for individual pools/databases + PoolSettings []*PoolSettings `json:"poolSettings,omitempty"` + // +k8s:openapi-gen=true +} + +type GlobalSettings struct { + // Specifies the setting to enable or disable metadata caching. + CacheMetadataEnabled *bool `json:"cache.metadata.enabled,omitempty"` + + // Specifies the duration after a GraphQL schema is not accessed from the cache that it expires. + CacheMetadataGraphQLExpireAfterAccess *time.Duration `json:"cache.metadata.graphql.expireAfterAccess,omitempty"` + + // Specifies the duration after a GraphQL schema is cached that it expires and has to be loaded again. + CacheMetadataGraphQLExpireAfterWrite *time.Duration `json:"cache.metadata.graphql.expireAfterWrite,omitempty"` + + // Specifies the setting to determine for how long a metadata record remains in the cache. + // Longer duration means, it takes longer to view the applied changes. + // The formats accepted are based on the ISO-8601 duration format. + CacheMetadataTimeout *time.Duration `json:"cache.metadata.timeout,omitempty"` + + // Specifies the setting to enable or disable JWKS caching. + CacheMetadataJWKSEnabled *bool `json:"cache.metadata.jwks.enabled,omitempty"` + + // Specifies the initial capacity of the JWKS cache. + CacheMetadataJWKSInitialCapacity *int32 `json:"cache.metadata.jwks.initialCapacity,omitempty"` + + // Specifies the maximum capacity of the JWKS cache. + CacheMetadataJWKSMaximumSize *int32 `json:"cache.metadata.jwks.maximumSize,omitempty"` + + // Specifies the duration after a JWK is not accessed from the cache that it expires. + // By default this is disabled. + CacheMetadataJWKSExpireAfterAccess *time.Duration `json:"cache.metadata.jwks.expireAfterAccess,omitempty"` + + // Specifies the duration after a JWK is cached, that is, it expires and has to be loaded again. + CacheMetadataJWKSExpireAfterWrite *time.Duration `json:"cache.metadata.jwks.expireAfterWrite,omitempty"` + + // Specifies whether the Database API is enabled. + DatabaseAPIEnabled *bool `json:"database.api.enabled,omitempty"` + + // Specifies to disable the Database API administration related services. + // Only applicable when Database API is enabled. + DatabaseAPIManagementServicesDisabled *bool `json:"database.api.management.services.disabled,omitempty"` + + // Specifies how long to wait before retrying an invalid pool. + DBInvalidPoolTimeout *time.Duration `json:"db.invalidPoolTimeout,omitempty"` + + // Specifies the maximum join nesting depth limit for GraphQL queries. + FeatureGraphQLMaxNestingDepth *int32 `json:"feature.grahpql.max.nesting.depth,omitempty"` + + // Specifies the name of the HTTP request header that uniquely identifies the request end to end as + // it passes through the various layers of the application stack. + // In Oracle this header is commonly referred to as the ECID (Entity Context ID). + RequestTraceHeaderName string `json:"request.traceHeaderName,omitempty"` + + // Specifies the maximum number of unsuccessful password attempts allowed. + // Enabled by setting a positive integer value. + SecurityCredentialsAttempts *int32 `json:"security.credentials.attempts,omitempty"` + + // Specifies the period to lock the account that has exceeded maximum attempts. + SecurityCredentialsLockTime *time.Duration `json:"security.credentials.lock.time,omitempty"` + + // Specifies the HTTP listen port. + //+kubebuilder:default:=8080 + StandaloneHTTPPort *int32 `json:"standalone.http.port,omitempty"` + + // Specifies the SSL certificate hostname. + StandaloneHTTPSHost string `json:"standalone.https.host,omitempty"` + + // Specifies the HTTPS listen port. + //+kubebuilder:default:=8443 + StandaloneHTTPSPort *int32 `json:"standalone.https.port,omitempty"` + + // Specifies the period for Standalone Mode to wait until it is gracefully shutdown. + StandaloneStopTimeout *time.Duration `json:"standalone.stop.timeout,omitempty"` + + // Specifies whether to display error messages on the browser. + DebugPrintDebugToScreen *bool `json:"debug.printDebugToScreen,omitempty"` + + // Specifies how the HTTP error responses must be formatted. + // html - Force all responses to be in HTML format + // json - Force all responses to be in JSON format + // auto - Automatically determines most appropriate format for the request (default). + ErrorResponseFormat string `json:"error.responseFormat,omitempty"` + + // Specifies the Internet Content Adaptation Protocol (ICAP) port to virus scan files. + // Either icap.port or icap.secure.port are required to have a value. + ICAPPort *int32 `json:"icap.port,omitempty"` + + // Specifies the Internet Content Adaptation Protocol (ICAP) port to virus scan files. + // Either icap.port or icap.secure.port are required to have a value. + // If values for both icap.port and icap.secure.port are provided, then the value of icap.port is ignored. + ICAPSecurePort *int32 `json:"icap.secure.port,omitempty"` + + // Specifies the Internet Content Adaptation Protocol (ICAP) server name or IP address to virus scan files. + // The icap.server is required to have a value. + ICAPServer string `json:"icap.server,omitempty"` + + // Specifies whether procedures are to be logged. + LogProcedure bool `json:"log.procedure,omitempty"` + + // Specifies to enable the API for MongoDB. + //+kubebuider:default=false + MongoEnabled bool `json:"mongo.enabled,omitempty"` + + // Specifies the API for MongoDB listen port. + //+kubebuilder:default:=27017 + MongoPort *int32 `json:"mongo.port,omitempty"` + + // Specifies the maximum idle time for a Mongo connection in milliseconds. + MongoIdleTimeout *time.Duration `json:"mongo.idle.timeout,omitempty"` + + // Specifies the maximum time for a Mongo database operation in milliseconds. + MongoOpTimeout *time.Duration `json:"mongo.op.timeout,omitempty"` + + // If this value is set to true, then the Oracle REST Data Services internal exclusion list is not enforced. + // Oracle recommends that you do not set this value to true. + SecurityDisableDefaultExclusionList *bool `json:"security.disableDefaultExclusionList,omitempty"` + + // Specifies a pattern for procedures, packages, or schema names which are forbidden to be directly executed from a browser. + SecurityExclusionList string `json:"security.exclusionList,omitempty"` + + // Specifies a pattern for procedures, packages, or schema names which are allowed to be directly executed from a browser. + SecurityInclusionList string `json:"security.inclusionList,omitempty"` + + // Specifies the maximum number of cached procedure validations. + // Set this value to 0 to force the validation procedure to be invoked on each request. + SecurityMaxEntries *int32 `json:"security.maxEntries,omitempty"` + + // Specifies whether HTTPS is available in your environment. + SecurityVerifySSL *bool `json:"security.verifySSL,omitempty"` + + // Specifies the context path where ords is located. + //+kubebuilder:default:="/ords" + StandaloneContextPath string `json:"standalone.context.path,omitempty"` + + /************************************************* + * Undocumented + /************************************************/ + + // Specifies that the HTTP Header contains the specified text + // Usually set to 'X-Forwarded-Proto: https' coming from a load-balancer + SecurityHTTPSHeaderCheck string `json:"security.httpsHeaderCheck,omitempty"` + + // Specifies to force HTTPS; this is set to default to false as in real-world TLS should + // terminiate at the LoadBalancer + SecurityForceHTTPS bool `json:"security.forceHTTPS,omitempty"` + + // Specifies to trust Access from originating domains + SecuirtyExternalSessionTrustedOrigins string `json:"security.externalSessionTrustedOrigins,omitempty"` + + /************************************************* + * Customised + /************************************************/ + /* Below are settings with physical path/file locations to be replaced by ConfigMaps/Secrets, Boolean or HardCoded */ + + /* + // Specifies the path to the folder to store HTTP request access logs. + // If not specified, then no access log is generated. + // HARDCODED + // StandaloneAccessLog string `json:"standalone.access.log,omitempty"` + */ + + // Specifies if HTTP request access logs should be enabled + // If enabled, logs will be written to /opt/oracle/sa/log/global + //+kubebuilder:default:=false + EnableStandaloneAccessLog bool `json:"enable.standalone.access.log,omitempty"` + + // Specifies if HTTP request access logs should be enabled + // If enabled, logs will be written to /opt/oracle/sa/log/global + //+kubebuilder:default:=false + EnableMongoAccessLog bool `json:"enable.mongo.access.log,omitempty"` + + /* + //Specifies the SSL certificate path. + // If you are providing the SSL certificate, then you must specify the certificate location. + // Replaced with: CertSecret *CertificateSecret `json:"certSecret,omitempty"` + //StandaloneHTTPSCert string `json:"standalone.https.cert"` + + // Specifies the SSL certificate key path. + // If you are providing the SSL certificate, you must specify the certificate key location. + // Replaced with: CertSecret *CertificateSecret `json:"certSecret,omitempty"` + //StandaloneHTTPSCertKey string `json:"standalone.https.cert.key"` + */ + + // Specifies the Secret containing the SSL Certificates + // Replaces: standalone.https.cert and standalone.https.cert.key + CertSecret *CertificateSecret `json:"certSecret,omitempty"` + + /************************************************* + * Disabled + /************************************************* + // Specifies the comma separated list of host names or IP addresses to identify a specific network + // interface on which to listen. + //+kubebuilder:default:="0.0.0.0" + //StandaloneBinds string `json:"standalone.binds,omitempty"` + // This is disabled as containerised + + // Specifies the file where credentials are stored. + //SecurityCredentialsFile string `json:"security.credentials.file,omitempty"` + // WTF does this do?!?! + + // Points to the location where static resources to be served under the / root server path are located. + // StandaloneDocRoot string `json:"standalone.doc.root,omitempty"` + // Maybe this gets implemented; difficult to predict valid use case + + // Specifies the path to a folder that contains the custom error page. + // ErrorExternalPath string `json:"error.externalPath,omitempty"` + // Can see use-case; but wait for implementation + + // Specifies the Context path where APEX static resources are located. + //+kubebuilder:default:="/i" + // StandaloneStaticContextPath string `json:"standalone.static.context.path,omitempty"` + // Does anyone ever change this? If so, need to also change the APEX install configmap to update path + */ + + // Specifies the path to the folder containing static resources required by APEX. + // StandaloneStaticPath string `json:"standalone.static.path,omitempty"` + // This is disabled as will use the container image path (/opt/oracle/apex/$ORDS_VER/images) + // HARDCODED into the entrypoint + + // Specifies a comma separated list of host names or IP addresses to identify a specific + // network interface on which to listen. + //+kubebuilder:default:="0.0.0.0" + // MongoHost string `json:"mongo.host,omitempty"` + // This is disabled as containerised + + // Specifies the path to the folder where you want to store the API for MongoDB access logs. + // MongoAccessLog string `json:"mongo.access.log,omitempty"` + // HARDCODED to global/logs +} + +type PoolSettings struct { + // Specifies the Pool Name + PoolName string `json:"poolName"` + + // Specify whether to perform ORDS installation/upgrades automatically + // The db.adminUser and db.adminUser.secret must be set, otherwise setting is ignored + // This setting will be ignored for ADB + //+kubebuilder:default:=false + AutoUpgradeORDS bool `json:"autoUpgradeORDS,omitempty"` + + // Specify whether to perform APEX installation/upgrades automatically + // The db.adminUser and db.adminUser.secret must be set, otherwise setting is ignored + // This setting will be ignored for ADB + //+kubebuilder:default:=false + AutoUpgradeAPEX bool `json:"autoUpgradeAPEX,omitempty"` + + // Specifies the name of the database user for the connection. + // For non-ADB this will default to ORDS_PUBLIC_USER + // For ADBs this must be specified and not ORDS_PUBLIC_USER + // If ORDS_PUBLIC_USER is specified for an ADB, the workload will fail + //+kubebuilder:default:="ORDS_PUBLIC_USER" + DBUsername string `json:"db.username,omitempty"` + + // Specifies the password of the specified database user. + // Replaced by: DBSecret PasswordSecret `json:"dbSecret"` + // DBPassword struct{} `json:"dbPassword,omitempty"` + + // Specifies the Secret with the dbUsername and dbPassword values + // for the connection. + DBSecret PasswordSecret `json:"db.secret"` + + // Specifies the username for the database account that ORDS uses for administration operations in the database. + DBAdminUser string `json:"db.adminUser,omitempty"` + + // Specifies the password for the database account that ORDS uses for administration operations in the database. + // Replaced by: DBAdminUserSecret PasswordSecret `json:"dbAdminUserSecret,omitempty"` + // DBAdminUserPassword struct{} `json:"db.adminUser.password,omitempty"` + + // Specifies the Secret with the dbAdminUser (SYS) and dbAdminPassword values + // for the database account that ORDS uses for administration operations in the database. + // replaces: db.adminUser.password + DBAdminUserSecret PasswordSecret `json:"db.adminUser.secret,omitempty"` + + // Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management. + DBCDBAdminUser string `json:"db.cdb.adminUser,omitempty"` + + // Specifies the password for the database account that ORDS uses for the Pluggable Database Lifecycle Management. + // Replaced by: DBCdbAdminUserSecret PasswordSecret `json:"dbCdbAdminUserSecret,omitempty"` + // DBCdbAdminUserPassword struct{} `json:"db.cdb.adminUser.password,omitempty"` + + // Specifies the Secret with the dbCdbAdminUser (SYS) and dbCdbAdminPassword values + // Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management. + // Replaces: db.cdb.adminUser.password + DBCDBAdminUserSecret PasswordSecret `json:"db.cdb.adminUser.secret,omitempty"` + + // Specifies the comma delimited list of additional roles to assign authenticated APEX administrator type users. + ApexSecurityAdministratorRoles string `json:"apex.security.administrator.roles,omitempty"` + + // Specifies the comma delimited list of additional roles to assign authenticated regular APEX users. + ApexSecurityUserRoles string `json:"apex.security.user.roles,omitempty"` + + // Specifies the source for database credentials when creating a direct connection for running SQL statements. + // Value can be one of pool or request. + // If the value is pool, then the credentials defined in this pool is used to create a JDBC connection. + // If the value request is used, then the credentials in the request is used to create a JDBC connection and if successful, grants the requestor SQL Developer role. + //+kubebuilder:validation:Enum=pool;request + DBCredentialsSource string `json:"db.credentialsSource,omitempty"` + + // Indicates how long to wait to gracefully destroy a pool before moving to forcefully destroy all connections including borrowed ones. + DBPoolDestroyTimeout *time.Duration `json:"db.poolDestroyTimeout,omitempty"` + + // Specifies to enable tracking of JDBC resources. + // If not released causes in resource leaks or exhaustion in the database. + // Tracking imposes a performance overhead. + DebugTrackResources *bool `json:"debug.trackResources,omitempty"` + + // Specifies to disable the Open Service Broker services available for the pool. + FeatureOpenservicebrokerExclude *bool `json:"feature.openservicebroker.exclude,omitempty"` + + // Specifies to enable the Database Actions feature. + FeatureSDW *bool `json:"feature.sdw,omitempty"` + + // Specifies a comma separated list of HTTP Cookies to exclude when initializing an Oracle Web Agent environment. + HttpCookieFilter string `json:"http.cookie.filter,omitempty"` + + // Identifies the database role that indicates that the database user must get the SQL Administrator role. + JDBCAuthAdminRole string `json:"jdbc.auth.admin.role,omitempty"` + + // Specifies how a pooled JDBC connection and corresponding database session, is released when a request has been processed. + JDBCCleanupMode string `json:"jdbc.cleanup.mode,omitempty"` + + // If it is true, then it causes a trace of the SQL statements performed by Oracle Web Agent to be echoed to the log. + OwaTraceSql *bool `json:"owa.trace.sql,omitempty"` + + // Indicates if the PL/SQL Gateway functionality should be available for a pool or not. + // Value can be one of disabled, direct, or proxied. + // If the value is direct, then the pool serves the PL/SQL Gateway requests directly. + // If the value is proxied, the PLSQL_GATEWAY_CONFIG view is used to determine the user to whom to proxy. + //+kubebuilder:validation:Enum=disabled;direct;proxied + PlsqlGatewayMode string `json:"plsql.gateway.mode,omitempty"` + + // Specifies whether the JWT Profile authentication is available. Supported values: + SecurityJWTProfileEnabled *bool `json:"security.jwt.profile.enabled,omitempty"` + + // Specifies the maximum number of bytes read from the JWK url. + SecurityJWKSSize *int32 `json:"security.jwks.size,omitempty"` + + // Specifies the maximum amount of time before timing-out when accessing a JWK url. + SecurityJWKSConnectionTimeout *time.Duration `json:"security.jwks.connection.timeout,omitempty"` + + // Specifies the maximum amount of time reading a response from the JWK url before timing-out. + SecurityJWKSReadTimeout *time.Duration `json:"security.jwks.read.timeout,omitempty"` + + // Specifies the minimum interval between refreshing the JWK cached value. + SecurityJWKSRefreshInterval *time.Duration `json:"security.jwks.refresh.interval,omitempty"` + + // Specifies the maximum skew the JWT time claims are accepted. + // This is useful if the clock on the JWT issuer and ORDS differs by a few seconds. + SecurityJWTAllowedSkew *time.Duration `json:"security.jwt.allowed.skew,omitempty"` + + // Specifies the maximum allowed age of a JWT in seconds, regardless of expired claim. + // The age of the JWT is taken from the JWT issued at claim. + SecurityJWTAllowedAge *time.Duration `json:"security.jwt.allowed.age,omitempty"` + + // Indicates the type of security.requestValidationFunction: javascript or plsql. + //+kubebuilder:validation:Enum=plsql;javascript + SecurityValidationFunctionType string `json:"security.validationFunctionType,omitempty"` + + // The type of connection. + //+kubebuilder:validation:Enum=basic;tns;customurl + DBConnectionType string `json:"db.connectionType,omitempty"` + + // Specifies the JDBC URL connection to connect to the database. + DBCustomURL string `json:"db.customURL,omitempty"` + + // Specifies the host system for the Oracle database. + DBHostname string `json:"db.hostname,omitempty"` + + // Specifies the database listener port. + DBPort *int32 `json:"db.port,omitempty"` + + // Specifies the network service name of the database. + DBServicename string `json:"db.servicename,omitempty"` + + // Specifies the name of the database. + DBSid string `json:"db.sid,omitempty"` + + // Specifies the TNS alias name that matches the name in the tnsnames.ora file. + DBTnsAliasName string `json:"db.tnsAliasName,omitempty"` + + // Specifies the service name in the wallet archive for the pool. + DBWalletZipService string `json:"db.wallet.zip.service,omitempty"` + + // Specifies the JDBC driver type. + //+kubebuilder:validation:Enum=thin;oci8 + JDBCDriverType string `json:"jdbc.DriverType,omitempty"` + + // Specifies how long an available connection can remain idle before it is closed. The inactivity connection timeout is in seconds. + JDBCInactivityTimeout *int32 `json:"jdbc.InactivityTimeout,omitempty"` + + // Specifies the initial size for the number of connections that will be created. + // The default is low, and should probably be set higher in most production environments. + JDBCInitialLimit *int32 `json:"jdbc.InitialLimit,omitempty"` + + // Specifies the maximum number of times to reuse a connection before it is discarded and replaced with a new connection. + JDBCMaxConnectionReuseCount *int32 `json:"jdbc.MaxConnectionReuseCount,omitempty"` + + // Sets the maximum connection reuse time property. + JDBCMaxConnectionReuseTime *int32 `json:"jdbc.MaxConnectionReuseTime,omitempty"` + + // Sets the time in seconds to trust an idle connection to skip a validation test. + JDBCSecondsToTrustIdleConnection *int32 `json:"jdbc.SecondsToTrustIdleConnection,omitempty"` + + // Specifies the maximum number of connections. + // Might be too low for some production environments. + JDBCMaxLimit *int32 `json:"jdbc.MaxLimit,omitempty"` + + // Specifies if the PL/SQL Gateway calls can be authenticated using database users. + // If the value is true then this feature is enabled. If the value is false, then this feature is disabled. + // Oracle recommends not to use this feature. + // This feature used only to facilitate customers migrating from mod_plsql. + JDBCAuthEnabled *bool `json:"jdbc.auth.enabled,omitempty"` + + // Specifies the maximum number of statements to cache for each connection. + JDBCMaxStatementsLimit *int32 `json:"jdbc.MaxStatementsLimit,omitempty"` + + // Specifies the minimum number of connections. + JDBCMinLimit *int32 `json:"jdbc.MinLimit,omitempty"` + + // Specifies a timeout period on a statement. + // An abnormally long running query or script, executed by a request, may leave it in a hanging state unless a timeout is + // set on the statement. Setting a timeout on the statement ensures that all the queries automatically timeout if + // they are not completed within the specified time period. + JDBCStatementTimeout *int32 `json:"jdbc.statementTimeout,omitempty"` + + // Specifies the default page to display. The Oracle REST Data Services Landing Page. + MiscDefaultPage string `json:"misc.defaultPage,omitempty"` + + // Specifies the maximum number of rows that will be returned from a query when processing a RESTful service + // and that will be returned from a nested cursor in a result set. + // Affects all RESTful services generated through a SQL query, regardless of whether the resource is paginated. + MiscPaginationMaxRows *int32 `json:"misc.pagination.maxRows,omitempty"` + + // Specifies the procedure name(s) to execute after executing the procedure specified on the URL. + // Multiple procedure names must be separated by commas. + ProcedurePostProcess string `json:"procedurePostProcess,omitempty"` + + // Specifies the procedure name(s) to execute prior to executing the procedure specified on the URL. + // Multiple procedure names must be separated by commas. + ProcedurePreProcess string `json:"procedure.preProcess,omitempty"` + + // Specifies the function to be invoked prior to dispatching each Oracle REST Data Services based REST Service. + // The function can perform configuration of the database session, perform additional validation or authorization of the request. + // If the function returns true, then processing of the request continues. + // If the function returns false, then processing of the request is aborted and an HTTP 403 Forbidden status is returned. + ProcedureRestPreHook string `json:"procedure.rest.preHook,omitempty"` + + // Specifies an authentication function to determine if the requested procedure in the URL should be allowed or disallowed for processing. + // The function should return true if the procedure is allowed; otherwise, it should return false. + // If it returns false, Oracle REST Data Services will return WWW-Authenticate in the response header. + SecurityRequestAuthenticationFunction string `json:"security.requestAuthenticationFunction,omitempty"` + + // Specifies a validation function to determine if the requested procedure in the URL should be allowed or disallowed for processing. + // The function should return true if the procedure is allowed; otherwise, return false. + //+kubebuilder:default:="ords_util.authorize_plsql_gateway" + SecurityRequestValidationFunction string `json:"security.requestValidationFunction,omitempty"` + + // When using the SODA REST API, specifies the default number of documents returned for a GET request on a collection when a + // limit is not specified in the URL. Must be a positive integer, or "unlimited" for no limit. + SODADefaultLimit string `json:"soda.defaultLimit,omitempty"` + + // When using the SODA REST API, specifies the maximum number of documents that will be returned for a GET request on a collection URL, + // regardless of any limit specified in the URL. Must be a positive integer, or "unlimited" for no limit. + SODAMaxLimit string `json:"soda.maxLimit,omitempty"` + + // Specifies whether the REST-Enabled SQL service is active. + RestEnabledSqlActive *bool `json:"restEnabledSql.active,omitempty"` + + /************************************************* + * Customised + /************************************************/ + /* Below are settings with physical path/file locations to be replaced by ConfigMaps/Secrets, Boolean or HardCoded */ + + /* + // Specifies the wallet archive (provided in BASE64 encoding) containing connection details for the pool. + // Replaced with: DBWalletSecret *DBWalletSecret `json:"dbWalletSecret,omitempty"` + DBWalletZip string `json:"db.wallet.zip,omitempty"` + + // Specifies the path to a wallet archive containing connection details for the pool. + // HARDCODED + DBWalletZipPath string `json:"db.wallet.zip.path,omitempty"` + */ + + // Specifies the Secret containing the wallet archive containing connection details for the pool. + // Replaces: db.wallet.zip + DBWalletSecret *DBWalletSecret `json:"dbWalletSecret,omitempty"` + + /* + // The directory location of your tnsnames.ora file. + // Replaced with: TNSAdminSecret *TNSAdminSecret `json:"tnsAdminSecret,omitempty"` + // DBTnsDirectory string `json:"db.tnsDirectory,omitempty"` + */ + + // Specifies the Secret containing the TNS_ADMIN directory + // Replaces: db.tnsDirectory + TNSAdminSecret *TNSAdminSecret `json:"tnsAdminSecret,omitempty"` + + /************************************************* + * Disabled + /************************************************* + // specifies a configuration setting for AutoUpgrade.jar location. + // AutoupgradeAPIAulocation string `json:"autoupgrade.api.aulocation,omitempty"` + // As of 23.4; AutoUpgrade.jar is not part of the container image + + // Specifies a configuration setting to enable AutoUpgrade REST API features. + // AutoupgradeAPIEnabled *bool `json:"autoupgrade.api.enabled,omitempty"` + // Guess this has to do with autoupgrade.api.aulocation which is not implemented + + // Specifies a configuration setting for AutoUpgrade REST API JVM location. + // AutoupgradeAPIJvmlocation string `json:"autoupgrade.api.jvmlocation,omitempty"` + // Guess this has to do with autoupgrade.api.aulocation which is not implemented + + // Specifies a configuration setting for AutoUpgrade REST API log location. + // AutoupgradeAPILoglocation string `json:"autoupgrade.api.loglocation,omitempty"` + // Guess this has to do with autoupgrade.api.aulocation which is not implemented + + // Specifies that the pool points to a CDB, and that the PDBs connected to that CDB should be made addressable + // by Oracle REST Data Services + // DBServiceNameSuffix string `json:"db.serviceNameSuffix,omitempty"` + // Not sure of use case here?!? + */ +} + +// Defines the secret containing Password mapped to secretKey +type PasswordSecret struct { + // Specifies the name of the password Secret + SecretName string `json:"secretName"` + // Specifies the key holding the value of the Secret + //+kubebuilder:default:="password" + PasswordKey string `json:"passwordKey,omitempty"` +} + +// Defines the secret containing Certificates +type CertificateSecret struct { + // Specifies the name of the certificate Secret + SecretName string `json:"secretName"` + // Specifies the Certificate + Certificate string `json:"cert"` + // Specifies the Certificate Key + CertificateKey string `json:"key"` +} + +// Defines the secret containing Certificates +type TNSAdminSecret struct { + // Specifies the name of the TNS_ADMIN Secret + SecretName string `json:"secretName"` +} + +// Defines the secret containing Certificates +type DBWalletSecret struct { + // Specifies the name of the Database Wallet Secret + SecretName string `json:"secretName"` + // Specifies the Secret key name containing the Wallet + WalletName string `json:"walletName"` +} + +// OrdsSrvsStatus defines the observed state of OrdsSrvs +type OrdsSrvsStatus struct { + //** PLACE HOLDER + OrdsInstalled bool `json:"ordsInstalled,omitempty"` + //** PLACE HOLDER + // Indicates the current status of the resource + Status string `json:"status,omitempty"` + // Indicates the current Workload type of the resource + WorkloadType string `json:"workloadType,omitempty"` + // Indicates the ORDS version + ORDSVersion string `json:"ordsVersion,omitempty"` + // Indicates the HTTP port of the resource exposed by the pods + HTTPPort *int32 `json:"httpPort,omitempty"` + // Indicates the HTTPS port of the resource exposed by the pods + HTTPSPort *int32 `json:"httpsPort,omitempty"` + // Indicates the MongoAPI port of the resource exposed by the pods (if enabled) + MongoPort int32 `json:"mongoPort,omitempty"` + // Indicates if the resource is out-of-sync with the configuration + RestartRequired bool `json:"restartRequired"` + + // +operator-sdk:csv:customresourcedefinitions:type=status + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:JSONPath=".status.status",name="status",type="string" +//+kubebuilder:printcolumn:JSONPath=".status.workloadType",name="workloadType",type="string" +//+kubebuilder:printcolumn:JSONPath=".status.ordsVersion",name="ordsVersion",type="string" +//+kubebuilder:printcolumn:JSONPath=".status.httpPort",name="httpPort",type="integer" +//+kubebuilder:printcolumn:JSONPath=".status.httpsPort",name="httpsPort",type="integer" +//+kubebuilder:printcolumn:JSONPath=".status.mongoPort",name="MongoPort",type="integer" +//+kubebuilder:printcolumn:JSONPath=".status.restartRequired",name="restartRequired",type="boolean" +//+kubebuilder:printcolumn:JSONPath=".metadata.creationTimestamp",name="AGE",type="date" +//+kubebuilder:printcolumn:JSONPath=".status.ordsInstalled",name="OrdsInstalled",type="boolean" +//+kubebuilder:resource:path=ordssrvs,scope=Namespaced + +// OrdsSrvs is the Schema for the ordssrvs API +type OrdsSrvs struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec OrdsSrvsSpec `json:"spec,omitempty"` + Status OrdsSrvsStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// OrdsSrvsList contains a list of OrdsSrvs +type OrdsSrvsList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []OrdsSrvs `json:"items"` +} + +func init() { + SchemeBuilder.Register(&OrdsSrvs{}, &OrdsSrvsList{}) +} diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index f1ced358..f765c6b3 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -46,6 +46,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + timex "time" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -888,6 +889,21 @@ func (in *CatalogSpec) DeepCopy() *CatalogSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CertificateSecret) DeepCopyInto(out *CertificateSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateSecret. +func (in *CertificateSecret) DeepCopy() *CertificateSecret { + if in == nil { + return nil + } + out := new(CertificateSecret) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ConnectionStringProfile) DeepCopyInto(out *ConnectionStringProfile) { *out = *in @@ -923,6 +939,21 @@ func (in *ConnectionStringSpec) DeepCopy() *ConnectionStringSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBWalletSecret) DeepCopyInto(out *DBWalletSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBWalletSecret. +func (in *DBWalletSecret) DeepCopy() *DBWalletSecret { + if in == nil { + return nil + } + out := new(DBWalletSecret) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DbCloneConfig) DeepCopyInto(out *DbCloneConfig) { *out = *in @@ -1244,6 +1275,161 @@ func (in *EnvironmentVariable) DeepCopy() *EnvironmentVariable { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalSettings) DeepCopyInto(out *GlobalSettings) { + *out = *in + if in.CacheMetadataEnabled != nil { + in, out := &in.CacheMetadataEnabled, &out.CacheMetadataEnabled + *out = new(bool) + **out = **in + } + if in.CacheMetadataGraphQLExpireAfterAccess != nil { + in, out := &in.CacheMetadataGraphQLExpireAfterAccess, &out.CacheMetadataGraphQLExpireAfterAccess + *out = new(timex.Duration) + **out = **in + } + if in.CacheMetadataGraphQLExpireAfterWrite != nil { + in, out := &in.CacheMetadataGraphQLExpireAfterWrite, &out.CacheMetadataGraphQLExpireAfterWrite + *out = new(timex.Duration) + **out = **in + } + if in.CacheMetadataTimeout != nil { + in, out := &in.CacheMetadataTimeout, &out.CacheMetadataTimeout + *out = new(timex.Duration) + **out = **in + } + if in.CacheMetadataJWKSEnabled != nil { + in, out := &in.CacheMetadataJWKSEnabled, &out.CacheMetadataJWKSEnabled + *out = new(bool) + **out = **in + } + if in.CacheMetadataJWKSInitialCapacity != nil { + in, out := &in.CacheMetadataJWKSInitialCapacity, &out.CacheMetadataJWKSInitialCapacity + *out = new(int32) + **out = **in + } + if in.CacheMetadataJWKSMaximumSize != nil { + in, out := &in.CacheMetadataJWKSMaximumSize, &out.CacheMetadataJWKSMaximumSize + *out = new(int32) + **out = **in + } + if in.CacheMetadataJWKSExpireAfterAccess != nil { + in, out := &in.CacheMetadataJWKSExpireAfterAccess, &out.CacheMetadataJWKSExpireAfterAccess + *out = new(timex.Duration) + **out = **in + } + if in.CacheMetadataJWKSExpireAfterWrite != nil { + in, out := &in.CacheMetadataJWKSExpireAfterWrite, &out.CacheMetadataJWKSExpireAfterWrite + *out = new(timex.Duration) + **out = **in + } + if in.DatabaseAPIEnabled != nil { + in, out := &in.DatabaseAPIEnabled, &out.DatabaseAPIEnabled + *out = new(bool) + **out = **in + } + if in.DatabaseAPIManagementServicesDisabled != nil { + in, out := &in.DatabaseAPIManagementServicesDisabled, &out.DatabaseAPIManagementServicesDisabled + *out = new(bool) + **out = **in + } + if in.DBInvalidPoolTimeout != nil { + in, out := &in.DBInvalidPoolTimeout, &out.DBInvalidPoolTimeout + *out = new(timex.Duration) + **out = **in + } + if in.FeatureGraphQLMaxNestingDepth != nil { + in, out := &in.FeatureGraphQLMaxNestingDepth, &out.FeatureGraphQLMaxNestingDepth + *out = new(int32) + **out = **in + } + if in.SecurityCredentialsAttempts != nil { + in, out := &in.SecurityCredentialsAttempts, &out.SecurityCredentialsAttempts + *out = new(int32) + **out = **in + } + if in.SecurityCredentialsLockTime != nil { + in, out := &in.SecurityCredentialsLockTime, &out.SecurityCredentialsLockTime + *out = new(timex.Duration) + **out = **in + } + if in.StandaloneHTTPPort != nil { + in, out := &in.StandaloneHTTPPort, &out.StandaloneHTTPPort + *out = new(int32) + **out = **in + } + if in.StandaloneHTTPSPort != nil { + in, out := &in.StandaloneHTTPSPort, &out.StandaloneHTTPSPort + *out = new(int32) + **out = **in + } + if in.StandaloneStopTimeout != nil { + in, out := &in.StandaloneStopTimeout, &out.StandaloneStopTimeout + *out = new(timex.Duration) + **out = **in + } + if in.DebugPrintDebugToScreen != nil { + in, out := &in.DebugPrintDebugToScreen, &out.DebugPrintDebugToScreen + *out = new(bool) + **out = **in + } + if in.ICAPPort != nil { + in, out := &in.ICAPPort, &out.ICAPPort + *out = new(int32) + **out = **in + } + if in.ICAPSecurePort != nil { + in, out := &in.ICAPSecurePort, &out.ICAPSecurePort + *out = new(int32) + **out = **in + } + if in.MongoPort != nil { + in, out := &in.MongoPort, &out.MongoPort + *out = new(int32) + **out = **in + } + if in.MongoIdleTimeout != nil { + in, out := &in.MongoIdleTimeout, &out.MongoIdleTimeout + *out = new(timex.Duration) + **out = **in + } + if in.MongoOpTimeout != nil { + in, out := &in.MongoOpTimeout, &out.MongoOpTimeout + *out = new(timex.Duration) + **out = **in + } + if in.SecurityDisableDefaultExclusionList != nil { + in, out := &in.SecurityDisableDefaultExclusionList, &out.SecurityDisableDefaultExclusionList + *out = new(bool) + **out = **in + } + if in.SecurityMaxEntries != nil { + in, out := &in.SecurityMaxEntries, &out.SecurityMaxEntries + *out = new(int32) + **out = **in + } + if in.SecurityVerifySSL != nil { + in, out := &in.SecurityVerifySSL, &out.SecurityVerifySSL + *out = new(bool) + **out = **in + } + if in.CertSecret != nil { + in, out := &in.CertSecret, &out.CertSecret + *out = new(CertificateSecret) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalSettings. +func (in *GlobalSettings) DeepCopy() *GlobalSettings { + if in == nil { + return nil + } + out := new(GlobalSettings) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GsmServiceSpec) DeepCopyInto(out *GsmServiceSpec) { *out = *in @@ -2164,6 +2350,124 @@ func (in *ORDSPassword) DeepCopy() *ORDSPassword { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OrdsSrvs) DeepCopyInto(out *OrdsSrvs) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OrdsSrvs. +func (in *OrdsSrvs) DeepCopy() *OrdsSrvs { + if in == nil { + return nil + } + out := new(OrdsSrvs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OrdsSrvs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OrdsSrvsList) DeepCopyInto(out *OrdsSrvsList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OrdsSrvs, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OrdsSrvsList. +func (in *OrdsSrvsList) DeepCopy() *OrdsSrvsList { + if in == nil { + return nil + } + out := new(OrdsSrvsList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OrdsSrvsList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OrdsSrvsSpec) DeepCopyInto(out *OrdsSrvsSpec) { + *out = *in + in.GlobalSettings.DeepCopyInto(&out.GlobalSettings) + if in.PoolSettings != nil { + in, out := &in.PoolSettings, &out.PoolSettings + *out = make([]*PoolSettings, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(PoolSettings) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OrdsSrvsSpec. +func (in *OrdsSrvsSpec) DeepCopy() *OrdsSrvsSpec { + if in == nil { + return nil + } + out := new(OrdsSrvsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OrdsSrvsStatus) DeepCopyInto(out *OrdsSrvsStatus) { + *out = *in + if in.HTTPPort != nil { + in, out := &in.HTTPPort, &out.HTTPPort + *out = new(int32) + **out = **in + } + if in.HTTPSPort != nil { + in, out := &in.HTTPSPort, &out.HTTPSPort + *out = new(int32) + **out = **in + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OrdsSrvsStatus. +func (in *OrdsSrvsStatus) DeepCopy() *OrdsSrvsStatus { + if in == nil { + return nil + } + out := new(OrdsSrvsStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDB) DeepCopyInto(out *PDB) { *out = *in @@ -2518,6 +2822,21 @@ func (in *PITSpec) DeepCopy() *PITSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PasswordSecret) DeepCopyInto(out *PasswordSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswordSecret. +func (in *PasswordSecret) DeepCopy() *PasswordSecret { + if in == nil { + return nil + } + out := new(PasswordSecret) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PasswordSpec) DeepCopyInto(out *PasswordSpec) { *out = *in @@ -2535,6 +2854,159 @@ func (in *PasswordSpec) DeepCopy() *PasswordSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PoolSettings) DeepCopyInto(out *PoolSettings) { + *out = *in + out.DBSecret = in.DBSecret + out.DBAdminUserSecret = in.DBAdminUserSecret + out.DBCDBAdminUserSecret = in.DBCDBAdminUserSecret + if in.DBPoolDestroyTimeout != nil { + in, out := &in.DBPoolDestroyTimeout, &out.DBPoolDestroyTimeout + *out = new(timex.Duration) + **out = **in + } + if in.DebugTrackResources != nil { + in, out := &in.DebugTrackResources, &out.DebugTrackResources + *out = new(bool) + **out = **in + } + if in.FeatureOpenservicebrokerExclude != nil { + in, out := &in.FeatureOpenservicebrokerExclude, &out.FeatureOpenservicebrokerExclude + *out = new(bool) + **out = **in + } + if in.FeatureSDW != nil { + in, out := &in.FeatureSDW, &out.FeatureSDW + *out = new(bool) + **out = **in + } + if in.OwaTraceSql != nil { + in, out := &in.OwaTraceSql, &out.OwaTraceSql + *out = new(bool) + **out = **in + } + if in.SecurityJWTProfileEnabled != nil { + in, out := &in.SecurityJWTProfileEnabled, &out.SecurityJWTProfileEnabled + *out = new(bool) + **out = **in + } + if in.SecurityJWKSSize != nil { + in, out := &in.SecurityJWKSSize, &out.SecurityJWKSSize + *out = new(int32) + **out = **in + } + if in.SecurityJWKSConnectionTimeout != nil { + in, out := &in.SecurityJWKSConnectionTimeout, &out.SecurityJWKSConnectionTimeout + *out = new(timex.Duration) + **out = **in + } + if in.SecurityJWKSReadTimeout != nil { + in, out := &in.SecurityJWKSReadTimeout, &out.SecurityJWKSReadTimeout + *out = new(timex.Duration) + **out = **in + } + if in.SecurityJWKSRefreshInterval != nil { + in, out := &in.SecurityJWKSRefreshInterval, &out.SecurityJWKSRefreshInterval + *out = new(timex.Duration) + **out = **in + } + if in.SecurityJWTAllowedSkew != nil { + in, out := &in.SecurityJWTAllowedSkew, &out.SecurityJWTAllowedSkew + *out = new(timex.Duration) + **out = **in + } + if in.SecurityJWTAllowedAge != nil { + in, out := &in.SecurityJWTAllowedAge, &out.SecurityJWTAllowedAge + *out = new(timex.Duration) + **out = **in + } + if in.DBPort != nil { + in, out := &in.DBPort, &out.DBPort + *out = new(int32) + **out = **in + } + if in.JDBCInactivityTimeout != nil { + in, out := &in.JDBCInactivityTimeout, &out.JDBCInactivityTimeout + *out = new(int32) + **out = **in + } + if in.JDBCInitialLimit != nil { + in, out := &in.JDBCInitialLimit, &out.JDBCInitialLimit + *out = new(int32) + **out = **in + } + if in.JDBCMaxConnectionReuseCount != nil { + in, out := &in.JDBCMaxConnectionReuseCount, &out.JDBCMaxConnectionReuseCount + *out = new(int32) + **out = **in + } + if in.JDBCMaxConnectionReuseTime != nil { + in, out := &in.JDBCMaxConnectionReuseTime, &out.JDBCMaxConnectionReuseTime + *out = new(int32) + **out = **in + } + if in.JDBCSecondsToTrustIdleConnection != nil { + in, out := &in.JDBCSecondsToTrustIdleConnection, &out.JDBCSecondsToTrustIdleConnection + *out = new(int32) + **out = **in + } + if in.JDBCMaxLimit != nil { + in, out := &in.JDBCMaxLimit, &out.JDBCMaxLimit + *out = new(int32) + **out = **in + } + if in.JDBCAuthEnabled != nil { + in, out := &in.JDBCAuthEnabled, &out.JDBCAuthEnabled + *out = new(bool) + **out = **in + } + if in.JDBCMaxStatementsLimit != nil { + in, out := &in.JDBCMaxStatementsLimit, &out.JDBCMaxStatementsLimit + *out = new(int32) + **out = **in + } + if in.JDBCMinLimit != nil { + in, out := &in.JDBCMinLimit, &out.JDBCMinLimit + *out = new(int32) + **out = **in + } + if in.JDBCStatementTimeout != nil { + in, out := &in.JDBCStatementTimeout, &out.JDBCStatementTimeout + *out = new(int32) + **out = **in + } + if in.MiscPaginationMaxRows != nil { + in, out := &in.MiscPaginationMaxRows, &out.MiscPaginationMaxRows + *out = new(int32) + **out = **in + } + if in.RestEnabledSqlActive != nil { + in, out := &in.RestEnabledSqlActive, &out.RestEnabledSqlActive + *out = new(bool) + **out = **in + } + if in.DBWalletSecret != nil { + in, out := &in.DBWalletSecret, &out.DBWalletSecret + *out = new(DBWalletSecret) + **out = **in + } + if in.TNSAdminSecret != nil { + in, out := &in.TNSAdminSecret, &out.TNSAdminSecret + *out = new(TNSAdminSecret) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PoolSettings. +func (in *PoolSettings) DeepCopy() *PoolSettings { + if in == nil { + return nil + } + out := new(PoolSettings) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PortMapping) DeepCopyInto(out *PortMapping) { *out = *in @@ -2857,6 +3329,21 @@ func (in *TDESecret) DeepCopy() *TDESecret { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TNSAdminSecret) DeepCopyInto(out *TNSAdminSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TNSAdminSecret. +func (in *TNSAdminSecret) DeepCopy() *TNSAdminSecret { + if in == nil { + return nil + } + out := new(TNSAdminSecret) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in diff --git a/config/crd/bases/database.oracle.com_ordssrvs.yaml b/config/crd/bases/database.oracle.com_ordssrvs.yaml new file mode 100644 index 00000000..7bfa4951 --- /dev/null +++ b/config/crd/bases/database.oracle.com_ordssrvs.yaml @@ -0,0 +1,478 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: ordssrvs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OrdsSrvs + listKind: OrdsSrvsList + plural: ordssrvs + singular: ordssrvs + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: status + type: string + - jsonPath: .status.workloadType + name: workloadType + type: string + - jsonPath: .status.ordsVersion + name: ordsVersion + type: string + - jsonPath: .status.httpPort + name: httpPort + type: integer + - jsonPath: .status.httpsPort + name: httpsPort + type: integer + - jsonPath: .status.mongoPort + name: MongoPort + type: integer + - jsonPath: .status.restartRequired + name: restartRequired + type: boolean + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.ordsInstalled + name: OrdsInstalled + type: boolean + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + forceRestart: + type: boolean + globalSettings: + properties: + cache.metadata.enabled: + type: boolean + cache.metadata.graphql.expireAfterAccess: + format: int64 + type: integer + cache.metadata.graphql.expireAfterWrite: + format: int64 + type: integer + cache.metadata.jwks.enabled: + type: boolean + cache.metadata.jwks.expireAfterAccess: + format: int64 + type: integer + cache.metadata.jwks.expireAfterWrite: + format: int64 + type: integer + cache.metadata.jwks.initialCapacity: + format: int32 + type: integer + cache.metadata.jwks.maximumSize: + format: int32 + type: integer + cache.metadata.timeout: + format: int64 + type: integer + certSecret: + properties: + cert: + type: string + key: + type: string + secretName: + type: string + required: + - cert + - key + - secretName + type: object + database.api.enabled: + type: boolean + database.api.management.services.disabled: + type: boolean + db.invalidPoolTimeout: + format: int64 + type: integer + debug.printDebugToScreen: + type: boolean + enable.mongo.access.log: + default: false + type: boolean + enable.standalone.access.log: + default: false + type: boolean + error.responseFormat: + type: string + feature.grahpql.max.nesting.depth: + format: int32 + type: integer + icap.port: + format: int32 + type: integer + icap.secure.port: + format: int32 + type: integer + icap.server: + type: string + log.procedure: + type: boolean + mongo.enabled: + type: boolean + mongo.idle.timeout: + format: int64 + type: integer + mongo.op.timeout: + format: int64 + type: integer + mongo.port: + default: 27017 + format: int32 + type: integer + request.traceHeaderName: + type: string + security.credentials.attempts: + format: int32 + type: integer + security.credentials.lock.time: + format: int64 + type: integer + security.disableDefaultExclusionList: + type: boolean + security.exclusionList: + type: string + security.externalSessionTrustedOrigins: + type: string + security.forceHTTPS: + type: boolean + security.httpsHeaderCheck: + type: string + security.inclusionList: + type: string + security.maxEntries: + format: int32 + type: integer + security.verifySSL: + type: boolean + standalone.context.path: + default: /ords + type: string + standalone.http.port: + default: 8080 + format: int32 + type: integer + standalone.https.host: + type: string + standalone.https.port: + default: 8443 + format: int32 + type: integer + standalone.stop.timeout: + format: int64 + type: integer + type: object + image: + type: string + imagePullPolicy: + default: IfNotPresent + enum: + - IfNotPresent + - Always + - Never + type: string + imagePullSecrets: + type: string + poolSettings: + items: + properties: + apex.security.administrator.roles: + type: string + apex.security.user.roles: + type: string + autoUpgradeAPEX: + default: false + type: boolean + autoUpgradeORDS: + default: false + type: boolean + db.adminUser: + type: string + db.adminUser.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.cdb.adminUser: + type: string + db.cdb.adminUser.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.connectionType: + enum: + - basic + - tns + - customurl + type: string + db.credentialsSource: + enum: + - pool + - request + type: string + db.customURL: + type: string + db.hostname: + type: string + db.poolDestroyTimeout: + format: int64 + type: integer + db.port: + format: int32 + type: integer + db.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.servicename: + type: string + db.sid: + type: string + db.tnsAliasName: + type: string + db.username: + default: ORDS_PUBLIC_USER + type: string + db.wallet.zip.service: + type: string + dbWalletSecret: + properties: + secretName: + type: string + walletName: + type: string + required: + - secretName + - walletName + type: object + debug.trackResources: + type: boolean + feature.openservicebroker.exclude: + type: boolean + feature.sdw: + type: boolean + http.cookie.filter: + type: string + jdbc.DriverType: + enum: + - thin + - oci8 + type: string + jdbc.InactivityTimeout: + format: int32 + type: integer + jdbc.InitialLimit: + format: int32 + type: integer + jdbc.MaxConnectionReuseCount: + format: int32 + type: integer + jdbc.MaxConnectionReuseTime: + format: int32 + type: integer + jdbc.MaxLimit: + format: int32 + type: integer + jdbc.MaxStatementsLimit: + format: int32 + type: integer + jdbc.MinLimit: + format: int32 + type: integer + jdbc.SecondsToTrustIdleConnection: + format: int32 + type: integer + jdbc.auth.admin.role: + type: string + jdbc.auth.enabled: + type: boolean + jdbc.cleanup.mode: + type: string + jdbc.statementTimeout: + format: int32 + type: integer + misc.defaultPage: + type: string + misc.pagination.maxRows: + format: int32 + type: integer + owa.trace.sql: + type: boolean + plsql.gateway.mode: + enum: + - disabled + - direct + - proxied + type: string + poolName: + type: string + procedure.preProcess: + type: string + procedure.rest.preHook: + type: string + procedurePostProcess: + type: string + restEnabledSql.active: + type: boolean + security.jwks.connection.timeout: + format: int64 + type: integer + security.jwks.read.timeout: + format: int64 + type: integer + security.jwks.refresh.interval: + format: int64 + type: integer + security.jwks.size: + format: int32 + type: integer + security.jwt.allowed.age: + format: int64 + type: integer + security.jwt.allowed.skew: + format: int64 + type: integer + security.jwt.profile.enabled: + type: boolean + security.requestAuthenticationFunction: + type: string + security.requestValidationFunction: + default: ords_util.authorize_plsql_gateway + type: string + security.validationFunctionType: + enum: + - plsql + - javascript + type: string + soda.defaultLimit: + type: string + soda.maxLimit: + type: string + tnsAdminSecret: + properties: + secretName: + type: string + required: + - secretName + type: object + required: + - db.secret + - poolName + type: object + type: array + replicas: + default: 1 + format: int32 + minimum: 1 + type: integer + workloadType: + default: Deployment + enum: + - Deployment + - StatefulSet + - DaemonSet + type: string + required: + - globalSettings + - image + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + httpPort: + format: int32 + type: integer + httpsPort: + format: int32 + type: integer + mongoPort: + format: int32 + type: integer + ordsInstalled: + type: boolean + ordsVersion: + type: string + restartRequired: + type: boolean + status: + type: string + workloadType: + type: string + required: + - restartRequired + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 66e53ac4..743c8246 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -20,6 +20,7 @@ resources: - bases/observability.oracle.com_databaseobservers.yaml - bases/database.oracle.com_lrests.yaml - bases/database.oracle.com_lrpdbs.yaml +- bases/database.oracle.com_ordssrvs.yaml # +kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -40,6 +41,7 @@ patchesStrategicMerge: - patches/webhook_in_autonomouscontainerdatabases.yaml #- patches/webhook_in_lrests.yaml #- patches/webhook_in_lrpdbs.yaml +#- patches/webhook_in_ordssrvs.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. @@ -60,6 +62,7 @@ patchesStrategicMerge: - patches/cainjection_in_autonomouscontainerdatabases.yaml #- patches/cainjection_in_lrests.yaml #- patches/cainjection_in_lrpdbs.yaml +#- patches/cainjection_in_ordssrvs.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_database_ordssrvs.yaml b/config/crd/patches/cainjection_in_database_ordssrvs.yaml new file mode 100644 index 00000000..d2bfc8bf --- /dev/null +++ b/config/crd/patches/cainjection_in_database_ordssrvs.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: ordssrvs.database.oracle.com diff --git a/config/crd/patches/webhook_in_ordssrvs.yaml b/config/crd/patches/webhook_in_ordssrvs.yaml new file mode 100644 index 00000000..0c3d7637 --- /dev/null +++ b/config/crd/patches/webhook_in_ordssrvs.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: ordssrvs.database.oracle.com +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/rbac/ordssrvs_editor_role.yaml b/config/rbac/ordssrvs_editor_role.yaml new file mode 100644 index 00000000..bc4170f6 --- /dev/null +++ b/config/rbac/ordssrvs_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit ordssrvs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: ordssrvs-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - ordssrvs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - ordssrvs/status + verbs: + - get diff --git a/config/rbac/ordssrvs_viewer_role.yaml b/config/rbac/ordssrvs_viewer_role.yaml new file mode 100644 index 00000000..8880c17d --- /dev/null +++ b/config/rbac/ordssrvs_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view ordssrvs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: ordssrvs-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - ordssrvs + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - ordssrvs/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 9910f504..3a12386c 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -27,6 +27,18 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - configmaps/status + - daemonsets/status + - deployments/status + - services/status + - statefulsets/status + verbs: + - get + - patch + - update - apiGroups: - "" resources: @@ -35,6 +47,12 @@ rules: - get - list - watch +- apiGroups: + - "" + resources: + - secrets/status + verbs: + - get - apiGroups: - '''''' resources: @@ -57,6 +75,7 @@ rules: - apiGroups: - apps resources: + - daemonsets - deployments - pods - replicasets @@ -91,6 +110,7 @@ rules: - lrests - lrpdbs - oraclerestdataservices + - ordssrvs - pdbs - shardingdatabases - singleinstancedatabases @@ -114,6 +134,7 @@ rules: - lrests/status - lrpdbs/status - oraclerestdataservices/status + - ordssrvs/status - pdbs/status - shardingdatabases/status - singleinstancedatabases/status @@ -147,6 +168,7 @@ rules: - dataguardbrokers/finalizers - lrests/finalizers - oraclerestdataservices/finalizers + - ordssrvs/finalizers - singleinstancedatabases/finalizers verbs: - update diff --git a/config/samples/database_v4_ordssrvs.yaml b/config/samples/database_v4_ordssrvs.yaml new file mode 100644 index 00000000..1c346139 --- /dev/null +++ b/config/samples/database_v4_ordssrvs.yaml @@ -0,0 +1,6 @@ +apiVersion: database.oracle.com/v4 +kind: OrdsSrvs +metadata: + name: ordssrvs-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 2dbdb7f6..40abec3b 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -34,4 +34,5 @@ resources: - database_v4_dbcssystem.yaml - database_v4_lrest.yaml - database_v4_lrpdb.yaml +- database_v4_ordssrvs.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/controllers/database/lrest_controller.go b/controllers/database/lrest_controller.go index 5ce90053..c2b9a5a7 100644 --- a/controllers/database/lrest_controller.go +++ b/controllers/database/lrest_controller.go @@ -403,7 +403,7 @@ func (r *LRESTReconciler) createPodSpec(lrest *dbapi.LREST) corev1.PodSpec { Image: lrest.Spec.LRESTImage, Name: lrest.Name + "-init", ImagePullPolicy: corev1.PullIfNotPresent, - SecurityContext: securityContextDefine(), + SecurityContext: securityContextDefineLrest(), Command: []string{"echo test > /opt/oracle/lrest/certificates/tests"}, Env: func() []corev1.EnvVar { return []corev1.EnvVar{ @@ -423,7 +423,7 @@ func (r *LRESTReconciler) createPodSpec(lrest *dbapi.LREST) corev1.PodSpec { Image: lrest.Spec.LRESTImage, Name: lrest.Name + "-lrest", ImagePullPolicy: corev1.PullIfNotPresent, - SecurityContext: securityContextDefine(), + SecurityContext: securityContextDefineLrest(), VolumeMounts: []corev1.VolumeMount{ { MountPath: "/opt/oracle/lrest/certificates", @@ -991,7 +991,7 @@ func (r *LRESTReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func securityContextDefine() *corev1.SecurityContext { +func securityContextDefineLrest() *corev1.SecurityContext { return &corev1.SecurityContext{ RunAsNonRoot: &[]bool{true}[0], RunAsUser: &[]int64{54321}[0], diff --git a/controllers/database/ordssrvs_controller.go b/controllers/database/ordssrvs_controller.go new file mode 100644 index 00000000..87efd461 --- /dev/null +++ b/controllers/database/ordssrvs_controller.go @@ -0,0 +1,1055 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "time" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + // dbapi "example.com/oracle-ords-operator/api/v1" + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" +) + +// Definitions of Standards +const ( + ordsSABase = "/opt/oracle/sa" + serviceHTTPPortName = "svc-http-port" + serviceHTTPSPortName = "svc-https-port" + serviceMongoPortName = "svc-mongo-port" + targetHTTPPortName = "pod-http-port" + targetHTTPSPortName = "pod-https-port" + targetMongoPortName = "pod-mongo-port" + globalConfigMapName = "settings-global" + poolConfigPreName = "settings-" // Append PoolName + controllerLabelKey = "oracle.com/ords-operator-filter" + controllerLabelVal = "oracle-database-operator" + specHashLabel = "oracle.com/ords-operator-spec-hash" +) + +// Definitions to manage status conditions +const ( + // typeAvailableORDS represents the status of the Workload reconciliation + typeAvailableORDS = "Available" + // typeUnsyncedORDS represents the status used when the configuration has changed but the Workload has not been restarted. + typeUnsyncedORDS = "Unsynced" +) + +// Trigger a restart of Pods on Config Changes +var RestartPods bool = false + +// OrdsSrvsReconciler reconciles a OrdsSrvs object +type OrdsSrvsReconciler struct { + client.Client + Scheme *runtime.Scheme + Recorder record.EventRecorder +} + +//+kubebuilder:rbac:groups=database.oracle.com,resources=ordssrvs,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=database.oracle.com,resources=ordssrvs/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=database.oracle.com,resources=ordssrvs/finalizers,verbs=update +//+kubebuilder:rbac:groups=core,resources=events,verbs=create;patch +//+kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=configmaps/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch +//+kubebuilder:rbac:groups=core,resources=secrets/status,verbs=get +//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=services/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=deployments/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=daemonsets/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=statefulsets/status,verbs=get;update;patch + +// SetupWithManager sets up the controller with the Manager. +func (r *OrdsSrvsReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbapi.OrdsSrvs{}). + Owns(&corev1.ConfigMap{}). + Owns(&corev1.Secret{}). + Owns(&appsv1.Deployment{}). + Owns(&appsv1.StatefulSet{}). + Owns(&appsv1.DaemonSet{}). + Owns(&corev1.Service{}). + Complete(r) +} + +func (r *OrdsSrvsReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logr := log.FromContext(ctx) + ords := &dbapi.OrdsSrvs{} + + // Check if resource exists or was deleted + if err := r.Get(ctx, req.NamespacedName, ords); err != nil { + if apierrors.IsNotFound(err) { + logr.Info("Resource deleted") + return ctrl.Result{}, nil + } + logr.Error(err, "Error retrieving resource") + return ctrl.Result{Requeue: true, RequeueAfter: time.Minute}, err + } + + // Set the status as Unknown when no status are available + if ords.Status.Conditions == nil || len(ords.Status.Conditions) == 0 { + condition := metav1.Condition{Type: typeUnsyncedORDS, Status: metav1.ConditionUnknown, Reason: "Reconciling", Message: "Starting reconciliation"} + if err := r.SetStatus(ctx, req, ords, condition); err != nil { + return ctrl.Result{}, err + } + } + + // ConfigMap - Init Script + if err := r.ConfigMapReconcile(ctx, ords, ords.Name+"-"+"init-script", 0); err != nil { + logr.Error(err, "Error in ConfigMapReconcile (init-script)") + return ctrl.Result{}, err + } + + // ConfigMap - Global Settings + if err := r.ConfigMapReconcile(ctx, ords, ords.Name+"-"+globalConfigMapName, 0); err != nil { + logr.Error(err, "Error in ConfigMapReconcile (Global)") + return ctrl.Result{}, err + } + + // ConfigMap - Pool Settings + definedPools := make(map[string]bool) + for i := 0; i < len(ords.Spec.PoolSettings); i++ { + poolName := strings.ToLower(ords.Spec.PoolSettings[i].PoolName) + poolConfigMapName := ords.Name + "-" + poolConfigPreName + poolName + if definedPools[poolConfigMapName] { + return ctrl.Result{}, errors.New("poolName: " + poolName + " is not unique") + } + definedPools[poolConfigMapName] = true + if err := r.ConfigMapReconcile(ctx, ords, poolConfigMapName, i); err != nil { + logr.Error(err, "Error in ConfigMapReconcile (Pools)") + return ctrl.Result{}, err + } + } + if err := r.ConfigMapDelete(ctx, req, ords, definedPools); err != nil { + logr.Error(err, "Error in ConfigMapDelete (Pools)") + return ctrl.Result{}, err + } + if err := r.Get(ctx, req.NamespacedName, ords); err != nil { + logr.Error(err, "Failed to re-fetch") + return ctrl.Result{}, err + } + + // // Secrets - Pool Settings + // for i := 0; i < len(ords.Spec.PoolSettings); i++ { + // if err := r.SecretsReconcile(ctx, ords, i); err != nil { + // logr.Error(err, "Error in SecretsReconcile (Pools)") + // return ctrl.Result{}, err + // } + // } + + // Set the Type as Unsynced when a pod restart is required + if RestartPods { + condition := metav1.Condition{Type: typeUnsyncedORDS, Status: metav1.ConditionTrue, Reason: "Unsynced", Message: "Configurations have changed"} + if err := r.SetStatus(ctx, req, ords, condition); err != nil { + return ctrl.Result{}, err + } + } + + // Workloads + if err := r.WorkloadReconcile(ctx, req, ords, ords.Spec.WorkloadType); err != nil { + logr.Error(err, "Error in WorkloadReconcile") + return ctrl.Result{}, err + } + if err := r.WorkloadDelete(ctx, req, ords, ords.Spec.WorkloadType); err != nil { + logr.Error(err, "Error in WorkloadDelete") + return ctrl.Result{}, err + } + if err := r.Get(ctx, req.NamespacedName, ords); err != nil { + logr.Error(err, "Failed to re-fetch") + return ctrl.Result{}, err + } + + // Service + if err := r.ServiceReconcile(ctx, ords); err != nil { + logr.Error(err, "Error in ServiceReconcile") + return ctrl.Result{}, err + } + + // Set the Type as Available when a pod restart is not required + if !RestartPods { + condition := metav1.Condition{Type: typeAvailableORDS, Status: metav1.ConditionTrue, Reason: "Available", Message: "Workload in Sync"} + if err := r.SetStatus(ctx, req, ords, condition); err != nil { + return ctrl.Result{}, err + } + } + if err := r.Get(ctx, req.NamespacedName, ords); err != nil { + logr.Error(err, "Failed to re-fetch") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +/************************************************ + * Status + *************************************************/ +func (r *OrdsSrvsReconciler) SetStatus(ctx context.Context, req ctrl.Request, ords *dbapi.OrdsSrvs, statusCondition metav1.Condition) error { + logr := log.FromContext(ctx).WithName("SetStatus") + + // Fetch before Status Update + if err := r.Get(ctx, req.NamespacedName, ords); err != nil { + logr.Error(err, "Failed to re-fetch") + return err + } + var readyWorkload int32 + var desiredWorkload int32 + switch ords.Spec.WorkloadType { + //nolint:goconst + case "StatefulSet": + workload := &appsv1.StatefulSet{} + if err := r.Get(ctx, types.NamespacedName{Name: ords.Name, Namespace: ords.Namespace}, workload); err != nil { + logr.Info("StatefulSet not ready") + } + readyWorkload = workload.Status.ReadyReplicas + desiredWorkload = workload.Status.Replicas + //nolint:goconst + case "DaemonSet": + workload := &appsv1.DaemonSet{} + if err := r.Get(ctx, types.NamespacedName{Name: ords.Name, Namespace: ords.Namespace}, workload); err != nil { + logr.Info("DaemonSet not ready") + } + readyWorkload = workload.Status.NumberReady + desiredWorkload = workload.Status.DesiredNumberScheduled + default: + workload := &appsv1.Deployment{} + if err := r.Get(ctx, types.NamespacedName{Name: ords.Name, Namespace: ords.Namespace}, workload); err != nil { + logr.Info("Deployment not ready") + } + readyWorkload = workload.Status.ReadyReplicas + desiredWorkload = workload.Status.Replicas + } + + var workloadStatus string + if readyWorkload == 0 { + workloadStatus = "Preparing" + } else if readyWorkload == desiredWorkload { + workloadStatus = "Healthy" + ords.Status.OrdsInstalled = true + } else { + workloadStatus = "Progressing" + } + + mongoPort := int32(0) + if ords.Spec.GlobalSettings.MongoEnabled { + mongoPort = *ords.Spec.GlobalSettings.MongoPort + } + + meta.SetStatusCondition(&ords.Status.Conditions, statusCondition) + ords.Status.Status = workloadStatus + ords.Status.WorkloadType = ords.Spec.WorkloadType + ords.Status.ORDSVersion = strings.Split(ords.Spec.Image, ":")[1] + ords.Status.HTTPPort = ords.Spec.GlobalSettings.StandaloneHTTPPort + ords.Status.HTTPSPort = ords.Spec.GlobalSettings.StandaloneHTTPSPort + ords.Status.MongoPort = mongoPort + ords.Status.RestartRequired = RestartPods + if err := r.Status().Update(ctx, ords); err != nil { + logr.Error(err, "Failed to update Status") + return err + } + return nil +} + +/************************************************ + * ConfigMaps + *************************************************/ +func (r *OrdsSrvsReconciler) ConfigMapReconcile(ctx context.Context, ords *dbapi.OrdsSrvs, configMapName string, poolIndex int) (err error) { + logr := log.FromContext(ctx).WithName("ConfigMapReconcile") + desiredConfigMap := r.ConfigMapDefine(ctx, ords, configMapName, poolIndex) + + // Create if ConfigMap not found + definedConfigMap := &corev1.ConfigMap{} + if err = r.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: ords.Namespace}, definedConfigMap); err != nil { + if apierrors.IsNotFound(err) { + if err := r.Create(ctx, desiredConfigMap); err != nil { + return err + } + logr.Info("Created: " + configMapName) + RestartPods = true + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Create", "ConfigMap %s Created", configMapName) + // Requery for comparison + if err := r.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: ords.Namespace}, definedConfigMap); err != nil { + return err + } + } else { + return err + } + } + if !equality.Semantic.DeepEqual(definedConfigMap.Data, desiredConfigMap.Data) { + if err = r.Update(ctx, desiredConfigMap); err != nil { + return err + } + logr.Info("Updated: " + configMapName) + RestartPods = true + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Update", "ConfigMap %s Updated", configMapName) + } + return nil +} + +/************************************************ + * Secrets - TODO (Watch and set RestartPods) + *************************************************/ +// func (r *OrdsSrvsReconciler) SecretsReconcile(ctx context.Context, ords *dbapi.OrdsSrvs, poolIndex int) (err error) { +// logr := log.FromContext(ctx).WithName("SecretsReconcile") +// definedSecret := &corev1.Secret{} + +// // Want to set ownership on the Secret for watching; also detects if TNS_ADMIN is needed. +// if ords.Spec.PoolSettings[i].DBSecret != nil { +// } +// if ords.Spec.PoolSettings[i].DBAdminUserSecret != nil { +// } +// if ords.Spec.PoolSettings[i].DBCDBAdminUserSecret != nil { +// } +// if ords.Spec.PoolSettings[i].TNSAdminSecret != nil { +// } +// if ords.Spec.PoolSettings[i].DBWalletSecret != nil { +// } + +// if ords.Spec.PoolSettings[i].TNSAdminSecret != nil { +// tnsSecretName := ords.Spec.PoolSettings[i].TNSAdminSecret.SecretName +// definedSecret := &corev1.Secret{} +// if err = r.Get(ctx, types.NamespacedName{Name: tnsSecretName, Namespace: ords.Namespace}, definedSecret); err != nil { +// ojdbcPropertiesData, ok := secret.Data["ojdbc.properties"] +// if ok { +// if err = r.Update(ctx, desiredConfigMap); err != nil { +// return err +// } +// } +// } +// } + +// return nil +// } + +/************************************************ + * Workloads + *************************************************/ +func (r *OrdsSrvsReconciler) WorkloadReconcile(ctx context.Context, req ctrl.Request, ords *dbapi.OrdsSrvs, kind string) (err error) { + logr := log.FromContext(ctx).WithName("WorkloadReconcile") + objectMeta := objectMetaDefine(ords, ords.Name) + selector := selectorDefine(ords) + template := podTemplateSpecDefine(ords) + + var desiredWorkload client.Object + var desiredSpecHash string + var definedSpecHash string + + switch kind { + case "StatefulSet": + desiredWorkload = &appsv1.StatefulSet{ + ObjectMeta: objectMeta, + Spec: appsv1.StatefulSetSpec{ + Replicas: &ords.Spec.Replicas, + Selector: &selector, + Template: template, + }, + } + desiredSpecHash = generateSpecHash(desiredWorkload.(*appsv1.StatefulSet).Spec) + desiredWorkload.(*appsv1.StatefulSet).ObjectMeta.Labels[specHashLabel] = desiredSpecHash + case "DaemonSet": + desiredWorkload = &appsv1.DaemonSet{ + ObjectMeta: objectMeta, + Spec: appsv1.DaemonSetSpec{ + Selector: &selector, + Template: template, + }, + } + desiredSpecHash = generateSpecHash(desiredWorkload.(*appsv1.DaemonSet).Spec) + desiredWorkload.(*appsv1.DaemonSet).ObjectMeta.Labels[specHashLabel] = desiredSpecHash + default: + desiredWorkload = &appsv1.Deployment{ + ObjectMeta: objectMeta, + Spec: appsv1.DeploymentSpec{ + Replicas: &ords.Spec.Replicas, + Selector: &selector, + Template: template, + }, + } + desiredSpecHash = generateSpecHash(desiredWorkload.(*appsv1.Deployment).Spec) + desiredWorkload.(*appsv1.Deployment).ObjectMeta.Labels[specHashLabel] = desiredSpecHash + } + + if err := ctrl.SetControllerReference(ords, desiredWorkload, r.Scheme); err != nil { + return err + } + + definedWorkload := reflect.New(reflect.TypeOf(desiredWorkload).Elem()).Interface().(client.Object) + if err = r.Get(ctx, types.NamespacedName{Name: ords.Name, Namespace: ords.Namespace}, definedWorkload); err != nil { + if apierrors.IsNotFound(err) { + if err := r.Create(ctx, desiredWorkload); err != nil { + condition := metav1.Condition{ + Type: typeAvailableORDS, + Status: metav1.ConditionFalse, + Reason: "Reconciling", + Message: fmt.Sprintf("Failed to create %s for the custom resource (%s): (%s)", kind, ords.Name, err), + } + if statusErr := r.SetStatus(ctx, req, ords, condition); statusErr != nil { + return statusErr + } + return err + } + logr.Info("Created: " + kind) + RestartPods = false + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Create", "Created %s", kind) + + return nil + } else { + return err + } + } + + definedLabelsField := reflect.ValueOf(definedWorkload).Elem().FieldByName("ObjectMeta").FieldByName("Labels") + if definedLabelsField.IsValid() { + specHashValue := definedLabelsField.MapIndex(reflect.ValueOf(specHashLabel)) + if specHashValue.IsValid() { + definedSpecHash = specHashValue.Interface().(string) + } else { + return err + } + } + + if desiredSpecHash != definedSpecHash { + logr.Info("Syncing Workload " + kind + " with new configuration") + if err := r.Client.Update(ctx, desiredWorkload); err != nil { + return err + } + RestartPods = true + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Update", "Updated %s", kind) + } + + if RestartPods && ords.Spec.ForceRestart { + logr.Info("Cycling: " + kind) + labelsField := reflect.ValueOf(desiredWorkload).Elem().FieldByName("Spec").FieldByName("Template").FieldByName("ObjectMeta").FieldByName("Labels") + if labelsField.IsValid() { + labels := labelsField.Interface().(map[string]string) + labels["configMapChanged"] = time.Now().Format("20060102T150405Z") + labelsField.Set(reflect.ValueOf(labels)) + if err := r.Update(ctx, desiredWorkload); err != nil { + return err + } + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Restart", "Restarted %s", kind) + RestartPods = false + } + } + + return nil +} + +// Service +func (r *OrdsSrvsReconciler) ServiceReconcile(ctx context.Context, ords *dbapi.OrdsSrvs) (err error) { + logr := log.FromContext(ctx).WithName("ServiceReconcile") + + HTTPport := *ords.Spec.GlobalSettings.StandaloneHTTPPort + HTTPSport := *ords.Spec.GlobalSettings.StandaloneHTTPSPort + MongoPort := *ords.Spec.GlobalSettings.MongoPort + + desiredService := r.ServiceDefine(ctx, ords, HTTPport, HTTPSport, MongoPort) + + definedService := &corev1.Service{} + if err = r.Get(ctx, types.NamespacedName{Name: ords.Name, Namespace: ords.Namespace}, definedService); err != nil { + if apierrors.IsNotFound(err) { + if err := r.Create(ctx, desiredService); err != nil { + return err + } + logr.Info("Created: Service") + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Create", "Service %s Created", ords.Name) + // Requery for comparison + if err := r.Get(ctx, types.NamespacedName{Name: ords.Name, Namespace: ords.Namespace}, definedService); err != nil { + return err + } + } else { + return err + } + } + + deisredPortCount := len(desiredService.Spec.Ports) + definedPortCount := len(definedService.Spec.Ports) + + if deisredPortCount != definedPortCount { + if err := r.Update(ctx, desiredService); err != nil { + return err + } + } + + for _, existingPort := range definedService.Spec.Ports { + if existingPort.Name == serviceHTTPPortName { + if existingPort.Port != HTTPport { + if err := r.Update(ctx, desiredService); err != nil { + return err + } + logr.Info("Updated HTTP Service Port: " + existingPort.Name) + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Update", "Service HTTP Port %s Updated", existingPort.Name) + } + } + if existingPort.Name == serviceHTTPSPortName { + if existingPort.Port != HTTPSport { + if err := r.Update(ctx, desiredService); err != nil { + return err + } + logr.Info("Updated HTTPS Service Port: " + existingPort.Name) + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Update", "Service HTTPS Port %s Updated", existingPort.Name) + } + } + if existingPort.Name == serviceMongoPortName { + if existingPort.Port != MongoPort { + if err := r.Update(ctx, desiredService); err != nil { + return err + } + logr.Info("Updated Mongo Service Port: " + existingPort.Name) + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Update", "Service Mongo Port %s Updated", existingPort.Name) + } + } + } + return nil +} + +/* +************************************************ + - Definers + +************************************************* +*/ +func objectMetaDefine(ords *dbapi.OrdsSrvs, name string) metav1.ObjectMeta { + labels := getLabels(ords.Name) + return metav1.ObjectMeta{ + Name: name, + Namespace: ords.Namespace, + Labels: labels, + } +} + +func selectorDefine(ords *dbapi.OrdsSrvs) metav1.LabelSelector { + labels := getLabels(ords.Name) + return metav1.LabelSelector{ + MatchLabels: labels, + } +} + +func podTemplateSpecDefine(ords *dbapi.OrdsSrvs) corev1.PodTemplateSpec { + labels := getLabels(ords.Name) + specVolumes, specVolumeMounts := VolumesDefine(ords) + + envPorts := []corev1.ContainerPort{ + { + ContainerPort: *ords.Spec.GlobalSettings.StandaloneHTTPPort, + Name: targetHTTPPortName, + }, + { + ContainerPort: *ords.Spec.GlobalSettings.StandaloneHTTPSPort, + Name: targetHTTPSPortName, + }, + } + + if ords.Spec.GlobalSettings.MongoEnabled { + mongoPort := corev1.ContainerPort{ + ContainerPort: *ords.Spec.GlobalSettings.MongoPort, + Name: targetMongoPortName, + } + envPorts = append(envPorts, mongoPort) + } + + // Environment From Source + podSpecTemplate := + corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + }, + Spec: corev1.PodSpec{ + Volumes: specVolumes, + SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: &[]bool{true}[0], + FSGroup: &[]int64{54321}[0], + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + }, + InitContainers: []corev1.Container{{ + Image: ords.Spec.Image, + Name: ords.Name + "-init", + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: securityContextDefine(), + //Command: []string{"ls", "-ltr"}, + Command: []string{"sh", "-c", ordsSABase + "/bin/init_script.sh"}, + Env: envDefine(ords, true), + VolumeMounts: specVolumeMounts, + }}, + Containers: []corev1.Container{{ + Image: ords.Spec.Image, + Name: ords.Name, + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: securityContextDefine(), + Ports: envPorts, + //Command: []string{"sh", "-c", "tail -f /dev/null"}, + //Command: []string{"/usr/sbin/init"}, + Command: []string{"/bin/bash", "-c", "ords --config $ORDS_CONFIG serve --apex-images /opt/oracle/apex/$APEX_VER/images --debug"}, + Env: envDefine(ords, false), + //Env: envDefine(ords, true), + VolumeMounts: specVolumeMounts, + }}}, + } + + return podSpecTemplate +} + +// Volumes +func VolumesDefine(ords *dbapi.OrdsSrvs) ([]corev1.Volume, []corev1.VolumeMount) { + // Initialize the slice to hold specifications + var volumes []corev1.Volume + var volumeMounts []corev1.VolumeMount + + // SecretHelper + secretHelperVolume := volumeBuild(ords.Name+"-"+"init-script", "ConfigMap", 0770) + secretHelperVolumeMount := volumeMountBuild(ords.Name+"-"+"init-script", ordsSABase+"/bin", true) + + volumes = append(volumes, secretHelperVolume) + volumeMounts = append(volumeMounts, secretHelperVolumeMount) + + // Build volume specifications for globalSettings + standaloneVolume := volumeBuild("standalone", "EmptyDir") + standaloneVolumeMount := volumeMountBuild("standalone", ordsSABase+"/config/global/standalone/", false) + + globalWalletVolume := volumeBuild("sa-wallet-global", "EmptyDir") + globalWalletVolumeMount := volumeMountBuild("sa-wallet-global", ordsSABase+"/config/global/wallet/", false) + + globalLogVolume := volumeBuild("sa-log-global", "EmptyDir") + globalLogVolumeMount := volumeMountBuild("sa-log-global", ordsSABase+"/log/global/", false) + + globalConfigVolume := volumeBuild(ords.Name+"-"+globalConfigMapName, "ConfigMap") + globalConfigVolumeMount := volumeMountBuild(ords.Name+"-"+globalConfigMapName, ordsSABase+"/config/global/", true) + + globalDocRootVolume := volumeBuild("sa-doc-root", "EmptyDir") + globalDocRootVolumeMount := volumeMountBuild("sa-doc-root", ordsSABase+"/config/global/doc_root/", false) + + volumes = append(volumes, standaloneVolume, globalWalletVolume, globalLogVolume, globalConfigVolume, globalDocRootVolume) + volumeMounts = append(volumeMounts, standaloneVolumeMount, globalWalletVolumeMount, globalLogVolumeMount, globalConfigVolumeMount, globalDocRootVolumeMount) + + if ords.Spec.GlobalSettings.CertSecret != nil { + globalCertVolume := volumeBuild(ords.Spec.GlobalSettings.CertSecret.SecretName, "Secret") + globalCertVolumeMount := volumeMountBuild(ords.Spec.GlobalSettings.CertSecret.SecretName, ordsSABase+"/config/certficate/", true) + + volumes = append(volumes, globalCertVolume) + volumeMounts = append(volumeMounts, globalCertVolumeMount) + } + + // Build volume specifications for each pool in poolSettings + definedWalletSecret := make(map[string]bool) + definedTNSSecret := make(map[string]bool) + for i := 0; i < len(ords.Spec.PoolSettings); i++ { + poolName := strings.ToLower(ords.Spec.PoolSettings[i].PoolName) + + poolWalletName := "sa-wallet-" + poolName + poolWalletVolume := volumeBuild(poolWalletName, "EmptyDir") + poolWalletVolumeMount := volumeMountBuild(poolWalletName, ordsSABase+"/config/databases/"+poolName+"/wallet/", false) + + poolConfigName := ords.Name + "-" + poolConfigPreName + poolName + poolConfigVolume := volumeBuild(poolConfigName, "ConfigMap") + poolConfigVolumeMount := volumeMountBuild(poolConfigName, ordsSABase+"/config/databases/"+poolName+"/", true) + + volumes = append(volumes, poolWalletVolume, poolConfigVolume) + volumeMounts = append(volumeMounts, poolWalletVolumeMount, poolConfigVolumeMount) + + if ords.Spec.PoolSettings[i].DBWalletSecret != nil { + walletSecretName := ords.Spec.PoolSettings[i].DBWalletSecret.SecretName + if !definedWalletSecret[walletSecretName] { + // Only create the volume once + poolDBWalletVolume := volumeBuild(walletSecretName, "Secret") + volumes = append(volumes, poolDBWalletVolume) + definedWalletSecret[walletSecretName] = true + } + poolDBWalletVolumeMount := volumeMountBuild(walletSecretName, ordsSABase+"/config/databases/"+poolName+"/network/admin/", true) + volumeMounts = append(volumeMounts, poolDBWalletVolumeMount) + } + + if ords.Spec.PoolSettings[i].TNSAdminSecret != nil { + tnsSecretName := ords.Spec.PoolSettings[i].TNSAdminSecret.SecretName + if !definedTNSSecret[tnsSecretName] { + // Only create the volume once + poolTNSAdminVolume := volumeBuild(tnsSecretName, "Secret") + volumes = append(volumes, poolTNSAdminVolume) + definedTNSSecret[tnsSecretName] = true + } + poolTNSAdminVolumeMount := volumeMountBuild(tnsSecretName, ordsSABase+"/config/databases/"+poolName+"/network/admin/", true) + volumeMounts = append(volumeMounts, poolTNSAdminVolumeMount) + } + } + return volumes, volumeMounts +} + +func volumeMountBuild(name string, path string, readOnly bool) corev1.VolumeMount { + return corev1.VolumeMount{ + Name: name, + MountPath: path, + ReadOnly: readOnly, + } +} + +func volumeBuild(name string, source string, mode ...int32) corev1.Volume { + defaultMode := int32(0660) + if len(mode) > 0 { + defaultMode = mode[0] + } + switch source { + case "ConfigMap": + return corev1.Volume{ + Name: name, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + DefaultMode: &defaultMode, + LocalObjectReference: corev1.LocalObjectReference{ + Name: name, + }, + }, + }, + } + case "Secret": + return corev1.Volume{ + Name: name, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: name, + }, + }, + } + case "EmptyDir": + return corev1.Volume{ + Name: name, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + } + default: + return corev1.Volume{} + } +} + +// Service +func (r *OrdsSrvsReconciler) ServiceDefine(ctx context.Context, ords *dbapi.OrdsSrvs, HTTPport int32, HTTPSport int32, MongoPort int32) *corev1.Service { + labels := getLabels(ords.Name) + + servicePorts := []corev1.ServicePort{ + { + Name: serviceHTTPPortName, + Protocol: corev1.ProtocolTCP, + Port: HTTPport, + TargetPort: intstr.FromString(targetHTTPPortName), + }, + { + Name: serviceHTTPSPortName, + Protocol: corev1.ProtocolTCP, + Port: HTTPSport, + TargetPort: intstr.FromString(targetHTTPSPortName), + }, + } + + if ords.Spec.GlobalSettings.MongoEnabled { + mongoServicePort := corev1.ServicePort{ + Name: serviceMongoPortName, + Protocol: corev1.ProtocolTCP, + Port: MongoPort, + TargetPort: intstr.FromString(targetMongoPortName), + } + servicePorts = append(servicePorts, mongoServicePort) + } + + objectMeta := objectMetaDefine(ords, ords.Name) + def := &corev1.Service{ + ObjectMeta: objectMeta, + Spec: corev1.ServiceSpec{ + Selector: labels, + Ports: servicePorts, + }, + } + + // Set the ownerRef + if err := ctrl.SetControllerReference(ords, def, r.Scheme); err != nil { + return nil + } + return def +} + +func securityContextDefine() *corev1.SecurityContext { + return &corev1.SecurityContext{ + RunAsNonRoot: &[]bool{true}[0], + RunAsUser: &[]int64{54321}[0], + AllowPrivilegeEscalation: &[]bool{false}[0], + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{ + "ALL", + }, + }, + } +} + +func envDefine(ords *dbapi.OrdsSrvs, initContainer bool) []corev1.EnvVar { + envVarSecrets := []corev1.EnvVar{ + { + Name: "ORDS_CONFIG", + Value: ordsSABase + "/config", + }, + { + Name: "JAVA_TOOL_OPTIONS", + Value: "-Doracle.ml.version_check=false", + }, + } + // Limitation case for ADB/mTLS/OraOper edge + if len(ords.Spec.PoolSettings) == 1 { + poolName := strings.ToLower(ords.Spec.PoolSettings[0].PoolName) + tnsAdmin := corev1.EnvVar{ + Name: "TNS_ADMIN", + Value: ordsSABase + "/config/databases/" + poolName + "/network/admin/", + } + envVarSecrets = append(envVarSecrets, tnsAdmin) + } + if initContainer { + for i := 0; i < len(ords.Spec.PoolSettings); i++ { + poolName := strings.ReplaceAll(strings.ToLower(ords.Spec.PoolSettings[i].PoolName), "-", "_") + dbSecret := corev1.EnvVar{ + Name: poolName + "_dbsecret", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: ords.Spec.PoolSettings[i].DBSecret.SecretName, + }, + Key: ords.Spec.PoolSettings[i].DBSecret.PasswordKey, + }, + }, + } + envVarSecrets = append(envVarSecrets, dbSecret) + if ords.Spec.PoolSettings[i].DBAdminUserSecret.SecretName != "" { + autoUpgradeORDSEnv := corev1.EnvVar{ + Name: poolName + "_autoupgrade_ords", + Value: strconv.FormatBool(ords.Spec.PoolSettings[i].AutoUpgradeORDS), + } + autoUpgradeAPEXEnv := corev1.EnvVar{ + Name: poolName + "_autoupgrade_apex", + Value: strconv.FormatBool(ords.Spec.PoolSettings[i].AutoUpgradeAPEX), + } + dbAdminUserSecret := corev1.EnvVar{ + Name: poolName + "_dbadminusersecret", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: ords.Spec.PoolSettings[i].DBAdminUserSecret.SecretName, + }, + Key: ords.Spec.PoolSettings[i].DBAdminUserSecret.PasswordKey, + }, + }, + } + envVarSecrets = append(envVarSecrets, dbAdminUserSecret, autoUpgradeORDSEnv, autoUpgradeAPEXEnv) + } + if ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.SecretName != "" { + dbCDBAdminUserSecret := corev1.EnvVar{ + Name: poolName + "_dbcdbadminusersecret", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.SecretName, + }, + Key: ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.PasswordKey, + }, + }, + } + envVarSecrets = append(envVarSecrets, dbCDBAdminUserSecret) + } + } + } + return envVarSecrets +} + +/************************************************* + * Deletions + **************************************************/ +func (r *OrdsSrvsReconciler) ConfigMapDelete(ctx context.Context, req ctrl.Request, ords *dbapi.OrdsSrvs, definedPools map[string]bool) (err error) { + // Delete Undefined Pool ConfigMaps + configMapList := &corev1.ConfigMapList{} + if err := r.List(ctx, configMapList, client.InNamespace(req.Namespace), + client.MatchingLabels(map[string]string{ + controllerLabelKey: controllerLabelVal, + "app.kubernetes.io/instance": ords.Name}), + ); err != nil { + return err + } + + for _, configMap := range configMapList.Items { + if configMap.Name == ords.Name+"-"+globalConfigMapName || configMap.Name == ords.Name+"-init-script" { + continue + } + if _, exists := definedPools[configMap.Name]; !exists { + if err := r.Delete(ctx, &configMap); err != nil { + return err + } + RestartPods = ords.Spec.ForceRestart + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Delete", "ConfigMap %s Deleted", configMap.Name) + } + } + + return nil +} + +func (r *OrdsSrvsReconciler) WorkloadDelete(ctx context.Context, req ctrl.Request, ords *dbapi.OrdsSrvs, kind string) (err error) { + logr := log.FromContext(ctx).WithName("WorkloadDelete") + + // Get Workloads + deploymentList := &appsv1.DeploymentList{} + if err := r.List(ctx, deploymentList, client.InNamespace(req.Namespace), + client.MatchingLabels(map[string]string{ + controllerLabelKey: controllerLabelVal, + "app.kubernetes.io/instance": ords.Name}), + ); err != nil { + return err + } + + statefulSetList := &appsv1.StatefulSetList{} + if err := r.List(ctx, statefulSetList, client.InNamespace(req.Namespace), + client.MatchingLabels(map[string]string{ + controllerLabelKey: controllerLabelVal, + "app.kubernetes.io/instance": ords.Name}), + ); err != nil { + return err + } + + daemonSetList := &appsv1.DaemonSetList{} + if err := r.List(ctx, daemonSetList, client.InNamespace(req.Namespace), + client.MatchingLabels(map[string]string{ + controllerLabelKey: controllerLabelVal, + "app.kubernetes.io/instance": ords.Name}), + ); err != nil { + return err + } + + switch kind { + case "StatefulSet": + for _, deleteDaemonSet := range daemonSetList.Items { + if err := r.Delete(ctx, &deleteDaemonSet); err != nil { + return err + } + logr.Info("Deleted: " + kind) + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Delete", "Workload %s Deleted", kind) + } + for _, deleteDeployment := range deploymentList.Items { + if err := r.Delete(ctx, &deleteDeployment); err != nil { + return err + } + logr.Info("Deleted: " + kind) + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Delete", "Workload %s Deleted", kind) + } + case "DaemonSet": + for _, deleteDeployment := range deploymentList.Items { + if err := r.Delete(ctx, &deleteDeployment); err != nil { + return err + } + logr.Info("Deleted: " + kind) + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Delete", "Workload %s Deleted", kind) + } + for _, deleteStatefulSet := range statefulSetList.Items { + if err := r.Delete(ctx, &deleteStatefulSet); err != nil { + return err + } + logr.Info("Deleted StatefulSet: " + deleteStatefulSet.Name) + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Delete", "Workload %s Deleted", kind) + } + default: + for _, deleteStatefulSet := range statefulSetList.Items { + if err := r.Delete(ctx, &deleteStatefulSet); err != nil { + return err + } + logr.Info("Deleted: " + kind) + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Delete", "Workload %s Deleted", kind) + } + for _, deleteDaemonSet := range daemonSetList.Items { + if err := r.Delete(ctx, &deleteDaemonSet); err != nil { + return err + } + logr.Info("Deleted: " + kind) + r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Delete", "Workload %s Deleted", kind) + } + } + return nil +} + +/************************************************* + * Helpers + **************************************************/ +func getLabels(name string) map[string]string { + return map[string]string{ + "app.kubernetes.io/instance": name, + controllerLabelKey: controllerLabelVal, + } +} + +func generateSpecHash(spec interface{}) string { + byteArray, err := json.Marshal(spec) + if err != nil { + return "" + } + + hash := sha256.New() + _, err = hash.Write(byteArray) + if err != nil { + return "" + } + + hashBytes := hash.Sum(nil) + hashString := hex.EncodeToString(hashBytes[:8]) + + return hashString +} diff --git a/controllers/database/ordssrvs_ordsconfig.go b/controllers/database/ordssrvs_ordsconfig.go new file mode 100644 index 00000000..edb2e0f6 --- /dev/null +++ b/controllers/database/ordssrvs_ordsconfig.go @@ -0,0 +1,258 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "context" + "fmt" + "os" + "strings" + "time" + + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +func (r *OrdsSrvsReconciler) ConfigMapDefine(ctx context.Context, ords *dbapi.OrdsSrvs, configMapName string, poolIndex int) *corev1.ConfigMap { + var defData map[string]string + if configMapName == ords.Name+"-init-script" { + // Read the file from controller's filesystem + filePath := "/ords_init.sh" + scriptData, err := os.ReadFile(filePath) + if err != nil { + return nil + } + defData = map[string]string{ + "init_script.sh": string(scriptData)} + } else if configMapName == ords.Name+"-"+globalConfigMapName { + // GlobalConfigMap + var defStandaloneAccessLog string + if ords.Spec.GlobalSettings.EnableStandaloneAccessLog { + defStandaloneAccessLog = ` ` + ordsSABase + `/log/global` + "\n" + } + var defMongoAccessLog string + if ords.Spec.GlobalSettings.EnableMongoAccessLog { + defMongoAccessLog = ` ` + ordsSABase + `/log/global` + "\n" + } + var defCert string + if ords.Spec.GlobalSettings.CertSecret != nil { + defCert = ` ` + ordsSABase + `/config/certficate/` + ords.Spec.GlobalSettings.CertSecret.Certificate + `` + "\n" + + ` ` + ordsSABase + `/config/certficate/` + ords.Spec.GlobalSettings.CertSecret.CertificateKey + `` + "\n" + } + defData = map[string]string{ + "settings.xml": fmt.Sprintf(`` + "\n" + + `` + "\n" + + `` + "\n" + + conditionalEntry("cache.metadata.graphql.expireAfterAccess", ords.Spec.GlobalSettings.CacheMetadataGraphQLExpireAfterAccess) + + conditionalEntry("cache.metadata.jwks.enabled", ords.Spec.GlobalSettings.CacheMetadataJWKSEnabled) + + conditionalEntry("cache.metadata.jwks.initialCapacity", ords.Spec.GlobalSettings.CacheMetadataJWKSInitialCapacity) + + conditionalEntry("cache.metadata.jwks.maximumSize", ords.Spec.GlobalSettings.CacheMetadataJWKSMaximumSize) + + conditionalEntry("cache.metadata.jwks.expireAfterAccess", ords.Spec.GlobalSettings.CacheMetadataJWKSExpireAfterAccess) + + conditionalEntry("cache.metadata.jwks.expireAfterWrite", ords.Spec.GlobalSettings.CacheMetadataJWKSExpireAfterWrite) + + conditionalEntry("database.api.management.services.disabled", ords.Spec.GlobalSettings.DatabaseAPIManagementServicesDisabled) + + conditionalEntry("db.invalidPoolTimeout", ords.Spec.GlobalSettings.DBInvalidPoolTimeout) + + conditionalEntry("feature.graphql.max.nesting.depth", ords.Spec.GlobalSettings.FeatureGraphQLMaxNestingDepth) + + conditionalEntry("request.traceHeaderName", ords.Spec.GlobalSettings.RequestTraceHeaderName) + + conditionalEntry("security.credentials.attempts", ords.Spec.GlobalSettings.SecurityCredentialsAttempts) + + conditionalEntry("security.credentials.lock.time", ords.Spec.GlobalSettings.SecurityCredentialsLockTime) + + conditionalEntry("standalone.context.path", ords.Spec.GlobalSettings.StandaloneContextPath) + + conditionalEntry("standalone.http.port", ords.Spec.GlobalSettings.StandaloneHTTPPort) + + conditionalEntry("standalone.https.host", ords.Spec.GlobalSettings.StandaloneHTTPSHost) + + conditionalEntry("standalone.https.port", ords.Spec.GlobalSettings.StandaloneHTTPSPort) + + conditionalEntry("standalone.stop.timeout", ords.Spec.GlobalSettings.StandaloneStopTimeout) + + conditionalEntry("cache.metadata.timeout", ords.Spec.GlobalSettings.CacheMetadataTimeout) + + conditionalEntry("cache.metadata.enabled", ords.Spec.GlobalSettings.CacheMetadataEnabled) + + conditionalEntry("database.api.enabled", ords.Spec.GlobalSettings.DatabaseAPIEnabled) + + conditionalEntry("debug.printDebugToScreen", ords.Spec.GlobalSettings.DebugPrintDebugToScreen) + + conditionalEntry("error.responseFormat", ords.Spec.GlobalSettings.ErrorResponseFormat) + + conditionalEntry("icap.port", ords.Spec.GlobalSettings.ICAPPort) + + conditionalEntry("icap.secure.port", ords.Spec.GlobalSettings.ICAPSecurePort) + + conditionalEntry("icap.server", ords.Spec.GlobalSettings.ICAPServer) + + conditionalEntry("log.procedure", ords.Spec.GlobalSettings.LogProcedure) + + conditionalEntry("mongo.enabled", ords.Spec.GlobalSettings.MongoEnabled) + + conditionalEntry("mongo.port", ords.Spec.GlobalSettings.MongoPort) + + conditionalEntry("mongo.idle.timeout", ords.Spec.GlobalSettings.MongoIdleTimeout) + + conditionalEntry("mongo.op.timeout", ords.Spec.GlobalSettings.MongoOpTimeout) + + conditionalEntry("security.disableDefaultExclusionList", ords.Spec.GlobalSettings.SecurityDisableDefaultExclusionList) + + conditionalEntry("security.exclusionList", ords.Spec.GlobalSettings.SecurityExclusionList) + + conditionalEntry("security.inclusionList", ords.Spec.GlobalSettings.SecurityInclusionList) + + conditionalEntry("security.maxEntries", ords.Spec.GlobalSettings.SecurityMaxEntries) + + conditionalEntry("security.verifySSL", ords.Spec.GlobalSettings.SecurityVerifySSL) + + conditionalEntry("security.httpsHeaderCheck", ords.Spec.GlobalSettings.SecurityHTTPSHeaderCheck) + + conditionalEntry("security.forceHTTPS", ords.Spec.GlobalSettings.SecurityForceHTTPS) + + conditionalEntry("externalSessionTrustedOrigins", ords.Spec.GlobalSettings.SecuirtyExternalSessionTrustedOrigins) + + ` ` + ordsSABase + `/config/global/doc_root/` + "\n" + + // Dynamic + defStandaloneAccessLog + + defMongoAccessLog + + defCert + + // Disabled (but not forgotten) + // conditionalEntry("standalone.binds", ords.Spec.GlobalSettings.StandaloneBinds) + + // conditionalEntry("error.externalPath", ords.Spec.GlobalSettings.ErrorExternalPath) + + // conditionalEntry("security.credentials.file ", ords.Spec.GlobalSettings.SecurityCredentialsFile) + + // conditionalEntry("standalone.static.path", ords.Spec.GlobalSettings.StandaloneStaticPath) + + // conditionalEntry("standalone.doc.root", ords.Spec.GlobalSettings.StandaloneDocRoot) + + // conditionalEntry("standalone.static.context.path", ords.Spec.GlobalSettings.StandaloneStaticContextPath) + + ``), + "logging.properties": fmt.Sprintf(`handlers=java.util.logging.FileHandler` + "\n" + + `.level=SEVERE` + "\n" + + `java.util.logging.FileHandler.level=ALL` + "\n" + + `oracle.dbtools.level=FINEST` + "\n" + + `java.util.logging.FileHandler.pattern = ` + ordsSABase + `/log/global/debug.log` + "\n" + + `java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter`), + } + } else { + // PoolConfigMap + poolName := strings.ToLower(ords.Spec.PoolSettings[poolIndex].PoolName) + var defDBNetworkPath string + if ords.Spec.PoolSettings[poolIndex].DBWalletSecret != nil { + defDBNetworkPath = ` ` + ordsSABase + `/config/databases/` + poolName + `/network/admin/` + ords.Spec.PoolSettings[poolIndex].DBWalletSecret.WalletName + `` + "\n" + + conditionalEntry("db.wallet.zip.service", strings.ToUpper(ords.Spec.PoolSettings[poolIndex].DBWalletZipService)) + "\n" + } else { + defDBNetworkPath = ` ` + ordsSABase + `/config/databases/` + poolName + `/network/admin/` + "\n" + } + defData = map[string]string{ + "pool.xml": fmt.Sprintf(`` + "\n" + + `` + "\n" + + `` + "\n" + + ` ` + ords.Spec.PoolSettings[poolIndex].DBUsername + `` + "\n" + + conditionalEntry("db.adminUser", ords.Spec.PoolSettings[poolIndex].DBAdminUser) + + conditionalEntry("db.cdb.adminUser", ords.Spec.PoolSettings[poolIndex].DBCDBAdminUser) + + conditionalEntry("apex.security.administrator.roles", ords.Spec.PoolSettings[poolIndex].ApexSecurityAdministratorRoles) + + conditionalEntry("apex.security.user.roles", ords.Spec.PoolSettings[poolIndex].ApexSecurityUserRoles) + + conditionalEntry("db.credentialsSource", ords.Spec.PoolSettings[poolIndex].DBCredentialsSource) + + conditionalEntry("db.poolDestroyTimeout", ords.Spec.PoolSettings[poolIndex].DBPoolDestroyTimeout) + + conditionalEntry("debug.trackResources", ords.Spec.PoolSettings[poolIndex].DebugTrackResources) + + conditionalEntry("feature.openservicebroker.exclude", ords.Spec.PoolSettings[poolIndex].FeatureOpenservicebrokerExclude) + + conditionalEntry("feature.sdw", ords.Spec.PoolSettings[poolIndex].FeatureSDW) + + conditionalEntry("http.cookie.filter", ords.Spec.PoolSettings[poolIndex].HttpCookieFilter) + + conditionalEntry("jdbc.auth.admin.role", ords.Spec.PoolSettings[poolIndex].JDBCAuthAdminRole) + + conditionalEntry("jdbc.cleanup.mode", ords.Spec.PoolSettings[poolIndex].JDBCCleanupMode) + + conditionalEntry("owa.trace.sql", ords.Spec.PoolSettings[poolIndex].OwaTraceSql) + + conditionalEntry("plsql.gateway.mode", ords.Spec.PoolSettings[poolIndex].PlsqlGatewayMode) + + conditionalEntry("security.jwt.profile.enabled", ords.Spec.PoolSettings[poolIndex].SecurityJWTProfileEnabled) + + conditionalEntry("security.jwks.size", ords.Spec.PoolSettings[poolIndex].SecurityJWKSSize) + + conditionalEntry("security.jwks.connection.timeout", ords.Spec.PoolSettings[poolIndex].SecurityJWKSConnectionTimeout) + + conditionalEntry("security.jwks.read.timeout", ords.Spec.PoolSettings[poolIndex].SecurityJWKSReadTimeout) + + conditionalEntry("security.jwks.refresh.interval", ords.Spec.PoolSettings[poolIndex].SecurityJWKSRefreshInterval) + + conditionalEntry("security.jwt.allowed.skew", ords.Spec.PoolSettings[poolIndex].SecurityJWTAllowedSkew) + + conditionalEntry("security.jwt.allowed.age", ords.Spec.PoolSettings[poolIndex].SecurityJWTAllowedAge) + + conditionalEntry("db.connectionType", ords.Spec.PoolSettings[poolIndex].DBConnectionType) + + conditionalEntry("db.customURL", ords.Spec.PoolSettings[poolIndex].DBCustomURL) + + conditionalEntry("db.hostname", ords.Spec.PoolSettings[poolIndex].DBHostname) + + conditionalEntry("db.port", ords.Spec.PoolSettings[poolIndex].DBPort) + + conditionalEntry("db.servicename", ords.Spec.PoolSettings[poolIndex].DBServicename) + + conditionalEntry("db.sid", ords.Spec.PoolSettings[poolIndex].DBSid) + + conditionalEntry("db.tnsAliasName", ords.Spec.PoolSettings[poolIndex].DBTnsAliasName) + + conditionalEntry("jdbc.DriverType", ords.Spec.PoolSettings[poolIndex].JDBCDriverType) + + conditionalEntry("jdbc.InactivityTimeout", ords.Spec.PoolSettings[poolIndex].JDBCInactivityTimeout) + + conditionalEntry("jdbc.InitialLimit", ords.Spec.PoolSettings[poolIndex].JDBCInitialLimit) + + conditionalEntry("jdbc.MaxConnectionReuseCount", ords.Spec.PoolSettings[poolIndex].JDBCMaxConnectionReuseCount) + + conditionalEntry("jdbc.MaxLimit", ords.Spec.PoolSettings[poolIndex].JDBCMaxLimit) + + conditionalEntry("jdbc.auth.enabled", ords.Spec.PoolSettings[poolIndex].JDBCAuthEnabled) + + conditionalEntry("jdbc.MaxStatementsLimit", ords.Spec.PoolSettings[poolIndex].JDBCMaxStatementsLimit) + + conditionalEntry("jdbc.MinLimit", ords.Spec.PoolSettings[poolIndex].JDBCMinLimit) + + conditionalEntry("jdbc.statementTimeout", ords.Spec.PoolSettings[poolIndex].JDBCStatementTimeout) + + conditionalEntry("jdbc.MaxConnectionReuseTime", ords.Spec.PoolSettings[poolIndex].JDBCMaxConnectionReuseTime) + + conditionalEntry("jdbc.SecondsToTrustIdleConnection", ords.Spec.PoolSettings[poolIndex].JDBCSecondsToTrustIdleConnection) + + conditionalEntry("misc.defaultPage", ords.Spec.PoolSettings[poolIndex].MiscDefaultPage) + + conditionalEntry("misc.pagination.maxRows", ords.Spec.PoolSettings[poolIndex].MiscPaginationMaxRows) + + conditionalEntry("procedure.postProcess", ords.Spec.PoolSettings[poolIndex].ProcedurePostProcess) + + conditionalEntry("procedure.preProcess", ords.Spec.PoolSettings[poolIndex].ProcedurePreProcess) + + conditionalEntry("procedure.rest.preHook", ords.Spec.PoolSettings[poolIndex].ProcedureRestPreHook) + + conditionalEntry("security.requestAuthenticationFunction", ords.Spec.PoolSettings[poolIndex].SecurityRequestAuthenticationFunction) + + conditionalEntry("security.requestValidationFunction", ords.Spec.PoolSettings[poolIndex].SecurityRequestValidationFunction) + + conditionalEntry("soda.defaultLimit", ords.Spec.PoolSettings[poolIndex].SODADefaultLimit) + + conditionalEntry("soda.maxLimit", ords.Spec.PoolSettings[poolIndex].SODAMaxLimit) + + conditionalEntry("restEnabledSql.active", ords.Spec.PoolSettings[poolIndex].RestEnabledSqlActive) + + defDBNetworkPath + + // Disabled (but not forgotten) + // conditionalEntry("autoupgrade.api.aulocation", ords.Spec.PoolSettings[poolIndex].AutoupgradeAPIAulocation) + + // conditionalEntry("autoupgrade.api.enabled", ords.Spec.PoolSettings[poolIndex].AutoupgradeAPIEnabled) + + // conditionalEntry("autoupgrade.api.jvmlocation", ords.Spec.PoolSettings[poolIndex].AutoupgradeAPIJvmlocation) + + // conditionalEntry("autoupgrade.api.loglocation", ords.Spec.PoolSettings[poolIndex].AutoupgradeAPILoglocation) + + // conditionalEntry("db.serviceNameSuffix", ords.Spec.PoolSettings[poolIndex].DBServiceNameSuffix) + + ``), + } + } + + objectMeta := objectMetaDefine(ords, configMapName) + def := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: objectMeta, + Data: defData, + } + + // Set the ownerRef + if err := ctrl.SetControllerReference(ords, def, r.Scheme); err != nil { + return nil + } + return def +} + +func conditionalEntry(key string, value interface{}) string { + switch v := value.(type) { + case nil: + return "" + case string: + if v != "" { + return fmt.Sprintf(` %s`+"\n", key, v) + } + case *int32: + if v != nil { + return fmt.Sprintf(` %d`+"\n", key, *v) + } + case *bool: + if v != nil { + return fmt.Sprintf(` %v`+"\n", key, *v) + } + case *time.Duration: + if v != nil { + return fmt.Sprintf(` %v`+"\n", key, *v) + } + default: + return fmt.Sprintf(` %v`+"\n", key, v) + } + return "" +} diff --git a/main.go b/main.go index 9e71967a..a9b6e58f 100644 --- a/main.go +++ b/main.go @@ -362,6 +362,15 @@ func main() { os.Exit(1) } + if err = (&databasecontroller.OrdsSrvsReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + // Config: mgr.GetConfig(), + Recorder: mgr.GetEventRecorderFor("OrdsSrvs"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "OrdsSrvs") + } + // Observability DatabaseObserver Reconciler if err = (&observabilitycontroller.DatabaseObserverReconciler{ Client: mgr.GetClient(), diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index ad8bb9c0..3b84bdda 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -3164,6 +3164,484 @@ spec: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: ordssrvs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OrdsSrvs + listKind: OrdsSrvsList + plural: ordssrvs + singular: ordssrvs + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: status + type: string + - jsonPath: .status.workloadType + name: workloadType + type: string + - jsonPath: .status.ordsVersion + name: ordsVersion + type: string + - jsonPath: .status.httpPort + name: httpPort + type: integer + - jsonPath: .status.httpsPort + name: httpsPort + type: integer + - jsonPath: .status.mongoPort + name: MongoPort + type: integer + - jsonPath: .status.restartRequired + name: restartRequired + type: boolean + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.ordsInstalled + name: OrdsInstalled + type: boolean + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + forceRestart: + type: boolean + globalSettings: + properties: + cache.metadata.enabled: + type: boolean + cache.metadata.graphql.expireAfterAccess: + format: int64 + type: integer + cache.metadata.graphql.expireAfterWrite: + format: int64 + type: integer + cache.metadata.jwks.enabled: + type: boolean + cache.metadata.jwks.expireAfterAccess: + format: int64 + type: integer + cache.metadata.jwks.expireAfterWrite: + format: int64 + type: integer + cache.metadata.jwks.initialCapacity: + format: int32 + type: integer + cache.metadata.jwks.maximumSize: + format: int32 + type: integer + cache.metadata.timeout: + format: int64 + type: integer + certSecret: + properties: + cert: + type: string + key: + type: string + secretName: + type: string + required: + - cert + - key + - secretName + type: object + database.api.enabled: + type: boolean + database.api.management.services.disabled: + type: boolean + db.invalidPoolTimeout: + format: int64 + type: integer + debug.printDebugToScreen: + type: boolean + enable.mongo.access.log: + default: false + type: boolean + enable.standalone.access.log: + default: false + type: boolean + error.responseFormat: + type: string + feature.grahpql.max.nesting.depth: + format: int32 + type: integer + icap.port: + format: int32 + type: integer + icap.secure.port: + format: int32 + type: integer + icap.server: + type: string + log.procedure: + type: boolean + mongo.enabled: + type: boolean + mongo.idle.timeout: + format: int64 + type: integer + mongo.op.timeout: + format: int64 + type: integer + mongo.port: + default: 27017 + format: int32 + type: integer + request.traceHeaderName: + type: string + security.credentials.attempts: + format: int32 + type: integer + security.credentials.lock.time: + format: int64 + type: integer + security.disableDefaultExclusionList: + type: boolean + security.exclusionList: + type: string + security.externalSessionTrustedOrigins: + type: string + security.forceHTTPS: + type: boolean + security.httpsHeaderCheck: + type: string + security.inclusionList: + type: string + security.maxEntries: + format: int32 + type: integer + security.verifySSL: + type: boolean + standalone.context.path: + default: /ords + type: string + standalone.http.port: + default: 8080 + format: int32 + type: integer + standalone.https.host: + type: string + standalone.https.port: + default: 8443 + format: int32 + type: integer + standalone.stop.timeout: + format: int64 + type: integer + type: object + image: + type: string + imagePullPolicy: + default: IfNotPresent + enum: + - IfNotPresent + - Always + - Never + type: string + imagePullSecrets: + type: string + poolSettings: + items: + properties: + apex.security.administrator.roles: + type: string + apex.security.user.roles: + type: string + autoUpgradeAPEX: + default: false + type: boolean + autoUpgradeORDS: + default: false + type: boolean + db.adminUser: + type: string + db.adminUser.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.cdb.adminUser: + type: string + db.cdb.adminUser.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.connectionType: + enum: + - basic + - tns + - customurl + type: string + db.credentialsSource: + enum: + - pool + - request + type: string + db.customURL: + type: string + db.hostname: + type: string + db.poolDestroyTimeout: + format: int64 + type: integer + db.port: + format: int32 + type: integer + db.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.servicename: + type: string + db.sid: + type: string + db.tnsAliasName: + type: string + db.username: + default: ORDS_PUBLIC_USER + type: string + db.wallet.zip.service: + type: string + dbWalletSecret: + properties: + secretName: + type: string + walletName: + type: string + required: + - secretName + - walletName + type: object + debug.trackResources: + type: boolean + feature.openservicebroker.exclude: + type: boolean + feature.sdw: + type: boolean + http.cookie.filter: + type: string + jdbc.DriverType: + enum: + - thin + - oci8 + type: string + jdbc.InactivityTimeout: + format: int32 + type: integer + jdbc.InitialLimit: + format: int32 + type: integer + jdbc.MaxConnectionReuseCount: + format: int32 + type: integer + jdbc.MaxConnectionReuseTime: + format: int32 + type: integer + jdbc.MaxLimit: + format: int32 + type: integer + jdbc.MaxStatementsLimit: + format: int32 + type: integer + jdbc.MinLimit: + format: int32 + type: integer + jdbc.SecondsToTrustIdleConnection: + format: int32 + type: integer + jdbc.auth.admin.role: + type: string + jdbc.auth.enabled: + type: boolean + jdbc.cleanup.mode: + type: string + jdbc.statementTimeout: + format: int32 + type: integer + misc.defaultPage: + type: string + misc.pagination.maxRows: + format: int32 + type: integer + owa.trace.sql: + type: boolean + plsql.gateway.mode: + enum: + - disabled + - direct + - proxied + type: string + poolName: + type: string + procedure.preProcess: + type: string + procedure.rest.preHook: + type: string + procedurePostProcess: + type: string + restEnabledSql.active: + type: boolean + security.jwks.connection.timeout: + format: int64 + type: integer + security.jwks.read.timeout: + format: int64 + type: integer + security.jwks.refresh.interval: + format: int64 + type: integer + security.jwks.size: + format: int32 + type: integer + security.jwt.allowed.age: + format: int64 + type: integer + security.jwt.allowed.skew: + format: int64 + type: integer + security.jwt.profile.enabled: + type: boolean + security.requestAuthenticationFunction: + type: string + security.requestValidationFunction: + default: ords_util.authorize_plsql_gateway + type: string + security.validationFunctionType: + enum: + - plsql + - javascript + type: string + soda.defaultLimit: + type: string + soda.maxLimit: + type: string + tnsAdminSecret: + properties: + secretName: + type: string + required: + - secretName + type: object + required: + - db.secret + - poolName + type: object + type: array + replicas: + default: 1 + format: int32 + minimum: 1 + type: integer + workloadType: + default: Deployment + enum: + - Deployment + - StatefulSet + - DaemonSet + type: string + required: + - globalSettings + - image + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + httpPort: + format: int32 + type: integer + httpsPort: + format: int32 + type: integer + mongoPort: + format: int32 + type: integer + ordsInstalled: + type: boolean + ordsVersion: + type: string + restartRequired: + type: boolean + status: + type: string + workloadType: + type: string + required: + - restartRequired + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert @@ -4988,6 +5466,18 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - configmaps/status + - daemonsets/status + - deployments/status + - services/status + - statefulsets/status + verbs: + - get + - patch + - update - apiGroups: - "" resources: @@ -4996,6 +5486,12 @@ rules: - get - list - watch +- apiGroups: + - "" + resources: + - secrets/status + verbs: + - get - apiGroups: - '''''' resources: @@ -5018,6 +5514,7 @@ rules: - apiGroups: - apps resources: + - daemonsets - deployments - pods - replicasets @@ -5052,6 +5549,7 @@ rules: - lrests - lrpdbs - oraclerestdataservices + - ordssrvs - pdbs - shardingdatabases - singleinstancedatabases @@ -5075,6 +5573,7 @@ rules: - lrests/status - lrpdbs/status - oraclerestdataservices/status + - ordssrvs/status - pdbs/status - shardingdatabases/status - singleinstancedatabases/status @@ -5108,6 +5607,7 @@ rules: - dataguardbrokers/finalizers - lrests/finalizers - oraclerestdataservices/finalizers + - ordssrvs/finalizers - singleinstancedatabases/finalizers verbs: - update diff --git a/ords/ords_init.sh b/ords/ords_init.sh new file mode 100644 index 00000000..0994dceb --- /dev/null +++ b/ords/ords_init.sh @@ -0,0 +1,484 @@ +#!/bin/bash +## Copyright (c) 2006, 2024, Oracle and/or its affiliates. +## +## The Universal Permissive License (UPL), Version 1.0 +## +## Subject to the condition set forth below, permission is hereby granted to any +## person obtaining a copy of this software, associated documentation and/or data +## (collectively the "Software"), free of charge and under any and all copyright +## rights in the Software, and any and all patent rights owned or freely +## licensable by each licensor hereunder covering either (i) the unmodified +## Software as contributed to or provided by such licensor, or (ii) the Larger +## Works (as defined below), to deal in both +## +## (a) the Software, and +## (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +## one is included with the Software (each a "Larger Work" to which the Software +## is contributed by such licensors), +## +## without restriction, including without limitation the rights to copy, create +## derivative works of, display, perform, and distribute the Software and make, +## use, sell, offer for sale, import, export, have made, and have sold the +## Software and the Larger Work(s), and to sublicense the foregoing rights on +## either these or other terms. +## +## This license is subject to the following condition: +## The above copyright notice and either this complete permission notice or at +## a minimum a reference to the UPL must be included in all copies or +## substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +## SOFTWARE. + +dump_stack(){ +_log_date=`date "+%y:%m:%d %H:%M:%S"` + local frame=0 + local line_no + local function_name + local file_name + echo -e "BACKTRACE [${_log_date}]\n" + echo -e "filename:line\tfunction " + echo -e "------------- --------" + while caller $frame ;do ((frame++)) ;done | \ + while read line_no function_name file_name;\ + do echo -e "$file_name:$line_no\t$function_name" ;done >&2 +} + + + +get_conn_string() { + local -n _conn_string="${1}" + + local -r _admin_user=$($ords_cfg_cmd get --secret db.adminUser | tail -1) + local _conn_type=$($ords_cfg_cmd get db.connectionType |tail -1) + if [[ $_conn_type == "customurl" ]]; then + local -r _conn=$($ords_cfg_cmd get db.customURL | tail -1) + elif [[ $_conn_type == "tns" ]]; then + local -r _tns_service=$($ords_cfg_cmd get db.tnsAliasName | tail -1) + local -r _conn=${_tns_service} + elif [[ $_conn_type == "basic" ]]; then + local -r _host=$($ords_cfg_cmd get db.hostname | tail -1) + local -r _port=$($ords_cfg_cmd get db.port | tail -1) + local -r _service=$($ords_cfg_cmd get db.servicename | tail -1) + local -r _sid=$($ords_cfg_cmd get db.sid | tail -1) + + if [[ -n ${_host} ]] && [[ -n ${_port} ]]; then + if [[ -n ${_service} ]] || [[ -n ${_sid} ]]; then + local -r _conn=${_host}:${_port}/${_service:-$_sid} + fi + fi + else + # wallet + _conn_type="wallet" + local -r _wallet_service=$($ords_cfg_cmd get db.wallet.zip.service | tail -1) + local -r _conn=${_wallet_service} + fi + + if [[ -n ${_conn} ]]; then + echo "Connection String (${_conn_type}): ${_conn}" + _conn_string="${_admin_user%%/ *}/${config["dbadminusersecret"]}@${_conn}" + if [[ ${_admin_user%%/ *} == "SYS" ]]; then + _conn_string="${_conn_string=} AS SYSDBA" + fi + fi +} + +#------------------------------------------------------------------------------ +function run_sql { + local -r _conn_string="${1}" + local -r _sql="${2}" + local -n _output="${3}" + local -i _rc=0 + + if [[ -z ${_sql} ]]; then + dump_stack + echo "FATAL: Dear Developer.. you've got a bug calling run_sql" && exit 1 + fi + ## Get TNS_ADMIN location + local -r _tns_admin=$($ords_cfg_cmd get db.tnsDirectory | tail -1) + if [[ ! $_tns_admin =~ "Cannot get setting" ]]; then + echo "Setting: TNS_ADMIN=${_tns_admin}" + export TNS_ADMIN=${_tns_admin} + fi + + ## Get ADB Wallet + local -r _wallet_zip_path=$($ords_cfg_cmd get db.wallet.zip.path | tail -1) + if [[ ! $_wallet_zip_path =~ "Cannot get setting" ]]; then + echo "Using: set cloudconfig ${_wallet_zip_path}" + local -r _cloudconfig="set cloudconfig ${_wallet_zip_path}" + fi + + # NOTE to maintainer; the heredoc must be TAB indented + echo "Running SQL..." + #_output=$(cd ${APEX_HOME}/${APEX_VER} && sql -S /nolog <<-EOSQL + _output=$(cd ${APEX_HOME}/${APEX_VER} && sql -S -nohistory -noupdates /nolog <<-EOSQL + WHENEVER SQLERROR EXIT 1 + WHENEVER OSERROR EXIT 1 + ${_cloudconfig} + connect $_conn_string + set serveroutput on echo off pause off feedback off + set heading off wrap off linesize 1000 pagesize 0 + SET TERMOUT OFF VERIFY OFF + ${_sql} + exit; + EOSQL + ) + _rc=$? + + if (( ${_rc} > 0 )); then + dump_stack + echo "SQLERROR: ${_output}" + fi + + return $_rc +} + +#------------------------------------------------------------------------------ +function check_adb() { + local -r _conn_string=$1 + local -n _is_adb=$2 + + local -r _adb_chk_sql=" + DECLARE + invalid_column exception; + pragma exception_init (invalid_column,-00904); + adb_check integer; + BEGIN + EXECUTE IMMEDIATE q'[SELECT COUNT(*) FROM ( + SELECT JSON_VALUE(cloud_identity, '\$.DATABASE_OCID') AS database_ocid + FROM v\$pdbs) t + WHERE t.database_ocid like '%AUTONOMOUS%']' INTO adb_check; + DBMS_OUTPUT.PUT_LINE(adb_check); + EXCEPTION WHEN invalid_column THEN + DBMS_OUTPUT.PUT_LINE('0'); + END; + /" + echo "Checking if Database is an ADB" + run_sql "${_conn_string}" "${_adb_chk_sql}" "_adb_check" + _rc=$? + + if (( ${_rc} == 0 )); then + _adb_check=${_adb_check//[[:space:]]/} + echo "ADB Check: ${_adb_check}" + if (( ${_adb_check} == 1 )); then + _is_adb=${_adb_check//[[:space:]]/} + fi + fi + + return ${_rc} +} + +function create_adb_user() { + local -r _conn_string="${1}" + local -r _pool_name="${2}" + + local _config_user=$($ords_cfg_cmd get db.username | tail -1) + + if [[ -z ${_config_user} ]] || [[ ${_config_user} == "ORDS_PUBLIC_USER" ]]; then + echo "FATAL: You must specify a db.username <> ORDS_PUBLIC_USER in pool ${_pool_name}" + dump_stack + return 1 + fi + + local -r _adb_user_sql=" + DECLARE + l_user VARCHAR2(255); + l_cdn VARCHAR2(255); + BEGIN + BEGIN + SELECT USERNAME INTO l_user FROM DBA_USERS WHERE USERNAME='${_config_user}'; + EXECUTE IMMEDIATE 'ALTER USER \"${_config_user}\" PROFILE ORA_APP_PROFILE'; + EXECUTE IMMEDIATE 'ALTER USER \"${_config_user}\" IDENTIFIED BY \"${config["dbsecret"]}\"'; + DBMS_OUTPUT.PUT_LINE('${_config_user} Exists - Password reset'); + EXCEPTION + WHEN NO_DATA_FOUND THEN + EXECUTE IMMEDIATE 'CREATE USER \"${_config_user}\" IDENTIFIED BY \"${config["dbsecret"]}\" PROFILE ORA_APP_PROFILE'; + DBMS_OUTPUT.PUT_LINE('${_config_user} Created'); + END; + EXECUTE IMMEDIATE 'GRANT CONNECT TO \"${_config_user}\"'; + BEGIN + SELECT USERNAME INTO l_user FROM DBA_USERS WHERE USERNAME='ORDS_PLSQL_GATEWAY_OPER'; + EXECUTE IMMEDIATE 'ALTER USER \"ORDS_PLSQL_GATEWAY_OPER\" PROFILE DEFAULT'; + EXECUTE IMMEDIATE 'ALTER USER \"ORDS_PLSQL_GATEWAY_OPER\" NO AUTHENTICATION'; + DBMS_OUTPUT.PUT_LINE('ORDS_PLSQL_GATEWAY_OPER Exists'); + EXCEPTION + WHEN NO_DATA_FOUND THEN + EXECUTE IMMEDIATE 'CREATE USER \"ORDS_PLSQL_GATEWAY_OPER\" NO AUTHENTICATION PROFILE DEFAULT'; + DBMS_OUTPUT.PUT_LINE('ORDS_PLSQL_GATEWAY_OPER Created'); + END; + EXECUTE IMMEDIATE 'GRANT CONNECT TO \"ORDS_PLSQL_GATEWAY_OPER\"'; + EXECUTE IMMEDIATE 'ALTER USER \"ORDS_PLSQL_GATEWAY_OPER\" GRANT CONNECT THROUGH \"${_config_user}\"'; + ORDS_ADMIN.PROVISION_RUNTIME_ROLE ( + p_user => '${_config_user}' + ,p_proxy_enabled_schemas => TRUE + ); + ORDS_ADMIN.CONFIG_PLSQL_GATEWAY ( + p_runtime_user => '${_config_user}' + ,p_plsql_gateway_user => 'ORDS_PLSQL_GATEWAY_OPER' + ); + -- TODO: Only do this if ADB APEX Version <> this ORDS Version + BEGIN + SELECT images_version INTO L_CDN + FROM APEX_PATCHES + where is_bundle_patch = 'Yes' + order by patch_version desc + fetch first 1 rows only; + EXCEPTION WHEN NO_DATA_FOUND THEN + select version_no INTO L_CDN + from APEX_RELEASE; + END; + apex_instance_admin.set_parameter( + p_parameter => 'IMAGE_PREFIX', + p_value => 'https://static.oracle.com/cdn/apex/'||L_CDN||'/' + ); + END; + /" + + run_sql "${_conn_string}" "${_adb_user_sql}" "_adb_user_sql_output" + _rc=$? + + echo "Installation Output: ${_adb_user_sql_output}" + return ${_rc} +} + +#------------------------------------------------------------------------------ +function compare_versions() { + local _db_ver=$1 + local _im_ver=$2 + + IFS='.' read -r -a _db_ver_array <<< "$_db_ver" + IFS='.' read -r -a _im_ver_array <<< "$_im_ver" + + # Compare each component + local i + for i in "${!_db_ver_array[@]}"; do + if [[ "${_db_ver_array[$i]}" -lt "${_im_ver_array[$i]}" ]]; then + # _db_ver < _im_ver (upgrade) + return 0 + elif [[ "${_db_ver_array[$i]}" -gt "${_im_ver_array[$i]}" ]]; then + # _db_ver < _im_ver (do nothing) + return 1 + fi + done + # _db_ver == __im_ver (do nothing) + return 1 +} + +#------------------------------------------------------------------------------ +set_secret() { + local -r _pool_name="${1}" + local -r _config_key="${2}" + local -r _config_val="${3}" + local -i _rc=0 + + if [[ -n "${_config_val}" ]]; then + ords --config "$ORDS_CONFIG" config --db-pool "${_pool_name}" secret --password-stdin "${_config_key}" <<< "${_config_val}" + _rc=$? + echo "${_config_key} in pool ${_pool_name} set" + else + echo "${_config_key} in pool ${_pool_name}, not defined" + _rc=0 + fi + + return ${_rc} +} + +#------------------------------------------------------------------------------ +ords_upgrade() { + local -r _pool_name="${1}" + local -r _upgrade_key="${2}" + local -i _rc=0 + + if [[ -n "${config["dbadminusersecret"]}" ]]; then + # Get usernames + local -r ords_user=$($ords_cfg_cmd get db.username | tail -1) + local -r ords_admin=$($ords_cfg_cmd get db.adminUser | tail -1) + + echo "Performing ORDS install/upgrade as $ords_admin into $ords_user on pool ${_pool_name}" + if [[ ${_pool_name} == "default" ]]; then + ords --config "$ORDS_CONFIG" install --db-only \ + --admin-user "$ords_admin" --password-stdin <<< "${config["dbadminusersecret"]}" + _rc=$? + else + ords --config "$ORDS_CONFIG" install --db-pool "${_pool_name}" --db-only \ + --admin-user "$ords_admin" --password-stdin <<< "${config["dbadminusersecret"]}" + _rc=$? + fi + + # Dar be bugs below deck with --db-user so using the above + # ords --config "$ORDS_CONFIG" install --db-pool "$1" --db-only \ + # --admin-user "$ords_admin" --db-user "$ords_user" --password-stdin <<< "${!2}" + fi + + return $_rc +} + +#------------------------------------------------------------------------------ +function get_apex_version() { + local -r _conn_string="${1}" + local -n _action="${2}" + local -i _rc=0 + + local -r _ver_sql="SELECT VERSION FROM DBA_REGISTRY WHERE COMP_ID='APEX';" + run_sql "${_conn_string}" "${_ver_sql}" "_db_apex_version" + _rc=$? + + if (( $_rc > 0 )); then + echo "FATAL: Unable to connect to ${_conn_string} to get APEX version" + dump_stack + return $_rc + fi + + local -r _db_apex_version=${_db_apex_version//[^0-9.]/} + echo "Database APEX Version: ${_db_apex_version:-Not Installed}" + + _action="none" + if [[ -z "${_db_apex_version}" ]]; then + echo "Installing APEX ${APEX_VER}" + _action="install" + elif compare_versions ${_db_apex_version} ${APEX_VER}; then + echo "Upgrading from ${_db_apex_version} to ${APEX_VER}" + _action="upgrade" + else + echo "No Installation/Upgrade Required" + fi + + return $_rc +} + +apex_upgrade() { + local -r _conn_string="${1}" + local -r _upgrade_key="${2}" + local -i _rc=0 + + if [[ -f ${APEX_HOME}/${APEX_VER}/apexins.sql ]] && [[ "${!_upgrade_key}" = "true" ]]; then + echo "Starting Installation of APEX ${APEX_VER}" + local -r _install_sql="@apxsilentins.sql SYSAUX SYSAUX TEMP /i/ ${config["dbsecret"]} ${config["dbsecret"]} ${config["dbsecret"]} ${config["dbsecret"]}" + run_sql "${_conn_string}" "${_install_sql}" "_install_output" + _rc=$? + echo "Installation Output: ${_install_output}" + fi + + return $_rc +} + +#------------------------------------------------------------------------------ +# INIT +#------------------------------------------------------------------------------ +declare -A pool_exit +for pool in "$ORDS_CONFIG"/databases/*; do + rc=0 + pool_name=$(basename "$pool") + pool_exit[${pool_name}]=0 + ords_cfg_cmd="ords --config $ORDS_CONFIG config --db-pool ${pool_name}" + echo "Found Pool: $pool_name..." + + declare -A config + for key in dbsecret dbadminusersecret dbcdbadminusersecret; do + var_key="${pool_name//-/_}_${key}" + echo "Obtaining value from initContainer variable: ${var_key}" + var_val="${!var_key}" + config[${key}]="${var_val}" + done + + # Set Secrets + set_secret "${pool_name}" "db.password" "${config["dbsecret"]}" + rc=$((rc + $?)) + set_secret "${pool_name}" "db.adminUser.password" "${config["dbadminusersecret"]}" + rc=$((rc + $?)) + set_secret "${pool_name}" "db.cdb.adminUser.password" "${config["dbcdbadminusersecret"]}" + rc=$((rc + $?)) + + if (( ${rc} > 0 )); then + echo "FATAL: Unable to set configuration for pool ${pool_name}" + dump_stack + pool_exit[${pool_name}]=1 + continue + elif [[ -z ${config["dbsecret"]} ]]; then + echo "FATAL: db.password must be specified for ${pool_name}" + dump_stack + pool_exit[${pool_name}]=1 + continue + elif [[ -z ${config["dbadminusersecret"]} ]]; then + echo "INFO: No additional configuration for ${pool_name}" + continue + fi + + get_conn_string "conn_string" + if [[ -z ${conn_string} ]]; then + echo "FATAL: Unable to get ${pool_name} database connect string" + dump_stack + pool_exit[${pool_name}]=1 + continue + fi + + check_adb "${conn_string}" "is_adb" + rc=$? + if (( ${rc} > 0 )); then + pool_exit[${pool_name}]=1 + continue + fi + + if (( is_adb )); then + # Create ORDS User + echo "Processing ADB in Pool: ${pool_name}" + create_adb_user "${conn_string}" "${pool_name}" + else + # APEX Upgrade + echo "---------------------------------------------------" + apex_upgrade_var=${pool_name}_autoupgrade_apex + if [[ ${!apex_upgrade_var} != "true" ]]; then + echo "APEX Install/Upgrade not requested for ${pool_name}" + continue + fi + + get_apex_version "${conn_string}" "action" + if [[ -z ${action} ]]; then + echo "FATAL: Unable to get ${pool_name} APEX Version" + dump_stack + pool_exit[${pool_name}]=1 + continue + fi + + if [[ ${action} != "none" ]]; then + apex_upgrade "${conn_string}" "${pool_name}_autoupgrade_apex" + if (( $? > 0 )); then + echo "FATAL: Unable to ${action} APEX for ${pool_name}" + dump_stack + pool_exit[${pool_name}]=1 + continue + fi + fi + + # ORDS Upgrade + ords_upgrade_var=${pool_name}_autoupgrade_ords + if [[ ${!ords_upgrade_var} != "true" ]]; then + echo "ORDS Install/Upgrade not requested for ${pool_name}" + continue + fi + + ords_upgrade "${pool_name}" "${pool_name}_autoupgrade_ords" + rc=$? + if (( $rc > 0 )); then + echo "FATAL: Unable to preform requested ORDS install/upgrade on ${pool_name}" + pool_exit[${pool_name}]=1 + dump_stack + continue + fi + fi +done + +for key in "${!pool_exit[@]}"; do + echo "Pool: $key, Exit Code: ${pool_exit[$key]}" + if (( ${pool_exit[$key]} > 0 )); then + rc=1 + fi +done + +exit $rc +#exit 0 From 67dcdd64e095186f5c9cf35274326686e512bc3c Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Tue, 17 Dec 2024 10:15:05 +0000 Subject: [PATCH 140/414] documentation.1 --- docs/multitenant/README.md | 257 +- docs/multitenant/lrest-based/README.md | 488 ++ .../lrest-based/images/Generalschema2.jpg | Bin 0 -> 96239 bytes .../lrest-based/images/UsecaseSchema.jpg | Bin 0 -> 185861 bytes .../multitenant/lrest-based/usecase/README.md | 139 + .../usecase/altersystem_pdb1_resource.yaml | 50 + .../usecase/cdbnamespace_binding.yaml | 13 + .../usecase/clone_pdb1_resource.yaml | 51 + .../usecase/clone_pdb2_resource.yaml | 51 + .../usecase/close_pdb1_resource.yaml | 47 + .../usecase/close_pdb2_resource.yaml | 47 + .../usecase/close_pdb3_resource.yaml | 47 + .../lrest-based/usecase/config-map-pdb.yaml | 11 + .../lrest-based/usecase/config_map_pdb.yaml | 11 + .../lrest-based/usecase/create_lrest_pod.yaml | 44 + .../usecase/create_pdb1_resource.yaml | 52 + .../usecase/create_pdb2_resource.yaml | 52 + .../usecase/delete_pdb1_resource.yaml | 45 + .../usecase/delete_pdb2_resource.yaml | 45 + docs/multitenant/lrest-based/usecase/makefile | 911 +++ .../usecase/map_pdb1_resource.yaml | 49 + .../usecase/map_pdb2_resource.yaml | 49 + .../usecase/map_pdb3_resource.yaml | 49 + .../usecase/open_pdb1_resource.yaml | 47 + .../usecase/open_pdb2_resource.yaml | 47 + .../usecase/open_pdb3_resource.yaml | 47 + .../usecase/oracle-database-operator.yaml | 5057 +++++++++++++++++ .../oracle-database-operator.yaml.ORIGNINAL | 5057 +++++++++++++++++ .../oracle-database-operator.yaml.pippo | 5057 +++++++++++++++++ .../lrest-based/usecase/parameters.txt | 53 + .../usecase/pdbnamespace_binding.yaml | 13 + .../usecase/plug_pdb1_resource.yaml | 54 + .../usecase/unplug_pdb1_resource.yaml | 46 + .../altersystem_lrpdb1_resource.yaml | 48 + .../usecase05_to_be_removed/ca.crt | 22 + .../usecase05_to_be_removed/ca.key | 28 + .../usecase05_to_be_removed/ca.srl | 1 + .../cdbnamespace_binding.yaml | 14 + .../cert_path/certificates/ca.crt | 22 + .../cert_path/certificates/ca.key | 27 + .../cert_path/certificates/ca.srl | 1 + .../cert_path/certificates/dbpass.txt | 1 + .../cert_path/certificates/dbuser.txt | 1 + .../cert_path/certificates/e_dbpass.txt | 5 + .../cert_path/certificates/e_dbuser.txt | 5 + .../cert_path/certificates/e_pdbpwd.txt | 5 + .../cert_path/certificates/e_pdbusr.txt | 5 + .../cert_path/certificates/e_wbpass.txt | 5 + .../cert_path/certificates/e_wbuser.txt | 5 + .../cert_path/certificates/extfile.txt | 1 + .../cert_path/certificates/lrest_server.crt | 21 + .../certificates/lrest_server.crt.orig | 21 + .../cert_path/certificates/lrest_server.csr | 17 + .../cert_path/certificates/lrest_server.key | 28 + .../cert_path/certificates/pdbpwd.txt | 1 + .../cert_path/certificates/pdbusr.txt | 1 + .../cert_path/certificates/public.pem | 9 + .../cert_path/certificates/wbpass.txt | 1 + .../cert_path/certificates/wbuser.txt | 1 + .../usecase05_to_be_removed/check.sh | 4 + .../clone_lrpdb1_resource.yaml | 47 + .../clone_lrpdb2_resource.yaml | 46 + .../close_lrpdb1_resource.yaml | 39 + .../close_lrpdb2_resource.yaml | 40 + .../close_lrpdb3_resource.yaml | 40 + .../create_lrest_pod.yaml | 48 + .../create_lrest_pod_delcascade.yaml | 48 + .../create_lrpdb1_resource.yaml | 53 + .../create_lrpdb2_resource.yaml | 51 + .../usecase05_to_be_removed/dbpass.txt | 1 + .../usecase05_to_be_removed/dbuser.txt | 1 + .../delete_lrpdb1_resource.yaml | 41 + .../delete_lrpdb2_resource.yaml | 41 + .../delete_lrpdb3_resource.yaml | 41 + .../usecase05_to_be_removed/dmpplane.819519 | 30 + .../usecase05_to_be_removed/e_dbpass.txt | 5 + .../usecase05_to_be_removed/e_dbuser.txt | 5 + .../usecase05_to_be_removed/e_pdbpwd.txt | 5 + .../usecase05_to_be_removed/e_pdbusr.txt | 5 + .../usecase05_to_be_removed/e_wbpass.txt | 5 + .../usecase05_to_be_removed/e_wbuser.txt | 5 + .../usecase05_to_be_removed/extfile.txt | 1 + .../usecase05_to_be_removed/makefile | 647 +++ .../map_lrpdb1_resource.yaml | 41 + .../map_lrpdb2_resource.yaml | 48 + .../map_lrpdb3_resource.yaml | 48 + .../open_lrpdb1_resource.yaml | 39 + .../open_lrpdb2_resource.yaml | 39 + .../open_lrpdb3_resource.yaml | 39 + .../oracle-database-operator.yaml | 1 + .../usecase05_to_be_removed/parameters.txt | 4 + .../pdbnamespace_binding.yaml | 14 + .../usecase05_to_be_removed/pdbpwd.txt | 1 + .../usecase05_to_be_removed/pdbusr.txt | 1 + .../plug_lrpdb1_resource.yaml | 50 + .../usecase05_to_be_removed/public.pem | 9 + .../resetaction_lrpdb1_resource.yaml | 45 + .../usecase05_to_be_removed/server.csr | 17 + .../usecase05_to_be_removed/testDelCascade.sh | 24 + .../tkpc_k8slrestsetup.sh | 294 + .../usecase05_to_be_removed/tls.crt | 21 + .../usecase05_to_be_removed/tls.key | 28 + .../unplug_lrpdb1_resource.yaml | 42 + .../usecase05_to_be_removed/wbpass.txt | 1 + .../usecase05_to_be_removed/wbuser.txt | 1 + .../{ => ords-based}/NamespaceSeg.md | 0 docs/multitenant/ords-based/README.md | 252 + .../images/K8S_NAMESPACE_SEG.png | Bin .../{ => ords-based}/images/K8S_SECURE1.png | Bin .../{ => ords-based}/images/K8S_SECURE2.png | Bin .../{ => ords-based}/images/K8S_SECURE3.png | Bin .../{ => ords-based}/images/K8S_SECURE4.png | Bin .../ords-based/images/makerunall.png | Bin 0 -> 211874 bytes .../ords-based/images/makesecrets_1_1.png | Bin 0 -> 117953 bytes .../{ => ords-based}/openssl_schema.jpg | Bin .../example_setup_using_oci_oke_cluster.md | 0 .../multinamespace/cdb_create.yaml | 2 +- .../multinamespace/cdb_secret.yaml | 17 + .../multinamespace/pdb_clone.yaml | 2 +- .../multinamespace/pdb_close.yaml | 2 +- .../multinamespace}/pdb_create.yaml | 2 +- .../multinamespace/pdb_delete.yaml | 2 +- .../provisioning/multinamespace/pdb_open.yaml | 2 +- .../provisioning/multinamespace/pdb_plug.yaml | 2 +- .../multinamespace}/pdb_secret.yaml | 2 +- .../multinamespace/pdb_unplug.yaml | 2 +- .../provisioning/ords_image.md | 0 .../provisioning/quickOKEcreation.md | 0 .../singlenamespace/cdb_create.yaml | 2 +- .../singlenamespace/cdb_secret.yaml | 17 + .../singlenamespace/pdb_clone.yaml | 0 .../singlenamespace/pdb_close.yaml | 2 +- .../singlenamespace/pdb_create.yaml | 2 +- .../singlenamespace}/pdb_delete.yaml | 2 +- .../singlenamespace}/pdb_open.yaml | 2 +- .../singlenamespace/pdb_plug.yaml | 2 +- .../singlenamespace/pdb_secret.yaml | 16 + .../singlenamespace}/pdb_unplug.yaml | 2 +- docs/multitenant/ords-based/usecase/README.md | 112 + .../ords-based/usecase/authsection01.yaml | 32 + .../ords-based/usecase/authsection02.yaml | 28 + docs/multitenant/ords-based/usecase/ca.crt | 22 + docs/multitenant/ords-based/usecase/ca.key | 28 + docs/multitenant/ords-based/usecase/ca.srl | 1 + .../usecase/cdbnamespace_binding.yaml | 13 + .../usecase/clone_pdb1_resource.yaml | 46 + .../usecase/clone_pdb2_resource.yaml | 46 + .../usecase/close_pdb1_resource.yaml | 43 + .../usecase/close_pdb2_resource.yaml | 43 + .../usecase/close_pdb3_resource.yaml | 43 + .../usecase/create_cdb_secrets.yaml | 13 + .../ords-based/usecase/create_ords_pod.yaml | 44 + .../usecase/create_pdb1_resource.yaml | 47 + .../usecase/create_pdb2_resource.yaml | 47 + .../usecase/create_pdb_secrets.yaml | 11 + .../usecase/delete_pdb1_resource.yaml | 41 + .../usecase/delete_pdb2_resource.yaml | 41 + .../ords-based/usecase/extfile.txt | 1 + docs/multitenant/ords-based/usecase/makefile | 906 +++ .../ords-based/usecase/map_pdb1_resource.yaml | 45 + .../ords-based/usecase/map_pdb2_resource.yaml | 45 + .../ords-based/usecase/map_pdb3_resource.yaml | 45 + .../usecase/open_pdb1_resource.yaml | 43 + .../usecase/open_pdb2_resource.yaml | 43 + .../usecase/open_pdb3_resource.yaml | 43 + .../usecase/oracle-database-operator.yaml | 5057 +++++++++++++++++ .../ords-based/usecase/parameters.txt | 61 + .../usecase/pdbnamespace_binding.yaml | 13 + .../usecase/plug_pdb1_resource.yaml | 49 + .../multitenant/ords-based/usecase/public.pem | 9 + .../multitenant/ords-based/usecase/server.csr | 17 + docs/multitenant/ords-based/usecase/tls.crt | 21 + docs/multitenant/ords-based/usecase/tls.key | 28 + .../usecase/unplug_pdb1_resource.yaml | 42 + .../{ => ords-based}/usecase01/README.md | 0 .../{ => ords-based}/usecase01/ca.crt | 0 .../{ => ords-based}/usecase01/ca.key | 0 .../{ => ords-based}/usecase01/ca.srl | 0 .../usecase01/cdb_create.yaml | 2 +- .../usecase01}/cdb_secret.yaml | 0 .../{ => ords-based}/usecase01/extfile.txt | 0 .../usecase01/logfiles/BuildImage.log | 0 .../usecase01/logfiles/ImagePush.log | 0 .../usecase01/logfiles/cdb.log | 0 .../usecase01/logfiles/cdb_creation.log | 0 .../usecase01/logfiles/openssl_execution.log | 0 .../usecase01/logfiles/ordsconfig.log | 0 .../usecase01/logfiles/tagandpush.log | 0 .../usecase01/logfiles/testapi.log | 0 .../{ => ords-based}/usecase01/makefile | 9 +- .../usecase01/oracle-database-operator.yaml | 0 .../{ => ords-based}/usecase01/pdb_close.yaml | 2 +- .../usecase01/pdb_create.yaml | 2 +- .../usecase01}/pdb_delete.yaml | 2 +- .../{ => ords-based}/usecase01/pdb_map.yaml | 2 +- .../usecase01}/pdb_open.yaml | 2 +- .../usecase01}/pdb_secret.yaml | 0 .../{ => ords-based}/usecase01/server.csr | 0 .../usecase01/tde_secret.yaml | 0 .../{ => ords-based}/usecase01/tls.crt | 0 .../{ => ords-based}/usecase01/tls.key | 0 .../{ => ords-based}/usecase02/README.md | 0 .../{ => ords-based}/usecase02/pdb_clone.yaml | 2 +- .../{ => ords-based}/usecase02/pdb_plug.yaml | 2 +- .../usecase02/pdb_plugtde.yaml | 2 +- .../usecase02}/pdb_unplug.yaml | 2 +- .../usecase02/pdb_unplugtde.yaml | 2 +- .../usecase02/tde_secret.yaml | 0 .../{ => ords-based}/usecase03/Dockerfile | 0 .../usecase03/NamespaceSegregation.png | Bin .../{ => ords-based}/usecase03/README.md | 0 docs/multitenant/ords-based/usecase03/ca.crt | 24 + docs/multitenant/ords-based/usecase03/ca.key | 27 + docs/multitenant/ords-based/usecase03/ca.srl | 1 + .../usecase03/cdb_create.yaml | 2 +- .../usecase03/cdb_creation_log.txt | 0 .../usecase03/cdb_secret.yaml | 0 .../ords-based/usecase03/extfile.txt | 1 + .../{ => ords-based}/usecase03/gentlscert.sh | 0 .../{ => ords-based}/usecase03/makefile | 10 +- .../usecase03/ns_namespace_cdb.yaml | 0 .../usecase03/ns_namespace_pdb.yaml | 0 .../usecase03/operator_creation_log.txt | 0 .../usecase03}/pdb_create.yaml | 2 +- .../usecase03/pdb_creation_log.txt | 0 .../usecase03/pdb_secret.yaml | 0 .../{ => ords-based}/usecase03/runOrdsSSL.sh | 0 .../ords-based/usecase03/server.csr | 17 + docs/multitenant/ords-based/usecase03/tls.crt | 23 + docs/multitenant/ords-based/usecase03/tls.key | 28 + docs/multitenant/usecase01/cdb_secret.yaml | 17 - docs/ordsservices/README.md | 79 + docs/ordsservices/TROUBLESHOOTING.md | 129 + docs/ordsservices/api.md | 1373 +++++ docs/ordsservices/autoupgrade.md | 54 + docs/ordsservices/examples/adb.md | 101 + docs/ordsservices/examples/adb_oraoper.md | 188 + docs/ordsservices/examples/mongo_api.md | 138 + docs/ordsservices/examples/multi_pool.md | 175 + docs/ordsservices/examples/sidb_container.md | 166 + .../usecase01/create_mong_schema.sql | 9 + .../usecase01/create_multisrv.yaml | 42 + docs/ordsservices/usecase01/create_ords.sh | 31 + docs/ordsservices/usecase01/create_ords.yaml | 25 + .../usecase01/create_ordsnamespace.yaml | 6 + .../usecase01/create_registry_secret.sh | 4 + .../usecase01/create_secretregistry.sh | 5 + .../usecase01/create_singleinstance_db.yaml | 17 + .../usecase01/default-ns-role-binding.yaml | 13 + docs/ordsservices/usecase01/diaginit.sh | 11 + docs/ordsservices/usecase01/help | 1 + docs/ordsservices/usecase01/makefile | 715 +++ docs/ordsservices/usecase01/node-rbac.yaml | 26 + docs/ordsservices/usecase01/node_rbac.yaml | 26 + .../usecase01/oracle-database-operator.yaml | 4956 ++++++++++++++++ .../usecase01/ords-ns-role-binding.yaml | 13 + .../usecase01/persistent-volume-rbac.yaml | 29 + .../usecase01/rest_server_creation.yaml | 25 + .../usecase01/rest_server_creation_mongo.yaml | 29 + docs/ordsservices/usecase01/sidb_create.yaml | 18 + docs/ordsservices/usecase01/sqlcreate.sql | 9 + .../usecase01/storage-class-rbac.yaml | 28 + .../usecase01/tnsadmin/tnsnames.ora | 3 + docs/ordsservices/usecase01/tnsnames.ora | 3 + 264 files changed, 36172 insertions(+), 299 deletions(-) create mode 100644 docs/multitenant/lrest-based/README.md create mode 100644 docs/multitenant/lrest-based/images/Generalschema2.jpg create mode 100644 docs/multitenant/lrest-based/images/UsecaseSchema.jpg create mode 100644 docs/multitenant/lrest-based/usecase/README.md create mode 100644 docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml create mode 100644 docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/config-map-pdb.yaml create mode 100644 docs/multitenant/lrest-based/usecase/config_map_pdb.yaml create mode 100644 docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml create mode 100644 docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/makefile create mode 100644 docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml create mode 100644 docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.ORIGNINAL create mode 100644 docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.pippo create mode 100644 docs/multitenant/lrest-based/usecase/parameters.txt create mode 100644 docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml create mode 100644 docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/altersystem_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/ca.crt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/ca.key create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/ca.srl create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cdbnamespace_binding.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.crt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.key create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.srl create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbpass.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbuser.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbpass.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbuser.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbpwd.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbusr.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbpass.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbuser.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/extfile.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt.orig create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.csr create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.key create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbpwd.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbusr.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/public.pem create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbpass.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbuser.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/check.sh create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb3_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod_delcascade.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/dbpass.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/dbuser.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb3_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/dmpplane.819519 create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbpass.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbuser.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbpwd.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbusr.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbpass.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbuser.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/extfile.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/makefile create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb3_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb2_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb3_resource.yaml create mode 120000 docs/multitenant/lrest-based/usecase05_to_be_removed/oracle-database-operator.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/parameters.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/pdbnamespace_binding.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/pdbpwd.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/pdbusr.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/plug_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/public.pem create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/resetaction_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/server.csr create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/testDelCascade.sh create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/tkpc_k8slrestsetup.sh create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/tls.crt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/tls.key create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/unplug_lrpdb1_resource.yaml create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/wbpass.txt create mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/wbuser.txt rename docs/multitenant/{ => ords-based}/NamespaceSeg.md (100%) create mode 100644 docs/multitenant/ords-based/README.md rename docs/multitenant/{ => ords-based}/images/K8S_NAMESPACE_SEG.png (100%) rename docs/multitenant/{ => ords-based}/images/K8S_SECURE1.png (100%) rename docs/multitenant/{ => ords-based}/images/K8S_SECURE2.png (100%) rename docs/multitenant/{ => ords-based}/images/K8S_SECURE3.png (100%) rename docs/multitenant/{ => ords-based}/images/K8S_SECURE4.png (100%) create mode 100644 docs/multitenant/ords-based/images/makerunall.png create mode 100644 docs/multitenant/ords-based/images/makesecrets_1_1.png rename docs/multitenant/{ => ords-based}/openssl_schema.jpg (100%) rename docs/multitenant/{ => ords-based}/provisioning/example_setup_using_oci_oke_cluster.md (100%) rename docs/multitenant/{ => ords-based}/provisioning/multinamespace/cdb_create.yaml (95%) create mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/cdb_secret.yaml rename docs/multitenant/{ => ords-based}/provisioning/multinamespace/pdb_clone.yaml (96%) rename docs/multitenant/{ => ords-based}/provisioning/multinamespace/pdb_close.yaml (95%) rename docs/multitenant/{usecase03 => ords-based/provisioning/multinamespace}/pdb_create.yaml (95%) rename docs/multitenant/{ => ords-based}/provisioning/multinamespace/pdb_delete.yaml (93%) rename docs/multitenant/{ => ords-based}/provisioning/multinamespace/pdb_open.yaml (95%) rename docs/multitenant/{ => ords-based}/provisioning/multinamespace/pdb_plug.yaml (95%) rename docs/multitenant/{usecase01 => ords-based/provisioning/multinamespace}/pdb_secret.yaml (91%) rename docs/multitenant/{ => ords-based}/provisioning/multinamespace/pdb_unplug.yaml (95%) rename docs/multitenant/{ => ords-based}/provisioning/ords_image.md (100%) rename docs/multitenant/{ => ords-based}/provisioning/quickOKEcreation.md (100%) rename docs/multitenant/{ => ords-based}/provisioning/singlenamespace/cdb_create.yaml (95%) create mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml rename docs/multitenant/{ => ords-based}/provisioning/singlenamespace/pdb_clone.yaml (100%) rename docs/multitenant/{ => ords-based}/provisioning/singlenamespace/pdb_close.yaml (95%) rename docs/multitenant/{ => ords-based}/provisioning/singlenamespace/pdb_create.yaml (95%) rename docs/multitenant/{usecase01 => ords-based/provisioning/singlenamespace}/pdb_delete.yaml (94%) rename docs/multitenant/{usecase01 => ords-based/provisioning/singlenamespace}/pdb_open.yaml (95%) rename docs/multitenant/{ => ords-based}/provisioning/singlenamespace/pdb_plug.yaml (96%) create mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml rename docs/multitenant/{usecase02 => ords-based/provisioning/singlenamespace}/pdb_unplug.yaml (95%) create mode 100644 docs/multitenant/ords-based/usecase/README.md create mode 100644 docs/multitenant/ords-based/usecase/authsection01.yaml create mode 100644 docs/multitenant/ords-based/usecase/authsection02.yaml create mode 100644 docs/multitenant/ords-based/usecase/ca.crt create mode 100644 docs/multitenant/ords-based/usecase/ca.key create mode 100644 docs/multitenant/ords-based/usecase/ca.srl create mode 100644 docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml create mode 100644 docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/create_cdb_secrets.yaml create mode 100644 docs/multitenant/ords-based/usecase/create_ords_pod.yaml create mode 100644 docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/create_pdb_secrets.yaml create mode 100644 docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/extfile.txt create mode 100644 docs/multitenant/ords-based/usecase/makefile create mode 100644 docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/oracle-database-operator.yaml create mode 100644 docs/multitenant/ords-based/usecase/parameters.txt create mode 100644 docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml create mode 100644 docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase/public.pem create mode 100644 docs/multitenant/ords-based/usecase/server.csr create mode 100644 docs/multitenant/ords-based/usecase/tls.crt create mode 100644 docs/multitenant/ords-based/usecase/tls.key create mode 100644 docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml rename docs/multitenant/{ => ords-based}/usecase01/README.md (100%) rename docs/multitenant/{ => ords-based}/usecase01/ca.crt (100%) rename docs/multitenant/{ => ords-based}/usecase01/ca.key (100%) rename docs/multitenant/{ => ords-based}/usecase01/ca.srl (100%) rename docs/multitenant/{ => ords-based}/usecase01/cdb_create.yaml (95%) rename docs/multitenant/{provisioning/singlenamespace => ords-based/usecase01}/cdb_secret.yaml (100%) rename docs/multitenant/{ => ords-based}/usecase01/extfile.txt (100%) rename docs/multitenant/{ => ords-based}/usecase01/logfiles/BuildImage.log (100%) rename docs/multitenant/{ => ords-based}/usecase01/logfiles/ImagePush.log (100%) rename docs/multitenant/{ => ords-based}/usecase01/logfiles/cdb.log (100%) rename docs/multitenant/{ => ords-based}/usecase01/logfiles/cdb_creation.log (100%) rename docs/multitenant/{ => ords-based}/usecase01/logfiles/openssl_execution.log (100%) rename docs/multitenant/{ => ords-based}/usecase01/logfiles/ordsconfig.log (100%) rename docs/multitenant/{ => ords-based}/usecase01/logfiles/tagandpush.log (100%) rename docs/multitenant/{ => ords-based}/usecase01/logfiles/testapi.log (100%) rename docs/multitenant/{ => ords-based}/usecase01/makefile (98%) rename docs/multitenant/{ => ords-based}/usecase01/oracle-database-operator.yaml (100%) rename docs/multitenant/{ => ords-based}/usecase01/pdb_close.yaml (95%) rename docs/multitenant/{ => ords-based}/usecase01/pdb_create.yaml (95%) rename docs/multitenant/{provisioning/singlenamespace => ords-based/usecase01}/pdb_delete.yaml (94%) rename docs/multitenant/{ => ords-based}/usecase01/pdb_map.yaml (95%) rename docs/multitenant/{provisioning/singlenamespace => ords-based/usecase01}/pdb_open.yaml (95%) rename docs/multitenant/{provisioning/singlenamespace => ords-based/usecase01}/pdb_secret.yaml (100%) rename docs/multitenant/{ => ords-based}/usecase01/server.csr (100%) rename docs/multitenant/{ => ords-based}/usecase01/tde_secret.yaml (100%) rename docs/multitenant/{ => ords-based}/usecase01/tls.crt (100%) rename docs/multitenant/{ => ords-based}/usecase01/tls.key (100%) rename docs/multitenant/{ => ords-based}/usecase02/README.md (100%) rename docs/multitenant/{ => ords-based}/usecase02/pdb_clone.yaml (96%) rename docs/multitenant/{ => ords-based}/usecase02/pdb_plug.yaml (96%) rename docs/multitenant/{ => ords-based}/usecase02/pdb_plugtde.yaml (96%) rename docs/multitenant/{provisioning/singlenamespace => ords-based/usecase02}/pdb_unplug.yaml (95%) rename docs/multitenant/{ => ords-based}/usecase02/pdb_unplugtde.yaml (96%) rename docs/multitenant/{ => ords-based}/usecase02/tde_secret.yaml (100%) rename docs/multitenant/{ => ords-based}/usecase03/Dockerfile (100%) rename docs/multitenant/{ => ords-based}/usecase03/NamespaceSegregation.png (100%) rename docs/multitenant/{ => ords-based}/usecase03/README.md (100%) create mode 100644 docs/multitenant/ords-based/usecase03/ca.crt create mode 100644 docs/multitenant/ords-based/usecase03/ca.key create mode 100644 docs/multitenant/ords-based/usecase03/ca.srl rename docs/multitenant/{ => ords-based}/usecase03/cdb_create.yaml (95%) rename docs/multitenant/{ => ords-based}/usecase03/cdb_creation_log.txt (100%) rename docs/multitenant/{ => ords-based}/usecase03/cdb_secret.yaml (100%) create mode 100644 docs/multitenant/ords-based/usecase03/extfile.txt rename docs/multitenant/{ => ords-based}/usecase03/gentlscert.sh (100%) rename docs/multitenant/{ => ords-based}/usecase03/makefile (97%) rename docs/multitenant/{ => ords-based}/usecase03/ns_namespace_cdb.yaml (100%) rename docs/multitenant/{ => ords-based}/usecase03/ns_namespace_pdb.yaml (100%) rename docs/multitenant/{ => ords-based}/usecase03/operator_creation_log.txt (100%) rename docs/multitenant/{provisioning/multinamespace => ords-based/usecase03}/pdb_create.yaml (95%) rename docs/multitenant/{ => ords-based}/usecase03/pdb_creation_log.txt (100%) rename docs/multitenant/{ => ords-based}/usecase03/pdb_secret.yaml (100%) rename docs/multitenant/{ => ords-based}/usecase03/runOrdsSSL.sh (100%) create mode 100644 docs/multitenant/ords-based/usecase03/server.csr create mode 100644 docs/multitenant/ords-based/usecase03/tls.crt create mode 100644 docs/multitenant/ords-based/usecase03/tls.key delete mode 100644 docs/multitenant/usecase01/cdb_secret.yaml create mode 100644 docs/ordsservices/README.md create mode 100644 docs/ordsservices/TROUBLESHOOTING.md create mode 100644 docs/ordsservices/api.md create mode 100644 docs/ordsservices/autoupgrade.md create mode 100644 docs/ordsservices/examples/adb.md create mode 100644 docs/ordsservices/examples/adb_oraoper.md create mode 100644 docs/ordsservices/examples/mongo_api.md create mode 100644 docs/ordsservices/examples/multi_pool.md create mode 100644 docs/ordsservices/examples/sidb_container.md create mode 100644 docs/ordsservices/usecase01/create_mong_schema.sql create mode 100644 docs/ordsservices/usecase01/create_multisrv.yaml create mode 100644 docs/ordsservices/usecase01/create_ords.sh create mode 100644 docs/ordsservices/usecase01/create_ords.yaml create mode 100644 docs/ordsservices/usecase01/create_ordsnamespace.yaml create mode 100644 docs/ordsservices/usecase01/create_registry_secret.sh create mode 100644 docs/ordsservices/usecase01/create_secretregistry.sh create mode 100644 docs/ordsservices/usecase01/create_singleinstance_db.yaml create mode 100644 docs/ordsservices/usecase01/default-ns-role-binding.yaml create mode 100644 docs/ordsservices/usecase01/diaginit.sh create mode 100644 docs/ordsservices/usecase01/help create mode 100644 docs/ordsservices/usecase01/makefile create mode 100644 docs/ordsservices/usecase01/node-rbac.yaml create mode 100644 docs/ordsservices/usecase01/node_rbac.yaml create mode 100644 docs/ordsservices/usecase01/oracle-database-operator.yaml create mode 100644 docs/ordsservices/usecase01/ords-ns-role-binding.yaml create mode 100644 docs/ordsservices/usecase01/persistent-volume-rbac.yaml create mode 100644 docs/ordsservices/usecase01/rest_server_creation.yaml create mode 100644 docs/ordsservices/usecase01/rest_server_creation_mongo.yaml create mode 100644 docs/ordsservices/usecase01/sidb_create.yaml create mode 100644 docs/ordsservices/usecase01/sqlcreate.sql create mode 100644 docs/ordsservices/usecase01/storage-class-rbac.yaml create mode 100644 docs/ordsservices/usecase01/tnsadmin/tnsnames.ora create mode 100644 docs/ordsservices/usecase01/tnsnames.ora diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index 2b5b576d..e7155457 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -1,252 +1,13 @@ - +# Multitenant Controllers -# Oracle Multitenant Database Controllers -The Oracle Database Operator for kubernetes uses two controllers to manage [Pluggable Database life cycle][oradocpdb] +Starting from operator version 1.2.0, there are two classes of multitenant controllers: one based on [ORDS](https://www.oracle.com/uk/database/technologies/appdev/rest.html) and another based on a dedicated REST server for the operator, called LREST. In both cases, the overall architecture remains unchanged. A pod running a REST server (either LREST or ORDS) acts as the proxy server connected to the container database (CDB) for all incoming kubectl requests. -- CDB controller -- PDB controller +## What are the differences -By usigng CDB/PDB controllers you can perform the following actions **CREATE**,**MODIFY(OPEN/COSE)**,**DELETE**,**CLONE**,**PLUG** and **UNPLUG** - -This file examplains how to setup CDB and PDB controllers, additional details can be found in the README files under usecases directories.. - -- [Usecase01][uc01] pdb crd and cdb pod are running in the same namesaoce -- [Usecase02][uc02] unplug and plug operation examples -- [Usecase03][uc03] multiple namespace example cdb pod ,pdb crd and pod operator are running in different namespaces. - -> **NOTE** that there is no controller for Container Database Operations - -## Macro steps for setup - -- Deply the Oracle Database Operator -- Create Ords based image for CDB pod -- Container DB user creation -- Create secrets for credentials -- Create certificates for https connection -- Create CDB pod - -## Oracle DB Operator Multitenant Database Controller Deployment - -To deploy OraOperator, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. - -After the Oracle Database Operator is deployed, you can see the DB Operator Pods running in the Kubernetes Cluster. As part of the `OraOperator` deployment, the multitenant Database Controller is deployed. You can see the CRDs (Custom Resource Definition) for the CDB and PDBs in the list of CRDs. The following output is an example of such a deployment: - -```bash -[root@test-server oracle-database-operator]# kubectl get ns -NAME STATUS AGE -cert-manager Active 32h -default Active 245d -kube-node-lease Active 245d -kube-public Active 245d -kube-system Active 245d -oracle-database-operator-system Active 24h <<<< namespace to deploy the Oracle Database Operator - -[root@test-server oracle-database-operator]# kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 0 28s - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 29s -service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 29s - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 29s - -NAME DESIRED CURRENT READY AGE -replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 29s -[root@docker-test-server oracle-database-operator]# - -[root@test-server oracle-database-operator]# kubectl get crd -NAME CREATED AT -autonomouscontainerdatabases.database.oracle.com 2022-06-22T01:21:36Z -autonomousdatabasebackups.database.oracle.com 2022-06-22T01:21:36Z -autonomousdatabaserestores.database.oracle.com 2022-06-22T01:21:37Z -autonomousdatabases.database.oracle.com 2022-06-22T01:21:37Z -cdbs.database.oracle.com 2022-06-22T01:21:37Z <<<< -certificaterequests.cert-manager.io 2022-06-21T17:03:46Z -certificates.cert-manager.io 2022-06-21T17:03:47Z -challenges.acme.cert-manager.io 2022-06-21T17:03:47Z -clusterissuers.cert-manager.io 2022-06-21T17:03:48Z -dbcssystems.database.oracle.com 2022-06-22T01:21:38Z -issuers.cert-manager.io 2022-06-21T17:03:49Z -oraclerestdataservices.database.oracle.com 2022-06-22T01:21:38Z -orders.acme.cert-manager.io 2022-06-21T17:03:49Z -pdbs.database.oracle.com 2022-06-22T01:21:39Z <<<< -shardingdatabases.database.oracle.com 2022-06-22T01:21:39Z -singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z -``` - - -## Prerequsites to manage PDB Life Cycle using Oracle DB Operator Multitenant Database Controller - -* [Prepare the container database for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) -* [Oracle REST Data Service or ORDS Image](#oracle-rest-data-service-ords-image) -* [Kubernetes Secrets](#kubernetes-secrets) -* [Kubernetes CRD for CDB](#kubernetes-crd-for-cdb) -* [Kubernetes CRD for PDB](#kubernetes-crd-for-pdb) - -## Prepare the container database for PDB Lifecycle Management (PDB-LM) - -Pluggable Database (PDB) management operations are performed in the Container Database (CDB). These operations include create, clone, plug, unplug, delete, modify and map pdb. - -You cannot have an ORDS-enabled schema in the container database. To perform the PDB lifecycle management operations, you must first use the following steps to define the default CDB administrator credentials on target CDBs: - -Create the CDB administrator user, and grant the required privileges. In this example, the user is `C##DBAPI_CDB_ADMIN`. However, any suitable common user name can be used. - -```SQL -SQL> conn /as sysdba - --- Create following users at the database level: - -ALTER SESSION SET "_oracle_script"=true; -DROP USER C##DBAPI_CDB_ADMIN cascade; -CREATE USER C##DBAPI_CDB_ADMIN IDENTIFIED BY CONTAINER=ALL ACCOUNT UNLOCK; -GRANT SYSOPER TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; -GRANT SYSDBA TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; -GRANT CREATE SESSION TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; - - --- Verify the account status of the following usernames. They should not be in locked status: - -col username for a30 -col account_status for a30 -select username, account_status from dba_users where username in ('ORDS_PUBLIC_USER','C##DBAPI_CDB_ADMIN','APEX_PUBLIC_USER','APEX_REST_PUBLIC_USER'); -``` - -## OCI OKE(Kubernetes Cluster) - -You can use an [OKE in Oracle Cloud Infrastructure][okelink] to configure the operator for PDB lifecycle management. **Note that there is no restriction about container database location; it can be anywhere (on cloud or premises , on any supported platform).** -To quickly create an OKE cluster in your OCI cloud environment you can use the following [link](./provisioning/quickOKEcreation.md). -In this setup example [provisioning example setup](./provisioning/example_setup_using_oci_oke_cluster.md), the Container database is running on a OCI Exadata Database Cluster. - - -## Oracle REST Data Service (ORDS) Image - - The PDB Database controllers require a pod running a dedicated rest server image based on [ORDS][ordsdoc]. Read the following [link](./provisioning/ords_image.md) to build the ords images. - - -## Kubernetes Secrets - - Multitenant Controllers use Kubernetes Secrets to store the required credential. The https certificates are stored in Kubernetes Secrets as well. - - **Note** In multi namespace enviroment you have to create specific secrets for each namespaces - - **Note** In multi namespace enviroment you have to create specific secrets for each namespaces - -### Secrets for CDB CRD - - Create a secret file as shown here: [config/samples/multitenant/cdb_secret.yaml](../multitenant/provisioning/singlenamespace/cdb_create.yaml). Modify this file with the `base64` encoded values of the required passwords for CDB, and use this file to create the required secrets. - - ```bash - kubectl apply -f cdb_secret.yaml - ``` - - **Note:** To obtain the `base64` encoded value for a password, use the following command: - - ```bash - echo -n "" | base64 - ``` - - **Note:** After successful creation of the CDB Resource, the CDB secrets are deleted from the Kubernetes system . - -### Secrets for PDB CRD - - Create a secret file as shown here: [pdb_secret.yaml](../multitenant/provisioning/singlenamespace/pdb_secret.yaml). Edit the file using your base64 credential and apply it. - - ```bash - kubectl apply -f pdb_secret.yaml - ``` - - **NOTE:** Don't leave plaintext files containing sensitive data on disk. After loading the Secret, remove the plaintext file or move it to secure storage. - -### Secrets for CERTIFICATES - -Create the certificates and key on your local host, and use them to create the Kubernetes secret. - -```bash -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost Root CA " -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost" -out server.csr -echo "subjectAltName=DNS:cdb-dev-ords,DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -``` - -```bash -kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system -kubectl create secret generic db-ca --from-file=ca.crt -n oracle-database-operator-system -``` - -image_not_found - -**Note:** On successful creation of the certificates secret creation remove files or move to secure storage . - -## Kubernetes CRD for CDB - -The Oracle Database Operator Multitenant Controller creates the CDB kind as a custom resource that models a target CDB as a native Kubernetes object. This kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. Each CDB resource follows the CDB CRD as defined here: [config/crd/bases/database.oracle.com_cdbs.yaml](../../config/crd/bases/database.oracle.com_cdbs.yaml) - -To create a CDB CRD, see this example `.yaml` file: [cdb_create.yaml](../multitenant/provisioning/singlenamespace/cdb_create.yaml) - -**Note:** The password and username fields in this *cdb.yaml* Yaml are the Kubernetes Secrets created earlier in this procedure. For more information, see the section [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). To understand more about creating secrets for pulling images from a Docker private registry, see [Kubernetes Private Registry Documenation]( https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). - -Create a CDB CRD Resource example - -```bash -kubectl apply -f cdb_create.yaml -``` - -see [usecase01][uc01] and usecase03[uc03] for more information about file configuration - -## Kubernetes CRD for PDB - -The Oracle Database Operator Multitenant Controller creates the PDB kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. You cannot have more than one Kubernetes resource for a target PDB. This PDB resource can be used to perform PDB-LM operations by specifying the action attribute in the PDB Specs. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -To create a PDB CRD Resource, a sample .yaml file is available here: [pdb_create.yaml](../multitenant/provisioning/singlenamespace/pdb_create.yaml) - -```bash -kubectl apply -f cdb_create.yaml -``` - -## Usecases files list - -### Single Namespace - -1. [Create CDB](./provisioning/singlenamespace/cdb_create.yaml) -2. [Create PDB](./provisioning/singlenamespace/pdb_create.yaml) -3. [Clone PDB](./provisioning/singlenamespace/pdb_clone.yaml) -4. [Open PDB](./provisioning/singlenamespace/pdb_open.yaml) -4. [Close PDB](./provisioning/singlenamespace/pdb_close.yaml) -5. [Delete PDB](./provisioning/singlenamespace/pdb_delete.yaml) -6. [Unplug PDB](./provisioning/singlenamespace/pdb_unplug.yaml) -7. [Plug PDB](./provisioning/singlenamespace/pdb_plug.yaml) - -### Multiple namespace (cdbnamespace,dbnamespace) - -1. [Create CDB](./provisioning/multinamespace/cdb_create.yaml) -2. [Create PDB](./provisioning/multinamespace/pdb_create.yaml) -3. [Clone PDB](./provisioning/multinamespace/pdb_clone.yaml) -4. [Open PDB](./provisioning/multinamespace/pdb_open.yaml) -4. [Close PDB](./provisioning/multinamespace/pdb_close.yaml) -5. [Delete PDB](./provisioning/multinamespace/pdb_delete.yaml) -6. [Unplug PDB](./provisioning/multinamespace/pdb_unplug.yaml) - -## Known issues - - - Ords installatian failure if pluaggable databases in the container db are not opened - - - Version 1.1.0: encoded password for https authentication may include carriege return as consequence the https request fails with http 404 error. W/A generate encoded password using **printf** instead of **echo**. - - - pdb controller authentication suddenly failes without any system change. Check the certificate expiration date **openssl .... -days 365** - - - Nothing happens after cdb yaml file applying: Make sure to have properly configure the WHATCH_NAMESPACE list in the operator yaml file - - [okelink]:https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm - [ordsdoc]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.1/index.html - [uc01]:../multitenant/usecase01/README.md - [uc02]:../multitenant/usecase02/README.md - [uc03]:../multitenant/usecase03/README.md - [oradocpdb]:https://docs.oracle.com/en/database/oracle/oracle-database/21/multi/introduction-to-the-multitenant-architecture.html#GUID-AB84D6C9-4BBE-4D36-992F-2BB85739329F - - \ No newline at end of file +- **ORDS** is a standalone product, while **LREST** is not. **LREST** is an integral component of the Oracle Database Operator. +- Regarding the YAML file, no payload parameters for the existing functionalities have been changed. +- The **CRD** names are different: for controllers based on [ORDS](./ords-based/README.md), we have **PDB** and **CDB**, while for controllers based on [LREST](./lrest-based/README.md), we have **LRPDB** and **LREST**. +- If you use an LREST-based controller, there is no need to manually create the REST server pod. The image is available for download on OCR. +- Controllers based on ORDS allow you to manage PDB parameters using kubectl. +- ORDS controllers currently do not support ORDS version 24.1. diff --git a/docs/multitenant/lrest-based/README.md b/docs/multitenant/lrest-based/README.md new file mode 100644 index 00000000..6475be90 --- /dev/null +++ b/docs/multitenant/lrest-based/README.md @@ -0,0 +1,488 @@ + + + +# LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT + + +- [LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT](#lrest-based-multitenant-controllers-for-pdb-life-cycle-management) + - [STEP BY STEP CONFIGURATION](#step-by-step-configuration) + - [Multiple namespace setup](#multiple-namespace-setup) + - [Create the operator](#create-the-operator) + - [Container database setup](#container-database-setup) + - [Apply rolebinding](#apply-rolebinding) + - [Certificate and credentials](#certificate-and-credentials) + - [Private key 🔑](#private-key-) + - [Public Key 🔑](#public-key-) + - [Certificates](#certificates) + - [Create secrets for certificate and keys](#create-secrets-for-certificate-and-keys) + - [Create secrets with encrypted password](#create-secrets-with-encrypted-password) + - [Create lrest pod](#create-lrest-pod) + - [Create PDB](#create-pdb) + - [pdb config map 🆕](#pdb-config-map) + - [Open PDB](#open-pdb) + - [Close PDB](#close-pdb) + - [Clone PDB](#clone-pdb) + - [Unplug PDB](#unplug-pdb) + - [Plug PDB](#plug-pdb) + - [Delete PDB](#delete-pdb) + - [Map PDB](#map-pdb) + + + + + +**Lrpdb** and **lrest** are two controllers for the pdb life cycle management (**PDBLCM**), they relye on a dedicated rest server (Lite Rest Server). +Image to run lrest controller is available on OCR. The container database can be anywhere (on prem and in cloud) + +![generaleschema](./images/Generalschema2.jpg) + +## STEP BY STEP CONFIGURATION + +### Multiple namespace setup + +Before proceeding with controllers setup make sure that the operator is configured to work with multiple namespaces as specified in the [README](../../../README.md). +In this document each controller is running in a dedicated namespace lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains all the files reported in this document. + +Configure the **WACTH_NAMESPACE** list of the operator yaml file + +```bash +sed -i 's/value: ""/value: "oracle-database-operator-system,pdbnamespace,cdbnamespace"/g' oracle-database-operator.yaml +``` + +### Create the operator + +```bash +kubectl apply -f oracle-database-operator.yaml +``` +Check controller +```bash +kubectl get pods -n oracle-database-operator-system +NAME READY STATUS RESTARTS AGE +oracle-database-operator-controller-manager-796c9b87df-6xn7c 1/1 Running 0 22m +oracle-database-operator-controller-manager-796c9b87df-sckf2 1/1 Running 0 22m +oracle-database-operator-controller-manager-796c9b87df-t4qns 1/1 Running 0 22m +``` +### Container database setup + +On the container database configure the following account for pdb administration + +```sql +alter session set "_oracle_script"=true; +create user identified by ; +grant create session to container=all; +grant sysdba to container=all; +``` + + +### Apply rolebinding + + +Apply the following files : [pdbnamespace_binding.yaml](./usecase/pdbnamespace_binding.yaml) [cdbnamespace_binding.yaml](./usecase/cdbnamespace_binding.yaml) +```bash +kubectl apply -f pdbnamespace_binding.yaml +kubectl apply -f cdbnamespace_binding.yaml +``` + +### Certificate and credentials + +#### Private key 🔑 +> Before starting keys and certificate generation note that only private key **PCKS8** format is supported by LREST controllers. If you are using [openssl3](https://docs.openssl.org/master/) then pcks8 is generated by default, otherwise use the following command to create a pcks8 private key + +```bash +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out private.key +``` +#### Public Key 🔑 + +```bash +/usr/bin/openssl rsa -in private.key -outform PEM -pubout -out public.pem +``` +#### Certificates +```bash +openssl req -new -x509 -days 365 -key private.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out ca.crt +``` +```bash +openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=cdb-dev-lrest.cdbnamespace" -out server.csr +``` +```bash +/usr/bin/echo "subjectAltName=DNS:cdb-dev-lrest.cdbnamespace,DNS:www.example.com" > extfile.txt +``` +```bash +/usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey private.key -CAcreateserial -out tls.crt +``` + +### Create secrets for certificate and keys + +```bash +kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system +kubectl create secret generic db-ca --from-file="ca.crt" -n oracle-database-operator-system +kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n cdbnamespace +kubectl create secret generic db-ca --from-file="ca.crt" -n cdbnamespace +kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n pdbnamespace +kubectl create secret generic db-ca --from-file="ca.crt" -n pdbnamespace +``` + +```bash +kubectl create secret tls prvkey --key="private.key" --cert=ca.crt -n cdbnamespace +kubectl create secret generic pubkey --from-file=publicKey=public.pem -n cdbnamespace +kubectl create secret generic prvkey --from-file=privateKey="private.key" -n pdbnamespace +``` + +### Create secrets with encrypted password + +We create secrets for each credential (username and password) + +| secret usr | secrets pwd | credential description | +| -----------|-------------|-----------------------------------------------------------| +| **dbuser** |**dbpass** | the administrative user created on the container database | +| **wbuser** |**wbpass** | the user for https authentication | +| **pdbusr** |**pdbpwd** | the administrative user of the pdbs | + + +```bash +echo "[ADMINUSERNAME]" > dbuser.txt +echo "[ADMINUSERNAME PASSWORD]" > dbpass.txt +echo "[WEBUSER]" > wbuser.txt +echo "[WEBUSER PASSWORD]" > wbpass.txt +echo "[PDBUSERNAME]" > pdbusr.txt +echo "[PDBUSERNAME PASSWORD]" > pdbpwd.txt + +## Encrypt the credentials +openssl rsautl -encrypt -pubin -inkey public.pem -in dbuser.txt |base64 > e_dbuser.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in dbpass.txt |base64 > e_dbpass.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in wbuser.txt |base64 > e_wbuser.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in wbpass.txt |base64 > e_wbpass.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in pdbusr.txt |base64 > e_pdbusr.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in pdbpwd.txt |base64 > e_pdbpwd.txt + +kubectl create secret generic dbuser --from-file=e_dbuser.txt -n cdbnamespace +kubectl create secret generic dbpass --from-file=e_dbpass.txt -n cdbnamespace +kubectl create secret generic wbuser --from-file=e_wbuser.txt -n cdbnamespace +kubectl create secret generic wbpass --from-file=e_wbpass.txt -n cdbnamespace +kubectl create secret generic wbuser --from-file=e_wbuser.txt -n pdbnamespace +kubectl create secret generic wbpass --from-file=e_wbpass.txt -n pdbnamespace +kubectl create secret generic pdbusr --from-file=e_pdbusr.txt -n pdbnamespace +kubectl create secret generic pdbpwd --from-file=e_pdbpwd.txt -n pdbnamespace + +rm dbuser.txt dbpass.txt wbuser.txt wbpass.txt pdbusr.txt pdbpwd.txt \ + e_dbuser.txt e_dbpass.txt e_wbuser.txt e_wbpass.txt e_pdbusr.txt e_pdbpwd.txt +``` + +### Create lrest pod + +Use yaml file [create_lrest_pod.yaml](./usecase/create_lrest_pod.yaml) to create the rest pod and monitor the execution. + +```bash +kubectl apply -f create_lrest_pod.yaml +``` + +monitor the execution + +```bash +kubectl get pods -n cdbnamespace --watch +NAME READY STATUS RESTARTS AGE +cdb-dev-lrest-rs-9gvx2 0/1 Pending 0 0s +cdb-dev-lrest-rs-9gvx2 0/1 Pending 0 0s +cdb-dev-lrest-rs-9gvx2 0/1 ContainerCreating 0 0s +cdb-dev-lrest-rs-9gvx2 1/1 Running 0 2s + +kubectl get lrest -n cdbnamespace +NAME CDB NAME DB SERVER DB PORT TNS STRING REPLICAS STATUS MESSAGE +cdb-dev DB12 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) 1 Ready +``` + +Check pod logs + +```bash +/usr/local/go/bin/kubectl logs -f `/usr/local/go/bin/kubectl get pods -n cdbnamespace|grep lrest|cut -d ' ' -f 1` -n cdbnamespace +``` + +Output example: + +```text +... +... +2024/09/05 12:44:09 wallet file /opt/oracle/lrest/walletfile exists completed +2024/09/05 12:44:09 call: C.ReadWallet +LENCHECK: 7 11 7 8 +2024/09/05 12:44:09 ===== DUMP INFO ==== +00000000 28 44 45 53 43 52 49 50 54 49 4f 4e 3d 28 43 4f |(DESCRIPTION=(CO| +00000010 4e 4e 45 43 54 5f 54 49 4d 45 4f 55 54 3d 39 30 |NNECT_TIMEOUT=90| +00000020 29 28 52 45 54 52 59 5f 43 4f 55 4e 54 3d 33 30 |)(RETRY_COUNT=30| +00000030 29 28 52 45 54 52 59 5f 44 45 4c 41 59 3d 31 30 |)(RETRY_DELAY=10| +00000040 29 28 54 52 41 4e 53 50 4f 52 54 5f 43 4f 4e 4e |)(TRANSPORT_CONN| +00000050 45 43 54 5f 54 49 4d 45 4f 55 54 3d 37 30 29 28 |ECT_TIMEOUT=70)(| +00000060 4c 4f 41 44 5f 42 41 4c 4c 41 4e 43 45 3d 4f 4e |LOAD_BALLANCE=ON| +00000070 29 28 41 44 44 52 45 53 53 3d 28 50 52 4f 54 4f |)(ADDRESS=(PROTO| +00000080 43 4f 4c 3d 54 43 50 29 28 48 4f 53 54 3d 73 63 |COL=TCP)(HOST=sc| +00000090 61 6e 31 32 2e 74 65 73 74 72 61 63 2e 63 6f 6d |an12.testrac.com| +000000a0 29 28 50 4f 52 54 3d 31 35 32 31 29 28 49 50 3d |)(PORT=1521)(IP=| +000000b0 56 34 5f 4f 4e 4c 59 29 29 28 4c 4f 41 44 5f 42 |V4_ONLY))(LOAD_B| +000000c0 41 4c 4c 41 4e 43 45 3d 4f 4e 29 28 41 44 44 52 |ALLANCE=ON)(ADDR| +000000d0 45 53 53 3d 28 50 52 4f 54 4f 43 4f 4c 3d 54 43 |ESS=(PROTOCOL=TC| +000000e0 50 29 28 48 4f 53 54 3d 73 63 61 6e 33 34 2e 74 |P)(HOST=scan34.t| +000000f0 65 73 74 72 61 63 2e 63 6f 6d 29 28 50 4f 52 54 |estrac.com)(PORT| +00000100 3d 31 35 32 31 29 28 49 50 3d 56 34 5f 4f 4e 4c |=1521)(IP=V4_ONL| +00000110 59 29 29 28 43 4f 4e 4e 45 43 54 5f 44 41 54 41 |Y))(CONNECT_DATA| +00000120 3d 28 53 45 52 56 45 52 3d 44 45 44 49 43 41 54 |=(SERVER=DEDICAT| +00000130 45 44 29 28 53 45 52 56 49 43 45 5f 4e 41 4d 45 |ED)(SERVICE_NAME| +00000140 3d 54 45 53 54 4f 52 44 53 29 29 29 |=TESTORDS)))| +00000000 2f 6f 70 74 2f 6f 72 61 63 6c 65 2f 6c 72 65 73 |/opt/oracle/lres| +00000010 74 2f 77 61 6c 6c 65 74 66 69 6c 65 |t/walletfile| +2024/09/05 12:44:09 Get credential from wallet +7 +8 +2024/09/05 12:44:09 dbuser: restdba webuser :welcome +2024/09/05 12:44:09 Connections Handle +2024/09/05 12:44:09 Working Session Aarry dbhanlde=0x1944120 +2024/09/05 12:44:09 Monitor Session Array dbhanlde=0x1a4af70 +2024/09/05 12:44:09 Open cursors +Parsing sqltext=select inst_id,con_id,open_mode,nvl(restricted,'NONE'),total_size from gv$pdbs where inst_id = SYS_CONTEXT('USERENV','INSTANCE') and name =upper(:b1) +Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 +2024/09/05 12:44:11 Protocol=https +2024/09/05 12:44:11 starting HTTPS/SSL server +2024/09/05 12:44:11 ==== TLS CONFIGURATION === +2024/09/05 12:44:11 srv=0xc000106000 +2024/09/05 12:44:11 cfg=0xc0000a2058 +2024/09/05 12:44:11 mux=0xc0000a2050 +2024/09/05 12:44:11 tls.minversion=771 +2024/09/05 12:44:11 CipherSuites=[49200 49172 157 53] +2024/09/05 12:44:11 cer=/opt/oracle/lrest/certificates/tls.crt +2024/09/05 12:44:11 key=/opt/oracle/lrest/certificates/tls.key +2024/09/05 12:44:11 ========================== +2024/09/05 12:44:11 HTTPS: Listening port=8888 +2024/09/05 12:44:23 call BasicAuth Succeded +2024/09/05 12:44:23 HTTP: [1:0] Invalid credential <-- This message can be ignored + +``` + +**lrest pod creation** - parameters list +| Name | Dcription | +--------------------------|-------------------------------------------------------------------------------| +|cdbName | Name of the container db | +|lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** | +|dbTnsurl | Tns alias of the container db | +|deletePdbCascade | Delete all the pdbs associated to cdb resource when cdb resource it's dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | +|cdbAdminUser | Secret: the admin user | +|cdbAdminPwd | Secret: the admin user password | +|webServerUser | Secret: the https user | +|webServerPwd | Secret: the https user password | +|cdbTlsCrt | Secret: the tls.crt | +|cdbPubKey | Secret: the public key | +|cdbPrvKey | Secret: the private key | + + + + +### Create PDB + +Apply yaml file [create_lrpdb1_resource.yaml](./usecase/clone_lrpdb1_resource.yaml) to create a pluggable database + +```bash +kubectl apply -f create_lrpdb1_resource.yaml +``` +Check the status of the resource and the pdb existence on the container db + +```bash +kubectl get lrpdb -n pdbnamespace +NAME CONNECT_STRING CDB NAME LRPDB NAME LRPDB STATE LRPDB SIZE STATUS MESSAGE LAST SQLCODE +lrpdb1 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) DB12 pdbdev MOUNTED 2G Ready Success +``` + +```bash +SQL> show pdbs + + CON_ID CON_NAME OPEN MODE RESTRICTED +---------- ------------------------------ ---------- ---------- + 2 PDB$SEED READ ONLY NO + 3 PDBDEV MOUNTED +SQL> +``` +Note that after creation the pdb is not open you need to explicitly open it using dedicated yaml file. + +**pdb creation** - parameters list + +| Name | Dcription | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | Rest server resource name | +|cdbNamespace | Namespace of the rest server | +|cdbName | Name of the container database | +|pdbName | Name of the pluggable database to be created | +|assertiveLrpdbDeletion | Boolean: turn on the imperative approach on pdb resource deletion | +|adminpdbUser | Secret: pluggable database administrative user | +|adminpdbPass | Secret: password of pluggable database admin user | +|lrpdbTlsKey | Secret: tls.key | +|lrpdbTlsCrt | Secret: tls.crt | +|lrpdbTlsCat | Secret: ca.crt | +|webServerUser | Secret: the https user | +|webServerPwd | Secret: the https user password | +|cdbPrvKey | Secret: private key | +|cdbPubKey | Secret: public key | +|pdbconfigmap | kubernet config map which contains pdb init parameters | + +> NOTE: **assertiveLrpdbDeletion** needs to be specified for the following pdb actions **CLONE** **CREATE** **PLUG** **MAP**. + +🔥 **assertiveLrpdbDeletion** drops pluggable database using **INCLUDE DATAFILES** option + +All these parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** need to be specified in all pdb life cycle management yaml files; for the sake of simplicity they won't be exposed in the subsequent tables. + + +#### pdb config map + +Using **pdbconfigmap** it's possible to specify a kubernetes configmap for the pdb init parameters. The config map payload has the following scaffold: + + +``` +;; +;; +;; +.... +.... +;; +``` + +example of configmap creation: + +```bash +cat < parameters.txt +session_cached_cursors;100;spfile +open_cursors;100;spfile +db_file_multiblock_read_count;16;spfile +EOF + +kubectl create configmap config-map-pdb -n pdbnamespace --from-file=./parameters.txt + +kubectl describe configmap config-map-pdb -n pdbnamespace +Name: config-map-pdb +Namespace: pdbnamespace +Labels: +Annotations: + +Data +==== +parameters.txt: +---- +session_cached_cursors;100;spfile +open_cursors;100;spfile +db_file_multiblock_read_count;16;spfile +test_invalid_parameter;16;spfile +``` + +- Configmap, if specified, is applied during pdb **cloning** **opening** and **plugging** +- Configmap is not monitored by the reconciliation loop; this feature will be available in feature releases. This means that is if someone decides to manually alter init parameter the operator does not take any actions to syncronize pdb configuration with config map. +- **Alter system parameter feature** will be available in future releases. +- A ConfigMap application error (whatever reason) does not stop the execution of the process. A warning with sqlcode is reported in the logfile. + + + +### Open PDB + +Use file [open_lrpdb1_resource.yaml](./usecase/open_lrpdb1_resource.yaml) to open pdb. + +```bash +kubectl apply -f open_lrpdb1_resource.yaml +``` + + **pdb opening** - parameters list + +| Name | Dcription/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | Rest server resource name | +|cdbNamespace | Namespace of the rest server | +|cdbName | Name of the container database | +|pdbName | Name of the pluggable database to be created | +|action | Use **Modify** to open PDB | +|pdbState | Use **OPEN** to open PDB | +|modifyOption | Use **READ WRITE** to open PDB | + +### Close PDB + +Use file [close_lrpdb1_resource.yaml](./usecase/close_lrpdb1_resource.yaml) to close pdb. + +```bash +kubectl apply -f close_lrpdb1_resource.yaml +``` +**pdb closing** - parameters list +| Name | Dcription/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | Rest server resource name | +|cdbNamespace | Namespace of the rest server | +|cdbName | Name of the container database | +|pdbName | Name of the pluggable database to be created | +|action | Use **Modify** to close PDB | +|pdbState | Use **CLOSE** to close PDB | +|modifyOption | Use **IMMEDIATE** to close PDB | + +### Clone PDB ### + +Use file [clone_lrpdb1_resource.yaml](./usecase/clone_lrpdb1_resource.yaml) to clone pdb. + +```bash +kubeclt apply -f clone_lrpdb1_resource.yaml +``` +**pdb cloning** - parameters list +| Name | Dcription/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | Rest server resource name | +|cdbNamespace | Namespace of the rest server | +|cdbName | Name of the container database | +|pdbName | The name of the new pdb | +|srcPdbName | The name of source pdb | +|fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | +|totalSize | Set **unlimited** for cloning | +|tempSize | Set **unlimited** for cloning | +|pdbconfigmap | kubernet config map which contains pdb init parameters | +|action | Use **clone** to clone PDB | + +### Unplug PDB + +Use file [unplug_lrpdb1_resource.yaml](./usecase/unplug_lrpdb1_resource.yaml) to unplug PDB + +**pdb unplugging** +| Name | Dcription/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | Rest server resource name | +|cdbNamespace | Namespace of the rest server | +|cdbName | Name of the container database | +|pdbName | The name of the pdb | +|**xmlFileName** | Path of the xml file | +|action | **Unplug** | + +### Plug PDB + +Use file [plug_lrpdb1_resource.yaml](./usecase/plug_lrpdb1_resource.yaml) to plug PDB (in this example we plug the pdb unpluged in the previous step) + +**pdb plugging** +| Name | Dcription/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | Rest server resource name | +|cdbNamespace | Namespace of the rest server | +|cdbName | Name of the container database | +|pdbName | The name of the pdb | +|**xmlFileName** | Path of the xml file | +|action | **plug** | +|fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | +|sourceFileNameConversion | See parameter [SOURCE_FILE_NAME_CONVERT](https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/CREATE-PLUGGABLE-DATABASE.html#GUID-F2DBA8DD-EEA8-4BB7-A07F-78DC04DB1FFC__CCHEJFID) documentation | +|pdbconfigmap | kubernet config map which contains pdb init parameters | + +### Delete PDB + +Use file [delete_lrpdb1_resource.yaml](./usecase/delete_lrpdb1_resource.yaml) + +**pdb deletion** + +| Name | Dcription/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | Rest server resource name | +|cdbNamespace | Namespace of the rest server | +|cdbName | Name of the container database | +|action | **Delete** | +|dropAction | **INCLUDING** - Including datafiles or **NONE** | + + +### Map PDB + +If you need to create a CRD for an existing PDB you can use the map option by applying the file [map_lrpdb1_resource.yaml](./usecase/map_lrpdb1_resource.yaml) + + + + diff --git a/docs/multitenant/lrest-based/images/Generalschema2.jpg b/docs/multitenant/lrest-based/images/Generalschema2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7e7c20c045cf40599983304a17c99bd187e9ebec GIT binary patch literal 96239 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∨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 StsFA*^K#nnXn{ybpV_(Ly`TetC!4<|*X^PQXi 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 qc}usJvBQm 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

+p?`Me)>`WtWPG- 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 + + +# Use case directory + +The use case directory contains the yaml files to test the multitenant controller functionalities: create lrest pod and pdb operation *create / open / close / unplug / plug / delete / clone /map / parameter session* + +## Makefile helper + +Customizing yaml files (tns alias / credential / namespaces name etc...) is a long procedure prone to human error. A simple [makefile](../usecase/makefile) is available to quickly and safely configure yaml files with your system environment information. Just edit the [parameter file](../usecase/parameters.txt) before proceding. + +```text +TNSALIAS...............:[Tnsalias do not use quotes and avoid space in the string --> (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELA....] +DBUSER.................:[CDB admin user] +DBPASS.................:[CDB admin user password] +WBUSER.................:[HTTPS user] +WBPASS.................:[HTTPS user password] +PDBUSR.................:[PDB admin user] +PDBPWD.................:[PDB admin user password] +PDBNAMESPACE...........:[pdb namespace] +LRSNAMESPACE...........:[cdb namespace] +COMPANY................:[your company name] +APIVERSION.............:v4 --> do not edit +``` + +⚠ **WARNING: The makefile is intended to speed up the usecase directory configuartion only, it is not supported, the editing and configuration of yaml files for production system is left up to the end user** + +### Pre requisistes: + +- Make sure that **kubectl** is properly configured. +- Make sure that all requirements listed in the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) +- Make sure that administrative user on the container database is configured as documented. + +```bash +make operator +``` +This command creates the operator-database-operator.yaml in the local directory and set up the watchnamespace list. Note that the yaml file is not applyed. + +```bash +make secrets +``` +All the secrets with the ecrypted credential are created by this command execution. + +```bash +make genyaml +``` +*make genyaml* generates the required yaml files to work with multitenant controllers. + + +![image](../images/UsecaseSchema.jpg) + +## Diag commands and troubleshooting + +### Connect to rest server pod + +```bash +/usr/bin/kubectl exec -n -it -- /bin/bash +``` + + +```bash +## example ## + +kubectl get pods -n cdbnamespace +NAME READY STATUS RESTARTS AGE +cdb-dev-lrest-rs-fnw99 1/1 Running 1 (17h ago) 18h + +kubectl exec cdb-dev-lrest-rs-fnw99 -n cdbnamespace -it -- /bin/bash +[oracle@cdb-dev-lrest-rs-fnw99 ~]$ +``` + +### Monitor control plane + +```bash +kubectl logs -f -l control-plane=controller-manager -n oracle-database-operator-system +``` +```bash +## output example: ## +2024-10-28T23:54:25Z INFO lrpdb-webhook ValidateUpdate-Validating LRPDB spec for : lrpdb2 +2024-10-28T23:54:25Z INFO lrpdb-webhook validateCommon {"name": "lrpdb2"} +2024-10-28T23:54:25Z INFO lrpdb-webhook Valdiating LRPDB Resource Action : MODIFY +2024-10-29T10:07:34Z INFO lrpdb-webhook ValidateUpdate-Validating LRPDB spec for : lrpdb2 +2024-10-29T10:07:34Z INFO lrpdb-webhook ValidateUpdate-Validating LRPDB spec for : lrpdb1 +2024-10-29T16:49:15Z INFO lrpdb-webhook ValidateUpdate-Validating LRPDB spec for : lrpdb1 +2024-10-29T16:49:15Z INFO lrpdb-webhook validateCommon {"name": "lrpdb1"} +2024-10-29T16:49:15Z INFO lrpdb-webhook Valdiating LRPDB Resource Action : CREATE +2024-10-29T10:07:20Z INFO controller-runtime.certwatcher Updated current TLS certificate +2024-10-29T10:07:20Z INFO controller-runtime.webhook Serving webhook server {"host": "", "port": 9443} +2024-10-29T10:07:20Z INFO controller-runtime.certwatcher Starting certificate watcher +I1029 10:07:20.189724 1 leaderelection.go:250] attempting to acquire leader lease oracle-database-operator-system/a9d608ea.oracle.com... +2024-10-29T16:49:15Z INFO lrpdb-webhook Setting default values in LRPDB spec for : lrpdb1 + +``` + +### Error decrypting credential + +Resource creation failure due to decription error + +```text +2024-10-30T10:09:08Z INFO controllers.LRPDB getEncriptedSecret :pdbusr {"getEncriptedSecret": {"name":"lrpdb1","namespace":"pdbnamespace"}} +2024-10-30T10:09:08Z ERROR controllers.LRPDB Failed to parse private key - x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format) {"DecryptWithPrivKey": {"name":"lrpdb1","namespace":"pdbnamespace"}, "error": "x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format)"} +``` + + +**Solution**: make sure to use **PCKS8** format during private key generation. If you are not using openssl3 then execute this command + +```bash +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > mykey +``` + +### Crd details + +Use **describe** option to get crd information + +```bash +kubectl describe lrpdb lrpdb1 -n pdbnamespace +[...] + Secret: + Key: e_wbuser.txt + Secret Name: wbuser +Status: + Action: CREATE + Bitstat: 25 + Bitstatstr: |MPAPPL|MPWARN|MPINIT| + Conn String: (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) + Msg: Success + Open Mode: MOUNTED + Phase: Ready + Status: true + Total Size: 2G +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Created 108s LRPDB LRPDB 'pdbdev' created successfully + Normal Created 108s LRPDB PDB 'pdbdev' assertive pdb deletion turned on + Warning LRESTINFO 95s LRPDB pdb=pdbdev:test_invalid_parameter:16:spfile:2065 + Warning Done 15s (x12 over 2m25s) LRPDB cdb-dev + +``` \ No newline at end of file diff --git a/docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml new file mode 100644 index 00000000..0467a948 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml @@ -0,0 +1,50 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + action: "Alter" + alterSystemParameter : "cpu_count" + alterSystemValue : "3" + parameterScope : "memory" + + + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml b/docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml new file mode 100644 index 00000000..5fd355f4 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 + namespace: cdbnamespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml new file mode 100644 index 00000000..2c4afe13 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml @@ -0,0 +1,51 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + srcPdbName: "pdbdev" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + pdbconfigmap: "config-map-pdb" + assertiveLrpdbDeletion: true + action: "Clone" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml new file mode 100644 index 00000000..16255a87 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml @@ -0,0 +1,51 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb4 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone2" + srcPdbName: "pdbprd" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + pdbconfigmap: "config-map-pdb" + assertiveLrpdbDeletion: true + action: "Clone" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml new file mode 100644 index 00000000..87f7383d --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml new file mode 100644 index 00000000..0743bd8c --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml b/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml new file mode 100644 index 00000000..6c6ca519 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: ""new_clone" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/config-map-pdb.yaml b/docs/multitenant/lrest-based/usecase/config-map-pdb.yaml new file mode 100644 index 00000000..2769b498 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/config-map-pdb.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-map-pdb + namespace: pdbnamespace +data: + rdbmsparameters.txt: | + session_cached_cursors;100;spfile + open_cursors;100;spfile + db_file_multiblock_read_count;16;spfile + test_invalid_parameter;16;spfile diff --git a/docs/multitenant/lrest-based/usecase/config_map_pdb.yaml b/docs/multitenant/lrest-based/usecase/config_map_pdb.yaml new file mode 100644 index 00000000..2769b498 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/config_map_pdb.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-map-pdb + namespace: pdbnamespace +data: + rdbmsparameters.txt: | + session_cached_cursors;100;spfile + open_cursors;100;spfile + db_file_multiblock_read_count;16;spfile + test_invalid_parameter;16;spfile diff --git a/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml b/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml new file mode 100644 index 00000000..843386df --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v4 +kind: LREST +metadata: + name: cdb-dev + namespace: cdbnamespace +spec: + cdbName: "DB12" + lrestImage: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/lrest-dboper:latest + lrestImagePullPolicy: "Always" + dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + replicas: 1 + deletePdbCascade: true + cdbAdminUser: + secret: + secretName: "dbuser" + key: "e_dbuser.txt" + cdbAdminPwd: + secret: + secretName: "dbpass" + key: "e_dbpass.txt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + cdbPubKey: + secret: + secretName: "pubkey" + key: "publicKey" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml new file mode 100644 index 00000000..fa58d36a --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml @@ -0,0 +1,52 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + pdbconfigmap: "config-map-pdb" + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml new file mode 100644 index 00000000..02d5763b --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml @@ -0,0 +1,52 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + pdbconfigmap: "config-map-pdb" + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml new file mode 100644 index 00000000..1a3c328a --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml @@ -0,0 +1,45 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml new file mode 100644 index 00000000..747641d4 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml @@ -0,0 +1,45 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + pdbName: "pdbprd" + action: "Delete" + dropAction: "INCLUDING" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/makefile b/docs/multitenant/lrest-based/usecase/makefile new file mode 100644 index 00000000..4203baa4 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/makefile @@ -0,0 +1,911 @@ +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# __ __ _ __ _ _ +# | \/ | __ _| | _____ / _(_) | ___ +# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ +# | | | | (_| | < __/ _| | | __/ +# |_| |_|\__,_|_|\_\___|_| |_|_|\___| +# | | | | ___| |_ __ ___ _ __ +# | |_| |/ _ \ | '_ \ / _ \ '__| +# | _ | __/ | |_) | __/ | +# |_| |_|\___|_| .__/ \___|_| +# |_| +# +# WARNING: Using this makefile helps you to customize yaml +# files. Edit parameters.txt with your enviroment +# informartion and execute the following steps +# +# 1) make operator +# it configures the operator yaml files with the +# watch namelist required by the multitenant controllers +# +# 2) make secrets +# It configure the required secrets necessary to operate +# with pdbs multitenant controllers +# +# 3) make genyaml +# It automatically creates all the yaml files based on the +# information available in the parameters file +# +# LIST OF GENERAED YAML FILE +# +# ----------------------------- ---------------------------------- +# oracle-database-operator.yaml : oracle database operator +# cdbnamespace_binding.yaml : role binding for cdbnamespace +# pdbnamespace_binding.yaml : role binding for pdbnamespace +# create_lrest_secret.yaml : create secrets for rest server pod +# create_lrpdb_secret.yaml : create secrets for pluggable database +# create_lrest_pod.yaml : create rest server pod +# create_pdb1_resource.yaml : create first pluggable database +# create_pdb2_resource.yaml : create second pluggable database +# open_pdb1_resource.yaml : open first pluggable database +# open_pdb2_resource.yaml : open second pluggable database +# close_pdb1_resource.yaml : close first pluggable database +# close_pdb2_resource.yaml : close second pluggable database +# clone_lrpdb_resource.yaml : clone thrid pluggable database +# clone_pdb2_resource.yaml : clone 4th pluggable database +# delete_pdb1_resource.yaml : delete first pluggable database +# delete_pdb2_resource.yaml : delete sencond pluggable database +# delete_pdb3_resource.yaml : delete thrid pluggable database +# unplug_pdb1_resource.yaml : unplug first pluggable database +# plug_pdb1_resource.yaml : plug first pluggable database +# map_pdb1_resource.yaml : map the first pluggable database +# config_map.yam : pdb parameters array +# altersystem_pdb1_resource.yaml : chage cpu_count count parameter for the first pdb +# +DATE := `date "+%y%m%d%H%M%S"` +###################### +# PARAMETER SECTIONS # +###################### + +export PARAMETERS=parameters.txt +export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) +export DBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep DBUSER|cut -d : -f 2) +export DBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep DBPASS|cut -d : -f 2) +export WBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBUSER|cut -d : -f 2) +export WBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBPASS|cut -d : -f 2) +export PDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBUSR|cut -d : -f 2) +export PDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBPWD|cut -d : -f 2) +export PDBNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBNAMESPACE|cut -d : -f 2) +export LRSNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep LRSNAMESPACE|cut -d : -f 2) +export LRESTIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep LRESTIMG|cut -d : -f 2,3) +export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) +export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) +export OPRNAMESPACE=oracle-database-operator-system +export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml +export TEST_EXEC_TIMEOUT=3m + +REST_SERVER=lrest +SKEY=tls.key +SCRT=tls.crt +CART=ca.crt +PRVKEY=ca.key +PUBKEY=public.pem +COMPANY=oracle +DBUSERFILE=dbuser.txt +DBPASSFILE=dbpass.txt +WBUSERFILE=wbuser.txt +WBPASSFILE=wbpass.txt +PDBUSRFILE=pdbusr.txt +PDBPWDFILE=pdbpwd.txt + +################# +### FILE LIST ### +################# + +export LREST_POD=create_lrest_pod.yaml + +export LRPDBCRE1=create_pdb1_resource.yaml +export LRPDBCRE2=create_pdb2_resource.yaml + +export LRPDBCLOSE1=close_pdb1_resource.yaml +export LRPDBCLOSE2=close_pdb2_resource.yaml +export LRPDBCLOSE3=close_pdb3_resource.yaml + +export LRPDBOPEN1=open_pdb1_resource.yaml +export LRPDBOPEN2=open_pdb2_resource.yaml +export LRPDBOPEN3=open_pdb3_resource.yaml + +export LRPDBCLONE1=clone_pdb1_resource.yaml +export LRPDBCLONE2=clone_pdb2_resource.yaml + +export LRPDBDELETE1=delete_pdb1_resource.yaml +export LRPDBDELETE2=delete_pdb2_resource.yaml +export LRPDBDELETE3=delete_pdb3_resource.yaml + +export LRPDBUNPLUG1=unplug_pdb1_resource.yaml +export LRPDBPLUG1=plug_pdb1_resource.yaml + +export LRPDBMAP1=map_pdb1_resource.yaml +export LRPDBMAP2=map_pdb2_resource.yaml +export LRPDBMAP3=map_pdb3_resource.yaml + +export LRPDBMAP1=map_pdb1_resource.yaml +export LRPDBMAP2=map_pdb2_resource.yaml +export LRPDBMAP3=map_pdb3_resource.yaml + +export ALTERSYSTEMYAML=altersystem_pdb1_resource.yaml +export CONFIG_MAP=config_map_pdb.yaml + + + + +##BINARIES +export KUBECTL=/usr/bin/kubectl +OPENSSL=/usr/bin/openssl +ECHO=/usr/bin/echo +RM=/usr/bin/rm +CP=/usr/bin/cp +TAR=/usr/bin/tar +MKDIR=/usr/bin/mkdir +SED=/usr/bin/sed + +check: + @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) + @printf "DBUSER.................:%s\n" $(DBUSER) + @printf "DBPASS.................:%s\n" $(DBPASS) + @printf "WBUSER.................:%s\n" $(WBUSER) + @printf "WBPASS.................:%s\n" $(WBPASS) + @printf "PDBUSR.................:%s\n" $(PDBUSR) + @printf "PDBPWD.................:%s\n" $(PDBPWD) + @printf "PDBNAMESPACE...........:%s\n" $(PDBNAMESPACE) + @printf "LRSNAMESPACE...........:%s\n" $(LRSNAMESPACE) + @printf "COMPANY................:%s\n" $(COMPANY) + @printf "APIVERSION.............:%s\n" $(APIVERSION) + +define msg +@printf "\033[31;7m%s\033[0m\r" "......................................]" +@printf "\033[31;7m[\xF0\x9F\x91\x89 %s\033[0m\n" $(1) +endef + +tls: + $(call msg,"TLS GENERATION") + #$(OPENSSL) genrsa -out $(PRVKEY) 2048 + $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) + $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) \ + -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj \ + "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(LRSNAMESPACE)" -out server.csr + $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(LRSNAMESPACE)" > extfile.txt + $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) + $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) + +secrets: tls delsecrets + $(call msg,"CREATING NEW TLS/PRVKEY/PUBKEY SECRETS") + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(LRSNAMESPACE) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDBNAMESPACE) + #$(KUBECTL) create secret tls prvkey --key="$(PRVKEY)" --cert=ca.crt -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic prvkey --from-file=privateKey="$(PRVKEY)" -n $(PDBNAMESPACE) + $(call msg,"CREATING NEW CREDENTIAL SECRETS") + @$(ECHO) $(DBUSER) > $(DBUSERFILE) + @$(ECHO) $(DBPASS) > $(DBPASSFILE) + @$(ECHO) $(WBUSER) > $(WBUSERFILE) + @$(ECHO) $(WBPASS) > $(WBPASSFILE) + @$(ECHO) $(PDBUSR) > $(PDBUSRFILE) + @$(ECHO) $(PDBPWD) > $(PDBPWDFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBUSERFILE) |base64 > e_$(DBUSERFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBPASSFILE) |base64 > e_$(DBPASSFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) + $(KUBECTL) create secret generic dbuser --from-file=e_$(DBUSERFILE) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic dbpass --from-file=e_$(DBPASSFILE) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(PDBNAMESPACE) + $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl \ + $(DBUSERFILE) $(DBPASSFILE) $(WBUSERFILE) $(WBPASSFILE) $(PDBUSRFILE) $(PDBPWDFILE)\ + e_$(DBUSERFILE) e_$(DBPASSFILE) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) + $(KUBECTL) get secrets -n $(LRSNAMESPACE) + $(KUBECTL) get secrets -n $(PDBNAMESPACE) + +delsecrets: + $(call msg,"CLEAN OLD SECRETS") + $(eval SECRETSP:=$(shell kubectl get secrets -n $(PDBNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) + $(eval SECRETSL:=$(shell kubectl get secrets -n $(LRSNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) + @[ "${SECRETSP}" ] && ( \ + printf "Deleteing secrets in namespace -n $(PDBNAMESPACE)\n") &&\ + ($(KUBECTL) delete secret $(SECRETSP) -n $(PDBNAMESPACE))\ + || ( echo "No screts in namespace $(PDBNAMESPACE)") + @[ "${SECRETSL}" ] && ( \ + printf "Deleteing secrets in namespace -n $(LRSNAMESPACE)\n") &&\ + ($(KUBECTL) delete secret $(SECRETSL) -n $(LRSNAMESPACE))\ + || ( echo "No screts in namespace $(PDBNAMESPACE)") + +cleanCert: + $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl \ + $(DBUSERFILE) $(DBPASSFILE) $(WBUSERFILE) $(WBPASSFILE) $(PDBUSRFILE) $(PDBPWDFILE)\ + e_$(DBUSERFILE) e_$(DBPASSFILE) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) + +### YAML FILE SECTION ### +define _opr +cp ${ORACLE_OPERATOR_YAML} . +export OPBASENAME=`basename ${ORACLE_OPERATOR_YAML}` +#export PDBNAMESPACE=$(cat ${PARAMETERS}|grep -v ^\#|grep PDBNAMESPACE|cut -d : -f 2) + +cp ${OPBASENAME} ${OPBASENAME}.ORIGNINAL +printf "\n\t\xF0\x9F\x91\x89 ${OPBASENAME}\n\n" +printf "\n\t\xF0\x9F\x91\x89 ${PDBNAMESPACE}\n\n" +sed -i 's/value: ""/value: ${OPRNAMESPACE},$PDBNAMESPACE,${LRSNAMESPACE}/g' ${OPBASENAME} +endef + +export opr = $(value _opr) + +operator: +# @ eval "$$opr" + $(CP) ${ORACLE_OPERATOR_YAML} . + ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG + $(SED) -i 's/value: ""/value: $(OPRNAMESPACE),$(PDBNAMESPACE),$(LRSNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` + + +define _script00 +cat < authsection.yaml + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" +EOF + + +cat < ${PDBNAMESPACE}_binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 + namespace: ${PDBNAMESPACE} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +EOF + +cat < ${LRSNAMESPACE}_binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 + namespace: ${LRSNAMESPACE} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +EOF + +endef +export script00 = $(value _script00) +secyaml: + @ eval "$$script00" + + +#echo lrest pod creation +define _script01 +cat < ${LREST_POD} +apiVersion: database.oracle.com/${APIVERSION} +kind: LREST +metadata: + name: cdb-dev + namespace: cdbnamespace +spec: + cdbName: "DB12" + lrestImage: ${LRESTIMG} + lrestImagePullPolicy: "Always" + dbTnsurl : ${TNSALIAS} + replicas: 1 + deletePdbCascade: true + cdbAdminUser: + secret: + secretName: "dbuser" + key: "e_dbuser.txt" + cdbAdminPwd: + secret: + secretName: "dbpass" + key: "e_dbpass.txt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + cdbPubKey: + secret: + secretName: "pubkey" + key: "publicKey" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" +EOF + +endef +export script01 = $(value _script01) + + +define _script02 + +cat <${LRPDBCRE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + pdbconfigmap: "config-map-pdb" + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" +EOF + +cat < ${LRPDBCRE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + pdbconfigmap: "config-map-pdb" + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" +EOF + +cat <${LRPDBOPEN1} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" +EOF + +cat <${LRPDBOPEN2} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" +EOF + +cat <${LRPDBOPEN3} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb3 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" +EOF + +cat <${LRPDBCLOSE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" +EOF + +cat <${LRPDBCLOSE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" +EOF + +cat <${LRPDBCLOSE3} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb3 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: ""new_clone" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" +EOF + +cat < ${LRPDBCLONE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb3 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone" + srcPdbName: "pdbdev" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + pdbconfigmap: "config-map-pdb" + assertiveLrpdbDeletion: true + action: "Clone" +EOF + +cat < ${LRPDBCLONE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb4 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone2" + srcPdbName: "pdbprd" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + pdbconfigmap: "config-map-pdb" + assertiveLrpdbDeletion: true + action: "Clone" +EOF + +cat < ${LRPDBDELETE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" +EOF + +cat < ${LRPDBDELETE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + pdbName: "pdbprd" + action: "Delete" + dropAction: "INCLUDING" +EOF + +cat < ${LRPDBUNPLUG1} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "Unplug" +EOF + +cat <${LRPDBPLUG1} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "plug" + fileNameConversions: "NONE" + sourceFileNameConversions: "NONE" + copyAction: "MOVE" + totalSize: "1G" + tempSize: "100M" + assertiveLrpdbDeletion: true + pdbconfigmap: "config-map-pdb" + action: "Plug" +EOF + +cat <${LRPDBMAP1} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" +EOF + +cat <${LRPDBMAP2} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" +EOF + + +cat <${LRPDBMAP3} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb3 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" +EOF + +cat <${CONFIG_MAP} +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-map-pdb + namespace: ${PDBNAMESPACE} +data: + rdbmsparameters.txt: | + session_cached_cursors;100;spfile + open_cursors;100;spfile + db_file_multiblock_read_count;16;spfile + test_invalid_parameter;16;spfile +EOF + + +cat < ${ALTERSYSTEMYAML} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + action: "Alter" + alterSystemParameter : "cpu_count" + alterSystemValue : "3" + parameterScope : "memory" + + +EOF + +## Auth information +for _file in ${LRPDBCRE1} ${LRPDBCRE2} ${LRPDBOPEN1} ${LRPDBOPEN2} ${LRPDBOPEN3} ${LRPDBCLOSE1} ${LRPDBCLOSE2} ${LRPDBCLOSE3} ${LRPDBCLONE1} ${LRPDBCLONE2} ${LRPDBDELETE1} ${LRPDBDELETE2} ${LRPDBUNPLUG1} ${LRPDBPLUG1} ${LRPDBMAP1} ${LRPDBMAP2} ${LRPDBMAP3} ${ALTERSYSTEMYAML} +do +ls -ltr ${_file} + cat authsection.yaml >> ${_file} +done +rm authsection.yaml +endef + +export script02 = $(value _script02) + +genyaml: secyaml + @ eval "$$script01" + @ eval "$$script02" + +cleanyaml: + - $(RM) $(LRPDBMAP3) $(LRPDBMAP2) $(LRPDBMAP1) $(LRPDBPLUG1) $(LRPDBUNPLUG1) $(LRPDBDELETE2) $(LRPDBDELETE1) $(LRPDBCLONE2) $(LRPDBCLONE1) $(LRPDBCLOSE3) $(LRPDBCLOSE2) $(LRPDBCLOSE1) $(LRPDBOPEN3) $(LRPDBOPEN2) $(LRPDBOPEN1) $(LRPDBCRE2) $(LRPDBCRE1) $(LREST_POD) ${ALTERSYSTEMYAML} + - $(RM) ${CONFIG_MAP} ${PDBNAMESPACE}_binding.yaml ${LRSNAMESPACE}_binding.yaml + + + + +################# +### PACKAGING ### +################# + +pkg: + - $(RM) -rf /tmp/pkgtestplan + $(MKDIR) /tmp/pkgtestplan + $(CP) -R * /tmp/pkgtestplan + $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ + $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan + +################ +### diag ### +################ + +login: + $(KUBECTL) exec `$(KUBECTL) get pods -n $(LRSNAMESPACE)|grep rest|cut -d ' ' -f 1` -n $(LRSNAMESPACE) -it -- /bin/bash + + +reloadop: + echo "RESTARTING OPERATOR" + $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) + $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) + $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) + $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + + +dump: + @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) + @$(eval DIAGFILE := ./opdmp.$(TMPSP)) + @>$(DIAGFILE) + @echo "OPERATOR DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + +####################################################### +#### TEST SECTION #### +####################################################### + +run00: + @$(call msg,"lrest pod creation") + - $(KUBECTL) delete lrest cdb-dev -n $(LRSNAMESPACE) + $(KUBECTL) apply -f $(LREST_POD) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"lrest pod completed") + $(KUBECTL) get lrest -n $(LRSNAMESPACE) + $(KUBECTL) get pod -n $(LRSNAMESPACE) + +run01.1: + @$(call msg,"lrpdb pdb1 creation") + $(KUBECTL) apply -f $(LRPDBCRE1) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "lrpdb pdb1 creation completed") + $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) + +run01.2: + @$(call msg, "lrpdb pdb2 creation") + $(KUBECTL) apply -f $(LRPDBCRE2) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "lrpdb pdb2 creation completed") + $(KUBECTL) get lrpdb pdb2 -n $(PDBNAMESPACE) + +run02.1: + @$(call msg, "lrpdb pdb1 open") + $(KUBECTL) apply -f $(LRPDBOPEN1) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "lrpdb pdb1 open completed") + $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) + +run02.2: + @$(call msg,"lrpdb pdb2 open") + $(KUBECTL) apply -f $(LRPDBOPEN2) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"lrpdb pdb2 open completed") + $(KUBECTL) get lrpdb pdb2 -n $(PDBNAMESPACE) + + +run03.1: + @$(call msg,"clone pdb1-->pdb3") + $(KUBECTL) apply -f $(LRPDBCLONE1) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb3 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"clone pdb1-->pdb3 completed") + $(KUBECTL) get lrpdb pdb3 -n $(PDBNAMESPACE) + + +run03.2: + @$(call msg,"clone pdb2-->pdb4") + $(KUBECTL) apply -f $(LRPDBCLONE2) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"clone pdb2-->pdb4 completed") + $(KUBECTL) get lrpdb pdb3 -n $(PDBNAMESPACE) + + +run04.1: + @$(call msg,"lrpdb pdb1 close") + $(KUBECTL) apply -f $(LRPDBCLOSE1) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "lrpdb pdb1 close completed") + $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) + +run04.2: + @$(call msg,"lrpdb pdb2 close") + $(KUBECTL) apply -f $(LRPDBCLOSE2) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"lrpdb pdb2 close completed") + $(KUBECTL) get lrpdb pdb2 -n $(PDBNAMESPACE) + +run05.1: + @$(call msg,"lrpdb pdb1 unplug") + $(KUBECTL) apply -f $(LRPDBUNPLUG1) + $(KUBECTL) wait --for=delete lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"lrpdb pdb1 unplug completed") + +run06.1: + @$(call msg, "lrpdb pdb1 plug") + $(KUBECTL) apply -f $(LRPDBPLUG1) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "lrpdb pdb1 plug completed") + $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) + +run07.1: + @$(call msg,"lrpdb pdb1 delete ") + - $(KUBECTL) apply -f $(LRPDBCLOSE1) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) apply -f $(LRPDBDELETE1) + $(KUBECTL) wait --for=delete lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"lrpdb pdb1 delete") + $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) + +run99.1: + $(KUBECTL) delete lrest cdb-dev -n cdbnamespace + $(KUBECTL) wait --for=delete lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) get lrest -n cdbnamespaace + $(KUBECTL) get lrpdb -n pdbnamespaace + +runall01: run00 run01.1 run01.2 run02.1 run02.2 run03.1 run03.2 run04.1 run04.2 run05.1 run06.1 run07.1 + + diff --git a/docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml new file mode 100644 index 00000000..2cd57b87 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml @@ -0,0 +1,49 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml new file mode 100644 index 00000000..bab614cf --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml @@ -0,0 +1,49 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml b/docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml new file mode 100644 index 00000000..7bbae48d --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml @@ -0,0 +1,49 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + assertiveLrpdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml new file mode 100644 index 00000000..a845a0bd --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml new file mode 100644 index 00000000..9356184f --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml b/docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml new file mode 100644 index 00000000..1b8024ba --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml new file mode 100644 index 00000000..b441338d --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml @@ -0,0 +1,5057 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomouscontainerdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousContainerDatabase + listKind: AutonomousContainerDatabaseList + plural: autonomouscontainerdatabases + shortNames: + - acd + - acds + singular: autonomouscontainerdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + properties: + lifecycleState: + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabasebackups.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseBackup + listKind: AutonomousDatabaseBackupList + plural: autonomousdatabasebackups + shortNames: + - adbbu + - adbbus + singular: autonomousdatabasebackup + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + type: string + timeEnded: + type: string + timeStarted: + type: string + type: + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabaserestores.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseRestore + listKind: AutonomousDatabaseRestoreList + plural: autonomousdatabaserestores + shortNames: + - adbr + - adbrs + singular: autonomousdatabaserestore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + type: string + type: object + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + properties: + dbName: + type: string + displayName: + type: string + status: + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabase + listKind: AutonomousDatabaseList + plural: autonomousdatabases + shortNames: + - adb + - adbs + singular: autonomousdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + details: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sACD: + properties: + name: + type: string + type: object + ociACD: + properties: + ocid: + type: string + type: object + type: object + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + lifecycleState: + type: string + networkAccess: + properties: + accessControlList: + items: + type: string + type: array + accessType: + enum: + - "" + - PUBLIC + - RESTRICTED + - PRIVATE + type: string + isAccessControlEnabled: + type: boolean + isMTLSConnectionRequired: + type: boolean + privateEndpoint: + properties: + hostnamePrefix: + type: string + nsgOCIDs: + items: + type: string + type: array + subnetOCID: + type: string + type: object + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + type: object + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + required: + - details + type: object + status: + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: cdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: CDB + listKind: CDBList + plural: cdbs + singular: cdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + ordsImage: + type: string + ordsImagePullPolicy: + enum: + - Always + - Never + type: string + ordsImagePullSecret: + type: string + ordsPort: + type: integer + ordsPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + configuration: + properties: + configmap: + properties: + configmapName: + type: string + key: + type: string + type: object + type: object + image: + type: string + service: + properties: + port: + format: int32 + type: integer + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + labels: + additionalProperties: + type: string + type: object + port: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + required: + - conditions + - exporterConfig + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dataguardbrokers.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + singular: dataguardbroker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailOver: + properties: + enable: + type: boolean + strategy: + items: + properties: + sourceDatabaseRef: + type: string + targetDatabaseRefs: + type: string + type: object + type: array + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: + type: string + externalConnectString: + type: string + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dbcssystems.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: dbcssystems + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - sshPublicKeys + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + ociConfigMap: + type: string + ociSecret: + type: string + required: + - ociConfigMap + type: object + status: + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbEdition: + type: string + dbInfo: + items: + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: lrests.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LREST + listKind: LRESTList + plural: lrests + singular: lrest + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the LREST + jsonPath: .spec.cdbName + name: CDB NAME + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the LREST Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message if any + jsonPath: .status.msg + name: Message + type: string + - description: string of the tnsalias + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + lrestImage: + type: string + lrestImagePullPolicy: + enum: + - Always + - Never + type: string + lrestImagePullSecret: + type: string + lrestPort: + type: integer + lrestPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + nodeSelector: + additionalProperties: + type: string + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: lrpdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LRPDB + listKind: LRPDBList + plural: lrpdbs + singular: lrpdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the LRPDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: last sqlcode + jsonPath: .status.sqlCode + name: last sqlcode + type: integer + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + - Alter + - Noaction + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbPass: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + alterSystem: + type: string + alterSystemParameter: + type: string + alterSystemValue: + type: string + asClone: + type: boolean + assertiveLrpdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + lrpdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + parameterScope: + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + - ALTER + type: string + pdbconfigmap: + type: string + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + - alterSystemParameter + - alterSystemValue + - webServerPwd + type: object + status: + properties: + action: + type: string + alterSystem: + type: string + bitstat: + type: integer + bitstatstr: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + sqlCode: + type: integer + status: + type: boolean + totalSize: + type: string + required: + - phase + - sqlCode + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: oraclerestdataservices.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestDataService + listKind: OracleRestDataServiceList + plural: oraclerestdataservices + singular: oraclerestdataservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + apexPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: pdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB + listKind: PDBList + plural: pdbs + singular: pdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + type: boolean + assertivePdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + type: object + status: + properties: + action: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + status: + type: boolean + totalSize: + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: shardingdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: ShardingDatabase + listKind: ShardingDatabaseList + plural: shardingdatabases + singular: shardingdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + createAs: + enum: + - primary + - standby + - clone + type: string + dgBrokerConfigured: + type: boolean + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + required: + - image + type: object + status: + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBrokerConfigured: + type: boolean + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: oracle-database-operator-leader-election-role + namespace: oracle-database-operator-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - deployments + - events + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - events + - pods + - pods/exec + - pods/log + - replicasets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list +- apiGroups: + - '''''' + resources: + - statefulsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - configmaps + - deployments + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases/status + verbs: + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - cdbs/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - events + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - lrests/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - lrpdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - pdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - prometheusrules + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/finalizers + verbs: + - update +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get + - patch + - update +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-oracle-database-operator-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: oracle-database-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-oracle-database-operator-proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager-metrics-service + namespace: oracle-database-operator-system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: v1 +kind: Service +metadata: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: oracle-database-operator-serving-cert + namespace: oracle-database-operator-system +spec: + dnsNames: + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local + issuerRef: + kind: Issuer + name: oracle-database-operator-selfsigned-issuer + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: oracle-database-operator-selfsigned-issuer + namespace: oracle-database-operator-system +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: mautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: mdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: moraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: msingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-cdb + failurePolicy: Fail + name: mcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: mlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: mlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-pdb + failurePolicy: Fail + name: mpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + failurePolicy: Fail + name: vautonomouscontainerdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomouscontainerdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: vautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore + failurePolicy: Fail + name: vautonomousdatabaserestore.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabaserestores + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: vdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: voraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: vsingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-cdb + failurePolicy: Fail + name: vcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: vlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: vlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-pdb + failurePolicy: Fail + name: vpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager + namespace: oracle-database-operator-system +spec: + replicas: 3 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --enable-leader-election + command: + - /manager + env: + - name: WATCH_NAMESPACE + value: oracle-database-operator-system,pdbnamespace,cdbnamespace + image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/mn_wallet_lrestbranch:latest + imagePullPolicy: Always + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + resources: + limits: + cpu: 400m + memory: 400Mi + requests: + cpu: 400m + memory: 400Mi + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert +--- diff --git a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.ORIGNINAL b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.ORIGNINAL new file mode 100644 index 00000000..d1cc3eca --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.ORIGNINAL @@ -0,0 +1,5057 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomouscontainerdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousContainerDatabase + listKind: AutonomousContainerDatabaseList + plural: autonomouscontainerdatabases + shortNames: + - acd + - acds + singular: autonomouscontainerdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + properties: + lifecycleState: + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabasebackups.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseBackup + listKind: AutonomousDatabaseBackupList + plural: autonomousdatabasebackups + shortNames: + - adbbu + - adbbus + singular: autonomousdatabasebackup + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + type: string + timeEnded: + type: string + timeStarted: + type: string + type: + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabaserestores.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseRestore + listKind: AutonomousDatabaseRestoreList + plural: autonomousdatabaserestores + shortNames: + - adbr + - adbrs + singular: autonomousdatabaserestore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + type: string + type: object + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + properties: + dbName: + type: string + displayName: + type: string + status: + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabase + listKind: AutonomousDatabaseList + plural: autonomousdatabases + shortNames: + - adb + - adbs + singular: autonomousdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + details: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sACD: + properties: + name: + type: string + type: object + ociACD: + properties: + ocid: + type: string + type: object + type: object + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + lifecycleState: + type: string + networkAccess: + properties: + accessControlList: + items: + type: string + type: array + accessType: + enum: + - "" + - PUBLIC + - RESTRICTED + - PRIVATE + type: string + isAccessControlEnabled: + type: boolean + isMTLSConnectionRequired: + type: boolean + privateEndpoint: + properties: + hostnamePrefix: + type: string + nsgOCIDs: + items: + type: string + type: array + subnetOCID: + type: string + type: object + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + type: object + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + required: + - details + type: object + status: + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: cdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: CDB + listKind: CDBList + plural: cdbs + singular: cdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + ordsImage: + type: string + ordsImagePullPolicy: + enum: + - Always + - Never + type: string + ordsImagePullSecret: + type: string + ordsPort: + type: integer + ordsPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + configuration: + properties: + configmap: + properties: + configmapName: + type: string + key: + type: string + type: object + type: object + image: + type: string + service: + properties: + port: + format: int32 + type: integer + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + labels: + additionalProperties: + type: string + type: object + port: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + required: + - conditions + - exporterConfig + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dataguardbrokers.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + singular: dataguardbroker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailOver: + properties: + enable: + type: boolean + strategy: + items: + properties: + sourceDatabaseRef: + type: string + targetDatabaseRefs: + type: string + type: object + type: array + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: + type: string + externalConnectString: + type: string + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dbcssystems.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: dbcssystems + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - sshPublicKeys + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + ociConfigMap: + type: string + ociSecret: + type: string + required: + - ociConfigMap + type: object + status: + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbEdition: + type: string + dbInfo: + items: + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: lrests.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LREST + listKind: LRESTList + plural: lrests + singular: lrest + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the LREST + jsonPath: .spec.cdbName + name: CDB NAME + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the LREST Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message if any + jsonPath: .status.msg + name: Message + type: string + - description: string of the tnsalias + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + lrestImage: + type: string + lrestImagePullPolicy: + enum: + - Always + - Never + type: string + lrestImagePullSecret: + type: string + lrestPort: + type: integer + lrestPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + nodeSelector: + additionalProperties: + type: string + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: lrpdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LRPDB + listKind: LRPDBList + plural: lrpdbs + singular: lrpdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the LRPDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: last sqlcode + jsonPath: .status.sqlCode + name: last sqlcode + type: integer + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + - Alter + - Noaction + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbPass: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + alterSystem: + type: string + alterSystemParameter: + type: string + alterSystemValue: + type: string + asClone: + type: boolean + assertiveLrpdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + lrpdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + parameterScope: + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + - ALTER + type: string + pdbconfigmap: + type: string + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + - alterSystemParameter + - alterSystemValue + - webServerPwd + type: object + status: + properties: + action: + type: string + alterSystem: + type: string + bitstat: + type: integer + bitstatstr: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + sqlCode: + type: integer + status: + type: boolean + totalSize: + type: string + required: + - phase + - sqlCode + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: oraclerestdataservices.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestDataService + listKind: OracleRestDataServiceList + plural: oraclerestdataservices + singular: oraclerestdataservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + apexPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: pdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB + listKind: PDBList + plural: pdbs + singular: pdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + type: boolean + assertivePdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + type: object + status: + properties: + action: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + status: + type: boolean + totalSize: + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: shardingdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: ShardingDatabase + listKind: ShardingDatabaseList + plural: shardingdatabases + singular: shardingdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + createAs: + enum: + - primary + - standby + - clone + type: string + dgBrokerConfigured: + type: boolean + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + required: + - image + type: object + status: + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBrokerConfigured: + type: boolean + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: oracle-database-operator-leader-election-role + namespace: oracle-database-operator-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - deployments + - events + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - events + - pods + - pods/exec + - pods/log + - replicasets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list +- apiGroups: + - '''''' + resources: + - statefulsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - configmaps + - deployments + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases/status + verbs: + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - cdbs/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - events + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - lrests/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - lrpdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - pdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - prometheusrules + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/finalizers + verbs: + - update +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get + - patch + - update +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-oracle-database-operator-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: oracle-database-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-oracle-database-operator-proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager-metrics-service + namespace: oracle-database-operator-system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: v1 +kind: Service +metadata: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: oracle-database-operator-serving-cert + namespace: oracle-database-operator-system +spec: + dnsNames: + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local + issuerRef: + kind: Issuer + name: oracle-database-operator-selfsigned-issuer + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: oracle-database-operator-selfsigned-issuer + namespace: oracle-database-operator-system +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: mautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: mdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: moraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: msingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-cdb + failurePolicy: Fail + name: mcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: mlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: mlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-pdb + failurePolicy: Fail + name: mpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + failurePolicy: Fail + name: vautonomouscontainerdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomouscontainerdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: vautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore + failurePolicy: Fail + name: vautonomousdatabaserestore.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabaserestores + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: vdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: voraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: vsingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-cdb + failurePolicy: Fail + name: vcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: vlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: vlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-pdb + failurePolicy: Fail + name: vpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager + namespace: oracle-database-operator-system +spec: + replicas: 3 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --enable-leader-election + command: + - /manager + env: + - name: WATCH_NAMESPACE + value: "" + image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/mn_wallet_lrestbranch:latest + imagePullPolicy: Always + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + resources: + limits: + cpu: 400m + memory: 400Mi + requests: + cpu: 400m + memory: 400Mi + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert +--- diff --git a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.pippo b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.pippo new file mode 100644 index 00000000..d1cc3eca --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.pippo @@ -0,0 +1,5057 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomouscontainerdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousContainerDatabase + listKind: AutonomousContainerDatabaseList + plural: autonomouscontainerdatabases + shortNames: + - acd + - acds + singular: autonomouscontainerdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + properties: + lifecycleState: + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabasebackups.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseBackup + listKind: AutonomousDatabaseBackupList + plural: autonomousdatabasebackups + shortNames: + - adbbu + - adbbus + singular: autonomousdatabasebackup + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + type: string + timeEnded: + type: string + timeStarted: + type: string + type: + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabaserestores.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseRestore + listKind: AutonomousDatabaseRestoreList + plural: autonomousdatabaserestores + shortNames: + - adbr + - adbrs + singular: autonomousdatabaserestore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + type: string + type: object + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + properties: + dbName: + type: string + displayName: + type: string + status: + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabase + listKind: AutonomousDatabaseList + plural: autonomousdatabases + shortNames: + - adb + - adbs + singular: autonomousdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + details: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sACD: + properties: + name: + type: string + type: object + ociACD: + properties: + ocid: + type: string + type: object + type: object + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + lifecycleState: + type: string + networkAccess: + properties: + accessControlList: + items: + type: string + type: array + accessType: + enum: + - "" + - PUBLIC + - RESTRICTED + - PRIVATE + type: string + isAccessControlEnabled: + type: boolean + isMTLSConnectionRequired: + type: boolean + privateEndpoint: + properties: + hostnamePrefix: + type: string + nsgOCIDs: + items: + type: string + type: array + subnetOCID: + type: string + type: object + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + type: object + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + required: + - details + type: object + status: + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: cdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: CDB + listKind: CDBList + plural: cdbs + singular: cdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + ordsImage: + type: string + ordsImagePullPolicy: + enum: + - Always + - Never + type: string + ordsImagePullSecret: + type: string + ordsPort: + type: integer + ordsPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + configuration: + properties: + configmap: + properties: + configmapName: + type: string + key: + type: string + type: object + type: object + image: + type: string + service: + properties: + port: + format: int32 + type: integer + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + labels: + additionalProperties: + type: string + type: object + port: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + required: + - conditions + - exporterConfig + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dataguardbrokers.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + singular: dataguardbroker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailOver: + properties: + enable: + type: boolean + strategy: + items: + properties: + sourceDatabaseRef: + type: string + targetDatabaseRefs: + type: string + type: object + type: array + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: + type: string + externalConnectString: + type: string + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dbcssystems.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: dbcssystems + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - sshPublicKeys + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + ociConfigMap: + type: string + ociSecret: + type: string + required: + - ociConfigMap + type: object + status: + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbEdition: + type: string + dbInfo: + items: + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: lrests.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LREST + listKind: LRESTList + plural: lrests + singular: lrest + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the LREST + jsonPath: .spec.cdbName + name: CDB NAME + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the LREST Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message if any + jsonPath: .status.msg + name: Message + type: string + - description: string of the tnsalias + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + lrestImage: + type: string + lrestImagePullPolicy: + enum: + - Always + - Never + type: string + lrestImagePullSecret: + type: string + lrestPort: + type: integer + lrestPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + nodeSelector: + additionalProperties: + type: string + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: lrpdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LRPDB + listKind: LRPDBList + plural: lrpdbs + singular: lrpdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the LRPDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: last sqlcode + jsonPath: .status.sqlCode + name: last sqlcode + type: integer + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + - Alter + - Noaction + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbPass: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + alterSystem: + type: string + alterSystemParameter: + type: string + alterSystemValue: + type: string + asClone: + type: boolean + assertiveLrpdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + lrpdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + parameterScope: + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + - ALTER + type: string + pdbconfigmap: + type: string + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + - alterSystemParameter + - alterSystemValue + - webServerPwd + type: object + status: + properties: + action: + type: string + alterSystem: + type: string + bitstat: + type: integer + bitstatstr: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + sqlCode: + type: integer + status: + type: boolean + totalSize: + type: string + required: + - phase + - sqlCode + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: oraclerestdataservices.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestDataService + listKind: OracleRestDataServiceList + plural: oraclerestdataservices + singular: oraclerestdataservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + apexPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: pdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB + listKind: PDBList + plural: pdbs + singular: pdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + type: boolean + assertivePdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + type: object + status: + properties: + action: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + status: + type: boolean + totalSize: + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: shardingdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: ShardingDatabase + listKind: ShardingDatabaseList + plural: shardingdatabases + singular: shardingdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + createAs: + enum: + - primary + - standby + - clone + type: string + dgBrokerConfigured: + type: boolean + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + required: + - image + type: object + status: + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBrokerConfigured: + type: boolean + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: oracle-database-operator-leader-election-role + namespace: oracle-database-operator-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - deployments + - events + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - events + - pods + - pods/exec + - pods/log + - replicasets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list +- apiGroups: + - '''''' + resources: + - statefulsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - configmaps + - deployments + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases/status + verbs: + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - cdbs/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - events + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - lrests/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - lrpdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - pdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - prometheusrules + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/finalizers + verbs: + - update +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get + - patch + - update +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-oracle-database-operator-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: oracle-database-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-oracle-database-operator-proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager-metrics-service + namespace: oracle-database-operator-system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: v1 +kind: Service +metadata: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: oracle-database-operator-serving-cert + namespace: oracle-database-operator-system +spec: + dnsNames: + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local + issuerRef: + kind: Issuer + name: oracle-database-operator-selfsigned-issuer + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: oracle-database-operator-selfsigned-issuer + namespace: oracle-database-operator-system +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: mautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: mdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: moraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: msingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-cdb + failurePolicy: Fail + name: mcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: mlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: mlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-pdb + failurePolicy: Fail + name: mpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + failurePolicy: Fail + name: vautonomouscontainerdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomouscontainerdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: vautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore + failurePolicy: Fail + name: vautonomousdatabaserestore.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabaserestores + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: vdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: voraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: vsingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-cdb + failurePolicy: Fail + name: vcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: vlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: vlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-pdb + failurePolicy: Fail + name: vpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager + namespace: oracle-database-operator-system +spec: + replicas: 3 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --enable-leader-election + command: + - /manager + env: + - name: WATCH_NAMESPACE + value: "" + image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/mn_wallet_lrestbranch:latest + imagePullPolicy: Always + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + resources: + limits: + cpu: 400m + memory: 400Mi + requests: + cpu: 400m + memory: 400Mi + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert +--- diff --git a/docs/multitenant/lrest-based/usecase/parameters.txt b/docs/multitenant/lrest-based/usecase/parameters.txt new file mode 100644 index 00000000..5b8ac4b2 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/parameters.txt @@ -0,0 +1,53 @@ + +######################## +## REST SERVER IMAGE ### +######################## + +#LRESTIMG:container-registry-admin.oraclecorp.com/database/operator:lrest-240910-amd64 +LRESTIMG:lin.ocir.io/intsanjaysingh/mmalvezz/testppr/lrest-dboper:latest + +############################## +## TNS URL FOR CDB CREATION ## +############################## +TNSALIAS:"(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + +########################################### +## CDB USER FOR PDB LIFECYCLE MANAGMENT ### +########################################### + +DBUSER:restdba +DBPASS:CLWKO655321 + +####################### +## HTTPS CREDENTIAL ### +####################### + +WBUSER:welcome +WBPASS:welcome1 + +##################### +## PDB ADMIN USER ### +##################### + +PDBUSR:Citizenkane +PDBPWD:Rosebud + +################### +### NAMESPACES #### +################### + +PDBNAMESPACE:pdbnamespace +LRSNAMESPACE:cdbnamespace + + +#################### +### COMPANY NAME ### +#################### + +COMPANY:oracle + +#################### +### APIVERSION ### +#################### + +APIVERSION:v4 diff --git a/docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml b/docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml new file mode 100644 index 00000000..5af79ed6 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 + namespace: pdbnamespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml new file mode 100644 index 00000000..d7d310db --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml @@ -0,0 +1,54 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "plug" + fileNameConversions: "NONE" + sourceFileNameConversions: "NONE" + copyAction: "MOVE" + totalSize: "1G" + tempSize: "100M" + assertiveLrpdbDeletion: true + pdbconfigmap: "config-map-pdb" + action: "Plug" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml new file mode 100644 index 00000000..a5da5a57 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml @@ -0,0 +1,46 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "Unplug" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/altersystem_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/altersystem_lrpdb1_resource.yaml new file mode 100644 index 00000000..a02514eb --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/altersystem_lrpdb1_resource.yaml @@ -0,0 +1,48 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "lrpdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "lrpdb-secret" + key: "sysadmin_pwd" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + action: "Alter" + alterSystemParameter : "cpu_count" + alterSystemValue : "3" + parameterScope : "memory" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.crt b/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.crt new file mode 100644 index 00000000..da6ca98b --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjzCCAnegAwIBAgIUX4OIYiqRwA+e0JPXV8iaW7h3f2swDQYJKoZIhvcNAQEL +BQAwVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEVMBMG +A1UECgwMb3JhY2xlLCBJbmMuMRcwFQYDVQQDDA5vcmFjbGUgUm9vdCBDQTAeFw0y +NDEwMjQyMDQyNTRaFw0yNTEwMjQyMDQyNTRaMFcxCzAJBgNVBAYTAkNOMQswCQYD +VQQIDAJHRDELMAkGA1UEBwwCU1oxFTATBgNVBAoMDG9yYWNsZSwgSW5jLjEXMBUG +A1UEAwwOb3JhY2xlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDoLJRupBvETN4JwO+zs9gUChtn0GBsr5DhfqY9Hs2FC1Z5wVzw5KTFKxPo +F7aXH/Zz4B+JiRLvaYv1AsfnFl3YsaVQ+8KMfaVNiRiSy2uSfRGXr9MYD/RR2eza +0hc9necBoUqfqVynWby+lVIVYXY5wcuyYpSDPvYuCGs+MiuUTDuQb4UEiSEc/ccr +mj7TaWCyqJGQ+ilm3Fjgfv2qFvekavtaUKrvDYIefz0FdlRWzLd4qEyWScxiGs4r ++TIRUsy5YVUqf+KzxO3p2m0gYrmv+EQvRyFuHtiGeinUCvx9ISTdTwCPt4YRviSC +HdhKO4qaUjG6m0Vcq97Po+5T0qeRAgMBAAGjUzBRMB0GA1UdDgQWBBQfkTdjH0v0 +Rc+tlhLvR0PIAGVRtjAfBgNVHSMEGDAWgBQfkTdjH0v0Rc+tlhLvR0PIAGVRtjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCkanIZUAAELgA6WC/O +f6Rmn6GDaI1/0qI1/lSlt0DgG2JCd5rz6yyOnMbDBiFDRKi00MvQpv6ksI8VCVFP +pr/L1A7ueLD990pZ8h/gMr2mMkQ5jNvwnrgph+dxwiXohSi+38P3JLrZmsnwmbBo +fKHN/iz7xNTPsaKtVHqRX1oSp22aORIFTpOvuHVUmQA8OGuBONnbL+v9sWzqeguq +Tz/EK6GJhUc1C4QHdGXhUivU/+CF2A2pDoaiB9BNbyzfKUxgh8ihMlRqZqMPEajb +e25mVoe+gAnAFC78qlSkdSS0qG6U+iFh4ClKE3CYiFhlQLfPuJOYBI5Frd2I8BTp +OfRV +-----END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.key b/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.key new file mode 100644 index 00000000..62b39c1d --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDoLJRupBvETN4J +wO+zs9gUChtn0GBsr5DhfqY9Hs2FC1Z5wVzw5KTFKxPoF7aXH/Zz4B+JiRLvaYv1 +AsfnFl3YsaVQ+8KMfaVNiRiSy2uSfRGXr9MYD/RR2eza0hc9necBoUqfqVynWby+ +lVIVYXY5wcuyYpSDPvYuCGs+MiuUTDuQb4UEiSEc/ccrmj7TaWCyqJGQ+ilm3Fjg +fv2qFvekavtaUKrvDYIefz0FdlRWzLd4qEyWScxiGs4r+TIRUsy5YVUqf+KzxO3p +2m0gYrmv+EQvRyFuHtiGeinUCvx9ISTdTwCPt4YRviSCHdhKO4qaUjG6m0Vcq97P +o+5T0qeRAgMBAAECggEADOflfFz8+iV3tj0WVfvqR/rj2Rp5MbF0sUQ0A3PB4uJl +dTEtdGqRSjCB08X1rWLmdTyBJEkNV2ebkwwLhqn0n4YIdX75KowVc0Dqr8ZE/Yic +QL1SU6UXZLFjfCpvhkpPvAR2PHfSoa7YUhPE01Fum/lM1GascLtAPgDTzUpGp+CV +1U3e8mioRb8WuUfF9NygCJ/aYomAE82JJg37dWe3gsRjdauN2b9aXXxH8bHP56Qf +dH+07b/Hqf3+oQL+ND3APcfIlvjE5SitfRFGy+c3JHagajgiDRhwnmx16EuoT4XX +craYyvJF+ddflg+cpfIMNSeWRseqBhMnYsJ8uG6UfQKBgQD7J+0f39CovWodjwY3 +hDEr+F9xC5ShZJPN/RUYXT1NoRRzDzgcU0K6O3etfh9LlSpjp0MYV0qKiLmJ173Q +TVegDdxl3bbvwaslR9yTpZtjejFh7I5taTogTxcijswvIyT0E5CE+LJtvVZ/ydQz +JRBzipBWSkvJx0NlvXcKzEdmmwKBgQDspu50lWTehSWVfOvIBS66BAI44MIJN6WV +8yU0QigX4lE+/uud6BfKbY9doy9J3c+lXBmzwd0TfF03v/wxQvcilXPDL7zNJQHq +hYxMyG2YtbAN1wbTjhmDhan6x31NKcSbj+hCR7+sCGqilMlZbF/+c8S1QV+EvL9v +3xjB3/63QwKBgBTY+5XpvihDkMtjU+DH2E0OEN4RWAUNlDdFCrs7Ahuvg0MJwF6Z +irBqv0Rnc7GScDJVb/xVQpq9Pqlyfw8LXHqsq97IyKaeLNarYGPhG0Tmn7LBMBFk +6q5D4me78rb289pOxXBENItnbAavei3EpdjxZ4nCIQhewflwALGkw0iBAoGBAOFL +qwhikb0mlTlTzESIaou89UCg+Rk9WO8ApBK0SOc0FpUaq8WRB9U3qJ1EleiEuwJ6 +RB3WGlpKs5Q3uihL0GukTKkt8Vh8Ou7CZWdUCMKxdgI3C9CsH64Oulx9PtUPRpWy +0zMGi3uEJ8DQVUBE5L6iCEtnCUCdUMN4aWFpll1xAoGAQHebcuIuadxYny1YtgGx +J3/+KK8+Jtuv3UCo6IHHL+pi56VxN/1c4pt/YQnaB3JThaNrkNNTGy2TxCrv3lCN +B9rjaIG4gUpDbyutwJHK3HWZ1O37GiqdmuZZu7VjGIR4CBDQOTdLA2X5tkOfSbEr +pBveqS1C0qdisUTqJXq29fI= +-----END PRIVATE KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.srl b/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.srl new file mode 100644 index 00000000..39f44174 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.srl @@ -0,0 +1 @@ +1DED5A4E240ECAD1381AB6983C1CAA2D456889BB diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cdbnamespace_binding.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/cdbnamespace_binding.yaml new file mode 100644 index 00000000..c7bc41e5 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cdbnamespace_binding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 + namespace: cdbnamespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.crt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.crt new file mode 100644 index 00000000..f3d2474f --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjTCCAnWgAwIBAgIUEyqDlUDop1mB0XE7Ch6SloDwCfMwDQYJKoZIhvcNAQEL +BQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEUMBIG +A1UECgwLb3JhY2xlLEluYy4xFzAVBgNVBAMMDm9yYWNsZSBSb290IENBMB4XDTI0 +MDkwMzIyMjcwM1oXDTI1MDkwMzIyMjcwM1owVjELMAkGA1UEBhMCQ04xCzAJBgNV +BAgMAkdEMQswCQYDVQQHDAJTWjEUMBIGA1UECgwLb3JhY2xlLEluYy4xFzAVBgNV +BAMMDm9yYWNsZSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAnltrUl+ARDT2aP+23XVNbpSEGN0p8jSGe852TsRQhjfffGtOtomiSMnmk9by +4IpYDjuWTUbnkjaaALxnLYVBJnIyEnqsUfSw2X0JT1QF3tZsh7C3tTNTY9vE2VTd +oFiKbahHuv+t5eGnxV8RHD3g0iNi7BlmTeI7XhbucNDgXU/eDQfIPNaFVGQEBqhk +H7mqTkMUjgq3a0UGqiEXhvUY8ytyskjHg7uDO8MgN371OtubGlyV2uQp6mqigKHl +98oY1Zl/39Panrg2NXEr+UIVtmnxi+LHdoNf0lICAnC9Kycd/5YxdvQi+Ztm+OK5 +vyTI77OMCqNPZWsdB2EcGfr+XwIDAQABo1MwUTAdBgNVHQ4EFgQUGg6a21+j3SMq +zI84cwBZFuZ7LGAwHwYDVR0jBBgwFoAUGg6a21+j3SMqzI84cwBZFuZ7LGAwDwYD +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAZZq2tQo+bFfr/UThuoBK +TBel6nG9La0lriUWu4InB5jTWDw8p+6RGgib0ngWLHjnG9TnyHJts7bcG64f9HWA +87WpuI+1+XXkEF6pMr5j/00nCRv5gD5RtwjJ/4s6SxOqRljsIt4fClmPRU4B1ZgS +sf0vp0vtLHr9kyPdKylprUfj0BVDXh4+mZHQjEeyJp1BxnUEBPpfQ7BJL2iirOhK +z/xhCug/MF2R5/Ewcf4LYUlb0LT5pJWf7gIEq4ADkGVqH4XH2V1WREfeO3hadKmk +iTBR7cL8RP7zPT/3ik7xwnqukIDw+EvHmeJKG8D2yE0KlbmPLAy6RXjPqt+RhC2v +PQ== +-----END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.key b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.key new file mode 100644 index 00000000..bf22e1b0 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAnltrUl+ARDT2aP+23XVNbpSEGN0p8jSGe852TsRQhjfffGtO +tomiSMnmk9by4IpYDjuWTUbnkjaaALxnLYVBJnIyEnqsUfSw2X0JT1QF3tZsh7C3 +tTNTY9vE2VTdoFiKbahHuv+t5eGnxV8RHD3g0iNi7BlmTeI7XhbucNDgXU/eDQfI +PNaFVGQEBqhkH7mqTkMUjgq3a0UGqiEXhvUY8ytyskjHg7uDO8MgN371OtubGlyV +2uQp6mqigKHl98oY1Zl/39Panrg2NXEr+UIVtmnxi+LHdoNf0lICAnC9Kycd/5Yx +dvQi+Ztm+OK5vyTI77OMCqNPZWsdB2EcGfr+XwIDAQABAoIBAA75TAOk/ib07xvs +mikEr4a2SmtjdlTtvI22bx9xlr+qRtqn9vBxwUOqu7/dAmhTDJKD9elmu/zqZtOM +/SCjyg5NnAh9SxAQ2Cwok5nJsyhoFCkcf7KFoOv/WvTF/pLr7GMEF4xKIvJqzQdv +vtlxzHFyYSc8G51fuAdkmbVXOu/3uEyPbo/1dWBovhxL3UMh2oyzKDLL2wi83Fev +5MQ1O8saK37chAfWDZvKWgJmlOHcJpQDu7HR5F+ynwbwRnIyMQyBuzYhQrWBWR74 +2p9HcoDVsnad+3FnaKlzMn0HsFVGy7R/ZvTHhh8V90jq/QXxO9TSLvxRCBujQ+V5 +VoutxsECgYEAzX7gdkUOjscEbT65+CEr7Dp1vflsbFZBwu++iQNKGJzoZswNym7e +eXBdSeDf9aE067dZVqxZv3TtH/Uto5QyNeov54VTaoFkFVKG55H6c+MzDmz2ieJ/ +B209JWDyAEfRJNQlqzqllKCgpf7ns1rD3dev1nOVM0riUVsJfrIuxiECgYEAxUa9 +OvEhGM3QA6HNjpzPBqL+6i8ocSVLGZGkrP9Et4jJMpsWJO5w/lEUYbAy4K6Ngk91 +xWBLnb3y4kdnUDVhzRQx4RC+SUBQ6/Rj8V09ENBwNq220uYl4vSZbd5WfJG+Hgw0 +W5iIb5ZngjCKSqO87P3n38ndt8ZJhwa4MpOINH8CgYApwARUoxUhFvgkReE8s2ic +FwiC72EC5cd4loLsjVmJhJ+G+yA2J65Vl+msL97wpa790QN6o4ucZWIj4wlBucM1 +xv2ymRjog59U4a587+ClmNxvqt2wB+9tD9mjdX3VZsHsLJn2nvSzLKtW7mIew2sV +8FNOS69de3JduxRDxGnSYQKBgQCqxNhrnG31BbwqWLcS26TObuxRli7g+tJuuMef +RX2bjpnz55IcuBcx3sfQ5HC2VAOFKPmeuQPb8aC9xeel4//cQb4HwLGnwTgLeMvH +FNoWowhwHKHlIHrtYqUqGUwumw7C/feYzopUZhMF/LTJdcHGOOiurRTzz364ltV+ +Wn2OSwKBgB9guT+voiLrgR/OQWFINotqnl1t4etex+vbUylDX7jFKeNQPJTSeP2Q +gHidHMw5PUKO5bDLitkR61xQAj+U+X+eYcKWvoJBsYdoYUPNGrCPuDI+frleQTBk +0yjH7AoOas7O/PIZKyfi2F2POtZCgfZK1IEizo2ZDat+RHvHYV7A +-----END RSA PRIVATE KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.srl b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.srl new file mode 100644 index 00000000..a85bf66f --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.srl @@ -0,0 +1 @@ +42A91AD49BA9B8D71512F5E0A9397015B2BDD883 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbpass.txt new file mode 100644 index 00000000..eab39d3f --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbpass.txt @@ -0,0 +1 @@ +CLWKO655321 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbuser.txt new file mode 100644 index 00000000..7028cc36 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbuser.txt @@ -0,0 +1 @@ +restdba diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbpass.txt new file mode 100644 index 00000000..8e1be181 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbpass.txt @@ -0,0 +1,5 @@ +MPGBdow5yQfh3/2Sud1qYMY75vI7Qlt0RkhbM+Dun3E76E9CGMJorFzIu/5FZi1fCGEYHWiMMygx +BR3m4Y7gE/PWETYOfbpI5efuPsOCQoFYPQCGce/Dfwd88RJTnp8RHgW1x2zwhH3aOJADsRQ7BChg +/oeb1naFig4Sznj4FbJ0ExdW+kIWjC57iDfX1osa+NC8OhMexBsKoTO0nAL6+6ji+ZEyoRrnvzT+ +hevdHjeIGTerZabiX/9kI0T3SJWpc2p3xH5985kEpToNE73l5nxAkRsfjp+KSwAgbWnKs/TPSeCn +l2J//Fjl4hNZwmTagR1qFLG0R7mbN+hT7Q6VJA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbuser.txt new file mode 100644 index 00000000..0f9a6c2d --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbuser.txt @@ -0,0 +1,5 @@ +N69kAA0Im0ObwAeXmeou+5J8hC4OaBqM7q38yjBMiIcgN8csNv7ZaD3csA5ZNqGUi5W/XtcOjgWN +4OtY8vvV1B1ATiGvZmaS4F9zKd8GAP9wVHq+BXef+H27xFZEhCC6QxDBThcUMH4dihR/sQFayU1p +gPcXD3X6VhMLSZLb4073kyaEEFyiZr3GRZkqXuFFvHCGfIpWDhlVptngUT4Qmk8xQXzbpqi5vzH5 +L40KNEH9BX+lRcbBeHk61MJLBn/HTeLuE24QOCpOT9+pyStEv0Dn8F8WlVhm/9TDuzBNETF6Or1N +qij6nPQOmswhF0KyFUtMoKM7HXhc3g+8tENKlw== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbpwd.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbpwd.txt new file mode 100644 index 00000000..68b13f5a --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbpwd.txt @@ -0,0 +1,5 @@ +gWoKcUSAAuNO8LkFaYjZhTQfmVGDiiWS7Ux2x55lFxn4qXeoXDA1f2hUeqPrUtoQFIfVSch7E94Y +jgOiRSBcFy41bkJVo8lPklWqvrF1/00CXhnkf+JcO6bLA1KAgjPxbNP9X9B8cCDX8Q36C276DDIT +0IEZ0bq0olAUNTyMNcN5kxIZz+vs8z4cZ49gXg4owtlYA1idqdFVjRC/aHvd0aTGtoFXXNJDAQq+ +JZuZgT2QBb5Pe8hH088qy4GWIOLRSYZbeaWY6ovLOD9NxLv61x8XXvQtyCJEaEjOUREXDtalv3fL +6DMu0dreLZYn61z7qquzmjsQGlAMGO+CTUWgsA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbusr.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbusr.txt new file mode 100644 index 00000000..5bf7219d --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbusr.txt @@ -0,0 +1,5 @@ +Aax80Z0EqZfy7giPro/RkQMBRweQNdFOQVgdCphBrgvnMTW1ugV7aj1dDBPIuuIXwmM0yJbPMMMp +gITIfT5ZqKwA2jY/GH35C5Aq1w+fvsrsZqHZOa6QZFfjBqZCA+CHcS4D1bMyFlIGj3SqgWdXPjWg +QMLhAvgjml1gDpBrRi0Hno02xI35X1VBZT8vMIr0dQii1hITcTeBS0r7zud6Ablrm+S5AMuqfJ22 +WfazFIj1FHZshHKjpRX4/FJaCQQWe4j7p4/THZ5uKBJgd9AG2wEztPdcu3brIAatkjhIRsSgVOB4 +T0PGK5rl/r079gKyHNdQwtmOs6uZLgIuggTVXA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbpass.txt new file mode 100644 index 00000000..8d8361fc --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbpass.txt @@ -0,0 +1,5 @@ +coxJfZQxjw7Fe4fbtrh62zyuD74pgF82nihiztEigHLKz2mY3lP8UuDd8gOOGM4NHEDemPDsAYND +NITal0csGSAWrLHzVDqShxsh87QfQlkY5VF78/zZmAgIJ9BXcH7V4e+QFgqyt+oxa5+r6FY7NmIf +LaGnC3DsdER1jVZxAPu9lL80Oe9Y/JSUfnzWygwhTgKrOMppdCU+jJNfwM+wOvRY1KDPntFvh089 +hsTrrzbARBQFEkDFToMzhCAo1NczNbEQvvPJB+lsL1e9Q+4W/bkweMa6zLLKAH6Gr8qVN5HuObRB +UskCSHgpVG01IoHbkFmZQLQ/2xuzFDr1gIESlg== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbuser.txt new file mode 100644 index 00000000..aa3f50cb --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbuser.txt @@ -0,0 +1,5 @@ +Mzq6FD10db2l7B0PUsGcYYduQeqiVSrnk9h3n19oc4LKdC76Ikmtwk/1yIveM4zRyiHrZAJufxOK +OeUgctjR/M1lWlkLPcbcPL4avIbnLRiEJyp18vCwF2nKf+ZO0+92zlob78x1kIuC7s4JT70ooxJd +DLq2OaT34cry7EZePOoLXt+2BE/8eaPRyITTge/ccdgIPGWiD+35X0YhwgDcuirD5BBMculLfovp +uZh2We2eVDFXUYRB0kzajN52DW0YdH4CPgZmAvIkxJW4hOW8nrReFsu2f4r4wLnDdCeFJeX/xVaR +qqls/6NndmpXQ5MoUBK1F1SRNVVxZEUJ65a4yw== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/extfile.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/extfile.txt new file mode 100644 index 00000000..042aa390 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/extfile.txt @@ -0,0 +1 @@ +subjectAltName=DNS:cdb-dev-lrest.cdbnamespace,DNS:www.example.com diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt new file mode 100644 index 00000000..3a6fa68c --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDgDCCAmigAwIBAgIUQqka1JupuNcVEvXgqTlwFbK92IMwDQYJKoZIhvcNAQEL +BQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEUMBIG +A1UECgwLb3JhY2xlLEluYy4xFzAVBgNVBAMMDm9yYWNsZSBSb290IENBMB4XDTI0 +MDkwMzIyMjcwOVoXDTI1MDkwMzIyMjcwOVowYjELMAkGA1UEBhMCQ04xCzAJBgNV +BAgMAkdEMQswCQYDVQQHDAJTWjEUMBIGA1UECgwLb3JhY2xlLEluYy4xIzAhBgNV +BAMMGmNkYi1kZXYtbHJlc3QuY2RibmFtZXNwYWNlMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAwixJqBXGeQLcVDiZ6ztNw4/qmPHhEaPXb2Ce3jyHgxrO +CKjeXnuXoJEQFPm2n8kV+g1ie2EI04YM+NtERLZGE+yfTbtsP0Uw3ZqR238To9ID +wYF3by+pShC4r64kVtJ75DQYd4khOE5cXj2UpjsiWzln7tdZgHh2QBasSvTGGNdt +CUXkvWPDMyM7GFdnLBtmUxEKKMKkwqAGCmqfxwiWdrniDEcWnJH02QWk/Yp9aiyT +TAz1PzHDNo/X8XX6oaCXle0Qlamx/WrNkXg7jWAMavPgU+CampBiL1xMkoIV2MpH +oabWpiLYLwZjkKHzyRw08Mt0NPbOWOBOP1/1sjomOQIDAQABozowODA2BgNVHREE +LzAtghpjZGItZGV2LWxyZXN0LmNkYm5hbWVzcGFjZYIPd3d3LmV4YW1wbGUuY29t +MA0GCSqGSIb3DQEBCwUAA4IBAQBeeyT+f2vp6tAR1DywiW3KuVdCARTwEbbLYOVx +MeRZkaGr1u/fKLnds6axWwsoKque1H1nCIaPH/9YKbNBtldxXKxS+TH7NcvJC3uE +TF96YBcUjteBn0pM1hUFCtadSwa8o3U7QfZkr06JUQm4RRcv+mspwIFHcrow2Us5 +SijbOCaZOJ9LkSRORo4NeJQwLyH29+IenRxKkjsawuIBXdmHWD/AMRhRrPT1sIXg +Mj8xnXgaQH3O8PhwlAqujzWqJAGYi0wQbCSrktgCBBY1DjLMfpYyJVF9IGC67v66 +QKuKusKSEkk80pHsQjLpXCGba1nnsgSL13TZ/ciaejwC1u+Y +-----END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt.orig b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt.orig new file mode 100644 index 00000000..0a1d1c2d --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt.orig @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDgDCCAmigAwIBAgIUQqka1JupuNcVEvXgqTlwFbK92IIwDQYJKoZIhvcNAQEL +BQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEUMBIG +A1UECgwLb3JhY2xlLEluYy4xFzAVBgNVBAMMDm9yYWNsZSBSb290IENBMB4XDTI0 +MDkwMzIxMjY1OFoXDTI1MDkwMzIxMjY1OFowYjELMAkGA1UEBhMCQ04xCzAJBgNV +BAgMAkdEMQswCQYDVQQHDAJTWjEUMBIGA1UECgwLb3JhY2xlLEluYy4xIzAhBgNV +BAMMGmNkYi1kZXYtbHJlc3QuY2RibmFtZXNwYWNlMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA3pC9MX99DkhF7npyoQK6uc/aN0HwGueOvtsOs0Cm4aPW +scxIy1UXrFUm62y4e3AILVEe32A8hqGwLFKsorWIolxNRnl2mzQnabMkeQOI+gq0 +sFWR598hlJ8kShWzA1n8b3XhVsz4T7Rf7WIa1cW0GWu+sS7h+s/4gDk3lCgUnqk6 +Xg3ohmHy4Plv6PuwgcHcFpFwOYlhzc2/6DsPe6+fCpSOZH7p6KwY+mdoOi8eCg4D +oawgyDS808qo0pjNqpChM6TsxQnkKvAHFDZkF2FK+YmV0DmCv2y7hL2LLFuSn5Gv +RIHwp+X6F1Ti9AqE0re+jNA2me0oxwrSwZJKm8yZaQIDAQABozowODA2BgNVHREE +LzAtghpjZGItZGV2LWxyZXN0LmNkYm5hbWVzcGFjZYIPd3d3LmV4YW1wbGUuY29t +MA0GCSqGSIb3DQEBCwUAA4IBAQCQjHYjG4BAXBMq5trx52eNsfg8q54gWh2KNOYw +pJEm9QxAG+FI41iMFLP++ixpMklyQEP0cFp7nG3BVjQiCKdjDTxBejhyRtXPF64+ +VEMPSwvj0CWbFi0ZKR66Q6cT9FAsSlP9vKlr6gAaqcTPZwpdg3z7ZDwdSBM1v1Uq +4+06mTZsgp/+j0x6o+uaeDSpUj8HyJMTz9VitMVEntkg4CTdu9qgivPIfKQLd9co +nZbSbEeXjqz70nMSxn+65gs1Ot4c0HRKvL9lRixUKvDPE7bcYVBpWJgq1Fh8xaBp +eRWxSCeWRRJKWB+eUzA36gGKFBwnGko44fHDaQij4y2s6EJg +-----END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.csr b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.csr new file mode 100644 index 00000000..7ce997ba --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICpzCCAY8CAQAwYjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQH +DAJTWjEUMBIGA1UECgwLb3JhY2xlLEluYy4xIzAhBgNVBAMMGmNkYi1kZXYtbHJl +c3QuY2RibmFtZXNwYWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +wixJqBXGeQLcVDiZ6ztNw4/qmPHhEaPXb2Ce3jyHgxrOCKjeXnuXoJEQFPm2n8kV ++g1ie2EI04YM+NtERLZGE+yfTbtsP0Uw3ZqR238To9IDwYF3by+pShC4r64kVtJ7 +5DQYd4khOE5cXj2UpjsiWzln7tdZgHh2QBasSvTGGNdtCUXkvWPDMyM7GFdnLBtm +UxEKKMKkwqAGCmqfxwiWdrniDEcWnJH02QWk/Yp9aiyTTAz1PzHDNo/X8XX6oaCX +le0Qlamx/WrNkXg7jWAMavPgU+CampBiL1xMkoIV2MpHoabWpiLYLwZjkKHzyRw0 +8Mt0NPbOWOBOP1/1sjomOQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBABINdvR5 +LBhtcnS53cPxV6yBjSZNnUXDKt6KYOPtHKDo1Gzkhpb9p/3mPVppY4s9xhNwqkEv +RJseqNCmCjvdi+bWesidqHmFw0eh++5MuGWjVQ3qlV8vepBJ+pRy/9RG2fe3aFuc +enoSR0ZpYZR5sNW1PfcwooY/Bhu6GY0YGapHH8m8lezZ+LSWIHnVBEfO5mC9l0ON +y5B8ehiTIZ+SkkKe1ByriucUuz/RO/zSjzNo1JS4y4F07M+nG2cwr+HjC1RMxYMJ +t2hIo3Xa3f9515ciaItDCsBQp0WbGLVfSoRVKmuY+/Z3lqaxp7ejOwVBxwTsViEX +1pIx9DOMqgnXg6s= +-----END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.key b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.key new file mode 100644 index 00000000..96511485 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDCLEmoFcZ5AtxU +OJnrO03Dj+qY8eERo9dvYJ7ePIeDGs4IqN5ee5egkRAU+bafyRX6DWJ7YQjThgz4 +20REtkYT7J9Nu2w/RTDdmpHbfxOj0gPBgXdvL6lKELivriRW0nvkNBh3iSE4Tlxe +PZSmOyJbOWfu11mAeHZAFqxK9MYY120JReS9Y8MzIzsYV2csG2ZTEQoowqTCoAYK +ap/HCJZ2ueIMRxackfTZBaT9in1qLJNMDPU/McM2j9fxdfqhoJeV7RCVqbH9as2R +eDuNYAxq8+BT4JqakGIvXEySghXYykehptamItgvBmOQofPJHDTwy3Q09s5Y4E4/ +X/WyOiY5AgMBAAECggEAcnoF2vErmECVgZn1NPg7uqZom09PcET2SJXgVAm/ow30 +lqpon6+iD2o23wbyInY9XLTzcOYefAa2MymisBf+bNS+fueWxB6Y1q3AfHYJMDYr +PLSFo6mn1yDQodK7XeyuWjZUjnTsfvPEBVOtBewrLzlDC08vV0yt+s7jOTXibNXd +z9JxaeXtunwErNSVngTQSAJLDQ2UQ5bwPO/yvpbI2qFcCfINcVZT8l42e4eSxuyE +Km1PKBsUtfxBtx1WHjTDdZ9fs4mjRmfUywQSfk2qv5VvjCk4IJfuB7OAEWW0ahbl +Kr0rfYza39/R4gwskjZEECWWYemsbl/atbDku62iAQKBgQDu5FGTT+j/2NqQ3WGy +P36YBJVZXblM6jrQ9mgMVCZaEgh0F8CpydALWmpVWzjf/Vhlr9IrBXvmjfMEMrbP +yjn7ltUvjA7sZi4l9komXYgCpBSkyqnQTMJ/h7FQsuMGxUPFtO44xiaOBbDFgZjv +AUuHhJ+D/gz/j6g7DIYcn5gZmQKBgQDQFB+nkku15mP3z3vzo2IuOgGZqSztWdM7 +tF/4+dZM3lMQMbtXWNx1gI21ruua6tbVnbQ6Cv+SrvC1kCKizr3v37+oMnL8gONY +zxRJMgQaz0JAVendd2Y+WxDY7/mNd7WHGFeGC2TntqQDXXSZ4WkybBFkhgyTnntl +rVkEVYyVoQKBgGEMYBd4Xy+Q6Tnwtb+0C7m2IvYrHRwiC8LYV2yqwunUaZrAC4Tu +d5YlJQ2IAZL9WOv2gWr4z1zaTd9mut2vSd7rZ6/pNibTW+wQIg9z780i20AXFOWK +SXmFpoMiKRiXCvFGj4CA/yizJx0ViQuPex+SwTrd+mnX8oAKlnEKZMABAoGALd9B +2z91UXSpNUiYmu26B1biY23YabsTxmXNgqYRiIC67ycHlgoREBZ3J0aYyl5bXlyW +DvW9wiivCFfuStYpKWi6Z+o3T1Nay4lbf4d3QDHQ+T33gMuBzt1kqW6+JWHuswm6 +weJhLHQnVFaWDhaTinoom3Cx9RlLOu8sYLg7dgECgYEAmSi1XwwOHiWuK9jiHcYZ +fzXfI+nslkSlaqNOZ89LUr8wlubODtqLlKc3ADQ0NSkJsAup9ufkQqke2eCjlvsx +4OoOKlu9Ag/ua2nC63cLEQM+2pVjWCrL16dMjQR+9fg/prkQrWPcle6xPd5V4cnw +y9/n1cN5/8uBeKviJ7cNzVk= +-----END PRIVATE KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbpwd.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbpwd.txt new file mode 100644 index 00000000..264f6ba0 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbpwd.txt @@ -0,0 +1 @@ +welcome1 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbusr.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbusr.txt new file mode 100644 index 00000000..97ce6cbb --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbusr.txt @@ -0,0 +1 @@ +welcome diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/public.pem b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/public.pem new file mode 100644 index 00000000..63eac5c4 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnltrUl+ARDT2aP+23XVN +bpSEGN0p8jSGe852TsRQhjfffGtOtomiSMnmk9by4IpYDjuWTUbnkjaaALxnLYVB +JnIyEnqsUfSw2X0JT1QF3tZsh7C3tTNTY9vE2VTdoFiKbahHuv+t5eGnxV8RHD3g +0iNi7BlmTeI7XhbucNDgXU/eDQfIPNaFVGQEBqhkH7mqTkMUjgq3a0UGqiEXhvUY +8ytyskjHg7uDO8MgN371OtubGlyV2uQp6mqigKHl98oY1Zl/39Panrg2NXEr+UIV +tmnxi+LHdoNf0lICAnC9Kycd/5YxdvQi+Ztm+OK5vyTI77OMCqNPZWsdB2EcGfr+ +XwIDAQAB +-----END PUBLIC KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbpass.txt new file mode 100644 index 00000000..264f6ba0 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbpass.txt @@ -0,0 +1 @@ +welcome1 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbuser.txt new file mode 100644 index 00000000..97ce6cbb --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbuser.txt @@ -0,0 +1 @@ +welcome diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/check.sh b/docs/multitenant/lrest-based/usecase05_to_be_removed/check.sh new file mode 100644 index 00000000..469a4ae1 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/check.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +kubectl apply -f open_lrpdb1_resource.yaml +kubectl get lrpdb lrpdb1 -n pdbnamespace --watch diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb1_resource.yaml new file mode 100644 index 00000000..1fe63c15 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb1_resource.yaml @@ -0,0 +1,47 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + srcPdbName: "pdbdev" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + pdbconfigmap: "config-map-pdb" + assertiveLrpdbDeletion: true + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + action: "Clone" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb2_resource.yaml new file mode 100644 index 00000000..926810e8 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb2_resource.yaml @@ -0,0 +1,46 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb4 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone2" + srcPdbName: "pdbprd" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertiveLrpdbDeletion: true + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + action: "Clone" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb1_resource.yaml new file mode 100644 index 00000000..8f5528d9 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb1_resource.yaml @@ -0,0 +1,39 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb2_resource.yaml new file mode 100644 index 00000000..884ebb90 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb2_resource.yaml @@ -0,0 +1,40 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb3_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb3_resource.yaml new file mode 100644 index 00000000..5a080963 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb3_resource.yaml @@ -0,0 +1,40 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod.yaml new file mode 100644 index 00000000..962a8fa1 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod.yaml @@ -0,0 +1,48 @@ +apiVersion: database.oracle.com/v4 +kind: LREST +metadata: + name: cdb-dev + namespace: cdbnamespace +spec: + cdbName: "DB12" + lrestImage: "lin.ocir.io/intsanjaysingh/mmalvezz/testppr/lrest-dboper:latest" + lrestImagePullPolicy: "Always" + dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + replicas: 1 + deletePdbCascade: true + cdbAdminUser: + secret: + secretName: "dbuser" + key: "e_dbuser.txt" + cdbAdminPwd: + secret: + secretName: "dbpass" + key: "e_dbpass.txt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + cdbPubKey: + secret: + secretName: "pubkey" + key: "publicKey" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + + + + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod_delcascade.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod_delcascade.yaml new file mode 100644 index 00000000..962a8fa1 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod_delcascade.yaml @@ -0,0 +1,48 @@ +apiVersion: database.oracle.com/v4 +kind: LREST +metadata: + name: cdb-dev + namespace: cdbnamespace +spec: + cdbName: "DB12" + lrestImage: "lin.ocir.io/intsanjaysingh/mmalvezz/testppr/lrest-dboper:latest" + lrestImagePullPolicy: "Always" + dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + replicas: 1 + deletePdbCascade: true + cdbAdminUser: + secret: + secretName: "dbuser" + key: "e_dbuser.txt" + cdbAdminPwd: + secret: + secretName: "dbpass" + key: "e_dbpass.txt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + cdbPubKey: + secret: + secretName: "pubkey" + key: "publicKey" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + + + + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb1_resource.yaml new file mode 100644 index 00000000..78076413 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb1_resource.yaml @@ -0,0 +1,53 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: "lrpdb1" + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + assertiveLrpdbDeletion: true + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + fileNameConversions: "NONE" + unlimitedStorage: false + pdbconfigmap: "config-map-pdb" + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb2_resource.yaml new file mode 100644 index 00000000..c9466df3 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb2_resource.yaml @@ -0,0 +1,51 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbName: "DB12" + cdbNamespace: "cdbnamespace" + pdbName: "pdbprd" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/dbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/dbpass.txt new file mode 100644 index 00000000..eab39d3f --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/dbpass.txt @@ -0,0 +1 @@ +CLWKO655321 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/dbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/dbuser.txt new file mode 100644 index 00000000..7028cc36 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/dbuser.txt @@ -0,0 +1 @@ +restdba diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb1_resource.yaml new file mode 100644 index 00000000..7390db88 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb1_resource.yaml @@ -0,0 +1,41 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb2_resource.yaml new file mode 100644 index 00000000..09525b98 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb2_resource.yaml @@ -0,0 +1,41 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + pdbName: "pdbprd" + action: "Delete" + dropAction: "INCLUDING" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb3_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb3_resource.yaml new file mode 100644 index 00000000..e362f068 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb3_resource.yaml @@ -0,0 +1,41 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + pdbName: "new_clone" + action: "Delete" + dropAction: "INCLUDING" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/dmpplane.819519 b/docs/multitenant/lrest-based/usecase05_to_be_removed/dmpplane.819519 new file mode 100644 index 00000000..c569c32d --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/dmpplane.819519 @@ -0,0 +1,30 @@ +2024-11-04T11:19:59Z INFO controller-runtime.builder Registering a validating webhook {"GVK": "database.oracle.com/v1alpha1, Kind=DataguardBroker", "path": "/validate-database-oracle-com-v1alpha1-dataguardbroker"} +2024-11-04T11:19:59Z INFO controller-runtime.webhook Registering webhook {"path": "/validate-database-oracle-com-v1alpha1-dataguardbroker"} +2024-11-04T11:19:59Z INFO setup starting manager +2024-11-04T11:19:59Z INFO controller-runtime.metrics Starting metrics server +2024-11-04T11:19:59Z INFO controller-runtime.metrics Serving metrics server {"bindAddress": ":8080", "secure": false} +2024-11-04T11:19:59Z INFO controller-runtime.webhook Starting webhook server +2024-11-04T11:19:59Z INFO controller-runtime.certwatcher Updated current TLS certificate +2024-11-04T11:19:59Z INFO controller-runtime.webhook Serving webhook server {"host": "", "port": 9443} +2024-11-04T11:19:59Z INFO controller-runtime.certwatcher Starting certificate watcher +I1104 11:19:59.901034 1 leaderelection.go:250] attempting to acquire leader lease oracle-database-operator-system/a9d608ea.oracle.com... +2024-11-04T11:19:37Z INFO controller-runtime.webhook Serving webhook server {"host": "", "port": 9443} +2024-11-04T11:19:37Z INFO controller-runtime.certwatcher Starting certificate watcher +I1104 11:19:37.669237 1 leaderelection.go:250] attempting to acquire leader lease oracle-database-operator-system/a9d608ea.oracle.com... +2024-11-04T11:20:49Z INFO lrpdb-webhook Setting default values in LRPDB spec for : lrpdb4 +2024-11-04T11:20:49Z INFO lrpdb-webhook - reuseTempFile : true +2024-11-04T11:20:49Z INFO lrpdb-webhook - unlimitedStorage : true +2024-11-04T11:20:49Z INFO lrpdb-webhook - tdeImport : false +2024-11-04T11:20:49Z INFO lrpdb-webhook - tdeExport : false +2024-11-04T11:20:49Z INFO lrpdb-webhook - asClone : false +2024-11-04T11:20:49Z INFO lrpdb-webhook - getScript : false +===> controllers.restSQLCollection{Env:struct { DefaultTimeZone string "json:\"defaultTimeZone,omitempty\"" }{DefaultTimeZone:""}, Items:[]controllers.SQL_Item(nil)} +===> {Env:{DefaultTimeZone:} Items:[]} +2024-11-04T11:22:24Z DEBUG events cdb-dev {"type": "Warning", "object": {"kind":"LRPDB","namespace":"pdbnamespace","name":"lrpdb1","uid":"cfbd1d27-5571-444c-b175-4838b1f68d5a","apiVersion":"database.oracle.com/v4","resourceVersion":"62119140"}, "reason": "Done"} +2024-11-04T11:22:24Z INFO controllers.LRPDB Begin respData {"getLRPDBState": {"name":"lrpdb1","namespace":"pdbnamespace"}} +2024-11-04T11:22:24Z INFO controllers.LRPDB {"inst_id":1,"con_id":3,"open_mode":"MOUNTED","restristced":"NONE","total_size":855638016,"sqlcode":0} + {"getLRPDBState": {"name":"lrpdb1","namespace":"pdbnamespace"}} +2024-11-04T11:22:24Z INFO controllers.LRPDB End respData {"getLRPDBState": {"name":"lrpdb1","namespace":"pdbnamespace"}} +2024-11-04T11:22:24Z INFO controllers.LRPDB Successfully obtained LRPDB state {"getLRPDBState": {"name":"lrpdb1","namespace":"pdbnamespace"}, "LRPDB Name": "pdbdev", "State": "MOUNTED"} +2024-11-04T11:22:24Z INFO controllers.LRPDB Frame: {"ManageConfigMapForCloningAndPlugin": {"name":"lrpdb1","namespace":"pdbnamespace"}} +2024-11-04T11:22:24Z INFO controllers.LRPDB Reconcile completed {"multitenantoperator": {"name":"lrpdb1","namespace":"pdbnamespace"}} diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbpass.txt new file mode 100644 index 00000000..3d0430f0 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbpass.txt @@ -0,0 +1,5 @@ +orwWVJyNxwyQNGhlvmugFC4Ce7UtXCoFYspwA4CRMQ1vc+ASaN6hcw2cH3yBhXUOisycmnUPsKwx +0JOiZ6B7Iee+IhJhIoXuYSiPShAs0MFvMautBtzSoX6uH4KOYC6vKVkcQWpsTDU1BMgOvgmswGkQ +v6d3J2jkxsJnXY2Ium43LyqVdKWEen2JDAYvdWCTPlte2YmHF48sqPx5fFrf/NYIti5Te6/afgct +ks6CYr7X55fSC/K1zXW6xde6uPU3F/JPUDGtB6E/XzhE/qyQSN+v32mBQZngw7o+wSNlC08XMOJR +umhK+Y8QiLxndyqhCdxT455KnjKToTaS7QwTNw== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbuser.txt new file mode 100644 index 00000000..365c5725 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbuser.txt @@ -0,0 +1,5 @@ +4Zxo3RqNxxWhTjKCk4mHM1H4gQN2iDB0qYfcdNlZmG2pxBEK8OIzB75IIb6wvDd2zYqninEiJhQB +HCbXKm+fs+uzappCnhqycIh132M7m2Xt0Kt24bAKBmN9KnKNcRJU7VnTadTG/sk8Q4A5cz1ZLywF +NS/KwP/1bWRdgTZDmUnJJYl7vhiM2nbcW0EW/a1DH0oybY/iQ/GhYpoEZiS9v1+S8boRRmdoj2Au +sZ+B13sHYyXKpL/ch/8LsYVcEQ1Tdb8U+sleaccfKwFuthwqBUx51EVxy5ITf90CQV+fEKUPmCPP +BVi6CbaklQr44yx24JB682WLFPH5JpbS4p8eTg== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbpwd.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbpwd.txt new file mode 100644 index 00000000..c07a3e3e --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbpwd.txt @@ -0,0 +1,5 @@ +UDzcNNEF8MSGwpzyEl9o6lkABX5WDGJ7QSxTfmIEI23WoGuBi3cBA259f3zc9kg75i+xjrDqLgp2 +TPCsNRU0ZiVSg2TWZqJBMl3TZ3RE51uijfQPWgJco4uttzxyzRqbJ2EY8Xv5ClbKqbjZDqIS+AIQ +hCDHd3sV/xtPziMluNEWlEQp+G6te48FLN8ipRxNKQIJZdRtfx0gUWSiHZHT10Y7GDr/lJmqFDCv +W9bnvLoTyxemIq5nI5HPCD8n3/Y0Hs+vxC8y96cQ5bUwVKjxwKnZ+dmUeOd9BgRzPyJqReqWF0m1 +lNh3Ott5ZMPeNA1eT4dFDmyJtRBV7+XDTHWPfA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbusr.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbusr.txt new file mode 100644 index 00000000..d4b710ea --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbusr.txt @@ -0,0 +1,5 @@ +l0hpojsR6gMWUM5pNAsIpFHSY1gti1VTTo5fIwWEPxcbhRLmbBN3sHSJFgue90Bkb9yC5jX0ShwJ +GZZWpI/QhfhYm9LSXF8I2Jhrz6Ptg+xnkl9VJYaX718CzcFU0hx18vPbXTfvCoiOod+I84wJGoNu +qf80bWmh+fUzdJdTGnw16oQ/JFpF3Q60hYV8a5OKQ7AsNpch9jaNZdxZmHfz6RRap2/Tmr0GQPJH +zmaWLFZmRYgwLaXsO3u8Hg4H92K+Adelz/qBaxGwmNd56ZsCgIlxyZ479fphjKVK1S92upQZgHo3 +1DAWMkLTyjc8lbsMCT5EwNL56P2Pg8+wsogbhA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbpass.txt new file mode 100644 index 00000000..80338208 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbpass.txt @@ -0,0 +1,5 @@ +PdZQQJ/7XB3F5evBDXmi8E90eHTsgYCpQ5tJl/m7QcXyMOcTozVVchI0eSOzxR+FXrVX0QiluzZ7 +1ceuqauRwuVyjRhvdHzFLRv6E4PogG+u4+ce2vlKVhyZnlSzpjfx2utDbIV+/jL74dPQuPPdQ/mb +5Kx01vaFLg42KMGzHFDd0wv5NtePspxHyk2czGspipVO1rwZLhC4NnODg3MWqfSuVwHNr1sCz4w6 +MMYsQkoRH5S23NZfVPnzVcBzw4mSNM1h0rlPZrwxEoLaUSPGtk26HZCNeDAKN7mtcUit1w7T+jAh +OVVjBQ2D38zYqqOVl7cjaA8+KLoUyoioGkHCag== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbuser.txt new file mode 100644 index 00000000..9508a37f --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbuser.txt @@ -0,0 +1,5 @@ +QWh2DCkOtZLKQYJQ5a329NpPgBp31KVvOVpKEJopg/j+mkZKOM9CM9AeF6ES0FXPKvkp9DJNTluE +AlnQe5Tt8h7FvD7yWvPnr25tepnbrAnykjCusQAUv9TBuncIXKMQnKAZU3h8CZEfNwNp9itVt1hJ +L0DUU3azS1utyBRMKPbI6uxsJKtnImO0nj37r+i4V0au0K9nbFntLu3h+tIEC/9owP1ydhLSYHM4 +SlhAWCSP17bzoi/+/AXlvteVMGGd+xeWG3qCunwCg4qEJHHNCA5sKv5DXclqlPZPHf1kW0zq2uLZ +oknIu95/ZoSSEPtVi4ws6faznKCoZ4toBCBguw== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/extfile.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/extfile.txt new file mode 100644 index 00000000..042aa390 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/extfile.txt @@ -0,0 +1 @@ +subjectAltName=DNS:cdb-dev-lrest.cdbnamespace,DNS:www.example.com diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/makefile b/docs/multitenant/lrest-based/usecase05_to_be_removed/makefile new file mode 100644 index 00000000..f3e87f69 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/makefile @@ -0,0 +1,647 @@ +# __ __ _ __ _ _ +# | \/ | __ _| | _____ / _(_) | ___ +# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ +# | | | | (_| | < __/ _| | | __/ +# |_| |_|\__,_|_|\_\___|_| |_|_|\___| +# +# ___ +# / _ \ _ __ _ __ _ __ ___ _ __ ___ +# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ +# | |_| | | | | |_) | | | __/ | | | | | +# \___/|_| |_| .__/|_| \___|_| |_| |_| +# |_| +# ____ _ _ _ +# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ +# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| +# | |__| (_) | | | | |_| | | (_) | | | __/ | +# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| +# +# +# This makefile helps to speed up the kubectl commands executions to deploy and test +# the multitenant operator. +# +# Quick start: +# ~~~~~~~~~~~ +# +# - Copy files of tab.1 in the makefile directory. +# - Edit the secret files and other yaml files with the correct credential as +# specified in the documentation. +# - Edit makefile updating variables of tab.2 +# - Execute commands of tab.3 "make step1" "make step2" "make step3".... +# +# Tab.1 - List of required files +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# oracle-database-operator.yaml : oracle database operator +# cdbnamespace_binding.yaml : role binding for cdbnamespace +# pdbnamespace_binding.yaml : role binding for pdbnamespace +# create_lrest_secret.yaml : create secrets for rest server pod +# create_lrpdb_secret.yaml : create secrets for pluggable database +# create_lrest_pod.yaml : create rest server pod +# create_lrpdb1_resource.yaml : create first pluggable database +# create_lrpdb2_resource.yaml : create second pluggable database +# open_lrpdb1_resource.yaml : open first pluggable database +# open_lrpdb2_resource.yaml : open second pluggable database +# close_lrpdb1_resource.yaml : close first pluggable database +# close_lrpdb2_resource.yaml : close second pluggable database +# clone_lrpdb_resource.yaml : clone thrid pluggable database +# clone_lrpdb2_resource.yaml : clone 4th pluggable database +# delete_lrpdb1_resource.yaml : delete first pluggable database +# delete_lrpdb2_resource.yaml : delete sencond pluggable database +# delete_lrpdb3_resource.yaml : delete thrid pluggable database +# unplug_lrpdb1_resource.yaml : unplug first pluggable database +# plug_lrpdb1_resource.yaml : plug first pluggable database +# map_lrpdb1_resource.yaml : map the first pluggable database +# config_map.yam : pdb parameters array +# altersystem_lrpdb1_resource.yaml : chage cpu_count count parameter for the first pdb + +# +# Tab.2 - List of variables +# ~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# |OCIR | Your image registry | +# +-----------------------------+---------------------------------------------+ +# |OCIRPATH | Path of the image in your registry | +# +-----------------------------+---------------------------------------------+ +# +# Tab.3 - Execution steps +# ~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# | MAKEFILE TARGETS LIST | +# | ----- ooo ----- | +# | - TARGET - - DESCRIPTION - | +# +-----------------------------+---------------------------------------------+ +# |step0 | list pods and lrpdb resources | +# +-----------------------------+-------------------------------------+-------| +# |step1 | Build rest server images | | +# +-----------------------------+-------------------------------------+ REST | +# |step2 | Tag the immages | SRV | +# +-----------------------------+-------------------------------------+ IMG | +# |step3 | Push the image into the repository | | +# +-----------------------------+-------------------------------------+-------+ +# |step4 | Load webhook certmanager | DB | +# +-----------------------------+-------------------------------------+ OPER | +# |step5 | Create the db operator | | +# +-----------------------------+-------------------------------------+-------+ +# |step6 | Create tls certificates | T | +# +-----------------------------+-------------------------------------+ L | +# |step7 | Create tls secret | S | +# +-----------------------------+---------------------------------------------+ +# |step8 | Create database secrets | +# +-----------------------------+---------------------------------------------+ +# |step9 | Create restserver pod | +# | | +---------------------------------------------+ +# | +---> checkstep9 | Monitor the executions | +# +-----------------------------+---------------------------------------------+ +# |step10.[1..9] | Create pluggable database | +# | | +---------------------------------------------+ +# | +---> checklrest | Monitor LRPDB status | +# +-----------------------------+---------------------------------------------+ +# | step11.[1..9] | Open pluggable database | +# +-----------------------------+---------------------------------------------+ +# | step12.[1..9] | Close pluggable database | +# +-----------------------------+---------------------------------------------+ +# | step13.[1..9] | Clone pluggable database | +# +-----------------------------+---------------------------------------------+ +# | step14.[1..9] | Delete pluggable database | +# +-----------------------------+---------------------------------------------+ +# | step15.[1..9] | Unplug pluggable database | +# +-----------------------------+---------------------------------------------+ +# | step16.[1..9] | Plug pluggable database | +# +-----------------------------+---------------------------------------------+ +# | step17.[1..9] | Map pluggable database | +# +-----------------------------+---------------------------------------------+ +# | DIAGNOSTIC TARGETS | +# +-----------------------------+---------------------------------------------+ +# | dump | Dump pods info into a file | +# +-----------------------------+---------------------------------------------+ +# | reloadop | Reload the db operator | +# +-----------------------------+---------------------------------------------+ +# | login | Login into cdb pod | +# +-----------------------------+---------------------------------------------+ + +DATE := `date "+%y%m%d%H%M%S"` + +################ TAB 2 VARIABLES ############ +OCIR=lin.ocir.io +OCIRPATH=intsanjaysingh/mmalvezz/testppr/$(REST_SERVER)-dboper:$(LRESTVERSION) +############################################# + +OPRNAMESPACE=oracle-database-operator-system +LRSNAMESPACE=cdbnamespace +PDBNAMESPACE=pdbnamespace +CONFIG_MAP=config-map-pdb + +REST_SERVER=lrest +LRESTVERSION=latest +DOCKER=/usr/bin/docker +#KUBECTL=/usr/bin/kubectl +KUBECTL=/usr/local/go/bin/kubectl +CONFIG=/etc/ords/config +IMAGE=oracle/$(REST_SERVER)-dboper:$(LRESTVERSION) +DBOPERATOR=oracle-database-operator.yaml +DBOPERATORNOVERBS=oracle-database-operator-noverbs.yaml +URLPATH=/_/db-api/stable/database/pdbs/ +OPENSSL=/usr/bin/openssl +ORDSPORT=8888 +MAKE=/usr/bin/make +DOCKERFILE=Dockerfile +RM=/usr/bin/rm +MKDIR=/bin/mkdir +TAR=/bin/tar +CP=/bin/cp +ECHO=/usr/bin/echo +SED=/bin/sed +SLEEP=sleep 32 +OPRNAMESPACE=oracle-database-operator-system +CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml +LREST_SECRET=create_lrest_secret.yaml +LRPDB_SECRET=create_lrpdb_secret.yaml +TDE_SECRET=tde_secret.yaml +PDBROLEBINDING=pdbnamespace_binding.yaml +CDBROLEBINDING=cdbnamespace_binding.yaml +LREST_POD=create_lrest_pod.yaml +LRPDB1=create_lrpdb1_resource.yaml +LRPDB2=create_lrpdb2_resource.yaml +LRPDBOPEN1=open_lrpdb1_resource.yaml +LRPDBOPEN2=open_lrpdb2_resource.yaml +LRPDBOPEN3=open_lrpdb3_resource.yaml +LRPDBCLOSE1=close_lrpdb1_resource.yaml +LRPDBCLOSE2=close_lrpdb2_resource.yaml +LRPDBCLOSE3=close_lrpdb3_resource.yaml +LRPDBCLONE1=clone_lrpdb1_resource.yaml +LRPDBDELETE1=delete_lrpdb1_resource.yaml +LRPDBDELETE2=delete_lrpdb2_resource.yaml +LRPDBDELETE3=delete_lrpdb3_resource.yaml +LRPDBUNPLUG1=unplug_lrpdb1_resource.yaml +LRPDBPLUG1=plug_lrpdb1_resource.yaml +LRPDBMAP1=map_lrpdb1_resource.yaml +LRPDBMAP2=map_lrpdb2_resource.yaml +LRPDBMAP3=map_lrpdb3_resource.yaml +ALTERSYSTEMYAML=altersystem_lrpdb1_resource.yaml +SWAPYAML=/tmp/swap.yaml +SKEY=tls.key +SCRT=tls.crt +CART=ca.crt +RESNAME=-o=jsonpath='{range .items[*]} {.metadata.name} {.status.openMode} ' +SQLCODE=-o=jsonpath='{range .items[*]} {.metadata.name} {.status.sqlCode} ' +COMPANY=oracle +MAKEFILE=./makefile +RNDFILE=./rnd.txt + +list: + grep ^step $(MAKEFILE) + +step0: listres #[OK] +step1: createimage #[OK] +step2: tagimage #[OK] +step3: push #[OK] +step4: certmanager #[OK] +step5: dboperator #[OK] +step5.1: rolebinding #[OK] +step5.2: deldbop #[OK] +step6: tlscert #[OK] +step7: tlssecret #[OK] +step8: lrestsecretcred #[OK] +step9: lrest_pod #[OK] +step9.1: checklrest #[OK] +step10.1: lrpdbcreate1 #[OK] +step10.2: lrpdbcreate2 #[OK] +step11.1: lrpdbopen1 #[OK] +step11.2: lrpdbopen2 #[OK] +step12.1: lrpdbclose1 #[OK] +step12.2: lrpdbclose2 #[OK] +step13.1: lrpdbclone1 #[OK] +step13.2: lrpdbopen3 #[0K] +step13.3: lrpdbclose3 #[0K] +step14.1: lrpdbunplug1 #[OK] +step15.1: lrpdbplug1 #[OK] +step16.1: lrptkmap1 #[OK] +step16.2: lrptkmap2 #[OK] +step16.3: lrptkmap3 #[OK] +step17.1: lrpdbdelete1 #[OK] +step17.2: lrpdbdelete2 #[OK] +step17.3: lrpdbdelete3 #[OK] +step17.4: lrpdbdelete4 #[OK] +step18.1: lrpdbgalter #[OK] +step19: dellrestcascade +step100.1: lrptkunplug1 #[OK] +step101.1: lrptndelete1 #[OK] +step102.1: lrptnclone1 #[OK] +step103.1: optnowatch #[OK] +step104.1: optnoverbs #[OK] +step105.1: lrpdbnegalter #[OK] + + +listres: + $(KUBECTL) get pods -n $(OPRNAMESPACE) + $(KUBECTL) get lrpdb -n $(OPRNAMESPACE) + +createimage: + @echo "BUILDING CDB IMAGES" + @if [[ ! -f ./Dockerfile ]]; \ + then\ + echo "DOCKERFILE DOES NOT EXISTS";\ + exit 1; \ + fi; + @if [[ ! -f ./runLREST.sh ]]; \ + then\ + echo "DOCKERFILE DOES NOT EXISTS";\ + exit 1; \ + fi; + $(DOCKER) build -t $(IMAGE) . + +tagimage: + @echo "TAG IMAGE" + $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) + +push: + @echo "PUSH IMAGE INTO THE REGISTRY" + $(DOCKER) push $(OCIR)$(OCIRPATH) + +certmanager: + @echo "WEBHOOK CERT MANAGER" + $(KUBECTL) apply -f $(CERTMANAGER) + +dboperator: + @echo "ORACLE DATABASE OPERATOR" + cat $(DBOPERATOR)| sed 's/value: ""/value: "$(OPRNAMESPACE),$(PDBNAMESPACE),$(LRSNAMESPACE)"/g' >temp_opr.yaml + $(KUBECTL) apply -f temp_opr.yaml + $(RM) temp_opr.yaml + +rolebinding: + $(KUBECTL) apply -f $(PDBROLEBINDING) + $(KUBECTL) apply -f $(CDBROLEBINDING) + +PRVKEY=ca.key +PUBKEY=public.pem +tlscert: + @echo "CREATING TLS CERTIFICATES" + #OL8 + $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) + #OL9 + #$(OPENSSL) genrsa -out $(PRVKEY) 2048 + $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(LRSNAMESPACE)" -out server.csr + $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(LRSNAMESPACE),DNS:www.example.com" > extfile.txt + $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) + $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) + +tlssecret: + @echo "CREATING TLS SECRETS" + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(LRSNAMESPACE) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDBNAMESPACE) + $(KUBECTL) create secret tls prvkey --key="$(PRVKEY)" --cert=ca.crt -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(LRSNAMESPACE) + + +opsecret: + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) + +dbsecret: + @echo "CREATING DB SECRETS" + $(KUBECTL) apply -f $(LREST_SECRET) -n $(LRSNAMESPACE) + $(KUBECTL) apply -f $(LRPDB_SECRET) -n $(PDBNAMESPACE) + #$(KUBECTL) apply -f $(TDE_SECRET) -n $(OPRNAMESPACE) + +DBUSERFILE=dbuser.txt +DBPASSFILE=dbpass.txt +WBUSERFILE=wbuser.txt +WBPASSFILE=wbpass.txt +PDBUSRFILE=pdbusr.txt +PDBPWDFILE=pdbpwd.txt + +lrestsecretcred: + $(KUBECTL) delete secret prvkey -n $(LRSNAMESPACE) + $(KUBECTL) delete secret pubkey -n $(LRSNAMESPACE) + $(ECHO) "restdba" > $(DBUSERFILE) + $(ECHO) "CLWKO655321" > $(DBPASSFILE) + $(ECHO) "welcome" > $(WBUSERFILE) + $(ECHO) "welcome1" > $(WBPASSFILE) + $(ECHO) "welcome" > $(PDBUSRFILE) + $(ECHO) "welcome1" > $(PDBPWDFILE) + $(KUBECTL) create secret generic prvkey --from-file=privateKey="$(PRVKEY)" -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic pubkey --from-file=publicKey="$(PUBKEY)" -n $(LRSNAMESPACE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBUSERFILE) |base64 > e_$(DBUSERFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBPASSFILE) |base64 > e_$(DBPASSFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) + $(KUBECTL) create secret generic dbuser --from-file=e_$(DBUSERFILE) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic dbpass --from-file=e_$(DBPASSFILE) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(LRSNAMESPACE) + $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic prvkey --from-file=privateKey="$(PRVKEY)" -n $(PDBNAMESPACE) + + +s2: + $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(PDBNAMESPACE) + + +# openssl rsautl -encrypt -pubin -inkey pub.key -in plaintext.txt | base64 > encrypted.txt +# cat encrypted.txt | base64 -d | openssl rsautl -decrypt -inkey priv.key +# kubectl create secret generic testsec --from-file=encrypted.txt --from-file=ca.key -n cdbnamespace + + + +lrest_pod: + @echo "CREATING LREST POD" + $(KUBECTL) apply -f $(LREST_POD) + $(KUBECTL) get pods -n $(LRSNAMESPACE) --watch + +checklrest: + $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(LRSNAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(LRSNAMESPACE) + +lrpdbcreate1: + $(KUBECTL) apply -f $(LRPDB1) + +lrpdbcreate1log: + $(KUBECTL) apply -f $(LRPDB1) & + $(KUBECTL) logs -f -l control-plane=controller-manager -n oracle-database-operator-system + +lrpdbcreate2: + $(KUBECTL) apply -f $(LRPDB2) + +lrpdbopen1log: + $(KUBECTL) apply -f $(LRPDBOPEN1) & + $(KUBECTL) logs -f -l control-plane=controller-manager -n oracle-database-operator-system + + +lrpdbopen1: + $(KUBECTL) apply -f $(LRPDBOPEN1) + +lrpdbopen2: + $(KUBECTL) apply -f $(LRPDBOPEN2) + +lrpdbopen3: + $(KUBECTL) apply -f $(LRPDBOPEN3) + +lrpdbclose1: + $(KUBECTL) apply -f $(LRPDBCLOSE1) + +lrpdbclose2: + $(KUBECTL) apply -f $(LRPDBCLOSE2) + +lrpdbclose3: + $(KUBECTL) apply -f $(LRPDBCLOSE3) + +lrpdbclone1: lrpdbopen1 + @echo "Waiting 10 sec before cloning" + @sleep 10 + $(KUBECTL) apply -f $(LRPDBCLONE1) + +lrpdbclone1log: + $(KUBECTL) apply -f $(LRPDBCLONE1) & + $(KUBECTL) logs -f -l control-plane=controller-manager -n oracle-database-operator-system + +lrpdbdelete1: + $(KUBECTL) apply -f $(LRPDBDELETE1) + +lrpdbdelete2: + $(KUBECTL) apply -f $(LRPDBDELETE2) + +lrpdbdelete3: + $(KUBECTL) apply -f $(LRPDBDELETE3) + +lrpdbdelete4: + echo "Imperative deletion" + $(KUBECTL) delete lrpdb lrpdb4 -n $(PDBNAMESPACE) + +lrpdbunplug1: + $(KUBECTL) apply -f $(LRPDBUNPLUG1) + +lrpdbplug1: + $(KUBECTL) apply -f $(LRPDBPLUG1) + +lrpdbplug1log: + $(KUBECTL) apply -f $(LRPDBPLUG1) & + $(KUBECTL) logs -f -l control-plane=controller-manager -n oracle-database-operator-system + + +lrptkmap1: + $(KUBECTL) delete lrpdb lrpdb1 -n $(PDBNAMESPACE) + $(SLEEP) + $(KUBECTL) apply -f $(LRPDBMAP1) + $(MAKE) -f $(MAKEFILE) checklrpdbs + +lrptkmap2: + $(KUBECTL) delete lrpdb lrpdb2 -n $(PDBNAMESPACE) + $(SLEEP) + $(KUBECTL) apply -f $(LRPDBMAP2) + $(MAKE) -f $(MAKEFILE) checklrpdbs + +lrptkmap3: + $(KUBECTL) delete lrpdb lrpdb3 -n $(PDBNAMESPACE) + $(SLEEP) + $(KUBECTL) apply -f $(LRPDBMAP3) + $(MAKE) -f $(MAKEFILE) checklrpdbs + +lrptkunplug1: + @echo "test unpluga and plug database" + @shuf -i 1-10000000 -n 1 >rnd_file.txt + @cat $(LRPDBUNPLUG1) |sed 's/pdb.xml/'$(shell cat ./rnd_file.txt)'.xml/g' > rnd_$(LRPDBUNPLUG1) + @cat $(LRPDBPLUG1) |sed 's/pdb.xml/'$(shell cat ./rnd_file.txt)'.xml/g' > rnd_$(LRPDBPLUG1) + $(KUBECTL) apply -f rnd_$(LRPDBUNPLUG1) + @echo -e "\033[5mWaiting for the unplug operation to complete\033[0m" + @while [ `$(KUBECTL) get lrpdb lrpdb1 -n $(PDBNAMESPACE) 2>/dev/null |grep ^lrpdb1 |wc -l` -eq 1 ];\ + do \ + sleep 1 ;\ + done + $(KUBECTL) apply -f rnd_$(LRPDBPLUG1) + @$(RM) ./rnd_file.txt rnd_$(LRPDBPLUG1) rnd_$(LRPDBUNPLUG1) + + +lrptndelete1: + @echo "lrptndelete1: Starting negative test: delete an open pdb" + $(KUBECTL) apply -f $(LRPDB1) + @echo -e "\033[5mCreating database\033[0m" + @while [ `$(KUBECTL) get lrpdbs -n $(PDBNAMESPACE) $(RESNAME) 2>/dev/null |grep lrpdb1 |wc -l` -eq 0 ];\ + do \ + $(SLEEP);\ + done + $(KUBECTL) apply -f $(LRPDBOPEN1) + @echo -e "\033[5mOpening database\033[0m" + @while [ `$(KUBECTL) get lrpdbs -n $(PDBNAMESPACE) $(RESNAME) 2>/dev/null |grep lrpdb1|grep "READ WRITE" |wc -l` -eq 0 ];\ + do \ + $(SLEEP);\ + done + $(SLEEP) + $(KUBECTL) apply -f $(LRPDBDELETE1) + $(SLEEP) + $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) + +lrpdbgalter: + $(KUBECTL) apply -f $(ALTERSYSTEMYAML) + $(SLEEP) + $(KUBECTL) describe lrpdb lrpdb1 -n $(PDBNAMESPACE) + +lrpdbnegalter: + $(SED) 's/cpu_count=[0-9]/cpu_count=100000/g' $(ALTERSYSTEMYAML) > $(SWAPYAML) + $(KUBECTL) apply -f $(SWAPYAML) + $(KUBECTL) get lrpdbs lrpdb1 -n $(PDBNAMESPACE) $(SQLCODE) + $(KUBECTL) describe lrpdb lrpdb1 -n $(PDBNAMESPACE) + +lrptnclone1: + @echo "lrptnclone1: Starting negative test: cloning a pdb in mount state" + $(KUBECTL) apply -f $(LRPDB1) + @echo -e "\033[5mCreating database\033[0m" + @while [ `$(KUBECTL) get lrpdbs -n $(PDBNAMESPACE) $(RESNAME) 2>/dev/null |grep ^lrpdb1 |wc -l` -eq 1 ];\ + do \ + $(SLEEP);\ + done + $(KUBECTL) apply -f $(LRPDBCLONE1) + $(SLEEP) + @while [ `$(KUBECTL) get lrpdbs lrpdb3 -n $(PDBNAMESPACE) $(SQLCODE) 2>/dev/null |grep lrpdb3 |grep 65036 |wc -l` -eq 0 ];\ + do \ + $(SLEEP);\ + done + $(KUBECTL) get lrpdb lrpdb3 -n $(PDBNAMESPACE) + +optnowatch: + cat $(DBOPERATOR) |sed 's/$(OPRNAMESPACE),$(PDBNAMESPACE),$(LRSNAMESPACE)/default/g' >temp_opr.yaml + $(KUBECTL) apply -f temp_opr.yaml + $(SLEEP) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) + $(KUBECTL) apply -f $(LREST_POD) + $(RM) temp_opr.yaml + +optnoverbs: + $(KUBECTL) apply -f $(DBOPERATORNOVERBS) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) + $(KUBECTL) apply -f $(LREST_POD) + +secretsop: + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) + +deldbop: + $(KUBECTL) delete -f $(DBOPERATOR) --ignore-not-found=true + +checklrpdbs: + $(KUBECTL) get lrpdbs -n $(PDBNAMESPACE) + + + +dump: + @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) + @$(eval DIAGFILE := ./opdmp.$(TMPSP)) + @>$(DIAGFILE) + @echo "OPERATOR DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + +dump2: + @echo "CDB LOG DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs `$(KUBECTL) get pods -n $(LRSNAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(LRSNAMESPACE) >>$(DIAGFILE) + @echo "SECRET DMP" >>$(DIAGFILE) + @echo "~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) get secrets -o yaml -n $(OPRNAMESPACE) >> $(DIAGFILE) + @echo "CDB/LRPDB DMP" >> $(DIAGFILE) + $(KUBECTL) get lrpdbs -o yaml -n $(PDBNAMESPACE) >> $(DIAGFILE) + $(KUBECTL) get lrest -o yaml -n $(LRSNAMESPACE) >> $(DIAGFILE) + @echo "CLUSTER INFO" >> $(DIAGFILE) + $(KUBECTL) get nodes -o wide + $(KUBECTL) get svc --namespace=kube-system + +cleardump: + $(RM) opdmp.* + +reloadop: + echo "RESTARTING OPERATOR" + $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) + $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) + $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) + $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + +getrolebinding: + $(KUBECTL) get rolebinding -n $(PDBNAMESPACE) + $(KUBECTL) get rolebinding -n $(LRSNAMESPACE) + +login: + $(KUBECTL) exec `$(KUBECTL) get pods -n $(LRSNAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(LRSNAMESPACE) -it -- /bin/bash + +log1: + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) + +log2: + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) + +log3: + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) + +clean: + $(KUBECTL) delete -f $(LRPDB1) + $(KUBECTL) delete -f $(LREST_POD) + +pkg: + $(MKDIR) /tmp/pkgtestplan + $(CP) -R * /tmp/pkgtestplan + rm /tmp/pkgtestplan/oracle-database-operator.yaml + $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ + $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan + rm -rf /tmp/pkgtestplan + + + +delsecrets: + @echo "delete $(LRSNAMESPACE) secrets" + $(KUBECTL) get secrets -n $(LRSNAMESPACE)|awk ' { print $$1 }'|grep -v NAME |while read sec ; do \ + $(KUBECTL) delete secrets $$sec -n $(LRSNAMESPACE) ; \ + done ; + @echo "delete $(PDBNAMESPACE) secrets" + $(KUBECTL) get secrets -n $(PDBNAMESPACE)|awk ' { print $$1 }'|grep -v NAME |while read sec ; do \ + $(KUBECTL) delete secrets $$sec -n $(PDBNAMESPACE) ; \ + done ; + $(KUBECTL) delete secrets db-ca db-tls -n $(OPRNAMESPACE) + + +dellrestcascade: + $(KUBECTL) delete lrest cdb-dev -n $(LRSNAMESPACE) + $(KUBECTL) get events -n $(LRSNAMESPACE) + $(KUBECTL) get lrest -n $(LRSNAMESPACE) + $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) + +#============================== Config Map Section =======================# + + +define genconfigmap + +echo "session_cached_cursors;100;spfile" >parameters.txt +echo "open_cursors;100;spfile" >>parameters.txt +echo "db_file_multiblock_read_count;16;spfile" >>parameters.txt +echo "test_invalid_parameter;16;spfile" >>parameters.txt + +$(KUBECTL) create configmap $(CONFIG_MAP) -n $(PDBNAMESPACE) --from-file=./parameters.txt +$(KUBECTL) describe configmap $(CONFIG_MAP) -n $(PDBNAMESPACE) + +endef + +crecfgmap: + $(call genconfigmap) + +delcfgmap: + - $(KUBECTL) delete configmap $(CONFIG_MAP) -n $(PDBNAMESPACE) + +map: delcfgmap crecfgmap + + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb1_resource.yaml new file mode 100644 index 00000000..cfc4d26f --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb1_resource.yaml @@ -0,0 +1,41 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + assertiveLrpdbDeletion: true + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb2_resource.yaml new file mode 100644 index 00000000..a9b14e08 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb2_resource.yaml @@ -0,0 +1,48 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "lrpdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "lrpdb-secret" + key: "webserver_pwd" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb3_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb3_resource.yaml new file mode 100644 index 00000000..cdc6c215 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb3_resource.yaml @@ -0,0 +1,48 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "lrpdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "lrpdb-secret" + key: "webserver_pwd" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb1_resource.yaml new file mode 100644 index 00000000..fda7b3fd --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb1_resource.yaml @@ -0,0 +1,39 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb2_resource.yaml new file mode 100644 index 00000000..fd59f37e --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb2_resource.yaml @@ -0,0 +1,39 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbName: "DB12" + cdbNamespace: "cdbnamespace" + pdbName: "pdbprd" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb3_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb3_resource.yaml new file mode 100644 index 00000000..6e01c212 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb3_resource.yaml @@ -0,0 +1,39 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/oracle-database-operator.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/oracle-database-operator.yaml new file mode 120000 index 00000000..bbf4775f --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/oracle-database-operator.yaml @@ -0,0 +1 @@ +../../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/parameters.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/parameters.txt new file mode 100644 index 00000000..836ea8da --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/parameters.txt @@ -0,0 +1,4 @@ +session_cached_cursors;100;spfile +open_cursors;100;spfile +db_file_multiblock_read_count;16;spfile +test_invalid_parameter;16;spfile diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbnamespace_binding.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbnamespace_binding.yaml new file mode 100644 index 00000000..6e0ee6cf --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbnamespace_binding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 + namespace: pdbnamespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbpwd.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbpwd.txt new file mode 100644 index 00000000..264f6ba0 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbpwd.txt @@ -0,0 +1 @@ +welcome1 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbusr.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbusr.txt new file mode 100644 index 00000000..97ce6cbb --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbusr.txt @@ -0,0 +1 @@ +welcome diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/plug_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/plug_lrpdb1_resource.yaml new file mode 100644 index 00000000..ab07de66 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/plug_lrpdb1_resource.yaml @@ -0,0 +1,50 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "plug" + fileNameConversions: "NONE" + sourceFileNameConversions: "NONE" + copyAction: "MOVE" + totalSize: "1G" + tempSize: "100M" + assertiveLrpdbDeletion: true + pdbconfigmap: "config-map-pdb" + action: "Plug" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/public.pem b/docs/multitenant/lrest-based/usecase05_to_be_removed/public.pem new file mode 100644 index 00000000..2ecc3de3 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6CyUbqQbxEzeCcDvs7PY +FAobZ9BgbK+Q4X6mPR7NhQtWecFc8OSkxSsT6Be2lx/2c+AfiYkS72mL9QLH5xZd +2LGlUPvCjH2lTYkYkstrkn0Rl6/TGA/0Udns2tIXPZ3nAaFKn6lcp1m8vpVSFWF2 +OcHLsmKUgz72LghrPjIrlEw7kG+FBIkhHP3HK5o+02lgsqiRkPopZtxY4H79qhb3 +pGr7WlCq7w2CHn89BXZUVsy3eKhMlknMYhrOK/kyEVLMuWFVKn/is8Tt6dptIGK5 +r/hEL0chbh7Yhnop1Ar8fSEk3U8Aj7eGEb4kgh3YSjuKmlIxuptFXKvez6PuU9Kn +kQIDAQAB +-----END PUBLIC KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/resetaction_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/resetaction_lrpdb1_resource.yaml new file mode 100644 index 00000000..dc148d51 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/resetaction_lrpdb1_resource.yaml @@ -0,0 +1,45 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "lrpdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "lrpdb-secret" + key: "sysadmin_pwd" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + action: "Noaction" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/server.csr b/docs/multitenant/lrest-based/usecase05_to_be_removed/server.csr new file mode 100644 index 00000000..94d10fcf --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/server.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICqDCCAZACAQAwYzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQH +DAJTWjEVMBMGA1UECgwMb3JhY2xlLCBJbmMuMSMwIQYDVQQDDBpjZGItZGV2LWxy +ZXN0LmNkYm5hbWVzcGFjZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +APFKMg3rUfwCiArrHP9OyTqePYwIzVNHEvm952DcfDYq6h2KNEIQON9hKOmFQo7f ++ktzRxen3C6XkStouU3z851kZsr9yWdhVFWWYRxgFC8Cn2o0B5/TweX8uBai7YB9 +CAd+/BvV2OhJBx/bGtPlbGT50puODuC8J3Bp6XYHuIpRcYW3VA4NBMGd0SpyL48t +7QfyWMEVttbIkC0f10nQhbzBQFtldyaFeu49NNjn4z9m/SgtQ78Rs3A8Ghept+77 +riNi7U0WCCMpUCMw57Q5ewhFITLhZ/nPCQ+1IRQFobUoZv6HWyBX8Dt2HaYCJnEP +5Z3pSwGTpW+FtTFO7WfMgo0CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQAnJ+xo +ZRHzn9D1IINnDXQPUJ2X470AScmY+XN90ciiEJlVHq5DVNg+womJ1jZoUXQidxGW +dYYw/NpKhXFeJcFK1tnNYAsiXclYbCDQQoe165i/R3SYlacJkcGcQZD7xTL7spbM +fYhvnBGfGP85VX1/YE3bObUhCHYK6sntMa4xVvBGPziPz6SgVTXuVa4UHb+XuEtm +exLkAc0PwLnADjIgyNcNrP+cdThk5qXm/5ZPqkJeZU2+QxeK/JIEvRUyStvyQf8f +7hJ4npFxXnEut8YjzE1qWASKYGcVuIsvxCr3czFRmYLgJF5bnKEghdjr93tgHllz +d/Ku6XPqTNqE1g+X +-----END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/testDelCascade.sh b/docs/multitenant/lrest-based/usecase05_to_be_removed/testDelCascade.sh new file mode 100644 index 00000000..e146e7d9 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/testDelCascade.sh @@ -0,0 +1,24 @@ +#!/bin/bash + + +#kubectl delete lrpdb lrpdb1 -n pdbnamespace 2>/dev/null +make deldbop && make dboperator + +kubectl apply -f create_lrest_pod.yaml +RUN=0 +sleep 2 +while [ $RUN -eq 0 ] +do + RUN=`kubectl get pods -n cdbnamespace | grep Running|wc -l` + sleep 1 + echo $RUN +done +echo "LREST RUNNING" + +kubectl apply -f create_lrpdb1_resource.yaml +sleep 1 +kubectl apply -f open_lrpdb1_resource.yaml + +#kubectl delete lrest cdb-dev -n cdbnamespace + + diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/tkpc_k8slrestsetup.sh b/docs/multitenant/lrest-based/usecase05_to_be_removed/tkpc_k8slrestsetup.sh new file mode 100644 index 00000000..aacb0369 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/tkpc_k8slrestsetup.sh @@ -0,0 +1,294 @@ +#!/bin/sh +# +# $Header: has/test/k8s/src/tkpc_k8slrestsetup.sh sohibane_lrest_negative_tests/3 2024/07/31 15:02:16 sohibane Exp $ +# +# tkpc_k8slrestsetup.sh +# +# Copyright (c) 2024, Oracle and/or its affiliates. +# +# NAME +# tkpc_k8slrestsetup.sh - script to be used to setup lrest in k8s env +# +# DESCRIPTION +# script to be used to setup lrest in k8s env +# +# NOTES +# +# +# MODIFIED (MM/DD/YY) +# prrastog 06/04/24 - Creation +# +alias k=kubectl +export KUBECONFIG=$HOME/.kube/config + +lrest_path=./ +lrest_path2=./ +cert_path="./cert_path" +operator_yaml="oracle-database-operator.yaml" + +while getopts ':o:' opt; do + case "$opt" in + o) operator_yaml=${OPTARG};; + esac +done +echo using operator file: $operator_yaml + +kubectl get all -n rac + +execute(){ + echo -e "\nExecuting: $1 \n" + $1 +} + +prerequisites(){ +echo "####### setting up prerequisites #######" +# create namespace cdbnamespace, pdbnamespace +create_ns1="kubectl create ns cdbnamespace" +create_ns2="kubectl create ns pdbnamespace" +execute "$create_ns1" +execute "$create_ns2" + +} + +step1(){ + +echo "######## STEP 1: create cert manager ###########" +cert_manager="kubectl apply -f $lrest_path/cert-manager.yaml" +#echo -e "\nExecuting: $cert_manager \n" +echo "BEGIN_IGNORE" +execute "$cert_manager" +#$cert_manager +echo "END_IGNORE" +sleep 300 +} + +step2(){ +echo "########## STEP 2: create operator pod ############" +operator_pod="kubectl apply -f $lrest_path2/$operator_yaml" +#echo -e "\nExecuting: $operator_pod \n" +echo "BEGIN_IGNORE" +execute "$operator_pod" +#$operator_pod +echo "END_IGNORE" +sleep 30 + +echo "######### STEP 2.1: check operator pods #########" +check_cmd="kubectl get pods -n oracle-database-operator-system" +#echo -e "\nExecuting: $check_cmd \n" +execute "$check_cmd" + +echo "############ STEP 2.2: create rolebindings ##############" +cdb_bind="kubectl apply -f $lrest_path2/cdbnamespace_binding.yaml" +pdb_bind="kubectl apply -f $lrest_path2/pdbnamespace_binding.yaml" +execute "$cdb_bind" +execute "$pdb_bind" +check_cdb_bind="kubectl get rolebinding -n cdbnamespace" +check_pdb_bind="kubectl get rolebinding -n pdbnamespace" +execute "$check_cdb_bind" +execute "$check_pdb_bind" +} + +step3(){ +# NOTE: only required when running on env the first time on new env, or if certificates are deleted +echo "########## STEP 3: generate certificates ############" +echo BEGIN_IGNORE +mkdir $cert_path/certificates +SCRT=lrest_server.crt +CANAME=ca +SKEY=lrest_server.key +REST_SERVER=lrest.cdbnamespace +SCSR=lrest_server.csr +COMPANY=oracle + +openssl genrsa -out $cert_path/certificates/$CANAME.key 2048 +sleep 2 +openssl req -new -x509 -days 365 -key $cert_path/certificates/$CANAME.key -subj "/C=CN/ST=GD/L=SZ/O=$COMPANY,Inc./CN=$COMPANY Root CA" -out $cert_path/certificates/$CANAME.crt +sleep 2 +openssl req -newkey rsa:2048 -nodes -keyout $cert_path/certificates/$SKEY -subj "/C=CN/ST=GD/L=SZ/O=$COMPANY,Inc./CN=cdb-dev-$REST_SERVER" -out $cert_path/certificates/$SCSR +sleep 2 +echo "subjectAltName=DNS:cdb-dev-$REST_SERVER,DNS:www.example.com" > $cert_path/certificates/extfile.txt +sleep 2 +openssl x509 -req -extfile $cert_path/certificates/extfile.txt -days 365 -in $cert_path/certificates/$SCSR -CA $cert_path/certificates/$CANAME.crt -CAkey $cert_path/certificates/$CANAME.key -CAcreateserial -out $cert_path/certificates/$SCRT +sleep 2 +openssl rsa -in $cert_path/certificates/$CANAME.key -outform PEM -pubout -out $cert_path/certificates/public.pem +echo END_IGNORE +} + +step4(){ + +echo "########## STEP 4: store certificates - create secrets ############" + +#echo "Executing: kubectl create secret tls db-tls --key="$cert_path/certificates/lrest_server.key" --cert="$cert_path/certificates/lrest_server.crt" -n oracle-database-operator-system" +#kubectl create secret tls db-tls --key="$cert_path/certificates/lrest_server.key" --cert="$cert_path/certificates/lrest_server.crt" -n oracle-database-operator-system +#echo "Executing: kubectl create secret generic db-ca --from-file="$cert_path/certificates/ca.crt" -n oracle-database-operator-system" +#kubectl create secret generic db-ca --from-file="$cert_path/certificates/ca.crt" -n oracle-database-operator-system + +secret_cdbns1="kubectl create secret tls db-tls --key=$cert_path/certificates/lrest_server.key --cert=$cert_path/certificates/lrest_server.crt -n cdbnamespace" +secret_cdbns2="kubectl create secret generic db-ca --from-file=$cert_path/certificates/ca.crt -n cdbnamespace" +secret_pdbns1="kubectl create secret tls db-tls --key=$cert_path/certificates/lrest_server.key --cert=$cert_path/certificates/lrest_server.crt -n pdbnamespace" +secret_pdbns2="kubectl create secret generic db-ca --from-file=$cert_path/certificates/ca.crt -n pdbnamespace" +secret_cdbns3="kubectl create secret tls prvkey --key=$cert_path/certificates/ca.key --cert=$cert_path/certificates/ca.crt -n cdbnamespace" +secret_cdbns4="kubectl create secret generic pubkey --from-file=publicKey=$cert_path/certificates/public.pem -n cdbnamespace" + +execute "$secret_cdbns1" +execute "$secret_cdbns2" +execute "$secret_pdbns1" +execute "$secret_pdbns2" +execute "$secret_cdbns3" +execute "$secret_cdbns4" +sleep 10; +echo "Executing: kubectl get secrets -n oracle-database-operator-system" +kubectl get secrets -n oracle-database-operator-system +check_cdbns="kubectl get secrets -n cdbnamespace" +check_pdbns="kubectl get secrets -n pdbnamespace" +execute "$check_cdbns" +execute "$check_pdbns" +# create dbsecrets +#lrest_secret="kubectl apply -f $lrest_path2/create_lrest_secret.yaml -n cdbnamespace" +#lrpdb_secret="kubectl apply -f $lrest_path2/create_lrpdb_secret.yaml -n pdbnamespace" +#execute "$lrest_secret" +#execute "$lrpdb_secret" + +} + +step4a(){ + +echo "############ create secret cred ##############" + +secret_del1="kubectl delete secret prvkey -n cdbnamespace" +secret_del2="kubectl delete secret pubkey -n cdbnamespace" + +execute "$secret_del1" +execute "$secret_del2" + +echo "restdba" > $cert_path/certificates/dbuser.txt +echo "CLWKO655321" > $cert_path/certificates/dbpass.txt +echo "welcome" > $cert_path/certificates/wbuser.txt +echo "welcome1" > $cert_path/certificates/wbpass.txt +echo "welcome" > $cert_path/certificates/pdbusr.txt +echo "welcome1" > $cert_path/certificates/pdbpwd.txt + +secretcred_cdbns1="kubectl create secret generic prvkey --from-file=privateKey=$cert_path/certificates/ca.key -n cdbnamespace" +secretcred_cdbns2="kubectl create secret generic pubkey --from-file=publicKey=$cert_path/certificates/public.pem -n cdbnamespace" + +execute "$secretcred_cdbns1" +execute "$secretcred_cdbns2" + +/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/dbuser.txt |base64 > $cert_path/certificates/e_dbuser.txt +/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/dbpass.txt |base64 > $cert_path/certificates/e_dbpass.txt +/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/wbuser.txt |base64 > $cert_path/certificates/e_wbuser.txt +/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/wbpass.txt |base64 > $cert_path/certificates/e_wbpass.txt +/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/pdbusr.txt |base64 > $cert_path/certificates/e_pdbusr.txt +/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/pdbpwd.txt |base64 > $cert_path/certificates/e_pdbpwd.txt + +secretcred_cdbns3="kubectl create secret generic dbuser --from-file=$cert_path/certificates/e_dbuser.txt -n cdbnamespace" +secretcred_cdbns4="kubectl create secret generic dbpass --from-file=$cert_path/certificates/e_dbpass.txt -n cdbnamespace" +secretcred_cdbns5="kubectl create secret generic wbuser --from-file=$cert_path/certificates/e_wbuser.txt -n cdbnamespace" +secretcred_cdbns6="kubectl create secret generic wbpass --from-file=$cert_path/certificates/e_wbpass.txt -n cdbnamespace" +secretcred_pdbns1="kubectl create secret generic wbuser --from-file=$cert_path/certificates/e_wbuser.txt -n pdbnamespace" +secretcred_pdbns2="kubectl create secret generic wbpass --from-file=$cert_path/certificates/e_wbpass.txt -n pdbnamespace" +secretcred_pdbns3="kubectl create secret generic pdbusr --from-file=$cert_path/certificates/e_pdbusr.txt -n pdbnamespace" +secretcred_pdbns4="kubectl create secret generic pdbpwd --from-file=$cert_path/certificates/e_pdbpwd.txt -n pdbnamespace" +secretcred_pdbns5="kubectl create secret generic prvkey --from-file=privateKey=$cert_path/certificates/ca.key -n pdbnamespace" + +execute "$secretcred_cdbns3" +execute "$secretcred_cdbns4" +execute "$secretcred_cdbns5" +execute "$secretcred_cdbns6" +execute "$secretcred_pdbns1" +execute "$secretcred_pdbns2" +execute "$secretcred_pdbns3" +execute "$secretcred_pdbns4" +execute "$secretcred_pdbns5" +sleep 10; + +check_sc_cdbns="kubectl get secrets -n cdbnamespace" +check_sc_pdbns="kubectl get secrets -n pdbnamespace" +execute "$check_sc_cdbns" +execute "$check_sc_pdbns" + +} + +step5(){ + + echo "############ create lrest pod ##############" + create_lrest_pod="kubectl apply -f $lrest_path2/create_lrest_pod.yaml" + execute "$create_lrest_pod" + sleep 120 + echo "############## check lrest pod creation #############" + check_lrest_pod=`kubectl get pods -n cdbnamespace |grep lrest` + echo $check_lrest_pod + #execute "$check_lrest_pod" + echo "############### check lrest pod creation log ##########" + echo "BEGIN_IGNORE" + check_log_lrest_pod="kubectl logs `kubectl get pods -n cdbnamespace |grep lrest|cut -d ' ' -f 1` -n cdbnamespace" + execute "$check_log_lrest_pod" + echo "END_IGNORE" + +} + +step5a(){ +echo "############## STEP 5a: create db service and lrest user restdba #############" +kubectl exec -it racnode1-0 -n rac -- su - oracle -c 'srvctl add service -d ORCLCDB -s lrest -r ORCLCDB1,ORCLCDB2; srvctl start service -d ORCLCDB -s lrest; srvctl status service -d orclcdb -s lrest' + +kubectl exec -it racnode1-0 -n rac -- su - oracle -c "export ORACLE_SID=ORCLCDB1; sqlplus -S / as sysdba < $cert_path/certificates/e_dbuser.txt +#/usr/bin/openssl pkeyutl -encrypt -pubin -inkey /home/opc/rack8s/operator/lrest/certificates/public.pem -in /home/opc/rack8s/operator/lrest/certificates/dbuser.txt > /home/opc/rack8s/operator/lrest/certificates/e_dbuser.txt diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.crt b/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.crt new file mode 100644 index 00000000..dc409c50 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDgjCCAmqgAwIBAgIUHe1aTiQOytE4GraYPByqLUVoibswDQYJKoZIhvcNAQEL +BQAwVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEVMBMG +A1UECgwMb3JhY2xlLCBJbmMuMRcwFQYDVQQDDA5vcmFjbGUgUm9vdCBDQTAeFw0y +NDEwMjQyMDQyNTRaFw0yNTEwMjQyMDQyNTRaMGMxCzAJBgNVBAYTAkNOMQswCQYD +VQQIDAJHRDELMAkGA1UEBwwCU1oxFTATBgNVBAoMDG9yYWNsZSwgSW5jLjEjMCEG +A1UEAwwaY2RiLWRldi1scmVzdC5jZGJuYW1lc3BhY2UwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDxSjIN61H8AogK6xz/Tsk6nj2MCM1TRxL5vedg3Hw2 +KuodijRCEDjfYSjphUKO3/pLc0cXp9wul5EraLlN8/OdZGbK/clnYVRVlmEcYBQv +Ap9qNAef08Hl/LgWou2AfQgHfvwb1djoSQcf2xrT5Wxk+dKbjg7gvCdwael2B7iK +UXGFt1QODQTBndEqci+PLe0H8ljBFbbWyJAtH9dJ0IW8wUBbZXcmhXruPTTY5+M/ +Zv0oLUO/EbNwPBoXqbfu+64jYu1NFggjKVAjMOe0OXsIRSEy4Wf5zwkPtSEUBaG1 +KGb+h1sgV/A7dh2mAiZxD+Wd6UsBk6VvhbUxTu1nzIKNAgMBAAGjOjA4MDYGA1Ud +EQQvMC2CGmNkYi1kZXYtbHJlc3QuY2RibmFtZXNwYWNlgg93d3cuZXhhbXBsZS5j +b20wDQYJKoZIhvcNAQELBQADggEBAFSQWoBQWwTVqRkyQZ7XAg0wEH3WL9GTFdCB +2i32mfA0at61U0QgGp8otCK2JkwAEdmPEG4kho1ITfsP1v2Mc0QyxYbNlYaNyLzn +m4eikTAJ7IvB5ArH8sz4O/+xLmUMBliSKVWZ1f1ukINgay/EJxfgHygdEJ3FAHFR +RxDMHWZ8smLTK2pTGEwpSRkXIrDDp3KftPNpd2meNz0ljdJAQUSkRwLOdW0SPAPX +JgNYZrcN+UjF22uci1Z8dzrV5/8pR+DynFXWpWvVHzOwXb4ad3Wepz+4LdqlmAXK +gD0B+dpwk8n4sDwiq5myIeV0Bng+BcNYYvSyzJFkGN3yh4JdD3c= +-----END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.key b/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.key new file mode 100644 index 00000000..ce373e9c --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDxSjIN61H8AogK +6xz/Tsk6nj2MCM1TRxL5vedg3Hw2KuodijRCEDjfYSjphUKO3/pLc0cXp9wul5Er +aLlN8/OdZGbK/clnYVRVlmEcYBQvAp9qNAef08Hl/LgWou2AfQgHfvwb1djoSQcf +2xrT5Wxk+dKbjg7gvCdwael2B7iKUXGFt1QODQTBndEqci+PLe0H8ljBFbbWyJAt +H9dJ0IW8wUBbZXcmhXruPTTY5+M/Zv0oLUO/EbNwPBoXqbfu+64jYu1NFggjKVAj +MOe0OXsIRSEy4Wf5zwkPtSEUBaG1KGb+h1sgV/A7dh2mAiZxD+Wd6UsBk6VvhbUx +Tu1nzIKNAgMBAAECggEAQwPSZx9Gg/HICxBF23rsVMWCXpRLQqfo00g1LSfPr9O3 +dkDeRsLuqMv5avJCDCCVCcOYqS9L7fzWqvHJFad9r+ckzZgzCe10WNkNW64rrOrT +j9GnlHJh1hicoFzcDeWsBzjzmF+KgOlXezeFefisZFcmcWHBLBXV9ljUKJ34WSG/ +j0NDi+yqn20keOiPXF+2RG28qDmsI5T3TNLnFwkcKUXVyiJRa/kXonZfGeOFpe3o +QBTrvb6iEzTMZEV+MuWI+1kH4tBo4CQy/yXz+RWsiPbXTqe0vVCMYTt7y6tLWTUI +bRnfzK29uzZXCBi9zOErzMIpsqt/nLv4jXk2NeB/wQKBgQD7MIyM1fD/rrrwtavx +UDnO5DGnBDkFiZBV1TE+U89XHo6+TK+VNuSVtmBCj6PgkVO8xqTBezNvbG0iMeR1 +JVYELqiKMPyAVBmnE5fmvNLdeOtkxu4KFc5sSbgtlSGyOUwIsoT/TuIhVCIb/5xW +F+/O9qhD/fUO8eqiNZVqPxevdQKBgQD16RzmOn9Vt7ljZReWeQZAdG0NWowga7lM +Z7WcHLwxibg3oRAgPLjlDkKxwU9+V4BGrpbw/63GMnY6S2bFC+wZnWNW4DzO8FKI +3NcKQy9KoJos3AeSXVTyX1Hp6mlOziHSze6LGEt/LFrsNbBbSBDOFQNVpoZH1t0+ +OZx85iv7uQKBgH85xdaHffti2tiUi7Q7+CusNOHtJ5yAYpS22KxC8t+jYJVObhhb +p0eVCghsjIpv0UPqMQS2jATBnD0XnCkhcgHISCFGUn4FGsEfEW8S8JKwbjmq8GI3 +TUhXxmt//4Mvti0pjmdp4usSm/wmaV7J3PKsx+k4NekN6tOh5D3eGvcJAoGBANey +kvvLAdghIEbz7F27aGVu+hzFeLYTZ2SCb4Uwm05ke6an6yoruQ6JlKywYFb0z3qw +N4vK6RU5PATkEI/0ZjMNn44bgJsPp4WPGDxTNA8kkjeZBDc5EILMY2Bnkdli674p +jfyJ4eb59ALPERLwQnVRbyxYtd4btNKY3A+eIaRZAoGBAI+K1WQeHhGsnDNB1Um+ +g/2uiZH/oK2Dx9iTJsKvXgoikDmWS+o527wdu2oD6/9GpjaMsvzyuL5Z48iYkYEg +3V097YAaCNLkfCm1zaUgs6cm48jivvSwrXQH0FI+z8dTyuFjDQJdDjVwup0V15k8 +XdMPGYZCX6Jrv9StTi+TWCz2 +-----END PRIVATE KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/unplug_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/unplug_lrpdb1_resource.yaml new file mode 100644 index 00000000..691b852c --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/unplug_lrpdb1_resource.yaml @@ -0,0 +1,42 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: lrpdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "Unplug" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/wbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/wbpass.txt new file mode 100644 index 00000000..264f6ba0 --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/wbpass.txt @@ -0,0 +1 @@ +welcome1 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/wbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/wbuser.txt new file mode 100644 index 00000000..97ce6cbb --- /dev/null +++ b/docs/multitenant/lrest-based/usecase05_to_be_removed/wbuser.txt @@ -0,0 +1 @@ +welcome diff --git a/docs/multitenant/NamespaceSeg.md b/docs/multitenant/ords-based/NamespaceSeg.md similarity index 100% rename from docs/multitenant/NamespaceSeg.md rename to docs/multitenant/ords-based/NamespaceSeg.md diff --git a/docs/multitenant/ords-based/README.md b/docs/multitenant/ords-based/README.md new file mode 100644 index 00000000..0a7221ff --- /dev/null +++ b/docs/multitenant/ords-based/README.md @@ -0,0 +1,252 @@ + + +# Oracle Multitenant Database Controllers + +The Oracle Database Operator for kubernetes uses two controllers to manage [Pluggable Database life cycle][oradocpdb] + +- CDB controller +- PDB controller + +By usigng CDB/PDB controllers you can perform the following actions **CREATE**,**MODIFY(OPEN/COSE)**,**DELETE**,**CLONE**,**PLUG** and **UNPLUG** against pluggable database + +Examples are located under the following directories: + +- [Usecase](./usecase/) This directory contains an init files where you can specify all the details of your environment and a makefile which reads the initfile and generates the all the yaml files. No need to edit yaml files one by one with your system information. +- [Singlenamespace provisioning](./provisioning/singlenamespace/) You will find base sample files to manage pdb and cdb within a single namespace. File editing is required. +- [Multinamespace provisioning](./provisioning/multinamespace/) You will find base sample files to manage pdb and cdb in different namespaces. +- [Usecase01](./usecase01/README.md) [Usecase02](./usecase02/README.md) and [Usecase03](./usecase03/README.md) contain other step by step examples. File editing is required. + +**NOTE** that the cdb controller is not intended to manage the container database. The cdb controller just provied a pod with a rest server connected to the container database. + + +## Macro steps for setup + +- Deply the Oracle Database Operator +- [Create Ords based image for CDB pod](./provisioning/ords_image.md) +- [Container RDBMB user creation](#prepare-the-container-database-for-pdb-lifecycle-management-pdb-lm) +- Create certificates for https connection +- Create secrets for credentials and certificates +- Create CDB pod using the Ords based image + +## Oracle DB Operator Multitenant Database Controller Deployment + +To deploy OraOperator, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. + +Once the **Oracle Database Operator** is deployed, you can see the DB Operator Pods running in the Kubernetes Cluster. The multitenant controllers are deployed as part of the `OraOperator` deploymentd. You can see the CRDs (Custom Resource Definition) for the CDB and PDBs in the list of CRDs. The following output is an example of such a deployment: + +```bash +[root@test-server oracle-database-operator]# kubectl get ns +NAME STATUS AGE +cert-manager Active 32h +default Active 245d +kube-node-lease Active 245d +kube-public Active 245d +kube-system Active 245d +oracle-database-operator-system Active 24h <---- namespace to deploy the Oracle Database Operator + +[root@test-server oracle-database-operator]# kubectl get all -n oracle-database-operator-system +NAME READY STATUS RESTARTS AGE +pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 0 28s +pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 0 28s +pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 0 28s + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 29s +service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 29s + +NAME READY UP-TO-DATE AVAILABLE AGE +deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 29s + +NAME DESIRED CURRENT READY AGE +replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 29s +[root@docker-test-server oracle-database-operator]# + +[root@test-server oracle-database-operator]# kubectl get crd +NAME CREATED AT +autonomouscontainerdatabases.database.oracle.com 2022-06-22T01:21:36Z +autonomousdatabasebackups.database.oracle.com 2022-06-22T01:21:36Z +autonomousdatabaserestores.database.oracle.com 2022-06-22T01:21:37Z +autonomousdatabases.database.oracle.com 2022-06-22T01:21:37Z +cdbs.database.oracle.com 2022-06-22T01:21:37Z <---- +certificaterequests.cert-manager.io 2022-06-21T17:03:46Z +certificates.cert-manager.io 2022-06-21T17:03:47Z +challenges.acme.cert-manager.io 2022-06-21T17:03:47Z +clusterissuers.cert-manager.io 2022-06-21T17:03:48Z +dbcssystems.database.oracle.com 2022-06-22T01:21:38Z +issuers.cert-manager.io 2022-06-21T17:03:49Z +oraclerestdataservices.database.oracle.com 2022-06-22T01:21:38Z +orders.acme.cert-manager.io 2022-06-21T17:03:49Z +pdbs.database.oracle.com 2022-06-22T01:21:39Z <--- +shardingdatabases.database.oracle.com 2022-06-22T01:21:39Z +singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z +``` + + +## Prerequsites to manage PDB Life Cycle using Oracle DB Operator Multitenant Database Controller + +* [Prepare the container database for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) +* [Oracle REST Data Service or ORDS Image](#oracle-rest-data-service-ords-image) +* [Kubernetes Secrets](#kubernetes-secrets) +* [Kubernetes CRD for CDB](#kubernetes-crd-for-cdb) +* [Kubernetes CRD for PDB](#kubernetes-crd-for-pdb) + +## Prepare the container database for PDB Lifecycle Management (PDB-LM) + +Pluggable Database (PDB) management operations are performed in the Container Database (CDB). These operations include create, clone, plug, unplug, delete, modify and map pdb. + +You cannot have an ORDS-enabled schema in the container database. To perform the PDB lifecycle management operations, you must first use the following steps to define the default CDB administrator credentials on target CDBs: + +Create the CDB administrator user, and grant the required privileges. In this example, the user is `C##DBAPI_CDB_ADMIN`. However, any suitable common user name can be used. + +```SQL +SQL> conn /as sysdba + +-- Create following users at the database level: + +ALTER SESSION SET "_oracle_script"=true; +DROP USER C##DBAPI_CDB_ADMIN cascade; +CREATE USER C##DBAPI_CDB_ADMIN IDENTIFIED BY CONTAINER=ALL ACCOUNT UNLOCK; +GRANT SYSOPER TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; +GRANT SYSDBA TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; +GRANT CREATE SESSION TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; + + +-- Verify the account status of the following usernames. They should not be in locked status: + +col username for a30 +col account_status for a30 +select username, account_status from dba_users where username in ('ORDS_PUBLIC_USER','C##DBAPI_CDB_ADMIN','APEX_PUBLIC_USER','APEX_REST_PUBLIC_USER'); +``` + +## OCI OKE(Kubernetes Cluster) + +You can use an [OKE in Oracle Cloud Infrastructure][okelink] to configure the controllers for PDB lifecycle management. **Note that there is no restriction about container database location; it can be anywhere (on cloud or on-premises).** +To quickly create an OKE cluster in your OCI cloud environment you can use the following [link](./provisioning/quickOKEcreation.md). +In this setup example [provisioning example setup](./provisioning/example_setup_using_oci_oke_cluster.md), the Container Database is running on a OCI Exadata Database Cluster. + + +## Oracle REST Data Service (ORDS) Image + + The PDB Database controllers require a pod running a dedicated rest server image based on [ORDS][ordsdoc]. Read the following [link](./provisioning/ords_image.md) to build the ords images. + + +## Kubernetes Secrets + + Multitenant Controllers use Kubernetes Secrets to store the required credential. The https certificates are stored in Kubernetes Secrets as well. + + **Note** In multi namespace enviroment you have to create specific secrets for each namespaces + +### Secrets for CDB CRD + + Create a secret file as shown here: [config/samples/multitenant/cdb_secret.yaml](./provisioning/singlenamespace/cdb_secret.yaml). Modify this file with the `base64` encoded values of the required passwords for CDB, and use this file to create the required secrets. + + ```bash + kubectl apply -f cdb_secret.yaml + ``` + + **Note:** To obtain the `base64` encoded value for a password, use the following command: + + ```bash + echo -n "" | base64 + ``` + + **Note:** After successful creation of the CDB Resource, the CDB secrets ca be deleted from the Kubernetes system . + +### Secrets for PDB CRD + + Create a secret file as shown here: [pdb_secret.yaml](./provisioning/singlenamespace/pdb_secret.yaml). Edit the file using your base64 credential and apply it. + + ```bash + kubectl apply -f pdb_secret.yaml + ``` + + **NOTE:** Don't leave plaintext files containing sensitive data on disk. After loading the Secret, remove the plaintext file or move it to secure storage. + +### Secrets for CERTIFICATES + +Create the certificates and key on your local host, and use them to create the Kubernetes secret. + +```bash +openssl genrsa -out ca.key 2048 +openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost Root CA " -out ca.crt +openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost" -out server.csr +echo "subjectAltName=DNS:cdb-dev-ords,DNS:www.example.com" > extfile.txt +openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt +``` + +```bash +kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system +kubectl create secret generic db-ca --from-file=ca.crt -n oracle-database-operator-system +``` + +image_not_found + +**Note:** On successful creation of the certificates secret creation remove files or move to secure storage . + +## Kubernetes CRD for CDB + +The Oracle Database Operator Multitenant Controller creates the CDB kind as a custom resource that models a target CDB as a native Kubernetes object. This kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. Each CDB resource follows the CDB CRD as defined here: [config/crd/bases/database.oracle.com_cdbs.yaml](../../../config/crd/bases/database.oracle.com_cdbs.yaml) + +To create a CDB CRD, see this example`.yaml` file: [cdb_create.yaml](../multitenant/provisioning/singlenamespace/cdb_create.yaml) + +**Note:** The password and username fields in this *cdb.yaml* Yaml are the Kubernetes Secrets created earlier in this procedure. For more information, see the section [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). To understand more about creating secrets for pulling images from a Docker private registry, see [Kubernetes Private Registry Documenation]( https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). + +Create a CDB CRD Resource example + +```bash +kubectl apply -f cdb_create.yaml +``` + +see [usecase01][uc01] and usecase03[uc03] for more information about file configuration + +## Kubernetes CRD for PDB + +The Oracle Database Operator Multitenant Controller creates the PDB kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. You cannot have more than one Kubernetes resource for a target PDB. This PDB resource can be used to perform PDB-LM operations by specifying the action attribute in the PDB Specs. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) + +Yaml file [pdb_create.yaml](../multitenant/provisioning/singlenamespace/pdb_create.yaml) to create a pdb + +```bash +kubectl apply -f cdb_create.yaml +``` + +## Usecases files list + +### Single Namespace + +1. [Create CDB](./provisioning/singlenamespace/cdb_create.yaml) +2. [Create PDB](./provisioning/singlenamespace/pdb_create.yaml) +3. [Clone PDB](./provisioning/singlenamespace/pdb_clone.yaml) +4. [Open PDB](./provisioning/singlenamespace/pdb_open.yaml) +4. [Close PDB](./provisioning/singlenamespace/pdb_close.yaml) +5. [Delete PDB](./provisioning/singlenamespace/pdb_delete.yaml) +6. [Unplug PDB](./provisioning/singlenamespace/pdb_unplug.yaml) +7. [Plug PDB](./provisioning/singlenamespace/pdb_plug.yaml) + +### Multiple namespace (cdbnamespace,dbnamespace) + +1. [Create CDB](./provisioning/multinamespace/cdb_create.yaml) +2. [Create PDB](./provisioning/multinamespace/pdb_create.yaml) +3. [Clone PDB](./provisioning/multinamespace/pdb_clone.yaml) +4. [Open PDB](./provisioning/multinamespace/pdb_open.yaml) +4. [Close PDB](./provisioning/multinamespace/pdb_close.yaml) +5. [Delete PDB](./provisioning/multinamespace/pdb_delete.yaml) +6. [Unplug PDB](./provisioning/multinamespace/pdb_unplug.yaml) + +## Known issues + + - Ords installatian failure if pluaggable databases in the container db are not opened + + - Version 1.1.0: encoded password for https authentication may include carriege return as consequence the https request fails with http 404 error. W/A generate encoded password using **printf** instead of **echo**. + + - pdb controller authentication suddenly failes without any system change. Check the certificate expiration date **openssl .... -days 365** + + - Nothing happens after cdb yaml file applying: Make sure to have properly configure the WHATCH_NAMESPACE list in the operator yaml file + + [okelink]:https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm + [ordsdoc]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.1/index.html + [uc01]:../multitenant/usecase01/README.md + [uc02]:../multitenant/usecase02/README.md + [uc03]:../multitenant/usecase03/README.md + [oradocpdb]:https://docs.oracle.com/en/database/oracle/oracle-database/21/multi/introduction-to-the-multitenant-architecture.html#GUID-AB84D6C9-4BBE-4D36-992F-2BB85739329F + + \ No newline at end of file diff --git a/docs/multitenant/images/K8S_NAMESPACE_SEG.png b/docs/multitenant/ords-based/images/K8S_NAMESPACE_SEG.png similarity index 100% rename from docs/multitenant/images/K8S_NAMESPACE_SEG.png rename to docs/multitenant/ords-based/images/K8S_NAMESPACE_SEG.png diff --git a/docs/multitenant/images/K8S_SECURE1.png b/docs/multitenant/ords-based/images/K8S_SECURE1.png similarity index 100% rename from docs/multitenant/images/K8S_SECURE1.png rename to docs/multitenant/ords-based/images/K8S_SECURE1.png diff --git a/docs/multitenant/images/K8S_SECURE2.png b/docs/multitenant/ords-based/images/K8S_SECURE2.png similarity index 100% rename from docs/multitenant/images/K8S_SECURE2.png rename to docs/multitenant/ords-based/images/K8S_SECURE2.png diff --git a/docs/multitenant/images/K8S_SECURE3.png b/docs/multitenant/ords-based/images/K8S_SECURE3.png similarity index 100% rename from docs/multitenant/images/K8S_SECURE3.png rename to docs/multitenant/ords-based/images/K8S_SECURE3.png diff --git a/docs/multitenant/images/K8S_SECURE4.png b/docs/multitenant/ords-based/images/K8S_SECURE4.png similarity index 100% rename from docs/multitenant/images/K8S_SECURE4.png rename to docs/multitenant/ords-based/images/K8S_SECURE4.png diff --git a/docs/multitenant/ords-based/images/makerunall.png b/docs/multitenant/ords-based/images/makerunall.png new file mode 100644 index 0000000000000000000000000000000000000000..ab856f90ca3a29bbaece7f4d80b0056801243613 GIT binary patch literal 211874 zcmdSB2T+r17cLq_K|w^6CJG3sAfSk10fZ=31Zg52A|TR1sY*#yq)11kC?y~u(xijb zNLNvMM|zds2_?x{5%>P=5$3-}|ohtfzc0Rg~mtsadHJ2n4PC zO=&d*V$WFwg5v8wN_gcmv4tM~wZ~pkUVR_@&u!m*ANcvGgN&BL9oq*E&W3g-2vZwd zYm>9~#&#wqHuh$=4)YW^2?XKE~ueVPCp~pW2LVZXQ=XbW~iAM?&MEi|sWpe5AC9d;|Wf#5+x!;1?ehh5ZwD zSMbsm9pU1lq-VJyqiTllLmsxUDlSQVYjgd_d@hrvW9P@Rbz5X=YRQLGjYIS?8oZxb zj2jakzAHcYOsJN{*k7pQwnjK_b?q({-m|KQ;pg8k)^;hcDCxW~&)>%*Ifx>1z3FbC z#kcF;@<)#D#yS>=AC42MiV&1*@}#NUdV0}xu*k(Rv-ZS$OSeWpj7|v?F$hs&#W@s`Ec#070PQHH*S2=)G`we?K#H6-pk=}TBM?*pQ)|Gx1e^M zo#xf*!Me}a_b@YDpK*#GA2w2PbrqDYiM%$T6h-O%XylOHFEx~RCHdRevg95LC9#P( z1YR;wWT7^}5ht4ph77CNes1HORktu=aSoexE^WJ1#F~gruZD8UJAz{Zyk=&~4|@D{ zrGigqOm$Y4_ZVmy(#%&6sjN-YA>sm^c(;0oHJA0zqj48cA6GZ7+$E;?)6(OmYsUFU zSVpggR~-Jv56|yzh^?tVdp1(-jt4WTlXSS{H3ijhC(7+v1c$ifLqpCP)Z-h1IJ?2? zdkCwUk2jjR@mLAku&Ah+VhYj{Gk&JWGx!~sqNPP;roG)gF|*)co4!mAgH60K`8C_Q zK96}UanCf7aND;rdX3`g0FkiJpG_fX)8}g*5D@9Ke$K0G<22&Bp>+WLe7Rprp^afr zUkOXpo0L{`r-=QbXl&4c@D=Wr2DZ|b#kwqwv|R{7Y3U)Q7lY4tBMzO*COIlKAeZFi zXzFSE*tyz~aNnw2x19EDZ#3e6l)3KvUVqWtZD*N{{RvB7#LNx%`u7SU1gpysmSHhj zT{`A8N$yDA+1YsYGls^-w3e28)6*HV3MkX9N0D)@PE9B9SW!>7gL(%JRF7%r-ZNPb zDwZuLNxc5}@eAES*>=}?LoK&bgQKvU>H=naLpWIW@rP0Oi#dNw+cuRlGFCmz?49dD z8DCa(RM4jPbYfW+T3qk_?W&|-t*Q+4yOQ@GS*WZHI9+3^v?WFxi{xMn;uwjJU_KXk z%-efx4Dl3m>vm1t0Fg;^yg>%7awyMvzH0}mlAkw~>trw(22am;T&7@YOA7)) z$ItgrC;mFT5iyu2`K*QNn{TtJP>^0_|yBSGqwv;0gQyd7E(tEqU9Xb3EuFoN_ zU>K`*=k^r$&cR_Z#tG!u(5dnwJk{LpPMvUYaEQyur%0MFh@U<8^zz`4~#Xeh%-#EW!liL_I<&WqT=SxyjPM2*sME4**;0rNQ zpSyFj9@#x#o72}8YGV`kr7|%=JWXBim%3-b{*f3&dKM@bbbbXzVjM_L9GpXus1YB2 z=I!qna~j-gPcy0O>{~;otgcyaFCa-gh`8B#jGThWDTa;?eDVN^J%YG_ZC+WXP_Ku3 zVLR9F`Rxa7kZoUOP>J*O>pNw}ROnTEk?<28$e5`>wknU77OVk>ICY^X%~=iPpSqDf z5wsE1McQ~1K+VsurL8++c$;btO{EfLUbC{kD%0ACdR&roOyW3o(%~OS%+TJaRHsh4 z@az>;zKZzlk)|1+mGeBto9eBfpGnQaf;Y#D&a<5#Z^WxtHuSLt*$#NYO^dO6t8nYy z$jpHxKN>h|-+Yz*wYIPh^AoyPWA9J=+HaZ9`s~RQ%2xJ);=QYpLXQ{A)(2S__Q1Ol zgQ|}CGx%yPv8z67%DTqfh{44ptN8;sx+v_(y}#<-fe=U6eY;e&j*?{dBPPvky$f#p z^ZV^Yq21yqN1MN@*yr3FEgKy)jK`(6)VqR;J5v%R=I|h;Pw?SaNTu2wmI*b zv-40#U<0lMNt&7Z)QFT*klEX?g(vT+;Yb$V^!vz`#9oKZlXST7u0daFHh%M@slI7>+=b4pF8C>w>EC{w(YUL6AEkKlaKnA zkjj^L(_$zmQcRc+v8#^IV=~ujB-aU(R;EUZ5bKZ11OtmCvFnv_~TzbmzC7ajj>hdfQ&iYi8DdSs$pI zl=5C!&n!QTzH#5^iOA*?SDI-rlG_rWE&k!P@+Wt4Fn_fHp*v@Me3X!n6eJ@x{GKn4 zJj%>cxt4G^OM|ddoWM5uc+ZeVh^Z=ohUdiza*w#h%QB@5{T)NAK0Vr3q1 zn}~3;DoEZ0gka+oe?{+Injwbfk5hnj^Q!MFg=)U5IyW09~ zS|}d>*?r1_WxY~FE7MNqN9SuJwS5hqBxOqdImFtL{j;3@LTlHCdZqsS4W~0c_#@(f zgPT~1CG^tkKmDJ@I6XrABG1|Px1+o=bPI2NEHtTSdg1M@TS`2uTX1h_^lM*(;1y!* zJB_q>72Y@%UKIla8>c5?&U4lH#gVOaVtGeCp-jA7TpyP*vQ_$PdeLO(JQJ!X<^8OC z3pjasKU^`+zDY=s3zEHm|30>TAcC5PCjR`A+pF>M2YGpUD|XW18K`vkzv@6N(gnx9 z8!v^dU#;f*g#!lWk7l}r@(K%$=YAGd<}55c zwCc{0d5pu5(0Dw4-@LA_E( zt+9wY@WM+Jhz}n=Je7<;^zp#28Sf7tcoP<*Qsd)ywI(Zj$HcJhrl5E<+jA+#L1fX7 zzFsideY}H?WgaCmJFi#Qg_oFJjk;JKA&BzI$T<7FQ!BJyaDZ?z(p3$`xjm`zqrt1%;64X#ce}r?S<991;iM%lZ0JyRG**yP#%j zrUr`ac{I|so>f*#UPEuQK*wcnd6$rIL%OT9h>M2HZiBT@^bcqf&$sh#9#}O z7wxg&urSX%$x1g8nprY3`Lpq@EiL4i8YQ+4?A^Qfp`D$il~sDM0SXo5QS-I}bMN<_G4KEtzbBN&B0lPIE)xqPR ziG(dx6pAiiC*W?H#@9gQRMPi8;?AkP=v#c%dG2^hyM=|7Rhu?D6O(Mz6-=B_Iy=>dB zXlQ8YGUny!*{o|@cB+&#!a=%ulVO4#NTWr| zd>ys4_SX>+Jc$D(&Or>+d)wRE4$(KmoiO_zDde&!krEg8^uU3XxTGXcL{od3rcAxi zYvmMmH6^9MEP>Ofy(A?ieSGfM2x{PdZrAqkT*0wq6T>W!A=?`p!X36R7jMhU$9LzN z()?N;|B+B#E&a84>{SVYwY8OCY2miDKA)_bAWKiLs-eNOvAKDezPTsg(!BYdjI=ZZ zJG&ArUvN7~ZEHY2^mH?vYR`4%b9yI|)a1{eJ^N~r{^dIMQ~`3QQusHQv>5dkSZn3n z-w*rww&;cshUgZJ#IO^I#G7z@;Y-kdnugA#>88NR_{u?sp%+eVwB$P@iIwb}Xm4cjvTn-kyP zP#imUtgEZ*-uU-7IrR~(xLnglStX@|IyyQF_(fy4jj@Qxi>86;1qwBt@(pX2inc46 zT5g&j^={n1e^Qv07VgmS@UVjoE-N<|3XD2mOed^G&L*mX=v?hXUpdWe@OYQCwe_bb z%kR_i)tilndpUNRLhWR9-X}CcRD!f^pRQM=W?SX>zbwJayDuy(?B=aolGfH| zD=RCrYzHLD?2fas1yFKpU^-cRzTDF@N>6uaY<39eEJVXW_hS}(7axCP{xU-fG2{s8 zN7NNhT@F!F?0amC+mh4KB(;qnPr zHm{sKxra4?l1DjK8V=Ov`n>Iu{N)sUVgbtjO)t)9v23l!a$#vn^2rkspM6wx?82Xd zCa1n$s3u>lW?K7H*_;5bWKsN=`2gm3pUXB4ZAhM3S#c=coJtcVg-nl*jwUW+3#XP*5El{BdPK+R3RvzQU)S4q;+qQaQb}WT&O8Th-dynkf1`WuUH(YS*q^ zUAd-o{qF1fdPR0JH$z*IwZrQxD=&01WDN{ro4efH-5<^U6loT;fa-;Cz(qUU*5nI2 zOnbm^p3W_4ZTr*627hcgH{Wf;a6kN)8SV=wP4w`t+SP z>^3#yRqjFGxQgDsdg0>5#m(u=Y4?{AMgkHFll6-k85uHn?=q_0xid61m8u!eP(-fc zx_Wx)txT|`K9!Wz`zoiC1hNS`P5*)aLdNi1UkQFRR5|$d>-!I34#eCyH60nM^8S#K zQPW@M>e%8u-ygs&7Z{h5b2#Fhe&v+`w*>p5e1??GT_^bYX}sw;WaZ`e!=I#QW21$` z(d`(ii%Et~R$X0vZ*jP$lG!YUNbfh$)5Ch^%o(kG3r6zyuA)Pp5EDDr+}teLUpRn1 zcK7aGt#UV!Z{NPDsH?viAQ9I^$6Oyj{#;v2SqD>Al{cLyihc6FiHYZk&Fnas3 zPN7XqgU9LhxjqHsT7OPgS64en$0R)dahZ2%O>J$(Cr_$8e0V-Z zozK_DC$6VQhqJG@HwrOHxT~poBxJ?oLekW{+ac3rKwQC5QOB%*ep1yS-K@!MxA+y~ z+|TLk?5vieE^Ie?Q>Vl!L+Jpsph05Fo#a=5B503Zcmb_#Wmr*JnFrv=?t2RrZW|jL zqa!0y)Si`t6bb<;X=w>?&sfjjL+quZYRxgJcVJiN{f}i#swMZq!y{?!7UoTHHxir8 zV4M6%EKmsPL61#-C5HJ5^6+>=SNhu57js%M^18XXxv*8&d9KZ0H=^OZNK5CT2`;`t zK|EaVj$VAAA}dQtFTaj!BRZ2ikKNcU>V5l8gx!+KEM21RD|gq`aI^cT>g2`iq#r%%7{RF2+mnKt#$jui%|A}uG!EhLmp2HNEP{r#i{;^B11+0w<@W07Lc zHrPFj3Yply;qE4bG!uFcjO^LT)F|_c-_17mu3O9Vzu%#Jsudj?{P&OP5exD(e?d-v z@vvg|7Nd!V7#1YDbOGIyXWmupm?rc5f;w6`*I~QySiTOer{peA$!AMTOXUYAQr1hY zIo@BmRGw^&7M~^5|NeVjf;IMzNiv3poD9@5DDOo*3mY3*1qB6^cXI+6`>jl>-hc3* zvavBUv+9?ldpBC)yI?_wC#TuB28Oh>w2@aIam~!k90Dw>TWIt3pmgfCaa|zi)vGKP z`K(ZJ6(Y{b*xO%3#JzjBo7!{tjT<)*C-fe$Gcumy$3^3lmI@0BxFjUlKYskElGeWO z;KAA?g@_j`fb-II3lG}Z*Z>NvOxMm;oWeqdF#6S!^w7zPjFy?s>b!tDJ(+%lnb{j4 zW4b{ZE1VVb+hfID?-!!1wv!@zB~mtPdN6>p9=p04Z{ruqB_8&Q*FiQo8~gI|^WQK? zIgdp7iJ8@nPbd_qWwUPead%a0#2=H_Rh`tAA)ShD;9mOINd-S_%`Z3>U%q{-G{w4m0al0cfb;TY4JVS-&rkHG4bc_Mz?=fW9opVl*21C7 z`dwfRy>2B*%E;_3-c{+o*Eh&dUk|IY2UP|sj2b8U_~Xfd&=7SeK$J* zzPSX2c0tqaKYPplDZ0S0ijwu>g9Arl2$Q^(^vcf8?uChvkPyS+!(63ww6sZ2mdE$c zFE1^@Rr$gsav&U|DV+9x6b7kGJtc<=aaN-``wJ zJ|=GH0%(gvJO(v~|5Msru<~@ndD+?Y2K-^S%2s%=Kb56j>c5{M<#!r*L_{!utFONy zef9Ce3Ao<-5odXDTH9(H9?zevXluVrOg!1v(ZMAkaA0d~w)G5oihv7>!Hg5rjXH^r z4i3VfVv7hoa>{wizHQr6$Lsp<&8=m-Z#lhw{W>l&aZicU?1dt?Y$DzM{m*0*D!zvD zghWO8!8qi+buvt7yc&LUBEt7i)$Z7O5z16vUJmn6bcNs(Kn31vDbgk;Nm<{UJf;9V zyyzSo8_Oyv@JdcjZV

C%Uk6rq%DXr{2boR=;!LcMZvbj-=m--|HI*NGXkTR!H8 zvOx#PdU(<>4+9W*y!_)zNK6bZV&QKfXAt}Gz-o|(2L7Kbxn;btZsgOe)IYOMwvB0c0QJXCTuptk^k`NQzXz5lFJi~oh2M5GhNx0%T%!^ zHPE`c$BI6EYJy|s<3oNEB^|qBl^1R6nS|`@5ZFA>G^ADrD<~|it;1qa0%(y9|H)~#DD+B}yphiiP)ONr1_+EyDGIK#{96E28SLK5-ZeMR3|L)WFHr#mx~0Vi>By#>C<gyyEf3Xfka zzgi!x$Tn8X-S6MNP(Y~vD#-YUhflYiyp}&VT;nI~IHjJGo14HXOTNz8vgHP` z(YZM#zA#_ez~l0kogc&ckUxI>$a}nKWcTpl2`GV&9zAk^lON;gACni{i!p-bK_ank zZf-Z@RaCUJB5Qhh?&7qzTt^Z;)!o8-B>Y48P)Z~i%vpua4w(zHT3A|MOyF@jEy1-s z5s^KMB7dbFdM3L({=`&G)E~@1hOGBjQAfz_sx*P{TjK z9;|C0uf2A5F)Sj2`@{*vt)#;+XU*hZjdlKuLKHoa5L3RlyV}D@X%%HANkve5_dfnrK* zUN++(gxTsHxCc`goJN{@z}}R!tS?6Df^-wzVG-aCkeR9Qv$~X?l7>sOoVB&dp`P{6Ogf7QkJ{Vy+oGWH;Ft$Qdbr}S&MOi7%w7HIhzK}IZ# z$nqVl?f)55*$vD$8K|sXyYRSWn6N$|2WxI+b-=*Kun7<)j7ol)<(t$=Nl8HP$hx|g z{5zT|6I?S|o&Q;M1eX0eAaQmi+8?+b8gm#Y%)g^EOl$kU!n&K8nVE9Z(tBis*aLv)B-ee|$Owd>px_~TYHBc7 zNblj21gdPND-l}%e;`Z84v~k0h_(5FAi%9~pwe~nza=^tLn(Rr`SbPTq7FB;!lR;S zGBo@E=0^=&MIK$!8yc{hVdLfW}b&ADEOy=4p(`h~VAw*8#__3LNClbj4^LkgAYoU@v(H5PM(Xs*`It5?-R@Ba%~%MfEY(ZUvOyhDUU`A{>MS}M1I zrwvw1NemBX(*C8k=)O=vCG0e#HAHxIN?|hKO8@y1Hhs?*FPgkL&}McLA|u9D#QLM8 zkHu@qkj2G$r44=Be+RP~TfpPp{8x7L#k$=Zhf(5qgx0p*aH!GLwwv8Hu(srG>*Le$ zQD4E2=`0u5J5V)59}YeZu6xxhxKo`Psmu7!LXGq~r4atPySqCvlmnW6-Ny37(ENN5 z0PSdT*N#CKHM&BEG|)#44jTlqUZQ4WWBc@I#A}8Hj_0{jr-u)(8yhFcucFG`j#_r4 z0}gu@1>Ou7v7>&t)KqF{Rgo7=d>@+QV1D!22UHE>X-kV5l(6Pe(X(SfVfgs_zkpwo zjK8j@7u_(+i8yamy>DohMzHiLXbC?{pWJaOj@(Pb6bF!6Gt)rE(vlxYpxeb@C<2P} zpV-dRV_{s*ZU=sl_h>RHAmmPYB+$PJz^X%mWC{n)B0eQW-uBpqwsK(XU=FP4?9}8T zg^tc4t68E@YK!$R$uRRZD-x;+;_0oVmM1^x2JZjvXgCK^p0giOxWGmv={UdU$2Q&U z>CD66OPDOBqo$TLF*)_>)hiWUUB9fX^E17L%9+QZ{w4qcIBjFyz3qOCKy>N`l zmkd;r+7O@$Gc&WM5?Yy5%xE=cdHnWJ3$X#0JZ8#_a-F^O_{o#_($eG7FFdH|rt($E zOe8udD5<69Q*kjD7uRlbK>@7WoQXmz#)!5jUbY%2XFGN36z`cc6(99W6u()XF8*hrNAM48rBY-!)E6!odZuYDMwUk%$X7tq=G#G5}F=K`@m|LU0&VGV`&zv<2F znH_qw;{PnYDYQdxmOAdxo9pl+6YP?kw{Lp_)-uo)Wn^Zi$*lkO?VhnQ1@KheaFA+h zYK8!8UIl1rWMs6WBq@LNvK0#;p23lkggcq1z8vK+POL@Qkd7V0h%77k@4)Z4i)dIR zpc29zWjg+eM7_8Yr(nO%Q9B-?zHK)`@f5~LKmja@7cV2n0Y;ZfEM!f!otve8! zH9T0i=TPW8_&2wFDNjl*d^RP ztk+X$XlSrpu{kcWF?xi2J*DEc!OtkDDr;)?qA-L@PqF0-M>pFeU`ibXB+Ps6Ty3%A z^m*If!`IJZj7J1b(em1$gpdhra`^|w zuo~3gtovUNYd`s~#A<`+_#6_kxN(XJI3Us6SP3>D>U=ANjhFAt;pKnLt^HxOE@mtb zG0m5)v+t#(WOc{O7#nlT$jZI~i8X)&84IFl@^akQGS~GMkW0Vx_s8n#>z|R3Fqq)< zlSYbvu!WLBSs#c&Dc`0!!gt5+k{`4~a%QL(np2%ejry^UBQf{^m^=H`tzYOBr? zKDn(iV$Q(D1YEZH$(x2r9PYHpUuh7V4=yPY13r~i#O`E2>=e| zbfWTG;g>MT$OE65sFpGrrfCr0TWAaGeGH|icRYYaNYd%&`>8pPt1lDlC@F>45@r(t-ZZnRb8EdmgW3Y&}qrTo+w@M>s-uEL^OQ4+!EnjvhTKI_BWKwWGVw zXeXWEYO%;?5i;6SVAcIRGBVNt`DjTYll0{LKe$reUv{3}-p0e#FT3*0L$$}UCj64d zBZ?lQFZ>2o5411;7YbG0(!!z=WXy*^rr7A~G-@diZ+!jT_O%+bcA;HV`ZZA|||(#}V(xjp{)F%KBP7n1Rl)8}IqpwF3^oBx_VFYo}q z>^^Ghsp<1wM;`xKPk+Z%y_mdvih+QTOX{0wUjy2oyK`%HJPoYPiKEy}lOXAFmV{wZvISXes1 zso>VQ2h@O%kI!{o-6#WbkYuD_j2dr<;V&VU$C8W)t{Y1xT|GVTpgU`kKsHiIQRn65 z4QBIb!~6XZOj*3*ycNcWYQX=wd9mXEr)gN$|Nqdiahjcf{1GwOuWmgPI4u&)5^yfq zuRa=nk%e}0x(qywz!nor(TJ@TT0Q_*)SIckVOQ(@XQLi?hqdm?dRFUyRLTp+Rx%uo zjy`Uvt*Ceapy$5%|DyGKn7q|NYyOeWe+lsO^2YJJJ(+5WzB4la7AYd~2B=Efk09g7 zXK363u`+80Zd#%W?}zvAWi?7+GEY=S64W2#K;*S^a_aa^-fVAg-w*4&gw9@^Zvz?_ zF$w!ewEk@KZ?Ya67A6DC90)v4vpPC?X5b@%!qN)e%Hf;H-}Z&q`WRFrMuZUc>Xjsj z+Dt7IKceKoF!9RK`PD^Y?YO3M?ZE3<&D8C=8RVa%)kryX%kK}|9DEt@57?(QJ~5G8 z*#EzqjGg~~BVz*yruuxa2oXj$Ks`saqW^}PeXB$9SXauyK~S*B3U7)Utw-+qe}@ZLFS+<97+J?aHl7 zqEB@{_-3S>y6u+yY6QcO|4prrS+eafJ)^hTzhOGp zc)@8F1^Cuu`b{s!ZUsp6n?Eue9_@JwGvLT_BeD)qeaYR3qGb%wx&TUV0Obknh>6{X z5#iY3!@Gey2<1`c0+$Z~HrbjyuMin~xrIk!)Rq-eNq^lt_Mt@2%uGQbEs~c{Ww!n+ zVk^nXxd1lcCnD(?8AHE*rQ|BzzD;XkVWG%}`&R&|A7f>0y$hj}p3Hd|sz$;l(_ny4C}E)`P;)Rr+_S%zfdHYkYhXJ4O5 zgkUm;k$UfgA8$Q@{@}iN@d&8JqUq_Q*wELoac|a`&aLs^E&sXr$J{UOAv%Ag$bIXw zh^S}_sHq?F@?Hb%)4e4xPd~@~($6mez$37B3D6p2g;@jo#R-JX)dVT;gQ|}n3Eh5k z!E?M3o9T@FM(+C)bl2q5w_%d9d-N#YeQOP@Fz_Z?_G^)VN8aC&1$fon)dgtUdlJOz zww@k7F`($}mM|;Lat`P0iwmL%Yj>9RQv_F;~gTN+1LLAcC2o{ek|XqH^dgsyrIKy(m{~ zKN0wcp3Ke3;XQHUDNL-HDCNb)MQLeai9kiY zUr##A$@vUmlzft!D)27t9UW75&1MwR)Hg&#L`DHV&dtpU8&>WCdsgdKi1*jOb{ zEcaZWsEAbCNCLqI=R9vfmb16;W`EOWx1Kd|kmRNOy(QJf$%W&Cxe*$ng9;@F{V9O1#q(FJd z)JRLZ;XxT9n-C9oyxXzf92tV&e&yTv6Y8Q{3ULQG`HM#}NcVkWkIpvr(MTij+AOeZ zg5_ex@#@Z~14Qoy5Z(R7{cQS*J+JgXrm&xAnEeN0dGjI*L)3X+bs))3XJ-7f8&)#{}I1tBTRDp(QaWn{9EKe@0%7!;>kC*{{Js zvuDqq>nZ9+gP)(38DL=C0;7@*@N3hK>xnyt&7`_%a3WOYnYT8qskY01-=o-JJR>MA}bb}|D7Eg=H$D}xsGz7QZDjPWc?Dh>spG(a1a(ScdqK*t!`fym)@y^1ckyyLoNNg) z-2j?0&C->FDTAo<2PcXSdHrPWe0&(s*Wr zX991S(ufnUh#o;%Sn!cm6d3Bjesl!*b^u=`p4|8S#b>P!oV5br2Y4blJiMcKF;bu8 zfAr#c@R~@ZVPED}<5vTj$^d~YL0bv}v+MP=cCyz&T#eb-U%RK3xRF`{ZYl5+`onCQ z0M;NNPl0Z!Bm@8Qg8@Pve*f;s{TM+vKY-%^J*;x8qeG**uC5CHAeap*Dk@;iex+3O zUs0(B0nr2whL=ZpD#5`3q_Zf8K@ht*-*(~mrm;uxGg+TEKyR3k0WPSivdC@LG?F}c zB3a-7gBO^aJP-Z{I93oWheHXQsj|PH9jb24+e?-R1Qbc&v@63nV+e;B8BK)IcgPYR zR1KGU()I+B1~nJNCguf*8)#VY{R!w7Gr-VLS!^`Y+PYXz&(3}j>>hB;3}JwS4-zf( zzUGEM1Xc>q+}w+x;$@pPbCr>*zr_rAfgnONd)dL&r)}*NBere(zccW`=PsAq)y;!N z3(&)9u)hfw6W$~wBv1gkaC+hh*_(Q?5F?O{VdTD4{uGbDX+5B}__Z7N)zI2HATaP? z=<-)sqGw>A90D!33fz&4;990OS6ftARVH)r|KQ#AYE}zz1X^GmzyMgBr3g11WDxL~ zeX8j(oxyW54unp#h=FxwRUu69^8d_rEP-Wd4JGh1V!O7HkB0Yv8wif+V_?KM!2}Cy z4U_zyo}M1WOC-5 z70>tj`1w^~N!zB~v&R1790y|a9+L>`A;H0jW5+z0jac%)yFEHSKAe_WE(xZLSD~S_ z2IY>>|G)vMuC6{o5PWAv!AxLYS8RUzTr_PM^b;)q3<|_U;`Wnt z6wV;?xX5zvA<)1Mg*4Mis`W-h;}ge80K&7t2jAA&DV3pN+@5+@@@*IX8ygNhBj2$| z4)9-ma$WzZLS7d~rLJomovRX-0kYi1Kg3;D_Cc|gZ4IC&Z?#-$lEW2=qUu`a^AZwj zv!O?@ZN!YwHay%+>a8**yNO1?Ak8=oW@WmQ4&;%GI(0!vq7Td}a0j}{TTV^w@YK{) zFu0Unzh;1uL-*z};AY59Cs=etZLoNP#sr(v#%|lohYguHJblt>+ofkZf)8co233v* zN@eubN0M5=?;ZncwP5mWq8$>;(sGQz&jhC^^W#TZ@CkuMJ2*Br5EdvdJ^cXKfe|Ol z+`o2{9M|!ul1Za;IG5+qBi?NjDElqc+yr*6+V1}lkxpa zut!>wv1q>1fkImj5iv1Mj7DN;xVLkOhim_Xfz?> z%yO<6!FY~%7Z^$3fFMr&ULGcFG$Be4phXgRph8kpQ(u^jH((!ru){`w-H}@ zdfpPZaHNvnMPxS;>@A7vd}nxgIdCX^23@;dUL&Y>fSa+Bq-!ACJ(!DdV~g zC5%U+sBR!R8x1ha-}crm--icIa=pD`%YN$g>02FVBYrmW)UG&?-m(saE+hH*{lLEa z(#LGJCqEC$HWwE)9bGaE#$To|DFkd$onSUGca&Iv+%6F5C4tg@M;dc0KO}~JStIC7 zDzof72l5RUf-Lcqy%g=RotCBMZ;^8_hF@Qi8SKlyM(e=e^$1Qb9yn+bk)x)Kv6nu@ zwqZ_JPhox~MZD-tkPVQOlcTcv`H54k7WynrCfo7jO1K$T%w4sxO!yfv05N{A<+ZlAvw|dE^pz46hbKhrW(jufi5OJF0Z+my2v7c8Lo*3C;Uf=~&Jq0A^5+C$wX1P5o13>QC&waD^DG^s)s{!F`<^wLnjAtNEquDJ9KS#Wqse? z^^~;TNRqyBV;2}l0+HK8bUrU%hOhyjqOPf#kdZ+P1cao%K1X5^klnAt!-s*OFtfIn zdGO%93@Mb~X*>7`$G!f=!Yfl(3NTPo8t@IV;>X~`1MODp&fNQ-POI+d6Cig3(gOZLO4$X$e5Q{442>RH~-qrHqxVRJQ8X8<@&g=&x&psNO zi}=^h|0}Z70p)+lPDLO)4ViX72SXMZ0l$5_1<`>x6b=m%12_gf7fuoW&Bt{wsL$2r z`@fm(SLP-~=ZuznkJ}Xrg03`<(@1M~?JEjARa-bcs}W-|-;+-RdfMOsX1fmag-;Be zhMk!xIdBve*bdmJ>0MvnDhw?m7^e`@Sh5%Q`UlaS=#vmx)HWg&z-hSlcmV-#uao0& zKPhn0nTYtDiC*R3-t5HH!uNPIdJ^;V%3of9FZ3~{IyY;;fQX#|sVyaRd}@kp`vj?b zA2|qsLle$}p(k{+R?i4*QyduAVsr|jM18|(W4~Wo6~=u_#>}Pzt+u?din8}5K6VTOS1r^8O72F0buq2Dbt@*0@dK$3Q zf;0P{!T_BbMIa<_IbsV_)}J}UDE=8nW)<+YKYfZoT;DOY-7e?)dc3wkP@zUsPfwuq z96!GYcnZn%N06;^oh=|RaQ5U>2;Zsw4s!?@i_cD`zJE{oS-FvF@|Fe3B;7rHl0#t@ z1&GngE$NFjHX9Ge_td(r9cXDoaJo9W=V9`9P~&B|31|oA8pv9cv@f7jh({&duD-r? zijSXP=>C^Oi1YXp)liPehS<)J`cjID{{0cs_5cY+YfT$|pWw?Kz>&Sdf;Tz$!H-J7 zafX(bew`>x-{|76xkb6a^5ZD`Ad=lWo!S->Fs2c~G=&gRgjN1daJ%oXzBTGr4tVnd z6UKRM0Oat;_=WiIOc!cnnJZPn$@Tv|5h3#Ke?1X#)ct=;B4RZ*+Uj2e5%LjhKnM?x z1R-SsV?#)1VX=;brqJua@vDbmN8o{L*|qI(J#;JW==7QqmOJ91S*i z>VpSQL#_c>adll?{zS-u96^-f8mL%riSyn}B{on!asndlAeoh`4LD}98l=9hlMzn- zI(E*^Jn);4z)8%v?3}88xHv5HR@9LX#N_5+PAT3~r(^^hK=FsUfg3@|p%;e5D#MTs zT)w}=q1NCaiA^7v8flpT!$n8A)2_;VP|FAWZV;Y?L7H*@p1FinzKDk9jZnLfI)Uz05hFYNGA7u65*i}SfEqDhx!hvU2<9mav+KmH+*6s z?`8(kHYRHnPxgp60M)w8vd!p#a-(Ns3li_fjn8nvuK6!~ z209mPg(uD~4$gT-~g%w=e1Wn2acnpl@`pXo^VMW6#uMqJ5f<$}= zY-fECkMtS+AqZga#trGjb_*f!!Tio4zN(Sy(&|4ZV}uPLA0A z$*puw&Flv|v?Mec;!*_e9+>jo*&6PQJYgAtiw3S@IHRzCs-sE+OSW2)102ww+B)OU zuYG^azGe%wU$yqG&p_WS)E=|F2h@lA;cCG$bS)9g1>^`bIpGULq08!vn&nXxaSV0d zsw<1s*dOC9bGHD4ARVkqtvdK|A7A8$$oJ1_-qNt}hQIxEs%SLrb!Mc`>$hR=Q>+Km z9%O12d?+$Zxfe2~{PO$WBexlDsqMd`uBLw3&}(bmdp?@w!FV*q#eGwRqH`bpL>fgJ z?UD7`6{Ezbwr~0Zc6X&lOuj&FL1@yCJ zOlC^`pNKkZxcAg8KY*B^N4Gh%&?Nyv1c2J%%<=$oFjN=}Y4f1R-PR&|Q=mFn6hm{- zerE@o7T5fC6hQazRW^k2dK4Js0n%o}j@mrvdG`ntlUG(&mWyueAFVuVQtx*Per0}u z1I(pn&F}V-(F$iqTpR*;7f;xzE-RDIz#uklqM@SlRKk7pb4LdgnSiq!^PRb#xJ1rl z12&0MOBsYf91sidU|v8#P8!Ao&TYGoWdWSR!s;DMcEb@m<=}4$2?;T4N!$YY-FKoA+4<@g?2d98xC3l2+s~>MKMP9OG3gkcrLWanQ9O0!1`G$jA0D@;N^i(nxLv~EkFbN**E5%;h520giAYpI}ccyvn zma7vWu9F36yvHssMo_}Z`3@L|{GicHhB0z*1VJ27ONVG5uPVhsGMep8QecL6a*2p& zM`Gst%glQ6Qf74TDOzI?kbC1N6LlaRRzcEY;ucfNCTPDNK3Q%Xc4b|o=>tafQQYV= zY9o+69c|ljc4W&j5gOmg_)P*r;gJF<<|`zJAf))h98O6x7A9DYiHY@l?)weRLI5gLP4ONLdC>P#)lR9lM`W6Bw@3UT06dmc+btjq2?y-7VhLz) zXBeo9-8OR4ANN6Ep;Z~lC}9if1u}pie{;(HX>x+g@^@$m!Dt3v{+;c z7DB#{}Uwj$fxW00V${DkV7>VObbc3L1U*;4 ztK9@b@2UEZ3rU2RJ}{h3)-aj? z&Y-texSa~+s=iNChrCtXH&=un8*vK?(g8y~*_IM|{zqe@vTZrg{?sJr$);TZukoPT zo>Gqf@|6T2Smx%aE2@AZJVuj#ycYsQdZkkKaBWMF><44=ZcxX`;YgUb zVWv>!Apt25mupE)K2OIPvw;aunG} z;P{b*upl|U44}2+DkghvwmX*x)|s4zClCDKss6Ys|KaTi9V87+-w*lr0;(rZRN$pNEApk^_5+7gtVHLY<%{fm|(>qFz~F zuV5MrkHr`Pui&L`NHApsM1>%lYu#lCL z(*nvkxdz)>5c0w5_7Y%DMQ?A3>{yiLHkh;y1JMF$I5_0qGuvZ%nUHD%V6gyb%^0RN z7NCCmBOVGlb`$M-m3^~Ub$yF!m94+(BQ13QsJZM9DhuC67`W_*t05~J>_otUlSw2v23H>hCu&jRnzrI3+o@<<03Z z9Ju%(mi1`x^De}C`9re-U)8YWWFCRDXR`tFf{wjUd2VN!1aLNeqGZP{P{Y@OFDC2Q zLw1jo)Lz2*1yei~RQO5`%=R` zkOWQyRq|6#$dMIlTG~@UL_l^r4w5XB;y&m3_90f3pr3}>6P&)TQzu=1$c;nQwEdcOP@)y zRt`B`76DDAjd4~|?aeWG98SB%C4kuKwkmE_C!-)Q&kp4`2FdMAl-UyliGO z-*zAtg7lCL{8epcMF`jFZ~NWn?KEBIeLUp=j(D69?g zBr`J_7WB>Zd3Le3T8t~=IYhYJ=Fkf#O2-?@GKG9n*ilkfvOesqkluG%(ik{H$r(~`4hC5S5<9}b_OjXNV``swehbXEU?elNo<7&j|m znTPG8hTnsSm8emq)pnR&>^NF>StNd)iP6QjKn5!)&^$pyv(jz$jEQ@cYUI$Nr*kPO zEb2PG=m^1tKC$_I<{CCOj{yLk|B!xQS6xumM{C`(v5cQT2T99~Eb1CwHpp#6)c6mR zh`y0z?BLdr_vDk|J(LVg!!KVmXqA-E{Q4#v(6kt;d_FGg5QXi4Y;q&gJLk~|YmMnG zLd`d45P{v%TeL*Ez!T?rsLm$emcrFXG|z6$!;|`6-9?^(=lJz2L=1oB*E~Pian_+b zU%_lZRzd=9#B~=@#J-40WXLNn%P02ve;%{qpY82e8p_0Uy|ewJ*|BD(TgP+_b6UQ! zYB&YlGcl#&KgO@6!v_xtxfic~J!Q}9@Fk8W^VzvT8vu9jT9Y5;$R;$WA_hqF|wD+;S zefK{YdlPV|_cm^P)G3vOR7551$&ye}QB+!pXhAfVN=Ootbt)7gZ73}$`;wHsn35$C z$ySIYd&n}D88h?$e5-Sw^ZeiEy{`AV&ULPH$};nt?{eRt?Ou}R?=SqZK5u-*ecqW{ zi#|kdls~_Af!pZLhC+XvIpM1rp;FBm2iF`!7T7a2u6M_e&tu15Q55?=30=!|ElCK! zUCTG`6}u~Tpr^BPV|%{1(X8#2oG~Th>Fn@esp%+O;#Q>;+>M?amo+6~(yHuLa)Lqg6?D%HEb+XjtbgYm z>ytd?0@h(z;6ndfI%C`9}H*Ym$%fP?U!{9=lmfNiSR&ndh|k z(9F8o!u#Dvq$!I>b4NccmEkw6iiv!dG*bq_Hy!2{_B|cpJQR^8SH51vzGY`S94|iH zz?>{7BSG0y5w2O;Rh8v5yy{fg2F;Rw`s1-5hMzL5mnV9f&)Kuwh(0)D-Q}6vTDXfo zgl%zHF|{dY3Raf==)1YMLQ=9^s=kl>QRv8z{FG-4vAAySkMi>;&D*nlkL=-ZwYx7! zS%+x1?t7p+Mwk0u^EtzMicj#|tL68FV&+<&==O-O@s#Af@GVIi!erB)EC6aY=-^;Kl*w`qSX60lbyND#1lO44D$BBI7jJ@W&hoDD;Ym~XcehTnHRD% zuaVQg>G?;!>DMO}(o5BL>mD4qt8iw+#@{@gy+X|a$JyD4h6A`k~-Z@Wo&$*iybi?Ep&!~Dd zcvg3ZPk9a2X2?B0?fpXcnGE6g^k**@T<9wiQZB8FVxKZfyP)kqZ>PoAVa-i{ha8@{ zF}k>X&)51OpSV@B>s;F|?M&Xr30j`zlyL5%F+96R! zW{x*xytXM{uu58{@@+A%c5y&u>6_lJ*hKK zFS&JcAfNB4Ri%0s!&^~|Z2m?5{$4C!p{GM5myZ~TF4EDeE}{RZP}^p$6c!*h<@n>B z`Sdyp2fXR(|8>TCP5ypzV%bk7XW*kd>6 z(aX+R@BIDkGvh>^`T8Gy)jgg(c{TkU|H`QXJ1x}Ih9}xYHm5qL>|lZyz5hC2RIqsQmWAC7nFDT)Y1hwr zRxWAO+N+V-pQn*2;rS~0vF1RrxLx%sRsBDo;yXZCEwdK{c!tVG4qaQAKg?%G52V+o zTGWuyiMOs%lI@^Un!v|rf4a|j^E`>5i-m=qVYipLx#&0i>+spOeOM#-$m~ni z)={I~J`Ke|{&FX$tg`K6Q{#E0N*6au4KD5;sM5&_x#O#L_(@W~Zg;_(qiRKzS9vyv zhs)l+@?5i~W&Y~dO6Q;O|K8_GW(XC-k0V#LV*{P)xGjNFyEn_*o z4ZPf?np?%R0~fve?T`_*$r^52G)T+HU=Yr1%75|>lUiw=Y$-s@ao)7 z;2_DNq2e@?|H7*>H}-`p+0;hBj+fuTJk4eG?oat+oBz>bIvlkFR+b4iyWdj!#A-Sq znrYLg$7c@qRDo#umDudsM34eZ#vs>K31#IudaHc)1pIW~ zK0Y-^#{aqc6$HEv8~uRg7E~KC_Qjy3w!mx6@6f)to>=AMXQg0AEqU>U-z&N?HBDs~nK*Z{bA-X>8i2VV*Y=Xq3dYy9SpO54P7b+>= zpdd|{i=<`v;fdD$pV(59JD?&$_Tk-~8T`JRc%s`<;2{6Qt=W(EnSiX+-vt2J6e3;{Lc9XcFIzM)G1#J^Yh8@iSM%C}*N{x4qIrAZm^$`eDBOU}gHRGNG@WmV zuU%X`QAA>K6Q_N#vwKYuI}R#nL;>B|;O2p(7Kh*Fi~|+>is2o53%JH{kwx2Re{(o&982Bz?cKW+{i2k#boE_3JkB|D z=0t_sp>rXU>--KdJ$eAyj)&3#q0N{%GYYs~I802P@UB*uA|kcm8`ACIK7%Pudl&s5 zjBA~ckc?59?;_fSeeU_hJ&607Y$AoPSx$)Pun!lF$)SIqi?oTceS6d zFJLguTPw6-O@=749+{m<1Cr_NX?VdvVrPU0El^g6YzM(5Qu8HloM)9lbM~vrNoDCk z3jFCF9KA)ImfyteR+!)Zm3` z;u{{u?S&SAWxTn_h_weFBNj}9mz`w$VK;5V9~=)&JTA>?Y&4!TcWxw>Tz;}Jz~9A7 zpc9-#!Kdm447@r^%i(QlsRo<~Do)+Yv=WU(ln3tZN#cNpCqT1b1p(Vs^@}Tz25D+# zH5~z6GbL1w7dy=#yO@}^4!N86I;$1^@p31fBNCDSpiW8P5;9fsM!VfdMn{XGw-Q9!B4n!& zGn-+ZxsJnqD$2hzX`&knf!A3D1_qrEA6K?ZX1YVVLm&z)e%H*%UFkvh$K*_MmNlwG zvJ;t&zL%q18+aHJw3-H&q@<+bkJ<~>{u|rsj@}!Xl5@hEUHFaGe%BI~TrY&33JSqT zXnrQ(@L?r^)B#CL7VH)bPF*{-VJsn`f8fu}RuwxWa~wc|==xK4%H_h0z&NVVjoZ4#SmhTZ2Lv&}&q~*(yC>spCn>#f z+|0|nus0p%Vmv5>y&?s4762e0Wavx=aI)k4`K+fuV2JX@D<|3#D9Q@KIQ)FKCZ+Rx ze^(b-x#Ze~hdTeBc$&1d^oMkt!(ah-n6L|Axe}BWo24kc5L~b zYSso5w~80axciY@2T&|ud|p&NvQ+Ahy75CEsewxp2Y@#$TM>vmQg_A4Ms70V_5ri2 zi5#Az92{0eb&RmfKoIqHS#RG+9Yc^{3af?%>c@Yj{+!TC6I8faMdrJG6#xw z1qvuP1Ys#7v0U{6vX~bvSOAf)+$Podky{F$f3mAU*{(T{#Lj@6_y@fD!vz4Qn(LZaQ|)c6 zEPOz7Vh$cU^aue&XB-^7ko_5xVw^o2e?>B+V{+jf0WnKHDIQ8>B))r&D-93b$)uRo z4t#tkG-My*_Ae#%C3e+R3OSAAQUF>4Y`g;6@xgI$McyvJ_65PRqW&$B8a3;u=bTof zkbK+~+F{;6U|&P}0&+6{Fx6vulz$EV=~LaYqythgv-u%??QvF?D21eLr`go)OVI*& z|B}P(S(gW%PNqY>MHX)7ZG#}g<*kV}?Vs}Y7RtqXnz!;&R9Qb19J{NhqeKOyU*odrphq7qisa|k zRdY5RcQ5_S@wJuszKH6}An4TzsRwIht}WVf6+8T~f&?yXa(aTD1-;<-5peKz5UTD? z;F0m)j_uP01T=Te!4Ajvf^8vA4>UtlC`1xZ zcpsctcrU&Xs*{pl+AhfhrfZ)Os1R47rSPeMuSO|yhxN_`E}mABN7TuN#mkm$kK=-& z_ilZAS$qlTjSkM}q*HB#D`vM6S-7D97l1v9|@P&j`fVTISh!0QT zS!?3nvn=WfQ$nt@GrG`<5$`6&6a}j&)Okn&go;sS&E+e(-0lQ6C|i=^`<3h_$vWjEp1susO&nr}g2d(SVR?k|r*|+1N+bjyAGiJbZ9=Tb6kWTG( zolsF$o?c2DC=?+zn~{+iNj4%`?x5)A?i-Xo`cf20OlV?6LJ;T+1EWy8{kd~HajEA9 zSj2N~b<-?*g%>T7HO$E12k{tzqU7C;=WIm5U4Ox;o(m1o2tAd#TSksyL-WWKFs&bf zeK(t-P@4y!KP7iWjHs5%9&8e!{45aGSu8EBb;#XGJApeiG{hs?%nhsO#$2=Otx+{^ zkcjW~GGKon`Lz#bU?0G#a1DHPeVB(LCbSa!K--@ciX8yN!WXY`*uL5H|^pR-Y;n^{_3f!O@c z@(enM$p--iczhlTg49rDE>==HcxNTT40X0J<{)*jWW4T5jwt;g6k)G{G?I7|e8l?= z4B{LjAWw5f={x`BlxWGcAW?^c!|5Jc%aIXrie=Wvh>a;bm{&cLn$i8jBhC7SN9a}R zX?8$*KL{LqMt6-E^={1v(FXDeoN{S5OAXkILWuvc_G#DsG(xx{586?7X-&>~ptW4K zGqHUjV-`UiHLU!#uEQrQy&j?7>1tHyYHhV>@2(DlFarr{yf`v$=W5}~=7T2AGD=3Ti1Ff28&V z$Z-(|HWtqv87?bXw#W`J0EkgDJG&sV)zIc{*f{H=+hN`Vb=QAAmMA8DMX$way#@hg zJd<_l?#d0t%z%moTA#_)5iA?)kxrk`fejSeD`Y=a8divZEnY@1$YuTAb&;6cd6RHVzJPVCy!MlE~%k>*tCNy@Um?2ey}4i#q5 z0pbo>MvNaVW_EfEyQ&!ehv&adM{wE<>@$q26MPbbv*scyDcm8L*X1Q^l7k^RCsa!B z-Q=%$YM6z>-Q!_dU>ea&nqNY0R?MxHF2Ph|-svzCBSgJxCJrat1wh zL_YX_Z|MErE7eT>I<3{zzL847HEY%wS~0$;Ua)F#jvp)hSXiHl=?;{>FBxBZdiW58 z;6M3kK-{8YVwNCyn5++M zQ6L_(&<3^O0#hj^E(-u0DXh2Rg0ON#L_~$M&y&i0CjfhhYswItRZn?@S$CNcAC6aBF{Cuz7G$HvEBS5@hE@VQ_Y zeg*CZvVK~X=Xje4S~0Jbo3_(>m&Rla%(sznC|H`oPAD1CD80=nUSO@~Y1@4`7;Odk zW5;*IEB;e@l76egN(bBK=x-@uvNd@y2_YWU&FkgaM=>*dY5el(+#Yl8vMZ6yEm~wd zCAEBf@o`d?^K?@PpL{CNa(F@Y@X)1we#`5NaOZd-;hdx*fA(RcSl{R}7*(zasZ&c` zDDP)TqWo|Kwj%i~ALe9|-V`TK{$@xIp!Cr=dv+Z{hMvMB{9yBwqq$eHK9RX9 z4!ZsVO0e=5m2IvDtQ6S>xwUHrNt1vx?+k0imNYUtn+Lqh<;IJ;?cSo=77`Zr!cN}I z95Ir#!J$N4#URz^vx8r#*2k`x zS!mhKbi(+U+_od>)>A;~8)u#2K})F)`3>y0M8t*auqBztyg@F$w-}J(@nkiplG}rA z)jIFcccl_z*5P(!!9@VJ%rA*%o|aHX1||fnU6{rae2it?JoOmXJFLvD@aj<fJD#1Ku(c)D$uv!NTbjuM@6%%*JLfG2b75H)qKb)Dyzp z4O#XU1mD8Qm`>@Q?ry2F*3Qmd*xS)q%%y+V+)ExI&`uwjX;4~TgCAk_=FRtsTn05a z2^{|G0urMmZgoJbunPrbeh1`1){rH_#-?9xAZ5CK_kc*js&RuxC{O7a6X^h zbyVQ6_*Y?N#3KyXH*RMy(J6>2S>OJVRkm%PyIxP|0oL}NJH?DSkAqZ!a-mS`iH0Vj z>$}-0*79`r;+scl-C>P`{BV>o{bY2uJ2JWty5b;u3zayUhm$xj@brSkiVusFrIPzq z+T=9r$&t)!^_g}$fFY`LoNSn1y#(T(J<~hnKI$LI39aYOzT3o&39wUckVx(Iy3fQ2 zli!v16d|?Met(MuEck=QPY0a%52n8KzuG3he>wOeM)+5%tIhTQ>b9bc!qX5G&yNdo zx%(pI|2NUN9?$H*6xrSo_K!7LCB#1*Fqh&6F<=7O3|WqfZasa z!LY^P3=tbf@%^)@|fLg9&GmHj9ZdIB~VOB z9SuIo!lDcoaiL!tezypi)$1{I*B=PDK3t`j!r0aNvKKL z!!1WvE~JpF6{TnkFi}OLYmoyhE!lHy)Z&?Y+vubNh1eQ(;oNs?qKTvOzGQSKc)feP16d-~6C)_B6 z^f`}VP(5j?0rErFjp%o~$mkh3GR&c)C4z7Wm}{7`{I(n;pEGhXUf0wdl+(86N+4DY z{^bXBs0Vo{uxAnrG?8@0$HxS8}k15WGtUHg>~zmBHhSvyyAbhh_?|gLB=)IMr;_K-vdj7 zf~LD(7v#(^wT_5bT;mM={J9mh)K3?19zTEnyl$eV+-tLELNBej9zmQ%*aA1uZ~|SE z^uU=hh7K;(W#|NXQ&jhXDXL>%CqLK>-b?=cPgA0P-&rcupt`F!JRm*1kttrPhFwZ@ zplCFFT)1F{$h$o!amRvv!~7ImUX%MGE6WsA0}=WXV+lc4QO~NYjM5eLN9@iL@clo% zymy!Dfc{*~pheM$O1ueYSv*jrVE~EKte}RDMUwV)-0DvKO%I@~j0e!D?g|X33lTce zxNvXb?*KkmqVwJ@8$SV=78%r2on)v$LSTrMbsT{QH-SEP;{48^z#X>)2(HSxA9sF; zT4Rw0Q9j_Se5@2sl!5tCYk7_s77xVPM`hQ2Yk14RsDO_e0^sN3zsxEhocg|}yuCk-Ech2y+L~fQcDniW>3Nws1RAW;3*`uSQLlcU?)N(~-78fpKGQi-DC46&+ z1_mE)EY%DM*@V$6uJ~B^V@l?8jj)!Yah`Q)rz~m)a5jyC8Fm_$dDDegh#DW2rXlki zC2Jbc3bT4N^CYSU^n*$ESq#vEtA`k7wr>Y#vxDMTyIOfBLjdf_U9oV4%`>S|c?nVV z&DCxE463{PW<)INVN<~YeFY0h6PEo5JPB8%FZ%9!>8aQ2hI(SsR-8D=*FEt#CPg@C z$*uI%0H!vbg3u$rDoD4rq36u+Kpj~H)Wy33EyhZ0k=&PL2m=vXS3a8=sHWT#TRrw+ z-2s~NIFU~yvoZeZ)9W~sG;pO4f3^(Y9TI!|9dl_7UCEHEy^w2!l3o#oRA>7B6y}i4Wlwjz%9VNZ79!B|~4O(UX<&!*jK?bOCY`KO`U7 zP~uWJ!mTmHL@C6YV1(J)JNvd1FWg#4rVZ`I#JEbw;W@@jc(C`XO%G6H=z*%OGVxwv z;oj9T?=#?F3)G`3#SdQPw(hYA)jaHREiCoV&2#UxmNqLh4=P!#!-eE&07gG9w=&bi zX0NsTNjaqy*Cbo;)mRq%0DVYuC>DRrP{5<3xvr8cSMuV}u3cS?27MYE_G%_B$p!j<{F-s&nXgiU#o$N@DyZzto-8n+c_Z+y%GEHIATW*Zd@=38{YaObGr5p1~#q zgEoI`n>T09Ul`>BlROl{*vylzXL50gRhO)ReFe{LJ;jAZhe4STDfq3EDP$ZKs3fOz zrultz4B!(Kf~-JRIVF@sntd?sRLBhu{+MU25yKrAoYImDZJj3D5J8KwI4Z9yDpyS2SJBYibRKF=?S(k zgg5hP)1kxZ+5vD9l7l1UKn^n58|VyM1R#-Q9N%f6Qn!NL*_Q)wEIkDSGj`kW4io^1 zgY>1T-0=z;x$98BPM$t}`r;+zKVUwf^lfoIjYWXYNw|+8hu{-0FR$z2=S~rAE&JrE zbdT#sYJutD#^&Q|=FGs1iWZ2oFJVXr+N?Pkwzap^QNI_vV(0@Oi-m?rNY6lp#<4$STt5Hqm_ZNne1KzTOIRGPT>-p^kLkN;Cu&X~m? z4fqA9(Mf#f)+SCXab&q<53eQ?KKE{X8uI4TSm+vzFvZ;@j}{T- zK`Qa|xVhD>63F&_OK7}U3v4zOsu50X@C(Pzct-23o}k|1$XmLfttw=4w|(vHC9wgh zNWEGgtsux~x}=gSN>ZYLz`8~v`ZScw`LLoOR%I5SKceDe4<%NnS?MEJXWGhj2`R;> z8!3x7AA2ShKd>_0GfYJ_YEWS2%n4UV!36YZg_Nz%-&z1Xa!Q{`*UkI)lZLkH+fIZO zt&=bKI2ispnUL6thqiAznYa&)bMVie;62d^_8he8>s&KZP@E8L4N6Wj$p8n5_?GDo zYMAph`9is2CbO?5yPzIPmlD|7vH<-g?x)xIuW&&0Lb(S;kbs`{@_W(I101}%b zL+9tvkjB=n6vPNGi9%sP@Cc}!2ro|@aR{QliuKxEwlsrAT2Uh1fin}=js%`$!q8-m zb2I=DJXxcqkDot(-ud$K2p9h0&ieGk%Hu$>36Ry2-SO}Z`jWLMEXWKq%*eWgh%MYj z%Z*~H$3oZr7f5xbTtr3xB+c&yg5B3@yP4}Y#2Ebxb2F*Pr;MuWDSNr6rpHWtC_2D1}w2;J3BemxF~onh$3DV1Cm7Et6~sM) zQ5acSSrGvDafZCX{u;ofn7y4F$Or$NCVUHXF2Lo?2OR|zWviZ4u&dog%*iA?#6t` z=YL3`zeNW+@8NFAbS_jqnusi#hr|yMf|L(mLQB4IK~R~(2}ryu#6FP1k~rasUyctC zjZw(=mwGIk@^Or8fgc6Q#EVz0+BFiqsiv52jA|5X$_odvVe#qz0kgFXQe{vhlgS}~ zYeiykdhF|uAFihZL7G1FyWvpE3yTuhtKuZOV>n~r47cunJS#H+yM_d#V}$Gd%`USEPWd~TE@o6qh{83t z8u++ZP7?e#fvM?SwjC;5GCzj2fF!=*kL`z0;3SU#Q=)SDT~QI>`CkU$ZiWO3P}f&- zb90Y<&>P3FDl)7E#TODWcHo%D+@?ShTicS<%O#`rR z{KS}Bo&amev`ZAeU{NOnBK1k!M&1I_S(|Cxg|BkG+Huo5{`e#WTa@S)S4EQN=iKDN z3sPpy1-CWlYmS^1wcx*1&3s>GgL?sSG~4iUA9^yaD%n^5V@D!RSBMC3k>&@_Jv&MR zsk;^S7V;t-C}^(&@0xXM1R#|`~|!y_l=o`alHrC13a&{W#zq)y)mOOas$EtTqqZRV^25CiA|W9m z1|%fm@Ch!iHqnh|BFb%fjr1C>BqXGSUpUXIxg9y7ZPv}yzr_hQq9@$y<|3Zd9QhFE zAxZiGo6^KcgB_`fewHazOdC4G-K%h%JGF7s{)1a`0kgx~=<^3Bjx=dVx8cPmAakel zd#+A7NL{1H2#y8B@EGzDNnDRM=mhqV00#sapy&MH)@B7Y2tw9LmrIgX(J$?w+Uo@$ z;8>@J5o%%Z=g-BES-Jj5Ek22cM#Tk^7?Rb1^DQAR?z%}vd|HY(&sOZx81!TH&n<(> zH{%!NZ-yYM8t_s*okqX@#AAvsgAQy>7dYkXG*He>nJVjwtaVPYPsGR%zmr??h~!|w z%jLVvm3srJ$-NDkD0B$gQYLkun!yzz2C%^l{7XK(WJ z9ls^^t{MIQovNp&F8A{1BobQ>jfD2Y&3CcT{CvF+FS2g9tV6th-4ACZHGqrF)-}&NTY!9=@75)G%((8 zNGBBg&^00NjakhZ?$exSaCf6L;{#g0-gU~fAo>-NQQDhV8@`Z0{S|293g8$gXCw%m zI_Hf^*HCoO=2E5m4AtF z5*PlB`hJ=m!%V!Ji0}cQ@}98yiC2p&9`|nRq5b{$zr-|t;s2J_bVaII{Xzmqg^Mw) z7lF#7l8-d~53n4#YxRGQ$qSqgzwB4M!J=q@xj!?@qenzSP2mDdXZDR2XGI$>hI${% zEoC6n8K=um++M%1nBk@rqb>dD0FUmX!a)a>6;9=a>8HPhHM#c4d_1Z6Y-r^YqL_xU zbN}hnPbxP;nYbOF?*U!i2nCaJx3csqh&flUU3(phl4I%b5vKjNxLDJU7-mo6C8|`R z%w_a8gi=J^b<*X*(J0#1E1q?9T*k}G>xu}nxTq*^Uyh5wSxE1E#*sb2~g96fBwwYlAHz6Yh| zlHUhib3n*(UraQ)eg$!!{0miX6VltTtu#LTK#l?DEHm7f%+Dj68^h+7g5QHl_vZBz zKem*Si#Kq0Y4yPFWA$S~HWKli&^>i|E5#{5k95%ubOM}=0c`!b*BO3J!lx+N*53yG z$@ua4*15jM)zY9`k*OopkXI6q>BH|n)887>3X5W^2hY|@SbXo3XnUeoG$N6{u(^H! zssCnXaDrVf2NI=ZvBG0dtjfx9`LC0p_~oO555>7Lr^ zmL0$duO0|DVC}J`e@~*ue3#p?k9$C-c@hPR8AL=}1lBqeDtLGjeo33vufdb>82Xv> zz3FuUKs{vg%Qx z9~NVrPPbNF#A-eDl46M1^fqjQQCCR=Cy;@vSQUMC- zRa&fm8rL6@#t+s3i@VLau_TrQ40k6Etrj%nX=#eNn@JAgh**z6+2Wr&{uO(1z_gbgEr6 zJm#8&K8q^Fh8#c>LK0voUeNZm5U6suhz&s^$T$}~W09lxTo4yzW*G07T~M!S&02vo zm=pY*c0)`;uT>xNWf|pxQbSr)eWq$=(4Up^9wKHSfW>&$1DK(*7|kW#Rn@(e3>Nj! zsFsOZL9Lo{gMh{F)nO^m-!k@j0ZRu|L$v=wu5Y2`6LwJbz`fK~jqJ92tz%6F+!AHZ zEfv)b8SI-cJZsj|vZI)qNAjAiSb*jr!h3w|fu#I)md%Aog+e^xXG4C!!Q9W|HXh8b z*lLKqACEJ~$18Cvo>KIHqGoE31h_>kto#n~4W|Vu^{EzD43sd@^4YVSVe4)8!2CWh z2BD5O(OH$JY(ZtX6ofDn=`A*nF$T$pZh^%~HhjMGP6k?K1VtP;dQ|rQ^o1b4Q4#q2 zTAz!#<9iZ~-1qM8J)oW(fuNGx5#5xQ9VC}2ws@B-57Lr>h*v_+Vuh`xXj#6%EGLpG z1=T797oIy?lgevqf>q%ZYZ(IYFrD%x*X{5#_jqQ>Zjn;VZiF=cCh>AVb~!A;RWiFa z5D)Csq=B3Z53N+mkS`8W5BH<;_Ng%MJ&X$&MWQ1}7Nh7o?_USO?i!swrslEp+$EMS zsROGmUG$%%UB60|Qe8663aSsVi(exX1yUJClVV;VT+sjmZebpd(&@%KF!po-x>Om0 zNco^x{jR$diLKf_x$1gf1VEqaVhDWihVv(W6Hvk^Dunt_VYmMIs)!Qbi>U zb5SD{k~3J%&`Iv*%m6gdg~Y+fz-MoOVdaNZex+*a$cmlS_bcqUC~(?ciEUuML~nOU>6Pm0GqyaXdwN5~Lc%F; zWHOCsJ4{)=Lr6Y)@s0)ad|Daq*dfM(yN zqCgkaF4ZX;xtrqQQzR$f6RJASC-mICJ4CMg;XKsfm$omd95cdwU*Y_GbT@-i~$k`Pd%2+o(}Yi zsGv@BV5jN<>)H|2}N?(p-q*gn{?7uwwB z8-SB6EG(MVS%ZXoU0po~XXt6&oKFLWp^U`7u@wi@QcDkuM~?e2WAoa2UU=BNWj;>! zfG%=QH&wkL4Rz^kG)>-Ejgo8D@Zt18`)|AmdzJ_mi82g|Pap%D#}}+ZS%tCBXZjkI zNsc67ZKO%-`~8B)F%AK^A6|9TfYyv6Q4;BcV42{N50`IKq`TB9ZUQ>&cj;ne)-VdblUx93#adMhl&xBVmsMccQx zHs&qvO%NHv+&L8pVkVfD-o~XmUbm z!lJG3cRuArJO~d*a>)j54Fu`Dgtp`Qo@h}B1z;cetHBZrz#zwQ^(Y%;uRMhIeM7jK zk(Y`I_L_;daXwHV^28}m$vD)@LV?qli_!`CH-P{m5mQBtU5CXB{Y}=j`%s1YZGW_W zA(h1!y}@yOT8%{9{5y!tnM$M4L`6jxBmIU1j)E#wZ2yb^Fv=@vgSXpFVC51O9GRs8 zDbZ`NkJ*YQrFFq~CemfNkXLSY9o}`uWZ$pI{w1TIvAj_QTxxv-T!2S$ANSNrZl&co zVqFJg8X6iVQ_yQIzOf59yJYYlGC3gFB@G9Z)USzgV4WmU86e9BW&lyV D4UFOA> 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 sLIlI>es1C)7S}^Z@9@*K% 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 RK2h*>P)%1gNBQWb{{_V`YY_kd literal 0 HcmV?d00001 diff --git a/docs/multitenant/ords-based/images/makesecrets_1_1.png b/docs/multitenant/ords-based/images/makesecrets_1_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f0f6f21569bfa17aab7bd37827d97aa041bb06ac GIT binary patch literal 117953 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 literal 0 HcmV?d00001 diff --git a/docs/multitenant/openssl_schema.jpg b/docs/multitenant/ords-based/openssl_schema.jpg similarity index 100% rename from docs/multitenant/openssl_schema.jpg rename to docs/multitenant/ords-based/openssl_schema.jpg diff --git a/docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md b/docs/multitenant/ords-based/provisioning/example_setup_using_oci_oke_cluster.md similarity index 100% rename from docs/multitenant/provisioning/example_setup_using_oci_oke_cluster.md rename to docs/multitenant/ords-based/provisioning/example_setup_using_oci_oke_cluster.md diff --git a/docs/multitenant/provisioning/multinamespace/cdb_create.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml similarity index 95% rename from docs/multitenant/provisioning/multinamespace/cdb_create.yaml rename to docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml index d3b5e04f..cc29fc71 100644 --- a/docs/multitenant/provisioning/multinamespace/cdb_create.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: CDB metadata: name: cdb-dev diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/cdb_secret.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/cdb_secret.yaml new file mode 100644 index 00000000..b65ea1fa --- /dev/null +++ b/docs/multitenant/ords-based/provisioning/multinamespace/cdb_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: cdb1-secret + namespace: cdbnamespace +type: Opaque +data: + ords_pwd: ".....base64 encoded password for ORDS_PUBLIC_USER" + sysadmin_pwd: ".....base64 encoded password for SYS" + cdbadmin_user: ".....base64 encoded username E.G. C##DBAPI_CDB_ADMIN" + cdbadmin_pwd: ".....base64 encoded password for cdbadmin_user" + webserver_user: ".....base64 encoded username for https users " + webserver_pwd: ".....base64 encoded password webserver_users" diff --git a/docs/multitenant/provisioning/multinamespace/pdb_clone.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml similarity index 96% rename from docs/multitenant/provisioning/multinamespace/pdb_clone.yaml rename to docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml index b88fb71b..930cec5e 100644 --- a/docs/multitenant/provisioning/multinamespace/pdb_clone.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb2 diff --git a/docs/multitenant/provisioning/multinamespace/pdb_close.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml similarity index 95% rename from docs/multitenant/provisioning/multinamespace/pdb_close.yaml rename to docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml index a823f5d9..a8b6d863 100644 --- a/docs/multitenant/provisioning/multinamespace/pdb_close.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase03/pdb_create.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml similarity index 95% rename from docs/multitenant/usecase03/pdb_create.yaml rename to docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml index 200f3712..920fc134 100644 --- a/docs/multitenant/usecase03/pdb_create.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/multinamespace/pdb_delete.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml similarity index 93% rename from docs/multitenant/provisioning/multinamespace/pdb_delete.yaml rename to docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml index 282885b0..81b539b1 100644 --- a/docs/multitenant/provisioning/multinamespace/pdb_delete.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/multinamespace/pdb_open.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml similarity index 95% rename from docs/multitenant/provisioning/multinamespace/pdb_open.yaml rename to docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml index 85fb2ce4..b5f07b59 100644 --- a/docs/multitenant/provisioning/multinamespace/pdb_open.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/multinamespace/pdb_plug.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_plug.yaml similarity index 95% rename from docs/multitenant/provisioning/multinamespace/pdb_plug.yaml rename to docs/multitenant/ords-based/provisioning/multinamespace/pdb_plug.yaml index d9135f13..dfc1c544 100644 --- a/docs/multitenant/provisioning/multinamespace/pdb_plug.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_plug.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase01/pdb_secret.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_secret.yaml similarity index 91% rename from docs/multitenant/usecase01/pdb_secret.yaml rename to docs/multitenant/ords-based/provisioning/multinamespace/pdb_secret.yaml index 60d95d76..3bbe4c79 100644 --- a/docs/multitenant/usecase01/pdb_secret.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_secret.yaml @@ -6,7 +6,7 @@ apiVersion: v1 kind: Secret metadata: name: pdb1-secret - namespace: oracle-database-operator-system + namespace: pdbnamespace type: Opaque data: sysadmin_user: ".....base64 encoded password...." diff --git a/docs/multitenant/provisioning/multinamespace/pdb_unplug.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_unplug.yaml similarity index 95% rename from docs/multitenant/provisioning/multinamespace/pdb_unplug.yaml rename to docs/multitenant/ords-based/provisioning/multinamespace/pdb_unplug.yaml index f3667dad..976d333b 100644 --- a/docs/multitenant/provisioning/multinamespace/pdb_unplug.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_unplug.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/ords_image.md b/docs/multitenant/ords-based/provisioning/ords_image.md similarity index 100% rename from docs/multitenant/provisioning/ords_image.md rename to docs/multitenant/ords-based/provisioning/ords_image.md diff --git a/docs/multitenant/provisioning/quickOKEcreation.md b/docs/multitenant/ords-based/provisioning/quickOKEcreation.md similarity index 100% rename from docs/multitenant/provisioning/quickOKEcreation.md rename to docs/multitenant/ords-based/provisioning/quickOKEcreation.md diff --git a/docs/multitenant/provisioning/singlenamespace/cdb_create.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml similarity index 95% rename from docs/multitenant/provisioning/singlenamespace/cdb_create.yaml rename to docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml index 01fc0a18..1e14c5d4 100644 --- a/docs/multitenant/provisioning/singlenamespace/cdb_create.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: CDB metadata: name: cdb-dev diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml new file mode 100644 index 00000000..db7b4438 --- /dev/null +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: cdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + ords_pwd: ".....base64 encoded password for ORDS_PUBLIC_USER" + sysadmin_pwd: ".....base64 encoded password for SYS" + cdbadmin_user: ".....base64 encoded username E.G. C##DBAPI_CDB_ADMIN" + cdbadmin_pwd: ".....base64 encoded password for cdbadmin_user" + webserver_user: ".....base64 encoded username for https users " + webserver_pwd: ".....base64 encoded password webserver_users" diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_clone.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml similarity index 100% rename from docs/multitenant/provisioning/singlenamespace/pdb_clone.yaml rename to docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_close.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml similarity index 95% rename from docs/multitenant/provisioning/singlenamespace/pdb_close.yaml rename to docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml index 5917d33a..5bca125a 100644 --- a/docs/multitenant/provisioning/singlenamespace/pdb_close.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_create.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml similarity index 95% rename from docs/multitenant/provisioning/singlenamespace/pdb_create.yaml rename to docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml index be3581ad..f6419e2e 100644 --- a/docs/multitenant/provisioning/singlenamespace/pdb_create.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase01/pdb_delete.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml similarity index 94% rename from docs/multitenant/usecase01/pdb_delete.yaml rename to docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml index c22b546a..aa10e5b4 100644 --- a/docs/multitenant/usecase01/pdb_delete.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase01/pdb_open.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml similarity index 95% rename from docs/multitenant/usecase01/pdb_open.yaml rename to docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml index 25fdccc4..ca5d1ab8 100644 --- a/docs/multitenant/usecase01/pdb_open.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_plug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml similarity index 96% rename from docs/multitenant/provisioning/singlenamespace/pdb_plug.yaml rename to docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml index 77c00b9c..9563c58a 100644 --- a/docs/multitenant/provisioning/singlenamespace/pdb_plug.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml new file mode 100644 index 00000000..6b500262 --- /dev/null +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: pdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + sysadmin_user: ".....base64 encoded pdb admin user...." + sysadmin_pwd: ".....base64 encoded pdb amdin password...." + webserver_user: ".....base64 encoded username https authentication" + webserver_pwd: ".....base64 encoded password webserver" + diff --git a/docs/multitenant/usecase02/pdb_unplug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml similarity index 95% rename from docs/multitenant/usecase02/pdb_unplug.yaml rename to docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml index 085d337e..b56a12a3 100644 --- a/docs/multitenant/usecase02/pdb_unplug.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/ords-based/usecase/README.md b/docs/multitenant/ords-based/usecase/README.md new file mode 100644 index 00000000..b6f5e590 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/README.md @@ -0,0 +1,112 @@ + + + +# Use case directory + +The use case directory contains the yaml files to test the multitenant controller functionalities: create ords pod and pdb operation *create / open / close / unplug / plug / delete / clone /map / parameter session* +In this exampl the cdb and pdbs resources are depolyed in different namespaces + +## Makefile helper + +Customizing yaml files (tns alias / credential / namespaces name etc...) is a long procedure prone to human error. A simple [makefile](../usecase/makefile) is available to quickly and safely configure yaml files with your system environment information. Just edit the [parameter file](../usecase/parameters.txt) before proceding. + +```text +[👉 CHECK PARAMETERS..................] +TNSALIAS...............:(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELA.... +ORDPWD.................:[Password for ORDS_PUBLIC_USER ] +SYSPWD.................:[SYS password] +WBUSER.................:[username for https authentication] +WBPASS.................:[password for https authentication] +PDBUSR.................:[pdb admin user] +PDBPWD.................:[pdb admin password] +CDBUSR.................:[cdb admin user e.g. C##DBAPI_CDB_ADMIN] +CDBPWD.................:[cdb admin password] +PDBNAMESPACE...........:[namespace for pdb] +CDBNAMESPACE...........:[namespace for cdb] +COMPANY................:oracle +APIVERSION.............:v4 ---> do not edit +``` + +⚠ **WARNING: The makefile is intended to speed up the usecase directory configuartion only, it is not supported, the editing and configuration of yaml files for production system is left up to the end user** + +### Pre requisistes: + +- Make sure that **kubectl** is properly configured. +- Make sure that all requirements listed in the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) +- Make sure that administrative user on the container database is configured as documented. + +### Commands + +Review your configuraton running ```make check```; if all the parameters are correct then you can proceed with yaml files and certificates generation + +By excuting command ```make operator``` You will have in your directory an operator yaml file with the WATCH LIST required to operate with multiple namespaces. +Note that the yaml file is not applyed; you need to manually execute ```kubectl apply -f oracle-database-operator.yaml```. + +```bash +make operator +``` +You can generate all the other yaml files for pdb life cycle management using ```make genyaml``` + +```bash +make genyaml +``` + +list of generated yaml files + +```text +-rw-r--r-- 1 mmalvezz g900 137142 Nov 13 09:35 oracle-database-operator.yaml +-rw-r--r-- 1 mmalvezz g900 321 Nov 13 10:27 create_cdb_secrets.yaml +-rw-r--r-- 1 mmalvezz g900 234 Nov 13 10:27 create_pdb_secrets.yaml +-rw-r--r-- 1 mmalvezz g900 381 Nov 13 10:27 pdbnamespace_binding.yaml +-rw-r--r-- 1 mmalvezz g900 381 Nov 13 10:27 cdbnamespace_binding.yaml +-rw-r--r-- 1 mmalvezz g900 1267 Nov 13 10:27 create_ords_pod.yaml +-rw-r--r-- 1 mmalvezz g900 935 Nov 13 10:27 create_pdb1_resource.yaml +-rw-r--r-- 1 mmalvezz g900 935 Nov 13 10:27 create_pdb2_resource.yaml +-rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 open_pdb1_resource.yaml +-rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 open_pdb2_resource.yaml +-rw-r--r-- 1 mmalvezz g900 845 Nov 13 10:27 open_pdb3_resource.yaml +-rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 close_pdb1_resource.yaml +-rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 close_pdb2_resource.yaml +-rw-r--r-- 1 mmalvezz g900 846 Nov 13 10:27 close_pdb3_resource.yaml +-rw-r--r-- 1 mmalvezz g900 927 Nov 13 10:27 clone_pdb1_resource.yaml +-rw-r--r-- 1 mmalvezz g900 928 Nov 13 10:27 clone_pdb2_resource.yaml +-rw-r--r-- 1 mmalvezz g900 802 Nov 13 10:27 delete_pdb1_resource.yaml +-rw-r--r-- 1 mmalvezz g900 802 Nov 13 10:27 delete_pdb2_resource.yaml +-rw-r--r-- 1 mmalvezz g900 824 Nov 13 10:27 unplug_pdb1_resource.yaml +-rw-r--r-- 1 mmalvezz g900 992 Nov 13 10:27 plug_pdb1_resource.yaml +-rw-r--r-- 1 mmalvezz g900 887 Nov 13 10:27 map_pdb1_resource.yaml +-rw-r--r-- 1 mmalvezz g900 887 Nov 13 10:27 map_pdb2_resource.yaml +-rw-r--r-- 1 mmalvezz g900 890 Nov 13 10:27 map_pdb3_resource.yaml +``` + +The command ```make secretes ``` will configure database secrets credential and certificates secretes + +```bash +make secrets +``` + + + +The makefile includes other different targets that can be used to test the various pdb operations available. E.g. + +```makefile +run03.2: + @$(call msg,"clone pdb2-->pdb4") + $(KUBECTL) apply -f $(PDBCLONE2) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"clone pdb2-->pdb4 completed") + $(KUBECTL) get pdb pdb3 -n $(PDBNAMESPACE) +``` +The target ```run03.2``` clones pdb2 into pdb4 and wait for ```$TEST_EXEC_TIMEOUT``` for the operation to complete. + +### Output executions:. + +```make secrets``` + +![image](../images/makesecrets_1_1.png) + + + +```make runall``` executes different pdb operations including the cdb controller creation + +![image](../images/makerunall.png) \ No newline at end of file diff --git a/docs/multitenant/ords-based/usecase/authsection01.yaml b/docs/multitenant/ords-based/usecase/authsection01.yaml new file mode 100644 index 00000000..2ddd7a53 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/authsection01.yaml @@ -0,0 +1,32 @@ + sysAdminPwd: + secret: + secretName: "cdb-secret" + key: "sysadmin_pwd" + ordsPwd: + secret: + secretName: "cdb-secret" + key: "ords_pwd" + cdbAdminUser: + secret: + secretName: "cdb-secret" + key: "cdbadmin_user" + cdbAdminPwd: + secret: + secretName: "cdb-secret" + key: "cdbadmin_pwd" + webServerUser: + secret: + secretName: "cdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "cdb-secret" + key: "webserver_pwd" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" diff --git a/docs/multitenant/ords-based/usecase/authsection02.yaml b/docs/multitenant/ords-based/usecase/authsection02.yaml new file mode 100644 index 00000000..78d1b1c5 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/authsection02.yaml @@ -0,0 +1,28 @@ + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/ca.crt b/docs/multitenant/ords-based/usecase/ca.crt new file mode 100644 index 00000000..b6218ca4 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjzCCAnegAwIBAgIUGYsc9F/MMqfWDuFGYK87jx9Wr7swDQYJKoZIhvcNAQEL +BQAwVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEVMBMG +A1UECgwMb3JhY2xlLCBJbmMuMRcwFQYDVQQDDA5vcmFjbGUgUm9vdCBDQTAeFw0y +NDEyMTExMDA5NTFaFw0yNTEyMTExMDA5NTFaMFcxCzAJBgNVBAYTAkNOMQswCQYD +VQQIDAJHRDELMAkGA1UEBwwCU1oxFTATBgNVBAoMDG9yYWNsZSwgSW5jLjEXMBUG +A1UEAwwOb3JhY2xlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC3H98eEWfMuvwKU/Jk6Lw3BRL+yZAJhK1XmLKNNrrqT9fa/rhHGHbc5a0H +l5GsqQ5hp85y5RX7XhwWcZG6ss/kU7LgKEWBKRs0vxwkHXdUfzrVXKmRIF6Qi4oI +7UCBGHUM1bvld9UnIJILnKC9dVzuUfIf5AEwILyJVJgFGozW8CM7vu+uv+S0mV6G +YTNxS7WeCjg0oU9pTV719/8kr51Y0pS557WpL/D/zugoa7zU/+3VjebpVkJBGCjP +xj26UUO64lnI/LCq3cqUvBXbTBRjwWs/T82SK1SKg4kJ6Z1jPMWJ2tVGU+OFAnp/ +c7nvRXSFlNaiqDUkP2Y+/YQhhrSJAgMBAAGjUzBRMB0GA1UdDgQWBBSTO0wHsvJC +AYCkZfwbS/B2RV+hOzAfBgNVHSMEGDAWgBSTO0wHsvJCAYCkZfwbS/B2RV+hOzAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAIdckJi19lVpUyYsoh +XNUfWRRe0Hc+B72xkojc2mD+YRMxSmR3VT1JPk8pBKh9O5IZQ8+k9Q3kR5Zy5UcZ +9dfoG2DyGswqU3FNZgygfN5bKX8HWoi/E4TaraNvlIbXNw0JQ55mYKdTCOXMUzKs +CpSak06SxvltlpiAvpNjZm/6sHC+lGfX6lzq3/7oDJdQHSyIYOmC7OBoO/tzTuvo +8x3i3ymkj3JZmIm9fSYxm6/yazwJSjDT92ccVkiGbr41H6mj6LarDGjYRHvrHg7p +TpkeU5QJfmMGoAxqbc7ibogvxXfoQVBt8rBcF1+wbHz0mI/6qu0A60AaiLLXpjoA +oUZV +-----END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase/ca.key b/docs/multitenant/ords-based/usecase/ca.key new file mode 100644 index 00000000..58377d76 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/ca.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3H98eEWfMuvwK +U/Jk6Lw3BRL+yZAJhK1XmLKNNrrqT9fa/rhHGHbc5a0Hl5GsqQ5hp85y5RX7XhwW +cZG6ss/kU7LgKEWBKRs0vxwkHXdUfzrVXKmRIF6Qi4oI7UCBGHUM1bvld9UnIJIL +nKC9dVzuUfIf5AEwILyJVJgFGozW8CM7vu+uv+S0mV6GYTNxS7WeCjg0oU9pTV71 +9/8kr51Y0pS557WpL/D/zugoa7zU/+3VjebpVkJBGCjPxj26UUO64lnI/LCq3cqU +vBXbTBRjwWs/T82SK1SKg4kJ6Z1jPMWJ2tVGU+OFAnp/c7nvRXSFlNaiqDUkP2Y+ +/YQhhrSJAgMBAAECggEBALYS6VtTAPqtG3YFW2d7g8/MqsNLk3vzQAptV3JSbWwV +MNCcHIsLxvsBHZYD3KJogFUWbI0OO1nvi0vBZZzUtyEVGzAGunyea195eLhr177a +tZt4g1w5a+yZ4QfZJR52O8MkK6sPB47csnj7JVLagyitr2iH4aqBkhOVWYURjeER +uaF1tJaOQqvYaGcHjnU0Zzm9DmNWTux2DEW9uvfK1ti9ASfI5DQLKn/+cjIEVzLs +qWj1xL72pUgFJ+74JURT3nxDVeFK/3l2xTWg6SIgii8Ttp0h0pjl7GDS38s4IsHq +48blsbj5I6yWG14Iw0AkzUHxxrHUjgUV8W3aa5ZZLmUCgYEA32tUVJLNZLxXPljO +GWCNX06uqt5X7Lg79QdMWOg3Yyx8Kw/xCUWCtLpT1v/QMI+Gs5W3EI8T6PQ6NJWQ +KrypiOzGzFtsqKpLhOLrRpV7Pp0Krwgbl0a/VQuhH9s8tkv0F1gTGV/dw0z7jtRF +nhvfCpFU/hNfcTA+j9lzKyXD80sCgYEA0dRC5ua1qqGlLAR68jGTkjv+6IBvbikZ +RLRZNsWrZX02B1UiXWcJyk5hKAOf1la0m+ZCcEP8VH0nP6ONTYfvJpzlpVHureEX +6spBfZIY5whgBYC2dFmaP/iVbcxX7qVMD3N1eMd43pcqgNaMgdHmKa9b5gNilezw +hv1s/IiRPvsCgYA7YCHMTCtrzyX8estjMSbBIn8HCAoj7h55ExR2OFin8aWaKCVt +ylxfPXmUlO22Sh6JUYuSSgQHkAh7+xeSVAtWSV7X5qAs/v0M9uWCH63eSQwS0jvK +61Fj/7A1cCVlaTAYmB6TSsf75FSic8WArHWNMGx/DZT61tB5mFekKug1mwKBgAsF +/rnItxSOx3Gnjpn4tssA8eRfEvuTlys/2kwBWH7NdDAwbczAZAhH09zZS+VjyrtB +/o2NVM1Pgeda/UpscMjzhzEHO+XQpIQGOiiRq1M/4mOHno5AQtVHhvosuRXlgzhw +lWRR/mybPnVkUB9l2pSoDMjptp0vxHcjCz29IvxPAoGBAN8MRq+Y7lPAwFMWm5P0 +Aa9e5ygHNeB3VzpdPJkGI4wtXbiCyGnoPDKNY1z0JpH9FLd70582r4AHL3noJ/c2 +hQMb6XSlGeW5R1GUCLvSK4Do5lsvrN6AgNOmaeexWkTYBzPx/2rBiD/rXtf800xI +oxFSaSzPDbDU1woVZU2M/XrQ +-----END PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase/ca.srl b/docs/multitenant/ords-based/usecase/ca.srl new file mode 100644 index 00000000..d2ec50af --- /dev/null +++ b/docs/multitenant/ords-based/usecase/ca.srl @@ -0,0 +1 @@ +3482FC9E27388FE655F7F6FC78C29F9C151AA952 diff --git a/docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml b/docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml new file mode 100644 index 00000000..5fd355f4 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 + namespace: cdbnamespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml new file mode 100644 index 00000000..c07d101e --- /dev/null +++ b/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml @@ -0,0 +1,46 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + srcPdbName: "pdbdev" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + action: "Clone" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml new file mode 100644 index 00000000..07889220 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml @@ -0,0 +1,46 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb4 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone2" + srcPdbName: "pdbprd" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + action: "Clone" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml new file mode 100644 index 00000000..915222fb --- /dev/null +++ b/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml new file mode 100644 index 00000000..4733bdf5 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml new file mode 100644 index 00000000..accb6978 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: ""new_clone" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/create_cdb_secrets.yaml b/docs/multitenant/ords-based/usecase/create_cdb_secrets.yaml new file mode 100644 index 00000000..c0f5cb64 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/create_cdb_secrets.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cdb-secret + namespace: cdbnamespace +type: Opaque +data: + ords_pwd: "TWFzY2V0dGkK" + sysadmin_pwd: "V0VsY29tZV8xMiMjCg==" + cdbadmin_user: "QyMjREJBUElfQ0RCX0FETUlOCg==" + cdbadmin_pwd: "V0VsY29tZV8xMiMjCg==" + webserver_user: "d2VsY29tZQo=" + webserver_pwd: "d2VsY29tZTEK" diff --git a/docs/multitenant/ords-based/usecase/create_ords_pod.yaml b/docs/multitenant/ords-based/usecase/create_ords_pod.yaml new file mode 100644 index 00000000..b031d662 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/create_ords_pod.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v4 +kind: CDB +metadata: + name: cdb-dev + namespace: cdbnamespace +spec: + cdbName: "DB12" + ordsImage: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/ords-dboper:latest + ordsImagePullPolicy: "Always" + dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + replicas: 1 + deletePdbCascade: true + sysAdminPwd: + secret: + secretName: "cdb-secret" + key: "sysadmin_pwd" + ordsPwd: + secret: + secretName: "cdb-secret" + key: "ords_pwd" + cdbAdminUser: + secret: + secretName: "cdb-secret" + key: "cdbadmin_user" + cdbAdminPwd: + secret: + secretName: "cdb-secret" + key: "cdbadmin_pwd" + webServerUser: + secret: + secretName: "cdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "cdb-secret" + key: "webserver_pwd" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" diff --git a/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml new file mode 100644 index 00000000..09b99269 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + assertivePdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml new file mode 100644 index 00000000..27381e0b --- /dev/null +++ b/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + assertivePdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/create_pdb_secrets.yaml b/docs/multitenant/ords-based/usecase/create_pdb_secrets.yaml new file mode 100644 index 00000000..d5491a8e --- /dev/null +++ b/docs/multitenant/ords-based/usecase/create_pdb_secrets.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: pdb-secret + namespace: pdbnamespace +type: Opaque +data: + sysadmin_user: "Q2l0aXplbmthbmUK" + sysadmin_pwd: "Um9zZWJ1ZAo=" + webserver_user: "d2VsY29tZQo=" + webserver_pwd: "d2VsY29tZTEK" diff --git a/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml new file mode 100644 index 00000000..f458350d --- /dev/null +++ b/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml @@ -0,0 +1,41 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml new file mode 100644 index 00000000..d4e362e8 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml @@ -0,0 +1,41 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + pdbName: "pdbprd" + action: "Delete" + dropAction: "INCLUDING" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/extfile.txt b/docs/multitenant/ords-based/usecase/extfile.txt new file mode 100644 index 00000000..1c65bfd6 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/extfile.txt @@ -0,0 +1 @@ +subjectAltName=DNS:cdb-dev-ords.cdbnamespace diff --git a/docs/multitenant/ords-based/usecase/makefile b/docs/multitenant/ords-based/usecase/makefile new file mode 100644 index 00000000..0df25e6d --- /dev/null +++ b/docs/multitenant/ords-based/usecase/makefile @@ -0,0 +1,906 @@ +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# __ __ _ __ _ _ +# | \/ | __ _| | _____ / _(_) | ___ +# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ +# | | | | (_| | < __/ _| | | __/ +# |_| |_|\__,_|_|\_\___|_| |_|_|\___| +# | | | | ___| |_ __ ___ _ __ +# | |_| |/ _ \ | '_ \ / _ \ '__| +# | _ | __/ | |_) | __/ | +# |_| |_|\___|_| .__/ \___|_| +# |_| +# +# WARNING: Using this makefile helps you to customize yaml +# files. Edit parameters.txt with your enviroment +# informartion and execute the following steps +# +# 1) make operator +# it configures the operator yaml files with the +# watch namelist required by the multitenant controllers +# +# 2) make genyaml +# It automatically creates all the yaml files based on the +# information available in the parameters file +# +# 3) make secrets +# It configure the required secrets necessary to operate +# with pdbs multitenant controllers +# +# +# LIST OF GENERAED YAML FILE +# +# ----------------------------- ---------------------------------- +# oracle-database-operator.yaml : oracle database operator +# cdbnamespace_binding.yaml : role binding for cdbnamespace +# pdbnamespace_binding.yaml : role binding for pdbnamespace +# create_cdb_secret.yaml : create secrets for ords server pod +# create_pdb_secret.yaml : create secrets for pluggable database +# create_ords_pod.yaml : create rest server pod +# create_pdb1_resource.yaml : create first pluggable database +# create_pdb2_resource.yaml : create second pluggable database +# open_pdb1_resource.yaml : open first pluggable database +# open_pdb2_resource.yaml : open second pluggable database +# close_pdb1_resource.yaml : close first pluggable database +# close_pdb2_resource.yaml : close second pluggable database +# clone_pdb_resource.yaml : clone thrid pluggable database +# clone_pdb2_resource.yaml : clone 4th pluggable database +# delete_pdb1_resource.yaml : delete first pluggable database +# delete_pdb2_resource.yaml : delete sencond pluggable database +# delete_pdb3_resource.yaml : delete thrid pluggable database +# unplug_pdb1_resource.yaml : unplug first pluggable database +# plug_pdb1_resource.yaml : plug first pluggable database +# map_pdb1_resource.yaml : map the first pluggable database +# config_map.yam : pdb parameters array +# +DATE := `date "+%y%m%d%H%M%S"` +###################### +# PARAMETER SECTIONS # +###################### + +export PARAMETERS=parameters.txt +export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) +export ORDPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDPWD|cut -d : -f 2) +export SYSPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep SYSPWD|cut -d : -f 2) +export WBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBUSER|cut -d : -f 2) +export WBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBPASS|cut -d : -f 2) +export PDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBUSR|cut -d : -f 2) +export PDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBPWD|cut -d : -f 2) +export CDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBUSR|cut -d : -f 2) +export CDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBPWD|cut -d : -f 2) +export PDBNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBNAMESPACE|cut -d : -f 2) +export CDBNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBNAMESPACE|cut -d : -f 2) +export ORDSIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDSIMG|cut -d : -f 2,3) +export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) +export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) +export OPRNAMESPACE=oracle-database-operator-system +export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml +export TEST_EXEC_TIMEOUT=3m + +REST_SERVER=ords +SKEY=tls.key +SCRT=tls.crt +CART=ca.crt +PRVKEY=ca.key +PUBKEY=public.pem +COMPANY=oracle + +################# +### FILE LIST ### +################# + +export ORDS_POD=create_ords_pod.yaml + +export CDB_SECRETS=create_cdb_secrets.yaml +export PDB_SECRETS=create_pdb_secrets.yaml + +export PDBCRE1=create_pdb1_resource.yaml +export PDBCRE2=create_pdb2_resource.yaml + +export PDBCLOSE1=close_pdb1_resource.yaml +export PDBCLOSE2=close_pdb2_resource.yaml +export PDBCLOSE3=close_pdb3_resource.yaml + +export PDBOPEN1=open_pdb1_resource.yaml +export PDBOPEN2=open_pdb2_resource.yaml +export PDBOPEN3=open_pdb3_resource.yaml + +export PDBCLONE1=clone_pdb1_resource.yaml +export PDBCLONE2=clone_pdb2_resource.yaml + +export PDBDELETE1=delete_pdb1_resource.yaml +export PDBDELETE2=delete_pdb2_resource.yaml +export PDBDELETE3=delete_pdb3_resource.yaml + +export PDBUNPLUG1=unplug_pdb1_resource.yaml +export PDBPLUG1=plug_pdb1_resource.yaml + +export PDBMAP1=map_pdb1_resource.yaml +export PDBMAP2=map_pdb2_resource.yaml +export PDBMAP3=map_pdb3_resource.yaml + +export PDBMAP1=map_pdb1_resource.yaml +export PDBMAP2=map_pdb2_resource.yaml +export PDBMAP3=map_pdb3_resource.yaml + + +##BINARIES +export KUBECTL=/usr/bin/kubectl +OPENSSL=/usr/bin/openssl +ECHO=/usr/bin/echo +RM=/usr/bin/rm +CP=/usr/bin/cp +TAR=/usr/bin/tar +MKDIR=/usr/bin/mkdir + +define msg +@printf "\033[31;7m%s\033[0m\r" "......................................]" +@printf "\033[31;7m[\xF0\x9F\x91\x89 %s\033[0m\n" $(1) +endef + +check: + $(call msg,"CHECK PARAMETERS") + @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) + @printf "ORDPWD.................:%s\n" $(ORDPWD) + @printf "SYSPWD.................:%s\n" $(SYSPWD) + @printf "WBUSER.................:%s\n" $(WBUSER) + @printf "WBPASS.................:%s\n" $(WBPASS) + @printf "PDBUSR.................:%s\n" $(PDBUSR) + @printf "PDBPWD.................:%s\n" $(PDBPWD) + @printf "CDBUSR.................:%s\n" $(CDBUSR) + @printf "CDBPWD.................:%s\n" $(CDBPWD) + @printf "PDBNAMESPACE...........:%s\n" $(PDBNAMESPACE) + @printf "CDBNAMESPACE...........:%s\n" $(CDBNAMESPACE) + @printf "COMPANY................:%s\n" $(COMPANY) + @printf "APIVERSION.............:%s\n" $(APIVERSION) + + +tlscrt: + $(call msg,"TLS GENERATION") + #$(OPENSSL) genrsa -out $(PRVKEY) 2048 + $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) + $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) \ + -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj \ + "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(CDBNAMESPACE)" -out server.csr + $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(CDBNAMESPACE)" > extfile.txt + $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) + $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) + +#- $(KUBECTL) delete secret db-tls -n $(CDBNAMESPACE) 2>/dev/null +#- $(KUBECTL) delete secret db-ca -n $(CDBNAMESPACE) 2>/dev/null +#- $(KUBECTL) delete secret db-tls -n $(PDBNAMESPACE) 2>/dev/null +#- $(KUBECTL) delete secret db-ca -n $(PDBNAMESPACE) 2>/dev/null + +tlssec: + $(call msg,"GENERATE TLS SECRET") + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDBNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(CDBNAMESPACE) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDBNAMESPACE) + +secrets: delsec tlscrt tlssec + $(call msg,"GENERATE CDB SECRET") + $(KUBECTL) apply -f $(CDB_SECRETS) + $(call msg,"GENERATE PDB SECRET") + $(KUBECTL) apply -f $(PDB_SECRETS) + + +delsec: + $(call msg,"CLEAN OLD SECRETS") + $(eval SECRETSP:=$(shell kubectl get secrets -n $(PDBNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) + $(eval SECRETSL:=$(shell kubectl get secrets -n $(CDBNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) + @[ "${SECRETSP}" ] && ( \ + printf "Deleteing secrets in namespace -n $(PDBNAMESPACE)\n") &&\ + ($(KUBECTL) delete secret $(SECRETSP) -n $(PDBNAMESPACE))\ + || ( echo "No screts in namespace $(PDBNAMESPACE)") + @[ "${SECRETSL}" ] && ( \ + printf "Deleteing secrets in namespace -n $(CDBNAMESPACE)\n") &&\ + ($(KUBECTL) delete secret $(SECRETSL) -n $(CDBNAMESPACE))\ + || ( echo "No screts in namespace $(PDBNAMESPACE)") + + + +### YAML FILE SECTION ### +define _opr +cp ${ORACLE_OPERATOR_YAML} . +export OPBASENAME=`basename ${ORACLE_OPERATOR_YAML}` +cp ${OPBASENAME} ${OPBASENAME}.ORIGNINAL +printf "\n\t\xF0\x9F\x91\x89 ${OPBASENAME}\n\n" +sed -i 's/value: ""/value: "${OPRNAMESPACE},${PDBNAMESPACE},${CDBNAMESPACE}"/g' ${OPBASENAME} +endef + +export opr = $(value _opr) + +operator: + @ eval "$$opr" + + +define _script00 +cat < authsection01.yaml + sysAdminPwd: + secret: + secretName: "cdb-secret" + key: "sysadmin_pwd" + ordsPwd: + secret: + secretName: "cdb-secret" + key: "ords_pwd" + cdbAdminUser: + secret: + secretName: "cdb-secret" + key: "cdbadmin_user" + cdbAdminPwd: + secret: + secretName: "cdb-secret" + key: "cdbadmin_pwd" + webServerUser: + secret: + secretName: "cdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "cdb-secret" + key: "webserver_pwd" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" +EOF + +cat< authsection02.yaml + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" +EOF + + +cat < ${PDBNAMESPACE}_binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 + namespace: ${PDBNAMESPACE} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +EOF + +cat < ${CDBNAMESPACE}_binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 + namespace: ${CDBNAMESPACE} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +EOF + +endef +export script00 = $(value _script00) +secyaml: + @ eval "$$script00" + +define _script_secrets +ORDS=`echo ${ORDPWD}|base64` +SYSA=`echo ${SYSPWD}|base64` +CDBU=`echo ${CDBUSR}|base64` +CDBP=`echo ${CDBPWD}|base64` +WEBU=`echo ${WBUSER}|base64` +WEBP=`echo ${WBPASS}|base64` +PDBU=`echo ${PDBUSR}|base64` +PDBP=`echo ${PDBPWD}|base64` +cat < ${CDB_SECRETS} +apiVersion: v1 +kind: Secret +metadata: + name: cdb-secret + namespace: ${CDBNAMESPACE} +type: Opaque +data: + ords_pwd: "${ORDS}" + sysadmin_pwd: "${SYSA}" + cdbadmin_user: "${CDBU}" + cdbadmin_pwd: "${CDBP}" + webserver_user: "${WEBU}" + webserver_pwd: "${WEBP}" +EOF + +cat < ${PDB_SECRETS} +apiVersion: v1 +kind: Secret +metadata: + name: pdb-secret + namespace: ${PDBNAMESPACE} +type: Opaque +data: + sysadmin_user: "${PDBU}" + sysadmin_pwd: "${PDBP}" + webserver_user: "${WEBU}" + webserver_pwd: "${WEBP}" +EOF + +endef + + +export script_secrets=$(value _script_secrets) + +dbsecr: + @ eval "$$script_secrets" + +#echo ords pod creation +define _script01 +cat < ${ORDS_POD} +apiVersion: database.oracle.com/${APIVERSION} +kind: CDB +metadata: + name: cdb-dev + namespace: cdbnamespace +spec: + cdbName: "DB12" + ordsImage: ${ORDSIMG} + ordsImagePullPolicy: "Always" + dbTnsurl : ${TNSALIAS} + replicas: 1 + deletePdbCascade: true +EOF + +cat authsection01.yaml >> ${ORDS_POD} + +endef +export script01 = $(value _script01) + + +define _script02 + +cat <${PDBCRE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + assertivePdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" +EOF + +cat < ${PDBCRE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + assertivePdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" +EOF + +cat <${PDBOPEN1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" +EOF + +cat <${PDBOPEN2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" +EOF + +cat <${PDBOPEN3} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb3 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" +EOF + +cat <${PDBCLOSE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" +EOF + +cat <${PDBCLOSE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" +EOF + +cat <${PDBCLOSE3} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb3 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: ""new_clone" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" +EOF + +cat < ${PDBCLONE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb3 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone" + srcPdbName: "pdbdev" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + action: "Clone" +EOF + +cat < ${PDBCLONE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb4 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone2" + srcPdbName: "pdbprd" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + action: "Clone" +EOF + + +cat < ${PDBDELETE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" +EOF + +cat < ${PDBDELETE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + pdbName: "pdbprd" + action: "Delete" + dropAction: "INCLUDING" +EOF + +cat < ${PDBUNPLUG1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "Unplug" +EOF + +cat <${PDBPLUG1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "plug" + fileNameConversions: "NONE" + sourceFileNameConversions: "NONE" + copyAction: "MOVE" + totalSize: "1G" + tempSize: "100M" + assertivePdbDeletion: true + action: "Plug" +EOF + +cat <${PDBMAP1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" +EOF + +cat <${PDBMAP2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" +EOF + + +cat <${PDBMAP3} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb3 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${CDBNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" +EOF + + +## Auth information +for _file in ${PDBCRE1} ${PDBCRE2} ${PDBOPEN1} ${PDBOPEN2} ${PDBOPEN3} ${PDBCLOSE1} ${PDBCLOSE2} ${PDBCLOSE3} ${PDBCLONE1} ${PDBCLONE2} ${PDBDELETE1} ${PDBDELETE2} ${PDBUNPLUG1} ${PDBPLUG1} ${PDBMAP1} ${PDBMAP2} ${PDBMAP3} +do +ls -ltr ${_file} + cat authsection02.yaml >> ${_file} +done +rm authsection02.yaml +rm authsection01.yaml +endef + +export script02 = $(value _script02) + +genyaml:dbsecr secyaml + @ eval "$$script01" + @ eval "$$script02" + +cleanyaml: + - $(RM) $(PDBMAP3) $(PDBMAP2) $(PDBMAP1) $(PDBPLUG1) $(PDBUNPLUG1) $(PDBDELETE2) $(PDBDELETE1) $(PDBCLONE2) $(PDBCLONE1) $(PDBCLOSE3) $(PDBCLOSE2) $(PDBCLOSE1) $(PDBOPEN3) $(PDBOPEN2) $(PDBOPEN1) $(PDBCRE2) $(PDBCRE1) $(ORDS_POD) $(CDB_SECRETS) $(PDB_SECRETS) + - $(RM) ${PDBNAMESPACE}_binding.yaml ${CDBNAMESPACE}_binding.yaml + + +cleancrt: + - $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl + + +################# +### PACKAGING ### +################# + +pkg: + - $(RM) -rf /tmp/pkgtestplan + $(MKDIR) /tmp/pkgtestplan + $(CP) -R * /tmp/pkgtestplan + $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ + $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan + +################ +### diag ### +################ + +login: + $(KUBECTL) exec `$(KUBECTL) get pods -n $(CDBNAMESPACE)|grep ords|cut -d ' ' -f 1` -n $(CDBNAMESPACE) -it -- /bin/bash + + +reloadop: + echo "RESTARTING OPERATOR" + $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) + $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) + $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) + $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + + +dump: + @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) + @$(eval DIAGFILE := ./opdmp.$(TMPSP)) + @>$(DIAGFILE) + @echo "OPERATOR DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + +####################################################### +#### TEST SECTION #### +####################################################### + +run00: + @$(call msg,"cdb pod creation") + - $(KUBECTL) delete cdb cdb-dev -n $(CDBNAMESPACE) + $(KUBECTL) apply -f $(ORDS_POD) + time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" cdb cdb-dev -n $(CDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"cdb pod completed") + $(KUBECTL) get cdb -n $(CDBNAMESPACE) + $(KUBECTL) get pod -n $(CDBNAMESPACE) + +run01.1: + @$(call msg,"pdb pdb1 creation") + $(KUBECTL) apply -f $(PDBCRE1) + time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb1 creation completed") + $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) + +run01.2: + @$(call msg, "pdb pdb2 creation") + $(KUBECTL) apply -f $(PDBCRE2) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb2 creation completed") + $(KUBECTL) get pdb pdb2 -n $(PDBNAMESPACE) + +run02.1: + @$(call msg, "pdb pdb1 open") + $(KUBECTL) apply -f $(PDBOPEN1) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb1 open completed") + $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) + +run02.2: + @$(call msg,"pdb pdb2 open") + $(KUBECTL) apply -f $(PDBOPEN2) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"pdb pdb2 open completed") + $(KUBECTL) get pdb pdb2 -n $(PDBNAMESPACE) + + +run03.1: + @$(call msg,"clone pdb1-->pdb3") + $(KUBECTL) apply -f $(PDBCLONE1) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb3 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"clone pdb1-->pdb3 completed") + $(KUBECTL) get pdb pdb3 -n $(PDBNAMESPACE) + + +run03.2: + @$(call msg,"clone pdb2-->pdb4") + $(KUBECTL) apply -f $(PDBCLONE2) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"clone pdb2-->pdb4 completed") + $(KUBECTL) get pdb pdb3 -n $(PDBNAMESPACE) + + +run04.1: + @$(call msg,"pdb pdb1 close") + $(KUBECTL) apply -f $(PDBCLOSE1) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb1 close completed") + $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) + +run04.2: + @$(call msg,"pdb pdb2 close") + $(KUBECTL) apply -f $(PDBCLOSE2) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"pdb pdb2 close completed") + $(KUBECTL) get pdb pdb2 -n $(PDBNAMESPACE) + +run05.1: + @$(call msg,"pdb pdb1 unplug") + $(KUBECTL) apply -f $(PDBUNPLUG1) + $(KUBECTL) wait --for=delete pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"pdb pdb1 unplug completed") + +run06.1: + @$(call msg, "pdb pdb1 plug") + $(KUBECTL) apply -f $(PDBPLUG1) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb1 plug completed") + $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) + +run07.1: + @$(call msg,"pdb pdb1 delete ") + - $(KUBECTL) apply -f $(PDBCLOSE1) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) apply -f $(PDBDELETE1) + $(KUBECTL) wait --for=delete pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"pdb pdb1 delete") + $(KUBECTL) get pdb -n $(PDBNAMESPACE) + +run99.1: + $(KUBECTL) delete cdb cdb-dev -n cdbnamespace + $(KUBECTL) wait --for=delete cdb cdb-dev -n $(CDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) get cdb -n cdbnamespaace + $(KUBECTL) get pdb -n pdbnamespaace + + +## SEQ | ACTION +## ----+---------------- +## 00 | create ords pod +## 01 | create pdb +## 02 | open pdb +## 03 | clone pdb +## 04 | close pdb +## 05 | unpug pdb +## 06 | plug pdb +## 07 | delete pdb (declarative) + + +runall01: run00 run01.1 run01.2 run03.1 run03.2 run04.1 run05.1 run06.1 run02.1 run07.1 + + + diff --git a/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml new file mode 100644 index 00000000..16815cf6 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml @@ -0,0 +1,45 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml new file mode 100644 index 00000000..d8dd6acc --- /dev/null +++ b/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml @@ -0,0 +1,45 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml new file mode 100644 index 00000000..2f561814 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml @@ -0,0 +1,45 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml new file mode 100644 index 00000000..3507ade3 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml new file mode 100644 index 00000000..1498f135 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbprd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml new file mode 100644 index 00000000..bd0f358a --- /dev/null +++ b/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "new_clone" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml new file mode 100644 index 00000000..45cfefbc --- /dev/null +++ b/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml @@ -0,0 +1,5057 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomouscontainerdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousContainerDatabase + listKind: AutonomousContainerDatabaseList + plural: autonomouscontainerdatabases + shortNames: + - acd + - acds + singular: autonomouscontainerdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + properties: + lifecycleState: + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabasebackups.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseBackup + listKind: AutonomousDatabaseBackupList + plural: autonomousdatabasebackups + shortNames: + - adbbu + - adbbus + singular: autonomousdatabasebackup + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + type: string + timeEnded: + type: string + timeStarted: + type: string + type: + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabaserestores.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseRestore + listKind: AutonomousDatabaseRestoreList + plural: autonomousdatabaserestores + shortNames: + - adbr + - adbrs + singular: autonomousdatabaserestore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + type: string + type: object + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + properties: + dbName: + type: string + displayName: + type: string + status: + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: autonomousdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabase + listKind: AutonomousDatabaseList + plural: autonomousdatabases + shortNames: + - adb + - adbs + singular: autonomousdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + details: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sACD: + properties: + name: + type: string + type: object + ociACD: + properties: + ocid: + type: string + type: object + type: object + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + lifecycleState: + type: string + networkAccess: + properties: + accessControlList: + items: + type: string + type: array + accessType: + enum: + - "" + - PUBLIC + - RESTRICTED + - PRIVATE + type: string + isAccessControlEnabled: + type: boolean + isMTLSConnectionRequired: + type: boolean + privateEndpoint: + properties: + hostnamePrefix: + type: string + nsgOCIDs: + items: + type: string + type: array + subnetOCID: + type: string + type: object + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + type: object + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + required: + - details + type: object + status: + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: cdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: CDB + listKind: CDBList + plural: cdbs + singular: cdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + ordsImage: + type: string + ordsImagePullPolicy: + enum: + - Always + - Never + type: string + ordsImagePullSecret: + type: string + ordsPort: + type: integer + ordsPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + configuration: + properties: + configmap: + properties: + configmapName: + type: string + key: + type: string + type: object + type: object + image: + type: string + service: + properties: + port: + format: int32 + type: integer + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + labels: + additionalProperties: + type: string + type: object + port: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + required: + - conditions + - exporterConfig + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dataguardbrokers.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + singular: dataguardbroker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailOver: + properties: + enable: + type: boolean + strategy: + items: + properties: + sourceDatabaseRef: + type: string + targetDatabaseRefs: + type: string + type: object + type: array + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: + type: string + externalConnectString: + type: string + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: dbcssystems.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: dbcssystems + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - sshPublicKeys + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + ociConfigMap: + type: string + ociSecret: + type: string + required: + - ociConfigMap + type: object + status: + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbEdition: + type: string + dbInfo: + items: + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: lrests.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LREST + listKind: LRESTList + plural: lrests + singular: lrest + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the LREST + jsonPath: .spec.cdbName + name: CDB NAME + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the LREST Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message if any + jsonPath: .status.msg + name: Message + type: string + - description: string of the tnsalias + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + lrestImage: + type: string + lrestImagePullPolicy: + enum: + - Always + - Never + type: string + lrestImagePullSecret: + type: string + lrestPort: + type: integer + lrestPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + nodeSelector: + additionalProperties: + type: string + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: lrpdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LRPDB + listKind: LRPDBList + plural: lrpdbs + singular: lrpdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the LRPDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: last sqlcode + jsonPath: .status.sqlCode + name: last sqlcode + type: integer + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + - Alter + - Noaction + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbPass: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + alterSystem: + type: string + alterSystemParameter: + type: string + alterSystemValue: + type: string + asClone: + type: boolean + assertiveLrpdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + lrpdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + lrpdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + parameterScope: + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + - ALTER + type: string + pdbconfigmap: + type: string + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + - alterSystemParameter + - alterSystemValue + - webServerPwd + type: object + status: + properties: + action: + type: string + alterSystem: + type: string + bitstat: + type: integer + bitstatstr: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + sqlCode: + type: integer + status: + type: boolean + totalSize: + type: string + required: + - phase + - sqlCode + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: oraclerestdataservices.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestDataService + listKind: OracleRestDataServiceList + plural: oraclerestdataservices + singular: oraclerestdataservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + apexPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: pdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB + listKind: PDBList + plural: pdbs + singular: pdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + type: boolean + assertivePdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + type: object + status: + properties: + action: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + status: + type: boolean + totalSize: + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: shardingdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: ShardingDatabase + listKind: ShardingDatabaseList + plural: shardingdatabases + singular: shardingdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.14.0 + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + createAs: + enum: + - primary + - standby + - clone + type: string + dgBrokerConfigured: + type: boolean + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + required: + - image + type: object + status: + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBrokerConfigured: + type: boolean + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: oracle-database-operator-leader-election-role + namespace: oracle-database-operator-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - deployments + - events + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - events + - pods + - pods/exec + - pods/log + - replicasets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list +- apiGroups: + - '''''' + resources: + - statefulsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - configmaps + - deployments + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases/status + verbs: + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - cdbs/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - events + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrests/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - lrests/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - lrpdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - lrpdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - pdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - prometheusrules + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/finalizers + verbs: + - update +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get + - patch + - update +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-oracle-database-operator-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: oracle-database-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-oracle-database-operator-proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager-metrics-service + namespace: oracle-database-operator-system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: v1 +kind: Service +metadata: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: oracle-database-operator-serving-cert + namespace: oracle-database-operator-system +spec: + dnsNames: + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local + issuerRef: + kind: Issuer + name: oracle-database-operator-selfsigned-issuer + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: oracle-database-operator-selfsigned-issuer + namespace: oracle-database-operator-system +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: mautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: mdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: moraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: msingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-cdb + failurePolicy: Fail + name: mcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: mlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: mlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-pdb + failurePolicy: Fail + name: mpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + failurePolicy: Fail + name: vautonomouscontainerdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomouscontainerdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: vautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore + failurePolicy: Fail + name: vautonomousdatabaserestore.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabaserestores + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: vdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: voraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: vsingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-cdb + failurePolicy: Fail + name: vcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrest + failurePolicy: Fail + name: vlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None +- admissionReviewVersions: + - v4 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-lrpdb + failurePolicy: Fail + name: vlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-pdb + failurePolicy: Fail + name: vpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager + namespace: oracle-database-operator-system +spec: + replicas: 3 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --enable-leader-election + command: + - /manager + env: + - name: WATCH_NAMESPACE + value: "${OPRNAMESPACE},${PDBNAMESPACE},${CDBNAMESPACE}" + image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/mn_lrestbranch:latest + imagePullPolicy: Always + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + resources: + limits: + cpu: 400m + memory: 400Mi + requests: + cpu: 400m + memory: 400Mi + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert +--- diff --git a/docs/multitenant/ords-based/usecase/parameters.txt b/docs/multitenant/ords-based/usecase/parameters.txt new file mode 100644 index 00000000..0dd66f73 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/parameters.txt @@ -0,0 +1,61 @@ + +######################## +## REST SERVER IMAGE ### +######################## + +ORDSIMG:lin.ocir.io/intsanjaysingh/mmalvezz/testppr/ords-dboper:latest + +############################## +## TNS URL FOR CDB CREATION ## +############################## +TNSALIAS:"(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + +########################################### +## ORDS PUBLIC USER ## +########################################### +ORDPWD:Mascetti + +########################################### +## SYSPASSWORD ## +########################################### +SYSPWD:WElcome_12## + +####################### +## HTTPS CREDENTIAL ### +####################### + +WBUSER:welcome +WBPASS:welcome1 + +##################### +## PDB ADMIN USER ### +##################### + +PDBUSR:Citizenkane +PDBPWD:Rosebud + +##################### +## CDB ADMIN USER ### +##################### + +CDBUSR:C##DBAPI_CDB_ADMIN +CDBPWD:WElcome_12## + +################### +### NAMESPACES #### +################### + +PDBNAMESPACE:pdbnamespace +CDBNAMESPACE:cdbnamespace + +#################### +### COMPANY NAME ### +#################### + +COMPANY:oracle + +#################### +### APIVERSION ### +#################### + +APIVERSION:v4 diff --git a/docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml b/docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml new file mode 100644 index 00000000..5af79ed6 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 + namespace: pdbnamespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml new file mode 100644 index 00000000..ab192655 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml @@ -0,0 +1,49 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "plug" + fileNameConversions: "NONE" + sourceFileNameConversions: "NONE" + copyAction: "MOVE" + totalSize: "1G" + tempSize: "100M" + assertivePdbDeletion: true + action: "Plug" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/public.pem b/docs/multitenant/ords-based/usecase/public.pem new file mode 100644 index 00000000..a83e254c --- /dev/null +++ b/docs/multitenant/ords-based/usecase/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtx/fHhFnzLr8ClPyZOi8 +NwUS/smQCYStV5iyjTa66k/X2v64Rxh23OWtB5eRrKkOYafOcuUV+14cFnGRurLP +5FOy4ChFgSkbNL8cJB13VH861VypkSBekIuKCO1AgRh1DNW75XfVJyCSC5ygvXVc +7lHyH+QBMCC8iVSYBRqM1vAjO77vrr/ktJlehmEzcUu1ngo4NKFPaU1e9ff/JK+d +WNKUuee1qS/w/87oKGu81P/t1Y3m6VZCQRgoz8Y9ulFDuuJZyPywqt3KlLwV20wU +Y8FrP0/NkitUioOJCemdYzzFidrVRlPjhQJ6f3O570V0hZTWoqg1JD9mPv2EIYa0 +iQIDAQAB +-----END PUBLIC KEY----- diff --git a/docs/multitenant/ords-based/usecase/server.csr b/docs/multitenant/ords-based/usecase/server.csr new file mode 100644 index 00000000..2a85d7c1 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/server.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICpzCCAY8CAQAwYjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQH +DAJTWjEVMBMGA1UECgwMb3JhY2xlLCBJbmMuMSIwIAYDVQQDDBljZGItZGV2LW9y +ZHMuY2RibmFtZXNwYWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +x36MZ8tRa9fsJk/J7CwCYN8tji+/ZNea2MQkpG01vA/8pKcTWzxRw4R+GOyNiOQ/ +K006HPj9jd5vkwlHUw0jh8TdUZfEVo6+j3RlAma4cRtQt+5GPCok1pzOGb5+FvSD +FF8YmE69Ao6aW+9U+Xg7u2skgSULN3rCOF1ADoBqRreuZuArxLvqxqMtQqrxwZO1 +3spap4rTLeahbSHaNrIZ3WVGFMGux+3zHMKnANEEo9Td3FQFsD4dZxCbdXIaqlFT +mowMjb0VG5t357aSfosPUFFxr/5lrsWeyKBNRyP0xUjwwDdEJOD49NKWpxew0+IZ +ZcfZUhcOXkXAfxSHuDGhlwIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAH6eOsDr +MBHHvrXujkXCDuwvkFwt26tqTGK4H1VaMlZ74aPABlrn0bgc9FhMS9Q1F/XiAVXB +pEmMde3wI9ZajO6g6FjYuLJZ2zhW37Mwf5Er8X2T+RsTLC8Df/8owTwUR0UqOkpb +vkmOpiWFz1aft0ir9JYpc/6pBJLJSKgdMBZVMVLOk/Ld9uJcpW0PW23DCCSYNcZN +e3UGz5Irjrnh9ivQm+8Xa/M8A7strjuDi2QSHMmioXQA9L6yLCxicxnLpE/uJnKh +ysxiUl87S+1jeiI5jagnYPLEYlGD/m/SyEmeYrblbFaGhkyIFGmzccbMQUeNjg/c +XJ4BZno1eyFlvOk= +-----END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/ords-based/usecase/tls.crt b/docs/multitenant/ords-based/usecase/tls.crt new file mode 100644 index 00000000..cf77a607 --- /dev/null +++ b/docs/multitenant/ords-based/usecase/tls.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbzCCAlegAwIBAgIUNIL8nic4j+ZV9/b8eMKfnBUaqVIwDQYJKoZIhvcNAQEL +BQAwVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEVMBMG +A1UECgwMb3JhY2xlLCBJbmMuMRcwFQYDVQQDDA5vcmFjbGUgUm9vdCBDQTAeFw0y +NDEyMTExMDA5NTFaFw0yNTEyMTExMDA5NTFaMGIxCzAJBgNVBAYTAkNOMQswCQYD +VQQIDAJHRDELMAkGA1UEBwwCU1oxFTATBgNVBAoMDG9yYWNsZSwgSW5jLjEiMCAG +A1UEAwwZY2RiLWRldi1vcmRzLmNkYm5hbWVzcGFjZTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMd+jGfLUWvX7CZPyewsAmDfLY4vv2TXmtjEJKRtNbwP +/KSnE1s8UcOEfhjsjYjkPytNOhz4/Y3eb5MJR1MNI4fE3VGXxFaOvo90ZQJmuHEb +ULfuRjwqJNaczhm+fhb0gxRfGJhOvQKOmlvvVPl4O7trJIElCzd6wjhdQA6Aaka3 +rmbgK8S76sajLUKq8cGTtd7KWqeK0y3moW0h2jayGd1lRhTBrsft8xzCpwDRBKPU +3dxUBbA+HWcQm3VyGqpRU5qMDI29FRubd+e2kn6LD1BRca/+Za7FnsigTUcj9MVI +8MA3RCTg+PTSlqcXsNPiGWXH2VIXDl5FwH8Uh7gxoZcCAwEAAaMoMCYwJAYDVR0R +BB0wG4IZY2RiLWRldi1vcmRzLmNkYm5hbWVzcGFjZTANBgkqhkiG9w0BAQsFAAOC +AQEAoi4TgBrCy+pEDH2d0GVx/Mqn3iDPeCH8VFYcLRK8NIjHiHIZtp1Y32Z3Xp5W +Xaor3SvBn00Rsy9y4Q2zBxx0pY0Tkub6nsm8cMGMpd9oBMuQ4ecTnuui3sjuA+au +Fv5+Qrb20rdJi4nbYFM17OuhQWrqpORtCfCk6rVLrj4zpejzd6r/wBZr2JgKJ4r5 +nTb/c2uUNO+ImU+XTCWTzOTq2XdXkPeMhfgBlrUB5yhDgCsx6IsE7Br/4MkbrLc9 +/1ESu5cNWP7lBQz5pp+uwgJdlVJwfqYvf2zVy9XA9l65DgGhyt135WGPDZfjqjtJ +GTPZyO88lA4eqvk7xFjpEYbSMw== +-----END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase/tls.key b/docs/multitenant/ords-based/usecase/tls.key new file mode 100644 index 00000000..4418929f --- /dev/null +++ b/docs/multitenant/ords-based/usecase/tls.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHfoxny1Fr1+wm +T8nsLAJg3y2OL79k15rYxCSkbTW8D/ykpxNbPFHDhH4Y7I2I5D8rTToc+P2N3m+T +CUdTDSOHxN1Rl8RWjr6PdGUCZrhxG1C37kY8KiTWnM4Zvn4W9IMUXxiYTr0Cjppb +71T5eDu7aySBJQs3esI4XUAOgGpGt65m4CvEu+rGoy1CqvHBk7XeylqnitMt5qFt +Ido2shndZUYUwa7H7fMcwqcA0QSj1N3cVAWwPh1nEJt1chqqUVOajAyNvRUbm3fn +tpJ+iw9QUXGv/mWuxZ7IoE1HI/TFSPDAN0Qk4Pj00panF7DT4hllx9lSFw5eRcB/ +FIe4MaGXAgMBAAECggEBAL/SBt+muFuh+we7oMTebEsJTZxyfO5SAip8L1+LQyfZ +l2wa2pnXnicJttXwXwdJsRPJ5h8Fq4hGYbJYMaTB9XAO82AJNlikWMdHe/ibHK5l +PXAeRJqFS3awv9wxNryLz54+8j9BNqzGwdW0g1h9K7JCi49CN07ZUvcvcvB+ubHU +KHOqbf0kc31CwEJSvpjaBrq+4MQy/NNqcsXHlDZKxg087Poyz4PFRUGW0Zj+jrkX +j3+HGS+ZRdYWqk0zCQfeGeOhgo+LEawYr63u3BgiOpUmGGnE/a20q3WphG4eTeNs +ZjTvn0KfHli6NmkYL6GGNLqQCYeGHxXa8LGYhFAhHgECgYEA9PUPKkCeJC23jjAB +fB+o2RA7ySIcrA08WqHg2V3f4o14Un7gjsEMSE+9E/PkDSPlDpwtG3m2z+xV+2V7 +yJpvKVVrfeC95JiKKyJIkr8hkOTQPb2P53jH20/nu6JlrHFcVT4P9tzfWwI+L3zS +ktulH3hnKWfCJiq7b9Zl6d6mybcCgYEA0HzSdjER2Ie8NuQLkoKV0APduTDZ0fPR +iyPOVhouoKTFKbN0eMG8OS/FkdD2jitxzk/Sk0gZ0lc4l6lnbThRDAF7RXMf4RYN +1u7XGEGteN1yIgpMA1nk1OHFsLb26cd73APNtzibqVsIClUkjKgaTmQMEulCtn4/ +s6Jv7ua/ZyECgYAi7rek7OYgfqyIiGwNyW4I1nnQKx8voguTWTZ5iRuecVJRYWqg +sQMRZN2v9L326cGGndsI6giaCjgQy94lM3Da0iWsI9qwuNgrYo467yrYQOk5hMj6 +14yYyAJiDby2JMxoE5uvNrxbfCxmFUTqUIxxunfrvbJAQyPLxSVvfqMGbQKBgQDI +v2fnZlhIpmoUcIC0Egkuwoo8R011W8/oqhGD1rIO/JjcDm+X1vPmHJmgThUZz7tF +bJbWOhai8lkLr5JnGFLxvHt14+ROV238Bv4V3SeruY6sOD34/BXa5Yn1Hjeh4vLg +ZskuItO/vmd/i3wl87dgw/TcQL57+hRcY4xj/MtQAQKBgFwkwEnQ3Q46stQRzeBc +zW5w3ABcCmc89MKwMF9vwzRFgrD1pH0sCREbHi599QhJN9KE4wmjomqK6J6r6mT7 +rWN+v80Nk35BIM/DXTRfo3vhzWVY35alwUo1Ur30tayJJ9PXhEBUWSCYtbhuc+Ey +W5zt+XiVe9vmpZ+qm5mqKA1Q +-----END PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml new file mode 100644 index 00000000..12627c6b --- /dev/null +++ b/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml @@ -0,0 +1,42 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "Unplug" + adminName: + secret: + secretName: "pdb-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb-secret" + key: "webserver_pwd" diff --git a/docs/multitenant/usecase01/README.md b/docs/multitenant/ords-based/usecase01/README.md similarity index 100% rename from docs/multitenant/usecase01/README.md rename to docs/multitenant/ords-based/usecase01/README.md diff --git a/docs/multitenant/usecase01/ca.crt b/docs/multitenant/ords-based/usecase01/ca.crt similarity index 100% rename from docs/multitenant/usecase01/ca.crt rename to docs/multitenant/ords-based/usecase01/ca.crt diff --git a/docs/multitenant/usecase01/ca.key b/docs/multitenant/ords-based/usecase01/ca.key similarity index 100% rename from docs/multitenant/usecase01/ca.key rename to docs/multitenant/ords-based/usecase01/ca.key diff --git a/docs/multitenant/usecase01/ca.srl b/docs/multitenant/ords-based/usecase01/ca.srl similarity index 100% rename from docs/multitenant/usecase01/ca.srl rename to docs/multitenant/ords-based/usecase01/ca.srl diff --git a/docs/multitenant/usecase01/cdb_create.yaml b/docs/multitenant/ords-based/usecase01/cdb_create.yaml similarity index 95% rename from docs/multitenant/usecase01/cdb_create.yaml rename to docs/multitenant/ords-based/usecase01/cdb_create.yaml index 01fc0a18..1e14c5d4 100644 --- a/docs/multitenant/usecase01/cdb_create.yaml +++ b/docs/multitenant/ords-based/usecase01/cdb_create.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: CDB metadata: name: cdb-dev diff --git a/docs/multitenant/provisioning/singlenamespace/cdb_secret.yaml b/docs/multitenant/ords-based/usecase01/cdb_secret.yaml similarity index 100% rename from docs/multitenant/provisioning/singlenamespace/cdb_secret.yaml rename to docs/multitenant/ords-based/usecase01/cdb_secret.yaml diff --git a/docs/multitenant/usecase01/extfile.txt b/docs/multitenant/ords-based/usecase01/extfile.txt similarity index 100% rename from docs/multitenant/usecase01/extfile.txt rename to docs/multitenant/ords-based/usecase01/extfile.txt diff --git a/docs/multitenant/usecase01/logfiles/BuildImage.log b/docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log similarity index 100% rename from docs/multitenant/usecase01/logfiles/BuildImage.log rename to docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log diff --git a/docs/multitenant/usecase01/logfiles/ImagePush.log b/docs/multitenant/ords-based/usecase01/logfiles/ImagePush.log similarity index 100% rename from docs/multitenant/usecase01/logfiles/ImagePush.log rename to docs/multitenant/ords-based/usecase01/logfiles/ImagePush.log diff --git a/docs/multitenant/usecase01/logfiles/cdb.log b/docs/multitenant/ords-based/usecase01/logfiles/cdb.log similarity index 100% rename from docs/multitenant/usecase01/logfiles/cdb.log rename to docs/multitenant/ords-based/usecase01/logfiles/cdb.log diff --git a/docs/multitenant/usecase01/logfiles/cdb_creation.log b/docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log similarity index 100% rename from docs/multitenant/usecase01/logfiles/cdb_creation.log rename to docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log diff --git a/docs/multitenant/usecase01/logfiles/openssl_execution.log b/docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log similarity index 100% rename from docs/multitenant/usecase01/logfiles/openssl_execution.log rename to docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log diff --git a/docs/multitenant/usecase01/logfiles/ordsconfig.log b/docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log similarity index 100% rename from docs/multitenant/usecase01/logfiles/ordsconfig.log rename to docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log diff --git a/docs/multitenant/usecase01/logfiles/tagandpush.log b/docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log similarity index 100% rename from docs/multitenant/usecase01/logfiles/tagandpush.log rename to docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log diff --git a/docs/multitenant/usecase01/logfiles/testapi.log b/docs/multitenant/ords-based/usecase01/logfiles/testapi.log similarity index 100% rename from docs/multitenant/usecase01/logfiles/testapi.log rename to docs/multitenant/ords-based/usecase01/logfiles/testapi.log diff --git a/docs/multitenant/usecase01/makefile b/docs/multitenant/ords-based/usecase01/makefile similarity index 98% rename from docs/multitenant/usecase01/makefile rename to docs/multitenant/ords-based/usecase01/makefile index d4176c75..cf8b8470 100644 --- a/docs/multitenant/usecase01/makefile +++ b/docs/multitenant/ords-based/usecase01/makefile @@ -137,9 +137,9 @@ URLPATH=/_/db-api/stable/database/pdbs/ OPENSSL=/usr/bin/openssl ORDSPORT=8888 MAKE=/usr/bin/make -DOCKERFILE=../../../ords/Dockerfile -RUNSCRIPT=../../../ords/runOrdsSSL.sh -ORDSIMGDIR=../../../ords +DOCKERFILE=../../../../ords/Dockerfile +RUNSCRIPT=../../../../ords/runOrdsSSL.sh +ORDSIMGDIR=../../../../ords RM=/usr/bin/rm CP=/usr/bin/cp ECHO=/usr/bin/echo @@ -181,6 +181,9 @@ checkstep9: checkcdb createimage: $(DOCKER) build -t $(IMAGE) $(ORDSIMGDIR) +createimageproxy: + $(DOCKER) build -t $(IMAGE) $(ORDSIMGDIR) --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) + tagimage: @echo "TAG IMAGE" $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) diff --git a/docs/multitenant/usecase01/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml similarity index 100% rename from docs/multitenant/usecase01/oracle-database-operator.yaml rename to docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml diff --git a/docs/multitenant/usecase01/pdb_close.yaml b/docs/multitenant/ords-based/usecase01/pdb_close.yaml similarity index 95% rename from docs/multitenant/usecase01/pdb_close.yaml rename to docs/multitenant/ords-based/usecase01/pdb_close.yaml index 5917d33a..5bca125a 100644 --- a/docs/multitenant/usecase01/pdb_close.yaml +++ b/docs/multitenant/ords-based/usecase01/pdb_close.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase01/pdb_create.yaml b/docs/multitenant/ords-based/usecase01/pdb_create.yaml similarity index 95% rename from docs/multitenant/usecase01/pdb_create.yaml rename to docs/multitenant/ords-based/usecase01/pdb_create.yaml index be3581ad..f6419e2e 100644 --- a/docs/multitenant/usecase01/pdb_create.yaml +++ b/docs/multitenant/ords-based/usecase01/pdb_create.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_delete.yaml b/docs/multitenant/ords-based/usecase01/pdb_delete.yaml similarity index 94% rename from docs/multitenant/provisioning/singlenamespace/pdb_delete.yaml rename to docs/multitenant/ords-based/usecase01/pdb_delete.yaml index c22b546a..aa10e5b4 100644 --- a/docs/multitenant/provisioning/singlenamespace/pdb_delete.yaml +++ b/docs/multitenant/ords-based/usecase01/pdb_delete.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase01/pdb_map.yaml b/docs/multitenant/ords-based/usecase01/pdb_map.yaml similarity index 95% rename from docs/multitenant/usecase01/pdb_map.yaml rename to docs/multitenant/ords-based/usecase01/pdb_map.yaml index 3300a7fa..ccda2aa4 100644 --- a/docs/multitenant/usecase01/pdb_map.yaml +++ b/docs/multitenant/ords-based/usecase01/pdb_map.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_open.yaml b/docs/multitenant/ords-based/usecase01/pdb_open.yaml similarity index 95% rename from docs/multitenant/provisioning/singlenamespace/pdb_open.yaml rename to docs/multitenant/ords-based/usecase01/pdb_open.yaml index 25fdccc4..ca5d1ab8 100644 --- a/docs/multitenant/provisioning/singlenamespace/pdb_open.yaml +++ b/docs/multitenant/ords-based/usecase01/pdb_open.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_secret.yaml b/docs/multitenant/ords-based/usecase01/pdb_secret.yaml similarity index 100% rename from docs/multitenant/provisioning/singlenamespace/pdb_secret.yaml rename to docs/multitenant/ords-based/usecase01/pdb_secret.yaml diff --git a/docs/multitenant/usecase01/server.csr b/docs/multitenant/ords-based/usecase01/server.csr similarity index 100% rename from docs/multitenant/usecase01/server.csr rename to docs/multitenant/ords-based/usecase01/server.csr diff --git a/docs/multitenant/usecase01/tde_secret.yaml b/docs/multitenant/ords-based/usecase01/tde_secret.yaml similarity index 100% rename from docs/multitenant/usecase01/tde_secret.yaml rename to docs/multitenant/ords-based/usecase01/tde_secret.yaml diff --git a/docs/multitenant/usecase01/tls.crt b/docs/multitenant/ords-based/usecase01/tls.crt similarity index 100% rename from docs/multitenant/usecase01/tls.crt rename to docs/multitenant/ords-based/usecase01/tls.crt diff --git a/docs/multitenant/usecase01/tls.key b/docs/multitenant/ords-based/usecase01/tls.key similarity index 100% rename from docs/multitenant/usecase01/tls.key rename to docs/multitenant/ords-based/usecase01/tls.key diff --git a/docs/multitenant/usecase02/README.md b/docs/multitenant/ords-based/usecase02/README.md similarity index 100% rename from docs/multitenant/usecase02/README.md rename to docs/multitenant/ords-based/usecase02/README.md diff --git a/docs/multitenant/usecase02/pdb_clone.yaml b/docs/multitenant/ords-based/usecase02/pdb_clone.yaml similarity index 96% rename from docs/multitenant/usecase02/pdb_clone.yaml rename to docs/multitenant/ords-based/usecase02/pdb_clone.yaml index 0ecc3c70..1dba02e3 100644 --- a/docs/multitenant/usecase02/pdb_clone.yaml +++ b/docs/multitenant/ords-based/usecase02/pdb_clone.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb2 diff --git a/docs/multitenant/usecase02/pdb_plug.yaml b/docs/multitenant/ords-based/usecase02/pdb_plug.yaml similarity index 96% rename from docs/multitenant/usecase02/pdb_plug.yaml rename to docs/multitenant/ords-based/usecase02/pdb_plug.yaml index 77c00b9c..9563c58a 100644 --- a/docs/multitenant/usecase02/pdb_plug.yaml +++ b/docs/multitenant/ords-based/usecase02/pdb_plug.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase02/pdb_plugtde.yaml b/docs/multitenant/ords-based/usecase02/pdb_plugtde.yaml similarity index 96% rename from docs/multitenant/usecase02/pdb_plugtde.yaml rename to docs/multitenant/ords-based/usecase02/pdb_plugtde.yaml index 17d84346..995be538 100644 --- a/docs/multitenant/usecase02/pdb_plugtde.yaml +++ b/docs/multitenant/ords-based/usecase02/pdb_plugtde.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/provisioning/singlenamespace/pdb_unplug.yaml b/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml similarity index 95% rename from docs/multitenant/provisioning/singlenamespace/pdb_unplug.yaml rename to docs/multitenant/ords-based/usecase02/pdb_unplug.yaml index 085d337e..b56a12a3 100644 --- a/docs/multitenant/provisioning/singlenamespace/pdb_unplug.yaml +++ b/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase02/pdb_unplugtde.yaml b/docs/multitenant/ords-based/usecase02/pdb_unplugtde.yaml similarity index 96% rename from docs/multitenant/usecase02/pdb_unplugtde.yaml rename to docs/multitenant/ords-based/usecase02/pdb_unplugtde.yaml index 4c26bffe..2eacc5b7 100644 --- a/docs/multitenant/usecase02/pdb_unplugtde.yaml +++ b/docs/multitenant/ords-based/usecase02/pdb_unplugtde.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 Kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase02/tde_secret.yaml b/docs/multitenant/ords-based/usecase02/tde_secret.yaml similarity index 100% rename from docs/multitenant/usecase02/tde_secret.yaml rename to docs/multitenant/ords-based/usecase02/tde_secret.yaml diff --git a/docs/multitenant/usecase03/Dockerfile b/docs/multitenant/ords-based/usecase03/Dockerfile similarity index 100% rename from docs/multitenant/usecase03/Dockerfile rename to docs/multitenant/ords-based/usecase03/Dockerfile diff --git a/docs/multitenant/usecase03/NamespaceSegregation.png b/docs/multitenant/ords-based/usecase03/NamespaceSegregation.png similarity index 100% rename from docs/multitenant/usecase03/NamespaceSegregation.png rename to docs/multitenant/ords-based/usecase03/NamespaceSegregation.png diff --git a/docs/multitenant/usecase03/README.md b/docs/multitenant/ords-based/usecase03/README.md similarity index 100% rename from docs/multitenant/usecase03/README.md rename to docs/multitenant/ords-based/usecase03/README.md diff --git a/docs/multitenant/ords-based/usecase03/ca.crt b/docs/multitenant/ords-based/usecase03/ca.crt new file mode 100644 index 00000000..aaca8285 --- /dev/null +++ b/docs/multitenant/ords-based/usecase03/ca.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID/zCCAuegAwIBAgIUO9C2/rgvVW6st0IxUm5H7PznIaMwDQYJKoZIhvcNAQEL +BQAwgY4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH +DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxIzAhBgNVBAMMGmNkYi1k +ZXYtb3Jkcy5jZGJuYW1lc3BhY2UgMRwwGgYDVQQDDBNsb2NhbGhvc3QgIFJvb3Qg +Q0EgMB4XDTI0MTExMjExMjgyOVoXDTI1MTExMjExMjgyOVowgY4xCzAJBgNVBAYT +AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW5GcmFuY2lzY28x +EDAOBgNVBAoMB29yYWNsZSAxIzAhBgNVBAMMGmNkYi1kZXYtb3Jkcy5jZGJuYW1l +c3BhY2UgMRwwGgYDVQQDDBNsb2NhbGhvc3QgIFJvb3QgQ0EgMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAswwGPMVAZTe8U2qu49+2xiakB3kavFccxEY0 +TlLLrCHMHQ2QoV47GvZ5OMXqi7zuQ0uDEmar5bkBL06bipaKTCJjYtCU5EJ8Pwhb +EIK3FXrtK5SpvbIyupRyHo/4oa53ilP9R5ykwyBnsOLnc67298MQQIM2S0Cf8ML6 +9P74I7obwz01LT/PsJwRUQW3gWkx90LLS2qllr+wn4RMzJGPmYgXTAv9BbIsl9hv +56N5iYeT+aat0dxr7xGh/SeiPIHNoRMeryde3Ag563JxjJJn4jZf83Dkiy0ykq/H +p2pN8A4XKj6HKABFIpXh7Dpj5e+9cMCmOeAoNJCP760cKIAF0QIDAQABo1MwUTAd +BgNVHQ4EFgQUNP29zpxd77qgmU6vZxtUIatet5cwHwYDVR0jBBgwFoAUNP29zpxd +77qgmU6vZxtUIatet5cwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AQEARFmBpMIsLMACV+QpsvozJgB7dxH0roO8CFuPV+KI/pk5PWotK8S+NhEx33hE +7U1u0BsiwUw/GVrHsyCgrCOgspUE6/UAQk1fCYfxgzxC3tAKxerJwzXWW5W0mdyU +iGvW5DGjjkCiYAqRWB1XdKyNdf5cvzykbF29mNWy5fjA7xwvvS1UjQb6wMc7RpYa +hAbCNNt+eU/qKBGuYegg5PENY92DLGRTcDmqImE/o7y6BQxL3x+AR8ouOV/MckZ6 +DAZ0V/t1Ijk3e8tuj2Q2t4f9e9y532lAbvbZSV8kWkxVrRLblAbkTU6cFQHnMDiU +KSLwBsbgZybJw1sR8dZW0kk9ww== +-----END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase03/ca.key b/docs/multitenant/ords-based/usecase03/ca.key new file mode 100644 index 00000000..044326f3 --- /dev/null +++ b/docs/multitenant/ords-based/usecase03/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAswwGPMVAZTe8U2qu49+2xiakB3kavFccxEY0TlLLrCHMHQ2Q +oV47GvZ5OMXqi7zuQ0uDEmar5bkBL06bipaKTCJjYtCU5EJ8PwhbEIK3FXrtK5Sp +vbIyupRyHo/4oa53ilP9R5ykwyBnsOLnc67298MQQIM2S0Cf8ML69P74I7obwz01 +LT/PsJwRUQW3gWkx90LLS2qllr+wn4RMzJGPmYgXTAv9BbIsl9hv56N5iYeT+aat +0dxr7xGh/SeiPIHNoRMeryde3Ag563JxjJJn4jZf83Dkiy0ykq/Hp2pN8A4XKj6H +KABFIpXh7Dpj5e+9cMCmOeAoNJCP760cKIAF0QIDAQABAoIBACpcEOm1vRt1ST/v +AHt3BRXyNGJ0O2gNsAebmGpjWj5aLKE/AVW8Jn2ljF5rLKdsxm3zYooq8kZCM86O +aWwrW4LRq/PH50HtnNmIz4qjck9JquzhmhsAGtJbpngmlasizLNUVZroq25ir0WB +GUMcRN+vLZwlruW032IlrxvharGAw6vw/nV0a2sLcG2owjxOI47UK/X2esJMqV/5 +2xPIs5ktBl+P/otAWVJ1lU6U43hZasUQ/RPBxLQY4H0DViR3eb+kuBgLvv8y12Tw +yCam3zoIMPN2HZbPz4fAavxfqywzilGoHWHyOCDNZK957WysyqJDWzohj2qvUt2c +bI8gVgECgYEA6wl/1cVpFeRXe4CULZJksEnLNXAFE5AAOo6gg40vCFQ1i8SHMaMg +PmJXWGxKA/r8swD3l4pRbBFtPyPWBBMvxiytieySy+2l4EjdgnVZTYMe0FeuN8/N +zDw2VUMtVfn27QMcYCmzHHY5Lux5PyDx2ZJ23J1mvTaiejCLOxmXv7ECgYEAwwQg +XWaA3yd511xJc8qnlgT6leeCJ7vD8OVUmoLvKT5pH2xm5ENEb5MF5FhcHdj1Y1Uu +cvE6HzjWlffZpdNfGiSXpWxhTqS3NLQ6mhHGLF6sIB3hiFfjcpAhh/mAY8MxQnhT +jU9PWoTgTHxRf8zUsMhx0XIgqc+8sSnz0+PZUCECgYEA4DiCvLQEaT8Z+hcSxVro +NYlgk80W+q+gmDxigr1YhhCXuIu3EbJ2Wy1D+HXuMlfpZPdiPC5XOMpzNw3/6IlJ +RTxwHakT7fn8CbhZoVBD7n9NESLkcBgNU26JwtNAV8oMN9WE53RAi8F5EfvEO2Y5 +cv+X5yU90vphDEJtQOdMbjECgYEAurCxlxhw2LB+2Kd1fc8MnfZIOcd19zmuO2gf +lQcxkwc3Najd2zl2Q2W5Jz9beAth5uT94XWDEa6k5s5t4my2R12ueuOFR/cxl9fN +nf7T+1fqaZiRdqfEisDmCLjWqyqKdrqgKdA6BEreOvpsPu9E0bQiGcKq+EVxuxyR +WlBukmECgYBJOESxKk0vpr1Gi+KyKWFzsr6GsinvgrNrZ1G0pcVSCJEPFAJlx/ib +uUJv4ziaPxd/RoshV2F6kmvKoiFZIrlKRr+UdelRcLJhDhku6w2OOMYfID1FqA7e +DW5IEvjqXqt7jcazU9GZ8zWNIRq1jqer6pp6aMiaHwG/04o4p5fjxw== +-----END RSA PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase03/ca.srl b/docs/multitenant/ords-based/usecase03/ca.srl new file mode 100644 index 00000000..8b6ab178 --- /dev/null +++ b/docs/multitenant/ords-based/usecase03/ca.srl @@ -0,0 +1 @@ +1F43145DD3322A387B31238953C100E61B09B64D diff --git a/docs/multitenant/usecase03/cdb_create.yaml b/docs/multitenant/ords-based/usecase03/cdb_create.yaml similarity index 95% rename from docs/multitenant/usecase03/cdb_create.yaml rename to docs/multitenant/ords-based/usecase03/cdb_create.yaml index d3b5e04f..cc29fc71 100644 --- a/docs/multitenant/usecase03/cdb_create.yaml +++ b/docs/multitenant/ords-based/usecase03/cdb_create.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: CDB metadata: name: cdb-dev diff --git a/docs/multitenant/usecase03/cdb_creation_log.txt b/docs/multitenant/ords-based/usecase03/cdb_creation_log.txt similarity index 100% rename from docs/multitenant/usecase03/cdb_creation_log.txt rename to docs/multitenant/ords-based/usecase03/cdb_creation_log.txt diff --git a/docs/multitenant/usecase03/cdb_secret.yaml b/docs/multitenant/ords-based/usecase03/cdb_secret.yaml similarity index 100% rename from docs/multitenant/usecase03/cdb_secret.yaml rename to docs/multitenant/ords-based/usecase03/cdb_secret.yaml diff --git a/docs/multitenant/ords-based/usecase03/extfile.txt b/docs/multitenant/ords-based/usecase03/extfile.txt new file mode 100644 index 00000000..53e1b951 --- /dev/null +++ b/docs/multitenant/ords-based/usecase03/extfile.txt @@ -0,0 +1 @@ +subjectAltName=DNS:cdb-dev-ords.cdbnamespace,DNS:www.example.com diff --git a/docs/multitenant/usecase03/gentlscert.sh b/docs/multitenant/ords-based/usecase03/gentlscert.sh similarity index 100% rename from docs/multitenant/usecase03/gentlscert.sh rename to docs/multitenant/ords-based/usecase03/gentlscert.sh diff --git a/docs/multitenant/usecase03/makefile b/docs/multitenant/ords-based/usecase03/makefile similarity index 97% rename from docs/multitenant/usecase03/makefile rename to docs/multitenant/ords-based/usecase03/makefile index 7270a5e0..62f8a642 100644 --- a/docs/multitenant/usecase03/makefile +++ b/docs/multitenant/ords-based/usecase03/makefile @@ -123,8 +123,8 @@ URLPATH=/_/db-api/stable/database/pdbs/ OPENSSL=/usr/bin/openssl ORDSPORT=8888 MAKE=/usr/bin/make -DOCKERFILE=../../../ords/Dockerfile -RUNSCRIPT=../../../ords/runOrdsSSL.sh +DOCKERFILE=../../../../ords/Dockerfile +RUNSCRIPT=../../../../ords/runOrdsSSL.sh RM=/usr/bin/rm CP=/bin/cp ECHO=/usr/bin/echo @@ -167,6 +167,12 @@ createimage: $(CP) $(RUNSCRIPT) . $(DOCKER) build -t $(IMAGE) . +createimageproy: + @echo "BUILDING CDB IMAGES" + $(CP) $(DOCKERFILE) . + $(CP) $(RUNSCRIPT) . + $(DOCKER) build --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) -t $(IMAGE) . + tagimage: @echo "TAG IMAGE" $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) diff --git a/docs/multitenant/usecase03/ns_namespace_cdb.yaml b/docs/multitenant/ords-based/usecase03/ns_namespace_cdb.yaml similarity index 100% rename from docs/multitenant/usecase03/ns_namespace_cdb.yaml rename to docs/multitenant/ords-based/usecase03/ns_namespace_cdb.yaml diff --git a/docs/multitenant/usecase03/ns_namespace_pdb.yaml b/docs/multitenant/ords-based/usecase03/ns_namespace_pdb.yaml similarity index 100% rename from docs/multitenant/usecase03/ns_namespace_pdb.yaml rename to docs/multitenant/ords-based/usecase03/ns_namespace_pdb.yaml diff --git a/docs/multitenant/usecase03/operator_creation_log.txt b/docs/multitenant/ords-based/usecase03/operator_creation_log.txt similarity index 100% rename from docs/multitenant/usecase03/operator_creation_log.txt rename to docs/multitenant/ords-based/usecase03/operator_creation_log.txt diff --git a/docs/multitenant/provisioning/multinamespace/pdb_create.yaml b/docs/multitenant/ords-based/usecase03/pdb_create.yaml similarity index 95% rename from docs/multitenant/provisioning/multinamespace/pdb_create.yaml rename to docs/multitenant/ords-based/usecase03/pdb_create.yaml index 200f3712..920fc134 100644 --- a/docs/multitenant/provisioning/multinamespace/pdb_create.yaml +++ b/docs/multitenant/ords-based/usecase03/pdb_create.yaml @@ -1,4 +1,4 @@ -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 diff --git a/docs/multitenant/usecase03/pdb_creation_log.txt b/docs/multitenant/ords-based/usecase03/pdb_creation_log.txt similarity index 100% rename from docs/multitenant/usecase03/pdb_creation_log.txt rename to docs/multitenant/ords-based/usecase03/pdb_creation_log.txt diff --git a/docs/multitenant/usecase03/pdb_secret.yaml b/docs/multitenant/ords-based/usecase03/pdb_secret.yaml similarity index 100% rename from docs/multitenant/usecase03/pdb_secret.yaml rename to docs/multitenant/ords-based/usecase03/pdb_secret.yaml diff --git a/docs/multitenant/usecase03/runOrdsSSL.sh b/docs/multitenant/ords-based/usecase03/runOrdsSSL.sh similarity index 100% rename from docs/multitenant/usecase03/runOrdsSSL.sh rename to docs/multitenant/ords-based/usecase03/runOrdsSSL.sh diff --git a/docs/multitenant/ords-based/usecase03/server.csr b/docs/multitenant/ords-based/usecase03/server.csr new file mode 100644 index 00000000..ce9aa147 --- /dev/null +++ b/docs/multitenant/ords-based/usecase03/server.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICyjCCAbICAQAwgYQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh +MRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxIzAhBgNV +BAMMGmNkYi1kZXYtb3Jkcy5jZGJuYW1lc3BhY2UgMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOuYl+mB0G/evcnF5z +HKxXWAhAIfY9jMcYtIDo3vw0Vi4CcDaNZHoQCKbPG/f/+Y1PQx5Pm7MQzwaNapd9 +kqAu5cCbM9+bn0yIJdn5r6SG3VOFakxjLKY1BcRvq8HqqpkaKK8Bslyz99Ba2Cnw +Gsk/Tnc8x29Gk00oMRZejVRO/CKSulAwG1j8TG4VgG8zl9ynWi63Z3Mv2D0l2YDE +lqtUS3j4A8i7hRyrH20j0F2x4vG+cEkJz8xhblfIlTlk1C2w8FlDg86ZlImojkU1 +lLRBACPRGBIO4CnFKbY0k1FVOdMr/83hYgEF+y/dod6Mus2q+eq6ScktUS15wX1T +hFtpAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEARTh4oxRZ1Wk8LlrDWHIWU/lk +f3q/tacMGoGWMrwAEbNfsVglt622FxImE4oeb77OAWK9iBCE0xiYSFxd58v5FgW5 +OG2Ze0/VCD5klyOQ6vTrZyQFnO8uPhxGa+0UdJlq5aN+jEp6ZpmRXiiT+Km09wMb +sypVJ/juOxULWEbRWwSaGaXXv2NrDhAp4WcOnZrO3+vgq80FGX1N3RRl/4pXTuyG +S7+4Md9fOnLRon4v0ODLP56xPsFnW3tmvBEdPbUM8CV6xgCAh5LAJbKfhDFKjAOJ +J49ek3EpXobyx4vn9sdvSJaQUfgMOugbZzoKVi127cmeeiM/U2UYcVxlOyUa4A== +-----END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/ords-based/usecase03/tls.crt b/docs/multitenant/ords-based/usecase03/tls.crt new file mode 100644 index 00000000..c75dbc54 --- /dev/null +++ b/docs/multitenant/ords-based/usecase03/tls.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID2zCCAsOgAwIBAgIUH0MUXdMyKjh7MSOJU8EA5hsJtk0wDQYJKoZIhvcNAQEL +BQAwgY4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH +DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxIzAhBgNVBAMMGmNkYi1k +ZXYtb3Jkcy5jZGJuYW1lc3BhY2UgMRwwGgYDVQQDDBNsb2NhbGhvc3QgIFJvb3Qg +Q0EgMB4XDTI0MTExMjExMjgyOVoXDTI1MTExMjExMjgyOVowgYQxCzAJBgNVBAYT +AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW5GcmFuY2lzY28x +EDAOBgNVBAoMB29yYWNsZSAxIzAhBgNVBAMMGmNkYi1kZXYtb3Jkcy5jZGJuYW1l +c3BhY2UgMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDOuYl+mB0G/evcnF5zHKxXWAhAIfY9jMcYtIDo3vw0Vi4CcDaN +ZHoQCKbPG/f/+Y1PQx5Pm7MQzwaNapd9kqAu5cCbM9+bn0yIJdn5r6SG3VOFakxj +LKY1BcRvq8HqqpkaKK8Bslyz99Ba2CnwGsk/Tnc8x29Gk00oMRZejVRO/CKSulAw +G1j8TG4VgG8zl9ynWi63Z3Mv2D0l2YDElqtUS3j4A8i7hRyrH20j0F2x4vG+cEkJ +z8xhblfIlTlk1C2w8FlDg86ZlImojkU1lLRBACPRGBIO4CnFKbY0k1FVOdMr/83h +YgEF+y/dod6Mus2q+eq6ScktUS15wX1ThFtpAgMBAAGjOTA3MDUGA1UdEQQuMCyC +GWNkYi1kZXYtb3Jkcy5jZGJuYW1lc3BhY2WCD3d3dy5leGFtcGxlLmNvbTANBgkq +hkiG9w0BAQsFAAOCAQEAAE/DCwfWNHXF794/ug7DQqN3EzR6SdPLMzQygIYkhwPS +9S0nfDde+wS2nbNDrl1sOrGTyn/ZfIUJD0ia+zOJOvaqR/fFKrC6z1fIiKUQOynY +jO/DFPu9XD8lIrrLGNb9sT8KPpmiaq9RtuVFVj77siMO2A6IwNuQiYRVSH0whaYi +7zvguB96DLsKlN+v80ucSm1TZbSYIi77Yx8gUJBwA1PkmEsp6ixMQltlMidsHGIu +H/lhamO9vrVN890WCXfJ7melS4SORt5aAH7cTUchNX4gTNbmDm2Y9qvYHqb4q0vq +H5qYpg3DLkju9FqIOgWF64cpGMXZ1A53ocujiDR67A== +-----END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase03/tls.key b/docs/multitenant/ords-based/usecase03/tls.key new file mode 100644 index 00000000..cabbc944 --- /dev/null +++ b/docs/multitenant/ords-based/usecase03/tls.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDOuYl+mB0G/evc +nF5zHKxXWAhAIfY9jMcYtIDo3vw0Vi4CcDaNZHoQCKbPG/f/+Y1PQx5Pm7MQzwaN +apd9kqAu5cCbM9+bn0yIJdn5r6SG3VOFakxjLKY1BcRvq8HqqpkaKK8Bslyz99Ba +2CnwGsk/Tnc8x29Gk00oMRZejVRO/CKSulAwG1j8TG4VgG8zl9ynWi63Z3Mv2D0l +2YDElqtUS3j4A8i7hRyrH20j0F2x4vG+cEkJz8xhblfIlTlk1C2w8FlDg86ZlImo +jkU1lLRBACPRGBIO4CnFKbY0k1FVOdMr/83hYgEF+y/dod6Mus2q+eq6ScktUS15 +wX1ThFtpAgMBAAECggEBAJfIZKq9lzqF/8K4USTl3ag3677i8MCad6itB6Vz6+ul +hxylNXi1WGYjZA7XM00P9vBGkJ/U6zIhP2df5yD9In2slbGe8cR6vx+L4zLQAtY+ +9U8VEEcVBmgR9vwjOTtKzSUG0BBH3IcLIkFBS/GOSUshGq1WpV5FUzZ6bDk6PbfL +WEDKIzZaO1qrd53xe3HzUIGjYMj6LR/eiXw5d63Km9ay7fCOeDJAAYCjR1m4VceK +xB/1vCLyWiy2gjrhNJZVF6cJd5Q6bSjKudoY+9ikcrZ77T9jgcYYXQZOM/L6bIFr +LAXklQl2isNvwZevdUVLv2aoZBM9BKGydD2RN6b0l10CgYEA9OJU77a4RTo3IGpY +reVr6T+/ZodU6LK6x5FYi2BGyD30H6zEokq1qzkswxCN8x3HFP/kxlokS10k2db7 +jqqzDaabNDOxapwgT24BBI6KYMbiPFeuHhtKsRDNPYbSUoyuzcuDnbUXYGiHutOw +sygQwoeoynFJD0BVeGRkrSmM99sCgYEA2BvGlMjXTBqzqUjavnOS1apUmgn5nvHG +6MikMyAbNX6iGqQubMj8PXVkJf31kctChonFFwN1qHyOUagkiT/28neiYnlN5qbQ +6rm1myvh/KL88oRoFwuXy7dspgrptoq++Eie/NP8FGBMavXLXr/369/oDgB2UW8t +ZdhZRM1FrwsCgYEAlYuaPwGgqnYQsDUBjsCqE7kgU6aVjb2IHtN99S+ISkS7TpSd +SFbDdprl4QG9DhjDOOnEdfUacQOiu310Bf3sdoumAE0JLYfGm3scGAJMMymBg0Qw +SqZ0CImwLMCmtE7BeV+tMqQicHelW8xm/iGbipB53Zcs/KlXCLyWVsEnGz0CgYEA +iETzciw2vlF0CoHbFQ6xfzOZh0xU7+nLIEwsZeSP46qHMz9l2sjdbS+Rx/ccmyNN +PqKu+bT/ww9PeqQI7da8s+XGKkwNWCzGPcx+fGsuY+yujYn6TNXNkPKHeygUeTuB +8fNw9UICiSKz7Royc/uerEiS/glCklUFjlve5mh90UMCgYARGaOLss2+3OGQ0/2P +ghwhA9g0GS2dovinabZXFZPBxfeEuqvWD2ghSFDA/adSEWFAk57OkzAt46heeB4S +pIL1MGpAYPGqxOfLtE1JTAtLExmJlGA4xIU5DNDNYVN4J/PGbE68lxgyvveS+lzA +W3PGwL4lNI1KlR221LufR/ZTUw== +-----END PRIVATE KEY----- diff --git a/docs/multitenant/usecase01/cdb_secret.yaml b/docs/multitenant/usecase01/cdb_secret.yaml deleted file mode 100644 index 567b90a4..00000000 --- a/docs/multitenant/usecase01/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - cdbadmin_user: ".....base64 encoded password...." - cdbadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." diff --git a/docs/ordsservices/README.md b/docs/ordsservices/README.md new file mode 100644 index 00000000..2334f17b --- /dev/null +++ b/docs/ordsservices/README.md @@ -0,0 +1,79 @@ +# Oracle Rest Data Services (ORDS) Controller for Kubernetes - ORDSLFMNG ORDS Life cycle management + + + + +## Description + +The ORDS controller extends the Kubernetes API with a Custom Resource (CR) and Controller for automating Oracle Rest Data +Services (ORDS) lifecycle management. Using the ORDS controller, you can easily migrate existing, or create new, ORDS implementations +into an existing Kubernetes cluster. + +This controller allows you to run what would otherwise be an On-Premises ORDS middle-tier, configured as you require, inside Kubernetes with the additional ability of the controller to perform automatic ORDS/APEX install/upgrades inside the database. + +## Features Summary + +The custom RestDataServices resource supports the following configurations as a Deployment, StatefulSet, or DaemonSet: + +* Single RestDataServices resource with one database pool +* Single RestDataServices resource with multiple database pools* +* Multiple RestDataServices resources, each with one database pool +* Multiple RestDataServices resources, each with multiple database pools* + +*See [Limitations](#limitations) + +It supports the majority of ORDS configuration settings as per the [API Documentation](./api.md). + +The ORDS and APEX schemas can be [automatically installed/upgraded](./autoupgrade.md) into the Oracle Database by the ORDS controller. + +ORDS Version support: +* v22.1+ + +Oracle Database Version: +* 19c +* 23ai (incl. 23ai Free) + + +### Quick Installation + +To install the ORDS controller, run: + +```bash +kubectl apply -f https://github.com/gotsysdba/oracle-ords-controller/releases/latest/download/oracle-ords-controller.yaml +``` + +This will create a new namespace, `oracle-ords-controller-system`, in which the Controller will run. + +### Common Configurations + +A few common configuration examples can be used to quickly familiarise yourself with the ORDS Custom Resource Definition. +The "Conclusion" section of each example highlights specific settings to enable functionality that maybe of interest. + +* [Containerised Single Instance Database using the Oracontroller](./examples/sidb_container.md) +* [Multipool, Multidatabase using a TNS Names file](./examples/multi_pool.md) +* [Autonomous Database using the Oracontroller](./examples/adb_oraoper.md) - (Customer Managed ORDS) *See [Limitations](#limitations) +* [Autonomous Database without the Oracontroller](./examples/adb.md) - (Customer Managed ORDS) +* [Oracle API for MongoDB Support](./examples/mongo_api.md) + +Running through all examples in the same Kubernetes cluster illustrates the ability to run multiple ORDS instances with a variety of different configurations. + +If you have a specific use-case that is not covered and would like it to be feel free to contribute it via a Pull Request. + +### Limitations + +When connecting to a mTLS enabled ADB and using the Oracontroller to retreive the Wallet, it is currently not supported to have multiple, different databases supported by the single RestDataServices resource. This is due to a requirement to set the `TNS_ADMIN` parameter at the Pod level ([#97](https://github.com/oracle/oracle-database-controller/issues/97)). + +### Troubleshooting +See [Troubleshooting](./TROUBLESHOOTING.md) + +## Contributing +See [Contributing to this Repository](./CONTRIBUTING.md) + +## Reporting a Security Issue + +See [Reporting security vulnerabilities](./SECURITY.md) + +## License + +Copyright (c) 2024 Oracle and/or its affiliates. +Released under the Universal Permissive License v1.0 as shown at [https://oss.oracle.com/licenses/upl/](https://oss.oracle.com/licenses/upl/) diff --git a/docs/ordsservices/TROUBLESHOOTING.md b/docs/ordsservices/TROUBLESHOOTING.md new file mode 100644 index 00000000..b1b5304d --- /dev/null +++ b/docs/ordsservices/TROUBLESHOOTING.md @@ -0,0 +1,129 @@ + + + +## TROUBLESHOOTING + +### Init container error + +Check the pod status and verify the init outcome + +---- +*Command:* +```bash +kubectl get pods -n +``` + +*Example:* +```bash +kubectl get pods -n ordsnamespace +NAME READY STATUS RESTARTS AGE +ords-multi-pool-55db776994-7rrff 0/1 Init:CrashLoopBackOff 6 (61s ago) 12m +``` +In case of error identify the *initContainer* name + +---- +*Command:* +```bash +kubectl get pod -n -o="custom-columns=NAME:.metadata.name,INIT-CONTAINERS:.spec.initContainers[*].name,CONTAINERS:.spec.containers[*].name" +``` + +Use the initContainers info to dump log information +**Command:** +```bash +kubectl logs -f --since=0 -n -c +``` + +*Example:* + +In this particular case we are providing wrong credential: "SYT" user does not exist + +```text +kubectl logs -f --since=0 ords-multi-pool-55db776994-m7782 -n ordsnamespace -c ords-multi-pool-init + +[..omissis...] +Running SQL... +Picked up JAVA_TOOL_OPTIONS: -Doracle.ml.version_check=false +BACKTRACE [24:09:17 08:59:03] + +filename:line function +------------- -------- +/opt/oracle/sa/bin/init_script.sh:115 run_sql +/opt/oracle/sa/bin/init_script.sh:143 check_adb +/opt/oracle/sa/bin/init_script.sh:401 main +SQLERROR: + USER = SYT + URL = jdbc:oracle:thin:@PDB2 + Error Message = 🔥ORA-01017: invalid username/password;🔥 logon denied +Pool: pdb2, Exit Code: 1 +Pool: pdb1, Exit Code: 1 +``` + +--- +*Diag shell* Use the following script to dump the container init log + +```bash +#!/bin/bash +NAMESPACE=${1:-"ordsnamespace"} +KUBECTL=/usr/bin/kubectl +for _pod in `${KUBECTL} get pods --no-headers -o custom-columns=":metadata.name" --no-headers -n ${NAMESPACE}` +do + for _podinit in `${KUBECTL} get pod ${_pod} -n ${NAMESPACE} -o="custom-columns=INIT-CONTAINERS:.spec.initContainers[*].name" --no-headers` + do + echo "DUMPINIT ${_pod}:${_podinit}" + ${KUBECTL} logs -f --since=0 ${_pod} -n ${NAMESPACE} -c ${_podinit} + done +done +``` + +## Ords init error + +Get pod name + +*Command:* +```bash +kubectl get pods -n +``` + +*Example:* +``` +kubectl get pods -n ordsnamespace +NAME READY STATUS RESTARTS AGE +ords-multi-pool-55db776994-m7782 1/1 Running 0 2m51s +``` +---- +Dump ords log + +*Commands:* +```bash +kubectl logs --since=0 -n +``` +*Example:* +```text +kubectl logs --since=0 ords-multi-pool-55db776994-m7782 -n ordsnamespace +[..omissis..] +2024-09-17T09:47:39.227Z WARNING The pool named: |pdb2|lo| is invalid and will be ignored: ORDS was unable to make a connection to the database. The database user specified by db.username configuration setting is locked. The connection pool named: |pdb2|lo| had the following error(s): 🔥ORA-28000: The account is locked.🔥 + +2024-09-17T09:47:39.370Z WARNING The pool named: |pdb1|lo| is invalid and will be ignored: ORDS was unable to make a connection to the database. The database user specified by db.username configuration setting is locked. The connection pool named: |pdb1|lo| had the following error(s): 🔥ORA-28000: The account is locked.🔥 + +2024-09-17T09:47:39.375Z INFO + +Mapped local pools from /opt/oracle/sa/config/databases: + /ords/pdb1/ => pdb1 => INVALID + /ords/pdb2/ => pdb2 => INVALID + + +2024-09-17T09:47:39.420Z INFO Oracle REST Data Services initialized +Oracle REST Data Services version : 24.1.1.r1201228 +Oracle REST Data Services server info: jetty/10.0.20 +Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 11.0.15+8-LTS-149 +``` + +*Solution:* Connect to the container db to unlock the account + +```sql +alter user ORDS_PUBLIC_USER account unlock; +``` + + + + diff --git a/docs/ordsservices/api.md b/docs/ordsservices/api.md new file mode 100644 index 00000000..d8f9643d --- /dev/null +++ b/docs/ordsservices/api.md @@ -0,0 +1,1373 @@ +# API Reference + +Packages: + +- [database.oracle.com/v1](#databaseoraclecomv1) + +# database.oracle.com/v1 + +Resource Types: + +- [RestDataServices](#restdataservices) + + + + +## RestDataServices +[↩ Parent](#databaseoraclecomv1 ) + + + + + + +RestDataServices is the Schema for the restdataservices API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
apiVersionstringdatabase.oracle.com/v1true
kindstringRestDataServicestrue
metadataobjectRefer to the Kubernetes API documentation for the fields of the `metadata` field.true
specobject + RestDataServicesSpec defines the desired state of RestDataServices
+
false
statusobject + RestDataServicesStatus defines the observed state of RestDataServices
+
false
+ + +### RestDataServices.spec +[↩ Parent](#restdataservices) + + + +RestDataServicesSpec defines the desired state of RestDataServices + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
globalSettingsobject + Contains settings that are configured across the entire ORDS instance.
+
true
imagestring + Specifies the ORDS container image
+
true
forceRestartboolean + Specifies whether to restart pods when Global or Pool configurations change
+
false
imagePullPolicyenum + Specifies the ORDS container image pull policy
+
+ Enum: IfNotPresent, Always, Never
+ Default: IfNotPresent
+
false
imagePullSecretsstring + Specifies the Secret Name for pulling the ORDS container image
+
false
poolSettings[]object + Contains settings for individual pools/databases
+
false
replicasinteger + Defines the number of desired Replicas when workloadType is Deployment or StatefulSet
+
+ Format: int32
+ Default: 1
+ Minimum: 1
+
false
workloadTypeenum + Specifies the desired Kubernetes Workload
+
+ Enum: Deployment, StatefulSet, DaemonSet
+ Default: Deployment
+
false
+ + +### RestDataServices.spec.globalSettings +[↩ Parent](#restdataservicesspec) + + + +Contains settings that are configured across the entire ORDS instance. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
cache.metadata.enabledboolean + Specifies the setting to enable or disable metadata caching.
+
false
cache.metadata.graphql.expireAfterAccessinteger + Specifies the duration after a GraphQL schema is not accessed from the cache that it expires.
+
+ Format: int64
+
false
cache.metadata.graphql.expireAfterWriteinteger + Specifies the duration after a GraphQL schema is cached that it expires and has to be loaded again.
+
+ Format: int64
+
false
cache.metadata.jwks.enabledboolean + Specifies the setting to enable or disable JWKS caching.
+
false
cache.metadata.jwks.expireAfterAccessinteger + Specifies the duration after a JWK is not accessed from the cache that it expires. By default this is disabled.
+
+ Format: int64
+
false
cache.metadata.jwks.expireAfterWriteinteger + Specifies the duration after a JWK is cached, that is, it expires and has to be loaded again.
+
+ Format: int64
+
false
cache.metadata.jwks.initialCapacityinteger + Specifies the initial capacity of the JWKS cache.
+
+ Format: int32
+
false
cache.metadata.jwks.maximumSizeinteger + Specifies the maximum capacity of the JWKS cache.
+
+ Format: int32
+
false
cache.metadata.timeoutinteger + Specifies the setting to determine for how long a metadata record remains in the cache. Longer duration means, it takes longer to view the applied changes. The formats accepted are based on the ISO-8601 duration format.
+
+ Format: int64
+
false
certSecretobject + Specifies the Secret containing the SSL Certificates Replaces: standalone.https.cert and standalone.https.cert.key
+
false
database.api.enabledboolean + Specifies whether the Database API is enabled.
+
false
database.api.management.services.disabledboolean + Specifies to disable the Database API administration related services. Only applicable when Database API is enabled.
+
false
db.invalidPoolTimeoutinteger + Specifies how long to wait before retrying an invalid pool.
+
+ Format: int64
+
false
debug.printDebugToScreenboolean + Specifies whether to display error messages on the browser.
+
false
enable.mongo.access.logboolean + Specifies if HTTP request access logs should be enabled If enabled, logs will be written to /opt/oracle/sa/log/global
+
+ Default: false
+
false
enable.standalone.access.logboolean + Specifies if HTTP request access logs should be enabled If enabled, logs will be written to /opt/oracle/sa/log/global
+
+ Default: false
+
false
error.responseFormatstring + Specifies how the HTTP error responses must be formatted. html - Force all responses to be in HTML format json - Force all responses to be in JSON format auto - Automatically determines most appropriate format for the request (default).
+
false
feature.grahpql.max.nesting.depthinteger + Specifies the maximum join nesting depth limit for GraphQL queries.
+
+ Format: int32
+
false
icap.portinteger + Specifies the Internet Content Adaptation Protocol (ICAP) port to virus scan files. Either icap.port or icap.secure.port are required to have a value.
+
+ Format: int32
+
false
icap.secure.portinteger + Specifies the Internet Content Adaptation Protocol (ICAP) port to virus scan files. Either icap.port or icap.secure.port are required to have a value. If values for both icap.port and icap.secure.port are provided, then the value of icap.port is ignored.
+
+ Format: int32
+
false
icap.serverstring + Specifies the Internet Content Adaptation Protocol (ICAP) server name or IP address to virus scan files. The icap.server is required to have a value.
+
false
log.procedureboolean + Specifies whether procedures are to be logged.
+
false
mongo.enabledboolean + Specifies to enable the API for MongoDB.
+
false
mongo.idle.timeoutinteger + Specifies the maximum idle time for a Mongo connection in milliseconds.
+
+ Format: int64
+
false
mongo.op.timeoutinteger + Specifies the maximum time for a Mongo database operation in milliseconds.
+
+ Format: int64
+
false
mongo.portinteger + Specifies the API for MongoDB listen port.
+
+ Format: int32
+ Default: 27017
+
false
request.traceHeaderNamestring + Specifies the name of the HTTP request header that uniquely identifies the request end to end as it passes through the various layers of the application stack. In Oracle this header is commonly referred to as the ECID (Entity Context ID).
+
false
security.credentials.attemptsinteger + Specifies the maximum number of unsuccessful password attempts allowed. Enabled by setting a positive integer value.
+
+ Format: int32
+
false
security.credentials.lock.timeinteger + Specifies the period to lock the account that has exceeded maximum attempts.
+
+ Format: int64
+
false
security.disableDefaultExclusionListboolean + If this value is set to true, then the Oracle REST Data Services internal exclusion list is not enforced. Oracle recommends that you do not set this value to true.
+
false
security.exclusionListstring + Specifies a pattern for procedures, packages, or schema names which are forbidden to be directly executed from a browser.
+
false
security.externalSessionTrustedOriginsstring + Specifies to trust Access from originating domains
+
false
security.forceHTTPSboolean + Specifies to force HTTPS; this is set to default to false as in real-world TLS should terminiate at the LoadBalancer
+
false
security.httpsHeaderCheckstring + Specifies that the HTTP Header contains the specified text Usually set to 'X-Forwarded-Proto: https' coming from a load-balancer
+
false
security.inclusionListstring + Specifies a pattern for procedures, packages, or schema names which are allowed to be directly executed from a browser.
+
false
security.maxEntriesinteger + Specifies the maximum number of cached procedure validations. Set this value to 0 to force the validation procedure to be invoked on each request.
+
+ Format: int32
+
false
security.verifySSLboolean + Specifies whether HTTPS is available in your environment.
+
false
standalone.context.pathstring + Specifies the context path where ords is located.
+
+ Default: /ords
+
false
standalone.http.portinteger + Specifies the HTTP listen port.
+
+ Format: int32
+ Default: 8080
+
false
standalone.https.hoststring + Specifies the SSL certificate hostname.
+
false
standalone.https.portinteger + Specifies the HTTPS listen port.
+
+ Format: int32
+ Default: 8443
+
false
standalone.stop.timeoutinteger + Specifies the period for Standalone Mode to wait until it is gracefully shutdown.
+
+ Format: int64
+
false
+ + +### RestDataServices.spec.globalSettings.certSecret +[↩ Parent](#restdataservicesspecglobalsettings) + + + +Specifies the Secret containing the SSL Certificates Replaces: standalone.https.cert and standalone.https.cert.key + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
certstring + Specifies the Certificate
+
true
keystring + Specifies the Certificate Key
+
true
secretNamestring + Specifies the name of the certificate Secret
+
true
+ + +### RestDataServices.spec.poolSettings[index] +[↩ Parent](#restdataservicesspec) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
db.secretobject + Specifies the Secret with the dbUsername and dbPassword values for the connection.
+
true
poolNamestring + Specifies the Pool Name
+
true
apex.security.administrator.rolesstring + Specifies the comma delimited list of additional roles to assign authenticated APEX administrator type users.
+
false
apex.security.user.rolesstring + Specifies the comma delimited list of additional roles to assign authenticated regular APEX users.
+
false
autoUpgradeAPEXboolean + Specify whether to perform APEX installation/upgrades automatically The db.adminUser and db.adminUser.secret must be set, otherwise setting is ignored This setting will be ignored for ADB
+
+ Default: false
+
false
autoUpgradeORDSboolean + Specify whether to perform ORDS installation/upgrades automatically The db.adminUser and db.adminUser.secret must be set, otherwise setting is ignored This setting will be ignored for ADB
+
+ Default: false
+
false
db.adminUserstring + Specifies the username for the database account that ORDS uses for administration operations in the database.
+
false
db.adminUser.secretobject + Specifies the Secret with the dbAdminUser (SYS) and dbAdminPassword values for the database account that ORDS uses for administration operations in the database. replaces: db.adminUser.password
+
false
db.cdb.adminUserstring + Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management.
+
false
db.cdb.adminUser.secretobject + Specifies the Secret with the dbCdbAdminUser (SYS) and dbCdbAdminPassword values Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management. Replaces: db.cdb.adminUser.password
+
false
db.connectionTypeenum + The type of connection.
+
+ Enum: basic, tns, customurl
+
false
db.credentialsSourceenum + Specifies the source for database credentials when creating a direct connection for running SQL statements. Value can be one of pool or request. If the value is pool, then the credentials defined in this pool is used to create a JDBC connection. If the value request is used, then the credentials in the request is used to create a JDBC connection and if successful, grants the requestor SQL Developer role.
+
+ Enum: pool, request
+
false
db.customURLstring + Specifies the JDBC URL connection to connect to the database.
+
false
db.hostnamestring + Specifies the host system for the Oracle database.
+
false
db.poolDestroyTimeoutinteger + Indicates how long to wait to gracefully destroy a pool before moving to forcefully destroy all connections including borrowed ones.
+
+ Format: int64
+
false
db.portinteger + Specifies the database listener port.
+
+ Format: int32
+
false
db.servicenamestring + Specifies the network service name of the database.
+
false
db.sidstring + Specifies the name of the database.
+
false
db.tnsAliasNamestring + Specifies the TNS alias name that matches the name in the tnsnames.ora file.
+
false
db.usernamestring + Specifies the name of the database user for the connection. For non-ADB this will default to ORDS_PUBLIC_USER For ADBs this must be specified and not ORDS_PUBLIC_USER If ORDS_PUBLIC_USER is specified for an ADB, the workload will fail
+
+ Default: ORDS_PUBLIC_USER
+
false
db.wallet.zip.servicestring + Specifies the service name in the wallet archive for the pool.
+
false
dbWalletSecretobject + Specifies the Secret containing the wallet archive containing connection details for the pool. Replaces: db.wallet.zip
+
false
debug.trackResourcesboolean + Specifies to enable tracking of JDBC resources. If not released causes in resource leaks or exhaustion in the database. Tracking imposes a performance overhead.
+
false
feature.openservicebroker.excludeboolean + Specifies to disable the Open Service Broker services available for the pool.
+
false
feature.sdwboolean + Specifies to enable the Database Actions feature.
+
false
http.cookie.filterstring + Specifies a comma separated list of HTTP Cookies to exclude when initializing an Oracle Web Agent environment.
+
false
jdbc.DriverTypeenum + Specifies the JDBC driver type.
+
+ Enum: thin, oci8
+
false
jdbc.InactivityTimeoutinteger + Specifies how long an available connection can remain idle before it is closed. The inactivity connection timeout is in seconds.
+
+ Format: int32
+
false
jdbc.InitialLimitinteger + Specifies the initial size for the number of connections that will be created. The default is low, and should probably be set higher in most production environments.
+
+ Format: int32
+
false
jdbc.MaxConnectionReuseCountinteger + Specifies the maximum number of times to reuse a connection before it is discarded and replaced with a new connection.
+
+ Format: int32
+
false
jdbc.MaxConnectionReuseTimeinteger + Sets the maximum connection reuse time property.
+
+ Format: int32
+
false
jdbc.MaxLimitinteger + Specifies the maximum number of connections. Might be too low for some production environments.
+
+ Format: int32
+
false
jdbc.MaxStatementsLimitinteger + Specifies the maximum number of statements to cache for each connection.
+
+ Format: int32
+
false
jdbc.MinLimitinteger + Specifies the minimum number of connections.
+
+ Format: int32
+
false
jdbc.SecondsToTrustIdleConnectioninteger + Sets the time in seconds to trust an idle connection to skip a validation test.
+
+ Format: int32
+
false
jdbc.auth.admin.rolestring + Identifies the database role that indicates that the database user must get the SQL Administrator role.
+
false
jdbc.auth.enabledboolean + Specifies if the PL/SQL Gateway calls can be authenticated using database users. If the value is true then this feature is enabled. If the value is false, then this feature is disabled. Oracle recommends not to use this feature. This feature used only to facilitate customers migrating from mod_plsql.
+
false
jdbc.cleanup.modestring + Specifies how a pooled JDBC connection and corresponding database session, is released when a request has been processed.
+
false
jdbc.statementTimeoutinteger + Specifies a timeout period on a statement. An abnormally long running query or script, executed by a request, may leave it in a hanging state unless a timeout is set on the statement. Setting a timeout on the statement ensures that all the queries automatically timeout if they are not completed within the specified time period.
+
+ Format: int32
+
false
misc.defaultPagestring + Specifies the default page to display. The Oracle REST Data Services Landing Page.
+
false
misc.pagination.maxRowsinteger + Specifies the maximum number of rows that will be returned from a query when processing a RESTful service and that will be returned from a nested cursor in a result set. Affects all RESTful services generated through a SQL query, regardless of whether the resource is paginated.
+
+ Format: int32
+
false
owa.trace.sqlboolean + If it is true, then it causes a trace of the SQL statements performed by Oracle Web Agent to be echoed to the log.
+
false
plsql.gateway.modeenum + Indicates if the PL/SQL Gateway functionality should be available for a pool or not. Value can be one of disabled, direct, or proxied. If the value is direct, then the pool serves the PL/SQL Gateway requests directly. If the value is proxied, the PLSQL_GATEWAY_CONFIG view is used to determine the user to whom to proxy.
+
+ Enum: disabled, direct, proxied
+
false
procedure.preProcessstring + Specifies the procedure name(s) to execute prior to executing the procedure specified on the URL. Multiple procedure names must be separated by commas.
+
false
procedure.rest.preHookstring + Specifies the function to be invoked prior to dispatching each Oracle REST Data Services based REST Service. The function can perform configuration of the database session, perform additional validation or authorization of the request. If the function returns true, then processing of the request continues. If the function returns false, then processing of the request is aborted and an HTTP 403 Forbidden status is returned.
+
false
procedurePostProcessstring + Specifies the procedure name(s) to execute after executing the procedure specified on the URL. Multiple procedure names must be separated by commas.
+
false
restEnabledSql.activeboolean + Specifies whether the REST-Enabled SQL service is active.
+
false
security.jwks.connection.timeoutinteger + Specifies the maximum amount of time before timing-out when accessing a JWK url.
+
+ Format: int64
+
false
security.jwks.read.timeoutinteger + Specifies the maximum amount of time reading a response from the JWK url before timing-out.
+
+ Format: int64
+
false
security.jwks.refresh.intervalinteger + Specifies the minimum interval between refreshing the JWK cached value.
+
+ Format: int64
+
false
security.jwks.sizeinteger + Specifies the maximum number of bytes read from the JWK url.
+
+ Format: int32
+
false
security.jwt.allowed.ageinteger + Specifies the maximum allowed age of a JWT in seconds, regardless of expired claim. The age of the JWT is taken from the JWT issued at claim.
+
+ Format: int64
+
false
security.jwt.allowed.skewinteger + Specifies the maximum skew the JWT time claims are accepted. This is useful if the clock on the JWT issuer and ORDS differs by a few seconds.
+
+ Format: int64
+
false
security.jwt.profile.enabledboolean + Specifies whether the JWT Profile authentication is available. Supported values:
+
false
security.requestAuthenticationFunctionstring + Specifies an authentication function to determine if the requested procedure in the URL should be allowed or disallowed for processing. The function should return true if the procedure is allowed; otherwise, it should return false. If it returns false, Oracle REST Data Services will return WWW-Authenticate in the response header.
+
false
security.requestValidationFunctionstring + Specifies a validation function to determine if the requested procedure in the URL should be allowed or disallowed for processing. The function should return true if the procedure is allowed; otherwise, return false.
+
+ Default: ords_util.authorize_plsql_gateway
+
false
security.validationFunctionTypeenum + Indicates the type of security.requestValidationFunction: javascript or plsql.
+
+ Enum: plsql, javascript
+
false
soda.defaultLimitstring + When using the SODA REST API, specifies the default number of documents returned for a GET request on a collection when a limit is not specified in the URL. Must be a positive integer, or "unlimited" for no limit.
+
false
soda.maxLimitstring + When using the SODA REST API, specifies the maximum number of documents that will be returned for a GET request on a collection URL, regardless of any limit specified in the URL. Must be a positive integer, or "unlimited" for no limit.
+
false
tnsAdminSecretobject + Specifies the Secret containing the TNS_ADMIN directory Replaces: db.tnsDirectory
+
false
+ + +### RestDataServices.spec.poolSettings[index].db.secret +[↩ Parent](#restdataservicesspecpoolsettingsindex) + + + +Specifies the Secret with the dbUsername and dbPassword values for the connection. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
secretNamestring + Specifies the name of the password Secret
+
true
passwordKeystring + Specifies the key holding the value of the Secret
+
+ Default: password
+
false
+ + +### RestDataServices.spec.poolSettings[index].db.adminUser.secret +[↩ Parent](#restdataservicesspecpoolsettingsindex) + + + +Specifies the Secret with the dbAdminUser (SYS) and dbAdminPassword values for the database account that ORDS uses for administration operations in the database. replaces: db.adminUser.password + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
secretNamestring + Specifies the name of the password Secret
+
true
passwordKeystring + Specifies the key holding the value of the Secret
+
+ Default: password
+
false
+ + +### RestDataServices.spec.poolSettings[index].db.cdb.adminUser.secret +[↩ Parent](#restdataservicesspecpoolsettingsindex) + + + +Specifies the Secret with the dbCdbAdminUser (SYS) and dbCdbAdminPassword values Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management. Replaces: db.cdb.adminUser.password + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
secretNamestring + Specifies the name of the password Secret
+
true
passwordKeystring + Specifies the key holding the value of the Secret
+
+ Default: password
+
false
+ + +### RestDataServices.spec.poolSettings[index].dbWalletSecret +[↩ Parent](#restdataservicesspecpoolsettingsindex) + + + +Specifies the Secret containing the wallet archive containing connection details for the pool. Replaces: db.wallet.zip + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
secretNamestring + Specifies the name of the Database Wallet Secret
+
true
walletNamestring + Specifies the Secret key name containing the Wallet
+
true
+ + +### RestDataServices.spec.poolSettings[index].tnsAdminSecret +[↩ Parent](#restdataservicesspecpoolsettingsindex) + + + +Specifies the Secret containing the TNS_ADMIN directory Replaces: db.tnsDirectory + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
secretNamestring + Specifies the name of the TNS_ADMIN Secret
+
true
+ + +### RestDataServices.status +[↩ Parent](#restdataservices) + + + +RestDataServicesStatus defines the observed state of RestDataServices + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
restartRequiredboolean + Indicates if the resource is out-of-sync with the configuration
+
true
conditions[]object +
+
false
httpPortinteger + Indicates the HTTP port of the resource exposed by the pods
+
+ Format: int32
+
false
httpsPortinteger + Indicates the HTTPS port of the resource exposed by the pods
+
+ Format: int32
+
false
mongoPortinteger + Indicates the MongoAPI port of the resource exposed by the pods (if enabled)
+
+ Format: int32
+
false
ordsVersionstring + Indicates the ORDS version
+
false
statusstring + Indicates the current status of the resource
+
false
workloadTypestring + Indicates the current Workload type of the resource
+
false
+ + +### RestDataServices.status.conditions[index] +[↩ Parent](#restdataservicesstatus) + + + +Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, + type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: "Available", "Progressing", and "Degraded" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + // other fields } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
lastTransitionTimestring + lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
+
+ Format: date-time
+
true
messagestring + message is a human readable message indicating details about the transition. This may be an empty string.
+
true
reasonstring + reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.
+
true
statusenum + status of the condition, one of True, False, Unknown.
+
+ Enum: True, False, Unknown
+
true
typestring + type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+
true
observedGenerationinteger + observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.
+
+ Format: int64
+ Minimum: 0
+
false
diff --git a/docs/ordsservices/autoupgrade.md b/docs/ordsservices/autoupgrade.md new file mode 100644 index 00000000..040e4da8 --- /dev/null +++ b/docs/ordsservices/autoupgrade.md @@ -0,0 +1,54 @@ +# AutoUpgrade + +Each pool can be configured to automatically install and upgrade the ORDS and/or APEX schemas in the database. +The ORDS and APEX version is based on the ORDS image used for the RestDataServices resource. + +For example, in the below manifest: +* `Pool: pdb1` is configured to automatically install/ugrade both ORDS and APEX to version 24.1.0 +* `Pool: pdb2` will not install or upgrade ORDS/APEX + +As an additional requirement for `Pool: pdb1`, the `spec.poolSettings.db.adminUser` and `spec.poolSettings.db.adminUser.secret` +must be provided. If they are not, the `autoUpgrade` specification is ignored. + +```yaml +apiVersion: database.oracle.com/v1 +kind: RestDataServices +metadata: + name: ordspoc-server +spec: + image: container-registry.oracle.com/database/ords:24.1.0 + forceRestart: true + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: pdb1 + autoUpgradeORDS: true + autoUpgradeAPEX: true + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//localhost:1521/PDB1 + db.secret: + secretName: pdb1-ords-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: pdb1-sys-auth + - poolName: pdb2 + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//localhost:1521/PDB2 + db.secret: + secretName: pdb2-ords-auth +``` + +## Minimum Privileges for Admin User + +The `db.adminUser` must have privileges to create users and objects in the database. For Oracle Autonomous Database (ADB), this could be `ADMIN` while for +non-ADBs this could be `SYS AS SYSDBA`. When you do not want to use `ADMIN` or `SYS AS SYSDBA` to install, upgrade, validate and uninstall ORDS a script is provided +to create a new user to be used. + +1. Download the equivalent version of ORDS to the image you will be using. +1. Extract the software and locate: `scripts/installer/ords_installer_privileges.sql` +1. Using SQLcl or SQL*Plus, connect to the Oracle PDB with SYSDBA privileges. +1. Execute the following script providing the database user: + ```sql + @/path/to/installer/ords_installer_privileges.sql privuser + exit + ``` diff --git a/docs/ordsservices/examples/adb.md b/docs/ordsservices/examples/adb.md new file mode 100644 index 00000000..e1196455 --- /dev/null +++ b/docs/ordsservices/examples/adb.md @@ -0,0 +1,101 @@ +# Example: Autonomous Database without the OraOperator + +This example walks through using the **ORDS Operator** with an Oracle Autonomous Database. + +This assumes that an ADB has already been provisioned and is configured as "Secure Access from Anywhere". +Note that if behind a Proxy, this example will not work as the Wallet will need to be modified to support the proxy configuration. + +### Install ORDS Operator + +Install the Oracle ORDS Operator: + +```bash +kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/latest/download/oracle-ords-operator.yaml +``` + +### ADB Wallet Secret + +Download the ADB Wallet and create a Secret, replacing `` with the path to the wallet zip file: + +```bash +kubectl create secret generic adb-wallet \ + --from-file= +``` + +### ADB ADMIN Password Secret + +Create a Secret for the ADB ADMIN password, replacing with the real password: + +```bash +kubectl create secret generic adb-db-auth \ + --from-literal=password= +``` + +### Create RestDataServices Resource + +1. Create a manifest for ORDS. + + As an ADB already maintains ORDS and APEX, `autoUpgradeORDS` and `autoUpgradeAPEX` will be ignored if set. A new DB User for ORDS will be created to avoid conflict with the pre-provisioned one. This user will be + named, `ORDS_PUBLIC_USER_OPER` if `db.username` is either not specified or set to `ORDS_PUBLIC_USER`. + + Replace with the ADB Name and ensure that the `db.wallet.zip.service` is valid for your ADB Workload (e.g. _TP or _HIGH, etc.): + + ```bash + echo " + apiVersion: database.oracle.com/v1 + kind: RestDataServices + metadata: + name: ords-adb + spec: + image: container-registry.oracle.com/database/ords:24.1.1 + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: adb + db.wallet.zip.service: _TP + dbWalletSecret: + secretName: adb-wallet + walletName: Wallet_.zip + restEnabledSql.active: true + feature.sdw: true + plsql.gateway.mode: proxied + db.username: ORDS_PUBLIC_USER_OPER + db.secret: + secretName: adb-db-auth + passwordKey: password + db.adminUser: ADMIN + db.adminUser.secret: + secretName: adb-db-auth + passwordKey: password" | kubectl apply -f - + ``` + latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + +1. Watch the restdataservices resource until the status is **Healthy**: + ```bash + kubectl get restdataservices ords-adb -w + ``` + + **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. If APEX + is being installed for the first time by the Operator, it may remain in the **Preparing** + status for an additional 5 minutes. + +### Test + +Open a port-forward to the ORDS service, for example: + +```bash +kubectl port-forward service/ords-adb 8443:8443 +``` + +Direct your browser to: `https://localhost:8443/ords/adb` + +## Conclusion + +This example has a single database pool, named `adb`. It is set to: + +* Not automatically restart when the configuration changes: `forceRestart` is not set. + The pod must be manually resarted for new configurations to be picked-up. +* Automatically install/update ORDS on startup, if required. This occurs due to the database being detected as an ADB. +* Automatically install/update APEX on startup, if required: This occurs due to the database being detected as an ADB. +* The ADB `ADMIN` user will be used to connect the ADB to install APEX/ORDS +* Use the ADB Wallet file to connect to the database: `db.wallet.zip.service: adbpoc_tp` and `dbWalletSecret` \ No newline at end of file diff --git a/docs/ordsservices/examples/adb_oraoper.md b/docs/ordsservices/examples/adb_oraoper.md new file mode 100644 index 00000000..b4ea5123 --- /dev/null +++ b/docs/ordsservices/examples/adb_oraoper.md @@ -0,0 +1,188 @@ +# Example: Autonomous Database using the OraOperator + +This example walks through using the **ORDS Operator** with a Containerised Oracle Database created by the **OraOperator** in the same Kubernetes Cluster. + +When connecting to a mTLS enabled ADB while using the OraOperator to retreive the Wallet as is done in the example, it is currently not supported to have multiple, different databases supported by the single RestDataServices resource. This is due to a requirement to set the `TNS_ADMIN` parameter at the Pod level ([#97](https://github.com/oracle/oracle-database-operator/issues/97)). + +### Install Cert-Manager + +The OraOperator uses webhooks for validating user input before persisting it in etcd. +Webhooks require TLS certificates that are generated and managed by a certificate manager. + +Install [cert-manager](https://cert-manager.io/) with the following command: + +```bash +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.5/cert-manager.yaml +``` +latest cert-manager version, **v1.14.5**, valid as of **30-May-2024** + +Check that all pods have a STATUS of **Running** before proceeding to the next step: +```bash +kubectl -n cert-manager get pods +``` + +Review [cert-managers installation documentation](https://cert-manager.io/docs/installation/kubectl/) for more information. + +### Install OraOperator + +Install the [Oracle Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/tree/main): + +```bash +kubectl apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/main/oracle-database-operator.yaml +``` + +### Setup Oracle Cloud Authorisation + +In order for the OraOperator to access the ADB, some pre-requisites are required, as detailed [here](https://github.com/oracle/oracle-database-operator/blob/main/docs/adb/ADB_PREREQUISITES.md). Either establish Instance Principles or create the required ConfigMap/Secret. This example uses the later: + +``` +kubectl create configmap oci-cred \ +--from-literal=tenancy= \ +--from-literal=user= \ +--from-literal=fingerprint= \ +--from-literal=region= + +kubectl create secret generic oci-privatekey \ +--from-file=privatekey= +``` + +### Install ORDS Operator + +Install the Oracle ORDS Operator: + +```bash +kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/latest/download/oracle-ords-operator.yaml +``` + +### ADB ADMIN Password Secret + +Create a Secret for the ADB Admin password: + +```bash +DB_PWD=$(echo "ORDSpoc_$(date +%H%S%M)") + +kubectl create secret generic adb-oraoper-db-auth \ + --from-literal=adb-oraoper-db-auth=${DB_PWD} +``` + +**NOTE**: When binding to the ADB in a later step, the OraOperator will change the ADB password to what is specified in the Secret. + +### Bind the OraOperator to the ADB + +1. Obtain the OCID of the ADB and set to an environment variable: + + ``` + export ADB_OCID= + ``` + +1. Create a manifest to bind to the ADB. + + ```bash + echo " + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabase + metadata: + name: adb-oraoper + spec: + hardLink: false + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + details: + autonomousDatabaseOCID: $ADB_OCID + wallet: + name: adb-oraoper-tns-admin + password: + k8sSecret: + name: adb-oraoper-db-auth" | kubectl apply -f - + ``` + +1. Update the ADMIN Password: + +```bash + kubectl patch adb adb-oraoper --type=merge \ + -p '{"spec":{"details":{"adminPassword":{"k8sSecret":{"name":"adb-oraoper-db-auth"}}}}}' +``` + +1. Watch the `adb` resource until the STATE is **AVAILABLE**: + + ```bash + kubectl get adb/adb-oraoper -w + ``` + +### Create RestDataServices Resource + +1. Obtain the Service Name from the OraOperator + + ```bash + SERVICE_NAME=$(kubectl get adb adb-oraoper -o=jsonpath='{.spec.details.dbName}'_TP) + ``` + +1. Create a manifest for ORDS. + + As an ADB already maintains ORDS and APEX, `autoUpgradeORDS` and `autoUpgradeAPEX` will be ignored if set. A new DB User for ORDS will be created to avoid conflict with the pre-provisioned one. This user will be + named, `ORDS_PUBLIC_USER_OPER` if `db.username` is either not specified or set to `ORDS_PUBLIC_USER`. + + ```bash + echo " + apiVersion: database.oracle.com/v1 + kind: RestDataServices + metadata: + name: ords-adb-oraoper + spec: + image: container-registry.oracle.com/database/ords:24.1.1 + forceRestart: true + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: adb-oraoper + db.connectionType: tns + db.tnsAliasName: $SERVICE_NAME + tnsAdminSecret: + secretName: adb-oraoper-tns-admin + restEnabledSql.active: true + feature.sdw: true + plsql.gateway.mode: proxied + db.username: ORDS_PUBLIC_USER_OPER + db.secret: + secretName: adb-oraoper-db-auth + passwordKey: adb-oraoper-db-auth + db.adminUser: ADMIN + db.adminUser.secret: + secretName: adb-oraoper-db-auth + passwordKey: adb-oraoper-db-auth" | kubectl apply -f - + ``` + latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + +1. Watch the restdataservices resource until the status is **Healthy**: + ```bash + kubectl get restdataservices ords-adb-oraoper -w + ``` + + **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. If APEX + is being installed for the first time by the Operator, it may remain in the **Preparing** + status for an additional 5 minutes. + + +### Test + +Open a port-forward to the ORDS service, for example: + +```bash +kubectl port-forward service/ords-adb-oraoper 8443:8443 +``` + +Direct your browser to: `https://localhost:8443/ords/adb-oraoper` + +## Conclusion + +This example has a single database pool, named `adb-oraoper`. It is set to: + +* Automatically restart when the configuration changes: `forceRestart: true` +* Automatically install/update ORDS on startup, if required. This occurs due to the database being detected as an ADB. +* Automatically install/update APEX on startup, if required: This occurs due to the database being detected as an ADB. +* The ADB `ADMIN` user will be used to connect the ADB to install APEX/ORDS +* Use a TNS connection string to connect to the database: `db.customURL: jdbc:oracle:thin:@//${CONN_STRING}` + The `tnsAdminSecret` Secret `adb-oraoper-tns-admin` was created by the OraOperator +* The `passwordKey` has been specified for both `db.secret` and `db.adminUser.secret` as `adb-oraoper-password` to match the OraOperator specification. +* The ADB `ADMIN` user will be used to connect the ADB to install APEX/ORDS \ No newline at end of file diff --git a/docs/ordsservices/examples/mongo_api.md b/docs/ordsservices/examples/mongo_api.md new file mode 100644 index 00000000..2d238ad2 --- /dev/null +++ b/docs/ordsservices/examples/mongo_api.md @@ -0,0 +1,138 @@ +# Example: Oracle API for MongoDB Support + +This example walks through using the **ORDS Operator** with a Containerised Oracle Database to enable MongoDB API Support. + +### Database Access + +This example assumes you have a running, accessible Oracle Database. For demonstration purposes, +the [Containerised Single Instance Database using the OraOperator](sidb_container.md) will be used. + +### Rest Enable a Schema + +In the database, create an ORDS-enabled user. As this example uses the [Containerised Single Instance Database using the OraOperator](sidb_container.md), the following was performed: + + +1. Connect to the database: + ```bash + DB_PWD=$(kubectl get secrets sidb-db-auth --template='{{.data.password | base64decode}}') + POD_NAME=$(kubectl get pod -l "app=oraoper-sidb" -o custom-columns=NAME:.metadata.name --no-headers) + + kubectl exec -it ${POD_NAME} -- sqlplus SYSTEM/${DB_PWD}@FREEPDB1 + ``` + +1. Create the User: + ```sql + create user MONGO identified by "My_Password1!"; + grant soda_app, create session, create table, create view, create sequence, create procedure, create job, + unlimited tablespace to MONGO; + -- Connect as new user + conn MONGO/My_Password1!@FREEDB1; + exec ords.enable_schema; + ``` + +### Create RestDataServices Resource + +1. Retrieve the Connection String from the containerised SIDB. + + ```bash + CONN_STRING=$(kubectl get singleinstancedatabase oraoper-sidb \ + -o jsonpath='{.status.pdbConnectString}') + + echo $CONN_STRING + ``` + +1. Create a manifest for ORDS. + + As the DB in the Free image does not contain ORDS (or APEX), the following additional keys are specified for the pool: + * `autoUpgradeORDS` - Boolean; when true the ORDS will be installed/upgraded in the database + * `db.adminUser` - User with privileges to install, upgrade or uninstall ORDS in the database (SYS). + * `db.adminUser.secret` - Secret containing the password for `db.adminUser` (created in the first step) + + The `db.username` will be used as the ORDS schema in the database during the install/upgrade process (ORDS_PUBLIC_USER). + + ```bash + echo " + apiVersion: database.oracle.com/v1 + kind: RestDataServices + metadata: + name: ords-sidb + spec: + image: container-registry.oracle.com/database/ords:24.1.1 + forceRestart: true + globalSettings: + database.api.enabled: true + mongo.enabled: true + poolSettings: + - poolName: default + autoUpgradeORDS: true + restEnabledSql.active: true + plsql.gateway.mode: direct + jdbc.MaxConnectionReuseCount: 5000 + jdbc.MaxConnectionReuseTime: 900 + jdbc.SecondsToTrustIdleConnection: 1 + jdbc.InitialLimit: 100 + jdbc.MaxLimit: 100 + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//${CONN_STRING} + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: sidb-db-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: sidb-db-auth" | kubectl apply -f - + ``` + latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + +1. Watch the restdataservices resource until the status is **Healthy**: + ```bash + kubectl get restdataservices ords-sidb -w + ``` + + **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. If APEX + is being installed for the first time by the Operator, it may remain in the **Preparing** + status for an additional 5 minutes. + + You can watch the APEX/ORDS Installation progress by running: + + ```bash + POD_NAME=$(kubectl get pod -l "app.kubernetes.io/instance=ords-sidb" -o custom-columns=NAME:.metadata.name --no-headers) + + kubectl logs ${POD_NAME} -c ords-sidb-init -f + ``` + +### Test + +1. Open a port-forward to the MongoAPI service, for example: + ```bash + kubectl port-forward service/ords-sidb 27017:27017 + ``` + +1. Connect to ORDS using the MongoDB shell: + ```bash + mongosh --tlsAllowInvalidCertificates 'mongodb://MONGO:My_Password1!@localhost:27017/MONGO?authMechanism=PLAIN&authSource=$external&tls=true&retryWrites=false&loadBalanced=true' + ``` + +1. Insert some data: + ```txt + db.createCollection('emp'); + db.emp.insertOne({"name":"Blake","job": "Intern","salary":30000}); + db.emp.insertOne({"name":"Miller","job": "Programmer","salary": 70000}); + db.emp.find({"name":"Miller"}); + ``` + +## Conclusion + +This example has a single database pool, named `default`. It is set to: + +* Automatically restart when the configuration changes: `forceRestart: true` +* Automatically install/update ORDS on startup, if required: `autoUpgradeORDS: true` +* Use a basic connection string to connect to the database: `db.customURL: jdbc:oracle:thin:@//${CONN_STRING}` +* The `passwordKey` has been ommitted from both `db.secret` and `db.adminUser.secret` as the password was stored in the default key (`password`) +* The MongoAPI service has been enabled: `mongo.enabled: true` +* The MongoAPI service will default to port: `27017` as the property: `mongo.port` has been left undefined +* A number of JDBC parameters were set at the pool level for achieving high performance: + * `jdbc.MaxConnectionReuseCount: 5000` + * `jdbc.MaxConnectionReuseTime: 900` + * `jdbc.SecondsToTrustIdleConnection: 1` + * `jdbc.InitialLimit: 100` + * `jdbc.MaxLimit: 100` diff --git a/docs/ordsservices/examples/multi_pool.md b/docs/ordsservices/examples/multi_pool.md new file mode 100644 index 00000000..e830bd9a --- /dev/null +++ b/docs/ordsservices/examples/multi_pool.md @@ -0,0 +1,175 @@ +# Example: Multipool, Multidatabase using a TNS Names file + +This example walks through using the **ORDS Operator** with multiple databases using a TNS Names file. +Keep in mind that all pools are running in the same Pod, therefore, changing the configuration of one pool will require +a recycle of all pools. + +### Install ORDS Operator + +Install the Oracle ORDS Operator: + +```bash +kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/latest/download/oracle-ords-operator.yaml +``` + +### TNS_ADMIN Secret + +Create a Secret with the contents of the TNS_ADMIN directory. This can be a single `tnsnames.ora` file or additional files such as `sqlnet.ora` or `ldap.ora`. +The example shows using a `$TNS_ADMIN` enviroment variable which points to a directory with valid TNS_ADMIN files. + +To create a secret with all files in the TNS_ADMIN directory: +```bash +kubectl create secret generic multi-tns-admin \ + --from-file=$TNS_ADMIN +``` + +To create a secret with just the tnsnames.ora file: +```bash +kubectl create secret generic multi-tns-admin \ + --from-file=$TNS_ADMIN/tnsnames.ora +``` + +In this example, 4 PDBs will be connected to and the example `tnsnames.ora` file contents are as below: +```text +PDB1=(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=on)(ADDRESS=(PROTOCOL=TCP)(HOST=10.10.0.1)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=PDB1))) + +PDB2=(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=on)(ADDRESS=(PROTOCOL=TCP)(HOST=10.10.0.2)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=PDB2))) + +PDB3=(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=on)(ADDRESS=(PROTOCOL=TCP)(HOST=10.10.0.3)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=PDB3))) + +PDB4=(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=on)(ADDRESS=(PROTOCOL=TCP)(HOST=10.10.0.4)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=PDB4))) +``` + +### ORDS_PUBLIC_USER Secret + +Create a Secret for each of the databases `ORDS_PUBLIC_USER` user. +If multiple databases use the same password, the same secret can be re-used. + +The following secret will be used for PDB1: +```bash +kubectl create secret generic pdb1-ords-auth \ + --from-literal=password=pdb1-battery-staple +``` + +The following secret will be used for PDB2: +```bash +kubectl create secret generic pdb2-ords-auth \ + --from-literal=password=pdb2-battery-staple +``` + +The following secret will be used for PDB3 and PDB4: +```bash +kubectl create secret generic multi-ords-auth \ + --from-literal=password=multiple-battery-staple +``` + +### Privileged Secret (*Optional) + +If taking advantage of the [AutoUpgrade](../autoupgrade.md) functionality, create a secret for a user with the privileges to modify the ORDS and/or APEX schemas. + +In this example, only PDB1 will be set for [AutoUpgrade](../autoupgrade.md), the other PDBs already have APEX and ORDS installed. + +```bash +kubectl create secret generic pdb1-priv-auth \ + --from-literal=password=pdb1-battery-staple +``` + +### Create RestDataServices Resource + +1. Create a manifest for ORDS. + + ```bash + echo " + apiVersion: database.oracle.com/v1 + kind: RestDataServices + metadata: + name: ords-multi-pool + spec: + image: container-registry.oracle.com/database/ords:24.1.1 + forceRestart: true + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: pdb1 + autoUpgradeORDS: true + autoUpgradeAPEX: true + db.connectionType: tns + db.tnsAliasName: PDB1 + tnsAdminSecret: + secretName: multi-tns-admin + restEnabledSql.active: true + feature.sdw: true + plsql.gateway.mode: proxied + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: pdb1-ords-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: pdb1-priv-auth + - poolName: pdb2 + db.connectionType: tns + db.tnsAliasName: PDB2 + tnsAdminSecret: + secretName: multi-tns-admin + restEnabledSql.active: true + feature.sdw: true + plsql.gateway.mode: proxied + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: pdb2-ords-auth + - poolName: pdb3 + db.connectionType: tns + db.tnsAliasName: PDB3 + tnsAdminSecret: + secretName: multi-tns-admin + restEnabledSql.active: true + feature.sdw: true + plsql.gateway.mode: proxied + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: multi-ords-auth + - poolName: pdb4 + db.connectionType: tns + db.tnsAliasName: PDB4 + tnsAdminSecret: + secretName: multi-tns-admin + restEnabledSql.active: true + feature.sdw: true + plsql.gateway.mode: proxied + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: multi-ords-auth" | kubectl apply -f - + ``` + latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + +1. Watch the restdataservices resource until the status is **Healthy**: + ```bash + kubectl get restdataservices ords-multi-pool -w + ``` + + **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. As APEX + is being installed for the first time by the Operator into PDB1, it will remain in the **Preparing** + status for an additional 5-10 minutes. + +### Test + +Open a port-forward to the ORDS service, for example: + +```bash +kubectl port-forward service/ords-multi-pool 8443:8443 +``` + +1. For PDB1, direct your browser to: `https://localhost:8443/ords/pdb1` +1. For PDB2, direct your browser to: `https://localhost:8443/ords/pdb2` +1. For PDB3, direct your browser to: `https://localhost:8443/ords/pdb3` +1. For PDB4, direct your browser to: `https://localhost:8443/ords/pdb4` + +## Conclusion + +This example has multiple pools, named `pdb1`, `pdb2`, `pdb3`, and `pdb4`. + +* They all share the same `tnsAdminSecret` to connect using thier individual `db.tnsAliasName` +* They will all automatically restart when the configuration changes: `forceRestart: true` +* Only the `pdb1` pool will automatically install/update ORDS on startup, if required: `autoUpgradeORDS: true` +* Only the `pdb1` pool will automatically install/update APEX on startup, if required: `autoUpgradeAPEX: true` +* The `passwordKey` has been ommitted from both `db.secret` and `db.adminUser.secret` as the password was stored in the default key (`password`) diff --git a/docs/ordsservices/examples/sidb_container.md b/docs/ordsservices/examples/sidb_container.md new file mode 100644 index 00000000..4530360e --- /dev/null +++ b/docs/ordsservices/examples/sidb_container.md @@ -0,0 +1,166 @@ +# Example: Containerised Single Instance Database using the OraOperator + +This example walks through using the **ORDS Operator** with a Containerised Oracle Database created by the **OraOperator** in the same Kubernetes Cluster. + +### Install Cert-Manager + +The OraOperator uses webhooks for validating user input before persisting it in etcd. +Webhooks require TLS certificates that are generated and managed by a certificate manager. + +Install [cert-manager](https://cert-manager.io/) with the following command: + +```bash +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.5/cert-manager.yaml +``` +latest cert-manager version, **v1.14.5**, valid as of **2-May-2024** + +Check that all pods have a STATUS of **Running** before proceeding to the next step: +```bash +kubectl -n cert-manager get pods +``` + +Review [cert-managers installation documentation](https://cert-manager.io/docs/installation/kubectl/) for more information. + +### Install OraOperator + +Install the [Oracle Operator for Kubernetes](https://github.com/oracle/oracle-database-operator): + +```bash +kubectl apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/main/oracle-database-operator.yaml +``` + +### Install ORDS Operator + +Install the Oracle ORDS Operator: + +```bash +kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/latest/download/oracle-ords-operator.yaml +``` + +### Deploy a Containerised Oracle Database + +1. Create a Secret for the Database password: + + ```bash + DB_PWD=$(echo "ORDSpoc_$(date +%H%S%M)") + + kubectl create secret generic sidb-db-auth \ + --from-literal=password=${DB_PWD} + ``` +1. Create a manifest for the containerised Oracle Database. + + The POC uses an Oracle Free Image, but other versions may be subsituted; review the OraOperator Documentation for details on the manifests. + + ```bash + echo " + apiVersion: database.oracle.com/v1alpha1 + kind: SingleInstanceDatabase + metadata: + name: oraoper-sidb + spec: + replicas: 1 + image: + pullFrom: container-registry.oracle.com/database/free:23.4.0.0 + prebuiltDB: true + sid: FREE + edition: free + adminPassword: + secretName: sidb-db-auth + secretKey: password + pdbName: FREEPDB1" | kubectl apply -f - + ``` + latest container-registry.oracle.com/database/free version, **23.4.0.0**, valid as of **2-May-2024** + +1. Watch the `singleinstancedatabases` resource until the database status is **Healthy**: + + ```bash + kubectl get singleinstancedatabases/oraoper-sidb -w + ``` + + **NOTE**: If this is the first time pulling the free database image, it may take up to 15 minutes for the database to become available. + +### Create RestDataServices Resource + +1. Retrieve the Connection String from the containerised SIDB. + + ```bash + CONN_STRING=$(kubectl get singleinstancedatabase oraoper-sidb \ + -o jsonpath='{.status.pdbConnectString}') + + echo $CONN_STRING + ``` + +1. Create a manifest for ORDS. + + As the DB in the Free image does not contain ORDS (or APEX), the following additional keys are specified for the pool: + * `autoUpgradeORDS` - Boolean; when true the ORDS will be installed/upgraded in the database + * `autoUpgradeAPEX` - Boolean; when true the APEX will be installed/upgraded in the database + * `db.adminUser` - User with privileges to install, upgrade or uninstall ORDS in the database (SYS). + * `db.adminUser.secret` - Secret containing the password for `db.adminUser` (created in the first step) + + The `db.username` will be used as the ORDS schema in the database during the install/upgrade process (ORDS_PUBLIC_USER). + + ```bash + echo " + apiVersion: database.oracle.com/v1 + kind: RestDataServices + metadata: + name: ords-sidb + spec: + image: container-registry.oracle.com/database/ords:24.1.1 + forceRestart: true + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: default + autoUpgradeORDS: true + autoUpgradeAPEX: true + restEnabledSql.active: true + plsql.gateway.mode: direct + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//${CONN_STRING} + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: sidb-db-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: sidb-db-auth" | kubectl apply -f - + ``` + latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + +1. Watch the restdataservices resource until the status is **Healthy**: + ```bash + kubectl get restdataservices ords-sidb -w + ``` + + **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. If APEX + is being installed for the first time by the Operator, it may remain in the **Preparing** + status for an additional 5 minutes. + + You can watch the APEX/ORDS Installation progress by running: + + ```bash + POD_NAME=$(kubectl get pod -l "app.kubernetes.io/instance=ords-sidb" -o custom-columns=NAME:.metadata.name --no-headers) + + kubectl logs ${POD_NAME} -c ords-sidb-init -f + ``` + +### Test + +Open a port-forward to the ORDS service, for example: + +```bash +kubectl port-forward service/ords-sidb 8443:8443 +``` + +Direct your browser to: `https://localhost:8443/ords` + +## Conclusion + +This example has a single database pool, named `default`. It is set to: + +* Automatically restart when the configuration changes: `forceRestart: true` +* Automatically install/update ORDS on startup, if required: `autoUpgradeORDS: true` +* Automatically install/update APEX on startup, if required: `autoUpgradeAPEX: true` +* Use a basic connection string to connect to the database: `db.customURL: jdbc:oracle:thin:@//${CONN_STRING}` +* The `passwordKey` has been ommitted from both `db.secret` and `db.adminUser.secret` as the password was stored in the default key (`password`) \ No newline at end of file diff --git a/docs/ordsservices/usecase01/create_mong_schema.sql b/docs/ordsservices/usecase01/create_mong_schema.sql new file mode 100644 index 00000000..a00ee441 --- /dev/null +++ b/docs/ordsservices/usecase01/create_mong_schema.sql @@ -0,0 +1,9 @@ +drop user MONGO cascade; +set echo on +set head on +create user MONGO identified by "My_Password1!"; +grant soda_app, create session, create table, create view, create sequence, create procedure, create job, +unlimited tablespace to MONGO; +conn MONGO/My_Password1!@158.180.233.248:30001/FREEPDB1 +exec ords.enable_schema; +exit; diff --git a/docs/ordsservices/usecase01/create_multisrv.yaml b/docs/ordsservices/usecase01/create_multisrv.yaml new file mode 100644 index 00000000..35ad8f85 --- /dev/null +++ b/docs/ordsservices/usecase01/create_multisrv.yaml @@ -0,0 +1,42 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: OrdsSrvs +metadata: + name: ords-multi-pool + namespace: ordsnamespace +spec: + image: container-registry.oracle.com/database/ords:24.1.1 + forceRestart: true + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: pdb1 + autoUpgradeORDS: true + db.connectionType: tns + db.tnsAliasName: pdb1 + tnsAdminSecret: + secretName: multi-tns-admin + restEnabledSql.active: true + feature.sdw: true + plsql.gateway.mode: proxied + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: multi-ords-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: pdb1-priv-auth + - poolName: pdb2 + db.connectionType: tns + db.tnsAliasName: PDB2 + tnsAdminSecret: + secretName: multi-tns-admin + restEnabledSql.active: true + feature.sdw: true + plsql.gateway.mode: proxied + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: multi-ords-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: pdb2-priv-auth + + diff --git a/docs/ordsservices/usecase01/create_ords.sh b/docs/ordsservices/usecase01/create_ords.sh new file mode 100644 index 00000000..ee892ddf --- /dev/null +++ b/docs/ordsservices/usecase01/create_ords.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -x + +TNS_ALIAS=`kubectl get singleinstancedatabase oraoper-sidb -n oracle-database-operator-system -o jsonpath='{.status.pdbConnectString}'` + +echo " +apiVersion: database.oracle.com/v1alpha1 +kind: RestDataServices +metadata: + name: ords-sidb + namespace: oracle-database-operator-system +spec: + image: container-registry.oracle.com/database/ords:24.1.0 + forceRestart: true + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: default + autoUpgradeORDS: true + autoUpgradeAPEX: true + restEnabledSql.active: true + plsql.gateway.mode: direct + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//${TNS_ALIAS} + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: sidb-db-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: sidb-db-auth" | kubectl apply -f - diff --git a/docs/ordsservices/usecase01/create_ords.yaml b/docs/ordsservices/usecase01/create_ords.yaml new file mode 100644 index 00000000..8b8374bf --- /dev/null +++ b/docs/ordsservices/usecase01/create_ords.yaml @@ -0,0 +1,25 @@ +apiVersion: database.oracle.com/v1 +kind: RestDataServices +metadata: + name: ords-sidb + namespace: oracle-database-operator-system +spec: + image: container-registry.oracle.com/database/ords:24.1.0 + pullSecrets: oracle-container-registry-secret + forceRestart: true + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: default + autoUpgradeORDS: true + autoUpgradeAPEX: true + restEnabledSql.active: true + plsql.gateway.mode: direct + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//204.216.216.178:30437/FREEPDB1 + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: sidb-db-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: sidb-db-auth diff --git a/docs/ordsservices/usecase01/create_ordsnamespace.yaml b/docs/ordsservices/usecase01/create_ordsnamespace.yaml new file mode 100644 index 00000000..ceeefd36 --- /dev/null +++ b/docs/ordsservices/usecase01/create_ordsnamespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: ordsnamespace diff --git a/docs/ordsservices/usecase01/create_registry_secret.sh b/docs/ordsservices/usecase01/create_registry_secret.sh new file mode 100644 index 00000000..fc03855b --- /dev/null +++ b/docs/ordsservices/usecase01/create_registry_secret.sh @@ -0,0 +1,4 @@ +echo enter password for matteo.malvezzi@oracle.com@container-registry.oracle.com +read -s scpwd +/usr/local/go/bin/kubectl create secret docker-registry oracle-container-registry-secret --docker-server=container-registry.oracle.com --docker-username=matteo.malvezzi@oracle.com --docker-password=$scpwd --docker-email=matteo.malvezzi@oracle.com -n oracle-database-operator-system +/usr/local/go/bin/kubectl create secret docker-registry oracle-container-registry-secret --docker-server=container-registry.oracle.com --docker-username=matteo.malvezzi@oracle.com --docker-password=$scpwd --docker-email=matteo.malvezzi@oracle.com -n ordsnamespace diff --git a/docs/ordsservices/usecase01/create_secretregistry.sh b/docs/ordsservices/usecase01/create_secretregistry.sh new file mode 100644 index 00000000..656eb19e --- /dev/null +++ b/docs/ordsservices/usecase01/create_secretregistry.sh @@ -0,0 +1,5 @@ +#!/bin/bash +read pwd +kubectl create secret docker-registry oracle-container-registry-secret --docker-server=container-registry.oracle.com --docker-username=matteo.malvezzi@oraclecom --docker-password=$pwd --docker-email=matteo.malvezzi@oracle.com -n oracle-database-operator-system + + diff --git a/docs/ordsservices/usecase01/create_singleinstance_db.yaml b/docs/ordsservices/usecase01/create_singleinstance_db.yaml new file mode 100644 index 00000000..0c3918bd --- /dev/null +++ b/docs/ordsservices/usecase01/create_singleinstance_db.yaml @@ -0,0 +1,17 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: SingleInstanceDatabase +metadata: + name: oraoper-sidb + namespace: oracle-database-operator-system +spec: + replicas: 1 + image: + pullFrom: container-registry.oracle.com/database/free:23.4.0.0 + pullSecrets: oracle-container-registry-secret + prebuiltDB: true + sid: FREE + edition: free + adminPassword: + secretName: sidb-db-auth + secretKey: password + pdbName: FREEPDB1 diff --git a/docs/ordsservices/usecase01/default-ns-role-binding.yaml b/docs/ordsservices/usecase01/default-ns-role-binding.yaml new file mode 100644 index 00000000..03adfcb3 --- /dev/null +++ b/docs/ordsservices/usecase01/default-ns-role-binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/ordsservices/usecase01/diaginit.sh b/docs/ordsservices/usecase01/diaginit.sh new file mode 100644 index 00000000..3373fba6 --- /dev/null +++ b/docs/ordsservices/usecase01/diaginit.sh @@ -0,0 +1,11 @@ +#!/bin/bash +NAMESPACE=${1:-"ordsnamespace"} +KUBECTL=/usr/bin/kubectl +for _pod in `${KUBECTL} get pods --no-headers -o custom-columns=":metadata.name" --no-headers -n ${NAMESPACE}` +do + for _podinit in `${KUBECTL} get pod ${_pod} -n ${NAMESPACE} -o="custom-columns=INIT-CONTAINERS:.spec.initContainers[*].name" --no-headers` + do + echo "DUMPINIT ${_pod}:${_podinit}" + ${KUBECTL} logs -f --since=0 ${_pod} -n ${NAMESPACE} -c ${_podinit} + done +done diff --git a/docs/ordsservices/usecase01/help b/docs/ordsservices/usecase01/help new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/docs/ordsservices/usecase01/help @@ -0,0 +1 @@ + diff --git a/docs/ordsservices/usecase01/makefile b/docs/ordsservices/usecase01/makefile new file mode 100644 index 00000000..54df7272 --- /dev/null +++ b/docs/ordsservices/usecase01/makefile @@ -0,0 +1,715 @@ +# +# Copyright (c) 2006, 2024, Oracle and/or its affiliates. +# +# +# NAME +# makefile: automation exectution of the follwing configuration +# in a multi namespace environment (orcle-database-operator-system, +# ordsnamespace) +# +# - SingleInstanceDatabase +# - Mongo API +# +# DESCRIPTION +# Main makefile - see target table +# +# | Target | Description | +# +-------------+--------------------------------------------------------+ +# | step0a | create_ordsnamespace.yaml | +# +-------------+--------------------------------------------------------+ +# | step1a | setup certmaneger | +# +-------------+--------------------------------------------------------+ +# | step2a | setup operator oracle-database-operator.yaml | +# +-------------+--------------------------------------------------------+ +# | step3a | default scoped deployment default-ns-role-binding.yaml | +# +-------------+--------------------------------------------------------+ +# | step4a | node - persistent volume - storage class for the db | +# +-------------+--------------------------------------------------------+ +# | step5a | setup secrets | +# +----------------------------------------------------------------------+ +# | step6a | setup secrets for OCR | +# +----------------------------------------------------------------------+ +# | step7a | setup sidb | +# +----------------------------------------------------------------------+ +# | step8a | ⭐Setup REST SERVER ⭐ | +# +-------------+--------------------------------------------------------+ +# +# step[1-7]a are required to start mongodb API rest server +# +# step[9-11] test mongo API +# +-------------+--------------------------------------------------------+ +# | step9 | configure a mongo db user on sidb | +# +-------------+--------------------------------------------------------+ +# | step10 | ⭐Setup REST SERVER FOR MONGO API ⭐ | +# +-------------+--------------------------------------------------------+ +# | step11 | Test Mongo API | +# +-------------+--------------------------------------------------------+ +# +# step[12- ] test multi tns configuration +# +-------------+--------------------------------------------------------+ +# | step12 | create tns secret | +# +-------------+--------------------------------------------------------+ +# | step13 | create passwords secret | +# +-------------+--------------------------------------------------------+ +# | step14 | ⭐SetupMulti Rest Server ⭐ | +# +-------------+--------------------------------------------------------+ +# + + +export OPRNAMESPACE=oracle-database-operator-system +export RSTNAMESPACE=ordsnamespace +export WATCHLIST=$(OPRNAMESPACE),$(RSTNAMESPACE) +export CREATE_SINGLEINSTANCE=create_singleinstance_db.yaml +export CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml +export SIDB_SECRET=sidb-db-auth +export ORDS_SECRET=ords-db-auth +export SIDB_IMAGE=container-registry.oracle.com/database/free:23.4.0.0 +export ORDS_IMAGE=container-registry.oracle.com/database/ords:24.1.0 +export ORDS_IMAGE.1=container-registry.oracle.com/database/ords:24.1.1 +export SIDB_PASSWORD=RSTpoc_061319 +export SECRET_CONTAINER_REGISTRY=oracle-container-registry-secret +export ORACLE_CONTAINER_REGISTRY=container-registry.oracle.com +export USER_CONTAINER_REGISTRY=matteo.malvezzi@oracle.com +export REST_SERVER_NAME=ords-sidb +export REST_SERVER_NAME_MONGO=ords-sidb-mongo +export MONGOSH=mongosh-2.3.1-linux-x64 +export KIND=OrdsSrvs + +export PDB1=pdb1 +export PDB2=pdb2 +export TNS1=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=$(PDB1)))) +export TNS2=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=$(PDB2)))) +export TNSNAMES=./tnsnames.ora +export TNSADMIN=`pwd`/tnsadmin + +## CMD SECTION## +export KUBECTL=/usr/local/go/bin/kubectl +export DIFF=/usr/bin/diff +export MAKE=/usr/bin/make +export CURL=/usr/bin/curl +export TAR=/usr/bin/tar + +## YAML AND OTHER FILES ## +CREATE_RSTNAMESPACE=create_$(RSTNAMESPACE).yaml +export DEFAULT_NAMESPACE_SCOPE=default-ns-role-binding.yaml +export RST_NAMESPACE_SCOPE=ords-ns-role-binding.yaml +export ORACLE_OPERATOR_YAML=../../../oracle-database-operator.yaml +export NODE_RBAC=node-rbac.yaml +export STORAGE_CLASS_RBAC=storage-class-rbac.yaml +export PERSISTENT_VOLUME_RBAC=persistent-volume-rbac.yaml +export SIDB_CREATION=sidb_create.yaml +export SECRET_CONTAINER_REGISTRY_SCRIPT=create_registry_secret.sh +export REST_SERVER_CREATION=rest_server_creation.yaml +export REST_SERVER_CREATION_MONGO=rest_server_creation_mongo.yaml +export MULTISRV_MANIFEST=create_multisrv.yaml +export MONGOORADBUSER=MONGO + + +MAKEFILE=./makefile +.ONESHELL: + +define manpage +@printf "\n" +@printf "\033[7m%s\033[0m \033[7m%s\033[0m \033[7m%s\033[0m\n" "TARGET " "DESCRIPTION " "YAML FILE " +@printf "%s %s %s\n" "---------" " --------------------------------------------------" "--------------------------------------" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step0/a/d setup new namespace" " " "$(CREATE_RSTNAMESPACE)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step1/a/d setup certmaneger " " " "$(CERTMANAGER)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step2/a/d setup operator" " " "$(shell basename $(ORACLE_OPERATOR_YAML))" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step3/a/d default scoped deployment" " " "$(DEFAULT_NAMESPACE_SCOPE)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" " ords scoped deployment" " " "$(RST_NAMESPACE_SCOPE)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step4/a/d node rbac" " " "$(NODE_RBAC)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" " storage class " " " "$(STORAGE_CLASS_RBAC)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" " persistent volume " " " "$(PERSISTENT_VOLUME_RBAC)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step5/a/d setup db secret" " " "n/a" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step6/a/d setup registry secret" " " "$(SECRET_CONTAINER_REGISTRY_SCRIPT)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step7/a/d setup sidb " " " "$(SIDB_CREATION)" +@printf "================================================\n" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step8/a/d setup RestServer " " " "$(REST_SERVER_CREATION)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step9/-/- configure " " " "Mongo ora db user:$(MONGOORADBUSER)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step10/a/d setup RestServer Mongo " " " "$(REST_SERVER_CREATION_MONGO)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step11/-/- test mongodb API " " " "----" +@printf "================================================\n" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step12/a/d create secret for tnsadmin " " " "$(TNSADMIN)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step13/a/d create secrets for adminusers" " " "---" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step14/a/d/e setup Multi Ords services " " " "---" +@printf "================================================\n" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "diagordsinit" "" "🔬dump initpod logs" + + + +@printf "================================================\n" +@printf " a=apply d=delete ⚡e=generate error ⚡\n" +@printf "\n" +endef + + +help:man +man: + $(call manpage) + +define namespace +cat< $(CREATE_RSTNAMESPACE) +#apiVersion: v1 +#kind: Namespace +#metadata: +# labels: +# control-plane: controller-manager +# name: $(2) +EOF +$(KUBECTL) $(1) -f $(CREATE_RSTNAMESPACE) +$(KUBECTL) get namespace +endef + +step0: + $(call namespace,$(ACTION),$(RSTNAMESPACE)) +step0a: + $(MAKE) -f $(MAKEFILE) step0 ACTION=apply +step0d: + $(MAKE) -f $(MAKEFILE) step0 ACTION=delete + +step1: + $(KUBECTL) $(ACTION) -f $(CERTMANAGER) +step1a: + $(MAKE) -f $(MAKEFILE) ACTION=apply step1 + $(KUBECTL) get pod -n cert-manager +step1d: + $(MAKE) -f $(MAKEFILE) ACTION=delete step1 + + +define setwatchnamespace +@echo "Setting watch namespace list: $(WATCHLIST)" +sed 's/value: ""/value: "$(WATCHLIST)"/g' $(ORACLE_OPERATOR_YAML) > `basename $(ORACLE_OPERATOR_YAML)` +$(KUBECTL) $(1) -f `basename $(ORACLE_OPERATOR_YAML)` +$(DIFF) $(ORACLE_OPERATOR_YAML) `basename $(ORACLE_OPERATOR_YAML)` +$(KUBECTL) get pods -n $(OPRNAMESPACE) +endef + +step2: + $(call setwatchnamespace,$(ACTION)) +step2a: + $(MAKE) -f $(MAKEFILE) ACTION=apply step2 +step2d: + $(MAKE) -f $(MAKEFILE) ACTION=delete step2 + + +define namespacescpe +cat<$(RST_NAMESPACE_SCOPE) +#apiVersion: rbac.authorization.k8s.io/v1 +#kind: RoleBinding +#metadata: +# name: $(RSTNAMESPACE)-rolebinding +# namespace: $(RSTNAMESPACE) +#roleRef: +# apiGroup: rbac.authorization.k8s.io +# kind: ClusterRole +# name: oracle-database-operator-manager-role +#subjects: +#- kind: ServiceAccount +# name: default +# namespace: oracle-database-operator-system +EOF + +cat< $(DEFAULT_NAMESPACE_SCOPE) +#apiVersion: rbac.authorization.k8s.io/v1 +#kind: RoleBinding +#metadata: +# name: oracle-database-operator-oracle-database-operator-manager-rolebinding +# namespace: default +#roleRef: +# apiGroup: rbac.authorization.k8s.io +# kind: ClusterRole +# name: oracle-database-operator-manager-role +#subjects: +#- kind: ServiceAccount +# name: default +# namespace: oracle-database-operator-system +EOF + +$(KUBECTL) $(1) -f $(RST_NAMESPACE_SCOPE) +$(KUBECTL) $(1) -f $(DEFAULT_NAMESPACE_SCOPE) +$(KUBECTL) get RoleBinding -n $(RSTNAMESPACE) + +endef + +step3: + $(call namespacescpe,$(ACTION)) + +step3a: + $(MAKE) -f $(MAKEFILE) ACTION=apply step3 + +step3d: + $(MAKE) -f $(MAKEFILE) ACTION=delete step3 + + +export NODE_RBAC=node-rbac.yaml +export STORAGE_CLASS_RBAC=storage-class-rbac.yaml +export PERSISTENT_VOLUME_RBAC=persistent-volume-rbac.yaml + + +define persistenvolume + +cat<$(NODE_RBAC) +#--- +#apiVersion: rbac.authorization.k8s.io/v1 +#kind: ClusterRole +#metadata: +# name: oracle-database-operator-manager-role-node +#rules: +#- apiGroups: +# - "" +# resources: +# - nodes +# verbs: +# - list +# - watch +#--- +#apiVersion: rbac.authorization.k8s.io/v1 +#kind: ClusterRoleBinding +#metadata: +# name: oracle-database-operator-manager-role-node-cluster-role-binding +#roleRef: +# apiGroup: rbac.authorization.k8s.io +# kind: ClusterRole +# name: oracle-database-operator-manager-role-node +#subjects: +#- kind: ServiceAccount +# name: default +# namespace: oracle-database-operator-system +EOF + +cat<$(STORAGE_CLASS_RBAC) +#--- +#apiVersion: rbac.authorization.k8s.io/v1 +#kind: ClusterRole +#metadata: +# name: oracle-database-operator-manager-role-storage-class +#rules: +#- apiGroups: +# - storage.k8s.io +# resources: +# - storageclasses +# verbs: +# - get +# - list +# - watch +#--- +#apiVersion: rbac.authorization.k8s.io/v1 +#kind: ClusterRoleBinding +#metadata: +# name: oracle-database-operator-manager-role-storage-class-cluster-role-binding +#roleRef: +# apiGroup: rbac.authorization.k8s.io +# kind: ClusterRole +# name: oracle-database-operator-manager-role-storage-class +#subjects: +#- kind: ServiceAccount +# name: default +# namespace: oracle-database-operator-system +#--- +EOF + +cat<$(PERSISTENT_VOLUME_RBAC) +# +#apiVersion: rbac.authorization.k8s.io/v1 +#kind: ClusterRole +#metadata: +# name: oracle-database-operator-manager-role-persistent-volume +#rules: +#- apiGroups: +# - "" +# resources: +# - persistentvolumes +# verbs: +# - get +# - list +# - watch +#--- +#apiVersion: rbac.authorization.k8s.io/v1 +#kind: ClusterRoleBinding +#metadata: +# name: oracle-database-operator-manager-role-persistent-volume-cluster-role-binding +#roleRef: +# apiGroup: rbac.authorization.k8s.io +# kind: ClusterRole +# name: oracle-database-operator-manager-role-persistent-volume +#subjects: +#- kind: ServiceAccount +# name: default +# namespace: oracle-database-operator-system +#--- +# +EOF + +$(KUBECTL) $(1) -f $(NODE_RBAC) +$(KUBECTL) $(1) -f $(STORAGE_CLASS_RBAC) +$(KUBECTL) $(1) -f $(PERSISTENT_VOLUME_RBAC) + +endef + +step4: + $(call persistenvolume,$(ACTION)) +step4a: + $(MAKE) -f $(MAKEFILE) ACTION=apply step4 +step4d: + $(MAKE) -f $(MAKEFILE) ACTION=delete step4 + +step5a: + $(KUBECTL) create secret generic $(SIDB_SECRET) --from-literal=password=$(SIDB_PASSWORD) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic $(ORDS_SECRET) --from-literal=password=$(SIDB_PASSWORD) -n $(RSTNAMESPACE) +step5d: + $(KUBECTL) delete secret $(SIDB_SECRET) -n $(OPRNAMESPACE) + $(KUBECTL) delete secret $(ORDS_SECRET) -n $(RSTNAMESPACE) + +define registry_secret +printf "#!/bin/bash \n" >$(SECRET_CONTAINER_REGISTRY_SCRIPT) +printf "echo enter password for $(USER_CONTAINER_REGISTRY)@$(ORACLE_CONTAINER_REGISTRY) \n" >$(SECRET_CONTAINER_REGISTRY_SCRIPT) +printf "read -s scpwd \n" >>$(SECRET_CONTAINER_REGISTRY_SCRIPT) +printf "$(KUBECTL) create secret docker-registry $(SECRET_CONTAINER_REGISTRY) --docker-server=$(ORACLE_CONTAINER_REGISTRY) --docker-username=$(USER_CONTAINER_REGISTRY) --docker-password=\u0024scpwd --docker-email=$(USER_CONTAINER_REGISTRY) -n $(OPRNAMESPACE) \n" >>$(SECRET_CONTAINER_REGISTRY_SCRIPT) +printf "$(KUBECTL) create secret docker-registry $(SECRET_CONTAINER_REGISTRY) --docker-server=$(ORACLE_CONTAINER_REGISTRY) --docker-username=$(USER_CONTAINER_REGISTRY) --docker-password=\u0024scpwd --docker-email=$(USER_CONTAINER_REGISTRY) -n $(RSTNAMESPACE) \n" >>$(SECRET_CONTAINER_REGISTRY_SCRIPT) + +bash $(SECRET_CONTAINER_REGISTRY_SCRIPT) +endef + +step6a: + $(call registry_secret) + +step6d: + $(KUBECTL) delete secret $(SECRET_CONTAINER_REGISTRY) -n $(OPRNAMESPACE) + + +define sidb + +cat<$(SIDB_CREATION) +#apiVersion: database.oracle.com/v1alpha1 +#kind: SingleInstanceDatabase +#metadata: +# name: oraoper-sidb +# namespace: $(OPRNAMESPACE) +#spec: +# replicas: 1 +# image: +# pullFrom: $(SIDB_IMAGE) +# pullSecrets: $(SECRET_CONTAINER_REGISTRY) +# prebuiltDB: true +# sid: FREE +# listenerPort: 30001 +# edition: free +# adminPassword: +# secretName: $(SIDB_SECRET) +# secretKey: password +# pdbName: FREEPDB1 +EOF + +$(KUBECTL) $(1) -f $(SIDB_CREATION) +endef + +step7: + $(call sidb,$(ACTION)) +step7a: + $(MAKE) -f $(MAKEFILE) ACTION=apply step7 +step7d: + $(MAKE) -f $(MAKEFILE) ACTION=delete step7 + + +define restservice +cat<$(REST_SERVER_CREATION) +#apiVersion: database.oracle.com/v1alpha1 +#kind: $(KIND) +#metadata: +# name: $(REST_SERVER_NAME) +# namespace: $(RSTNAMESPACE) +#spec: +# image: $(ORDS_IMAGE) +# forceRestart: true +# globalSettings: +# database.api.enabled: true +# poolSettings: +# - poolName: default +# autoUpgradeORDS: true +# autoUpgradeAPEX: true +# restEnabledSql.active: true +# plsql.gateway.mode: direct +# db.connectionType: customurl +# db.customURL: jdbc:oracle:thin:@//$(2) +# db.username: ORDS_PUBLIC_USER +# db.secret: +# secretName: $(ORDS_SECRET) +# db.adminUser: SYS +# db.adminUser.secret: +# secretName: $(ORDS_SECRET) +# +EOF + +[ $(3) -eq 1 ] && { +sed -i 's/SYS/SYT/g' $(REST_SERVER_CREATION) +echo -e "TYPO" +} + +$(KUBECTL) $(1) -f $(REST_SERVER_CREATION) +endef + +step8: + $(eval TNS_ALIAS_CDB := $(shell $(KUBECTL) get SingleInstanceDatabase -n $(OPRNAMESPACE) --template '{{range .items}}{{.status.clusterConnectString}}{{"\n"}}{{end}}')) + $(eval TNS_ALIAS_PDB := $(shell $(KUBECTL) get SingleInstanceDatabase -n $(OPRNAMESPACE) --template '{{range .items}}{{.status.pdbConnectString}}{{"\n"}}{{end}}')) + echo $(TNS_ALIAS) + $(call restservice,$(ACTION),$(TNS_ALIAS_PDB),$(ERR)) +step8a: + $(MAKE) -f $(MAKEFILE) ACTION=apply step8 ERR=0 +step8d: + $(MAKE) -f $(MAKEFILE) ACTION=delete step8 ERR=0 +step8e: + $(MAKE) -f $(MAKEFILE) ACTION=apply step8 ERR=1 + +reloadop: + echo "RESTARTING OPERATOR" + $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) + $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) + $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) + $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + +loginords: + @$(eval RESTPOD := $(shell $(KUBECTL) get pods --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' -n $(RSTNAMESPACE))) + $(KUBECTL) logs $(RESTPOD) -n $(RSTNAMESPACE) + $(KUBECTL) exec $(RESTPOD) -n $(RSTNAMESPACE) -it -- /bin/bash + +logindb: + $(eval PODPDB := $(shell $(KUBECTL) get pods --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' -n $(OPRNAMESPACE)|grep -v oracle-database-operator)) + echo $(PODPDB) + $(KUBECTL) exec $(PODPDB) -n $(OPRNAMESPACE) -it -- bash + + +report: + $(KUBECTL) get pods -n $(OPRNAMESPACE) + $(KUBECTL) get SingleInstanceDatabase -n $(OPRNAMESPACE) + $(KUBECTL) get pods -n $(RSTNAMESPACE) + $(KUBECTL) get $(KIND) -n $(RSTNAMESPACE) + + +someattributes: + kubectl get SingleInstanceDatabase -n oracle-database-operator-system --template '{{range .items}}{{.status.connectString}}{{"\n"}}{{end}}' + kubectl get SingleInstanceDatabase -n oracle-database-operator-system --template '{{range .items}}{{.status.tcpsConnectString}}{{"\n"}}{{end}}' + kubectl get SingleInstanceDatabase -n oracle-database-operator-system --template '{{range .items}}{{.status.clusterConnectString}}{{"\n"}}{{end}}' + kubectl get SingleInstanceDatabase -n oracle-database-operator-system --template '{{range .items}}{{.status.tcpsPdbConnectString}}{{"\n"}}{{end}}' + kubectl get SingleInstanceDatabase -n oracle-database-operator-system --template '{{range .items}}{{.status.pdbConnectString}}{{"\n"}}{{end}}' + + + + + +dump: + @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) + @$(eval DIAGFILE := ./opdmp.$(TMPSP)) + @>$(DIAGFILE) + @echo "OPERATOR DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + + + +step9: sql +define dbenv +$(1): DB_PWD=`$(KUBECTL) get secrets sidb-db-auth -n $(OPRNAMESPACE) --template='{{.data.password | base64decode}}'` +$(1): POD_NAME=`$(KUBECTL) get pod -l "app=oraoper-sidb" -o custom-columns=NAME:.metadata.name -n $(OPRNAMESPACE) --no-headers` +$(1): TNSSTR=`$(KUBECTL) get SingleInstanceDatabase -n $(OPRNAMESPACE) --template '{{range .items}}{{.status.pdbConnectString}}{{"\n"}}{{end}}'` +endef + +$(eval $(call dbenv,sqlplus sql)) +#$(eval $(call dbenv,sqlplus)) + +define copyfile +cat <create_mong_schema.sql +drop user MONGO cascade; +set echo on +set head on +create user MONGO identified by "My_Password1!"; +grant soda_app, create session, create table, create view, create sequence, create procedure, create job, +unlimited tablespace to MONGO; +conn MONGO/My_Password1!@${TNSSTR} +exec ords.enable_schema; +exit; +EOF +$(KUBECTL) cp ./create_mong_schema.sql $(POD_NAME):/home/oracle -n $(OPRNAMESPACE) +endef + +sql: + echo $(TNSSTR) + $(call copyfile) + @$(KUBECTL) exec -it $(POD_NAME) -n $(OPRNAMESPACE) -- sqlplus SYSTEM/$(DB_PWD)@$(TNSSTR) @/home/oracle/create_mong_schema.sql + +sqlplus: + @$(KUBECTL) exec -it $(POD_NAME) -n $(OPRNAMESPACE) -- sqlplus SYSTEM/$(DB_PWD)@$(TNSSTR) + + +define restservicemongo +cat <$(REST_SERVER_CREATION_MONGO) +#apiVersion: database.oracle.com/v1alpha1 +#kind: $(KIND) +#metadata: +# name: $(REST_SERVER_NAME_MONGO) +# namespace: $(RSTNAMESPACE) +#spec: +# image: $(ORDS_IMAGE.1) +# forceRestart: true +# globalSettings: +# database.api.enabled: true +# mongo.enabled: true +# poolSettings: +# - poolName: default +# autoUpgradeORDS: true +# restEnabledSql.active: true +# plsql.gateway.mode: direct +# jdbc.MaxConnectionReuseCount: 5000 +# jdbc.MaxConnectionReuseTime: 900 +# jdbc.SecondsToTrustIdleConnection: 1 +# jdbc.InitialLimit: 100 +# jdbc.MaxLimit: 100 +# db.connectionType: customurl +# db.customURL: jdbc:oracle:thin:@//${2} +# db.username: ORDS_PUBLIC_USER +# db.secret: +# secretName: ords-db-auth +# db.adminUser: SYS +# db.adminUser.secret: +# secretName: ords-db-auth +EOF +$(KUBECTL) $(1) -f $(REST_SERVER_CREATION_MONGO) +endef + + + +step10: + $(eval TNS_ALIAS_PDB := $(shell $(KUBECTL) get SingleInstanceDatabase -n $(OPRNAMESPACE) --template '{{range .items}}{{.status.pdbConnectString}}{{"\n"}}{{end}}')) + echo $(TNS_ALIAS_PDB) + $(call restservicemongo,$(ACTION),$(TNS_ALIAS_PDB)) +step10a: + $(MAKE) -f $(MAKEFILE) ACTION=apply step10 +step10d: + $(MAKE) -f $(MAKEFILE) ACTION=delete step10 + + +step11: + echo "Open a port-forward to the MongoAPI service" + @nohup $(KUBECTL) port-forward service/$(REST_SERVER_NAME_MONGO) 27017:27017 -n $(RSTNAMESPACE) 1>portfwd.log 2>&1 & + @echo "DOWNLOADING MONGOSH" + @$(CURL) https://downloads.mongodb.com/compass/$(MONGOSH).tgz --output mongosh-2.3.1-linux-x64.tgz + @echo "UNTAR FILE" + @$(TAR) -zxvf $(MONGOSH).tgz + ./$(MONGOSH)/bin/mongosh --tlsAllowInvalidCertificates 'mongodb://MONGO:My_Password1!@localhost:27017/MONGO?authMechanism=PLAIN&authSource=$external&tls=true&retryWrites=false&loadBalanced=true' + @echo "STOP PORT FRWD" + @kill `ps -ef | grep kubectl | grep 27017 | grep -v grep | awk '{printf $$2}'` + $(RM) $(MONGOSH).tgz + $(RM) -rf ./$(MONGOSH) + + +define buildtns +echo "Building tnsnames.ora" +cat <$(TNSADMIN)/$(TNSNAMES) +$(PDB1)=$(TNS1) + +$(PDB2)=$(TNS2) +EOF +$(KUBECTL) create secret generic multi-tns-admin -n $(RSTNAMESPACE) --from-file=$(TNSADMIN)/ +endef + +step12a: + $(call buildtns) + +step12d: + $(KUBECTL) delete secret multi-tns-admin -n $(RSTNAMESPACE) + + +step13a: + $(KUBECTL) create secret generic multi-ords-auth -n $(RSTNAMESPACE) --from-literal=password=chageme + $(KUBECTL) create secret generic pdb1-priv-auth -n $(RSTNAMESPACE) --from-literal=password=WElcome_12## + $(KUBECTL) create secret generic pdb2-priv-auth -n $(RSTNAMESPACE) --from-literal=password=WElcome_12## + +step13d: + $(KUBECTL) delete secret multi-ords-auth -n $(RSTNAMESPACE) + $(KUBECTL) delete secret pdb1-priv-auth -n $(RSTNAMESPACE) + $(KUBECTL) delete secret pdb2-priv-auth -n $(RSTNAMESPACE) + + +define multisrv +cat <$(MULTISRV_MANIFEST) +#apiVersion: database.oracle.com/v1alpha1 +#kind: $(KIND) +#metadata: +# name: ords-multi-pool +# namespace: $(RSTNAMESPACE) +#spec: +# image: container-registry.oracle.com/database/ords:24.1.1 +# forceRestart: true +# globalSettings: +# database.api.enabled: true +# poolSettings: +# - poolName: pdb1 +# autoUpgradeORDS: true +# db.connectionType: tns +# db.tnsAliasName: pdb1 +# tnsAdminSecret: +# secretName: multi-tns-admin +# restEnabledSql.active: true +# feature.sdw: true +# plsql.gateway.mode: proxied +# db.username: ORDS_PUBLIC_USER +# db.secret: +# secretName: multi-ords-auth +# db.adminUser: SYS +# db.adminUser.secret: +# secretName: pdb1-priv-auth +# - poolName: pdb2 +# db.connectionType: tns +# db.tnsAliasName: PDB2 +# tnsAdminSecret: +# secretName: multi-tns-admin +# restEnabledSql.active: true +# feature.sdw: true +# plsql.gateway.mode: proxied +# db.username: ORDS_PUBLIC_USER +# db.secret: +# secretName: multi-ords-auth +# db.adminUser: SYS +# db.adminUser.secret: +# secretName: pdb2-priv-auth + +# +EOF +[ $(2) -eq 1 ] && { +sed -i 's/SYS/SYT/g' $(MULTISRV_MANIFEST) +echo -e "TYPO" +} + +$(KUBECTL) $(1) -f $(MULTISRV_MANIFEST) +endef + +step14: + $(call multisrv,$(ACTION),$(ERR)) +step14a: + $(MAKE) -f $(MAKEFILE) ACTION=apply ERR=0 step14 +step14d: + $(MAKE) -f $(MAKEFILE) ACTION=delete ERR=0 step14 +step14e: + $(MAKE) -f $(MAKEFILE) ACTION=apply ERR=1 step14 + + +define dumpinit +#!/bin/bash +NAMESPACE=${1} +KUBECTL=/usr/bin/kubectl +for _pod in `${KUBECTL} get pods --no-headers -o custom-columns=":metadata.name" --no-headers -n $${NAMESPACE}` +do + for _podinit in `${KUBECTL} get pod $${_pod} -n $${NAMESPACE} -o="custom-columns=INIT-CONTAINERS:.spec.initContainers[*].name" --no-headers` + do + echo "DUMPINIT $${_pod}:$${_podinit}" + ${KUBECTL} logs -f --since=0 $${_pod} -n $${NAMESPACE} -c $${_podinit} + done +done +endef + +diagordsinit: + $(call dumpinit ,$(RSTNAMESPACE)) + diff --git a/docs/ordsservices/usecase01/node-rbac.yaml b/docs/ordsservices/usecase01/node-rbac.yaml new file mode 100644 index 00000000..58823a22 --- /dev/null +++ b/docs/ordsservices/usecase01/node-rbac.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role-node +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-manager-role-node-cluster-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role-node +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/ordsservices/usecase01/node_rbac.yaml b/docs/ordsservices/usecase01/node_rbac.yaml new file mode 100644 index 00000000..58823a22 --- /dev/null +++ b/docs/ordsservices/usecase01/node_rbac.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role-node +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-manager-role-node-cluster-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role-node +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/ordsservices/usecase01/oracle-database-operator.yaml b/docs/ordsservices/usecase01/oracle-database-operator.yaml new file mode 100644 index 00000000..87cf6582 --- /dev/null +++ b/docs/ordsservices/usecase01/oracle-database-operator.yaml @@ -0,0 +1,4956 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomouscontainerdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousContainerDatabase + listKind: AutonomousContainerDatabaseList + plural: autonomouscontainerdatabases + shortNames: + - acd + - acds + singular: autonomouscontainerdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + description: 'AutonomousContainerDatabasePatchModelEnum Enum with underlying type: string' + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + description: AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase + properties: + lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomousdatabasebackups.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseBackup + listKind: AutonomousDatabaseBackupList + plural: autonomousdatabasebackups + shortNames: + - adbbu + - adbbus + singular: autonomousdatabasebackup + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AutonomousDatabaseBackupSpec defines the desired state of AutonomousDatabaseBackup + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + isLongTermBackup: + type: boolean + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + retentionPeriodInDays: + type: integer + target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' + properties: + k8sADB: + description: "*********************** *\tADB spec ***********************" + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + description: AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with underlying type: string' + type: string + timeEnded: + type: string + timeStarted: + type: string + type: + description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying type: string' + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomousdatabaserestores.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseRestore + listKind: AutonomousDatabaseRestoreList + plural: autonomousdatabaserestores + shortNames: + - adbr + - adbrs + singular: autonomousdatabaserestore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore + properties: + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.' + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + description: 'The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT' + type: string + type: object + type: object + target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' + properties: + k8sADB: + description: "*********************** *\tADB spec ***********************" + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + description: AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore + properties: + dbName: + type: string + displayName: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + status: + description: 'WorkRequestStatusEnum Enum with underlying type: string' + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomousdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabase + listKind: AutonomousDatabaseList + plural: autonomousdatabases + shortNames: + - adb + - adbs + singular: autonomousdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousDatabase is the Schema for the autonomousdatabases API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase Important: Run "make" to regenerate code after modifying this file' + properties: + details: + description: AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase + properties: + adminPassword: + properties: + k8sSecret: + description: "*********************** *\tSecret specs ***********************" + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + autonomousContainerDatabase: + description: ACDSpec defines the spec of the target for backup/restore runs. The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup + properties: + k8sACD: + description: "*********************** *\tACD specs ***********************" + properties: + name: + type: string + type: object + ociACD: + properties: + ocid: + type: string + type: object + type: object + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying type: string' + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + licenseModel: + description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying type: string' + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + lifecycleState: + description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' + type: string + networkAccess: + properties: + accessControlList: + items: + type: string + type: array + accessType: + enum: + - "" + - PUBLIC + - RESTRICTED + - PRIVATE + type: string + isAccessControlEnabled: + type: boolean + isMTLSConnectionRequired: + type: boolean + privateEndpoint: + properties: + hostnamePrefix: + type: string + nsgOCIDs: + items: + type: string + type: array + subnetOCID: + type: string + type: object + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + description: "*********************** *\tSecret specs ***********************" + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + type: object + type: object + hardLink: + default: false + type: boolean + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + required: + - details + type: object + status: + description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.6.1 + name: cdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: CDB + listKind: CDBList + plural: cdbs + singular: cdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: CDB is the Schema for the cdbs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: CDBSpec defines the desired state of CDB + properties: + cdbAdminPwd: + description: Password for the CDB Administrator to manage PDB lifecycle + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + description: User in the root container with sysdba priviledges to manage PDB lifecycle + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + description: Name of the CDB + type: string + cdbTlsCrt: + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + description: DB server port + type: integer + dbServer: + description: Name of the DB server + type: string + dbTnsurl: + type: string + nodeSelector: + additionalProperties: + type: string + description: Node Selector for running the Pod + type: object + ordsImage: + description: ORDS Image Name + type: string + ordsImagePullPolicy: + description: ORDS Image Pull Policy + enum: + - Always + - Never + type: string + ordsImagePullSecret: + description: The name of the image pull secret in case of a private docker repository. + type: string + ordsPort: + description: ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. + type: integer + ordsPwd: + description: Password for user ORDS_PUBLIC_USER + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + description: Number of ORDS Containers to create + type: integer + serviceName: + description: Name of the CDB Service + type: string + sysAdminPwd: + description: Password for the CDB System Administrator + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + description: Password for the Web Server User + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + description: CDBStatus defines the observed state of CDB + properties: + msg: + description: Message + type: string + phase: + description: Phase of the CDB Resource + type: string + status: + description: CDB Resource Status + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver + properties: + database: + description: DatabaseObserverDatabase defines the database details used for DatabaseObserver + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver + properties: + configuration: + properties: + configmap: + description: ConfigMapDetails defines the configmap name + properties: + configmapName: + type: string + key: + type: string + type: object + type: object + image: + type: string + service: + description: DatabaseObserverService defines the exporter service component of DatabaseObserver + properties: + port: + format: int32 + type: integer + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + description: PrometheusConfig defines the generated resources for Prometheus + properties: + labels: + additionalProperties: + type: string + type: object + port: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver + properties: + conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + required: + - conditions + - exporterConfig + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: dataguardbrokers.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + singular: dataguardbroker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: DataguardBroker is the Schema for the dataguardbrokers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DataguardBrokerSpec defines the desired state of DataguardBroker + properties: + fastStartFailOver: + properties: + enable: + type: boolean + strategy: + items: + description: FSFO strategy + properties: + sourceDatabaseRef: + type: string + targetDatabaseRefs: + type: string + type: object + type: array + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + description: DataguardBrokerStatus defines the observed state of DataguardBroker + properties: + clusterConnectString: + type: string + externalConnectString: + type: string + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: dbcssystems.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: dbcssystems + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: DbcsSystem is the Schema for the dbcssystems API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DbcsSystemSpec defines the desired state of DbcsSystem + properties: + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + description: DB Backup COnfig Network Struct + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - sshPublicKeys + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + ociConfigMap: + type: string + ociSecret: + type: string + required: + - ociConfigMap + type: object + status: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbEdition: + type: string + dbInfo: + items: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: oraclerestdataservices.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestDataService + listKind: OracleRestDataServiceList + plural: oraclerestdataservices + singular: oraclerestdataservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: OracleRestDataService is the Schema for the oraclerestdataservices API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService + properties: + adminPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + apexPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + description: OracleRestDataServicePersistence defines the storage releated params + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + description: OracleRestDataServicePDBSchemas defines the PDB Schemas to be ORDS Enabled + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + description: OracleRestDataServiceStatus defines the observed state of OracleRestDataService + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: ordssrvs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OrdsSrvs + listKind: OrdsSrvsList + plural: ordssrvs + singular: ordssrvs + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: status + type: string + - jsonPath: .status.workloadType + name: workloadType + type: string + - jsonPath: .status.ordsVersion + name: ordsVersion + type: string + - jsonPath: .status.httpPort + name: httpPort + type: integer + - jsonPath: .status.httpsPort + name: httpsPort + type: integer + - jsonPath: .status.mongoPort + name: MongoPort + type: integer + - jsonPath: .status.restartRequired + name: restartRequired + type: boolean + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.ordsInstalled + name: OrdsInstalled + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: OrdsSrvs is the Schema for the ordssrvs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: OrdsSrvsSpec defines the desired state of OrdsSrvs + properties: + forceRestart: + description: Specifies whether to restart pods when Global or Pool configurations change + type: boolean + globalSettings: + description: Contains settings that are configured across the entire ORDS instance. + properties: + cache.metadata.enabled: + description: Specifies the setting to enable or disable metadata caching. + type: boolean + cache.metadata.graphql.expireAfterAccess: + description: Specifies the duration after a GraphQL schema is not accessed from the cache that it expires. + format: int64 + type: integer + cache.metadata.graphql.expireAfterWrite: + description: Specifies the duration after a GraphQL schema is cached that it expires and has to be loaded again. + format: int64 + type: integer + cache.metadata.jwks.enabled: + description: Specifies the setting to enable or disable JWKS caching. + type: boolean + cache.metadata.jwks.expireAfterAccess: + description: Specifies the duration after a JWK is not accessed from the cache that it expires. By default this is disabled. + format: int64 + type: integer + cache.metadata.jwks.expireAfterWrite: + description: Specifies the duration after a JWK is cached, that is, it expires and has to be loaded again. + format: int64 + type: integer + cache.metadata.jwks.initialCapacity: + description: Specifies the initial capacity of the JWKS cache. + format: int32 + type: integer + cache.metadata.jwks.maximumSize: + description: Specifies the maximum capacity of the JWKS cache. + format: int32 + type: integer + cache.metadata.timeout: + description: Specifies the setting to determine for how long a metadata record remains in the cache. Longer duration means, it takes longer to view the applied changes. The formats accepted are based on the ISO-8601 duration format. + format: int64 + type: integer + certSecret: + description: 'Specifies the Secret containing the SSL Certificates Replaces: standalone.https.cert and standalone.https.cert.key' + properties: + cert: + description: Specifies the Certificate + type: string + key: + description: Specifies the Certificate Key + type: string + secretName: + description: Specifies the name of the certificate Secret + type: string + required: + - cert + - key + - secretName + type: object + database.api.enabled: + description: Specifies whether the Database API is enabled. + type: boolean + database.api.management.services.disabled: + description: Specifies to disable the Database API administration related services. Only applicable when Database API is enabled. + type: boolean + db.invalidPoolTimeout: + description: Specifies how long to wait before retrying an invalid pool. + format: int64 + type: integer + debug.printDebugToScreen: + description: Specifies whether to display error messages on the browser. + type: boolean + enable.mongo.access.log: + default: false + description: Specifies if HTTP request access logs should be enabled If enabled, logs will be written to /opt/oracle/sa/log/global + type: boolean + enable.standalone.access.log: + default: false + description: Specifies if HTTP request access logs should be enabled If enabled, logs will be written to /opt/oracle/sa/log/global + type: boolean + error.responseFormat: + description: Specifies how the HTTP error responses must be formatted. html - Force all responses to be in HTML format json - Force all responses to be in JSON format auto - Automatically determines most appropriate format for the request (default). + type: string + feature.grahpql.max.nesting.depth: + description: Specifies the maximum join nesting depth limit for GraphQL queries. + format: int32 + type: integer + icap.port: + description: Specifies the Internet Content Adaptation Protocol (ICAP) port to virus scan files. Either icap.port or icap.secure.port are required to have a value. + format: int32 + type: integer + icap.secure.port: + description: Specifies the Internet Content Adaptation Protocol (ICAP) port to virus scan files. Either icap.port or icap.secure.port are required to have a value. If values for both icap.port and icap.secure.port are provided, then the value of icap.port is ignored. + format: int32 + type: integer + icap.server: + description: Specifies the Internet Content Adaptation Protocol (ICAP) server name or IP address to virus scan files. The icap.server is required to have a value. + type: string + log.procedure: + description: Specifies whether procedures are to be logged. + type: boolean + mongo.enabled: + description: Specifies to enable the API for MongoDB. + type: boolean + mongo.idle.timeout: + description: Specifies the maximum idle time for a Mongo connection in milliseconds. + format: int64 + type: integer + mongo.op.timeout: + description: Specifies the maximum time for a Mongo database operation in milliseconds. + format: int64 + type: integer + mongo.port: + default: 27017 + description: Specifies the API for MongoDB listen port. + format: int32 + type: integer + request.traceHeaderName: + description: Specifies the name of the HTTP request header that uniquely identifies the request end to end as it passes through the various layers of the application stack. In Oracle this header is commonly referred to as the ECID (Entity Context ID). + type: string + security.credentials.attempts: + description: Specifies the maximum number of unsuccessful password attempts allowed. Enabled by setting a positive integer value. + format: int32 + type: integer + security.credentials.lock.time: + description: Specifies the period to lock the account that has exceeded maximum attempts. + format: int64 + type: integer + security.disableDefaultExclusionList: + description: If this value is set to true, then the Oracle REST Data Services internal exclusion list is not enforced. Oracle recommends that you do not set this value to true. + type: boolean + security.exclusionList: + description: Specifies a pattern for procedures, packages, or schema names which are forbidden to be directly executed from a browser. + type: string + security.externalSessionTrustedOrigins: + description: Specifies to trust Access from originating domains + type: string + security.forceHTTPS: + description: Specifies to force HTTPS; this is set to default to false as in real-world TLS should terminiate at the LoadBalancer + type: boolean + security.httpsHeaderCheck: + description: 'Specifies that the HTTP Header contains the specified text Usually set to ''X-Forwarded-Proto: https'' coming from a load-balancer' + type: string + security.inclusionList: + description: Specifies a pattern for procedures, packages, or schema names which are allowed to be directly executed from a browser. + type: string + security.maxEntries: + description: Specifies the maximum number of cached procedure validations. Set this value to 0 to force the validation procedure to be invoked on each request. + format: int32 + type: integer + security.verifySSL: + description: Specifies whether HTTPS is available in your environment. + type: boolean + standalone.context.path: + default: /ords + description: Specifies the context path where ords is located. + type: string + standalone.http.port: + default: 8080 + description: Specifies the HTTP listen port. + format: int32 + type: integer + standalone.https.host: + description: Specifies the SSL certificate hostname. + type: string + standalone.https.port: + default: 8443 + description: Specifies the HTTPS listen port. + format: int32 + type: integer + standalone.stop.timeout: + description: Specifies the period for Standalone Mode to wait until it is gracefully shutdown. + format: int64 + type: integer + type: object + image: + description: Specifies the ORDS container image + type: string + imagePullPolicy: + default: IfNotPresent + description: Specifies the ORDS container image pull policy + enum: + - IfNotPresent + - Always + - Never + type: string + imagePullSecrets: + description: Specifies the Secret Name for pulling the ORDS container image + type: string + poolSettings: + description: Contains settings for individual pools/databases + items: + properties: + apex.security.administrator.roles: + description: Specifies the comma delimited list of additional roles to assign authenticated APEX administrator type users. + type: string + apex.security.user.roles: + description: Specifies the comma delimited list of additional roles to assign authenticated regular APEX users. + type: string + autoUpgradeAPEX: + default: false + description: Specify whether to perform APEX installation/upgrades automatically The db.adminUser and db.adminUser.secret must be set, otherwise setting is ignored This setting will be ignored for ADB + type: boolean + autoUpgradeORDS: + default: false + description: Specify whether to perform ORDS installation/upgrades automatically The db.adminUser and db.adminUser.secret must be set, otherwise setting is ignored This setting will be ignored for ADB + type: boolean + db.adminUser: + description: Specifies the username for the database account that ORDS uses for administration operations in the database. + type: string + db.adminUser.secret: + description: 'Specifies the Secret with the dbAdminUser (SYS) and dbAdminPassword values for the database account that ORDS uses for administration operations in the database. replaces: db.adminUser.password' + properties: + passwordKey: + default: password + description: Specifies the key holding the value of the Secret + type: string + secretName: + description: Specifies the name of the password Secret + type: string + required: + - secretName + type: object + db.cdb.adminUser: + description: Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management. + type: string + db.cdb.adminUser.secret: + description: 'Specifies the Secret with the dbCdbAdminUser (SYS) and dbCdbAdminPassword values Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management. Replaces: db.cdb.adminUser.password' + properties: + passwordKey: + default: password + description: Specifies the key holding the value of the Secret + type: string + secretName: + description: Specifies the name of the password Secret + type: string + required: + - secretName + type: object + db.connectionType: + description: The type of connection. + enum: + - basic + - tns + - customurl + type: string + db.credentialsSource: + description: Specifies the source for database credentials when creating a direct connection for running SQL statements. Value can be one of pool or request. If the value is pool, then the credentials defined in this pool is used to create a JDBC connection. If the value request is used, then the credentials in the request is used to create a JDBC connection and if successful, grants the requestor SQL Developer role. + enum: + - pool + - request + type: string + db.customURL: + description: Specifies the JDBC URL connection to connect to the database. + type: string + db.hostname: + description: Specifies the host system for the Oracle database. + type: string + db.poolDestroyTimeout: + description: Indicates how long to wait to gracefully destroy a pool before moving to forcefully destroy all connections including borrowed ones. + format: int64 + type: integer + db.port: + description: Specifies the database listener port. + format: int32 + type: integer + db.secret: + description: Specifies the Secret with the dbUsername and dbPassword values for the connection. + properties: + passwordKey: + default: password + description: Specifies the key holding the value of the Secret + type: string + secretName: + description: Specifies the name of the password Secret + type: string + required: + - secretName + type: object + db.servicename: + description: Specifies the network service name of the database. + type: string + db.sid: + description: Specifies the name of the database. + type: string + db.tnsAliasName: + description: Specifies the TNS alias name that matches the name in the tnsnames.ora file. + type: string + db.username: + default: ORDS_PUBLIC_USER + description: Specifies the name of the database user for the connection. For non-ADB this will default to ORDS_PUBLIC_USER For ADBs this must be specified and not ORDS_PUBLIC_USER If ORDS_PUBLIC_USER is specified for an ADB, the workload will fail + type: string + db.wallet.zip.service: + description: Specifies the service name in the wallet archive for the pool. + type: string + dbWalletSecret: + description: 'Specifies the Secret containing the wallet archive containing connection details for the pool. Replaces: db.wallet.zip' + properties: + secretName: + description: Specifies the name of the Database Wallet Secret + type: string + walletName: + description: Specifies the Secret key name containing the Wallet + type: string + required: + - secretName + - walletName + type: object + debug.trackResources: + description: Specifies to enable tracking of JDBC resources. If not released causes in resource leaks or exhaustion in the database. Tracking imposes a performance overhead. + type: boolean + feature.openservicebroker.exclude: + description: Specifies to disable the Open Service Broker services available for the pool. + type: boolean + feature.sdw: + description: Specifies to enable the Database Actions feature. + type: boolean + http.cookie.filter: + description: Specifies a comma separated list of HTTP Cookies to exclude when initializing an Oracle Web Agent environment. + type: string + jdbc.DriverType: + description: Specifies the JDBC driver type. + enum: + - thin + - oci8 + type: string + jdbc.InactivityTimeout: + description: Specifies how long an available connection can remain idle before it is closed. The inactivity connection timeout is in seconds. + format: int32 + type: integer + jdbc.InitialLimit: + description: Specifies the initial size for the number of connections that will be created. The default is low, and should probably be set higher in most production environments. + format: int32 + type: integer + jdbc.MaxConnectionReuseCount: + description: Specifies the maximum number of times to reuse a connection before it is discarded and replaced with a new connection. + format: int32 + type: integer + jdbc.MaxConnectionReuseTime: + description: Sets the maximum connection reuse time property. + format: int32 + type: integer + jdbc.MaxLimit: + description: Specifies the maximum number of connections. Might be too low for some production environments. + format: int32 + type: integer + jdbc.MaxStatementsLimit: + description: Specifies the maximum number of statements to cache for each connection. + format: int32 + type: integer + jdbc.MinLimit: + description: Specifies the minimum number of connections. + format: int32 + type: integer + jdbc.SecondsToTrustIdleConnection: + description: Sets the time in seconds to trust an idle connection to skip a validation test. + format: int32 + type: integer + jdbc.auth.admin.role: + description: Identifies the database role that indicates that the database user must get the SQL Administrator role. + type: string + jdbc.auth.enabled: + description: Specifies if the PL/SQL Gateway calls can be authenticated using database users. If the value is true then this feature is enabled. If the value is false, then this feature is disabled. Oracle recommends not to use this feature. This feature used only to facilitate customers migrating from mod_plsql. + type: boolean + jdbc.cleanup.mode: + description: Specifies how a pooled JDBC connection and corresponding database session, is released when a request has been processed. + type: string + jdbc.statementTimeout: + description: Specifies a timeout period on a statement. An abnormally long running query or script, executed by a request, may leave it in a hanging state unless a timeout is set on the statement. Setting a timeout on the statement ensures that all the queries automatically timeout if they are not completed within the specified time period. + format: int32 + type: integer + misc.defaultPage: + description: Specifies the default page to display. The Oracle REST Data Services Landing Page. + type: string + misc.pagination.maxRows: + description: Specifies the maximum number of rows that will be returned from a query when processing a RESTful service and that will be returned from a nested cursor in a result set. Affects all RESTful services generated through a SQL query, regardless of whether the resource is paginated. + format: int32 + type: integer + owa.trace.sql: + description: If it is true, then it causes a trace of the SQL statements performed by Oracle Web Agent to be echoed to the log. + type: boolean + plsql.gateway.mode: + description: Indicates if the PL/SQL Gateway functionality should be available for a pool or not. Value can be one of disabled, direct, or proxied. If the value is direct, then the pool serves the PL/SQL Gateway requests directly. If the value is proxied, the PLSQL_GATEWAY_CONFIG view is used to determine the user to whom to proxy. + enum: + - disabled + - direct + - proxied + type: string + poolName: + description: Specifies the Pool Name + type: string + procedure.preProcess: + description: Specifies the procedure name(s) to execute prior to executing the procedure specified on the URL. Multiple procedure names must be separated by commas. + type: string + procedure.rest.preHook: + description: Specifies the function to be invoked prior to dispatching each Oracle REST Data Services based REST Service. The function can perform configuration of the database session, perform additional validation or authorization of the request. If the function returns true, then processing of the request continues. If the function returns false, then processing of the request is aborted and an HTTP 403 Forbidden status is returned. + type: string + procedurePostProcess: + description: Specifies the procedure name(s) to execute after executing the procedure specified on the URL. Multiple procedure names must be separated by commas. + type: string + restEnabledSql.active: + description: Specifies whether the REST-Enabled SQL service is active. + type: boolean + security.jwks.connection.timeout: + description: Specifies the maximum amount of time before timing-out when accessing a JWK url. + format: int64 + type: integer + security.jwks.read.timeout: + description: Specifies the maximum amount of time reading a response from the JWK url before timing-out. + format: int64 + type: integer + security.jwks.refresh.interval: + description: Specifies the minimum interval between refreshing the JWK cached value. + format: int64 + type: integer + security.jwks.size: + description: Specifies the maximum number of bytes read from the JWK url. + format: int32 + type: integer + security.jwt.allowed.age: + description: Specifies the maximum allowed age of a JWT in seconds, regardless of expired claim. The age of the JWT is taken from the JWT issued at claim. + format: int64 + type: integer + security.jwt.allowed.skew: + description: Specifies the maximum skew the JWT time claims are accepted. This is useful if the clock on the JWT issuer and ORDS differs by a few seconds. + format: int64 + type: integer + security.jwt.profile.enabled: + description: 'Specifies whether the JWT Profile authentication is available. Supported values:' + type: boolean + security.requestAuthenticationFunction: + description: Specifies an authentication function to determine if the requested procedure in the URL should be allowed or disallowed for processing. The function should return true if the procedure is allowed; otherwise, it should return false. If it returns false, Oracle REST Data Services will return WWW-Authenticate in the response header. + type: string + security.requestValidationFunction: + default: ords_util.authorize_plsql_gateway + description: Specifies a validation function to determine if the requested procedure in the URL should be allowed or disallowed for processing. The function should return true if the procedure is allowed; otherwise, return false. + type: string + security.validationFunctionType: + description: 'Indicates the type of security.requestValidationFunction: javascript or plsql.' + enum: + - plsql + - javascript + type: string + soda.defaultLimit: + description: When using the SODA REST API, specifies the default number of documents returned for a GET request on a collection when a limit is not specified in the URL. Must be a positive integer, or "unlimited" for no limit. + type: string + soda.maxLimit: + description: When using the SODA REST API, specifies the maximum number of documents that will be returned for a GET request on a collection URL, regardless of any limit specified in the URL. Must be a positive integer, or "unlimited" for no limit. + type: string + tnsAdminSecret: + description: 'Specifies the Secret containing the TNS_ADMIN directory Replaces: db.tnsDirectory' + properties: + secretName: + description: Specifies the name of the TNS_ADMIN Secret + type: string + required: + - secretName + type: object + required: + - db.secret + - poolName + type: object + type: array + replicas: + default: 1 + description: Defines the number of desired Replicas when workloadType is Deployment or StatefulSet + format: int32 + minimum: 1 + type: integer + workloadType: + default: Deployment + description: Specifies the desired Kubernetes Workload + enum: + - Deployment + - StatefulSet + - DaemonSet + type: string + required: + - globalSettings + - image + type: object + status: + description: OrdsSrvsStatus defines the observed state of OrdsSrvs + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + httpPort: + description: Indicates the HTTP port of the resource exposed by the pods + format: int32 + type: integer + httpsPort: + description: Indicates the HTTPS port of the resource exposed by the pods + format: int32 + type: integer + mongoPort: + description: Indicates the MongoAPI port of the resource exposed by the pods (if enabled) + format: int32 + type: integer + ordsInstalled: + description: '** PLACE HOLDER' + type: boolean + ordsVersion: + description: Indicates the ORDS version + type: string + restartRequired: + description: Indicates if the resource is out-of-sync with the configuration + type: boolean + status: + description: '** PLACE HOLDER Indicates the current status of the resource' + type: string + workloadType: + description: Indicates the current Workload type of the resource + type: string + required: + - restartRequired + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.6.1 + name: pdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB + listKind: PDBList + plural: pdbs + singular: pdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: PDB is the Schema for the pdbs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PDBSpec defines the desired state of PDB + properties: + action: + description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR.' + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + description: The administrator username for the new PDB. This property is required when the Action property is Create. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + description: The administrator password for the new PDB. This property is required when the Action property is Create. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + description: Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. + type: boolean + assertivePdbDeletion: + description: turn on the assertive approach to delete pdb resource kubectl delete pdb ..... automatically triggers the pluggable database deletion + type: boolean + cdbName: + description: Name of the CDB + type: string + cdbNamespace: + description: CDB Namespace + type: string + cdbResName: + description: Name of the CDB Custom Resource that runs the ORDS container + type: string + copyAction: + description: To copy files or not while cloning a PDB + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + description: Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + description: Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. + type: string + getScript: + description: Whether you need the script only or execute the script + type: boolean + modifyOption: + description: Extra options for opening and closing a PDB + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + description: The name of the new PDB. Relevant for both Create and Plug Actions. + type: string + pdbState: + description: The target state of the PDB + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + description: Whether to reuse temp file + type: boolean + sourceFileNameConversions: + description: This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. + type: string + sparseClonePath: + description: A Path specified for sparse clone snapshot copy. (Optional) + type: string + srcPdbName: + description: Name of the Source PDB from which to clone + type: string + tdeExport: + description: TDE export for unplug operations + type: boolean + tdeImport: + description: TDE import for plug operations + type: boolean + tdeKeystorePath: + description: TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + type: string + tdePassword: + description: TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + description: TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + description: Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. + type: string + totalSize: + description: Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. + type: string + unlimitedStorage: + description: Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. + type: boolean + webServerPwd: + description: Password for the Web ServerPDB User + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + description: XML metadata filename to be used for Plug or Unplug operations + type: string + required: + - action + type: object + status: + description: PDBStatus defines the observed state of PDB + properties: + action: + description: Last Completed Action + type: string + connString: + description: PDB Connect String + type: string + modifyOption: + description: Modify Option of the PDB + type: string + msg: + description: Message + type: string + openMode: + description: Open mode of the PDB + type: string + phase: + description: Phase of the PDB Resource + type: string + status: + description: PDB Resource Status + type: boolean + totalSize: + description: Total size of the PDB + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: shardingdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: ShardingDatabase + listKind: ShardingDatabaseList + plural: shardingdatabases + singular: shardingdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ShardingDatabase is the Schema for the shardingdatabases API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ShardingDatabaseSpec defines the desired state of ShardingDatabase + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + description: CatalogSpec defines the desired state of CatalogSpec + properties: + envVars: + items: + description: EnvironmentVariable represents a named variable accessible for containers. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbEdition: + type: string + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + description: Secret Details + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + fssStorageClass: + type: string + gsm: + items: + description: GsmSpec defines the desired state of GsmSpec + properties: + directorName: + type: string + envVars: + description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. + items: + description: EnvironmentVariable represents a named variable accessible for containers. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + description: Service Definition + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + description: ShardSpace Specs + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: string + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + description: PortMapping is a specification of port mapping for an application deployment. + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' + items: + description: ShardSpec is a specification of Shards for an application deployment. + properties: + deployAs: + type: string + envVars: + items: + description: EnvironmentVariable represents a named variable accessible for containers. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image + type: string + isDelete: + enum: + - enable + - disable + - failed + - force + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 ShardingDatabaseStatus defines the observed state of ShardingDatabase + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.6.1 + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase + properties: + adminPassword: + description: SingleInsatnceAdminPassword defines the secret containing Admin Password mapped to secretKey for Database + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + createAs: + enum: + - primary + - standby + - clone + type: string + dgBrokerConfigured: + type: boolean + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + description: SingleInstanceDatabasePersistence defines the storage size and class for PVC + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + resources: + properties: + limits: + properties: + cpu: + type: string + memory: + type: string + type: object + requests: + properties: + cpu: + type: string + memory: + type: string + type: object + type: object + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + description: SID must be alphanumeric (no special characters, only a-z, A-Z, 0-9), and no longer than 12 characters. + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + required: + - image + type: object + status: + description: SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBrokerConfigured: + type: boolean + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + description: SingleInstanceDatabasePersistence defines the storage size and class for PVC + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: oracle-database-operator-leader-election-role + namespace: oracle-database-operator-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: oracle-database-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - events + - pods + - pods/exec + - pods/log + - replicasets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - deployments + - events + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch +- apiGroups: + - '''''' + resources: + - statefulsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - configmaps + verbs: + - get + - list +- apiGroups: + - apps + resources: + - daemonsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - deployments + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - patch + - update +- apiGroups: + - "" + resources: + - daemonsets/status + verbs: + - get + - patch + - update +- apiGroups: + - "" + resources: + - deployments/status + verbs: + - get + - patch + - update +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - secrets/status + verbs: + - get +- apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - services/status + verbs: + - get + - patch + - update +- apiGroups: + - "" + resources: + - statefulsets/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases/status + verbs: + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - cdbs/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - ordssrvs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - ordssrvs/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - ordssrvs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - pdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/finalizers + verbs: + - update +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get + - patch + - update +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-oracle-database-operator-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: oracle-database-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-oracle-database-operator-proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager-metrics-service + namespace: oracle-database-operator-system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: v1 +kind: Service +metadata: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: oracle-database-operator-serving-cert + namespace: oracle-database-operator-system +spec: + dnsNames: + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local + issuerRef: + kind: Issuer + name: oracle-database-operator-selfsigned-issuer + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: oracle-database-operator-selfsigned-issuer + namespace: oracle-database-operator-system +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: mautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-cdb + failurePolicy: Fail + name: mcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: mdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: moraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-pdb + failurePolicy: Fail + name: mpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: mshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: msingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-observability-oracle-com-v1alpha1-databaseobserver + failurePolicy: Fail + name: mdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + failurePolicy: Fail + name: vautonomouscontainerdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomouscontainerdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: vautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore + failurePolicy: Fail + name: vautonomousdatabaserestore.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabaserestores + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-cdb + failurePolicy: Fail + name: vcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: vdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: voraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-pdb + failurePolicy: Fail + name: vpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: vshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: vsingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - singleinstancedatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-observability-oracle-com-v1alpha1-databaseobserver + failurePolicy: Fail + name: vdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager + namespace: oracle-database-operator-system +spec: + replicas: 3 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --enable-leader-election + command: + - /manager + env: + - name: WATCH_NAMESPACE + value: "oracle-database-operator-system,ordsnamespace" + image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/ordslfcmng:latest + imagePullPolicy: Always + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + resources: + limits: + cpu: 400m + memory: 400Mi + requests: + cpu: 400m + memory: 400Mi + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert +--- diff --git a/docs/ordsservices/usecase01/ords-ns-role-binding.yaml b/docs/ordsservices/usecase01/ords-ns-role-binding.yaml new file mode 100644 index 00000000..2ad9a497 --- /dev/null +++ b/docs/ordsservices/usecase01/ords-ns-role-binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: ordsnamespace-rolebinding + namespace: ordsnamespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/ordsservices/usecase01/persistent-volume-rbac.yaml b/docs/ordsservices/usecase01/persistent-volume-rbac.yaml new file mode 100644 index 00000000..e329ca3e --- /dev/null +++ b/docs/ordsservices/usecase01/persistent-volume-rbac.yaml @@ -0,0 +1,29 @@ + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role-persistent-volume +rules: +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-manager-role-persistent-volume-cluster-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role-persistent-volume +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- + diff --git a/docs/ordsservices/usecase01/rest_server_creation.yaml b/docs/ordsservices/usecase01/rest_server_creation.yaml new file mode 100644 index 00000000..e5492315 --- /dev/null +++ b/docs/ordsservices/usecase01/rest_server_creation.yaml @@ -0,0 +1,25 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: OrdsSrvs +metadata: + name: ords-sidb + namespace: ordsnamespace +spec: + image: container-registry.oracle.com/database/ords:24.1.0 + forceRestart: true + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: default + autoUpgradeORDS: true + autoUpgradeAPEX: true + restEnabledSql.active: true + plsql.gateway.mode: direct + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//204.216.216.178:30001/FREEPDB1 + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: ords-db-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: ords-db-auth + diff --git a/docs/ordsservices/usecase01/rest_server_creation_mongo.yaml b/docs/ordsservices/usecase01/rest_server_creation_mongo.yaml new file mode 100644 index 00000000..4b620f9d --- /dev/null +++ b/docs/ordsservices/usecase01/rest_server_creation_mongo.yaml @@ -0,0 +1,29 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: OrdsSrvs +metadata: + name: ords-sidb-mongo + namespace: ordsnamespace +spec: + image: container-registry.oracle.com/database/ords:24.1.1 + forceRestart: true + globalSettings: + database.api.enabled: true + mongo.enabled: true + poolSettings: + - poolName: default + autoUpgradeORDS: true + restEnabledSql.active: true + plsql.gateway.mode: direct + jdbc.MaxConnectionReuseCount: 5000 + jdbc.MaxConnectionReuseTime: 900 + jdbc.SecondsToTrustIdleConnection: 1 + jdbc.InitialLimit: 100 + jdbc.MaxLimit: 100 + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//158.180.233.248:30001/FREEPDB1 + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: ords-db-auth + db.adminUser: SYS + db.adminUser.secret: + secretName: ords-db-auth diff --git a/docs/ordsservices/usecase01/sidb_create.yaml b/docs/ordsservices/usecase01/sidb_create.yaml new file mode 100644 index 00000000..2d5d8b28 --- /dev/null +++ b/docs/ordsservices/usecase01/sidb_create.yaml @@ -0,0 +1,18 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: SingleInstanceDatabase +metadata: + name: oraoper-sidb + namespace: oracle-database-operator-system +spec: + replicas: 1 + image: + pullFrom: container-registry.oracle.com/database/free:23.4.0.0 + pullSecrets: oracle-container-registry-secret + prebuiltDB: true + sid: FREE + listenerPort: 30001 + edition: free + adminPassword: + secretName: sidb-db-auth + secretKey: password + pdbName: FREEPDB1 diff --git a/docs/ordsservices/usecase01/sqlcreate.sql b/docs/ordsservices/usecase01/sqlcreate.sql new file mode 100644 index 00000000..906af0ad --- /dev/null +++ b/docs/ordsservices/usecase01/sqlcreate.sql @@ -0,0 +1,9 @@ +drop user MONGO cascade +create user MONGO identified by "My_Password1!"; +grant soda_app, create session, create table, create view, create sequence, create procedure, create job, +unlimited tablespace to MONGO; +conn MONGO/My_Password1!@FREEPDB1 +exec ords.enable_schema; + + + diff --git a/docs/ordsservices/usecase01/storage-class-rbac.yaml b/docs/ordsservices/usecase01/storage-class-rbac.yaml new file mode 100644 index 00000000..a34f67d4 --- /dev/null +++ b/docs/ordsservices/usecase01/storage-class-rbac.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role-storage-class +rules: +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-manager-role-storage-class-cluster-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role-storage-class +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- diff --git a/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora b/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora new file mode 100644 index 00000000..16a85226 --- /dev/null +++ b/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora @@ -0,0 +1,3 @@ +pdb1=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb1))) + +pdb2=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb2))) diff --git a/docs/ordsservices/usecase01/tnsnames.ora b/docs/ordsservices/usecase01/tnsnames.ora new file mode 100644 index 00000000..16a85226 --- /dev/null +++ b/docs/ordsservices/usecase01/tnsnames.ora @@ -0,0 +1,3 @@ +pdb1=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb1))) + +pdb2=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb2))) From d8e392c2cf59568e6521053f64041e1b3ed8a2d8 Mon Sep 17 00:00:00 2001 From: ting-lan-wang Date: Thu, 19 Dec 2024 11:14:45 -0500 Subject: [PATCH 141/414] update go.mod --- go.mod | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 90a91797..d2378d98 100644 --- a/go.mod +++ b/go.mod @@ -3,20 +3,20 @@ module github.com/oracle/oracle-database-operator go 1.23.3 require ( - github.com/go-logr/logr v1.4.1 - github.com/onsi/ginkgo/v2 v2.14.0 - github.com/onsi/gomega v1.30.0 - github.com/oracle/oci-go-sdk/v65 v65.49.3 - github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0 + github.com/go-logr/logr v1.4.2 + github.com/onsi/ginkgo/v2 v2.20.2 + github.com/onsi/gomega v1.34.2 + github.com/oracle/oci-go-sdk/v65 v65.77.1 + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.78.2 go.uber.org/zap v1.26.0 - golang.org/x/text v0.14.0 + golang.org/x/text v0.19.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.29.2 - k8s.io/apimachinery v0.29.2 - k8s.io/cli-runtime v0.29.2 - k8s.io/client-go v0.29.2 - k8s.io/kubectl v0.29.2 - sigs.k8s.io/controller-runtime v0.17.3 + k8s.io/api v0.31.3 + k8s.io/apimachinery v0.31.3 + k8s.io/cli-runtime v0.31.3 + k8s.io/client-go v0.31.3 + k8s.io/kubectl v0.31.3 + sigs.k8s.io/controller-runtime v0.19.3 sigs.k8s.io/yaml v1.4.0 ) From f683da3413eae33e8ba7442be2cb591435f8901f Mon Sep 17 00:00:00 2001 From: ting-lan-wang Date: Thu, 19 Dec 2024 11:15:13 -0500 Subject: [PATCH 142/414] go mod tidy --- go.mod | 68 +++++++++--------- go.sum | 212 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 137 insertions(+), 143 deletions(-) diff --git a/go.mod b/go.mod index d2378d98..863f2e99 100644 --- a/go.mod +++ b/go.mod @@ -24,33 +24,33 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.8.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fvbommel/sortorder v1.1.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/imdario/mergo v0.3.6 // indirect @@ -59,10 +59,9 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/moby/spdystream v0.4.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect @@ -70,37 +69,38 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.18.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sony/gobreaker v0.5.0 // indirect - github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.2.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.12.0 // indirect - golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.16.1 // indirect + golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.29.2 // indirect - k8s.io/component-base v0.29.2 // indirect - k8s.io/klog/v2 v2.110.1 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + k8s.io/apiextensions-apiserver v0.31.2 // indirect + k8s.io/component-base v0.31.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect - sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/kustomize/api v0.17.2 // indirect + sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/go.sum b/go.sum index e05155ae..d23debb8 100644 --- a/go.sum +++ b/go.sum @@ -8,53 +8,54 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= -github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= -github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -64,7 +65,6 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -72,9 +72,8 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= @@ -85,25 +84,22 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -123,16 +119,16 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= +github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -144,39 +140,40 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY= -github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/oracle/oci-go-sdk/v65 v65.49.3 h1:HHv+XMZiBYHtoU8Ac/fURdp9v1vJPPCpIbJAWeadREw= -github.com/oracle/oci-go-sdk/v65 v65.49.3/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/oracle/oci-go-sdk/v65 v65.77.1 h1:gqjTXIUWvTihkn470AclxSAMcR1JecqjD2IUtp+sDIU= +github.com/oracle/oci-go-sdk/v65 v65.77.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0 h1:55138zTXw/yRYizPxZ672I/aDD7Yte3uYRAfUjWUu2M= -github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.62.0/go.mod h1:j51242bf6LQwvJ1JPKWApzTnifmCwcQq0i1p29ylWiM= -github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= -github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.78.2 h1:SyoVBXD/r0PntR1rprb90ClI32FSUNOCWqqTatnipHM= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.78.2/go.mod h1:SvsRXw4m1F2vk7HquU5h475bFpke27mIUswfyw9u3ug= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -184,13 +181,15 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -207,8 +206,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -219,41 +218,38 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -264,8 +260,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -274,8 +270,6 @@ gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= @@ -290,13 +284,13 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -307,34 +301,34 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= -k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= -k8s.io/apiextensions-apiserver v0.29.2 h1:UK3xB5lOWSnhaCk0RFZ0LUacPZz9RY4wi/yt2Iu+btg= -k8s.io/apiextensions-apiserver v0.29.2/go.mod h1:aLfYjpA5p3OwtqNXQFkhJ56TB+spV8Gc4wfMhUA3/b8= -k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= -k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= -k8s.io/cli-runtime v0.29.2 h1:smfsOcT4QujeghsNjECKN3lwyX9AwcFU0nvJ7sFN3ro= -k8s.io/cli-runtime v0.29.2/go.mod h1:KLisYYfoqeNfO+MkTWvpqIyb1wpJmmFJhioA0xd4MW8= -k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= -k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= -k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8= -k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM= -k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= -k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/kubectl v0.29.2 h1:uaDYaBhumvkwz0S2XHt36fK0v5IdNgL7HyUniwb2IUo= -k8s.io/kubectl v0.29.2/go.mod h1:BhizuYBGcKaHWyq+G7txGw2fXg576QbPrrnQdQDZgqI= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.17.3 h1:65QmN7r3FWgTxDMz9fvGnO1kbf2nu+acg9p2R9oYYYk= -sigs.k8s.io/controller-runtime v0.17.3/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY= +k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= +k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0= +k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM= +k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= +k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/cli-runtime v0.31.3 h1:fEQD9Xokir78y7pVK/fCJN090/iYNrLHpFbGU4ul9TI= +k8s.io/cli-runtime v0.31.3/go.mod h1:Q2jkyTpl+f6AtodQvgDI8io3jrfr+Z0LyQBPJJ2Btq8= +k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= +k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= +k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kubectl v0.31.3 h1:3r111pCjPsvnR98oLLxDMwAeM6OPGmPty6gSKaLTQes= +k8s.io/kubectl v0.31.3/go.mod h1:lhMECDCbJN8He12qcKqs2QfmVo9Pue30geovBVpH5fs= +k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3 h1:b2FmK8YH+QEwq/Sy2uAEhmqL5nPfGYbJOcaqjeYYZoA= +k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw= +sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= -sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= -sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= -sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= +sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= +sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= +sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ= +sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= From b34ca2cefc31a732f9941597568e21396fe9db0d Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Thu, 19 Dec 2024 22:09:13 +0000 Subject: [PATCH 143/414] Update cert-manager download link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cad25dec..e0e7cdb8 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer Install the certificate manager with the following command: ```sh - kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml + kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml ``` * ### Create Role Bindings for Access Management From 6b4451249ed47c2683646eaa4b66614f7bc77c1d Mon Sep 17 00:00:00 2001 From: ting-lan-wang Date: Mon, 6 Jan 2025 21:36:02 +0800 Subject: [PATCH 144/414] Add support for Autonomous Database Cloning --- Makefile | 7 +- .../v1alpha1/adbfamily_common_spec.go | 67 ++ .../v1alpha1/adbfamily_common_utils.go | 328 ------- .../autonomouscontainerdatabase_types.go | 14 +- .../autonomouscontainerdatabase_webhook.go | 5 +- .../v1alpha1/autonomousdatabase_conversion.go | 180 ++-- .../v1alpha1/autonomousdatabase_types.go | 336 ++----- .../v1alpha1/autonomousdatabase_webhook.go | 168 +--- .../autonomousdatabase_webhook_test.go | 150 +--- .../autonomousdatabasebackup_types.go | 15 +- .../autonomousdatabasebackup_webhook.go | 12 +- .../autonomousdatabasebackup_webhook_test.go | 18 +- .../autonomousdatabaserestore_types.go | 19 +- .../autonomousdatabaserestore_webhook.go | 11 +- .../autonomousdatabaserestore_webhook_test.go | 12 +- .../v1alpha1/zz_generated.deepcopy.go | 278 +++--- apis/database/v4/adbfamily_common_spec.go | 67 ++ ...ily_common_utils.go => adbfamily_utils.go} | 43 +- .../v4/autonomouscontainerdatabase_types.go | 4 +- .../v4/autonomouscontainerdatabase_webhook.go | 2 +- apis/database/v4/autonomousdatabase_types.go | 327 ++++--- .../database/v4/autonomousdatabase_webhook.go | 171 +--- .../v4/autonomousdatabasebackup_types.go | 4 +- .../v4/autonomousdatabasebackup_webhook.go | 12 +- .../v4/autonomousdatabaserestore_types.go | 6 +- .../v4/autonomousdatabaserestore_webhook.go | 10 +- apis/database/v4/zz_generated.deepcopy.go | 259 +++--- commons/adb_family/utils.go | 16 +- commons/k8s/create.go | 16 +- commons/k8s/fetch.go | 4 +- commons/oci/containerdatabase.go | 10 +- ...onomous_database_clone_request_response.go | 104 +++ commons/oci/database.go | 382 ++++---- commons/oci/provider.go | 8 +- ...tabase.oracle.com_autonomousdatabases.yaml | 387 ++++++-- config/webhook/manifests.yaml | 60 -- .../autonomouscontainerdatabase_controller.go | 4 +- .../database/autonomousdatabase_controller.go | 840 +++++++----------- .../autonomousdatabasebackup_controller.go | 34 +- .../autonomousdatabaserestore_controller.go | 32 +- controllers/database/dbcssystem_controller.go | 4 +- test/e2e/autonomouscontainerdatabase_test.go | 8 +- ...autonomousdatabase_controller_bind_test.go | 44 +- ...tonomousdatabase_controller_create_test.go | 109 +-- test/e2e/behavior/shared_behaviors.go | 133 ++- test/e2e/suite_test.go | 6 +- 46 files changed, 2058 insertions(+), 2668 deletions(-) create mode 100644 apis/database/v1alpha1/adbfamily_common_spec.go delete mode 100644 apis/database/v1alpha1/adbfamily_common_utils.go create mode 100644 apis/database/v4/adbfamily_common_spec.go rename apis/database/v4/{adbfamily_common_utils.go => adbfamily_utils.go} (91%) create mode 100644 commons/oci/create_autonomous_database_clone_request_response.go diff --git a/Makefile b/Makefile index 684f3259..e762a618 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,10 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) # Image URL to use all building/pushing image targets IMG ?= controller:latest -# Produce CRDs that work back to Kubernetes 1.11 (no version conversion) -# API version has to be v1 to use defaulting (https://github.com/kubernetes-sigs/controller-tools/issues/478) -CRD_OPTIONS ?= "crd:maxDescLen=0" +# Enable allowDangerousTypes to use float type in CRD +# Remove the Desc to avoid YAML getting too long. See the discussion: +# https://github.com/kubernetes-sigs/kubebuilder/issues/1140 +CRD_OPTIONS ?= "crd:maxDescLen=0,allowDangerousTypes=true" # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.29.0 # Operator YAML file diff --git a/apis/database/v1alpha1/adbfamily_common_spec.go b/apis/database/v1alpha1/adbfamily_common_spec.go new file mode 100644 index 00000000..74eb9f94 --- /dev/null +++ b/apis/database/v1alpha1/adbfamily_common_spec.go @@ -0,0 +1,67 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1alpha1 + +// LastSuccessfulSpec is an annotation key which maps to the value of last successful spec +const LastSuccessfulSpec string = "lastSuccessfulSpec" + +/************************ +* OCI config +************************/ +type OciConfigSpec struct { + ConfigMapName *string `json:"configMapName,omitempty"` + SecretName *string `json:"secretName,omitempty"` +} + +/************************ +* ADB spec +************************/ +type K8sAdbSpec struct { + Name *string `json:"name,omitempty"` +} + +type OciAdbSpec struct { + Ocid *string `json:"ocid,omitempty"` +} + +// TargetSpec defines the spec of the target for backup/restore runs. +type TargetSpec struct { + K8sAdb K8sAdbSpec `json:"k8sADB,omitempty"` + OciAdb OciAdbSpec `json:"ociADB,omitempty"` +} diff --git a/apis/database/v1alpha1/adbfamily_common_utils.go b/apis/database/v1alpha1/adbfamily_common_utils.go deleted file mode 100644 index d4d3ae9f..00000000 --- a/apis/database/v1alpha1/adbfamily_common_utils.go +++ /dev/null @@ -1,328 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v1alpha1 - -import ( - "errors" - "reflect" - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/oracle/oci-go-sdk/v65/common" - "github.com/oracle/oci-go-sdk/v65/database" - "github.com/oracle/oci-go-sdk/v65/workrequests" -) - -// LastSuccessfulSpec is an annotation key which maps to the value of last successful spec -const LastSuccessfulSpec string = "lastSuccessfulSpec" - -// File the meta condition and return the meta view -func CreateMetaCondition(obj client.Object, err error, lifecycleState string, stateMsg string) metav1.Condition { - - return metav1.Condition{ - Type: lifecycleState, - LastTransitionTime: metav1.Now(), - ObservedGeneration: obj.GetGeneration(), - Reason: stateMsg, - Message: err.Error(), - Status: metav1.ConditionTrue, - } -} - -/************************ -* OCI config -************************/ -type OCIConfigSpec struct { - ConfigMapName *string `json:"configMapName,omitempty"` - SecretName *string `json:"secretName,omitempty"` -} - -/************************ -* ADB spec -************************/ -type K8sADBSpec struct { - Name *string `json:"name,omitempty"` -} - -type OCIADBSpec struct { - OCID *string `json:"ocid,omitempty"` -} - -// TargetSpec defines the spec of the target for backup/restore runs. -type TargetSpec struct { - K8sADB K8sADBSpec `json:"k8sADB,omitempty"` - OCIADB OCIADBSpec `json:"ociADB,omitempty"` -} - -/************************** -* Remove Unchanged Fields -**************************/ - -// removeUnchangedFields removes the unchanged fields in the struct and returns if the struct is changed. -// lastSpec should be a derefereced struct that is the last successful spec, e.g. AutonomousDatabaseSpec. -// curSpec should be a pointer pointing to the struct that is being proccessed, e.g., *AutonomousDatabaseSpec. -func removeUnchangedFields(lastSpec interface{}, curSpec interface{}) (bool, error) { - if reflect.ValueOf(lastSpec).Kind() != reflect.Struct { - return false, errors.New("lastSpec should be a struct") - } - - if reflect.ValueOf(curSpec).Kind() != reflect.Ptr || reflect.ValueOf(curSpec).Elem().Kind() != reflect.Struct { - return false, errors.New("curSpec should be a struct pointer") - } - - if reflect.ValueOf(lastSpec).Type() != reflect.ValueOf(curSpec).Elem().Type() { - return false, errors.New("the referenced type of curSpec should be the same as the type of lastSpec") - } - - return traverse(lastSpec, curSpec), nil -} - -// Traverse and compare each fields in the lastSpec and the the curSpec. -// If unchanged, set the field in curSpec to a zero value. -// lastSpec should be a derefereced struct that is the last successful spec, e.g. AutonomousDatabaseSpec. -// curSpec should be a pointer pointing to the struct that is being proccessed, e.g., *AutonomousDatabaseSpec. -func traverse(lastSpec interface{}, curSpec interface{}) bool { - var changed bool = false - - fields := reflect.VisibleFields(reflect.TypeOf(lastSpec)) - - lastSpecValue := reflect.ValueOf(lastSpec) - curSpecValue := reflect.ValueOf(curSpec).Elem() // deref the struct - - for _, field := range fields { - lastField := lastSpecValue.FieldByName(field.Name) - curField := curSpecValue.FieldByName(field.Name) - - // call traverse() if the current field is a struct - if field.Type.Kind() == reflect.Struct { - childrenChanged := traverse(lastField.Interface(), curField.Addr().Interface()) - if childrenChanged && !changed { - changed = true - } - } else { - fieldChanged := hasChanged(lastField, curField) - - // if fieldChanged { - // if curField.Kind() == reflect.Ptr { - // fmt.Printf("== field %s changed\n", field.Name) - // if lastField.IsZero() { - // fmt.Printf("=== lastField is nil\n") - // } else { - // fmt.Printf("=== lastField = %v\n", lastField.Elem().Interface()) - // } - // if curField.IsZero() { - // fmt.Printf("===== curField is nil\n") - // } else { - // fmt.Printf("===== curField = %v\n", curField.Elem().Interface()) - // } - // } else { - // fmt.Printf("=== lastField = %v\n", lastField.Interface()) - // fmt.Printf("===== curField = %v\n", curField.Interface()) - // } - // } - - if fieldChanged && !changed { - changed = true - } - - // Set the field to zero value if unchanged - if !fieldChanged { - curField.Set(reflect.Zero(curField.Type())) - } - } - } - - return changed -} - -// 1. If the current field is with a zero value, then the field is unchanged. -// 2. If the current field is NOT with a zero value, then we want to comapre it with the last field. -// In this case if the last field is with a zero value, then the field is changed -func hasChanged(lastField reflect.Value, curField reflect.Value) bool { - zero := reflect.Zero(lastField.Type()).Interface() - lastFieldIsZero := reflect.DeepEqual(lastField.Interface(), zero) - curFieldIsZero := reflect.DeepEqual(curField.Interface(), zero) - - if curFieldIsZero { - return false - } else if !lastFieldIsZero { - var lastIntrf interface{} - var curIntrf interface{} - - if curField.Kind() == reflect.Ptr { - lastIntrf = lastField.Elem().Interface() - curIntrf = curField.Elem().Interface() - } else { - lastIntrf = lastField.Interface() - curIntrf = curField.Interface() - } - - return !reflect.DeepEqual(lastIntrf, curIntrf) - } - - return true -} - -/************************ -* SDKTime format -************************/ - -// Follow the format of the display time -const displayFormat = "2006-01-02 15:04:05 MST" - -func FormatSDKTime(sdkTime *common.SDKTime) string { - if sdkTime == nil { - return "" - } - - time := sdkTime.Time - return time.Format(displayFormat) -} - -func parseDisplayTime(val string) (*common.SDKTime, error) { - parsedTime, err := time.Parse(displayFormat, val) - if err != nil { - return nil, err - } - sdkTime := common.SDKTime{Time: parsedTime} - return &sdkTime, nil -} - -/************************ -* LifecycleState check -************************/ -func IsADBIntermediateState(state database.AutonomousDatabaseLifecycleStateEnum) bool { - if state == database.AutonomousDatabaseLifecycleStateProvisioning || - state == database.AutonomousDatabaseLifecycleStateUpdating || - state == database.AutonomousDatabaseLifecycleStateScaleInProgress || - state == database.AutonomousDatabaseLifecycleStateStarting || - state == database.AutonomousDatabaseLifecycleStateStopping || - state == database.AutonomousDatabaseLifecycleStateTerminating || - state == database.AutonomousDatabaseLifecycleStateRestoreInProgress || - state == database.AutonomousDatabaseLifecycleStateBackupInProgress || - state == database.AutonomousDatabaseLifecycleStateMaintenanceInProgress || - state == database.AutonomousDatabaseLifecycleStateRestarting || - state == database.AutonomousDatabaseLifecycleStateRecreating || - state == database.AutonomousDatabaseLifecycleStateRoleChangeInProgress || - state == database.AutonomousDatabaseLifecycleStateUpgrading { - return true - } - return false -} - -func ValidADBTerminateState(state database.AutonomousDatabaseLifecycleStateEnum) bool { - if state == database.AutonomousDatabaseLifecycleStateProvisioning || - state == database.AutonomousDatabaseLifecycleStateAvailable || - state == database.AutonomousDatabaseLifecycleStateStopped || - state == database.AutonomousDatabaseLifecycleStateUnavailable || - state == database.AutonomousDatabaseLifecycleStateRestoreInProgress || - state == database.AutonomousDatabaseLifecycleStateRestoreFailed || - state == database.AutonomousDatabaseLifecycleStateBackupInProgress || - state == database.AutonomousDatabaseLifecycleStateScaleInProgress || - state == database.AutonomousDatabaseLifecycleStateAvailableNeedsAttention || - state == database.AutonomousDatabaseLifecycleStateUpdating || - state == database.AutonomousDatabaseLifecycleStateMaintenanceInProgress || - state == database.AutonomousDatabaseLifecycleStateRoleChangeInProgress || - state == database.AutonomousDatabaseLifecycleStateUpgrading { - return true - } - return false -} - -// NextADBStableState returns the next stable state if it's an intermediate state. -// Otherwise returns the same state. -func NextADBStableState(state database.AutonomousDatabaseLifecycleStateEnum) database.AutonomousDatabaseLifecycleStateEnum { - if state == database.AutonomousDatabaseLifecycleStateProvisioning || - state == database.AutonomousDatabaseLifecycleStateStarting || - state == database.AutonomousDatabaseLifecycleStateRestoreInProgress || - state == database.AutonomousDatabaseLifecycleStateBackupInProgress || - state == database.AutonomousDatabaseLifecycleStateScaleInProgress || - state == database.AutonomousDatabaseLifecycleStateUpdating || - state == database.AutonomousDatabaseLifecycleStateMaintenanceInProgress || - state == database.AutonomousDatabaseLifecycleStateRestarting || - state == database.AutonomousDatabaseLifecycleStateRecreating || - state == database.AutonomousDatabaseLifecycleStateRoleChangeInProgress || - state == database.AutonomousDatabaseLifecycleStateUpgrading { - - return database.AutonomousDatabaseLifecycleStateAvailable - } - - if state == database.AutonomousDatabaseLifecycleStateStopping { - return database.AutonomousDatabaseLifecycleStateStopped - } - - if state == database.AutonomousDatabaseLifecycleStateTerminating { - return database.AutonomousDatabaseLifecycleStateTerminated - } - - return state -} - -func IsBackupIntermediateState(state database.AutonomousDatabaseBackupLifecycleStateEnum) bool { - if state == database.AutonomousDatabaseBackupLifecycleStateCreating || - state == database.AutonomousDatabaseBackupLifecycleStateDeleting { - return true - } - return false -} - -func IsRestoreIntermediateState(state workrequests.WorkRequestStatusEnum) bool { - if state == workrequests.WorkRequestStatusAccepted || - state == workrequests.WorkRequestStatusInProgress || - state == workrequests.WorkRequestStatusCanceling { - return true - } - return false -} - -func IsACDIntermediateState(state database.AutonomousContainerDatabaseLifecycleStateEnum) bool { - if state == database.AutonomousContainerDatabaseLifecycleStateProvisioning || - state == database.AutonomousContainerDatabaseLifecycleStateUpdating || - state == database.AutonomousContainerDatabaseLifecycleStateTerminating || - state == database.AutonomousContainerDatabaseLifecycleStateBackupInProgress || - state == database.AutonomousContainerDatabaseLifecycleStateRestoring || - state == database.AutonomousContainerDatabaseLifecycleStateRestarting || - state == database.AutonomousContainerDatabaseLifecycleStateMaintenanceInProgress || - state == database.AutonomousContainerDatabaseLifecycleStateRoleChangeInProgress { - return true - } - return false -} diff --git a/apis/database/v1alpha1/autonomouscontainerdatabase_types.go b/apis/database/v1alpha1/autonomouscontainerdatabase_types.go index a1350dda..fd71b210 100644 --- a/apis/database/v1alpha1/autonomouscontainerdatabase_types.go +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_types.go @@ -42,6 +42,8 @@ import ( "encoding/json" "reflect" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" + "github.com/oracle/oci-go-sdk/v65/database" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -85,7 +87,7 @@ type AutonomousContainerDatabaseSpec struct { Action AcdActionEnum `json:"action,omitempty"` FreeformTags map[string]string `json:"freeformTags,omitempty"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + OCIConfig OciConfigSpec `json:"ociConfig,omitempty"` // +kubebuilder:default:=false HardLink *bool `json:"hardLink,omitempty"` } @@ -168,13 +170,13 @@ func (acd *AutonomousContainerDatabase) UpdateLastSuccessfulSpec() error { } // UpdateStatusFromOCIACD updates the status subresource -func (acd *AutonomousContainerDatabase) UpdateStatusFromOCIACD(ociObj database.AutonomousContainerDatabase) { +func (acd *AutonomousContainerDatabase) UpdateStatusFromOciAcd(ociObj database.AutonomousContainerDatabase) { acd.Status.LifecycleState = ociObj.LifecycleState - acd.Status.TimeCreated = FormatSDKTime(ociObj.TimeCreated) + acd.Status.TimeCreated = dbv4.FormatSDKTime(ociObj.TimeCreated) } // UpdateFromOCIADB updates the attributes using database.AutonomousContainerDatabase object -func (acd *AutonomousContainerDatabase) UpdateFromOCIACD(ociObj database.AutonomousContainerDatabase) (specChanged bool) { +func (acd *AutonomousContainerDatabase) UpdateFromOciAcd(ociObj database.AutonomousContainerDatabase) (specChanged bool) { oldACD := acd.DeepCopy() /*********************************** @@ -197,14 +199,14 @@ func (acd *AutonomousContainerDatabase) UpdateFromOCIACD(ociObj database.Autonom /*********************************** * update the status subresource ***********************************/ - acd.UpdateStatusFromOCIACD(ociObj) + acd.UpdateStatusFromOciAcd(ociObj) return !reflect.DeepEqual(oldACD.Spec, acd.Spec) } // RemoveUnchangedSpec removes the unchanged fields in spec, and returns if the spec has been changed. func (acd *AutonomousContainerDatabase) RemoveUnchangedSpec(prevSpec AutonomousContainerDatabaseSpec) (bool, error) { - changed, err := removeUnchangedFields(prevSpec, &acd.Spec) + changed, err := dbv4.RemoveUnchangedFields(prevSpec, &acd.Spec) if err != nil { return changed, err } diff --git a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go index baabda54..10a16cd1 100644 --- a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -82,12 +83,12 @@ func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) (admiss // cannot update when the old state is in intermediate state, except for the terminate operatrion var copiedSpec *AutonomousContainerDatabaseSpec = r.Spec.DeepCopy() - changed, err := removeUnchangedFields(oldACD.Spec, copiedSpec) + changed, err := dbv4.RemoveUnchangedFields(oldACD.Spec, copiedSpec) if err != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), err.Error())) } - if IsACDIntermediateState(oldACD.Status.LifecycleState) && changed { + if dbv4.IsACDIntermediateState(oldACD.Status.LifecycleState) && changed { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "cannot change the spec when the lifecycleState is in an intermdeiate state")) diff --git a/apis/database/v1alpha1/autonomousdatabase_conversion.go b/apis/database/v1alpha1/autonomousdatabase_conversion.go index 9afa8113..02fd2fc8 100644 --- a/apis/database/v1alpha1/autonomousdatabase_conversion.go +++ b/apis/database/v1alpha1/autonomousdatabase_conversion.go @@ -11,42 +11,71 @@ import ( func (src *AutonomousDatabase) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*v4.AutonomousDatabase) // Convert the Spec - dst.Spec.Details.AutonomousDatabaseOCID = src.Spec.Details.AutonomousDatabaseOCID - dst.Spec.Details.CompartmentOCID = src.Spec.Details.CompartmentOCID - dst.Spec.Details.AutonomousContainerDatabase.K8sACD.Name = src.Spec.Details.AutonomousContainerDatabase.K8sACD.Name - dst.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID = src.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID + dst.Spec.Action = src.Spec.Action + + // Details + dst.Spec.Details.Id = src.Spec.Details.Id + dst.Spec.Details.CompartmentId = src.Spec.Details.CompartmentId + dst.Spec.Details.AutonomousContainerDatabase.K8sAcd.Name = src.Spec.Details.AutonomousContainerDatabase.K8sAcd.Name + dst.Spec.Details.AutonomousContainerDatabase.OciAcd.Id = src.Spec.Details.AutonomousContainerDatabase.OciAcd.Id dst.Spec.Details.DisplayName = src.Spec.Details.DisplayName dst.Spec.Details.DbName = src.Spec.Details.DbName dst.Spec.Details.DbWorkload = src.Spec.Details.DbWorkload dst.Spec.Details.LicenseModel = src.Spec.Details.LicenseModel dst.Spec.Details.DbVersion = src.Spec.Details.DbVersion dst.Spec.Details.DataStorageSizeInTBs = src.Spec.Details.DataStorageSizeInTBs - dst.Spec.Details.CPUCoreCount = src.Spec.Details.CPUCoreCount + dst.Spec.Details.CpuCoreCount = src.Spec.Details.CpuCoreCount + dst.Spec.Details.ComputeModel = src.Spec.Details.ComputeModel + dst.Spec.Details.ComputeCount = src.Spec.Details.ComputeCount + dst.Spec.Details.OcpuCount = src.Spec.Details.OcpuCount dst.Spec.Details.AdminPassword.K8sSecret.Name = src.Spec.Details.AdminPassword.K8sSecret.Name - dst.Spec.Details.AdminPassword.OCISecret.OCID = src.Spec.Details.AdminPassword.OCISecret.OCID + dst.Spec.Details.AdminPassword.OciSecret.Id = src.Spec.Details.AdminPassword.OciSecret.Id dst.Spec.Details.IsAutoScalingEnabled = src.Spec.Details.IsAutoScalingEnabled dst.Spec.Details.IsDedicated = src.Spec.Details.IsDedicated - dst.Spec.Details.LifecycleState = src.Spec.Details.LifecycleState - - if val, ok := v4.GetNetworkAccessTypeFromString(string(src.Spec.Details.NetworkAccess.AccessType)); !ok { - return errors.New("Unable to convert to NetworkAccessTypeEnum: " + string(src.Spec.Details.NetworkAccess.AccessType)) - } else { - dst.Spec.Details.NetworkAccess.AccessType = val - } - dst.Spec.Details.NetworkAccess.IsAccessControlEnabled = src.Spec.Details.NetworkAccess.IsAccessControlEnabled - dst.Spec.Details.NetworkAccess.AccessControlList = src.Spec.Details.NetworkAccess.AccessControlList - dst.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = src.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID - dst.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = src.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs - dst.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = src.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix - dst.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = src.Spec.Details.NetworkAccess.IsMTLSConnectionRequired - + dst.Spec.Details.IsFreeTier = src.Spec.Details.IsFreeTier + dst.Spec.Details.IsAccessControlEnabled = src.Spec.Details.IsAccessControlEnabled + dst.Spec.Details.WhitelistedIps = src.Spec.Details.WhitelistedIps + dst.Spec.Details.SubnetId = src.Spec.Details.SubnetId + dst.Spec.Details.NsgIds = src.Spec.Details.NsgIds + dst.Spec.Details.PrivateEndpointLabel = src.Spec.Details.PrivateEndpointLabel + dst.Spec.Details.IsMtlsConnectionRequired = src.Spec.Details.IsMtlsConnectionRequired dst.Spec.Details.FreeformTags = src.Spec.Details.FreeformTags - dst.Spec.Details.Wallet.Name = src.Spec.Details.Wallet.Name - dst.Spec.Details.Wallet.Password.K8sSecret.Name = src.Spec.Details.Wallet.Password.K8sSecret.Name - dst.Spec.Details.Wallet.Password.OCISecret.OCID = src.Spec.Details.Wallet.Password.OCISecret.OCID - dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName - dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + // Clone + dst.Spec.Clone.CompartmentId = src.Spec.Clone.CompartmentId + dst.Spec.Clone.AutonomousContainerDatabase.K8sAcd.Name = src.Spec.Clone.AutonomousContainerDatabase.K8sAcd.Name + dst.Spec.Clone.AutonomousContainerDatabase.OciAcd.Id = src.Spec.Clone.AutonomousContainerDatabase.OciAcd.Id + dst.Spec.Clone.DisplayName = src.Spec.Clone.DisplayName + dst.Spec.Clone.DbName = src.Spec.Clone.DbName + dst.Spec.Clone.DbWorkload = src.Spec.Clone.DbWorkload + dst.Spec.Clone.LicenseModel = src.Spec.Clone.LicenseModel + dst.Spec.Clone.DbVersion = src.Spec.Clone.DbVersion + dst.Spec.Clone.DataStorageSizeInTBs = src.Spec.Clone.DataStorageSizeInTBs + dst.Spec.Clone.CpuCoreCount = src.Spec.Clone.CpuCoreCount + dst.Spec.Clone.ComputeModel = src.Spec.Clone.ComputeModel + dst.Spec.Clone.ComputeCount = src.Spec.Clone.ComputeCount + dst.Spec.Clone.OcpuCount = src.Spec.Clone.OcpuCount + dst.Spec.Clone.AdminPassword.K8sSecret.Name = src.Spec.Clone.AdminPassword.K8sSecret.Name + dst.Spec.Clone.AdminPassword.OciSecret.Id = src.Spec.Clone.AdminPassword.OciSecret.Id + dst.Spec.Clone.IsAutoScalingEnabled = src.Spec.Clone.IsAutoScalingEnabled + dst.Spec.Clone.IsDedicated = src.Spec.Clone.IsDedicated + dst.Spec.Clone.IsFreeTier = src.Spec.Clone.IsFreeTier + dst.Spec.Clone.IsAccessControlEnabled = src.Spec.Clone.IsAccessControlEnabled + dst.Spec.Clone.WhitelistedIps = src.Spec.Clone.WhitelistedIps + dst.Spec.Clone.SubnetId = src.Spec.Clone.SubnetId + dst.Spec.Clone.NsgIds = src.Spec.Clone.NsgIds + dst.Spec.Clone.PrivateEndpointLabel = src.Spec.Clone.PrivateEndpointLabel + dst.Spec.Clone.IsMtlsConnectionRequired = src.Spec.Clone.IsMtlsConnectionRequired + dst.Spec.Clone.FreeformTags = src.Spec.Clone.FreeformTags + dst.Spec.Clone.CloneType = src.Spec.Clone.CloneType + + // Wallet + dst.Spec.Wallet.Name = src.Spec.Wallet.Name + dst.Spec.Wallet.Password.K8sSecret.Name = src.Spec.Wallet.Password.K8sSecret.Name + dst.Spec.Wallet.Password.OciSecret.Id = src.Spec.Wallet.Password.OciSecret.Id + + dst.Spec.OciConfig.ConfigMapName = src.Spec.OciConfig.ConfigMapName + dst.Spec.OciConfig.SecretName = src.Spec.OciConfig.SecretName dst.Spec.HardLink = src.Spec.HardLink @@ -54,6 +83,7 @@ func (src *AutonomousDatabase) ConvertTo(dstRaw conversion.Hub) error { dst.Status.LifecycleState = src.Status.LifecycleState dst.Status.TimeCreated = src.Status.TimeCreated dst.Status.WalletExpiringDate = src.Status.WalletExpiringDate + dst.Status.Action = src.Status.Action // convert status.allConnectionStrings if src.Status.AllConnectionStrings != nil { @@ -89,42 +119,71 @@ func (dst *AutonomousDatabase) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*v4.AutonomousDatabase) // Convert the Spec - dst.Spec.Details.AutonomousDatabaseOCID = src.Spec.Details.AutonomousDatabaseOCID - dst.Spec.Details.CompartmentOCID = src.Spec.Details.CompartmentOCID - dst.Spec.Details.AutonomousContainerDatabase.K8sACD.Name = src.Spec.Details.AutonomousContainerDatabase.K8sACD.Name - dst.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID = src.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID + dst.Spec.Action = src.Spec.Action + + // Details + dst.Spec.Details.Id = src.Spec.Details.Id + dst.Spec.Details.CompartmentId = src.Spec.Details.CompartmentId + dst.Spec.Details.AutonomousContainerDatabase.K8sAcd.Name = src.Spec.Details.AutonomousContainerDatabase.K8sAcd.Name + dst.Spec.Details.AutonomousContainerDatabase.OciAcd.Id = src.Spec.Details.AutonomousContainerDatabase.OciAcd.Id dst.Spec.Details.DisplayName = src.Spec.Details.DisplayName dst.Spec.Details.DbName = src.Spec.Details.DbName dst.Spec.Details.DbWorkload = src.Spec.Details.DbWorkload dst.Spec.Details.LicenseModel = src.Spec.Details.LicenseModel dst.Spec.Details.DbVersion = src.Spec.Details.DbVersion dst.Spec.Details.DataStorageSizeInTBs = src.Spec.Details.DataStorageSizeInTBs - dst.Spec.Details.CPUCoreCount = src.Spec.Details.CPUCoreCount + dst.Spec.Details.CpuCoreCount = src.Spec.Details.CpuCoreCount + dst.Spec.Details.ComputeModel = src.Spec.Details.ComputeModel + dst.Spec.Details.ComputeCount = src.Spec.Details.ComputeCount + dst.Spec.Details.OcpuCount = src.Spec.Details.OcpuCount dst.Spec.Details.AdminPassword.K8sSecret.Name = src.Spec.Details.AdminPassword.K8sSecret.Name - dst.Spec.Details.AdminPassword.OCISecret.OCID = src.Spec.Details.AdminPassword.OCISecret.OCID + dst.Spec.Details.AdminPassword.OciSecret.Id = src.Spec.Details.AdminPassword.OciSecret.Id dst.Spec.Details.IsAutoScalingEnabled = src.Spec.Details.IsAutoScalingEnabled dst.Spec.Details.IsDedicated = src.Spec.Details.IsDedicated - dst.Spec.Details.LifecycleState = src.Spec.Details.LifecycleState - - if val, ok := GetNetworkAccessTypeFromString(string(src.Spec.Details.NetworkAccess.AccessType)); !ok { - return errors.New("Unable to convert to NetworkAccessTypeEnum: " + string(src.Spec.Details.NetworkAccess.AccessType)) - } else { - dst.Spec.Details.NetworkAccess.AccessType = val - } - dst.Spec.Details.NetworkAccess.IsAccessControlEnabled = src.Spec.Details.NetworkAccess.IsAccessControlEnabled - dst.Spec.Details.NetworkAccess.AccessControlList = src.Spec.Details.NetworkAccess.AccessControlList - dst.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = src.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID - dst.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = src.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs - dst.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = src.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix - dst.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = src.Spec.Details.NetworkAccess.IsMTLSConnectionRequired - + dst.Spec.Details.IsFreeTier = src.Spec.Details.IsFreeTier + dst.Spec.Details.IsAccessControlEnabled = src.Spec.Details.IsAccessControlEnabled + dst.Spec.Details.WhitelistedIps = src.Spec.Details.WhitelistedIps + dst.Spec.Details.SubnetId = src.Spec.Details.SubnetId + dst.Spec.Details.NsgIds = src.Spec.Details.NsgIds + dst.Spec.Details.PrivateEndpointLabel = src.Spec.Details.PrivateEndpointLabel + dst.Spec.Details.IsMtlsConnectionRequired = src.Spec.Details.IsMtlsConnectionRequired dst.Spec.Details.FreeformTags = src.Spec.Details.FreeformTags - dst.Spec.Details.Wallet.Name = src.Spec.Details.Wallet.Name - dst.Spec.Details.Wallet.Password.K8sSecret.Name = src.Spec.Details.Wallet.Password.K8sSecret.Name - dst.Spec.Details.Wallet.Password.OCISecret.OCID = src.Spec.Details.Wallet.Password.OCISecret.OCID - dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName - dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName + // Clone + dst.Spec.Clone.CompartmentId = src.Spec.Clone.CompartmentId + dst.Spec.Clone.AutonomousContainerDatabase.K8sAcd.Name = src.Spec.Clone.AutonomousContainerDatabase.K8sAcd.Name + dst.Spec.Clone.AutonomousContainerDatabase.OciAcd.Id = src.Spec.Clone.AutonomousContainerDatabase.OciAcd.Id + dst.Spec.Clone.DisplayName = src.Spec.Clone.DisplayName + dst.Spec.Clone.DbName = src.Spec.Clone.DbName + dst.Spec.Clone.DbWorkload = src.Spec.Clone.DbWorkload + dst.Spec.Clone.LicenseModel = src.Spec.Clone.LicenseModel + dst.Spec.Clone.DbVersion = src.Spec.Clone.DbVersion + dst.Spec.Clone.DataStorageSizeInTBs = src.Spec.Clone.DataStorageSizeInTBs + dst.Spec.Clone.CpuCoreCount = src.Spec.Clone.CpuCoreCount + dst.Spec.Clone.ComputeModel = src.Spec.Clone.ComputeModel + dst.Spec.Clone.ComputeCount = src.Spec.Clone.ComputeCount + dst.Spec.Clone.OcpuCount = src.Spec.Clone.OcpuCount + dst.Spec.Clone.AdminPassword.K8sSecret.Name = src.Spec.Clone.AdminPassword.K8sSecret.Name + dst.Spec.Clone.AdminPassword.OciSecret.Id = src.Spec.Clone.AdminPassword.OciSecret.Id + dst.Spec.Clone.IsAutoScalingEnabled = src.Spec.Clone.IsAutoScalingEnabled + dst.Spec.Clone.IsDedicated = src.Spec.Clone.IsDedicated + dst.Spec.Clone.IsFreeTier = src.Spec.Clone.IsFreeTier + dst.Spec.Clone.IsAccessControlEnabled = src.Spec.Clone.IsAccessControlEnabled + dst.Spec.Clone.WhitelistedIps = src.Spec.Clone.WhitelistedIps + dst.Spec.Clone.SubnetId = src.Spec.Clone.SubnetId + dst.Spec.Clone.NsgIds = src.Spec.Clone.NsgIds + dst.Spec.Clone.PrivateEndpointLabel = src.Spec.Clone.PrivateEndpointLabel + dst.Spec.Clone.IsMtlsConnectionRequired = src.Spec.Clone.IsMtlsConnectionRequired + dst.Spec.Clone.FreeformTags = src.Spec.Clone.FreeformTags + dst.Spec.Clone.CloneType = src.Spec.Clone.CloneType + + // Wallet + dst.Spec.Wallet.Name = src.Spec.Wallet.Name + dst.Spec.Wallet.Password.K8sSecret.Name = src.Spec.Wallet.Password.K8sSecret.Name + dst.Spec.Wallet.Password.OciSecret.Id = src.Spec.Wallet.Password.OciSecret.Id + + dst.Spec.OciConfig.ConfigMapName = src.Spec.OciConfig.ConfigMapName + dst.Spec.OciConfig.SecretName = src.Spec.OciConfig.SecretName dst.Spec.HardLink = src.Spec.HardLink @@ -132,6 +191,7 @@ func (dst *AutonomousDatabase) ConvertFrom(srcRaw conversion.Hub) error { dst.Status.LifecycleState = src.Status.LifecycleState dst.Status.TimeCreated = src.Status.TimeCreated dst.Status.WalletExpiringDate = src.Status.WalletExpiringDate + dst.Status.Action = src.Status.Action // convert status.allConnectionStrings if src.Status.AllConnectionStrings != nil { @@ -165,8 +225,8 @@ func (dst *AutonomousDatabase) ConvertFrom(srcRaw conversion.Hub) error { func (src *AutonomousDatabaseBackup) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*v4.AutonomousDatabaseBackup) - dst.Spec.Target.K8sADB.Name = src.Spec.Target.K8sADB.Name - dst.Spec.Target.OCIADB.OCID = src.Spec.Target.OCIADB.OCID + dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name + dst.Spec.Target.OciAdb.OCID = src.Spec.Target.OciAdb.Ocid dst.Spec.DisplayName = src.Spec.DisplayName dst.Spec.AutonomousDatabaseBackupOCID = src.Spec.AutonomousDatabaseBackupOCID dst.Spec.IsLongTermBackup = src.Spec.IsLongTermBackup @@ -191,8 +251,8 @@ func (src *AutonomousDatabaseBackup) ConvertTo(dstRaw conversion.Hub) error { func (dst *AutonomousDatabaseBackup) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*v4.AutonomousDatabaseBackup) - dst.Spec.Target.K8sADB.Name = src.Spec.Target.K8sADB.Name - dst.Spec.Target.OCIADB.OCID = src.Spec.Target.OCIADB.OCID + dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name + dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.OCID dst.Spec.DisplayName = src.Spec.DisplayName dst.Spec.AutonomousDatabaseBackupOCID = src.Spec.AutonomousDatabaseBackupOCID dst.Spec.IsLongTermBackup = src.Spec.IsLongTermBackup @@ -217,9 +277,9 @@ func (dst *AutonomousDatabaseBackup) ConvertFrom(srcRaw conversion.Hub) error { func (src *AutonomousDatabaseRestore) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*v4.AutonomousDatabaseRestore) - dst.Spec.Target.K8sADB.Name = src.Spec.Target.K8sADB.Name - dst.Spec.Target.OCIADB.OCID = src.Spec.Target.OCIADB.OCID - dst.Spec.Source.K8sADBBackup.Name = src.Spec.Source.K8sADBBackup.Name + dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name + dst.Spec.Target.OciAdb.OCID = src.Spec.Target.OciAdb.Ocid + dst.Spec.Source.K8sAdbBackup.Name = src.Spec.Source.K8sAdbBackup.Name dst.Spec.Source.PointInTime.Timestamp = src.Spec.Source.PointInTime.Timestamp dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName @@ -239,9 +299,9 @@ func (src *AutonomousDatabaseRestore) ConvertTo(dstRaw conversion.Hub) error { func (dst *AutonomousDatabaseRestore) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*v4.AutonomousDatabaseRestore) - dst.Spec.Target.K8sADB.Name = src.Spec.Target.K8sADB.Name - dst.Spec.Target.OCIADB.OCID = src.Spec.Target.OCIADB.OCID - dst.Spec.Source.K8sADBBackup.Name = src.Spec.Source.K8sADBBackup.Name + dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name + dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.OCID + dst.Spec.Source.K8sAdbBackup.Name = src.Spec.Source.K8sAdbBackup.Name dst.Spec.Source.PointInTime.Timestamp = src.Spec.Source.PointInTime.Timestamp dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName dst.Spec.OCIConfig.SecretName = src.Spec.OCIConfig.SecretName diff --git a/apis/database/v1alpha1/autonomousdatabase_types.go b/apis/database/v1alpha1/autonomousdatabase_types.go index 34abe59e..b0f7bc69 100644 --- a/apis/database/v1alpha1/autonomousdatabase_types.go +++ b/apis/database/v1alpha1/autonomousdatabase_types.go @@ -39,9 +39,6 @@ package v1alpha1 import ( - "encoding/json" - "reflect" - "github.com/oracle/oci-go-sdk/v65/database" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -49,34 +46,79 @@ import ( // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. -// name of our custom finalizer -const ADB_FINALIZER = "database.oracle.com/adb-finalizer" - // AutonomousDatabaseSpec defines the desired state of AutonomousDatabase // Important: Run "make" to regenerate code after modifying this file type AutonomousDatabaseSpec struct { + // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone + Action string `json:"action"` Details AutonomousDatabaseDetails `json:"details"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + Clone AutonomousDatabaseClone `json:"clone"` + Wallet WalletSpec `json:"wallet,omitempty"` + OciConfig OciConfigSpec `json:"ociConfig,omitempty"` // +kubebuilder:default:=false HardLink *bool `json:"hardLink,omitempty"` } +type AutonomousDatabaseDetails struct { + AutonomousDatabaseBase `json:",inline"` + Id *string `json:"id,omitempty"` +} + +type AutonomousDatabaseClone struct { + AutonomousDatabaseBase `json:",inline"` + // +kubebuilder:validation:Enum:="FULL";"METADATA" + CloneType database.CreateAutonomousDatabaseCloneDetailsCloneTypeEnum `json:"cloneType,omitempty"` +} + +// AutonomousDatabaseBase defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase +type AutonomousDatabaseBase struct { + CompartmentId *string `json:"compartmentId,omitempty"` + AutonomousContainerDatabase AcdSpec `json:"autonomousContainerDatabase,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + DbName *string `json:"dbName,omitempty"` + // +kubebuilder:validation:Enum:="OLTP";"DW";"AJD";"APEX" + DbWorkload database.AutonomousDatabaseDbWorkloadEnum `json:"dbWorkload,omitempty"` + // +kubebuilder:validation:Enum:="LICENSE_INCLUDED";"BRING_YOUR_OWN_LICENSE" + LicenseModel database.AutonomousDatabaseLicenseModelEnum `json:"licenseModel,omitempty"` + DbVersion *string `json:"dbVersion,omitempty"` + DataStorageSizeInTBs *int `json:"dataStorageSizeInTBs,omitempty"` + CpuCoreCount *int `json:"cpuCoreCount,omitempty"` + // +kubebuilder:validation:Enum:="ECPU";"OCPU" + ComputeModel database.AutonomousDatabaseComputeModelEnum `json:"computeModel,omitempty"` + ComputeCount *float32 `json:"computeCount,omitempty"` + OcpuCount *float32 `json:"ocpuCount,omitempty"` + AdminPassword PasswordSpec `json:"adminPassword,omitempty"` + IsAutoScalingEnabled *bool `json:"isAutoScalingEnabled,omitempty"` + IsDedicated *bool `json:"isDedicated,omitempty"` + IsFreeTier *bool `json:"isFreeTier,omitempty"` + + // NetworkAccess + IsAccessControlEnabled *bool `json:"isAccessControlEnabled,omitempty"` + WhitelistedIps []string `json:"whitelistedIps,omitempty"` + SubnetId *string `json:"subnetId,omitempty"` + NsgIds []string `json:"nsgIds,omitempty"` + PrivateEndpointLabel *string `json:"privateEndpointLabel,omitempty"` + IsMtlsConnectionRequired *bool `json:"isMtlsConnectionRequired,omitempty"` + + FreeformTags map[string]string `json:"freeformTags,omitempty"` +} + /************************ * ACD specs ************************/ -type K8sACDSpec struct { +type K8sAcdSpec struct { Name *string `json:"name,omitempty"` } -type OCIACDSpec struct { - OCID *string `json:"ocid,omitempty"` +type OciAcdSpec struct { + Id *string `json:"id,omitempty"` } -// ACDSpec defines the spec of the target for backup/restore runs. +// AcdSpec defines the spec of the target for backup/restore runs. // The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup -type ACDSpec struct { - K8sACD K8sACDSpec `json:"k8sACD,omitempty"` - OCIACD OCIACDSpec `json:"ociACD,omitempty"` +type AcdSpec struct { + K8sAcd K8sAcdSpec `json:"k8sAcd,omitempty"` + OciAcd OciAcdSpec `json:"ociAcd,omitempty"` } /************************ @@ -86,13 +128,13 @@ type K8sSecretSpec struct { Name *string `json:"name,omitempty"` } -type OCISecretSpec struct { - OCID *string `json:"ocid,omitempty"` +type OciSecretSpec struct { + Id *string `json:"id,omitempty"` } type PasswordSpec struct { K8sSecret K8sSecretSpec `json:"k8sSecret,omitempty"` - OCISecret OCISecretSpec `json:"ociSecret,omitempty"` + OciSecret OciSecretSpec `json:"ociSecret,omitempty"` } type WalletSpec struct { @@ -100,80 +142,18 @@ type WalletSpec struct { Password PasswordSpec `json:"password,omitempty"` } -/************************ -* Network Access specs -************************/ - -type NetworkAccessTypeEnum string - -const ( - NetworkAccessTypePublic NetworkAccessTypeEnum = "PUBLIC" - NetworkAccessTypeRestricted NetworkAccessTypeEnum = "RESTRICTED" - NetworkAccessTypePrivate NetworkAccessTypeEnum = "PRIVATE" - NetworkAccessTypeEmpty NetworkAccessTypeEnum = "" -) - -func GetNetworkAccessTypeFromString(val string) (NetworkAccessTypeEnum, bool) { - var mappingNetworkAccessTypeEnum = map[string]NetworkAccessTypeEnum{ - "PUBLIC": NetworkAccessTypePublic, - "RESTRICTED": NetworkAccessTypeRestricted, - "PRIVATE": NetworkAccessTypePrivate, - "": NetworkAccessTypeEmpty, - } - - enum, ok := mappingNetworkAccessTypeEnum[val] - return enum, ok -} - -type NetworkAccessSpec struct { - // +kubebuilder:validation:Enum:="";"PUBLIC";"RESTRICTED";"PRIVATE" - AccessType NetworkAccessTypeEnum `json:"accessType,omitempty"` - IsAccessControlEnabled *bool `json:"isAccessControlEnabled,omitempty"` - AccessControlList []string `json:"accessControlList,omitempty"` - PrivateEndpoint PrivateEndpointSpec `json:"privateEndpoint,omitempty"` - IsMTLSConnectionRequired *bool `json:"isMTLSConnectionRequired,omitempty"` -} - -type PrivateEndpointSpec struct { - SubnetOCID *string `json:"subnetOCID,omitempty"` - NsgOCIDs []string `json:"nsgOCIDs,omitempty"` - HostnamePrefix *string `json:"hostnamePrefix,omitempty"` -} - -// AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase -type AutonomousDatabaseDetails struct { - AutonomousDatabaseOCID *string `json:"autonomousDatabaseOCID,omitempty"` - CompartmentOCID *string `json:"compartmentOCID,omitempty"` - AutonomousContainerDatabase ACDSpec `json:"autonomousContainerDatabase,omitempty"` - DisplayName *string `json:"displayName,omitempty"` - DbName *string `json:"dbName,omitempty"` - // +kubebuilder:validation:Enum:="OLTP";"DW";"AJD";"APEX" - DbWorkload database.AutonomousDatabaseDbWorkloadEnum `json:"dbWorkload,omitempty"` - // +kubebuilder:validation:Enum:="LICENSE_INCLUDED";"BRING_YOUR_OWN_LICENSE" - LicenseModel database.AutonomousDatabaseLicenseModelEnum `json:"licenseModel,omitempty"` - DbVersion *string `json:"dbVersion,omitempty"` - DataStorageSizeInTBs *int `json:"dataStorageSizeInTBs,omitempty"` - CPUCoreCount *int `json:"cpuCoreCount,omitempty"` - AdminPassword PasswordSpec `json:"adminPassword,omitempty"` - IsAutoScalingEnabled *bool `json:"isAutoScalingEnabled,omitempty"` - IsDedicated *bool `json:"isDedicated,omitempty"` - LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` - - NetworkAccess NetworkAccessSpec `json:"networkAccess,omitempty"` - - FreeformTags map[string]string `json:"freeformTags,omitempty"` - - Wallet WalletSpec `json:"wallet,omitempty"` -} - // AutonomousDatabaseStatus defines the observed state of AutonomousDatabase type AutonomousDatabaseStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` - TimeCreated string `json:"timeCreated,omitempty"` - WalletExpiringDate string `json:"walletExpiringDate,omitempty"` - AllConnectionStrings []ConnectionStringProfile `json:"allConnectionStrings,omitempty"` + // Lifecycle State of the ADB + LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` + // Creation time of the ADB + TimeCreated string `json:"timeCreated,omitempty"` + // Expiring date of the instance wallet + WalletExpiringDate string `json:"walletExpiringDate,omitempty"` + // Connection Strings of the ADB + AllConnectionStrings []ConnectionStringProfile `json:"allConnectionStrings,omitempty"` + // Last Completed Action + Action string `json:"action,omitempty"` // +patchMergeKey=type // +patchStrategy=merge // +listType=map @@ -240,181 +220,3 @@ type AutonomousDatabaseList struct { func init() { SchemeBuilder.Register(&AutonomousDatabase{}, &AutonomousDatabaseList{}) } - -// GetLastSuccessfulSpec returns spec from the lass successful reconciliation. -// Returns nil, nil if there is no lastSuccessfulSpec. -func (adb *AutonomousDatabase) GetLastSuccessfulSpec() (*AutonomousDatabaseSpec, error) { - val, ok := adb.GetAnnotations()[LastSuccessfulSpec] - if !ok { - return nil, nil - } - - specBytes := []byte(val) - sucSpec := AutonomousDatabaseSpec{} - - err := json.Unmarshal(specBytes, &sucSpec) - if err != nil { - return nil, err - } - - return &sucSpec, nil -} - -func (adb *AutonomousDatabase) UpdateLastSuccessfulSpec() error { - specBytes, err := json.Marshal(adb.Spec) - if err != nil { - return err - } - - anns := adb.GetAnnotations() - - if anns == nil { - anns = map[string]string{ - LastSuccessfulSpec: string(specBytes), - } - } else { - anns[LastSuccessfulSpec] = string(specBytes) - } - - adb.SetAnnotations(anns) - - return nil -} - -// UpdateStatusFromOCIADB updates the status subresource -func (adb *AutonomousDatabase) UpdateStatusFromOCIADB(ociObj database.AutonomousDatabase) { - adb.Status.LifecycleState = ociObj.LifecycleState - adb.Status.TimeCreated = FormatSDKTime(ociObj.TimeCreated) - - if *ociObj.IsDedicated { - conns := make([]ConnectionStringSpec, len(ociObj.ConnectionStrings.AllConnectionStrings)) - for key, val := range ociObj.ConnectionStrings.AllConnectionStrings { - conns = append(conns, ConnectionStringSpec{TNSName: key, ConnectionString: val}) - } - - adb.Status.AllConnectionStrings = []ConnectionStringProfile{ - {ConnectionStrings: conns}, - } - } else { - var mTLSConns []ConnectionStringSpec - var tlsConns []ConnectionStringSpec - - var conns []ConnectionStringProfile - - for _, profile := range ociObj.ConnectionStrings.Profiles { - if profile.TlsAuthentication == database.DatabaseConnectionStringProfileTlsAuthenticationMutual { - mTLSConns = append(mTLSConns, ConnectionStringSpec{TNSName: *profile.DisplayName, ConnectionString: *profile.Value}) - } else { - tlsConns = append(tlsConns, ConnectionStringSpec{TNSName: *profile.DisplayName, ConnectionString: *profile.Value}) - } - } - - if len(mTLSConns) > 0 { - conns = append(conns, ConnectionStringProfile{ - TLSAuthentication: tlsAuthenticationMTLS, - ConnectionStrings: mTLSConns, - }) - } - - if len(tlsConns) > 0 { - conns = append(conns, ConnectionStringProfile{ - TLSAuthentication: tlsAuthenticationTLS, - ConnectionStrings: tlsConns, - }) - } - - adb.Status.AllConnectionStrings = conns - } -} - -// UpdateFromOCIADB updates the attributes using database.AutonomousDatabase object -func (adb *AutonomousDatabase) UpdateFromOCIADB(ociObj database.AutonomousDatabase) (specChanged bool) { - oldADB := adb.DeepCopy() - - /*********************************** - * update the spec - ***********************************/ - adb.Spec.Details.AutonomousDatabaseOCID = ociObj.Id - adb.Spec.Details.CompartmentOCID = ociObj.CompartmentId - adb.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID = ociObj.AutonomousContainerDatabaseId - adb.Spec.Details.DisplayName = ociObj.DisplayName - adb.Spec.Details.DbName = ociObj.DbName - adb.Spec.Details.DbWorkload = ociObj.DbWorkload - adb.Spec.Details.LicenseModel = ociObj.LicenseModel - adb.Spec.Details.DbVersion = ociObj.DbVersion - adb.Spec.Details.DataStorageSizeInTBs = ociObj.DataStorageSizeInTBs - adb.Spec.Details.CPUCoreCount = ociObj.CpuCoreCount - adb.Spec.Details.IsAutoScalingEnabled = ociObj.IsAutoScalingEnabled - adb.Spec.Details.IsDedicated = ociObj.IsDedicated - adb.Spec.Details.LifecycleState = NextADBStableState(ociObj.LifecycleState) - // Special case: an emtpy map will be nil after unmarshalling while the OCI always returns an emty map. - if len(ociObj.FreeformTags) != 0 { - adb.Spec.Details.FreeformTags = ociObj.FreeformTags - } else { - adb.Spec.Details.FreeformTags = nil - } - - // Determine network.accessType - if *ociObj.IsDedicated { - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate - } else { - if ociObj.NsgIds != nil || ociObj.PrivateEndpoint != nil || ociObj.PrivateEndpointIp != nil || ociObj.PrivateEndpointLabel != nil { - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate - } else if ociObj.WhitelistedIps != nil { - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypeRestricted - } else { - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePublic - } - } - - adb.Spec.Details.NetworkAccess.IsAccessControlEnabled = ociObj.IsAccessControlEnabled - if len(ociObj.WhitelistedIps) != 0 { - adb.Spec.Details.NetworkAccess.AccessControlList = ociObj.WhitelistedIps - } else { - adb.Spec.Details.NetworkAccess.AccessControlList = nil - } - adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = ociObj.IsMtlsConnectionRequired - adb.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = ociObj.SubnetId - if len(ociObj.NsgIds) != 0 { - adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = ociObj.NsgIds - } else { - adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil - } - adb.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = ociObj.PrivateEndpointLabel - - // The admin password is not going to be updated in a bind operation. Erase the field if the lastSucSpec is nil. - // Leave the wallet field as is because the download wallet operation is independent from the update operation. - lastSucSpec, _ := adb.GetLastSuccessfulSpec() - if lastSucSpec == nil { - adb.Spec.Details.AdminPassword = PasswordSpec{} - } else { - adb.Spec.Details.AdminPassword = lastSucSpec.Details.AdminPassword - } - - /*********************************** - * update the status subresource - ***********************************/ - adb.UpdateStatusFromOCIADB(ociObj) - - return !reflect.DeepEqual(oldADB.Spec, adb.Spec) -} - -// RemoveUnchangedDetails removes the unchanged fields in spec.details, and returns if the details has been changed. -func (adb *AutonomousDatabase) RemoveUnchangedDetails(prevSpec AutonomousDatabaseSpec) (bool, error) { - - changed, err := removeUnchangedFields(prevSpec.Details, &adb.Spec.Details) - if err != nil { - return changed, err - } - - return changed, nil -} - -// A helper function which is useful for debugging. The function prints out a structural JSON format. -func (adb *AutonomousDatabase) String() (string, error) { - out, err := json.MarshalIndent(adb, "", " ") - if err != nil { - return "", err - } - return string(out), nil -} diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go index 27a0df29..e209ae7a 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -39,10 +39,6 @@ package v1alpha1 import ( - "fmt" - - "github.com/oracle/oci-go-sdk/v65/common" - "github.com/oracle/oci-go-sdk/v65/database" dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -63,36 +59,6 @@ func (r *AutonomousDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -//+kubebuilder:webhook:verbs=create;update,path=/mutate-database-oracle-com-v1alpha1-autonomousdatabase,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v1alpha1,name=mautonomousdatabasev1alpha1.kb.io,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &AutonomousDatabase{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *AutonomousDatabase) Default() { - autonomousdatabaselog.Info("default", "name", r.Name) - - if !isDedicated(r) { // Shared database - // AccessType is PUBLIC by default - if r.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypePublic { - r.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = common.Bool(true) - r.Spec.Details.NetworkAccess.AccessControlList = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = nil - } else if r.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypeRestricted { - r.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = nil - } else if r.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypePrivate { - r.Spec.Details.NetworkAccess.AccessControlList = nil - } - } else { // Dedicated database - // AccessType can only be PRIVATE for a dedicated database - r.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate - } - -} - //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v1alpha1,name=vautonomousdatabasev1alpha1.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &AutonomousDatabase{} @@ -117,17 +83,6 @@ func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { } } - if r.Spec.Details.AutonomousDatabaseOCID == nil { // provisioning operation - allErrs = validateCommon(r, allErrs) - allErrs = validateNetworkAccess(r, allErrs) - - if r.Spec.Details.LifecycleState != "" { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("lifecycleState"), - "cannot apply lifecycleState to a provision operation")) - } - } - if len(allErrs) == 0 { return nil, nil } @@ -139,66 +94,43 @@ func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList - var oldADB *AutonomousDatabase = old.(*AutonomousDatabase) + var oldAdb *AutonomousDatabase = old.(*AutonomousDatabase) autonomousdatabaselog.Info("validate update", "name", r.Name) // skip the update of adding ADB OCID or binding - if oldADB.Status.LifecycleState == "" { - return nil, nil - } + // if oldAdb.Status.LifecycleState == "" { + // return nil, nil + // } // cannot update when the old state is in intermediate, except for the change to the hardLink or the terminate operatrion during valid lifecycleState - var copySpec *AutonomousDatabaseSpec = r.Spec.DeepCopy() - specChanged, err := removeUnchangedFields(oldADB.Spec, copySpec) - if err != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec"), err.Error())) - } + // var copySpec *AutonomousDatabaseSpec = r.Spec.DeepCopy() + // specChanged, err := dbv4.RemoveUnchangedFields(oldAdb.Spec, copySpec) + // if err != nil { + // allErrs = append(allErrs, + // field.Forbidden(field.NewPath("spec"), err.Error())) + // } - hardLinkChanged := copySpec.HardLink != nil + // hardLinkChanged := copySpec.HardLink != nil - terminateOp := ValidADBTerminateState(oldADB.Status.LifecycleState) && copySpec.Details.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated + // isTerminateOp := dbv4.CanBeTerminated(oldAdb.Status.LifecycleState) && copySpec.Action == "Terminate" - if specChanged && IsADBIntermediateState(oldADB.Status.LifecycleState) && !terminateOp && !hardLinkChanged { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec"), - "cannot change the spec when the lifecycleState is in an intermdeiate state")) - } + // if specChanged && dbv4.IsAdbIntermediateState(oldAdb.Status.LifecycleState) && !isTerminateOp && !hardLinkChanged { + // allErrs = append(allErrs, + // field.Forbidden(field.NewPath("spec"), + // "cannot change the spec when the lifecycleState is in an intermdeiate state")) + // } // cannot modify autonomousDatabaseOCID - if r.Spec.Details.AutonomousDatabaseOCID != nil && - oldADB.Spec.Details.AutonomousDatabaseOCID != nil && - *r.Spec.Details.AutonomousDatabaseOCID != *oldADB.Spec.Details.AutonomousDatabaseOCID { + if r.Spec.Details.Id != nil && + oldAdb.Spec.Details.Id != nil && + *r.Spec.Details.Id != *oldAdb.Spec.Details.Id { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("details").Child("autonomousDatabaseOCID"), "autonomousDatabaseOCID cannot be modified")) } - // cannot change lifecycleState with other fields together (except the oci config) - var lifecycleChanged, otherFieldsChanged bool - - lifecycleChanged = oldADB.Spec.Details.LifecycleState != "" && - r.Spec.Details.LifecycleState != "" && - oldADB.Spec.Details.LifecycleState != r.Spec.Details.LifecycleState - var copiedADB *AutonomousDatabaseSpec = r.Spec.DeepCopy() - copiedADB.Details.LifecycleState = oldADB.Spec.Details.LifecycleState - copiedADB.OCIConfig = oldADB.Spec.OCIConfig - - otherFieldsChanged, err = removeUnchangedFields(oldADB.Spec, copiedADB) - if err != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec"), err.Error())) - } - - if lifecycleChanged && otherFieldsChanged { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("LifecycleState"), - "cannot change lifecycleState with other spec attributes at the same time")) - } - allErrs = validateCommon(r, allErrs) - allErrs = validateNetworkAccess(r, allErrs) if len(allErrs) == 0 { return nil, nil @@ -210,13 +142,13 @@ func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) (admission.Warni func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.ErrorList { // password - if adb.Spec.Details.AdminPassword.K8sSecret.Name != nil && adb.Spec.Details.AdminPassword.OCISecret.OCID != nil { + if adb.Spec.Details.AdminPassword.K8sSecret.Name != nil && adb.Spec.Details.AdminPassword.OciSecret.Id != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("details").Child("adminPassword"), "cannot apply k8sSecret.name and ociSecret.ocid at the same time")) } - if adb.Spec.Details.Wallet.Password.K8sSecret.Name != nil && adb.Spec.Details.Wallet.Password.OCISecret.OCID != nil { + if adb.Spec.Wallet.Password.K8sSecret.Name != nil && adb.Spec.Wallet.Password.OciSecret.Id != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("details").Child("wallet").Child("password"), "cannot apply k8sSecret.name and ociSecret.ocid at the same time")) @@ -225,58 +157,6 @@ func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.Erro return allErrs } -func validateNetworkAccess(adb *AutonomousDatabase, allErrs field.ErrorList) field.ErrorList { - if !isDedicated(adb) { - // Shared database - if adb.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypeRestricted { - if adb.Spec.Details.NetworkAccess.AccessControlList == nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("accessControlList"), - fmt.Sprintf("accessControlList cannot be empty when the network access type is %s", NetworkAccessTypeRestricted))) - } - } else if adb.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypePrivate { // the accessType is PRIVATE - if adb.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID == nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("privateEndpoint").Child("subnetOCID"), - fmt.Sprintf("subnetOCID cannot be empty when the network access type is %s", NetworkAccessTypePrivate))) - } - } - - // NsgOCIDs only applies to PRIVATE accessType - if adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs != nil && adb.Spec.Details.NetworkAccess.AccessType != NetworkAccessTypePrivate { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("privateEndpoint").Child("nsgOCIDs"), - fmt.Sprintf("NsgOCIDs cannot only be applied when network access type is %s.", NetworkAccessTypePrivate))) - } - - // IsAccessControlEnabled is not applicable to a shared database - if adb.Spec.Details.NetworkAccess.IsAccessControlEnabled != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("IsAccessControlEnabled"), - "isAccessControlEnabled is not applicable on a shared Autonomous Database")) - } - } else { - // Dedicated database - - // accessControlList cannot be provided when Autonomous Database's access control is disabled - if adb.Spec.Details.NetworkAccess.AccessControlList != nil && - (adb.Spec.Details.NetworkAccess.IsAccessControlEnabled == nil || !*adb.Spec.Details.NetworkAccess.IsAccessControlEnabled) { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("accessControlList"), - "access control list cannot be provided when Autonomous Database's access control is disabled")) - } - - // IsMTLSConnectionRequired is not supported by dedicated database - if adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("isMTLSConnectionRequired"), - "isMTLSConnectionRequired is not supported on a dedicated database")) - } - } - - return allErrs -} - // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabase) ValidateDelete() (admission.Warnings, error) { autonomousdatabaselog.Info("validate delete", "name", r.Name) @@ -286,6 +166,6 @@ func (r *AutonomousDatabase) ValidateDelete() (admission.Warnings, error) { // Returns true if AutonomousContainerDatabaseOCID has value. // We don't use Details.IsDedicated because the parameter might be null when it's a provision operation. func isDedicated(adb *AutonomousDatabase) bool { - return adb.Spec.Details.AutonomousContainerDatabase.K8sACD.Name != nil || - adb.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID != nil + return adb.Spec.Details.AutonomousContainerDatabase.K8sAcd.Name != nil || + adb.Spec.Details.AutonomousContainerDatabase.OciAcd.Id != nil } diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook_test.go b/apis/database/v1alpha1/autonomousdatabase_webhook_test.go index ee26021f..8949f8f4 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook_test.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook_test.go @@ -50,55 +50,6 @@ import ( ) var _ = Describe("test AutonomousDatabase webhook", func() { - Describe("Test AutonomousDatabase mutating webhook", func() { - var ( - resourceName = "testadb" - namespace = "default" - adbLookupKey = types.NamespacedName{Name: resourceName, Namespace: namespace} - - timeout = time.Second * 5 - - adb *AutonomousDatabase - ) - - BeforeEach(func() { - adb = &AutonomousDatabase{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "database.oracle.com/v1alpha1", - Kind: "AutonomousDatabase", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: resourceName, - Namespace: namespace, - }, - Spec: AutonomousDatabaseSpec{ - Details: AutonomousDatabaseDetails{}, - }, - } - }) - - AfterEach(func() { - Expect(k8sClient.Delete(context.TODO(), adb)).To(Succeed()) - }) - - It("Should set the default network access type to PRIVATE, if it's a dedicated ADB", func() { - By("Creating an AutonomousDatabase with ACD_OCID") - adb.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID = common.String("ocid1.autonomouscontainerdatabase.oc1.dummy-acd-ocid") - - Expect(k8sClient.Create(context.TODO(), adb)).To(Succeed()) - - By("Checking the AutonomousDatabase has a network access type PRIVATE") - Eventually(func() NetworkAccessTypeEnum { - err := k8sClient.Get(context.TODO(), adbLookupKey, adb) - if err != nil { - return "" - } - - return adb.Spec.Details.NetworkAccess.AccessType - }, timeout).Should(Equal(NetworkAccessTypePrivate)) - }) - }) - Describe("Test ValidateCreate of the AutonomousDatabase validating webhook", func() { var ( resourceName = "testadb" @@ -119,16 +70,18 @@ var _ = Describe("test AutonomousDatabase webhook", func() { }, Spec: AutonomousDatabaseSpec{ Details: AutonomousDatabaseDetails{ - CompartmentOCID: common.String("fake-compartment-ocid"), - DbName: common.String("fake-dbName"), - DisplayName: common.String("fake-displayName"), - CPUCoreCount: common.Int(1), - AdminPassword: PasswordSpec{ - K8sSecret: K8sSecretSpec{ - Name: common.String("fake-admin-password"), + AutonomousDatabaseBase: AutonomousDatabaseBase{ + CompartmentId: common.String("fake-compartment-ocid"), + DbName: common.String("fake-dbName"), + DisplayName: common.String("fake-displayName"), + CpuCoreCount: common.Int(1), + AdminPassword: PasswordSpec{ + K8sSecret: K8sSecretSpec{ + Name: common.String("fake-admin-password"), + }, }, + DataStorageSizeInTBs: common.Int(1), }, - DataStorageSizeInTBs: common.Int(1), }, }, } @@ -139,7 +92,7 @@ var _ = Describe("test AutonomousDatabase webhook", func() { var errMsg string = "cannot apply k8sSecret.name and ociSecret.ocid at the same time" adb.Spec.Details.AdminPassword.K8sSecret.Name = common.String("test-admin-password") - adb.Spec.Details.AdminPassword.OCISecret.OCID = common.String("fake.ocid1.vaultsecret.oc1...") + adb.Spec.Details.AdminPassword.OciSecret.Id = common.String("fake.ocid1.vaultsecret.oc1...") validateInvalidTest(adb, false, errMsg) }) @@ -147,54 +100,23 @@ var _ = Describe("test AutonomousDatabase webhook", func() { It("Should not apply values to wallet.password.k8sSecret and wallet.password.ociSecret at the same time", func() { var errMsg string = "cannot apply k8sSecret.name and ociSecret.ocid at the same time" - adb.Spec.Details.Wallet.Password.K8sSecret.Name = common.String("test-wallet-password") - adb.Spec.Details.Wallet.Password.OCISecret.OCID = common.String("fake.ocid1.vaultsecret.oc1...") + adb.Spec.Wallet.Password.K8sSecret.Name = common.String("test-wallet-password") + adb.Spec.Wallet.Password.OciSecret.Id = common.String("fake.ocid1.vaultsecret.oc1...") validateInvalidTest(adb, false, errMsg) }) - // Network validation - Context("Shared Autonomous Database", func() { - It("AccessControlList cannot be empty when the network access type is RESTRICTED", func() { - var errMsg string = "accessControlList cannot be empty when the network access type is " + string(NetworkAccessTypeRestricted) - - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypeRestricted - adb.Spec.Details.NetworkAccess.AccessControlList = nil - - validateInvalidTest(adb, false, errMsg) - }) - - It("SubnetOCID and nsgOCIDs cannot be empty when the network access type is PRIVATE", func() { - var errMsg1 string = "subnetOCID cannot be empty when the network access type is " + string(NetworkAccessTypePrivate) - var errMsg2 string = "nsgOCIDs cannot be empty when the network access type is " + string(NetworkAccessTypePrivate) - - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate - adb.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = nil - adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil - - validateInvalidTest(adb, false, errMsg1, errMsg2) - }) - - It("IsAccessControlEnabled is not applicable on a shared Autonomous Database", func() { - var errMsg string = "isAccessControlEnabled is not applicable on a shared Autonomous Database" - - adb.Spec.Details.NetworkAccess.IsAccessControlEnabled = common.Bool(true) - - validateInvalidTest(adb, false, errMsg) - }) - }) - Context("Dedicated Autonomous Database", func() { BeforeEach(func() { - adb.Spec.Details.AutonomousContainerDatabase.K8sACD.Name = common.String("testACD") - adb.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID = common.String("fake-acd-ocid") + adb.Spec.Details.AutonomousContainerDatabase.K8sAcd.Name = common.String("testACD") + adb.Spec.Details.AutonomousContainerDatabase.OciAcd.Id = common.String("fake-acd-ocid") }) It("AccessControlList cannot be empty when the network access type is RESTRICTED", func() { var errMsg string = "access control list cannot be provided when Autonomous Database's access control is disabled" - adb.Spec.Details.NetworkAccess.IsAccessControlEnabled = common.Bool(false) - adb.Spec.Details.NetworkAccess.AccessControlList = []string{"192.168.1.1"} + adb.Spec.Details.IsAccessControlEnabled = common.Bool(false) + adb.Spec.Details.WhitelistedIps = []string{"192.168.1.1"} validateInvalidTest(adb, false, errMsg) }) @@ -202,21 +124,12 @@ var _ = Describe("test AutonomousDatabase webhook", func() { It("AccessControlList cannot be empty when the network access type is RESTRICTED", func() { var errMsg string = "isMTLSConnectionRequired is not supported on a dedicated database" - adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = common.Bool(true) + adb.Spec.Details.IsMtlsConnectionRequired = common.Bool(true) validateInvalidTest(adb, false, errMsg) }) }) - - // Others - It("Cannot apply lifecycleState to a provision operation", func() { - var errMsg string = "cannot apply lifecycleState to a provision operation" - - adb.Spec.Details.LifecycleState = database.AutonomousDatabaseLifecycleStateStopped - - validateInvalidTest(adb, false, errMsg) - }) }) // Skip the common and network validations since they're already verified in the test for ValidateCreate @@ -242,14 +155,16 @@ var _ = Describe("test AutonomousDatabase webhook", func() { Namespace: namespace, }, Spec: AutonomousDatabaseSpec{ + Action: "Create", Details: AutonomousDatabaseDetails{ - CompartmentOCID: common.String("fake-compartment-ocid"), - AutonomousDatabaseOCID: common.String("fake-adb-ocid"), - DbName: common.String("fake-dbName"), - DisplayName: common.String("fake-displayName"), - CPUCoreCount: common.Int(1), - DataStorageSizeInTBs: common.Int(1), - LifecycleState: database.AutonomousDatabaseLifecycleStateAvailable, + Id: common.String("fake-adb-ocid"), + AutonomousDatabaseBase: AutonomousDatabaseBase{ + CompartmentId: common.String("fake-compartment-ocid"), + DbName: common.String("fake-dbName"), + DisplayName: common.String("fake-displayName"), + CpuCoreCount: common.Int(1), + DataStorageSizeInTBs: common.Int(1), + }, }, }, } @@ -293,16 +208,7 @@ var _ = Describe("test AutonomousDatabase webhook", func() { It("AutonomousDatabaseOCID cannot be modified", func() { var errMsg string = "autonomousDatabaseOCID cannot be modified" - adb.Spec.Details.AutonomousDatabaseOCID = common.String("modified-adb-ocid") - - validateInvalidTest(adb, true, errMsg) - }) - - It("Cannot change lifecycleState with other spec attributes at the same time", func() { - var errMsg string = "cannot change lifecycleState with other spec attributes at the same time" - - adb.Spec.Details.LifecycleState = database.AutonomousDatabaseLifecycleStateStopped - adb.Spec.Details.CPUCoreCount = common.Int(2) + adb.Spec.Details.Id = common.String("modified-adb-ocid") validateInvalidTest(adb, true, errMsg) }) diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_types.go b/apis/database/v1alpha1/autonomousdatabasebackup_types.go index 95c77560..aa70c2d5 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_types.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_types.go @@ -43,6 +43,7 @@ import ( "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! @@ -57,7 +58,7 @@ type AutonomousDatabaseBackupSpec struct { AutonomousDatabaseBackupOCID *string `json:"autonomousDatabaseBackupOCID,omitempty"` IsLongTermBackup *bool `json:"isLongTermBackup,omitempty"` RetentionPeriodInDays *int `json:"retentionPeriodInDays,omitempty"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + OCIConfig OciConfigSpec `json:"ociConfig,omitempty"` } // AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup @@ -104,7 +105,7 @@ func init() { SchemeBuilder.Register(&AutonomousDatabaseBackup{}, &AutonomousDatabaseBackupList{}) } -func (b *AutonomousDatabaseBackup) UpdateStatusFromOCIBackup(ociBackup database.AutonomousDatabaseBackup, ociADB database.AutonomousDatabase) { +func (b *AutonomousDatabaseBackup) UpdateStatusFromOCIBackup(ociBackup database.AutonomousDatabaseBackup, ociAdb database.AutonomousDatabase) { b.Status.AutonomousDatabaseOCID = *ociBackup.AutonomousDatabaseId b.Status.CompartmentOCID = *ociBackup.CompartmentId b.Status.Type = ociBackup.Type @@ -112,14 +113,14 @@ func (b *AutonomousDatabaseBackup) UpdateStatusFromOCIBackup(ociBackup database. b.Status.LifecycleState = ociBackup.LifecycleState - b.Status.TimeStarted = FormatSDKTime(ociBackup.TimeStarted) - b.Status.TimeEnded = FormatSDKTime(ociBackup.TimeEnded) + b.Status.TimeStarted = dbv4.FormatSDKTime(ociBackup.TimeStarted) + b.Status.TimeEnded = dbv4.FormatSDKTime(ociBackup.TimeEnded) - b.Status.DBDisplayName = *ociADB.DisplayName - b.Status.DBName = *ociADB.DbName + b.Status.DBDisplayName = *ociAdb.DisplayName + b.Status.DBName = *ociAdb.DbName } // GetTimeEnded returns the status.timeEnded in SDKTime format func (b *AutonomousDatabaseBackup) GetTimeEnded() (*common.SDKTime, error) { - return parseDisplayTime(b.Status.TimeEnded) + return dbv4.ParseDisplayTime(b.Status.TimeEnded) } diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go index e48c9d3c..ffa9b888 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go @@ -91,12 +91,12 @@ func (r *AutonomousDatabaseBackup) ValidateCreate() (admission.Warnings, error) } } - if r.Spec.Target.K8sADB.Name == nil && r.Spec.Target.OCIADB.OCID == nil { + if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.Ocid == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sADB.Name != nil && r.Spec.Target.OCIADB.OCID != nil { + if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.Ocid != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB or ociADB, but not both")) } @@ -123,14 +123,14 @@ func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) (admission "cannot assign a new autonomousDatabaseBackupOCID to this backup")) } - if oldBackup.Spec.Target.K8sADB.Name != nil && r.Spec.Target.K8sADB.Name != nil && - *oldBackup.Spec.Target.K8sADB.Name != *r.Spec.Target.K8sADB.Name { + if oldBackup.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.K8sAdb.Name != nil && + *oldBackup.Spec.Target.K8sAdb.Name != *r.Spec.Target.K8sAdb.Name { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target").Child("k8sADB").Child("name"), "cannot assign a new name to the target")) } - if oldBackup.Spec.Target.OCIADB.OCID != nil && r.Spec.Target.OCIADB.OCID != nil && - *oldBackup.Spec.Target.OCIADB.OCID != *r.Spec.Target.OCIADB.OCID { + if oldBackup.Spec.Target.OciAdb.Ocid != nil && r.Spec.Target.OciAdb.Ocid != nil && + *oldBackup.Spec.Target.OciAdb.Ocid != *r.Spec.Target.OciAdb.Ocid { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target").Child("ociADB").Child("ocid"), "cannot assign a new ocid to the target")) } diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go index 497c3f28..87eb1618 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go @@ -75,8 +75,8 @@ var _ = Describe("test AutonomousDatabaseBackup webhook", func() { It("Should specify at least one of the k8sADB and ociADB", func() { var errMsg string = "target ADB is empty" - backup.Spec.Target.K8sADB.Name = nil - backup.Spec.Target.OCIADB.OCID = nil + backup.Spec.Target.K8sAdb.Name = nil + backup.Spec.Target.OciAdb.Ocid = nil validateInvalidTest(backup, false, errMsg) }) @@ -84,8 +84,8 @@ var _ = Describe("test AutonomousDatabaseBackup webhook", func() { It("Should specify either k8sADB or ociADB, but not both", func() { var errMsg string = "specify either k8sADB or ociADB, but not both" - backup.Spec.Target.K8sADB.Name = common.String("fake-target-adb") - backup.Spec.Target.OCIADB.OCID = common.String("fake.ocid1.autonomousdatabase.oc1...") + backup.Spec.Target.K8sAdb.Name = common.String("fake-target-adb") + backup.Spec.Target.OciAdb.Ocid = common.String("fake.ocid1.autonomousdatabase.oc1...") validateInvalidTest(backup, false, errMsg) }) @@ -133,15 +133,15 @@ var _ = Describe("test AutonomousDatabaseBackup webhook", func() { Expect(k8sClient.Delete(context.TODO(), backup)).To(Succeed()) }) - Context("The bakcup is using target.k8sADB.name", func() { + Context("The bakcup is using target.k8sAdb.name", func() { BeforeEach(func() { - backup.Spec.Target.K8sADB.Name = common.String("fake-target-adb") + backup.Spec.Target.K8sAdb.Name = common.String("fake-target-adb") }) It("Cannot assign a new name to the target", func() { var errMsg string = "cannot assign a new name to the target" - backup.Spec.Target.K8sADB.Name = common.String("modified-target-adb") + backup.Spec.Target.K8sAdb.Name = common.String("modified-target-adb") validateInvalidTest(backup, true, errMsg) }) @@ -157,13 +157,13 @@ var _ = Describe("test AutonomousDatabaseBackup webhook", func() { Context("The bakcup is using target.ociADB.ocid", func() { BeforeEach(func() { - backup.Spec.Target.OCIADB.OCID = common.String("fake.ocid1.autonomousdatabase.oc1...") + backup.Spec.Target.OciAdb.Ocid = common.String("fake.ocid1.autonomousdatabase.oc1...") }) It("Cannot assign a new ocid to the target", func() { var errMsg string = "cannot assign a new ocid to the target" - backup.Spec.Target.OCIADB.OCID = common.String("modified.ocid1.autonomousdatabase.oc1...") + backup.Spec.Target.OciAdb.Ocid = common.String("modified.ocid1.autonomousdatabase.oc1...") validateInvalidTest(backup, true, errMsg) }) diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_types.go b/apis/database/v1alpha1/autonomousdatabaserestore_types.go index 4bea0043..ef8698b2 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_types.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_types.go @@ -46,22 +46,23 @@ import ( "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" "github.com/oracle/oci-go-sdk/v65/workrequests" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. -type K8sADBBackupSpec struct { +type K8sAdbBackupSpec struct { Name *string `json:"name,omitempty"` } -type PITSpec struct { +type PitSpec struct { // The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT Timestamp *string `json:"timestamp,omitempty"` } type SourceSpec struct { - K8sADBBackup K8sADBBackupSpec `json:"k8sADBBackup,omitempty"` - PointInTime PITSpec `json:"pointInTime,omitempty"` + K8sAdbBackup K8sAdbBackupSpec `json:"k8sADBBackup,omitempty"` + PointInTime PitSpec `json:"pointInTime,omitempty"` } // AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore @@ -70,7 +71,7 @@ type AutonomousDatabaseRestoreSpec struct { // Important: Run "make" to regenerate code after modifying this file Target TargetSpec `json:"target"` Source SourceSpec `json:"source"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + OCIConfig OciConfigSpec `json:"ociConfig,omitempty"` } // AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore @@ -120,7 +121,7 @@ func (r *AutonomousDatabaseRestore) GetPIT() (*common.SDKTime, error) { if r.Spec.Source.PointInTime.Timestamp == nil { return nil, errors.New("the timestamp is empty") } - return parseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) + return dbv4.ParseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) } func (r *AutonomousDatabaseRestore) UpdateStatus( @@ -132,7 +133,7 @@ func (r *AutonomousDatabaseRestore) UpdateStatus( r.Status.WorkRequestOCID = *workResp.Id r.Status.Status = workResp.Status - r.Status.TimeAccepted = FormatSDKTime(workResp.TimeAccepted) - r.Status.TimeStarted = FormatSDKTime(workResp.TimeStarted) - r.Status.TimeEnded = FormatSDKTime(workResp.TimeFinished) + r.Status.TimeAccepted = dbv4.FormatSDKTime(workResp.TimeAccepted) + r.Status.TimeStarted = dbv4.FormatSDKTime(workResp.TimeStarted) + r.Status.TimeEnded = dbv4.FormatSDKTime(workResp.TimeFinished) } diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go index 2784d8dc..dcd57137 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -83,24 +84,24 @@ func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) } // Validate the target ADB - if r.Spec.Target.K8sADB.Name == nil && r.Spec.Target.OCIADB.OCID == nil { + if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.Ocid == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sADB.Name != nil && r.Spec.Target.OCIADB.OCID != nil { + if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.Ocid != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB.name or ociADB.ocid, but not both")) } // Validate the restore source - if r.Spec.Source.K8sADBBackup.Name == nil && + if r.Spec.Source.K8sAdbBackup.Name == nil && r.Spec.Source.PointInTime.Timestamp == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source"), "retore source is empty")) } - if r.Spec.Source.K8sADBBackup.Name != nil && + if r.Spec.Source.K8sAdbBackup.Name != nil && r.Spec.Source.PointInTime.Timestamp != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source"), "cannot apply backupName and the PITR parameters at the same time")) @@ -108,7 +109,7 @@ func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) // Verify the timestamp format if it's PITR if r.Spec.Source.PointInTime.Timestamp != nil { - _, err := parseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) + _, err := dbv4.ParseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) if err != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source").Child("pointInTime").Child("timestamp"), "invalid timestamp format")) diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go index aed87d71..0cc9b692 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go @@ -71,8 +71,8 @@ var _ = Describe("test AutonomousDatabaseRestore webhook", func() { It("Should specify at least one of the k8sADB and ociADB", func() { var errMsg string = "target ADB is empty" - restore.Spec.Target.K8sADB.Name = nil - restore.Spec.Target.OCIADB.OCID = nil + restore.Spec.Target.K8sAdb.Name = nil + restore.Spec.Target.OciAdb.Ocid = nil validateInvalidTest(restore, false, errMsg) }) @@ -80,8 +80,8 @@ var _ = Describe("test AutonomousDatabaseRestore webhook", func() { It("Should specify either k8sADB.name or ociADB.ocid, but not both", func() { var errMsg string = "specify either k8sADB.name or ociADB.ocid, but not both" - restore.Spec.Target.K8sADB.Name = common.String("fake-target-adb") - restore.Spec.Target.OCIADB.OCID = common.String("fake.ocid1.autonomousdatabase.oc1...") + restore.Spec.Target.K8sAdb.Name = common.String("fake-target-adb") + restore.Spec.Target.OciAdb.Ocid = common.String("fake.ocid1.autonomousdatabase.oc1...") validateInvalidTest(restore, false, errMsg) }) @@ -89,7 +89,7 @@ var _ = Describe("test AutonomousDatabaseRestore webhook", func() { It("Should select at least one restore source", func() { var errMsg string = "retore source is empty" - restore.Spec.Source.K8sADBBackup.Name = nil + restore.Spec.Source.K8sAdbBackup.Name = nil restore.Spec.Source.PointInTime.Timestamp = nil validateInvalidTest(restore, false, errMsg) @@ -98,7 +98,7 @@ var _ = Describe("test AutonomousDatabaseRestore webhook", func() { It("Cannot apply backupName and the PITR parameters at the same time", func() { var errMsg string = "cannot apply backupName and the PITR parameters at the same time" - restore.Spec.Source.K8sADBBackup.Name = common.String("fake-source-adb-backup") + restore.Spec.Source.K8sAdbBackup.Name = common.String("fake-source-adb-backup") restore.Spec.Source.PointInTime.Timestamp = common.String("2021-12-23 11:03:13 UTC") validateInvalidTest(restore, false, errMsg) diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 36c26a7c..eb68895b 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -49,18 +49,18 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ACDSpec) DeepCopyInto(out *ACDSpec) { +func (in *AcdSpec) DeepCopyInto(out *AcdSpec) { *out = *in - in.K8sACD.DeepCopyInto(&out.K8sACD) - in.OCIACD.DeepCopyInto(&out.OCIACD) + in.K8sAcd.DeepCopyInto(&out.K8sAcd) + in.OciAcd.DeepCopyInto(&out.OciAcd) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACDSpec. -func (in *ACDSpec) DeepCopy() *ACDSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AcdSpec. +func (in *AcdSpec) DeepCopy() *AcdSpec { if in == nil { return nil } - out := new(ACDSpec) + out := new(AcdSpec) in.DeepCopyInto(out) return out } @@ -326,15 +326,10 @@ func (in *AutonomousDatabaseBackupStatus) DeepCopy() *AutonomousDatabaseBackupSt } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails) { +func (in *AutonomousDatabaseBase) DeepCopyInto(out *AutonomousDatabaseBase) { *out = *in - if in.AutonomousDatabaseOCID != nil { - in, out := &in.AutonomousDatabaseOCID, &out.AutonomousDatabaseOCID - *out = new(string) - **out = **in - } - if in.CompartmentOCID != nil { - in, out := &in.CompartmentOCID, &out.CompartmentOCID + if in.CompartmentId != nil { + in, out := &in.CompartmentId, &out.CompartmentId *out = new(string) **out = **in } @@ -359,11 +354,21 @@ func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails *out = new(int) **out = **in } - if in.CPUCoreCount != nil { - in, out := &in.CPUCoreCount, &out.CPUCoreCount + if in.CpuCoreCount != nil { + in, out := &in.CpuCoreCount, &out.CpuCoreCount *out = new(int) **out = **in } + if in.ComputeCount != nil { + in, out := &in.ComputeCount, &out.ComputeCount + *out = new(float32) + **out = **in + } + if in.OcpuCount != nil { + in, out := &in.OcpuCount, &out.OcpuCount + *out = new(float32) + **out = **in + } in.AdminPassword.DeepCopyInto(&out.AdminPassword) if in.IsAutoScalingEnabled != nil { in, out := &in.IsAutoScalingEnabled, &out.IsAutoScalingEnabled @@ -375,7 +380,41 @@ func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails *out = new(bool) **out = **in } - in.NetworkAccess.DeepCopyInto(&out.NetworkAccess) + if in.IsFreeTier != nil { + in, out := &in.IsFreeTier, &out.IsFreeTier + *out = new(bool) + **out = **in + } + if in.IsAccessControlEnabled != nil { + in, out := &in.IsAccessControlEnabled, &out.IsAccessControlEnabled + *out = new(bool) + **out = **in + } + if in.WhitelistedIps != nil { + in, out := &in.WhitelistedIps, &out.WhitelistedIps + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.SubnetId != nil { + in, out := &in.SubnetId, &out.SubnetId + *out = new(string) + **out = **in + } + if in.NsgIds != nil { + in, out := &in.NsgIds, &out.NsgIds + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PrivateEndpointLabel != nil { + in, out := &in.PrivateEndpointLabel, &out.PrivateEndpointLabel + *out = new(string) + **out = **in + } + if in.IsMtlsConnectionRequired != nil { + in, out := &in.IsMtlsConnectionRequired, &out.IsMtlsConnectionRequired + *out = new(bool) + **out = **in + } if in.FreeformTags != nil { in, out := &in.FreeformTags, &out.FreeformTags *out = make(map[string]string, len(*in)) @@ -383,7 +422,43 @@ func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails (*out)[key] = val } } - in.Wallet.DeepCopyInto(&out.Wallet) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseBase. +func (in *AutonomousDatabaseBase) DeepCopy() *AutonomousDatabaseBase { + if in == nil { + return nil + } + out := new(AutonomousDatabaseBase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseClone) DeepCopyInto(out *AutonomousDatabaseClone) { + *out = *in + in.AutonomousDatabaseBase.DeepCopyInto(&out.AutonomousDatabaseBase) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseClone. +func (in *AutonomousDatabaseClone) DeepCopy() *AutonomousDatabaseClone { + if in == nil { + return nil + } + out := new(AutonomousDatabaseClone) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails) { + *out = *in + in.AutonomousDatabaseBase.DeepCopyInto(&out.AutonomousDatabaseBase) + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseDetails. @@ -524,7 +599,9 @@ func (in *AutonomousDatabaseRestoreStatus) DeepCopy() *AutonomousDatabaseRestore func (in *AutonomousDatabaseSpec) DeepCopyInto(out *AutonomousDatabaseSpec) { *out = *in in.Details.DeepCopyInto(&out.Details) - in.OCIConfig.DeepCopyInto(&out.OCIConfig) + in.Clone.DeepCopyInto(&out.Clone) + in.Wallet.DeepCopyInto(&out.Wallet) + in.OciConfig.DeepCopyInto(&out.OciConfig) if in.HardLink != nil { in, out := &in.HardLink, &out.HardLink *out = new(bool) @@ -1284,7 +1361,7 @@ func (in *GsmStatusDetails) DeepCopy() *GsmStatusDetails { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *K8sACDSpec) DeepCopyInto(out *K8sACDSpec) { +func (in *K8sAcdSpec) DeepCopyInto(out *K8sAcdSpec) { *out = *in if in.Name != nil { in, out := &in.Name, &out.Name @@ -1293,18 +1370,18 @@ func (in *K8sACDSpec) DeepCopyInto(out *K8sACDSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sACDSpec. -func (in *K8sACDSpec) DeepCopy() *K8sACDSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sAcdSpec. +func (in *K8sAcdSpec) DeepCopy() *K8sAcdSpec { if in == nil { return nil } - out := new(K8sACDSpec) + out := new(K8sAcdSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *K8sADBBackupSpec) DeepCopyInto(out *K8sADBBackupSpec) { +func (in *K8sAdbBackupSpec) DeepCopyInto(out *K8sAdbBackupSpec) { *out = *in if in.Name != nil { in, out := &in.Name, &out.Name @@ -1313,18 +1390,18 @@ func (in *K8sADBBackupSpec) DeepCopyInto(out *K8sADBBackupSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sADBBackupSpec. -func (in *K8sADBBackupSpec) DeepCopy() *K8sADBBackupSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sAdbBackupSpec. +func (in *K8sAdbBackupSpec) DeepCopy() *K8sAdbBackupSpec { if in == nil { return nil } - out := new(K8sADBBackupSpec) + out := new(K8sAdbBackupSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *K8sADBSpec) DeepCopyInto(out *K8sADBSpec) { +func (in *K8sAdbSpec) DeepCopyInto(out *K8sAdbSpec) { *out = *in if in.Name != nil { in, out := &in.Name, &out.Name @@ -1333,12 +1410,12 @@ func (in *K8sADBSpec) DeepCopyInto(out *K8sADBSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sADBSpec. -func (in *K8sADBSpec) DeepCopy() *K8sADBSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sAdbSpec. +func (in *K8sAdbSpec) DeepCopy() *K8sAdbSpec { if in == nil { return nil } - out := new(K8sADBSpec) + out := new(K8sAdbSpec) in.DeepCopyInto(out) return out } @@ -1394,78 +1471,47 @@ func (in *KMSDetailsStatus) DeepCopy() *KMSDetailsStatus { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NetworkAccessSpec) DeepCopyInto(out *NetworkAccessSpec) { +func (in *OciAcdSpec) DeepCopyInto(out *OciAcdSpec) { *out = *in - if in.IsAccessControlEnabled != nil { - in, out := &in.IsAccessControlEnabled, &out.IsAccessControlEnabled - *out = new(bool) - **out = **in - } - if in.AccessControlList != nil { - in, out := &in.AccessControlList, &out.AccessControlList - *out = make([]string, len(*in)) - copy(*out, *in) - } - in.PrivateEndpoint.DeepCopyInto(&out.PrivateEndpoint) - if in.IsMTLSConnectionRequired != nil { - in, out := &in.IsMTLSConnectionRequired, &out.IsMTLSConnectionRequired - *out = new(bool) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkAccessSpec. -func (in *NetworkAccessSpec) DeepCopy() *NetworkAccessSpec { - if in == nil { - return nil - } - out := new(NetworkAccessSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIACDSpec) DeepCopyInto(out *OCIACDSpec) { - *out = *in - if in.OCID != nil { - in, out := &in.OCID, &out.OCID + if in.Id != nil { + in, out := &in.Id, &out.Id *out = new(string) **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIACDSpec. -func (in *OCIACDSpec) DeepCopy() *OCIACDSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciAcdSpec. +func (in *OciAcdSpec) DeepCopy() *OciAcdSpec { if in == nil { return nil } - out := new(OCIACDSpec) + out := new(OciAcdSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIADBSpec) DeepCopyInto(out *OCIADBSpec) { +func (in *OciAdbSpec) DeepCopyInto(out *OciAdbSpec) { *out = *in - if in.OCID != nil { - in, out := &in.OCID, &out.OCID + if in.Ocid != nil { + in, out := &in.Ocid, &out.Ocid *out = new(string) **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIADBSpec. -func (in *OCIADBSpec) DeepCopy() *OCIADBSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciAdbSpec. +func (in *OciAdbSpec) DeepCopy() *OciAdbSpec { if in == nil { return nil } - out := new(OCIADBSpec) + out := new(OciAdbSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { +func (in *OciConfigSpec) DeepCopyInto(out *OciConfigSpec) { *out = *in if in.ConfigMapName != nil { in, out := &in.ConfigMapName, &out.ConfigMapName @@ -1479,32 +1525,32 @@ func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfigSpec. -func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciConfigSpec. +func (in *OciConfigSpec) DeepCopy() *OciConfigSpec { if in == nil { return nil } - out := new(OCIConfigSpec) + out := new(OciConfigSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCISecretSpec) DeepCopyInto(out *OCISecretSpec) { +func (in *OciSecretSpec) DeepCopyInto(out *OciSecretSpec) { *out = *in - if in.OCID != nil { - in, out := &in.OCID, &out.OCID + if in.Id != nil { + in, out := &in.Id, &out.Id *out = new(string) **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCISecretSpec. -func (in *OCISecretSpec) DeepCopy() *OCISecretSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciSecretSpec. +func (in *OciSecretSpec) DeepCopy() *OciSecretSpec { if in == nil { return nil } - out := new(OCISecretSpec) + out := new(OciSecretSpec) in.DeepCopyInto(out) return out } @@ -1798,31 +1844,11 @@ func (in *PDBDetailsStatus) DeepCopy() *PDBDetailsStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PITSpec) DeepCopyInto(out *PITSpec) { - *out = *in - if in.Timestamp != nil { - in, out := &in.Timestamp, &out.Timestamp - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PITSpec. -func (in *PITSpec) DeepCopy() *PITSpec { - if in == nil { - return nil - } - out := new(PITSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PasswordSpec) DeepCopyInto(out *PasswordSpec) { *out = *in in.K8sSecret.DeepCopyInto(&out.K8sSecret) - in.OCISecret.DeepCopyInto(&out.OCISecret) + in.OciSecret.DeepCopyInto(&out.OciSecret) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswordSpec. @@ -1836,46 +1862,36 @@ func (in *PasswordSpec) DeepCopy() *PasswordSpec { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PortMapping) DeepCopyInto(out *PortMapping) { +func (in *PitSpec) DeepCopyInto(out *PitSpec) { *out = *in + if in.Timestamp != nil { + in, out := &in.Timestamp, &out.Timestamp + *out = new(string) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortMapping. -func (in *PortMapping) DeepCopy() *PortMapping { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PitSpec. +func (in *PitSpec) DeepCopy() *PitSpec { if in == nil { return nil } - out := new(PortMapping) + out := new(PitSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrivateEndpointSpec) DeepCopyInto(out *PrivateEndpointSpec) { +func (in *PortMapping) DeepCopyInto(out *PortMapping) { *out = *in - if in.SubnetOCID != nil { - in, out := &in.SubnetOCID, &out.SubnetOCID - *out = new(string) - **out = **in - } - if in.NsgOCIDs != nil { - in, out := &in.NsgOCIDs, &out.NsgOCIDs - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.HostnamePrefix != nil { - in, out := &in.HostnamePrefix, &out.HostnamePrefix - *out = new(string) - **out = **in - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateEndpointSpec. -func (in *PrivateEndpointSpec) DeepCopy() *PrivateEndpointSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortMapping. +func (in *PortMapping) DeepCopy() *PortMapping { if in == nil { return nil } - out := new(PrivateEndpointSpec) + out := new(PortMapping) in.DeepCopyInto(out) return out } @@ -2374,7 +2390,7 @@ func (in *SingleInstanceDatabaseStatus) DeepCopy() *SingleInstanceDatabaseStatus // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { *out = *in - in.K8sADBBackup.DeepCopyInto(&out.K8sADBBackup) + in.K8sAdbBackup.DeepCopyInto(&out.K8sAdbBackup) in.PointInTime.DeepCopyInto(&out.PointInTime) } @@ -2391,8 +2407,8 @@ func (in *SourceSpec) DeepCopy() *SourceSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in - in.K8sADB.DeepCopyInto(&out.K8sADB) - in.OCIADB.DeepCopyInto(&out.OCIADB) + in.K8sAdb.DeepCopyInto(&out.K8sAdb) + in.OciAdb.DeepCopyInto(&out.OciAdb) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSpec. diff --git a/apis/database/v4/adbfamily_common_spec.go b/apis/database/v4/adbfamily_common_spec.go new file mode 100644 index 00000000..87434852 --- /dev/null +++ b/apis/database/v4/adbfamily_common_spec.go @@ -0,0 +1,67 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +// LastSuccessfulSpec is an annotation key which maps to the value of last successful spec +const LastSuccessfulSpec string = "lastSuccessfulSpec" + +/************************ +* OCI config +************************/ +type OciConfigSpec struct { + ConfigMapName *string `json:"configMapName,omitempty"` + SecretName *string `json:"secretName,omitempty"` +} + +/************************ +* ADB spec +************************/ +type K8sAdbSpec struct { + Name *string `json:"name,omitempty"` +} + +type OciAdbSpec struct { + OCID *string `json:"ocid,omitempty"` +} + +// TargetSpec defines the spec of the target for backup/restore runs. +type TargetSpec struct { + K8sAdb K8sAdbSpec `json:"k8sADB,omitempty"` + OciAdb OciAdbSpec `json:"ociADB,omitempty"` +} diff --git a/apis/database/v4/adbfamily_common_utils.go b/apis/database/v4/adbfamily_utils.go similarity index 91% rename from apis/database/v4/adbfamily_common_utils.go rename to apis/database/v4/adbfamily_utils.go index da432662..dc384408 100644 --- a/apis/database/v4/adbfamily_common_utils.go +++ b/apis/database/v4/adbfamily_utils.go @@ -43,16 +43,16 @@ import ( "reflect" "time" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" "github.com/oracle/oci-go-sdk/v65/workrequests" + "sigs.k8s.io/controller-runtime/pkg/client" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// LastSuccessfulSpec is an annotation key which maps to the value of last successful spec -const LastSuccessfulSpec string = "lastSuccessfulSpec" +// This file contains the util functions that are shared by specs in both +// apis/database/v1alpha1 and apis/database/v4. // File the meta condition and return the meta view func CreateMetaCondition(obj client.Object, err error, lifecycleState string, stateMsg string) metav1.Condition { @@ -67,31 +67,6 @@ func CreateMetaCondition(obj client.Object, err error, lifecycleState string, st } } -/************************ -* OCI config -************************/ -type OCIConfigSpec struct { - ConfigMapName *string `json:"configMapName,omitempty"` - SecretName *string `json:"secretName,omitempty"` -} - -/************************ -* ADB spec -************************/ -type K8sADBSpec struct { - Name *string `json:"name,omitempty"` -} - -type OCIADBSpec struct { - OCID *string `json:"ocid,omitempty"` -} - -// TargetSpec defines the spec of the target for backup/restore runs. -type TargetSpec struct { - K8sADB K8sADBSpec `json:"k8sADB,omitempty"` - OCIADB OCIADBSpec `json:"ociADB,omitempty"` -} - /************************** * Remove Unchanged Fields **************************/ @@ -99,7 +74,7 @@ type TargetSpec struct { // removeUnchangedFields removes the unchanged fields in the struct and returns if the struct is changed. // lastSpec should be a derefereced struct that is the last successful spec, e.g. AutonomousDatabaseSpec. // curSpec should be a pointer pointing to the struct that is being proccessed, e.g., *AutonomousDatabaseSpec. -func removeUnchangedFields(lastSpec interface{}, curSpec interface{}) (bool, error) { +func RemoveUnchangedFields(lastSpec interface{}, curSpec interface{}) (bool, error) { if reflect.ValueOf(lastSpec).Kind() != reflect.Struct { return false, errors.New("lastSpec should be a struct") } @@ -217,7 +192,7 @@ func FormatSDKTime(sdkTime *common.SDKTime) string { return time.Format(displayFormat) } -func parseDisplayTime(val string) (*common.SDKTime, error) { +func ParseDisplayTime(val string) (*common.SDKTime, error) { parsedTime, err := time.Parse(displayFormat, val) if err != nil { return nil, err @@ -229,7 +204,7 @@ func parseDisplayTime(val string) (*common.SDKTime, error) { /************************ * LifecycleState check ************************/ -func IsADBIntermediateState(state database.AutonomousDatabaseLifecycleStateEnum) bool { +func IsAdbIntermediateState(state database.AutonomousDatabaseLifecycleStateEnum) bool { if state == database.AutonomousDatabaseLifecycleStateProvisioning || state == database.AutonomousDatabaseLifecycleStateUpdating || state == database.AutonomousDatabaseLifecycleStateScaleInProgress || @@ -248,7 +223,7 @@ func IsADBIntermediateState(state database.AutonomousDatabaseLifecycleStateEnum) return false } -func ValidADBTerminateState(state database.AutonomousDatabaseLifecycleStateEnum) bool { +func CanBeTerminated(state database.AutonomousDatabaseLifecycleStateEnum) bool { if state == database.AutonomousDatabaseLifecycleStateProvisioning || state == database.AutonomousDatabaseLifecycleStateAvailable || state == database.AutonomousDatabaseLifecycleStateStopped || diff --git a/apis/database/v4/autonomouscontainerdatabase_types.go b/apis/database/v4/autonomouscontainerdatabase_types.go index d6a7e161..be9cc615 100644 --- a/apis/database/v4/autonomouscontainerdatabase_types.go +++ b/apis/database/v4/autonomouscontainerdatabase_types.go @@ -85,7 +85,7 @@ type AutonomousContainerDatabaseSpec struct { Action AcdActionEnum `json:"action,omitempty"` FreeformTags map[string]string `json:"freeformTags,omitempty"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + OCIConfig OciConfigSpec `json:"ociConfig,omitempty"` // +kubebuilder:default:=false HardLink *bool `json:"hardLink,omitempty"` } @@ -208,7 +208,7 @@ func (acd *AutonomousContainerDatabase) UpdateFromOCIACD(ociObj database.Autonom // RemoveUnchangedSpec removes the unchanged fields in spec, and returns if the spec has been changed. func (acd *AutonomousContainerDatabase) RemoveUnchangedSpec(prevSpec AutonomousContainerDatabaseSpec) (bool, error) { - changed, err := removeUnchangedFields(prevSpec, &acd.Spec) + changed, err := RemoveUnchangedFields(prevSpec, &acd.Spec) if err != nil { return changed, err } diff --git a/apis/database/v4/autonomouscontainerdatabase_webhook.go b/apis/database/v4/autonomouscontainerdatabase_webhook.go index f18df608..9fcb9d8b 100644 --- a/apis/database/v4/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v4/autonomouscontainerdatabase_webhook.go @@ -82,7 +82,7 @@ func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) (admiss // cannot update when the old state is in intermediate state, except for the terminate operatrion var copiedSpec *AutonomousContainerDatabaseSpec = r.Spec.DeepCopy() - changed, err := removeUnchangedFields(oldACD.Spec, copiedSpec) + changed, err := RemoveUnchangedFields(oldACD.Spec, copiedSpec) if err != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), err.Error())) diff --git a/apis/database/v4/autonomousdatabase_types.go b/apis/database/v4/autonomousdatabase_types.go index d0899b0b..de50669b 100644 --- a/apis/database/v4/autonomousdatabase_types.go +++ b/apis/database/v4/autonomousdatabase_types.go @@ -49,34 +49,79 @@ import ( // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. -// name of our custom finalizer -const ADB_FINALIZER = "database.oracle.com/adb-finalizer" - // AutonomousDatabaseSpec defines the desired state of AutonomousDatabase // Important: Run "make" to regenerate code after modifying this file type AutonomousDatabaseSpec struct { + // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone + Action string `json:"action"` Details AutonomousDatabaseDetails `json:"details"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + Clone AutonomousDatabaseClone `json:"clone"` + Wallet WalletSpec `json:"wallet,omitempty"` + OciConfig OciConfigSpec `json:"ociConfig,omitempty"` // +kubebuilder:default:=false HardLink *bool `json:"hardLink,omitempty"` } +type AutonomousDatabaseDetails struct { + AutonomousDatabaseBase `json:",inline"` + Id *string `json:"id,omitempty"` +} + +type AutonomousDatabaseClone struct { + AutonomousDatabaseBase `json:",inline"` + CloneType database.CreateAutonomousDatabaseCloneDetailsCloneTypeEnum `json:"id,cloneType"` +} + +// AutonomousDatabaseBase defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase +type AutonomousDatabaseBase struct { + Id *string `json:"id,omitempty"` + CompartmentId *string `json:"compartmentId,omitempty"` + AutonomousContainerDatabase AcdSpec `json:"autonomousContainerDatabase,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + DbName *string `json:"dbName,omitempty"` + // +kubebuilder:validation:Enum:="OLTP";"DW";"AJD";"APEX" + DbWorkload database.AutonomousDatabaseDbWorkloadEnum `json:"dbWorkload,omitempty"` + // +kubebuilder:validation:Enum:="LICENSE_INCLUDED";"BRING_YOUR_OWN_LICENSE" + LicenseModel database.AutonomousDatabaseLicenseModelEnum `json:"licenseModel,omitempty"` + DbVersion *string `json:"dbVersion,omitempty"` + DataStorageSizeInTBs *int `json:"dataStorageSizeInTBs,omitempty"` + CpuCoreCount *int `json:"cpuCoreCount,omitempty"` + // +kubebuilder:validation:Enum:="ECPU";"OCPU" + ComputeModel database.AutonomousDatabaseComputeModelEnum `json:"computeModel,omitempty"` + ComputeCount *float32 `json:"computeCount,omitempty"` + OcpuCount *float32 `json:"ocpuCount,omitempty"` + AdminPassword PasswordSpec `json:"adminPassword,omitempty"` + IsAutoScalingEnabled *bool `json:"isAutoScalingEnabled,omitempty"` + IsDedicated *bool `json:"isDedicated,omitempty"` + IsFreeTier *bool `json:"isFreeTier,omitempty"` + + // NetworkAccess + IsAccessControlEnabled *bool `json:"isAccessControlEnabled,omitempty"` + WhitelistedIps []string `json:"whitelistedIps,omitempty"` + SubnetId *string `json:"subnetId,omitempty"` + NsgIds []string `json:"nsgIds,omitempty"` + PrivateEndpointLabel *string `json:"privateEndpointLabel,omitempty"` + IsMtlsConnectionRequired *bool `json:"isMtlsConnectionRequired,omitempty"` + + FreeformTags map[string]string `json:"freeformTags,omitempty"` +} + /************************ * ACD specs ************************/ -type K8sACDSpec struct { +type K8sAcdSpec struct { Name *string `json:"name,omitempty"` } -type OCIACDSpec struct { - OCID *string `json:"ocid,omitempty"` +type OCIAcdSpec struct { + Id *string `json:"id,omitempty"` } -// ACDSpec defines the spec of the target for backup/restore runs. +// AcdSpec defines the spec of the target for backup/restore runs. // The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup -type ACDSpec struct { - K8sACD K8sACDSpec `json:"k8sACD,omitempty"` - OCIACD OCIACDSpec `json:"ociACD,omitempty"` +type AcdSpec struct { + K8sAcd K8sAcdSpec `json:"k8sAcd,omitempty"` + OciAcd OCIAcdSpec `json:"ociAcd,omitempty"` } /************************ @@ -86,13 +131,13 @@ type K8sSecretSpec struct { Name *string `json:"name,omitempty"` } -type OCISecretSpec struct { - OCID *string `json:"ocid,omitempty"` +type OciSecretSpec struct { + Id *string `json:"id,omitempty"` } type PasswordSpec struct { K8sSecret K8sSecretSpec `json:"k8sSecret,omitempty"` - OCISecret OCISecretSpec `json:"ociSecret,omitempty"` + OciSecret OciSecretSpec `json:"ociSecret,omitempty"` } type WalletSpec struct { @@ -100,80 +145,18 @@ type WalletSpec struct { Password PasswordSpec `json:"password,omitempty"` } -/************************ -* Network Access specs -************************/ - -type NetworkAccessTypeEnum string - -const ( - NetworkAccessTypePublic NetworkAccessTypeEnum = "PUBLIC" - NetworkAccessTypeRestricted NetworkAccessTypeEnum = "RESTRICTED" - NetworkAccessTypePrivate NetworkAccessTypeEnum = "PRIVATE" - NetworkAccessTypeEmpty NetworkAccessTypeEnum = "" -) - -func GetNetworkAccessTypeFromString(val string) (NetworkAccessTypeEnum, bool) { - var mappingNetworkAccessTypeEnum = map[string]NetworkAccessTypeEnum{ - "PUBLIC": NetworkAccessTypePublic, - "RESTRICTED": NetworkAccessTypeRestricted, - "PRIVATE": NetworkAccessTypePrivate, - "": NetworkAccessTypeEmpty, - } - - enum, ok := mappingNetworkAccessTypeEnum[val] - return enum, ok -} - -type NetworkAccessSpec struct { - // +kubebuilder:validation:Enum:="";"PUBLIC";"RESTRICTED";"PRIVATE" - AccessType NetworkAccessTypeEnum `json:"accessType,omitempty"` - IsAccessControlEnabled *bool `json:"isAccessControlEnabled,omitempty"` - AccessControlList []string `json:"accessControlList,omitempty"` - PrivateEndpoint PrivateEndpointSpec `json:"privateEndpoint,omitempty"` - IsMTLSConnectionRequired *bool `json:"isMTLSConnectionRequired,omitempty"` -} - -type PrivateEndpointSpec struct { - SubnetOCID *string `json:"subnetOCID,omitempty"` - NsgOCIDs []string `json:"nsgOCIDs,omitempty"` - HostnamePrefix *string `json:"hostnamePrefix,omitempty"` -} - -// AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase -type AutonomousDatabaseDetails struct { - AutonomousDatabaseOCID *string `json:"autonomousDatabaseOCID,omitempty"` - CompartmentOCID *string `json:"compartmentOCID,omitempty"` - AutonomousContainerDatabase ACDSpec `json:"autonomousContainerDatabase,omitempty"` - DisplayName *string `json:"displayName,omitempty"` - DbName *string `json:"dbName,omitempty"` - // +kubebuilder:validation:Enum:="OLTP";"DW";"AJD";"APEX" - DbWorkload database.AutonomousDatabaseDbWorkloadEnum `json:"dbWorkload,omitempty"` - // +kubebuilder:validation:Enum:="LICENSE_INCLUDED";"BRING_YOUR_OWN_LICENSE" - LicenseModel database.AutonomousDatabaseLicenseModelEnum `json:"licenseModel,omitempty"` - DbVersion *string `json:"dbVersion,omitempty"` - DataStorageSizeInTBs *int `json:"dataStorageSizeInTBs,omitempty"` - CPUCoreCount *int `json:"cpuCoreCount,omitempty"` - AdminPassword PasswordSpec `json:"adminPassword,omitempty"` - IsAutoScalingEnabled *bool `json:"isAutoScalingEnabled,omitempty"` - IsDedicated *bool `json:"isDedicated,omitempty"` - LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` - - NetworkAccess NetworkAccessSpec `json:"networkAccess,omitempty"` - - FreeformTags map[string]string `json:"freeformTags,omitempty"` - - Wallet WalletSpec `json:"wallet,omitempty"` -} - // AutonomousDatabaseStatus defines the observed state of AutonomousDatabase type AutonomousDatabaseStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` - TimeCreated string `json:"timeCreated,omitempty"` - WalletExpiringDate string `json:"walletExpiringDate,omitempty"` - AllConnectionStrings []ConnectionStringProfile `json:"allConnectionStrings,omitempty"` + // Lifecycle State of the ADB + LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` + // Creation time of the ADB + TimeCreated string `json:"timeCreated,omitempty"` + // Expiring date of the instance wallet + WalletExpiringDate string `json:"walletExpiringDate,omitempty"` + // Connection Strings of the ADB + AllConnectionStrings []ConnectionStringProfile `json:"allConnectionStrings,omitempty"` + // Last Completed Action + Action string `json:"action,omitempty"` // +patchMergeKey=type // +patchStrategy=merge // +listType=map @@ -245,48 +228,8 @@ func init() { // Implement conversion.Hub interface, which means any resource version can convert into v4 func (*AutonomousDatabase) Hub() {} -// GetLastSuccessfulSpec returns spec from the lass successful reconciliation. -// Returns nil, nil if there is no lastSuccessfulSpec. -func (adb *AutonomousDatabase) GetLastSuccessfulSpec() (*AutonomousDatabaseSpec, error) { - val, ok := adb.GetAnnotations()[LastSuccessfulSpec] - if !ok { - return nil, nil - } - - specBytes := []byte(val) - sucSpec := AutonomousDatabaseSpec{} - - err := json.Unmarshal(specBytes, &sucSpec) - if err != nil { - return nil, err - } - - return &sucSpec, nil -} - -func (adb *AutonomousDatabase) UpdateLastSuccessfulSpec() error { - specBytes, err := json.Marshal(adb.Spec) - if err != nil { - return err - } - - anns := adb.GetAnnotations() - - if anns == nil { - anns = map[string]string{ - LastSuccessfulSpec: string(specBytes), - } - } else { - anns[LastSuccessfulSpec] = string(specBytes) - } - - adb.SetAnnotations(anns) - - return nil -} - // UpdateStatusFromOCIADB updates the status subresource -func (adb *AutonomousDatabase) UpdateStatusFromOCIADB(ociObj database.AutonomousDatabase) { +func (adb *AutonomousDatabase) UpdateStatusFromOciAdb(ociObj database.AutonomousDatabase) { adb.Status.LifecycleState = ociObj.LifecycleState adb.Status.TimeCreated = FormatSDKTime(ociObj.TimeCreated) @@ -332,73 +275,101 @@ func (adb *AutonomousDatabase) UpdateStatusFromOCIADB(ociObj database.Autonomous } // UpdateFromOCIADB updates the attributes using database.AutonomousDatabase object -func (adb *AutonomousDatabase) UpdateFromOCIADB(ociObj database.AutonomousDatabase) (specChanged bool) { +func (adb *AutonomousDatabase) UpdateFromOciAdb(ociObj database.AutonomousDatabase, overwrite bool) (specChanged bool) { oldADB := adb.DeepCopy() /*********************************** * update the spec ***********************************/ - adb.Spec.Details.AutonomousDatabaseOCID = ociObj.Id - adb.Spec.Details.CompartmentOCID = ociObj.CompartmentId - adb.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID = ociObj.AutonomousContainerDatabaseId - adb.Spec.Details.DisplayName = ociObj.DisplayName - adb.Spec.Details.DbName = ociObj.DbName - adb.Spec.Details.DbWorkload = ociObj.DbWorkload - adb.Spec.Details.LicenseModel = ociObj.LicenseModel - adb.Spec.Details.DbVersion = ociObj.DbVersion - adb.Spec.Details.DataStorageSizeInTBs = ociObj.DataStorageSizeInTBs - adb.Spec.Details.CPUCoreCount = ociObj.CpuCoreCount - adb.Spec.Details.IsAutoScalingEnabled = ociObj.IsAutoScalingEnabled - adb.Spec.Details.IsDedicated = ociObj.IsDedicated - adb.Spec.Details.LifecycleState = NextADBStableState(ociObj.LifecycleState) - // Special case: an emtpy map will be nil after unmarshalling while the OCI always returns an emty map. - if len(ociObj.FreeformTags) != 0 { - adb.Spec.Details.FreeformTags = ociObj.FreeformTags - } else { - adb.Spec.Details.FreeformTags = nil + if overwrite || adb.Spec.Details.Id == nil { + adb.Spec.Details.Id = ociObj.Id } - - // Determine network.accessType - if *ociObj.IsDedicated { - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate - } else { - if ociObj.NsgIds != nil || ociObj.PrivateEndpoint != nil || ociObj.PrivateEndpointIp != nil || ociObj.PrivateEndpointLabel != nil { - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate - } else if ociObj.WhitelistedIps != nil { - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypeRestricted + if overwrite || adb.Spec.Details.CompartmentId == nil { + adb.Spec.Details.CompartmentId = ociObj.CompartmentId + } + if overwrite || adb.Spec.Details.AutonomousContainerDatabase.OciAcd.Id == nil { + adb.Spec.Details.AutonomousContainerDatabase.OciAcd.Id = ociObj.AutonomousContainerDatabaseId + } + if overwrite || adb.Spec.Details.DisplayName == nil { + adb.Spec.Details.DisplayName = ociObj.DisplayName + } + if overwrite || adb.Spec.Details.DbName == nil { + adb.Spec.Details.DbName = ociObj.DbName + } + if overwrite || adb.Spec.Details.DbWorkload == "" { + adb.Spec.Details.DbWorkload = ociObj.DbWorkload + } + if overwrite || adb.Spec.Details.LicenseModel == "" { + adb.Spec.Details.LicenseModel = ociObj.LicenseModel + } + if overwrite || adb.Spec.Details.DbVersion == nil { + adb.Spec.Details.DbVersion = ociObj.DbVersion + } + if overwrite || adb.Spec.Details.DataStorageSizeInTBs == nil { + adb.Spec.Details.DataStorageSizeInTBs = ociObj.DataStorageSizeInTBs + } + if overwrite || adb.Spec.Details.CpuCoreCount == nil { + adb.Spec.Details.CpuCoreCount = ociObj.CpuCoreCount + } + if overwrite || adb.Spec.Details.ComputeModel == "" { + adb.Spec.Details.ComputeModel = ociObj.ComputeModel + } + if overwrite || adb.Spec.Details.OcpuCount == nil { + adb.Spec.Details.OcpuCount = ociObj.OcpuCount + } + if overwrite || adb.Spec.Details.ComputeCount == nil { + adb.Spec.Details.ComputeCount = ociObj.ComputeCount + } + if overwrite || adb.Spec.Details.IsAutoScalingEnabled == nil { + adb.Spec.Details.IsAutoScalingEnabled = ociObj.IsAutoScalingEnabled + } + if overwrite || adb.Spec.Details.IsDedicated == nil { + adb.Spec.Details.IsDedicated = ociObj.IsDedicated + } + if overwrite || adb.Spec.Details.IsFreeTier == nil { + adb.Spec.Details.IsFreeTier = ociObj.IsFreeTier + } + if overwrite || adb.Spec.Details.FreeformTags == nil { + // Special case: an emtpy map will be nil after unmarshalling while the OCI always returns an emty map. + if len(ociObj.FreeformTags) != 0 { + adb.Spec.Details.FreeformTags = ociObj.FreeformTags } else { - adb.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePublic + adb.Spec.Details.FreeformTags = nil } } - adb.Spec.Details.NetworkAccess.IsAccessControlEnabled = ociObj.IsAccessControlEnabled - if len(ociObj.WhitelistedIps) != 0 { - adb.Spec.Details.NetworkAccess.AccessControlList = ociObj.WhitelistedIps - } else { - adb.Spec.Details.NetworkAccess.AccessControlList = nil + if overwrite || adb.Spec.Details.IsAccessControlEnabled == nil { + adb.Spec.Details.IsAccessControlEnabled = ociObj.IsAccessControlEnabled } - adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = ociObj.IsMtlsConnectionRequired - adb.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = ociObj.SubnetId - if len(ociObj.NsgIds) != 0 { - adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = ociObj.NsgIds - } else { - adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil - } - adb.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = ociObj.PrivateEndpointLabel - // The admin password is not going to be updated in a bind operation. Erase the field if the lastSucSpec is nil. - // Leave the wallet field as is because the download wallet operation is independent from the update operation. - lastSucSpec, _ := adb.GetLastSuccessfulSpec() - if lastSucSpec == nil { - adb.Spec.Details.AdminPassword = PasswordSpec{} - } else { - adb.Spec.Details.AdminPassword = lastSucSpec.Details.AdminPassword + if overwrite || adb.Spec.Details.WhitelistedIps == nil { + if len(ociObj.WhitelistedIps) != 0 { + adb.Spec.Details.WhitelistedIps = ociObj.WhitelistedIps + } else { + adb.Spec.Details.WhitelistedIps = nil + } + } + if overwrite || adb.Spec.Details.IsMtlsConnectionRequired == nil { + adb.Spec.Details.IsMtlsConnectionRequired = ociObj.IsMtlsConnectionRequired + } + if overwrite || adb.Spec.Details.SubnetId == nil { + adb.Spec.Details.SubnetId = ociObj.SubnetId + } + if overwrite || adb.Spec.Details.NsgIds == nil { + if len(ociObj.NsgIds) != 0 { + adb.Spec.Details.NsgIds = ociObj.NsgIds + } else { + adb.Spec.Details.NsgIds = nil + } + } + if overwrite || adb.Spec.Details.PrivateEndpointLabel == nil { + adb.Spec.Details.PrivateEndpointLabel = ociObj.PrivateEndpointLabel } /*********************************** * update the status subresource ***********************************/ - adb.UpdateStatusFromOCIADB(ociObj) + adb.UpdateStatusFromOciAdb(ociObj) return !reflect.DeepEqual(oldADB.Spec, adb.Spec) } @@ -406,7 +377,7 @@ func (adb *AutonomousDatabase) UpdateFromOCIADB(ociObj database.AutonomousDataba // RemoveUnchangedDetails removes the unchanged fields in spec.details, and returns if the details has been changed. func (adb *AutonomousDatabase) RemoveUnchangedDetails(prevSpec AutonomousDatabaseSpec) (bool, error) { - changed, err := removeUnchangedFields(prevSpec.Details, &adb.Spec.Details) + changed, err := RemoveUnchangedFields(prevSpec.Details, &adb.Spec.Details) if err != nil { return changed, err } diff --git a/apis/database/v4/autonomousdatabase_webhook.go b/apis/database/v4/autonomousdatabase_webhook.go index 82c56d34..f7eb60aa 100644 --- a/apis/database/v4/autonomousdatabase_webhook.go +++ b/apis/database/v4/autonomousdatabase_webhook.go @@ -39,10 +39,6 @@ package v4 import ( - "fmt" - - "github.com/oracle/oci-go-sdk/v65/common" - "github.com/oracle/oci-go-sdk/v65/database" dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -63,38 +59,7 @@ func (r *AutonomousDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -//+kubebuilder:webhook:verbs=create;update,path=/mutate-database-oracle-com-v4-autonomousdatabase,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v4,name=mautonomousdatabasev4.kb.io,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &AutonomousDatabase{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *AutonomousDatabase) Default() { - autonomousdatabaselog.Info("default", "name", r.Name) - - if !isDedicated(r) { // Shared database - // AccessType is PUBLIC by default - if r.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypePublic { - r.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = common.Bool(true) - r.Spec.Details.NetworkAccess.AccessControlList = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = nil - } else if r.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypeRestricted { - r.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil - r.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = nil - } else if r.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypePrivate { - r.Spec.Details.NetworkAccess.AccessControlList = nil - } - } else { // Dedicated database - // AccessType can only be PRIVATE for a dedicated database - r.Spec.Details.NetworkAccess.AccessType = NetworkAccessTypePrivate - } - -} - -//+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomousdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v4,name=vautonomousdatabasev4.kb.io,admissionReviewVersions=v1 - +// +kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomousdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v4,name=vautonomousdatabasev4.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &AutonomousDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type @@ -117,17 +82,6 @@ func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { } } - if r.Spec.Details.AutonomousDatabaseOCID == nil { // provisioning operation - allErrs = validateCommon(r, allErrs) - allErrs = validateNetworkAccess(r, allErrs) - - if r.Spec.Details.LifecycleState != "" { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("lifecycleState"), - "cannot apply lifecycleState to a provision operation")) - } - } - if len(allErrs) == 0 { return nil, nil } @@ -143,62 +97,39 @@ func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) (admission.Warni autonomousdatabaselog.Info("validate update", "name", r.Name) - // skip the update of adding ADB OCID or binding - if oldADB.Status.LifecycleState == "" { - return nil, nil - } + // skip the verification of adding ADB OCID or binding + // if oldADB.Status.LifecycleState == "" { + // return nil, nil + // } // cannot update when the old state is in intermediate, except for the change to the hardLink or the terminate operatrion during valid lifecycleState - var copySpec *AutonomousDatabaseSpec = r.Spec.DeepCopy() - specChanged, err := removeUnchangedFields(oldADB.Spec, copySpec) - if err != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec"), err.Error())) - } + // var copySpec *AutonomousDatabaseSpec = r.Spec.DeepCopy() + // specChanged, err := RemoveUnchangedFields(oldADB.Spec, copySpec) + // if err != nil { + // allErrs = append(allErrs, + // field.Forbidden(field.NewPath("spec"), err.Error())) + // } - hardLinkChanged := copySpec.HardLink != nil + // hardLinkChanged := copySpec.HardLink != nil - terminateOp := ValidADBTerminateState(oldADB.Status.LifecycleState) && copySpec.Details.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated + // isTerminateOp := CanBeTerminated(oldADB.Status.LifecycleState) && copySpec.Action == "Terminate" - if specChanged && IsADBIntermediateState(oldADB.Status.LifecycleState) && !terminateOp && !hardLinkChanged { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec"), - "cannot change the spec when the lifecycleState is in an intermdeiate state")) - } + // if specChanged && IsAdbIntermediateState(oldADB.Status.LifecycleState) && !isTerminateOp && !hardLinkChanged { + // allErrs = append(allErrs, + // field.Forbidden(field.NewPath("spec"), + // "cannot change the spec when the lifecycleState is in an intermdeiate state")) + // } // cannot modify autonomousDatabaseOCID - if r.Spec.Details.AutonomousDatabaseOCID != nil && - oldADB.Spec.Details.AutonomousDatabaseOCID != nil && - *r.Spec.Details.AutonomousDatabaseOCID != *oldADB.Spec.Details.AutonomousDatabaseOCID { + if r.Spec.Details.Id != nil && + oldADB.Spec.Details.Id != nil && + *r.Spec.Details.Id != *oldADB.Spec.Details.Id { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("details").Child("autonomousDatabaseOCID"), "autonomousDatabaseOCID cannot be modified")) } - // cannot change lifecycleState with other fields together (except the oci config) - var lifecycleChanged, otherFieldsChanged bool - - lifecycleChanged = oldADB.Spec.Details.LifecycleState != "" && - r.Spec.Details.LifecycleState != "" && - oldADB.Spec.Details.LifecycleState != r.Spec.Details.LifecycleState - var copiedADB *AutonomousDatabaseSpec = r.Spec.DeepCopy() - copiedADB.Details.LifecycleState = oldADB.Spec.Details.LifecycleState - copiedADB.OCIConfig = oldADB.Spec.OCIConfig - - otherFieldsChanged, err = removeUnchangedFields(oldADB.Spec, copiedADB) - if err != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec"), err.Error())) - } - - if lifecycleChanged && otherFieldsChanged { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("LifecycleState"), - "cannot change lifecycleState with other spec attributes at the same time")) - } - allErrs = validateCommon(r, allErrs) - allErrs = validateNetworkAccess(r, allErrs) if len(allErrs) == 0 { return nil, nil @@ -210,13 +141,13 @@ func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) (admission.Warni func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.ErrorList { // password - if adb.Spec.Details.AdminPassword.K8sSecret.Name != nil && adb.Spec.Details.AdminPassword.OCISecret.OCID != nil { + if adb.Spec.Details.AdminPassword.K8sSecret.Name != nil && adb.Spec.Details.AdminPassword.OciSecret.Id != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("details").Child("adminPassword"), "cannot apply k8sSecret.name and ociSecret.ocid at the same time")) } - if adb.Spec.Details.Wallet.Password.K8sSecret.Name != nil && adb.Spec.Details.Wallet.Password.OCISecret.OCID != nil { + if adb.Spec.Wallet.Password.K8sSecret.Name != nil && adb.Spec.Wallet.Password.OciSecret.Id != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("details").Child("wallet").Child("password"), "cannot apply k8sSecret.name and ociSecret.ocid at the same time")) @@ -225,58 +156,6 @@ func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.Erro return allErrs } -func validateNetworkAccess(adb *AutonomousDatabase, allErrs field.ErrorList) field.ErrorList { - if !isDedicated(adb) { - // Shared database - if adb.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypeRestricted { - if adb.Spec.Details.NetworkAccess.AccessControlList == nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("accessControlList"), - fmt.Sprintf("accessControlList cannot be empty when the network access type is %s", NetworkAccessTypeRestricted))) - } - } else if adb.Spec.Details.NetworkAccess.AccessType == NetworkAccessTypePrivate { // the accessType is PRIVATE - if adb.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID == nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("privateEndpoint").Child("subnetOCID"), - fmt.Sprintf("subnetOCID cannot be empty when the network access type is %s", NetworkAccessTypePrivate))) - } - } - - // NsgOCIDs only applies to PRIVATE accessType - if adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs != nil && adb.Spec.Details.NetworkAccess.AccessType != NetworkAccessTypePrivate { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("privateEndpoint").Child("nsgOCIDs"), - fmt.Sprintf("NsgOCIDs cannot only be applied when network access type is %s.", NetworkAccessTypePrivate))) - } - - // IsAccessControlEnabled is not applicable to a shared database - if adb.Spec.Details.NetworkAccess.IsAccessControlEnabled != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("IsAccessControlEnabled"), - "isAccessControlEnabled is not applicable on a shared Autonomous Database")) - } - } else { - // Dedicated database - - // accessControlList cannot be provided when Autonomous Database's access control is disabled - if adb.Spec.Details.NetworkAccess.AccessControlList != nil && - (adb.Spec.Details.NetworkAccess.IsAccessControlEnabled == nil || !*adb.Spec.Details.NetworkAccess.IsAccessControlEnabled) { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("accessControlList"), - "access control list cannot be provided when Autonomous Database's access control is disabled")) - } - - // IsMTLSConnectionRequired is not supported by dedicated database - if adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("networkAccess").Child("isMTLSConnectionRequired"), - "isMTLSConnectionRequired is not supported on a dedicated database")) - } - } - - return allErrs -} - // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabase) ValidateDelete() (admission.Warnings, error) { autonomousdatabaselog.Info("validate delete", "name", r.Name) @@ -286,6 +165,6 @@ func (r *AutonomousDatabase) ValidateDelete() (admission.Warnings, error) { // Returns true if AutonomousContainerDatabaseOCID has value. // We don't use Details.IsDedicated because the parameter might be null when it's a provision operation. func isDedicated(adb *AutonomousDatabase) bool { - return adb.Spec.Details.AutonomousContainerDatabase.K8sACD.Name != nil || - adb.Spec.Details.AutonomousContainerDatabase.OCIACD.OCID != nil + return adb.Spec.Details.AutonomousContainerDatabase.K8sAcd.Name != nil || + adb.Spec.Details.AutonomousContainerDatabase.OciAcd.Id != nil } diff --git a/apis/database/v4/autonomousdatabasebackup_types.go b/apis/database/v4/autonomousdatabasebackup_types.go index 798c8acf..925256c0 100644 --- a/apis/database/v4/autonomousdatabasebackup_types.go +++ b/apis/database/v4/autonomousdatabasebackup_types.go @@ -57,7 +57,7 @@ type AutonomousDatabaseBackupSpec struct { AutonomousDatabaseBackupOCID *string `json:"autonomousDatabaseBackupOCID,omitempty"` IsLongTermBackup *bool `json:"isLongTermBackup,omitempty"` RetentionPeriodInDays *int `json:"retentionPeriodInDays,omitempty"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + OCIConfig OciConfigSpec `json:"ociConfig,omitempty"` } // AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup @@ -125,5 +125,5 @@ func (b *AutonomousDatabaseBackup) UpdateStatusFromOCIBackup(ociBackup database. // GetTimeEnded returns the status.timeEnded in SDKTime format func (b *AutonomousDatabaseBackup) GetTimeEnded() (*common.SDKTime, error) { - return parseDisplayTime(b.Status.TimeEnded) + return ParseDisplayTime(b.Status.TimeEnded) } diff --git a/apis/database/v4/autonomousdatabasebackup_webhook.go b/apis/database/v4/autonomousdatabasebackup_webhook.go index 49f82542..7858adce 100644 --- a/apis/database/v4/autonomousdatabasebackup_webhook.go +++ b/apis/database/v4/autonomousdatabasebackup_webhook.go @@ -91,12 +91,12 @@ func (r *AutonomousDatabaseBackup) ValidateCreate() (admission.Warnings, error) } } - if r.Spec.Target.K8sADB.Name == nil && r.Spec.Target.OCIADB.OCID == nil { + if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.OCID == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sADB.Name != nil && r.Spec.Target.OCIADB.OCID != nil { + if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.OCID != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB or ociADB, but not both")) } @@ -123,14 +123,14 @@ func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) (admission "cannot assign a new autonomousDatabaseBackupOCID to this backup")) } - if oldBackup.Spec.Target.K8sADB.Name != nil && r.Spec.Target.K8sADB.Name != nil && - *oldBackup.Spec.Target.K8sADB.Name != *r.Spec.Target.K8sADB.Name { + if oldBackup.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.K8sAdb.Name != nil && + *oldBackup.Spec.Target.K8sAdb.Name != *r.Spec.Target.K8sAdb.Name { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target").Child("k8sADB").Child("name"), "cannot assign a new name to the target")) } - if oldBackup.Spec.Target.OCIADB.OCID != nil && r.Spec.Target.OCIADB.OCID != nil && - *oldBackup.Spec.Target.OCIADB.OCID != *r.Spec.Target.OCIADB.OCID { + if oldBackup.Spec.Target.OciAdb.OCID != nil && r.Spec.Target.OciAdb.OCID != nil && + *oldBackup.Spec.Target.OciAdb.OCID != *r.Spec.Target.OciAdb.OCID { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target").Child("ociADB").Child("ocid"), "cannot assign a new ocid to the target")) } diff --git a/apis/database/v4/autonomousdatabaserestore_types.go b/apis/database/v4/autonomousdatabaserestore_types.go index 2589ce62..3337c983 100644 --- a/apis/database/v4/autonomousdatabaserestore_types.go +++ b/apis/database/v4/autonomousdatabaserestore_types.go @@ -60,7 +60,7 @@ type PITSpec struct { } type SourceSpec struct { - K8sADBBackup K8sADBBackupSpec `json:"k8sADBBackup,omitempty"` + K8sAdbBackup K8sADBBackupSpec `json:"k8sADBBackup,omitempty"` PointInTime PITSpec `json:"pointInTime,omitempty"` } @@ -70,7 +70,7 @@ type AutonomousDatabaseRestoreSpec struct { // Important: Run "make" to regenerate code after modifying this file Target TargetSpec `json:"target"` Source SourceSpec `json:"source"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + OCIConfig OciConfigSpec `json:"ociConfig,omitempty"` } // AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore @@ -124,7 +124,7 @@ func (r *AutonomousDatabaseRestore) GetPIT() (*common.SDKTime, error) { if r.Spec.Source.PointInTime.Timestamp == nil { return nil, errors.New("the timestamp is empty") } - return parseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) + return ParseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) } func (r *AutonomousDatabaseRestore) UpdateStatus( diff --git a/apis/database/v4/autonomousdatabaserestore_webhook.go b/apis/database/v4/autonomousdatabaserestore_webhook.go index 98e004ba..6e3b4656 100644 --- a/apis/database/v4/autonomousdatabaserestore_webhook.go +++ b/apis/database/v4/autonomousdatabaserestore_webhook.go @@ -83,24 +83,24 @@ func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) } // Validate the target ADB - if r.Spec.Target.K8sADB.Name == nil && r.Spec.Target.OCIADB.OCID == nil { + if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.OCID == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sADB.Name != nil && r.Spec.Target.OCIADB.OCID != nil { + if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.OCID != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB.name or ociADB.ocid, but not both")) } // Validate the restore source - if r.Spec.Source.K8sADBBackup.Name == nil && + if r.Spec.Source.K8sAdbBackup.Name == nil && r.Spec.Source.PointInTime.Timestamp == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source"), "retore source is empty")) } - if r.Spec.Source.K8sADBBackup.Name != nil && + if r.Spec.Source.K8sAdbBackup.Name != nil && r.Spec.Source.PointInTime.Timestamp != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source"), "cannot apply backupName and the PITR parameters at the same time")) @@ -108,7 +108,7 @@ func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) // Verify the timestamp format if it's PITR if r.Spec.Source.PointInTime.Timestamp != nil { - _, err := parseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) + _, err := ParseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) if err != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source").Child("pointInTime").Child("timestamp"), "invalid timestamp format")) diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index f765c6b3..aeca28e7 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -50,18 +50,18 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ACDSpec) DeepCopyInto(out *ACDSpec) { +func (in *AcdSpec) DeepCopyInto(out *AcdSpec) { *out = *in - in.K8sACD.DeepCopyInto(&out.K8sACD) - in.OCIACD.DeepCopyInto(&out.OCIACD) + in.K8sAcd.DeepCopyInto(&out.K8sAcd) + in.OciAcd.DeepCopyInto(&out.OciAcd) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACDSpec. -func (in *ACDSpec) DeepCopy() *ACDSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AcdSpec. +func (in *AcdSpec) DeepCopy() *AcdSpec { if in == nil { return nil } - out := new(ACDSpec) + out := new(AcdSpec) in.DeepCopyInto(out) return out } @@ -359,15 +359,15 @@ func (in *AutonomousDatabaseBackupStatus) DeepCopy() *AutonomousDatabaseBackupSt } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails) { +func (in *AutonomousDatabaseBase) DeepCopyInto(out *AutonomousDatabaseBase) { *out = *in - if in.AutonomousDatabaseOCID != nil { - in, out := &in.AutonomousDatabaseOCID, &out.AutonomousDatabaseOCID + if in.Id != nil { + in, out := &in.Id, &out.Id *out = new(string) **out = **in } - if in.CompartmentOCID != nil { - in, out := &in.CompartmentOCID, &out.CompartmentOCID + if in.CompartmentId != nil { + in, out := &in.CompartmentId, &out.CompartmentId *out = new(string) **out = **in } @@ -392,11 +392,21 @@ func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails *out = new(int) **out = **in } - if in.CPUCoreCount != nil { - in, out := &in.CPUCoreCount, &out.CPUCoreCount + if in.CpuCoreCount != nil { + in, out := &in.CpuCoreCount, &out.CpuCoreCount *out = new(int) **out = **in } + if in.ComputeCount != nil { + in, out := &in.ComputeCount, &out.ComputeCount + *out = new(float32) + **out = **in + } + if in.OcpuCount != nil { + in, out := &in.OcpuCount, &out.OcpuCount + *out = new(float32) + **out = **in + } in.AdminPassword.DeepCopyInto(&out.AdminPassword) if in.IsAutoScalingEnabled != nil { in, out := &in.IsAutoScalingEnabled, &out.IsAutoScalingEnabled @@ -408,7 +418,41 @@ func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails *out = new(bool) **out = **in } - in.NetworkAccess.DeepCopyInto(&out.NetworkAccess) + if in.IsFreeTier != nil { + in, out := &in.IsFreeTier, &out.IsFreeTier + *out = new(bool) + **out = **in + } + if in.IsAccessControlEnabled != nil { + in, out := &in.IsAccessControlEnabled, &out.IsAccessControlEnabled + *out = new(bool) + **out = **in + } + if in.WhitelistedIps != nil { + in, out := &in.WhitelistedIps, &out.WhitelistedIps + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.SubnetId != nil { + in, out := &in.SubnetId, &out.SubnetId + *out = new(string) + **out = **in + } + if in.NsgIds != nil { + in, out := &in.NsgIds, &out.NsgIds + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PrivateEndpointLabel != nil { + in, out := &in.PrivateEndpointLabel, &out.PrivateEndpointLabel + *out = new(string) + **out = **in + } + if in.IsMtlsConnectionRequired != nil { + in, out := &in.IsMtlsConnectionRequired, &out.IsMtlsConnectionRequired + *out = new(bool) + **out = **in + } if in.FreeformTags != nil { in, out := &in.FreeformTags, &out.FreeformTags *out = make(map[string]string, len(*in)) @@ -416,7 +460,43 @@ func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails (*out)[key] = val } } - in.Wallet.DeepCopyInto(&out.Wallet) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseBase. +func (in *AutonomousDatabaseBase) DeepCopy() *AutonomousDatabaseBase { + if in == nil { + return nil + } + out := new(AutonomousDatabaseBase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseClone) DeepCopyInto(out *AutonomousDatabaseClone) { + *out = *in + in.AutonomousDatabaseBase.DeepCopyInto(&out.AutonomousDatabaseBase) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseClone. +func (in *AutonomousDatabaseClone) DeepCopy() *AutonomousDatabaseClone { + if in == nil { + return nil + } + out := new(AutonomousDatabaseClone) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails) { + *out = *in + in.AutonomousDatabaseBase.DeepCopyInto(&out.AutonomousDatabaseBase) + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutonomousDatabaseDetails. @@ -557,7 +637,9 @@ func (in *AutonomousDatabaseRestoreStatus) DeepCopy() *AutonomousDatabaseRestore func (in *AutonomousDatabaseSpec) DeepCopyInto(out *AutonomousDatabaseSpec) { *out = *in in.Details.DeepCopyInto(&out.Details) - in.OCIConfig.DeepCopyInto(&out.OCIConfig) + in.Clone.DeepCopyInto(&out.Clone) + in.Wallet.DeepCopyInto(&out.Wallet) + in.OciConfig.DeepCopyInto(&out.OciConfig) if in.HardLink != nil { in, out := &in.HardLink, &out.HardLink *out = new(bool) @@ -1586,7 +1668,7 @@ func (in *GsmStatusDetails) DeepCopy() *GsmStatusDetails { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *K8sACDSpec) DeepCopyInto(out *K8sACDSpec) { +func (in *K8sADBBackupSpec) DeepCopyInto(out *K8sADBBackupSpec) { *out = *in if in.Name != nil { in, out := &in.Name, &out.Name @@ -1595,18 +1677,18 @@ func (in *K8sACDSpec) DeepCopyInto(out *K8sACDSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sACDSpec. -func (in *K8sACDSpec) DeepCopy() *K8sACDSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sADBBackupSpec. +func (in *K8sADBBackupSpec) DeepCopy() *K8sADBBackupSpec { if in == nil { return nil } - out := new(K8sACDSpec) + out := new(K8sADBBackupSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *K8sADBBackupSpec) DeepCopyInto(out *K8sADBBackupSpec) { +func (in *K8sAcdSpec) DeepCopyInto(out *K8sAcdSpec) { *out = *in if in.Name != nil { in, out := &in.Name, &out.Name @@ -1615,18 +1697,18 @@ func (in *K8sADBBackupSpec) DeepCopyInto(out *K8sADBBackupSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sADBBackupSpec. -func (in *K8sADBBackupSpec) DeepCopy() *K8sADBBackupSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sAcdSpec. +func (in *K8sAcdSpec) DeepCopy() *K8sAcdSpec { if in == nil { return nil } - out := new(K8sADBBackupSpec) + out := new(K8sAcdSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *K8sADBSpec) DeepCopyInto(out *K8sADBSpec) { +func (in *K8sAdbSpec) DeepCopyInto(out *K8sAdbSpec) { *out = *in if in.Name != nil { in, out := &in.Name, &out.Name @@ -1635,12 +1717,12 @@ func (in *K8sADBSpec) DeepCopyInto(out *K8sADBSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sADBSpec. -func (in *K8sADBSpec) DeepCopy() *K8sADBSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sAdbSpec. +func (in *K8sAdbSpec) DeepCopy() *K8sAdbSpec { if in == nil { return nil } - out := new(K8sADBSpec) + out := new(K8sAdbSpec) in.DeepCopyInto(out) return out } @@ -2219,58 +2301,43 @@ func (in *LTDESecret) DeepCopy() *LTDESecret { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NetworkAccessSpec) DeepCopyInto(out *NetworkAccessSpec) { +func (in *OCIAcdSpec) DeepCopyInto(out *OCIAcdSpec) { *out = *in - if in.IsAccessControlEnabled != nil { - in, out := &in.IsAccessControlEnabled, &out.IsAccessControlEnabled - *out = new(bool) - **out = **in - } - if in.AccessControlList != nil { - in, out := &in.AccessControlList, &out.AccessControlList - *out = make([]string, len(*in)) - copy(*out, *in) - } - in.PrivateEndpoint.DeepCopyInto(&out.PrivateEndpoint) - if in.IsMTLSConnectionRequired != nil { - in, out := &in.IsMTLSConnectionRequired, &out.IsMTLSConnectionRequired - *out = new(bool) + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkAccessSpec. -func (in *NetworkAccessSpec) DeepCopy() *NetworkAccessSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIAcdSpec. +func (in *OCIAcdSpec) DeepCopy() *OCIAcdSpec { if in == nil { return nil } - out := new(NetworkAccessSpec) + out := new(OCIAcdSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIACDSpec) DeepCopyInto(out *OCIACDSpec) { +func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { *out = *in - if in.OCID != nil { - in, out := &in.OCID, &out.OCID - *out = new(string) - **out = **in - } + out.Secret = in.Secret } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIACDSpec. -func (in *OCIACDSpec) DeepCopy() *OCIACDSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ORDSPassword. +func (in *ORDSPassword) DeepCopy() *ORDSPassword { if in == nil { return nil } - out := new(OCIACDSpec) + out := new(ORDSPassword) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIADBSpec) DeepCopyInto(out *OCIADBSpec) { +func (in *OciAdbSpec) DeepCopyInto(out *OciAdbSpec) { *out = *in if in.OCID != nil { in, out := &in.OCID, &out.OCID @@ -2279,18 +2346,18 @@ func (in *OCIADBSpec) DeepCopyInto(out *OCIADBSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIADBSpec. -func (in *OCIADBSpec) DeepCopy() *OCIADBSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciAdbSpec. +func (in *OciAdbSpec) DeepCopy() *OciAdbSpec { if in == nil { return nil } - out := new(OCIADBSpec) + out := new(OciAdbSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { +func (in *OciConfigSpec) DeepCopyInto(out *OciConfigSpec) { *out = *in if in.ConfigMapName != nil { in, out := &in.ConfigMapName, &out.ConfigMapName @@ -2304,48 +2371,32 @@ func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfigSpec. -func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciConfigSpec. +func (in *OciConfigSpec) DeepCopy() *OciConfigSpec { if in == nil { return nil } - out := new(OCIConfigSpec) + out := new(OciConfigSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCISecretSpec) DeepCopyInto(out *OCISecretSpec) { +func (in *OciSecretSpec) DeepCopyInto(out *OciSecretSpec) { *out = *in - if in.OCID != nil { - in, out := &in.OCID, &out.OCID + if in.Id != nil { + in, out := &in.Id, &out.Id *out = new(string) **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCISecretSpec. -func (in *OCISecretSpec) DeepCopy() *OCISecretSpec { - if in == nil { - return nil - } - out := new(OCISecretSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ORDSPassword. -func (in *ORDSPassword) DeepCopy() *ORDSPassword { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciSecretSpec. +func (in *OciSecretSpec) DeepCopy() *OciSecretSpec { if in == nil { return nil } - out := new(ORDSPassword) + out := new(OciSecretSpec) in.DeepCopyInto(out) return out } @@ -2841,7 +2892,7 @@ func (in *PasswordSecret) DeepCopy() *PasswordSecret { func (in *PasswordSpec) DeepCopyInto(out *PasswordSpec) { *out = *in in.K8sSecret.DeepCopyInto(&out.K8sSecret) - in.OCISecret.DeepCopyInto(&out.OCISecret) + in.OciSecret.DeepCopyInto(&out.OciSecret) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswordSpec. @@ -3022,36 +3073,6 @@ func (in *PortMapping) DeepCopy() *PortMapping { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrivateEndpointSpec) DeepCopyInto(out *PrivateEndpointSpec) { - *out = *in - if in.SubnetOCID != nil { - in, out := &in.SubnetOCID, &out.SubnetOCID - *out = new(string) - **out = **in - } - if in.NsgOCIDs != nil { - in, out := &in.NsgOCIDs, &out.NsgOCIDs - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.HostnamePrefix != nil { - in, out := &in.HostnamePrefix, &out.HostnamePrefix - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateEndpointSpec. -func (in *PrivateEndpointSpec) DeepCopy() *PrivateEndpointSpec { - if in == nil { - return nil - } - out := new(PrivateEndpointSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretDetails) DeepCopyInto(out *SecretDetails) { *out = *in @@ -3283,7 +3304,7 @@ func (in *ShardingDatabaseStatus) DeepCopy() *ShardingDatabaseStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { *out = *in - in.K8sADBBackup.DeepCopyInto(&out.K8sADBBackup) + in.K8sAdbBackup.DeepCopyInto(&out.K8sAdbBackup) in.PointInTime.DeepCopyInto(&out.PointInTime) } @@ -3347,8 +3368,8 @@ func (in *TNSAdminSecret) DeepCopy() *TNSAdminSecret { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in - in.K8sADB.DeepCopyInto(&out.K8sADB) - in.OCIADB.DeepCopyInto(&out.OCIADB) + in.K8sAdb.DeepCopyInto(&out.K8sAdb) + in.OciAdb.DeepCopyInto(&out.OciAdb) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSpec. diff --git a/commons/adb_family/utils.go b/commons/adb_family/utils.go index 93994458..591b3130 100644 --- a/commons/adb_family/utils.go +++ b/commons/adb_family/utils.go @@ -44,30 +44,30 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -// VerifyTargetADB searches if the target ADB is in the cluster, and set the owner reference to the ADB if it exists. +// VerifyTargetAdb searches if the target ADB is in the cluster. // The function returns two values in the following order: // ocid: the OCID of the target ADB. An empty string is returned if the ocid is nil. // ownerADB: the resource of the targetADB if it's found in the cluster -func VerifyTargetADB(kubeClient client.Client, target dbv4.TargetSpec, namespace string) (*dbv4.AutonomousDatabase, error) { +func VerifyTargetAdb(kubeClient client.Client, target dbv4.TargetSpec, namespace string) (*dbv4.AutonomousDatabase, error) { var err error - var ownerADB *dbv4.AutonomousDatabase + var ownerAdb *dbv4.AutonomousDatabase // Get the target ADB OCID - if target.K8sADB.Name != nil { + if target.K8sAdb.Name != nil { // Find the target ADB using the name of the k8s ADB - ownerADB = &dbv4.AutonomousDatabase{} - if err := k8s.FetchResource(kubeClient, namespace, *target.K8sADB.Name, ownerADB); err != nil { + ownerAdb = &dbv4.AutonomousDatabase{} + if err := k8s.FetchResource(kubeClient, namespace, *target.K8sAdb.Name, ownerAdb); err != nil { return nil, err } } else { // Find the target ADB using the ADB OCID - ownerADB, err = k8s.FetchAutonomousDatabaseWithOCID(kubeClient, namespace, *target.OCIADB.OCID) + ownerAdb, err = k8s.FetchAutonomousDatabaseWithOCID(kubeClient, namespace, *target.OciAdb.OCID) if err != nil { return nil, err } } - return ownerADB, nil + return ownerAdb, nil } diff --git a/commons/k8s/create.go b/commons/k8s/create.go index 9f161968..e56872f4 100644 --- a/commons/k8s/create.go +++ b/commons/k8s/create.go @@ -78,25 +78,25 @@ func CreateSecret(kubeClient client.Client, namespace string, name string, data func CreateAutonomousBackup(kubeClient client.Client, backupName string, backupSummary database.AutonomousDatabaseBackupSummary, - ownerADB *dbv4.AutonomousDatabase) error { + ownerAdb *dbv4.AutonomousDatabase) error { backup := &dbv4.AutonomousDatabaseBackup{ ObjectMeta: metav1.ObjectMeta{ - Namespace: ownerADB.GetNamespace(), + Namespace: ownerAdb.GetNamespace(), Name: backupName, - OwnerReferences: NewOwnerReference(ownerADB), + OwnerReferences: NewOwnerReference(ownerAdb), }, Spec: dbv4.AutonomousDatabaseBackupSpec{ Target: dbv4.TargetSpec{ - K8sADB: dbv4.K8sADBSpec{ - Name: common.String(ownerADB.Name), + K8sAdb: dbv4.K8sAdbSpec{ + Name: common.String(ownerAdb.Name), }, }, DisplayName: backupSummary.DisplayName, AutonomousDatabaseBackupOCID: backupSummary.Id, - OCIConfig: dbv4.OCIConfigSpec{ - ConfigMapName: ownerADB.Spec.OCIConfig.ConfigMapName, - SecretName: ownerADB.Spec.OCIConfig.SecretName, + OCIConfig: dbv4.OciConfigSpec{ + ConfigMapName: ownerAdb.Spec.OciConfig.ConfigMapName, + SecretName: ownerAdb.Spec.OciConfig.SecretName, }, }, } diff --git a/commons/k8s/fetch.go b/commons/k8s/fetch.go index 15087f28..d8fb3c3e 100644 --- a/commons/k8s/fetch.go +++ b/commons/k8s/fetch.go @@ -64,7 +64,7 @@ func FetchResource(kubeClient client.Client, namespace string, name string, obje // Returns the first AutonomousDatabase resource that matches the AutonomousDatabaseOCID of the backup // Sometimes the AutonomousDatabase doesn't exist. It could happen if a user simply want to restore or -// backup the ADB without creating an ADB rersource in the cluster. +// backup the AutonomousDatabase without creating an AutonomousDatabase rersource in the cluster. // If there isn't an AutonomousDatabase with the same OCID, a nil is returned. func FetchAutonomousDatabaseWithOCID(kubeClient client.Client, namespace string, ocid string) (*dbv4.AutonomousDatabase, error) { adbList, err := fetchAutonomousDatabases(kubeClient, namespace) @@ -73,7 +73,7 @@ func FetchAutonomousDatabaseWithOCID(kubeClient client.Client, namespace string, } for _, adb := range adbList.Items { - if adb.Spec.Details.AutonomousDatabaseOCID != nil && *adb.Spec.Details.AutonomousDatabaseOCID == ocid { + if adb.Spec.Details.Id != nil && *adb.Spec.Details.Id == ocid { return &adb, nil } } diff --git a/commons/oci/containerdatabase.go b/commons/oci/containerdatabase.go index da49977d..9391d6f8 100644 --- a/commons/oci/containerdatabase.go +++ b/commons/oci/containerdatabase.go @@ -50,7 +50,7 @@ import ( /******************************** * Autonomous Container Database *******************************/ -func (d *databaseService) CreateAutonomousContainerDatabase(acd *dbv4.AutonomousContainerDatabase) (database.CreateAutonomousContainerDatabaseResponse, error) { +func (d *DatabaseService) CreateAutonomousContainerDatabase(acd *dbv4.AutonomousContainerDatabase) (database.CreateAutonomousContainerDatabaseResponse, error) { createAutonomousContainerDatabaseRequest := database.CreateAutonomousContainerDatabaseRequest{ CreateAutonomousContainerDatabaseDetails: database.CreateAutonomousContainerDatabaseDetails{ CompartmentId: acd.Spec.CompartmentOCID, @@ -63,7 +63,7 @@ func (d *databaseService) CreateAutonomousContainerDatabase(acd *dbv4.Autonomous return d.dbClient.CreateAutonomousContainerDatabase(context.TODO(), createAutonomousContainerDatabaseRequest) } -func (d *databaseService) GetAutonomousContainerDatabase(acdOCID string) (database.GetAutonomousContainerDatabaseResponse, error) { +func (d *DatabaseService) GetAutonomousContainerDatabase(acdOCID string) (database.GetAutonomousContainerDatabaseResponse, error) { getAutonomousContainerDatabaseRequest := database.GetAutonomousContainerDatabaseRequest{ AutonomousContainerDatabaseId: common.String(acdOCID), } @@ -71,7 +71,7 @@ func (d *databaseService) GetAutonomousContainerDatabase(acdOCID string) (databa return d.dbClient.GetAutonomousContainerDatabase(context.TODO(), getAutonomousContainerDatabaseRequest) } -func (d *databaseService) UpdateAutonomousContainerDatabase(acdOCID string, difACD *dbv4.AutonomousContainerDatabase) (database.UpdateAutonomousContainerDatabaseResponse, error) { +func (d *DatabaseService) UpdateAutonomousContainerDatabase(acdOCID string, difACD *dbv4.AutonomousContainerDatabase) (database.UpdateAutonomousContainerDatabaseResponse, error) { updateAutonomousContainerDatabaseRequest := database.UpdateAutonomousContainerDatabaseRequest{ AutonomousContainerDatabaseId: common.String(acdOCID), UpdateAutonomousContainerDatabaseDetails: database.UpdateAutonomousContainerDatabaseDetails{ @@ -84,7 +84,7 @@ func (d *databaseService) UpdateAutonomousContainerDatabase(acdOCID string, difA return d.dbClient.UpdateAutonomousContainerDatabase(context.TODO(), updateAutonomousContainerDatabaseRequest) } -func (d *databaseService) RestartAutonomousContainerDatabase(acdOCID string) (database.RestartAutonomousContainerDatabaseResponse, error) { +func (d *DatabaseService) RestartAutonomousContainerDatabase(acdOCID string) (database.RestartAutonomousContainerDatabaseResponse, error) { restartRequest := database.RestartAutonomousContainerDatabaseRequest{ AutonomousContainerDatabaseId: common.String(acdOCID), } @@ -92,7 +92,7 @@ func (d *databaseService) RestartAutonomousContainerDatabase(acdOCID string) (da return d.dbClient.RestartAutonomousContainerDatabase(context.TODO(), restartRequest) } -func (d *databaseService) TerminateAutonomousContainerDatabase(acdOCID string) (database.TerminateAutonomousContainerDatabaseResponse, error) { +func (d *DatabaseService) TerminateAutonomousContainerDatabase(acdOCID string) (database.TerminateAutonomousContainerDatabaseResponse, error) { terminateRequest := database.TerminateAutonomousContainerDatabaseRequest{ AutonomousContainerDatabaseId: common.String(acdOCID), } diff --git a/commons/oci/create_autonomous_database_clone_request_response.go b/commons/oci/create_autonomous_database_clone_request_response.go new file mode 100644 index 00000000..40b645fb --- /dev/null +++ b/commons/oci/create_autonomous_database_clone_request_response.go @@ -0,0 +1,104 @@ +// Copyright (c) 2016, 2018, 2024, Oracle and/or its affiliates. All rights reserved. +// This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license. +// Code generated. DO NOT EDIT. + +package oci + +import ( + "fmt" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + "net/http" + "strings" +) + +// CreateAutonomousDatabaseRequest wrapper for the CreateAutonomousDatabase operation +// +// # See also +// +// Click https://docs.cloud.oracle.com/en-us/iaas/tools/go-sdk-examples/latest/database/CreateAutonomousDatabase.go.html to see an example of how to use CreateAutonomousDatabaseRequest. +type CreateAutonomousDatabaseCloneRequest struct { + + // Request to create a new Autonomous Database. + CreateAutonomousDatabaseCloneDetails database.CreateAutonomousDatabaseCloneDetails `contributesTo:"body"` + + // A token that uniquely identifies a request so it can be retried in case of a timeout or + // server error without risk of executing that same action again. Retry tokens expire after 24 + // hours, but can be invalidated before then due to conflicting operations (for example, if a resource + // has been deleted and purged from the system, then a retry of the original creation request + // may be rejected). + OpcRetryToken *string `mandatory:"false" contributesTo:"header" name:"opc-retry-token"` + + // Unique identifier for the request. + OpcRequestId *string `mandatory:"false" contributesTo:"header" name:"opc-request-id"` + + // Metadata about the request. This information will not be transmitted to the service, but + // represents information that the SDK will consume to drive retry behavior. + RequestMetadata common.RequestMetadata +} + +func (request CreateAutonomousDatabaseCloneRequest) String() string { + return common.PointerString(request) +} + +// HTTPRequest implements the OCIRequest interface +func (request CreateAutonomousDatabaseCloneRequest) HTTPRequest(method, path string, binaryRequestBody *common.OCIReadSeekCloser, extraHeaders map[string]string) (http.Request, error) { + + _, err := request.ValidateEnumValue() + if err != nil { + return http.Request{}, err + } + return common.MakeDefaultHTTPRequestWithTaggedStructAndExtraHeaders(method, path, request, extraHeaders) +} + +// BinaryRequestBody implements the OCIRequest interface +func (request CreateAutonomousDatabaseCloneRequest) BinaryRequestBody() (*common.OCIReadSeekCloser, bool) { + + return nil, false + +} + +// RetryPolicy implements the OCIRetryableRequest interface. This retrieves the specified retry policy. +func (request CreateAutonomousDatabaseCloneRequest) RetryPolicy() *common.RetryPolicy { + return request.RequestMetadata.RetryPolicy +} + +// ValidateEnumValue returns an error when providing an unsupported enum value +// This function is being called during constructing API request process +// Not recommended for calling this function directly +func (request CreateAutonomousDatabaseCloneRequest) ValidateEnumValue() (bool, error) { + errMessage := []string{} + if len(errMessage) > 0 { + return true, fmt.Errorf(strings.Join(errMessage, "\n")) + } + return false, nil +} + +// CreateAutonomousDatabaseResponse wrapper for the CreateAutonomousDatabase operation +type CreateAutonomousDatabaseCloneResponse struct { + + // The underlying http response + RawResponse *http.Response + + // The AutonomousDatabase instance + database.AutonomousDatabase `presentIn:"body"` + + // For optimistic concurrency control. See `if-match`. + Etag *string `presentIn:"header" name:"etag"` + + // Unique Oracle-assigned identifier for the request. If you need to contact Oracle about + // a particular request, please provide the request ID. + OpcRequestId *string `presentIn:"header" name:"opc-request-id"` + + // The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the work request. Multiple OCID values are returned in a comma-separated list. Use GetWorkRequest with a work request OCID to track the status of the request. + OpcWorkRequestId *string `presentIn:"header" name:"opc-work-request-id"` +} + +func (response CreateAutonomousDatabaseCloneResponse) String() string { + return common.PointerString(response) +} + +// HTTPResponse implements the OCIResponse interface +func (response CreateAutonomousDatabaseCloneResponse) HTTPResponse() *http.Response { + return response.RawResponse +} diff --git a/commons/oci/database.go b/commons/oci/database.go index 8e2fd2b1..1fa6c309 100644 --- a/commons/oci/database.go +++ b/commons/oci/database.go @@ -41,6 +41,7 @@ package oci import ( "context" "fmt" + "net/http" "github.com/go-logr/logr" "github.com/oracle/oci-go-sdk/v65/common" @@ -51,34 +52,7 @@ import ( "github.com/oracle/oracle-database-operator/commons/k8s" ) -type DatabaseService interface { - CreateAutonomousDatabase(adb *dbv4.AutonomousDatabase) (database.CreateAutonomousDatabaseResponse, error) - GetAutonomousDatabase(adbOCID string) (database.GetAutonomousDatabaseResponse, error) - UpdateAutonomousDatabaseGeneralFields(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateAutonomousDatabaseDBWorkload(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateAutonomousDatabaseLicenseModel(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateAutonomousDatabaseAdminPassword(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateAutonomousDatabaseScalingFields(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateNetworkAccessMTLSRequired(adbOCID string) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateNetworkAccessMTLS(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateNetworkAccessPublic(lastAccessType dbv4.NetworkAccessTypeEnum, adbOCID string) (resp database.UpdateAutonomousDatabaseResponse, err error) - UpdateNetworkAccess(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) - StartAutonomousDatabase(adbOCID string) (database.StartAutonomousDatabaseResponse, error) - StopAutonomousDatabase(adbOCID string) (database.StopAutonomousDatabaseResponse, error) - DeleteAutonomousDatabase(adbOCID string) (database.DeleteAutonomousDatabaseResponse, error) - DownloadWallet(adb *dbv4.AutonomousDatabase) (database.GenerateAutonomousDatabaseWalletResponse, error) - RestoreAutonomousDatabase(adbOCID string, sdkTime common.SDKTime) (database.RestoreAutonomousDatabaseResponse, error) - ListAutonomousDatabaseBackups(adbOCID string) (database.ListAutonomousDatabaseBackupsResponse, error) - CreateAutonomousDatabaseBackup(adbBackup *dbv4.AutonomousDatabaseBackup, adbOCID string) (database.CreateAutonomousDatabaseBackupResponse, error) - GetAutonomousDatabaseBackup(backupOCID string) (database.GetAutonomousDatabaseBackupResponse, error) - CreateAutonomousContainerDatabase(acd *dbv4.AutonomousContainerDatabase) (database.CreateAutonomousContainerDatabaseResponse, error) - GetAutonomousContainerDatabase(acdOCID string) (database.GetAutonomousContainerDatabaseResponse, error) - UpdateAutonomousContainerDatabase(acdOCID string, difACD *dbv4.AutonomousContainerDatabase) (database.UpdateAutonomousContainerDatabaseResponse, error) - RestartAutonomousContainerDatabase(acdOCID string) (database.RestartAutonomousContainerDatabaseResponse, error) - TerminateAutonomousContainerDatabase(acdOCID string) (database.TerminateAutonomousContainerDatabaseResponse, error) -} - -type databaseService struct { +type DatabaseService struct { logger logr.Logger kubeClient client.Client dbClient database.DatabaseClient @@ -88,19 +62,19 @@ type databaseService struct { func NewDatabaseService( logger logr.Logger, kubeClient client.Client, - provider common.ConfigurationProvider) (DatabaseService, error) { + provider common.ConfigurationProvider) (databaseService DatabaseService, err error) { dbClient, err := database.NewDatabaseClientWithConfigurationProvider(provider) if err != nil { - return nil, err + return databaseService, err } vaultService, err := NewVaultService(logger, provider) if err != nil { - return nil, err + return databaseService, err } - return &databaseService{ + return DatabaseService{ logger: logger.WithName("dbService"), kubeClient: kubeClient, dbClient: dbClient, @@ -114,7 +88,7 @@ func NewDatabaseService( // ReadPassword reads the password from passwordSpec, and returns the pointer to the read password string. // The function returns a nil if nothing is read -func (d *databaseService) readPassword(namespace string, passwordSpec dbv4.PasswordSpec) (*string, error) { +func (d *DatabaseService) readPassword(namespace string, passwordSpec dbv4.PasswordSpec) (*string, error) { logger := d.logger.WithName("readPassword") if passwordSpec.K8sSecret.Name != nil { @@ -129,10 +103,10 @@ func (d *databaseService) readPassword(namespace string, passwordSpec dbv4.Passw return common.String(password), nil } - if passwordSpec.OCISecret.OCID != nil { - logger.Info(fmt.Sprintf("Getting password from OCI Vault Secret OCID %s", *passwordSpec.OCISecret.OCID)) + if passwordSpec.OciSecret.Id != nil { + logger.Info(fmt.Sprintf("Getting password from OCI Vault Secret OCID %s", *passwordSpec.OciSecret.Id)) - password, err := d.vaultService.GetSecretValue(*passwordSpec.OCISecret.OCID) + password, err := d.vaultService.GetSecretValue(*passwordSpec.OciSecret.Id) if err != nil { return nil, err } @@ -142,14 +116,14 @@ func (d *databaseService) readPassword(namespace string, passwordSpec dbv4.Passw return nil, nil } -func (d *databaseService) readACD_OCID(acd *dbv4.ACDSpec, namespace string) (*string, error) { - if acd.OCIACD.OCID != nil { - return acd.OCIACD.OCID, nil +func (d *DatabaseService) readACD_OCID(acd *dbv4.AcdSpec, namespace string) (*string, error) { + if acd.OciAcd.Id != nil { + return acd.OciAcd.Id, nil } - if acd.K8sACD.Name != nil { + if acd.K8sAcd.Name != nil { fetchedACD := &dbv4.AutonomousContainerDatabase{} - if err := k8s.FetchResource(d.kubeClient, namespace, *acd.K8sACD.Name, fetchedACD); err != nil { + if err := k8s.FetchResource(d.kubeClient, namespace, *acd.K8sAcd.Name, fetchedACD); err != nil { return nil, err } @@ -160,7 +134,7 @@ func (d *databaseService) readACD_OCID(acd *dbv4.ACDSpec, namespace string) (*st } // CreateAutonomousDatabase sends a request to OCI to provision a database and returns the AutonomousDatabase OCID. -func (d *databaseService) CreateAutonomousDatabase(adb *dbv4.AutonomousDatabase) (resp database.CreateAutonomousDatabaseResponse, err error) { +func (d *DatabaseService) CreateAutonomousDatabase(adb *dbv4.AutonomousDatabase) (resp database.CreateAutonomousDatabaseResponse, err error) { adminPassword, err := d.readPassword(adb.Namespace, adb.Spec.Details.AdminPassword) if err != nil { return resp, err @@ -172,9 +146,12 @@ func (d *databaseService) CreateAutonomousDatabase(adb *dbv4.AutonomousDatabase) } createAutonomousDatabaseDetails := database.CreateAutonomousDatabaseDetails{ - CompartmentId: adb.Spec.Details.CompartmentOCID, + CompartmentId: adb.Spec.Details.CompartmentId, DbName: adb.Spec.Details.DbName, - CpuCoreCount: adb.Spec.Details.CPUCoreCount, + CpuCoreCount: adb.Spec.Details.CpuCoreCount, + ComputeModel: database.CreateAutonomousDatabaseBaseComputeModelEnum(adb.Spec.Details.ComputeModel), + ComputeCount: adb.Spec.Details.ComputeCount, + OcpuCount: adb.Spec.Details.OcpuCount, DataStorageSizeInTBs: adb.Spec.Details.DataStorageSizeInTBs, AdminPassword: adminPassword, DisplayName: adb.Spec.Details.DisplayName, @@ -182,21 +159,26 @@ func (d *databaseService) CreateAutonomousDatabase(adb *dbv4.AutonomousDatabase) IsDedicated: adb.Spec.Details.IsDedicated, AutonomousContainerDatabaseId: acdOCID, DbVersion: adb.Spec.Details.DbVersion, - DbWorkload: database.CreateAutonomousDatabaseBaseDbWorkloadEnum( - adb.Spec.Details.DbWorkload), - LicenseModel: database.CreateAutonomousDatabaseBaseLicenseModelEnum(adb.Spec.Details.LicenseModel), - IsAccessControlEnabled: adb.Spec.Details.NetworkAccess.IsAccessControlEnabled, - WhitelistedIps: adb.Spec.Details.NetworkAccess.AccessControlList, - IsMtlsConnectionRequired: adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired, - SubnetId: adb.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID, - NsgIds: adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs, - PrivateEndpointLabel: adb.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix, + DbWorkload: database.CreateAutonomousDatabaseBaseDbWorkloadEnum(adb.Spec.Details.DbWorkload), + LicenseModel: database.CreateAutonomousDatabaseBaseLicenseModelEnum(adb.Spec.Details.LicenseModel), + IsFreeTier: adb.Spec.Details.IsFreeTier, + IsAccessControlEnabled: adb.Spec.Details.IsAccessControlEnabled, + WhitelistedIps: adb.Spec.Details.WhitelistedIps, + IsMtlsConnectionRequired: adb.Spec.Details.IsMtlsConnectionRequired, + SubnetId: adb.Spec.Details.SubnetId, + NsgIds: adb.Spec.Details.NsgIds, + PrivateEndpointLabel: adb.Spec.Details.PrivateEndpointLabel, FreeformTags: adb.Spec.Details.FreeformTags, } + retryPolicy := common.DefaultRetryPolicy() + createAutonomousDatabaseRequest := database.CreateAutonomousDatabaseRequest{ CreateAutonomousDatabaseDetails: createAutonomousDatabaseDetails, + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, } resp, err = d.dbClient.CreateAutonomousDatabase(context.TODO(), createAutonomousDatabaseRequest) @@ -207,166 +189,116 @@ func (d *databaseService) CreateAutonomousDatabase(adb *dbv4.AutonomousDatabase) return resp, nil } -func (d *databaseService) GetAutonomousDatabase(adbOCID string) (database.GetAutonomousDatabaseResponse, error) { - getAutonomousDatabaseRequest := database.GetAutonomousDatabaseRequest{ - AutonomousDatabaseId: common.String(adbOCID), - } - - return d.dbClient.GetAutonomousDatabase(context.TODO(), getAutonomousDatabaseRequest) -} +func (d *DatabaseService) GetAutonomousDatabase(adbOCID string) (database.GetAutonomousDatabaseResponse, error) { + retryPolicy := common.DefaultRetryPolicy() -func (d *databaseService) UpdateAutonomousDatabaseGeneralFields(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - AutonomousDatabaseId: common.String(adbOCID), - UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ - DisplayName: difADB.Spec.Details.DisplayName, - DbName: difADB.Spec.Details.DbName, - DbVersion: difADB.Spec.Details.DbVersion, - FreeformTags: difADB.Spec.Details.FreeformTags, - }, - } - return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) -} - -func (d *databaseService) UpdateAutonomousDatabaseDBWorkload(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ + getAutonomousDatabaseRequest := database.GetAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), - UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ - DbWorkload: database.UpdateAutonomousDatabaseDetailsDbWorkloadEnum(difADB.Spec.Details.DbWorkload), + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, }, } - return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) -} -func (d *databaseService) UpdateAutonomousDatabaseLicenseModel(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - AutonomousDatabaseId: common.String(adbOCID), - UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ - LicenseModel: database.UpdateAutonomousDatabaseDetailsLicenseModelEnum(difADB.Spec.Details.LicenseModel), - }, - } - return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) + return d.dbClient.GetAutonomousDatabase(context.TODO(), getAutonomousDatabaseRequest) } -func (d *databaseService) UpdateAutonomousDatabaseAdminPassword(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - adminPassword, err := d.readPassword(difADB.Namespace, difADB.Spec.Details.AdminPassword) +func (d *DatabaseService) UpdateAutonomousDatabase(adbOCID string, adb *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { + // Retrieve admin password + adminPassword, err := d.readPassword(adb.Namespace, adb.Spec.Details.AdminPassword) if err != nil { return resp, err } - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - AutonomousDatabaseId: common.String(adbOCID), - UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ - AdminPassword: adminPassword, - }, - } - return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) -} + retryPolicy := common.DefaultRetryPolicy() -func (d *databaseService) UpdateAutonomousDatabaseScalingFields(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ - DataStorageSizeInTBs: difADB.Spec.Details.DataStorageSizeInTBs, - CpuCoreCount: difADB.Spec.Details.CPUCoreCount, - IsAutoScalingEnabled: difADB.Spec.Details.IsAutoScalingEnabled, + DisplayName: adb.Spec.Details.DisplayName, + DbName: adb.Spec.Details.DbName, + DbVersion: adb.Spec.Details.DbVersion, + FreeformTags: adb.Spec.Details.FreeformTags, + DbWorkload: database.UpdateAutonomousDatabaseDetailsDbWorkloadEnum(adb.Spec.Details.DbWorkload), + LicenseModel: database.UpdateAutonomousDatabaseDetailsLicenseModelEnum(adb.Spec.Details.LicenseModel), + AdminPassword: adminPassword, + DataStorageSizeInTBs: adb.Spec.Details.DataStorageSizeInTBs, + CpuCoreCount: adb.Spec.Details.CpuCoreCount, + ComputeModel: database.UpdateAutonomousDatabaseDetailsComputeModelEnum(adb.Spec.Details.ComputeModel), + ComputeCount: adb.Spec.Details.ComputeCount, + OcpuCount: adb.Spec.Details.OcpuCount, + IsAutoScalingEnabled: adb.Spec.Details.IsAutoScalingEnabled, + IsFreeTier: adb.Spec.Details.IsFreeTier, + IsMtlsConnectionRequired: adb.Spec.Details.IsMtlsConnectionRequired, + IsAccessControlEnabled: adb.Spec.Details.IsAccessControlEnabled, + WhitelistedIps: adb.Spec.Details.WhitelistedIps, + SubnetId: adb.Spec.Details.SubnetId, + NsgIds: adb.Spec.Details.NsgIds, + PrivateEndpointLabel: adb.Spec.Details.PrivateEndpointLabel, }, - } - return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) -} - -func (d *databaseService) UpdateNetworkAccessMTLSRequired(adbOCID string) (resp database.UpdateAutonomousDatabaseResponse, err error) { - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - AutonomousDatabaseId: common.String(adbOCID), - UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ - IsMtlsConnectionRequired: common.Bool(true), + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, }, } return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -func (d *databaseService) UpdateNetworkAccessMTLS(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - AutonomousDatabaseId: common.String(adbOCID), - UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ - IsMtlsConnectionRequired: difADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired, - }, - } - return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) -} - -func (d *databaseService) UpdateNetworkAccessPublic( - lastAccessType dbv4.NetworkAccessTypeEnum, - adbOCID string) (resp database.UpdateAutonomousDatabaseResponse, err error) { +func (d *DatabaseService) StartAutonomousDatabase(adbOCID string) (database.StartAutonomousDatabaseResponse, error) { + retryPolicy := common.DefaultRetryPolicy() - updateAutonomousDatabaseDetails := database.UpdateAutonomousDatabaseDetails{} - - if lastAccessType == dbv4.NetworkAccessTypeRestricted { - updateAutonomousDatabaseDetails.WhitelistedIps = []string{""} - } else if lastAccessType == dbv4.NetworkAccessTypePrivate { - updateAutonomousDatabaseDetails.PrivateEndpointLabel = common.String("") - } - - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - AutonomousDatabaseId: common.String(adbOCID), - UpdateAutonomousDatabaseDetails: updateAutonomousDatabaseDetails, - } - - return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) -} - -func (d *databaseService) UpdateNetworkAccess(adbOCID string, difADB *dbv4.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - AutonomousDatabaseId: common.String(adbOCID), - UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ - IsAccessControlEnabled: difADB.Spec.Details.NetworkAccess.IsAccessControlEnabled, - WhitelistedIps: difADB.Spec.Details.NetworkAccess.AccessControlList, - SubnetId: difADB.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID, - NsgIds: difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs, - PrivateEndpointLabel: difADB.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix, - }, - } - - return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) -} - -func (d *databaseService) StartAutonomousDatabase(adbOCID string) (database.StartAutonomousDatabaseResponse, error) { startRequest := database.StartAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, } return d.dbClient.StartAutonomousDatabase(context.TODO(), startRequest) } -func (d *databaseService) StopAutonomousDatabase(adbOCID string) (database.StopAutonomousDatabaseResponse, error) { +func (d *DatabaseService) StopAutonomousDatabase(adbOCID string) (database.StopAutonomousDatabaseResponse, error) { + retryPolicy := common.DefaultRetryPolicy() + stopRequest := database.StopAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, } return d.dbClient.StopAutonomousDatabase(context.TODO(), stopRequest) } -func (d *databaseService) DeleteAutonomousDatabase(adbOCID string) (database.DeleteAutonomousDatabaseResponse, error) { +func (d *DatabaseService) DeleteAutonomousDatabase(adbOCID string) (database.DeleteAutonomousDatabaseResponse, error) { + retryPolicy := common.DefaultRetryPolicy() + deleteRequest := database.DeleteAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, } return d.dbClient.DeleteAutonomousDatabase(context.TODO(), deleteRequest) } -func (d *databaseService) DownloadWallet(adb *dbv4.AutonomousDatabase) (resp database.GenerateAutonomousDatabaseWalletResponse, err error) { +func (d *DatabaseService) DownloadWallet(adb *dbv4.AutonomousDatabase) (resp database.GenerateAutonomousDatabaseWalletResponse, err error) { // Prepare wallet password - walletPassword, err := d.readPassword(adb.Namespace, adb.Spec.Details.Wallet.Password) + walletPassword, err := d.readPassword(adb.Namespace, adb.Spec.Wallet.Password) if err != nil { return resp, err } + retryPolicy := common.DefaultRetryPolicy() + // Download a Wallet req := database.GenerateAutonomousDatabaseWalletRequest{ - AutonomousDatabaseId: adb.Spec.Details.AutonomousDatabaseOCID, + AutonomousDatabaseId: adb.Spec.Details.Id, GenerateAutonomousDatabaseWalletDetails: database.GenerateAutonomousDatabaseWalletDetails{ Password: walletPassword, }, + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, } // Send the request using the service client @@ -382,12 +314,17 @@ func (d *databaseService) DownloadWallet(adb *dbv4.AutonomousDatabase) (resp dat * Autonomous Database Restore *******************************/ -func (d *databaseService) RestoreAutonomousDatabase(adbOCID string, sdkTime common.SDKTime) (database.RestoreAutonomousDatabaseResponse, error) { +func (d *DatabaseService) RestoreAutonomousDatabase(adbOCID string, sdkTime common.SDKTime) (database.RestoreAutonomousDatabaseResponse, error) { + retryPolicy := common.DefaultRetryPolicy() + request := database.RestoreAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), RestoreAutonomousDatabaseDetails: database.RestoreAutonomousDatabaseDetails{ Timestamp: &sdkTime, }, + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, } return d.dbClient.RestoreAutonomousDatabase(context.TODO(), request) } @@ -396,21 +333,31 @@ func (d *databaseService) RestoreAutonomousDatabase(adbOCID string, sdkTime comm * Autonomous Database Backup *******************************/ -func (d *databaseService) ListAutonomousDatabaseBackups(adbOCID string) (database.ListAutonomousDatabaseBackupsResponse, error) { +func (d *DatabaseService) ListAutonomousDatabaseBackups(adbOCID string) (database.ListAutonomousDatabaseBackupsResponse, error) { + retryPolicy := common.DefaultRetryPolicy() + listBackupRequest := database.ListAutonomousDatabaseBackupsRequest{ AutonomousDatabaseId: common.String(adbOCID), + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, } return d.dbClient.ListAutonomousDatabaseBackups(context.TODO(), listBackupRequest) } -func (d *databaseService) CreateAutonomousDatabaseBackup(adbBackup *dbv4.AutonomousDatabaseBackup, adbOCID string) (database.CreateAutonomousDatabaseBackupResponse, error) { +func (d *DatabaseService) CreateAutonomousDatabaseBackup(adbBackup *dbv4.AutonomousDatabaseBackup, adbOCID string) (database.CreateAutonomousDatabaseBackupResponse, error) { + retryPolicy := common.DefaultRetryPolicy() + createBackupRequest := database.CreateAutonomousDatabaseBackupRequest{ CreateAutonomousDatabaseBackupDetails: database.CreateAutonomousDatabaseBackupDetails{ AutonomousDatabaseId: common.String(adbOCID), IsLongTermBackup: adbBackup.Spec.IsLongTermBackup, RetentionPeriodInDays: adbBackup.Spec.RetentionPeriodInDays, }, + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, } // Use the spec.displayName as the displayName of the backup if is provided, @@ -424,10 +371,117 @@ func (d *databaseService) CreateAutonomousDatabaseBackup(adbBackup *dbv4.Autonom return d.dbClient.CreateAutonomousDatabaseBackup(context.TODO(), createBackupRequest) } -func (d *databaseService) GetAutonomousDatabaseBackup(backupOCID string) (database.GetAutonomousDatabaseBackupResponse, error) { +func (d *DatabaseService) GetAutonomousDatabaseBackup(backupOCID string) (database.GetAutonomousDatabaseBackupResponse, error) { + retryPolicy := common.DefaultRetryPolicy() + getBackupRequest := database.GetAutonomousDatabaseBackupRequest{ AutonomousDatabaseBackupId: common.String(backupOCID), + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, } return d.dbClient.GetAutonomousDatabaseBackup(context.TODO(), getBackupRequest) } + +func (d *DatabaseService) CreateAutonomousDatabaseClone(adb *dbv4.AutonomousDatabase) (resp CreateAutonomousDatabaseCloneResponse, err error) { + adminPassword, err := d.readPassword(adb.Namespace, adb.Spec.Clone.AdminPassword) + if err != nil { + return resp, err + } + + acdOCID, err := d.readACD_OCID(&adb.Spec.Clone.AutonomousContainerDatabase, adb.Namespace) + if err != nil { + return resp, err + } + + retryPolicy := common.DefaultRetryPolicy() + + request := CreateAutonomousDatabaseCloneRequest{ + CreateAutonomousDatabaseCloneDetails: database.CreateAutonomousDatabaseCloneDetails{ + CompartmentId: adb.Spec.Clone.CompartmentId, + SourceId: adb.Spec.Details.Id, + AutonomousContainerDatabaseId: acdOCID, + DisplayName: adb.Spec.Clone.DisplayName, + DbName: adb.Spec.Clone.DbName, + DbWorkload: database.CreateAutonomousDatabaseBaseDbWorkloadEnum(adb.Spec.Clone.DbWorkload), + LicenseModel: database.CreateAutonomousDatabaseBaseLicenseModelEnum(adb.Spec.Clone.LicenseModel), + DbVersion: adb.Spec.Clone.DbVersion, + DataStorageSizeInTBs: adb.Spec.Clone.DataStorageSizeInTBs, + CpuCoreCount: adb.Spec.Clone.CpuCoreCount, + ComputeModel: database.CreateAutonomousDatabaseBaseComputeModelEnum(adb.Spec.Clone.ComputeModel), + ComputeCount: adb.Spec.Clone.ComputeCount, + OcpuCount: adb.Spec.Clone.OcpuCount, + AdminPassword: adminPassword, + IsAutoScalingEnabled: adb.Spec.Clone.IsAutoScalingEnabled, + IsDedicated: adb.Spec.Clone.IsDedicated, + IsFreeTier: adb.Spec.Clone.IsFreeTier, + IsAccessControlEnabled: adb.Spec.Clone.IsAccessControlEnabled, + WhitelistedIps: adb.Spec.Clone.WhitelistedIps, + SubnetId: adb.Spec.Clone.SubnetId, + NsgIds: adb.Spec.Clone.NsgIds, + PrivateEndpointLabel: adb.Spec.Clone.PrivateEndpointLabel, + IsMtlsConnectionRequired: adb.Spec.Clone.IsMtlsConnectionRequired, + FreeformTags: adb.Spec.Clone.FreeformTags, + CloneType: adb.Spec.Clone.CloneType, + }, + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, + } + + return d.createAutonomousDatabaseClone(context.TODO(), request) +} + +func (d *DatabaseService) createAutonomousDatabaseClone(ctx context.Context, request CreateAutonomousDatabaseCloneRequest) (response CreateAutonomousDatabaseCloneResponse, err error) { + var ociResponse common.OCIResponse + policy := common.NoRetryPolicy() + if request.RetryPolicy() != nil { + policy = *request.RetryPolicy() + } + + if !(request.OpcRetryToken != nil && *request.OpcRetryToken != "") { + request.OpcRetryToken = common.String(common.RetryToken()) + } + + ociResponse, err = common.Retry(ctx, request, d.sencCreateAutonomousDatabaseClone, policy) + if err != nil { + if ociResponse != nil { + if httpResponse := ociResponse.HTTPResponse(); httpResponse != nil { + opcRequestId := httpResponse.Header.Get("opc-request-id") + response = CreateAutonomousDatabaseCloneResponse{RawResponse: httpResponse, OpcRequestId: &opcRequestId} + } else { + response = CreateAutonomousDatabaseCloneResponse{} + } + } + return + } + if convertedResponse, ok := ociResponse.(CreateAutonomousDatabaseCloneResponse); ok { + response = convertedResponse + } else { + err = fmt.Errorf("failed to convert OCIResponse into CreateAutonomousDatabaseResponse") + } + return +} + +func (d *DatabaseService) sencCreateAutonomousDatabaseClone(ctx context.Context, request common.OCIRequest, binaryReqBody *common.OCIReadSeekCloser, extraHeaders map[string]string) (common.OCIResponse, error) { + + httpRequest, err := request.HTTPRequest(http.MethodPost, "/autonomousDatabases", binaryReqBody, extraHeaders) + if err != nil { + return nil, err + } + + var response CreateAutonomousDatabaseCloneResponse + var httpResponse *http.Response + httpResponse, err = d.dbClient.Call(ctx, &httpRequest) + defer common.CloseBodyIfValid(httpResponse) + response.RawResponse = httpResponse + if err != nil { + apiReferenceLink := "https://docs.oracle.com/iaas/api/#/en/database/20160918/AutonomousDatabase/CreateAutonomousDatabase" + err = common.PostProcessServiceError(err, "Database", "CreateAutonomousDatabase", apiReferenceLink) + return response, err + } + + err = common.UnmarshalResponse(httpResponse, &response) + return response, err +} diff --git a/commons/oci/provider.go b/commons/oci/provider.go index 152f1efd..f466f226 100644 --- a/commons/oci/provider.go +++ b/commons/oci/provider.go @@ -60,13 +60,13 @@ const ( privatekeyKey = "privatekey" ) -type APIKeyAuth struct { +type ApiKeyAuth struct { ConfigMapName *string SecretName *string Namespace string } -func GetOCIProvider(kubeClient client.Client, authData APIKeyAuth) (common.ConfigurationProvider, error) { +func GetOciProvider(kubeClient client.Client, authData ApiKeyAuth) (common.ConfigurationProvider, error) { if authData.ConfigMapName != nil && authData.SecretName == nil { return getWorkloadIdentityProvider(kubeClient, authData) } else if authData.ConfigMapName != nil && authData.SecretName != nil { @@ -84,7 +84,7 @@ func GetOCIProvider(kubeClient client.Client, authData APIKeyAuth) (common.Confi } } -func getWorkloadIdentityProvider(kubeClient client.Client, authData APIKeyAuth) (common.ConfigurationProvider, error) { +func getWorkloadIdentityProvider(kubeClient client.Client, authData ApiKeyAuth) (common.ConfigurationProvider, error) { ociConfigMap, err := k8s.FetchConfigMap(kubeClient, authData.Namespace, *authData.ConfigMapName) if err != nil { return nil, err @@ -108,7 +108,7 @@ func getWorkloadIdentityProvider(kubeClient client.Client, authData APIKeyAuth) return auth.OkeWorkloadIdentityConfigurationProvider() } -func getProviderWithAPIKey(kubeClient client.Client, authData APIKeyAuth) (common.ConfigurationProvider, error) { +func getProviderWithAPIKey(kubeClient client.Client, authData ApiKeyAuth) (common.ConfigurationProvider, error) { var region, fingerprint, user, tenancy, passphrase, privatekeyValue string // Prepare ConfigMap diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index cb22180a..435eb552 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -54,7 +54,18 @@ spec: type: object spec: properties: - details: + action: + enum: + - "" + - Create + - Sync + - Update + - Stop + - Start + - Terminate + - Clone + type: string + clone: properties: adminPassword: properties: @@ -65,26 +76,36 @@ spec: type: object ociSecret: properties: - ocid: + id: type: string type: object type: object autonomousContainerDatabase: properties: - k8sACD: + k8sAcd: properties: name: type: string type: object - ociACD: + ociAcd: properties: - ocid: + id: type: string type: object type: object - autonomousDatabaseOCID: + cloneType: + enum: + - FULL + - METADATA type: string - compartmentOCID: + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU type: string cpuCoreCount: type: integer @@ -107,64 +128,125 @@ spec: additionalProperties: type: string type: object + isAccessControlEnabled: + type: boolean isAutoScalingEnabled: type: boolean isDedicated: type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean licenseModel: enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string - lifecycleState: + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: type: string - networkAccess: + subnetId: + type: string + whitelistedIps: + items: + type: string + type: array + type: object + details: + properties: + adminPassword: properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: + k8sSecret: properties: - hostnamePrefix: + name: type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: + type: object + ociSecret: + properties: + id: type: string type: object type: object - wallet: + autonomousContainerDatabase: properties: - name: - type: string - password: + k8sAcd: properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object + name: + type: string + type: object + ociAcd: + properties: + id: + type: string type: object type: object + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + id: + type: string + isAccessControlEnabled: + type: boolean + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: + type: string + whitelistedIps: + items: + type: string + type: array type: object hardLink: default: false @@ -176,11 +258,33 @@ spec: secretName: type: string type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + type: object required: + - action + - clone - details type: object status: properties: + action: + type: string allConnectionStrings: items: properties: @@ -287,7 +391,18 @@ spec: type: object spec: properties: - details: + action: + enum: + - "" + - Create + - Sync + - Update + - Stop + - Start + - Terminate + - Clone + type: string + clone: properties: adminPassword: properties: @@ -298,26 +413,31 @@ spec: type: object ociSecret: properties: - ocid: + id: type: string type: object type: object autonomousContainerDatabase: properties: - k8sACD: + k8sAcd: properties: name: type: string type: object - ociACD: + ociAcd: properties: - ocid: + id: type: string type: object type: object - autonomousDatabaseOCID: + compartmentId: type: string - compartmentOCID: + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU type: string cpuCoreCount: type: integer @@ -340,64 +460,129 @@ spec: additionalProperties: type: string type: object + id: + type: string + isAccessControlEnabled: + type: boolean isAutoScalingEnabled: type: boolean isDedicated: type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean licenseModel: enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string - lifecycleState: + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: type: string - networkAccess: + whitelistedIps: + items: + type: string + type: array + required: + - id + type: object + details: + properties: + adminPassword: properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: + k8sSecret: properties: - hostnamePrefix: + name: type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: + type: object + ociSecret: + properties: + id: type: string type: object type: object - wallet: + autonomousContainerDatabase: properties: - name: - type: string - password: + k8sAcd: properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object + name: + type: string + type: object + ociAcd: + properties: + id: + type: string type: object type: object + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + id: + type: string + isAccessControlEnabled: + type: boolean + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: + type: string + whitelistedIps: + items: + type: string + type: array type: object hardLink: default: false @@ -409,11 +594,33 @@ spec: secretName: type: string type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + type: object required: + - action + - clone - details type: object status: properties: + action: + type: string allConnectionStrings: items: properties: diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 3c219e67..12f4ca91 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -24,26 +24,6 @@ webhooks: resources: - autonomousdatabasebackups sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v4-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabasev4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -188,26 +168,6 @@ webhooks: resources: - autonomousdatabasebackups sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabasev1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -397,26 +357,6 @@ webhooks: resources: - autonomousdatabaserestores sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /validate-database-oracle-com-v4-autonomousdatabase - failurePolicy: Fail - name: vautonomousdatabasev4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/controllers/database/autonomouscontainerdatabase_controller.go b/controllers/database/autonomouscontainerdatabase_controller.go index 712d2399..73830eee 100644 --- a/controllers/database/autonomouscontainerdatabase_controller.go +++ b/controllers/database/autonomouscontainerdatabase_controller.go @@ -235,13 +235,13 @@ func (r *AutonomousContainerDatabaseReconciler) Reconcile(ctx context.Context, r func (r *AutonomousContainerDatabaseReconciler) setupOCIClients(logger logr.Logger, acd *dbv4.AutonomousContainerDatabase) error { var err error - authData := oci.APIKeyAuth{ + authData := oci.ApiKeyAuth{ ConfigMapName: acd.Spec.OCIConfig.ConfigMapName, SecretName: acd.Spec.OCIConfig.SecretName, Namespace: acd.GetNamespace(), } - provider, err := oci.GetOCIProvider(r.KubeClient, authData) + provider, err := oci.GetOciProvider(r.KubeClient, authData) if err != nil { return err } diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index d6b798ab..85a801fc 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -40,7 +40,6 @@ package controllers import ( "context" - "encoding/json" "errors" "fmt" "reflect" @@ -49,7 +48,6 @@ import ( "time" "github.com/go-logr/logr" - "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" apiErrors "k8s.io/apimachinery/pkg/api/errors" @@ -69,11 +67,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" - "github.com/oracle/oracle-database-operator/commons/annotations" "github.com/oracle/oracle-database-operator/commons/k8s" "github.com/oracle/oracle-database-operator/commons/oci" ) +// name of our custom finalizer +const ADB_FINALIZER = "database.oracle.com/adb-finalizer" + var requeueResult ctrl.Result = ctrl.Result{Requeue: true, RequeueAfter: 15 * time.Second} var emptyResult ctrl.Result = ctrl.Result{} @@ -150,21 +150,17 @@ func (r *AutonomousDatabaseReconciler) eventFilterPredicate() predicate.Predicat return predicate.Funcs{ UpdateFunc: func(e event.UpdateEvent) bool { // source object can be AutonomousDatabase, AutonomousDatabaseBackup, or AutonomousDatabaseRestore - desiredADB, adbOk := e.ObjectNew.(*dbv4.AutonomousDatabase) + desiredAdb, adbOk := e.ObjectNew.(*dbv4.AutonomousDatabase) if adbOk { - oldADB := e.ObjectOld.(*dbv4.AutonomousDatabase) - - specChanged := !reflect.DeepEqual(oldADB.Spec, desiredADB.Spec) - statusChanged := !reflect.DeepEqual(oldADB.Status, desiredADB.Status) + oldAdb := e.ObjectOld.(*dbv4.AutonomousDatabase) - oldLastSucSpec := oldADB.GetAnnotations()[dbv4.LastSuccessfulSpec] - desiredLastSucSpec := desiredADB.GetAnnotations()[dbv4.LastSuccessfulSpec] - lastSucSpecChanged := oldLastSucSpec != desiredLastSucSpec + specChanged := !reflect.DeepEqual(oldAdb.Spec, desiredAdb.Spec) + statusChanged := !reflect.DeepEqual(oldAdb.Status, desiredAdb.Status) - if (!specChanged && statusChanged) || lastSucSpecChanged || - (controllerutil.ContainsFinalizer(oldADB, dbv4.ADB_FINALIZER) != controllerutil.ContainsFinalizer(desiredADB, dbv4.ADB_FINALIZER)) { + if (!specChanged && statusChanged) || + (controllerutil.ContainsFinalizer(oldAdb, ADB_FINALIZER) != controllerutil.ContainsFinalizer(desiredAdb, ADB_FINALIZER)) { // Don't enqueue in the folowing condition: - // 1. only status changes 2. lastSucSpec changes 3. ADB_FINALIZER changes + // 1. only status changes 2. ADB_FINALIZER changes return false } @@ -189,142 +185,168 @@ func (r *AutonomousDatabaseReconciler) eventFilterPredicate() predicate.Predicat // +kubebuilder:rbac:groups="",resources=configmaps;secrets,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=events,verbs=create;patch -// Reconcile is the funtion that the operator calls every time when the reconciliation loop is triggered. -// It go to the beggining of the reconcile if an error is returned. We won't return a error if it is related -// to OCI, because the issues cannot be solved by re-run the reconcile. func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := r.Log.WithValues("Namespace/Name", req.NamespacedName) var err error - var ociADB *dbv4.AutonomousDatabase + // Indicates whether spec has been changed at the end of the reconcile. + var specChanged bool = false // Get the autonomousdatabase instance from the cluster - desiredADB := &dbv4.AutonomousDatabase{} - if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, desiredADB); err != nil { + desiredAdb := &dbv4.AutonomousDatabase{} + if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, desiredAdb); err != nil { // Ignore not-found errors, since they can't be fixed by an immediate requeue. - // No need to change the since we don't know if we obtain the object. if apiErrors.IsNotFound(err) { return emptyResult, nil } - // Failed to get ADB, so we don't need to update the status return emptyResult, err } /****************************************************************** * Get OCI database client ******************************************************************/ - if err := r.setupOCIClients(logger, desiredADB); err != nil { - logger.Error(err, "Fail to setup OCI clients") - - return r.manageError(logger.WithName("setupOCIClients"), desiredADB, err) + if err := r.setupOCIClients(logger, desiredAdb); err != nil { + return r.manageError( + logger.WithName("setupOCIClients"), + desiredAdb, + fmt.Errorf("Failed to get OCI Database Client: %w", err)) } logger.Info("OCI clients configured succesfully") /****************************************************************** - * Cleanup the resource if the resource is to be deleted. - * Deletion timestamp will be added to a object before it is deleted. - * Kubernetes server calls the clean up function if a finalizer exitsts, and won't delete the real object until - * all the finalizers are removed from the object metadata. - * Refer to this page for more details of using finalizers: https://kubernetes.io/blog/2022/05/14/using-finalizers-to-control-deletion/ + * Fill the empty fields in the local resource at the beginning of + * the reconciliation. ******************************************************************/ - exitReconcile, err := r.validateCleanup(logger, desiredADB) - if err != nil { - return r.manageError(logger.WithName("validateCleanup"), desiredADB, err) - } - - if exitReconcile { - return emptyResult, nil + // Fill the empty fields in the AutonomousDatabase resource by syncing up + // with the Autonomous Database in OCI. Only the fields that have nil + // values will be overwritten. + // This operation will be skipped if the AutonomousDatabaseOCID is nil. + // var specChangedAfterFirstSync bool + if _, err = r.syncAutonomousDatabase(logger, desiredAdb, false); err != nil { + return r.manageError( + logger.WithName("syncAutonomousDatabase"), + desiredAdb, + fmt.Errorf("Failed to sync AutonomousDatabase: %w", err)) } /****************************************************************** - * Register/unregister the finalizer + * Determine if the external resource needs to be cleaned up. + * If yes, delete the Autonomous Database in OCI and exits the + * reconcile function immediately. + * + * There is no need to check the other fields if the resource is + * under deletion. This method should be executed soon after the OCI + * database client is obtained and the local resource is synced in + * the above two steps. + * + * Kubernetes server calls the clean up function if a finalizer exitsts, + * and won't delete the object until all the finalizers are removed + * from the object metadata. ******************************************************************/ - exit, err := r.validateFinalizer(logger, desiredADB) - if err != nil { - return r.manageError(logger.WithName("validateFinalizer"), desiredADB, err) - } + if desiredAdb.GetDeletionTimestamp().IsZero() { + // The Autonomous Database is not being deleted. Update the finalizer. + if desiredAdb.Spec.HardLink != nil && + *desiredAdb.Spec.HardLink && + !controllerutil.ContainsFinalizer(desiredAdb, ADB_FINALIZER) { + + if err := k8s.AddFinalizerAndPatch(r.KubeClient, desiredAdb, ADB_FINALIZER); err != nil { + return emptyResult, fmt.Errorf("Failed to add finalizer to Autonomous Database "+desiredAdb.Name+": %w", err) + } + } else if desiredAdb.Spec.HardLink != nil && + !*desiredAdb.Spec.HardLink && + controllerutil.ContainsFinalizer(desiredAdb, ADB_FINALIZER) { - if exit { - return emptyResult, nil + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, desiredAdb, ADB_FINALIZER); err != nil { + return emptyResult, fmt.Errorf("Failed to remove finalizer to Autonomous Database "+desiredAdb.Name+": %w", err) + } + } + } else { + // The Autonomous Database is being deleted + if controllerutil.ContainsFinalizer(desiredAdb, ADB_FINALIZER) { + if dbv4.IsAdbIntermediateState(desiredAdb.Status.LifecycleState) { + // No-op + } else if desiredAdb.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated { + // The Autonomous Database in OCI has been deleted. Remove the finalizer. + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, desiredAdb, ADB_FINALIZER); err != nil { + return emptyResult, fmt.Errorf("Failed to remove finalizer to Autonomous Database "+desiredAdb.Name+": %w", err) + } + } else { + // Remove the Autonomous Database in OCI. + // Change the action to Terminate and proceed with the rest of the reconcile logic + desiredAdb.Spec.Action = "Terminate" + } + } } - /****************************************************************** - * Validate operations - ******************************************************************/ - modifiedADB := desiredADB.DeepCopy() // the ADB which stores the changes - exitReconcile, result, err := r.validateOperation(logger, modifiedADB, ociADB) - if err != nil { - return r.manageError(logger.WithName("validateOperation"), modifiedADB, err) - } - if exitReconcile { - return result, nil - } + if !dbv4.IsAdbIntermediateState(desiredAdb.Status.LifecycleState) { + /****************************************************************** + * Perform operations + ******************************************************************/ + var specChangedAfterOperation bool + specChangedAfterOperation, err = r.performOperation(logger, desiredAdb) + if err != nil { + return r.manageError( + logger.WithName("performOperation"), + desiredAdb, + fmt.Errorf("Failed to operate database action: %w", err)) + } - /***************************************************** - * Sync AutonomousDatabase Backups from OCI - *****************************************************/ - if err := r.syncBackupResources(logger, modifiedADB); err != nil { - return r.manageError(logger.WithName("syncBackupResources"), modifiedADB, err) - } + if specChangedAfterOperation { + specChanged = true + } - /***************************************************** - * Validate Wallet - *****************************************************/ - if err := r.validateWallet(logger, modifiedADB); err != nil { - return r.manageError(logger.WithName("validateWallet"), modifiedADB, err) + /***************************************************** + * Sync AutonomousDatabase Backups from OCI + *****************************************************/ + if err := r.syncBackupResources(logger, desiredAdb); err != nil { + return r.manageError(logger.WithName("syncBackupResources"), desiredAdb, err) + } + + /***************************************************** + * Validate Wallet + *****************************************************/ + if err := r.validateWallet(logger, desiredAdb); err != nil { + return r.manageError( + logger.WithName("validateWallet"), + desiredAdb, + fmt.Errorf("Failed to validate Wallet: %w", err)) + } } /****************************************************************** - * Update the resource if the spec has been changed. - * This will trigger another reconcile, so returns with an empty - * result. + * Update the Autonomous Database at the end of every reconcile. ******************************************************************/ - if !reflect.DeepEqual(modifiedADB.Spec, desiredADB.Spec) { - if err := r.KubeClient.Update(context.TODO(), modifiedADB); err != nil { - return r.manageError(logger.WithName("updateSpec"), modifiedADB, err) + if specChanged { + if err := r.KubeClient.Update(context.TODO(), desiredAdb); err != nil { + return r.manageError( + logger.WithName("updateSpec"), + desiredAdb, + fmt.Errorf("Failed to update AutonomousDatabase spec: %w", err)) } + // Immediately exit the reconcile loop if the resource is updated, and let + // the next run continue. return emptyResult, nil } - /****************************************************************** - * Update the status at the end of every reconcile. - ******************************************************************/ - copiedADB := modifiedADB.DeepCopy() - - updateCondition(modifiedADB, nil) - if err := r.KubeClient.Status().Update(context.TODO(), modifiedADB); err != nil { - return r.manageError(logger.WithName("Status().Update"), modifiedADB, err) - } - modifiedADB.Spec = copiedADB.Spec - - if dbv4.IsADBIntermediateState(modifiedADB.Status.LifecycleState) { - logger.WithName("IsADBIntermediateState").Info("LifecycleState is " + string(modifiedADB.Status.LifecycleState) + "; reconcile queued") - return requeueResult, nil + updateCondition(desiredAdb, nil) + if err := r.KubeClient.Status().Update(context.TODO(), desiredAdb); err != nil { + return r.manageError( + logger, + desiredAdb, + fmt.Errorf("Failed to update AutonomousDatabase status: %w", err)) } /****************************************************************** - * Update the lastSucSpec, and then finish the reconcile. - * Requeue if the ADB is terminated, but the finalizer is not yet - * removed. + * Requeue the request in the following cases: + * 1. the ADB is in intermediate state + * 2. the ADB is terminated, but the finalizer is not yet removed. ******************************************************************/ - - var requeue bool = false - if modifiedADB.GetDeletionTimestamp() != nil && - controllerutil.ContainsFinalizer(modifiedADB, dbv4.ADB_FINALIZER) && - modifiedADB.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated { - logger.Info("The ADB is TERMINATED. The CR is to be deleted but finalizer is not yet removed; reconcile queued") - requeue = true - } - - if err := r.patchLastSuccessfulSpec(modifiedADB); err != nil { - return r.manageError(logger.WithName("patchLastSuccessfulSpec"), modifiedADB, err) - } - - if requeue { - logger.Info("Reconcile queued") + if dbv4.IsAdbIntermediateState(desiredAdb.Status.LifecycleState) { + logger. + WithName("IsAdbIntermediateState"). + Info("LifecycleState is " + string(desiredAdb.Status.LifecycleState) + "; reconciliation queued") return requeueResult, nil - } else { logger.Info("AutonomousDatabase reconciles successfully") return emptyResult, nil @@ -334,13 +356,13 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R func (r *AutonomousDatabaseReconciler) setupOCIClients(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { var err error - authData := oci.APIKeyAuth{ - ConfigMapName: adb.Spec.OCIConfig.ConfigMapName, - SecretName: adb.Spec.OCIConfig.SecretName, + authData := oci.ApiKeyAuth{ + ConfigMapName: adb.Spec.OciConfig.ConfigMapName, + SecretName: adb.Spec.OciConfig.SecretName, Namespace: adb.GetNamespace(), } - provider, err := oci.GetOCIProvider(r.KubeClient, authData) + provider, err := oci.GetOciProvider(r.KubeClient, authData) if err != nil { return err } @@ -353,47 +375,17 @@ func (r *AutonomousDatabaseReconciler) setupOCIClients(logger logr.Logger, adb * return nil } +// Upates the status with the error and returns an empty result func (r *AutonomousDatabaseReconciler) manageError(logger logr.Logger, adb *dbv4.AutonomousDatabase, err error) (ctrl.Result, error) { l := logger.WithName("manageError") - if adb.Status.LifecycleState == "" { - // First time entering reconcile - updateCondition(adb, err) - l.Error(err, "CreateFailed") + l.Error(err, "Error occured") - return emptyResult, nil - } else { - // Has synced at least once - var finalError = err - - // Roll back - ociADB := adb.DeepCopy() - specChanged, err := r.getADB(l, ociADB) - if err != nil { - finalError = k8s.CombineErrors(finalError, err) - } - - // Will exit the Reconcile anyway after the manageError is called. - if specChanged { - // Clear the lifecycleState first to avoid the webhook error when update during an intermediate state - adb.Status.LifecycleState = "" - if err := r.KubeClient.Status().Update(context.TODO(), adb); err != nil { - finalError = k8s.CombineErrors(finalError, err) - } - - adb.Spec = ociADB.Spec - - if err := r.KubeClient.Update(context.TODO(), adb); err != nil { - finalError = k8s.CombineErrors(finalError, err) - } - } - - updateCondition(adb, err) - - l.Error(finalError, "UpdateFailed") - - return emptyResult, nil + updateCondition(adb, err) + if err := r.KubeClient.Status().Update(context.TODO(), adb); err != nil { + return emptyResult, fmt.Errorf("Failed to update status: %w", err) } + return emptyResult, nil } const CONDITION_TYPE_COMPLETE = "Complete" @@ -420,7 +412,7 @@ func updateCondition(adb *dbv4.AutonomousDatabase, err error) { Message: errMsg, Status: metav1.ConditionTrue, } - } else if dbv4.IsADBIntermediateState(adb.Status.LifecycleState) { + } else if dbv4.IsAdbIntermediateState(adb.Status.LifecycleState) { condition = metav1.Condition{ Type: CONDITION_TYPE_COMPLETE, LastTransitionTime: metav1.Now(), @@ -446,352 +438,224 @@ func updateCondition(adb *dbv4.AutonomousDatabase, err error) { meta.SetStatusCondition(&adb.Status.Conditions, condition) } -func (r *AutonomousDatabaseReconciler) validateOperation( +func (r *AutonomousDatabaseReconciler) performOperation( logger logr.Logger, - adb *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (exit bool, result ctrl.Result, err error) { - - lastSucSpec, err := adb.GetLastSuccessfulSpec() - if err != nil { - return false, emptyResult, err - } + adb *dbv4.AutonomousDatabase) (specChanged bool, err error) { l := logger.WithName("validateOperation") - // If lastSucSpec is nil, then it's CREATE or BIND opertaion - if lastSucSpec == nil { - if adb.Spec.Details.AutonomousDatabaseOCID == nil { - l.Info("Create operation") - err := r.createADB(logger, adb) - if err != nil { - return false, emptyResult, err - } - - // Update the ADB OCID - if err := r.updateCR(adb); err != nil { - return false, emptyResult, err - } - - l.Info("AutonomousDatabaseOCID updated; exit reconcile") - return true, emptyResult, nil - } else { - l.Info("Bind operation") - _, err := r.getADB(logger, adb) - if err != nil { - return false, emptyResult, err - } - - if err := r.updateCR(adb); err != nil { - return false, emptyResult, err - } - - l.Info("spec updated; exit reconcile") - return true, emptyResult, nil + switch adb.Spec.Action { + case "Create": + l.Info("Create operation") + err := r.createAutonomousDatabase(logger, adb) + if err != nil { + return false, err } - } - - // If it's not CREATE or BIND opertaion, then it's UPDATE or SYNC operation. - // In most of the case the user changes the spec, and we update the oci ADB, but when the user updates on - // the Cloud Console, the controller cannot tell the direction and how to update the resource. - // Thus we compare the current spec with the lastSucSpec. If the details are different, it means that - // the user updates the spec (UPDATE operation), otherwise it's a SYNC operation. - lastDifADB := adb.DeepCopy() - - lastDetailsChanged, err := lastDifADB.RemoveUnchangedDetails(*lastSucSpec) - if err != nil { - return false, emptyResult, err - } + adb.Spec.Action = "" + return true, nil - if lastDetailsChanged { - // Double check if the user input spec is actually different from the spec in OCI. If so, then update the resource. - // When the update completes and the status changes from UPDATING to AVAILABLE, the lastSucSpec is not updated yet, - // so we compare with the oci ADB again to make sure that the updates are completed. + case "Sync": + l.Info("Sync operation") + _, err = r.syncAutonomousDatabase(logger, adb, true) + if err != nil { + return false, err + } + adb.Spec.Action = "" + return true, nil + case "Update": l.Info("Update operation") - - exit, err := r.updateADB(logger, adb) + _, err = r.updateAutonomousDatabase(logger, adb) if err != nil { - return false, emptyResult, err + return false, err } + adb.Spec.Action = "" + return true, nil - return exit, emptyResult, nil - - } else { - l.Info("No operation specified; sync the resource") + case "Stop": + l.Info("Sending StopAutonomousDatabase request to OCI") - // The user doesn't change the spec and the controller should pull the spec from the OCI. - specChanged, err := r.getADB(logger, adb) + resp, err := r.dbService.StopAutonomousDatabase(*adb.Spec.Details.Id) if err != nil { - return false, emptyResult, err + return false, err } + adb.Spec.Action = "" + adb.Status.LifecycleState = resp.LifecycleState + return true, nil - if specChanged { - l.Info("The local spec doesn't match the oci's spec; update the CR") - - // Erase the status.lifecycleState temporarily to avoid the webhook error. - tmpADB := adb.DeepCopy() - adb.Status.LifecycleState = "" - if err := r.KubeClient.Status().Update(context.TODO(), adb); err != nil { - return false, emptyResult, err - } - adb.Spec = tmpADB.Spec - - if err := r.updateCR(adb); err != nil { - return false, emptyResult, err - } + case "Start": + l.Info("Sending StartAutonomousDatabase request to OCI") - return true, emptyResult, nil + resp, err := r.dbService.StartAutonomousDatabase(*adb.Spec.Details.Id) + if err != nil { + return false, err } - return false, emptyResult, nil - } -} -func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb *dbv4.AutonomousDatabase) (exitReconcile bool, err error) { - l := logger.WithName("validateCleanup") - - isADBToBeDeleted := adb.GetDeletionTimestamp() != nil - - if !isADBToBeDeleted { - return false, nil - } + adb.Spec.Action = "" + adb.Status.LifecycleState = resp.LifecycleState + return true, nil - if controllerutil.ContainsFinalizer(adb, dbv4.ADB_FINALIZER) { - if adb.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminating { - // Delete in progress, continue with the reconcile logic - return false, nil - } + case "Terminate": + // OCI only allows terminate operation when the ADB is in an valid state, otherwise requeue the reconcile. + if dbv4.CanBeTerminated(adb.Status.LifecycleState) { + l.Info("Sending DeleteAutonomousDatabase request to OCI") - if adb.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated { - // The adb has been deleted. Remove the finalizer and exit the reconcile. - // Once all finalizers have been removed, the object will be deleted. - l.Info("Resource is in TERMINATED state; remove the finalizer") - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv4.ADB_FINALIZER); err != nil { + _, err := r.dbService.DeleteAutonomousDatabase(*adb.Spec.Details.Id) + if err != nil { return false, err } - return true, nil - } - if adb.Spec.Details.AutonomousDatabaseOCID == nil { - l.Info("Missing AutonomousDatabaseOCID to terminate Autonomous Database; remove the finalizer anyway", "Name", adb.Name, "Namespace", adb.Namespace) - // Remove finalizer anyway. - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv4.ADB_FINALIZER); err != nil { - return false, err - } - return true, nil + adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateTerminating + } else if dbv4.IsAdbIntermediateState(adb.Status.LifecycleState) { + l.Info("Can not terminate an ADB in an intermediate state; exit reconcile") } - if adb.Spec.Details.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminated { - // Run finalization logic for finalizer. If the finalization logic fails, don't remove the finalizer so - // that we can retry during the next reconciliation. - l.Info("Terminating Autonomous Database") - adb.Spec.Details.LifecycleState = database.AutonomousDatabaseLifecycleStateTerminated - if err := r.KubeClient.Update(context.TODO(), adb); err != nil { - return false, err - } - // Exit the reconcile since we have updated the spec - return true, nil + adb.Spec.Action = "" + return true, nil + case "Clone": + resp, err := r.dbService.CreateAutonomousDatabaseClone(adb) + if err != nil { + return false, err } + adb.Status.LifecycleState = resp.LifecycleState - // Continue with the reconcile logic - return false, nil - } - - // Exit the Reconcile since the to-be-deleted resource doesn't has a finalizer - return true, nil -} - -func (r *AutonomousDatabaseReconciler) validateFinalizer(logger logr.Logger, adb *dbv4.AutonomousDatabase) (exit bool, err error) { - l := logger.WithName("validateFinalizer") - - // Delete is not schduled. Update the finalizer for this CR if hardLink is present - var finalizerChanged = false - if adb.Spec.HardLink != nil { - if *adb.Spec.HardLink && !controllerutil.ContainsFinalizer(adb, dbv4.ADB_FINALIZER) { - l.Info("Finalizer added") - if err := k8s.AddFinalizerAndPatch(r.KubeClient, adb, dbv4.ADB_FINALIZER); err != nil { - return false, err - } - - finalizerChanged = true - - } else if !*adb.Spec.HardLink && controllerutil.ContainsFinalizer(adb, dbv4.ADB_FINALIZER) { - l.Info("Finalizer removed") + adb.Spec.Action = "" - if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv4.ADB_FINALIZER); err != nil { - return false, err - } - - finalizerChanged = true + // Create cloned Autonomous Database resource + clonedAdb := &dbv4.AutonomousDatabase{ + ObjectMeta: metav1.ObjectMeta{ + Name: *adb.Spec.Clone.DisplayName, + Namespace: adb.Namespace, + }, + Spec: dbv4.AutonomousDatabaseSpec{ + OciConfig: *adb.Spec.OciConfig.DeepCopy(), + }, + } + clonedAdb.UpdateFromOciAdb(resp.AutonomousDatabase, true) + if err := r.KubeClient.Create(context.TODO(), clonedAdb); err != nil { + return false, err } - } - // If the finalizer is changed during an intermediate state, e.g. set hardLink to true and - // delete the resource, then there must be another ongoing reconcile. In this case we should - // exit the reconcile. - if finalizerChanged && dbv4.IsADBIntermediateState(adb.Status.LifecycleState) { - l.Info("Finalizer changed during an intermediate state, exit the reconcile") return true, nil + case "": + // No-op + return false, nil + default: + adb.Spec.Action = "" + return true, errors.New("Unknown action: " + adb.Spec.Action) } - - return false, nil -} - -// updateCR updates the lastSucSpec and the CR -func (r *AutonomousDatabaseReconciler) updateCR(adb *dbv4.AutonomousDatabase) error { - // Update the lastSucSpec - // Should patch the lastSuccessfulSpec first, otherwise, the update event will be - // filtered out by predicate since the lastSuccessfulSpec is changed. - if err := r.patchLastSuccessfulSpec(adb); err != nil { - return err - } - - if err := r.KubeClient.Update(context.TODO(), adb); err != nil { - return err - } - return nil -} - -func (r *AutonomousDatabaseReconciler) patchLastSuccessfulSpec(adb *dbv4.AutonomousDatabase) error { - copyADB := adb.DeepCopy() - - specBytes, err := json.Marshal(adb.Spec) - if err != nil { - return err - } - - specBytesString := string(specBytes) - - anns := map[string]string{ - dbv4.LastSuccessfulSpec: specBytesString, - } - - annotations.PatchAnnotations(r.KubeClient, adb, anns) - - adb.Spec = copyADB.Spec - adb.Status = copyADB.Status - - return nil } -func (r *AutonomousDatabaseReconciler) createADB(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { +func (r *AutonomousDatabaseReconciler) createAutonomousDatabase(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { logger.WithName("createADB").Info("Sending CreateAutonomousDatabase request to OCI") resp, err := r.dbService.CreateAutonomousDatabase(adb) if err != nil { return err } - // Restore the admin password after updating from OCI ADB - adminPass := adb.Spec.Details.AdminPassword - adb.UpdateFromOCIADB(resp.AutonomousDatabase) - adb.Spec.Details.AdminPassword = adminPass + adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) return nil } -// getADB gets the information from OCI and overwrites the spec and the status, but not update the CR in the cluster -func (r *AutonomousDatabaseReconciler) getADB(logger logr.Logger, adb *dbv4.AutonomousDatabase) (bool, error) { - if adb == nil { - return false, errors.New("AutonomousDatabase OCID is missing") +// syncAutonomousDatabase retrieve the information of AutonomousDatabase from +// OCI and "overwrite" decides whether the spec and the status of "adb" will +// be overwritten. +// It will be a no-op if "Spec.Details.AutonomousDatabaseOCID" of the provided +// AutonomousDatabase is nil. +// This method does not update the actual resource in the cluster. +// +// The returned values are: +// 1. bool: indicates whether the spec is changed after the sync +// 2. error: not nil if an error occurs during the sync +func (r *AutonomousDatabaseReconciler) syncAutonomousDatabase( + logger logr.Logger, + adb *dbv4.AutonomousDatabase, overwrite bool) (specChanged bool, err error) { + if adb.Spec.Details.Id == nil { + return false, nil } - l := logger.WithName("getADB") + l := logger.WithName("syncAutonomousDatabase") // Get the information from OCI l.Info("Sending GetAutonomousDatabase request to OCI") - resp, err := r.dbService.GetAutonomousDatabase(*adb.Spec.Details.AutonomousDatabaseOCID) + resp, err := r.dbService.GetAutonomousDatabase(*adb.Spec.Details.Id) if err != nil { return false, err } - specChanged := adb.UpdateFromOCIADB(resp.AutonomousDatabase) - + specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, overwrite) return specChanged, nil } -// updateADB returns true if an OCI request is sent. +// updateAutonomousDatabase returns true if an OCI request is sent. // The AutonomousDatabase is updated with the returned object from the OCI requests. -func (r *AutonomousDatabaseReconciler) updateADB( +func (r *AutonomousDatabaseReconciler) updateAutonomousDatabase( logger logr.Logger, - adb *dbv4.AutonomousDatabase) (exit bool, err error) { - - l := logger.WithName("updateADB") + adb *dbv4.AutonomousDatabase) (specChanged bool, err error) { // Get OCI AutonomousDatabase and update the lifecycleState of the CR, // so that the validatexx functions know when the state changes back to AVAILABLE - ociADB := adb.DeepCopy() - _, err = r.getADB(logger, ociADB) + ociAdb := adb.DeepCopy() + _, err = r.syncAutonomousDatabase(logger, ociAdb, true) if err != nil { return false, err } - adb.Status.LifecycleState = ociADB.Status.LifecycleState - // Start update - difADB := adb.DeepCopy() + // difAdb is used to store ONLY the values of Autonomous Database that are + // difference from the one in OCI + difAdb := adb.DeepCopy() - ociDetailsChanged, err := difADB.RemoveUnchangedDetails(ociADB.Spec) + detailsAreChanged, err := difAdb.RemoveUnchangedDetails(ociAdb.Spec) if err != nil { return false, err } // Do the update request only if the current ADB is actually different from the OCI ADB - if ociDetailsChanged { - // Special case: if the oci ADB is terminating, then update the spec and exit the reconcile. - // This happens when the lifecycleState changes to TERMINATED during an intermediate state, - // whatever is in progress should be abandonded and the desired spec should the same as oci ADB. - if ociADB.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminating { - l.Info("OCI ADB is in TERMINATING state; update the spec and exit the reconcile") - - adb.Status.LifecycleState = "" - if err := r.KubeClient.Status().Update(context.TODO(), adb); err != nil { - return false, err - } - - adb.Spec = ociADB.Spec - if err := r.KubeClient.Update(context.TODO(), adb); err != nil { - return false, err - } - return true, nil - } - - // Special case: if the lifecycleState is changed, it might have to exit the reconcile in some cases. - sent, exit, err := r.validateDesiredLifecycleState(logger, adb, difADB, ociADB) + if detailsAreChanged { + logger.Info("Sending UpdateAutonomousDatabase request to OCI") + resp, err := r.dbService.UpdateAutonomousDatabase(*adb.Spec.Details.Id, difAdb) if err != nil { return false, err } - if sent { - return exit, nil - } - - validations := []func(logr.Logger, *dbv4.AutonomousDatabase, *dbv4.AutonomousDatabase, *dbv4.AutonomousDatabase) (bool, error){ - r.validateGeneralFields, - r.validateAdminPassword, - r.validateDbWorkload, - r.validateLicenseModel, - r.validateScalingFields, - r.validateGeneralNetworkAccess, - } - for _, op := range validations { - sent, err := op(logger, adb, difADB, ociADB) - if err != nil { - return false, err - } - - if sent { - return false, nil - } - } + specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) + return specChanged, nil + + // validations := []func( + // logr.Logger, + // *dbv4.AutonomousDatabase, + // *dbv4.AutonomousDatabase, + // *dbv4.AutonomousDatabase) (specChanged bool, err error){ + // r.validateGeneralFields, + // r.validateAdminPassword, + // r.validateDbWorkload, + // r.validateLicenseModel, + // r.validateScalingFields, + // r.validateGeneralNetworkAccess, + // } + + // for _, op := range validations { + // sent, err := op(logger, adb, difAdb, ociAdb) + // if err != nil { + // return false, err + // } + + // if sent { + // return false, nil + // } + // } } return false, nil } +/* func (r *AutonomousDatabaseReconciler) validateGeneralFields( logger logr.Logger, adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { + difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { if difADB.Spec.Details.DisplayName == nil && difADB.Spec.Details.DbName == nil && @@ -800,10 +664,6 @@ func (r *AutonomousDatabaseReconciler) validateGeneralFields( return false, nil } - if ociADB.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateAvailable { - return false, nil - } - l := logger.WithName("validateGeneralFields") l.Info("Sending UpdateAutonomousDatabase request to OCI") @@ -812,27 +672,21 @@ func (r *AutonomousDatabaseReconciler) validateGeneralFields( return false, err } - adb.UpdateFromOCIADB(resp.AutonomousDatabase) + specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - return true, nil + return specChanged, nil } -// Special case: compare with lastSpec but not ociSpec func (r *AutonomousDatabaseReconciler) validateAdminPassword( logger logr.Logger, adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { + difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { if difADB.Spec.Details.AdminPassword.K8sSecret.Name == nil && difADB.Spec.Details.AdminPassword.OCISecret.OCID == nil { return false, nil } - if ociADB.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateAvailable { - return false, nil - } - l := logger.WithName("validateAdminPassword") l.Info("Sending UpdateAutonomousDatabase request to OCI") @@ -841,27 +695,20 @@ func (r *AutonomousDatabaseReconciler) validateAdminPassword( return false, err } - adb.UpdateFromOCIADB(resp.AutonomousDatabase) - // Update the admin password fields because they are missing in the ociADB - adb.Spec.Details.AdminPassword = difADB.Spec.Details.AdminPassword + specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - return true, nil + return specChanged, nil } func (r *AutonomousDatabaseReconciler) validateDbWorkload( logger logr.Logger, adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { + difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { if difADB.Spec.Details.DbWorkload == "" { return false, nil } - if ociADB.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateAvailable { - return false, nil - } - l := logger.WithName("validateDbWorkload") l.Info("Sending UpdateAutonomousDatabase request to OCI") @@ -870,25 +717,20 @@ func (r *AutonomousDatabaseReconciler) validateDbWorkload( return false, err } - adb.UpdateFromOCIADB(resp.AutonomousDatabase) + specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - return true, nil + return specChanged, nil } func (r *AutonomousDatabaseReconciler) validateLicenseModel( logger logr.Logger, adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { + difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { if difADB.Spec.Details.LicenseModel == "" { return false, nil } - if ociADB.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateAvailable { - return false, nil - } - l := logger.WithName("validateLicenseModel") l.Info("Sending UpdateAutonomousDatabase request to OCI") @@ -897,16 +739,15 @@ func (r *AutonomousDatabaseReconciler) validateLicenseModel( return false, err } - adb.UpdateFromOCIADB(resp.AutonomousDatabase) + specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - return true, nil + return specChanged, nil } func (r *AutonomousDatabaseReconciler) validateScalingFields( logger logr.Logger, adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { + difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { if difADB.Spec.Details.DataStorageSizeInTBs == nil && difADB.Spec.Details.CPUCoreCount == nil && @@ -914,10 +755,6 @@ func (r *AutonomousDatabaseReconciler) validateScalingFields( return false, nil } - if ociADB.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateAvailable { - return false, nil - } - l := logger.WithName("validateScalingFields") l.Info("Sending UpdateAutonomousDatabase request to OCI") @@ -926,73 +763,9 @@ func (r *AutonomousDatabaseReconciler) validateScalingFields( return false, err } - adb.UpdateFromOCIADB(resp.AutonomousDatabase) - - return true, nil -} - -func (r *AutonomousDatabaseReconciler) validateDesiredLifecycleState( - logger logr.Logger, - adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (sent bool, exit bool, err error) { - - if difADB.Spec.Details.LifecycleState == "" { - return false, false, nil - } - - if difADB.Spec.Details.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminated { - // OCI only allows terminate operation when the ADB is in an valid state, otherwise requeue the reconcile. - if !dbv4.ValidADBTerminateState(adb.Status.LifecycleState) { - return false, false, nil - } - } else if dbv4.IsADBIntermediateState(ociADB.Status.LifecycleState) { - // Other lifecycle management operation; requeue the reconcile if it's in an intermediate state - return false, false, nil - } - - l := logger.WithName("validateDesiredLifecycleState") - - switch difADB.Spec.Details.LifecycleState { - case database.AutonomousDatabaseLifecycleStateAvailable: - l.Info("Sending StartAutonomousDatabase request to OCI") - - resp, err := r.dbService.StartAutonomousDatabase(*adb.Spec.Details.AutonomousDatabaseOCID) - if err != nil { - return false, false, err - } - - adb.Status.LifecycleState = resp.LifecycleState - case database.AutonomousDatabaseLifecycleStateStopped: - l.Info("Sending StopAutonomousDatabase request to OCI") - - resp, err := r.dbService.StopAutonomousDatabase(*adb.Spec.Details.AutonomousDatabaseOCID) - if err != nil { - return false, false, err - } + specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - adb.Status.LifecycleState = resp.LifecycleState - case database.AutonomousDatabaseLifecycleStateTerminated: - l.Info("Sending DeleteAutonomousDatabase request to OCI") - - _, err := r.dbService.DeleteAutonomousDatabase(*adb.Spec.Details.AutonomousDatabaseOCID) - if err != nil { - return false, false, err - } - - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateTerminating - - // The controller allows terminate during some intermediate states. - // Exit the reconcile because there is already another ongoing reconcile. - if dbv4.IsADBIntermediateState(ociADB.Status.LifecycleState) { - l.Info("Terminating an ADB which is in an intermediate state; exit reconcile") - return true, true, nil - } - default: - return false, false, errors.New("unknown lifecycleState") - } - - return true, false, nil + return specChanged, nil } // The logic of updating the network access configurations is as follows: @@ -1017,8 +790,7 @@ func (r *AutonomousDatabaseReconciler) validateDesiredLifecycleState( func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( logger logr.Logger, adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { + difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { if difADB.Spec.Details.NetworkAccess.AccessType == "" && difADB.Spec.Details.NetworkAccess.IsAccessControlEnabled == nil && @@ -1030,14 +802,10 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( return false, nil } - if ociADB.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateAvailable { - return false, nil - } - l := logger.WithName("validateGeneralNetworkAccess") if !*adb.Spec.Details.IsDedicated { - var lastAccessType = ociADB.Spec.Details.NetworkAccess.AccessType + var lastAccessType = adb.Spec.Details.NetworkAccess.AccessType var difAccessType = difADB.Spec.Details.NetworkAccess.AccessType if difAccessType != "" { @@ -1045,14 +813,14 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( case dbv4.NetworkAccessTypePublic: l.Info("Configuring network access type to PUBLIC") // OCI validation requires IsMTLSConnectionRequired to be enabled before changing the network access type to PUBLIC - if !*ociADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired { + if !*adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired { if err := r.setMTLSRequired(logger, adb); err != nil { return false, err } return true, nil } - if err := r.setNetworkAccessPublic(logger, ociADB.Spec.Details.NetworkAccess.AccessType, adb); err != nil { + if err := r.setNetworkAccessPublic(logger, adb.Spec.Details.NetworkAccess.AccessType, adb); err != nil { return false, err } return true, nil @@ -1075,7 +843,7 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( return true, nil } - sent, err := r.validateNetworkAccess(logger, adb, difADB, ociADB) + sent, err := r.validateNetworkAccess(logger, adb, difADB) if err != nil { return false, err } @@ -1093,7 +861,7 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( case dbv4.NetworkAccessTypePrivate: l.Info("Configuring network access type to PRIVATE") - sent, err := r.validateNetworkAccess(logger, adb, difADB, ociADB) + sent, err := r.validateNetworkAccess(logger, adb, difADB) if err != nil { return false, err } @@ -1111,7 +879,7 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( } } else { // Access type doesn't change - sent, err := r.validateNetworkAccess(logger, adb, difADB, ociADB) + sent, err := r.validateNetworkAccess(logger, adb, difADB) if err != nil { return false, err } @@ -1129,7 +897,7 @@ func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( } } else { // Dedicated database - sent, err := r.validateNetworkAccess(logger, adb, difADB, ociADB) + sent, err := r.validateNetworkAccess(logger, adb, difADB) if err != nil { return false, err } @@ -1163,7 +931,7 @@ func (r *AutonomousDatabaseReconciler) validateMTLS( logger logr.Logger, adb *dbv4.AutonomousDatabase, difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { + ociADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { if difADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired == nil { return false, nil @@ -1207,8 +975,7 @@ func (r *AutonomousDatabaseReconciler) setNetworkAccessPublic(logger logr.Logger func (r *AutonomousDatabaseReconciler) validateNetworkAccess( logger logr.Logger, adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (sent bool, err error) { + difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { if difADB.Spec.Details.NetworkAccess.AccessType == "" && difADB.Spec.Details.NetworkAccess.IsAccessControlEnabled == nil && @@ -1238,11 +1005,12 @@ func (r *AutonomousDatabaseReconciler) validateNetworkAccess( return true, nil } +*/ func (r *AutonomousDatabaseReconciler) validateWallet(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { - if adb.Spec.Details.Wallet.Name == nil && - adb.Spec.Details.Wallet.Password.K8sSecret.Name == nil && - adb.Spec.Details.Wallet.Password.OCISecret.OCID == nil { + if adb.Spec.Wallet.Name == nil && + adb.Spec.Wallet.Password.K8sSecret.Name == nil && + adb.Spec.Wallet.Password.OciSecret.Id == nil { return nil } @@ -1255,10 +1023,10 @@ func (r *AutonomousDatabaseReconciler) validateWallet(logger logr.Logger, adb *d // lastSucSpec may be nil if this is the first time entering the reconciliation loop var walletName string - if adb.Spec.Details.Wallet.Name == nil { + if adb.Spec.Wallet.Name == nil { walletName = adb.GetName() + "-instance-wallet" } else { - walletName = *adb.Spec.Details.Wallet.Name + walletName = *adb.Spec.Wallet.Name } secret, err := k8s.FetchSecret(r.KubeClient, adb.GetNamespace(), walletName) @@ -1321,7 +1089,7 @@ func (r *AutonomousDatabaseReconciler) syncBackupResources(logger logr.Logger, a } } - resp, err := r.dbService.ListAutonomousDatabaseBackups(*adb.Spec.Details.AutonomousDatabaseOCID) + resp, err := r.dbService.ListAutonomousDatabaseBackups(*adb.Spec.Details.Id) if err != nil { return err } diff --git a/controllers/database/autonomousdatabasebackup_controller.go b/controllers/database/autonomousdatabasebackup_controller.go index 0bd98067..9744f3fb 100644 --- a/controllers/database/autonomousdatabasebackup_controller.go +++ b/controllers/database/autonomousdatabasebackup_controller.go @@ -55,7 +55,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" - "github.com/oracle/oracle-database-operator/commons/adb_family" + adbfamily "github.com/oracle/oracle-database-operator/commons/adb_family" "github.com/oracle/oracle-database-operator/commons/oci" ) @@ -92,7 +92,7 @@ func (r *AutonomousDatabaseBackupReconciler) Reconcile(ctx context.Context, req if apiErrors.IsNotFound(err) { return emptyResult, nil } - // Failed to get ADBBackup, so we don't need to update the status + // Failed to get AutonomousDatabaseBackup, so we don't need to update the status return emptyResult, err } @@ -100,7 +100,7 @@ func (r *AutonomousDatabaseBackupReconciler) Reconcile(ctx context.Context, req * Look up the owner AutonomousDatabase and set the ownerReference * if the owner hasn't been set yet. ******************************************************************/ - adbOCID, err := r.verifyTargetADB(backup) + adbOCID, err := r.verifyTargetAdb(backup) if err != nil { return r.manageError(backup, err) } @@ -133,7 +133,7 @@ func (r *AutonomousDatabaseBackupReconciler) Reconcile(ctx context.Context, req /****************************************************************** * Requeue if the Backup is in an intermediate state - * No-op if the ADB OCID is nil + * No-op if the Autonomous Database OCID is nil * To get the latest status, execute before all the reconcile logic ******************************************************************/ if dbv4.IsBackupIntermediateState(backup.Status.LifecycleState) { @@ -210,43 +210,43 @@ func (r *AutonomousDatabaseBackupReconciler) setOwnerAutonomousDatabase(backup * return nil } -// verifyTargetADB searches if the target ADB is in the cluster, and set the owner reference to the ADB if it exists. -// The function returns the OCID of the target ADB. -func (r *AutonomousDatabaseBackupReconciler) verifyTargetADB(backup *dbv4.AutonomousDatabaseBackup) (string, error) { +// verifyTargetAdb searches if the target AutonomousDatabase is in the cluster, and set the owner reference to that AutonomousDatabase if it exists. +// The function returns the OCID of the target AutonomousDatabase. +func (r *AutonomousDatabaseBackupReconciler) verifyTargetAdb(backup *dbv4.AutonomousDatabaseBackup) (string, error) { // Get the target ADB OCID and the ADB resource - ownerADB, err := adbfamily.VerifyTargetADB(r.KubeClient, backup.Spec.Target, backup.Namespace) + ownerAdb, err := adbfamily.VerifyTargetAdb(r.KubeClient, backup.Spec.Target, backup.Namespace) if err != nil { return "", err } // Set the owner reference if needed - if len(backup.GetOwnerReferences()) == 0 && ownerADB != nil { - if err := r.setOwnerAutonomousDatabase(backup, ownerADB); err != nil { + if len(backup.GetOwnerReferences()) == 0 && ownerAdb != nil { + if err := r.setOwnerAutonomousDatabase(backup, ownerAdb); err != nil { return "", err } } - if backup.Spec.Target.OCIADB.OCID != nil { - return *backup.Spec.Target.OCIADB.OCID, nil + if backup.Spec.Target.OciAdb.OCID != nil { + return *backup.Spec.Target.OciAdb.OCID, nil } - if ownerADB != nil && ownerADB.Spec.Details.AutonomousDatabaseOCID != nil { - return *ownerADB.Spec.Details.AutonomousDatabaseOCID, nil + if ownerAdb != nil && ownerAdb.Spec.Details.Id != nil { + return *ownerAdb.Spec.Details.Id, nil } - return "", errors.New("cannot get the OCID of the targetADB") + return "", errors.New("cannot get the OCID of the target AutonomousDatabase") } func (r *AutonomousDatabaseBackupReconciler) setupOCIClients(backup *dbv4.AutonomousDatabaseBackup) error { var err error - authData := oci.APIKeyAuth{ + authData := oci.ApiKeyAuth{ ConfigMapName: backup.Spec.OCIConfig.ConfigMapName, SecretName: backup.Spec.OCIConfig.SecretName, Namespace: backup.GetNamespace(), } - provider, err := oci.GetOCIProvider(r.KubeClient, authData) + provider, err := oci.GetOciProvider(r.KubeClient, authData) if err != nil { return err } diff --git a/controllers/database/autonomousdatabaserestore_controller.go b/controllers/database/autonomousdatabaserestore_controller.go index f4579475..61b84c5d 100644 --- a/controllers/database/autonomousdatabaserestore_controller.go +++ b/controllers/database/autonomousdatabaserestore_controller.go @@ -102,7 +102,7 @@ func (r *AutonomousDatabaseRestoreReconciler) Reconcile(ctx context.Context, req if apiErrors.IsNotFound(err) { return emptyResult, nil } - // Failed to get ADBRestore, so we don't need to update the status + // Failed to get the resource return emptyResult, err } @@ -110,7 +110,7 @@ func (r *AutonomousDatabaseRestoreReconciler) Reconcile(ctx context.Context, req * Look up the owner AutonomousDatabase and set the ownerReference * if the owner hasn't been set yet. ******************************************************************/ - adbOCID, err := r.verifyTargetADB(restore) + adbOCID, err := r.verifyTargetAdb(restore) if err != nil { return r.manageError(restore, err) } @@ -183,9 +183,9 @@ func (r *AutonomousDatabaseRestoreReconciler) Reconcile(ctx context.Context, req } func (r *AutonomousDatabaseRestoreReconciler) getRestoreSDKTime(restore *dbv4.AutonomousDatabaseRestore) (*common.SDKTime, error) { - if restore.Spec.Source.K8sADBBackup.Name != nil { // restore using backupName + if restore.Spec.Source.K8sAdbBackup.Name != nil { // restore using backupName backup := &dbv4.AutonomousDatabaseBackup{} - if err := k8s.FetchResource(r.KubeClient, restore.Namespace, *restore.Spec.Source.K8sADBBackup.Name, backup); err != nil { + if err := k8s.FetchResource(r.KubeClient, restore.Namespace, *restore.Spec.Source.K8sAdbBackup.Name, backup); err != nil { return nil, err } @@ -219,43 +219,43 @@ func (r *AutonomousDatabaseRestoreReconciler) setOwnerAutonomousDatabase(restore return nil } -// verifyTargetADB searches if the target ADB is in the cluster, and set the owner reference to the ADB if it exists. +// verifyTargetAdb searches if the target ADB is in the cluster, and set the owner reference to the ADB if it exists. // The function returns the OCID of the target ADB. -func (r *AutonomousDatabaseRestoreReconciler) verifyTargetADB(restore *dbv4.AutonomousDatabaseRestore) (string, error) { +func (r *AutonomousDatabaseRestoreReconciler) verifyTargetAdb(restore *dbv4.AutonomousDatabaseRestore) (string, error) { // Get the target ADB OCID and the ADB resource - ownerADB, err := adbfamily.VerifyTargetADB(r.KubeClient, restore.Spec.Target, restore.Namespace) + ownerAdb, err := adbfamily.VerifyTargetAdb(r.KubeClient, restore.Spec.Target, restore.Namespace) if err != nil { return "", err } // Set the owner reference if needed - if len(restore.GetOwnerReferences()) == 0 && ownerADB != nil { - if err := r.setOwnerAutonomousDatabase(restore, ownerADB); err != nil { + if len(restore.GetOwnerReferences()) == 0 && ownerAdb != nil { + if err := r.setOwnerAutonomousDatabase(restore, ownerAdb); err != nil { return "", err } } - if restore.Spec.Target.OCIADB.OCID != nil { - return *restore.Spec.Target.OCIADB.OCID, nil + if restore.Spec.Target.OciAdb.OCID != nil { + return *restore.Spec.Target.OciAdb.OCID, nil } - if ownerADB != nil && ownerADB.Spec.Details.AutonomousDatabaseOCID != nil { - return *ownerADB.Spec.Details.AutonomousDatabaseOCID, nil + if ownerAdb != nil && ownerAdb.Spec.Details.Id != nil { + return *ownerAdb.Spec.Details.Id, nil } - return "", errors.New("cannot get the OCID of the targetADB") + return "", errors.New("cannot get the OCID of the target Autonomous Database") } func (r *AutonomousDatabaseRestoreReconciler) setupOCIClients(restore *dbv4.AutonomousDatabaseRestore) error { var err error - authData := oci.APIKeyAuth{ + authData := oci.ApiKeyAuth{ ConfigMapName: restore.Spec.OCIConfig.ConfigMapName, SecretName: restore.Spec.OCIConfig.SecretName, Namespace: restore.GetNamespace(), } - provider, err := oci.GetOCIProvider(r.KubeClient, authData) + provider, err := oci.GetOciProvider(r.KubeClient, authData) if err != nil { return err } diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index a746d7ba..a30e49ce 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -115,12 +115,12 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } // Create oci-go-sdk client - authData := oci.APIKeyAuth{ + authData := oci.ApiKeyAuth{ ConfigMapName: dbcsInst.Spec.OCIConfigMap, SecretName: dbcsInst.Spec.OCISecret, Namespace: dbcsInst.GetNamespace(), } - provider, err := oci.GetOCIProvider(r.KubeClient, authData) + provider, err := oci.GetOciProvider(r.KubeClient, authData) if err != nil { result := resultNq return result, err diff --git a/test/e2e/autonomouscontainerdatabase_test.go b/test/e2e/autonomouscontainerdatabase_test.go index f27d8a6d..a76fc33f 100644 --- a/test/e2e/autonomouscontainerdatabase_test.go +++ b/test/e2e/autonomouscontainerdatabase_test.go @@ -49,8 +49,8 @@ import ( "k8s.io/apimachinery/pkg/types" dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - "github.com/oracle/oracle-database-operator/test/e2e/behavior" - "github.com/oracle/oracle-database-operator/test/e2e/util" + e2ebehavior "github.com/oracle/oracle-database-operator/test/e2e/behavior" + e2eutil "github.com/oracle/oracle-database-operator/test/e2e/util" // +kubebuilder:scaffold:imports ) @@ -81,7 +81,7 @@ var _ = Describe("test ACD binding", func() { CompartmentOCID: common.String(SharedCompartmentOCID), AutonomousExadataVMClusterOCID: common.String(SharedExadataVMClusterOCID), PatchModel: database.AutonomousContainerDatabasePatchModelUpdates, - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OCIConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, @@ -119,7 +119,7 @@ var _ = Describe("test ACD binding", func() { }, Spec: dbv1alpha1.AutonomousContainerDatabaseSpec{ AutonomousContainerDatabaseOCID: common.String(acdID), - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OCIConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, diff --git a/test/e2e/autonomousdatabase_controller_bind_test.go b/test/e2e/autonomousdatabase_controller_bind_test.go index 58de3356..48e60f0d 100644 --- a/test/e2e/autonomousdatabase_controller_bind_test.go +++ b/test/e2e/autonomousdatabase_controller_bind_test.go @@ -50,8 +50,8 @@ import ( "k8s.io/apimachinery/pkg/types" dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - "github.com/oracle/oracle-database-operator/test/e2e/behavior" - "github.com/oracle/oracle-database-operator/test/e2e/util" + e2ebehavior "github.com/oracle/oracle-database-operator/test/e2e/behavior" + e2eutil "github.com/oracle/oracle-database-operator/test/e2e/util" // +kubebuilder:scaffold:imports ) @@ -103,18 +103,18 @@ var _ = Describe("test ADB binding with hardLink=true", func() { }, Spec: dbv1alpha1.AutonomousDatabaseSpec{ Details: dbv1alpha1.AutonomousDatabaseDetails{ - AutonomousDatabaseOCID: adbID, - Wallet: dbv1alpha1.WalletSpec{ - Name: common.String(downloadedWallet), - Password: dbv1alpha1.PasswordSpec{ - K8sSecret: dbv1alpha1.K8sSecretSpec{ - Name: common.String(SharedWalletPassSecretName), - }, + Id: adbID, + }, + Wallet: dbv1alpha1.WalletSpec{ + Name: common.String(downloadedWallet), + Password: dbv1alpha1.PasswordSpec{ + K8sSecret: dbv1alpha1.K8sSecretSpec{ + Name: common.String(SharedWalletPassSecretName), }, }, }, HardLink: common.Bool(false), - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OciConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, @@ -136,9 +136,9 @@ var _ = Describe("test ADB binding with hardLink=true", func() { It("Should restart ADB", e2ebehavior.UpdateAndAssertADBState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)) - It("Should change to RESTRICTED network access", e2ebehavior.TestNetworkAccessRestricted(&k8sClient, &dbClient, &adbLookupKey, false)) + It("Should change to RESTRICTED network access", e2ebehavior.TestNetworkAccessRestricted(&k8sClient, &dbClient, &adbLookupKey, []string{"192.168.0.1"}, false)) - It("Should change isMTLSConnectionRequired to false", e2ebehavior.TestNetworkAccessRestricted(&k8sClient, &dbClient, &adbLookupKey, false)) + It("Should change isMTLSConnectionRequired to false", e2ebehavior.TestNetworkAccessRestricted(&k8sClient, &dbClient, &adbLookupKey, []string{"192.168.0.1"}, false)) It("Should should change to PRIVATE network access", e2ebehavior.TestNetworkAccessPrivate(&k8sClient, &dbClient, &adbLookupKey, false, &SharedSubnetOCID, &SharedNsgOCID)) @@ -162,18 +162,18 @@ var _ = Describe("test ADB binding with hardLink=true", func() { }, Spec: dbv1alpha1.AutonomousDatabaseSpec{ Details: dbv1alpha1.AutonomousDatabaseDetails{ - AutonomousDatabaseOCID: adbID, - Wallet: dbv1alpha1.WalletSpec{ - Name: common.String(downloadedWallet), - Password: dbv1alpha1.PasswordSpec{ - OCISecret: dbv1alpha1.OCISecretSpec{ - OCID: common.String(SharedInstanceWalletPasswordOCID), - }, + Id: adbID, + }, + Wallet: dbv1alpha1.WalletSpec{ + Name: common.String(downloadedWallet), + Password: dbv1alpha1.PasswordSpec{ + OciSecret: dbv1alpha1.OciSecretSpec{ + Id: common.String(SharedInstanceWalletPasswordOCID), }, }, }, HardLink: common.Bool(true), - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OciConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, @@ -210,10 +210,10 @@ var _ = Describe("test ADB binding with hardLink=true", func() { }, Spec: dbv1alpha1.AutonomousDatabaseSpec{ Details: dbv1alpha1.AutonomousDatabaseDetails{ - AutonomousDatabaseOCID: &terminatedAdbID, + Id: &terminatedAdbID, }, HardLink: common.Bool(true), - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OciConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, diff --git a/test/e2e/autonomousdatabase_controller_create_test.go b/test/e2e/autonomousdatabase_controller_create_test.go index 9cf0e7e7..cc7fd288 100644 --- a/test/e2e/autonomousdatabase_controller_create_test.go +++ b/test/e2e/autonomousdatabase_controller_create_test.go @@ -48,8 +48,8 @@ import ( "k8s.io/apimachinery/pkg/types" dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - "github.com/oracle/oracle-database-operator/test/e2e/behavior" - "github.com/oracle/oracle-database-operator/test/e2e/util" + e2ebehavior "github.com/oracle/oracle-database-operator/test/e2e/behavior" + e2eutil "github.com/oracle/oracle-database-operator/test/e2e/util" // +kubebuilder:scaffold:imports ) @@ -89,28 +89,30 @@ var _ = Describe("test ADB provisioning", func() { }, Spec: dbv1alpha1.AutonomousDatabaseSpec{ Details: dbv1alpha1.AutonomousDatabaseDetails{ - CompartmentOCID: common.String(SharedCompartmentOCID), - DbName: common.String(dbName), - DisplayName: common.String(dbName), - CPUCoreCount: common.Int(1), - AdminPassword: dbv1alpha1.PasswordSpec{ - K8sSecret: dbv1alpha1.K8sSecretSpec{ - Name: common.String(SharedAdminPassSecretName), - }, - }, - DataStorageSizeInTBs: common.Int(1), - IsAutoScalingEnabled: common.Bool(true), - Wallet: dbv1alpha1.WalletSpec{ - Name: common.String(downloadedWallet), - Password: dbv1alpha1.PasswordSpec{ + AutonomousDatabaseBase: dbv1alpha1.AutonomousDatabaseBase{ + CompartmentId: common.String(SharedCompartmentOCID), + DbName: common.String(dbName), + DisplayName: common.String(dbName), + CpuCoreCount: common.Int(1), + AdminPassword: dbv1alpha1.PasswordSpec{ K8sSecret: dbv1alpha1.K8sSecretSpec{ - Name: common.String(SharedWalletPassSecretName), + Name: common.String(SharedAdminPassSecretName), }, }, + DataStorageSizeInTBs: common.Int(1), + IsAutoScalingEnabled: common.Bool(true), + }, + }, + Wallet: dbv1alpha1.WalletSpec{ + Name: common.String(downloadedWallet), + Password: dbv1alpha1.PasswordSpec{ + K8sSecret: dbv1alpha1.K8sSecretSpec{ + Name: common.String(SharedWalletPassSecretName), + }, }, }, HardLink: common.Bool(true), - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OciConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, @@ -134,20 +136,22 @@ var _ = Describe("test ADB provisioning", func() { }, Spec: dbv1alpha1.AutonomousDatabaseSpec{ Details: dbv1alpha1.AutonomousDatabaseDetails{ - CompartmentOCID: common.String(SharedCompartmentOCID), - DbName: common.String(dbName), - DisplayName: common.String(dbName), - CPUCoreCount: common.Int(1), - AdminPassword: dbv1alpha1.PasswordSpec{ - K8sSecret: dbv1alpha1.K8sSecretSpec{ - Name: common.String(SharedAdminPassSecretName), + AutonomousDatabaseBase: dbv1alpha1.AutonomousDatabaseBase{ + CompartmentId: common.String(SharedCompartmentOCID), + DbName: common.String(dbName), + DisplayName: common.String(dbName), + CpuCoreCount: common.Int(1), + AdminPassword: dbv1alpha1.PasswordSpec{ + K8sSecret: dbv1alpha1.K8sSecretSpec{ + Name: common.String(SharedAdminPassSecretName), + }, }, + DataStorageSizeInTBs: common.Int(1), + IsAutoScalingEnabled: common.Bool(true), }, - DataStorageSizeInTBs: common.Int(1), - IsAutoScalingEnabled: common.Bool(true), }, HardLink: common.Bool(true), - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OciConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, @@ -179,7 +183,7 @@ var _ = Describe("test ADB provisioning", func() { // Get adb ocid adb := &dbv1alpha1.AutonomousDatabase{} Expect(k8sClient.Get(context.TODO(), adbLookupKey, adb)).To(Succeed()) - databaseOCID := adb.Spec.Details.AutonomousDatabaseOCID + databaseOCID := adb.Spec.Details.Id tnsEntry := dbName + "_high" err := e2ebehavior.ConfigureADBBackup(&dbClient, databaseOCID, &tnsEntry, &SharedPlainTextAdminPassword, &SharedPlainTextWalletPassword, &SharedBucketUrl, &SharedAuthToken, &SharedOciUser) Expect(err).ShouldNot(HaveOccurred()) @@ -195,12 +199,12 @@ var _ = Describe("test ADB provisioning", func() { }, Spec: dbv1alpha1.AutonomousDatabaseBackupSpec{ Target: dbv1alpha1.TargetSpec{ - OCIADB: dbv1alpha1.OCIADBSpec{ - OCID: common.String(*databaseOCID), + OciAdb: dbv1alpha1.OciAdbSpec{ + Ocid: common.String(*databaseOCID), }, }, DisplayName: common.String(backupName), - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OCIConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, @@ -227,16 +231,16 @@ var _ = Describe("test ADB provisioning", func() { }, Spec: dbv1alpha1.AutonomousDatabaseRestoreSpec{ Target: dbv1alpha1.TargetSpec{ - K8sADB: dbv1alpha1.K8sADBSpec{ + K8sAdb: dbv1alpha1.K8sAdbSpec{ Name: common.String(resourceName), }, }, Source: dbv1alpha1.SourceSpec{ - K8sADBBackup: dbv1alpha1.K8sADBBackupSpec{ + K8sAdbBackup: dbv1alpha1.K8sAdbBackupSpec{ Name: common.String(backupName), }, }, - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OCIConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, @@ -273,29 +277,30 @@ var _ = Describe("test ADB provisioning", func() { }, Spec: dbv1alpha1.AutonomousDatabaseSpec{ Details: dbv1alpha1.AutonomousDatabaseDetails{ - CompartmentOCID: common.String(SharedCompartmentOCID), - DbName: common.String(dbName), - DisplayName: common.String(dbName), - CPUCoreCount: common.Int(1), - AdminPassword: dbv1alpha1.PasswordSpec{ - OCISecret: dbv1alpha1.OCISecretSpec{ - OCID: common.String(SharedAdminPasswordOCID), + AutonomousDatabaseBase: dbv1alpha1.AutonomousDatabaseBase{ + CompartmentId: common.String(SharedCompartmentOCID), + DbName: common.String(dbName), + DisplayName: common.String(dbName), + CpuCoreCount: common.Int(1), + AdminPassword: dbv1alpha1.PasswordSpec{ + OciSecret: dbv1alpha1.OciSecretSpec{ + Id: common.String(SharedAdminPasswordOCID), + }, }, + DataStorageSizeInTBs: common.Int(1), + IsAutoScalingEnabled: common.Bool(true), }, - DataStorageSizeInTBs: common.Int(1), - IsAutoScalingEnabled: common.Bool(true), - - Wallet: dbv1alpha1.WalletSpec{ - Name: common.String(downloadedWallet), - Password: dbv1alpha1.PasswordSpec{ - OCISecret: dbv1alpha1.OCISecretSpec{ - OCID: common.String(SharedInstanceWalletPasswordOCID), - }, + }, + Wallet: dbv1alpha1.WalletSpec{ + Name: common.String(downloadedWallet), + Password: dbv1alpha1.PasswordSpec{ + OciSecret: dbv1alpha1.OciSecretSpec{ + Id: common.String(SharedInstanceWalletPasswordOCID), }, }, }, HardLink: common.Bool(true), - OCIConfig: dbv1alpha1.OCIConfigSpec{ + OciConfig: dbv1alpha1.OciConfigSpec{ ConfigMapName: common.String(SharedOCIConfigMapName), SecretName: common.String(SharedOCISecretName), }, diff --git a/test/e2e/behavior/shared_behaviors.go b/test/e2e/behavior/shared_behaviors.go index 00fc02c9..3d87ce94 100644 --- a/test/e2e/behavior/shared_behaviors.go +++ b/test/e2e/behavior/shared_behaviors.go @@ -55,11 +55,12 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - "github.com/oracle/oracle-database-operator/test/e2e/util" "os" "os/exec" "strings" + + dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + e2eutil "github.com/oracle/oracle-database-operator/test/e2e/util" ) /************************************************************** @@ -110,11 +111,11 @@ func AssertProvision(k8sClient *client.Client, adbLookupKey *types.NamespacedNam return nil, err } - return createdADB.Spec.Details.AutonomousDatabaseOCID, nil + return createdADB.Spec.Details.Id, nil }, provisionTimeout, intervalTime).ShouldNot(BeNil()) fmt.Fprintf(GinkgoWriter, "AutonomousDatabase DbName = %s, and AutonomousDatabaseOCID = %s\n", - *createdADB.Spec.Details.DbName, *createdADB.Spec.Details.AutonomousDatabaseOCID) + *createdADB.Spec.Details.DbName, *createdADB.Spec.Details.Id) } } @@ -138,13 +139,13 @@ func AssertBind(k8sClient *client.Client, adbLookupKey *types.NamespacedName) fu if err != nil { return false } - return (boundADB.Spec.Details.CompartmentOCID != nil && + return (boundADB.Spec.Details.CompartmentId != nil && boundADB.Spec.Details.DbWorkload != "" && boundADB.Spec.Details.DbName != nil) }, bindTimeout).Should(Equal(true), "Attributes in the resource should not be empty") fmt.Fprintf(GinkgoWriter, "AutonomousDatabase DbName = %s, and AutonomousDatabaseOCID = %s\n", - *boundADB.Spec.Details.DbName, *boundADB.Spec.Details.AutonomousDatabaseOCID) + *boundADB.Spec.Details.DbName, *boundADB.Spec.Details.Id) } } @@ -163,10 +164,10 @@ func AssertWallet(k8sClient *client.Client, adbLookupKey *types.NamespacedName) Expect(derefK8sClient.Get(context.TODO(), *adbLookupKey, adb)).To(Succeed()) // The default name is xxx-instance-wallet - if adb.Spec.Details.Wallet.Name == nil { + if adb.Spec.Wallet.Name == nil { walletName = adb.Name + "-instance-wallet" } else { - walletName = *adb.Spec.Details.Wallet.Name + walletName = *adb.Spec.Wallet.Name } By("Checking the wallet secret " + walletName + " is created and is not empty") @@ -249,7 +250,7 @@ func UpdateDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, // , the List request returns PROVISIONING state. In this case the update request will fail with // conflict state error. Eventually(func() (database.AutonomousDatabaseLifecycleStateEnum, error) { - listResp, err := e2eutil.ListAutonomousDatabases(derefDBClient, expectedADB.Spec.Details.CompartmentOCID, expectedADB.Spec.Details.DisplayName) + listResp, err := e2eutil.ListAutonomousDatabases(derefDBClient, expectedADB.Spec.Details.CompartmentId, expectedADB.Spec.Details.DisplayName) if err != nil { return "", err } @@ -265,7 +266,7 @@ func UpdateDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, var newDisplayName = *expectedADB.Spec.Details.DisplayName + "_new" var newCPUCoreCount int - if *expectedADB.Spec.Details.CPUCoreCount == 1 { + if *expectedADB.Spec.Details.CpuCoreCount == 1 { newCPUCoreCount = 2 } else { newCPUCoreCount = 1 @@ -278,7 +279,7 @@ func UpdateDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, newDisplayName, newCPUCoreCount, newKey, newVal)) expectedADB.Spec.Details.DisplayName = common.String(newDisplayName) - expectedADB.Spec.Details.CPUCoreCount = common.Int(newCPUCoreCount) + expectedADB.Spec.Details.CpuCoreCount = common.Int(newCPUCoreCount) expectedADB.Spec.Details.FreeformTags = map[string]string{newKey: newVal} expectedADB.Spec.Details.AdminPassword.K8sSecret.Name = common.String(newSecretName) @@ -307,18 +308,18 @@ func AssertADBDetails(k8sClient *client.Client, Eventually(func() (bool, error) { // Fetch the ADB from OCI when it's in AVAILABLE state, and retry if its attributes doesn't match the new ADB's attributes retryPolicy := e2eutil.NewLifecycleStateRetryPolicyADB(database.AutonomousDatabaseLifecycleStateAvailable) - resp, err := e2eutil.GetAutonomousDatabase(derefDBClient, expectedADB.Spec.Details.AutonomousDatabaseOCID, &retryPolicy) + resp, err := e2eutil.GetAutonomousDatabase(derefDBClient, expectedADB.Spec.Details.Id, &retryPolicy) if err != nil { return false, err } debug := false if debug { - if !compareString(expectedADBDetails.AutonomousDatabaseOCID, resp.AutonomousDatabase.Id) { - fmt.Fprintf(GinkgoWriter, "Expected OCID: %v\nGot: %v\n", expectedADBDetails.AutonomousDatabaseOCID, resp.AutonomousDatabase.Id) + if !compareString(expectedADBDetails.Id, resp.AutonomousDatabase.Id) { + fmt.Fprintf(GinkgoWriter, "Expected OCID: %v\nGot: %v\n", expectedADBDetails.Id, resp.AutonomousDatabase.Id) } - if !compareString(expectedADBDetails.CompartmentOCID, resp.AutonomousDatabase.CompartmentId) { - fmt.Fprintf(GinkgoWriter, "Expected CompartmentOCID: %v\nGot: %v\n", expectedADBDetails.CompartmentOCID, resp.CompartmentId) + if !compareString(expectedADBDetails.CompartmentId, resp.AutonomousDatabase.CompartmentId) { + fmt.Fprintf(GinkgoWriter, "Expected CompartmentOCID: %v\nGot: %v\n", expectedADBDetails.CompartmentId, resp.CompartmentId) } if !compareString(expectedADBDetails.DisplayName, resp.AutonomousDatabase.DisplayName) { fmt.Fprintf(GinkgoWriter, "Expected DisplayName: %v\nGot: %v\n", expectedADBDetails.DisplayName, resp.AutonomousDatabase.DisplayName) @@ -338,8 +339,8 @@ func AssertADBDetails(k8sClient *client.Client, if !compareInt(expectedADBDetails.DataStorageSizeInTBs, resp.AutonomousDatabase.DataStorageSizeInTBs) { fmt.Fprintf(GinkgoWriter, "Expected DataStorageSize: %v\nGot: %v\n", expectedADBDetails.DataStorageSizeInTBs, resp.AutonomousDatabase.DataStorageSizeInTBs) } - if !compareInt(expectedADBDetails.CPUCoreCount, resp.AutonomousDatabase.CpuCoreCount) { - fmt.Fprintf(GinkgoWriter, "Expected CPUCoreCount: %v\nGot: %v\n", expectedADBDetails.CPUCoreCount, resp.AutonomousDatabase.CpuCoreCount) + if !compareInt(expectedADBDetails.CpuCoreCount, resp.AutonomousDatabase.CpuCoreCount) { + fmt.Fprintf(GinkgoWriter, "Expected CPUCoreCount: %v\nGot: %v\n", expectedADBDetails.CpuCoreCount, resp.AutonomousDatabase.CpuCoreCount) } if !compareBool(expectedADBDetails.IsAutoScalingEnabled, resp.AutonomousDatabase.IsAutoScalingEnabled) { fmt.Fprintf(GinkgoWriter, "Expected IsAutoScalingEnabled: %v\nGot: %v\n", expectedADBDetails.IsAutoScalingEnabled, resp.AutonomousDatabase.IsAutoScalingEnabled) @@ -347,23 +348,23 @@ func AssertADBDetails(k8sClient *client.Client, if !compareStringMap(expectedADBDetails.FreeformTags, resp.AutonomousDatabase.FreeformTags) { fmt.Fprintf(GinkgoWriter, "Expected FreeformTags: %v\nGot: %v\n", expectedADBDetails.FreeformTags, resp.AutonomousDatabase.FreeformTags) } - if !compareBool(expectedADBDetails.NetworkAccess.IsAccessControlEnabled, resp.AutonomousDatabase.IsAccessControlEnabled) { - fmt.Fprintf(GinkgoWriter, "Expected IsAccessControlEnabled: %v\nGot: %v\n", expectedADBDetails.NetworkAccess.IsAccessControlEnabled, resp.AutonomousDatabase.IsAccessControlEnabled) + if !compareBool(expectedADBDetails.IsAccessControlEnabled, resp.AutonomousDatabase.IsAccessControlEnabled) { + fmt.Fprintf(GinkgoWriter, "Expected IsAccessControlEnabled: %v\nGot: %v\n", expectedADBDetails.IsAccessControlEnabled, resp.AutonomousDatabase.IsAccessControlEnabled) } - if !reflect.DeepEqual(expectedADBDetails.NetworkAccess.AccessControlList, resp.AutonomousDatabase.WhitelistedIps) { - fmt.Fprintf(GinkgoWriter, "Expected AccessControlList: %v\nGot: %v\n", expectedADBDetails.NetworkAccess.AccessControlList, resp.AutonomousDatabase.WhitelistedIps) + if !reflect.DeepEqual(expectedADBDetails.WhitelistedIps, resp.AutonomousDatabase.WhitelistedIps) { + fmt.Fprintf(GinkgoWriter, "Expected AccessControlList: %v\nGot: %v\n", expectedADBDetails.WhitelistedIps, resp.AutonomousDatabase.WhitelistedIps) } - if !compareBool(expectedADBDetails.NetworkAccess.IsMTLSConnectionRequired, resp.AutonomousDatabase.IsMtlsConnectionRequired) { - fmt.Fprintf(GinkgoWriter, "Expected IsMTLSConnectionRequired: %v\nGot: %v\n", expectedADBDetails.NetworkAccess.IsMTLSConnectionRequired, resp.AutonomousDatabase.IsMtlsConnectionRequired) + if !compareBool(expectedADBDetails.IsMtlsConnectionRequired, resp.AutonomousDatabase.IsMtlsConnectionRequired) { + fmt.Fprintf(GinkgoWriter, "Expected IsMTLSConnectionRequired: %v\nGot: %v\n", expectedADBDetails.IsMtlsConnectionRequired, resp.AutonomousDatabase.IsMtlsConnectionRequired) } - if !compareString(expectedADBDetails.NetworkAccess.PrivateEndpoint.SubnetOCID, resp.AutonomousDatabase.SubnetId) { - fmt.Fprintf(GinkgoWriter, "Expected SubnetOCID: %v\nGot: %v\n", expectedADBDetails.NetworkAccess.PrivateEndpoint.SubnetOCID, resp.AutonomousDatabase.SubnetId) + if !compareString(expectedADBDetails.SubnetId, resp.AutonomousDatabase.SubnetId) { + fmt.Fprintf(GinkgoWriter, "Expected SubnetOCID: %v\nGot: %v\n", expectedADBDetails.SubnetId, resp.AutonomousDatabase.SubnetId) } - if !reflect.DeepEqual(expectedADBDetails.NetworkAccess.PrivateEndpoint.NsgOCIDs, resp.AutonomousDatabase.NsgIds) { - fmt.Fprintf(GinkgoWriter, "Expected NsgOCIDs: %v\nGot: %v\n", expectedADBDetails.NetworkAccess.PrivateEndpoint.NsgOCIDs, resp.AutonomousDatabase.NsgIds) + if !reflect.DeepEqual(expectedADBDetails.NsgIds, resp.AutonomousDatabase.NsgIds) { + fmt.Fprintf(GinkgoWriter, "Expected NsgOCIDs: %v\nGot: %v\n", expectedADBDetails.NsgIds, resp.AutonomousDatabase.NsgIds) } - if !compareString(expectedADBDetails.NetworkAccess.PrivateEndpoint.HostnamePrefix, resp.AutonomousDatabase.PrivateEndpointLabel) { - fmt.Fprintf(GinkgoWriter, "Expected HostnamePrefix: %v\nGot: %v\n", expectedADBDetails.NetworkAccess.PrivateEndpoint.HostnamePrefix, resp.AutonomousDatabase.PrivateEndpointLabel) + if !compareString(expectedADBDetails.PrivateEndpointLabel, resp.AutonomousDatabase.PrivateEndpointLabel) { + fmt.Fprintf(GinkgoWriter, "Expected PrivateEndpointLabel: %v\nGot: %v\n", expectedADBDetails.PrivateEndpointLabel, resp.AutonomousDatabase.PrivateEndpointLabel) } } @@ -371,23 +372,23 @@ func AssertADBDetails(k8sClient *client.Client, // (e.g. adminPassword, wallet) are missing from e2eutil.GetAutonomousDatabase(). // We don't compare LifecycleState in this case. We only make sure that the ADB is in AVAIABLE state before // proceeding to the next test. - same := compareString(expectedADBDetails.AutonomousDatabaseOCID, resp.AutonomousDatabase.Id) && - compareString(expectedADBDetails.CompartmentOCID, resp.AutonomousDatabase.CompartmentId) && + same := compareString(expectedADBDetails.Id, resp.AutonomousDatabase.Id) && + compareString(expectedADBDetails.CompartmentId, resp.AutonomousDatabase.CompartmentId) && compareString(expectedADBDetails.DisplayName, resp.AutonomousDatabase.DisplayName) && compareString(expectedADBDetails.DbName, resp.AutonomousDatabase.DbName) && expectedADBDetails.DbWorkload == resp.AutonomousDatabase.DbWorkload && compareBool(expectedADBDetails.IsDedicated, resp.AutonomousDatabase.IsDedicated) && compareString(expectedADBDetails.DbVersion, resp.AutonomousDatabase.DbVersion) && compareInt(expectedADBDetails.DataStorageSizeInTBs, resp.AutonomousDatabase.DataStorageSizeInTBs) && - compareInt(expectedADBDetails.CPUCoreCount, resp.AutonomousDatabase.CpuCoreCount) && + compareInt(expectedADBDetails.CpuCoreCount, resp.AutonomousDatabase.CpuCoreCount) && compareBool(expectedADBDetails.IsAutoScalingEnabled, resp.AutonomousDatabase.IsAutoScalingEnabled) && compareStringMap(expectedADBDetails.FreeformTags, resp.AutonomousDatabase.FreeformTags) && - compareBool(expectedADBDetails.NetworkAccess.IsAccessControlEnabled, resp.AutonomousDatabase.IsAccessControlEnabled) && - reflect.DeepEqual(expectedADBDetails.NetworkAccess.AccessControlList, resp.AutonomousDatabase.WhitelistedIps) && - compareBool(expectedADBDetails.NetworkAccess.IsMTLSConnectionRequired, resp.AutonomousDatabase.IsMtlsConnectionRequired) && - compareString(expectedADBDetails.NetworkAccess.PrivateEndpoint.SubnetOCID, resp.AutonomousDatabase.SubnetId) && - reflect.DeepEqual(expectedADBDetails.NetworkAccess.PrivateEndpoint.NsgOCIDs, resp.AutonomousDatabase.NsgIds) && - compareString(expectedADBDetails.NetworkAccess.PrivateEndpoint.HostnamePrefix, resp.AutonomousDatabase.PrivateEndpointLabel) + compareBool(expectedADBDetails.IsAccessControlEnabled, resp.AutonomousDatabase.IsAccessControlEnabled) && + reflect.DeepEqual(expectedADBDetails.WhitelistedIps, resp.AutonomousDatabase.WhitelistedIps) && + compareBool(expectedADBDetails.IsMtlsConnectionRequired, resp.AutonomousDatabase.IsMtlsConnectionRequired) && + compareString(expectedADBDetails.SubnetId, resp.AutonomousDatabase.SubnetId) && + reflect.DeepEqual(expectedADBDetails.NsgIds, resp.AutonomousDatabase.NsgIds) && + compareString(expectedADBDetails.PrivateEndpointLabel, resp.AutonomousDatabase.PrivateEndpointLabel) return same, nil }, updateADBTimeout, intervalTime).Should(BeTrue()) @@ -398,15 +399,9 @@ func AssertADBDetails(k8sClient *client.Client, } } -func TestNetworkAccessRestricted(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, isMTLSConnectionRequired bool) func() { +func TestNetworkAccessRestricted(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, acl []string, isMTLSConnectionRequired bool) func() { return func() { - networkRestrictedSpec := dbv1alpha1.NetworkAccessSpec{ - AccessType: dbv1alpha1.NetworkAccessTypeRestricted, - IsMTLSConnectionRequired: common.Bool(isMTLSConnectionRequired), - AccessControlList: []string{"192.168.0.1"}, - } - - TestNetworkAccess(k8sClient, dbClient, adbLookupKey, networkRestrictedSpec)() + TestNetworkAccess(k8sClient, dbClient, adbLookupKey, nil, nil, acl, isMTLSConnectionRequired)() } } @@ -450,33 +445,17 @@ func TestNetworkAccessPrivate(k8sClient *client.Client, dbClient *database.Datab derefK8sClient := *k8sClient Expect(derefK8sClient.Get(context.TODO(), *adbLookupKey, adb)).Should(Succeed()) - networkPrivateSpec := dbv1alpha1.NetworkAccessSpec{ - AccessType: dbv1alpha1.NetworkAccessTypePrivate, - AccessControlList: []string{}, - IsMTLSConnectionRequired: common.Bool(isMTLSConnectionRequired), - PrivateEndpoint: dbv1alpha1.PrivateEndpointSpec{ - HostnamePrefix: adb.Spec.Details.DbName, - NsgOCIDs: []string{*nsgOCIDs}, - SubnetOCID: common.String(*subnetOCID), - }, - } - - TestNetworkAccess(k8sClient, dbClient, adbLookupKey, networkPrivateSpec)() + TestNetworkAccess(k8sClient, dbClient, adbLookupKey, subnetOCID, nsgOCIDs, nil, isMTLSConnectionRequired)() } } func TestNetworkAccessPublic(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName) func() { return func() { - networkPublicSpec := dbv1alpha1.NetworkAccessSpec{ - AccessType: dbv1alpha1.NetworkAccessTypePublic, - IsMTLSConnectionRequired: common.Bool(true), - } - - TestNetworkAccess(k8sClient, dbClient, adbLookupKey, networkPublicSpec)() + TestNetworkAccess(k8sClient, dbClient, adbLookupKey, nil, nil, nil, true)() } } -func TestNetworkAccess(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, networkSpec dbv1alpha1.NetworkAccessSpec) func() { +func TestNetworkAccess(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, subnetOCID *string, nsgOCIDs *string, acl []string, isMTLSConnectionRequired bool) func() { return func() { Expect(k8sClient).NotTo(BeNil()) Expect(dbClient).NotTo(BeNil()) @@ -488,7 +467,10 @@ func TestNetworkAccess(k8sClient *client.Client, dbClient *database.DatabaseClie AssertADBState(k8sClient, dbClient, adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)() Expect(derefK8sClient.Get(context.TODO(), *adbLookupKey, adb)).To(Succeed()) - adb.Spec.Details.NetworkAccess = networkSpec + adb.Spec.Details.SubnetId = subnetOCID + adb.Spec.Details.NsgIds = []string{*nsgOCIDs} + adb.Spec.Details.WhitelistedIps = acl + adb.Spec.Details.IsMtlsConnectionRequired = common.Bool(isMTLSConnectionRequired) Expect(derefK8sClient.Update(context.TODO(), adb)).To(Succeed()) AssertADBDetails(k8sClient, dbClient, adbLookupKey, adb)() } @@ -503,7 +485,7 @@ func UpdateAndAssertDetails(k8sClient *client.Client, dbClient *database.Databas expectedADB := UpdateDetails(k8sClient, dbClient, adbLookupKey, newSecretName, newAdminPassword)() AssertADBDetails(k8sClient, dbClient, adbLookupKey, expectedADB)() - ocid := expectedADB.Spec.Details.AutonomousDatabaseOCID + ocid := expectedADB.Spec.Details.Id tnsEntry := *expectedADB.Spec.Details.DbName + "_high" err := AssertAdminPassword(dbClient, ocid, &tnsEntry, newAdminPassword, walletPassword) Expect(err).ShouldNot(HaveOccurred()) @@ -547,7 +529,7 @@ func AssertHardLinkDelete(k8sClient *client.Client, dbClient *database.DatabaseC // Check every 10 secs for total 60 secs Eventually(func() (database.AutonomousDatabaseLifecycleStateEnum, error) { retryPolicy := e2eutil.NewLifecycleStateRetryPolicyADB(database.AutonomousDatabaseLifecycleStateTerminating) - return returnADBRemoteState(derefK8sClient, derefDBClient, adb.Spec.Details.AutonomousDatabaseOCID, &retryPolicy) + return returnADBRemoteState(derefK8sClient, derefDBClient, adb.Spec.Details.Id, &retryPolicy) }, changeTimeout).Should(Equal(database.AutonomousDatabaseLifecycleStateTerminating)) AssertSoftLinkDelete(k8sClient, adbLookupKey)() @@ -606,7 +588,7 @@ func AssertADBRemoteState(k8sClient *client.Client, dbClient *database.DatabaseC adb := &dbv1alpha1.AutonomousDatabase{} Expect(derefK8sClient.Get(context.TODO(), *adbLookupKey, adb)).To(Succeed()) By("Checking if the lifecycleState of remote resource is " + string(state)) - AssertADBRemoteStateOCID(k8sClient, dbClient, adb.Spec.Details.AutonomousDatabaseOCID, state, changeTimeout)() + AssertADBRemoteStateOCID(k8sClient, dbClient, adb.Spec.Details.Id, state, changeTimeout)() } } @@ -622,7 +604,7 @@ func AssertADBRemoteStateForBackupRestore(k8sClient *client.Client, dbClient *da adb := &dbv1alpha1.AutonomousDatabase{} Expect(derefK8sClient.Get(context.TODO(), *adbLookupKey, adb)).To(Succeed()) By("Checking if the lifecycleState of remote resource is " + string(state)) - AssertADBRemoteStateOCID(k8sClient, dbClient, adb.Spec.Details.AutonomousDatabaseOCID, state, backupTimeout)() + AssertADBRemoteStateOCID(k8sClient, dbClient, adb.Spec.Details.Id, state, backupTimeout)() } } @@ -655,8 +637,15 @@ func UpdateState(k8sClient *client.Client, adbLookupKey *types.NamespacedName, s adb := &dbv1alpha1.AutonomousDatabase{} Expect(derefK8sClient.Get(context.TODO(), *adbLookupKey, adb)).To(Succeed()) - adb.Spec.Details.LifecycleState = state By("Updating adb state to " + string(state)) + switch state { + case database.AutonomousDatabaseLifecycleStateAvailable: + adb.Spec.Action = "Start" + case database.AutonomousDatabaseLifecycleStateStopped: + adb.Spec.Action = "Stop" + case database.AutonomousDatabaseLifecycleStateTerminated: + adb.Spec.Action = "Terminate" + } Expect(derefK8sClient.Update(context.TODO(), adb)).To(Succeed()) } } diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go index a9fa06e5..9d9914f7 100644 --- a/test/e2e/suite_test.go +++ b/test/e2e/suite_test.go @@ -62,7 +62,7 @@ import ( databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" controllers "github.com/oracle/oracle-database-operator/controllers/database" - "github.com/oracle/oracle-database-operator/test/e2e/util" + e2eutil "github.com/oracle/oracle-database-operator/test/e2e/util" // +kubebuilder:scaffold:imports ) @@ -309,9 +309,9 @@ var _ = AfterSuite(func() { By(fmt.Sprintf("Found %d AutonomousDatabase(s)", len(adbList.Items))) for _, adb := range adbList.Items { - if adb.Spec.Details.AutonomousDatabaseOCID != nil { + if adb.Spec.Details.Id != nil { By("Terminating database " + *adb.Spec.Details.DbName) - Expect(e2eutil.DeleteAutonomousDatabase(dbClient, adb.Spec.Details.AutonomousDatabaseOCID)).Should(Succeed()) + Expect(e2eutil.DeleteAutonomousDatabase(dbClient, adb.Spec.Details.Id)).Should(Succeed()) } } From 88a2807a937e4aa1c9ec1b226984d0cdd1409e48 Mon Sep 17 00:00:00 2001 From: ting-lan-wang Date: Mon, 6 Jan 2025 22:18:17 +0800 Subject: [PATCH 145/414] Update doc for ADB Cloning --- .../samples/adb/autonomousdatabase_bind.yaml | 1 + .../samples/adb/autonomousdatabase_clone.yaml | 35 +++++++++++ .../adb/autonomousdatabase_create.yaml | 1 + .../adb/autonomousdatabase_rename.yaml | 1 + .../samples/adb/autonomousdatabase_scale.yaml | 1 + ...tonomousdatabase_stop_start_terminate.yaml | 4 +- ...onomousdatabase_update_admin_password.yaml | 1 + .../adb/autonomousdatabase_update_mtls.yaml | 1 + ...onomousdatabase_update_network_access.yaml | 1 + .../adb/autonomousdatabase_wallet.yaml | 1 + docs/adb/README.md | 62 +++++++++++++++++-- 11 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 config/samples/adb/autonomousdatabase_clone.yaml diff --git a/config/samples/adb/autonomousdatabase_bind.yaml b/config/samples/adb/autonomousdatabase_bind.yaml index 8d1de0fe..67f5c4b0 100644 --- a/config/samples/adb/autonomousdatabase_bind.yaml +++ b/config/samples/adb/autonomousdatabase_bind.yaml @@ -7,6 +7,7 @@ kind: AutonomousDatabase metadata: name: autonomousdatabase-sample spec: + action: Sync details: autonomousDatabaseOCID: ocid1.autonomousdatabase... # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. diff --git a/config/samples/adb/autonomousdatabase_clone.yaml b/config/samples/adb/autonomousdatabase_clone.yaml new file mode 100644 index 00000000..fbf6f99d --- /dev/null +++ b/config/samples/adb/autonomousdatabase_clone.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2022, 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v1alpha1 +kind: AutonomousDatabase +metadata: + name: autonomousdatabase-sample +spec: + action: Clone + details: + autonomousDatabaseOCID: ocid1.autonomousdatabase... + clone: + # Update compartmentOCID with your compartment OCID. + compartmentOCID: ocid1.compartment... OR ocid1.tenancy... + # The dbName must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. + dbName: ClonedADB + displayName: ClonedADB + cpuCoreCount: 1 + adminPassword: + # Comment out k8sSecret and uncomment ociSecret if you pass the admin password using OCI Secret. + k8sSecret: + # The Name of the K8s secret where you want to hold the password of the ADMIN account. + name: admin-password + # ociSecret: + # # The OCID of the OCI Secret that holds the password of the ADMIN account. It should start with ocid1.vaultsecret... . + # ocid: ocid1.vaultsecret... + dataStorageSizeInTBs: 1 + dbWorkload: OLTP + cloneType: METADATA + # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. + ociConfig: + configMapName: oci-cred + # Comment out secretName if using OKE workload identity + secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/adb/autonomousdatabase_create.yaml b/config/samples/adb/autonomousdatabase_create.yaml index aa77a94c..b9207370 100644 --- a/config/samples/adb/autonomousdatabase_create.yaml +++ b/config/samples/adb/autonomousdatabase_create.yaml @@ -7,6 +7,7 @@ kind: AutonomousDatabase metadata: name: autonomousdatabase-sample spec: + action: Create details: # Update compartmentOCID with your compartment OCID. compartmentOCID: ocid1.compartment... OR ocid1.tenancy... diff --git a/config/samples/adb/autonomousdatabase_rename.yaml b/config/samples/adb/autonomousdatabase_rename.yaml index d3a29998..24460ead 100644 --- a/config/samples/adb/autonomousdatabase_rename.yaml +++ b/config/samples/adb/autonomousdatabase_rename.yaml @@ -7,6 +7,7 @@ kind: AutonomousDatabase metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... # The database name diff --git a/config/samples/adb/autonomousdatabase_scale.yaml b/config/samples/adb/autonomousdatabase_scale.yaml index cd100675..e8abc206 100644 --- a/config/samples/adb/autonomousdatabase_scale.yaml +++ b/config/samples/adb/autonomousdatabase_scale.yaml @@ -7,6 +7,7 @@ kind: AutonomousDatabase metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... # Your database's OPCU core count diff --git a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml index a41f8d0b..cc41b380 100644 --- a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml +++ b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml @@ -7,10 +7,10 @@ kind: AutonomousDatabase metadata: name: autonomousdatabase-sample spec: + + action: Stop # Use the value "Start" to start the database details: autonomousDatabaseOCID: ocid1.autonomousdatabase... - # Change the lifecycleState to "AVAILABLE" to start the database - lifecycleState: STOPPED # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: configMapName: oci-cred diff --git a/config/samples/adb/autonomousdatabase_update_admin_password.yaml b/config/samples/adb/autonomousdatabase_update_admin_password.yaml index 67fd142d..534920f2 100644 --- a/config/samples/adb/autonomousdatabase_update_admin_password.yaml +++ b/config/samples/adb/autonomousdatabase_update_admin_password.yaml @@ -7,6 +7,7 @@ kind: AutonomousDatabase metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... adminPassword: diff --git a/config/samples/adb/autonomousdatabase_update_mtls.yaml b/config/samples/adb/autonomousdatabase_update_mtls.yaml index 93db0d8a..82eadd69 100644 --- a/config/samples/adb/autonomousdatabase_update_mtls.yaml +++ b/config/samples/adb/autonomousdatabase_update_mtls.yaml @@ -7,6 +7,7 @@ kind: AutonomousDatabase metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... networkAccess: diff --git a/config/samples/adb/autonomousdatabase_update_network_access.yaml b/config/samples/adb/autonomousdatabase_update_network_access.yaml index f0e98806..9577a823 100644 --- a/config/samples/adb/autonomousdatabase_update_network_access.yaml +++ b/config/samples/adb/autonomousdatabase_update_network_access.yaml @@ -7,6 +7,7 @@ kind: AutonomousDatabase metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... networkAccess: diff --git a/config/samples/adb/autonomousdatabase_wallet.yaml b/config/samples/adb/autonomousdatabase_wallet.yaml index 15fa6ca0..2159e36c 100644 --- a/config/samples/adb/autonomousdatabase_wallet.yaml +++ b/config/samples/adb/autonomousdatabase_wallet.yaml @@ -7,6 +7,7 @@ kind: AutonomousDatabase metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... wallet: diff --git a/docs/adb/README.md b/docs/adb/README.md index 1b59c4d6..9b06eef3 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -25,6 +25,7 @@ After you create the resource, you can use the operator to perform the following * [Download instance credentials (wallets)](#download-wallets) of an Autonomous Database * [Stop/Start/Terminate](#stopstartterminate) an Autonomous Database * [Delete the resource](#delete-the-resource) from the cluster +* [Clone](#clone-an-existing-autonomous-database) an existing Autonomous Database To debug the Oracle Autonomous Databases with Oracle Database operator, see [Debugging and troubleshooting](#debugging-and-troubleshooting) @@ -155,6 +156,7 @@ The operator also generates the `AutonomousBackup` custom resources if a databas name: autonomousdatabase-sample spec: details: + action: Sync autonomousDatabaseOCID: ocid1.autonomousdatabase... ociConfig: configMapName: oci-cred @@ -183,6 +185,7 @@ You can scale up or scale down the Oracle Autonomous Database OCPU core count or metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... cpuCoreCount: 2 @@ -215,6 +218,7 @@ You can rename the database by changing the values of the `dbName` and `displayN metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... dbName: RenamedADB @@ -257,6 +261,7 @@ You can rename the database by changing the values of the `dbName` and `displayN metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... adminPassword: @@ -301,6 +306,7 @@ A client Wallet is required to connect to a shared Oracle Autonomous Database. U metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... wallet: @@ -339,12 +345,12 @@ To use the secret in a deployment, refer to [Using Secrets](https://kubernetes.i > Note: this operation requires an `AutonomousDatabase` object to be in your cluster. This example assumes the provision operation or the bind operation has been done by the users and the operator is authorized with API Key Authentication. -To start, stop, or terminate a database, use the `lifecycleState` attribute. -Here's a list of the values you can set for `lifecycleState`: +To start, stop, or terminate a database, use the `action` attribute. +Here's a list of the values you can set for `action`: -* `AVAILABLE`: to start the database -* `STOPPED`: to stop the database -* `TERMINATED`: to terminate the database +* `START`: to start the database +* `STOP`: to stop the database +* `TERMINATE`: to terminate the database 1. An example .yaml file is available here: [config/samples/adb/autonomousdatabase_stop_start_terminate.yaml](./../../config/samples/adb/autonomousdatabase_stop_start_terminate.yaml) @@ -355,9 +361,9 @@ Here's a list of the values you can set for `lifecycleState`: metadata: name: autonomousdatabase-sample spec: + action: STOP details: autonomousDatabaseOCID: ocid1.autonomousdatabase... - lifecycleState: STOPPED ociConfig: configMapName: oci-cred secretName: oci-privatekey @@ -387,6 +393,7 @@ To delete the resource and terminate the Autonomous Database, complete these ste metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... hardLink: true @@ -411,6 +418,49 @@ To delete the resource and terminate the Autonomous Database, complete these ste Now, you can verify that the database is in TERMINATING state on the Cloud Console. +## Clone an existing Autonomous Database + +> Note: this operation requires an `AutonomousDatabase` object to be in your cluster. This example assumes the provision operation or the bind operation has been done by the users and the operator is authorized with API Key Authentication. + +To clone an existing Autonomous Database, complete these steps: + +1. An example YAML file is available here: [config/samples/adb/autonomousdatabase_clone.yaml](./../../config/samples/adb/autonomousdatabase_clone.yaml) + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + action: Clone + details: + autonomousDatabaseOCID: ocid1.autonomousdatabase... + clone: + compartmentOCID: ocid1.compartment... OR ocid1.tenancy... + dbName: ClonedADB + displayName: ClonedADB + cpuCoreCount: 1 + adminPassword: + k8sSecret: + name: admin-password + dataStorageSizeInTBs: 1 + dbWorkload: OLTP + cloneType: METADATA + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml + + ```sh + kubectl apply -f config/samples/adb/autonomousdatabase_clone.yaml + autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured + ``` + +Now, you can verify that a cloned database with name "ClonedADB" is being provisioned on the Cloud Console. + ## Roles and Privileges requirements for Oracle Autonomous Database Controller Autonomous Database controller uses Kubernetes objects such as: From 767329cd502d84eb0cc7de0be96087527e115b93 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Mon, 6 Jan 2025 21:07:39 +0000 Subject: [PATCH 146/414] observability/feature/log-exporter --- Makefile | 12 +- PROJECT | 22 + .../v1/databaseobserver_types.go | 193 + .../v1/databaseobserver_webhook.go | 185 + apis/observability/v1/groupversion_info.go | 58 + .../observability/v1/zz_generated.deepcopy.go | 471 + .../v1alpha1/databaseobserver_types.go | 81 +- .../v1alpha1/databaseobserver_webhook.go | 8 +- .../v1alpha1/zz_generated.deepcopy.go | 190 +- .../v4/databaseobserver_types.go | 194 + .../v4/databaseobserver_webhook.go | 182 + apis/observability/v4/groupversion_info.go | 58 + .../observability/v4/zz_generated.deepcopy.go | 471 + commons/observability/constants.go | 70 +- commons/observability/utils.go | 486 +- ...vability.oracle.com_databaseobservers.yaml | 5687 +++++++++- ...on_in_observability_databaseobservers.yaml | 8 + ...vability.oracle.com_databaseobservers.yaml | 9350 ++++++++++++++++- config/samples/kustomization.yaml | 10 + .../observability/databaseobserver.yaml | 44 - .../observability/v1/databaseobserver.yaml | 81 + ...databaseobserver_customization_fields.yaml | 54 + .../v1/databaseobserver_logs_promtail.yaml | 77 + .../v1alpha1/databaseobserver.yaml | 80 + .../databaseobserver_custom_config.yaml | 49 + .../databaseobserver_logs_promtail.yaml | 77 + .../databaseobserver_minimal.yaml | 8 +- .../databaseobserver_vault.yaml | 5 + .../observability/v4/databaseobserver.yaml | 79 + .../v4/databaseobserver_custom_config.yaml | 49 + .../v4/databaseobserver_logs_promtail.yaml | 79 + .../databaseobserver_minimal.yaml} | 14 +- .../v4/databaseobserver_vault.yaml | 39 + config/webhook/manifests.yaml | 81 + .../databaseobserver_controller.go | 293 +- .../databaseobserver_resource.go | 122 +- .../usecase01/oracle-database-operator.yaml | 4052 +++++++ docs/observability/README.md | 453 +- main.go | 13 + 39 files changed, 22834 insertions(+), 651 deletions(-) create mode 100644 apis/observability/v1/databaseobserver_types.go create mode 100644 apis/observability/v1/databaseobserver_webhook.go create mode 100644 apis/observability/v1/groupversion_info.go create mode 100644 apis/observability/v1/zz_generated.deepcopy.go create mode 100644 apis/observability/v4/databaseobserver_types.go create mode 100644 apis/observability/v4/databaseobserver_webhook.go create mode 100644 apis/observability/v4/groupversion_info.go create mode 100644 apis/observability/v4/zz_generated.deepcopy.go create mode 100644 config/crd/patches/cainjection_in_observability_databaseobservers.yaml delete mode 100644 config/samples/observability/databaseobserver.yaml create mode 100644 config/samples/observability/v1/databaseobserver.yaml create mode 100644 config/samples/observability/v1/databaseobserver_customization_fields.yaml create mode 100644 config/samples/observability/v1/databaseobserver_logs_promtail.yaml create mode 100644 config/samples/observability/v1alpha1/databaseobserver.yaml create mode 100644 config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml create mode 100644 config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml rename config/samples/observability/{ => v1alpha1}/databaseobserver_minimal.yaml (75%) rename config/samples/observability/{ => v1alpha1}/databaseobserver_vault.yaml (86%) create mode 100644 config/samples/observability/v4/databaseobserver.yaml create mode 100644 config/samples/observability/v4/databaseobserver_custom_config.yaml create mode 100644 config/samples/observability/v4/databaseobserver_logs_promtail.yaml rename config/samples/observability/{databaseobserver_custom_config.yaml => v4/databaseobserver_minimal.yaml} (57%) create mode 100644 config/samples/observability/v4/databaseobserver_vault.yaml create mode 100644 docs/multitenant/usecase01/oracle-database-operator.yaml diff --git a/Makefile b/Makefile index e762a618..0c3fd28a 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # - + # Current Operator version VERSION ?= 0.0.1 # Default bundle image tag @@ -32,16 +32,16 @@ GOBIN=$(shell go env GOPATH)/bin else GOBIN=$(shell go env GOBIN) endif - + # Setting SHELL to bash allows bash commands to be executed by recipes. # This is a requirement for 'setup-envtest.sh' in the test target. # Options are set to exit when a recipe line exits non-zero or a piped command fails. SHELL = /usr/bin/env bash -o pipefail .SHELLFLAGS = -ec - + all: build ##@ Development - + manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases @@ -110,7 +110,7 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) $(KUSTOMIZE) build config/default | kubectl apply -f - - + minikube-deploy: minikube-operator-yaml minikube-push kubectl apply -f $(OPERATOR_YAML) @@ -122,7 +122,7 @@ operator-yaml: manifests kustomize sed -i.bak -e '/^apiVersion: apps\/v1/,/---/d' "$(OPERATOR_YAML)" (echo --- && sed '/^apiVersion: apps\/v1/,/---/!d' "$(OPERATOR_YAML).bak") >> "$(OPERATOR_YAML)" rm "$(OPERATOR_YAML).bak" - + minikube-operator-yaml: IMG:=localhost:5000/$(IMG) minikube-operator-yaml: operator-yaml sed -i.bak 's/\(replicas.\) 3/\1 1/g' "$(OPERATOR_YAML)" diff --git a/PROJECT b/PROJECT index f90ecb57..1bb07e55 100644 --- a/PROJECT +++ b/PROJECT @@ -204,4 +204,26 @@ resources: kind: OrdsSrvs path: github.com/oracle/oracle-database-operator/apis/database/v4 version: v4 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: observability + kind: DatabaseObserver + path: github.com/oracle/oracle-database-operator/apis/observability/v1 + version: v1 + webhooks: + conversion: true + webhookVersion: v1beta1 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: observability + kind: DatabaseObserver + path: github.com/oracle/oracle-database-operator/apis/observability/v4 + version: v4 + webhooks: + conversion: true + webhookVersion: v1beta1 version: "3" diff --git a/apis/observability/v1/databaseobserver_types.go b/apis/observability/v1/databaseobserver_types.go new file mode 100644 index 00000000..d20b6417 --- /dev/null +++ b/apis/observability/v1/databaseobserver_types.go @@ -0,0 +1,193 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1 + +import ( + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type StatusEnum string + +// DatabaseObserverSpec defines the desired state of DatabaseObserver +type DatabaseObserverSpec struct { + Database DatabaseObserverDatabase `json:"database,omitempty"` + Exporter DatabaseObserverExporterConfig `json:"exporter,omitempty"` + ExporterConfig DatabaseObserverConfigMap `json:"configuration,omitempty"` + Prometheus PrometheusConfig `json:"prometheus,omitempty"` + OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + Replicas int32 `json:"replicas,omitempty"` + Log LogConfig `json:"log,omitempty"` + InheritLabels []string `json:"inheritLabels,omitempty"` + ExporterSidecars []corev1.Container `json:"sidecars,omitempty"` + SideCarVolumes []corev1.Volume `json:"sidecarVolumes,omitempty"` +} + +// LogConfig defines the configuration details relation to the logs of DatabaseObserver +type LogConfig struct { + Path string `json:"path,omitempty"` + Filename string `json:"filename,omitempty"` + Volume LogVolume `json:"volume,omitempty"` +} + +type LogVolume struct { + Name string `json:"name,omitempty"` + PersistentVolumeClaim LogVolumePVClaim `json:"persistentVolumeClaim,omitempty"` +} + +type LogVolumePVClaim struct { + ClaimName string `json:"claimName,omitempty"` +} + +// DatabaseObserverDatabase defines the database details used for DatabaseObserver +type DatabaseObserverDatabase struct { + DBUser DBSecret `json:"dbUser,omitempty"` + DBPassword DBSecretWithVault `json:"dbPassword,omitempty"` + DBWallet DBSecret `json:"dbWallet,omitempty"` + DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` +} + +// DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver +type DatabaseObserverExporterConfig struct { + Deployment DatabaseObserverDeployment `json:"deployment,omitempty"` + Service DatabaseObserverService `json:"service,omitempty"` +} + +// DatabaseObserverDeployment defines the exporter deployment component of DatabaseObserver +type DatabaseObserverDeployment struct { + ExporterImage string `json:"image,omitempty"` + ExporterArgs []string `json:"args,omitempty"` + ExporterCommands []string `json:"commands,omitempty"` + ExporterEnvs map[string]string `json:"env,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` +} + +// DeploymentPodTemplate defines the labels for the DatabaseObserver pods component of a deployment +type DeploymentPodTemplate struct { + Labels map[string]string `json:"labels,omitempty"` +} + +// DatabaseObserverService defines the exporter service component of DatabaseObserver +type DatabaseObserverService struct { + Ports []corev1.ServicePort `json:"ports,omitempty"` + Labels map[string]string `json:"labels,omitempty"` +} + +// PrometheusConfig defines the generated resources for Prometheus +type PrometheusConfig struct { + ServiceMonitor PrometheusServiceMonitor `json:"serviceMonitor,omitempty"` +} + +// PrometheusServiceMonitor defines DatabaseObserver servicemonitor spec +type PrometheusServiceMonitor struct { + Labels map[string]string `json:"labels,omitempty"` + NamespaceSelector *monitorv1.NamespaceSelector `json:"namespaceSelector,omitempty"` + Endpoints []monitorv1.Endpoint `json:"endpoints,omitempty"` +} + +// DBSecret defines secrets used in reference +type DBSecret struct { + Key string `json:"key,omitempty"` + SecretName string `json:"secret,omitempty"` +} + +// DBSecretWithVault defines secrets used in reference with vault fields +type DBSecretWithVault struct { + Key string `json:"key,omitempty"` + SecretName string `json:"secret,omitempty"` + VaultOCID string `json:"vaultOCID,omitempty"` + VaultSecretName string `json:"vaultSecretName,omitempty"` +} + +// DatabaseObserverConfigMap defines configMap used for metrics configuration +type DatabaseObserverConfigMap struct { + Configmap ConfigMapDetails `json:"configMap,omitempty"` +} + +// ConfigMapDetails defines the configmap name +type ConfigMapDetails struct { + Key string `json:"key,omitempty"` + Name string `json:"name,omitempty"` +} + +// OCIConfigSpec defines the configmap name and secret name used for connecting to OCI +type OCIConfigSpec struct { + ConfigMapName string `json:"configMapName,omitempty"` + SecretName string `json:"secretName,omitempty"` +} + +// DatabaseObserverStatus defines the observed state of DatabaseObserver +type DatabaseObserverStatus struct { + Conditions []metav1.Condition `json:"conditions"` + Status string `json:"status,omitempty"` + ExporterConfig string `json:"exporterConfig"` + Version string `json:"version"` + Replicas int `json:"replicas,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +// +kubebuilder:resource:shortName="dbobserver";"dbobservers" + +// DatabaseObserver is the Schema for the databaseobservers API +// +kubebuilder:printcolumn:JSONPath=".status.exporterConfig",name="ExporterConfig",type=string +// +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string +// +kubebuilder:printcolumn:JSONPath=".status.version",name="Version",type=string +type DatabaseObserver struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DatabaseObserverSpec `json:"spec,omitempty"` + Status DatabaseObserverStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// DatabaseObserverList contains a list of DatabaseObserver +type DatabaseObserverList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DatabaseObserver `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DatabaseObserver{}, &DatabaseObserverList{}) +} diff --git a/apis/observability/v1/databaseobserver_webhook.go b/apis/observability/v1/databaseobserver_webhook.go new file mode 100644 index 00000000..286d6ed6 --- /dev/null +++ b/apis/observability/v1/databaseobserver_webhook.go @@ -0,0 +1,185 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1 + +import ( + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "strings" +) + +// log is for logging in this package. +var databaseobserverlog = logf.Log.WithName("databaseobserver-resource") + +const ( + AllowedExporterImage = "container-registry.oracle.com/database/observability-exporter" + ErrorSpecValidationMissingConnString = "a required field for database connection string secret is missing or does not have a value" + ErrorSpecValidationMissingDBUser = "a required field for database user secret is missing or does not have a value" + ErrorSpecValidationMissingDBVaultField = "a field for the OCI vault has a value but the other required field is missing or does not have a value" + ErrorSpecValidationMissingOCIConfig = "a field(s) for the OCI Config is missing or does not have a value when fields for the OCI vault has values" + ErrorSpecValidationMissingDBPasswordSecret = "a required field for the database password secret is missing or does not have a value" + ErrorSpecExporterImageNotAllowed = "a different exporter image was found, only official database exporter container images are currently supported" +) + +func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +//+kubebuilder:webhook:path=/mutate-observability-oracle-com-v1-databaseobserver,mutating=true,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,verbs=create;update,versions=v1,name=mdatabaseobserver.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &DatabaseObserver{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *DatabaseObserver) Default() { + databaseobserverlog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:verbs=create;update,path=/validate-observability-oracle-com-v1-databaseobserver,mutating=false,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,versions=v1,name=vdatabaseobserver.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &DatabaseObserver{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { + databaseobserverlog.Info("validate create", "name", r.Name) + + var e field.ErrorList + ns := dbcommons.GetWatchNamespaces() + + // Check for namespace/cluster scope access + if _, isDesiredNamespaceWithinScope := ns[r.Namespace]; !isDesiredNamespaceWithinScope && len(ns) > 0 { + e = append(e, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + + // Check required secret for db user has value + if r.Spec.Database.DBUser.SecretName == "" { + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbUser").Child("secret"), r.Spec.Database.DBUser.SecretName, + ErrorSpecValidationMissingDBUser)) + } + + // Check required secret for db connection string has value + if r.Spec.Database.DBConnectionString.SecretName == "" { + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbConnectionString").Child("secret"), r.Spec.Database.DBConnectionString.SecretName, + ErrorSpecValidationMissingConnString)) + } + + // The other vault field must have value if one does + if (r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName == "") || + (r.Spec.Database.DBPassword.VaultSecretName != "" && r.Spec.Database.DBPassword.VaultOCID == "") { + + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword"), r.Spec.Database.DBPassword, + ErrorSpecValidationMissingDBVaultField)) + } + + // if vault fields have value, ociConfig must have values + if r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName != "" && + (r.Spec.OCIConfig.SecretName == "" || r.Spec.OCIConfig.ConfigMapName == "") { + + e = append(e, + field.Invalid(field.NewPath("spec").Child("ociConfig"), r.Spec.OCIConfig, + ErrorSpecValidationMissingOCIConfig)) + } + + // If all of {DB Password Secret Name and vaultOCID+vaultSecretName} have no value, then error out + if r.Spec.Database.DBPassword.SecretName == "" && + r.Spec.Database.DBPassword.VaultOCID == "" && + r.Spec.Database.DBPassword.VaultSecretName == "" { + + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword").Child("secret"), r.Spec.Database.DBPassword.SecretName, + ErrorSpecValidationMissingDBPasswordSecret)) + } + + // disallow usage of any other image than the observability-exporter + if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { + e = append(e, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, + ErrorSpecExporterImageNotAllowed)) + } + + // Return if any errors + if len(e) > 0 { + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + } + return nil, nil + +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + databaseobserverlog.Info("validate update", "name", r.Name) + var e field.ErrorList + + // disallow usage of any other image than the observability-exporter + if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { + e = append(e, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, + ErrorSpecExporterImageNotAllowed)) + } + // Return if any errors + if len(e) > 0 { + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateDelete() (admission.Warnings, error) { + databaseobserverlog.Info("validate delete", "name", r.Name) + + return nil, nil +} diff --git a/apis/observability/v1/groupversion_info.go b/apis/observability/v1/groupversion_info.go new file mode 100644 index 00000000..3f332c05 --- /dev/null +++ b/apis/observability/v1/groupversion_info.go @@ -0,0 +1,58 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Package v1 contains API Schema definitions for the observability v1 API group +// +kubebuilder:object:generate=true +// +groupName=observability.oracle.com +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "observability.oracle.com", Version: "v1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/observability/v1/zz_generated.deepcopy.go b/apis/observability/v1/zz_generated.deepcopy.go new file mode 100644 index 00000000..ac8c88ce --- /dev/null +++ b/apis/observability/v1/zz_generated.deepcopy.go @@ -0,0 +1,471 @@ +//go:build !ignore_autogenerated + +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +import ( + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapDetails) DeepCopyInto(out *ConfigMapDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapDetails. +func (in *ConfigMapDetails) DeepCopy() *ConfigMapDetails { + if in == nil { + return nil + } + out := new(ConfigMapDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBSecret) DeepCopyInto(out *DBSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecret. +func (in *DBSecret) DeepCopy() *DBSecret { + if in == nil { + return nil + } + out := new(DBSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBSecretWithVault) DeepCopyInto(out *DBSecretWithVault) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecretWithVault. +func (in *DBSecretWithVault) DeepCopy() *DBSecretWithVault { + if in == nil { + return nil + } + out := new(DBSecretWithVault) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserver) DeepCopyInto(out *DatabaseObserver) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserver. +func (in *DatabaseObserver) DeepCopy() *DatabaseObserver { + if in == nil { + return nil + } + out := new(DatabaseObserver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatabaseObserver) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverConfigMap) DeepCopyInto(out *DatabaseObserverConfigMap) { + *out = *in + out.Configmap = in.Configmap +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverConfigMap. +func (in *DatabaseObserverConfigMap) DeepCopy() *DatabaseObserverConfigMap { + if in == nil { + return nil + } + out := new(DatabaseObserverConfigMap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverDatabase) DeepCopyInto(out *DatabaseObserverDatabase) { + *out = *in + out.DBUser = in.DBUser + out.DBPassword = in.DBPassword + out.DBWallet = in.DBWallet + out.DBConnectionString = in.DBConnectionString +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDatabase. +func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { + if in == nil { + return nil + } + out := new(DatabaseObserverDatabase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverDeployment) DeepCopyInto(out *DatabaseObserverDeployment) { + *out = *in + if in.ExporterArgs != nil { + in, out := &in.ExporterArgs, &out.ExporterArgs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterCommands != nil { + in, out := &in.ExporterCommands, &out.ExporterCommands + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterEnvs != nil { + in, out := &in.ExporterEnvs, &out.ExporterEnvs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.DeploymentPodTemplate.DeepCopyInto(&out.DeploymentPodTemplate) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDeployment. +func (in *DatabaseObserverDeployment) DeepCopy() *DatabaseObserverDeployment { + if in == nil { + return nil + } + out := new(DatabaseObserverDeployment) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverExporterConfig) DeepCopyInto(out *DatabaseObserverExporterConfig) { + *out = *in + in.Deployment.DeepCopyInto(&out.Deployment) + in.Service.DeepCopyInto(&out.Service) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverExporterConfig. +func (in *DatabaseObserverExporterConfig) DeepCopy() *DatabaseObserverExporterConfig { + if in == nil { + return nil + } + out := new(DatabaseObserverExporterConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverList) DeepCopyInto(out *DatabaseObserverList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DatabaseObserver, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverList. +func (in *DatabaseObserverList) DeepCopy() *DatabaseObserverList { + if in == nil { + return nil + } + out := new(DatabaseObserverList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatabaseObserverList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { + *out = *in + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]corev1.ServicePort, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverService. +func (in *DatabaseObserverService) DeepCopy() *DatabaseObserverService { + if in == nil { + return nil + } + out := new(DatabaseObserverService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { + *out = *in + out.Database = in.Database + in.Exporter.DeepCopyInto(&out.Exporter) + out.ExporterConfig = in.ExporterConfig + in.Prometheus.DeepCopyInto(&out.Prometheus) + out.OCIConfig = in.OCIConfig + out.Log = in.Log + if in.InheritLabels != nil { + in, out := &in.InheritLabels, &out.InheritLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterSidecars != nil { + in, out := &in.ExporterSidecars, &out.ExporterSidecars + *out = make([]corev1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SideCarVolumes != nil { + in, out := &in.SideCarVolumes, &out.SideCarVolumes + *out = make([]corev1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. +func (in *DatabaseObserverSpec) DeepCopy() *DatabaseObserverSpec { + if in == nil { + return nil + } + out := new(DatabaseObserverSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverStatus. +func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { + if in == nil { + return nil + } + out := new(DatabaseObserverStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentPodTemplate. +func (in *DeploymentPodTemplate) DeepCopy() *DeploymentPodTemplate { + if in == nil { + return nil + } + out := new(DeploymentPodTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogConfig) DeepCopyInto(out *LogConfig) { + *out = *in + out.Volume = in.Volume +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogConfig. +func (in *LogConfig) DeepCopy() *LogConfig { + if in == nil { + return nil + } + out := new(LogConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogVolume) DeepCopyInto(out *LogVolume) { + *out = *in + out.PersistentVolumeClaim = in.PersistentVolumeClaim +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolume. +func (in *LogVolume) DeepCopy() *LogVolume { + if in == nil { + return nil + } + out := new(LogVolume) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogVolumePVClaim) DeepCopyInto(out *LogVolumePVClaim) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolumePVClaim. +func (in *LogVolumePVClaim) DeepCopy() *LogVolumePVClaim { + if in == nil { + return nil + } + out := new(LogVolumePVClaim) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfigSpec. +func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { + if in == nil { + return nil + } + out := new(OCIConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrometheusConfig) DeepCopyInto(out *PrometheusConfig) { + *out = *in + in.ServiceMonitor.DeepCopyInto(&out.ServiceMonitor) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusConfig. +func (in *PrometheusConfig) DeepCopy() *PrometheusConfig { + if in == nil { + return nil + } + out := new(PrometheusConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrometheusServiceMonitor) DeepCopyInto(out *PrometheusServiceMonitor) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(monitoringv1.NamespaceSelector) + (*in).DeepCopyInto(*out) + } + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]monitoringv1.Endpoint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusServiceMonitor. +func (in *PrometheusServiceMonitor) DeepCopy() *PrometheusServiceMonitor { + if in == nil { + return nil + } + out := new(PrometheusServiceMonitor) + in.DeepCopyInto(out) + return out +} diff --git a/apis/observability/v1alpha1/databaseobserver_types.go b/apis/observability/v1alpha1/databaseobserver_types.go index 97827d17..447cdbf2 100644 --- a/apis/observability/v1alpha1/databaseobserver_types.go +++ b/apis/observability/v1alpha1/databaseobserver_types.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2022 Oracle and/or its affiliates. +** Copyright (c) 2024 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -39,6 +39,8 @@ package v1alpha1 import ( + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -46,11 +48,32 @@ type StatusEnum string // DatabaseObserverSpec defines the desired state of DatabaseObserver type DatabaseObserverSpec struct { - Database DatabaseObserverDatabase `json:"database,omitempty"` - Exporter DatabaseObserverExporterConfig `json:"exporter,omitempty"` - Prometheus PrometheusConfig `json:"prometheus,omitempty"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` - Replicas int32 `json:"replicas,omitempty"` + Database DatabaseObserverDatabase `json:"database,omitempty"` + Exporter DatabaseObserverExporterConfig `json:"exporter,omitempty"` + ExporterConfig DatabaseObserverConfigMap `json:"configuration,omitempty"` + Prometheus PrometheusConfig `json:"prometheus,omitempty"` + OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + Replicas int32 `json:"replicas,omitempty"` + Log LogConfig `json:"log,omitempty"` + InheritLabels []string `json:"inheritLabels,omitempty"` + ExporterSidecars []corev1.Container `json:"sidecars,omitempty"` + SideCarVolumes []corev1.Volume `json:"sidecarVolumes,omitempty"` +} + +// LogConfig defines the configuration details relation to the logs of DatabaseObserver +type LogConfig struct { + Path string `json:"path,omitempty"` + Filename string `json:"filename,omitempty"` + Volume LogVolume `json:"volume,omitempty"` +} + +type LogVolume struct { + Name string `json:"name,omitempty"` + PersistentVolumeClaim LogVolumePVClaim `json:"persistentVolumeClaim,omitempty"` +} + +type LogVolumePVClaim struct { + ClaimName string `json:"claimName,omitempty"` } // DatabaseObserverDatabase defines the database details used for DatabaseObserver @@ -63,27 +86,50 @@ type DatabaseObserverDatabase struct { // DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver type DatabaseObserverExporterConfig struct { - ExporterImage string `json:"image,omitempty"` - ExporterConfig DatabaseObserverConfigMap `json:"configuration,omitempty"` - Service DatabaseObserverService `json:"service,omitempty"` + Deployment DatabaseObserverDeployment `json:"deployment,omitempty"` + Service DatabaseObserverService `json:"service,omitempty"` +} + +// DatabaseObserverDeployment defines the exporter deployment component of DatabaseObserver +type DatabaseObserverDeployment struct { + ExporterImage string `json:"image,omitempty"` + ExporterArgs []string `json:"args,omitempty"` + ExporterCommands []string `json:"commands,omitempty"` + ExporterEnvs map[string]string `json:"env,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` +} + +// DeploymentPodTemplate defines the labels for the DatabaseObserver pods component of a deployment +type DeploymentPodTemplate struct { + Labels map[string]string `json:"labels,omitempty"` } // DatabaseObserverService defines the exporter service component of DatabaseObserver type DatabaseObserverService struct { - Port int32 `json:"port,omitempty"` + Ports []corev1.ServicePort `json:"ports,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } // PrometheusConfig defines the generated resources for Prometheus type PrometheusConfig struct { - Labels map[string]string `json:"labels,omitempty"` - Port string `json:"port,omitempty"` + ServiceMonitor PrometheusServiceMonitor `json:"serviceMonitor,omitempty"` +} + +// PrometheusServiceMonitor defines DatabaseObserver servicemonitor spec +type PrometheusServiceMonitor struct { + Labels map[string]string `json:"labels,omitempty"` + NamespaceSelector *monitorv1.NamespaceSelector `json:"namespaceSelector,omitempty"` + Endpoints []monitorv1.Endpoint `json:"endpoints,omitempty"` } +// DBSecret defines secrets used in reference type DBSecret struct { Key string `json:"key,omitempty"` SecretName string `json:"secret,omitempty"` } +// DBSecretWithVault defines secrets used in reference with vault fields type DBSecretWithVault struct { Key string `json:"key,omitempty"` SecretName string `json:"secret,omitempty"` @@ -91,16 +137,18 @@ type DBSecretWithVault struct { VaultSecretName string `json:"vaultSecretName,omitempty"` } +// DatabaseObserverConfigMap defines configMap used for metrics configuration type DatabaseObserverConfigMap struct { - Configmap ConfigMapDetails `json:"configmap,omitempty"` + Configmap ConfigMapDetails `json:"configMap,omitempty"` } // ConfigMapDetails defines the configmap name type ConfigMapDetails struct { Key string `json:"key,omitempty"` - Name string `json:"configmapName,omitempty"` + Name string `json:"name,omitempty"` } +// OCIConfigSpec defines the configmap name and secret name used for connecting to OCI type OCIConfigSpec struct { ConfigMapName string `json:"configMapName,omitempty"` SecretName string `json:"secretName,omitempty"` @@ -108,20 +156,21 @@ type OCIConfigSpec struct { // DatabaseObserverStatus defines the observed state of DatabaseObserver type DatabaseObserverStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file Conditions []metav1.Condition `json:"conditions"` Status string `json:"status,omitempty"` ExporterConfig string `json:"exporterConfig"` + Version string `json:"version"` Replicas int `json:"replicas,omitempty"` } //+kubebuilder:object:root=true //+kubebuilder:subresource:status +// +kubebuilder:resource:shortName="dbobserver";"dbobservers" // DatabaseObserver is the Schema for the databaseobservers API // +kubebuilder:printcolumn:JSONPath=".status.exporterConfig",name="ExporterConfig",type=string // +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string +// +kubebuilder:printcolumn:JSONPath=".status.version",name="Version",type=string type DatabaseObserver struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/apis/observability/v1alpha1/databaseobserver_webhook.go b/apis/observability/v1alpha1/databaseobserver_webhook.go index 2ab9b732..585ad3bf 100644 --- a/apis/observability/v1alpha1/databaseobserver_webhook.go +++ b/apis/observability/v1alpha1/databaseobserver_webhook.go @@ -145,9 +145,9 @@ func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { } // disallow usage of any other image than the observability-exporter - if r.Spec.Exporter.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.ExporterImage, AllowedExporterImage) { + if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.ExporterImage, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, ErrorSpecExporterImageNotAllowed)) } @@ -165,9 +165,9 @@ func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warning var e field.ErrorList // disallow usage of any other image than the observability-exporter - if r.Spec.Exporter.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.ExporterImage, AllowedExporterImage) { + if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.ExporterImage, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, ErrorSpecExporterImageNotAllowed)) } // Return if any errors diff --git a/apis/observability/v1alpha1/zz_generated.deepcopy.go b/apis/observability/v1alpha1/zz_generated.deepcopy.go index 23a349c0..daca675c 100644 --- a/apis/observability/v1alpha1/zz_generated.deepcopy.go +++ b/apis/observability/v1alpha1/zz_generated.deepcopy.go @@ -43,7 +43,9 @@ package v1alpha1 import ( - "k8s.io/apimachinery/pkg/apis/meta/v1" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -154,11 +156,51 @@ func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverDeployment) DeepCopyInto(out *DatabaseObserverDeployment) { + *out = *in + if in.ExporterArgs != nil { + in, out := &in.ExporterArgs, &out.ExporterArgs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterCommands != nil { + in, out := &in.ExporterCommands, &out.ExporterCommands + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterEnvs != nil { + in, out := &in.ExporterEnvs, &out.ExporterEnvs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.DeploymentPodTemplate.DeepCopyInto(&out.DeploymentPodTemplate) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDeployment. +func (in *DatabaseObserverDeployment) DeepCopy() *DatabaseObserverDeployment { + if in == nil { + return nil + } + out := new(DatabaseObserverDeployment) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatabaseObserverExporterConfig) DeepCopyInto(out *DatabaseObserverExporterConfig) { *out = *in - out.ExporterConfig = in.ExporterConfig - out.Service = in.Service + in.Deployment.DeepCopyInto(&out.Deployment) + in.Service.DeepCopyInto(&out.Service) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverExporterConfig. @@ -206,6 +248,20 @@ func (in *DatabaseObserverList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { *out = *in + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]v1.ServicePort, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverService. @@ -222,9 +278,30 @@ func (in *DatabaseObserverService) DeepCopy() *DatabaseObserverService { func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { *out = *in out.Database = in.Database - out.Exporter = in.Exporter + in.Exporter.DeepCopyInto(&out.Exporter) + out.ExporterConfig = in.ExporterConfig in.Prometheus.DeepCopyInto(&out.Prometheus) out.OCIConfig = in.OCIConfig + out.Log = in.Log + if in.InheritLabels != nil { + in, out := &in.InheritLabels, &out.InheritLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterSidecars != nil { + in, out := &in.ExporterSidecars, &out.ExporterSidecars + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SideCarVolumes != nil { + in, out := &in.SideCarVolumes, &out.SideCarVolumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. @@ -242,7 +319,7 @@ func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -259,6 +336,75 @@ func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentPodTemplate. +func (in *DeploymentPodTemplate) DeepCopy() *DeploymentPodTemplate { + if in == nil { + return nil + } + out := new(DeploymentPodTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogConfig) DeepCopyInto(out *LogConfig) { + *out = *in + out.Volume = in.Volume +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogConfig. +func (in *LogConfig) DeepCopy() *LogConfig { + if in == nil { + return nil + } + out := new(LogConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogVolume) DeepCopyInto(out *LogVolume) { + *out = *in + out.PersistentVolumeClaim = in.PersistentVolumeClaim +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolume. +func (in *LogVolume) DeepCopy() *LogVolume { + if in == nil { + return nil + } + out := new(LogVolume) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogVolumePVClaim) DeepCopyInto(out *LogVolumePVClaim) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolumePVClaim. +func (in *LogVolumePVClaim) DeepCopy() *LogVolumePVClaim { + if in == nil { + return nil + } + out := new(LogVolumePVClaim) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { *out = *in @@ -276,6 +422,22 @@ func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PrometheusConfig) DeepCopyInto(out *PrometheusConfig) { + *out = *in + in.ServiceMonitor.DeepCopyInto(&out.ServiceMonitor) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusConfig. +func (in *PrometheusConfig) DeepCopy() *PrometheusConfig { + if in == nil { + return nil + } + out := new(PrometheusConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrometheusServiceMonitor) DeepCopyInto(out *PrometheusServiceMonitor) { *out = *in if in.Labels != nil { in, out := &in.Labels, &out.Labels @@ -284,14 +446,26 @@ func (in *PrometheusConfig) DeepCopyInto(out *PrometheusConfig) { (*out)[key] = val } } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(monitoringv1.NamespaceSelector) + (*in).DeepCopyInto(*out) + } + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]monitoringv1.Endpoint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusConfig. -func (in *PrometheusConfig) DeepCopy() *PrometheusConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusServiceMonitor. +func (in *PrometheusServiceMonitor) DeepCopy() *PrometheusServiceMonitor { if in == nil { return nil } - out := new(PrometheusConfig) + out := new(PrometheusServiceMonitor) in.DeepCopyInto(out) return out } diff --git a/apis/observability/v4/databaseobserver_types.go b/apis/observability/v4/databaseobserver_types.go new file mode 100644 index 00000000..246be339 --- /dev/null +++ b/apis/observability/v4/databaseobserver_types.go @@ -0,0 +1,194 @@ +/* +** Copyright (c) 2024 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type StatusEnum string + +// DatabaseObserverSpec defines the desired state of DatabaseObserver +type DatabaseObserverSpec struct { + Database DatabaseObserverDatabase `json:"database,omitempty"` + Exporter DatabaseObserverExporterConfig `json:"exporter,omitempty"` + ExporterConfig DatabaseObserverConfigMap `json:"configuration,omitempty"` + Prometheus PrometheusConfig `json:"prometheus,omitempty"` + OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` + Replicas int32 `json:"replicas,omitempty"` + Log LogConfig `json:"log,omitempty"` + InheritLabels []string `json:"inheritLabels,omitempty"` + ExporterSidecars []corev1.Container `json:"sidecars,omitempty"` + SideCarVolumes []corev1.Volume `json:"sidecarVolumes,omitempty"` +} + +// LogConfig defines the configuration details relation to the logs of DatabaseObserver +type LogConfig struct { + Path string `json:"path,omitempty"` + Filename string `json:"filename,omitempty"` + Volume LogVolume `json:"volume,omitempty"` +} + +type LogVolume struct { + Name string `json:"name,omitempty"` + PersistentVolumeClaim LogVolumePVClaim `json:"persistentVolumeClaim,omitempty"` +} + +type LogVolumePVClaim struct { + ClaimName string `json:"claimName,omitempty"` +} + +// DatabaseObserverDatabase defines the database details used for DatabaseObserver +type DatabaseObserverDatabase struct { + DBUser DBSecret `json:"dbUser,omitempty"` + DBPassword DBSecretWithVault `json:"dbPassword,omitempty"` + DBWallet DBSecret `json:"dbWallet,omitempty"` + DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` +} + +// DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver +type DatabaseObserverExporterConfig struct { + Deployment DatabaseObserverDeployment `json:"deployment,omitempty"` + Service DatabaseObserverService `json:"service,omitempty"` +} + +// DatabaseObserverDeployment defines the exporter deployment component of DatabaseObserver +type DatabaseObserverDeployment struct { + ExporterImage string `json:"image,omitempty"` + ExporterArgs []string `json:"args,omitempty"` + ExporterCommands []string `json:"commands,omitempty"` + ExporterEnvs map[string]string `json:"env,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` +} + +// DeploymentPodTemplate defines the labels for the DatabaseObserver pods component of a deployment +type DeploymentPodTemplate struct { + Labels map[string]string `json:"labels,omitempty"` +} + +// DatabaseObserverService defines the exporter service component of DatabaseObserver +type DatabaseObserverService struct { + Ports []corev1.ServicePort `json:"ports,omitempty"` + Labels map[string]string `json:"labels,omitempty"` +} + +// PrometheusConfig defines the generated resources for Prometheus +type PrometheusConfig struct { + ServiceMonitor PrometheusServiceMonitor `json:"serviceMonitor,omitempty"` +} + +// PrometheusServiceMonitor defines DatabaseObserver servicemonitor spec +type PrometheusServiceMonitor struct { + Labels map[string]string `json:"labels,omitempty"` + NamespaceSelector *monitorv1.NamespaceSelector `json:"namespaceSelector,omitempty"` + Endpoints []monitorv1.Endpoint `json:"endpoints,omitempty"` +} + +// DBSecret defines secrets used in reference +type DBSecret struct { + Key string `json:"key,omitempty"` + SecretName string `json:"secret,omitempty"` +} + +// DBSecretWithVault defines secrets used in reference with vault fields +type DBSecretWithVault struct { + Key string `json:"key,omitempty"` + SecretName string `json:"secret,omitempty"` + VaultOCID string `json:"vaultOCID,omitempty"` + VaultSecretName string `json:"vaultSecretName,omitempty"` +} + +// DatabaseObserverConfigMap defines configMap used for metrics configuration +type DatabaseObserverConfigMap struct { + Configmap ConfigMapDetails `json:"configMap,omitempty"` +} + +// ConfigMapDetails defines the configmap name +type ConfigMapDetails struct { + Key string `json:"key,omitempty"` + Name string `json:"name,omitempty"` +} + +// OCIConfigSpec defines the configmap name and secret name used for connecting to OCI +type OCIConfigSpec struct { + ConfigMapName string `json:"configMapName,omitempty"` + SecretName string `json:"secretName,omitempty"` +} + +// DatabaseObserverStatus defines the observed state of DatabaseObserver +type DatabaseObserverStatus struct { + Conditions []metav1.Condition `json:"conditions"` + Status string `json:"status,omitempty"` + ExporterConfig string `json:"exporterConfig"` + Version string `json:"version"` + Replicas int `json:"replicas,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +// +kubebuilder:resource:shortName="dbobserver";"dbobservers" + +// DatabaseObserver is the Schema for the databaseobservers API +// +kubebuilder:printcolumn:JSONPath=".status.exporterConfig",name="ExporterConfig",type=string +// +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string +// +kubebuilder:printcolumn:JSONPath=".status.version",name="Version",type=string +// +kubebuilder:storageversion +type DatabaseObserver struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DatabaseObserverSpec `json:"spec,omitempty"` + Status DatabaseObserverStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// DatabaseObserverList contains a list of DatabaseObserver +type DatabaseObserverList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DatabaseObserver `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DatabaseObserver{}, &DatabaseObserverList{}) +} diff --git a/apis/observability/v4/databaseobserver_webhook.go b/apis/observability/v4/databaseobserver_webhook.go new file mode 100644 index 00000000..c0a5d8b7 --- /dev/null +++ b/apis/observability/v4/databaseobserver_webhook.go @@ -0,0 +1,182 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "strings" +) + +// log is for logging in this package. +var databaseobserverlog = logf.Log.WithName("databaseobserver-resource") + +const ( + AllowedExporterImage = "container-registry.oracle.com/database/observability-exporter" + ErrorSpecValidationMissingConnString = "a required field for database connection string secret is missing or does not have a value" + ErrorSpecValidationMissingDBUser = "a required field for database user secret is missing or does not have a value" + ErrorSpecValidationMissingDBVaultField = "a field for the OCI vault has a value but the other required field is missing or does not have a value" + ErrorSpecValidationMissingOCIConfig = "a field(s) for the OCI Config is missing or does not have a value when fields for the OCI vault has values" + ErrorSpecValidationMissingDBPasswordSecret = "a required field for the database password secret is missing or does not have a value" + ErrorSpecExporterImageNotAllowed = "a different exporter image was found, only official database exporter container images are currently supported" +) + +func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:path=/mutate-observability-oracle-com-v4-databaseobserver,mutating=true,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,verbs=create;update,versions=v4,name=mdatabaseobserver.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &DatabaseObserver{} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *DatabaseObserver) Default() { + databaseobserverlog.Info("default", "name", r.Name) +} + +//+kubebuilder:webhook:verbs=create;update,path=/validate-observability-oracle-com-v4-databaseobserver,mutating=false,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,versions=v4,name=vdatabaseobserver.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &DatabaseObserver{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { + databaseobserverlog.Info("validate create", "name", r.Name) + + var e field.ErrorList + ns := dbcommons.GetWatchNamespaces() + + // Check for namespace/cluster scope access + if _, isDesiredNamespaceWithinScope := ns[r.Namespace]; !isDesiredNamespaceWithinScope && len(ns) > 0 { + e = append(e, + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + + // Check required secret for db user has value + if r.Spec.Database.DBUser.SecretName == "" { + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbUser").Child("secret"), r.Spec.Database.DBUser.SecretName, + ErrorSpecValidationMissingDBUser)) + } + + // Check required secret for db connection string has value + if r.Spec.Database.DBConnectionString.SecretName == "" { + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbConnectionString").Child("secret"), r.Spec.Database.DBConnectionString.SecretName, + ErrorSpecValidationMissingConnString)) + } + + // The other vault field must have value if one does + if (r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName == "") || + (r.Spec.Database.DBPassword.VaultSecretName != "" && r.Spec.Database.DBPassword.VaultOCID == "") { + + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword"), r.Spec.Database.DBPassword, + ErrorSpecValidationMissingDBVaultField)) + } + + // if vault fields have value, ociConfig must have values + if r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName != "" && + (r.Spec.OCIConfig.SecretName == "" || r.Spec.OCIConfig.ConfigMapName == "") { + + e = append(e, + field.Invalid(field.NewPath("spec").Child("ociConfig"), r.Spec.OCIConfig, + ErrorSpecValidationMissingOCIConfig)) + } + + // If all of {DB Password Secret Name and vaultOCID+vaultSecretName} have no value, then error out + if r.Spec.Database.DBPassword.SecretName == "" && + r.Spec.Database.DBPassword.VaultOCID == "" && + r.Spec.Database.DBPassword.VaultSecretName == "" { + + e = append(e, + field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword").Child("secret"), r.Spec.Database.DBPassword.SecretName, + ErrorSpecValidationMissingDBPasswordSecret)) + } + + // disallow usage of any other image than the observability-exporter + if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { + e = append(e, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, + ErrorSpecExporterImageNotAllowed)) + } + + // Return if any errors + if len(e) > 0 { + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + } + return nil, nil + +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + databaseobserverlog.Info("validate update", "name", r.Name) + var e field.ErrorList + + // disallow usage of any other image than the observability-exporter + if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { + e = append(e, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, + ErrorSpecExporterImageNotAllowed)) + } + // Return if any errors + if len(e) > 0 { + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateDelete() (admission.Warnings, error) { + databaseobserverlog.Info("validate delete", "name", r.Name) + + return nil, nil +} diff --git a/apis/observability/v4/groupversion_info.go b/apis/observability/v4/groupversion_info.go new file mode 100644 index 00000000..155b1c11 --- /dev/null +++ b/apis/observability/v4/groupversion_info.go @@ -0,0 +1,58 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Package v4 contains API Schema definitions for the observability v4 API group +// +kubebuilder:object:generate=true +// +groupName=observability.oracle.com +package v4 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "observability.oracle.com", Version: "v4"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/observability/v4/zz_generated.deepcopy.go b/apis/observability/v4/zz_generated.deepcopy.go new file mode 100644 index 00000000..fbfba0ef --- /dev/null +++ b/apis/observability/v4/zz_generated.deepcopy.go @@ -0,0 +1,471 @@ +//go:build !ignore_autogenerated + +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package v4 + +import ( + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapDetails) DeepCopyInto(out *ConfigMapDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapDetails. +func (in *ConfigMapDetails) DeepCopy() *ConfigMapDetails { + if in == nil { + return nil + } + out := new(ConfigMapDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBSecret) DeepCopyInto(out *DBSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecret. +func (in *DBSecret) DeepCopy() *DBSecret { + if in == nil { + return nil + } + out := new(DBSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBSecretWithVault) DeepCopyInto(out *DBSecretWithVault) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecretWithVault. +func (in *DBSecretWithVault) DeepCopy() *DBSecretWithVault { + if in == nil { + return nil + } + out := new(DBSecretWithVault) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserver) DeepCopyInto(out *DatabaseObserver) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserver. +func (in *DatabaseObserver) DeepCopy() *DatabaseObserver { + if in == nil { + return nil + } + out := new(DatabaseObserver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatabaseObserver) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverConfigMap) DeepCopyInto(out *DatabaseObserverConfigMap) { + *out = *in + out.Configmap = in.Configmap +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverConfigMap. +func (in *DatabaseObserverConfigMap) DeepCopy() *DatabaseObserverConfigMap { + if in == nil { + return nil + } + out := new(DatabaseObserverConfigMap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverDatabase) DeepCopyInto(out *DatabaseObserverDatabase) { + *out = *in + out.DBUser = in.DBUser + out.DBPassword = in.DBPassword + out.DBWallet = in.DBWallet + out.DBConnectionString = in.DBConnectionString +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDatabase. +func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { + if in == nil { + return nil + } + out := new(DatabaseObserverDatabase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverDeployment) DeepCopyInto(out *DatabaseObserverDeployment) { + *out = *in + if in.ExporterArgs != nil { + in, out := &in.ExporterArgs, &out.ExporterArgs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterCommands != nil { + in, out := &in.ExporterCommands, &out.ExporterCommands + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterEnvs != nil { + in, out := &in.ExporterEnvs, &out.ExporterEnvs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.DeploymentPodTemplate.DeepCopyInto(&out.DeploymentPodTemplate) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDeployment. +func (in *DatabaseObserverDeployment) DeepCopy() *DatabaseObserverDeployment { + if in == nil { + return nil + } + out := new(DatabaseObserverDeployment) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverExporterConfig) DeepCopyInto(out *DatabaseObserverExporterConfig) { + *out = *in + in.Deployment.DeepCopyInto(&out.Deployment) + in.Service.DeepCopyInto(&out.Service) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverExporterConfig. +func (in *DatabaseObserverExporterConfig) DeepCopy() *DatabaseObserverExporterConfig { + if in == nil { + return nil + } + out := new(DatabaseObserverExporterConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverList) DeepCopyInto(out *DatabaseObserverList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DatabaseObserver, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverList. +func (in *DatabaseObserverList) DeepCopy() *DatabaseObserverList { + if in == nil { + return nil + } + out := new(DatabaseObserverList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatabaseObserverList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { + *out = *in + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]v1.ServicePort, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverService. +func (in *DatabaseObserverService) DeepCopy() *DatabaseObserverService { + if in == nil { + return nil + } + out := new(DatabaseObserverService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { + *out = *in + out.Database = in.Database + in.Exporter.DeepCopyInto(&out.Exporter) + out.ExporterConfig = in.ExporterConfig + in.Prometheus.DeepCopyInto(&out.Prometheus) + out.OCIConfig = in.OCIConfig + out.Log = in.Log + if in.InheritLabels != nil { + in, out := &in.InheritLabels, &out.InheritLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterSidecars != nil { + in, out := &in.ExporterSidecars, &out.ExporterSidecars + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SideCarVolumes != nil { + in, out := &in.SideCarVolumes, &out.SideCarVolumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. +func (in *DatabaseObserverSpec) DeepCopy() *DatabaseObserverSpec { + if in == nil { + return nil + } + out := new(DatabaseObserverSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverStatus. +func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { + if in == nil { + return nil + } + out := new(DatabaseObserverStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentPodTemplate. +func (in *DeploymentPodTemplate) DeepCopy() *DeploymentPodTemplate { + if in == nil { + return nil + } + out := new(DeploymentPodTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogConfig) DeepCopyInto(out *LogConfig) { + *out = *in + out.Volume = in.Volume +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogConfig. +func (in *LogConfig) DeepCopy() *LogConfig { + if in == nil { + return nil + } + out := new(LogConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogVolume) DeepCopyInto(out *LogVolume) { + *out = *in + out.PersistentVolumeClaim = in.PersistentVolumeClaim +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolume. +func (in *LogVolume) DeepCopy() *LogVolume { + if in == nil { + return nil + } + out := new(LogVolume) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogVolumePVClaim) DeepCopyInto(out *LogVolumePVClaim) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolumePVClaim. +func (in *LogVolumePVClaim) DeepCopy() *LogVolumePVClaim { + if in == nil { + return nil + } + out := new(LogVolumePVClaim) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfigSpec. +func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { + if in == nil { + return nil + } + out := new(OCIConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrometheusConfig) DeepCopyInto(out *PrometheusConfig) { + *out = *in + in.ServiceMonitor.DeepCopyInto(&out.ServiceMonitor) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusConfig. +func (in *PrometheusConfig) DeepCopy() *PrometheusConfig { + if in == nil { + return nil + } + out := new(PrometheusConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrometheusServiceMonitor) DeepCopyInto(out *PrometheusServiceMonitor) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(monitoringv1.NamespaceSelector) + (*in).DeepCopyInto(*out) + } + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]monitoringv1.Endpoint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusServiceMonitor. +func (in *PrometheusServiceMonitor) DeepCopy() *PrometheusServiceMonitor { + if in == nil { + return nil + } + out := new(PrometheusServiceMonitor) + in.DeepCopyInto(out) + return out +} diff --git a/commons/observability/constants.go b/commons/observability/constants.go index 89ecb946..1faabde8 100644 --- a/commons/observability/constants.go +++ b/commons/observability/constants.go @@ -1,6 +1,8 @@ package observability -import "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" +import ( + v4 "github.com/oracle/oracle-database-operator/apis/observability/v4" +) const ( UnknownValue = "UNKNOWN" @@ -9,9 +11,9 @@ const ( // Observability Status const ( - StatusObservabilityPending v1alpha1.StatusEnum = "PENDING" - StatusObservabilityError v1alpha1.StatusEnum = "ERROR" - StatusObservabilityReady v1alpha1.StatusEnum = "READY" + StatusObservabilityPending v4.StatusEnum = "PENDING" + StatusObservabilityError v4.StatusEnum = "ERROR" + StatusObservabilityReady v4.StatusEnum = "READY" ) // Log Names @@ -27,8 +29,9 @@ const ( DefaultDbUserKey = "username" DefaultDBPasswordKey = "password" DefaultDBConnectionStringKey = "connection" - DefaultLabelKey = "app" DefaultConfigVolumeString = "config-volume" + DefaultLogFilename = "alert.log" + DefaultLogVolumeString = "log-volume" DefaultWalletVolumeString = "creds" DefaultOCIPrivateKeyVolumeString = "ocikey" DefaultOCIConfigFingerprintKey = "fingerprint" @@ -36,13 +39,15 @@ const ( DefaultOCIConfigTenancyKey = "tenancy" DefaultOCIConfigUserKey = "user" - DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:1.1.0" + DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:1.5.1" DefaultServicePort = 9161 DefaultServiceTargetPort = 9161 + DefaultAppPort = 8080 DefaultPrometheusPort = "metrics" + DefaultServiceType = "ClusterIP" DefaultReplicaCount = 1 DefaultExporterConfigMountRootPath = "/oracle/observability" - DefaultOracleHome = "/lib/oracle/21/client64/lib" + DefaultOracleHome = "/lib/oracle/23/client64/lib" DefaultOracleTNSAdmin = DefaultOracleHome + "/network/admin" DefaultExporterConfigmapFilename = "config.toml" DefaultVaultPrivateKeyRootPath = "/oracle/config" @@ -52,12 +57,15 @@ const ( DefaultExporterConfigmapAbsolutePath = DefaultExporterConfigMountRootPath + "/" + DefaultExporterConfigmapFilename ) -// default resource prefixes +// labeling +const ( + DefaultSelectorLabelKey = "app" + DefaultReleaseLabelKey = "release" +) + +// default resource const ( - DefaultServiceMonitorPrefix = "obs-servicemonitor-" - DefaultLabelPrefix = "obs-" - DefaultExporterDeploymentPrefix = "obs-deploy-" - DefaultExporterContainerName = "observability-exporter" + DefaultExporterContainerName = "observability-exporter" ) // Known environment variables @@ -66,6 +74,7 @@ const ( EnvVarDataSourceUser = "DB_USERNAME" EnvVarDataSourcePassword = "DB_PASSWORD" EnvVarDataSourceConnectString = "DB_CONNECT_STRING" + EnvVarDataSourceLogDestination = "LOG_DESTINATION" EnvVarDataSourcePwdVaultSecretName = "VAULT_SECRET_NAME" EnvVarDataSourcePwdVaultId = "VAULT_ID" EnvVarCustomConfigmap = "CUSTOM_METRICS" @@ -93,11 +102,11 @@ const ( ReasonReadyFailed = "ReadinessValidationFailed" ReasonDeploymentSpecValidationFailed = "SpecValidationFailed" - ReasonDeploymentSuccessful = "ResourceDeployed" - ReasonDeploymentUpdated = "ResourceDeploymentUpdated" - ReasonDeploymentUpdateFailed = "ResourceDeploymentUpdateFailed" - ReasonDeploymentFailed = "ResourceDeploymentFailed" - ReasonDeploymentPending = "ResourceDeploymentInProgress" + ReasonDeploymentSuccessful = "ResourceDeployed" + ReasonResourceUpdated = "ResourceUpdated" + ReasonResourceUpdateFailed = "ResourceUpdateFailed" + ReasonDeploymentFailed = "ResourceDeploymentFailed" + ReasonDeploymentPending = "ResourceDeploymentInProgress" ReasonGeneralResourceGenerationFailed = "ResourceGenerationFailed" ReasonGeneralResourceCreated = "ResourceCreated" @@ -112,18 +121,19 @@ const ( ErrorStatusUpdate = "an error occurred with updating the cr status" ErrorSpecValidationFailedDueToAnError = "an error occurred with validating the exporter deployment spec" ErrorDeploymentPodsFailure = "an error occurred with deploying exporter deployment pods" - ErrorDeploymentUpdate = "an error occurred with updating exporter deployment" ErrorResourceCreationFailure = "an error occurred with creating databaseobserver resource" ErrorResourceRetrievalFailureDueToAnError = "an error occurred with retrieving databaseobserver resource" + LogErrorWithResourceUpdate = "an error occurred with updating resource" ) // Log Infos const ( - LogCRStart = "Started DatabaseObserver instance reconciliation" - LogCREnd = "Ended DatabaseObserver instance reconciliation, resource must have been deleted." - LogResourceCreated = "Created DatabaseObserver resource successfully" - LogResourceUpdated = "Updated DatabaseObserver resource successfully" - LogResourceFound = "Validated DatabaseObserver resource readiness" + LogCRStart = "Started DatabaseObserver instance reconciliation" + LogCREnd = "Ended DatabaseObserver instance reconciliation, resource must have been deleted." + LogResourceCreated = "Created DatabaseObserver resource successfully" + LogResourceUpdated = "Updated DatabaseObserver resource successfully" + LogResourceFound = "Validated DatabaseObserver resource readiness" + LogSuccessWithResourceUpdate = "Updated DatabaseObserver resource successfully" ) // Messages @@ -140,11 +150,8 @@ const ( MessageResourceGenerationFailed = "Failed to generate resource due to an error" MessageExporterDeploymentSpecValidationFailed = "Failed to validate export deployment spec due to an error with the spec" - MessageExporterDeploymentImageUpdated = "Completed updating exporter deployment image successfully" - MessageExporterDeploymentEnvironmentUpdated = "Completed updating exporter deployment environment values successfully" - MessageExporterDeploymentReplicaUpdated = "Completed updating exporter deployment replicaCount successfully" - MessageExporterDeploymentVolumesUpdated = "Completed updating exporter deployment volumes successfully" - MessageExporterDeploymentUpdateFailed = "Failed to update exporter deployment due to an error" + MessageExporterResourceUpdateFailed = "Failed to update exporter resource due to an error" + MessageExporterResourceUpdated = "Updated exporter resource successfully" MessageExporterDeploymentValidationFailed = "Failed to validate exporter deployment due to an error retrieving resource" MessageExporterDeploymentSuccessful = "Completed validation of exporter deployment readiness" MessageExporterDeploymentFailed = "Failed to deploy exporter deployment due to PodFailure" @@ -158,16 +165,11 @@ const ( EventMessageFailedCRRetrieval = "Encountered error retrieving databaseObserver instance" EventReasonSpecError = "DeploymentSpecValidationFailed" - EventMessageSpecErrorDBPasswordMissing = "Spec validation failed due to missing dbPassword field values" EventMessageSpecErrorDBPasswordSecretMissing = "Spec validation failed due to required dbPassword secret not found" EventMessageSpecErrorDBConnectionStringSecretMissing = "Spec validation failed due to required dbConnectionString secret not found" EventMessageSpecErrorDBPUserSecretMissing = "Spec validation failed due to dbUser secret not found" EventMessageSpecErrorConfigmapMissing = "Spec validation failed due to custom config configmap not found" EventMessageSpecErrorDBWalletSecretMissing = "Spec validation failed due to provided dbWallet secret not found" - EventReasonUpdateSucceeded = "ExporterDeploymentUpdated" - EventMessageUpdatedImageSucceeded = "Exporter deployment image updated successfully" - EventMessageUpdatedEnvironmentSucceeded = "Exporter deployment environment values updated successfully" - EventMessageUpdatedVolumesSucceeded = "Exporter deployment volumes updated successfully" - EventMessageUpdatedReplicaSucceeded = "Exporter deployment replicaCount updated successfully" + EventReasonUpdateSucceeded = "ExporterDeploymentUpdated" ) diff --git a/commons/observability/utils.go b/commons/observability/utils.go index f396b95a..bd4ea0b5 100644 --- a/commons/observability/utils.go +++ b/commons/observability/utils.go @@ -1,87 +1,213 @@ package observability import ( - apiv1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" - appsv1 "k8s.io/api/apps/v1" + api "github.com/oracle/oracle-database-operator/apis/observability/v4" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "path/filepath" + "strings" ) -// GetExporterLabels function retrieves exporter labels from api or provides default -func GetExporterLabels(api *apiv1.DatabaseObserver) map[string]string { +func AddSidecarContainers(a *api.DatabaseObserver, listing *[]corev1.Container) { + + if containers := a.Spec.ExporterSidecars; len(containers) > 0 { + for _, container := range containers { + *listing = append(*listing, container) + } + + } +} + +func AddSidecarVolumes(a *api.DatabaseObserver, listing *[]corev1.Volume) { + + if volumes := a.Spec.SideCarVolumes; len(volumes) > 0 { + for _, v := range volumes { + *listing = append(*listing, v) + } + + } +} + +// GetLabels retrieves labels from the spec +func GetLabels(a *api.DatabaseObserver, customResourceLabels map[string]string) map[string]string { + var l = make(map[string]string) - if labels := api.Spec.Prometheus.Labels; labels != nil && len(labels) > 0 { - for k, v := range labels { - l[k] = v + // get inherited labels + if iLabels := a.Spec.InheritLabels; iLabels != nil { + for _, v := range iLabels { + if v != DefaultSelectorLabelKey { + l[v] = a.Labels[v] + } + } + } + + if customResourceLabels != nil { + for k, v := range customResourceLabels { + if k != DefaultSelectorLabelKey { + l[k] = v + } } - l["release"] = "stable" - return l } - return map[string]string{ - DefaultLabelKey: DefaultLabelPrefix + api.Name, - "release": "stable", + + // add app label + l[DefaultSelectorLabelKey] = a.Name + return l +} + +// GetSelectorLabel adds selector label +func GetSelectorLabel(a *api.DatabaseObserver) map[string]string { + selectors := make(map[string]string) + selectors[DefaultSelectorLabelKey] = a.Name + return selectors +} + +// GetExporterVersion retrieves version of exporter used +func GetExporterVersion(a *api.DatabaseObserver) string { + appVersion := "latest" + whichImage := DefaultExporterImage + if img := a.Spec.Exporter.Deployment.ExporterImage; img != "" { + whichImage = img + } + + // return tag in image:tag + if str := strings.Split(whichImage, ":"); len(str) == 2 { + appVersion = str[1] + } + return appVersion +} + +// GetExporterArgs retrieves args +func GetExporterArgs(a *api.DatabaseObserver) []string { + if args := a.Spec.Exporter.Deployment.ExporterArgs; args != nil || len(args) > 0 { + return args + } + return nil +} + +// GetExporterCommands retrieves commands +func GetExporterCommands(a *api.DatabaseObserver) []string { + if c := a.Spec.Exporter.Deployment.ExporterCommands; c != nil || len(c) > 0 { + return c + } + return nil +} + +// GetExporterServicePort function retrieves exporter service port from a or provides default +func GetExporterServicePort(a *api.DatabaseObserver) []corev1.ServicePort { + + servicePorts := make([]corev1.ServicePort, 0) + + // get service ports + if ports := a.Spec.Exporter.Service.Ports; len(ports) > 0 { + for _, port := range ports { + servicePorts = append(servicePorts, port) + } + + } else { + // if not, provide default service port + servicePorts = append(servicePorts, corev1.ServicePort{ + Name: DefaultPrometheusPort, + Port: DefaultServicePort, + TargetPort: intstr.FromInt32(DefaultServiceTargetPort), + }) } + return servicePorts + } -// GetExporterServicePort function retrieves exporter service port from api or provides default -func GetExporterServicePort(api *apiv1.DatabaseObserver) int32 { - if rPort := api.Spec.Exporter.Service.Port; rPort != 0 { - return rPort +// GetEndpoints function +func GetEndpoints(a *api.DatabaseObserver) []monitorv1.Endpoint { + + endpoints := make([]monitorv1.Endpoint, 0) + + // get endpoints + if es := a.Spec.Prometheus.ServiceMonitor.Endpoints; len(es) > 0 { + for _, e := range es { + endpoints = append(endpoints, e) + } } - return int32(DefaultServicePort) + + // if not, provide default endpoint + endpoints = append(endpoints, monitorv1.Endpoint{ + Port: DefaultPrometheusPort, + Interval: "20s", + }) + + return endpoints } -// GetExporterServiceMonitorPort function retrieves exporter service monitor port from api or provides default -func GetExporterServiceMonitorPort(api *apiv1.DatabaseObserver) string { - if rPort := api.Spec.Prometheus.Port; rPort != "" { - return rPort +func AddNamespaceSelector(a *api.DatabaseObserver, spec *monitorv1.ServiceMonitorSpec) { + + if ns := a.Spec.Prometheus.ServiceMonitor.NamespaceSelector; ns != nil { + a.Spec.Prometheus.ServiceMonitor.NamespaceSelector.DeepCopyInto(&spec.NamespaceSelector) } - return DefaultPrometheusPort } -// GetExporterDeploymentVolumeMounts function retrieves volume mounts from api or provides default -func GetExporterDeploymentVolumeMounts(api *apiv1.DatabaseObserver) []corev1.VolumeMount { +// GetExporterDeploymentVolumeMounts function retrieves volume mounts from a or provides default +func GetExporterDeploymentVolumeMounts(a *api.DatabaseObserver) []corev1.VolumeMount { volM := make([]corev1.VolumeMount, 0) - if cVolumeSourceName := api.Spec.Exporter.ExporterConfig.Configmap.Name; cVolumeSourceName != "" { + if cVolumeSourceName := a.Spec.ExporterConfig.Configmap.Name; cVolumeSourceName != "" { volM = append(volM, corev1.VolumeMount{ Name: DefaultConfigVolumeString, MountPath: DefaultExporterConfigMountRootPath, }) } - // api.Spec.Database.DBWallet.SecretName optional + // a.Spec.Database.DBWallet.SecretName optional // if null, consider the database NON-ADB and connect as such - if secretName := api.Spec.Database.DBWallet.SecretName; secretName != "" { + if secretName := a.Spec.Database.DBWallet.SecretName; secretName != "" { + + p := DefaultOracleTNSAdmin + + // Determine what the value of TNS_ADMIN + // if custom TNS_ADMIN environment variable is set and found, use that instead as the path + if rCustomEnvs := a.Spec.Exporter.Deployment.ExporterEnvs; rCustomEnvs != nil { + if v, f := rCustomEnvs[EnvVarTNSAdmin]; f { + p = v + } + } + volM = append(volM, corev1.VolumeMount{ Name: DefaultWalletVolumeString, - MountPath: DefaultOracleTNSAdmin, + MountPath: p, }) } - // api.Spec.OCIConfig.SecretName required if vault is used - if secretName := api.Spec.OCIConfig.SecretName; secretName != "" { + // a.Spec.OCIConfig.SecretName required if vault is used + if secretName := a.Spec.OCIConfig.SecretName; secretName != "" { volM = append(volM, corev1.VolumeMount{ Name: DefaultOCIPrivateKeyVolumeString, MountPath: DefaultVaultPrivateKeyRootPath, }) } + + // a.Spec.Log.Path path to mount for a custom log path, a volume is required + if rLogPath := a.Spec.Log.Path; rLogPath != "" { + volM = append(volM, corev1.VolumeMount{ + Name: DefaultLogVolumeString, + MountPath: rLogPath, + }) + } + return volM } -// GetExporterDeploymentVolumes function retrieves volumes from api or provides default -func GetExporterDeploymentVolumes(api *apiv1.DatabaseObserver) []corev1.Volume { +// GetExporterDeploymentVolumes function retrieves volumes from a or provides default +func GetExporterDeploymentVolumes(a *api.DatabaseObserver) []corev1.Volume { vol := make([]corev1.Volume, 0) // config-volume Volume // if null, the exporter uses the default built-in config - if cVolumeSourceName := api.Spec.Exporter.ExporterConfig.Configmap.Name; cVolumeSourceName != "" { + if cVolumeSourceName := a.Spec.ExporterConfig.Configmap.Name; cVolumeSourceName != "" { - cVolumeSourceKey := api.Spec.Exporter.ExporterConfig.Configmap.Key + cVolumeSourceKey := a.Spec.ExporterConfig.Configmap.Key cMSource := &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: cVolumeSourceName, @@ -96,9 +222,9 @@ func GetExporterDeploymentVolumes(api *apiv1.DatabaseObserver) []corev1.Volume { } // creds Volume - // api.Spec.Database.DBWallet.SecretName optional + // a.Spec.Database.DBWallet.SecretName optional // if null, consider the database NON-ADB and connect as such - if secretName := api.Spec.Database.DBWallet.SecretName; secretName != "" { + if secretName := a.Spec.Database.DBWallet.SecretName; secretName != "" { vol = append(vol, corev1.Volume{ Name: DefaultWalletVolumeString, @@ -111,8 +237,8 @@ func GetExporterDeploymentVolumes(api *apiv1.DatabaseObserver) []corev1.Volume { } // ocikey Volume - // api.Spec.Database.DBWallet.SecretName optional - if secretName := api.Spec.OCIConfig.SecretName; secretName != "" { + // a.Spec.Database.DBWallet.SecretName optional + if secretName := a.Spec.OCIConfig.SecretName; secretName != "" { OCIConfigSource := &corev1.SecretVolumeSource{ SecretName: secretName, @@ -127,39 +253,104 @@ func GetExporterDeploymentVolumes(api *apiv1.DatabaseObserver) []corev1.Volume { VolumeSource: corev1.VolumeSource{Secret: OCIConfigSource}, }) } + + // log-volume Volume + if rLogPath := a.Spec.Log.Path; rLogPath != "" { + vs := GetLogVolumeSource(a) + vName := GetLogName(a) + + vol = append(vol, corev1.Volume{ + Name: vName, + VolumeSource: vs, + }) + } + return vol } -// GetExporterSelector function retrieves labels from api or provides default -func GetExporterSelector(api *apiv1.DatabaseObserver) map[string]string { - var s = make(map[string]string) - if labels := api.Spec.Prometheus.Labels; labels != nil && len(labels) > 0 { - for k, v := range labels { - s[k] = v +// GetExporterConfig function retrieves config name for status +func GetExporterConfig(a *api.DatabaseObserver) string { + + configName := DefaultValue + if cmName := a.Spec.ExporterConfig.Configmap.Name; cmName != "" { + configName = cmName + } + + return configName +} + +func GetLogName(a *api.DatabaseObserver) string { + if name := a.Spec.Log.Volume.Name; name != "" { + return name + } + return DefaultLogVolumeString +} + +// GetLogVolumeSource function retrieves the source to help GetExporterDeploymentVolumes +func GetLogVolumeSource(a *api.DatabaseObserver) corev1.VolumeSource { + + vs := corev1.VolumeSource{} + rLogVolumeClaimName := a.Spec.Log.Volume.PersistentVolumeClaim.ClaimName + + // volume claims take precedence + if rLogVolumeClaimName != "" { + vs.PersistentVolumeClaim = &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: rLogVolumeClaimName, } - return s + return vs + } else { + vs.EmptyDir = &corev1.EmptyDirVolumeSource{} + return vs } - return map[string]string{DefaultLabelKey: DefaultLabelPrefix + api.Name} +} +// AddEnv is a helper method that appends an Env Var value +func AddEnv(env []corev1.EnvVar, existing map[string]string, name string, v string) []corev1.EnvVar { + + // Evaluate if env already exists + if _, f := existing[name]; !f { + env = append(env, corev1.EnvVar{Name: name, Value: v}) + } + return env +} + +// AddEnvFrom is a helper method that appends an Env Var value source +func AddEnvFrom(env []corev1.EnvVar, existing map[string]string, name string, v *corev1.EnvVarSource) []corev1.EnvVar { + + // Evaluate if env already exists + if _, f := existing[name]; !f { + env = append(env, corev1.EnvVar{Name: name, ValueFrom: v}) + } + return env } -// GetExporterEnvs function retrieves env from api or provides default -func GetExporterEnvs(api *apiv1.DatabaseObserver) []corev1.EnvVar { +// GetExporterEnvs function retrieves env from a or provides default +func GetExporterEnvs(a *api.DatabaseObserver) []corev1.EnvVar { optional := true - rDBPasswordKey := api.Spec.Database.DBPassword.Key - rDBPasswordName := api.Spec.Database.DBPassword.SecretName - rDBConnectStrKey := api.Spec.Database.DBConnectionString.Key - rDBConnectStrName := api.Spec.Database.DBConnectionString.SecretName - rDBVaultSecretName := api.Spec.Database.DBPassword.VaultSecretName - rDBVaultOCID := api.Spec.Database.DBPassword.VaultOCID - rDBUserSKey := api.Spec.Database.DBUser.Key - rDBUserSName := api.Spec.Database.DBUser.SecretName - rOCIConfigCMName := api.Spec.OCIConfig.ConfigMapName + rDBPasswordKey := a.Spec.Database.DBPassword.Key + rDBPasswordName := a.Spec.Database.DBPassword.SecretName + rDBConnectStrKey := a.Spec.Database.DBConnectionString.Key + rDBConnectStrName := a.Spec.Database.DBConnectionString.SecretName + rDBVaultSecretName := a.Spec.Database.DBPassword.VaultSecretName + rDBVaultOCID := a.Spec.Database.DBPassword.VaultOCID + rDBUserSKey := a.Spec.Database.DBUser.Key + rDBUserSName := a.Spec.Database.DBUser.SecretName + rOCIConfigCMName := a.Spec.OCIConfig.ConfigMapName + rLogPath := a.Spec.Log.Path + rLogFilename := a.Spec.Log.Filename + rCustomEnvs := a.Spec.Exporter.Deployment.ExporterEnvs var env = make([]corev1.EnvVar, 0) + // add CustomEnvs + if rCustomEnvs != nil { + for k, v := range rCustomEnvs { + env = append(env, corev1.EnvVar{Name: k, Value: v}) + } + } + // DB_USERNAME environment variable if rDBUserSKey == "" { // overwrite rDBUserSKey = DefaultDbUserKey @@ -170,7 +361,7 @@ func GetExporterEnvs(api *apiv1.DatabaseObserver) []corev1.EnvVar { LocalObjectReference: corev1.LocalObjectReference{Name: rDBUserSName}, Optional: &optional, }} - env = append(env, corev1.EnvVar{Name: EnvVarDataSourceUser, ValueFrom: envUser}) + env = AddEnvFrom(env, rCustomEnvs, EnvVarDataSourceUser, envUser) // DB_CONNECT_STRING environment variable if rDBConnectStrKey == "" { @@ -182,15 +373,15 @@ func GetExporterEnvs(api *apiv1.DatabaseObserver) []corev1.EnvVar { LocalObjectReference: corev1.LocalObjectReference{Name: rDBConnectStrName}, Optional: &optional, }} - env = append(env, corev1.EnvVar{Name: EnvVarDataSourceConnectString, ValueFrom: envConnectStr}) + env = AddEnvFrom(env, rCustomEnvs, EnvVarDataSourceConnectString, envConnectStr) // DB_PASSWORD environment variable // if useVault, add environment variables for Vault ID and Vault Secret Name useVault := rDBVaultSecretName != "" && rDBVaultOCID != "" if useVault { - env = append(env, corev1.EnvVar{Name: EnvVarDataSourcePwdVaultSecretName, Value: rDBVaultSecretName}) - env = append(env, corev1.EnvVar{Name: EnvVarDataSourcePwdVaultId, Value: rDBVaultOCID}) + env = AddEnv(env, rCustomEnvs, EnvVarDataSourcePwdVaultSecretName, rDBVaultSecretName) + env = AddEnv(env, rCustomEnvs, EnvVarDataSourcePwdVaultId, rDBVaultOCID) // Configuring the configProvider prefixed with vault_ // https://github.com/oracle/oracle-db-appdev-monitoring/blob/main/vault/vault.go @@ -222,12 +413,11 @@ func GetExporterEnvs(api *apiv1.DatabaseObserver) []corev1.EnvVar { Optional: &optional, }, } - - env = append(env, corev1.EnvVar{Name: EnvVarVaultFingerprint, ValueFrom: configSourceFingerprintValue}) - env = append(env, corev1.EnvVar{Name: EnvVarVaultUserOCID, ValueFrom: configSourceUserValue}) - env = append(env, corev1.EnvVar{Name: EnvVarVaultTenancyOCID, ValueFrom: configSourceTenancyValue}) - env = append(env, corev1.EnvVar{Name: EnvVarVaultRegion, ValueFrom: configSourceRegionValue}) - env = append(env, corev1.EnvVar{Name: EnvVarVaultPrivateKeyPath, Value: DefaultVaultPrivateKeyAbsolutePath}) + env = AddEnvFrom(env, rCustomEnvs, EnvVarVaultFingerprint, configSourceFingerprintValue) + env = AddEnvFrom(env, rCustomEnvs, EnvVarVaultUserOCID, configSourceUserValue) + env = AddEnvFrom(env, rCustomEnvs, EnvVarVaultTenancyOCID, configSourceTenancyValue) + env = AddEnvFrom(env, rCustomEnvs, EnvVarVaultRegion, configSourceRegionValue) + env = AddEnv(env, rCustomEnvs, EnvVarVaultPrivateKeyPath, DefaultVaultPrivateKeyAbsolutePath) } else { @@ -241,162 +431,46 @@ func GetExporterEnvs(api *apiv1.DatabaseObserver) []corev1.EnvVar { Optional: &optional, }} - env = append(env, corev1.EnvVar{Name: EnvVarDataSourcePassword, ValueFrom: dbPassword}) + env = AddEnvFrom(env, rCustomEnvs, EnvVarDataSourcePassword, dbPassword) } // CUSTOM_METRICS environment variable - if customMetricsName := api.Spec.Exporter.ExporterConfig.Configmap.Name; customMetricsName != "" { + if customMetricsName := a.Spec.ExporterConfig.Configmap.Name; customMetricsName != "" { customMetrics := DefaultExporterConfigmapAbsolutePath - env = append(env, corev1.EnvVar{Name: EnvVarCustomConfigmap, Value: customMetrics}) + + env = AddEnv(env, rCustomEnvs, EnvVarCustomConfigmap, customMetrics) + } + + env = AddEnv(env, rCustomEnvs, EnvVarOracleHome, DefaultOracleHome) + env = AddEnv(env, rCustomEnvs, EnvVarTNSAdmin, DefaultOracleTNSAdmin) + + // LOG_DESTINATION environment variable + if rLogPath != "" { + if rLogFilename == "" { + rLogFilename = DefaultLogFilename + } + d := filepath.Join(rLogPath, rLogFilename) + env = AddEnv(env, rCustomEnvs, EnvVarDataSourceLogDestination, d) } - env = append(env, corev1.EnvVar{Name: EnvVarOracleHome, Value: DefaultOracleHome}) - env = append(env, corev1.EnvVar{Name: EnvVarTNSAdmin, Value: DefaultOracleTNSAdmin}) return env } -// GetExporterReplicas function retrieves replicaCount from api or provides default -func GetExporterReplicas(api *apiv1.DatabaseObserver) int32 { - if rc := api.Spec.Replicas; rc != 0 { +// GetExporterReplicas function retrieves replicaCount from a or provides default +func GetExporterReplicas(a *api.DatabaseObserver) int32 { + if rc := a.Spec.Replicas; rc != 0 { return rc } return int32(DefaultReplicaCount) } -// GetExporterImage function retrieves image from api or provides default -func GetExporterImage(api *apiv1.DatabaseObserver) string { - if img := api.Spec.Exporter.ExporterImage; img != "" { +// GetExporterImage function retrieves image from a or provides default +func GetExporterImage(a *api.DatabaseObserver) string { + if img := a.Spec.Exporter.Deployment.ExporterImage; img != "" { return img } - return DefaultExporterImage - -} - -func IsUpdateRequiredForContainerImage(desired *appsv1.Deployment, found *appsv1.Deployment) bool { - foundImage := found.Spec.Template.Spec.Containers[0].Image - desiredImage := desired.Spec.Template.Spec.Containers[0].Image - - return foundImage != desiredImage -} - -func IsUpdateRequiredForEnvironmentVars(desired *appsv1.Deployment, found *appsv1.Deployment) bool { - var updateEnvsRequired bool - desiredEnvValues := make(map[string]string) - - foundEnvs := found.Spec.Template.Spec.Containers[0].Env - desiredEnvs := desired.Spec.Template.Spec.Containers[0].Env - if len(foundEnvs) != len(desiredEnvs) { - updateEnvsRequired = true - } else { - for _, v := range desiredEnvs { - - if v.Name == EnvVarDataSourceUser || - v.Name == EnvVarDataSourceConnectString || - v.Name == EnvVarDataSourcePassword { - ref := *(*v.ValueFrom).SecretKeyRef - desiredEnvValues[v.Name] = ref.Key + "-" + ref.Name - - } else if v.Name == EnvVarVaultFingerprint || - v.Name == EnvVarVaultRegion || - v.Name == EnvVarVaultTenancyOCID || - v.Name == EnvVarVaultUserOCID { - - ref := *(*v.ValueFrom).ConfigMapKeyRef - desiredEnvValues[v.Name] = ref.Key + "-" + ref.Name - - } else if v.Name == EnvVarDataSourcePwdVaultId || - v.Name == EnvVarDataSourcePwdVaultSecretName || - v.Name == EnvVarCustomConfigmap { - - desiredEnvValues[v.Name] = v.Value - } - } - - for _, v := range foundEnvs { - var foundValue string - - if v.Name == EnvVarDataSourceUser || - v.Name == EnvVarDataSourceConnectString || - v.Name == EnvVarDataSourcePassword { - - ref := *(*v.ValueFrom).SecretKeyRef - foundValue = ref.Key + "-" + ref.Name - - } else if v.Name == EnvVarVaultFingerprint || - v.Name == EnvVarVaultRegion || - v.Name == EnvVarVaultTenancyOCID || - v.Name == EnvVarVaultUserOCID { - - ref := *(*v.ValueFrom).ConfigMapKeyRef - foundValue = ref.Key + "-" + ref.Name - - } else if v.Name == EnvVarDataSourcePwdVaultId || - v.Name == EnvVarDataSourcePwdVaultSecretName || - v.Name == EnvVarCustomConfigmap { - - foundValue = v.Value - } - - if desiredEnvValues[v.Name] != foundValue { - updateEnvsRequired = true - } - } - } - return updateEnvsRequired -} - -func IsUpdateRequiredForVolumes(desired *appsv1.Deployment, found *appsv1.Deployment) bool { - var updateVolumesRequired bool - var foundConfigmap, desiredConfigmap string - var foundWalletSecret, desiredWalletSecret string - var foundOCIConfig, desiredOCIConfig string - - desiredVolumes := desired.Spec.Template.Spec.Volumes - foundVolumes := found.Spec.Template.Spec.Volumes - - if len(desiredVolumes) != len(foundVolumes) { - updateVolumesRequired = true - } else { - for _, v := range desiredVolumes { - if v.Name == DefaultConfigVolumeString { - desiredConfigmap = v.ConfigMap.Name - for _, key := range v.ConfigMap.Items { - desiredConfigmap += key.Key - } - } else if v.Name == DefaultWalletVolumeString { - desiredWalletSecret = v.VolumeSource.Secret.SecretName - - } else if v.Name == DefaultOCIPrivateKeyVolumeString { - desiredOCIConfig = v.VolumeSource.Secret.SecretName - } - } - - for _, v := range foundVolumes { - if v.Name == DefaultConfigVolumeString { - foundConfigmap = v.ConfigMap.Name - for _, key := range v.ConfigMap.Items { - foundConfigmap += key.Key - } - } else if v.Name == DefaultWalletVolumeString { - foundWalletSecret = v.VolumeSource.Secret.SecretName - - } else if v.Name == DefaultOCIPrivateKeyVolumeString { - foundOCIConfig = v.VolumeSource.Secret.SecretName - } - } - } - - return updateVolumesRequired || - desiredConfigmap != foundConfigmap || - desiredWalletSecret != foundWalletSecret || - desiredOCIConfig != foundOCIConfig -} - -func IsUpdateRequiredForReplicas(desired *appsv1.Deployment, found *appsv1.Deployment) bool { - foundReplicas := *found.Spec.Replicas - desiredReplicas := *desired.Spec.Replicas + return DefaultExporterImage - return desiredReplicas != foundReplicas } diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index c5d36d54..8d566b20 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -11,6 +11,9 @@ spec: kind: DatabaseObserver listKind: DatabaseObserverList plural: databaseobservers + shortNames: + - dbobserver + - dbobservers singular: databaseobserver scope: Namespaced versions: @@ -21,6 +24,1940 @@ spec: - jsonPath: .status.status name: Status type: string + - jsonPath: .status.version + name: Version + type: string + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + configuration: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podTemplate: + properties: + labels: + additionalProperties: + type: string + type: object + type: object + type: object + service: + properties: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + scopes: + items: + type: string + type: array + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + serverName: + type: string + type: object + type: object + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object + type: object + type: object + replicas: + format: int32 + type: integer + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string name: v1alpha1 schema: openAPIV3Schema: @@ -33,6 +1970,1954 @@ spec: type: object spec: properties: + configuration: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podTemplate: + properties: + labels: + additionalProperties: + type: string + type: object + type: object + type: object + service: + properties: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + scopes: + items: + type: string + type: array + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + serverName: + type: string + type: object + type: object + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object + type: object + type: object + replicas: + format: int32 + type: integer + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + configuration: + properties: + configMap: + properties: + configmap: + properties: + configmapName: + type: string + key: + type: string + type: object + key: + type: string + name: + type: string + type: object + type: object database: properties: dbConnectionString: @@ -70,23 +3955,86 @@ spec: type: object exporter: properties: - configuration: + deployment: properties: - configmap: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podTemplate: properties: - configmapName: - type: string - key: - type: string + labels: + additionalProperties: + type: string + type: object type: object type: object - image: - type: string service: properties: - port: - format: int32 - type: integer + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object type: object type: object ociConfig: @@ -98,16 +4046,1720 @@ spec: type: object prometheus: properties: - labels: - additionalProperties: - type: string + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + scopes: + items: + type: string + type: array + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + serverName: + type: string + type: object + type: object + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object type: object - port: - type: string type: object replicas: format: int32 type: integer + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + type: string + required: + - name + type: object + type: array type: object status: properties: @@ -153,9 +5805,12 @@ spec: type: integer status: type: string + version: + type: string required: - conditions - exporterConfig + - version type: object type: object served: true diff --git a/config/crd/patches/cainjection_in_observability_databaseobservers.yaml b/config/crd/patches/cainjection_in_observability_databaseobservers.yaml new file mode 100644 index 00000000..bef0b6c0 --- /dev/null +++ b/config/crd/patches/cainjection_in_observability_databaseobservers.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: databaseobservers.observability.oracle.com diff --git a/config/observability.oracle.com_databaseobservers.yaml b/config/observability.oracle.com_databaseobservers.yaml index b0801738..c69a3b99 100644 --- a/config/observability.oracle.com_databaseobservers.yaml +++ b/config/observability.oracle.com_databaseobservers.yaml @@ -1,4 +1,3 @@ - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -23,6 +22,3181 @@ spec: - jsonPath: .status.status name: Status type: string + - jsonPath: .status.version + name: Version + type: string + name: v1 + schema: + openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver + properties: + configuration: + properties: + configMap: + description: ConfigMapDetails defines the configmap name + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + description: DatabaseObserverDatabase defines the database details + used for DatabaseObserver + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbRole: + type: string + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + description: DatabaseObserverExporterConfig defines the configuration + details related to the exporters of DatabaseObserver + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + image: + type: string + service: + description: DatabaseObserverService defines the exporter service + component of DatabaseObserver + properties: + port: + format: int32 + type: integer + type: object + type: object + inherit_labels: + items: + type: string + type: array + log: + description: LogConfig defines the configuration details relation + to the logs of DatabaseObserver + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + description: PrometheusConfig defines the generated resources for + Prometheus + properties: + port: + type: string + release: + type: string + type: object + replicas: + format: int32 + type: integer + sidecarVolumes: + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1". Similarly, the volume partition + for /dev/sda is "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent disk + resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set). defaults to shared' + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is the + path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'user is optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: items if unspecified, each key-value pair in + the Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the ConfigMap, the volume setup will error unless it is + marked optional. Paths must be relative and may not contain + the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated + CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the secret + object contains more than one secret, all secret references + are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: readOnly specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a Optional: mode bits used to set + permissions on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to set permissions + on this file, must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'medium represents what type of storage medium + should back this directory. The default is "" which means + to use the node''s default medium. Must be an empty string + (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'sizeLimit is the total amount of local storage + required for this EmptyDir volume. The size limit is also + applicable for memory medium. The maximum usage on memory + medium EmptyDir would be the minimum value between the + SizeLimit specified here and the sum of memory limits + of all containers in a pod. The default is nil which means + that the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "ephemeral represents a volume that is handled + by a cluster storage driver. The volume's lifecycle is tied + to the pod that defines it - it will be created before the + pod starts, and deleted when the pod is removed. \n Use this + if: a) the volume is only needed while the pod runs, b) features + of normal volumes like restoring from snapshot or capacity + \ tracking are needed, c) the storage driver is specified + through a storage class, and d) the storage driver supports + dynamic volume provisioning through a PersistentVolumeClaim + (see EphemeralVolumeSource for more information on the + connection between this volume type and PersistentVolumeClaim). + \n Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. \n Use CSI for light-weight local ephemeral + volumes if the CSI driver is meant to be used that way - see + the documentation of the driver for more information. \n A + pod can use both types of ephemeral volumes and persistent + volumes at the same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC to + provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. the PVC + will be deleted together with the pod. The name of the + PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. + Pod validation will reject the pod if the concatenated + name is not valid for a PVC (for example, too long). \n + An existing PVC with that name that is not owned by the + pod will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC + is meant to be used by the pod, the PVC has to updated + with an owner reference to the pod once the pod exists. + Normally this should not be necessary, but it may be useful + when manually reconstructing a broken cluster. \n This + field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. \n Required, must + not be nil." + properties: + metadata: + description: May contain labels and annotations that + will be copied into the PVC when creating it. No other + fields are allowed and will be rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the PVC + that gets created from this template. The same fields + as in a PersistentVolumeClaim are also valid here. + properties: + accessModes: + description: 'accessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) If the + provisioner or an external controller can support + the specified data source, it will create a new + volume based on the contents of the specified + data source. When the AnyVolumeDataSource feature + gate is enabled, dataSource contents will be copied + to dataSourceRef, and dataSourceRef contents will + be copied to dataSource when dataSourceRef.namespace + is not specified. If the namespace is specified, + then dataSourceRef will not be copied to dataSource.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'dataSourceRef specifies the object + from which to populate the volume with data, if + a non-empty volume is desired. This may be any + object from a non-empty API group (non core object) + or a PersistentVolumeClaim object. When this field + is specified, volume binding will only succeed + if the type of the specified object matches some + installed volume populator or dynamic provisioner. + This field will replace the functionality of the + dataSource field and as such if both fields are + non-empty, they must have the same value. For + backwards compatibility, when namespace isn''t + specified in dataSourceRef, both fields (dataSource + and dataSourceRef) will be set to the same value + automatically if one of them is empty and the + other is non-empty. When namespace is specified + in dataSourceRef, dataSource isn''t set to the + same value and must be empty. There are three + important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types + of objects, dataSourceRef allows any non-core + object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping + them), dataSourceRef preserves all values, and + generates an error if a disallowed value is specified. + * While dataSource only allows local objects, + dataSourceRef allows objects in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource + feature gate to be enabled. (Alpha) Using the + namespace field of dataSourceRef requires the + CrossNamespaceVolumeDataSource feature gate to + be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace + is specified, a gateway.networking.k8s.io/ReferenceGrant + object is required in the referent namespace + to allow that namespace's owner to accept + the reference. See the ReferenceGrant documentation + for details. (Alpha) This field requires the + CrossNamespaceVolumeDataSource feature gate + to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: 'resources represents the minimum resources + the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify + resource requirements that are lower than previous + value but must still be higher than capacity recorded + in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. If Requests + is omitted for a container, it defaults to + Limits if that is explicitly specified, otherwise + to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'storageClassName is the name of the + StorageClass required by the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeAttributesClassName: + description: 'volumeAttributesClassName may be used + to set the VolumeAttributesClass used by this + claim. If specified, the CSI driver will create + or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This + has a different purpose than storageClassName, + it can be changed after the claim is created. + An empty string value means that no VolumeAttributesClass + will be applied to the claim but it''s not allowed + to reset this field to empty string once it is + set. If unspecified and the PersistentVolumeClaim + is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller + if it exists. If the resource referred to by volumeAttributesClass + does not exist, this PersistentVolumeClaim will + be set to a Pending state, as reflected by the + modifyVolumeStatus field, until such as a resource + exists. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + (Alpha) Using this field requires the VolumeAttributesClass + feature gate to be enabled.' + type: string + volumeMode: + description: volumeMode defines what type of volume + is required by the claim. Value of Filesystem + is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends + on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if no + secret object is specified. If the secret object contains + more than one secret, all secrets are passed to the plugin + scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: datasetName is Name of the dataset stored as + metadata -> name on the dataset for Flocker should be + considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'fsType is filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1". Similarly, the volume partition + for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource in + GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: directory is the target directory name. Must + not contain or start with '..'. If '.' is supplied, the + volume directory will be the git repository. Otherwise, + if specified, the volume will contain the git repository + in the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'hostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to + the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. If initiatorName is specified with iscsiInterface + simultaneously, new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that uses + an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. The + portal is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: targetPortal is iSCSI Target Portal. The Portal + is either an IP or ip_addr:port if the port is other than + default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a DNS_LABEL and unique + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export to + be mounted with read-only permissions. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents a + reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: defaultMode are the mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Directories within the path are + not affected by this setting. This might be in conflict + with other options that affect the file mode, like fsGroup, + and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + clusterTrustBundle: + description: "ClusterTrustBundle allows a pod to access + the `.spec.trustBundle` field of ClusterTrustBundle + objects in an auto-updating file. \n Alpha, gated + by the ClusterTrustBundleProjection feature gate. + \n ClusterTrustBundle objects can either be selected + by name, or by the combination of signer name and + a label selector. \n Kubelet performs aggressive + normalization of the PEM contents written into the + pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates + are deduplicated. The ordering of certificates within + the file is arbitrary, and Kubelet may change the + order over time." + properties: + labelSelector: + description: Select all ClusterTrustBundles that + match this label selector. Only has effect + if signerName is set. Mutually-exclusive with + name. If unset, interpreted as "match nothing". If + set but empty, interpreted as "match everything". + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, a + key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only + "value". The requirements are ANDed. + type: object + type: object + name: + description: Select a single ClusterTrustBundle + by object name. Mutually-exclusive with signerName + and labelSelector. + type: string + optional: + description: If true, don't block pod startup + if the referenced ClusterTrustBundle(s) aren't + available. If using name, then the named ClusterTrustBundle + is allowed not to exist. If using signerName, + then the combination of signerName and labelSelector + is allowed to match zero ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume root + to write the bundle. + type: string + signerName: + description: Select all ClusterTrustBundles that + match this signer name. Mutually-exclusive with + name. The contents of all selected ClusterTrustBundles + will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the ConfigMap, the volume + setup will error unless it is marked optional. + Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511. YAML accepts both octal and decimal + values, JSON requires decimal values for + mode bits. If not specified, the volume + defaultMode will be used. This might be + in conflict with other options that affect + the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu + and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced Secret + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the Secret, the volume setup + will error unless it is marked optional. Paths + must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511. YAML accepts both octal and decimal + values, JSON requires decimal values for + mode bits. If not specified, the volume + defaultMode will be used. This might be + in conflict with other options that affect + the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: audience is the intended audience + of the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: expirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, the + kubelet volume plugin will proactively rotate + the service account token. The kubelet will + start trying to rotate the token if the token + is older than 80 percent of its time to live + or if the token is older than 24 hours.Defaults + to 1 hour and must be at least 10 minutes. + format: int64 + type: integer + path: + description: path is the path relative to the + mount point of the file to project the token + into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is no + group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults to + false. + type: boolean + registry: + description: registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is + nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'user is the rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage for + a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: items If unspecified, each key-value pair in + the Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the Secret, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in the + pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: volumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within + a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS + for tighter integration. Set VolumeName to any name to + override the default behaviour. Set to "default" if you + are not using namespaces within StorageOS. Namespaces + that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The container image''s + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. If a variable + cannot be resolved, the reference in the input string will + be unchanged. Double $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references + will never be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within a shell. + The container image''s ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container''s + environment. If a variable cannot be resolved, the reference + in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: + i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether + the variable exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + If a variable cannot be resolved, the reference in the + input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) + syntax: i.e. "$$(VAR_NAME)" will produce the string + literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists + or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be + a C_IDENTIFIER. All invalid keys will be reported as an event + when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take + precedence. Values defined by an Env with a duplicate key + will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management + to default or override container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. Other management + of the container blocks until the hook completes. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents the duration that the + container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + There are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. The handler is not called if the container + crashes or exits. The Pod''s termination grace period + countdown begins before the PreStop hook is executed. + Regardless of the outcome of the handler, the container + will eventually terminate within the Pod''s termination + grace period (unless delayed by finalizers). Other management + of the container blocks until the hook completes or until + the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents the duration that the + container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + There are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to + place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and the + time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup + time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, + this value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates + stop immediately via the kill signal (no opportunity to + shut down). This is a beta field and requires enabling + ProbeTerminationGracePeriod feature gate. Minimum value + is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not + specifying a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default "0.0.0.0" + address inside a container will be accessible from the network. + Modifying this array with strategic merge patch may corrupt + the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: Number of port to expose on the pod's IP + address. This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If + specified, this must be a valid port number, 0 < x < + 65536. If HostNetwork is specified, this must match + ContainerPort. Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod + must have a unique name. Name for the port that can + be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe + fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to + place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and the + time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup + time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, + this value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates + stop immediately via the kill signal (no opportunity to + shut down). This is a beta field and requires enabling + ProbeTerminationGracePeriod feature gate. Minimum value + is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + restartPolicy: + description: 'RestartPolicy defines the restart behavior of + individual containers in a pod. This field may only be set + for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod''s restart policy + and the container type. Setting the RestartPolicy as "Always" + for the init container will have the following effect: this + init container will be continually restarted on exit until + all regular containers have terminated. Once all regular containers + have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init + containers and is often referred to as a "sidecar" container. + Although this init container still starts in the init container + sequence, it does not wait for the container to complete before + proceeding to the next init container. Instead, the next init + container starts immediately after this init container is + started, or after any startupProbe has successfully completed.' + type: string + securityContext: + description: 'SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. More + info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether + a process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by + the container runtime. Note that this field cannot be + set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent to + root on the host. Defaults to false. Note that this field + cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to + use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. This requires the ProcMountType + feature flag to be enabled. Note that this field cannot + be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root + filesystem. Default is false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a + non-root user. If true, the Kubelet will validate the + image at runtime to ensure that it does not run as UID + 0 (root) and fail to start the container if it does. If + unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a + random SELinux context for each container. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. + Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile + must be preconfigured on the node to work. Must be + a descending path, relative to the kubelet's configured + seccomp profile location. Must be set if type is "Localhost". + Must NOT be set for any other type. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - + a profile defined in a file on the node should be + used. RuntimeDefault - the container runtime default + profile should be used. Unconfined - no profile should + be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. All of a Pod's + containers must have the same effective HostProcess + value (it is not allowed to have a mix of HostProcess + containers and non-HostProcess containers). In addition, + if HostProcess is true then HostNetwork must also + be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set + in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. If this probe fails, the Pod + will be restarted, just as if the livenessProbe failed. This + can be used to provide different probe parameters at the beginning + of a Pod''s lifecycle, when it might take a long time to load + data or warm a cache, than during steady-state operation. + This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to + place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and the + time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup + time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, + this value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates + stop immediately via the kill signal (no opportunity to + shut down). This is a beta field and requires enabling + ProbeTerminationGracePeriod feature gate. Minimum value + is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, reads + from stdin in the container will always result in EOF. Default + is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the + stdin channel after it has been opened by a single attach. + When stdin is true the stdin stream will remain open across + multiple attach sessions. If stdinOnce is set to true, stdin + is opened on container start, is empty until the first client + attaches to stdin, and then remains open and accepts data + until the client disconnects, at which time stdin is closed + and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin + will never receive an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the + container''s termination message will be written is mounted + into the container''s filesystem. Message written is intended + to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. + The total message length across all containers will be limited + to 12kb. Defaults to /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be + populated. File will use the contents of terminationMessagePath + to populate the container status message on both success and + failure. FallbackToLogsOnError will use the last chunk of + container log output if the termination message file is empty + and the container exited with an error. The log output is + limited to 2048 bytes or 80 lines, whichever is smaller. Defaults + to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other + way around. When not set, MountPropagationNone is used. + This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves similarly + to SubPath but environment variable references $(VAR_NAME) + are expanded using the container's environment. Defaults + to "" (volume's root). SubPathExpr and SubPath are mutually + exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + type: object + status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string name: v1alpha1 schema: openAPIV3Schema: @@ -43,6 +3217,3189 @@ spec: spec: description: DatabaseObserverSpec defines the desired state of DatabaseObserver properties: + configuration: + properties: + configMap: + description: ConfigMapDetails defines the configmap name + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + description: DatabaseObserverDatabase defines the database details + used for DatabaseObserver + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbRole: + type: string + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + description: DatabaseObserverExporterConfig defines the configuration + details related to the exporters of DatabaseObserver + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + image: + type: string + service: + description: DatabaseObserverService defines the exporter service + component of DatabaseObserver + properties: + port: + format: int32 + type: integer + type: object + type: object + inherit_labels: + items: + type: string + type: array + log: + description: LogConfig defines the configuration details relation + to the logs of DatabaseObserver + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + description: PrometheusConfig defines the generated resources for + Prometheus + properties: + port: + type: string + release: + type: string + type: object + replicas: + format: int32 + type: integer + sidecarVolumes: + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1". Similarly, the volume partition + for /dev/sda is "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent disk + resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set). defaults to shared' + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is the + path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'user is optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: items if unspecified, each key-value pair in + the Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the ConfigMap, the volume setup will error unless it is + marked optional. Paths must be relative and may not contain + the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated + CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the secret + object contains more than one secret, all secret references + are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: readOnly specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a Optional: mode bits used to set + permissions on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to set permissions + on this file, must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'medium represents what type of storage medium + should back this directory. The default is "" which means + to use the node''s default medium. Must be an empty string + (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'sizeLimit is the total amount of local storage + required for this EmptyDir volume. The size limit is also + applicable for memory medium. The maximum usage on memory + medium EmptyDir would be the minimum value between the + SizeLimit specified here and the sum of memory limits + of all containers in a pod. The default is nil which means + that the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "ephemeral represents a volume that is handled + by a cluster storage driver. The volume's lifecycle is tied + to the pod that defines it - it will be created before the + pod starts, and deleted when the pod is removed. \n Use this + if: a) the volume is only needed while the pod runs, b) features + of normal volumes like restoring from snapshot or capacity + \ tracking are needed, c) the storage driver is specified + through a storage class, and d) the storage driver supports + dynamic volume provisioning through a PersistentVolumeClaim + (see EphemeralVolumeSource for more information on the + connection between this volume type and PersistentVolumeClaim). + \n Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. \n Use CSI for light-weight local ephemeral + volumes if the CSI driver is meant to be used that way - see + the documentation of the driver for more information. \n A + pod can use both types of ephemeral volumes and persistent + volumes at the same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC to + provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. the PVC + will be deleted together with the pod. The name of the + PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. + Pod validation will reject the pod if the concatenated + name is not valid for a PVC (for example, too long). \n + An existing PVC with that name that is not owned by the + pod will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC + is meant to be used by the pod, the PVC has to updated + with an owner reference to the pod once the pod exists. + Normally this should not be necessary, but it may be useful + when manually reconstructing a broken cluster. \n This + field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. \n Required, must + not be nil." + properties: + metadata: + description: May contain labels and annotations that + will be copied into the PVC when creating it. No other + fields are allowed and will be rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the PVC + that gets created from this template. The same fields + as in a PersistentVolumeClaim are also valid here. + properties: + accessModes: + description: 'accessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) If the + provisioner or an external controller can support + the specified data source, it will create a new + volume based on the contents of the specified + data source. When the AnyVolumeDataSource feature + gate is enabled, dataSource contents will be copied + to dataSourceRef, and dataSourceRef contents will + be copied to dataSource when dataSourceRef.namespace + is not specified. If the namespace is specified, + then dataSourceRef will not be copied to dataSource.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'dataSourceRef specifies the object + from which to populate the volume with data, if + a non-empty volume is desired. This may be any + object from a non-empty API group (non core object) + or a PersistentVolumeClaim object. When this field + is specified, volume binding will only succeed + if the type of the specified object matches some + installed volume populator or dynamic provisioner. + This field will replace the functionality of the + dataSource field and as such if both fields are + non-empty, they must have the same value. For + backwards compatibility, when namespace isn''t + specified in dataSourceRef, both fields (dataSource + and dataSourceRef) will be set to the same value + automatically if one of them is empty and the + other is non-empty. When namespace is specified + in dataSourceRef, dataSource isn''t set to the + same value and must be empty. There are three + important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types + of objects, dataSourceRef allows any non-core + object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping + them), dataSourceRef preserves all values, and + generates an error if a disallowed value is specified. + * While dataSource only allows local objects, + dataSourceRef allows objects in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource + feature gate to be enabled. (Alpha) Using the + namespace field of dataSourceRef requires the + CrossNamespaceVolumeDataSource feature gate to + be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace + is specified, a gateway.networking.k8s.io/ReferenceGrant + object is required in the referent namespace + to allow that namespace's owner to accept + the reference. See the ReferenceGrant documentation + for details. (Alpha) This field requires the + CrossNamespaceVolumeDataSource feature gate + to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: 'resources represents the minimum resources + the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify + resource requirements that are lower than previous + value but must still be higher than capacity recorded + in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. If Requests + is omitted for a container, it defaults to + Limits if that is explicitly specified, otherwise + to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'storageClassName is the name of the + StorageClass required by the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeAttributesClassName: + description: 'volumeAttributesClassName may be used + to set the VolumeAttributesClass used by this + claim. If specified, the CSI driver will create + or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This + has a different purpose than storageClassName, + it can be changed after the claim is created. + An empty string value means that no VolumeAttributesClass + will be applied to the claim but it''s not allowed + to reset this field to empty string once it is + set. If unspecified and the PersistentVolumeClaim + is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller + if it exists. If the resource referred to by volumeAttributesClass + does not exist, this PersistentVolumeClaim will + be set to a Pending state, as reflected by the + modifyVolumeStatus field, until such as a resource + exists. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + (Alpha) Using this field requires the VolumeAttributesClass + feature gate to be enabled.' + type: string + volumeMode: + description: volumeMode defines what type of volume + is required by the claim. Value of Filesystem + is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends + on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if no + secret object is specified. If the secret object contains + more than one secret, all secrets are passed to the plugin + scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: datasetName is Name of the dataset stored as + metadata -> name on the dataset for Flocker should be + considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'fsType is filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1". Similarly, the volume partition + for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource in + GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: directory is the target directory name. Must + not contain or start with '..'. If '.' is supplied, the + volume directory will be the git repository. Otherwise, + if specified, the volume will contain the git repository + in the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'hostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to + the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. If initiatorName is specified with iscsiInterface + simultaneously, new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that uses + an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. The + portal is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: targetPortal is iSCSI Target Portal. The Portal + is either an IP or ip_addr:port if the port is other than + default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a DNS_LABEL and unique + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export to + be mounted with read-only permissions. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents a + reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: defaultMode are the mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Directories within the path are + not affected by this setting. This might be in conflict + with other options that affect the file mode, like fsGroup, + and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + clusterTrustBundle: + description: "ClusterTrustBundle allows a pod to access + the `.spec.trustBundle` field of ClusterTrustBundle + objects in an auto-updating file. \n Alpha, gated + by the ClusterTrustBundleProjection feature gate. + \n ClusterTrustBundle objects can either be selected + by name, or by the combination of signer name and + a label selector. \n Kubelet performs aggressive + normalization of the PEM contents written into the + pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates + are deduplicated. The ordering of certificates within + the file is arbitrary, and Kubelet may change the + order over time." + properties: + labelSelector: + description: Select all ClusterTrustBundles that + match this label selector. Only has effect + if signerName is set. Mutually-exclusive with + name. If unset, interpreted as "match nothing". If + set but empty, interpreted as "match everything". + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, a + key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only + "value". The requirements are ANDed. + type: object + type: object + name: + description: Select a single ClusterTrustBundle + by object name. Mutually-exclusive with signerName + and labelSelector. + type: string + optional: + description: If true, don't block pod startup + if the referenced ClusterTrustBundle(s) aren't + available. If using name, then the named ClusterTrustBundle + is allowed not to exist. If using signerName, + then the combination of signerName and labelSelector + is allowed to match zero ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume root + to write the bundle. + type: string + signerName: + description: Select all ClusterTrustBundles that + match this signer name. Mutually-exclusive with + name. The contents of all selected ClusterTrustBundles + will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the ConfigMap, the volume + setup will error unless it is marked optional. + Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511. YAML accepts both octal and decimal + values, JSON requires decimal values for + mode bits. If not specified, the volume + defaultMode will be used. This might be + in conflict with other options that affect + the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu + and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced Secret + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the Secret, the volume setup + will error unless it is marked optional. Paths + must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511. YAML accepts both octal and decimal + values, JSON requires decimal values for + mode bits. If not specified, the volume + defaultMode will be used. This might be + in conflict with other options that affect + the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: audience is the intended audience + of the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: expirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, the + kubelet volume plugin will proactively rotate + the service account token. The kubelet will + start trying to rotate the token if the token + is older than 80 percent of its time to live + or if the token is older than 24 hours.Defaults + to 1 hour and must be at least 10 minutes. + format: int64 + type: integer + path: + description: path is the path relative to the + mount point of the file to project the token + into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is no + group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults to + false. + type: boolean + registry: + description: registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is + nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'user is the rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage for + a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: items If unspecified, each key-value pair in + the Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the Secret, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in the + pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: volumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within + a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS + for tighter integration. Set VolumeName to any name to + override the default behaviour. Set to "default" if you + are not using namespaces within StorageOS. Namespaces + that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The container image''s + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. If a variable + cannot be resolved, the reference in the input string will + be unchanged. Double $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references + will never be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within a shell. + The container image''s ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container''s + environment. If a variable cannot be resolved, the reference + in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: + i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether + the variable exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + If a variable cannot be resolved, the reference in the + input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) + syntax: i.e. "$$(VAR_NAME)" will produce the string + literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists + or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be + a C_IDENTIFIER. All invalid keys will be reported as an event + when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take + precedence. Values defined by an Env with a duplicate key + will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management + to default or override container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. Other management + of the container blocks until the hook completes. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents the duration that the + container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + There are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. The handler is not called if the container + crashes or exits. The Pod''s termination grace period + countdown begins before the PreStop hook is executed. + Regardless of the outcome of the handler, the container + will eventually terminate within the Pod''s termination + grace period (unless delayed by finalizers). Other management + of the container blocks until the hook completes or until + the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents the duration that the + container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + There are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to + place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and the + time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup + time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, + this value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates + stop immediately via the kill signal (no opportunity to + shut down). This is a beta field and requires enabling + ProbeTerminationGracePeriod feature gate. Minimum value + is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not + specifying a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default "0.0.0.0" + address inside a container will be accessible from the network. + Modifying this array with strategic merge patch may corrupt + the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: Number of port to expose on the pod's IP + address. This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If + specified, this must be a valid port number, 0 < x < + 65536. If HostNetwork is specified, this must match + ContainerPort. Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod + must have a unique name. Name for the port that can + be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe + fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to + place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and the + time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup + time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, + this value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates + stop immediately via the kill signal (no opportunity to + shut down). This is a beta field and requires enabling + ProbeTerminationGracePeriod feature gate. Minimum value + is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + restartPolicy: + description: 'RestartPolicy defines the restart behavior of + individual containers in a pod. This field may only be set + for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod''s restart policy + and the container type. Setting the RestartPolicy as "Always" + for the init container will have the following effect: this + init container will be continually restarted on exit until + all regular containers have terminated. Once all regular containers + have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init + containers and is often referred to as a "sidecar" container. + Although this init container still starts in the init container + sequence, it does not wait for the container to complete before + proceeding to the next init container. Instead, the next init + container starts immediately after this init container is + started, or after any startupProbe has successfully completed.' + type: string + securityContext: + description: 'SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. More + info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether + a process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by + the container runtime. Note that this field cannot be + set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent to + root on the host. Defaults to false. Note that this field + cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to + use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. This requires the ProcMountType + feature flag to be enabled. Note that this field cannot + be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root + filesystem. Default is false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a + non-root user. If true, the Kubelet will validate the + image at runtime to ensure that it does not run as UID + 0 (root) and fail to start the container if it does. If + unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a + random SELinux context for each container. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. + Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile + must be preconfigured on the node to work. Must be + a descending path, relative to the kubelet's configured + seccomp profile location. Must be set if type is "Localhost". + Must NOT be set for any other type. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - + a profile defined in a file on the node should be + used. RuntimeDefault - the container runtime default + profile should be used. Unconfined - no profile should + be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. All of a Pod's + containers must have the same effective HostProcess + value (it is not allowed to have a mix of HostProcess + containers and non-HostProcess containers). In addition, + if HostProcess is true then HostNetwork must also + be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set + in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. If this probe fails, the Pod + will be restarted, just as if the livenessProbe failed. This + can be used to provide different probe parameters at the beginning + of a Pod''s lifecycle, when it might take a long time to load + data or warm a cache, than during steady-state operation. + This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to + place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and the + time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup + time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, + this value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates + stop immediately via the kill signal (no opportunity to + shut down). This is a beta field and requires enabling + ProbeTerminationGracePeriod feature gate. Minimum value + is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, reads + from stdin in the container will always result in EOF. Default + is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the + stdin channel after it has been opened by a single attach. + When stdin is true the stdin stream will remain open across + multiple attach sessions. If stdinOnce is set to true, stdin + is opened on container start, is empty until the first client + attaches to stdin, and then remains open and accepts data + until the client disconnects, at which time stdin is closed + and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin + will never receive an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the + container''s termination message will be written is mounted + into the container''s filesystem. Message written is intended + to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. + The total message length across all containers will be limited + to 12kb. Defaults to /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be + populated. File will use the contents of terminationMessagePath + to populate the container status message on both success and + failure. FallbackToLogsOnError will use the last chunk of + container log output if the termination message file is empty + and the container exited with an error. The log output is + limited to 2048 bytes or 80 lines, whichever is smaller. Defaults + to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other + way around. When not set, MountPropagationNone is used. + This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves similarly + to SubPath but environment variable references $(VAR_NAME) + are expanded using the container's environment. Defaults + to "" (volume's root). SubPathExpr and SubPath are mutually + exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + type: object + status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string + name: v4 + schema: + openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver + properties: + configuration: + properties: + configMap: + description: ConfigMapDetails defines the configmap name + properties: + key: + type: string + name: + type: string + type: object + type: object database: description: DatabaseObserverDatabase defines the database details used for DatabaseObserver @@ -65,6 +6422,8 @@ spec: vaultSecretName: type: string type: object + dbRole: + type: string dbUser: properties: key: @@ -84,17 +6443,14 @@ spec: description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver properties: - configuration: - properties: - configmap: - description: ConfigMapDetails defines the configmap name - properties: - configmapName: - type: string - key: - type: string - type: object - type: object + args: + items: + type: string + type: array + commands: + items: + type: string + type: array image: type: string service: @@ -106,6 +6462,29 @@ spec: type: integer type: object type: object + inherit_labels: + items: + type: string + type: array + log: + description: LogConfig defines the configuration details relation + to the logs of DatabaseObserver + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object ociConfig: properties: configMapName: @@ -117,24 +6496,2954 @@ spec: description: PrometheusConfig defines the generated resources for Prometheus properties: - labels: - additionalProperties: - type: string - type: object port: type: string + release: + type: string type: object replicas: format: int32 type: integer + sidecarVolumes: + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1". Similarly, the volume partition + for /dev/sda is "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent disk + resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set). defaults to shared' + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is the + path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'user is optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: items if unspecified, each key-value pair in + the Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the ConfigMap, the volume setup will error unless it is + marked optional. Paths must be relative and may not contain + the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated + CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the secret + object contains more than one secret, all secret references + are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: readOnly specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a Optional: mode bits used to set + permissions on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to set permissions + on this file, must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'medium represents what type of storage medium + should back this directory. The default is "" which means + to use the node''s default medium. Must be an empty string + (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'sizeLimit is the total amount of local storage + required for this EmptyDir volume. The size limit is also + applicable for memory medium. The maximum usage on memory + medium EmptyDir would be the minimum value between the + SizeLimit specified here and the sum of memory limits + of all containers in a pod. The default is nil which means + that the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "ephemeral represents a volume that is handled + by a cluster storage driver. The volume's lifecycle is tied + to the pod that defines it - it will be created before the + pod starts, and deleted when the pod is removed. \n Use this + if: a) the volume is only needed while the pod runs, b) features + of normal volumes like restoring from snapshot or capacity + \ tracking are needed, c) the storage driver is specified + through a storage class, and d) the storage driver supports + dynamic volume provisioning through a PersistentVolumeClaim + (see EphemeralVolumeSource for more information on the + connection between this volume type and PersistentVolumeClaim). + \n Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. \n Use CSI for light-weight local ephemeral + volumes if the CSI driver is meant to be used that way - see + the documentation of the driver for more information. \n A + pod can use both types of ephemeral volumes and persistent + volumes at the same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC to + provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. the PVC + will be deleted together with the pod. The name of the + PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. + Pod validation will reject the pod if the concatenated + name is not valid for a PVC (for example, too long). \n + An existing PVC with that name that is not owned by the + pod will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC + is meant to be used by the pod, the PVC has to updated + with an owner reference to the pod once the pod exists. + Normally this should not be necessary, but it may be useful + when manually reconstructing a broken cluster. \n This + field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. \n Required, must + not be nil." + properties: + metadata: + description: May contain labels and annotations that + will be copied into the PVC when creating it. No other + fields are allowed and will be rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the PVC + that gets created from this template. The same fields + as in a PersistentVolumeClaim are also valid here. + properties: + accessModes: + description: 'accessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) If the + provisioner or an external controller can support + the specified data source, it will create a new + volume based on the contents of the specified + data source. When the AnyVolumeDataSource feature + gate is enabled, dataSource contents will be copied + to dataSourceRef, and dataSourceRef contents will + be copied to dataSource when dataSourceRef.namespace + is not specified. If the namespace is specified, + then dataSourceRef will not be copied to dataSource.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'dataSourceRef specifies the object + from which to populate the volume with data, if + a non-empty volume is desired. This may be any + object from a non-empty API group (non core object) + or a PersistentVolumeClaim object. When this field + is specified, volume binding will only succeed + if the type of the specified object matches some + installed volume populator or dynamic provisioner. + This field will replace the functionality of the + dataSource field and as such if both fields are + non-empty, they must have the same value. For + backwards compatibility, when namespace isn''t + specified in dataSourceRef, both fields (dataSource + and dataSourceRef) will be set to the same value + automatically if one of them is empty and the + other is non-empty. When namespace is specified + in dataSourceRef, dataSource isn''t set to the + same value and must be empty. There are three + important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types + of objects, dataSourceRef allows any non-core + object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping + them), dataSourceRef preserves all values, and + generates an error if a disallowed value is specified. + * While dataSource only allows local objects, + dataSourceRef allows objects in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource + feature gate to be enabled. (Alpha) Using the + namespace field of dataSourceRef requires the + CrossNamespaceVolumeDataSource feature gate to + be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace + is specified, a gateway.networking.k8s.io/ReferenceGrant + object is required in the referent namespace + to allow that namespace's owner to accept + the reference. See the ReferenceGrant documentation + for details. (Alpha) This field requires the + CrossNamespaceVolumeDataSource feature gate + to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: 'resources represents the minimum resources + the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify + resource requirements that are lower than previous + value but must still be higher than capacity recorded + in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. If Requests + is omitted for a container, it defaults to + Limits if that is explicitly specified, otherwise + to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'storageClassName is the name of the + StorageClass required by the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeAttributesClassName: + description: 'volumeAttributesClassName may be used + to set the VolumeAttributesClass used by this + claim. If specified, the CSI driver will create + or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This + has a different purpose than storageClassName, + it can be changed after the claim is created. + An empty string value means that no VolumeAttributesClass + will be applied to the claim but it''s not allowed + to reset this field to empty string once it is + set. If unspecified and the PersistentVolumeClaim + is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller + if it exists. If the resource referred to by volumeAttributesClass + does not exist, this PersistentVolumeClaim will + be set to a Pending state, as reflected by the + modifyVolumeStatus field, until such as a resource + exists. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + (Alpha) Using this field requires the VolumeAttributesClass + feature gate to be enabled.' + type: string + volumeMode: + description: volumeMode defines what type of volume + is required by the claim. Value of Filesystem + is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends + on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if no + secret object is specified. If the secret object contains + more than one secret, all secrets are passed to the plugin + scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: datasetName is Name of the dataset stored as + metadata -> name on the dataset for Flocker should be + considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'fsType is filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1". Similarly, the volume partition + for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource in + GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: directory is the target directory name. Must + not contain or start with '..'. If '.' is supplied, the + volume directory will be the git repository. Otherwise, + if specified, the volume will contain the git repository + in the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'hostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to + the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. If initiatorName is specified with iscsiInterface + simultaneously, new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that uses + an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. The + portal is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: targetPortal is iSCSI Target Portal. The Portal + is either an IP or ip_addr:port if the port is other than + default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a DNS_LABEL and unique + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export to + be mounted with read-only permissions. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents a + reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: defaultMode are the mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Directories within the path are + not affected by this setting. This might be in conflict + with other options that affect the file mode, like fsGroup, + and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + clusterTrustBundle: + description: "ClusterTrustBundle allows a pod to access + the `.spec.trustBundle` field of ClusterTrustBundle + objects in an auto-updating file. \n Alpha, gated + by the ClusterTrustBundleProjection feature gate. + \n ClusterTrustBundle objects can either be selected + by name, or by the combination of signer name and + a label selector. \n Kubelet performs aggressive + normalization of the PEM contents written into the + pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates + are deduplicated. The ordering of certificates within + the file is arbitrary, and Kubelet may change the + order over time." + properties: + labelSelector: + description: Select all ClusterTrustBundles that + match this label selector. Only has effect + if signerName is set. Mutually-exclusive with + name. If unset, interpreted as "match nothing". If + set but empty, interpreted as "match everything". + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, a + key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only + "value". The requirements are ANDed. + type: object + type: object + name: + description: Select a single ClusterTrustBundle + by object name. Mutually-exclusive with signerName + and labelSelector. + type: string + optional: + description: If true, don't block pod startup + if the referenced ClusterTrustBundle(s) aren't + available. If using name, then the named ClusterTrustBundle + is allowed not to exist. If using signerName, + then the combination of signerName and labelSelector + is allowed to match zero ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume root + to write the bundle. + type: string + signerName: + description: Select all ClusterTrustBundles that + match this signer name. Mutually-exclusive with + name. The contents of all selected ClusterTrustBundles + will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the ConfigMap, the volume + setup will error unless it is marked optional. + Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511. YAML accepts both octal and decimal + values, JSON requires decimal values for + mode bits. If not specified, the volume + defaultMode will be used. This might be + in conflict with other options that affect + the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu + and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced Secret + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the Secret, the volume setup + will error unless it is marked optional. Paths + must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511. YAML accepts both octal and decimal + values, JSON requires decimal values for + mode bits. If not specified, the volume + defaultMode will be used. This might be + in conflict with other options that affect + the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: audience is the intended audience + of the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: expirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, the + kubelet volume plugin will proactively rotate + the service account token. The kubelet will + start trying to rotate the token if the token + is older than 80 percent of its time to live + or if the token is older than 24 hours.Defaults + to 1 hour and must be at least 10 minutes. + format: int64 + type: integer + path: + description: path is the path relative to the + mount point of the file to project the token + into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is no + group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults to + false. + type: boolean + registry: + description: registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is + nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'user is the rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage for + a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: items If unspecified, each key-value pair in + the Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the Secret, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in the + pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: volumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within + a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS + for tighter integration. Set VolumeName to any name to + override the default behaviour. Set to "default" if you + are not using namespaces within StorageOS. Namespaces + that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The container image''s + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. If a variable + cannot be resolved, the reference in the input string will + be unchanged. Double $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references + will never be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within a shell. + The container image''s ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container''s + environment. If a variable cannot be resolved, the reference + in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: + i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether + the variable exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + If a variable cannot be resolved, the reference in the + input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) + syntax: i.e. "$$(VAR_NAME)" will produce the string + literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists + or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be + a C_IDENTIFIER. All invalid keys will be reported as an event + when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take + precedence. Values defined by an Env with a duplicate key + will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management + to default or override container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. Other management + of the container blocks until the hook completes. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents the duration that the + container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + There are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. The handler is not called if the container + crashes or exits. The Pod''s termination grace period + countdown begins before the PreStop hook is executed. + Regardless of the outcome of the handler, the container + will eventually terminate within the Pod''s termination + grace period (unless delayed by finalizers). Other management + of the container blocks until the hook completes or until + the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents the duration that the + container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + There are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to + place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and the + time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup + time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, + this value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates + stop immediately via the kill signal (no opportunity to + shut down). This is a beta field and requires enabling + ProbeTerminationGracePeriod feature gate. Minimum value + is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not + specifying a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default "0.0.0.0" + address inside a container will be accessible from the network. + Modifying this array with strategic merge patch may corrupt + the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: Number of port to expose on the pod's IP + address. This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If + specified, this must be a valid port number, 0 < x < + 65536. If HostNetwork is specified, this must match + ContainerPort. Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod + must have a unique name. Name for the port that can + be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe + fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to + place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and the + time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup + time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, + this value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates + stop immediately via the kill signal (no opportunity to + shut down). This is a beta field and requires enabling + ProbeTerminationGracePeriod feature gate. Minimum value + is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + restartPolicy: + description: 'RestartPolicy defines the restart behavior of + individual containers in a pod. This field may only be set + for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod''s restart policy + and the container type. Setting the RestartPolicy as "Always" + for the init container will have the following effect: this + init container will be continually restarted on exit until + all regular containers have terminated. Once all regular containers + have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init + containers and is often referred to as a "sidecar" container. + Although this init container still starts in the init container + sequence, it does not wait for the container to complete before + proceeding to the next init container. Instead, the next init + container starts immediately after this init container is + started, or after any startupProbe has successfully completed.' + type: string + securityContext: + description: 'SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. More + info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether + a process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by + the container runtime. Note that this field cannot be + set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent to + root on the host. Defaults to false. Note that this field + cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to + use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. This requires the ProcMountType + feature flag to be enabled. Note that this field cannot + be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root + filesystem. Default is false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a + non-root user. If true, the Kubelet will validate the + image at runtime to ensure that it does not run as UID + 0 (root) and fail to start the container if it does. If + unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a + random SELinux context for each container. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. + Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile + must be preconfigured on the node to work. Must be + a descending path, relative to the kubelet's configured + seccomp profile location. Must be set if type is "Localhost". + Must NOT be set for any other type. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - + a profile defined in a file on the node should be + used. RuntimeDefault - the container runtime default + profile should be used. Unconfined - no profile should + be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. All of a Pod's + containers must have the same effective HostProcess + value (it is not allowed to have a mix of HostProcess + containers and non-HostProcess containers). In addition, + if HostProcess is true then HostNetwork must also + be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set + in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. If this probe fails, the Pod + will be restarted, just as if the livenessProbe failed. This + can be used to provide different probe parameters at the beginning + of a Pod''s lifecycle, when it might take a long time to load + data or warm a cache, than during steady-state operation. + This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to + place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and the + time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup + time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, + this value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates + stop immediately via the kill signal (no opportunity to + shut down). This is a beta field and requires enabling + ProbeTerminationGracePeriod feature gate. Minimum value + is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, reads + from stdin in the container will always result in EOF. Default + is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the + stdin channel after it has been opened by a single attach. + When stdin is true the stdin stream will remain open across + multiple attach sessions. If stdinOnce is set to true, stdin + is opened on container start, is empty until the first client + attaches to stdin, and then remains open and accepts data + until the client disconnects, at which time stdin is closed + and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin + will never receive an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the + container''s termination message will be written is mounted + into the container''s filesystem. Message written is intended + to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. + The total message length across all containers will be limited + to 12kb. Defaults to /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be + populated. File will use the contents of terminationMessagePath + to populate the container status message on both success and + failure. FallbackToLogsOnError will use the last chunk of + container log output if the termination message file is empty + and the container exited with an error. The log output is + limited to 2048 bytes or 80 lines, whichever is smaller. Defaults + to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other + way around. When not set, MountPropagationNone is used. + This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves similarly + to SubPath but environment variable references $(VAR_NAME) + are expanded using the container's environment. Defaults + to "" (volume's root). SubPathExpr and SubPath are mutually + exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array type: object status: description: DatabaseObserverStatus defines the observed state of DatabaseObserver properties: conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' items: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct @@ -210,9 +9519,12 @@ spec: type: integer status: type: string + version: + type: string required: - conditions - exporterConfig + - version type: object type: object served: true diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 40abec3b..f08c784d 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -29,6 +29,16 @@ resources: - adb/autonomousdatabase_bind.yaml - adb/autonomousdatabase_backup.yaml - adb/autonomousdatabase_restore.yaml + - acd/autonomouscontainerdatabase_create.yaml + - sidb/singleinstancedatabase.yaml + - sharding/shardingdatabase.yaml + - sharding/sharding_v1alpha1_provshard.yaml + - dbcs/database_v1alpha1_dbcssystem.yaml + - database_v1alpha1_dataguardbroker.yaml + - database_v1alpha1_shardingdatabase.yaml + - observability/v1alpha1/databaseobserver.yaml + - observability/v1/databaseobserver.yaml + - observability/v4/databaseobserver.yaml - acd/autonomouscontainerdatabase_restart_terminate.yaml - database_v4_shardingdatabase.yaml - database_v4_dbcssystem.yaml diff --git a/config/samples/observability/databaseobserver.yaml b/config/samples/observability/databaseobserver.yaml deleted file mode 100644 index b3140549..00000000 --- a/config/samples/observability/databaseobserver.yaml +++ /dev/null @@ -1,44 +0,0 @@ -# example -apiVersion: observability.oracle.com/v1alpha1 -kind: DatabaseObserver -metadata: - name: obs-sample -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - key: "password" - secret: db-secret - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallet - - exporter: - image: "container-registry.oracle.com/database/observability-exporter:latest" - configuration: - configmap: - key: "config.toml" - configmapName: "devcm-oradevdb-config" - - service: - port: 9161 - - prometheus: - port: metrics - labels: - app: app-sample-label - - replicas: 1 - - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey - - diff --git a/config/samples/observability/v1/databaseobserver.yaml b/config/samples/observability/v1/databaseobserver.yaml new file mode 100644 index 00000000..82a7e89e --- /dev/null +++ b/config/samples/observability/v1/databaseobserver.yaml @@ -0,0 +1,81 @@ +# example +apiVersion: observability.oracle.com/v1 +kind: DatabaseObserver +metadata: + name: obs-sample + labels: + app.kubernetes.io/name: observability-exporter + app.kubernetes.io/instance: obs-sample + app.kubernetes.io/version: 1.5.1 +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + inheritLabels: + - app.kubernetes.io/name + - app.kubernetes.io/instance + - app.kubernetes.io/version + + sidecars: [ ] + sidecarVolumes: [ ] + + exporter: + deployment: + env: + TNS_ADMIN: /some/custom/path + ORACLE_HOME: /some/custom/path + DB_ROLE: SYSDBA + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + labels: + environment: dev + podTemplate: + labels: + environment: dev + + service: + labels: + environment: dev + + configuration: + configMap: + key: "config.toml" + name: "devcm-oradevdb-config" + + prometheus: + serviceMonitor: + labels: + release: prometheus + + + log: + filename: "alert.log" + path: "/log" + + volume: + name: volume + persistentVolumeClaim: + claimName: "my-pvc" + + replicas: 1 + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + + diff --git a/config/samples/observability/v1/databaseobserver_customization_fields.yaml b/config/samples/observability/v1/databaseobserver_customization_fields.yaml new file mode 100644 index 00000000..d88caec4 --- /dev/null +++ b/config/samples/observability/v1/databaseobserver_customization_fields.yaml @@ -0,0 +1,54 @@ +# example +apiVersion: observability.oracle.com/v4 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallets + + exporter: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: + - "--log.level=info" + commands: + - "/oracledb_exporter" + env: + TNS_ADMIN: /some/custom/path + labels: + environment: dev + podTemplate: + labels: + environment: dev + service: + ports: + - name: "metrics" + port: 9161 + targetPort: 9161 + labels: + environment: dev + + prometheus: + serviceMonitor: + endpoints: + - bearerTokenSecret: + key: '' + interval: 15s + port: metrics + labels: + release: prometheus + diff --git a/config/samples/observability/v1/databaseobserver_logs_promtail.yaml b/config/samples/observability/v1/databaseobserver_logs_promtail.yaml new file mode 100644 index 00000000..3a92cfea --- /dev/null +++ b/config/samples/observability/v1/databaseobserver_logs_promtail.yaml @@ -0,0 +1,77 @@ +# example +apiVersion: observability.oracle.com/v1 +kind: DatabaseObserver +metadata: + name: obs-sample + labels: + app.kubernetes.io/name: observability-exporter + app.kubernetes.io/instance: obs-sample + app.kubernetes.io/version: 1.5.1 +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + inheritLabels: + - app.kubernetes.io/name + - app.kubernetes.io/instance + - app.kubernetes.io/version + + sidecars: + - name: promtail + image: grafana/promtail + args: + - -config.file=/etc/promtail/promtail.yaml + volumeMounts: + - name: config + mountPath: /etc/promtail + - name: log-volume + mountPath: /log + + sidecarVolumes: + - name: config + configMap: + name: promtail-sidecar-config + exporter: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + service: + port: 9161 + + configuration: + configMap: + key: "config.toml" + name: "devcm-oradevdb-config" + + prometheus: + serviceMonitor: + labels: + release: prometheus + + log: + filename: "alert.log" + path: "/log" + + volume: + name: log-volume + + replicas: 1 + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey diff --git a/config/samples/observability/v1alpha1/databaseobserver.yaml b/config/samples/observability/v1alpha1/databaseobserver.yaml new file mode 100644 index 00000000..24672d8b --- /dev/null +++ b/config/samples/observability/v1alpha1/databaseobserver.yaml @@ -0,0 +1,80 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample + labels: + app.kubernetes.io/name: observability-exporter + app.kubernetes.io/instance: obs-sample + app.kubernetes.io/version: 1.5.1 +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + + inheritLabels: + - app.kubernetes.io/name + - app.kubernetes.io/instance + - app.kubernetes.io/version + + sidecars: [ ] + sidecarVolumes: [ ] + + exporter: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + env: + TNS_ADMIN: /some/custom/path + ORACLE_HOME: /some/custom/path + labels: + environment: dev + podTemplate: + labels: + environment: dev + + service: + labels: + environment: dev + + configuration: + configMap: + key: "config.toml" + name: "devcm-oradevdb-config" + + prometheus: + serviceMonitor: + labels: + release: prometheus + + + log: + filename: "alert.log" + path: "/log" + + volume: + name: volume + persistentVolumeClaim: + claimName: "my-pvc" + + replicas: 1 + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + + diff --git a/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml b/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml new file mode 100644 index 00000000..6aec94c2 --- /dev/null +++ b/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml @@ -0,0 +1,49 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample + labels: + app.kubernetes.io/name: observability-exporter + app.kubernetes.io/instance: obs-sample + app.kubernetes.io/version: 1.5.1 +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + inherit_labels: + - app.kubernetes.io/name + - app.kubernetes.io/instance + - app.kubernetes.io/version + + exporter: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + service: + port: 9161 + + prometheus: + serviceMonitor: + labels: + release: prometheus + + configuration: + configMap: + key: "config.toml" + name: "devcm-oradevdb-config" \ No newline at end of file diff --git a/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml b/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml new file mode 100644 index 00000000..0d9cc434 --- /dev/null +++ b/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml @@ -0,0 +1,77 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample + labels: + app.kubernetes.io/name: observability-exporter + app.kubernetes.io/instance: obs-sample + app.kubernetes.io/version: 1.5.1 +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + inheritLabels: + - app.kubernetes.io/name + - app.kubernetes.io/instance + - app.kubernetes.io/version + + sidecars: + - name: promtail + image: grafana/promtail + args: + - -config.file=/etc/promtail/promtail.yaml + volumeMounts: + - name: config + mountPath: /etc/promtail + - name: log-volume + mountPath: /log + + sidecarVolumes: + - name: config + configMap: + name: promtail-sidecar-config + exporter: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + service: + port: 9161 + + configuration: + configMap: + key: "config.toml" + name: "devcm-oradevdb-config" + + prometheus: + serviceMonitor: + labels: + release: prometheus + + log: + filename: "alert.log" + path: "/log" + + volume: + name: log-volume + + replicas: 1 + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey diff --git a/config/samples/observability/databaseobserver_minimal.yaml b/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml similarity index 75% rename from config/samples/observability/databaseobserver_minimal.yaml rename to config/samples/observability/v1alpha1/databaseobserver_minimal.yaml index 2eeaf3ab..74620ac7 100644 --- a/config/samples/observability/databaseobserver_minimal.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml @@ -3,7 +3,6 @@ apiVersion: observability.oracle.com/v1alpha1 kind: DatabaseObserver metadata: name: obs-sample - namespace: observer spec: database: dbUser: @@ -19,4 +18,9 @@ spec: secret: db-secret dbWallet: - secret: instance-wallets \ No newline at end of file + secret: instance-wallets + + prometheus: + serviceMonitor: + labels: + release: prometheus \ No newline at end of file diff --git a/config/samples/observability/databaseobserver_vault.yaml b/config/samples/observability/v1alpha1/databaseobserver_vault.yaml similarity index 86% rename from config/samples/observability/databaseobserver_vault.yaml rename to config/samples/observability/v1alpha1/databaseobserver_vault.yaml index fa2e09d4..2fc3c9f0 100644 --- a/config/samples/observability/databaseobserver_vault.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_vault.yaml @@ -20,6 +20,11 @@ spec: dbWallet: secret: instance-wallet + prometheus: + serviceMonitor: + labels: + release: prometheus + ociConfig: configMapName: oci-cred secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/observability/v4/databaseobserver.yaml b/config/samples/observability/v4/databaseobserver.yaml new file mode 100644 index 00000000..f7b310f7 --- /dev/null +++ b/config/samples/observability/v4/databaseobserver.yaml @@ -0,0 +1,79 @@ +# example +apiVersion: observability.oracle.com/v4 +kind: DatabaseObserver +metadata: + name: obs-sample + labels: + app.kubernetes.io/name: observability-exporter + app.kubernetes.io/instance: obs-sample + app.kubernetes.io/version: latest +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + inheritLabels: + - app.kubernetes.io/name + - app.kubernetes.io/instance + - app.kubernetes.io/version + + sidecars: [ ] + sidecarVolumes: [ ] + + exporter: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + env: + TNS_ADMIN: /some/custom/path + ORACLE_HOME: /some/custom/path + labels: + environment: dev + podTemplate: + labels: + environment: dev + + service: + labels: + environment: dev + + + configuration: + configMap: + key: "config.toml" + name: "devcm-oradevdb-config" + + prometheus: + serviceMonitor: + labels: + release: prometheus + + log: + filename: "alert.log" + path: "/log" + + volume: + name: volume + persistentVolumeClaim: + claimName: "my-pvc" + + replicas: 1 + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + + diff --git a/config/samples/observability/v4/databaseobserver_custom_config.yaml b/config/samples/observability/v4/databaseobserver_custom_config.yaml new file mode 100644 index 00000000..c4258b0f --- /dev/null +++ b/config/samples/observability/v4/databaseobserver_custom_config.yaml @@ -0,0 +1,49 @@ +# example +apiVersion: observability.oracle.com/v4 +kind: DatabaseObserver +metadata: + name: obs-sample + labels: + app.kubernetes.io/name: observability-exporter + app.kubernetes.io/instance: obs-sample + app.kubernetes.io/version: latest +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + inherit_labels: + - app.kubernetes.io/name + - app.kubernetes.io/instance + - app.kubernetes.io/version + + exporter: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + service: + port: 9161 + + configuration: + configMap: + key: "config.toml" + name: "devcm-oradevdb-config" + + prometheus: + serviceMonitor: + labels: + release: prometheus \ No newline at end of file diff --git a/config/samples/observability/v4/databaseobserver_logs_promtail.yaml b/config/samples/observability/v4/databaseobserver_logs_promtail.yaml new file mode 100644 index 00000000..b1a40281 --- /dev/null +++ b/config/samples/observability/v4/databaseobserver_logs_promtail.yaml @@ -0,0 +1,79 @@ +# example +apiVersion: observability.oracle.com/v4 +kind: DatabaseObserver +metadata: + name: obs-sample + labels: + app.kubernetes.io/name: observability-exporter + app.kubernetes.io/instance: obs-sample + app.kubernetes.io/version: latest +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + inheritLabels: + - app.kubernetes.io/name + - app.kubernetes.io/instance + - app.kubernetes.io/version + + sidecars: + - name: promtail + image: grafana/promtail + args: + - -config.file=/etc/promtail/promtail.yaml + volumeMounts: + - name: config + mountPath: /etc/promtail + - name: log-volume + mountPath: /log + + sidecarVolumes: + - name: config + configMap: + name: promtail-sidecar-config + exporter: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + service: + port: 9161 + + configuration: + configMap: + key: "config.toml" + name: "devcm-oradevdb-config" + + prometheus: + serviceMonitor: + labels: + release: prometheus + + log: + filename: "alert.log" + path: "/log" + + volume: + name: log-volume + + replicas: 1 + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + + diff --git a/config/samples/observability/databaseobserver_custom_config.yaml b/config/samples/observability/v4/databaseobserver_minimal.yaml similarity index 57% rename from config/samples/observability/databaseobserver_custom_config.yaml rename to config/samples/observability/v4/databaseobserver_minimal.yaml index 1e9fff47..cc14fbea 100644 --- a/config/samples/observability/databaseobserver_custom_config.yaml +++ b/config/samples/observability/v4/databaseobserver_minimal.yaml @@ -1,9 +1,8 @@ # example -apiVersion: observability.oracle.com/v1alpha1 +apiVersion: observability.oracle.com/v4 kind: DatabaseObserver metadata: name: obs-sample - namespace: observer spec: database: dbUser: @@ -19,10 +18,9 @@ spec: secret: db-secret dbWallet: - secret: instance-wallet + secret: instance-wallets - exporter: - configuration: - configmap: - key: "config.toml" - configmapName: "devcm-oradevdb-config" \ No newline at end of file + prometheus: + serviceMonitor: + labels: + release: prometheus diff --git a/config/samples/observability/v4/databaseobserver_vault.yaml b/config/samples/observability/v4/databaseobserver_vault.yaml new file mode 100644 index 00000000..4f5845f6 --- /dev/null +++ b/config/samples/observability/v4/databaseobserver_vault.yaml @@ -0,0 +1,39 @@ +# example +apiVersion: observability.oracle.com/v4 +kind: DatabaseObserver +metadata: + name: obs-sample + labels: + app.kubernetes.io/name: observability-exporter + app.kubernetes.io/instance: obs-sample + app.kubernetes.io/version: latest +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + vaultSecretName: sample_secret + vaultOCID: ocid1.vault.oc1.. + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + inherit_labels: + - app.kubernetes.io/name + - app.kubernetes.io/instance + - app.kubernetes.io/version + + prometheus: + serviceMonitor: + labels: + release: prometheus + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey \ No newline at end of file diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 12f4ca91..4392ec97 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -271,6 +271,26 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-observability-oracle-com-v1-databaseobserver + failurePolicy: Fail + name: mdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -291,6 +311,27 @@ webhooks: resources: - databaseobservers sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-observability-oracle-com-v4-databaseobserver + failurePolicy: Fail + name: mdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None + --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -627,6 +668,26 @@ webhooks: resources: - singleinstancedatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-observability-oracle-com-v1-databaseobserver + failurePolicy: Fail + name: vdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -647,3 +708,23 @@ webhooks: resources: - databaseobservers sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-observability-oracle-com-v4-databaseobserver + failurePolicy: Fail + name: vdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None diff --git a/controllers/observability/databaseobserver_controller.go b/controllers/observability/databaseobserver_controller.go index bd58e71e..e17ee0b3 100644 --- a/controllers/observability/databaseobserver_controller.go +++ b/controllers/observability/databaseobserver_controller.go @@ -44,6 +44,7 @@ import ( "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" apiError "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -55,7 +56,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "time" - apiv1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + api "github.com/oracle/oracle-database-operator/apis/observability/v4" constants "github.com/oracle/oracle-database-operator/commons/observability" ) @@ -90,8 +91,8 @@ func (r *DatabaseObserverReconciler) Reconcile(ctx context.Context, req ctrl.Req r.Log.WithName(constants.LogReconcile).Info(constants.LogCRStart, "NamespacedName", req.NamespacedName) // fetch databaseObserver - api := &apiv1.DatabaseObserver{} - if e := r.Get(context.TODO(), req.NamespacedName, api); e != nil { + a := &api.DatabaseObserver{} + if e := r.Get(context.TODO(), req.NamespacedName, a); e != nil { // if CR is not found or does not exist then // consider either CR has been deleted @@ -101,7 +102,7 @@ func (r *DatabaseObserverReconciler) Reconcile(ctx context.Context, req ctrl.Req } r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorCRRetrieve) - r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonFailedCRRetrieval, constants.EventMessageFailedCRRetrieval) + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonFailedCRRetrieval, constants.EventMessageFailedCRRetrieval) return ctrl.Result{}, e } @@ -110,19 +111,19 @@ func (r *DatabaseObserverReconciler) Reconcile(ctx context.Context, req ctrl.Req defer r.validateCustomResourceReadiness(ctx, req) // initialize databaseObserver custom resource - if e := r.initialize(ctx, api, req); e != nil { + if e := r.initialize(ctx, a, req); e != nil { return ctrl.Result{}, e } // validate specs - if e := r.validateSpecs(api); e != nil { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + if e := r.validateSpecs(a); e != nil { + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsExporterDeploymentReady, Status: metav1.ConditionFalse, Reason: constants.ReasonDeploymentSpecValidationFailed, Message: constants.MessageExporterDeploymentSpecValidationFailed, }) - if e := r.Status().Update(ctx, api); e != nil { + if e := r.Status().Update(ctx, a); e != nil { r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorStatusUpdate) } r.Log.WithName(constants.LogExportersDeploy).Error(e, constants.ErrorSpecValidationFailedDueToAnError) @@ -131,50 +132,74 @@ func (r *DatabaseObserverReconciler) Reconcile(ctx context.Context, req ctrl.Req // create resource if they do not exist exporterDeployment := &ObservabilityDeploymentResource{} - if res, e := r.createResourceIfNotExists(exporterDeployment, api, ctx, req); e != nil { + if res, e := r.createResourceIfNotExists(exporterDeployment, a, ctx, req); e != nil { return res, e } - if res, e := r.checkDeploymentForUpdates(exporterDeployment, api, ctx, req); e != nil { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + // otherwise, check for updates on resource for any changes + if res, e := r.checkResourceForUpdates(exporterDeployment, a, ctx, req); e != nil { + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsExporterDeploymentReady, Status: metav1.ConditionFalse, - Reason: constants.ReasonDeploymentUpdateFailed, - Message: constants.MessageExporterDeploymentUpdateFailed, + Reason: constants.ReasonResourceUpdateFailed, + Message: constants.MessageExporterResourceUpdateFailed, }) return res, e } exporterService := &ObservabilityServiceResource{} - if res, e := r.createResourceIfNotExists(exporterService, api, ctx, req); e != nil { + if res, e := r.createResourceIfNotExists(exporterService, a, ctx, req); e != nil { + return res, e + } + + // otherwise, check for updates on resource for any changes + if res, e := r.checkResourceForUpdates(exporterService, a, ctx, req); e != nil { + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterServiceReady, + Status: metav1.ConditionFalse, + Reason: constants.ReasonResourceUpdateFailed, + Message: constants.MessageExporterResourceUpdateFailed, + }) return res, e } exporterServiceMonitor := &ObservabilityServiceMonitorResource{} - if res, e := r.createResourceIfNotExists(exporterServiceMonitor, api, ctx, req); e != nil { + if res, e := r.createResourceIfNotExists(exporterServiceMonitor, a, ctx, req); e != nil { + return res, e + } + + // otherwise, check for updates on resource for any changes + if res, e := r.checkResourceForUpdates(exporterServiceMonitor, a, ctx, req); e != nil { + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ + Type: constants.IsExporterServiceMonitorReady, + Status: metav1.ConditionFalse, + Reason: constants.ReasonResourceUpdateFailed, + Message: constants.MessageExporterResourceUpdateFailed, + }) return res, e } // check if deployment pods are ready - return r.validateDeploymentReadiness(api, ctx, req) + return r.validateDeploymentReadiness(a, ctx, req) } // initialize method sets the initial status to PENDING, exporterConfig and sets the base condition -func (r *DatabaseObserverReconciler) initialize(ctx context.Context, api *apiv1.DatabaseObserver, req ctrl.Request) error { +func (r *DatabaseObserverReconciler) initialize(ctx context.Context, a *api.DatabaseObserver, req ctrl.Request) error { - if api.Status.Conditions == nil || len(api.Status.Conditions) == 0 { + if a.Status.Conditions == nil || len(a.Status.Conditions) == 0 { // set condition - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsCRAvailable, Status: metav1.ConditionFalse, Reason: constants.ReasonInitStart, Message: constants.MessageCRInitializationStarted, }) - api.Status.Status = string(constants.StatusObservabilityPending) - api.Status.ExporterConfig = constants.UnknownValue - if e := r.Status().Update(ctx, api); e != nil { + a.Status.Status = string(constants.StatusObservabilityPending) + a.Status.ExporterConfig = constants.UnknownValue + a.Status.Version = constants.UnknownValue + if e := r.Status().Update(ctx, a); e != nil { r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorStatusUpdate) return e } @@ -185,45 +210,45 @@ func (r *DatabaseObserverReconciler) initialize(ctx context.Context, api *apiv1. } // validateSpecs method checks the values and secrets passed in the spec -func (r *DatabaseObserverReconciler) validateSpecs(api *apiv1.DatabaseObserver) error { +func (r *DatabaseObserverReconciler) validateSpecs(a *api.DatabaseObserver) error { // If either Vault Fields are empty, then assume a DBPassword secret is supplied. If the DBPassword secret not found, then error out - if api.Spec.Database.DBPassword.VaultOCID == "" || api.Spec.Database.DBPassword.VaultSecretName == "" { + if a.Spec.Database.DBPassword.VaultOCID == "" || a.Spec.Database.DBPassword.VaultSecretName == "" { dbSecret := &corev1.Secret{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: api.Spec.Database.DBPassword.SecretName, Namespace: api.Namespace}, dbSecret); e != nil { - r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPasswordSecretMissing) + if e := r.Get(context.TODO(), types.NamespacedName{Name: a.Spec.Database.DBPassword.SecretName, Namespace: a.Namespace}, dbSecret); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPasswordSecretMissing) return e } } // Does DB Connection String Secret Name actually exist dbConnectSecret := &corev1.Secret{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: api.Spec.Database.DBConnectionString.SecretName, Namespace: api.Namespace}, dbConnectSecret); e != nil { - r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBConnectionStringSecretMissing) + if e := r.Get(context.TODO(), types.NamespacedName{Name: a.Spec.Database.DBConnectionString.SecretName, Namespace: a.Namespace}, dbConnectSecret); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBConnectionStringSecretMissing) return e } // Does DB User String Secret Name actually exist dbUserSecret := &corev1.Secret{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: api.Spec.Database.DBUser.SecretName, Namespace: api.Namespace}, dbUserSecret); e != nil { - r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPUserSecretMissing) + if e := r.Get(context.TODO(), types.NamespacedName{Name: a.Spec.Database.DBUser.SecretName, Namespace: a.Namespace}, dbUserSecret); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPUserSecretMissing) return e } // Does a custom configuration configmap actually exist, if provided - if configurationCMName := api.Spec.Exporter.ExporterConfig.Configmap.Name; configurationCMName != "" { + if configurationCMName := a.Spec.ExporterConfig.Configmap.Name; configurationCMName != "" { configurationCM := &corev1.ConfigMap{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: configurationCMName, Namespace: api.Namespace}, configurationCM); e != nil { - r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorConfigmapMissing) + if e := r.Get(context.TODO(), types.NamespacedName{Name: configurationCMName, Namespace: a.Namespace}, configurationCM); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorConfigmapMissing) return e } } // Does DBWallet actually exist, if provided - if dbWalletSecretName := api.Spec.Database.DBWallet.SecretName; dbWalletSecretName != "" { + if dbWalletSecretName := a.Spec.Database.DBWallet.SecretName; dbWalletSecretName != "" { dbWalletSecret := &corev1.Secret{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: dbWalletSecretName, Namespace: api.Namespace}, dbWalletSecret); e != nil { - r.Recorder.Event(api, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBWalletSecretMissing) + if e := r.Get(context.TODO(), types.NamespacedName{Name: dbWalletSecretName, Namespace: a.Namespace}, dbWalletSecret); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBWalletSecretMissing) return e } } @@ -232,17 +257,17 @@ func (r *DatabaseObserverReconciler) validateSpecs(api *apiv1.DatabaseObserver) } // createResourceIfNotExists method creates an ObserverResource if they have not yet been created -func (r *DatabaseObserverReconciler) createResourceIfNotExists(or ObserverResource, api *apiv1.DatabaseObserver, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +func (r *DatabaseObserverReconciler) createResourceIfNotExists(or ObserverResource, a *api.DatabaseObserver, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { conditionType, logger, groupVersionKind := or.identify() // update after - defer r.Status().Update(ctx, api) + defer r.Status().Update(ctx, a) - // generate desired object based on api.Spec - desiredObj, genErr := or.generate(api, r.Scheme) + // generate desired object based on a.Spec + desiredObj, genErr := or.generate(a, r.Scheme) if genErr != nil { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: conditionType, Status: metav1.ConditionFalse, Reason: constants.ReasonGeneralResourceGenerationFailed, @@ -260,7 +285,7 @@ func (r *DatabaseObserverReconciler) createResourceIfNotExists(or ObserverResour if getErr != nil && apiError.IsNotFound(getErr) { if e := r.Create(context.TODO(), desiredObj); e != nil { // create - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: conditionType, Status: metav1.ConditionFalse, Reason: constants.ReasonGeneralResourceCreationFailed, @@ -271,7 +296,7 @@ func (r *DatabaseObserverReconciler) createResourceIfNotExists(or ObserverResour } // mark ready if created - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: conditionType, Status: metav1.ConditionTrue, Reason: constants.ReasonGeneralResourceCreated, @@ -280,7 +305,7 @@ func (r *DatabaseObserverReconciler) createResourceIfNotExists(or ObserverResour r.Log.WithName(logger).Info(constants.LogResourceCreated, "ResourceName", desiredObj.GetName(), "Kind", groupVersionKind, "Namespace", req.Namespace) } else if getErr != nil { // if an error occurred - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: conditionType, Status: metav1.ConditionFalse, Reason: constants.ReasonGeneralResourceValidationFailureDueToError, @@ -290,7 +315,7 @@ func (r *DatabaseObserverReconciler) createResourceIfNotExists(or ObserverResour return ctrl.Result{}, getErr } else if getErr == nil && conditionType != constants.IsExporterDeploymentReady { // exclude deployment - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: conditionType, Status: metav1.ConditionTrue, Reason: constants.ReasonGeneralResourceValidationCompleted, @@ -304,138 +329,68 @@ func (r *DatabaseObserverReconciler) createResourceIfNotExists(or ObserverResour return ctrl.Result{}, nil } -// checkDeploymentForUpdates method checks the deployment if it needs to be updated -func (r *DatabaseObserverReconciler) checkDeploymentForUpdates(or ObserverResource, api *apiv1.DatabaseObserver, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +// checkResourceForUpdates method checks the resource if it needs to be updated, updates if changes are found +func (r *DatabaseObserverReconciler) checkResourceForUpdates(or ObserverResource, a *api.DatabaseObserver, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - // declare - foundDeployment := &appsv1.Deployment{} + conditionType, logName, groupVersionKind := or.identify() - // generate object - desiredObj, genErr := or.generate(api, r.Scheme) + // generate desired object + dO, genErr := or.generate(a, r.Scheme) if genErr != nil { return ctrl.Result{}, genErr } - // convert - desiredDeployment := &appsv1.Deployment{} - if e := r.Scheme.Convert(desiredObj, desiredDeployment, nil); e != nil { - return ctrl.Result{}, e - } - - // retrieve latest deployment - if e := r.Get(context.TODO(), types.NamespacedName{Name: desiredObj.GetName(), Namespace: req.Namespace}, foundDeployment); e != nil { - return ctrl.Result{}, e - } - // check for containerImage - if constants.IsUpdateRequiredForContainerImage(desiredDeployment, foundDeployment) { - foundDeployment.Spec.Template.Spec.Containers[0].Image = constants.GetExporterImage(api) - - if e := r.updateDeployment(api, ctx, req, foundDeployment, constants.MessageExporterDeploymentImageUpdated, constants.EventMessageUpdatedImageSucceeded); e != nil { - return ctrl.Result{}, e - } - } - - // retrieve latest deployment - if e := r.Get(context.TODO(), types.NamespacedName{Name: desiredObj.GetName(), Namespace: req.Namespace}, foundDeployment); e != nil { + // convert dO -> d + d := &unstructured.Unstructured{} + d.SetGroupVersionKind(groupVersionKind) + if e := r.Scheme.Convert(dO, d, nil); e != nil { return ctrl.Result{}, e } - // check environment variables - if constants.IsUpdateRequiredForEnvironmentVars(desiredDeployment, foundDeployment) { - foundDeployment.Spec.Template.Spec.Containers[0].Env = constants.GetExporterEnvs(api) - - if e := r.updateDeployment(api, ctx, req, foundDeployment, constants.MessageExporterDeploymentEnvironmentUpdated, constants.EventMessageUpdatedEnvironmentSucceeded); e != nil { - return ctrl.Result{}, e - } - } - // retrieve latest deployment - foundDeployment = &appsv1.Deployment{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: desiredObj.GetName(), Namespace: req.Namespace}, foundDeployment); e != nil { + // declare found + // retrieve latest into f + f := &unstructured.Unstructured{} + f.SetGroupVersionKind(groupVersionKind) + if e := r.Get(context.TODO(), types.NamespacedName{Name: dO.GetName(), Namespace: req.Namespace}, f); e != nil { return ctrl.Result{}, e } - // check config-volume, creds and ocikey - if constants.IsUpdateRequiredForVolumes(desiredDeployment, foundDeployment) { - foundDeployment.Spec.Template.Spec.Volumes = constants.GetExporterDeploymentVolumes(api) - foundDeployment.Spec.Template.Spec.Containers[0].VolumeMounts = constants.GetExporterDeploymentVolumeMounts(api) - if e := r.updateDeployment(api, ctx, req, foundDeployment, constants.MessageExporterDeploymentVolumesUpdated, constants.EventMessageUpdatedVolumesSucceeded); e != nil { - return ctrl.Result{}, e - } - } - - // update status for exporter config - var setConfigmapNameStatus string - for _, v := range desiredDeployment.Spec.Template.Spec.Volumes { - if v.Name == constants.DefaultConfigVolumeString { - setConfigmapNameStatus = v.ConfigMap.Name - api.Status.ExporterConfig = setConfigmapNameStatus - } - } - if api.Status.ExporterConfig != setConfigmapNameStatus { - api.Status.ExporterConfig = constants.DefaultValue - } - r.Status().Update(ctx, api) - - // retrieve latest deployment - foundDeployment = &appsv1.Deployment{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: desiredObj.GetName(), Namespace: req.Namespace}, foundDeployment); e != nil { - return ctrl.Result{}, e - } - // check replicateCount - if constants.IsUpdateRequiredForReplicas(desiredDeployment, foundDeployment) { - desiredReplicaCount := constants.GetExporterReplicas(api) - foundDeployment.Spec.Replicas = &desiredReplicaCount + // check if something changed + if !equality.Semantic.DeepDerivative(d.Object, f.Object) { - if e := r.updateDeployment(api, ctx, req, foundDeployment, constants.MessageExporterDeploymentReplicaUpdated, constants.EventMessageUpdatedReplicaSucceeded); e != nil { + if e := r.Update(context.TODO(), d); e != nil { + r.Log.WithName(logName).Error(e, constants.LogErrorWithResourceUpdate, "ResourceName", f.GetName(), "Kind", groupVersionKind.Kind, "Namespace", req.Namespace) return ctrl.Result{}, e } - } - - return ctrl.Result{}, nil -} - -// updateDeployment method updates the deployment and sets the condition -func (r *DatabaseObserverReconciler) updateDeployment(api *apiv1.DatabaseObserver, ctx context.Context, req ctrl.Request, d *appsv1.Deployment, updateMessage string, recorderMessage string) error { - // make update - defer r.Status().Update(ctx, api) - - if e := r.Update(context.TODO(), d); e != nil { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ - Type: constants.IsExporterDeploymentReady, + // update completed, however the pods needs to be validated for readiness + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ + Type: conditionType, Status: metav1.ConditionFalse, - Reason: constants.ReasonDeploymentUpdateFailed, - Message: constants.MessageExporterDeploymentUpdateFailed, + Reason: constants.ReasonResourceUpdated, + Message: constants.MessageExporterResourceUpdated, }) - r.Log.WithName(constants.LogExportersDeploy).Error(e, constants.ErrorDeploymentUpdate, "ResourceName", d.GetName(), "Kind", "Deployment", "Namespace", req.Namespace) - return e + r.Log.WithName(logName).Info(constants.LogSuccessWithResourceUpdate, "ResourceName", f.GetName(), "Kind", groupVersionKind.Kind, "Namespace", req.Namespace) + r.Recorder.Event(a, corev1.EventTypeNormal, constants.EventReasonUpdateSucceeded, groupVersionKind.Kind+" is updated.") + r.Status().Update(ctx, a) } - // update completed, however the pods needs to be validated for readiness - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ - Type: constants.IsExporterDeploymentReady, - Status: metav1.ConditionFalse, - Reason: constants.ReasonDeploymentUpdated, - Message: updateMessage, - }) - r.Log.WithName(constants.LogExportersDeploy).Info(constants.LogResourceUpdated, "ResourceName", d.GetName(), "Kind", "Deployment", "Namespace", req.Namespace) - r.Recorder.Event(api, corev1.EventTypeNormal, constants.EventReasonUpdateSucceeded, recorderMessage) + return ctrl.Result{}, nil - return nil } // validateDeploymentReadiness method evaluates deployment readiness by checking the status of all deployment pods -func (r *DatabaseObserverReconciler) validateDeploymentReadiness(api *apiv1.DatabaseObserver, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +func (r *DatabaseObserverReconciler) validateDeploymentReadiness(a *api.DatabaseObserver, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { d := &appsv1.Deployment{} - rName := constants.DefaultExporterDeploymentPrefix + api.Name + rName := a.Name // update after - defer r.Status().Update(ctx, api) + defer r.Status().Update(ctx, a) // get latest deployment - if e := r.Get(context.TODO(), types.NamespacedName{Name: rName, Namespace: api.Namespace}, d); e != nil { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + if e := r.Get(context.TODO(), types.NamespacedName{Name: rName, Namespace: a.Namespace}, d); e != nil { + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsExporterDeploymentReady, Status: metav1.ConditionFalse, Reason: constants.ReasonGeneralResourceValidationFailureDueToError, @@ -445,16 +400,14 @@ func (r *DatabaseObserverReconciler) validateDeploymentReadiness(api *apiv1.Data } // get deployment labels - labels := d.Spec.Template.Labels - cLabels := client.MatchingLabels{} - for k, v := range labels { - cLabels[k] = v + cLabels := client.MatchingLabels{ + "app": a.Name, } // list pods pods := &corev1.PodList{} if e := r.List(context.TODO(), pods, []client.ListOption{cLabels}...); e != nil { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsExporterDeploymentReady, Status: metav1.ConditionFalse, Reason: constants.ReasonDeploymentFailed, @@ -466,7 +419,7 @@ func (r *DatabaseObserverReconciler) validateDeploymentReadiness(api *apiv1.Data // check each pod phase for _, pod := range pods.Items { if pod.Status.Phase == corev1.PodFailed { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsExporterDeploymentReady, Status: metav1.ConditionFalse, Reason: constants.ReasonDeploymentFailed, @@ -475,7 +428,7 @@ func (r *DatabaseObserverReconciler) validateDeploymentReadiness(api *apiv1.Data return ctrl.Result{}, errors.New(constants.ErrorDeploymentPodsFailure) } else if pod.Status.Phase != corev1.PodRunning { // pod could be creating, - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsExporterDeploymentReady, Status: metav1.ConditionUnknown, Reason: constants.ReasonDeploymentPending, @@ -486,12 +439,14 @@ func (r *DatabaseObserverReconciler) validateDeploymentReadiness(api *apiv1.Data } // once all pods are found to be running, mark deployment as ready and the exporter as ready - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsExporterDeploymentReady, Status: metav1.ConditionTrue, Reason: constants.ReasonDeploymentSuccessful, Message: constants.MessageExporterDeploymentSuccessful, }) + a.Status.Version = constants.GetExporterVersion(a) + a.Status.ExporterConfig = constants.GetExporterConfig(a) return ctrl.Result{}, nil } @@ -499,48 +454,48 @@ func (r *DatabaseObserverReconciler) validateDeploymentReadiness(api *apiv1.Data func (r *DatabaseObserverReconciler) validateCustomResourceReadiness(ctx context.Context, req ctrl.Request) { // get latest object - api := &apiv1.DatabaseObserver{} - if e := r.Get(context.TODO(), req.NamespacedName, api); e != nil { + a := &api.DatabaseObserver{} + if e := r.Get(context.TODO(), req.NamespacedName, a); e != nil { r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorCRRetrieve) return } // make update - defer r.Status().Update(ctx, api) + defer r.Status().Update(ctx, a) - if meta.IsStatusConditionPresentAndEqual(api.Status.Conditions, constants.IsExporterDeploymentReady, metav1.ConditionUnknown) { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + if meta.IsStatusConditionPresentAndEqual(a.Status.Conditions, constants.IsExporterDeploymentReady, metav1.ConditionUnknown) { + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsCRAvailable, Status: metav1.ConditionFalse, Reason: constants.ReasonValidationInProgress, Message: constants.MessageCRValidationWaiting, }) - api.Status.Status = string(constants.StatusObservabilityPending) - } else if meta.IsStatusConditionFalse(api.Status.Conditions, constants.IsExporterDeploymentReady) || - meta.IsStatusConditionFalse(api.Status.Conditions, constants.IsExporterServiceReady) || - meta.IsStatusConditionFalse(api.Status.Conditions, constants.IsExporterServiceMonitorReady) { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + a.Status.Status = string(constants.StatusObservabilityPending) + } else if meta.IsStatusConditionFalse(a.Status.Conditions, constants.IsExporterDeploymentReady) || + meta.IsStatusConditionFalse(a.Status.Conditions, constants.IsExporterServiceReady) || + meta.IsStatusConditionFalse(a.Status.Conditions, constants.IsExporterServiceMonitorReady) { + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsCRAvailable, Status: metav1.ConditionFalse, Reason: constants.ReasonReadyFailed, Message: constants.MessageCRValidationFailed, }) - api.Status.Status = string(constants.StatusObservabilityError) + a.Status.Status = string(constants.StatusObservabilityError) } else { - meta.SetStatusCondition(&api.Status.Conditions, metav1.Condition{ + meta.SetStatusCondition(&a.Status.Conditions, metav1.Condition{ Type: constants.IsCRAvailable, Status: metav1.ConditionTrue, Reason: constants.ReasonReadyValidated, Message: constants.MessageCRValidated, }) - api.Status.Status = string(constants.StatusObservabilityReady) + a.Status.Status = string(constants.StatusObservabilityReady) } } // SetupWithManager sets up the controller with the Manager. func (r *DatabaseObserverReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&apiv1.DatabaseObserver{}). + For(&api.DatabaseObserver{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Complete(r) diff --git a/controllers/observability/databaseobserver_resource.go b/controllers/observability/databaseobserver_resource.go index 8c20ebe5..b58cbf57 100644 --- a/controllers/observability/databaseobserver_resource.go +++ b/controllers/observability/databaseobserver_resource.go @@ -1,7 +1,7 @@ package controllers import ( - apiv1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + api "github.com/oracle/oracle-database-operator/apis/observability/v4" constants "github.com/oracle/oracle-database-operator/commons/observability" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" @@ -10,7 +10,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -25,47 +24,64 @@ type ObservabilityServiceResource struct{} type ObservabilityServiceMonitorResource struct{} type ObserverResource interface { - generate(*apiv1.DatabaseObserver, *runtime.Scheme) (*unstructured.Unstructured, error) + generate(*api.DatabaseObserver, *runtime.Scheme) (*unstructured.Unstructured, error) identify() (string, string, schema.GroupVersionKind) } -func (resource *ObservabilityDeploymentResource) generate(api *apiv1.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { - rName := constants.DefaultExporterDeploymentPrefix + api.Name +func (resource *ObservabilityDeploymentResource) generate(a *api.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { + rName := a.Name rContainerName := constants.DefaultExporterContainerName - rContainerImage := constants.GetExporterImage(api) - rVolumes := constants.GetExporterDeploymentVolumes(api) - rVolumeMounts := constants.GetExporterDeploymentVolumeMounts(api) - rSelectors := constants.GetExporterSelector(api) - rReplicas := constants.GetExporterReplicas(api) - rEnvs := constants.GetExporterEnvs(api) + rContainerImage := constants.GetExporterImage(a) + rArgs := constants.GetExporterArgs(a) + rCommands := constants.GetExporterCommands(a) + rVolumes := constants.GetExporterDeploymentVolumes(a) + rVolumeMounts := constants.GetExporterDeploymentVolumeMounts(a) + + rReplicas := constants.GetExporterReplicas(a) + rEnvs := constants.GetExporterEnvs(a) + + rLabels := constants.GetLabels(a, a.Spec.Exporter.Deployment.Labels) + rPodLabels := constants.GetLabels(a, a.Spec.Exporter.Deployment.DeploymentPodTemplate.Labels) + rSelector := constants.GetSelectorLabel(a) rPort := []corev1.ContainerPort{ - {ContainerPort: 8080}, + {ContainerPort: constants.DefaultAppPort}, + } + + // exporterContainer + rContainers := make([]corev1.Container, 1) + rContainers[0] = corev1.Container{ + Image: rContainerImage, + ImagePullPolicy: corev1.PullAlways, + Name: rContainerName, + Env: rEnvs, + VolumeMounts: rVolumeMounts, + Ports: rPort, + Args: rArgs, + Command: rCommands, } + constants.AddSidecarContainers(a, &rContainers) + constants.AddSidecarVolumes(a, &rVolumes) + + // additionalContainers obj := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: rName, - Namespace: api.Namespace, + Namespace: a.Namespace, + Labels: rLabels, }, Spec: appsv1.DeploymentSpec{ Replicas: &rReplicas, Selector: &metav1.LabelSelector{ - MatchLabels: rSelectors, + MatchLabels: rSelector, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: rSelectors, + Labels: rPodLabels, }, Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Image: rContainerImage, - ImagePullPolicy: corev1.PullAlways, - Name: rContainerName, - Env: rEnvs, - VolumeMounts: rVolumeMounts, - Ports: rPort, - }}, + Containers: rContainers, RestartPolicy: corev1.RestartPolicyAlways, Volumes: rVolumes, }, @@ -73,7 +89,7 @@ func (resource *ObservabilityDeploymentResource) generate(api *apiv1.DatabaseObs }, } - if err := controllerutil.SetControllerReference(api, obj, scheme); err != nil { + if err := controllerutil.SetControllerReference(a, obj, scheme); err != nil { return nil, err } @@ -84,32 +100,26 @@ func (resource *ObservabilityDeploymentResource) generate(api *apiv1.DatabaseObs return u, nil } -func (resource *ObservabilityServiceResource) generate(api *apiv1.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { - rServiceName := "obs-svc-" + api.Name - rLabels := constants.GetExporterLabels(api) - rPort := constants.GetExporterServicePort(api) - rSelector := constants.GetExporterSelector(api) +func (resource *ObservabilityServiceResource) generate(a *api.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { + rServiceName := a.Name + rLabels := constants.GetLabels(a, a.Spec.Exporter.Service.Labels) + rSelector := constants.GetSelectorLabel(a) + rPorts := constants.GetExporterServicePort(a) obj := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: rServiceName, Labels: rLabels, - Namespace: api.Namespace, + Namespace: a.Namespace, }, Spec: corev1.ServiceSpec{ - Type: "ClusterIP", + Type: constants.DefaultServiceType, Selector: rSelector, - Ports: []corev1.ServicePort{ - { - Name: "metrics", - Port: rPort, - TargetPort: intstr.FromInt32(constants.DefaultServiceTargetPort), - }, - }, + Ports: rPorts, }, } - if err := controllerutil.SetControllerReference(api, obj, scheme); err != nil { + if err := controllerutil.SetControllerReference(a, obj, scheme); err != nil { return nil, err } @@ -120,32 +130,32 @@ func (resource *ObservabilityServiceResource) generate(api *apiv1.DatabaseObserv return u, nil } -func (resource *ObservabilityServiceMonitorResource) generate(api *apiv1.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { - rName := constants.DefaultServiceMonitorPrefix + api.Name - rLabels := constants.GetExporterLabels(api) - rSelector := constants.GetExporterSelector(api) - rPort := constants.GetExporterServiceMonitorPort(api) - rInterval := "20s" +func (resource *ObservabilityServiceMonitorResource) generate(a *api.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { + rName := a.Name + rEndpoints := constants.GetEndpoints(a) + + rSelector := constants.GetSelectorLabel(a) + rLabels := constants.GetLabels(a, a.Spec.Prometheus.ServiceMonitor.Labels) + + smSpec := monitorv1.ServiceMonitorSpec{ + Endpoints: rEndpoints, + Selector: metav1.LabelSelector{ + MatchLabels: rSelector, + }, + } + constants.AddNamespaceSelector(a, &smSpec) obj := &monitorv1.ServiceMonitor{ ObjectMeta: metav1.ObjectMeta{ Name: rName, Labels: rLabels, - Namespace: api.Namespace, - }, - Spec: monitorv1.ServiceMonitorSpec{ - Endpoints: []monitorv1.Endpoint{{ - Interval: monitorv1.Duration(rInterval), - Port: rPort, - }}, - Selector: metav1.LabelSelector{ - MatchLabels: rSelector, - }, + Namespace: a.Namespace, }, + Spec: smSpec, } // set reference - if e := controllerutil.SetControllerReference(api, obj, scheme); e != nil { + if e := controllerutil.SetControllerReference(a, obj, scheme); e != nil { return nil, e } diff --git a/docs/multitenant/usecase01/oracle-database-operator.yaml b/docs/multitenant/usecase01/oracle-database-operator.yaml new file mode 100644 index 00000000..1838ad9f --- /dev/null +++ b/docs/multitenant/usecase01/oracle-database-operator.yaml @@ -0,0 +1,4052 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomouscontainerdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousContainerDatabase + listKind: AutonomousContainerDatabaseList + plural: autonomouscontainerdatabases + shortNames: + - acd + - acds + singular: autonomouscontainerdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + description: 'AutonomousContainerDatabasePatchModelEnum Enum with underlying type: string' + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + description: AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase + properties: + lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomousdatabasebackups.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseBackup + listKind: AutonomousDatabaseBackupList + plural: autonomousdatabasebackups + shortNames: + - adbbu + - adbbus + singular: autonomousdatabasebackup + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AutonomousDatabaseBackupSpec defines the desired state of AutonomousDatabaseBackup + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' + properties: + k8sADB: + description: "*********************** *\tADB spec ***********************" + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + description: AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with underlying type: string' + type: string + timeEnded: + type: string + timeStarted: + type: string + type: + description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying type: string' + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomousdatabaserestores.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabaseRestore + listKind: AutonomousDatabaseRestoreList + plural: autonomousdatabaserestores + shortNames: + - adbr + - adbrs + singular: autonomousdatabaserestore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore + properties: + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.' + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + description: 'The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT' + type: string + type: object + type: object + target: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' + properties: + k8sADB: + description: "*********************** *\tADB spec ***********************" + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + description: AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore + properties: + dbName: + type: string + displayName: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + status: + description: 'WorkRequestStatusEnum Enum with underlying type: string' + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: autonomousdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: AutonomousDatabase + listKind: AutonomousDatabaseList + plural: autonomousdatabases + shortNames: + - adb + - adbs + singular: autonomousdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: AutonomousDatabase is the Schema for the autonomousdatabases API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase Important: Run "make" to regenerate code after modifying this file' + properties: + details: + description: AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase + properties: + adminPassword: + properties: + k8sSecret: + description: "*********************** *\tSecret specs ***********************" + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + autonomousContainerDatabase: + description: ACDSpec defines the spec of the target for backup/restore runs. The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup + properties: + k8sACD: + description: "*********************** *\tACD specs ***********************" + properties: + name: + type: string + type: object + ociACD: + properties: + ocid: + type: string + type: object + type: object + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying type: string' + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + licenseModel: + description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying type: string' + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + lifecycleState: + description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' + type: string + networkAccess: + properties: + accessControlList: + items: + type: string + type: array + accessType: + enum: + - "" + - PUBLIC + - RESTRICTED + - PRIVATE + type: string + isAccessControlEnabled: + type: boolean + isMTLSConnectionRequired: + type: boolean + privateEndpoint: + properties: + hostnamePrefix: + type: string + nsgOCIDs: + items: + type: string + type: array + subnetOCID: + type: string + type: object + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + description: "*********************** *\tSecret specs ***********************" + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object + type: object + type: object + type: object + hardLink: + default: false + type: boolean + ociConfig: + description: "*********************** *\tOCI config ***********************" + properties: + configMapName: + type: string + secretName: + type: string + type: object + required: + - details + type: object + status: + description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.6.1 + name: cdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: CDB + listKind: CDBList + plural: cdbs + singular: cdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: CDB is the Schema for the cdbs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: CDBSpec defines the desired state of CDB + properties: + cdbAdminPwd: + description: Password for the CDB Administrator to manage PDB lifecycle + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + description: User in the root container with sysdba priviledges to manage PDB lifecycle + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + description: Name of the CDB + type: string + cdbTlsCrt: + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + description: DB server port + type: integer + dbServer: + description: Name of the DB server + type: string + dbTnsurl: + type: string + nodeSelector: + additionalProperties: + type: string + description: Node Selector for running the Pod + type: object + ordsImage: + description: ORDS Image Name + type: string + ordsImagePullPolicy: + description: ORDS Image Pull Policy + enum: + - Always + - Never + type: string + ordsImagePullSecret: + description: The name of the image pull secret in case of a private docker repository. + type: string + ordsPort: + description: ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. + type: integer + ordsPwd: + description: Password for user ORDS_PUBLIC_USER + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + description: Number of ORDS Containers to create + type: integer + serviceName: + description: Name of the CDB Service + type: string + sysAdminPwd: + description: Password for the CDB System Administrator + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + description: Password for the Web Server User + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: CDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + description: CDBStatus defines the observed state of CDB + properties: + msg: + description: Message + type: string + phase: + description: Phase of the CDB Resource + type: string + status: + description: CDB Resource Status + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: DatabaseObserver is the Schema for the databaseobservers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DatabaseObserverSpec defines the desired state of DatabaseObserver + properties: + database: + description: DatabaseObserverDatabase defines the database details used for DatabaseObserver + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver + properties: + configuration: + properties: + configmap: + description: ConfigMapDetails defines the configmap name + properties: + configmapName: + type: string + key: + type: string + type: object + type: object + image: + type: string + service: + description: DatabaseObserverService defines the exporter service component of DatabaseObserver + properties: + port: + format: int32 + type: integer + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + description: PrometheusConfig defines the generated resources for Prometheus + properties: + labels: + additionalProperties: + type: string + type: object + port: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + status: + description: DatabaseObserverStatus defines the observed state of DatabaseObserver + properties: + conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + required: + - conditions + - exporterConfig + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: dataguardbrokers.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + singular: dataguardbroker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: DataguardBroker is the Schema for the dataguardbrokers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DataguardBrokerSpec defines the desired state of DataguardBroker + properties: + fastStartFailOver: + properties: + enable: + type: boolean + strategy: + items: + description: FSFO strategy + properties: + sourceDatabaseRef: + type: string + targetDatabaseRefs: + type: string + type: object + type: array + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + description: DataguardBrokerStatus defines the observed state of DataguardBroker + properties: + clusterConnectString: + type: string + externalConnectString: + type: string + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: dbcssystems.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: dbcssystems + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: DbcsSystem is the Schema for the dbcssystems API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DbcsSystemSpec defines the desired state of DbcsSystem + properties: + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + description: DB Backup COnfig Network Struct + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - sshPublicKeys + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + ociConfigMap: + type: string + ociSecret: + type: string + required: + - ociConfigMap + type: object + status: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbEdition: + type: string + dbInfo: + items: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: oraclerestdataservices.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestDataService + listKind: OracleRestDataServiceList + plural: oraclerestdataservices + singular: oraclerestdataservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: OracleRestDataService is the Schema for the oraclerestdataservices API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService + properties: + adminPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + apexPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + description: OracleRestDataServicePersistence defines the storage releated params + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + description: OracleRestDataServicePDBSchemas defines the PDB Schemas to be ORDS Enabled + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + description: OracleRestDataServiceStatus defines the observed state of OracleRestDataService + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.6.1 + name: pdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB + listKind: PDBList + plural: pdbs + singular: pdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: PDB is the Schema for the pdbs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PDBSpec defines the desired state of PDB + properties: + action: + description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR.' + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + description: The administrator username for the new PDB. This property is required when the Action property is Create. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + description: The administrator password for the new PDB. This property is required when the Action property is Create. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + description: Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. + type: boolean + cdbName: + description: Name of the CDB + type: string + cdbNamespace: + description: CDB Namespace + type: string + cdbResName: + description: Name of the CDB Custom Resource that runs the ORDS container + type: string + copyAction: + description: To copy files or not while cloning a PDB + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + description: Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + description: Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. + type: string + getScript: + description: Whether you need the script only or execute the script + type: boolean + modifyOption: + description: Extra options for opening and closing a PDB + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + description: The name of the new PDB. Relevant for both Create and Plug Actions. + type: string + pdbState: + description: The target state of the PDB + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + description: Whether to reuse temp file + type: boolean + sourceFileNameConversions: + description: This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. + type: string + sparseClonePath: + description: A Path specified for sparse clone snapshot copy. (Optional) + type: string + srcPdbName: + description: Name of the Source PDB from which to clone + type: string + tdeExport: + description: TDE export for unplug operations + type: boolean + tdeImport: + description: TDE import for plug operations + type: boolean + tdeKeystorePath: + description: TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + type: string + tdePassword: + description: TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + description: TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + description: Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. + type: string + totalSize: + description: Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. + type: string + unlimitedStorage: + description: Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. + type: boolean + webServerPwd: + description: Password for the Web ServerPDB User + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + properties: + secret: + description: PDBSecret defines the secretName + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + description: XML metadata filename to be used for Plug or Unplug operations + type: string + required: + - action + type: object + status: + description: PDBStatus defines the observed state of PDB + properties: + action: + description: Last Completed Action + type: string + connString: + description: PDB Connect String + type: string + modifyOption: + description: Modify Option of the PDB + type: string + msg: + description: Message + type: string + openMode: + description: Open mode of the PDB + type: string + phase: + description: Phase of the PDB Resource + type: string + status: + description: PDB Resource Status + type: boolean + totalSize: + description: Total size of the PDB + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: shardingdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: ShardingDatabase + listKind: ShardingDatabaseList + plural: shardingdatabases + singular: shardingdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ShardingDatabase is the Schema for the shardingdatabases API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ShardingDatabaseSpec defines the desired state of ShardingDatabase + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + description: CatalogSpec defines the desired state of CatalogSpec + properties: + envVars: + items: + description: EnvironmentVariable represents a named variable accessible for containers. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + description: Secret Details + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + gsm: + items: + description: GsmSpec defines the desired state of GsmSpec + properties: + directorName: + type: string + envVars: + description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. + items: + description: EnvironmentVariable represents a named variable accessible for containers. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + description: Service Definition + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + description: ShardSpace Specs + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: boolean + liveinessCheckPeriod: + type: integer + namespace: + type: string + portMappings: + items: + description: PortMapping is a specification of port mapping for an application deployment. + properties: + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' + items: + description: ShardSpec is a specification of Shards for an application deployment. + properties: + deployAs: + type: string + envVars: + items: + description: EnvironmentVariable represents a named variable accessible for containers. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull a container image + type: string + isDelete: + type: boolean + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 ShardingDatabaseStatus defines the observed state of ShardingDatabase + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.6.1 + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase + properties: + adminPassword: + description: SingleInsatnceAdminPassword defines the secret containing Admin Password mapped to secretKey for Database + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + createAs: + enum: + - primary + - standby + - clone + type: string + dgBrokerConfigured: + type: boolean + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + description: SingleInstanceDatabasePersistence defines the storage size and class for PVC + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + description: SID must be alphanumeric (no special characters, only a-z, A-Z, 0-9), and no longer than 12 characters. + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + required: + - image + type: object + status: + description: SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBrokerConfigured: + type: boolean + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + description: SingleInstanceDatabaseInitParams defines the Init Parameters + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + description: SingleInstanceDatabasePersistence defines the storage size and class for PVC + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: oracle-database-operator-leader-election-role + namespace: oracle-database-operator-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: oracle-database-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - deployments + - events + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - events + - pods + - pods/exec + - pods/log + - replicasets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list +- apiGroups: + - '''''' + resources: + - statefulsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - configmaps + - deployments + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + - containers + - events + - namespaces + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - namespaces + - pods + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabases/status + verbs: + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - cdbs/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - cdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - pdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - shardingdatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - singleinstancedatabases/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - prometheusrules + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/finalizers + verbs: + - update +- apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get + - patch + - update +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-oracle-database-operator-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: oracle-database-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-oracle-database-operator-proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager-metrics-service + namespace: oracle-database-operator-system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: v1 +kind: Service +metadata: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: oracle-database-operator-serving-cert + namespace: oracle-database-operator-system +spec: + dnsNames: + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc + - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local + issuerRef: + kind: Issuer + name: oracle-database-operator-selfsigned-issuer + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: oracle-database-operator-selfsigned-issuer + namespace: oracle-database-operator-system +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: mautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: mautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-cdb + failurePolicy: Fail + name: mcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: mdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: moraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-pdb + failurePolicy: Fail + name: mpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: msingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - singleinstancedatabases + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + name: oracle-database-operator-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + failurePolicy: Fail + name: vautonomouscontainerdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomouscontainerdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup + failurePolicy: Fail + name: vautonomousdatabasebackup.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore + failurePolicy: Fail + name: vautonomousdatabaserestore.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabaserestores + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-cdb + failurePolicy: Fail + name: vcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-dataguardbroker + failurePolicy: Fail + name: vdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + failurePolicy: Fail + name: voraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-pdb + failurePolicy: Fail + name: vpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase + failurePolicy: Fail + name: vsingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - singleinstancedatabases + sideEffects: None +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager + namespace: oracle-database-operator-system +spec: + replicas: 3 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --enable-leader-election + command: + - /manager + env: + - name: WATCH_NAMESPACE + value: "" + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator + imagePullPolicy: Always + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + resources: + limits: + cpu: 400m + memory: 400Mi + requests: + cpu: 400m + memory: 400Mi + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert +--- diff --git a/docs/observability/README.md b/docs/observability/README.md index 986b1885..e5970b0d 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -2,21 +2,33 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Observability controller for Oracle Databases and adds the `DatabaseObserver` CRD, which enables users to observe -Oracle Databases by scraping database metrics using SQL queries. The controller +Oracle Databases by scraping database metrics using SQL queries and observe logs in the Database _alert.log_. The controller automates the deployment and maintenance of the metrics exporter container image, -metrics exporter service and a Prometheus servicemonitor. +metrics exporter service and Prometheus servicemonitor. The following sections explains the configuration and functionality of the controller. * [Prerequisites](#prerequisites) * [The DatabaseObserver Custom Resource Definition](#the-databaseobserver-custom-resource) -* [Configuration of DatabaseObservers](#configuration) + * [Configuration Options](#configuration-options) + * [Resources Managed by the Controller](#resources-managed-by-the-controller) +* [DatabaseObserver Operations](#databaseobserver-operations) * [Create](#create-resource) * [List](#list-resource) * [Get Status](#get-detailed-status) * [Update](#patch-resource) * [Delete](#delete-resource) +* [Configuration Options for Scraping Metrics](#scraping-metrics) + * [Custom Metrics Config](#custom-metrics-config) + * [Prometheus Release](#prometheus-release) +* [Configuration Options for Scraping Logs](#scraping-logs) + * [Custom Log Location with PersistentVolumes](#custom-log-location-with-persistentvolumes) + * [Example Working with Sidecars and Promtail](#working-with-sidecars-to-deploy-promtail) + * [Promtail Config Example](#Promtail-Config-Example) +* [Other Configuration Options](#other-configuration-options) + * [Labels](#labels) + * [Custom Exporter Image or Version](#custom-exporter-image-or-version) * [Mandatory Roles and Privileges](#mandatory-roles-and-privileges-requirements-for-observability-controller) * [Debugging and troubleshooting](#debugging-and-troubleshooting) @@ -35,47 +47,60 @@ The `DatabaseObserver` custom resource has the following pre-requisites: and specify through a _toml_ file. The necessary access privileges to the tables used in the queries are not provided and applied automatically. -### The DatabaseObserver Custom Resource -The Oracle Database Operator (__v1.1.0__) includes the Oracle Database Observability controller which automates -the deployment and setting up of the Oracle Database metrics exporter and the related resources to make Oracle databases observable. +## The DatabaseObserver Custom Resource +The Oracle Database Operator (__v1.2.0__ or later) includes the Oracle Database Observability controller which automates +the deployment and setting up of the Oracle Database exporter and the related resources to make Oracle databases observable. In the sample YAML file found in -[./config/samples/observability/databaseobserver.yaml](../../config/samples/observability/databaseobserver.yaml), +[./config/samples/observability/v4/databaseobserver.yaml](../../config/samples/observability/v4/databaseobserver.yaml), the databaseObserver custom resource offers the following properties to be configured: -| Attribute | Type | Default | Required? | Example | -|-------------------------------------------------------|---------|-----------------|--------------|-----------------------------------------------------------------------| -| `spec.database.dbUser.key` | string | user | Optional | _username_ | -| `spec.database.dbUser.secret` | string | - | Yes | _db-secret_ | -| `spec.database.dbPassword.key` | string | password | Optional | _admin-password_ | -| `spec.database.dbPassword.secret` | string | - | Conditional | _db-secret_ | -| `spec.database.dbPassword.vaultOCID` | string | - | Conditional | _ocid1.vault.oc1..._ | -| `spec.database.dbPassword.vaultSecretName` | string | - | Conditional | _db-vault_ | -| `spec.database.dbWallet.secret` | string | - | Conditional | _devsec-oradevdb-wallet_ | -| `spec.database.dbConnectionString.key` | string | connection | Optional | _connection_ | -| `spec.database.dbConnectionString.secret` | string | - | Yes | _db-secretg_ | -| `spec.exporter.image` | string | - | Optional | _container-registry.oracle.com/database/observability-exporter:1.0.2_ | -| `spec.exporter.configuration.configmap.key` | string | config.toml | Optional | _config.toml_ | -| `spec.exporter.configuration.configmap.configmapName` | string | - | Optional | _devcm-oradevdb-config_ | -| `spec.exporter.service.port` | number | 9161 | Optional | _9161_ | -| `spec.prometheus.port` | string | metrics | Optional | _metrics_ | -| `spec.prometheus.labels` | map | app: obs-{name} | Optional | _app: oradevdb-apps_ | -| `spec.replicas` | number | 1 | Optional | _1_ | -| `spec.ociConfig.configMapName` | string | - | Conditional | _oci-cred_ | -| `spec.ociConfig.secretName` | string | - | Conditional | _oci-privatekey_ | - - - - - -### Configuration +| Attribute | Type | Default | Required? | Example | +|----------------------------------------------------|--------|---------------------------------------------------------------------|:------------|-----------------------------------------------------------------------| +| `spec.database.dbRole` | string | - | Optional | _SYSDBA_ | +| `spec.database.dbUser.key` | string | user | Optional | _username_ | +| `spec.database.dbUser.secret` | string | - | Yes | _db-secret_ | +| `spec.database.dbPassword.key` | string | password | Optional | _admin-password_ | +| `spec.database.dbPassword.secret` | string | - | Conditional | _db-secret_ | +| `spec.database.dbPassword.vaultOCID` | string | - | Conditional | _ocid1.vault.oc1..._ | +| `spec.database.dbPassword.vaultSecretName` | string | - | Conditional | _db-vault_ | +| `spec.database.dbWallet.secret` | string | - | Conditional | _devsec-oradevdb-wallet_ | +| `spec.database.dbConnectionString.key` | string | connection | Optional | _connection_ | +| `spec.database.dbConnectionString.secret` | string | - | Yes | _db-secretg_ | +| `spec.sidecars` | array | - | Optional | - | +| `spec.sidecarVolumes` | array | - | Optional | - | +| `spec.exporter.deployment.env` | map | - | Optional | _DB_ROLE: "SYSDBA"_ | +| `spec.exporter.deployment.image` | string | container-registry.oracle.com/database/observability-exporter:1.5.1 | Optional | _container-registry.oracle.com/database/observability-exporter:1.3.0_ | +| `spec.exporter.deployment.args` | array | - | Optional | _[ "--log.level=info" ]_ | +| `spec.exporter.deployment.commands` | array | - | Optional | _[ "/oracledb_exporter" ]_ | +| `spec.exporter.deployment.labels` | map | - | Optional | _environment: dev_ | +| `spec.exporter.deployment.podTemplate.labels` | map | - | Optional | _environment: dev_ | +| `spec.exporter.service.ports` | array | - | Optional | - | +| `spec.exporter.service.labels` | map | - | Optional | _environment: dev_ | | +| `spec.configuration.configMap.key` | string | config.toml | Optional | _config.toml_ | +| `spec.configuration.configMap.name` | string | - | Optional | _devcm-oradevdb-config_ | +| `spec.prometheus.serviceMonitor.labels` | map | - | Yes | _release: prometheus_ | +| `spec.prometheus.serviceMonitor.namespaceSelector` | - | - | Yes | - | +| `spec.prometheus.serviceMonitor.endpoints` | array | - | Optional | - | +| `spec.log.filename` | string | alert.log | Optional | _alert.log_ | +| `spec.log.path` | string | /log | Optional | _/log_ | +| `spec.log.volume.name` | string | log-volume | Optional | _my-persistent-volume_ | +| `spec.log.volume.persistentVolumeClaim.claimName` | string | - | Optional | _my-pvc_ | +| `spec.replicas` | number | 1 | Optional | _1_ | +| `spec.inheritLabels` | array | - | Optional | _- environment: dev_
- app.kubernetes.io/name: observer | +| `spec.ociConfig.configMapName` | string | - | Conditional | _oci-cred_ | +| `spec.ociConfig.secretName` | string | - | Conditional | _oci-privatekey_ | + + +### Configuration Options The `databaseObserver` custom resource has the following fields for all configurations that are required: * `spec.database.dbUser.secret` - secret containing the database username. The corresponding key can be any value but must match the key in the secret provided. * `spec.database.dbPassword.secret` - secret containing the database password (if vault is NOT used). The corresponding key field can be any value but must match the key in the secret provided -* `spec.database.dbConnectionString.secret` - secret containing the database connection string. The corresponding key field can be any value but must match the key in the secret provided i +* `spec.database.dbConnectionString.secret` - secret containing the database connection string. The corresponding key field can be any value but must match the key in the secret provided +* `spec.prometheus.serviceMonitor.labels` - custom labels to add to the service monitors labels. A label is required for your serviceMonitor to be discovered. This label must match what is set in the serviceMonitorSelector of your Prometheus configuration -If a database wallet is required to connect, the following field containing the secret is required: -* `spec.database.dbWallet.secret` - secret containing the database wallet. The filenames must be used as the keys +If a database wallet is required to connect, the following field containing the wallet secret is required: +* `spec.database.dbWallet.secret` - secret containing the database wallet. The filenames inside the wallet must be used as keys If vault is used to store the database password instead, the following fields are required: * `spec.database.dbPassword.vaultOCID` - OCID of the vault used @@ -83,14 +108,41 @@ If vault is used to store the database password instead, the following fields ar * `spec.ociConfig.configMapName` - holds the rest of the information of the OCI API signing key. The following keys must be used: `fingerprint`, `region`, `tenancy` and `user` * `spec.ociConfig.secretName` - holds the private key of the OCI API signing key. The key to the file containing the user private key must be: `privatekey` -The `databaseObserver` provides the remaining multiple fields that are optional: -* `spec.prometheus.labels` - labels to use for Service, ServiceMonitor and Deployment -* `spec.prometheus.port` - port to use for ServiceMonitor +The `databaseObserver` resource provides the remaining multiple fields that are optional: +* `spec.prometheus.serviceMonitor.endpoints` - ServiceMonitor endpoints +* `spec.prometheus.serviceMonitor.namespaceSelector` - ServiceMonitor namespace selector +* `spec.sidecars` - list of containers to run as a sidecar container with the observability exporter container image +* `spec.sidecarVolumes` - volumes of any sidecar containers +* `spec.log.path` - custom path to create +* `spec.log.filename` - custom filename for the log file +* `spec.log.volume.name` - custom name for the log volume +* `spec.log.volume.persistentVolumeClaim.claimName` - a volume in which to place the log in order to be shared by the containers. If not specified, an EmptyDir is used by defautl. +* `spec.configuration.configMap.key` - configuration filename inside the container and the configmap +* `spec.configuration.configMap.name` - name of the configMap that holds the custom metrics configuration * `spec.replicas` - number of replicas to deploy -* `spec.exporter.service.port` - port of service -* `spec.exporter.image` - image version of observability exporter to use +* `spec.exporter.service.ports` - port number for the generated service to use +* `spec.exporter.service.labels` - custom labels to add to service labels +* `spec.exporter.deployment.image` - image version of observability exporter to use +* `spec.exporter.deployment.env` - custom environment variables for the observability exporter +* `spec.exporter.deployment.labels` - custom labels to add to deployment labels +* `spec.exporter.deployment.podTemplate.labels` - custom labels to add to pod labels +* `spec.exporter.deployment.args` - additional arguments to provide the observability-exporter +* `spec.exporter.deployment.commands` - commands to supply to the observability-exporter +* `spec.inheritLabels` - keys of inherited labels from the databaseObserver resource. These labels are applied to generated resources. + +### Resources Managed by the Controller +When you create a DatabaseObserver resource, the controller creates and manages the following resources: +1. __Deployment__ - the deployment will have the same name as the databaseObserver resource + - deploys a container named `observability-exporter` + - the default container image version of the `container-registry.oracle.com/database/observability-exporter` supported is __[v1.5.1](https://github.com/oracle/oracle-db-appdev-monitoring/releases/tag/1.5.1)__ +2. __Service__ - the service will have the same name as the databaseObserver + - the service is of type `ClusterIP` + +3. __Prometheus ServiceMonitor__ - the serviceMonitor will have the same name as the databaseObserver + +## DatabaseObserver Operations ### Create Resource Follow the steps below to create a new databaseObserver resource object. @@ -113,13 +165,13 @@ Otherwise, you can create the wallet secret from a local directory containing th kubectl create secret generic db-wallet --from-file=wallet_dir ``` -3. Finally, update the databaseObserver manifest with the resources you have created. You can use the example manifest -inside config/samples/observability to specify and create your databaseObserver object with a +3. Finally, update the databaseObserver manifest with the resources you have created. You can use the example _minimal_ manifest +inside [config/samples/observability/v4](../../config/samples/observability/v4/databaseobserver_minimal.yaml) to specify and create your databaseObserver object with a YAML file. ```YAML # example -apiVersion: observability.oracle.com/v1alpha1 +apiVersion: observability.oracle.com/v4 kind: DatabaseObserver metadata: name: obs-sample @@ -139,6 +191,11 @@ spec: dbWallet: secret: db-wallet + + prometheus: + serviceMonitor: + labels: + release: prometheus ``` ```bash @@ -159,8 +216,8 @@ To obtain a quick status, use the following command as an example: ```sh $ kubectl get databaseobserver obs-sample -NAME EXPORTERCONFIG STATUS -obs-sample default READY +NAME EXPORTERCONFIG STATUS VERSION +obs-sample DEFAULT READY 1.5.1 ``` @@ -178,25 +235,9 @@ value for every ConditionType. ### Patch Resource The Observability controller currently supports updates for most of the fields in the manifest. An example of patching the databaseObserver resource is as follows: ```bash -kubectl --type=merge -p '{"spec":{"exporter":{"image":"container-registry.oracle.com/database/observability-exporter:latest"}}}' patch databaseobserver obs-sample +kubectl --type=merge -p '{"spec":{"exporter":{"image":"container-registry.oracle.com/database/observability-exporter:1.5.0"}}}' patch databaseobserver obs-sample ``` -The fields listed below can be updated with the given example command: - -* spec.exporter.image -* spec.exporter.configuration.configmap.configmapName -* spec.exporter.configuration.configmap.key -* spec.database.dbUser.secret -* spec.database.dbPassword.secret -* spec.database.dbConnectionString.secret -* spec.database.dbWallet.secret -* spec.ociConfig.configMapName -* spec.ociConfig.secretName -* spec.replicas -* spec.database.dbPassword.vaultOCID -* spec.database.dbPassword.vaultSecretName - - ### Delete Resource To delete the DatabaseObserver custom resource and all related resources: @@ -205,6 +246,288 @@ To delete the DatabaseObserver custom resource and all related resources: kubectl delete databaseobserver obs-sample ``` +## Scraping Metrics +The DatabaseObserver resource deploys the Observability exporter container which connects to an Oracle Database and +scrapes metrics via SQL queries. By default, the exporter provides standard metrics, which are listed in the [official GitHub page of the +Observability Exporter](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#standard-metrics). + +To define custom metrics in the Oracle Database for scraping, a TOML file that lists your custom queries and properties is required. +The file will have metric sections with the following parts: +- a context +- a request, which contains the SQL query +- a map between the field(s) in the request and comment(s) + +For example, following code snippet below shows how you can define custom metrics: +```toml +[[metric]] +context = "test" +request = "SELECT 1 as value_1, 2 as value_2 FROM DUAL" +metricsdesc = { value_1 = "Simple example returning always 1.", value_2 = "Same but returning always 2." } +``` +This file produces the following entries: +``` +# HELP oracledb_test_value_1 Simple example returning always 1. +# TYPE oracledb_test_value_1 gauge +oracledb_test_value_1 1 +# HELP oracledb_test_value_2 Same but returning always 2. +# TYPE oracledb_test_value_2 gauge +oracledb_test_value_2 2 +``` + +More information can be found in the [__Custom Metrics__](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#custom-metrics) +section of the Official GitHub page. + + + +### Custom Metrics Config +When configuring a DatabaseObserver resource, you can use the field `spec.configuration.configMap` to provide a +custom metrics file as a configMap. + +You can create the configmap by running the following command: +```bash +kubectl create cm custom-metrics-cm --from-file=metrics.toml +``` + +Finally, when creating or updating a databaseObserver resource, considering the example above, you can set the following fields in your YAML file to: +```yaml +spec: + configuration: + configMap: + key: "metrics.toml" + name: "custom-metrics-cm" +``` + +### Prometheus Release +The field `spec.prometheus.serviceMonitor.labels` is an important and required field in order to enable your Prometheus configuration +to find and include the `ServiceMonitor` created by the DatabaseObserver resource. The label on the ServiceMonitor +must match the `spec.serviceMonitorSelector` field in your Prometheus configuration. + +```yaml + prometheus: + serviceMonitor: + labels: + release: stable +``` + +## Scraping Logs +Currently, the observability exporter provides the `alert.log` from the Oracle Database, which provides important information +on errors and exceptions during database operations. + +The logs are stored in the pod filesystem, inside `/log/alert.log` by default. Note that the log can also be placed in a custom path with a custom filename, +as well as a volume available to multiple pods with the use of PersistentVolumes by specifying a persistentVolumeClaim. +Since the logs are stored in a file, scraping the logs will need to be pushed to a log aggregation system like _Loki_. +In the following example, Promtail is used as a sidecar container which ships the contents of local logs to the Loki instance. + + +To configure the DatabaseObserver resource with a sidecar, two fields can be used: +```yaml +spec: + sidecars: [] + sidecarVolumes: [] +``` + +You can find an example in the samples directory, which deploys a Promtail sidecar container as an example: +[config/samples/observability/v4/databaseobserver_logs_promtail.yaml](../../config/samples/observability/v4/databaseobserver_logs_promtail.yaml) + +### Custom Log Location with PersistentVolumes + +The fields `spec.log.filename` and `spec.log.path` enable you to configure a custom location and filename for the log. +This enables you to control where to place the logfile, such as a persistentVolume. + +```yaml + log: + filename: "alert.log" + path: "/log" +``` + +To configure the DatabaseObserver resource to put the log file in a persistentVolume, you can set the following fields +in your DatabaseObserver YAML file. The field `spec.log.volume.name` is provided to control the name of the volume used +for the log, while the field `spec.log.volume.persistentVolumeClaim.claimName` is used to specify the claim to use. +These details can be used towards any sidecar containers, or other containers. + +If `spec.log.volume.persistentVolumeClaim.claimName` is not specified, an `EmptyDir` volume is automatically used. + +```yaml + log: + volume: + name: my-log-volume + persistentVolumeClaim: + claimName: "my-pvc" +``` + + +### Working with Sidecars, to deploy Promtail +The fields `spec.sidecars` and `spec.sidecarVolumes` provide the ability to deploy container images as a sidecar container +alongside the `observability-exporter` container. + +You can specify container images to deploy inside `spec.sidecars` as you would normally define a container in a deployment. The field +`spec.sidecars` is of an array of containers (`[]corev1.Container`). + +For example, to deploy a Grafana Promtail image, you can specify the container and its details as an element to the array, `spec.sidecars`. +```yaml + sidecars: + - name: promtail + image: grafana/promtail + args: + - -config.file=/etc/promtail/config.yaml + volumeMounts: + - name: promtail-config-volume + mountPath: /etc/promtail + - name: my-log-volume + mountPath: /log +``` + +In the field `spec.sidecarVolumes`, you can specify and list the volumes you need in your sidecar containers. The field +`spec.sidecarVolumes` is an array of Volumes (`[]corev1.Volume`). + +For example, when deploying the Promtail container, you can specify in the field any volume that needs to be mounted in the sidecar container above. + +```yaml + sidecarVolumes: + - name: promtail-config-volume + configMap: + name: promtail-config-file +``` + +In this example, the `promtail-config-file` configMap contains the Promtail configuration, which specifies where to find +the target and the path to the file, as well as the endpoint where Loki is listening for any push API requests. + +__Promtail Config Example__ + +```yaml +# config.yaml +server: + http_listen_port: 9080 + grpc_listen_port: 0 +positions: + filename: /tmp/positions.yaml + +clients: + - url: http://{loki-endpoint}:3100/loki/api/v1/push + +scrape_configs: + - job_name: "alert-log" + static_configs: + - targets: + - localhost + labels: + app: {my-database-observer-label} + __path__: /log/*.log + ``` + +To create the configmap, you can run the following command: +```bash +kubectl create cm promtail-config-file --from-file=config.yaml +``` + + +## Other Configuration Options + +### Labels + +__About the Default Label__ - The resources created by the Observability Controller will automatically be labelled with: +- `app`: `` + + +For example, if the databaseObserver instance is named: `metrics-exporter`, resources like the deployment will be labelled +with `app: metrics-exporter`. This label cannot be overwritten. Selectors used by the deployment, service and servicemonitor use this label. + +The following configuration shows an example: + +```yaml +apiVersion: observability.oracle.com/v4 +kind: DatabaseObserver +metadata: + name: metrics-exporter + labels: + app: my-db-metrics + some: non-inherited-label +spec: + + # inheritLabels + inheritLabels: + - some + + # ... +``` + +Meanwhile, you can provide extra labels to the resources created by the DatabaseObserver controller such as the Deployment, Pods, Service and ServiceMonitor. +```yaml +spec: + exporter: + deployment: + labels: + podTemplate: + labels: + service: + labels: + prometheus: + serviceMonitor: + labels: +``` + +### Custom Exporter Image or Version +The field `spec.exporter.deployment.image` is provided to enable you to make use of a newer or older version of the [observability-exporter](https://github.com/oracle/oracle-db-appdev-monitoring) +container image. + +```yaml +spec: + exporter: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.1.0" +``` + +### Custom Environment Variables, Arguments and Commands +The fields `spec.exporter.deployment.env`, `spec.exporter.deployment.args` and `spec.exporter.deployment.commands` are provided for adding custom environment variables, args and commands to the containers. +Any custom environment variable will overwrite environment variables set by the controller. + +```yaml +spec: + exporter: + deployment: + env: + DB_ROLE: "" + TNS_ADMIN: "" + args: + - "--log.level=info" + commands: + - "/oracledb_exporter" +``` + + +### Custom Service Ports +The field `spec.exporter.service.ports` is provided for setting the ports of the service. If not set, the following definition is set by default. + +```yaml +spec: + exporter: + service: + ports: + - name: metrics + port: 9161 + targetPort: 9161 + +``` + +### Custom ServiceMonitor Endpoints +The field `spec.prometheus.serviceMonitor.endpoints` is provided for providing custom endpoints for the ServiceMonitor resource created by the DatabaseObserver + +```yaml +spec: + prometheus: + serviceMonitor: + endpoints: + - bearerTokenSecret: + key: '' + interval: 20s + port: metrics + relabelings: + - action: replace + sourceLabels: + - __meta_kubernetes_endpoints_label_app + targetLabel: instance +``` + ## Mandatory roles and privileges requirements for Observability Controller The Observability controller issues the following policy rules for the following resources. Besides @@ -254,3 +577,7 @@ Follow the steps to check the logs. ```sh kubectl logs deployment.apps/oracle-database-operator-controller-manager -n oracle-database-operator-system ``` + + +## Resources +- [GitHub - Unified Observability for Oracle Database Project](https://github.com/oracle/oracle-db-appdev-monitoring) \ No newline at end of file diff --git a/main.go b/main.go index a9b6e58f..35374717 100644 --- a/main.go +++ b/main.go @@ -67,6 +67,8 @@ import ( databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + observabilityv1 "github.com/oracle/oracle-database-operator/apis/observability/v1" + observabilityv4 "github.com/oracle/oracle-database-operator/apis/observability/v4" observabilitycontroller "github.com/oracle/oracle-database-operator/controllers/observability" // +kubebuilder:scaffold:imports ) @@ -82,6 +84,8 @@ func init() { utilruntime.Must(monitorv1.AddToScheme(scheme)) utilruntime.Must(databasev1alpha1.AddToScheme(scheme)) utilruntime.Must(databasev4.AddToScheme(scheme)) + utilruntime.Must(observabilityv1.AddToScheme(scheme)) + utilruntime.Must(observabilityv4.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -299,6 +303,15 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") os.Exit(1) } + if err = (&observabilityv1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + os.Exit(1) + } + + if err = (&observabilityv4.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + os.Exit(1) + } } // PDB Reconciler From f008d3553c509ffd0fabac1aa9aa8bf8ca3f1f09 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Mon, 6 Jan 2025 22:35:27 +0000 Subject: [PATCH 147/414] update documetation folder --- docs/multitenant/lrest-based/README.md | 10 + .../lrest-based/usecase/create_lrest_pod.yaml | 2 +- .../usecase/oracle-database-operator.yaml | 5058 +---------------- .../oracle-database-operator.yaml.ORIGNINAL | 5057 ---------------- .../oracle-database-operator.yaml.pippo | 5057 ---------------- .../lrest-based/usecase/parameters.txt | 3 +- .../altersystem_lrpdb1_resource.yaml | 48 - .../usecase05_to_be_removed/ca.crt | 22 - .../usecase05_to_be_removed/ca.key | 28 - .../usecase05_to_be_removed/ca.srl | 1 - .../cdbnamespace_binding.yaml | 14 - .../cert_path/certificates/ca.crt | 22 - .../cert_path/certificates/ca.key | 27 - .../cert_path/certificates/ca.srl | 1 - .../cert_path/certificates/dbpass.txt | 1 - .../cert_path/certificates/dbuser.txt | 1 - .../cert_path/certificates/e_dbpass.txt | 5 - .../cert_path/certificates/e_dbuser.txt | 5 - .../cert_path/certificates/e_pdbpwd.txt | 5 - .../cert_path/certificates/e_pdbusr.txt | 5 - .../cert_path/certificates/e_wbpass.txt | 5 - .../cert_path/certificates/e_wbuser.txt | 5 - .../cert_path/certificates/extfile.txt | 1 - .../cert_path/certificates/lrest_server.crt | 21 - .../certificates/lrest_server.crt.orig | 21 - .../cert_path/certificates/lrest_server.csr | 17 - .../cert_path/certificates/lrest_server.key | 28 - .../cert_path/certificates/pdbpwd.txt | 1 - .../cert_path/certificates/pdbusr.txt | 1 - .../cert_path/certificates/public.pem | 9 - .../cert_path/certificates/wbpass.txt | 1 - .../cert_path/certificates/wbuser.txt | 1 - .../usecase05_to_be_removed/check.sh | 4 - .../clone_lrpdb1_resource.yaml | 47 - .../clone_lrpdb2_resource.yaml | 46 - .../close_lrpdb1_resource.yaml | 39 - .../close_lrpdb2_resource.yaml | 40 - .../close_lrpdb3_resource.yaml | 40 - .../create_lrest_pod.yaml | 48 - .../create_lrest_pod_delcascade.yaml | 48 - .../create_lrpdb1_resource.yaml | 53 - .../create_lrpdb2_resource.yaml | 51 - .../usecase05_to_be_removed/dbpass.txt | 1 - .../usecase05_to_be_removed/dbuser.txt | 1 - .../delete_lrpdb1_resource.yaml | 41 - .../delete_lrpdb2_resource.yaml | 41 - .../delete_lrpdb3_resource.yaml | 41 - .../usecase05_to_be_removed/dmpplane.819519 | 30 - .../usecase05_to_be_removed/e_dbpass.txt | 5 - .../usecase05_to_be_removed/e_dbuser.txt | 5 - .../usecase05_to_be_removed/e_pdbpwd.txt | 5 - .../usecase05_to_be_removed/e_pdbusr.txt | 5 - .../usecase05_to_be_removed/e_wbpass.txt | 5 - .../usecase05_to_be_removed/e_wbuser.txt | 5 - .../usecase05_to_be_removed/extfile.txt | 1 - .../usecase05_to_be_removed/makefile | 647 --- .../map_lrpdb1_resource.yaml | 41 - .../map_lrpdb2_resource.yaml | 48 - .../map_lrpdb3_resource.yaml | 48 - .../open_lrpdb1_resource.yaml | 39 - .../open_lrpdb2_resource.yaml | 39 - .../open_lrpdb3_resource.yaml | 39 - .../usecase05_to_be_removed/parameters.txt | 4 - .../pdbnamespace_binding.yaml | 14 - .../usecase05_to_be_removed/pdbpwd.txt | 1 - .../usecase05_to_be_removed/pdbusr.txt | 1 - .../plug_lrpdb1_resource.yaml | 50 - .../usecase05_to_be_removed/public.pem | 9 - .../resetaction_lrpdb1_resource.yaml | 45 - .../usecase05_to_be_removed/server.csr | 17 - .../usecase05_to_be_removed/testDelCascade.sh | 24 - .../tkpc_k8slrestsetup.sh | 294 - .../usecase05_to_be_removed/tls.crt | 21 - .../usecase05_to_be_removed/tls.key | 28 - .../unplug_lrpdb1_resource.yaml | 42 - .../usecase05_to_be_removed/wbpass.txt | 1 - .../usecase05_to_be_removed/wbuser.txt | 1 - .../ords-based/usecase/create_ords_pod.yaml | 2 +- .../usecase/oracle-database-operator.yaml | 5058 +---------------- .../ords-based/usecase/parameters.txt | 20 +- .../usecase01/oracle-database-operator.yaml | 2 +- .../usecase03}/oracle-database-operator.yaml | 0 82 files changed, 27 insertions(+), 22593 deletions(-) mode change 100644 => 120000 docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.ORIGNINAL delete mode 100644 docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.pippo delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/altersystem_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/ca.crt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/ca.key delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/ca.srl delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cdbnamespace_binding.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.crt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.key delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.srl delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbpass.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbuser.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbpass.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbuser.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbpwd.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbusr.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbpass.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbuser.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/extfile.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt.orig delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.csr delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.key delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbpwd.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbusr.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/public.pem delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbpass.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbuser.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/check.sh delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb3_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod_delcascade.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/dbpass.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/dbuser.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb3_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/dmpplane.819519 delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbpass.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbuser.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbpwd.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbusr.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbpass.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbuser.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/extfile.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/makefile delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb3_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb3_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/parameters.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/pdbnamespace_binding.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/pdbpwd.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/pdbusr.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/plug_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/public.pem delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/resetaction_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/server.csr delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/testDelCascade.sh delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/tkpc_k8slrestsetup.sh delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/tls.crt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/tls.key delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/unplug_lrpdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/wbpass.txt delete mode 100644 docs/multitenant/lrest-based/usecase05_to_be_removed/wbuser.txt mode change 100644 => 120000 docs/multitenant/ords-based/usecase/oracle-database-operator.yaml rename docs/multitenant/{lrest-based/usecase05_to_be_removed => ords-based/usecase03}/oracle-database-operator.yaml (100%) diff --git a/docs/multitenant/lrest-based/README.md b/docs/multitenant/lrest-based/README.md index 6475be90..fb7b3b31 100644 --- a/docs/multitenant/lrest-based/README.md +++ b/docs/multitenant/lrest-based/README.md @@ -171,6 +171,16 @@ rm dbuser.txt dbpass.txt wbuser.txt wbpass.txt pdbusr.txt pdbpwd.txt \ Use yaml file [create_lrest_pod.yaml](./usecase/create_lrest_pod.yaml) to create the rest pod and monitor the execution. +Be sure to update the **lrestImage** with latest version available on the [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:4:104288359787984:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1283,1283,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,1,0&cs=3076h-hg1qX3eJANBcUHBNBCmYWjMvxLkZyTAhDn2e8VR8Gxb_a-I8jZLhf9j6gmnimHwlP_a0OQjX6vjBfSAqQ) + +```bash +--> for amd64 +lrestImage: container-registry.oracle.com/database/operator:lrest-241210-amd64 + +--> for arm64 +lrestImage: container-registry.oracle.com/database/operator:lrest-241210-arm64 +``` + ```bash kubectl apply -f create_lrest_pod.yaml ``` diff --git a/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml b/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml index 843386df..0545c501 100644 --- a/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml +++ b/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml @@ -5,7 +5,7 @@ metadata: namespace: cdbnamespace spec: cdbName: "DB12" - lrestImage: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/lrest-dboper:latest + lrestImage: container-registry.oracle.com/database/operator:lrest-241210-amd64 lrestImagePullPolicy: "Always" dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" replicas: 1 diff --git a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml deleted file mode 100644 index b441338d..00000000 --- a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml +++ /dev/null @@ -1,5057 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-system ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomouscontainerdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousContainerDatabase - listKind: AutonomousContainerDatabaseList - plural: autonomouscontainerdatabases - shortNames: - - acd - - acds - singular: autonomouscontainerdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.displayName - name: DisplayName - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - SYNC - - RESTART - - TERMINATE - type: string - autonomousContainerDatabaseOCID: - type: string - autonomousExadataVMClusterOCID: - type: string - compartmentOCID: - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - hardLink: - default: false - type: boolean - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - patchModel: - enum: - - RELEASE_UPDATES - - RELEASE_UPDATE_REVISIONS - type: string - type: object - status: - properties: - lifecycleState: - type: string - timeCreated: - type: string - required: - - lifecycleState - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabasebackups.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseBackup - listKind: AutonomousDatabaseBackupList - plural: autonomousdatabasebackups - shortNames: - - adbbu - - adbbus - singular: autonomousdatabasebackup - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.dbDisplayName - name: DB DisplayName - type: string - - jsonPath: .status.type - name: Type - type: string - - jsonPath: .status.timeStarted - name: Started - type: string - - jsonPath: .status.timeEnded - name: Ended - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - autonomousDatabaseBackupOCID: - type: string - displayName: - type: string - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - target: - properties: - k8sADB: - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - type: object - status: - properties: - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - dbDisplayName: - type: string - dbName: - type: string - isAutomatic: - type: boolean - lifecycleState: - type: string - timeEnded: - type: string - timeStarted: - type: string - type: - type: string - required: - - autonomousDatabaseOCID - - compartmentOCID - - dbDisplayName - - dbName - - isAutomatic - - lifecycleState - - type - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabaserestores.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseRestore - listKind: AutonomousDatabaseRestoreList - plural: autonomousdatabaserestores - shortNames: - - adbr - - adbrs - singular: autonomousdatabaserestore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.displayName - name: DbDisplayName - type: string - - jsonPath: .status.dbName - name: DbName - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - source: - properties: - k8sADBBackup: - properties: - name: - type: string - type: object - pointInTime: - properties: - timestamp: - type: string - type: object - type: object - target: - properties: - k8sADB: - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - required: - - source - - target - type: object - status: - properties: - dbName: - type: string - displayName: - type: string - status: - type: string - timeAccepted: - type: string - timeEnded: - type: string - timeStarted: - type: string - workRequestOCID: - type: string - required: - - dbName - - displayName - - status - - workRequestOCID - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases - shortNames: - - adb - - adbs - singular: autonomousdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.details.displayName - name: Display Name - type: string - - jsonPath: .spec.details.dbName - name: Db Name - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .spec.details.isDedicated - name: Dedicated - type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .spec.details.dataStorageSizeInTBs - name: Storage (TB) - type: integer - - jsonPath: .spec.details.dbWorkload - name: Workload Type - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - details: - properties: - adminPassword: - properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - autonomousContainerDatabase: - properties: - k8sACD: - properties: - name: - type: string - type: object - ociACD: - properties: - ocid: - type: string - type: object - type: object - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbName: - type: string - dbVersion: - type: string - dbWorkload: - enum: - - OLTP - - DW - - AJD - - APEX - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - isAutoScalingEnabled: - type: boolean - isDedicated: - type: boolean - licenseModel: - enum: - - LICENSE_INCLUDED - - BRING_YOUR_OWN_LICENSE - type: string - lifecycleState: - type: string - networkAccess: - properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: - properties: - hostnamePrefix: - type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: - type: string - type: object - type: object - wallet: - properties: - name: - type: string - password: - properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - type: object - type: object - hardLink: - default: false - type: boolean - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - required: - - details - type: object - status: - properties: - allConnectionStrings: - items: - properties: - connectionStrings: - items: - properties: - connectionString: - type: string - tnsName: - type: string - type: object - type: array - tlsAuthentication: - type: string - required: - - connectionStrings - type: object - type: array - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - lifecycleState: - type: string - timeCreated: - type: string - walletExpiringDate: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: cdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - ordsImage: - type: string - ordsImagePullPolicy: - enum: - - Always - - Never - type: string - ordsImagePullSecret: - type: string - ordsPort: - type: integer - ordsPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: databaseobservers.observability.oracle.com -spec: - group: observability.oracle.com - names: - kind: DatabaseObserver - listKind: DatabaseObserverList - plural: databaseobservers - singular: databaseobserver - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - database: - properties: - dbConnectionString: - properties: - key: - type: string - secret: - type: string - type: object - dbPassword: - properties: - key: - type: string - secret: - type: string - vaultOCID: - type: string - vaultSecretName: - type: string - type: object - dbUser: - properties: - key: - type: string - secret: - type: string - type: object - dbWallet: - properties: - key: - type: string - secret: - type: string - type: object - type: object - exporter: - properties: - configuration: - properties: - configmap: - properties: - configmapName: - type: string - key: - type: string - type: object - type: object - image: - type: string - service: - properties: - port: - format: int32 - type: integer - type: object - type: object - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - prometheus: - properties: - labels: - additionalProperties: - type: string - type: object - port: - type: string - type: object - replicas: - format: int32 - type: integer - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - exporterConfig: - type: string - replicas: - type: integer - status: - type: string - required: - - conditions - - exporterConfig - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - properties: - clusterConnectString: - type: string - externalConnectString: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: dbcssystems.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DbcsSystem - listKind: DbcsSystemList - plural: dbcssystems - singular: dbcssystem - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - sshPublicKeys - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - ociConfigMap: - type: string - ociSecret: - type: string - required: - - ociConfigMap - type: object - status: - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbEdition: - type: string - dbInfo: - items: - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: lrests.database.oracle.com -spec: - group: database.oracle.com - names: - kind: LREST - listKind: LRESTList - plural: lrests - singular: lrest - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the LREST - jsonPath: .spec.cdbName - name: CDB NAME - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the LREST Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message if any - jsonPath: .status.msg - name: Message - type: string - - description: string of the tnsalias - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - lrestImage: - type: string - lrestImagePullPolicy: - enum: - - Always - - Never - type: string - lrestImagePullSecret: - type: string - lrestPort: - type: integer - lrestPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - nodeSelector: - additionalProperties: - type: string - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: lrpdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: LRPDB - listKind: LRPDBList - plural: lrpdbs - singular: lrpdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the LRPDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: last sqlcode - jsonPath: .status.sqlCode - name: last sqlcode - type: integer - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - - Alter - - Noaction - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbPass: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - alterSystem: - type: string - alterSystemParameter: - type: string - alterSystemValue: - type: string - asClone: - type: boolean - assertiveLrpdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - lrpdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - parameterScope: - type: string - pdbName: - type: string - pdbState: - enum: - - OPEN - - CLOSE - - ALTER - type: string - pdbconfigmap: - type: string - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - - alterSystemParameter - - alterSystemValue - - webServerPwd - type: object - status: - properties: - action: - type: string - alterSystem: - type: string - bitstat: - type: integer - bitstatstr: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - sqlCode: - type: integer - status: - type: boolean - totalSize: - type: string - required: - - phase - - sqlCode - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: oraclerestdataservices.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestDataService - listKind: OracleRestDataServiceList - plural: oraclerestdataservices - singular: oraclerestdataservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - apexPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - oracleService: - type: string - ordsPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - ordsUser: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - size: - type: string - storageClass: - type: string - volumeName: - type: string - type: object - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: - type: string - urlMapping: - type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - required: - - adminPassword - - databaseRef - - ordsPassword - type: object - status: - properties: - apexConfigured: - type: boolean - apexUrl: - type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: - type: string - databaseApiUrl: - type: string - databaseRef: - type: string - image: - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: string - ordsInstalled: - type: boolean - replicas: - type: integer - serviceIP: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: pdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - type: boolean - assertivePdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - type: string - pdbState: - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - type: object - status: - properties: - action: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - status: - type: boolean - totalSize: - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: shardingdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: ShardingDatabase - listKind: ShardingDatabaseList - plural: shardingdatabases - singular: shardingdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - gsm: - items: - properties: - directorName: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: boolean - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - items: - properties: - deployAs: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - gsm: - items: - properties: - directorName: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: boolean - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - items: - properties: - deployAs: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.edition - name: Edition - type: string - - jsonPath: .status.sid - name: Sid - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version - type: string - - jsonPath: .status.connectString - name: Connect Str - type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.tcpsConnectString - name: TCPS Connect Str - type: string - - jsonPath: .status.tcpsPdbConnectString - name: TCPS Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.oemExpressUrl - name: Oem Express Url - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - archiveLog: - type: boolean - charset: - type: string - createAs: - enum: - - primary - - standby - - clone - type: string - dgBrokerConfigured: - type: boolean - edition: - enum: - - standard - - enterprise - - express - - free - type: string - enableTCPS: - type: boolean - flashBack: - type: boolean - forceLog: - type: boolean - image: - properties: - prebuiltDB: - type: boolean - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - listenerPort: - type: integer - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - pdbName: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - primaryDatabaseRef: - type: string - readinessCheckPeriod: - type: integer - replicas: - type: integer - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - sid: - maxLength: 12 - pattern: ^[a-zA-Z0-9]+$ - type: string - tcpsCertRenewInterval: - type: string - tcpsListenerPort: - type: integer - tcpsTlsSecret: - type: string - required: - - image - type: object - status: - properties: - apexInstalled: - type: boolean - archiveLog: - type: string - certCreationTimestamp: - type: string - certRenewInterval: - type: string - charset: - type: string - clientWalletLoc: - type: string - clusterConnectString: - type: string - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - connectString: - type: string - createdAs: - type: string - datafilesCreated: - default: "false" - type: string - datafilesPatched: - default: "false" - type: string - dgBrokerConfigured: - type: boolean - edition: - type: string - flashBack: - type: string - forceLog: - type: string - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - initPgaSize: - type: integer - initSgaSize: - type: integer - isTcpsEnabled: - default: false - type: boolean - nodes: - items: - type: string - type: array - oemExpressUrl: - type: string - ordsReference: - type: string - pdbConnectString: - type: string - pdbName: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - prebuiltDB: - type: boolean - primaryDatabase: - type: string - releaseUpdate: - type: string - replicas: - type: integer - role: - type: string - sid: - type: string - standbyDatabases: - additionalProperties: - type: string - type: object - status: - type: string - tcpsConnectString: - type: string - tcpsPdbConnectString: - type: string - tcpsTlsSecret: - default: "" - type: string - required: - - isTcpsEnabled - - persistence - - tcpsTlsSecret - type: object - type: object - served: true - storage: true - subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.replicas - status: {} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: oracle-database-operator-leader-election-role - namespace: oracle-database-operator-system -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - - deployments - - events - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list -- apiGroups: - - '''''' - resources: - - statefulsets/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - configmaps - - deployments - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - replicasets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - list - - update -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods/exec - verbs: - - create -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases/status - verbs: - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - cdbs/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - events - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrests - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrests/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - lrests/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - lrpdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrpdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - lrpdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - pdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - monitoring.coreos.com - resources: - - prometheusrules - - servicemonitors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/finalizers - verbs: - - update -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/status - verbs: - - get - - patch - - update -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-oracle-database-operator-proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: oracle-database-operator-leader-election-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-oracle-database-operator-proxy-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager-metrics-service - namespace: oracle-database-operator-system -spec: - ports: - - name: https - port: 8443 - targetPort: https - selector: - control-plane: controller-manager ---- -apiVersion: v1 -kind: Service -metadata: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - control-plane: controller-manager ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: oracle-database-operator-serving-cert - namespace: oracle-database-operator-system -spec: - dnsNames: - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local - issuerRef: - kind: Issuer - name: oracle-database-operator-selfsigned-issuer - secretName: webhook-server-cert ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: oracle-database-operator-selfsigned-issuer - namespace: oracle-database-operator-system -spec: - selfSigned: {} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-mutating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: mdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: moraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: msingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: mcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-lrest - failurePolicy: Fail - name: mlrest.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrests - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-lrpdb - failurePolicy: Fail - name: mlrpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrpdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: mpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-validating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase - failurePolicy: Fail - name: vautonomouscontainerdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomouscontainerdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: vautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: vautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore - failurePolicy: Fail - name: vautonomousdatabaserestore.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabaserestores - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: vdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: voraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: vsingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: vcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-lrest - failurePolicy: Fail - name: vlrest.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrests - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-lrpdb - failurePolicy: Fail - name: vlrpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrpdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: vpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager - namespace: oracle-database-operator-system -spec: - replicas: 3 - selector: - matchLabels: - control-plane: controller-manager - template: - metadata: - labels: - control-plane: controller-manager - spec: - containers: - - args: - - --enable-leader-election - command: - - /manager - env: - - name: WATCH_NAMESPACE - value: oracle-database-operator-system,pdbnamespace,cdbnamespace - image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/mn_wallet_lrestbranch:latest - imagePullPolicy: Always - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - resources: - limits: - cpu: 400m - memory: 400Mi - requests: - cpu: 400m - memory: 400Mi - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - terminationGracePeriodSeconds: 10 - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert ---- diff --git a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml new file mode 120000 index 00000000..bbf4775f --- /dev/null +++ b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml @@ -0,0 +1 @@ +../../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.ORIGNINAL b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.ORIGNINAL deleted file mode 100644 index d1cc3eca..00000000 --- a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.ORIGNINAL +++ /dev/null @@ -1,5057 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-system ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomouscontainerdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousContainerDatabase - listKind: AutonomousContainerDatabaseList - plural: autonomouscontainerdatabases - shortNames: - - acd - - acds - singular: autonomouscontainerdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.displayName - name: DisplayName - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - SYNC - - RESTART - - TERMINATE - type: string - autonomousContainerDatabaseOCID: - type: string - autonomousExadataVMClusterOCID: - type: string - compartmentOCID: - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - hardLink: - default: false - type: boolean - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - patchModel: - enum: - - RELEASE_UPDATES - - RELEASE_UPDATE_REVISIONS - type: string - type: object - status: - properties: - lifecycleState: - type: string - timeCreated: - type: string - required: - - lifecycleState - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabasebackups.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseBackup - listKind: AutonomousDatabaseBackupList - plural: autonomousdatabasebackups - shortNames: - - adbbu - - adbbus - singular: autonomousdatabasebackup - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.dbDisplayName - name: DB DisplayName - type: string - - jsonPath: .status.type - name: Type - type: string - - jsonPath: .status.timeStarted - name: Started - type: string - - jsonPath: .status.timeEnded - name: Ended - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - autonomousDatabaseBackupOCID: - type: string - displayName: - type: string - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - target: - properties: - k8sADB: - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - type: object - status: - properties: - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - dbDisplayName: - type: string - dbName: - type: string - isAutomatic: - type: boolean - lifecycleState: - type: string - timeEnded: - type: string - timeStarted: - type: string - type: - type: string - required: - - autonomousDatabaseOCID - - compartmentOCID - - dbDisplayName - - dbName - - isAutomatic - - lifecycleState - - type - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabaserestores.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseRestore - listKind: AutonomousDatabaseRestoreList - plural: autonomousdatabaserestores - shortNames: - - adbr - - adbrs - singular: autonomousdatabaserestore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.displayName - name: DbDisplayName - type: string - - jsonPath: .status.dbName - name: DbName - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - source: - properties: - k8sADBBackup: - properties: - name: - type: string - type: object - pointInTime: - properties: - timestamp: - type: string - type: object - type: object - target: - properties: - k8sADB: - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - required: - - source - - target - type: object - status: - properties: - dbName: - type: string - displayName: - type: string - status: - type: string - timeAccepted: - type: string - timeEnded: - type: string - timeStarted: - type: string - workRequestOCID: - type: string - required: - - dbName - - displayName - - status - - workRequestOCID - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases - shortNames: - - adb - - adbs - singular: autonomousdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.details.displayName - name: Display Name - type: string - - jsonPath: .spec.details.dbName - name: Db Name - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .spec.details.isDedicated - name: Dedicated - type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .spec.details.dataStorageSizeInTBs - name: Storage (TB) - type: integer - - jsonPath: .spec.details.dbWorkload - name: Workload Type - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - details: - properties: - adminPassword: - properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - autonomousContainerDatabase: - properties: - k8sACD: - properties: - name: - type: string - type: object - ociACD: - properties: - ocid: - type: string - type: object - type: object - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbName: - type: string - dbVersion: - type: string - dbWorkload: - enum: - - OLTP - - DW - - AJD - - APEX - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - isAutoScalingEnabled: - type: boolean - isDedicated: - type: boolean - licenseModel: - enum: - - LICENSE_INCLUDED - - BRING_YOUR_OWN_LICENSE - type: string - lifecycleState: - type: string - networkAccess: - properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: - properties: - hostnamePrefix: - type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: - type: string - type: object - type: object - wallet: - properties: - name: - type: string - password: - properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - type: object - type: object - hardLink: - default: false - type: boolean - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - required: - - details - type: object - status: - properties: - allConnectionStrings: - items: - properties: - connectionStrings: - items: - properties: - connectionString: - type: string - tnsName: - type: string - type: object - type: array - tlsAuthentication: - type: string - required: - - connectionStrings - type: object - type: array - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - lifecycleState: - type: string - timeCreated: - type: string - walletExpiringDate: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: cdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - ordsImage: - type: string - ordsImagePullPolicy: - enum: - - Always - - Never - type: string - ordsImagePullSecret: - type: string - ordsPort: - type: integer - ordsPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: databaseobservers.observability.oracle.com -spec: - group: observability.oracle.com - names: - kind: DatabaseObserver - listKind: DatabaseObserverList - plural: databaseobservers - singular: databaseobserver - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - database: - properties: - dbConnectionString: - properties: - key: - type: string - secret: - type: string - type: object - dbPassword: - properties: - key: - type: string - secret: - type: string - vaultOCID: - type: string - vaultSecretName: - type: string - type: object - dbUser: - properties: - key: - type: string - secret: - type: string - type: object - dbWallet: - properties: - key: - type: string - secret: - type: string - type: object - type: object - exporter: - properties: - configuration: - properties: - configmap: - properties: - configmapName: - type: string - key: - type: string - type: object - type: object - image: - type: string - service: - properties: - port: - format: int32 - type: integer - type: object - type: object - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - prometheus: - properties: - labels: - additionalProperties: - type: string - type: object - port: - type: string - type: object - replicas: - format: int32 - type: integer - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - exporterConfig: - type: string - replicas: - type: integer - status: - type: string - required: - - conditions - - exporterConfig - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - properties: - clusterConnectString: - type: string - externalConnectString: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: dbcssystems.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DbcsSystem - listKind: DbcsSystemList - plural: dbcssystems - singular: dbcssystem - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - sshPublicKeys - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - ociConfigMap: - type: string - ociSecret: - type: string - required: - - ociConfigMap - type: object - status: - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbEdition: - type: string - dbInfo: - items: - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: lrests.database.oracle.com -spec: - group: database.oracle.com - names: - kind: LREST - listKind: LRESTList - plural: lrests - singular: lrest - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the LREST - jsonPath: .spec.cdbName - name: CDB NAME - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the LREST Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message if any - jsonPath: .status.msg - name: Message - type: string - - description: string of the tnsalias - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - lrestImage: - type: string - lrestImagePullPolicy: - enum: - - Always - - Never - type: string - lrestImagePullSecret: - type: string - lrestPort: - type: integer - lrestPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - nodeSelector: - additionalProperties: - type: string - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: lrpdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: LRPDB - listKind: LRPDBList - plural: lrpdbs - singular: lrpdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the LRPDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: last sqlcode - jsonPath: .status.sqlCode - name: last sqlcode - type: integer - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - - Alter - - Noaction - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbPass: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - alterSystem: - type: string - alterSystemParameter: - type: string - alterSystemValue: - type: string - asClone: - type: boolean - assertiveLrpdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - lrpdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - parameterScope: - type: string - pdbName: - type: string - pdbState: - enum: - - OPEN - - CLOSE - - ALTER - type: string - pdbconfigmap: - type: string - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - - alterSystemParameter - - alterSystemValue - - webServerPwd - type: object - status: - properties: - action: - type: string - alterSystem: - type: string - bitstat: - type: integer - bitstatstr: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - sqlCode: - type: integer - status: - type: boolean - totalSize: - type: string - required: - - phase - - sqlCode - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: oraclerestdataservices.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestDataService - listKind: OracleRestDataServiceList - plural: oraclerestdataservices - singular: oraclerestdataservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - apexPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - oracleService: - type: string - ordsPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - ordsUser: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - size: - type: string - storageClass: - type: string - volumeName: - type: string - type: object - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: - type: string - urlMapping: - type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - required: - - adminPassword - - databaseRef - - ordsPassword - type: object - status: - properties: - apexConfigured: - type: boolean - apexUrl: - type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: - type: string - databaseApiUrl: - type: string - databaseRef: - type: string - image: - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: string - ordsInstalled: - type: boolean - replicas: - type: integer - serviceIP: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: pdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - type: boolean - assertivePdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - type: string - pdbState: - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - type: object - status: - properties: - action: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - status: - type: boolean - totalSize: - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: shardingdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: ShardingDatabase - listKind: ShardingDatabaseList - plural: shardingdatabases - singular: shardingdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - gsm: - items: - properties: - directorName: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: boolean - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - items: - properties: - deployAs: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - gsm: - items: - properties: - directorName: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: boolean - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - items: - properties: - deployAs: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.edition - name: Edition - type: string - - jsonPath: .status.sid - name: Sid - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version - type: string - - jsonPath: .status.connectString - name: Connect Str - type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.tcpsConnectString - name: TCPS Connect Str - type: string - - jsonPath: .status.tcpsPdbConnectString - name: TCPS Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.oemExpressUrl - name: Oem Express Url - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - archiveLog: - type: boolean - charset: - type: string - createAs: - enum: - - primary - - standby - - clone - type: string - dgBrokerConfigured: - type: boolean - edition: - enum: - - standard - - enterprise - - express - - free - type: string - enableTCPS: - type: boolean - flashBack: - type: boolean - forceLog: - type: boolean - image: - properties: - prebuiltDB: - type: boolean - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - listenerPort: - type: integer - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - pdbName: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - primaryDatabaseRef: - type: string - readinessCheckPeriod: - type: integer - replicas: - type: integer - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - sid: - maxLength: 12 - pattern: ^[a-zA-Z0-9]+$ - type: string - tcpsCertRenewInterval: - type: string - tcpsListenerPort: - type: integer - tcpsTlsSecret: - type: string - required: - - image - type: object - status: - properties: - apexInstalled: - type: boolean - archiveLog: - type: string - certCreationTimestamp: - type: string - certRenewInterval: - type: string - charset: - type: string - clientWalletLoc: - type: string - clusterConnectString: - type: string - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - connectString: - type: string - createdAs: - type: string - datafilesCreated: - default: "false" - type: string - datafilesPatched: - default: "false" - type: string - dgBrokerConfigured: - type: boolean - edition: - type: string - flashBack: - type: string - forceLog: - type: string - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - initPgaSize: - type: integer - initSgaSize: - type: integer - isTcpsEnabled: - default: false - type: boolean - nodes: - items: - type: string - type: array - oemExpressUrl: - type: string - ordsReference: - type: string - pdbConnectString: - type: string - pdbName: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - prebuiltDB: - type: boolean - primaryDatabase: - type: string - releaseUpdate: - type: string - replicas: - type: integer - role: - type: string - sid: - type: string - standbyDatabases: - additionalProperties: - type: string - type: object - status: - type: string - tcpsConnectString: - type: string - tcpsPdbConnectString: - type: string - tcpsTlsSecret: - default: "" - type: string - required: - - isTcpsEnabled - - persistence - - tcpsTlsSecret - type: object - type: object - served: true - storage: true - subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.replicas - status: {} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: oracle-database-operator-leader-election-role - namespace: oracle-database-operator-system -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - - deployments - - events - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list -- apiGroups: - - '''''' - resources: - - statefulsets/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - configmaps - - deployments - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - replicasets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - list - - update -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods/exec - verbs: - - create -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases/status - verbs: - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - cdbs/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - events - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrests - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrests/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - lrests/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - lrpdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrpdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - lrpdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - pdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - monitoring.coreos.com - resources: - - prometheusrules - - servicemonitors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/finalizers - verbs: - - update -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/status - verbs: - - get - - patch - - update -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-oracle-database-operator-proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: oracle-database-operator-leader-election-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-oracle-database-operator-proxy-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager-metrics-service - namespace: oracle-database-operator-system -spec: - ports: - - name: https - port: 8443 - targetPort: https - selector: - control-plane: controller-manager ---- -apiVersion: v1 -kind: Service -metadata: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - control-plane: controller-manager ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: oracle-database-operator-serving-cert - namespace: oracle-database-operator-system -spec: - dnsNames: - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local - issuerRef: - kind: Issuer - name: oracle-database-operator-selfsigned-issuer - secretName: webhook-server-cert ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: oracle-database-operator-selfsigned-issuer - namespace: oracle-database-operator-system -spec: - selfSigned: {} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-mutating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: mdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: moraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: msingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: mcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-lrest - failurePolicy: Fail - name: mlrest.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrests - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-lrpdb - failurePolicy: Fail - name: mlrpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrpdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: mpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-validating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase - failurePolicy: Fail - name: vautonomouscontainerdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomouscontainerdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: vautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: vautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore - failurePolicy: Fail - name: vautonomousdatabaserestore.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabaserestores - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: vdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: voraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: vsingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: vcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-lrest - failurePolicy: Fail - name: vlrest.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrests - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-lrpdb - failurePolicy: Fail - name: vlrpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrpdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: vpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager - namespace: oracle-database-operator-system -spec: - replicas: 3 - selector: - matchLabels: - control-plane: controller-manager - template: - metadata: - labels: - control-plane: controller-manager - spec: - containers: - - args: - - --enable-leader-election - command: - - /manager - env: - - name: WATCH_NAMESPACE - value: "" - image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/mn_wallet_lrestbranch:latest - imagePullPolicy: Always - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - resources: - limits: - cpu: 400m - memory: 400Mi - requests: - cpu: 400m - memory: 400Mi - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - terminationGracePeriodSeconds: 10 - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert ---- diff --git a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.pippo b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.pippo deleted file mode 100644 index d1cc3eca..00000000 --- a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml.pippo +++ /dev/null @@ -1,5057 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-system ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomouscontainerdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousContainerDatabase - listKind: AutonomousContainerDatabaseList - plural: autonomouscontainerdatabases - shortNames: - - acd - - acds - singular: autonomouscontainerdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.displayName - name: DisplayName - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - SYNC - - RESTART - - TERMINATE - type: string - autonomousContainerDatabaseOCID: - type: string - autonomousExadataVMClusterOCID: - type: string - compartmentOCID: - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - hardLink: - default: false - type: boolean - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - patchModel: - enum: - - RELEASE_UPDATES - - RELEASE_UPDATE_REVISIONS - type: string - type: object - status: - properties: - lifecycleState: - type: string - timeCreated: - type: string - required: - - lifecycleState - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabasebackups.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseBackup - listKind: AutonomousDatabaseBackupList - plural: autonomousdatabasebackups - shortNames: - - adbbu - - adbbus - singular: autonomousdatabasebackup - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.dbDisplayName - name: DB DisplayName - type: string - - jsonPath: .status.type - name: Type - type: string - - jsonPath: .status.timeStarted - name: Started - type: string - - jsonPath: .status.timeEnded - name: Ended - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - autonomousDatabaseBackupOCID: - type: string - displayName: - type: string - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - target: - properties: - k8sADB: - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - type: object - status: - properties: - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - dbDisplayName: - type: string - dbName: - type: string - isAutomatic: - type: boolean - lifecycleState: - type: string - timeEnded: - type: string - timeStarted: - type: string - type: - type: string - required: - - autonomousDatabaseOCID - - compartmentOCID - - dbDisplayName - - dbName - - isAutomatic - - lifecycleState - - type - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabaserestores.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseRestore - listKind: AutonomousDatabaseRestoreList - plural: autonomousdatabaserestores - shortNames: - - adbr - - adbrs - singular: autonomousdatabaserestore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.displayName - name: DbDisplayName - type: string - - jsonPath: .status.dbName - name: DbName - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - source: - properties: - k8sADBBackup: - properties: - name: - type: string - type: object - pointInTime: - properties: - timestamp: - type: string - type: object - type: object - target: - properties: - k8sADB: - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - required: - - source - - target - type: object - status: - properties: - dbName: - type: string - displayName: - type: string - status: - type: string - timeAccepted: - type: string - timeEnded: - type: string - timeStarted: - type: string - workRequestOCID: - type: string - required: - - dbName - - displayName - - status - - workRequestOCID - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases - shortNames: - - adb - - adbs - singular: autonomousdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.details.displayName - name: Display Name - type: string - - jsonPath: .spec.details.dbName - name: Db Name - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .spec.details.isDedicated - name: Dedicated - type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .spec.details.dataStorageSizeInTBs - name: Storage (TB) - type: integer - - jsonPath: .spec.details.dbWorkload - name: Workload Type - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - details: - properties: - adminPassword: - properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - autonomousContainerDatabase: - properties: - k8sACD: - properties: - name: - type: string - type: object - ociACD: - properties: - ocid: - type: string - type: object - type: object - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbName: - type: string - dbVersion: - type: string - dbWorkload: - enum: - - OLTP - - DW - - AJD - - APEX - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - isAutoScalingEnabled: - type: boolean - isDedicated: - type: boolean - licenseModel: - enum: - - LICENSE_INCLUDED - - BRING_YOUR_OWN_LICENSE - type: string - lifecycleState: - type: string - networkAccess: - properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: - properties: - hostnamePrefix: - type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: - type: string - type: object - type: object - wallet: - properties: - name: - type: string - password: - properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - type: object - type: object - hardLink: - default: false - type: boolean - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - required: - - details - type: object - status: - properties: - allConnectionStrings: - items: - properties: - connectionStrings: - items: - properties: - connectionString: - type: string - tnsName: - type: string - type: object - type: array - tlsAuthentication: - type: string - required: - - connectionStrings - type: object - type: array - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - lifecycleState: - type: string - timeCreated: - type: string - walletExpiringDate: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: cdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - ordsImage: - type: string - ordsImagePullPolicy: - enum: - - Always - - Never - type: string - ordsImagePullSecret: - type: string - ordsPort: - type: integer - ordsPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: databaseobservers.observability.oracle.com -spec: - group: observability.oracle.com - names: - kind: DatabaseObserver - listKind: DatabaseObserverList - plural: databaseobservers - singular: databaseobserver - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - database: - properties: - dbConnectionString: - properties: - key: - type: string - secret: - type: string - type: object - dbPassword: - properties: - key: - type: string - secret: - type: string - vaultOCID: - type: string - vaultSecretName: - type: string - type: object - dbUser: - properties: - key: - type: string - secret: - type: string - type: object - dbWallet: - properties: - key: - type: string - secret: - type: string - type: object - type: object - exporter: - properties: - configuration: - properties: - configmap: - properties: - configmapName: - type: string - key: - type: string - type: object - type: object - image: - type: string - service: - properties: - port: - format: int32 - type: integer - type: object - type: object - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - prometheus: - properties: - labels: - additionalProperties: - type: string - type: object - port: - type: string - type: object - replicas: - format: int32 - type: integer - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - exporterConfig: - type: string - replicas: - type: integer - status: - type: string - required: - - conditions - - exporterConfig - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - properties: - clusterConnectString: - type: string - externalConnectString: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: dbcssystems.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DbcsSystem - listKind: DbcsSystemList - plural: dbcssystems - singular: dbcssystem - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - sshPublicKeys - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - ociConfigMap: - type: string - ociSecret: - type: string - required: - - ociConfigMap - type: object - status: - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbEdition: - type: string - dbInfo: - items: - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: lrests.database.oracle.com -spec: - group: database.oracle.com - names: - kind: LREST - listKind: LRESTList - plural: lrests - singular: lrest - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the LREST - jsonPath: .spec.cdbName - name: CDB NAME - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the LREST Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message if any - jsonPath: .status.msg - name: Message - type: string - - description: string of the tnsalias - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - lrestImage: - type: string - lrestImagePullPolicy: - enum: - - Always - - Never - type: string - lrestImagePullSecret: - type: string - lrestPort: - type: integer - lrestPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - nodeSelector: - additionalProperties: - type: string - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: lrpdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: LRPDB - listKind: LRPDBList - plural: lrpdbs - singular: lrpdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the LRPDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: last sqlcode - jsonPath: .status.sqlCode - name: last sqlcode - type: integer - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - - Alter - - Noaction - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbPass: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - alterSystem: - type: string - alterSystemParameter: - type: string - alterSystemValue: - type: string - asClone: - type: boolean - assertiveLrpdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - lrpdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - parameterScope: - type: string - pdbName: - type: string - pdbState: - enum: - - OPEN - - CLOSE - - ALTER - type: string - pdbconfigmap: - type: string - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - - alterSystemParameter - - alterSystemValue - - webServerPwd - type: object - status: - properties: - action: - type: string - alterSystem: - type: string - bitstat: - type: integer - bitstatstr: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - sqlCode: - type: integer - status: - type: boolean - totalSize: - type: string - required: - - phase - - sqlCode - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: oraclerestdataservices.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestDataService - listKind: OracleRestDataServiceList - plural: oraclerestdataservices - singular: oraclerestdataservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - apexPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - oracleService: - type: string - ordsPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - ordsUser: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - size: - type: string - storageClass: - type: string - volumeName: - type: string - type: object - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: - type: string - urlMapping: - type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - required: - - adminPassword - - databaseRef - - ordsPassword - type: object - status: - properties: - apexConfigured: - type: boolean - apexUrl: - type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: - type: string - databaseApiUrl: - type: string - databaseRef: - type: string - image: - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: string - ordsInstalled: - type: boolean - replicas: - type: integer - serviceIP: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: pdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - type: boolean - assertivePdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - type: string - pdbState: - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - type: object - status: - properties: - action: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - status: - type: boolean - totalSize: - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: shardingdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: ShardingDatabase - listKind: ShardingDatabaseList - plural: shardingdatabases - singular: shardingdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - gsm: - items: - properties: - directorName: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: boolean - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - items: - properties: - deployAs: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - gsm: - items: - properties: - directorName: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: boolean - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - items: - properties: - deployAs: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.edition - name: Edition - type: string - - jsonPath: .status.sid - name: Sid - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version - type: string - - jsonPath: .status.connectString - name: Connect Str - type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.tcpsConnectString - name: TCPS Connect Str - type: string - - jsonPath: .status.tcpsPdbConnectString - name: TCPS Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.oemExpressUrl - name: Oem Express Url - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - archiveLog: - type: boolean - charset: - type: string - createAs: - enum: - - primary - - standby - - clone - type: string - dgBrokerConfigured: - type: boolean - edition: - enum: - - standard - - enterprise - - express - - free - type: string - enableTCPS: - type: boolean - flashBack: - type: boolean - forceLog: - type: boolean - image: - properties: - prebuiltDB: - type: boolean - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - listenerPort: - type: integer - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - pdbName: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - primaryDatabaseRef: - type: string - readinessCheckPeriod: - type: integer - replicas: - type: integer - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - sid: - maxLength: 12 - pattern: ^[a-zA-Z0-9]+$ - type: string - tcpsCertRenewInterval: - type: string - tcpsListenerPort: - type: integer - tcpsTlsSecret: - type: string - required: - - image - type: object - status: - properties: - apexInstalled: - type: boolean - archiveLog: - type: string - certCreationTimestamp: - type: string - certRenewInterval: - type: string - charset: - type: string - clientWalletLoc: - type: string - clusterConnectString: - type: string - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - connectString: - type: string - createdAs: - type: string - datafilesCreated: - default: "false" - type: string - datafilesPatched: - default: "false" - type: string - dgBrokerConfigured: - type: boolean - edition: - type: string - flashBack: - type: string - forceLog: - type: string - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - initPgaSize: - type: integer - initSgaSize: - type: integer - isTcpsEnabled: - default: false - type: boolean - nodes: - items: - type: string - type: array - oemExpressUrl: - type: string - ordsReference: - type: string - pdbConnectString: - type: string - pdbName: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - prebuiltDB: - type: boolean - primaryDatabase: - type: string - releaseUpdate: - type: string - replicas: - type: integer - role: - type: string - sid: - type: string - standbyDatabases: - additionalProperties: - type: string - type: object - status: - type: string - tcpsConnectString: - type: string - tcpsPdbConnectString: - type: string - tcpsTlsSecret: - default: "" - type: string - required: - - isTcpsEnabled - - persistence - - tcpsTlsSecret - type: object - type: object - served: true - storage: true - subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.replicas - status: {} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: oracle-database-operator-leader-election-role - namespace: oracle-database-operator-system -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - - deployments - - events - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list -- apiGroups: - - '''''' - resources: - - statefulsets/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - configmaps - - deployments - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - replicasets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - list - - update -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods/exec - verbs: - - create -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases/status - verbs: - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - cdbs/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - events - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrests - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrests/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - lrests/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - lrpdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrpdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - lrpdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - pdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - monitoring.coreos.com - resources: - - prometheusrules - - servicemonitors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/finalizers - verbs: - - update -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/status - verbs: - - get - - patch - - update -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-oracle-database-operator-proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: oracle-database-operator-leader-election-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-oracle-database-operator-proxy-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager-metrics-service - namespace: oracle-database-operator-system -spec: - ports: - - name: https - port: 8443 - targetPort: https - selector: - control-plane: controller-manager ---- -apiVersion: v1 -kind: Service -metadata: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - control-plane: controller-manager ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: oracle-database-operator-serving-cert - namespace: oracle-database-operator-system -spec: - dnsNames: - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local - issuerRef: - kind: Issuer - name: oracle-database-operator-selfsigned-issuer - secretName: webhook-server-cert ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: oracle-database-operator-selfsigned-issuer - namespace: oracle-database-operator-system -spec: - selfSigned: {} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-mutating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: mdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: moraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: msingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: mcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-lrest - failurePolicy: Fail - name: mlrest.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrests - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-lrpdb - failurePolicy: Fail - name: mlrpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrpdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: mpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-validating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase - failurePolicy: Fail - name: vautonomouscontainerdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomouscontainerdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: vautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: vautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore - failurePolicy: Fail - name: vautonomousdatabaserestore.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabaserestores - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: vdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: voraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: vsingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: vcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-lrest - failurePolicy: Fail - name: vlrest.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrests - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-lrpdb - failurePolicy: Fail - name: vlrpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrpdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: vpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager - namespace: oracle-database-operator-system -spec: - replicas: 3 - selector: - matchLabels: - control-plane: controller-manager - template: - metadata: - labels: - control-plane: controller-manager - spec: - containers: - - args: - - --enable-leader-election - command: - - /manager - env: - - name: WATCH_NAMESPACE - value: "" - image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/mn_wallet_lrestbranch:latest - imagePullPolicy: Always - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - resources: - limits: - cpu: 400m - memory: 400Mi - requests: - cpu: 400m - memory: 400Mi - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - terminationGracePeriodSeconds: 10 - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert ---- diff --git a/docs/multitenant/lrest-based/usecase/parameters.txt b/docs/multitenant/lrest-based/usecase/parameters.txt index 5b8ac4b2..1f21ed38 100644 --- a/docs/multitenant/lrest-based/usecase/parameters.txt +++ b/docs/multitenant/lrest-based/usecase/parameters.txt @@ -3,8 +3,7 @@ ## REST SERVER IMAGE ### ######################## -#LRESTIMG:container-registry-admin.oraclecorp.com/database/operator:lrest-240910-amd64 -LRESTIMG:lin.ocir.io/intsanjaysingh/mmalvezz/testppr/lrest-dboper:latest +LRESTIMG:container-registry.oracle.com/database/operator:lrest-241210-amd64 ############################## ## TNS URL FOR CDB CREATION ## diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/altersystem_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/altersystem_lrpdb1_resource.yaml deleted file mode 100644 index a02514eb..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/altersystem_lrpdb1_resource.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "lrpdb-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "lrpdb-secret" - key: "sysadmin_pwd" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Alter" - alterSystemParameter : "cpu_count" - alterSystemValue : "3" - parameterScope : "memory" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.crt b/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.crt deleted file mode 100644 index da6ca98b..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDjzCCAnegAwIBAgIUX4OIYiqRwA+e0JPXV8iaW7h3f2swDQYJKoZIhvcNAQEL -BQAwVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEVMBMG -A1UECgwMb3JhY2xlLCBJbmMuMRcwFQYDVQQDDA5vcmFjbGUgUm9vdCBDQTAeFw0y -NDEwMjQyMDQyNTRaFw0yNTEwMjQyMDQyNTRaMFcxCzAJBgNVBAYTAkNOMQswCQYD -VQQIDAJHRDELMAkGA1UEBwwCU1oxFTATBgNVBAoMDG9yYWNsZSwgSW5jLjEXMBUG -A1UEAwwOb3JhY2xlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQDoLJRupBvETN4JwO+zs9gUChtn0GBsr5DhfqY9Hs2FC1Z5wVzw5KTFKxPo -F7aXH/Zz4B+JiRLvaYv1AsfnFl3YsaVQ+8KMfaVNiRiSy2uSfRGXr9MYD/RR2eza -0hc9necBoUqfqVynWby+lVIVYXY5wcuyYpSDPvYuCGs+MiuUTDuQb4UEiSEc/ccr -mj7TaWCyqJGQ+ilm3Fjgfv2qFvekavtaUKrvDYIefz0FdlRWzLd4qEyWScxiGs4r -+TIRUsy5YVUqf+KzxO3p2m0gYrmv+EQvRyFuHtiGeinUCvx9ISTdTwCPt4YRviSC -HdhKO4qaUjG6m0Vcq97Po+5T0qeRAgMBAAGjUzBRMB0GA1UdDgQWBBQfkTdjH0v0 -Rc+tlhLvR0PIAGVRtjAfBgNVHSMEGDAWgBQfkTdjH0v0Rc+tlhLvR0PIAGVRtjAP -BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCkanIZUAAELgA6WC/O -f6Rmn6GDaI1/0qI1/lSlt0DgG2JCd5rz6yyOnMbDBiFDRKi00MvQpv6ksI8VCVFP -pr/L1A7ueLD990pZ8h/gMr2mMkQ5jNvwnrgph+dxwiXohSi+38P3JLrZmsnwmbBo -fKHN/iz7xNTPsaKtVHqRX1oSp22aORIFTpOvuHVUmQA8OGuBONnbL+v9sWzqeguq -Tz/EK6GJhUc1C4QHdGXhUivU/+CF2A2pDoaiB9BNbyzfKUxgh8ihMlRqZqMPEajb -e25mVoe+gAnAFC78qlSkdSS0qG6U+iFh4ClKE3CYiFhlQLfPuJOYBI5Frd2I8BTp -OfRV ------END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.key b/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.key deleted file mode 100644 index 62b39c1d..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDoLJRupBvETN4J -wO+zs9gUChtn0GBsr5DhfqY9Hs2FC1Z5wVzw5KTFKxPoF7aXH/Zz4B+JiRLvaYv1 -AsfnFl3YsaVQ+8KMfaVNiRiSy2uSfRGXr9MYD/RR2eza0hc9necBoUqfqVynWby+ -lVIVYXY5wcuyYpSDPvYuCGs+MiuUTDuQb4UEiSEc/ccrmj7TaWCyqJGQ+ilm3Fjg -fv2qFvekavtaUKrvDYIefz0FdlRWzLd4qEyWScxiGs4r+TIRUsy5YVUqf+KzxO3p -2m0gYrmv+EQvRyFuHtiGeinUCvx9ISTdTwCPt4YRviSCHdhKO4qaUjG6m0Vcq97P -o+5T0qeRAgMBAAECggEADOflfFz8+iV3tj0WVfvqR/rj2Rp5MbF0sUQ0A3PB4uJl -dTEtdGqRSjCB08X1rWLmdTyBJEkNV2ebkwwLhqn0n4YIdX75KowVc0Dqr8ZE/Yic -QL1SU6UXZLFjfCpvhkpPvAR2PHfSoa7YUhPE01Fum/lM1GascLtAPgDTzUpGp+CV -1U3e8mioRb8WuUfF9NygCJ/aYomAE82JJg37dWe3gsRjdauN2b9aXXxH8bHP56Qf -dH+07b/Hqf3+oQL+ND3APcfIlvjE5SitfRFGy+c3JHagajgiDRhwnmx16EuoT4XX -craYyvJF+ddflg+cpfIMNSeWRseqBhMnYsJ8uG6UfQKBgQD7J+0f39CovWodjwY3 -hDEr+F9xC5ShZJPN/RUYXT1NoRRzDzgcU0K6O3etfh9LlSpjp0MYV0qKiLmJ173Q -TVegDdxl3bbvwaslR9yTpZtjejFh7I5taTogTxcijswvIyT0E5CE+LJtvVZ/ydQz -JRBzipBWSkvJx0NlvXcKzEdmmwKBgQDspu50lWTehSWVfOvIBS66BAI44MIJN6WV -8yU0QigX4lE+/uud6BfKbY9doy9J3c+lXBmzwd0TfF03v/wxQvcilXPDL7zNJQHq -hYxMyG2YtbAN1wbTjhmDhan6x31NKcSbj+hCR7+sCGqilMlZbF/+c8S1QV+EvL9v -3xjB3/63QwKBgBTY+5XpvihDkMtjU+DH2E0OEN4RWAUNlDdFCrs7Ahuvg0MJwF6Z -irBqv0Rnc7GScDJVb/xVQpq9Pqlyfw8LXHqsq97IyKaeLNarYGPhG0Tmn7LBMBFk -6q5D4me78rb289pOxXBENItnbAavei3EpdjxZ4nCIQhewflwALGkw0iBAoGBAOFL -qwhikb0mlTlTzESIaou89UCg+Rk9WO8ApBK0SOc0FpUaq8WRB9U3qJ1EleiEuwJ6 -RB3WGlpKs5Q3uihL0GukTKkt8Vh8Ou7CZWdUCMKxdgI3C9CsH64Oulx9PtUPRpWy -0zMGi3uEJ8DQVUBE5L6iCEtnCUCdUMN4aWFpll1xAoGAQHebcuIuadxYny1YtgGx -J3/+KK8+Jtuv3UCo6IHHL+pi56VxN/1c4pt/YQnaB3JThaNrkNNTGy2TxCrv3lCN -B9rjaIG4gUpDbyutwJHK3HWZ1O37GiqdmuZZu7VjGIR4CBDQOTdLA2X5tkOfSbEr -pBveqS1C0qdisUTqJXq29fI= ------END PRIVATE KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.srl b/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.srl deleted file mode 100644 index 39f44174..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/ca.srl +++ /dev/null @@ -1 +0,0 @@ -1DED5A4E240ECAD1381AB6983C1CAA2D456889BB diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cdbnamespace_binding.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/cdbnamespace_binding.yaml deleted file mode 100644 index c7bc41e5..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cdbnamespace_binding.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: cdbnamespace -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.crt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.crt deleted file mode 100644 index f3d2474f..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDjTCCAnWgAwIBAgIUEyqDlUDop1mB0XE7Ch6SloDwCfMwDQYJKoZIhvcNAQEL -BQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEUMBIG -A1UECgwLb3JhY2xlLEluYy4xFzAVBgNVBAMMDm9yYWNsZSBSb290IENBMB4XDTI0 -MDkwMzIyMjcwM1oXDTI1MDkwMzIyMjcwM1owVjELMAkGA1UEBhMCQ04xCzAJBgNV -BAgMAkdEMQswCQYDVQQHDAJTWjEUMBIGA1UECgwLb3JhY2xlLEluYy4xFzAVBgNV -BAMMDm9yYWNsZSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAnltrUl+ARDT2aP+23XVNbpSEGN0p8jSGe852TsRQhjfffGtOtomiSMnmk9by -4IpYDjuWTUbnkjaaALxnLYVBJnIyEnqsUfSw2X0JT1QF3tZsh7C3tTNTY9vE2VTd -oFiKbahHuv+t5eGnxV8RHD3g0iNi7BlmTeI7XhbucNDgXU/eDQfIPNaFVGQEBqhk -H7mqTkMUjgq3a0UGqiEXhvUY8ytyskjHg7uDO8MgN371OtubGlyV2uQp6mqigKHl -98oY1Zl/39Panrg2NXEr+UIVtmnxi+LHdoNf0lICAnC9Kycd/5YxdvQi+Ztm+OK5 -vyTI77OMCqNPZWsdB2EcGfr+XwIDAQABo1MwUTAdBgNVHQ4EFgQUGg6a21+j3SMq -zI84cwBZFuZ7LGAwHwYDVR0jBBgwFoAUGg6a21+j3SMqzI84cwBZFuZ7LGAwDwYD -VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAZZq2tQo+bFfr/UThuoBK -TBel6nG9La0lriUWu4InB5jTWDw8p+6RGgib0ngWLHjnG9TnyHJts7bcG64f9HWA -87WpuI+1+XXkEF6pMr5j/00nCRv5gD5RtwjJ/4s6SxOqRljsIt4fClmPRU4B1ZgS -sf0vp0vtLHr9kyPdKylprUfj0BVDXh4+mZHQjEeyJp1BxnUEBPpfQ7BJL2iirOhK -z/xhCug/MF2R5/Ewcf4LYUlb0LT5pJWf7gIEq4ADkGVqH4XH2V1WREfeO3hadKmk -iTBR7cL8RP7zPT/3ik7xwnqukIDw+EvHmeJKG8D2yE0KlbmPLAy6RXjPqt+RhC2v -PQ== ------END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.key b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.key deleted file mode 100644 index bf22e1b0..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAnltrUl+ARDT2aP+23XVNbpSEGN0p8jSGe852TsRQhjfffGtO -tomiSMnmk9by4IpYDjuWTUbnkjaaALxnLYVBJnIyEnqsUfSw2X0JT1QF3tZsh7C3 -tTNTY9vE2VTdoFiKbahHuv+t5eGnxV8RHD3g0iNi7BlmTeI7XhbucNDgXU/eDQfI -PNaFVGQEBqhkH7mqTkMUjgq3a0UGqiEXhvUY8ytyskjHg7uDO8MgN371OtubGlyV -2uQp6mqigKHl98oY1Zl/39Panrg2NXEr+UIVtmnxi+LHdoNf0lICAnC9Kycd/5Yx -dvQi+Ztm+OK5vyTI77OMCqNPZWsdB2EcGfr+XwIDAQABAoIBAA75TAOk/ib07xvs -mikEr4a2SmtjdlTtvI22bx9xlr+qRtqn9vBxwUOqu7/dAmhTDJKD9elmu/zqZtOM -/SCjyg5NnAh9SxAQ2Cwok5nJsyhoFCkcf7KFoOv/WvTF/pLr7GMEF4xKIvJqzQdv -vtlxzHFyYSc8G51fuAdkmbVXOu/3uEyPbo/1dWBovhxL3UMh2oyzKDLL2wi83Fev -5MQ1O8saK37chAfWDZvKWgJmlOHcJpQDu7HR5F+ynwbwRnIyMQyBuzYhQrWBWR74 -2p9HcoDVsnad+3FnaKlzMn0HsFVGy7R/ZvTHhh8V90jq/QXxO9TSLvxRCBujQ+V5 -VoutxsECgYEAzX7gdkUOjscEbT65+CEr7Dp1vflsbFZBwu++iQNKGJzoZswNym7e -eXBdSeDf9aE067dZVqxZv3TtH/Uto5QyNeov54VTaoFkFVKG55H6c+MzDmz2ieJ/ -B209JWDyAEfRJNQlqzqllKCgpf7ns1rD3dev1nOVM0riUVsJfrIuxiECgYEAxUa9 -OvEhGM3QA6HNjpzPBqL+6i8ocSVLGZGkrP9Et4jJMpsWJO5w/lEUYbAy4K6Ngk91 -xWBLnb3y4kdnUDVhzRQx4RC+SUBQ6/Rj8V09ENBwNq220uYl4vSZbd5WfJG+Hgw0 -W5iIb5ZngjCKSqO87P3n38ndt8ZJhwa4MpOINH8CgYApwARUoxUhFvgkReE8s2ic -FwiC72EC5cd4loLsjVmJhJ+G+yA2J65Vl+msL97wpa790QN6o4ucZWIj4wlBucM1 -xv2ymRjog59U4a587+ClmNxvqt2wB+9tD9mjdX3VZsHsLJn2nvSzLKtW7mIew2sV -8FNOS69de3JduxRDxGnSYQKBgQCqxNhrnG31BbwqWLcS26TObuxRli7g+tJuuMef -RX2bjpnz55IcuBcx3sfQ5HC2VAOFKPmeuQPb8aC9xeel4//cQb4HwLGnwTgLeMvH -FNoWowhwHKHlIHrtYqUqGUwumw7C/feYzopUZhMF/LTJdcHGOOiurRTzz364ltV+ -Wn2OSwKBgB9guT+voiLrgR/OQWFINotqnl1t4etex+vbUylDX7jFKeNQPJTSeP2Q -gHidHMw5PUKO5bDLitkR61xQAj+U+X+eYcKWvoJBsYdoYUPNGrCPuDI+frleQTBk -0yjH7AoOas7O/PIZKyfi2F2POtZCgfZK1IEizo2ZDat+RHvHYV7A ------END RSA PRIVATE KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.srl b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.srl deleted file mode 100644 index a85bf66f..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/ca.srl +++ /dev/null @@ -1 +0,0 @@ -42A91AD49BA9B8D71512F5E0A9397015B2BDD883 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbpass.txt deleted file mode 100644 index eab39d3f..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbpass.txt +++ /dev/null @@ -1 +0,0 @@ -CLWKO655321 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbuser.txt deleted file mode 100644 index 7028cc36..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/dbuser.txt +++ /dev/null @@ -1 +0,0 @@ -restdba diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbpass.txt deleted file mode 100644 index 8e1be181..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbpass.txt +++ /dev/null @@ -1,5 +0,0 @@ -MPGBdow5yQfh3/2Sud1qYMY75vI7Qlt0RkhbM+Dun3E76E9CGMJorFzIu/5FZi1fCGEYHWiMMygx -BR3m4Y7gE/PWETYOfbpI5efuPsOCQoFYPQCGce/Dfwd88RJTnp8RHgW1x2zwhH3aOJADsRQ7BChg -/oeb1naFig4Sznj4FbJ0ExdW+kIWjC57iDfX1osa+NC8OhMexBsKoTO0nAL6+6ji+ZEyoRrnvzT+ -hevdHjeIGTerZabiX/9kI0T3SJWpc2p3xH5985kEpToNE73l5nxAkRsfjp+KSwAgbWnKs/TPSeCn -l2J//Fjl4hNZwmTagR1qFLG0R7mbN+hT7Q6VJA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbuser.txt deleted file mode 100644 index 0f9a6c2d..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_dbuser.txt +++ /dev/null @@ -1,5 +0,0 @@ -N69kAA0Im0ObwAeXmeou+5J8hC4OaBqM7q38yjBMiIcgN8csNv7ZaD3csA5ZNqGUi5W/XtcOjgWN -4OtY8vvV1B1ATiGvZmaS4F9zKd8GAP9wVHq+BXef+H27xFZEhCC6QxDBThcUMH4dihR/sQFayU1p -gPcXD3X6VhMLSZLb4073kyaEEFyiZr3GRZkqXuFFvHCGfIpWDhlVptngUT4Qmk8xQXzbpqi5vzH5 -L40KNEH9BX+lRcbBeHk61MJLBn/HTeLuE24QOCpOT9+pyStEv0Dn8F8WlVhm/9TDuzBNETF6Or1N -qij6nPQOmswhF0KyFUtMoKM7HXhc3g+8tENKlw== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbpwd.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbpwd.txt deleted file mode 100644 index 68b13f5a..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbpwd.txt +++ /dev/null @@ -1,5 +0,0 @@ -gWoKcUSAAuNO8LkFaYjZhTQfmVGDiiWS7Ux2x55lFxn4qXeoXDA1f2hUeqPrUtoQFIfVSch7E94Y -jgOiRSBcFy41bkJVo8lPklWqvrF1/00CXhnkf+JcO6bLA1KAgjPxbNP9X9B8cCDX8Q36C276DDIT -0IEZ0bq0olAUNTyMNcN5kxIZz+vs8z4cZ49gXg4owtlYA1idqdFVjRC/aHvd0aTGtoFXXNJDAQq+ -JZuZgT2QBb5Pe8hH088qy4GWIOLRSYZbeaWY6ovLOD9NxLv61x8XXvQtyCJEaEjOUREXDtalv3fL -6DMu0dreLZYn61z7qquzmjsQGlAMGO+CTUWgsA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbusr.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbusr.txt deleted file mode 100644 index 5bf7219d..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_pdbusr.txt +++ /dev/null @@ -1,5 +0,0 @@ -Aax80Z0EqZfy7giPro/RkQMBRweQNdFOQVgdCphBrgvnMTW1ugV7aj1dDBPIuuIXwmM0yJbPMMMp -gITIfT5ZqKwA2jY/GH35C5Aq1w+fvsrsZqHZOa6QZFfjBqZCA+CHcS4D1bMyFlIGj3SqgWdXPjWg -QMLhAvgjml1gDpBrRi0Hno02xI35X1VBZT8vMIr0dQii1hITcTeBS0r7zud6Ablrm+S5AMuqfJ22 -WfazFIj1FHZshHKjpRX4/FJaCQQWe4j7p4/THZ5uKBJgd9AG2wEztPdcu3brIAatkjhIRsSgVOB4 -T0PGK5rl/r079gKyHNdQwtmOs6uZLgIuggTVXA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbpass.txt deleted file mode 100644 index 8d8361fc..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbpass.txt +++ /dev/null @@ -1,5 +0,0 @@ -coxJfZQxjw7Fe4fbtrh62zyuD74pgF82nihiztEigHLKz2mY3lP8UuDd8gOOGM4NHEDemPDsAYND -NITal0csGSAWrLHzVDqShxsh87QfQlkY5VF78/zZmAgIJ9BXcH7V4e+QFgqyt+oxa5+r6FY7NmIf -LaGnC3DsdER1jVZxAPu9lL80Oe9Y/JSUfnzWygwhTgKrOMppdCU+jJNfwM+wOvRY1KDPntFvh089 -hsTrrzbARBQFEkDFToMzhCAo1NczNbEQvvPJB+lsL1e9Q+4W/bkweMa6zLLKAH6Gr8qVN5HuObRB -UskCSHgpVG01IoHbkFmZQLQ/2xuzFDr1gIESlg== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbuser.txt deleted file mode 100644 index aa3f50cb..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/e_wbuser.txt +++ /dev/null @@ -1,5 +0,0 @@ -Mzq6FD10db2l7B0PUsGcYYduQeqiVSrnk9h3n19oc4LKdC76Ikmtwk/1yIveM4zRyiHrZAJufxOK -OeUgctjR/M1lWlkLPcbcPL4avIbnLRiEJyp18vCwF2nKf+ZO0+92zlob78x1kIuC7s4JT70ooxJd -DLq2OaT34cry7EZePOoLXt+2BE/8eaPRyITTge/ccdgIPGWiD+35X0YhwgDcuirD5BBMculLfovp -uZh2We2eVDFXUYRB0kzajN52DW0YdH4CPgZmAvIkxJW4hOW8nrReFsu2f4r4wLnDdCeFJeX/xVaR -qqls/6NndmpXQ5MoUBK1F1SRNVVxZEUJ65a4yw== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/extfile.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/extfile.txt deleted file mode 100644 index 042aa390..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/extfile.txt +++ /dev/null @@ -1 +0,0 @@ -subjectAltName=DNS:cdb-dev-lrest.cdbnamespace,DNS:www.example.com diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt deleted file mode 100644 index 3a6fa68c..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDgDCCAmigAwIBAgIUQqka1JupuNcVEvXgqTlwFbK92IMwDQYJKoZIhvcNAQEL -BQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEUMBIG -A1UECgwLb3JhY2xlLEluYy4xFzAVBgNVBAMMDm9yYWNsZSBSb290IENBMB4XDTI0 -MDkwMzIyMjcwOVoXDTI1MDkwMzIyMjcwOVowYjELMAkGA1UEBhMCQ04xCzAJBgNV -BAgMAkdEMQswCQYDVQQHDAJTWjEUMBIGA1UECgwLb3JhY2xlLEluYy4xIzAhBgNV -BAMMGmNkYi1kZXYtbHJlc3QuY2RibmFtZXNwYWNlMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAwixJqBXGeQLcVDiZ6ztNw4/qmPHhEaPXb2Ce3jyHgxrO -CKjeXnuXoJEQFPm2n8kV+g1ie2EI04YM+NtERLZGE+yfTbtsP0Uw3ZqR238To9ID -wYF3by+pShC4r64kVtJ75DQYd4khOE5cXj2UpjsiWzln7tdZgHh2QBasSvTGGNdt -CUXkvWPDMyM7GFdnLBtmUxEKKMKkwqAGCmqfxwiWdrniDEcWnJH02QWk/Yp9aiyT -TAz1PzHDNo/X8XX6oaCXle0Qlamx/WrNkXg7jWAMavPgU+CampBiL1xMkoIV2MpH -oabWpiLYLwZjkKHzyRw08Mt0NPbOWOBOP1/1sjomOQIDAQABozowODA2BgNVHREE -LzAtghpjZGItZGV2LWxyZXN0LmNkYm5hbWVzcGFjZYIPd3d3LmV4YW1wbGUuY29t -MA0GCSqGSIb3DQEBCwUAA4IBAQBeeyT+f2vp6tAR1DywiW3KuVdCARTwEbbLYOVx -MeRZkaGr1u/fKLnds6axWwsoKque1H1nCIaPH/9YKbNBtldxXKxS+TH7NcvJC3uE -TF96YBcUjteBn0pM1hUFCtadSwa8o3U7QfZkr06JUQm4RRcv+mspwIFHcrow2Us5 -SijbOCaZOJ9LkSRORo4NeJQwLyH29+IenRxKkjsawuIBXdmHWD/AMRhRrPT1sIXg -Mj8xnXgaQH3O8PhwlAqujzWqJAGYi0wQbCSrktgCBBY1DjLMfpYyJVF9IGC67v66 -QKuKusKSEkk80pHsQjLpXCGba1nnsgSL13TZ/ciaejwC1u+Y ------END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt.orig b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt.orig deleted file mode 100644 index 0a1d1c2d..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.crt.orig +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDgDCCAmigAwIBAgIUQqka1JupuNcVEvXgqTlwFbK92IIwDQYJKoZIhvcNAQEL -BQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEUMBIG -A1UECgwLb3JhY2xlLEluYy4xFzAVBgNVBAMMDm9yYWNsZSBSb290IENBMB4XDTI0 -MDkwMzIxMjY1OFoXDTI1MDkwMzIxMjY1OFowYjELMAkGA1UEBhMCQ04xCzAJBgNV -BAgMAkdEMQswCQYDVQQHDAJTWjEUMBIGA1UECgwLb3JhY2xlLEluYy4xIzAhBgNV -BAMMGmNkYi1kZXYtbHJlc3QuY2RibmFtZXNwYWNlMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA3pC9MX99DkhF7npyoQK6uc/aN0HwGueOvtsOs0Cm4aPW -scxIy1UXrFUm62y4e3AILVEe32A8hqGwLFKsorWIolxNRnl2mzQnabMkeQOI+gq0 -sFWR598hlJ8kShWzA1n8b3XhVsz4T7Rf7WIa1cW0GWu+sS7h+s/4gDk3lCgUnqk6 -Xg3ohmHy4Plv6PuwgcHcFpFwOYlhzc2/6DsPe6+fCpSOZH7p6KwY+mdoOi8eCg4D -oawgyDS808qo0pjNqpChM6TsxQnkKvAHFDZkF2FK+YmV0DmCv2y7hL2LLFuSn5Gv -RIHwp+X6F1Ti9AqE0re+jNA2me0oxwrSwZJKm8yZaQIDAQABozowODA2BgNVHREE -LzAtghpjZGItZGV2LWxyZXN0LmNkYm5hbWVzcGFjZYIPd3d3LmV4YW1wbGUuY29t -MA0GCSqGSIb3DQEBCwUAA4IBAQCQjHYjG4BAXBMq5trx52eNsfg8q54gWh2KNOYw -pJEm9QxAG+FI41iMFLP++ixpMklyQEP0cFp7nG3BVjQiCKdjDTxBejhyRtXPF64+ -VEMPSwvj0CWbFi0ZKR66Q6cT9FAsSlP9vKlr6gAaqcTPZwpdg3z7ZDwdSBM1v1Uq -4+06mTZsgp/+j0x6o+uaeDSpUj8HyJMTz9VitMVEntkg4CTdu9qgivPIfKQLd9co -nZbSbEeXjqz70nMSxn+65gs1Ot4c0HRKvL9lRixUKvDPE7bcYVBpWJgq1Fh8xaBp -eRWxSCeWRRJKWB+eUzA36gGKFBwnGko44fHDaQij4y2s6EJg ------END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.csr b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.csr deleted file mode 100644 index 7ce997ba..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.csr +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIICpzCCAY8CAQAwYjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQH -DAJTWjEUMBIGA1UECgwLb3JhY2xlLEluYy4xIzAhBgNVBAMMGmNkYi1kZXYtbHJl -c3QuY2RibmFtZXNwYWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -wixJqBXGeQLcVDiZ6ztNw4/qmPHhEaPXb2Ce3jyHgxrOCKjeXnuXoJEQFPm2n8kV -+g1ie2EI04YM+NtERLZGE+yfTbtsP0Uw3ZqR238To9IDwYF3by+pShC4r64kVtJ7 -5DQYd4khOE5cXj2UpjsiWzln7tdZgHh2QBasSvTGGNdtCUXkvWPDMyM7GFdnLBtm -UxEKKMKkwqAGCmqfxwiWdrniDEcWnJH02QWk/Yp9aiyTTAz1PzHDNo/X8XX6oaCX -le0Qlamx/WrNkXg7jWAMavPgU+CampBiL1xMkoIV2MpHoabWpiLYLwZjkKHzyRw0 -8Mt0NPbOWOBOP1/1sjomOQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBABINdvR5 -LBhtcnS53cPxV6yBjSZNnUXDKt6KYOPtHKDo1Gzkhpb9p/3mPVppY4s9xhNwqkEv -RJseqNCmCjvdi+bWesidqHmFw0eh++5MuGWjVQ3qlV8vepBJ+pRy/9RG2fe3aFuc -enoSR0ZpYZR5sNW1PfcwooY/Bhu6GY0YGapHH8m8lezZ+LSWIHnVBEfO5mC9l0ON -y5B8ehiTIZ+SkkKe1ByriucUuz/RO/zSjzNo1JS4y4F07M+nG2cwr+HjC1RMxYMJ -t2hIo3Xa3f9515ciaItDCsBQp0WbGLVfSoRVKmuY+/Z3lqaxp7ejOwVBxwTsViEX -1pIx9DOMqgnXg6s= ------END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.key b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.key deleted file mode 100644 index 96511485..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/lrest_server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDCLEmoFcZ5AtxU -OJnrO03Dj+qY8eERo9dvYJ7ePIeDGs4IqN5ee5egkRAU+bafyRX6DWJ7YQjThgz4 -20REtkYT7J9Nu2w/RTDdmpHbfxOj0gPBgXdvL6lKELivriRW0nvkNBh3iSE4Tlxe -PZSmOyJbOWfu11mAeHZAFqxK9MYY120JReS9Y8MzIzsYV2csG2ZTEQoowqTCoAYK -ap/HCJZ2ueIMRxackfTZBaT9in1qLJNMDPU/McM2j9fxdfqhoJeV7RCVqbH9as2R -eDuNYAxq8+BT4JqakGIvXEySghXYykehptamItgvBmOQofPJHDTwy3Q09s5Y4E4/ -X/WyOiY5AgMBAAECggEAcnoF2vErmECVgZn1NPg7uqZom09PcET2SJXgVAm/ow30 -lqpon6+iD2o23wbyInY9XLTzcOYefAa2MymisBf+bNS+fueWxB6Y1q3AfHYJMDYr -PLSFo6mn1yDQodK7XeyuWjZUjnTsfvPEBVOtBewrLzlDC08vV0yt+s7jOTXibNXd -z9JxaeXtunwErNSVngTQSAJLDQ2UQ5bwPO/yvpbI2qFcCfINcVZT8l42e4eSxuyE -Km1PKBsUtfxBtx1WHjTDdZ9fs4mjRmfUywQSfk2qv5VvjCk4IJfuB7OAEWW0ahbl -Kr0rfYza39/R4gwskjZEECWWYemsbl/atbDku62iAQKBgQDu5FGTT+j/2NqQ3WGy -P36YBJVZXblM6jrQ9mgMVCZaEgh0F8CpydALWmpVWzjf/Vhlr9IrBXvmjfMEMrbP -yjn7ltUvjA7sZi4l9komXYgCpBSkyqnQTMJ/h7FQsuMGxUPFtO44xiaOBbDFgZjv -AUuHhJ+D/gz/j6g7DIYcn5gZmQKBgQDQFB+nkku15mP3z3vzo2IuOgGZqSztWdM7 -tF/4+dZM3lMQMbtXWNx1gI21ruua6tbVnbQ6Cv+SrvC1kCKizr3v37+oMnL8gONY -zxRJMgQaz0JAVendd2Y+WxDY7/mNd7WHGFeGC2TntqQDXXSZ4WkybBFkhgyTnntl -rVkEVYyVoQKBgGEMYBd4Xy+Q6Tnwtb+0C7m2IvYrHRwiC8LYV2yqwunUaZrAC4Tu -d5YlJQ2IAZL9WOv2gWr4z1zaTd9mut2vSd7rZ6/pNibTW+wQIg9z780i20AXFOWK -SXmFpoMiKRiXCvFGj4CA/yizJx0ViQuPex+SwTrd+mnX8oAKlnEKZMABAoGALd9B -2z91UXSpNUiYmu26B1biY23YabsTxmXNgqYRiIC67ycHlgoREBZ3J0aYyl5bXlyW -DvW9wiivCFfuStYpKWi6Z+o3T1Nay4lbf4d3QDHQ+T33gMuBzt1kqW6+JWHuswm6 -weJhLHQnVFaWDhaTinoom3Cx9RlLOu8sYLg7dgECgYEAmSi1XwwOHiWuK9jiHcYZ -fzXfI+nslkSlaqNOZ89LUr8wlubODtqLlKc3ADQ0NSkJsAup9ufkQqke2eCjlvsx -4OoOKlu9Ag/ua2nC63cLEQM+2pVjWCrL16dMjQR+9fg/prkQrWPcle6xPd5V4cnw -y9/n1cN5/8uBeKviJ7cNzVk= ------END PRIVATE KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbpwd.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbpwd.txt deleted file mode 100644 index 264f6ba0..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbpwd.txt +++ /dev/null @@ -1 +0,0 @@ -welcome1 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbusr.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbusr.txt deleted file mode 100644 index 97ce6cbb..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/pdbusr.txt +++ /dev/null @@ -1 +0,0 @@ -welcome diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/public.pem b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/public.pem deleted file mode 100644 index 63eac5c4..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/public.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnltrUl+ARDT2aP+23XVN -bpSEGN0p8jSGe852TsRQhjfffGtOtomiSMnmk9by4IpYDjuWTUbnkjaaALxnLYVB -JnIyEnqsUfSw2X0JT1QF3tZsh7C3tTNTY9vE2VTdoFiKbahHuv+t5eGnxV8RHD3g -0iNi7BlmTeI7XhbucNDgXU/eDQfIPNaFVGQEBqhkH7mqTkMUjgq3a0UGqiEXhvUY -8ytyskjHg7uDO8MgN371OtubGlyV2uQp6mqigKHl98oY1Zl/39Panrg2NXEr+UIV -tmnxi+LHdoNf0lICAnC9Kycd/5YxdvQi+Ztm+OK5vyTI77OMCqNPZWsdB2EcGfr+ -XwIDAQAB ------END PUBLIC KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbpass.txt deleted file mode 100644 index 264f6ba0..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbpass.txt +++ /dev/null @@ -1 +0,0 @@ -welcome1 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbuser.txt deleted file mode 100644 index 97ce6cbb..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/cert_path/certificates/wbuser.txt +++ /dev/null @@ -1 +0,0 @@ -welcome diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/check.sh b/docs/multitenant/lrest-based/usecase05_to_be_removed/check.sh deleted file mode 100644 index 469a4ae1..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/check.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -kubectl apply -f open_lrpdb1_resource.yaml -kubectl get lrpdb lrpdb1 -n pdbnamespace --watch diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb1_resource.yaml deleted file mode 100644 index 1fe63c15..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - pdbconfigmap: "config-map-pdb" - assertiveLrpdbDeletion: true - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Clone" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb2_resource.yaml deleted file mode 100644 index 926810e8..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/clone_lrpdb2_resource.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb4 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertiveLrpdbDeletion: true - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Clone" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb1_resource.yaml deleted file mode 100644 index 8f5528d9..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb1_resource.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb2_resource.yaml deleted file mode 100644 index 884ebb90..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb2_resource.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb3_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb3_resource.yaml deleted file mode 100644 index 5a080963..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/close_lrpdb3_resource.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod.yaml deleted file mode 100644 index 962a8fa1..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LREST -metadata: - name: cdb-dev - namespace: cdbnamespace -spec: - cdbName: "DB12" - lrestImage: "lin.ocir.io/intsanjaysingh/mmalvezz/testppr/lrest-dboper:latest" - lrestImagePullPolicy: "Always" - dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - replicas: 1 - deletePdbCascade: true - cdbAdminUser: - secret: - secretName: "dbuser" - key: "e_dbuser.txt" - cdbAdminPwd: - secret: - secretName: "dbpass" - key: "e_dbpass.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbPubKey: - secret: - secretName: "pubkey" - key: "publicKey" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - - - - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod_delcascade.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod_delcascade.yaml deleted file mode 100644 index 962a8fa1..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrest_pod_delcascade.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LREST -metadata: - name: cdb-dev - namespace: cdbnamespace -spec: - cdbName: "DB12" - lrestImage: "lin.ocir.io/intsanjaysingh/mmalvezz/testppr/lrest-dboper:latest" - lrestImagePullPolicy: "Always" - dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - replicas: 1 - deletePdbCascade: true - cdbAdminUser: - secret: - secretName: "dbuser" - key: "e_dbuser.txt" - cdbAdminPwd: - secret: - secretName: "dbpass" - key: "e_dbpass.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbPubKey: - secret: - secretName: "pubkey" - key: "publicKey" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - - - - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb1_resource.yaml deleted file mode 100644 index 78076413..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb1_resource.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: "lrpdb1" - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - assertiveLrpdbDeletion: true - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - fileNameConversions: "NONE" - unlimitedStorage: false - pdbconfigmap: "config-map-pdb" - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb2_resource.yaml deleted file mode 100644 index c9466df3..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/create_lrpdb2_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "DB12" - cdbNamespace: "cdbnamespace" - pdbName: "pdbprd" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/dbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/dbpass.txt deleted file mode 100644 index eab39d3f..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/dbpass.txt +++ /dev/null @@ -1 +0,0 @@ -CLWKO655321 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/dbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/dbuser.txt deleted file mode 100644 index 7028cc36..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/dbuser.txt +++ /dev/null @@ -1 +0,0 @@ -restdba diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb1_resource.yaml deleted file mode 100644 index 7390db88..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb1_resource.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb2_resource.yaml deleted file mode 100644 index 09525b98..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb2_resource.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb3_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb3_resource.yaml deleted file mode 100644 index e362f068..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/delete_lrpdb3_resource.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "new_clone" - action: "Delete" - dropAction: "INCLUDING" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/dmpplane.819519 b/docs/multitenant/lrest-based/usecase05_to_be_removed/dmpplane.819519 deleted file mode 100644 index c569c32d..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/dmpplane.819519 +++ /dev/null @@ -1,30 +0,0 @@ -2024-11-04T11:19:59Z INFO controller-runtime.builder Registering a validating webhook {"GVK": "database.oracle.com/v1alpha1, Kind=DataguardBroker", "path": "/validate-database-oracle-com-v1alpha1-dataguardbroker"} -2024-11-04T11:19:59Z INFO controller-runtime.webhook Registering webhook {"path": "/validate-database-oracle-com-v1alpha1-dataguardbroker"} -2024-11-04T11:19:59Z INFO setup starting manager -2024-11-04T11:19:59Z INFO controller-runtime.metrics Starting metrics server -2024-11-04T11:19:59Z INFO controller-runtime.metrics Serving metrics server {"bindAddress": ":8080", "secure": false} -2024-11-04T11:19:59Z INFO controller-runtime.webhook Starting webhook server -2024-11-04T11:19:59Z INFO controller-runtime.certwatcher Updated current TLS certificate -2024-11-04T11:19:59Z INFO controller-runtime.webhook Serving webhook server {"host": "", "port": 9443} -2024-11-04T11:19:59Z INFO controller-runtime.certwatcher Starting certificate watcher -I1104 11:19:59.901034 1 leaderelection.go:250] attempting to acquire leader lease oracle-database-operator-system/a9d608ea.oracle.com... -2024-11-04T11:19:37Z INFO controller-runtime.webhook Serving webhook server {"host": "", "port": 9443} -2024-11-04T11:19:37Z INFO controller-runtime.certwatcher Starting certificate watcher -I1104 11:19:37.669237 1 leaderelection.go:250] attempting to acquire leader lease oracle-database-operator-system/a9d608ea.oracle.com... -2024-11-04T11:20:49Z INFO lrpdb-webhook Setting default values in LRPDB spec for : lrpdb4 -2024-11-04T11:20:49Z INFO lrpdb-webhook - reuseTempFile : true -2024-11-04T11:20:49Z INFO lrpdb-webhook - unlimitedStorage : true -2024-11-04T11:20:49Z INFO lrpdb-webhook - tdeImport : false -2024-11-04T11:20:49Z INFO lrpdb-webhook - tdeExport : false -2024-11-04T11:20:49Z INFO lrpdb-webhook - asClone : false -2024-11-04T11:20:49Z INFO lrpdb-webhook - getScript : false -===> controllers.restSQLCollection{Env:struct { DefaultTimeZone string "json:\"defaultTimeZone,omitempty\"" }{DefaultTimeZone:""}, Items:[]controllers.SQL_Item(nil)} -===> {Env:{DefaultTimeZone:} Items:[]} -2024-11-04T11:22:24Z DEBUG events cdb-dev {"type": "Warning", "object": {"kind":"LRPDB","namespace":"pdbnamespace","name":"lrpdb1","uid":"cfbd1d27-5571-444c-b175-4838b1f68d5a","apiVersion":"database.oracle.com/v4","resourceVersion":"62119140"}, "reason": "Done"} -2024-11-04T11:22:24Z INFO controllers.LRPDB Begin respData {"getLRPDBState": {"name":"lrpdb1","namespace":"pdbnamespace"}} -2024-11-04T11:22:24Z INFO controllers.LRPDB {"inst_id":1,"con_id":3,"open_mode":"MOUNTED","restristced":"NONE","total_size":855638016,"sqlcode":0} - {"getLRPDBState": {"name":"lrpdb1","namespace":"pdbnamespace"}} -2024-11-04T11:22:24Z INFO controllers.LRPDB End respData {"getLRPDBState": {"name":"lrpdb1","namespace":"pdbnamespace"}} -2024-11-04T11:22:24Z INFO controllers.LRPDB Successfully obtained LRPDB state {"getLRPDBState": {"name":"lrpdb1","namespace":"pdbnamespace"}, "LRPDB Name": "pdbdev", "State": "MOUNTED"} -2024-11-04T11:22:24Z INFO controllers.LRPDB Frame: {"ManageConfigMapForCloningAndPlugin": {"name":"lrpdb1","namespace":"pdbnamespace"}} -2024-11-04T11:22:24Z INFO controllers.LRPDB Reconcile completed {"multitenantoperator": {"name":"lrpdb1","namespace":"pdbnamespace"}} diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbpass.txt deleted file mode 100644 index 3d0430f0..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbpass.txt +++ /dev/null @@ -1,5 +0,0 @@ -orwWVJyNxwyQNGhlvmugFC4Ce7UtXCoFYspwA4CRMQ1vc+ASaN6hcw2cH3yBhXUOisycmnUPsKwx -0JOiZ6B7Iee+IhJhIoXuYSiPShAs0MFvMautBtzSoX6uH4KOYC6vKVkcQWpsTDU1BMgOvgmswGkQ -v6d3J2jkxsJnXY2Ium43LyqVdKWEen2JDAYvdWCTPlte2YmHF48sqPx5fFrf/NYIti5Te6/afgct -ks6CYr7X55fSC/K1zXW6xde6uPU3F/JPUDGtB6E/XzhE/qyQSN+v32mBQZngw7o+wSNlC08XMOJR -umhK+Y8QiLxndyqhCdxT455KnjKToTaS7QwTNw== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbuser.txt deleted file mode 100644 index 365c5725..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_dbuser.txt +++ /dev/null @@ -1,5 +0,0 @@ -4Zxo3RqNxxWhTjKCk4mHM1H4gQN2iDB0qYfcdNlZmG2pxBEK8OIzB75IIb6wvDd2zYqninEiJhQB -HCbXKm+fs+uzappCnhqycIh132M7m2Xt0Kt24bAKBmN9KnKNcRJU7VnTadTG/sk8Q4A5cz1ZLywF -NS/KwP/1bWRdgTZDmUnJJYl7vhiM2nbcW0EW/a1DH0oybY/iQ/GhYpoEZiS9v1+S8boRRmdoj2Au -sZ+B13sHYyXKpL/ch/8LsYVcEQ1Tdb8U+sleaccfKwFuthwqBUx51EVxy5ITf90CQV+fEKUPmCPP -BVi6CbaklQr44yx24JB682WLFPH5JpbS4p8eTg== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbpwd.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbpwd.txt deleted file mode 100644 index c07a3e3e..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbpwd.txt +++ /dev/null @@ -1,5 +0,0 @@ -UDzcNNEF8MSGwpzyEl9o6lkABX5WDGJ7QSxTfmIEI23WoGuBi3cBA259f3zc9kg75i+xjrDqLgp2 -TPCsNRU0ZiVSg2TWZqJBMl3TZ3RE51uijfQPWgJco4uttzxyzRqbJ2EY8Xv5ClbKqbjZDqIS+AIQ -hCDHd3sV/xtPziMluNEWlEQp+G6te48FLN8ipRxNKQIJZdRtfx0gUWSiHZHT10Y7GDr/lJmqFDCv -W9bnvLoTyxemIq5nI5HPCD8n3/Y0Hs+vxC8y96cQ5bUwVKjxwKnZ+dmUeOd9BgRzPyJqReqWF0m1 -lNh3Ott5ZMPeNA1eT4dFDmyJtRBV7+XDTHWPfA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbusr.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbusr.txt deleted file mode 100644 index d4b710ea..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_pdbusr.txt +++ /dev/null @@ -1,5 +0,0 @@ -l0hpojsR6gMWUM5pNAsIpFHSY1gti1VTTo5fIwWEPxcbhRLmbBN3sHSJFgue90Bkb9yC5jX0ShwJ -GZZWpI/QhfhYm9LSXF8I2Jhrz6Ptg+xnkl9VJYaX718CzcFU0hx18vPbXTfvCoiOod+I84wJGoNu -qf80bWmh+fUzdJdTGnw16oQ/JFpF3Q60hYV8a5OKQ7AsNpch9jaNZdxZmHfz6RRap2/Tmr0GQPJH -zmaWLFZmRYgwLaXsO3u8Hg4H92K+Adelz/qBaxGwmNd56ZsCgIlxyZ479fphjKVK1S92upQZgHo3 -1DAWMkLTyjc8lbsMCT5EwNL56P2Pg8+wsogbhA== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbpass.txt deleted file mode 100644 index 80338208..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbpass.txt +++ /dev/null @@ -1,5 +0,0 @@ -PdZQQJ/7XB3F5evBDXmi8E90eHTsgYCpQ5tJl/m7QcXyMOcTozVVchI0eSOzxR+FXrVX0QiluzZ7 -1ceuqauRwuVyjRhvdHzFLRv6E4PogG+u4+ce2vlKVhyZnlSzpjfx2utDbIV+/jL74dPQuPPdQ/mb -5Kx01vaFLg42KMGzHFDd0wv5NtePspxHyk2czGspipVO1rwZLhC4NnODg3MWqfSuVwHNr1sCz4w6 -MMYsQkoRH5S23NZfVPnzVcBzw4mSNM1h0rlPZrwxEoLaUSPGtk26HZCNeDAKN7mtcUit1w7T+jAh -OVVjBQ2D38zYqqOVl7cjaA8+KLoUyoioGkHCag== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbuser.txt deleted file mode 100644 index 9508a37f..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/e_wbuser.txt +++ /dev/null @@ -1,5 +0,0 @@ -QWh2DCkOtZLKQYJQ5a329NpPgBp31KVvOVpKEJopg/j+mkZKOM9CM9AeF6ES0FXPKvkp9DJNTluE -AlnQe5Tt8h7FvD7yWvPnr25tepnbrAnykjCusQAUv9TBuncIXKMQnKAZU3h8CZEfNwNp9itVt1hJ -L0DUU3azS1utyBRMKPbI6uxsJKtnImO0nj37r+i4V0au0K9nbFntLu3h+tIEC/9owP1ydhLSYHM4 -SlhAWCSP17bzoi/+/AXlvteVMGGd+xeWG3qCunwCg4qEJHHNCA5sKv5DXclqlPZPHf1kW0zq2uLZ -oknIu95/ZoSSEPtVi4ws6faznKCoZ4toBCBguw== diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/extfile.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/extfile.txt deleted file mode 100644 index 042aa390..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/extfile.txt +++ /dev/null @@ -1 +0,0 @@ -subjectAltName=DNS:cdb-dev-lrest.cdbnamespace,DNS:www.example.com diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/makefile b/docs/multitenant/lrest-based/usecase05_to_be_removed/makefile deleted file mode 100644 index f3e87f69..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/makefile +++ /dev/null @@ -1,647 +0,0 @@ -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# -# ___ -# / _ \ _ __ _ __ _ __ ___ _ __ ___ -# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ -# | |_| | | | | |_) | | | __/ | | | | | -# \___/|_| |_| .__/|_| \___|_| |_| |_| -# |_| -# ____ _ _ _ -# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ -# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| -# | |__| (_) | | | | |_| | | (_) | | | __/ | -# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| -# -# -# This makefile helps to speed up the kubectl commands executions to deploy and test -# the multitenant operator. -# -# Quick start: -# ~~~~~~~~~~~ -# -# - Copy files of tab.1 in the makefile directory. -# - Edit the secret files and other yaml files with the correct credential as -# specified in the documentation. -# - Edit makefile updating variables of tab.2 -# - Execute commands of tab.3 "make step1" "make step2" "make step3".... -# -# Tab.1 - List of required files -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# oracle-database-operator.yaml : oracle database operator -# cdbnamespace_binding.yaml : role binding for cdbnamespace -# pdbnamespace_binding.yaml : role binding for pdbnamespace -# create_lrest_secret.yaml : create secrets for rest server pod -# create_lrpdb_secret.yaml : create secrets for pluggable database -# create_lrest_pod.yaml : create rest server pod -# create_lrpdb1_resource.yaml : create first pluggable database -# create_lrpdb2_resource.yaml : create second pluggable database -# open_lrpdb1_resource.yaml : open first pluggable database -# open_lrpdb2_resource.yaml : open second pluggable database -# close_lrpdb1_resource.yaml : close first pluggable database -# close_lrpdb2_resource.yaml : close second pluggable database -# clone_lrpdb_resource.yaml : clone thrid pluggable database -# clone_lrpdb2_resource.yaml : clone 4th pluggable database -# delete_lrpdb1_resource.yaml : delete first pluggable database -# delete_lrpdb2_resource.yaml : delete sencond pluggable database -# delete_lrpdb3_resource.yaml : delete thrid pluggable database -# unplug_lrpdb1_resource.yaml : unplug first pluggable database -# plug_lrpdb1_resource.yaml : plug first pluggable database -# map_lrpdb1_resource.yaml : map the first pluggable database -# config_map.yam : pdb parameters array -# altersystem_lrpdb1_resource.yaml : chage cpu_count count parameter for the first pdb - -# -# Tab.2 - List of variables -# ~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |OCIR | Your image registry | -# +-----------------------------+---------------------------------------------+ -# |OCIRPATH | Path of the image in your registry | -# +-----------------------------+---------------------------------------------+ -# -# Tab.3 - Execution steps -# ~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# | MAKEFILE TARGETS LIST | -# | ----- ooo ----- | -# | - TARGET - - DESCRIPTION - | -# +-----------------------------+---------------------------------------------+ -# |step0 | list pods and lrpdb resources | -# +-----------------------------+-------------------------------------+-------| -# |step1 | Build rest server images | | -# +-----------------------------+-------------------------------------+ REST | -# |step2 | Tag the immages | SRV | -# +-----------------------------+-------------------------------------+ IMG | -# |step3 | Push the image into the repository | | -# +-----------------------------+-------------------------------------+-------+ -# |step4 | Load webhook certmanager | DB | -# +-----------------------------+-------------------------------------+ OPER | -# |step5 | Create the db operator | | -# +-----------------------------+-------------------------------------+-------+ -# |step6 | Create tls certificates | T | -# +-----------------------------+-------------------------------------+ L | -# |step7 | Create tls secret | S | -# +-----------------------------+---------------------------------------------+ -# |step8 | Create database secrets | -# +-----------------------------+---------------------------------------------+ -# |step9 | Create restserver pod | -# | | +---------------------------------------------+ -# | +---> checkstep9 | Monitor the executions | -# +-----------------------------+---------------------------------------------+ -# |step10.[1..9] | Create pluggable database | -# | | +---------------------------------------------+ -# | +---> checklrest | Monitor LRPDB status | -# +-----------------------------+---------------------------------------------+ -# | step11.[1..9] | Open pluggable database | -# +-----------------------------+---------------------------------------------+ -# | step12.[1..9] | Close pluggable database | -# +-----------------------------+---------------------------------------------+ -# | step13.[1..9] | Clone pluggable database | -# +-----------------------------+---------------------------------------------+ -# | step14.[1..9] | Delete pluggable database | -# +-----------------------------+---------------------------------------------+ -# | step15.[1..9] | Unplug pluggable database | -# +-----------------------------+---------------------------------------------+ -# | step16.[1..9] | Plug pluggable database | -# +-----------------------------+---------------------------------------------+ -# | step17.[1..9] | Map pluggable database | -# +-----------------------------+---------------------------------------------+ -# | DIAGNOSTIC TARGETS | -# +-----------------------------+---------------------------------------------+ -# | dump | Dump pods info into a file | -# +-----------------------------+---------------------------------------------+ -# | reloadop | Reload the db operator | -# +-----------------------------+---------------------------------------------+ -# | login | Login into cdb pod | -# +-----------------------------+---------------------------------------------+ - -DATE := `date "+%y%m%d%H%M%S"` - -################ TAB 2 VARIABLES ############ -OCIR=lin.ocir.io -OCIRPATH=intsanjaysingh/mmalvezz/testppr/$(REST_SERVER)-dboper:$(LRESTVERSION) -############################################# - -OPRNAMESPACE=oracle-database-operator-system -LRSNAMESPACE=cdbnamespace -PDBNAMESPACE=pdbnamespace -CONFIG_MAP=config-map-pdb - -REST_SERVER=lrest -LRESTVERSION=latest -DOCKER=/usr/bin/docker -#KUBECTL=/usr/bin/kubectl -KUBECTL=/usr/local/go/bin/kubectl -CONFIG=/etc/ords/config -IMAGE=oracle/$(REST_SERVER)-dboper:$(LRESTVERSION) -DBOPERATOR=oracle-database-operator.yaml -DBOPERATORNOVERBS=oracle-database-operator-noverbs.yaml -URLPATH=/_/db-api/stable/database/pdbs/ -OPENSSL=/usr/bin/openssl -ORDSPORT=8888 -MAKE=/usr/bin/make -DOCKERFILE=Dockerfile -RM=/usr/bin/rm -MKDIR=/bin/mkdir -TAR=/bin/tar -CP=/bin/cp -ECHO=/usr/bin/echo -SED=/bin/sed -SLEEP=sleep 32 -OPRNAMESPACE=oracle-database-operator-system -CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -LREST_SECRET=create_lrest_secret.yaml -LRPDB_SECRET=create_lrpdb_secret.yaml -TDE_SECRET=tde_secret.yaml -PDBROLEBINDING=pdbnamespace_binding.yaml -CDBROLEBINDING=cdbnamespace_binding.yaml -LREST_POD=create_lrest_pod.yaml -LRPDB1=create_lrpdb1_resource.yaml -LRPDB2=create_lrpdb2_resource.yaml -LRPDBOPEN1=open_lrpdb1_resource.yaml -LRPDBOPEN2=open_lrpdb2_resource.yaml -LRPDBOPEN3=open_lrpdb3_resource.yaml -LRPDBCLOSE1=close_lrpdb1_resource.yaml -LRPDBCLOSE2=close_lrpdb2_resource.yaml -LRPDBCLOSE3=close_lrpdb3_resource.yaml -LRPDBCLONE1=clone_lrpdb1_resource.yaml -LRPDBDELETE1=delete_lrpdb1_resource.yaml -LRPDBDELETE2=delete_lrpdb2_resource.yaml -LRPDBDELETE3=delete_lrpdb3_resource.yaml -LRPDBUNPLUG1=unplug_lrpdb1_resource.yaml -LRPDBPLUG1=plug_lrpdb1_resource.yaml -LRPDBMAP1=map_lrpdb1_resource.yaml -LRPDBMAP2=map_lrpdb2_resource.yaml -LRPDBMAP3=map_lrpdb3_resource.yaml -ALTERSYSTEMYAML=altersystem_lrpdb1_resource.yaml -SWAPYAML=/tmp/swap.yaml -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -RESNAME=-o=jsonpath='{range .items[*]} {.metadata.name} {.status.openMode} ' -SQLCODE=-o=jsonpath='{range .items[*]} {.metadata.name} {.status.sqlCode} ' -COMPANY=oracle -MAKEFILE=./makefile -RNDFILE=./rnd.txt - -list: - grep ^step $(MAKEFILE) - -step0: listres #[OK] -step1: createimage #[OK] -step2: tagimage #[OK] -step3: push #[OK] -step4: certmanager #[OK] -step5: dboperator #[OK] -step5.1: rolebinding #[OK] -step5.2: deldbop #[OK] -step6: tlscert #[OK] -step7: tlssecret #[OK] -step8: lrestsecretcred #[OK] -step9: lrest_pod #[OK] -step9.1: checklrest #[OK] -step10.1: lrpdbcreate1 #[OK] -step10.2: lrpdbcreate2 #[OK] -step11.1: lrpdbopen1 #[OK] -step11.2: lrpdbopen2 #[OK] -step12.1: lrpdbclose1 #[OK] -step12.2: lrpdbclose2 #[OK] -step13.1: lrpdbclone1 #[OK] -step13.2: lrpdbopen3 #[0K] -step13.3: lrpdbclose3 #[0K] -step14.1: lrpdbunplug1 #[OK] -step15.1: lrpdbplug1 #[OK] -step16.1: lrptkmap1 #[OK] -step16.2: lrptkmap2 #[OK] -step16.3: lrptkmap3 #[OK] -step17.1: lrpdbdelete1 #[OK] -step17.2: lrpdbdelete2 #[OK] -step17.3: lrpdbdelete3 #[OK] -step17.4: lrpdbdelete4 #[OK] -step18.1: lrpdbgalter #[OK] -step19: dellrestcascade -step100.1: lrptkunplug1 #[OK] -step101.1: lrptndelete1 #[OK] -step102.1: lrptnclone1 #[OK] -step103.1: optnowatch #[OK] -step104.1: optnoverbs #[OK] -step105.1: lrpdbnegalter #[OK] - - -listres: - $(KUBECTL) get pods -n $(OPRNAMESPACE) - $(KUBECTL) get lrpdb -n $(OPRNAMESPACE) - -createimage: - @echo "BUILDING CDB IMAGES" - @if [[ ! -f ./Dockerfile ]]; \ - then\ - echo "DOCKERFILE DOES NOT EXISTS";\ - exit 1; \ - fi; - @if [[ ! -f ./runLREST.sh ]]; \ - then\ - echo "DOCKERFILE DOES NOT EXISTS";\ - exit 1; \ - fi; - $(DOCKER) build -t $(IMAGE) . - -tagimage: - @echo "TAG IMAGE" - $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) - -push: - @echo "PUSH IMAGE INTO THE REGISTRY" - $(DOCKER) push $(OCIR)$(OCIRPATH) - -certmanager: - @echo "WEBHOOK CERT MANAGER" - $(KUBECTL) apply -f $(CERTMANAGER) - -dboperator: - @echo "ORACLE DATABASE OPERATOR" - cat $(DBOPERATOR)| sed 's/value: ""/value: "$(OPRNAMESPACE),$(PDBNAMESPACE),$(LRSNAMESPACE)"/g' >temp_opr.yaml - $(KUBECTL) apply -f temp_opr.yaml - $(RM) temp_opr.yaml - -rolebinding: - $(KUBECTL) apply -f $(PDBROLEBINDING) - $(KUBECTL) apply -f $(CDBROLEBINDING) - -PRVKEY=ca.key -PUBKEY=public.pem -tlscert: - @echo "CREATING TLS CERTIFICATES" - #OL8 - $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) - #OL9 - #$(OPENSSL) genrsa -out $(PRVKEY) 2048 - $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(LRSNAMESPACE)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(LRSNAMESPACE),DNS:www.example.com" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - -tlssecret: - @echo "CREATING TLS SECRETS" - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(LRSNAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDBNAMESPACE) - $(KUBECTL) create secret tls prvkey --key="$(PRVKEY)" --cert=ca.crt -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(LRSNAMESPACE) - - -opsecret: - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) - -dbsecret: - @echo "CREATING DB SECRETS" - $(KUBECTL) apply -f $(LREST_SECRET) -n $(LRSNAMESPACE) - $(KUBECTL) apply -f $(LRPDB_SECRET) -n $(PDBNAMESPACE) - #$(KUBECTL) apply -f $(TDE_SECRET) -n $(OPRNAMESPACE) - -DBUSERFILE=dbuser.txt -DBPASSFILE=dbpass.txt -WBUSERFILE=wbuser.txt -WBPASSFILE=wbpass.txt -PDBUSRFILE=pdbusr.txt -PDBPWDFILE=pdbpwd.txt - -lrestsecretcred: - $(KUBECTL) delete secret prvkey -n $(LRSNAMESPACE) - $(KUBECTL) delete secret pubkey -n $(LRSNAMESPACE) - $(ECHO) "restdba" > $(DBUSERFILE) - $(ECHO) "CLWKO655321" > $(DBPASSFILE) - $(ECHO) "welcome" > $(WBUSERFILE) - $(ECHO) "welcome1" > $(WBPASSFILE) - $(ECHO) "welcome" > $(PDBUSRFILE) - $(ECHO) "welcome1" > $(PDBPWDFILE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey="$(PRVKEY)" -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic pubkey --from-file=publicKey="$(PUBKEY)" -n $(LRSNAMESPACE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBUSERFILE) |base64 > e_$(DBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBPASSFILE) |base64 > e_$(DBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) - $(KUBECTL) create secret generic dbuser --from-file=e_$(DBUSERFILE) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic dbpass --from-file=e_$(DBPASSFILE) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey="$(PRVKEY)" -n $(PDBNAMESPACE) - - -s2: - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(PDBNAMESPACE) - - -# openssl rsautl -encrypt -pubin -inkey pub.key -in plaintext.txt | base64 > encrypted.txt -# cat encrypted.txt | base64 -d | openssl rsautl -decrypt -inkey priv.key -# kubectl create secret generic testsec --from-file=encrypted.txt --from-file=ca.key -n cdbnamespace - - - -lrest_pod: - @echo "CREATING LREST POD" - $(KUBECTL) apply -f $(LREST_POD) - $(KUBECTL) get pods -n $(LRSNAMESPACE) --watch - -checklrest: - $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(LRSNAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(LRSNAMESPACE) - -lrpdbcreate1: - $(KUBECTL) apply -f $(LRPDB1) - -lrpdbcreate1log: - $(KUBECTL) apply -f $(LRPDB1) & - $(KUBECTL) logs -f -l control-plane=controller-manager -n oracle-database-operator-system - -lrpdbcreate2: - $(KUBECTL) apply -f $(LRPDB2) - -lrpdbopen1log: - $(KUBECTL) apply -f $(LRPDBOPEN1) & - $(KUBECTL) logs -f -l control-plane=controller-manager -n oracle-database-operator-system - - -lrpdbopen1: - $(KUBECTL) apply -f $(LRPDBOPEN1) - -lrpdbopen2: - $(KUBECTL) apply -f $(LRPDBOPEN2) - -lrpdbopen3: - $(KUBECTL) apply -f $(LRPDBOPEN3) - -lrpdbclose1: - $(KUBECTL) apply -f $(LRPDBCLOSE1) - -lrpdbclose2: - $(KUBECTL) apply -f $(LRPDBCLOSE2) - -lrpdbclose3: - $(KUBECTL) apply -f $(LRPDBCLOSE3) - -lrpdbclone1: lrpdbopen1 - @echo "Waiting 10 sec before cloning" - @sleep 10 - $(KUBECTL) apply -f $(LRPDBCLONE1) - -lrpdbclone1log: - $(KUBECTL) apply -f $(LRPDBCLONE1) & - $(KUBECTL) logs -f -l control-plane=controller-manager -n oracle-database-operator-system - -lrpdbdelete1: - $(KUBECTL) apply -f $(LRPDBDELETE1) - -lrpdbdelete2: - $(KUBECTL) apply -f $(LRPDBDELETE2) - -lrpdbdelete3: - $(KUBECTL) apply -f $(LRPDBDELETE3) - -lrpdbdelete4: - echo "Imperative deletion" - $(KUBECTL) delete lrpdb lrpdb4 -n $(PDBNAMESPACE) - -lrpdbunplug1: - $(KUBECTL) apply -f $(LRPDBUNPLUG1) - -lrpdbplug1: - $(KUBECTL) apply -f $(LRPDBPLUG1) - -lrpdbplug1log: - $(KUBECTL) apply -f $(LRPDBPLUG1) & - $(KUBECTL) logs -f -l control-plane=controller-manager -n oracle-database-operator-system - - -lrptkmap1: - $(KUBECTL) delete lrpdb lrpdb1 -n $(PDBNAMESPACE) - $(SLEEP) - $(KUBECTL) apply -f $(LRPDBMAP1) - $(MAKE) -f $(MAKEFILE) checklrpdbs - -lrptkmap2: - $(KUBECTL) delete lrpdb lrpdb2 -n $(PDBNAMESPACE) - $(SLEEP) - $(KUBECTL) apply -f $(LRPDBMAP2) - $(MAKE) -f $(MAKEFILE) checklrpdbs - -lrptkmap3: - $(KUBECTL) delete lrpdb lrpdb3 -n $(PDBNAMESPACE) - $(SLEEP) - $(KUBECTL) apply -f $(LRPDBMAP3) - $(MAKE) -f $(MAKEFILE) checklrpdbs - -lrptkunplug1: - @echo "test unpluga and plug database" - @shuf -i 1-10000000 -n 1 >rnd_file.txt - @cat $(LRPDBUNPLUG1) |sed 's/pdb.xml/'$(shell cat ./rnd_file.txt)'.xml/g' > rnd_$(LRPDBUNPLUG1) - @cat $(LRPDBPLUG1) |sed 's/pdb.xml/'$(shell cat ./rnd_file.txt)'.xml/g' > rnd_$(LRPDBPLUG1) - $(KUBECTL) apply -f rnd_$(LRPDBUNPLUG1) - @echo -e "\033[5mWaiting for the unplug operation to complete\033[0m" - @while [ `$(KUBECTL) get lrpdb lrpdb1 -n $(PDBNAMESPACE) 2>/dev/null |grep ^lrpdb1 |wc -l` -eq 1 ];\ - do \ - sleep 1 ;\ - done - $(KUBECTL) apply -f rnd_$(LRPDBPLUG1) - @$(RM) ./rnd_file.txt rnd_$(LRPDBPLUG1) rnd_$(LRPDBUNPLUG1) - - -lrptndelete1: - @echo "lrptndelete1: Starting negative test: delete an open pdb" - $(KUBECTL) apply -f $(LRPDB1) - @echo -e "\033[5mCreating database\033[0m" - @while [ `$(KUBECTL) get lrpdbs -n $(PDBNAMESPACE) $(RESNAME) 2>/dev/null |grep lrpdb1 |wc -l` -eq 0 ];\ - do \ - $(SLEEP);\ - done - $(KUBECTL) apply -f $(LRPDBOPEN1) - @echo -e "\033[5mOpening database\033[0m" - @while [ `$(KUBECTL) get lrpdbs -n $(PDBNAMESPACE) $(RESNAME) 2>/dev/null |grep lrpdb1|grep "READ WRITE" |wc -l` -eq 0 ];\ - do \ - $(SLEEP);\ - done - $(SLEEP) - $(KUBECTL) apply -f $(LRPDBDELETE1) - $(SLEEP) - $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) - -lrpdbgalter: - $(KUBECTL) apply -f $(ALTERSYSTEMYAML) - $(SLEEP) - $(KUBECTL) describe lrpdb lrpdb1 -n $(PDBNAMESPACE) - -lrpdbnegalter: - $(SED) 's/cpu_count=[0-9]/cpu_count=100000/g' $(ALTERSYSTEMYAML) > $(SWAPYAML) - $(KUBECTL) apply -f $(SWAPYAML) - $(KUBECTL) get lrpdbs lrpdb1 -n $(PDBNAMESPACE) $(SQLCODE) - $(KUBECTL) describe lrpdb lrpdb1 -n $(PDBNAMESPACE) - -lrptnclone1: - @echo "lrptnclone1: Starting negative test: cloning a pdb in mount state" - $(KUBECTL) apply -f $(LRPDB1) - @echo -e "\033[5mCreating database\033[0m" - @while [ `$(KUBECTL) get lrpdbs -n $(PDBNAMESPACE) $(RESNAME) 2>/dev/null |grep ^lrpdb1 |wc -l` -eq 1 ];\ - do \ - $(SLEEP);\ - done - $(KUBECTL) apply -f $(LRPDBCLONE1) - $(SLEEP) - @while [ `$(KUBECTL) get lrpdbs lrpdb3 -n $(PDBNAMESPACE) $(SQLCODE) 2>/dev/null |grep lrpdb3 |grep 65036 |wc -l` -eq 0 ];\ - do \ - $(SLEEP);\ - done - $(KUBECTL) get lrpdb lrpdb3 -n $(PDBNAMESPACE) - -optnowatch: - cat $(DBOPERATOR) |sed 's/$(OPRNAMESPACE),$(PDBNAMESPACE),$(LRSNAMESPACE)/default/g' >temp_opr.yaml - $(KUBECTL) apply -f temp_opr.yaml - $(SLEEP) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) - $(KUBECTL) apply -f $(LREST_POD) - $(RM) temp_opr.yaml - -optnoverbs: - $(KUBECTL) apply -f $(DBOPERATORNOVERBS) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) - $(KUBECTL) apply -f $(LREST_POD) - -secretsop: - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) - -deldbop: - $(KUBECTL) delete -f $(DBOPERATOR) --ignore-not-found=true - -checklrpdbs: - $(KUBECTL) get lrpdbs -n $(PDBNAMESPACE) - - - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - -dump2: - @echo "CDB LOG DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs `$(KUBECTL) get pods -n $(LRSNAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(LRSNAMESPACE) >>$(DIAGFILE) - @echo "SECRET DMP" >>$(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) get secrets -o yaml -n $(OPRNAMESPACE) >> $(DIAGFILE) - @echo "CDB/LRPDB DMP" >> $(DIAGFILE) - $(KUBECTL) get lrpdbs -o yaml -n $(PDBNAMESPACE) >> $(DIAGFILE) - $(KUBECTL) get lrest -o yaml -n $(LRSNAMESPACE) >> $(DIAGFILE) - @echo "CLUSTER INFO" >> $(DIAGFILE) - $(KUBECTL) get nodes -o wide - $(KUBECTL) get svc --namespace=kube-system - -cleardump: - $(RM) opdmp.* - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - -getrolebinding: - $(KUBECTL) get rolebinding -n $(PDBNAMESPACE) - $(KUBECTL) get rolebinding -n $(LRSNAMESPACE) - -login: - $(KUBECTL) exec `$(KUBECTL) get pods -n $(LRSNAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(LRSNAMESPACE) -it -- /bin/bash - -log1: - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) - -log2: - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) - -log3: - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) - -clean: - $(KUBECTL) delete -f $(LRPDB1) - $(KUBECTL) delete -f $(LREST_POD) - -pkg: - $(MKDIR) /tmp/pkgtestplan - $(CP) -R * /tmp/pkgtestplan - rm /tmp/pkgtestplan/oracle-database-operator.yaml - $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ - $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan - rm -rf /tmp/pkgtestplan - - - -delsecrets: - @echo "delete $(LRSNAMESPACE) secrets" - $(KUBECTL) get secrets -n $(LRSNAMESPACE)|awk ' { print $$1 }'|grep -v NAME |while read sec ; do \ - $(KUBECTL) delete secrets $$sec -n $(LRSNAMESPACE) ; \ - done ; - @echo "delete $(PDBNAMESPACE) secrets" - $(KUBECTL) get secrets -n $(PDBNAMESPACE)|awk ' { print $$1 }'|grep -v NAME |while read sec ; do \ - $(KUBECTL) delete secrets $$sec -n $(PDBNAMESPACE) ; \ - done ; - $(KUBECTL) delete secrets db-ca db-tls -n $(OPRNAMESPACE) - - -dellrestcascade: - $(KUBECTL) delete lrest cdb-dev -n $(LRSNAMESPACE) - $(KUBECTL) get events -n $(LRSNAMESPACE) - $(KUBECTL) get lrest -n $(LRSNAMESPACE) - $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) - -#============================== Config Map Section =======================# - - -define genconfigmap - -echo "session_cached_cursors;100;spfile" >parameters.txt -echo "open_cursors;100;spfile" >>parameters.txt -echo "db_file_multiblock_read_count;16;spfile" >>parameters.txt -echo "test_invalid_parameter;16;spfile" >>parameters.txt - -$(KUBECTL) create configmap $(CONFIG_MAP) -n $(PDBNAMESPACE) --from-file=./parameters.txt -$(KUBECTL) describe configmap $(CONFIG_MAP) -n $(PDBNAMESPACE) - -endef - -crecfgmap: - $(call genconfigmap) - -delcfgmap: - - $(KUBECTL) delete configmap $(CONFIG_MAP) -n $(PDBNAMESPACE) - -map: delcfgmap crecfgmap - - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb1_resource.yaml deleted file mode 100644 index cfc4d26f..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb1_resource.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - assertiveLrpdbDeletion: true - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb2_resource.yaml deleted file mode 100644 index a9b14e08..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb2_resource.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "lrpdb-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "lrpdb-secret" - key: "webserver_pwd" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb3_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb3_resource.yaml deleted file mode 100644 index cdc6c215..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/map_lrpdb3_resource.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "lrpdb-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "lrpdb-secret" - key: "webserver_pwd" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb1_resource.yaml deleted file mode 100644 index fda7b3fd..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb1_resource.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb2_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb2_resource.yaml deleted file mode 100644 index fd59f37e..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb2_resource.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "DB12" - cdbNamespace: "cdbnamespace" - pdbName: "pdbprd" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb3_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb3_resource.yaml deleted file mode 100644 index 6e01c212..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/open_lrpdb3_resource.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/parameters.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/parameters.txt deleted file mode 100644 index 836ea8da..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/parameters.txt +++ /dev/null @@ -1,4 +0,0 @@ -session_cached_cursors;100;spfile -open_cursors;100;spfile -db_file_multiblock_read_count;16;spfile -test_invalid_parameter;16;spfile diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbnamespace_binding.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbnamespace_binding.yaml deleted file mode 100644 index 6e0ee6cf..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbnamespace_binding.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: pdbnamespace -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbpwd.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbpwd.txt deleted file mode 100644 index 264f6ba0..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbpwd.txt +++ /dev/null @@ -1 +0,0 @@ -welcome1 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbusr.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbusr.txt deleted file mode 100644 index 97ce6cbb..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/pdbusr.txt +++ /dev/null @@ -1 +0,0 @@ -welcome diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/plug_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/plug_lrpdb1_resource.yaml deleted file mode 100644 index ab07de66..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/plug_lrpdb1_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertiveLrpdbDeletion: true - pdbconfigmap: "config-map-pdb" - action: "Plug" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/public.pem b/docs/multitenant/lrest-based/usecase05_to_be_removed/public.pem deleted file mode 100644 index 2ecc3de3..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/public.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6CyUbqQbxEzeCcDvs7PY -FAobZ9BgbK+Q4X6mPR7NhQtWecFc8OSkxSsT6Be2lx/2c+AfiYkS72mL9QLH5xZd -2LGlUPvCjH2lTYkYkstrkn0Rl6/TGA/0Udns2tIXPZ3nAaFKn6lcp1m8vpVSFWF2 -OcHLsmKUgz72LghrPjIrlEw7kG+FBIkhHP3HK5o+02lgsqiRkPopZtxY4H79qhb3 -pGr7WlCq7w2CHn89BXZUVsy3eKhMlknMYhrOK/kyEVLMuWFVKn/is8Tt6dptIGK5 -r/hEL0chbh7Yhnop1Ar8fSEk3U8Aj7eGEb4kgh3YSjuKmlIxuptFXKvez6PuU9Kn -kQIDAQAB ------END PUBLIC KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/resetaction_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/resetaction_lrpdb1_resource.yaml deleted file mode 100644 index dc148d51..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/resetaction_lrpdb1_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "lrpdb-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "lrpdb-secret" - key: "sysadmin_pwd" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Noaction" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/server.csr b/docs/multitenant/lrest-based/usecase05_to_be_removed/server.csr deleted file mode 100644 index 94d10fcf..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/server.csr +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIICqDCCAZACAQAwYzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQH -DAJTWjEVMBMGA1UECgwMb3JhY2xlLCBJbmMuMSMwIQYDVQQDDBpjZGItZGV2LWxy -ZXN0LmNkYm5hbWVzcGFjZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -APFKMg3rUfwCiArrHP9OyTqePYwIzVNHEvm952DcfDYq6h2KNEIQON9hKOmFQo7f -+ktzRxen3C6XkStouU3z851kZsr9yWdhVFWWYRxgFC8Cn2o0B5/TweX8uBai7YB9 -CAd+/BvV2OhJBx/bGtPlbGT50puODuC8J3Bp6XYHuIpRcYW3VA4NBMGd0SpyL48t -7QfyWMEVttbIkC0f10nQhbzBQFtldyaFeu49NNjn4z9m/SgtQ78Rs3A8Ghept+77 -riNi7U0WCCMpUCMw57Q5ewhFITLhZ/nPCQ+1IRQFobUoZv6HWyBX8Dt2HaYCJnEP -5Z3pSwGTpW+FtTFO7WfMgo0CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQAnJ+xo -ZRHzn9D1IINnDXQPUJ2X470AScmY+XN90ciiEJlVHq5DVNg+womJ1jZoUXQidxGW -dYYw/NpKhXFeJcFK1tnNYAsiXclYbCDQQoe165i/R3SYlacJkcGcQZD7xTL7spbM -fYhvnBGfGP85VX1/YE3bObUhCHYK6sntMa4xVvBGPziPz6SgVTXuVa4UHb+XuEtm -exLkAc0PwLnADjIgyNcNrP+cdThk5qXm/5ZPqkJeZU2+QxeK/JIEvRUyStvyQf8f -7hJ4npFxXnEut8YjzE1qWASKYGcVuIsvxCr3czFRmYLgJF5bnKEghdjr93tgHllz -d/Ku6XPqTNqE1g+X ------END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/testDelCascade.sh b/docs/multitenant/lrest-based/usecase05_to_be_removed/testDelCascade.sh deleted file mode 100644 index e146e7d9..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/testDelCascade.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - - -#kubectl delete lrpdb lrpdb1 -n pdbnamespace 2>/dev/null -make deldbop && make dboperator - -kubectl apply -f create_lrest_pod.yaml -RUN=0 -sleep 2 -while [ $RUN -eq 0 ] -do - RUN=`kubectl get pods -n cdbnamespace | grep Running|wc -l` - sleep 1 - echo $RUN -done -echo "LREST RUNNING" - -kubectl apply -f create_lrpdb1_resource.yaml -sleep 1 -kubectl apply -f open_lrpdb1_resource.yaml - -#kubectl delete lrest cdb-dev -n cdbnamespace - - diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/tkpc_k8slrestsetup.sh b/docs/multitenant/lrest-based/usecase05_to_be_removed/tkpc_k8slrestsetup.sh deleted file mode 100644 index aacb0369..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/tkpc_k8slrestsetup.sh +++ /dev/null @@ -1,294 +0,0 @@ -#!/bin/sh -# -# $Header: has/test/k8s/src/tkpc_k8slrestsetup.sh sohibane_lrest_negative_tests/3 2024/07/31 15:02:16 sohibane Exp $ -# -# tkpc_k8slrestsetup.sh -# -# Copyright (c) 2024, Oracle and/or its affiliates. -# -# NAME -# tkpc_k8slrestsetup.sh - script to be used to setup lrest in k8s env -# -# DESCRIPTION -# script to be used to setup lrest in k8s env -# -# NOTES -# -# -# MODIFIED (MM/DD/YY) -# prrastog 06/04/24 - Creation -# -alias k=kubectl -export KUBECONFIG=$HOME/.kube/config - -lrest_path=./ -lrest_path2=./ -cert_path="./cert_path" -operator_yaml="oracle-database-operator.yaml" - -while getopts ':o:' opt; do - case "$opt" in - o) operator_yaml=${OPTARG};; - esac -done -echo using operator file: $operator_yaml - -kubectl get all -n rac - -execute(){ - echo -e "\nExecuting: $1 \n" - $1 -} - -prerequisites(){ -echo "####### setting up prerequisites #######" -# create namespace cdbnamespace, pdbnamespace -create_ns1="kubectl create ns cdbnamespace" -create_ns2="kubectl create ns pdbnamespace" -execute "$create_ns1" -execute "$create_ns2" - -} - -step1(){ - -echo "######## STEP 1: create cert manager ###########" -cert_manager="kubectl apply -f $lrest_path/cert-manager.yaml" -#echo -e "\nExecuting: $cert_manager \n" -echo "BEGIN_IGNORE" -execute "$cert_manager" -#$cert_manager -echo "END_IGNORE" -sleep 300 -} - -step2(){ -echo "########## STEP 2: create operator pod ############" -operator_pod="kubectl apply -f $lrest_path2/$operator_yaml" -#echo -e "\nExecuting: $operator_pod \n" -echo "BEGIN_IGNORE" -execute "$operator_pod" -#$operator_pod -echo "END_IGNORE" -sleep 30 - -echo "######### STEP 2.1: check operator pods #########" -check_cmd="kubectl get pods -n oracle-database-operator-system" -#echo -e "\nExecuting: $check_cmd \n" -execute "$check_cmd" - -echo "############ STEP 2.2: create rolebindings ##############" -cdb_bind="kubectl apply -f $lrest_path2/cdbnamespace_binding.yaml" -pdb_bind="kubectl apply -f $lrest_path2/pdbnamespace_binding.yaml" -execute "$cdb_bind" -execute "$pdb_bind" -check_cdb_bind="kubectl get rolebinding -n cdbnamespace" -check_pdb_bind="kubectl get rolebinding -n pdbnamespace" -execute "$check_cdb_bind" -execute "$check_pdb_bind" -} - -step3(){ -# NOTE: only required when running on env the first time on new env, or if certificates are deleted -echo "########## STEP 3: generate certificates ############" -echo BEGIN_IGNORE -mkdir $cert_path/certificates -SCRT=lrest_server.crt -CANAME=ca -SKEY=lrest_server.key -REST_SERVER=lrest.cdbnamespace -SCSR=lrest_server.csr -COMPANY=oracle - -openssl genrsa -out $cert_path/certificates/$CANAME.key 2048 -sleep 2 -openssl req -new -x509 -days 365 -key $cert_path/certificates/$CANAME.key -subj "/C=CN/ST=GD/L=SZ/O=$COMPANY,Inc./CN=$COMPANY Root CA" -out $cert_path/certificates/$CANAME.crt -sleep 2 -openssl req -newkey rsa:2048 -nodes -keyout $cert_path/certificates/$SKEY -subj "/C=CN/ST=GD/L=SZ/O=$COMPANY,Inc./CN=cdb-dev-$REST_SERVER" -out $cert_path/certificates/$SCSR -sleep 2 -echo "subjectAltName=DNS:cdb-dev-$REST_SERVER,DNS:www.example.com" > $cert_path/certificates/extfile.txt -sleep 2 -openssl x509 -req -extfile $cert_path/certificates/extfile.txt -days 365 -in $cert_path/certificates/$SCSR -CA $cert_path/certificates/$CANAME.crt -CAkey $cert_path/certificates/$CANAME.key -CAcreateserial -out $cert_path/certificates/$SCRT -sleep 2 -openssl rsa -in $cert_path/certificates/$CANAME.key -outform PEM -pubout -out $cert_path/certificates/public.pem -echo END_IGNORE -} - -step4(){ - -echo "########## STEP 4: store certificates - create secrets ############" - -#echo "Executing: kubectl create secret tls db-tls --key="$cert_path/certificates/lrest_server.key" --cert="$cert_path/certificates/lrest_server.crt" -n oracle-database-operator-system" -#kubectl create secret tls db-tls --key="$cert_path/certificates/lrest_server.key" --cert="$cert_path/certificates/lrest_server.crt" -n oracle-database-operator-system -#echo "Executing: kubectl create secret generic db-ca --from-file="$cert_path/certificates/ca.crt" -n oracle-database-operator-system" -#kubectl create secret generic db-ca --from-file="$cert_path/certificates/ca.crt" -n oracle-database-operator-system - -secret_cdbns1="kubectl create secret tls db-tls --key=$cert_path/certificates/lrest_server.key --cert=$cert_path/certificates/lrest_server.crt -n cdbnamespace" -secret_cdbns2="kubectl create secret generic db-ca --from-file=$cert_path/certificates/ca.crt -n cdbnamespace" -secret_pdbns1="kubectl create secret tls db-tls --key=$cert_path/certificates/lrest_server.key --cert=$cert_path/certificates/lrest_server.crt -n pdbnamespace" -secret_pdbns2="kubectl create secret generic db-ca --from-file=$cert_path/certificates/ca.crt -n pdbnamespace" -secret_cdbns3="kubectl create secret tls prvkey --key=$cert_path/certificates/ca.key --cert=$cert_path/certificates/ca.crt -n cdbnamespace" -secret_cdbns4="kubectl create secret generic pubkey --from-file=publicKey=$cert_path/certificates/public.pem -n cdbnamespace" - -execute "$secret_cdbns1" -execute "$secret_cdbns2" -execute "$secret_pdbns1" -execute "$secret_pdbns2" -execute "$secret_cdbns3" -execute "$secret_cdbns4" -sleep 10; -echo "Executing: kubectl get secrets -n oracle-database-operator-system" -kubectl get secrets -n oracle-database-operator-system -check_cdbns="kubectl get secrets -n cdbnamespace" -check_pdbns="kubectl get secrets -n pdbnamespace" -execute "$check_cdbns" -execute "$check_pdbns" -# create dbsecrets -#lrest_secret="kubectl apply -f $lrest_path2/create_lrest_secret.yaml -n cdbnamespace" -#lrpdb_secret="kubectl apply -f $lrest_path2/create_lrpdb_secret.yaml -n pdbnamespace" -#execute "$lrest_secret" -#execute "$lrpdb_secret" - -} - -step4a(){ - -echo "############ create secret cred ##############" - -secret_del1="kubectl delete secret prvkey -n cdbnamespace" -secret_del2="kubectl delete secret pubkey -n cdbnamespace" - -execute "$secret_del1" -execute "$secret_del2" - -echo "restdba" > $cert_path/certificates/dbuser.txt -echo "CLWKO655321" > $cert_path/certificates/dbpass.txt -echo "welcome" > $cert_path/certificates/wbuser.txt -echo "welcome1" > $cert_path/certificates/wbpass.txt -echo "welcome" > $cert_path/certificates/pdbusr.txt -echo "welcome1" > $cert_path/certificates/pdbpwd.txt - -secretcred_cdbns1="kubectl create secret generic prvkey --from-file=privateKey=$cert_path/certificates/ca.key -n cdbnamespace" -secretcred_cdbns2="kubectl create secret generic pubkey --from-file=publicKey=$cert_path/certificates/public.pem -n cdbnamespace" - -execute "$secretcred_cdbns1" -execute "$secretcred_cdbns2" - -/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/dbuser.txt |base64 > $cert_path/certificates/e_dbuser.txt -/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/dbpass.txt |base64 > $cert_path/certificates/e_dbpass.txt -/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/wbuser.txt |base64 > $cert_path/certificates/e_wbuser.txt -/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/wbpass.txt |base64 > $cert_path/certificates/e_wbpass.txt -/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/pdbusr.txt |base64 > $cert_path/certificates/e_pdbusr.txt -/usr/bin/openssl rsautl -encrypt -pubin -inkey $cert_path/certificates/public.pem -in $cert_path/certificates/pdbpwd.txt |base64 > $cert_path/certificates/e_pdbpwd.txt - -secretcred_cdbns3="kubectl create secret generic dbuser --from-file=$cert_path/certificates/e_dbuser.txt -n cdbnamespace" -secretcred_cdbns4="kubectl create secret generic dbpass --from-file=$cert_path/certificates/e_dbpass.txt -n cdbnamespace" -secretcred_cdbns5="kubectl create secret generic wbuser --from-file=$cert_path/certificates/e_wbuser.txt -n cdbnamespace" -secretcred_cdbns6="kubectl create secret generic wbpass --from-file=$cert_path/certificates/e_wbpass.txt -n cdbnamespace" -secretcred_pdbns1="kubectl create secret generic wbuser --from-file=$cert_path/certificates/e_wbuser.txt -n pdbnamespace" -secretcred_pdbns2="kubectl create secret generic wbpass --from-file=$cert_path/certificates/e_wbpass.txt -n pdbnamespace" -secretcred_pdbns3="kubectl create secret generic pdbusr --from-file=$cert_path/certificates/e_pdbusr.txt -n pdbnamespace" -secretcred_pdbns4="kubectl create secret generic pdbpwd --from-file=$cert_path/certificates/e_pdbpwd.txt -n pdbnamespace" -secretcred_pdbns5="kubectl create secret generic prvkey --from-file=privateKey=$cert_path/certificates/ca.key -n pdbnamespace" - -execute "$secretcred_cdbns3" -execute "$secretcred_cdbns4" -execute "$secretcred_cdbns5" -execute "$secretcred_cdbns6" -execute "$secretcred_pdbns1" -execute "$secretcred_pdbns2" -execute "$secretcred_pdbns3" -execute "$secretcred_pdbns4" -execute "$secretcred_pdbns5" -sleep 10; - -check_sc_cdbns="kubectl get secrets -n cdbnamespace" -check_sc_pdbns="kubectl get secrets -n pdbnamespace" -execute "$check_sc_cdbns" -execute "$check_sc_pdbns" - -} - -step5(){ - - echo "############ create lrest pod ##############" - create_lrest_pod="kubectl apply -f $lrest_path2/create_lrest_pod.yaml" - execute "$create_lrest_pod" - sleep 120 - echo "############## check lrest pod creation #############" - check_lrest_pod=`kubectl get pods -n cdbnamespace |grep lrest` - echo $check_lrest_pod - #execute "$check_lrest_pod" - echo "############### check lrest pod creation log ##########" - echo "BEGIN_IGNORE" - check_log_lrest_pod="kubectl logs `kubectl get pods -n cdbnamespace |grep lrest|cut -d ' ' -f 1` -n cdbnamespace" - execute "$check_log_lrest_pod" - echo "END_IGNORE" - -} - -step5a(){ -echo "############## STEP 5a: create db service and lrest user restdba #############" -kubectl exec -it racnode1-0 -n rac -- su - oracle -c 'srvctl add service -d ORCLCDB -s lrest -r ORCLCDB1,ORCLCDB2; srvctl start service -d ORCLCDB -s lrest; srvctl status service -d orclcdb -s lrest' - -kubectl exec -it racnode1-0 -n rac -- su - oracle -c "export ORACLE_SID=ORCLCDB1; sqlplus -S / as sysdba < $cert_path/certificates/e_dbuser.txt -#/usr/bin/openssl pkeyutl -encrypt -pubin -inkey /home/opc/rack8s/operator/lrest/certificates/public.pem -in /home/opc/rack8s/operator/lrest/certificates/dbuser.txt > /home/opc/rack8s/operator/lrest/certificates/e_dbuser.txt diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.crt b/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.crt deleted file mode 100644 index dc409c50..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDgjCCAmqgAwIBAgIUHe1aTiQOytE4GraYPByqLUVoibswDQYJKoZIhvcNAQEL -BQAwVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEVMBMG -A1UECgwMb3JhY2xlLCBJbmMuMRcwFQYDVQQDDA5vcmFjbGUgUm9vdCBDQTAeFw0y -NDEwMjQyMDQyNTRaFw0yNTEwMjQyMDQyNTRaMGMxCzAJBgNVBAYTAkNOMQswCQYD -VQQIDAJHRDELMAkGA1UEBwwCU1oxFTATBgNVBAoMDG9yYWNsZSwgSW5jLjEjMCEG -A1UEAwwaY2RiLWRldi1scmVzdC5jZGJuYW1lc3BhY2UwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQDxSjIN61H8AogK6xz/Tsk6nj2MCM1TRxL5vedg3Hw2 -KuodijRCEDjfYSjphUKO3/pLc0cXp9wul5EraLlN8/OdZGbK/clnYVRVlmEcYBQv -Ap9qNAef08Hl/LgWou2AfQgHfvwb1djoSQcf2xrT5Wxk+dKbjg7gvCdwael2B7iK -UXGFt1QODQTBndEqci+PLe0H8ljBFbbWyJAtH9dJ0IW8wUBbZXcmhXruPTTY5+M/ -Zv0oLUO/EbNwPBoXqbfu+64jYu1NFggjKVAjMOe0OXsIRSEy4Wf5zwkPtSEUBaG1 -KGb+h1sgV/A7dh2mAiZxD+Wd6UsBk6VvhbUxTu1nzIKNAgMBAAGjOjA4MDYGA1Ud -EQQvMC2CGmNkYi1kZXYtbHJlc3QuY2RibmFtZXNwYWNlgg93d3cuZXhhbXBsZS5j -b20wDQYJKoZIhvcNAQELBQADggEBAFSQWoBQWwTVqRkyQZ7XAg0wEH3WL9GTFdCB -2i32mfA0at61U0QgGp8otCK2JkwAEdmPEG4kho1ITfsP1v2Mc0QyxYbNlYaNyLzn -m4eikTAJ7IvB5ArH8sz4O/+xLmUMBliSKVWZ1f1ukINgay/EJxfgHygdEJ3FAHFR -RxDMHWZ8smLTK2pTGEwpSRkXIrDDp3KftPNpd2meNz0ljdJAQUSkRwLOdW0SPAPX -JgNYZrcN+UjF22uci1Z8dzrV5/8pR+DynFXWpWvVHzOwXb4ad3Wepz+4LdqlmAXK -gD0B+dpwk8n4sDwiq5myIeV0Bng+BcNYYvSyzJFkGN3yh4JdD3c= ------END CERTIFICATE----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.key b/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.key deleted file mode 100644 index ce373e9c..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/tls.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDxSjIN61H8AogK -6xz/Tsk6nj2MCM1TRxL5vedg3Hw2KuodijRCEDjfYSjphUKO3/pLc0cXp9wul5Er -aLlN8/OdZGbK/clnYVRVlmEcYBQvAp9qNAef08Hl/LgWou2AfQgHfvwb1djoSQcf -2xrT5Wxk+dKbjg7gvCdwael2B7iKUXGFt1QODQTBndEqci+PLe0H8ljBFbbWyJAt -H9dJ0IW8wUBbZXcmhXruPTTY5+M/Zv0oLUO/EbNwPBoXqbfu+64jYu1NFggjKVAj -MOe0OXsIRSEy4Wf5zwkPtSEUBaG1KGb+h1sgV/A7dh2mAiZxD+Wd6UsBk6VvhbUx -Tu1nzIKNAgMBAAECggEAQwPSZx9Gg/HICxBF23rsVMWCXpRLQqfo00g1LSfPr9O3 -dkDeRsLuqMv5avJCDCCVCcOYqS9L7fzWqvHJFad9r+ckzZgzCe10WNkNW64rrOrT -j9GnlHJh1hicoFzcDeWsBzjzmF+KgOlXezeFefisZFcmcWHBLBXV9ljUKJ34WSG/ -j0NDi+yqn20keOiPXF+2RG28qDmsI5T3TNLnFwkcKUXVyiJRa/kXonZfGeOFpe3o -QBTrvb6iEzTMZEV+MuWI+1kH4tBo4CQy/yXz+RWsiPbXTqe0vVCMYTt7y6tLWTUI -bRnfzK29uzZXCBi9zOErzMIpsqt/nLv4jXk2NeB/wQKBgQD7MIyM1fD/rrrwtavx -UDnO5DGnBDkFiZBV1TE+U89XHo6+TK+VNuSVtmBCj6PgkVO8xqTBezNvbG0iMeR1 -JVYELqiKMPyAVBmnE5fmvNLdeOtkxu4KFc5sSbgtlSGyOUwIsoT/TuIhVCIb/5xW -F+/O9qhD/fUO8eqiNZVqPxevdQKBgQD16RzmOn9Vt7ljZReWeQZAdG0NWowga7lM -Z7WcHLwxibg3oRAgPLjlDkKxwU9+V4BGrpbw/63GMnY6S2bFC+wZnWNW4DzO8FKI -3NcKQy9KoJos3AeSXVTyX1Hp6mlOziHSze6LGEt/LFrsNbBbSBDOFQNVpoZH1t0+ -OZx85iv7uQKBgH85xdaHffti2tiUi7Q7+CusNOHtJ5yAYpS22KxC8t+jYJVObhhb -p0eVCghsjIpv0UPqMQS2jATBnD0XnCkhcgHISCFGUn4FGsEfEW8S8JKwbjmq8GI3 -TUhXxmt//4Mvti0pjmdp4usSm/wmaV7J3PKsx+k4NekN6tOh5D3eGvcJAoGBANey -kvvLAdghIEbz7F27aGVu+hzFeLYTZ2SCb4Uwm05ke6an6yoruQ6JlKywYFb0z3qw -N4vK6RU5PATkEI/0ZjMNn44bgJsPp4WPGDxTNA8kkjeZBDc5EILMY2Bnkdli674p -jfyJ4eb59ALPERLwQnVRbyxYtd4btNKY3A+eIaRZAoGBAI+K1WQeHhGsnDNB1Um+ -g/2uiZH/oK2Dx9iTJsKvXgoikDmWS+o527wdu2oD6/9GpjaMsvzyuL5Z48iYkYEg -3V097YAaCNLkfCm1zaUgs6cm48jivvSwrXQH0FI+z8dTyuFjDQJdDjVwup0V15k8 -XdMPGYZCX6Jrv9StTi+TWCz2 ------END PRIVATE KEY----- diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/unplug_lrpdb1_resource.yaml b/docs/multitenant/lrest-based/usecase05_to_be_removed/unplug_lrpdb1_resource.yaml deleted file mode 100644 index 691b852c..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/unplug_lrpdb1_resource.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/wbpass.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/wbpass.txt deleted file mode 100644 index 264f6ba0..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/wbpass.txt +++ /dev/null @@ -1 +0,0 @@ -welcome1 diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/wbuser.txt b/docs/multitenant/lrest-based/usecase05_to_be_removed/wbuser.txt deleted file mode 100644 index 97ce6cbb..00000000 --- a/docs/multitenant/lrest-based/usecase05_to_be_removed/wbuser.txt +++ /dev/null @@ -1 +0,0 @@ -welcome diff --git a/docs/multitenant/ords-based/usecase/create_ords_pod.yaml b/docs/multitenant/ords-based/usecase/create_ords_pod.yaml index b031d662..6c951e16 100644 --- a/docs/multitenant/ords-based/usecase/create_ords_pod.yaml +++ b/docs/multitenant/ords-based/usecase/create_ords_pod.yaml @@ -5,7 +5,7 @@ metadata: namespace: cdbnamespace spec: cdbName: "DB12" - ordsImage: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/ords-dboper:latest + ordsImage: ".............your registry............./ords-dboper:latest" ordsImagePullPolicy: "Always" dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" replicas: 1 diff --git a/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml deleted file mode 100644 index 45cfefbc..00000000 --- a/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml +++ /dev/null @@ -1,5057 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-system ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomouscontainerdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousContainerDatabase - listKind: AutonomousContainerDatabaseList - plural: autonomouscontainerdatabases - shortNames: - - acd - - acds - singular: autonomouscontainerdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.displayName - name: DisplayName - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - SYNC - - RESTART - - TERMINATE - type: string - autonomousContainerDatabaseOCID: - type: string - autonomousExadataVMClusterOCID: - type: string - compartmentOCID: - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - hardLink: - default: false - type: boolean - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - patchModel: - enum: - - RELEASE_UPDATES - - RELEASE_UPDATE_REVISIONS - type: string - type: object - status: - properties: - lifecycleState: - type: string - timeCreated: - type: string - required: - - lifecycleState - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabasebackups.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseBackup - listKind: AutonomousDatabaseBackupList - plural: autonomousdatabasebackups - shortNames: - - adbbu - - adbbus - singular: autonomousdatabasebackup - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.dbDisplayName - name: DB DisplayName - type: string - - jsonPath: .status.type - name: Type - type: string - - jsonPath: .status.timeStarted - name: Started - type: string - - jsonPath: .status.timeEnded - name: Ended - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - autonomousDatabaseBackupOCID: - type: string - displayName: - type: string - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - target: - properties: - k8sADB: - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - type: object - status: - properties: - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - dbDisplayName: - type: string - dbName: - type: string - isAutomatic: - type: boolean - lifecycleState: - type: string - timeEnded: - type: string - timeStarted: - type: string - type: - type: string - required: - - autonomousDatabaseOCID - - compartmentOCID - - dbDisplayName - - dbName - - isAutomatic - - lifecycleState - - type - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabaserestores.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseRestore - listKind: AutonomousDatabaseRestoreList - plural: autonomousdatabaserestores - shortNames: - - adbr - - adbrs - singular: autonomousdatabaserestore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.displayName - name: DbDisplayName - type: string - - jsonPath: .status.dbName - name: DbName - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - source: - properties: - k8sADBBackup: - properties: - name: - type: string - type: object - pointInTime: - properties: - timestamp: - type: string - type: object - type: object - target: - properties: - k8sADB: - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - required: - - source - - target - type: object - status: - properties: - dbName: - type: string - displayName: - type: string - status: - type: string - timeAccepted: - type: string - timeEnded: - type: string - timeStarted: - type: string - workRequestOCID: - type: string - required: - - dbName - - displayName - - status - - workRequestOCID - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: autonomousdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases - shortNames: - - adb - - adbs - singular: autonomousdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.details.displayName - name: Display Name - type: string - - jsonPath: .spec.details.dbName - name: Db Name - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .spec.details.isDedicated - name: Dedicated - type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .spec.details.dataStorageSizeInTBs - name: Storage (TB) - type: integer - - jsonPath: .spec.details.dbWorkload - name: Workload Type - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - details: - properties: - adminPassword: - properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - autonomousContainerDatabase: - properties: - k8sACD: - properties: - name: - type: string - type: object - ociACD: - properties: - ocid: - type: string - type: object - type: object - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbName: - type: string - dbVersion: - type: string - dbWorkload: - enum: - - OLTP - - DW - - AJD - - APEX - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - isAutoScalingEnabled: - type: boolean - isDedicated: - type: boolean - licenseModel: - enum: - - LICENSE_INCLUDED - - BRING_YOUR_OWN_LICENSE - type: string - lifecycleState: - type: string - networkAccess: - properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: - properties: - hostnamePrefix: - type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: - type: string - type: object - type: object - wallet: - properties: - name: - type: string - password: - properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - type: object - type: object - hardLink: - default: false - type: boolean - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - required: - - details - type: object - status: - properties: - allConnectionStrings: - items: - properties: - connectionStrings: - items: - properties: - connectionString: - type: string - tnsName: - type: string - type: object - type: array - tlsAuthentication: - type: string - required: - - connectionStrings - type: object - type: array - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - lifecycleState: - type: string - timeCreated: - type: string - walletExpiringDate: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: cdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - ordsImage: - type: string - ordsImagePullPolicy: - enum: - - Always - - Never - type: string - ordsImagePullSecret: - type: string - ordsPort: - type: integer - ordsPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: databaseobservers.observability.oracle.com -spec: - group: observability.oracle.com - names: - kind: DatabaseObserver - listKind: DatabaseObserverList - plural: databaseobservers - singular: databaseobserver - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - database: - properties: - dbConnectionString: - properties: - key: - type: string - secret: - type: string - type: object - dbPassword: - properties: - key: - type: string - secret: - type: string - vaultOCID: - type: string - vaultSecretName: - type: string - type: object - dbUser: - properties: - key: - type: string - secret: - type: string - type: object - dbWallet: - properties: - key: - type: string - secret: - type: string - type: object - type: object - exporter: - properties: - configuration: - properties: - configmap: - properties: - configmapName: - type: string - key: - type: string - type: object - type: object - image: - type: string - service: - properties: - port: - format: int32 - type: integer - type: object - type: object - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - prometheus: - properties: - labels: - additionalProperties: - type: string - type: object - port: - type: string - type: object - replicas: - format: int32 - type: integer - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - exporterConfig: - type: string - replicas: - type: integer - status: - type: string - required: - - conditions - - exporterConfig - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - properties: - clusterConnectString: - type: string - externalConnectString: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: dbcssystems.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DbcsSystem - listKind: DbcsSystemList - plural: dbcssystems - singular: dbcssystem - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - sshPublicKeys - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - ociConfigMap: - type: string - ociSecret: - type: string - required: - - ociConfigMap - type: object - status: - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbEdition: - type: string - dbInfo: - items: - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: lrests.database.oracle.com -spec: - group: database.oracle.com - names: - kind: LREST - listKind: LRESTList - plural: lrests - singular: lrest - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the LREST - jsonPath: .spec.cdbName - name: CDB NAME - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the LREST Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message if any - jsonPath: .status.msg - name: Message - type: string - - description: string of the tnsalias - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - lrestImage: - type: string - lrestImagePullPolicy: - enum: - - Always - - Never - type: string - lrestImagePullSecret: - type: string - lrestPort: - type: integer - lrestPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - nodeSelector: - additionalProperties: - type: string - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: lrpdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: LRPDB - listKind: LRPDBList - plural: lrpdbs - singular: lrpdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the LRPDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: last sqlcode - jsonPath: .status.sqlCode - name: last sqlcode - type: integer - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - - Alter - - Noaction - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbPass: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - alterSystem: - type: string - alterSystemParameter: - type: string - alterSystemValue: - type: string - asClone: - type: boolean - assertiveLrpdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - lrpdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - parameterScope: - type: string - pdbName: - type: string - pdbState: - enum: - - OPEN - - CLOSE - - ALTER - type: string - pdbconfigmap: - type: string - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - - alterSystemParameter - - alterSystemValue - - webServerPwd - type: object - status: - properties: - action: - type: string - alterSystem: - type: string - bitstat: - type: integer - bitstatstr: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - sqlCode: - type: integer - status: - type: boolean - totalSize: - type: string - required: - - phase - - sqlCode - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: oraclerestdataservices.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestDataService - listKind: OracleRestDataServiceList - plural: oraclerestdataservices - singular: oraclerestdataservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - apexPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - oracleService: - type: string - ordsPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - ordsUser: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - size: - type: string - storageClass: - type: string - volumeName: - type: string - type: object - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: - type: string - urlMapping: - type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - required: - - adminPassword - - databaseRef - - ordsPassword - type: object - status: - properties: - apexConfigured: - type: boolean - apexUrl: - type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: - type: string - databaseApiUrl: - type: string - databaseRef: - type: string - image: - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: string - ordsInstalled: - type: boolean - replicas: - type: integer - serviceIP: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: pdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - type: boolean - assertivePdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - type: string - pdbState: - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - type: object - status: - properties: - action: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - status: - type: boolean - totalSize: - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: shardingdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: ShardingDatabase - listKind: ShardingDatabaseList - plural: shardingdatabases - singular: shardingdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - gsm: - items: - properties: - directorName: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: boolean - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - items: - properties: - deployAs: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - gsm: - items: - properties: - directorName: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: boolean - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - items: - properties: - deployAs: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.14.0 - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.edition - name: Edition - type: string - - jsonPath: .status.sid - name: Sid - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version - type: string - - jsonPath: .status.connectString - name: Connect Str - type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.tcpsConnectString - name: TCPS Connect Str - type: string - - jsonPath: .status.tcpsPdbConnectString - name: TCPS Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.oemExpressUrl - name: Oem Express Url - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - archiveLog: - type: boolean - charset: - type: string - createAs: - enum: - - primary - - standby - - clone - type: string - dgBrokerConfigured: - type: boolean - edition: - enum: - - standard - - enterprise - - express - - free - type: string - enableTCPS: - type: boolean - flashBack: - type: boolean - forceLog: - type: boolean - image: - properties: - prebuiltDB: - type: boolean - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - listenerPort: - type: integer - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - pdbName: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - primaryDatabaseRef: - type: string - readinessCheckPeriod: - type: integer - replicas: - type: integer - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - sid: - maxLength: 12 - pattern: ^[a-zA-Z0-9]+$ - type: string - tcpsCertRenewInterval: - type: string - tcpsListenerPort: - type: integer - tcpsTlsSecret: - type: string - required: - - image - type: object - status: - properties: - apexInstalled: - type: boolean - archiveLog: - type: string - certCreationTimestamp: - type: string - certRenewInterval: - type: string - charset: - type: string - clientWalletLoc: - type: string - clusterConnectString: - type: string - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - connectString: - type: string - createdAs: - type: string - datafilesCreated: - default: "false" - type: string - datafilesPatched: - default: "false" - type: string - dgBrokerConfigured: - type: boolean - edition: - type: string - flashBack: - type: string - forceLog: - type: string - initParams: - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - initPgaSize: - type: integer - initSgaSize: - type: integer - isTcpsEnabled: - default: false - type: boolean - nodes: - items: - type: string - type: array - oemExpressUrl: - type: string - ordsReference: - type: string - pdbConnectString: - type: string - pdbName: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - prebuiltDB: - type: boolean - primaryDatabase: - type: string - releaseUpdate: - type: string - replicas: - type: integer - role: - type: string - sid: - type: string - standbyDatabases: - additionalProperties: - type: string - type: object - status: - type: string - tcpsConnectString: - type: string - tcpsPdbConnectString: - type: string - tcpsTlsSecret: - default: "" - type: string - required: - - isTcpsEnabled - - persistence - - tcpsTlsSecret - type: object - type: object - served: true - storage: true - subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.replicas - status: {} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: oracle-database-operator-leader-election-role - namespace: oracle-database-operator-system -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - - deployments - - events - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list -- apiGroups: - - '''''' - resources: - - statefulsets/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - configmaps - - deployments - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - replicasets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - list - - update -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods/exec - verbs: - - create -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases/status - verbs: - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - cdbs/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - events - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrests - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrests/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - lrests/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - lrpdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - lrpdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - lrpdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - pdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - monitoring.coreos.com - resources: - - prometheusrules - - servicemonitors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/finalizers - verbs: - - update -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/status - verbs: - - get - - patch - - update -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-oracle-database-operator-proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: oracle-database-operator-leader-election-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-oracle-database-operator-proxy-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager-metrics-service - namespace: oracle-database-operator-system -spec: - ports: - - name: https - port: 8443 - targetPort: https - selector: - control-plane: controller-manager ---- -apiVersion: v1 -kind: Service -metadata: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - control-plane: controller-manager ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: oracle-database-operator-serving-cert - namespace: oracle-database-operator-system -spec: - dnsNames: - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local - issuerRef: - kind: Issuer - name: oracle-database-operator-selfsigned-issuer - secretName: webhook-server-cert ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: oracle-database-operator-selfsigned-issuer - namespace: oracle-database-operator-system -spec: - selfSigned: {} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-mutating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: mdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: moraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: msingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: mcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-lrest - failurePolicy: Fail - name: mlrest.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrests - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-lrpdb - failurePolicy: Fail - name: mlrpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrpdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: mpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-validating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase - failurePolicy: Fail - name: vautonomouscontainerdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomouscontainerdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: vautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: vautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore - failurePolicy: Fail - name: vautonomousdatabaserestore.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabaserestores - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: vdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: voraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: vsingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: vcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-lrest - failurePolicy: Fail - name: vlrest.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrests - sideEffects: None -- admissionReviewVersions: - - v4 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-lrpdb - failurePolicy: Fail - name: vlrpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - lrpdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: vpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager - namespace: oracle-database-operator-system -spec: - replicas: 3 - selector: - matchLabels: - control-plane: controller-manager - template: - metadata: - labels: - control-plane: controller-manager - spec: - containers: - - args: - - --enable-leader-election - command: - - /manager - env: - - name: WATCH_NAMESPACE - value: "${OPRNAMESPACE},${PDBNAMESPACE},${CDBNAMESPACE}" - image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/mn_lrestbranch:latest - imagePullPolicy: Always - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - resources: - limits: - cpu: 400m - memory: 400Mi - requests: - cpu: 400m - memory: 400Mi - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - terminationGracePeriodSeconds: 10 - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert ---- diff --git a/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml new file mode 120000 index 00000000..bbf4775f --- /dev/null +++ b/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml @@ -0,0 +1 @@ +../../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/ords-based/usecase/parameters.txt b/docs/multitenant/ords-based/usecase/parameters.txt index 0dd66f73..3068311b 100644 --- a/docs/multitenant/ords-based/usecase/parameters.txt +++ b/docs/multitenant/ords-based/usecase/parameters.txt @@ -3,43 +3,45 @@ ## REST SERVER IMAGE ### ######################## -ORDSIMG:lin.ocir.io/intsanjaysingh/mmalvezz/testppr/ords-dboper:latest +ORDSIMG:[Enter the image build using oracle-database-operator/ords/Dockerfile] ############################## ## TNS URL FOR CDB CREATION ## ############################## -TNSALIAS:"(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" +TNSALIAS:"[cdb tns descriptor]" + +#e.g. TNSALIAS:"(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" ########################################### ## ORDS PUBLIC USER ## ########################################### -ORDPWD:Mascetti +ORDPWD:[enter ords public user passwrod] ########################################### ## SYSPASSWORD ## ########################################### -SYSPWD:WElcome_12## +SYSPWD:[enter database sys password] ####################### ## HTTPS CREDENTIAL ### ####################### -WBUSER:welcome -WBPASS:welcome1 +WBUSER:[webuser credential:username] +WBPASS:[webuser credential:password] ##################### ## PDB ADMIN USER ### ##################### -PDBUSR:Citizenkane -PDBPWD:Rosebud +PDBUSR:[Pdb admin credential: username] +PDBPWD:[Pdb admin credential: password] ##################### ## CDB ADMIN USER ### ##################### CDBUSR:C##DBAPI_CDB_ADMIN -CDBPWD:WElcome_12## +CDBPWD:[cdb admin password] ################### ### NAMESPACES #### diff --git a/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml index d5bae7bc..bbf4775f 120000 --- a/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml +++ b/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml @@ -1 +1 @@ -../../../oracle-database-operator.yaml \ No newline at end of file +../../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/lrest-based/usecase05_to_be_removed/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase03/oracle-database-operator.yaml similarity index 100% rename from docs/multitenant/lrest-based/usecase05_to_be_removed/oracle-database-operator.yaml rename to docs/multitenant/ords-based/usecase03/oracle-database-operator.yaml From d2119d164ae0eeb8c88f31f56dc88051f05b29c7 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Wed, 8 Jan 2025 13:31:53 +0000 Subject: [PATCH 148/414] typo correction --- docs/multitenant/README.md | 2 +- docs/multitenant/lrest-based/README.md | 4 ++-- docs/multitenant/ords-based/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index e7155457..43d1d9a4 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -9,5 +9,5 @@ Starting from operator version 1.2.0, there are two classes of multitenant contr - Regarding the YAML file, no payload parameters for the existing functionalities have been changed. - The **CRD** names are different: for controllers based on [ORDS](./ords-based/README.md), we have **PDB** and **CDB**, while for controllers based on [LREST](./lrest-based/README.md), we have **LRPDB** and **LREST**. - If you use an LREST-based controller, there is no need to manually create the REST server pod. The image is available for download on OCR. -- Controllers based on ORDS allow you to manage PDB parameters using kubectl. +- Controllers based on **LRSET** allow you to manage PDB parameters using kubectl. - ORDS controllers currently do not support ORDS version 24.1. diff --git a/docs/multitenant/lrest-based/README.md b/docs/multitenant/lrest-based/README.md index fb7b3b31..186f8c3e 100644 --- a/docs/multitenant/lrest-based/README.md +++ b/docs/multitenant/lrest-based/README.md @@ -18,7 +18,7 @@ - [Create secrets with encrypted password](#create-secrets-with-encrypted-password) - [Create lrest pod](#create-lrest-pod) - [Create PDB](#create-pdb) - - [pdb config map 🆕](#pdb-config-map) + - [pdb config map ](#pdb-config-map) - [Open PDB](#open-pdb) - [Close PDB](#close-pdb) - [Clone PDB](#clone-pdb) @@ -41,7 +41,7 @@ Image to run lrest controller is available on OCR. The container database can be ### Multiple namespace setup Before proceeding with controllers setup make sure that the operator is configured to work with multiple namespaces as specified in the [README](../../../README.md). -In this document each controller is running in a dedicated namespace lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains all the files reported in this document. +In this document each controller is running in a dedicated namespace: lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains all the files reported in this document. Configure the **WACTH_NAMESPACE** list of the operator yaml file diff --git a/docs/multitenant/ords-based/README.md b/docs/multitenant/ords-based/README.md index 0a7221ff..ecef82ba 100644 --- a/docs/multitenant/ords-based/README.md +++ b/docs/multitenant/ords-based/README.md @@ -150,7 +150,7 @@ In this setup example [provisioning example setup](./provisioning/example_setup_ echo -n "" | base64 ``` - **Note:** After successful creation of the CDB Resource, the CDB secrets ca be deleted from the Kubernetes system . + **Note:** After successful creation of the CDB Resource, the CDB secrets can be deleted from the Kubernetes system . ### Secrets for PDB CRD From f60849e2fd0ac091dcec5ecbd80b2d6c490c0368 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Thu, 9 Jan 2025 00:10:00 +0000 Subject: [PATCH 149/414] Tinglwan fix v4 spec --- apis/database/v4/autonomousdatabase_types.go | 10 +++--- apis/database/v4/zz_generated.deepcopy.go | 33 ++++++++----------- ...tabase.oracle.com_autonomousdatabases.yaml | 9 ++--- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/apis/database/v4/autonomousdatabase_types.go b/apis/database/v4/autonomousdatabase_types.go index de50669b..88255b3b 100644 --- a/apis/database/v4/autonomousdatabase_types.go +++ b/apis/database/v4/autonomousdatabase_types.go @@ -69,12 +69,12 @@ type AutonomousDatabaseDetails struct { type AutonomousDatabaseClone struct { AutonomousDatabaseBase `json:",inline"` - CloneType database.CreateAutonomousDatabaseCloneDetailsCloneTypeEnum `json:"id,cloneType"` + // +kubebuilder:validation:Enum:="FULL";"METADATA" + CloneType database.CreateAutonomousDatabaseCloneDetailsCloneTypeEnum `json:"cloneType,omitempty"` } // AutonomousDatabaseBase defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase type AutonomousDatabaseBase struct { - Id *string `json:"id,omitempty"` CompartmentId *string `json:"compartmentId,omitempty"` AutonomousContainerDatabase AcdSpec `json:"autonomousContainerDatabase,omitempty"` DisplayName *string `json:"displayName,omitempty"` @@ -113,7 +113,7 @@ type K8sAcdSpec struct { Name *string `json:"name,omitempty"` } -type OCIAcdSpec struct { +type OciAcdSpec struct { Id *string `json:"id,omitempty"` } @@ -121,7 +121,7 @@ type OCIAcdSpec struct { // The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup type AcdSpec struct { K8sAcd K8sAcdSpec `json:"k8sAcd,omitempty"` - OciAcd OCIAcdSpec `json:"ociAcd,omitempty"` + OciAcd OciAcdSpec `json:"ociAcd,omitempty"` } /************************ @@ -274,7 +274,7 @@ func (adb *AutonomousDatabase) UpdateStatusFromOciAdb(ociObj database.Autonomous } } -// UpdateFromOCIADB updates the attributes using database.AutonomousDatabase object +// UpdateFromOciAdb updates the attributes using database.AutonomousDatabase object func (adb *AutonomousDatabase) UpdateFromOciAdb(ociObj database.AutonomousDatabase, overwrite bool) (specChanged bool) { oldADB := adb.DeepCopy() diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index aeca28e7..78f98f1e 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -361,11 +361,6 @@ func (in *AutonomousDatabaseBackupStatus) DeepCopy() *AutonomousDatabaseBackupSt // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AutonomousDatabaseBase) DeepCopyInto(out *AutonomousDatabaseBase) { *out = *in - if in.Id != nil { - in, out := &in.Id, &out.Id - *out = new(string) - **out = **in - } if in.CompartmentId != nil { in, out := &in.CompartmentId, &out.CompartmentId *out = new(string) @@ -2301,37 +2296,37 @@ func (in *LTDESecret) DeepCopy() *LTDESecret { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIAcdSpec) DeepCopyInto(out *OCIAcdSpec) { +func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { *out = *in - if in.Id != nil { - in, out := &in.Id, &out.Id - *out = new(string) - **out = **in - } + out.Secret = in.Secret } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIAcdSpec. -func (in *OCIAcdSpec) DeepCopy() *OCIAcdSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ORDSPassword. +func (in *ORDSPassword) DeepCopy() *ORDSPassword { if in == nil { return nil } - out := new(OCIAcdSpec) + out := new(ORDSPassword) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { +func (in *OciAcdSpec) DeepCopyInto(out *OciAcdSpec) { *out = *in - out.Secret = in.Secret + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ORDSPassword. -func (in *ORDSPassword) DeepCopy() *ORDSPassword { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciAcdSpec. +func (in *OciAcdSpec) DeepCopy() *OciAcdSpec { if in == nil { return nil } - out := new(ORDSPassword) + out := new(OciAcdSpec) in.DeepCopyInto(out) return out } diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index 435eb552..ad17ed13 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -430,6 +430,11 @@ spec: type: string type: object type: object + cloneType: + enum: + - FULL + - METADATA + type: string compartmentId: type: string computeCount: @@ -460,8 +465,6 @@ spec: additionalProperties: type: string type: object - id: - type: string isAccessControlEnabled: type: boolean isAutoScalingEnabled: @@ -491,8 +494,6 @@ spec: items: type: string type: array - required: - - id type: object details: properties: From 8310c6b51b56c3d23f9c4d3b03ce52448bd321b7 Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Thu, 9 Jan 2025 13:26:39 +0530 Subject: [PATCH 150/414] Pass platform for AMD only builds also --- Makefile | 4 +- Makefile.orig | 202 -------------------------------------------------- 2 files changed, 2 insertions(+), 204 deletions(-) delete mode 100644 Makefile.orig diff --git a/Makefile b/Makefile index 684f3259..46cc1b65 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # @@ -83,7 +83,7 @@ ifeq ($(BUILD_MANIFEST), true) BUILD_ARGS := $(BUILD_ARGS) --platform=linux/arm64,linux/amd64 --jobs=2 --manifest PUSH_ARGS := manifest else -BUILD_ARGS := $(BUILD_ARGS) --tag +BUILD_ARGS := $(BUILD_ARGS) --platform=linux/amd64 --tag endif docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast docker build --no-cache=true --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ diff --git a/Makefile.orig b/Makefile.orig deleted file mode 100644 index f52c9ad6..00000000 --- a/Makefile.orig +++ /dev/null @@ -1,202 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# - -# Current Operator version -VERSION ?= 0.0.1 -# Default bundle image tag -BUNDLE_IMG ?= controller-bundle:$(VERSION) -# Options for 'bundle-build' -ifneq ($(origin CHANNELS), undefined) -BUNDLE_CHANNELS := --channels=$(CHANNELS) -endif -ifneq ($(origin DEFAULT_CHANNEL), undefined) -BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) -endif -BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) - -# Image URL to use all building/pushing image targets -IMG ?= controller:latest -# Produce CRDs that work back to Kubernetes 1.11 (no version conversion) -# API version has to be v1 to use defaulting (https://github.com/kubernetes-sigs/controller-tools/issues/478) -CRD_OPTIONS ?= "crd:maxDescLen=0" -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.29.0 -# Operator YAML file -OPERATOR_YAML=$$(basename $$(pwd)).yaml -# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) -ifeq (,$(shell go env GOBIN)) -GOBIN=$(shell go env GOPATH)/bin -else -GOBIN=$(shell go env GOBIN) -endif - -# Setting SHELL to bash allows bash commands to be executed by recipes. -# This is a requirement for 'setup-envtest.sh' in the test target. -# Options are set to exit when a recipe line exits non-zero or a piped command fails. -SHELL = /usr/bin/env bash -o pipefail -.SHELLFLAGS = -ec - -all: build -##@ Development - -manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases - -generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." - -fmt: ## Run go fmt against code. - go fmt ./... - -vet: ## Run go vet against code. - go vet ./... - -TEST ?= ./apis/database/v1alpha1 ./commons/... ./controllers/... -test: manifests generate fmt vet envtest ## Run unit tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $(TEST) -coverprofile cover.out - -E2ETEST ?= ./test/e2e/ -e2e: manifests generate fmt vet envtest ## Run e2e tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $(E2ETEST) -test.timeout 0 -test.v --ginkgo.fail-fast - -##@ Build - -build: generate fmt vet ## Build manager binary. - go build -o bin/manager main.go - -run: manifests generate fmt vet ## Run a controller from your host. - go run ./main.go - -GOLANG_VERSION ?= 1.23.3 -## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. -## Otherwise, use golang image from docker hub as the builder. -ifeq ($(BUILD_INTERNAL), true) -BUILDER_IMG = oraclelinux:9 -BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) --build-arg INSTALL_GO=true -else -BUILDER_IMG = golang:$(GOLANG_VERSION) -#BUILDER_IMG = phx.ocir.io/intsanjaysingh/db-repo/oracle/golang:1.23.3 -BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO="false" --build-arg GOLANG_VERSION=$(GOLANG_VERSION) -endif -docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast - docker build --no-cache=true --platform=linux/arm64,linux/amd64 --jobs=2 --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ - --build-arg CI_COMMIT_SHA=$(CI_COMMIT_SHA) --build-arg CI_COMMIT_BRANCH=$(CI_COMMIT_BRANCH) \ - $(BUILD_ARGS) --manifest $(IMG) . - -docker-push: ## Push docker image with the manager. - docker push $(IMG) - -##@ Deployment - -install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl apply -f - - -uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl delete -f - - -deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) - $(KUSTOMIZE) build config/default | kubectl apply -f - - -# Bug:34265574 -# Used sed to reposition the controller-manager Deployment after the certificate creation in the OPERATOR_YAML -operator-yaml: manifests kustomize - cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) - $(KUSTOMIZE) build config/default > "$(OPERATOR_YAML)" - sed -i.bak -e '/^apiVersion: apps\/v1/,/---/d' "$(OPERATOR_YAML)" - (echo --- && sed '/^apiVersion: apps\/v1/,/---/!d' "$(OPERATOR_YAML).bak") >> "$(OPERATOR_YAML)" - rm "$(OPERATOR_YAML).bak" - -undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/default | kubectl delete -f - - -##@ Build Dependencies - -## Location to install dependencies to -LOCALBIN ?= $(shell pwd)/bin -$(LOCALBIN): - mkdir -p $(LOCALBIN) - -## Tool Binaries -KUSTOMIZE ?= $(LOCALBIN)/kustomize -CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen -ENVTEST ?= $(LOCALBIN)/setup-envtest - -## Tool Versions -KUSTOMIZE_VERSION ?= v4.5.7 -CONTROLLER_TOOLS_VERSION ?= v0.16.5 - -KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" -.PHONY: kustomize -kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. -$(KUSTOMIZE): $(LOCALBIN) - curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN) - -.PHONY: controller-gen -controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. -$(CONTROLLER_GEN): $(LOCALBIN) - GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) - -.PHONY: envtest -envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. -$(ENVTEST): $(LOCALBIN) - GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest - - -.PHONY: bundle -bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files. - operator-sdk generate kustomize manifests -q - cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) - $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) - operator-sdk bundle validate ./bundle - -.PHONY: bundle-build -bundle-build: ## Build the bundle image. - docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . - -.PHONY: bundle-push -bundle-push: ## Push the bundle image. - $(MAKE) docker-push IMG=$(BUNDLE_IMG) - -.PHONY: opm -OPM = ./bin/opm -opm: ## Download opm locally if necessary. -ifeq (,$(wildcard $(OPM))) -ifeq (,$(shell which opm 2>/dev/null)) - @{ \ - set -e ;\ - mkdir -p $(dir $(OPM)) ;\ - OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.1/$${OS}-$${ARCH}-opm ;\ - chmod +x $(OPM) ;\ - } -else -OPM = $(shell which opm) -endif -endif - -# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). -# These images MUST exist in a registry and be pull-able. -BUNDLE_IMGS ?= $(BUNDLE_IMG) - -# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). -CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) - -# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. -ifneq ($(origin CATALOG_BASE_IMG), undefined) -FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) -endif - -# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. -# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: -# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator -.PHONY: catalog-build -catalog-build: opm ## Build a catalog image. - $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) - -# Push the catalog image. -.PHONY: catalog-push -catalog-push: ## Push a catalog image. - $(MAKE) docker-push IMG=$(CATALOG_IMG) From b96032ab942d3a86d185463e01bba47a9a522a1c Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Fri, 10 Jan 2025 10:30:03 +0530 Subject: [PATCH 151/414] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e1f4168e..8ac4ec17 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ onpremtest/* ords/*zip .gitattributes .vscode - +.DS_Store # development .idea .local From 39e8fa8eda236bf3154184afaf27c5846a13eb88 Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Fri, 10 Jan 2025 14:23:15 +0000 Subject: [PATCH 152/414] Ords fixes --- .../v1alpha1/oraclerestdataservice_types.go | 8 ++-- commons/database/constants.go | 4 +- .../oraclerestdataservice_controller.go | 47 ++++++++++++++++--- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/apis/database/v1alpha1/oraclerestdataservice_types.go b/apis/database/v1alpha1/oraclerestdataservice_types.go index d653c11e..7d61fdcc 100644 --- a/apis/database/v1alpha1/oraclerestdataservice_types.go +++ b/apis/database/v1alpha1/oraclerestdataservice_types.go @@ -66,7 +66,8 @@ type OracleRestDataServiceSpec struct { // +k8s:openapi-gen=true // +kubebuilder:validation:Minimum=1 - Replicas int `json:"replicas,omitempty"` + Replicas int `json:"replicas,omitempty"` + ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` } // OracleRestDataServicePersistence defines the storage releated params @@ -75,8 +76,9 @@ type OracleRestDataServicePersistence struct { StorageClass string `json:"storageClass,omitempty"` // +kubebuilder:validation:Enum=ReadWriteOnce;ReadWriteMany - AccessMode string `json:"accessMode,omitempty"` - VolumeName string `json:"volumeName,omitempty"` + AccessMode string `json:"accessMode,omitempty"` + VolumeName string `json:"volumeName,omitempty"` + SetWritePermissions *bool `json:"setWritePermissions,omitempty"` } // OracleRestDataServiceImage defines the Image source and pullSecrets for POD diff --git a/commons/database/constants.go b/commons/database/constants.go index fd549ca6..940a2727 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -368,7 +368,9 @@ const UninstallORDSCMD string = "\numask 177" + "\nrm -rf /opt/oracle/ords/config/ords/standalone" + "\nrm -rf /opt/oracle/ords/config/ords/apex" -const GetORDSStatus string = "curl -sSkv -k -X GET http://localhost:8181/ords/_/db-api/stable/metadata-catalog/" +const GetORDSStatus string = "curl -sSkvf -k -X GET http://localhost:8181/ords/_/db-api/stable/metadata-catalog/" + +const ORDSReadinessProbe string = "curl -sSkvf -k -X GET http://localhost:8181/ords/_/landing" const ValidateAdminPassword string = "conn sys/\\\"%s\\\"@${ORACLE_SID} as sysdba\nshow user" diff --git a/controllers/database/oraclerestdataservice_controller.go b/controllers/database/oraclerestdataservice_controller.go index 4e26676e..ba23a491 100644 --- a/controllers/database/oraclerestdataservice_controller.go +++ b/controllers/database/oraclerestdataservice_controller.go @@ -382,7 +382,7 @@ func (r *OracleRestDataServiceReconciler) checkHealthStatus(m *dbapi.OracleRestD return requeueY, readyPod } if readyPod.Name == "" { - m.Status.Status = dbcommons.StatusNotReady + m.Status.Status = dbcommons.StatusPending return requeueY, readyPod } @@ -401,7 +401,7 @@ func (r *OracleRestDataServiceReconciler) checkHealthStatus(m *dbapi.OracleRestD } } - m.Status.Status = dbcommons.StatusNotReady + m.Status.Status = dbcommons.StatusUpdating if strings.Contains(out, "HTTP/1.1 200 OK") || strings.Contains(strings.ToUpper(err.Error()), "HTTP/1.1 200 OK") { if n.Status.Status == dbcommons.StatusReady || n.Status.Status == dbcommons.StatusUpdating || n.Status.Status == dbcommons.StatusPatching { m.Status.Status = dbcommons.StatusReady @@ -423,7 +423,7 @@ func (r *OracleRestDataServiceReconciler) checkHealthStatus(m *dbapi.OracleRestD } } } - if m.Status.Status == dbcommons.StatusNotReady { + if m.Status.Status == dbcommons.StatusUpdating { return requeueY, readyPod } return requeueN, readyPod @@ -554,8 +554,25 @@ func (r *OracleRestDataServiceReconciler) instantiatePodSpec(m *dbapi.OracleRest }, }, }, - InitContainers: []corev1.Container{ - { + InitContainers: func() []corev1.Container{ + initContainers := []corev1.Container{} + if m.Spec.Persistence.Size != "" && m.Spec.Persistence.SetWritePermissions != nil && *m.Spec.Persistence.SetWritePermissions { + initContainers = append(initContainers, corev1.Container{ + Name: "init-permissions", + Image: m.Spec.Image.PullFrom, + Command: []string{"/bin/sh", "-c", fmt.Sprintf("chown %d:%d /etc/ords/config/ || true", int(dbcommons.ORACLE_UID), int(dbcommons.DBA_GUID))}, + SecurityContext: &corev1.SecurityContext{ + // User ID 0 means, root user + RunAsUser: func() *int64 { i := int64(0); return &i }(), + }, + VolumeMounts: []corev1.VolumeMount{{ + MountPath: "/etc/ords/config/", + Name: "datamount", + }}, + }) + } + + initContainers = append(initContainers, corev1.Container{ Name: "init-ords", Image: m.Spec.Image.PullFrom, Command: []string{"/bin/sh"}, @@ -573,8 +590,9 @@ func (r *OracleRestDataServiceReconciler) instantiatePodSpec(m *dbapi.OracleRest Name: "varmount", }, }, - }, - }, + }) + return initContainers + }(), Containers: []corev1.Container{{ Name: m.Name, Image: m.Spec.Image.PullFrom, @@ -591,6 +609,21 @@ func (r *OracleRestDataServiceReconciler) instantiatePodSpec(m *dbapi.OracleRest } return ports }(), + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"/bin/sh", "-c", dbcommons.ORDSReadinessProbe}, + }, + }, + InitialDelaySeconds: 20, + TimeoutSeconds: 20, + PeriodSeconds: func() int32 { + if m.Spec.ReadinessCheckPeriod > 0 { + return int32(m.Spec.ReadinessCheckPeriod) + } + return 60 + }(), + }, VolumeMounts: []corev1.VolumeMount{ { MountPath: "/etc/ords/config/", From 7bfdd24c132449bf79b1ae745e81971e949fbb5f Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Mon, 13 Jan 2025 15:22:07 +0000 Subject: [PATCH 153/414] support sidb truecache --- .../v1alpha1/singleinstancedatabase_types.go | 9 +- .../singleinstancedatabase_webhook.go | 12 ++ .../samples/sidb/singleinstancedatabase.yaml | 12 +- ...singleinstancedatabase_free-truecache.yaml | 51 ++++++++ .../singleinstancedatabase_controller.go | 109 +++++++++++++----- docs/sidb/README.md | 7 ++ 6 files changed, 167 insertions(+), 33 deletions(-) create mode 100644 config/samples/sidb/singleinstancedatabase_free-truecache.yaml diff --git a/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index 810f2e10..36125d37 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -72,10 +72,11 @@ type SingleInstanceDatabaseSpec struct { TcpsTlsSecret string `json:"tcpsTlsSecret,omitempty"` PrimaryDatabaseRef string `json:"primaryDatabaseRef,omitempty"` - // +kubebuilder:validation:Enum=primary;standby;clone - CreateAs string `json:"createAs,omitempty"` - ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` - ServiceAccountName string `json:"serviceAccountName,omitempty"` + // +kubebuilder:validation:Enum=primary;standby;clone;truecache + CreateAs string `json:"createAs,omitempty"` + ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty"` + TrueCacheServices []string `json:"trueCacheServices,omitempty"` // +k8s:openapi-gen=true Replicas int `json:"replicas,omitempty"` diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index e5870a68..bc095f7c 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -135,6 +135,10 @@ func (r *SingleInstanceDatabase) Default() { r.Spec.Replicas = 1 } } + + if r.Spec.TrueCacheServices == nil { + r.Spec.TrueCacheServices = make([]string, 0) + } } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. @@ -293,6 +297,14 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { } } + if r.Spec.CreateAs != "truecache" { + if len(r.Spec.TrueCacheServices) > 0 { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("trueCacheServices"), r.Spec.TrueCacheServices, + "Creation of trueCacheServices only supported with True Cache instances")) + } + } + if r.Status.FlashBack == "true" && r.Spec.FlashBack != nil && *r.Spec.FlashBack { if r.Spec.ArchiveLog != nil && !*r.Spec.ArchiveLog { allErrs = append(allErrs, diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index b24f517d..23a6fb73 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -17,8 +17,7 @@ spec: edition: enterprise ## Type of database. - ## Valid values for createAs are primary, clone or standby - ## Valid only for enterprise and standard editions + ## Valid values for createAs are primary, clone, standby or truecache createAs: primary ## Specify true to convert this standby to a snapshot standby @@ -26,9 +25,16 @@ spec: convertToSnapshotStandby: false ## Reference to a source primary database. - ## Valid only for createAs clone or standby + ## Valid only when createAs is clone, standby or truecache ## The name of a source primary database resource from the same namespace primaryDatabaseRef: "" + + ## Only valid when createAs is set to truecache + ## Accepts a semi colon separated map of `PRIMARY_PDB_SERIVCE_NAME:PRIMARY_SERVICE_NAME:TRUECACHE_SERVICE_NAME` + trueCacheServices: + # - "FREEPDB1:sales1:sales1_tc" + # - "FREEPDB1:sales2:sales2_tc" + # - "FREEPDB1:sales3:sales3_tc" ## Secret containing SIDB password mapped to secretKey. secretKey defaults to oracle_pwd ## Should refer to adminPassword of Source DB if createAs is clone or standby diff --git a/config/samples/sidb/singleinstancedatabase_free-truecache.yaml b/config/samples/sidb/singleinstancedatabase_free-truecache.yaml new file mode 100644 index 00000000..13377427 --- /dev/null +++ b/config/samples/sidb/singleinstancedatabase_free-truecache.yaml @@ -0,0 +1,51 @@ +# +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# + +apiVersion: database.oracle.com/v1alpha1 +kind: SingleInstanceDatabase +metadata: + name: truecache-sample + namespace: default +spec: + + ## Use only alphanumeric characters for sid + sid: FREE + + ## DB edition + edition: free + + ## DB Type + createAs: truecache + + ## Reference to the source primary database. + primaryDatabaseRef: "freedb-sample" + + ## Accepts a semi colon separated list of `PRIMARY_PDB_SERIVCE_NAME:PRIMARY_SERVICE_NAME:TRUECACHE_SERVICE_NAME` + trueCacheServices: + # - "FREEPDB1:sales1:sales1_tc" + # - "FREEPDB1:sales2:sales2_tc" + # - "FREEPDB1:sales3:sales3_tc" + + ## Secret containing SIDB password mapped to secretKey + adminPassword: + secretName: freedb-admin-secret + + ## Database image details + image: + ## Oracle True Cache is only supported with 23ai + pullFrom: container-registry.oracle.com/database/free:latest + + + ## size is the required minimum size of the persistent volume + ## storageClass is specified for automatic volume provisioning + ## accessMode can only accept one of ReadWriteOnce, ReadWriteMany + persistence: + size: 50Gi + ## oci-bv applies to OCI block volumes. Use "standard" storageClass for dynamic provisioning in Minikube. Update as appropriate for other cloud service providers + storageClass: "oci-bv" + accessMode: "ReadWriteOnce" + + ## Count of Database Pods. Should be 1 for free edition. + replicas: 1 diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 5f8902b2..1e78424d 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -482,8 +482,20 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab m.Status.Pdbname = m.Spec.Pdbname m.Status.Persistence = m.Spec.Persistence m.Status.PrebuiltDB = m.Spec.Image.PrebuiltDB - + if m.Spec.CreateAs == "truecache" { + // Fetch the Primary database reference, required for all iterations + err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: m.Spec.PrimaryDatabaseRef}, rp) + if err != nil { + if apierrors.IsNotFound(err) { + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, err.Error()) + r.Log.Info(err.Error()) + return requeueN, err + } + return requeueY, err + } + } if m.Spec.CreateAs == "clone" { + // Once a clone database has created , it has no link with its reference if m.Status.DatafilesCreated == "true" || !dbcommons.IsSourceDatabaseOnCluster(m.Spec.PrimaryDatabaseRef) { @@ -920,6 +932,49 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns return mounts }(), Env: func() []corev1.EnvVar { + if m.Spec.CreateAs == "truecache" { + return []corev1.EnvVar{ + { + Name: "SVC_HOST", + Value: m.Name, + }, + { + Name: "SVC_PORT", + Value: strconv.Itoa(int(dbcommons.CONTAINER_LISTENER_PORT)), + }, + { + Name: "ORACLE_CHARACTERSET", + Value: m.Spec.Charset, + }, + { + Name: "ORACLE_EDITION", + Value: m.Spec.Edition, + }, + { + Name: "TRUE_CACHE", + Value: "true", + }, + { + Name: "PRIMARY_DB_CONN_STR", + Value: func() string { + if dbcommons.IsSourceDatabaseOnCluster(m.Spec.PrimaryDatabaseRef) { + return rp.Name + ":" + strconv.Itoa(int(dbcommons.CONTAINER_LISTENER_PORT)) + "/" + rp.Spec.Sid + } + return m.Spec.PrimaryDatabaseRef + }(), + }, + { + Name: "PDB_TC_SVCS", + Value: func() string { + return strings.Join(m.Spec.TrueCacheServices, ";") + }(), + }, + { + Name: "ORACLE_HOSTNAME", + Value: m.Name, + }, + } + } // adding XE support, useful for dev/test/CI-CD if m.Spec.Edition == "express" || m.Spec.Edition == "free" { return []corev1.EnvVar{ @@ -1817,32 +1872,34 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplaceSVC(ctx context.Contex if pdbName == "" { pdbName = strings.ToUpper(m.Spec.Pdbname) } - if m.Spec.LoadBalancer { - m.Status.ClusterConnectString = extSvc.Name + "." + extSvc.Namespace + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) - if len(extSvc.Status.LoadBalancer.Ingress) > 0 { - // 'lbAddress' will contain the Fully Qualified Hostname of the LB. If the hostname is not available it will contain the IP address of the LB - lbAddress := extSvc.Status.LoadBalancer.Ingress[0].Hostname - if lbAddress == "" { - lbAddress = extSvc.Status.LoadBalancer.Ingress[0].IP - } - m.Status.ConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) - m.Status.PdbConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(pdbName) - oemExpressUrl = "https://" + lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[0].Port) + "/em" - if m.Spec.EnableTCPS { - m.Status.TcpsConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].Port) + "/" + strings.ToUpper(sid) - m.Status.TcpsPdbConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].Port) + "/" + strings.ToUpper(pdbName) + if m.Spec.CreateAs != "truecache" { + if m.Spec.LoadBalancer { + m.Status.ClusterConnectString = extSvc.Name + "." + extSvc.Namespace + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) + if len(extSvc.Status.LoadBalancer.Ingress) > 0 { + // 'lbAddress' will contain the Fully Qualified Hostname of the LB. If the hostname is not available it will contain the IP address of the LB + lbAddress := extSvc.Status.LoadBalancer.Ingress[0].Hostname + if lbAddress == "" { + lbAddress = extSvc.Status.LoadBalancer.Ingress[0].IP + } + m.Status.ConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) + m.Status.PdbConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(pdbName) + oemExpressUrl = "https://" + lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[0].Port) + "/em" + if m.Spec.EnableTCPS { + m.Status.TcpsConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].Port) + "/" + strings.ToUpper(sid) + m.Status.TcpsPdbConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].Port) + "/" + strings.ToUpper(pdbName) + } } - } - } else { - m.Status.ClusterConnectString = extSvc.Name + "." + extSvc.Namespace + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) - nodeip := dbcommons.GetNodeIp(r, ctx, req) - if nodeip != "" { - m.Status.ConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[1].NodePort) + "/" + strings.ToUpper(sid) - m.Status.PdbConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[1].NodePort) + "/" + strings.ToUpper(pdbName) - oemExpressUrl = "https://" + nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[0].NodePort) + "/em" - if m.Spec.EnableTCPS { - m.Status.TcpsConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].NodePort) + "/" + strings.ToUpper(sid) - m.Status.TcpsPdbConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].NodePort) + "/" + strings.ToUpper(pdbName) + } else { + m.Status.ClusterConnectString = extSvc.Name + "." + extSvc.Namespace + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) + nodeip := dbcommons.GetNodeIp(r, ctx, req) + if nodeip != "" { + m.Status.ConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[1].NodePort) + "/" + strings.ToUpper(sid) + m.Status.PdbConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[1].NodePort) + "/" + strings.ToUpper(pdbName) + oemExpressUrl = "https://" + nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[0].NodePort) + "/em" + if m.Spec.EnableTCPS { + m.Status.TcpsConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].NodePort) + "/" + strings.ToUpper(sid) + m.Status.TcpsPdbConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].NodePort) + "/" + strings.ToUpper(pdbName) + } } } } diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 1ac0c2fe..6caf7d16 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -13,6 +13,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst * [XE Database](#xe-database) * [Free Database](#free-database) * [Free Lite Database](#free-lite-database) + * [Oracle True Cache](#oracle-true-cache) * [Connecting to Database](#connecting-to-database) * [Database Persistence (Storage) Configuration Options](#database-persistence-storage-configuration-options) * [Dynamic Persistence](#dynamic-persistence) @@ -310,6 +311,12 @@ This command pulls the Free lite image available in [Oracle Container Registry]( - For Free database, you **cannot change** the init parameters i.e. `cpuCount, processes, sgaTarget or pgaAggregateTarget`. - Oracle Enterprise Manager Express (OEM Express) is not supported from release 23.3.0 and later releases. +#### Oracle True Cache +Oracle True Cache is an in-memory, consistent, and automatically managed cache for Oracle Database. +To provision a True Cache instance for the Oracle Free Database in Kubernetes, use the sample **[config/samples/sidb/singleinstancedatabase_free-truecache.yaml](../../config/samples/sidb/singleinstancedatabase_free-truecache.yaml)** file. For example + + kubectl apply -f singleinstancedatabase_free-truecache.yaml + #### Additional Information You are required to specify the database admin password secret in the corresponding YAML file. The default values mentioned in the `adminPassword.secretName` fields of [singleinstancedatabase_create.yaml](../../config/samples/sidb/singleinstancedatabase_create.yaml), [singleinstancedatabase_prebuiltdb.yaml](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml), [singleinstancedatabase_express.yaml](../../config/samples/sidb/singleinstancedatabase_express.yaml) and [singleinstancedatabse_free.yaml](../../config/samples/sidb/singleinstancedatabase_free.yaml) files are `db-admin-secret`, `prebuiltdb-admin-secret`, `xedb-admin-secret` and `free-admin-secret` respectively. You can create these secrets manually by using the sample command mentioned in the [Template YAML](#template-yaml) section. Alternatively, you can create these secrets by filling the passwords in the **[singleinstancedatabase_secrets.yaml](../../config/samples/sidb/singleinstancedatabase_secrets.yaml)** file and applying it using the command below: From efb04cfb750ca135e553c550765a18b2c8e5d834 Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Wed, 15 Jan 2025 18:29:14 +0000 Subject: [PATCH 154/414] Kms master --- apis/database/v4/dbcssystem_types.go | 8 +- commons/dbcssystem/dbcs_reconciler.go | 94 ++++++++----- .../database.oracle.com_dbcssystems.yaml | 6 +- controllers/database/dbcssystem_controller.go | 2 +- docs/dbcs/README.md | 3 - .../bind_to_existing_dbcs_system.yaml | 2 +- docs/dbcs/provisioning/clone_dbcs_system.yaml | 4 +- .../clone_dbcs_system_from_backup.yaml | 4 +- .../clone_dbcs_system_from_database.yaml | 4 +- .../provisioning/clone_from_backup_dbcs.md | 2 +- docs/dbcs/provisioning/clone_from_database.md | 2 +- .../dbcs/provisioning/create_dbcs_with_kms.md | 44 ++++-- ...reatepdb_in_existing_dbcs_system_list.yaml | 2 +- .../dbcs_controller_parameters.md | 2 +- .../dbcs_service_migrate_to_kms.log | 132 ++++++++++++++++++ .../dbcs_service_migrate_to_kms.yaml | 21 +-- .../dbcs_service_with_2_node_rac.md | 2 +- .../dbcs_service_with_2_node_rac.yaml | 4 +- .../dbcs_service_with_all_parameters_asm.md | 2 +- .../dbcs_service_with_all_parameters_asm.yaml | 6 +- .../dbcs_service_with_all_parameters_lvm.md | 2 +- .../dbcs_service_with_all_parameters_lvm.yaml | 4 +- .../provisioning/dbcs_service_with_kms.yaml | 23 ++- .../dbcs_service_with_kms_sample_output.log | 92 +++++++++++- .../dbcs_service_with_minimal_parameters.yaml | 4 +- .../provisioning/dbcs_service_with_pdb.yaml | 2 +- docs/dbcs/provisioning/migrate_to_kms.md | 11 +- .../scale_down_dbcs_system_shape.md | 2 +- .../scale_down_dbcs_system_shape.yaml | 4 +- .../scale_up_dbcs_system_shape.md | 2 +- .../scale_up_dbcs_system_shape.yaml | 4 +- docs/dbcs/provisioning/scale_up_storage.md | 2 +- docs/dbcs/provisioning/scale_up_storage.yaml | 4 +- docs/dbcs/provisioning/update_license.md | 2 +- docs/dbcs/provisioning/update_license.yaml | 4 +- oracle-database-operator.yaml | 6 +- 36 files changed, 381 insertions(+), 133 deletions(-) create mode 100644 docs/dbcs/provisioning/dbcs_service_migrate_to_kms.log diff --git a/apis/database/v4/dbcssystem_types.go b/apis/database/v4/dbcssystem_types.go index c8b6d632..9810a3b7 100644 --- a/apis/database/v4/dbcssystem_types.go +++ b/apis/database/v4/dbcssystem_types.go @@ -85,7 +85,7 @@ type DbSystemDetails struct { Domain string `json:"domain,omitempty"` InitialDataStorageSizeInGB int `json:"initialDataStorageSizeInGB,omitempty"` ClusterName string `json:"clusterName,omitempty"` - DbAdminPaswordSecret string `json:"dbAdminPaswordSecret"` + DbAdminPasswordSecret string `json:"dbAdminPasswordSecret"` DbName string `json:"dbName,omitempty"` PdbName string `json:"pdbName,omitempty"` DbDomain string `json:"dbDomain,omitempty"` @@ -167,7 +167,7 @@ type VmNetworkDetails struct { // DbCloneConfig defines the configuration for the database clone type DbCloneConfig struct { - DbAdminPaswordSecret string `json:"dbAdminPaswordSecret,omitempty"` + DbAdminPasswordSecret string `json:"dbAdminPasswordSecret,omitempty"` TdeWalletPasswordSecret string `json:"tdeWalletPasswordSecret,omitempty"` DbName string `json:"dbName"` HostName string `json:"hostName"` @@ -201,8 +201,8 @@ type DbCloneStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:resource:path=dbcssystems,scope=Namespaced - // +kubebuilder:storageversion - // +kubebuilder:storageversion +// +kubebuilder:storageversion +// +kubebuilder:storageversion // DbcsSystem is the Schema for the dbcssystems API type DbcsSystem struct { diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 152d4f6a..9978c1ab 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -201,7 +201,7 @@ func CloneAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient da // tdePassword := "" logger.Info("Starting the clone process for DBCS", "dbcs", dbcs) // Get the admin password from Kubernetes secret - if dbcs.Spec.DbClone.DbAdminPaswordSecret != "" { + if dbcs.Spec.DbClone.DbAdminPasswordSecret != "" { dbAdminPassword, err = GetCloningAdminPassword(kubeClient, dbcs) if err != nil { logger.Error(err, "Failed to get DB Admin password") @@ -352,7 +352,7 @@ func CloneFromBackupAndGetDbcsId(logger logr.Logger, kubeClient client.Client, d return "", err } // Get the admin password from Kubernetes secret - if dbcs.Spec.DbClone.DbAdminPaswordSecret != "" { + if dbcs.Spec.DbClone.DbAdminPasswordSecret != "" { dbAdminPassword, err = GetCloningAdminPassword(kubeClient, dbcs) if err != nil { logger.Error(err, "Failed to get DB Admin password") @@ -455,7 +455,7 @@ func CloneFromDatabaseAndGetDbcsId(logger logr.Logger, kubeClient client.Client, logger.Info("Starting the clone process for Database", "dbcs", dbcs) // Get the admin password from Kubernetes secret - if dbcs.Spec.DbClone.DbAdminPaswordSecret != "" { + if dbcs.Spec.DbClone.DbAdminPasswordSecret != "" { dbAdminPassword, err = GetCloningAdminPassword(kubeClient, dbcs) if err != nil { logger.Error(err, "Failed to get DB Admin password") @@ -579,12 +579,12 @@ func CloneFromDatabaseAndGetDbcsId(logger logr.Logger, kubeClient client.Client, // Get admin password from Secret then OCI valut secret func GetCloningAdminPassword(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (string, error) { - if dbcs.Spec.DbClone.DbAdminPaswordSecret != "" { + if dbcs.Spec.DbClone.DbAdminPasswordSecret != "" { // Get the Admin Secret adminSecret := &corev1.Secret{} err := kubeClient.Get(context.TODO(), types.NamespacedName{ Namespace: dbcs.GetNamespace(), - Name: dbcs.Spec.DbClone.DbAdminPaswordSecret, + Name: dbcs.Spec.DbClone.DbAdminPasswordSecret, }, adminSecret) if err != nil { @@ -605,12 +605,12 @@ func GetCloningAdminPassword(kubeClient client.Client, dbcs *databasev4.DbcsSyst // Get admin password from Secret then OCI valut secret func GetAdminPassword(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (string, error) { - if dbcs.Spec.DbSystem.DbAdminPaswordSecret != "" { + if dbcs.Spec.DbSystem.DbAdminPasswordSecret != "" { // Get the Admin Secret adminSecret := &corev1.Secret{} err := kubeClient.Get(context.TODO(), types.NamespacedName{ Namespace: dbcs.GetNamespace(), - Name: dbcs.Spec.DbSystem.DbAdminPaswordSecret, + Name: dbcs.Spec.DbSystem.DbAdminPasswordSecret, }, adminSecret) if err != nil { @@ -1117,7 +1117,7 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d return nil } dbAdminPassword := "" - if dbcs.Spec.DbSystem.DbAdminPaswordSecret != "" { + if dbcs.Spec.DbSystem.DbAdminPasswordSecret != "" { dbAdminPassword, err = GetAdminPassword(kubeClient, dbcs) if err != nil { log.Error(err, "Failed to get DB Admin password") @@ -1147,13 +1147,10 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d if dbAdminPassword != "" { migrateRequest.AdminPassword = common.String(dbAdminPassword) } - // // Wait for the database to reach the desired state after migration - // err = WaitForDatabaseState(log, dbClient, databaseID, "AVAILABLE") - // if err != nil { - // log.Error(err, "Database did not reach the desired state after migration") - // return err - // } - + // Change the phase to "Updating" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { + return statusErr + } // Send the request migrateResponse, err := dbClient.MigrateVaultKey(context.TODO(), migrateRequest) if err != nil { @@ -1169,15 +1166,22 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d log.Info("MigrateVaultKey request succeeded, waiting for database to reach the desired state") - // // Wait for the database to reach the desired state after migration - // err = WaitForDatabaseState(log, dbClient, databaseID, "AVAILABLE") - // if err != nil { - // log.Error(err, "Database did not reach the desired state after migration") - // return err - // } + // // Wait for the database to reach the desired state after migration, timeout for 2 hours + // Define timeout and check interval + timeout := 2 * time.Hour + checkInterval := 1 * time.Minute + + err = WaitForDatabaseState(log, dbClient, databaseID, "AVAILABLE", timeout, checkInterval) + if err != nil { + log.Error(err, "Database did not reach the desired state within the timeout period") + return err + } + // Change the phase to "Available" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Available, nwClient, wrClient); statusErr != nil { + return statusErr + } log.Info("KMS migration process completed successfully") - updateFlag = true } } @@ -1206,36 +1210,50 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d return nil } -func WaitForDatabaseState(log logr.Logger, dbClient database.DatabaseClient, dbHomeID string, desiredState database.DbHomeLifecycleStateEnum) error { +func WaitForDatabaseState( + log logr.Logger, + dbClient database.DatabaseClient, + databaseId string, + desiredState database.DbHomeLifecycleStateEnum, + timeout time.Duration, + checkInterval time.Duration, +) error { + // Set a deadline for the timeout deadline := time.Now().Add(timeout) - client, err := database.NewDatabaseClientWithConfigurationProvider(common.DefaultConfigProvider()) - if err != nil { - log.Error(err, "Failed to get DBHome") - return err - } + + log.Info("Starting to wait for the database to reach the desired state", "DatabaseID", databaseId, "DesiredState", desiredState, "Timeout", timeout) + for time.Now().Before(deadline) { - dbHomeReq := database.GetDbHomeRequest{ - DbHomeId: common.String(dbHomeID), + // Prepare the request to fetch database details + getDatabaseReq := database.GetDatabaseRequest{ + DatabaseId: &databaseId, } - log.Info("Sending GetDbHome request", "dbHomeID", dbHomeID) - - dbHomeResp, err := client.GetDbHome(context.TODO(), dbHomeReq) + // Fetch database details + databaseResp, err := dbClient.GetDatabase(context.TODO(), getDatabaseReq) if err != nil { - log.Error(err, "Failed to get DBHome") + log.Error(err, "Failed to get database details", "DatabaseID", databaseId) return err } - if dbHomeResp.DbHome.LifecycleState == desiredState { - log.Info("DBHome reached desired state", "DBHomeID", dbHomeID, "State", desiredState) + // Log the current database state + log.Info("Database State", "DatabaseID", databaseId, "CurrentState", databaseResp.LifecycleState) + + // Check if the database has reached the desired state + if databaseResp.LifecycleState == database.DatabaseLifecycleStateEnum(desiredState) { + log.Info("Database reached the desired state", "DatabaseID", databaseId, "State", desiredState) return nil } - log.Info("Waiting for DBHome to reach desired state", "DBHomeID", dbHomeID, "CurrentState", dbHomeResp.DbHome.LifecycleState, "DesiredState", desiredState) + // Wait for the specified interval before checking again + log.Info("Database not in the desired state yet, waiting...", "DatabaseID", databaseId, "CurrentState", databaseResp.LifecycleState, "DesiredState", desiredState, "NextCheckIn", checkInterval) time.Sleep(checkInterval) } - return fmt.Errorf("timed out waiting for DBHome to reach desired state: %s", desiredState) + // Return an error if the timeout is reached + err := fmt.Errorf("timed out waiting for database to reach the desired state: %s", desiredState) + log.Error(err, "Timeout reached while waiting for the database to reach the desired state", "DatabaseID", databaseId) + return err } func UpdateDbcsSystemId(kubeClient client.Client, dbcs *databasev4.DbcsSystem) error { diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index a8a9e6c9..468d7612 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -405,7 +405,7 @@ spec: type: string dbClone: properties: - dbAdminPaswordSecret: + dbAdminPasswordSecret: type: string dbDbUniqueName: type: string @@ -456,7 +456,7 @@ spec: type: string cpuCoreCount: type: integer - dbAdminPaswordSecret: + dbAdminPasswordSecret: type: string dbBackupConfig: properties: @@ -537,7 +537,7 @@ spec: required: - availabilityDomain - compartmentId - - dbAdminPaswordSecret + - dbAdminPasswordSecret - hostName - shape - subnetId diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index a30e49ce..1fd94dde 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -268,7 +268,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("Created vault Id", existingVaultId) } else { // Optionally, perform additional checks or operations if needed - r.Logger.Info("Found existing active vault with displayName to migrate existing database", "DisplayName", displayName, "VaultId", *existingVaultId) + r.Logger.Info("Found existing active vault with displayName", "DisplayName", displayName, "VaultId", *existingVaultId) dbcsInst.Status.KMSDetailsStatus.VaultId = *existingVaultId dbcsInst.Status.KMSDetailsStatus.ManagementEndpoint = *existingVaultManagementEndpoint } diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md index d5b6f4dd..fda76d7e 100644 --- a/docs/dbcs/README.md +++ b/docs/dbcs/README.md @@ -161,9 +161,6 @@ The key's randomart image is: [root@test-server DBCS]# kubectl create secret generic oci-publickey --from-file=publickey=/root/DBCS/id_rsa.pub ``` - - - # Use Cases to manage the lifecycle of an OCI DBCS System with Oracle DB Operator DBCS Controller For more informatoin about the multiple use cases available to you to deploy and manage the OCI BDBCS Service-based database using the Oracle DB Operator DBCS Controller, review this list: diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml index 9fd60a17..6ff24bc8 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml @@ -4,5 +4,5 @@ metadata: name: dbcssystem-existing spec: id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" diff --git a/docs/dbcs/provisioning/clone_dbcs_system.yaml b/docs/dbcs/provisioning/clone_dbcs_system.yaml index 5f52d8e2..fd6cc1d4 100644 --- a/docs/dbcs/provisioning/clone_dbcs_system.yaml +++ b/docs/dbcs/provisioning/clone_dbcs_system.yaml @@ -5,11 +5,11 @@ metadata: namespace: default spec: id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyaqui4hoqdyzmzl65jwkncyp3bnohengniqienetsdzw2q" - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" setupDBCloning: true dbClone: - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" dbName: "db1212" hostName: "host1213" displayName: "dbsystem01312" diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_backup.yaml b/docs/dbcs/provisioning/clone_dbcs_system_from_backup.yaml index f5d9a393..54280af9 100644 --- a/docs/dbcs/provisioning/clone_dbcs_system_from_backup.yaml +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_backup.yaml @@ -4,12 +4,12 @@ metadata: name: dbcssystem-clone namespace: default spec: - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" setupDBCloning: true dbBackupId: "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyaae3fmnpacavkuwt2zqaj5q3gol2g6m6tirriveytoarq" dbClone: - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" tdeWalletPasswordSecret: "tde-password" dbName: "db1212" hostName: "host1213" diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_database.yaml b/docs/dbcs/provisioning/clone_dbcs_system_from_database.yaml index 53e4bf83..40767739 100644 --- a/docs/dbcs/provisioning/clone_dbcs_system_from_database.yaml +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_database.yaml @@ -5,11 +5,11 @@ metadata: namespace: default spec: databaseId: "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyapxtsgw6hy3kyosmrawefq2csm4kjv4d5au7biuiaabsq" - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" setupDBCloning: true dbClone: - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" tdeWalletPasswordSecret: "tde-password" dbName: "db1212" hostName: "host1213" diff --git a/docs/dbcs/provisioning/clone_from_backup_dbcs.md b/docs/dbcs/provisioning/clone_from_backup_dbcs.md index b511e153..80378398 100644 --- a/docs/dbcs/provisioning/clone_from_backup_dbcs.md +++ b/docs/dbcs/provisioning/clone_from_backup_dbcs.md @@ -12,7 +12,7 @@ This example uses `clone_dbcs_system_from_backup.yaml` to clone a Single Instanc - OCI Secret as `oci-privatekey` - setupDBCloning: as `true` - OCID of Backup DB as `dbBackupId` of existing DBCS system. -- Specification for DB Cloning as `dbClone`-> `dbAdminPaswordSecret`,`tdeWalletPasswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`, `initialDataStorageSizeInGB` +- Specification for DB Cloning as `dbClone`-> `dbAdminPasswordSecret`,`tdeWalletPasswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`, `initialDataStorageSizeInGB` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). Use the file: [clone_dbcs_system_from_backup.yaml](./clone_dbcs_system_from_backup.yaml) for this use case as below: diff --git a/docs/dbcs/provisioning/clone_from_database.md b/docs/dbcs/provisioning/clone_from_database.md index 049593fc..758f0739 100644 --- a/docs/dbcs/provisioning/clone_from_database.md +++ b/docs/dbcs/provisioning/clone_from_database.md @@ -11,7 +11,7 @@ This example uses `clone_dbcs_system_from_database.yaml` to clone a Single Insta - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - setupDBCloning: as `true` -- Specification of dbClone as - Details of new DB system for cloning `dbAdminPaswordSecret`,`tdeWalletPasswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`, `initialDataStorageSizeInGB` +- Specification of dbClone as - Details of new DB system for cloning `dbAdminPasswordSecret`,`tdeWalletPasswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`, `initialDataStorageSizeInGB` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). Use the file: [clone_dbcs_system_from_database.yaml](./clone_dbcs_system_from_database.yaml) for this use case as below: diff --git a/docs/dbcs/provisioning/create_dbcs_with_kms.md b/docs/dbcs/provisioning/create_dbcs_with_kms.md index 42d00a66..852a1db8 100644 --- a/docs/dbcs/provisioning/create_dbcs_with_kms.md +++ b/docs/dbcs/provisioning/create_dbcs_with_kms.md @@ -4,25 +4,51 @@ In this use case, an OCI DBCS system is deployed using Oracle DB Operator DBCS c **NOTE** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +## Pre-requisites for KMS Vaults related to DBCS System +There is also other set of pre-requisites for KMS Vaults related to dynamic group and policies. Please follow instructions for same. +1. Create Dynamic group with rule `ALL {resource.compartment.id =` and give it some name. +2. Create policy in your compartment for this dynamic group to access to key/vaults by database. + +```txt +Allow dynamic-group <> to manage secret-family in compartment <> +Allow dynamic-group <> to manage instance-family in compartment <> +Allow dynamic-group <> to manage database-family in compartment <> +Allow dynamic-group <> to manage keys in compartment <> +Allow dynamic-group <> to manage vaults in compartment <> +``` + +E.g + +```txt +ALL {resource.compartment.id = 'ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a'} +``` +```txt +Allow dynamic-group db_dynamic_group to manage secret-family in compartment sauahuja +Allow dynamic-group db_dynamic_group to manage instance-family in compartment sauahuja +Allow dynamic-group db_dynamic_group to manage database-family in compartment sauahuja +Allow dynamic-group db_dynamic_group to manage keys in compartment sauahuja +Allow dynamic-group db_dynamic_group to manage vaults in compartment sauahuja +``` +3. Do also create KMS Vault and KMS Key in order to use it during DBCS provisioning. We are going to refer those variables (`vaultName`, `keyName`) in the yaml file. This example uses `dbcs_service_with_kms.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:US-ASHBURN-AD-1` +- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` -- Database Name as `dbsystem0130` -- Oracle Database Software Image Version as `21c` +- Database Name as `kmsdb` +- Oracle Database Software Image Version as `19c` - Database Workload Type as Transaction Processing i.e. `OLTP` -- Database Hostname Prefix as `host1205` +- Database Hostname Prefix as `kmshost` - Oracle VMDB Shape as `VM.Standard2.2` - SSH Public key for the DBCS system being deployed as `oci-publickey` -- domain `subd215df3e6.k8stest.oraclevcn.com` -- OCID of the Subnet as `ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua` -- KMS Vault Name as `basdbvault` +- domain `subdda0b5eaa.cluster1.oraclevcn.com` +- OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` +- KMS Vault Name as `dbvault` - KMS Compartment Id as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` -- KMS Key Name as `dbvaultkey` +- KMS Key Name as `dbkey` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). While giving KMS Vault make sure not to pass TDE wallet password in DB creation as either of them can be only used for encryption. @@ -31,7 +57,7 @@ Use the file: [dbcs_service_with_kms.yaml](./dbcs_service_with_kms.yaml) for thi 1. Deploy the .yaml file: ```bash [root@docker-test-server DBCS]# kubectl apply -f dbcs_service_with_kms.yaml -dbcssystem.database.oracle.com/dbcssystem-create configured +dbcssystem.database.oracle.com/dbcssystem-create created ``` 2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. diff --git a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml index 24f9423e..589ce0cf 100644 --- a/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml +++ b/docs/dbcs/provisioning/createpdb_in_existing_dbcs_system_list.yaml @@ -4,7 +4,7 @@ metadata: name: dbcssystem-existing spec: id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" pdbConfigs: - pdbName: "pdb_sauahuja_sdk_13" diff --git a/docs/dbcs/provisioning/dbcs_controller_parameters.md b/docs/dbcs/provisioning/dbcs_controller_parameters.md index 82fc3dc2..96bedf30 100644 --- a/docs/dbcs/provisioning/dbcs_controller_parameters.md +++ b/docs/dbcs/provisioning/dbcs_controller_parameters.md @@ -8,7 +8,7 @@ This page has the details of the parameters to define the specs related to an op | ociSecret | Kubernetes Secret created using PEM Key for OCI account in the prerequisites steps. | Y | String | | | | availabilityDomain | Availability Domain of the OCI region where you want to provision the DBCS System. | Y | String | | Please refer to this link: https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm | | compartmentId | OCID of the OCI Compartment. | Y | String | | | -| dbAdminPaswordSecret | Kubernetes Secret created for DB Admin Account in prerequisites steps. | Y | String | | A strong password for SYS, SYSTEM, and PDB Admin. The password must be at least nine characters and contain at least two uppercase, two lowercase, two numbers, and two special characters. The special characters must be _, #, or -.| +| dbAdminPasswordSecret | Kubernetes Secret created for DB Admin Account in prerequisites steps. | Y | String | | A strong password for SYS, SYSTEM, and PDB Admin. The password must be at least nine characters and contain at least two uppercase, two lowercase, two numbers, and two special characters. The special characters must be _, #, or -.| | autoBackupEnabled | Whether to enable automatic backup or not. | N | Boolean | | True or False | | autoBackupWindow | Time window selected for initiating automatic backup for the database system. There are twelve available two-hour time windows. | N | String | | Please refer to this link: https://docs.oracle.com/en-us/iaas/api/#/en/database/20160918/datatypes/DbBackupConfig | | recoveryWindowsInDays | Number of days between the current and the earliest point of recoverability covered by automatic backups. | N | Integer | | Minimum: 1 and Maximum: 60 | diff --git a/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.log b/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.log new file mode 100644 index 00000000..2405a90a --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.log @@ -0,0 +1,132 @@ +2025-01-10T14:30:21Z INFO Updating KMS details in Existing Database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc"} +2025-01-10T14:30:27Z INFO MigrateVaultKey request succeeded, waiting for database to reach the desired state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc"} +2025-01-10T14:30:27Z INFO Starting to wait for the database to reach the desired state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "DesiredState": "AVAILABLE", "Timeout": "2h0m0s"} +2025-01-10T14:30:27Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:30:27Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:31:28Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:31:28Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:32:29Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:32:29Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:33:30Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:33:30Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:34:31Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:34:31Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:35:32Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:35:32Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:36:33Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:36:33Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:37:34Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:37:34Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:38:35Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:38:35Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:30:21Z INFO Updating KMS details in Existing Database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc"} +2025-01-10T14:30:27Z INFO MigrateVaultKey request succeeded, waiting for database to reach the desired state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc"} +2025-01-10T14:30:27Z INFO Starting to wait for the database to reach the desired state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "DesiredState": "AVAILABLE", "Timeout": "2h0m0s"} +2025-01-10T14:30:27Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:30:27Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:31:28Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:31:28Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:32:29Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:32:29Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:33:30Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:33:30Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:34:31Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:34:31Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:35:32Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:35:32Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:36:33Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:36:33Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:37:34Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:37:34Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:38:35Z INFO Database State {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING"} +2025-01-10T14:38:35Z INFO Database not in the desired state yet, waiting... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "CurrentState": "UPDATING", "DesiredState": "AVAILABLE", "NextCheckIn": "1m0s"} +2025-01-10T14:40:37Z INFO Database reached the desired state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq", "State": "AVAILABLE"} +2025-01-10T14:40:39Z INFO KMS migration process completed successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "890fac49-f8c0-43a8-817b-fb94e96627cc"} + + +basedb/ $ kubectl describe dbcssystems.database.oracle.com/dbcssystem-existing +Name: dbcssystem-existing +Namespace: default +Labels: +Annotations: lastSuccessfulSpec: + {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a","availabilityDomain":""... +API Version: database.oracle.com/v4 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2025-01-10T14:29:37Z + Generation: 2 + Resource Version: 130979222 + UID: f7535120-dd4a-4cbc-9e29-b9f104904773 +Spec: + Db System: + Availability Domain: + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a + Db Admin Password Secret: admin-password + Db Backup Config: + Host Name: + Kms Config: + Shape: + Subnet Id: + Tde Wallet Password Secret: tde-password + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyafdoaarkuhhxjfgjrzjtxpbcaycib3woadfmcz545mwua + Kms Config: + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a + Key Name: dbkey + Vault Name: dbvault + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey +Status: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 2 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 256 + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Info: + Db Home Id: ocid1.dbhome.oc1.ap-mumbai-1.anrg6ljrqlb5nxia3s627hwjr36bix3dnh4dlbny22tzcmb2a3b4rcp74clq + Db Name: cdb12 + Db Unique Name: cdb12_hf8_bom + Db Workload: OLTP + Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyavlxjvktbs6xko5fbutwdwb3wgr2mnisovycrisj7abxq + Display Name: dbsys123 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyafdoaarkuhhxjfgjrzjtxpbcaycib3woadfmcz545mwua + Kms Details Status: + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a + Key Id: ocid1.key.oc1.ap-mumbai-1.fbtxxaolaaavw.abrg6ljr63rcu5h6lmaeux752pzmp334zihovh3n2acags6zt37emab34yba + Key Name: dbkey + Management Endpoint: https://fbtxxaolaaavw-management.kms.ap-mumbai-1.oraclecloud.com + Vault Id: ocid1.vault.oc1.ap-mumbai-1.fbtxxaolaaavw.abrg6ljrbjokn2fwhh36tqzyog4yjrth3mj2emxea4fxmzw6z35zlmh65p2a + Vault Name: dbvault + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host01234 + Listener Port: 1521 + Scan Dns Name: host01234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Pdb Details Status: + Pdb Config Status: + Freeform Tags: + Created By: MAA_TEAM + TEST: test_case_provision + Pdb Name: PDB0123 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyafnt7gvokjw7cvzs6xjxw5nmlz6awzycqcnf57blcuefa + Reco Storage Size In GB: 256 + Shape: VM.Standard2.2 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljroc46ic555q2rfcwxg3srsbq4indueiuvj7tlziyy63uz3pvpe4ra + Operation Type: Create DB System + Percent Complete: 100 + Time Accepted: 2025-01-10 06:30:12.411 +0000 UTC + Time Finished: 2025-01-10 07:51:04.59 +0000 UTC + Time Started: 2025-01-10 06:30:20.62 +0000 UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.yaml b/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.yaml index acbbd89b..922f7eeb 100644 --- a/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.yaml +++ b/docs/dbcs/provisioning/dbcs_service_migrate_to_kms.yaml @@ -3,27 +3,14 @@ kind: DbcsSystem metadata: name: dbcssystem-existing spec: - id: "ocid1.dbsystem.oc1.iad.anuwcljsabf7htyaoja4v2kx5rcfe5w2onndjfpqjhjoakxgwxo2sbgei5iq" + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyafdoaarkuhhxjfgjrzjtxpbcaycib3woadfmcz545mwua" ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:US-ASHBURN-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" - dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" - dbName: "testdb1" - displayName: "dbsystem0131" - licenseModel: "BRING_YOUR_OWN_LICENSE" - dbVersion: "21c" - dbWorkload: "OLTP" - hostName: "host1206" - shape: "VM.Standard2.2" - domain: "subd215df3e6.k8stest.oraclevcn.com" - sshPublicKeys: - - "oci-publickey" - subnetId: "ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua" + dbAdminPasswordSecret: "admin-password" tdeWalletPasswordSecret: "tde-password" kmsConfig: - vaultName: "basdbvault" + vaultName: "dbvault" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - keyName: "dbvaultkey" \ No newline at end of file + keyName: "dbkey" \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md index f7b1e319..4cc4ceca 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md +++ b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md @@ -6,7 +6,7 @@ In this use case, a 2 Node RAC OCI DBCS system is deployed using Oracle DB Opera This example uses `dbcs_service_with_2_node_rac.yaml` to deploy a 2 Node RAC VMDB using Oracle DB Operator DBCS Controller with: -- OCI Configmap as `oci-cred-mumbai` +- OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - Cluster Name as `maa-cluster` - Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` diff --git a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml index 01d80595..3b4f35e3 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml @@ -3,13 +3,13 @@ kind: DbcsSystem metadata: name: dbcssystem-create spec: - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" clusterName: "maa-cluster" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" dbBackupConfig: autoBackupEnabled: True autoBackupWindow: "SLOT_FOUR" diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md index 64a24cf9..89ed3a9f 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md @@ -6,7 +6,7 @@ In this use case, the an OCI DBCS system is deployed using Oracle DB Operator DB This example uses `dbcs_service_with_all_parameters_asm.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: -- OCI Configmap as `oci-cred-mumbai` +- OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml index 7146d1f5..4eb0fa27 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml @@ -3,13 +3,13 @@ kind: DbcsSystem metadata: name: dbcssystem-create spec: - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" clusterName: "maa-cluster" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" dbBackupConfig: autoBackupEnabled: True autoBackupWindow: "SLOT_FOUR" @@ -35,4 +35,4 @@ spec: "TEST": "test_case_provision" "CreatedBy": "MAA_TEAM" tdeWalletPasswordSecret: "tde-password" - timeZone: "Europe/Berlin" + timeZone: "Europe/Berlin" \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md index 408e1aa9..de11fca1 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md @@ -6,7 +6,7 @@ In this use case, the an OCI DBCS system is deployed using Oracle DB Operator DB This example uses `dbcs_service_with_all_parameters_lvm.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: -- OCI Configmap as `oci-cred-mumbai` +- OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml index afc976d5..f76962d1 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml @@ -3,12 +3,12 @@ kind: DbcsSystem metadata: name: dbcssystem-create spec: - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" dbBackupConfig: autoBackupEnabled: True autoBackupWindow: "SLOT_FOUR" diff --git a/docs/dbcs/provisioning/dbcs_service_with_kms.yaml b/docs/dbcs/provisioning/dbcs_service_with_kms.yaml index 6d9b716b..691b17a1 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_kms.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_kms.yaml @@ -2,27 +2,26 @@ apiVersion: database.oracle.com/v4 kind: DbcsSystem metadata: name: dbcssystem-create - namespace: default spec: ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: - availabilityDomain: "OLou:US-ASHBURN-AD-1" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" - dbName: "testdb" - displayName: "dbsystem0130" + dbName: "kmsdb" + displayName: "kmsdbsystem" licenseModel: "BRING_YOUR_OWN_LICENSE" - dbVersion: "21c" + dbVersion: "19c" dbWorkload: "OLTP" - hostName: "host1205" + hostName: "kmshost" shape: "VM.Standard2.2" - domain: "subd215df3e6.k8stest.oraclevcn.com" - sshPublicKeys: + domain: "subdda0b5eaa.cluster1.oraclevcn.com" + sshPublicKeys: - "oci-publickey" - subnetId: "ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" kmsConfig: - vaultName: "basdbvault" + vaultName: "dbvault" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - keyName: "dbvaultkey" \ No newline at end of file + keyName: "dbkey" \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_kms_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_kms_sample_output.log index 82ac7e05..7ddf7d2f 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_kms_sample_output.log +++ b/docs/dbcs/provisioning/dbcs_service_with_kms_sample_output.log @@ -1 +1,91 @@ -# To be Added \ No newline at end of file +kubectl describe dbcssystems.database.oracle.com/dbcssystem-create +Name: dbcssystem-create +Namespace: default +Labels: +Annotations: lastSuccessfulSpec: + {"dbSystem":{"compartmentId":"ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a","availabilityDomain":"O... +API Version: database.oracle.com/v4 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2025-01-09T18:10:30Z + Generation: 2 + Resource Version: 130640272 + UID: 85e39113-0a02-4cf6-84d8-2270c543b0bf +Spec: + Db System: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a + Db Admin Password Secret: admin-password + Db Backup Config: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Name: kmsdb + Db Version: 19c + Db Workload: OLTP + Display Name: kmsdbsystem + Domain: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: kmshost + Kms Config: + License Model: BRING_YOUR_OWN_LICENSE + Shape: VM.Standard2.2 + Ssh Public Keys: + oci-publickey + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyai52ll4ifn52jcwdwpvv2exqqfa2wptypvi46wibx5sea + Kms Config: + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a + Key Name: dbkey + Vault Name: dbvault + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey +Status: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 2 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 256 + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Info: + Db Home Id: ocid1.dbhome.oc1.ap-mumbai-1.anrg6ljrqlb5nxiasjpqkykdchfksgas4k62cqsf6p5gkvubsj53fdokovnq + Db Name: kmsdb + Db Unique Name: kmsdb_7cb_bom + Db Workload: OLTP + Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarhrmiqzf4pari5sshhdglj6bpuijy3fupxvveblr2l6q + Display Name: kmsdbsystem + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyai52ll4ifn52jcwdwpvv2exqqfa2wptypvi46wibx5sea + Kms Details Status: + Compartment Id: ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a + Key Id: ocid1.key.oc1.ap-mumbai-1.fbtxxaolaaavw.abrg6ljr63rcu5h6lmaeux752pzmp334zihovh3n2acags6zt37emab34yba + Key Name: dbkey + Management Endpoint: https://fbtxxaolaaavw-management.kms.ap-mumbai-1.oraclecloud.com + Vault Id: ocid1.vault.oc1.ap-mumbai-1.fbtxxaolaaavw.abrg6ljrbjokn2fwhh36tqzyog4yjrth3mj2emxea4fxmzw6z35zlmh65p2a + Vault Name: dbvault + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: kmshost + Listener Port: 1521 + Scan Dns Name: kmshost-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Pdb Details Status: + Pdb Config Status: + Pdb Name: kmsdb_pdb1 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyaqbjqveoqzvn5dklbuc575xdrclsrkjt5juzzcelmuqla + Reco Storage Size In GB: 256 + Shape: VM.Standard2.2 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrcik5rtyygbv7qzzxqsmv6dvdwlfb7i2k3pitfqr2zomspcnkx7oa + Operation Type: Create DB System + Percent Complete: 100 + Time Accepted: 2025-01-09 18:10:41.171 +0000 UTC + Time Finished: 2025-01-09 19:31:17.126 +0000 UTC + Time Started: 2025-01-09 18:10:49.668 +0000 UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml index 52cb0a68..66e1c229 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml @@ -3,12 +3,12 @@ kind: DbcsSystem metadata: name: dbcssystem-create spec: - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" dbName: "cdb1" displayName: "dbsystem1234" diff --git a/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml b/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml index 0d2e12a2..7da5f729 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_pdb.yaml @@ -9,7 +9,7 @@ spec: dbSystem: availabilityDomain: "OLou:US-ASHBURN-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" dbName: "dbsys" displayName: "dbsystem24" diff --git a/docs/dbcs/provisioning/migrate_to_kms.md b/docs/dbcs/provisioning/migrate_to_kms.md index b1022d61..092d2599 100644 --- a/docs/dbcs/provisioning/migrate_to_kms.md +++ b/docs/dbcs/provisioning/migrate_to_kms.md @@ -7,25 +7,24 @@ In order to create KMS Vaults to an existing DBCS system, the steps will be: 1. Bind the existing DBCS System (having encryption enabled with TDE Wallet password) to the DBCS Controller. 2. Apply the change to create KMS Vaults. -**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. It is also assumed that DBCS System you created earlier is using TDE Wallet password. As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- ```bash kubectl get dbcssystems NAME AGE -dbcssystem-create-tde 3m33s +dbcssystem-create 3m33s ``` Below proceeding further create PDB Admin Password which is going to used as name suggests. - This example uses `dbcs_service_migrate_to_kms.yaml` to create KMS Vault to existing DBCS VMDB having encryption already enabled earlier with TDE Wallet using Oracle DB Operator DBCS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyaoja4v2kx5rcfe5w2onndjfpqjhjoakxgwxo2sbgei5iq` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Existing `dbSystem` used before to create DBCS system. -- kmsConfig - vaultName as `basdbvault` as an example. -- kmsConfig - keyName as `dbvaultkey` as an example. +- Existing `dbSystem` details (`compartmentId`,`dbAdminPasswordSecret`,`tdeWalletPasswordSecret`)used before to create DBCS system. +- kmsConfig - vaultName as `dbvault` as an example. +- kmsConfig - keyName as `dbkey` as an example. - kmsConfig - compartmentId as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` as an example. **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). diff --git a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md index c7c5ba28..24d4405c 100644 --- a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md @@ -12,7 +12,7 @@ In order to scale down an existing DBCS system, the steps will be: This example uses `scale_down_dbcs_system_shape.yaml` to scale down a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` -- OCI Configmap as `oci-cred-mumbai` +- OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72` diff --git a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml index 110c1fd4..f4394ddc 100644 --- a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml @@ -4,12 +4,12 @@ metadata: name: dbcssystem-existing spec: id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" hostName: "host1234" shape: "VM.Standard2.1" domain: "subdda0b5eaa.cluster1.oraclevcn.com" diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md index 19641629..66c2028f 100644 --- a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md @@ -12,7 +12,7 @@ In order to scale up an existing DBCS system, the steps will be: This example uses `scale_up_dbcs_system_shape.yaml` to scale up a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` -- OCI Configmap as `oci-cred-mumbai` +- OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72` diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml index 7762a0cd..0be84c53 100644 --- a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml @@ -4,12 +4,12 @@ metadata: name: dbcssystem-existing spec: id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" hostName: "host1234" shape: "VM.Standard2.2" domain: "subdda0b5eaa.cluster1.oraclevcn.com" diff --git a/docs/dbcs/provisioning/scale_up_storage.md b/docs/dbcs/provisioning/scale_up_storage.md index 8f9059e8..7cdfa574 100644 --- a/docs/dbcs/provisioning/scale_up_storage.md +++ b/docs/dbcs/provisioning/scale_up_storage.md @@ -12,7 +12,7 @@ In order to scale up storage of an existing DBCS system, the steps will be: This example uses `scale_up_storage.yaml` to scale up storage of an existing Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` -- OCI Configmap as `oci-cred-mumbai` +- OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` diff --git a/docs/dbcs/provisioning/scale_up_storage.yaml b/docs/dbcs/provisioning/scale_up_storage.yaml index a757da42..a2977157 100644 --- a/docs/dbcs/provisioning/scale_up_storage.yaml +++ b/docs/dbcs/provisioning/scale_up_storage.yaml @@ -4,12 +4,12 @@ metadata: name: dbcssystem-existing spec: id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" hostName: "host1234" shape: "VM.Standard2.1" domain: "subdda0b5eaa.cluster1.oraclevcn.com" diff --git a/docs/dbcs/provisioning/update_license.md b/docs/dbcs/provisioning/update_license.md index f77f0dd8..efadb32b 100644 --- a/docs/dbcs/provisioning/update_license.md +++ b/docs/dbcs/provisioning/update_license.md @@ -12,7 +12,7 @@ In order to update the license type an existing DBCS system, the steps will be: This example uses `update_license.yaml` to change the license type of a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` -- OCI Configmap as `oci-cred-mumbai` +- OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Target license model as `BRING_YOUR_OWN_LICENSE` diff --git a/docs/dbcs/provisioning/update_license.yaml b/docs/dbcs/provisioning/update_license.yaml index b97e16fd..7c192b6b 100644 --- a/docs/dbcs/provisioning/update_license.yaml +++ b/docs/dbcs/provisioning/update_license.yaml @@ -4,12 +4,12 @@ name: dbcssystem-existing spec: id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" - ociConfigMap: "oci-cred-mumbai" + ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" - dbAdminPaswordSecret: "admin-password" + dbAdminPasswordSecret: "admin-password" hostName: "host1234" licenseModel: "BRING_YOUR_OWN_LICENSE" shape: "VM.Standard2.1" diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 3b84bdda..fb87ac5b 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -2002,7 +2002,7 @@ spec: type: string dbClone: properties: - dbAdminPaswordSecret: + dbAdminPasswordSecret: type: string dbDbUniqueName: type: string @@ -2053,7 +2053,7 @@ spec: type: string cpuCoreCount: type: integer - dbAdminPaswordSecret: + dbAdminPasswordSecret: type: string dbBackupConfig: properties: @@ -2134,7 +2134,7 @@ spec: required: - availabilityDomain - compartmentId - - dbAdminPaswordSecret + - dbAdminPasswordSecret - hostName - shape - subnetId From db8344576858b04b4e081e857c1efedb2292f56e Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Thu, 16 Jan 2025 01:29:37 +0000 Subject: [PATCH 155/414] Tinglwan fix gen error when backup --- .../v1alpha1/autonomousdatabase_conversion.go | 2 - .../v1alpha1/autonomousdatabase_types.go | 4 +- .../v1alpha1/zz_generated.deepcopy.go | 12 +- apis/database/v4/autonomousdatabase_types.go | 4 +- ...tabase.oracle.com_autonomousdatabases.yaml | 6 - .../samples/adb/autonomousdatabase_bind.yaml | 2 +- .../samples/adb/autonomousdatabase_clone.yaml | 6 +- .../adb/autonomousdatabase_create.yaml | 2 +- .../autonomousdatabase_delete_resource.yaml | 2 +- .../adb/autonomousdatabase_rename.yaml | 2 +- .../samples/adb/autonomousdatabase_scale.yaml | 2 +- ...tonomousdatabase_stop_start_terminate.yaml | 2 +- ...onomousdatabase_update_admin_password.yaml | 4 +- .../adb/autonomousdatabase_update_mtls.yaml | 2 +- ...onomousdatabase_update_network_access.yaml | 2 +- .../adb/autonomousdatabase_wallet.yaml | 24 +- .../database/autonomousdatabase_controller.go | 441 ++---------------- docs/adb/README.md | 42 +- 18 files changed, 94 insertions(+), 467 deletions(-) diff --git a/apis/database/v1alpha1/autonomousdatabase_conversion.go b/apis/database/v1alpha1/autonomousdatabase_conversion.go index 02fd2fc8..ffccc181 100644 --- a/apis/database/v1alpha1/autonomousdatabase_conversion.go +++ b/apis/database/v1alpha1/autonomousdatabase_conversion.go @@ -83,7 +83,6 @@ func (src *AutonomousDatabase) ConvertTo(dstRaw conversion.Hub) error { dst.Status.LifecycleState = src.Status.LifecycleState dst.Status.TimeCreated = src.Status.TimeCreated dst.Status.WalletExpiringDate = src.Status.WalletExpiringDate - dst.Status.Action = src.Status.Action // convert status.allConnectionStrings if src.Status.AllConnectionStrings != nil { @@ -191,7 +190,6 @@ func (dst *AutonomousDatabase) ConvertFrom(srcRaw conversion.Hub) error { dst.Status.LifecycleState = src.Status.LifecycleState dst.Status.TimeCreated = src.Status.TimeCreated dst.Status.WalletExpiringDate = src.Status.WalletExpiringDate - dst.Status.Action = src.Status.Action // convert status.allConnectionStrings if src.Status.AllConnectionStrings != nil { diff --git a/apis/database/v1alpha1/autonomousdatabase_types.go b/apis/database/v1alpha1/autonomousdatabase_types.go index b0f7bc69..960e27eb 100644 --- a/apis/database/v1alpha1/autonomousdatabase_types.go +++ b/apis/database/v1alpha1/autonomousdatabase_types.go @@ -52,7 +52,7 @@ type AutonomousDatabaseSpec struct { // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone Action string `json:"action"` Details AutonomousDatabaseDetails `json:"details"` - Clone AutonomousDatabaseClone `json:"clone"` + Clone AutonomousDatabaseClone `json:"clone,omitempty"` Wallet WalletSpec `json:"wallet,omitempty"` OciConfig OciConfigSpec `json:"ociConfig,omitempty"` // +kubebuilder:default:=false @@ -152,8 +152,6 @@ type AutonomousDatabaseStatus struct { WalletExpiringDate string `json:"walletExpiringDate,omitempty"` // Connection Strings of the ADB AllConnectionStrings []ConnectionStringProfile `json:"allConnectionStrings,omitempty"` - // Last Completed Action - Action string `json:"action,omitempty"` // +patchMergeKey=type // +patchStrategy=merge // +listType=map diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index eb68895b..d0426da8 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1652,6 +1652,11 @@ func (in *OracleRestDataServicePassword) DeepCopy() *OracleRestDataServicePasswo // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OracleRestDataServicePersistence) DeepCopyInto(out *OracleRestDataServicePersistence) { *out = *in + if in.SetWritePermissions != nil { + in, out := &in.SetWritePermissions, &out.SetWritePermissions + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServicePersistence. @@ -1704,7 +1709,7 @@ func (in *OracleRestDataServiceSpec) DeepCopyInto(out *OracleRestDataServiceSpec *out = make([]OracleRestDataServiceRestEnableSchemas, len(*in)) copy(*out, *in) } - out.Persistence = in.Persistence + in.Persistence.DeepCopyInto(&out.Persistence) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServiceSpec. @@ -2318,6 +2323,11 @@ func (in *SingleInstanceDatabaseSpec) DeepCopyInto(out *SingleInstanceDatabaseSp *out = new(bool) **out = **in } + if in.TrueCacheServices != nil { + in, out := &in.TrueCacheServices, &out.TrueCacheServices + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.NodeSelector != nil { in, out := &in.NodeSelector, &out.NodeSelector *out = make(map[string]string, len(*in)) diff --git a/apis/database/v4/autonomousdatabase_types.go b/apis/database/v4/autonomousdatabase_types.go index 88255b3b..745f0320 100644 --- a/apis/database/v4/autonomousdatabase_types.go +++ b/apis/database/v4/autonomousdatabase_types.go @@ -55,7 +55,7 @@ type AutonomousDatabaseSpec struct { // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone Action string `json:"action"` Details AutonomousDatabaseDetails `json:"details"` - Clone AutonomousDatabaseClone `json:"clone"` + Clone AutonomousDatabaseClone `json:"clone,omitempty"` Wallet WalletSpec `json:"wallet,omitempty"` OciConfig OciConfigSpec `json:"ociConfig,omitempty"` // +kubebuilder:default:=false @@ -155,8 +155,6 @@ type AutonomousDatabaseStatus struct { WalletExpiringDate string `json:"walletExpiringDate,omitempty"` // Connection Strings of the ADB AllConnectionStrings []ConnectionStringProfile `json:"allConnectionStrings,omitempty"` - // Last Completed Action - Action string `json:"action,omitempty"` // +patchMergeKey=type // +patchStrategy=merge // +listType=map diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index ad17ed13..69c7d554 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -278,13 +278,10 @@ spec: type: object required: - action - - clone - details type: object status: properties: - action: - type: string allConnectionStrings: items: properties: @@ -615,13 +612,10 @@ spec: type: object required: - action - - clone - details type: object status: properties: - action: - type: string allConnectionStrings: items: properties: diff --git a/config/samples/adb/autonomousdatabase_bind.yaml b/config/samples/adb/autonomousdatabase_bind.yaml index 67f5c4b0..702b8f03 100644 --- a/config/samples/adb/autonomousdatabase_bind.yaml +++ b/config/samples/adb/autonomousdatabase_bind.yaml @@ -9,7 +9,7 @@ metadata: spec: action: Sync details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: configMapName: oci-cred diff --git a/config/samples/adb/autonomousdatabase_clone.yaml b/config/samples/adb/autonomousdatabase_clone.yaml index fbf6f99d..559d7185 100644 --- a/config/samples/adb/autonomousdatabase_clone.yaml +++ b/config/samples/adb/autonomousdatabase_clone.yaml @@ -9,10 +9,10 @@ metadata: spec: action: Clone details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... clone: # Update compartmentOCID with your compartment OCID. - compartmentOCID: ocid1.compartment... OR ocid1.tenancy... + compartmentId: ocid1.compartment... OR ocid1.tenancy... # The dbName must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. dbName: ClonedADB displayName: ClonedADB @@ -24,7 +24,7 @@ spec: name: admin-password # ociSecret: # # The OCID of the OCI Secret that holds the password of the ADMIN account. It should start with ocid1.vaultsecret... . - # ocid: ocid1.vaultsecret... + # id: ocid1.vaultsecret... dataStorageSizeInTBs: 1 dbWorkload: OLTP cloneType: METADATA diff --git a/config/samples/adb/autonomousdatabase_create.yaml b/config/samples/adb/autonomousdatabase_create.yaml index b9207370..d633cb84 100644 --- a/config/samples/adb/autonomousdatabase_create.yaml +++ b/config/samples/adb/autonomousdatabase_create.yaml @@ -10,7 +10,7 @@ spec: action: Create details: # Update compartmentOCID with your compartment OCID. - compartmentOCID: ocid1.compartment... OR ocid1.tenancy... + compartmentId: ocid1.compartment... OR ocid1.tenancy... # The dbName must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. dbName: NewADB displayName: NewADB diff --git a/config/samples/adb/autonomousdatabase_delete_resource.yaml b/config/samples/adb/autonomousdatabase_delete_resource.yaml index 60a8fe5c..bae1f605 100644 --- a/config/samples/adb/autonomousdatabase_delete_resource.yaml +++ b/config/samples/adb/autonomousdatabase_delete_resource.yaml @@ -8,7 +8,7 @@ metadata: name: autonomousdatabase-sample spec: details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... # Delete this resource to terminate database after the changes applied hardLink: true # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. diff --git a/config/samples/adb/autonomousdatabase_rename.yaml b/config/samples/adb/autonomousdatabase_rename.yaml index 24460ead..22dbcc0f 100644 --- a/config/samples/adb/autonomousdatabase_rename.yaml +++ b/config/samples/adb/autonomousdatabase_rename.yaml @@ -9,7 +9,7 @@ metadata: spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... # The database name dbName: RenamedADB # The user-friendly name for the Autonomous Database diff --git a/config/samples/adb/autonomousdatabase_scale.yaml b/config/samples/adb/autonomousdatabase_scale.yaml index e8abc206..ea53e94d 100644 --- a/config/samples/adb/autonomousdatabase_scale.yaml +++ b/config/samples/adb/autonomousdatabase_scale.yaml @@ -9,7 +9,7 @@ metadata: spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... # Your database's OPCU core count cpuCoreCount: 2 # Your database's storage size in TB diff --git a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml index cc41b380..4a191dd6 100644 --- a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml +++ b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml @@ -10,7 +10,7 @@ spec: action: Stop # Use the value "Start" to start the database details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: configMapName: oci-cred diff --git a/config/samples/adb/autonomousdatabase_update_admin_password.yaml b/config/samples/adb/autonomousdatabase_update_admin_password.yaml index 534920f2..be7aca69 100644 --- a/config/samples/adb/autonomousdatabase_update_admin_password.yaml +++ b/config/samples/adb/autonomousdatabase_update_admin_password.yaml @@ -9,7 +9,7 @@ metadata: spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... adminPassword: # Comment out k8sSecret and uncomment ociSecret if you pass the admin password using OCI Secret. k8sSecret: @@ -17,7 +17,7 @@ spec: name: new-admin-password # ociSecret: # # The OCID of the OCI Secret that holds the password of the ADMIN account. It should start with ocid1.vaultsecret... . - # ocid: ocid1.vaultsecret... + # id: ocid1.vaultsecret... # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: configMapName: oci-cred diff --git a/config/samples/adb/autonomousdatabase_update_mtls.yaml b/config/samples/adb/autonomousdatabase_update_mtls.yaml index 82eadd69..9499523f 100644 --- a/config/samples/adb/autonomousdatabase_update_mtls.yaml +++ b/config/samples/adb/autonomousdatabase_update_mtls.yaml @@ -9,7 +9,7 @@ metadata: spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... networkAccess: # Set the patameter to false to allow both TLS and mutual TLS (mTLS) authentication. # Avaiable when the networkAccessType is RESTRICTED or PRIVATE on shared Autnomous Database. diff --git a/config/samples/adb/autonomousdatabase_update_network_access.yaml b/config/samples/adb/autonomousdatabase_update_network_access.yaml index 9577a823..bb28b3a9 100644 --- a/config/samples/adb/autonomousdatabase_update_network_access.yaml +++ b/config/samples/adb/autonomousdatabase_update_network_access.yaml @@ -9,7 +9,7 @@ metadata: spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... networkAccess: # Allow secure access from everywhere. accessType: PUBLIC diff --git a/config/samples/adb/autonomousdatabase_wallet.yaml b/config/samples/adb/autonomousdatabase_wallet.yaml index 2159e36c..84136647 100644 --- a/config/samples/adb/autonomousdatabase_wallet.yaml +++ b/config/samples/adb/autonomousdatabase_wallet.yaml @@ -9,18 +9,18 @@ metadata: spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... - wallet: - # Insert a name of the secret where you want the wallet to be stored. The default name is -instance-wallet. - name: instance-wallet - password: - # Comment out k8sSecret and uncomment ociSecret if you pass the admin password using OCI Secret. - k8sSecret: - # The Name of the K8s secret where you want to hold the password of the ADMIN account. - name: instance-wallet-password - # ociSecret: - # # The OCID of the OCI Secret that holds the password of the ADMIN account. It should start with ocid1.vaultsecret... . - # ocid: ocid1.vaultsecret... + id: ocid1.autonomousdatabase... + wallet: + # Insert a name of the secret where you want the wallet to be stored. The default name is -instance-wallet. + name: instance-wallet + password: + # Comment out k8sSecret and uncomment ociSecret if you pass the admin password using OCI Secret. + k8sSecret: + # The Name of the K8s secret where you want to hold the password of the ADMIN account. + name: instance-wallet-password + # ociSecret: + # # The OCID of the OCI Secret that holds the password of the ADMIN account. It should start with ocid1.vaultsecret... . + # id: ocid1.vaultsecret... # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: configMapName: oci-cred diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 85a801fc..a6a378d4 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -91,10 +91,6 @@ type AutonomousDatabaseReconciler struct { func (r *AutonomousDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&dbv4.AutonomousDatabase{}). - Watches( - &dbv4.AutonomousDatabaseBackup{}, - handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn), - ). Watches( &dbv4.AutonomousDatabaseRestore{}, handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn), @@ -121,19 +117,12 @@ func (r *AutonomousDatabaseReconciler) enqueueMapFn(ctx context.Context, o clien func (r *AutonomousDatabaseReconciler) watchPredicate() predicate.Predicate { return predicate.Funcs{ CreateFunc: func(e event.CreateEvent) bool { - _, backupOk := e.Object.(*dbv4.AutonomousDatabaseBackup) _, restoreOk := e.Object.(*dbv4.AutonomousDatabaseRestore) // Don't enqueue if the event is from Backup or Restore - return !(backupOk || restoreOk) + return !restoreOk }, UpdateFunc: func(e event.UpdateEvent) bool { // Enqueue the update event only when the status changes the first time - desiredBackup, backupOk := e.ObjectNew.(*dbv4.AutonomousDatabaseBackup) - if backupOk { - oldBackup := e.ObjectOld.(*dbv4.AutonomousDatabaseBackup) - return oldBackup.Status.LifecycleState == "" && desiredBackup.Status.LifecycleState != "" - } - desiredRestore, restoreOk := e.ObjectNew.(*dbv4.AutonomousDatabaseRestore) if restoreOk { oldRestore := e.ObjectOld.(*dbv4.AutonomousDatabaseRestore) @@ -218,11 +207,10 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R * Fill the empty fields in the local resource at the beginning of * the reconciliation. ******************************************************************/ - // Fill the empty fields in the AutonomousDatabase resource by syncing up - // with the Autonomous Database in OCI. Only the fields that have nil - // values will be overwritten. - // This operation will be skipped if the AutonomousDatabaseOCID is nil. - // var specChangedAfterFirstSync bool + // Fill the empty fields in the AutonomousDatabase resource by + // syncing up with the Autonomous Database in OCI. Only the fields + // that have nil values will be overwritten. + var stateBeforeFirstSync = desiredAdb.Status.LifecycleState if _, err = r.syncAutonomousDatabase(logger, desiredAdb, false); err != nil { return r.manageError( logger.WithName("syncAutonomousDatabase"), @@ -230,6 +218,23 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R fmt.Errorf("Failed to sync AutonomousDatabase: %w", err)) } + // If the lifecycle state changes from any other states to + // AVAILABLE and spec.action is an empty string, it means that + // the resource in OCI just finished the work, and the spec + // of the Autonomous Database in OCI might also change. + // This is because OCI won't update the spec until the work + // completes. In this case, we need to update the spec of + // the resource in local cluster. + if stateBeforeFirstSync != database.AutonomousDatabaseLifecycleStateAvailable && + desiredAdb.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateAvailable { + if specChanged, err = r.syncAutonomousDatabase(logger, desiredAdb, true); err != nil { + return r.manageError( + logger.WithName("syncAutonomousDatabase"), + desiredAdb, + fmt.Errorf("Failed to sync AutonomousDatabase: %w", err)) + } + } + /****************************************************************** * Determine if the external resource needs to be cleaned up. * If yes, delete the Autonomous Database in OCI and exits the @@ -451,6 +456,7 @@ func (r *AutonomousDatabaseReconciler) performOperation( if err != nil { return false, err } + adb.Spec.Action = "" return true, nil @@ -460,15 +466,17 @@ func (r *AutonomousDatabaseReconciler) performOperation( if err != nil { return false, err } + adb.Spec.Action = "" return true, nil case "Update": l.Info("Update operation") - _, err = r.updateAutonomousDatabase(logger, adb) + err = r.updateAutonomousDatabase(logger, adb) if err != nil { return false, err } + adb.Spec.Action = "" return true, nil @@ -479,6 +487,7 @@ func (r *AutonomousDatabaseReconciler) performOperation( if err != nil { return false, err } + adb.Spec.Action = "" adb.Status.LifecycleState = resp.LifecycleState return true, nil @@ -512,6 +521,7 @@ func (r *AutonomousDatabaseReconciler) performOperation( adb.Spec.Action = "" return true, nil + case "Clone": resp, err := r.dbService.CreateAutonomousDatabaseClone(adb) if err != nil { @@ -535,8 +545,8 @@ func (r *AutonomousDatabaseReconciler) performOperation( if err := r.KubeClient.Create(context.TODO(), clonedAdb); err != nil { return false, err } - return true, nil + case "": // No-op return false, nil @@ -592,14 +602,14 @@ func (r *AutonomousDatabaseReconciler) syncAutonomousDatabase( // The AutonomousDatabase is updated with the returned object from the OCI requests. func (r *AutonomousDatabaseReconciler) updateAutonomousDatabase( logger logr.Logger, - adb *dbv4.AutonomousDatabase) (specChanged bool, err error) { + adb *dbv4.AutonomousDatabase) (err error) { // Get OCI AutonomousDatabase and update the lifecycleState of the CR, // so that the validatexx functions know when the state changes back to AVAILABLE ociAdb := adb.DeepCopy() _, err = r.syncAutonomousDatabase(logger, ociAdb, true) if err != nil { - return false, err + return err } // Start update @@ -609,404 +619,23 @@ func (r *AutonomousDatabaseReconciler) updateAutonomousDatabase( detailsAreChanged, err := difAdb.RemoveUnchangedDetails(ociAdb.Spec) if err != nil { - return false, err + return err } // Do the update request only if the current ADB is actually different from the OCI ADB if detailsAreChanged { logger.Info("Sending UpdateAutonomousDatabase request to OCI") - resp, err := r.dbService.UpdateAutonomousDatabase(*adb.Spec.Details.Id, difAdb) - if err != nil { - return false, err - } - - specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - return specChanged, nil - - // validations := []func( - // logr.Logger, - // *dbv4.AutonomousDatabase, - // *dbv4.AutonomousDatabase, - // *dbv4.AutonomousDatabase) (specChanged bool, err error){ - // r.validateGeneralFields, - // r.validateAdminPassword, - // r.validateDbWorkload, - // r.validateLicenseModel, - // r.validateScalingFields, - // r.validateGeneralNetworkAccess, - // } - - // for _, op := range validations { - // sent, err := op(logger, adb, difAdb, ociAdb) - // if err != nil { - // return false, err - // } - - // if sent { - // return false, nil - // } - // } - } - - return false, nil -} - -/* -func (r *AutonomousDatabaseReconciler) validateGeneralFields( - logger logr.Logger, - adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { - - if difADB.Spec.Details.DisplayName == nil && - difADB.Spec.Details.DbName == nil && - difADB.Spec.Details.DbVersion == nil && - difADB.Spec.Details.FreeformTags == nil { - return false, nil - } - l := logger.WithName("validateGeneralFields") - - l.Info("Sending UpdateAutonomousDatabase request to OCI") - resp, err := r.dbService.UpdateAutonomousDatabaseGeneralFields(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) - if err != nil { - return false, err - } - - specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - - return specChanged, nil -} - -func (r *AutonomousDatabaseReconciler) validateAdminPassword( - logger logr.Logger, - adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { - - if difADB.Spec.Details.AdminPassword.K8sSecret.Name == nil && - difADB.Spec.Details.AdminPassword.OCISecret.OCID == nil { - return false, nil - } - - l := logger.WithName("validateAdminPassword") - - l.Info("Sending UpdateAutonomousDatabase request to OCI") - resp, err := r.dbService.UpdateAutonomousDatabaseAdminPassword(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) - if err != nil { - return false, err - } - - specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - - return specChanged, nil -} - -func (r *AutonomousDatabaseReconciler) validateDbWorkload( - logger logr.Logger, - adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { - - if difADB.Spec.Details.DbWorkload == "" { - return false, nil - } - - l := logger.WithName("validateDbWorkload") - - l.Info("Sending UpdateAutonomousDatabase request to OCI") - resp, err := r.dbService.UpdateAutonomousDatabaseDBWorkload(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) - if err != nil { - return false, err - } - - specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - - return specChanged, nil -} - -func (r *AutonomousDatabaseReconciler) validateLicenseModel( - logger logr.Logger, - adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { - - if difADB.Spec.Details.LicenseModel == "" { - return false, nil - } - - l := logger.WithName("validateLicenseModel") - - l.Info("Sending UpdateAutonomousDatabase request to OCI") - resp, err := r.dbService.UpdateAutonomousDatabaseLicenseModel(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) - if err != nil { - return false, err - } - - specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - - return specChanged, nil -} - -func (r *AutonomousDatabaseReconciler) validateScalingFields( - logger logr.Logger, - adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { - - if difADB.Spec.Details.DataStorageSizeInTBs == nil && - difADB.Spec.Details.CPUCoreCount == nil && - difADB.Spec.Details.IsAutoScalingEnabled == nil { - return false, nil - } - - l := logger.WithName("validateScalingFields") - - l.Info("Sending UpdateAutonomousDatabase request to OCI") - resp, err := r.dbService.UpdateAutonomousDatabaseScalingFields(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) - if err != nil { - return false, err - } - - specChanged = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) - - return specChanged, nil -} - -// The logic of updating the network access configurations is as follows: -// -// 1. Shared databases: -// If the network access type changes -// a. to PUBLIC: -// was RESTRICTED: re-enable IsMTLSConnectionRequired if its not. Then set WhitelistedIps to an array with a single empty string entry. -// was PRIVATE: re-enable IsMTLSConnectionRequired if its not. Then set PrivateEndpointLabel to an emtpy string. -// b. to RESTRICTED: -// was PUBLIC: set WhitelistedIps to desired IPs/CIDR blocks/VCN OCID. Configure the IsMTLSConnectionRequired settings if it is set to disabled. -// was PRIVATE: re-enable IsMTLSConnectionRequired if its not. Set the type to PUBLIC first, and then configure the WhitelistedIps. Finally resume the IsMTLSConnectionRequired settings if it was, or is configured as disabled. -// c. to PRIVATE: -// was PUBLIC: set subnetOCID and nsgOCIDs. Configure the IsMTLSConnectionRequired settings if it is set. -// was RESTRICTED: set subnetOCID and nsgOCIDs. Configure the IsMTLSConnectionRequired settings if it is set. -// *Note: OCI requires nsgOCIDs to be an empty string rather than nil when we don't want the adb to be included in any network security group. -// -// Otherwise, if the network access type remains the same, apply the network configuration, and then set the IsMTLSConnectionRequired. -// -// 2. Dedicated databases: -// Apply the configs directly -func (r *AutonomousDatabaseReconciler) validateGeneralNetworkAccess( - logger logr.Logger, - adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { - - if difADB.Spec.Details.NetworkAccess.AccessType == "" && - difADB.Spec.Details.NetworkAccess.IsAccessControlEnabled == nil && - difADB.Spec.Details.NetworkAccess.AccessControlList == nil && - difADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired == nil && - difADB.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID == nil && - difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs == nil && - difADB.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix == nil { - return false, nil - } - - l := logger.WithName("validateGeneralNetworkAccess") - - if !*adb.Spec.Details.IsDedicated { - var lastAccessType = adb.Spec.Details.NetworkAccess.AccessType - var difAccessType = difADB.Spec.Details.NetworkAccess.AccessType - - if difAccessType != "" { - switch difAccessType { - case dbv4.NetworkAccessTypePublic: - l.Info("Configuring network access type to PUBLIC") - // OCI validation requires IsMTLSConnectionRequired to be enabled before changing the network access type to PUBLIC - if !*adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired { - if err := r.setMTLSRequired(logger, adb); err != nil { - return false, err - } - return true, nil - } - - if err := r.setNetworkAccessPublic(logger, adb.Spec.Details.NetworkAccess.AccessType, adb); err != nil { - return false, err - } - return true, nil - case dbv4.NetworkAccessTypeRestricted: - l.Info("Configuring network access type to RESTRICTED") - // If the access type was PRIVATE, then OCI validation requires IsMTLSConnectionRequired - // to be enabled before setting ACL. Also, we can only change the network access type from - // PRIVATE to PUBLIC, so the steps are PRIVATE->(requeue)->PUBLIC->(requeue)->RESTRICTED. - if lastAccessType == dbv4.NetworkAccessTypePrivate { - if !*ociADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired { - if err := r.setMTLSRequired(logger, adb); err != nil { - return false, err - } - return true, nil - } - - if err := r.setNetworkAccessPublic(logger, ociADB.Spec.Details.NetworkAccess.AccessType, adb); err != nil { - return false, err - } - return true, nil - } - - sent, err := r.validateNetworkAccess(logger, adb, difADB) - if err != nil { - return false, err - } - if sent { - return true, nil - } - - sent, err = r.validateMTLS(logger, adb, difADB, ociADB) - if err != nil { - return false, err - } - if sent { - return true, nil - } - case dbv4.NetworkAccessTypePrivate: - l.Info("Configuring network access type to PRIVATE") - - sent, err := r.validateNetworkAccess(logger, adb, difADB) - if err != nil { - return false, err - } - if sent { - return true, nil - } - - sent, err = r.validateMTLS(logger, adb, difADB, ociADB) - if err != nil { - return false, err - } - if sent { - return true, nil - } - } - } else { - // Access type doesn't change - sent, err := r.validateNetworkAccess(logger, adb, difADB) - if err != nil { - return false, err - } - if sent { - return true, nil - } - - sent, err = r.validateMTLS(logger, adb, difADB, ociADB) - if err != nil { - return false, err - } - if sent { - return true, nil - } - } - } else { - // Dedicated database - sent, err := r.validateNetworkAccess(logger, adb, difADB) + resp, err := r.dbService.UpdateAutonomousDatabase(*adb.Spec.Details.Id, difAdb) if err != nil { - return false, err - } - if sent { - return true, nil + return err } + _ = adb.UpdateFromOciAdb(resp.AutonomousDatabase, true) } - return false, nil -} - -// Set the mTLS to true but not changing the spec -func (r *AutonomousDatabaseReconciler) setMTLSRequired(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { - l := logger.WithName("setMTLSRequired") - - l.Info("Sending request to OCI to set IsMtlsConnectionRequired to true") - - adb.Spec.Details.NetworkAccess.IsMTLSConnectionRequired = common.Bool(true) - - resp, err := r.dbService.UpdateNetworkAccessMTLSRequired(*adb.Spec.Details.AutonomousDatabaseOCID) - if err != nil { - return err - } - - adb.UpdateFromOCIADB(resp.AutonomousDatabase) - return nil } -func (r *AutonomousDatabaseReconciler) validateMTLS( - logger logr.Logger, - adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase, - ociADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { - - if difADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired == nil { - return false, nil - } - - l := logger.WithName("validateMTLS") - - l.Info("Sending request to OCI to configure IsMtlsConnectionRequired") - - resp, err := r.dbService.UpdateNetworkAccessMTLS(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) - if err != nil { - return false, err - } - - adb.UpdateFromOCIADB(resp.AutonomousDatabase) - - return true, nil -} - -func (r *AutonomousDatabaseReconciler) setNetworkAccessPublic(logger logr.Logger, lastAcessType dbv4.NetworkAccessTypeEnum, adb *dbv4.AutonomousDatabase) error { - adb.Spec.Details.NetworkAccess.AccessType = dbv4.NetworkAccessTypePublic - adb.Spec.Details.NetworkAccess.AccessControlList = nil - adb.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix = common.String("") - adb.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = nil - adb.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID = nil - - l := logger.WithName("setNetworkAccessPublic") - - l.Info("Sending request to OCI to configure network access options to PUBLIC") - - resp, err := r.dbService.UpdateNetworkAccessPublic(lastAcessType, *adb.Spec.Details.AutonomousDatabaseOCID) - if err != nil { - return err - } - - adb.UpdateFromOCIADB(resp.AutonomousDatabase) - - return nil -} - -func (r *AutonomousDatabaseReconciler) validateNetworkAccess( - logger logr.Logger, - adb *dbv4.AutonomousDatabase, - difADB *dbv4.AutonomousDatabase) (specChanged bool, err error) { - - if difADB.Spec.Details.NetworkAccess.AccessType == "" && - difADB.Spec.Details.NetworkAccess.IsAccessControlEnabled == nil && - difADB.Spec.Details.NetworkAccess.AccessControlList == nil && - difADB.Spec.Details.NetworkAccess.PrivateEndpoint.SubnetOCID == nil && - difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs == nil && - difADB.Spec.Details.NetworkAccess.PrivateEndpoint.HostnamePrefix == nil { - return false, nil - } - - l := logger.WithName("validateNetworkAccess") - - l.Info("Sending request to OCI to configure network access options") - - // When the network access type is set to PRIVATE, any nil type of nsgOCIDs needs to be set to an empty string, otherwise, OCI SDK returns a 400 error - if difADB.Spec.Details.NetworkAccess.AccessType == dbv4.NetworkAccessTypePrivate && - difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs == nil { - difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = []string{} - } - - resp, err := r.dbService.UpdateNetworkAccess(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) - if err != nil { - return false, err - } - - adb.UpdateFromOCIADB(resp.AutonomousDatabase) - - return true, nil -} -*/ - func (r *AutonomousDatabaseReconciler) validateWallet(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { if adb.Spec.Wallet.Name == nil && adb.Spec.Wallet.Password.K8sSecret.Name == nil && diff --git a/docs/adb/README.md b/docs/adb/README.md index 9b06eef3..c7e57527 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -70,18 +70,18 @@ Follow these steps to provision an Autonomous Database that will map objects in 4. Add the following fields to the AutonomousDatabase resource definition. An example `.yaml` file is available here: [`config/samples/adb/autonomousdatabase_create.yaml`](./../../config/samples/adb/autonomousdatabase_create.yaml) | Attribute | Type | Description | Required? | |----|----|----|----| - | `spec.details.compartmentOCID` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the compartment of the Autonomous Database. | Yes | + | `spec.details.compartmentId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the compartment of the Autonomous Database. | Yes | | `spec.details.dbName` | string | The database name. The name must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. | Yes | | `spec.details.displayName` | string | The user-friendly name for the Autonomous Database. The name does not have to be unique. | Yes | | `spec.details.cpuCoreCount` | int | The number of OCPU cores to be made available to the database. | Yes | - | `spec.details.adminPassword` | dictionary | The password for the ADMIN user. The password must be between 12 and 30 characters long, and must contain at least 1 uppercase, 1 lowercase, and 1 numeric character. It cannot contain the double quote symbol (") or the username "admin", regardless of casing.

Either `k8sSecret.name` or `ociSecret.ocid` must be provided. If both `k8sSecret.name` and `ociSecret.ocid` appear, the Operator reads the password from the K8s secret that `k8sSecret.name` refers to. | Yes | + | `spec.details.adminPassword` | dictionary | The password for the ADMIN user. The password must be between 12 and 30 characters long, and must contain at least 1 uppercase, 1 lowercase, and 1 numeric character. It cannot contain the double quote symbol (") or the username "admin", regardless of casing.

Either `k8sSecret.name` or `ociSecret.id` must be provided. If both `k8sSecret.name` and `ociSecret.id` appear, the Operator reads the password from the K8s secret that `k8sSecret.name` refers to. | Yes | | `spec.details.adminPassword.k8sSecret.name` | string | The **name** of the K8s Secret where you want to hold the password for the ADMIN user. | Conditional | - |`spec.details.adminPassword.ociSecret.ocid` | string | The **[OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm)** of the [OCI Secret](https://docs.oracle.com/en-us/iaas/Content/KeyManagement/Tasks/managingsecrets.htm) where you want to hold the password for the ADMIN user. | Conditional | + |`spec.details.adminPassword.ociSecret.id` | string | The **[OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm)** of the [OCI Secret](https://docs.oracle.com/en-us/iaas/Content/KeyManagement/Tasks/managingsecrets.htm) where you want to hold the password for the ADMIN user. | Conditional | | `spec.details.dataStorageSizeInTBs` | int | The size, in terabytes, of the data volume that will be created and attached to the database. This storage can later be scaled up if needed. | Yes | | `spec.details.isAutoScalingEnabled` | boolean | Indicates if auto scaling is enabled for the Autonomous Database OCPU core count. The default value is `FALSE` | No | - | `spec.details.isDedicated` | boolean | True if the database is on dedicated [Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adbddoverview.htm). `spec.details.autonomousContainerDatabase.k8sACD.name` or `spec.details.autonomousContainerDatabase.ociACD.ocid` has to be provided if the value is true. | No | + | `spec.details.isDedicated` | boolean | True if the database is on dedicated [Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adbddoverview.htm). `spec.details.autonomousContainerDatabase.k8sACD.name` or `spec.details.autonomousContainerDatabase.ociACD.id` has to be provided if the value is true. | No | | `spec.details.autonomousContainerDatabase.k8sACD.name` | string | The **name** of the K8s Autonomous Container Database resource | No | - | `spec.details.autonomousContainerDatabase.ociACD.ocid` | string | The Autonomous Container Database [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm). | No | + | `spec.details.autonomousContainerDatabase.ociACD.id` | string | The Autonomous Container Database [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm). | No | | `spec.details.freeformTags` | dictionary | Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. For more information, see [Resource Tag](https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm).

Example:
`freeformTags:`
    `key1: value1`
    `key2: value2`| No | | `spec.details.dbWorkload` | string | The Oracle Autonomous Database workload type. The following values are valid:
- OLTP - indicates an Autonomous Transaction Processing database
- DW - indicates an Autonomous Data Warehouse database
- AJD - indicates an Autonomous JSON Database
- APEX - indicates an Autonomous Database with the Oracle APEX Application Development workload type. | No | | `spec.details.dbVersion` | string | A valid Oracle Database release for Oracle Autonomous Database. | No | @@ -97,7 +97,7 @@ Follow these steps to provision an Autonomous Database that will map objects in name: autonomousdatabase-sample spec: details: - compartmentOCID: ocid1.compartment... + compartmentId: ocid1.compartment... dbName: NewADB displayName: NewADB cpuCoreCount: 1 @@ -143,7 +143,7 @@ The operator also generates the `AutonomousBackup` custom resources if a databas 3. Add the following fields to the AutonomousDatabase resource definition. An example `.yaml` file is available here: [`config/samples/adb/autonomousdatabase_bind.yaml`](./../../config/samples/adb/autonomousdatabase_bind.yaml) | Attribute | Type | Description | Required? | |----|----|----|----| - | `spec.details.autonomousDatabaseOCID` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the Autonomous Database you want to bind (create a reference) in your cluster. | Yes | + | `spec.details.id` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the Autonomous Database you want to bind (create a reference) in your cluster. | Yes | | `spec.ociConfig` | dictionary | Not required when the Operator is authorized with [Instance Principal](./ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from the [Authorized with API Key Authentication](./ADB_PREREQUISITES.md#authorized-with-api-key-authentication) section. | Conditional | | `spec.ociConfig.configMapName` | string | Name of the ConfigMap that holds the local OCI configuration | Conditional | | `spec.ociConfig.secretName`| string | Name of the K8s Secret that holds the private key value | Conditional | @@ -157,7 +157,7 @@ The operator also generates the `AutonomousBackup` custom resources if a databas spec: details: action: Sync - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... ociConfig: configMapName: oci-cred secretName: oci-privatekey @@ -187,7 +187,7 @@ You can scale up or scale down the Oracle Autonomous Database OCPU core count or spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... cpuCoreCount: 2 dataStorageSizeInTBs: 2 isAutoScalingEnabled: false @@ -220,7 +220,7 @@ You can rename the database by changing the values of the `dbName` and `displayN spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... dbName: RenamedADB displayName: RenamedADB ociConfig: @@ -263,7 +263,7 @@ You can rename the database by changing the values of the `dbName` and `displayN spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... adminPassword: k8sSecret: name: new-admin-password @@ -308,12 +308,12 @@ A client Wallet is required to connect to a shared Oracle Autonomous Database. U spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... - wallet: - name: instance-wallet - password: - k8sSecret: - name: instance-wallet-password + id: ocid1.autonomousdatabase... + wallet: + name: instance-wallet + password: + k8sSecret: + name: instance-wallet-password ociConfig: configMapName: oci-cred secretName: oci-privatekey @@ -363,7 +363,7 @@ Here's a list of the values you can set for `action`: spec: action: STOP details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... ociConfig: configMapName: oci-cred secretName: oci-privatekey @@ -395,7 +395,7 @@ To delete the resource and terminate the Autonomous Database, complete these ste spec: action: Update details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... hardLink: true ociConfig: configMapName: oci-cred @@ -435,9 +435,9 @@ To clone an existing Autonomous Database, complete these steps: spec: action: Clone details: - autonomousDatabaseOCID: ocid1.autonomousdatabase... + id: ocid1.autonomousdatabase... clone: - compartmentOCID: ocid1.compartment... OR ocid1.tenancy... + compartmentId: ocid1.compartment... OR ocid1.tenancy... dbName: ClonedADB displayName: ClonedADB cpuCoreCount: 1 From 4f0e039bd7266fb75d0aa5a230eadb18c4f0bc9c Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Mon, 20 Jan 2025 07:10:38 +0000 Subject: [PATCH 156/414] version v1alpha1 -> v4 --- PROJECT | 33 + .../v1alpha1/dataguardbroker_conversion.go | 14 + .../oraclerestdataservice_conversion.go | 14 + .../singleinstancedatabase_conversion.go | 14 + .../database/v4/dataguardbroker_conversion.go | 4 + apis/database/v4/dataguardbroker_types.go | 163 ++++ apis/database/v4/dataguardbroker_webhook.go | 55 ++ .../v4/oraclerestdataservice_conversion.go | 4 + .../v4/oraclerestdataservice_types.go | 158 ++++ .../v4/oraclerestdataservice_webhook.go | 55 ++ .../v4/singleinstancedatabase_conversion.go | 4 + .../v4/singleinstancedatabase_types.go | 231 ++++++ .../v4/singleinstancedatabase_webhook.go | 55 ++ apis/database/v4/zz_generated.deepcopy.go | 267 +++++++ .../database.oracle.com_dataguardbrokers.yaml | 97 +++ ...ase.oracle.com_oraclerestdataservices.yaml | 177 +++++ ...database.oracle.com_shardingdatabases.yaml | 12 + ...se.oracle.com_singleinstancedatabases.yaml | 352 +++++++++ ...vability.oracle.com_databaseobservers.yaml | 736 +++++++++++++++++- config/crd/kustomization.yaml | 1 + ...njection_in_database_dataguardbrokers.yaml | 8 + ...on_in_database_oraclerestdataservices.yaml | 8 + ...n_in_database_singleinstancedatabases.yaml | 8 + config/samples/database_v4_lrest.yaml | 6 - config/samples/database_v4_lrpdb.yaml | 6 - config/samples/database_v4_ordssrvs.yaml | 6 - config/samples/kustomization.yaml | 3 + config/samples/sidb/dataguardbroker.yaml | 2 +- .../samples/sidb/oraclerestdataservice.yaml | 2 +- .../sidb/oraclerestdataservice_create.yaml | 2 +- .../samples/sidb/singleinstancedatabase.yaml | 2 +- .../sidb/singleinstancedatabase_clone.yaml | 2 +- .../sidb/singleinstancedatabase_create.yaml | 2 +- .../sidb/singleinstancedatabase_express.yaml | 2 +- .../singleinstancedatabase_free-lite.yaml | 5 +- ...singleinstancedatabase_free-truecache.yaml | 5 +- .../sidb/singleinstancedatabase_free.yaml | 7 +- .../sidb/singleinstancedatabase_patch.yaml | 2 +- .../singleinstancedatabase_prebuiltdb.yaml | 2 +- .../sidb/singleinstancedatabase_standby.yaml | 2 +- .../sidb/singleinstancedatabase_tcps.yaml | 2 +- config/webhook/manifests.yaml | 1 - .../oraclerestdataservice_controller.go | 392 +++++----- .../singleinstancedatabase_controller.go | 2 +- controllers/dataguard/dataguard_utils.go | 2 +- .../dataguard/dataguardbroker_controller.go | 2 +- main.go | 14 +- 47 files changed, 2692 insertions(+), 251 deletions(-) create mode 100644 apis/database/v1alpha1/dataguardbroker_conversion.go create mode 100644 apis/database/v1alpha1/oraclerestdataservice_conversion.go create mode 100644 apis/database/v1alpha1/singleinstancedatabase_conversion.go create mode 100644 apis/database/v4/dataguardbroker_conversion.go create mode 100644 apis/database/v4/dataguardbroker_types.go create mode 100644 apis/database/v4/dataguardbroker_webhook.go create mode 100644 apis/database/v4/oraclerestdataservice_conversion.go create mode 100644 apis/database/v4/oraclerestdataservice_types.go create mode 100644 apis/database/v4/oraclerestdataservice_webhook.go create mode 100644 apis/database/v4/singleinstancedatabase_conversion.go create mode 100644 apis/database/v4/singleinstancedatabase_types.go create mode 100644 apis/database/v4/singleinstancedatabase_webhook.go create mode 100644 config/crd/patches/cainjection_in_database_dataguardbrokers.yaml create mode 100644 config/crd/patches/cainjection_in_database_oraclerestdataservices.yaml create mode 100644 config/crd/patches/cainjection_in_database_singleinstancedatabases.yaml delete mode 100644 config/samples/database_v4_lrest.yaml delete mode 100644 config/samples/database_v4_lrpdb.yaml delete mode 100644 config/samples/database_v4_ordssrvs.yaml diff --git a/PROJECT b/PROJECT index 1bb07e55..97e9409c 100644 --- a/PROJECT +++ b/PROJECT @@ -226,4 +226,37 @@ resources: webhooks: conversion: true webhookVersion: v1beta1 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: database + kind: SingleInstanceDatabase + path: github.com/oracle/oracle-database-operator/apis/database/v4 + version: v4 + webhooks: + conversion: true + webhookVersion: v1beta1 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: database + kind: DataguardBroker + path: github.com/oracle/oracle-database-operator/apis/database/v4 + version: v4 + webhooks: + conversion: true + webhookVersion: v1beta1 +- api: + crdVersion: v1beta1 + namespaced: true + domain: oracle.com + group: database + kind: OracleRestDataService + path: github.com/oracle/oracle-database-operator/apis/database/v4 + version: v4 + webhooks: + conversion: true + webhookVersion: v1beta1 version: "3" diff --git a/apis/database/v1alpha1/dataguardbroker_conversion.go b/apis/database/v1alpha1/dataguardbroker_conversion.go new file mode 100644 index 00000000..39751a05 --- /dev/null +++ b/apis/database/v1alpha1/dataguardbroker_conversion.go @@ -0,0 +1,14 @@ +package v1alpha1 + +import ( + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *DataguardBroker) ConvertTo(dst conversion.Hub) error { + return nil +} + +// ConvertFrom converts v1 to v1alpha1 +func (dst *DataguardBroker) ConvertFrom(src conversion.Hub) error { + return nil +} diff --git a/apis/database/v1alpha1/oraclerestdataservice_conversion.go b/apis/database/v1alpha1/oraclerestdataservice_conversion.go new file mode 100644 index 00000000..a16e1ff6 --- /dev/null +++ b/apis/database/v1alpha1/oraclerestdataservice_conversion.go @@ -0,0 +1,14 @@ +package v1alpha1 + +import ( + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *OracleRestDataService) ConvertTo(dst conversion.Hub) error { + return nil +} + +// ConvertFrom converts v1 to v1alpha1 +func (dst *OracleRestDataService) ConvertFrom(src conversion.Hub) error { + return nil +} diff --git a/apis/database/v1alpha1/singleinstancedatabase_conversion.go b/apis/database/v1alpha1/singleinstancedatabase_conversion.go new file mode 100644 index 00000000..76968dce --- /dev/null +++ b/apis/database/v1alpha1/singleinstancedatabase_conversion.go @@ -0,0 +1,14 @@ +package v1alpha1 + +import ( + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *SingleInstanceDatabase) ConvertTo(dst conversion.Hub) error { + return nil +} + +// ConvertFrom converts v1 to v1alpha1 +func (dst *SingleInstanceDatabase) ConvertFrom(src conversion.Hub) error { + return nil +} diff --git a/apis/database/v4/dataguardbroker_conversion.go b/apis/database/v4/dataguardbroker_conversion.go new file mode 100644 index 00000000..22ace70d --- /dev/null +++ b/apis/database/v4/dataguardbroker_conversion.go @@ -0,0 +1,4 @@ +package v4 + +// Hub defines v1 as the hub version +func (*DataguardBroker) Hub() {} \ No newline at end of file diff --git a/apis/database/v4/dataguardbroker_types.go b/apis/database/v4/dataguardbroker_types.go new file mode 100644 index 00000000..9abc54ac --- /dev/null +++ b/apis/database/v4/dataguardbroker_types.go @@ -0,0 +1,163 @@ +/* +** Copyright (c) 2023 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// DataguardBrokerSpec defines the desired state of DataguardBroker +type DataguardBrokerSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + PrimaryDatabaseRef string `json:"primaryDatabaseRef"` + StandbyDatabaseRefs []string `json:"standbyDatabaseRefs"` + SetAsPrimaryDatabase string `json:"setAsPrimaryDatabase,omitempty"` + LoadBalancer bool `json:"loadBalancer,omitempty"` + ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` + // +kubebuilder:validation:Enum=MaxPerformance;MaxAvailability + ProtectionMode string `json:"protectionMode"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + + FastStartFailover bool `json:"fastStartFailover,omitempty"` +} + +// DataguardBrokerStatus defines the observed state of DataguardBroker +type DataguardBrokerStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + PrimaryDatabaseRef string `json:"primaryDatabaseRef,omitempty"` + ProtectionMode string `json:"protectionMode,omitempty"` + PrimaryDatabase string `json:"primaryDatabase,omitempty"` + StandbyDatabases string `json:"standbyDatabases,omitempty"` + ExternalConnectString string `json:"externalConnectString,omitempty"` + ClusterConnectString string `json:"clusterConnectString,omitempty"` + Status string `json:"status,omitempty"` + + FastStartFailover bool `json:"fastStartFailover,omitempty"` + DatabasesInDataguardConfig map[string]string `json:"databasesInDataguardConfig,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=".status.primaryDatabase",name="Primary",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.standbyDatabases",name="Standbys",type="string" +// +kubebuilder:printcolumn:JSONPath=".spec.protectionMode",name="Protection Mode",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.clusterConnectString",name="Cluster Connect Str",type="string",priority=1 +// +kubebuilder:printcolumn:JSONPath=".status.externalConnectString",name="Connect Str",type="string" +// +kubebuilder:printcolumn:JSONPath=".spec.primaryDatabaseRef",name="Primary Database",type="string", priority=1 +// +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.fastStartFailover",name="FSFO", type="string" + +// DataguardBroker is the Schema for the dataguardbrokers API +// +kubebuilder:storageversion +type DataguardBroker struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DataguardBrokerSpec `json:"spec,omitempty"` + Status DataguardBrokerStatus `json:"status,omitempty"` +} + +// ////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns the current primary database in the dataguard configuration from the resource status/spec +// ////////////////////////////////////////////////////////////////////////////////////////////////// +func (broker *DataguardBroker) GetCurrentPrimaryDatabase() string { + if broker.Status.PrimaryDatabase != "" { + return broker.Status.DatabasesInDataguardConfig[broker.Status.PrimaryDatabase] + } + return broker.Spec.PrimaryDatabaseRef +} + +// ////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns databases in Dataguard configuration from the resource status/spec +// ////////////////////////////////////////////////////////////////////////////////////////////////// +func (broker *DataguardBroker) GetDatabasesInDataGuardConfiguration() []string { + var databases []string + if len(broker.Status.DatabasesInDataguardConfig) > 0 { + for _, value := range broker.Status.DatabasesInDataguardConfig { + if value != "" { + databases = append(databases, value) + } + } + + return databases + } + + databases = append(databases, broker.Spec.PrimaryDatabaseRef) + databases = append(databases, broker.Spec.StandbyDatabaseRefs...) + return databases +} + +// ////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns standby databases in the dataguard configuration from the resource status/spec +// ////////////////////////////////////////////////////////////////////////////////////////////////// +func (broker *DataguardBroker) GetStandbyDatabasesInDgConfig() []string { + var databases []string + if len(broker.Status.DatabasesInDataguardConfig) > 0 { + for _, value := range broker.Status.DatabasesInDataguardConfig { + if value != "" && value != broker.Status.PrimaryDatabase { + databases = append(databases, value) + } + } + + return databases + } + + databases = append(databases, broker.Spec.StandbyDatabaseRefs...) + return databases +} + +//+kubebuilder:object:root=true + +// DataguardBrokerList contains a list of DataguardBroker +type DataguardBrokerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DataguardBroker `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DataguardBroker{}, &DataguardBrokerList{}) +} diff --git a/apis/database/v4/dataguardbroker_webhook.go b/apis/database/v4/dataguardbroker_webhook.go new file mode 100644 index 00000000..bcd35de9 --- /dev/null +++ b/apis/database/v4/dataguardbroker_webhook.go @@ -0,0 +1,55 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// log is for logging in this package. +var dataguardbrokerlog = logf.Log.WithName("dataguardbroker-resource") + +func (r *DataguardBroker) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! diff --git a/apis/database/v4/oraclerestdataservice_conversion.go b/apis/database/v4/oraclerestdataservice_conversion.go new file mode 100644 index 00000000..a19cdfd5 --- /dev/null +++ b/apis/database/v4/oraclerestdataservice_conversion.go @@ -0,0 +1,4 @@ +package v4 + +// Hub defines v1 as the hub version +func (*OracleRestDataService) Hub() {} diff --git a/apis/database/v4/oraclerestdataservice_types.go b/apis/database/v4/oraclerestdataservice_types.go new file mode 100644 index 00000000..20cc7a74 --- /dev/null +++ b/apis/database/v4/oraclerestdataservice_types.go @@ -0,0 +1,158 @@ +/* +** Copyright (c) 2023 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// OracleRestDataServiceSpec defines the desired state of OracleRestDataService +type OracleRestDataServiceSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + DatabaseRef string `json:"databaseRef"` + LoadBalancer bool `json:"loadBalancer,omitempty"` + ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + Image OracleRestDataServiceImage `json:"image,omitempty"` + OrdsPassword OracleRestDataServicePassword `json:"ordsPassword"` + AdminPassword OracleRestDataServicePassword `json:"adminPassword"` + OrdsUser string `json:"ordsUser,omitempty"` + RestEnableSchemas []OracleRestDataServiceRestEnableSchemas `json:"restEnableSchemas,omitempty"` + OracleService string `json:"oracleService,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty"` + Persistence OracleRestDataServicePersistence `json:"persistence,omitempty"` + MongoDbApi bool `json:"mongoDbApi,omitempty"` + + // +k8s:openapi-gen=true + // +kubebuilder:validation:Minimum=1 + Replicas int `json:"replicas,omitempty"` + ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` +} + +// OracleRestDataServicePersistence defines the storage releated params +type OracleRestDataServicePersistence struct { + Size string `json:"size,omitempty"` + StorageClass string `json:"storageClass,omitempty"` + + // +kubebuilder:validation:Enum=ReadWriteOnce;ReadWriteMany + AccessMode string `json:"accessMode,omitempty"` + VolumeName string `json:"volumeName,omitempty"` + SetWritePermissions *bool `json:"setWritePermissions,omitempty"` +} + +// OracleRestDataServiceImage defines the Image source and pullSecrets for POD +type OracleRestDataServiceImage struct { + Version string `json:"version,omitempty"` + PullFrom string `json:"pullFrom"` + PullSecrets string `json:"pullSecrets,omitempty"` +} + +// OracleRestDataServicePassword defines the secret containing Password mapped to secretKey +type OracleRestDataServicePassword struct { + SecretName string `json:"secretName"` + // +kubebuilder:default:="oracle_pwd" + SecretKey string `json:"secretKey,omitempty"` + KeepSecret *bool `json:"keepSecret,omitempty"` +} + +// OracleRestDataServicePDBSchemas defines the PDB Schemas to be ORDS Enabled +type OracleRestDataServiceRestEnableSchemas struct { + PdbName string `json:"pdbName,omitempty"` + SchemaName string `json:"schemaName"` + UrlMapping string `json:"urlMapping,omitempty"` + Enable bool `json:"enable"` +} + +// OracleRestDataServiceStatus defines the observed state of OracleRestDataService +type OracleRestDataServiceStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + Status string `json:"status,omitempty"` + DatabaseApiUrl string `json:"databaseApiUrl,omitempty"` + LoadBalancer string `json:"loadBalancer,omitempty"` + DatabaseRef string `json:"databaseRef,omitempty"` + ServiceIP string `json:"serviceIP,omitempty"` + DatabaseActionsUrl string `json:"databaseActionsUrl,omitempty"` + MongoDbApiAccessUrl string `json:"mongoDbApiAccessUrl,omitempty"` + OrdsInstalled bool `json:"ordsInstalled,omitempty"` + ApexConfigured bool `json:"apexConfigured,omitempty"` + ApxeUrl string `json:"apexUrl,omitempty"` + MongoDbApi bool `json:"mongoDbApi,omitempty"` + CommonUsersCreated bool `json:"commonUsersCreated,omitempty"` + Replicas int `json:"replicas,omitempty"` + + Image OracleRestDataServiceImage `json:"image,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type="string" +// +kubebuilder:printcolumn:JSONPath=".spec.databaseRef",name="Database",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.databaseApiUrl",name="Database API URL",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.databaseActionsUrl",name="Database Actions URL",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.apexUrl",name="Apex URL",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.mongoDbApiAccessUrl",name="MongoDbApi Access URL",type="string" + +// OracleRestDataService is the Schema for the oraclerestdataservices API +// +kubebuilder:storageversion +type OracleRestDataService struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec OracleRestDataServiceSpec `json:"spec,omitempty"` + Status OracleRestDataServiceStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// OracleRestDataServiceList contains a list of OracleRestDataService +type OracleRestDataServiceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []OracleRestDataService `json:"items"` +} + +func init() { + SchemeBuilder.Register(&OracleRestDataService{}, &OracleRestDataServiceList{}) +} diff --git a/apis/database/v4/oraclerestdataservice_webhook.go b/apis/database/v4/oraclerestdataservice_webhook.go new file mode 100644 index 00000000..5211528a --- /dev/null +++ b/apis/database/v4/oraclerestdataservice_webhook.go @@ -0,0 +1,55 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// log is for logging in this package. +var oraclerestdataservicelog = logf.Log.WithName("oraclerestdataservice-resource") + +func (r *OracleRestDataService) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! diff --git a/apis/database/v4/singleinstancedatabase_conversion.go b/apis/database/v4/singleinstancedatabase_conversion.go new file mode 100644 index 00000000..93638482 --- /dev/null +++ b/apis/database/v4/singleinstancedatabase_conversion.go @@ -0,0 +1,4 @@ +package v4 + +// Hub defines v1 as the hub version +func (*SingleInstanceDatabase) Hub() {} diff --git a/apis/database/v4/singleinstancedatabase_types.go b/apis/database/v4/singleinstancedatabase_types.go new file mode 100644 index 00000000..4f4836d7 --- /dev/null +++ b/apis/database/v4/singleinstancedatabase_types.go @@ -0,0 +1,231 @@ +/* +** Copyright (c) 2023 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase +type SingleInstanceDatabaseSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Enum=standard;enterprise;express;free + Edition string `json:"edition,omitempty"` + + // SID must be alphanumeric (no special characters, only a-z, A-Z, 0-9), and no longer than 12 characters. + // +k8s:openapi-gen=true + // +kubebuilder:validation:Pattern=`^[a-zA-Z0-9]+$` + // +kubebuilder:validation:MaxLength:=12 + Sid string `json:"sid,omitempty"` + Charset string `json:"charset,omitempty"` + Pdbname string `json:"pdbName,omitempty"` + LoadBalancer bool `json:"loadBalancer,omitempty"` + ListenerPort int `json:"listenerPort,omitempty"` + TcpsListenerPort int `json:"tcpsListenerPort,omitempty"` + ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` + FlashBack *bool `json:"flashBack,omitempty"` + ArchiveLog *bool `json:"archiveLog,omitempty"` + ForceLogging *bool `json:"forceLog,omitempty"` + EnableTCPS bool `json:"enableTCPS,omitempty"` + TcpsCertRenewInterval string `json:"tcpsCertRenewInterval,omitempty"` + TcpsTlsSecret string `json:"tcpsTlsSecret,omitempty"` + + PrimaryDatabaseRef string `json:"primaryDatabaseRef,omitempty"` + // +kubebuilder:validation:Enum=primary;standby;clone;truecache + CreateAs string `json:"createAs,omitempty"` + ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty"` + TrueCacheServices []string `json:"trueCacheServices,omitempty"` + + // +k8s:openapi-gen=true + Replicas int `json:"replicas,omitempty"` + + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + AdminPassword SingleInstanceDatabaseAdminPassword `json:"adminPassword,omitempty"` + Image SingleInstanceDatabaseImage `json:"image"` + Persistence SingleInstanceDatabasePersistence `json:"persistence,omitempty"` + InitParams *SingleInstanceDatabaseInitParams `json:"initParams,omitempty"` + Resources SingleInstanceDatabaseResources `json:"resources,omitempty"` + + ConvertToSnapshotStandby bool `json:"convertToSnapshotStandby,omitempty"` +} + +type SingleInstanceDatabaseResource struct { + Cpu string `json:"cpu,omitempty"` + Memory string `json:"memory,omitempty"` +} + +type SingleInstanceDatabaseResources struct { + Requests *SingleInstanceDatabaseResource `json:"requests,omitempty"` + Limits *SingleInstanceDatabaseResource `json:"limits,omitempty"` +} + +// SingleInstanceDatabasePersistence defines the storage size and class for PVC +type SingleInstanceDatabasePersistence struct { + Size string `json:"size,omitempty"` + StorageClass string `json:"storageClass,omitempty"` + // +kubebuilder:validation:Enum=ReadWriteOnce;ReadWriteMany + AccessMode string `json:"accessMode,omitempty"` + DatafilesVolumeName string `json:"datafilesVolumeName,omitempty"` + ScriptsVolumeName string `json:"scriptsVolumeName,omitempty"` + VolumeClaimAnnotation string `json:"volumeClaimAnnotation,omitempty"` + SetWritePermissions *bool `json:"setWritePermissions,omitempty"` +} + +// SingleInstanceDatabaseInitParams defines the Init Parameters +type SingleInstanceDatabaseInitParams struct { + SgaTarget int `json:"sgaTarget,omitempty"` + PgaAggregateTarget int `json:"pgaAggregateTarget,omitempty"` + CpuCount int `json:"cpuCount,omitempty"` + Processes int `json:"processes,omitempty"` +} + +// SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD +type SingleInstanceDatabaseImage struct { + Version string `json:"version,omitempty"` + PullFrom string `json:"pullFrom"` + PullSecrets string `json:"pullSecrets,omitempty"` + PrebuiltDB bool `json:"prebuiltDB,omitempty"` +} + +// SingleInsatnceAdminPassword defines the secret containing Admin Password mapped to secretKey for Database +type SingleInstanceDatabaseAdminPassword struct { + SecretName string `json:"secretName"` + // +kubebuilder:default:="oracle_pwd" + SecretKey string `json:"secretKey,omitempty"` + KeepSecret *bool `json:"keepSecret,omitempty"` +} + +// SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase +type SingleInstanceDatabaseStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + Nodes []string `json:"nodes,omitempty"` + Role string `json:"role,omitempty"` + Status string `json:"status,omitempty"` + Replicas int `json:"replicas,omitempty"` + ReleaseUpdate string `json:"releaseUpdate,omitempty"` + DgBroker *string `json:"dgBroker,omitempty"` + // +kubebuilder:default:="false" + DatafilesPatched string `json:"datafilesPatched,omitempty"` + ConnectString string `json:"connectString,omitempty"` + ClusterConnectString string `json:"clusterConnectString,omitempty"` + TcpsConnectString string `json:"tcpsConnectString,omitempty"` + StandbyDatabases map[string]string `json:"standbyDatabases,omitempty"` + // +kubebuilder:default:="false" + DatafilesCreated string `json:"datafilesCreated,omitempty"` + Sid string `json:"sid,omitempty"` + Edition string `json:"edition,omitempty"` + Charset string `json:"charset,omitempty"` + Pdbname string `json:"pdbName,omitempty"` + InitSgaSize int `json:"initSgaSize,omitempty"` + InitPgaSize int `json:"initPgaSize,omitempty"` + CreatedAs string `json:"createdAs,omitempty"` + FlashBack string `json:"flashBack,omitempty"` + ArchiveLog string `json:"archiveLog,omitempty"` + ForceLogging string `json:"forceLog,omitempty"` + OemExpressUrl string `json:"oemExpressUrl,omitempty"` + OrdsReference string `json:"ordsReference,omitempty"` + PdbConnectString string `json:"pdbConnectString,omitempty"` + TcpsPdbConnectString string `json:"tcpsPdbConnectString,omitempty"` + ApexInstalled bool `json:"apexInstalled,omitempty"` + PrebuiltDB bool `json:"prebuiltDB,omitempty"` + // +kubebuilder:default:=false + IsTcpsEnabled bool `json:"isTcpsEnabled"` + CertCreationTimestamp string `json:"certCreationTimestamp,omitempty"` + CertRenewInterval string `json:"certRenewInterval,omitempty"` + ClientWalletLoc string `json:"clientWalletLoc,omitempty"` + PrimaryDatabase string `json:"primaryDatabase,omitempty"` + // +kubebuilder:default:="" + TcpsTlsSecret string `json:"tcpsTlsSecret"` + + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + InitParams SingleInstanceDatabaseInitParams `json:"initParams,omitempty"` + Persistence SingleInstanceDatabasePersistence `json:"persistence"` + + ConvertToSnapshotStandby bool `json:"convertToSnapshotStandby,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas +// +kubebuilder:printcolumn:JSONPath=".status.edition",name="Edition",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.sid",name="Sid",type="string",priority=1 +// +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.role",name="Role",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.releaseUpdate",name="Version",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.connectString",name="Connect Str",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.pdbConnectString",name="Pdb Connect Str",type="string",priority=1 +// +kubebuilder:printcolumn:JSONPath=".status.tcpsConnectString",name="TCPS Connect Str",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.tcpsPdbConnectString",name="TCPS Pdb Connect Str",type="string", priority=1 +// +kubebuilder:printcolumn:JSONPath=".status.oemExpressUrl",name="Oem Express Url",type="string" + +// SingleInstanceDatabase is the Schema for the singleinstancedatabases API +// +kubebuilder:storageversion +type SingleInstanceDatabase struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SingleInstanceDatabaseSpec `json:"spec,omitempty"` + Status SingleInstanceDatabaseStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// SingleInstanceDatabaseList contains a list of SingleInstanceDatabase +type SingleInstanceDatabaseList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []SingleInstanceDatabase `json:"items"` +} + +func init() { + SchemeBuilder.Register(&SingleInstanceDatabase{}, &SingleInstanceDatabaseList{}) +} diff --git a/apis/database/v4/singleinstancedatabase_webhook.go b/apis/database/v4/singleinstancedatabase_webhook.go new file mode 100644 index 00000000..b327d7d4 --- /dev/null +++ b/apis/database/v4/singleinstancedatabase_webhook.go @@ -0,0 +1,55 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// log is for logging in this package. +var singleinstancedatabaselog = logf.Log.WithName("singleinstancedatabase-resource") + +func (r *SingleInstanceDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 78f98f1e..801f1b9e 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -1031,6 +1031,95 @@ func (in *DBWalletSecret) DeepCopy() *DBWalletSecret { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataguardBroker) DeepCopyInto(out *DataguardBroker) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBroker. +func (in *DataguardBroker) DeepCopy() *DataguardBroker { + if in == nil { + return nil + } + out := new(DataguardBroker) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DataguardBroker) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataguardBrokerList) DeepCopyInto(out *DataguardBrokerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DataguardBroker, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBrokerList. +func (in *DataguardBrokerList) DeepCopy() *DataguardBrokerList { + if in == nil { + return nil + } + out := new(DataguardBrokerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DataguardBrokerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataguardBrokerSpec) DeepCopyInto(out *DataguardBrokerSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBrokerSpec. +func (in *DataguardBrokerSpec) DeepCopy() *DataguardBrokerSpec { + if in == nil { + return nil + } + out := new(DataguardBrokerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataguardBrokerStatus) DeepCopyInto(out *DataguardBrokerStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBrokerStatus. +func (in *DataguardBrokerStatus) DeepCopy() *DataguardBrokerStatus { + if in == nil { + return nil + } + out := new(DataguardBrokerStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DbCloneConfig) DeepCopyInto(out *DbCloneConfig) { *out = *in @@ -2396,6 +2485,95 @@ func (in *OciSecretSpec) DeepCopy() *OciSecretSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestDataService) DeepCopyInto(out *OracleRestDataService) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataService. +func (in *OracleRestDataService) DeepCopy() *OracleRestDataService { + if in == nil { + return nil + } + out := new(OracleRestDataService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OracleRestDataService) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestDataServiceList) DeepCopyInto(out *OracleRestDataServiceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OracleRestDataService, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServiceList. +func (in *OracleRestDataServiceList) DeepCopy() *OracleRestDataServiceList { + if in == nil { + return nil + } + out := new(OracleRestDataServiceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OracleRestDataServiceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestDataServiceSpec) DeepCopyInto(out *OracleRestDataServiceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServiceSpec. +func (in *OracleRestDataServiceSpec) DeepCopy() *OracleRestDataServiceSpec { + if in == nil { + return nil + } + out := new(OracleRestDataServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestDataServiceStatus) DeepCopyInto(out *OracleRestDataServiceStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServiceStatus. +func (in *OracleRestDataServiceStatus) DeepCopy() *OracleRestDataServiceStatus { + if in == nil { + return nil + } + out := new(OracleRestDataServiceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OrdsSrvs) DeepCopyInto(out *OrdsSrvs) { *out = *in @@ -3296,6 +3474,95 @@ func (in *ShardingDatabaseStatus) DeepCopy() *ShardingDatabaseStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabase) DeepCopyInto(out *SingleInstanceDatabase) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabase. +func (in *SingleInstanceDatabase) DeepCopy() *SingleInstanceDatabase { + if in == nil { + return nil + } + out := new(SingleInstanceDatabase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SingleInstanceDatabase) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseList) DeepCopyInto(out *SingleInstanceDatabaseList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SingleInstanceDatabase, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseList. +func (in *SingleInstanceDatabaseList) DeepCopy() *SingleInstanceDatabaseList { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SingleInstanceDatabaseList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseSpec) DeepCopyInto(out *SingleInstanceDatabaseSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseSpec. +func (in *SingleInstanceDatabaseSpec) DeepCopy() *SingleInstanceDatabaseSpec { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseStatus) DeepCopyInto(out *SingleInstanceDatabaseStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseStatus. +func (in *SingleInstanceDatabaseStatus) DeepCopy() *SingleInstanceDatabaseStatus { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { *out = *in diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index 8fa583b5..5efceff4 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -108,6 +108,103 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.fastStartFailover + name: FSFO + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailover: + type: boolean + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: + type: string + databasesInDataguardConfig: + additionalProperties: + type: string + type: object + externalConnectString: + type: string + fastStartFailover: + type: boolean + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml index 534091b4..fe93a531 100644 --- a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml @@ -101,6 +101,8 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + setWritePermissions: + type: boolean size: type: string storageClass: @@ -108,6 +110,181 @@ spec: volumeName: type: string type: object + readinessCheckPeriod: + type: integer + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + mongoDbApi: + type: boolean + mongoDbApiAccessUrl: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + - jsonPath: .status.mongoDbApiAccessUrl + name: MongoDbApi Access URL + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + mongoDbApi: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + readinessCheckPeriod: + type: integer replicas: minimum: 1 type: integer diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 446c2393..90c6dd53 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -83,6 +83,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -194,6 +196,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -416,6 +420,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -623,6 +629,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -734,6 +742,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -956,6 +966,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index 2f17fa23..8357f2c5 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -83,6 +83,7 @@ spec: - primary - standby - clone + - truecache type: string edition: enum: @@ -190,6 +191,357 @@ spec: type: integer tcpsTlsSecret: type: string + trueCacheServices: + items: + type: string + type: array + required: + - image + type: object + status: + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + convertToSnapshotStandby: + type: boolean + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBroker: + type: string + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: false + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + convertToSnapshotStandby: + type: boolean + createAs: + enum: + - primary + - standby + - clone + - truecache + type: string + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + resources: + properties: + limits: + properties: + cpu: + type: string + memory: + type: string + type: object + requests: + properties: + cpu: + type: string + memory: + type: string + type: object + type: object + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + trueCacheServices: + items: + type: string + type: array required: - image type: object diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index 8d566b20..7460f76f 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -189,6 +189,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -206,6 +207,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -218,6 +220,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -233,6 +236,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -277,6 +281,10 @@ spec: - Lowercase - uppercase - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual type: string modulus: format: int64 @@ -305,6 +313,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -317,6 +326,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -330,6 +340,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -341,10 +352,127 @@ spec: additionalProperties: type: string type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string scopes: items: type: string type: array + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object tokenUrl: minLength: 1 type: string @@ -389,6 +517,10 @@ spec: - Lowercase - uppercase - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual type: string modulus: format: int64 @@ -409,6 +541,9 @@ spec: type: object type: array scheme: + enum: + - http + - https type: string scrapeTimeout: pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ @@ -427,6 +562,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -439,6 +575,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -456,6 +593,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -468,6 +606,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -487,6 +626,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -494,9 +634,25 @@ spec: - key type: object x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string serverName: type: string type: object + trackTimestampsStaleness: + type: boolean type: object type: array labels: @@ -543,10 +699,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -570,6 +728,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: type: string readOnly: @@ -579,6 +738,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -596,6 +756,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -624,7 +785,9 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" type: string optional: type: boolean @@ -639,6 +802,7 @@ spec: nodePublishSecretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -694,6 +858,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: properties: @@ -718,6 +883,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: properties: apiGroup: @@ -777,11 +943,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -814,10 +982,12 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: properties: @@ -834,6 +1004,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -893,6 +1064,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -906,6 +1084,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -914,11 +1093,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: type: boolean secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -995,11 +1176,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -1034,7 +1217,9 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" type: string optional: type: boolean @@ -1080,6 +1265,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: properties: @@ -1098,7 +1284,9 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" type: string optional: type: boolean @@ -1118,6 +1306,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: properties: @@ -1144,22 +1333,27 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: type: string type: array + x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -1168,6 +1362,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -1178,12 +1373,14 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -1216,6 +1413,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: type: boolean secretName: @@ -1230,6 +1428,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -1262,10 +1461,12 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic command: items: type: string type: array + x-kubernetes-list-type: atomic env: items: properties: @@ -1280,6 +1481,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -1318,6 +1520,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -1330,12 +1533,16 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map envFrom: items: properties: configMapRef: properties: name: + default: "" type: string optional: type: boolean @@ -1346,6 +1553,7 @@ spec: secretRef: properties: name: + default: "" type: string optional: type: boolean @@ -1353,6 +1561,7 @@ spec: x-kubernetes-map-type: atomic type: object type: array + x-kubernetes-list-type: atomic image: type: string imagePullPolicy: @@ -1367,6 +1576,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: properties: @@ -1384,6 +1594,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -1425,6 +1636,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: properties: @@ -1442,6 +1654,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -1484,6 +1697,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: format: int32 @@ -1494,6 +1708,7 @@ spec: format: int32 type: integer service: + default: "" type: string required: - port @@ -1514,6 +1729,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -1588,6 +1804,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: format: int32 @@ -1598,6 +1815,7 @@ spec: format: int32 type: integer service: + default: "" type: string required: - port @@ -1618,6 +1836,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -1678,6 +1897,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -1708,16 +1929,27 @@ spec: properties: allowPrivilegeEscalation: type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object capabilities: properties: add: items: type: string type: array + x-kubernetes-list-type: atomic drop: items: type: string type: array + x-kubernetes-list-type: atomic type: object privileged: type: boolean @@ -1773,6 +2005,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: format: int32 @@ -1783,6 +2016,7 @@ spec: format: int32 type: integer service: + default: "" type: string required: - port @@ -1803,6 +2037,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -1865,6 +2100,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map volumeMounts: items: properties: @@ -1876,6 +2114,8 @@ spec: type: string readOnly: type: boolean + recursiveReadOnly: + type: string subPath: type: string subPathExpr: @@ -1885,6 +2125,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map workingDir: type: string required: @@ -2120,6 +2363,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2137,6 +2381,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2149,6 +2394,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2164,6 +2410,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2208,6 +2455,10 @@ spec: - Lowercase - uppercase - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual type: string modulus: format: int64 @@ -2236,6 +2487,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2248,6 +2500,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2261,6 +2514,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2272,10 +2526,127 @@ spec: additionalProperties: type: string type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string scopes: items: type: string type: array + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object tokenUrl: minLength: 1 type: string @@ -2320,6 +2691,10 @@ spec: - Lowercase - uppercase - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual type: string modulus: format: int64 @@ -2340,6 +2715,9 @@ spec: type: object type: array scheme: + enum: + - http + - https type: string scrapeTimeout: pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ @@ -2358,6 +2736,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2370,6 +2749,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2387,6 +2767,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2399,6 +2780,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2418,6 +2800,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -2425,9 +2808,25 @@ spec: - key type: object x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string serverName: type: string type: object + trackTimestampsStaleness: + type: boolean type: object type: array labels: @@ -2474,10 +2873,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -2501,6 +2902,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: type: string readOnly: @@ -2510,6 +2912,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -2527,6 +2930,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -2555,7 +2959,9 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" type: string optional: type: boolean @@ -2570,6 +2976,7 @@ spec: nodePublishSecretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -2625,6 +3032,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: properties: @@ -2649,6 +3057,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: properties: apiGroup: @@ -2708,11 +3117,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -2745,10 +3156,12 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: properties: @@ -2765,6 +3178,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -2824,6 +3238,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -2837,6 +3258,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -2845,11 +3267,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: type: boolean secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -2926,11 +3350,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -2965,7 +3391,9 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" type: string optional: type: boolean @@ -3011,6 +3439,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: properties: @@ -3029,7 +3458,9 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" type: string optional: type: boolean @@ -3049,6 +3480,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: properties: @@ -3075,22 +3507,27 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: type: string type: array + x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -3099,6 +3536,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -3109,12 +3547,14 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -3147,6 +3587,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: type: boolean secretName: @@ -3161,6 +3602,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -3193,10 +3635,12 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic command: items: type: string type: array + x-kubernetes-list-type: atomic env: items: properties: @@ -3211,6 +3655,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -3249,6 +3694,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -3261,12 +3707,16 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map envFrom: items: properties: configMapRef: properties: name: + default: "" type: string optional: type: boolean @@ -3277,6 +3727,7 @@ spec: secretRef: properties: name: + default: "" type: string optional: type: boolean @@ -3284,6 +3735,7 @@ spec: x-kubernetes-map-type: atomic type: object type: array + x-kubernetes-list-type: atomic image: type: string imagePullPolicy: @@ -3298,6 +3750,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: properties: @@ -3315,6 +3768,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -3356,6 +3810,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: properties: @@ -3373,6 +3828,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -3415,6 +3871,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: format: int32 @@ -3425,6 +3882,7 @@ spec: format: int32 type: integer service: + default: "" type: string required: - port @@ -3445,6 +3903,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -3519,6 +3978,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: format: int32 @@ -3529,6 +3989,7 @@ spec: format: int32 type: integer service: + default: "" type: string required: - port @@ -3549,6 +4010,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -3609,6 +4071,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -3639,16 +4103,27 @@ spec: properties: allowPrivilegeEscalation: type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object capabilities: properties: add: items: type: string type: array + x-kubernetes-list-type: atomic drop: items: type: string type: array + x-kubernetes-list-type: atomic type: object privileged: type: boolean @@ -3704,6 +4179,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: format: int32 @@ -3714,6 +4190,7 @@ spec: format: int32 type: integer service: + default: "" type: string required: - port @@ -3734,6 +4211,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -3796,6 +4274,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map volumeMounts: items: properties: @@ -3807,6 +4288,8 @@ spec: type: string readOnly: type: boolean + recursiveReadOnly: + type: string subPath: type: string subPathExpr: @@ -3816,6 +4299,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map workingDir: type: string required: @@ -3905,13 +4391,6 @@ spec: properties: configMap: properties: - configmap: - properties: - configmapName: - type: string - key: - type: string - type: object key: type: string name: @@ -4058,6 +4537,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4075,6 +4555,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4087,6 +4568,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4102,6 +4584,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4146,6 +4629,10 @@ spec: - Lowercase - uppercase - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual type: string modulus: format: int64 @@ -4174,6 +4661,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4186,6 +4674,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4199,6 +4688,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4210,10 +4700,127 @@ spec: additionalProperties: type: string type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string scopes: items: type: string type: array + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object tokenUrl: minLength: 1 type: string @@ -4258,6 +4865,10 @@ spec: - Lowercase - uppercase - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual type: string modulus: format: int64 @@ -4278,6 +4889,9 @@ spec: type: object type: array scheme: + enum: + - http + - https type: string scrapeTimeout: pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ @@ -4296,6 +4910,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4308,6 +4923,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4325,6 +4941,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4337,6 +4954,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4356,6 +4974,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -4363,9 +4982,25 @@ spec: - key type: object x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string serverName: type: string type: object + trackTimestampsStaleness: + type: boolean type: object type: array labels: @@ -4412,10 +5047,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -4439,6 +5076,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: type: string readOnly: @@ -4448,6 +5086,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -4465,6 +5104,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -4493,7 +5133,9 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" type: string optional: type: boolean @@ -4508,6 +5150,7 @@ spec: nodePublishSecretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -4563,6 +5206,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: properties: @@ -4587,6 +5231,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: properties: apiGroup: @@ -4646,11 +5291,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -4683,10 +5330,12 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: properties: @@ -4703,6 +5352,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -4762,6 +5412,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -4775,6 +5432,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -4783,11 +5441,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: type: boolean secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -4864,11 +5524,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -4903,7 +5565,9 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" type: string optional: type: boolean @@ -4949,6 +5613,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: properties: @@ -4967,7 +5632,9 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" type: string optional: type: boolean @@ -4987,6 +5654,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: properties: @@ -5013,22 +5681,27 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: type: string type: array + x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -5037,6 +5710,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -5047,12 +5721,14 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -5085,6 +5761,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: type: boolean secretName: @@ -5099,6 +5776,7 @@ spec: secretRef: properties: name: + default: "" type: string type: object x-kubernetes-map-type: atomic @@ -5131,10 +5809,12 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic command: items: type: string type: array + x-kubernetes-list-type: atomic env: items: properties: @@ -5149,6 +5829,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -5187,6 +5868,7 @@ spec: key: type: string name: + default: "" type: string optional: type: boolean @@ -5199,12 +5881,16 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map envFrom: items: properties: configMapRef: properties: name: + default: "" type: string optional: type: boolean @@ -5215,6 +5901,7 @@ spec: secretRef: properties: name: + default: "" type: string optional: type: boolean @@ -5222,6 +5909,7 @@ spec: x-kubernetes-map-type: atomic type: object type: array + x-kubernetes-list-type: atomic image: type: string imagePullPolicy: @@ -5236,6 +5924,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: properties: @@ -5253,6 +5942,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -5294,6 +5984,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: properties: @@ -5311,6 +6002,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -5353,6 +6045,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: format: int32 @@ -5363,6 +6056,7 @@ spec: format: int32 type: integer service: + default: "" type: string required: - port @@ -5383,6 +6077,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -5457,6 +6152,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: format: int32 @@ -5467,6 +6163,7 @@ spec: format: int32 type: integer service: + default: "" type: string required: - port @@ -5487,6 +6184,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -5547,6 +6245,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -5577,16 +6277,27 @@ spec: properties: allowPrivilegeEscalation: type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object capabilities: properties: add: items: type: string type: array + x-kubernetes-list-type: atomic drop: items: type: string type: array + x-kubernetes-list-type: atomic type: object privileged: type: boolean @@ -5642,6 +6353,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: format: int32 @@ -5652,6 +6364,7 @@ spec: format: int32 type: integer service: + default: "" type: string required: - port @@ -5672,6 +6385,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: type: string port: @@ -5734,6 +6448,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map volumeMounts: items: properties: @@ -5745,6 +6462,8 @@ spec: type: string readOnly: type: boolean + recursiveReadOnly: + type: string subPath: type: string subPathExpr: @@ -5754,6 +6473,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map workingDir: type: string required: diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 743c8246..726521b0 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -63,6 +63,7 @@ patchesStrategicMerge: #- patches/cainjection_in_lrests.yaml #- patches/cainjection_in_lrpdbs.yaml #- patches/cainjection_in_ordssrvs.yaml +#- patches/cainjection_in_singleinstancedatabases.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_database_dataguardbrokers.yaml b/config/crd/patches/cainjection_in_database_dataguardbrokers.yaml new file mode 100644 index 00000000..6409f54c --- /dev/null +++ b/config/crd/patches/cainjection_in_database_dataguardbrokers.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: dataguardbrokers.database.oracle.com diff --git a/config/crd/patches/cainjection_in_database_oraclerestdataservices.yaml b/config/crd/patches/cainjection_in_database_oraclerestdataservices.yaml new file mode 100644 index 00000000..d2b5d4ee --- /dev/null +++ b/config/crd/patches/cainjection_in_database_oraclerestdataservices.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: oraclerestdataservices.database.oracle.com diff --git a/config/crd/patches/cainjection_in_database_singleinstancedatabases.yaml b/config/crd/patches/cainjection_in_database_singleinstancedatabases.yaml new file mode 100644 index 00000000..b87b9351 --- /dev/null +++ b/config/crd/patches/cainjection_in_database_singleinstancedatabases.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: singleinstancedatabases.database.oracle.com diff --git a/config/samples/database_v4_lrest.yaml b/config/samples/database_v4_lrest.yaml deleted file mode 100644 index 8b8f9d16..00000000 --- a/config/samples/database_v4_lrest.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LREST -metadata: - name: lrest-sample -spec: - # TODO(user): Add fields here diff --git a/config/samples/database_v4_lrpdb.yaml b/config/samples/database_v4_lrpdb.yaml deleted file mode 100644 index c674b264..00000000 --- a/config/samples/database_v4_lrpdb.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: lrpdb-sample -spec: - # TODO(user): Add fields here diff --git a/config/samples/database_v4_ordssrvs.yaml b/config/samples/database_v4_ordssrvs.yaml deleted file mode 100644 index 1c346139..00000000 --- a/config/samples/database_v4_ordssrvs.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: OrdsSrvs -metadata: - name: ordssrvs-sample -spec: - # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index f08c784d..1a032832 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -45,4 +45,7 @@ resources: - database_v4_lrest.yaml - database_v4_lrpdb.yaml - database_v4_ordssrvs.yaml +- database_v4_singleinstancedatabase.yaml +- database_v4_dataguardbroker.yaml +- database_v4_oraclerestdataservice.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/sidb/dataguardbroker.yaml b/config/samples/sidb/dataguardbroker.yaml index 214c6f86..644d2d40 100644 --- a/config/samples/sidb/dataguardbroker.yaml +++ b/config/samples/sidb/dataguardbroker.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: DataguardBroker metadata: name: dataguardbroker-sample diff --git a/config/samples/sidb/oraclerestdataservice.yaml b/config/samples/sidb/oraclerestdataservice.yaml index a8f1b9bb..77555f47 100644 --- a/config/samples/sidb/oraclerestdataservice.yaml +++ b/config/samples/sidb/oraclerestdataservice.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: OracleRestDataService metadata: name: ords-sample diff --git a/config/samples/sidb/oraclerestdataservice_create.yaml b/config/samples/sidb/oraclerestdataservice_create.yaml index 8647592b..e98ca018 100644 --- a/config/samples/sidb/oraclerestdataservice_create.yaml +++ b/config/samples/sidb/oraclerestdataservice_create.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: OracleRestDataService metadata: name: ords-sample diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 23a6fb73..368762f5 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2023, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: name: sidb-sample diff --git a/config/samples/sidb/singleinstancedatabase_clone.yaml b/config/samples/sidb/singleinstancedatabase_clone.yaml index f25484d9..438d4ea5 100644 --- a/config/samples/sidb/singleinstancedatabase_clone.yaml +++ b/config/samples/sidb/singleinstancedatabase_clone.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: name: sidb-sample-clone diff --git a/config/samples/sidb/singleinstancedatabase_create.yaml b/config/samples/sidb/singleinstancedatabase_create.yaml index 3989d6ac..2a4e4bae 100644 --- a/config/samples/sidb/singleinstancedatabase_create.yaml +++ b/config/samples/sidb/singleinstancedatabase_create.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: # Creates base sidb-sample. Use singleinstancedatabase_clone.yaml for cloning diff --git a/config/samples/sidb/singleinstancedatabase_express.yaml b/config/samples/sidb/singleinstancedatabase_express.yaml index 64f2e351..2cabbdaf 100644 --- a/config/samples/sidb/singleinstancedatabase_express.yaml +++ b/config/samples/sidb/singleinstancedatabase_express.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: name: xedb-sample diff --git a/config/samples/sidb/singleinstancedatabase_free-lite.yaml b/config/samples/sidb/singleinstancedatabase_free-lite.yaml index 6641790f..93b3c4c9 100644 --- a/config/samples/sidb/singleinstancedatabase_free-lite.yaml +++ b/config/samples/sidb/singleinstancedatabase_free-lite.yaml @@ -3,16 +3,13 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: name: freedb-lite-sample namespace: default spec: - ## Use only alphanumeric characters for sid - sid: FREE - ## DB edition edition: free diff --git a/config/samples/sidb/singleinstancedatabase_free-truecache.yaml b/config/samples/sidb/singleinstancedatabase_free-truecache.yaml index 13377427..c2481f7c 100644 --- a/config/samples/sidb/singleinstancedatabase_free-truecache.yaml +++ b/config/samples/sidb/singleinstancedatabase_free-truecache.yaml @@ -3,16 +3,13 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: name: truecache-sample namespace: default spec: - ## Use only alphanumeric characters for sid - sid: FREE - ## DB edition edition: free diff --git a/config/samples/sidb/singleinstancedatabase_free.yaml b/config/samples/sidb/singleinstancedatabase_free.yaml index 6dd0aa39..6238e52e 100644 --- a/config/samples/sidb/singleinstancedatabase_free.yaml +++ b/config/samples/sidb/singleinstancedatabase_free.yaml @@ -3,16 +3,13 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: name: freedb-sample namespace: default spec: - - ## Use only alphanumeric characters for sid - sid: FREE - + ## DB edition edition: free diff --git a/config/samples/sidb/singleinstancedatabase_patch.yaml b/config/samples/sidb/singleinstancedatabase_patch.yaml index 9a211cdc..455bdc79 100644 --- a/config/samples/sidb/singleinstancedatabase_patch.yaml +++ b/config/samples/sidb/singleinstancedatabase_patch.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: # sidb-sample should have already been created using singleinstancedatabase_create.yaml diff --git a/config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml b/config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml index 5e4d0a4f..4eec988a 100644 --- a/config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml +++ b/config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: name: prebuiltdb-sample diff --git a/config/samples/sidb/singleinstancedatabase_standby.yaml b/config/samples/sidb/singleinstancedatabase_standby.yaml index dbc9c267..d7ad4b23 100644 --- a/config/samples/sidb/singleinstancedatabase_standby.yaml +++ b/config/samples/sidb/singleinstancedatabase_standby.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: # Creates base standbydatabase-sample. Use singleinstancedatabase_clone.yaml for cloning diff --git a/config/samples/sidb/singleinstancedatabase_tcps.yaml b/config/samples/sidb/singleinstancedatabase_tcps.yaml index 06389f96..d3e3100b 100644 --- a/config/samples/sidb/singleinstancedatabase_tcps.yaml +++ b/config/samples/sidb/singleinstancedatabase_tcps.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: # Creates base sidb-sample. Use singleinstancedatabase_clone.yaml for cloning diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 4392ec97..0166160d 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -331,7 +331,6 @@ webhooks: resources: - databaseobservers sideEffects: None - --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration diff --git a/controllers/database/oraclerestdataservice_controller.go b/controllers/database/oraclerestdataservice_controller.go index ba23a491..053f4a19 100644 --- a/controllers/database/oraclerestdataservice_controller.go +++ b/controllers/database/oraclerestdataservice_controller.go @@ -60,7 +60,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" - dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" dbcommons "github.com/oracle/oracle-database-operator/commons/database" "github.com/go-logr/logr" @@ -215,7 +215,7 @@ func (r *OracleRestDataServiceReconciler) Reconcile(ctx context.Context, req ctr if result.Requeue { r.Log.Info("Reconcile queued") return result, nil - } + } // Delete Secrets r.deleteSecrets(oracleRestDataService, ctx, req) @@ -497,132 +497,88 @@ func (r *OracleRestDataServiceReconciler) instantiateSVCSpec(m *dbapi.OracleRest // ############################################################################# func (r *OracleRestDataServiceReconciler) instantiatePodSpec(m *dbapi.OracleRestDataService, n *dbapi.SingleInstanceDatabase, req ctrl.Request) *corev1.Pod { - - pod := &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: m.Name + "-" + dbcommons.GenerateRandomString(5), - Namespace: m.Namespace, - Labels: map[string]string{ - "app": m.Name, - "version": m.Spec.Image.Version, - }, + + pod := &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: m.Name + "-" + dbcommons.GenerateRandomString(5), + Namespace: m.Namespace, + Labels: map[string]string{ + "app": m.Name, + "version": m.Spec.Image.Version, }, - Spec: corev1.PodSpec{ - Affinity: func() *corev1.Affinity { - if m.Spec.Persistence.Size == "" && n.Spec.Persistence.AccessMode == "ReadWriteOnce" { - // Only allowing pods to be scheduled on the node where SIDB pods are running - return &corev1.Affinity{ - PodAffinity: &corev1.PodAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{{ - Key: "app", - Operator: metav1.LabelSelectorOpIn, - Values: []string{n.Name}, // Schedule on same host as DB Pod - }}, - }, - TopologyKey: "kubernetes.io/hostname", - }, + }, + Spec: corev1.PodSpec{ + Affinity: func() *corev1.Affinity { + if m.Spec.Persistence.Size == "" && n.Spec.Persistence.AccessMode == "ReadWriteOnce" { + // Only allowing pods to be scheduled on the node where SIDB pods are running + return &corev1.Affinity{ + PodAffinity: &corev1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "app", + Operator: metav1.LabelSelectorOpIn, + Values: []string{n.Name}, // Schedule on same host as DB Pod + }}, }, + TopologyKey: "kubernetes.io/hostname", }, - } - } - return nil - }(), - Volumes: []corev1.Volume{ - { - Name: "datamount", - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: func() string { - if m.Spec.Persistence.AccessMode != "" { - return m.Name - } - return n.Name - }(), - ReadOnly: false, }, }, - }, - { - Name: "varmount", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + } + } + return nil + }(), + Volumes: []corev1.Volume{ + { + Name: "datamount", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: func() string { + if m.Spec.Persistence.AccessMode != "" { + return m.Name + } + return n.Name + }(), + ReadOnly: false, }, }, }, - InitContainers: func() []corev1.Container{ - initContainers := []corev1.Container{} - if m.Spec.Persistence.Size != "" && m.Spec.Persistence.SetWritePermissions != nil && *m.Spec.Persistence.SetWritePermissions { - initContainers = append(initContainers, corev1.Container{ - Name: "init-permissions", - Image: m.Spec.Image.PullFrom, - Command: []string{"/bin/sh", "-c", fmt.Sprintf("chown %d:%d /etc/ords/config/ || true", int(dbcommons.ORACLE_UID), int(dbcommons.DBA_GUID))}, - SecurityContext: &corev1.SecurityContext{ - // User ID 0 means, root user - RunAsUser: func() *int64 { i := int64(0); return &i }(), - }, - VolumeMounts: []corev1.VolumeMount{{ - MountPath: "/etc/ords/config/", - Name: "datamount", - }}, - }) - } - + { + Name: "varmount", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: func() []corev1.Container { + initContainers := []corev1.Container{} + if m.Spec.Persistence.Size != "" && m.Spec.Persistence.SetWritePermissions != nil && *m.Spec.Persistence.SetWritePermissions { initContainers = append(initContainers, corev1.Container{ - Name: "init-ords", + Name: "init-permissions", Image: m.Spec.Image.PullFrom, - Command: []string{"/bin/sh"}, - Args: []string{ - "-c", - fmt.Sprintf("while [ ! -f /opt/oracle/variables/%s ]; do sleep 0.5; done", "conn_string.txt"), - }, - VolumeMounts: []corev1.VolumeMount{ - { - MountPath: "/etc/ords/config/", - Name: "datamount", - }, - { - MountPath: "/opt/oracle/variables/", - Name: "varmount", - }, + Command: []string{"/bin/sh", "-c", fmt.Sprintf("chown %d:%d /etc/ords/config/ || true", int(dbcommons.ORACLE_UID), int(dbcommons.DBA_GUID))}, + SecurityContext: &corev1.SecurityContext{ + // User ID 0 means, root user + RunAsUser: func() *int64 { i := int64(0); return &i }(), }, + VolumeMounts: []corev1.VolumeMount{{ + MountPath: "/etc/ords/config/", + Name: "datamount", + }}, }) - return initContainers - }(), - Containers: []corev1.Container{{ - Name: m.Name, - Image: m.Spec.Image.PullFrom, - Ports: func() []corev1.ContainerPort { - ports := []corev1.ContainerPort{ - { - ContainerPort: 8181, // Default application port - }, - } - if m.Spec.MongoDbApi { - ports = append(ports, corev1.ContainerPort{ - ContainerPort: 27017, // MongoDB port - }) - } - return ports - }(), - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"/bin/sh", "-c", dbcommons.ORDSReadinessProbe}, - }, - }, - InitialDelaySeconds: 20, - TimeoutSeconds: 20, - PeriodSeconds: func() int32 { - if m.Spec.ReadinessCheckPeriod > 0 { - return int32(m.Spec.ReadinessCheckPeriod) - } - return 60 - }(), + } + + initContainers = append(initContainers, corev1.Container{ + Name: "init-ords", + Image: m.Spec.Image.PullFrom, + Command: []string{"/bin/sh"}, + Args: []string{ + "-c", + fmt.Sprintf("while [ ! -f /opt/oracle/variables/%s ]; do sleep 0.5; done", "conn_string.txt"), }, VolumeMounts: []corev1.VolumeMount{ { @@ -633,75 +589,119 @@ func (r *OracleRestDataServiceReconciler) instantiatePodSpec(m *dbapi.OracleRest MountPath: "/opt/oracle/variables/", Name: "varmount", }, - }, - Env: func() []corev1.EnvVar { - // After ORDS is Installed, we DELETE THE OLD ORDS Pod and create new ones ONLY USING BELOW ENV VARIABLES. - return []corev1.EnvVar{ - { - Name: "ORACLE_HOST", - Value: n.Name, - }, - { - Name: "ORACLE_PORT", - Value: "1521", - }, - { - Name: "ORACLE_SERVICE", - Value: func() string { - if m.Spec.OracleService != "" { - return m.Spec.OracleService - } - return n.Spec.Sid - }(), - }, - { - Name: "ORDS_USER", - Value: func() string { - if m.Spec.OrdsUser != "" { - return m.Spec.OrdsUser - } - return "ORDS_PUBLIC_USER" - }(), - }, - } - }(), - }}, - - TerminationGracePeriodSeconds: func() *int64 { i := int64(30); return &i }(), - - NodeSelector: func() map[string]string { - ns := make(map[string]string) - if len(m.Spec.NodeSelector) != 0 { - for key, value := range m.Spec.NodeSelector { - ns[key] = value - } + }, + }) + return initContainers + }(), + Containers: []corev1.Container{{ + Name: m.Name, + Image: m.Spec.Image.PullFrom, + Ports: func() []corev1.ContainerPort { + ports := []corev1.ContainerPort{ + { + ContainerPort: 8181, // Default application port + }, } - return ns - }(), - ServiceAccountName: func() string { - if m.Spec.ServiceAccountName != "" { - return m.Spec.ServiceAccountName + if m.Spec.MongoDbApi { + ports = append(ports, corev1.ContainerPort{ + ContainerPort: 27017, // MongoDB port + }) } - return "default" + return ports }(), - SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: func() *int64 { i := int64(dbcommons.ORACLE_UID); return &i }(), - RunAsGroup: func() *int64 { i := int64(dbcommons.DBA_GUID); return &i }(), - FSGroup: func() *int64 { i := int64(dbcommons.DBA_GUID); return &i }(), + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"/bin/sh", "-c", dbcommons.ORDSReadinessProbe}, + }, + }, + InitialDelaySeconds: 20, + TimeoutSeconds: 20, + PeriodSeconds: func() int32 { + if m.Spec.ReadinessCheckPeriod > 0 { + return int32(m.Spec.ReadinessCheckPeriod) + } + return 60 + }(), }, - - ImagePullSecrets: []corev1.LocalObjectReference{ + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/etc/ords/config/", + Name: "datamount", + }, { - Name: m.Spec.Image.PullSecrets, + MountPath: "/opt/oracle/variables/", + Name: "varmount", }, }, + Env: func() []corev1.EnvVar { + // After ORDS is Installed, we DELETE THE OLD ORDS Pod and create new ones ONLY USING BELOW ENV VARIABLES. + return []corev1.EnvVar{ + { + Name: "ORACLE_HOST", + Value: n.Name, + }, + { + Name: "ORACLE_PORT", + Value: "1521", + }, + { + Name: "ORACLE_SERVICE", + Value: func() string { + if m.Spec.OracleService != "" { + return m.Spec.OracleService + } + return n.Spec.Sid + }(), + }, + { + Name: "ORDS_USER", + Value: func() string { + if m.Spec.OrdsUser != "" { + return m.Spec.OrdsUser + } + return "ORDS_PUBLIC_USER" + }(), + }, + } + }(), + }}, + + TerminationGracePeriodSeconds: func() *int64 { i := int64(30); return &i }(), + + NodeSelector: func() map[string]string { + ns := make(map[string]string) + if len(m.Spec.NodeSelector) != 0 { + for key, value := range m.Spec.NodeSelector { + ns[key] = value + } + } + return ns + }(), + ServiceAccountName: func() string { + if m.Spec.ServiceAccountName != "" { + return m.Spec.ServiceAccountName + } + return "default" + }(), + SecurityContext: &corev1.PodSecurityContext{ + RunAsUser: func() *int64 { i := int64(dbcommons.ORACLE_UID); return &i }(), + RunAsGroup: func() *int64 { i := int64(dbcommons.DBA_GUID); return &i }(), + FSGroup: func() *int64 { i := int64(dbcommons.DBA_GUID); return &i }(), }, - } - - // Set oracleRestDataService instance as the owner and controller - // ctrl.SetControllerReference(m, initSecret, r.Scheme) - ctrl.SetControllerReference(m, pod, r.Scheme) - return pod + + ImagePullSecrets: []corev1.LocalObjectReference{ + { + Name: m.Spec.Image.PullSecrets, + }, + }, + }, + } + + // Set oracleRestDataService instance as the owner and controller + // ctrl.SetControllerReference(m, initSecret, r.Scheme) + ctrl.SetControllerReference(m, pod, r.Scheme) + return pod } //############################################################################# @@ -834,8 +834,8 @@ func (r *OracleRestDataServiceReconciler) createSVC(ctx context.Context, req ctr fmt.Sprint(svc.Spec.Ports[0].Port) + "/ords/apex" } if m.Status.MongoDbApi && len(svc.Spec.Ports) > 1 { - m.Status.MongoDbApiAccessUrl = "mongodb://[{user}:{password}@]"+ lbAddress + ":"+ - fmt.Sprint(svc.Spec.Ports[1].Port)+ "/{user}?" + + m.Status.MongoDbApiAccessUrl = "mongodb://[{user}:{password}@]" + lbAddress + ":" + + fmt.Sprint(svc.Spec.Ports[1].Port) + "/{user}?" + "authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true" } else { m.Status.MongoDbApiAccessUrl = "" @@ -854,8 +854,8 @@ func (r *OracleRestDataServiceReconciler) createSVC(ctx context.Context, req ctr m.Status.ApxeUrl = "http://" + nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/ords/apex" } if m.Status.MongoDbApi && len(svc.Spec.Ports) > 1 { - m.Status.MongoDbApiAccessUrl = "mongodb://[{user}:{password}@]"+ nodeip + ":"+ - fmt.Sprint(svc.Spec.Ports[1].NodePort)+ "/{user}?" + + m.Status.MongoDbApiAccessUrl = "mongodb://[{user}:{password}@]" + nodeip + ":" + + fmt.Sprint(svc.Spec.Ports[1].NodePort) + "/{user}?" + "authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true" } else { m.Status.MongoDbApiAccessUrl = "" @@ -900,7 +900,6 @@ func (r *OracleRestDataServiceReconciler) createPVC(ctx context.Context, req ctr return requeueN, nil } - // ############################################################################# // // Function for creating connection sting file @@ -977,10 +976,10 @@ func (r *OracleRestDataServiceReconciler) createConnectionString(m *dbapi.Oracle adminPassword := string(secret.Data[m.Spec.AdminPassword.SecretKey]) _, err = dbcommons.ExecCommand(r, r.Config, pod.Name, pod.Namespace, "init-ords", - ctx, req, true, "bash", "-c", - fmt.Sprintf("mkdir -p /opt/oracle/variables && echo %[1]s > /opt/oracle/variables/%[2]s", - fmt.Sprintf(dbcommons.DbConnectString, adminPassword, n.Name, n.Status.Pdbname), - "conn_string.txt")) + ctx, req, true, "bash", "-c", + fmt.Sprintf("mkdir -p /opt/oracle/variables && echo %[1]s > /opt/oracle/variables/%[2]s", + fmt.Sprintf(dbcommons.DbConnectString, adminPassword, n.Name, n.Status.Pdbname), + "conn_string.txt")) if err != nil { r.Log.Error(err, err.Error()) @@ -1318,7 +1317,7 @@ func (r *OracleRestDataServiceReconciler) configureApex(m *dbapi.OracleRestDataS } // Obtain admin password of the referred database - + adminPasswordSecret := &corev1.Secret{} err := r.Get(ctx, types.NamespacedName{Name: m.Spec.AdminPassword.SecretName, Namespace: m.Namespace}, adminPasswordSecret) if err != nil { @@ -1400,7 +1399,6 @@ func (r *OracleRestDataServiceReconciler) deleteSecrets(m *dbapi.OracleRestDataS } } - // ############################################################################# // // Enable MongoDB API Support @@ -1410,14 +1408,14 @@ func (r *OracleRestDataServiceReconciler) enableMongoDB(m *dbapi.OracleRestDataS sidbReadyPod corev1.Pod, ordsReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) ctrl.Result { log := r.Log.WithValues("enableMongoDB", req.NamespacedName) - if (m.Spec.MongoDbApi && !m.Status.MongoDbApi) || // setting MongoDbApi to true - (!m.Spec.MongoDbApi && m.Status.MongoDbApi) { // setting MongoDbApi to false - m.Status.Status = dbcommons.StatusUpdating + if (m.Spec.MongoDbApi && !m.Status.MongoDbApi) || // setting MongoDbApi to true + (!m.Spec.MongoDbApi && m.Status.MongoDbApi) { // setting MongoDbApi to false + m.Status.Status = dbcommons.StatusUpdating - out, err := dbcommons.ExecCommand(r, r.Config, ordsReadyPod.Name, ordsReadyPod.Namespace, "", ctx, req, true, "bash", "-c", - fmt.Sprintf(dbcommons.ConfigMongoDb, strconv.FormatBool(m.Spec.MongoDbApi)) ) + out, err := dbcommons.ExecCommand(r, r.Config, ordsReadyPod.Name, ordsReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf(dbcommons.ConfigMongoDb, strconv.FormatBool(m.Spec.MongoDbApi))) log.Info("configMongoDB Output: \n" + out) - + if strings.Contains(strings.ToUpper(out), "ERROR") { return requeueY } @@ -1435,16 +1433,16 @@ func (r *OracleRestDataServiceReconciler) enableMongoDB(m *dbapi.OracleRestDataS eventMsg := "configuration of MongoDb API completed!" r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) log.Info(eventMsg) - + // ORDS service is resatrted r.Log.Info("Restarting ORDS Service : " + m.Name) svc := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{Name: m.Name, Namespace: m.Namespace,}, + ObjectMeta: metav1.ObjectMeta{Name: m.Name, Namespace: m.Namespace}, } var gracePeriodSeconds int64 = 0 policy := metav1.DeletePropagationForeground err = r.Delete(ctx, svc, &client.DeleteOptions{ - GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy,}) + GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy}) if err != nil { r.Log.Error(err, "Failed to delete ORDS service", "Service Name", m.Name) return requeueY diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 1e78424d..72dd38d8 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -46,7 +46,7 @@ import ( "strings" "time" - dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" dbcommons "github.com/oracle/oracle-database-operator/commons/database" "golang.org/x/text/cases" "golang.org/x/text/language" diff --git a/controllers/dataguard/dataguard_utils.go b/controllers/dataguard/dataguard_utils.go index 65efeb5d..6b529068 100644 --- a/controllers/dataguard/dataguard_utils.go +++ b/controllers/dataguard/dataguard_utils.go @@ -45,7 +45,7 @@ import ( "strings" "time" - dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" dbcommons "github.com/oracle/oracle-database-operator/commons/database" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" diff --git a/controllers/dataguard/dataguardbroker_controller.go b/controllers/dataguard/dataguardbroker_controller.go index f3878a05..6eadc953 100644 --- a/controllers/dataguard/dataguardbroker_controller.go +++ b/controllers/dataguard/dataguardbroker_controller.go @@ -45,7 +45,7 @@ import ( "time" "github.com/go-logr/logr" - dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" dbcommons "github.com/oracle/oracle-database-operator/commons/database" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" diff --git a/main.go b/main.go index 35374717..ee9992b7 100644 --- a/main.go +++ b/main.go @@ -66,8 +66,8 @@ import ( dataguardcontroller "github.com/oracle/oracle-database-operator/controllers/dataguard" databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" - observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" observabilityv1 "github.com/oracle/oracle-database-operator/apis/observability/v1" + observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" observabilityv4 "github.com/oracle/oracle-database-operator/apis/observability/v4" observabilitycontroller "github.com/oracle/oracle-database-operator/controllers/observability" // +kubebuilder:scaffold:imports @@ -395,6 +395,18 @@ func main() { os.Exit(1) } + if err = (&databasev4.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") + os.Exit(1) + } + if err = (&databasev4.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") + os.Exit(1) + } + if err = (&databasev4.OracleRestDataService{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") + os.Exit(1) + } // +kubebuilder:scaffold:builder // Add index for PDB CR to enable mgr to cache PDBs From ef0f4a320e973463a6758beea6230a4c71302005 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Wed, 22 Jan 2025 19:24:08 +0000 Subject: [PATCH 157/414] Patch/update fields and constants --- commons/observability/constants.go | 4 ++-- .../observability/v1/databaseobserver_logs_promtail.yaml | 3 --- .../v1alpha1/databaseobserver_custom_config.yaml | 3 --- .../v1alpha1/databaseobserver_logs_promtail.yaml | 3 --- .../observability/v4/databaseobserver_custom_config.yaml | 3 --- .../observability/v4/databaseobserver_logs_promtail.yaml | 3 --- docs/observability/README.md | 1 - 7 files changed, 2 insertions(+), 18 deletions(-) diff --git a/commons/observability/constants.go b/commons/observability/constants.go index 1faabde8..45f06e49 100644 --- a/commons/observability/constants.go +++ b/commons/observability/constants.go @@ -39,7 +39,7 @@ const ( DefaultOCIConfigTenancyKey = "tenancy" DefaultOCIConfigUserKey = "user" - DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:1.5.1" + DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:1.5.2" DefaultServicePort = 9161 DefaultServiceTargetPort = 9161 DefaultAppPort = 8080 @@ -47,7 +47,7 @@ const ( DefaultServiceType = "ClusterIP" DefaultReplicaCount = 1 DefaultExporterConfigMountRootPath = "/oracle/observability" - DefaultOracleHome = "/lib/oracle/23/client64/lib" + DefaultOracleHome = "/lib/oracle/21/client64/lib" DefaultOracleTNSAdmin = DefaultOracleHome + "/network/admin" DefaultExporterConfigmapFilename = "config.toml" DefaultVaultPrivateKeyRootPath = "/oracle/config" diff --git a/config/samples/observability/v1/databaseobserver_logs_promtail.yaml b/config/samples/observability/v1/databaseobserver_logs_promtail.yaml index 3a92cfea..8130f487 100644 --- a/config/samples/observability/v1/databaseobserver_logs_promtail.yaml +++ b/config/samples/observability/v1/databaseobserver_logs_promtail.yaml @@ -50,9 +50,6 @@ spec: args: [ "--log.level=info" ] commands: [ "/oracledb_exporter" ] - service: - port: 9161 - configuration: configMap: key: "config.toml" diff --git a/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml b/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml index 6aec94c2..8e0d0623 100644 --- a/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml @@ -35,9 +35,6 @@ spec: args: [ "--log.level=info" ] commands: [ "/oracledb_exporter" ] - service: - port: 9161 - prometheus: serviceMonitor: labels: diff --git a/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml b/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml index 0d9cc434..28592cb0 100644 --- a/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml @@ -50,9 +50,6 @@ spec: args: [ "--log.level=info" ] commands: [ "/oracledb_exporter" ] - service: - port: 9161 - configuration: configMap: key: "config.toml" diff --git a/config/samples/observability/v4/databaseobserver_custom_config.yaml b/config/samples/observability/v4/databaseobserver_custom_config.yaml index c4258b0f..dd2e3da5 100644 --- a/config/samples/observability/v4/databaseobserver_custom_config.yaml +++ b/config/samples/observability/v4/databaseobserver_custom_config.yaml @@ -35,9 +35,6 @@ spec: args: [ "--log.level=info" ] commands: [ "/oracledb_exporter" ] - service: - port: 9161 - configuration: configMap: key: "config.toml" diff --git a/config/samples/observability/v4/databaseobserver_logs_promtail.yaml b/config/samples/observability/v4/databaseobserver_logs_promtail.yaml index b1a40281..26a747a3 100644 --- a/config/samples/observability/v4/databaseobserver_logs_promtail.yaml +++ b/config/samples/observability/v4/databaseobserver_logs_promtail.yaml @@ -50,9 +50,6 @@ spec: args: [ "--log.level=info" ] commands: [ "/oracledb_exporter" ] - service: - port: 9161 - configuration: configMap: key: "config.toml" diff --git a/docs/observability/README.md b/docs/observability/README.md index e5970b0d..895b8f85 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -57,7 +57,6 @@ the databaseObserver custom resource offers the following properties to be confi | Attribute | Type | Default | Required? | Example | |----------------------------------------------------|--------|---------------------------------------------------------------------|:------------|-----------------------------------------------------------------------| -| `spec.database.dbRole` | string | - | Optional | _SYSDBA_ | | `spec.database.dbUser.key` | string | user | Optional | _username_ | | `spec.database.dbUser.secret` | string | - | Yes | _db-secret_ | | `spec.database.dbPassword.key` | string | password | Optional | _admin-password_ | From 7e64952a654444284f6d12713719e7310c42ebf4 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Thu, 23 Jan 2025 11:51:24 +0000 Subject: [PATCH 158/414] Exposing truecache connect string --- .../singleinstancedatabase_controller.go | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 72dd38d8..7e248248 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -225,7 +225,6 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct sidbRole, err := dbcommons.GetDatabaseRole(readyPod, r, r.Config, ctx, req) if sidbRole == "PRIMARY" { - // Update DB config result, err = r.updateDBConfig(singleInstanceDatabase, readyPod, ctx, req) if result.Requeue { @@ -248,7 +247,6 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct } } else { - // Database is in role of standby if singleInstanceDatabase.Status.DgBroker == nil { err = SetupStandbyDatabase(r, singleInstanceDatabase, referredPrimaryDatabase, ctx, req) if err != nil { @@ -309,7 +307,7 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct } } - // If LoadBalancer = true , ensure Connect String is updated + // This is to ensure that in case of LoadBalancer services the, the Load Balancer is ready to serve the requests if singleInstanceDatabase.Status.ConnectString == dbcommons.ValueUnavailable { r.Log.Info("Connect string not available for the database " + singleInstanceDatabase.Name) return requeueY, nil @@ -1872,34 +1870,32 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplaceSVC(ctx context.Contex if pdbName == "" { pdbName = strings.ToUpper(m.Spec.Pdbname) } - if m.Spec.CreateAs != "truecache" { - if m.Spec.LoadBalancer { - m.Status.ClusterConnectString = extSvc.Name + "." + extSvc.Namespace + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) - if len(extSvc.Status.LoadBalancer.Ingress) > 0 { - // 'lbAddress' will contain the Fully Qualified Hostname of the LB. If the hostname is not available it will contain the IP address of the LB - lbAddress := extSvc.Status.LoadBalancer.Ingress[0].Hostname - if lbAddress == "" { - lbAddress = extSvc.Status.LoadBalancer.Ingress[0].IP - } - m.Status.ConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) - m.Status.PdbConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(pdbName) - oemExpressUrl = "https://" + lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[0].Port) + "/em" - if m.Spec.EnableTCPS { - m.Status.TcpsConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].Port) + "/" + strings.ToUpper(sid) - m.Status.TcpsPdbConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].Port) + "/" + strings.ToUpper(pdbName) - } + if m.Spec.LoadBalancer { + m.Status.ClusterConnectString = extSvc.Name + "." + extSvc.Namespace + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) + if len(extSvc.Status.LoadBalancer.Ingress) > 0 { + // 'lbAddress' will contain the Fully Qualified Hostname of the LB. If the hostname is not available it will contain the IP address of the LB + lbAddress := extSvc.Status.LoadBalancer.Ingress[0].Hostname + if lbAddress == "" { + lbAddress = extSvc.Status.LoadBalancer.Ingress[0].IP } - } else { - m.Status.ClusterConnectString = extSvc.Name + "." + extSvc.Namespace + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) - nodeip := dbcommons.GetNodeIp(r, ctx, req) - if nodeip != "" { - m.Status.ConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[1].NodePort) + "/" + strings.ToUpper(sid) - m.Status.PdbConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[1].NodePort) + "/" + strings.ToUpper(pdbName) - oemExpressUrl = "https://" + nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[0].NodePort) + "/em" - if m.Spec.EnableTCPS { - m.Status.TcpsConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].NodePort) + "/" + strings.ToUpper(sid) - m.Status.TcpsPdbConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].NodePort) + "/" + strings.ToUpper(pdbName) - } + m.Status.ConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) + m.Status.PdbConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(pdbName) + oemExpressUrl = "https://" + lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[0].Port) + "/em" + if m.Spec.EnableTCPS { + m.Status.TcpsConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].Port) + "/" + strings.ToUpper(sid) + m.Status.TcpsPdbConnectString = lbAddress + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].Port) + "/" + strings.ToUpper(pdbName) + } + } + } else { + m.Status.ClusterConnectString = extSvc.Name + "." + extSvc.Namespace + ":" + fmt.Sprint(extSvc.Spec.Ports[1].Port) + "/" + strings.ToUpper(sid) + nodeip := dbcommons.GetNodeIp(r, ctx, req) + if nodeip != "" { + m.Status.ConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[1].NodePort) + "/" + strings.ToUpper(sid) + m.Status.PdbConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[1].NodePort) + "/" + strings.ToUpper(pdbName) + oemExpressUrl = "https://" + nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[0].NodePort) + "/em" + if m.Spec.EnableTCPS { + m.Status.TcpsConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].NodePort) + "/" + strings.ToUpper(sid) + m.Status.TcpsPdbConnectString = nodeip + ":" + fmt.Sprint(extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].NodePort) + "/" + strings.ToUpper(pdbName) } } } From f7f6d7cbf6f4d95f540a575c270764614a5d37c0 Mon Sep 17 00:00:00 2001 From: ting-lan-wang Date: Fri, 24 Jan 2025 13:08:02 -0500 Subject: [PATCH 159/414] Update documentation for configuring network access --- .../adb/autonomousdatabase_update_mtls.yaml | 6 +- ...onomousdatabase_update_network_access.yaml | 54 +++-- docs/adb/NETWORK_ACCESS_OPTIONS.md | 217 ++++++++++-------- 3 files changed, 145 insertions(+), 132 deletions(-) diff --git a/config/samples/adb/autonomousdatabase_update_mtls.yaml b/config/samples/adb/autonomousdatabase_update_mtls.yaml index 9499523f..25eda529 100644 --- a/config/samples/adb/autonomousdatabase_update_mtls.yaml +++ b/config/samples/adb/autonomousdatabase_update_mtls.yaml @@ -10,10 +10,8 @@ spec: action: Update details: id: ocid1.autonomousdatabase... - networkAccess: - # Set the patameter to false to allow both TLS and mutual TLS (mTLS) authentication. - # Avaiable when the networkAccessType is RESTRICTED or PRIVATE on shared Autnomous Database. - isMTLSConnectionRequired: false + # Set the patameter to false to allow both TLS and mutual TLS (mTLS) authentication, or true to require mTLS connections and disallow TLS connections. + isMTLSConnectionRequired: true # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: diff --git a/config/samples/adb/autonomousdatabase_update_network_access.yaml b/config/samples/adb/autonomousdatabase_update_network_access.yaml index bb28b3a9..7dd3fa0c 100644 --- a/config/samples/adb/autonomousdatabase_update_network_access.yaml +++ b/config/samples/adb/autonomousdatabase_update_network_access.yaml @@ -10,37 +10,35 @@ spec: action: Update details: id: ocid1.autonomousdatabase... - networkAccess: - # Allow secure access from everywhere. - accessType: PUBLIC + # # Allow secure access from everywhere. Uncomment one of the following field depends on your network access configuration. + # accessControlList: + # - + # privateEndpoint: "" - # # Uncomment this block to configure the network access type with the RESTRICTED option. - # # This option lets you restrict access by defining access control rules in an Access Control List (ACL). - # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. - # # Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs. - # accessType: RESTRICTED - # accessControlList: - # - 1.1.1.1 - # - 1.1.0.0/16 - # - ocid1.vcn... - # - ocid1.vcn...;1.1.1.1 - # - ocid1.vcn...;1.1.0.0/16 + # # Uncomment this block to configure the network access type with the RESTRICTED option. + # # This option lets you restrict access by defining access control rules in an Access Control List (ACL). + # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. + # # Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs. + # accessControlList: + # - 1.1.1.1 + # - 1.1.0.0/16 + # - ocid1.vcn... + # - ocid1.vcn...;1.1.1.1 + # - ocid1.vcn...;1.1.0.0/16 - # # Uncomment this block to configure the network access type with the PRIVATE option. - # # This option assigns a private endpoint, private IP, and hostname to your database. - # # Specifying this option allows traffic only from the VCN you specify. - # # This allows you to define security rules, ingress/egress, at the Network Security Group (NSG) level and to control traffic to your Autonomous Database. - # accessType: PRIVATE - # privateEndpoint: - # subnetOCID: ocid1.subnet... - # nsgOCIDs: # Optional - # - ocid1.networksecuritygroup... + # # Uncomment this block to configure the network access type with the PRIVATE option. + # # This option assigns a private endpoint, private IP, and hostname to your database. + # # Specifying this option allows traffic only from the VCN you specify. + # # This allows you to define security rules, ingress/egress, at the Network Security Group (NSG) level and to control traffic to your Autonomous Database. + # privateEndpoint: + # subnetOCID: ocid1.subnet... + # nsgOCIDs: # Optional + # - ocid1.networksecuritygroup... - # # Uncomment this block to configure the network access of an dedicated Autonomous Database (ADB-D) with an access control list. - # isAccessControlEnabled: true - # accessControlList: - # - 1.1.1.1 - # - 1.1.0.0/16 + # # Uncomment this block to configure the network access of an dedicated Autonomous Database (ADB-D) with an access control list. + # accessControlList: + # - 1.1.1.1 + # - 1.1.0.0/16 # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: diff --git a/docs/adb/NETWORK_ACCESS_OPTIONS.md b/docs/adb/NETWORK_ACCESS_OPTIONS.md index e029b52d..66b968d0 100644 --- a/docs/adb/NETWORK_ACCESS_OPTIONS.md +++ b/docs/adb/NETWORK_ACCESS_OPTIONS.md @@ -7,77 +7,58 @@ Network access for Autonomous Database includes public access, and configuring s For more information about these options, see: [Configuring Network Access with Access Control Rules (ACLs) and Private Endpoints ](https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/autonomous-network-access.html#GUID-D2D468C3-CA2D-411E-92BC-E122F795A413). ## Supported Features -Review the network access configuration options available to you with Autonomous Database. +Review the following options available to you with Autonomous Database. -### Types of Network Access +* [Configuring Network Access with Allowing Secure Access from Anywhere](#configuring-network-access-with-allowing-secure-access-from-anywhere) on shared Exadata infrastructure +* [Configuring Network Access with Access Control Rules (ACLs)](#configuring-network-access-with-access-control-rules-acls) on shared Exadata infrastructure +* [Configure Network Access with Private Endpoint Access Only](#configure-network-access-with-private-endpoint-access-only) on shared Exadata infrastructure +* [Allowing TLS or Require Only Mutual TLS (mTLS) Authentication](#allowing-tls-or-require-only-mutual-tls-mtls-authentication) on shared Exadata infrastructure +* [Autonomous Database with access control list enabled](#autonomous-database-with-access-control-list-enabled-on-dedicated-exadata-infrastructure) on dedicated Exadata infrastructure -There are three types of network access supported by Autonomous Database: +## Configuring Network Access with Allowing Secure Access from Anywhere -* **PUBLIC** +Before changing the Network Access to Allowing Secure Access from Anywhere, ensure that your network security protocol requries only mTLS (Mutual TLS) authentication. For more details, see: [Allow both TLS and mutual TLS (mTLS) authentication](#allow-both-tls-and-mutual-tls-mtls-authentication). If mTLS enforcement is already enabled on your Autonomous Database, you can skip this step. - The Public option permits secure access from anywhere. The network access type is PUBLIC if no option is specified in the specification. With this option, mutual TLS (mTLS) authentication is always required to connect to the database. This option is available only for databases on shared Exadata infrastructure. +To specify that Autonomous Database can be connected from any location with a valid credential, complete one of the following procedures based on your network access configuration. -* **RESTRICTED** - - The Restricted option permits connections to the database only as specified by the access control lists (ACLs) that you create. This option is available only for databases on shared Exadata infrastructure. - - You can add the following to your ACL: - * **IP Address**: Specify one or more individual public IP addresses. Use commas to delimit your addresses in the input field. - * **CIDR Block**: Specify one or more ranges of public IP addresses using CIDR notation. Use commas to separate your CIDR block entries in the input field. - * **Virtual Cloud Network (OCID)** (applies to Autonomous Databases on shared Exadata infrastructure): Specify the Oracle Cloud Identifier (OCID) of a virtual cloud network (VCN). If you want to specify multiple IP addresses or CIDR ranges within the same VCN, then do not create multiple access control list entries. Instead, use one access control list entry with the values for the multiple IP addresses or CIDR ranges, separated by commas. - -* **PRIVATE** - - The Private option creates a private endpoint for your database within a specified VCN. This option is available for databases on shared Exadata infrastructure, and is the only available option for databases on dedicated Exadata infrastructure. Review the private options for your configuration: - - * **Autonomous Databases on shared Exadata infrastructure**: - - This option permits access through private enpoints by specifying the OCIDs of a subnet and the network security groups (NSGs) under the same VCN in the specification. - - * **Autonomous Databases on dedicated Exadata infrastructure**: - - The network path to a dedicated Autonomous Database is through a VCN and subnet defined by the dedicated infrastucture hosting the database. Usually, the subnet is defined as private, which means that there is no public Internet access to the databases. - - Autonomous Database supports restricted access using an ACL. You have the option to enable an ACL by setting the `isAccessControlEnabled` parameter. If access is disabled, then database access is defined by the network security rules. If enabled, then database access is restricted to the IP addresses and CIDR blocks defined in the ACL. Note that enabling an ACL with an empty list of IP addresses makes the database inaccessible. See [Autonomous Database with Private Endpoint](https://docs.oracle.com/en-us/iaas/Content/Database/Concepts/adbsprivateaccess.htm) for overview and examples for private endpoint. - -### Allowing TLS or Require Only Mutual TLS (mTLS) Authentication - -If your Autonomous Database instance is configured to allow only mTLS connections, then you can reconfigure the instance to permit both mTLS and TLS connections. When you reconfigure the instance to permit both mTLS and TLS, you can use both authentication types at the same time, so that connections are no longer restricted to require mTLS authentication. - -This option only applies to Autonomous Databases on shared Exadata infrastructure. You can permit TLS connections when network access type is configured by using one of the following options: - -* **RESTRICTED**: with ACLs defined. -* **PRIVATE**: with a private endpoint defined. - -## Example YAML - -You can always configure the network access options when you create an Autonomous Database, or update the settings after you create the database. Following are some example YAMLs that show how to configure the networking with different network access options. - -For Autonomous Databases on shared Exadata infrastructure, review the following examples: - -* Configure network access [with PUBLIC access type](#autonomous-database-with-public-access-type-on-shared-exadata-infrastructure) -* Configure network access [with RESTRICTED access type](#autonomous-database-with-restricted-access-type-on-shared-exadata-infrastructure) -* Configure network access [with PRIVATE access type](#autonomous-database-with-private-access-type-on-shared-exadata-infrastructure) -* [Change the mutual TLS (mTLS) authentication setting](#allow-both-tls-and-mutual-tls-mtls-authentication-of-autonomous-database-on-shared-exadata-infrastructure) +### Option 1 - Change the Network Access from "Secure Access from Allowed IPs and VCNs Only" to "Allowing Secure Access from Anywhere" +1. Add the following parameters to the specification. An example file is availble here: [`config/samples/adb/autonomousdatabase_update_network_access.yaml`](./../../config/samples/adb/autonomousdatabase_update_network_access.yaml): -For Autonomous Databases on dedicated Exadata infrastructure, refiew the following examples: + | Attribute | Type | Description | + |----|----|----| + | `whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for Autonomous Databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | -* Configure network access [with access control list enabled](#autonomous-database-with-access-control-list-enabled-on-dedicated-exadata-infrastructure) + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + action: Update + details: + autonomousDatabaseOCID: ocid1.autonomousdatabase... + whitelistedIps: + - + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` -> Note: -> -> * Operations on Exadata infrastructure require an `AutonomousDatabase` object to be in your cluster. These examples assume either the provision operation or the bind operation has been done before you begin, and the operator is authorized with API Key Authentication. -> * If you are creating an Autonomous Database, then see step 4 of [Provision an Autonomous Database](./README.md#provision-an-autonomous-database) in [Managing Oracle Autonomous Databases with Oracle Database Operator for Kubernetes](./README.md) topic to return to provisioning instructions. +2. Apply the yaml: -### Autonomous Database with PUBLIC access type on shared Exadata infrastructure + ```sh + $ kubectl apply -f config/samples/adb/autonomousdatabase_update_network_access.yaml + autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured + ``` -To configure the network with PUBLIC access type, complete this procedure. +### Option 2 - Change the Network Access from "Private Endpoint Access Only" to "Allowing Secure Access from Anywhere" 1. Add the following parameters to the specification. An example file is availble here: [`config/samples/adb/autonomousdatabase_update_network_access.yaml`](./../../config/samples/adb/autonomousdatabase_update_network_access.yaml): - | Attribute | Type | Description | Required? | - |----|----|----|----| - | `networkAccess.accessType` | string | An enumeration (enum) value that defines how the database can be accessed. The value can be PUBLIC, RESTRICTED or PRIVATE. See [Types of Network Access](#types-of-network-access) for more descriptions. | Yes | + | Attribute | Type | Description | + |----|----|----| + | `privateEndpointLabel` | string | The hostname prefix for the resource. | ```yaml --- @@ -86,11 +67,10 @@ To configure the network with PUBLIC access type, complete this procedure. metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... - networkAccess: - # Allow secure access from everywhere. - accessType: PUBLIC + privateEndpointLabel: "" ociConfig: configMapName: oci-cred secretName: oci-privatekey @@ -99,21 +79,20 @@ To configure the network with PUBLIC access type, complete this procedure. 2. Apply the yaml: ```sh - kubectl apply -f config/samples/adb/autonomousdatabase_update_network_access.yaml + $ kubectl apply -f config/samples/adb/autonomousdatabase_update_network_access.yaml autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured ``` -### Autonomous Database with RESTRICTED access type on shared Exadata infrastructure +## Configuring Network Access with Access Control Rules (ACLs) -To configure the network with RESTRICTED access type, complete this procedure. +To configure Network Access with ACLs, complete this procedure. 1. Add the following parameters to the specification. An example file is availble here: [`config/samples/adb/autonomousdatabase_update_network_access.yaml`](./../../config/samples/adb/autonomousdatabase_update_network_access.yaml): - | Attribute | Type | Description | Required? | - |----|----|----|----| - | `networkAccess.accessType` | string | An enumerated (enum) that defines how the database can be accessed. The value can be PUBLIC, RESTRICTED or PRIVATE. See [Types of Network Access](#types-of-network-access) for more descriptions. | Yes | - | `networkAccess.accessControlList` | []string | The client IP access control list (ACL). This feature is available for Autonomous Databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | Yes | + | Attribute | Type | Description | + |----|----|----| + | `whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for Autonomous Databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | ```yaml --- @@ -122,12 +101,12 @@ To configure the network with RESTRICTED access type, complete this procedure. metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... networkAccess: # Restrict access by defining access control rules in an Access Control List (ACL). - accessType: RESTRICTED - accessControlList: + whitelistedIps: - 1.1.1.1 - 1.1.0.0/16 - ocid1.vcn... @@ -141,12 +120,12 @@ To configure the network with RESTRICTED access type, complete this procedure. 2. Apply the yaml: ```sh - kubectl apply -f config/samples/adb/autonomousdatabase_update_network_access.yaml + $ kubectl apply -f config/samples/adb/autonomousdatabase_update_network_access.yaml autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured -### Autonomous Database with PRIVATE access type on shared Exadata infrastructure +## Configure Network Access with Private Endpoint Access Only -To configure the network with PRIVATE access type, complete this procedure +To change the Network Access to Private Endpoint Access Only, complete this procedure 1. Visit [Overview of VCNs and Subnets](https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/managingVCNs_topic-Overview_of_VCNs_and_Subnets.htm#console) and [Network Security Groups](https://docs.oracle.com/en-us/iaas/Content/Network/Concepts/networksecuritygroups.htm#working) to see how to create VCNs, subnets, and network security groups (NSGs) if you haven't created them yet. The subnet and the NSG has to be in the same VCN. @@ -154,10 +133,9 @@ To configure the network with PRIVATE access type, complete this procedure | Attribute | Type | Description | Required? | |----|----|----|----| - | `networkAccess.accessType` | string | An enumeration (enum) value that defines how the database can be accessed. The value can be PUBLIC, RESTRICTED or PRIVATE. See [Types of Network Access](#types-of-network-access) for more descriptions. | Yes | - | `networkAccess.privateEndpoint.subnetOCID` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the subnet the resource is associated with.

**Subnet Restrictions:**
- For bare metal DB systems and for single node virtual machine DB systems, do not use a subnet that overlaps with 192.168.16.16/28.
- For Exadata and virtual machine 2-node RAC systems, do not use a subnet that overlaps with 192.168.128.0/20.
- For Autonomous Database, setting this will disable public secure access to the database.
These subnets are used by the Oracle Clusterware private interconnect on the database instance.
Specifying an overlapping subnet will cause the private interconnect to malfunction.
This restriction applies to both the client subnet and the backup subnet. | Yes | - | `networkAccess.privateEndpoint.nsgOCIDs` | string[] | A list of the [OCIDs](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the network security groups (NSGs) that this resource belongs to. Setting this to an empty array after the list is created removes the resource from all NSGs. For more information about NSGs, see [Security Rules](https://docs.cloud.oracle.com/Content/Network/Concepts/securityrules.htm).

**NsgOCIDs restrictions:**
- Autonomous Databases with private access require at least 1 Network Security Group (NSG). The nsgOCIDs array cannot be empty. | Yes | - | `networkAccess.privateEndpoint.hostnamePrefix` | string | The hostname prefix for the resource. | No | + | `subnetId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the subnet the resource is associated with.

**Subnet Restrictions:**
- For bare metal DB systems and for single node virtual machine DB systems, do not use a subnet that overlaps with 192.168.16.16/28.
- For Exadata and virtual machine 2-node RAC systems, do not use a subnet that overlaps with 192.168.128.0/20.
- For Autonomous Database, setting this will disable public secure access to the database.
These subnets are used by the Oracle Clusterware private interconnect on the database instance.
Specifying an overlapping subnet will cause the private interconnect to malfunction.
This restriction applies to both the client subnet and the backup subnet. | Yes | + | `nsgIds` | string[] | The list of [OCIDs](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) for the network security groups (NSGs) to which this resource belongs. Setting this to an empty list removes all resources from all NSGs. For more information about NSGs, see [Security Rule](https://docs.cloud.oracle.com/Content/Network/Concepts/securityrules.htm).

**NsgIds restrictions:**
- A network security group (NSG) is optional for Autonomous Databases with private access. The nsgIds list can be empty. | No | + | `privateEndpointLabel` | string | The resource's private endpoint label.
- Setting the endpoint label to a non-empty string creates a private endpoint database.
- Resetting the endpoint label to an empty string, after the creation of the private endpoint database, changes the private endpoint database to a public endpoint database.
- Setting the endpoint label to a non-empty string value, updates to a new private endpoint database, when the database is disabled and re-enabled.
This setting cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMTLSConnectionRequired, dbWorkload, dbVersion, dbName, or isFreeTier. | No | ```yaml --- @@ -166,29 +144,28 @@ To configure the network with PRIVATE access type, complete this procedure metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... - networkAccess: - # Assigns a private endpoint, private IP, and hostname to your database. - accessType: PRIVATE - privateEndpoint: - subnetOCID: ocid1.subnet... - nsgOCIDs: - - ocid1.networksecuritygroup... + subnetId: ocid1.subnet... + nsgIds: + - ocid1.networksecuritygroup... ociConfig: configMapName: oci-cred secretName: oci-privatekey ``` -### Allow both TLS and mutual TLS (mTLS) authentication of Autonomous Database on shared Exadata infrastructure +## Allowing TLS or Require Only Mutual TLS (mTLS) Authentication + +### Require mutual TLS (mTLS) authentication and Disallow TLS Authentication -If you are using either the RESTRICTED or the PRIVATE network access option, then you can choose whether to permit both TLS and mutual TLS (mTLS) authentication, or to permit only mTLS authentication. To change the mTLS authentication setting, complete the following steps: +To configure your Autonomous Database instance to require mTLS connections and disallow TLS connections, complete this procedure. 1. Add the following parameters to the specification. An example file is availble here: [`config/samples/adb/autonomousdatabase_update_mtls.yaml`](./../../config/samples/adb/autonomousdatabase_update_mtls.yaml): | Attribute | Type | Description | Required? | |----|----|----|----| - | `networkAccess.isMTLSConnectionRequired` | boolean| Indicates whether the Autonomous Database requires mTLS connections. | Yes | + | `isMtlsConnectionRequired` | boolean| Indicates whether the Autonomous Database requires mTLS connections. | Yes | ```yaml --- @@ -197,10 +174,10 @@ If you are using either the RESTRICTED or the PRIVATE network access option, the metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... - networkAccess: - isMTLSConnectionRequired: false + isMtlsConnectionRequired: true ociConfig: configMapName: oci-cred secretName: oci-privatekey @@ -209,20 +186,60 @@ If you are using either the RESTRICTED or the PRIVATE network access option, the 2. Apply the yaml: ```sh - kubectl apply -f config/samples/adb/autonomousdatabase_update_mtls.yaml + $ kubectl apply -f config/samples/adb/autonomousdatabase_update_mtls.yaml autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured ``` -### Autonomous Database with access control list enabled on dedicated Exadata infrastructure +### Allow both TLS and mutual TLS (mTLS) authentication + +If your Autonomous Database instance is configured to allow only mTLS connections, then you can reconfigure the instance to permit both mTLS and TLS connections. When you reconfigure the instance to permit both mTLS and TLS, you can use both authentication types at the same time, so that connections are no longer restricted to require mTLS authentication. + +This option only applies to Autonomous Databases on shared Exadata infrastructure. You can permit TLS connections when network access type is configured by using one of the following options: + +* **Access Control Rules (ACLs)**: with ACLs defined. +* **Private Endpoint Access Only**: with a private endpoint defined. + +Complete this procedure to allow both TLS and mTLS authentication. -To configure the network with RESTRICTED access type using an access control list (ACL), complete this procedure. +1. Add the following parameters to the specification. An example file is availble here: [`config/samples/adb/autonomousdatabase_update_mtls.yaml`](./../../config/samples/adb/autonomousdatabase_update_mtls.yaml): + + | Attribute | Type | Description | Required? | + |----|----|----|----| + | `isMtlsConnectionRequired` | boolean| Indicates whether the Autonomous Database requires mTLS connections. | Yes | + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + action: Update + details: + autonomousDatabaseOCID: ocid1.autonomousdatabase... + isMtlsConnectionRequired: false + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml: + + ```sh + $ kubectl apply -f config/samples/adb/autonomousdatabase_update_mtls.yaml + autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured + ``` + +## Autonomous Database with access control list enabled on dedicated Exadata infrastructure + +To configure the network access of Autonomous Database with access control list (ACL) on dedicated Exadata infrastructure, complete this procedure. 1. Add the following parameters to the specification. An example file is availble here: [`config/samples/adb/autonomousdatabase_update_network_access.yaml`](./../../config/samples/adb/autonomousdatabase_update_network_access.yaml): | Attribute | Type | Description | Required? | |----|----|----|----| - | `networkAccess.isAccessControlEnabled` | boolean | Indicates if the database-level access control is enabled.

If disabled, then database access is defined by the network security rules.

If enabled, then database access is restricted to the IP addresses defined by the rules specified with the `accessControlList` property. While specifying `accessControlList` rules is optional, if database-level access control is enabled, and no rules are specified, then the database will become inaccessible. The rules can be added later by using the `UpdateAutonomousDatabase` API operation, or by using the edit option in console.

When creating a database clone, you should specify the access control setting that you want the clone database to use. By default, database-level access control will be disabled for the clone.
This property is applicable only to Autonomous Databases on the Exadata Cloud@Customer platform. | Yes | - | `networkAccess.accessControlList` | []string | The client IP access control list (ACL). This feature is available for autonomous databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | Yes | + | `isAccessControlEnabled` | boolean | Indicates if the database-level access control is enabled.

If disabled, then database access is defined by the network security rules.

If enabled, then database access is restricted to the IP addresses defined by the rules specified with the `accessControlList` property. While specifying `accessControlList` rules is optional, if database-level access control is enabled, and no rules are specified, then the database will become inaccessible. The rules can be added later by using the `UpdateAutonomousDatabase` API operation, or by using the edit option in console.

When creating a database clone, you should specify the access control setting that you want the clone database to use. By default, database-level access control will be disabled for the clone.
This property is applicable only to Autonomous Databases on the Exadata Cloud@Customer platform. | Yes | + | `accessControlList` | []string | The client IP access control list (ACL). This feature is available for autonomous databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | Yes | ```yaml --- @@ -231,13 +248,13 @@ To configure the network with RESTRICTED access type using an access control lis metadata: name: autonomousdatabase-sample spec: + action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... - networkAccess: - isAccessControlEnabled: true - accessControlList: - - 1.1.1.1 - - 1.1.0.0/16 + isAccessControlEnabled: true + accessControlList: + - 1.1.1.1 + - 1.1.0.0/16 ociConfig: configMapName: oci-cred secretName: oci-privatekey @@ -246,5 +263,5 @@ To configure the network with RESTRICTED access type using an access control lis 2. Apply the yaml: ```sh - kubectl apply -f config/samples/adb/autonomousdatabase_update_network_access.yaml + $ kubectl apply -f config/samples/adb/autonomousdatabase_update_network_access.yaml autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured From 655c9ad3d40431288e84b4a9a8bce35ed5a7c957 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Mon, 27 Jan 2025 08:11:34 +0000 Subject: [PATCH 160/414] Proper cleanup after build via .gitlab-ci.yml --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cfbc829a..7a5c3431 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,7 +16,8 @@ build-operator: docker rmi "$IMAGE"; sed -i "s/\(replicas.\) 3/\1 1/g" ./$OP_YAML; fi - - docker system prune -f + - buildah containers -q | xargs -n1 buildah rm + - podman system prune -f - curl -s --netrc-file $HOME/.netrc_gitlab $ARTIFACTORY_REPO/$CI_COMMIT_BRANCH/$OP_YAML -T ./$OP_YAML only: variables: From 96c9d09b3b14cc7f2bef06c26b4049b1ab3f988f Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Mon, 27 Jan 2025 10:36:26 +0000 Subject: [PATCH 161/414] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b9755e6f..72c21aa9 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ BUILDER_IMG = golang:$(GOLANG_VERSION) BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO="false" --build-arg GOLANG_VERSION=$(GOLANG_VERSION) endif ifeq ($(BUILD_MANIFEST), true) -BUILD_ARGS := $(BUILD_ARGS) --platform=linux/arm64,linux/amd64 --jobs=2 --manifest +BUILD_ARGS := $(BUILD_ARGS) --platform=linux/arm64,linux/amd64 --manifest PUSH_ARGS := manifest else BUILD_ARGS := $(BUILD_ARGS) --platform=linux/amd64 --tag From 05c2888b52b5cc4487a631583570e0e3e0f96014 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Mon, 27 Jan 2025 11:44:00 +0000 Subject: [PATCH 162/414] decommissionig base64 secrets (ords mtnt ctr) --- apis/database/v4/cdb_types.go | 10 + apis/database/v4/cdb_webhook.go | 5 + apis/database/v4/pdb_types.go | 12 +- apis/database/v4/pdb_webhook.go | 4 + apis/database/v4/zz_generated.deepcopy.go | 377 +- .../crd/bases/database.oracle.com_cdbs.yaml | 30 + .../crd/bases/database.oracle.com_pdbs.yaml | 30 + controllers/database/cdb_controller.go | 73 +- controllers/database/pdb_controller.go | 67 +- .../lrest-based/usecase/create_lrest_pod.yaml | 2 +- .../usecase/oracle-database-operator.yaml | 1 - docs/multitenant/ords-based/README.md | 239 +- .../multinamespace/cdb_create.yaml | 28 +- .../multinamespace/cdb_secret.yaml | 17 - .../multinamespace/pdb_clone.yaml | 20 +- .../multinamespace/pdb_close.yaml | 20 +- .../multinamespace/pdb_create.yaml | 20 +- .../multinamespace/pdb_delete.yaml | 13 +- .../provisioning/multinamespace/pdb_open.yaml | 20 +- .../provisioning/multinamespace/pdb_plug.yaml | 13 +- .../multinamespace/pdb_secret.yaml | 16 - .../multinamespace/pdb_unplug.yaml | 12 +- .../singlenamespace/cdb_create.yaml | 37 +- .../singlenamespace/cdb_secret.yaml | 17 - .../singlenamespace/pdb_clone.yaml | 20 +- .../singlenamespace/pdb_close.yaml | 20 +- .../singlenamespace/pdb_create.yaml | 20 +- .../singlenamespace/pdb_delete.yaml | 13 +- .../singlenamespace/pdb_open.yaml | 20 +- .../singlenamespace/pdb_plug.yaml | 13 +- .../singlenamespace/pdb_secret.yaml | 16 - .../singlenamespace/pdb_unplug.yaml | 12 +- .../ords-based/usecase/authsection01.yaml | 32 - .../ords-based/usecase/authsection02.yaml | 28 - docs/multitenant/ords-based/usecase/ca.crt | 22 - docs/multitenant/ords-based/usecase/ca.key | 28 - docs/multitenant/ords-based/usecase/ca.srl | 1 - .../usecase/clone_pdb1_resource.yaml | 20 +- .../usecase/clone_pdb2_resource.yaml | 20 +- .../usecase/close_pdb1_resource.yaml | 20 +- .../usecase/close_pdb2_resource.yaml | 20 +- .../usecase/close_pdb3_resource.yaml | 20 +- .../usecase/create_cdb_secrets.yaml | 13 - .../ords-based/usecase/create_ords_pod.yaml | 32 +- .../usecase/create_pdb1_resource.yaml | 20 +- .../usecase/create_pdb2_resource.yaml | 20 +- .../usecase/create_pdb_secrets.yaml | 11 - .../usecase/delete_pdb1_resource.yaml | 20 +- .../usecase/delete_pdb2_resource.yaml | 20 +- .../ords-based/usecase/extfile.txt | 1 - docs/multitenant/ords-based/usecase/makefile | 195 +- .../ords-based/usecase/map_pdb1_resource.yaml | 20 +- .../ords-based/usecase/map_pdb2_resource.yaml | 20 +- .../ords-based/usecase/map_pdb3_resource.yaml | 20 +- .../usecase/open_pdb1_resource.yaml | 20 +- .../usecase/open_pdb2_resource.yaml | 20 +- .../usecase/open_pdb3_resource.yaml | 20 +- .../usecase/oracle-database-operator.yaml | 1 - .../ords-based/usecase/parameters.txt | 20 +- .../usecase/plug_pdb1_resource.yaml | 20 +- .../multitenant/ords-based/usecase/public.pem | 9 - .../multitenant/ords-based/usecase/server.csr | 17 - docs/multitenant/ords-based/usecase/tls.crt | 21 - docs/multitenant/ords-based/usecase/tls.key | 28 - .../usecase/unplug_pdb1_resource.yaml | 20 +- .../ords-based/usecase01/README.md | 197 +- docs/multitenant/ords-based/usecase01/ca.crt | 25 - docs/multitenant/ords-based/usecase01/ca.key | 27 - docs/multitenant/ords-based/usecase01/ca.srl | 1 - .../ords-based/usecase01/cdb_create.yaml | 44 - .../ords-based/usecase01/cdb_secret.yaml | 17 - .../usecase01/clone_pdb1_resource.yaml | 50 + .../usecase01/clone_pdb2_resource.yaml | 50 + ...db_close.yaml => close_pdb1_resource.yaml} | 27 +- .../usecase01/close_pdb2_resource.yaml | 47 + .../usecase01/close_pdb3_resource.yaml | 47 + .../ords-based/usecase01/create_ords_pod.yaml | 48 + ..._create.yaml => create_pdb1_resource.yaml} | 34 +- .../usecase01/create_pdb2_resource.yaml | 51 + ..._delete.yaml => delete_pdb1_resource.yaml} | 21 +- .../usecase01/delete_pdb2_resource.yaml | 45 + .../ords-based/usecase01/extfile.txt | 1 - .../multitenant/ords-based/usecase01/makefile | 1101 ++- .../{pdb_map.yaml => map_pdb1_resource.yaml} | 30 +- .../usecase01/map_pdb2_resource.yaml | 49 + .../usecase01/map_pdb3_resource.yaml | 49 + ...{pdb_open.yaml => open_pdb1_resource.yaml} | 26 +- .../usecase01/open_pdb2_resource.yaml | 47 + .../usecase01/open_pdb3_resource.yaml | 47 + ...acle-database-operator-system_binding.yaml | 13 + .../usecase01/oracle-database-operator.yaml | 1 - .../ords-based/usecase01/parameters.txt | 61 + .../ords-based/usecase01/pdb_secret.yaml | 16 - .../usecase01/plug_pdb1_resource.yaml | 53 + .../ords-based/usecase01/server.csr | 18 - .../ords-based/usecase01/tde_secret.yaml | 17 - docs/multitenant/ords-based/usecase01/tls.crt | 24 - docs/multitenant/ords-based/usecase01/tls.key | 28 - .../usecase01/unplug_pdb1_resource.yaml | 46 + .../ords-based/usecase02/README.md | 144 +- .../ords-based/usecase02/pdb_clone.yaml | 34 +- .../ords-based/usecase02/pdb_plug.yaml | 33 +- .../ords-based/usecase02/pdb_unplug.yaml | 29 +- .../ords-based/usecase02/tde_secret.yaml | 15 - .../ords-based/usecase03/Dockerfile | 80 - .../usecase03/NamespaceSegregation.png | Bin 270813 -> 0 bytes .../ords-based/usecase03/README.md | 268 - docs/multitenant/ords-based/usecase03/ca.crt | 24 - docs/multitenant/ords-based/usecase03/ca.key | 27 - docs/multitenant/ords-based/usecase03/ca.srl | 1 - .../ords-based/usecase03/cdb_create.yaml | 44 - .../ords-based/usecase03/cdb_creation_log.txt | 336 - .../ords-based/usecase03/cdb_secret.yaml | 17 - .../ords-based/usecase03/extfile.txt | 1 - .../ords-based/usecase03/gentlscert.sh | 23 - .../multitenant/ords-based/usecase03/makefile | 291 - .../usecase03/ns_namespace_cdb.yaml | 7 - .../usecase03/ns_namespace_pdb.yaml | 7 - .../usecase03/operator_creation_log.txt | 27 - .../usecase03/oracle-database-operator.yaml | 1 - .../ords-based/usecase03/pdb_create.yaml | 46 - .../ords-based/usecase03/pdb_creation_log.txt | 6 - .../ords-based/usecase03/pdb_secret.yaml | 16 - .../ords-based/usecase03/runOrdsSSL.sh | 190 - .../ords-based/usecase03/server.csr | 17 - docs/multitenant/ords-based/usecase03/tls.crt | 23 - docs/multitenant/ords-based/usecase03/tls.key | 28 - .../usecase01/oracle-database-operator.yaml | 4052 -------- oracle-database-operator.yaml | 8211 ++++++++++++++++- ords/Dockerfile | 15 +- ords/runOrdsSSL.sh | 19 +- 131 files changed, 10768 insertions(+), 7638 deletions(-) delete mode 120000 docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/cdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase/authsection01.yaml delete mode 100644 docs/multitenant/ords-based/usecase/authsection02.yaml delete mode 100644 docs/multitenant/ords-based/usecase/ca.crt delete mode 100644 docs/multitenant/ords-based/usecase/ca.key delete mode 100644 docs/multitenant/ords-based/usecase/ca.srl delete mode 100644 docs/multitenant/ords-based/usecase/create_cdb_secrets.yaml delete mode 100644 docs/multitenant/ords-based/usecase/create_pdb_secrets.yaml delete mode 100644 docs/multitenant/ords-based/usecase/extfile.txt delete mode 120000 docs/multitenant/ords-based/usecase/oracle-database-operator.yaml delete mode 100644 docs/multitenant/ords-based/usecase/public.pem delete mode 100644 docs/multitenant/ords-based/usecase/server.csr delete mode 100644 docs/multitenant/ords-based/usecase/tls.crt delete mode 100644 docs/multitenant/ords-based/usecase/tls.key delete mode 100644 docs/multitenant/ords-based/usecase01/ca.crt delete mode 100644 docs/multitenant/ords-based/usecase01/ca.key delete mode 100644 docs/multitenant/ords-based/usecase01/ca.srl delete mode 100644 docs/multitenant/ords-based/usecase01/cdb_create.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/cdb_secret.yaml create mode 100644 docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml rename docs/multitenant/ords-based/usecase01/{pdb_close.yaml => close_pdb1_resource.yaml} (68%) create mode 100644 docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase01/create_ords_pod.yaml rename docs/multitenant/ords-based/usecase01/{pdb_create.yaml => create_pdb1_resource.yaml} (65%) create mode 100644 docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml rename docs/multitenant/ords-based/usecase01/{pdb_delete.yaml => delete_pdb1_resource.yaml} (62%) create mode 100644 docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/extfile.txt rename docs/multitenant/ords-based/usecase01/{pdb_map.yaml => map_pdb1_resource.yaml} (70%) create mode 100644 docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml rename docs/multitenant/ords-based/usecase01/{pdb_open.yaml => open_pdb1_resource.yaml} (68%) create mode 100644 docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml create mode 100644 docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml delete mode 120000 docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml create mode 100644 docs/multitenant/ords-based/usecase01/parameters.txt delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_secret.yaml create mode 100644 docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/server.csr delete mode 100644 docs/multitenant/ords-based/usecase01/tde_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/tls.crt delete mode 100644 docs/multitenant/ords-based/usecase01/tls.key create mode 100644 docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase02/tde_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase03/Dockerfile delete mode 100644 docs/multitenant/ords-based/usecase03/NamespaceSegregation.png delete mode 100644 docs/multitenant/ords-based/usecase03/README.md delete mode 100644 docs/multitenant/ords-based/usecase03/ca.crt delete mode 100644 docs/multitenant/ords-based/usecase03/ca.key delete mode 100644 docs/multitenant/ords-based/usecase03/ca.srl delete mode 100644 docs/multitenant/ords-based/usecase03/cdb_create.yaml delete mode 100644 docs/multitenant/ords-based/usecase03/cdb_creation_log.txt delete mode 100644 docs/multitenant/ords-based/usecase03/cdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase03/extfile.txt delete mode 100644 docs/multitenant/ords-based/usecase03/gentlscert.sh delete mode 100644 docs/multitenant/ords-based/usecase03/makefile delete mode 100644 docs/multitenant/ords-based/usecase03/ns_namespace_cdb.yaml delete mode 100644 docs/multitenant/ords-based/usecase03/ns_namespace_pdb.yaml delete mode 100644 docs/multitenant/ords-based/usecase03/operator_creation_log.txt delete mode 120000 docs/multitenant/ords-based/usecase03/oracle-database-operator.yaml delete mode 100644 docs/multitenant/ords-based/usecase03/pdb_create.yaml delete mode 100644 docs/multitenant/ords-based/usecase03/pdb_creation_log.txt delete mode 100644 docs/multitenant/ords-based/usecase03/pdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase03/runOrdsSSL.sh delete mode 100644 docs/multitenant/ords-based/usecase03/server.csr delete mode 100644 docs/multitenant/ords-based/usecase03/tls.crt delete mode 100644 docs/multitenant/ords-based/usecase03/tls.key delete mode 100644 docs/multitenant/usecase01/oracle-database-operator.yaml diff --git a/apis/database/v4/cdb_types.go b/apis/database/v4/cdb_types.go index 7bb4cbd3..ce3f6f28 100644 --- a/apis/database/v4/cdb_types.go +++ b/apis/database/v4/cdb_types.go @@ -87,6 +87,8 @@ type CDBSpec struct { NodeSelector map[string]string `json:"nodeSelector,omitempty"` DeletePDBCascade bool `json:"deletePdbCascade,omitempty"` DBTnsurl string `json:"dbTnsurl,omitempty"` + CDBPubKey CDBPUBKEY `json:"cdbOrdsPubKey,omitempty"` + CDBPriKey CDBPRIVKEY `json:"cdbOrdsPrvKey,omitempty"` } // CDBSecret defines the secretName @@ -133,6 +135,14 @@ type CDBTLSCRT struct { Secret CDBSecret `json:"secret"` } +type CDBPUBKEY struct { + Secret CDBSecret `json:"secret"` +} + +type CDBPRIVKEY struct { + Secret CDBSecret `json:"secret"` +} + // CDBStatus defines the observed state of CDB type CDBStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster diff --git a/apis/database/v4/cdb_webhook.go b/apis/database/v4/cdb_webhook.go index 98dcf823..235b2627 100644 --- a/apis/database/v4/cdb_webhook.go +++ b/apis/database/v4/cdb_webhook.go @@ -104,6 +104,11 @@ func (r *CDB) ValidateCreate() (admission.Warnings, error) { field.Required(field.NewPath("spec").Child("cdbTlsCrt"), "Please specify CDB Tls Certificate(secret)")) } + if reflect.ValueOf(r.Spec.CDBPriKey).IsZero() { + allErrs = append(allErrs, + field.Required(field.NewPath("spec").Child("CDBPriKey"), "Please specify CDB CDBPriKey(secret)")) + } + /*if r.Spec.SCANName == "" { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("scanName"), "Please specify SCAN Name for CDB")) diff --git a/apis/database/v4/pdb_types.go b/apis/database/v4/pdb_types.go index e621fb53..16021f12 100644 --- a/apis/database/v4/pdb_types.go +++ b/apis/database/v4/pdb_types.go @@ -117,7 +117,9 @@ type PDBSpec struct { // turn on the assertive approach to delete pdb resource // kubectl delete pdb ..... automatically triggers the pluggable database // deletion - AssertivePdbDeletion bool `json:"assertivePdbDeletion,omitempty"` + AssertivePdbDeletion bool `json:"assertivePdbDeletion,omitempty"` + PDBPubKey PDBPUBKEY `json:"pdbOrdsPubKey,omitempty"` + PDBPriKey PDBPRIVKEY `json:"pdbOrdsPrvKey,omitempty"` } // PDBAdminName defines the secret containing Sys Admin User mapped to key 'adminName' for PDB @@ -222,6 +224,14 @@ type PDBList struct { Items []PDB `json:"items"` } +type PDBPUBKEY struct { + Secret PDBSecret `json:"secret"` +} + +type PDBPRIVKEY struct { + Secret PDBSecret `json:"secret"` +} + func init() { SchemeBuilder.Register(&PDB{}, &PDBList{}) } diff --git a/apis/database/v4/pdb_webhook.go b/apis/database/v4/pdb_webhook.go index 896e6dc7..f651accf 100644 --- a/apis/database/v4/pdb_webhook.go +++ b/apis/database/v4/pdb_webhook.go @@ -163,6 +163,10 @@ func (r *PDB) validateAction(allErrs *field.ErrorList) { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("pdbTlsCat"), "Please specify PDB Tls Certificate Authority(secret)")) } + if reflect.ValueOf(r.Spec.PDBPriKey).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("pdbOrdsPrvKey"), "Please specify PDB Tls Certificate Authority(secret)")) + } switch action { case "DELETE": diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 801f1b9e..94a2a9c3 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -807,6 +807,38 @@ func (in *CDBList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBPRIVKEY) DeepCopyInto(out *CDBPRIVKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBPRIVKEY. +func (in *CDBPRIVKEY) DeepCopy() *CDBPRIVKEY { + if in == nil { + return nil + } + out := new(CDBPRIVKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBPUBKEY) DeepCopyInto(out *CDBPUBKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBPUBKEY. +func (in *CDBPUBKEY) DeepCopy() *CDBPUBKEY { + if in == nil { + return nil + } + out := new(CDBPUBKEY) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CDBSecret) DeepCopyInto(out *CDBSecret) { *out = *in @@ -840,6 +872,8 @@ func (in *CDBSpec) DeepCopyInto(out *CDBSpec) { (*out)[key] = val } } + out.CDBPubKey = in.CDBPubKey + out.CDBPriKey = in.CDBPriKey } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSpec. @@ -1036,8 +1070,8 @@ func (in *DataguardBroker) DeepCopyInto(out *DataguardBroker) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBroker. @@ -1093,6 +1127,25 @@ func (in *DataguardBrokerList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DataguardBrokerSpec) DeepCopyInto(out *DataguardBrokerSpec) { *out = *in + if in.StandbyDatabaseRefs != nil { + in, out := &in.StandbyDatabaseRefs, &out.StandbyDatabaseRefs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ServiceAnnotations != nil { + in, out := &in.ServiceAnnotations, &out.ServiceAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBrokerSpec. @@ -1108,6 +1161,13 @@ func (in *DataguardBrokerSpec) DeepCopy() *DataguardBrokerSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DataguardBrokerStatus) DeepCopyInto(out *DataguardBrokerStatus) { *out = *in + if in.DatabasesInDataguardConfig != nil { + in, out := &in.DatabasesInDataguardConfig, &out.DatabasesInDataguardConfig + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataguardBrokerStatus. @@ -2490,7 +2550,7 @@ func (in *OracleRestDataService) DeepCopyInto(out *OracleRestDataService) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status } @@ -2512,6 +2572,21 @@ func (in *OracleRestDataService) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestDataServiceImage) DeepCopyInto(out *OracleRestDataServiceImage) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServiceImage. +func (in *OracleRestDataServiceImage) DeepCopy() *OracleRestDataServiceImage { + if in == nil { + return nil + } + out := new(OracleRestDataServiceImage) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OracleRestDataServiceList) DeepCopyInto(out *OracleRestDataServiceList) { *out = *in @@ -2544,9 +2619,87 @@ func (in *OracleRestDataServiceList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestDataServicePassword) DeepCopyInto(out *OracleRestDataServicePassword) { + *out = *in + if in.KeepSecret != nil { + in, out := &in.KeepSecret, &out.KeepSecret + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServicePassword. +func (in *OracleRestDataServicePassword) DeepCopy() *OracleRestDataServicePassword { + if in == nil { + return nil + } + out := new(OracleRestDataServicePassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestDataServicePersistence) DeepCopyInto(out *OracleRestDataServicePersistence) { + *out = *in + if in.SetWritePermissions != nil { + in, out := &in.SetWritePermissions, &out.SetWritePermissions + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServicePersistence. +func (in *OracleRestDataServicePersistence) DeepCopy() *OracleRestDataServicePersistence { + if in == nil { + return nil + } + out := new(OracleRestDataServicePersistence) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestDataServiceRestEnableSchemas) DeepCopyInto(out *OracleRestDataServiceRestEnableSchemas) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServiceRestEnableSchemas. +func (in *OracleRestDataServiceRestEnableSchemas) DeepCopy() *OracleRestDataServiceRestEnableSchemas { + if in == nil { + return nil + } + out := new(OracleRestDataServiceRestEnableSchemas) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OracleRestDataServiceSpec) DeepCopyInto(out *OracleRestDataServiceSpec) { *out = *in + if in.ServiceAnnotations != nil { + in, out := &in.ServiceAnnotations, &out.ServiceAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.Image = in.Image + in.OrdsPassword.DeepCopyInto(&out.OrdsPassword) + in.AdminPassword.DeepCopyInto(&out.AdminPassword) + if in.RestEnableSchemas != nil { + in, out := &in.RestEnableSchemas, &out.RestEnableSchemas + *out = make([]OracleRestDataServiceRestEnableSchemas, len(*in)) + copy(*out, *in) + } + in.Persistence.DeepCopyInto(&out.Persistence) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServiceSpec. @@ -2562,6 +2715,7 @@ func (in *OracleRestDataServiceSpec) DeepCopy() *OracleRestDataServiceSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OracleRestDataServiceStatus) DeepCopyInto(out *OracleRestDataServiceStatus) { *out = *in + out.Image = in.Image } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServiceStatus. @@ -2894,6 +3048,38 @@ func (in *PDBList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBPRIVKEY) DeepCopyInto(out *PDBPRIVKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBPRIVKEY. +func (in *PDBPRIVKEY) DeepCopy() *PDBPRIVKEY { + if in == nil { + return nil + } + out := new(PDBPRIVKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBPUBKEY) DeepCopyInto(out *PDBPUBKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBPUBKEY. +func (in *PDBPUBKEY) DeepCopy() *PDBPUBKEY { + if in == nil { + return nil + } + out := new(PDBPUBKEY) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBSecret) DeepCopyInto(out *PDBSecret) { *out = *in @@ -2951,6 +3137,8 @@ func (in *PDBSpec) DeepCopyInto(out *PDBSpec) { *out = new(bool) **out = **in } + out.PDBPubKey = in.PDBPubKey + out.PDBPriKey = in.PDBPriKey } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSpec. @@ -3479,8 +3667,8 @@ func (in *SingleInstanceDatabase) DeepCopyInto(out *SingleInstanceDatabase) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabase. @@ -3501,6 +3689,56 @@ func (in *SingleInstanceDatabase) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseAdminPassword) DeepCopyInto(out *SingleInstanceDatabaseAdminPassword) { + *out = *in + if in.KeepSecret != nil { + in, out := &in.KeepSecret, &out.KeepSecret + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseAdminPassword. +func (in *SingleInstanceDatabaseAdminPassword) DeepCopy() *SingleInstanceDatabaseAdminPassword { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseAdminPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseImage) DeepCopyInto(out *SingleInstanceDatabaseImage) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseImage. +func (in *SingleInstanceDatabaseImage) DeepCopy() *SingleInstanceDatabaseImage { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseImage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseInitParams) DeepCopyInto(out *SingleInstanceDatabaseInitParams) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseInitParams. +func (in *SingleInstanceDatabaseInitParams) DeepCopy() *SingleInstanceDatabaseInitParams { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseInitParams) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SingleInstanceDatabaseList) DeepCopyInto(out *SingleInstanceDatabaseList) { *out = *in @@ -3533,9 +3771,112 @@ func (in *SingleInstanceDatabaseList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabasePersistence) DeepCopyInto(out *SingleInstanceDatabasePersistence) { + *out = *in + if in.SetWritePermissions != nil { + in, out := &in.SetWritePermissions, &out.SetWritePermissions + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabasePersistence. +func (in *SingleInstanceDatabasePersistence) DeepCopy() *SingleInstanceDatabasePersistence { + if in == nil { + return nil + } + out := new(SingleInstanceDatabasePersistence) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseResource) DeepCopyInto(out *SingleInstanceDatabaseResource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseResource. +func (in *SingleInstanceDatabaseResource) DeepCopy() *SingleInstanceDatabaseResource { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleInstanceDatabaseResources) DeepCopyInto(out *SingleInstanceDatabaseResources) { + *out = *in + if in.Requests != nil { + in, out := &in.Requests, &out.Requests + *out = new(SingleInstanceDatabaseResource) + **out = **in + } + if in.Limits != nil { + in, out := &in.Limits, &out.Limits + *out = new(SingleInstanceDatabaseResource) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseResources. +func (in *SingleInstanceDatabaseResources) DeepCopy() *SingleInstanceDatabaseResources { + if in == nil { + return nil + } + out := new(SingleInstanceDatabaseResources) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SingleInstanceDatabaseSpec) DeepCopyInto(out *SingleInstanceDatabaseSpec) { *out = *in + if in.ServiceAnnotations != nil { + in, out := &in.ServiceAnnotations, &out.ServiceAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.FlashBack != nil { + in, out := &in.FlashBack, &out.FlashBack + *out = new(bool) + **out = **in + } + if in.ArchiveLog != nil { + in, out := &in.ArchiveLog, &out.ArchiveLog + *out = new(bool) + **out = **in + } + if in.ForceLogging != nil { + in, out := &in.ForceLogging, &out.ForceLogging + *out = new(bool) + **out = **in + } + if in.TrueCacheServices != nil { + in, out := &in.TrueCacheServices, &out.TrueCacheServices + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.AdminPassword.DeepCopyInto(&out.AdminPassword) + out.Image = in.Image + in.Persistence.DeepCopyInto(&out.Persistence) + if in.InitParams != nil { + in, out := &in.InitParams, &out.InitParams + *out = new(SingleInstanceDatabaseInitParams) + **out = **in + } + in.Resources.DeepCopyInto(&out.Resources) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseSpec. @@ -3551,6 +3892,32 @@ func (in *SingleInstanceDatabaseSpec) DeepCopy() *SingleInstanceDatabaseSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SingleInstanceDatabaseStatus) DeepCopyInto(out *SingleInstanceDatabaseStatus) { *out = *in + if in.Nodes != nil { + in, out := &in.Nodes, &out.Nodes + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.DgBroker != nil { + in, out := &in.DgBroker, &out.DgBroker + *out = new(string) + **out = **in + } + if in.StandbyDatabases != nil { + in, out := &in.StandbyDatabases, &out.StandbyDatabases + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.InitParams = in.InitParams + in.Persistence.DeepCopyInto(&out.Persistence) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleInstanceDatabaseStatus. diff --git a/config/crd/bases/database.oracle.com_cdbs.yaml b/config/crd/bases/database.oracle.com_cdbs.yaml index 4308600b..8ea594e6 100644 --- a/config/crd/bases/database.oracle.com_cdbs.yaml +++ b/config/crd/bases/database.oracle.com_cdbs.yaml @@ -87,6 +87,36 @@ spec: type: object cdbName: type: string + cdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object cdbTlsCrt: properties: secret: diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index c51ab4e7..938c5512 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -131,6 +131,36 @@ spec: type: string pdbName: type: string + pdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object pdbState: enum: - OPEN diff --git a/controllers/database/cdb_controller.go b/controllers/database/cdb_controller.go index e5f056c2..e1e64a57 100644 --- a/controllers/database/cdb_controller.go +++ b/controllers/database/cdb_controller.go @@ -508,6 +508,17 @@ func (r *CDBReconciler) createPodSpec(cdb *dbapi.CDB) corev1.PodSpec { Name: "WEBSERVER_PASSWORD_KEY", Value: cdb.Spec.WebServerPwd.Secret.Key, }, + { + Name: "R1", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cdb.Spec.CDBPriKey.Secret.SecretName, + }, + Key: cdb.Spec.CDBPriKey.Secret.Key, + }, + }, + }, } }(), }}, @@ -751,55 +762,9 @@ func (r *CDBReconciler) createSvcSpec(cdb *dbapi.CDB) *corev1.Service { /* ************************************************ - Check CDB deletion - /*********************************************** -*/ - -/* -func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - log := r.Log.WithValues("manageCDBDeletion", req.NamespacedName) - - isCDBMarkedToBeDeleted := cdb.GetDeletionTimestamp() != nil - if isCDBMarkedToBeDeleted { - log.Info("Marked to be deleted") - cdb.Status.Phase = cdbPhaseDelete - cdb.Status.Status = true - r.Status().Update(ctx, cdb) - if controllerutil.ContainsFinalizer(cdb, CDBFinalizer) { - err := r.deleteCDBInstance(ctx, req, cdb) - if err != nil { - log.Info("Could not delete CDB Resource", "CDB Name", cdb.Spec.CDBName, "err", err.Error()) - return err - } - - log.Info("Removing finalizer") - controllerutil.RemoveFinalizer(cdb, CDBFinalizer) - err = r.Update(ctx, cdb) - if err != nil { - log.Info("Could not remove finalizer", "err", err.Error()) - return err - } - log.Info("Successfully removed CDB Resource") - return nil - } - } - - if !controllerutil.ContainsFinalizer(cdb, CDBFinalizer) { - log.Info("Adding finalizer") - - cdb.Status.Phase = cdbPhaseInit - cdb.Status.Status = false - controllerutil.AddFinalizer(cdb, CDBFinalizer) - err := r.Update(ctx, cdb) - if err != nil { - log.Info("Could not add finalizer", "err", err.Error()) - return err - } - } - return nil -} +/*********************************************** */ - func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { log := r.Log.WithValues("manageCDBDeletion", req.NamespacedName) @@ -890,7 +855,8 @@ func (r *CDBReconciler) deleteCDBInstance(ctx context.Context, req ctrl.Request, /* ************************************************ - Get Secret Key for a Secret Name - /*********************************************** + +/*********************************************** */ func (r *CDBReconciler) verifySecrets(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { @@ -914,6 +880,9 @@ func (r *CDBReconciler) verifySecrets(ctx context.Context, req ctrl.Request, cdb if err := r.checkSecret(ctx, req, cdb, cdb.Spec.WebServerPwd.Secret.SecretName); err != nil { return err } + if err := r.checkSecret(ctx, req, cdb, cdb.Spec.CDBPriKey.Secret.SecretName); err != nil { + return err + } cdb.Status.Msg = "" log.Info("Verified secrets successfully") @@ -923,7 +892,8 @@ func (r *CDBReconciler) verifySecrets(ctx context.Context, req ctrl.Request, cdb /* ************************************************ - Get Secret Key for a Secret Name - /*********************************************** + +/*********************************************** */ func (r *CDBReconciler) checkSecret(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB, secretName string) error { @@ -947,7 +917,8 @@ func (r *CDBReconciler) checkSecret(ctx context.Context, req ctrl.Request, cdb * /* ************************************************ - Delete Secrets - /*********************************************** + +/*********************************************** */ func (r *CDBReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) { @@ -1009,7 +980,7 @@ func (r *CDBReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, cdb /* ************************************************************* - SetupWithManager sets up the controller with the Manager. - /************************************************************ +/************************************************************ */ func (r *CDBReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { diff --git a/controllers/database/pdb_controller.go b/controllers/database/pdb_controller.go index 1fd11c10..a2ca0f85 100644 --- a/controllers/database/pdb_controller.go +++ b/controllers/database/pdb_controller.go @@ -56,6 +56,7 @@ import ( "time" dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" + lrcommons "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" @@ -637,15 +638,52 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db return err } - pdbAdminName, err := r.getSecret(ctx, req, pdb, pdb.Spec.AdminName.Secret.SecretName, pdb.Spec.AdminName.Secret.Key) + /*** BEGIN GET ENCPASS ***/ + secret := &corev1.Secret{} + + err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.AdminName.Secret.SecretName, Namespace: pdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + pdb.Spec.AdminName.Secret.SecretName) + return err + } + log.Error(err, "Unable to get the secret.") + return err + } + pdbAdminNameEnc := string(secret.Data[pdb.Spec.AdminName.Secret.Key]) + pdbAdminNameEnc = strings.TrimSpace(pdbAdminNameEnc) + + err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBPriKey.Secret.SecretName, Namespace: pdb.Namespace}, secret) if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + pdb.Spec.PDBPriKey.Secret.SecretName) + return err + } + log.Error(err, "Unable to get the secret.") return err } - pdbAdminPwd, err := r.getSecret(ctx, req, pdb, pdb.Spec.AdminPwd.Secret.SecretName, pdb.Spec.AdminPwd.Secret.Key) + privKey := string(secret.Data[pdb.Spec.PDBPriKey.Secret.Key]) + pdbAdminName, err := lrcommons.CommonDecryptWithPrivKey(privKey, pdbAdminNameEnc, req) + + // Get Web Server User Password + secret = &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.AdminPwd.Secret.SecretName, Namespace: pdb.Namespace}, secret) if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + pdb.Spec.AdminPwd.Secret.SecretName) + return err + } + log.Error(err, "Unable to get the secret.") return err } + pdbAdminPwdEnc := string(secret.Data[pdb.Spec.AdminPwd.Secret.Key]) + pdbAdminPwdEnc = strings.TrimSpace(pdbAdminPwdEnc) + pdbAdminPwd, err := lrcommons.CommonDecryptWithPrivKey(privKey, pdbAdminPwdEnc, req) + pdbAdminName = strings.TrimSuffix(pdbAdminName, "\n") + pdbAdminPwd = strings.TrimSuffix(pdbAdminPwd, "\n") + /*** END GET ENCPASS ***/ + log.Info("====================> " + pdbAdminName + ":" + pdbAdminPwd) /* Prevent creating an existing pdb */ err = r.getPDBState(ctx, req, pdb) if err != nil { @@ -658,9 +696,6 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db return nil } - pdbAdminName = strings.TrimSuffix(pdbAdminName, "\n") - pdbAdminPwd = strings.TrimSuffix(pdbAdminPwd, "\n") - values := map[string]string{ "method": "CREATE", "pdb_name": pdb.Spec.PDBName, @@ -1475,10 +1510,22 @@ func NewCallApi(intr interface{}, ctx context.Context, req ctrl.Request, pdb *db log.Error(err, "Unable to get the secret.") return "", err } + webUserEnc := string(secret.Data[pdb.Spec.WebServerUsr.Secret.Key]) + webUserEnc = strings.TrimSpace(webUserEnc) - webUser := string(secret.Data[pdb.Spec.WebServerUsr.Secret.Key]) - webUser = strings.TrimSpace(webUser) + err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBPriKey.Secret.SecretName, Namespace: pdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + pdb.Spec.PDBPriKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + privKey := string(secret.Data[pdb.Spec.PDBPriKey.Secret.Key]) + webUser, err := lrcommons.CommonDecryptWithPrivKey(privKey, webUserEnc, req) + // Get Web Server User Password secret = &corev1.Secret{} err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.WebServerPwd.Secret.SecretName, Namespace: pdb.Namespace}, secret) if err != nil { @@ -1489,8 +1536,10 @@ func NewCallApi(intr interface{}, ctx context.Context, req ctrl.Request, pdb *db log.Error(err, "Unable to get the secret.") return "", err } - webUserPwd := string(secret.Data[pdb.Spec.WebServerPwd.Secret.Key]) - webUserPwd = strings.TrimSpace(webUserPwd) + webUserPwdEnc := string(secret.Data[pdb.Spec.WebServerPwd.Secret.Key]) + webUserPwdEnc = strings.TrimSpace(webUserPwdEnc) + webUserPwd, err := lrcommons.CommonDecryptWithPrivKey(privKey, webUserPwdEnc, req) + /////////////////////////////////////////////////////////////////////////////////// var httpreq *http.Request if action == "GET" { diff --git a/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml b/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml index 0545c501..b80c1c56 100644 --- a/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml +++ b/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml @@ -5,7 +5,7 @@ metadata: namespace: cdbnamespace spec: cdbName: "DB12" - lrestImage: container-registry.oracle.com/database/operator:lrest-241210-amd64 + lrestImage: container-registry.oracle.com/database/operator:lrest-241210-amd64 lrestImagePullPolicy: "Always" dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" replicas: 1 diff --git a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml b/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml deleted file mode 120000 index bbf4775f..00000000 --- a/docs/multitenant/lrest-based/usecase/oracle-database-operator.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/ords-based/README.md b/docs/multitenant/ords-based/README.md index ecef82ba..7a988bd5 100644 --- a/docs/multitenant/ords-based/README.md +++ b/docs/multitenant/ords-based/README.md @@ -11,12 +11,14 @@ By usigng CDB/PDB controllers you can perform the following actions **CREATE**,* Examples are located under the following directories: -- [Usecase](./usecase/) This directory contains an init files where you can specify all the details of your environment and a makefile which reads the initfile and generates the all the yaml files. No need to edit yaml files one by one with your system information. -- [Singlenamespace provisioning](./provisioning/singlenamespace/) You will find base sample files to manage pdb and cdb within a single namespace. File editing is required. +- [Usecase](./usecase/) and [usecase01] directories contain a [configuration file](./usecase/parameters.txt) where you can specify all the details of your environment. A [makefile](./usecase/makefile) takes this file in input to generate the all the yaml files. There is no need to edit yaml files one by one. +- [Singlenamespace provisioning](./provisioning/singlenamespace/) You will find base sample files to manage pdb and cdb within a single namespace - [Multinamespace provisioning](./provisioning/multinamespace/) You will find base sample files to manage pdb and cdb in different namespaces. -- [Usecase01](./usecase01/README.md) [Usecase02](./usecase02/README.md) and [Usecase03](./usecase03/README.md) contain other step by step examples. File editing is required. +- [Usecase01](./usecase01/README.md) [Usecase02](./usecase02/README.md) contain other step by step examples; -**NOTE** that the cdb controller is not intended to manage the container database. The cdb controller just provied a pod with a rest server connected to the container database. +Authomatic yaml generation is not available for directory usecase02 and provisioning directories + +**NOTE** that the cdb controller is not intended to manage the container database. The cdb controller is meant to provied a pod with a rest server connected to the container database. ## Macro steps for setup @@ -87,8 +89,8 @@ singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z * [Prepare the container database for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) * [Oracle REST Data Service or ORDS Image](#oracle-rest-data-service-ords-image) * [Kubernetes Secrets](#kubernetes-secrets) -* [Kubernetes CRD for CDB](#kubernetes-crd-for-cdb) -* [Kubernetes CRD for PDB](#kubernetes-crd-for-pdb) +* [Kubernetes CRD for CDB](#cdb-crd) +* [Kubernetes CRD for PDB](#pdb-crd) ## Prepare the container database for PDB Lifecycle Management (PDB-LM) @@ -132,36 +134,10 @@ In this setup example [provisioning example setup](./provisioning/example_setup_ ## Kubernetes Secrets - Multitenant Controllers use Kubernetes Secrets to store the required credential. The https certificates are stored in Kubernetes Secrets as well. + Multitenant Controllers use Kubernetes secrets to store the required credential and https certificates. **Note** In multi namespace enviroment you have to create specific secrets for each namespaces -### Secrets for CDB CRD - - Create a secret file as shown here: [config/samples/multitenant/cdb_secret.yaml](./provisioning/singlenamespace/cdb_secret.yaml). Modify this file with the `base64` encoded values of the required passwords for CDB, and use this file to create the required secrets. - - ```bash - kubectl apply -f cdb_secret.yaml - ``` - - **Note:** To obtain the `base64` encoded value for a password, use the following command: - - ```bash - echo -n "" | base64 - ``` - - **Note:** After successful creation of the CDB Resource, the CDB secrets can be deleted from the Kubernetes system . - -### Secrets for PDB CRD - - Create a secret file as shown here: [pdb_secret.yaml](./provisioning/singlenamespace/pdb_secret.yaml). Edit the file using your base64 credential and apply it. - - ```bash - kubectl apply -f pdb_secret.yaml - ``` - - **NOTE:** Don't leave plaintext files containing sensitive data on disk. After loading the Secret, remove the plaintext file or move it to secure storage. - ### Secrets for CERTIFICATES Create the certificates and key on your local host, and use them to create the Kubernetes secret. @@ -183,11 +159,111 @@ kubectl create secret generic db-ca --from-file=ca.crt -n oracle-database-operat **Note:** On successful creation of the certificates secret creation remove files or move to secure storage . -## Kubernetes CRD for CDB +### Secrets for CDB CRD + + **Note:** base64 encoded secrets are no longer supported ; use openssl secrets as documented in the following section. After successful creation of the CDB Resource, the CDB and PDB secrets can be deleted from the Kubernetes system. Don't leave plaintext files containing sensitive data on disk. After loading the secret, remove the plaintext file or move it to secure storage. + + ```bash + +export PRVKEY=ca.key +export PUBKEY=public.pem +WBUSERFILE=wbuser.txt +WBPASSFILE=wbpass.txt +CDBUSRFILE=cdbusr.txt +CDBPWDFILE=cdbpwd.txt +SYSPWDFILE=syspwd.txt +ORDPWDFILE=ordpwd.txt +PDBUSRFILE=pdbusr.txt +PDBPWDFILE=pdbpwd.txt + +# Webuser credential +echo [WBUSER] > ${WBUSERFILE} +echo [WBPASS] > ${WBPASSFILE} + +# CDB admin user credentioan +echo [CDBPWD] > ${CDBPWDFILE} +echo [CDBUSR] > ${CDBUSRFILE} + +# SYS Password +echo [SYSPWD] > ${SYSPWDFILE} + +# Ords Password +echo [ORDPWD] > ${ORDPWDFILE} + +## PDB admin credential +echo [PDBUSR] > ${PDBUSRFILE} +echo [PDBPWD] > ${PDBPWDFILE} + +#Secrets creation for pub and priv keys +openssl rsa -in ${PRVKEY} -outform PEM -pubout -out ${PUBKEY} +kubectl create secret generic pubkey --from-file=publicKey=${PUBKEY} -n ${CDBNAMESPACE} +kubectl create secret generic prvkey --from-file=privateKey=${PRVKEY} -n ${CDBNAMESPACE} +kubectl create secret generic prvkey --from-file=privateKey="${PRVKEY}" -n ${PDBNAMESPACE} + +#Password encryption +openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${WBUSERFILE} |base64 > e_${WBUSERFILE} +openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${WBPASSFILE} |base64 > e_${WBPASSFILE} +openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${CDBPWDFILE} |base64 > e_${CDBPWDFILE} +openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${CDBUSRFILE} |base64 > e_${CDBUSRFILE} +openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${SYSPWDFILE} |base64 > e_${SYSPWDFILE} +openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${ORDPWDFILE} |base64 > e_${ORDPWDFILE} +openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${PDBUSRFILE} |base64 > e_${PDBUSRFILE} +openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${PDBPWDFILE} |base64 > e_${PDBPWDFILE} + +#Ecrypted secrets creation +kubectl create secret generic wbuser --from-file=e_${WBUSERFILE} -n ${CDBNAMESPACE} +kubectl create secret generic wbpass --from-file=e_${WBPASSFILE} -n ${CDBNAMESPACE} +kubectl create secret generic wbuser --from-file=e_${WBUSERFILE} -n ${PDBNAMESPACE} +kubectl create secret generic wbpass --from-file=e_${WBPASSFILE} -n ${PDBNAMESPACE} +kubectl create secret generic cdbpwd --from-file=e_${CDBPWDFILE} -n ${CDBNAMESPACE} +kubectl create secret generic cdbusr --from-file=e_${CDBUSRFILE} -n ${CDBNAMESPACE} +kubectl create secret generic syspwd --from-file=e_${SYSPWDFILE} -n ${CDBNAMESPACE} +kubectl create secret generic ordpwd --from-file=e_${ORDPWDFILE} -n ${CDBNAMESPACE} +kubectl create secret generic pdbusr --from-file=e_${PDBUSRFILE} -n ${PDBNAMESPACE} +kubectl create secret generic pdbpwd --from-file=e_${PDBPWDFILE} -n ${PDBNAMESPACE} + +#Get rid of the swap files +rm ${WBUSERFILE} ${WBPASSFILE} ${CDBPWDFILE} ${CDBUSRFILE} \ + ${SYSPWDFILE} ${ORDPWDFILE} ${PDBUSRFILE} ${PDBPWDFILE} \ + e_${WBUSERFILE} e_${WBPASSFILE} e_${CDBPWDFILE} e_${CDBUSRFILE} \ + e_${SYSPWDFILE} e_${ORDPWDFILE} e_${PDBUSRFILE} e_${PDBPWDFILE} +``` + +Check secrets details + +```bash +kubectl describe secrets syspwd -n cdbnamespace +Name: syspwd +Namespace: cdbnamespace +Labels: +Annotations: + +Type: Opaque + +Data +==== +e_syspwd.txt: 349 bytes +``` +Example of yaml file secret section: + +```yaml +[...] + sysAdminPwd: + secret: + secretName: "syspwd" + key: "e_syspwd.txt" + ordsPwd: + secret: + secretName: "ordpwd" + key: "e_ordpwd.txt" +[...] +``` + +## CDB CRD The Oracle Database Operator Multitenant Controller creates the CDB kind as a custom resource that models a target CDB as a native Kubernetes object. This kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. Each CDB resource follows the CDB CRD as defined here: [config/crd/bases/database.oracle.com_cdbs.yaml](../../../config/crd/bases/database.oracle.com_cdbs.yaml) -To create a CDB CRD, see this example`.yaml` file: [cdb_create.yaml](../multitenant/provisioning/singlenamespace/cdb_create.yaml) +To create a CDB CRD, use this example`.yaml` file: [cdb_create.yaml](../multitenant/provisioning/singlenamespace/cdb_create.yaml) **Note:** The password and username fields in this *cdb.yaml* Yaml are the Kubernetes Secrets created earlier in this procedure. For more information, see the section [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). To understand more about creating secrets for pulling images from a Docker private registry, see [Kubernetes Private Registry Documenation]( https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). @@ -197,18 +273,65 @@ Create a CDB CRD Resource example kubectl apply -f cdb_create.yaml ``` -see [usecase01][uc01] and usecase03[uc03] for more information about file configuration +see [usecase01][uc01] and usecase02[uc02] for more information about file configuration -## Kubernetes CRD for PDB +## PDB CRD The Oracle Database Operator Multitenant Controller creates the PDB kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. You cannot have more than one Kubernetes resource for a target PDB. This PDB resource can be used to perform PDB-LM operations by specifying the action attribute in the PDB Specs. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) Yaml file [pdb_create.yaml](../multitenant/provisioning/singlenamespace/pdb_create.yaml) to create a pdb ```bash -kubectl apply -f cdb_create.yaml +kubectl apply -f pdb_create.yaml ``` +## CRD TABLE PARAMETERS + +| yaml file parameters | value | description /ords parameter | CRD | +|------------------ |--------------------------- |-------------------------------------------------------------------------------|-----------| +| dbserver | or | [--db-hostname][1] | CDB | +| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | CDB | +| port | | [--db-port][2] | CDB | +| cdbName | | Container Name | CDB | +| name | | Ords podname prefix in cdb.yaml | CDB | +| name | | pdb resource in pdb.yaml | PDB | +| ordsImage | ords-dboper:latest | ords pod public container registry | CDB | +| pdbName | | Pluggable database name | CDB | +| servicename | | [--db-servicename][3] | CDB | +| sysadmin_user | | [--admin-user][adminuser] | CDB | +| sysadmin_pwd | | [--password-stdin][pwdstdin] | CDB | +| cdbadmin_user | | [db.cdb.adminUser][1] | CDB | +| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | CDB | +| webserver_user | | [https user][http] NOT A DB USER | CDB PDB | +| webserver_pwd | | [http user password][http] | CDB PDB | +| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | CDB | +| pdbTlsKey | | [standalone.https.cert.key][key] | PDB | +| pdbTlsCrt | | [standalone.https.cert][cr] | PDB | +| pdbTlsCat | | certificate authority | PDB | +| cdbTlsKey | | [standalone.https.cert.key][key] | CDB | +| cdbTlsCrt | | [standalone.https.cert][cr] | CDB | +| cdbTlsCat | | certificate authority | CDB | +| cdbOrdsPrvKey | | private key | CDB | +| pdbOrdsPrvKey | | private key | PDB | +| xmlFileName | | path for the unplug and plug operation | PDB | +| srcPdbName | | name of the database to be cloned | PDB | +| action | | create open close delete clone plug unplug and map | PDB | +| deletePdbCascade | boolean | delete pdbs cascade during cdb deletion | CDB | +| assertivePdbDeletion | boolean | Deleting pdb crd means deleteing pdb as well | PDB | +| fileNameConversions | | used for database cloning | PDB | +| totalSize | | dbsize | PDB | +| pdbState | | change pdb state | PDB | +| modifyOption | | to be used along with pdbState | PDB | +| dropAction | | delete datafiles during pdb deletion | PDB | +| sourceFileNameConversions | | [sourceFileNameConversions(optional): string][4] | PDB | +| tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | N/A | +| tdeExport | | [tdeExport] | N/A ] +| tdeSecret | | [tdeSecret][tdeSecret] | N/A | +| tdePassword | | [tdeSecret][tdeSecret] | N/A | + + + + ## Usecases files list ### Single Namespace @@ -243,10 +366,46 @@ kubectl apply -f cdb_create.yaml - Nothing happens after cdb yaml file applying: Make sure to have properly configure the WHATCH_NAMESPACE list in the operator yaml file [okelink]:https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm + [ordsdoc]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.1/index.html + [uc01]:../multitenant/usecase01/README.md + [uc02]:../multitenant/usecase02/README.md - [uc03]:../multitenant/usecase03/README.md + [oradocpdb]:https://docs.oracle.com/en/database/oracle/oracle-database/21/multi/introduction-to-the-multitenant-architecture.html#GUID-AB84D6C9-4BBE-4D36-992F-2BB85739329F -
\ No newline at end of file + [1]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation + + [2]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation + + [3]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-DAA027FA-A4A6-43E1-B8DD-C92B330C2341:~:text=%2D%2Ddb%2Dservicename%20%3Cstring%3E + + [4]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.3/orrst/op-database-pdbs-post.html + +[adminuser]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22:~:text=Table%202%2D6%20Command%20Options%20for%20Uninstall%20CLI + +[public_user]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/using-multitenant-architecture-oracle-rest-data-services.html#GUID-E64A141A-A71F-4979-8D33-C5F8496D3C19:~:text=Preinstallation%20Tasks%20for%20Oracle%20REST%20Data%20Services%20CDB%20Installation + +[key]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=standalone.https.cert.key + +[cr]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0 + +[cdbadminpwd]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=Table%20C%2D1%20Oracle%20REST%20Data%20Services%20Configuration%20Settings + + +[pwdstdin]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-88479C84-CAC1-4133-A33E-7995A645EC05:~:text=default%20database%20pool.-,2.1.4.1%20Understanding%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation,-Table%202%2D2 + +[http]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-BEECC057-A8F5-4EAB-B88E-9828C2809CD8:~:text=Example%3A%20delete%20%5B%2D%2Dglobal%5D-,user%20add,-Add%20a%20user + +[dbtnsurl]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22 + +[tdeKeystorePath]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/21.4/orrst/op-database-pdbs-pdb_name-post.html + +[tdeSecret]:https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ADMINISTER-KEY-MANAGEMENT.html#GUID-E5B2746F-19DC-4E94-83EC-A6A5C84A3EA9 +~ + + + + +
diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml index cc29fc71..8ace42e8 100644 --- a/docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml @@ -11,28 +11,28 @@ spec: replicas: 1 sysAdminPwd: secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" ordsPwd: secret: - secretName: "cdb1-secret" - key: "ords_pwd" + secretName: "[...]" + key: "[...]" cdbAdminUser: secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" + secretName: "[...]" + key: "[...]" cdbAdminPwd: secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" + secretName: "[...]" + key: "[...]" webServerUser: secret: - secretName: "cdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "cdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" cdbTlsKey: secret: secretName: "db-tls" @@ -41,4 +41,8 @@ spec: secret: secretName: "db-tls" key: "tls.crt" + cdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/cdb_secret.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/cdb_secret.yaml deleted file mode 100644 index b65ea1fa..00000000 --- a/docs/multitenant/ords-based/provisioning/multinamespace/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: cdbnamespace -type: Opaque -data: - ords_pwd: ".....base64 encoded password for ORDS_PUBLIC_USER" - sysadmin_pwd: ".....base64 encoded password for SYS" - cdbadmin_user: ".....base64 encoded username E.G. C##DBAPI_CDB_ADMIN" - cdbadmin_pwd: ".....base64 encoded password for cdbadmin_user" - webserver_user: ".....base64 encoded username for https users " - webserver_pwd: ".....base64 encoded password webserver_users" diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml index 930cec5e..4dac1aea 100644 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml @@ -21,12 +21,12 @@ spec: assertivePdbDeletion: true adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "[...]" + key: "[...]" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" pdbTlsKey: secret: secretName: "db-tls" @@ -41,10 +41,14 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" action: "Clone" diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml index a8b6d863..44b1a086 100644 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml @@ -12,12 +12,12 @@ spec: pdbName: "pdbdev" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "[...]" + key: "[...]" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" pdbTlsKey: secret: secretName: "db-tls" @@ -32,12 +32,16 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" pdbState: "CLOSE" modifyOption: "IMMEDIATE" action: "Modify" diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml index 920fc134..2bf2189b 100644 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml @@ -12,12 +12,12 @@ spec: pdbName: "pdbdev" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "[...]" + key: "[...]" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" pdbTlsKey: secret: secretName: "db-tls" @@ -32,12 +32,16 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" fileNameConversions: "NONE" tdeImport: false totalSize: "1G" diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml index 81b539b1..296c9feb 100644 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml @@ -25,10 +25,15 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml index b5f07b59..9f85f0b5 100644 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml @@ -12,12 +12,12 @@ spec: pdbName: "pdbdev" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "[...]" + key: "[...]" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" pdbTlsKey: secret: secretName: "db-tls" @@ -32,12 +32,16 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" action: "Modify" pdbState: "OPEN" modifyOption: "READ WRITE" diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_plug.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_plug.yaml index dfc1c544..10719ccc 100644 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_plug.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_plug.yaml @@ -36,11 +36,16 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_secret.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_secret.yaml deleted file mode 100644 index 3bbe4c79..00000000 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_secret.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: pdbnamespace -type: Opaque -data: - sysadmin_user: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." - diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_unplug.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_unplug.yaml index 976d333b..f30f2699 100644 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_unplug.yaml +++ b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_unplug.yaml @@ -30,10 +30,14 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml index 1e14c5d4..5e020de6 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml @@ -11,34 +11,39 @@ spec: replicas: 1 sysAdminPwd: secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" ordsPwd: secret: - secretName: "cdb1-secret" - key: "ords_pwd" + secretName: "[...]" + key: "[...]" cdbAdminUser: secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" + secretName: "[...]" + key: "[...]" cdbAdminPwd: secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" + secretName: "[...]" + key: "[...]" webServerUser: secret: - secretName: "cdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "cdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" cdbTlsKey: secret: - secretName: "db-tls" - key: "tls.key" + secretName: "[...]" + key: "[...]" cdbTlsCrt: secret: - secretName: "db-tls" - key: "tls.crt" + secretName: "[...]" + key: "[...]" + cdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml deleted file mode 100644 index db7b4438..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: ".....base64 encoded password for ORDS_PUBLIC_USER" - sysadmin_pwd: ".....base64 encoded password for SYS" - cdbadmin_user: ".....base64 encoded username E.G. C##DBAPI_CDB_ADMIN" - cdbadmin_pwd: ".....base64 encoded password for cdbadmin_user" - webserver_user: ".....base64 encoded username for https users " - webserver_pwd: ".....base64 encoded password webserver_users" diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml index 0ecc3c70..21055be3 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml @@ -21,12 +21,12 @@ spec: assertivePdbDeletion: true adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "[...]" + key: "[...]" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" pdbTlsKey: secret: secretName: "db-tls" @@ -41,10 +41,14 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" action: "Clone" diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml index 5bca125a..06d92469 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml @@ -12,12 +12,12 @@ spec: pdbName: "pdbdev" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "[...]" + key: "[...]" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" pdbTlsKey: secret: secretName: "db-tls" @@ -32,12 +32,16 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" pdbState: "CLOSE" modifyOption: "IMMEDIATE" action: "Modify" diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml index f6419e2e..2744223e 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml @@ -12,12 +12,12 @@ spec: pdbName: "pdbdev" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "[...]" + key: "[...]" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" pdbTlsKey: secret: secretName: "db-tls" @@ -32,12 +32,16 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" fileNameConversions: "NONE" tdeImport: false totalSize: "1G" diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml index aa10e5b4..523ac1cb 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml @@ -25,10 +25,15 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml index ca5d1ab8..866db3e4 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml @@ -12,12 +12,12 @@ spec: pdbName: "pdbdev" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "[...]" + key: "[...]" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "[...]" + key: "[...]" pdbTlsKey: secret: secretName: "db-tls" @@ -32,12 +32,16 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" action: "Modify" pdbState: "OPEN" modifyOption: "READ WRITE" diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml index 9563c58a..bd575a8d 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml @@ -36,11 +36,16 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml deleted file mode 100644 index 6b500262..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - sysadmin_user: ".....base64 encoded pdb admin user...." - sysadmin_pwd: ".....base64 encoded pdb amdin password...." - webserver_user: ".....base64 encoded username https authentication" - webserver_pwd: ".....base64 encoded password webserver" - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml index b56a12a3..c001ae28 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml @@ -30,10 +30,14 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "[...]" + key: "[...]" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" + secretName: "[...]" + key: "[...]" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/authsection01.yaml b/docs/multitenant/ords-based/usecase/authsection01.yaml deleted file mode 100644 index 2ddd7a53..00000000 --- a/docs/multitenant/ords-based/usecase/authsection01.yaml +++ /dev/null @@ -1,32 +0,0 @@ - sysAdminPwd: - secret: - secretName: "cdb-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb-secret" - key: "webserver_pwd" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" diff --git a/docs/multitenant/ords-based/usecase/authsection02.yaml b/docs/multitenant/ords-based/usecase/authsection02.yaml deleted file mode 100644 index 78d1b1c5..00000000 --- a/docs/multitenant/ords-based/usecase/authsection02.yaml +++ /dev/null @@ -1,28 +0,0 @@ - adminName: - secret: - secretName: "pdb-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb-secret" - key: "webserver_pwd" diff --git a/docs/multitenant/ords-based/usecase/ca.crt b/docs/multitenant/ords-based/usecase/ca.crt deleted file mode 100644 index b6218ca4..00000000 --- a/docs/multitenant/ords-based/usecase/ca.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDjzCCAnegAwIBAgIUGYsc9F/MMqfWDuFGYK87jx9Wr7swDQYJKoZIhvcNAQEL -BQAwVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEVMBMG -A1UECgwMb3JhY2xlLCBJbmMuMRcwFQYDVQQDDA5vcmFjbGUgUm9vdCBDQTAeFw0y -NDEyMTExMDA5NTFaFw0yNTEyMTExMDA5NTFaMFcxCzAJBgNVBAYTAkNOMQswCQYD -VQQIDAJHRDELMAkGA1UEBwwCU1oxFTATBgNVBAoMDG9yYWNsZSwgSW5jLjEXMBUG -A1UEAwwOb3JhY2xlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQC3H98eEWfMuvwKU/Jk6Lw3BRL+yZAJhK1XmLKNNrrqT9fa/rhHGHbc5a0H -l5GsqQ5hp85y5RX7XhwWcZG6ss/kU7LgKEWBKRs0vxwkHXdUfzrVXKmRIF6Qi4oI -7UCBGHUM1bvld9UnIJILnKC9dVzuUfIf5AEwILyJVJgFGozW8CM7vu+uv+S0mV6G -YTNxS7WeCjg0oU9pTV719/8kr51Y0pS557WpL/D/zugoa7zU/+3VjebpVkJBGCjP -xj26UUO64lnI/LCq3cqUvBXbTBRjwWs/T82SK1SKg4kJ6Z1jPMWJ2tVGU+OFAnp/ -c7nvRXSFlNaiqDUkP2Y+/YQhhrSJAgMBAAGjUzBRMB0GA1UdDgQWBBSTO0wHsvJC -AYCkZfwbS/B2RV+hOzAfBgNVHSMEGDAWgBSTO0wHsvJCAYCkZfwbS/B2RV+hOzAP -BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAIdckJi19lVpUyYsoh -XNUfWRRe0Hc+B72xkojc2mD+YRMxSmR3VT1JPk8pBKh9O5IZQ8+k9Q3kR5Zy5UcZ -9dfoG2DyGswqU3FNZgygfN5bKX8HWoi/E4TaraNvlIbXNw0JQ55mYKdTCOXMUzKs -CpSak06SxvltlpiAvpNjZm/6sHC+lGfX6lzq3/7oDJdQHSyIYOmC7OBoO/tzTuvo -8x3i3ymkj3JZmIm9fSYxm6/yazwJSjDT92ccVkiGbr41H6mj6LarDGjYRHvrHg7p -TpkeU5QJfmMGoAxqbc7ibogvxXfoQVBt8rBcF1+wbHz0mI/6qu0A60AaiLLXpjoA -oUZV ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase/ca.key b/docs/multitenant/ords-based/usecase/ca.key deleted file mode 100644 index 58377d76..00000000 --- a/docs/multitenant/ords-based/usecase/ca.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3H98eEWfMuvwK -U/Jk6Lw3BRL+yZAJhK1XmLKNNrrqT9fa/rhHGHbc5a0Hl5GsqQ5hp85y5RX7XhwW -cZG6ss/kU7LgKEWBKRs0vxwkHXdUfzrVXKmRIF6Qi4oI7UCBGHUM1bvld9UnIJIL -nKC9dVzuUfIf5AEwILyJVJgFGozW8CM7vu+uv+S0mV6GYTNxS7WeCjg0oU9pTV71 -9/8kr51Y0pS557WpL/D/zugoa7zU/+3VjebpVkJBGCjPxj26UUO64lnI/LCq3cqU -vBXbTBRjwWs/T82SK1SKg4kJ6Z1jPMWJ2tVGU+OFAnp/c7nvRXSFlNaiqDUkP2Y+ -/YQhhrSJAgMBAAECggEBALYS6VtTAPqtG3YFW2d7g8/MqsNLk3vzQAptV3JSbWwV -MNCcHIsLxvsBHZYD3KJogFUWbI0OO1nvi0vBZZzUtyEVGzAGunyea195eLhr177a -tZt4g1w5a+yZ4QfZJR52O8MkK6sPB47csnj7JVLagyitr2iH4aqBkhOVWYURjeER -uaF1tJaOQqvYaGcHjnU0Zzm9DmNWTux2DEW9uvfK1ti9ASfI5DQLKn/+cjIEVzLs -qWj1xL72pUgFJ+74JURT3nxDVeFK/3l2xTWg6SIgii8Ttp0h0pjl7GDS38s4IsHq -48blsbj5I6yWG14Iw0AkzUHxxrHUjgUV8W3aa5ZZLmUCgYEA32tUVJLNZLxXPljO -GWCNX06uqt5X7Lg79QdMWOg3Yyx8Kw/xCUWCtLpT1v/QMI+Gs5W3EI8T6PQ6NJWQ -KrypiOzGzFtsqKpLhOLrRpV7Pp0Krwgbl0a/VQuhH9s8tkv0F1gTGV/dw0z7jtRF -nhvfCpFU/hNfcTA+j9lzKyXD80sCgYEA0dRC5ua1qqGlLAR68jGTkjv+6IBvbikZ -RLRZNsWrZX02B1UiXWcJyk5hKAOf1la0m+ZCcEP8VH0nP6ONTYfvJpzlpVHureEX -6spBfZIY5whgBYC2dFmaP/iVbcxX7qVMD3N1eMd43pcqgNaMgdHmKa9b5gNilezw -hv1s/IiRPvsCgYA7YCHMTCtrzyX8estjMSbBIn8HCAoj7h55ExR2OFin8aWaKCVt -ylxfPXmUlO22Sh6JUYuSSgQHkAh7+xeSVAtWSV7X5qAs/v0M9uWCH63eSQwS0jvK -61Fj/7A1cCVlaTAYmB6TSsf75FSic8WArHWNMGx/DZT61tB5mFekKug1mwKBgAsF -/rnItxSOx3Gnjpn4tssA8eRfEvuTlys/2kwBWH7NdDAwbczAZAhH09zZS+VjyrtB -/o2NVM1Pgeda/UpscMjzhzEHO+XQpIQGOiiRq1M/4mOHno5AQtVHhvosuRXlgzhw -lWRR/mybPnVkUB9l2pSoDMjptp0vxHcjCz29IvxPAoGBAN8MRq+Y7lPAwFMWm5P0 -Aa9e5ygHNeB3VzpdPJkGI4wtXbiCyGnoPDKNY1z0JpH9FLd70582r4AHL3noJ/c2 -hQMb6XSlGeW5R1GUCLvSK4Do5lsvrN6AgNOmaeexWkTYBzPx/2rBiD/rXtf800xI -oxFSaSzPDbDU1woVZU2M/XrQ ------END PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase/ca.srl b/docs/multitenant/ords-based/usecase/ca.srl deleted file mode 100644 index d2ec50af..00000000 --- a/docs/multitenant/ords-based/usecase/ca.srl +++ /dev/null @@ -1 +0,0 @@ -3482FC9E27388FE655F7F6FC78C29F9C151AA952 diff --git a/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml index c07d101e..5723f7c6 100644 --- a/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml +++ b/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml @@ -18,12 +18,12 @@ spec: action: "Clone" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -38,9 +38,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml index 07889220..2b9fc70a 100644 --- a/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml +++ b/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml @@ -18,12 +18,12 @@ spec: action: "Clone" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -38,9 +38,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml index 915222fb..ae837ce0 100644 --- a/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml +++ b/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml @@ -15,12 +15,12 @@ spec: action: "Modify" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -35,9 +35,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml index 4733bdf5..1b5d1324 100644 --- a/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml +++ b/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml @@ -15,12 +15,12 @@ spec: action: "Modify" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -35,9 +35,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml index accb6978..f4a32938 100644 --- a/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml +++ b/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml @@ -15,12 +15,12 @@ spec: action: "Modify" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -35,9 +35,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_cdb_secrets.yaml b/docs/multitenant/ords-based/usecase/create_cdb_secrets.yaml deleted file mode 100644 index c0f5cb64..00000000 --- a/docs/multitenant/ords-based/usecase/create_cdb_secrets.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: cdb-secret - namespace: cdbnamespace -type: Opaque -data: - ords_pwd: "TWFzY2V0dGkK" - sysadmin_pwd: "V0VsY29tZV8xMiMjCg==" - cdbadmin_user: "QyMjREJBUElfQ0RCX0FETUlOCg==" - cdbadmin_pwd: "V0VsY29tZV8xMiMjCg==" - webserver_user: "d2VsY29tZQo=" - webserver_pwd: "d2VsY29tZTEK" diff --git a/docs/multitenant/ords-based/usecase/create_ords_pod.yaml b/docs/multitenant/ords-based/usecase/create_ords_pod.yaml index 6c951e16..ad196c9d 100644 --- a/docs/multitenant/ords-based/usecase/create_ords_pod.yaml +++ b/docs/multitenant/ords-based/usecase/create_ords_pod.yaml @@ -5,35 +5,35 @@ metadata: namespace: cdbnamespace spec: cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" + ordsImage: _your_container_registry/ords-dboper:latest ordsImagePullPolicy: "Always" - dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + dbTnsurl : "T H I S I S J U S T A N E X A M P L E (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" replicas: 1 deletePdbCascade: true sysAdminPwd: secret: - secretName: "cdb-secret" - key: "sysadmin_pwd" + secretName: "syspwd" + key: "e_syspwd.txt" ordsPwd: secret: - secretName: "cdb-secret" - key: "ords_pwd" + secretName: "ordpwd" + key: "e_ordpwd.txt" cdbAdminUser: secret: - secretName: "cdb-secret" - key: "cdbadmin_user" + secretName: "cdbusr" + key: "e_cdbusr.txt" cdbAdminPwd: secret: - secretName: "cdb-secret" - key: "cdbadmin_pwd" + secretName: "cdbpwd" + key: "e_cdbpwd.txt" webServerUser: secret: - secretName: "cdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "cdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" cdbTlsKey: secret: secretName: "db-tls" @@ -42,3 +42,7 @@ spec: secret: secretName: "db-tls" key: "tls.crt" + cdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml index 09b99269..84e910e0 100644 --- a/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml +++ b/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml @@ -19,12 +19,12 @@ spec: action: "Create" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -39,9 +39,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml index 27381e0b..0a71c7c3 100644 --- a/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml +++ b/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml @@ -19,12 +19,12 @@ spec: action: "Create" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -39,9 +39,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_pdb_secrets.yaml b/docs/multitenant/ords-based/usecase/create_pdb_secrets.yaml deleted file mode 100644 index d5491a8e..00000000 --- a/docs/multitenant/ords-based/usecase/create_pdb_secrets.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: pdb-secret - namespace: pdbnamespace -type: Opaque -data: - sysadmin_user: "Q2l0aXplbmthbmUK" - sysadmin_pwd: "Um9zZWJ1ZAo=" - webserver_user: "d2VsY29tZQo=" - webserver_pwd: "d2VsY29tZTEK" diff --git a/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml index f458350d..3aba580c 100644 --- a/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml +++ b/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml @@ -13,12 +13,12 @@ spec: dropAction: "INCLUDING" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -33,9 +33,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml index d4e362e8..59b50a64 100644 --- a/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml +++ b/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml @@ -13,12 +13,12 @@ spec: dropAction: "INCLUDING" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -33,9 +33,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/extfile.txt b/docs/multitenant/ords-based/usecase/extfile.txt deleted file mode 100644 index 1c65bfd6..00000000 --- a/docs/multitenant/ords-based/usecase/extfile.txt +++ /dev/null @@ -1 +0,0 @@ -subjectAltName=DNS:cdb-dev-ords.cdbnamespace diff --git a/docs/multitenant/ords-based/usecase/makefile b/docs/multitenant/ords-based/usecase/makefile index 0df25e6d..dc881598 100644 --- a/docs/multitenant/ords-based/usecase/makefile +++ b/docs/multitenant/ords-based/usecase/makefile @@ -27,6 +27,8 @@ # It configure the required secrets necessary to operate # with pdbs multitenant controllers # +# 4) make runall01 +# Start a series of operation create open close delete and so on # # LIST OF GENERAED YAML FILE # @@ -34,8 +36,6 @@ # oracle-database-operator.yaml : oracle database operator # cdbnamespace_binding.yaml : role binding for cdbnamespace # pdbnamespace_binding.yaml : role binding for pdbnamespace -# create_cdb_secret.yaml : create secrets for ords server pod -# create_pdb_secret.yaml : create secrets for pluggable database # create_ords_pod.yaml : create rest server pod # create_pdb1_resource.yaml : create first pluggable database # create_pdb2_resource.yaml : create second pluggable database @@ -76,6 +76,8 @@ export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : export OPRNAMESPACE=oracle-database-operator-system export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml export TEST_EXEC_TIMEOUT=3m +export IMAGE=oracle/ords-dboper:latest +export ORDSIMGDIR=../../../../ords REST_SERVER=ords SKEY=tls.key @@ -84,6 +86,7 @@ CART=ca.crt PRVKEY=ca.key PUBKEY=public.pem COMPANY=oracle +RUNTIME=/usr/bin/podman ################# ### FILE LIST ### @@ -132,6 +135,7 @@ RM=/usr/bin/rm CP=/usr/bin/cp TAR=/usr/bin/tar MKDIR=/usr/bin/mkdir +SED=/usr/bin/sed define msg @printf "\033[31;7m%s\033[0m\r" "......................................]" @@ -167,11 +171,6 @@ tlscrt: $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) -#- $(KUBECTL) delete secret db-tls -n $(CDBNAMESPACE) 2>/dev/null -#- $(KUBECTL) delete secret db-ca -n $(CDBNAMESPACE) 2>/dev/null -#- $(KUBECTL) delete secret db-tls -n $(PDBNAMESPACE) 2>/dev/null -#- $(KUBECTL) delete secret db-ca -n $(PDBNAMESPACE) 2>/dev/null - tlssec: $(call msg,"GENERATE TLS SECRET") $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDBNAMESPACE) @@ -179,12 +178,6 @@ tlssec: $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDBNAMESPACE) $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDBNAMESPACE) -secrets: delsec tlscrt tlssec - $(call msg,"GENERATE CDB SECRET") - $(KUBECTL) apply -f $(CDB_SECRETS) - $(call msg,"GENERATE PDB SECRET") - $(KUBECTL) apply -f $(PDB_SECRETS) - delsec: $(call msg,"CLEAN OLD SECRETS") @@ -199,49 +192,89 @@ delsec: ($(KUBECTL) delete secret $(SECRETSL) -n $(CDBNAMESPACE))\ || ( echo "No screts in namespace $(PDBNAMESPACE)") - -### YAML FILE SECTION ### -define _opr -cp ${ORACLE_OPERATOR_YAML} . -export OPBASENAME=`basename ${ORACLE_OPERATOR_YAML}` -cp ${OPBASENAME} ${OPBASENAME}.ORIGNINAL -printf "\n\t\xF0\x9F\x91\x89 ${OPBASENAME}\n\n" -sed -i 's/value: ""/value: "${OPRNAMESPACE},${PDBNAMESPACE},${CDBNAMESPACE}"/g' ${OPBASENAME} -endef +###### ENCRYPTED SECRETS ###### +export PRVKEY=ca.key +export PUBKEY=public.pem +WBUSERFILE=wbuser.txt +WBPASSFILE=wbpass.txt +CDBUSRFILE=cdbusr.txt +CDBPWDFILE=cdbpwd.txt +SYSPWDFILE=syspwd.txt +ORDPWDFILE=ordpwd.txt +PDBUSRFILE=pdbusr.txt +PDBPWDFILE=pdbpwd.txt + + + +secrets: delsec tlscrt tlssec + $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) + $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(CDBNAMESPACE) + $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(CDBNAMESPACE) + $(KUBECTL) create secret generic prvkey --from-file=privateKey="$(PRVKEY)" -n $(PDBNAMESPACE) + @$(ECHO) $(WBUSER) > $(WBUSERFILE) + @$(ECHO) $(WBPASS) > $(WBPASSFILE) + @$(ECHO) $(CDBPWD) > $(CDBPWDFILE) + @$(ECHO) $(CDBUSR) > $(CDBUSRFILE) + @$(ECHO) $(SYSPWD) > $(SYSPWDFILE) + @$(ECHO) $(ORDPWD) > $(ORDPWDFILE) + @$(ECHO) $(PDBUSR) > $(PDBUSRFILE) + @$(ECHO) $(PDBPWD) > $(PDBPWDFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBPWDFILE) |base64 > e_$(CDBPWDFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBUSRFILE) |base64 > e_$(CDBUSRFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE) |base64 > e_$(SYSPWDFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(ORDPWDFILE) |base64 > e_$(ORDPWDFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) + $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(CDBNAMESPACE) + $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(CDBNAMESPACE) + $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic cdbpwd --from-file=e_$(CDBPWDFILE) -n $(CDBNAMESPACE) + $(KUBECTL) create secret generic cdbusr --from-file=e_$(CDBUSRFILE) -n $(CDBNAMESPACE) + $(KUBECTL) create secret generic syspwd --from-file=e_$(SYSPWDFILE) -n $(CDBNAMESPACE) + $(KUBECTL) create secret generic ordpwd --from-file=e_$(ORDPWDFILE) -n $(CDBNAMESPACE) + $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(PDBNAMESPACE) + $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(PDBNAMESPACE) + $(RM) $(WBUSERFILE) $(WBPASSFILE) $(CDBPWDFILE) $(CDBUSRFILE) $(SYSPWDFILE) $(ORDPWDFILE) $(PDBUSRFILE) $(PDBPWDFILE) + $(RM) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(CDBPWDFILE) e_$(CDBUSRFILE) e_$(SYSPWDFILE) e_$(ORDPWDFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) -export opr = $(value _opr) +### YAML FILE SECTION ### operator: - @ eval "$$opr" + $(CP) ${ORACLE_OPERATOR_YAML} . + ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG + $(SED) -i 's/value: ""/value: $(OPRNAMESPACE),$(PDBNAMESPACE),$(CDBNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` define _script00 cat < authsection01.yaml sysAdminPwd: secret: - secretName: "cdb-secret" - key: "sysadmin_pwd" + secretName: "syspwd" + key: "e_syspwd.txt" ordsPwd: secret: - secretName: "cdb-secret" - key: "ords_pwd" + secretName: "ordpwd" + key: "e_ordpwd.txt" cdbAdminUser: secret: - secretName: "cdb-secret" - key: "cdbadmin_user" + secretName: "cdbusr" + key: "e_cdbusr.txt" cdbAdminPwd: secret: - secretName: "cdb-secret" - key: "cdbadmin_pwd" + secretName: "cdbpwd" + key: "e_cdbpwd.txt" webServerUser: secret: - secretName: "cdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "cdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" cdbTlsKey: secret: secretName: "db-tls" @@ -250,17 +283,21 @@ cat < authsection01.yaml secret: secretName: "db-tls" key: "tls.crt" + cdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" EOF cat< authsection02.yaml adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -275,12 +312,16 @@ cat< authsection02.yaml key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" EOF @@ -321,53 +362,6 @@ export script00 = $(value _script00) secyaml: @ eval "$$script00" -define _script_secrets -ORDS=`echo ${ORDPWD}|base64` -SYSA=`echo ${SYSPWD}|base64` -CDBU=`echo ${CDBUSR}|base64` -CDBP=`echo ${CDBPWD}|base64` -WEBU=`echo ${WBUSER}|base64` -WEBP=`echo ${WBPASS}|base64` -PDBU=`echo ${PDBUSR}|base64` -PDBP=`echo ${PDBPWD}|base64` -cat < ${CDB_SECRETS} -apiVersion: v1 -kind: Secret -metadata: - name: cdb-secret - namespace: ${CDBNAMESPACE} -type: Opaque -data: - ords_pwd: "${ORDS}" - sysadmin_pwd: "${SYSA}" - cdbadmin_user: "${CDBU}" - cdbadmin_pwd: "${CDBP}" - webserver_user: "${WEBU}" - webserver_pwd: "${WEBP}" -EOF - -cat < ${PDB_SECRETS} -apiVersion: v1 -kind: Secret -metadata: - name: pdb-secret - namespace: ${PDBNAMESPACE} -type: Opaque -data: - sysadmin_user: "${PDBU}" - sysadmin_pwd: "${PDBP}" - webserver_user: "${WEBU}" - webserver_pwd: "${WEBP}" -EOF - -endef - - -export script_secrets=$(value _script_secrets) - -dbsecr: - @ eval "$$script_secrets" - #echo ords pod creation define _script01 cat < ${ORDS_POD} @@ -375,7 +369,7 @@ apiVersion: database.oracle.com/${APIVERSION} kind: CDB metadata: name: cdb-dev - namespace: cdbnamespace + namespace: ${CDBNAMESPACE} spec: cdbName: "DB12" ordsImage: ${ORDSIMG} @@ -735,7 +729,7 @@ endef export script02 = $(value _script02) -genyaml:dbsecr secyaml +genyaml: secyaml @ eval "$$script01" @ eval "$$script02" @@ -903,4 +897,19 @@ run99.1: runall01: run00 run01.1 run01.2 run03.1 run03.2 run04.1 run05.1 run06.1 run02.1 run07.1 +###### BUILD ORDS IMAGE ###### + +createimage: + $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) + +createimageproxy: + $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) + +tagimage: + @echo "TAG IMAGE" + $(RUNTIME) tag $(IMAGE) $(ORDSIMG) + +push: + $(RUNTIME) push $(ORDSIMG) + diff --git a/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml index 16815cf6..b71b59d5 100644 --- a/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml +++ b/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml @@ -17,12 +17,12 @@ spec: action: "Map" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -37,9 +37,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml index d8dd6acc..75d056d0 100644 --- a/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml +++ b/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml @@ -17,12 +17,12 @@ spec: action: "Map" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -37,9 +37,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml index 2f561814..3523aa68 100644 --- a/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml +++ b/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml @@ -17,12 +17,12 @@ spec: action: "Map" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -37,9 +37,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml index 3507ade3..93a1d43a 100644 --- a/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml +++ b/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml @@ -15,12 +15,12 @@ spec: modifyOption: "READ WRITE" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -35,9 +35,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml index 1498f135..deb27f9a 100644 --- a/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml +++ b/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml @@ -15,12 +15,12 @@ spec: modifyOption: "READ WRITE" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -35,9 +35,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml index bd0f358a..586f2f57 100644 --- a/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml +++ b/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml @@ -15,12 +15,12 @@ spec: modifyOption: "READ WRITE" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -35,9 +35,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml deleted file mode 120000 index bbf4775f..00000000 --- a/docs/multitenant/ords-based/usecase/oracle-database-operator.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/ords-based/usecase/parameters.txt b/docs/multitenant/ords-based/usecase/parameters.txt index 3068311b..64dc3759 100644 --- a/docs/multitenant/ords-based/usecase/parameters.txt +++ b/docs/multitenant/ords-based/usecase/parameters.txt @@ -3,45 +3,43 @@ ## REST SERVER IMAGE ### ######################## -ORDSIMG:[Enter the image build using oracle-database-operator/ords/Dockerfile] +ORDSIMG:_your_container_registry/ords-dboper:latest ############################## ## TNS URL FOR CDB CREATION ## ############################## -TNSALIAS:"[cdb tns descriptor]" - -#e.g. TNSALIAS:"(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" +TNSALIAS:"T H I S I S J U S T A N E X A M P L E (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" ########################################### ## ORDS PUBLIC USER ## ########################################### -ORDPWD:[enter ords public user passwrod] +ORDPWD:change_me_please ########################################### ## SYSPASSWORD ## ########################################### -SYSPWD:[enter database sys password] +SYSPWD:change_me_please ####################### ## HTTPS CREDENTIAL ### ####################### -WBUSER:[webuser credential:username] -WBPASS:[webuser credential:password] +WBUSER:change_me_please +WBPASS:change_me_please ##################### ## PDB ADMIN USER ### ##################### -PDBUSR:[Pdb admin credential: username] -PDBPWD:[Pdb admin credential: password] +PDBUSR:change_me_please +PDBPWD:change_me_please ##################### ## CDB ADMIN USER ### ##################### CDBUSR:C##DBAPI_CDB_ADMIN -CDBPWD:[cdb admin password] +CDBPWD:change_me_please ################### ### NAMESPACES #### diff --git a/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml index ab192655..9eb5ed77 100644 --- a/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml +++ b/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml @@ -21,12 +21,12 @@ spec: action: "Plug" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -41,9 +41,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/public.pem b/docs/multitenant/ords-based/usecase/public.pem deleted file mode 100644 index a83e254c..00000000 --- a/docs/multitenant/ords-based/usecase/public.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtx/fHhFnzLr8ClPyZOi8 -NwUS/smQCYStV5iyjTa66k/X2v64Rxh23OWtB5eRrKkOYafOcuUV+14cFnGRurLP -5FOy4ChFgSkbNL8cJB13VH861VypkSBekIuKCO1AgRh1DNW75XfVJyCSC5ygvXVc -7lHyH+QBMCC8iVSYBRqM1vAjO77vrr/ktJlehmEzcUu1ngo4NKFPaU1e9ff/JK+d -WNKUuee1qS/w/87oKGu81P/t1Y3m6VZCQRgoz8Y9ulFDuuJZyPywqt3KlLwV20wU -Y8FrP0/NkitUioOJCemdYzzFidrVRlPjhQJ6f3O570V0hZTWoqg1JD9mPv2EIYa0 -iQIDAQAB ------END PUBLIC KEY----- diff --git a/docs/multitenant/ords-based/usecase/server.csr b/docs/multitenant/ords-based/usecase/server.csr deleted file mode 100644 index 2a85d7c1..00000000 --- a/docs/multitenant/ords-based/usecase/server.csr +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIICpzCCAY8CAQAwYjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQH -DAJTWjEVMBMGA1UECgwMb3JhY2xlLCBJbmMuMSIwIAYDVQQDDBljZGItZGV2LW9y -ZHMuY2RibmFtZXNwYWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -x36MZ8tRa9fsJk/J7CwCYN8tji+/ZNea2MQkpG01vA/8pKcTWzxRw4R+GOyNiOQ/ -K006HPj9jd5vkwlHUw0jh8TdUZfEVo6+j3RlAma4cRtQt+5GPCok1pzOGb5+FvSD -FF8YmE69Ao6aW+9U+Xg7u2skgSULN3rCOF1ADoBqRreuZuArxLvqxqMtQqrxwZO1 -3spap4rTLeahbSHaNrIZ3WVGFMGux+3zHMKnANEEo9Td3FQFsD4dZxCbdXIaqlFT -mowMjb0VG5t357aSfosPUFFxr/5lrsWeyKBNRyP0xUjwwDdEJOD49NKWpxew0+IZ -ZcfZUhcOXkXAfxSHuDGhlwIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAH6eOsDr -MBHHvrXujkXCDuwvkFwt26tqTGK4H1VaMlZ74aPABlrn0bgc9FhMS9Q1F/XiAVXB -pEmMde3wI9ZajO6g6FjYuLJZ2zhW37Mwf5Er8X2T+RsTLC8Df/8owTwUR0UqOkpb -vkmOpiWFz1aft0ir9JYpc/6pBJLJSKgdMBZVMVLOk/Ld9uJcpW0PW23DCCSYNcZN -e3UGz5Irjrnh9ivQm+8Xa/M8A7strjuDi2QSHMmioXQA9L6yLCxicxnLpE/uJnKh -ysxiUl87S+1jeiI5jagnYPLEYlGD/m/SyEmeYrblbFaGhkyIFGmzccbMQUeNjg/c -XJ4BZno1eyFlvOk= ------END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/ords-based/usecase/tls.crt b/docs/multitenant/ords-based/usecase/tls.crt deleted file mode 100644 index cf77a607..00000000 --- a/docs/multitenant/ords-based/usecase/tls.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDbzCCAlegAwIBAgIUNIL8nic4j+ZV9/b8eMKfnBUaqVIwDQYJKoZIhvcNAQEL -BQAwVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEVMBMG -A1UECgwMb3JhY2xlLCBJbmMuMRcwFQYDVQQDDA5vcmFjbGUgUm9vdCBDQTAeFw0y -NDEyMTExMDA5NTFaFw0yNTEyMTExMDA5NTFaMGIxCzAJBgNVBAYTAkNOMQswCQYD -VQQIDAJHRDELMAkGA1UEBwwCU1oxFTATBgNVBAoMDG9yYWNsZSwgSW5jLjEiMCAG -A1UEAwwZY2RiLWRldi1vcmRzLmNkYm5hbWVzcGFjZTCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAMd+jGfLUWvX7CZPyewsAmDfLY4vv2TXmtjEJKRtNbwP -/KSnE1s8UcOEfhjsjYjkPytNOhz4/Y3eb5MJR1MNI4fE3VGXxFaOvo90ZQJmuHEb -ULfuRjwqJNaczhm+fhb0gxRfGJhOvQKOmlvvVPl4O7trJIElCzd6wjhdQA6Aaka3 -rmbgK8S76sajLUKq8cGTtd7KWqeK0y3moW0h2jayGd1lRhTBrsft8xzCpwDRBKPU -3dxUBbA+HWcQm3VyGqpRU5qMDI29FRubd+e2kn6LD1BRca/+Za7FnsigTUcj9MVI -8MA3RCTg+PTSlqcXsNPiGWXH2VIXDl5FwH8Uh7gxoZcCAwEAAaMoMCYwJAYDVR0R -BB0wG4IZY2RiLWRldi1vcmRzLmNkYm5hbWVzcGFjZTANBgkqhkiG9w0BAQsFAAOC -AQEAoi4TgBrCy+pEDH2d0GVx/Mqn3iDPeCH8VFYcLRK8NIjHiHIZtp1Y32Z3Xp5W -Xaor3SvBn00Rsy9y4Q2zBxx0pY0Tkub6nsm8cMGMpd9oBMuQ4ecTnuui3sjuA+au -Fv5+Qrb20rdJi4nbYFM17OuhQWrqpORtCfCk6rVLrj4zpejzd6r/wBZr2JgKJ4r5 -nTb/c2uUNO+ImU+XTCWTzOTq2XdXkPeMhfgBlrUB5yhDgCsx6IsE7Br/4MkbrLc9 -/1ESu5cNWP7lBQz5pp+uwgJdlVJwfqYvf2zVy9XA9l65DgGhyt135WGPDZfjqjtJ -GTPZyO88lA4eqvk7xFjpEYbSMw== ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase/tls.key b/docs/multitenant/ords-based/usecase/tls.key deleted file mode 100644 index 4418929f..00000000 --- a/docs/multitenant/ords-based/usecase/tls.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHfoxny1Fr1+wm -T8nsLAJg3y2OL79k15rYxCSkbTW8D/ykpxNbPFHDhH4Y7I2I5D8rTToc+P2N3m+T -CUdTDSOHxN1Rl8RWjr6PdGUCZrhxG1C37kY8KiTWnM4Zvn4W9IMUXxiYTr0Cjppb -71T5eDu7aySBJQs3esI4XUAOgGpGt65m4CvEu+rGoy1CqvHBk7XeylqnitMt5qFt -Ido2shndZUYUwa7H7fMcwqcA0QSj1N3cVAWwPh1nEJt1chqqUVOajAyNvRUbm3fn -tpJ+iw9QUXGv/mWuxZ7IoE1HI/TFSPDAN0Qk4Pj00panF7DT4hllx9lSFw5eRcB/ -FIe4MaGXAgMBAAECggEBAL/SBt+muFuh+we7oMTebEsJTZxyfO5SAip8L1+LQyfZ -l2wa2pnXnicJttXwXwdJsRPJ5h8Fq4hGYbJYMaTB9XAO82AJNlikWMdHe/ibHK5l -PXAeRJqFS3awv9wxNryLz54+8j9BNqzGwdW0g1h9K7JCi49CN07ZUvcvcvB+ubHU -KHOqbf0kc31CwEJSvpjaBrq+4MQy/NNqcsXHlDZKxg087Poyz4PFRUGW0Zj+jrkX -j3+HGS+ZRdYWqk0zCQfeGeOhgo+LEawYr63u3BgiOpUmGGnE/a20q3WphG4eTeNs -ZjTvn0KfHli6NmkYL6GGNLqQCYeGHxXa8LGYhFAhHgECgYEA9PUPKkCeJC23jjAB -fB+o2RA7ySIcrA08WqHg2V3f4o14Un7gjsEMSE+9E/PkDSPlDpwtG3m2z+xV+2V7 -yJpvKVVrfeC95JiKKyJIkr8hkOTQPb2P53jH20/nu6JlrHFcVT4P9tzfWwI+L3zS -ktulH3hnKWfCJiq7b9Zl6d6mybcCgYEA0HzSdjER2Ie8NuQLkoKV0APduTDZ0fPR -iyPOVhouoKTFKbN0eMG8OS/FkdD2jitxzk/Sk0gZ0lc4l6lnbThRDAF7RXMf4RYN -1u7XGEGteN1yIgpMA1nk1OHFsLb26cd73APNtzibqVsIClUkjKgaTmQMEulCtn4/ -s6Jv7ua/ZyECgYAi7rek7OYgfqyIiGwNyW4I1nnQKx8voguTWTZ5iRuecVJRYWqg -sQMRZN2v9L326cGGndsI6giaCjgQy94lM3Da0iWsI9qwuNgrYo467yrYQOk5hMj6 -14yYyAJiDby2JMxoE5uvNrxbfCxmFUTqUIxxunfrvbJAQyPLxSVvfqMGbQKBgQDI -v2fnZlhIpmoUcIC0Egkuwoo8R011W8/oqhGD1rIO/JjcDm+X1vPmHJmgThUZz7tF -bJbWOhai8lkLr5JnGFLxvHt14+ROV238Bv4V3SeruY6sOD34/BXa5Yn1Hjeh4vLg -ZskuItO/vmd/i3wl87dgw/TcQL57+hRcY4xj/MtQAQKBgFwkwEnQ3Q46stQRzeBc -zW5w3ABcCmc89MKwMF9vwzRFgrD1pH0sCREbHi599QhJN9KE4wmjomqK6J6r6mT7 -rWN+v80Nk35BIM/DXTRfo3vhzWVY35alwUo1Ur30tayJJ9PXhEBUWSCYtbhuc+Ey -W5zt+XiVe9vmpZ+qm5mqKA1Q ------END PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml index 12627c6b..0036d5f7 100644 --- a/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml +++ b/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml @@ -14,12 +14,12 @@ spec: action: "Unplug" adminName: secret: - secretName: "pdb-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -34,9 +34,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb-secret" - key: "webserver_pwd" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/README.md b/docs/multitenant/ords-based/usecase01/README.md index 7352257e..85aad7c0 100644 --- a/docs/multitenant/ords-based/usecase01/README.md +++ b/docs/multitenant/ords-based/usecase01/README.md @@ -50,7 +50,9 @@ The following table reports the parameters required to configure and use oracle | pdbTlsKey | | [standalone.https.cert.key][key] | | pdbTlsCrt | | [standalone.https.cert][cr] | | pdbTlsCat | | certificate authority | -| assertivePdbDeletion | boolean | [turn on imperative approach on crd deleteion][imperative] | +| cdbOrdsPrvKey | | private key (cdb crd) | +| pdbOrdsPrvKey | | private key (pdb crd) | +| assertivePdbDeletion | boolean | [turn on imperative approach on crd deleteion][imperative] | > A [makfile](./makefile) is available to sped up the command execution for the multitenant setup and test. See the comments in the header of file @@ -162,62 +164,6 @@ GRANT CREATE SESSION TO CONTAINER = ALL; ``` ---- -#### Create CDB secret - -+ Create secret for CDB connection - -```bash -kubectl apply -f cdb_secret.yaml -n oracle-database-operator-system - -``` -Exmaple: **cdb_secret.yaml** - -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: "encoded value" - sysadmin_pwd: "encoded value" - cdbadmin_user: "encoded value" - cdbadmin_pwd: "encoded value" - webserver_user: "encoded value" - webserver_pwd: "encoded value" - -``` -Use **base64** command to encode/decode username and password in the secret file as shown in the following example - -- encode -```bash -echo "ThisIsMyPassword" |base64 -i -VGhpc0lzTXlQYXNzd29yZAo= -``` -- decode -```bash - echo "VGhpc0lzTXlQYXNzd29yZAo=" | base64 --decode -ThisIsMyPassword - -``` - - ->Note that we do not have to create webuser on the database. - -+ Check secret: - -```bash -kubectl get secret -n oracle-database-operator-system -NAME TYPE DATA AGE -cdb1-secret Opaque 6 7s <--- -container-registry-secret kubernetes.io/dockerconfigjson 1 2m17s -webhook-server-cert kubernetes.io/tls 3 4m55s -``` - ->**TIPS:** Use the following commands to analyze contents of an existing secret ```bash kubectl get secret -o yaml -n ``` ----- - #### Create Certificates + Create certificates: At this stage we need to create certificates on our local machine and upload into kubernetes cluster by creating new secrets. @@ -257,7 +203,7 @@ webhook-server-cert kubernetes.io/tls 3 4m55s ```bash -genrsa -out 2048 +openssl genrsa -out 2048 openssl req -new -x509 -days 365 -key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out openssl req -newkey rsa:2048 -nodes -keyout -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=-ords" -out server.csr /usr/bin/echo "subjectAltName=DNS:-ords,DNS:www.example.com" > extfile.txt @@ -270,6 +216,9 @@ kubectl create secret generic db-ca --from-file= -n oracle-database-op [Example of execution:](./logfiles/openssl_execution.log) +#### CDB and PDB credential + +Refer to the [landing page](../README.md) to implement openssl encrpted secrets. ---- @@ -283,9 +232,9 @@ kubectl create secret generic db-ca --from-file= -n oracle-database-op + Create ords container ```bash -/usr/bin/kubectl apply -f cdb_create.yaml -n oracle-database-operator-system +/usr/bin/kubectl apply -f create_ords_pod.yaml -n oracle-database-operator-system ``` -Example: **cdb_create.yaml** +Example: **create_ords_pod.yaml** ```yaml apiVersion: database.oracle.com/v1alpha1 @@ -299,30 +248,30 @@ spec: ordsImagePullPolicy: "Always" dbTnsurl : "...Container tns alias....." replicas: 1 - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" + sysAdminPwd: + secret: + secretName: "syspwd" + key: "e_syspwd.txt" ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" + secret: + secretName: "ordpwd" + key: "e_ordpwd.txt" + cdbAdminUser: + secret: + secretName: "cdbusr" + key: "e_cdbusr.txt" + cdbAdminPwd: + secret: + secretName: "cdbpwd" + key: "e_cdbpwd.txt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" cdbTlsKey: secret: secretName: "db-tls" @@ -331,6 +280,11 @@ spec: secret: secretName: "db-tls" key: "tls.crt" + cdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" + ``` > **Note** if you are working in dataguard environment with multiple sites (AC/DR) specifying the host name (dbServer/dbPort/serviceName) may not be the suitable solution for this kind of configuration, use **dbTnsurl** instead. Specify the whole tns string which includes the hosts/scan list. @@ -351,7 +305,7 @@ spec: dbtnsurl:((DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(TRANS...... ``` -[Example of cdb.yaml](./cdb_create.yaml) +[create_ords_pod.yaml example](./create_ords_pod.yaml) ---- @@ -418,50 +372,16 @@ NAME CDB NAME DB SERVER DB PORT REPLICAS STATUS MESSAG ----- -#### Create PDB secret - - -```bash -/usr/bin/kubectl apply -f pdb.yaml -n oracle-database-operator-system -``` -Exmaple: **pdb_secret.yaml** - -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - sysadmin_user: "encoded value" - sysadmin_pwd: "encoded value" -``` - -+ Check secret creation - -```bash -kubectl get secret -n oracle-database-operator-system -NAME TYPE DATA AGE -cdb1-secret Opaque 6 79m -container-registry-secret kubernetes.io/dockerconfigjson 1 79m -db-ca Opaque 1 78m -db-tls kubernetes.io/tls 2 78m -pdb1-secret Opaque 2 79m <--- -webhook-server-cert kubernetes.io/tls 3 79m -``` ---- - #### Apply pdb yaml file to create pdb ```bash -/usr/bin/kubectl apply -f pdb.yaml -n oracle-database-operator-system +/usr/bin/kubectl apply -f create_pdb1_resource.yaml -n oracle-database-operator-system ``` -Example: **pdb_create.yaml** +Example: **create_pdb1_resource.yaml** ```yaml -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 @@ -470,17 +390,24 @@ metadata: cdb: cdb-dev spec: cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" + cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbdev" + assertivePdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -495,18 +422,16 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - assertivePdbDeletion: true + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" ``` + Monitor the pdb creation status until message is success diff --git a/docs/multitenant/ords-based/usecase01/ca.crt b/docs/multitenant/ords-based/usecase01/ca.crt deleted file mode 100644 index cc9aa8bb..00000000 --- a/docs/multitenant/ords-based/usecase01/ca.crt +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEJTCCAw2gAwIBAgIUNXPtpnNEFBCMcnxRP5kJsBDpafcwDQYJKoZIhvcNAQEL -BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH -DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k -ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE -AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx -NTMyMzVaMIGhMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG -A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j -ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxHDAa -BgNVBAMME2xvY2FsaG9zdCAgUm9vdCBDQSAwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQCmnGVApwUBF1kpqcyr2nYeED0VKvefpoHLtxHSP+vP0lWhW7NU -NJlb1YuUagjJ4/rpGRQmPxcVU51n3aAW3a5qHazIpNxNa3fvgB1rMOPFxGmdel2d -8lIt+u19q19DknX/GNgH9Mog8RcyZyPeA7d2icT8TBo74ognr+8p68O3CjBHQ8EM -SnRQR7/bh1c10Uia317ilKvs+I7oErTq5JFLeIuPDdAJ6UncaeblTf1XJ/1FrpHG -fSS7xmR8x0/MblBQlku4eImYmN35g+eRgf8bLDDwC+GPzDnAqqMLjx6h2N+btDxr -tnn05qyqmN9G08uUlP4d4BXi9ISb/toYypklAgMBAAGjUzBRMB0GA1UdDgQWBBS+ -a4X2XTmdPivdQtqDWNpfOtHypDAfBgNVHSMEGDAWgBS+a4X2XTmdPivdQtqDWNpf -OtHypDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZIrGBNdSw -pe+1agefHfaR8hjZQiXBxdwHM1gR2LWOaFzMS8Q/eRETHTO6+VwQ0/FNaXbAqgqk -G317gZMXS5ZmXuOi28fTpAQtuzokkEKpoK0puTnbXOKGA2QSbBlpSFPqb3aJXvVt -afXFQb5P/0mhr4kuVt7Ech82WM/o5ryFgObygDayDmLatTp+VaRmBZPksnSMhslq -3zPyS7bx2YhbPTLkDxq8Mfr/Msxme8LvSXUpFf4PpQ5zwp1RE32gekct6eRQLmqU -5LXY2aPtqpMF0fBpcwPWbqA9gOYCRKcvXXIr+u1x8hf6Er6grZegHkM9TQ8s0hJd -sxi5tK0lPMHJ ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase01/ca.key b/docs/multitenant/ords-based/usecase01/ca.key deleted file mode 100644 index 1a0ef89d..00000000 --- a/docs/multitenant/ords-based/usecase01/ca.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAppxlQKcFARdZKanMq9p2HhA9FSr3n6aBy7cR0j/rz9JVoVuz -VDSZW9WLlGoIyeP66RkUJj8XFVOdZ92gFt2uah2syKTcTWt374AdazDjxcRpnXpd -nfJSLfrtfatfQ5J1/xjYB/TKIPEXMmcj3gO3donE/EwaO+KIJ6/vKevDtwowR0PB -DEp0UEe/24dXNdFImt9e4pSr7PiO6BK06uSRS3iLjw3QCelJ3Gnm5U39Vyf9Ra6R -xn0ku8ZkfMdPzG5QUJZLuHiJmJjd+YPnkYH/Gyww8Avhj8w5wKqjC48eodjfm7Q8 -a7Z59OasqpjfRtPLlJT+HeAV4vSEm/7aGMqZJQIDAQABAoIBAGXRGYdjCgnarOBr -Jeq3vIsuvUVcVqs35AYMQFXOPltoXHAZTAPfiQC4BW6TRf+q1MDyVH/y+jZMPNsm -cxjGLDopHFgZd4/QZyDzmAbTf75yA2D7UI6fcV0sBUpRGgx/SqC0HADwtT1gWB6z -LRYWC13jX4AXOcjy7OXj/DIQJDCMivedt3dv0rDWJUcBCnVot5tr6zjycefxGKa8 -mG9LZQb3x71FxwpFUau3WLDSwOjtXCeMytaGXnGmIiofJmXnFi0KA4ApzKL7QV6I -cCBS1WBLLXeVM9vOfrtzKVLWGe0qADyLm35p5Fnl3j+vimkk8h/2DEvCZ75c987m -O3PEgdkCgYEA0Scg+KINTA78sdZL5v2+8fT4b+EfoCgUqfr10ReUPKrz3HfrVHcj -7Vf00RT52TkfmkL3mIdLyBUzQ9vzPgweo1o4yKCKNCpR9G3ydNW+KI5jSYnq2efz -Gpe3wTt+8YoyCgm9eUxNWjfO9fipS91sSotY0PovkBohj9aezfcWp1sCgYEAy+3n -MIvW/9PoYxCvQ9fDGLvx3B4/uy0ZYPh7j5edDuaRzwFd2YXUysXhJVuqTp0KT2tv -dRPFRE9Oq5N8e5ITIUiKLQ5PIRNBZm8CiAof+XS1fIuU+MTDaTfXwyGQo0xSg8MB -ITnJulmUlkcTWEtGyBi9sIjor5ve8kqvyrdAKX8CgYA9ZUUSd0978jJPad6iEf6J -PCXpgaYs91cJhre+BzPmkzA+mZ0lEEwlkdo1vfiRwWj7eYkA50Zhl4eS9e/zWM9t -mEBu9GFdasbf/55amZvWf+W5YpjkGmiMd9jjCjn7YVvLAozyHGngf91q6vGXaYou -X7VUsvxfSqxrcs7vGwc1XQKBgB0qaD80MMqj5v+MGlTsndWCw8OEe/7sI04QG7Pc -rjS8Wyws+NwsXNOnW1z5cDEQGrJjHiyzaCot4YV+cXZG3P+MnV52RnDnjRn2VHla -YVpPC8nFOMgfdAcvWmdo/IOuXbrEf/vdhPFm8G5Ruf2NvpDNoQuHeSfsdgVXEy89 -6CpHAoGBAMZInYD0XjcnZNqiQnQdcIJN3CqDIU76Z45OOpcUrYrvTos2xhGLrRI5 -qrk5Od/sovJfse+oUIIbgsABieqtyfxM03iu8fvbahIY6Un1iw2KN9t+mcPrSZJK -jTXKf7XxZ1+yN9kvohdLc65ySyXFSm++glDq8WGrmnOtLUlr0oMm ------END RSA PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase01/ca.srl b/docs/multitenant/ords-based/usecase01/ca.srl deleted file mode 100644 index 7c9868bb..00000000 --- a/docs/multitenant/ords-based/usecase01/ca.srl +++ /dev/null @@ -1 +0,0 @@ -77D97AB4C4B6D5A9377B84B455D3E16348C6DE04 diff --git a/docs/multitenant/ords-based/usecase01/cdb_create.yaml b/docs/multitenant/ords-based/usecase01/cdb_create.yaml deleted file mode 100644 index 1e14c5d4..00000000 --- a/docs/multitenant/ords-based/usecase01/cdb_create.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - diff --git a/docs/multitenant/ords-based/usecase01/cdb_secret.yaml b/docs/multitenant/ords-based/usecase01/cdb_secret.yaml deleted file mode 100644 index 567b90a4..00000000 --- a/docs/multitenant/ords-based/usecase01/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - cdbadmin_user: ".....base64 encoded password...." - cdbadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." diff --git a/docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml new file mode 100644 index 00000000..3cc2c3dd --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml @@ -0,0 +1,50 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb3 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "new_clone" + srcPdbName: "pdbdev" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + action: "Clone" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml new file mode 100644 index 00000000..28a4eab6 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml @@ -0,0 +1,50 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb4 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "new_clone2" + srcPdbName: "pdbprd" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + action: "Clone" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/pdb_close.yaml b/docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml similarity index 68% rename from docs/multitenant/ords-based/usecase01/pdb_close.yaml rename to docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml index 5bca125a..a5c3cf59 100644 --- a/docs/multitenant/ords-based/usecase01/pdb_close.yaml +++ b/docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml @@ -10,14 +10,17 @@ spec: cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -32,13 +35,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml new file mode 100644 index 00000000..7fa15111 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbprd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml new file mode 100644 index 00000000..fa7cf009 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb3 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: ""new_clone" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/create_ords_pod.yaml b/docs/multitenant/ords-based/usecase01/create_ords_pod.yaml new file mode 100644 index 00000000..e39c4c56 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/create_ords_pod.yaml @@ -0,0 +1,48 @@ +apiVersion: database.oracle.com/v4 +kind: CDB +metadata: + name: cdb-dev + namespace: oracle-database-operator-system +spec: + cdbName: "DB12" + ordsImage: _your_container_registry/ords-dboper:latest + ordsImagePullPolicy: "Always" + dbTnsurl : "T H I S I S J U S T A N E X A M P L E ....(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + replicas: 1 + deletePdbCascade: true + sysAdminPwd: + secret: + secretName: "syspwd" + key: "e_syspwd.txt" + ordsPwd: + secret: + secretName: "ordpwd" + key: "e_ordpwd.txt" + cdbAdminUser: + secret: + secretName: "cdbusr" + key: "e_cdbusr.txt" + cdbAdminPwd: + secret: + secretName: "cdbpwd" + key: "e_cdbpwd.txt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + cdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/pdb_create.yaml b/docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml similarity index 65% rename from docs/multitenant/ords-based/usecase01/pdb_create.yaml rename to docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml index f6419e2e..044d466b 100644 --- a/docs/multitenant/ords-based/usecase01/pdb_create.yaml +++ b/docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml @@ -10,14 +10,21 @@ spec: cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" + assertivePdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -32,16 +39,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - assertivePdbDeletion: true - + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml new file mode 100644 index 00000000..eb36aaa2 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml @@ -0,0 +1,51 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbprd" + assertivePdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/pdb_delete.yaml b/docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml similarity index 62% rename from docs/multitenant/ords-based/usecase01/pdb_delete.yaml rename to docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml index aa10e5b4..b0816929 100644 --- a/docs/multitenant/ords-based/usecase01/pdb_delete.yaml +++ b/docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml @@ -11,6 +11,14 @@ spec: pdbName: "pdbdev" action: "Delete" dropAction: "INCLUDING" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -25,10 +33,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml new file mode 100644 index 00000000..d2ad95cc --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml @@ -0,0 +1,45 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + pdbName: "pdbprd" + action: "Delete" + dropAction: "INCLUDING" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/extfile.txt b/docs/multitenant/ords-based/usecase01/extfile.txt deleted file mode 100644 index c51d22a3..00000000 --- a/docs/multitenant/ords-based/usecase01/extfile.txt +++ /dev/null @@ -1 +0,0 @@ -subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com diff --git a/docs/multitenant/ords-based/usecase01/makefile b/docs/multitenant/ords-based/usecase01/makefile index cf8b8470..ec454e28 100644 --- a/docs/multitenant/ords-based/usecase01/makefile +++ b/docs/multitenant/ords-based/usecase01/makefile @@ -1,255 +1,766 @@ +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # __ __ _ __ _ _ # | \/ | __ _| | _____ / _(_) | ___ # | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ # | | | | (_| | < __/ _| | | __/ # |_| |_|\__,_|_|\_\___|_| |_|_|\___| +# | | | | ___| |_ __ ___ _ __ +# | |_| |/ _ \ | '_ \ / _ \ '__| +# | _ | __/ | |_) | __/ | +# |_| |_|\___|_| .__/ \___|_| +# |_| # -# ___ -# / _ \ _ __ _ __ _ __ ___ _ __ ___ -# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ -# | |_| | | | | |_) | | | __/ | | | | | -# \___/|_| |_| .__/|_| \___|_| |_| |_| -# |_| -# ____ _ _ _ -# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ -# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| -# | |__| (_) | | | | |_| | | (_) | | | __/ | -# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| +# WARNING: Using this makefile helps you to customize yaml +# files. Edit parameters.txt with your enviroment +# informartion and execute the following steps # -# -# This makefile helps to speed up the kubectl commands executions to deploy and test -# the OnPremises operator. Although it has few functionality you can adapt to your needs -# by adding much more targets. +# 1) make operator +# it configures the operator yaml files with the +# watch namelist required by the multitenant controllers +# +# 2) make genyaml +# It automatically creates all the yaml files based on the +# information available in the parameters file # -# Quick start: -# ~~~~~~~~~~~ +# 3) make secrets +# It configure the required secrets necessary to operate +# with pdbs multitenant controllers # -# - Copy files of tab.1 in the makefile directory. -# - Edit the secret files and other yaml files with the correct credential as -# specified in the documentation. -# - Edit makefile updating variables of tab.2 -# - Execute commands of tab.3 "make step1" "make step2" "make step3".... +# 4) make runall01 +# Start a series of operation create open close delete and so on +# +# LIST OF GENERAED YAML FILE # -# Tab.1 - List of required files -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Opertaor yaml file | -# +-----------------------------+---------------------------------------------+ -# |cdb_secret.yaml | Secret file for the rest server pod | -# +-----------------------------+---------------------------------------------+ -# |pdb_secret.yaml | Secret file for the pdb creation | -# +-----------------------------+---------------------------------------------+ -# |tde_secret.yaml | Secret file for the tablepsace enc. | -# +-----------------------------+---------------------------------------------+ -# |cdb_create.yaml | Rest server pod creation | -# +-----------------------------+---------------------------------------------+ -# |pdb_create.yaml | Pluggable database creation | -# +-----------------------------+---------------------------------------------+ -# |pdb_close.yaml | Close pluggable database | -# +-----------------------------+---------------------------------------------+ -# |pdb_open.yaml | Open pluggable database | -# +-----------------------------+---------------------------------------------+ -# |pdb_map.yaml | Map an existing pdb | -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Database operator | -# +-----------------------------+---------------------------------------------+ -# |Dockerfiles | Dockerfile for CBD | -# +-----------------------------+---------------------------------------------+ -# |runOrdsSSL.sh | Init script executed by Dockerfile | -# +-----------------------------+---------------------------------------------+ +# ----------------------------- ---------------------------------- +# oracle-database-operator.yaml : oracle database operator +# cdbnamespace_binding.yaml : role binding for cdbnamespace +# pdbnamespace_binding.yaml : role binding for pdbnamespace +# create_cdb_secret.yaml : create secrets for ords server pod +# create_pdb_secret.yaml : create secrets for pluggable database +# create_ords_pod.yaml : create rest server pod +# create_pdb1_resource.yaml : create first pluggable database +# create_pdb2_resource.yaml : create second pluggable database +# open_pdb1_resource.yaml : open first pluggable database +# open_pdb2_resource.yaml : open second pluggable database +# close_pdb1_resource.yaml : close first pluggable database +# close_pdb2_resource.yaml : close second pluggable database +# clone_pdb_resource.yaml : clone thrid pluggable database +# clone_pdb2_resource.yaml : clone 4th pluggable database +# delete_pdb1_resource.yaml : delete first pluggable database +# delete_pdb2_resource.yaml : delete sencond pluggable database +# delete_pdb3_resource.yaml : delete thrid pluggable database +# unplug_pdb1_resource.yaml : unplug first pluggable database +# plug_pdb1_resource.yaml : plug first pluggable database +# map_pdb1_resource.yaml : map the first pluggable database +# config_map.yam : pdb parameters array # -# Tab.2 - List of variables -# ~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |OCIR | Your image registry | -# +-----------------------------+---------------------------------------------+ -# |OCIRPATH | Path of the image in your registry | -# +-----------------------------+---------------------------------------------+ -# -# Tab.3 - Execution steps -# ~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# | MAKEFILE TARGETS LIST | -# | ----- ooo ----- | -# | - TARGET - - DESCRIPTION - | -# +-----------------------------+-------------------------------------+-------+ -# |step1 | Build rest server images | | -# +-----------------------------+-------------------------------------+ REST | -# |step2 | Tag the immages | SRV | -# +-----------------------------+-------------------------------------+ IMG | -# |step3 | Push the image into the repository | | -# +-----------------------------+-------------------------------------+-------+ -# |step4 | Load webhook certmanager | DB | -# +-----------------------------+-------------------------------------+ OPER | -# |step5 | Create the db operator | | -# +-----------------------------+-------------------------------------+-------+ -# |step6 | Create tls certificates | T | -# +-----------------------------+-------------------------------------+ L | -# |step7 | Create tls secret | S | -# +-----------------------------+---------------------------------------------+ -# |step8 | Create database secrets | -# +-----------------------------+---------------------------------------------+ -# |step9 | Create restserver pod | -# | | +---------------------------------------------+ -# | +---> checkstep9 | Monitor the executions | -# +-----------------------------+---------------------------------------------+ -# |step10 | Create pluggable database | -# | | +---------------------------------------------+ -# | +---> checkpdb | Monitor PDB status | -# +-----------------------------+---------------------------------------------+ -# |step11 | Close pluggable database | -# +-----------------------------+---------------------------------------------+ -# |step12 | Open pluggable database | -# +-----------------------------+---------------------------------------------+ -# |step13 | Map pluggable database | -# +-----------------------------+---------------------------------------------+ -# | Before testing step13 delete the crd: | -# | kubectl delete pdb pdb1 -n oracle-database-operator-system | -# +---------------------------------------------------------------------------+ -# |step14 | delete pdb | -# +-----------------------------+---------------------------------------------+ -# | DIAGNOSTIC TARGETS | -# +-----------------------------+---------------------------------------------+ -# | dump | Dump pods info into a file | -# +-----------------------------+---------------------------------------------+ -# | reloadop | Reload the db operator | -# +-----------------------------+---------------------------------------------+ -# | login | Login into cdb pod | -# +-----------------------------+---------------------------------------------+ - - -################ TAB 2 VARIABLES ############ -OCIR=[...........YOUR REGISTRY...........] -OCIRPATH=[...PATH IN YOUR REGISTRY.....]/$(REST_SERVER)-dboper:$(ORDSVERSION) -############################################# +DATE := `date "+%y%m%d%H%M%S"` +###################### +# PARAMETER SECTIONS # +###################### + +export PARAMETERS=parameters.txt +export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) +export ORDPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDPWD|cut -d : -f 2) +export SYSPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep SYSPWD|cut -d : -f 2) +export WBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBUSER|cut -d : -f 2) +export WBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBPASS|cut -d : -f 2) +export PDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBUSR|cut -d : -f 2) +export PDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBPWD|cut -d : -f 2) +export CDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBUSR|cut -d : -f 2) +export CDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBPWD|cut -d : -f 2) +export OPRNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPRNAMESPACE|cut -d : -f 2) +export OPRNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPRNAMESPACE|cut -d : -f 2) +export ORDSIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDSIMG|cut -d : -f 2,3) +export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) +export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) +export OPRNAMESPACE=oracle-database-operator-system +export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml +export TEST_EXEC_TIMEOUT=3m +export IMAGE=oracle/ords-dboper:latest +export ORDSIMGDIR=../../../../ords + REST_SERVER=ords -ORDSVERSION=latest -DOCKER=/usr/bin/docker -KUBECTL=/usr/bin/kubectl -ORDS=/usr/local/bin/ords -CONFIG=/etc/ords/config -IMAGE=oracle/$(REST_SERVER)-dboper:$(ORDSVERSION) -DBOPERATOR=oracle-database-operator.yaml -URLPATH=/_/db-api/stable/database/pdbs/ -OPENSSL=/usr/bin/openssl -ORDSPORT=8888 -MAKE=/usr/bin/make -DOCKERFILE=../../../../ords/Dockerfile -RUNSCRIPT=../../../../ords/runOrdsSSL.sh -ORDSIMGDIR=../../../../ords -RM=/usr/bin/rm -CP=/usr/bin/cp -ECHO=/usr/bin/echo -NAMESPACE=oracle-database-operator-system -CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -CDB_SECRET=cdb_secret.yaml -PDB_SECRET=pdb_secret.yaml -TDE_SECRET=tde_secret.yaml -CDB=cdb_create.yaml -PDB=pdb_create.yaml -PDB_CLOSE=pdb_close.yaml -PDB_OPEN=pdb_open.yaml -PDB_MAP=pdb_map.yaml SKEY=tls.key SCRT=tls.crt CART=ca.crt +PRVKEY=ca.key +PUBKEY=public.pem COMPANY=oracle -LOCALHOST=localhost -RESTPREFIX=cdb-dev - -step1: createimage -step2: tagimage -step3: push -step4: certmanager -step5: dboperator -step6: tlscert -step7: tlssecret -step8: dbsecret -step9: cdb -step10: pdb -step11: close -step12: open -step13: map -step14: delete - -checkstep9: checkcdb +RUNTIME=/usr/bin/podman +################# +### FILE LIST ### +################# -createimage: - $(DOCKER) build -t $(IMAGE) $(ORDSIMGDIR) +export ORDS_POD=create_ords_pod.yaml -createimageproxy: - $(DOCKER) build -t $(IMAGE) $(ORDSIMGDIR) --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) +export CDB_SECRETS=create_cdb_secrets.yaml +export PDB_SECRETS=create_pdb_secrets.yaml -tagimage: - @echo "TAG IMAGE" - $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) +export PDBCRE1=create_pdb1_resource.yaml +export PDBCRE2=create_pdb2_resource.yaml -push: - @echo "PUSH IMAGE INTO THE REGISTRY" - $(DOCKER) push $(OCIR)$(OCIRPATH) +export PDBCLOSE1=close_pdb1_resource.yaml +export PDBCLOSE2=close_pdb2_resource.yaml +export PDBCLOSE3=close_pdb3_resource.yaml + +export PDBOPEN1=open_pdb1_resource.yaml +export PDBOPEN2=open_pdb2_resource.yaml +export PDBOPEN3=open_pdb3_resource.yaml + +export PDBCLONE1=clone_pdb1_resource.yaml +export PDBCLONE2=clone_pdb2_resource.yaml + +export PDBDELETE1=delete_pdb1_resource.yaml +export PDBDELETE2=delete_pdb2_resource.yaml +export PDBDELETE3=delete_pdb3_resource.yaml + +export PDBUNPLUG1=unplug_pdb1_resource.yaml +export PDBPLUG1=plug_pdb1_resource.yaml + +export PDBMAP1=map_pdb1_resource.yaml +export PDBMAP2=map_pdb2_resource.yaml +export PDBMAP3=map_pdb3_resource.yaml + +export PDBMAP1=map_pdb1_resource.yaml +export PDBMAP2=map_pdb2_resource.yaml +export PDBMAP3=map_pdb3_resource.yaml + + +##BINARIES +export KUBECTL=/usr/bin/kubectl +OPENSSL=/usr/bin/openssl +ECHO=/usr/bin/echo +RM=/usr/bin/rm +CP=/usr/bin/cp +TAR=/usr/bin/tar +MKDIR=/usr/bin/mkdir +SED=/usr/bin/sed + +define msg +@printf "\033[31;7m%s\033[0m\r" "......................................]" +@printf "\033[31;7m[\xF0\x9F\x91\x89 %s\033[0m\n" $(1) +endef + +check: + $(call msg,"CHECK PARAMETERS") + @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) + @printf "ORDPWD.................:%s\n" $(ORDPWD) + @printf "SYSPWD.................:%s\n" $(SYSPWD) + @printf "WBUSER.................:%s\n" $(WBUSER) + @printf "WBPASS.................:%s\n" $(WBPASS) + @printf "PDBUSR.................:%s\n" $(PDBUSR) + @printf "PDBPWD.................:%s\n" $(PDBPWD) + @printf "CDBUSR.................:%s\n" $(CDBUSR) + @printf "CDBPWD.................:%s\n" $(CDBPWD) + @printf "OPRNAMESPACE...........:%s\n" $(OPRNAMESPACE) + @printf "COMPANY................:%s\n" $(COMPANY) + @printf "APIVERSION.............:%s\n" $(APIVERSION) + + +tlscrt: + $(call msg,"TLS GENERATION") + #$(OPENSSL) genrsa -out $(PRVKEY) 2048 + $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) + $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) \ + -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj \ + "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(OPRNAMESPACE)" -out server.csr + $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(OPRNAMESPACE)" > extfile.txt + $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) + $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) + +tlssec: + $(call msg,"GENERATE TLS SECRET") + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) + + +delsec: + $(call msg,"CLEAN OLD SECRETS") + $(eval SECRETSP:=$(shell kubectl get secrets -n $(OPRNAMESPACE) -o custom-columns=":metadata.name" --no-headers|grep -v webhook-server-cert) ) + @[ "${SECRETSP}" ] && ( \ + printf "Deleteing secrets in namespace -n $(OPRNAMESPACE)\n") &&\ + ($(KUBECTL) delete secret $(SECRETSP) -n $(OPRNAMESPACE))\ + || ( echo "No screts in namespace $(OPRNAMESPACE)") + + +###### ENCRYPTED SECRETS ###### +export PRVKEY=ca.key +export PUBKEY=public.pem +WBUSERFILE=wbuser.txt +WBPASSFILE=wbpass.txt +CDBUSRFILE=cdbusr.txt +CDBPWDFILE=cdbpwd.txt +SYSPWDFILE=syspwd.txt +ORDPWDFILE=ordpwd.txt +PDBUSRFILE=pdbusr.txt +PDBPWDFILE=pdbpwd.txt + + + +secrets: delsec tlscrt tlssec + $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) + $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(OPRNAMESPACE) + @$(ECHO) $(WBUSER) > $(WBUSERFILE) + @$(ECHO) $(WBPASS) > $(WBPASSFILE) + @$(ECHO) $(CDBPWD) > $(CDBPWDFILE) + @$(ECHO) $(CDBUSR) > $(CDBUSRFILE) + @$(ECHO) $(SYSPWD) > $(SYSPWDFILE) + @$(ECHO) $(ORDPWD) > $(ORDPWDFILE) + @$(ECHO) $(PDBUSR) > $(PDBUSRFILE) + @$(ECHO) $(PDBPWD) > $(PDBPWDFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBPWDFILE) |base64 > e_$(CDBPWDFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBUSRFILE) |base64 > e_$(CDBUSRFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE) |base64 > e_$(SYSPWDFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(ORDPWDFILE) |base64 > e_$(ORDPWDFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) + $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic cdbpwd --from-file=e_$(CDBPWDFILE) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic cdbusr --from-file=e_$(CDBUSRFILE) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic syspwd --from-file=e_$(SYSPWDFILE) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic ordpwd --from-file=e_$(ORDPWDFILE) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(OPRNAMESPACE) + $(RM) $(WBUSERFILE) $(WBPASSFILE) $(CDBPWDFILE) $(CDBUSRFILE) $(SYSPWDFILE) $(ORDPWDFILE) $(PDBUSRFILE) $(PDBPWDFILE) + $(RM) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(CDBPWDFILE) e_$(CDBUSRFILE) e_$(SYSPWDFILE) e_$(ORDPWDFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) + + +### YAML FILE SECTION ### +operator: + $(CP) ${ORACLE_OPERATOR_YAML} . + ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG + $(SED) -i 's/value: ""/value: $(OPRNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` + + +define _script00 +cat < authsection01.yaml + sysAdminPwd: + secret: + secretName: "syspwd" + key: "e_syspwd.txt" + ordsPwd: + secret: + secretName: "ordpwd" + key: "e_ordpwd.txt" + cdbAdminUser: + secret: + secretName: "cdbusr" + key: "e_cdbusr.txt" + cdbAdminPwd: + secret: + secretName: "cdbpwd" + key: "e_cdbpwd.txt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + cdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" +EOF + +cat< authsection02.yaml + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" +EOF + + +cat < ${OPRNAMESPACE}_binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 + namespace: ${OPRNAMESPACE} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +EOF + +cat < ${OPRNAMESPACE}_binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 + namespace: ${OPRNAMESPACE} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +EOF + +endef +export script00 = $(value _script00) +secyaml: + @ eval "$$script00" + +#echo ords pod creation +define _script01 +cat < ${ORDS_POD} +apiVersion: database.oracle.com/${APIVERSION} +kind: CDB +metadata: + name: cdb-dev + namespace: oracle-database-operator-system +spec: + cdbName: "DB12" + ordsImage: ${ORDSIMG} + ordsImagePullPolicy: "Always" + dbTnsurl : ${TNSALIAS} + replicas: 1 + deletePdbCascade: true +EOF + +cat authsection01.yaml >> ${ORDS_POD} + +endef +export script01 = $(value _script01) + + +define _script02 + +cat <${PDBCRE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + assertivePdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" +EOF + +cat < ${PDBCRE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + assertivePdbDeletion: true + fileNameConversions: "NONE" + unlimitedStorage: false + tdeImport: false + totalSize: "2G" + tempSize: "800M" + action: "Create" +EOF -certmanager: - @echo "WEBHOOK CERT MANAGER" - $(KUBECTL) apply -f $(CERTMANAGER) +cat <${PDBOPEN1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" +EOF -dboperator: - @echo "ORACLE DATABASE OPERATOR" - $(KUBECTL) apply -f $(DBOPERATOR) +cat <${PDBOPEN2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" +EOF +cat <${PDBOPEN3} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb3 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" +EOF -#C: Country -#ST: State -#L: locality (city) -#O: Organization Name Organization Unit -#CN: Common Name +cat <${PDBCLOSE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" +EOF -tlscert: - @echo "CREATING TLS CERTIFICATES" - $(OPENSSL) genrsa -out ca.key 2048 - $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE) /CN=$(LOCALHOST)" -out server.csr - $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE),DNS:www.example.com" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) +cat <${PDBCLOSE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" +EOF -tlssecret: - @echo "CREATING TLS SECRETS" - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(NAMESPACE) +cat <${PDBCLOSE3} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb3 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: ""new_clone" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" +EOF -dbsecret: - @echo "CREATING DB SECRETS" - $(KUBECTL) apply -f $(CDB_SECRET) -n $(NAMESPACE) - $(KUBECTL) apply -f $(PDB_SECRET) -n $(NAMESPACE) - $(KUBECTL) apply -f $(TDE_SECRET) -n $(NAMESPACE) +cat < ${PDBCLONE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb3 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone" + srcPdbName: "pdbdev" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + action: "Clone" +EOF -cdb: - @echo "CREATING REST SRV POD" - $(KUBECTL) apply -f $(CDB) +cat < ${PDBCLONE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb4 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone2" + srcPdbName: "pdbprd" + fileNameConversions: "NONE" + totalSize: "UNLIMITED" + tempSize: "UNLIMITED" + assertivePdbDeletion: true + action: "Clone" +EOF -checkcdb: - $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(NAMESPACE) -pdb: - $(KUBECTL) apply -f $(PDB) +cat < ${PDBDELETE1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" +EOF -close: - $(KUBECTL) apply -f $(PDB_CLOSE) +cat < ${PDBDELETE2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + pdbName: "pdbprd" + action: "Delete" + dropAction: "INCLUDING" +EOF -open: - $(KUBECTL) apply -f $(PDB_OPEN) +cat < ${PDBUNPLUG1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "Unplug" +EOF -map: - $(KUBECTL) apply -f $(PDB_MAP) +cat <${PDBPLUG1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "plug" + fileNameConversions: "NONE" + sourceFileNameConversions: "NONE" + copyAction: "MOVE" + totalSize: "1G" + tempSize: "100M" + assertivePdbDeletion: true + action: "Plug" +EOF -checkpdb: - $(KUBECTL) get pdbs -n $(NAMESPACE) +cat <${PDBMAP1} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb1 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbdev" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" +EOF + +cat <${PDBMAP2} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb2 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "pdbprd" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" +EOF + + +cat <${PDBMAP3} +apiVersion: database.oracle.com/${APIVERSION} +kind: PDB +metadata: + name: pdb3 + namespace: ${OPRNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${OPRNAMESPACE}" + cdbName: "DB12" + pdbName: "new_clone" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" +EOF + + +## Auth information +for _file in ${PDBCRE1} ${PDBCRE2} ${PDBOPEN1} ${PDBOPEN2} ${PDBOPEN3} ${PDBCLOSE1} ${PDBCLOSE2} ${PDBCLOSE3} ${PDBCLONE1} ${PDBCLONE2} ${PDBDELETE1} ${PDBDELETE2} ${PDBUNPLUG1} ${PDBPLUG1} ${PDBMAP1} ${PDBMAP2} ${PDBMAP3} +do +ls -ltr ${_file} + cat authsection02.yaml >> ${_file} +done +rm authsection02.yaml +rm authsection01.yaml +endef + +export script02 = $(value _script02) + +genyaml: secyaml + @ eval "$$script01" + @ eval "$$script02" + +cleanyaml: + - $(RM) $(PDBMAP3) $(PDBMAP2) $(PDBMAP1) $(PDBPLUG1) $(PDBUNPLUG1) $(PDBDELETE2) $(PDBDELETE1) $(PDBCLONE2) $(PDBCLONE1) $(PDBCLOSE3) $(PDBCLOSE2) $(PDBCLOSE1) $(PDBOPEN3) $(PDBOPEN2) $(PDBOPEN1) $(PDBCRE2) $(PDBCRE1) $(ORDS_POD) $(CDB_SECRETS) $(PDB_SECRETS) + - $(RM) ${OPRNAMESPACE}_binding.yaml ${OPRNAMESPACE}_binding.yaml + + +cleancrt: + - $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl + + +################# +### PACKAGING ### +################# + +pkg: + - $(RM) -rf /tmp/pkgtestplan + $(MKDIR) /tmp/pkgtestplan + $(CP) -R * /tmp/pkgtestplan + $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ + $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan + +################ +### diag ### +################ + +login: + $(KUBECTL) exec `$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep ords|cut -d ' ' -f 1` -n $(OPRNAMESPACE) -it -- /bin/bash + + +reloadop: + echo "RESTARTING OPERATOR" + $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) + $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) + $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) + $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - -delete: - $(KUBECTL) apply -f pdb_delete.yaml dump: @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) @@ -257,31 +768,139 @@ dump: @>$(DIAGFILE) @echo "OPERATOR DUMP" >> $(DIAGFILE) @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) - @echo "CDB LOG DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs `$(KUBECTL) get pods -n $(NAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) - @echo "SECRET DMP" >>$(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) get secrets -o yaml -n $(NAMESPACE) >> $(DIAGFILE) - @echo "CDB/PDB DMP" >> $(DIAGFILE) - $(KUBECTL) get pdbs -o yaml -n $(NAMESPACE) >> $(DIAGFILE) - $(KUBECTL) get cdb -o yaml -n $(NAMESPACE) >> $(DIAGFILE) - @echo "CLUSTER INFO" >> $(DIAGFILE) - $(KUBECTL) get nodes -o wide - $(KUBECTL) get svc --namespace=kube-system + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(NAMESPACE) -o yaml | kubectl replace --force -f - +####################################################### +#### TEST SECTION #### +####################################################### + +run00: + @$(call msg,"cdb pod creation") + - $(KUBECTL) delete cdb cdb-dev -n $(OPRNAMESPACE) + $(KUBECTL) apply -f $(ORDS_POD) + time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" cdb cdb-dev -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"cdb pod completed") + $(KUBECTL) get cdb -n $(OPRNAMESPACE) + $(KUBECTL) get pod -n $(OPRNAMESPACE) + +run01.1: + @$(call msg,"pdb pdb1 creation") + $(KUBECTL) apply -f $(PDBCRE1) + time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb1 creation completed") + $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) + +run01.2: + @$(call msg, "pdb pdb2 creation") + $(KUBECTL) apply -f $(PDBCRE2) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb2 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb2 creation completed") + $(KUBECTL) get pdb pdb2 -n $(OPRNAMESPACE) + +run02.1: + @$(call msg, "pdb pdb1 open") + $(KUBECTL) apply -f $(PDBOPEN1) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb1 open completed") + $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) + +run02.2: + @$(call msg,"pdb pdb2 open") + $(KUBECTL) apply -f $(PDBOPEN2) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb2 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"pdb pdb2 open completed") + $(KUBECTL) get pdb pdb2 -n $(OPRNAMESPACE) + + +run03.1: + @$(call msg,"clone pdb1-->pdb3") + $(KUBECTL) apply -f $(PDBCLONE1) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb3 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"clone pdb1-->pdb3 completed") + $(KUBECTL) get pdb pdb3 -n $(OPRNAMESPACE) + + +run03.2: + @$(call msg,"clone pdb2-->pdb4") + $(KUBECTL) apply -f $(PDBCLONE2) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb4 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"clone pdb2-->pdb4 completed") + $(KUBECTL) get pdb pdb3 -n $(OPRNAMESPACE) + + +run04.1: + @$(call msg,"pdb pdb1 close") + $(KUBECTL) apply -f $(PDBCLOSE1) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb1 close completed") + $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) + +run04.2: + @$(call msg,"pdb pdb2 close") + $(KUBECTL) apply -f $(PDBCLOSE2) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb2 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"pdb pdb2 close completed") + $(KUBECTL) get pdb pdb2 -n $(OPRNAMESPACE) + +run05.1: + @$(call msg,"pdb pdb1 unplug") + $(KUBECTL) apply -f $(PDBUNPLUG1) + $(KUBECTL) wait --for=delete pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"pdb pdb1 unplug completed") + +run06.1: + @$(call msg, "pdb pdb1 plug") + $(KUBECTL) apply -f $(PDBPLUG1) + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "pdb pdb1 plug completed") + $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) + +run07.1: + @$(call msg,"pdb pdb1 delete ") + - $(KUBECTL) apply -f $(PDBCLOSE1) + $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) apply -f $(PDBDELETE1) + $(KUBECTL) wait --for=delete pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"pdb pdb1 delete") + $(KUBECTL) get pdb -n $(OPRNAMESPACE) + +run99.1: + $(KUBECTL) delete cdb cdb-dev -n cdbnamespace + $(KUBECTL) wait --for=delete cdb cdb-dev -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) get cdb -n cdbnamespaace + $(KUBECTL) get pdb -n pdbnamespaace + + +## SEQ | ACTION +## ----+---------------- +## 00 | create ords pod +## 01 | create pdb +## 02 | open pdb +## 03 | clone pdb +## 04 | close pdb +## 05 | unpug pdb +## 06 | plug pdb +## 07 | delete pdb (declarative) + + +runall01: run00 run01.1 run01.2 run03.1 run03.2 run04.1 run05.1 run06.1 run02.1 run07.1 + + +###### BUILD ORDS IMAGE ###### + +createimage: + $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) + +createimageproxy: + $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) + +tagimage: + @echo "TAG IMAGE" + $(RUNTIME) tag $(IMAGE) $(ORDSIMG) + +push: + $(RUNTIME) push $(ORDSIMG) -login: - $(KUBECTL) exec -it `$(KUBECTL) get pods -n $(NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(NAMESPACE) bash diff --git a/docs/multitenant/ords-based/usecase01/pdb_map.yaml b/docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml similarity index 70% rename from docs/multitenant/ords-based/usecase01/pdb_map.yaml rename to docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml index ccda2aa4..18cb35b1 100644 --- a/docs/multitenant/ords-based/usecase01/pdb_map.yaml +++ b/docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml @@ -10,14 +10,19 @@ spec: cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -32,14 +37,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - assertivePdbDeletion: true + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml new file mode 100644 index 00000000..85899597 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml @@ -0,0 +1,49 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbprd" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml new file mode 100644 index 00000000..9c2c1cd3 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml @@ -0,0 +1,49 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb3 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "new_clone" + assertivePdbDeletion: true + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/pdb_open.yaml b/docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml similarity index 68% rename from docs/multitenant/ords-based/usecase01/pdb_open.yaml rename to docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml index ca5d1ab8..63a0a49c 100644 --- a/docs/multitenant/ords-based/usecase01/pdb_open.yaml +++ b/docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml @@ -10,14 +10,17 @@ spec: cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -32,12 +35,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml new file mode 100644 index 00000000..8c4eed0d --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb2 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbprd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml new file mode 100644 index 00000000..5f0e4b77 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb3 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "new_clone" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml b/docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml new file mode 100644 index 00000000..79e44269 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 + namespace: oracle-database-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml deleted file mode 120000 index bbf4775f..00000000 --- a/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/ords-based/usecase01/parameters.txt b/docs/multitenant/ords-based/usecase01/parameters.txt new file mode 100644 index 00000000..0a7b394a --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/parameters.txt @@ -0,0 +1,61 @@ + +######################## +## REST SERVER IMAGE ### +######################## + +ORDSIMG:_your_container_registry/ords-dboper:latest + +############################## +## TNS URL FOR CDB CREATION ## +############################## +TNSALIAS:"T H I S I S J U S T A N E X A M P L E ....(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" + +########################################### +## ORDS PUBLIC USER ## +########################################### +ORDPWD:Change_me_please + +########################################### +## SYSPASSWORD ## +########################################### +SYSPWD:Change_me_please + +####################### +## HTTPS CREDENTIAL ### +####################### + +WBUSER:Change_me_please +WBPASS:Change_me_please + +##################### +## PDB ADMIN USER ### +##################### + +PDBUSR:Change_me_please +PDBPWD:Change_me_please + +##################### +## CDB ADMIN USER ### +##################### + +CDBUSR:C##DBAPI_CDB_ADMIN +CDBPWD:Change_me_please + +################### +### NAMESPACES #### +################### + +PDBNAMESPACE:pdbnamespace +CDBNAMESPACE:cdbnamespace + +#################### +### COMPANY NAME ### +#################### + +COMPANY:oracle + +#################### +### APIVERSION ### +#################### + +APIVERSION:v4 diff --git a/docs/multitenant/ords-based/usecase01/pdb_secret.yaml b/docs/multitenant/ords-based/usecase01/pdb_secret.yaml deleted file mode 100644 index 60d95d76..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_secret.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - sysadmin_user: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." - diff --git a/docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml new file mode 100644 index 00000000..0e86e10c --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml @@ -0,0 +1,53 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "plug" + fileNameConversions: "NONE" + sourceFileNameConversions: "NONE" + copyAction: "MOVE" + totalSize: "1G" + tempSize: "100M" + assertivePdbDeletion: true + action: "Plug" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/server.csr b/docs/multitenant/ords-based/usecase01/server.csr deleted file mode 100644 index e308d301..00000000 --- a/docs/multitenant/ords-based/usecase01/server.csr +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIC3TCCAcUCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh -MRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNV -BAMMLWNkYi1kZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVt -IDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAm9nlNSQNsPTVqH57MkWKZEyaVtzVKQ8Z3oDK6hWXfB24p0jVj6sTOJkf -NVAxnqmU8DpW3odpbU6qWe/n+B5vJpqdXUGdsq9NKyus2fGb/xf1UnskpA2FUuWZ -o3upyCFxDAOvE4eZUzlxIn+54XXaNAdQiU9E8VXPr5YxrvZ15T/xCXLtJPs/RCOF -cJ8+gvZGcjMbdP16auJDVWZzBaur3eKbiHN7LXNCCRzGO++dv0kGY8vH7MyFfgp3 -qYBiSHS3WDiFUJjYIvfa8lLfP1hnlCyHn8TnU9gjGjmd1YcccSKqWIAT24wPUKVU -Lme4n91jxDPp7g8nRtDw0Smj9gYCtQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEB -AGOG/9IJJRvT2JLcuzE5Arai1XHc6Jh65iuDRqXQav47Bz38FFF2gZNO69gzDmhq -6k7tie+5bPcAHuuJZ0dAa71a9SLjKl+XNkkI0vS6te6OK3DCVUoMqNCk5VdwrJw0 -RORbKUwgLEG6mu80Gc/6wCdeR/36hoYTMeNPjm6M9e+X5ppsXqxCNsgDxasJFT82 -FejuJE2sZ6RCradlDToUHNS1dMLoW0WAIISqOmrDvEI6snm9ZZr3Sxo1auEtpI6v -NllBM4AgEghy/2mAtke+By4WHCfXBpxEGv9S7ATqJHYrR5Qa3nwx0eojWW1vmn0/ -aEzslX1tAH6oz2jA6QZ0sNo= ------END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/ords-based/usecase01/tde_secret.yaml b/docs/multitenant/ords-based/usecase01/tde_secret.yaml deleted file mode 100644 index 7cf66c03..00000000 --- a/docs/multitenant/ords-based/usecase01/tde_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: tde1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - tdepassword: "bW1hbHZlenoK" - tdesecret: "bW1hbHZlenoK" - - - - diff --git a/docs/multitenant/ords-based/usecase01/tls.crt b/docs/multitenant/ords-based/usecase01/tls.crt deleted file mode 100644 index 6bf8aef4..00000000 --- a/docs/multitenant/ords-based/usecase01/tls.crt +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEFDCCAvygAwIBAgIUd9l6tMS21ak3e4S0VdPhY0jG3gQwDQYJKoZIhvcNAQEL -BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH -DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k -ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE -AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx -NTMyMzVaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG -A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j -ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxEjAQ -BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AJvZ5TUkDbD01ah+ezJFimRMmlbc1SkPGd6AyuoVl3wduKdI1Y+rEziZHzVQMZ6p -lPA6Vt6HaW1Oqlnv5/gebyaanV1BnbKvTSsrrNnxm/8X9VJ7JKQNhVLlmaN7qcgh -cQwDrxOHmVM5cSJ/ueF12jQHUIlPRPFVz6+WMa72deU/8Qly7ST7P0QjhXCfPoL2 -RnIzG3T9emriQ1VmcwWrq93im4hzey1zQgkcxjvvnb9JBmPLx+zMhX4Kd6mAYkh0 -t1g4hVCY2CL32vJS3z9YZ5Qsh5/E51PYIxo5ndWHHHEiqliAE9uMD1ClVC5nuJ/d -Y8Qz6e4PJ0bQ8NEpo/YGArUCAwEAAaNMMEowSAYDVR0RBEEwP4IsY2RiLWRldi1v -cmRzLm9yYWNsZS1kYXRhYmFzZS1vcGVyYXRvci1zeXN0ZW2CD3d3dy5leGFtcGxl -LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAh7Lsu2ITS6Bc2q/Ef4No5Us0Vo9BWKoL -AlrfQPjsv1erMGsyEEyZ0Cg8l3QrXlscQ1ESvx0BnRGjoqZGE4+PoVZTEYSkokXP -aAr69epPzXQRyyAGCg5GeL6IFAj1AzqJGNnKOrPaLpcTri4MboiWmW+MHmgLdyPK -iwl8bNa8841nK/L/m6QET15BI+MIAvn7pgcpztum5jmkB+eceXzXnKUGg77TaFiX -bXqVBR4EvexC4DgUfQJI4zJLFdcH/GHxCpaaXNjbXeVz1ZK/qo2TCrXp2UXVrznU -9VTUuCaQA2VYZCitvAbupt+1OvMFYhWiIAroJSmzrvH4oK+IXgY6GA== ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase01/tls.key b/docs/multitenant/ords-based/usecase01/tls.key deleted file mode 100644 index 666c5639..00000000 --- a/docs/multitenant/ords-based/usecase01/tls.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb2eU1JA2w9NWo -fnsyRYpkTJpW3NUpDxnegMrqFZd8HbinSNWPqxM4mR81UDGeqZTwOlbeh2ltTqpZ -7+f4Hm8mmp1dQZ2yr00rK6zZ8Zv/F/VSeySkDYVS5Zmje6nIIXEMA68Th5lTOXEi -f7nhddo0B1CJT0TxVc+vljGu9nXlP/EJcu0k+z9EI4Vwnz6C9kZyMxt0/Xpq4kNV -ZnMFq6vd4puIc3stc0IJHMY7752/SQZjy8fszIV+CnepgGJIdLdYOIVQmNgi99ry -Ut8/WGeULIefxOdT2CMaOZ3VhxxxIqpYgBPbjA9QpVQuZ7if3WPEM+nuDydG0PDR -KaP2BgK1AgMBAAECggEAKUwl1l0FW7yk2Q8a6glPUKCTzSybN1QPEMyj+D9ccsEV -aw57uKQmZbr9cA0d+OMK2lU7K6BKKXLM5SQTHcZCwcH6rPl0JiMZmbTrCp1hLslU -clS7MtV6XKsGeTGNncBuyjY3sD8gO9NezTt3L+0gsuS1TI06wZBxhh+QbsJUHzjW -bC3mNjD4SqXree4Snp05nlFaT2s2isIjj25mKDwBu8IX0BN2VjsaSiQcjb8Dmzmu -42Xh7bcWBebns8Ehuq9TIl6ZjQht+pmVOMlB862baVpW/9CxkknzM+UQhIkXTSJk -Jt/mGeO89V4/Zh2N4ixIOE1hw87EvRFBoYh2VF58QQKBgQDMujXYblh+eEdsB1LG -kY0LerFHuQgdzifYmjPl0jtBsWDmh5i6q9PRUs2JZ/Fsq4QMQ8SLinGzaIBq5FKr -CL067X5blrFA9H0D6exJI3iHBTQpeMFwtqvu3j+zpCmgzonaUDQrczUpc0hxU7YI -/jhDe9LSWknPrzzMoWWKuy0sTQKBgQDC4g8F2krqm9Q5ug8bRKTAvMrY0skFIwrP -5LXBq9C8YCnLnT4S4tYQfbnWaBeG7YpkkmkZe30c9MUjsr1OHZbo+jlxHBU+oRYZ -e1j0UorVGt7FfNe/zjW0fLd72CBO741EDvV6pVeItkAwH6P5/cbRu085dwvyFbxv -JmOaYddECQKBgQCuid6YG1NE10SE3CV89uAZtktny18ZEgY0ixrNx5MPaaskPtw9 -4Xofjol+qOhR7lQQpMHu+WQAQYqiFvBHspapo4pDiVCrAQWIDamNnTkHW69h3/qD -HqmsZzxF6iI3X351akVf+cOMCCXtwCGEvz+2gN12ytT8w/iAuOS6BuP3TQKBgBlf -v57+diSn13EQtajSPjVOH4ctorjFgEHjQHsP+OSeDLMTLSLeYArTo9+zu+R4hz1j -BsYnmvmrMQPd4OIL3jtFYTdF9coqxSraMZHWMXdfwUOrZpf1rG5skqNQV5yPejAz -Vmj6oDQPrrnVVM9W6I0kO0N7KZYCmH9MW0mdlZ6pAoGAB60f2sk35VUBpvh7qzTY -70WDbNnCCU3I3KZ7LCUwUPWzGLQwMXRlAb5ZMheT/SGPChX4QXCNUCjXkR3Am3NO -yURHqZIRy0bwZRVjYnlCtc9YQ8pB0isZ1z2a9FXRD75o2WboFZ+VsG0FU81IE2ZO -gW802gT76NRnz851B7/nFNs= ------END PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml new file mode 100644 index 00000000..61fe915d --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml @@ -0,0 +1,46 @@ +apiVersion: database.oracle.com/v4 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + xmlFileName: "/tmp/pdb.xml" + action: "Unplug" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/README.md b/docs/multitenant/ords-based/usecase02/README.md index c434271f..39978747 100644 --- a/docs/multitenant/ords-based/usecase02/README.md +++ b/docs/multitenant/ords-based/usecase02/README.md @@ -13,37 +13,7 @@ > ☞ The examples of this folder are based on single namespace **oracle-database-operator-system** -This page explains how to plug and unplug database a pdb; it assumes that you have already configured a pluggable database (see [usecase01](../usecase01/README.md)) -The following table reports the parameters required to configure and use oracle multi tenant controller for pluggable database lifecycle management. - -| yaml file parameters | value | description /ords parameter | -|-------------- |--------------------------- |-------------------------------------------------| -| dbserver | or | [--db-hostname][1] | -| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | -| port | | [--db-port][2] | -| cdbName | | Container Name | -| name | | Ords podname prefix in cdb.yaml | -| name | | pdb resource in pdb.yaml | -| ordsImage | /ords-dboper:latest|My public container registry | -| pdbName | | Pluggable database name | -| servicename | | [--db-servicename][3] | -| sysadmin_user | | [--admin-user][adminuser] | -| sysadmin_pwd | | [--password-stdin][pwdstdin] | -| cdbadmin_user | | [db.cdb.adminUser][1] | -| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | -| webserver_user| | [https user][http] NOT A DB USER | -| webserver_pwd | | [http user password][http] | -| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | -| pdbTlsKey | | [standalone.https.cert.key][key] | -| pdbTlsCrt | | [standalone.https.cert][cr] | -| pdbTlsCat | | certificate authority | -| xmlFileName | | path for the unplug and plug operation | -| srcPdbName | | name of the database to be cloned | -| fileNameConversions | | used for database cloning | -| tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | -| tdeExport | | [tdeExport] | -| tdeSecret | | [tdeSecret][tdeSecret] | -| tdePassword | | [tdeSecret][tdeSecret] | +This page explains how to plug and unplug database a pdb; it assumes that you have already configured a pluggable database (see [usecase01](../usecase01/README.md)). Check yaml parameters in the CRD tables in the main [README](../README.md) file. ```text @@ -127,27 +97,7 @@ spec: pdbName: "pdbdev" xmlFileName: "/tmp/pdbunplug.xml" action: "Unplug" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - + [ secret sections ] ``` Close the pluggable database by applying the following yaml file **pdb_close.yaml** @@ -169,29 +119,10 @@ spec: cdbNamespace: "oracle-database-operator-system" cdbName: "DB12" pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" pdbState: "CLOSE" modifyOption: "IMMEDIATE" action: "Modify" + [secret section] ``` ```bash @@ -294,19 +225,7 @@ spec: totalSize: "1G" tempSize: "100M" action: "Plug" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - + [secrets section] ``` Apply **pdb_plug.yaml** @@ -390,27 +309,8 @@ spec: fileNameConversions: "NONE" totalSize: "UNLIMITED" tempSize: "UNLIMITED" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" action: "Clone" + [secret section] ``` ```bash @@ -487,7 +387,7 @@ PDBDEV(3):Buffer Cache flush finished: 3
-You can use unplug and plug database with TDE; in order to do that you have to specify a key store path and create new kubernets secret for TDE using the following yaml file. **tde_secrete.yaml**. The procedure to unplug and plug database does not change apply the same file. +You can use unplug and plug database with TDE; in order to do that you have to specify a key store path and create new kubernets secret for TDE using the following yaml file. **tde_secrete.yaml**. ```yaml #tde_secret @@ -498,8 +398,8 @@ metadata: namespace: oracle-database-operator-system type: Opaque data: - tdepassword: "d2VsY29tZTEK" - tdesecret: "bW1hbHZlenoK" + tdepassword: "...." + tdesecret: "...." ``` ```bash @@ -525,7 +425,7 @@ spec: pdbName: "pdbdev" adminName: secret: - secretName: pdb1-secret + secretName: key: "sysadmin_user" adminPwd: secret: @@ -621,29 +521,3 @@ spec: -[1]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation - -[2]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation - -[3]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-DAA027FA-A4A6-43E1-B8DD-C92B330C2341:~:text=%2D%2Ddb%2Dservicename%20%3Cstring%3E - -[adminuser]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22:~:text=Table%202%2D6%20Command%20Options%20for%20Uninstall%20CLI - -[public_user]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/using-multitenant-architecture-oracle-rest-data-services.html#GUID-E64A141A-A71F-4979-8D33-C5F8496D3C19:~:text=Preinstallation%20Tasks%20for%20Oracle%20REST%20Data%20Services%20CDB%20Installation - -[key]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=standalone.https.cert.key - -[cr]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0 - -[cdbadminpwd]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=Table%20C%2D1%20Oracle%20REST%20Data%20Services%20Configuration%20Settings - - -[pwdstdin]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-88479C84-CAC1-4133-A33E-7995A645EC05:~:text=default%20database%20pool.-,2.1.4.1%20Understanding%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation,-Table%202%2D2 - -[http]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-BEECC057-A8F5-4EAB-B88E-9828C2809CD8:~:text=Example%3A%20delete%20%5B%2D%2Dglobal%5D-,user%20add,-Add%20a%20user - -[dbtnsurl]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22 - -[tdeKeystorePath]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/21.4/orrst/op-database-pdbs-pdb_name-post.html - -[tdeSecret]:https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ADMINISTER-KEY-MANAGEMENT.html#GUID-E5B2746F-19DC-4E94-83EC-A6A5C84A3EA9 diff --git a/docs/multitenant/ords-based/usecase02/pdb_clone.yaml b/docs/multitenant/ords-based/usecase02/pdb_clone.yaml index 1dba02e3..5723f7c6 100644 --- a/docs/multitenant/ords-based/usecase02/pdb_clone.yaml +++ b/docs/multitenant/ords-based/usecase02/pdb_clone.yaml @@ -1,32 +1,29 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# apiVersion: database.oracle.com/v4 kind: PDB metadata: - name: pdb2 - namespace: oracle-database-operator-system + name: pdb3 + namespace: pdbnamespace labels: cdb: cdb-dev spec: cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" + cdbNamespace: "cdbnamespace" cdbName: "DB12" - pdbName: "pdb2_clone" + pdbName: "new_clone" srcPdbName: "pdbdev" fileNameConversions: "NONE" totalSize: "UNLIMITED" tempSize: "UNLIMITED" assertivePdbDeletion: true + action: "Clone" adminName: secret: - secretName: "pdb1-secret" - key: "sysadmin_user" + secretName: "pdbusr" + key: "e_pdbusr.txt" adminPwd: secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -41,10 +38,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - action: "Clone" + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/pdb_plug.yaml b/docs/multitenant/ords-based/usecase02/pdb_plug.yaml index 9563c58a..9eb5ed77 100644 --- a/docs/multitenant/ords-based/usecase02/pdb_plug.yaml +++ b/docs/multitenant/ords-based/usecase02/pdb_plug.yaml @@ -1,27 +1,32 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 - namespace: oracle-database-operator-system + namespace: pdbnamespace labels: cdb: cdb-dev spec: cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" + cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbdev" xmlFileName: "/tmp/pdb.xml" + action: "plug" fileNameConversions: "NONE" sourceFileNameConversions: "NONE" copyAction: "MOVE" totalSize: "1G" tempSize: "100M" - action: "Plug" assertivePdbDeletion: true + action: "Plug" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -36,11 +41,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - - + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml b/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml index b56a12a3..0036d5f7 100644 --- a/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml +++ b/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml @@ -1,21 +1,25 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# apiVersion: database.oracle.com/v4 kind: PDB metadata: name: pdb1 - namespace: oracle-database-operator-system + namespace: pdbnamespace labels: cdb: cdb-dev spec: cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" + cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbdev" xmlFileName: "/tmp/pdb.xml" action: "Unplug" + adminName: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminPwd: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" pdbTlsKey: secret: secretName: "db-tls" @@ -30,10 +34,13 @@ spec: key: "ca.crt" webServerUser: secret: - secretName: "pdb1-secret" - key: "webserver_user" + secretName: "wbuser" + key: "e_wbuser.txt" webServerPwd: secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - + secretName: "wbpass" + key: "e_wbpass.txt" + pdbOrdsPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/tde_secret.yaml b/docs/multitenant/ords-based/usecase02/tde_secret.yaml deleted file mode 100644 index d0186ff2..00000000 --- a/docs/multitenant/ords-based/usecase02/tde_secret.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# - -apiVersion: v1 -kind: Secret -metadata: - name: tde1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - tdepassword: "[base64 encode value]" - tdesecret: "[base64 encode value]" - diff --git a/docs/multitenant/ords-based/usecase03/Dockerfile b/docs/multitenant/ords-based/usecase03/Dockerfile deleted file mode 100644 index 772a7e6d..00000000 --- a/docs/multitenant/ords-based/usecase03/Dockerfile +++ /dev/null @@ -1,80 +0,0 @@ -## Copyright (c) 2022 Oracle and/or its affiliates. -## -## The Universal Permissive License (UPL), Version 1.0 -## -## Subject to the condition set forth below, permission is hereby granted to any -## person obtaining a copy of this software, associated documentation and/or data -## (collectively the "Software"), free of charge and under any and all copyright -## rights in the Software, and any and all patent rights owned or freely -## licensable by each licensor hereunder covering either (i) the unmodified -## Software as contributed to or provided by such licensor, or (ii) the Larger -## Works (as defined below), to deal in both -## -## (a) the Software, and -## (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -## one is included with the Software (each a "Larger Work" to which the Software -## is contributed by such licensors), -## -## without restriction, including without limitation the rights to copy, create -## derivative works of, display, perform, and distribute the Software and make, -## use, sell, offer for sale, import, export, have made, and have sold the -## Software and the Larger Work(s), and to sublicense the foregoing rights on -## either these or other terms. -## -## This license is subject to the following condition: -## The above copyright notice and either this complete permission notice or at -## a minimum a reference to the UPL must be included in all copies or -## substantial portions of the Software. -## -## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -## SOFTWARE. - -FROM container-registry.oracle.com/java/jdk:latest - -# Environment variables required for this build (do NOT change) -# ------------------------------------------------------------- -ENV ORDS_HOME=/opt/oracle/ords/ \ - RUN_FILE="runOrdsSSL.sh" \ - ORDSVERSION=23.4.0-8 - -# Copy binaries -# ------------- -COPY $RUN_FILE $ORDS_HOME - -RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && \ - yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && \ - yum -y install java-11-openjdk-devel && \ - yum -y install iproute && \ - yum clean all - -RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm - -RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm - -# Setup filesystem and oracle user -# -------------------------------- -RUN mkdir -p $ORDS_HOME/doc_root && \ - mkdir -p $ORDS_HOME/error && \ - mkdir -p $ORDS_HOME/secrets && \ - chmod ug+x $ORDS_HOME/*.sh && \ - groupadd -g 54322 dba && \ - usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && \ - chown -R oracle:dba $ORDS_HOME && \ - echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - -# Finalize setup -# ------------------- -USER oracle -WORKDIR /home/oracle - -VOLUME ["$ORDS_HOME/config/ords"] -EXPOSE 8888 - -# Define default command to start Ords Services -CMD $ORDS_HOME/$RUN_FILE - diff --git a/docs/multitenant/ords-based/usecase03/NamespaceSegregation.png b/docs/multitenant/ords-based/usecase03/NamespaceSegregation.png deleted file mode 100644 index bcb0ae77818e87ed64bf248bc0c173a4aac28c73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 270813 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≪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 S*V+lde;zJA&et8oss9JAh>QII diff --git a/docs/multitenant/ords-based/usecase03/README.md b/docs/multitenant/ords-based/usecase03/README.md deleted file mode 100644 index c06368cd..00000000 --- a/docs/multitenant/ords-based/usecase03/README.md +++ /dev/null @@ -1,268 +0,0 @@ - - - -# STEP BY STEP (NAMESPACE SEGREGATION) - -- [STEP BY STEP (NAMESPACE SEGREGATION)](#step-by-step-namespace-segregation) - - [INTRODUCTION](#introduction) - - [GIT CLONE ORACLE DATABASE OPERATOR PROJECT](#git-clone-oracle-database-operator-project) - - [NAMESPACE CREATION](#namespace-creation) - - [WEBHOOK CERTIFICATES](#webhook-certificates) - - [ORACLE DATABASE OPERATOR](#oracle-database-operator) - - [CREATE PDB AND CDB SECRETS](#create-pdb-and-cdb-secrets) - - [CREATE TLS CERTIFICATE](#create-tls-certificate) - - [REST SERVER IMAGE CREATION](#rest-server-image-creation) - - [CDB POD CREATION](#cdb-pod-creation) - - [PDB CREATION](#pdb-creation) - - [MAKEFILE](#makefile) - - -### INTRODUCTION - -> ☞ This folder contains the yaml files required to configure and manage cdb and pdb in different namespaces. The main change here is the possibility to specify the namespace where CDB will be created, this implies the introduction of new parameter at PDB level in order to specify the CDB namespace. - -Tasks performed in the usecase03 are the same ones of the other usecase01 with the exception that controller pods cdb pods and pdb crd are running in different namespaces. You must be aware of the fact that secrets must be created in the proper namespaces; cdb secrets go into cdb namespace , pdb secrets go into pdbnamespace while certificate secrets need to be created in every namespace. - - -| yaml file parameters | value | description /ords parameter | -|-------------- |--------------------------- |-------------------------------------------------| -| ☞ cdbNamespace | | Cdb namespace | -| dbserver | or | [--db-hostname][1] | -| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | -| port | | [--db-port][2] | -| cdbName | | Container Name | -| name | | Ords podname prefix in cdb.yaml | -| name | | pdb resource in pdb.yaml | -| ordsImage | /ords-dboper:latest|My public container registry | -| pdbName | | Pluggable database name | -| servicename | | [--db-servicename][3] | -| sysadmin_user | | [--admin-user][adminuser] | -| sysadmin_pwd | | [--password-stdin][pwdstdin] | -| cdbadmin_user | | [db.cdb.adminUser][1] | -| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | -| webserver_user| | [https user][http] NOT A DB USER | -| webserver_pwd | | [http user password][http] | -| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | -| pdbTlsKey | | [standalone.https.cert.key][key] | -| pdbTlsCrt | | [standalone.https.cert][cr] | -| pdbTlsCat | | certificate authority | -| xmlFileName | | path for the unplug and plug operation | -| srcPdbName | | name of the database to be cloned | -| fileNameConversions | | used for database cloning | -| tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | -| tdeExport | | [tdeExport] | -| tdeSecret | | [tdeSecret][tdeSecret] | -| tdePassword | | [tdeSecret][tdeSecret] | -| assertivePdbDeletion | boolean | [turn on imperative approach on crd deleteion][imperative] | - -![generla schema](./NamespaceSegregation.png) - -### GIT CLONE ORACLE DATABASE OPERATOR PROJECT - -```bash -git clone https://github.com/oracle/oracle-database-operator.git -cd oracle-database-operator/docs/multitenant/usecase03 -``` -### NAMESPACE CREATION - -We need first to create two different namespaces (**cdbnamespace**,**pdbnamespace**) using ns_pdb_namespace.yaml and ns_cdb_namespace.yaml - -```bash -kubectl apply -f ns_pdb_namespace.yaml -kubectl apply -f ns_cdb_namespace.yaml -``` - -### WEBHOOK CERTIFICATES -Create cert manager and verify the status - -```bash -kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -``` - -```bash -kubectl get pods --namespace cert-manager -NAME READY STATUS RESTARTS AGE -cert-manager-75997f4b44-4nf5c 1/1 Running 1 9d -cert-manager-cainjector-769785cd7b-mzfq5 1/1 Running 1 9d -cert-manager-webhook-6bc9944d78-tslrp 1/1 Running 1 9d -``` - -### ORACLE DATABASE OPERATOR - -Create the oracle database operator using oracle-database-operator.yaml -```bash -cd oracle-database-operator -kubectl apply -f oracle-database-operator.yaml -cd - -``` - -[operator creation log](operator_creation_log.txt) -### CREATE PDB AND CDB SECRETS - -Update secrets files with your base64 encodede password. - -```bash -echo ImAdemoPassword | base64 -SW1BZGVtb1Bhc3N3b3JkCg== -``` -Apply the cdb_secret and pdb_secret yaml file to generate credential information in each namespace. - -``` -kubectl apply -f cdb_secret.yaml -kubectl apply -f pdb_secret.yaml -``` -> ☞ Note that https credential needs to be replicated in any secret file. It is possible to improve configuration by creating a dedicated namespace for https credential in order to specify this information only once. - -Namespace segregation enables the capability of deploying and manages pluggable database without the cdb administrative passwords. - -### CREATE TLS CERTIFICATE - -Here follow an example of script shell that can be used to create secret certificates in each namespace involved in the kubernets multi tenant architecture - -```bash -#!/bin/bash -export CDB_NAMESPACE=cdbnamespace -export PDB_NAMESPACE=pdbnamespace -export OPR_NAMESPACE=oracle-database-operator-system -export SKEY=tls.key -export SCRT=tls.crt -export CART=ca.crt -export COMPANY=oracle -export REST_SERVER=ords - -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr -echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} - -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} -``` -after all secrets creation you shoud have the following pattern - -```bash -kubectl get secrets -n oracle-database-operator-system -NAME TYPE DATA AGE -db-ca Opaque 1 6d5h -db-tls kubernetes.io/tls 2 6d5h -webhook-server-cert kubernetes.io/tls 3 6d15h - - -kubectl get secrets -n cdbnamespace -NAME TYPE DATA AGE -cdb1-secret Opaque 6 6d15h -db-ca Opaque 1 6d6h -db-tls kubernetes.io/tls 2 6d6h - - -kubectl get secrets -n pdbnamespace -NAME TYPE DATA AGE -db-ca Opaque 1 6d6h -db-tls kubernetes.io/tls 2 6d6h -pdb1-secret Opaque 4 2d16h -tde1-secret Opaque 2 22h -``` -### REST SERVER IMAGE CREATION - -```bash -cd oracle-database-operator/ords -docker build -t oracle/ords-dboper:latest . -docker tag oracle/ords-dboper:latest [path_of_your_registry]/ords-dboper:latest -docker push [path_of_your_registry]/ords-dboper.latest -cd - -``` - -### CDB POD CREATION - -**note:** - Before creating the CDB pod make sure that all the pluggable databases in the container DB are open. - - - -Update the cdb_create.yaml with the path of the image generated before to create CDB pod - -```bash -kubectl apply -f cdb_create.yaml -``` - -Verify the status of the operation and cdb pod existence using the following commands - -```bash -## check the pod creation -kubectl get pods -n cdbnamespace - -## check the rest server log after pod creation -kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace - -##login to the pod for further debug and information gathering -kubectl exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash -``` - -[log cdb creation](./cdb_creation_log.txt) - -### PDB CREATION - -Apply the the pdb_create.yaml file to create a new pdb , after pdb creation you should be able to get pdb details using **kubectl get** command - -```bash -kubectl apply -f pdb_create.yaml -``` - -```bash -#!/bin/bash -#checkpdbs.sh -kubectl get pdbs -n pdbnamespace -o=jsonpath='{range .items[*]} -{"\n==================================================================\n"} -{"CDB="}{.metadata.labels.cdb} -{"K8SNAME="}{.metadata.name} -{"PDBNAME="}{.spec.pdbName} -{"OPENMODE="}{.status.openMode} -{"ACTION="}{.status.action} -{"MSG="}{.status.msg} -{"\n"}{end}' -``` - -```bash -./checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE=READ WRITE -ACTION=CREATE -MSG=Success - -``` -[pdb creation log](./pdb_creation_log.txt) - -### MAKEFILE - -In order to facilitate the command execution use the [makefile](./makefile) available target details are exposed in the following tables. - -|target |Action | -|-----------------------------|-------------------------------------| -|step1 | Build rest server images | -|step2 | Tag the immages | -|step3 | Push the image into the repository | -|step4 | Load webhook certmanager | -|step5 | Create the db operator | -|step6 | Create tls certificates | -|step7 | Create tls secret | -|step8 | Create database secrets | -|step9 | Create restserver pod | -|checkstep9 | Monitor the executions | -|step10 | Create pluggable database | -|checkpdb | Monitor PDB status | -|dump | Dump pods info into a file | -|reloadop | Reload the db operator | -|login | Login into cdb pod | - -[imperative]:https://kubernetes.io/docs/concepts/overview/working-with-objects/object-management/ - - - diff --git a/docs/multitenant/ords-based/usecase03/ca.crt b/docs/multitenant/ords-based/usecase03/ca.crt deleted file mode 100644 index aaca8285..00000000 --- a/docs/multitenant/ords-based/usecase03/ca.crt +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID/zCCAuegAwIBAgIUO9C2/rgvVW6st0IxUm5H7PznIaMwDQYJKoZIhvcNAQEL -BQAwgY4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH -DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxIzAhBgNVBAMMGmNkYi1k -ZXYtb3Jkcy5jZGJuYW1lc3BhY2UgMRwwGgYDVQQDDBNsb2NhbGhvc3QgIFJvb3Qg -Q0EgMB4XDTI0MTExMjExMjgyOVoXDTI1MTExMjExMjgyOVowgY4xCzAJBgNVBAYT -AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW5GcmFuY2lzY28x -EDAOBgNVBAoMB29yYWNsZSAxIzAhBgNVBAMMGmNkYi1kZXYtb3Jkcy5jZGJuYW1l -c3BhY2UgMRwwGgYDVQQDDBNsb2NhbGhvc3QgIFJvb3QgQ0EgMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAswwGPMVAZTe8U2qu49+2xiakB3kavFccxEY0 -TlLLrCHMHQ2QoV47GvZ5OMXqi7zuQ0uDEmar5bkBL06bipaKTCJjYtCU5EJ8Pwhb -EIK3FXrtK5SpvbIyupRyHo/4oa53ilP9R5ykwyBnsOLnc67298MQQIM2S0Cf8ML6 -9P74I7obwz01LT/PsJwRUQW3gWkx90LLS2qllr+wn4RMzJGPmYgXTAv9BbIsl9hv -56N5iYeT+aat0dxr7xGh/SeiPIHNoRMeryde3Ag563JxjJJn4jZf83Dkiy0ykq/H -p2pN8A4XKj6HKABFIpXh7Dpj5e+9cMCmOeAoNJCP760cKIAF0QIDAQABo1MwUTAd -BgNVHQ4EFgQUNP29zpxd77qgmU6vZxtUIatet5cwHwYDVR0jBBgwFoAUNP29zpxd -77qgmU6vZxtUIatet5cwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC -AQEARFmBpMIsLMACV+QpsvozJgB7dxH0roO8CFuPV+KI/pk5PWotK8S+NhEx33hE -7U1u0BsiwUw/GVrHsyCgrCOgspUE6/UAQk1fCYfxgzxC3tAKxerJwzXWW5W0mdyU -iGvW5DGjjkCiYAqRWB1XdKyNdf5cvzykbF29mNWy5fjA7xwvvS1UjQb6wMc7RpYa -hAbCNNt+eU/qKBGuYegg5PENY92DLGRTcDmqImE/o7y6BQxL3x+AR8ouOV/MckZ6 -DAZ0V/t1Ijk3e8tuj2Q2t4f9e9y532lAbvbZSV8kWkxVrRLblAbkTU6cFQHnMDiU -KSLwBsbgZybJw1sR8dZW0kk9ww== ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase03/ca.key b/docs/multitenant/ords-based/usecase03/ca.key deleted file mode 100644 index 044326f3..00000000 --- a/docs/multitenant/ords-based/usecase03/ca.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAswwGPMVAZTe8U2qu49+2xiakB3kavFccxEY0TlLLrCHMHQ2Q -oV47GvZ5OMXqi7zuQ0uDEmar5bkBL06bipaKTCJjYtCU5EJ8PwhbEIK3FXrtK5Sp -vbIyupRyHo/4oa53ilP9R5ykwyBnsOLnc67298MQQIM2S0Cf8ML69P74I7obwz01 -LT/PsJwRUQW3gWkx90LLS2qllr+wn4RMzJGPmYgXTAv9BbIsl9hv56N5iYeT+aat -0dxr7xGh/SeiPIHNoRMeryde3Ag563JxjJJn4jZf83Dkiy0ykq/Hp2pN8A4XKj6H -KABFIpXh7Dpj5e+9cMCmOeAoNJCP760cKIAF0QIDAQABAoIBACpcEOm1vRt1ST/v -AHt3BRXyNGJ0O2gNsAebmGpjWj5aLKE/AVW8Jn2ljF5rLKdsxm3zYooq8kZCM86O -aWwrW4LRq/PH50HtnNmIz4qjck9JquzhmhsAGtJbpngmlasizLNUVZroq25ir0WB -GUMcRN+vLZwlruW032IlrxvharGAw6vw/nV0a2sLcG2owjxOI47UK/X2esJMqV/5 -2xPIs5ktBl+P/otAWVJ1lU6U43hZasUQ/RPBxLQY4H0DViR3eb+kuBgLvv8y12Tw -yCam3zoIMPN2HZbPz4fAavxfqywzilGoHWHyOCDNZK957WysyqJDWzohj2qvUt2c -bI8gVgECgYEA6wl/1cVpFeRXe4CULZJksEnLNXAFE5AAOo6gg40vCFQ1i8SHMaMg -PmJXWGxKA/r8swD3l4pRbBFtPyPWBBMvxiytieySy+2l4EjdgnVZTYMe0FeuN8/N -zDw2VUMtVfn27QMcYCmzHHY5Lux5PyDx2ZJ23J1mvTaiejCLOxmXv7ECgYEAwwQg -XWaA3yd511xJc8qnlgT6leeCJ7vD8OVUmoLvKT5pH2xm5ENEb5MF5FhcHdj1Y1Uu -cvE6HzjWlffZpdNfGiSXpWxhTqS3NLQ6mhHGLF6sIB3hiFfjcpAhh/mAY8MxQnhT -jU9PWoTgTHxRf8zUsMhx0XIgqc+8sSnz0+PZUCECgYEA4DiCvLQEaT8Z+hcSxVro -NYlgk80W+q+gmDxigr1YhhCXuIu3EbJ2Wy1D+HXuMlfpZPdiPC5XOMpzNw3/6IlJ -RTxwHakT7fn8CbhZoVBD7n9NESLkcBgNU26JwtNAV8oMN9WE53RAi8F5EfvEO2Y5 -cv+X5yU90vphDEJtQOdMbjECgYEAurCxlxhw2LB+2Kd1fc8MnfZIOcd19zmuO2gf -lQcxkwc3Najd2zl2Q2W5Jz9beAth5uT94XWDEa6k5s5t4my2R12ueuOFR/cxl9fN -nf7T+1fqaZiRdqfEisDmCLjWqyqKdrqgKdA6BEreOvpsPu9E0bQiGcKq+EVxuxyR -WlBukmECgYBJOESxKk0vpr1Gi+KyKWFzsr6GsinvgrNrZ1G0pcVSCJEPFAJlx/ib -uUJv4ziaPxd/RoshV2F6kmvKoiFZIrlKRr+UdelRcLJhDhku6w2OOMYfID1FqA7e -DW5IEvjqXqt7jcazU9GZ8zWNIRq1jqer6pp6aMiaHwG/04o4p5fjxw== ------END RSA PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase03/ca.srl b/docs/multitenant/ords-based/usecase03/ca.srl deleted file mode 100644 index 8b6ab178..00000000 --- a/docs/multitenant/ords-based/usecase03/ca.srl +++ /dev/null @@ -1 +0,0 @@ -1F43145DD3322A387B31238953C100E61B09B64D diff --git a/docs/multitenant/ords-based/usecase03/cdb_create.yaml b/docs/multitenant/ords-based/usecase03/cdb_create.yaml deleted file mode 100644 index cc29fc71..00000000 --- a/docs/multitenant/ords-based/usecase03/cdb_create.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: CDB -metadata: - name: cdb-dev - namespace: cdbnamespace -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - diff --git a/docs/multitenant/ords-based/usecase03/cdb_creation_log.txt b/docs/multitenant/ords-based/usecase03/cdb_creation_log.txt deleted file mode 100644 index 8c7dc161..00000000 --- a/docs/multitenant/ords-based/usecase03/cdb_creation_log.txt +++ /dev/null @@ -1,336 +0,0 @@ -kubectl get pods -n cdbnamespace -NAME READY STATUS RESTARTS AGE -cdb-dev-ords-rs-pgqqh 0/1 ContainerCreating 0 1s - -kubectl get pods -n cdbnamespace -NAME READY STATUS RESTARTS AGE -cdb-dev-ords-rs-pgqqh 1/1 Running 0 6s - -kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M -NOT_INSTALLED=2 - SETUP -==================================================== -CONFIG=/etc/ords/config -total 0 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:20 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.connectionType was set to: customurl in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:21 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.customURL was set to: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:23 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: security.requestValidationFunction was set to: false in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:25 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.MaxLimit was set to: 100 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:27 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.InitialLimit was set to: 50 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:29 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: error.externalPath was set to: /opt/oracle/ords/error -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:31 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.access.log was set to: /home/oracle -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:32 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.port was set to: 8888 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:34 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert was set to: /opt/oracle/ords//secrets/tls.crt -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:36 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert.key was set to: /opt/oracle/ords//secrets/tls.key -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:38 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: restEnabledSql.active was set to: true in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:40 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: security.verifySSL was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:42 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.enabled was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:43 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: plsql.gateway.mode was set to: disabled in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:45 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.management.services.disabled was set to: false -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:47 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: misc.pagination.maxRows was set to: 1000 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:49 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser was set to: C##DBAPI_CDB_ADMIN AS SYSDBA in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:51 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser.password was set to: ****** in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:53 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Created user welcome in file /etc/ords/config/global/credentials -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:55 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Oracle REST Data Services - Non-Interactive Install - -Retrieving information.. -Completed verifying Oracle REST Data Services schema version 23.3.0.r2891830. -Connecting to database user: ORDS_PUBLIC_USER url: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -The setting named: db.serviceNameSuffix was set to: in configuration: default -The setting named: db.username was set to: ORDS_PUBLIC_USER in configuration: default -The setting named: db.password was set to: ****** in configuration: default -The setting named: security.requestValidationFunction was set to: ords_util.authorize_plsql_gateway in configuration: default -2024-01-25T17:17:58.898Z INFO Oracle REST Data Services schema version 23.3.0.r2891830 is installed. -2024-01-25T17:17:58.900Z INFO To run in standalone mode, use the ords serve command: -2024-01-25T17:17:58.900Z INFO ords --config /etc/ords/config serve -2024-01-25T17:17:58.900Z INFO Visit the ORDS Documentation to access tutorials, developer guides and more to help you get started with the new ORDS Command Line Interface (http://oracle.com/rest). -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:18:00 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -2024-01-25T17:18:00.960Z INFO HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8080 -2024-01-25T17:18:00.963Z INFO HTTPS and HTTPS/2 listening on host: 0.0.0.0 port: 8888 -2024-01-25T17:18:00.980Z INFO Disabling document root because the specified folder does not exist: /etc/ords/config/global/doc_root -2024-01-25T17:18:00.981Z INFO Default forwarding from / to contextRoot configured. -2024-01-25T17:18:06.634Z INFO Configuration properties for: |default|lo| -db.serviceNameSuffix= -java.specification.version=21 -conf.use.wallet=true -database.api.management.services.disabled=false -sun.jnu.encoding=UTF-8 -user.region=US -java.class.path=/opt/oracle/ords/ords.war -java.vm.vendor=Oracle Corporation -standalone.https.cert.key=/opt/oracle/ords//secrets/tls.key -sun.arch.data.model=64 -nashorn.args=--no-deprecation-warning -java.vendor.url=https://java.oracle.com/ -resource.templates.enabled=false -user.timezone=UTC -java.vm.specification.version=21 -os.name=Linux -sun.java.launcher=SUN_STANDARD -user.country=US -sun.boot.library.path=/usr/java/jdk-21/lib -sun.java.command=/opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure -jdk.debug=release -sun.cpu.endian=little -user.home=/home/oracle -oracle.dbtools.launcher.executable.jar.path=/opt/oracle/ords/ords.war -user.language=en -db.cdb.adminUser.password=****** -java.specification.vendor=Oracle Corporation -java.version.date=2023-10-17 -database.api.enabled=true -java.home=/usr/java/jdk-21 -db.username=ORDS_PUBLIC_USER -file.separator=/ -java.vm.compressedOopsMode=32-bit -line.separator= - -restEnabledSql.active=true -java.specification.name=Java Platform API Specification -java.vm.specification.vendor=Oracle Corporation -java.awt.headless=true -standalone.https.cert=/opt/oracle/ords//secrets/tls.crt -db.password=****** -sun.management.compiler=HotSpot 64-Bit Tiered Compilers -security.requestValidationFunction=ords_util.authorize_plsql_gateway -misc.pagination.maxRows=1000 -java.runtime.version=21.0.1+12-LTS-29 -user.name=oracle -error.externalPath=/opt/oracle/ords/error -stdout.encoding=UTF-8 -path.separator=: -db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA -os.version=5.4.17-2136.323.8.1.el7uek.x86_64 -java.runtime.name=Java(TM) SE Runtime Environment -file.encoding=UTF-8 -plsql.gateway.mode=disabled -security.verifySSL=true -standalone.https.port=8888 -java.vm.name=Java HotSpot(TM) 64-Bit Server VM -java.vendor.url.bug=https://bugreport.java.com/bugreport/ -java.io.tmpdir=/tmp -oracle.dbtools.cmdline.ShellCommand=ords -java.version=21.0.1 -user.dir=/home/oracle/keystore -os.arch=amd64 -java.vm.specification.name=Java Virtual Machine Specification -jdbc.MaxLimit=100 -oracle.dbtools.cmdline.home=/opt/oracle/ords -native.encoding=UTF-8 -java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib -java.vendor=Oracle Corporation -java.vm.info=mixed mode, sharing -stderr.encoding=UTF-8 -java.vm.version=21.0.1+12-LTS-29 -sun.io.unicode.encoding=UnicodeLittle -jdbc.InitialLimit=50 -db.connectionType=customurl -java.class.version=65.0 -db.customURL=jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -standalone.access.log=/home/oracle - -2024-01-25T17:18:10.381Z INFO - -Mapped local pools from /etc/ords/config/databases: - /ords/ => default => VALID - - -2024-01-25T17:18:10.532Z INFO Oracle REST Data Services initialized -Oracle REST Data Services version : 23.3.0.r2891830 -Oracle REST Data Services server info: jetty/10.0.17 -Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 21.0.1+12-LTS-29 - - -exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash -[oracle@cdb-dev-ords-rs-pgqqh ~]$ ps -ef|grep java -oracle 1147 1116 10 17:17 ? 00:00:21 /usr/java/jdk-21/bin/java -Doracle.dbtools.cmdline.home=/opt/oracle/ords -Duser.language=en -Duser.region=US -Dfile.encoding=UTF-8 -Djava.awt.headless=true -Dnashorn.args=--no-deprecation-warning -Doracle.dbtools.cmdline.ShellCommand=ords -Duser.timezone=UTC -jar /opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure -oracle 1227 1200 0 17:21 pts/0 00:00:00 grep --color=auto java diff --git a/docs/multitenant/ords-based/usecase03/cdb_secret.yaml b/docs/multitenant/ords-based/usecase03/cdb_secret.yaml deleted file mode 100644 index 8f1b6fc9..00000000 --- a/docs/multitenant/ords-based/usecase03/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: cdbnamespace -type: Opaque -data: - ords_pwd: "[...base64 encoded password...]" - sysadmin_pwd: "[...base64 encoded password...]" - cdbadmin_user: "[...base64 encoded password...]" - cdbadmin_pwd: "[...base64 encoded password...]" - webserver_user: "[...base64 encoded password...]" - webserver_pwd: "[...base64 encoded password...]" diff --git a/docs/multitenant/ords-based/usecase03/extfile.txt b/docs/multitenant/ords-based/usecase03/extfile.txt deleted file mode 100644 index 53e1b951..00000000 --- a/docs/multitenant/ords-based/usecase03/extfile.txt +++ /dev/null @@ -1 +0,0 @@ -subjectAltName=DNS:cdb-dev-ords.cdbnamespace,DNS:www.example.com diff --git a/docs/multitenant/ords-based/usecase03/gentlscert.sh b/docs/multitenant/ords-based/usecase03/gentlscert.sh deleted file mode 100644 index 49e29147..00000000 --- a/docs/multitenant/ords-based/usecase03/gentlscert.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -export CDB_NAMESPACE=cdbnamespace -export PDB_NAMESPACE=pdbnamespace -export OPR_NAMESPACE=oracle-database-operator-system -export SKEY=tls.key -export SCRT=tls.crt -export CART=ca.crt -export COMPANY=oracle -export REST_SERVER=ords - -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr -echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} - -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} - diff --git a/docs/multitenant/ords-based/usecase03/makefile b/docs/multitenant/ords-based/usecase03/makefile deleted file mode 100644 index 62f8a642..00000000 --- a/docs/multitenant/ords-based/usecase03/makefile +++ /dev/null @@ -1,291 +0,0 @@ -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# -# ___ -# / _ \ _ __ _ __ _ __ ___ _ __ ___ -# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ -# | |_| | | | | |_) | | | __/ | | | | | -# \___/|_| |_| .__/|_| \___|_| |_| |_| -# |_| -# ____ _ _ _ -# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ -# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| -# | |__| (_) | | | | |_| | | (_) | | | __/ | -# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| -# -# -# This makefile helps to speed up the kubectl commands executions to deploy and test -# the mutlitenant operator. Although it has few functionality you can adapt to your needs -# by adding much more targets. -# -# Quick start: -# ~~~~~~~~~~~ -# -# - Copy files of tab.1 in the makefile directory. -# - Edit the secret files and other yaml files with the correct credential as -# specified in the documentation. -# - Edit makefile updating variables of tab.2 -# - Execute commands of tab.3 "make step1" "make step2" "make step3".... -# -# Tab.1 - List of required files -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Opertaor yaml file | -# +-----------------------------+---------------------------------------------+ -# |cdb_secret.yaml | Secret file for the rest server pod | -# +-----------------------------+---------------------------------------------+ -# |pdb_secret.yaml | Secret file for the pdb creation | -# +-----------------------------+---------------------------------------------+ -# |cdb_create.yaml | Rest server pod creation | -# +-----------------------------+---------------------------------------------+ -# |pdb_create.yaml | Pluggable database creation | -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Database operator | -# +-----------------------------+---------------------------------------------+ -# |Dockerfiles | Dockerfile for CBD | -# +-----------------------------+---------------------------------------------+ -# |runOrdsSSL.sh | Init script executed by Dockerfile | -# +-----------------------------+---------------------------------------------+ -# -# Tab.2 - List of variables -# ~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |OCIR | Your image registry | -# +-----------------------------+---------------------------------------------+ -# |OCIRPATH | Path of the image in your registry | -# +-----------------------------+---------------------------------------------+ -# -# Tab.3 - Execution steps -# ~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# | MAKEFILE TARGETS LIST | -# | ----- ooo ----- | -# | - TARGET - - DESCRIPTION - | -# +-----------------------------+-------------------------------------+-------+ -# |step1 | Build rest server images | | -# +-----------------------------+-------------------------------------+ REST | -# |step2 | Tag the immages | SRV | -# +-----------------------------+-------------------------------------+ IMG | -# |step3 | Push the image into the repository | | -# +-----------------------------+-------------------------------------+-------+ -# |step4 | Load webhook certmanager | DB | -# +-----------------------------+-------------------------------------+ OPER | -# |step5 | Create the db operator | | -# +-----------------------------+-------------------------------------+-------+ -# |step6 | Create tls certificates | T | -# +-----------------------------+-------------------------------------+ L | -# |step7 | Create tls secret | S | -# +-----------------------------+---------------------------------------------+ -# |step8 | Create database secrets | -# +-----------------------------+---------------------------------------------+ -# |step9 | Create restserver pod | -# | | +---------------------------------------------+ -# | +---> checkstep9 | Monitor the executions | -# +-----------------------------+---------------------------------------------+ -# |step10 | Create pluggable database | -# | | +---------------------------------------------+ -# | +---> checkpdb | Monitor PDB status | -# +-----------------------------+---------------------------------------------+ -# | DIAGNOSTIC TARGETS | -# +-----------------------------+---------------------------------------------+ -# | dump | Dump pods info into a file | -# +-----------------------------+---------------------------------------------+ -# | reloadop | Reload the db operator | -# +-----------------------------+---------------------------------------------+ -# | login | Login into cdb pod | -# +-----------------------------+---------------------------------------------+ - - -################ TAB 2 VARIABLES ############ -REST_SERVER=ords -ORDSVERSION=latest - -OCIR=[container registry] -OCIRPATH=$(REST_SERVER)-dboper:$(ORDSVERSION) - -#examples: -#OCIR=lin.ocir.io -#OCIRPATH=/sampletenancy/samplepath/sampledir/$(REST_SERVER)-dboper:$(ORDSVERSION) -############################################# -DOCKER=/usr/bin/docker -KUBECTL=/usr/bin/kubectl -ORDS=/usr/local/bin/ords -CONFIG=/etc/ords/config -IMAGE=oracle/$(REST_SERVER)-dboper:$(ORDSVERSION) -DBOPERATOR=oracle-database-operator.yaml -URLPATH=/_/db-api/stable/database/pdbs/ -OPENSSL=/usr/bin/openssl -ORDSPORT=8888 -MAKE=/usr/bin/make -DOCKERFILE=../../../../ords/Dockerfile -RUNSCRIPT=../../../../ords/runOrdsSSL.sh -RM=/usr/bin/rm -CP=/bin/cp -ECHO=/usr/bin/echo -CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -CDB_SECRET_YAML=cdb_secret.yaml -PDB_SECRET_YAML=pdb_secret.yaml -TDE_SECRET_YAML=tde_secret.yaml -CDB_NAMESPACE_YAML=ns_namespace_cdb.yaml -PDB_NAMESPACE_YAML=ns_namespace_pdb.yaml -OPR_NAMESPACE=oracle-database-operator-system -PDB_NAMESPACE=$(shell grep namespace $(PDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') -CDB_NAMESPACE=$(shell grep namespace $(CDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') -CDB=cdb_create.yaml -PDB=pdb_create.yaml -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -COMPANY=oracle -LOCALHOST=localhost -RESTPREFIX=cdb-dev - - -step1: createimage -step2: tagimage -step3: push -step4: certmanager -step5: dboperator -step6: tlscert -step7: tlssecret -step8: dbsecret -step9: cdb -step10: pdb - -checkstep9: checkcdb - - -createimage: - @echo "BUILDING CDB IMAGES" - $(CP) $(DOCKERFILE) . - $(CP) $(RUNSCRIPT) . - $(DOCKER) build -t $(IMAGE) . - -createimageproy: - @echo "BUILDING CDB IMAGES" - $(CP) $(DOCKERFILE) . - $(CP) $(RUNSCRIPT) . - $(DOCKER) build --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) -t $(IMAGE) . - -tagimage: - @echo "TAG IMAGE" - $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) - -push: - @echo "PUSH IMAGE INTO THE REGISTRY" - $(DOCKER) push $(OCIR)$(OCIRPATH) - -certmanager: - @echo "WEBHOOK CERT MANAGER" - $(KUBECTL) apply -f $(CERTMANAGER) - -dboperator: - @echo "ORACLE DATABASE OPERATOR" - $(KUBECTL) apply -f $(DBOPERATOR) - -namespace: - $(KUBECTL) get namespaces - $(KUBECTL) apply -f $(CDB_NAMESPACE_YAML) - $(KUBECTL) apply -f $(PDB_NAMESPACE_YAML) - $(KUBECTL) get namespaces - - -tlscert: - @echo "CREATING TLS CERTIFICATES" - $(OPENSSL) genrsa -out ca.key 2048 - $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST)" -out server.csr - $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE),DNS:www.example.com" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) - - -tlssecret: - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDB_NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(CDB_NAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDB_NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDB_NAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPR_NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPR_NAMESPACE) - - -dbsecret: - @echo "CREATING DB SECRETS" - $(KUBECTL) apply -f $(CDB_SECRET_YAML) - $(KUBECTL) apply -f $(PDB_SECRET_YAML) - $(KUBECTL) apply -f $(TDE_SECRET_YAML) - - -cdb: - @echo "CREATING REST SRV POD" - $(KUBECTL) apply -f $(CDB) - -checkcdb: - $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) - -pdb: - $(KUBECTL) apply -f $(PDB) - -checkpdb: - $(KUBECTL) get pdbs -n $(OPR_NAMESPACE) - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - @echo "CDB LOG DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs `$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - @echo "SECRET DMP" >>$(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) get secrets -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) - @echo "CDB/PDB DMP" >> $(DIAGFILE) - $(KUBECTL) get pdbs -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) - $(KUBECTL) get cdb -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) - @echo "CLUSTER INFO" >> $(DIAGFILE) - $(KUBECTL) get nodes -o wide - $(KUBECTL) get svc --namespace=kube-system - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - - -login: - $(KUBECTL) exec -it `$(KUBECTL) get pods -n $(CDB_NAMESPACE) |grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) bash - -cdblog: - $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) - - - -xlog1: - $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) - -xlog2: - $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) - -xlog3: - $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) - -checkdep: - $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(OPR_NAMESPACE) - $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(CBD_NAMESPACE) - $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(PDB_NAMESPACE) - - - diff --git a/docs/multitenant/ords-based/usecase03/ns_namespace_cdb.yaml b/docs/multitenant/ords-based/usecase03/ns_namespace_cdb.yaml deleted file mode 100644 index f4c6d77b..00000000 --- a/docs/multitenant/ords-based/usecase03/ns_namespace_cdb.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: cdbnamespace - diff --git a/docs/multitenant/ords-based/usecase03/ns_namespace_pdb.yaml b/docs/multitenant/ords-based/usecase03/ns_namespace_pdb.yaml deleted file mode 100644 index b22245f9..00000000 --- a/docs/multitenant/ords-based/usecase03/ns_namespace_pdb.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: pdbnamespace - diff --git a/docs/multitenant/ords-based/usecase03/operator_creation_log.txt b/docs/multitenant/ords-based/usecase03/operator_creation_log.txt deleted file mode 100644 index 36ed02ac..00000000 --- a/docs/multitenant/ords-based/usecase03/operator_creation_log.txt +++ /dev/null @@ -1,27 +0,0 @@ -kubectl apply -f oracle-database-operator.yaml -namespace/oracle-database-operator-system created -customresourcedefinition.apiextensions.k8s.io/autonomouscontainerdatabases.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/autonomousdatabasebackups.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/autonomousdatabaserestores.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/autonomousdatabases.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/cdbs.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/dataguardbrokers.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/dbcssystems.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/oraclerestdataservices.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/pdbs.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/shardingdatabases.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/singleinstancedatabases.database.oracle.com configured -role.rbac.authorization.k8s.io/oracle-database-operator-leader-election-role created -clusterrole.rbac.authorization.k8s.io/oracle-database-operator-manager-role created -clusterrole.rbac.authorization.k8s.io/oracle-database-operator-metrics-reader created -clusterrole.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-role created -rolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-leader-election-rolebinding created -clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-manager-rolebinding created -clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-rolebinding created -service/oracle-database-operator-controller-manager-metrics-service created -service/oracle-database-operator-webhook-service created -certificate.cert-manager.io/oracle-database-operator-serving-cert created -issuer.cert-manager.io/oracle-database-operator-selfsigned-issuer created -mutatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-mutating-webhook-configuration created -validatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-validating-webhook-configuration created -deployment.apps/oracle-database-operator-controller-manager created diff --git a/docs/multitenant/ords-based/usecase03/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase03/oracle-database-operator.yaml deleted file mode 120000 index bbf4775f..00000000 --- a/docs/multitenant/ords-based/usecase03/oracle-database-operator.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/ords-based/usecase03/pdb_create.yaml b/docs/multitenant/ords-based/usecase03/pdb_create.yaml deleted file mode 100644 index 920fc134..00000000 --- a/docs/multitenant/ords-based/usecase03/pdb_create.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - diff --git a/docs/multitenant/ords-based/usecase03/pdb_creation_log.txt b/docs/multitenant/ords-based/usecase03/pdb_creation_log.txt deleted file mode 100644 index 71d0eb4f..00000000 --- a/docs/multitenant/ords-based/usecase03/pdb_creation_log.txt +++ /dev/null @@ -1,6 +0,0 @@ -kubectl apply -f pdb_create.yaml -pdb.database.oracle.com/pdb1 created - -kubectl get pdbs -n pdbnamespace -NAME CONNECT_STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -pdb1 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) DB12 pdbdev READ WRITE 0.78G Ready Success diff --git a/docs/multitenant/ords-based/usecase03/pdb_secret.yaml b/docs/multitenant/ords-based/usecase03/pdb_secret.yaml deleted file mode 100644 index f1dfdac6..00000000 --- a/docs/multitenant/ords-based/usecase03/pdb_secret.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: pdbnamespace -type: Opaque -data: - sysadmin_user: "[...base64 encoded password...]" - sysadmin_pwd: "[...base64 encoded password...]" - webserver_user: "[...base64 encoded password...]" - webserver_pwd: "[...base64 encoded password...]" - diff --git a/docs/multitenant/ords-based/usecase03/runOrdsSSL.sh b/docs/multitenant/ords-based/usecase03/runOrdsSSL.sh deleted file mode 100644 index 35f1b77b..00000000 --- a/docs/multitenant/ords-based/usecase03/runOrdsSSL.sh +++ /dev/null @@ -1,190 +0,0 @@ -#!/bin/bash - -cat <$TNSNAME - - -function SetParameter() { - ##ords config info <--- Use this command to get the list - -[[ ! -z "${ORACLE_HOST}" && -z "${DBTNSURL}" ]] && { - $ORDS --config ${CONFIG} config set db.hostname ${ORACLE_HOST:-racnode1} - $ORDS --config ${CONFIG} config set db.port ${ORACLE_PORT:-1521} - $ORDS --config ${CONFIG} config set db.servicename ${ORACLE_SERVICE:-TESTORDS} -} - -[[ -z "${ORACLE_HOST}" && ! -z "${DBTNSURL}" ]] && { - #$ORDS --config ${CONFIG} config set db.tnsAliasName ${TNSALIAS} - #$ORDS --config ${CONFIG} config set db.tnsDirectory ${TNS_ADMIN} - #$ORDS --config ${CONFIG} config set db.connectionType tns - - $ORDS --config ${CONFIG} config set db.connectionType customurl - $ORDS --config ${CONFIG} config set db.customURL jdbc:oracle:thin:@${DBTNSURL} -} - - $ORDS --config ${CONFIG} config set security.requestValidationFunction false - $ORDS --config ${CONFIG} config set jdbc.MaxLimit 100 - $ORDS --config ${CONFIG} config set jdbc.InitialLimit 50 - $ORDS --config ${CONFIG} config set error.externalPath ${ERRORFOLDER} - $ORDS --config ${CONFIG} config set standalone.access.log /home/oracle - $ORDS --config ${CONFIG} config set standalone.https.port 8888 - $ORDS --config ${CONFIG} config set standalone.https.cert ${CERTIFICATE} - $ORDS --config ${CONFIG} config set standalone.https.cert.key ${KEY} - $ORDS --config ${CONFIG} config set restEnabledSql.active true - $ORDS --config ${CONFIG} config set security.verifySSL true - $ORDS --config ${CONFIG} config set database.api.enabled true - $ORDS --config ${CONFIG} config set plsql.gateway.mode disabled - $ORDS --config ${CONFIG} config set database.api.management.services.disabled false - $ORDS --config ${CONFIG} config set misc.pagination.maxRows 1000 - $ORDS --config ${CONFIG} config set db.cdb.adminUser "${CDBADMIN_USER:-C##DBAPI_CDB_ADMIN} AS SYSDBA" - $ORDS --config ${CONFIG} config secret --password-stdin db.cdb.adminUser.password << EOF -${CDBADMIN_PWD:-PROVIDE_A_PASSWORD} -EOF - -$ORDS --config ${CONFIG} config user add --password-stdin ${WEBSERVER_USER:-ordspdbadmin} "SQL Administrator, System Administrator" <${CKF} 2>&1 -echo "checkfile" >> ${CKF} -NOT_INSTALLED=`cat ${CKF} | grep "INFO: The" |wc -l ` -echo NOT_INSTALLED=$NOT_INSTALLED - - -function StartUp () { - $ORDS --config $CONFIG serve --port 8888 --secure -} - -# Check whether ords is already setup -if [ $NOT_INSTALLED -ne 0 ] -then - echo " SETUP " - setupOrds; - StartUp; -fi - -if [ $NOT_INSTALLED -eq 0 ] -then - echo " STARTUP " - StartUp; -fi - - diff --git a/docs/multitenant/ords-based/usecase03/server.csr b/docs/multitenant/ords-based/usecase03/server.csr deleted file mode 100644 index ce9aa147..00000000 --- a/docs/multitenant/ords-based/usecase03/server.csr +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIICyjCCAbICAQAwgYQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh -MRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxIzAhBgNV -BAMMGmNkYi1kZXYtb3Jkcy5jZGJuYW1lc3BhY2UgMRIwEAYDVQQDDAlsb2NhbGhv -c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOuYl+mB0G/evcnF5z -HKxXWAhAIfY9jMcYtIDo3vw0Vi4CcDaNZHoQCKbPG/f/+Y1PQx5Pm7MQzwaNapd9 -kqAu5cCbM9+bn0yIJdn5r6SG3VOFakxjLKY1BcRvq8HqqpkaKK8Bslyz99Ba2Cnw -Gsk/Tnc8x29Gk00oMRZejVRO/CKSulAwG1j8TG4VgG8zl9ynWi63Z3Mv2D0l2YDE -lqtUS3j4A8i7hRyrH20j0F2x4vG+cEkJz8xhblfIlTlk1C2w8FlDg86ZlImojkU1 -lLRBACPRGBIO4CnFKbY0k1FVOdMr/83hYgEF+y/dod6Mus2q+eq6ScktUS15wX1T -hFtpAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEARTh4oxRZ1Wk8LlrDWHIWU/lk -f3q/tacMGoGWMrwAEbNfsVglt622FxImE4oeb77OAWK9iBCE0xiYSFxd58v5FgW5 -OG2Ze0/VCD5klyOQ6vTrZyQFnO8uPhxGa+0UdJlq5aN+jEp6ZpmRXiiT+Km09wMb -sypVJ/juOxULWEbRWwSaGaXXv2NrDhAp4WcOnZrO3+vgq80FGX1N3RRl/4pXTuyG -S7+4Md9fOnLRon4v0ODLP56xPsFnW3tmvBEdPbUM8CV6xgCAh5LAJbKfhDFKjAOJ -J49ek3EpXobyx4vn9sdvSJaQUfgMOugbZzoKVi127cmeeiM/U2UYcVxlOyUa4A== ------END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/ords-based/usecase03/tls.crt b/docs/multitenant/ords-based/usecase03/tls.crt deleted file mode 100644 index c75dbc54..00000000 --- a/docs/multitenant/ords-based/usecase03/tls.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID2zCCAsOgAwIBAgIUH0MUXdMyKjh7MSOJU8EA5hsJtk0wDQYJKoZIhvcNAQEL -BQAwgY4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH -DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxIzAhBgNVBAMMGmNkYi1k -ZXYtb3Jkcy5jZGJuYW1lc3BhY2UgMRwwGgYDVQQDDBNsb2NhbGhvc3QgIFJvb3Qg -Q0EgMB4XDTI0MTExMjExMjgyOVoXDTI1MTExMjExMjgyOVowgYQxCzAJBgNVBAYT -AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW5GcmFuY2lzY28x -EDAOBgNVBAoMB29yYWNsZSAxIzAhBgNVBAMMGmNkYi1kZXYtb3Jkcy5jZGJuYW1l -c3BhY2UgMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDOuYl+mB0G/evcnF5zHKxXWAhAIfY9jMcYtIDo3vw0Vi4CcDaN -ZHoQCKbPG/f/+Y1PQx5Pm7MQzwaNapd9kqAu5cCbM9+bn0yIJdn5r6SG3VOFakxj -LKY1BcRvq8HqqpkaKK8Bslyz99Ba2CnwGsk/Tnc8x29Gk00oMRZejVRO/CKSulAw -G1j8TG4VgG8zl9ynWi63Z3Mv2D0l2YDElqtUS3j4A8i7hRyrH20j0F2x4vG+cEkJ -z8xhblfIlTlk1C2w8FlDg86ZlImojkU1lLRBACPRGBIO4CnFKbY0k1FVOdMr/83h -YgEF+y/dod6Mus2q+eq6ScktUS15wX1ThFtpAgMBAAGjOTA3MDUGA1UdEQQuMCyC -GWNkYi1kZXYtb3Jkcy5jZGJuYW1lc3BhY2WCD3d3dy5leGFtcGxlLmNvbTANBgkq -hkiG9w0BAQsFAAOCAQEAAE/DCwfWNHXF794/ug7DQqN3EzR6SdPLMzQygIYkhwPS -9S0nfDde+wS2nbNDrl1sOrGTyn/ZfIUJD0ia+zOJOvaqR/fFKrC6z1fIiKUQOynY -jO/DFPu9XD8lIrrLGNb9sT8KPpmiaq9RtuVFVj77siMO2A6IwNuQiYRVSH0whaYi -7zvguB96DLsKlN+v80ucSm1TZbSYIi77Yx8gUJBwA1PkmEsp6ixMQltlMidsHGIu -H/lhamO9vrVN890WCXfJ7melS4SORt5aAH7cTUchNX4gTNbmDm2Y9qvYHqb4q0vq -H5qYpg3DLkju9FqIOgWF64cpGMXZ1A53ocujiDR67A== ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase03/tls.key b/docs/multitenant/ords-based/usecase03/tls.key deleted file mode 100644 index cabbc944..00000000 --- a/docs/multitenant/ords-based/usecase03/tls.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDOuYl+mB0G/evc -nF5zHKxXWAhAIfY9jMcYtIDo3vw0Vi4CcDaNZHoQCKbPG/f/+Y1PQx5Pm7MQzwaN -apd9kqAu5cCbM9+bn0yIJdn5r6SG3VOFakxjLKY1BcRvq8HqqpkaKK8Bslyz99Ba -2CnwGsk/Tnc8x29Gk00oMRZejVRO/CKSulAwG1j8TG4VgG8zl9ynWi63Z3Mv2D0l -2YDElqtUS3j4A8i7hRyrH20j0F2x4vG+cEkJz8xhblfIlTlk1C2w8FlDg86ZlImo -jkU1lLRBACPRGBIO4CnFKbY0k1FVOdMr/83hYgEF+y/dod6Mus2q+eq6ScktUS15 -wX1ThFtpAgMBAAECggEBAJfIZKq9lzqF/8K4USTl3ag3677i8MCad6itB6Vz6+ul -hxylNXi1WGYjZA7XM00P9vBGkJ/U6zIhP2df5yD9In2slbGe8cR6vx+L4zLQAtY+ -9U8VEEcVBmgR9vwjOTtKzSUG0BBH3IcLIkFBS/GOSUshGq1WpV5FUzZ6bDk6PbfL -WEDKIzZaO1qrd53xe3HzUIGjYMj6LR/eiXw5d63Km9ay7fCOeDJAAYCjR1m4VceK -xB/1vCLyWiy2gjrhNJZVF6cJd5Q6bSjKudoY+9ikcrZ77T9jgcYYXQZOM/L6bIFr -LAXklQl2isNvwZevdUVLv2aoZBM9BKGydD2RN6b0l10CgYEA9OJU77a4RTo3IGpY -reVr6T+/ZodU6LK6x5FYi2BGyD30H6zEokq1qzkswxCN8x3HFP/kxlokS10k2db7 -jqqzDaabNDOxapwgT24BBI6KYMbiPFeuHhtKsRDNPYbSUoyuzcuDnbUXYGiHutOw -sygQwoeoynFJD0BVeGRkrSmM99sCgYEA2BvGlMjXTBqzqUjavnOS1apUmgn5nvHG -6MikMyAbNX6iGqQubMj8PXVkJf31kctChonFFwN1qHyOUagkiT/28neiYnlN5qbQ -6rm1myvh/KL88oRoFwuXy7dspgrptoq++Eie/NP8FGBMavXLXr/369/oDgB2UW8t -ZdhZRM1FrwsCgYEAlYuaPwGgqnYQsDUBjsCqE7kgU6aVjb2IHtN99S+ISkS7TpSd -SFbDdprl4QG9DhjDOOnEdfUacQOiu310Bf3sdoumAE0JLYfGm3scGAJMMymBg0Qw -SqZ0CImwLMCmtE7BeV+tMqQicHelW8xm/iGbipB53Zcs/KlXCLyWVsEnGz0CgYEA -iETzciw2vlF0CoHbFQ6xfzOZh0xU7+nLIEwsZeSP46qHMz9l2sjdbS+Rx/ccmyNN -PqKu+bT/ww9PeqQI7da8s+XGKkwNWCzGPcx+fGsuY+yujYn6TNXNkPKHeygUeTuB -8fNw9UICiSKz7Royc/uerEiS/glCklUFjlve5mh90UMCgYARGaOLss2+3OGQ0/2P -ghwhA9g0GS2dovinabZXFZPBxfeEuqvWD2ghSFDA/adSEWFAk57OkzAt46heeB4S -pIL1MGpAYPGqxOfLtE1JTAtLExmJlGA4xIU5DNDNYVN4J/PGbE68lxgyvveS+lzA -W3PGwL4lNI1KlR221LufR/ZTUw== ------END PRIVATE KEY----- diff --git a/docs/multitenant/usecase01/oracle-database-operator.yaml b/docs/multitenant/usecase01/oracle-database-operator.yaml deleted file mode 100644 index 1838ad9f..00000000 --- a/docs/multitenant/usecase01/oracle-database-operator.yaml +++ /dev/null @@ -1,4052 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-system ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomouscontainerdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousContainerDatabase - listKind: AutonomousContainerDatabaseList - plural: autonomouscontainerdatabases - shortNames: - - acd - - acds - singular: autonomouscontainerdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.displayName - name: DisplayName - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase - properties: - action: - enum: - - SYNC - - RESTART - - TERMINATE - type: string - autonomousContainerDatabaseOCID: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - autonomousExadataVMClusterOCID: - type: string - compartmentOCID: - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - hardLink: - default: false - type: boolean - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - patchModel: - description: 'AutonomousContainerDatabasePatchModelEnum Enum with underlying type: string' - enum: - - RELEASE_UPDATES - - RELEASE_UPDATE_REVISIONS - type: string - type: object - status: - description: AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase - properties: - lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - timeCreated: - type: string - required: - - lifecycleState - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabasebackups.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseBackup - listKind: AutonomousDatabaseBackupList - plural: autonomousdatabasebackups - shortNames: - - adbbu - - adbbus - singular: autonomousdatabasebackup - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.dbDisplayName - name: DB DisplayName - type: string - - jsonPath: .status.type - name: Type - type: string - - jsonPath: .status.timeStarted - name: Started - type: string - - jsonPath: .status.timeEnded - name: Ended - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AutonomousDatabaseBackupSpec defines the desired state of AutonomousDatabaseBackup - properties: - autonomousDatabaseBackupOCID: - type: string - displayName: - type: string - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' - properties: - k8sADB: - description: "*********************** *\tADB spec ***********************" - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - type: object - status: - description: AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup - properties: - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - dbDisplayName: - type: string - dbName: - type: string - isAutomatic: - type: boolean - lifecycleState: - description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with underlying type: string' - type: string - timeEnded: - type: string - timeStarted: - type: string - type: - description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying type: string' - type: string - required: - - autonomousDatabaseOCID - - compartmentOCID - - dbDisplayName - - dbName - - isAutomatic - - lifecycleState - - type - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabaserestores.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseRestore - listKind: AutonomousDatabaseRestoreList - plural: autonomousdatabaserestores - shortNames: - - adbr - - adbrs - singular: autonomousdatabaserestore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.displayName - name: DbDisplayName - type: string - - jsonPath: .status.dbName - name: DbName - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore - properties: - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - source: - properties: - k8sADBBackup: - description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.' - properties: - name: - type: string - type: object - pointInTime: - properties: - timestamp: - description: 'The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT' - type: string - type: object - type: object - target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' - properties: - k8sADB: - description: "*********************** *\tADB spec ***********************" - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - required: - - source - - target - type: object - status: - description: AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore - properties: - dbName: - type: string - displayName: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - status: - description: 'WorkRequestStatusEnum Enum with underlying type: string' - type: string - timeAccepted: - type: string - timeEnded: - type: string - timeStarted: - type: string - workRequestOCID: - type: string - required: - - dbName - - displayName - - status - - workRequestOCID - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases - shortNames: - - adb - - adbs - singular: autonomousdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.details.displayName - name: Display Name - type: string - - jsonPath: .spec.details.dbName - name: Db Name - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .spec.details.isDedicated - name: Dedicated - type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .spec.details.dataStorageSizeInTBs - name: Storage (TB) - type: integer - - jsonPath: .spec.details.dbWorkload - name: Workload Type - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabase is the Schema for the autonomousdatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase Important: Run "make" to regenerate code after modifying this file' - properties: - details: - description: AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase - properties: - adminPassword: - properties: - k8sSecret: - description: "*********************** *\tSecret specs ***********************" - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - autonomousContainerDatabase: - description: ACDSpec defines the spec of the target for backup/restore runs. The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup - properties: - k8sACD: - description: "*********************** *\tACD specs ***********************" - properties: - name: - type: string - type: object - ociACD: - properties: - ocid: - type: string - type: object - type: object - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbName: - type: string - dbVersion: - type: string - dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying type: string' - enum: - - OLTP - - DW - - AJD - - APEX - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - isAutoScalingEnabled: - type: boolean - isDedicated: - type: boolean - licenseModel: - description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying type: string' - enum: - - LICENSE_INCLUDED - - BRING_YOUR_OWN_LICENSE - type: string - lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' - type: string - networkAccess: - properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: - properties: - hostnamePrefix: - type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: - type: string - type: object - type: object - wallet: - properties: - name: - type: string - password: - properties: - k8sSecret: - description: "*********************** *\tSecret specs ***********************" - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - type: object - type: object - hardLink: - default: false - type: boolean - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - required: - - details - type: object - status: - description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase - properties: - allConnectionStrings: - items: - properties: - connectionStrings: - items: - properties: - connectionString: - type: string - tnsName: - type: string - type: object - type: array - tlsAuthentication: - type: string - required: - - connectionStrings - type: object - type: array - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - timeCreated: - type: string - walletExpiringDate: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.6.1 - name: cdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: CDB is the Schema for the cdbs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: CDBSpec defines the desired state of CDB - properties: - cdbAdminPwd: - description: Password for the CDB Administrator to manage PDB lifecycle - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - description: User in the root container with sysdba priviledges to manage PDB lifecycle - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - description: Name of the CDB - type: string - cdbTlsCrt: - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - description: DB server port - type: integer - dbServer: - description: Name of the DB server - type: string - dbTnsurl: - type: string - nodeSelector: - additionalProperties: - type: string - description: Node Selector for running the Pod - type: object - ordsImage: - description: ORDS Image Name - type: string - ordsImagePullPolicy: - description: ORDS Image Pull Policy - enum: - - Always - - Never - type: string - ordsImagePullSecret: - description: The name of the image pull secret in case of a private docker repository. - type: string - ordsPort: - description: ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. - type: integer - ordsPwd: - description: Password for user ORDS_PUBLIC_USER - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - description: Number of ORDS Containers to create - type: integer - serviceName: - description: Name of the CDB Service - type: string - sysAdminPwd: - description: Password for the CDB System Administrator - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - description: Password for the Web Server User - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - description: CDBStatus defines the observed state of CDB - properties: - msg: - description: Message - type: string - phase: - description: Phase of the CDB Resource - type: string - status: - description: CDB Resource Status - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: databaseobservers.observability.oracle.com -spec: - group: observability.oracle.com - names: - kind: DatabaseObserver - listKind: DatabaseObserverList - plural: databaseobservers - singular: databaseobserver - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: DatabaseObserver is the Schema for the databaseobservers API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: DatabaseObserverSpec defines the desired state of DatabaseObserver - properties: - database: - description: DatabaseObserverDatabase defines the database details used for DatabaseObserver - properties: - dbConnectionString: - properties: - key: - type: string - secret: - type: string - type: object - dbPassword: - properties: - key: - type: string - secret: - type: string - vaultOCID: - type: string - vaultSecretName: - type: string - type: object - dbUser: - properties: - key: - type: string - secret: - type: string - type: object - dbWallet: - properties: - key: - type: string - secret: - type: string - type: object - type: object - exporter: - description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver - properties: - configuration: - properties: - configmap: - description: ConfigMapDetails defines the configmap name - properties: - configmapName: - type: string - key: - type: string - type: object - type: object - image: - type: string - service: - description: DatabaseObserverService defines the exporter service component of DatabaseObserver - properties: - port: - format: int32 - type: integer - type: object - type: object - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - prometheus: - description: PrometheusConfig defines the generated resources for Prometheus - properties: - labels: - additionalProperties: - type: string - type: object - port: - type: string - type: object - replicas: - format: int32 - type: integer - type: object - status: - description: DatabaseObserverStatus defines the observed state of DatabaseObserver - properties: - conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - exporterConfig: - type: string - replicas: - type: integer - status: - type: string - required: - - conditions - - exporterConfig - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: DataguardBroker is the Schema for the dataguardbrokers API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: DataguardBrokerSpec defines the desired state of DataguardBroker - properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - description: FSFO strategy - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - description: DataguardBrokerStatus defines the observed state of DataguardBroker - properties: - clusterConnectString: - type: string - externalConnectString: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: dbcssystems.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DbcsSystem - listKind: DbcsSystemList - plural: dbcssystems - singular: dbcssystem - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: DbcsSystem is the Schema for the dbcssystems API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: DbcsSystemSpec defines the desired state of DbcsSystem - properties: - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - description: DB Backup COnfig Network Struct - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - sshPublicKeys - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - ociConfigMap: - type: string - ociSecret: - type: string - required: - - ociConfigMap - type: object - status: - description: DbcsSystemStatus defines the observed state of DbcsSystem - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbEdition: - type: string - dbInfo: - items: - description: DbcsSystemStatus defines the observed state of DbcsSystem - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: oraclerestdataservices.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestDataService - listKind: OracleRestDataServiceList - plural: oraclerestdataservices - singular: oraclerestdataservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: OracleRestDataService is the Schema for the oraclerestdataservices API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService - properties: - adminPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - apexPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - oracleService: - type: string - ordsPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - ordsUser: - type: string - persistence: - description: OracleRestDataServicePersistence defines the storage releated params - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - size: - type: string - storageClass: - type: string - volumeName: - type: string - type: object - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - description: OracleRestDataServicePDBSchemas defines the PDB Schemas to be ORDS Enabled - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: - type: string - urlMapping: - type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - required: - - adminPassword - - databaseRef - - ordsPassword - type: object - status: - description: OracleRestDataServiceStatus defines the observed state of OracleRestDataService - properties: - apexConfigured: - type: boolean - apexUrl: - type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: - type: string - databaseApiUrl: - type: string - databaseRef: - type: string - image: - description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: string - ordsInstalled: - type: boolean - replicas: - type: integer - serviceIP: - type: string - status: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.6.1 - name: pdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: PDB is the Schema for the pdbs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: PDBSpec defines the desired state of PDB - properties: - action: - description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR.' - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - description: The administrator username for the new PDB. This property is required when the Action property is Create. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - description: The administrator password for the new PDB. This property is required when the Action property is Create. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - description: Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. - type: boolean - cdbName: - description: Name of the CDB - type: string - cdbNamespace: - description: CDB Namespace - type: string - cdbResName: - description: Name of the CDB Custom Resource that runs the ORDS container - type: string - copyAction: - description: To copy files or not while cloning a PDB - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - description: Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - description: Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. - type: string - getScript: - description: Whether you need the script only or execute the script - type: boolean - modifyOption: - description: Extra options for opening and closing a PDB - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - description: The name of the new PDB. Relevant for both Create and Plug Actions. - type: string - pdbState: - description: The target state of the PDB - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - description: Whether to reuse temp file - type: boolean - sourceFileNameConversions: - description: This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. - type: string - sparseClonePath: - description: A Path specified for sparse clone snapshot copy. (Optional) - type: string - srcPdbName: - description: Name of the Source PDB from which to clone - type: string - tdeExport: - description: TDE export for unplug operations - type: boolean - tdeImport: - description: TDE import for plug operations - type: boolean - tdeKeystorePath: - description: TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - type: string - tdePassword: - description: TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - description: TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - description: Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - type: string - totalSize: - description: Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - type: string - unlimitedStorage: - description: Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. - type: boolean - webServerPwd: - description: Password for the Web ServerPDB User - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - description: XML metadata filename to be used for Plug or Unplug operations - type: string - required: - - action - type: object - status: - description: PDBStatus defines the observed state of PDB - properties: - action: - description: Last Completed Action - type: string - connString: - description: PDB Connect String - type: string - modifyOption: - description: Modify Option of the PDB - type: string - msg: - description: Message - type: string - openMode: - description: Open mode of the PDB - type: string - phase: - description: Phase of the PDB Resource - type: string - status: - description: PDB Resource Status - type: boolean - totalSize: - description: Total size of the PDB - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: shardingdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: ShardingDatabase - listKind: ShardingDatabaseList - plural: shardingdatabases - singular: shardingdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: ShardingDatabase is the Schema for the shardingdatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ShardingDatabaseSpec defines the desired state of ShardingDatabase - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - description: CatalogSpec defines the desired state of CatalogSpec - properties: - envVars: - items: - description: EnvironmentVariable represents a named variable accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - description: ResourceRequirements describes the compute resource requirements. - properties: - claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - description: Secret Details - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - gsm: - items: - description: GsmSpec defines the desired state of GsmSpec - properties: - directorName: - type: string - envVars: - description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. - items: - description: EnvironmentVariable represents a named variable accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - description: ResourceRequirements describes the compute resource requirements. - properties: - claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - description: Service Definition - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - description: ShardSpace Specs - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: boolean - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - description: PortMapping is a specification of port mapping for an application deployment. - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' - items: - description: ShardSpec is a specification of Shards for an application deployment. - properties: - deployAs: - type: string - envVars: - items: - description: EnvironmentVariable represents a named variable accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image - type: string - isDelete: - type: boolean - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - description: ResourceRequirements describes the compute resource requirements. - properties: - claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 ShardingDatabaseStatus defines the observed state of ShardingDatabase - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.6.1 - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.edition - name: Edition - type: string - - jsonPath: .status.sid - name: Sid - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version - type: string - - jsonPath: .status.connectString - name: Connect Str - type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.tcpsConnectString - name: TCPS Connect Str - type: string - - jsonPath: .status.tcpsPdbConnectString - name: TCPS Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.oemExpressUrl - name: Oem Express Url - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase - properties: - adminPassword: - description: SingleInsatnceAdminPassword defines the secret containing Admin Password mapped to secretKey for Database - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - archiveLog: - type: boolean - charset: - type: string - createAs: - enum: - - primary - - standby - - clone - type: string - dgBrokerConfigured: - type: boolean - edition: - enum: - - standard - - enterprise - - express - - free - type: string - enableTCPS: - type: boolean - flashBack: - type: boolean - forceLog: - type: boolean - image: - description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD - properties: - prebuiltDB: - type: boolean - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - listenerPort: - type: integer - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - pdbName: - type: string - persistence: - description: SingleInstanceDatabasePersistence defines the storage size and class for PVC - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - primaryDatabaseRef: - type: string - readinessCheckPeriod: - type: integer - replicas: - type: integer - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - sid: - description: SID must be alphanumeric (no special characters, only a-z, A-Z, 0-9), and no longer than 12 characters. - maxLength: 12 - pattern: ^[a-zA-Z0-9]+$ - type: string - tcpsCertRenewInterval: - type: string - tcpsListenerPort: - type: integer - tcpsTlsSecret: - type: string - required: - - image - type: object - status: - description: SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase - properties: - apexInstalled: - type: boolean - archiveLog: - type: string - certCreationTimestamp: - type: string - certRenewInterval: - type: string - charset: - type: string - clientWalletLoc: - type: string - clusterConnectString: - type: string - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - connectString: - type: string - createdAs: - type: string - datafilesCreated: - default: "false" - type: string - datafilesPatched: - default: "false" - type: string - dgBrokerConfigured: - type: boolean - edition: - type: string - flashBack: - type: string - forceLog: - type: string - initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - initPgaSize: - type: integer - initSgaSize: - type: integer - isTcpsEnabled: - default: false - type: boolean - nodes: - items: - type: string - type: array - oemExpressUrl: - type: string - ordsReference: - type: string - pdbConnectString: - type: string - pdbName: - type: string - persistence: - description: SingleInstanceDatabasePersistence defines the storage size and class for PVC - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - prebuiltDB: - type: boolean - primaryDatabase: - type: string - releaseUpdate: - type: string - replicas: - type: integer - role: - type: string - sid: - type: string - standbyDatabases: - additionalProperties: - type: string - type: object - status: - type: string - tcpsConnectString: - type: string - tcpsPdbConnectString: - type: string - tcpsTlsSecret: - default: "" - type: string - required: - - isTcpsEnabled - - persistence - - tcpsTlsSecret - type: object - type: object - served: true - storage: true - subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.replicas - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: oracle-database-operator-leader-election-role - namespace: oracle-database-operator-system -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: oracle-database-operator-manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - - deployments - - events - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list -- apiGroups: - - '''''' - resources: - - statefulsets/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - configmaps - - deployments - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - replicasets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - list - - update -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods/exec - verbs: - - create -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases/status - verbs: - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - cdbs/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - pdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - monitoring.coreos.com - resources: - - prometheusrules - - servicemonitors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/finalizers - verbs: - - update -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/status - verbs: - - get - - patch - - update -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-oracle-database-operator-proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: oracle-database-operator-leader-election-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-oracle-database-operator-proxy-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager-metrics-service - namespace: oracle-database-operator-system -spec: - ports: - - name: https - port: 8443 - targetPort: https - selector: - control-plane: controller-manager ---- -apiVersion: v1 -kind: Service -metadata: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - control-plane: controller-manager ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: oracle-database-operator-serving-cert - namespace: oracle-database-operator-system -spec: - dnsNames: - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local - issuerRef: - kind: Issuer - name: oracle-database-operator-selfsigned-issuer - secretName: webhook-server-cert ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: oracle-database-operator-selfsigned-issuer - namespace: oracle-database-operator-system -spec: - selfSigned: {} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-mutating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-cdb - failurePolicy: Fail - name: mcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: mdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: moraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-pdb - failurePolicy: Fail - name: mpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: msingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - singleinstancedatabases - sideEffects: None ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-validating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase - failurePolicy: Fail - name: vautonomouscontainerdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomouscontainerdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: vautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: vautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore - failurePolicy: Fail - name: vautonomousdatabaserestore.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabaserestores - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-cdb - failurePolicy: Fail - name: vcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: vdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: voraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-pdb - failurePolicy: Fail - name: vpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: vsingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - singleinstancedatabases - sideEffects: None ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager - namespace: oracle-database-operator-system -spec: - replicas: 3 - selector: - matchLabels: - control-plane: controller-manager - template: - metadata: - labels: - control-plane: controller-manager - spec: - containers: - - args: - - --enable-leader-election - command: - - /manager - env: - - name: WATCH_NAMESPACE - value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:sharding-operator - imagePullPolicy: Always - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - resources: - limits: - cpu: 400m - memory: 400Mi - requests: - cpu: 400m - memory: 400Mi - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - terminationGracePeriodSeconds: 10 - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert ---- diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index fb87ac5b..62a2dc29 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -663,7 +663,18 @@ spec: type: object spec: properties: - details: + action: + enum: + - "" + - Create + - Sync + - Update + - Stop + - Start + - Terminate + - Clone + type: string + clone: properties: adminPassword: properties: @@ -674,26 +685,36 @@ spec: type: object ociSecret: properties: - ocid: + id: type: string type: object type: object autonomousContainerDatabase: properties: - k8sACD: + k8sAcd: properties: name: type: string type: object - ociACD: + ociAcd: properties: - ocid: + id: type: string type: object type: object - autonomousDatabaseOCID: + cloneType: + enum: + - FULL + - METADATA + type: string + compartmentId: type: string - compartmentOCID: + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU type: string cpuCoreCount: type: integer @@ -716,64 +737,125 @@ spec: additionalProperties: type: string type: object + isAccessControlEnabled: + type: boolean isAutoScalingEnabled: type: boolean isDedicated: type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean licenseModel: enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string - lifecycleState: + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: type: string - networkAccess: + whitelistedIps: + items: + type: string + type: array + type: object + details: + properties: + adminPassword: properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: + k8sSecret: properties: - hostnamePrefix: + name: type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: + type: object + ociSecret: + properties: + id: type: string type: object type: object - wallet: + autonomousContainerDatabase: properties: - name: - type: string - password: + k8sAcd: properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object + name: + type: string + type: object + ociAcd: + properties: + id: + type: string type: object type: object + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + id: + type: string + isAccessControlEnabled: + type: boolean + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: + type: string + whitelistedIps: + items: + type: string + type: array type: object hardLink: default: false @@ -785,7 +867,26 @@ spec: secretName: type: string type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + type: object required: + - action - details type: object status: @@ -896,7 +997,18 @@ spec: type: object spec: properties: - details: + action: + enum: + - "" + - Create + - Sync + - Update + - Stop + - Start + - Terminate + - Clone + type: string + clone: properties: adminPassword: properties: @@ -907,26 +1019,36 @@ spec: type: object ociSecret: properties: - ocid: + id: type: string type: object type: object autonomousContainerDatabase: properties: - k8sACD: + k8sAcd: properties: name: type: string type: object - ociACD: + ociAcd: properties: - ocid: + id: type: string type: object type: object - autonomousDatabaseOCID: + cloneType: + enum: + - FULL + - METADATA type: string - compartmentOCID: + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU type: string cpuCoreCount: type: integer @@ -949,64 +1071,125 @@ spec: additionalProperties: type: string type: object + isAccessControlEnabled: + type: boolean isAutoScalingEnabled: type: boolean isDedicated: type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean licenseModel: enum: - LICENSE_INCLUDED - BRING_YOUR_OWN_LICENSE type: string - lifecycleState: + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: type: string - networkAccess: + whitelistedIps: + items: + type: string + type: array + type: object + details: + properties: + adminPassword: properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: + k8sSecret: properties: - hostnamePrefix: + name: type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: + type: object + ociSecret: + properties: + id: type: string type: object type: object - wallet: + autonomousContainerDatabase: properties: - name: - type: string - password: + k8sAcd: properties: - k8sSecret: - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object + name: + type: string + type: object + ociAcd: + properties: + id: + type: string type: object type: object + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + id: + type: string + isAccessControlEnabled: + type: boolean + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: + type: string + whitelistedIps: + items: + type: string + type: array type: object hardLink: default: false @@ -1018,7 +1201,26 @@ spec: secretName: type: string type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + type: object required: + - action - details type: object status: @@ -1182,7 +1384,7 @@ spec: type: object cdbName: type: string - cdbTlsCrt: + cdbOrdsPrvKey: properties: secret: properties: @@ -1197,7 +1399,7 @@ spec: required: - secret type: object - cdbTlsKey: + cdbOrdsPubKey: properties: secret: properties: @@ -1212,26 +1414,56 @@ spec: required: - secret type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - ordsImage: - type: string - ordsImagePullPolicy: - enum: - - Always - - Never - type: string - ordsImagePullSecret: + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + ordsImage: + type: string + ordsImagePullPolicy: + enum: + - Always + - Never + type: string + ordsImagePullSecret: type: string ordsPort: type: integer @@ -1330,6 +1562,9 @@ spec: kind: DatabaseObserver listKind: DatabaseObserverList plural: databaseobservers + shortNames: + - dbobserver + - dbobservers singular: databaseobserver scope: Namespaced versions: @@ -1340,7 +1575,10 @@ spec: - jsonPath: .status.status name: Status type: string - name: v1alpha1 + - jsonPath: .status.version + name: Version + type: string + name: v1 schema: openAPIV3Schema: properties: @@ -1352,6 +1590,16 @@ spec: type: object spec: properties: + configuration: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + type: object database: properties: dbConnectionString: @@ -1389,23 +1637,86 @@ spec: type: object exporter: properties: - configuration: + deployment: properties: - configmap: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podTemplate: properties: - configmapName: - type: string - key: - type: string + labels: + additionalProperties: + type: string + type: object type: object type: object - image: - type: string service: properties: - port: - format: int32 - type: integer + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object type: object type: object ociConfig: @@ -1417,150 +1728,6545 @@ spec: type: object prometheus: properties: - labels: - additionalProperties: - type: string + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string + scopes: + items: + type: string + type: array + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + enum: + - http + - https + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + trackTimestampsStaleness: + type: boolean + type: object + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object type: object - port: - type: string type: object replicas: format: int32 type: integer - type: object - status: - properties: - conditions: + sidecarVolumes: items: properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - exporterConfig: - type: string - replicas: - type: integer - status: - type: string - required: - - conditions - - exporterConfig - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.fastStartFailover - name: FSFO - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - fastStartFailover: - type: boolean - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + configuration: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podTemplate: + properties: + labels: + additionalProperties: + type: string + type: object + type: object + type: object + service: + properties: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string + scopes: + items: + type: string + type: array + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + enum: + - http + - https + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + trackTimestampsStaleness: + type: boolean + type: object + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object + type: object + type: object + replicas: + format: int32 + type: integer + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + configuration: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podTemplate: + properties: + labels: + additionalProperties: + type: string + type: object + type: object + type: object + service: + properties: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string + scopes: + items: + type: string + type: array + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + enum: + - http + - https + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + trackTimestampsStaleness: + type: boolean + type: object + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object + type: object + type: object + replicas: + format: int32 + type: integer + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: dataguardbrokers.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + singular: dataguardbroker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.fastStartFailover + name: FSFO + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailover: + type: boolean + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: + type: string + databasesInDataguardConfig: + additionalProperties: + type: string + type: object + externalConnectString: + type: string + fastStartFailover: + type: boolean + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.fastStartFailover + name: FSFO + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailover: + type: boolean + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array required: - primaryDatabaseRef - protectionMode @@ -3079,6 +9785,181 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + readinessCheckPeriod: + type: integer + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + mongoDbApi: + type: boolean + mongoDbApiAccessUrl: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + - jsonPath: .status.mongoDbApiAccessUrl + name: MongoDbApi Access URL + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + mongoDbApi: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + ordsUser: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + setWritePermissions: + type: boolean size: type: string storageClass: @@ -3086,6 +9967,8 @@ spec: volumeName: type: string type: object + readinessCheckPeriod: + type: integer replicas: minimum: 1 type: integer @@ -3773,6 +10656,36 @@ spec: type: string pdbName: type: string + pdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object pdbState: enum: - OPEN @@ -4021,6 +10934,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -4132,6 +11047,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -4354,6 +11271,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -4561,6 +11480,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -4672,6 +11593,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -4806,164 +11729,451 @@ spec: type: string isClone: type: boolean - isDataGuard: + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: string + liveinessCheckPeriod: + type: integer + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + enum: + - enable + - disable + - failed + - force + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string + topicId: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + convertToSnapshotStandby: type: boolean - isDebug: + createAs: + enum: + - primary + - standby + - clone + - truecache + type: string + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: type: boolean - isDeleteOraPvc: + flashBack: type: boolean - isDownloadScripts: + forceLog: type: boolean - isExternalSvc: + image: + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: type: boolean - isTdeWallet: + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: type: string - liveinessCheckPeriod: - type: integer - portMappings: - items: - properties: - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array readinessCheckPeriod: type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - items: - properties: - deployAs: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - type: string - isDelete: - enum: - - enable - - disable - - failed - - force - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - properties: - claims: - items: - properties: - name: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: + replicas: + type: integer + resources: + properties: + limits: + properties: + cpu: + type: string + memory: + type: string + type: object + requests: + properties: + cpu: + type: string + memory: + type: string + type: object + type: object + serviceAccountName: type: string - shardConfigName: + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ type: string - shardRegion: + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + trueCacheServices: items: type: string type: array - shardingType: + required: + - image + type: object + status: + properties: + apexInstalled: + type: boolean + archiveLog: type: string - stagePvcName: + certCreationTimestamp: type: string - storageClass: + certRenewInterval: type: string - tdeWalletPvc: + charset: type: string - tdeWalletPvcMountLocation: + clientWalletLoc: type: string - topicId: + clusterConnectString: type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - properties: - catalogs: - additionalProperties: - type: string - type: object conditions: items: properties: @@ -5003,52 +12213,114 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - gsm: + connectString: + type: string + convertToSnapshotStandby: + type: boolean + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBroker: + type: string + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany type: string - internalConnectStr: + datafilesVolumeName: type: string - services: + scriptsVolumeName: type: string - shards: - additionalProperties: - type: string - type: object - state: + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: type: string type: object - shards: + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: additionalProperties: type: string type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret type: object type: object served: true - storage: true + storage: false subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - additionalPrinterColumns: - jsonPath: .status.edition name: Edition @@ -5083,7 +12355,7 @@ spec: - jsonPath: .status.oemExpressUrl name: Oem Express Url type: string - name: v1alpha1 + name: v4 schema: openAPIV3Schema: properties: @@ -5118,6 +12390,7 @@ spec: - primary - standby - clone + - truecache type: string edition: enum: @@ -5225,6 +12498,10 @@ spec: type: integer tcpsTlsSecret: type: string + trueCacheServices: + items: + type: string + type: array required: - image type: object @@ -5817,26 +13094,6 @@ webhooks: resources: - autonomousdatabasebackups sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabasev4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -5981,26 +13238,6 @@ webhooks: resources: - autonomousdatabasebackups sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabasev1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -6110,14 +13347,14 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /mutate-observability-oracle-com-v1alpha1-databaseobserver + path: /mutate-observability-oracle-com-v4-databaseobserver failurePolicy: Fail name: mdatabaseobserver.kb.io rules: - apiGroups: - observability.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE @@ -6192,26 +13429,6 @@ webhooks: resources: - autonomousdatabaserestores sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-autonomousdatabase - failurePolicy: Fail - name: vautonomousdatabasev4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -6488,14 +13705,14 @@ webhooks: service: name: oracle-database-operator-webhook-service namespace: oracle-database-operator-system - path: /validate-observability-oracle-com-v1alpha1-databaseobserver + path: /validate-observability-oracle-com-v4-databaseobserver failurePolicy: Fail name: vdatabaseobserver.kb.io rules: - apiGroups: - observability.oracle.com apiVersions: - - v1alpha1 + - v4 operations: - CREATE - UPDATE diff --git a/ords/Dockerfile b/ords/Dockerfile index 772a7e6d..827a115b 100644 --- a/ords/Dockerfile +++ b/ords/Dockerfile @@ -40,7 +40,9 @@ FROM container-registry.oracle.com/java/jdk:latest # ------------------------------------------------------------- ENV ORDS_HOME=/opt/oracle/ords/ \ RUN_FILE="runOrdsSSL.sh" \ - ORDSVERSION=23.4.0-8 + ORDSVERSION=23.4.0-8 \ + JAVA=17 +#see https://www.oracle.com/tools/ords/ords-relnotes-23.4.0.html # Copy binaries # ------------- @@ -48,7 +50,7 @@ COPY $RUN_FILE $ORDS_HOME RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && \ yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && \ - yum -y install java-11-openjdk-devel && \ + yum -y install java-$JAVA-openjdk-devel && \ yum -y install iproute && \ yum clean all @@ -64,17 +66,22 @@ RUN mkdir -p $ORDS_HOME/doc_root && \ chmod ug+x $ORDS_HOME/*.sh && \ groupadd -g 54322 dba && \ usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && \ - chown -R oracle:dba $ORDS_HOME && \ - echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + chown -R oracle:dba $ORDS_HOME +# echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +RUN echo "unset R1" >> /home/oracle/.bashrc && \ + chown root:root /home/oracle/.bashrc && chmod +r /home/oracle/.bashrc # Finalize setup # ------------------- USER oracle WORKDIR /home/oracle + VOLUME ["$ORDS_HOME/config/ords"] EXPOSE 8888 # Define default command to start Ords Services CMD $ORDS_HOME/$RUN_FILE + diff --git a/ords/runOrdsSSL.sh b/ords/runOrdsSSL.sh index 35f1b77b..07e2b931 100644 --- a/ords/runOrdsSSL.sh +++ b/ords/runOrdsSSL.sh @@ -106,36 +106,42 @@ function setupOrds() { echo "====================================================" echo CONFIG=$CONFIG +echo $R1|sed 's/-----BEGIN PRIVATE KEY-----/-----BEGIN PRIVATE KEY-----\n/g'|\ + sed 's/-----END PRIVATE KEY-----/\n-----END PRIVATE KEY-----/' > $ORDS_HOME/k.txt + + export ORDS_LOGS=/tmp [ -f $ORDS_HOME/secrets/$WEBSERVER_USER_KEY ] && { - WEBSERVER_USER=`cat $ORDS_HOME/secrets/$WEBSERVER_USER_KEY` + WEBSERVER_USER=$(cat /opt/oracle/ords/secrets/${WEBSERVER_USER_KEY}|base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) } [ -f $ORDS_HOME/secrets/$WEBSERVER_PASSWORD_KEY ] && { - WEBSERVER_PASSWORD=`cat $ORDS_HOME/secrets/$WEBSERVER_PASSWORD_KEY` + WEBSERVER_PASSWORD=$(cat /opt/oracle/ords/secrets/${WEBSERVER_PASSWORD_KEY}|base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) } [ -f $ORDS_HOME/secrets/$CDBADMIN_USER_KEY ] && { - CDBADMIN_USER=`cat $ORDS_HOME/secrets/$CDBADMIN_USER_KEY` + CDBADMIN_USER=$(cat /opt/oracle/ords/secrets/${CDBADMIN_USER_KEY} | base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) } [ -f $ORDS_HOME/secrets/$CDBADMIN_PWD_KEY ] && { - CDBADMIN_PWD=`cat $ORDS_HOME/secrets/$CDBADMIN_PWD_KEY` + CDBADMIN_PWD=$(cat /opt/oracle/ords/secrets/${CDBADMIN_PWD_KEY} | base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) } [ -f $ORDS_HOME/secrets/$ORACLE_PWD_KEY ] && { - SYSDBA_PASSWORD=`cat $ORDS_HOME/secrets/$ORACLE_PWD_KEY` + #SYSDBA_PASSWORD=`cat $ORDS_HOME/secrets/$ORACLE_PWD_KEY` + SYSDBA_PASSWORD=$(cat $ORDS_HOME/secrets/${ORACLE_PWD_KEY} | base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) } [ -f $ORDS_HOME/secrets/$ORACLE_PWD_KEY ] && { - ORDS_PASSWORD=`cat $ORDS_HOME/secrets/$ORDS_PWD_KEY` + #ORDS_PASSWORD=`cat $ORDS_HOME/secrets/$ORDS_PWD_KEY` + ORDS_PASSWORD=$(cat $ORDS_HOME/secrets/${ORDS_PWD_KEY} | base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) } @@ -151,6 +157,7 @@ ${SYSDBA_PASSWORD:-PROVIDE_A_PASSWORD} ${ORDS_PASSWORD:-PROVIDE_A_PASSWORD} EOF +rm $ORDS_HOME/k.txt if [ $? -ne 0 ] From 78b5ba0b6f20f10e93041514797f51db78cee457 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Mon, 27 Jan 2025 11:58:11 +0000 Subject: [PATCH 163/414] Update .gitlab-ci.yml --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7a5c3431..c194a98a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,6 +9,7 @@ build-operator: - export PATH="${GOROOT}/bin:${PATH}" - make operator-yaml IMG=$IMAGE - if [ "$CI_COMMIT_BRANCH" = "master" ]; then + podman run --rm --privileged multiarch/qemu-user-static --reset -p yes; make docker-build docker-push IMG="$IMAGE" BUILD_MANIFEST=true; docker manifest rm "$IMAGE"; else From df178ed2b60403f5b582979cededaee882337046 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Mon, 27 Jan 2025 12:22:33 +0000 Subject: [PATCH 164/414] Update .gitlab-ci.yml --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c194a98a..cac081cd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,7 +17,7 @@ build-operator: docker rmi "$IMAGE"; sed -i "s/\(replicas.\) 3/\1 1/g" ./$OP_YAML; fi - - buildah containers -q | xargs -n1 buildah rm + - buildah containers -q | xargs -n1 buildah rm || true - podman system prune -f - curl -s --netrc-file $HOME/.netrc_gitlab $ARTIFACTORY_REPO/$CI_COMMIT_BRANCH/$OP_YAML -T ./$OP_YAML only: From 702e90e91d8ba6da231dbad19dac0c479dc927ad Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Mon, 27 Jan 2025 13:28:30 +0000 Subject: [PATCH 165/414] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 72c21aa9..b9755e6f 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ BUILDER_IMG = golang:$(GOLANG_VERSION) BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO="false" --build-arg GOLANG_VERSION=$(GOLANG_VERSION) endif ifeq ($(BUILD_MANIFEST), true) -BUILD_ARGS := $(BUILD_ARGS) --platform=linux/arm64,linux/amd64 --manifest +BUILD_ARGS := $(BUILD_ARGS) --platform=linux/arm64,linux/amd64 --jobs=2 --manifest PUSH_ARGS := manifest else BUILD_ARGS := $(BUILD_ARGS) --platform=linux/amd64 --tag From 63519253c1e4921af09f0f860a5325ab3e31ef10 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Wed, 29 Jan 2025 16:19:28 +0000 Subject: [PATCH 166/414] doc typo correction --- docs/multitenant/ords-based/README.md | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/multitenant/ords-based/README.md b/docs/multitenant/ords-based/README.md index 7a988bd5..70bbd576 100644 --- a/docs/multitenant/ords-based/README.md +++ b/docs/multitenant/ords-based/README.md @@ -2,28 +2,28 @@ # Oracle Multitenant Database Controllers -The Oracle Database Operator for kubernetes uses two controllers to manage [Pluggable Database life cycle][oradocpdb] +The Oracle Database Operator for Kubernetes uses two controllers to manage [Pluggable Database life cycle][oradocpdb] - CDB controller - PDB controller -By usigng CDB/PDB controllers you can perform the following actions **CREATE**,**MODIFY(OPEN/COSE)**,**DELETE**,**CLONE**,**PLUG** and **UNPLUG** against pluggable database +By using CDB/PDB controllers you can perform the following actions **CREATE**, **MODIFY(OPEN/COSE)**, **DELETE**, **CLONE**, **PLUG** and **UNPLUG** against pluggable database Examples are located under the following directories: -- [Usecase](./usecase/) and [usecase01] directories contain a [configuration file](./usecase/parameters.txt) where you can specify all the details of your environment. A [makefile](./usecase/makefile) takes this file in input to generate the all the yaml files. There is no need to edit yaml files one by one. +- [Usecase](./usecase/) and [usecase01](./usecase01/) directories contain a [configuration file](./usecase/parameters.txt) where you can specify all the details of your environment. A [makefile](./usecase/makefile) takes this file in input to generate the all the yaml files. There is no need to edit yaml files one by one. - [Singlenamespace provisioning](./provisioning/singlenamespace/) You will find base sample files to manage pdb and cdb within a single namespace - [Multinamespace provisioning](./provisioning/multinamespace/) You will find base sample files to manage pdb and cdb in different namespaces. - [Usecase01](./usecase01/README.md) [Usecase02](./usecase02/README.md) contain other step by step examples; -Authomatic yaml generation is not available for directory usecase02 and provisioning directories +Automatic yaml generation is not available for directory usecase02 and provisioning directories -**NOTE** that the cdb controller is not intended to manage the container database. The cdb controller is meant to provied a pod with a rest server connected to the container database. +**NOTE** cdb controller is not intended to manage the container database. The CDB controller is meant to provide a pod with a rest server connected to the container database to manage PDBs. ## Macro steps for setup -- Deply the Oracle Database Operator +- Deploy the Oracle Database Operator - [Create Ords based image for CDB pod](./provisioning/ords_image.md) - [Container RDBMB user creation](#prepare-the-container-database-for-pdb-lifecycle-management-pdb-lm) - Create certificates for https connection @@ -84,7 +84,7 @@ singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z ``` -## Prerequsites to manage PDB Life Cycle using Oracle DB Operator Multitenant Database Controller +## Prerequisites to manage PDB Life Cycle using Oracle DB Operator Multitenant Database Controller * [Prepare the container database for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) * [Oracle REST Data Service or ORDS Image](#oracle-rest-data-service-ords-image) @@ -96,9 +96,9 @@ singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z Pluggable Database (PDB) management operations are performed in the Container Database (CDB). These operations include create, clone, plug, unplug, delete, modify and map pdb. -You cannot have an ORDS-enabled schema in the container database. To perform the PDB lifecycle management operations, you must first use the following steps to define the default CDB administrator credentials on target CDBs: +To perform PDB lifecycle management operations, you must first use the following steps to define the default CDB administrator credentials on target CDBs: -Create the CDB administrator user, and grant the required privileges. In this example, the user is `C##DBAPI_CDB_ADMIN`. However, any suitable common user name can be used. +Create the CDB administrator user and grant the required privileges. In this example, the user is `C##DBAPI_CDB_ADMIN`. However, any suitable common username can be used. ```SQL SQL> conn /as sysdba @@ -120,7 +120,7 @@ col account_status for a30 select username, account_status from dba_users where username in ('ORDS_PUBLIC_USER','C##DBAPI_CDB_ADMIN','APEX_PUBLIC_USER','APEX_REST_PUBLIC_USER'); ``` -## OCI OKE(Kubernetes Cluster) +## OCI OKE (Kubernetes Cluster) You can use an [OKE in Oracle Cloud Infrastructure][okelink] to configure the controllers for PDB lifecycle management. **Note that there is no restriction about container database location; it can be anywhere (on cloud or on-premises).** To quickly create an OKE cluster in your OCI cloud environment you can use the following [link](./provisioning/quickOKEcreation.md). @@ -136,11 +136,11 @@ In this setup example [provisioning example setup](./provisioning/example_setup_ Multitenant Controllers use Kubernetes secrets to store the required credential and https certificates. - **Note** In multi namespace enviroment you have to create specific secrets for each namespaces + **Note** In multi namespace environment you have to create specific secrets for each namespaces ### Secrets for CERTIFICATES -Create the certificates and key on your local host, and use them to create the Kubernetes secret. +Create the certificates and key on your local host and use them to create the Kubernetes secret. ```bash openssl genrsa -out ca.key 2048 @@ -157,11 +157,11 @@ kubectl create secret generic db-ca --from-file=ca.crt -n oracle-database-operat image_not_found -**Note:** On successful creation of the certificates secret creation remove files or move to secure storage . +**Note:** Remove temporary files after successfful secret creation. ### Secrets for CDB CRD - **Note:** base64 encoded secrets are no longer supported ; use openssl secrets as documented in the following section. After successful creation of the CDB Resource, the CDB and PDB secrets can be deleted from the Kubernetes system. Don't leave plaintext files containing sensitive data on disk. After loading the secret, remove the plaintext file or move it to secure storage. + **Note:** base64 encoded secrets are no longer supported; use openssl secrets as documented in the following section. After successful creation of the CDB Resource, the CDB and PDB secrets can be deleted from the Kubernetes system. Don't leave plaintext files containing sensitive data on disk. After loading the secret, remove the plaintext file or move it to secure storage. ```bash @@ -357,13 +357,13 @@ kubectl apply -f pdb_create.yaml ## Known issues - - Ords installatian failure if pluaggable databases in the container db are not opened + - Ords installation failure if pluaggable databases in the container db are not opened - - Version 1.1.0: encoded password for https authentication may include carriege return as consequence the https request fails with http 404 error. W/A generate encoded password using **printf** instead of **echo**. + - Version 1.1.0: encoded password for https authentication may include carriage return as consequence the https request fails with http 404 error. W/A generate encoded password using **printf** instead of **echo**. - - pdb controller authentication suddenly failes without any system change. Check the certificate expiration date **openssl .... -days 365** + - pdb controller authentication suddenly fails without any system change. Check the certificate expiration date **openssl .... -days 365** - - Nothing happens after cdb yaml file applying: Make sure to have properly configure the WHATCH_NAMESPACE list in the operator yaml file + - Nothing happens after applying cdb yaml files: Make sure to have properly configured the WHATCH_NAMESPACE list in the operator yaml file [okelink]:https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm From c25d92cf0526eb22e7ba3330fb0ebc1cf67864e8 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Wed, 29 Jan 2025 21:18:04 +0000 Subject: [PATCH 167/414] Fix typos in ADB README.md --- docs/adb/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/adb/README.md b/docs/adb/README.md index c7e57527..34247ff8 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -96,6 +96,7 @@ Follow these steps to provision an Autonomous Database that will map objects in metadata: name: autonomousdatabase-sample spec: + action: Create details: compartmentId: ocid1.compartment... dbName: NewADB @@ -155,8 +156,8 @@ The operator also generates the `AutonomousBackup` custom resources if a databas metadata: name: autonomousdatabase-sample spec: + action: Sync details: - action: Sync id: ocid1.autonomousdatabase... ociConfig: configMapName: oci-cred @@ -348,9 +349,9 @@ To use the secret in a deployment, refer to [Using Secrets](https://kubernetes.i To start, stop, or terminate a database, use the `action` attribute. Here's a list of the values you can set for `action`: -* `START`: to start the database -* `STOP`: to stop the database -* `TERMINATE`: to terminate the database +* `Start`: to start the database +* `Stop`: to stop the database +* `Terminate`: to terminate the database 1. An example .yaml file is available here: [config/samples/adb/autonomousdatabase_stop_start_terminate.yaml](./../../config/samples/adb/autonomousdatabase_stop_start_terminate.yaml) @@ -361,7 +362,7 @@ Here's a list of the values you can set for `action`: metadata: name: autonomousdatabase-sample spec: - action: STOP + action: Stop details: id: ocid1.autonomousdatabase... ociConfig: From 09cc9522ab44766891a66c57d70c03b7a54740eb Mon Sep 17 00:00:00 2001 From: ting-lan-wang Date: Thu, 30 Jan 2025 16:59:25 -0500 Subject: [PATCH 168/414] Update README.md --- docs/adb/README.md | 54 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/docs/adb/README.md b/docs/adb/README.md index c7e57527..4a7c0f76 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -73,18 +73,30 @@ Follow these steps to provision an Autonomous Database that will map objects in | `spec.details.compartmentId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the compartment of the Autonomous Database. | Yes | | `spec.details.dbName` | string | The database name. The name must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. | Yes | | `spec.details.displayName` | string | The user-friendly name for the Autonomous Database. The name does not have to be unique. | Yes | - | `spec.details.cpuCoreCount` | int | The number of OCPU cores to be made available to the database. | Yes | + | `spec.details.dbWorkload` | string | The Autonomous Database workload type. The following values are valid:
`OLTP` - indicates an Autonomous Transaction Processing database
`DW` - indicates an Autonomous Data Warehouse database
`AJD` - indicates an Autonomous JSON Database
`APEX` - indicates an Autonomous Database with the Oracle APEX Application Development workload type.
This cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMtlsConnectionRequired, privateEndpointLabel, nsgIds, dbVersion, dbName, or isFreeTier. | No | + | `spec.details.licenseModel` | string | The Oracle license model that applies to the Oracle Autonomous Database. Bring your own license (BYOL) allows you to apply your current on-premises Oracle software licenses to equivalent, highly automated Oracle services in the cloud.License Included allows you to subscribe to new Oracle Database software licenses and the Oracle Database service. Note that when provisioning an [Autonomous Database on dedicated Exadata infrastructure](https://docs.oracle.com/en/cloud/paas/autonomous-database/index.html), this attribute must be null. It is already set at the Autonomous Exadata Infrastructure level. When provisioning an [Autonomous Database Serverless ](https://docs.oracle.com/en/cloud/paas/autonomous-database/index.html) database, if a value is not specified, the system defaults the value to `BRING_YOUR_OWN_LICENSE`. Bring your own license (BYOL) also allows you to select the DB edition using the optional parameter.
This cannot be updated in parallel with any of the following: cpuCoreCount, computeCount, dataStorageSizeInTBs, adminPassword, isMtlsConnectionRequired, dbWorkload, privateEndpointLabel, nsgIds, dbVersion, dbName, or isFreeTier. | No | + | `spec.details.dbVersion` | string | A valid Oracle Database version for Autonomous Database. | No | + | `spec.details.dataStorageSizeInTBs` | int | The size, in terabytes, of the data volume that will be created and attached to the database. This storage can later be scaled up if needed. For Autonomous Databases on dedicated Exadata infrastructure, the maximum storage value is determined by the infrastructure shape. See Characteristics of [Infrastructure Shapes](https://www.oracle.com/pls/topic/lookup?ctx=en/cloud/paas/autonomous-database&id=ATPFG-GUID-B0F033C1-CC5A-42F0-B2E7-3CECFEDA1FD1) for shape details. A full Exadata service is allocated when the Autonomous Database size is set to the upper limit (384 TB). | No | + | `spec.details.cpuCoreCount` | int | The number of CPU cores to be made available to the database. For Autonomous Databases on dedicated Exadata infrastructure, the maximum number of cores is determined by the infrastructure shape. See [Characteristics of Infrastructure Shapes](https://www.oracle.com/pls/topic/lookup?ctx=en/cloud/paas/autonomous-database&id=ATPFG-GUID-B0F033C1-CC5A-42F0-B2E7-3CECFEDA1FD1) for shape details.
**Note:** This parameter cannot be used with the `ocpuCount` parameter. | Conditional | + | `spec.details.computeModel` | string | The compute model of the Autonomous Database. This is required if using the `computeCount` parameter. If using `cpuCoreCount` then it is an error to specify `computeModel` to a non-null value. ECPU compute model is the recommended model and OCPU compute model is legacy. | Conditional | + | `spec.details.computeCount` | float32 | The compute amount (CPUs) available to the database. Minimum and maximum values depend on the compute model and whether the database is an Autonomous Database Serverless instance or an Autonomous Database on Dedicated Exadata Infrastructure.
For an Autonomous Database Serverless instance, the 'ECPU' compute model requires a minimum value of one, for databases in the elastic resource pool and minimum value of two, otherwise. Required when using the `computeModel` parameter. When using `cpuCoreCount` parameter, it is an error to specify computeCount to a non-null value. Providing `computeModel` and `computeCount` is the preferred method for both OCPU and ECPU. | Conditional | + | `spec.details.ocpuCount` | float32 | The number of OCPU cores to be made available to the database.
The following points apply:
- For Autonomous Databases on Dedicated Exadata infrastructure, to provision less than 1 core, enter a fractional value in an increment of 0.1. For example, you can provision 0.3 or 0.4 cores, but not 0.35 cores. (Note that fractional OCPU values are not supported for Autonomous Database Serverless instances.)
- To provision 1 or more cores, you must enter an integer between 1 and the maximum number of cores available for the infrastructure shape. For example, you can provision 2 cores or 3 cores, but not 2.5 cores. This applies to an Autonomous Database Serverless instance or an Autonomous Database on Dedicated Exadata Infrastructure.
- For Autonomous Database Serverless instances, this parameter is not used.
For Autonomous Databases on Dedicated Exadata infrastructure, the maximum number of cores is determined by the infrastructure shape. See [Characteristics of Infrastructure Shapes](https://www.oracle.com/pls/topic/lookup?ctx=en/cloud/paas/autonomous-database&id=ATPFG-GUID-B0F033C1-CC5A-42F0-B2E7-3CECFEDA1FD1) for shape details.
**Note:** This parameter cannot be used with the `cpuCoreCount` parameter. | Conditional | | `spec.details.adminPassword` | dictionary | The password for the ADMIN user. The password must be between 12 and 30 characters long, and must contain at least 1 uppercase, 1 lowercase, and 1 numeric character. It cannot contain the double quote symbol (") or the username "admin", regardless of casing.

Either `k8sSecret.name` or `ociSecret.id` must be provided. If both `k8sSecret.name` and `ociSecret.id` appear, the Operator reads the password from the K8s secret that `k8sSecret.name` refers to. | Yes | | `spec.details.adminPassword.k8sSecret.name` | string | The **name** of the K8s Secret where you want to hold the password for the ADMIN user. | Conditional | |`spec.details.adminPassword.ociSecret.id` | string | The **[OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm)** of the [OCI Secret](https://docs.oracle.com/en-us/iaas/Content/KeyManagement/Tasks/managingsecrets.htm) where you want to hold the password for the ADMIN user. | Conditional | | `spec.details.dataStorageSizeInTBs` | int | The size, in terabytes, of the data volume that will be created and attached to the database. This storage can later be scaled up if needed. | Yes | | `spec.details.isAutoScalingEnabled` | boolean | Indicates if auto scaling is enabled for the Autonomous Database OCPU core count. The default value is `FALSE` | No | | `spec.details.isDedicated` | boolean | True if the database is on dedicated [Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adbddoverview.htm). `spec.details.autonomousContainerDatabase.k8sACD.name` or `spec.details.autonomousContainerDatabase.ociACD.id` has to be provided if the value is true. | No | + | `spec.details.isFreeTier` | boolean | Indicates if this is an Always Free resource. The default value is false. Note that Always Free Autonomous Databases have 1 CPU and 20GB of memory. For Always Free databases, memory and CPU cannot be scaled.
This cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMtlsConnectionRequired, privateEndpointLabel, nsgIds, dbVersion, or dbName. | No | + | `spec.details.isAccessControlEnabled` | boolean | Indicates if the database-level access control is enabled.
If disabled, database access is defined by the network security rules.
If enabled, database access is restricted to the IP addresses defined by the rules specified with the `whitelistedIps` property. While specifying `whitelistedIps` rules is optional, if database-level access control is enabled and no rules are specified, the database will become inaccessible.
When creating a database clone, the desired access control setting should be specified. By default, database-level access control will be disabled for the clone.
This property is applicable only to Autonomous Databases on the Exadata Cloud@Customer platform. For Autonomous Database Serverless instances, `whitelistedIps` is used. | No | + | `spec.details.whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for [Autonomous Database Serverless](https://docs.oracle.com/en/cloud/paas/autonomous-database/index.html) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.
If `arePrimaryWhitelistedIpsUsed` is 'TRUE' then Autonomous Database uses this primary's IP access control list (ACL) for the disaster recovery peer called `standbywhitelistedips`.
For Autonomous Database Serverless, this is an array of CIDR (classless inter-domain routing) notations for a subnet or VCN OCID (virtual cloud network Oracle Cloud ID).
Multiple IPs and VCN OCIDs should be separate strings separated by commas, but if it’s other configurations that need multiple pieces of information then its each piece is connected with semicolon (;) as a delimiter.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`
For Exadata Cloud@Customer, this is an array of IP addresses or CIDR notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`
For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry.
This cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, isMtlsConnectionRequired, dbWorkload, dbVersion, dbName, or isFreeTier. | No | + | `spec.details.subnetId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the subnet the resource is associated with.
**Subnet Restrictions:**
- For Autonomous Database, setting this will disable public secure access to the database.
These subnets are used by the Oracle Clusterware private interconnect on the database instance.
Specifying an overlapping subnet will cause the private interconnect to malfunction.
This restriction applies to both the client subnet and the backup subnet. | No | + | `spec.details.nsgIds` | []string | The list of [OCIDs](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) for the network security groups (NSGs) to which this resource belongs. Setting this to an empty list removes all resources from all NSGs. For more information about NSGs, see [Security Rules](https://docs.cloud.oracle.com/Content/Network/Concepts/securityrules.htm).
**NsgIds restrictions:**
- A network security group (NSG) is optional for Autonomous Databases with private access. The nsgIds list can be empty. | No | + | `spec.details.privateEndpointLabel` | string | The resource's private endpoint label.
- Setting the endpoint label to a non-empty string creates a private endpoint database.
- Resetting the endpoint label to an empty string, after the creation of the private endpoint database, changes the private endpoint database to a public endpoint database.
- Setting the endpoint label to a non-empty string value, updates to a new private endpoint database, when the database is disabled and re-enabled.
This setting cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMTLSConnectionRequired, dbWorkload, dbVersion, dbName, or isFreeTier. | No | + | `spec.details.isMtlsConnectionRequired` | boolean | Specifies if the Autonomous Database requires mTLS connections. | No | | `spec.details.autonomousContainerDatabase.k8sACD.name` | string | The **name** of the K8s Autonomous Container Database resource | No | | `spec.details.autonomousContainerDatabase.ociACD.id` | string | The Autonomous Container Database [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm). | No | | `spec.details.freeformTags` | dictionary | Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. For more information, see [Resource Tag](https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm).

Example:
`freeformTags:`
    `key1: value1`
    `key2: value2`| No | - | `spec.details.dbWorkload` | string | The Oracle Autonomous Database workload type. The following values are valid:
- OLTP - indicates an Autonomous Transaction Processing database
- DW - indicates an Autonomous Data Warehouse database
- AJD - indicates an Autonomous JSON Database
- APEX - indicates an Autonomous Database with the Oracle APEX Application Development workload type. | No | - | `spec.details.dbVersion` | string | A valid Oracle Database release for Oracle Autonomous Database. | No | | `spec.ociConfig` | dictionary | Not required when the Operator is authorized with [Instance Principal](./ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from the [Authorized with API Key Authentication](./ADB_PREREQUISITES.md#authorized-with-api-key-authentication) section. | Conditional | | `spec.ociConfig.configMapName` | string | Name of the ConfigMap that holds the local OCI configuration | Conditional | | `spec.ociConfig.secretName`| string | Name of the K8s Secret that holds the private key value | Conditional | @@ -424,7 +436,41 @@ Now, you can verify that the database is in TERMINATING state on the Cloud Conso To clone an existing Autonomous Database, complete these steps: -1. An example YAML file is available here: [config/samples/adb/autonomousdatabase_clone.yaml](./../../config/samples/adb/autonomousdatabase_clone.yaml) +1. Add the following fields to the AutonomousDatabase resource definition. An example YAML file is available here: [config/samples/adb/autonomousdatabase_clone.yaml](./../../config/samples/adb/autonomousdatabase_clone.yaml) + | Attribute | Type | Description | Required? | + |----|----|----|----| + | `spec.details.id` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the source Autonomous Database that you will clone to create a new Autonomous Database. | Yes | + | `spec.clone.cloneType` | string | The Autonomous Database clone type. Accepted values are: `FULL` and `METADATA`. | No | + | `spec.clone.compartmentId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the compartment of the Autonomous Database. | Yes | + | `spec.clone.dbName` | string | The database name. The name must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. | Yes | + | `spec.clone.displayName` | string | The user-friendly name for the Autonomous Database. The name does not have to be unique. | Yes | + | `spec.clone.dbWorkload` | string | The Autonomous Database workload type. The following values are valid:
`OLTP` - indicates an Autonomous Transaction Processing database
`DW` - indicates an Autonomous Data Warehouse database
`AJD` - indicates an Autonomous JSON Database
`APEX` - indicates an Autonomous Database with the Oracle APEX Application Development workload type.
This cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMtlsConnectionRequired, privateEndpointLabel, nsgIds, dbVersion, dbName, or isFreeTier. | No | + | `spec.clone.licenseModel` | string | The Oracle license model that applies to the Oracle Autonomous Database. Bring your own license (BYOL) allows you to apply your current on-premises Oracle software licenses to equivalent, highly automated Oracle services in the cloud.License Included allows you to subscribe to new Oracle Database software licenses and the Oracle Database service. Note that when provisioning an [Autonomous Database on dedicated Exadata infrastructure](https://docs.oracle.com/en/cloud/paas/autonomous-database/index.html), this attribute must be null. It is already set at the Autonomous Exadata Infrastructure level. When provisioning an [Autonomous Database Serverless ](https://docs.oracle.com/en/cloud/paas/autonomous-database/index.html) database, if a value is not specified, the system defaults the value to `BRING_YOUR_OWN_LICENSE`. Bring your own license (BYOL) also allows you to select the DB edition using the optional parameter.
This cannot be updated in parallel with any of the following: cpuCoreCount, computeCount, dataStorageSizeInTBs, adminPassword, isMtlsConnectionRequired, dbWorkload, privateEndpointLabel, nsgIds, dbVersion, dbName, or isFreeTier. | No | + | `spec.clone.dbVersion` | string | A valid Oracle Database version for Autonomous Database. | No | + | `spec.clone.dataStorageSizeInTBs` | int | The size, in terabytes, of the data volume that will be created and attached to the database. This storage can later be scaled up if needed. For Autonomous Databases on dedicated Exadata infrastructure, the maximum storage value is determined by the infrastructure shape. See Characteristics of [Infrastructure Shapes](https://www.oracle.com/pls/topic/lookup?ctx=en/cloud/paas/autonomous-database&id=ATPFG-GUID-B0F033C1-CC5A-42F0-B2E7-3CECFEDA1FD1) for shape details. A full Exadata service is allocated when the Autonomous Database size is set to the upper limit (384 TB). | No | + | `spec.clone.cpuCoreCount` | int | The number of CPU cores to be made available to the database. For Autonomous Databases on dedicated Exadata infrastructure, the maximum number of cores is determined by the infrastructure shape. See [Characteristics of Infrastructure Shapes](https://www.oracle.com/pls/topic/lookup?ctx=en/cloud/paas/autonomous-database&id=ATPFG-GUID-B0F033C1-CC5A-42F0-B2E7-3CECFEDA1FD1) for shape details.
**Note:** This parameter cannot be used with the `ocpuCount` parameter. | Conditional | + | `spec.clone.computeModel` | string | The compute model of the Autonomous Database. This is required if using the `computeCount` parameter. If using `cpuCoreCount` then it is an error to specify `computeModel` to a non-null value. ECPU compute model is the recommended model and OCPU compute model is legacy. | Conditional | + | `spec.clone.computeCount` | float32 | The compute amount (CPUs) available to the database. Minimum and maximum values depend on the compute model and whether the database is an Autonomous Database Serverless instance or an Autonomous Database on Dedicated Exadata Infrastructure.
For an Autonomous Database Serverless instance, the 'ECPU' compute model requires a minimum value of one, for databases in the elastic resource pool and minimum value of two, otherwise. Required when using the `computeModel` parameter. When using `cpuCoreCount` parameter, it is an error to specify computeCount to a non-null value. Providing `computeModel` and `computeCount` is the preferred method for both OCPU and ECPU. | Conditional | + | `spec.clone.ocpuCount` | float32 | The number of OCPU cores to be made available to the database.
The following points apply:
- For Autonomous Databases on Dedicated Exadata infrastructure, to provision less than 1 core, enter a fractional value in an increment of 0.1. For example, you can provision 0.3 or 0.4 cores, but not 0.35 cores. (Note that fractional OCPU values are not supported for Autonomous Database Serverless instances.)
- To provision 1 or more cores, you must enter an integer between 1 and the maximum number of cores available for the infrastructure shape. For example, you can provision 2 cores or 3 cores, but not 2.5 cores. This applies to an Autonomous Database Serverless instance or an Autonomous Database on Dedicated Exadata Infrastructure.
- For Autonomous Database Serverless instances, this parameter is not used.
For Autonomous Databases on Dedicated Exadata infrastructure, the maximum number of cores is determined by the infrastructure shape. See [Characteristics of Infrastructure Shapes](https://www.oracle.com/pls/topic/lookup?ctx=en/cloud/paas/autonomous-database&id=ATPFG-GUID-B0F033C1-CC5A-42F0-B2E7-3CECFEDA1FD1) for shape details.
**Note:** This parameter cannot be used with the `cpuCoreCount` parameter. | Conditional | + | `spec.clone.adminPassword` | dictionary | The password for the ADMIN user. The password must be between 12 and 30 characters long, and must contain at least 1 uppercase, 1 lowercase, and 1 numeric character. It cannot contain the double quote symbol (") or the username "admin", regardless of casing.

Either `k8sSecret.name` or `ociSecret.id` must be provided. If both `k8sSecret.name` and `ociSecret.id` appear, the Operator reads the password from the K8s secret that `k8sSecret.name` refers to. | Yes | + | `spec.clone.adminPassword.k8sSecret.name` | string | The **name** of the K8s Secret where you want to hold the password for the ADMIN user. | Conditional | + |`spec.clone.adminPassword.ociSecret.id` | string | The **[OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm)** of the [OCI Secret](https://docs.oracle.com/en-us/iaas/Content/KeyManagement/Tasks/managingsecrets.htm) where you want to hold the password for the ADMIN user. | Conditional | + | `spec.clone.dataStorageSizeInTBs` | int | The size, in terabytes, of the data volume that will be created and attached to the database. This storage can later be scaled up if needed. | Yes | + | `spec.clone.isAutoScalingEnabled` | boolean | Indicates if auto scaling is enabled for the Autonomous Database OCPU core count. The default value is `FALSE` | No | + | `spec.clone.isDedicated` | boolean | True if the database is on dedicated [Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adbddoverview.htm). `spec.clone.autonomousContainerDatabase.k8sACD.name` or `spec.clone.autonomousContainerDatabase.ociACD.id` has to be provided if the value is true. | No | + | `spec.clone.isFreeTier` | boolean | Indicates if this is an Always Free resource. The default value is false. Note that Always Free Autonomous Databases have 1 CPU and 20GB of memory. For Always Free databases, memory and CPU cannot be scaled.
This cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMtlsConnectionRequired, privateEndpointLabel, nsgIds, dbVersion, or dbName. | No | + | `spec.clone.isAccessControlEnabled` | boolean | Indicates if the database-level access control is enabled.
If disabled, database access is defined by the network security rules.
If enabled, database access is restricted to the IP addresses defined by the rules specified with the `whitelistedIps` property. While specifying `whitelistedIps` rules is optional, if database-level access control is enabled and no rules are specified, the database will become inaccessible.
When creating a database clone, the desired access control setting should be specified. By default, database-level access control will be disabled for the clone.
This property is applicable only to Autonomous Databases on the Exadata Cloud@Customer platform. For Autonomous Database Serverless instances, `whitelistedIps` is used. | No | + | `spec.clone.whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for [Autonomous Database Serverless](https://docs.oracle.com/en/cloud/paas/autonomous-database/index.html) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.
If `arePrimaryWhitelistedIpsUsed` is 'TRUE' then Autonomous Database uses this primary's IP access control list (ACL) for the disaster recovery peer called `standbywhitelistedips`.
For Autonomous Database Serverless, this is an array of CIDR (classless inter-domain routing) notations for a subnet or VCN OCID (virtual cloud network Oracle Cloud ID).
Multiple IPs and VCN OCIDs should be separate strings separated by commas, but if it’s other configurations that need multiple pieces of information then its each piece is connected with semicolon (;) as a delimiter.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`
For Exadata Cloud@Customer, this is an array of IP addresses or CIDR notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`
For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry.
This cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, isMtlsConnectionRequired, dbWorkload, dbVersion, dbName, or isFreeTier. | No | + | `spec.clone.subnetId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the subnet the resource is associated with.
**Subnet Restrictions:**
- For Autonomous Database, setting this will disable public secure access to the database.
These subnets are used by the Oracle Clusterware private interconnect on the database instance.
Specifying an overlapping subnet will cause the private interconnect to malfunction.
This restriction applies to both the client subnet and the backup subnet. | No | + | `spec.clone.nsgIds` | []string | The list of [OCIDs](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) for the network security groups (NSGs) to which this resource belongs. Setting this to an empty list removes all resources from all NSGs. For more information about NSGs, see [Security Rules](https://docs.cloud.oracle.com/Content/Network/Concepts/securityrules.htm).
**NsgIds restrictions:**
- A network security group (NSG) is optional for Autonomous Databases with private access. The nsgIds list can be empty. | No | + | `spec.clone.privateEndpointLabel` | string | The resource's private endpoint label.
- Setting the endpoint label to a non-empty string creates a private endpoint database.
- Resetting the endpoint label to an empty string, after the creation of the private endpoint database, changes the private endpoint database to a public endpoint database.
- Setting the endpoint label to a non-empty string value, updates to a new private endpoint database, when the database is disabled and re-enabled.
This setting cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMTLSConnectionRequired, dbWorkload, dbVersion, dbName, or isFreeTier. | No | + | `spec.clone.isMtlsConnectionRequired` | boolean | Specifies if the Autonomous Database requires mTLS connections. | No | + | `spec.clone.autonomousContainerDatabase.k8sACD.name` | string | The **name** of the K8s Autonomous Container Database resource | No | + | `spec.clone.autonomousContainerDatabase.ociACD.id` | string | The Autonomous Container Database [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm). | No | + | `spec.clone.freeformTags` | dictionary | Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. For more information, see [Resource Tag](https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm).

Example:
`freeformTags:`
    `key1: value1`
    `key2: value2`| No | + | `spec.ociConfig` | dictionary | Not required when the Operator is authorized with [Instance Principal](./ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from the [Authorized with API Key Authentication](./ADB_PREREQUISITES.md#authorized-with-api-key-authentication) section. | Conditional | + | `spec.ociConfig.configMapName` | string | Name of the ConfigMap that holds the local OCI configuration | Conditional | + | `spec.ociConfig.secretName`| string | Name of the K8s Secret that holds the private key value | Conditional | ```yaml --- From 39afedd602c1b41f761cb8d3501b96c1731d469d Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 31 Jan 2025 15:52:57 +0000 Subject: [PATCH 169/414] switch ordssrvs controller to encrypted secrets --- .../v1alpha1/dbcssystem_conversion.go | 6 +- .../v1alpha1/oraclerestdataservice_types.go | 32 +- .../v1alpha1/shardingdatabase_conversion.go | 6 +- .../database/v4/dataguardbroker_conversion.go | 2 +- apis/database/v4/dbcssystem_webhook.go | 2 +- apis/database/v4/ordssrvs_types.go | 7 + apis/database/v4/shardingdatabase_types.go | 2 +- apis/database/v4/zz_generated.deepcopy.go | 17 + commons/sharding/catalog.go | 17 +- commons/sharding/scommon.go | 4 +- .../bases/database.oracle.com_ordssrvs.yaml | 10 + controllers/database/ordssrvs_controller.go | 141 +- .../database/shardingdatabase_controller.go | 112 +- docs/ordsservices/README.md | 26 +- docs/ordsservices/api.md | 245 +- docs/ordsservices/autoupgrade.md | 9 +- docs/ordsservices/examples/adb.md | 33 +- docs/ordsservices/examples/adb_oraoper.md | 78 +- docs/ordsservices/examples/mongo_api.md | 50 +- docs/ordsservices/examples/multi_pool.md | 74 +- docs/ordsservices/examples/sidb_container.md | 72 +- .../usecase01/create_multisrv.yaml | 42 - docs/ordsservices/usecase01/create_ords.sh | 31 - docs/ordsservices/usecase01/create_ords.yaml | 25 - .../usecase01/create_ordsnamespace.yaml | 6 - .../usecase01/create_registry_secret.sh | 4 - .../usecase01/create_secretregistry.sh | 5 - .../usecase01/create_singleinstance_db.yaml | 17 - .../usecase01/default-ns-role-binding.yaml | 13 - docs/ordsservices/usecase01/diaginit.sh | 11 - docs/ordsservices/usecase01/makefile | 175 +- docs/ordsservices/usecase01/node-rbac.yaml | 26 - docs/ordsservices/usecase01/node_rbac.yaml | 26 - .../usecase01/oracle-database-operator.yaml | 4956 ----------------- .../usecase01/ords-ns-role-binding.yaml | 13 - .../usecase01/persistent-volume-rbac.yaml | 29 - .../usecase01/rest_server_creation.yaml | 25 - .../usecase01/rest_server_creation_mongo.yaml | 29 - docs/ordsservices/usecase01/sidb_create.yaml | 18 - docs/ordsservices/usecase01/sqlcreate.sql | 9 - .../usecase01/storage-class-rbac.yaml | 28 - .../usecase01/tnsadmin/tnsnames.ora | 2 +- .../usecase01/tnsadmin/tnsnames.ora.offline | 1 + docs/ordsservices/usecase01/tnsnames.ora | 3 - oracle-database-operator.yaml | 10 + 45 files changed, 670 insertions(+), 5779 deletions(-) delete mode 100644 docs/ordsservices/usecase01/create_multisrv.yaml delete mode 100644 docs/ordsservices/usecase01/create_ords.sh delete mode 100644 docs/ordsservices/usecase01/create_ords.yaml delete mode 100644 docs/ordsservices/usecase01/create_ordsnamespace.yaml delete mode 100644 docs/ordsservices/usecase01/create_registry_secret.sh delete mode 100644 docs/ordsservices/usecase01/create_secretregistry.sh delete mode 100644 docs/ordsservices/usecase01/create_singleinstance_db.yaml delete mode 100644 docs/ordsservices/usecase01/default-ns-role-binding.yaml delete mode 100644 docs/ordsservices/usecase01/diaginit.sh delete mode 100644 docs/ordsservices/usecase01/node-rbac.yaml delete mode 100644 docs/ordsservices/usecase01/node_rbac.yaml delete mode 100644 docs/ordsservices/usecase01/oracle-database-operator.yaml delete mode 100644 docs/ordsservices/usecase01/ords-ns-role-binding.yaml delete mode 100644 docs/ordsservices/usecase01/persistent-volume-rbac.yaml delete mode 100644 docs/ordsservices/usecase01/rest_server_creation.yaml delete mode 100644 docs/ordsservices/usecase01/rest_server_creation_mongo.yaml delete mode 100644 docs/ordsservices/usecase01/sidb_create.yaml delete mode 100644 docs/ordsservices/usecase01/sqlcreate.sql delete mode 100644 docs/ordsservices/usecase01/storage-class-rbac.yaml create mode 100644 docs/ordsservices/usecase01/tnsadmin/tnsnames.ora.offline delete mode 100644 docs/ordsservices/usecase01/tnsnames.ora diff --git a/apis/database/v1alpha1/dbcssystem_conversion.go b/apis/database/v1alpha1/dbcssystem_conversion.go index 7e4a25c8..0aa6a258 100644 --- a/apis/database/v1alpha1/dbcssystem_conversion.go +++ b/apis/database/v1alpha1/dbcssystem_conversion.go @@ -1,14 +1,14 @@ package v1alpha1 import ( - "sigs.k8s.io/controller-runtime/pkg/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" ) func (src *DbcsSystem) ConvertTo(dst conversion.Hub) error { - return nil + return nil } // ConvertFrom converts v1 to v1alpha1 func (dst *DbcsSystem) ConvertFrom(src conversion.Hub) error { - return nil + return nil } diff --git a/apis/database/v1alpha1/oraclerestdataservice_types.go b/apis/database/v1alpha1/oraclerestdataservice_types.go index 7d61fdcc..bab04092 100644 --- a/apis/database/v1alpha1/oraclerestdataservice_types.go +++ b/apis/database/v1alpha1/oraclerestdataservice_types.go @@ -76,9 +76,9 @@ type OracleRestDataServicePersistence struct { StorageClass string `json:"storageClass,omitempty"` // +kubebuilder:validation:Enum=ReadWriteOnce;ReadWriteMany - AccessMode string `json:"accessMode,omitempty"` - VolumeName string `json:"volumeName,omitempty"` - SetWritePermissions *bool `json:"setWritePermissions,omitempty"` + AccessMode string `json:"accessMode,omitempty"` + VolumeName string `json:"volumeName,omitempty"` + SetWritePermissions *bool `json:"setWritePermissions,omitempty"` } // OracleRestDataServiceImage defines the Image source and pullSecrets for POD @@ -108,19 +108,19 @@ type OracleRestDataServiceRestEnableSchemas struct { type OracleRestDataServiceStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - Status string `json:"status,omitempty"` - DatabaseApiUrl string `json:"databaseApiUrl,omitempty"` - LoadBalancer string `json:"loadBalancer,omitempty"` - DatabaseRef string `json:"databaseRef,omitempty"` - ServiceIP string `json:"serviceIP,omitempty"` - DatabaseActionsUrl string `json:"databaseActionsUrl,omitempty"` - MongoDbApiAccessUrl string `json:"mongoDbApiAccessUrl,omitempty"` - OrdsInstalled bool `json:"ordsInstalled,omitempty"` - ApexConfigured bool `json:"apexConfigured,omitempty"` - ApxeUrl string `json:"apexUrl,omitempty"` - MongoDbApi bool `json:"mongoDbApi,omitempty"` - CommonUsersCreated bool `json:"commonUsersCreated,omitempty"` - Replicas int `json:"replicas,omitempty"` + Status string `json:"status,omitempty"` + DatabaseApiUrl string `json:"databaseApiUrl,omitempty"` + LoadBalancer string `json:"loadBalancer,omitempty"` + DatabaseRef string `json:"databaseRef,omitempty"` + ServiceIP string `json:"serviceIP,omitempty"` + DatabaseActionsUrl string `json:"databaseActionsUrl,omitempty"` + MongoDbApiAccessUrl string `json:"mongoDbApiAccessUrl,omitempty"` + OrdsInstalled bool `json:"ordsInstalled,omitempty"` + ApexConfigured bool `json:"apexConfigured,omitempty"` + ApxeUrl string `json:"apexUrl,omitempty"` + MongoDbApi bool `json:"mongoDbApi,omitempty"` + CommonUsersCreated bool `json:"commonUsersCreated,omitempty"` + Replicas int `json:"replicas,omitempty"` Image OracleRestDataServiceImage `json:"image,omitempty"` } diff --git a/apis/database/v1alpha1/shardingdatabase_conversion.go b/apis/database/v1alpha1/shardingdatabase_conversion.go index f3871221..d8db75ca 100644 --- a/apis/database/v1alpha1/shardingdatabase_conversion.go +++ b/apis/database/v1alpha1/shardingdatabase_conversion.go @@ -1,14 +1,14 @@ package v1alpha1 import ( - "sigs.k8s.io/controller-runtime/pkg/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" ) func (src *ShardingDatabase) ConvertTo(dst conversion.Hub) error { - return nil + return nil } // ConvertFrom converts v1 to v1alpha1 func (dst *ShardingDatabase) ConvertFrom(src conversion.Hub) error { - return nil + return nil } diff --git a/apis/database/v4/dataguardbroker_conversion.go b/apis/database/v4/dataguardbroker_conversion.go index 22ace70d..c63210e0 100644 --- a/apis/database/v4/dataguardbroker_conversion.go +++ b/apis/database/v4/dataguardbroker_conversion.go @@ -1,4 +1,4 @@ package v4 // Hub defines v1 as the hub version -func (*DataguardBroker) Hub() {} \ No newline at end of file +func (*DataguardBroker) Hub() {} diff --git a/apis/database/v4/dbcssystem_webhook.go b/apis/database/v4/dbcssystem_webhook.go index 1526deef..c3ff8ddb 100644 --- a/apis/database/v4/dbcssystem_webhook.go +++ b/apis/database/v4/dbcssystem_webhook.go @@ -70,7 +70,7 @@ func (r *DbcsSystem) Default() { // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv4.kb.io,admissionReviewVersions=v1 +// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv4.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &DbcsSystem{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v4/ordssrvs_types.go b/apis/database/v4/ordssrvs_types.go index cd4e28c6..1fbf820a 100644 --- a/apis/database/v4/ordssrvs_types.go +++ b/apis/database/v4/ordssrvs_types.go @@ -70,8 +70,11 @@ type OrdsSrvsSpec struct { // Contains settings that are configured across the entire ORDS instance. GlobalSettings GlobalSettings `json:"globalSettings"` // Contains settings for individual pools/databases + // Private key + EncPrivKey PasswordSecret `json:"encPrivKey,omitempty"` PoolSettings []*PoolSettings `json:"poolSettings,omitempty"` // +k8s:openapi-gen=true + } type GlobalSettings struct { @@ -593,6 +596,10 @@ type PoolSettings struct { */ } +type PriVKey struct { + Secret PasswordSecret `json:"secret"` +} + // Defines the secret containing Password mapped to secretKey type PasswordSecret struct { // Specifies the name of the password Secret diff --git a/apis/database/v4/shardingdatabase_types.go b/apis/database/v4/shardingdatabase_types.go index c312f419..cc01b24d 100644 --- a/apis/database/v4/shardingdatabase_types.go +++ b/apis/database/v4/shardingdatabase_types.go @@ -148,7 +148,7 @@ type GsmStatusDetails struct { // ShardingDatabase is the Schema for the shardingdatabases API // +kubebuilder:resource:path=shardingdatabases,scope=Namespaced - // +kubebuilder:storageversion +// +kubebuilder:storageversion type ShardingDatabase struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 94a2a9c3..4eb9425d 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -2791,6 +2791,7 @@ func (in *OrdsSrvsList) DeepCopyObject() runtime.Object { func (in *OrdsSrvsSpec) DeepCopyInto(out *OrdsSrvsSpec) { *out = *in in.GlobalSettings.DeepCopyInto(&out.GlobalSettings) + out.EncPrivKey = in.EncPrivKey if in.PoolSettings != nil { in, out := &in.PoolSettings, &out.PoolSettings *out = make([]*PoolSettings, len(*in)) @@ -3434,6 +3435,22 @@ func (in *PortMapping) DeepCopy() *PortMapping { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PriVKey) DeepCopyInto(out *PriVKey) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PriVKey. +func (in *PriVKey) DeepCopy() *PriVKey { + if in == nil { + return nil + } + out := new(PriVKey) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretDetails) DeepCopyInto(out *SecretDetails) { *out = *in diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index 063dc13f..646c89b8 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -376,15 +376,15 @@ func volumeClaimTemplatesForCatalog(instance *databasev4.ShardingDatabase, OraCa } if len(OraCatalogSpex.PvAnnotations) > 0 { - claims[0].ObjectMeta.Annotations = make(map[string]string) - for key, value := range OraCatalogSpex.PvAnnotations { - claims[0].ObjectMeta.Annotations[key] = value - } - } + claims[0].ObjectMeta.Annotations = make(map[string]string) + for key, value := range OraCatalogSpex.PvAnnotations { + claims[0].ObjectMeta.Annotations[key] = value + } + } - if len(OraCatalogSpex.PvMatchLabels) > 0 { - claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} - } + if len(OraCatalogSpex.PvMatchLabels) > 0 { + claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} + } if checkTdeWalletFlag(instance) { if len(instance.Spec.FssStorageClass) > 0 && len(instance.Spec.TdeWalletPvc) == 0 { @@ -526,4 +526,3 @@ func UpdateProvForCatalog(instance *databasev4.ShardingDatabase, return ctrl.Result{}, nil } - diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index bcef0763..3b3f1b04 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -411,8 +411,8 @@ func LogMessages(msgtype string, msg string, err error, instance *databasev4.Sha } else if msgtype == "INFO" { logger.Info(msg) } else if msgtype == "Error" { - logger.Error(err, msg) - } + logger.Error(err, msg) + } } func GetGsmPodName(gsmName string) string { diff --git a/config/crd/bases/database.oracle.com_ordssrvs.yaml b/config/crd/bases/database.oracle.com_ordssrvs.yaml index 7bfa4951..9c4ab88f 100644 --- a/config/crd/bases/database.oracle.com_ordssrvs.yaml +++ b/config/crd/bases/database.oracle.com_ordssrvs.yaml @@ -54,6 +54,16 @@ spec: type: object spec: properties: + encPrivKey: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object forceRestart: type: boolean globalSettings: diff --git a/controllers/database/ordssrvs_controller.go b/controllers/database/ordssrvs_controller.go index 87efd461..14c7f46e 100644 --- a/controllers/database/ordssrvs_controller.go +++ b/controllers/database/ordssrvs_controller.go @@ -40,9 +40,13 @@ package controllers import ( "context" + "crypto/rsa" "crypto/sha256" + "crypto/x509" + "encoding/base64" "encoding/hex" "encoding/json" + "encoding/pem" "errors" "fmt" "reflect" @@ -387,7 +391,7 @@ func (r *OrdsSrvsReconciler) WorkloadReconcile(ctx context.Context, req ctrl.Req logr := log.FromContext(ctx).WithName("WorkloadReconcile") objectMeta := objectMetaDefine(ords, ords.Name) selector := selectorDefine(ords) - template := podTemplateSpecDefine(ords) + template := r.podTemplateSpecDefine(ords, ctx, req) var desiredWorkload client.Object var desiredSpecHash string @@ -584,7 +588,7 @@ func selectorDefine(ords *dbapi.OrdsSrvs) metav1.LabelSelector { } } -func podTemplateSpecDefine(ords *dbapi.OrdsSrvs) corev1.PodTemplateSpec { +func (r *OrdsSrvsReconciler) podTemplateSpecDefine(ords *dbapi.OrdsSrvs, ctx context.Context, req ctrl.Request) corev1.PodTemplateSpec { labels := getLabels(ords.Name) specVolumes, specVolumeMounts := VolumesDefine(ords) @@ -627,10 +631,9 @@ func podTemplateSpecDefine(ords *dbapi.OrdsSrvs) corev1.PodTemplateSpec { Name: ords.Name + "-init", ImagePullPolicy: corev1.PullIfNotPresent, SecurityContext: securityContextDefine(), - //Command: []string{"ls", "-ltr"}, - Command: []string{"sh", "-c", ordsSABase + "/bin/init_script.sh"}, - Env: envDefine(ords, true), - VolumeMounts: specVolumeMounts, + Command: []string{"sh", "-c", ordsSABase + "/bin/init_script.sh"}, + Env: r.envDefine(ords, true, ctx), + VolumeMounts: specVolumeMounts, }}, Containers: []corev1.Container{{ Image: ords.Spec.Image, @@ -638,12 +641,9 @@ func podTemplateSpecDefine(ords *dbapi.OrdsSrvs) corev1.PodTemplateSpec { ImagePullPolicy: corev1.PullIfNotPresent, SecurityContext: securityContextDefine(), Ports: envPorts, - //Command: []string{"sh", "-c", "tail -f /dev/null"}, - //Command: []string{"/usr/sbin/init"}, - Command: []string{"/bin/bash", "-c", "ords --config $ORDS_CONFIG serve --apex-images /opt/oracle/apex/$APEX_VER/images --debug"}, - Env: envDefine(ords, false), - //Env: envDefine(ords, true), - VolumeMounts: specVolumeMounts, + Command: []string{"/bin/bash", "-c", "ords --config $ORDS_CONFIG serve --apex-images /opt/oracle/apex/$APEX_VER/images --debug"}, + Env: r.envDefine(ords, false, ctx), + VolumeMounts: specVolumeMounts, }}}, } @@ -839,7 +839,7 @@ func securityContextDefine() *corev1.SecurityContext { } } -func envDefine(ords *dbapi.OrdsSrvs, initContainer bool) []corev1.EnvVar { +func (r *OrdsSrvsReconciler) envDefine(ords *dbapi.OrdsSrvs, initContainer bool, ctx context.Context) []corev1.EnvVar { envVarSecrets := []corev1.EnvVar{ { Name: "ORDS_CONFIG", @@ -850,6 +850,7 @@ func envDefine(ords *dbapi.OrdsSrvs, initContainer bool) []corev1.EnvVar { Value: "-Doracle.ml.version_check=false", }, } + // Limitation case for ADB/mTLS/OraOper edge if len(ords.Spec.PoolSettings) == 1 { poolName := strings.ToLower(ords.Spec.PoolSettings[0].PoolName) @@ -862,18 +863,14 @@ func envDefine(ords *dbapi.OrdsSrvs, initContainer bool) []corev1.EnvVar { if initContainer { for i := 0; i < len(ords.Spec.PoolSettings); i++ { poolName := strings.ReplaceAll(strings.ToLower(ords.Spec.PoolSettings[i].PoolName), "-", "_") + dbSecret := corev1.EnvVar{ - Name: poolName + "_dbsecret", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: ords.Spec.PoolSettings[i].DBSecret.SecretName, - }, - Key: ords.Spec.PoolSettings[i].DBSecret.PasswordKey, - }, - }, + Name: poolName + "_dbsecret", + Value: r.CommonDecryptWithPrivKey3(ords, ords.Spec.PoolSettings[i].DBSecret.SecretName, ords.Spec.PoolSettings[i].DBSecret.PasswordKey, ctx), } + envVarSecrets = append(envVarSecrets, dbSecret) + if ords.Spec.PoolSettings[i].DBAdminUserSecret.SecretName != "" { autoUpgradeORDSEnv := corev1.EnvVar{ Name: poolName + "_autoupgrade_ords", @@ -883,35 +880,26 @@ func envDefine(ords *dbapi.OrdsSrvs, initContainer bool) []corev1.EnvVar { Name: poolName + "_autoupgrade_apex", Value: strconv.FormatBool(ords.Spec.PoolSettings[i].AutoUpgradeAPEX), } + dbAdminUserSecret := corev1.EnvVar{ - Name: poolName + "_dbadminusersecret", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: ords.Spec.PoolSettings[i].DBAdminUserSecret.SecretName, - }, - Key: ords.Spec.PoolSettings[i].DBAdminUserSecret.PasswordKey, - }, - }, + Name: poolName + "_dbadminusersecret", + Value: r.CommonDecryptWithPrivKey3(ords, ords.Spec.PoolSettings[i].DBAdminUserSecret.SecretName, ords.Spec.PoolSettings[i].DBAdminUserSecret.PasswordKey, ctx), } envVarSecrets = append(envVarSecrets, dbAdminUserSecret, autoUpgradeORDSEnv, autoUpgradeAPEXEnv) } + if ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.SecretName != "" { + dbCDBAdminUserSecret := corev1.EnvVar{ - Name: poolName + "_dbcdbadminusersecret", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.SecretName, - }, - Key: ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.PasswordKey, - }, - }, + Name: poolName + "_dbcdbadminusersecret", + Value: r.CommonDecryptWithPrivKey3(ords, ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.SecretName, ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.PasswordKey, ctx), } + envVarSecrets = append(envVarSecrets, dbCDBAdminUserSecret) } } } + return envVarSecrets } @@ -1053,3 +1041,76 @@ func generateSpecHash(spec interface{}) string { return hashString } + +func CommonDecryptWithPrivKey(Key string, Buffer string) (string, error) { + + Debug := 0 + block, _ := pem.Decode([]byte(Key)) + pkcs8PrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + fmt.Printf("Failed to parse private key %s \n", err.Error()) + return "", err + } + if Debug == 1 { + fmt.Printf("======================================\n") + fmt.Printf("%s\n", Key) + fmt.Printf("======================================\n") + } + + encString64, err := base64.StdEncoding.DecodeString(string(Buffer)) + if err != nil { + fmt.Printf("Failed to decode encrypted string to base64: %s\n", err.Error()) + return "", err + } + + if Debug == 1 { + fmt.Printf("======================================\n") + fmt.Printf("%s\n", encString64) + fmt.Printf("======================================\n") + } + + decryptedB, err := rsa.DecryptPKCS1v15(nil, pkcs8PrivateKey.(*rsa.PrivateKey), encString64) + if err != nil { + fmt.Printf("Failed to decrypt string %s\n", err.Error()) + return "", err + } + if Debug == 1 { + fmt.Printf("[%s]\n", string(decryptedB)) + } + return strings.TrimSpace(string(decryptedB)), err + +} + +func (r *OrdsSrvsReconciler) CommonDecryptWithPrivKey3(ords *dbapi.OrdsSrvs, sname string, skey string, ctx context.Context) string { + logr := log.FromContext(ctx).WithName("CommonDecryptWithPrivKey2") + secret_par := &corev1.Secret{} + fmt.Printf("sname: %s\n", sname) + fmt.Printf("skey: %s\n", skey) + err := r.Get(ctx, types.NamespacedName{Name: sname, Namespace: ords.Namespace}, secret_par) + if err != nil { + logr.Error(err, "Cannot read secret"+sname) + return "" + } + encVal := string(secret_par.Data[skey]) + encVal = strings.TrimSpace(encVal) + + secret_key := &corev1.Secret{} + /* get private key */ + if err := r.Get(ctx, types.NamespacedName{Name: ords.Spec.EncPrivKey.SecretName, + Namespace: ords.Namespace}, secret_key); err != nil { + logr.Error(err, "Cannot get privte key") + return "" + } + PrvKeyVal := string(secret_key.Data[ords.Spec.EncPrivKey.PasswordKey]) + PrvKeyVal = strings.TrimSpace(PrvKeyVal) + + decVal, err := CommonDecryptWithPrivKey(PrvKeyVal, encVal) + if err != nil { + logr.Error(err, "Fail to decrypt secret") + return "" + } + + logr.Info("Password decryption completed") + + return decVal +} diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 2c227c14..1ec77253 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -73,11 +73,11 @@ import ( // Struct keeping Oracle Notification Server Info type OnsStatus struct { - Topicid string `json:"topicid,omitempty"` - Instance *databasev4.ShardingDatabase `json:"instance,omitempty"` - OnsProvider common.ConfigurationProvider `json:"onsProvider,omitempty"` - OnsProviderFlag bool `json:"onsProviderFlag,omitempty"` - Rclient ons.NotificationDataPlaneClient `json:"rclient,omitempty"` + Topicid string `json:"topicid,omitempty"` + Instance *databasev4.ShardingDatabase `json:"instance,omitempty"` + OnsProvider common.ConfigurationProvider `json:"onsProvider,omitempty"` + OnsProviderFlag bool `json:"onsProviderFlag,omitempty"` + Rclient ons.NotificationDataPlaneClient `json:"rclient,omitempty"` } // ShardingDatabaseReconciler reconciles a ShardingDatabase object @@ -88,14 +88,14 @@ type ShardingDatabaseReconciler struct { kubeClient kubernetes.Interface kubeConfig clientcmd.ClientConfig Recorder record.EventRecorder - InCluster bool - Namespace string + InCluster bool + Namespace string } var sentFailMsg = make(map[string]bool) var sentCompleteMsg = make(map[string]bool) -var oshMap=make(map[string]*OnsStatus) +var oshMap = make(map[string]*OnsStatus) // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases/status,verbs=get;update;patch @@ -159,7 +159,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, err } - instFlag := r.checkProvInstance(instance) + instFlag := r.checkProvInstance(instance) if !instFlag { oshMap[instance.Name] = &OnsStatus{} oshMap[instance.Name].Instance = instance @@ -475,31 +475,31 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate DeleteFunc: func(e event.DeleteEvent) bool { instance := &databasev4.ShardingDatabase{} _, podOk := e.Object.GetLabels()["statefulset.kubernetes.io/pod-name"] - if oshMap[instance.Name] != nil { - oshInst := instance - if instance.DeletionTimestamp == nil { - - if e.Object.GetLabels()[string(databasev4.ShardingDelLabelKey)] == string(databasev4.ShardingDelLabelTrueValue) { - } - - if podOk { - delObj := e.Object.(*corev1.Pod) - if e.Object.GetLabels()["type"] == "Shard" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Name { - - if delObj.DeletionTimestamp != nil { - go r.gsmInvitedNodeOp(oshInst, delObj.Name) - } - } - - if e.Object.GetLabels()["type"] == "Catalog" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Name { - - if delObj.DeletionTimestamp != nil { - go r.gsmInvitedNodeOp(oshInst, delObj.Name) - } - } - } - } - } + if oshMap[instance.Name] != nil { + oshInst := instance + if instance.DeletionTimestamp == nil { + + if e.Object.GetLabels()[string(databasev4.ShardingDelLabelKey)] == string(databasev4.ShardingDelLabelTrueValue) { + } + + if podOk { + delObj := e.Object.(*corev1.Pod) + if e.Object.GetLabels()["type"] == "Shard" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Name { + + if delObj.DeletionTimestamp != nil { + go r.gsmInvitedNodeOp(oshInst, delObj.Name) + } + } + + if e.Object.GetLabels()["type"] == "Catalog" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Name { + + if delObj.DeletionTimestamp != nil { + go r.gsmInvitedNodeOp(oshInst, delObj.Name) + } + } + } + } + } return true }, } @@ -534,19 +534,19 @@ func (r *ShardingDatabaseReconciler) getOnsConfigProvider(instance *databasev4.S region, user, tenancy, passphrase, fingerprint, topicid := shardingv1.ReadConfigMap(cmName, instance, r.Client, r.Log) privatekey := shardingv1.ReadSecret(secName, instance, r.Client, r.Log) - oshMap[instance.Name].Topicid = topicid - oshMap[instance.Name].OnsProvider = common.NewRawConfigurationProvider(tenancy, user, region, fingerprint, privatekey, &passphrase) -//VV instance.Spec.TopicId = topicid - oshMap[instance.Name].Rclient, err = ons.NewNotificationDataPlaneClientWithConfigurationProvider(oshMap[instance.Name].OnsProvider) - if err != nil { - msg := "Error occurred in getting the OCI notification service based client." - oshMap[instance.Name].OnsProviderFlag = false - r.Log.Error(err, msg) - shardingv1.LogMessages("Error", msg, nil, instance, r.Log) - } else { - oshMap[instance.Name].OnsProviderFlag = true - } - } + oshMap[instance.Name].Topicid = topicid + oshMap[instance.Name].OnsProvider = common.NewRawConfigurationProvider(tenancy, user, region, fingerprint, privatekey, &passphrase) + //VV instance.Spec.TopicId = topicid + oshMap[instance.Name].Rclient, err = ons.NewNotificationDataPlaneClientWithConfigurationProvider(oshMap[instance.Name].OnsProvider) + if err != nil { + msg := "Error occurred in getting the OCI notification service based client." + oshMap[instance.Name].OnsProviderFlag = false + r.Log.Error(err, msg) + shardingv1.LogMessages("Error", msg, nil, instance, r.Log) + } else { + oshMap[instance.Name].OnsProviderFlag = true + } + } } func (r ShardingDatabaseReconciler) marshalOnsInfo(instance *databasev4.ShardingDatabase) (OnsStatus, error) { @@ -567,14 +567,14 @@ func (r ShardingDatabaseReconciler) marshalOnsInfo(instance *databasev4.Sharding // ================== Function the Message ============== func (r *ShardingDatabaseReconciler) sendMessage(instance *databasev4.ShardingDatabase, title string, body string) { - instFlag := r.checkProvInstance(instance) - if instFlag { - shardingv1.LogMessages("INFO", "sendMessage():instFlag true", nil, instance, r.Log) - if oshMap[instance.Name].OnsProviderFlag { - shardingv1.LogMessages("INFO", "sendMessage():OnsProviderFlag true", nil, instance, r.Log) - shardingv1.SendNotification(title, body, instance, oshMap[instance.Name].Topicid, oshMap[instance.Name].Rclient, r.Log) - } - } + instFlag := r.checkProvInstance(instance) + if instFlag { + shardingv1.LogMessages("INFO", "sendMessage():instFlag true", nil, instance, r.Log) + if oshMap[instance.Name].OnsProviderFlag { + shardingv1.LogMessages("INFO", "sendMessage():OnsProviderFlag true", nil, instance, r.Log) + shardingv1.SendNotification(title, body, instance, oshMap[instance.Name].Topicid, oshMap[instance.Name].Rclient, r.Log) + } + } } func (r *ShardingDatabaseReconciler) publishEvents(instance *databasev4.ShardingDatabase, eventMsg string, state string) { @@ -839,7 +839,7 @@ func (r *ShardingDatabaseReconciler) finalizeShardingDatabase(instance *database } } - oshMap[instance.Name].Instance = &databasev4.ShardingDatabase{} + oshMap[instance.Name].Instance = &databasev4.ShardingDatabase{} return nil } diff --git a/docs/ordsservices/README.md b/docs/ordsservices/README.md index 2334f17b..1740e99f 100644 --- a/docs/ordsservices/README.md +++ b/docs/ordsservices/README.md @@ -1,11 +1,9 @@ -# Oracle Rest Data Services (ORDS) Controller for Kubernetes - ORDSLFMNG ORDS Life cycle management - - +# Oracle Rest Data Services (ORDSSRVS) Controller for Kubernetes - ORDS Life cycle management ## Description -The ORDS controller extends the Kubernetes API with a Custom Resource (CR) and Controller for automating Oracle Rest Data +The ORDSRVS controller extends the Kubernetes API with a Custom Resource (CR) and Controller for automating Oracle Rest Data Services (ORDS) lifecycle management. Using the ORDS controller, you can easily migrate existing, or create new, ORDS implementations into an existing Kubernetes cluster. @@ -15,10 +13,10 @@ This controller allows you to run what would otherwise be an On-Premises ORDS mi The custom RestDataServices resource supports the following configurations as a Deployment, StatefulSet, or DaemonSet: -* Single RestDataServices resource with one database pool -* Single RestDataServices resource with multiple database pools* -* Multiple RestDataServices resources, each with one database pool -* Multiple RestDataServices resources, each with multiple database pools* +* Single OrdsSrvs resource with one database pool +* Single OrdsSrvs resource with multiple database pools* +* Multiple OrdsSrvs resources, each with one database pool +* Multiple OrdsSrvs resources, each with multiple database pools* *See [Limitations](#limitations) @@ -34,16 +32,6 @@ Oracle Database Version: * 23ai (incl. 23ai Free) -### Quick Installation - -To install the ORDS controller, run: - -```bash -kubectl apply -f https://github.com/gotsysdba/oracle-ords-controller/releases/latest/download/oracle-ords-controller.yaml -``` - -This will create a new namespace, `oracle-ords-controller-system`, in which the Controller will run. - ### Common Configurations A few common configuration examples can be used to quickly familiarise yourself with the ORDS Custom Resource Definition. @@ -75,5 +63,5 @@ See [Reporting security vulnerabilities](./SECURITY.md) ## License -Copyright (c) 2024 Oracle and/or its affiliates. +Copyright (c) 2025 Oracle and/or its affiliates. Released under the Universal Permissive License v1.0 as shown at [https://oss.oracle.com/licenses/upl/](https://oss.oracle.com/licenses/upl/) diff --git a/docs/ordsservices/api.md b/docs/ordsservices/api.md index d8f9643d..da4db09c 100644 --- a/docs/ordsservices/api.md +++ b/docs/ordsservices/api.md @@ -8,12 +8,12 @@ Packages: Resource Types: -- [RestDataServices](#restdataservices) +- [OrdsSrvs](#ordssrvs) -## RestDataServices +## OrdsSrvs [↩ Parent](#databaseoraclecomv1 ) @@ -21,7 +21,7 @@ Resource Types: -RestDataServices is the Schema for the restdataservices API +OrdsSrvs is the Schema for the ordssrvs API @@ -41,7 +41,7 @@ RestDataServices is the Schema for the restdataservices API - + @@ -50,111 +50,126 @@ RestDataServices is the Schema for the restdataservices API - + - +
kind stringRestDataServicesOrdsSrvs true
Refer to the Kubernetes API documentation for the fields of the `metadata` field. true
specspec object - RestDataServicesSpec defines the desired state of RestDataServices
+ OrdsSrvsSpec defines the desired state of OrdsSrvs
false
statusstatus object - RestDataServicesStatus defines the observed state of RestDataServices
+ OrdsSrvsStatus defines the observed state of OrdsSrvs
false
-### RestDataServices.spec -[↩ Parent](#restdataservices) +### OrdsSrvs.spec +[↩ Parent](#ordssrvs) -RestDataServicesSpec defines the desired state of RestDataServices +OrdsSrvsSpec defines the desired state of OrdsSrvs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
globalSettingsobject - Contains settings that are configured across the entire ORDS instance.
-
true
imagestring - Specifies the ORDS container image
-
true
forceRestartboolean - Specifies whether to restart pods when Global or Pool configurations change
-
false
imagePullPolicyenum - Specifies the ORDS container image pull policy
-
- Enum: IfNotPresent, Always, Never
- Default: IfNotPresent
-
false
imagePullSecretsstring - Specifies the Secret Name for pulling the ORDS container image
-
false
poolSettings[]object - Contains settings for individual pools/databases
-
false
replicasinteger - Defines the number of desired Replicas when workloadType is Deployment or StatefulSet
-
- Format: int32
- Default: 1
- Minimum: 1
-
false
workloadTypeenum - Specifies the desired Kubernetes Workload
-
- Enum: Deployment, StatefulSet, DaemonSet
- Default: Deployment
-
false
NameTypeDescriptionRequired
globalSettingsobject Contains settings that are configured across the entire +ORDS instance.
+
true
imagestring Specifies the ORDS container image
+
true
forceRestartboolean Specifies whether to restart pods when Global or Pool +configurations change
+
false
imagePullPolicyenum Specifies the ORDS container image pull policy
+
+Enum: IfNotPresent, Always, Never
+Default: IfNotPresent
+
false
imagePullSecretsstring Specifies the Secret Name for pulling the ORDS container +image
+
false
poolSettings< +a>[]object Contains settings for individual pools/databases
+
false
replicasinteger Defines the number of desired Replicas when workloadType +Deployment or StatefulSet
+
+Format: int32
+Default: 1
+Minimum: 1
+
false
workloadTypeenum Specifies the desired Kubernetes Workload
+
+Enum: Deployment, StatefulSet, DaemonSet
+Default: Deployment
+
false
encPrivKey
+
secret
+
secretName: string  passwordKey: +string Define the private key to decrypt passwords
+
true
+
- -### RestDataServices.spec.globalSettings -[↩ Parent](#restdataservicesspec) +### OrdsSrvs.spec.globalSettings +[↩ Parent](#ordssrvsspec) @@ -247,7 +262,7 @@ Contains settings that are configured across the entire ORDS instance. false -
certSecret + certSecret object Specifies the Secret containing the SSL Certificates Replaces: standalone.https.cert and standalone.https.cert.key
@@ -516,8 +531,8 @@ Contains settings that are configured across the entire ORDS instance. -### RestDataServices.spec.globalSettings.certSecret -[↩ Parent](#restdataservicesspecglobalsettings) +### OrdsSrvs.spec.globalSettings.certSecret +[↩ Parent](#ordssrvsspecglobalsettings) @@ -557,8 +572,8 @@ Specifies the Secret containing the SSL Certificates Replaces: standalone.https. -### RestDataServices.spec.poolSettings[index] -[↩ Parent](#restdataservicesspec) +### OrdsSrvs.spec.poolSettings[index] +[↩ Parent](#ordssrvsspec) @@ -574,7 +589,7 @@ Specifies the Secret containing the SSL Certificates Replaces: standalone.https. - db.secret + db.secret object Specifies the Secret with the dbUsername and dbPassword values for the connection.
@@ -627,7 +642,7 @@ Specifies the Secret containing the SSL Certificates Replaces: standalone.https. false - db.adminUser.secret + db.adminUser.secret object Specifies the Secret with the dbAdminUser (SYS) and dbAdminPassword values for the database account that ORDS uses for administration operations in the database. replaces: db.adminUser.password
@@ -641,7 +656,7 @@ Specifies the Secret containing the SSL Certificates Replaces: standalone.https. false - db.cdb.adminUser.secret + db.cdb.adminUser.secret object Specifies the Secret with the dbCdbAdminUser (SYS) and dbCdbAdminPassword values Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management. Replaces: db.cdb.adminUser.password
@@ -735,7 +750,7 @@ Specifies the Secret containing the SSL Certificates Replaces: standalone.https. false - dbWalletSecret + dbWalletSecret object Specifies the Secret containing the wallet archive containing connection details for the pool. Replaces: db.wallet.zip
@@ -1041,7 +1056,7 @@ Specifies the Secret containing the SSL Certificates Replaces: standalone.https. false - tnsAdminSecret + tnsAdminSecret object Specifies the Secret containing the TNS_ADMIN directory Replaces: db.tnsDirectory
@@ -1051,8 +1066,8 @@ Specifies the Secret containing the SSL Certificates Replaces: standalone.https. -### RestDataServices.spec.poolSettings[index].db.secret -[↩ Parent](#restdataservicesspecpoolsettingsindex) +### OrdsSrvs.spec.poolSettings[index].db.secret +[↩ Parent](#ordssrvsspecpoolsettingsindex) @@ -1087,8 +1102,8 @@ Specifies the Secret with the dbUsername and dbPassword values for the connectio -### RestDataServices.spec.poolSettings[index].db.adminUser.secret -[↩ Parent](#restdataservicesspecpoolsettingsindex) +### OrdsSrvs.spec.poolSettings[index].db.adminUser.secret +[↩ Parent](#ordssrvsspecpoolsettingsindex) @@ -1123,8 +1138,8 @@ Specifies the Secret with the dbAdminUser (SYS) and dbAdminPassword values for t -### RestDataServices.spec.poolSettings[index].db.cdb.adminUser.secret -[↩ Parent](#restdataservicesspecpoolsettingsindex) +### OrdsSrvs.spec.poolSettings[index].db.cdb.adminUser.secret +[↩ Parent](#ordssrvsspecpoolsettingsindex) @@ -1159,8 +1174,8 @@ Specifies the Secret with the dbCdbAdminUser (SYS) and dbCdbAdminPassword values -### RestDataServices.spec.poolSettings[index].dbWalletSecret -[↩ Parent](#restdataservicesspecpoolsettingsindex) +### OrdsSrvs.spec.poolSettings[index].dbWalletSecret +[↩ Parent](#ordssrvsspecpoolsettingsindex) @@ -1193,8 +1208,8 @@ Specifies the Secret containing the wallet archive containing connection details -### RestDataServices.spec.poolSettings[index].tnsAdminSecret -[↩ Parent](#restdataservicesspecpoolsettingsindex) +### OrdsSrvs.spec.poolSettings[index].tnsAdminSecret +[↩ Parent](#ordssrvsspecpoolsettingsindex) @@ -1220,12 +1235,12 @@ Specifies the Secret containing the TNS_ADMIN directory Replaces: db.tnsDirector -### RestDataServices.status -[↩ Parent](#restdataservices) +### OrdsSrvs.status +[↩ Parent](#ordssrvs) -RestDataServicesStatus defines the observed state of RestDataServices +OrdsSrvsStatus defines the observed state of OrdsSrvs @@ -1244,7 +1259,7 @@ RestDataServicesStatus defines the observed state of RestDataServices - +
true
conditionsconditions []object
@@ -1302,8 +1317,8 @@ RestDataServicesStatus defines the observed state of RestDataServices
-### RestDataServices.status.conditions[index] -[↩ Parent](#restdataservicesstatus) +### OrdsSrvs.status.conditions[index] +[↩ Parent](#ordssrvsstatus) diff --git a/docs/ordsservices/autoupgrade.md b/docs/ordsservices/autoupgrade.md index 040e4da8..fddc30b3 100644 --- a/docs/ordsservices/autoupgrade.md +++ b/docs/ordsservices/autoupgrade.md @@ -12,7 +12,7 @@ must be provided. If they are not, the `autoUpgrade` specification is ignored. ```yaml apiVersion: database.oracle.com/v1 -kind: RestDataServices +kind: OrdsSrvs metadata: name: ordspoc-server spec: @@ -20,6 +20,9 @@ spec: forceRestart: true globalSettings: database.api.enabled: true + encPrivKey: + secretName: prvkey + passwordKey: privateKey poolSettings: - poolName: pdb1 autoUpgradeORDS: true @@ -30,12 +33,12 @@ spec: secretName: pdb1-ords-auth db.adminUser: SYS db.adminUser.secret: - secretName: pdb1-sys-auth + secretName: pdb1-sys-auth-enc - poolName: pdb2 db.connectionType: customurl db.customURL: jdbc:oracle:thin:@//localhost:1521/PDB2 db.secret: - secretName: pdb2-ords-auth + secretName: pdb2-ords-auth-enc ``` ## Minimum Privileges for Admin User diff --git a/docs/ordsservices/examples/adb.md b/docs/ordsservices/examples/adb.md index e1196455..ba53aac5 100644 --- a/docs/ordsservices/examples/adb.md +++ b/docs/ordsservices/examples/adb.md @@ -1,17 +1,15 @@ # Example: Autonomous Database without the OraOperator -This example walks through using the **ORDS Operator** with an Oracle Autonomous Database. +This example walks through using the **ORDSSRVS controller** with an Oracle Autonomous Database. This assumes that an ADB has already been provisioned and is configured as "Secure Access from Anywhere". Note that if behind a Proxy, this example will not work as the Wallet will need to be modified to support the proxy configuration. -### Install ORDS Operator -Install the Oracle ORDS Operator: +### Cert-Manager and Oracle Database Operator installation + +Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. -```bash -kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/latest/download/oracle-ords-operator.yaml -``` ### ADB Wallet Secret @@ -19,7 +17,7 @@ Download the ADB Wallet and create a Secret, replacing ` + --from-file= -n ordsnamespace ``` ### ADB ADMIN Password Secret @@ -27,8 +25,13 @@ kubectl create secret generic adb-wallet \ Create a Secret for the ADB ADMIN password, replacing with the real password: ```bash -kubectl create secret generic adb-db-auth \ - --from-literal=password= +echo adb-db-auth-enc +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.k +openssl rsa -in ca.key -outform PEM -pubout -out public.pem +kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace +openssl rsautl -encrypt -pubin -inkey public.pem -in adb-db-auth-enc |base64 > e_sidb-db-auth-enc +kubectl create secret generic adb-db-auth-enc --from-file=password=e_sidb-db-auth-enc -n ordsnamespace +rm adb-db-auth-enc e_sidb-db-auth-enc ``` ### Create RestDataServices Resource @@ -43,13 +46,17 @@ kubectl create secret generic adb-db-auth \ ```bash echo " apiVersion: database.oracle.com/v1 - kind: RestDataServices + kind: OrdsSrvs metadata: name: ords-adb + namespace: ordsnamespace spec: image: container-registry.oracle.com/database/ords:24.1.1 globalSettings: database.api.enabled: true + encPrivKey: + secretName: prvkey + passwordKey: privateKey poolSettings: - poolName: adb db.wallet.zip.service: _TP @@ -61,18 +68,18 @@ kubectl create secret generic adb-db-auth \ plsql.gateway.mode: proxied db.username: ORDS_PUBLIC_USER_OPER db.secret: - secretName: adb-db-auth + secretName: adb-db-auth-enc passwordKey: password db.adminUser: ADMIN db.adminUser.secret: - secretName: adb-db-auth + secretName: adb-db-auth-enc passwordKey: password" | kubectl apply -f - ``` latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** 1. Watch the restdataservices resource until the status is **Healthy**: ```bash - kubectl get restdataservices ords-adb -w + kubectl get ordssrvs ords-adb -w ``` **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. If APEX diff --git a/docs/ordsservices/examples/adb_oraoper.md b/docs/ordsservices/examples/adb_oraoper.md index b4ea5123..b0872fb3 100644 --- a/docs/ordsservices/examples/adb_oraoper.md +++ b/docs/ordsservices/examples/adb_oraoper.md @@ -1,41 +1,18 @@ # Example: Autonomous Database using the OraOperator -This example walks through using the **ORDS Operator** with a Containerised Oracle Database created by the **OraOperator** in the same Kubernetes Cluster. +This example walks through using the **ORDS Controller** with a Containerised Oracle Database created by the **ADB Controller** in the same Kubernetes Cluster. -When connecting to a mTLS enabled ADB while using the OraOperator to retreive the Wallet as is done in the example, it is currently not supported to have multiple, different databases supported by the single RestDataServices resource. This is due to a requirement to set the `TNS_ADMIN` parameter at the Pod level ([#97](https://github.com/oracle/oracle-database-operator/issues/97)). +When connecting to a mTLS enabled ADB while using the OraOperator to retreive the Wallet as is done in the example, it is currently not supported to have multiple, different databases supported by the single Ordssrvs resource. This is due to a requirement to set the `TNS_ADMIN` parameter at the Pod level ([#97](https://github.com/oracle/oracle-database-operator/issues/97)). -### Install Cert-Manager +### Cert-Manager and Oracle Database Operator installation -The OraOperator uses webhooks for validating user input before persisting it in etcd. -Webhooks require TLS certificates that are generated and managed by a certificate manager. - -Install [cert-manager](https://cert-manager.io/) with the following command: - -```bash -kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.5/cert-manager.yaml -``` -latest cert-manager version, **v1.14.5**, valid as of **30-May-2024** - -Check that all pods have a STATUS of **Running** before proceeding to the next step: -```bash -kubectl -n cert-manager get pods -``` - -Review [cert-managers installation documentation](https://cert-manager.io/docs/installation/kubectl/) for more information. - -### Install OraOperator - -Install the [Oracle Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/tree/main): - -```bash -kubectl apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/main/oracle-database-operator.yaml -``` +Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. ### Setup Oracle Cloud Authorisation In order for the OraOperator to access the ADB, some pre-requisites are required, as detailed [here](https://github.com/oracle/oracle-database-operator/blob/main/docs/adb/ADB_PREREQUISITES.md). Either establish Instance Principles or create the required ConfigMap/Secret. This example uses the later: -``` +```bash kubectl create configmap oci-cred \ --from-literal=tenancy= \ --from-literal=user= \ @@ -46,14 +23,6 @@ kubectl create secret generic oci-privatekey \ --from-file=privatekey= ``` -### Install ORDS Operator - -Install the Oracle ORDS Operator: - -```bash -kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/latest/download/oracle-ords-operator.yaml -``` - ### ADB ADMIN Password Secret Create a Secret for the ADB Admin password: @@ -110,7 +79,22 @@ kubectl create secret generic adb-oraoper-db-auth \ kubectl get adb/adb-oraoper -w ``` -### Create RestDataServices Resource +### Create encrypted password + + +```bash +echo ${DB_PWD} adb-db-auth-enc +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.k +openssl rsa -in ca.key -outform PEM -pubout -out public.pem +kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace +openssl rsautl -encrypt -pubin -inkey public.pem -in adb-db-auth-enc |base64 > e_adb-db-auth-enc +kubectl create secret generic adb-oraoper-db-auth-enc --from-file=password=e_adb-db-auth-enc -n ordsnamespace +rm adb-db-auth-enc e_adb-db-auth-enc +``` + + + +### Create OrdsSrvs Resource 1. Obtain the Service Name from the OraOperator @@ -126,13 +110,17 @@ kubectl create secret generic adb-oraoper-db-auth \ ```bash echo " apiVersion: database.oracle.com/v1 - kind: RestDataServices + kind: OrdsSrvs metadata: name: ords-adb-oraoper + namespace: ordsnamespace spec: image: container-registry.oracle.com/database/ords:24.1.1 forceRestart: true - globalSettings: + encPrivKey: + secretName: prvkey + passwordKey: privateKey + globalSettings: database.api.enabled: true poolSettings: - poolName: adb-oraoper @@ -145,18 +133,18 @@ kubectl create secret generic adb-oraoper-db-auth \ plsql.gateway.mode: proxied db.username: ORDS_PUBLIC_USER_OPER db.secret: - secretName: adb-oraoper-db-auth - passwordKey: adb-oraoper-db-auth + secretName: adb-oraoper-db-auth-enc + passwordKey: adb-oraoper-db-auth-enc db.adminUser: ADMIN db.adminUser.secret: - secretName: adb-oraoper-db-auth - passwordKey: adb-oraoper-db-auth" | kubectl apply -f - + secretName: adb-oraoper-db-auth-enc + passwordKey: adb-oraoper-db-auth-enc" | kubectl apply -f - ``` latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** -1. Watch the restdataservices resource until the status is **Healthy**: +1. Watch the ordssrvs resource until the status is **Healthy**: ```bash - kubectl get restdataservices ords-adb-oraoper -w + kubectl get ordssrvs ords-adb-oraoper -n ordsnamespace -w ``` **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. If APEX diff --git a/docs/ordsservices/examples/mongo_api.md b/docs/ordsservices/examples/mongo_api.md index 2d238ad2..70391fbd 100644 --- a/docs/ordsservices/examples/mongo_api.md +++ b/docs/ordsservices/examples/mongo_api.md @@ -1,6 +1,12 @@ # Example: Oracle API for MongoDB Support -This example walks through using the **ORDS Operator** with a Containerised Oracle Database to enable MongoDB API Support. +This example walks through using the **ORDSSRVS Controller** with a Containerised Oracle Database to enable MongoDB API Support. + + +### Cert-Manager and Oracle Database Operator installation + +Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. + ### Database Access @@ -12,11 +18,11 @@ the [Containerised Single Instance Database using the OraOperator](sidb_containe In the database, create an ORDS-enabled user. As this example uses the [Containerised Single Instance Database using the OraOperator](sidb_container.md), the following was performed: -1. Connect to the database: +1. Connect to the database: + ```bash DB_PWD=$(kubectl get secrets sidb-db-auth --template='{{.data.password | base64decode}}') POD_NAME=$(kubectl get pod -l "app=oraoper-sidb" -o custom-columns=NAME:.metadata.name --no-headers) - kubectl exec -it ${POD_NAME} -- sqlplus SYSTEM/${DB_PWD}@FREEPDB1 ``` @@ -26,11 +32,23 @@ In the database, create an ORDS-enabled user. As this example uses the [Contain grant soda_app, create session, create table, create view, create sequence, create procedure, create job, unlimited tablespace to MONGO; -- Connect as new user - conn MONGO/My_Password1!@FREEDB1; + conn MONGO/My_Password1!@FREEPDB1; exec ords.enable_schema; ``` -### Create RestDataServices Resource +### Create encrypted secrets + +```bash +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.k +openssl rsa -in ca.key -outform PEM -pubout -out public.pem +kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace +openssl rsautl -encrypt -pubin -inkey public.pem -in sidb-db-auth-enc |base64 > e_sidb-db-auth-enc +kubectl create secret generic sidb-db-auth-enc --from-file=password=e_sidb-db-auth-enc -n ordsnamespace +rm sidb-db-auth-enc e_sidb-db-auth-enc + +``` + +### Create ordssrvs Resource 1. Retrieve the Connection String from the containerised SIDB. @@ -52,13 +70,17 @@ In the database, create an ORDS-enabled user. As this example uses the [Contain ```bash echo " - apiVersion: database.oracle.com/v1 - kind: RestDataServices + apiVersion: database.oracle.com/v4 + kind: ordssrvs metadata: name: ords-sidb + namespace: ordsnamespace spec: image: container-registry.oracle.com/database/ords:24.1.1 forceRestart: true + encPrivKey: + secretName: prvkey + passwordKey: privateKey globalSettings: database.api.enabled: true mongo.enabled: true @@ -76,16 +98,16 @@ In the database, create an ORDS-enabled user. As this example uses the [Contain db.customURL: jdbc:oracle:thin:@//${CONN_STRING} db.username: ORDS_PUBLIC_USER db.secret: - secretName: sidb-db-auth + secretName: sidb-db-auth-enc db.adminUser: SYS db.adminUser.secret: - secretName: sidb-db-auth" | kubectl apply -f - + secretName: sidb-db-auth-enc" | kubectl apply -f - ``` latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** 1. Watch the restdataservices resource until the status is **Healthy**: ```bash - kubectl get restdataservices ords-sidb -w + kubectl get ordssrvs ords-sidb -w ``` **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. If APEX @@ -95,21 +117,21 @@ In the database, create an ORDS-enabled user. As this example uses the [Contain You can watch the APEX/ORDS Installation progress by running: ```bash - POD_NAME=$(kubectl get pod -l "app.kubernetes.io/instance=ords-sidb" -o custom-columns=NAME:.metadata.name --no-headers) + POD_NAME=$(kubectl get pod -l "app.kubernetes.io/instance=ords-sidb" -o custom-columns=NAME:.metadata.name -n ordsnamespace --no-headers) - kubectl logs ${POD_NAME} -c ords-sidb-init -f + kubectl logs ${POD_NAME} -c ords-sidb-init -n ordsnamespace -f ``` ### Test 1. Open a port-forward to the MongoAPI service, for example: ```bash - kubectl port-forward service/ords-sidb 27017:27017 + kubectl port-forward service/ords-sidb 27017:27017 -n ordsnamespace ``` 1. Connect to ORDS using the MongoDB shell: ```bash - mongosh --tlsAllowInvalidCertificates 'mongodb://MONGO:My_Password1!@localhost:27017/MONGO?authMechanism=PLAIN&authSource=$external&tls=true&retryWrites=false&loadBalanced=true' + mongosh --tlsAllowInvalidCertificates 'mongodb://MONGO:My_Password1!@localhost:27017/MONGO?authMechanism=PLAIN&authSource=$external&tls=true&retryWrites=false&loadBalanced=true' ``` 1. Insert some data: diff --git a/docs/ordsservices/examples/multi_pool.md b/docs/ordsservices/examples/multi_pool.md index e830bd9a..21c5f24d 100644 --- a/docs/ordsservices/examples/multi_pool.md +++ b/docs/ordsservices/examples/multi_pool.md @@ -1,16 +1,12 @@ # Example: Multipool, Multidatabase using a TNS Names file -This example walks through using the **ORDS Operator** with multiple databases using a TNS Names file. +This example walks through using the **ORDSSRVS Operator** with multiple databases using a TNS Names file. Keep in mind that all pools are running in the same Pod, therefore, changing the configuration of one pool will require a recycle of all pools. -### Install ORDS Operator +### Cert-Manager and Oracle Database Operator installation -Install the Oracle ORDS Operator: - -```bash -kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/latest/download/oracle-ords-operator.yaml -``` +Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. ### TNS_ADMIN Secret @@ -40,27 +36,47 @@ PDB3=(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=on)(ADDRESS=(PROTOCOL=TCP)(HOST=10 PDB4=(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=on)(ADDRESS=(PROTOCOL=TCP)(HOST=10.10.0.4)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=PDB4))) ``` +### PRIVATE KEY SECRET + +Secrets are encrypted using openssl rsa algorithm. Create public and private key. +Use private key to create a secret. + +```bash +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key +openssl rsa -in ca.key -outform PEM -pubout -out public.pem +kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace +``` + ### ORDS_PUBLIC_USER Secret Create a Secret for each of the databases `ORDS_PUBLIC_USER` user. If multiple databases use the same password, the same secret can be re-used. The following secret will be used for PDB1: + ```bash -kubectl create secret generic pdb1-ords-auth \ - --from-literal=password=pdb1-battery-staple +echo "THIS_IS_A_PASSWORD" > ordspwdfile +openssl rsautl -encrypt -pubin -inkey public.pem -in ordspwdfile |base64 > e_ordspwdfile +kubectl create secret generic pdb1-ords-auth-enc --from-file=password=e_ordspwdfile -n ordsnamespace +rm ordspwdfile e_ordspwdfile ``` The following secret will be used for PDB2: + ```bash -kubectl create secret generic pdb2-ords-auth \ - --from-literal=password=pdb2-battery-staple +echo "THIS_IS_A_PASSWORD" > ordspwdfile +openssl rsautl -encrypt -pubin -inkey public.pem -in ordspwdfile |base64 > e_ordspwdfile +kubectl create secret generic pdb2-ords-auth-enc --from-file=password=e_ordspwdfile -n ordsnamespace +rm ordspwdfile e_ordspwdfile ``` The following secret will be used for PDB3 and PDB4: + ```bash -kubectl create secret generic multi-ords-auth \ - --from-literal=password=multiple-battery-staple +echo "THIS_IS_A_PASSWORD" > ordspwdfile +openssl rsautl -encrypt -pubin -inkey public.pem -in ordspwdfile |base64 > e_ordspwdfile +kubectl create secret generic multi-ords-auth-enc --from-file=password=e_ordspwdfile -n ordsnamespace +rm ordspwdfile e_ordspwdfile ``` ### Privileged Secret (*Optional) @@ -70,23 +86,35 @@ If taking advantage of the [AutoUpgrade](../autoupgrade.md) functionality, creat In this example, only PDB1 will be set for [AutoUpgrade](../autoupgrade.md), the other PDBs already have APEX and ORDS installed. ```bash + + + +echo "THIS_IS_A_PASSWORD" > syspwdfile +openssl rsautl -encrypt -pubin -inkey public.pem -in ordspwdfile |base64 > e_syspwdfile +kubectl create secret generic pdb1-priv-auth-enc --from-file=password=e_syspwdfile -n ordsnamespace +rm syspwdfile e_syspwdfile + kubectl create secret generic pdb1-priv-auth \ --from-literal=password=pdb1-battery-staple ``` -### Create RestDataServices Resource +### Create OrdsSrvs Resource 1. Create a manifest for ORDS. ```bash echo " apiVersion: database.oracle.com/v1 - kind: RestDataServices + kind: OrdsSrvs metadata: name: ords-multi-pool + namespace: ordsnamespace spec: image: container-registry.oracle.com/database/ords:24.1.1 forceRestart: true + encPrivKey: + secretName: prvkey + passwordKey: privateKey globalSettings: database.api.enabled: true poolSettings: @@ -102,10 +130,10 @@ kubectl create secret generic pdb1-priv-auth \ plsql.gateway.mode: proxied db.username: ORDS_PUBLIC_USER db.secret: - secretName: pdb1-ords-auth + secretName: pdb1-ords-auth-enc db.adminUser: SYS db.adminUser.secret: - secretName: pdb1-priv-auth + secretName: pdb1-priv-auth-enc - poolName: pdb2 db.connectionType: tns db.tnsAliasName: PDB2 @@ -116,7 +144,7 @@ kubectl create secret generic pdb1-priv-auth \ plsql.gateway.mode: proxied db.username: ORDS_PUBLIC_USER db.secret: - secretName: pdb2-ords-auth + secretName: pdb2-ords-auth-enc - poolName: pdb3 db.connectionType: tns db.tnsAliasName: PDB3 @@ -127,7 +155,7 @@ kubectl create secret generic pdb1-priv-auth \ plsql.gateway.mode: proxied db.username: ORDS_PUBLIC_USER db.secret: - secretName: multi-ords-auth + secretName: multi-ords-auth-enc - poolName: pdb4 db.connectionType: tns db.tnsAliasName: PDB4 @@ -138,13 +166,13 @@ kubectl create secret generic pdb1-priv-auth \ plsql.gateway.mode: proxied db.username: ORDS_PUBLIC_USER db.secret: - secretName: multi-ords-auth" | kubectl apply -f - + secretName: multi-ords-auth-enc" | kubectl apply -f - ``` latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** -1. Watch the restdataservices resource until the status is **Healthy**: +1. Watch the ordssrvs resource until the status is **Healthy**: ```bash - kubectl get restdataservices ords-multi-pool -w + kubectl get OrdsSrvs ords-multi-pool -n ordsnamespace -w ``` **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. As APEX @@ -156,7 +184,7 @@ kubectl create secret generic pdb1-priv-auth \ Open a port-forward to the ORDS service, for example: ```bash -kubectl port-forward service/ords-multi-pool 8443:8443 +kubectl port-forward service/ords-multi-pool -n ordsnamespace 8443:8443 ``` 1. For PDB1, direct your browser to: `https://localhost:8443/ords/pdb1` diff --git a/docs/ordsservices/examples/sidb_container.md b/docs/ordsservices/examples/sidb_container.md index 4530360e..804ecca4 100644 --- a/docs/ordsservices/examples/sidb_container.md +++ b/docs/ordsservices/examples/sidb_container.md @@ -1,41 +1,11 @@ # Example: Containerised Single Instance Database using the OraOperator -This example walks through using the **ORDS Operator** with a Containerised Oracle Database created by the **OraOperator** in the same Kubernetes Cluster. +This example walks through using the **ORDSSRVS Controller** with a Containerised Oracle Database created by the **SIDB Controller** in the same Kubernetes Cluster. -### Install Cert-Manager +### Cert-Manager and Oracle Database Operator installation -The OraOperator uses webhooks for validating user input before persisting it in etcd. -Webhooks require TLS certificates that are generated and managed by a certificate manager. +Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. -Install [cert-manager](https://cert-manager.io/) with the following command: - -```bash -kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.5/cert-manager.yaml -``` -latest cert-manager version, **v1.14.5**, valid as of **2-May-2024** - -Check that all pods have a STATUS of **Running** before proceeding to the next step: -```bash -kubectl -n cert-manager get pods -``` - -Review [cert-managers installation documentation](https://cert-manager.io/docs/installation/kubectl/) for more information. - -### Install OraOperator - -Install the [Oracle Operator for Kubernetes](https://github.com/oracle/oracle-database-operator): - -```bash -kubectl apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/main/oracle-database-operator.yaml -``` - -### Install ORDS Operator - -Install the Oracle ORDS Operator: - -```bash -kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/latest/download/oracle-ords-operator.yaml -``` ### Deploy a Containerised Oracle Database @@ -79,6 +49,23 @@ kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/late **NOTE**: If this is the first time pulling the free database image, it may take up to 15 minutes for the database to become available. +### Create encryped secret + +```bash + +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key +openssl rsa -in ca.key -outform PEM -pubout -out public.pem +kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace + +echo "${DB_PWD}" > sidb-db-auth +openssl rsautl -encrypt -pubin -inkey public.pem -in sidb-db-auth |base64 > e_sidb-db-auth +kubectl create secret generic sidb-db-auth-enc --from-file=password=e_sidb-db-auth -n ordsnamespace +rm sidb-db-auth e_sidb-db-auth + + +``` + + ### Create RestDataServices Resource 1. Retrieve the Connection String from the containerised SIDB. @@ -103,9 +90,10 @@ kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/late ```bash echo " apiVersion: database.oracle.com/v1 - kind: RestDataServices + kind: OrdsSrvs metadata: name: ords-sidb + namespace: ordsnamespace spec: image: container-registry.oracle.com/database/ords:24.1.1 forceRestart: true @@ -121,16 +109,16 @@ kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/late db.customURL: jdbc:oracle:thin:@//${CONN_STRING} db.username: ORDS_PUBLIC_USER db.secret: - secretName: sidb-db-auth + secretName: sidb-db-auth-enc db.adminUser: SYS db.adminUser.secret: - secretName: sidb-db-auth" | kubectl apply -f - + secretName: sidb-db-auth-enc" | kubectl apply -f - ``` latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** -1. Watch the restdataservices resource until the status is **Healthy**: +1. Watch the ordssrvs resource until the status is **Healthy**: ```bash - kubectl get restdataservices ords-sidb -w + kubectl get ordssrvs ords-sidb -n ordsnamespace -w ``` **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. If APEX @@ -140,9 +128,9 @@ kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/late You can watch the APEX/ORDS Installation progress by running: ```bash - POD_NAME=$(kubectl get pod -l "app.kubernetes.io/instance=ords-sidb" -o custom-columns=NAME:.metadata.name --no-headers) + POD_NAME=$(kubectl get pod -l "app.kubernetes.io/instance=ords-sidb" -n ordsnamespace -o custom-columns=NAME:.metadata.name --no-headers) - kubectl logs ${POD_NAME} -c ords-sidb-init -f + kubectl logs ${POD_NAME} -c ords-sidb-init -n ordsnamespace -f ``` ### Test @@ -150,7 +138,7 @@ kubectl apply -f https://github.com/gotsysdba/oracle-ords-operator/releases/late Open a port-forward to the ORDS service, for example: ```bash -kubectl port-forward service/ords-sidb 8443:8443 +kubectl port-forward service/ords-sidb -n ordsnamespace 8443:8443 ``` Direct your browser to: `https://localhost:8443/ords` @@ -163,4 +151,4 @@ This example has a single database pool, named `default`. It is set to: * Automatically install/update ORDS on startup, if required: `autoUpgradeORDS: true` * Automatically install/update APEX on startup, if required: `autoUpgradeAPEX: true` * Use a basic connection string to connect to the database: `db.customURL: jdbc:oracle:thin:@//${CONN_STRING}` -* The `passwordKey` has been ommitted from both `db.secret` and `db.adminUser.secret` as the password was stored in the default key (`password`) \ No newline at end of file +* The `passwordKey` has been ommitted from both `db.secret` and `db.adminUser.secret` as the password was stored in the default key (`password`) diff --git a/docs/ordsservices/usecase01/create_multisrv.yaml b/docs/ordsservices/usecase01/create_multisrv.yaml deleted file mode 100644 index 35ad8f85..00000000 --- a/docs/ordsservices/usecase01/create_multisrv.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: OrdsSrvs -metadata: - name: ords-multi-pool - namespace: ordsnamespace -spec: - image: container-registry.oracle.com/database/ords:24.1.1 - forceRestart: true - globalSettings: - database.api.enabled: true - poolSettings: - - poolName: pdb1 - autoUpgradeORDS: true - db.connectionType: tns - db.tnsAliasName: pdb1 - tnsAdminSecret: - secretName: multi-tns-admin - restEnabledSql.active: true - feature.sdw: true - plsql.gateway.mode: proxied - db.username: ORDS_PUBLIC_USER - db.secret: - secretName: multi-ords-auth - db.adminUser: SYS - db.adminUser.secret: - secretName: pdb1-priv-auth - - poolName: pdb2 - db.connectionType: tns - db.tnsAliasName: PDB2 - tnsAdminSecret: - secretName: multi-tns-admin - restEnabledSql.active: true - feature.sdw: true - plsql.gateway.mode: proxied - db.username: ORDS_PUBLIC_USER - db.secret: - secretName: multi-ords-auth - db.adminUser: SYS - db.adminUser.secret: - secretName: pdb2-priv-auth - - diff --git a/docs/ordsservices/usecase01/create_ords.sh b/docs/ordsservices/usecase01/create_ords.sh deleted file mode 100644 index ee892ddf..00000000 --- a/docs/ordsservices/usecase01/create_ords.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -set -x - -TNS_ALIAS=`kubectl get singleinstancedatabase oraoper-sidb -n oracle-database-operator-system -o jsonpath='{.status.pdbConnectString}'` - -echo " -apiVersion: database.oracle.com/v1alpha1 -kind: RestDataServices -metadata: - name: ords-sidb - namespace: oracle-database-operator-system -spec: - image: container-registry.oracle.com/database/ords:24.1.0 - forceRestart: true - globalSettings: - database.api.enabled: true - poolSettings: - - poolName: default - autoUpgradeORDS: true - autoUpgradeAPEX: true - restEnabledSql.active: true - plsql.gateway.mode: direct - db.connectionType: customurl - db.customURL: jdbc:oracle:thin:@//${TNS_ALIAS} - db.username: ORDS_PUBLIC_USER - db.secret: - secretName: sidb-db-auth - db.adminUser: SYS - db.adminUser.secret: - secretName: sidb-db-auth" | kubectl apply -f - diff --git a/docs/ordsservices/usecase01/create_ords.yaml b/docs/ordsservices/usecase01/create_ords.yaml deleted file mode 100644 index 8b8374bf..00000000 --- a/docs/ordsservices/usecase01/create_ords.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: database.oracle.com/v1 -kind: RestDataServices -metadata: - name: ords-sidb - namespace: oracle-database-operator-system -spec: - image: container-registry.oracle.com/database/ords:24.1.0 - pullSecrets: oracle-container-registry-secret - forceRestart: true - globalSettings: - database.api.enabled: true - poolSettings: - - poolName: default - autoUpgradeORDS: true - autoUpgradeAPEX: true - restEnabledSql.active: true - plsql.gateway.mode: direct - db.connectionType: customurl - db.customURL: jdbc:oracle:thin:@//204.216.216.178:30437/FREEPDB1 - db.username: ORDS_PUBLIC_USER - db.secret: - secretName: sidb-db-auth - db.adminUser: SYS - db.adminUser.secret: - secretName: sidb-db-auth diff --git a/docs/ordsservices/usecase01/create_ordsnamespace.yaml b/docs/ordsservices/usecase01/create_ordsnamespace.yaml deleted file mode 100644 index ceeefd36..00000000 --- a/docs/ordsservices/usecase01/create_ordsnamespace.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: ordsnamespace diff --git a/docs/ordsservices/usecase01/create_registry_secret.sh b/docs/ordsservices/usecase01/create_registry_secret.sh deleted file mode 100644 index fc03855b..00000000 --- a/docs/ordsservices/usecase01/create_registry_secret.sh +++ /dev/null @@ -1,4 +0,0 @@ -echo enter password for matteo.malvezzi@oracle.com@container-registry.oracle.com -read -s scpwd -/usr/local/go/bin/kubectl create secret docker-registry oracle-container-registry-secret --docker-server=container-registry.oracle.com --docker-username=matteo.malvezzi@oracle.com --docker-password=$scpwd --docker-email=matteo.malvezzi@oracle.com -n oracle-database-operator-system -/usr/local/go/bin/kubectl create secret docker-registry oracle-container-registry-secret --docker-server=container-registry.oracle.com --docker-username=matteo.malvezzi@oracle.com --docker-password=$scpwd --docker-email=matteo.malvezzi@oracle.com -n ordsnamespace diff --git a/docs/ordsservices/usecase01/create_secretregistry.sh b/docs/ordsservices/usecase01/create_secretregistry.sh deleted file mode 100644 index 656eb19e..00000000 --- a/docs/ordsservices/usecase01/create_secretregistry.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -read pwd -kubectl create secret docker-registry oracle-container-registry-secret --docker-server=container-registry.oracle.com --docker-username=matteo.malvezzi@oraclecom --docker-password=$pwd --docker-email=matteo.malvezzi@oracle.com -n oracle-database-operator-system - - diff --git a/docs/ordsservices/usecase01/create_singleinstance_db.yaml b/docs/ordsservices/usecase01/create_singleinstance_db.yaml deleted file mode 100644 index 0c3918bd..00000000 --- a/docs/ordsservices/usecase01/create_singleinstance_db.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: SingleInstanceDatabase -metadata: - name: oraoper-sidb - namespace: oracle-database-operator-system -spec: - replicas: 1 - image: - pullFrom: container-registry.oracle.com/database/free:23.4.0.0 - pullSecrets: oracle-container-registry-secret - prebuiltDB: true - sid: FREE - edition: free - adminPassword: - secretName: sidb-db-auth - secretKey: password - pdbName: FREEPDB1 diff --git a/docs/ordsservices/usecase01/default-ns-role-binding.yaml b/docs/ordsservices/usecase01/default-ns-role-binding.yaml deleted file mode 100644 index 03adfcb3..00000000 --- a/docs/ordsservices/usecase01/default-ns-role-binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding - namespace: default -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/ordsservices/usecase01/diaginit.sh b/docs/ordsservices/usecase01/diaginit.sh deleted file mode 100644 index 3373fba6..00000000 --- a/docs/ordsservices/usecase01/diaginit.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -NAMESPACE=${1:-"ordsnamespace"} -KUBECTL=/usr/bin/kubectl -for _pod in `${KUBECTL} get pods --no-headers -o custom-columns=":metadata.name" --no-headers -n ${NAMESPACE}` -do - for _podinit in `${KUBECTL} get pod ${_pod} -n ${NAMESPACE} -o="custom-columns=INIT-CONTAINERS:.spec.initContainers[*].name" --no-headers` - do - echo "DUMPINIT ${_pod}:${_podinit}" - ${KUBECTL} logs -f --since=0 ${_pod} -n ${NAMESPACE} -c ${_podinit} - done -done diff --git a/docs/ordsservices/usecase01/makefile b/docs/ordsservices/usecase01/makefile index 54df7272..76b47210 100644 --- a/docs/ordsservices/usecase01/makefile +++ b/docs/ordsservices/usecase01/makefile @@ -3,12 +3,25 @@ # # # NAME -# makefile: automation exectution of the follwing configuration -# in a multi namespace environment (orcle-database-operator-system, -# ordsnamespace) -# -# - SingleInstanceDatabase -# - Mongo API +# makefile: +# This makefile helps to set up multipool and sidb cases +# edit the following variables with your system information +# and execute make help to list the list of avilable targets +# + +export PDB1=pdb1 +export PDB2=pdb2 +export TNS1=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=$(PDB1)))) +export TNS2=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=$(PDB2)))) +export SIDB_PASSWORD=....write password here .... +export PDB1_PWD=.....write password here.... +export PDB2_PWD=.....write password.... +export ORDS_MULTI_POOL_PWD=....write password here.... +export USER_CONTAINER_REGISTRY=username@oracle.com <--Your OCR account +export OPRNAMESPACE=oracle-database-operator-system +export ORDSNAMESPACE=ordsnamespace + + # # DESCRIPTION # Main makefile - see target table @@ -56,31 +69,31 @@ # -export OPRNAMESPACE=oracle-database-operator-system -export RSTNAMESPACE=ordsnamespace -export WATCHLIST=$(OPRNAMESPACE),$(RSTNAMESPACE) + +export WATCHLIST=$(OPRNAMESPACE),$(ORDSNAMESPACE) export CREATE_SINGLEINSTANCE=create_singleinstance_db.yaml export CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml export SIDB_SECRET=sidb-db-auth export ORDS_SECRET=ords-db-auth +export MULTI_ORDS_AUTH_SECRET=multi-ords-auth-enc +export PDB1_PRIV_AUTH_SECRET=pdb1-priv-auth-enc +export PDB2_PRIV_AUTH_SECRET=pdb2-priv-auth-enc + + export SIDB_IMAGE=container-registry.oracle.com/database/free:23.4.0.0 export ORDS_IMAGE=container-registry.oracle.com/database/ords:24.1.0 export ORDS_IMAGE.1=container-registry.oracle.com/database/ords:24.1.1 -export SIDB_PASSWORD=RSTpoc_061319 export SECRET_CONTAINER_REGISTRY=oracle-container-registry-secret export ORACLE_CONTAINER_REGISTRY=container-registry.oracle.com -export USER_CONTAINER_REGISTRY=matteo.malvezzi@oracle.com export REST_SERVER_NAME=ords-sidb export REST_SERVER_NAME_MONGO=ords-sidb-mongo export MONGOSH=mongosh-2.3.1-linux-x64 export KIND=OrdsSrvs -export PDB1=pdb1 -export PDB2=pdb2 -export TNS1=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=$(PDB1)))) -export TNS2=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=$(PDB2)))) export TNSNAMES=./tnsnames.ora export TNSADMIN=`pwd`/tnsadmin +export PRVKEY=ca.key +export PUBKEY=public.pem ## CMD SECTION## export KUBECTL=/usr/local/go/bin/kubectl @@ -88,9 +101,10 @@ export DIFF=/usr/bin/diff export MAKE=/usr/bin/make export CURL=/usr/bin/curl export TAR=/usr/bin/tar +export OPENSSL=/usr/bin/openssl ## YAML AND OTHER FILES ## -CREATE_RSTNAMESPACE=create_$(RSTNAMESPACE).yaml +export CREATE_ORDSNAMESPACE=create_$(ORDSNAMESPACE).yaml export DEFAULT_NAMESPACE_SCOPE=default-ns-role-binding.yaml export RST_NAMESPACE_SCOPE=ords-ns-role-binding.yaml export ORACLE_OPERATOR_YAML=../../../oracle-database-operator.yaml @@ -112,7 +126,7 @@ define manpage @printf "\n" @printf "\033[7m%s\033[0m \033[7m%s\033[0m \033[7m%s\033[0m\n" "TARGET " "DESCRIPTION " "YAML FILE " @printf "%s %s %s\n" "---------" " --------------------------------------------------" "--------------------------------------" -@printf "%-40s %+20s \033[1m %s\033[0m\n" "step0/a/d setup new namespace" " " "$(CREATE_RSTNAMESPACE)" +@printf "%-40s %+20s \033[1m %s\033[0m\n" "step0/a/d setup new namespace" " " "$(CREATE_ORDSNAMESPACE)" @printf "%-40s %+20s \033[1m %s\033[0m\n" "step1/a/d setup certmaneger " " " "$(CERTMANAGER)" @printf "%-40s %+20s \033[1m %s\033[0m\n" "step2/a/d setup operator" " " "$(shell basename $(ORACLE_OPERATOR_YAML))" @printf "%-40s %+20s \033[1m %s\033[0m\n" "step3/a/d default scoped deployment" " " "$(DEFAULT_NAMESPACE_SCOPE)" @@ -148,7 +162,7 @@ man: $(call manpage) define namespace -cat< $(CREATE_RSTNAMESPACE) +cat< $(CREATE_ORDSNAMESPACE) #apiVersion: v1 #kind: Namespace #metadata: @@ -156,12 +170,12 @@ cat< $(CREATE_RSTNAMESPACE) # control-plane: controller-manager # name: $(2) EOF -$(KUBECTL) $(1) -f $(CREATE_RSTNAMESPACE) +$(KUBECTL) $(1) -f $(CREATE_ORDSNAMESPACE) $(KUBECTL) get namespace endef step0: - $(call namespace,$(ACTION),$(RSTNAMESPACE)) + $(call namespace,$(ACTION),$(ORDSNAMESPACE)) step0a: $(MAKE) -f $(MAKEFILE) step0 ACTION=apply step0d: @@ -197,8 +211,8 @@ cat<$(RST_NAMESPACE_SCOPE) #apiVersion: rbac.authorization.k8s.io/v1 #kind: RoleBinding #metadata: -# name: $(RSTNAMESPACE)-rolebinding -# namespace: $(RSTNAMESPACE) +# name: $(ORDSNAMESPACE)-rolebinding +# namespace: $(ORDSNAMESPACE) #roleRef: # apiGroup: rbac.authorization.k8s.io # kind: ClusterRole @@ -227,7 +241,7 @@ EOF $(KUBECTL) $(1) -f $(RST_NAMESPACE_SCOPE) $(KUBECTL) $(1) -f $(DEFAULT_NAMESPACE_SCOPE) -$(KUBECTL) get RoleBinding -n $(RSTNAMESPACE) +$(KUBECTL) get RoleBinding -n $(ORDSNAMESPACE) endef @@ -353,19 +367,43 @@ step4a: step4d: $(MAKE) -f $(MAKEFILE) ACTION=delete step4 + +export SYSPWDFILE1=syspwdfile +export ORDPWDFILE=ordspwdfile +export SIDB_PASSWORD_FILE=sidbpasswordfile + +export PRVKEY=ca.key +export PUBKEY=public.pem +export OPENSSL=/usr/bin/openssl + step5a: - $(KUBECTL) create secret generic $(SIDB_SECRET) --from-literal=password=$(SIDB_PASSWORD) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic $(ORDS_SECRET) --from-literal=password=$(SIDB_PASSWORD) -n $(RSTNAMESPACE) + echo $(SIDB_PASSWORD) > $(SIDB_PASSWORD_FILE) + - $(KUBECTL) delete secret pubkey -n ${ORDSNAMESPACE} + - $(KUBECTL) delete secret prvkey -n ${ORDSNAMESPACE} + - $(KUBECTL) delete secret $(SIDB_SECRET) -n ${ORDSNAMESPACE} + - $(KUBECTL) delete secret $(ORDS_SECRET) -n ${ORDSNAMESPACE} + $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ${PRVKEY} + $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) + $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(ORDSNAMESPACE) + $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(ORDSNAMESPACE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SIDB_PASSWORD_FILE) |base64 > e_$(SIDB_PASSWORD_FILE) + $(KUBECTL) create secret generic $(SIDB_SECRET) --from-literal=password=$(SIDB_PASSWORD) -n $(OPRNAMESPACE) + $(KUBECTL) create secret generic $(ORDS_SECRET) --from-file=password=e_$(SIDB_PASSWORD_FILE) -n $(ORDSNAMESPACE) + $(RM) e_$(SIDB_PASSWORD_FILE) $(SIDB_PASSWORD_FILE) + step5d: - $(KUBECTL) delete secret $(SIDB_SECRET) -n $(OPRNAMESPACE) - $(KUBECTL) delete secret $(ORDS_SECRET) -n $(RSTNAMESPACE) + - $(KUBECTL) delete secret pubkey -n ${ORDSNAMESPACE} + - $(KUBECTL) delete secret prvkey -n ${ORDSNAMESPACE} + - $(KUBECTL) delete secret $(SIDB_SECRET) -n ${ORDSNAMESPACE} + - $(KUBECTL) delete secret $(ORDS_SECRET) -n ${ORDSNAMESPACE} + define registry_secret printf "#!/bin/bash \n" >$(SECRET_CONTAINER_REGISTRY_SCRIPT) printf "echo enter password for $(USER_CONTAINER_REGISTRY)@$(ORACLE_CONTAINER_REGISTRY) \n" >$(SECRET_CONTAINER_REGISTRY_SCRIPT) printf "read -s scpwd \n" >>$(SECRET_CONTAINER_REGISTRY_SCRIPT) printf "$(KUBECTL) create secret docker-registry $(SECRET_CONTAINER_REGISTRY) --docker-server=$(ORACLE_CONTAINER_REGISTRY) --docker-username=$(USER_CONTAINER_REGISTRY) --docker-password=\u0024scpwd --docker-email=$(USER_CONTAINER_REGISTRY) -n $(OPRNAMESPACE) \n" >>$(SECRET_CONTAINER_REGISTRY_SCRIPT) -printf "$(KUBECTL) create secret docker-registry $(SECRET_CONTAINER_REGISTRY) --docker-server=$(ORACLE_CONTAINER_REGISTRY) --docker-username=$(USER_CONTAINER_REGISTRY) --docker-password=\u0024scpwd --docker-email=$(USER_CONTAINER_REGISTRY) -n $(RSTNAMESPACE) \n" >>$(SECRET_CONTAINER_REGISTRY_SCRIPT) +printf "$(KUBECTL) create secret docker-registry $(SECRET_CONTAINER_REGISTRY) --docker-server=$(ORACLE_CONTAINER_REGISTRY) --docker-username=$(USER_CONTAINER_REGISTRY) --docker-password=\u0024scpwd --docker-email=$(USER_CONTAINER_REGISTRY) -n $(ORDSNAMESPACE) \n" >>$(SECRET_CONTAINER_REGISTRY_SCRIPT) bash $(SECRET_CONTAINER_REGISTRY_SCRIPT) endef @@ -380,7 +418,7 @@ step6d: define sidb cat<$(SIDB_CREATION) -#apiVersion: database.oracle.com/v1alpha1 +#apiVersion: database.oracle.com/v4 #kind: SingleInstanceDatabase #metadata: # name: oraoper-sidb @@ -413,14 +451,17 @@ step7d: define restservice cat<$(REST_SERVER_CREATION) -#apiVersion: database.oracle.com/v1alpha1 +#apiVersion: database.oracle.com/v4 #kind: $(KIND) #metadata: # name: $(REST_SERVER_NAME) -# namespace: $(RSTNAMESPACE) +# namespace: $(ORDSNAMESPACE) #spec: # image: $(ORDS_IMAGE) # forceRestart: true +# encPrivKey: +# secretName: prvkey +# passwordKey: privateKey # globalSettings: # database.api.enabled: true # poolSettings: @@ -470,9 +511,9 @@ reloadop: $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - loginords: - @$(eval RESTPOD := $(shell $(KUBECTL) get pods --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' -n $(RSTNAMESPACE))) - $(KUBECTL) logs $(RESTPOD) -n $(RSTNAMESPACE) - $(KUBECTL) exec $(RESTPOD) -n $(RSTNAMESPACE) -it -- /bin/bash + @$(eval RESTPOD := $(shell $(KUBECTL) get pods --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' -n $(ORDSNAMESPACE))) + $(KUBECTL) logs $(RESTPOD) -n $(ORDSNAMESPACE) + $(KUBECTL) exec $(RESTPOD) -n $(ORDSNAMESPACE) -it -- /bin/bash logindb: $(eval PODPDB := $(shell $(KUBECTL) get pods --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' -n $(OPRNAMESPACE)|grep -v oracle-database-operator)) @@ -483,8 +524,8 @@ logindb: report: $(KUBECTL) get pods -n $(OPRNAMESPACE) $(KUBECTL) get SingleInstanceDatabase -n $(OPRNAMESPACE) - $(KUBECTL) get pods -n $(RSTNAMESPACE) - $(KUBECTL) get $(KIND) -n $(RSTNAMESPACE) + $(KUBECTL) get pods -n $(ORDSNAMESPACE) + $(KUBECTL) get $(KIND) -n $(ORDSNAMESPACE) someattributes: @@ -546,11 +587,11 @@ sqlplus: define restservicemongo cat <$(REST_SERVER_CREATION_MONGO) -#apiVersion: database.oracle.com/v1alpha1 +#apiVersion: database.oracle.com/v4 #kind: $(KIND) #metadata: # name: $(REST_SERVER_NAME_MONGO) -# namespace: $(RSTNAMESPACE) +# namespace: $(ORDSNAMESPACE) #spec: # image: $(ORDS_IMAGE.1) # forceRestart: true @@ -593,7 +634,7 @@ step10d: step11: echo "Open a port-forward to the MongoAPI service" - @nohup $(KUBECTL) port-forward service/$(REST_SERVER_NAME_MONGO) 27017:27017 -n $(RSTNAMESPACE) 1>portfwd.log 2>&1 & + @nohup $(KUBECTL) port-forward service/$(REST_SERVER_NAME_MONGO) 27017:27017 -n $(ORDSNAMESPACE) 1>portfwd.log 2>&1 & @echo "DOWNLOADING MONGOSH" @$(CURL) https://downloads.mongodb.com/compass/$(MONGOSH).tgz --output mongosh-2.3.1-linux-x64.tgz @echo "UNTAR FILE" @@ -612,42 +653,62 @@ $(PDB1)=$(TNS1) $(PDB2)=$(TNS2) EOF -$(KUBECTL) create secret generic multi-tns-admin -n $(RSTNAMESPACE) --from-file=$(TNSADMIN)/ +$(KUBECTL) create secret generic multi-tns-admin -n $(ORDSNAMESPACE) --from-file=$(TNSADMIN)/ endef step12a: $(call buildtns) step12d: - $(KUBECTL) delete secret multi-tns-admin -n $(RSTNAMESPACE) + $(KUBECTL) delete secret multi-tns-admin -n $(ORDSNAMESPACE) + +export SYSPWDFILE1=syspwdfile1 +export SYSPWDFILE2=syspwdfile2 +export ORDPWDFILE=ordspwdfile step13a: - $(KUBECTL) create secret generic multi-ords-auth -n $(RSTNAMESPACE) --from-literal=password=chageme - $(KUBECTL) create secret generic pdb1-priv-auth -n $(RSTNAMESPACE) --from-literal=password=WElcome_12## - $(KUBECTL) create secret generic pdb2-priv-auth -n $(RSTNAMESPACE) --from-literal=password=WElcome_12## + echo $(PDB1_PWD) > $(SYSPWDFILE1) + echo $(PDB2_PWD) > $(SYSPWDFILE2) + echo $(ORDS_MULTI_POOL_PWD) > $(ORDPWDFILE) + $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ${PRVKEY} + $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) + #$(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(ORDSNAMESPACE) + $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(ORDSNAMESPACE) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE1) |base64 > e_$(SYSPWDFILE1) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE2) |base64 > e_$(SYSPWDFILE2) + $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(ORDPWDFILE) |base64 > e_$(ORDPWDFILE) + $(KUBECTL) create secret generic $(PDB1_PRIV_AUTH_SECRET) --from-file=password=e_$(SYSPWDFILE1) -n $(ORDSNAMESPACE) + $(KUBECTL) create secret generic $(PDB2_PRIV_AUTH_SECRET) --from-file=password=e_$(SYSPWDFILE2) -n $(ORDSNAMESPACE) + $(KUBECTL) create secret generic $(MULTI_ORDS_AUTH_SECRET) --from-file=password=e_$(ORDPWDFILE) -n $(ORDSNAMESPACE) + $(RM) $(SYSPWDFILE1) $(SYSPWDFILE2) $(ORDPWDFILE) e_$(SYSPWDFILE1) e_$(SYSPWDFILE2) e_$(ORDPWDFILE) step13d: - $(KUBECTL) delete secret multi-ords-auth -n $(RSTNAMESPACE) - $(KUBECTL) delete secret pdb1-priv-auth -n $(RSTNAMESPACE) - $(KUBECTL) delete secret pdb2-priv-auth -n $(RSTNAMESPACE) - + - $(KUBECTL) delete secret pubkey -n $(ORDSNAMESPACE) + - $(KUBECTL) delete secret prvkey -n $(ORDSNAMESPACE) + - $(KUBECTL) delete secret $(PDB1_PRIV_AUTH_SECRET) -n $(ORDSNAMESPACE) + - $(KUBECTL) delete secret $(PDB2_PRIV_AUTH_SECRET) -n $(ORDSNAMESPACE) + - $(KUBECTL) delete secret $(MULTI_ORDS_AUTH_SECRET) -n $(ORDSNAMESPACE) define multisrv cat <$(MULTISRV_MANIFEST) -#apiVersion: database.oracle.com/v1alpha1 +#apiVersion: database.oracle.com/v4 #kind: $(KIND) #metadata: # name: ords-multi-pool -# namespace: $(RSTNAMESPACE) +# namespace: $(ORDSNAMESPACE) #spec: # image: container-registry.oracle.com/database/ords:24.1.1 # forceRestart: true +# encPrivKey: +# secretName: prvkey +# passwordKey: privateKey # globalSettings: # database.api.enabled: true # poolSettings: # - poolName: pdb1 -# autoUpgradeORDS: true +# autoUpgradeAPEX: false +# autoUpgradeORDS: false # db.connectionType: tns # db.tnsAliasName: pdb1 # tnsAdminSecret: @@ -657,11 +718,13 @@ cat <$(MULTISRV_MANIFEST) # plsql.gateway.mode: proxied # db.username: ORDS_PUBLIC_USER # db.secret: -# secretName: multi-ords-auth +# secretName: $(MULTI_ORDS_AUTH_SECRET) # db.adminUser: SYS # db.adminUser.secret: -# secretName: pdb1-priv-auth +# secretName: $(PDB1_PRIV_AUTH_SECRET) # - poolName: pdb2 +# autoUpgradeAPEX: false +# autoUpgradeORDS: false # db.connectionType: tns # db.tnsAliasName: PDB2 # tnsAdminSecret: @@ -671,10 +734,10 @@ cat <$(MULTISRV_MANIFEST) # plsql.gateway.mode: proxied # db.username: ORDS_PUBLIC_USER # db.secret: -# secretName: multi-ords-auth +# secretName: $(MULTI_ORDS_AUTH_SECRET) # db.adminUser: SYS # db.adminUser.secret: -# secretName: pdb2-priv-auth +# secretName: $(PDB1_PRIV_AUTH_SECRET) # EOF @@ -711,5 +774,5 @@ done endef diagordsinit: - $(call dumpinit ,$(RSTNAMESPACE)) + $(call dumpinit ,$(ORDSNAMESPACE)) diff --git a/docs/ordsservices/usecase01/node-rbac.yaml b/docs/ordsservices/usecase01/node-rbac.yaml deleted file mode 100644 index 58823a22..00000000 --- a/docs/ordsservices/usecase01/node-rbac.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-manager-role-node -rules: -- apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-manager-role-node-cluster-role-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role-node -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/ordsservices/usecase01/node_rbac.yaml b/docs/ordsservices/usecase01/node_rbac.yaml deleted file mode 100644 index 58823a22..00000000 --- a/docs/ordsservices/usecase01/node_rbac.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-manager-role-node -rules: -- apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-manager-role-node-cluster-role-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role-node -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/ordsservices/usecase01/oracle-database-operator.yaml b/docs/ordsservices/usecase01/oracle-database-operator.yaml deleted file mode 100644 index 87cf6582..00000000 --- a/docs/ordsservices/usecase01/oracle-database-operator.yaml +++ /dev/null @@ -1,4956 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-system ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomouscontainerdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousContainerDatabase - listKind: AutonomousContainerDatabaseList - plural: autonomouscontainerdatabases - shortNames: - - acd - - acds - singular: autonomouscontainerdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.displayName - name: DisplayName - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase - properties: - action: - enum: - - SYNC - - RESTART - - TERMINATE - type: string - autonomousContainerDatabaseOCID: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - autonomousExadataVMClusterOCID: - type: string - compartmentOCID: - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - hardLink: - default: false - type: boolean - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - patchModel: - description: 'AutonomousContainerDatabasePatchModelEnum Enum with underlying type: string' - enum: - - RELEASE_UPDATES - - RELEASE_UPDATE_REVISIONS - type: string - type: object - status: - description: AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase - properties: - lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - timeCreated: - type: string - required: - - lifecycleState - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabasebackups.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseBackup - listKind: AutonomousDatabaseBackupList - plural: autonomousdatabasebackups - shortNames: - - adbbu - - adbbus - singular: autonomousdatabasebackup - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.dbDisplayName - name: DB DisplayName - type: string - - jsonPath: .status.type - name: Type - type: string - - jsonPath: .status.timeStarted - name: Started - type: string - - jsonPath: .status.timeEnded - name: Ended - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AutonomousDatabaseBackupSpec defines the desired state of AutonomousDatabaseBackup - properties: - autonomousDatabaseBackupOCID: - type: string - displayName: - type: string - isLongTermBackup: - type: boolean - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - retentionPeriodInDays: - type: integer - target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' - properties: - k8sADB: - description: "*********************** *\tADB spec ***********************" - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - type: object - status: - description: AutonomousDatabaseBackupStatus defines the observed state of AutonomousDatabaseBackup - properties: - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - dbDisplayName: - type: string - dbName: - type: string - isAutomatic: - type: boolean - lifecycleState: - description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with underlying type: string' - type: string - timeEnded: - type: string - timeStarted: - type: string - type: - description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying type: string' - type: string - required: - - autonomousDatabaseOCID - - compartmentOCID - - dbDisplayName - - dbName - - isAutomatic - - lifecycleState - - type - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabaserestores.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseRestore - listKind: AutonomousDatabaseRestoreList - plural: autonomousdatabaserestores - shortNames: - - adbr - - adbrs - singular: autonomousdatabaserestore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.displayName - name: DbDisplayName - type: string - - jsonPath: .status.dbName - name: DbName - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore - properties: - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - source: - properties: - k8sADBBackup: - description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.' - properties: - name: - type: string - type: object - pointInTime: - properties: - timestamp: - description: 'The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT' - type: string - type: object - type: object - target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' - properties: - k8sADB: - description: "*********************** *\tADB spec ***********************" - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - required: - - source - - target - type: object - status: - description: AutonomousDatabaseRestoreStatus defines the observed state of AutonomousDatabaseRestore - properties: - dbName: - type: string - displayName: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - status: - description: 'WorkRequestStatusEnum Enum with underlying type: string' - type: string - timeAccepted: - type: string - timeEnded: - type: string - timeStarted: - type: string - workRequestOCID: - type: string - required: - - dbName - - displayName - - status - - workRequestOCID - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases - shortNames: - - adb - - adbs - singular: autonomousdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.details.displayName - name: Display Name - type: string - - jsonPath: .spec.details.dbName - name: Db Name - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .spec.details.isDedicated - name: Dedicated - type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .spec.details.dataStorageSizeInTBs - name: Storage (TB) - type: integer - - jsonPath: .spec.details.dbWorkload - name: Workload Type - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabase is the Schema for the autonomousdatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase Important: Run "make" to regenerate code after modifying this file' - properties: - details: - description: AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase - properties: - adminPassword: - properties: - k8sSecret: - description: "*********************** *\tSecret specs ***********************" - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - autonomousContainerDatabase: - description: ACDSpec defines the spec of the target for backup/restore runs. The name could be the name of an AutonomousDatabase or an AutonomousDatabaseBackup - properties: - k8sACD: - description: "*********************** *\tACD specs ***********************" - properties: - name: - type: string - type: object - ociACD: - properties: - ocid: - type: string - type: object - type: object - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbName: - type: string - dbVersion: - type: string - dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying type: string' - enum: - - OLTP - - DW - - AJD - - APEX - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - isAutoScalingEnabled: - type: boolean - isDedicated: - type: boolean - licenseModel: - description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying type: string' - enum: - - LICENSE_INCLUDED - - BRING_YOUR_OWN_LICENSE - type: string - lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' - type: string - networkAccess: - properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: - properties: - hostnamePrefix: - type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: - type: string - type: object - type: object - wallet: - properties: - name: - type: string - password: - properties: - k8sSecret: - description: "*********************** *\tSecret specs ***********************" - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - type: object - type: object - hardLink: - default: false - type: boolean - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - required: - - details - type: object - status: - description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase - properties: - allConnectionStrings: - items: - properties: - connectionStrings: - items: - properties: - connectionString: - type: string - tnsName: - type: string - type: object - type: array - tlsAuthentication: - type: string - required: - - connectionStrings - type: object - type: array - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - timeCreated: - type: string - walletExpiringDate: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.6.1 - name: cdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: CDB is the Schema for the cdbs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: CDBSpec defines the desired state of CDB - properties: - cdbAdminPwd: - description: Password for the CDB Administrator to manage PDB lifecycle - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - description: User in the root container with sysdba priviledges to manage PDB lifecycle - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - description: Name of the CDB - type: string - cdbTlsCrt: - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - description: DB server port - type: integer - dbServer: - description: Name of the DB server - type: string - dbTnsurl: - type: string - nodeSelector: - additionalProperties: - type: string - description: Node Selector for running the Pod - type: object - ordsImage: - description: ORDS Image Name - type: string - ordsImagePullPolicy: - description: ORDS Image Pull Policy - enum: - - Always - - Never - type: string - ordsImagePullSecret: - description: The name of the image pull secret in case of a private docker repository. - type: string - ordsPort: - description: ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. - type: integer - ordsPwd: - description: Password for user ORDS_PUBLIC_USER - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - description: Number of ORDS Containers to create - type: integer - serviceName: - description: Name of the CDB Service - type: string - sysAdminPwd: - description: Password for the CDB System Administrator - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - description: Password for the Web Server User - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - description: CDBStatus defines the observed state of CDB - properties: - msg: - description: Message - type: string - phase: - description: Phase of the CDB Resource - type: string - status: - description: CDB Resource Status - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: databaseobservers.observability.oracle.com -spec: - group: observability.oracle.com - names: - kind: DatabaseObserver - listKind: DatabaseObserverList - plural: databaseobservers - singular: databaseobserver - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: DatabaseObserver is the Schema for the databaseobservers API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: DatabaseObserverSpec defines the desired state of DatabaseObserver - properties: - database: - description: DatabaseObserverDatabase defines the database details used for DatabaseObserver - properties: - dbConnectionString: - properties: - key: - type: string - secret: - type: string - type: object - dbPassword: - properties: - key: - type: string - secret: - type: string - vaultOCID: - type: string - vaultSecretName: - type: string - type: object - dbUser: - properties: - key: - type: string - secret: - type: string - type: object - dbWallet: - properties: - key: - type: string - secret: - type: string - type: object - type: object - exporter: - description: DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver - properties: - configuration: - properties: - configmap: - description: ConfigMapDetails defines the configmap name - properties: - configmapName: - type: string - key: - type: string - type: object - type: object - image: - type: string - service: - description: DatabaseObserverService defines the exporter service component of DatabaseObserver - properties: - port: - format: int32 - type: integer - type: object - type: object - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - prometheus: - description: PrometheusConfig defines the generated resources for Prometheus - properties: - labels: - additionalProperties: - type: string - type: object - port: - type: string - type: object - replicas: - format: int32 - type: integer - type: object - status: - description: DatabaseObserverStatus defines the observed state of DatabaseObserver - properties: - conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - exporterConfig: - type: string - replicas: - type: integer - status: - type: string - required: - - conditions - - exporterConfig - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: DataguardBroker is the Schema for the dataguardbrokers API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: DataguardBrokerSpec defines the desired state of DataguardBroker - properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - description: FSFO strategy - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - description: DataguardBrokerStatus defines the observed state of DataguardBroker - properties: - clusterConnectString: - type: string - externalConnectString: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: dbcssystems.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DbcsSystem - listKind: DbcsSystemList - plural: dbcssystems - singular: dbcssystem - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: DbcsSystem is the Schema for the dbcssystems API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: DbcsSystemSpec defines the desired state of DbcsSystem - properties: - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - description: DB Backup COnfig Network Struct - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - sshPublicKeys - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - ociConfigMap: - type: string - ociSecret: - type: string - required: - - ociConfigMap - type: object - status: - description: DbcsSystemStatus defines the observed state of DbcsSystem - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbEdition: - type: string - dbInfo: - items: - description: DbcsSystemStatus defines the observed state of DbcsSystem - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: oraclerestdataservices.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestDataService - listKind: OracleRestDataServiceList - plural: oraclerestdataservices - singular: oraclerestdataservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: OracleRestDataService is the Schema for the oraclerestdataservices API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService - properties: - adminPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - apexPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - oracleService: - type: string - ordsPassword: - description: OracleRestDataServicePassword defines the secret containing Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - ordsUser: - type: string - persistence: - description: OracleRestDataServicePersistence defines the storage releated params - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - size: - type: string - storageClass: - type: string - volumeName: - type: string - type: object - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - description: OracleRestDataServicePDBSchemas defines the PDB Schemas to be ORDS Enabled - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: - type: string - urlMapping: - type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - required: - - adminPassword - - databaseRef - - ordsPassword - type: object - status: - description: OracleRestDataServiceStatus defines the observed state of OracleRestDataService - properties: - apexConfigured: - type: boolean - apexUrl: - type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: - type: string - databaseApiUrl: - type: string - databaseRef: - type: string - image: - description: OracleRestDataServiceImage defines the Image source and pullSecrets for POD - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: string - ordsInstalled: - type: boolean - replicas: - type: integer - serviceIP: - type: string - status: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: ordssrvs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OrdsSrvs - listKind: OrdsSrvsList - plural: ordssrvs - singular: ordssrvs - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: status - type: string - - jsonPath: .status.workloadType - name: workloadType - type: string - - jsonPath: .status.ordsVersion - name: ordsVersion - type: string - - jsonPath: .status.httpPort - name: httpPort - type: integer - - jsonPath: .status.httpsPort - name: httpsPort - type: integer - - jsonPath: .status.mongoPort - name: MongoPort - type: integer - - jsonPath: .status.restartRequired - name: restartRequired - type: boolean - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.ordsInstalled - name: OrdsInstalled - type: boolean - name: v1alpha1 - schema: - openAPIV3Schema: - description: OrdsSrvs is the Schema for the ordssrvs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: OrdsSrvsSpec defines the desired state of OrdsSrvs - properties: - forceRestart: - description: Specifies whether to restart pods when Global or Pool configurations change - type: boolean - globalSettings: - description: Contains settings that are configured across the entire ORDS instance. - properties: - cache.metadata.enabled: - description: Specifies the setting to enable or disable metadata caching. - type: boolean - cache.metadata.graphql.expireAfterAccess: - description: Specifies the duration after a GraphQL schema is not accessed from the cache that it expires. - format: int64 - type: integer - cache.metadata.graphql.expireAfterWrite: - description: Specifies the duration after a GraphQL schema is cached that it expires and has to be loaded again. - format: int64 - type: integer - cache.metadata.jwks.enabled: - description: Specifies the setting to enable or disable JWKS caching. - type: boolean - cache.metadata.jwks.expireAfterAccess: - description: Specifies the duration after a JWK is not accessed from the cache that it expires. By default this is disabled. - format: int64 - type: integer - cache.metadata.jwks.expireAfterWrite: - description: Specifies the duration after a JWK is cached, that is, it expires and has to be loaded again. - format: int64 - type: integer - cache.metadata.jwks.initialCapacity: - description: Specifies the initial capacity of the JWKS cache. - format: int32 - type: integer - cache.metadata.jwks.maximumSize: - description: Specifies the maximum capacity of the JWKS cache. - format: int32 - type: integer - cache.metadata.timeout: - description: Specifies the setting to determine for how long a metadata record remains in the cache. Longer duration means, it takes longer to view the applied changes. The formats accepted are based on the ISO-8601 duration format. - format: int64 - type: integer - certSecret: - description: 'Specifies the Secret containing the SSL Certificates Replaces: standalone.https.cert and standalone.https.cert.key' - properties: - cert: - description: Specifies the Certificate - type: string - key: - description: Specifies the Certificate Key - type: string - secretName: - description: Specifies the name of the certificate Secret - type: string - required: - - cert - - key - - secretName - type: object - database.api.enabled: - description: Specifies whether the Database API is enabled. - type: boolean - database.api.management.services.disabled: - description: Specifies to disable the Database API administration related services. Only applicable when Database API is enabled. - type: boolean - db.invalidPoolTimeout: - description: Specifies how long to wait before retrying an invalid pool. - format: int64 - type: integer - debug.printDebugToScreen: - description: Specifies whether to display error messages on the browser. - type: boolean - enable.mongo.access.log: - default: false - description: Specifies if HTTP request access logs should be enabled If enabled, logs will be written to /opt/oracle/sa/log/global - type: boolean - enable.standalone.access.log: - default: false - description: Specifies if HTTP request access logs should be enabled If enabled, logs will be written to /opt/oracle/sa/log/global - type: boolean - error.responseFormat: - description: Specifies how the HTTP error responses must be formatted. html - Force all responses to be in HTML format json - Force all responses to be in JSON format auto - Automatically determines most appropriate format for the request (default). - type: string - feature.grahpql.max.nesting.depth: - description: Specifies the maximum join nesting depth limit for GraphQL queries. - format: int32 - type: integer - icap.port: - description: Specifies the Internet Content Adaptation Protocol (ICAP) port to virus scan files. Either icap.port or icap.secure.port are required to have a value. - format: int32 - type: integer - icap.secure.port: - description: Specifies the Internet Content Adaptation Protocol (ICAP) port to virus scan files. Either icap.port or icap.secure.port are required to have a value. If values for both icap.port and icap.secure.port are provided, then the value of icap.port is ignored. - format: int32 - type: integer - icap.server: - description: Specifies the Internet Content Adaptation Protocol (ICAP) server name or IP address to virus scan files. The icap.server is required to have a value. - type: string - log.procedure: - description: Specifies whether procedures are to be logged. - type: boolean - mongo.enabled: - description: Specifies to enable the API for MongoDB. - type: boolean - mongo.idle.timeout: - description: Specifies the maximum idle time for a Mongo connection in milliseconds. - format: int64 - type: integer - mongo.op.timeout: - description: Specifies the maximum time for a Mongo database operation in milliseconds. - format: int64 - type: integer - mongo.port: - default: 27017 - description: Specifies the API for MongoDB listen port. - format: int32 - type: integer - request.traceHeaderName: - description: Specifies the name of the HTTP request header that uniquely identifies the request end to end as it passes through the various layers of the application stack. In Oracle this header is commonly referred to as the ECID (Entity Context ID). - type: string - security.credentials.attempts: - description: Specifies the maximum number of unsuccessful password attempts allowed. Enabled by setting a positive integer value. - format: int32 - type: integer - security.credentials.lock.time: - description: Specifies the period to lock the account that has exceeded maximum attempts. - format: int64 - type: integer - security.disableDefaultExclusionList: - description: If this value is set to true, then the Oracle REST Data Services internal exclusion list is not enforced. Oracle recommends that you do not set this value to true. - type: boolean - security.exclusionList: - description: Specifies a pattern for procedures, packages, or schema names which are forbidden to be directly executed from a browser. - type: string - security.externalSessionTrustedOrigins: - description: Specifies to trust Access from originating domains - type: string - security.forceHTTPS: - description: Specifies to force HTTPS; this is set to default to false as in real-world TLS should terminiate at the LoadBalancer - type: boolean - security.httpsHeaderCheck: - description: 'Specifies that the HTTP Header contains the specified text Usually set to ''X-Forwarded-Proto: https'' coming from a load-balancer' - type: string - security.inclusionList: - description: Specifies a pattern for procedures, packages, or schema names which are allowed to be directly executed from a browser. - type: string - security.maxEntries: - description: Specifies the maximum number of cached procedure validations. Set this value to 0 to force the validation procedure to be invoked on each request. - format: int32 - type: integer - security.verifySSL: - description: Specifies whether HTTPS is available in your environment. - type: boolean - standalone.context.path: - default: /ords - description: Specifies the context path where ords is located. - type: string - standalone.http.port: - default: 8080 - description: Specifies the HTTP listen port. - format: int32 - type: integer - standalone.https.host: - description: Specifies the SSL certificate hostname. - type: string - standalone.https.port: - default: 8443 - description: Specifies the HTTPS listen port. - format: int32 - type: integer - standalone.stop.timeout: - description: Specifies the period for Standalone Mode to wait until it is gracefully shutdown. - format: int64 - type: integer - type: object - image: - description: Specifies the ORDS container image - type: string - imagePullPolicy: - default: IfNotPresent - description: Specifies the ORDS container image pull policy - enum: - - IfNotPresent - - Always - - Never - type: string - imagePullSecrets: - description: Specifies the Secret Name for pulling the ORDS container image - type: string - poolSettings: - description: Contains settings for individual pools/databases - items: - properties: - apex.security.administrator.roles: - description: Specifies the comma delimited list of additional roles to assign authenticated APEX administrator type users. - type: string - apex.security.user.roles: - description: Specifies the comma delimited list of additional roles to assign authenticated regular APEX users. - type: string - autoUpgradeAPEX: - default: false - description: Specify whether to perform APEX installation/upgrades automatically The db.adminUser and db.adminUser.secret must be set, otherwise setting is ignored This setting will be ignored for ADB - type: boolean - autoUpgradeORDS: - default: false - description: Specify whether to perform ORDS installation/upgrades automatically The db.adminUser and db.adminUser.secret must be set, otherwise setting is ignored This setting will be ignored for ADB - type: boolean - db.adminUser: - description: Specifies the username for the database account that ORDS uses for administration operations in the database. - type: string - db.adminUser.secret: - description: 'Specifies the Secret with the dbAdminUser (SYS) and dbAdminPassword values for the database account that ORDS uses for administration operations in the database. replaces: db.adminUser.password' - properties: - passwordKey: - default: password - description: Specifies the key holding the value of the Secret - type: string - secretName: - description: Specifies the name of the password Secret - type: string - required: - - secretName - type: object - db.cdb.adminUser: - description: Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management. - type: string - db.cdb.adminUser.secret: - description: 'Specifies the Secret with the dbCdbAdminUser (SYS) and dbCdbAdminPassword values Specifies the username for the database account that ORDS uses for the Pluggable Database Lifecycle Management. Replaces: db.cdb.adminUser.password' - properties: - passwordKey: - default: password - description: Specifies the key holding the value of the Secret - type: string - secretName: - description: Specifies the name of the password Secret - type: string - required: - - secretName - type: object - db.connectionType: - description: The type of connection. - enum: - - basic - - tns - - customurl - type: string - db.credentialsSource: - description: Specifies the source for database credentials when creating a direct connection for running SQL statements. Value can be one of pool or request. If the value is pool, then the credentials defined in this pool is used to create a JDBC connection. If the value request is used, then the credentials in the request is used to create a JDBC connection and if successful, grants the requestor SQL Developer role. - enum: - - pool - - request - type: string - db.customURL: - description: Specifies the JDBC URL connection to connect to the database. - type: string - db.hostname: - description: Specifies the host system for the Oracle database. - type: string - db.poolDestroyTimeout: - description: Indicates how long to wait to gracefully destroy a pool before moving to forcefully destroy all connections including borrowed ones. - format: int64 - type: integer - db.port: - description: Specifies the database listener port. - format: int32 - type: integer - db.secret: - description: Specifies the Secret with the dbUsername and dbPassword values for the connection. - properties: - passwordKey: - default: password - description: Specifies the key holding the value of the Secret - type: string - secretName: - description: Specifies the name of the password Secret - type: string - required: - - secretName - type: object - db.servicename: - description: Specifies the network service name of the database. - type: string - db.sid: - description: Specifies the name of the database. - type: string - db.tnsAliasName: - description: Specifies the TNS alias name that matches the name in the tnsnames.ora file. - type: string - db.username: - default: ORDS_PUBLIC_USER - description: Specifies the name of the database user for the connection. For non-ADB this will default to ORDS_PUBLIC_USER For ADBs this must be specified and not ORDS_PUBLIC_USER If ORDS_PUBLIC_USER is specified for an ADB, the workload will fail - type: string - db.wallet.zip.service: - description: Specifies the service name in the wallet archive for the pool. - type: string - dbWalletSecret: - description: 'Specifies the Secret containing the wallet archive containing connection details for the pool. Replaces: db.wallet.zip' - properties: - secretName: - description: Specifies the name of the Database Wallet Secret - type: string - walletName: - description: Specifies the Secret key name containing the Wallet - type: string - required: - - secretName - - walletName - type: object - debug.trackResources: - description: Specifies to enable tracking of JDBC resources. If not released causes in resource leaks or exhaustion in the database. Tracking imposes a performance overhead. - type: boolean - feature.openservicebroker.exclude: - description: Specifies to disable the Open Service Broker services available for the pool. - type: boolean - feature.sdw: - description: Specifies to enable the Database Actions feature. - type: boolean - http.cookie.filter: - description: Specifies a comma separated list of HTTP Cookies to exclude when initializing an Oracle Web Agent environment. - type: string - jdbc.DriverType: - description: Specifies the JDBC driver type. - enum: - - thin - - oci8 - type: string - jdbc.InactivityTimeout: - description: Specifies how long an available connection can remain idle before it is closed. The inactivity connection timeout is in seconds. - format: int32 - type: integer - jdbc.InitialLimit: - description: Specifies the initial size for the number of connections that will be created. The default is low, and should probably be set higher in most production environments. - format: int32 - type: integer - jdbc.MaxConnectionReuseCount: - description: Specifies the maximum number of times to reuse a connection before it is discarded and replaced with a new connection. - format: int32 - type: integer - jdbc.MaxConnectionReuseTime: - description: Sets the maximum connection reuse time property. - format: int32 - type: integer - jdbc.MaxLimit: - description: Specifies the maximum number of connections. Might be too low for some production environments. - format: int32 - type: integer - jdbc.MaxStatementsLimit: - description: Specifies the maximum number of statements to cache for each connection. - format: int32 - type: integer - jdbc.MinLimit: - description: Specifies the minimum number of connections. - format: int32 - type: integer - jdbc.SecondsToTrustIdleConnection: - description: Sets the time in seconds to trust an idle connection to skip a validation test. - format: int32 - type: integer - jdbc.auth.admin.role: - description: Identifies the database role that indicates that the database user must get the SQL Administrator role. - type: string - jdbc.auth.enabled: - description: Specifies if the PL/SQL Gateway calls can be authenticated using database users. If the value is true then this feature is enabled. If the value is false, then this feature is disabled. Oracle recommends not to use this feature. This feature used only to facilitate customers migrating from mod_plsql. - type: boolean - jdbc.cleanup.mode: - description: Specifies how a pooled JDBC connection and corresponding database session, is released when a request has been processed. - type: string - jdbc.statementTimeout: - description: Specifies a timeout period on a statement. An abnormally long running query or script, executed by a request, may leave it in a hanging state unless a timeout is set on the statement. Setting a timeout on the statement ensures that all the queries automatically timeout if they are not completed within the specified time period. - format: int32 - type: integer - misc.defaultPage: - description: Specifies the default page to display. The Oracle REST Data Services Landing Page. - type: string - misc.pagination.maxRows: - description: Specifies the maximum number of rows that will be returned from a query when processing a RESTful service and that will be returned from a nested cursor in a result set. Affects all RESTful services generated through a SQL query, regardless of whether the resource is paginated. - format: int32 - type: integer - owa.trace.sql: - description: If it is true, then it causes a trace of the SQL statements performed by Oracle Web Agent to be echoed to the log. - type: boolean - plsql.gateway.mode: - description: Indicates if the PL/SQL Gateway functionality should be available for a pool or not. Value can be one of disabled, direct, or proxied. If the value is direct, then the pool serves the PL/SQL Gateway requests directly. If the value is proxied, the PLSQL_GATEWAY_CONFIG view is used to determine the user to whom to proxy. - enum: - - disabled - - direct - - proxied - type: string - poolName: - description: Specifies the Pool Name - type: string - procedure.preProcess: - description: Specifies the procedure name(s) to execute prior to executing the procedure specified on the URL. Multiple procedure names must be separated by commas. - type: string - procedure.rest.preHook: - description: Specifies the function to be invoked prior to dispatching each Oracle REST Data Services based REST Service. The function can perform configuration of the database session, perform additional validation or authorization of the request. If the function returns true, then processing of the request continues. If the function returns false, then processing of the request is aborted and an HTTP 403 Forbidden status is returned. - type: string - procedurePostProcess: - description: Specifies the procedure name(s) to execute after executing the procedure specified on the URL. Multiple procedure names must be separated by commas. - type: string - restEnabledSql.active: - description: Specifies whether the REST-Enabled SQL service is active. - type: boolean - security.jwks.connection.timeout: - description: Specifies the maximum amount of time before timing-out when accessing a JWK url. - format: int64 - type: integer - security.jwks.read.timeout: - description: Specifies the maximum amount of time reading a response from the JWK url before timing-out. - format: int64 - type: integer - security.jwks.refresh.interval: - description: Specifies the minimum interval between refreshing the JWK cached value. - format: int64 - type: integer - security.jwks.size: - description: Specifies the maximum number of bytes read from the JWK url. - format: int32 - type: integer - security.jwt.allowed.age: - description: Specifies the maximum allowed age of a JWT in seconds, regardless of expired claim. The age of the JWT is taken from the JWT issued at claim. - format: int64 - type: integer - security.jwt.allowed.skew: - description: Specifies the maximum skew the JWT time claims are accepted. This is useful if the clock on the JWT issuer and ORDS differs by a few seconds. - format: int64 - type: integer - security.jwt.profile.enabled: - description: 'Specifies whether the JWT Profile authentication is available. Supported values:' - type: boolean - security.requestAuthenticationFunction: - description: Specifies an authentication function to determine if the requested procedure in the URL should be allowed or disallowed for processing. The function should return true if the procedure is allowed; otherwise, it should return false. If it returns false, Oracle REST Data Services will return WWW-Authenticate in the response header. - type: string - security.requestValidationFunction: - default: ords_util.authorize_plsql_gateway - description: Specifies a validation function to determine if the requested procedure in the URL should be allowed or disallowed for processing. The function should return true if the procedure is allowed; otherwise, return false. - type: string - security.validationFunctionType: - description: 'Indicates the type of security.requestValidationFunction: javascript or plsql.' - enum: - - plsql - - javascript - type: string - soda.defaultLimit: - description: When using the SODA REST API, specifies the default number of documents returned for a GET request on a collection when a limit is not specified in the URL. Must be a positive integer, or "unlimited" for no limit. - type: string - soda.maxLimit: - description: When using the SODA REST API, specifies the maximum number of documents that will be returned for a GET request on a collection URL, regardless of any limit specified in the URL. Must be a positive integer, or "unlimited" for no limit. - type: string - tnsAdminSecret: - description: 'Specifies the Secret containing the TNS_ADMIN directory Replaces: db.tnsDirectory' - properties: - secretName: - description: Specifies the name of the TNS_ADMIN Secret - type: string - required: - - secretName - type: object - required: - - db.secret - - poolName - type: object - type: array - replicas: - default: 1 - description: Defines the number of desired Replicas when workloadType is Deployment or StatefulSet - format: int32 - minimum: 1 - type: integer - workloadType: - default: Deployment - description: Specifies the desired Kubernetes Workload - enum: - - Deployment - - StatefulSet - - DaemonSet - type: string - required: - - globalSettings - - image - type: object - status: - description: OrdsSrvsStatus defines the observed state of OrdsSrvs - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - httpPort: - description: Indicates the HTTP port of the resource exposed by the pods - format: int32 - type: integer - httpsPort: - description: Indicates the HTTPS port of the resource exposed by the pods - format: int32 - type: integer - mongoPort: - description: Indicates the MongoAPI port of the resource exposed by the pods (if enabled) - format: int32 - type: integer - ordsInstalled: - description: '** PLACE HOLDER' - type: boolean - ordsVersion: - description: Indicates the ORDS version - type: string - restartRequired: - description: Indicates if the resource is out-of-sync with the configuration - type: boolean - status: - description: '** PLACE HOLDER Indicates the current status of the resource' - type: string - workloadType: - description: Indicates the current Workload type of the resource - type: string - required: - - restartRequired - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.6.1 - name: pdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: PDB is the Schema for the pdbs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: PDBSpec defines the desired state of PDB - properties: - action: - description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR.' - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - description: The administrator username for the new PDB. This property is required when the Action property is Create. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - description: The administrator password for the new PDB. This property is required when the Action property is Create. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - description: Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. - type: boolean - assertivePdbDeletion: - description: turn on the assertive approach to delete pdb resource kubectl delete pdb ..... automatically triggers the pluggable database deletion - type: boolean - cdbName: - description: Name of the CDB - type: string - cdbNamespace: - description: CDB Namespace - type: string - cdbResName: - description: Name of the CDB Custom Resource that runs the ORDS container - type: string - copyAction: - description: To copy files or not while cloning a PDB - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - description: Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - description: Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. - type: string - getScript: - description: Whether you need the script only or execute the script - type: boolean - modifyOption: - description: Extra options for opening and closing a PDB - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - description: The name of the new PDB. Relevant for both Create and Plug Actions. - type: string - pdbState: - description: The target state of the PDB - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - description: Whether to reuse temp file - type: boolean - sourceFileNameConversions: - description: This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. - type: string - sparseClonePath: - description: A Path specified for sparse clone snapshot copy. (Optional) - type: string - srcPdbName: - description: Name of the Source PDB from which to clone - type: string - tdeExport: - description: TDE export for unplug operations - type: boolean - tdeImport: - description: TDE import for plug operations - type: boolean - tdeKeystorePath: - description: TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - type: string - tdePassword: - description: TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - description: TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - description: Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - type: string - totalSize: - description: Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - type: string - unlimitedStorage: - description: Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. - type: boolean - webServerPwd: - description: Password for the Web ServerPDB User - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - description: Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - description: XML metadata filename to be used for Plug or Unplug operations - type: string - required: - - action - type: object - status: - description: PDBStatus defines the observed state of PDB - properties: - action: - description: Last Completed Action - type: string - connString: - description: PDB Connect String - type: string - modifyOption: - description: Modify Option of the PDB - type: string - msg: - description: Message - type: string - openMode: - description: Open mode of the PDB - type: string - phase: - description: Phase of the PDB Resource - type: string - status: - description: PDB Resource Status - type: boolean - totalSize: - description: Total size of the PDB - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: shardingdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: ShardingDatabase - listKind: ShardingDatabaseList - plural: shardingdatabases - singular: shardingdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: ShardingDatabase is the Schema for the shardingdatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ShardingDatabaseSpec defines the desired state of ShardingDatabase - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - description: CatalogSpec defines the desired state of CatalogSpec - properties: - envVars: - items: - description: EnvironmentVariable represents a named variable accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image - type: string - isDelete: - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - description: ResourceRequirements describes the compute resource requirements. - properties: - claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbEdition: - type: string - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - description: Secret Details - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - fssStorageClass: - type: string - gsm: - items: - description: GsmSpec defines the desired state of GsmSpec - properties: - directorName: - type: string - envVars: - description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. - items: - description: EnvironmentVariable represents a named variable accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image - type: string - isDelete: - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - description: ResourceRequirements describes the compute resource requirements. - properties: - claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - description: Service Definition - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - description: ShardSpace Specs - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: string - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - description: PortMapping is a specification of port mapping for an application deployment. - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' - items: - description: ShardSpec is a specification of Shards for an application deployment. - properties: - deployAs: - type: string - envVars: - items: - description: EnvironmentVariable represents a named variable accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull a container image - type: string - isDelete: - enum: - - enable - - disable - - failed - - force - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - description: ResourceRequirements describes the compute resource requirements. - properties: - claims: - description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - tdeWalletPvc: - type: string - tdeWalletPvcMountLocation: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 ShardingDatabaseStatus defines the observed state of ShardingDatabase - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.6.1 - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.edition - name: Edition - type: string - - jsonPath: .status.sid - name: Sid - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version - type: string - - jsonPath: .status.connectString - name: Connect Str - type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.tcpsConnectString - name: TCPS Connect Str - type: string - - jsonPath: .status.tcpsPdbConnectString - name: TCPS Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.oemExpressUrl - name: Oem Express Url - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase - properties: - adminPassword: - description: SingleInsatnceAdminPassword defines the secret containing Admin Password mapped to secretKey for Database - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - archiveLog: - type: boolean - charset: - type: string - createAs: - enum: - - primary - - standby - - clone - type: string - dgBrokerConfigured: - type: boolean - edition: - enum: - - standard - - enterprise - - express - - free - type: string - enableTCPS: - type: boolean - flashBack: - type: boolean - forceLog: - type: boolean - image: - description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD - properties: - prebuiltDB: - type: boolean - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - listenerPort: - type: integer - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - pdbName: - type: string - persistence: - description: SingleInstanceDatabasePersistence defines the storage size and class for PVC - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - primaryDatabaseRef: - type: string - readinessCheckPeriod: - type: integer - replicas: - type: integer - resources: - properties: - limits: - properties: - cpu: - type: string - memory: - type: string - type: object - requests: - properties: - cpu: - type: string - memory: - type: string - type: object - type: object - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - sid: - description: SID must be alphanumeric (no special characters, only a-z, A-Z, 0-9), and no longer than 12 characters. - maxLength: 12 - pattern: ^[a-zA-Z0-9]+$ - type: string - tcpsCertRenewInterval: - type: string - tcpsListenerPort: - type: integer - tcpsTlsSecret: - type: string - required: - - image - type: object - status: - description: SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase - properties: - apexInstalled: - type: boolean - archiveLog: - type: string - certCreationTimestamp: - type: string - certRenewInterval: - type: string - charset: - type: string - clientWalletLoc: - type: string - clusterConnectString: - type: string - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - connectString: - type: string - createdAs: - type: string - datafilesCreated: - default: "false" - type: string - datafilesPatched: - default: "false" - type: string - dgBrokerConfigured: - type: boolean - edition: - type: string - flashBack: - type: string - forceLog: - type: string - initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - initPgaSize: - type: integer - initSgaSize: - type: integer - isTcpsEnabled: - default: false - type: boolean - nodes: - items: - type: string - type: array - oemExpressUrl: - type: string - ordsReference: - type: string - pdbConnectString: - type: string - pdbName: - type: string - persistence: - description: SingleInstanceDatabasePersistence defines the storage size and class for PVC - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - prebuiltDB: - type: boolean - primaryDatabase: - type: string - releaseUpdate: - type: string - replicas: - type: integer - role: - type: string - sid: - type: string - standbyDatabases: - additionalProperties: - type: string - type: object - status: - type: string - tcpsConnectString: - type: string - tcpsPdbConnectString: - type: string - tcpsTlsSecret: - default: "" - type: string - required: - - isTcpsEnabled - - persistence - - tcpsTlsSecret - type: object - type: object - served: true - storage: true - subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.replicas - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: oracle-database-operator-leader-election-role - namespace: oracle-database-operator-system -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: oracle-database-operator-manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - - events - - pods - - pods/exec - - pods/log - - replicasets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - deployments - - events - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch -- apiGroups: - - '''''' - resources: - - statefulsets/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - configmaps - verbs: - - get - - list -- apiGroups: - - apps - resources: - - daemonsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - deployments - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - deployments - - pods - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - replicasets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - list - - update -- apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - containers - - events - - namespaces - - pods - - pods/exec - - pods/log - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps - - namespaces - - pods - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - patch - - update -- apiGroups: - - "" - resources: - - daemonsets/status - verbs: - - get - - patch - - update -- apiGroups: - - "" - resources: - - deployments/status - verbs: - - get - - patch - - update -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - pods/exec - verbs: - - create -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - secrets/status - verbs: - - get -- apiGroups: - - "" - resources: - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - services/status - verbs: - - get - - patch - - update -- apiGroups: - - "" - resources: - - statefulsets/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomouscontainerdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabasebackups/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabaserestores/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - autonomousdatabases/status - verbs: - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - cdbs/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - cdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - dataguardbrokers/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - dbcssystems/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - oraclerestdataservices/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - ordssrvs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - ordssrvs/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - ordssrvs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - pdbs/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - pdbs/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/finalizers - verbs: - - create - - delete - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - shardingdatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/finalizers - verbs: - - update -- apiGroups: - - database.oracle.com - resources: - - singleinstancedatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/finalizers - verbs: - - update -- apiGroups: - - observability.oracle.com - resources: - - databaseobservers/status - verbs: - - get - - patch - - update -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-oracle-database-operator-proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-leader-election-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: oracle-database-operator-leader-election-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-oracle-database-operator-proxy-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager-metrics-service - namespace: oracle-database-operator-system -spec: - ports: - - name: https - port: 8443 - targetPort: https - selector: - control-plane: controller-manager ---- -apiVersion: v1 -kind: Service -metadata: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - control-plane: controller-manager ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: oracle-database-operator-serving-cert - namespace: oracle-database-operator-system -spec: - dnsNames: - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc - - oracle-database-operator-webhook-service.oracle-database-operator-system.svc.cluster.local - issuerRef: - kind: Issuer - name: oracle-database-operator-selfsigned-issuer - secretName: webhook-server-cert ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: oracle-database-operator-selfsigned-issuer - namespace: oracle-database-operator-system -spec: - selfSigned: {} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-mutating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: mautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-cdb - failurePolicy: Fail - name: mcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: mdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: moraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-pdb - failurePolicy: Fail - name: mpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: mshardingdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - shardingdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: msingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-observability-oracle-com-v1alpha1-databaseobserver - failurePolicy: Fail - name: mdatabaseobserver.kb.io - rules: - - apiGroups: - - observability.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - databaseobservers - sideEffects: None ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - name: oracle-database-operator-validating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase - failurePolicy: Fail - name: vautonomouscontainerdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomouscontainerdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabase - failurePolicy: Fail - name: vautonomousdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: vautonomousdatabasebackup.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore - failurePolicy: Fail - name: vautonomousdatabaserestore.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabaserestores - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-cdb - failurePolicy: Fail - name: vcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-dataguardbroker - failurePolicy: Fail - name: vdataguardbroker.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - dataguardbrokers - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-oraclerestdataservice - failurePolicy: Fail - name: voraclerestdataservice.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - oraclerestdataservices - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-pdb - failurePolicy: Fail - name: vpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: vshardingdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - shardingdatabases - sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-singleinstancedatabase - failurePolicy: Fail - name: vsingleinstancedatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - singleinstancedatabases - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-observability-oracle-com-v1alpha1-databaseobserver - failurePolicy: Fail - name: vdatabaseobserver.kb.io - rules: - - apiGroups: - - observability.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - databaseobservers - sideEffects: None ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - control-plane: controller-manager - name: oracle-database-operator-controller-manager - namespace: oracle-database-operator-system -spec: - replicas: 3 - selector: - matchLabels: - control-plane: controller-manager - template: - metadata: - labels: - control-plane: controller-manager - spec: - containers: - - args: - - --enable-leader-election - command: - - /manager - env: - - name: WATCH_NAMESPACE - value: "oracle-database-operator-system,ordsnamespace" - image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/ordslfcmng:latest - imagePullPolicy: Always - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - resources: - limits: - cpu: 400m - memory: 400Mi - requests: - cpu: 400m - memory: 400Mi - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - terminationGracePeriodSeconds: 10 - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert ---- diff --git a/docs/ordsservices/usecase01/ords-ns-role-binding.yaml b/docs/ordsservices/usecase01/ords-ns-role-binding.yaml deleted file mode 100644 index 2ad9a497..00000000 --- a/docs/ordsservices/usecase01/ords-ns-role-binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: ordsnamespace-rolebinding - namespace: ordsnamespace -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/ordsservices/usecase01/persistent-volume-rbac.yaml b/docs/ordsservices/usecase01/persistent-volume-rbac.yaml deleted file mode 100644 index e329ca3e..00000000 --- a/docs/ordsservices/usecase01/persistent-volume-rbac.yaml +++ /dev/null @@ -1,29 +0,0 @@ - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-manager-role-persistent-volume -rules: -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-manager-role-persistent-volume-cluster-role-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role-persistent-volume -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- - diff --git a/docs/ordsservices/usecase01/rest_server_creation.yaml b/docs/ordsservices/usecase01/rest_server_creation.yaml deleted file mode 100644 index e5492315..00000000 --- a/docs/ordsservices/usecase01/rest_server_creation.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: OrdsSrvs -metadata: - name: ords-sidb - namespace: ordsnamespace -spec: - image: container-registry.oracle.com/database/ords:24.1.0 - forceRestart: true - globalSettings: - database.api.enabled: true - poolSettings: - - poolName: default - autoUpgradeORDS: true - autoUpgradeAPEX: true - restEnabledSql.active: true - plsql.gateway.mode: direct - db.connectionType: customurl - db.customURL: jdbc:oracle:thin:@//204.216.216.178:30001/FREEPDB1 - db.username: ORDS_PUBLIC_USER - db.secret: - secretName: ords-db-auth - db.adminUser: SYS - db.adminUser.secret: - secretName: ords-db-auth - diff --git a/docs/ordsservices/usecase01/rest_server_creation_mongo.yaml b/docs/ordsservices/usecase01/rest_server_creation_mongo.yaml deleted file mode 100644 index 4b620f9d..00000000 --- a/docs/ordsservices/usecase01/rest_server_creation_mongo.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: OrdsSrvs -metadata: - name: ords-sidb-mongo - namespace: ordsnamespace -spec: - image: container-registry.oracle.com/database/ords:24.1.1 - forceRestart: true - globalSettings: - database.api.enabled: true - mongo.enabled: true - poolSettings: - - poolName: default - autoUpgradeORDS: true - restEnabledSql.active: true - plsql.gateway.mode: direct - jdbc.MaxConnectionReuseCount: 5000 - jdbc.MaxConnectionReuseTime: 900 - jdbc.SecondsToTrustIdleConnection: 1 - jdbc.InitialLimit: 100 - jdbc.MaxLimit: 100 - db.connectionType: customurl - db.customURL: jdbc:oracle:thin:@//158.180.233.248:30001/FREEPDB1 - db.username: ORDS_PUBLIC_USER - db.secret: - secretName: ords-db-auth - db.adminUser: SYS - db.adminUser.secret: - secretName: ords-db-auth diff --git a/docs/ordsservices/usecase01/sidb_create.yaml b/docs/ordsservices/usecase01/sidb_create.yaml deleted file mode 100644 index 2d5d8b28..00000000 --- a/docs/ordsservices/usecase01/sidb_create.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: SingleInstanceDatabase -metadata: - name: oraoper-sidb - namespace: oracle-database-operator-system -spec: - replicas: 1 - image: - pullFrom: container-registry.oracle.com/database/free:23.4.0.0 - pullSecrets: oracle-container-registry-secret - prebuiltDB: true - sid: FREE - listenerPort: 30001 - edition: free - adminPassword: - secretName: sidb-db-auth - secretKey: password - pdbName: FREEPDB1 diff --git a/docs/ordsservices/usecase01/sqlcreate.sql b/docs/ordsservices/usecase01/sqlcreate.sql deleted file mode 100644 index 906af0ad..00000000 --- a/docs/ordsservices/usecase01/sqlcreate.sql +++ /dev/null @@ -1,9 +0,0 @@ -drop user MONGO cascade -create user MONGO identified by "My_Password1!"; -grant soda_app, create session, create table, create view, create sequence, create procedure, create job, -unlimited tablespace to MONGO; -conn MONGO/My_Password1!@FREEPDB1 -exec ords.enable_schema; - - - diff --git a/docs/ordsservices/usecase01/storage-class-rbac.yaml b/docs/ordsservices/usecase01/storage-class-rbac.yaml deleted file mode 100644 index a34f67d4..00000000 --- a/docs/ordsservices/usecase01/storage-class-rbac.yaml +++ /dev/null @@ -1,28 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: oracle-database-operator-manager-role-storage-class -rules: -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: oracle-database-operator-manager-role-storage-class-cluster-role-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role-storage-class -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system ---- diff --git a/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora b/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora index 16a85226..1b1b8943 100644 --- a/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora +++ b/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora @@ -1,3 +1,3 @@ -pdb1=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb1))) +pdb1=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb1))) pdb2=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb2))) diff --git a/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora.offline b/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora.offline new file mode 100644 index 00000000..b58a8a66 --- /dev/null +++ b/docs/ordsservices/usecase01/tnsadmin/tnsnames.ora.offline @@ -0,0 +1 @@ +pdb1=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) diff --git a/docs/ordsservices/usecase01/tnsnames.ora b/docs/ordsservices/usecase01/tnsnames.ora deleted file mode 100644 index 16a85226..00000000 --- a/docs/ordsservices/usecase01/tnsnames.ora +++ /dev/null @@ -1,3 +0,0 @@ -pdb1=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb1))) - -pdb2=(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb2))) diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 62a2dc29..2e6c31ff 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10100,6 +10100,16 @@ spec: type: object spec: properties: + encPrivKey: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object forceRestart: type: boolean globalSettings: From f53e021fcea861bc64968f1830755c2b9248e08b Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Mon, 3 Feb 2025 09:33:27 +0000 Subject: [PATCH 170/414] Idesai resource management bug --- .../singleinstancedatabase_controller.go | 46 ++++++++----------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 7e248248..c34105a0 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -1167,35 +1167,27 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }(), Resources: func() corev1.ResourceRequirements { - if m.Spec.Resources.Requests != nil && m.Spec.Resources.Limits != nil { - return corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - "cpu": resource.MustParse(m.Spec.Resources.Requests.Cpu), - "memory": resource.MustParse(m.Spec.Resources.Requests.Memory), - }, - Limits: corev1.ResourceList{ - "cpu": resource.MustParse(m.Spec.Resources.Limits.Cpu), - "memory": resource.MustParse(m.Spec.Resources.Requests.Memory), - }, - } - } else if m.Spec.Resources.Requests != nil { - return corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - "cpu": resource.MustParse(m.Spec.Resources.Requests.Cpu), - "memory": resource.MustParse(m.Spec.Resources.Requests.Memory), - }, - } - } else if m.Spec.Resources.Limits != nil { - return corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - "cpu": resource.MustParse(m.Spec.Resources.Limits.Cpu), - "memory": resource.MustParse(m.Spec.Resources.Requests.Memory), - }, - } - } else { - return corev1.ResourceRequirements{} + var resourceReqRequests corev1.ResourceList = corev1.ResourceList{} + var resourceReqLimits corev1.ResourceList = corev1.ResourceList{} + + if m.Spec.Resources.Requests != nil && m.Spec.Resources.Requests.Cpu != "" { + resourceReqRequests["cpu"] = resource.MustParse(m.Spec.Resources.Requests.Cpu) + } + if m.Spec.Resources.Requests != nil && m.Spec.Resources.Requests.Memory != "" { + resourceReqRequests["memory"] = resource.MustParse(m.Spec.Resources.Requests.Memory) } + if m.Spec.Resources.Limits != nil && m.Spec.Resources.Limits.Cpu != "" { + resourceReqLimits["cpu"] = resource.MustParse(m.Spec.Resources.Limits.Cpu) + } + if m.Spec.Resources.Limits != nil && m.Spec.Resources.Limits.Memory != "" { + resourceReqLimits["memory"] = resource.MustParse(m.Spec.Resources.Limits.Memory) + } + + return corev1.ResourceRequirements{ + Requests: resourceReqRequests, + Limits: resourceReqLimits, + } }(), }}, From aaa326b992d10b5bf9947af70658283fb192ad3e Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Mon, 3 Feb 2025 10:09:30 +0000 Subject: [PATCH 171/414] update lrest readme --- docs/multitenant/lrest-based/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/multitenant/lrest-based/README.md b/docs/multitenant/lrest-based/README.md index 186f8c3e..8d093db7 100644 --- a/docs/multitenant/lrest-based/README.md +++ b/docs/multitenant/lrest-based/README.md @@ -31,8 +31,8 @@ -**Lrpdb** and **lrest** are two controllers for the pdb life cycle management (**PDBLCM**), they relye on a dedicated rest server (Lite Rest Server). -Image to run lrest controller is available on OCR. The container database can be anywhere (on prem and in cloud) +**Lrpdb** and **lrest** are two controllers for the PDBs life cycle management (**PDBLCM**), they relie on a dedicated rest server (Lite Rest Server). +Container image to run lrest controller is available on OCR. The container database can be anywhere (on prem and in cloud). ![generaleschema](./images/Generalschema2.jpg) @@ -185,7 +185,7 @@ lrestImage: container-registry.oracle.com/database/operator:lrest-241210-arm64 kubectl apply -f create_lrest_pod.yaml ``` -monitor the execution +monitor the execution: ```bash kubectl get pods -n cdbnamespace --watch @@ -200,7 +200,7 @@ NAME CDB NAME DB SERVER DB PORT TNS STRING REPLICAS STATUS MESSAGE cdb-dev DB12 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) 1 Ready ``` -Check pod logs +Check pod logs: ```bash /usr/local/go/bin/kubectl logs -f `/usr/local/go/bin/kubectl get pods -n cdbnamespace|grep lrest|cut -d ' ' -f 1` -n cdbnamespace @@ -271,7 +271,7 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 |cdbName | Name of the container db | |lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** | |dbTnsurl | Tns alias of the container db | -|deletePdbCascade | Delete all the pdbs associated to cdb resource when cdb resource it's dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | +|deletePdbCascade | Delete all the pdbs associated to cdb resource when cdb resource it is dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | |cdbAdminUser | Secret: the admin user | |cdbAdminPwd | Secret: the admin user password | |webServerUser | Secret: the https user | @@ -333,12 +333,12 @@ Note that after creation the pdb is not open you need to explicitly open it usin 🔥 **assertiveLrpdbDeletion** drops pluggable database using **INCLUDE DATAFILES** option -All these parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** need to be specified in all pdb life cycle management yaml files; for the sake of simplicity they won't be exposed in the subsequent tables. +All these parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** need to be specified in all pdb life cycle management yaml files; for the sake of simplicity, they won't be exposed in the subsequent tables. #### pdb config map -Using **pdbconfigmap** it's possible to specify a kubernetes configmap for the pdb init parameters. The config map payload has the following scaffold: +Using **pdbconfigmap** it's possible to specify a kubernetes configmap with init pdb parameters. The config map payload has the following scaffold: ``` @@ -377,8 +377,8 @@ db_file_multiblock_read_count;16;spfile test_invalid_parameter;16;spfile ``` -- Configmap, if specified, is applied during pdb **cloning** **opening** and **plugging** -- Configmap is not monitored by the reconciliation loop; this feature will be available in feature releases. This means that is if someone decides to manually alter init parameter the operator does not take any actions to syncronize pdb configuration with config map. +- Configmap, if specified, is applied during pdb **cloning**, **opening** and **plugging** +- Configmap is not monitored by the reconciliation loop; this feature will be available in feature releases. This means that if someone decides to manually alter an init parameter the operator does not take any actions to syncronize pdb configuration with config map. - **Alter system parameter feature** will be available in future releases. - A ConfigMap application error (whatever reason) does not stop the execution of the process. A warning with sqlcode is reported in the logfile. From 8036d5bb2a849ba8dcbe0da07c032d006d2b2a05 Mon Sep 17 00:00:00 2001 From: ting-lan-wang Date: Tue, 4 Feb 2025 16:40:49 -0500 Subject: [PATCH 172/414] Remove backups when the database is terminated --- .../v1alpha1/autonomousdatabase_types.go | 2 +- apis/database/v4/autonomousdatabase_types.go | 2 +- commons/k8s/create.go | 3 + commons/k8s/fetch.go | 14 ++- ...onomous_database_clone_request_response.go | 104 ------------------ commons/oci/database.go | 63 +---------- .../database/autonomousdatabase_controller.go | 42 ++++++- 7 files changed, 57 insertions(+), 173 deletions(-) delete mode 100644 commons/oci/create_autonomous_database_clone_request_response.go diff --git a/apis/database/v1alpha1/autonomousdatabase_types.go b/apis/database/v1alpha1/autonomousdatabase_types.go index 960e27eb..099703c2 100644 --- a/apis/database/v1alpha1/autonomousdatabase_types.go +++ b/apis/database/v1alpha1/autonomousdatabase_types.go @@ -51,7 +51,7 @@ import ( type AutonomousDatabaseSpec struct { // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone Action string `json:"action"` - Details AutonomousDatabaseDetails `json:"details"` + Details AutonomousDatabaseDetails `json:"details,omitempty"` Clone AutonomousDatabaseClone `json:"clone,omitempty"` Wallet WalletSpec `json:"wallet,omitempty"` OciConfig OciConfigSpec `json:"ociConfig,omitempty"` diff --git a/apis/database/v4/autonomousdatabase_types.go b/apis/database/v4/autonomousdatabase_types.go index 745f0320..628dd882 100644 --- a/apis/database/v4/autonomousdatabase_types.go +++ b/apis/database/v4/autonomousdatabase_types.go @@ -54,7 +54,7 @@ import ( type AutonomousDatabaseSpec struct { // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone Action string `json:"action"` - Details AutonomousDatabaseDetails `json:"details"` + Details AutonomousDatabaseDetails `json:"details,omitempty"` Clone AutonomousDatabaseClone `json:"clone,omitempty"` Wallet WalletSpec `json:"wallet,omitempty"` OciConfig OciConfigSpec `json:"ociConfig,omitempty"` diff --git a/commons/k8s/create.go b/commons/k8s/create.go index e56872f4..cd836af7 100644 --- a/commons/k8s/create.go +++ b/commons/k8s/create.go @@ -85,6 +85,9 @@ func CreateAutonomousBackup(kubeClient client.Client, Namespace: ownerAdb.GetNamespace(), Name: backupName, OwnerReferences: NewOwnerReference(ownerAdb), + Labels: map[string]string{ + "adb": ownerAdb.Name, + }, }, Spec: dbv4.AutonomousDatabaseBackupSpec{ Target: dbv4.TargetSpec{ diff --git a/commons/k8s/fetch.go b/commons/k8s/fetch.go index d8fb3c3e..617abdb5 100644 --- a/commons/k8s/fetch.go +++ b/commons/k8s/fetch.go @@ -44,6 +44,7 @@ import ( corev1 "k8s.io/api/core/v1" apiErrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -96,11 +97,20 @@ func fetchAutonomousDatabases(kubeClient client.Client, namespace string) (*dbv4 return adbList, nil } -func FetchAutonomousDatabaseBackups(kubeClient client.Client, namespace string) (*dbv4.AutonomousDatabaseBackupList, error) { +func FetchAutonomousDatabaseBackups(kubeClient client.Client, namespace string, adbName string) (*dbv4.AutonomousDatabaseBackupList, error) { // Get the list of AutonomousDatabaseBackupOCID in the same namespace backupList := &dbv4.AutonomousDatabaseBackupList{} - if err := kubeClient.List(context.TODO(), backupList, &client.ListOptions{Namespace: namespace}); err != nil { + // Create a label selector + selector := labels.Set{"adb": adbName}.AsSelector() + + if err := kubeClient.List( + context.TODO(), + backupList, + &client.ListOptions{ + Namespace: namespace, + LabelSelector: selector, + }); err != nil { // Ignore not-found errors, since they can't be fixed by an immediate requeue. // No need to change the since we don't know if we obtain the object. if !apiErrors.IsNotFound(err) { diff --git a/commons/oci/create_autonomous_database_clone_request_response.go b/commons/oci/create_autonomous_database_clone_request_response.go deleted file mode 100644 index 40b645fb..00000000 --- a/commons/oci/create_autonomous_database_clone_request_response.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2016, 2018, 2024, Oracle and/or its affiliates. All rights reserved. -// This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license. -// Code generated. DO NOT EDIT. - -package oci - -import ( - "fmt" - "github.com/oracle/oci-go-sdk/v65/common" - "github.com/oracle/oci-go-sdk/v65/database" - "net/http" - "strings" -) - -// CreateAutonomousDatabaseRequest wrapper for the CreateAutonomousDatabase operation -// -// # See also -// -// Click https://docs.cloud.oracle.com/en-us/iaas/tools/go-sdk-examples/latest/database/CreateAutonomousDatabase.go.html to see an example of how to use CreateAutonomousDatabaseRequest. -type CreateAutonomousDatabaseCloneRequest struct { - - // Request to create a new Autonomous Database. - CreateAutonomousDatabaseCloneDetails database.CreateAutonomousDatabaseCloneDetails `contributesTo:"body"` - - // A token that uniquely identifies a request so it can be retried in case of a timeout or - // server error without risk of executing that same action again. Retry tokens expire after 24 - // hours, but can be invalidated before then due to conflicting operations (for example, if a resource - // has been deleted and purged from the system, then a retry of the original creation request - // may be rejected). - OpcRetryToken *string `mandatory:"false" contributesTo:"header" name:"opc-retry-token"` - - // Unique identifier for the request. - OpcRequestId *string `mandatory:"false" contributesTo:"header" name:"opc-request-id"` - - // Metadata about the request. This information will not be transmitted to the service, but - // represents information that the SDK will consume to drive retry behavior. - RequestMetadata common.RequestMetadata -} - -func (request CreateAutonomousDatabaseCloneRequest) String() string { - return common.PointerString(request) -} - -// HTTPRequest implements the OCIRequest interface -func (request CreateAutonomousDatabaseCloneRequest) HTTPRequest(method, path string, binaryRequestBody *common.OCIReadSeekCloser, extraHeaders map[string]string) (http.Request, error) { - - _, err := request.ValidateEnumValue() - if err != nil { - return http.Request{}, err - } - return common.MakeDefaultHTTPRequestWithTaggedStructAndExtraHeaders(method, path, request, extraHeaders) -} - -// BinaryRequestBody implements the OCIRequest interface -func (request CreateAutonomousDatabaseCloneRequest) BinaryRequestBody() (*common.OCIReadSeekCloser, bool) { - - return nil, false - -} - -// RetryPolicy implements the OCIRetryableRequest interface. This retrieves the specified retry policy. -func (request CreateAutonomousDatabaseCloneRequest) RetryPolicy() *common.RetryPolicy { - return request.RequestMetadata.RetryPolicy -} - -// ValidateEnumValue returns an error when providing an unsupported enum value -// This function is being called during constructing API request process -// Not recommended for calling this function directly -func (request CreateAutonomousDatabaseCloneRequest) ValidateEnumValue() (bool, error) { - errMessage := []string{} - if len(errMessage) > 0 { - return true, fmt.Errorf(strings.Join(errMessage, "\n")) - } - return false, nil -} - -// CreateAutonomousDatabaseResponse wrapper for the CreateAutonomousDatabase operation -type CreateAutonomousDatabaseCloneResponse struct { - - // The underlying http response - RawResponse *http.Response - - // The AutonomousDatabase instance - database.AutonomousDatabase `presentIn:"body"` - - // For optimistic concurrency control. See `if-match`. - Etag *string `presentIn:"header" name:"etag"` - - // Unique Oracle-assigned identifier for the request. If you need to contact Oracle about - // a particular request, please provide the request ID. - OpcRequestId *string `presentIn:"header" name:"opc-request-id"` - - // The OCID (https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the work request. Multiple OCID values are returned in a comma-separated list. Use GetWorkRequest with a work request OCID to track the status of the request. - OpcWorkRequestId *string `presentIn:"header" name:"opc-work-request-id"` -} - -func (response CreateAutonomousDatabaseCloneResponse) String() string { - return common.PointerString(response) -} - -// HTTPResponse implements the OCIResponse interface -func (response CreateAutonomousDatabaseCloneResponse) HTTPResponse() *http.Response { - return response.RawResponse -} diff --git a/commons/oci/database.go b/commons/oci/database.go index 1fa6c309..e43afb56 100644 --- a/commons/oci/database.go +++ b/commons/oci/database.go @@ -41,7 +41,6 @@ package oci import ( "context" "fmt" - "net/http" "github.com/go-logr/logr" "github.com/oracle/oci-go-sdk/v65/common" @@ -384,7 +383,7 @@ func (d *DatabaseService) GetAutonomousDatabaseBackup(backupOCID string) (databa return d.dbClient.GetAutonomousDatabaseBackup(context.TODO(), getBackupRequest) } -func (d *DatabaseService) CreateAutonomousDatabaseClone(adb *dbv4.AutonomousDatabase) (resp CreateAutonomousDatabaseCloneResponse, err error) { +func (d *DatabaseService) CreateAutonomousDatabaseClone(adb *dbv4.AutonomousDatabase) (resp database.CreateAutonomousDatabaseResponse, err error) { adminPassword, err := d.readPassword(adb.Namespace, adb.Spec.Clone.AdminPassword) if err != nil { return resp, err @@ -396,9 +395,8 @@ func (d *DatabaseService) CreateAutonomousDatabaseClone(adb *dbv4.AutonomousData } retryPolicy := common.DefaultRetryPolicy() - - request := CreateAutonomousDatabaseCloneRequest{ - CreateAutonomousDatabaseCloneDetails: database.CreateAutonomousDatabaseCloneDetails{ + request := database.CreateAutonomousDatabaseRequest{ + CreateAutonomousDatabaseDetails: database.CreateAutonomousDatabaseCloneDetails{ CompartmentId: adb.Spec.Clone.CompartmentId, SourceId: adb.Spec.Details.Id, AutonomousContainerDatabaseId: acdOCID, @@ -430,58 +428,5 @@ func (d *DatabaseService) CreateAutonomousDatabaseClone(adb *dbv4.AutonomousData }, } - return d.createAutonomousDatabaseClone(context.TODO(), request) -} - -func (d *DatabaseService) createAutonomousDatabaseClone(ctx context.Context, request CreateAutonomousDatabaseCloneRequest) (response CreateAutonomousDatabaseCloneResponse, err error) { - var ociResponse common.OCIResponse - policy := common.NoRetryPolicy() - if request.RetryPolicy() != nil { - policy = *request.RetryPolicy() - } - - if !(request.OpcRetryToken != nil && *request.OpcRetryToken != "") { - request.OpcRetryToken = common.String(common.RetryToken()) - } - - ociResponse, err = common.Retry(ctx, request, d.sencCreateAutonomousDatabaseClone, policy) - if err != nil { - if ociResponse != nil { - if httpResponse := ociResponse.HTTPResponse(); httpResponse != nil { - opcRequestId := httpResponse.Header.Get("opc-request-id") - response = CreateAutonomousDatabaseCloneResponse{RawResponse: httpResponse, OpcRequestId: &opcRequestId} - } else { - response = CreateAutonomousDatabaseCloneResponse{} - } - } - return - } - if convertedResponse, ok := ociResponse.(CreateAutonomousDatabaseCloneResponse); ok { - response = convertedResponse - } else { - err = fmt.Errorf("failed to convert OCIResponse into CreateAutonomousDatabaseResponse") - } - return -} - -func (d *DatabaseService) sencCreateAutonomousDatabaseClone(ctx context.Context, request common.OCIRequest, binaryReqBody *common.OCIReadSeekCloser, extraHeaders map[string]string) (common.OCIResponse, error) { - - httpRequest, err := request.HTTPRequest(http.MethodPost, "/autonomousDatabases", binaryReqBody, extraHeaders) - if err != nil { - return nil, err - } - - var response CreateAutonomousDatabaseCloneResponse - var httpResponse *http.Response - httpResponse, err = d.dbClient.Call(ctx, &httpRequest) - defer common.CloseBodyIfValid(httpResponse) - response.RawResponse = httpResponse - if err != nil { - apiReferenceLink := "https://docs.oracle.com/iaas/api/#/en/database/20160918/AutonomousDatabase/CreateAutonomousDatabase" - err = common.PostProcessServiceError(err, "Database", "CreateAutonomousDatabase", apiReferenceLink) - return response, err - } - - err = common.UnmarshalResponse(httpResponse, &response) - return response, err + return d.dbClient.CreateAutonomousDatabase(context.TODO(), request) } diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index a6a378d4..5145aea5 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -301,11 +301,16 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R specChanged = true } - /***************************************************** - * Sync AutonomousDatabase Backups from OCI - *****************************************************/ - if err := r.syncBackupResources(logger, desiredAdb); err != nil { - return r.manageError(logger.WithName("syncBackupResources"), desiredAdb, err) + /****************************************************************** + * Sync AutonomousDatabase Backups from OCI. + * The backups will not be synced when the lifecycle state is + * TERMINATING or TERMINATED. + ******************************************************************/ + if desiredAdb.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminating && + desiredAdb.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminated { + if err := r.syncBackupResources(logger, desiredAdb); err != nil { + return r.manageError(logger.WithName("syncBackupResources"), desiredAdb, err) + } } /***************************************************** @@ -514,6 +519,10 @@ func (r *AutonomousDatabaseReconciler) performOperation( return false, err } + if err := r.removeBackupResources(l, adb); err != nil { + return false, err + } + adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateTerminating } else if dbv4.IsAdbIntermediateState(adb.Status.LifecycleState) { l.Info("Can not terminate an ADB in an intermediate state; exit reconcile") @@ -700,7 +709,7 @@ func (r *AutonomousDatabaseReconciler) syncBackupResources(logger logr.Logger, a l := logger.WithName("syncBackupResources") // Get the list of AutonomousDatabaseBackupOCID in the same namespace - backupList, err := k8s.FetchAutonomousDatabaseBackups(r.KubeClient, adb.Namespace) + backupList, err := k8s.FetchAutonomousDatabaseBackups(r.KubeClient, adb.Namespace, adb.Name) if err != nil { return err } @@ -790,3 +799,24 @@ func (r *AutonomousDatabaseReconciler) ifBackupExists(backupSummary database.Aut return false } + +// removeBackupResources remove all the AutonomousDatabasBackups that +// are associated with the adb +func (r *AutonomousDatabaseReconciler) removeBackupResources(logger logr.Logger, adb *dbv4.AutonomousDatabase) error { + l := logger.WithName("removeBackupResources") + + // Get the list of AutonomousDatabaseBackupOCID in the same namespace + backupList, err := k8s.FetchAutonomousDatabaseBackups(r.KubeClient, adb.Namespace, adb.Name) + if err != nil { + return err + } + + for _, backup := range backupList.Items { + if err := r.KubeClient.Delete(context.TODO(), &backup); err != nil { + return err + } + l.Info("Delete AutonomousDatabaseBackup " + backup.Name) + } + + return nil +} From 4c06101a4cfc6abc3690c2f2f82ab8e36e27b841 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Wed, 5 Feb 2025 08:09:39 +0000 Subject: [PATCH 173/414] fix in documentation 37518342 --- docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml index 4eb0fa27..34811df7 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml @@ -23,7 +23,6 @@ spec: diskRedundancy: "EXTERNAL" hostName: "host01234" initialDataStorageSizeInGB: 256 - licenseModel: "BRING_YOUR_OWN_LICENSE" pdbName: "PDB0123" privateIp: "10.0.1.99" shape: "VM.Standard2.2" From f6453ada42396da6108a9efcad1d3930a062a5d9 Mon Sep 17 00:00:00 2001 From: Kuassi Mensah <> Date: Wed, 5 Feb 2025 10:19:19 -0800 Subject: [PATCH 174/414] Updated Readme.md Add new lifecycle operations and new ORDS controler. --- README.md | 91 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index e0e7cdb8..3e2d15c8 100644 --- a/README.md +++ b/README.md @@ -2,53 +2,68 @@ ## Make Oracle Database Kubernetes Native -As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released _Oracle Database Operator for Kubernetes_ (`OraOperator` or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating Oracle Database lifecycle management. +As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released the _Oracle Database Operator for Kubernetes_ (`OraOperator` or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating the management of the Oracle Database lifecycle. -In this v1.1.0 production release, `OraOperator` supports the following database configurations and infrastructure: +## Supported Database Configurations in V1.2.0 +In this v1.2.0 production release, `OraOperator` supports the following database configurations, and controllers: * Oracle Autonomous Database: * Oracle Autonomous Database shared Oracle Cloud Infrastructure (OCI) (ADB-S) * Oracle Autonomous Database on dedicated Cloud infrastructure (ADB-D) - * Oracle Autonomous Container Database (ACD) (infrastructure) is the infrastructure for provisioning Autonomous Databases. + * Oracle Autonomous Container Database (ACD), the infrastructure for provisioning Autonomous Databases. * Containerized Single Instance databases (SIDB) deployed in the Oracle Kubernetes Engine (OKE) and any k8s where OraOperator is deployed * Containerized Sharded databases (SHARDED) deployed in OKE and any k8s where OraOperator is deployed * Oracle Multitenant Databases (CDB/PDBs) * Oracle Base Database Cloud Service (BDBCS) -* Oracle Data Guard (Preview status) -* Oracle Database Observability (Preview status) - -Oracle will continue to extend `OraOperator` to support additional Oracle Database configurations. - -## New in V1.1.0 Release -* Namespace scope deployment option -* Enhanced security with namespace scope deployment option -* Support for Oracle Database 23ai Free (with SIDB) -* Automatic Storage Expansion for SIDB and Sharded DB -* User-Defined Sharding -* TCPS support customer provided certs -* Execute custom scripts during DB setup/startup -* Patching for SIDB Primary/Standby in Data Guard -* Long-term backup for Autonomous Databases (ADB): Support for [long-term retention backup](https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/backup-long-term.html) and removed support for the deprecated mandatory backup -* Wallet expiry date for ADB: A user-friendly enhancement to display the wallet expiry date in the status of the associated ADB -* Wait-for-Completion option for ADB: Supports `kubectl wait` command that allows the user to wait for a specific condition on ADB -* OKE workload Identify: Supports OKE workload identity authentication method (i.e., uses OKE credentials). For more details, refer to [Oracle Autonomous Database (ADB) Prerequisites](docs/adb/ADB_PREREQUISITES.md#authorized-with-oke-workload-identity) -* Database Observability (Preview - Metrics) - -## Features Summary +* Oracle Data Guard +* Oracle Database Observability +* Oracle Database Rest Service (ORDS) instances + +## New Lifecycle Features in V1.2.0 Release (Controllers Enhancements) +* ORDSSERVICES + - Install on SIDB and ADB + - Provision and Delete ORDS instances +* SIDB + - Oracle Database 23ai Free support + - Oracle Database 23ai Free-lite support + - SIDB resource management + - True Cache support for Free SIDB databases (Preview) + - Observer for FastStartFailover with Data Guard + - Snapshot Standby support in Data Guard setup +* Globally Distributed Database : Support for Oracle Database 23ai Raft replication +* Autonomous Database: support for Database cloning +* Multitenant DB: + - ORDS-based Controller: assertive deletion policy. + - New LRES based Controller (ARM & AM) + - PDBs settings with init parameters config map + - Assertive deletion policy. +* Database Observability (still preview) + - Support for Database Logs + - Support for the latest Exporter container images + +* Base DB: support for Oracle Database 23ai Cloning +* Prometheus label config (bug fix) + + +* Published on operatorhub.io +* Operator Lifecycle Manager (OLM) support (install from operatorhub.io) +* Validated on Google Kubernetes Engine + +## Overall Features Summary This release of Oracle Database Operator for Kubernetes (the operator) supports the following lifecycle operations: -* ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore +* ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning. * ACD: provision, bind, restart, terminate (soft/hard) -* SIDB: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (a basic observability console), Oracle REST Data Service (ORDS) to support REST based SQL, PDB management, SQL Developer Web, and Application Express (Apex) -* SHARDED: Provision/deploy sharded databases and the shard topology, Add a new shard, Delete an existing shard -* Oracle Multitenant Database: Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB -* Oracle Base Database Cloud Service (BDBCS): provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License +* SIDB: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (basic console), Oracle REST Data Service (ORDS) to support REST based SQL, PDB management, SQL Developer Web, Application Express (Apex), Resource management, True Cache, Observer for FastStartFailover (Data Guard), and Snapshot Standby (Data Guard) +* ORDS Services: provision and delete ORDS instances +* Globally Distrib. (Sharded): Provision/deploy sharded databases and the shard topology, Add a new shard, Delete an existing shard, Raft replication. +* Oracle Multitenant Database (choice of controller): Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB, Assertive deletion policy +* Oracle Base Database Cloud Service (BDBCS): provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License, Cloning. * Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration -* Oracle Database Observability: create, patch, delete databaseObserver resources +* Oracle Database Observability: create, patch, delete databaseObserver resources (Logs and Metrics) * Watch over a set of namespaces or all the namespaces in the cluster using the "WATCH_NAMESPACE" env variable of the operator deployment -The upcoming releases will support new configurations, operations, and capabilities. ## Release Status @@ -56,11 +71,11 @@ This production release has been installed and tested on the following Kubernete * [Oracle Container Engine for Kubernetes (OKE)](https://www.oracle.com/cloud-native/container-engine-kubernetes/) with Kubernetes 1.24 * [Oracle Linux Cloud Native Environment(OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) 1.6 -* [Minikube](https://minikube.sigs.k8s.io/docs/) with version v1.29.0 -* [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) +* [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) * [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/) +* [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) * [Red Hat OKD](https://www.okd.io/) -* [Red Hat OpenShift](https://www.redhat.com/en/technologies/cloud-computing/openshift/) +* [Minikube](https://minikube.sigs.k8s.io/docs/) with version v1.29.0 ## Prerequisites @@ -84,7 +99,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer This is the default mode, in which OraOperator is deployed to operate in a cluster, and to monitor all the namespaces in the cluster. - Grant the `serviceaccount:oracle-database-operator-system:default` cluster wide access for the resources by applying [cluster-role-binding.yaml](./rbac/cluster-role-binding.yaml) - + ```sh kubectl apply -f rbac/cluster-role-binding.yaml ``` @@ -118,7 +133,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer kubectl apply -f oracle-database-operator.yaml ``` - + * ### ClusterRole and ClusterRoleBinding for NodePort services To expose services on each node's IP and port (the NodePort) apply the [node-rbac.yaml](./rbac/node-rbac.yaml). Note that this step is not required for LoadBalancer services. @@ -141,7 +156,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer ```sh $ kubectl get pods -n oracle-database-operator-system - + NAME READY STATUS RESTARTS AGE pod/oracle-database-operator-controller-manager-78666fdddb-s4xcm 1/1 Running 0 11d pod/oracle-database-operator-controller-manager-78666fdddb-5k6n4 1/1 Running 0 11d @@ -242,7 +257,7 @@ The following is an example of a YAML file fragment for specifying Oracle Cloud ociSecretOCID: ocid1.vaultsecret.oc1... ``` -Examples in this repository where passwords are entered on the command line are for demonstration purposes only. +Examples in this repository where passwords are entered on the command line are for demonstration purposes only. ### Reporting a Security Issue From 35ffd93888d9aa3f6c8673696b0317c9e213d782 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Thu, 6 Feb 2025 10:00:56 +0000 Subject: [PATCH 175/414] log message typo correction --- config/crd/bases/database.oracle.com_autonomousdatabases.yaml | 2 -- controllers/database/cdb_controller.go | 2 +- controllers/database/lrest_controller.go | 4 ++-- oracle-database-operator.yaml | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index 69c7d554..1672ae81 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -278,7 +278,6 @@ spec: type: object required: - action - - details type: object status: properties: @@ -612,7 +611,6 @@ spec: type: object required: - action - - details type: object status: properties: diff --git a/controllers/database/cdb_controller.go b/controllers/database/cdb_controller.go index e1e64a57..832e80b1 100644 --- a/controllers/database/cdb_controller.go +++ b/controllers/database/cdb_controller.go @@ -778,7 +778,7 @@ func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, } } else { - log.Info("cdb mark to be delited") + log.Info("cdb set to be deleted") cdb.Status.Phase = cdbPhaseDelete cdb.Status.Status = true r.Status().Update(ctx, cdb) diff --git a/controllers/database/lrest_controller.go b/controllers/database/lrest_controller.go index c2b9a5a7..d32bb645 100644 --- a/controllers/database/lrest_controller.go +++ b/controllers/database/lrest_controller.go @@ -772,7 +772,7 @@ func (r *LRESTReconciler) manageLRESTDeletion(ctx context.Context, req ctrl.Requ } } else { - log.Info("lrest mark to be delited") + log.Info("lrest set to be deleted") lrest.Status.Phase = lrestPhaseDelete lrest.Status.Status = true r.Status().Update(ctx, lrest) @@ -1046,7 +1046,7 @@ func (r *LRESTReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, lres } pdbitem.Status.SqlCode = int(objmap["sqlcode"].(float64)) - log.Info("pdb closuer.......:", "sqlcode", pdbitem.Status.SqlCode) + log.Info("pdb closure.......:", "sqlcode", pdbitem.Status.SqlCode) if errapi != nil { log.Error(err, "callAPI cannot close pdb "+pdbitem.Spec.LRPDBName, "err", err.Error()) diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 2e6c31ff..3ffefe05 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -887,7 +887,6 @@ spec: type: object required: - action - - details type: object status: properties: @@ -1221,7 +1220,6 @@ spec: type: object required: - action - - details type: object status: properties: From 69c9e83cf72cafabe4aecd3c6a95075b15139f9e Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Fri, 7 Feb 2025 12:14:12 +0000 Subject: [PATCH 176/414] vm down update dg --- controllers/dataguard/dataguard_utils.go | 20 +++++++++++++++++-- .../dataguard/dataguardbroker_controller.go | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/controllers/dataguard/dataguard_utils.go b/controllers/dataguard/dataguard_utils.go index 6b529068..1bf0e1b0 100644 --- a/controllers/dataguard/dataguard_utils.go +++ b/controllers/dataguard/dataguard_utils.go @@ -150,6 +150,10 @@ func validateSidbReadiness(r *DataguardBrokerReconciler, broker *dbapi.Dataguard log.Error(err, err.Error()) return err } + if sidbReadyPod.Name == "" { + log.Info("No ready pod avail for the singleinstancedatabase") + return ErrCurrentPrimaryDatabaseNotReady + } log.Info(fmt.Sprintf("Ready pod for the singleInstanceDatabase %s is %s", sidb.Name, sidbReadyPod.Name)) @@ -160,7 +164,7 @@ func validateSidbReadiness(r *DataguardBrokerReconciler, broker *dbapi.Dataguard if apierrors.IsNotFound(err) { //m.Status.Status = dbcommons.StatusError eventReason := "Waiting" - eventMsg := "waiting for secret : " + sidb.Spec.AdminPassword.SecretName + " to get created" + eventMsg := "waiting for : " + sidb.Spec.AdminPassword.SecretName + " to get created" r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) r.Log.Info("Secret " + sidb.Spec.AdminPassword.SecretName + " Not Found") return fmt.Errorf("adminPassword secret for singleinstancedatabase %v not found", sidb.Name) @@ -173,6 +177,14 @@ func validateSidbReadiness(r *DataguardBrokerReconciler, broker *dbapi.Dataguard out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", fmt.Sprintf(dbcommons.ValidateAdminPassword, adminPassword), dbcommons.GetSqlClient(sidb.Spec.Edition))) if err != nil { + if strings.Contains(err.Error(), "dialing backend") && broker.Status.Status == dbcommons.StatusReady && broker.Status.FastStartFailover { + // Connection to the pod is failing after broker came up and running + // Might suggest disconnect or pod/vm going down + log.Info("Dialing connection error") + if err := updateReconcileStatus(r, broker, ctx, req); err != nil { + return err + } + } log.Error(err, err.Error()) return err } @@ -210,8 +222,12 @@ func setupDataguardBrokerConfiguration(r *DataguardBrokerReconciler, broker *dba return err } + log.Info(fmt.Sprintf("broker.Spec.StandbyDatabaseRefs are %v", broker.Spec.StandbyDatabaseRefs)) + for _, database := range broker.Spec.StandbyDatabaseRefs { + log.Info(fmt.Sprintf("adding database %v", database)) + // Get the standby database resource var standbyDatabase dbapi.SingleInstanceDatabase err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: database}, &standbyDatabase) @@ -655,7 +671,7 @@ func setFSFOTargets(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, if apierrors.IsNotFound(err) { //m.Status.Status = dbcommons.StatusError eventReason := "Waiting" - eventMsg := "waiting for secret : " + currentPrimaryDatabase.Spec.AdminPassword.SecretName + " to get created" + eventMsg := "waiting for : " + currentPrimaryDatabase.Spec.AdminPassword.SecretName + " to get created" r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) r.Log.Info("Secret " + currentPrimaryDatabase.Spec.AdminPassword.SecretName + " Not Found") return errors.New("admin password secret not found") diff --git a/controllers/dataguard/dataguardbroker_controller.go b/controllers/dataguard/dataguardbroker_controller.go index 6eadc953..a018a539 100644 --- a/controllers/dataguard/dataguardbroker_controller.go +++ b/controllers/dataguard/dataguardbroker_controller.go @@ -394,6 +394,9 @@ func (r *DataguardBrokerReconciler) manageDataguardBrokerCreation(broker *dbapi. if broker.Status.Status != "" && broker.Status.FastStartFailover { r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Possible Failover", "Primary db not in ready state after setting up DG configuration") } + if err := updateReconcileStatus(r, broker, ctx, req); err != nil { + log.Info("Error updating Dgbroker status") + } r.Recorder.Eventf(broker, corev1.EventTypeWarning, "Waiting", err.Error()) return ctrl.Result{Requeue: true, RequeueAfter: 60 * time.Second}, nil } From 3d0862de5dadb8c40b0bbe833032875525919d80 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Mon, 10 Feb 2025 13:47:56 -0600 Subject: [PATCH 177/414] Fixed logName not persisting --- commons/observability/utils.go | 3 ++- docs/observability/README.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/commons/observability/utils.go b/commons/observability/utils.go index bd4ea0b5..1c62e673 100644 --- a/commons/observability/utils.go +++ b/commons/observability/utils.go @@ -189,8 +189,9 @@ func GetExporterDeploymentVolumeMounts(a *api.DatabaseObserver) []corev1.VolumeM // a.Spec.Log.Path path to mount for a custom log path, a volume is required if rLogPath := a.Spec.Log.Path; rLogPath != "" { + vName := GetLogName(a) volM = append(volM, corev1.VolumeMount{ - Name: DefaultLogVolumeString, + Name: vName, MountPath: rLogPath, }) } diff --git a/docs/observability/README.md b/docs/observability/README.md index 895b8f85..f99cddb5 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -115,7 +115,7 @@ The `databaseObserver` resource provides the remaining multiple fields that are * `spec.log.path` - custom path to create * `spec.log.filename` - custom filename for the log file * `spec.log.volume.name` - custom name for the log volume -* `spec.log.volume.persistentVolumeClaim.claimName` - a volume in which to place the log in order to be shared by the containers. If not specified, an EmptyDir is used by defautl. +* `spec.log.volume.persistentVolumeClaim.claimName` - a volume in which to place the log in order to be shared by the containers. If not specified, an EmptyDir is used by default. * `spec.configuration.configMap.key` - configuration filename inside the container and the configmap * `spec.configuration.configMap.name` - name of the configMap that holds the custom metrics configuration * `spec.replicas` - number of replicas to deploy From ffb87d228b745cf2d02a4d456c8dd42b5d97dcef Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 10 Feb 2025 22:11:27 +0000 Subject: [PATCH 178/414] Update ADB_LONG_TERM_BACKUP.md : one minor phrase fix. --- docs/adb/ADB_LONG_TERM_BACKUP.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/adb/ADB_LONG_TERM_BACKUP.md b/docs/adb/ADB_LONG_TERM_BACKUP.md index 4720697d..312dac0d 100644 --- a/docs/adb/ADB_LONG_TERM_BACKUP.md +++ b/docs/adb/ADB_LONG_TERM_BACKUP.md @@ -2,13 +2,13 @@ To create long-term backups of Autonomous Databases, use this procedure. -Oracle Cloud Infrastructure (OCI) automatically backs up your Autonomous Databases, and retains these backups for 60 days. You can restore and recover your database to any point-in-time in this retention period. Automatic backups are full backups taken every 60 days, with daily incremental backups. You can also create long-term backups for your database with a retention period between 3 months and up to 10 years. For more information, please visit [Create Long-Term Backups on Autonomous Database](https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/backup-long-term.html) and [Backup and Restore Notes](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/backup-restore-notes.html). +Oracle Cloud Infrastructure (OCI) automatically backs up your Autonomous Databases, and retains these backups for 60 days. You can restore and recover your database to any point-in-time in this retention period. Automatic backups are full backups taken every 60 days, with daily incremental backups. You can also create long-term backups for your database with a retention period ranging from 3 months to 10 years. For more information, see: [Create Long-Term Backups on Autonomous Database](https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/backup-long-term.html) and [Backup and Restore Notes](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/backup-restore-notes.html). ## Create Long-Term Backup To back up an Autonomous Database, complete this procedure. -1. Add the following fields to the AutonomousDatabaseBackup resource definition. An example `.yaml` file is available here: [`config/samples/adb/autonomousdatabase_backup.yaml`](./../../config/samples/adb/autonomousdatabase_backup.yaml) +1. Add the following fields to the `AutonomousDatabaseBackup` resource definition. An example `.yaml` file is available here: [`config/samples/adb/autonomousdatabase_backup.yaml`](./../../config/samples/adb/autonomousdatabase_backup.yaml) | Attribute | Type | Description | Required? | |----|----|----|----| | `spec.displayName` | string | The user-friendly name for the backup. This name does not have to be unique. | Yes | From b74362cd2df679f100d8d548c4340e9ff73b817a Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 10 Feb 2025 22:14:27 +0000 Subject: [PATCH 179/414] Update ADB_PREREQUISITES.md minor style edit. --- docs/adb/ADB_PREREQUISITES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/adb/ADB_PREREQUISITES.md b/docs/adb/ADB_PREREQUISITES.md index f8c04c4b..a730f4fe 100644 --- a/docs/adb/ADB_PREREQUISITES.md +++ b/docs/adb/ADB_PREREQUISITES.md @@ -48,7 +48,7 @@ After creating the ConfigMap and the Secret, use their names as the values of `o Instance principal authorization enables the operator to make API calls from an instance (that is, a node) without requiring the `ociConfigMap`, and `ociSecret` attributes in the `.yaml` file. This approach applies only to instances that are running in the Oracle Cloud Infrastructure (OCI). In addition, this approach grants permissions to the nodes that match the rules, which means that all the pods in the nodes can make the service calls. -To set up the instance principals, you will have to: +To set up the instance principals, complete the following tasks: * [Define dynamic group that includes the nodes in which the operator runs](#define-dynamic-group) * [Define policies that grant to the dynamic group the required permissions for the operator to its OCI interactions](#define-policies) @@ -147,4 +147,4 @@ kubectl create configmap oci-cred \ Allow any-user to manage all-resources in compartment where all {request.principal.namespace='oracle-database-operator-system',request.principal.type='workload',request.principal.cluster_id='',request.principal.service_account='default'} ``` -After creating the policy, operator pods will be granted sufficient permissions to call OCI services. You can now proceed to the installation. \ No newline at end of file +After creating the policy, operator pods will be granted sufficient permissions to call OCI services. You can now proceed to the installation. From 2dbc947a8ac94c23fc1bce2d93632387e703b1e0 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 10 Feb 2025 22:45:05 +0000 Subject: [PATCH 180/414] Update PREREQUISITES.md spelling fix, minor phrase fix. --- PREREQUISITES.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PREREQUISITES.md b/PREREQUISITES.md index 01bb94b4..0cbd0b24 100644 --- a/PREREQUISITES.md +++ b/PREREQUISITES.md @@ -2,7 +2,7 @@ ## Prerequisites for Using Oracle Database Operator for Kubernetes -Oracle Database Operator for Kubernetes (OraOperator) manages all Cloud deployments of Oracle Database, including: +Oracle Database Operator for Kubernetes (`OraOperator`) manages all Cloud deployments of Oracle Database, including: * Oracle Autonomous Database (ADB) * Containerized Oracle Database Single Instance (SIDB) @@ -19,16 +19,16 @@ To set up a Kubernetes cluster on Oracle Cloud Infrastructure: 1. Create an OKE Cluster 1. Provision persistent storage for data files (NFS or Block) -Note: You must provision persistent storage if you intend to deploy containerized databases over the OKE cluster. +Note: If you intend to deploy containerized databases over the OKE cluster, then you must provision persistent storage. ### Prerequites for Oracle Autonomous Database (ADB) -If you intent to use `OraOperator` to handle Oracle Autonomous Database lifecycles, then read [Oracle Autonomous Database prerequisites](./docs/adb/ADB_PREREQUISITES.md) +If you intend to use `OraOperator` to handle Oracle Autonomous Database lifecycles, then read [Oracle Autonomous Database prerequisites](./docs/adb/ADB_PREREQUISITES.md) ### Prerequites for Single Instance Databases (SIDB) -If you intent to use `OraOperator` to handle Oracle Database Single Instance lifecycles, then read [Single Instance Database Prerequisites](./docs/sidb/PREREQUISITES.md) +If you intend to use `OraOperator` to handle Oracle Database Single Instance lifecycles, then read [Single Instance Database Prerequisites](./docs/sidb/PREREQUISITES.md) ### Prerequites for Sharded Databases (SHARDING) - If you intent to use OraOperator to handle the lifecycle of Oracle Database deployed with Oracle Sharding, then read [Sharded Database Prerequisites](./docs/sharding/README.md#prerequsites-for-running-oracle-sharding-database-controller) + If you intend to use OraOperator to handle the lifecycle of Oracle Database deployed with Oracle Sharding, then read [Sharded Database Prerequisites](./docs/sharding/README.md#prerequsites-for-running-oracle-sharding-database-controller) From daf2e58db39e7278fcca48f63960f5c77d805892 Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Tue, 11 Feb 2025 11:46:58 +0000 Subject: [PATCH 181/414] Restart observer pods --- controllers/dataguard/dataguard_utils.go | 35 ++++++++ .../dataguard/dataguardbroker_controller.go | 88 +++++++++---------- 2 files changed, 78 insertions(+), 45 deletions(-) diff --git a/controllers/dataguard/dataguard_utils.go b/controllers/dataguard/dataguard_utils.go index 1bf0e1b0..371d23f9 100644 --- a/controllers/dataguard/dataguard_utils.go +++ b/controllers/dataguard/dataguard_utils.go @@ -748,6 +748,41 @@ func createObserverPods(r *DataguardBrokerReconciler, broker *dbapi.DataguardBro return nil } + // Stop the already running observer + // find the avail pods for the currPrimaryDatabase + log.Info("Need to stop the observer if already running") + currPrimaryDatabaseReadyPod, _, _, _, err := dbcommons.FindPods(r, "", "", currPrimaryDatabase.Name, currPrimaryDatabase.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return err + } + if currPrimaryDatabaseReadyPod.Name == "" { + return errors.New("No ready pods avail ") + } + + // fetch singleinstancedatabase admin password + var adminPasswordSecret corev1.Secret + if err = r.Get(ctx, types.NamespacedName{Name: currPrimaryDatabase.Spec.AdminPassword.SecretName, Namespace: currPrimaryDatabase.Namespace}, &adminPasswordSecret); err != nil { + if apierrors.IsNotFound(err) { + //m.Status.Status = dbcommons.StatusError + eventReason := "Waiting" + eventMsg := "waiting for : " + currPrimaryDatabase.Spec.AdminPassword.SecretName + " to get created" + r.Recorder.Eventf(broker, corev1.EventTypeNormal, eventReason, eventMsg) + r.Log.Info("Secret " + currPrimaryDatabase.Spec.AdminPassword.SecretName + " Not Found") + return errors.New("admin password secret not found") + } + log.Error(err, err.Error()) + return err + } + adminPassword := string(adminPasswordSecret.Data[currPrimaryDatabase.Spec.AdminPassword.SecretKey]) + + out, err := dbcommons.ExecCommand(r, r.Config, currPrimaryDatabaseReadyPod.Name, currPrimaryDatabaseReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \" STOP OBSERVER %s \" | dgmgrl sys/%s@%s ", broker.Name, adminPassword, currPrimaryDatabase.Status.Sid)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info(out) // instantiate observer pod specification pod := dbcommons.NewRealPodBuilder(). SetNamespacedName(types.NamespacedName{ diff --git a/controllers/dataguard/dataguardbroker_controller.go b/controllers/dataguard/dataguardbroker_controller.go index a018a539..c321d8ab 100644 --- a/controllers/dataguard/dataguardbroker_controller.go +++ b/controllers/dataguard/dataguardbroker_controller.go @@ -123,64 +123,62 @@ func (r *DataguardBrokerReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // manage enabling and disabling faststartfailover - if dataguardBroker.Spec.FastStartFailover != dataguardBroker.Status.FastStartFailover { - if dataguardBroker.Spec.FastStartFailover { - - for _, DbResource := range dataguardBroker.Status.DatabasesInDataguardConfig { - var singleInstanceDatabase dbapi.SingleInstanceDatabase - if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: DbResource}, &singleInstanceDatabase); err != nil { - return ctrl.Result{Requeue: false}, err - } - r.Log.Info("Check the role for database", "database", singleInstanceDatabase.Name, "role", singleInstanceDatabase.Status.Role) - if singleInstanceDatabase.Status.Role == "SNAPSHOT_STANDBY" { - r.Recorder.Eventf(&dataguardBroker, corev1.EventTypeWarning, "Enabling FSFO failed", "database %s is a snapshot database", singleInstanceDatabase.Name) - r.Log.Info("Enabling FSFO failed, one of the database is a snapshot database", "snapshot database", singleInstanceDatabase.Name) - return ctrl.Result{Requeue: true}, nil - } - } + if dataguardBroker.Spec.FastStartFailover { - // set faststartfailover targets for all the singleinstancedatabases in the dataguard configuration - if err := setFSFOTargets(r, &dataguardBroker, ctx, req); err != nil { + for _, DbResource := range dataguardBroker.Status.DatabasesInDataguardConfig { + var singleInstanceDatabase dbapi.SingleInstanceDatabase + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: DbResource}, &singleInstanceDatabase); err != nil { return ctrl.Result{Requeue: false}, err } - - // enable faststartfailover in the dataguard configuration - if err := enableFSFOForDgConfig(r, &dataguardBroker, ctx, req); err != nil { - return ctrl.Result{Requeue: false}, err + r.Log.Info("Check the role for database", "database", singleInstanceDatabase.Name, "role", singleInstanceDatabase.Status.Role) + if singleInstanceDatabase.Status.Role == "SNAPSHOT_STANDBY" { + r.Recorder.Eventf(&dataguardBroker, corev1.EventTypeWarning, "Enabling FSFO failed", "database %s is a snapshot database", singleInstanceDatabase.Name) + r.Log.Info("Enabling FSFO failed, one of the database is a snapshot database", "snapshot database", singleInstanceDatabase.Name) + return ctrl.Result{Requeue: true}, nil } + } - // create Observer Pod - if err := createObserverPods(r, &dataguardBroker, ctx, req); err != nil { - return ctrl.Result{Requeue: false}, err - } + // set faststartfailover targets for all the singleinstancedatabases in the dataguard configuration + if err := setFSFOTargets(r, &dataguardBroker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } - // set faststartfailover status to true - dataguardBroker.Status.FastStartFailover = true + // enable faststartfailover in the dataguard configuration + if err := enableFSFOForDgConfig(r, &dataguardBroker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } - } else { + // create Observer Pod + if err := createObserverPods(r, &dataguardBroker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } - // disable faststartfailover - if err := disableFSFOForDGConfig(r, &dataguardBroker, ctx, req); err != nil { - return ctrl.Result{Requeue: false}, err - } + // set faststartfailover status to true + dataguardBroker.Status.FastStartFailover = true + + } else { - // delete Observer Pod - observerReadyPod, _, _, _, err := dbcommons.FindPods(r, "", "", dataguardBroker.Name, dataguardBroker.Namespace, ctx, req) - if err != nil { + // disable faststartfailover + if err := disableFSFOForDGConfig(r, &dataguardBroker, ctx, req); err != nil { + return ctrl.Result{Requeue: false}, err + } + + // delete Observer Pod + observerReadyPod, _, _, _, err := dbcommons.FindPods(r, "", "", dataguardBroker.Name, dataguardBroker.Namespace, ctx, req) + if err != nil { + return ctrl.Result{Requeue: false}, err + } + if observerReadyPod.Name != "" { + if err := r.Delete(ctx, &observerReadyPod); err != nil { return ctrl.Result{Requeue: false}, err } - if observerReadyPod.Name != "" { - if err := r.Delete(ctx, &observerReadyPod); err != nil { - return ctrl.Result{Requeue: false}, err - } - } + } - r.Recorder.Eventf(&dataguardBroker, corev1.EventTypeNormal, "Observer Deleted", "database observer pod deleted") - log.Info("database observer deleted") + r.Recorder.Eventf(&dataguardBroker, corev1.EventTypeNormal, "Observer Deleted", "database observer pod deleted") + log.Info("database observer deleted") - // set faststartfailover status to false - dataguardBroker.Status.FastStartFailover = false - } + // set faststartfailover status to false + dataguardBroker.Status.FastStartFailover = false } // manage manual switchover From 9ef4c244117ec56b34bbbd057bafb707c40029b3 Mon Sep 17 00:00:00 2001 From: Kuassi Mensah <> Date: Tue, 11 Feb 2025 07:27:59 -0800 Subject: [PATCH 182/414] Updated Third_Party_Licenses.txt Updated Third_Party_Licenses.txt. --- README.md | 7 +++-- THIRD_PARTY_LICENSES.txt | 65 +++++++++++++++++++--------------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 3e2d15c8..3d055ab8 100644 --- a/README.md +++ b/README.md @@ -37,14 +37,15 @@ In this v1.2.0 production release, `OraOperator` supports the following database - New LRES based Controller (ARM & AM) - PDBs settings with init parameters config map - Assertive deletion policy. -* Database Observability (still preview) - - Support for Database Logs +* Database Observability (preview) + - Support for Database Logs (in addition to Metrics) - Support for the latest Exporter container images * Base DB: support for Oracle Database 23ai Cloning * Prometheus label config (bug fix) - +## New Product Features +*The Operator itself, as a product, brings the following new features: * Published on operatorhub.io * Operator Lifecycle Manager (OLM) support (install from operatorhub.io) * Validated on Google Kubernetes Engine diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt index d75f3946..14e4308f 100644 --- a/THIRD_PARTY_LICENSES.txt +++ b/THIRD_PARTY_LICENSES.txt @@ -1,11 +1,11 @@ ------------------------------------- -Operator SDK 1.32.0 +Operator SDK 1.37.0 https://github.com/operator-framework/operator-sdk Apache 2.0 ------------------------------------- -Apache License: +Apache License: Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -206,13 +206,13 @@ Apache License: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + ------------------------------ - GO lang 1.21.4 + GO lang 1.23.3 https://github.com/golang - Copyright (c) 2009 The Go Authors. + Copyright (c) 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -241,22 +241,20 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------- -apimachinery 0.28.4 +apimachinery 0.31.3 https://github.com/kubernetes/apimachinery/tr Apache 2.0 ------------------------- -controller-runtime 0.16.3 +controller-runtime 0.19.3 https://github.com/kubernetes-sigs/controller-runtime/releases/tag/v0.16.3 Apache 2.0 ------------------------- -golang 1.21.4 +golang 1.23.3 https://github.com/golang/go/releases/tag/go1.21.4 -BSD 2-clause or 3-clause - -BSD 2-clause or 3-clause License: +BSD 2-clause or 3-clause License: Copyright (c) 2009 The Go Authors. All rights reserved. @@ -1008,23 +1006,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. limitations under the License. -------------------------- -Logr 1.3.0 +Logr 1.4.2 https://pkg.go.dev/github.com/go-logr/logr https://github.com/go-logr/logr/tree/v1.3.0 Apache 2.0 License ------------------------- -OCI Go SDK 65.53.0 +OCI Go SDK 65.77.1 https://github.com/oracle/oci-go-sdk/releases/tag/v65.53.0 Dual-License: UPL + Apache 2.0 -UPL license: +UPL license: -Copyright (c) 2016, 2018, 2020, Oracle and/or its affiliates. -This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 +Copyright (c) 2016, 2018, 2020, Oracle and/or its affiliates. +This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl -or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. +or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license. Copyright (c) 2019, 2020 Oracle and/or its affiliates. @@ -1065,58 +1063,58 @@ The Universal Permissive License (UPL), Version 1.0 ------------------------- -ginkgo 2.13.1 +ginkgo 2.202. https://github.com/onsi/ginkgo/releases/tag/v2.13.1 MIT ------------------------------------ Gomega github.com/onsi/gomega -MIT License +MIT License Copyright (c) 2013-2014 Onsi Fakhouri ---------------------------- -gomega 1.30.0 +gomega 1.34.2 http://onsi.github.io/gomega/ MIT ------------------------- -Kubernetes api 0.28.4 +Kubernetes api 0.31.3 https://pkg.go.dev/k8s.io/api Apache 2.0 ---------------------------------- -Kubernetes apimachinery 0.28.4 +Kubernetes apimachinery 0.31.3 https://pkg.go.dev/k8s.io/apimachinery Apache 2.0 ----------------------------------- -Kubernetes client-go 0.28.4 +Kubernetes client-go 0.31.3 https://pkg.go.dev/k8s.io/client-go Apache 2.0 ------------------------------------- -Kubernetes controller-runtime project 0.16.3 +Kubernetes controller-runtime project 0.19.3 https://pkg.go.dev/sigs.k8s.io/controller-runtime Apache 2.0 ------------------------------------ kubernetes-sigs/yaml 1.4.0 https://github.com/kubernetes-sigs/yaml/tree/v1.3.0 -MIT +MIT ------------------------- -OCI SDK for Go 65.53.0 +OCI SDK for Go 65.77.1 https://github.com/oracle/oci-go-sdk Multiple Licenses: Apache 2.0, UPL ------------------------------ -Operator Lifecycle Manager (OLM) +Operator Lifecycle Manager (OLM) 0.30.0 github.com/operator-framework/operator-lifecycle-manager Apache 2.0 ------------------------------------ -Prometheus Operator 0.65.2 +Prometheus Operator 0.78.2 https://github.com/prometheus-operator/prometheus-operator Apache 2.0 @@ -1135,8 +1133,8 @@ https://pkg.go.dev/sigs.k8s.io/yaml Dual license: BSD-3-Clause, MIT ------------------------------------ -zap 1.26.0 -https://github.com/uber-go/zap/releases/tag/v1.26.0 +zap 1.27.0 +https://github.com/uber-go/zap/releases/tag/v1.27.0 MIT ------------------------------------ @@ -1163,7 +1161,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Copyright (c) 2012 The Go Authors. +Copyright (c) 2012 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1192,9 +1190,9 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------ -Ginkgo 2.13.1 +Ginkgo 2.20.2 github.com/onsi/ginkgo -MIT License +MIT License Copyright (c) 2013-2014 Onsi Fakhouri Permission is hereby granted, free of charge, to any person obtaining @@ -1216,4 +1214,3 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------- - From 9fb5ae09807919fc85d8ad2af8a5999fb5795213 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Tue, 11 Feb 2025 16:41:14 +0000 Subject: [PATCH 183/414] lrfinalizer --- controllers/database/cdb_controller.go | 2 +- controllers/database/lrest_controller.go | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/controllers/database/cdb_controller.go b/controllers/database/cdb_controller.go index 832e80b1..6c5fc747 100644 --- a/controllers/database/cdb_controller.go +++ b/controllers/database/cdb_controller.go @@ -1058,7 +1058,7 @@ func (r *CDBReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, cdb *d } r.Recorder.Eventf(cdb, corev1.EventTypeNormal, "drop pdb", "pdbname=%s", pdbitem.Spec.PDBName) - err = r.Delete(context.Background(), &pdbitem, client.GracePeriodSeconds(1)) + err = r.Delete(context.Background(), &pdbitem, client.GracePeriodSeconds(0)) if err != nil { log.Info("Could not delete PDB resource", "err", err.Error()) return err diff --git a/controllers/database/lrest_controller.go b/controllers/database/lrest_controller.go index d32bb645..91c883e1 100644 --- a/controllers/database/lrest_controller.go +++ b/controllers/database/lrest_controller.go @@ -1077,17 +1077,17 @@ func (r *LRESTReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, lres } r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "drop pdb", "pdbname=%s", pdbitem.Spec.LRPDBName) - /* - if controllerutil.ContainsFinalizer(&pdbitem, LRPDBFinalizer) { - log.Info("Removing finalizer") - controllerutil.RemoveFinalizer(&pdbitem, LRPDBFinalizer) - err = r.Update(ctx, &pdbitem) - if err != nil { - log.Info("Could not remove finalizer", "err", err.Error()) - return err - } + /* remove finalizer */ + + if controllerutil.ContainsFinalizer(&pdbitem, LRPDBFinalizer) { + log.Info("Removing finalizer") + controllerutil.RemoveFinalizer(&pdbitem, LRPDBFinalizer) + err = r.Update(ctx, &pdbitem) + if err != nil { + log.Info("Could not remove finalizer", "err", err.Error()) + return err } - */ + } err = r.Delete(context.Background(), &pdbitem, client.GracePeriodSeconds(1)) if err != nil { From e43d15e6a0940c23bbe30812227fddc492022e9a Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 17:19:27 +0000 Subject: [PATCH 184/414] Update README.md minor updates, change copyright date to 2025 --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3d055ab8..746c58ef 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,8 @@ In this v1.2.0 production release, `OraOperator` supports the following database ## New Product Features *The Operator itself, as a product, brings the following new features: -* Published on operatorhub.io -* Operator Lifecycle Manager (OLM) support (install from operatorhub.io) +* Published on `operatorhub.io` +* Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) * Validated on Google Kubernetes Engine ## Overall Features Summary @@ -55,15 +55,15 @@ In this v1.2.0 production release, `OraOperator` supports the following database This release of Oracle Database Operator for Kubernetes (the operator) supports the following lifecycle operations: * ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning. -* ACD: provision, bind, restart, terminate (soft/hard) +* ACD: Provision, bind, restart, terminate (soft/hard) * SIDB: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (basic console), Oracle REST Data Service (ORDS) to support REST based SQL, PDB management, SQL Developer Web, Application Express (Apex), Resource management, True Cache, Observer for FastStartFailover (Data Guard), and Snapshot Standby (Data Guard) -* ORDS Services: provision and delete ORDS instances +* ORDS Services: Provision and delete ORDS instances * Globally Distrib. (Sharded): Provision/deploy sharded databases and the shard topology, Add a new shard, Delete an existing shard, Raft replication. * Oracle Multitenant Database (choice of controller): Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB, Assertive deletion policy -* Oracle Base Database Cloud Service (BDBCS): provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License, Cloning. +* Oracle Base Database Cloud Service (BDBCS): Provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License, Cloning. * Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration -* Oracle Database Observability: create, patch, delete databaseObserver resources (Logs and Metrics) -* Watch over a set of namespaces or all the namespaces in the cluster using the "WATCH_NAMESPACE" env variable of the operator deployment +* Oracle Database Observability: create, patch, delete `databaseObserver` resources (Logs and Metrics) +* Watch over a set of namespaces or all the namespaces in the cluster using the `WATCH_NAMESPACE` environment variable of the operator deployment ## Release Status @@ -84,7 +84,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer * ### Install cert-manager - The operator uses webhooks for validating user input before persisting it in etcd. Webhooks require TLS certificates that are generated and managed by a certificate manager. + The operator uses webhooks for validating user input before persisting it in `etcd`. Webhooks require TLS certificates that are generated and managed by a certificate manager. Install the certificate manager with the following command: @@ -99,7 +99,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer This is the default mode, in which OraOperator is deployed to operate in a cluster, and to monitor all the namespaces in the cluster. - - Grant the `serviceaccount:oracle-database-operator-system:default` cluster wide access for the resources by applying [cluster-role-binding.yaml](./rbac/cluster-role-binding.yaml) + - Grant the `serviceaccount:oracle-database-operator-system:default` clusterwide access for the resources by applying [cluster-role-binding.yaml](./rbac/cluster-role-binding.yaml) ```sh kubectl apply -f rbac/cluster-role-binding.yaml @@ -113,22 +113,22 @@ Oracle strongly recommends that you ensure your system meets the following [Prer ##### 2. Namespace Scoped Deployment - In this mode, OraOperator can be deployed to operate in a namespace, and to monitor one or many namespaces. + In this mode, `OraOperator` can be deployed to operate in a namespace, and to monitor one or many namespaces. - - Grant `serviceaccount:oracle-database-operator-system:default` service account with resource access in the required namespaces. For example, to monitor only the default namespace, apply the [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) + - Grant `serviceaccount:oracle-database-operator-system:default` service account with resource access in the required namespaces. For example, to monitor only the default namespace, apply the [`default-ns-role-binding.yaml`](./rbac/default-ns-role-binding.yaml) ```sh kubectl apply -f rbac/default-ns-role-binding.yaml ``` To watch additional namespaces, create different role binding files for each namespace, using [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) as a template, and changing the `metadata.name` and `metadata.namespace` fields - - Next, edit the [oracle-database-operator.yaml](./oracle-database-operator.yaml) to add the required namespaces under `WATCH_NAMESPACE`. Use comma-delimited values for multiple namespaces. + - Next, edit the [`oracle-database-operator.yaml`](./oracle-database-operator.yaml) to add the required namespaces under `WATCH_NAMESPACE`. Use comma-delimited values for multiple namespaces. ```sh - name: WATCH_NAMESPACE value: "default" ``` - - Finally, apply the edited [oracle-database-operator.yaml](./oracle-database-operator.yaml) to deploy the Operator + - Finally, apply the edited [`oracle-database-operator.yaml`](./oracle-database-operator.yaml) to deploy the Operator ```sh kubectl apply -f oracle-database-operator.yaml @@ -137,7 +137,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer * ### ClusterRole and ClusterRoleBinding for NodePort services - To expose services on each node's IP and port (the NodePort) apply the [node-rbac.yaml](./rbac/node-rbac.yaml). Note that this step is not required for LoadBalancer services. + To expose services on each node's IP and port (the NodePort), apply the [`node-rbac.yaml`](./rbac/node-rbac.yaml). Note that this step is not required for LoadBalancer services. ```sh kubectl apply -f rbac/node-rbac.yaml @@ -153,7 +153,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer kubectl apply -f oracle-database-operator.yaml ``` - Ensure that the operator pods are up and running. For high availability, Operator pod replicas are set to a default of 3. You can scale this setting up or down. + Ensure that the operator pods are up and running. For high availability, operator pod replicas are set to a default of 3. You can scale this setting up or down. ```sh $ kubectl get pods -n oracle-database-operator-system @@ -190,7 +190,7 @@ YAML file templates are available under [`/config/samples`](./config/samples/). ## Uninstall the Operator - To uninstall the operator, the final step consists of deciding whether you want to delete the custom resource definitions (CRDs) and Kubernetes APIServices introduced into the cluster by the operator. Choose one of the following options: + To uninstall the operator, the final step consists of deciding whether you want to delete the custom resource definitions (CRDs) and Kubernetes `APIServices` introduced into the cluster by the operator. Choose one of the following options: * ### Delete the CRDs and APIServices @@ -241,7 +241,7 @@ See [Contributing to this Repository](./CONTRIBUTING.md) ## Support -You can submit a GitHub issue, oir submit an issue and then file an [Oracle Support service](https://support.oracle.com/portal/) request. To file an issue or a service request, use the following product ID: 14430. +You can submit a GitHub issue, or submit an issue and then file an [Oracle Support service](https://support.oracle.com/portal/) request. To file an issue or a service request, use the following product ID: 14430. ## Security @@ -266,5 +266,5 @@ See [Reporting security vulnerabilities](./SECURITY.md) ## License -Copyright (c) 2022, 2024 Oracle and/or its affiliates. +Copyright (c) 2022, 2025 Oracle and/or its affiliates. Released under the Universal Permissive License v1.0 as shown at [https://oss.oracle.com/licenses/upl/](https://oss.oracle.com/licenses/upl/) From 351a46e98520602b7a91dbfd7d55b93d5720fe80 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 17:34:20 +0000 Subject: [PATCH 185/414] Update README.md minor style edits --- docs/adb/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/adb/README.md b/docs/adb/README.md index ebcba6d5..14733347 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -2,13 +2,13 @@ Before you use the Oracle Database Operator for Kubernetes (the operator), ensure your system meets all of the Oracle Autonomous Database (ADB) Prerequisites [ADB_PREREQUISITES](./ADB_PREREQUISITES.md). -As indicated in the prerequisites (see above), to interact with OCI services, either the cluster has to be authorized using Principal Instance, or using the API Key Authentication by specifying the configMap and the secret under the `ociConfig` field. +As indicated in the prerequisites (see above), to interact with OCI services, either the cluster must be authorized using Principal Instance, or by using the API Key Authentication and specifying the configMap and the secret under the `ociConfig` field. ## Required Permissions -The operator must be given the required type of access in a policy written by an administrator to manage the Autonomous Databases. See [Let database and fleet admins manage Autonomous Databases](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/commonpolicies.htm#db-admins-manage-adb) for sample Autonomous Database policies. +The operator must be given the required type of access in a policy written by an administrator to manage the Autonomous Databases. See [Let database and fleet admins manage Autonomous Databases](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/commonpolicies.htm#db-admins-manage-adb) for example Autonomous Database policies. -The permission to view the workrequests is also required, so that the operator will update the resources when the work is done. See [Viewing Work Requests](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengviewingworkrequests.htm#contengviewingworkrequests) for sample work request policies. +The permission to view the work requests is also required, so that the operator will update the resources when the work is done. See [Viewing Work Requests](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengviewingworkrequests.htm#contengviewingworkrequests) for example work request policies. ## Supported Features @@ -27,7 +27,7 @@ After you create the resource, you can use the operator to perform the following * [Delete the resource](#delete-the-resource) from the cluster * [Clone](#clone-an-existing-autonomous-database) an existing Autonomous Database -To debug the Oracle Autonomous Databases with Oracle Database operator, see [Debugging and troubleshooting](#debugging-and-troubleshooting) +To debug the Oracle Autonomous Databases with Oracle Database Operator, see [Debugging and troubleshooting](#debugging-and-troubleshooting) ## Provision an Autonomous Database From c4b7ed12cdd321e617d789b98936cd1a5c337f06 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 17:39:08 +0000 Subject: [PATCH 186/414] Update README.md minor style edits --- docs/dbcs/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md index fda76d7e..76620a4d 100644 --- a/docs/dbcs/README.md +++ b/docs/dbcs/README.md @@ -40,9 +40,9 @@ For fast provisioning of single-node virtual machine database systems (using Log # Oracle DB Operator DBCS Controller Deployment -To deploy OraOperator, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. +To deploy Oracle Database Operator (`OraOperator`), use the [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. -After the Oracle Database Operator is deployed, you can see the DB operator pods running in the Kubernetes Cluster. As part of the OraOperator deployment, the DBCS Controller is deployed as a CRD (Custom Resource Definition). The following screen output is an example of such a deployment: +After the Oracle Database Operator is deployed, you can see the DB operator pods running in the Kubernetes Cluster. As part of the `OraOperator` deployment, the DBCS Controller is deployed as a CRD (Custom Resource Definition). The following screen output is an example of such a deployment: ```bash [root@test-server oracle-database-operator]# kubectl get ns NAME STATUS AGE @@ -94,7 +94,7 @@ Before you deploy a DBCS system in OCI using the Oracle DB Operator DBCS Control **CAUTION :** You must make the changes specified in this section before you proceed to the next section. -## 1. Create a Kubernetes Configmap. For example: We are creating a Kubernetes Configmap named `oci-cred` using the OCI account we are using as below: +## 1. Create a Kubernetes Configmap. In this example. we create a Kubernetes Configmap named `oci-cred` with the OCI account we are using: ```bash kubectl create configmap oci-cred \ @@ -134,7 +134,7 @@ kubectl create secret generic tde-password --from-file=./tde-password -n default ``` -## 5. Create an ssh key pair, and use its public key to create a Kubernetes secret named `oci-publickey`; the private key for this public key can be used later to access the DBCS system's host machine using ssh: +## 5. Create an SSH key pair, and use its public key to create a Kubernetes secret named `oci-publickey`; the private key for this public key can be used later to access the DBCS system's host machine using SSH: ```bash [root@test-server DBCS]# ssh-keygen -N "" -C "DBCS_System"-`date +%Y%m` -P "" From ce8eac81b3868620754eee2d505abcb84bd9f1c1 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 19:02:36 +0000 Subject: [PATCH 187/414] Update NETWORK_ACCESS_OPTIONS.md minor style changes --- docs/adb/NETWORK_ACCESS_OPTIONS.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/adb/NETWORK_ACCESS_OPTIONS.md b/docs/adb/NETWORK_ACCESS_OPTIONS.md index 66b968d0..e7eb0a56 100644 --- a/docs/adb/NETWORK_ACCESS_OPTIONS.md +++ b/docs/adb/NETWORK_ACCESS_OPTIONS.md @@ -17,16 +17,16 @@ Review the following options available to you with Autonomous Database. ## Configuring Network Access with Allowing Secure Access from Anywhere -Before changing the Network Access to Allowing Secure Access from Anywhere, ensure that your network security protocol requries only mTLS (Mutual TLS) authentication. For more details, see: [Allow both TLS and mutual TLS (mTLS) authentication](#allow-both-tls-and-mutual-tls-mtls-authentication). If mTLS enforcement is already enabled on your Autonomous Database, you can skip this step. +Before changing the Network Access to Allowing Secure Access from Anywhere, ensure that your network security protocol requries only mTLS (Mutual TLS) authentication. For more details, see: [Allow both TLS and mutual TLS (mTLS) authentication](#allow-both-tls-and-mutual-tls-mtls-authentication). If mTLS enforcement is already enabled on your Autonomous Database, then you can skip this step. -To specify that Autonomous Database can be connected from any location with a valid credential, complete one of the following procedures based on your network access configuration. +To specify that Autonomous Database can be connected from any location with a valid credential, complete one of the following procedures, based on your network access configuration. ### Option 1 - Change the Network Access from "Secure Access from Allowed IPs and VCNs Only" to "Allowing Secure Access from Anywhere" 1. Add the following parameters to the specification. An example file is availble here: [`config/samples/adb/autonomousdatabase_update_network_access.yaml`](./../../config/samples/adb/autonomousdatabase_update_network_access.yaml): | Attribute | Type | Description | |----|----|----| - | `whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for Autonomous Databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | + | `whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for Autonomous Databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL can access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, then use an array with a single empty string entry. | ```yaml --- @@ -92,7 +92,7 @@ To configure Network Access with ACLs, complete this procedure. | Attribute | Type | Description | |----|----|----| - | `whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for Autonomous Databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | + | `whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for Autonomous Databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL can access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | ```yaml --- @@ -127,14 +127,14 @@ To configure Network Access with ACLs, complete this procedure. To change the Network Access to Private Endpoint Access Only, complete this procedure -1. Visit [Overview of VCNs and Subnets](https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/managingVCNs_topic-Overview_of_VCNs_and_Subnets.htm#console) and [Network Security Groups](https://docs.oracle.com/en-us/iaas/Content/Network/Concepts/networksecuritygroups.htm#working) to see how to create VCNs, subnets, and network security groups (NSGs) if you haven't created them yet. The subnet and the NSG has to be in the same VCN. +1. Visit [Overview of VCNs and Subnets](https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/managingVCNs_topic-Overview_of_VCNs_and_Subnets.htm#console) and [Network Security Groups](https://docs.oracle.com/en-us/iaas/Content/Network/Concepts/networksecuritygroups.htm#working) to see how to create VCNs, subnets, and network security groups (NSGs) if you haven't already created them. The subnet and the NSG must be in the same VCN. 2. Copy and paste the OCIDs of the subnet and NSG to the corresponding parameters. An example file is availble here: [`config/samples/adb/autonomousdatabase_update_network_access.yaml`](./../../config/samples/adb/autonomousdatabase_update_network_access.yaml): | Attribute | Type | Description | Required? | |----|----|----|----| - | `subnetId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the subnet the resource is associated with.

**Subnet Restrictions:**
- For bare metal DB systems and for single node virtual machine DB systems, do not use a subnet that overlaps with 192.168.16.16/28.
- For Exadata and virtual machine 2-node RAC systems, do not use a subnet that overlaps with 192.168.128.0/20.
- For Autonomous Database, setting this will disable public secure access to the database.
These subnets are used by the Oracle Clusterware private interconnect on the database instance.
Specifying an overlapping subnet will cause the private interconnect to malfunction.
This restriction applies to both the client subnet and the backup subnet. | Yes | - | `nsgIds` | string[] | The list of [OCIDs](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) for the network security groups (NSGs) to which this resource belongs. Setting this to an empty list removes all resources from all NSGs. For more information about NSGs, see [Security Rule](https://docs.cloud.oracle.com/Content/Network/Concepts/securityrules.htm).

**NsgIds restrictions:**
- A network security group (NSG) is optional for Autonomous Databases with private access. The nsgIds list can be empty. | No | + | `subnetId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the subnet the resource is associated with.

**Subnet Restrictions:**
- For bare metal DB systems and for single-node virtual machine DB systems, do not use a subnet that overlaps with 192.168.16.16/28.
- For Exadata and virtual machine 2-node Oracle RAC systems, do not use a subnet that overlaps with 192.168.128.0/20.
- For Autonomous Database, setting `subnetID` disables public secure access to the database.
These subnets are used by the Oracle Clusterware private interconnect on the database instance.
Specifying an overlapping subnet will cause the private interconnect to malfunction.
This restriction applies to both the client subnet and the backup subnet. | Yes | + | `nsgIds` | string[] | The list of [OCIDs](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) for the network security groups (NSGs) to which this resource belongs. Setting `nsgIds` to an empty list removes all resources from all NSGs. For more information about NSGs, see [Security Rule](https://docs.cloud.oracle.com/Content/Network/Concepts/securityrules.htm).

**NsgIds restrictions:**
- A network security group (NSG) is optional for Autonomous Databases with private access. The nsgIds list can be empty. | No | | `privateEndpointLabel` | string | The resource's private endpoint label.
- Setting the endpoint label to a non-empty string creates a private endpoint database.
- Resetting the endpoint label to an empty string, after the creation of the private endpoint database, changes the private endpoint database to a public endpoint database.
- Setting the endpoint label to a non-empty string value, updates to a new private endpoint database, when the database is disabled and re-enabled.
This setting cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMTLSConnectionRequired, dbWorkload, dbVersion, dbName, or isFreeTier. | No | ```yaml @@ -156,6 +156,7 @@ To change the Network Access to Private Endpoint Access Only, complete this proc ``` ## Allowing TLS or Require Only Mutual TLS (mTLS) Authentication +You can choose either to require mTLS authentication and disallow TLS authentication, or allow both mTLS and TLS authentication. ### Require mutual TLS (mTLS) authentication and Disallow TLS Authentication @@ -234,12 +235,12 @@ Complete this procedure to allow both TLS and mTLS authentication. To configure the network access of Autonomous Database with access control list (ACL) on dedicated Exadata infrastructure, complete this procedure. -1. Add the following parameters to the specification. An example file is availble here: [`config/samples/adb/autonomousdatabase_update_network_access.yaml`](./../../config/samples/adb/autonomousdatabase_update_network_access.yaml): +1. Add the following parameters to the specification. An example file is available here: [`config/samples/adb/autonomousdatabase_update_network_access.yaml`](./../../config/samples/adb/autonomousdatabase_update_network_access.yaml): | Attribute | Type | Description | Required? | |----|----|----|----| - | `isAccessControlEnabled` | boolean | Indicates if the database-level access control is enabled.

If disabled, then database access is defined by the network security rules.

If enabled, then database access is restricted to the IP addresses defined by the rules specified with the `accessControlList` property. While specifying `accessControlList` rules is optional, if database-level access control is enabled, and no rules are specified, then the database will become inaccessible. The rules can be added later by using the `UpdateAutonomousDatabase` API operation, or by using the edit option in console.

When creating a database clone, you should specify the access control setting that you want the clone database to use. By default, database-level access control will be disabled for the clone.
This property is applicable only to Autonomous Databases on the Exadata Cloud@Customer platform. | Yes | - | `accessControlList` | []string | The client IP access control list (ACL). This feature is available for autonomous databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.

For shared Exadata infrastructure, this is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | Yes | + | `isAccessControlEnabled` | boolean | Indicates if the database-level access control is enabled.

If disabled, then database access is defined by the network security rules.

If enabled, then database access is restricted to the IP addresses defined by the rules specified with the `accessControlList` property. While specifying `accessControlList` rules is optional, if database-level access control is enabled, and no rules are specified, then the database will become inaccessible. The rules can be added later by using the `UpdateAutonomousDatabase` API operation, or by using the edit option in console.

When creating a database clone, you should specify the access control setting that you want the clone database to use. By default, database-level access control is disabled for the clone.
This property is applicable only to Autonomous Databases on the Exadata Cloud@Customer platform. | Yes | + | `accessControlList` | []string | The client IP access control list (ACL). This feature is available for Autonomous Databases on [shared Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adboverview.htm#AEI) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL can access the Autonomous Database instance.

For shared Exadata infrastructure, the access control list is an array of CIDR (Classless Inter-Domain Routing) notations for a subnet or VCN OCID.
Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`

For Exadata Cloud@Customer, this is an array of IP addresses or CIDR (Classless Inter-Domain Routing) notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`

For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry. | Yes | ```yaml --- From ca4b3677a618029c03ae070a47a2c9a1ac81b8aa Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Wed, 12 Feb 2025 19:03:22 +0000 Subject: [PATCH 188/414] Bugfix/observability/37582342 --- .../v1/databaseobserver_types.go | 16 +- .../observability/v1/zz_generated.deepcopy.go | 10 + .../v1alpha1/databaseobserver_types.go | 16 +- .../v1alpha1/zz_generated.deepcopy.go | 10 + .../v4/databaseobserver_types.go | 16 +- .../observability/v4/zz_generated.deepcopy.go | 10 + commons/observability/utils.go | 16 + ...vability.oracle.com_databaseobservers.yaml | 447 ++++++++++++++++++ .../databaseobserver_resource.go | 12 +- docs/observability/README.md | 100 ++-- 10 files changed, 592 insertions(+), 61 deletions(-) diff --git a/apis/observability/v1/databaseobserver_types.go b/apis/observability/v1/databaseobserver_types.go index d20b6417..642ff18b 100644 --- a/apis/observability/v1/databaseobserver_types.go +++ b/apis/observability/v1/databaseobserver_types.go @@ -92,17 +92,19 @@ type DatabaseObserverExporterConfig struct { // DatabaseObserverDeployment defines the exporter deployment component of DatabaseObserver type DatabaseObserverDeployment struct { - ExporterImage string `json:"image,omitempty"` - ExporterArgs []string `json:"args,omitempty"` - ExporterCommands []string `json:"commands,omitempty"` - ExporterEnvs map[string]string `json:"env,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` + ExporterImage string `json:"image,omitempty"` + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + ExporterArgs []string `json:"args,omitempty"` + ExporterCommands []string `json:"commands,omitempty"` + ExporterEnvs map[string]string `json:"env,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` } // DeploymentPodTemplate defines the labels for the DatabaseObserver pods component of a deployment type DeploymentPodTemplate struct { - Labels map[string]string `json:"labels,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } // DatabaseObserverService defines the exporter service component of DatabaseObserver diff --git a/apis/observability/v1/zz_generated.deepcopy.go b/apis/observability/v1/zz_generated.deepcopy.go index ac8c88ce..4924216f 100644 --- a/apis/observability/v1/zz_generated.deepcopy.go +++ b/apis/observability/v1/zz_generated.deepcopy.go @@ -159,6 +159,11 @@ func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatabaseObserverDeployment) DeepCopyInto(out *DatabaseObserverDeployment) { *out = *in + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.SecurityContext) + (*in).DeepCopyInto(*out) + } if in.ExporterArgs != nil { in, out := &in.ExporterArgs, &out.ExporterArgs *out = make([]string, len(*in)) @@ -339,6 +344,11 @@ func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { *out = *in + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) diff --git a/apis/observability/v1alpha1/databaseobserver_types.go b/apis/observability/v1alpha1/databaseobserver_types.go index 447cdbf2..f4c62900 100644 --- a/apis/observability/v1alpha1/databaseobserver_types.go +++ b/apis/observability/v1alpha1/databaseobserver_types.go @@ -92,17 +92,19 @@ type DatabaseObserverExporterConfig struct { // DatabaseObserverDeployment defines the exporter deployment component of DatabaseObserver type DatabaseObserverDeployment struct { - ExporterImage string `json:"image,omitempty"` - ExporterArgs []string `json:"args,omitempty"` - ExporterCommands []string `json:"commands,omitempty"` - ExporterEnvs map[string]string `json:"env,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` + ExporterImage string `json:"image,omitempty"` + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + ExporterArgs []string `json:"args,omitempty"` + ExporterCommands []string `json:"commands,omitempty"` + ExporterEnvs map[string]string `json:"env,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` } // DeploymentPodTemplate defines the labels for the DatabaseObserver pods component of a deployment type DeploymentPodTemplate struct { - Labels map[string]string `json:"labels,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } // DatabaseObserverService defines the exporter service component of DatabaseObserver diff --git a/apis/observability/v1alpha1/zz_generated.deepcopy.go b/apis/observability/v1alpha1/zz_generated.deepcopy.go index daca675c..4b2a29b0 100644 --- a/apis/observability/v1alpha1/zz_generated.deepcopy.go +++ b/apis/observability/v1alpha1/zz_generated.deepcopy.go @@ -159,6 +159,11 @@ func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatabaseObserverDeployment) DeepCopyInto(out *DatabaseObserverDeployment) { *out = *in + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.SecurityContext) + (*in).DeepCopyInto(*out) + } if in.ExporterArgs != nil { in, out := &in.ExporterArgs, &out.ExporterArgs *out = make([]string, len(*in)) @@ -339,6 +344,11 @@ func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { *out = *in + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) diff --git a/apis/observability/v4/databaseobserver_types.go b/apis/observability/v4/databaseobserver_types.go index 246be339..2b9df606 100644 --- a/apis/observability/v4/databaseobserver_types.go +++ b/apis/observability/v4/databaseobserver_types.go @@ -92,17 +92,19 @@ type DatabaseObserverExporterConfig struct { // DatabaseObserverDeployment defines the exporter deployment component of DatabaseObserver type DatabaseObserverDeployment struct { - ExporterImage string `json:"image,omitempty"` - ExporterArgs []string `json:"args,omitempty"` - ExporterCommands []string `json:"commands,omitempty"` - ExporterEnvs map[string]string `json:"env,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` + ExporterImage string `json:"image,omitempty"` + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + ExporterArgs []string `json:"args,omitempty"` + ExporterCommands []string `json:"commands,omitempty"` + ExporterEnvs map[string]string `json:"env,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` } // DeploymentPodTemplate defines the labels for the DatabaseObserver pods component of a deployment type DeploymentPodTemplate struct { - Labels map[string]string `json:"labels,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } // DatabaseObserverService defines the exporter service component of DatabaseObserver diff --git a/apis/observability/v4/zz_generated.deepcopy.go b/apis/observability/v4/zz_generated.deepcopy.go index fbfba0ef..d9892643 100644 --- a/apis/observability/v4/zz_generated.deepcopy.go +++ b/apis/observability/v4/zz_generated.deepcopy.go @@ -159,6 +159,11 @@ func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatabaseObserverDeployment) DeepCopyInto(out *DatabaseObserverDeployment) { *out = *in + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.SecurityContext) + (*in).DeepCopyInto(*out) + } if in.ExporterArgs != nil { in, out := &in.ExporterArgs, &out.ExporterArgs *out = make([]string, len(*in)) @@ -339,6 +344,11 @@ func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { *out = *in + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) diff --git a/commons/observability/utils.go b/commons/observability/utils.go index bd4ea0b5..8aa67906 100644 --- a/commons/observability/utils.go +++ b/commons/observability/utils.go @@ -86,6 +86,22 @@ func GetExporterArgs(a *api.DatabaseObserver) []string { return nil } +// GetExporterDeploymentSecurityContext retrieves security context for container +func GetExporterDeploymentSecurityContext(a *api.DatabaseObserver) *corev1.SecurityContext { + if sc := a.Spec.Exporter.Deployment.SecurityContext; sc != nil { + return sc + } + return &corev1.SecurityContext{} +} + +// GetExporterPodSecurityContext retrieves security context for pods +func GetExporterPodSecurityContext(a *api.DatabaseObserver) *corev1.PodSecurityContext { + if sc := a.Spec.Exporter.Deployment.DeploymentPodTemplate.SecurityContext; sc != nil { + return sc + } + return &corev1.PodSecurityContext{} +} + // GetExporterCommands retrieves commands func GetExporterCommands(a *api.DatabaseObserver) []string { if c := a.Spec.Exporter.Deployment.ExporterCommands; c != nil || len(c) > 0 { diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index 7460f76f..298f9d4e 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -112,6 +112,155 @@ spec: additionalProperties: type: string type: object + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object type: object service: @@ -2286,6 +2435,155 @@ spec: additionalProperties: type: string type: object + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object type: object service: @@ -4460,6 +4758,155 @@ spec: additionalProperties: type: string type: object + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object type: object service: diff --git a/controllers/observability/databaseobserver_resource.go b/controllers/observability/databaseobserver_resource.go index b58cbf57..6be6f693 100644 --- a/controllers/observability/databaseobserver_resource.go +++ b/controllers/observability/databaseobserver_resource.go @@ -44,6 +44,9 @@ func (resource *ObservabilityDeploymentResource) generate(a *api.DatabaseObserve rPodLabels := constants.GetLabels(a, a.Spec.Exporter.Deployment.DeploymentPodTemplate.Labels) rSelector := constants.GetSelectorLabel(a) + rDeploymentSecurityContext := constants.GetExporterDeploymentSecurityContext(a) + rPodSecurityContext := constants.GetExporterPodSecurityContext(a) + rPort := []corev1.ContainerPort{ {ContainerPort: constants.DefaultAppPort}, } @@ -59,7 +62,9 @@ func (resource *ObservabilityDeploymentResource) generate(a *api.DatabaseObserve Ports: rPort, Args: rArgs, Command: rCommands, + SecurityContext: rDeploymentSecurityContext, } + constants.AddSidecarContainers(a, &rContainers) constants.AddSidecarVolumes(a, &rVolumes) @@ -81,9 +86,10 @@ func (resource *ObservabilityDeploymentResource) generate(a *api.DatabaseObserve Labels: rPodLabels, }, Spec: corev1.PodSpec{ - Containers: rContainers, - RestartPolicy: corev1.RestartPolicyAlways, - Volumes: rVolumes, + Containers: rContainers, + RestartPolicy: corev1.RestartPolicyAlways, + Volumes: rVolumes, + SecurityContext: rPodSecurityContext, }, }, }, diff --git a/docs/observability/README.md b/docs/observability/README.md index 895b8f85..bcb5aec4 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -55,40 +55,42 @@ In the sample YAML file found in [./config/samples/observability/v4/databaseobserver.yaml](../../config/samples/observability/v4/databaseobserver.yaml), the databaseObserver custom resource offers the following properties to be configured: -| Attribute | Type | Default | Required? | Example | -|----------------------------------------------------|--------|---------------------------------------------------------------------|:------------|-----------------------------------------------------------------------| -| `spec.database.dbUser.key` | string | user | Optional | _username_ | -| `spec.database.dbUser.secret` | string | - | Yes | _db-secret_ | -| `spec.database.dbPassword.key` | string | password | Optional | _admin-password_ | -| `spec.database.dbPassword.secret` | string | - | Conditional | _db-secret_ | -| `spec.database.dbPassword.vaultOCID` | string | - | Conditional | _ocid1.vault.oc1..._ | -| `spec.database.dbPassword.vaultSecretName` | string | - | Conditional | _db-vault_ | -| `spec.database.dbWallet.secret` | string | - | Conditional | _devsec-oradevdb-wallet_ | -| `spec.database.dbConnectionString.key` | string | connection | Optional | _connection_ | -| `spec.database.dbConnectionString.secret` | string | - | Yes | _db-secretg_ | -| `spec.sidecars` | array | - | Optional | - | -| `spec.sidecarVolumes` | array | - | Optional | - | -| `spec.exporter.deployment.env` | map | - | Optional | _DB_ROLE: "SYSDBA"_ | -| `spec.exporter.deployment.image` | string | container-registry.oracle.com/database/observability-exporter:1.5.1 | Optional | _container-registry.oracle.com/database/observability-exporter:1.3.0_ | -| `spec.exporter.deployment.args` | array | - | Optional | _[ "--log.level=info" ]_ | -| `spec.exporter.deployment.commands` | array | - | Optional | _[ "/oracledb_exporter" ]_ | -| `spec.exporter.deployment.labels` | map | - | Optional | _environment: dev_ | -| `spec.exporter.deployment.podTemplate.labels` | map | - | Optional | _environment: dev_ | -| `spec.exporter.service.ports` | array | - | Optional | - | -| `spec.exporter.service.labels` | map | - | Optional | _environment: dev_ | | -| `spec.configuration.configMap.key` | string | config.toml | Optional | _config.toml_ | -| `spec.configuration.configMap.name` | string | - | Optional | _devcm-oradevdb-config_ | -| `spec.prometheus.serviceMonitor.labels` | map | - | Yes | _release: prometheus_ | -| `spec.prometheus.serviceMonitor.namespaceSelector` | - | - | Yes | - | -| `spec.prometheus.serviceMonitor.endpoints` | array | - | Optional | - | -| `spec.log.filename` | string | alert.log | Optional | _alert.log_ | -| `spec.log.path` | string | /log | Optional | _/log_ | -| `spec.log.volume.name` | string | log-volume | Optional | _my-persistent-volume_ | -| `spec.log.volume.persistentVolumeClaim.claimName` | string | - | Optional | _my-pvc_ | -| `spec.replicas` | number | 1 | Optional | _1_ | -| `spec.inheritLabels` | array | - | Optional | _- environment: dev_
- app.kubernetes.io/name: observer | -| `spec.ociConfig.configMapName` | string | - | Conditional | _oci-cred_ | -| `spec.ociConfig.secretName` | string | - | Conditional | _oci-privatekey_ | +| Attribute | Type | Default | Required? | Example | +|--------------------------------------------------------|--------|---------------------------------------------------------------------|:------------|-----------------------------------------------------------------------| +| `spec.database.dbUser.key` | string | user | Optional | _username_ | +| `spec.database.dbUser.secret` | string | - | Yes | _db-secret_ | +| `spec.database.dbPassword.key` | string | password | Optional | _admin-password_ | +| `spec.database.dbPassword.secret` | string | - | Conditional | _db-secret_ | +| `spec.database.dbPassword.vaultOCID` | string | - | Conditional | _ocid1.vault.oc1..._ | +| `spec.database.dbPassword.vaultSecretName` | string | - | Conditional | _db-vault_ | +| `spec.database.dbWallet.secret` | string | - | Conditional | _devsec-oradevdb-wallet_ | +| `spec.database.dbConnectionString.key` | string | connection | Optional | _connection_ | +| `spec.database.dbConnectionString.secret` | string | - | Yes | _db-secretg_ | +| `spec.sidecars` | array | - | Optional | - | +| `spec.sidecarVolumes` | array | - | Optional | - | +| `spec.exporter.deployment.securityContext` | object | | Optional | _ | +| `spec.exporter.deployment.env` | map | - | Optional | _DB_ROLE: "SYSDBA"_ | +| `spec.exporter.deployment.image` | string | container-registry.oracle.com/database/observability-exporter:1.5.1 | Optional | _container-registry.oracle.com/database/observability-exporter:1.3.0_ | +| `spec.exporter.deployment.args` | array | - | Optional | _[ "--log.level=info" ]_ | +| `spec.exporter.deployment.commands` | array | - | Optional | _[ "/oracledb_exporter" ]_ | +| `spec.exporter.deployment.labels` | map | - | Optional | _environment: dev_ | +| `spec.exporter.deployment.podTemplate.labels` | map | - | Optional | _environment: dev_ | +| `spec.exporter.deployment.podTemplate.securityContext` | object | - | Optional | _ | +| `spec.exporter.service.ports` | array | - | Optional | - | +| `spec.exporter.service.labels` | map | - | Optional | _environment: dev_ | | +| `spec.configuration.configMap.key` | string | config.toml | Optional | _config.toml_ | +| `spec.configuration.configMap.name` | string | - | Optional | _devcm-oradevdb-config_ | +| `spec.prometheus.serviceMonitor.labels` | map | - | Yes | _release: prometheus_ | +| `spec.prometheus.serviceMonitor.namespaceSelector` | - | - | Yes | - | +| `spec.prometheus.serviceMonitor.endpoints` | array | - | Optional | - | +| `spec.log.filename` | string | alert.log | Optional | _alert.log_ | +| `spec.log.path` | string | /log | Optional | _/log_ | +| `spec.log.volume.name` | string | log-volume | Optional | _my-persistent-volume_ | +| `spec.log.volume.persistentVolumeClaim.claimName` | string | - | Optional | _my-pvc_ | +| `spec.replicas` | number | 1 | Optional | _1_ | +| `spec.inheritLabels` | array | - | Optional | _- environment: dev_
- app.kubernetes.io/name: observer | +| `spec.ociConfig.configMapName` | string | - | Conditional | _oci-cred_ | +| `spec.ociConfig.secretName` | string | - | Conditional | _oci-privatekey_ | ### Configuration Options @@ -125,8 +127,10 @@ The `databaseObserver` resource provides the remaining multiple fields that are * `spec.exporter.deployment.env` - custom environment variables for the observability exporter * `spec.exporter.deployment.labels` - custom labels to add to deployment labels * `spec.exporter.deployment.podTemplate.labels` - custom labels to add to pod labels +* `spec.exporter.deployment.podTemplate.securityContext` - configures pod securityContext * `spec.exporter.deployment.args` - additional arguments to provide the observability-exporter * `spec.exporter.deployment.commands` - commands to supply to the observability-exporter +* `spec.exporter.deployment.securityContext` - configures container securityContext * `spec.inheritLabels` - keys of inherited labels from the databaseObserver resource. These labels are applied to generated resources. ### Resources Managed by the Controller @@ -354,6 +358,28 @@ If `spec.log.volume.persistentVolumeClaim.claimName` is not specified, an `Empty claimName: "my-pvc" ``` +If security context, which defines privilege and access control settings for a pod container, need to be updated in the pod, +the same field is available on the DatabaseObserver spec. You can set this object under deployment: `spec.exporter.deployment.securityContext`. + +```yaml +spec: + exporter: + deployment: + securityContext: + supplementalGroups: [1000] +``` + +Configuring security context under the PodTemplate is also possible. You can set this object under: `spec.exporter.deployment.podTemplate.securityContext` + +```yaml +spec: + exporter: + deployment: + podTemplate: + securityContext: + supplementalGroups: [1000] +``` + ### Working with Sidecars, to deploy Promtail The fields `spec.sidecars` and `spec.sidecarVolumes` provide the ability to deploy container images as a sidecar container @@ -460,9 +486,9 @@ spec: labels: service: labels: - prometheus: - serviceMonitor: - labels: + prometheus: + serviceMonitor: + labels: ``` ### Custom Exporter Image or Version From d9ca3df9bf271ea9d090824f34b52c580b080b60 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 20:56:34 +0000 Subject: [PATCH 189/414] Update README.md substantial style edits --- docs/multitenant/lrest-based/README.md | 188 +++++++++++++------------ 1 file changed, 95 insertions(+), 93 deletions(-) diff --git a/docs/multitenant/lrest-based/README.md b/docs/multitenant/lrest-based/README.md index 8d093db7..f17abd41 100644 --- a/docs/multitenant/lrest-based/README.md +++ b/docs/multitenant/lrest-based/README.md @@ -31,30 +31,31 @@ -**Lrpdb** and **lrest** are two controllers for the PDBs life cycle management (**PDBLCM**), they relie on a dedicated rest server (Lite Rest Server). -Container image to run lrest controller is available on OCR. The container database can be anywhere (on prem and in cloud). +**Lrpdb** and **lrest** are two controllers for PDB lifecycle management (**PDBLCM**). They rely on a dedicated REST server (Lite Rest Server) Container image to run. The `lrest` controller is available on the Oracle Container Registry (OCR). The container database can be anywhere (on-premises or in the Cloud). ![generaleschema](./images/Generalschema2.jpg) ## STEP BY STEP CONFIGURATION +Complete each of these steps in the order given. ### Multiple namespace setup -Before proceeding with controllers setup make sure that the operator is configured to work with multiple namespaces as specified in the [README](../../../README.md). -In this document each controller is running in a dedicated namespace: lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains all the files reported in this document. +Before proceeding with controllers setup, ensure that the Oracle Database Operator (operator) is configured to work with multiple namespaces, as specified in the [README](../../../README.md). +In this document, each controller is running in a dedicated namespace: lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains all the files reported in this document. -Configure the **WACTH_NAMESPACE** list of the operator yaml file +Configure the **WACTH_NAMESPACE** list of the operator `yaml` file ```bash sed -i 's/value: ""/value: "oracle-database-operator-system,pdbnamespace,cdbnamespace"/g' oracle-database-operator.yaml ``` ### Create the operator +Run the following command: ```bash kubectl apply -f oracle-database-operator.yaml ``` -Check controller +Check the controller: ```bash kubectl get pods -n oracle-database-operator-system NAME READY STATUS RESTARTS AGE @@ -64,7 +65,7 @@ oracle-database-operator-controller-manager-796c9b87df-t4qns 1/1 Running ``` ### Container database setup -On the container database configure the following account for pdb administration +On the container database, use the following commands to configure the account for PDB administration: ```sql alter session set "_oracle_script"=true; @@ -77,26 +78,29 @@ grant sysdba to container=all; ### Apply rolebinding -Apply the following files : [pdbnamespace_binding.yaml](./usecase/pdbnamespace_binding.yaml) [cdbnamespace_binding.yaml](./usecase/cdbnamespace_binding.yaml) +Apply the following files : [`pdbnamespace_binding.yaml`](./usecase/pdbnamespace_binding.yaml) [`cdbnamespace_binding.yaml`](./usecase/cdbnamespace_binding.yaml) ```bash kubectl apply -f pdbnamespace_binding.yaml kubectl apply -f cdbnamespace_binding.yaml ``` ### Certificate and credentials +You must create the public key, private key, certificates and Kubernetes Secrets for the security configuration. #### Private key 🔑 -> Before starting keys and certificate generation note that only private key **PCKS8** format is supported by LREST controllers. If you are using [openssl3](https://docs.openssl.org/master/) then pcks8 is generated by default, otherwise use the following command to create a pcks8 private key +> Note: Only private key **PCKS8** format is supported by LREST controllers. Before you start configuration, ensure that you can use it. If you are using [`openssl3`](https://docs.openssl.org/master/) then `pcks8` is generated by default. If it is not already generated, then use the following command to create a `pcks8` private key ```bash openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out private.key ``` #### Public Key 🔑 +Create the public key. ```bash /usr/bin/openssl rsa -in private.key -outform PEM -pubout -out public.pem ``` #### Certificates +Create certificates. ```bash openssl req -new -x509 -days 365 -key private.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out ca.crt ``` @@ -111,6 +115,7 @@ openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=CN/ST=GD/L=SZ/O=or ``` ### Create secrets for certificate and keys +Create the Kubernetes Secrets. ```bash kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system @@ -129,7 +134,7 @@ kubectl create secret generic prvkey --from-file=privateKey="private.key" -n pdb ### Create secrets with encrypted password -We create secrets for each credential (username and password) +In this example, we create the Secrets for each credential (username and password) | secret usr | secrets pwd | credential description | | -----------|-------------|-----------------------------------------------------------| @@ -169,9 +174,9 @@ rm dbuser.txt dbpass.txt wbuser.txt wbpass.txt pdbusr.txt pdbpwd.txt \ ### Create lrest pod -Use yaml file [create_lrest_pod.yaml](./usecase/create_lrest_pod.yaml) to create the rest pod and monitor the execution. +To create the REST pod and monitor its processing, use the `yaml` file [`create_lrest_pod.yaml`](./usecase/create_lrest_pod.yaml) -Be sure to update the **lrestImage** with latest version available on the [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:4:104288359787984:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1283,1283,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,1,0&cs=3076h-hg1qX3eJANBcUHBNBCmYWjMvxLkZyTAhDn2e8VR8Gxb_a-I8jZLhf9j6gmnimHwlP_a0OQjX6vjBfSAqQ) +Ensure that you update the **lrestImage** with the latest version available on the [Oracle Container Registry (OCR)](https://container-registry.oracle.com/ords/f?p=113:4:104288359787984:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1283,1283,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,1,0&cs=3076h-hg1qX3eJANBcUHBNBCmYWjMvxLkZyTAhDn2e8VR8Gxb_a-I8jZLhf9j6gmnimHwlP_a0OQjX6vjBfSAqQ) ```bash --> for amd64 @@ -185,7 +190,7 @@ lrestImage: container-registry.oracle.com/database/operator:lrest-241210-arm64 kubectl apply -f create_lrest_pod.yaml ``` -monitor the execution: +monitor the file processing: ```bash kubectl get pods -n cdbnamespace --watch @@ -200,7 +205,7 @@ NAME CDB NAME DB SERVER DB PORT TNS STRING REPLICAS STATUS MESSAGE cdb-dev DB12 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) 1 Ready ``` -Check pod logs: +Check the Pod logs: ```bash /usr/local/go/bin/kubectl logs -f `/usr/local/go/bin/kubectl get pods -n cdbnamespace|grep lrest|cut -d ' ' -f 1` -n cdbnamespace @@ -265,18 +270,18 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 ``` -**lrest pod creation** - parameters list +**lrest Pod creation** - parameters list | Name | Dcription | --------------------------|-------------------------------------------------------------------------------| -|cdbName | Name of the container db | +|cdbName | Name of the container database (db) | |lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** | -|dbTnsurl | Tns alias of the container db | -|deletePdbCascade | Delete all the pdbs associated to cdb resource when cdb resource it is dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | -|cdbAdminUser | Secret: the admin user | +|dbTnsurl | TNS alias of the container db | +|deletePdbCascade | Delete all of the PDBs associated to a CDB resource when the CDB resource is dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | +|cdbAdminUser | Secret: the administrative (admin) user | |cdbAdminPwd | Secret: the admin user password | -|webServerUser | Secret: the https user | -|webServerPwd | Secret: the https user password | -|cdbTlsCrt | Secret: the tls.crt | +|webServerUser | Secret: the HTTPS user | +|webServerPwd | Secret: the HTTPS user password | +|cdbTlsCrt | Secret: the `tls.crt ` | |cdbPubKey | Secret: the public key | |cdbPrvKey | Secret: the private key | @@ -285,12 +290,12 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 ### Create PDB -Apply yaml file [create_lrpdb1_resource.yaml](./usecase/clone_lrpdb1_resource.yaml) to create a pluggable database +To create a pluggable database (PDB), apply the yaml file [`create_lrpdb1_resource.yaml`](./usecase/clone_lrpdb1_resource.yaml) ```bash kubectl apply -f create_lrpdb1_resource.yaml ``` -Check the status of the resource and the pdb existence on the container db +Check the status of the resource and the PDB existence on the container db: ```bash kubectl get lrpdb -n pdbnamespace @@ -307,38 +312,38 @@ SQL> show pdbs 3 PDBDEV MOUNTED SQL> ``` -Note that after creation the pdb is not open you need to explicitly open it using dedicated yaml file. +``Note that after creation, the PDB is not open. You must explicitly open it using a dedicated `yaml` file. **pdb creation** - parameters list | Name | Dcription | |-------------------------|-------------------------------------------------------------------------------| -|cdbResName | Rest server resource name | -|cdbNamespace | Namespace of the rest server | +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | |cdbName | Name of the container database | -|pdbName | Name of the pluggable database to be created | -|assertiveLrpdbDeletion | Boolean: turn on the imperative approach on pdb resource deletion | -|adminpdbUser | Secret: pluggable database administrative user | -|adminpdbPass | Secret: password of pluggable database admin user | -|lrpdbTlsKey | Secret: tls.key | -|lrpdbTlsCrt | Secret: tls.crt | -|lrpdbTlsCat | Secret: ca.crt | -|webServerUser | Secret: the https user | -|webServerPwd | Secret: the https user password | +|pdbName | Name of the PDB that you want to create | +|assertiveLrpdbDeletion | Boolean: Turn on the imperative approach on PDB resource deletion | +|adminpdbUser | Secret: PDB admin user | +|adminpdbPass | Secret: password of PDB admin user | +|lrpdbTlsKey | Secret: `tls.key ` | +|lrpdbTlsCrt | Secret: `tls.crt` | +|lrpdbTlsCat | Secret: `ca.crt` | +|webServerUser | Secret: the HTTPS user | +|webServerPwd | Secret: the HTTPS user password | |cdbPrvKey | Secret: private key | |cdbPubKey | Secret: public key | -|pdbconfigmap | kubernet config map which contains pdb init parameters | +|pdbconfigmap | kubernetes config map that contains the PDB initialization (init) parameters | -> NOTE: **assertiveLrpdbDeletion** needs to be specified for the following pdb actions **CLONE** **CREATE** **PLUG** **MAP**. +> NOTE: **assertiveLrpdbDeletion** must be specified for the following PDB actions **CLONE** **CREATE** **PLUG** **MAP**. 🔥 **assertiveLrpdbDeletion** drops pluggable database using **INCLUDE DATAFILES** option -All these parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** need to be specified in all pdb life cycle management yaml files; for the sake of simplicity, they won't be exposed in the subsequent tables. +All of the parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** must be specified in all PDB lifecycle management `yaml` files. To simplify presentation of requirements, we will not include them in the subsequent tables. #### pdb config map -Using **pdbconfigmap** it's possible to specify a kubernetes configmap with init pdb parameters. The config map payload has the following scaffold: +By using **pdbconfigmap** it is possible to specify a kubernetes `configmap` with init PDB parameters. The config map payload has the following format: ``` @@ -350,7 +355,7 @@ Using **pdbconfigmap** it's possible to specify a kubernetes configmap with ini ;; ``` -example of configmap creation: +Example of `configmap` creation: ```bash cat < parameters.txt @@ -377,16 +382,16 @@ db_file_multiblock_read_count;16;spfile test_invalid_parameter;16;spfile ``` -- Configmap, if specified, is applied during pdb **cloning**, **opening** and **plugging** -- Configmap is not monitored by the reconciliation loop; this feature will be available in feature releases. This means that if someone decides to manually alter an init parameter the operator does not take any actions to syncronize pdb configuration with config map. +- If specified, the `configmap` is applied during PDB **cloning**, **opening** and **plugging** +- The `configmap` is not monitored by the reconciliation loop; this feature will be available in future releases. This means that if someone decides to manually alter an init parameter, then the operator does not take any actions to syncronize PDB configuration with the `configmap`. - **Alter system parameter feature** will be available in future releases. -- A ConfigMap application error (whatever reason) does not stop the execution of the process. A warning with sqlcode is reported in the logfile. +- An application error with the `configmap` (for whatever reason) does not stop processes from completing. A warning with the associated SQL code is reported in the log file. ### Open PDB -Use file [open_lrpdb1_resource.yaml](./usecase/open_lrpdb1_resource.yaml) to open pdb. +To open the PDB, use the file [`open_lrpdb1_resource.yaml`](./usecase/open_lrpdb1_resource.yaml): ```bash kubectl apply -f open_lrpdb1_resource.yaml @@ -394,104 +399,101 @@ kubectl apply -f open_lrpdb1_resource.yaml **pdb opening** - parameters list -| Name | Dcription/Value | +| Name | Description/Value | |-------------------------|-------------------------------------------------------------------------------| -|cdbResName | Rest server resource name | -|cdbNamespace | Namespace of the rest server | -|cdbName | Name of the container database | -|pdbName | Name of the pluggable database to be created | -|action | Use **Modify** to open PDB | -|pdbState | Use **OPEN** to open PDB | -|modifyOption | Use **READ WRITE** to open PDB | +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | +|pdbName | Name of the pluggable database (PDB) that you are creating | +|action | Use **Modify** to open the PDB | +|pdbState | Use **OPEN** to open the PDB | +|modifyOption | Use **READ WRITE** to open the PDB | ### Close PDB -Use file [close_lrpdb1_resource.yaml](./usecase/close_lrpdb1_resource.yaml) to close pdb. +To close the PDB, use the file [`close_lrpdb1_resource.yaml`](./usecase/close_lrpdb1_resource.yaml): ```bash kubectl apply -f close_lrpdb1_resource.yaml ``` **pdb closing** - parameters list -| Name | Dcription/Value | +| Name | Description/Value | |-------------------------|-------------------------------------------------------------------------------| -|cdbResName | Rest server resource name | -|cdbNamespace | Namespace of the rest server | -|cdbName | Name of the container database | -|pdbName | Name of the pluggable database to be created | -|action | Use **Modify** to close PDB | -|pdbState | Use **CLOSE** to close PDB | -|modifyOption | Use **IMMEDIATE** to close PDB | +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | +|pdbName | Name of the pluggable database (PDB) that you want to create | +|action | Use **Modify** to close the PDB | +|pdbState | Use **CLOSE** to close the PDB | +|modifyOption | Use **IMMEDIATE** to close the PDB | ### Clone PDB ### -Use file [clone_lrpdb1_resource.yaml](./usecase/clone_lrpdb1_resource.yaml) to clone pdb. +To clone the PDB, use the file [`clone_lrpdb1_resource.yaml`](./usecase/clone_lrpdb1_resource.yaml): ```bash kubeclt apply -f clone_lrpdb1_resource.yaml ``` **pdb cloning** - parameters list -| Name | Dcription/Value | +| Name | Description/Value | |-------------------------|-------------------------------------------------------------------------------| -|cdbResName | Rest server resource name | -|cdbNamespace | Namespace of the rest server | -|cdbName | Name of the container database | -|pdbName | The name of the new pdb | -|srcPdbName | The name of source pdb | +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | +|pdbName | The name of the new pluggable database (PDB) | +|srcPdbName | The name of the source PDB | |fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | |totalSize | Set **unlimited** for cloning | |tempSize | Set **unlimited** for cloning | -|pdbconfigmap | kubernet config map which contains pdb init parameters | -|action | Use **clone** to clone PDB | +|pdbconfigmap | kubernetes `configmap` which contains the PDB init parameters | +|action | Use **clone** to clone the PDB | ### Unplug PDB -Use file [unplug_lrpdb1_resource.yaml](./usecase/unplug_lrpdb1_resource.yaml) to unplug PDB +To unplug the PDB, use the file [`unplug_lrpdb1_resource.yaml`](./usecase/unplug_lrpdb1_resource.yaml): **pdb unplugging** -| Name | Dcription/Value | +| Name | Description/Value | |-------------------------|-------------------------------------------------------------------------------| -|cdbResName | Rest server resource name | -|cdbNamespace | Namespace of the rest server | -|cdbName | Name of the container database | -|pdbName | The name of the pdb | -|**xmlFileName** | Path of the xml file | -|action | **Unplug** | - +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | +|pdbName | Name of the pluggable database (PDB)| ### Plug PDB -Use file [plug_lrpdb1_resource.yaml](./usecase/plug_lrpdb1_resource.yaml) to plug PDB (in this example we plug the pdb unpluged in the previous step) +To plug in the PDB, use the file [`plug_lrpdb1_resource.yaml`](./usecase/plug_lrpdb1_resource.yaml). In this example, we plug in the PDB that was unpluged in the previous step: **pdb plugging** -| Name | Dcription/Value | +| Name | Description/Value | |-------------------------|-------------------------------------------------------------------------------| -|cdbResName | Rest server resource name | -|cdbNamespace | Namespace of the rest server | -|cdbName | Name of the container database | -|pdbName | The name of the pdb | -|**xmlFileName** | Path of the xml file | +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB)| | +|pdbName | Name of the pluggable database (PDB) | +|**xmlFileName** | Path of the XML file | |action | **plug** | |fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | |sourceFileNameConversion | See parameter [SOURCE_FILE_NAME_CONVERT](https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/CREATE-PLUGGABLE-DATABASE.html#GUID-F2DBA8DD-EEA8-4BB7-A07F-78DC04DB1FFC__CCHEJFID) documentation | -|pdbconfigmap | kubernet config map which contains pdb init parameters | +|pdbconfigmap | Kubernetes `configmap` that contains the PDB init parameters | ### Delete PDB -Use file [delete_lrpdb1_resource.yaml](./usecase/delete_lrpdb1_resource.yaml) +To delete the PDB, use the file [`delete_lrpdb1_resource.yaml`](./usecase/delete_lrpdb1_resource.yaml) **pdb deletion** | Name | Dcription/Value | |-------------------------|-------------------------------------------------------------------------------| -|cdbResName | Rest server resource name | -|cdbNamespace | Namespace of the rest server | -|cdbName | Name of the container database | +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | |action | **Delete** | |dropAction | **INCLUDING** - Including datafiles or **NONE** | ### Map PDB -If you need to create a CRD for an existing PDB you can use the map option by applying the file [map_lrpdb1_resource.yaml](./usecase/map_lrpdb1_resource.yaml) +If you need to create a CRD for an existing PDB, then you can use the map option by applying the file [`map_lrpdb1_resource.yaml`](./usecase/map_lrpdb1_resource.yaml) From 24674144041326693b3b1eb84df36a76bc8f8734 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 21:07:41 +0000 Subject: [PATCH 190/414] Update README.md minor style/spelling edits. --- .../multitenant/lrest-based/usecase/README.md | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/multitenant/lrest-based/usecase/README.md b/docs/multitenant/lrest-based/usecase/README.md index ae9979b7..98897d7b 100644 --- a/docs/multitenant/lrest-based/usecase/README.md +++ b/docs/multitenant/lrest-based/usecase/README.md @@ -3,11 +3,11 @@ # Use case directory -The use case directory contains the yaml files to test the multitenant controller functionalities: create lrest pod and pdb operation *create / open / close / unplug / plug / delete / clone /map / parameter session* +The use case directory contains the `yaml` files to test the multitenant controller functionalities: create `lrest` pod, and create PDB operations *create / open / close / unplug / plug / delete / clone /map / parameter session* ## Makefile helper -Customizing yaml files (tns alias / credential / namespaces name etc...) is a long procedure prone to human error. A simple [makefile](../usecase/makefile) is available to quickly and safely configure yaml files with your system environment information. Just edit the [parameter file](../usecase/parameters.txt) before proceding. +Customizing `yaml` files (tns alias / credential / namespaces name, and so on) is a long procedure that is prone to human error. A simple [`makefile`](../usecase/makefile) is available to quickly and safely configure `yaml` files with your system environment information. Just edit the [parameter file](../usecase/parameters.txt) before proceding. ```text TNSALIAS...............:[Tnsalias do not use quotes and avoid space in the string --> (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELA....] @@ -23,28 +23,28 @@ COMPANY................:[your company name] APIVERSION.............:v4 --> do not edit ``` -⚠ **WARNING: The makefile is intended to speed up the usecase directory configuartion only, it is not supported, the editing and configuration of yaml files for production system is left up to the end user** +⚠ **WARNING: The makefile is only intended to speed up the usecase directory configuration. Use of this file for production purposes is not supported. The editing and configuration of yaml files for production system is left up to the end user** -### Pre requisistes: +### Prerequisistes: -- Make sure that **kubectl** is properly configured. -- Make sure that all requirements listed in the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) -- Make sure that administrative user on the container database is configured as documented. +- Ensure that **kubectl** is properly configured. +- Ensure that all requirements listed in the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) +- Ensure that the administrative user (admin) on the container database is configured as documented. ```bash make operator ``` -This command creates the operator-database-operator.yaml in the local directory and set up the watchnamespace list. Note that the yaml file is not applyed. +This command creates the `operator-database-operator.yaml` file in the local directory, and set up the `watchnamespace` list. Note that the `yaml` file is not applied. ```bash make secrets ``` -All the secrets with the ecrypted credential are created by this command execution. +This command creates all of the Secrets with the encrypted credentials. ```bash make genyaml ``` -*make genyaml* generates the required yaml files to work with multitenant controllers. +*make genyaml* generates the required `yaml` files to work with multitenant controllers. ![image](../images/UsecaseSchema.jpg) @@ -94,7 +94,7 @@ I1029 10:07:20.189724 1 leaderelection.go:250] attempting to acquire leade ### Error decrypting credential -Resource creation failure due to decription error +The following is an example of a resource creation failure due to decription error: ```text 2024-10-30T10:09:08Z INFO controllers.LRPDB getEncriptedSecret :pdbusr {"getEncriptedSecret": {"name":"lrpdb1","namespace":"pdbnamespace"}} @@ -102,7 +102,7 @@ Resource creation failure due to decription error ``` -**Solution**: make sure to use **PCKS8** format during private key generation. If you are not using openssl3 then execute this command +**Solution**: Ensure you use **PCKS8** format during private key generation. If you are not using `openssl3`, then run this command: ```bash openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > mykey @@ -110,7 +110,7 @@ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keyge ### Crd details -Use **describe** option to get crd information +Use the **describe** option to obtain `crd` information ```bash kubectl describe lrpdb lrpdb1 -n pdbnamespace @@ -136,4 +136,4 @@ Events: Warning LRESTINFO 95s LRPDB pdb=pdbdev:test_invalid_parameter:16:spfile:2065 Warning Done 15s (x12 over 2m25s) LRPDB cdb-dev -``` \ No newline at end of file +``` From c90fae38d4dcb4e459cd045897929b21dc4efca2 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 21:27:39 +0000 Subject: [PATCH 191/414] Update README.md minor style updates --- docs/multitenant/ords-based/README.md | 88 +++++++++++++-------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/docs/multitenant/ords-based/README.md b/docs/multitenant/ords-based/README.md index 70bbd576..edfd0208 100644 --- a/docs/multitenant/ords-based/README.md +++ b/docs/multitenant/ords-based/README.md @@ -2,28 +2,28 @@ # Oracle Multitenant Database Controllers -The Oracle Database Operator for Kubernetes uses two controllers to manage [Pluggable Database life cycle][oradocpdb] +The Oracle Database Operator for Kubernetes uses two controllers to manage the [Pluggable Database lifecycle][oradocpdb] - CDB controller - PDB controller -By using CDB/PDB controllers you can perform the following actions **CREATE**, **MODIFY(OPEN/COSE)**, **DELETE**, **CLONE**, **PLUG** and **UNPLUG** against pluggable database +By using CDB/PDB controllers, you can perform the following actions **CREATE**, **MODIFY(OPEN/COSE)**, **DELETE**, **CLONE**, **PLUG** and **UNPLUG** against pluggable database Examples are located under the following directories: -- [Usecase](./usecase/) and [usecase01](./usecase01/) directories contain a [configuration file](./usecase/parameters.txt) where you can specify all the details of your environment. A [makefile](./usecase/makefile) takes this file in input to generate the all the yaml files. There is no need to edit yaml files one by one. -- [Singlenamespace provisioning](./provisioning/singlenamespace/) You will find base sample files to manage pdb and cdb within a single namespace -- [Multinamespace provisioning](./provisioning/multinamespace/) You will find base sample files to manage pdb and cdb in different namespaces. -- [Usecase01](./usecase01/README.md) [Usecase02](./usecase02/README.md) contain other step by step examples; +- the directories [`Usecase`](./usecase/) and [`usecase01`](./usecase01/) contain a [configuration file](./usecase/parameters.txt) where you can specify all the details of your environment. A [`makefile`](./usecase/makefile) takes this file as input to generate all of the `yaml` files. There is no need to edit `yaml` files one by one. +- [Singlenamespace provisioning](./provisioning/singlenamespace/) This file contains base example files that you can use to manage the PDB and CDB within a single namespace. +- [Multinamespace provisioning](./provisioning/multinamespace/) This file contains base example files that you can use to manage the PDB and CDB in different namespaces. +- [Usecase01](./usecase01/README.md) [Usecase02](./usecase02/README.md) This file contains other step-by-step examples; -Automatic yaml generation is not available for directory usecase02 and provisioning directories +Automatic `yaml` generation is not available for the directory `usecase02` and provisioning directories. -**NOTE** cdb controller is not intended to manage the container database. The CDB controller is meant to provide a pod with a rest server connected to the container database to manage PDBs. +**NOTE** the CDB controller is not intended to manage the container database. The CDB controller is meant to provide a pod with a REST server connected to the container database that you can use to manage PDBs. ## Macro steps for setup -- Deploy the Oracle Database Operator +- Deploy the Oracle Database Operator (operator, or `OraOperator`) - [Create Ords based image for CDB pod](./provisioning/ords_image.md) - [Container RDBMB user creation](#prepare-the-container-database-for-pdb-lifecycle-management-pdb-lm) - Create certificates for https connection @@ -32,9 +32,9 @@ Automatic yaml generation is not available for directory usecase02 and provision ## Oracle DB Operator Multitenant Database Controller Deployment -To deploy OraOperator, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. +To deploy `OraOperator`, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. -Once the **Oracle Database Operator** is deployed, you can see the DB Operator Pods running in the Kubernetes Cluster. The multitenant controllers are deployed as part of the `OraOperator` deploymentd. You can see the CRDs (Custom Resource Definition) for the CDB and PDBs in the list of CRDs. The following output is an example of such a deployment: +After the **Oracle Database Operator** is deployed, you can see the Oracle Database (DB) Operator Pods running in the Kubernetes Cluster. The multitenant controllers are deployed as part of the `OraOperator` deployment. You can see the CRDs (Custom Resource Definition) for the CDB and PDBs in the list of CRDs. The following output is an example of such a deployment: ```bash [root@test-server oracle-database-operator]# kubectl get ns @@ -86,7 +86,7 @@ singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z ## Prerequisites to manage PDB Life Cycle using Oracle DB Operator Multitenant Database Controller -* [Prepare the container database for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) +* [Prepare the container database (CDB) for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) * [Oracle REST Data Service or ORDS Image](#oracle-rest-data-service-ords-image) * [Kubernetes Secrets](#kubernetes-secrets) * [Kubernetes CRD for CDB](#cdb-crd) @@ -94,7 +94,7 @@ singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z ## Prepare the container database for PDB Lifecycle Management (PDB-LM) -Pluggable Database (PDB) management operations are performed in the Container Database (CDB). These operations include create, clone, plug, unplug, delete, modify and map pdb. +Pluggable Database (PDB) management operations are performed in the Container Database (CDB). These operations include **create**, **clone**, **plug**, **unplug**, **delete**, **modify** and **map pdb**. To perform PDB lifecycle management operations, you must first use the following steps to define the default CDB administrator credentials on target CDBs: @@ -122,25 +122,25 @@ select username, account_status from dba_users where username in ('ORDS_PUBLIC_U ## OCI OKE (Kubernetes Cluster) -You can use an [OKE in Oracle Cloud Infrastructure][okelink] to configure the controllers for PDB lifecycle management. **Note that there is no restriction about container database location; it can be anywhere (on cloud or on-premises).** +You can use an [OKE in Oracle Cloud Infrastructure][okelink] to configure the controllers for PDB lifecycle management. **Note that there is no restriction about container database location; it can be anywhere (on Cloud or on-premises).** To quickly create an OKE cluster in your OCI cloud environment you can use the following [link](./provisioning/quickOKEcreation.md). -In this setup example [provisioning example setup](./provisioning/example_setup_using_oci_oke_cluster.md), the Container Database is running on a OCI Exadata Database Cluster. +In this setup example [provisioning example setup](./provisioning/example_setup_using_oci_oke_cluster.md), the Container Database is running on an OCI Exadata Database Cluster. ## Oracle REST Data Service (ORDS) Image - The PDB Database controllers require a pod running a dedicated rest server image based on [ORDS][ordsdoc]. Read the following [link](./provisioning/ords_image.md) to build the ords images. +The PDB Database controllers require a pod running a dedicated REST server image based on [ORDS][ordsdoc]. Read the following [document on ORDS images](./provisioning/ords_image.md) to build the ORDS images. ## Kubernetes Secrets - Multitenant Controllers use Kubernetes secrets to store the required credential and https certificates. + Multitenant Controllers use Kubernetes Secrets to store the required credential and HTTPS certificates. - **Note** In multi namespace environment you have to create specific secrets for each namespaces + **Note** In multi-namespace environments you must create specific Secrets for each namespaces. ### Secrets for CERTIFICATES -Create the certificates and key on your local host and use them to create the Kubernetes secret. +Create the certificates and key on your local host, and then use them to create the Kubernetes Secret. ```bash openssl genrsa -out ca.key 2048 @@ -157,11 +157,11 @@ kubectl create secret generic db-ca --from-file=ca.crt -n oracle-database-operat image_not_found -**Note:** Remove temporary files after successfful secret creation. +**Note:** Remove temporary files after successfful Secret creation. ### Secrets for CDB CRD - **Note:** base64 encoded secrets are no longer supported; use openssl secrets as documented in the following section. After successful creation of the CDB Resource, the CDB and PDB secrets can be deleted from the Kubernetes system. Don't leave plaintext files containing sensitive data on disk. After loading the secret, remove the plaintext file or move it to secure storage. + **Note:** base64 encoded secrets are no longer supported; use OpenSSL secrets as documented in the following section. After successful creation of the CDB Resource, the CDB and PDB Secrets can be deleted from the Kubernetes system. Don't leave plaintext files containing sensitive data on disk. After loading the Secret, remove the plaintext file or move it to secure storage. ```bash @@ -229,7 +229,7 @@ rm ${WBUSERFILE} ${WBPASSFILE} ${CDBPWDFILE} ${CDBUSRFILE} \ e_${SYSPWDFILE} e_${ORDPWDFILE} e_${PDBUSRFILE} e_${PDBPWDFILE} ``` -Check secrets details +Check Secrets details ```bash kubectl describe secrets syspwd -n cdbnamespace @@ -244,7 +244,7 @@ Data ==== e_syspwd.txt: 349 bytes ``` -Example of yaml file secret section: +Example of `yaml` file Secret section: ```yaml [...] @@ -261,9 +261,9 @@ Example of yaml file secret section: ## CDB CRD -The Oracle Database Operator Multitenant Controller creates the CDB kind as a custom resource that models a target CDB as a native Kubernetes object. This kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. Each CDB resource follows the CDB CRD as defined here: [config/crd/bases/database.oracle.com_cdbs.yaml](../../../config/crd/bases/database.oracle.com_cdbs.yaml) +The Oracle Database Operator Multitenant Controller creates the CDB as a custom resource object kind that models a target CDB as a native Kubernetes object. This object kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. Each CDB resource follows the CDB CRD as defined here: [`config/crd/bases/database.oracle.com_cdbs.yaml`](../../../config/crd/bases/database.oracle.com_cdbs.yaml) -To create a CDB CRD, use this example`.yaml` file: [cdb_create.yaml](../multitenant/provisioning/singlenamespace/cdb_create.yaml) +To create a CDB CRD, use this example`.yaml` file: [`cdb_create.yaml`](../multitenant/provisioning/singlenamespace/cdb_create.yaml) **Note:** The password and username fields in this *cdb.yaml* Yaml are the Kubernetes Secrets created earlier in this procedure. For more information, see the section [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). To understand more about creating secrets for pulling images from a Docker private registry, see [Kubernetes Private Registry Documenation]( https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). @@ -277,7 +277,7 @@ see [usecase01][uc01] and usecase02[uc02] for more information about file config ## PDB CRD -The Oracle Database Operator Multitenant Controller creates the PDB kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. You cannot have more than one Kubernetes resource for a target PDB. This PDB resource can be used to perform PDB-LM operations by specifying the action attribute in the PDB Specs. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) +The Oracle Database Operator Multitenant Controller creates the PDB object kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. You cannot have more than one Kubernetes resource for a target PDB. This PDB resource can be used to perform PDB-LM operations by specifying the action attribute in the PDB Specs. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) Yaml file [pdb_create.yaml](../multitenant/provisioning/singlenamespace/pdb_create.yaml) to create a pdb @@ -293,10 +293,10 @@ kubectl apply -f pdb_create.yaml | dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | CDB | | port | | [--db-port][2] | CDB | | cdbName | | Container Name | CDB | -| name | | Ords podname prefix in cdb.yaml | CDB | -| name | | pdb resource in pdb.yaml | PDB | -| ordsImage | ords-dboper:latest | ords pod public container registry | CDB | -| pdbName | | Pluggable database name | CDB | +| name | | ORDS podname prefix in `cdb.yaml` | CDB | +| name | | Pdb resource in `pdb.yaml` | PDB | +| ordsImage | ords-dboper:latest | ORDS pod public container registry | CDB | +| pdbName | | Pluggable database (PDB) name | Container database (CDB) | | servicename | | [--db-servicename][3] | CDB | | sysadmin_user | | [--admin-user][adminuser] | CDB | | sysadmin_pwd | | [--password-stdin][pwdstdin] | CDB | @@ -310,19 +310,19 @@ kubectl apply -f pdb_create.yaml | pdbTlsCat | | certificate authority | PDB | | cdbTlsKey | | [standalone.https.cert.key][key] | CDB | | cdbTlsCrt | | [standalone.https.cert][cr] | CDB | -| cdbTlsCat | | certificate authority | CDB | -| cdbOrdsPrvKey | | private key | CDB | -| pdbOrdsPrvKey | | private key | PDB | -| xmlFileName | | path for the unplug and plug operation | PDB | -| srcPdbName | | name of the database to be cloned | PDB | -| action | | create open close delete clone plug unplug and map | PDB | -| deletePdbCascade | boolean | delete pdbs cascade during cdb deletion | CDB | -| assertivePdbDeletion | boolean | Deleting pdb crd means deleteing pdb as well | PDB | -| fileNameConversions | | used for database cloning | PDB | -| totalSize | | dbsize | PDB | -| pdbState | | change pdb state | PDB | -| modifyOption | | to be used along with pdbState | PDB | -| dropAction | | delete datafiles during pdb deletion | PDB | +| cdbTlsCat | | Certificate authority | CDB | +| cdbOrdsPrvKey | | Private key | CDB | +| pdbOrdsPrvKey | | Private key | PDB | +| xmlFileName | | Path for the unplug and plug operation | PDB | +| srcPdbName | | Name of the database that you want to be cloned | PDB | +| action | | Create open close delete clone plug unplug and map | PDB | +| deletePdbCascade | boolean | Delete PDBs cascade during CDB deletion | CDB | +| assertivePdbDeletion | boolean | Deleting the PDB crd means deleting the PDB as well | PDB | +| fileNameConversions | | Used for database cloning | PDB | +| totalSize | | `dbsize` | PDB | +| pdbState | | Change PDB state | PDB | +| modifyOption | | To be used along with `pdbState` | PDB | +| dropAction | | Delete datafiles during PDB deletion | PDB | | sourceFileNameConversions | | [sourceFileNameConversions(optional): string][4] | PDB | | tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | N/A | | tdeExport | | [tdeExport] | N/A ] @@ -357,7 +357,7 @@ kubectl apply -f pdb_create.yaml ## Known issues - - Ords installation failure if pluaggable databases in the container db are not opened + - ORDS installation failure if pluaggable databases in the container db are not openedS - Version 1.1.0: encoded password for https authentication may include carriage return as consequence the https request fails with http 404 error. W/A generate encoded password using **printf** instead of **echo**. From 0e588822878de3503590944e7b13c26a8879f6c7 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 22:05:39 +0000 Subject: [PATCH 192/414] Update README.md minor style changes --- docs/observability/README.md | 196 +++++++++++++++++------------------ 1 file changed, 94 insertions(+), 102 deletions(-) diff --git a/docs/observability/README.md b/docs/observability/README.md index 7f6279e1..1da6c30d 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -33,27 +33,27 @@ of the controller. * [Debugging and troubleshooting](#debugging-and-troubleshooting) ## Prerequisites -The `DatabaseObserver` custom resource has the following pre-requisites: +The `DatabaseObserver` custom resource has the following prerequisites: 1. Prometheus and its `servicemonitor` custom resource definition must be installed on the cluster. - The Observability controller creates multiple Kubernetes resources that include - a Prometheus `servicemonitor`. In order for the controller + a Prometheus `servicemonitor`. For the controller to create ServiceMonitors, the ServiceMonitor custom resource must exist. -2. A pre-existing Oracle Database and the proper database grants and privileges. +2. A preexisting Oracle Database and the proper database grants and privileges. - The controller exports metrics through SQL queries that the user can control and specify through a _toml_ file. The necessary access privileges to the tables used in the queries are not provided and applied automatically. ## The DatabaseObserver Custom Resource -The Oracle Database Operator (__v1.2.0__ or later) includes the Oracle Database Observability controller which automates -the deployment and setting up of the Oracle Database exporter and the related resources to make Oracle databases observable. +The Oracle Database Operator (__v1.2.0__ or later) includes the Oracle Database Observability controller, which automates +the deployment and setting up of the Oracle Database exporter and the related resources to make Oracle Databases observable. -In the sample YAML file found in -[./config/samples/observability/v4/databaseobserver.yaml](../../config/samples/observability/v4/databaseobserver.yaml), -the databaseObserver custom resource offers the following properties to be configured: +In the example YAML file found in +[`./config/samples/observability/v4/databaseobserver.yaml`](../../config/samples/observability/v4/databaseobserver.yaml), +the databaseObserver custom resource provides the following configurable properties: | Attribute | Type | Default | Required? | Example | |--------------------------------------------------------|--------|---------------------------------------------------------------------|:------------|-----------------------------------------------------------------------| @@ -94,62 +94,62 @@ the databaseObserver custom resource offers the following properties to be confi ### Configuration Options -The `databaseObserver` custom resource has the following fields for all configurations that are required: -* `spec.database.dbUser.secret` - secret containing the database username. The corresponding key can be any value but must match the key in the secret provided. -* `spec.database.dbPassword.secret` - secret containing the database password (if vault is NOT used). The corresponding key field can be any value but must match the key in the secret provided -* `spec.database.dbConnectionString.secret` - secret containing the database connection string. The corresponding key field can be any value but must match the key in the secret provided -* `spec.prometheus.serviceMonitor.labels` - custom labels to add to the service monitors labels. A label is required for your serviceMonitor to be discovered. This label must match what is set in the serviceMonitorSelector of your Prometheus configuration +The `databaseObserver` Custom resource has the following fields for all configurations that are required: +* `spec.database.dbUser.secret` - Secret containing the database username. The corresponding key can be any value but must match the key in the secret provided. +* `spec.database.dbPassword.secret` - Secret containing the database password (if `vault` is NOT used). The corresponding key field can be any value, but must match the key in the Secret provided +* `spec.database.dbConnectionString.secret` - Secret containing the database connection string. The corresponding key field can be any value but must match the key in the Secret provided +* `spec.prometheus.serviceMonitor.labels` - Custom labels to add to the service monitors labels. A label is required for your serviceMonitor to be discovered. This label must match what is set in the serviceMonitorSelector of your Prometheus configuration -If a database wallet is required to connect, the following field containing the wallet secret is required: -* `spec.database.dbWallet.secret` - secret containing the database wallet. The filenames inside the wallet must be used as keys +If a database wallet is required to connect, then the following field containing the wallet secret is required: +* `spec.database.dbWallet.secret` - Secret containing the database wallet. The filenames inside the wallet must be used as keys -If vault is used to store the database password instead, the following fields are required: +If vault is used to store the database password instead, then the following fields are required: * `spec.database.dbPassword.vaultOCID` - OCID of the vault used * `spec.database.dbPassword.vaultSecretName` - Name of the secret inside the desired vault -* `spec.ociConfig.configMapName` - holds the rest of the information of the OCI API signing key. The following keys must be used: `fingerprint`, `region`, `tenancy` and `user` -* `spec.ociConfig.secretName` - holds the private key of the OCI API signing key. The key to the file containing the user private key must be: `privatekey` +* `spec.ociConfig.configMapName` - Holds the rest of the information of the OCI API signing key. The following keys must be used: `fingerprint`, `region`, `tenancy` and `user` +* `spec.ociConfig.secretName` - Holds the private key of the OCI API signing key. The key to the file containing the user private key must be: `privatekey` -The `databaseObserver` resource provides the remaining multiple fields that are optional: +The `databaseObserver` Resource provides the remaining multiple fields that are optional: * `spec.prometheus.serviceMonitor.endpoints` - ServiceMonitor endpoints * `spec.prometheus.serviceMonitor.namespaceSelector` - ServiceMonitor namespace selector -* `spec.sidecars` - list of containers to run as a sidecar container with the observability exporter container image -* `spec.sidecarVolumes` - volumes of any sidecar containers -* `spec.log.path` - custom path to create -* `spec.log.filename` - custom filename for the log file -* `spec.log.volume.name` - custom name for the log volume -* `spec.log.volume.persistentVolumeClaim.claimName` - a volume in which to place the log in order to be shared by the containers. If not specified, an EmptyDir is used by default. -* `spec.configuration.configMap.key` - configuration filename inside the container and the configmap -* `spec.configuration.configMap.name` - name of the configMap that holds the custom metrics configuration -* `spec.replicas` - number of replicas to deploy -* `spec.exporter.service.ports` - port number for the generated service to use -* `spec.exporter.service.labels` - custom labels to add to service labels -* `spec.exporter.deployment.image` - image version of observability exporter to use -* `spec.exporter.deployment.env` - custom environment variables for the observability exporter -* `spec.exporter.deployment.labels` - custom labels to add to deployment labels -* `spec.exporter.deployment.podTemplate.labels` - custom labels to add to pod labels -* `spec.exporter.deployment.podTemplate.securityContext` - configures pod securityContext -* `spec.exporter.deployment.args` - additional arguments to provide the observability-exporter -* `spec.exporter.deployment.commands` - commands to supply to the observability-exporter -* `spec.exporter.deployment.securityContext` - configures container securityContext -* `spec.inheritLabels` - keys of inherited labels from the databaseObserver resource. These labels are applied to generated resources. +* `spec.sidecars` - List of containers to run as a sidecar container with the observability exporter container image +* `spec.sidecarVolumes` - Volumes of any sidecar containers +* `spec.log.path` - Custom path to create +* `spec.log.filename` - Custom filename for the log file +* `spec.log.volume.name` - Custom name for the log volume +* `spec.log.volume.persistentVolumeClaim.claimName` - A volume in which to place the log to be shared by the containers. If not specified, an EmptyDir is used by default. +* `spec.configuration.configMap.key` - Configuration filename inside the container and the configmap +* `spec.configuration.configMap.name` - Name of the `configMap` that holds the custom metrics configuration +* `spec.replicas` - Number of replicas to deploy +* `spec.exporter.service.ports` - Port number for the generated service to use +* `spec.exporter.service.labels` - Custom labels to add to service labels +* `spec.exporter.deployment.image` - Image version of observability exporter to use +* `spec.exporter.deployment.env` - Custom environment variables for the observability exporter +* `spec.exporter.deployment.labels` - Custom labels to add to deployment labels +* `spec.exporter.deployment.podTemplate.labels` - Custom labels to add to pod labels +* `spec.exporter.deployment.podTemplate.securityContext` - Configures pod securityContext +* `spec.exporter.deployment.args` - Additional arguments to provide the observability-exporter +* `spec.exporter.deployment.commands` - Commands to supply to the observability-exporter +* `spec.exporter.deployment.securityContext` - Configures container securityContext +* `spec.inheritLabels` - Keys of inherited labels from the databaseObserver resource. These labels are applied to generated resources. ### Resources Managed by the Controller -When you create a DatabaseObserver resource, the controller creates and manages the following resources: +When you create a `DatabaseObserver` resource, the controller creates and manages the following resources: -1. __Deployment__ - the deployment will have the same name as the databaseObserver resource - - deploys a container named `observability-exporter` - - the default container image version of the `container-registry.oracle.com/database/observability-exporter` supported is __[v1.5.1](https://github.com/oracle/oracle-db-appdev-monitoring/releases/tag/1.5.1)__ +1. __Deployment__ - The deployment will have the same name as the `databaseObserver` resource + - Deploys a container named `observability-exporter` + - The default container image version of the `container-registry.oracle.com/database/observability-exporter` supported is __[v1.5.1](https://github.com/oracle/oracle-db-appdev-monitoring/releases/tag/1.5.1)__ -2. __Service__ - the service will have the same name as the databaseObserver - - the service is of type `ClusterIP` +2. __Service__ - The service will have the same name as the databaseObserver + - The service is of type `ClusterIP` -3. __Prometheus ServiceMonitor__ - the serviceMonitor will have the same name as the databaseObserver +3. __Prometheus ServiceMonitor__ - The serviceMonitor will have the same name as the `databaseObserver` ## DatabaseObserver Operations ### Create Resource -Follow the steps below to create a new databaseObserver resource object. +Follow the steps below to create a new `databaseObserver` resource object. -1. To begin, creating a databaseObserver requires you to create and provide kubernetes Secrets to provide connection details: +1. To begin, creating a `databaseObserver` requires you to create and provide Kubernetes Secrets to provide connection details: ```bash kubectl create secret generic db-secret \ --from-literal=username='username' \ @@ -157,18 +157,17 @@ kubectl create secret generic db-secret \ --from-literal=connection='dbsample_tp' ``` -2. (Conditional) Create a Kubernetes secret for the wallet (if a wallet is required to connect to the database). +2. (Conditional) Create a Kubernetes Secret for the wallet (if a wallet is required to connect to the database). -You can create this secret by using a command similar to the following example below. -If you are connecting to an Autunomous Database and the operator is used to manage the Oracle Autonomous Database, -a client wallet can also be downloaded as a secret through kubectl commands. You can find out how, [here](../../docs/adb/README.md#download-wallets). +You can create this Secret by using a command similar to the example that follows. +If you are connecting to an Autunomous Database, and the operator is used to manage the Oracle Autonomous Database, then a client wallet can also be downloaded as a Secret through `kubectl` commands. See the ADB README section on [Download Wallets](../../docs/adb/README.md#download-wallets). -Otherwise, you can create the wallet secret from a local directory containing the wallet files. +You can also choose to create the wallet secret from a local directory containing the wallet files: ```bash kubectl create secret generic db-wallet --from-file=wallet_dir ``` -3. Finally, update the databaseObserver manifest with the resources you have created. You can use the example _minimal_ manifest +3. Finally, update the `databaseObserver` manifest with the resources you have created. You can use the example _minimal_ manifest inside [config/samples/observability/v4](../../config/samples/observability/v4/databaseobserver_minimal.yaml) to specify and create your databaseObserver object with a YAML file. @@ -230,37 +229,35 @@ To obtain a more detailed status, use the following command as an example: kubectl describe databaseobserver obs-sample ``` -This provides details of the current state of your databaseObserver resource object. A successful -deployment of the databaseObserver resource object should display `READY` as the status and all conditions with a `True` -value for every ConditionType. +This command displays details of the current state of your `databaseObserver` resource object. A successful +deployment of the `databaseObserver` resource object should display `READY` as the status, and all conditions should display with a `True` value for every ConditionType. ### Patch Resource -The Observability controller currently supports updates for most of the fields in the manifest. An example of patching the databaseObserver resource is as follows: +The Observability controller currently supports updates for most of the fields in the manifest. The following is an example of patching the `databaseObserver` resource: ```bash kubectl --type=merge -p '{"spec":{"exporter":{"image":"container-registry.oracle.com/database/observability-exporter:1.5.0"}}}' patch databaseobserver obs-sample ``` ### Delete Resource -To delete the DatabaseObserver custom resource and all related resources: +To delete the `databaseObserver` custom resource and all related resources, use this command: ```bash kubectl delete databaseobserver obs-sample ``` ## Scraping Metrics -The DatabaseObserver resource deploys the Observability exporter container which connects to an Oracle Database and -scrapes metrics via SQL queries. By default, the exporter provides standard metrics, which are listed in the [official GitHub page of the -Observability Exporter](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#standard-metrics). +The `databaseObserve`r resource deploys the Observability exporter container. This container connects to an Oracle Database and +scrapes metrics using SQL queries. By default, the exporter provides standard metrics, which are listed in the [official GitHub page of the Observability Exporter](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#standard-metrics). -To define custom metrics in the Oracle Database for scraping, a TOML file that lists your custom queries and properties is required. +To define custom metrics in Oracle Database for scraping, a TOML file that lists your custom queries and properties is required. The file will have metric sections with the following parts: - a context - a request, which contains the SQL query - a map between the field(s) in the request and comment(s) -For example, following code snippet below shows how you can define custom metrics: +For example, the code snippet that follows shows how you can define custom metrics: ```toml [[metric]] context = "test" @@ -277,21 +274,20 @@ oracledb_test_value_1 1 oracledb_test_value_2 2 ``` -More information can be found in the [__Custom Metrics__](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#custom-metrics) -section of the Official GitHub page. +You can find more information in the [__Custom Metrics__](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#custom-metrics) section of the Official GitHub page. ### Custom Metrics Config -When configuring a DatabaseObserver resource, you can use the field `spec.configuration.configMap` to provide a -custom metrics file as a configMap. +When configuring a `databaseObserver` resource, you can use the field `spec.configuration.configMap` to provide a +custom metrics file as a `configMap`. -You can create the configmap by running the following command: +You can create the `configMap` by running the following command: ```bash kubectl create cm custom-metrics-cm --from-file=metrics.toml ``` -Finally, when creating or updating a databaseObserver resource, considering the example above, you can set the following fields in your YAML file to: +Finally, when creating or updating a `databaseObserver` resource, if we assume using the example above, you can set the fields in your YAML file as follows: ```yaml spec: configuration: @@ -301,8 +297,7 @@ spec: ``` ### Prometheus Release -The field `spec.prometheus.serviceMonitor.labels` is an important and required field in order to enable your Prometheus configuration -to find and include the `ServiceMonitor` created by the DatabaseObserver resource. The label on the ServiceMonitor +To enable your Prometheus configuration to find and include the `ServiceMonitor` created by the `databaseObserver` resource, the field `spec.prometheus.serviceMonitor.labels` is an important and required field. The label on the ServiceMonitor must match the `spec.serviceMonitorSelector` field in your Prometheus configuration. ```yaml @@ -313,29 +308,27 @@ must match the `spec.serviceMonitorSelector` field in your Prometheus configurat ``` ## Scraping Logs -Currently, the observability exporter provides the `alert.log` from the Oracle Database, which provides important information -on errors and exceptions during database operations. +Currently, the observability exporter provides the `alert.log` from Oracle Database, which provides important information about errors and exceptions during database operations. -The logs are stored in the pod filesystem, inside `/log/alert.log` by default. Note that the log can also be placed in a custom path with a custom filename, -as well as a volume available to multiple pods with the use of PersistentVolumes by specifying a persistentVolumeClaim. -Since the logs are stored in a file, scraping the logs will need to be pushed to a log aggregation system like _Loki_. -In the following example, Promtail is used as a sidecar container which ships the contents of local logs to the Loki instance. +By default, the logs are stored in the pod filesystem, inside `/log/alert.log`. Note that the log can also be placed in a custom path with a custom filename, You can also place a volume available to multiple pods with the use of `PersistentVolumes` by specifying a `persistentVolumeClaim`. +Because the logs are stored in a file, scraping the logs must be pushed to a log aggregation system, such as _Loki_. +In the following example, `Promtail` is used as a sidecar container that ships the contents of local logs to the Loki instance. -To configure the DatabaseObserver resource with a sidecar, two fields can be used: +To configure the `databaseObserver` resource with a sidecar, two fields can be used: ```yaml spec: sidecars: [] sidecarVolumes: [] ``` -You can find an example in the samples directory, which deploys a Promtail sidecar container as an example: -[config/samples/observability/v4/databaseobserver_logs_promtail.yaml](../../config/samples/observability/v4/databaseobserver_logs_promtail.yaml) +You can find an example in the `samples` directory, which deploys a Promtail sidecar container as an example: +[`config/samples/observability/v4/databaseobserver_logs_promtail.yaml`](../../config/samples/observability/v4/databaseobserver_logs_promtail.yaml) ### Custom Log Location with PersistentVolumes The fields `spec.log.filename` and `spec.log.path` enable you to configure a custom location and filename for the log. -This enables you to control where to place the logfile, such as a persistentVolume. +Using a custom location enables you to control where to place the logfile, such as a `persistentVolume`. ```yaml log: @@ -343,12 +336,12 @@ This enables you to control where to place the logfile, such as a persistentVolu path: "/log" ``` -To configure the DatabaseObserver resource to put the log file in a persistentVolume, you can set the following fields -in your DatabaseObserver YAML file. The field `spec.log.volume.name` is provided to control the name of the volume used +To configure the `databaseObserver` resource to put the log file in a `persistentVolume`, you can set the following fields +in your `databaseObserver` YAML file. The field `spec.log.volume.name` is provided to control the name of the volume used for the log, while the field `spec.log.volume.persistentVolumeClaim.claimName` is used to specify the claim to use. -These details can be used towards any sidecar containers, or other containers. +These details can be used with any sidecar containers, or with other containers. -If `spec.log.volume.persistentVolumeClaim.claimName` is not specified, an `EmptyDir` volume is automatically used. +If `spec.log.volume.persistentVolumeClaim.claimName` is not specified, then an `EmptyDir` volume is automatically used. ```yaml log: @@ -358,8 +351,7 @@ If `spec.log.volume.persistentVolumeClaim.claimName` is not specified, an `Empty claimName: "my-pvc" ``` -If security context, which defines privilege and access control settings for a pod container, need to be updated in the pod, -the same field is available on the DatabaseObserver spec. You can set this object under deployment: `spec.exporter.deployment.securityContext`. +The security context defines privilege and access control settings for a pod container, If these privileges and access control settingrs need to be updated in the pod, then the same field is available on the `databaseObserver` spec. You can set this object under deployment: `spec.exporter.deployment.securityContext`. ```yaml spec: @@ -381,7 +373,7 @@ spec: ``` -### Working with Sidecars, to deploy Promtail +### Working with Sidecars to deploy Promtail The fields `spec.sidecars` and `spec.sidecarVolumes` provide the ability to deploy container images as a sidecar container alongside the `observability-exporter` container. @@ -414,7 +406,7 @@ For example, when deploying the Promtail container, you can specify in the field name: promtail-config-file ``` -In this example, the `promtail-config-file` configMap contains the Promtail configuration, which specifies where to find +In this example, the `promtail-config-file` `configMap` contains the Promtail configuration, which specifies where to find the target and the path to the file, as well as the endpoint where Loki is listening for any push API requests. __Promtail Config Example__ @@ -440,7 +432,7 @@ scrape_configs: __path__: /log/*.log ``` -To create the configmap, you can run the following command: +To create the `configmap`, you can run the following command: ```bash kubectl create cm promtail-config-file --from-file=config.yaml ``` @@ -454,7 +446,7 @@ __About the Default Label__ - The resources created by the Observability Control - `app`: `` -For example, if the databaseObserver instance is named: `metrics-exporter`, resources like the deployment will be labelled +For example, if the `databaseObserver` instance is named: `metrics-exporter`, then resources such as the deployment will be labelled with `app: metrics-exporter`. This label cannot be overwritten. Selectors used by the deployment, service and servicemonitor use this label. The following configuration shows an example: @@ -476,7 +468,7 @@ spec: # ... ``` -Meanwhile, you can provide extra labels to the resources created by the DatabaseObserver controller such as the Deployment, Pods, Service and ServiceMonitor. +Meanwhile, you can provide extra labels to the resources created by the `databaseObserver` controller, such as the Deployment, Pods, Service and ServiceMonitor. ```yaml spec: exporter: @@ -503,7 +495,7 @@ spec: ``` ### Custom Environment Variables, Arguments and Commands -The fields `spec.exporter.deployment.env`, `spec.exporter.deployment.args` and `spec.exporter.deployment.commands` are provided for adding custom environment variables, args and commands to the containers. +The fields `spec.exporter.deployment.env`, `spec.exporter.deployment.args` and `spec.exporter.deployment.commands` are provided for adding custom environment variables, arguments (`args`) and commands to the containers. Any custom environment variable will overwrite environment variables set by the controller. ```yaml @@ -521,7 +513,7 @@ spec: ### Custom Service Ports -The field `spec.exporter.service.ports` is provided for setting the ports of the service. If not set, the following definition is set by default. +The field `spec.exporter.service.ports` is provided to enable setting the ports of the service. If not set, then the following definition is set by default. ```yaml spec: @@ -535,7 +527,7 @@ spec: ``` ### Custom ServiceMonitor Endpoints -The field `spec.prometheus.serviceMonitor.endpoints` is provided for providing custom endpoints for the ServiceMonitor resource created by the DatabaseObserver +The field `spec.prometheus.serviceMonitor.endpoints` is provided for providing custom endpoints for the ServiceMonitor resource created by the `databaseObserver`: ```yaml spec: @@ -579,17 +571,17 @@ and gets and lists configmaps and secrets. ## Debugging and troubleshooting ### Show the details of the resource -To get the verbose output of the current spec, use the command below: +To obtain the verbose output of the current spec, use the following command: ```sh kubectl describe databaseobserver/database-observer-sample ``` -If any error occurs during the reconciliation loop, the Operator either reports -the error using the resource's event stream, or will show the error under conditions. +If any error occurs during the reconciliation loop, then the Operator either reports +the error using the resource's event stream, or it will show the error under conditions. ### Check the logs of the pod where the operator deploys -Follow the steps to check the logs. +Follow these steps to check the logs. 1. List the pod replicas @@ -597,7 +589,7 @@ Follow the steps to check the logs. kubectl get pods -n oracle-database-operator-system ``` -2. Use the below command to check the logs of the deployment +2. Use the following command to check the logs of the deployment ```sh kubectl logs deployment.apps/oracle-database-operator-controller-manager -n oracle-database-operator-system @@ -605,4 +597,4 @@ Follow the steps to check the logs. ## Resources -- [GitHub - Unified Observability for Oracle Database Project](https://github.com/oracle/oracle-db-appdev-monitoring) \ No newline at end of file +- [GitHub - Unified Observability for Oracle Database Project](https://github.com/oracle/oracle-db-appdev-monitoring) From 4b31bfc4c19a41439419af67c31e8d988c0aa7f4 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 22:08:26 +0000 Subject: [PATCH 193/414] Update create_kubernetes_secret_for_db_user.md minor style edits --- .../create_kubernetes_secret_for_db_user.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md b/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md index 99620f04..744f972c 100644 --- a/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md +++ b/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md @@ -1,14 +1,14 @@ # Create kubernetes secret for db user -Below are the steps to create an encrypted file with a password for the DB User: +Use the following steps to create an encrypted file with a password for the DB User: -- Create a text file which is having the password which you want to use for the DB user. +- Create a text file that has the password that you want to use for the DB user. - Create an RSA key pair using `openssl`. -- Encrypt the text file with password using `openssl` with the RSA key pair generated earlier. +- Encrypt the text file with a password, using `openssl` with the RSA key pair generated earlier. - Remove the initial text file. -- Create the Kubernetes secret named `db-user-pass-rsa` using the encrypted file. +- Create the Kubernetes Secret named `db-user-pass-rsa` using the encrypted file. -Please refer the below example for the above steps: +To understand how to create your own file, use the following example: ```sh # Create a directory for files for the secret: @@ -43,4 +43,4 @@ kubectl delete secret $SECRET_NAME -n $NAMESPACE # Create the Kubernetes secret in namespace "NAMESPACE" kubectl create secret generic $SECRET_NAME --from-file=$PWDFILE_ENC --from-file=${PRIVKEY} -n $NAMESPACE -``` \ No newline at end of file +``` From 8028959d4ff49bfdbe9389799dc21a4922b74065 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 22:10:35 +0000 Subject: [PATCH 194/414] Update database_connection.md minor style changes --- docs/sharding/provisioning/database_connection.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sharding/provisioning/database_connection.md b/docs/sharding/provisioning/database_connection.md index 7f64bbd5..735cc2d5 100644 --- a/docs/sharding/provisioning/database_connection.md +++ b/docs/sharding/provisioning/database_connection.md @@ -1,10 +1,10 @@ # Database Connectivity -The Oracle Database Sharding Topology deployed by Sharding Controller in Oracle Database Operator has an external IP available for each of the container. +The Oracle Database Sharding Topology deployed by Sharding Controller in Oracle Database Operator has an external IP available for each of the containers. ## Below is an example setup with connection details -Check the details of the Sharding Topology provisioned using Sharding Controller: +Check the details of the Sharding Topology provisioned by using the Sharding Controller: ```sh $ kubectl get all -n shns @@ -35,7 +35,7 @@ statefulset.apps/shard1 1/1 10d statefulset.apps/shard2 1/1 10d ``` -After you have the external IP address, you can use the services shown below to make the database connection using the above example: +After you have the external IP address, you can use the services shown below to make the database connection. Using the preceding example, that file should look as follows: 1. **Direct connection to the CATALOG Database**: Connect to the service `catalogpdb` on catalog container external IP `xx.xx.xx.116` on port `1521` 2. **Direct connection to the shard Database SHARD1**: Connect to the service `shard1pdb` on catalog container external IP `xx.xx.xx.187` on port `1521` From 7414a5acc26ad61ebf04638f35c21e18bb6856c6 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 22:20:15 +0000 Subject: [PATCH 195/414] Update debugging.md Minor style edits. --- docs/sharding/provisioning/debugging.md | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/sharding/provisioning/debugging.md b/docs/sharding/provisioning/debugging.md index 63e02b6a..8922bc1d 100644 --- a/docs/sharding/provisioning/debugging.md +++ b/docs/sharding/provisioning/debugging.md @@ -1,50 +1,50 @@ # Debugging and Troubleshooting -When the Oracle Database Sharding Topology is provisioned using the Oracle Database Kubernetes Operator, the debugging of an issue with the deployment depends on at which stage the issue has been seen. +When the Oracle Database Sharding Topology is provisioned using the Oracle Database Kubernetes Operator, debugging an issue with the deployment depends on which stage the issue is seen. -Below are the possible cases and the steps to debug such an issue: +The following sections provide possible issue cases, and the steps to debug such an issue: ## Failure during the provisioning of Kubernetes Pods -In case the failure occurs during the provisioning, we need to check the status of the Kubernetes Pod which has failed to deployed. +If the failure occurs during the provisioning, then check the status of the Kubernetes Pod that has failed to be deployed. -Use the below command to check the logs of the Pod which has a failure. For example, for failure in case of Pod `pod/catalog-0`, use below command: +To check the logs of the Pod that has a failure, use the command that follows. In this example, we are checking for failure in provisioning Pod `pod/catalog-0`: ```sh kubectl logs -f pod/catalog-0 -n shns ``` -In case the Pod has failed to provision due to an issue with the Docker Image, you will see the error `Error: ErrImagePull` in above logs. +If the Pod has failed to provision due to an issue with the Docker Image, then you will see the error `Error: ErrImagePull` in the logs displayed by the command. -If the Pod has not yet got initialized, use the below command to find the reason for it: +If the Pod has not yet been initialized, then use the following command to find the reason for it: ```sh kubectl describe pod/catalog-0 -n shns ``` -In case the failure is related to the Cloud Infrastructure, you will need to troubleshooting that using the documentation from the cloud provider. +If the failure is related to the Cloud Infrastructure, then troubleshoot the infrastructure using the documentation from the Cloud infrastructure provider. ## Failure in the provisioning of the Sharded Database -In case the failure occures after the Kubernetes Pods are created but during the execution of the scripts to create the shard databases, catalog database or the GSM, you will need to trobleshoot that at the individual Pod level. +If the failure occures after the Kubernetes Pods are created but during the execution of the scripts to create the shard databases, catalog database or the GSM, then you must troubleshoot that failure at the individual Pod level. -Initially, check the logs of the Kubernetes Pod using the command like below (change the name of the Pod with the actual Pod) +Initially, check the logs of the Kubernetes Pod using the following command (change the name of the Pod in the command with the actual Pod): ```sh kubectl logs -f pod/catalog-0 -n shns ``` -To check the logs at the GSM or at the Database level or at the host level, switch to the corresponding Kubernetes container using the command like below: +To check the logs at the GSM level, the database level, or at the host level, switch to the corresponding Kubernetes container. For example: ```sh kubectl exec -it catalog-0 -n shns /bin/bash ``` -Now, you can troubleshooting the corresponding component using the alert log or the trace files etc just like a normal Sharding Database Deployment. Please refer to [Oracle Database Sharding Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/19/shard/sharding-troubleshooting.html#GUID-629262E5-7910-4690-A726-A565C59BA73E) for this purpose. +When you are in the correct Kubernetes container, you can troubleshooting the corresponding component using the alert log, the trace files, and so on, just as you would with a normal Sharding Database Deployment. For more information, see: [Oracle Database Sharding Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/19/shard/sharding-troubleshooting.html#GUID-629262E5-7910-4690-A726-A565C59BA73E) ## Debugging using Database Events * You can enable database events as part of the Sharded Database Deployment -* This can be enabled using the `envVars` -* One example of enabling Database Events is [sharding_provisioning_with_db_events.md](./debugging/sharding_provisioning_with_db_events.md) \ No newline at end of file +* Enable events using `envVars` +* One example of enabling Database Events is [sharding_provisioning_with_db_events.md](./debugging/sharding_provisioning_with_db_events.md) From 327a9a483d91df52d8d8940ae4a15b8f367c77a7 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Wed, 12 Feb 2025 22:26:58 +0000 Subject: [PATCH 196/414] Update PREREQUISITES.md minor style edits. --- docs/sidb/PREREQUISITES.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/sidb/PREREQUISITES.md b/docs/sidb/PREREQUISITES.md index b904f5da..4bf09283 100644 --- a/docs/sidb/PREREQUISITES.md +++ b/docs/sidb/PREREQUISITES.md @@ -3,27 +3,26 @@ To deploy Oracle Single Instance Database in Kubernetes using the OraOperator, c * ### Prepare Oracle Container Images - Build Single Instance Database Container Images from source, following the instructions at [https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance), or - use the pre-built images available at [https://container-registry.oracle.com](https://container-registry.oracle.com) by signing in and accepting the required license agreement. + You can either build Single Instance Database Container Images from the source, following the instructions at [https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance), or you can use the the pre-built images available at [https://container-registry.oracle.com](https://container-registry.oracle.com) by signing in and accepting the required license agreement. - Oracle Database Releases Supported: Enterprise and Standard Edition for Oracle Database 19c, and later releases. Express Edition for Oracle Database 21.3.0 only. Oracle Database Free 23.2.0 and later Free releases + Oracle Database Releases Supported: Enterprise and Standard Edition for Oracle Database 19c, and later releases. Express Edition for Oracle Database 21.3.0 only. Oracle Database Free 23.2.0 and later Free releases Build Oracle REST Data Service Container Images from source following the instructions at [https://github.com/oracle/docker-images/tree/main/OracleRestDataServices](https://github.com/oracle/docker-images/tree/main/OracleRestDataServices). - Supported Oracle REST Data Service version is 21.4.2 + The supported Oracle REST Data Service version is 21.4.2 * ### Ensure Sufficient Disk Space in Kubernetes Worker Nodes - Provision Kubernetes worker nodes with recommended 250 GiB or more of free disk space required for pulling the base and patched database container images. If deploying on cloud you may choose to increase the custom boot volume size of the worker nodes. + Provision Kubernetes worker nodes. Oracle recommends you provision them with 250 GB or more free disk space, which is required for pulling the base and patched database container images. If you are doing a Cloud deployment, then you can choose to increase the custom boot volume size of the worker nodes. * ### Set Up Kubernetes and Volumes for Database Persistence Set up an on-premises Kubernetes cluster, or subscribe to a managed Kubernetes service, such as Oracle Cloud Infrastructure Container Engine for Kubernetes. Use a dynamic volume provisioner or pre-provision static persistent volumes manually. These volumes are required for persistent storage of the database files. - More info on creating persistent volumes available at [https://kubernetes.io/docs/concepts/storage/persistent-volumes/](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) + For more more information about creating persistent volumes, see: [https://kubernetes.io/docs/concepts/storage/persistent-volumes/](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) * ### Minikube Cluster Environment - By default, Minikube creates a node with 2GB RAM, 2 CPUs, and 20GB disk space when a cluster is created using `minikube start` command. However, these resources (particularly disk space and RAM) may not be sufficient for running and managing Oracle Database using the OraOperator. It is recommended to have larger RAM and disk space for better performance. For example, the following command creates a Minikube cluster with 8GB RAM and 100GB disk space for the Minikube VM: + By default, when you create a cluster using the `minicube start` command, Minikube creates a node with 2GB RAM, 2 CPUs, and 20GB disk space. However, these resources (particularly disk space and RAM) may not be sufficient for running and managing Oracle Database using the OraOperator. For better performance, Oracle recommends that you configure the cluster to have a larger RAM and disk space than the Minikube default. For example, the following command creates a Minikube cluster with 8GB RAM and 100GB disk space for the Minikube VM: ``` minikube start --memory=8g --disk-size=100g From bf0691b52d1c7892a6c948ac904d05f5c06bfe96 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Wed, 12 Feb 2025 23:21:30 +0000 Subject: [PATCH 197/414] mtnreadme --- docs/multitenant/README.md | 8 +- oracle-database-operator.yaml | 447 ++++++++++++++++++++++++++++++++++ 2 files changed, 450 insertions(+), 5 deletions(-) diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index 43d1d9a4..0d3057fc 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -1,13 +1,11 @@ # Multitenant Controllers - -Starting from operator version 1.2.0, there are two classes of multitenant controllers: one based on [ORDS](https://www.oracle.com/uk/database/technologies/appdev/rest.html) and another based on a dedicated REST server for the operator, called LREST. In both cases, the overall architecture remains unchanged. A pod running a REST server (either LREST or ORDS) acts as the proxy server connected to the container database (CDB) for all incoming kubectl requests. +Starting from OraOperator version 1.2.0, there are two classes of multitenant controllers: one based on [ORDS](https://www.oracle.com/uk/database/technologies/appdev/rest.html) and another based on a dedicated REST server for the operator, called LREST. In both cases, the features remains unchanged (a part from CRD name changes). A pod running a REST server (either LREST or ORDS) acts as the proxy server connected to the container database (CDB) for all incoming kubectl requests. We plan to discontinue the ORDS based controller, in the next release; no regression (a part form CRD name changes). ## What are the differences -- **ORDS** is a standalone product, while **LREST** is not. **LREST** is an integral component of the Oracle Database Operator. -- Regarding the YAML file, no payload parameters for the existing functionalities have been changed. +- Regarding the YAML file, the parameters for the existing functionalities are unchanged. - The **CRD** names are different: for controllers based on [ORDS](./ords-based/README.md), we have **PDB** and **CDB**, while for controllers based on [LREST](./lrest-based/README.md), we have **LRPDB** and **LREST**. - If you use an LREST-based controller, there is no need to manually create the REST server pod. The image is available for download on OCR. -- Controllers based on **LRSET** allow you to manage PDB parameters using kubectl. +- Controllers based on **LREST** allow you to manage PDB parameters using kubectl. - ORDS controllers currently do not support ORDS version 24.1. diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 3ffefe05..d9bededf 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -1661,6 +1661,155 @@ spec: additionalProperties: type: string type: object + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object type: object service: @@ -3835,6 +3984,155 @@ spec: additionalProperties: type: string type: object + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object type: object service: @@ -6009,6 +6307,155 @@ spec: additionalProperties: type: string type: object + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object type: object service: From 268959567fdc8dc0dcbd9ae62190ec109a32dd7d Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 13 Feb 2025 00:01:31 +0000 Subject: [PATCH 198/414] Update README.md significant style and language updates --- docs/sidb/README.md | 227 ++++++++++++++++++++++---------------------- 1 file changed, 114 insertions(+), 113 deletions(-) diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 6caf7d16..bdc9a457 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -58,7 +58,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Single Inst ## Prerequisites -Oracle strongly recommends to comply with the [prerequisites](./PREREQUISITES.md) and the following requirements +Oracle strongly recommends that you comply with the [prerequisites](./PREREQUISITES.md) and the following requirements ### Mandatory Resource Privileges @@ -77,7 +77,7 @@ Oracle strongly recommends to comply with the [prerequisites](./PREREQUISITES.md ### Optional Resource Privileges - Single Instance Database(sidb) controller optionally requires the following Kubernetes resource privileges depending on the functionality being used: + Single Instance Database(`sidb`) controller optionally requires the following Kubernetes resource privileges, depending on the functionality being used: | Functionality | Resources | Privileges | | --- | --- | --- | @@ -86,7 +86,7 @@ Oracle strongly recommends to comply with the [prerequisites](./PREREQUISITES.md | Custom Scripts Execution | PersistentVolumes | get list watch | - For exposing the database via Nodeport services, apply [RBAC](../../rbac/node-rbac.yaml) + For exposing the database using Nodeport services, apply [RBAC](../../rbac/node-rbac.yaml) ```sh kubectl apply -f rbac/node-rbac.yaml ``` @@ -94,22 +94,22 @@ Oracle strongly recommends to comply with the [prerequisites](./PREREQUISITES.md ```sh kubectl apply -f rbac/storage-class-rbac.yaml ``` - For automatic execution of custom scripts post database setup or startup, apply [RBAC](../../rbac/persistent-volume-rbac.yaml) + For automatic execution of custom scripts after database setup or startup, apply [RBAC](../../rbac/persistent-volume-rbac.yaml) ```sh kubectl apply -f rbac/persistent-volume-rbac.yaml ``` ### OpenShift Security Context Constraints - OpenShift requires additional Security Context Constraints (SCC) for deploying and managing the SingleInstanceDatabase resource. Follow these steps to create the appropriate SCCs before deploying the SingleInstanceDatabase resource. + OpenShift requires additional Security Context Constraints (SCC) for deploying and managing the `SingleInstanceDatabase` resource. To create the appropriate SCCs before deploying the `SingleInstanceDatabase` resource, complete these steps: - 1. Create a new project/namespace for deploying the SingleInstanceDatabase resource + 1. Create a new project/namespace for deploying the `SingleInstanceDatabase` resource ```sh oc new-project sidb-ns ``` - **Note:** OpenShift recommends not to deploy in namespaces starting with `kube`, `openshift` and the `default` namespace. + **Note:** OpenShift recommends that you should not deploy in namespaces starting with `kube`, `openshift` and the `default` namespace. 2. Apply the file [openshift_rbac.yaml](../../config/samples/sidb/openshift_rbac.yaml) with cluster-admin user privileges. @@ -117,15 +117,15 @@ Oracle strongly recommends to comply with the [prerequisites](./PREREQUISITES.md oc apply -f openshift-rbac.yaml ``` - This would result in creation of SCC (Security Context Constraints) and serviceaccount `sidb-sa` in the namespace `sidb-ns` which has access to the SCC. + Running this example procedure results in creation of SCC (Security Context Constraints) and serviceaccount `sidb-sa` in the namespace `sidb-ns`, which has access to the SCC. - **Note:** The above config yaml file will bind the SCC to the serviceaccount `sidb-sa` in namespace `sidb-ns`. For any other project/namespace update the file appropriately with the namespace before applying. + **Note:** This configuration yaml file example binds the SCC to the serviceaccount `sidb-sa` in namespace `sidb-ns`. For any other project/namespace, you must update the file appropriately with the namespace before applying this example. 3. Set the `serviceAccountName` attribute to `sidb-sa` and the namespace to `sidb-ns` in **[config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)** before deploying the SingleInstanceDatabase resource. ## SingleInstanceDatabase Resource -The Oracle Database Operator creates the `SingleInstanceDatabase` as a custom resource. Doing this enables Oracle Database to be managed as a native Kubernetes object. We will refer `SingleInstanceDatabase` resource as Database from now onwards. +The Oracle Database Operator creates the `SingleInstanceDatabase` as a custom resource. Doing this enables Oracle Database to be managed as a native Kubernetes object. In this document, we will refer to the `SingleInstanceDatabase` resource as the database. ### Resource Details @@ -213,22 +213,22 @@ $ kubectl describe singleinstancedatabase sidb-sample-clone ### Template YAML The template `.yaml` file for Single Instance Database (Enterprise and Standard Editions), including all the configurable options, is available at: -**[config/samples/sidb/singleinstancedatabase.yaml](./../../config/samples/sidb/singleinstancedatabase.yaml)** +**[`config/samples/sidb/singleinstancedatabase.yaml`](./../../config/samples/sidb/singleinstancedatabase.yaml)** **Note:** -The `adminPassword` field in the above `singleinstancedatabase.yaml` file refers to a secret for the SYS, SYSTEM and PDBADMIN users of the Single Instance Database. This secret is required when you provision a new database, or when you clone an existing database. +The `adminPassword` field in the above `singleinstancedatabase.yaml`example file refers to a Secret for the SYS, SYSTEM and PDBADMIN users of the Single Instance Database. This Secret is required when you provision a new database, or when you clone an existing database. -Create this secret using the following command as an example: +Create this Secret using the following command as an example: kubectl create secret generic db-admin-secret --from-literal=oracle_pwd= -This command creates a secret named `db-admin-secret`, with the key `oracle_pwd` mapped to the actual password specified in the command. +This command creates a Secret named `db-admin-secret`, with the key `oracle_pwd` mapped to the actual password specified in the command. ### Create a Database #### New Database -To provision a new database instance on the Kubernetes cluster, use the example **[config/samples/sidb/singleinstancedatabase_create.yaml](../../config/samples/sidb/singleinstancedatabase_create.yaml)**. +To provision a new database instance on the Kubernetes cluster, use the example **[`config/samples/sidb/singleinstancedatabase_create.yaml`](../../config/samples/sidb/singleinstancedatabase_create.yaml)**. 1. Log into [Oracle Container Registry](https://container-registry.oracle.com/) and accept the license agreement for the Database image; ignore if you have accepted the license agreement already. @@ -260,15 +260,15 @@ To provision a new database instance on the Kubernetes cluster, use the example ``` **Note:** -- For ease of use, the storage class **oci-bv** is specified in the **[singleinstancedatabase_create.yaml](../../config/samples/sidb/singleinstancedatabase_create.yaml)**. This storage class facilitates dynamic provisioning of the OCI block volumes on the Oracle OKE for persistent storage of the database. The supported access mode for this class is `ReadWriteOnce`. For other cloud providers, you can similarly use their dynamic provisioning storage classes. +- For ease of use, the storage class **oci-bv** is specified in the **[`singleinstancedatabase_create.yaml`](../../config/samples/sidb/singleinstancedatabase_create.yaml)**. This storage class facilitates dynamic provisioning of the OCI block volumes on the Oracle OKE for persistent storage of the database. The supported access mode for this class is `ReadWriteOnce`. For other cloud providers, you can similarly use their dynamic provisioning storage classes. - It is beneficial to have the database replica pods more than or equal to the number of available nodes if `ReadWriteMany` access mode is used with the OCI NFS volume. By doing so, the pods get distributed on different nodes and the database image is downloaded on all those nodes. This helps in reducing time for the database fail-over if the active database pod dies. - Supports Oracle Database Enterprise Edition (19.3.0), and later releases. -- To pull the database image faster from the container registry, so that you can bring up the SIDB instance quickly, you can use the container-registry mirror of the corresponding cluster's region. For example, if the cluster exists in Mumbai region, then you can use the `container-registry-bom.oracle.com` mirror. For more information on container-registry mirrors, follow the link [https://blogs.oracle.com/wim/post/oracle-container-registry-mirrors-in-oracle-cloud-infrastructure](https://blogs.oracle.com/wim/post/oracle-container-registry-mirrors-in-oracle-cloud-infrastructure). -- To update the init parameters like `sgaTarget` and `pgaAggregateTarget`, refer the `initParams` section of the [singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file. +- To pull the database image faster from the container registry, so that you can bring up the SIDB instance quickly, you can use the `container-registry mirror` of the corresponding cluster's region. For example, if the cluster exists in Mumbai region, then you can use the `container-registry-bom.oracle.com` mirror. For more information on container-registry mirrors, see: [https://blogs.oracle.com/wim/post/oracle-container-registry-mirrors-in-oracle-cloud-infrastructure](https://blogs.oracle.com/wim/post/oracle-container-registry-mirrors-in-oracle-cloud-infrastructure). +- To update the initialization (init) parameters, such as `sgaTarget` and `pgaAggregateTarget`, see the `initParams` section of the [`singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml) file. #### Pre-built Database -To provision a new pre-built database instance, use the sample **[config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml)** file. For example: +To provision a new pre-built database instance, use the sample **[`config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml)** file. For example: ```sh $ kubectl apply -f singleinstancedatabase_prebuiltdb.yaml @@ -280,16 +280,16 @@ This pre-built image includes the data files of the database inside the image it To build the pre-built database image for the Enterprise/Standard edition, follow these instructions: [Pre-built Database (prebuiltdb) Extension](https://github.com/oracle/docker-images/blob/main/OracleDatabase/SingleInstance/extensions/prebuiltdb/README.md). #### XE Database -To provision new Oracle Database Express Edition (XE) database, use the sample **[config/samples/sidb/singleinstancedatabase_express.yaml](../../config/samples/sidb/singleinstancedatabase_express.yaml)** file. For example: +To provision a new Oracle Database Express Edition (XE) database, use the sample **[config/samples/sidb/singleinstancedatabase_express.yaml](../../config/samples/sidb/singleinstancedatabase_express.yaml)** file. For example: kubectl apply -f singleinstancedatabase_express.yaml This command pulls the XE image available in [Oracle Container Registry](https://container-registry.oracle.com/). **Note:** -- Provisioning Oracle Database express edition is supported for release 21c (21.3.0) only. +- Provisioning Oracle Database Express Edition is supported for release 21c (21.3.0) only. Oracle Database Free replaces Oracle Database Express Edition. - For XE database, only single replica mode (i.e. `replicas: 1`) is supported. -- For XE database, you **cannot change** the init parameters i.e. `cpuCount, processes, sgaTarget or pgaAggregateTarget`. +- For XE database, you **cannot change** the init parameters, such as `cpuCount, processes, sgaTarget or pgaAggregateTarget`. #### Free Database To provision new Oracle Database Free, use the sample **[config/samples/sidb/singleinstancedatabase_free.yaml](../../config/samples/sidb/singleinstancedatabase_free.yaml)** file. For example: @@ -307,18 +307,18 @@ This command pulls the Free lite image available in [Oracle Container Registry]( **Note:** - Provisioning Oracle Database Free is supported for release 23.3.0 and later releases. -- For Free database, only single replica mode (i.e. `replicas: 1`) is supported. -- For Free database, you **cannot change** the init parameters i.e. `cpuCount, processes, sgaTarget or pgaAggregateTarget`. -- Oracle Enterprise Manager Express (OEM Express) is not supported from release 23.3.0 and later releases. +- For Free database, only single replica mode (such as `replicas: 1`) is supported. +- For Free database, you **cannot change** the init parameters. These include parameters such as `cpuCount, processes, sgaTarget or pgaAggregateTarget`. +- Oracle Enterprise Manager Express (OEM Express) is not supported in release 23.3.0 and later releases. #### Oracle True Cache Oracle True Cache is an in-memory, consistent, and automatically managed cache for Oracle Database. -To provision a True Cache instance for the Oracle Free Database in Kubernetes, use the sample **[config/samples/sidb/singleinstancedatabase_free-truecache.yaml](../../config/samples/sidb/singleinstancedatabase_free-truecache.yaml)** file. For example +To provision a True Cache instance for Oracle Free Database in Kubernetes, use the sample **[`config/samples/sidb/singleinstancedatabase_free-truecache.yaml`](../../config/samples/sidb/singleinstancedatabase_free-truecache.yaml)** file. For example kubectl apply -f singleinstancedatabase_free-truecache.yaml #### Additional Information -You are required to specify the database admin password secret in the corresponding YAML file. The default values mentioned in the `adminPassword.secretName` fields of [singleinstancedatabase_create.yaml](../../config/samples/sidb/singleinstancedatabase_create.yaml), [singleinstancedatabase_prebuiltdb.yaml](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml), [singleinstancedatabase_express.yaml](../../config/samples/sidb/singleinstancedatabase_express.yaml) and [singleinstancedatabse_free.yaml](../../config/samples/sidb/singleinstancedatabase_free.yaml) files are `db-admin-secret`, `prebuiltdb-admin-secret`, `xedb-admin-secret` and `free-admin-secret` respectively. You can create these secrets manually by using the sample command mentioned in the [Template YAML](#template-yaml) section. Alternatively, you can create these secrets by filling the passwords in the **[singleinstancedatabase_secrets.yaml](../../config/samples/sidb/singleinstancedatabase_secrets.yaml)** file and applying it using the command below: +You are required to specify the database administrative user (admin) password Secret in the corresponding YAML file. The default values mentioned in the `adminPassword.secretName` fields of [`singleinstancedatabase_create.yaml`](../../config/samples/sidb/singleinstancedatabase_create.yaml), [`singleinstancedatabase_prebuiltdb.yaml`](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml), [`singleinstancedatabase_express.yaml`](../../config/samples/sidb/singleinstancedatabase_express.yaml) and [`singleinstancedatabse_free.yaml`](../../config/samples/sidb/singleinstancedatabase_free.yaml) files are `db-admin-secret`, `prebuiltdb-admin-secret`, `xedb-admin-secret` and `free-admin-secret` respectively. You can create these Secrets manually by using the sample command mentioned in the [`Template YAML`](#template-yaml) section. Alternatively, you can create these Secrets by filling in the passwords in the **[`singleinstancedatabase_secrets.yaml`](../../config/samples/sidb/singleinstancedatabase_secrets.yaml)** file and applying them using the following command: ```bash kubectl apply -f singleinstancedatabase_secrets.yaml @@ -326,7 +326,7 @@ kubectl apply -f singleinstancedatabase_secrets.yaml ### Connecting to Database -Creating a new database instance takes a while. When the `status` column returns the response `Healthy`, the Database is open for connections. +Creating a new database instance takes a while. When the `status` column returns the response `Healthy`, the database is open for connections. ```sh $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.status}" @@ -334,7 +334,7 @@ $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.status}" Healthy ``` -Clients can get the connect-string to the CDB from `.status.connectString` and PDB from `.status.pdbConnectString`. For example: +Clients can obtain the connect string to the CDB from `.status.connectString`, and the connect string to the PDB from `.status.pdbConnectString`. For example: ```sh $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.connectString}" @@ -347,7 +347,7 @@ $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.pdbConnec 10.0.25.54:1521/ORCLPDB ``` -Use any supported client or SQLPlus to connect to the database using the above connect strings as follows +To connect to the database using the connect strings returned by the commands above, you can use any supported client, or use SQLPlus. For example: ```sh $ sqlplus sys/<.spec.adminPassword>@10.0.25.54:1521/ORCL as sysdba @@ -375,7 +375,7 @@ $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.oemExpres **Note:** OEM Express is not available for 23.3.0 and later releases ### Database Persistence (Storage) Configuration Options -The database persistence can be achieved in the following two ways: +You can configure database persistence in the following two ways: - Dynamic Persistence Provisioning - Static Persistence Provisioning @@ -398,8 +398,8 @@ $ kubectl patch singleinstancedatabase sidb-sample -p '{"spec":{"persistence":{" - User can only scale up a volume/storage and not scale down #### Static Persistence -In **Static Persistence Provisioning**, you have to create a volume manually, and then use the name of this volume with the `<.spec.persistence.datafilesVolumeName>` field which corresponds to the `datafilesVolumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. The `Reclaim Policy` of such volume can be set to `Retain`. So, this volume does not get deleted with the deletion of its corresponding deployment. -For example in **Minikube**, a persistent volume can be provisioned using the sample yaml file below: +In **Static Persistence Provisioning**, you must create a volume manually, and then use the name of this volume with the `<.spec.persistence.datafilesVolumeName>` field, which corresponds to the `datafilesVolumeName` field of the persistence section in the **[`singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml)**. The `Reclaim Policy` of such volumes can be set to `Retain`. When this policy is set, the volume is not deleted when its corresponding deployment is deleted. +For example in **Minikube**, a persistent volume can be provisioned using the following yaml file example: ```yaml apiVersion: v1 kind: PersistentVolume @@ -414,7 +414,7 @@ spec: hostPath: path: /data/oradata ``` -The persistent volume name (i.e. db-vol) can be mentioned in the `datafilesVolumeName` field of the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. `storageClass` field is not required in this case, and can be left empty. +The persistent volume name (in this case, `db-vol`) can be mentioned in the `datafilesVolumeName` field of the **[`singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml)**. `storageClass` field is not required in this case, and can be left empty. Static Persistence Provisioning in Oracle Cloud Infrastructure (OCI) is explained in the following subsections: @@ -471,7 +471,7 @@ spec: - Whenever a mount target is provisioned in OCI, its `Reported Size (GiB)` values are very large. This is visible on the mount target page when logged in to the OCI console. Some applications will fail to install if the results of a space requirements check show too much available disk space. So in the OCI Console, click the little "Pencil" icon besides the **Reported Size** parameter of the Mount Target to specify, in gigabytes (GiB), the maximum capacity reported by file systems exported through this mount target. This setting does not limit the actual amount of data you can store. -- Make sure to open the required ports to access the NFS volume from the K8S cluster: add the required ports to the security list of the subnet where your K8S nodes are connected to; see **[here](https://docs.oracle.com/en-us/iaas/Content/File/Tasks/securitylistsfilestorage.htm)** for the details. +- You must open the required ports to access the NFS volume from the K8S cluster. Add the required ports to the security list of the subnet to which your K8S nodes are connected. For more information, see **[Security Lists File Storage](https://docs.oracle.com/en-us/iaas/Content/File/Tasks/securitylistsfilestorage.htm)** for the details. ### Configuring a Database The `OraOperator` facilitates you to configure the database. Various database configuration options are explained in the following subsections: @@ -527,7 +527,7 @@ The following attributes cannot be modified after creating the Single Instance D - `pdbName` - `primaryDatabaseRef` -If you attempt to changing one of these attributes, then you receive an error similar to the following: +If you attempt to change one of these attributes, then you receive an error similar to the following: ```sh $ kubectl --type=merge -p '{"spec":{"sid":"ORCL1"}}' patch singleinstancedatabase sidb-sample @@ -539,7 +539,7 @@ $ kubectl --type=merge -p '{"spec":{"sid":"ORCL1"}}' patch singleinstancedatabas To create copies of your existing database quickly, you can use the cloning functionality. A cloned database is an exact, block-for-block copy of the source database. Cloning is much faster than creating a fresh database and copying over the data. -To quickly clone the existing database sidb-sample created above, use the sample **[config/samples/sidb/singleinstancedatabase_clone.yaml](../../config/samples/sidb/singleinstancedatabase_clone.yaml)** file. +To quickly clone the existing database `sidb-sample` we previously created for this document, use the sample **[`config/samples/sidb/singleinstancedatabase_clone.yaml`](../../config/samples/sidb/singleinstancedatabase_clone.yaml)** file. For example: @@ -563,7 +563,7 @@ Patched Oracle Docker images can be built by using this [patching extension](htt #### Patch -To patch an existing database, edit and apply the **[config/samples/sidb/singleinstancedatabase_patch.yaml](../../config/samples/sidb/singleinstancedatabase_patch.yaml)** file of the database resource/object either by specifying a new release update for image attributes, or by running the following command: +To patch an existing database, edit and apply the **[`config/samples/sidb/singleinstancedatabase_patch.yaml`](../../config/samples/sidb/singleinstancedatabase_patch.yaml)** file of the database resource/object either by specifying a new release update for image attributes, or by running the following command: ```sh kubectl --type=merge -p '{"spec":{"image":{"pullFrom":"patched-image:tag","pullSecrets":"pull-secret"}}}' patch singleinstancedatabase sidb-sample @@ -575,7 +575,7 @@ singleinstancedatabase.database.oracle.com/sidb-sample patched After patching is complete, the database pods are restarted with the new release update image. **Note:** -- Only enterprise and standard editions support patching. +- Only Enterprise and Standard Editions support patching. #### Patch after Cloning @@ -597,7 +597,7 @@ $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.releaseUp ``` #### Rollback -You can roll back to a prior database version by specifying the old image in the `image` field of the **[config/samples/sidb/singleinstancedatabase_patch.yaml](../../config/samples/sidb/singleinstancedatabase_patch.yaml)** file, and applying it by the following command: +You can roll back to a prior database version by specifying the old image in the `image` field of the **[`config/samples/sidb/singleinstancedatabase_patch.yaml`](../../config/samples/sidb/singleinstancedatabase_patch.yaml)** file, and applying it using the following command: ```bash kubectl apply -f singleinstancedatabase_patch.yaml @@ -613,12 +613,12 @@ singleinstancedatabase.database.oracle.com/sidb-sample patched ``` ### Delete a Database -Please run the following command to delete the database: +To delete the database, run the following command : ```bash kubectl delete singleinstancedatabase.database.oracle.com sidb-sample ``` -The command above will delete the database pods and associated service. +This command will delete the database pods and associated service. ### Advanced Database Configurations Some advanced database configuration scenarios are as follows: @@ -642,17 +642,17 @@ The following table depicts the fail over matrix for any destructive operation t | PDB close | No | **Note:** -- Maintence shutdown/startup can be executed using the scripts /home/oracle/shutDown.sh and /home/oracle/startUp.sh +- Maintence shutdown/startup can be run by using the scripts `/home/oracle/shutDown.sh` and `/home/oracle/startUp.sh` - This functionality requires the [k8s extension](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/k8s) extended images. The database image from the container registry `container-registry.oracle.com` includes the K8s extension. - Because Oracle Database Express Edition (XE) does not support [k8s extension](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/k8s), it does not support multiple replicas. -- If the `ReadWriteOnce` access mode is used, all the replicas will be scheduled on the same node where the persistent volume would be mounted. -- If the `ReadWriteMany` access mode is used, all the replicas will be distributed on different nodes. So, it is recommended to have replicas more than or equal to the number of the nodes as the database image is downloaded on all those nodes. This is beneficial in quick cold fail-over scenario (when the active pod dies) as the image would already be available on that node. +- If the `ReadWriteOnce` access mode is used, then all the replicas will be scheduled on the same node where the persistent volume would be mounted. +- If the `ReadWriteMany` access mode is used, then all the replicas will be distributed on different nodes. For this reason, Oracle recommends that you have replicas more than or equal to the number of the nodes, because the database image is downloaded on all those nodes. This is beneficial in quick cold fail-over scenario (when the active pod dies) as the image would already be available on that node. #### Database Pod Resource Management -When creating a Single Instance Database you can specify the cpu and memory resources needed by the database pod. These specified resources are passed to the `kube-scheduler` so that the pod gets scheduled on one of the pods that has the required resources available. To use database pod resource management specify values for the `resources` attributes in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. +When creating a Single Instance Database, you can specify the CPU and memory resources needed by the database pod. These specified resources are passed to the `kube-scheduler` so that the pod is scheduled on one of the pods that has the required resources available. To use database pod resource management, specify values for the `resources` attributes in the [`config/samples/sidb/singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. #### Setup Database with LoadBalancer -For the Single Instance Database, the default service is the `NodePort` service. You can enable the `LoadBalancer` service by using `kubectl patch` command. +For the Single Instance Database, the default service is the `NodePort` service. You can enable the `LoadBalancer` service by using the `kubectl patch` command. For example: @@ -663,7 +663,7 @@ $ kubectl --type=merge -p '{"spec":{"loadBalancer": true}}' patch singleinstance ``` ### Enabling TCPS Connections -You can enable TCPS connections in the database by setting the `enableTCPS` field to `true` in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and applying it. +You can enable TCPS connections in the database by setting the `enableTCPS` field to `true` in the [`config/samples/sidb/singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml) file, and applying it. Alternatively, you can use the following command: ```bash @@ -703,44 +703,45 @@ true cd export TNS_ADMIN=$(pwd) ``` - After this, connect using SQL\*Plus using the following sample commands: + After this, connect with SQL*Plus, using the following example commands: ```bash sqlplus sys@ORCL1 as sysdba ``` ### Specifying Custom Ports -As mentioned in the section [Setup Database with LoadBalancer](#setup-database-with-loadbalancer), there are two kubernetes services possible for the database: NodePort and LoadBalancer. You can specify which port to use with these services by editing the `listenerPort` and `tcpsListenerPort` fields of the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file. +As mentioned in the section [Setup Database with LoadBalancer](#setup-database-with-loadbalancer), there are two kubernetes services possible for the database: NodePort and LoadBalancer. You can specify which port to use with these services by editing the `listenerPort` and `tcpsListenerPort` fields of the [`config/samples/sidb/singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml) file. `listenerPort` is intended for normal database connections. Similarly, `tcpsListenerPort` is intended for TCPS database connections. -If the `LoadBalancer` is enabled, the `listenerPort`, and `tcpsListenerPort` will be the opened ports on the Load Balancer for normal and TCPS database connections respectively. The default values of `listenerPort` and `tcpsListenerPort` are 1521 and 2484 respectively when the `LoadBalancer` is enabled. +If the `LoadBalancer` is enabled, then the `listenerPort`, and `tcpsListenerPort` will be the opened ports on the Load Balancer for normal and TCPS database connections respectively. When the `LoadBalancer` is enabled, the default values of `listenerPort` and `tcpsListenerPort` are 1521 and 2484. -In case of `NodePort` service, `listenerPort`, and `tcpsListenerPort` will be the opened ports on the Kubernetes nodes for for normal and TCPS database connections respectively. In this case, the allowed range for the `listenerPort`, and `tcpsListenerPort` is 30000-32767. +If the `NodePort` service is enabled, then the `listenerPort`, and `tcpsListenerPort` will be the opened ports on the Kubernetes nodes for for normal and TCPS database connections respectively. In this case, the allowed range for the `listenerPort`, and `tcpsListenerPort` is 30000-32767. **Note:** -- `listenerPort` and `tcpsListenerPort` can not have same values. -- `tcpsListenerPort` will come into effect only when TCPS connections are enabled (i.e. `enableTCPS` field is set in [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file). -- If TCPS connections are enabled, and `listenerPort` is commented/removed in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, only TCPS endpoint will be exposed. -- If LoadBalancer is enabled, and either `listenerPort` or `tcpsListenerPort` is changed, then it takes some time to complete the work requests (drain existing backend sets and create new ones). In this time, the database connectivity is broken. Although, SingleInstanceDatabase and LoadBalancer remain in the healthy state, you can check the progress of the work requests by logging into the cloud provider's console and checking the corresponding LoadBalancer. +- `listenerPort` and `tcpsListenerPort` cannot have same values. +- `tcpsListenerPort` will come into effect only when TCPS connections are enabled (specifically, the `enableTCPS` field is set in [`config/samples/sidb/singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml) file). +- If TCPS connections are enabled, and `listenerPort` is commented or removed in the [`config/samples/sidb/singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml) file, then only the TCPS endpoint will be exposed. +- If LoadBalancer is enabled, and either `listenerPort` or `tcpsListenerPort` is changed, then it takes some time to complete the work requests (drain existing backend sets and create new ones). During this time, the database connectivity is broken, although `SingleInstanceDatabase` and `LoadBalancer` remain in a healthy state. To check the progress of the work requests, you can by log in to the Cloud provider's console and check the corresponding LoadBalancer. ### Setup Data Guard Configuration for a Single Instance Database ### Create a Standby Database #### Prerequisites -- Before creating a Standby, ensure that ArchiveLog, FlashBack, and ForceLog on primary Single Instance Database(`.spec.primaryDatabaseRef`) are turned on. -- Standby database is not supported for TCPS enabled Primary databases. +- Before creating a Standby, ensure that ArchiveLog, FlashBack, and ForceLog on the primary Single Instance Database(`.spec.primaryDatabaseRef`) are turned on. +- Standby database is not supported for TCPS-enabled Primary databases. #### Template YAML -To create a standby database, edit and apply the sample yaml file [config/samples/sidb/singleinstancedatabase_standby.yaml](../../config/samples/sidb/singleinstancedatabase_standby.yaml). +To create a standby database, edit and apply the example YAML file [`config/samples/sidb/singleinstancedatabase_standby.yaml`](../../config/samples/sidb/singleinstancedatabase_standby.yaml). **Note:** -- The `adminPassword` field of the above [config/samples/sidb/singleinstancedatabase_standby.yaml](../../config/samples/sidb/singleinstancedatabase_standby.yaml) contains an admin password secret of the primary database ref for Standby Database creation. This secret will get deleted after the database pod becomes ready if the `keepSecret` attribute of `adminPassword` field is set to `false`. By default `keepSecret` is set to `true`. -- Mention referred primary database in `.spec.primaryDatabaseRef` in the yaml file. -- `.spec.createAs` field of the yaml file should be set to "standby". -- Database configuration like `Archivelog`, `FlashBack`, `ForceLog`, `TCPS connections` are not supported for standby database. +- The `adminPassword` field of the above [`config/samples/sidb/singleinstancedatabase_standby.yaml`](../../config/samples/sidb/singleinstancedatabase_standby.yaml) contains an admin password Secret of the primary database referred to for Standby Database creation. By default `keepSecret` is set to `true`, which means that the secret is saved. However, if you want to delete the Secret after the database pod becomes ready, then this Secret will be deleted if the `keepSecret` attribute of `adminPassword` field is set to `false`. . +- Specify the primary database with which the standby database is associateed in the `.spec.primaryDatabaseRef` yaml file. +- The `.spec.createAs` field of the yaml file should be set to "standby". +- Database configuration, such as `Archivelog`, `FlashBack`, `ForceLog`, `TCPS connections`, are not supported for standby database. #### List Standby Databases +To list the standby databases, use the `get singleinstancedatabase` command. For example: ```sh kubectl get singleinstancedatabase @@ -752,7 +753,7 @@ stdby-1 Enterprise Healthy PHYSICAL_STANDBY 19.3.0.0.0 10.25.0.27:3239 ``` ### Query Primary Database Reference -You can query the corresponding primary database for every standby database. +You can query the corresponding primary database for every standby database. For example: ```sh kubectl get singleinstancedatabase stdby-1 -o "jsonpath={.status.primaryDatabase}" @@ -761,7 +762,7 @@ sidb-19 #### Creation Status - Creating a new standby database instance takes a while. When the 'status' status returns the response "Healthy", the Database is open for connections. + Creating a new standby database instance takes a while. When the 'status' status returns the response "Healthy", the database is open for connections. For example: ```sh $ kubectl get singleinstancedatabase stdby-1 -o "jsonpath={.status.status}" @@ -773,23 +774,23 @@ $ kubectl get singleinstancedatabase stdby-1 -o "jsonpath={.status.status}" #### Template YAML -After creating standbys, setup a dataguard configuration with protection mode and switch over capability using the following sample yaml. -[config/samples/sidb/dataguardbroker.yaml](./../../config/samples/sidb/dataguardbroker.yaml) +After creating standbys, set up an Oracle Data Guard (Data Guard) configuration with protection mode, and switch over capability using the following example YAML: +[`config/samples/sidb/dataguardbroker.yaml`](./../../config/samples/sidb/dataguardbroker.yaml) #### Create DataGuardBroker Resource -Provision a new DataguardBroker custom resource for a single instance database(`.spec.primaryDatabaseRef`) by specifying appropriate values for the primary and standby databases in the example `.yaml` file, and running the following command: +To use the Data Guard broker, provision a new `dataguardbroker` custom resource for a single instance database(`.spec.primaryDatabaseRef`) by specifying the appropriate values for the primary and standby databases in the example `.yaml` file, and running the following command: ```sh $ kubectl create -f dataguardbroker.yaml dataguardbroker.database.oracle.com/dataguardbroker-sample created ``` -**Note:** The following attributes cannot be patched post DataguardBroker resource creation : `primaryDatabaseRef, protectionMode` +**Note:** The following attributes cannot be patched after you create the `dataguardbroker` resource: `primaryDatabaseRef, protectionMode` #### DataguardBroker List -To list the DataguardBroker resources, use the following command: +To list the Data Guard broker resources, use the following command: ```sh $ kubectl get dataguardbroker -o name @@ -799,6 +800,7 @@ To list the DataguardBroker resources, use the following command: ``` #### Quick Status +You can obtain a quick status of Data Guard broker by using the following command: ```sh $ kubectl get dataguardbroker dataguardbroker-sample @@ -809,6 +811,7 @@ To list the DataguardBroker resources, use the following command: ``` #### Detailed Status +To obtain more detailed Data Guard broker status, use this command: ```sh $ kubectl describe dataguardbroker dataguardbroker-sample @@ -869,9 +872,9 @@ To list the DataguardBroker resources, use the following command: ### Perform a Switchover -Specify the approppriate SID (SID of one of `.spec.primaryDatabaseRef` , `.spec.standbyDatabaseRefs[]`) to be set primary in the `.spec.setAsPrimaryDatabase` of [dataguardbroker.yaml](./../../config/samples/sidb/dataguardbroker.yaml) and apply the yaml file. +Specify the approppriate database system identifier (SID) (the SID of one of `.spec.primaryDatabaseRef` , `.spec.standbyDatabaseRefs[]`) to be set primary in the `.spec.setAsPrimaryDatabase` of [`dataguardbroker.yaml`](./../../config/samples/sidb/dataguardbroker.yaml) and apply the yaml file. -The database will be set to primary. Ignored if the database is already primary. +When you apply the YAML file, the database you specify will be set to primary. However, if the database specified with the `apply` command is already the primary, then this command has no effect: ```sh $ kubectl apply -f dataguardbroker.yaml @@ -879,7 +882,7 @@ $ kubectl apply -f dataguardbroker.yaml dataguardbroker.database.oracle.com/dataguardbroker-sample apply ``` -Or use the patch command +You can also use the patch command ```sh $ kubectl --type=merge -p '{"spec":{"setAsPrimaryDatabase":"ORCLS1"}}' patch dataguardbroker dataguardbroker-sample @@ -889,8 +892,8 @@ $ kubectl --type=merge -p '{"spec":{"setAsPrimaryDatabase":"ORCLS1"}}' patch dat ### Enable Fast-Start Failover -Oracle Data Guard Fast-Start Failover (FSFO) monitors your Data Guard environments and initiates an automatic failover in the case of an outage. -To enable FSFO, make sure the primary database is in the primary role. Then set the attribute `.spec.fastStartFailover` to true in [datguardbroker.yaml](./../../config/samples/sidb/dataguardbroker.yaml) and apply it. +Oracle Data Guard Fast-Start Failover (FSFO) monitors your Oracle Data Guard environments and initiates an automatic failover in the case of an outage. +To enable FSFO, ensure the primary database is in the primary role, set the attribute `.spec.fastStartFailover` to `true` in [`datguardbroker.yaml`](./../../config/samples/sidb/dataguardbroker.yaml), and then apply it. For example: ```sh $ kubectl apply -f dataguardbroker.yaml @@ -898,7 +901,7 @@ $ kubectl apply -f dataguardbroker.yaml dataguardbroker.database.oracle.com/dataguardbroker-sample configured ``` -Or use the patch command +You can also use the patch command: ```sh $ kubectl --type=merge -p '{"spec":{"fastStartFailover": true}}' patch dataguardbroker dataguardbroker-sample @@ -906,15 +909,15 @@ $ kubectl --type=merge -p '{"spec":{"fastStartFailover": true}}' patch dataguard dataguardbroker.database.oracle.com/dataguardbroker-sample patched ``` -This results in the creation of a pod running the Observer. The Observer is a component of the DGMGRL interface which monitors the availability of the primary database. +Applying this results in the creation of a pod running the Observer. The Observer is a component of the DGMGRL interface, which monitors the availability of the primary database. -**Note:** When the attribute fastStartFailover is true, performing a switchover by specifying setAsPrimaryDatabase is not allowed. +**Note:** When the attribute `fastStartFailover` is `true`, then performing a switchover by specifying `setAsPrimaryDatabase` is not allowed. ### Convert Standby to Snapshot Standby -A snapshot standby is a fully updatable standby database that can be used development and testing. It receives and archives, but does not apply, redo data from a primary database. The redo data received from the primary database is applied once a snapshot standby database is converted back into a physical standby database, after discarding all local updates to the snapshot standby database. +A snapshot standby is a fully updatable standby database that can be used development and testing. It receives and archives, but does not apply redo data from a primary database. The redo data received from the primary database is applied after a snapshot standby database is converted back into a physical standby database, and after discarding all local updates to the snapshot standby database. -To convert a standby database to snapshot standby, make sure Fast-Start Failover is disabled, then set the attribute `.spec.convertToSnapshotStandby` to true in [singleinstancedatabase.yaml](./../../config/samples/sidb/singleinstancedatabase.yaml) before applying it. +To convert a standby database to a snapshot standby, Ensure Fast-Start Failover is disabled, and tshen set the attribute `.spec.convertToSnapshotStandby` to `true` in [`singleinstancedatabase.yaml`](./../../config/samples/sidb/singleinstancedatabase.yaml) before applying it. For example: ```sh $ kubectl apply -f singleinstancedatabase.yaml @@ -922,7 +925,7 @@ $ kubectl apply -f singleinstancedatabase.yaml singleinstancedatabase.database.oracle.com/sidb-sample configured ``` -Or use the patch command +You can also use the patch command: ```sh $ kubectl --type=merge -p '{"spec":{"convertToSnapshotStandby":true}}' patch singleinstancedatabase sidb-sample @@ -932,42 +935,42 @@ $ kubectl --type=merge -p '{"spec":{"convertToSnapshotStandby":true}}' patch sin ### Static Data Guard Connect String - External and internal (running in pods) applications can always connect to the database in the primary role using `.status.externalConnectString` and `.status.clusterConnectString` of the DataguardBroker resource respectively. These connect strings are fixed for the DataguardBroker resource and will not change on switchover or failover. The external connect string can be obtained using the following command + External and internal (running in pods) applications can always connect to the database in the primary role by using `.status.externalConnectString` and `.status.clusterConnectString` of the Data Guard broker resource respectively. These connect strings are fixed for the Data Guard broker resource, and will not change on switchover or failover. The external connect string can be obtained using the following command: ```sh $ kubectl get dataguardbroker dataguardbroker-sample -o "jsonpath={.status.externalConnectString}" 10.0.25.87:1521/DATAGUARD ``` - The above connect string will always automatically route to the database in the primary role. Client applications can be totally agnostic of the databases in the Data Guard configuration. Their number or host/IP details are not needed in the connect string. + This connect string will always automatically route to the database in the primary role. Client applications can be totally agnostic of the databases in the Oracle Data Guard configuration. Their number or host/IP details are not needed in the connect string. ### Patch Primary and Standby databases Databases (both primary and standby) running in you cluster and managed by the Oracle Database operator can be patched between release updates of the same major release. -To patch an existing database, edit and apply the **[config/samples/sidb/singleinstancedatabase_patch.yaml](../../config/samples/sidb/singleinstancedatabase_patch.yaml)** file of the database resource/object either by specifying a new release update for image attributes, or by running the following command: +To patch an existing database, edit and apply the **[`config/samples/sidb/singleinstancedatabase_patch.yaml`](../../config/samples/sidb/singleinstancedatabase_patch.yaml)** file of the database resource/object either by specifying a new release update for image attributes, or by running the following command: ```sh kubectl --type=merge -p '{"spec":{"image":{"pullFrom":"patched-image:tag","pullSecrets":"pull-secret"}}}' patch singleinstancedatabase ``` -Follow these steps for patching databases configured with the dataguard broker: -1. Ensure Fast-Start Failover is disabled by executing the following command +Follow these steps for patching databases configured with the Data Guard broker: +1. Ensure Fast-Start Failover is disabled by running the following command ```sh kubectl patch dataguardbroker dataguardbroker-sample -p '{"spec":{"fastStartFailover": false}}' --type=merge ``` -2. First patch all the standby databases by replacing the image with the new release update image -3. Perform switch over of the primary to one of the standby databases -4. Now patch the original primary database (currently standby after #2) - After #3 the software for primary and standby databases is at the same release update -5. Now bounce the current primary database by updating the replica count to 0 and then 1 - #4 will trigger a datapatch execution resulting in patching of the datafiles -6. Finally perform switch over of the current primary back to the original primary (current standby) +2. Patch all the standby databases by replacing the image with the new release update image. +3. Perform switchover of the primary to one of the standby databases. +4. Patch the original primary database (currently standby after #2) + After step 3, the software for primary and standby databases is at the same release update +5. Bounce the current primary database by updating the replica count to 0 and then 1 + Step 5 will trigger a datapatch execution, which results in patching the datafiles +6. Finally, perform switch over of the current primary back to the original primary (current standby) ### Delete the Data Guard Configuration -To delete a standby or primary database configured for Data Guard, delete the dataguardbroker resource first followed by the standby databases and finally the primary database +To delete a standby or primary database configured for Oracle Data Guard, delete the `dataguardbroker` resource. After that is done, delete the standby databases, and then finally the primary database. #### Delete DataguardBroker Resource ```sh @@ -976,8 +979,7 @@ $ kubectl delete dataguardbroker dgbroker-sample dataguardbroker.database.oracle.com/dgbroker-sample deleted ``` -**Note:** If a switch over to standby was performed, make sure to switch back to the original primary database before deleting the dataguard broker resource - +**Note:** If a switchover to standby was performed, then ensure that you switch back to the original primary database before deleting the Data Guard broker resource. For example: #### Delete Standby Database ```sh $ kubectl delete singleinstancedatabase stdby-1 @@ -987,15 +989,15 @@ $ kubectl delete singleinstancedatabase stdby-1 ### Execute Custom Scripts -Custom scripts (sql and/or shell scripts) can be executed after the initial database setup and/or after each startup of the database. SQL scripts will be executed as sysdba, shell scripts will be executed as the current user. To ensure proper order it is recommended to prefix your scripts with a number. For example `01_users.sql`, `02_permissions.sql`, etc. Place all such scripts in setup and startup folders created in a persistent volume to execute them post setup and post startup respectively. +You can set up custom scripts (SQL, shell scripts, or both) to run after the initial database setup, and to have scripts run after each startup of the database. SQL scripts will be executed as `sysdba`, and shell scripts will be executed as the current user. To ensure proper order, Oracle recommends that you prefix your scripts with a number. For example: `01_users.sql`, `02_permissions.sql`, and son on. To ensure that these scripts are available to run after setup or after each database startup, place all such scripts in setup and startup folders created in a persistent volume. -Create a persistent volume using [static provisioning](#static-persistence) and then specify the name of this volume with the `<.spec.persistence.scriptsVolumeName>` field which corresponds to the `scriptsVolumeName` field of the persistence section in the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)**. +Create a persistent volume by using [static provisioning](#static-persistence) and then specify the name of this volume with the `<.spec.persistence.scriptsVolumeName>` field which corresponds to the `scriptsVolumeName` field of the persistence section in the **[`singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml)**. -**Note:** Executing custom scripts requires read and list access for persistent volumes as mentioned in [prerequisites](#prerequisites) +**Note:** Running custom scripts requires read and list access for persistent volumes, as mentioned in [prerequisites](#prerequisites) ## OracleRestDataService Resource -The Oracle Database Operator creates the `OracleRestDataService` as a custom resource. We will refer `OracleRestDataService` as ORDS from now onwards. Creating ORDS as a custom resource enables the RESTful API access to the Oracle Database in K8s and enables it to be managed as a native Kubernetes object. +The Oracle Database Operator creates the `OracleRestDataService` as a custom resource. In this documeent, we will refer to `OracleRestDataService` as ORDS. Creating ORDS as a custom resource enables the RESTful API access to the Oracle Database in K8s, and enables it to be managed as a native Kubernetes object. ### Resource Details @@ -1061,13 +1063,13 @@ The template `.yaml` file for Oracle Rest Data Services (`OracleRestDataService` **Note:** - The `adminPassword` and `ordsPassword` fields in the `oraclerestdataservice.yaml` file contains secrets for authenticating the Single Instance Database and the ORDS user with the following roles: `SQL Administrator, System Administrator, SQL Developer, oracle.dbtools.autorest.any.schema`. -- If you want to install ORDS in a [prebuilt database](#provision-a-pre-built-database), make sure to attach the **database persistence** by uncommenting the `persistence` section in the **[config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml)** file, while provisioning the prebuilt database. +- If you want to install ORDS in a [prebuilt database](#provision-a-pre-built-database), then ensure that you attach the **database persistence** by uncommenting the `persistence` section in the **[`config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml`](../../config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml)** file, while provisioning the prebuilt database. ### REST Enable a Database #### Provision ORDS -To quickly provision a new ORDS instance, use the sample **[config/samples/sidb/oraclerestdataservice_create.yaml](../../config/samples/sidb/oraclerestdataservice_create.yaml)** file. For example: +To quickly provision a new ORDS instance, use the example **[`config/samples/sidb/oraclerestdataservice_create.yaml`](../../config/samples/sidb/oraclerestdataservice_create.yaml)** file. For example: ```sh $ kubectl apply -f oraclerestdataservice_create.yaml @@ -1077,13 +1079,13 @@ $ kubectl apply -f oraclerestdataservice_create.yaml After this command completes, ORDS is installed in the container database (CDB) of the Single Instance Database. ##### Note: -You are required to specify the ORDS secret in the [oraclerestdataservice_create.yaml](../../config/samples/sidb/oraclerestdataservice_create.yaml) file. The default value mentioned in the `adminPassword.secretName` field is `ords-secret`. You can create this secret manually by using the following command: +You are required to specify the ORDS Secret in the [`oraclerestdataservice_create.yaml`](../../config/samples/sidb/oraclerestdataservice_create.yaml) file. The default value mentioned in the `adminPassword.secretName` field is `ords-secret`. You can create this Secret manually by using the following command: ```bash kubectl create secret generic ords-secret --from-literal=oracle_pwd= ``` -Alternatively, you can create this secret by filling the passwords in the **[oraclerestdataservice_secrets.yaml](../../config/samples/sidb/oraclerestdataservice_secrets.yaml)** file and applying it using the command below: +Alternatively, you can create this Secret by filling the passwords in the **[`oraclerestdataservice_secrets.yaml`](../../config/samples/sidb/oraclerestdataservice_secrets.yaml)** file and applying it using the following command: ```bash kubectl apply -f singleinstancedatabase_secrets.yaml @@ -1116,7 +1118,7 @@ There are two basic approaches for authentication to the REST Endpoints. Certain #### Database API -To call certain REST endpoints, you must use the Schema User which is Rest Enabled with role `SQL Administrator`, and `.spec.ordsPassword` credentials. +To call certain REST endpoints, you must use the Schema User, which is REST-Enabled with role `SQL Administrator`, and `.spec.ordsPassword` credentials. The Schema user also has the following additional roles: `System Administrator, SQL Developer`. @@ -1126,7 +1128,7 @@ Use this Schema user to authenticate the following: * Database Actions of any REST Enabled Schema ##### Examples -Some examples for the Database API usage for REST Enabled schema1 are as follows: +Some examples for the Database API usage for REST-Enabled schema1 are as follows: - **Get all Database Components** ```sh curl -s -k -X GET -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' http://10.0.25.54:8181/ords/schema1/_/db-api/stable/database/components/ | python -m json.tool @@ -1150,7 +1152,7 @@ Some examples for the Database API usage for REST Enabled schema1 are as follows #### MongoDB API -To enable the Database API for MongoDB set `.spec.mongoDbApi` to `true`. Thus MongoDB applications would now be able to connect to Oracle Database using the MongoDB API Access URL. +To enable the Database API for MongoDB, set `.spec.mongoDbApi` to `true`. When this is done, MongoDB applications are be able to connect to Oracle Database using the MongoDB API Access URL. For example: ```sh $ kubectl get oraclerestdataservice/ords-sample -o "jsonpath={.status.mongoDbApiAccessUrl}" @@ -1169,10 +1171,9 @@ The Oracle REST Data Services (ORDS) database API enables you to create Oracle D REST APIs for Oracle Data Pump Jobs can be found at [https://docs.oracle.com/en/database/oracle/oracle-database/21/dbrst/op-database-datapump-jobs-post.html](https://docs.oracle.com/en/database/oracle/oracle-database/21/dbrst/op-database-datapump-jobs-post.html). ##### REST Enabled SQL -The REST Enable SQL functionality is available to all the schemas specified in the `.spec.restEnableSchemas` attribute of the sample yaml. -Only these schemas will have access SQL Developer Web Console specified by the Database Actions URL. +The REST-Enabled SQL functionality is available to all of the schemas specified in the `.spec.restEnableSchemas` attribute of the example yaml in the sample folder. Only these schemas will have access SQL Developer Web Console specified by the Database Actions URL. -The REST Enabled SQL functionality enables REST calls to send DML, DDL and scripts to any REST enabled schema by exposing the same SQL engine used in SQL Developer and Oracle SQLcl (SQL Developer Command Line). +The REST-Enabled SQL functionality enables REST calls to send DML, DDL and scripts to any REST-Enabled schema by exposing the same SQL engine used in SQL Developer and Oracle SQLcl (SQL Developer Command Line). For example: @@ -1233,7 +1234,7 @@ $ kubectl get oraclerestdataservice/ords-sample -o "jsonpath={.status.databaseAc http://10.0.25.54:8181/ords/sql-developer ``` -To access Database Actions, sign in by using the following code as a database user whose schema has been REST-enabled: +To access Database Actions, sign in by using the following code as a database user whose schema has been REST-Enabled: * Login Page: \ Username: `.spec.restEnableSchemas[].schemaName` \ @@ -1275,11 +1276,11 @@ password: `Welcome_1` ![application-express-admin-home](/images/sidb/application-express-admin-home.png) **Note:** -- It is strongly recommend that default apex admin password is changed. +- Oracle strongly recommends that you change the default APEX admin password. - By default, the full development environment is initialized in APEX. After deployment, you can change it manually to the runtime environment. To change environments, run the script `apxdevrm.sql` after connecting to the primary database from the ORDS pod as the `SYS` user with `SYSDBA` privilege. For detailed instructions, see: [Converting a Full Development Environment to a Runtime Environment](https://docs.oracle.com/en/database/oracle/application-express/21.2/htmig/converting-between-runtime-and-full-development-environments.html#GUID-B0621B40-3441-44ED-9D86-29B058E26BE9). ### Delete ORDS -- To delete ORDS run the following command: +- To delete ORDS, run the following command: kubectl delete oraclerestdataservice ords-sample From 0104f9116cc66de9aeb5417d3ff27b244067ee6d Mon Sep 17 00:00:00 2001 From: ishaan_desai Date: Thu, 13 Feb 2025 10:13:22 +0000 Subject: [PATCH 199/414] fixing fsfo status bug --- apis/database/v1alpha1/dataguardbroker_types.go | 2 +- apis/database/v1alpha1/dataguardbroker_webhook.go | 5 +++-- apis/database/v4/dataguardbroker_types.go | 2 +- .../database/singleinstancedatabase_controller.go | 2 +- controllers/dataguard/dataguard_utils.go | 4 +++- controllers/dataguard/dataguardbroker_controller.go | 10 ++++++---- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/apis/database/v1alpha1/dataguardbroker_types.go b/apis/database/v1alpha1/dataguardbroker_types.go index 416c2ce4..768d6dd3 100644 --- a/apis/database/v1alpha1/dataguardbroker_types.go +++ b/apis/database/v1alpha1/dataguardbroker_types.go @@ -75,7 +75,7 @@ type DataguardBrokerStatus struct { ClusterConnectString string `json:"clusterConnectString,omitempty"` Status string `json:"status,omitempty"` - FastStartFailover bool `json:"fastStartFailover,omitempty"` + FastStartFailover string `json:"fastStartFailover,omitempty"` DatabasesInDataguardConfig map[string]string `json:"databasesInDataguardConfig,omitempty"` } diff --git a/apis/database/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go index 4b470665..89a9d3fd 100644 --- a/apis/database/v1alpha1/dataguardbroker_webhook.go +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + "strconv" "strings" dbcommons "github.com/oracle/oracle-database-operator/commons/database" @@ -158,8 +159,8 @@ func (r *DataguardBroker) ValidateUpdate(old runtime.Object) (admission.Warnings allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("primaryDatabaseRef"), "cannot be changed")) } - - if (oldObj.Status.FastStartFailover || r.Spec.FastStartFailover) && r.Spec.SetAsPrimaryDatabase != "" { + fastStartFailoverStatus, _ := strconv.ParseBool(oldObj.Status.FastStartFailover) + if (fastStartFailoverStatus || r.Spec.FastStartFailover) && r.Spec.SetAsPrimaryDatabase != "" { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("setAsPrimaryDatabase"), "switchover not supported when fastStartFailover is true")) } diff --git a/apis/database/v4/dataguardbroker_types.go b/apis/database/v4/dataguardbroker_types.go index 9abc54ac..cec11ca4 100644 --- a/apis/database/v4/dataguardbroker_types.go +++ b/apis/database/v4/dataguardbroker_types.go @@ -75,7 +75,7 @@ type DataguardBrokerStatus struct { ClusterConnectString string `json:"clusterConnectString,omitempty"` Status string `json:"status,omitempty"` - FastStartFailover bool `json:"fastStartFailover,omitempty"` + FastStartFailover string `json:"fastStartFailover,omitempty"` DatabasesInDataguardConfig map[string]string `json:"databasesInDataguardConfig,omitempty"` } diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index c34105a0..13f2ec6f 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -3329,7 +3329,7 @@ func convertPhysicalStdToSnapshotStdDB(r *SingleInstanceDatabaseReconciler, sing return err } log.Info(fmt.Sprintf("database %s is configured with dgbroker %s", singleInstanceDatabase.Name, *singleInstanceDatabase.Status.DgBroker)) - if dataguardBroker.Status.FastStartFailover { + if fastStartFailoverStatus, _ := strconv.ParseBool(dataguardBroker.Status.FastStartFailover); fastStartFailoverStatus { // not allowed to convert to snapshot standby return ErrFSFOEnabledForDGConfig } diff --git a/controllers/dataguard/dataguard_utils.go b/controllers/dataguard/dataguard_utils.go index 371d23f9..4c16f82b 100644 --- a/controllers/dataguard/dataguard_utils.go +++ b/controllers/dataguard/dataguard_utils.go @@ -42,6 +42,7 @@ import ( "context" "errors" "fmt" + "strconv" "strings" "time" @@ -177,7 +178,8 @@ func validateSidbReadiness(r *DataguardBrokerReconciler, broker *dbapi.Dataguard out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", fmt.Sprintf(dbcommons.ValidateAdminPassword, adminPassword), dbcommons.GetSqlClient(sidb.Spec.Edition))) if err != nil { - if strings.Contains(err.Error(), "dialing backend") && broker.Status.Status == dbcommons.StatusReady && broker.Status.FastStartFailover { + fastStartFailoverStatus, _ := strconv.ParseBool(broker.Status.FastStartFailover) + if strings.Contains(err.Error(), "dialing backend") && broker.Status.Status == dbcommons.StatusReady && fastStartFailoverStatus { // Connection to the pod is failing after broker came up and running // Might suggest disconnect or pod/vm going down log.Info("Dialing connection error") diff --git a/controllers/dataguard/dataguardbroker_controller.go b/controllers/dataguard/dataguardbroker_controller.go index c321d8ab..4d7ae044 100644 --- a/controllers/dataguard/dataguardbroker_controller.go +++ b/controllers/dataguard/dataguardbroker_controller.go @@ -42,6 +42,7 @@ import ( "context" "errors" "fmt" + "strconv" "time" "github.com/go-logr/logr" @@ -104,7 +105,7 @@ func (r *DataguardBrokerReconciler) Reconcile(ctx context.Context, req ctrl.Requ dataguardBroker.Status.Status = dbcommons.StatusCreating dataguardBroker.Status.ExternalConnectString = dbcommons.ValueUnavailable dataguardBroker.Status.ClusterConnectString = dbcommons.ValueUnavailable - dataguardBroker.Status.FastStartFailover = false + dataguardBroker.Status.FastStartFailover = "false" if len(dataguardBroker.Status.DatabasesInDataguardConfig) == 0 { dataguardBroker.Status.DatabasesInDataguardConfig = map[string]string{} } @@ -154,7 +155,7 @@ func (r *DataguardBrokerReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // set faststartfailover status to true - dataguardBroker.Status.FastStartFailover = true + dataguardBroker.Status.FastStartFailover = "true" } else { @@ -178,7 +179,7 @@ func (r *DataguardBrokerReconciler) Reconcile(ctx context.Context, req ctrl.Requ log.Info("database observer deleted") // set faststartfailover status to false - dataguardBroker.Status.FastStartFailover = false + dataguardBroker.Status.FastStartFailover = "false" } // manage manual switchover @@ -389,7 +390,8 @@ func (r *DataguardBrokerReconciler) manageDataguardBrokerCreation(broker *dbapi. log.Info(fmt.Sprintf("Validating readiness for singleinstancedatabase %v", sidb.Name)) if err := validateSidbReadiness(r, broker, &sidb, ctx, req); err != nil { if errors.Is(err, ErrCurrentPrimaryDatabaseNotReady) { - if broker.Status.Status != "" && broker.Status.FastStartFailover { + fastStartFailoverStatus, _ := strconv.ParseBool(broker.Status.FastStartFailover) + if broker.Status.Status != "" && fastStartFailoverStatus { r.Recorder.Eventf(broker, corev1.EventTypeNormal, "Possible Failover", "Primary db not in ready state after setting up DG configuration") } if err := updateReconcileStatus(r, broker, ctx, req); err != nil { From d202560c3bd51f49070d52ee4ff84e946b236135 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Thu, 13 Feb 2025 18:03:44 +0000 Subject: [PATCH 200/414] doc changes to Oracle Base Database Service --- README.md | 8 +- docs/dbcs/README.md | 81 +++++++++---------- .../bind_to_existing_dbcs_system.md | 14 ++-- .../provisioning/clone_from_backup_dbcs.md | 18 ++--- docs/dbcs/provisioning/clone_from_database.md | 16 ++-- .../provisioning/clone_from_existing_dbcs.md | 8 +- .../dbcs/provisioning/create_dbcs_with_kms.md | 22 ++--- .../dbcs/provisioning/create_dbcs_with_pdb.md | 18 ++--- .../create_pdb_to_existing_dbcs_system.md | 20 ++--- .../dbcs_service_with_2_node_rac.md | 26 +++--- .../dbcs_service_with_all_parameters_asm.md | 28 +++---- .../dbcs_service_with_all_parameters_lvm.md | 28 +++---- .../dbcs_service_with_minimal_parameters.md | 14 ++-- docs/dbcs/provisioning/migrate_to_kms.md | 24 +++--- .../scale_down_dbcs_system_shape.md | 22 ++--- .../scale_up_dbcs_system_shape.md | 22 ++--- docs/dbcs/provisioning/scale_up_storage.md | 22 ++--- .../provisioning/terminate_dbcs_system.md | 24 +++--- docs/dbcs/provisioning/update_license.md | 22 ++--- 19 files changed, 215 insertions(+), 222 deletions(-) diff --git a/README.md b/README.md index 746c58ef..d0d236e7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ In this v1.2.0 production release, `OraOperator` supports the following database * Containerized Single Instance databases (SIDB) deployed in the Oracle Kubernetes Engine (OKE) and any k8s where OraOperator is deployed * Containerized Sharded databases (SHARDED) deployed in OKE and any k8s where OraOperator is deployed * Oracle Multitenant Databases (CDB/PDBs) -* Oracle Base Database Cloud Service (BDBCS) +* Oracle Base Database Service (OBDS) on Oracle Cloud Infrastructure (OCI) * Oracle Data Guard * Oracle Database Observability * Oracle Database Rest Service (ORDS) instances @@ -41,7 +41,7 @@ In this v1.2.0 production release, `OraOperator` supports the following database - Support for Database Logs (in addition to Metrics) - Support for the latest Exporter container images -* Base DB: support for Oracle Database 23ai Cloning +* Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation. * Prometheus label config (bug fix) ## New Product Features @@ -60,7 +60,7 @@ This release of Oracle Database Operator for Kubernetes (the operator) supports * ORDS Services: Provision and delete ORDS instances * Globally Distrib. (Sharded): Provision/deploy sharded databases and the shard topology, Add a new shard, Delete an existing shard, Raft replication. * Oracle Multitenant Database (choice of controller): Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB, Assertive deletion policy -* Oracle Base Database Cloud Service (BDBCS): Provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License, Cloning. +* Oracle Base Database Service (OBDS): Provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License, Cloning, PDB creation, using KMS Vaults on Oracle Cloud Infrastructure (OCI) * Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration * Oracle Database Observability: create, patch, delete `databaseObserver` resources (Logs and Metrics) * Watch over a set of namespaces or all the namespaces in the cluster using the `WATCH_NAMESPACE` environment variable of the operator deployment @@ -180,7 +180,7 @@ The following quickstarts are designed for specific database configurations: * [Containerized Oracle Single Instance Database and Data Guard](./docs/sidb/README.md) * [Containerized Oracle Sharded Database](./docs/sharding/README.md) * [Oracle Multitenant Database](./docs/multitenant/README.md) -* [Oracle Base Database Cloud Service (BDBCS)](./docs/dbcs/README.md) +* [Oracle Base Database Service (OBDS)](./docs/dbcs/README.md) The following quickstart is designed for non-database configurations: diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md index 76620a4d..2c06511c 100644 --- a/docs/dbcs/README.md +++ b/docs/dbcs/README.md @@ -1,12 +1,12 @@ -# Using the DB Operator DBCS Controller +# Using the DB Operator Oracle Base Database Service (OBDS) Controller -Oracle Cloud Infastructure (OCI) Oracle Base Database Cloud Service (BDBCS) provides single-node Database (DB) systems, deployed on virtual machines, and provides two-node Oracle Real Appliation Clusters (Oracle RAC) database systems on virtual machines. +Oracle Cloud Infastructure (OCI) Oracle Base Database Service (OBDS) provides single-node Database (DB) systems, deployed on virtual machines, and provides two-node Oracle Real Application Clusters (Oracle RAC) database systems on virtual machines. -The single-node DB systems and Oracle RAC systems on virtual machines are [co-managed Oracle Database cloud solutions](https://docs.oracle.com/en-us/iaas/Content/Database/Concepts/overview.htm). To manage the lifecycle of an OCI DBCS system, you can use the OCI Console, the REST API, or the Oracle Cloud Infrastructure command-line interface (CLI). At the granular level, you can use the Oracle Database CLI (DBCLI), Oracle Enterprise Manager, or Oracle SQL Developer. +The single-node DB systems and Oracle RAC systems on virtual machines are [co-managed Oracle Database cloud solutions](https://docs.oracle.com/en-us/iaas/Content/Database/Concepts/overview.htm). To manage the lifecycle of an OCI OBDS system, you can use the OCI Console, the REST API, or the Oracle Cloud Infrastructure command-line interface (CLI). At the granular level, you can use the Oracle Database CLI (DBCLI), Oracle Enterprise Manager, or Oracle SQL Developer. -The Oracle DB Operator DBCS Controller is a feature of the Oracle DB Operator for Kubernetes (OraOperator) which uses OCI's BDBCS service to support lifecycle management of the database systems. +The Oracle DB Operator Oracle Base Database Service (OBDS) Controller is a feature of the Oracle DB Operator for Kubernetes (OraOperator) which uses OCI's Oracle Base Database Service OBDS service to support lifecycle management of the database systems. -Note: Oracle Base Database Cloud Service (BDBCS) was previously known as Database Cloud Service (DBCS). +Note: Oracle Base Database Cloud Service (OBDS) was previously known as Database Cloud Service (DBCS). # Supported Database Editions and Versions @@ -24,25 +24,18 @@ For standard provisioning of DB systems (using Oracle Automatic Storage Manageme - Oracle Database 23ai - Oracle Database 19c -- Oracle Database 18c (18.0) -- Oracle Database 12c Release 2 (12.2) -- Oracle Database 12c Release 1 (12.1) -- Oracle Database 11g Release 2 (11.2) - For fast provisioning of single-node virtual machine database systems (using Logical Volume Manager as your storage management software), the following database releases are supported: - Oracle Database 23ai - Oracle Database 19c -- Oracle Database 18c -- Oracle Database 12c Release 2 (12.2) -# Oracle DB Operator DBCS Controller Deployment +# Oracle DB Operator Oracle Base Database Service (OBDS) Controller Deployment To deploy Oracle Database Operator (`OraOperator`), use the [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. -After the Oracle Database Operator is deployed, you can see the DB operator pods running in the Kubernetes Cluster. As part of the `OraOperator` deployment, the DBCS Controller is deployed as a CRD (Custom Resource Definition). The following screen output is an example of such a deployment: +After the Oracle Database Operator is deployed, you can see the DB operator pods running in the Kubernetes Cluster. As part of the `OraOperator` deployment, the OBDS Controller is deployed as a CRD (Custom Resource Definition). The following screen output is an example of such a deployment: ```bash [root@test-server oracle-database-operator]# kubectl get ns NAME STATUS AGE @@ -80,7 +73,7 @@ certificaterequests.cert-manager.io 2022-02-22T23:21:35Z certificates.cert-manager.io 2022-02-22T23:21:36Z challenges.acme.cert-manager.io 2022-02-22T23:21:36Z clusterissuers.cert-manager.io 2022-02-22T23:21:36Z -dbcssystems.database.oracle.com 2022-02-22T23:23:25Z <<<< CRD for DBCS Controller +dbcssystems.database.oracle.com 2022-02-22T23:23:25Z <<<< CRD for OBDS Controller issuers.cert-manager.io 2022-02-22T23:21:36Z orders.acme.cert-manager.io 2022-02-22T23:21:37Z shardingdatabases.database.oracle.com 2022-02-22T23:23:25Z @@ -88,9 +81,9 @@ singleinstancedatabases.database.oracle.com 2022-02-22T23:23:25Z ``` -# Prerequisites to deploy a DBCS system using Oracle DB Operator DBCS Controller +# Prerequisites to deploy a OBDS system using Oracle DB Operator OBDS Controller -Before you deploy a DBCS system in OCI using the Oracle DB Operator DBCS Controller, complete the following procedure. +Before you deploy a OBDS system in OCI using the Oracle DB Operator OBDS Controller, complete the following procedure. **CAUTION :** You must make the changes specified in this section before you proceed to the next section. @@ -114,7 +107,7 @@ kubectl create secret generic oci-privatekey --from-file=privatekey=/root/.oci/o ``` -## 3. Create a Kubernetes secret named `admin-password`; This passward must meet the minimum passward requirements for the OCI BDBCS Service. +## 3. Create a Kubernetes secret named `admin-password`; This passward must meet the minimum passward requirements for the OCI OBDS Service. For example: ```bash @@ -124,7 +117,7 @@ kubectl create secret generic admin-password --from-file=./admin-password -n def ``` -## 4. Create a Kubernetes secret named `tde-password`; this passward must meet the minimum passward requirements for the OCI BDBCS Service. +## 4. Create a Kubernetes secret named `tde-password`; this passward must meet the minimum passward requirements for the OCI OBDS Service. For example: ```bash @@ -134,10 +127,10 @@ kubectl create secret generic tde-password --from-file=./tde-password -n default ``` -## 5. Create an SSH key pair, and use its public key to create a Kubernetes secret named `oci-publickey`; the private key for this public key can be used later to access the DBCS system's host machine using SSH: +## 5. Create an SSH key pair, and use its public key to create a Kubernetes secret named `oci-publickey`; the private key for this public key can be used later to access the OBDS system's host machine using SSH: ```bash -[root@test-server DBCS]# ssh-keygen -N "" -C "DBCS_System"-`date +%Y%m` -P "" +[root@test-server OBDS]# ssh-keygen -N "" -C "DBCS_System"-`date +%Y%m` -P "" Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Your identification has been saved in /root/.ssh/id_rsa. @@ -158,35 +151,35 @@ The key's randomart image is: +----[SHA256]-----+ -[root@test-server DBCS]# kubectl create secret generic oci-publickey --from-file=publickey=/root/DBCS/id_rsa.pub +[root@test-server OBDS]# kubectl create secret generic oci-publickey --from-file=publickey=/root/DBCS/id_rsa.pub ``` -# Use Cases to manage the lifecycle of an OCI DBCS System with Oracle DB Operator DBCS Controller +# Use Cases to manage the lifecycle of an OCI OBDS System with Oracle DB Operator OBDS Controller -For more informatoin about the multiple use cases available to you to deploy and manage the OCI BDBCS Service-based database using the Oracle DB Operator DBCS Controller, review this list: +For more informatoin about the multiple use cases available to you to deploy and manage the OCI OBDS Service-based database using the Oracle DB Operator OBDS Controller, review this list: -[1. Deploy a DB System using OCI BDBCS Service with minimal parameters](./provisioning/dbcs_service_with_minimal_parameters.md) -[2. Binding to an existing DBCS System already deployed in OCI BDBCS Service](./provisioning/bind_to_existing_dbcs_system.md) -[3. Scale UP the shape of an existing BDBCS System](./provisioning/scale_up_dbcs_system_shape.md) -[4. Scale DOWN the shape of an existing BDBCS System](./provisioning/scale_down_dbcs_system_shape.md) -[5. Scale UP the storage of an existing BDBCS System](./provisioning/scale_up_storage.md) -[6. Update License type of an existing BDBCS System](./provisioning/update_license.md) -[7. Terminate an existing BDBCS System](./provisioning/terminate_dbcs_system.md) -[8. Create BDBCS with All Parameters with Storage Management as LVM](./provisioning/dbcs_service_with_all_parameters_lvm.md) -[9. Create BDBCS with All Parameters with Storage Management as ASM](./provisioning/dbcs_service_with_all_parameters_asm.md) -[10. Deploy a 2 Node RAC DB System using OCI BDBCS Service](./provisioning/dbcs_service_with_2_node_rac.md) -[11. Create PDB to an existing DBCS System already deployed in OCI Base DBCS Service](./provisioning/create_pdb_to_existing_dbcs_system.md) -[12. Create Base DBCS with PDB in OCI](./provisioning/create_dbcs_with_pdb.md) -[13. Create Base DBCS with KMS Vault Encryption in OCI](./provisioning/create_dbcs_with_kms.md) -[14. Migrate to KMS vault from TDE Wallet password encryption of an existing DBCS System already deployed in OCI Base DBCS Service](./provisioning/migrate_to_kms.md) -[15. Clone DB System from Existing DB System in OCI Base DBCS Service](./provisioning/clone_from_existing_dbcs.md) -[16. Clone DB System from Backup of Existing DB System in OCI Base DBCS Service](./provisioning/clone_from_backup_dbcs.md) -[17. Clone DB System from Existing Database of DB System in OCI Base DBCS Service](./provisioning/clone_from_database.md) +[1. Deploy a DB System using OCI OBDS Service with minimal parameters](./provisioning/dbcs_service_with_minimal_parameters.md) +[2. Binding to an existing OBDS System already deployed in OCI Oracle Base Database Service](./provisioning/bind_to_existing_dbcs_system.md) +[3. Scale UP the shape of an existing OBDS System](./provisioning/scale_up_dbcs_system_shape.md) +[4. Scale DOWN the shape of an existing OBDS System](./provisioning/scale_down_dbcs_system_shape.md) +[5. Scale UP the storage of an existing OBDS System](./provisioning/scale_up_storage.md) +[6. Update License type of an existing OBDS System](./provisioning/update_license.md) +[7. Terminate an existing OBDS System](./provisioning/terminate_dbcs_system.md) +[8. Create OBDS with All Parameters with Storage Management as LVM](./provisioning/dbcs_service_with_all_parameters_lvm.md) +[9. Create OBDS with All Parameters with Storage Management as ASM](./provisioning/dbcs_service_with_all_parameters_asm.md) +[10. Deploy a 2 Node RAC DB System using OCI OBDS Service](./provisioning/dbcs_service_with_2_node_rac.md) +[11. Create PDB to an existing OBDS System already deployed in OCI OBDS Service](./provisioning/create_pdb_to_existing_dbcs_system.md) +[12. Create OBDS with PDB in OCI](./provisioning/create_dbcs_with_pdb.md) +[13. Create OBDS with KMS Vault Encryption in OCI](./provisioning/create_dbcs_with_kms.md) +[14. Migrate to KMS vault from TDE Wallet password encryption of an existing OBDS System already deployed in OCI Base OBDS Service](./provisioning/migrate_to_kms.md) +[15. Clone DB System from Existing DB System in OCI OBDS Service](./provisioning/clone_from_existing_dbcs.md) +[16. Clone DB System from Backup of Existing DB System in OCI OBDS Service](./provisioning/clone_from_backup_dbcs.md) +[17. Clone DB System from Existing Database of DB System in OCI OBDS Service](./provisioning/clone_from_database.md) -## Connecting to OCI DBCS database deployed using Oracle DB Operator DBCS Controller +## Connecting to OCI OBDS database deployed using Oracle DB Operator OBDS Controller -After you have deployed the OCI BDBCS database with the Oracle DB Operator DBCS Controller, you can connect to the database. To see how to connect and use the database, refer to the steps in [Database Connectivity](./provisioning/database_connection.md). +After you have deployed the OCI OBDS database with the Oracle DB Operator OBDS Controller, you can connect to the database. To see how to connect and use the database, refer to the steps in [Database Connectivity](./provisioning/database_connection.md). ## Known Issues -If you encounter any issues with deployment, refer to the list of [Known Issues](./provisioning/known_issues.md) for an OCI DBCS System deployed using Oracle DB Operator DBCS Controller. +If you encounter any issues with deployment, refer to the list of [Known Issues](./provisioning/known_issues.md) for an OCI OBDS System deployed using Oracle DB Operator OBDS Controller. diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md index 099902bc..eced7538 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md @@ -1,14 +1,14 @@ -# Binding to an existing DBCS System already deployed in OCI DBCS Service +# Binding to an existing OBDS System already deployed in OCI Oracle Base Database Service -In this use case, we bind the Oracle DB Operator DBCS Controller to an existing OCI DBCS System which has already been deployed earlier. This will help to manage the life cycle of that DBCS System using the Oracle DB Operator DBCS Controller. +In this use case, we bind the Oracle DB Operator OBDS Controller to an existing OCI OBDS System which has already been deployed earlier. This will help to manage the life cycle of that OBDS System using the Oracle DB Operator OBDS Controller. **NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `bind_to_existing_dbcs_system.yaml` to bind to an existing DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `bind_to_existing_dbcs_system.yaml` to bind to an existing OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` -- OCID of the existing DBCS System as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` +- OCID of the existing OBDS System as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` Use the file: [bind_to_existing_dbcs_system.yaml](./bind_to_existing_dbcs_system.yaml) for this use case as below: @@ -19,14 +19,14 @@ kubectl apply -f bind_to_existing_dbcs_system.yaml dbcssystem.database.oracle.com/dbcssystem-existing created ``` -2. Monitor the Oracle DB Leader Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. +2. Monitor the Oracle DB Leader Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. ```bash -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./bind_to_existing_dbcs_system_sample_output.log) is the sample output for binding to an existing DBCS System already deployed in OCI using Oracle DB Operator DBCS Controller. +[Here](./bind_to_existing_dbcs_system_sample_output.log) is the sample output for binding to an existing OBDS System already deployed in OCI using Oracle DB Operator OBDS Controller. diff --git a/docs/dbcs/provisioning/clone_from_backup_dbcs.md b/docs/dbcs/provisioning/clone_from_backup_dbcs.md index 80378398..4597cff7 100644 --- a/docs/dbcs/provisioning/clone_from_backup_dbcs.md +++ b/docs/dbcs/provisioning/clone_from_backup_dbcs.md @@ -1,17 +1,17 @@ -# Clone DB System from Backup of Existing DB System in OCI Base DBCS Service +# Clone DB System from Backup of Existing DB System in OCI Oracle Base Database System (OBDS) -In this use case, an existing OCI DBCS system deployed earlier with the Backup is going to be cloned. +In this use case, an existing OCI OBDS system deployed earlier with the Backup is going to be cloned. -In order to clone DBCS to an existing DBCS system using Backup, get the details of OCID of backup in OCI DBCS. +In order to clone OBDS to an existing OBDS system using Backup, get the details of OCID of backup in OCI OBDS. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `clone_dbcs_system_from_backup.yaml` to clone a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `clone_dbcs_system_from_backup.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - setupDBCloning: as `true` -- OCID of Backup DB as `dbBackupId` of existing DBCS system. +- OCID of Backup DB as `dbBackupId` of existing OBDS system. - Specification for DB Cloning as `dbClone`-> `dbAdminPasswordSecret`,`tdeWalletPasswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`, `initialDataStorageSizeInGB` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). @@ -19,18 +19,18 @@ Use the file: [clone_dbcs_system_from_backup.yaml](./clone_dbcs_system_from_back 1. Deploy the .yaml file: ```sh -[root@docker-test-server DBCS]# kubectl apply -f clone_dbcs_system_from_backup.yaml +[root@docker-test-server OBDS]# kubectl apply -f clone_dbcs_system_from_backup.yaml dbcssystem.database.oracle.com/dbcssystem-clone created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of PDBs. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./clone_dbcs_system_from_backup_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +[Here](./clone_dbcs_system_from_backup_sample_output.log) is the sample output for cloning an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. diff --git a/docs/dbcs/provisioning/clone_from_database.md b/docs/dbcs/provisioning/clone_from_database.md index 758f0739..05b294b5 100644 --- a/docs/dbcs/provisioning/clone_from_database.md +++ b/docs/dbcs/provisioning/clone_from_database.md @@ -1,12 +1,12 @@ -# Clone DB System from Existing Database of DB System in OCI Base DBCS Service +# Clone DB System from Existing Database of DB System in OCI Oracle Base Database System (OBDS) -In this use case, an existing OCI DBCS system deployed earlier with existing Database is going to be cloned in OCI Base DBCS Service using existing Database ID. +In this use case, an existing OCI OBDS system deployed earlier with existing Database is going to be cloned in OCI Base OBDS Service using existing Database ID. -As an pre-requisite, get the details of OCID of database of an existing DBCS System which you want to clone. +As an pre-requisite, get the details of OCID of database of an existing OBDS System which you want to clone. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `clone_dbcs_system_from_database.yaml` to clone a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `clone_dbcs_system_from_database.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCID of existing as `databaseId` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` @@ -18,18 +18,18 @@ Use the file: [clone_dbcs_system_from_database.yaml](./clone_dbcs_system_from_da 1. Deploy the .yaml file: ```sh -[root@docker-test-server DBCS]# kubectl apply -f clone_dbcs_system_from_database.yaml +[root@docker-test-server OBDS]# kubectl apply -f clone_dbcs_system_from_database.yaml dbcssystem.database.oracle.com/dbcssystem-clone created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of PDBs. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./clone_dbcs_system_from_database_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +[Here](./clone_dbcs_system_from_database_sample_output.log) is the sample output for cloning an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. diff --git a/docs/dbcs/provisioning/clone_from_existing_dbcs.md b/docs/dbcs/provisioning/clone_from_existing_dbcs.md index 383c9887..61665188 100644 --- a/docs/dbcs/provisioning/clone_from_existing_dbcs.md +++ b/docs/dbcs/provisioning/clone_from_existing_dbcs.md @@ -1,12 +1,12 @@ -# Clone DB System from Existing DB System in OCI Base DBCS Service +# Clone DB System from Existing DB System in OCI Oracle Base Database System (OBDS) -In this use case, an existing OCI DBCS system deployed earlier is going to be cloned in OCI Base DBCS Service. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is going to be cloned in OCI Oracle Base Database System (OBDS). Its a 2 Step operation. -In order to clone DBCS to an existing DBCS system, get the OCID of DB System ID you want to clone. +In order to clone OBDS to an existing OBDS system, get the OCID of DB System ID you want to clone. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `clone_dbcs_system.yaml` to clone a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `clone_dbcs_system.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCID of existing VMDB as `id` to be cloned. - OCI Configmap as `oci-cred` diff --git a/docs/dbcs/provisioning/create_dbcs_with_kms.md b/docs/dbcs/provisioning/create_dbcs_with_kms.md index 852a1db8..97d912d4 100644 --- a/docs/dbcs/provisioning/create_dbcs_with_kms.md +++ b/docs/dbcs/provisioning/create_dbcs_with_kms.md @@ -1,10 +1,10 @@ -# Deploy a DBCS DB System alongwith KMS Vault Encryption in OCI +# Deploy a OBDS DB System alongwith KMS Vault Encryption in OCI -In this use case, an OCI DBCS system is deployed using Oracle DB Operator DBCS controller along with KMS Vault configuration +In this use case, an OCI OBDS system is deployed using Oracle DB Operator OBDS controller along with KMS Vault configuration **NOTE** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -## Pre-requisites for KMS Vaults related to DBCS System +## Pre-requisites for KMS Vaults related to OBDS System There is also other set of pre-requisites for KMS Vaults related to dynamic group and policies. Please follow instructions for same. 1. Create Dynamic group with rule `ALL {resource.compartment.id =` and give it some name. 2. Create policy in your compartment for this dynamic group to access to key/vaults by database. @@ -29,13 +29,13 @@ Allow dynamic-group db_dynamic_group to manage database-family in compartment sa Allow dynamic-group db_dynamic_group to manage keys in compartment sauahuja Allow dynamic-group db_dynamic_group to manage vaults in compartment sauahuja ``` -3. Do also create KMS Vault and KMS Key in order to use it during DBCS provisioning. We are going to refer those variables (`vaultName`, `keyName`) in the yaml file. +3. Do also create KMS Vault and KMS Key in order to use it during OBDS provisioning. We are going to refer those variables (`vaultName`, `keyName`) in the yaml file. -This example uses `dbcs_service_with_kms.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `dbcs_service_with_kms.yaml` to deploy a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` - Database Name as `kmsdb` @@ -43,7 +43,7 @@ This example uses `dbcs_service_with_kms.yaml` to deploy a Single Instance DBCS - Database Workload Type as Transaction Processing i.e. `OLTP` - Database Hostname Prefix as `kmshost` - Oracle VMDB Shape as `VM.Standard2.2` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - domain `subdda0b5eaa.cluster1.oraclevcn.com` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` - KMS Vault Name as `dbvault` @@ -56,18 +56,18 @@ Use the file: [dbcs_service_with_kms.yaml](./dbcs_service_with_kms.yaml) for thi 1. Deploy the .yaml file: ```bash -[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_with_kms.yaml +[root@docker-test-server OBDS]# kubectl apply -f dbcs_service_with_kms.yaml dbcssystem.database.oracle.com/dbcssystem-create created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. ```bash -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./dbcs_service_with_kms_sample_output.log) is the sample output for a DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with KMS configurations. +[Here](./dbcs_service_with_kms_sample_output.log) is the sample output for a OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with KMS configurations. diff --git a/docs/dbcs/provisioning/create_dbcs_with_pdb.md b/docs/dbcs/provisioning/create_dbcs_with_pdb.md index cb49ec51..d68a1991 100644 --- a/docs/dbcs/provisioning/create_dbcs_with_pdb.md +++ b/docs/dbcs/provisioning/create_dbcs_with_pdb.md @@ -1,6 +1,6 @@ -# Deploy a DBCS DB System using OCI DBCS Service alongwith PDB +# Deploy a OBDS DB System using OCI Oracle Base Database System (OBDS) alongwith PDB -In this use case, an OCI DBCS system is deployed using Oracle DB Operator DBCS controller along with PDB configuration +In this use case, an OCI OBDS system is deployed using Oracle DB Operator OBDS controller along with PDB configuration **NOTE** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. @@ -12,11 +12,11 @@ Also, create a Kubernetes secret `pdb-password` using the file: kubectl create secret generic pdb-password --from-file=./pdb-password -n default ``` -This example uses `dbcs_service_with_pdb.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `dbcs_service_with_pdb.yaml` to deploy a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:US-ASHBURN-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:US-ASHBURN-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` - Database Name as `dbsystem24` @@ -25,7 +25,7 @@ This example uses `dbcs_service_with_pdb.yaml` to deploy a Single Instance DBCS - Database Hostname Prefix as `host24` - Cpu Core Count as `1` - Oracle VMDB Shape as `VM.Standard2.1` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - domain `subd215df3e6.k8stest.oraclevcn.com` - OCID of the Subnet as `ocid1.subnet.oc1.iad.aaaaaaaa3lmmxwsykn2jc2vphzpq6eoyoqtte3dpwg6s5fzfkti22ibol2ua` - PDB Name as `pdb_sauahuja_11` @@ -38,18 +38,18 @@ Use the file: [dbcs_service_with_pdb.yaml](./dbcs_service_with_pdb.yaml) for thi 1. Deploy the .yaml file: ```bash -[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_with_pdb.yaml +[root@docker-test-server OBDS]# kubectl apply -f dbcs_service_with_pdb.yaml dbcssystem.database.oracle.com/dbcssystem-create-with-pdb created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. ```bash -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./dbcs_service_with_pdb_sample_output.log) is the sample output for a DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with PDB configurations. +[Here](./dbcs_service_with_pdb_sample_output.log) is the sample output for a OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with PDB configurations. diff --git a/docs/dbcs/provisioning/create_pdb_to_existing_dbcs_system.md b/docs/dbcs/provisioning/create_pdb_to_existing_dbcs_system.md index 37c3fb49..d1c4ed5b 100644 --- a/docs/dbcs/provisioning/create_pdb_to_existing_dbcs_system.md +++ b/docs/dbcs/provisioning/create_pdb_to_existing_dbcs_system.md @@ -1,15 +1,15 @@ -# Create PDB to an existing DBCS System +# Create PDB to an existing OBDS System -In this use case, an existing OCI DBCS system deployed earlier is going to have a PDB/many PDBs created. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is going to have a PDB/many PDBs created. Its a 2 Step operation. -In order to create PDBs to an existing DBCS system, the steps will be: +In order to create PDBs to an existing OBDS system, the steps will be: -1. Bind the existing DBCS System to DBCS Controller. +1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to create PDBs. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +As step 1, first bind the existing OBDS System to OBDS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- ```bash kubectl get dbcssystems NAME AGE @@ -25,7 +25,7 @@ Create a Kubernetes secret `pdb-password` using the file: kubectl create secret generic pdb-password --from-file=./pdb-password -n default ``` -This example uses `createpdb_in_existing_dbcs_system_list.yaml` to scale up a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `createpdb_in_existing_dbcs_system_list.yaml` to scale up a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htya55wz5vfil7ul3pkzpubnymp6zrp3fhgomv3fcdr2vtiq` - OCI Configmap as `oci-cred` @@ -38,18 +38,18 @@ Use the file: [createpdb_in_existing_dbcs_system_list.yaml](./createpdb_in_exist 1. Deploy the .yaml file: ```sh -[root@docker-test-server DBCS]# kubectl apply -f createpdb_in_existing_dbcs_system_list.yaml +[root@docker-test-server OBDS]# kubectl apply -f createpdb_in_existing_dbcs_system_list.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of PDBs. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./createpdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for creation of PDBs on an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +[Here](./createpdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for creation of PDBs on an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. diff --git a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md index 4cc4ceca..b9ce6931 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md +++ b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md @@ -1,26 +1,26 @@ -# Deploy a 2 Node RAC DB System using OCI DBCS Service +# Deploy a 2 Node RAC DB System using OCI OBDS Service -In this use case, a 2 Node RAC OCI DBCS system is deployed using Oracle DB Operator DBCS controller using all the available parameters in the .yaml file being used during the deployment. The type of the Storage Management in this case is ASM. +In this use case, a 2 Node RAC OCI OBDS system is deployed using Oracle DB Operator OBDS controller using all the available parameters in the .yaml file being used during the deployment. The type of the Storage Management in this case is ASM. **NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `dbcs_service_with_2_node_rac.yaml` to deploy a 2 Node RAC VMDB using Oracle DB Operator DBCS Controller with: +This example uses `dbcs_service_with_2_node_rac.yaml` to deploy a 2 Node RAC VMDB using Oracle DB Operator OBDS Controller with: - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - Cluster Name as `maa-cluster` -- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` -- Enable flag for Automatic Backup for DBCS Database as `True` -- Auto Backup Window for DBCS Database as `SLOT_FOUR` +- Enable flag for Automatic Backup for OBDS Database as `True` +- Auto Backup Window for OBDS Database as `SLOT_FOUR` - Recovery Windows for Backup retention in days as `15` - Oracle Database Edition as `STANDARD_EDITION` - Database Name as `db0130` - Oracle Database Software Image Version as `19c` - Database Workload Type as Transaction Processing i.e. `OLTP` - Redundancy of the ASM Disks as `EXTERNAL` -- Display Name for the DBCS System as `dbsys123` +- Display Name for the OBDS System as `dbsys123` - Database Hostname Prefix as `host01234` - Initial Size of the DATA Storage in GB as `256` - License Model as `BRING_YOUR_OWN_LICENSE` @@ -28,12 +28,12 @@ This example uses `dbcs_service_with_2_node_rac.yaml` to deploy a 2 Node RAC VMD - Private IP explicitly assigned to be `10.0.1.99` - Node count as `2` - Oracle VMDB Shape as `VM.Standard2.1` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - Storage Management type as `ASM` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbv` -- Tag the DBCS system with two key value pairs as `"TEST": "test_case_provision"` and `"CreatedBy": "MAA_TEAM"` +- Tag the OBDS system with two key value pairs as `"TEST": "test_case_provision"` and `"CreatedBy": "MAA_TEAM"` - TDE Wallet Secret as `tde-password` -- Time Zone for the DBCS System as `Europe/Berlin` +- Time Zone for the OBDS System as `Europe/Berlin` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). @@ -46,14 +46,14 @@ Use the file: [dbcs_service_with_all_parameters_asm.yaml](./dbcs_service_with_2_ dbcssystem.database.oracle.com/dbcssystem-create configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./dbcs_service_with_2_node_rac_sample_output.log) is the sample output for a 2 Node RAC DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with all parameters and with Storage Management as ASM. +[Here](./dbcs_service_with_2_node_rac_sample_output.log) is the sample output for a 2 Node RAC OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with all parameters and with Storage Management as ASM. diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md index 89ed3a9f..7bd9abea 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md @@ -1,37 +1,37 @@ -# Create DBCS with All Parameters with Storage Management as ASM +# Create OBDS with All Parameters with Storage Management as ASM -In this use case, the an OCI DBCS system is deployed using Oracle DB Operator DBCS controller using all the available parameters in the .yaml file being used during the deployment. The type of the Storage Management in this case is ASM. +In this use case, the an OCI OBDS system is deployed using Oracle DB Operator OBDS controller using all the available parameters in the .yaml file being used during the deployment. The type of the Storage Management in this case is ASM. **NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `dbcs_service_with_all_parameters_asm.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `dbcs_service_with_all_parameters_asm.yaml` to deploy a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` -- Enable flag for Automatic Backup for DBCS Database as `True` -- Auto Backup Window for DBCS Database as `SLOT_FOUR` +- Enable flag for Automatic Backup for OBDS Database as `True` +- Auto Backup Window for OBDS Database as `SLOT_FOUR` - Recovery Windows for Backup retention in days as `15` - Oracle Database Edition as `STANDARD_EDITION` - Database Name as `db0130` - Oracle Database Software Image Version as `19c` - Database Workload Type as Transaction Processing i.e. `OLTP` - Redundancy of the ASM Disks as `EXTERNAL` -- Display Name for the DBCS System as `dbsys123` +- Display Name for the OBDS System as `dbsys123` - Database Hostname Prefix as `host01234` - Initial Size of the DATA Storage in GB as `256` - License Model as `BRING_YOUR_OWN_LICENSE` - Name of the PDB to be created as `PDB0123` - Private IP explicitly assigned to be `10.0.1.99` - Oracle VMDB Shape as `VM.Standard2.1` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - Storage Management type as `ASM` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbv` -- Tag the DBCS system with two key value pairs as `"TEST": "test_case_provision"` and `"CreatedBy": "MAA_TEAM"` +- Tag the OBDS system with two key value pairs as `"TEST": "test_case_provision"` and `"CreatedBy": "MAA_TEAM"` - TDE Wallet Secret as `tde-password` -- Time Zone for the DBCS System as `Europe/Berlin` +- Time Zone for the OBDS System as `Europe/Berlin` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). @@ -40,18 +40,18 @@ Use the file: [dbcs_service_with_all_parameters_asm.yaml](./dbcs_service_with_al 1. Deploy the .yaml file: ```sh -[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_with_all_parameters_asm.yaml +[root@docker-test-server OBDS]# kubectl apply -f dbcs_service_with_all_parameters_asm.yaml dbcssystem.database.oracle.com/dbcssystem-create created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./dbcs_service_with_all_parameters_asm_sample_output.log) is the sample output for a DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with all parameters and with Storage Management as ASM. +[Here](./dbcs_service_with_all_parameters_asm_sample_output.log) is the sample output for a OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with all parameters and with Storage Management as ASM. diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md index de11fca1..1bc560f4 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md @@ -1,37 +1,37 @@ -# Create DBCS with All Parameters with Storage Management as LVM +# Create OBDS with All Parameters with Storage Management as LVM -In this use case, the an OCI DBCS system is deployed using Oracle DB Operator DBCS controller using all the available parameters in the .yaml file being used during the deployment. +In this use case, the an OCI OBDS system is deployed using Oracle DB Operator OBDS controller using all the available parameters in the .yaml file being used during the deployment. **NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `dbcs_service_with_all_parameters_lvm.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `dbcs_service_with_all_parameters_lvm.yaml` to deploy a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` -- Enable flag for Automatic Backup for DBCS Database as `True` -- Auto Backup Window for DBCS Database as `SLOT_FOUR` +- Enable flag for Automatic Backup for OBDS Database as `True` +- Auto Backup Window for OBDS Database as `SLOT_FOUR` - Recovery Windows for Backup retention in days as `15` - Oracle Database Edition as `STANDARD_EDITION` - Database Name as `db0130` - Oracle Database Software Image Version as `19c` - Database Workload Type as Transaction Processing i.e. `OLTP` - Redundancy of the ASM Disks as `EXTERNAL` -- Display Name for the DBCS System as `dbsys123` +- Display Name for the OBDS System as `dbsys123` - Database Hostname Prefix as `host01234` - Initial Size of the DATA Storage in GB as `256` - License Model as `BRING_YOUR_OWN_LICENSE` - Name of the PDB to be created as `PDB0123` - Private IP explicitly assigned to be `10.0.1.99` - Oracle VMDB Shape as `VM.Standard2.1` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - Storage Management type as `LVM` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbv` -- Tag the DBCS system with two key value pairs as `"TEST": "test_case_provision"` and `"CreatedBy": "MAA_TEAM"` +- Tag the OBDS system with two key value pairs as `"TEST": "test_case_provision"` and `"CreatedBy": "MAA_TEAM"` - TDE Wallet Secret as `tde-password` -- Time Zone for the DBCS System as `Europe/Berlin` +- Time Zone for the OBDS System as `Europe/Berlin` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). @@ -40,18 +40,18 @@ Use the file: [dbcs_service_with_all_parameters_lvm.yaml](./dbcs_service_with_al 1. Deploy the .yaml file: ```sh -[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_with_all_parameters_lvm.yaml +[root@docker-test-server OBDS]# kubectl apply -f dbcs_service_with_all_parameters_lvm.yaml dbcssystem.database.oracle.com/dbcssystem-create created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB deployment. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./dbcs_service_with_all_parameters_lvm_sample_output.log) is the sample output for a DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with all parameters and with Storage Management as LVM. +[Here](./dbcs_service_with_all_parameters_lvm_sample_output.log) is the sample output for a OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with all parameters and with Storage Management as LVM. diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md index b2ba70a1..0d75297b 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md @@ -1,14 +1,14 @@ -# Deploy a DBCS DB System using OCI DBCS Service with minimal parameters +# Deploy a DB System using OCI Oracle Base Database System (OBDS) with minimal parameters -In this use case, an OCI DBCS system is deployed using Oracle DB Operator DBCS controller using minimal required parameters in the .yaml file being used during the deployment. +In this use case, an OCI Oracle Base Database System (OBDS) system is deployed using Oracle DB Operator OBDS controller using minimal required parameters in the .yaml file being used during the deployment. **NOTE** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `dbcs_service_with_minimal_parameters.yaml` to deploy a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `dbcs_service_with_minimal_parameters.yaml` to deploy a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `cid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` - Database Name as `dbsystem1234` @@ -16,7 +16,7 @@ This example uses `dbcs_service_with_minimal_parameters.yaml` to deploy a Single - Database Workload Type as Transaction Processing i.e. `OLTP` - Database Hostname Prefix as `host1234` - Oracle VMDB Shape as `VM.Standard2.1` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - domain `vcndns.oraclevcn.com` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` @@ -36,9 +36,9 @@ dbcssystem.database.oracle.com/dbcssystem-create created NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./dbcs_service_with_minimal_parameters_sample_output.log) is the sample output for a DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with minimal parameters. +[Here](./dbcs_service_with_minimal_parameters_sample_output.log) is the sample output for a OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with minimal parameters. diff --git a/docs/dbcs/provisioning/migrate_to_kms.md b/docs/dbcs/provisioning/migrate_to_kms.md index 092d2599..0c5ee10c 100644 --- a/docs/dbcs/provisioning/migrate_to_kms.md +++ b/docs/dbcs/provisioning/migrate_to_kms.md @@ -1,15 +1,15 @@ -# Create and update KMS vault to an existing DBCS System already deployed in OCI Base DBCS Service +# Create and update KMS vault to an existing OBDS System already deployed in OCI Oracle Base Database System (OBDS) -In this use case, an existing OCI DBCS system deployed earlier having encryption with TDE Wallet Password, will be migrated to have KMS Vault created and update DBCS System in OCI. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier having encryption with TDE Wallet Password, will be migrated to have KMS Vault created and update OBDS System in OCI. Its a 2 Step operation. -In order to create KMS Vaults to an existing DBCS system, the steps will be: +In order to create KMS Vaults to an existing OBDS system, the steps will be: -1. Bind the existing DBCS System (having encryption enabled with TDE Wallet password) to the DBCS Controller. +1. Bind the existing OBDS System (having encryption enabled with TDE Wallet password) to the OBDS Controller. 2. Apply the change to create KMS Vaults. -**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. It is also assumed that DBCS System you created earlier is using TDE Wallet password. +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. It is also assumed that OBDS System you created earlier is using TDE Wallet password. -As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +As step 1, first bind the existing OBDS System to OBDS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- ```bash kubectl get dbcssystems NAME AGE @@ -17,12 +17,12 @@ dbcssystem-create 3m33s ``` Below proceeding further create PDB Admin Password which is going to used as name suggests. -This example uses `dbcs_service_migrate_to_kms.yaml` to create KMS Vault to existing DBCS VMDB having encryption already enabled earlier with TDE Wallet using Oracle DB Operator DBCS Controller with: +This example uses `dbcs_service_migrate_to_kms.yaml` to create KMS Vault to existing OBDS VMDB having encryption already enabled earlier with TDE Wallet using Oracle DB Operator OBDS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyaoja4v2kx5rcfe5w2onndjfpqjhjoakxgwxo2sbgei5iq` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Existing `dbSystem` details (`compartmentId`,`dbAdminPasswordSecret`,`tdeWalletPasswordSecret`)used before to create DBCS system. +- Existing `dbSystem` details (`compartmentId`,`dbAdminPasswordSecret`,`tdeWalletPasswordSecret`)used before to create OBDS system. - kmsConfig - vaultName as `dbvault` as an example. - kmsConfig - keyName as `dbkey` as an example. - kmsConfig - compartmentId as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` as an example. @@ -32,18 +32,18 @@ Use the file: [dbcs_service_migrate_to_kms.yaml](./dbcs_service_migrate_to_kms.y 1. Deploy the .yaml file: ```sh -[root@docker-test-server DBCS]# kubectl apply -f dbcs_service_migrate_to_kms.yaml +[root@docker-test-server OBDS]# kubectl apply -f dbcs_service_migrate_to_kms.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of KMS Vaults. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of KMS Vaults. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./dbcs_service_migrate_to_kms.log) is the sample output for creation of KMS Vaults on an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +[Here](./dbcs_service_migrate_to_kms.log) is the sample output for creation of KMS Vaults on an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. diff --git a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md index 24d4405c..1f03ff9f 100644 --- a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md @@ -1,25 +1,25 @@ -# Scale Down the shape of an existing DBCS System +# Scale Down the shape of an existing OBDS System -In this use case, an existing OCI DBCS system deployed earlier is scaled down for its shape using Oracle DB Operator DBCS controller. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is scaled down for its shape using Oracle DB Operator OBDS controller. Its a 2 Step operation. -In order to scale down an existing DBCS system, the steps will be: +In order to scale down an existing OBDS system, the steps will be: -1. Bind the existing DBCS System to DBCS Controller. +1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to scale down its shape. **NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `scale_down_dbcs_system_shape.yaml` to scale down a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `scale_down_dbcs_system_shape.yaml` to scale down a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72` - Database Admin Credential as `admin-password` - Database Hostname Prefix as `host1234` - Oracle VMDB target Shape as `VM.Standard2.1` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). @@ -28,18 +28,18 @@ Use the file: [scale_down_dbcs_system_shape.yaml](./scale_down_dbcs_system_shape 1. Deploy the .yaml file: ```sh -[root@docker-test-server DBCS]# kubectl apply -f scale_down_dbcs_system_shape.yaml +[root@docker-test-server OBDS]# kubectl apply -f scale_down_dbcs_system_shape.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB Scale down. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB Scale down. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./scale_down_dbcs_system_shape_sample_output.log) is the sample output for scaling down the shape of an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +[Here](./scale_down_dbcs_system_shape_sample_output.log) is the sample output for scaling down the shape of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md index 66c2028f..924a8517 100644 --- a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md @@ -1,25 +1,25 @@ -# Scale UP the shape of an existing DBCS System +# Scale UP the shape of an existing OBDS System -In this use case, an existing OCI DBCS system deployed earlier is scaled up for its shape using Oracle DB Operator DBCS controller. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is scaled up for its shape using Oracle DB Operator OBDS controller. Its a 2 Step operation. -In order to scale up an existing DBCS system, the steps will be: +In order to scale up an existing OBDS system, the steps will be: -1. Bind the existing DBCS System to DBCS Controller. +1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to scale up its shape. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `scale_up_dbcs_system_shape.yaml` to scale up a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `scale_up_dbcs_system_shape.yaml` to scale up a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72` - Database Admin Credential as `admin-password` - Database Hostname Prefix as `host1234` - Oracle VMDB target Shape as `VM.Standard2.2` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). @@ -28,18 +28,18 @@ Use the file: [scale_up_dbcs_system_shape.yaml](./scale_up_dbcs_system_shape.yam 1. Deploy the .yaml file: ```sh -[root@docker-test-server DBCS]# kubectl apply -f scale_up_dbcs_system_shape.yaml +[root@docker-test-server OBDS]# kubectl apply -f scale_up_dbcs_system_shape.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB Scale up. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB Scale up. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./scale_up_dbcs_system_shape_sample_output.log) is the sample output for scaling up the shape of an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +[Here](./scale_up_dbcs_system_shape_sample_output.log) is the sample output for scaling up the shape of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. diff --git a/docs/dbcs/provisioning/scale_up_storage.md b/docs/dbcs/provisioning/scale_up_storage.md index 7cdfa574..ff16cbf9 100644 --- a/docs/dbcs/provisioning/scale_up_storage.md +++ b/docs/dbcs/provisioning/scale_up_storage.md @@ -1,26 +1,26 @@ -# Scale UP the storage of an existing DBCS System +# Scale UP the storage of an existing OBDS System -In this use case, an existing OCI DBCS system deployed earlier is scaled up for its storage using Oracle DB Operator DBCS controller. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is scaled up for its storage using Oracle DB Operator OBDS controller. Its a 2 Step operation. -In order to scale up storage of an existing DBCS system, the steps will be: +In order to scale up storage of an existing OBDS system, the steps will be: -1. Bind the existing DBCS System to DBCS Controller. +1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to scale up its storage. **NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `scale_up_storage.yaml` to scale up storage of an existing Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `scale_up_storage.yaml` to scale up storage of an existing Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` - Database Hostname Prefix as `host1234` - Target Data Storage Size in GBs as `512` - Oracle VMDB Shape as `VM.Standard2.1` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` @@ -28,18 +28,18 @@ Use the file: [scale_up_storage.yaml](./scale_up_storage.yaml) for this use case 1. Deploy the .yaml file: ```sh -[root@test-server DBCS]# kubectl apply -f scale_storage.yaml +[root@test-server OBDS]# kubectl apply -f scale_storage.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB Scale up. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB Scale up. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./scale_up_storage_sample_output.log) is the sample output for scaling up the storage of an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with minimal parameters. +[Here](./scale_up_storage_sample_output.log) is the sample output for scaling up the storage of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with minimal parameters. diff --git a/docs/dbcs/provisioning/terminate_dbcs_system.md b/docs/dbcs/provisioning/terminate_dbcs_system.md index 071cda30..f3b19cbc 100644 --- a/docs/dbcs/provisioning/terminate_dbcs_system.md +++ b/docs/dbcs/provisioning/terminate_dbcs_system.md @@ -1,15 +1,15 @@ -# Terminate an existing DBCS System +# Terminate an existing Oracle Base Database System (OBDS) -In this use case, an existing OCI DBCS system deployed earlier is terminated using Oracle DB Operator DBCS controller. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is terminated using Oracle DB Operator OBDS controller. Its a 2 Step operation. -In order to terminate an existing DBCS system, the steps will be: +In order to terminate an existing OBDS system, the steps will be: -1. Bind the existing DBCS System to DBCS Controller. -2. Apply the change to terminate this DBCS System. +1. Bind the existing OBDS System to OBDS Controller. +2. Apply the change to terminate this OBDS System. **NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `terminate_dbcs_system.yaml` to terminated a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `terminate_dbcs_system.yaml` to terminated a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` - OCI Configmap as `oci-cred` @@ -21,26 +21,26 @@ Use the file: [terminate_dbcs_system.yaml](./terminate_dbcs_system.yaml) for thi 1. Deploy the .yaml file: ```sh -[root@test-server DBCS]# kubectl apply -f terminate_dbcs_system.yaml +[root@test-server OBDS]# kubectl apply -f terminate_dbcs_system.yaml dbcssystem.database.oracle.com/dbcssystem-terminate created -[root@test-server DBCS]# kubectl delete -f terminate_dbcs_system.yaml +[root@test-server OBDS]# kubectl delete -f terminate_dbcs_system.yaml dbcssystem.database.oracle.com "dbcssystem-terminate" deleted ``` 2. Check the logs of Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for an update on the terminate operation been accepted. ``` -[root@test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` -3. Check and confirm if the existing OCI DBCS system is NO longer available after sometime because of termination: +3. Check and confirm if the existing OCI OBDS system is NO longer available after sometime because of termination: ``` -[root@test-server DBCS]# kubectl describe dbcssystems.database.oracle.com dbcssystem-terminate +[root@test-server OBDS]# kubectl describe dbcssystems.database.oracle.com dbcssystem-terminate ``` ## Sample Output -[Here](./terminate_dbcs_system_sample_output.log) is the sample output for terminating an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller with minimal parameters. +[Here](./terminate_dbcs_system_sample_output.log) is the sample output for terminating an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with minimal parameters. diff --git a/docs/dbcs/provisioning/update_license.md b/docs/dbcs/provisioning/update_license.md index efadb32b..6f32c31b 100644 --- a/docs/dbcs/provisioning/update_license.md +++ b/docs/dbcs/provisioning/update_license.md @@ -1,26 +1,26 @@ -# Update License type of an existing DBCS System +# Update License type of an existing OBDS System -In this use case, the license type of an existing OCI DBCS system deployed earlier is changed from `License Included` to `Bring your own license` using Oracle DB Operator DBCS controller. Its a 2 Step operation. +In this use case, the license type of an existing OCI OBDS system deployed earlier is changed from `License Included` to `Bring your own license` using Oracle DB Operator OBDS controller. Its a 2 Step operation. -In order to update the license type an existing DBCS system, the steps will be: +In order to update the license type an existing OBDS system, the steps will be: -1. Bind the existing DBCS System to DBCS Controller. +1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to change its license type. **NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `update_license.yaml` to change the license type of a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `update_license.yaml` to change the license type of a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Availability Domain for the DBCS VMDB as `OLou:AP-MUMBAI-1-AD-1` +- Availability Domain for the OBDS VMDB as `OLou:AP-MUMBAI-1-AD-1` - Target license model as `BRING_YOUR_OWN_LICENSE` - Compartment OCID as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - Database Admin Credential as `admin-password` - Database Hostname Prefix as `host1234` - Oracle VMDB Shape as `VM.Standard2.1` -- SSH Public key for the DBCS system being deployed as `oci-publickey` +- SSH Public key for the OBDS system being deployed as `oci-publickey` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). @@ -29,18 +29,18 @@ Use the file: [update_license.yaml](./update_license.yaml) for this use case as 1. Deploy the .yaml file: ```sh -[root@test-server DBCS]# kubectl apply -f update_license.yaml +[root@test-server OBDS]# kubectl apply -f update_license.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB Scale up. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB Scale up. NOTE: Check the DB Operator Pod name in your environment. ``` -[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` ## Sample Output -[Here](./update_license_sample_output.log) is the sample output for updating the license type an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +[Here](./update_license_sample_output.log) is the sample output for updating the license type an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. From 81553ab9f56172feecec41cb8a37ec6c5ae00d42 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 13 Feb 2025 18:09:01 +0000 Subject: [PATCH 201/414] Update README.md minor style updates --- docs/adb/README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/adb/README.md b/docs/adb/README.md index 14733347..e8164697 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -1,14 +1,14 @@ # Managing Oracle Autonomous Databases with Oracle Database Operator for Kubernetes -Before you use the Oracle Database Operator for Kubernetes (the operator), ensure your system meets all of the Oracle Autonomous Database (ADB) Prerequisites [ADB_PREREQUISITES](./ADB_PREREQUISITES.md). +Before you use the Oracle Database Operator for Kubernetes (the operator), ensure that your system meets all of the Oracle Autonomous Database (ADB) Prerequisites [ADB_PREREQUISITES](./ADB_PREREQUISITES.md). -As indicated in the prerequisites (see above), to interact with OCI services, either the cluster must be authorized using Principal Instance, or by using the API Key Authentication and specifying the configMap and the secret under the `ociConfig` field. +As indicated in the prerequisites (see above), to interact with OCI services, either the cluster must be authorized using Principal Instance, or the cluster must be authorized using the API Key Authentication by specifying the configMap and the secret under the `ociConfig` field. ## Required Permissions -The operator must be given the required type of access in a policy written by an administrator to manage the Autonomous Databases. See [Let database and fleet admins manage Autonomous Databases](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/commonpolicies.htm#db-admins-manage-adb) for example Autonomous Database policies. +The operator must be given the required type of access in a policy written by an administrator to manage the Autonomous Databases. For examples of Autonomous Database policies, see: [Let database and fleet admins manage Autonomous Databases](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/commonpolicies.htm#db-admins-manage-adb) -The permission to view the work requests is also required, so that the operator will update the resources when the work is done. See [Viewing Work Requests](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengviewingworkrequests.htm#contengviewingworkrequests) for example work request policies. +Permissions to view the work requests are also required, so that the operator can update the resources when the work is done. For example work request policies, see: [Viewing Work Requests](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengviewingworkrequests.htm#contengviewingworkrequests) ## Supported Features @@ -31,9 +31,9 @@ To debug the Oracle Autonomous Databases with Oracle Database Operator, see [Deb ## Provision an Autonomous Database -Follow these steps to provision an Autonomous Database that will map objects in your cluster. +To provision an Autonomous Database that will map objects in your cluster, complete the following steps: -1. Get the `Compartment OCID`. +1. Obtain the `Compartment OCID`. Log in to the Cloud Console and click `Compartment`. @@ -67,7 +67,7 @@ Follow these steps to provision an Autonomous Database that will map objects in kubectl create secret generic admin-password --from-literal=admin-password='password_here' ``` -4. Add the following fields to the AutonomousDatabase resource definition. An example `.yaml` file is available here: [`config/samples/adb/autonomousdatabase_create.yaml`](./../../config/samples/adb/autonomousdatabase_create.yaml) +4. Add the following fields to the Autonomous Database resource definition. An example `.yaml` file is available here: [`config/samples/adb/autonomousdatabase_create.yaml`](./../../config/samples/adb/autonomousdatabase_create.yaml) | Attribute | Type | Description | Required? | |----|----|----|----| | `spec.details.compartmentId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the compartment of the Autonomous Database. | Yes | @@ -89,7 +89,7 @@ Follow these steps to provision an Autonomous Database that will map objects in | `spec.details.isDedicated` | boolean | True if the database is on dedicated [Exadata infrastructure](https://docs.cloud.oracle.com/Content/Database/Concepts/adbddoverview.htm). `spec.details.autonomousContainerDatabase.k8sACD.name` or `spec.details.autonomousContainerDatabase.ociACD.id` has to be provided if the value is true. | No | | `spec.details.isFreeTier` | boolean | Indicates if this is an Always Free resource. The default value is false. Note that Always Free Autonomous Databases have 1 CPU and 20GB of memory. For Always Free databases, memory and CPU cannot be scaled.
This cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMtlsConnectionRequired, privateEndpointLabel, nsgIds, dbVersion, or dbName. | No | | `spec.details.isAccessControlEnabled` | boolean | Indicates if the database-level access control is enabled.
If disabled, database access is defined by the network security rules.
If enabled, database access is restricted to the IP addresses defined by the rules specified with the `whitelistedIps` property. While specifying `whitelistedIps` rules is optional, if database-level access control is enabled and no rules are specified, the database will become inaccessible.
When creating a database clone, the desired access control setting should be specified. By default, database-level access control will be disabled for the clone.
This property is applicable only to Autonomous Databases on the Exadata Cloud@Customer platform. For Autonomous Database Serverless instances, `whitelistedIps` is used. | No | - | `spec.details.whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for [Autonomous Database Serverless](https://docs.oracle.com/en/cloud/paas/autonomous-database/index.html) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.
If `arePrimaryWhitelistedIpsUsed` is 'TRUE' then Autonomous Database uses this primary's IP access control list (ACL) for the disaster recovery peer called `standbywhitelistedips`.
For Autonomous Database Serverless, this is an array of CIDR (classless inter-domain routing) notations for a subnet or VCN OCID (virtual cloud network Oracle Cloud ID).
Multiple IPs and VCN OCIDs should be separate strings separated by commas, but if it’s other configurations that need multiple pieces of information then its each piece is connected with semicolon (;) as a delimiter.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`
For Exadata Cloud@Customer, this is an array of IP addresses or CIDR notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`
For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry.
This cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, isMtlsConnectionRequired, dbWorkload, dbVersion, dbName, or isFreeTier. | No | + | `spec.details.whitelistedIps` | []string | The client IP access control list (ACL). This feature is available for [Autonomous Database Serverless](https://docs.oracle.com/en/cloud/paas/autonomous-database/index.html) and on Exadata Cloud@Customer.
Only clients connecting from an IP address included in the ACL may access the Autonomous Database instance.
If `arePrimaryWhitelistedIpsUsed` is 'TRUE' then Autonomous Database uses this primary's IP access control list (ACL) for the disaster recovery peer called `standbywhitelistedips`.
For Autonomous Database Serverless, this is an array of CIDR (classless inter-domain routing) notations for a subnet or VCN OCID (virtual cloud network Oracle Cloud ID).
Multiple IPs and VCN OCIDs should be separate strings separated by commas. However, if other configurations require multiple pieces of information, then each piece is connected with semicolon (;) as a delimiter.
Example: `["1.1.1.1","1.1.1.0/24","ocid1.vcn.oc1.sea.","ocid1.vcn.oc1.sea.;1.1.1.1","ocid1.vcn.oc1.sea.;1.1.0.0/16"]`
For Exadata Cloud@Customer, this is an array of IP addresses or CIDR notations.
Example: `["1.1.1.1","1.1.1.0/24","1.1.2.25"]`
For an update operation, if you want to delete all the IPs in the ACL, use an array with a single empty string entry.
This cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, isMtlsConnectionRequired, dbWorkload, dbVersion, dbName, or isFreeTier. | No | | `spec.details.subnetId` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the subnet the resource is associated with.
**Subnet Restrictions:**
- For Autonomous Database, setting this will disable public secure access to the database.
These subnets are used by the Oracle Clusterware private interconnect on the database instance.
Specifying an overlapping subnet will cause the private interconnect to malfunction.
This restriction applies to both the client subnet and the backup subnet. | No | | `spec.details.nsgIds` | []string | The list of [OCIDs](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) for the network security groups (NSGs) to which this resource belongs. Setting this to an empty list removes all resources from all NSGs. For more information about NSGs, see [Security Rules](https://docs.cloud.oracle.com/Content/Network/Concepts/securityrules.htm).
**NsgIds restrictions:**
- A network security group (NSG) is optional for Autonomous Databases with private access. The nsgIds list can be empty. | No | | `spec.details.privateEndpointLabel` | string | The resource's private endpoint label.
- Setting the endpoint label to a non-empty string creates a private endpoint database.
- Resetting the endpoint label to an empty string, after the creation of the private endpoint database, changes the private endpoint database to a public endpoint database.
- Setting the endpoint label to a non-empty string value, updates to a new private endpoint database, when the database is disabled and re-enabled.
This setting cannot be updated in parallel with any of the following: licenseModel, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMTLSConnectionRequired, dbWorkload, dbVersion, dbName, or isFreeTier. | No | @@ -125,7 +125,7 @@ Follow these steps to provision an Autonomous Database that will map objects in 5. Choose the type of network access (optional): - By default, the network access type is set to PUBLIC, which allows secure connections from anywhere. Uncomment the code block if you want configure the network access. See [Configuring Network Access of Autonomous Database](./NETWORK_ACCESS_OPTIONS.md) for more information. + By default, the network access type is set to PUBLIC, which allows secure connections from anywhere. Uncomment the code block if you want configure the network access. For more information, see: [Configuring Network Access of Autonomous Database](./NETWORK_ACCESS_OPTIONS.md) 6. Apply the YAML: @@ -156,7 +156,7 @@ The operator also generates the `AutonomousBackup` custom resources if a databas 3. Add the following fields to the AutonomousDatabase resource definition. An example `.yaml` file is available here: [`config/samples/adb/autonomousdatabase_bind.yaml`](./../../config/samples/adb/autonomousdatabase_bind.yaml) | Attribute | Type | Description | Required? | |----|----|----|----| - | `spec.details.id` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the Autonomous Database you want to bind (create a reference) in your cluster. | Yes | + | `spec.details.id` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the Autonomous Database that you want to bind (create a reference) in your cluster. | Yes | | `spec.ociConfig` | dictionary | Not required when the Operator is authorized with [Instance Principal](./ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from the [Authorized with API Key Authentication](./ADB_PREREQUISITES.md#authorized-with-api-key-authentication) section. | Conditional | | `spec.ociConfig.configMapName` | string | Name of the ConfigMap that holds the local OCI configuration | Conditional | | `spec.ociConfig.secretName`| string | Name of the K8s Secret that holds the private key value | Conditional | @@ -185,7 +185,7 @@ The operator also generates the `AutonomousBackup` custom resources if a databas ## Scale the OCPU core count or storage -> Note: this operation requires an `AutonomousDatabase` object to be in your cluster. To use this example, either the provision operation or the bind operation must be done, and the operator is authorized with API Key Authentication. +> Note: this operation requires an `AutonomousDatabase` object to be in your cluster. To use this example, either the provision operation or the bind operation must be completed, and the operator must be authorized with API Key Authentication. You can scale up or scale down the Oracle Autonomous Database OCPU core count or storage by updating the `cpuCoreCount` and `dataStorageSizeInTBs` parameters. The `isAutoScalingEnabled` indicates whether auto scaling is enabled. In this example, the CPU count and storage size (TB) are scaled up to 2 and the auto-scaling is turned off by updating the `autonomousdatabase-sample` custom resource. From e23196ba173ebebbb1dd6158e5c9412497d8d7f6 Mon Sep 17 00:00:00 2001 From: ting-lan-wang Date: Thu, 13 Feb 2025 13:12:13 -0500 Subject: [PATCH 202/414] Condition status should be false when reconcile error --- apis/database/v4/adbfamily_utils.go | 16 ------ .../database/autonomousdatabase_controller.go | 56 +++++++++++-------- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/apis/database/v4/adbfamily_utils.go b/apis/database/v4/adbfamily_utils.go index dc384408..380dab35 100644 --- a/apis/database/v4/adbfamily_utils.go +++ b/apis/database/v4/adbfamily_utils.go @@ -46,27 +46,11 @@ import ( "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" "github.com/oracle/oci-go-sdk/v65/workrequests" - "sigs.k8s.io/controller-runtime/pkg/client" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // This file contains the util functions that are shared by specs in both // apis/database/v1alpha1 and apis/database/v4. -// File the meta condition and return the meta view -func CreateMetaCondition(obj client.Object, err error, lifecycleState string, stateMsg string) metav1.Condition { - - return metav1.Condition{ - Type: lifecycleState, - LastTransitionTime: metav1.Now(), - ObservedGeneration: obj.GetGeneration(), - Reason: stateMsg, - Message: err.Error(), - Status: metav1.ConditionTrue, - } -} - /************************** * Remove Unchanged Fields **************************/ diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 5145aea5..37ae1b14 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -398,53 +398,65 @@ func (r *AutonomousDatabaseReconciler) manageError(logger logr.Logger, adb *dbv4 return emptyResult, nil } -const CONDITION_TYPE_COMPLETE = "Complete" -const CONDITION_REASON_COMPLETE = "ReconcileComplete" +const CONDITION_TYPE_AVAILABLE = "Available" +const CONDITION_REASON_AVAILABLE = "Available" +const CONDITION_TYPE_RECONCILE_QUEUED = "ReconcileQueued" +const CONDITION_REASON_RECONCILE_QUEUED = "LastReconcileQueued" +const CONDITION_TYPE_RECONCILE_ERROR = "ReconfileError" +const CONDITION_REASON_RECONCILE_ERROR = "LastReconcileError" func updateCondition(adb *dbv4.AutonomousDatabase, err error) { var condition metav1.Condition + var errMsg string - errMsg := func() string { - if err != nil { - return err.Error() + if err != nil { + errMsg = err.Error() + } + + // Clean up the Conditions array + if len(adb.Status.Conditions) > 0 { + var allConditions = []string{ + CONDITION_TYPE_AVAILABLE, + CONDITION_TYPE_RECONCILE_QUEUED, + CONDITION_TYPE_RECONCILE_ERROR} + + for _, conditionType := range allConditions { + meta.RemoveStatusCondition(&adb.Status.Conditions, conditionType) } - return "no reconcile errors" - }() + } - // If error occurs, ReconcileComplete will be marked as true and the error message will still be listed - // If the ADB lifecycleState is intermediate, then ReconcileComplete will be marked as false + // If error occurs, the condition status will be marked as false and the error message will still be listed + // If the ADB lifecycleState is intermediate, then condition status will be marked as true + // Otherwise, then condition status will be marked as true if no error occurs if err != nil { condition = metav1.Condition{ - Type: CONDITION_TYPE_COMPLETE, + Type: CONDITION_TYPE_RECONCILE_ERROR, LastTransitionTime: metav1.Now(), ObservedGeneration: adb.GetGeneration(), - Reason: CONDITION_REASON_COMPLETE, + Reason: CONDITION_REASON_RECONCILE_ERROR, Message: errMsg, - Status: metav1.ConditionTrue, + Status: metav1.ConditionFalse, } } else if dbv4.IsAdbIntermediateState(adb.Status.LifecycleState) { condition = metav1.Condition{ - Type: CONDITION_TYPE_COMPLETE, + Type: CONDITION_TYPE_RECONCILE_QUEUED, LastTransitionTime: metav1.Now(), ObservedGeneration: adb.GetGeneration(), - Reason: CONDITION_REASON_COMPLETE, - Message: errMsg, - Status: metav1.ConditionFalse, + Reason: CONDITION_REASON_RECONCILE_QUEUED, + Message: "no reconcile errors", + Status: metav1.ConditionTrue, } } else { condition = metav1.Condition{ - Type: CONDITION_TYPE_COMPLETE, + Type: CONDITION_TYPE_AVAILABLE, LastTransitionTime: metav1.Now(), ObservedGeneration: adb.GetGeneration(), - Reason: CONDITION_REASON_COMPLETE, - Message: errMsg, + Reason: CONDITION_REASON_AVAILABLE, + Message: "no reconcile errors", Status: metav1.ConditionTrue, } } - if len(adb.Status.Conditions) > 0 { - meta.RemoveStatusCondition(&adb.Status.Conditions, condition.Type) - } meta.SetStatusCondition(&adb.Status.Conditions, condition) } From b0bd73891e17b2846f4c77f3958a36058a351484 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Fri, 14 Feb 2025 05:31:23 +0000 Subject: [PATCH 203/414] Update README.md --- docs/sidb/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sidb/README.md b/docs/sidb/README.md index bdc9a457..92af1880 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -73,7 +73,7 @@ Oracle strongly recommends that you comply with the [prerequisites](./PREREQUISI | Secrets | create delete get list patch update watch | | Events | create patch | - For managing the required levels of access, configure [role binding](../../README.md#role-binding-for-access-management) + For managing the required levels of access, configure [role binding](../../README.md#create-role-bindings-for-access-management) ### Optional Resource Privileges From b32302e47175ee6ca643401d3b3c949dd29737f6 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Fri, 14 Feb 2025 07:05:02 +0000 Subject: [PATCH 204/414] Observability - doc updates --- README.md | 4 ++-- docs/observability/README.md | 20 +++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d0d236e7..9fd1b5d5 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,9 @@ In this v1.2.0 production release, `OraOperator` supports the following database * Database Observability (preview) - Support for Database Logs (in addition to Metrics) - Support for the latest Exporter container images - + - Bug Fix: Prometheus label config * Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation. -* Prometheus label config (bug fix) + ## New Product Features *The Operator itself, as a product, brings the following new features: diff --git a/docs/observability/README.md b/docs/observability/README.md index 1da6c30d..5a281c9c 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -31,6 +31,7 @@ of the controller. * [Custom Exporter Image or Version](#custom-exporter-image-or-version) * [Mandatory Roles and Privileges](#mandatory-roles-and-privileges-requirements-for-observability-controller) * [Debugging and troubleshooting](#debugging-and-troubleshooting) +* [Known Issues](#known-issues) ## Prerequisites The `DatabaseObserver` custom resource has the following prerequisites: @@ -297,7 +298,7 @@ spec: ``` ### Prometheus Release -To enable your Prometheus configuration to find and include the `ServiceMonitor` created by the `databaseObserver` resource, the field `spec.prometheus.serviceMonitor.labels` is an important and required field. The label on the ServiceMonitor +To enable your Prometheus configuration to find and include the `ServiceMonitor` created by the `databaseObserver` resource, the field `spec.prometheus.serviceMonitor.labels` is an __important__ and __required__ field. The label on the ServiceMonitor must match the `spec.serviceMonitorSelector` field in your Prometheus configuration. ```yaml @@ -343,6 +344,8 @@ These details can be used with any sidecar containers, or with other containers. If `spec.log.volume.persistentVolumeClaim.claimName` is not specified, then an `EmptyDir` volume is automatically used. +> Important Note: the volume name must match all references of the volume, such as in any sidecar containers that use and mount this volume. + ```yaml log: volume: @@ -357,8 +360,7 @@ The security context defines privilege and access control settings for a pod con spec: exporter: deployment: - securityContext: - supplementalGroups: [1000] + runAsUser: 1000 ``` Configuring security context under the PodTemplate is also possible. You can set this object under: `spec.exporter.deployment.podTemplate.securityContext` @@ -394,6 +396,8 @@ For example, to deploy a Grafana Promtail image, you can specify the container a mountPath: /log ``` +> Important Note: Make sure the volumeMount name matches the actual name of the volumes referenced. In this case, `my-log-volume` is referenced in `spec.log.volume.name`. + In the field `spec.sidecarVolumes`, you can specify and list the volumes you need in your sidecar containers. The field `spec.sidecarVolumes` is an array of Volumes (`[]corev1.Volume`). @@ -447,7 +451,7 @@ __About the Default Label__ - The resources created by the Observability Control For example, if the `databaseObserver` instance is named: `metrics-exporter`, then resources such as the deployment will be labelled -with `app: metrics-exporter`. This label cannot be overwritten. Selectors used by the deployment, service and servicemonitor use this label. +with `app: metrics-exporter`. This label `cannot be overwritten` as this label is used by multiple resources created. Selectors used by the deployment, service and servicemonitor use this label. The following configuration shows an example: @@ -491,7 +495,7 @@ container image. spec: exporter: deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.1.0" + image: "container-registry.oracle.com/database/observability-exporter:1.5.3" ``` ### Custom Environment Variables, Arguments and Commands @@ -595,6 +599,12 @@ Follow these steps to check the logs. kubectl logs deployment.apps/oracle-database-operator-controller-manager -n oracle-database-operator-system ``` +## Known Potential Issues + +| Issue | Example error | Potential Workaround | +|---------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| Pod may encounter error Permission denied when creating log file. Pod cannot access file system due to insufficient permissions | ```level=error msg="Failed to create the log file: /log/alert.log"``` | Configure securityContext in the spec, add your group ID to the `supplementalgroups` inside `spec.exporter.deployment.podTemplate.securityContext` field. | + ## Resources - [GitHub - Unified Observability for Oracle Database Project](https://github.com/oracle/oracle-db-appdev-monitoring) From 1a331bf94db882bee4b19fd1f598538c0d136a7c Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Tue, 18 Feb 2025 03:12:52 +0000 Subject: [PATCH 205/414] doc changes for v4 --- .../debugging/sharding_provisioning_with_db_events.yaml | 5 ++--- .../free/sharding_provisioning_with_free_images.yaml | 3 +-- .../snr_system_sharding/snr_ssharding_shard_prov.yaml | 5 ++--- .../snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml | 3 +-- .../snr_system_sharding/snr_ssharding_shard_prov_clone.yaml | 5 ++--- .../snr_ssharding_shard_prov_clone_across_ads.yaml | 5 ++--- .../snr_ssharding_shard_prov_delshard.yaml | 5 ++--- .../snr_ssharding_shard_prov_extshard.yaml | 5 ++--- .../snr_ssharding_shard_prov_memory_cpu.yaml | 5 ++--- .../snr_ssharding_shard_prov_send_notification.yaml | 5 ++--- .../provisioning/system_sharding/ssharding_shard_prov.yaml | 5 ++--- .../system_sharding/ssharding_shard_prov_chunks.yaml | 3 +-- .../system_sharding/ssharding_shard_prov_clone.yaml | 3 +-- .../ssharding_shard_prov_clone_across_ads.yaml | 3 +-- .../system_sharding/ssharding_shard_prov_delshard.yaml | 5 ++--- .../system_sharding/ssharding_shard_prov_extshard.yaml | 5 ++--- .../system_sharding/ssharding_shard_prov_memory_cpu.yaml | 3 +-- .../ssharding_shard_prov_send_notification.yaml | 3 +-- .../user-defined-sharding/udsharding_shard_prov.yaml | 3 +-- .../user-defined-sharding/udsharding_shard_prov_clone.yaml | 3 +-- .../udsharding_shard_prov_clone_across_ads.yaml | 3 +-- .../udsharding_shard_prov_delshard.yaml | 5 ++--- .../udsharding_shard_prov_extshard.yaml | 3 +-- .../udsharding_shard_prov_memory_cpu.yaml | 3 +-- .../udsharding_shard_prov_send_notification.yaml | 3 +-- 25 files changed, 37 insertions(+), 62 deletions(-) diff --git a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml index 7d136d58..ec95eab5 100644 --- a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml +++ b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -65,5 +65,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml index 7e39b3b2..954ede63 100644 --- a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml +++ b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -55,4 +55,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml index efe3abec..e6b35b8b 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -54,5 +54,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml index a79eafdc..def7e73a 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -58,4 +58,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml index 218fda0a..1f986529 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -79,5 +79,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml index 4eb3954a..9d0439c4 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -87,5 +87,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml index 145ef616..ac22e5fe 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -65,5 +65,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml index ea0c05a5..c3761f64 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -64,5 +64,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml index 5c15c724..7137d912 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -85,5 +85,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml index 50c85443..54039d79 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -81,5 +81,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml index 7d4e16ec..8fbe5438 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -53,5 +53,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml index 39e6a6ff..5c135229 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -57,4 +57,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml index 9a690626..f5816a87 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -79,4 +79,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml index b7dd1397..8fee0526 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -87,4 +87,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml index 75caca31..ecbb50ab 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -64,5 +64,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml index d25dc901..670f04fe 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -63,5 +63,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml index 793a3e4d..3f092b89 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -85,4 +85,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml index 96217b74..0ca6ec6f 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -81,5 +81,4 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml index 9b565b73..d33be599 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -55,4 +55,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml index adc2271f..04ee5d95 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -80,4 +80,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml index 28f36608..5be6ecde 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -88,4 +88,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml index 2342dc55..b0058758 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -65,5 +65,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - namespace: shns \ No newline at end of file + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml index f45d421f..3899f2ab 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -64,4 +64,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml index e663aa65..6c65916e 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -86,4 +86,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml index afd951fe..ef1b5561 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: ShardingDatabase metadata: name: shardingdatabase-sample @@ -82,4 +82,3 @@ spec: role: primary - name: oltp_ro_svc role: primary - namespace: shns From 9838a8ec2c55fa2c92bc6084894aeeacf1d08117 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Fri, 7 Mar 2025 17:50:23 +0000 Subject: [PATCH 206/414] Added fix --- oracle-database-operator.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index d9bededf..0cfb8d40 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -14200,7 +14200,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/operatormntnns:latest + image: container-registry.oracle.com/database/operator:1.2.0 imagePullPolicy: Always name: manager ports: From ca9b29426c6b2d1f58861507bdbaa4e944c28627 Mon Sep 17 00:00:00 2001 From: Kuassi Mensah Date: Tue, 11 Mar 2025 01:47:27 -0700 Subject: [PATCH 207/414] Update Readme.md Rephrasing and fixing minor issues. --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9fd1b5d5..594584d7 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,16 @@ In this v1.2.0 production release, `OraOperator` supports the following database * Oracle Multitenant Databases (CDB/PDBs) * Oracle Base Database Service (OBDS) on Oracle Cloud Infrastructure (OCI) * Oracle Data Guard -* Oracle Database Observability * Oracle Database Rest Service (ORDS) instances -## New Lifecycle Features in V1.2.0 Release (Controllers Enhancements) +## New Product Features in V1.2.0 +The Operator itself, as a product, brings the following new features: +* Upgraded Kubernetes API version to V4 +* Published on `operatorhub.io` +* Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) +* Validated on Google Kubernetes Engine + +## New Services and Lifecycle Enhancements (Controllers Enhancements) in V1.2.0 * ORDSSERVICES - Install on SIDB and ADB - Provision and Delete ORDS instances @@ -44,15 +50,9 @@ In this v1.2.0 production release, `OraOperator` supports the following database * Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation. -## New Product Features -*The Operator itself, as a product, brings the following new features: -* Published on `operatorhub.io` -* Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) -* Validated on Google Kubernetes Engine - ## Overall Features Summary -This release of Oracle Database Operator for Kubernetes (the operator) supports the following lifecycle operations: +As of release v1.2, the Oracle Database Operator for Kubernetes ( OraOperator) supports the following lifecycle operations: * ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning. * ACD: Provision, bind, restart, terminate (soft/hard) From 780bb3571fe8c084148ea0988bfecdb1f2419402 Mon Sep 17 00:00:00 2001 From: Kuassi Mensah Date: Thu, 13 Mar 2025 04:48:20 -0700 Subject: [PATCH 208/414] Update Readme Clarifies K8s platforms tests and releases. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 594584d7..31e05ef3 100644 --- a/README.md +++ b/README.md @@ -68,13 +68,15 @@ As of release v1.2, the Oracle Database Operator for Kubernetes ( OraOperator) s ## Release Status -This production release has been installed and tested on the following Kubernetes platforms: +This production release has been installed and tested on: +* [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) + +The previous releases were tested on the following platforms: * [Oracle Container Engine for Kubernetes (OKE)](https://www.oracle.com/cloud-native/container-engine-kubernetes/) with Kubernetes 1.24 * [Oracle Linux Cloud Native Environment(OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) 1.6 * [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) * [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/) -* [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) * [Red Hat OKD](https://www.okd.io/) * [Minikube](https://minikube.sigs.k8s.io/docs/) with version v1.29.0 From c2cc822e116e16dd07d2715f7bba1ac7e622175a Mon Sep 17 00:00:00 2001 From: racpack Date: Mon, 17 Mar 2025 08:42:14 +0000 Subject: [PATCH 209/414] Dboper v1.2.0 release branch --- PREREQUISITES.md | 6 +- README.md | 42 +- SECURITY.md | 2 +- commons/dbcssystem/dbcs_reconciler.go | 5 + .../bases/database.oracle.com_DbcsSystem.yaml | 240 +++++ .../crd/bases/database.oracle.com_pdbs.yaml | 8 + ...database.oracle.com_shardingdatabases.yaml | 9 + .../observability/databaseobserver.yaml | 44 + .../databaseobserver_custom_config.yaml | 28 + .../databaseobserver_minimal.yaml | 22 + .../observability/databaseobserver_vault.yaml | 25 + config/webhook/manifests.yaml | 41 + ..._with_minimal_parameters_sample_output.log | 2 +- docs/multitenant/NamespaceSeg.md | 14 + .../singlenamespace/cdb_secret.yaml | 17 + .../singlenamespace/pdb_clone.yaml | 6 + .../singlenamespace/pdb_plug.yaml | 8 +- .../singlenamespace/pdb_secret.yaml | 16 + .../singlenamespace/pdb_unplug.yaml | 6 + .../ords-based/usecase01/README.md | 2 - docs/multitenant/ords-based/usecase01/ca.crt | 25 + docs/multitenant/ords-based/usecase01/ca.key | 27 + docs/multitenant/ords-based/usecase01/ca.srl | 1 + .../ords-based/usecase01/cdb_create.yaml | 44 + .../ords-based/usecase01/cdb_secret.yaml | 17 + .../ords-based/usecase01/extfile.txt | 1 + .../usecase01/oracle-database-operator.yaml | 1 + .../ords-based/usecase01/pdb_close.yaml | 44 + .../ords-based/usecase01/pdb_create.yaml | 47 + .../ords-based/usecase01/pdb_delete.yaml | 34 + .../ords-based/usecase01/pdb_map.yaml | 45 + .../ords-based/usecase01/pdb_open.yaml | 43 + .../ords-based/usecase01/pdb_secret.yaml | 16 + .../ords-based/usecase01/server.csr | 18 + .../ords-based/usecase01/tde_secret.yaml | 17 + docs/multitenant/ords-based/usecase01/tls.crt | 24 + docs/multitenant/ords-based/usecase01/tls.key | 28 + docs/multitenant/provisioning/ords_image.md | 81 ++ .../usecase01/logfiles/BuildImage.log | 896 ++++++++++++++++++ .../usecase01/logfiles/openssl_execution.log | 19 + .../usecase01/logfiles/ordsconfig.log | 39 + docs/multitenant/usecase01/makefile | 284 ++++++ docs/multitenant/usecase03/Dockerfile | 80 ++ .../usecase03/NamespaceSegregation.png | Bin 0 -> 270813 bytes docs/multitenant/usecase03/README.md | 268 ++++++ docs/multitenant/usecase03/cdb_create.yaml | 44 + .../usecase03/cdb_creation_log.txt | 336 +++++++ docs/multitenant/usecase03/cdb_secret.yaml | 17 + docs/multitenant/usecase03/gentlscert.sh | 23 + docs/multitenant/usecase03/makefile | 285 ++++++ .../usecase03/ns_namespace_cdb.yaml | 7 + .../usecase03/ns_namespace_pdb.yaml | 7 + .../usecase03/operator_creation_log.txt | 27 + docs/multitenant/usecase03/pdb_create.yaml | 46 + .../usecase03/pdb_creation_log.txt | 6 + docs/multitenant/usecase03/pdb_secret.yaml | 16 + docs/multitenant/usecase03/runOrdsSSL.sh | 190 ++++ docs/sharding/README.md | 109 ++- .../provisioning/database_connection.md | 2 +- docs/sharding/provisioning/debugging.md | 2 +- .../sharding_provisioning_with_db_events.yaml | 2 +- ..._persistent_volume_having_db_gold_image.md | 2 +- .../snr_ssharding_shard_prov.yaml | 2 +- .../snr_ssharding_shard_prov_clone.yaml | 2 +- ...ssharding_shard_prov_clone_across_ads.yaml | 2 +- .../snr_ssharding_shard_prov_delshard.yaml | 2 +- .../snr_ssharding_shard_prov_extshard.yaml | 2 +- .../snr_ssharding_shard_prov_memory_cpu.yaml | 2 +- ...sharding_shard_prov_send_notification.yaml | 2 +- ...y_cloning_db_from_gold_image_across_ads.md | 2 +- ...ing_by_cloning_db_gold_image_in_same_ad.md | 2 +- .../system_sharding/ssharding_shard_prov.yaml | 2 +- .../ssharding_shard_prov_delshard.yaml | 2 +- .../ssharding_shard_prov_extshard.yaml | 2 +- .../udsharding_shard_prov_delshard.yaml | 2 +- docs/sidb/README.md | 3 + oracle-database-operator.yaml | 54 ++ ords/Dockerfile | 1 - 78 files changed, 3792 insertions(+), 55 deletions(-) create mode 100644 config/crd/bases/database.oracle.com_DbcsSystem.yaml create mode 100644 config/samples/observability/databaseobserver.yaml create mode 100644 config/samples/observability/databaseobserver_custom_config.yaml create mode 100644 config/samples/observability/databaseobserver_minimal.yaml create mode 100644 config/samples/observability/databaseobserver_vault.yaml create mode 100644 docs/multitenant/NamespaceSeg.md create mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml create mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml create mode 100644 docs/multitenant/ords-based/usecase01/ca.crt create mode 100644 docs/multitenant/ords-based/usecase01/ca.key create mode 100644 docs/multitenant/ords-based/usecase01/ca.srl create mode 100644 docs/multitenant/ords-based/usecase01/cdb_create.yaml create mode 100644 docs/multitenant/ords-based/usecase01/cdb_secret.yaml create mode 100644 docs/multitenant/ords-based/usecase01/extfile.txt create mode 120000 docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml create mode 100644 docs/multitenant/ords-based/usecase01/pdb_close.yaml create mode 100644 docs/multitenant/ords-based/usecase01/pdb_create.yaml create mode 100644 docs/multitenant/ords-based/usecase01/pdb_delete.yaml create mode 100644 docs/multitenant/ords-based/usecase01/pdb_map.yaml create mode 100644 docs/multitenant/ords-based/usecase01/pdb_open.yaml create mode 100644 docs/multitenant/ords-based/usecase01/pdb_secret.yaml create mode 100644 docs/multitenant/ords-based/usecase01/server.csr create mode 100644 docs/multitenant/ords-based/usecase01/tde_secret.yaml create mode 100644 docs/multitenant/ords-based/usecase01/tls.crt create mode 100644 docs/multitenant/ords-based/usecase01/tls.key create mode 100644 docs/multitenant/provisioning/ords_image.md create mode 100644 docs/multitenant/usecase01/logfiles/BuildImage.log create mode 100644 docs/multitenant/usecase01/logfiles/openssl_execution.log create mode 100644 docs/multitenant/usecase01/logfiles/ordsconfig.log create mode 100644 docs/multitenant/usecase01/makefile create mode 100644 docs/multitenant/usecase03/Dockerfile create mode 100644 docs/multitenant/usecase03/NamespaceSegregation.png create mode 100644 docs/multitenant/usecase03/README.md create mode 100644 docs/multitenant/usecase03/cdb_create.yaml create mode 100644 docs/multitenant/usecase03/cdb_creation_log.txt create mode 100644 docs/multitenant/usecase03/cdb_secret.yaml create mode 100644 docs/multitenant/usecase03/gentlscert.sh create mode 100644 docs/multitenant/usecase03/makefile create mode 100644 docs/multitenant/usecase03/ns_namespace_cdb.yaml create mode 100644 docs/multitenant/usecase03/ns_namespace_pdb.yaml create mode 100644 docs/multitenant/usecase03/operator_creation_log.txt create mode 100644 docs/multitenant/usecase03/pdb_create.yaml create mode 100644 docs/multitenant/usecase03/pdb_creation_log.txt create mode 100644 docs/multitenant/usecase03/pdb_secret.yaml create mode 100644 docs/multitenant/usecase03/runOrdsSSL.sh diff --git a/PREREQUISITES.md b/PREREQUISITES.md index 0cbd0b24..3c73ad4b 100644 --- a/PREREQUISITES.md +++ b/PREREQUISITES.md @@ -6,7 +6,7 @@ Oracle Database Operator for Kubernetes (`OraOperator`) manages all Cloud deploy * Oracle Autonomous Database (ADB) * Containerized Oracle Database Single Instance (SIDB) -* Containerized Sharded Oracle Database (SHARDING) +* Containerized Oracle Globally Distributed Database (GDD) ### Setting Up a Kubernetes Cluster and Volumes Review and complete each step as needed. @@ -29,6 +29,6 @@ If you intend to use `OraOperator` to handle Oracle Autonomous Database lifecycl If you intend to use `OraOperator` to handle Oracle Database Single Instance lifecycles, then read [Single Instance Database Prerequisites](./docs/sidb/PREREQUISITES.md) -### Prerequites for Sharded Databases (SHARDING) +### Prerequites for Oracle Globally Distributed Databases(GDD) - If you intend to use OraOperator to handle the lifecycle of Oracle Database deployed with Oracle Sharding, then read [Sharded Database Prerequisites](./docs/sharding/README.md#prerequsites-for-running-oracle-sharding-database-controller) + If you intent to use OraOperator to handle the lifecycle of Oracle Globally Distributed Database(GDD), then read [Oracle Globally Distributed Database Prerequisites](./docs/sharding/README.md#prerequsites-for-running-oracle-sharding-database-controller) diff --git a/README.md b/README.md index 31e05ef3..18cceced 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ In this v1.2.0 production release, `OraOperator` supports the following database * Oracle Autonomous Database on dedicated Cloud infrastructure (ADB-D) * Oracle Autonomous Container Database (ACD), the infrastructure for provisioning Autonomous Databases. * Containerized Single Instance databases (SIDB) deployed in the Oracle Kubernetes Engine (OKE) and any k8s where OraOperator is deployed -* Containerized Sharded databases (SHARDED) deployed in OKE and any k8s where OraOperator is deployed +* Containerized Oracle Globally Distributed Databases(GDD) deployed in OKE and any k8s where OraOperator is deployed * Oracle Multitenant Databases (CDB/PDBs) * Oracle Base Database Service (OBDS) on Oracle Cloud Infrastructure (OCI) * Oracle Data Guard @@ -49,6 +49,14 @@ The Operator itself, as a product, brings the following new features: - Bug Fix: Prometheus label config * Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation. +<<<<<<< README.md +## New Product Features +* The Operator itself, as a product, brings the following new features: +* Published on `operatorhub.io` +* Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) +* Validated on Google Kubernetes Engine +======= +>>>>>>> README.md ## Overall Features Summary @@ -136,7 +144,6 @@ Oracle strongly recommends that you ensure your system meets the following [Prer kubectl apply -f oracle-database-operator.yaml ``` - * ### ClusterRole and ClusterRoleBinding for NodePort services To expose services on each node's IP and port (the NodePort), apply the [`node-rbac.yaml`](./rbac/node-rbac.yaml). Note that this step is not required for LoadBalancer services. @@ -144,6 +151,16 @@ Oracle strongly recommends that you ensure your system meets the following [Prer ```sh kubectl apply -f rbac/node-rbac.yaml ``` +## Installation +### Install Oracle DB Operator + + After you have completed the preceding prerequisite changes, you can install the operator. To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the preceding step. + + Run the following command + + ```sh + kubectl apply -f oracle-database-operator.yaml + ``` ## Install Oracle DB Operator @@ -172,7 +189,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer You should see that the operator is up and running, along with the shipped controllers. For more details, see [Oracle Database Operator Installation Instructions](./docs/installation/OPERATOR_INSTALLATION_README.md). - +## Documentation ## Getting Started with the Operator (Quickstart) The following quickstarts are designed for specific database configurations: @@ -180,7 +197,7 @@ The following quickstarts are designed for specific database configurations: * [Oracle Autonomous Database](./docs/adb/README.md) * [Oracle Autonomous Container Database](./docs/adb/ACD.md) * [Containerized Oracle Single Instance Database and Data Guard](./docs/sidb/README.md) -* [Containerized Oracle Sharded Database](./docs/sharding/README.md) +* [Containerized Oracle Globally Distributed Database](./docs/sharding/README.md) * [Oracle Multitenant Database](./docs/multitenant/README.md) * [Oracle Base Database Service (OBDS)](./docs/dbcs/README.md) @@ -188,6 +205,11 @@ The following quickstarts are designed for specific database configurations: The following quickstart is designed for non-database configurations: * [Oracle Database Observability](./docs/observability/README.md) + +The following quickstart is designed for non-database configurations: +* [Oracle Database Observability](./docs/observability/README.md) + +## Examples YAML file templates are available under [`/config/samples`](./config/samples/). You can copy and edit these template files to configure them for your use cases. ## Uninstall the Operator @@ -234,20 +256,20 @@ YAML file templates are available under [`/config/samples`](./config/samples/). * [Oracle Autonomous Database](https://docs.oracle.com/en-us/iaas/Content/Database/Concepts/adboverview.htm) * [Components of Dedicated Autonomous Database](https://docs.oracle.com/en-us/iaas/autonomous-database/doc/components.html) * [Oracle Database Single Instance](https://docs.oracle.com/en/database/oracle/oracle-database/) -* [Oracle Database Sharding](https://docs.oracle.com/en/database/oracle/oracle-database/21/shard/index.html) +* [Oracle Globally Distributed Database](https://docs.oracle.com/en/database/oracle/oracle-database/21/shard/index.html) * [Oracle Database Cloud Service](https://docs.oracle.com/en/database/database-cloud-services.html) ## Contributing -See [Contributing to this Repository](./CONTRIBUTING.md) +This project welcomes contributions from the community. Before submitting a pull request, please [review our contribution guide](./CONTRIBUTING.md) -## Support +## Help You can submit a GitHub issue, or submit an issue and then file an [Oracle Support service](https://support.oracle.com/portal/) request. To file an issue or a service request, use the following product ID: 14430. ## Security -Secure platforms are an important basis for general system security. Ensure that your deployment is in compliance with common security practices. +Please consult the [security guide](./SECURITY.md) for our responsible security vulnerability disclosure process ### Managing Sensitive Data @@ -262,10 +284,6 @@ The following is an example of a YAML file fragment for specifying Oracle Cloud Examples in this repository where passwords are entered on the command line are for demonstration purposes only. -### Reporting a Security Issue - -See [Reporting security vulnerabilities](./SECURITY.md) - ## License Copyright (c) 2022, 2025 Oracle and/or its affiliates. diff --git a/SECURITY.md b/SECURITY.md index fb238413..2ca81027 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -21,7 +21,7 @@ security features are welcome on GitHub Issues. Security updates will be released on a regular cadence. Many of our projects will typically release security fixes in conjunction with the -[Oracle Critical Patch Update][3] program. Additional +Oracle Critical Patch Update program. Additional information, including past advisories, is available on our [security alerts][4] page. diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 9978c1ab..6c498320 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -1323,6 +1323,11 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem return nil } + if dbcs.Spec.Id == nil { + dbcs.Status.State = "FAILED" + return nil + } + dbcsId := *dbcs.Spec.Id dbcsReq := database.GetDbSystemRequest{ diff --git a/config/crd/bases/database.oracle.com_DbcsSystem.yaml b/config/crd/bases/database.oracle.com_DbcsSystem.yaml new file mode 100644 index 00000000..e933d5a4 --- /dev/null +++ b/config/crd/bases/database.oracle.com_DbcsSystem.yaml @@ -0,0 +1,240 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: DbcsSystem.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: DbcsSystem + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: DbcsSystem is the Schema for the dbcssystems API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DbcsSystemSpec defines the desired state of DbcsSystem + properties: + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: + description: DB Backup COnfig Network Struct + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - sshPublicKeys + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + ociConfigMap: + type: string + ociSecret: + type: string + required: + - ociConfigMap + type: object + status: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbEdition: + type: string + dbInfo: + items: + description: DbcsSystemStatus defines the observed state of DbcsSystem + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index 938c5512..b674f856 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -100,10 +100,18 @@ spec: type: boolean assertivePdbDeletion: type: boolean + assertivePdbDeletion: + description: turn on the assertive approach to delete pdb resource + kubectl delete pdb ..... automatically triggers the pluggable database + deletion + type: boolean cdbName: type: string cdbNamespace: type: string + cdbNamespace: + description: CDB Namespace + type: string cdbResName: type: string copyAction: diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 90c6dd53..e46d883e 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -154,6 +154,9 @@ spec: directorName: type: string envVars: + description: Replicas int32 `json:"replicas,omitempty"` // + Gsm Replicas. If you set OraGsmPvcName then it is set default + to 1. items: properties: name: @@ -655,6 +658,12 @@ spec: x-kubernetes-int-or-string: true type: object type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string storageSizeInGb: format: int32 type: integer diff --git a/config/samples/observability/databaseobserver.yaml b/config/samples/observability/databaseobserver.yaml new file mode 100644 index 00000000..b3140549 --- /dev/null +++ b/config/samples/observability/databaseobserver.yaml @@ -0,0 +1,44 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + exporter: + image: "container-registry.oracle.com/database/observability-exporter:latest" + configuration: + configmap: + key: "config.toml" + configmapName: "devcm-oradevdb-config" + + service: + port: 9161 + + prometheus: + port: metrics + labels: + app: app-sample-label + + replicas: 1 + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + + diff --git a/config/samples/observability/databaseobserver_custom_config.yaml b/config/samples/observability/databaseobserver_custom_config.yaml new file mode 100644 index 00000000..1e9fff47 --- /dev/null +++ b/config/samples/observability/databaseobserver_custom_config.yaml @@ -0,0 +1,28 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample + namespace: observer +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + exporter: + configuration: + configmap: + key: "config.toml" + configmapName: "devcm-oradevdb-config" \ No newline at end of file diff --git a/config/samples/observability/databaseobserver_minimal.yaml b/config/samples/observability/databaseobserver_minimal.yaml new file mode 100644 index 00000000..2eeaf3ab --- /dev/null +++ b/config/samples/observability/databaseobserver_minimal.yaml @@ -0,0 +1,22 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample + namespace: observer +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallets \ No newline at end of file diff --git a/config/samples/observability/databaseobserver_vault.yaml b/config/samples/observability/databaseobserver_vault.yaml new file mode 100644 index 00000000..fa2e09d4 --- /dev/null +++ b/config/samples/observability/databaseobserver_vault.yaml @@ -0,0 +1,25 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + vaultSecretName: sample_secret + vaultOCID: ocid1.vault.oc1.. + + dbConnectionString: + key: "connection" + secret: db-secret + + dbWallet: + secret: instance-wallet + + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey \ No newline at end of file diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 0166160d..3a0f15ec 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -250,6 +250,26 @@ webhooks: resources: - shardingdatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: mshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -645,6 +665,27 @@ webhooks: resources: - shardingdatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: vshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log index 5135daf7..80860c51 100644 --- a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log @@ -79,4 +79,4 @@ Status: Time Accepted: 2024-12-10 05:19:52.499 +0000 UTC Time Finished: 2024-12-10 07:59:19.083 +0000 UTC Time Started: 2024-12-10 05:19:55.747 +0000 UTC -Events: \ No newline at end of file +Events: diff --git a/docs/multitenant/NamespaceSeg.md b/docs/multitenant/NamespaceSeg.md new file mode 100644 index 00000000..6738fe56 --- /dev/null +++ b/docs/multitenant/NamespaceSeg.md @@ -0,0 +1,14 @@ + + +# Namespace segregation + +With the namespace segregation pdb controller and cdb controller run in different namespaces. The new functionality introduces a new parameter (the cdb namespace) in pdb crd definition. In case you don't need the namespace segregation you have to sepcify the namespace name that you are using for yours crd and pods anyway. Refer to usercase01 and usecase02 to see single namespace configuration. Refer to usecase03 to see examples of namespace segregation. + +# Secrets + +In order to use multiple namespace we need to create approriate secrets in each namespace. Tls certificate secrets must be created in all namespaces (db-ca db-tls). + +![general_schema](./images/K8S_NAMESPACE_SEG.png) + + + diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml new file mode 100644 index 00000000..567b90a4 --- /dev/null +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: cdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + ords_pwd: ".....base64 encoded password...." + sysadmin_pwd: ".....base64 encoded password...." + cdbadmin_user: ".....base64 encoded password...." + cdbadmin_pwd: ".....base64 encoded password...." + webserver_user: ".....base64 encoded password...." + webserver_pwd: ".....base64 encoded password...." diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml index 21055be3..964d1e5e 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml @@ -41,6 +41,12 @@ spec: key: "ca.crt" webServerUser: secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" secretName: "[...]" key: "[...]" webServerPwd: diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml index bd575a8d..e6605276 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml @@ -36,6 +36,12 @@ spec: key: "ca.crt" webServerUser: secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" secretName: "[...]" key: "[...]" webServerPwd: @@ -47,5 +53,3 @@ spec: secretName: "prvkey" key: "privateKey" - - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml new file mode 100644 index 00000000..60d95d76 --- /dev/null +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: pdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + sysadmin_user: ".....base64 encoded password...." + sysadmin_pwd: ".....base64 encoded password...." + webserver_user: ".....base64 encoded password...." + webserver_pwd: ".....base64 encoded password...." + diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml index c001ae28..4e404efe 100644 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml +++ b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml @@ -30,6 +30,12 @@ spec: key: "ca.crt" webServerUser: secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" secretName: "[...]" key: "[...]" webServerPwd: diff --git a/docs/multitenant/ords-based/usecase01/README.md b/docs/multitenant/ords-based/usecase01/README.md index 85aad7c0..0020541c 100644 --- a/docs/multitenant/ords-based/usecase01/README.md +++ b/docs/multitenant/ords-based/usecase01/README.md @@ -163,7 +163,6 @@ GRANT SYSDBA TO CONTAINER = ALL; GRANT CREATE SESSION TO CONTAINER = ALL; ``` ---- - #### Create Certificates + Create certificates: At this stage we need to create certificates on our local machine and upload into kubernetes cluster by creating new secrets. @@ -371,7 +370,6 @@ NAME CDB NAME DB SERVER DB PORT REPLICAS STATUS MESSAG [Example of executions](./logfiles/ordsconfig.log) ----- - #### Apply pdb yaml file to create pdb ```bash diff --git a/docs/multitenant/ords-based/usecase01/ca.crt b/docs/multitenant/ords-based/usecase01/ca.crt new file mode 100644 index 00000000..cc9aa8bb --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/ca.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEJTCCAw2gAwIBAgIUNXPtpnNEFBCMcnxRP5kJsBDpafcwDQYJKoZIhvcNAQEL +BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH +DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k +ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE +AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx +NTMyMzVaMIGhMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG +A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j +ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxHDAa +BgNVBAMME2xvY2FsaG9zdCAgUm9vdCBDQSAwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCmnGVApwUBF1kpqcyr2nYeED0VKvefpoHLtxHSP+vP0lWhW7NU +NJlb1YuUagjJ4/rpGRQmPxcVU51n3aAW3a5qHazIpNxNa3fvgB1rMOPFxGmdel2d +8lIt+u19q19DknX/GNgH9Mog8RcyZyPeA7d2icT8TBo74ognr+8p68O3CjBHQ8EM +SnRQR7/bh1c10Uia317ilKvs+I7oErTq5JFLeIuPDdAJ6UncaeblTf1XJ/1FrpHG +fSS7xmR8x0/MblBQlku4eImYmN35g+eRgf8bLDDwC+GPzDnAqqMLjx6h2N+btDxr +tnn05qyqmN9G08uUlP4d4BXi9ISb/toYypklAgMBAAGjUzBRMB0GA1UdDgQWBBS+ +a4X2XTmdPivdQtqDWNpfOtHypDAfBgNVHSMEGDAWgBS+a4X2XTmdPivdQtqDWNpf +OtHypDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZIrGBNdSw +pe+1agefHfaR8hjZQiXBxdwHM1gR2LWOaFzMS8Q/eRETHTO6+VwQ0/FNaXbAqgqk +G317gZMXS5ZmXuOi28fTpAQtuzokkEKpoK0puTnbXOKGA2QSbBlpSFPqb3aJXvVt +afXFQb5P/0mhr4kuVt7Ech82WM/o5ryFgObygDayDmLatTp+VaRmBZPksnSMhslq +3zPyS7bx2YhbPTLkDxq8Mfr/Msxme8LvSXUpFf4PpQ5zwp1RE32gekct6eRQLmqU +5LXY2aPtqpMF0fBpcwPWbqA9gOYCRKcvXXIr+u1x8hf6Er6grZegHkM9TQ8s0hJd +sxi5tK0lPMHJ +-----END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase01/ca.key b/docs/multitenant/ords-based/usecase01/ca.key new file mode 100644 index 00000000..1a0ef89d --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAppxlQKcFARdZKanMq9p2HhA9FSr3n6aBy7cR0j/rz9JVoVuz +VDSZW9WLlGoIyeP66RkUJj8XFVOdZ92gFt2uah2syKTcTWt374AdazDjxcRpnXpd +nfJSLfrtfatfQ5J1/xjYB/TKIPEXMmcj3gO3donE/EwaO+KIJ6/vKevDtwowR0PB +DEp0UEe/24dXNdFImt9e4pSr7PiO6BK06uSRS3iLjw3QCelJ3Gnm5U39Vyf9Ra6R +xn0ku8ZkfMdPzG5QUJZLuHiJmJjd+YPnkYH/Gyww8Avhj8w5wKqjC48eodjfm7Q8 +a7Z59OasqpjfRtPLlJT+HeAV4vSEm/7aGMqZJQIDAQABAoIBAGXRGYdjCgnarOBr +Jeq3vIsuvUVcVqs35AYMQFXOPltoXHAZTAPfiQC4BW6TRf+q1MDyVH/y+jZMPNsm +cxjGLDopHFgZd4/QZyDzmAbTf75yA2D7UI6fcV0sBUpRGgx/SqC0HADwtT1gWB6z +LRYWC13jX4AXOcjy7OXj/DIQJDCMivedt3dv0rDWJUcBCnVot5tr6zjycefxGKa8 +mG9LZQb3x71FxwpFUau3WLDSwOjtXCeMytaGXnGmIiofJmXnFi0KA4ApzKL7QV6I +cCBS1WBLLXeVM9vOfrtzKVLWGe0qADyLm35p5Fnl3j+vimkk8h/2DEvCZ75c987m +O3PEgdkCgYEA0Scg+KINTA78sdZL5v2+8fT4b+EfoCgUqfr10ReUPKrz3HfrVHcj +7Vf00RT52TkfmkL3mIdLyBUzQ9vzPgweo1o4yKCKNCpR9G3ydNW+KI5jSYnq2efz +Gpe3wTt+8YoyCgm9eUxNWjfO9fipS91sSotY0PovkBohj9aezfcWp1sCgYEAy+3n +MIvW/9PoYxCvQ9fDGLvx3B4/uy0ZYPh7j5edDuaRzwFd2YXUysXhJVuqTp0KT2tv +dRPFRE9Oq5N8e5ITIUiKLQ5PIRNBZm8CiAof+XS1fIuU+MTDaTfXwyGQo0xSg8MB +ITnJulmUlkcTWEtGyBi9sIjor5ve8kqvyrdAKX8CgYA9ZUUSd0978jJPad6iEf6J +PCXpgaYs91cJhre+BzPmkzA+mZ0lEEwlkdo1vfiRwWj7eYkA50Zhl4eS9e/zWM9t +mEBu9GFdasbf/55amZvWf+W5YpjkGmiMd9jjCjn7YVvLAozyHGngf91q6vGXaYou +X7VUsvxfSqxrcs7vGwc1XQKBgB0qaD80MMqj5v+MGlTsndWCw8OEe/7sI04QG7Pc +rjS8Wyws+NwsXNOnW1z5cDEQGrJjHiyzaCot4YV+cXZG3P+MnV52RnDnjRn2VHla +YVpPC8nFOMgfdAcvWmdo/IOuXbrEf/vdhPFm8G5Ruf2NvpDNoQuHeSfsdgVXEy89 +6CpHAoGBAMZInYD0XjcnZNqiQnQdcIJN3CqDIU76Z45OOpcUrYrvTos2xhGLrRI5 +qrk5Od/sovJfse+oUIIbgsABieqtyfxM03iu8fvbahIY6Un1iw2KN9t+mcPrSZJK +jTXKf7XxZ1+yN9kvohdLc65ySyXFSm++glDq8WGrmnOtLUlr0oMm +-----END RSA PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase01/ca.srl b/docs/multitenant/ords-based/usecase01/ca.srl new file mode 100644 index 00000000..7c9868bb --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/ca.srl @@ -0,0 +1 @@ +77D97AB4C4B6D5A9377B84B455D3E16348C6DE04 diff --git a/docs/multitenant/ords-based/usecase01/cdb_create.yaml b/docs/multitenant/ords-based/usecase01/cdb_create.yaml new file mode 100644 index 00000000..01fc0a18 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/cdb_create.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: CDB +metadata: + name: cdb-dev + namespace: oracle-database-operator-system +spec: + cdbName: "DB12" + ordsImage: ".............your registry............./ords-dboper:latest" + ordsImagePullPolicy: "Always" + dbTnsurl : "...Container tns alias....." + replicas: 1 + sysAdminPwd: + secret: + secretName: "cdb1-secret" + key: "sysadmin_pwd" + ordsPwd: + secret: + secretName: "cdb1-secret" + key: "ords_pwd" + cdbAdminUser: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_user" + cdbAdminPwd: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_pwd" + webServerUser: + secret: + secretName: "cdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "cdb1-secret" + key: "webserver_pwd" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + diff --git a/docs/multitenant/ords-based/usecase01/cdb_secret.yaml b/docs/multitenant/ords-based/usecase01/cdb_secret.yaml new file mode 100644 index 00000000..567b90a4 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/cdb_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: cdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + ords_pwd: ".....base64 encoded password...." + sysadmin_pwd: ".....base64 encoded password...." + cdbadmin_user: ".....base64 encoded password...." + cdbadmin_pwd: ".....base64 encoded password...." + webserver_user: ".....base64 encoded password...." + webserver_pwd: ".....base64 encoded password...." diff --git a/docs/multitenant/ords-based/usecase01/extfile.txt b/docs/multitenant/ords-based/usecase01/extfile.txt new file mode 100644 index 00000000..c51d22a3 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/extfile.txt @@ -0,0 +1 @@ +subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com diff --git a/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml new file mode 120000 index 00000000..d5bae7bc --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml @@ -0,0 +1 @@ +../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/ords-based/usecase01/pdb_close.yaml b/docs/multitenant/ords-based/usecase01/pdb_close.yaml new file mode 100644 index 00000000..5917d33a --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/pdb_close.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + pdbState: "CLOSE" + modifyOption: "IMMEDIATE" + action: "Modify" + diff --git a/docs/multitenant/ords-based/usecase01/pdb_create.yaml b/docs/multitenant/ords-based/usecase01/pdb_create.yaml new file mode 100644 index 00000000..be3581ad --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/pdb_create.yaml @@ -0,0 +1,47 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + fileNameConversions: "NONE" + tdeImport: false + totalSize: "1G" + tempSize: "100M" + action: "Create" + assertivePdbDeletion: true + diff --git a/docs/multitenant/ords-based/usecase01/pdb_delete.yaml b/docs/multitenant/ords-based/usecase01/pdb_delete.yaml new file mode 100644 index 00000000..c22b546a --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/pdb_delete.yaml @@ -0,0 +1,34 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + pdbName: "pdbdev" + action: "Delete" + dropAction: "INCLUDING" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + diff --git a/docs/multitenant/ords-based/usecase01/pdb_map.yaml b/docs/multitenant/ords-based/usecase01/pdb_map.yaml new file mode 100644 index 00000000..3300a7fa --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/pdb_map.yaml @@ -0,0 +1,45 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + fileNameConversions: "NONE" + totalSize: "1G" + tempSize: "100M" + action: "Map" + assertivePdbDeletion: true diff --git a/docs/multitenant/ords-based/usecase01/pdb_open.yaml b/docs/multitenant/ords-based/usecase01/pdb_open.yaml new file mode 100644 index 00000000..25fdccc4 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/pdb_open.yaml @@ -0,0 +1,43 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: oracle-database-operator-system + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "oracle-database-operator-system" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + action: "Modify" + pdbState: "OPEN" + modifyOption: "READ WRITE" diff --git a/docs/multitenant/ords-based/usecase01/pdb_secret.yaml b/docs/multitenant/ords-based/usecase01/pdb_secret.yaml new file mode 100644 index 00000000..60d95d76 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/pdb_secret.yaml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: pdb1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + sysadmin_user: ".....base64 encoded password...." + sysadmin_pwd: ".....base64 encoded password...." + webserver_user: ".....base64 encoded password...." + webserver_pwd: ".....base64 encoded password...." + diff --git a/docs/multitenant/ords-based/usecase01/server.csr b/docs/multitenant/ords-based/usecase01/server.csr new file mode 100644 index 00000000..e308d301 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/server.csr @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIC3TCCAcUCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh +MRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNV +BAMMLWNkYi1kZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVt +IDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAm9nlNSQNsPTVqH57MkWKZEyaVtzVKQ8Z3oDK6hWXfB24p0jVj6sTOJkf +NVAxnqmU8DpW3odpbU6qWe/n+B5vJpqdXUGdsq9NKyus2fGb/xf1UnskpA2FUuWZ +o3upyCFxDAOvE4eZUzlxIn+54XXaNAdQiU9E8VXPr5YxrvZ15T/xCXLtJPs/RCOF +cJ8+gvZGcjMbdP16auJDVWZzBaur3eKbiHN7LXNCCRzGO++dv0kGY8vH7MyFfgp3 +qYBiSHS3WDiFUJjYIvfa8lLfP1hnlCyHn8TnU9gjGjmd1YcccSKqWIAT24wPUKVU +Lme4n91jxDPp7g8nRtDw0Smj9gYCtQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEB +AGOG/9IJJRvT2JLcuzE5Arai1XHc6Jh65iuDRqXQav47Bz38FFF2gZNO69gzDmhq +6k7tie+5bPcAHuuJZ0dAa71a9SLjKl+XNkkI0vS6te6OK3DCVUoMqNCk5VdwrJw0 +RORbKUwgLEG6mu80Gc/6wCdeR/36hoYTMeNPjm6M9e+X5ppsXqxCNsgDxasJFT82 +FejuJE2sZ6RCradlDToUHNS1dMLoW0WAIISqOmrDvEI6snm9ZZr3Sxo1auEtpI6v +NllBM4AgEghy/2mAtke+By4WHCfXBpxEGv9S7ATqJHYrR5Qa3nwx0eojWW1vmn0/ +aEzslX1tAH6oz2jA6QZ0sNo= +-----END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/ords-based/usecase01/tde_secret.yaml b/docs/multitenant/ords-based/usecase01/tde_secret.yaml new file mode 100644 index 00000000..7cf66c03 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/tde_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: tde1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + tdepassword: "bW1hbHZlenoK" + tdesecret: "bW1hbHZlenoK" + + + + diff --git a/docs/multitenant/ords-based/usecase01/tls.crt b/docs/multitenant/ords-based/usecase01/tls.crt new file mode 100644 index 00000000..6bf8aef4 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/tls.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEFDCCAvygAwIBAgIUd9l6tMS21ak3e4S0VdPhY0jG3gQwDQYJKoZIhvcNAQEL +BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH +DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k +ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE +AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx +NTMyMzVaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG +A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j +ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxEjAQ +BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AJvZ5TUkDbD01ah+ezJFimRMmlbc1SkPGd6AyuoVl3wduKdI1Y+rEziZHzVQMZ6p +lPA6Vt6HaW1Oqlnv5/gebyaanV1BnbKvTSsrrNnxm/8X9VJ7JKQNhVLlmaN7qcgh +cQwDrxOHmVM5cSJ/ueF12jQHUIlPRPFVz6+WMa72deU/8Qly7ST7P0QjhXCfPoL2 +RnIzG3T9emriQ1VmcwWrq93im4hzey1zQgkcxjvvnb9JBmPLx+zMhX4Kd6mAYkh0 +t1g4hVCY2CL32vJS3z9YZ5Qsh5/E51PYIxo5ndWHHHEiqliAE9uMD1ClVC5nuJ/d +Y8Qz6e4PJ0bQ8NEpo/YGArUCAwEAAaNMMEowSAYDVR0RBEEwP4IsY2RiLWRldi1v +cmRzLm9yYWNsZS1kYXRhYmFzZS1vcGVyYXRvci1zeXN0ZW2CD3d3dy5leGFtcGxl +LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAh7Lsu2ITS6Bc2q/Ef4No5Us0Vo9BWKoL +AlrfQPjsv1erMGsyEEyZ0Cg8l3QrXlscQ1ESvx0BnRGjoqZGE4+PoVZTEYSkokXP +aAr69epPzXQRyyAGCg5GeL6IFAj1AzqJGNnKOrPaLpcTri4MboiWmW+MHmgLdyPK +iwl8bNa8841nK/L/m6QET15BI+MIAvn7pgcpztum5jmkB+eceXzXnKUGg77TaFiX +bXqVBR4EvexC4DgUfQJI4zJLFdcH/GHxCpaaXNjbXeVz1ZK/qo2TCrXp2UXVrznU +9VTUuCaQA2VYZCitvAbupt+1OvMFYhWiIAroJSmzrvH4oK+IXgY6GA== +-----END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase01/tls.key b/docs/multitenant/ords-based/usecase01/tls.key new file mode 100644 index 00000000..666c5639 --- /dev/null +++ b/docs/multitenant/ords-based/usecase01/tls.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb2eU1JA2w9NWo +fnsyRYpkTJpW3NUpDxnegMrqFZd8HbinSNWPqxM4mR81UDGeqZTwOlbeh2ltTqpZ +7+f4Hm8mmp1dQZ2yr00rK6zZ8Zv/F/VSeySkDYVS5Zmje6nIIXEMA68Th5lTOXEi +f7nhddo0B1CJT0TxVc+vljGu9nXlP/EJcu0k+z9EI4Vwnz6C9kZyMxt0/Xpq4kNV +ZnMFq6vd4puIc3stc0IJHMY7752/SQZjy8fszIV+CnepgGJIdLdYOIVQmNgi99ry +Ut8/WGeULIefxOdT2CMaOZ3VhxxxIqpYgBPbjA9QpVQuZ7if3WPEM+nuDydG0PDR +KaP2BgK1AgMBAAECggEAKUwl1l0FW7yk2Q8a6glPUKCTzSybN1QPEMyj+D9ccsEV +aw57uKQmZbr9cA0d+OMK2lU7K6BKKXLM5SQTHcZCwcH6rPl0JiMZmbTrCp1hLslU +clS7MtV6XKsGeTGNncBuyjY3sD8gO9NezTt3L+0gsuS1TI06wZBxhh+QbsJUHzjW +bC3mNjD4SqXree4Snp05nlFaT2s2isIjj25mKDwBu8IX0BN2VjsaSiQcjb8Dmzmu +42Xh7bcWBebns8Ehuq9TIl6ZjQht+pmVOMlB862baVpW/9CxkknzM+UQhIkXTSJk +Jt/mGeO89V4/Zh2N4ixIOE1hw87EvRFBoYh2VF58QQKBgQDMujXYblh+eEdsB1LG +kY0LerFHuQgdzifYmjPl0jtBsWDmh5i6q9PRUs2JZ/Fsq4QMQ8SLinGzaIBq5FKr +CL067X5blrFA9H0D6exJI3iHBTQpeMFwtqvu3j+zpCmgzonaUDQrczUpc0hxU7YI +/jhDe9LSWknPrzzMoWWKuy0sTQKBgQDC4g8F2krqm9Q5ug8bRKTAvMrY0skFIwrP +5LXBq9C8YCnLnT4S4tYQfbnWaBeG7YpkkmkZe30c9MUjsr1OHZbo+jlxHBU+oRYZ +e1j0UorVGt7FfNe/zjW0fLd72CBO741EDvV6pVeItkAwH6P5/cbRu085dwvyFbxv +JmOaYddECQKBgQCuid6YG1NE10SE3CV89uAZtktny18ZEgY0ixrNx5MPaaskPtw9 +4Xofjol+qOhR7lQQpMHu+WQAQYqiFvBHspapo4pDiVCrAQWIDamNnTkHW69h3/qD +HqmsZzxF6iI3X351akVf+cOMCCXtwCGEvz+2gN12ytT8w/iAuOS6BuP3TQKBgBlf +v57+diSn13EQtajSPjVOH4ctorjFgEHjQHsP+OSeDLMTLSLeYArTo9+zu+R4hz1j +BsYnmvmrMQPd4OIL3jtFYTdF9coqxSraMZHWMXdfwUOrZpf1rG5skqNQV5yPejAz +Vmj6oDQPrrnVVM9W6I0kO0N7KZYCmH9MW0mdlZ6pAoGAB60f2sk35VUBpvh7qzTY +70WDbNnCCU3I3KZ7LCUwUPWzGLQwMXRlAb5ZMheT/SGPChX4QXCNUCjXkR3Am3NO +yURHqZIRy0bwZRVjYnlCtc9YQ8pB0isZ1z2a9FXRD75o2WboFZ+VsG0FU81IE2ZO +gW802gT76NRnz851B7/nFNs= +-----END PRIVATE KEY----- diff --git a/docs/multitenant/provisioning/ords_image.md b/docs/multitenant/provisioning/ords_image.md new file mode 100644 index 00000000..e2d1dcef --- /dev/null +++ b/docs/multitenant/provisioning/ords_image.md @@ -0,0 +1,81 @@ + + +# Build ORDS Docker Image + +This file contains the steps to create an ORDS based image to be used solely by the PDB life cycle multitentant controllers. + +**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. + +#### Clone the software using git: + +> Under directory ./oracle-database-operator/ords you will find the [Dockerfile](../../../ords/Dockerfile) and [runOrdsSSL.sh](../../../ords/runOrdsSSL.sh) required to build the image. + +```sh + git clone git@orahub.oci.oraclecorp.com:rac-docker-dev/oracle-database-operator.git + cd oracle-database-operator/ords/ +``` + +#### Login to the registry: container-registry.oracle.com + +**NOTE:** To login to this registry, you will need to the URL https://container-registry.oracle.com , Sign in, then click on "Java" and then accept the agreement. + +```bash +docker login container-registry.oracle.com +``` + +#### Login to the your container registry + +Login to a repo where you want to push your docker image (if needed) to pull during deployment in your environment. + +```bash +docker login +``` + +#### Build the image + +Build the docker image by using below command: + +```bash +docker build -t oracle/ords-dboper:latest . +``` +> If your are working behind a proxy mind to specify https_proxy and http_proxy during image creation + +Check the docker image details using: + +```bash +docker images +``` + +> OUTPUT EXAMPLE +```bash +REPOSITORY TAG IMAGE ID CREATED SIZE +oracle/ords-dboper latest fdb17aa242f8 4 hours ago 1.46GB + +``` + +#### Tag and push the image + +Tag and push the image to your image repository. + +NOTE: We have the repo as `phx.ocir.io//oracle/ords:latest`. Please change as per your environment. + +```bash +docker tag oracle/ords-dboper:ords-latest phx.ocir.io//oracle/ords:latest +docker push phx.ocir.io//oracle/ords:latest +``` + +#### In case of private image + +If you the image not be public then yuo need to create a secret containing the password of your image repository. +Create a Kubernetes Secret for your docker repository to pull the image during deployment using the below command: + +```bash +kubectl create secret generic container-registry-secret --from-file=.dockerconfigjson=./.docker/config.json --type=kubernetes.io/dockerconfigjson -n oracle-database-operator-system +``` + +Use the parameter `ordsImagePullSecret` to specify the container secrets in pod creation yaml file + +#### [Image createion example](../usecase01/logfiles/BuildImage.log) + + + diff --git a/docs/multitenant/usecase01/logfiles/BuildImage.log b/docs/multitenant/usecase01/logfiles/BuildImage.log new file mode 100644 index 00000000..f35c66d8 --- /dev/null +++ b/docs/multitenant/usecase01/logfiles/BuildImage.log @@ -0,0 +1,896 @@ +/usr/bin/docker build -t oracle/ords-dboper:latest ../../../ords +Sending build context to Docker daemon 13.82kB +Step 1/12 : FROM container-registry.oracle.com/java/jdk:latest + ---> b8457e2f0b73 +Step 2/12 : ENV ORDS_HOME=/opt/oracle/ords/ RUN_FILE="runOrdsSSL.sh" ORDSVERSION=23.4.0-8 + ---> Using cache + ---> 3317a16cd6f8 +Step 3/12 : COPY $RUN_FILE $ORDS_HOME + ---> 7995edec33cc +Step 4/12 : RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && yum -y install java-11-openjdk-devel && yum -y install iproute && yum clean all + ---> Running in fe168b01f3ad +Oracle Linux 8 BaseOS Latest (x86_64) 91 MB/s | 79 MB 00:00 +Oracle Linux 8 Application Stream (x86_64) 69 MB/s | 62 MB 00:00 +Last metadata expiration check: 0:00:12 ago on Tue 20 Aug 2024 08:54:50 AM UTC. +Package yum-utils-4.0.21-23.0.1.el8.noarch is already installed. +Package tar-2:1.30-9.el8.x86_64 is already installed. +Package vim-minimal-2:8.0.1763-19.0.1.el8_6.4.x86_64 is already installed. +Package procps-ng-3.3.15-14.0.1.el8.x86_64 is already installed. +Package curl-7.61.1-33.el8_9.5.x86_64 is already installed. +Dependencies resolved. +================================================================================ + Package Arch Version Repository Size +================================================================================ +Installing: + bind-utils x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 453 k + expect x86_64 5.45.4-5.el8 ol8_baseos_latest 266 k + hostname x86_64 3.20-6.el8 ol8_baseos_latest 32 k + lsof x86_64 4.93.2-1.el8 ol8_baseos_latest 253 k + net-tools x86_64 2.0-0.52.20160912git.el8 ol8_baseos_latest 322 k + openssl x86_64 1:1.1.1k-12.el8_9 ol8_baseos_latest 710 k + sudo x86_64 1.9.5p2-1.el8_9 ol8_baseos_latest 1.0 M + tree x86_64 1.7.0-15.el8 ol8_baseos_latest 59 k + unzip x86_64 6.0-46.0.1.el8 ol8_baseos_latest 196 k + wget x86_64 1.19.5-12.0.1.el8_10 ol8_appstream 733 k + which x86_64 2.21-20.el8 ol8_baseos_latest 50 k + zip x86_64 3.0-23.el8 ol8_baseos_latest 270 k +Upgrading: + curl x86_64 7.61.1-34.el8 ol8_baseos_latest 352 k + dnf-plugins-core noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 76 k + libcurl x86_64 7.61.1-34.el8 ol8_baseos_latest 303 k + python3-dnf-plugins-core + noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 263 k + yum-utils noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 75 k +Installing dependencies: + bind-libs x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 176 k + bind-libs-lite x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 1.2 M + bind-license noarch 32:9.11.36-16.el8_10.2 ol8_appstream 104 k + fstrm x86_64 0.6.1-3.el8 ol8_appstream 29 k + libmaxminddb x86_64 1.2.0-10.el8_9.1 ol8_appstream 32 k + libmetalink x86_64 0.1.3-7.el8 ol8_baseos_latest 32 k + protobuf-c x86_64 1.3.0-8.el8 ol8_appstream 37 k + python3-bind noarch 32:9.11.36-16.el8_10.2 ol8_appstream 151 k + python3-ply noarch 3.9-9.el8 ol8_baseos_latest 111 k + tcl x86_64 1:8.6.8-2.el8 ol8_baseos_latest 1.1 M +Installing weak dependencies: + geolite2-city noarch 20180605-1.el8 ol8_appstream 19 M + geolite2-country noarch 20180605-1.el8 ol8_appstream 1.0 M + +Transaction Summary +================================================================================ +Install 24 Packages +Upgrade 5 Packages + +Total download size: 28 M +Downloading Packages: +(1/29): hostname-3.20-6.el8.x86_64.rpm 268 kB/s | 32 kB 00:00 +(2/29): libmetalink-0.1.3-7.el8.x86_64.rpm 257 kB/s | 32 kB 00:00 +(3/29): expect-5.45.4-5.el8.x86_64.rpm 1.4 MB/s | 266 kB 00:00 +(4/29): lsof-4.93.2-1.el8.x86_64.rpm 3.2 MB/s | 253 kB 00:00 +(5/29): net-tools-2.0-0.52.20160912git.el8.x86_ 3.6 MB/s | 322 kB 00:00 +(6/29): python3-ply-3.9-9.el8.noarch.rpm 2.7 MB/s | 111 kB 00:00 +(7/29): openssl-1.1.1k-12.el8_9.x86_64.rpm 10 MB/s | 710 kB 00:00 +(8/29): tree-1.7.0-15.el8.x86_64.rpm 2.2 MB/s | 59 kB 00:00 +(9/29): sudo-1.9.5p2-1.el8_9.x86_64.rpm 14 MB/s | 1.0 MB 00:00 +(10/29): unzip-6.0-46.0.1.el8.x86_64.rpm 6.8 MB/s | 196 kB 00:00 +(11/29): which-2.21-20.el8.x86_64.rpm 2.0 MB/s | 50 kB 00:00 +(12/29): tcl-8.6.8-2.el8.x86_64.rpm 13 MB/s | 1.1 MB 00:00 +(13/29): bind-libs-9.11.36-16.el8_10.2.x86_64.r 6.7 MB/s | 176 kB 00:00 +(14/29): zip-3.0-23.el8.x86_64.rpm 8.4 MB/s | 270 kB 00:00 +(15/29): bind-libs-lite-9.11.36-16.el8_10.2.x86 29 MB/s | 1.2 MB 00:00 +(16/29): bind-license-9.11.36-16.el8_10.2.noarc 3.3 MB/s | 104 kB 00:00 +(17/29): bind-utils-9.11.36-16.el8_10.2.x86_64. 13 MB/s | 453 kB 00:00 +(18/29): fstrm-0.6.1-3.el8.x86_64.rpm 1.2 MB/s | 29 kB 00:00 +(19/29): libmaxminddb-1.2.0-10.el8_9.1.x86_64.r 1.3 MB/s | 32 kB 00:00 +(20/29): geolite2-country-20180605-1.el8.noarch 17 MB/s | 1.0 MB 00:00 +(21/29): protobuf-c-1.3.0-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 +(22/29): python3-bind-9.11.36-16.el8_10.2.noarc 5.8 MB/s | 151 kB 00:00 +(23/29): wget-1.19.5-12.0.1.el8_10.x86_64.rpm 17 MB/s | 733 kB 00:00 +(24/29): curl-7.61.1-34.el8.x86_64.rpm 12 MB/s | 352 kB 00:00 +(25/29): dnf-plugins-core-4.0.21-25.0.1.el8.noa 2.4 MB/s | 76 kB 00:00 +(26/29): libcurl-7.61.1-34.el8.x86_64.rpm 8.6 MB/s | 303 kB 00:00 +(27/29): python3-dnf-plugins-core-4.0.21-25.0.1 9.8 MB/s | 263 kB 00:00 +(28/29): yum-utils-4.0.21-25.0.1.el8.noarch.rpm 3.0 MB/s | 75 kB 00:00 +(29/29): geolite2-city-20180605-1.el8.noarch.rp 66 MB/s | 19 MB 00:00 +-------------------------------------------------------------------------------- +Total 43 MB/s | 28 MB 00:00 +Running transaction check +Transaction check succeeded. +Running transaction test +Transaction test succeeded. +Running transaction + Preparing : 1/1 + Running scriptlet: protobuf-c-1.3.0-8.el8.x86_64 1/1 + Installing : protobuf-c-1.3.0-8.el8.x86_64 1/34 + Installing : fstrm-0.6.1-3.el8.x86_64 2/34 + Installing : bind-license-32:9.11.36-16.el8_10.2.noarch 3/34 + Upgrading : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 4/34 + Upgrading : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 5/34 + Upgrading : libcurl-7.61.1-34.el8.x86_64 6/34 + Installing : geolite2-country-20180605-1.el8.noarch 7/34 + Installing : geolite2-city-20180605-1.el8.noarch 8/34 + Installing : libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 + Running scriptlet: libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 + Installing : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 10/34 + Installing : bind-libs-32:9.11.36-16.el8_10.2.x86_64 11/34 + Installing : unzip-6.0-46.0.1.el8.x86_64 12/34 + Installing : tcl-1:8.6.8-2.el8.x86_64 13/34 + Running scriptlet: tcl-1:8.6.8-2.el8.x86_64 13/34 + Installing : python3-ply-3.9-9.el8.noarch 14/34 + Installing : python3-bind-32:9.11.36-16.el8_10.2.noarch 15/34 + Installing : libmetalink-0.1.3-7.el8.x86_64 16/34 + Installing : wget-1.19.5-12.0.1.el8_10.x86_64 17/34 + Running scriptlet: wget-1.19.5-12.0.1.el8_10.x86_64 17/34 + Installing : bind-utils-32:9.11.36-16.el8_10.2.x86_64 18/34 + Installing : expect-5.45.4-5.el8.x86_64 19/34 + Installing : zip-3.0-23.el8.x86_64 20/34 + Upgrading : curl-7.61.1-34.el8.x86_64 21/34 + Upgrading : yum-utils-4.0.21-25.0.1.el8.noarch 22/34 + Installing : which-2.21-20.el8.x86_64 23/34 + Installing : tree-1.7.0-15.el8.x86_64 24/34 + Installing : sudo-1.9.5p2-1.el8_9.x86_64 25/34 + Running scriptlet: sudo-1.9.5p2-1.el8_9.x86_64 25/34 + Installing : openssl-1:1.1.1k-12.el8_9.x86_64 26/34 + Installing : net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 + Running scriptlet: net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 + Installing : lsof-4.93.2-1.el8.x86_64 28/34 + Installing : hostname-3.20-6.el8.x86_64 29/34 + Running scriptlet: hostname-3.20-6.el8.x86_64 29/34 + Cleanup : curl-7.61.1-33.el8_9.5.x86_64 30/34 + Cleanup : yum-utils-4.0.21-23.0.1.el8.noarch 31/34 + Cleanup : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 + Cleanup : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 33/34 + Cleanup : libcurl-7.61.1-33.el8_9.5.x86_64 34/34 + Running scriptlet: libcurl-7.61.1-33.el8_9.5.x86_64 34/34 + Verifying : expect-5.45.4-5.el8.x86_64 1/34 + Verifying : hostname-3.20-6.el8.x86_64 2/34 + Verifying : libmetalink-0.1.3-7.el8.x86_64 3/34 + Verifying : lsof-4.93.2-1.el8.x86_64 4/34 + Verifying : net-tools-2.0-0.52.20160912git.el8.x86_64 5/34 + Verifying : openssl-1:1.1.1k-12.el8_9.x86_64 6/34 + Verifying : python3-ply-3.9-9.el8.noarch 7/34 + Verifying : sudo-1.9.5p2-1.el8_9.x86_64 8/34 + Verifying : tcl-1:8.6.8-2.el8.x86_64 9/34 + Verifying : tree-1.7.0-15.el8.x86_64 10/34 + Verifying : unzip-6.0-46.0.1.el8.x86_64 11/34 + Verifying : which-2.21-20.el8.x86_64 12/34 + Verifying : zip-3.0-23.el8.x86_64 13/34 + Verifying : bind-libs-32:9.11.36-16.el8_10.2.x86_64 14/34 + Verifying : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 15/34 + Verifying : bind-license-32:9.11.36-16.el8_10.2.noarch 16/34 + Verifying : bind-utils-32:9.11.36-16.el8_10.2.x86_64 17/34 + Verifying : fstrm-0.6.1-3.el8.x86_64 18/34 + Verifying : geolite2-city-20180605-1.el8.noarch 19/34 + Verifying : geolite2-country-20180605-1.el8.noarch 20/34 + Verifying : libmaxminddb-1.2.0-10.el8_9.1.x86_64 21/34 + Verifying : protobuf-c-1.3.0-8.el8.x86_64 22/34 + Verifying : python3-bind-32:9.11.36-16.el8_10.2.noarch 23/34 + Verifying : wget-1.19.5-12.0.1.el8_10.x86_64 24/34 + Verifying : curl-7.61.1-34.el8.x86_64 25/34 + Verifying : curl-7.61.1-33.el8_9.5.x86_64 26/34 + Verifying : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 27/34 + Verifying : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 28/34 + Verifying : libcurl-7.61.1-34.el8.x86_64 29/34 + Verifying : libcurl-7.61.1-33.el8_9.5.x86_64 30/34 + Verifying : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 31/34 + Verifying : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 + Verifying : yum-utils-4.0.21-25.0.1.el8.noarch 33/34 + Verifying : yum-utils-4.0.21-23.0.1.el8.noarch 34/34 + +Upgraded: + curl-7.61.1-34.el8.x86_64 + dnf-plugins-core-4.0.21-25.0.1.el8.noarch + libcurl-7.61.1-34.el8.x86_64 + python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch + yum-utils-4.0.21-25.0.1.el8.noarch +Installed: + bind-libs-32:9.11.36-16.el8_10.2.x86_64 + bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 + bind-license-32:9.11.36-16.el8_10.2.noarch + bind-utils-32:9.11.36-16.el8_10.2.x86_64 + expect-5.45.4-5.el8.x86_64 + fstrm-0.6.1-3.el8.x86_64 + geolite2-city-20180605-1.el8.noarch + geolite2-country-20180605-1.el8.noarch + hostname-3.20-6.el8.x86_64 + libmaxminddb-1.2.0-10.el8_9.1.x86_64 + libmetalink-0.1.3-7.el8.x86_64 + lsof-4.93.2-1.el8.x86_64 + net-tools-2.0-0.52.20160912git.el8.x86_64 + openssl-1:1.1.1k-12.el8_9.x86_64 + protobuf-c-1.3.0-8.el8.x86_64 + python3-bind-32:9.11.36-16.el8_10.2.noarch + python3-ply-3.9-9.el8.noarch + sudo-1.9.5p2-1.el8_9.x86_64 + tcl-1:8.6.8-2.el8.x86_64 + tree-1.7.0-15.el8.x86_64 + unzip-6.0-46.0.1.el8.x86_64 + wget-1.19.5-12.0.1.el8_10.x86_64 + which-2.21-20.el8.x86_64 + zip-3.0-23.el8.x86_64 + +Complete! +Adding repo from: http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 +created by dnf config-manager from http://yum.o 496 kB/s | 139 kB 00:00 +Last metadata expiration check: 0:00:01 ago on Tue 20 Aug 2024 08:55:14 AM UTC. +Dependencies resolved. +============================================================================================== + Package Arch Version Repository Size +============================================================================================== +Installing: + java-11-openjdk-devel x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 3.4 M +Installing dependencies: + adwaita-cursor-theme noarch 3.28.0-3.el8 ol8_appstream 647 k + adwaita-icon-theme noarch 3.28.0-3.el8 ol8_appstream 11 M + alsa-lib x86_64 1.2.10-2.el8 ol8_appstream 500 k + at-spi2-atk x86_64 2.26.2-1.el8 ol8_appstream 89 k + at-spi2-core x86_64 2.28.0-1.el8 ol8_appstream 169 k + atk x86_64 2.28.1-1.el8 ol8_appstream 272 k + avahi-libs x86_64 0.7-27.el8 ol8_baseos_latest 61 k + cairo x86_64 1.15.12-6.el8 ol8_appstream 719 k + cairo-gobject x86_64 1.15.12-6.el8 ol8_appstream 33 k + colord-libs x86_64 1.4.2-1.el8 ol8_appstream 236 k + copy-jdk-configs noarch 4.0-2.el8 ol8_appstream 30 k + cpio x86_64 2.12-11.el8 ol8_baseos_latest 266 k + crypto-policies-scripts noarch 20230731-1.git3177e06.el8 ol8_baseos_latest 84 k + cups-libs x86_64 1:2.2.6-60.el8_10 ol8_baseos_latest 435 k + dracut x86_64 049-233.git20240115.0.1.el8 ol8_baseos_latest 382 k + file x86_64 5.33-25.el8 ol8_baseos_latest 77 k + fribidi x86_64 1.0.4-9.el8 ol8_appstream 89 k + gdk-pixbuf2 x86_64 2.36.12-6.el8_10 ol8_baseos_latest 465 k + gdk-pixbuf2-modules x86_64 2.36.12-6.el8_10 ol8_appstream 108 k + gettext x86_64 0.19.8.1-17.el8 ol8_baseos_latest 1.1 M + gettext-libs x86_64 0.19.8.1-17.el8 ol8_baseos_latest 312 k + glib-networking x86_64 2.56.1-1.1.el8 ol8_baseos_latest 155 k + graphite2 x86_64 1.3.10-10.el8 ol8_appstream 122 k + grub2-common noarch 1:2.02-156.0.2.el8 ol8_baseos_latest 897 k + grub2-tools x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 2.0 M + grub2-tools-minimal x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 215 k + gsettings-desktop-schemas x86_64 3.32.0-6.el8 ol8_baseos_latest 633 k + gtk-update-icon-cache x86_64 3.22.30-11.el8 ol8_appstream 32 k + harfbuzz x86_64 1.7.5-4.el8 ol8_appstream 295 k + hicolor-icon-theme noarch 0.17-2.el8 ol8_appstream 48 k + jasper-libs x86_64 2.0.14-5.el8 ol8_appstream 167 k + java-11-openjdk x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 475 k + java-11-openjdk-headless x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 42 M + javapackages-filesystem noarch 5.3.0-1.module+el8+5136+7ff78f74 ol8_appstream 30 k + jbigkit-libs x86_64 2.1-14.el8 ol8_appstream 55 k + json-glib x86_64 1.4.4-1.el8 ol8_baseos_latest 144 k + kbd-legacy noarch 2.0.4-11.el8 ol8_baseos_latest 481 k + kbd-misc noarch 2.0.4-11.el8 ol8_baseos_latest 1.5 M + lcms2 x86_64 2.9-2.el8 ol8_appstream 164 k + libX11 x86_64 1.6.8-8.el8 ol8_appstream 611 k + libX11-common noarch 1.6.8-8.el8 ol8_appstream 157 k + libXau x86_64 1.0.9-3.el8 ol8_appstream 37 k + libXcomposite x86_64 0.4.4-14.el8 ol8_appstream 28 k + libXcursor x86_64 1.1.15-3.el8 ol8_appstream 36 k + libXdamage x86_64 1.1.4-14.el8 ol8_appstream 27 k + libXext x86_64 1.3.4-1.el8 ol8_appstream 45 k + libXfixes x86_64 5.0.3-7.el8 ol8_appstream 25 k + libXft x86_64 2.3.3-1.el8 ol8_appstream 67 k + libXi x86_64 1.7.10-1.el8 ol8_appstream 49 k + libXinerama x86_64 1.1.4-1.el8 ol8_appstream 15 k + libXrandr x86_64 1.5.2-1.el8 ol8_appstream 34 k + libXrender x86_64 0.9.10-7.el8 ol8_appstream 33 k + libXtst x86_64 1.2.3-7.el8 ol8_appstream 22 k + libcroco x86_64 0.6.12-4.el8_2.1 ol8_baseos_latest 113 k + libdatrie x86_64 0.2.9-7.el8 ol8_appstream 33 k + libepoxy x86_64 1.5.8-1.el8 ol8_appstream 225 k + libfontenc x86_64 1.1.3-8.el8 ol8_appstream 37 k + libgomp x86_64 8.5.0-22.0.1.el8_10 ol8_baseos_latest 218 k + libgusb x86_64 0.3.0-1.el8 ol8_baseos_latest 49 k + libjpeg-turbo x86_64 1.5.3-12.el8 ol8_appstream 157 k + libkcapi x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 52 k + libkcapi-hmaccalc x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 31 k + libmodman x86_64 2.0.1-17.el8 ol8_baseos_latest 36 k + libpkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 35 k + libproxy x86_64 0.4.15-5.2.el8 ol8_baseos_latest 75 k + libsoup x86_64 2.62.3-5.el8 ol8_baseos_latest 424 k + libthai x86_64 0.1.27-2.el8 ol8_appstream 203 k + libtiff x86_64 4.0.9-32.el8_10 ol8_appstream 189 k + libwayland-client x86_64 1.21.0-1.el8 ol8_appstream 41 k + libwayland-cursor x86_64 1.21.0-1.el8 ol8_appstream 26 k + libwayland-egl x86_64 1.21.0-1.el8 ol8_appstream 19 k + libxcb x86_64 1.13.1-1.el8 ol8_appstream 231 k + libxkbcommon x86_64 0.9.1-1.el8 ol8_appstream 116 k + lksctp-tools x86_64 1.0.18-3.el8 ol8_baseos_latest 100 k + lua x86_64 5.3.4-12.el8 ol8_appstream 192 k + nspr x86_64 4.35.0-1.el8_8 ol8_appstream 143 k + nss x86_64 3.90.0-7.el8_10 ol8_appstream 750 k + nss-softokn x86_64 3.90.0-7.el8_10 ol8_appstream 1.2 M + nss-softokn-freebl x86_64 3.90.0-7.el8_10 ol8_appstream 375 k + nss-sysinit x86_64 3.90.0-7.el8_10 ol8_appstream 74 k + nss-util x86_64 3.90.0-7.el8_10 ol8_appstream 139 k + os-prober x86_64 1.74-9.0.1.el8 ol8_baseos_latest 51 k + pango x86_64 1.42.4-8.el8 ol8_appstream 297 k + pixman x86_64 0.38.4-4.el8 ol8_appstream 256 k + pkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 38 k + pkgconf-m4 noarch 1.4.2-1.el8 ol8_baseos_latest 17 k + pkgconf-pkg-config x86_64 1.4.2-1.el8 ol8_baseos_latest 15 k + rest x86_64 0.8.1-2.el8 ol8_appstream 70 k + shared-mime-info x86_64 1.9-4.el8 ol8_baseos_latest 328 k + systemd-udev x86_64 239-78.0.4.el8 ol8_baseos_latest 1.6 M + ttmkfdir x86_64 3.0.9-54.el8 ol8_appstream 62 k + tzdata-java noarch 2024a-1.0.1.el8 ol8_appstream 186 k + xkeyboard-config noarch 2.28-1.el8 ol8_appstream 782 k + xorg-x11-font-utils x86_64 1:7.5-41.el8 ol8_appstream 104 k + xorg-x11-fonts-Type1 noarch 7.5-19.el8 ol8_appstream 522 k + xz x86_64 5.2.4-4.el8_6 ol8_baseos_latest 153 k +Installing weak dependencies: + abattis-cantarell-fonts noarch 0.0.25-6.el8 ol8_appstream 155 k + dconf x86_64 0.28.0-4.0.1.el8 ol8_appstream 108 k + dejavu-sans-mono-fonts noarch 2.35-7.el8 ol8_baseos_latest 447 k + grubby x86_64 8.40-49.0.2.el8 ol8_baseos_latest 50 k + gtk3 x86_64 3.22.30-11.el8 ol8_appstream 4.5 M + hardlink x86_64 1:1.3-6.el8 ol8_baseos_latest 29 k + kbd x86_64 2.0.4-11.el8 ol8_baseos_latest 390 k + memstrack x86_64 0.2.5-2.el8 ol8_baseos_latest 51 k + pigz x86_64 2.4-4.el8 ol8_baseos_latest 80 k +Enabling module streams: + javapackages-runtime 201801 + +Transaction Summary +============================================================================================== +Install 106 Packages + +Total download size: 86 M +Installed size: 312 M +Downloading Packages: +(1/106): crypto-policies-scripts-20230731-1.git 862 kB/s | 84 kB 00:00 +(2/106): avahi-libs-0.7-27.el8.x86_64.rpm 602 kB/s | 61 kB 00:00 +(3/106): cpio-2.12-11.el8.x86_64.rpm 1.8 MB/s | 266 kB 00:00 +(4/106): cups-libs-2.2.6-60.el8_10.x86_64.rpm 5.7 MB/s | 435 kB 00:00 +(5/106): dejavu-sans-mono-fonts-2.35-7.el8.noar 5.1 MB/s | 447 kB 00:00 +(6/106): dracut-049-233.git20240115.0.1.el8.x86 7.0 MB/s | 382 kB 00:00 +(7/106): gdk-pixbuf2-2.36.12-6.el8_10.x86_64.rp 12 MB/s | 465 kB 00:00 +(8/106): gettext-libs-0.19.8.1-17.el8.x86_64.rp 9.3 MB/s | 312 kB 00:00 +(9/106): gettext-0.19.8.1-17.el8.x86_64.rpm 16 MB/s | 1.1 MB 00:00 +(10/106): glib-networking-2.56.1-1.1.el8.x86_64 6.0 MB/s | 155 kB 00:00 +(11/106): grub2-common-2.02-156.0.2.el8.noarch. 26 MB/s | 897 kB 00:00 +(12/106): grub2-tools-minimal-2.02-156.0.2.el8. 8.2 MB/s | 215 kB 00:00 +(13/106): grubby-8.40-49.0.2.el8.x86_64.rpm 2.1 MB/s | 50 kB 00:00 +(14/106): grub2-tools-2.02-156.0.2.el8.x86_64.r 26 MB/s | 2.0 MB 00:00 +(15/106): gsettings-desktop-schemas-3.32.0-6.el 19 MB/s | 633 kB 00:00 +(16/106): hardlink-1.3-6.el8.x86_64.rpm 1.1 MB/s | 29 kB 00:00 +(17/106): json-glib-1.4.4-1.el8.x86_64.rpm 5.9 MB/s | 144 kB 00:00 +(18/106): kbd-2.0.4-11.el8.x86_64.rpm 14 MB/s | 390 kB 00:00 +(19/106): kbd-legacy-2.0.4-11.el8.noarch.rpm 17 MB/s | 481 kB 00:00 +(20/106): kbd-misc-2.0.4-11.el8.noarch.rpm 41 MB/s | 1.5 MB 00:00 +(21/106): libcroco-0.6.12-4.el8_2.1.x86_64.rpm 4.7 MB/s | 113 kB 00:00 +(22/106): libgomp-8.5.0-22.0.1.el8_10.x86_64.rp 9.1 MB/s | 218 kB 00:00 +(23/106): libgusb-0.3.0-1.el8.x86_64.rpm 2.1 MB/s | 49 kB 00:00 +(24/106): libkcapi-1.4.0-2.0.1.el8.x86_64.rpm 1.6 MB/s | 52 kB 00:00 +(25/106): libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86 822 kB/s | 31 kB 00:00 +(26/106): libmodman-2.0.1-17.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 +(27/106): libpkgconf-1.4.2-1.el8.x86_64.rpm 1.2 MB/s | 35 kB 00:00 +(28/106): libproxy-0.4.15-5.2.el8.x86_64.rpm 3.0 MB/s | 75 kB 00:00 +(29/106): libsoup-2.62.3-5.el8.x86_64.rpm 15 MB/s | 424 kB 00:00 +(30/106): lksctp-tools-1.0.18-3.el8.x86_64.rpm 3.5 MB/s | 100 kB 00:00 +(31/106): memstrack-0.2.5-2.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 +(32/106): os-prober-1.74-9.0.1.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 +(33/106): pigz-2.4-4.el8.x86_64.rpm 3.5 MB/s | 80 kB 00:00 +(34/106): pkgconf-1.4.2-1.el8.x86_64.rpm 1.7 MB/s | 38 kB 00:00 +(35/106): pkgconf-m4-1.4.2-1.el8.noarch.rpm 761 kB/s | 17 kB 00:00 +(36/106): pkgconf-pkg-config-1.4.2-1.el8.x86_64 691 kB/s | 15 kB 00:00 +(37/106): shared-mime-info-1.9-4.el8.x86_64.rpm 13 MB/s | 328 kB 00:00 +(38/106): systemd-udev-239-78.0.4.el8.x86_64.rp 32 MB/s | 1.6 MB 00:00 +(39/106): xz-5.2.4-4.el8_6.x86_64.rpm 5.2 MB/s | 153 kB 00:00 +(40/106): abattis-cantarell-fonts-0.0.25-6.el8. 6.4 MB/s | 155 kB 00:00 +(41/106): adwaita-cursor-theme-3.28.0-3.el8.noa 22 MB/s | 647 kB 00:00 +(42/106): alsa-lib-1.2.10-2.el8.x86_64.rpm 18 MB/s | 500 kB 00:00 +(43/106): at-spi2-atk-2.26.2-1.el8.x86_64.rpm 3.8 MB/s | 89 kB 00:00 +(44/106): at-spi2-core-2.28.0-1.el8.x86_64.rpm 6.9 MB/s | 169 kB 00:00 +(45/106): atk-2.28.1-1.el8.x86_64.rpm 9.2 MB/s | 272 kB 00:00 +(46/106): cairo-1.15.12-6.el8.x86_64.rpm 24 MB/s | 719 kB 00:00 +(47/106): adwaita-icon-theme-3.28.0-3.el8.noarc 65 MB/s | 11 MB 00:00 +(48/106): cairo-gobject-1.15.12-6.el8.x86_64.rp 914 kB/s | 33 kB 00:00 +(49/106): colord-libs-1.4.2-1.el8.x86_64.rpm 9.5 MB/s | 236 kB 00:00 +(50/106): copy-jdk-configs-4.0-2.el8.noarch.rpm 1.1 MB/s | 30 kB 00:00 +(51/106): dconf-0.28.0-4.0.1.el8.x86_64.rpm 4.4 MB/s | 108 kB 00:00 +(52/106): fribidi-1.0.4-9.el8.x86_64.rpm 3.9 MB/s | 89 kB 00:00 +(53/106): graphite2-1.3.10-10.el8.x86_64.rpm 5.1 MB/s | 122 kB 00:00 +(54/106): gdk-pixbuf2-modules-2.36.12-6.el8_10. 3.6 MB/s | 108 kB 00:00 +(55/106): gtk-update-icon-cache-3.22.30-11.el8. 1.4 MB/s | 32 kB 00:00 +(56/106): harfbuzz-1.7.5-4.el8.x86_64.rpm 11 MB/s | 295 kB 00:00 +(57/106): gtk3-3.22.30-11.el8.x86_64.rpm 68 MB/s | 4.5 MB 00:00 +(58/106): hicolor-icon-theme-0.17-2.el8.noarch. 2.1 MB/s | 48 kB 00:00 +(59/106): java-11-openjdk-11.0.24.0.8-3.0.1.el8 17 MB/s | 475 kB 00:00 +(60/106): jasper-libs-2.0.14-5.el8.x86_64.rpm 5.0 MB/s | 167 kB 00:00 +(61/106): java-11-openjdk-devel-11.0.24.0.8-3.0 61 MB/s | 3.4 MB 00:00 +(62/106): javapackages-filesystem-5.3.0-1.modul 1.2 MB/s | 30 kB 00:00 +(63/106): jbigkit-libs-2.1-14.el8.x86_64.rpm 2.1 MB/s | 55 kB 00:00 +(64/106): lcms2-2.9-2.el8.x86_64.rpm 3.8 MB/s | 164 kB 00:00 +(65/106): libX11-1.6.8-8.el8.x86_64.rpm 20 MB/s | 611 kB 00:00 +(66/106): libX11-common-1.6.8-8.el8.noarch.rpm 6.8 MB/s | 157 kB 00:00 +(67/106): libXau-1.0.9-3.el8.x86_64.rpm 1.6 MB/s | 37 kB 00:00 +(68/106): libXcomposite-0.4.4-14.el8.x86_64.rpm 1.3 MB/s | 28 kB 00:00 +(69/106): libXcursor-1.1.15-3.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 +(70/106): libXdamage-1.1.4-14.el8.x86_64.rpm 1.2 MB/s | 27 kB 00:00 +(71/106): libXext-1.3.4-1.el8.x86_64.rpm 2.0 MB/s | 45 kB 00:00 +(72/106): libXfixes-5.0.3-7.el8.x86_64.rpm 1.1 MB/s | 25 kB 00:00 +(73/106): libXft-2.3.3-1.el8.x86_64.rpm 2.9 MB/s | 67 kB 00:00 +(74/106): libXi-1.7.10-1.el8.x86_64.rpm 2.2 MB/s | 49 kB 00:00 +(75/106): libXinerama-1.1.4-1.el8.x86_64.rpm 717 kB/s | 15 kB 00:00 +(76/106): libXrandr-1.5.2-1.el8.x86_64.rpm 1.5 MB/s | 34 kB 00:00 +(77/106): libXrender-0.9.10-7.el8.x86_64.rpm 1.4 MB/s | 33 kB 00:00 +(78/106): libXtst-1.2.3-7.el8.x86_64.rpm 957 kB/s | 22 kB 00:00 +(79/106): java-11-openjdk-headless-11.0.24.0.8- 71 MB/s | 42 MB 00:00 +(80/106): libdatrie-0.2.9-7.el8.x86_64.rpm 274 kB/s | 33 kB 00:00 +(81/106): libepoxy-1.5.8-1.el8.x86_64.rpm 9.1 MB/s | 225 kB 00:00 +(82/106): libfontenc-1.1.3-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 +(83/106): libthai-0.1.27-2.el8.x86_64.rpm 8.2 MB/s | 203 kB 00:00 +(84/106): libjpeg-turbo-1.5.3-12.el8.x86_64.rpm 5.1 MB/s | 157 kB 00:00 +(85/106): libtiff-4.0.9-32.el8_10.x86_64.rpm 7.8 MB/s | 189 kB 00:00 +(86/106): libwayland-client-1.21.0-1.el8.x86_64 1.7 MB/s | 41 kB 00:00 +(87/106): libwayland-cursor-1.21.0-1.el8.x86_64 1.2 MB/s | 26 kB 00:00 +(88/106): libwayland-egl-1.21.0-1.el8.x86_64.rp 801 kB/s | 19 kB 00:00 +(89/106): libxcb-1.13.1-1.el8.x86_64.rpm 9.7 MB/s | 231 kB 00:00 +(90/106): libxkbcommon-0.9.1-1.el8.x86_64.rpm 5.0 MB/s | 116 kB 00:00 +(91/106): nspr-4.35.0-1.el8_8.x86_64.rpm 6.0 MB/s | 143 kB 00:00 +(92/106): lua-5.3.4-12.el8.x86_64.rpm 5.9 MB/s | 192 kB 00:00 +(93/106): nss-softokn-3.90.0-7.el8_10.x86_64.rp 38 MB/s | 1.2 MB 00:00 +(94/106): nss-3.90.0-7.el8_10.x86_64.rpm 17 MB/s | 750 kB 00:00 +(95/106): nss-softokn-freebl-3.90.0-7.el8_10.x8 14 MB/s | 375 kB 00:00 +(96/106): nss-sysinit-3.90.0-7.el8_10.x86_64.rp 3.2 MB/s | 74 kB 00:00 +(97/106): nss-util-3.90.0-7.el8_10.x86_64.rpm 5.8 MB/s | 139 kB 00:00 +(98/106): pango-1.42.4-8.el8.x86_64.rpm 11 MB/s | 297 kB 00:00 +(99/106): pixman-0.38.4-4.el8.x86_64.rpm 10 MB/s | 256 kB 00:00 +(100/106): rest-0.8.1-2.el8.x86_64.rpm 3.1 MB/s | 70 kB 00:00 +(101/106): ttmkfdir-3.0.9-54.el8.x86_64.rpm 2.5 MB/s | 62 kB 00:00 +(102/106): tzdata-java-2024a-1.0.1.el8.noarch.r 7.4 MB/s | 186 kB 00:00 +(103/106): xkeyboard-config-2.28-1.el8.noarch.r 27 MB/s | 782 kB 00:00 +(104/106): xorg-x11-font-utils-7.5-41.el8.x86_6 3.9 MB/s | 104 kB 00:00 +(105/106): xorg-x11-fonts-Type1-7.5-19.el8.noar 1.3 MB/s | 522 kB 00:00 +(106/106): file-5.33-25.el8.x86_64.rpm 26 kB/s | 77 kB 00:02 +-------------------------------------------------------------------------------- +Total 27 MB/s | 86 MB 00:03 +Running transaction check +Transaction check succeeded. +Running transaction test +Transaction test succeeded. +Running transaction + Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 1/1 + Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86 1/1 + Preparing : 1/1 + Installing : nspr-4.35.0-1.el8_8.x86_64 1/106 + Running scriptlet: nspr-4.35.0-1.el8_8.x86_64 1/106 + Installing : nss-util-3.90.0-7.el8_10.x86_64 2/106 + Installing : libjpeg-turbo-1.5.3-12.el8.x86_64 3/106 + Installing : pixman-0.38.4-4.el8.x86_64 4/106 + Installing : libwayland-client-1.21.0-1.el8.x86_64 5/106 + Installing : atk-2.28.1-1.el8.x86_64 6/106 + Installing : libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 + Running scriptlet: libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 + Installing : libcroco-0.6.12-4.el8_2.1.x86_64 8/106 + Running scriptlet: libcroco-0.6.12-4.el8_2.1.x86_64 8/106 + Installing : grub2-common-1:2.02-156.0.2.el8.noarch 9/106 + Installing : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 + Installing : gettext-0.19.8.1-17.el8.x86_64 11/106 + Running scriptlet: gettext-0.19.8.1-17.el8.x86_64 11/106 + Installing : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 12/106 + Installing : libwayland-cursor-1.21.0-1.el8.x86_64 13/106 + Installing : jasper-libs-2.0.14-5.el8.x86_64 14/106 + Installing : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 15/106 + Installing : nss-softokn-3.90.0-7.el8_10.x86_64 16/106 + Installing : xkeyboard-config-2.28-1.el8.noarch 17/106 + Installing : libxkbcommon-0.9.1-1.el8.x86_64 18/106 + Installing : tzdata-java-2024a-1.0.1.el8.noarch 19/106 + Installing : ttmkfdir-3.0.9-54.el8.x86_64 20/106 + Installing : lua-5.3.4-12.el8.x86_64 21/106 + Installing : copy-jdk-configs-4.0-2.el8.noarch 22/106 + Installing : libwayland-egl-1.21.0-1.el8.x86_64 23/106 + Installing : libfontenc-1.1.3-8.el8.x86_64 24/106 + Installing : libepoxy-1.5.8-1.el8.x86_64 25/106 + Installing : libdatrie-0.2.9-7.el8.x86_64 26/106 + Running scriptlet: libdatrie-0.2.9-7.el8.x86_64 26/106 + Installing : libthai-0.1.27-2.el8.x86_64 27/106 + Running scriptlet: libthai-0.1.27-2.el8.x86_64 27/106 + Installing : libXau-1.0.9-3.el8.x86_64 28/106 + Installing : libxcb-1.13.1-1.el8.x86_64 29/106 + Installing : libX11-common-1.6.8-8.el8.noarch 30/106 + Installing : libX11-1.6.8-8.el8.x86_64 31/106 + Installing : libXext-1.3.4-1.el8.x86_64 32/106 + Installing : libXrender-0.9.10-7.el8.x86_64 33/106 + Installing : cairo-1.15.12-6.el8.x86_64 34/106 + Installing : libXi-1.7.10-1.el8.x86_64 35/106 + Installing : libXfixes-5.0.3-7.el8.x86_64 36/106 + Installing : libXtst-1.2.3-7.el8.x86_64 37/106 + Installing : libXcomposite-0.4.4-14.el8.x86_64 38/106 + Installing : at-spi2-core-2.28.0-1.el8.x86_64 39/106 + Running scriptlet: at-spi2-core-2.28.0-1.el8.x86_64 39/106 + Installing : at-spi2-atk-2.26.2-1.el8.x86_64 40/106 + Running scriptlet: at-spi2-atk-2.26.2-1.el8.x86_64 40/106 + Installing : libXcursor-1.1.15-3.el8.x86_64 41/106 + Installing : libXdamage-1.1.4-14.el8.x86_64 42/106 + Installing : cairo-gobject-1.15.12-6.el8.x86_64 43/106 + Installing : libXft-2.3.3-1.el8.x86_64 44/106 + Installing : libXrandr-1.5.2-1.el8.x86_64 45/106 + Installing : libXinerama-1.1.4-1.el8.x86_64 46/106 + Installing : lcms2-2.9-2.el8.x86_64 47/106 + Running scriptlet: lcms2-2.9-2.el8.x86_64 47/106 + Installing : jbigkit-libs-2.1-14.el8.x86_64 48/106 + Running scriptlet: jbigkit-libs-2.1-14.el8.x86_64 48/106 + Installing : libtiff-4.0.9-32.el8_10.x86_64 49/106 + Installing : javapackages-filesystem-5.3.0-1.module+el8+5136+ 50/106 + Installing : hicolor-icon-theme-0.17-2.el8.noarch 51/106 + Installing : graphite2-1.3.10-10.el8.x86_64 52/106 + Installing : harfbuzz-1.7.5-4.el8.x86_64 53/106 + Running scriptlet: harfbuzz-1.7.5-4.el8.x86_64 53/106 + Installing : fribidi-1.0.4-9.el8.x86_64 54/106 + Installing : pango-1.42.4-8.el8.x86_64 55/106 + Running scriptlet: pango-1.42.4-8.el8.x86_64 55/106 + Installing : dconf-0.28.0-4.0.1.el8.x86_64 56/106 + Installing : alsa-lib-1.2.10-2.el8.x86_64 57/106 + Running scriptlet: alsa-lib-1.2.10-2.el8.x86_64 57/106 + Installing : adwaita-cursor-theme-3.28.0-3.el8.noarch 58/106 + Installing : adwaita-icon-theme-3.28.0-3.el8.noarch 59/106 + Installing : abattis-cantarell-fonts-0.0.25-6.el8.noarch 60/106 + Installing : xz-5.2.4-4.el8_6.x86_64 61/106 + Installing : shared-mime-info-1.9-4.el8.x86_64 62/106 + Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 62/106 + Installing : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 + Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 + Installing : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 64/106 + Installing : gtk-update-icon-cache-3.22.30-11.el8.x86_64 65/106 + Installing : pkgconf-m4-1.4.2-1.el8.noarch 66/106 + Installing : pigz-2.4-4.el8.x86_64 67/106 + Installing : memstrack-0.2.5-2.el8.x86_64 68/106 + Installing : lksctp-tools-1.0.18-3.el8.x86_64 69/106 + Running scriptlet: lksctp-tools-1.0.18-3.el8.x86_64 69/106 + Installing : libpkgconf-1.4.2-1.el8.x86_64 70/106 + Installing : pkgconf-1.4.2-1.el8.x86_64 71/106 + Installing : pkgconf-pkg-config-1.4.2-1.el8.x86_64 72/106 + Installing : xorg-x11-font-utils-1:7.5-41.el8.x86_64 73/106 + Installing : xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 + Running scriptlet: xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 + Installing : libmodman-2.0.1-17.el8.x86_64 75/106 + Running scriptlet: libmodman-2.0.1-17.el8.x86_64 75/106 + Installing : libproxy-0.4.15-5.2.el8.x86_64 76/106 + Running scriptlet: libproxy-0.4.15-5.2.el8.x86_64 76/106 + Installing : libkcapi-1.4.0-2.0.1.el8.x86_64 77/106 + Installing : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 78/106 + Installing : libgusb-0.3.0-1.el8.x86_64 79/106 + Installing : colord-libs-1.4.2-1.el8.x86_64 80/106 + Installing : kbd-misc-2.0.4-11.el8.noarch 81/106 + Installing : kbd-legacy-2.0.4-11.el8.noarch 82/106 + Installing : kbd-2.0.4-11.el8.x86_64 83/106 + Installing : systemd-udev-239-78.0.4.el8.x86_64 84/106 + Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 84/106 + Installing : os-prober-1.74-9.0.1.el8.x86_64 85/106 + Installing : json-glib-1.4.4-1.el8.x86_64 86/106 + Installing : hardlink-1:1.3-6.el8.x86_64 87/106 + Installing : file-5.33-25.el8.x86_64 88/106 + Installing : dejavu-sans-mono-fonts-2.35-7.el8.noarch 89/106 + Installing : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 90/106 + Installing : glib-networking-2.56.1-1.1.el8.x86_64 91/106 + Installing : libsoup-2.62.3-5.el8.x86_64 92/106 + Installing : rest-0.8.1-2.el8.x86_64 93/106 + Running scriptlet: rest-0.8.1-2.el8.x86_64 93/106 + Installing : cpio-2.12-11.el8.x86_64 94/106 + Installing : dracut-049-233.git20240115.0.1.el8.x86_64 95/106 + Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 + Installing : grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 + Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 + Installing : grubby-8.40-49.0.2.el8.x86_64 97/106 + Installing : crypto-policies-scripts-20230731-1.git3177e06.el 98/106 + Installing : nss-sysinit-3.90.0-7.el8_10.x86_64 99/106 + Installing : nss-3.90.0-7.el8_10.x86_64 100/106 + Installing : avahi-libs-0.7-27.el8.x86_64 101/106 + Installing : cups-libs-1:2.2.6-60.el8_10.x86_64 102/106 + Installing : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 + Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 + Installing : gtk3-3.22.30-11.el8.x86_64 104/106 + Installing : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 + Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 + Installing : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 + Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 + Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 106/106 + Running scriptlet: dconf-0.28.0-4.0.1.el8.x86_64 106/106 + Running scriptlet: crypto-policies-scripts-20230731-1.git3177e06.el 106/106 + Running scriptlet: nss-3.90.0-7.el8_10.x86_64 106/106 + Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 106/106 + Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 106/106 + Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 + Running scriptlet: hicolor-icon-theme-0.17-2.el8.noarch 106/106 + Running scriptlet: adwaita-icon-theme-3.28.0-3.el8.noarch 106/106 + Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 106/106 + Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 106/106 + Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 106/106 + Verifying : avahi-libs-0.7-27.el8.x86_64 1/106 + Verifying : cpio-2.12-11.el8.x86_64 2/106 + Verifying : crypto-policies-scripts-20230731-1.git3177e06.el 3/106 + Verifying : cups-libs-1:2.2.6-60.el8_10.x86_64 4/106 + Verifying : dejavu-sans-mono-fonts-2.35-7.el8.noarch 5/106 + Verifying : dracut-049-233.git20240115.0.1.el8.x86_64 6/106 + Verifying : file-5.33-25.el8.x86_64 7/106 + Verifying : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 8/106 + Verifying : gettext-0.19.8.1-17.el8.x86_64 9/106 + Verifying : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 + Verifying : glib-networking-2.56.1-1.1.el8.x86_64 11/106 + Verifying : grub2-common-1:2.02-156.0.2.el8.noarch 12/106 + Verifying : grub2-tools-1:2.02-156.0.2.el8.x86_64 13/106 + Verifying : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 14/106 + Verifying : grubby-8.40-49.0.2.el8.x86_64 15/106 + Verifying : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 16/106 + Verifying : hardlink-1:1.3-6.el8.x86_64 17/106 + Verifying : json-glib-1.4.4-1.el8.x86_64 18/106 + Verifying : kbd-2.0.4-11.el8.x86_64 19/106 + Verifying : kbd-legacy-2.0.4-11.el8.noarch 20/106 + Verifying : kbd-misc-2.0.4-11.el8.noarch 21/106 + Verifying : libcroco-0.6.12-4.el8_2.1.x86_64 22/106 + Verifying : libgomp-8.5.0-22.0.1.el8_10.x86_64 23/106 + Verifying : libgusb-0.3.0-1.el8.x86_64 24/106 + Verifying : libkcapi-1.4.0-2.0.1.el8.x86_64 25/106 + Verifying : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 26/106 + Verifying : libmodman-2.0.1-17.el8.x86_64 27/106 + Verifying : libpkgconf-1.4.2-1.el8.x86_64 28/106 + Verifying : libproxy-0.4.15-5.2.el8.x86_64 29/106 + Verifying : libsoup-2.62.3-5.el8.x86_64 30/106 + Verifying : lksctp-tools-1.0.18-3.el8.x86_64 31/106 + Verifying : memstrack-0.2.5-2.el8.x86_64 32/106 + Verifying : os-prober-1.74-9.0.1.el8.x86_64 33/106 + Verifying : pigz-2.4-4.el8.x86_64 34/106 + Verifying : pkgconf-1.4.2-1.el8.x86_64 35/106 + Verifying : pkgconf-m4-1.4.2-1.el8.noarch 36/106 + Verifying : pkgconf-pkg-config-1.4.2-1.el8.x86_64 37/106 + Verifying : shared-mime-info-1.9-4.el8.x86_64 38/106 + Verifying : systemd-udev-239-78.0.4.el8.x86_64 39/106 + Verifying : xz-5.2.4-4.el8_6.x86_64 40/106 + Verifying : abattis-cantarell-fonts-0.0.25-6.el8.noarch 41/106 + Verifying : adwaita-cursor-theme-3.28.0-3.el8.noarch 42/106 + Verifying : adwaita-icon-theme-3.28.0-3.el8.noarch 43/106 + Verifying : alsa-lib-1.2.10-2.el8.x86_64 44/106 + Verifying : at-spi2-atk-2.26.2-1.el8.x86_64 45/106 + Verifying : at-spi2-core-2.28.0-1.el8.x86_64 46/106 + Verifying : atk-2.28.1-1.el8.x86_64 47/106 + Verifying : cairo-1.15.12-6.el8.x86_64 48/106 + Verifying : cairo-gobject-1.15.12-6.el8.x86_64 49/106 + Verifying : colord-libs-1.4.2-1.el8.x86_64 50/106 + Verifying : copy-jdk-configs-4.0-2.el8.noarch 51/106 + Verifying : dconf-0.28.0-4.0.1.el8.x86_64 52/106 + Verifying : fribidi-1.0.4-9.el8.x86_64 53/106 + Verifying : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 54/106 + Verifying : graphite2-1.3.10-10.el8.x86_64 55/106 + Verifying : gtk-update-icon-cache-3.22.30-11.el8.x86_64 56/106 + Verifying : gtk3-3.22.30-11.el8.x86_64 57/106 + Verifying : harfbuzz-1.7.5-4.el8.x86_64 58/106 + Verifying : hicolor-icon-theme-0.17-2.el8.noarch 59/106 + Verifying : jasper-libs-2.0.14-5.el8.x86_64 60/106 + Verifying : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 61/106 + Verifying : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 62/106 + Verifying : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 63/106 + Verifying : javapackages-filesystem-5.3.0-1.module+el8+5136+ 64/106 + Verifying : jbigkit-libs-2.1-14.el8.x86_64 65/106 + Verifying : lcms2-2.9-2.el8.x86_64 66/106 + Verifying : libX11-1.6.8-8.el8.x86_64 67/106 + Verifying : libX11-common-1.6.8-8.el8.noarch 68/106 + Verifying : libXau-1.0.9-3.el8.x86_64 69/106 + Verifying : libXcomposite-0.4.4-14.el8.x86_64 70/106 + Verifying : libXcursor-1.1.15-3.el8.x86_64 71/106 + Verifying : libXdamage-1.1.4-14.el8.x86_64 72/106 + Verifying : libXext-1.3.4-1.el8.x86_64 73/106 + Verifying : libXfixes-5.0.3-7.el8.x86_64 74/106 + Verifying : libXft-2.3.3-1.el8.x86_64 75/106 + Verifying : libXi-1.7.10-1.el8.x86_64 76/106 + Verifying : libXinerama-1.1.4-1.el8.x86_64 77/106 + Verifying : libXrandr-1.5.2-1.el8.x86_64 78/106 + Verifying : libXrender-0.9.10-7.el8.x86_64 79/106 + Verifying : libXtst-1.2.3-7.el8.x86_64 80/106 + Verifying : libdatrie-0.2.9-7.el8.x86_64 81/106 + Verifying : libepoxy-1.5.8-1.el8.x86_64 82/106 + Verifying : libfontenc-1.1.3-8.el8.x86_64 83/106 + Verifying : libjpeg-turbo-1.5.3-12.el8.x86_64 84/106 + Verifying : libthai-0.1.27-2.el8.x86_64 85/106 + Verifying : libtiff-4.0.9-32.el8_10.x86_64 86/106 + Verifying : libwayland-client-1.21.0-1.el8.x86_64 87/106 + Verifying : libwayland-cursor-1.21.0-1.el8.x86_64 88/106 + Verifying : libwayland-egl-1.21.0-1.el8.x86_64 89/106 + Verifying : libxcb-1.13.1-1.el8.x86_64 90/106 + Verifying : libxkbcommon-0.9.1-1.el8.x86_64 91/106 + Verifying : lua-5.3.4-12.el8.x86_64 92/106 + Verifying : nspr-4.35.0-1.el8_8.x86_64 93/106 + Verifying : nss-3.90.0-7.el8_10.x86_64 94/106 + Verifying : nss-softokn-3.90.0-7.el8_10.x86_64 95/106 + Verifying : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 96/106 + Verifying : nss-sysinit-3.90.0-7.el8_10.x86_64 97/106 + Verifying : nss-util-3.90.0-7.el8_10.x86_64 98/106 + Verifying : pango-1.42.4-8.el8.x86_64 99/106 + Verifying : pixman-0.38.4-4.el8.x86_64 100/106 + Verifying : rest-0.8.1-2.el8.x86_64 101/106 + Verifying : ttmkfdir-3.0.9-54.el8.x86_64 102/106 + Verifying : tzdata-java-2024a-1.0.1.el8.noarch 103/106 + Verifying : xkeyboard-config-2.28-1.el8.noarch 104/106 + Verifying : xorg-x11-font-utils-1:7.5-41.el8.x86_64 105/106 + Verifying : xorg-x11-fonts-Type1-7.5-19.el8.noarch 106/106 + +Installed: + abattis-cantarell-fonts-0.0.25-6.el8.noarch + adwaita-cursor-theme-3.28.0-3.el8.noarch + adwaita-icon-theme-3.28.0-3.el8.noarch + alsa-lib-1.2.10-2.el8.x86_64 + at-spi2-atk-2.26.2-1.el8.x86_64 + at-spi2-core-2.28.0-1.el8.x86_64 + atk-2.28.1-1.el8.x86_64 + avahi-libs-0.7-27.el8.x86_64 + cairo-1.15.12-6.el8.x86_64 + cairo-gobject-1.15.12-6.el8.x86_64 + colord-libs-1.4.2-1.el8.x86_64 + copy-jdk-configs-4.0-2.el8.noarch + cpio-2.12-11.el8.x86_64 + crypto-policies-scripts-20230731-1.git3177e06.el8.noarch + cups-libs-1:2.2.6-60.el8_10.x86_64 + dconf-0.28.0-4.0.1.el8.x86_64 + dejavu-sans-mono-fonts-2.35-7.el8.noarch + dracut-049-233.git20240115.0.1.el8.x86_64 + file-5.33-25.el8.x86_64 + fribidi-1.0.4-9.el8.x86_64 + gdk-pixbuf2-2.36.12-6.el8_10.x86_64 + gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 + gettext-0.19.8.1-17.el8.x86_64 + gettext-libs-0.19.8.1-17.el8.x86_64 + glib-networking-2.56.1-1.1.el8.x86_64 + graphite2-1.3.10-10.el8.x86_64 + grub2-common-1:2.02-156.0.2.el8.noarch + grub2-tools-1:2.02-156.0.2.el8.x86_64 + grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 + grubby-8.40-49.0.2.el8.x86_64 + gsettings-desktop-schemas-3.32.0-6.el8.x86_64 + gtk-update-icon-cache-3.22.30-11.el8.x86_64 + gtk3-3.22.30-11.el8.x86_64 + hardlink-1:1.3-6.el8.x86_64 + harfbuzz-1.7.5-4.el8.x86_64 + hicolor-icon-theme-0.17-2.el8.noarch + jasper-libs-2.0.14-5.el8.x86_64 + java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 + java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x86_64 + java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86_64 + javapackages-filesystem-5.3.0-1.module+el8+5136+7ff78f74.noarch + jbigkit-libs-2.1-14.el8.x86_64 + json-glib-1.4.4-1.el8.x86_64 + kbd-2.0.4-11.el8.x86_64 + kbd-legacy-2.0.4-11.el8.noarch + kbd-misc-2.0.4-11.el8.noarch + lcms2-2.9-2.el8.x86_64 + libX11-1.6.8-8.el8.x86_64 + libX11-common-1.6.8-8.el8.noarch + libXau-1.0.9-3.el8.x86_64 + libXcomposite-0.4.4-14.el8.x86_64 + libXcursor-1.1.15-3.el8.x86_64 + libXdamage-1.1.4-14.el8.x86_64 + libXext-1.3.4-1.el8.x86_64 + libXfixes-5.0.3-7.el8.x86_64 + libXft-2.3.3-1.el8.x86_64 + libXi-1.7.10-1.el8.x86_64 + libXinerama-1.1.4-1.el8.x86_64 + libXrandr-1.5.2-1.el8.x86_64 + libXrender-0.9.10-7.el8.x86_64 + libXtst-1.2.3-7.el8.x86_64 + libcroco-0.6.12-4.el8_2.1.x86_64 + libdatrie-0.2.9-7.el8.x86_64 + libepoxy-1.5.8-1.el8.x86_64 + libfontenc-1.1.3-8.el8.x86_64 + libgomp-8.5.0-22.0.1.el8_10.x86_64 + libgusb-0.3.0-1.el8.x86_64 + libjpeg-turbo-1.5.3-12.el8.x86_64 + libkcapi-1.4.0-2.0.1.el8.x86_64 + libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 + libmodman-2.0.1-17.el8.x86_64 + libpkgconf-1.4.2-1.el8.x86_64 + libproxy-0.4.15-5.2.el8.x86_64 + libsoup-2.62.3-5.el8.x86_64 + libthai-0.1.27-2.el8.x86_64 + libtiff-4.0.9-32.el8_10.x86_64 + libwayland-client-1.21.0-1.el8.x86_64 + libwayland-cursor-1.21.0-1.el8.x86_64 + libwayland-egl-1.21.0-1.el8.x86_64 + libxcb-1.13.1-1.el8.x86_64 + libxkbcommon-0.9.1-1.el8.x86_64 + lksctp-tools-1.0.18-3.el8.x86_64 + lua-5.3.4-12.el8.x86_64 + memstrack-0.2.5-2.el8.x86_64 + nspr-4.35.0-1.el8_8.x86_64 + nss-3.90.0-7.el8_10.x86_64 + nss-softokn-3.90.0-7.el8_10.x86_64 + nss-softokn-freebl-3.90.0-7.el8_10.x86_64 + nss-sysinit-3.90.0-7.el8_10.x86_64 + nss-util-3.90.0-7.el8_10.x86_64 + os-prober-1.74-9.0.1.el8.x86_64 + pango-1.42.4-8.el8.x86_64 + pigz-2.4-4.el8.x86_64 + pixman-0.38.4-4.el8.x86_64 + pkgconf-1.4.2-1.el8.x86_64 + pkgconf-m4-1.4.2-1.el8.noarch + pkgconf-pkg-config-1.4.2-1.el8.x86_64 + rest-0.8.1-2.el8.x86_64 + shared-mime-info-1.9-4.el8.x86_64 + systemd-udev-239-78.0.4.el8.x86_64 + ttmkfdir-3.0.9-54.el8.x86_64 + tzdata-java-2024a-1.0.1.el8.noarch + xkeyboard-config-2.28-1.el8.noarch + xorg-x11-font-utils-1:7.5-41.el8.x86_64 + xorg-x11-fonts-Type1-7.5-19.el8.noarch + xz-5.2.4-4.el8_6.x86_64 + +Complete! +Last metadata expiration check: 0:00:23 ago on Tue 20 Aug 2024 08:55:14 AM UTC. +Package iproute-6.2.0-5.el8_9.x86_64 is already installed. +Dependencies resolved. +================================================================================ + Package Architecture Version Repository Size +================================================================================ +Upgrading: + iproute x86_64 6.2.0-6.el8_10 ol8_baseos_latest 853 k + +Transaction Summary +================================================================================ +Upgrade 1 Package + +Total download size: 853 k +Downloading Packages: +iproute-6.2.0-6.el8_10.x86_64.rpm 4.2 MB/s | 853 kB 00:00 +-------------------------------------------------------------------------------- +Total 4.2 MB/s | 853 kB 00:00 +Running transaction check +Transaction check succeeded. +Running transaction test +Transaction test succeeded. +Running transaction + Preparing : 1/1 + Upgrading : iproute-6.2.0-6.el8_10.x86_64 1/2 + Cleanup : iproute-6.2.0-5.el8_9.x86_64 2/2 + Running scriptlet: iproute-6.2.0-5.el8_9.x86_64 2/2 + Verifying : iproute-6.2.0-6.el8_10.x86_64 1/2 + Verifying : iproute-6.2.0-5.el8_9.x86_64 2/2 + +Upgraded: + iproute-6.2.0-6.el8_10.x86_64 + +Complete! +24 files removed +Removing intermediate container fe168b01f3ad + ---> 791878694a50 +Step 5/12 : RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm + ---> Running in 59d7143da358 + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 108M 100 108M 0 0 1440k 0 0:01:16 0:01:16 --:--:-- 1578k +Removing intermediate container 59d7143da358 + ---> 17c4534293e5 +Step 6/12 : RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm + ---> Running in 84b1cbffdc51 +Verifying... ######################################## +Preparing... ######################################## +Updating / installing... +ords-23.4.0-8.el8 ######################################## +INFO: Before starting ORDS service, run the below command as user oracle: + ords --config /etc/ords/config install +Removing intermediate container 84b1cbffdc51 + ---> 6e7151b79588 +Step 7/12 : RUN mkdir -p $ORDS_HOME/doc_root && mkdir -p $ORDS_HOME/error && mkdir -p $ORDS_HOME/secrets && chmod ug+x $ORDS_HOME/*.sh && groupadd -g 54322 dba && usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && chown -R oracle:dba $ORDS_HOME && echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + ---> Running in 66e5db5f343f +Removing intermediate container 66e5db5f343f + ---> 0523dc897bf4 +Step 8/12 : USER oracle + ---> Running in ffda8495ac77 +Removing intermediate container ffda8495ac77 + ---> 162acd4d0b93 +Step 9/12 : WORKDIR /home/oracle + ---> Running in 8c14310ffbc7 +Removing intermediate container 8c14310ffbc7 + ---> c8dae809e772 +Step 10/12 : VOLUME ["$ORDS_HOME/config/ords"] + ---> Running in ed64548fd997 +Removing intermediate container ed64548fd997 + ---> 22e2c99247b0 +Step 11/12 : EXPOSE 8888 + ---> Running in 921f7c85d61d +Removing intermediate container 921f7c85d61d + ---> e5d503c92224 +Step 12/12 : CMD $ORDS_HOME/$RUN_FILE + ---> Running in cad487298d63 +Removing intermediate container cad487298d63 + ---> fdb17aa242f8 +Successfully built fdb17aa242f8 +Successfully tagged oracle/ords-dboper:latest +08:57:18 oracle@mitk01:# + diff --git a/docs/multitenant/usecase01/logfiles/openssl_execution.log b/docs/multitenant/usecase01/logfiles/openssl_execution.log new file mode 100644 index 00000000..e3915a21 --- /dev/null +++ b/docs/multitenant/usecase01/logfiles/openssl_execution.log @@ -0,0 +1,19 @@ +CREATING TLS CERTIFICATES +/usr/bin/openssl genrsa -out ca.key 2048 +Generating RSA private key, 2048 bit long modulus (2 primes) +......................+++++ +..................................................+++++ +e is 65537 (0x010001) +/usr/bin/openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost Root CA " -out ca.crt +/usr/bin/openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost" -out server.csr +Generating a RSA private key +...........+++++ +...........................................+++++ +writing new private key to 'tls.key' +----- +/usr/bin/echo "subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com" > extfile.txt +/usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt +Signature ok +subject=C = US, ST = California, L = SanFrancisco, O = "oracle ", CN = "cdb-dev-ords.oracle-database-operator-system ", CN = localhost +Getting CA Private Key + diff --git a/docs/multitenant/usecase01/logfiles/ordsconfig.log b/docs/multitenant/usecase01/logfiles/ordsconfig.log new file mode 100644 index 00000000..b787b752 --- /dev/null +++ b/docs/multitenant/usecase01/logfiles/ordsconfig.log @@ -0,0 +1,39 @@ +ORDS: Release 23.4 Production on Tue Aug 20 07:48:44 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +Database pool: default + +Setting Value Source +----------------------------------------- -------------------------------------------------- ----------- +database.api.enabled true Global +database.api.management.services.disabled false Global +db.cdb.adminUser C##DBAPI_CDB_ADMIN AS SYSDBA Pool +db.cdb.adminUser.password ****** Pool Wallet +db.connectionType customurl Pool +db.customURL jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90 Pool + )(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNEC + T_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL= + TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONL + Y))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST= + scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNEC + T_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) +db.password ****** Pool Wallet +db.serviceNameSuffix Pool +db.username ORDS_PUBLIC_USER Pool +error.externalPath /opt/oracle/ords/error Global +jdbc.InitialLimit 50 Pool +jdbc.MaxLimit 100 Pool +misc.pagination.maxRows 1000 Pool +plsql.gateway.mode disabled Pool +restEnabledSql.active true Pool +security.requestValidationFunction ords_util.authorize_plsql_gateway Pool +security.verifySSL true Global +standalone.access.log /home/oracle Global +standalone.https.cert /opt/oracle/ords//secrets/tls.crt Global +standalone.https.cert.key /opt/oracle/ords//secrets/tls.key Global +standalone.https.port 8888 Global + diff --git a/docs/multitenant/usecase01/makefile b/docs/multitenant/usecase01/makefile new file mode 100644 index 00000000..d4176c75 --- /dev/null +++ b/docs/multitenant/usecase01/makefile @@ -0,0 +1,284 @@ +# __ __ _ __ _ _ +# | \/ | __ _| | _____ / _(_) | ___ +# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ +# | | | | (_| | < __/ _| | | __/ +# |_| |_|\__,_|_|\_\___|_| |_|_|\___| +# +# ___ +# / _ \ _ __ _ __ _ __ ___ _ __ ___ +# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ +# | |_| | | | | |_) | | | __/ | | | | | +# \___/|_| |_| .__/|_| \___|_| |_| |_| +# |_| +# ____ _ _ _ +# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ +# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| +# | |__| (_) | | | | |_| | | (_) | | | __/ | +# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| +# +# +# This makefile helps to speed up the kubectl commands executions to deploy and test +# the OnPremises operator. Although it has few functionality you can adapt to your needs +# by adding much more targets. +# +# Quick start: +# ~~~~~~~~~~~ +# +# - Copy files of tab.1 in the makefile directory. +# - Edit the secret files and other yaml files with the correct credential as +# specified in the documentation. +# - Edit makefile updating variables of tab.2 +# - Execute commands of tab.3 "make step1" "make step2" "make step3".... +# +# Tab.1 - List of required files +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# |oracle-database-operator.yaml| Opertaor yaml file | +# +-----------------------------+---------------------------------------------+ +# |cdb_secret.yaml | Secret file for the rest server pod | +# +-----------------------------+---------------------------------------------+ +# |pdb_secret.yaml | Secret file for the pdb creation | +# +-----------------------------+---------------------------------------------+ +# |tde_secret.yaml | Secret file for the tablepsace enc. | +# +-----------------------------+---------------------------------------------+ +# |cdb_create.yaml | Rest server pod creation | +# +-----------------------------+---------------------------------------------+ +# |pdb_create.yaml | Pluggable database creation | +# +-----------------------------+---------------------------------------------+ +# |pdb_close.yaml | Close pluggable database | +# +-----------------------------+---------------------------------------------+ +# |pdb_open.yaml | Open pluggable database | +# +-----------------------------+---------------------------------------------+ +# |pdb_map.yaml | Map an existing pdb | +# +-----------------------------+---------------------------------------------+ +# |oracle-database-operator.yaml| Database operator | +# +-----------------------------+---------------------------------------------+ +# |Dockerfiles | Dockerfile for CBD | +# +-----------------------------+---------------------------------------------+ +# |runOrdsSSL.sh | Init script executed by Dockerfile | +# +-----------------------------+---------------------------------------------+ +# +# Tab.2 - List of variables +# ~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# |OCIR | Your image registry | +# +-----------------------------+---------------------------------------------+ +# |OCIRPATH | Path of the image in your registry | +# +-----------------------------+---------------------------------------------+ +# +# Tab.3 - Execution steps +# ~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# | MAKEFILE TARGETS LIST | +# | ----- ooo ----- | +# | - TARGET - - DESCRIPTION - | +# +-----------------------------+-------------------------------------+-------+ +# |step1 | Build rest server images | | +# +-----------------------------+-------------------------------------+ REST | +# |step2 | Tag the immages | SRV | +# +-----------------------------+-------------------------------------+ IMG | +# |step3 | Push the image into the repository | | +# +-----------------------------+-------------------------------------+-------+ +# |step4 | Load webhook certmanager | DB | +# +-----------------------------+-------------------------------------+ OPER | +# |step5 | Create the db operator | | +# +-----------------------------+-------------------------------------+-------+ +# |step6 | Create tls certificates | T | +# +-----------------------------+-------------------------------------+ L | +# |step7 | Create tls secret | S | +# +-----------------------------+---------------------------------------------+ +# |step8 | Create database secrets | +# +-----------------------------+---------------------------------------------+ +# |step9 | Create restserver pod | +# | | +---------------------------------------------+ +# | +---> checkstep9 | Monitor the executions | +# +-----------------------------+---------------------------------------------+ +# |step10 | Create pluggable database | +# | | +---------------------------------------------+ +# | +---> checkpdb | Monitor PDB status | +# +-----------------------------+---------------------------------------------+ +# |step11 | Close pluggable database | +# +-----------------------------+---------------------------------------------+ +# |step12 | Open pluggable database | +# +-----------------------------+---------------------------------------------+ +# |step13 | Map pluggable database | +# +-----------------------------+---------------------------------------------+ +# | Before testing step13 delete the crd: | +# | kubectl delete pdb pdb1 -n oracle-database-operator-system | +# +---------------------------------------------------------------------------+ +# |step14 | delete pdb | +# +-----------------------------+---------------------------------------------+ +# | DIAGNOSTIC TARGETS | +# +-----------------------------+---------------------------------------------+ +# | dump | Dump pods info into a file | +# +-----------------------------+---------------------------------------------+ +# | reloadop | Reload the db operator | +# +-----------------------------+---------------------------------------------+ +# | login | Login into cdb pod | +# +-----------------------------+---------------------------------------------+ + + +################ TAB 2 VARIABLES ############ +OCIR=[...........YOUR REGISTRY...........] +OCIRPATH=[...PATH IN YOUR REGISTRY.....]/$(REST_SERVER)-dboper:$(ORDSVERSION) +############################################# +REST_SERVER=ords +ORDSVERSION=latest +DOCKER=/usr/bin/docker +KUBECTL=/usr/bin/kubectl +ORDS=/usr/local/bin/ords +CONFIG=/etc/ords/config +IMAGE=oracle/$(REST_SERVER)-dboper:$(ORDSVERSION) +DBOPERATOR=oracle-database-operator.yaml +URLPATH=/_/db-api/stable/database/pdbs/ +OPENSSL=/usr/bin/openssl +ORDSPORT=8888 +MAKE=/usr/bin/make +DOCKERFILE=../../../ords/Dockerfile +RUNSCRIPT=../../../ords/runOrdsSSL.sh +ORDSIMGDIR=../../../ords +RM=/usr/bin/rm +CP=/usr/bin/cp +ECHO=/usr/bin/echo +NAMESPACE=oracle-database-operator-system +CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml +CDB_SECRET=cdb_secret.yaml +PDB_SECRET=pdb_secret.yaml +TDE_SECRET=tde_secret.yaml +CDB=cdb_create.yaml +PDB=pdb_create.yaml +PDB_CLOSE=pdb_close.yaml +PDB_OPEN=pdb_open.yaml +PDB_MAP=pdb_map.yaml +SKEY=tls.key +SCRT=tls.crt +CART=ca.crt +COMPANY=oracle +LOCALHOST=localhost +RESTPREFIX=cdb-dev + +step1: createimage +step2: tagimage +step3: push +step4: certmanager +step5: dboperator +step6: tlscert +step7: tlssecret +step8: dbsecret +step9: cdb +step10: pdb +step11: close +step12: open +step13: map +step14: delete + +checkstep9: checkcdb + + +createimage: + $(DOCKER) build -t $(IMAGE) $(ORDSIMGDIR) + +tagimage: + @echo "TAG IMAGE" + $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) + +push: + @echo "PUSH IMAGE INTO THE REGISTRY" + $(DOCKER) push $(OCIR)$(OCIRPATH) + +certmanager: + @echo "WEBHOOK CERT MANAGER" + $(KUBECTL) apply -f $(CERTMANAGER) + +dboperator: + @echo "ORACLE DATABASE OPERATOR" + $(KUBECTL) apply -f $(DBOPERATOR) + + +#C: Country +#ST: State +#L: locality (city) +#O: Organization Name Organization Unit +#CN: Common Name + +tlscert: + @echo "CREATING TLS CERTIFICATES" + $(OPENSSL) genrsa -out ca.key 2048 + $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE) /CN=$(LOCALHOST)" -out server.csr + $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE),DNS:www.example.com" > extfile.txt + $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) + +tlssecret: + @echo "CREATING TLS SECRETS" + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(NAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(NAMESPACE) + +dbsecret: + @echo "CREATING DB SECRETS" + $(KUBECTL) apply -f $(CDB_SECRET) -n $(NAMESPACE) + $(KUBECTL) apply -f $(PDB_SECRET) -n $(NAMESPACE) + $(KUBECTL) apply -f $(TDE_SECRET) -n $(NAMESPACE) + +cdb: + @echo "CREATING REST SRV POD" + $(KUBECTL) apply -f $(CDB) + +checkcdb: + $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(NAMESPACE) + +pdb: + $(KUBECTL) apply -f $(PDB) + +close: + $(KUBECTL) apply -f $(PDB_CLOSE) + +open: + $(KUBECTL) apply -f $(PDB_OPEN) + +map: + $(KUBECTL) apply -f $(PDB_MAP) + +checkpdb: + $(KUBECTL) get pdbs -n $(NAMESPACE) + +delete: + $(KUBECTL) apply -f pdb_delete.yaml + +dump: + @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) + @$(eval DIAGFILE := ./opdmp.$(TMPSP)) + @>$(DIAGFILE) + @echo "OPERATOR DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) + @echo "CDB LOG DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs `$(KUBECTL) get pods -n $(NAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) + @echo "SECRET DMP" >>$(DIAGFILE) + @echo "~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) get secrets -o yaml -n $(NAMESPACE) >> $(DIAGFILE) + @echo "CDB/PDB DMP" >> $(DIAGFILE) + $(KUBECTL) get pdbs -o yaml -n $(NAMESPACE) >> $(DIAGFILE) + $(KUBECTL) get cdb -o yaml -n $(NAMESPACE) >> $(DIAGFILE) + @echo "CLUSTER INFO" >> $(DIAGFILE) + $(KUBECTL) get nodes -o wide + $(KUBECTL) get svc --namespace=kube-system + +reloadop: + echo "RESTARTING OPERATOR" + $(eval OP1 := $(shell $(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) + $(eval OP2 := $(shell $(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) + $(eval OP3 := $(shell $(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) + $(KUBECTL) get pod $(OP1) -n $(NAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP2) -n $(NAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP3) -n $(NAMESPACE) -o yaml | kubectl replace --force -f - + +login: + $(KUBECTL) exec -it `$(KUBECTL) get pods -n $(NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(NAMESPACE) bash + diff --git a/docs/multitenant/usecase03/Dockerfile b/docs/multitenant/usecase03/Dockerfile new file mode 100644 index 00000000..772a7e6d --- /dev/null +++ b/docs/multitenant/usecase03/Dockerfile @@ -0,0 +1,80 @@ +## Copyright (c) 2022 Oracle and/or its affiliates. +## +## The Universal Permissive License (UPL), Version 1.0 +## +## Subject to the condition set forth below, permission is hereby granted to any +## person obtaining a copy of this software, associated documentation and/or data +## (collectively the "Software"), free of charge and under any and all copyright +## rights in the Software, and any and all patent rights owned or freely +## licensable by each licensor hereunder covering either (i) the unmodified +## Software as contributed to or provided by such licensor, or (ii) the Larger +## Works (as defined below), to deal in both +## +## (a) the Software, and +## (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +## one is included with the Software (each a "Larger Work" to which the Software +## is contributed by such licensors), +## +## without restriction, including without limitation the rights to copy, create +## derivative works of, display, perform, and distribute the Software and make, +## use, sell, offer for sale, import, export, have made, and have sold the +## Software and the Larger Work(s), and to sublicense the foregoing rights on +## either these or other terms. +## +## This license is subject to the following condition: +## The above copyright notice and either this complete permission notice or at +## a minimum a reference to the UPL must be included in all copies or +## substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +## SOFTWARE. + +FROM container-registry.oracle.com/java/jdk:latest + +# Environment variables required for this build (do NOT change) +# ------------------------------------------------------------- +ENV ORDS_HOME=/opt/oracle/ords/ \ + RUN_FILE="runOrdsSSL.sh" \ + ORDSVERSION=23.4.0-8 + +# Copy binaries +# ------------- +COPY $RUN_FILE $ORDS_HOME + +RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && \ + yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && \ + yum -y install java-11-openjdk-devel && \ + yum -y install iproute && \ + yum clean all + +RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm + +RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm + +# Setup filesystem and oracle user +# -------------------------------- +RUN mkdir -p $ORDS_HOME/doc_root && \ + mkdir -p $ORDS_HOME/error && \ + mkdir -p $ORDS_HOME/secrets && \ + chmod ug+x $ORDS_HOME/*.sh && \ + groupadd -g 54322 dba && \ + usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && \ + chown -R oracle:dba $ORDS_HOME && \ + echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +# Finalize setup +# ------------------- +USER oracle +WORKDIR /home/oracle + +VOLUME ["$ORDS_HOME/config/ords"] +EXPOSE 8888 + +# Define default command to start Ords Services +CMD $ORDS_HOME/$RUN_FILE + diff --git a/docs/multitenant/usecase03/NamespaceSegregation.png b/docs/multitenant/usecase03/NamespaceSegregation.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb0ae77818e87ed64bf248bc0c173a4aac28c73 GIT binary patch literal 270813 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≪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 S*V+lde;zJA&et8oss9JAh>QII literal 0 HcmV?d00001 diff --git a/docs/multitenant/usecase03/README.md b/docs/multitenant/usecase03/README.md new file mode 100644 index 00000000..c06368cd --- /dev/null +++ b/docs/multitenant/usecase03/README.md @@ -0,0 +1,268 @@ + + + +# STEP BY STEP (NAMESPACE SEGREGATION) + +- [STEP BY STEP (NAMESPACE SEGREGATION)](#step-by-step-namespace-segregation) + - [INTRODUCTION](#introduction) + - [GIT CLONE ORACLE DATABASE OPERATOR PROJECT](#git-clone-oracle-database-operator-project) + - [NAMESPACE CREATION](#namespace-creation) + - [WEBHOOK CERTIFICATES](#webhook-certificates) + - [ORACLE DATABASE OPERATOR](#oracle-database-operator) + - [CREATE PDB AND CDB SECRETS](#create-pdb-and-cdb-secrets) + - [CREATE TLS CERTIFICATE](#create-tls-certificate) + - [REST SERVER IMAGE CREATION](#rest-server-image-creation) + - [CDB POD CREATION](#cdb-pod-creation) + - [PDB CREATION](#pdb-creation) + - [MAKEFILE](#makefile) + + +### INTRODUCTION + +> ☞ This folder contains the yaml files required to configure and manage cdb and pdb in different namespaces. The main change here is the possibility to specify the namespace where CDB will be created, this implies the introduction of new parameter at PDB level in order to specify the CDB namespace. + +Tasks performed in the usecase03 are the same ones of the other usecase01 with the exception that controller pods cdb pods and pdb crd are running in different namespaces. You must be aware of the fact that secrets must be created in the proper namespaces; cdb secrets go into cdb namespace , pdb secrets go into pdbnamespace while certificate secrets need to be created in every namespace. + + +| yaml file parameters | value | description /ords parameter | +|-------------- |--------------------------- |-------------------------------------------------| +| ☞ cdbNamespace | | Cdb namespace | +| dbserver | or | [--db-hostname][1] | +| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | +| port | | [--db-port][2] | +| cdbName | | Container Name | +| name | | Ords podname prefix in cdb.yaml | +| name | | pdb resource in pdb.yaml | +| ordsImage | /ords-dboper:latest|My public container registry | +| pdbName | | Pluggable database name | +| servicename | | [--db-servicename][3] | +| sysadmin_user | | [--admin-user][adminuser] | +| sysadmin_pwd | | [--password-stdin][pwdstdin] | +| cdbadmin_user | | [db.cdb.adminUser][1] | +| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | +| webserver_user| | [https user][http] NOT A DB USER | +| webserver_pwd | | [http user password][http] | +| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | +| pdbTlsKey | | [standalone.https.cert.key][key] | +| pdbTlsCrt | | [standalone.https.cert][cr] | +| pdbTlsCat | | certificate authority | +| xmlFileName | | path for the unplug and plug operation | +| srcPdbName | | name of the database to be cloned | +| fileNameConversions | | used for database cloning | +| tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | +| tdeExport | | [tdeExport] | +| tdeSecret | | [tdeSecret][tdeSecret] | +| tdePassword | | [tdeSecret][tdeSecret] | +| assertivePdbDeletion | boolean | [turn on imperative approach on crd deleteion][imperative] | + +![generla schema](./NamespaceSegregation.png) + +### GIT CLONE ORACLE DATABASE OPERATOR PROJECT + +```bash +git clone https://github.com/oracle/oracle-database-operator.git +cd oracle-database-operator/docs/multitenant/usecase03 +``` +### NAMESPACE CREATION + +We need first to create two different namespaces (**cdbnamespace**,**pdbnamespace**) using ns_pdb_namespace.yaml and ns_cdb_namespace.yaml + +```bash +kubectl apply -f ns_pdb_namespace.yaml +kubectl apply -f ns_cdb_namespace.yaml +``` + +### WEBHOOK CERTIFICATES +Create cert manager and verify the status + +```bash +kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml +``` + +```bash +kubectl get pods --namespace cert-manager +NAME READY STATUS RESTARTS AGE +cert-manager-75997f4b44-4nf5c 1/1 Running 1 9d +cert-manager-cainjector-769785cd7b-mzfq5 1/1 Running 1 9d +cert-manager-webhook-6bc9944d78-tslrp 1/1 Running 1 9d +``` + +### ORACLE DATABASE OPERATOR + +Create the oracle database operator using oracle-database-operator.yaml +```bash +cd oracle-database-operator +kubectl apply -f oracle-database-operator.yaml +cd - +``` + +[operator creation log](operator_creation_log.txt) +### CREATE PDB AND CDB SECRETS + +Update secrets files with your base64 encodede password. + +```bash +echo ImAdemoPassword | base64 +SW1BZGVtb1Bhc3N3b3JkCg== +``` +Apply the cdb_secret and pdb_secret yaml file to generate credential information in each namespace. + +``` +kubectl apply -f cdb_secret.yaml +kubectl apply -f pdb_secret.yaml +``` +> ☞ Note that https credential needs to be replicated in any secret file. It is possible to improve configuration by creating a dedicated namespace for https credential in order to specify this information only once. + +Namespace segregation enables the capability of deploying and manages pluggable database without the cdb administrative passwords. + +### CREATE TLS CERTIFICATE + +Here follow an example of script shell that can be used to create secret certificates in each namespace involved in the kubernets multi tenant architecture + +```bash +#!/bin/bash +export CDB_NAMESPACE=cdbnamespace +export PDB_NAMESPACE=pdbnamespace +export OPR_NAMESPACE=oracle-database-operator-system +export SKEY=tls.key +export SCRT=tls.crt +export CART=ca.crt +export COMPANY=oracle +export REST_SERVER=ords + +openssl genrsa -out ca.key 2048 +openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt +openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr +echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt +openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} + +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} +``` +after all secrets creation you shoud have the following pattern + +```bash +kubectl get secrets -n oracle-database-operator-system +NAME TYPE DATA AGE +db-ca Opaque 1 6d5h +db-tls kubernetes.io/tls 2 6d5h +webhook-server-cert kubernetes.io/tls 3 6d15h + + +kubectl get secrets -n cdbnamespace +NAME TYPE DATA AGE +cdb1-secret Opaque 6 6d15h +db-ca Opaque 1 6d6h +db-tls kubernetes.io/tls 2 6d6h + + +kubectl get secrets -n pdbnamespace +NAME TYPE DATA AGE +db-ca Opaque 1 6d6h +db-tls kubernetes.io/tls 2 6d6h +pdb1-secret Opaque 4 2d16h +tde1-secret Opaque 2 22h +``` +### REST SERVER IMAGE CREATION + +```bash +cd oracle-database-operator/ords +docker build -t oracle/ords-dboper:latest . +docker tag oracle/ords-dboper:latest [path_of_your_registry]/ords-dboper:latest +docker push [path_of_your_registry]/ords-dboper.latest +cd - +``` + +### CDB POD CREATION + +**note:** + Before creating the CDB pod make sure that all the pluggable databases in the container DB are open. + + + +Update the cdb_create.yaml with the path of the image generated before to create CDB pod + +```bash +kubectl apply -f cdb_create.yaml +``` + +Verify the status of the operation and cdb pod existence using the following commands + +```bash +## check the pod creation +kubectl get pods -n cdbnamespace + +## check the rest server log after pod creation +kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace + +##login to the pod for further debug and information gathering +kubectl exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash +``` + +[log cdb creation](./cdb_creation_log.txt) + +### PDB CREATION + +Apply the the pdb_create.yaml file to create a new pdb , after pdb creation you should be able to get pdb details using **kubectl get** command + +```bash +kubectl apply -f pdb_create.yaml +``` + +```bash +#!/bin/bash +#checkpdbs.sh +kubectl get pdbs -n pdbnamespace -o=jsonpath='{range .items[*]} +{"\n==================================================================\n"} +{"CDB="}{.metadata.labels.cdb} +{"K8SNAME="}{.metadata.name} +{"PDBNAME="}{.spec.pdbName} +{"OPENMODE="}{.status.openMode} +{"ACTION="}{.status.action} +{"MSG="}{.status.msg} +{"\n"}{end}' +``` + +```bash +./checkpdbs.sh +================================================================== +CDB=cdb-dev +K8SNAME=pdb1 +PDBNAME=pdbdev +OPENMODE=READ WRITE +ACTION=CREATE +MSG=Success + +``` +[pdb creation log](./pdb_creation_log.txt) + +### MAKEFILE + +In order to facilitate the command execution use the [makefile](./makefile) available target details are exposed in the following tables. + +|target |Action | +|-----------------------------|-------------------------------------| +|step1 | Build rest server images | +|step2 | Tag the immages | +|step3 | Push the image into the repository | +|step4 | Load webhook certmanager | +|step5 | Create the db operator | +|step6 | Create tls certificates | +|step7 | Create tls secret | +|step8 | Create database secrets | +|step9 | Create restserver pod | +|checkstep9 | Monitor the executions | +|step10 | Create pluggable database | +|checkpdb | Monitor PDB status | +|dump | Dump pods info into a file | +|reloadop | Reload the db operator | +|login | Login into cdb pod | + +[imperative]:https://kubernetes.io/docs/concepts/overview/working-with-objects/object-management/ + + + diff --git a/docs/multitenant/usecase03/cdb_create.yaml b/docs/multitenant/usecase03/cdb_create.yaml new file mode 100644 index 00000000..d3b5e04f --- /dev/null +++ b/docs/multitenant/usecase03/cdb_create.yaml @@ -0,0 +1,44 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: CDB +metadata: + name: cdb-dev + namespace: cdbnamespace +spec: + cdbName: "DB12" + ordsImage: ".............your registry............./ords-dboper:latest" + ordsImagePullPolicy: "Always" + dbTnsurl : "...Container tns alias....." + replicas: 1 + sysAdminPwd: + secret: + secretName: "cdb1-secret" + key: "sysadmin_pwd" + ordsPwd: + secret: + secretName: "cdb1-secret" + key: "ords_pwd" + cdbAdminUser: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_user" + cdbAdminPwd: + secret: + secretName: "cdb1-secret" + key: "cdbadmin_pwd" + webServerUser: + secret: + secretName: "cdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "cdb1-secret" + key: "webserver_pwd" + cdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + cdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + diff --git a/docs/multitenant/usecase03/cdb_creation_log.txt b/docs/multitenant/usecase03/cdb_creation_log.txt new file mode 100644 index 00000000..8c7dc161 --- /dev/null +++ b/docs/multitenant/usecase03/cdb_creation_log.txt @@ -0,0 +1,336 @@ +kubectl get pods -n cdbnamespace +NAME READY STATUS RESTARTS AGE +cdb-dev-ords-rs-pgqqh 0/1 ContainerCreating 0 1s + +kubectl get pods -n cdbnamespace +NAME READY STATUS RESTARTS AGE +cdb-dev-ords-rs-pgqqh 1/1 Running 0 6s + +kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M +NOT_INSTALLED=2 + SETUP +==================================================== +CONFIG=/etc/ords/config +total 0 +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:20 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.connectionType was set to: customurl in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:21 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.customURL was set to: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:23 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: security.requestValidationFunction was set to: false in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:25 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: jdbc.MaxLimit was set to: 100 in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:27 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: jdbc.InitialLimit was set to: 50 in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:29 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: error.externalPath was set to: /opt/oracle/ords/error +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:31 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.access.log was set to: /home/oracle +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:32 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.https.port was set to: 8888 +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:34 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.https.cert was set to: /opt/oracle/ords//secrets/tls.crt +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:36 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: standalone.https.cert.key was set to: /opt/oracle/ords//secrets/tls.key +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:38 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: restEnabledSql.active was set to: true in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:40 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: security.verifySSL was set to: true +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:42 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: database.api.enabled was set to: true +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:43 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: plsql.gateway.mode was set to: disabled in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:45 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The global setting named: database.api.management.services.disabled was set to: false +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:47 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: misc.pagination.maxRows was set to: 1000 in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:49 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.cdb.adminUser was set to: C##DBAPI_CDB_ADMIN AS SYSDBA in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:51 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +The setting named: db.cdb.adminUser.password was set to: ****** in configuration: default +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:53 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +Created user welcome in file /etc/ords/config/global/credentials +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:17:55 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +Oracle REST Data Services - Non-Interactive Install + +Retrieving information.. +Completed verifying Oracle REST Data Services schema version 23.3.0.r2891830. +Connecting to database user: ORDS_PUBLIC_USER url: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) +The setting named: db.serviceNameSuffix was set to: in configuration: default +The setting named: db.username was set to: ORDS_PUBLIC_USER in configuration: default +The setting named: db.password was set to: ****** in configuration: default +The setting named: security.requestValidationFunction was set to: ords_util.authorize_plsql_gateway in configuration: default +2024-01-25T17:17:58.898Z INFO Oracle REST Data Services schema version 23.3.0.r2891830 is installed. +2024-01-25T17:17:58.900Z INFO To run in standalone mode, use the ords serve command: +2024-01-25T17:17:58.900Z INFO ords --config /etc/ords/config serve +2024-01-25T17:17:58.900Z INFO Visit the ORDS Documentation to access tutorials, developer guides and more to help you get started with the new ORDS Command Line Interface (http://oracle.com/rest). +Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M + +ORDS: Release 23.3 Production on Thu Jan 25 17:18:00 2024 + +Copyright (c) 2010, 2024, Oracle. + +Configuration: + /etc/ords/config/ + +2024-01-25T17:18:00.960Z INFO HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8080 +2024-01-25T17:18:00.963Z INFO HTTPS and HTTPS/2 listening on host: 0.0.0.0 port: 8888 +2024-01-25T17:18:00.980Z INFO Disabling document root because the specified folder does not exist: /etc/ords/config/global/doc_root +2024-01-25T17:18:00.981Z INFO Default forwarding from / to contextRoot configured. +2024-01-25T17:18:06.634Z INFO Configuration properties for: |default|lo| +db.serviceNameSuffix= +java.specification.version=21 +conf.use.wallet=true +database.api.management.services.disabled=false +sun.jnu.encoding=UTF-8 +user.region=US +java.class.path=/opt/oracle/ords/ords.war +java.vm.vendor=Oracle Corporation +standalone.https.cert.key=/opt/oracle/ords//secrets/tls.key +sun.arch.data.model=64 +nashorn.args=--no-deprecation-warning +java.vendor.url=https://java.oracle.com/ +resource.templates.enabled=false +user.timezone=UTC +java.vm.specification.version=21 +os.name=Linux +sun.java.launcher=SUN_STANDARD +user.country=US +sun.boot.library.path=/usr/java/jdk-21/lib +sun.java.command=/opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure +jdk.debug=release +sun.cpu.endian=little +user.home=/home/oracle +oracle.dbtools.launcher.executable.jar.path=/opt/oracle/ords/ords.war +user.language=en +db.cdb.adminUser.password=****** +java.specification.vendor=Oracle Corporation +java.version.date=2023-10-17 +database.api.enabled=true +java.home=/usr/java/jdk-21 +db.username=ORDS_PUBLIC_USER +file.separator=/ +java.vm.compressedOopsMode=32-bit +line.separator= + +restEnabledSql.active=true +java.specification.name=Java Platform API Specification +java.vm.specification.vendor=Oracle Corporation +java.awt.headless=true +standalone.https.cert=/opt/oracle/ords//secrets/tls.crt +db.password=****** +sun.management.compiler=HotSpot 64-Bit Tiered Compilers +security.requestValidationFunction=ords_util.authorize_plsql_gateway +misc.pagination.maxRows=1000 +java.runtime.version=21.0.1+12-LTS-29 +user.name=oracle +error.externalPath=/opt/oracle/ords/error +stdout.encoding=UTF-8 +path.separator=: +db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA +os.version=5.4.17-2136.323.8.1.el7uek.x86_64 +java.runtime.name=Java(TM) SE Runtime Environment +file.encoding=UTF-8 +plsql.gateway.mode=disabled +security.verifySSL=true +standalone.https.port=8888 +java.vm.name=Java HotSpot(TM) 64-Bit Server VM +java.vendor.url.bug=https://bugreport.java.com/bugreport/ +java.io.tmpdir=/tmp +oracle.dbtools.cmdline.ShellCommand=ords +java.version=21.0.1 +user.dir=/home/oracle/keystore +os.arch=amd64 +java.vm.specification.name=Java Virtual Machine Specification +jdbc.MaxLimit=100 +oracle.dbtools.cmdline.home=/opt/oracle/ords +native.encoding=UTF-8 +java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib +java.vendor=Oracle Corporation +java.vm.info=mixed mode, sharing +stderr.encoding=UTF-8 +java.vm.version=21.0.1+12-LTS-29 +sun.io.unicode.encoding=UnicodeLittle +jdbc.InitialLimit=50 +db.connectionType=customurl +java.class.version=65.0 +db.customURL=jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) +standalone.access.log=/home/oracle + +2024-01-25T17:18:10.381Z INFO + +Mapped local pools from /etc/ords/config/databases: + /ords/ => default => VALID + + +2024-01-25T17:18:10.532Z INFO Oracle REST Data Services initialized +Oracle REST Data Services version : 23.3.0.r2891830 +Oracle REST Data Services server info: jetty/10.0.17 +Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 21.0.1+12-LTS-29 + + +exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash +[oracle@cdb-dev-ords-rs-pgqqh ~]$ ps -ef|grep java +oracle 1147 1116 10 17:17 ? 00:00:21 /usr/java/jdk-21/bin/java -Doracle.dbtools.cmdline.home=/opt/oracle/ords -Duser.language=en -Duser.region=US -Dfile.encoding=UTF-8 -Djava.awt.headless=true -Dnashorn.args=--no-deprecation-warning -Doracle.dbtools.cmdline.ShellCommand=ords -Duser.timezone=UTC -jar /opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure +oracle 1227 1200 0 17:21 pts/0 00:00:00 grep --color=auto java diff --git a/docs/multitenant/usecase03/cdb_secret.yaml b/docs/multitenant/usecase03/cdb_secret.yaml new file mode 100644 index 00000000..8f1b6fc9 --- /dev/null +++ b/docs/multitenant/usecase03/cdb_secret.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: cdb1-secret + namespace: cdbnamespace +type: Opaque +data: + ords_pwd: "[...base64 encoded password...]" + sysadmin_pwd: "[...base64 encoded password...]" + cdbadmin_user: "[...base64 encoded password...]" + cdbadmin_pwd: "[...base64 encoded password...]" + webserver_user: "[...base64 encoded password...]" + webserver_pwd: "[...base64 encoded password...]" diff --git a/docs/multitenant/usecase03/gentlscert.sh b/docs/multitenant/usecase03/gentlscert.sh new file mode 100644 index 00000000..49e29147 --- /dev/null +++ b/docs/multitenant/usecase03/gentlscert.sh @@ -0,0 +1,23 @@ +#!/bin/bash +export CDB_NAMESPACE=cdbnamespace +export PDB_NAMESPACE=pdbnamespace +export OPR_NAMESPACE=oracle-database-operator-system +export SKEY=tls.key +export SCRT=tls.crt +export CART=ca.crt +export COMPANY=oracle +export REST_SERVER=ords + +openssl genrsa -out ca.key 2048 +openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt +openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr +echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt +openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} + +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} +kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} +kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} + diff --git a/docs/multitenant/usecase03/makefile b/docs/multitenant/usecase03/makefile new file mode 100644 index 00000000..7270a5e0 --- /dev/null +++ b/docs/multitenant/usecase03/makefile @@ -0,0 +1,285 @@ +# __ __ _ __ _ _ +# | \/ | __ _| | _____ / _(_) | ___ +# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ +# | | | | (_| | < __/ _| | | __/ +# |_| |_|\__,_|_|\_\___|_| |_|_|\___| +# +# ___ +# / _ \ _ __ _ __ _ __ ___ _ __ ___ +# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ +# | |_| | | | | |_) | | | __/ | | | | | +# \___/|_| |_| .__/|_| \___|_| |_| |_| +# |_| +# ____ _ _ _ +# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ +# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| +# | |__| (_) | | | | |_| | | (_) | | | __/ | +# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| +# +# +# This makefile helps to speed up the kubectl commands executions to deploy and test +# the mutlitenant operator. Although it has few functionality you can adapt to your needs +# by adding much more targets. +# +# Quick start: +# ~~~~~~~~~~~ +# +# - Copy files of tab.1 in the makefile directory. +# - Edit the secret files and other yaml files with the correct credential as +# specified in the documentation. +# - Edit makefile updating variables of tab.2 +# - Execute commands of tab.3 "make step1" "make step2" "make step3".... +# +# Tab.1 - List of required files +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# |oracle-database-operator.yaml| Opertaor yaml file | +# +-----------------------------+---------------------------------------------+ +# |cdb_secret.yaml | Secret file for the rest server pod | +# +-----------------------------+---------------------------------------------+ +# |pdb_secret.yaml | Secret file for the pdb creation | +# +-----------------------------+---------------------------------------------+ +# |cdb_create.yaml | Rest server pod creation | +# +-----------------------------+---------------------------------------------+ +# |pdb_create.yaml | Pluggable database creation | +# +-----------------------------+---------------------------------------------+ +# |oracle-database-operator.yaml| Database operator | +# +-----------------------------+---------------------------------------------+ +# |Dockerfiles | Dockerfile for CBD | +# +-----------------------------+---------------------------------------------+ +# |runOrdsSSL.sh | Init script executed by Dockerfile | +# +-----------------------------+---------------------------------------------+ +# +# Tab.2 - List of variables +# ~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# |OCIR | Your image registry | +# +-----------------------------+---------------------------------------------+ +# |OCIRPATH | Path of the image in your registry | +# +-----------------------------+---------------------------------------------+ +# +# Tab.3 - Execution steps +# ~~~~~~~~~~~~~~~~~~~~~~~ +# +# +-----------------------------+---------------------------------------------+ +# | MAKEFILE TARGETS LIST | +# | ----- ooo ----- | +# | - TARGET - - DESCRIPTION - | +# +-----------------------------+-------------------------------------+-------+ +# |step1 | Build rest server images | | +# +-----------------------------+-------------------------------------+ REST | +# |step2 | Tag the immages | SRV | +# +-----------------------------+-------------------------------------+ IMG | +# |step3 | Push the image into the repository | | +# +-----------------------------+-------------------------------------+-------+ +# |step4 | Load webhook certmanager | DB | +# +-----------------------------+-------------------------------------+ OPER | +# |step5 | Create the db operator | | +# +-----------------------------+-------------------------------------+-------+ +# |step6 | Create tls certificates | T | +# +-----------------------------+-------------------------------------+ L | +# |step7 | Create tls secret | S | +# +-----------------------------+---------------------------------------------+ +# |step8 | Create database secrets | +# +-----------------------------+---------------------------------------------+ +# |step9 | Create restserver pod | +# | | +---------------------------------------------+ +# | +---> checkstep9 | Monitor the executions | +# +-----------------------------+---------------------------------------------+ +# |step10 | Create pluggable database | +# | | +---------------------------------------------+ +# | +---> checkpdb | Monitor PDB status | +# +-----------------------------+---------------------------------------------+ +# | DIAGNOSTIC TARGETS | +# +-----------------------------+---------------------------------------------+ +# | dump | Dump pods info into a file | +# +-----------------------------+---------------------------------------------+ +# | reloadop | Reload the db operator | +# +-----------------------------+---------------------------------------------+ +# | login | Login into cdb pod | +# +-----------------------------+---------------------------------------------+ + + +################ TAB 2 VARIABLES ############ +REST_SERVER=ords +ORDSVERSION=latest + +OCIR=[container registry] +OCIRPATH=$(REST_SERVER)-dboper:$(ORDSVERSION) + +#examples: +#OCIR=lin.ocir.io +#OCIRPATH=/sampletenancy/samplepath/sampledir/$(REST_SERVER)-dboper:$(ORDSVERSION) +############################################# +DOCKER=/usr/bin/docker +KUBECTL=/usr/bin/kubectl +ORDS=/usr/local/bin/ords +CONFIG=/etc/ords/config +IMAGE=oracle/$(REST_SERVER)-dboper:$(ORDSVERSION) +DBOPERATOR=oracle-database-operator.yaml +URLPATH=/_/db-api/stable/database/pdbs/ +OPENSSL=/usr/bin/openssl +ORDSPORT=8888 +MAKE=/usr/bin/make +DOCKERFILE=../../../ords/Dockerfile +RUNSCRIPT=../../../ords/runOrdsSSL.sh +RM=/usr/bin/rm +CP=/bin/cp +ECHO=/usr/bin/echo +CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml +CDB_SECRET_YAML=cdb_secret.yaml +PDB_SECRET_YAML=pdb_secret.yaml +TDE_SECRET_YAML=tde_secret.yaml +CDB_NAMESPACE_YAML=ns_namespace_cdb.yaml +PDB_NAMESPACE_YAML=ns_namespace_pdb.yaml +OPR_NAMESPACE=oracle-database-operator-system +PDB_NAMESPACE=$(shell grep namespace $(PDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') +CDB_NAMESPACE=$(shell grep namespace $(CDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') +CDB=cdb_create.yaml +PDB=pdb_create.yaml +SKEY=tls.key +SCRT=tls.crt +CART=ca.crt +COMPANY=oracle +LOCALHOST=localhost +RESTPREFIX=cdb-dev + + +step1: createimage +step2: tagimage +step3: push +step4: certmanager +step5: dboperator +step6: tlscert +step7: tlssecret +step8: dbsecret +step9: cdb +step10: pdb + +checkstep9: checkcdb + + +createimage: + @echo "BUILDING CDB IMAGES" + $(CP) $(DOCKERFILE) . + $(CP) $(RUNSCRIPT) . + $(DOCKER) build -t $(IMAGE) . + +tagimage: + @echo "TAG IMAGE" + $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) + +push: + @echo "PUSH IMAGE INTO THE REGISTRY" + $(DOCKER) push $(OCIR)$(OCIRPATH) + +certmanager: + @echo "WEBHOOK CERT MANAGER" + $(KUBECTL) apply -f $(CERTMANAGER) + +dboperator: + @echo "ORACLE DATABASE OPERATOR" + $(KUBECTL) apply -f $(DBOPERATOR) + +namespace: + $(KUBECTL) get namespaces + $(KUBECTL) apply -f $(CDB_NAMESPACE_YAML) + $(KUBECTL) apply -f $(PDB_NAMESPACE_YAML) + $(KUBECTL) get namespaces + + +tlscert: + @echo "CREATING TLS CERTIFICATES" + $(OPENSSL) genrsa -out ca.key 2048 + $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt + $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST)" -out server.csr + $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE),DNS:www.example.com" > extfile.txt + $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) + + +tlssecret: + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDB_NAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(CDB_NAMESPACE) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDB_NAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDB_NAMESPACE) + $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPR_NAMESPACE) + $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPR_NAMESPACE) + + +dbsecret: + @echo "CREATING DB SECRETS" + $(KUBECTL) apply -f $(CDB_SECRET_YAML) + $(KUBECTL) apply -f $(PDB_SECRET_YAML) + $(KUBECTL) apply -f $(TDE_SECRET_YAML) + + +cdb: + @echo "CREATING REST SRV POD" + $(KUBECTL) apply -f $(CDB) + +checkcdb: + $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) + +pdb: + $(KUBECTL) apply -f $(PDB) + +checkpdb: + $(KUBECTL) get pdbs -n $(OPR_NAMESPACE) + +dump: + @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) + @$(eval DIAGFILE := ./opdmp.$(TMPSP)) + @>$(DIAGFILE) + @echo "OPERATOR DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) + $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) + @echo "CDB LOG DUMP" >> $(DIAGFILE) + @echo "~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) logs `$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) + @echo "SECRET DMP" >>$(DIAGFILE) + @echo "~~~~~~~~" >> $(DIAGFILE) + $(KUBECTL) get secrets -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) + @echo "CDB/PDB DMP" >> $(DIAGFILE) + $(KUBECTL) get pdbs -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) + $(KUBECTL) get cdb -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) + @echo "CLUSTER INFO" >> $(DIAGFILE) + $(KUBECTL) get nodes -o wide + $(KUBECTL) get svc --namespace=kube-system + +reloadop: + echo "RESTARTING OPERATOR" + $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) + $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) + $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) + $(KUBECTL) get pod $(OP1) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP2) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - + $(KUBECTL) get pod $(OP3) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - + +login: + $(KUBECTL) exec -it `$(KUBECTL) get pods -n $(CDB_NAMESPACE) |grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) bash + +cdblog: + $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) + + + +xlog1: + $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) + +xlog2: + $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) + +xlog3: + $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) + +checkdep: + $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(OPR_NAMESPACE) + $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(CBD_NAMESPACE) + $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(PDB_NAMESPACE) + + + diff --git a/docs/multitenant/usecase03/ns_namespace_cdb.yaml b/docs/multitenant/usecase03/ns_namespace_cdb.yaml new file mode 100644 index 00000000..f4c6d77b --- /dev/null +++ b/docs/multitenant/usecase03/ns_namespace_cdb.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: cdbnamespace + diff --git a/docs/multitenant/usecase03/ns_namespace_pdb.yaml b/docs/multitenant/usecase03/ns_namespace_pdb.yaml new file mode 100644 index 00000000..b22245f9 --- /dev/null +++ b/docs/multitenant/usecase03/ns_namespace_pdb.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: pdbnamespace + diff --git a/docs/multitenant/usecase03/operator_creation_log.txt b/docs/multitenant/usecase03/operator_creation_log.txt new file mode 100644 index 00000000..36ed02ac --- /dev/null +++ b/docs/multitenant/usecase03/operator_creation_log.txt @@ -0,0 +1,27 @@ +kubectl apply -f oracle-database-operator.yaml +namespace/oracle-database-operator-system created +customresourcedefinition.apiextensions.k8s.io/autonomouscontainerdatabases.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/autonomousdatabasebackups.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/autonomousdatabaserestores.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/autonomousdatabases.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/cdbs.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/dataguardbrokers.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/dbcssystems.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/oraclerestdataservices.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/pdbs.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/shardingdatabases.database.oracle.com configured +customresourcedefinition.apiextensions.k8s.io/singleinstancedatabases.database.oracle.com configured +role.rbac.authorization.k8s.io/oracle-database-operator-leader-election-role created +clusterrole.rbac.authorization.k8s.io/oracle-database-operator-manager-role created +clusterrole.rbac.authorization.k8s.io/oracle-database-operator-metrics-reader created +clusterrole.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-role created +rolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-leader-election-rolebinding created +clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-manager-rolebinding created +clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-rolebinding created +service/oracle-database-operator-controller-manager-metrics-service created +service/oracle-database-operator-webhook-service created +certificate.cert-manager.io/oracle-database-operator-serving-cert created +issuer.cert-manager.io/oracle-database-operator-selfsigned-issuer created +mutatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-mutating-webhook-configuration created +validatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-validating-webhook-configuration created +deployment.apps/oracle-database-operator-controller-manager created diff --git a/docs/multitenant/usecase03/pdb_create.yaml b/docs/multitenant/usecase03/pdb_create.yaml new file mode 100644 index 00000000..200f3712 --- /dev/null +++ b/docs/multitenant/usecase03/pdb_create.yaml @@ -0,0 +1,46 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: PDB +metadata: + name: pdb1 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + cdbName: "DB12" + pdbName: "pdbdev" + adminName: + secret: + secretName: "pdb1-secret" + key: "sysadmin_user" + adminPwd: + secret: + secretName: "pdb1-secret" + key: "sysadmin_pwd" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "pdb1-secret" + key: "webserver_user" + webServerPwd: + secret: + secretName: "pdb1-secret" + key: "webserver_pwd" + fileNameConversions: "NONE" + tdeImport: false + totalSize: "1G" + tempSize: "100M" + action: "Create" + diff --git a/docs/multitenant/usecase03/pdb_creation_log.txt b/docs/multitenant/usecase03/pdb_creation_log.txt new file mode 100644 index 00000000..71d0eb4f --- /dev/null +++ b/docs/multitenant/usecase03/pdb_creation_log.txt @@ -0,0 +1,6 @@ +kubectl apply -f pdb_create.yaml +pdb.database.oracle.com/pdb1 created + +kubectl get pdbs -n pdbnamespace +NAME CONNECT_STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE +pdb1 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) DB12 pdbdev READ WRITE 0.78G Ready Success diff --git a/docs/multitenant/usecase03/pdb_secret.yaml b/docs/multitenant/usecase03/pdb_secret.yaml new file mode 100644 index 00000000..f1dfdac6 --- /dev/null +++ b/docs/multitenant/usecase03/pdb_secret.yaml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: v1 +kind: Secret +metadata: + name: pdb1-secret + namespace: pdbnamespace +type: Opaque +data: + sysadmin_user: "[...base64 encoded password...]" + sysadmin_pwd: "[...base64 encoded password...]" + webserver_user: "[...base64 encoded password...]" + webserver_pwd: "[...base64 encoded password...]" + diff --git a/docs/multitenant/usecase03/runOrdsSSL.sh b/docs/multitenant/usecase03/runOrdsSSL.sh new file mode 100644 index 00000000..35f1b77b --- /dev/null +++ b/docs/multitenant/usecase03/runOrdsSSL.sh @@ -0,0 +1,190 @@ +#!/bin/bash + +cat <$TNSNAME + + +function SetParameter() { + ##ords config info <--- Use this command to get the list + +[[ ! -z "${ORACLE_HOST}" && -z "${DBTNSURL}" ]] && { + $ORDS --config ${CONFIG} config set db.hostname ${ORACLE_HOST:-racnode1} + $ORDS --config ${CONFIG} config set db.port ${ORACLE_PORT:-1521} + $ORDS --config ${CONFIG} config set db.servicename ${ORACLE_SERVICE:-TESTORDS} +} + +[[ -z "${ORACLE_HOST}" && ! -z "${DBTNSURL}" ]] && { + #$ORDS --config ${CONFIG} config set db.tnsAliasName ${TNSALIAS} + #$ORDS --config ${CONFIG} config set db.tnsDirectory ${TNS_ADMIN} + #$ORDS --config ${CONFIG} config set db.connectionType tns + + $ORDS --config ${CONFIG} config set db.connectionType customurl + $ORDS --config ${CONFIG} config set db.customURL jdbc:oracle:thin:@${DBTNSURL} +} + + $ORDS --config ${CONFIG} config set security.requestValidationFunction false + $ORDS --config ${CONFIG} config set jdbc.MaxLimit 100 + $ORDS --config ${CONFIG} config set jdbc.InitialLimit 50 + $ORDS --config ${CONFIG} config set error.externalPath ${ERRORFOLDER} + $ORDS --config ${CONFIG} config set standalone.access.log /home/oracle + $ORDS --config ${CONFIG} config set standalone.https.port 8888 + $ORDS --config ${CONFIG} config set standalone.https.cert ${CERTIFICATE} + $ORDS --config ${CONFIG} config set standalone.https.cert.key ${KEY} + $ORDS --config ${CONFIG} config set restEnabledSql.active true + $ORDS --config ${CONFIG} config set security.verifySSL true + $ORDS --config ${CONFIG} config set database.api.enabled true + $ORDS --config ${CONFIG} config set plsql.gateway.mode disabled + $ORDS --config ${CONFIG} config set database.api.management.services.disabled false + $ORDS --config ${CONFIG} config set misc.pagination.maxRows 1000 + $ORDS --config ${CONFIG} config set db.cdb.adminUser "${CDBADMIN_USER:-C##DBAPI_CDB_ADMIN} AS SYSDBA" + $ORDS --config ${CONFIG} config secret --password-stdin db.cdb.adminUser.password << EOF +${CDBADMIN_PWD:-PROVIDE_A_PASSWORD} +EOF + +$ORDS --config ${CONFIG} config user add --password-stdin ${WEBSERVER_USER:-ordspdbadmin} "SQL Administrator, System Administrator" <${CKF} 2>&1 +echo "checkfile" >> ${CKF} +NOT_INSTALLED=`cat ${CKF} | grep "INFO: The" |wc -l ` +echo NOT_INSTALLED=$NOT_INSTALLED + + +function StartUp () { + $ORDS --config $CONFIG serve --port 8888 --secure +} + +# Check whether ords is already setup +if [ $NOT_INSTALLED -ne 0 ] +then + echo " SETUP " + setupOrds; + StartUp; +fi + +if [ $NOT_INSTALLED -eq 0 ] +then + echo " STARTUP " + StartUp; +fi + + diff --git a/docs/sharding/README.md b/docs/sharding/README.md index 0c817467..661e2546 100644 --- a/docs/sharding/README.md +++ b/docs/sharding/README.md @@ -1,26 +1,26 @@ -# Using Oracle Sharding with Oracle Database Operator for Kubernetes +# Using Oracle Globally Distributed Database with Oracle Database Operator for Kubernetes -Oracle Sharding distributes segments of a data set across many databases (shards) on different computers, either on-premises or in cloud. Sharding enables globally distributed, linearly scalable, multimodel databases. It requires no specialized hardware or software. Oracle Sharding does all this while rendering the strong consistency, full power of SQL, support for structured and unstructured data, and the Oracle Database ecosystem. It meets data sovereignty requirements, and supports applications that require low latency and high availability. +Oracle Globally Distributed Database distributes segments of a data set across many databases (shards) on different computers, either on-premises or in cloud. This feature enables globally distributed, linearly scalable, multimodel databases. It requires no specialized hardware or software. Oracle Globally Distributed Database does all this while rendering the strong consistency, full power of SQL, support for structured and unstructured data, and the Oracle Database ecosystem. It meets data sovereignty requirements, and supports applications that require low latency and high availability. -All of the shards together make up a single logical database, which is referred to as a sharded database (SDB). +All of the shards together make up a single logical database, which is referred to as a Oracle Globally Distributed Database (GDD). -Kubernetes provides infrastructure building blocks, such as compute, storage, and networks. Kubernetes makes the infrastructure available as code. It enables rapid provisioning of multi-node topolgies. Additionally, Kubernetes also provides statefulsets, which are the workload API objects that are used to manage stateful applications. This provides us lifecycle management elasticity for databases as a stateful application for various database topologies, such as sharded databases, Oracle Real Application Clusters (Oracle RAC), single instance Oracle Database, and other Oracle features and configurations. +Kubernetes provides infrastructure building blocks, such as compute, storage, and networks. Kubernetes makes the infrastructure available as code. It enables rapid provisioning of multi-node topolgies. Additionally, Kubernetes also provides statefulsets, which are the workload API objects that are used to manage stateful applications. This provides us lifecycle management elasticity for databases as a stateful application for various database topologies, such as Oracle Globally Distributed Database, Oracle Real Application Clusters (Oracle RAC), single instance Oracle Database, and other Oracle features and configurations. -The Sharding Database controller in Oracle Database Operator deploys Oracle Sharding topology as a statefulset in the Kubernetes clusters, using Oracle Database and Global Data Services Docker images. The Oracle Sharding database controller manages the typical lifecycle of Oracle Sharding topology in the Kubernetes cluster, as shown below: +The Sharding Database controller in Oracle Database Operator deploys Oracle Globally Distributed Database Topology as a statefulset in the Kubernetes clusters, using Oracle Database and Global Data Services Docker images. The Oracle Sharding database controller manages the typical lifecycle of Oracle Globally Distributed Database topology in the Kubernetes cluster, as shown below: * Create primary statefulsets shards * Create master and standby Global Data Services statefulsets * Create persistent storage, along with statefulset * Create services * Create load balancer service -* Provision sharding topology by creating and configuring the following: +* Provision Oracle Globally Distributed Database topology by creating and configuring the following: * Catalog database * Shard Databases * GSMs * Shard scale up and scale down * Shard topology cleanup -The Oracle Sharding database controller provides end-to-end automation of Oracle Database sharding topology deployment in Kubernetes clusters. +The Oracle Sharding database controller provides end-to-end automation of Oracle Globally Distributed Database topology deployment in Kubernetes clusters. ## Using Oracle Database Operator Sharding Controller @@ -72,8 +72,9 @@ Choose one of the following deployment options: **Use Oracle-Supplied Docker Images:** The Oracle Sharding Database controller uses Oracle Global Data Services and Oracle Database images to provision the sharding topology. - You can also download the pre-built Oracle Global Data Services `container-registry.oracle.com/database/gsm:latest` and Oracle Database images `container-registry.oracle.com/database/enterprise:latest` from [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::). These images are functionally tested and evaluated with various use cases of sharding topology by deploying on OKE and OLCNE. - + You can also download the pre-built Oracle Global Data Services `container-registry.oracle.com/database/gsm:latest` and Oracle Database images `container-registry.oracle.com/database/enterprise:latest` from [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::). These images are functionally tested and evaluated with various use cases of Oracle Globally Distributed Database topology by deploying on OKE and OLCNE. + + **Note:** You will need to accept Agreement from container-registry.orcale.com to be able to pull the pre-built container images. **OR** @@ -82,17 +83,19 @@ Choose one of the following deployment options: * [Oracle Global Data Services Image](https://github.com/oracle/db-sharding/tree/master/docker-based-sharding-deployment/dockerfiles) * [Oracle Database Image](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance) -After the images are ready, push them to your Docker Images Repository, so that you can pull them during Oracle Database Sharding topology provisioning. +After the images are ready, push them to your Docker Images Repository, so that you can pull them during Oracle Globally Distributed Database topology provisioning. You can either download the images and push them to your Docker Images Repository, or, if your Kubernetes cluster can reach OCR, you can download these images directly from OCR. -**Note**: In the sharding example yaml files, we are using GDS and database images available on [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::). +**Note**: In the Oracle Globally Distributed Database Topology example yaml files, we are using GDS and database images available on [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::). **Note:** In case you want to use the `Oracle Database 23ai Free` Image for Database and GSM, refer to section [Oracle Database 23ai Free](#oracle-database-23ai-free) for more details. ### 4. Create a namespace for the Oracle DB Sharding Setup - Create a Kubernetes namespace named `shns`. All the resources belonging to the Oracle Database Sharding Setup will be provisioned in this namespace named `shns`. For example: +### 4. Create a namespace for the Oracle Globally Distributed Database Setup + + Create a Kubernetes namespace named `shns`. All the resources belonging to the Oracle Globally Distributed Database Topology Setup will be provisioned in this namespace named `shns`. For example: ```sh #### Create the namespace @@ -102,7 +105,7 @@ You can either download the images and push them to your Docker Images Repositor kubectl get ns ``` -### 5. Create a Kubernetes secret for the database installation owner for the database Sharding Deployment +### 5. Create a Kubernetes secret for the database installation owner for the Oracle Globally Distributed Database Topology Deployment Create a Kubernetes secret named `db-user-pass-rsa` using these steps: [Create Kubernetes Secret](./provisioning/create_kubernetes_secret_for_db_user.md) @@ -179,10 +182,84 @@ In this example, the deployment uses the YAML file based on `OCI OKE` cluster. T [7. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT replication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md) [8. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT reolication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md) -## Connecting to Shard Databases +You can refer [here](./provisioning/provisioning_persistent_volume_having_db_gold_image.md) for the steps involved. + +**NOTE:** Provisioning the Oracle Globally Distributed Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. So, this step will not be needed if you are deploying Oracle Globally Distributed Database using Oracle 23ai Free Database and GSM Images. + +## Oracle Database 23ai Free + +Please refer to [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) documentation for more details. + +If you want to use Oracle Database 23ai Free Image for Database and GSM for deployment of the Oracle Globally Distributed Database using Sharding Controller in Oracle Database Kubernetes Operator, you need to consider the below points: + +* To deploy using the FREE Database and GSM Image, you will need to add the additional parameter `dbEdition: "free"` to the .yaml file. +* Refer to [Sample Oracle Globally Distributed Database Deployment using Oracle 23ai FREE Database and GSM Images](./provisioning/free/sharding_provisioning_with_free_images.md) for an example. +* For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. +* Provisioning the Oracle Globally Distributed Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. +* Total number of chunks for FREE Database defaults to `12` if `CATALOG_CHUNKS` parameter is not specified. This default value is determined considering limitation of 12 GB of user data on disk for oracle free database. + + +## Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding in a Cloud-Based Kubernetes Cluster + +Deploy Oracle Globally Distributed Database Topology with `System-Managed Sharding` on your Cloud based Kubernetes cluster. + +In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Globally Distributed Database Topology covered by below examples: -After the Oracle Database Sharding Topology has been provisioned using the Sharding Controller in Oracle Database Kubernetes Operator, you can follow the steps in this document to connect to the Sharded Database or to the individual Shards: [Database Connectivity](./provisioning/database_connection.md) +[1. Provisioning Oracle Globally Distributed Database with System-Managed Sharding without Database Gold Image](./provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md) +[2. Provisioning Oracle Globally Distributed Database with System-Managed Sharding with number of chunks specified](./provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md) +[3. Provisioning Oracle Globally Distributed Database with System-Managed Sharding with additional control on resources like Memory and CPU allocated to Pods](./provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md) +[4. Provisioning Oracle Globally Distributed Database with System-Managed Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) +[5. Provisioning Oracle Globally Distributed Database with System-Managed Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) +[6. Provisioning Oracle Globally Distributed Database with System-Managed Sharding and send Notification using OCI Notification Service](./provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md) +[7. Scale Out - Add Shards to an existing Oracle Globally Distributed Database provisioned earlier with System-Managed Sharding](./provisioning/system_sharding/ssharding_scale_out_add_shards.md) +[8. Scale In - Delete an existing Shard from a working Oracle Globally Distributed Database provisioned earlier with System-Managed Sharding](./provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md) + + +## Provisioning Oracle Globally Distributed Database Topology with User-Defined Sharding in a Cloud-Based Kubernetes Cluster + +Deploy Oracle Globally Distributed Database Topology with `User-Defined Sharding` on your Cloud based Kubernetes cluster. + +In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Globally Distributed Database Topology covered by below examples: + +[1. Provisioning Oracle Globally Distributed Database with User-Defined Sharding without Database Gold Image](./provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md) +[2. Provisioning Oracle Globally Distributed Database with User-Defined Sharding with additional control on resources like Memory and CPU allocated to Pods](./provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md) +[3. Provisioning Oracle Globally Distributed Database with User-Defined Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) +[4. Provisioning Oracle Globally Distributed Database with User-Defined Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) +[5. Provisioning Oracle Globally Distributed Database with User-Defined Sharding and send Notification using OCI Notification Service](./provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md) +[6. Scale Out - Add Shards to an existing Oracle Globally Distributed Database provisioned earlier with User-Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md) +[7. Scale In - Delete an existing Shard from a working Oracle Globally Distributed Database provisioned earlier with User-Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md) + + +## Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled in a Cloud-Based Kubernetes Cluster + +Deploy Oracle Globally Distributed Database Topology with `System-Managed Sharding` and with `RAFT Replication` enabled on your Cloud based Kubernetes cluster. + +**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** + +In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Globally Distributed Database Topology covered by below examples: + +[1. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled without Database Gold Image](./provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md) +[2. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled with number of chunks specified](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md) +[3. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled with additional control on resources like Memory and CPU allocated to Pods](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md) +[4. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) +[5. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) +[6. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled send Notification using OCI Notification Service](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md) +[7. Scale Out - Add Shards to an existing Oracle Globally Distributed Database provisioned earlier with System-Managed Sharding and RAFT replication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md) +[8. Scale In - Delete an existing Shard from a working Oracle Globally Distributed Database provisioned earlier with System-Managed Sharding and RAFT reolication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md) + +## Connecting to Oracle Globally Distributed Database + +After the Oracle Globally Distributed Database Topology has been provisioned using the Sharding Controller in Oracle Database Kubernetes Operator, you can follow the steps in this document to connect to the Oracle Globally Distributed Database or to the individual Shards: [Database Connectivity](./provisioning/database_connection.md) ## Debugging and Troubleshooting -To debug the Oracle Database Sharding Topology provisioned using the Sharding Controller of Oracle Database Kubernetes Operator, follow this document: [Debugging and troubleshooting](./provisioning/debugging.md) +To debug the Oracle Globally Distributed Database Topology provisioned using the Sharding Controller of Oracle Database Kubernetes Operator, follow this document: [Debugging and troubleshooting](./provisioning/debugging.md) + +## Known Issues + +* For both ENTERPRISE and FREE Images, if the Oracle Global Service Manager (GSM) POD is stopped using `crictl stopp` at the worker node level, it leaves GSM in failed state. The `gdsctl` commands fail with error **GSM-45034: Connection to GDS catalog is not established**. This is because with change, the network namespace is lost when checked from the GSM Pod. +* For both ENTERPRISE and FREE Images, restart of the node running CATALOG using `/sbin/reboot -f` results in **GSM-45076: GSM IS NOT RUNNING**. After you encounter this issue, wait until the `gdsctl` commands start working as the database connection start working. When the stack comes up again after the node restart, you can encounter an unexpected restart of the GSM Pod. +* For both ENTERPRISE and FREE Images, if the CATALOG Database Pod is stopped from the worker node using the command `crictl stopp`, then it can leave the CATALOG in an error state. This error state results in GSM reporting the error message **GSM-45034: Connection to GDS catalog is not established.** +* For both ENTERPRISE and FREE Images, either restart of node running the SHARD Pod using `/sbin/reboot -f` or stopping the Shard Database Pod from the worker node using `crictl stopp` command can leave the shard in an error state. +* For both ENTERPRISE and FREE Images, after force restarts of the node running GSM Pod, the GSM pod restarts multiple times, and then becomes stable. The GSM pod restarts itself because when the worker node comes up, the GSM pod is recreated, but does not obtain DB connection to the Catalog. The Liveness Probe fails which restarts the Pod. Be aware of this issue, and permit the GSM pod to become stable. +* **DDL Propagation from Catalog to Shards:** DDL Propagation from the Catalog Database to the Shard Databases can take several minutes to complete. To see faster propagation of DDLs such as the tablespace set from the Catalog Database to the Shard Databases, Oracle recommends that you set smaller chunk values by using the `CATALOG_CHUNKS` attribute in the .yaml file while creating the Sharded Database Topology. \ No newline at end of file diff --git a/docs/sharding/provisioning/database_connection.md b/docs/sharding/provisioning/database_connection.md index 735cc2d5..58a54930 100644 --- a/docs/sharding/provisioning/database_connection.md +++ b/docs/sharding/provisioning/database_connection.md @@ -40,5 +40,5 @@ After you have the external IP address, you can use the services shown below to 1. **Direct connection to the CATALOG Database**: Connect to the service `catalogpdb` on catalog container external IP `xx.xx.xx.116` on port `1521` 2. **Direct connection to the shard Database SHARD1**: Connect to the service `shard1pdb` on catalog container external IP `xx.xx.xx.187` on port `1521` 3. **Direct connection to the shard Database SHARD2**: Connect to the service `shard2pdb` on catalog container external IP `xx.xx.xx.197` on port `1521` -4. **Connection to SHARDED Database for DML activity (INSERT/UPDATE/DELETE)**: Connect to the service `oltp_rw_svc.catalog.oradbcloud` either on primary gsm GSM1 container external IP `xx.xx.xx.38` on port `1522` **or** on standby gsm GSM2 container external IP `xx.xx.xx.66` on port `1522` +4. **Connection to Oracle Globally Distributed Database for DML activity (INSERT/UPDATE/DELETE)**: Connect to the service `oltp_rw_svc.catalog.oradbcloud` either on primary gsm GSM1 container external IP `xx.xx.xx.38` on port `1522` **or** on standby gsm GSM2 container external IP `xx.xx.xx.66` on port `1522` 5. **Connection to the catalog database for DDL activity**: Connect to the service `GDS$CATALOG.oradbcloud` on catalog container external IP `xx.xx.xx.116` on port `1521` diff --git a/docs/sharding/provisioning/debugging.md b/docs/sharding/provisioning/debugging.md index 8922bc1d..372f104d 100644 --- a/docs/sharding/provisioning/debugging.md +++ b/docs/sharding/provisioning/debugging.md @@ -24,7 +24,7 @@ kubectl describe pod/catalog-0 -n shns If the failure is related to the Cloud Infrastructure, then troubleshoot the infrastructure using the documentation from the Cloud infrastructure provider. -## Failure in the provisioning of the Sharded Database +## Failure in the provisioning of the Oracle Globally Distributed Database If the failure occures after the Kubernetes Pods are created but during the execution of the scripts to create the shard databases, catalog database or the GSM, then you must troubleshoot that failure at the individual Pod level. diff --git a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml index ec95eab5..40ad600a 100644 --- a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml +++ b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml @@ -65,4 +65,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/provisioning_persistent_volume_having_db_gold_image.md b/docs/sharding/provisioning/provisioning_persistent_volume_having_db_gold_image.md index 7d3312a6..0a453c15 100644 --- a/docs/sharding/provisioning/provisioning_persistent_volume_having_db_gold_image.md +++ b/docs/sharding/provisioning/provisioning_persistent_volume_having_db_gold_image.md @@ -2,7 +2,7 @@ In this use case, a Persistent Volume with a Oracle Database Gold Image is created. - This is required when you do not already have a Persistent Volume with a Database Gold Image from which you can clone database to save time while deploying Oracle Sharding topology using Oracle Sharding controller. + This is required when you do not already have a Persistent Volume with a Database Gold Image from which you can clone database to save time while deploying Oracle Globally Distributed Database topology using Oracle Sharding controller. This example uses file `oraclesi.yaml` to provision a single instance Oracle Database: diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml index e6b35b8b..aabd8470 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml @@ -54,4 +54,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml index 1f986529..8f17331e 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml @@ -79,4 +79,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml index 9d0439c4..d0c1c6e0 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml @@ -87,4 +87,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml index ac22e5fe..0859b089 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml @@ -65,4 +65,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml index c3761f64..123b3ae1 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml @@ -64,4 +64,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml index 7137d912..0cfccf9a 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml @@ -85,4 +85,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml index 54039d79..345e9c09 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml @@ -81,4 +81,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md index 64e2f4eb..e457b7eb 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -6,7 +6,7 @@ In this test case, you provision the Oracle Database sharding topology with Syst This use case applies when you want to provision the database Pods on a Kubernetes Node in any availability domain (AD), which can also be different from the availability domain (AD) of the Block Volume that has the Oracle Database Gold Image provisioned earlier. -Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup across ADs. +Choosing this option takes substantially less time during the Oracle Globally Distributed Database Topology setup across ADs. NOTE: diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md index f7aef949..cb01fa0d 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -6,7 +6,7 @@ In this case, the database is created automatically by cloning from an existing This use case applies when you are cloning from a Block Volume, and you can clone _only_ in the same availability domain (AD). The result is that the cloned shard database PODs can be created _only_ in the same AD where the Gold Image Block Volume is present. -Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup. +Choosing this option takes substantially less time during the Oracle Globally Distributed Database Topology setup. **NOTE** For this step, the Persistent Volume that has the Oracle Database Gold Image is identified using its OCID. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml index 8fbe5438..5adbd2ce 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml @@ -53,4 +53,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml index ecbb50ab..3902ceef 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml @@ -64,4 +64,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml index 670f04fe..a11833e0 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml @@ -63,4 +63,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml index b0058758..e00d2272 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml @@ -65,4 +65,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary \ No newline at end of file + role: primary diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 92af1880..35f42f22 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -651,6 +651,9 @@ The following table depicts the fail over matrix for any destructive operation t #### Database Pod Resource Management When creating a Single Instance Database, you can specify the CPU and memory resources needed by the database pod. These specified resources are passed to the `kube-scheduler` so that the pod is scheduled on one of the pods that has the required resources available. To use database pod resource management, specify values for the `resources` attributes in the [`config/samples/sidb/singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. +#### Database Pod Resource Management +When creating a Single Instance Database you can specify the cpu and memory resources needed by the database pod. These specified resources are passed to the `kube-scheduler` so that the pod gets scheduled on one of the pods that has the required resources available. To use database pod resource management specify values for the `resources` attributes in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. + #### Setup Database with LoadBalancer For the Single Instance Database, the default service is the `NodePort` service. You can enable the `LoadBalancer` service by using the `kubectl patch` command. diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 0cfb8d40..70147329 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -11080,10 +11080,16 @@ spec: type: boolean assertivePdbDeletion: type: boolean + assertivePdbDeletion: + description: turn on the assertive approach to delete pdb resource kubectl delete pdb ..... automatically triggers the pluggable database deletion + type: boolean cdbName: type: string cdbNamespace: type: string + cdbNamespace: + description: CDB Namespace + type: string cdbResName: type: string copyAction: @@ -11460,6 +11466,7 @@ spec: directorName: type: string envVars: + description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. items: properties: name: @@ -11961,6 +11968,12 @@ spec: x-kubernetes-int-or-string: true type: object type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string storageSizeInGb: format: int32 type: integer @@ -13775,6 +13788,26 @@ webhooks: resources: - shardingdatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: mshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -14132,6 +14165,27 @@ webhooks: resources: - shardingdatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v1alpha1-shardingdatabase + failurePolicy: Fail + name: vshardingdatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/ords/Dockerfile b/ords/Dockerfile index 827a115b..25ba08ec 100644 --- a/ords/Dockerfile +++ b/ords/Dockerfile @@ -84,4 +84,3 @@ EXPOSE 8888 # Define default command to start Ords Services CMD $ORDS_HOME/$RUN_FILE - From 5383c3ba0b629bb80e637d644b55dad95ba4e07d Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 21 Mar 2025 09:33:43 +0000 Subject: [PATCH 210/414] lrest readme and makefile correction --- docs/multitenant/lrest-based/README.md | 31 ++++++------- .../usecase/close_pdb3_resource.yaml | 2 +- docs/multitenant/lrest-based/usecase/makefile | 44 +++++++++---------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/docs/multitenant/lrest-based/README.md b/docs/multitenant/lrest-based/README.md index f17abd41..d9b72a9d 100644 --- a/docs/multitenant/lrest-based/README.md +++ b/docs/multitenant/lrest-based/README.md @@ -274,10 +274,11 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 | Name | Dcription | --------------------------|-------------------------------------------------------------------------------| |cdbName | Name of the container database (db) | -|lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** | -|dbTnsurl | TNS alias of the container db | +|lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** use the latest label availble on OCR | +|dbTnsurl | The string of the tns alias to connect to cdb. Attention: remove all white space from string | |deletePdbCascade | Delete all of the PDBs associated to a CDB resource when the CDB resource is dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | |cdbAdminUser | Secret: the administrative (admin) user | +|fileNameConversions | Use file name conversion if you are not using ASM | |cdbAdminPwd | Secret: the admin user password | |webServerUser | Secret: the HTTPS user | |webServerPwd | Secret: the HTTPS user password | @@ -290,10 +291,10 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 ### Create PDB -To create a pluggable database (PDB), apply the yaml file [`create_lrpdb1_resource.yaml`](./usecase/clone_lrpdb1_resource.yaml) +To create a pluggable database, apply the yaml file [`create_pdb1_resource.yaml`](./usecase/create_pdb1_resource.yaml) ```bash -kubectl apply -f create_lrpdb1_resource.yaml +kubectl apply -f create_pdb1_resource.yaml ``` Check the status of the resource and the PDB existence on the container db: @@ -391,10 +392,10 @@ test_invalid_parameter;16;spfile ### Open PDB -To open the PDB, use the file [`open_lrpdb1_resource.yaml`](./usecase/open_lrpdb1_resource.yaml): +To open the PDB, use the file [`open_pdb1_resource.yaml`](./usecase/open_pdb1_resource.yaml): ```bash -kubectl apply -f open_lrpdb1_resource.yaml +kubectl apply -f open_pdb1_resource.yaml ``` **pdb opening** - parameters list @@ -411,10 +412,10 @@ kubectl apply -f open_lrpdb1_resource.yaml ### Close PDB -To close the PDB, use the file [`close_lrpdb1_resource.yaml`](./usecase/close_lrpdb1_resource.yaml): +To close the PDB, use the file [`close_pdb1_resource.yaml`](./usecase/close_pdb1_resource.yaml): ```bash -kubectl apply -f close_lrpdb1_resource.yaml +kubectl apply -f close_pdb1_resource.yaml ``` **pdb closing** - parameters list | Name | Description/Value | @@ -429,10 +430,10 @@ kubectl apply -f close_lrpdb1_resource.yaml ### Clone PDB ### -To clone the PDB, use the file [`clone_lrpdb1_resource.yaml`](./usecase/clone_lrpdb1_resource.yaml): +To clone the PDB, use the file [`clone_pdb1_resource.yaml`](./usecase/clone_pdb1_resource.yaml): ```bash -kubeclt apply -f clone_lrpdb1_resource.yaml +kubeclt apply -f clone_pdb1_resource.yaml ``` **pdb cloning** - parameters list | Name | Description/Value | @@ -450,7 +451,7 @@ kubeclt apply -f clone_lrpdb1_resource.yaml ### Unplug PDB -To unplug the PDB, use the file [`unplug_lrpdb1_resource.yaml`](./usecase/unplug_lrpdb1_resource.yaml): +To unplug the PDB, use the file [`unplug_pdb1_resource.yaml`](./usecase/unplug_pdb1_resource.yaml): **pdb unplugging** | Name | Description/Value | @@ -461,7 +462,7 @@ To unplug the PDB, use the file [`unplug_lrpdb1_resource.yaml`](./usecase/unplug |pdbName | Name of the pluggable database (PDB)| ### Plug PDB -To plug in the PDB, use the file [`plug_lrpdb1_resource.yaml`](./usecase/plug_lrpdb1_resource.yaml). In this example, we plug in the PDB that was unpluged in the previous step: +To plug in the PDB, use the file [`plug_pdb1_resource.yaml`](./usecase/plug_pdb1_resource.yaml). In this example, we plug in the PDB that was unpluged in the previous step: **pdb plugging** | Name | Description/Value | @@ -478,7 +479,7 @@ To plug in the PDB, use the file [`plug_lrpdb1_resource.yaml`](./usecase/plug_lr ### Delete PDB -To delete the PDB, use the file [`delete_lrpdb1_resource.yaml`](./usecase/delete_lrpdb1_resource.yaml) +To delete the PDB, use the file [`delete_pdb1_resource.yaml`](./usecase/delete_pdb1_resource.yaml) **pdb deletion** @@ -493,8 +494,8 @@ To delete the PDB, use the file [`delete_lrpdb1_resource.yaml`](./usecase/delete ### Map PDB -If you need to create a CRD for an existing PDB, then you can use the map option by applying the file [`map_lrpdb1_resource.yaml`](./usecase/map_lrpdb1_resource.yaml) - +If you need to create a CRD for an existing PDB, then you can use the map option by applying the file [`map_pdb1_resource.yaml`](./usecase/map_pdb1_resource.yaml) +Map functionality can be used in a situation where you have a pdb which is not registered in the operator as a CRD. It's a temporary solution while waiting the autodiscovery to be available. diff --git a/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml b/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml index 6c6ca519..6ba34a7b 100644 --- a/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml +++ b/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml @@ -9,7 +9,7 @@ spec: cdbResName: "cdb-dev" cdbNamespace: "cdbnamespace" cdbName: "DB12" - pdbName: ""new_clone" + pdbName: "new_clone" pdbState: "CLOSE" modifyOption: "IMMEDIATE" action: "Modify" diff --git a/docs/multitenant/lrest-based/usecase/makefile b/docs/multitenant/lrest-based/usecase/makefile index 4203baa4..1de320ad 100644 --- a/docs/multitenant/lrest-based/usecase/makefile +++ b/docs/multitenant/lrest-based/usecase/makefile @@ -31,27 +31,27 @@ # # ----------------------------- ---------------------------------- # oracle-database-operator.yaml : oracle database operator -# cdbnamespace_binding.yaml : role binding for cdbnamespace +# lrestnamespace_binding.yaml : role binding for lrestnamespace # pdbnamespace_binding.yaml : role binding for pdbnamespace # create_lrest_secret.yaml : create secrets for rest server pod # create_lrpdb_secret.yaml : create secrets for pluggable database # create_lrest_pod.yaml : create rest server pod -# create_pdb1_resource.yaml : create first pluggable database -# create_pdb2_resource.yaml : create second pluggable database -# open_pdb1_resource.yaml : open first pluggable database -# open_pdb2_resource.yaml : open second pluggable database -# close_pdb1_resource.yaml : close first pluggable database -# close_pdb2_resource.yaml : close second pluggable database +# create_pdb1_resource.yaml : create first pluggable database +# create_pdb2_resource.yaml : create second pluggable database +# open_pdb1_resource.yaml : open first pluggable database +# open_pdb2_resource.yaml : open second pluggable database +# close_pdb1_resource.yaml : close first pluggable database +# close_pdb2_resource.yaml : close second pluggable database # clone_lrpdb_resource.yaml : clone thrid pluggable database -# clone_pdb2_resource.yaml : clone 4th pluggable database -# delete_pdb1_resource.yaml : delete first pluggable database -# delete_pdb2_resource.yaml : delete sencond pluggable database -# delete_pdb3_resource.yaml : delete thrid pluggable database -# unplug_pdb1_resource.yaml : unplug first pluggable database -# plug_pdb1_resource.yaml : plug first pluggable database -# map_pdb1_resource.yaml : map the first pluggable database +# clone_pdb2_resource.yaml : clone 4th pluggable database +# delete_pdb1_resource.yaml : delete first pluggable database +# delete_pdb2_resource.yaml : delete sencond pluggable database +# delete_pdb3_resource.yaml : delete thrid pluggable database +# unplug_pdb1_resource.yaml : unplug first pluggable database +# plug_pdb1_resource.yaml : plug first pluggable database +# map_pdb1_resource.yaml : map the first pluggable database # config_map.yam : pdb parameters array -# altersystem_pdb1_resource.yaml : chage cpu_count count parameter for the first pdb +# altersystem_pdb1_resource.yaml : chage cpu_count count parameter for the first pdb # DATE := `date "+%y%m%d%H%M%S"` ###################### @@ -328,7 +328,7 @@ apiVersion: database.oracle.com/${APIVERSION} kind: LREST metadata: name: cdb-dev - namespace: cdbnamespace + namespace: ${LRSNAMESPACE} spec: cdbName: "DB12" lrestImage: ${LRESTIMG} @@ -524,7 +524,7 @@ spec: cdbResName: "cdb-dev" cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" - pdbName: ""new_clone" + pdbName: "new_clone" pdbState: "CLOSE" modifyOption: "IMMEDIATE" action: "Modify" @@ -619,7 +619,7 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" + xmlFileName: "/var/tmp/pdb.$$.xml" action: "Unplug" EOF @@ -636,7 +636,7 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" + xmlFileName: "/var/tmp/pdb.$$.xml" action: "plug" fileNameConversions: "NONE" sourceFileNameConversions: "NONE" @@ -901,10 +901,10 @@ run07.1: $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) run99.1: - $(KUBECTL) delete lrest cdb-dev -n cdbnamespace + $(KUBECTL) delete lrest cdb-dev -n $(LRSNAMESPACE) $(KUBECTL) wait --for=delete lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) get lrest -n cdbnamespaace - $(KUBECTL) get lrpdb -n pdbnamespaace + $(KUBECTL) get lrest -n $(LRSNAMESPACE) + $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) runall01: run00 run01.1 run01.2 run02.1 run02.2 run03.1 run03.2 run04.1 run04.2 run05.1 run06.1 run07.1 From bc1fa1e3f27dd091bc9b39dea6ec4c8692893946 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Tue, 25 Mar 2025 16:59:17 +0000 Subject: [PATCH 211/414] fix master for permissions issues --- main.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/main.go b/main.go index ee9992b7..1b3db6d2 100644 --- a/main.go +++ b/main.go @@ -312,6 +312,18 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") os.Exit(1) } + if err = (&databasev4.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") + os.Exit(1) + } + if err = (&databasev4.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") + os.Exit(1) + } + if err = (&databasev4.OracleRestDataService{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") + os.Exit(1) + } } // PDB Reconciler @@ -394,19 +406,6 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "DatabaseObserver") os.Exit(1) } - - if err = (&databasev4.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") - os.Exit(1) - } - if err = (&databasev4.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") - os.Exit(1) - } - if err = (&databasev4.OracleRestDataService{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") - os.Exit(1) - } // +kubebuilder:scaffold:builder // Add index for PDB CR to enable mgr to cache PDBs From 36611bc5323f2d6761c55f21754c8c4e9bed4c20 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Thu, 27 Mar 2025 11:02:51 +0000 Subject: [PATCH 212/414] ords multitenant readme correction --- docs/multitenant/multitenant/usecase02/README.md | 0 docs/multitenant/ords-based/README.md | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 docs/multitenant/multitenant/usecase02/README.md diff --git a/docs/multitenant/multitenant/usecase02/README.md b/docs/multitenant/multitenant/usecase02/README.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/multitenant/ords-based/README.md b/docs/multitenant/ords-based/README.md index edfd0208..992748bd 100644 --- a/docs/multitenant/ords-based/README.md +++ b/docs/multitenant/ords-based/README.md @@ -11,7 +11,7 @@ By using CDB/PDB controllers, you can perform the following actions **CREATE**, Examples are located under the following directories: -- the directories [`Usecase`](./usecase/) and [`usecase01`](./usecase01/) contain a [configuration file](./usecase/parameters.txt) where you can specify all the details of your environment. A [`makefile`](./usecase/makefile) takes this file as input to generate all of the `yaml` files. There is no need to edit `yaml` files one by one. +- the directories [`usecase`](./usecase/) and [`usecase01`](./usecase01/) contain a [configuration file](./usecase/parameters.txt) where you can specify all the details of your environment. A [`makefile`](./usecase/makefile) takes this file as input to generate all of the `yaml` files. There is no need to edit `yaml` files one by one. - [Singlenamespace provisioning](./provisioning/singlenamespace/) This file contains base example files that you can use to manage the PDB and CDB within a single namespace. - [Multinamespace provisioning](./provisioning/multinamespace/) This file contains base example files that you can use to manage the PDB and CDB in different namespaces. - [Usecase01](./usecase01/README.md) [Usecase02](./usecase02/README.md) This file contains other step-by-step examples; @@ -263,9 +263,9 @@ Example of `yaml` file Secret section: The Oracle Database Operator Multitenant Controller creates the CDB as a custom resource object kind that models a target CDB as a native Kubernetes object. This object kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. Each CDB resource follows the CDB CRD as defined here: [`config/crd/bases/database.oracle.com_cdbs.yaml`](../../../config/crd/bases/database.oracle.com_cdbs.yaml) -To create a CDB CRD, use this example`.yaml` file: [`cdb_create.yaml`](../multitenant/provisioning/singlenamespace/cdb_create.yaml) +To create a CDB CRD, use this example`.yaml` file: [`cdb_create.yaml`](./provisioning/singlenamespace/cdb_create.yaml) -**Note:** The password and username fields in this *cdb.yaml* Yaml are the Kubernetes Secrets created earlier in this procedure. For more information, see the section [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). To understand more about creating secrets for pulling images from a Docker private registry, see [Kubernetes Private Registry Documenation]( https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). +**Note:** The password and username fields in this *cdb.yaml* Yaml are the Kubernetes Secrets created earlier in this procedure. For more information, see the section [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). To understand more about creating secrets for pulling images from a Docker private registry, see [Kubernetes Private Registry Documentation]( https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). Create a CDB CRD Resource example @@ -273,7 +273,7 @@ Create a CDB CRD Resource example kubectl apply -f cdb_create.yaml ``` -see [usecase01][uc01] and usecase02[uc02] for more information about file configuration +see [usecase01][uc01] and [usecase02][uc02] for more information about file configuration ## PDB CRD @@ -369,9 +369,9 @@ kubectl apply -f pdb_create.yaml [ordsdoc]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.1/index.html - [uc01]:../multitenant/usecase01/README.md + [uc01]:./usecase01/README.md - [uc02]:../multitenant/usecase02/README.md + [uc02]:./usecase02/README.md [oradocpdb]:https://docs.oracle.com/en/database/oracle/oracle-database/21/multi/introduction-to-the-multitenant-architecture.html#GUID-AB84D6C9-4BBE-4D36-992F-2BB85739329F From c15d88fc281916685d068290ab16213bb47f0cf6 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 28 Mar 2025 16:51:58 +0000 Subject: [PATCH 213/414] cdb pdb place holder v1alpha1 --- apis/database/v1alpha1/cdb_types.go | 190 ++++++ apis/database/v1alpha1/pdb_types.go | 236 +++++++ .../v1alpha1/zz_generated.deepcopy.go | 602 +++++++++++++++++ .../crd/bases/database.oracle.com_cdbs.yaml | 238 +++++++ .../database.oracle.com_dataguardbrokers.yaml | 4 +- .../crd/bases/database.oracle.com_pdbs.yaml | 315 ++++++++- ...database.oracle.com_shardingdatabases.yaml | 9 - config/manager/kustomization.yaml | 2 +- config/webhook/manifests.yaml | 41 -- oracle-database-operator.yaml | 623 ++++++++++++++++-- 10 files changed, 2135 insertions(+), 125 deletions(-) create mode 100644 apis/database/v1alpha1/cdb_types.go create mode 100644 apis/database/v1alpha1/pdb_types.go diff --git a/apis/database/v1alpha1/cdb_types.go b/apis/database/v1alpha1/cdb_types.go new file mode 100644 index 00000000..f97df391 --- /dev/null +++ b/apis/database/v1alpha1/cdb_types.go @@ -0,0 +1,190 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// CDBSpec defines the desired state of CDB +type CDBSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Name of the CDB + CDBName string `json:"cdbName,omitempty"` + // Name of the CDB Service + ServiceName string `json:"serviceName,omitempty"` + + // Password for the CDB System Administrator + SysAdminPwd CDBSysAdminPassword `json:"sysAdminPwd,omitempty"` + // User in the root container with sysdba priviledges to manage PDB lifecycle + CDBAdminUser CDBAdminUser `json:"cdbAdminUser,omitempty"` + // Password for the CDB Administrator to manage PDB lifecycle + CDBAdminPwd CDBAdminPassword `json:"cdbAdminPwd,omitempty"` + + CDBTlsKey CDBTLSKEY `json:"cdbTlsKey,omitempty"` + CDBTlsCrt CDBTLSCRT `json:"cdbTlsCrt,omitempty"` + + // Password for user ORDS_PUBLIC_USER + ORDSPwd ORDSPassword `json:"ordsPwd,omitempty"` + // ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. + ORDSPort int `json:"ordsPort,omitempty"` + // ORDS Image Name + ORDSImage string `json:"ordsImage,omitempty"` + // The name of the image pull secret in case of a private docker repository. + ORDSImagePullSecret string `json:"ordsImagePullSecret,omitempty"` + // ORDS Image Pull Policy + // +kubebuilder:validation:Enum=Always;Never + ORDSImagePullPolicy string `json:"ordsImagePullPolicy,omitempty"` + // Number of ORDS Containers to create + Replicas int `json:"replicas,omitempty"` + // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + WebServerUser WebServerUser `json:"webServerUser,omitempty"` + // Password for the Web Server User + WebServerPwd WebServerPassword `json:"webServerPwd,omitempty"` + // Name of the DB server + DBServer string `json:"dbServer,omitempty"` + // DB server port + DBPort int `json:"dbPort,omitempty"` + // Node Selector for running the Pod + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + DeletePDBCascade bool `json:"deletePdbCascade,omitempty"` + DBTnsurl string `json:"dbTnsurl,omitempty"` + CDBPubKey CDBPUBKEY `json:"cdbOrdsPubKey,omitempty"` + CDBPriKey CDBPRIVKEY `json:"cdbOrdsPrvKey,omitempty"` +} + +// CDBSecret defines the secretName +type CDBSecret struct { + SecretName string `json:"secretName"` + Key string `json:"key"` +} + +// CDBSysAdminPassword defines the secret containing SysAdmin Password mapped to key 'sysAdminPwd' for CDB +type CDBSysAdminPassword struct { + Secret CDBSecret `json:"secret"` +} + +// CDBAdminUser defines the secret containing CDB Administrator User mapped to key 'cdbAdminUser' to manage PDB lifecycle +type CDBAdminUser struct { + Secret CDBSecret `json:"secret"` +} + +// CDBAdminPassword defines the secret containing CDB Administrator Password mapped to key 'cdbAdminPwd' to manage PDB lifecycle +type CDBAdminPassword struct { + Secret CDBSecret `json:"secret"` +} + +// ORDSPassword defines the secret containing ORDS_PUBLIC_USER Password mapped to key 'ordsPwd' +type ORDSPassword struct { + Secret CDBSecret `json:"secret"` +} + +// WebServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle +type WebServerUser struct { + Secret CDBSecret `json:"secret"` +} + +// WebServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle +type WebServerPassword struct { + Secret CDBSecret `json:"secret"` +} + +type CDBTLSKEY struct { + Secret CDBSecret `json:"secret"` +} + +type CDBTLSCRT struct { + Secret CDBSecret `json:"secret"` +} + +type CDBPUBKEY struct { + Secret CDBSecret `json:"secret"` +} + +type CDBPRIVKEY struct { + Secret CDBSecret `json:"secret"` +} + +// CDBStatus defines the observed state of CDB +type CDBStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Phase of the CDB Resource + Phase string `json:"phase"` + // CDB Resource Status + Status bool `json:"status"` + // Message + Msg string `json:"msg,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" +// +kubebuilder:printcolumn:JSONPath=".spec.dbServer",name="DB Server",type="string",description=" Name of the DB Server" +// +kubebuilder:printcolumn:JSONPath=".spec.dbPort",name="DB Port",type="integer",description="DB server port" +// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="Replicas",type="integer",description="Replicas" +// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the CDB Resource" +// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" +// +kubebuilder:printcolumn:JSONPath=".spec.dbTnsurl",name="TNS STRING",type="string",description=" string of the tnsalias" +// +kubebuilder:resource:path=cdbs,scope=Namespaced + +// CDB is the Schema for the cdbs API +type CDB struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CDBSpec `json:"spec,omitempty"` + Status CDBStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// CDBList contains a list of CDB +type CDBList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CDB `json:"items"` +} + +func init() { + SchemeBuilder.Register(&CDB{}, &CDBList{}) +} diff --git a/apis/database/v1alpha1/pdb_types.go b/apis/database/v1alpha1/pdb_types.go new file mode 100644 index 00000000..8b966c38 --- /dev/null +++ b/apis/database/v1alpha1/pdb_types.go @@ -0,0 +1,236 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PDBSpec defines the desired state of PDB +type PDBSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + PDBTlsKey PDBTLSKEY `json:"pdbTlsKey,omitempty"` + PDBTlsCrt PDBTLSCRT `json:"pdbTlsCrt,omitempty"` + PDBTlsCat PDBTLSCAT `json:"pdbTlsCat,omitempty"` + + // CDB Namespace + CDBNamespace string `json:"cdbNamespace,omitempty"` + // Name of the CDB Custom Resource that runs the ORDS container + CDBResName string `json:"cdbResName,omitempty"` + // Name of the CDB + CDBName string `json:"cdbName,omitempty"` + // The name of the new PDB. Relevant for both Create and Plug Actions. + PDBName string `json:"pdbName,omitempty"` + // Name of the Source PDB from which to clone + SrcPDBName string `json:"srcPdbName,omitempty"` + // The administrator username for the new PDB. This property is required when the Action property is Create. + AdminName PDBAdminName `json:"adminName,omitempty"` + // The administrator password for the new PDB. This property is required when the Action property is Create. + AdminPwd PDBAdminPassword `json:"adminPwd,omitempty"` + // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints + WebServerUsr WebServerUserPDB `json:"webServerUser,omitempty"` + // Password for the Web ServerPDB User + WebServerPwd WebServerPasswordPDB `json:"webServerPwd,omitempty"` + // Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. + FileNameConversions string `json:"fileNameConversions,omitempty"` + // This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. + SourceFileNameConversions string `json:"sourceFileNameConversions,omitempty"` + // XML metadata filename to be used for Plug or Unplug operations + XMLFileName string `json:"xmlFileName,omitempty"` + // To copy files or not while cloning a PDB + // +kubebuilder:validation:Enum=COPY;NOCOPY;MOVE + CopyAction string `json:"copyAction,omitempty"` + // Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). + // +kubebuilder:validation:Enum=INCLUDING;KEEP + DropAction string `json:"dropAction,omitempty"` + // A Path specified for sparse clone snapshot copy. (Optional) + SparseClonePath string `json:"sparseClonePath,omitempty"` + // Whether to reuse temp file + ReuseTempFile *bool `json:"reuseTempFile,omitempty"` + // Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. + UnlimitedStorage *bool `json:"unlimitedStorage,omitempty"` + // Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. + AsClone *bool `json:"asClone,omitempty"` + // Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. + TotalSize string `json:"totalSize,omitempty"` + // Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. + TempSize string `json:"tempSize,omitempty"` + // TDE import for plug operations + TDEImport *bool `json:"tdeImport,omitempty"` + // TDE export for unplug operations + TDEExport *bool `json:"tdeExport,omitempty"` + // TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations + TDEPassword TDEPwd `json:"tdePassword,omitempty"` + // TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + TDEKeystorePath string `json:"tdeKeystorePath,omitempty"` + // TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + TDESecret TDESecret `json:"tdeSecret,omitempty"` + // Whether you need the script only or execute the script + GetScript *bool `json:"getScript,omitempty"` + // Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR. + // +kubebuilder:validation:Enum=Create;Clone;Plug;Unplug;Delete;Modify;Status;Map + Action string `json:"action"` + // Extra options for opening and closing a PDB + // +kubebuilder:validation:Enum=IMMEDIATE;NORMAL;READ ONLY;READ WRITE;RESTRICTED + ModifyOption string `json:"modifyOption,omitempty"` + // The target state of the PDB + // +kubebuilder:validation:Enum=OPEN;CLOSE + PDBState string `json:"pdbState,omitempty"` + // turn on the assertive approach to delete pdb resource + // kubectl delete pdb ..... automatically triggers the pluggable database + // deletion + AssertivePdbDeletion bool `json:"assertivePdbDeletion,omitempty"` + PDBPubKey PDBPUBKEY `json:"pdbOrdsPubKey,omitempty"` + PDBPriKey PDBPRIVKEY `json:"pdbOrdsPrvKey,omitempty"` +} + +// PDBAdminName defines the secret containing Sys Admin User mapped to key 'adminName' for PDB +type PDBAdminName struct { + Secret PDBSecret `json:"secret"` +} + +// PDBAdminPassword defines the secret containing Sys Admin Password mapped to key 'adminPwd' for PDB +type PDBAdminPassword struct { + Secret PDBSecret `json:"secret"` +} + +// TDEPwd defines the secret containing TDE Wallet Password mapped to key 'tdePassword' for PDB +type TDEPwd struct { + Secret PDBSecret `json:"secret"` +} + +// TDESecret defines the secret containing TDE Secret to key 'tdeSecret' for PDB +type TDESecret struct { + Secret PDBSecret `json:"secret"` +} + +// WebServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle + +type WebServerUserPDB struct { + Secret PDBSecret `json:"secret"` +} + +// WebServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle +type WebServerPasswordPDB struct { + Secret PDBSecret `json:"secret"` +} + +// PDBSecret defines the secretName +type PDBSecret struct { + SecretName string `json:"secretName"` + Key string `json:"key"` +} + +type PDBTLSKEY struct { + Secret PDBSecret `json:"secret"` +} + +type PDBTLSCRT struct { + Secret PDBSecret `json:"secret"` +} + +type PDBTLSCAT struct { + Secret PDBSecret `json:"secret"` +} + +// PDBStatus defines the observed state of PDB +type PDBStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // PDB Connect String + ConnString string `json:"connString,omitempty"` + // Phase of the PDB Resource + Phase string `json:"phase"` + // PDB Resource Status + Status bool `json:"status"` + // Total size of the PDB + TotalSize string `json:"totalSize,omitempty"` + // Open mode of the PDB + OpenMode string `json:"openMode,omitempty"` + // Modify Option of the PDB + ModifyOption string `json:"modifyOption,omitempty"` + // Message + Msg string `json:"msg,omitempty"` + // Last Completed Action + Action string `json:"action,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" +// +kubebuilder:printcolumn:JSONPath=".spec.pdbName",name="PDB Name",type="string",description="Name of the PDB" +// +kubebuilder:printcolumn:JSONPath=".status.openMode",name="PDB State",type="string",description="PDB Open Mode" +// +kubebuilder:printcolumn:JSONPath=".status.totalSize",name="PDB Size",type="string",description="Total Size of the PDB" +// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the PDB Resource" +// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" +// +kubebuilder:printcolumn:JSONPath=".status.connString",name="Connect_String",type="string",description="The connect string to be used" +// +kubebuilder:resource:path=pdbs,scope=Namespaced + +// PDB is the Schema for the pdbs API +type PDB struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PDBSpec `json:"spec,omitempty"` + Status PDBStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// PDBList contains a list of PDB +type PDBList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PDB `json:"items"` +} + +type PDBPUBKEY struct { + Secret PDBSecret `json:"secret"` +} + +type PDBPRIVKEY struct { + Secret PDBSecret `json:"secret"` +} + +func init() { + SchemeBuilder.Register(&PDB{}, &PDBList{}) +} diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index d0426da8..b20cf834 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -683,6 +683,239 @@ func (in *Backupconfig) DeepCopy() *Backupconfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDB) DeepCopyInto(out *CDB) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDB. +func (in *CDB) DeepCopy() *CDB { + if in == nil { + return nil + } + out := new(CDB) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CDB) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBAdminPassword) DeepCopyInto(out *CDBAdminPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminPassword. +func (in *CDBAdminPassword) DeepCopy() *CDBAdminPassword { + if in == nil { + return nil + } + out := new(CDBAdminPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBAdminUser) DeepCopyInto(out *CDBAdminUser) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminUser. +func (in *CDBAdminUser) DeepCopy() *CDBAdminUser { + if in == nil { + return nil + } + out := new(CDBAdminUser) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBList) DeepCopyInto(out *CDBList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CDB, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBList. +func (in *CDBList) DeepCopy() *CDBList { + if in == nil { + return nil + } + out := new(CDBList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CDBList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBPRIVKEY) DeepCopyInto(out *CDBPRIVKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBPRIVKEY. +func (in *CDBPRIVKEY) DeepCopy() *CDBPRIVKEY { + if in == nil { + return nil + } + out := new(CDBPRIVKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBPUBKEY) DeepCopyInto(out *CDBPUBKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBPUBKEY. +func (in *CDBPUBKEY) DeepCopy() *CDBPUBKEY { + if in == nil { + return nil + } + out := new(CDBPUBKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBSecret) DeepCopyInto(out *CDBSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSecret. +func (in *CDBSecret) DeepCopy() *CDBSecret { + if in == nil { + return nil + } + out := new(CDBSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBSpec) DeepCopyInto(out *CDBSpec) { + *out = *in + out.SysAdminPwd = in.SysAdminPwd + out.CDBAdminUser = in.CDBAdminUser + out.CDBAdminPwd = in.CDBAdminPwd + out.CDBTlsKey = in.CDBTlsKey + out.CDBTlsCrt = in.CDBTlsCrt + out.ORDSPwd = in.ORDSPwd + out.WebServerUser = in.WebServerUser + out.WebServerPwd = in.WebServerPwd + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.CDBPubKey = in.CDBPubKey + out.CDBPriKey = in.CDBPriKey +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSpec. +func (in *CDBSpec) DeepCopy() *CDBSpec { + if in == nil { + return nil + } + out := new(CDBSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBStatus) DeepCopyInto(out *CDBStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBStatus. +func (in *CDBStatus) DeepCopy() *CDBStatus { + if in == nil { + return nil + } + out := new(CDBStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBSysAdminPassword) DeepCopyInto(out *CDBSysAdminPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSysAdminPassword. +func (in *CDBSysAdminPassword) DeepCopy() *CDBSysAdminPassword { + if in == nil { + return nil + } + out := new(CDBSysAdminPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBTLSCRT) DeepCopyInto(out *CDBTLSCRT) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSCRT. +func (in *CDBTLSCRT) DeepCopy() *CDBTLSCRT { + if in == nil { + return nil + } + out := new(CDBTLSCRT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CDBTLSKEY) DeepCopyInto(out *CDBTLSKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSKEY. +func (in *CDBTLSKEY) DeepCopy() *CDBTLSKEY { + if in == nil { + return nil + } + out := new(CDBTLSKEY) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CatalogSpec) DeepCopyInto(out *CatalogSpec) { *out = *in @@ -1470,6 +1703,22 @@ func (in *KMSDetailsStatus) DeepCopy() *KMSDetailsStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ORDSPassword. +func (in *ORDSPassword) DeepCopy() *ORDSPassword { + if in == nil { + return nil + } + out := new(ORDSPassword) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OciAcdSpec) DeepCopyInto(out *OciAcdSpec) { *out = *in @@ -1738,6 +1987,65 @@ func (in *OracleRestDataServiceStatus) DeepCopy() *OracleRestDataServiceStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDB) DeepCopyInto(out *PDB) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDB. +func (in *PDB) DeepCopy() *PDB { + if in == nil { + return nil + } + out := new(PDB) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PDB) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBAdminName) DeepCopyInto(out *PDBAdminName) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminName. +func (in *PDBAdminName) DeepCopy() *PDBAdminName { + if in == nil { + return nil + } + out := new(PDBAdminName) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBAdminPassword) DeepCopyInto(out *PDBAdminPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminPassword. +func (in *PDBAdminPassword) DeepCopy() *PDBAdminPassword { + if in == nil { + return nil + } + out := new(PDBAdminPassword) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { *out = *in @@ -1849,6 +2157,204 @@ func (in *PDBDetailsStatus) DeepCopy() *PDBDetailsStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBList) DeepCopyInto(out *PDBList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PDB, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBList. +func (in *PDBList) DeepCopy() *PDBList { + if in == nil { + return nil + } + out := new(PDBList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PDBList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBPRIVKEY) DeepCopyInto(out *PDBPRIVKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBPRIVKEY. +func (in *PDBPRIVKEY) DeepCopy() *PDBPRIVKEY { + if in == nil { + return nil + } + out := new(PDBPRIVKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBPUBKEY) DeepCopyInto(out *PDBPUBKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBPUBKEY. +func (in *PDBPUBKEY) DeepCopy() *PDBPUBKEY { + if in == nil { + return nil + } + out := new(PDBPUBKEY) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBSecret) DeepCopyInto(out *PDBSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSecret. +func (in *PDBSecret) DeepCopy() *PDBSecret { + if in == nil { + return nil + } + out := new(PDBSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBSpec) DeepCopyInto(out *PDBSpec) { + *out = *in + out.PDBTlsKey = in.PDBTlsKey + out.PDBTlsCrt = in.PDBTlsCrt + out.PDBTlsCat = in.PDBTlsCat + out.AdminName = in.AdminName + out.AdminPwd = in.AdminPwd + out.WebServerUsr = in.WebServerUsr + out.WebServerPwd = in.WebServerPwd + if in.ReuseTempFile != nil { + in, out := &in.ReuseTempFile, &out.ReuseTempFile + *out = new(bool) + **out = **in + } + if in.UnlimitedStorage != nil { + in, out := &in.UnlimitedStorage, &out.UnlimitedStorage + *out = new(bool) + **out = **in + } + if in.AsClone != nil { + in, out := &in.AsClone, &out.AsClone + *out = new(bool) + **out = **in + } + if in.TDEImport != nil { + in, out := &in.TDEImport, &out.TDEImport + *out = new(bool) + **out = **in + } + if in.TDEExport != nil { + in, out := &in.TDEExport, &out.TDEExport + *out = new(bool) + **out = **in + } + out.TDEPassword = in.TDEPassword + out.TDESecret = in.TDESecret + if in.GetScript != nil { + in, out := &in.GetScript, &out.GetScript + *out = new(bool) + **out = **in + } + out.PDBPubKey = in.PDBPubKey + out.PDBPriKey = in.PDBPriKey +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSpec. +func (in *PDBSpec) DeepCopy() *PDBSpec { + if in == nil { + return nil + } + out := new(PDBSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBStatus) DeepCopyInto(out *PDBStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBStatus. +func (in *PDBStatus) DeepCopy() *PDBStatus { + if in == nil { + return nil + } + out := new(PDBStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBTLSCAT) DeepCopyInto(out *PDBTLSCAT) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCAT. +func (in *PDBTLSCAT) DeepCopy() *PDBTLSCAT { + if in == nil { + return nil + } + out := new(PDBTLSCAT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBTLSCRT) DeepCopyInto(out *PDBTLSCRT) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCRT. +func (in *PDBTLSCRT) DeepCopy() *PDBTLSCRT { + if in == nil { + return nil + } + out := new(PDBTLSCRT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PDBTLSKEY) DeepCopyInto(out *PDBTLSKEY) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSKEY. +func (in *PDBTLSKEY) DeepCopy() *PDBTLSKEY { + if in == nil { + return nil + } + out := new(PDBTLSKEY) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PasswordSpec) DeepCopyInto(out *PasswordSpec) { *out = *in @@ -2414,6 +2920,38 @@ func (in *SourceSpec) DeepCopy() *SourceSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TDEPwd) DeepCopyInto(out *TDEPwd) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDEPwd. +func (in *TDEPwd) DeepCopy() *TDEPwd { + if in == nil { + return nil + } + out := new(TDEPwd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TDESecret) DeepCopyInto(out *TDESecret) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDESecret. +func (in *TDESecret) DeepCopy() *TDESecret { + if in == nil { + return nil + } + out := new(TDESecret) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in @@ -2486,3 +3024,67 @@ func (in *WalletSpec) DeepCopy() *WalletSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerPassword) DeepCopyInto(out *WebServerPassword) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPassword. +func (in *WebServerPassword) DeepCopy() *WebServerPassword { + if in == nil { + return nil + } + out := new(WebServerPassword) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerPasswordPDB) DeepCopyInto(out *WebServerPasswordPDB) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPasswordPDB. +func (in *WebServerPasswordPDB) DeepCopy() *WebServerPasswordPDB { + if in == nil { + return nil + } + out := new(WebServerPasswordPDB) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerUser) DeepCopyInto(out *WebServerUser) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUser. +func (in *WebServerUser) DeepCopy() *WebServerUser { + if in == nil { + return nil + } + out := new(WebServerUser) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebServerUserPDB) DeepCopyInto(out *WebServerUserPDB) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUserPDB. +func (in *WebServerUserPDB) DeepCopy() *WebServerUserPDB { + if in == nil { + return nil + } + out := new(WebServerUserPDB) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/database.oracle.com_cdbs.yaml b/config/crd/bases/database.oracle.com_cdbs.yaml index 8ea594e6..924946ee 100644 --- a/config/crd/bases/database.oracle.com_cdbs.yaml +++ b/config/crd/bases/database.oracle.com_cdbs.yaml @@ -14,6 +14,244 @@ spec: singular: cdb scope: Namespaced versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + ordsImage: + type: string + ordsImagePullPolicy: + enum: + - Always + - Never + type: string + ordsImagePullSecret: + type: string + ordsPort: + type: integer + ordsPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: false + subresources: + status: {} - additionalPrinterColumns: - description: Name of the CDB jsonPath: .spec.cdbName diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index 5efceff4..0e27126d 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -94,7 +94,7 @@ spec: externalConnectString: type: string fastStartFailover: - type: boolean + type: string primaryDatabase: type: string primaryDatabaseRef: @@ -191,7 +191,7 @@ spec: externalConnectString: type: string fastStartFailover: - type: boolean + type: string primaryDatabase: type: string primaryDatabaseRef: diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml index b674f856..b2f37ac9 100644 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ b/config/crd/bases/database.oracle.com_pdbs.yaml @@ -43,7 +43,7 @@ spec: jsonPath: .status.connString name: Connect_String type: string - name: v4 + name: v1alpha1 schema: openAPIV3Schema: properties: @@ -100,17 +100,318 @@ spec: type: boolean assertivePdbDeletion: type: boolean - assertivePdbDeletion: - description: turn on the assertive approach to delete pdb resource - kubectl delete pdb ..... automatically triggers the pluggable database - deletion - type: boolean cdbName: type: string cdbNamespace: type: string + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + type: string + pdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbState: + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + type: object + status: + properties: + action: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + status: + type: boolean + totalSize: + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + type: boolean + assertivePdbDeletion: + type: boolean + cdbName: + type: string cdbNamespace: - description: CDB Namespace type: string cdbResName: type: string diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index e46d883e..90c6dd53 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -154,9 +154,6 @@ spec: directorName: type: string envVars: - description: Replicas int32 `json:"replicas,omitempty"` // - Gsm Replicas. If you set OraGsmPvcName then it is set default - to 1. items: properties: name: @@ -658,12 +655,6 @@ spec: x-kubernetes-int-or-string: true type: object type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string storageSizeInGb: format: int32 type: integer diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 1a9d97d3..6aa2242d 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lin.ocir.io/intsanjaysingh/mmalvezz/testppr/operatormntnns + newName: lin.ocir.io/intsanjaysingh/operator/master/dboperator newTag: latest diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 3a0f15ec..0166160d 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -250,26 +250,6 @@ webhooks: resources: - shardingdatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: mshardingdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -665,27 +645,6 @@ webhooks: resources: - shardingdatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /validate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: vshardingdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 70147329..e7b562b1 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -1309,6 +1309,244 @@ spec: singular: cdb scope: Namespaced versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + ordsImage: + type: string + ordsImagePullPolicy: + enum: + - Always + - Never + type: string + ordsImagePullSecret: + type: string + ordsPort: + type: integer + ordsPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: false + subresources: + status: {} - additionalPrinterColumns: - description: Name of the CDB jsonPath: .spec.cdbName @@ -8631,7 +8869,7 @@ spec: externalConnectString: type: string fastStartFailover: - type: boolean + type: string primaryDatabase: type: string primaryDatabaseRef: @@ -8728,7 +8966,7 @@ spec: externalConnectString: type: string fastStartFailover: - type: boolean + type: string primaryDatabase: type: string primaryDatabaseRef: @@ -11023,7 +11261,7 @@ spec: jsonPath: .status.connString name: Connect_String type: string - name: v4 + name: v1alpha1 schema: openAPIV3Schema: properties: @@ -11080,16 +11318,10 @@ spec: type: boolean assertivePdbDeletion: type: boolean - assertivePdbDeletion: - description: turn on the assertive approach to delete pdb resource kubectl delete pdb ..... automatically triggers the pluggable database deletion - type: boolean cdbName: type: string cdbNamespace: type: string - cdbNamespace: - description: CDB Namespace - type: string cdbResName: type: string copyAction: @@ -11306,15 +11538,324 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + type: boolean + assertivePdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + type: string + pdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbState: + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + type: object + status: + properties: + action: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + status: + type: boolean + totalSize: + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: shardingdatabases.database.oracle.com spec: @@ -11466,7 +12007,6 @@ spec: directorName: type: string envVars: - description: Replicas int32 `json:"replicas,omitempty"` // Gsm Replicas. If you set OraGsmPvcName then it is set default to 1. items: properties: name: @@ -11968,12 +12508,6 @@ spec: x-kubernetes-int-or-string: true type: object type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string storageSizeInGb: format: int32 type: integer @@ -13788,26 +14322,6 @@ webhooks: resources: - shardingdatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: mshardingdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -14165,27 +14679,6 @@ webhooks: resources: - shardingdatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: vshardingdatabase.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -14254,7 +14747,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: container-registry.oracle.com/database/operator:1.2.0 + image: lin.ocir.io/intsanjaysingh/operator/master/dboperator:latest imagePullPolicy: Always name: manager ports: From be013e08cc35c60058791e03e6f16f48922d5e06 Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Wed, 2 Apr 2025 16:53:44 +0000 Subject: [PATCH 214/414] Doc changes openssl --- docs/sharding/README.md | 76 ++----------------- .../create_kubernetes_secret_for_db_user.md | 2 + .../sharding_provisioning_with_free_images.md | 5 +- ...harding_provisioning_with_free_images.yaml | 4 +- ...y_cloning_db_from_gold_image_across_ads.md | 3 +- ...ing_by_cloning_db_gold_image_in_same_ad.md | 3 +- ...ding_provisioning_with_chunks_specified.md | 3 +- ..._provisioning_with_control_on_resources.md | 3 +- ...ith_notification_using_oci_notification.md | 3 +- ...ding_provisioning_without_db_gold_image.md | 3 +- ...rding_scale_in_delete_an_existing_shard.md | 3 +- .../snr_ssharding_scale_out_add_shards.md | 3 +- .../snr_ssharding_shard_prov.yaml | 3 +- .../snr_ssharding_shard_prov_chunks.yaml | 3 +- .../snr_ssharding_shard_prov_clone.yaml | 3 +- ...ssharding_shard_prov_clone_across_ads.yaml | 3 +- .../snr_ssharding_shard_prov_delshard.yaml | 3 +- .../snr_ssharding_shard_prov_extshard.yaml | 3 +- .../snr_ssharding_shard_prov_memory_cpu.yaml | 3 +- ...sharding_shard_prov_send_notification.yaml | 3 +- ...y_cloning_db_from_gold_image_across_ads.md | 1 + ...ing_by_cloning_db_gold_image_in_same_ad.md | 1 + ...ding_provisioning_with_chunks_specified.md | 1 + ..._provisioning_with_control_on_resources.md | 1 + ...ith_notification_using_oci_notification.md | 1 + ...ding_provisioning_without_db_gold_image.md | 2 +- ...rding_scale_in_delete_an_existing_shard.md | 1 + .../ssharding_scale_out_add_shards.md | 1 + .../system_sharding/ssharding_shard_prov.yaml | 4 +- .../ssharding_shard_prov_chunks.yaml | 4 +- .../ssharding_shard_prov_clone.yaml | 4 +- ...ssharding_shard_prov_clone_across_ads.yaml | 4 +- .../ssharding_shard_prov_delshard.yaml | 4 +- .../ssharding_shard_prov_extshard.yaml | 4 +- .../ssharding_shard_prov_memory_cpu.yaml | 4 +- ...sharding_shard_prov_send_notification.yaml | 4 +- ...y_cloning_db_from_gold_image_across_ads.md | 1 + ...ing_by_cloning_db_gold_image_in_same_ad.md | 1 + ..._provisioning_with_control_on_resources.md | 1 + ...ith_notification_using_oci_notification.md | 1 + ...ding_provisioning_without_db_gold_image.md | 1 + ...rding_scale_in_delete_an_existing_shard.md | 1 + .../udsharding_scale_out_add_shards.md | 1 + .../udsharding_shard_prov.yaml | 4 +- .../udsharding_shard_prov_clone.yaml | 4 +- ...dsharding_shard_prov_clone_across_ads.yaml | 4 +- .../udsharding_shard_prov_delshard.yaml | 4 +- .../udsharding_shard_prov_extshard.yaml | 4 +- .../udsharding_shard_prov_memory_cpu.yaml | 4 +- ...sharding_shard_prov_send_notification.yaml | 4 +- 50 files changed, 91 insertions(+), 120 deletions(-) diff --git a/docs/sharding/README.md b/docs/sharding/README.md index 661e2546..487d9ec3 100644 --- a/docs/sharding/README.md +++ b/docs/sharding/README.md @@ -72,7 +72,7 @@ Choose one of the following deployment options: **Use Oracle-Supplied Docker Images:** The Oracle Sharding Database controller uses Oracle Global Data Services and Oracle Database images to provision the sharding topology. - You can also download the pre-built Oracle Global Data Services `container-registry.oracle.com/database/gsm:latest` and Oracle Database images `container-registry.oracle.com/database/enterprise:latest` from [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::). These images are functionally tested and evaluated with various use cases of Oracle Globally Distributed Database topology by deploying on OKE and OLCNE. + You can also download the pre-built Oracle Global Data Services and Oracle Database images from [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::). These images are functionally tested and evaluated with various use cases of Oracle Globally Distributed Database topology by deploying on OKE and OLCNE. You can refer to [Oracle Container Registry Images for Oracle Globally Distributed Database Deployment](https://github.com/oracle/db-sharding/blob/master/container-based-sharding-deployment/README.md#oracle-container-registry-images-for-oracle-globally-distributed-database-deployment) **Note:** You will need to accept Agreement from container-registry.orcale.com to be able to pull the pre-built container images. @@ -80,7 +80,7 @@ Choose one of the following deployment options: **Build your own Oracle Database and Global Data Services Docker Images:** You can build these images using instructions provided on Oracle official GitHub Repositories: - * [Oracle Global Data Services Image](https://github.com/oracle/db-sharding/tree/master/docker-based-sharding-deployment/dockerfiles) + * [Oracle Global Data Services Image](https://github.com/oracle/db-sharding/tree/master/container-based-sharding-deployment) * [Oracle Database Image](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance) After the images are ready, push them to your Docker Images Repository, so that you can pull them during Oracle Globally Distributed Database topology provisioning. @@ -91,8 +91,6 @@ You can either download the images and push them to your Docker Images Repositor **Note:** In case you want to use the `Oracle Database 23ai Free` Image for Database and GSM, refer to section [Oracle Database 23ai Free](#oracle-database-23ai-free) for more details. -### 4. Create a namespace for the Oracle DB Sharding Setup - ### 4. Create a namespace for the Oracle Globally Distributed Database Setup Create a Kubernetes namespace named `shns`. All the resources belonging to the Oracle Globally Distributed Database Topology Setup will be provisioned in this namespace named `shns`. For example: @@ -107,6 +105,8 @@ You can either download the images and push them to your Docker Images Repositor ### 5. Create a Kubernetes secret for the database installation owner for the Oracle Globally Distributed Database Topology Deployment +**IMPORTANT:** Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generate the encrypted password file during the deployment. If you want to use Prebuilt Oracle Database and Oracle GSM Images from Oracle Container Registry for your deployment, you can refer to [Oracle Container Registry Images for Oracle Globally Distributed Database Deployment](https://github.com/oracle/db-sharding/blob/master/container-based-sharding-deployment/README.md#oracle-container-registry-images-for-oracle-globally-distributed-database-deployment) + Create a Kubernetes secret named `db-user-pass-rsa` using these steps: [Create Kubernetes Secret](./provisioning/create_kubernetes_secret_for_db_user.md) After you have the above prerequisites completed, you can proceed to the next section for your environment to provision the Oracle Database Sharding Topology. @@ -119,71 +119,6 @@ In case of an `OCI OKE` cluster, you can use this Persistent Volume during provi You can refer [here](./provisioning/provisioning_persistent_volume_having_db_gold_image.md) for the steps involved. -**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. So, this step will not be needed if you are deploying Oracle Sharded Database using Oracle 23ai Free Database and GSM Images. - -## Oracle Database 23ai Free - -Please refer to [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) documentation for more details. - -If you want to use Oracle Database 23ai Free Image for Database and GSM for deployment of the Sharded Database using Sharding Controller in Oracle Database Kubernetes Operator, you need to consider the below points: - -* To deploy using the FREE Database and GSM Image, you will need to add the additional parameter `dbEdition: "free"` to the .yaml file. -* Refer to [Sample Sharded Database Deployment using Oracle 23ai FREE Database and GSM Images](./provisioning/free/sharding_provisioning_with_free_images.md) for an example. -* For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. -* Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. -* Total number of chunks for FREE Database defaults to `12` if `CATALOG_CHUNKS` parameter is not specified. This default value is determined considering limitation of 12 GB of user data on disk for oracle free database. - - -## Provisioning Sharding Topology with System-Managed Sharding in a Cloud-Based Kubernetes Cluster - -Deploy Oracle Database Sharding Topology with `System-Managed Sharding` on your Cloud based Kubernetes cluster. - -In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database Sharding Topology covered by below examples: - -[1. Provisioning Oracle Sharded Database with System-Managed Sharding without Database Gold Image](./provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md) -[2. Provisioning Oracle Sharded Database with System-Managed Sharding with number of chunks specified](./provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md) -[3. Provisioning Oracle Sharded Database with System-Managed Sharding with additional control on resources like Memory and CPU allocated to Pods](./provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md) -[4. Provisioning Oracle Sharded Database with System-Managed Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) -[5. Provisioning Oracle Sharded Database with System-Managed Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) -[6. Provisioning Oracle Sharded Database with System-Managed Sharding and send Notification using OCI Notification Service](./provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md) -[7. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding](./provisioning/system_sharding/ssharding_scale_out_add_shards.md) -[8. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding](./provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md) - - -## Provisioning Sharding Topology with User Defined Sharding in a Cloud-Based Kubernetes Cluster - -Deploy Oracle Database Sharding Topology with `User Defined Sharding` on your Cloud based Kubernetes cluster. - -In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database Sharding Topology covered by below examples: - -[1. Provisioning Oracle Sharded Database with User Defined Sharding without Database Gold Image](./provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md) -[2. Provisioning Oracle Sharded Database with User Defined Sharding with additional control on resources like Memory and CPU allocated to Pods](./provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md) -[3. Provisioning Oracle Sharded Database with User Defined Sharding by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) -[4. Provisioning Oracle Sharded Database with User Defined Sharding by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) -[5. Provisioning Oracle Sharded Database with User Defined Sharding and send Notification using OCI Notification Service](./provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md) -[6. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md) -[7. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with User Defined Sharding](./provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md) - - -## Provisioning System-Managed Sharding Topology with Raft replication enabled in a Cloud-Based Kubernetes Cluster - -Deploy Oracle Database Sharding Topology with `System-Managed Sharding with SNR RAFT enabled` on your Cloud based Kubernetes cluster. - -**NOTE: SNR RAFT Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Database Sharding Topology covered by below examples: - -[1. Provisioning System-Managed Sharding Topology with Raft replication enabled without Database Gold Image](./provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md) -[2. Provisioning System-Managed Sharding Topology with Raft replication enabled with number of chunks specified](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md) -[3. Provisioning System-Managed Sharding Topology with Raft replication enabled with additional control on resources like Memory and CPU allocated to Pods](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md) -[4. Provisioning System-Managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) -[5. Provisioning System-Managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) -[6. Provisioning System-Managed Sharding Topology with Raft replication enabled and send Notification using OCI Notification Service](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md) -[7. Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT replication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md) -[8. Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT reolication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md) - -You can refer [here](./provisioning/provisioning_persistent_volume_having_db_gold_image.md) for the steps involved. - **NOTE:** Provisioning the Oracle Globally Distributed Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. So, this step will not be needed if you are deploying Oracle Globally Distributed Database using Oracle 23ai Free Database and GSM Images. ## Oracle Database 23ai Free @@ -262,4 +197,5 @@ To debug the Oracle Globally Distributed Database Topology provisioned using the * For both ENTERPRISE and FREE Images, if the CATALOG Database Pod is stopped from the worker node using the command `crictl stopp`, then it can leave the CATALOG in an error state. This error state results in GSM reporting the error message **GSM-45034: Connection to GDS catalog is not established.** * For both ENTERPRISE and FREE Images, either restart of node running the SHARD Pod using `/sbin/reboot -f` or stopping the Shard Database Pod from the worker node using `crictl stopp` command can leave the shard in an error state. * For both ENTERPRISE and FREE Images, after force restarts of the node running GSM Pod, the GSM pod restarts multiple times, and then becomes stable. The GSM pod restarts itself because when the worker node comes up, the GSM pod is recreated, but does not obtain DB connection to the Catalog. The Liveness Probe fails which restarts the Pod. Be aware of this issue, and permit the GSM pod to become stable. -* **DDL Propagation from Catalog to Shards:** DDL Propagation from the Catalog Database to the Shard Databases can take several minutes to complete. To see faster propagation of DDLs such as the tablespace set from the Catalog Database to the Shard Databases, Oracle recommends that you set smaller chunk values by using the `CATALOG_CHUNKS` attribute in the .yaml file while creating the Sharded Database Topology. \ No newline at end of file +* **DDL Propagation from Catalog to Shards:** DDL Propagation from the Catalog Database to the Shard Databases can take several minutes to complete. To see faster propagation of DDLs such as the tablespace set from the Catalog Database to the Shard Databases, Oracle recommends that you set smaller chunk values by using the `CATALOG_CHUNKS` attribute in the .yaml file while creating the Sharded Database Topology. +* If the version of `openssl` used to create the encrypted password file for Kubernetes secrets is not compatible with the openssl verion of the Oracle Database and Oracle GSM Image, then you can get the error `OS command returned code : 1, returned error : bad magic number` in the logs of the Database or GSM Pod. In this case, during the deployment, openssl will not be able to decrypt the encrypted password file and the deployment will not complete. \ No newline at end of file diff --git a/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md b/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md index 744f972c..db534575 100644 --- a/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md +++ b/docs/sharding/provisioning/create_kubernetes_secret_for_db_user.md @@ -8,6 +8,8 @@ Use the following steps to create an encrypted file with a password for the DB U - Remove the initial text file. - Create the Kubernetes Secret named `db-user-pass-rsa` using the encrypted file. +**IMPORTANT:** Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. + To understand how to create your own file, use the following example: ```sh diff --git a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.md b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.md index 61641312..0425920b 100644 --- a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.md +++ b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.md @@ -19,8 +19,11 @@ This example uses `sharding_provisioning_with_free_images.yaml` to provision an To get the Oracle 23ai FREE Database and GSM Images: * The Oracle 23ai FREE RDBMS Image used is `container-registry.oracle.com/database/free:latest`. Check [Oracle Database Free Get Started](https://www.oracle.com/database/free/get-started/?source=v0-DBFree-ChatCTA-j2032-20240709) for details. * To pull the above image from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * Use the Oracle 23ai FREE GSM Binaries `LINUX.X64_234000_gsm.zip` as listed on page [Oracle Database Free Get Started](https://www.oracle.com/database/free/get-started/?source=v0-DBFree-ChatCTA-j2032-20240709) and prepare the GSM Container Image following [Oracle Global Data Services Image](https://github.com/oracle/db-sharding/tree/master/docker-based-sharding-deployment/dockerfiles) + * The the Oracle 23ai FREE GSM Image used is `container-registry.oracle.com/database/gsm:latest`. + * To pull the above image from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * You need to change `dbImage` and `gsmImage` tag with the images you want to use in your enviornment in file `sharding_provisioning_with_free_images.yaml`. + +**IMPORTANT:** Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. diff --git a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml index 954ede63..dadd619a 100644 --- a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml +++ b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml @@ -41,8 +41,8 @@ spec: storageClass: oci dbImage: container-registry.oracle.com/database/free:latest dbImagePullSecret: ocr-reg-cred - gsmImage: - gsmImagePullSecret: + gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImagePullSecret: ocr-reg-cred dbEdition: "free" isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md index ba72be25..9ffebad9 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -1,6 +1,6 @@ # Provisioning System managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image across Availability Domains(ADs) -**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** +**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -40,6 +40,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone_across_ads.yaml`. * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [snr_ssharding_shard_prov_clone_across_ads.yaml](./snr_ssharding_shard_prov_clone_across_ads.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md index cf4240f7..054d760e 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -1,6 +1,6 @@ # Provisioning System managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD) -**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** +**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -34,6 +34,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone.yaml`. * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md index 44972090..253d099b 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md @@ -1,6 +1,6 @@ # Provisioning System-Managed Sharding Topology with Raft replication enabled with number of chunks specified -**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** +**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -25,6 +25,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [snr_ssharding_shard_prov_chunks.yaml](./snr_ssharding_shard_prov_chunks.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md index 9cfd6afb..e017f6a9 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md @@ -1,6 +1,6 @@ # Provisioning System-Managed Sharding Topology with Raft replication enabled with additional control on resources like Memory and CPU allocated to Pods -**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** +**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -21,6 +21,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_memory_cpu.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md index d4cb11de..3b8d1665 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md @@ -1,6 +1,6 @@ # Provisioning System managed Sharding Topology with Raft replication enabled and send Notification using OCI Notification Service -**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** +**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -69,6 +69,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_send_notification.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md index 892741a5..91caddf1 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md @@ -1,6 +1,6 @@ # Provisioning System-Managed Sharding Topology with Raft replication enabled without Database Gold Image -**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** +**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -22,6 +22,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [snr_ssharding_shard_prov.yaml](./snr_ssharding_shard_prov.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md index fe3157ec..fc093654 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md @@ -1,6 +1,6 @@ # Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT reolication enabled -**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** +**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -21,6 +21,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_delshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. NOTE: Use tag `isDelete: enable` to delete the shard you want. diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md index 03423e72..3461bf13 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md @@ -1,6 +1,6 @@ # Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT replication enabled -**NOTE: RAFT Replication Feature is available only for Oracle 23c RDBMS and Oracle 23c GSM version.** +**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** **IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. @@ -19,6 +19,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_extshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml index aabd8470..53b93a0d 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml @@ -39,10 +39,11 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/free:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred + dbEdition: "free" replicationType: "native" isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml index def7e73a..0230eac2 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml @@ -42,10 +42,11 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/free:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred + dbEdition: "free" replicationType: "native" isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml index 8f17331e..fcc18da0 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml @@ -63,10 +63,11 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/free:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred + dbEdition: "free" replicationType: "native" isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml index d0c1c6e0..0663f8a5 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml @@ -71,10 +71,11 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/free:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred + dbEdition: "free" replicationType: "native" isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml index 0859b089..ce194246 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml @@ -50,10 +50,11 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/free:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred + dbEdition: "free" replicationType: "native" isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml index 123b3ae1..8848b8c7 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml @@ -49,10 +49,11 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/free:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred + dbEdition: "free" replicationType: "native" isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml index 0cfccf9a..dce4ba29 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml @@ -70,10 +70,11 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/free:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred + dbEdition: "free" replicationType: "native" isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml index 345e9c09..2b410e8b 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml @@ -63,10 +63,11 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/free:latest dbImagePullSecret: ocr-reg-cred gsmImage: container-registry.oracle.com/database/gsm:latest gsmImagePullSecret: ocr-reg-cred + dbEdition: "free" replicationType: "native" isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md index e457b7eb..4d24655d 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -38,6 +38,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_clone_across_ads.yaml`. * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [ssharding_shard_prov_clone_across_ads.yaml](./ssharding_shard_prov_clone_across_ads.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md index cb01fa0d..5e44a601 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -33,6 +33,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_clone.yaml`. * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [ssharding_shard_prov_clone.yaml](./ssharding_shard_prov_clone.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md index 0c6ea8fe..649fc7c4 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md @@ -22,6 +22,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [ssharding_shard_prov_chunks.yaml](./ssharding_shard_prov_chunks.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md index c4f45a48..d284bf9b 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md @@ -18,6 +18,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_memory_cpu.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md index 1a6a1ee3..e77718f4 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md @@ -66,6 +66,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_send_notification.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md index b223d1af..1ecb0ec1 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md @@ -19,7 +19,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [ssharding_shard_prov.yaml](./ssharding_shard_prov.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md index bca34253..889de98c 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md @@ -18,6 +18,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_delshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. NOTE: Use tag `isDelete: enable` to delete the shard you want. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md index 1db8e6c3..5086d887 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md @@ -16,6 +16,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_extshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml index 5adbd2ce..1bdb9ce5 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml @@ -39,9 +39,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml index 5c135229..868e8bc1 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml @@ -42,9 +42,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml index f5816a87..3cafeba7 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml @@ -63,9 +63,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml index 8fee0526..d7ec6365 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml @@ -71,9 +71,9 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml index 3902ceef..1017a9d5 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml @@ -50,9 +50,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml index a11833e0..d23052fb 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml @@ -49,9 +49,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml index 3f092b89..075919f7 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml @@ -70,9 +70,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml index 0ca6ec6f..aea6fc7c 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml @@ -63,9 +63,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred isExternalSvc: False isDeleteOraPvc: True diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md index 9b2905e8..e55df2de 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -36,6 +36,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_clone_across_ads.yaml`. * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md index a4669667..edd9c484 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -32,6 +32,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_clone.yaml`. * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md index b52b8745..638b7124 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md @@ -19,6 +19,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_memory_cpu.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md index 640301a2..fe1ca870 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md @@ -67,6 +67,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_send_notification.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md index 2be5ac9f..b0378e04 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md @@ -20,6 +20,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [udsharding_shard_prov.yaml](./udsharding_shard_prov.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md index 2c4cbfc2..673e455e 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md @@ -17,6 +17,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_delshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Use tag `isDelete: enable` to delete the shard you want. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md index 20f50b29..abdc53ff 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md @@ -17,6 +17,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_extshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. + * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml index d33be599..c9f20eb3 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml @@ -39,9 +39,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred shardingType: USER isExternalSvc: False diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml index 04ee5d95..d7e5ce78 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml @@ -63,9 +63,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred shardingType: USER isExternalSvc: False diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml index 5be6ecde..ae02c7fe 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml @@ -71,9 +71,9 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred shardingType: USER isExternalSvc: False diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml index e00d2272..d83bf546 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml @@ -50,9 +50,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred shardingType: USER isExternalSvc: False diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml index 3899f2ab..7526feb7 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml @@ -48,9 +48,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred shardingType: USER isExternalSvc: False diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml index 6c65916e..8be81d39 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml @@ -70,9 +70,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred shardingType: USER isExternalSvc: False diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml index ef1b5561..4dda6db9 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml @@ -63,9 +63,9 @@ spec: storageSizeInGb: 50 region: standby storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 gsmImagePullSecret: ocr-reg-cred shardingType: USER isExternalSvc: False From f092dd834815a3514df56437c1e35eeb1fa7b770 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Mon, 14 Apr 2025 16:45:21 +0000 Subject: [PATCH 215/414] Tinglwan olm bundle v1.2.0 --- Makefile | 2 +- PROJECT | 2 +- bundle.Dockerfile | 14 +- ...acle.com_autonomouscontainerdatabases.yaml | 111 +- ....oracle.com_autonomousdatabasebackups.yaml | 132 +- ...oracle.com_autonomousdatabaserestores.yaml | 132 +- ...tabase.oracle.com_autonomousdatabases.yaml | 704 ++ .../manifests/database.oracle.com_cdbs.yaml | 499 ++ .../database.oracle.com_dataguardbrokers.yaml | 217 + .../database.oracle.com_dbcssystems.yaml | 418 +- .../manifests/database.oracle.com_lrests.yaml | 127 +- .../manifests/database.oracle.com_lrpdbs.yaml | 223 +- ...ase.oracle.com_oraclerestdataservices.yaml | 213 +- .../database.oracle.com_ordssrvs.yaml | 495 ++ .../manifests/database.oracle.com_pdbs.yaml | 641 ++ ...database.oracle.com_shardingdatabases.yaml | 1115 +++ ...se.oracle.com_singleinstancedatabases.yaml | 717 ++ ...vability.oracle.com_databaseobservers.yaml | 6995 +++++++++++++++++ ...er-manager-metrics-service_v1_service.yaml | 16 + ...c.authorization.k8s.io_v1_clusterrole.yaml | 10 + ...e-operator-webhook-service_v1_service.yaml | 14 + ...tabase-operator.clusterserviceversion.yaml | 1442 ++++ bundle/metadata/annotations.yaml | 10 + config/certmanager/certificate.yaml | 6 +- config/crd/kustomization.yaml | 34 +- ...ction_in_autonomouscontainerdatabases.yaml | 2 +- ...njection_in_autonomousdatabasebackups.yaml | 2 +- ...jection_in_autonomousdatabaserestores.yaml | 2 +- .../cainjection_in_autonomousdatabases.yaml | 2 +- config/crd/patches/cainjection_in_cdbs.yaml | 2 +- .../patches/cainjection_in_dbcssystems.yaml | 2 +- config/crd/patches/cainjection_in_pdbs.yaml | 2 +- .../cainjection_in_shardingdatabases.yaml | 2 +- ...ainjection_in_singleinstancedatabases.yaml | 2 +- ...tabase.oracle.com_autonomousdatabases.yaml | 324 - .../database.oracle.com_dataguardbrokers.yaml | 134 - ...database.oracle.com_shardingdatabases.yaml | 688 -- ...se.oracle.com_singleinstancedatabases.yaml | 421 - config/default/kustomization.yaml | 138 +- config/default/webhookcainjection_patch.yaml | 6 +- config/manager/kustomization.yaml | 4 +- config/manager/manager.yaml | 2 +- ...tabase-operator.clusterserviceversion.yaml | 147 +- .../acd/autonomouscontainerdatabase_bind.yaml | 2 +- ...scontainerdatabase_change_displayname.yaml | 2 +- .../autonomouscontainerdatabase_create.yaml | 2 +- ...mouscontainerdatabase_delete_resource.yaml | 2 +- ...uscontainerdatabase_restart_terminate.yaml | 2 +- .../adb/autonomousdatabase_backup.yaml | 2 +- .../samples/adb/autonomousdatabase_bind.yaml | 2 +- .../samples/adb/autonomousdatabase_clone.yaml | 2 +- .../adb/autonomousdatabase_create.yaml | 2 +- .../autonomousdatabase_delete_resource.yaml | 2 +- .../adb/autonomousdatabase_rename.yaml | 2 +- .../adb/autonomousdatabase_restore.yaml | 2 +- .../samples/adb/autonomousdatabase_scale.yaml | 2 +- ...tonomousdatabase_stop_start_terminate.yaml | 2 +- ...onomousdatabase_update_admin_password.yaml | 2 +- .../adb/autonomousdatabase_update_mtls.yaml | 2 +- ...onomousdatabase_update_network_access.yaml | 2 +- .../adb/autonomousdatabase_wallet.yaml | 2 +- config/samples/kustomization.yaml | 54 +- config/scorecard/kustomization.yaml | 9 +- 63 files changed, 14191 insertions(+), 2079 deletions(-) rename {config => bundle/manifests}/database.oracle.com_autonomouscontainerdatabases.yaml (50%) rename {config => bundle/manifests}/database.oracle.com_autonomousdatabasebackups.yaml (50%) rename {config => bundle/manifests}/database.oracle.com_autonomousdatabaserestores.yaml (50%) create mode 100644 bundle/manifests/database.oracle.com_autonomousdatabases.yaml create mode 100644 bundle/manifests/database.oracle.com_cdbs.yaml create mode 100644 bundle/manifests/database.oracle.com_dataguardbrokers.yaml rename config/database.oracle.com_DbcsSystem.yaml => bundle/manifests/database.oracle.com_dbcssystems.yaml (50%) rename config/database.oracle.com_cdbs.yaml => bundle/manifests/database.oracle.com_lrests.yaml (63%) rename config/database.oracle.com_pdbs.yaml => bundle/manifests/database.oracle.com_lrpdbs.yaml (55%) rename {config => bundle/manifests}/database.oracle.com_oraclerestdataservices.yaml (50%) create mode 100644 bundle/manifests/database.oracle.com_ordssrvs.yaml create mode 100644 bundle/manifests/database.oracle.com_pdbs.yaml create mode 100644 bundle/manifests/database.oracle.com_shardingdatabases.yaml create mode 100644 bundle/manifests/database.oracle.com_singleinstancedatabases.yaml create mode 100644 bundle/manifests/observability.oracle.com_databaseobservers.yaml create mode 100644 bundle/manifests/oracle-database-operator-controller-manager-metrics-service_v1_service.yaml create mode 100644 bundle/manifests/oracle-database-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml create mode 100644 bundle/manifests/oracle-database-operator-webhook-service_v1_service.yaml create mode 100644 bundle/manifests/oracle-database-operator.clusterserviceversion.yaml create mode 100644 bundle/metadata/annotations.yaml delete mode 100644 config/database.oracle.com_autonomousdatabases.yaml delete mode 100644 config/database.oracle.com_dataguardbrokers.yaml delete mode 100644 config/database.oracle.com_shardingdatabases.yaml delete mode 100644 config/database.oracle.com_singleinstancedatabases.yaml diff --git a/Makefile b/Makefile index b9755e6f..33e52dd6 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # # Current Operator version -VERSION ?= 0.0.1 +VERSION ?= 1.2.0 # Default bundle image tag BUNDLE_IMG ?= controller-bundle:$(VERSION) # Options for 'bundle-build' diff --git a/PROJECT b/PROJECT index 97e9409c..f55c035c 100644 --- a/PROJECT +++ b/PROJECT @@ -4,7 +4,7 @@ # More info: https://book.kubebuilder.io/reference/project-config.html domain: oracle.com layout: -- go.kubebuilder.io/v2 +- go.kubebuilder.io/v4 multigroup: true plugins: manifests.sdk.operatorframework.io/v2: {} diff --git a/bundle.Dockerfile b/bundle.Dockerfile index d591c4ef..f64398cf 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -1,7 +1,3 @@ -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -# - FROM scratch # Core bundle labels. @@ -10,16 +6,10 @@ LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=oracle-database-operator LABEL operators.operatorframework.io.bundle.channels.v1=alpha -LABEL operators.operatorframework.io.bundle.channel.default.v1=alpha +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.39.2 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.6.1+git -LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v2 - -# Labels for testing. -LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 -LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ +LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 # Copy files to locations specified by labels. COPY bundle/manifests /manifests/ COPY bundle/metadata /metadata/ -COPY bundle/tests/scorecard /tests/scorecard/ diff --git a/config/database.oracle.com_autonomouscontainerdatabases.yaml b/bundle/manifests/database.oracle.com_autonomouscontainerdatabases.yaml similarity index 50% rename from config/database.oracle.com_autonomouscontainerdatabases.yaml rename to bundle/manifests/database.oracle.com_autonomouscontainerdatabases.yaml index bac3a28c..1cae1263 100644 --- a/config/database.oracle.com_autonomouscontainerdatabases.yaml +++ b/bundle/manifests/database.oracle.com_autonomouscontainerdatabases.yaml @@ -1,13 +1,24 @@ - ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: autonomouscontainerdatabases.database.oracle.com spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 group: database.oracle.com names: kind: AutonomousContainerDatabase @@ -32,24 +43,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousContainerDatabaseSpec defines the desired state - of AutonomousContainerDatabase properties: action: enum: @@ -58,8 +59,6 @@ spec: - TERMINATE type: string autonomousContainerDatabaseOCID: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' type: string autonomousExadataVMClusterOCID: type: string @@ -75,7 +74,6 @@ spec: default: false type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -83,21 +81,84 @@ spec: type: string type: object patchModel: - description: 'AutonomousContainerDatabasePatchModelEnum Enum with - underlying type: string' enum: - RELEASE_UPDATES - RELEASE_UPDATE_REVISIONS type: string type: object status: - description: AutonomousContainerDatabaseStatus defines the observed state - of AutonomousContainerDatabase properties: lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' + type: string + timeCreated: + type: string + required: + - lifecycleState + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.displayName + name: DisplayName + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - SYNC + - RESTART + - TERMINATE + type: string + autonomousContainerDatabaseOCID: + type: string + autonomousExadataVMClusterOCID: + type: string + compartmentOCID: + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + patchModel: + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string + type: object + status: + properties: + lifecycleState: type: string timeCreated: type: string @@ -113,5 +174,5 @@ status: acceptedNames: kind: "" plural: "" - conditions: [] - storedVersions: [] + conditions: null + storedVersions: null diff --git a/config/database.oracle.com_autonomousdatabasebackups.yaml b/bundle/manifests/database.oracle.com_autonomousdatabasebackups.yaml similarity index 50% rename from config/database.oracle.com_autonomousdatabasebackups.yaml rename to bundle/manifests/database.oracle.com_autonomousdatabasebackups.yaml index a5c37507..06df19c3 100644 --- a/config/database.oracle.com_autonomousdatabasebackups.yaml +++ b/bundle/manifests/database.oracle.com_autonomousdatabasebackups.yaml @@ -1,13 +1,24 @@ - ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: autonomousdatabasebackups.database.oracle.com spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 group: database.oracle.com names: kind: AutonomousDatabaseBackup @@ -38,24 +49,14 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseBackupSpec defines the desired state of - AutonomousDatabaseBackup properties: autonomousDatabaseBackupOCID: type: string @@ -64,7 +65,6 @@ spec: isLongTermBackup: type: boolean ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -74,11 +74,8 @@ spec: retentionPeriodInDays: type: integer target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -91,8 +88,6 @@ spec: type: object type: object status: - description: AutonomousDatabaseBackupStatus defines the observed state - of AutonomousDatabaseBackup properties: autonomousDatabaseOCID: type: string @@ -105,16 +100,103 @@ spec: isAutomatic: type: boolean lifecycleState: - description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with - underlying type: string' type: string timeEnded: type: string timeStarted: type: string type: - description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying - type: string' + type: string + required: + - autonomousDatabaseOCID + - compartmentOCID + - dbDisplayName + - dbName + - isAutomatic + - lifecycleState + - type + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .status.dbDisplayName + name: DB DisplayName + type: string + - jsonPath: .status.type + name: Type + type: string + - jsonPath: .status.timeStarted + name: Started + type: string + - jsonPath: .status.timeEnded + name: Ended + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autonomousDatabaseBackupOCID: + type: string + displayName: + type: string + isLongTermBackup: + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + retentionPeriodInDays: + type: integer + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + type: object + status: + properties: + autonomousDatabaseOCID: + type: string + compartmentOCID: + type: string + dbDisplayName: + type: string + dbName: + type: string + isAutomatic: + type: boolean + lifecycleState: + type: string + timeEnded: + type: string + timeStarted: + type: string + type: type: string required: - autonomousDatabaseOCID @@ -134,5 +216,5 @@ status: acceptedNames: kind: "" plural: "" - conditions: [] - storedVersions: [] + conditions: null + storedVersions: null diff --git a/config/database.oracle.com_autonomousdatabaserestores.yaml b/bundle/manifests/database.oracle.com_autonomousdatabaserestores.yaml similarity index 50% rename from config/database.oracle.com_autonomousdatabaserestores.yaml rename to bundle/manifests/database.oracle.com_autonomousdatabaserestores.yaml index 5e9f2c73..7f2d386d 100644 --- a/config/database.oracle.com_autonomousdatabaserestores.yaml +++ b/bundle/manifests/database.oracle.com_autonomousdatabaserestores.yaml @@ -1,13 +1,24 @@ - ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: autonomousdatabaserestores.database.oracle.com spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 group: database.oracle.com names: kind: AutonomousDatabaseRestore @@ -32,27 +43,16 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: AutonomousDatabaseRestoreSpec defines the desired state of - AutonomousDatabaseRestore properties: ociConfig: - description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -62,9 +62,6 @@ spec: source: properties: k8sADBBackup: - description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO - OWN! NOTE: json tags are required. Any new fields you add must - have json tags for the fields to be serialized.' properties: name: type: string @@ -72,17 +69,12 @@ spec: pointInTime: properties: timestamp: - description: 'The timestamp must follow this format: YYYY-MM-DD - HH:MM:SS GMT' type: string type: object type: object target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' properties: k8sADB: - description: "*********************** *\tADB spec ***********************" properties: name: type: string @@ -98,18 +90,98 @@ spec: - target type: object status: - description: AutonomousDatabaseRestoreStatus defines the observed state - of AutonomousDatabaseRestore properties: dbName: type: string displayName: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string status: - description: 'WorkRequestStatusEnum Enum with underlying type: string' + type: string + timeAccepted: + type: string + timeEnded: + type: string + timeStarted: + type: string + workRequestOCID: + type: string + required: + - dbName + - displayName + - status + - workRequestOCID + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.displayName + name: DbDisplayName + type: string + - jsonPath: .status.dbName + name: DbName + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + source: + properties: + k8sADBBackup: + properties: + name: + type: string + type: object + pointInTime: + properties: + timestamp: + type: string + type: object + type: object + target: + properties: + k8sADB: + properties: + name: + type: string + type: object + ociADB: + properties: + ocid: + type: string + type: object + type: object + required: + - source + - target + type: object + status: + properties: + dbName: + type: string + displayName: + type: string + status: type: string timeAccepted: type: string @@ -134,5 +206,5 @@ status: acceptedNames: kind: "" plural: "" - conditions: [] - storedVersions: [] + conditions: null + storedVersions: null diff --git a/bundle/manifests/database.oracle.com_autonomousdatabases.yaml b/bundle/manifests/database.oracle.com_autonomousdatabases.yaml new file mode 100644 index 00000000..f2e82d0e --- /dev/null +++ b/bundle/manifests/database.oracle.com_autonomousdatabases.yaml @@ -0,0 +1,704 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + creationTimestamp: null + name: autonomousdatabases.database.oracle.com +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /convert + conversionReviewVersions: + - v1alpha1 + - v1 + - v4 + group: database.oracle.com + names: + kind: AutonomousDatabase + listKind: AutonomousDatabaseList + plural: autonomousdatabases + shortNames: + - adb + - adbs + singular: autonomousdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - "" + - Create + - Sync + - Update + - Stop + - Start + - Terminate + - Clone + type: string + clone: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sAcd: + properties: + name: + type: string + type: object + ociAcd: + properties: + id: + type: string + type: object + type: object + cloneType: + enum: + - FULL + - METADATA + type: string + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAccessControlEnabled: + type: boolean + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: + type: string + whitelistedIps: + items: + type: string + type: array + type: object + details: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sAcd: + properties: + name: + type: string + type: object + ociAcd: + properties: + id: + type: string + type: object + type: object + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + id: + type: string + isAccessControlEnabled: + type: boolean + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: + type: string + whitelistedIps: + items: + type: string + type: array + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + type: object + required: + - action + type: object + status: + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.details.displayName + name: Display Name + type: string + - jsonPath: .spec.details.dbName + name: Db Name + type: string + - jsonPath: .status.lifecycleState + name: State + type: string + - jsonPath: .spec.details.isDedicated + name: Dedicated + type: string + - jsonPath: .spec.details.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .spec.details.dataStorageSizeInTBs + name: Storage (TB) + type: integer + - jsonPath: .spec.details.dbWorkload + name: Workload Type + type: string + - jsonPath: .status.timeCreated + name: Created + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - "" + - Create + - Sync + - Update + - Stop + - Start + - Terminate + - Clone + type: string + clone: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sAcd: + properties: + name: + type: string + type: object + ociAcd: + properties: + id: + type: string + type: object + type: object + cloneType: + enum: + - FULL + - METADATA + type: string + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + isAccessControlEnabled: + type: boolean + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: + type: string + whitelistedIps: + items: + type: string + type: array + type: object + details: + properties: + adminPassword: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + autonomousContainerDatabase: + properties: + k8sAcd: + properties: + name: + type: string + type: object + ociAcd: + properties: + id: + type: string + type: object + type: object + compartmentId: + type: string + computeCount: + type: number + computeModel: + enum: + - ECPU + - OCPU + type: string + cpuCoreCount: + type: integer + dataStorageSizeInTBs: + type: integer + dbName: + type: string + dbVersion: + type: string + dbWorkload: + enum: + - OLTP + - DW + - AJD + - APEX + type: string + displayName: + type: string + freeformTags: + additionalProperties: + type: string + type: object + id: + type: string + isAccessControlEnabled: + type: boolean + isAutoScalingEnabled: + type: boolean + isDedicated: + type: boolean + isFreeTier: + type: boolean + isMtlsConnectionRequired: + type: boolean + licenseModel: + enum: + - LICENSE_INCLUDED + - BRING_YOUR_OWN_LICENSE + type: string + nsgIds: + items: + type: string + type: array + ocpuCount: + type: number + privateEndpointLabel: + type: string + subnetId: + type: string + whitelistedIps: + items: + type: string + type: array + type: object + hardLink: + default: false + type: boolean + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + wallet: + properties: + name: + type: string + password: + properties: + k8sSecret: + properties: + name: + type: string + type: object + ociSecret: + properties: + id: + type: string + type: object + type: object + type: object + required: + - action + type: object + status: + properties: + allConnectionStrings: + items: + properties: + connectionStrings: + items: + properties: + connectionString: + type: string + tnsName: + type: string + type: object + type: array + tlsAuthentication: + type: string + required: + - connectionStrings + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lifecycleState: + type: string + timeCreated: + type: string + walletExpiringDate: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/database.oracle.com_cdbs.yaml b/bundle/manifests/database.oracle.com_cdbs.yaml new file mode 100644 index 00000000..5ad34bbf --- /dev/null +++ b/bundle/manifests/database.oracle.com_cdbs.yaml @@ -0,0 +1,499 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + creationTimestamp: null + name: cdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: CDB + listKind: CDBList + plural: cdbs + singular: cdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + ordsImage: + type: string + ordsImagePullPolicy: + enum: + - Always + - Never + type: string + ordsImagePullSecret: + type: string + ordsPort: + type: integer + ordsPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the CDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: ' string of the tnsalias' + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbAdminUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbName: + type: string + cdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + dbPort: + type: integer + dbServer: + type: string + dbTnsurl: + type: string + deletePdbCascade: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + ordsImage: + type: string + ordsImagePullPolicy: + enum: + - Always + - Never + type: string + ordsImagePullSecret: + type: string + ordsPort: + type: integer + ordsPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + replicas: + type: integer + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/database.oracle.com_dataguardbrokers.yaml b/bundle/manifests/database.oracle.com_dataguardbrokers.yaml new file mode 100644 index 00000000..029fb91e --- /dev/null +++ b/bundle/manifests/database.oracle.com_dataguardbrokers.yaml @@ -0,0 +1,217 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + creationTimestamp: null + name: dataguardbrokers.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + singular: dataguardbroker + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.fastStartFailover + name: FSFO + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailover: + type: boolean + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: + type: string + databasesInDataguardConfig: + additionalProperties: + type: string + type: object + externalConnectString: + type: string + fastStartFailover: + type: string + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.fastStartFailover + name: FSFO + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailover: + type: boolean + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + primaryDatabaseRef: + type: string + protectionMode: + enum: + - MaxPerformance + - MaxAvailability + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: + type: string + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: + type: string + databasesInDataguardConfig: + additionalProperties: + type: string + type: object + externalConnectString: + type: string + fastStartFailover: + type: string + primaryDatabase: + type: string + primaryDatabaseRef: + type: string + protectionMode: + type: string + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/config/database.oracle.com_DbcsSystem.yaml b/bundle/manifests/database.oracle.com_dbcssystems.yaml similarity index 50% rename from config/database.oracle.com_DbcsSystem.yaml rename to bundle/manifests/database.oracle.com_dbcssystems.yaml index c342c363..b925812d 100644 --- a/config/database.oracle.com_DbcsSystem.yaml +++ b/bundle/manifests/database.oracle.com_dbcssystems.yaml @@ -1,48 +1,37 @@ - ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null - name: DbcsSystem.database.oracle.com + name: dbcssystems.database.oracle.com spec: group: database.oracle.com names: kind: DbcsSystem listKind: DbcsSystemList - plural: DbcsSystem + plural: dbcssystems singular: dbcssystem scope: Namespaced versions: - - name: v4 + - name: v1alpha1 schema: openAPIV3Schema: - description: DbcsSystem is the Schema for the dbcssystems API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: DbcsSystemSpec defines the desired state of DbcsSystem properties: databaseId: type: string dbBackupId: type: string dbClone: - description: DbCloneConfig defines the configuration for the database - clone properties: dbAdminPaswordSecret: type: string @@ -98,7 +87,6 @@ spec: dbAdminPaswordSecret: type: string dbBackupConfig: - description: DB Backup Config Network Struct properties: autoBackupEnabled: type: boolean @@ -205,44 +193,22 @@ spec: type: string pdbConfigs: items: - description: PDBConfig defines details of PDB struct for DBCS systems properties: freeformTags: additionalProperties: type: string - description: '// Free-form tags for this resource. Each tag - is a simple key-value pair with no predefined name, type, - or namespace. // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). - // Example: `{"Department": "Finance"}`' type: object isDelete: - description: To specify whether to delete the PDB type: boolean pdbAdminPassword: - description: // A strong password for PDB Admin. The password - must be at least nine characters and contain at least two - uppercase, two lowercase, two numbers, and two special characters. - The special characters must be _, \#, or -. type: string pdbName: - description: The name for the pluggable database (PDB). The - name is unique in the context of a Database. The name must - begin with an alphabetic character and can contain a maximum - of thirty alphanumeric characters. Special characters are - not permitted. The pluggable database name should not be same - as the container database name. type: string pluggableDatabaseId: - description: The OCID of the PDB for deletion purposes. type: string shouldPdbAdminAccountBeLocked: - description: // The locked mode of the pluggable database admin - account. If false, the user needs to provide the PDB Admin - Password to connect to it. // If true, the pluggable database - will be locked and user cannot login to it. type: boolean tdeWalletPassword: - description: // The existing TDE wallet password of the CDB. type: string required: - freeformTags @@ -258,7 +224,6 @@ spec: - ociConfigMap type: object status: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: availabilityDomain: type: string @@ -269,7 +234,6 @@ spec: dataStorageSizeInGBs: type: integer dbCloneStatus: - description: DbCloneStatus defines the observed state of DbClone properties: dbAdminPaswordSecret: type: string @@ -301,7 +265,6 @@ spec: type: string dbInfo: items: - description: DbcsSystemStatus defines the observed state of DbcsSystem properties: dbHomeId: type: string @@ -422,6 +385,375 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + databaseId: + type: string + dbBackupId: + type: string + dbClone: + properties: + dbAdminPasswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + tdeWalletPasswordSecret: + type: string + required: + - dbDbUniqueName + - dbName + - displayName + - hostName + - subnetId + type: object + dbSystem: + properties: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPasswordSecret: + type: string + dbBackupConfig: + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string + required: + - availabilityDomain + - compartmentId + - dbAdminPasswordSecret + - hostName + - shape + - subnetId + type: object + hardLink: + type: boolean + id: + type: string + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + ociConfigMap: + type: string + ociSecret: + type: string + pdbConfigs: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + isDelete: + type: boolean + pdbAdminPassword: + type: string + pdbName: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + tdeWalletPassword: + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean + required: + - ociConfigMap + type: object + status: + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbCloneStatus: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + id: + type: string + licenseModel: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + required: + - dbDbUniqueName + - hostName + type: object + dbEdition: + type: string + dbInfo: + items: + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: + type: string + id: + type: string + kmsDetailsStatus: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyId: + type: string + keyName: + type: string + managementEndpoint: + type: string + vaultId: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: + type: integer + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + type: object + type: array + type: object + type: array + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array + required: + - state + type: object + type: object + served: true storage: true subresources: status: {} @@ -429,5 +761,5 @@ status: acceptedNames: kind: "" plural: "" - conditions: [] - storedVersions: [] + conditions: null + storedVersions: null diff --git a/config/database.oracle.com_cdbs.yaml b/bundle/manifests/database.oracle.com_lrests.yaml similarity index 63% rename from config/database.oracle.com_cdbs.yaml rename to bundle/manifests/database.oracle.com_lrests.yaml index 6b1c350c..fb2d9300 100644 --- a/config/database.oracle.com_cdbs.yaml +++ b/bundle/manifests/database.oracle.com_lrests.yaml @@ -1,25 +1,24 @@ - ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null - name: cdbs.database.oracle.com + name: lrests.database.oracle.com spec: group: database.oracle.com names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb + kind: LREST + listKind: LRESTList + plural: lrests + singular: lrest scope: Namespaced versions: - additionalPrinterColumns: - - description: Name of the CDB + - description: Name of the LREST jsonPath: .spec.cdbName - name: CDB Name + name: CDB NAME type: string - description: ' Name of the DB Server' jsonPath: .spec.dbServer @@ -29,47 +28,37 @@ spec: jsonPath: .spec.dbPort name: DB Port type: integer - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - description: Replicas jsonPath: .spec.replicas name: Replicas type: integer - - description: Status of the CDB Resource + - description: Status of the LREST Resource jsonPath: .status.phase name: Status type: string - - description: Error message, if any + - description: Error message if any jsonPath: .status.msg name: Message type: string - name: v1alpha1 + - description: string of the tnsalias + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 schema: openAPIV3Schema: - description: CDB is the Schema for the cdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: CDBSpec defines the desired state of CDB properties: cdbAdminPwd: - description: Password for the CDB Administrator to manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -83,11 +72,8 @@ spec: - secret type: object cdbAdminUser: - description: User in the root container with sysdba priviledges to - manage PDB lifecycle properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -101,12 +87,40 @@ spec: - secret type: object cdbName: - description: Name of the CDB type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object cdbTlsCrt: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -122,7 +136,6 @@ spec: cdbTlsKey: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -136,40 +149,27 @@ spec: - secret type: object dbPort: - description: DB server port type: integer dbServer: - description: Name of the DB server type: string dbTnsurl: type: string - nodeSelector: - additionalProperties: - type: string - description: Node Selector for running the Pod - type: object - ordsImage: - description: ORDS Image Name + deletePdbCascade: + type: boolean + lrestImage: type: string - ordsImagePullPolicy: - description: ORDS Image Pull Policy + lrestImagePullPolicy: enum: - Always - Never type: string - ordsImagePullSecret: - description: The name of the image pull secret in case of a private - docker repository. + lrestImagePullSecret: type: string - ordsPort: - description: ORDS server port. For now, keep it as 8888. TO BE USED - IN FUTURE RELEASE. + lrestPort: type: integer - ordsPwd: - description: Password for user ORDS_PUBLIC_USER + lrestPwd: properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -182,17 +182,17 @@ spec: required: - secret type: object + nodeSelector: + additionalProperties: + type: string + type: object replicas: - description: Number of ORDS Containers to create type: integer serviceName: - description: Name of the CDB Service type: string sysAdminPwd: - description: Password for the CDB System Administrator properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -206,10 +206,8 @@ spec: - secret type: object webServerPwd: - description: Password for the Web Server User properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -223,11 +221,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: CDBSecret defines the secretName properties: key: type: string @@ -242,16 +237,12 @@ spec: type: object type: object status: - description: CDBStatus defines the observed state of CDB properties: msg: - description: Message type: string phase: - description: Phase of the CDB Resource type: string status: - description: CDB Resource Status type: boolean required: - phase @@ -266,5 +257,5 @@ status: acceptedNames: kind: "" plural: "" - conditions: [] - storedVersions: [] + conditions: null + storedVersions: null diff --git a/config/database.oracle.com_pdbs.yaml b/bundle/manifests/database.oracle.com_lrpdbs.yaml similarity index 55% rename from config/database.oracle.com_pdbs.yaml rename to bundle/manifests/database.oracle.com_lrpdbs.yaml index 85af8c1b..c0ccee9d 100644 --- a/config/database.oracle.com_pdbs.yaml +++ b/bundle/manifests/database.oracle.com_lrpdbs.yaml @@ -1,26 +1,21 @@ - ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null - name: pdbs.database.oracle.com + name: lrpdbs.database.oracle.com spec: group: database.oracle.com names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb + kind: LRPDB + listKind: LRPDBList + plural: lrpdbs + singular: lrpdb scope: Namespaced versions: - additionalPrinterColumns: - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - description: Name of the CDB jsonPath: .spec.cdbName name: CDB Name @@ -37,7 +32,7 @@ spec: jsonPath: .status.totalSize name: PDB Size type: string - - description: Status of the PDB Resource + - description: Status of the LRPDB Resource jsonPath: .status.phase name: Status type: string @@ -45,29 +40,27 @@ spec: jsonPath: .status.msg name: Message type: string - name: v1alpha1 + - description: last sqlcode + jsonPath: .status.sqlCode + name: last sqlcode + type: integer + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 schema: openAPIV3Schema: - description: PDB is the Schema for the pdbs API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: PDBSpec defines the desired state of PDB properties: action: - description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. - Map is used to map a Databse PDB to a Kubernetes PDB CR.' enum: - Create - Clone @@ -77,13 +70,12 @@ spec: - Modify - Status - Map + - Alter + - Noaction type: string adminName: - description: The administrator username for the new PDB. This property - is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -97,11 +89,8 @@ spec: - secret type: object adminPwd: - description: The administrator password for the new PDB. This property - is required when the Action property is Create. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -114,70 +103,85 @@ spec: required: - secret type: object + adminpdbPass: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminpdbUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + alterSystem: + type: string + alterSystemParameter: + type: string + alterSystemValue: + type: string asClone: - description: Indicate if 'AS CLONE' option should be used in the command - to plug in a PDB. This property is applicable when the Action property - is PLUG but not required. type: boolean - assertivePdbDeletion: - description: turn on the assertive approach to delete pdb resource - kubectl delete pdb ..... automatically triggers the pluggable database - deletion + assertiveLrpdbDeletion: type: boolean cdbName: - description: Name of the CDB type: string cdbNamespace: - description: CDB Namespace type: string + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object cdbResName: - description: Name of the CDB Custom Resource that runs the ORDS container type: string copyAction: - description: To copy files or not while cloning a PDB enum: - COPY - NOCOPY - MOVE type: string dropAction: - description: Specify if datafiles should be removed or not. The value - can be INCLUDING or KEEP (default). enum: - INCLUDING - KEEP type: string fileNameConversions: - description: Relevant for Create and Plug operations. As defined in - the Oracle Multitenant Database documentation. Values can be a - filename convert pattern or NONE. type: string getScript: - description: Whether you need the script only or execute the script type: boolean - modifyOption: - description: Extra options for opening and closing a PDB - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - description: The name of the new PDB. Relevant for both Create and - Plug Actions. - type: string - pdbState: - description: The target state of the PDB - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: + lrpdbTlsCat: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -190,10 +194,9 @@ spec: required: - secret type: object - pdbTlsCrt: + lrpdbTlsCrt: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -206,10 +209,9 @@ spec: required: - secret type: object - pdbTlsKey: + lrpdbTlsKey: properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -222,36 +224,43 @@ spec: required: - secret type: object + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + parameterScope: + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + - ALTER + type: string + pdbconfigmap: + type: string reuseTempFile: - description: Whether to reuse temp file type: boolean sourceFileNameConversions: - description: This property is required when the Action property is - Plug. As defined in the Oracle Multitenant Database documentation. - Values can be a source filename convert pattern or NONE. type: string sparseClonePath: - description: A Path specified for sparse clone snapshot copy. (Optional) type: string srcPdbName: - description: Name of the Source PDB from which to clone type: string tdeExport: - description: TDE export for unplug operations type: boolean tdeImport: - description: TDE import for plug operations type: boolean tdeKeystorePath: - description: TDE keystore path is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. type: string tdePassword: - description: TDE password if the tdeImport or tdeExport flag is set - to true. Can be used in create, plug or unplug operations properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -265,11 +274,8 @@ spec: - secret type: object tdeSecret: - description: TDE secret is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -283,26 +289,14 @@ spec: - secret type: object tempSize: - description: Relevant for Create and Clone operations. Total size - for temporary tablespace as defined in the Oracle Multitenant Database - documentation. See size_clause description in Database SQL Language - Reference documentation. type: string totalSize: - description: Relevant for create and plug operations. Total size as - defined in the Oracle Multitenant Database documentation. See size_clause - description in Database SQL Language Reference documentation. type: string unlimitedStorage: - description: Relevant for Create and Plug operations. True for unlimited - storage. Even when set to true, totalSize and tempSize MUST be specified - in the request if Action is Create. type: boolean webServerPwd: - description: Password for the Web ServerPDB User properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -316,11 +310,8 @@ spec: - secret type: object webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints properties: secret: - description: PDBSecret defines the secretName properties: key: type: string @@ -334,40 +325,42 @@ spec: - secret type: object xmlFileName: - description: XML metadata filename to be used for Plug or Unplug operations type: string required: - action + - alterSystemParameter + - alterSystemValue + - webServerPwd type: object status: - description: PDBStatus defines the observed state of PDB properties: action: - description: Last Completed Action + type: string + alterSystem: + type: string + bitstat: + type: integer + bitstatstr: type: string connString: - description: PDB Connect String type: string modifyOption: - description: Modify Option of the PDB type: string msg: - description: Message type: string openMode: - description: Open mode of the PDB type: string phase: - description: Phase of the PDB Resource type: string + sqlCode: + type: integer status: - description: PDB Resource Status type: boolean totalSize: - description: Total size of the PDB type: string required: - phase + - sqlCode - status type: object type: object @@ -379,5 +372,5 @@ status: acceptedNames: kind: "" plural: "" - conditions: [] - storedVersions: [] + conditions: null + storedVersions: null diff --git a/config/database.oracle.com_oraclerestdataservices.yaml b/bundle/manifests/database.oracle.com_oraclerestdataservices.yaml similarity index 50% rename from config/database.oracle.com_oraclerestdataservices.yaml rename to bundle/manifests/database.oracle.com_oraclerestdataservices.yaml index 121383fd..acc46597 100644 --- a/config/database.oracle.com_oraclerestdataservices.yaml +++ b/bundle/manifests/database.oracle.com_oraclerestdataservices.yaml @@ -1,10 +1,9 @@ - ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: oraclerestdataservices.database.oracle.com spec: @@ -32,30 +31,22 @@ spec: - jsonPath: .status.apexUrl name: Apex URL type: string + - jsonPath: .status.mongoDbApiAccessUrl + name: MongoDbApi Access URL + type: string name: v1alpha1 schema: openAPIV3Schema: - description: OracleRestDataService is the Schema for the oraclerestdataservices - API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: - description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService properties: adminPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey properties: keepSecret: type: boolean @@ -67,9 +58,30 @@ spec: required: - secretName type: object - apexPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + mongoDbApi: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: properties: keepSecret: type: boolean @@ -81,11 +93,71 @@ spec: required: - secretName type: object + ordsUser: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeName: + type: string + type: object + readinessCheckPeriod: + type: integer + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + properties: + apexConfigured: + type: boolean + apexUrl: + type: string + commonUsersCreated: + type: boolean + databaseActionsUrl: + type: string + databaseApiUrl: + type: string databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and - pullSecrets for POD properties: pullFrom: type: string @@ -97,6 +169,84 @@ spec: - pullFrom type: object loadBalancer: + type: string + mongoDbApi: + type: boolean + mongoDbApiAccessUrl: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: + type: string + status: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + - jsonPath: .status.mongoDbApiAccessUrl + name: MongoDbApi Access URL + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: boolean + mongoDbApi: type: boolean nodeSelector: additionalProperties: @@ -105,8 +255,6 @@ spec: oracleService: type: string ordsPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey properties: keepSecret: type: boolean @@ -121,14 +269,14 @@ spec: ordsUser: type: string persistence: - description: OracleRestDataServicePersistence defines the storage - releated params properties: accessMode: enum: - ReadWriteOnce - ReadWriteMany type: string + setWritePermissions: + type: boolean size: type: string storageClass: @@ -136,13 +284,13 @@ spec: volumeName: type: string type: object + readinessCheckPeriod: + type: integer replicas: minimum: 1 type: integer restEnableSchemas: items: - description: OracleRestDataServicePDBSchemas defines the PDB Schemas - to be ORDS Enabled properties: enable: type: boolean @@ -169,8 +317,6 @@ spec: - ordsPassword type: object status: - description: OracleRestDataServiceStatus defines the observed state of - OracleRestDataService properties: apexConfigured: type: boolean @@ -185,8 +331,6 @@ spec: databaseRef: type: string image: - description: OracleRestDataServiceImage defines the Image source and - pullSecrets for POD properties: pullFrom: type: string @@ -199,6 +343,10 @@ spec: type: object loadBalancer: type: string + mongoDbApi: + type: boolean + mongoDbApiAccessUrl: + type: string ordsInstalled: type: boolean replicas: @@ -206,9 +354,6 @@ spec: serviceIP: type: string status: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' type: string type: object type: object @@ -220,5 +365,5 @@ status: acceptedNames: kind: "" plural: "" - conditions: [] - storedVersions: [] + conditions: null + storedVersions: null diff --git a/bundle/manifests/database.oracle.com_ordssrvs.yaml b/bundle/manifests/database.oracle.com_ordssrvs.yaml new file mode 100644 index 00000000..b97ace72 --- /dev/null +++ b/bundle/manifests/database.oracle.com_ordssrvs.yaml @@ -0,0 +1,495 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + creationTimestamp: null + name: ordssrvs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OrdsSrvs + listKind: OrdsSrvsList + plural: ordssrvs + singular: ordssrvs + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: status + type: string + - jsonPath: .status.workloadType + name: workloadType + type: string + - jsonPath: .status.ordsVersion + name: ordsVersion + type: string + - jsonPath: .status.httpPort + name: httpPort + type: integer + - jsonPath: .status.httpsPort + name: httpsPort + type: integer + - jsonPath: .status.mongoPort + name: MongoPort + type: integer + - jsonPath: .status.restartRequired + name: restartRequired + type: boolean + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.ordsInstalled + name: OrdsInstalled + type: boolean + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + encPrivKey: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + forceRestart: + type: boolean + globalSettings: + properties: + cache.metadata.enabled: + type: boolean + cache.metadata.graphql.expireAfterAccess: + format: int64 + type: integer + cache.metadata.graphql.expireAfterWrite: + format: int64 + type: integer + cache.metadata.jwks.enabled: + type: boolean + cache.metadata.jwks.expireAfterAccess: + format: int64 + type: integer + cache.metadata.jwks.expireAfterWrite: + format: int64 + type: integer + cache.metadata.jwks.initialCapacity: + format: int32 + type: integer + cache.metadata.jwks.maximumSize: + format: int32 + type: integer + cache.metadata.timeout: + format: int64 + type: integer + certSecret: + properties: + cert: + type: string + key: + type: string + secretName: + type: string + required: + - cert + - key + - secretName + type: object + database.api.enabled: + type: boolean + database.api.management.services.disabled: + type: boolean + db.invalidPoolTimeout: + format: int64 + type: integer + debug.printDebugToScreen: + type: boolean + enable.mongo.access.log: + default: false + type: boolean + enable.standalone.access.log: + default: false + type: boolean + error.responseFormat: + type: string + feature.grahpql.max.nesting.depth: + format: int32 + type: integer + icap.port: + format: int32 + type: integer + icap.secure.port: + format: int32 + type: integer + icap.server: + type: string + log.procedure: + type: boolean + mongo.enabled: + type: boolean + mongo.idle.timeout: + format: int64 + type: integer + mongo.op.timeout: + format: int64 + type: integer + mongo.port: + default: 27017 + format: int32 + type: integer + request.traceHeaderName: + type: string + security.credentials.attempts: + format: int32 + type: integer + security.credentials.lock.time: + format: int64 + type: integer + security.disableDefaultExclusionList: + type: boolean + security.exclusionList: + type: string + security.externalSessionTrustedOrigins: + type: string + security.forceHTTPS: + type: boolean + security.httpsHeaderCheck: + type: string + security.inclusionList: + type: string + security.maxEntries: + format: int32 + type: integer + security.verifySSL: + type: boolean + standalone.context.path: + default: /ords + type: string + standalone.http.port: + default: 8080 + format: int32 + type: integer + standalone.https.host: + type: string + standalone.https.port: + default: 8443 + format: int32 + type: integer + standalone.stop.timeout: + format: int64 + type: integer + type: object + image: + type: string + imagePullPolicy: + default: IfNotPresent + enum: + - IfNotPresent + - Always + - Never + type: string + imagePullSecrets: + type: string + poolSettings: + items: + properties: + apex.security.administrator.roles: + type: string + apex.security.user.roles: + type: string + autoUpgradeAPEX: + default: false + type: boolean + autoUpgradeORDS: + default: false + type: boolean + db.adminUser: + type: string + db.adminUser.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.cdb.adminUser: + type: string + db.cdb.adminUser.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.connectionType: + enum: + - basic + - tns + - customurl + type: string + db.credentialsSource: + enum: + - pool + - request + type: string + db.customURL: + type: string + db.hostname: + type: string + db.poolDestroyTimeout: + format: int64 + type: integer + db.port: + format: int32 + type: integer + db.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.servicename: + type: string + db.sid: + type: string + db.tnsAliasName: + type: string + db.username: + default: ORDS_PUBLIC_USER + type: string + db.wallet.zip.service: + type: string + dbWalletSecret: + properties: + secretName: + type: string + walletName: + type: string + required: + - secretName + - walletName + type: object + debug.trackResources: + type: boolean + feature.openservicebroker.exclude: + type: boolean + feature.sdw: + type: boolean + http.cookie.filter: + type: string + jdbc.DriverType: + enum: + - thin + - oci8 + type: string + jdbc.InactivityTimeout: + format: int32 + type: integer + jdbc.InitialLimit: + format: int32 + type: integer + jdbc.MaxConnectionReuseCount: + format: int32 + type: integer + jdbc.MaxConnectionReuseTime: + format: int32 + type: integer + jdbc.MaxLimit: + format: int32 + type: integer + jdbc.MaxStatementsLimit: + format: int32 + type: integer + jdbc.MinLimit: + format: int32 + type: integer + jdbc.SecondsToTrustIdleConnection: + format: int32 + type: integer + jdbc.auth.admin.role: + type: string + jdbc.auth.enabled: + type: boolean + jdbc.cleanup.mode: + type: string + jdbc.statementTimeout: + format: int32 + type: integer + misc.defaultPage: + type: string + misc.pagination.maxRows: + format: int32 + type: integer + owa.trace.sql: + type: boolean + plsql.gateway.mode: + enum: + - disabled + - direct + - proxied + type: string + poolName: + type: string + procedure.preProcess: + type: string + procedure.rest.preHook: + type: string + procedurePostProcess: + type: string + restEnabledSql.active: + type: boolean + security.jwks.connection.timeout: + format: int64 + type: integer + security.jwks.read.timeout: + format: int64 + type: integer + security.jwks.refresh.interval: + format: int64 + type: integer + security.jwks.size: + format: int32 + type: integer + security.jwt.allowed.age: + format: int64 + type: integer + security.jwt.allowed.skew: + format: int64 + type: integer + security.jwt.profile.enabled: + type: boolean + security.requestAuthenticationFunction: + type: string + security.requestValidationFunction: + default: ords_util.authorize_plsql_gateway + type: string + security.validationFunctionType: + enum: + - plsql + - javascript + type: string + soda.defaultLimit: + type: string + soda.maxLimit: + type: string + tnsAdminSecret: + properties: + secretName: + type: string + required: + - secretName + type: object + required: + - db.secret + - poolName + type: object + type: array + replicas: + default: 1 + format: int32 + minimum: 1 + type: integer + workloadType: + default: Deployment + enum: + - Deployment + - StatefulSet + - DaemonSet + type: string + required: + - globalSettings + - image + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + httpPort: + format: int32 + type: integer + httpsPort: + format: int32 + type: integer + mongoPort: + format: int32 + type: integer + ordsInstalled: + type: boolean + ordsVersion: + type: string + restartRequired: + type: boolean + status: + type: string + workloadType: + type: string + required: + - restartRequired + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/database.oracle.com_pdbs.yaml b/bundle/manifests/database.oracle.com_pdbs.yaml new file mode 100644 index 00000000..b1e1032f --- /dev/null +++ b/bundle/manifests/database.oracle.com_pdbs.yaml @@ -0,0 +1,641 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + creationTimestamp: null + name: pdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: PDB + listKind: PDBList + plural: pdbs + singular: pdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + type: boolean + assertivePdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + type: string + pdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbState: + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + type: object + status: + properties: + action: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + status: + type: boolean + totalSize: + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Status of the PDB Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + enum: + - Create + - Clone + - Plug + - Unplug + - Delete + - Modify + - Status + - Map + type: string + adminName: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + adminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + asClone: + type: boolean + assertivePdbDeletion: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbResName: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + type: boolean + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED + type: string + pdbName: + type: string + pdbOrdsPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbOrdsPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbState: + enum: + - OPEN + - CLOSE + type: string + pdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + pdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + reuseTempFile: + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tdeSecret: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + tempSize: + type: string + totalSize: + type: string + unlimitedStorage: + type: boolean + webServerPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: + type: string + required: + - action + type: object + status: + properties: + action: + type: string + connString: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + phase: + type: string + status: + type: boolean + totalSize: + type: string + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/database.oracle.com_shardingdatabases.yaml b/bundle/manifests/database.oracle.com_shardingdatabases.yaml new file mode 100644 index 00000000..f8cd38a8 --- /dev/null +++ b/bundle/manifests/database.oracle.com_shardingdatabases.yaml @@ -0,0 +1,1115 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + creationTimestamp: null + name: shardingdatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: ShardingDatabase + listKind: ShardingDatabaseList + plural: shardingdatabases + singular: shardingdatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbEdition: + type: string + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + fssStorageClass: + type: string + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: string + liveinessCheckPeriod: + type: integer + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + enum: + - enable + - disable + - failed + - force + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string + topicId: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.gsm.state + name: Gsm State + type: string + - jsonPath: .status.gsm.services + name: Services + type: string + - jsonPath: .status.gsm.shards + name: shards + priority: 1 + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + InvitedNodeSubnet: + type: string + catalog: + items: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + dbEdition: + type: string + dbImage: + type: string + dbImagePullSecret: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + nsConfigMap: + type: string + nsSecret: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + required: + - name + - pwdFileName + type: object + fssStorageClass: + type: string + gsm: + items: + properties: + directorName: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + region: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + gsmDevMode: + type: string + gsmImage: + type: string + gsmImagePullSecret: + type: string + gsmService: + items: + properties: + available: + type: string + clbGoal: + type: string + commitOutcome: + type: string + drainTimeout: + type: string + dtp: + type: string + edition: + type: string + failoverDelay: + type: string + failoverMethod: + type: string + failoverPrimary: + type: string + failoverRestore: + type: string + failoverRetry: + type: string + failoverType: + type: string + gdsPool: + type: string + lag: + type: integer + locality: + type: string + name: + type: string + notification: + type: string + pdbName: + type: string + policy: + type: string + preferred: + type: string + prferredAll: + type: string + regionFailover: + type: string + retention: + type: string + role: + type: string + sessionState: + type: string + sqlTransactionProfile: + type: string + stopOption: + type: string + tableFamily: + type: string + tfaPolicy: + type: string + required: + - name + type: object + type: array + gsmShardGroup: + items: + properties: + deployAs: + type: string + name: + type: string + region: + type: string + required: + - name + type: object + type: array + gsmShardSpace: + items: + properties: + chunks: + type: integer + name: + type: string + protectionMode: + type: string + shardGroup: + type: string + required: + - name + type: object + type: array + invitedNodeSubnetFlag: + type: string + isClone: + type: boolean + isDataGuard: + type: boolean + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + isTdeWallet: + type: string + liveinessCheckPeriod: + type: integer + portMappings: + items: + properties: + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + readinessCheckPeriod: + type: integer + replicationType: + type: string + scriptsLocation: + type: string + shard: + items: + properties: + deployAs: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + imagePullPolicy: + type: string + isDelete: + enum: + - enable + - disable + - failed + - force + type: string + label: + type: string + name: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + pvAnnotations: + additionalProperties: + type: string + type: object + pvMatchLabels: + additionalProperties: + type: string + type: object + pvcName: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + shardGroup: + type: string + shardRegion: + type: string + shardSpace: + type: string + storageSizeInGb: + format: int32 + type: integer + required: + - name + type: object + type: array + shardBuddyRegion: + type: string + shardConfigName: + type: string + shardRegion: + items: + type: string + type: array + shardingType: + type: string + stagePvcName: + type: string + storageClass: + type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string + topicId: + type: string + required: + - catalog + - dbImage + - gsm + - gsmImage + - shard + type: object + status: + properties: + catalogs: + additionalProperties: + type: string + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gsm: + properties: + details: + additionalProperties: + type: string + type: object + externalConnectStr: + type: string + internalConnectStr: + type: string + services: + type: string + shards: + additionalProperties: + type: string + type: object + state: + type: string + type: object + shards: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/database.oracle.com_singleinstancedatabases.yaml b/bundle/manifests/database.oracle.com_singleinstancedatabases.yaml new file mode 100644 index 00000000..2a633c70 --- /dev/null +++ b/bundle/manifests/database.oracle.com_singleinstancedatabases.yaml @@ -0,0 +1,717 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + creationTimestamp: null + name: singleinstancedatabases.database.oracle.com +spec: + group: database.oracle.com + names: + kind: SingleInstanceDatabase + listKind: SingleInstanceDatabaseList + plural: singleinstancedatabases + singular: singleinstancedatabase + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + convertToSnapshotStandby: + type: boolean + createAs: + enum: + - primary + - standby + - clone + - truecache + type: string + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + resources: + properties: + limits: + properties: + cpu: + type: string + memory: + type: string + type: object + requests: + properties: + cpu: + type: string + memory: + type: string + type: object + type: object + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + trueCacheServices: + items: + type: string + type: array + required: + - image + type: object + status: + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + convertToSnapshotStandby: + type: boolean + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBroker: + type: string + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: false + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + - additionalPrinterColumns: + - jsonPath: .status.edition + name: Edition + type: string + - jsonPath: .status.sid + name: Sid + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.connectString + name: Connect Str + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.tcpsConnectString + name: TCPS Connect Str + type: string + - jsonPath: .status.tcpsPdbConnectString + name: TCPS Pdb Connect Str + priority: 1 + type: string + - jsonPath: .status.oemExpressUrl + name: Oem Express Url + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: + properties: + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string + required: + - secretName + type: object + archiveLog: + type: boolean + charset: + type: string + convertToSnapshotStandby: + type: boolean + createAs: + enum: + - primary + - standby + - clone + - truecache + type: string + edition: + enum: + - standard + - enterprise + - express + - free + type: string + enableTCPS: + type: boolean + flashBack: + type: boolean + forceLog: + type: boolean + image: + properties: + prebuiltDB: + type: boolean + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + listenerPort: + type: integer + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + primaryDatabaseRef: + type: string + readinessCheckPeriod: + type: integer + replicas: + type: integer + resources: + properties: + limits: + properties: + cpu: + type: string + memory: + type: string + type: object + requests: + properties: + cpu: + type: string + memory: + type: string + type: object + type: object + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sid: + maxLength: 12 + pattern: ^[a-zA-Z0-9]+$ + type: string + tcpsCertRenewInterval: + type: string + tcpsListenerPort: + type: integer + tcpsTlsSecret: + type: string + trueCacheServices: + items: + type: string + type: array + required: + - image + type: object + status: + properties: + apexInstalled: + type: boolean + archiveLog: + type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string + charset: + type: string + clientWalletLoc: + type: string + clusterConnectString: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + connectString: + type: string + convertToSnapshotStandby: + type: boolean + createdAs: + type: string + datafilesCreated: + default: "false" + type: string + datafilesPatched: + default: "false" + type: string + dgBroker: + type: string + edition: + type: string + flashBack: + type: string + forceLog: + type: string + initParams: + properties: + cpuCount: + type: integer + pgaAggregateTarget: + type: integer + processes: + type: integer + sgaTarget: + type: integer + type: object + initPgaSize: + type: integer + initSgaSize: + type: integer + isTcpsEnabled: + default: false + type: boolean + nodes: + items: + type: string + type: array + oemExpressUrl: + type: string + ordsReference: + type: string + pdbConnectString: + type: string + pdbName: + type: string + persistence: + properties: + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeClaimAnnotation: + type: string + type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string + releaseUpdate: + type: string + replicas: + type: integer + role: + type: string + sid: + type: string + standbyDatabases: + additionalProperties: + type: string + type: object + status: + type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string + required: + - isTcpsEnabled + - persistence + - tcpsTlsSecret + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/observability.oracle.com_databaseobservers.yaml b/bundle/manifests/observability.oracle.com_databaseobservers.yaml new file mode 100644 index 00000000..652d773e --- /dev/null +++ b/bundle/manifests/observability.oracle.com_databaseobservers.yaml @@ -0,0 +1,6995 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + creationTimestamp: null + name: databaseobservers.observability.oracle.com +spec: + group: observability.oracle.com + names: + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + shortNames: + - dbobserver + - dbobservers + singular: databaseobserver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + configuration: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podTemplate: + properties: + labels: + additionalProperties: + type: string + type: object + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + service: + properties: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string + scopes: + items: + type: string + type: array + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + enum: + - http + - https + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + trackTimestampsStaleness: + type: boolean + type: object + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object + type: object + type: object + replicas: + format: int32 + type: integer + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + configuration: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podTemplate: + properties: + labels: + additionalProperties: + type: string + type: object + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + service: + properties: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string + scopes: + items: + type: string + type: array + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + enum: + - http + - https + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + trackTimestampsStaleness: + type: boolean + type: object + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object + type: object + type: object + replicas: + format: int32 + type: integer + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.exporterConfig + name: ExporterConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + configuration: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + properties: + dbConnectionString: + properties: + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + key: + type: string + secret: + type: string + vaultOCID: + type: string + vaultSecretName: + type: string + type: object + dbUser: + properties: + key: + type: string + secret: + type: string + type: object + dbWallet: + properties: + key: + type: string + secret: + type: string + type: object + type: object + exporter: + properties: + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podTemplate: + properties: + labels: + additionalProperties: + type: string + type: object + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + service: + properties: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + filename: + type: string + path: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + ociConfig: + properties: + configMapName: + type: string + secretName: + type: string + type: object + prometheus: + properties: + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string + scopes: + items: + type: string + type: array + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + enum: + - http + - https + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + trackTimestampsStaleness: + type: boolean + type: object + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object + type: object + type: object + replicas: + format: int32 + type: integer + sidecarVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + sidecars: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + exporterConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - exporterConfig + - version + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/oracle-database-operator-controller-manager-metrics-service_v1_service.yaml b/bundle/manifests/oracle-database-operator-controller-manager-metrics-service_v1_service.yaml new file mode 100644 index 00000000..ea25c27f --- /dev/null +++ b/bundle/manifests/oracle-database-operator-controller-manager-metrics-service_v1_service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + control-plane: controller-manager + name: oracle-database-operator-controller-manager-metrics-service +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +status: + loadBalancer: {} diff --git a/bundle/manifests/oracle-database-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml b/bundle/manifests/oracle-database-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml new file mode 100644 index 00000000..f479f494 --- /dev/null +++ b/bundle/manifests/oracle-database-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -0,0 +1,10 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: oracle-database-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get diff --git a/bundle/manifests/oracle-database-operator-webhook-service_v1_service.yaml b/bundle/manifests/oracle-database-operator-webhook-service_v1_service.yaml new file mode 100644 index 00000000..53f17480 --- /dev/null +++ b/bundle/manifests/oracle-database-operator-webhook-service_v1_service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + name: oracle-database-operator-webhook-service +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager +status: + loadBalancer: {} diff --git a/bundle/manifests/oracle-database-operator.clusterserviceversion.yaml b/bundle/manifests/oracle-database-operator.clusterserviceversion.yaml new file mode 100644 index 00000000..c28637bd --- /dev/null +++ b/bundle/manifests/oracle-database-operator.clusterserviceversion.yaml @@ -0,0 +1,1442 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [ + { + "apiVersion": "database.oracle.com/v1alpha1", + "kind": "CDB", + "metadata": { + "name": "cdb-dev", + "namespace": "oracle-database-operator-system" + }, + "spec": { + "cdbAdminPwd": { + "secret": { + "key": "cdbadmin_pwd", + "secretName": "cdb1-secret" + } + }, + "cdbAdminUser": { + "secret": { + "key": "cdbadmin_user", + "secretName": "cdb1-secret" + } + }, + "cdbName": "devcdb", + "dbPort": 1521, + "dbServer": "172.17.0.4", + "ordsImage": "", + "ordsImagePullPolicy": "Always", + "ordsPwd": { + "secret": { + "key": "ords_pwd", + "secretName": "cdb1-secret" + } + }, + "replicas": 1, + "serviceName": "devdb.example.com", + "sysAdminPwd": { + "secret": { + "key": "sysadmin_pwd", + "secretName": "cdb1-secret" + } + }, + "webServerPwd": { + "secret": { + "key": "webserver_pwd", + "secretName": "cdb1-secret" + } + }, + "webServerUser": { + "secret": { + "key": "webserver_user", + "secretName": "cdb1-secret" + } + } + } + }, + { + "apiVersion": "database.oracle.com/v1alpha1", + "kind": "DbcsSystem", + "metadata": { + "name": "dbcssystem-sample" + }, + "spec": { + "foo": "bar" + } + }, + { + "apiVersion": "database.oracle.com/v1alpha1", + "kind": "PDB", + "metadata": { + "labels": { + "cdb": "cdb-dev" + }, + "name": "pdb1", + "namespace": "oracle-database-operator-system" + }, + "spec": { + "action": "Create", + "adminName": { + "secret": { + "key": "sysadmin_user", + "secretName": "pdb1-secret" + } + }, + "adminPwd": { + "secret": { + "key": "sysadmin_pwd", + "secretName": "pdb1-secret" + } + }, + "cdbName": "devcdb", + "cdbResName": "cdb-dev", + "fileNameConversions": "NONE", + "pdbName": "pdbdev", + "tempSize": "100M", + "totalSize": "1G" + } + }, + { + "apiVersion": "database.oracle.com/v4", + "kind": "AutonomousContainerDatabase", + "metadata": { + "name": "autonomouscontainerdatabase-sample" + }, + "spec": { + "autonomousExadataVMClusterOCID": "ocid1.autonomousexainfrastructure...", + "compartmentOCID": "ocid1.compartment... OR ocid1.tenancy...", + "displayName": "newACD", + "ociConfig": { + "configMapName": "oci-cred", + "secretName": "oci-privatekey" + } + } + }, + { + "apiVersion": "database.oracle.com/v4", + "kind": "AutonomousDatabase", + "metadata": { + "name": "autonomousdatabase-sample" + }, + "spec": { + "action": "Create", + "details": { + "adminPassword": { + "k8sSecret": { + "name": "admin-password" + } + }, + "compartmentId": "ocid1.compartment... OR ocid1.tenancy...", + "cpuCoreCount": 1, + "dataStorageSizeInTBs": 1, + "dbName": "NewADB", + "displayName": "NewADB" + }, + "ociConfig": { + "configMapName": "oci-cred", + "secretName": "oci-privatekey" + } + } + }, + { + "apiVersion": "database.oracle.com/v4", + "kind": "AutonomousDatabaseBackup", + "metadata": { + "name": "autonomousdatabasebackup-sample" + }, + "spec": { + "displayName": "autonomousdatabasebackup-sample", + "isLongTermBackup": true, + "ociConfig": { + "configMapName": "oci-cred", + "secretName": "oci-privatekey" + }, + "retentionPeriodInDays": 90, + "target": { + "k8sADB": { + "name": "autonomousdatabase-sample" + } + } + } + }, + { + "apiVersion": "database.oracle.com/v4", + "kind": "SingleInstanceDatabase", + "metadata": { + "name": "sidb-sample", + "namespace": "default" + }, + "spec": { + "adminPassword": { + "secretName": "db-admin-secret" + }, + "archiveLog": true, + "charset": "AL32UTF8", + "edition": "enterprise", + "image": { + "pullFrom": "container-registry.oracle.com/database/enterprise_ru:19", + "pullSecrets": "oracle-container-registry-secret" + }, + "pdbName": "orclpdb1", + "persistence": { + "accessMode": "ReadWriteOnce", + "size": "100Gi", + "storageClass": "oci-bv" + }, + "replicas": 1, + "sid": "ORCL1" + } + }, + { + "apiVersion": "observability.oracle.com/v1alpha1", + "kind": "DatabaseObserver", + "metadata": { + "name": "obs-sample" + }, + "spec": { + "database": { + "dbConnectionString": { + "key": "connection", + "secret": "db-secret" + }, + "dbPassword": { + "key": "password", + "secret": "db-secret" + }, + "dbUser": { + "key": "username", + "secret": "db-secret" + }, + "dbWallet": { + "secret": "instance-wallet" + } + }, + "exporter": { + "configuration": { + "configmap": { + "configmapName": "devcm-oradevdb-config", + "key": "config.toml" + } + }, + "image": "container-registry.oracle.com/database/observability-exporter:latest", + "service": { + "port": 9161 + } + }, + "ociConfig": { + "configMapName": "oci-cred", + "secretName": "oci-privatekey" + }, + "prometheus": { + "labels": { + "app": "app-sample-label" + }, + "port": "metrics" + }, + "replicas": 1 + } + } + ] + capabilities: Seamless Upgrades + categories: Database + containerImage: container-registry.oracle.com/database/operator:1.2.0 + createdAt: "2025-04-10T20:09:48Z" + operators.operatorframework.io/builder: operator-sdk-v1.39.2 + operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 + name: oracle-database-operator.v1.2.0 + namespace: oracle-database-operator-system +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases + API + displayName: Autonomous Container Database + kind: AutonomousContainerDatabase + name: autonomouscontainerdatabases.database.oracle.com + version: v1alpha1 + - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases + API + displayName: Autonomous Container Database + kind: AutonomousContainerDatabase + name: autonomouscontainerdatabases.database.oracle.com + version: v4 + - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups + API + displayName: Autonomous Database Backup + kind: AutonomousDatabaseBackup + name: autonomousdatabasebackups.database.oracle.com + version: v1alpha1 + - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups + API + displayName: Autonomous Database Backup + kind: AutonomousDatabaseBackup + name: autonomousdatabasebackups.database.oracle.com + version: v4 + - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores + API + displayName: Autonomous Database Restore + kind: AutonomousDatabaseRestore + name: autonomousdatabaserestores.database.oracle.com + version: v1alpha1 + - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores + API + displayName: Autonomous Database Restore + kind: AutonomousDatabaseRestore + name: autonomousdatabaserestores.database.oracle.com + version: v4 + - description: AutonomousDatabase is the Schema for the autonomousdatabases API + displayName: Autonomous Database + kind: AutonomousDatabase + name: autonomousdatabases.database.oracle.com + version: v1alpha1 + - description: AutonomousDatabase is the Schema for the autonomousdatabases API + displayName: Autonomous Database + kind: AutonomousDatabase + name: autonomousdatabases.database.oracle.com + version: v4 + - description: CDB is the Schema for the cdbs API + displayName: CDB + kind: CDB + name: cdbs.database.oracle.com + version: v1alpha1 + - description: CDB is the Schema for the cdbs API + displayName: CDB + kind: CDB + name: cdbs.database.oracle.com + version: v4 + - description: DatabaseObserver is the Schema for the databaseobservers API + displayName: Database Observer + kind: DatabaseObserver + name: databaseobservers.observability.oracle.com + version: v1 + - description: DatabaseObserver is the Schema for the databaseobservers API + displayName: Database Observer + kind: DatabaseObserver + name: databaseobservers.observability.oracle.com + version: v1alpha1 + - description: DatabaseObserver is the Schema for the databaseobservers API + displayName: Database Observer + kind: DatabaseObserver + name: databaseobservers.observability.oracle.com + version: v4 + - description: DataguardBroker is the Schema for the dataguardbrokers API + displayName: Dataguard Broker + kind: DataguardBroker + name: dataguardbrokers.database.oracle.com + version: v1alpha1 + - description: DataguardBroker is the Schema for the dataguardbrokers API + displayName: Dataguard Broker + kind: DataguardBroker + name: dataguardbrokers.database.oracle.com + version: v4 + - description: DbcsSystem is the Schema for the dbcssystems API + displayName: Dbcs System + kind: DbcsSystem + name: dbcssystems.database.oracle.com + version: v1alpha1 + - description: DbcsSystem is the Schema for the dbcssystems API + displayName: Dbcs System + kind: DbcsSystem + name: dbcssystems.database.oracle.com + version: v4 + - description: LREST is the Schema for the lrests API + displayName: LREST + kind: LREST + name: lrests.database.oracle.com + version: v4 + - description: LRPDB is the Schema for the pdbs API + displayName: LRPDB + kind: LRPDB + name: lrpdbs.database.oracle.com + version: v4 + - description: OracleRestDataService is the Schema for the oraclerestdataservices + API + displayName: Oracle Rest Data Service + kind: OracleRestDataService + name: oraclerestdataservices.database.oracle.com + version: v1alpha1 + - description: OracleRestDataService is the Schema for the oraclerestdataservices + API + displayName: Oracle Rest Data Service + kind: OracleRestDataService + name: oraclerestdataservices.database.oracle.com + version: v4 + - description: OrdsSrvs is the Schema for the ordssrvs API + displayName: Ords Srvs + kind: OrdsSrvs + name: ordssrvs.database.oracle.com + statusDescriptors: + - displayName: Conditions + path: conditions + version: v4 + - description: PDB is the Schema for the pdbs API + displayName: PDB + kind: PDB + name: pdbs.database.oracle.com + version: v1alpha1 + - description: PDB is the Schema for the pdbs API + displayName: PDB + kind: PDB + name: pdbs.database.oracle.com + version: v4 + - description: ShardingDatabase is the Schema for the shardingdatabases API + displayName: Sharding Database + kind: ShardingDatabase + name: shardingdatabases.database.oracle.com + version: v1alpha1 + - description: ShardingDatabase is the Schema for the shardingdatabases API + displayName: Sharding Database + kind: ShardingDatabase + name: shardingdatabases.database.oracle.com + version: v4 + - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases + API + displayName: Single Instance Database + kind: SingleInstanceDatabase + name: singleinstancedatabases.database.oracle.com + version: v1alpha1 + - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases + API + displayName: Single Instance Database + kind: SingleInstanceDatabase + name: singleinstancedatabases.database.oracle.com + version: v4 + description: | + As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released Oracle Database Operator for Kubernetes (OraOperator or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating Oracle Database lifecycle management. + In this v1.2.0 production release, OraOperator supports the following database configurations and infrastructure: + ##Supported Database Configurations: + * Oracle Cloud Infrastructure (OCI) Databases + * Oracle Autonomous Database Serverless (ADB-S) + * Oracle Autonomous Database on Dedicated on (ADB-D) + * Oracle Base Database Cloud Service (Base DB) + * Containerized Database deployments on Kubernetes + * Oracle Kubernetes Engine (OKE) and Oracle Cloud Native Environments (OCNE) + * Single Instance databases (SIDB) + * Globally Distributed Databases (Shared) + * Oracle Data Guard + * True Cache (Preview release) + * Oracle Database Observability + * On-Premises Databases + * Oracle Multitenant Databases (CDB/PDBs management) + * ORDS Services + + Refer to full feature list and capabilities: [https://github.com/oracle/oracle-database-operator/blob/main/README.md](https://github.com/oracle/oracle-database-operator/blob/main/README.md) + displayName: Oracle Database Operator + icon: + - base64data: iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAJjUlEQVR42u3cfcwcRQHH8S9PH0BokZfCVBgpOgjyFjRoQIQQkLeA0PLWqgQMFDVgja9AChIKKCEKSgQEQVsQJGKxtNCAvAi2vJiCqAQMUpQRMKM4vFiCQEUo/jH7kOt19m7vbveK8fdJLukzMzuzczc7OzszWxAREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREZH/X2tVSRStmwi8B5gErN1nWS8DAVhmgl9ZsdwpVc+xoteKc/iTCf7VujKN1o0A+xef5cDPTfCP1XjeY+VsAWwFTATGDZjdPSb4F6J1U9sjTPA31n3uXeq1MfBe4F30376ADo0lWjcBOAa4EHhHzXW4FzgDuNsE/2aHc3gJmFBz2WMuBc4ywT87SCbRunWAB4APtEV92gR/zaAnGa2bBJwInFVz/SeY4F+O1q32/Zvg6+xEyuq1PnA08F1gg7ryHSkpbDrwEnAZ9TdmgD2BxcDj0bptG8i/is8DMVq394D5zGb1xgxwdbRum34zjdaNi9adAjxD/Y15PxP8yzXn2UvdDiPdsX9IjY0ZOHOVKzFaNw64Apgx5DoelrvNNdxDt9rDBP/rXg+K1u0EPNIhyWPADp3uQiX5bggsIX+hDOp6E/z0lrKG1kMXQ7OLgJkNZL8SWO+tHjpatxbwM4bfmAEWRuuOWAPljrmvGGJVVlz8d3ZJth1wXI/5jgeW0UxjhnRnGrqifV1FM40ZYDcT/GujLQGnAUd2OGAu6Qd8oY/CxgGTgWOB3UvSzI/W7WiCf7RLXgcNUOnJwOUlcScCF/SQ10mAqZBubrTuVhP837sljNZB6lQmdUh2AbCUdMvu1Ssm+Of6OK4OXyb9/mV+DNxOf+0LE/yDUDwURuu2A/5YknYWcLEJ/pU6ahWt2wq4FtgjE/08YMZmQUqGHHua4O8boPx1gNuAvTPRI1WGB0UdnsxEPQzsnAm/HTjQBN8t36nAwpLoTwDzTfBv9Fv3kjIbH3JE6xzwREn0bOA7dY3px4Ycl5bEH2CC/1ZdjRnABP8UsBfwg0z0RODgusoqKf810uxNzsbdjm/pRXOOIP1A7Q4ADu2S7yjljXl7E/y8uhvzEF1YEj7FBH9OnQ+oI9G6zYF9MnEzTfB3NFG7ogf+ApDrsr7XRJlt/lYSvmGFY6cDu2XCZ5ngnwC+XXLcTcXDXpmyodheTcxpD0u0bjNgSibqZBP8orrLGyH1HjlXNFnRorc5KhPlioWcJo2WhL/e6aBo3abke+eVFL2QCX4F+eEMpDnXMsdnwu4ywd/T8HfRtLLv4pImChsFDsmEn2aCf73XzPrwUEn4+4Gep9F68LGS8G4PTN8vCf9oMZQBwAS/JFp3PTCtLd2MaN3ckmeAXIP+ZoPfwbCUDSFXFMO3Om03QlrkaLd4GDUtHsByPd7WTZUZrdsLuDUTdX+n5fBo3b6k4Ua7K0zw92fCy6an7o3WVV2seqRiurez3QfPopI5Jvhlo+SniPqaOunT05mwjmPZaN2WwKPAmy0fOvw99u/NOmR7aofyxgO/7OU4E/yz0brjSHOv7c4oPt3U9jC+Bk0aPItKvgZpDJ27zVZ5OKrLFpmwlzodYIL/K2k+egPgncX5bghsRJqp2KT4TAQ2LT6dGvONJvi7O8R/oyR8qgn+xQ7HXUP+wffrxSpjN+v195W+rQxj3vuQsd9hhDRJ326P3vLrTzGGOjoT9Zdux5rg7yWtxA3qsZJzGDvHXYCvZKIWAzd1OceVlE/X3VmsNnayfQ31W9MeaDj/W0zwN4/9MQosYvUv/YJo3UVVt3kOYAfyO/4qTVOZ4JcVu9EeAjbvo/xLSNNH/85FRuvWBsp67guBKRUfbM4jrcSucvqk1caxp/3rgE+2pfkqaWfi/7JfkO8w1m5i4mGUtGrWbhzwKdKKXiOKtf0rM1HPAbFqPib4GK17H2lRYv8Kh7xIWkj6kQned0n7JWB8JnwmaSfiFgzm4mjdomKxaQ6rN+jDo3UfMsH/dsBy1qSy/S4zaGBqeMQE/zTwu0zcT6J1u/WaYRVFr3YOsGsmema3JeJ2xUrmQcDFHZKdU9R3IxP86d0ac7Rua+D8XBRpP0iVi6eKecXFXXYneDBaN7mmsoau2MOyJBN1eTHjVKuxpe/PlMQvjdadUGGsV1mxcjSP8qf8Bf3ka4J/wwT/RdImmJwzSbv6uj5oFQ1sYUn0fkVZjwJn1/CV7ApM77Ik/1S0rq4LaE0o2+G3JFp3UjG0q8Vb49do3fnAyR3SngvcQ3rFqFfjAEtaaJjWId2uJvjftJxTX5uTonWHAzeURQMf7LT7LVp3LHB1Jup8E/ypLenWBVb08X3kbEbanLWU/J0L0uzP2aQ76qBTeq+a4B/ObU4CPlJTnVodTOpUypxH6smXD1JIa4MeBX5FfqFlGGaY4FcZUw+y264YLi3tkGQXE/zvM8dNIr0lkjO+faNWtG5n0sXai1syYfNN8EdF6zYhNeymHWmCv6GkQdftKtIo4GbgwCYLan9jZV1gPvDxIVSy1fEm+KvaAwfdPlqMg//cIcnhJviFbccsIr8dYD8TfLcN/ZVE6y4HPpeJOsAEf0dxUf2BNH/ehMXAPiZ4htSgNzbBLy+27l5Lfg9PLVZ5p7CYvjqU4b3V8B/gw7nGXIdi99umlO/FXRCtmzU29RatO4h8Y15QV2MuzCoJvz1aN8EE/w9gS3p74aAXx/T64D2AKSb45fDW1t1plD+zDWrf1V6SNcG/aYK/jDSmm917npU8Tdo7vH7TU1Im+OeBnShfBDkP+Gm0bgPyQwFIb7PUeU7/pHwx59wizQoT/CmAI793vF+fNcGHlr+P7zun7m4jrXO01h0T/BzSKu7pNZZ1nQn+rqr/L8dkYFvSvGu/T6T/Ap4CHjfBV9orEq07IRN8pwn+yV4LL17Q7PTj3UV+F95zTfw/FcVMSvb9zeIHz53/1sA2pEWZfmeermxfMIvW7Uh6EB3pL8tSN5vgn+mWqNibsw3wbvpvXwuqtisREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREWnxX2ox1/vZSvwPAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI0LTA4LTEzVDE5OjUyOjMxKzAwOjAwsDIMcAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNC0wOC0xM1QxOTo1MjozMSswMDowMMFvtMwAAABVdEVYdHN2Zzpjb21tZW50ACBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyBFB1wTAAAAAElFTkSuQmCC + mediatype: image/png + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: default + deployments: + - label: + control-plane: controller-manager + name: oracle-database-operator-controller-manager + spec: + replicas: 3 + selector: + matchLabels: + control-plane: controller-manager + strategy: {} + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --enable-leader-election + command: + - /manager + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.annotations['olm.targetNamespaces'] + image: container-registry.oracle.com/database/operator:1.2.0 + imagePullPolicy: Always + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + resources: + limits: + cpu: 400m + memory: 400Mi + requests: + cpu: 400m + memory: 400Mi + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - "" + resources: + - configmaps + - containers + - deployments + - events + - namespaces + - persistentvolumeclaims + - pods + - pods/exec + - pods/log + - replicasets + - secrets + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - configmaps/status + - daemonsets/status + - deployments/status + - services/status + - statefulsets/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - secrets/status + verbs: + - get + - apiGroups: + - '''''' + resources: + - statefulsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - configmaps + verbs: + - get + - list + - apiGroups: + - apps + resources: + - daemonsets + - deployments + - pods + - replicasets + - services + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update + - apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases + - autonomousdatabases + - cdbs + - dataguardbrokers + - dbcssystems + - events + - lrests + - lrpdbs + - oraclerestdataservices + - ordssrvs + - pdbs + - shardingdatabases + - singleinstancedatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases/status + - autonomousdatabasebackups/status + - autonomousdatabaserestores/status + - cdbs/status + - dataguardbrokers/status + - dbcssystems/status + - lrests/status + - lrpdbs/status + - oraclerestdataservices/status + - ordssrvs/status + - pdbs/status + - shardingdatabases/status + - singleinstancedatabases/status + verbs: + - get + - patch + - update + - apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups + - autonomousdatabaserestores + verbs: + - create + - delete + - get + - list + - update + - watch + - apiGroups: + - database.oracle.com + resources: + - autonomousdatabases/status + verbs: + - patch + - update + - apiGroups: + - database.oracle.com + resources: + - cdbs/finalizers + - dataguardbrokers/finalizers + - lrests/finalizers + - oraclerestdataservices/finalizers + - ordssrvs/finalizers + - singleinstancedatabases/finalizers + verbs: + - update + - apiGroups: + - database.oracle.com + resources: + - dbcssystems/finalizers + - lrpdbs/finalizers + - pdbs/finalizers + - shardingdatabases/finalizers + verbs: + - create + - delete + - get + - patch + - update + - apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - observability.oracle.com + resources: + - databaseobservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - observability.oracle.com + resources: + - databaseobservers/finalizers + verbs: + - update + - apiGroups: + - observability.oracle.com + resources: + - databaseobservers/status + verbs: + - get + - patch + - update + - apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch + serviceAccountName: default + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - Oracle + - Database + - Operator + links: + - name: Oracle Database Operator + url: https://github.com/oracle/oracle-database-operator + maturity: alpha + provider: + name: Oracle + replaces: oracle-database-operator.v1.1.0 + version: 1.2.0 + webhookdefinitions: + - admissionReviewVersions: + - v1alpha1 + - v1 + - v4 + containerPort: 443 + conversionCRDs: + - autonomouscontainerdatabases.database.oracle.com + - autonomousdatabasebackups.database.oracle.com + - autonomousdatabaserestores.database.oracle.com + - autonomousdatabases.database.oracle.com + deploymentName: oracle-database-operator-controller-manager + generateName: cautonomouscontainerdatabasesautonomousdatabasebackupsautonomousdatabaserestoresautonomousdatabases.kb.io + sideEffects: None + targetPort: 9443 + type: ConversionWebhook + webhookPath: /convert + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mautonomousdatabasebackupv1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mautonomousdatabasebackupv4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v4-autonomousdatabasebackup + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v4-cdb + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-observability-oracle-com-v4-databaseobserver + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v1alpha1-dataguardbroker + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mdbcssystemv1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - dbcssystems + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v4-dbcssystem + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mdbcssystemv4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - dbcssystems + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v4-dbcssystem + - admissionReviewVersions: + - v4 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v4-lrest + - admissionReviewVersions: + - v4 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v4-lrpdb + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: moraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v1alpha1-oraclerestdataservice + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v4-pdb + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mshardingdatabasev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v1alpha1-shardingdatabase + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: mshardingdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - shardingdatabases + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v4-shardingdatabase + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: msingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - singleinstancedatabases + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-database-oracle-com-v1alpha1-singleinstancedatabase + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vautonomouscontainerdatabasev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomouscontainerdatabases + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vautonomouscontainerdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - autonomouscontainerdatabases + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v4-autonomouscontainerdatabase + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vautonomousdatabasebackupv1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v1alpha1-autonomousdatabasebackup + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vautonomousdatabasebackupv4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabasebackups + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v4-autonomousdatabasebackup + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vautonomousdatabaserestorev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabaserestores + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v1alpha1-autonomousdatabaserestore + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vautonomousdatabaserestorev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabaserestores + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v4-autonomousdatabaserestore + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vautonomousdatabasev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v1alpha1-autonomousdatabase + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vcdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - cdbs + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v4-cdb + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vdatabaseobserver.kb.io + rules: + - apiGroups: + - observability.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - databaseobservers + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-observability-oracle-com-v4-databaseobserver + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vdataguardbroker.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - dataguardbrokers + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v1alpha1-dataguardbroker + - admissionReviewVersions: + - v4 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vlrest.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrests + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v4-lrest + - admissionReviewVersions: + - v4 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vlrpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - lrpdbs + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v4-lrpdb + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: voraclerestdataservice.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - oraclerestdataservices + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v1alpha1-oraclerestdataservice + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vpdb.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - pdbs + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v4-pdb + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vshardingdatabasev1alpha1.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v1alpha1-shardingdatabase + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vshardingdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - shardingdatabases + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v4-shardingdatabase + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: oracle-database-operator-controller-manager + failurePolicy: Fail + generateName: vsingleinstancedatabase.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - singleinstancedatabases + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-database-oracle-com-v1alpha1-singleinstancedatabase diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml new file mode 100644 index 00000000..cf18a9f1 --- /dev/null +++ b/bundle/metadata/annotations.yaml @@ -0,0 +1,10 @@ +annotations: + # Core bundle annotations. + operators.operatorframework.io.bundle.mediatype.v1: registry+v1 + operators.operatorframework.io.bundle.manifests.v1: manifests/ + operators.operatorframework.io.bundle.metadata.v1: metadata/ + operators.operatorframework.io.bundle.package.v1: oracle-database-operator + operators.operatorframework.io.bundle.channels.v1: alpha + operators.operatorframework.io.metrics.builder: operator-sdk-v1.39.2 + operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 + operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml index 6b331894..320f647e 100644 --- a/config/certmanager/certificate.yaml +++ b/config/certmanager/certificate.yaml @@ -21,10 +21,10 @@ metadata: name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml namespace: system spec: - # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + # SERVICE_NAME_PLACEHOLDER and SERVICE_NAMESPACE_PLACEHOLDER will be substituted by kustomize dnsNames: - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + - SERVICE_NAME_PLACEHOLDER.SERVICE_NAMESPACE_PLACEHOLDER.svc + - SERVICE_NAME_PLACEHOLDER.SERVICE_NAMESPACE_PLACEHOLDER.svc.cluster.local issuerRef: kind: Issuer name: selfsigned-issuer diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 726521b0..73e00151 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -23,7 +23,8 @@ resources: - bases/database.oracle.com_ordssrvs.yaml # +kubebuilder:scaffold:crdkustomizeresource -patchesStrategicMerge: +patches: + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_provshards.yaml @@ -35,35 +36,34 @@ patchesStrategicMerge: #- patches/webhook_in_dbcssystems.yaml #- patches/webhook_in_dataguardbrokers.yaml #- patches/webhook_in_databaseobservers.yaml -- patches/webhook_in_autonomousdatabases.yaml -- patches/webhook_in_autonomousdatabasebackups.yaml -- patches/webhook_in_autonomousdatabaserestores.yaml -- patches/webhook_in_autonomouscontainerdatabases.yaml +- path: patches/webhook_in_autonomousdatabases.yaml +- path: patches/webhook_in_autonomousdatabasebackups.yaml +- path: patches/webhook_in_autonomousdatabaserestores.yaml +- path: patches/webhook_in_autonomouscontainerdatabases.yaml #- patches/webhook_in_lrests.yaml #- patches/webhook_in_lrpdbs.yaml #- patches/webhook_in_ordssrvs.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch -# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_provshards.yaml -- patches/cainjection_in_singleinstancedatabases.yaml -- patches/cainjection_in_shardingdatabases.yaml -- patches/cainjection_in_pdbs.yaml -- patches/cainjection_in_cdbs.yaml #- patches/cainjection_in_oraclerestdataservices.yaml -#- patches/cainjection_in_autonomouscontainerdatabases.yaml -- patches/cainjection_in_dbcssystems.yaml #- patches/cainjection_in_dataguardbrokers.yaml #- patches/cainjection_in_databaseobservers.yaml -- patches/cainjection_in_autonomousdatabases.yaml -- patches/cainjection_in_autonomousdatabasebackups.yaml -- patches/cainjection_in_autonomousdatabaserestores.yaml -- patches/cainjection_in_autonomouscontainerdatabases.yaml #- patches/cainjection_in_lrests.yaml #- patches/cainjection_in_lrpdbs.yaml #- patches/cainjection_in_ordssrvs.yaml -#- patches/cainjection_in_singleinstancedatabases.yaml +- path: patches/cainjection_in_singleinstancedatabases.yaml +- path: patches/cainjection_in_shardingdatabases.yaml +- path: patches/cainjection_in_pdbs.yaml +- path: patches/cainjection_in_cdbs.yaml +- path: patches/cainjection_in_dbcssystems.yaml +- path: patches/cainjection_in_autonomousdatabases.yaml +- path: patches/cainjection_in_autonomousdatabasebackups.yaml +- path: patches/cainjection_in_autonomousdatabaserestores.yaml +- path: patches/cainjection_in_autonomouscontainerdatabases.yaml + # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml b/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml index 734407bc..45edfbf0 100644 --- a/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml +++ b/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml @@ -4,5 +4,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER name: autonomouscontainerdatabases.database.oracle.com diff --git a/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml b/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml index 9468569d..65af0df2 100644 --- a/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml +++ b/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml @@ -4,5 +4,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER name: autonomousdatabasebackups.database.oracle.com diff --git a/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml b/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml index cfc941f8..bf846773 100644 --- a/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml +++ b/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml @@ -4,5 +4,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER name: autonomousdatabaserestores.database.oracle.com diff --git a/config/crd/patches/cainjection_in_autonomousdatabases.yaml b/config/crd/patches/cainjection_in_autonomousdatabases.yaml index 05842d0b..45c3325d 100644 --- a/config/crd/patches/cainjection_in_autonomousdatabases.yaml +++ b/config/crd/patches/cainjection_in_autonomousdatabases.yaml @@ -8,5 +8,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER name: autonomousdatabases.database.oracle.com diff --git a/config/crd/patches/cainjection_in_cdbs.yaml b/config/crd/patches/cainjection_in_cdbs.yaml index 8cb50343..97b3a780 100644 --- a/config/crd/patches/cainjection_in_cdbs.yaml +++ b/config/crd/patches/cainjection_in_cdbs.yaml @@ -4,5 +4,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER name: cdbs.database.oracle.com diff --git a/config/crd/patches/cainjection_in_dbcssystems.yaml b/config/crd/patches/cainjection_in_dbcssystems.yaml index 1c14e1fd..31bdd9e1 100644 --- a/config/crd/patches/cainjection_in_dbcssystems.yaml +++ b/config/crd/patches/cainjection_in_dbcssystems.yaml @@ -4,5 +4,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER name: dbcssystems.database.oracle.com diff --git a/config/crd/patches/cainjection_in_pdbs.yaml b/config/crd/patches/cainjection_in_pdbs.yaml index 8c41010a..90ee25bb 100644 --- a/config/crd/patches/cainjection_in_pdbs.yaml +++ b/config/crd/patches/cainjection_in_pdbs.yaml @@ -4,5 +4,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER name: pdbs.database.oracle.com diff --git a/config/crd/patches/cainjection_in_shardingdatabases.yaml b/config/crd/patches/cainjection_in_shardingdatabases.yaml index 45d35376..f7220bc6 100644 --- a/config/crd/patches/cainjection_in_shardingdatabases.yaml +++ b/config/crd/patches/cainjection_in_shardingdatabases.yaml @@ -8,5 +8,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER name: shardingdatabases.database.oracle.com diff --git a/config/crd/patches/cainjection_in_singleinstancedatabases.yaml b/config/crd/patches/cainjection_in_singleinstancedatabases.yaml index 11114339..65d1336e 100644 --- a/config/crd/patches/cainjection_in_singleinstancedatabases.yaml +++ b/config/crd/patches/cainjection_in_singleinstancedatabases.yaml @@ -8,5 +8,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER name: singleinstancedatabases.database.oracle.com diff --git a/config/database.oracle.com_autonomousdatabases.yaml b/config/database.oracle.com_autonomousdatabases.yaml deleted file mode 100644 index f77407f3..00000000 --- a/config/database.oracle.com_autonomousdatabases.yaml +++ /dev/null @@ -1,324 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases - shortNames: - - adb - - adbs - singular: autonomousdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.details.displayName - name: Display Name - type: string - - jsonPath: .spec.details.dbName - name: Db Name - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .spec.details.isDedicated - name: Dedicated - type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .spec.details.dataStorageSizeInTBs - name: Storage (TB) - type: integer - - jsonPath: .spec.details.dbWorkload - name: Workload Type - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabase is the Schema for the autonomousdatabases - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase - Important: Run "make" to regenerate code after modifying this file' - properties: - details: - description: AutonomousDatabaseDetails defines the detail information - of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase - properties: - adminPassword: - properties: - k8sSecret: - description: "*********************** *\tSecret specs ***********************" - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - autonomousContainerDatabase: - description: ACDSpec defines the spec of the target for backup/restore - runs. The name could be the name of an AutonomousDatabase or - an AutonomousDatabaseBackup - properties: - k8sACD: - description: "*********************** *\tACD specs ***********************" - properties: - name: - type: string - type: object - ociACD: - properties: - ocid: - type: string - type: object - type: object - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbName: - type: string - dbVersion: - type: string - dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying - type: string' - enum: - - OLTP - - DW - - AJD - - APEX - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - isAutoScalingEnabled: - type: boolean - isDedicated: - type: boolean - licenseModel: - description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying - type: string' - enum: - - LICENSE_INCLUDED - - BRING_YOUR_OWN_LICENSE - type: string - lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying - type: string' - type: string - networkAccess: - properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: - properties: - hostnamePrefix: - type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: - type: string - type: object - type: object - wallet: - properties: - name: - type: string - password: - properties: - k8sSecret: - description: "*********************** *\tSecret specs - ***********************" - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - type: object - type: object - hardLink: - default: false - type: boolean - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - required: - - details - type: object - status: - description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase - properties: - allConnectionStrings: - items: - properties: - connectionStrings: - items: - properties: - connectionString: - type: string - tnsName: - type: string - type: object - type: array - tlsAuthentication: - type: string - required: - - connectionStrings - type: object - type: array - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' - type: string - timeCreated: - type: string - walletExpiringDate: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_dataguardbrokers.yaml b/config/database.oracle.com_dataguardbrokers.yaml deleted file mode 100644 index f19a3e22..00000000 --- a/config/database.oracle.com_dataguardbrokers.yaml +++ /dev/null @@ -1,134 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: DataguardBroker is the Schema for the dataguardbrokers API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: DataguardBrokerSpec defines the desired state of DataguardBroker - properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - description: FSFO strategy - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - description: DataguardBrokerStatus defines the observed state of DataguardBroker - properties: - clusterConnectString: - type: string - externalConnectString: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_shardingdatabases.yaml b/config/database.oracle.com_shardingdatabases.yaml deleted file mode 100644 index bb9bbd38..00000000 --- a/config/database.oracle.com_shardingdatabases.yaml +++ /dev/null @@ -1,688 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - creationTimestamp: null - name: shardingdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: ShardingDatabase - listKind: ShardingDatabaseList - plural: shardingdatabases - singular: shardingdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v4 - schema: - openAPIV3Schema: - description: ShardingDatabase is the Schema for the shardingdatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ShardingDatabaseSpec defines the desired state of ShardingDatabase - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - description: CatalogSpec defines the desired state of CatalogSpec - properties: - envVars: - items: - description: EnvironmentVariable represents a named variable - accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string - isDelete: - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - description: ResourceRequirements describes the compute resource - requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbEdition: - type: string - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - description: Secret Details - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - fssStorageClass: - type: string - gsm: - items: - description: GsmSpec defines the desired state of GsmSpec - properties: - directorName: - type: string - envVars: - description: Replicas int32 `json:"replicas,omitempty"` // - Gsm Replicas. If you set OraGsmPvcName then it is set default - to 1. - items: - description: EnvironmentVariable represents a named variable - accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string - isDelete: - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - description: ResourceRequirements describes the compute resource - requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - description: Service Definition - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - description: ShardSpace Specs - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: string - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - description: PortMapping is a specification of port mapping for - an application deployment. - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' - items: - description: ShardSpec is a specification of Shards for an application - deployment. - properties: - deployAs: - type: string - envVars: - items: - description: EnvironmentVariable represents a named variable - accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string - isDelete: - enum: - - enable - - disable - - failed - - force - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - description: ResourceRequirements describes the compute resource - requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - tdeWalletPvc: - type: string - tdeWalletPvcMountLocation: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 - ShardingDatabaseStatus defines the observed state of ShardingDatabase - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_singleinstancedatabases.yaml b/config/database.oracle.com_singleinstancedatabases.yaml deleted file mode 100644 index 1c011e17..00000000 --- a/config/database.oracle.com_singleinstancedatabases.yaml +++ /dev/null @@ -1,421 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.edition - name: Edition - type: string - - jsonPath: .status.sid - name: Sid - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version - type: string - - jsonPath: .status.connectString - name: Connect Str - type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.tcpsConnectString - name: TCPS Connect Str - type: string - - jsonPath: .status.tcpsPdbConnectString - name: TCPS Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.oemExpressUrl - name: Oem Express Url - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase - properties: - adminPassword: - description: SingleInsatnceAdminPassword defines the secret containing - Admin Password mapped to secretKey for Database - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - archiveLog: - type: boolean - charset: - type: string - createAs: - enum: - - primary - - standby - - clone - type: string - dgBrokerConfigured: - type: boolean - edition: - enum: - - standard - - enterprise - - express - - free - type: string - enableTCPS: - type: boolean - flashBack: - type: boolean - forceLog: - type: boolean - image: - description: SingleInstanceDatabaseImage defines the Image source - and pullSecrets for POD - properties: - prebuiltDB: - type: boolean - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - listenerPort: - type: integer - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - pdbName: - type: string - persistence: - description: SingleInstanceDatabasePersistence defines the storage - size and class for PVC - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - primaryDatabaseRef: - type: string - readinessCheckPeriod: - type: integer - replicas: - type: integer - resources: - properties: - limits: - properties: - cpu: - type: string - memory: - type: string - type: object - requests: - properties: - cpu: - type: string - memory: - type: string - type: object - type: object - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - sid: - description: SID must be alphanumeric (no special characters, only - a-z, A-Z, 0-9), and no longer than 12 characters. - maxLength: 12 - pattern: ^[a-zA-Z0-9]+$ - type: string - tcpsCertRenewInterval: - type: string - tcpsListenerPort: - type: integer - tcpsTlsSecret: - type: string - required: - - image - type: object - status: - description: SingleInstanceDatabaseStatus defines the observed state of - SingleInstanceDatabase - properties: - apexInstalled: - type: boolean - archiveLog: - type: string - certCreationTimestamp: - type: string - certRenewInterval: - type: string - charset: - type: string - clientWalletLoc: - type: string - clusterConnectString: - type: string - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - connectString: - type: string - createdAs: - type: string - datafilesCreated: - default: "false" - type: string - datafilesPatched: - default: "false" - type: string - dgBrokerConfigured: - type: boolean - edition: - type: string - flashBack: - type: string - forceLog: - type: string - initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - initPgaSize: - type: integer - initSgaSize: - type: integer - isTcpsEnabled: - default: false - type: boolean - nodes: - items: - type: string - type: array - oemExpressUrl: - type: string - ordsReference: - type: string - pdbConnectString: - type: string - pdbName: - type: string - persistence: - description: SingleInstanceDatabasePersistence defines the storage - size and class for PVC - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - prebuiltDB: - type: boolean - primaryDatabase: - type: string - releaseUpdate: - type: string - replicas: - type: integer - role: - type: string - sid: - type: string - standbyDatabases: - additionalProperties: - type: string - type: object - status: - type: string - tcpsConnectString: - type: string - tcpsPdbConnectString: - type: string - tcpsTlsSecret: - default: "" - type: string - required: - - isTcpsEnabled - - persistence - - tcpsTlsSecret - type: object - type: object - served: true - storage: true - subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.replicas - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index d41001b0..f2bd4689 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -17,59 +17,125 @@ namePrefix: oracle-database-operator- #commonLabels: # someName: someValue -bases: +resources: - ../crd - ../rbac - ../manager -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml - ../webhook -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. - ../certmanager # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus -patchesStrategicMerge: # Protect the /metrics endpoint by putting it behind auth. # If you want your controller-manager to expose the /metrics # endpoint w/o any authn/z, please comment the following line. #- manager_auth_proxy_patch.yaml +patches: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -- manager_webhook_patch.yaml - +- path: manager_webhook_patch.yaml # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. # 'CERTMANAGER' needs to be enabled to use ca injection -- webhookcainjection_patch.yaml +- path: webhookcainjection_patch.yaml -# the following config is for teaching kustomize how to do var substitution -vars: # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR - objref: - kind: Certificate - group: cert-manager.io - version: v1 - name: serving-cert # this name should match the one in certificate.yaml - fieldref: - fieldpath: metadata.namespace -- name: CERTIFICATE_NAME - objref: - kind: Certificate - group: cert-manager.io - version: v1 - name: serving-cert # this name should match the one in certificate.yaml -- name: SERVICE_NAMESPACE # namespace of the service - objref: - kind: Service - version: v1 - name: webhook-service - fieldref: - fieldpath: metadata.namespace -- name: SERVICE_NAME - objref: - kind: Service - version: v1 - name: webhook-service +# Uncomment the following replacements to add the cert-manager CA injection annotations +replacements: + - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml + fieldPath: .metadata.namespace # namespace of the certificate CR + targets: + - select: + kind: ValidatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: MutatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: CustomResourceDefinition + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - source: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml + fieldPath: .metadata.name + targets: + - select: + kind: ValidatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: MutatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: CustomResourceDefinition + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - source: # Add cert-manager annotation to the webhook Service + kind: Service + version: v1 + name: webhook-service + fieldPath: .metadata.name # namespace of the service + targets: + - select: + kind: Certificate + group: cert-manager.io + version: v1 + fieldPaths: + - .spec.dnsNames.0 + - .spec.dnsNames.1 + options: + delimiter: '.' + index: 0 + create: true + - source: + kind: Service + version: v1 + name: webhook-service + fieldPath: .metadata.namespace # namespace of the service + targets: + - select: + kind: Certificate + group: cert-manager.io + version: v1 + fieldPaths: + - .spec.dnsNames.0 + - .spec.dnsNames.1 + options: + delimiter: '.' + index: 1 + create: true diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml index c6b7ea34..63ef5ff6 100644 --- a/config/default/webhookcainjection_patch.yaml +++ b/config/default/webhookcainjection_patch.yaml @@ -4,17 +4,17 @@ # # This patch add annotation to admission webhook config and -# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +# the variables CERTIFICATE_NAMESPACE_PLACEHOLDER and CERTIFICATE_NAME_PLACEHOLDER will be substituted by kustomize. apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-configuration annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: validating-webhook-configuration annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE_PLACEHOLDER/CERTIFICATE_NAME_PLACEHOLDER diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 6aa2242d..7a52fb17 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lin.ocir.io/intsanjaysingh/operator/master/dboperator - newTag: latest + newName: container-registry.oracle.com/database/operator + newTag: 1.2.0 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 54340faf..90d20e14 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -31,7 +31,7 @@ spec: - /manager args: - --enable-leader-election - image: controller:latest + image: container-registry.oracle.com/database/operator:1.2.0 imagePullPolicy: Always name: manager resources: diff --git a/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml b/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml index 933a2bfa..b1a0cc31 100644 --- a/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml @@ -4,47 +4,82 @@ metadata: annotations: alm-examples: '[]' capabilities: Seamless Upgrades - operators.operatorframework.io/builder: operator-sdk-v1.2.0 - operators.operatorframework.io/project_layout: go.kubebuilder.io/v2 - name: oracle-database-operator.v1.1.0 + categories: Database + containerImage: container-registry.oracle.com/database/operator:1.2.0 + operators.operatorframework.io/builder: operator-sdk-v1.39.2 + operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 + name: oracle-database-operator.v1.2.0 namespace: oracle-database-operator-system spec: apiservicedefinitions: {} customresourcedefinitions: owned: - - description: DbcsSystem is the Schema for the dbcssystems API - displayName: Dbcs System - kind: DbcsSystem - name: DbcsSystem.database.oracle.com - version: v4 - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases API displayName: Autonomous Container Database kind: AutonomousContainerDatabase name: autonomouscontainerdatabases.database.oracle.com version: v1alpha1 + - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases + API + displayName: Autonomous Container Database + kind: AutonomousContainerDatabase + name: autonomouscontainerdatabases.database.oracle.com + version: v4 - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups API displayName: Autonomous Database Backup kind: AutonomousDatabaseBackup name: autonomousdatabasebackups.database.oracle.com version: v1alpha1 + - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups + API + displayName: Autonomous Database Backup + kind: AutonomousDatabaseBackup + name: autonomousdatabasebackups.database.oracle.com + version: v4 - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores API displayName: Autonomous Database Restore kind: AutonomousDatabaseRestore name: autonomousdatabaserestores.database.oracle.com version: v1alpha1 + - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores + API + displayName: Autonomous Database Restore + kind: AutonomousDatabaseRestore + name: autonomousdatabaserestores.database.oracle.com + version: v4 - description: AutonomousDatabase is the Schema for the autonomousdatabases API displayName: Autonomous Database kind: AutonomousDatabase name: autonomousdatabases.database.oracle.com version: v1alpha1 + - description: AutonomousDatabase is the Schema for the autonomousdatabases API + displayName: Autonomous Database + kind: AutonomousDatabase + name: autonomousdatabases.database.oracle.com + version: v4 - description: CDB is the Schema for the cdbs API displayName: CDB kind: CDB name: cdbs.database.oracle.com version: v1alpha1 + - description: CDB is the Schema for the cdbs API + displayName: CDB + kind: CDB + name: cdbs.database.oracle.com + version: v4 + - description: DatabaseObserver is the Schema for the databaseobservers API + displayName: Database Observer + kind: DatabaseObserver + name: databaseobservers.observability.oracle.com + version: v4 + - description: DatabaseObserver is the Schema for the databaseobservers API + displayName: Database Observer + kind: DatabaseObserver + name: databaseobservers.observability.oracle.com + version: v1 - description: DatabaseObserver is the Schema for the databaseobservers API displayName: Database Observer kind: DatabaseObserver @@ -54,22 +89,85 @@ spec: displayName: Dataguard Broker kind: DataguardBroker name: dataguardbrokers.database.oracle.com + version: v4 + - description: DataguardBroker is the Schema for the dataguardbrokers API + displayName: Dataguard Broker + kind: DataguardBroker + name: dataguardbrokers.database.oracle.com + version: v1alpha1 + - description: DbcsSystem is the Schema for the dbcssystems API + displayName: Dbcs System + kind: DbcsSystem + name: dbcssystems.database.oracle.com + version: v4 + - description: DbcsSystem is the Schema for the dbcssystems API + displayName: Dbcs System + kind: DbcsSystem + name: dbcssystems.database.oracle.com version: v1alpha1 + - description: LREST is the Schema for the lrests API + displayName: LREST + kind: LREST + name: lrests.database.oracle.com + version: v4 + - description: LRPDB is the Schema for the pdbs API + displayName: LRPDB + kind: LRPDB + name: lrpdbs.database.oracle.com + version: v4 + - description: OracleRestDataService is the Schema for the oraclerestdataservices + API + displayName: Oracle Rest Data Service + kind: OracleRestDataService + name: oraclerestdataservices.database.oracle.com + version: v4 - description: OracleRestDataService is the Schema for the oraclerestdataservices API displayName: Oracle Rest Data Service kind: OracleRestDataService name: oraclerestdataservices.database.oracle.com version: v1alpha1 + - description: OrdsSrvs is the Schema for the ordssrvs API + displayName: Ords Srvs + kind: OrdsSrvs + name: ordssrvs.database.oracle.com + statusDescriptors: + - displayName: Conditions + path: conditions + version: v1alpha1 + - description: OrdsSrvs is the Schema for the ordssrvs API + displayName: Ords Srvs + kind: OrdsSrvs + name: ordssrvs.database.oracle.com + statusDescriptors: + - displayName: Conditions + path: conditions + version: v4 - description: PDB is the Schema for the pdbs API displayName: PDB kind: PDB name: pdbs.database.oracle.com version: v1alpha1 + - description: PDB is the Schema for the pdbs API + displayName: PDB + kind: PDB + name: pdbs.database.oracle.com + version: v4 + - description: ShardingDatabase is the Schema for the shardingdatabases API + displayName: Sharding Database + kind: ShardingDatabase + name: shardingdatabases.database.oracle.com + version: v4 - description: ShardingDatabase is the Schema for the shardingdatabases API displayName: Sharding Database kind: ShardingDatabase name: shardingdatabases.database.oracle.com + version: v1alpha1 + - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases + API + displayName: Single Instance Database + kind: SingleInstanceDatabase + name: singleinstancedatabases.database.oracle.com version: v4 - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases API @@ -79,22 +177,28 @@ spec: version: v1alpha1 description: | As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released Oracle Database Operator for Kubernetes (OraOperator or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating Oracle Database lifecycle management. - In this v1.1.0 production release, OraOperator supports the following database configurations and infrastructure: - ## Oracle Autonomous Database: - * Oracle Autonomous Database shared Oracle Cloud Infrastructure (OCI) (ADB-S) - * Oracle Autonomous Database on dedicated Cloud infrastructure (ADB-D) - * Oracle Autonomous Container Database (ACD) (infrastructure) is the infrastructure for provisioning Autonomous Databases. - * Containerized Single Instance databases (SIDB) deployed in the Oracle Kubernetes Engine (OKE) and any k8s where OraOperator is deployed - * Containerized Sharded databases (SHARDED) deployed in OKE and any k8s where OraOperator is deployed - * Oracle Multitenant Databases (CDB/PDBs) - * Oracle Base Database Cloud Service (BDBCS) - * Oracle Data Guard (Preview status) - * Oracle Database Observability (Preview status) - * Oracle will continue to extend OraOperator to support additional Oracle Database configurations. + In this v1.2.0 production release, OraOperator supports the following database configurations and infrastructure: + ##Supported Database Configurations: + * Oracle Cloud Infrastructure (OCI) Databases + * Oracle Autonomous Database Serverless (ADB-S) + * Oracle Autonomous Database on Dedicated on (ADB-D) + * Oracle Base Database Cloud Service (Base DB) + * Containerized Database deployments on Kubernetes + * Oracle Kubernetes Engine (OKE) and Oracle Cloud Native Environments (OCNE) + * Single Instance databases (SIDB) + * Globally Distributed Databases (Shared) + * Oracle Data Guard + * True Cache (Preview release) + * Oracle Database Observability + * On-Premises Databases + * Oracle Multitenant Databases (CDB/PDBs management) + * ORDS Services + + Refer to full feature list and capabilities: [https://github.com/oracle/oracle-database-operator/blob/main/README.md](https://github.com/oracle/oracle-database-operator/blob/main/README.md) displayName: Oracle Database Operator icon: - base64data: iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAJjUlEQVR42u3cfcwcRQHH8S9PH0BokZfCVBgpOgjyFjRoQIQQkLeA0PLWqgQMFDVgja9AChIKKCEKSgQEQVsQJGKxtNCAvAi2vJiCqAQMUpQRMKM4vFiCQEUo/jH7kOt19m7vbveK8fdJLukzMzuzczc7OzszWxAREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREZH/X2tVSRStmwi8B5gErN1nWS8DAVhmgl9ZsdwpVc+xoteKc/iTCf7VujKN1o0A+xef5cDPTfCP1XjeY+VsAWwFTATGDZjdPSb4F6J1U9sjTPA31n3uXeq1MfBe4F30376ADo0lWjcBOAa4EHhHzXW4FzgDuNsE/2aHc3gJmFBz2WMuBc4ywT87SCbRunWAB4APtEV92gR/zaAnGa2bBJwInFVz/SeY4F+O1q32/Zvg6+xEyuq1PnA08F1gg7ryHSkpbDrwEnAZ9TdmgD2BxcDj0bptG8i/is8DMVq394D5zGb1xgxwdbRum34zjdaNi9adAjxD/Y15PxP8yzXn2UvdDiPdsX9IjY0ZOHOVKzFaNw64Apgx5DoelrvNNdxDt9rDBP/rXg+K1u0EPNIhyWPADp3uQiX5bggsIX+hDOp6E/z0lrKG1kMXQ7OLgJkNZL8SWO+tHjpatxbwM4bfmAEWRuuOWAPljrmvGGJVVlz8d3ZJth1wXI/5jgeW0UxjhnRnGrqifV1FM40ZYDcT/GujLQGnAUd2OGAu6Qd8oY/CxgGTgWOB3UvSzI/W7WiCf7RLXgcNUOnJwOUlcScCF/SQ10mAqZBubrTuVhP837sljNZB6lQmdUh2AbCUdMvu1Ssm+Of6OK4OXyb9/mV+DNxOf+0LE/yDUDwURuu2A/5YknYWcLEJ/pU6ahWt2wq4FtgjE/08YMZmQUqGHHua4O8boPx1gNuAvTPRI1WGB0UdnsxEPQzsnAm/HTjQBN8t36nAwpLoTwDzTfBv9Fv3kjIbH3JE6xzwREn0bOA7dY3px4Ycl5bEH2CC/1ZdjRnABP8UsBfwg0z0RODgusoqKf810uxNzsbdjm/pRXOOIP1A7Q4ADu2S7yjljXl7E/y8uhvzEF1YEj7FBH9OnQ+oI9G6zYF9MnEzTfB3NFG7ogf+ApDrsr7XRJlt/lYSvmGFY6cDu2XCZ5ngnwC+XXLcTcXDXpmyodheTcxpD0u0bjNgSibqZBP8orrLGyH1HjlXNFnRorc5KhPlioWcJo2WhL/e6aBo3abke+eVFL2QCX4F+eEMpDnXMsdnwu4ywd/T8HfRtLLv4pImChsFDsmEn2aCf73XzPrwUEn4+4Gep9F68LGS8G4PTN8vCf9oMZQBwAS/JFp3PTCtLd2MaN3ckmeAXIP+ZoPfwbCUDSFXFMO3Om03QlrkaLd4GDUtHsByPd7WTZUZrdsLuDUTdX+n5fBo3b6k4Ua7K0zw92fCy6an7o3WVV2seqRiurez3QfPopI5Jvhlo+SniPqaOunT05mwjmPZaN2WwKPAmy0fOvw99u/NOmR7aofyxgO/7OU4E/yz0brjSHOv7c4oPt3U9jC+Bk0aPItKvgZpDJ27zVZ5OKrLFpmwlzodYIL/K2k+egPgncX5bghsRJqp2KT4TAQ2LT6dGvONJvi7O8R/oyR8qgn+xQ7HXUP+wffrxSpjN+v195W+rQxj3vuQsd9hhDRJ326P3vLrTzGGOjoT9Zdux5rg7yWtxA3qsZJzGDvHXYCvZKIWAzd1OceVlE/X3VmsNnayfQ31W9MeaDj/W0zwN4/9MQosYvUv/YJo3UVVt3kOYAfyO/4qTVOZ4JcVu9EeAjbvo/xLSNNH/85FRuvWBsp67guBKRUfbM4jrcSucvqk1caxp/3rgE+2pfkqaWfi/7JfkO8w1m5i4mGUtGrWbhzwKdKKXiOKtf0rM1HPAbFqPib4GK17H2lRYv8Kh7xIWkj6kQned0n7JWB8JnwmaSfiFgzm4mjdomKxaQ6rN+jDo3UfMsH/dsBy1qSy/S4zaGBqeMQE/zTwu0zcT6J1u/WaYRVFr3YOsGsmema3JeJ2xUrmQcDFHZKdU9R3IxP86d0ac7Rua+D8XBRpP0iVi6eKecXFXXYneDBaN7mmsoau2MOyJBN1eTHjVKuxpe/PlMQvjdadUGGsV1mxcjSP8qf8Bf3ka4J/wwT/RdImmJwzSbv6uj5oFQ1sYUn0fkVZjwJn1/CV7ApM77Ik/1S0rq4LaE0o2+G3JFp3UjG0q8Vb49do3fnAyR3SngvcQ3rFqFfjAEtaaJjWId2uJvjftJxTX5uTonWHAzeURQMf7LT7LVp3LHB1Jup8E/ypLenWBVb08X3kbEbanLWU/J0L0uzP2aQ76qBTeq+a4B/ObU4CPlJTnVodTOpUypxH6smXD1JIa4MeBX5FfqFlGGaY4FcZUw+y264YLi3tkGQXE/zvM8dNIr0lkjO+faNWtG5n0sXai1syYfNN8EdF6zYhNeymHWmCv6GkQdftKtIo4GbgwCYLan9jZV1gPvDxIVSy1fEm+KvaAwfdPlqMg//cIcnhJviFbccsIr8dYD8TfLcN/ZVE6y4HPpeJOsAEf0dxUf2BNH/ehMXAPiZ4htSgNzbBLy+27l5Lfg9PLVZ5p7CYvjqU4b3V8B/gw7nGXIdi99umlO/FXRCtmzU29RatO4h8Y15QV2MuzCoJvz1aN8EE/w9gS3p74aAXx/T64D2AKSb45fDW1t1plD+zDWrf1V6SNcG/aYK/jDSmm917npU8Tdo7vH7TU1Im+OeBnShfBDkP+Gm0bgPyQwFIb7PUeU7/pHwx59wizQoT/CmAI793vF+fNcGHlr+P7zun7m4jrXO01h0T/BzSKu7pNZZ1nQn+rqr/L8dkYFvSvGu/T6T/Ap4CHjfBV9orEq07IRN8pwn+yV4LL17Q7PTj3UV+F95zTfw/FcVMSvb9zeIHz53/1sA2pEWZfmeermxfMIvW7Uh6EB3pL8tSN5vgn+mWqNibsw3wbvpvXwuqtisREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREWnxX2ox1/vZSvwPAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI0LTA4LTEzVDE5OjUyOjMxKzAwOjAwsDIMcAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNC0wOC0xM1QxOTo1MjozMSswMDowMMFvtMwAAABVdEVYdHN2Zzpjb21tZW50ACBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyBFB1wTAAAAAElFTkSuQmCC - mediatype: png + mediatype: image/png install: spec: deployments: null @@ -118,4 +222,5 @@ spec: maturity: alpha provider: name: Oracle + replaces: oracle-database-operator.v1.1.0 version: 1.2.0 diff --git a/config/samples/acd/autonomouscontainerdatabase_bind.yaml b/config/samples/acd/autonomouscontainerdatabase_bind.yaml index 3d28ba4d..12912ac9 100644 --- a/config/samples/acd/autonomouscontainerdatabase_bind.yaml +++ b/config/samples/acd/autonomouscontainerdatabase_bind.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousContainerDatabase metadata: name: autonomouscontainerdatabase-sample diff --git a/config/samples/acd/autonomouscontainerdatabase_change_displayname.yaml b/config/samples/acd/autonomouscontainerdatabase_change_displayname.yaml index dd75250d..99568e52 100644 --- a/config/samples/acd/autonomouscontainerdatabase_change_displayname.yaml +++ b/config/samples/acd/autonomouscontainerdatabase_change_displayname.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousContainerDatabase metadata: name: autonomouscontainerdatabase-sample diff --git a/config/samples/acd/autonomouscontainerdatabase_create.yaml b/config/samples/acd/autonomouscontainerdatabase_create.yaml index 5f42a136..1a1daeb6 100644 --- a/config/samples/acd/autonomouscontainerdatabase_create.yaml +++ b/config/samples/acd/autonomouscontainerdatabase_create.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousContainerDatabase metadata: name: autonomouscontainerdatabase-sample diff --git a/config/samples/acd/autonomouscontainerdatabase_delete_resource.yaml b/config/samples/acd/autonomouscontainerdatabase_delete_resource.yaml index 5be06b5a..b2bd4c33 100644 --- a/config/samples/acd/autonomouscontainerdatabase_delete_resource.yaml +++ b/config/samples/acd/autonomouscontainerdatabase_delete_resource.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousContainerDatabase metadata: name: autonomouscontainerdatabase-sample diff --git a/config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml b/config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml index 0e884f6e..96644fed 100644 --- a/config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml +++ b/config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousContainerDatabase metadata: name: autonomouscontainerdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_backup.yaml b/config/samples/adb/autonomousdatabase_backup.yaml index 0099a347..cf633b5d 100644 --- a/config/samples/adb/autonomousdatabase_backup.yaml +++ b/config/samples/adb/autonomousdatabase_backup.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabaseBackup metadata: name: autonomousdatabasebackup-sample diff --git a/config/samples/adb/autonomousdatabase_bind.yaml b/config/samples/adb/autonomousdatabase_bind.yaml index 702b8f03..29105860 100644 --- a/config/samples/adb/autonomousdatabase_bind.yaml +++ b/config/samples/adb/autonomousdatabase_bind.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_clone.yaml b/config/samples/adb/autonomousdatabase_clone.yaml index 559d7185..c1216d64 100644 --- a/config/samples/adb/autonomousdatabase_clone.yaml +++ b/config/samples/adb/autonomousdatabase_clone.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_create.yaml b/config/samples/adb/autonomousdatabase_create.yaml index d633cb84..776b9bff 100644 --- a/config/samples/adb/autonomousdatabase_create.yaml +++ b/config/samples/adb/autonomousdatabase_create.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_delete_resource.yaml b/config/samples/adb/autonomousdatabase_delete_resource.yaml index bae1f605..0d5504a7 100644 --- a/config/samples/adb/autonomousdatabase_delete_resource.yaml +++ b/config/samples/adb/autonomousdatabase_delete_resource.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_rename.yaml b/config/samples/adb/autonomousdatabase_rename.yaml index 22dbcc0f..1d5f3d0f 100644 --- a/config/samples/adb/autonomousdatabase_rename.yaml +++ b/config/samples/adb/autonomousdatabase_rename.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_restore.yaml b/config/samples/adb/autonomousdatabase_restore.yaml index 3db8a1b6..7d3164c4 100644 --- a/config/samples/adb/autonomousdatabase_restore.yaml +++ b/config/samples/adb/autonomousdatabase_restore.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabaseRestore metadata: name: autonomousdatabaserestore-sample diff --git a/config/samples/adb/autonomousdatabase_scale.yaml b/config/samples/adb/autonomousdatabase_scale.yaml index ea53e94d..33fe6938 100644 --- a/config/samples/adb/autonomousdatabase_scale.yaml +++ b/config/samples/adb/autonomousdatabase_scale.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml index 4a191dd6..56630045 100644 --- a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml +++ b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_update_admin_password.yaml b/config/samples/adb/autonomousdatabase_update_admin_password.yaml index be7aca69..14a43639 100644 --- a/config/samples/adb/autonomousdatabase_update_admin_password.yaml +++ b/config/samples/adb/autonomousdatabase_update_admin_password.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_update_mtls.yaml b/config/samples/adb/autonomousdatabase_update_mtls.yaml index 25eda529..988da9a6 100644 --- a/config/samples/adb/autonomousdatabase_update_mtls.yaml +++ b/config/samples/adb/autonomousdatabase_update_mtls.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_update_network_access.yaml b/config/samples/adb/autonomousdatabase_update_network_access.yaml index 7dd3fa0c..a8aeb251 100644 --- a/config/samples/adb/autonomousdatabase_update_network_access.yaml +++ b/config/samples/adb/autonomousdatabase_update_network_access.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/adb/autonomousdatabase_wallet.yaml b/config/samples/adb/autonomousdatabase_wallet.yaml index 84136647..c717c4f3 100644 --- a/config/samples/adb/autonomousdatabase_wallet.yaml +++ b/config/samples/adb/autonomousdatabase_wallet.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # -apiVersion: database.oracle.com/v1alpha1 +apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: autonomousdatabase-sample diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 1a032832..956027a3 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -5,47 +5,13 @@ ## Append samples you want in your CSV to this file as resources ## resources: - - multitenant/pdb_plug.yaml - - multitenant/cdb_secret.yaml - - multitenant/pdb_secret.yaml - - multitenant/pdb_clone.yaml - - multitenant/cdb.yaml - - sidb/singleinstancedatabase_patch.yaml - - sidb/oraclerestdataservice_apex.yaml - - sidb/singleinstancedatabase_express.yaml - - sidb/singleinstancedatabase_secrets.yaml - - sidb/singleinstancedatabase_clone.yaml - - sidb/singleinstancedatabase_prebuiltdb.yaml - - sidb/dataguardbroker.yaml - - sidb/oraclerestdataservice_secrets.yaml - - sidb/singleinstancedatabase_free.yaml - - sidb/singleinstancedatabase_standby.yaml - - sidb/openshift_rbac.yaml - - sharding/sharding_v1alpha1_provshard_clonespec1.yaml - - sharding/shardingdatabase.yaml - - sharding/sharding_v1alpha1_provshard_clonespec.yaml - - observability/databaseobserver_vault.yaml - - observability/databaseobserver_minimal.yaml - - adb/autonomousdatabase_bind.yaml - - adb/autonomousdatabase_backup.yaml - - adb/autonomousdatabase_restore.yaml - - acd/autonomouscontainerdatabase_create.yaml - - sidb/singleinstancedatabase.yaml - - sharding/shardingdatabase.yaml - - sharding/sharding_v1alpha1_provshard.yaml - - dbcs/database_v1alpha1_dbcssystem.yaml - - database_v1alpha1_dataguardbroker.yaml - - database_v1alpha1_shardingdatabase.yaml - - observability/v1alpha1/databaseobserver.yaml - - observability/v1/databaseobserver.yaml - - observability/v4/databaseobserver.yaml - - acd/autonomouscontainerdatabase_restart_terminate.yaml - - database_v4_shardingdatabase.yaml - - database_v4_dbcssystem.yaml -- database_v4_lrest.yaml -- database_v4_lrpdb.yaml -- database_v4_ordssrvs.yaml -- database_v4_singleinstancedatabase.yaml -- database_v4_dataguardbroker.yaml -- database_v4_oraclerestdataservice.yaml - # +kubebuilder:scaffold:manifestskustomizesamples +- acd/autonomouscontainerdatabase_create.yaml +- adb/autonomousdatabase_backup.yaml +- adb/autonomousdatabase_create.yaml +- dbcs/database_v1alpha1_dbcssystem.yaml +- multitenant/cdb.yaml +- multitenant/pdb_create.yaml +- observability/databaseobserver.yaml +- sharding/sharding_v1alpha1_provshard.yaml +- sidb/singleinstancedatabase_create.yaml +# +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/scorecard/kustomization.yaml b/config/scorecard/kustomization.yaml index bf4c1e7c..576c02a4 100644 --- a/config/scorecard/kustomization.yaml +++ b/config/scorecard/kustomization.yaml @@ -4,17 +4,18 @@ # resources: - bases/config.yaml -patchesJson6902: +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +patches: - path: patches/basic.config.yaml target: group: scorecard.operatorframework.io - version: v1alpha3 kind: Configuration name: config + version: v1alpha3 - path: patches/olm.config.yaml target: group: scorecard.operatorframework.io - version: v1alpha3 kind: Configuration name: config -# +kubebuilder:scaffold:patchesJson6902 + version: v1alpha3 From 51b7ab75d29eaf97b970dc90cd02c732b577a8e6 Mon Sep 17 00:00:00 2001 From: marco_stefanetti Date: Tue, 6 May 2025 08:43:07 +0000 Subject: [PATCH 216/414] ORDSSRVS documentation --- README.md | 5 +- docs/ordsservices/README.md | 43 ++++++- docs/ordsservices/examples/adb.md | 40 +++---- docs/ordsservices/examples/adb_oraoper.md | 86 ++++++-------- docs/ordsservices/examples/existing_db.md | 112 ++++++++++++++++++ docs/ordsservices/examples/mongo_api.md | 14 +-- docs/ordsservices/examples/multi_pool.md | 27 ++--- .../examples/ordsnamespace-role-binding.yaml | 13 ++ docs/ordsservices/examples/sidb_container.md | 50 ++++---- 9 files changed, 259 insertions(+), 131 deletions(-) create mode 100644 docs/ordsservices/examples/existing_db.md create mode 100644 docs/ordsservices/examples/ordsnamespace-role-binding.yaml diff --git a/README.md b/README.md index 18cceced..55eba951 100644 --- a/README.md +++ b/README.md @@ -200,10 +200,7 @@ The following quickstarts are designed for specific database configurations: * [Containerized Oracle Globally Distributed Database](./docs/sharding/README.md) * [Oracle Multitenant Database](./docs/multitenant/README.md) * [Oracle Base Database Service (OBDS)](./docs/dbcs/README.md) - - -The following quickstart is designed for non-database configurations: -* [Oracle Database Observability](./docs/observability/README.md) +* [ORDS Services (ORDSSRVS)](./docs/ordsservices/README.md) The following quickstart is designed for non-database configurations: diff --git a/docs/ordsservices/README.md b/docs/ordsservices/README.md index 1740e99f..57195120 100644 --- a/docs/ordsservices/README.md +++ b/docs/ordsservices/README.md @@ -25,22 +25,53 @@ It supports the majority of ORDS configuration settings as per the [API Document The ORDS and APEX schemas can be [automatically installed/upgraded](./autoupgrade.md) into the Oracle Database by the ORDS controller. ORDS Version support: -* v22.1+ +* 24.1.1 +(Newer versions of ORDS will be supported in the next update of OraOperator) Oracle Database Version: * 19c * 23ai (incl. 23ai Free) +### Prerequisites -### Common Configurations +1. Oracle Database Operator + + Install the Oracle Database Operator (OraOperator) using the instructions in the [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. + +1. Namespace + + For a dedicated namespace deployment of the ORDSSRVS controller, refer to the "Namespace Scoped Deployment" section in the OraOperator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md#2-namespace-scoped-deployment). + + The following examples deploy the controller to the 'ordsnamespace' namespace. + + Create the namespace: + ```bash + kubectl create namespace ordsnamespace + ``` + + Apply namespace role binding [ordsnamespace-role-binding.yaml](./ordsnamespace-role-binding.yaml): + ```bash + kubectl apply -f ordsnamespace-role-binding.yaml + ``` + + Edit OraOperator to add the namespace under WATCH_NAMESPACE: + ```yaml + - name: WATCH_NAMESPACE + value: "default,,ordsnamespace" + ``` + +### Common configuration examples A few common configuration examples can be used to quickly familiarise yourself with the ORDS Custom Resource Definition. The "Conclusion" section of each example highlights specific settings to enable functionality that maybe of interest. -* [Containerised Single Instance Database using the Oracontroller](./examples/sidb_container.md) -* [Multipool, Multidatabase using a TNS Names file](./examples/multi_pool.md) -* [Autonomous Database using the Oracontroller](./examples/adb_oraoper.md) - (Customer Managed ORDS) *See [Limitations](#limitations) -* [Autonomous Database without the Oracontroller](./examples/adb.md) - (Customer Managed ORDS) +Before + +* [Pre-existing Database](./examples/existing_db.md) +* [Containerised Single Instance Database (SIDB)](./examples/sidb_container.md) +* [Multidatabase using a TNS Names file](./examples/multi_pool.md) +* [Autonomous Database using the OraOperator](./examples/adb_oraoper.md) *See [Limitations](#limitations) +* [Autonomous Database without the OraOperator](./examples/adb.md) * [Oracle API for MongoDB Support](./examples/mongo_api.md) Running through all examples in the same Kubernetes cluster illustrates the ability to run multiple ORDS instances with a variety of different configurations. diff --git a/docs/ordsservices/examples/adb.md b/docs/ordsservices/examples/adb.md index ba53aac5..90a21b5c 100644 --- a/docs/ordsservices/examples/adb.md +++ b/docs/ordsservices/examples/adb.md @@ -5,11 +5,7 @@ This example walks through using the **ORDSSRVS controller** with an Oracle Auto This assumes that an ADB has already been provisioned and is configured as "Secure Access from Anywhere". Note that if behind a Proxy, this example will not work as the Wallet will need to be modified to support the proxy configuration. - -### Cert-Manager and Oracle Database Operator installation - -Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. - +Before testing this example, please verify the prerequisites : [ORDSSRVS prerequisites](../README.md#prerequisites) ### ADB Wallet Secret @@ -25,13 +21,13 @@ kubectl create secret generic adb-wallet \ Create a Secret for the ADB ADMIN password, replacing with the real password: ```bash -echo adb-db-auth-enc -openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.k +echo ${ADMIN_PASSWORD} > adb-db-auth-enc +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key openssl rsa -in ca.key -outform PEM -pubout -out public.pem kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace -openssl rsautl -encrypt -pubin -inkey public.pem -in adb-db-auth-enc |base64 > e_sidb-db-auth-enc -kubectl create secret generic adb-db-auth-enc --from-file=password=e_sidb-db-auth-enc -n ordsnamespace -rm adb-db-auth-enc e_sidb-db-auth-enc +openssl rsautl -encrypt -pubin -inkey public.pem -in adb-db-auth-enc |base64 > e_adb-db-auth-enc +kubectl create secret generic adb-oraoper-db-auth-enc --from-file=password=e_adb-db-auth-enc -n ordsnamespace +rm adb-db-auth-enc e_adb-db-auth-enc ``` ### Create RestDataServices Resource @@ -43,22 +39,24 @@ rm adb-db-auth-enc e_sidb-db-auth-enc Replace with the ADB Name and ensure that the `db.wallet.zip.service` is valid for your ADB Workload (e.g. _TP or _HIGH, etc.): - ```bash - echo " - apiVersion: database.oracle.com/v1 - kind: OrdsSrvs + ```yaml + apiVersion: database.oracle.com/v4 + kind: OrdsSrvs metadata: name: ords-adb namespace: ordsnamespace spec: image: container-registry.oracle.com/database/ords:24.1.1 - globalSettings: - database.api.enabled: true + forceRestart: true encPrivKey: secretName: prvkey passwordKey: privateKey + globalSettings: + database.api.enabled: true poolSettings: - poolName: adb + restEnabledSql.active: true + plsql.gateway.mode: direct db.wallet.zip.service: _TP dbWalletSecret: secretName: adb-wallet @@ -68,18 +66,16 @@ rm adb-db-auth-enc e_sidb-db-auth-enc plsql.gateway.mode: proxied db.username: ORDS_PUBLIC_USER_OPER db.secret: - secretName: adb-db-auth-enc - passwordKey: password + secretName: adb-oraoper-db-auth-enc db.adminUser: ADMIN db.adminUser.secret: - secretName: adb-db-auth-enc - passwordKey: password" | kubectl apply -f - + secretName: adb-oraoper-db-auth-enc ``` latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** 1. Watch the restdataservices resource until the status is **Healthy**: ```bash - kubectl get ordssrvs ords-adb -w + kubectl get -n ordsnamespace ordssrvs ords-adb -w ``` **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. If APEX @@ -91,7 +87,7 @@ rm adb-db-auth-enc e_sidb-db-auth-enc Open a port-forward to the ORDS service, for example: ```bash -kubectl port-forward service/ords-adb 8443:8443 +kubectl port-forward service/ords-adb -n ordsnamespace 8443:8443 ``` Direct your browser to: `https://localhost:8443/ords/adb` diff --git a/docs/ordsservices/examples/adb_oraoper.md b/docs/ordsservices/examples/adb_oraoper.md index b0872fb3..253365c5 100644 --- a/docs/ordsservices/examples/adb_oraoper.md +++ b/docs/ordsservices/examples/adb_oraoper.md @@ -4,23 +4,15 @@ This example walks through using the **ORDS Controller** with a Containerised Or When connecting to a mTLS enabled ADB while using the OraOperator to retreive the Wallet as is done in the example, it is currently not supported to have multiple, different databases supported by the single Ordssrvs resource. This is due to a requirement to set the `TNS_ADMIN` parameter at the Pod level ([#97](https://github.com/oracle/oracle-database-operator/issues/97)). -### Cert-Manager and Oracle Database Operator installation - -Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. +Before testing this example, please verify the prerequisites : [ORDSSRVS prerequisites](../README.md#prerequisites) ### Setup Oracle Cloud Authorisation -In order for the OraOperator to access the ADB, some pre-requisites are required, as detailed [here](https://github.com/oracle/oracle-database-operator/blob/main/docs/adb/ADB_PREREQUISITES.md). Either establish Instance Principles or create the required ConfigMap/Secret. This example uses the later: +In order for the OraOperator to access the ADB, some additional pre-requisites are required, as detailed [here](https://github.com/oracle/oracle-database-operator/blob/main/docs/adb/ADB_PREREQUISITES.md). +Either establish Instance Principles or create the required ConfigMap/Secret. This example uses the later, using the helper script [set_ocicredentials.sh](https://github.com/oracle/oracle-database-operator/blob/main/set_ocicredentials.sh) : ```bash -kubectl create configmap oci-cred \ ---from-literal=tenancy= \ ---from-literal=user= \ ---from-literal=fingerprint= \ ---from-literal=region= - -kubectl create secret generic oci-privatekey \ ---from-file=privatekey= +./set_ocicredentials.sh run -n ordsnamespace ``` ### ADB ADMIN Password Secret @@ -31,6 +23,7 @@ Create a Secret for the ADB Admin password: DB_PWD=$(echo "ORDSpoc_$(date +%H%S%M)") kubectl create secret generic adb-oraoper-db-auth \ + -n ordsnamespace \ --from-literal=adb-oraoper-db-auth=${DB_PWD} ``` @@ -40,51 +33,49 @@ kubectl create secret generic adb-oraoper-db-auth \ 1. Obtain the OCID of the ADB and set to an environment variable: - ``` - export ADB_OCID= - ``` + ```bash + export ADB_OCID= + ``` -1. Create a manifest to bind to the ADB. +1. Create and apply a manifest to bind to the ADB. + "adb-oraoper-tns-admin" secret will be created by the controller. - ```bash - echo " - apiVersion: database.oracle.com/v1alpha1 + ```yaml + apiVersion: database.oracle.com/v4 kind: AutonomousDatabase metadata: name: adb-oraoper + namespace: ordsnamespace spec: - hardLink: false - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey - details: - autonomousDatabaseOCID: $ADB_OCID - wallet: + action: Sync + wallet: name: adb-oraoper-tns-admin password: k8sSecret: - name: adb-oraoper-db-auth" | kubectl apply -f - + name: adb-oraoper-db-auth + details: + id: $ADB_OCID ``` 1. Update the ADMIN Password: -```bash - kubectl patch adb adb-oraoper --type=merge \ - -p '{"spec":{"details":{"adminPassword":{"k8sSecret":{"name":"adb-oraoper-db-auth"}}}}}' -``` + ```bash + kubectl patch adb adb-oraoper --type=merge \ + -n ordsnamespace \ + -p '{"spec":{"details":{"adminPassword":{"k8sSecret":{"name":"adb-oraoper-db-auth"}}}}}' + ``` 1. Watch the `adb` resource until the STATE is **AVAILABLE**: ```bash - kubectl get adb/adb-oraoper -w + kubectl get -n ordsnamespace adb/adb-oraoper -w ``` ### Create encrypted password - ```bash -echo ${DB_PWD} adb-db-auth-enc -openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.k +echo ${DB_PWD} > adb-db-auth-enc +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key openssl rsa -in ca.key -outform PEM -pubout -out public.pem kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace openssl rsautl -encrypt -pubin -inkey public.pem -in adb-db-auth-enc |base64 > e_adb-db-auth-enc @@ -92,24 +83,21 @@ kubectl create secret generic adb-oraoper-db-auth-enc --from-file=password=e_ad rm adb-db-auth-enc e_adb-db-auth-enc ``` - - ### Create OrdsSrvs Resource 1. Obtain the Service Name from the OraOperator - ```bash - SERVICE_NAME=$(kubectl get adb adb-oraoper -o=jsonpath='{.spec.details.dbName}'_TP) - ``` + ```bash + SERVICE_NAME=$(kubectl get -n ordsnamespace adb adb-oraoper -o=jsonpath='{.spec.details.dbName}'_TP) + ``` 1. Create a manifest for ORDS. As an ADB already maintains ORDS and APEX, `autoUpgradeORDS` and `autoUpgradeAPEX` will be ignored if set. A new DB User for ORDS will be created to avoid conflict with the pre-provisioned one. This user will be named, `ORDS_PUBLIC_USER_OPER` if `db.username` is either not specified or set to `ORDS_PUBLIC_USER`. - ```bash - echo " - apiVersion: database.oracle.com/v1 + ```yaml + apiVersion: database.oracle.com/v4 kind: OrdsSrvs metadata: name: ords-adb-oraoper @@ -117,10 +105,10 @@ rm adb-db-auth-enc e_adb-db-auth-enc spec: image: container-registry.oracle.com/database/ords:24.1.1 forceRestart: true - encPrivKey: - secretName: prvkey - passwordKey: privateKey - globalSettings: + encPrivKey: + secretName: prvkey + passwordKey: privateKey + globalSettings: database.api.enabled: true poolSettings: - poolName: adb-oraoper @@ -134,11 +122,9 @@ rm adb-db-auth-enc e_adb-db-auth-enc db.username: ORDS_PUBLIC_USER_OPER db.secret: secretName: adb-oraoper-db-auth-enc - passwordKey: adb-oraoper-db-auth-enc db.adminUser: ADMIN db.adminUser.secret: secretName: adb-oraoper-db-auth-enc - passwordKey: adb-oraoper-db-auth-enc" | kubectl apply -f - ``` latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** @@ -157,7 +143,7 @@ rm adb-db-auth-enc e_adb-db-auth-enc Open a port-forward to the ORDS service, for example: ```bash -kubectl port-forward service/ords-adb-oraoper 8443:8443 +kubectl port-forward service/ords-adb-oraoper -n ordsnamespace 8443:8443 ``` Direct your browser to: `https://localhost:8443/ords/adb-oraoper` diff --git a/docs/ordsservices/examples/existing_db.md b/docs/ordsservices/examples/existing_db.md new file mode 100644 index 00000000..6d4791ae --- /dev/null +++ b/docs/ordsservices/examples/existing_db.md @@ -0,0 +1,112 @@ +# Example: Pre-existing Database + +This example walks through configuring the ORDS Controller to use either a database deployed within Kubernetes, or an existing database external to your cluster. + +Before testing this example, please verify the prerequisites : [ORDSSRVS prerequisites](../README.md#prerequisites) + +### Database Access + +This example assumes you have a running, accessible Oracle Database. + +```bash +export CONN_STRING=:/ +``` + +### Create encrypted secrets + +```bash +DB_PWD= + +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key +openssl rsa -in ca.key -outform PEM -pubout -out public.pem +kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace + +echo "${DB_PWD}" > db-auth +openssl rsautl -encrypt -pubin -inkey public.pem -in db-auth |base64 > e_db-auth-enc +kubectl create secret generic db-auth-enc --from-file=password=e_db-auth-enc -n ordsnamespace + +rm db-auth e_db-auth-enc + +``` + +### Create ordssrvs Resource + +1. Create a manifest for ORDS. + + This example assumes APEX is already installed in the database. + + The following additional keys are specified for the pool: + * `autoUpgradeORDS` - Boolean; when true the ORDS will be installed/upgraded in the database + * `db.adminUser` - User with privileges to install, upgrade or uninstall ORDS in the database (SYS). + * `db.adminUser.secret` - Secret containing the password for `db.adminUser` (created in the first step) + * `db.username` will be used as the ORDS schema in the database during the install/upgrade process (ORDS_PUBLIC_USER). + + ```bash + echo " + apiVersion: database.oracle.com/v4 + kind: OrdsSrvs + metadata: + name: ords-db + namespace: ordsnamespace + spec: + image: container-registry.oracle.com/database/ords:24.1.1 + forceRestart: true + encPrivKey: + secretName: prvkey + passwordKey: privateKey + globalSettings: + database.api.enabled: true + poolSettings: + - poolName: default + autoUpgradeORDS: true + restEnabledSql.active: true + plsql.gateway.mode: direct + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//${CONN_STRING} + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: db-auth-enc + db.adminUser: SYS + db.adminUser.secret: + secretName: db-auth-enc + " > ords-db.yaml + + kubectl apply -f ords-db.yaml + ``` + + latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + +1. Watch the restdataservices resource until the status is **Healthy**: + ```bash + kubectl get ordssrvs ords-sidb -w + ``` + + **NOTE**: If this is the first time pulling the ORDS image, it may take up to 5 minutes. + + You can watch the APEX/ORDS Installation progress by running: + + ```bash + POD_NAME=$(kubectl get pod -l "app.kubernetes.io/instance=ords-sidb" -o custom-columns=NAME:.metadata.name -n ordsnamespace --no-headers) + + kubectl logs ${POD_NAME} -c ords-sidb-init -n ordsnamespace -f + ``` + +### Test + +Open a port-forward to the ORDS service, for example: + +```bash +kubectl port-forward service/ords-db -n ordsnamespace 8443:8443 +``` + +Direct your browser to: `https://localhost:8443/ords` + + +## Conclusion + +This example has a single database pool, named `default`. It is set to: + +* Automatically restart when the configuration changes: `forceRestart: true` +* Automatically install/update ORDS on startup, if required: `autoUpgradeORDS: true` +* Use a basic connection string to connect to the database: `db.customURL: jdbc:oracle:thin:@//${CONN_STRING}` +* The `passwordKey` has been ommitted from both `db.secret` and `db.adminUser.secret` as the password was stored in the default key (`password`) diff --git a/docs/ordsservices/examples/mongo_api.md b/docs/ordsservices/examples/mongo_api.md index 70391fbd..f0fd0cf5 100644 --- a/docs/ordsservices/examples/mongo_api.md +++ b/docs/ordsservices/examples/mongo_api.md @@ -2,11 +2,7 @@ This example walks through using the **ORDSSRVS Controller** with a Containerised Oracle Database to enable MongoDB API Support. - -### Cert-Manager and Oracle Database Operator installation - -Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. - +Before testing this example, please verify the prerequisites : [ORDSSRVS prerequisites](../README.md#prerequisites) ### Database Access @@ -39,13 +35,15 @@ In the database, create an ORDS-enabled user. As this example uses the [Contain ### Create encrypted secrets ```bash -openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.k + +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key openssl rsa -in ca.key -outform PEM -pubout -out public.pem kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace + +echo "${DB_PWD}" > sidb-db-auth-enc openssl rsautl -encrypt -pubin -inkey public.pem -in sidb-db-auth-enc |base64 > e_sidb-db-auth-enc kubectl create secret generic sidb-db-auth-enc --from-file=password=e_sidb-db-auth-enc -n ordsnamespace rm sidb-db-auth-enc e_sidb-db-auth-enc - ``` ### Create ordssrvs Resource @@ -71,7 +69,7 @@ rm sidb-db-auth-enc e_sidb-db-auth-enc ```bash echo " apiVersion: database.oracle.com/v4 - kind: ordssrvs + kind: OrdsSrvs metadata: name: ords-sidb namespace: ordsnamespace diff --git a/docs/ordsservices/examples/multi_pool.md b/docs/ordsservices/examples/multi_pool.md index 21c5f24d..ffb537bf 100644 --- a/docs/ordsservices/examples/multi_pool.md +++ b/docs/ordsservices/examples/multi_pool.md @@ -4,9 +4,8 @@ This example walks through using the **ORDSSRVS Operator** with multiple databas Keep in mind that all pools are running in the same Pod, therefore, changing the configuration of one pool will require a recycle of all pools. -### Cert-Manager and Oracle Database Operator installation +Before testing this example, please verify the prerequisites : [ORDSSRVS prerequisites](../README.md#prerequisites) -Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. ### TNS_ADMIN Secret @@ -86,25 +85,18 @@ If taking advantage of the [AutoUpgrade](../autoupgrade.md) functionality, creat In this example, only PDB1 will be set for [AutoUpgrade](../autoupgrade.md), the other PDBs already have APEX and ORDS installed. ```bash - - - echo "THIS_IS_A_PASSWORD" > syspwdfile -openssl rsautl -encrypt -pubin -inkey public.pem -in ordspwdfile |base64 > e_syspwdfile +openssl rsautl -encrypt -pubin -inkey public.pem -in syspwdfile |base64 > e_syspwdfile kubectl create secret generic pdb1-priv-auth-enc --from-file=password=e_syspwdfile -n ordsnamespace rm syspwdfile e_syspwdfile - -kubectl create secret generic pdb1-priv-auth \ - --from-literal=password=pdb1-battery-staple ``` ### Create OrdsSrvs Resource -1. Create a manifest for ORDS. +1. Create a manifest for ORDS, ords-multi-pool.yaml: - ```bash - echo " - apiVersion: database.oracle.com/v1 + ```yaml + apiVersion: database.oracle.com/v4 kind: OrdsSrvs metadata: name: ords-multi-pool @@ -166,10 +158,15 @@ kubectl create secret generic pdb1-priv-auth \ plsql.gateway.mode: proxied db.username: ORDS_PUBLIC_USER db.secret: - secretName: multi-ords-auth-enc" | kubectl apply -f - + secretName: multi-ords-auth-enc ``` latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** - + +1. Apply the yaml file: + ```bash + kubectl apply -f ords-multi-pool.yaml + ``` + 1. Watch the ordssrvs resource until the status is **Healthy**: ```bash kubectl get OrdsSrvs ords-multi-pool -n ordsnamespace -w diff --git a/docs/ordsservices/examples/ordsnamespace-role-binding.yaml b/docs/ordsservices/examples/ordsnamespace-role-binding.yaml new file mode 100644 index 00000000..018d8934 --- /dev/null +++ b/docs/ordsservices/examples/ordsnamespace-role-binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: ordsnamespace-oracle-database-operator-manager-rolebinding + namespace: ordsnamespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system diff --git a/docs/ordsservices/examples/sidb_container.md b/docs/ordsservices/examples/sidb_container.md index 804ecca4..3cda09ea 100644 --- a/docs/ordsservices/examples/sidb_container.md +++ b/docs/ordsservices/examples/sidb_container.md @@ -2,57 +2,50 @@ This example walks through using the **ORDSSRVS Controller** with a Containerised Oracle Database created by the **SIDB Controller** in the same Kubernetes Cluster. -### Cert-Manager and Oracle Database Operator installation - -Install the [Cert Manager](https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml) and the [Oracle Database Operator](https://github.com/oracle/oracle-database-operator) using the instractions in the Operator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. - +Before testing this example, please verify the prerequisites : [ORDSSRVS prerequisites](../README.md#prerequisites) ### Deploy a Containerised Oracle Database +Refer to Single Instance Database (SIDB) [README](https://github.com/oracle/oracle-database-operator/blob/main/docs/sidb/README.md) for details. + 1. Create a Secret for the Database password: ```bash - DB_PWD=$(echo "ORDSpoc_$(date +%H%S%M)") - - kubectl create secret generic sidb-db-auth \ - --from-literal=password=${DB_PWD} + DB_PWD= + kubectl create secret generic sidb-db-auth --from-literal=password=${DB_PWD} --namespace ordsnamespace ``` 1. Create a manifest for the containerised Oracle Database. The POC uses an Oracle Free Image, but other versions may be subsituted; review the OraOperator Documentation for details on the manifests. - ```bash - echo " - apiVersion: database.oracle.com/v1alpha1 + ```yaml + apiVersion: database.oracle.com/v4 kind: SingleInstanceDatabase metadata: name: oraoper-sidb + namespace: ordsnamespace spec: - replicas: 1 - image: - pullFrom: container-registry.oracle.com/database/free:23.4.0.0 - prebuiltDB: true - sid: FREE edition: free adminPassword: secretName: sidb-db-auth - secretKey: password - pdbName: FREEPDB1" | kubectl apply -f - + image: + pullFrom: container-registry.oracle.com/database/free:23.7.0.0 + prebuiltDB: true + replicas: 1 ``` - latest container-registry.oracle.com/database/free version, **23.4.0.0**, valid as of **2-May-2024** + latest container-registry.oracle.com/database/free version, **23.7.0.0-lite**, valid as of **2-May-2025** + 1. Watch the `singleinstancedatabases` resource until the database status is **Healthy**: ```bash - kubectl get singleinstancedatabases/oraoper-sidb -w + kubectl get singleinstancedatabases/oraoper-sidb -w -n ordsnamespace ``` - **NOTE**: If this is the first time pulling the free database image, it may take up to 15 minutes for the database to become available. ### Create encryped secret ```bash - openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key openssl rsa -in ca.key -outform PEM -pubout -out public.pem kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace @@ -61,8 +54,6 @@ echo "${DB_PWD}" > sidb-db-auth openssl rsautl -encrypt -pubin -inkey public.pem -in sidb-db-auth |base64 > e_sidb-db-auth kubectl create secret generic sidb-db-auth-enc --from-file=password=e_sidb-db-auth -n ordsnamespace rm sidb-db-auth e_sidb-db-auth - - ``` @@ -72,6 +63,7 @@ rm sidb-db-auth e_sidb-db-auth ```bash CONN_STRING=$(kubectl get singleinstancedatabase oraoper-sidb \ + -n ordsnamespace \ -o jsonpath='{.status.pdbConnectString}') echo $CONN_STRING @@ -89,7 +81,7 @@ rm sidb-db-auth e_sidb-db-auth ```bash echo " - apiVersion: database.oracle.com/v1 + apiVersion: database.oracle.com/v4 kind: OrdsSrvs metadata: name: ords-sidb @@ -97,6 +89,9 @@ rm sidb-db-auth e_sidb-db-auth spec: image: container-registry.oracle.com/database/ords:24.1.1 forceRestart: true + encPrivKey: + secretName: prvkey + passwordKey: privateKey globalSettings: database.api.enabled: true poolSettings: @@ -112,7 +107,10 @@ rm sidb-db-auth e_sidb-db-auth secretName: sidb-db-auth-enc db.adminUser: SYS db.adminUser.secret: - secretName: sidb-db-auth-enc" | kubectl apply -f - + secretName: sidb-db-auth-enc + " > ords-sidb.yaml + + kubectl apply -f ords-sidb.yaml ``` latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** From edfdaf1699070754586c7f40d3c587dbeb9e4552 Mon Sep 17 00:00:00 2001 From: marcstef Date: Fri, 9 May 2025 12:42:14 +0000 Subject: [PATCH 217/414] ORDSSRVS role-binding example --- docs/ordsservices/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ordsservices/README.md b/docs/ordsservices/README.md index 57195120..e2fa97be 100644 --- a/docs/ordsservices/README.md +++ b/docs/ordsservices/README.md @@ -49,7 +49,7 @@ Oracle Database Version: kubectl create namespace ordsnamespace ``` - Apply namespace role binding [ordsnamespace-role-binding.yaml](./ordsnamespace-role-binding.yaml): + Apply namespace role binding [ordsnamespace-role-binding.yaml](./examples/ordsnamespace-role-binding.yaml): ```bash kubectl apply -f ordsnamespace-role-binding.yaml ``` From a8e4321ff171f325e64e0d4ca11e367764f79199 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Mon, 12 May 2025 18:48:10 +0000 Subject: [PATCH 218/414] Fix README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 55eba951..56a21c9a 100644 --- a/README.md +++ b/README.md @@ -49,14 +49,11 @@ The Operator itself, as a product, brings the following new features: - Bug Fix: Prometheus label config * Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation. -<<<<<<< README.md ## New Product Features * The Operator itself, as a product, brings the following new features: * Published on `operatorhub.io` * Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) * Validated on Google Kubernetes Engine -======= ->>>>>>> README.md ## Overall Features Summary From 10b4facb1f9c2f0b512480088ecf0a91d48acc10 Mon Sep 17 00:00:00 2001 From: Rahil Budhwani Date: Tue, 13 May 2025 06:23:31 +0000 Subject: [PATCH 219/414] Bug-37516046: Patching to snapshot standby --- .../singleinstancedatabase_controller.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 13f2ec6f..b9352d83 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -3241,6 +3241,7 @@ func (r *SingleInstanceDatabaseReconciler) manageConvPhysicalToSnapshot(ctx cont if err != nil { return requeueY, err } + if sidbReadyPod.Name == "" { log.Info("No ready Pod for the requested singleinstancedatabase") return requeueY, nil @@ -3248,25 +3249,30 @@ func (r *SingleInstanceDatabaseReconciler) manageConvPhysicalToSnapshot(ctx cont if singleInstanceDatabase.Spec.ConvertToSnapshotStandby { // Convert a PHYSICAL_STANDBY -> SNAPSHOT_STANDBY - singleInstanceDatabase.Status.Status = dbcommons.StatusUpdating + if singleInstanceDatabase.Status.Status != dbcommons.StatusPending { + singleInstanceDatabase.Status.Status = dbcommons.StatusUpdating + } + r.Status().Update(ctx, &singleInstanceDatabase) if err := convertPhysicalStdToSnapshotStdDB(r, &singleInstanceDatabase, &sidbReadyPod, ctx, req); err != nil { + singleInstanceDatabase.Status.Status = dbcommons.StatusPending + r.Status().Update(ctx, &singleInstanceDatabase) switch err { case ErrNotPhysicalStandby: - r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Conversion to Snapshot Standby Not allowed", "Database not in physical standby role") - log.Info("Conversion to Snapshot Standby not allowed as database not in physical standby role") + r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Error: Conversion to Snapshot Standby Not allowed", "Database not in physical standby role") + log.Info("Error: Conversion to Snapshot Standby not allowed as database not in physical standby role") return requeueY, nil case ErrDBNotConfiguredWithDG: // cannot convert to snapshot database - r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Conversion to Snapshot Standby Not allowed", "Database is not configured with dataguard") + r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Error: Conversion to Snapshot Standby Not allowed", "Database is not configured with dataguard") log.Info("Conversion to Snapshot Standby not allowed as requested database is not configured with dataguard") return requeueY, nil case ErrFSFOEnabledForDGConfig: - r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Conversion to Snapshot Standby Not allowed", "Database is a FastStartFailover target") + r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Error: Conversion to Snapshot Standby Not allowed", "Database is a FastStartFailover target") log.Info("Conversion to Snapshot Standby Not allowed as database is a FastStartFailover target") return requeueY, nil case ErrAdminPasswordSecretNotFound: - r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Admin Password", "Database admin password secret not found") + r.Recorder.Event(&singleInstanceDatabase, corev1.EventTypeWarning, "Error: Admin Password", "Database admin password secret not found") log.Info("Database admin password secret not found") return requeueY, nil default: From e9969508c28f329e53ad94dcef5b7e4063cf0808 Mon Sep 17 00:00:00 2001 From: Ting-Lan Wang Date: Thu, 22 May 2025 10:25:13 -0400 Subject: [PATCH 220/414] Update readme with operatorhub.io --- README.md | 57 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 56a21c9a..df537efa 100644 --- a/README.md +++ b/README.md @@ -148,26 +148,20 @@ Oracle strongly recommends that you ensure your system meets the following [Prer ```sh kubectl apply -f rbac/node-rbac.yaml ``` -## Installation -### Install Oracle DB Operator - After you have completed the preceding prerequisite changes, you can install the operator. To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the preceding step. - - Run the following command +## Install Oracle DB Operator - ```sh - kubectl apply -f oracle-database-operator.yaml - ``` + After you have completed the preceding prerequisite changes, you can install the operator using one of the following methods: -## Install Oracle DB Operator +### Option 1: Install Using `oracle-database-operator.yaml` - After you have completed the preceding prerequisite changes, you can install the operator. To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the preceding step. + To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the preceding step. - Run the following command + Run the following command - ```sh - kubectl apply -f oracle-database-operator.yaml - ``` + ```sh + kubectl apply -f oracle-database-operator.yaml + ``` Ensure that the operator pods are up and running. For high availability, operator pod replicas are set to a default of 3. You can scale this setting up or down. @@ -181,12 +175,20 @@ Oracle strongly recommends that you ensure your system meets the following [Prer ``` -* Check the resources +### Option 2: Install via OperatorHub.io + + You can also install the Oracle DB Operator from [OperatorHub.io](https://operatorhub.io/operator/oracle-database-operator). + + 1. Visit the [Oracle Database Operator](https://operatorhub.io/operator/oracle-database-operator) page on OperatorHub.io. + + 2. Click the **Install** button to view and follow the step-by-step installation instructions for your Kubernetes environment. + +### Check the resources You should see that the operator is up and running, along with the shipped controllers. For more details, see [Oracle Database Operator Installation Instructions](./docs/installation/OPERATOR_INSTALLATION_README.md). -## Documentation + ## Getting Started with the Operator (Quickstart) The following quickstarts are designed for specific database configurations: @@ -237,6 +239,10 @@ YAML file templates are available under [`/config/samples`](./config/samples/). * ### Delete the Deployment + #### Option1: Delete `oracle-database-operator.yaml` + + Use this option if you install the operator using `oracle-database-operator.yaml` + After all CRD instances are deleted, it is safe to remove the CRDs, APIServices and operator deployment. To remove these files, use the following command: ```sh @@ -245,6 +251,25 @@ YAML file templates are available under [`/config/samples`](./config/samples/). Note: If the CRD instances are not deleted, and the operator is deleted by using the preceding command, then operator deployment and instance objects (pods, services, PVCs, and so on) are deleted. However, if that happens, then the CRD deletion stops responding. This is because the CRD instances have properties that prevent their deletion, and that can only be removed by the operator pod, which is deleted when the APIServices are deleted. + #### Option2: Delete the Operator’s ClusterServiceVersion (CSV) + + Use this option if you install the operation from OperatorHub.io. + + First, identify the name of the installed operator’s ClusterServiceVersion (CSV) using the following command: + + ```sh + kubectl clusterserviceversion -n operators + ``` + + Look for a CSV name similar to oracle-database-operator.vx.x.x. + + Once identified, delete the ClusterServiceVersion with the following command (replace the placeholder with the actual CSV name): + + ```sh + kubectl delete clusterserviceversion oracle-database-operator.vx.x.x -n operators + ``` + + ## Documentation for the supported Oracle Database configurations * [Oracle Autonomous Database](https://docs.oracle.com/en-us/iaas/Content/Database/Concepts/adboverview.htm) From 908209c6de8c5c2ecf4544eddaab446b3e5649f8 Mon Sep 17 00:00:00 2001 From: Shajahan Fariz Date: Thu, 15 May 2025 21:46:29 +0530 Subject: [PATCH 221/414] Reconcile dataguardbroker in case of update status errors --- controllers/dataguard/dataguard_utils.go | 2 ++ controllers/dataguard/dataguardbroker_controller.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/controllers/dataguard/dataguard_utils.go b/controllers/dataguard/dataguard_utils.go index 4c16f82b..a528dbdb 100644 --- a/controllers/dataguard/dataguard_utils.go +++ b/controllers/dataguard/dataguard_utils.go @@ -545,6 +545,8 @@ func patchService(r *DataguardBrokerReconciler, broker *dbapi.DataguardBroker, c lbAddress = svc.Status.LoadBalancer.Ingress[0].IP } broker.Status.ExternalConnectString = lbAddress + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/DATAGUARD" + } else { + return errors.New("load balancer ingress IP not available") } } else { nodeip := dbcommons.GetNodeIp(r, ctx, req) diff --git a/controllers/dataguard/dataguardbroker_controller.go b/controllers/dataguard/dataguardbroker_controller.go index 4d7ae044..6ed4e4d8 100644 --- a/controllers/dataguard/dataguardbroker_controller.go +++ b/controllers/dataguard/dataguardbroker_controller.go @@ -202,7 +202,7 @@ func (r *DataguardBrokerReconciler) Reconcile(ctx context.Context, req ctrl.Requ // Update Status for broker and sidb resources if err := updateReconcileStatus(r, &dataguardBroker, ctx, req); err != nil { - return ctrl.Result{Requeue: false}, err + return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, err } dataguardBroker.Status.Status = dbcommons.StatusReady From c149695e258539853d192dfce13470a8882ce89c Mon Sep 17 00:00:00 2001 From: Rahil Budhwani Date: Tue, 3 Jun 2025 05:25:52 +0000 Subject: [PATCH 222/414] Setting resource recommendations --- config/samples/sidb/singleinstancedatabase.yaml | 3 +++ docs/sidb/README.md | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index 368762f5..a99a569c 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -118,6 +118,9 @@ spec: ## memory is measured in bytes and can be expressed in plain integer or as a fixed-point number ## using one of these quantity suffixes: E, P, T, G, M, k. ## You can also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki. + ## For Enterprise Edition, the recommended values are: + ## cpu="2" + ## memory="16Gi" resources: ## requests denotes minimum node resources required/to be utilized by the database pod requests: diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 35f42f22..e1bc6148 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -649,10 +649,10 @@ The following table depicts the fail over matrix for any destructive operation t - If the `ReadWriteMany` access mode is used, then all the replicas will be distributed on different nodes. For this reason, Oracle recommends that you have replicas more than or equal to the number of the nodes, because the database image is downloaded on all those nodes. This is beneficial in quick cold fail-over scenario (when the active pod dies) as the image would already be available on that node. #### Database Pod Resource Management -When creating a Single Instance Database, you can specify the CPU and memory resources needed by the database pod. These specified resources are passed to the `kube-scheduler` so that the pod is scheduled on one of the pods that has the required resources available. To use database pod resource management, specify values for the `resources` attributes in the [`config/samples/sidb/singleinstancedatabase.yaml`](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. - -#### Database Pod Resource Management -When creating a Single Instance Database you can specify the cpu and memory resources needed by the database pod. These specified resources are passed to the `kube-scheduler` so that the pod gets scheduled on one of the pods that has the required resources available. To use database pod resource management specify values for the `resources` attributes in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. +When creating a Single Instance Database you can specify the cpu and memory resources needed by the database pod. These specified resources are passed to the `kube-scheduler` so that the pod gets scheduled on one of the nodes that has the required resources available. To use database pod resource management specify values for the `resources` attributes in the [config/samples/sidb/singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) file, and apply it. +For Enterprise Edition, the recommended values are: +cpu="2" +memory="16Gi" #### Setup Database with LoadBalancer For the Single Instance Database, the default service is the `NodePort` service. You can enable the `LoadBalancer` service by using the `kubectl patch` command. From e0f850447717d2ca157aef3dec44229a510bbace Mon Sep 17 00:00:00 2001 From: Shajahan Fariz Date: Tue, 3 Jun 2025 06:33:55 +0000 Subject: [PATCH 223/414] Build process enhancements for development builds and shortNames for some CRDs --- .gitlab-ci.yml | 8 +++---- Dockerfile | 22 +++++++++--------- Makefile | 23 ++++++++++--------- apis/database/v4/dataguardbroker_types.go | 1 + .../v4/oraclerestdataservice_types.go | 1 + .../v4/singleinstancedatabase_types.go | 5 ++-- .../database.oracle.com_dataguardbrokers.yaml | 3 +++ ...ase.oracle.com_oraclerestdataservices.yaml | 2 ++ ...se.oracle.com_singleinstancedatabases.yaml | 3 +++ config/manager/manager.yaml | 2 +- oracle-database-operator.yaml | 16 ++++++++++++- 11 files changed, 56 insertions(+), 30 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cac081cd..c9cd0768 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,11 +10,11 @@ build-operator: - make operator-yaml IMG=$IMAGE - if [ "$CI_COMMIT_BRANCH" = "master" ]; then podman run --rm --privileged multiarch/qemu-user-static --reset -p yes; - make docker-build docker-push IMG="$IMAGE" BUILD_MANIFEST=true; - docker manifest rm "$IMAGE"; + make image-build image-push IMG="$IMAGE" BUILD_MANIFEST=true; + podman manifest rm "$IMAGE"; else - make docker-build docker-push IMG="$IMAGE"; - docker rmi "$IMAGE"; + make image-build image-push IMG="$IMAGE"; + podman rmi "$IMAGE"; sed -i "s/\(replicas.\) 3/\1 1/g" ./$OP_YAML; fi - buildah containers -q | xargs -n1 buildah rm || true diff --git a/Dockerfile b/Dockerfile index f444d508..92952363 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,8 +3,9 @@ # # Build the manager binary -ARG BUILDER_IMG -FROM ${BUILDER_IMG} as builder +ARG BUILDER_IMG="oraclelinux:9" +ARG RUNNER_IMG="oraclelinux:9-slim" +FROM ${BUILDER_IMG} AS builder ARG TARGETARCH # Download golang if INSTALL_GO is set to true @@ -18,28 +19,27 @@ RUN if [ "$INSTALL_GO" = "true" ]; then \ echo "Go Arch: $(/usr/local/go/bin/go env GOARCH)"; \ fi ENV PATH=${GOLANG_VERSION:+"${PATH}:/usr/local/go/bin"} +ENV GOCACHE=/go-cache +ENV GOMODCACHE=/gomod-cache WORKDIR /workspace # Copy the Go Modules manifests COPY go.mod go.mod COPY go.sum go.sum -# cache deps before building and copying source so that we don't need to re-download as much -# and so that source changes don't invalidate our downloaded layer -RUN go mod download # Copy the go source +COPY LICENSE.txt LICENSE.txt +COPY THIRD_PARTY_LICENSES_DOCKER.txt THIRD_PARTY_LICENSES_DOCKER.txt COPY main.go main.go COPY apis/ apis/ -COPY controllers/ controllers/ COPY commons/ commons/ -COPY LICENSE.txt LICENSE.txt -COPY THIRD_PARTY_LICENSES_DOCKER.txt THIRD_PARTY_LICENSES_DOCKER.txt +COPY controllers/ controllers/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} GO111MODULE=on go build -a -o manager main.go +RUN --mount=type=cache,target=/go-cache --mount=type=cache,target=/gomod-cache CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} GO111MODULE=on go build -o manager main.go -# Use oraclelinux:9 as base image to package the manager binary -FROM oraclelinux:9 +# Use oraclelinux:9-slim as default base image to package the manager binary +FROM ${RUNNER_IMG} ARG CI_COMMIT_SHA ARG CI_COMMIT_BRANCH ENV COMMIT_SHA=${CI_COMMIT_SHA} \ diff --git a/Makefile b/Makefile index 33e52dd6..860eb57a 100644 --- a/Makefile +++ b/Makefile @@ -71,11 +71,13 @@ run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go GOLANG_VERSION ?= 1.23.3 +DOCKER ?= docker ## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. ## Otherwise, use golang image from docker hub as the builder. ifeq ($(BUILD_INTERNAL), true) BUILDER_IMG = oraclelinux:9 BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg GOLANG_VERSION=$(GOLANG_VERSION) --build-arg INSTALL_GO=true +DOCKER = podman else BUILDER_IMG = golang:$(GOLANG_VERSION) BUILD_ARGS = --build-arg BUILDER_IMG=$(BUILDER_IMG) --build-arg INSTALL_GO="false" --build-arg GOLANG_VERSION=$(GOLANG_VERSION) @@ -86,18 +88,18 @@ PUSH_ARGS := manifest else BUILD_ARGS := $(BUILD_ARGS) --platform=linux/amd64 --tag endif -docker-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast - docker build --no-cache=true --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ +image-build: #manifests generate fmt vet #test ## Build docker image with the manager. Disable the test but keep the validations to fail fast + $(DOCKER) build --build-arg http_proxy=$(HTTP_PROXY) --build-arg https_proxy=$(HTTPS_PROXY) \ --build-arg CI_COMMIT_SHA=$(CI_COMMIT_SHA) --build-arg CI_COMMIT_BRANCH=$(CI_COMMIT_BRANCH) \ $(BUILD_ARGS) $(IMG) . - -docker-push: ## Push docker image with the manager. - docker $(PUSH_ARGS) push $(IMG) + +image-push: ## Push docker image with the manager. + $(DOCKER) $(PUSH_ARGS) push $(IMG) # Push to minikube's local registry enabled by registry add-on minikube-push: - docker tag $(IMG) $$(minikube ip):5000/$(IMG) - docker push --tls-verify=false $$(minikube ip):5000/$(IMG) + $(DOCKER) tag $(IMG) $$(minikube ip):5000/$(IMG) + $(DOCKER) push --tls-verify=false $$(minikube ip):5000/$(IMG) ##@ Deployment @@ -123,7 +125,6 @@ operator-yaml: manifests kustomize (echo --- && sed '/^apiVersion: apps\/v1/,/---/!d' "$(OPERATOR_YAML).bak") >> "$(OPERATOR_YAML)" rm "$(OPERATOR_YAML).bak" -minikube-operator-yaml: IMG:=localhost:5000/$(IMG) minikube-operator-yaml: operator-yaml sed -i.bak 's/\(replicas.\) 3/\1 1/g' "$(OPERATOR_YAML)" rm "$(OPERATOR_YAML).bak" @@ -173,11 +174,11 @@ bundle: manifests kustomize ## Generate bundle manifests and metadata, then vali .PHONY: bundle-build bundle-build: ## Build the bundle image. - docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . + $(DOCKER) build -f bundle.Dockerfile -t $(BUNDLE_IMG) . .PHONY: bundle-push bundle-push: ## Push the bundle image. - $(MAKE) docker-push IMG=$(BUNDLE_IMG) + $(MAKE) image-push IMG=$(BUNDLE_IMG) .PHONY: opm OPM = ./bin/opm @@ -218,4 +219,4 @@ catalog-build: opm ## Build a catalog image. # Push the catalog image. .PHONY: catalog-push catalog-push: ## Push a catalog image. - $(MAKE) docker-push IMG=$(CATALOG_IMG) + $(MAKE) image-push IMG=$(CATALOG_IMG) diff --git a/apis/database/v4/dataguardbroker_types.go b/apis/database/v4/dataguardbroker_types.go index cec11ca4..80c2afe6 100644 --- a/apis/database/v4/dataguardbroker_types.go +++ b/apis/database/v4/dataguardbroker_types.go @@ -80,6 +80,7 @@ type DataguardBrokerStatus struct { } // +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=dgbroker;dgbrokers // +kubebuilder:subresource:status // +kubebuilder:printcolumn:JSONPath=".status.primaryDatabase",name="Primary",type="string" // +kubebuilder:printcolumn:JSONPath=".status.standbyDatabases",name="Standbys",type="string" diff --git a/apis/database/v4/oraclerestdataservice_types.go b/apis/database/v4/oraclerestdataservice_types.go index 20cc7a74..c58de9f4 100644 --- a/apis/database/v4/oraclerestdataservice_types.go +++ b/apis/database/v4/oraclerestdataservice_types.go @@ -126,6 +126,7 @@ type OracleRestDataServiceStatus struct { } //+kubebuilder:object:root=true +// +kubebuilder:resource:shortName=ords //+kubebuilder:subresource:status // +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type="string" // +kubebuilder:printcolumn:JSONPath=".spec.databaseRef",name="Database",type="string" diff --git a/apis/database/v4/singleinstancedatabase_types.go b/apis/database/v4/singleinstancedatabase_types.go index 4f4836d7..a8d004c7 100644 --- a/apis/database/v4/singleinstancedatabase_types.go +++ b/apis/database/v4/singleinstancedatabase_types.go @@ -193,8 +193,9 @@ type SingleInstanceDatabaseStatus struct { ConvertToSnapshotStandby bool `json:"convertToSnapshotStandby,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=sidb;sidbs +// +kubebuilder:subresource:status // +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas // +kubebuilder:printcolumn:JSONPath=".status.edition",name="Edition",type="string" // +kubebuilder:printcolumn:JSONPath=".status.sid",name="Sid",type="string",priority=1 diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index 0e27126d..40cc0e4a 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -11,6 +11,9 @@ spec: kind: DataguardBroker listKind: DataguardBrokerList plural: dataguardbrokers + shortNames: + - dgbroker + - dgbrokers singular: dataguardbroker scope: Namespaced versions: diff --git a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml index fe93a531..e4c3f379 100644 --- a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml @@ -11,6 +11,8 @@ spec: kind: OracleRestDataService listKind: OracleRestDataServiceList plural: oraclerestdataservices + shortNames: + - ords singular: oraclerestdataservice scope: Namespaced versions: diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index 8357f2c5..4bd2f0b7 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -11,6 +11,9 @@ spec: kind: SingleInstanceDatabase listKind: SingleInstanceDatabaseList plural: singleinstancedatabases + shortNames: + - sidb + - sidbs singular: singleinstancedatabase scope: Namespaced versions: diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 90d20e14..54340faf 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -31,7 +31,7 @@ spec: - /manager args: - --enable-leader-election - image: container-registry.oracle.com/database/operator:1.2.0 + image: controller:latest imagePullPolicy: Always name: manager resources: diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index e7b562b1..dc06e1c8 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -1790,6 +1790,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: databaseobservers.observability.oracle.com spec: @@ -8778,6 +8779,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: dataguardbrokers.database.oracle.com spec: @@ -8786,6 +8788,9 @@ spec: kind: DataguardBroker listKind: DataguardBrokerList plural: dataguardbrokers + shortNames: + - dgbroker + - dgbrokers singular: dataguardbroker scope: Namespaced versions: @@ -9747,6 +9752,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: lrests.database.oracle.com spec: @@ -10001,6 +10007,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: lrpdbs.database.oracle.com spec: @@ -10370,6 +10377,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: oraclerestdataservices.database.oracle.com spec: @@ -10378,6 +10386,8 @@ spec: kind: OracleRestDataService listKind: OracleRestDataServiceList plural: oraclerestdataservices + shortNames: + - ords singular: oraclerestdataservice scope: Namespaced versions: @@ -10732,6 +10742,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 name: ordssrvs.database.oracle.com spec: @@ -12973,6 +12984,9 @@ spec: kind: SingleInstanceDatabase listKind: SingleInstanceDatabaseList plural: singleinstancedatabases + shortNames: + - sidb + - sidbs singular: singleinstancedatabase scope: Namespaced versions: @@ -14747,7 +14761,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: lin.ocir.io/intsanjaysingh/operator/master/dboperator:latest + image: controller:latest imagePullPolicy: Always name: manager ports: From 6f0e1ced16eff53a2b0d72aeb6b3795ddd1c7140 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Tue, 17 Jun 2025 00:17:19 +0000 Subject: [PATCH 224/414] Adb switchover --- .../v1alpha1/autonomousdatabase_types.go | 2 +- apis/database/v4/autonomousdatabase_types.go | 2 +- commons/oci/database.go | 24 +++++++++++++++++++ ...tabase.oracle.com_autonomousdatabases.yaml | 4 ++++ .../database/autonomousdatabase_controller.go | 24 +++++++++++++++++++ 5 files changed, 54 insertions(+), 2 deletions(-) diff --git a/apis/database/v1alpha1/autonomousdatabase_types.go b/apis/database/v1alpha1/autonomousdatabase_types.go index 099703c2..ba0eb4e6 100644 --- a/apis/database/v1alpha1/autonomousdatabase_types.go +++ b/apis/database/v1alpha1/autonomousdatabase_types.go @@ -49,7 +49,7 @@ import ( // AutonomousDatabaseSpec defines the desired state of AutonomousDatabase // Important: Run "make" to regenerate code after modifying this file type AutonomousDatabaseSpec struct { - // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone + // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone;Switchover;Failover Action string `json:"action"` Details AutonomousDatabaseDetails `json:"details,omitempty"` Clone AutonomousDatabaseClone `json:"clone,omitempty"` diff --git a/apis/database/v4/autonomousdatabase_types.go b/apis/database/v4/autonomousdatabase_types.go index 628dd882..9db63c72 100644 --- a/apis/database/v4/autonomousdatabase_types.go +++ b/apis/database/v4/autonomousdatabase_types.go @@ -52,7 +52,7 @@ import ( // AutonomousDatabaseSpec defines the desired state of AutonomousDatabase // Important: Run "make" to regenerate code after modifying this file type AutonomousDatabaseSpec struct { - // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone + // +kubebuilder:validation:Enum:="";Create;Sync;Update;Stop;Start;Terminate;Clone;Switchover;Failover Action string `json:"action"` Details AutonomousDatabaseDetails `json:"details,omitempty"` Clone AutonomousDatabaseClone `json:"clone,omitempty"` diff --git a/commons/oci/database.go b/commons/oci/database.go index e43afb56..99240d1a 100644 --- a/commons/oci/database.go +++ b/commons/oci/database.go @@ -430,3 +430,27 @@ func (d *DatabaseService) CreateAutonomousDatabaseClone(adb *dbv4.AutonomousData return d.dbClient.CreateAutonomousDatabase(context.TODO(), request) } + +func (d *DatabaseService) SwitchoverAutonomousDatabase(adbOCID string) (database.SwitchoverAutonomousDatabaseResponse, error) { + retryPolicy := common.DefaultRetryPolicy() + + request := database.SwitchoverAutonomousDatabaseRequest{ + AutonomousDatabaseId: common.String(adbOCID), + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, + } + return d.dbClient.SwitchoverAutonomousDatabase(context.TODO(), request) +} + +func (d *DatabaseService) FailoverAutonomousDatabase(adbOCID string) (database.FailOverAutonomousDatabaseResponse, error) { + retryPolicy := common.DefaultRetryPolicy() + + request := database.FailOverAutonomousDatabaseRequest{ + AutonomousDatabaseId: common.String(adbOCID), + RequestMetadata: common.RequestMetadata{ + RetryPolicy: &retryPolicy, + }, + } + return d.dbClient.FailOverAutonomousDatabase(context.TODO(), request) +} diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index 1672ae81..c86ced26 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -64,6 +64,8 @@ spec: - Start - Terminate - Clone + - Switchover + - Failover type: string clone: properties: @@ -397,6 +399,8 @@ spec: - Start - Terminate - Clone + - Switchover + - Failover type: string clone: properties: diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 37ae1b14..db09de83 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -568,6 +568,30 @@ func (r *AutonomousDatabaseReconciler) performOperation( } return true, nil + case "Switchover": + l.Info("Sending SwitchoverAutonomousDatabase request to OCI") + + resp, err := r.dbService.SwitchoverAutonomousDatabase(*adb.Spec.Details.Id) + if err != nil { + return false, err + } + + adb.Spec.Action = "" + adb.Status.LifecycleState = resp.LifecycleState + return true, nil + + case "Failover": + l.Info("Sending FailOverAutonomousDatabase request to OCI") + + resp, err := r.dbService.FailoverAutonomousDatabase(*adb.Spec.Details.Id) + if err != nil { + return false, err + } + + adb.Spec.Action = "" + adb.Status.LifecycleState = resp.LifecycleState + return true, nil + case "": // No-op return false, nil From b64acd6dc9cd0aae39dea7935dfe53b304aba0d2 Mon Sep 17 00:00:00 2001 From: Yunus Qureshi Date: Thu, 17 Jul 2025 16:45:39 +0530 Subject: [PATCH 225/414] Update golang to 1.24.4 --- .gitlab-ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c9cd0768..d2cbc132 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,15 +5,16 @@ build-operator: OP_YAML: oracle-database-operator.yaml BUILD_INTERNAL: "true" script: - - export GOROOT=$(go1.23.3 env GOROOT) + - export GOLANG_VERSION=1.24.4 + - export GOROOT=$(go${GOLANG_VERSION} env GOROOT) - export PATH="${GOROOT}/bin:${PATH}" - - make operator-yaml IMG=$IMAGE + - make operator-yaml IMG=$IMAGE GOLANG_VERSION=$GOLANG_VERSION - if [ "$CI_COMMIT_BRANCH" = "master" ]; then podman run --rm --privileged multiarch/qemu-user-static --reset -p yes; - make image-build image-push IMG="$IMAGE" BUILD_MANIFEST=true; + make image-build image-push IMG="$IMAGE" BUILD_MANIFEST=true GOLANG_VERSION=$GOLANG_VERSION; podman manifest rm "$IMAGE"; else - make image-build image-push IMG="$IMAGE"; + make image-build image-push IMG="$IMAGE" GOLANG_VERSION=$GOLANG_VERSION; podman rmi "$IMAGE"; sed -i "s/\(replicas.\) 3/\1 1/g" ./$OP_YAML; fi From 830b54ff88ed5121ebee7295de4f5dcf03ebf202 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Thu, 24 Jul 2025 14:45:23 +0000 Subject: [PATCH 226/414] Update ADB controllers to use new webhook apis --- .../autonomouscontainerdatabase_webhook.go | 16 ++++++------- .../v1alpha1/autonomousdatabase_webhook.go | 12 ++++++---- .../autonomousdatabasebackup_webhook.go | 24 ++++++------------- .../autonomousdatabaserestore_webhook.go | 24 ++++++------------- .../v4/autonomouscontainerdatabase_webhook.go | 16 ++++++------- .../database/v4/autonomousdatabase_webhook.go | 12 ++++++---- .../v4/autonomousdatabasebackup_webhook.go | 24 ++++++------------- .../v4/autonomousdatabaserestore_webhook.go | 24 ++++++------------- 8 files changed, 56 insertions(+), 96 deletions(-) diff --git a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go index 10a16cd1..e90dfd40 100644 --- a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go @@ -39,6 +39,8 @@ package v1alpha1 import ( + "context" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -61,18 +63,17 @@ func (r *AutonomousContainerDatabase) SetupWebhookWithManager(mgr ctrl.Manager) //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomouscontainerdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomouscontainerdatabases,versions=v1alpha1,name=vautonomouscontainerdatabasev1alpha1.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &AutonomousContainerDatabase{} +var _ webhook.CustomValidator = &AutonomousContainerDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousContainerDatabase) ValidateCreate() (admission.Warnings, error) { - autonomouscontainerdatabaselog.Info("validate create", "name", r.Name) +func (r *AutonomousContainerDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { return nil, nil } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *AutonomousContainerDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList - var oldACD *AutonomousContainerDatabase = old.(*AutonomousContainerDatabase) + var oldACD *AutonomousContainerDatabase = oldObj.(*AutonomousContainerDatabase) autonomouscontainerdatabaselog.Info("validate update", "name", r.Name) @@ -103,9 +104,6 @@ func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) (admiss } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousContainerDatabase) ValidateDelete() (admission.Warnings, error) { - autonomouscontainerdatabaselog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. +func (r *AutonomousContainerDatabase) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) { return nil, nil } diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go index e209ae7a..f33444a1 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -39,6 +39,8 @@ package v1alpha1 import ( + "context" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -61,11 +63,11 @@ func (r *AutonomousDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v1alpha1,name=vautonomousdatabasev1alpha1.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &AutonomousDatabase{} +var _ webhook.CustomValidator = &AutonomousDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type // ValidateCreate checks if the spec is valid for a provisioning or a binding operation -func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { +func (r *AutonomousDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList autonomousdatabaselog.Info("validate create", "name", r.Name) @@ -92,9 +94,9 @@ func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *AutonomousDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList - var oldAdb *AutonomousDatabase = old.(*AutonomousDatabase) + var oldAdb *AutonomousDatabase = oldObj.(*AutonomousDatabase) autonomousdatabaselog.Info("validate update", "name", r.Name) @@ -158,7 +160,7 @@ func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.Erro } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabase) ValidateDelete() (admission.Warnings, error) { +func (r *AutonomousDatabase) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { autonomousdatabaselog.Info("validate delete", "name", r.Name) return nil, nil } diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go index ffa9b888..9feb6440 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go @@ -39,6 +39,8 @@ package v1alpha1 import ( + "context" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -59,21 +61,12 @@ func (r *AutonomousDatabaseBackup) SetupWebhookWithManager(mgr ctrl.Manager) err Complete() } -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,verbs=create;update,versions=v1alpha1,name=mautonomousdatabasebackupv1alpha1.kb.io,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &AutonomousDatabaseBackup{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) Default() { - autonomousdatabasebackuplog.Info("default", "name", r.Name) -} - //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabasebackup,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,versions=v1alpha1,name=vautonomousdatabasebackupv1alpha1.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &AutonomousDatabaseBackup{} +var _ webhook.CustomValidator = &AutonomousDatabaseBackup{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) ValidateCreate() (admission.Warnings, error) { +func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { autonomousdatabasebackuplog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -110,11 +103,11 @@ func (r *AutonomousDatabaseBackup) ValidateCreate() (admission.Warnings, error) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *AutonomousDatabaseBackup) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { autonomousdatabasebackuplog.Info("validate update", "name", r.Name) var allErrs field.ErrorList - oldBackup := old.(*AutonomousDatabaseBackup) + oldBackup := oldObj.(*AutonomousDatabaseBackup) if oldBackup.Spec.AutonomousDatabaseBackupOCID != nil && r.Spec.AutonomousDatabaseBackupOCID != nil && *oldBackup.Spec.AutonomousDatabaseBackupOCID != *r.Spec.AutonomousDatabaseBackupOCID { @@ -150,9 +143,6 @@ func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) (admission } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) ValidateDelete() (admission.Warnings, error) { - autonomousdatabasebackuplog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. +func (r *AutonomousDatabaseBackup) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) { return nil, nil } diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go index dcd57137..018d5521 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go @@ -39,6 +39,8 @@ package v1alpha1 import ( + "context" + dbv4 "github.com/oracle/oracle-database-operator/apis/database/v4" dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -62,10 +64,10 @@ func (r *AutonomousDatabaseRestore) SetupWebhookWithManager(mgr ctrl.Manager) er //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-autonomousdatabaserestore,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabaserestores,versions=v1alpha1,name=vautonomousdatabaserestorev1alpha1.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &AutonomousDatabaseRestore{} +var _ webhook.CustomValidator = &AutonomousDatabaseRestore{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) { +func (r *AutonomousDatabaseRestore) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { autonomousdatabaserestorelog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -125,23 +127,11 @@ func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseRestore) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - autonomousdatabaserestorelog.Info("validate update", "name", r.Name) - - var allErrs field.ErrorList - - if len(allErrs) == 0 { - return nil, nil - } - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseRestore"}, - r.Name, allErrs) +func (r *AutonomousDatabaseRestore) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + return nil, nil } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseRestore) ValidateDelete() (admission.Warnings, error) { - autonomousdatabaserestorelog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. +func (r *AutonomousDatabaseRestore) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) { return nil, nil } diff --git a/apis/database/v4/autonomouscontainerdatabase_webhook.go b/apis/database/v4/autonomouscontainerdatabase_webhook.go index 9fcb9d8b..5187ffab 100644 --- a/apis/database/v4/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v4/autonomouscontainerdatabase_webhook.go @@ -39,6 +39,8 @@ package v4 import ( + "context" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -60,18 +62,17 @@ func (r *AutonomousContainerDatabase) SetupWebhookWithManager(mgr ctrl.Manager) //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomouscontainerdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomouscontainerdatabases,versions=v4,name=vautonomouscontainerdatabasev4.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &AutonomousContainerDatabase{} +var _ webhook.CustomValidator = &AutonomousContainerDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousContainerDatabase) ValidateCreate() (admission.Warnings, error) { - autonomouscontainerdatabaselog.Info("validate create", "name", r.Name) +func (r *AutonomousContainerDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { return nil, nil } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *AutonomousContainerDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList - var oldACD *AutonomousContainerDatabase = old.(*AutonomousContainerDatabase) + var oldACD *AutonomousContainerDatabase = oldObj.(*AutonomousContainerDatabase) autonomouscontainerdatabaselog.Info("validate update", "name", r.Name) @@ -102,9 +103,6 @@ func (r *AutonomousContainerDatabase) ValidateUpdate(old runtime.Object) (admiss } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousContainerDatabase) ValidateDelete() (admission.Warnings, error) { - autonomouscontainerdatabaselog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. +func (r *AutonomousContainerDatabase) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) { return nil, nil } diff --git a/apis/database/v4/autonomousdatabase_webhook.go b/apis/database/v4/autonomousdatabase_webhook.go index f7eb60aa..4daa920f 100644 --- a/apis/database/v4/autonomousdatabase_webhook.go +++ b/apis/database/v4/autonomousdatabase_webhook.go @@ -39,6 +39,8 @@ package v4 import ( + "context" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -60,11 +62,11 @@ func (r *AutonomousDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { } // +kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomousdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v4,name=vautonomousdatabasev4.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &AutonomousDatabase{} +var _ webhook.CustomValidator = &AutonomousDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type // ValidateCreate checks if the spec is valid for a provisioning or a binding operation -func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { +func (r *AutonomousDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList autonomousdatabaselog.Info("validate create", "name", r.Name) @@ -91,9 +93,9 @@ func (r *AutonomousDatabase) ValidateCreate() (admission.Warnings, error) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *AutonomousDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList - var oldADB *AutonomousDatabase = old.(*AutonomousDatabase) + var oldADB *AutonomousDatabase = oldObj.(*AutonomousDatabase) autonomousdatabaselog.Info("validate update", "name", r.Name) @@ -157,7 +159,7 @@ func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.Erro } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabase) ValidateDelete() (admission.Warnings, error) { +func (r *AutonomousDatabase) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { autonomousdatabaselog.Info("validate delete", "name", r.Name) return nil, nil } diff --git a/apis/database/v4/autonomousdatabasebackup_webhook.go b/apis/database/v4/autonomousdatabasebackup_webhook.go index 7858adce..0cf1adc2 100644 --- a/apis/database/v4/autonomousdatabasebackup_webhook.go +++ b/apis/database/v4/autonomousdatabasebackup_webhook.go @@ -39,6 +39,8 @@ package v4 import ( + "context" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -59,21 +61,12 @@ func (r *AutonomousDatabaseBackup) SetupWebhookWithManager(mgr ctrl.Manager) err Complete() } -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-autonomousdatabasebackup,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,verbs=create;update,versions=v4,name=mautonomousdatabasebackupv4.kb.io,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &AutonomousDatabaseBackup{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) Default() { - autonomousdatabasebackuplog.Info("default", "name", r.Name) -} - //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomousdatabasebackup,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabasebackups,versions=v4,name=vautonomousdatabasebackupv4.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &AutonomousDatabaseBackup{} +var _ webhook.CustomValidator = &AutonomousDatabaseBackup{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) ValidateCreate() (admission.Warnings, error) { +func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { autonomousdatabasebackuplog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -110,11 +103,11 @@ func (r *AutonomousDatabaseBackup) ValidateCreate() (admission.Warnings, error) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *AutonomousDatabaseBackup) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { autonomousdatabasebackuplog.Info("validate update", "name", r.Name) var allErrs field.ErrorList - oldBackup := old.(*AutonomousDatabaseBackup) + oldBackup := oldObj.(*AutonomousDatabaseBackup) if oldBackup.Spec.AutonomousDatabaseBackupOCID != nil && r.Spec.AutonomousDatabaseBackupOCID != nil && *oldBackup.Spec.AutonomousDatabaseBackupOCID != *r.Spec.AutonomousDatabaseBackupOCID { @@ -150,9 +143,6 @@ func (r *AutonomousDatabaseBackup) ValidateUpdate(old runtime.Object) (admission } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseBackup) ValidateDelete() (admission.Warnings, error) { - autonomousdatabasebackuplog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. +func (r *AutonomousDatabaseBackup) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) { return nil, nil } diff --git a/apis/database/v4/autonomousdatabaserestore_webhook.go b/apis/database/v4/autonomousdatabaserestore_webhook.go index 6e3b4656..dd59f358 100644 --- a/apis/database/v4/autonomousdatabaserestore_webhook.go +++ b/apis/database/v4/autonomousdatabaserestore_webhook.go @@ -39,6 +39,8 @@ package v4 import ( + "context" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -61,10 +63,10 @@ func (r *AutonomousDatabaseRestore) SetupWebhookWithManager(mgr ctrl.Manager) er //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomousdatabaserestore,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabaserestores,versions=v4,name=vautonomousdatabaserestorev4.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &AutonomousDatabaseRestore{} +var _ webhook.CustomValidator = &AutonomousDatabaseRestore{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) { +func (r *AutonomousDatabaseRestore) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { autonomousdatabaserestorelog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -124,23 +126,11 @@ func (r *AutonomousDatabaseRestore) ValidateCreate() (admission.Warnings, error) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseRestore) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - autonomousdatabaserestorelog.Info("validate update", "name", r.Name) - - var allErrs field.ErrorList - - if len(allErrs) == 0 { - return nil, nil - } - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseRestore"}, - r.Name, allErrs) +func (r *AutonomousDatabaseRestore) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + return nil, nil } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *AutonomousDatabaseRestore) ValidateDelete() (admission.Warnings, error) { - autonomousdatabaserestorelog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. +func (r *AutonomousDatabaseRestore) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) { return nil, nil } From 052ec01d4642adcb1a8d9f46e4128ad7f3737eba Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Fri, 25 Jul 2025 03:33:50 +0000 Subject: [PATCH 227/414] Psaini orestart --- Makefile | 6 +- PROJECT | 9 + README.md | 1 + apis/database/v4/oraclerestart_types.go | 375 ++ apis/database/v4/oraclerestart_webhook.go | 834 ++++ apis/database/v4/zz_generated.deepcopy.go | 562 +++ cmd/main.go | 471 +++ commons/oraclerestart/exec.go | 112 + commons/oraclerestart/oraclerestartcommon.go | 1376 +++++++ .../oraclerestart/oraclerestartconstants.go | 159 + commons/oraclerestart/oraclerestartprov.go | 1034 +++++ commons/oraclerestart/oraclerestartstatus.go | 344 ++ commons/oraclerestart/utils/utils.go | 179 + ...database.oracle.com_oraclerestartnews.yaml | 1468 +++++++ .../database.oracle.com_oraclerestarts.yaml | 1419 +++++++ ...base.oracle.com_oraclerestartservices.yaml | 1468 +++++++ config/crd/kustomization.yaml | 1 + config/manager/kustomization.yaml | 4 +- .../database_oraclerestart_admin_role.yaml | 27 + .../database_oraclerestart_editor_role.yaml | 33 + .../database_oraclerestart_viewer_role.yaml | 29 + config/rbac/kustomization.yaml | 8 + config/rbac/role.yaml | 13 +- config/samples/database_v4_oraclerestart.yaml | 9 + config/samples/kustomization.yaml | 1 + config/webhook/manifests.yaml | 41 + .../database/oraclerestart_controller.go | 3362 +++++++++++++++++ .../orestart/custom-kubeletconfig.yaml | 13 + docs/config/samples/orestart/custom-scc.yaml | 50 + docs/oraclerestart/README.md | 60 + docs/oraclerestart/provisioning/cleanup.md | 17 + .../create_kubernetes_secret_for_db_user.md | 25 + .../create_kubernetes_secret_for_ssh_setup.md | 17 + .../provisioning/database_connection.md | 47 + docs/oraclerestart/provisioning/debugging.md | 41 + .../provisioning/known_issues.md | 0 .../provisioning/oraclerestart_prov.yaml | 92 + .../oraclerestart_prov_nodeports.yaml | 104 + .../oraclerestart_prov_rupatch.yaml | 107 + .../oraclerestart_prov_storage.yaml | 105 + .../provisioning/orestart_nodeport_object.txt | 154 + .../provisioning/orestart_object.txt | 127 + .../provisioning/orestart_rupatch_object.txt | 158 + .../provisioning/orestart_storage_object.txt | 166 + .../prerequisites_oracle_restart_db.md | 282 ++ .../provisioning_oracle_restart_db.md | 44 + ...provisioning_oracle_restart_db_nodeport.md | 44 + .../provisioning_oracle_restart_db_rupatch.md | 63 + ...ovisioning_oracle_restart_storage_class.md | 42 + go.mod | 5 +- go.sum | 5 + main.go | 128 +- oracle-database-operator.yaml | 1547 +++++++- 53 files changed, 16744 insertions(+), 44 deletions(-) create mode 100644 apis/database/v4/oraclerestart_types.go create mode 100644 apis/database/v4/oraclerestart_webhook.go create mode 100644 cmd/main.go create mode 100644 commons/oraclerestart/exec.go create mode 100644 commons/oraclerestart/oraclerestartcommon.go create mode 100644 commons/oraclerestart/oraclerestartconstants.go create mode 100644 commons/oraclerestart/oraclerestartprov.go create mode 100644 commons/oraclerestart/oraclerestartstatus.go create mode 100644 commons/oraclerestart/utils/utils.go create mode 100644 config/crd/bases/database.oracle.com_oraclerestartnews.yaml create mode 100644 config/crd/bases/database.oracle.com_oraclerestarts.yaml create mode 100644 config/crd/bases/database.oracle.com_oraclerestartservices.yaml create mode 100644 config/rbac/database_oraclerestart_admin_role.yaml create mode 100644 config/rbac/database_oraclerestart_editor_role.yaml create mode 100644 config/rbac/database_oraclerestart_viewer_role.yaml create mode 100644 config/samples/database_v4_oraclerestart.yaml create mode 100644 controllers/database/oraclerestart_controller.go create mode 100644 docs/config/samples/orestart/custom-kubeletconfig.yaml create mode 100644 docs/config/samples/orestart/custom-scc.yaml create mode 100644 docs/oraclerestart/README.md create mode 100644 docs/oraclerestart/provisioning/cleanup.md create mode 100644 docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md create mode 100644 docs/oraclerestart/provisioning/create_kubernetes_secret_for_ssh_setup.md create mode 100644 docs/oraclerestart/provisioning/database_connection.md create mode 100644 docs/oraclerestart/provisioning/debugging.md create mode 100644 docs/oraclerestart/provisioning/known_issues.md create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml create mode 100644 docs/oraclerestart/provisioning/orestart_nodeport_object.txt create mode 100644 docs/oraclerestart/provisioning/orestart_object.txt create mode 100644 docs/oraclerestart/provisioning/orestart_rupatch_object.txt create mode 100644 docs/oraclerestart/provisioning/orestart_storage_object.txt create mode 100644 docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md diff --git a/Makefile b/Makefile index 860eb57a..2aa6af9a 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ IMG ?= controller:latest # https://github.com/kubernetes-sigs/kubebuilder/issues/1140 CRD_OPTIONS ?= "crd:maxDescLen=0,allowDangerousTypes=true" # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.29.0 +ENVTEST_K8S_VERSION = 1.31.0 # Operator YAML file OPERATOR_YAML=$$(basename $$(pwd)).yaml # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) @@ -70,7 +70,7 @@ build: generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go -GOLANG_VERSION ?= 1.23.3 +GOLANG_VERSION ?= 1.24.4 DOCKER ?= docker ## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. ## Otherwise, use golang image from docker hub as the builder. @@ -145,7 +145,7 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions -KUSTOMIZE_VERSION ?= v5.3.0 +KUSTOMIZE_VERSION ?= v5.4.3 CONTROLLER_TOOLS_VERSION ?= v0.16.5 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" diff --git a/PROJECT b/PROJECT index f55c035c..d51d3f7c 100644 --- a/PROJECT +++ b/PROJECT @@ -259,4 +259,13 @@ resources: webhooks: conversion: true webhookVersion: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: oracle.com + group: database + kind: OracleRestart + path: github.com/oracle/oracle-database-operator/api/database/v4 + version: v4 version: "3" diff --git a/README.md b/README.md index df537efa..2817ba72 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,7 @@ The following quickstarts are designed for specific database configurations: * [Oracle Multitenant Database](./docs/multitenant/README.md) * [Oracle Base Database Service (OBDS)](./docs/dbcs/README.md) * [ORDS Services (ORDSSRVS)](./docs/ordsservices/README.md) +* [Oracle Restart Database](./docs/oraclerestart/README.md) The following quickstart is designed for non-database configurations: diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go new file mode 100644 index 00000000..57601030 --- /dev/null +++ b/apis/database/v4/oraclerestart_types.go @@ -0,0 +1,375 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "sync" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// OracleRestartSpec defines the desired state of OracleRestart +type OracleRestartSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + InstDetails OracleRestartInstDetailSpec `json:"instDetails"` + ConfigParams *InitParams `json:"configParams,omitempty"` + AsmStorageDetails *AsmDiskDetails `json:"asmStorageDetails,omitempty"` + NfsStorageDetails *corev1.NFSVolumeSource `json:"nfsStorageDetails,omitempty"` + UseNfsforSwStorage string `json:"useNfsforSwStorage,omitempty"` + StorageClass string `json:"storageClass,omitempty"` + StorageSizeInGB int `json:"storageSizeInGB,omitempty"` + Image string `json:"image,omitempty"` + ImagePullSecret string `json:"imagePullSecret,omitempty"` + ScriptsLocation string `json:"scriptsLocation,omitempty"` + IsDeleteOraPvc string `json:"isDeleteOraPvc,omitempty"` + SshKeySecret *OracleRestartSshSecretDetails `json:"sshKeySecret,omitempty"` + ImagePullPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` + ScriptsGetCmd string `json:"scriptsGetCmd,omitempty"` + IsDebug string `json:"isDebug,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext"` + IsDeleteTopolgy string `json:"isDeleteTopology,omitempty"` + ExternalSvcType *string `json:"externalSvcType,omitempty"` + DbSecret *OracleRestartDbPwdSecretDetails `json:"dbSecret,omitempty"` + TdeWalletSecret *OracleRestartDbPwdSecretDetails `json:"tdeWalletSecret,omitempty"` + ServiceDetails ServiceSpec `json:"serviceDetails,omitempty"` + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` + IsFailed bool `json:"isFailed,omitempty"` + IsManual bool `json:"isManual,omitempty"` + SrvAccountName string `json:"serviceAccountName,omitempty"` +} + +type AsmDiskDetails struct { + DisksBySize []DiskBySize `json:"disksBySize,omitempty"` + AutoUpdate string `json:"autoUpdate,omitempty"` +} + +// DiskBySize represents a list of disks grouped by size +type DiskBySize struct { + StorageSizeInGb int `json:"storageSizeInGb,omitempty"` + DiskNames []string `json:"diskNames,omitempty"` +} + +type InitParams struct { + GridHome string `json:"gridHome,omitempty"` + DbHome string `json:"dbHome,omitempty"` + GridBase string `json:"gridBase,omitempty"` + DbBase string `json:"dbBase,omitempty"` + Inventory string `json:"inventory,omitempty"` + GridResponseFile ResponseFile `json:"gridResponseFile,omitempty"` + DbResponseFile ResponseFile `json:"dbResponseFile,omitempty"` + GridSwZipFile string `json:"gridSwZipFile,omitempty"` + DbSwZipFile string `json:"dbSwZipFile,omitempty"` + OPatchSwZipFile string `json:"oPatchSwZipFile,omitempty"` + StagingSoftwareLocation string `json:"stagingSoftwareLocation,omitempty"` + OpType string `json:"opType,omitempty"` + CpuCount int `json:"cpuCount,omitempty"` + SgaSize string `json:"sgaSize,omitempty"` + PgaSize string `json:"pgaSize,omitempty"` + Processes int `json:"processes,omitempty"` + DbUniqueName string `json:"dbUniqueName,omitempty"` + CrsAsmDiskDg string `json:"crsAsmDiskDg,omitempty"` + CrsAsmDeviceList string `json:"crsAsmDeviceList,omitempty"` + DbRecoveryFileDest string `json:"dbRecoveryFileDest,omitempty"` + DbRecoveryFileDestSize string `json:"dbRecoveryFileDestSize,omitempty"` + DbDataFileDestDg string `json:"dbDataFileDestDg,omitempty"` + CrsAsmDiskDgRedundancy string `json:"crsAsmDiskDgRedundancy,omitempty"` + DBAsmDiskDgRedundancy string `json:"dbAsmDiskDgRedundancy,omitempty"` + RecoAsmDiskDgRedundancy string `json:"recoAsmDiskDgRedudancy,omitempty"` + RedoAsmDiskDgRedudancy string `json:"redoAsmDiskDgRedundancy,omitempty"` + DbName string `json:"dbName,omitempty"` + PdbName string `json:"pdbName,omitempty"` + DbStorageType string `json:"dbStorageType,omitempty"` + DbAsmDeviceList string `json:"dbAsmDeviceList,omitempty"` + RecoAsmDeviceList string `json:"recoAsmDeviceList,omitempty"` + RedoAsmDeviceList string `json:"redoAsmDeviceList,omitempty"` + DbCharSet string `json:"dbCharSet,omitempty"` + DbRedoFileSize string `json:"dbRedoFileSize,omitempty"` + DbType string `json:"dbType,omitempty"` + DbConfigType string `json:"dbConfigType,omitempty"` + EnableArchiveLog string `json:"enableArchiveLog,omitempty"` + SwMountLocation string `json:"swMountLocation,omitempty"` + HostSwStageLocation string `json:"hostSwStageLocation,omitempty"` + RuPatchLocation string `json:"ruPatchLocation,omitempty"` + RuFolderName string `json:"ruFolderName,omitempty"` + OPatchLocation string `json:"oPatchLocation,omitempty"` +} + +type OracleRestartInstDetailSpec struct { + Name string `json:"name"` + HostSwLocation string `json:"hostSwLocation,omitempty"` + WorkerNode []string `json:"workerNode,omitempty"` + EnvVars []corev1.EnvVar `json:"envVars,omitempty"` //Optional Env variables for Shards + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` //Optional resource requiremen + Label string `json:"label,omitempty"` + IsDelete string `json:"isDelete,omitempty"` + IsForceDelete string `json:"isForceDelete,omitempty"` + IsKeepPVC string `json:"isKeepPVC,omitempty"` + PvcName map[string]string `json:"pvcName,omitempty"` + NodePortSvc []OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if + PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least + EnvFile string `json:"envFile,omitempty"` +} + +// Responsefile Name +type ResponseFile struct { + ConfigMapName string `json:"configMapName,omitempty"` + Name string `json:"name,omitempty"` +} + +// NetworkDetailsSPec defines the OracleRestart network + +type NetworkDetailSpec struct { + Name string `json:"name,omitempty"` + IPs []string `json:"ips,omitempty"` + Interface string `json:"interface,omitempty"` + Namespace string `json:"namespace,omitempty"` + Mac string `json:"mac,omitempty"` +} + +// OracleRestart DB Secret Details +type OracleRestartDbPwdSecretDetails struct { + Name string `json:"name,omitempty"` // Name of the secret. + KeyFileName string `json:"keyFileName,omitempty"` // Name of the key. + PwdFileName string `json:"pwdFileName,omitempty"` + PwdFileMountLocation string `json:"pwdFileMountLocation,omitempty"` + KeyFileMountLocation string `json:"keyFileMountLocation,omitempty"` + KeySecretName string `json:"keySecretName,omitempty"` + EncryptionType string `json:"encryptionType,omitempty"` +} + +// OracleRestart Ssh secret Details +type OracleRestartSshSecretDetails struct { + Name string `json:"name"` // Name of the secret. + KeyMountLocation string `json:"keyMountLocation,omitempty"` + PrivKeySecretName string `json:"privKeySecretName,omitempty"` + PubKeySecretName string `json:"pubKeySecretName,omitempty"` +} + +// Service Definition +type ServiceSpec struct { + Name string `json:"name"` // Name of the shardSpace. + Cardinality string `json:"cardinality,omitempty"` + Preferred []string `json:"preferred,omitempty"` + TafPolicy string `json:"tafPolicy,omitempty"` + Available []string `json:"available,omitempty"` + Role string `json:"role,omitempty"` + Notification string `json:"notification,omitempty"` + CommitOutCome string `json:"commitOutcome,omitempty"` + CommitOutComeFastPath string `json:"commitOutComeFastPath,omitempty"` + Retention int `json:"retenion,omitempty"` + SessionState string `json:"sessionState,omitempty"` + Pdb string `json:"pdb,omitempty"` + StopOption string `json:"stopOption,omitempty"` + DrainTimeOut int `json:"drainTimeOut,omitempty"` + FailOverType string `json:"failOverType,omitempty"` + FailOverDelay int `json:"failOverDelay,omitempty"` + FailOverRetry int `json:"failOverRetry,omitempty"` + FailBack string `json:"failBack,omitempty"` + FailOverRestore string `json:"failOverRestore,omitempty"` + ClbGoal string `json:"clbGoal,omitempty"` + RlbGoal string `json:"rlbGoal,omitempty"` + Dtp string `json:"dtp,omitempty"` + Edition string `json:"edition,omitempty"` + SvcState string `json:"svcState,omitempty"` +} + +// OracleRestartStatus defines the observed state of OracleRestart +type OracleRestartStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + DbName string `json:"DbName,omitempty"` + ConnectString string `json:"connectString,omitempty"` + PdbConnectString string `json:"pdbConnectString,omitempty"` + OracleRestartNodes []*OracleRestartNodestatus `json:"OracleRestartNodes,omitempty"` + ReleaseUpdate string `json:"releaseUpdate,omitempty"` + Role string `json:"role,omitempty"` + DbState string `json:"dbState,omitempty"` + State string `json:"state,omitempty"` + InstallNode string `json:"installNode,omitempty"` + ClientEtcHost []string `json:"clientEtcHost,omitempty"` + + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + InstDetails OracleRestartInstDetailSpec `json:"instDetails,omitempty"` + ConfigParams *InitParams `json:"configParams,omitempty"` + AsmDetails *AsmInstanceStatus `json:"asmDetails,omitempty"` + NfsStorageDetails *corev1.NFSVolumeSource `json:"nfsStorageDetails,omitempty"` + UseNfsforSwStorage string `json:"useNfsforSwStorage,omitempty"` + StorageClass string `json:"storageClass,omitempty"` + StorageSizeInGB int `json:"storageSizeInGB,omitempty"` + Image string `json:"image,omitempty"` + ImagePullSecret string `json:"imagePullSecret,omitempty"` + ScriptsLocation string `json:"scriptsLocation,omitempty"` + IsDeleteOraPvc string `json:"isDeleteOraPvc,omitempty"` + SshKeySecret *OracleRestartSshSecretDetails `json:"sshKeySecret,omitempty"` + ImagePullPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` + ScriptsGetCmd string `json:"scriptsGetCmd,omitempty"` + IsDebug string `json:"isDebug,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + IsDeleteTopolgy string `json:"isDeleteTopology,omitempty"` + ExternalSvcType *string `json:"externalSvcType,omitempty"` + DbSecret *OracleRestartDbPwdSecretDetails `json:"dbSecret,omitempty"` + TdeWalletSecret *OracleRestartDbPwdSecretDetails `json:"tdeWalletSecret,omitempty"` + ServiceDetails ServiceSpec `json:"serviceDetails,omitempty"` + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` + OldSpec string `json:"oldSpec,omitempty"` +} + +type OracleRestartNodePortSvc struct { + PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` + SvcName string `json:"name,omitempty"` + SvcType string `json:"svcType,omitempty"` +} + +type OracleRestartPortMapping struct { + Port int32 `json:"port,omitempty"` + TargetPort int32 `json:"targetPort,omitempty"` + Protocol corev1.Protocol `json:"protocol,omitempty"` + NodePort int32 `json:"nodePort,omitempty"` +} + +type OracleRestartNodestatus struct { + Name string `json:"name,omitempty"` + NodeDetails *OracleRestartNodeDetailedStatus `json:"nodeDetails,omitempty"` +} + +type OracleRestartNodeDetailedStatus struct { + WorkerNode string `json:"workerNode,omitempty"` //Optional Env variables for Shards + PvcName map[string]string `json:"pvcName,omitempty"` + NodePortSvc []OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if + PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` + ClusterState string `json:"clusterState,omitempty"` + InstanceState string `json:"InstanceState,omitempty"` + PodState string `json:"PodState,omitempty"` + IsDelete string `json:"isDelete,omitempty"` + State string `json:"state,omitempty"` + MountedDevices []string `json:"mountedDevices,omitempty"` +} + +type AsmInstanceStatus struct { + Diskgroup []AsmDiskgroupStatus `json:"diskgroup,omitempty"` +} + +type AsmDiskgroupStatus struct { + Name string `json:"name,omitempty"` + Disks []string `json:"disks,omitempty"` + Redundancy string `json:"redundancy,omitempty"` +} + +type OracleRestartLifecycleState string + +const ( + OracleRestartAvailableState OracleRestartLifecycleState = "AVAILABLE" + OracleRestartFailedState OracleRestartLifecycleState = "FAILED" + OracleRestartUpdateState OracleRestartLifecycleState = "UPDATING" + OracleRestartProvisionState OracleRestartLifecycleState = "PROVISIONING" + OracleRestartPendingState OracleRestartLifecycleState = "PENDING" + OracleRestartFieldNotDefined OracleRestartLifecycleState = "NOT_DEFINED" + OracleRestartPodNotReadyState OracleRestartLifecycleState = "PODNOTREADY" + OracleRestartPodFailureState OracleRestartLifecycleState = "PODFAILURE" + OracleRestartPodNotFound OracleRestartLifecycleState = "PODNOTFOUND" + OracleRestartStatefulSetFailure OracleRestartLifecycleState = "STATEFULSETFAILURE" + OracleRestartStatefulSetNotFound OracleRestartLifecycleState = "STATEFULSETNOTFOUND" + OracleRestartPodAvailableState OracleRestartLifecycleState = "PODAVAILABLE" + OracleRestartDeletingState OracleRestartLifecycleState = "DELETING" + OracleRestartDeleteErrorState OracleRestartLifecycleState = "DELETE_ERROR" + OracleRestartTerminated OracleRestartLifecycleState = "TERMINATED" + OracleRestartLabelPatchingError OracleRestartLifecycleState = "LABELPATCHINGERROR" + OracleRestartDeletePVCError OracleRestartLifecycleState = "DELETEPVCERROR" + OracleRestartAddInstState OracleRestartLifecycleState = "OracleRestart_INST_ADDITION" + OracleRestartManualState OracleRestartLifecycleState = "MANUAL" +) + +type OracleRestartCrdReconcileState string + +const ( + OracleRestartCrdReconcileErrorState OracleRestartCrdReconcileState = "ReconcileError" + OracleRestartCrdReconcileErrorReason OracleRestartCrdReconcileState = "LastReconcileCycleFailed" + OracleRestartCrdReconcileQueuedState OracleRestartCrdReconcileState = "ReconcileQueued" + OracleRestartCrdReconcileQueuedReason OracleRestartCrdReconcileState = "LastReconcileCycleQueued" + OracleRestartCrdReconcileCompeleteState OracleRestartCrdReconcileState = "ReconcileComplete" + OracleRestartCrdReconcileCompleteReason OracleRestartCrdReconcileState = "LastReconcileCycleCompleted" + OracleRestartCrdReconcileWaitingState OracleRestartCrdReconcileState = "ReconcileWaiting" + OracleRestartCrdReconcileWaitingReason OracleRestartCrdReconcileState = "LastReconcileCycleWaiting" +) + +// var +var OracleRestartKubeConfigOnce sync.Once + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:JSONPath=".status.configParams.dbName",name="DbName",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.dbState",name="DbState",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.role",name="Role",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.releaseUpdate",name="Version",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.pdbConnectString",name="Pdb Connect Str",type="string" +// +kubebuilder:printcolumn:JSONPath=".status.state",name="State",type="string" +// OracleRestart is the Schema for the OracleRestarts API +type OracleRestart struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec OracleRestartSpec `json:"spec,omitempty"` + Status OracleRestartStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// OracleRestartList contains a list of OracleRestart +type OracleRestartList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []OracleRestart `json:"items"` +} + +func init() { + SchemeBuilder.Register(&OracleRestart{}, &OracleRestartList{}) +} diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go new file mode 100644 index 00000000..f9660314 --- /dev/null +++ b/apis/database/v4/oraclerestart_webhook.go @@ -0,0 +1,834 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "context" + "fmt" + "reflect" + "regexp" + "strings" + + utils "github.com/oracle/oracle-database-operator/commons/oraclerestart/utils" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + corev1 "k8s.io/api/core/v1" +) + +// log is for logging in this package. +var OracleRestartlog = logf.Log.WithName("OracleRestart-resource") + +func (r *OracleRestart) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&OracleRestart{}). + WithDefaulter(r). + WithValidator(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-oraclerestart,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=oraclerestarts,verbs=create;update,versions=v4,name=moraclerestart.kb.io,admissionReviewVersions={v1} + +var _ webhook.CustomDefaulter = &OracleRestart{} + +func (r *OracleRestart) Default(ctx context.Context, obj runtime.Object) error { + cr, ok := obj.(*OracleRestart) + if !ok { + return fmt.Errorf("expected *OracleRestart but got %T", obj) + } + + OracleRestartlog.Info("default", "name", cr.Name) + + if cr.Spec.ImagePullPolicy == nil { + policy := corev1.PullAlways + cr.Spec.ImagePullPolicy = &policy + } + + if cr.Spec.SshKeySecret != nil && cr.Spec.SshKeySecret.KeyMountLocation == "" { + cr.Spec.SshKeySecret.KeyMountLocation = utils.OraRacSshSecretMount + } + + if cr.Spec.DbSecret != nil && cr.Spec.DbSecret.Name != "" { + if cr.Spec.DbSecret.PwdFileMountLocation == "" { + cr.Spec.DbSecret.PwdFileMountLocation = utils.OraRacDbPwdFileSecretMount + } + if cr.Spec.DbSecret.KeyFileMountLocation == "" { + cr.Spec.DbSecret.KeyFileMountLocation = utils.OraRacDbKeyFileSecretMount + } + } + + if cr.Spec.TdeWalletSecret != nil && cr.Spec.TdeWalletSecret.Name != "" { + if cr.Spec.TdeWalletSecret.PwdFileMountLocation == "" { + cr.Spec.TdeWalletSecret.PwdFileMountLocation = utils.OraRacTdePwdFileSecretMount + } + if cr.Spec.TdeWalletSecret.KeyFileMountLocation == "" { + cr.Spec.TdeWalletSecret.KeyFileMountLocation = utils.OraRacTdeKeyFileSecretMount + } + } + + if cr.Spec.ConfigParams != nil { + if cr.Spec.ConfigParams.SwMountLocation == "" { + cr.Spec.ConfigParams.SwMountLocation = utils.OraSwLocation + } + + if cr.Spec.ConfigParams.GridResponseFile.ConfigMapName == "" { + if cr.Spec.ConfigParams.CrsAsmDiskDg == "" { + cr.Spec.ConfigParams.CrsAsmDiskDg = "+DATA" + } + if cr.Spec.ConfigParams.CrsAsmDiskDgRedundancy == "" { + cr.Spec.ConfigParams.CrsAsmDiskDgRedundancy = "external" + } + } + + if cr.Spec.ConfigParams.DbResponseFile.ConfigMapName == "" { + if cr.Spec.ConfigParams.DbDataFileDestDg == "" { + cr.Spec.ConfigParams.DbDataFileDestDg = cr.Spec.ConfigParams.CrsAsmDiskDg + } + if cr.Spec.ConfigParams.DbRecoveryFileDest == "" { + cr.Spec.ConfigParams.DbRecoveryFileDest = cr.Spec.ConfigParams.DbDataFileDestDg + } + if cr.Spec.ConfigParams.DbCharSet == "" { + cr.Spec.ConfigParams.DbCharSet = "AL32UTF8" + } + } + } + + return nil +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-oraclerestart,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=oraclerestarts,versions=v4,name=voraclerestart.kb.io,admissionReviewVersions={v1} + +var _ webhook.CustomValidator = &OracleRestart{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *OracleRestart) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + cr, ok := obj.(*OracleRestart) + if !ok { + return nil, fmt.Errorf("expected *OracleRestart but got %T", obj) + } + + OracleRestartlog.Info("validate create", "name", cr.Name) + var validationErrs field.ErrorList + var warnings admission.Warnings + + namespaces := utils.GetWatchNamespaces() + _, containsNamespace := namespaces[cr.Namespace] + + if len(namespaces) != 0 && !containsNamespace { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("metadata").Child("namespace"), cr.Namespace, + "Oracle database operator doesn't watch over this namespace")) + } + + if cr.Spec.Image == "" { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("image"), cr.Spec.Image, + "image cannot be set to empty")) + } + + validationErrs = append(validationErrs, cr.validateSshSecret()...) + validationErrs = append(validationErrs, cr.validateDbSecret()...) + validationErrs = append(validationErrs, cr.validateTdeSecret()...) + validationErrs = append(validationErrs, cr.validateServiceSpecs()...) + validationErrs = append(validationErrs, cr.validateAsmStorage()...) + validationErrs = append(validationErrs, cr.validateGeneric()...) + + // ASM disk warnings + var deviceWarnings []string + w, errs := cr.validateCrsAsmDeviceListSize() + deviceWarnings = append(deviceWarnings, w...) + validationErrs = append(validationErrs, errs...) + + w, errs = cr.validateDbAsmDeviceList() + deviceWarnings = append(deviceWarnings, w...) + validationErrs = append(validationErrs, errs...) + + w, errs = cr.validateRecoAsmDeviceList() + deviceWarnings = append(deviceWarnings, w...) + validationErrs = append(validationErrs, errs...) + + w, errs = cr.validateRedoAsmDeviceList() + deviceWarnings = append(deviceWarnings, w...) + validationErrs = append(validationErrs, errs...) + + for _, warning := range deviceWarnings { + warnings = append(warnings, warning) + } + + if len(validationErrs) > 0 { + return warnings, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "OracleRestart"}, + cr.Name, validationErrs) + } + + return warnings, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *OracleRestart) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + old, okOld := oldObj.(*OracleRestart) + newCr, okNew := newObj.(*OracleRestart) + if !okOld || !okNew { + return nil, fmt.Errorf("expected *OracleRestart for both old and new objects") + } + + OracleRestartlog.Info("validate update", "name", newCr.Name) + + if newCr.Status.State == "PROVISIONING" || newCr.Status.State == "UPDATING" || newCr.Status.State == "PODAVAILABLE" { + if !reflect.DeepEqual(old.Spec, newCr.Spec) { + return nil, apierrors.NewForbidden( + schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, + newCr.Name, fmt.Errorf("updates to RAC Spec are not allowed while RAC is in state %s", newCr.Status.State)) + } + } + + isDiskChanged := !reflect.DeepEqual(old.Spec.AsmStorageDetails.DisksBySize, newCr.Spec.AsmStorageDetails.DisksBySize) + if isDiskChanged { + if old.Spec.ConfigParams.HostSwStageLocation != newCr.Spec.ConfigParams.HostSwStageLocation || + old.Spec.ConfigParams.GridSwZipFile != newCr.Spec.ConfigParams.GridSwZipFile || + old.Spec.ConfigParams.DbSwZipFile != newCr.Spec.ConfigParams.DbSwZipFile || + old.Spec.Image != newCr.Spec.Image { + + return nil, apierrors.NewForbidden( + schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, + newCr.Name, fmt.Errorf("updates to the following fields are not allowed during ASM disk updates: %v", []string{"hostSwStageLocation", "gridSwZipFile", "dbSwZipFile", "image"})) + } + } + + var validationErrs field.ErrorList + + // Re-use create validations on update + warnings, err := r.ValidateCreate(ctx, newObj) + if err != nil { + return warnings, err + } + + // ValidateDelete logic if being deleted + if newCr.GetDeletionTimestamp() != nil { + warnings, err := r.ValidateDelete(ctx, newObj) + if err != nil { + return warnings, err + } + } + + // Skip if only metadata is changing + if reflect.DeepEqual(old.Spec, newCr.Spec) && reflect.DeepEqual(old.Status, newCr.Status) { + return nil, nil + } + + validationErrs = append(validationErrs, newCr.validateUpdateSshSecret(old)...) + validationErrs = append(validationErrs, newCr.validateUpdateDbSecret(old)...) + validationErrs = append(validationErrs, newCr.validateUpdateTdeSecret(old)...) + validationErrs = append(validationErrs, newCr.validateUpdateServiceSpecs(old)...) + validationErrs = append(validationErrs, newCr.validateUpdateAsmStorage(old)...) + validationErrs = append(validationErrs, newCr.validateUpdateGeneric(old)...) + + if len(validationErrs) > 0 { + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "OracleRestart"}, + newCr.Name, validationErrs) + } + + return nil, nil +} + +func (r *OracleRestart) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + cr, ok := obj.(*OracleRestart) + if !ok { + return nil, fmt.Errorf("expected *OracleRestart but got %T", obj) + } + + OracleRestartlog.Info("validate delete", "name", cr.Name) + + // TODO: Add any deletion-specific logic if required + return nil, nil +} + +//========== User Functions to check the fields ========== + +func (r *OracleRestart) validateSshSecret() field.ErrorList { + var validationErrs field.ErrorList + sshPath := field.NewPath("spec").Child("SshKeySecret") + + if r.Spec.SshKeySecret == nil { + validationErrs = append(validationErrs, + field.Required(sshPath, "SshKeySecret must be specified")) + return validationErrs + } + + if r.Spec.SshKeySecret.Name == "" { + validationErrs = append(validationErrs, + field.Required(sshPath.Child("Name"), "SshKeySecret.Name cannot be empty")) + } + if r.Spec.SshKeySecret.PrivKeySecretName == "" { + validationErrs = append(validationErrs, + field.Required(sshPath.Child("PrivKeySecretName"), "PrivKeySecretName cannot be empty")) + } + if r.Spec.SshKeySecret.PubKeySecretName == "" { + validationErrs = append(validationErrs, + field.Required(sshPath.Child("PubKeySecretName"), "PubKeySecretName cannot be empty")) + } + + return validationErrs +} +func (r *OracleRestart) validateDbSecret() field.ErrorList { + var validationErrs field.ErrorList + dbPath := field.NewPath("spec").Child("DbSecret") + + if r.Spec.DbSecret.Name != "" && strings.ToLower(r.Spec.DbSecret.EncryptionType) != "base64" { + if r.Spec.DbSecret.KeyFileName == "" { + validationErrs = append(validationErrs, + field.Required(dbPath.Child("KeyFileName"), "KeyFileName cannot be empty when encryptionType is not 'base64'")) + } + if r.Spec.DbSecret.PwdFileName == "" { + validationErrs = append(validationErrs, + field.Required(dbPath.Child("PwdFileName"), "PwdFileName cannot be empty when encryptionType is not 'base64'")) + } + } + + return validationErrs +} +func (r *OracleRestart) validateTdeSecret() field.ErrorList { + var validationErrs field.ErrorList + tdePath := field.NewPath("spec").Child("TdeWalletSecret") + + if r.Spec.TdeWalletSecret != nil && + r.Spec.TdeWalletSecret.Name != "" && + strings.ToLower(r.Spec.TdeWalletSecret.EncryptionType) != "base64" { + + if r.Spec.TdeWalletSecret.KeyFileName == "" { + validationErrs = append(validationErrs, + field.Required(tdePath.Child("KeyFileName"), "KeyFileName cannot be empty when encryptionType is not 'base64'")) + } + if r.Spec.TdeWalletSecret.PwdFileName == "" { + validationErrs = append(validationErrs, + field.Required(tdePath.Child("PwdFileName"), "PwdFileName cannot be empty when encryptionType is not 'base64'")) + } + } + + return validationErrs +} +func (r *OracleRestart) validateServiceSpecs() field.ErrorList { + var validationErrs field.ErrorList + svcPath := field.NewPath("spec").Child("ServiceDetails") + + svc := r.Spec.ServiceDetails + if svc.Name == "" { + return nil + } + + if svc.Cardinality != "" { + if len(svc.Preferred) > 0 { + validationErrs = append(validationErrs, + field.Invalid(svcPath.Child("Preferred"), svc.Preferred, + "Preferred cannot be used with Cardinality. Use one or the other.")) + } + if len(svc.Available) > 0 { + validationErrs = append(validationErrs, + field.Invalid(svcPath.Child("Available"), svc.Available, + "Available cannot be used with Cardinality. Use one or the other.")) + } + if !utils.CheckStringInList(svc.Cardinality, utils.GetServiceCardinality()) { + validationErrs = append(validationErrs, + field.NotSupported(svcPath.Child("Cardinality"), svc.Cardinality, utils.GetServiceCardinality())) + } + } + + if svc.TafPolicy != "" && !utils.CheckStringInList(svc.TafPolicy, utils.GetTafPolicy()) { + validationErrs = append(validationErrs, + field.NotSupported(svcPath.Child("TafPolicy"), svc.TafPolicy, utils.GetTafPolicy())) + } + + if svc.FailOverType != "" && !utils.CheckStringInList(svc.FailOverType, utils.GetServiceFailoverType()) { + validationErrs = append(validationErrs, + field.NotSupported(svcPath.Child("FailOverType"), svc.FailOverType, utils.GetServiceFailoverType())) + } + + if svc.Role != "" && !utils.CheckStringInList(svc.Role, utils.GetServiceRole()) { + validationErrs = append(validationErrs, + field.NotSupported(svcPath.Child("Role"), svc.Role, utils.GetServiceRole())) + } + + return validationErrs +} +func (r *OracleRestart) validateAsmStorage() field.ErrorList { + var validationErrs field.ErrorList + asmPath := field.NewPath("spec").Child("AsmStorageDetails") + + if r.Spec.AsmStorageDetails == nil { + validationErrs = append(validationErrs, + field.Required(asmPath, "ASM storage details must be provided")) + return validationErrs + } + + if len(r.Spec.AsmStorageDetails.DisksBySize) == 0 { + validationErrs = append(validationErrs, + field.Invalid(asmPath.Child("DisksBySize"), r.Spec.AsmStorageDetails.DisksBySize, + "At least one disk size group must be defined")) + } else { + for i, group := range r.Spec.AsmStorageDetails.DisksBySize { + if len(group.DiskNames) == 0 { + validationErrs = append(validationErrs, + field.Invalid(asmPath.Child("DisksBySize").Index(i).Child("DiskNames"), group.DiskNames, + "Each disk size group must have at least one disk name")) + } + } + } + + return validationErrs +} +func (r *OracleRestart) validateGeneric() field.ErrorList { + var validationErrs field.ErrorList + + if !utils.CheckStatusFlag(r.Spec.InstDetails.IsDelete) { + isAlphanumeric := regexp.MustCompile(`^[a-zA-Z0-9]*$`).MatchString(r.Spec.InstDetails.Name) + if !isAlphanumeric { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("InstDetails").Child("Name"), r.Spec.InstDetails.Name, + "Name must contain only alphanumeric characters")) + } + + if r.Spec.InstDetails.HostSwLocation == "" && r.Spec.StorageClass == "" { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("InstDetails").Child("HostSwLocation"), r.Spec.InstDetails.HostSwLocation, + "Either HostSwLocation or StorageClass must be specified")) + } + } + + if r.Spec.ConfigParams == nil { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("ConfigParams"), r.Spec.ConfigParams, + "ConfigParams cannot be empty")) + return validationErrs + } + + cfg := r.Spec.ConfigParams + cfgPath := field.NewPath("spec").Child("ConfigParams") + + // Grid Response File validation + if cfg.GridResponseFile.ConfigMapName != "" { + if cfg.GridResponseFile.Name == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("GridResponseFile").Child("Name"), cfg.GridResponseFile.Name, + "GridResponseFile name cannot be empty")) + } + + for _, fieldVal := range []struct { + name string + value string + }{ + {"Inventory", cfg.Inventory}, + {"CrsAsmDeviceList", cfg.CrsAsmDeviceList}, + {"GridBase", cfg.GridBase}, + {"CrsAsmDiskDg", cfg.CrsAsmDiskDg}, + {"CrsAsmDiskDgRedundancy", cfg.CrsAsmDiskDgRedundancy}, + } { + if fieldVal.value != "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child(fieldVal.name), fieldVal.value, + fmt.Sprintf("%s cannot be used when GridResponseFile is set", fieldVal.name))) + } + } + } else { + if cfg.GridBase == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("GridBase"), cfg.GridBase, "GridBase cannot be empty")) + } + if cfg.GridHome == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("GridHome"), cfg.GridHome, "GridHome cannot be empty")) + } + if cfg.Inventory == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("Inventory"), cfg.Inventory, "Inventory cannot be empty")) + } + if cfg.CrsAsmDeviceList == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("CrsAsmDeviceList"), cfg.CrsAsmDeviceList, "CrsAsmDeviceList cannot be empty")) + } + } + + // DB Response File validation + if cfg.DbResponseFile.ConfigMapName != "" { + if cfg.DbResponseFile.Name == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("DbResponseFile").Child("Name"), cfg.DbResponseFile.Name, + "DbResponseFile name cannot be empty")) + } + + for _, fieldVal := range []struct { + name string + value string + }{ + {"DbCharSet", cfg.DbCharSet}, + {"DbConfigType", cfg.DbConfigType}, + {"DbRedoFileSize", cfg.DbRedoFileSize}, + {"DbType", cfg.DbType}, + {"DbUniqueName", cfg.DbUniqueName}, + {"DbStorageType", cfg.DbStorageType}, + {"DbName", cfg.DbName}, + } { + if fieldVal.value != "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child(fieldVal.name), fieldVal.value, + fmt.Sprintf("%s cannot be used when DbResponseFile is set", fieldVal.name))) + } + } + } else { + if cfg.DbBase == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("DbBase"), cfg.DbBase, "DbBase cannot be empty")) + } + if cfg.DbHome == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("DbHome"), cfg.DbHome, "DbHome cannot be empty")) + } + if cfg.DbName == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("DbName"), cfg.DbName, "DbName cannot be empty")) + } + } + + if cfg.GridSwZipFile == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("GridSwZipFile"), cfg.GridSwZipFile, + "GridSwZipFile cannot be empty")) + } + if cfg.DbSwZipFile == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("DbSwZipFile"), cfg.DbSwZipFile, + "DbSwZipFile cannot be empty")) + } + + if !utils.CheckStatusFlag(r.Spec.UseNfsforSwStorage) { + if cfg.HostSwStageLocation == "" && r.Spec.StorageClass == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("HostSwStageLocation"), cfg.HostSwStageLocation, + "Either HostSwStageLocation or StorageClass must be specified")) + } + } + + if r.Spec.Image == "" { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("Image"), r.Spec.Image, + "Image cannot be empty")) + } + + return validationErrs +} + +//========================== Validate inital specs check ends here ================ + +// =========================== Update specs checks block begin Here ======================= + +func (r *OracleRestart) validateUpdateGeneric(old *OracleRestart) field.ErrorList { + var validationErrs field.ErrorList + + check := func(path *field.Path, oldVal, newVal string) { + if oldVal != "" && newVal != "" && !strings.EqualFold(oldVal, newVal) { + validationErrs = append(validationErrs, field.Forbidden(path, path.String()+" cannot be changed post creation")) + } + } + + if r.Spec.ConfigParams != nil && old.Spec.ConfigParams != nil { + cpPath := field.NewPath("spec", "ConfigParams") + check(cpPath.Child("DbName"), old.Spec.ConfigParams.DbName, r.Spec.ConfigParams.DbName) + check(cpPath.Child("GridBase"), old.Spec.ConfigParams.GridBase, r.Spec.ConfigParams.GridBase) + check(cpPath.Child("GridHome"), old.Spec.ConfigParams.GridHome, r.Spec.ConfigParams.GridHome) + check(cpPath.Child("DbBase"), old.Spec.ConfigParams.DbBase, r.Spec.ConfigParams.DbBase) + check(cpPath.Child("DbHome"), old.Spec.ConfigParams.DbHome, r.Spec.ConfigParams.DbHome) + check(cpPath.Child("CrsAsmDiskDg"), old.Spec.ConfigParams.CrsAsmDiskDg, r.Spec.ConfigParams.CrsAsmDiskDg) + check(cpPath.Child("CrsAsmDiskDgRedundancy"), old.Spec.ConfigParams.CrsAsmDiskDgRedundancy, r.Spec.ConfigParams.CrsAsmDiskDgRedundancy) + check(cpPath.Child("DBAsmDiskDgRedundancy"), old.Spec.ConfigParams.DBAsmDiskDgRedundancy, r.Spec.ConfigParams.DBAsmDiskDgRedundancy) + check(cpPath.Child("DbCharSet"), old.Spec.ConfigParams.DbCharSet, r.Spec.ConfigParams.DbCharSet) + check(cpPath.Child("DbConfigType"), old.Spec.ConfigParams.DbConfigType, r.Spec.ConfigParams.DbConfigType) + check(cpPath.Child("DbDataFileDestDg"), old.Spec.ConfigParams.DbDataFileDestDg, r.Spec.ConfigParams.DbDataFileDestDg) + check(cpPath.Child("DbUniqueName"), old.Spec.ConfigParams.DbUniqueName, r.Spec.ConfigParams.DbUniqueName) + check(cpPath.Child("DbRecoveryFileDest"), old.Spec.ConfigParams.DbRecoveryFileDest, r.Spec.ConfigParams.DbRecoveryFileDest) + check(cpPath.Child("DbRedoFileSize"), old.Spec.ConfigParams.DbRedoFileSize, r.Spec.ConfigParams.DbRedoFileSize) + check(cpPath.Child("DbStorageType"), old.Spec.ConfigParams.DbStorageType, r.Spec.ConfigParams.DbStorageType) + check(cpPath.Child("DbSwZipFile"), old.Spec.ConfigParams.DbSwZipFile, r.Spec.ConfigParams.DbSwZipFile) + check(cpPath.Child("GridSwZipFile"), old.Spec.ConfigParams.GridSwZipFile, r.Spec.ConfigParams.GridSwZipFile) + + // Nested response files + check(cpPath.Child("GridResponseFile", "ConfigMapName"), old.Spec.ConfigParams.GridResponseFile.ConfigMapName, r.Spec.ConfigParams.GridResponseFile.ConfigMapName) + check(cpPath.Child("GridResponseFile", "Name"), old.Spec.ConfigParams.GridResponseFile.Name, r.Spec.ConfigParams.GridResponseFile.Name) + check(cpPath.Child("DbResponseFile", "ConfigMapName"), old.Spec.ConfigParams.DbResponseFile.ConfigMapName, r.Spec.ConfigParams.DbResponseFile.ConfigMapName) + check(cpPath.Child("DbResponseFile", "Name"), old.Spec.ConfigParams.DbResponseFile.Name, r.Spec.ConfigParams.DbResponseFile.Name) + } + + return validationErrs +} + +func (r *OracleRestart) validateUpdateServiceSpecs(old *OracleRestart) field.ErrorList { + var validationErrs field.ErrorList + + check := func(path *field.Path, oldVal, newVal string) { + if oldVal != "" && newVal != "" && !strings.EqualFold(oldVal, newVal) { + validationErrs = append(validationErrs, field.Forbidden(path, path.String()+" cannot be changed post creation")) + } + } + + sdPath := field.NewPath("spec", "ServiceDetail") + + check(sdPath.Child("Name"), old.Status.ServiceDetails.Name, r.Spec.ServiceDetails.Name) + check(sdPath.Child("Cardinality"), old.Status.ServiceDetails.Cardinality, r.Spec.ServiceDetails.Cardinality) + check(sdPath.Child("Notification"), old.Status.ServiceDetails.Notification, r.Spec.ServiceDetails.Notification) + check(sdPath.Child("ClbGoal"), old.Status.ServiceDetails.ClbGoal, r.Spec.ServiceDetails.ClbGoal) + check(sdPath.Child("CommitOutCome"), old.Status.ServiceDetails.CommitOutCome, r.Spec.ServiceDetails.CommitOutCome) + check(sdPath.Child("CommitOutComeFastPath"), old.Status.ServiceDetails.CommitOutComeFastPath, r.Spec.ServiceDetails.CommitOutComeFastPath) + check(sdPath.Child("Dtp"), old.Status.ServiceDetails.Dtp, r.Spec.ServiceDetails.Dtp) + check(sdPath.Child("SessionState"), old.Status.ServiceDetails.SessionState, r.Spec.ServiceDetails.SessionState) + check(sdPath.Child("Edition"), old.Status.ServiceDetails.Edition, r.Spec.ServiceDetails.Edition) + check(sdPath.Child("FailBack"), old.Status.ServiceDetails.FailBack, r.Spec.ServiceDetails.FailBack) + check(sdPath.Child("FailOverRestore"), old.Status.ServiceDetails.FailOverRestore, r.Spec.ServiceDetails.FailOverRestore) // ✅ Fixed error message + check(sdPath.Child("FailOverType"), old.Status.ServiceDetails.FailOverType, r.Spec.ServiceDetails.FailOverType) + check(sdPath.Child("TafPolicy"), old.Status.ServiceDetails.TafPolicy, r.Spec.ServiceDetails.TafPolicy) + check(sdPath.Child("RlbGoal"), old.Status.ServiceDetails.RlbGoal, r.Spec.ServiceDetails.RlbGoal) + check(sdPath.Child("Role"), old.Status.ServiceDetails.Role, r.Spec.ServiceDetails.Role) + check(sdPath.Child("Pdb"), old.Status.ServiceDetails.Pdb, r.Spec.ServiceDetails.Pdb) + + return validationErrs +} +func (r *OracleRestart) validateUpdateAsmStorage(old *OracleRestart) field.ErrorList { + var validationErrs field.ErrorList + // Add actual validation logic here if needed + return validationErrs +} + +func (r *OracleRestart) validateUpdateDbSecret(old *OracleRestart) field.ErrorList { + var validationErrs field.ErrorList + + if r.Spec.DbSecret != nil && old.Status.DbSecret != nil { + if r.Spec.DbSecret.Name != "" && old.Status.DbSecret.Name != "" && + !strings.EqualFold(old.Status.DbSecret.Name, r.Spec.DbSecret.Name) { + validationErrs = append(validationErrs, + field.Forbidden(field.NewPath("spec").Child("DbSecret").Child("Name"), + "DbSecret name cannot be changed post creation")) + } + + if r.Spec.DbSecret.KeyFileName != "" && old.Status.DbSecret.KeyFileName != "" && + !strings.EqualFold(old.Status.DbSecret.KeyFileName, r.Spec.DbSecret.KeyFileName) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileName"), + r.Spec.DbSecret.KeyFileName, "KeyFileName cannot be changed post creation")) + } + + if r.Spec.DbSecret.PwdFileName != "" && old.Status.DbSecret.PwdFileName != "" && + !strings.EqualFold(old.Status.DbSecret.PwdFileName, r.Spec.DbSecret.PwdFileName) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileName"), + r.Spec.DbSecret.PwdFileName, "PwdFileName cannot be changed post creation")) + } + } + + return validationErrs +} + +func (r *OracleRestart) validateUpdateTdeSecret(old *OracleRestart) field.ErrorList { + var validationErrs field.ErrorList + + if r.Spec.TdeWalletSecret != nil && old.Status.TdeWalletSecret != nil { + if r.Spec.TdeWalletSecret.Name != "" && old.Status.TdeWalletSecret.Name != "" && + !strings.EqualFold(old.Status.TdeWalletSecret.Name, r.Spec.TdeWalletSecret.Name) { + validationErrs = append(validationErrs, + field.Forbidden(field.NewPath("spec").Child("TdeWalletSecret").Child("Name"), + "TdeWalletSecret name cannot be changed post creation")) + } + + if r.Spec.TdeWalletSecret.KeyFileName != "" && old.Status.TdeWalletSecret.KeyFileName != "" && + !strings.EqualFold(old.Status.TdeWalletSecret.KeyFileName, r.Spec.TdeWalletSecret.KeyFileName) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("TdeWalletSecret").Child("KeyFileName"), + r.Spec.TdeWalletSecret.KeyFileName, "KeyFileName cannot be changed post creation")) + } + + if r.Spec.TdeWalletSecret.PwdFileName != "" && old.Status.TdeWalletSecret.PwdFileName != "" && + !strings.EqualFold(old.Status.TdeWalletSecret.PwdFileName, r.Spec.TdeWalletSecret.PwdFileName) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("TdeWalletSecret").Child("PwdFileName"), + r.Spec.TdeWalletSecret.PwdFileName, "PwdFileName cannot be changed post creation")) + } + } + + return validationErrs +} + +func (r *OracleRestart) validateUpdateSshSecret(old *OracleRestart) field.ErrorList { + var validationErrs field.ErrorList + + if r.Spec.SshKeySecret != nil && old.Status.SshKeySecret != nil { + if r.Spec.SshKeySecret.Name != "" && old.Status.SshKeySecret.Name != "" && + !strings.EqualFold(old.Status.SshKeySecret.Name, r.Spec.SshKeySecret.Name) { + validationErrs = append(validationErrs, + field.Forbidden(field.NewPath("spec").Child("SshKeySecret").Child("Name"), + "SshKeySecret name cannot be changed post creation")) + } + + if r.Spec.SshKeySecret.PrivKeySecretName != "" && old.Status.SshKeySecret.PrivKeySecretName != "" && + !strings.EqualFold(old.Status.SshKeySecret.PrivKeySecretName, r.Spec.SshKeySecret.PrivKeySecretName) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("SshKeySecret").Child("PrivKeySecretName"), + r.Spec.SshKeySecret.PrivKeySecretName, "PrivKeySecretName cannot be changed post creation")) + } + + if r.Spec.SshKeySecret.PubKeySecretName != "" && old.Status.SshKeySecret.PubKeySecretName != "" && + !strings.EqualFold(old.Status.SshKeySecret.PubKeySecretName, r.Spec.SshKeySecret.PubKeySecretName) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("SshKeySecret").Child("PubKeySecretName"), + r.Spec.SshKeySecret.PubKeySecretName, "PubKeySecretName cannot be changed post creation")) + } + } + + return validationErrs +} + +func (r *OracleRestart) validateAsmDeviceList(deviceListStr, deviceListName string) ([]string, field.ErrorList) { + var warnings []string + var validationErrs field.ErrorList + + // Skip validation if the device list is empty or not provided + if deviceListStr == "" { + return warnings, validationErrs + } + + if r.Spec.AsmStorageDetails == nil { + validationErrs = append(validationErrs, + field.Required(field.NewPath("spec").Child("AsmStorageDetails"), + "ASM storage details must be provided when device list is specified")) + return warnings, validationErrs + } + + deviceList := strings.Split(deviceListStr, ",") + var sizeGroup string // Placeholder for the expected storage size as string + + for _, device := range deviceList { + found := false + for _, diskBySize := range r.Spec.AsmStorageDetails.DisksBySize { + // Check if the device exists in the current size group + if contains(diskBySize.DiskNames, device) { + // Check for storage size mismatch + if sizeGroup == "" { + // Set the expected size group on first match + sizeGroup = fmt.Sprintf("%d", diskBySize.StorageSizeInGb) + } else if sizeGroup != fmt.Sprintf("%d", diskBySize.StorageSizeInGb) { + // Add warning for size mismatch + warnings = append(warnings, + fmt.Sprintf("Disk %s in %s is not of the same storage size as others (%s GB expected, but found %s GB)", + device, deviceListName, sizeGroup, fmt.Sprintf("%d", diskBySize.StorageSizeInGb))) + } + found = true + break + } + } + // Error if a device in the list is not found in any DisksBySize group + if !found { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("AsmStorageDetails").Child(deviceListName), device, + fmt.Sprintf("Disk %s not found in any storage size group", device))) + } + } + + return warnings, validationErrs +} + +func (r *OracleRestart) validateCrsAsmDeviceListSize() ([]string, field.ErrorList) { + var warnings []string + var validationErrs field.ErrorList + + if r.Spec.ConfigParams == nil || r.Spec.ConfigParams.CrsAsmDeviceList == "" { + return warnings, validationErrs + } + + return r.validateAsmDeviceList(r.Spec.ConfigParams.CrsAsmDeviceList, "CrsAsmDeviceList") +} + +func (r *OracleRestart) validateDbAsmDeviceList() ([]string, field.ErrorList) { + var warnings []string + var validationErrs field.ErrorList + + if r.Spec.ConfigParams == nil || r.Spec.ConfigParams.DbAsmDeviceList == "" { + return warnings, validationErrs + } + + return r.validateAsmDeviceList(r.Spec.ConfigParams.DbAsmDeviceList, "DbAsmDeviceList") +} + +func (r *OracleRestart) validateRecoAsmDeviceList() ([]string, field.ErrorList) { + var warnings []string + var validationErrs field.ErrorList + + if r.Spec.ConfigParams == nil || r.Spec.ConfigParams.RecoAsmDeviceList == "" { + return warnings, validationErrs + } + + return r.validateAsmDeviceList(r.Spec.ConfigParams.RecoAsmDeviceList, "RecoAsmDeviceList") +} + +func (r *OracleRestart) validateRedoAsmDeviceList() ([]string, field.ErrorList) { + var warnings []string + var validationErrs field.ErrorList + + if r.Spec.ConfigParams == nil || r.Spec.ConfigParams.RedoAsmDeviceList == "" { + return warnings, validationErrs + } + + return r.validateAsmDeviceList(r.Spec.ConfigParams.RedoAsmDeviceList, "RedoAsmDeviceList") +} + +// Helper function to check if a slice contains a specific element +func contains(slice []string, item string) bool { + for _, elem := range slice { + if elem == item { + return true + } + } + return false +} + +// =========================== Update specs checks block ends Here ======================= diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 4eb9425d..b99f2c83 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -98,6 +98,70 @@ func (in *AdminpdbUser) DeepCopy() *AdminpdbUser { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AsmDiskDetails) DeepCopyInto(out *AsmDiskDetails) { + *out = *in + if in.DisksBySize != nil { + in, out := &in.DisksBySize, &out.DisksBySize + *out = make([]DiskBySize, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AsmDiskDetails. +func (in *AsmDiskDetails) DeepCopy() *AsmDiskDetails { + if in == nil { + return nil + } + out := new(AsmDiskDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AsmDiskgroupStatus) DeepCopyInto(out *AsmDiskgroupStatus) { + *out = *in + if in.Disks != nil { + in, out := &in.Disks, &out.Disks + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AsmDiskgroupStatus. +func (in *AsmDiskgroupStatus) DeepCopy() *AsmDiskgroupStatus { + if in == nil { + return nil + } + out := new(AsmDiskgroupStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AsmInstanceStatus) DeepCopyInto(out *AsmInstanceStatus) { + *out = *in + if in.Diskgroup != nil { + in, out := &in.Diskgroup, &out.Diskgroup + *out = make([]AsmDiskgroupStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AsmInstanceStatus. +func (in *AsmInstanceStatus) DeepCopy() *AsmInstanceStatus { + if in == nil { + return nil + } + out := new(AsmInstanceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AutonomousContainerDatabase) DeepCopyInto(out *AutonomousContainerDatabase) { *out = *in @@ -1486,6 +1550,26 @@ func (in *DbcsSystemStatus) DeepCopy() *DbcsSystemStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiskBySize) DeepCopyInto(out *DiskBySize) { + *out = *in + if in.DiskNames != nil { + in, out := &in.DiskNames, &out.DiskNames + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiskBySize. +func (in *DiskBySize) DeepCopy() *DiskBySize { + if in == nil { + return nil + } + out := new(DiskBySize) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvironmentVariable) DeepCopyInto(out *EnvironmentVariable) { *out = *in @@ -1811,6 +1895,23 @@ func (in *GsmStatusDetails) DeepCopy() *GsmStatusDetails { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InitParams) DeepCopyInto(out *InitParams) { + *out = *in + out.GridResponseFile = in.GridResponseFile + out.DbResponseFile = in.DbResponseFile +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InitParams. +func (in *InitParams) DeepCopy() *InitParams { + if in == nil { + return nil + } + out := new(InitParams) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *K8sADBBackupSpec) DeepCopyInto(out *K8sADBBackupSpec) { *out = *in @@ -2444,6 +2545,26 @@ func (in *LTDESecret) DeepCopy() *LTDESecret { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkDetailSpec) DeepCopyInto(out *NetworkDetailSpec) { + *out = *in + if in.IPs != nil { + in, out := &in.IPs, &out.IPs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkDetailSpec. +func (in *NetworkDetailSpec) DeepCopy() *NetworkDetailSpec { + if in == nil { + return nil + } + out := new(NetworkDetailSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { *out = *in @@ -2728,6 +2849,407 @@ func (in *OracleRestDataServiceStatus) DeepCopy() *OracleRestDataServiceStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestart) DeepCopyInto(out *OracleRestart) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestart. +func (in *OracleRestart) DeepCopy() *OracleRestart { + if in == nil { + return nil + } + out := new(OracleRestart) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OracleRestart) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartDbPwdSecretDetails) DeepCopyInto(out *OracleRestartDbPwdSecretDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartDbPwdSecretDetails. +func (in *OracleRestartDbPwdSecretDetails) DeepCopy() *OracleRestartDbPwdSecretDetails { + if in == nil { + return nil + } + out := new(OracleRestartDbPwdSecretDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartInstDetailSpec) DeepCopyInto(out *OracleRestartInstDetailSpec) { + *out = *in + if in.WorkerNode != nil { + in, out := &in.WorkerNode, &out.WorkerNode + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.EnvVars != nil { + in, out := &in.EnvVars, &out.EnvVars + *out = make([]corev1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.PvcName != nil { + in, out := &in.PvcName, &out.PvcName + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NodePortSvc != nil { + in, out := &in.NodePortSvc, &out.NodePortSvc + *out = make([]OracleRestartNodePortSvc, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.PortMappings != nil { + in, out := &in.PortMappings, &out.PortMappings + *out = make([]OracleRestartPortMapping, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartInstDetailSpec. +func (in *OracleRestartInstDetailSpec) DeepCopy() *OracleRestartInstDetailSpec { + if in == nil { + return nil + } + out := new(OracleRestartInstDetailSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartList) DeepCopyInto(out *OracleRestartList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OracleRestart, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartList. +func (in *OracleRestartList) DeepCopy() *OracleRestartList { + if in == nil { + return nil + } + out := new(OracleRestartList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OracleRestartList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartNodeDetailedStatus) DeepCopyInto(out *OracleRestartNodeDetailedStatus) { + *out = *in + if in.PvcName != nil { + in, out := &in.PvcName, &out.PvcName + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NodePortSvc != nil { + in, out := &in.NodePortSvc, &out.NodePortSvc + *out = make([]OracleRestartNodePortSvc, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.PortMappings != nil { + in, out := &in.PortMappings, &out.PortMappings + *out = make([]OracleRestartPortMapping, len(*in)) + copy(*out, *in) + } + if in.MountedDevices != nil { + in, out := &in.MountedDevices, &out.MountedDevices + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartNodeDetailedStatus. +func (in *OracleRestartNodeDetailedStatus) DeepCopy() *OracleRestartNodeDetailedStatus { + if in == nil { + return nil + } + out := new(OracleRestartNodeDetailedStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartNodePortSvc) DeepCopyInto(out *OracleRestartNodePortSvc) { + *out = *in + if in.PortMappings != nil { + in, out := &in.PortMappings, &out.PortMappings + *out = make([]OracleRestartPortMapping, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartNodePortSvc. +func (in *OracleRestartNodePortSvc) DeepCopy() *OracleRestartNodePortSvc { + if in == nil { + return nil + } + out := new(OracleRestartNodePortSvc) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartNodestatus) DeepCopyInto(out *OracleRestartNodestatus) { + *out = *in + if in.NodeDetails != nil { + in, out := &in.NodeDetails, &out.NodeDetails + *out = new(OracleRestartNodeDetailedStatus) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartNodestatus. +func (in *OracleRestartNodestatus) DeepCopy() *OracleRestartNodestatus { + if in == nil { + return nil + } + out := new(OracleRestartNodestatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartPortMapping) DeepCopyInto(out *OracleRestartPortMapping) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartPortMapping. +func (in *OracleRestartPortMapping) DeepCopy() *OracleRestartPortMapping { + if in == nil { + return nil + } + out := new(OracleRestartPortMapping) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartSpec) DeepCopyInto(out *OracleRestartSpec) { + *out = *in + in.InstDetails.DeepCopyInto(&out.InstDetails) + if in.ConfigParams != nil { + in, out := &in.ConfigParams, &out.ConfigParams + *out = new(InitParams) + **out = **in + } + if in.AsmStorageDetails != nil { + in, out := &in.AsmStorageDetails, &out.AsmStorageDetails + *out = new(AsmDiskDetails) + (*in).DeepCopyInto(*out) + } + if in.NfsStorageDetails != nil { + in, out := &in.NfsStorageDetails, &out.NfsStorageDetails + *out = new(corev1.NFSVolumeSource) + **out = **in + } + if in.SshKeySecret != nil { + in, out := &in.SshKeySecret, &out.SshKeySecret + *out = new(OracleRestartSshSecretDetails) + **out = **in + } + if in.ImagePullPolicy != nil { + in, out := &in.ImagePullPolicy, &out.ImagePullPolicy + *out = new(corev1.PullPolicy) + **out = **in + } + if in.ReadinessProbe != nil { + in, out := &in.ReadinessProbe, &out.ReadinessProbe + *out = new(corev1.Probe) + (*in).DeepCopyInto(*out) + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.ExternalSvcType != nil { + in, out := &in.ExternalSvcType, &out.ExternalSvcType + *out = new(string) + **out = **in + } + if in.DbSecret != nil { + in, out := &in.DbSecret, &out.DbSecret + *out = new(OracleRestartDbPwdSecretDetails) + **out = **in + } + if in.TdeWalletSecret != nil { + in, out := &in.TdeWalletSecret, &out.TdeWalletSecret + *out = new(OracleRestartDbPwdSecretDetails) + **out = **in + } + in.ServiceDetails.DeepCopyInto(&out.ServiceDetails) + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartSpec. +func (in *OracleRestartSpec) DeepCopy() *OracleRestartSpec { + if in == nil { + return nil + } + out := new(OracleRestartSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartSshSecretDetails) DeepCopyInto(out *OracleRestartSshSecretDetails) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartSshSecretDetails. +func (in *OracleRestartSshSecretDetails) DeepCopy() *OracleRestartSshSecretDetails { + if in == nil { + return nil + } + out := new(OracleRestartSshSecretDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OracleRestartStatus) DeepCopyInto(out *OracleRestartStatus) { + *out = *in + if in.OracleRestartNodes != nil { + in, out := &in.OracleRestartNodes, &out.OracleRestartNodes + *out = make([]*OracleRestartNodestatus, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(OracleRestartNodestatus) + (*in).DeepCopyInto(*out) + } + } + } + if in.ClientEtcHost != nil { + in, out := &in.ClientEtcHost, &out.ClientEtcHost + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.InstDetails.DeepCopyInto(&out.InstDetails) + if in.ConfigParams != nil { + in, out := &in.ConfigParams, &out.ConfigParams + *out = new(InitParams) + **out = **in + } + if in.AsmDetails != nil { + in, out := &in.AsmDetails, &out.AsmDetails + *out = new(AsmInstanceStatus) + (*in).DeepCopyInto(*out) + } + if in.NfsStorageDetails != nil { + in, out := &in.NfsStorageDetails, &out.NfsStorageDetails + *out = new(corev1.NFSVolumeSource) + **out = **in + } + if in.SshKeySecret != nil { + in, out := &in.SshKeySecret, &out.SshKeySecret + *out = new(OracleRestartSshSecretDetails) + **out = **in + } + if in.ImagePullPolicy != nil { + in, out := &in.ImagePullPolicy, &out.ImagePullPolicy + *out = new(corev1.PullPolicy) + **out = **in + } + if in.ReadinessProbe != nil { + in, out := &in.ReadinessProbe, &out.ReadinessProbe + *out = new(corev1.Probe) + (*in).DeepCopyInto(*out) + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.ExternalSvcType != nil { + in, out := &in.ExternalSvcType, &out.ExternalSvcType + *out = new(string) + **out = **in + } + if in.DbSecret != nil { + in, out := &in.DbSecret, &out.DbSecret + *out = new(OracleRestartDbPwdSecretDetails) + **out = **in + } + if in.TdeWalletSecret != nil { + in, out := &in.TdeWalletSecret, &out.TdeWalletSecret + *out = new(OracleRestartDbPwdSecretDetails) + **out = **in + } + in.ServiceDetails.DeepCopyInto(&out.ServiceDetails) + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartStatus. +func (in *OracleRestartStatus) DeepCopy() *OracleRestartStatus { + if in == nil { + return nil + } + out := new(OracleRestartStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OrdsSrvs) DeepCopyInto(out *OrdsSrvs) { *out = *in @@ -3451,6 +3973,21 @@ func (in *PriVKey) DeepCopy() *PriVKey { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResponseFile) DeepCopyInto(out *ResponseFile) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseFile. +func (in *ResponseFile) DeepCopy() *ResponseFile { + if in == nil { + return nil + } + out := new(ResponseFile) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretDetails) DeepCopyInto(out *SecretDetails) { *out = *in @@ -3466,6 +4003,31 @@ func (in *SecretDetails) DeepCopy() *SecretDetails { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { + *out = *in + if in.Preferred != nil { + in, out := &in.Preferred, &out.Preferred + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Available != nil { + in, out := &in.Available, &out.Available + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceSpec. +func (in *ServiceSpec) DeepCopy() *ServiceSpec { + if in == nil { + return nil + } + out := new(ServiceSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ShardSpec) DeepCopyInto(out *ShardSpec) { *out = *in diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 00000000..ad286a58 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,471 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package main + +import ( + "context" + "flag" + "fmt" + "os" + "strconv" + "strings" + "time" + + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + + "go.uber.org/zap/zapcore" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + + databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" + observabilityv1 "github.com/oracle/oracle-database-operator/apis/observability/v1" + observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" + observabilityv4 "github.com/oracle/oracle-database-operator/apis/observability/v4" + databasecontroller "github.com/oracle/oracle-database-operator/controllers/database" + dataguardcontroller "github.com/oracle/oracle-database-operator/controllers/dataguard" + observabilitycontroller "github.com/oracle/oracle-database-operator/controllers/observability" + // +kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(observabilityv1alpha1.AddToScheme(scheme)) + utilruntime.Must(monitorv1.AddToScheme(scheme)) + utilruntime.Must(databasev1alpha1.AddToScheme(scheme)) + utilruntime.Must(databasev4.AddToScheme(scheme)) + utilruntime.Must(observabilityv1.AddToScheme(scheme)) + utilruntime.Must(observabilityv4.AddToScheme(scheme)) + // +kubebuilder:scaffold:scheme +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.Parse() + + // Initialize new logger Opts + options := &zap.Options{ + Development: true, + TimeEncoder: zapcore.RFC3339TimeEncoder, + } + + ctrl.SetLogger(zap.New(func(o *zap.Options) { *o = *options })) + + watchNamespaces, err := getWatchNamespace() + if err != nil { + setupLog.Error(err, "Failed to get watch namespaces") + os.Exit(1) + } + opt := ctrl.Options{ + Scheme: scheme, + Metrics: metricsserver.Options{ + BindAddress: metricsAddr, + }, + LeaderElection: enableLeaderElection, + LeaderElectionID: "a9d608ea.oracle.com", + NewCache: func(config *rest.Config, opts cache.Options) (cache.Cache, error) { + opts.DefaultNamespaces = watchNamespaces + return cache.New(config, opts) + }, + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), opt) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + // Get Cache + cache := mgr.GetCache() + + // ADB family controllers + if err = (&databasecontroller.AutonomousDatabaseReconciler{ + KubeClient: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("database").WithName("AutonomousDatabase"), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("AutonomousDatabase"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "AutonomousDatabase") + os.Exit(1) + } + if err = (&databasecontroller.AutonomousDatabaseBackupReconciler{ + KubeClient: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseBackup"), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("AutonomousDatabaseBackup"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "AutonomousDatabaseBackup") + os.Exit(1) + } + if err = (&databasecontroller.AutonomousDatabaseRestoreReconciler{ + KubeClient: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseRestore"), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("AutonomousDatabaseRestore"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "AutonomousDatabaseRestore") + os.Exit(1) + } + if err = (&databasecontroller.AutonomousContainerDatabaseReconciler{ + KubeClient: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("AutonomousContainerDatabase"), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("AutonomousContainerDatabase"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "AutonomousContainerDatabase") + os.Exit(1) + } + + if err = (&databasecontroller.SingleInstanceDatabaseReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("database").WithName("SingleInstanceDatabase"), + Scheme: mgr.GetScheme(), + Config: mgr.GetConfig(), + Recorder: mgr.GetEventRecorderFor("SingleInstanceDatabase"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "SingleInstanceDatabase") + os.Exit(1) + } + if err = (&databasecontroller.ShardingDatabaseReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("database").WithName("ShardingDatabase"), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("ShardingDatabase"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ShardingDatabase") + os.Exit(1) + } + if err = (&databasecontroller.DbcsSystemReconciler{ + KubeClient: mgr.GetClient(), + Logger: ctrl.Log.WithName("controllers").WithName("database").WithName("DbcsSystem"), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("DbcsSystem"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "DbcsSystem") + os.Exit(1) + } + if err = (&databasecontroller.OracleRestDataServiceReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("OracleRestDataService"), + Scheme: mgr.GetScheme(), + Config: mgr.GetConfig(), + Recorder: mgr.GetEventRecorderFor("OracleRestDataService"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "OracleRestDataService") + os.Exit(1) + } + + if err = (&databasecontroller.OracleRestartReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "OracleRestart") + os.Exit(1) + } + + // Set RECONCILE_INTERVAL environment variable if you want to change the default value from 15 secs + interval := os.Getenv("RECONCILE_INTERVAL") + i, err := strconv.ParseInt(interval, 10, 64) + if err != nil { + i = 15 + setupLog.Info("Setting default reconcile period for database-controller", "Secs", i) + } + + // Set ENABLE_WEBHOOKS=false when we run locally to skip webhook part when testing just the controller. Not to be used in production. + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err = (&databasev1alpha1.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") + os.Exit(1) + } + if err = (&databasev1alpha1.OracleRestDataService{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") + os.Exit(1) + } + if err = (&databasev4.PDB{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "PDB") + os.Exit(1) + } + if err = (&databasev4.LRPDB{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "LRPDB") + os.Exit(1) + } + if err = (&databasev4.CDB{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "CDB") + os.Exit(1) + } + if err = (&databasev4.LREST{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "LREST") + os.Exit(1) + } + if err = (&databasev1alpha1.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") + os.Exit(1) + } + if err = (&databasev1alpha1.AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseBackup") + os.Exit(1) + } + if err = (&databasev1alpha1.AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseRestore") + os.Exit(1) + } + if err = (&databasev1alpha1.AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") + os.Exit(1) + } + if err = (&databasev4.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") + os.Exit(1) + } + if err = (&databasev4.AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseBackup") + os.Exit(1) + } + if err = (&databasev4.AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseRestore") + os.Exit(1) + } + if err = (&databasev4.AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") + os.Exit(1) + } + if err = (&databasev1alpha1.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") + os.Exit(1) + } + if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + os.Exit(1) + } + if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } + if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + } + if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + os.Exit(1) + } + if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } + if err = (&databasev4.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } + if err = (&observabilityv1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + os.Exit(1) + } + + if err = (&observabilityv4.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + os.Exit(1) + } + if err = (&databasev4.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") + os.Exit(1) + } + if err = (&databasev4.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") + os.Exit(1) + } + if err = (&databasev4.OracleRestDataService{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") + os.Exit(1) + } + // PDB Reconciler + if err = (&databasecontroller.PDBReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.Log.WithName("controllers").WithName("PDB"), + Interval: time.Duration(i), + Recorder: mgr.GetEventRecorderFor("PDB"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "PDB") + os.Exit(1) + } + + // LRPDBR Reconciler + if err = (&databasecontroller.LRPDBReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.Log.WithName("controllers").WithName("LRPDB"), + Interval: time.Duration(i), + Recorder: mgr.GetEventRecorderFor("LRPDB"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "LRPDB") + os.Exit(1) + } + + // CDB Reconciler + if err = (&databasecontroller.CDBReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Config: mgr.GetConfig(), + Log: ctrl.Log.WithName("controllers").WithName("CDB"), + Interval: time.Duration(i), + Recorder: mgr.GetEventRecorderFor("CDB"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "CDB") + os.Exit(1) + } + + // LREST Reconciler + if err = (&databasecontroller.LRESTReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Config: mgr.GetConfig(), + Log: ctrl.Log.WithName("controllers").WithName("LREST"), + Interval: time.Duration(i), + Recorder: mgr.GetEventRecorderFor("LREST"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "LREST") + os.Exit(1) + } + + if err = (&dataguardcontroller.DataguardBrokerReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("dataguard").WithName("DataguardBroker"), + Scheme: mgr.GetScheme(), + Config: mgr.GetConfig(), + Recorder: mgr.GetEventRecorderFor("DataguardBroker"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "DataguardBroker") + os.Exit(1) + } + + if err = (&databasecontroller.OrdsSrvsReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + // Config: mgr.GetConfig(), + Recorder: mgr.GetEventRecorderFor("OrdsSrvs"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "OrdsSrvs") + } + + // Observability DatabaseObserver Reconciler + if err = (&observabilitycontroller.DatabaseObserverReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("observability").WithName("DatabaseObserver"), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("DatabaseObserver"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "DatabaseObserver") + os.Exit(1) + } + // +kubebuilder:scaffold:builder + + // Add index for PDB CR to enable mgr to cache PDBs + indexFunc := func(obj client.Object) []string { + return []string{obj.(*databasev4.PDB).Spec.PDBName} + } + if err = cache.IndexField(context.TODO(), &databasev4.PDB{}, "spec.pdbName", indexFunc); err != nil { + setupLog.Error(err, "unable to create index function for ", "controller", "PDB") + os.Exit(1) + } + + indexFunc2 := func(obj client.Object) []string { + return []string{obj.(*databasev4.LRPDB).Spec.LRPDBName} + } + if err = cache.IndexField(context.TODO(), &databasev4.LRPDB{}, "spec.pdbName", indexFunc2); err != nil { + setupLog.Error(err, "unable to create index function for ", "controller", "LRPDB") + os.Exit(1) + } + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} + +func getWatchNamespace() (map[string]cache.Config, error) { + // WatchNamespaceEnvVar is the constant for env variable WATCH_NAMESPACE + // which specifies the Namespace to watch. + // An empty value means the operator is running with cluster scope. + + var watchNamespaceEnvVar = "WATCH_NAMESPACE" + var nsmap map[string]cache.Config + ns, found := os.LookupEnv(watchNamespaceEnvVar) + values := strings.Split(ns, ",") + if len(values) == 1 && values[0] == "" { + fmt.Printf(":CLUSTER SCOPED:\n") + return nil, nil + } + fmt.Printf(":NAMESPACE SCOPED:\n") + fmt.Printf("WATCH LIST=%s\n", values) + nsmap = make(map[string]cache.Config, len(values)) + if !found { + return nsmap, fmt.Errorf("%s must be set", watchNamespaceEnvVar) + } + + if ns == "" { + return nil, nil + } + + for _, ns := range values { + nsmap[ns] = cache.Config{} + } + + return nsmap, nil + +} diff --git a/commons/oraclerestart/exec.go b/commons/oraclerestart/exec.go new file mode 100644 index 00000000..b91d4226 --- /dev/null +++ b/commons/oraclerestart/exec.go @@ -0,0 +1,112 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +import ( + "bytes" + "net/http" + + oraclerestartdb "github.com/oracle/oracle-database-operator/apis/database/v4" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/remotecommand" +) + +// ExecCMDInContainer execute command in first container of a pod +func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *oraclerestartdb.OracleRestart, logger logr.Logger) (string, string, error) { + + var msg string + var ( + execOut bytes.Buffer + execErr bytes.Buffer + ) + + req := kubeClient.CoreV1().RESTClient(). + Post(). + Namespace(instance.Namespace). + Resource("pods"). + Name(podName). + SubResource("exec"). + VersionedParams(&corev1.PodExecOptions{ + Command: cmd, + Stdout: true, + Stderr: true, + TTY: true, + }, scheme.ParameterCodec) + + config, err := kubeConfig.ClientConfig() + if err != nil { + return "Error Occurred", "Error Occurred", err + } + + if config.NegotiatedSerializer == nil { + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + } + + // Connect to url (constructed from req) using SPDY (HTTP/2) protocol which allows bidirectional streams. + exec, err := remotecommand.NewSPDYExecutor(config, http.MethodPost, req.URL()) + if err != nil { + msg = "Error after executing remotecommand.NewSPDYExecutor" + LogMessages("Error", msg, err, instance, logger) + return "Error Occurred", "Error Occurred", err + } + + err = exec.Stream(remotecommand.StreamOptions{ + Stdout: &execOut, + Stderr: &execErr, + Tty: true, + }) + if err != nil { + msg = "Command execution failed inside the container!" + LogMessages("DEBUG", msg, err, instance, logger) + if len(execOut.String()) > 0 { + LogMessages("INFO", execOut.String(), nil, instance, logger) + } + if len(execErr.String()) > 0 { + LogMessages("INFO", execErr.String(), nil, instance, logger) + } + return execOut.String(), execErr.String(), err + } + + return execOut.String(), execErr.String(), nil +} diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go new file mode 100644 index 00000000..1093683c --- /dev/null +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -0,0 +1,1376 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +import ( + "bufio" + "context" + "errors" + "fmt" + "strconv" + + oraclerestart "github.com/oracle/oracle-database-operator/apis/database/v4" + utils "github.com/oracle/oracle-database-operator/commons/oraclerestart/utils" + + "strings" + + "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// Function to build the env var specification +func buildEnvVarsSpec(envVariables []corev1.EnvVar) []corev1.EnvVar { + var result []corev1.EnvVar + + /** + if len(envVariables) > 0 { + result = append(result, corev1.EnvVar{Name: "container", Value: "true"}) + } else { + result = append(result, corev1.EnvVar{Name: "container", Value: "true"}) + } + **/ + + return result +} + +// FUnction to build the svc definition for RAC +func buildContainerPortsDef(instance *oraclerestart.OracleRestart, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.ContainerPort { + var result []corev1.ContainerPort + if len(OraRestartSpex.PortMappings) > 0 { + for _, portMapping := range OraRestartSpex.PortMappings { + name := generatePortMapping(portMapping) + if len(name) > 15 { + name = name[:15] + } + containerPort := + corev1.ContainerPort{ + Protocol: portMapping.Protocol, + ContainerPort: portMapping.Port, + Name: name, + } + result = append(result, containerPort) + } + } else { + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraDBPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraDBPort))}) + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraLsnrPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraLsnrPort))}) + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraSSHPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraSSHPort))}) + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraLocalOnsPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraLocalOnsPort))}) + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraOemPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraOemPort))}) + } + + return result +} + +func truncateName(name string) string { + if len(name) > 15 { + return name[:15] + } + return name +} + +// FUnction to build the svc definition for RAC +func buildOracleRestartSvcPortsDef(npsvc oraclerestart.OracleRestartNodePortSvc) []corev1.ServicePort { + var result []corev1.ServicePort + + for _, portMapping := range npsvc.PortMappings { + servicePort := + corev1.ServicePort{ + Protocol: portMapping.Protocol, + Port: portMapping.Port, + Name: generatePortMapping(portMapping), + } + if portMapping.TargetPort > 0 { + servicePort.TargetPort = intstr.IntOrString{ + Type: intstr.Int, + IntVal: portMapping.TargetPort, + } + } + if portMapping.NodePort > 0 { + servicePort.NodePort = portMapping.NodePort + } + + result = append(result, servicePort) + } + + return result +} + +// Function to generate the Name +func generateName(base string) string { + maxNameLength := 50 + randomLength := 5 + maxGeneratedLength := maxNameLength - randomLength + if len(base) > maxGeneratedLength { + base = base[:maxGeneratedLength] + } + return fmt.Sprintf("%s%s", base, rand.String(randomLength)) +} + +// Function to generate the port mapping +func generatePortMapping(portMapping oraclerestart.OracleRestartPortMapping) string { + return generateName(fmt.Sprintf("%s-%d-%d-", "tcp", + portMapping.Port, portMapping.TargetPort)) +} + +func LogMessages(msgtype string, msg string, err error, instance *oraclerestart.OracleRestart, logger logr.Logger) { + // setting logrus formatter + //logrus.SetFormatter(&logrus.JSONFormatter{}) + //logrus.SetOutput(os.Stdout) + + if msgtype == "DEBUG" && utils.CheckStatusFlag(instance.Spec.IsDebug) { + if err != nil { + logger.Error(err, msg) + } else { + logger.Info(msg) + } + } else if msgtype == "INFO" { + logger.Info(msg) + } +} + +func GetRacPodName(racName string) string { + podName := racName + return podName +} + +func getlabelsForRac(instance *oraclerestart.OracleRestart) map[string]string { + return buildLabelsForOracleRestart(instance, "OracleRestart") +} + +func getAsmPvcName(index int, name string) string { + + pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name + + return pvcName + +} +func GetAsmPvcName(index int, name string) string { + + pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name + + return pvcName + +} + +func getAsmPvName(index int, name string) string { + + pvName := "asm-pv-disk-" + strconv.Itoa(index) + "-" + name + return pvName +} + +func GetAsmPvName(index int, name string) string { + + pvName := "asm-pv-disk-" + strconv.Itoa(index) + "-" + name + return pvName +} + +func CheckSfset(sfsetName string, instance *oraclerestart.OracleRestart, kClient client.Client) (*appsv1.StatefulSet, error) { + sfSetFound := &appsv1.StatefulSet{} + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: sfsetName, + Namespace: instance.Namespace, + }, sfSetFound) + if err != nil { + return sfSetFound, err + } + return sfSetFound, nil +} + +func GetRacK8sClientConfig(kClient client.Client) (clientcmd.ClientConfig, kubernetes.Interface, error) { + var err1 error + var kubeConfig clientcmd.ClientConfig + var kubeClient kubernetes.Interface + + oraclerestart.KubeConfigOnce.Do(func() { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + kubeConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + config, err := kubeConfig.ClientConfig() + if err != nil { + err1 = err + } + kubeClient, err = kubernetes.NewForConfig(config) + if err != nil { + err1 = err + } + + }) + return kubeConfig, kubeClient, err1 +} + +func oraclerestartValidationCmd() []string { + + oraScriptMount1 := getOraScriptMount() + var oraShardValidateCmd = []string{oraScriptMount1 + "/cmdExec", "/bin/python3", oraScriptMount1 + "/main.py ", "--checkliveness=true ", "--optype=primaryoraclerestart"} + return oraShardValidateCmd +} + +func OracleRestartNodeDelCmd() []string { + oraScriptMount1 := getOraScriptMount() + var oraOracleRestartNodeDelCmd = []string{oraScriptMount1 + "/cmdExec", "/bin/python3", oraScriptMount1 + "/main.py ", "--delOracleRestartNode=\"del_rachome=true;del_gridnode=true\""} + return oraOracleRestartNodeDelCmd +} + +func oraclerestartLsnrSetup() []string { + oraScriptMount1 := getOraScriptMount() + var oraOracleRestartNodeDelCmd = []string{oraScriptMount1 + "/cmdExec", "/bin/python3", oraScriptMount1 + "/main.py ", "--setupdblsnr=\"del_rachome=true;del_gridnode=true\""} + return oraOracleRestartNodeDelCmd +} + +func getAsmCmd() []string { + asmCmd := []string{"bash", "-c", "cat /etc/rac_env_vars/envfile | grep CRS_ASM_DEVICE_LIST"} + return asmCmd +} + +func getDbAsmCmd() []string { + asmCmd := []string{"bash", "-c", "cat /etc/rac_env_vars/envfile | grep DB_ASM_DEVICE_LIST"} + return asmCmd +} + +func getOraScriptMount() string { + + return utils.OraScriptMount + +} + +func getOraDbUser() string { + + return utils.OraDBUser + +} + +func getOraGiUser() string { + + return utils.OraGridUser + +} + +func getOraPythonCmd() string { + + return "/bin/python3" + +} + +func UpdateAsmCount(gihome string, podName string, instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +) error { + + _, _, err := ExecCommand(podName, getUpdateAsmCount(gihome), kubeClient, kubeconfig, instance, logger) + if err != nil { + return fmt.Errorf("error ocurred while updating TCP listener ports") + } + + return nil +} + +func ValidateDbSetup(podName string, instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +) error { + + _, _, err := ExecCommand(podName, oraclerestartValidationCmd(), kubeClient, kubeconfig, instance, logger) + if err != nil { + return fmt.Errorf("error ocurred while validating the DB Setup") + } + return nil +} + +func DelOracleRestartNode(podName string, instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, +) error { + + _, _, err := ExecCommand(podName, OracleRestartNodeDelCmd(), kubeClient, kubeconfig, instance, logger) + if err != nil { + return fmt.Errorf("error ocurred while deleting the RAC node") + } + return nil +} + +func CheckAsmList(podName string, instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger) (string, error) { + output, _, err := ExecCommand(podName, getAsmCmd(), kubeClient, kubeconfig, instance, logger) + if err != nil { + return "", err + } + + parts := strings.SplitN(output, "=", 2) + if len(parts) < 2 { + return "", fmt.Errorf("unable to parse ASM device list from output: %s", output) + } + + // Trim the \r and \n characters from the end of the string + deviceList := strings.TrimSpace(parts[1]) + deviceList = strings.ReplaceAll(deviceList, "\r", "") + return deviceList, nil +} + +func CheckDbAsmList(podName string, instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger) (string, error) { + output, _, err := ExecCommand(podName, getDbAsmCmd(), kubeClient, kubeconfig, instance, logger) + if err != nil { + return "", err + } + + parts := strings.SplitN(output, "=", 2) + if len(parts) < 2 { + return "", fmt.Errorf("unable to parse ASM device list from output: %s", output) + } + + // Trim the \r and \n characters from the end of the string + deviceList := strings.TrimSpace(parts[1]) + deviceList = strings.ReplaceAll(deviceList, "\r", "") + return deviceList, nil +} + +func checkPvc(pvcName string, instance *oraclerestart.OracleRestart, kClient client.Client) (*corev1.PersistentVolumeClaim, error) { + pvcFound := &corev1.PersistentVolumeClaim{} + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: pvcName, + Namespace: instance.Namespace, + }, pvcFound) + if err != nil { + return pvcFound, err + } + return pvcFound, nil +} + +func checkPv(pvName string, instance *oraclerestart.OracleRestart, kClient client.Client) (*corev1.PersistentVolume, error) { + pvFound := &corev1.PersistentVolume{} + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: pvName, + Namespace: instance.Namespace, + }, pvFound) + if err != nil { + return pvFound, err + } + return pvFound, nil +} +func DelORestartPVC(instance *oraclerestart.OracleRestart, index int, diskName string, disk *oraclerestart.AsmDiskDetails, kClient client.Client, logger logr.Logger) error { + pvcName := getAsmPvcName(index, instance.Name) + LogMessages("DEBUG", "Attempting to delete PVC: "+GetFmtStr(pvcName), nil, instance, logger) + + pvc, err := checkPvc(pvcName, instance, kClient) + if err != nil { + if apierrors.IsNotFound(err) { + LogMessages("DEBUG", "PVC not found, skipping deletion.", nil, instance, logger) + return nil + } + return err + } + + // Remove finalizers if any + if len(pvc.GetFinalizers()) > 0 { + LogMessages("DEBUG", "Removing PVC finalizers", nil, instance, logger) + pvc.SetFinalizers([]string{}) + if err := kClient.Update(context.Background(), pvc); err != nil { + return fmt.Errorf("failed to remove finalizers from PVC %s: %v", pvcName, err) + } + } + + if err := kClient.Delete(context.Background(), pvc); err != nil { + return fmt.Errorf("failed to delete PVC %s: %v", pvcName, err) + } + return nil +} + +func DelRestartSwPvc(instance *oraclerestart.OracleRestart, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec, kClient client.Client, logger logr.Logger) error { + + pvcName := OraRestartSpex.Name + "-oradata-sw-vol-" + OraRestartSpex.Name + "-0" + LogMessages("DEBUG", "Inside the delPvc and received param: "+GetFmtStr(pvcName), nil, instance, logger) + pvcFound, err := checkPvc(pvcName, instance, kClient) + if err != nil { + LogMessages("DEBUG", "Error occurred in finding the pvc claim!", nil, instance, logger) + return nil + } + err = kClient.Delete(context.Background(), pvcFound) + if err != nil { + LogMessages("DEBUG", "Error occurred in deleting the pvc claim!", nil, instance, logger) + return err + } + return nil +} + +func DelORestartPv(instance *oraclerestart.OracleRestart, index int, diskName string, disk *oraclerestart.AsmDiskDetails, kClient client.Client, logger logr.Logger) error { + + pvName := getAsmPvName(index, instance.Name) + LogMessages("DEBUG", "Inside the delPv and received param: "+GetFmtStr(pvName), nil, instance, logger) + pvFound, err := checkPv(pvName, instance, kClient) + if err != nil { + LogMessages("DEBUG", "Error occurred in finding the pv claim!", nil, instance, logger) + return nil + } + err = kClient.Delete(context.Background(), pvFound) + if err != nil { + LogMessages("DEBUG", "Error occurred in deleting the pv claim!", nil, instance, logger) + return err + } + return nil +} + +func CheckORestartSvc(instance *oraclerestart.OracleRestart, svcType string, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec, svcName string, kClient client.Client) (*corev1.Service, error) { + svcFound := &corev1.Service{} + var name string + if svcType == "nodeport" { + name = svcName + } else { + name = getOracleRestartSvcName(instance, OraRestartSpex, svcType) + } + + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: name, + Namespace: instance.Namespace, + }, svcFound) + if err != nil { + return svcFound, err + } + return svcFound, nil +} + +func PodListValidation(podList *corev1.PodList, sfName string, instance *oraclerestart.OracleRestart, kClient client.Client) (bool, *corev1.Pod, *corev1.Pod) { + var notReadyPod *corev1.Pod + + for _, pod := range podList.Items { + if strings.Contains(pod.Name, sfName) { + // Check pod status + if pod.Status.Phase != corev1.PodRunning { + if notReadyPod == nil { + notReadyPod = &pod + } + continue + } + + // Check container readiness + allContainersReady := true + for _, containerStatus := range pod.Status.ContainerStatuses { + if !containerStatus.Ready { + allContainersReady = false + break + } + } + + if allContainersReady { + // Return the pod if it is ready + return true, &pod, nil + } else { + // Return the first not ready pod found + if notReadyPod == nil { + notReadyPod = &pod + } + } + } + } + + // Return false if no ready pod was found, and the first not ready pod (if any) + return false, nil, notReadyPod +} + +func GetPodList(sfsetName string, instance *oraclerestart.OracleRestart, kClient client.Client, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec, +) (*corev1.PodList, error) { + podList := &corev1.PodList{} + //labelSelector := labels.SelectorFromSet(getlabelsForRAC(instance)) + //labelSelector := map[string]labels.Selector{} + var labelSelector labels.Selector = labels.SelectorFromSet(getSvcLabelsForOracleRestart(-1, OraRestartSpex)) + + listOps := &client.ListOptions{Namespace: instance.Namespace, LabelSelector: labelSelector} + + err := kClient.List(context.TODO(), podList, listOps) + if err != nil { + return nil, err + } + return podList, nil +} + +func checkPod(instance *oraclerestart.OracleRestart, pod *corev1.Pod, kClient client.Client, +) error { + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: pod.Name, + Namespace: instance.Namespace, + }, pod) + + if err != nil { + // Pod Doesn't exist + return err + } + + return nil +} + +func checkPodStatus(pod *corev1.Pod, kClient client.Client, +) error { + var msg string + for _, condition := range pod.Status.Conditions { + if pod.Status.Phase == corev1.PodRunning { + if condition.Type == corev1.PodReady { + // msg = "Pod Status is running" + // LogMessages("DEBUG", msg) + return nil + } + } else { + msg = "Pod is not scheduled or ready " + pod.Name + ".Describe the pod to check the detailed message" + return fmt.Errorf(msg) + } + } + return nil +} + +func checkContainerStatus(pod *corev1.Pod, kClient client.Client, +) error { + + var statuses []corev1.ContainerStatus + var msg string + // msg = "Inside the function checkContainerStatus" + // LogMessages("DEBUG", msg) + statuses = pod.Status.ContainerStatuses + var isRunning bool = false + for _, status := range statuses { + if status.State.Running == nil { + isRunning = false + } else { + isRunning = true + break + } + } + msg = "Container is not in running state" + pod.Name + ".Describe the pod to check the detailed message" + if isRunning { + return nil + } else { + return fmt.Errorf(msg) + } +} + +// NewNamespace creates a corev1.Namespace object using the provided name. +func NewNamespace(name string) *corev1.Namespace { + return &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + APIVersion: corev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } +} + +func getOwnerRef(instance *oraclerestart.OracleRestart, +) []metav1.OwnerReference { + + var ownerRef []metav1.OwnerReference + ownerRef = append(ownerRef, metav1.OwnerReference{Kind: instance.GroupVersionKind().Kind, APIVersion: instance.APIVersion, Name: instance.Name, UID: types.UID(instance.UID)}) + return ownerRef +} + +func getRacInitContainerCmd(resType string, name string, oraScriptMount string, +) string { + var initCmd string + if oraScriptMount != "NOLOC" { + initCmd = resType + ";chown -R 54321:54321 " + oraScriptMount + ";chmod 755 " + oraScriptMount + "/*" + } else { + initCmd = resType + } + + return initCmd +} + +func GetFmtStr(pstr string, +) string { + return "[" + pstr + "]" +} + +func getClusterState(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + stdoutput, _, err := ExecCommand(podName, getGiHealthCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getDbInstState(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + stdoutput, _, err := ExecCommand(podName, getOracleRestartDbModeCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getAsmInstState(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) *oraclerestart.AsmInstanceStatus { + AsmStorageStatus := &oraclerestart.AsmInstanceStatus{} + diskGroup := getAsmDiskgroup(podName, instance, specidx, kubeClient, kubeConfig, logger) + if diskGroup == "Pending" { + return AsmStorageStatus + } + dglist := strings.Split(diskGroup, ",") + for _, dg := range dglist { + asmdg := oraclerestart.AsmDiskgroupStatus{} + disks := getAsmDisks(podName, string(dg), instance, specidx, kubeClient, kubeConfig, logger) + redundancy := getAsmDgRedundancy(podName, string(dg), instance, specidx, kubeClient, kubeConfig, logger) + + asmdg.Name = string(dg) + asmdg.Disks = disks + asmdg.Redundancy = redundancy + AsmStorageStatus.Diskgroup = append(AsmStorageStatus.Diskgroup, asmdg) + } + return AsmStorageStatus +} + +func getAsmDiskgroup(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + + stdoutput, _, err := ExecCommand(podName, getAsmDiskgroupCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getAsmDisks(podName string, dg string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) []string { + + stdoutput, _, err := ExecCommand(podName, getAsmDisksCmd(dg), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return strings.Split(msg, ",") + } + cleanOutput := strings.ReplaceAll(stdoutput, "\r", "") + return strings.Split(strings.TrimSpace(cleanOutput), "\n") +} + +func getAsmDgRedundancy(podName string, dg string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + + stdoutput, _, err := ExecCommand(podName, getAsmDgRedundancyCmd(dg), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getAsmInstName(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + + stdoutput, _, err := ExecCommand(podName, getAsmInstNameCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getAsmInstStatus(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + + stdoutput, _, err := ExecCommand(podName, getAsmInstStatusCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getOracleRestartInstStateFile(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + + stdoutput, _, err := ExecCommand(podName, getOracleRestartInstStateFileCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + //Possible values are "provisioning", "addnode", "failed", "completed" + if strings.ToLower(strings.TrimSpace(stdoutput)) == "provisioning" { + return string(oraclerestart.OracleRestartProvisionState) + } else if strings.ToLower(strings.TrimSpace(stdoutput)) == "completed" { + return string(oraclerestart.OracleRestartAvailableState) + } else if strings.ToLower(strings.TrimSpace(stdoutput)) == "addnode" { + return string(oraclerestart.OracleRestartAddInstState) + } else if strings.ToLower(strings.TrimSpace(stdoutput)) == "failed" { + return string(oraclerestart.OracleRestartFailedState) + } else { + return string(oraclerestart.OracleRestartPendingState) + } +} + +func getDBVersion(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + stdoutput, _, err := ExecCommand(podName, getOracleRestartDbVersionCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + if strings.Contains(stdoutput, "ERROR") { + return "NOTAVAILABLE" + } else { + return strings.TrimSpace(stdoutput) + } +} + +func getDbState(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + stdoutput, _, err := ExecCommand(podName, getOracleRestartHealthCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getDbRole(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + stdoutput, _, err := ExecCommand(podName, getDbRoleCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getConnStr(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + stdoutput, _, err := ExecCommand(podName, getConnStrCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getExternalConnStr(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + // stdoutput, _, err := ExecCommand(podName, getConnStrCmd(), kubeClient, kubeConfig, instance, logger) + // if err != nil { + // msg := "Pending" + // LogMessages("DEBUG", msg, err, instance, logger) + // return msg + // } + // return strings.TrimSpace(stdoutput) + // Fetch the OracleRestartNode-scan-lsnr service + svc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), "OracleRestartNode-scan-lsnr", metav1.GetOptions{}) + if err != nil { + msg := "Failed to get OracleRestartNode-scan-lsnr service" + LogMessages("DEBUG", msg, err, instance, logger) + return "Pending" + } + + // Find the NodePort for port 1521 + var nodePort int32 + for _, port := range svc.Spec.Ports { + if port.Port == 1521 { + nodePort = port.NodePort + break + } + } + + if nodePort == 0 { + msg := "Failed to find NodePort for port 1521 in OracleRestartNode-scan-lsnr service" + LogMessages("DEBUG", msg, err, instance, logger) + return "Pending" + } + + // Construct the external connect string + externalConnectString := fmt.Sprintf("OracleRestartNode-scan.%s.svc.cluster.local:%d/%s", instance.Namespace, nodePort, instance.Spec.ServiceDetails.Name) + return externalConnectString +} + +func getClientEtcHost(podNames []string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, nodeDetails map[string]*corev1.Node) []string { + // Prepare to update ClientEtcHost + var clientEtcHost []string + + // Loop through all pod names + for _, podName := range podNames { + node := nodeDetails[podName] + if node == nil { + logger.Error(nil, "Node details not found for pod", "podName", podName) + continue + } + + // Get nodeIP from node details + nodeIP := getNodeIPFromNodeDetails(node) + + // Construct the line to be added to ClientEtcHost + line := fmt.Sprintf("%s %s.rac.svc.cluster.local %s-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local", nodeIP, podName, podName) + clientEtcHost = append(clientEtcHost, line) + } + + // Update instance status with the new ClientEtcHost + instance.Status.ClientEtcHost = clientEtcHost + + // Assuming you are returning clientEtcHost as well for any further processing + return clientEtcHost +} + +func getNodeIPFromNodeDetails(node *corev1.Node) string { + var internalIP, externalIP string + + // Extract internal and external IPs from node details + for _, addr := range node.Status.Addresses { + if addr.Type == corev1.NodeInternalIP { + internalIP = addr.Address + } else if addr.Type == corev1.NodeExternalIP { + externalIP = addr.Address + } + } + + // Return external IP if it exists, otherwise return internal IP + if externalIP != "" { + return externalIP + } + return internalIP +} + +// readHostsFile reads the /etc/hosts file from the pod running in privileged mode +func readHostsFile(podName string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *oraclerestart.OracleRestart, logger logr.Logger) (string, error) { + + cmd := []string{"cat", "/etc/hosts"} // Assuming this matches the MountPath in buildContainerSpecForRac + + stdOutput, _, err := ExecCommand(podName, cmd, kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Failed to read /etc/hosts from pod" + podName + LogMessages("DEBUG", msg, err, instance, logger) + return "", err + } + return strings.TrimSpace(stdOutput), nil +} + +// Helper function to parse /etc/hosts content +func parseHostsContent(content string) string { + var uncommentedLines string + + lines := strings.Split(content, "\n") + for _, line := range lines { + trimmedLine := strings.TrimSpace(line) + if trimmedLine != "" && !strings.HasPrefix(trimmedLine, "#") { + uncommentedLines = trimmedLine + } + } + + return uncommentedLines +} + +func getSvcState(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + + stdoutput, _, err := ExecCommand(podName, getDBServiceStatus(instance.Status.ConfigParams.DbHome, instance.Status.ConfigParams.DbName, instance.Status.ServiceDetails.Name), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +func getPdbConnStr(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) string { + stdoutput, _, err := ExecCommand(podName, getPdbConnStrCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Pending" + LogMessages("DEBUG", msg, err, instance, logger) + return msg + } + return strings.TrimSpace(stdoutput) +} + +// getGridHome retrieves the GRID_HOME environment variable from the specified pod +func getGridHome(podName string, instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger) (string, error) { + stdoutput, _, err := ExecCommand(podName, getGridHomeCmd(), kubeClient, kubeConfig, instance, logger) + if err != nil { + msg := "Error retrieving GRID_HOME" + LogMessages("DEBUG", msg, err, instance, logger) + return "", err + } + + return strings.TrimSpace(stdoutput), nil +} + +// func getASMListDisks(podName string, instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger) []string { +// gridHome, err := getGridHome(podName, instance, kubeClient, kubeConfig, logger) +// if err != nil { +// msg := "Error retrieving GRID_HOME" +// LogMessages("DEBUG", msg, err, instance, logger) +// return []string{msg} +// } +// stdoutput, _, err := ExecCommand(podName, getASMListDisksCommand(gridHome), kubeClient, kubeConfig, instance, logger) +// if err != nil { +// msg := "Error retrieving ASM disks" +// LogMessages("DEBUG", msg, err, instance, logger) +// return []string{msg} +// } + +// // Split the output by new lines, remove carriage returns and filter out the header +// var disks []string +// lines := strings.Split(strings.TrimSpace(stdoutput), "\n") +// for _, line := range lines { +// // Remove any carriage returns and trim spaces +// line = strings.TrimSpace(strings.Trim(line, "\r")) +// if line == "Path" { +// continue // Skip the header +// } +// if line != "" { +// disks = append(disks, line) +// } +// } + +// return disks +// } + +// func GetCrsNodes(instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, kClient client.Client) (string, string, string, string, string) { + +// var new_crs_nodes []string +// var new_crs_nodes_list []string +// var existing_crs_nodes_healthy []string +// var existing_crs_nodes_not_healthy []string +// var install_node string +// var install_node_flag bool + +// if len(instance.Spec.InstDetails) > 0 { +// for _, OraRestartSpex := range instance.Spec.InstDetails { +// if !utils.CheckStatusFlag(OraRestartSpex.IsDelete) { +// _, err := CheckSfset(OraRestartSpex.Name, instance, kClient) +// if err != nil { +// new_crs_nodes = append(new_crs_nodes, ("pubhost:" + OraRestartSpex.Name + "-0" + "," + "viphost:" + OraRestartSpex.VipSvcName)) +// new_crs_nodes_list = append(new_crs_nodes_list, OraRestartSpex.Name+"-0") +// if !install_node_flag { +// install_node = OraRestartSpex.Name + "-0" +// install_node_flag = true +// } +// } +// } +// } +// // Updating OracleRestartNode status + +// if len(instance.Status.OracleRestartNodes) > 0 { +// for _, oraRacStatusSpex := range instance.Status.OracleRestartNodes { +// _, err := CheckSfset(strings.Split(oraRacStatusSpex.Name, "-")[0], instance, kClient) +// if err != nil { +// newRacStatus := delOracleRestartNodestatus(instance, oraRacStatusSpex.Name) +// instance.Status.OracleRestartNodes = newRacStatus +// } +// } +// // ====Updating the OracleRestartNode status block ends here==== + +// /// The loop check for healthy and non healthy nodes + +// for _, oraRacStatusSpex := range instance.Status.OracleRestartNodes { +// if oraRacStatusSpex.NodeDetails.ClusterState == "HEALTHY" { +// if !checkElem(existing_crs_nodes_healthy, oraRacStatusSpex.Name) { +// existing_crs_nodes_healthy = append(existing_crs_nodes_healthy, oraRacStatusSpex.Name) +// } +// } else { +// if !checkElem(existing_crs_nodes_not_healthy, oraRacStatusSpex.Name) { +// existing_crs_nodes_not_healthy = append(existing_crs_nodes_not_healthy, oraRacStatusSpex.Name) +// } +// } +// } +// } + +// } +// //External for loop ends here + +// // Main if condition ends here +// return strings.Join(new_crs_nodes[:], ";"), strings.Join(existing_crs_nodes_healthy, ","), strings.Join(existing_crs_nodes_not_healthy, ","), install_node, strings.Join(new_crs_nodes_list, ",") + +// } + +func GetAsmDevices(instance *oraclerestart.OracleRestart) string { + var asmDisk []string + if instance.Spec.AsmStorageDetails != nil { + // Loop through each DiskBySize and add disk names to asmDisk + for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { + asmDisk = append(asmDisk, diskBySize.DiskNames...) + } + } + return strings.Join(asmDisk, ",") +} + +// func GetScanname(instance *oraclerestart.OracleRestart) string { +// return instance.Spec.ScanSvcName +// } + +func GetSSHkey(instance *oraclerestart.OracleRestart, name string, kClient client.Client) (bool, bool) { + + var privKeyFlag, pubKeyFlag bool + secretFound, err := CheckSecret(instance, name, kClient) + + if err != nil { + + } else { + for key := range secretFound.Data { + switch key { + case instance.Spec.SshKeySecret.PrivKeySecretName: + privKeyFlag = true + case instance.Spec.SshKeySecret.PubKeySecretName: + pubKeyFlag = true + } + } + } + + return privKeyFlag, pubKeyFlag + +} + +func GetDbSecret(instance *oraclerestart.OracleRestart, name string, kClient client.Client) (bool, bool, bool) { + + var commonospassflag, commonpwdfile, pwdkeyflag bool + secretFound, err := CheckSecret(instance, name, kClient) + + if err != nil { + return false, false, false + } else { + for key := range secretFound.Data { + switch key { + case instance.Spec.DbSecret.PwdFileName: + commonospassflag = true + case instance.Spec.DbSecret.KeyFileName: + pwdkeyflag = true + case "pwdfile": + commonpwdfile = true + } + } + } + + return commonospassflag, pwdkeyflag, commonpwdfile + +} + +func GetTdeWalletSecret(instance *oraclerestart.OracleRestart, name string, kClient client.Client) (bool, bool, bool) { + + var commonospassflag, commonpwdfile, pwdkeyflag bool + secretFound, err := CheckSecret(instance, name, kClient) + + if err != nil { + return false, false, false + } else { + for key := range secretFound.Data { + switch key { + case instance.Spec.TdeWalletSecret.PwdFileName: + commonospassflag = true + case instance.Spec.TdeWalletSecret.KeyFileName: + pwdkeyflag = true + case "pwdfile": + commonpwdfile = true + } + } + } + return commonospassflag, pwdkeyflag, commonpwdfile +} + +func CheckSecret(instance *oraclerestart.OracleRestart, secretName string, kClient client.Client) (*corev1.Secret, error) { + + secretFound := &corev1.Secret{} + + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: secretName, + Namespace: instance.Namespace, + }, secretFound) + + if err != nil { + return secretFound, err + } + + return secretFound, nil +} + +func GetGiResponseFile(instance *oraclerestart.OracleRestart, kClient client.Client) (bool, map[string]string) { + + var giFileFlag bool + cName := instance.Spec.ConfigParams.GridResponseFile.ConfigMapName + + configMapFound, err := CheckConfigMap(instance, cName, kClient) + if err != nil { + + } else { + for key := range configMapFound.Data { + if key == instance.Spec.ConfigParams.GridResponseFile.Name { + giFileFlag = true + } + } + } + return giFileFlag, configMapFound.Data +} + +func GetDbResponseFile(instance *oraclerestart.OracleRestart, kClient client.Client) (bool, map[string]string) { + + var dbFileFlag bool + cName := instance.Spec.ConfigParams.DbResponseFile.ConfigMapName + + configMapFound, err := CheckConfigMap(instance, cName, kClient) + if err != nil { + + } else { + for key := range configMapFound.Data { + if key == instance.Spec.ConfigParams.DbResponseFile.Name { + dbFileFlag = true + } + } + } + return dbFileFlag, configMapFound.Data +} + +func CheckConfigMap(instance *oraclerestart.OracleRestart, configMapName string, kClient client.Client) (*corev1.ConfigMap, error) { + + configMapFound := &corev1.ConfigMap{} + + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: configMapName, + Namespace: instance.Namespace, + }, configMapFound) + + if err != nil { + return configMapFound, err + } + + return configMapFound, nil + +} + +func GetConfigList(sfsetName string, instance *oraclerestart.OracleRestart, kClient client.Client, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec, +) (*corev1.ConfigMapList, error) { + cmapList := &corev1.ConfigMapList{} + //labelSelector := labels.SelectorFromSet(getlabelsForRAC(instance)) + //labelSelector := map[string]labels.Selector{} + var labelSelector labels.Selector = labels.SelectorFromSet(getSvcLabelsForOracleRestart(-1, OraRestartSpex)) + + listOps := &client.ListOptions{Namespace: instance.Namespace, LabelSelector: labelSelector} + + err := kClient.List(context.TODO(), cmapList, listOps) + if err != nil { + return nil, err + } + return cmapList, nil +} + +func checkElem(list1 []string, element string) bool { + + if element != "" { + if len(list1) > 0 { + for _, v := range list1 { + if v == element { + return true + } + } + } + } + + return false +} + +func ValidateNetInterface(net string, instance *oraclerestart.OracleRestart, rspNetData string) error { + + var err error + + if net != "" { + if !strings.Contains(rspNetData, net) { + err = fmt.Errorf("Error occurred during retreiving network card detail from grid responsefile: %s", "The key does not exist") + } + } + + return err +} + +func CheckRspData(instance *oraclerestart.OracleRestart, kClient client.Client, key string, cName string, fname string) (string, error) { + + var rspData string + configMapFound, _ := CheckConfigMap(instance, cName, kClient) + var status bool + var value []string + + data := configMapFound.Data[fname] + + scanner := bufio.NewScanner(strings.NewReader(data)) + for scanner.Scan() { + if strings.Contains(strings.ToLower(scanner.Text()), strings.ToLower(key)) { + if strings.ToLower(key) == "variables=" { + str1 := strings.Replace(scanner.Text(), "=", ";", 1) + value = strings.Split(str1, ";") + } else { + value = strings.Split(scanner.Text(), "=") + } + rspData = value[1] + fmt.Print("Key = " + value[0] + " value= " + value[1]) + status = true + break + } + } + + if !status { + return "", errors.New("the " + key + " key and value does not exist in grid responsefile. Invalid grid responsefile.") + } + + return rspData, nil +} + +func GetServiceParams(instance *oraclerestart.OracleRestart) string { + var sparams string + + sparams = "service:" + instance.Spec.ServiceDetails.Name + notficationFlag, _ := strconv.ParseBool(instance.Spec.ServiceDetails.Notification) + if notficationFlag { + sparams = sparams + ";notification:" + "True" + } + if instance.Spec.ServiceDetails.Cardinality != "" { + sparams = sparams + ";cardinality:" + instance.Spec.ServiceDetails.Cardinality + } + if len(instance.Spec.ServiceDetails.Preferred) > 0 { + sparams = sparams + ";preferred:" + strings.Join(instance.Spec.ServiceDetails.Preferred[:], ",") + } + if len(instance.Spec.ServiceDetails.Available) > 0 { + sparams = sparams + ";available:" + strings.Join(instance.Spec.ServiceDetails.Available[:], ",") + } + if instance.Spec.ServiceDetails.Pdb != "" { + sparams = sparams + ";pdb:" + instance.Spec.ServiceDetails.Pdb + } + + if instance.Spec.ServiceDetails.ClbGoal != "" { + sparams = sparams + ";clbgoal:" + instance.Spec.ServiceDetails.ClbGoal + } + + if instance.Spec.ServiceDetails.RlbGoal != "" { + sparams = sparams + ";rlbgoal:" + instance.Spec.ServiceDetails.RlbGoal + } + + if instance.Spec.ServiceDetails.FailOverRestore != "" { + sparams = sparams + ";failover_restore:" + instance.Spec.ServiceDetails.FailOverRestore + } + + if instance.Spec.ServiceDetails.FailBack != "" { + sparams = sparams + ";failback:" + instance.Spec.ServiceDetails.FailBack + } + + cmdFlag, _ := strconv.ParseBool(instance.Spec.ServiceDetails.CommitOutCome) + if cmdFlag { + sparams = sparams + ";commit_outcome:" + "True" + } + + cmtPathFlag, _ := strconv.ParseBool(instance.Spec.ServiceDetails.CommitOutComeFastPath) + if cmtPathFlag { + sparams = sparams + ";commit_outcome_fastpath:" + "True" + } + + if instance.Spec.ServiceDetails.FailBack != "" { + sparams = sparams + ";failback:" + instance.Spec.ServiceDetails.FailBack + } + + if instance.Spec.ServiceDetails.FailOverType != "" { + sparams = sparams + ";failovertype:" + instance.Spec.ServiceDetails.FailOverType + } + + if instance.Spec.ServiceDetails.FailOverDelay > 0 { + sparams = sparams + ";failoverdelay:" + strconv.FormatInt(int64(instance.Spec.ServiceDetails.FailOverDelay), 10) + } + + if instance.Spec.ServiceDetails.FailOverRetry > 0 { + sparams = sparams + ";failoverretry:" + strconv.FormatInt(int64(instance.Spec.ServiceDetails.FailOverRetry), 10) + } + + if instance.Spec.ServiceDetails.DrainTimeOut > 0 { + sparams = sparams + ";drain_timeout:" + strconv.FormatInt(int64(instance.Spec.ServiceDetails.DrainTimeOut), 10) + } + + if instance.Spec.ServiceDetails.Dtp != "" { + sparams = sparams + ";dtp:" + instance.Spec.ServiceDetails.Dtp + } + + if instance.Spec.ServiceDetails.Role != "" { + sparams = sparams + ";role:" + instance.Spec.ServiceDetails.Role + } + + if instance.Spec.ServiceDetails.Retention > 0 { + sparams = sparams + ";retention:" + strconv.FormatInt(int64(instance.Spec.ServiceDetails.Retention), 10) + } + + return sparams +} + +// . This function get the healthy node name from instance.status + +func GetHealthyNode(instance *oraclerestart.OracleRestart) (string, error) { + var i int32 + + if len(instance.Status.OracleRestartNodes) > 0 { + for i = 0; i < int32(len(instance.Status.OracleRestartNodes)); i++ { + if instance.Status.OracleRestartNodes[i].NodeDetails != nil { + if instance.Status.OracleRestartNodes[i].NodeDetails.ClusterState == "HEALTHY" { + return instance.Status.OracleRestartNodes[i].Name, nil + } + } + } + } + return "", fmt.Errorf("no healthy node exist") +} + +func GetHealthyNodeCounts(instance *oraclerestart.OracleRestart) (int, error) { + var i, totalNodes, healthyNodeCount int + + totalNodes = len(instance.Status.OracleRestartNodes) + + if totalNodes > 0 { + for i = 0; i < totalNodes; i++ { + if instance.Status.OracleRestartNodes[i].NodeDetails != nil { + if instance.Status.OracleRestartNodes[i].NodeDetails.ClusterState == "HEALTHY" { + healthyNodeCount++ + } + } + } + } + + if totalNodes == healthyNodeCount { + return healthyNodeCount, nil + } + return 0, fmt.Errorf("healthy cluster node counts are not matching with total cluster nodes") +} diff --git a/commons/oraclerestart/oraclerestartconstants.go b/commons/oraclerestart/oraclerestartconstants.go new file mode 100644 index 00000000..2e4ea565 --- /dev/null +++ b/commons/oraclerestart/oraclerestartconstants.go @@ -0,0 +1,159 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +func getOracleRestartDbModeCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + var oraRacInstCmd = []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--checkracinst=true "} + return oraRacInstCmd +} + +func getGiHealthCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + var oraGiCmd = []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--checkgilocal=true "} + return oraGiCmd +} + +func getOracleRestartHealthCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + var oraRacCmd = []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--checkracdb=true "} + return oraRacCmd +} + +func getConnStrCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + var oraRacConnCmd = []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--checkconnstr=true "} + return oraRacConnCmd +} +func getGridHomeCmd() []string { + // Command to source the envfile and echo GRID_HOME + gridHomeCmd := []string{"sh", "-c", "grep '^GRID_HOME=' /etc/rac_env_vars/envfile | cut -d'=' -f2"} + return gridHomeCmd +} + +func getPdbConnStrCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + var oraRacConnCmd = []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--checkpdbconnstr=true "} + return oraRacConnCmd +} + +func getDbRoleCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + var oraRacRoleCmd = []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--checkdbrole=true "} + return oraRacRoleCmd +} + +func getOracleRestartDbVersionCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + var oraRacVersionCmd = []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--checkdbversion=true "} + return oraRacVersionCmd +} + +func getDBServiceStatus(dbhome string, dbname string, svcname string) []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + svcStr := "\"" + "service=" + svcname + ";dbname=" + dbname + "\"" + var oraRacVersionCmd = []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--checkdbsvc=" + svcStr} + return oraRacVersionCmd +} + +func modifyDBServiceStatus(dbhome string, dbname string, svcname string) []string { + oraDBUser := getOraDbUser() + + //oraGiUser := getOraGiUser() + svcModifyCmd := "su " + oraDBUser + " -c \"" + oraDBUser + " ;srvctl modify service -s " + svcname + " -d " + dbname + " " + dbhome + "\"" + var oraModifySvcCmd = []string{svcModifyCmd} + return oraModifySvcCmd +} + +func getAsmDiskgroupCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + diskgroupscmd := []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--getasmdiskgroup=true"} + return diskgroupscmd +} + +func getAsmDisksCmd(diskgroup string) []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + disks := []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--getasmdisks=" + diskgroup} + return disks +} + +func getAsmDgRedundancyCmd(diskgroup string) []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + redundancy := []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--getdgredundancy=" + diskgroup} + return redundancy +} + +func getAsmInstNameCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + name := []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--getasminstname=true"} + return name +} + +func getAsmInstStatusCmd() []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + status := []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--getasminststatus=true"} + return status +} + +// This function generates cmmand for modifying asm cardinality for the nodes +func getUpdateAsmCount(gihome string) []string { + oraScriptMount1 := getOraScriptMount() + oraPythonCmd := getOraPythonCmd() + var oraUpdAsmCountCmd = []string{oraScriptMount1 + "/cmdExec", oraPythonCmd, oraScriptMount1 + "/main.py ", "--updateasmcount=2"} + return oraUpdAsmCountCmd +} + +// This function generates cmmand for modifying asm cardinality for the nodes +func getOracleRestartInstStateFileCmd() []string { + var oraStateCmd = []string{"/bin/bash", "-c", " cat /tmp/orod/.statefile"} + return oraStateCmd +} diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go new file mode 100644 index 00000000..86f2232c --- /dev/null +++ b/commons/oraclerestart/oraclerestartprov.go @@ -0,0 +1,1034 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/go-logr/logr" + oraclerestart "github.com/oracle/oracle-database-operator/apis/database/v4" + utils "github.com/oracle/oracle-database-operator/commons/oraclerestart/utils" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// Constants for rac-stateful StatefulSet & Volumes +func buildLabelsForOracleRestart(instance *oraclerestart.OracleRestart, label string) map[string]string { + return map[string]string{ + "cluster": "oraclerestart", + } + + // "oralabel": getLabelForOracleRestart(instance), +} + +func buildLabelsForAsmPv(instance *oraclerestart.OracleRestart, label string, index int) map[string]string { + return map[string]string{ + "asm_vol": "block-asm-pv-" + getLabelForOracleRestart(instance) + "-" + fmt.Sprint(index), + } +} + +func getLabelForOracleRestart(instance *oraclerestart.OracleRestart) string { + + return instance.Name +} + +func BuildStatefulSetForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, kClient client.Client) *appsv1.StatefulSet { + sfset := &appsv1.StatefulSet{ + TypeMeta: buildTypeMetaForOracleRestart(), + ObjectMeta: builObjectMetaForOracleRestart(instance, OracleRestartSpex), + Spec: *buildStatefulSpecForOracleRestart(instance, OracleRestartSpex, kClient), + } + return sfset +} + +// Function to build TypeMeta +func buildTypeMetaForOracleRestart() metav1.TypeMeta { + // building TypeMeta + typeMeta := metav1.TypeMeta{ + Kind: "StatefulSet", + APIVersion: "apps/v1", + } + return typeMeta +} + +// Function to build ObjectMeta +func builObjectMetaForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) metav1.ObjectMeta { + // building objectMeta + objmeta := metav1.ObjectMeta{ + Name: OracleRestartSpex.Name, + Namespace: instance.Namespace, + Labels: buildLabelsForOracleRestart(instance, "OracleRestart"), + } + return objmeta +} + +// Function to build Stateful Specs +func buildStatefulSpecForOracleRestart( + instance *oraclerestart.OracleRestart, + OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, + kClient client.Client, +) *appsv1.StatefulSetSpec { + + // Build PodSpec first + podSpec := buildPodSpecForOracleRestart(instance, OracleRestartSpex) + + // Add service account name if specified + if instance.Spec.SrvAccountName != "" { + podSpec.ServiceAccountName = instance.Spec.SrvAccountName + } + + // Build StatefulSetSpec + sfsetspec := &appsv1.StatefulSetSpec{ + ServiceName: utils.OraSubDomain, + Selector: &metav1.LabelSelector{ + MatchLabels: buildLabelsForOracleRestart(instance, "OracleRestart"), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: OracleRestartSpex.Name, + Labels: buildLabelsForOracleRestart(instance, "OracleRestart"), + }, + Spec: *podSpec, // dereference after modification + }, + } + + // Add volume claim templates if a storage class is specified + if len(instance.Spec.StorageClass) != 0 && !asmPvcsExist(instance, kClient) { + sfsetspec.VolumeClaimTemplates = VolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex) + } + + // Add annotations to the Pod template + // sfsetspec.Template.Annotations = generateNetworkDetails(instance, OracleRestartSpex, kClient) + + return sfsetspec +} +func asmPvcsExist(instance *oraclerestart.OracleRestart, kClient client.Client) bool { + for i := range instance.Spec.AsmStorageDetails.DisksBySize { + pvcName := GetAsmPvcName(i, instance.Name) + var pvc corev1.PersistentVolumeClaim + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: pvcName, + Namespace: instance.Namespace, + }, &pvc) + + if err != nil { + if apierrors.IsNotFound(err) { + // If even one expected PVC is not found, treat as "not all exist" + return false + } + // If error is something else, assume PVCs exist to avoid accidental overwrite + return true + } + } + return true +} + +// Function to build PodSpec + +func buildPodSpecForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) *corev1.PodSpec { + + spec := &corev1.PodSpec{ + Hostname: OracleRestartSpex.Name + "-0", + Subdomain: utils.OraSubDomain, + InitContainers: buildInitContainerSpecForOracleRestart(instance, OracleRestartSpex), + Containers: buildContainerSpecForOracleRestart(instance, OracleRestartSpex), + Volumes: buildVolumeSpecForOracleRestart(instance, OracleRestartSpex), + Affinity: getNodeAffinity(instance, OracleRestartSpex), + } + + if instance.Spec.SecurityContext != nil { + spec.SecurityContext = instance.Spec.SecurityContext + } + + if len(instance.Spec.ImagePullSecret) > 0 { + spec.ImagePullSecrets = []corev1.LocalObjectReference{ + { + Name: instance.Spec.ImagePullSecret, + }, + } + } + return spec +} + +// Function get the Node Affinity +func getNodeAffinity(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) *corev1.Affinity { + + nodeAffinity := &corev1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{}, + }, + } + + racTerm := corev1.NodeSelectorTerm{ + MatchExpressions: []corev1.NodeSelectorRequirement{}, + } + + racMatch := corev1.NodeSelectorRequirement{ + Key: utils.OraNodeKey, + Operator: utils.OraOperatorKey, + Values: OracleRestartSpex.WorkerNode, + } + + racTerm.MatchExpressions = append(racTerm.MatchExpressions, racMatch) + nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = append(nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms, + racTerm) + + affinity := &corev1.Affinity{NodeAffinity: nodeAffinity} + return affinity + +} + +// Function get the Node Affinity +func getAsmNodeAffinity(instance *oraclerestart.OracleRestart, index int, disk *oraclerestart.AsmDiskDetails) *corev1.VolumeNodeAffinity { + + nodeAffinity := &corev1.VolumeNodeAffinity{ + Required: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{}, + }, + } + + racTerm := corev1.NodeSelectorTerm{ + MatchExpressions: []corev1.NodeSelectorRequirement{}, + } + + racMatch := corev1.NodeSelectorRequirement{ + Key: utils.OraNodeKey, + Operator: utils.OraOperatorKey, + Values: instance.Spec.InstDetails.WorkerNode, + } + + racTerm.MatchExpressions = append(racTerm.MatchExpressions, racMatch) + nodeAffinity.Required.NodeSelectorTerms = append(nodeAffinity.Required.NodeSelectorTerms, + racTerm) + + return nodeAffinity + +} + +// Function to build Volume Spec +func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.Volume { + var result []corev1.Volume + result = []corev1.Volume{ + { + Name: OracleRestartSpex.Name + "-ssh-secretmap-vol", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: instance.Spec.SshKeySecret.Name, + }, + }, + }, + { + Name: OracleRestartSpex.Name + "-oradshm-vol", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory}, + }, + }, + } + + if len(instance.Spec.ScriptsLocation) != 0 { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-scripts-vol", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}) + } + + if instance.Spec.DbSecret != nil { + if instance.Spec.DbSecret.Name != "" { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-dbsecret-pwd-vol", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: instance.Spec.DbSecret.Name}}}) + } + + if instance.Spec.DbSecret.KeySecretName != "" { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-dbsecret-key-vol", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: instance.Spec.DbSecret.KeySecretName}}}) + } + } + + if instance.Spec.TdeWalletSecret != nil { + if instance.Spec.TdeWalletSecret.Name != "" { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-tdesecret-pwd-vol", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: instance.Spec.TdeWalletSecret.Name}}}) + } + + if instance.Spec.TdeWalletSecret.KeySecretName != "" { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-tdesecret-key-vol", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: instance.Spec.TdeWalletSecret.KeySecretName}}}) + } + } + + if len(instance.Spec.ConfigParams.GridResponseFile.ConfigMapName) != 0 { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-girsp", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: instance.Spec.ConfigParams.GridResponseFile.ConfigMapName}}}}) + } + + if len(instance.Spec.ConfigParams.DbResponseFile.ConfigMapName) != 0 { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-dbrsp", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: instance.Spec.ConfigParams.DbResponseFile.ConfigMapName}}}}) + } + + if len(OracleRestartSpex.EnvFile) != 0 { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-envfile", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OracleRestartSpex.EnvFile}}}}) + } + + if len(OracleRestartSpex.HostSwLocation) != 0 { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: OracleRestartSpex.HostSwLocation}}}) + } + + if instance.Spec.ConfigParams != nil { + if instance.Spec.ConfigParams.HostSwStageLocation != "" { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-swstage-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: instance.Spec.ConfigParams.HostSwStageLocation}}}) + } + } + + if len(OracleRestartSpex.PvcName) != 0 { + for source := range OracleRestartSpex.PvcName { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-ora-vol-" + source, VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: source}}}) + } + } + + if instance.Spec.ConfigParams != nil { + if len(instance.Spec.ConfigParams.RuPatchLocation) != 0 { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.RuPatchLocation, + }, + }, + }) + } + + if len(instance.Spec.ConfigParams.OPatchLocation) != 0 { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-opatch-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.OPatchLocation, + }, + }, + }) + } + } + + if instance.Spec.AsmStorageDetails != nil { + // Iterate over the DisksBySize slice + for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { + // For each DiskBySize, append PVCs for the disks in DiskNames + for index := range diskBySize.DiskNames { + // Construct PVC name based on index and instance name + pvcName := getAsmPvcName(index, instance.Name) + result = append(result, corev1.Volume{ + Name: pvcName + "-pvc", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvcName, + }, + }, + }) + } + } + } + + return result +} + +// Function to build the container Specification +func buildContainerSpecForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.Container { + // building Continer spec + var result []corev1.Container + privileged := false + failureThreshold := 1 + periodSeconds := 5 + initialDelaySeconds := 120 + oraLsnrPort := 1521 + + // Get the Idx + + containerSpec := corev1.Container{ + Name: OracleRestartSpex.Name, + Image: instance.Spec.Image, + SecurityContext: &corev1.SecurityContext{ + Privileged: &privileged, + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("SYS_NICE"), corev1.Capability("SYS_RESOURCE"), corev1.Capability("AUDIT_WRITE"), corev1.Capability("NET_RAW"), corev1.Capability("AUDIT_CONTROL"), corev1.Capability("SYS_CHROOT")}, + }, + }, + Command: []string{ + "/usr/sbin/init", + }, + VolumeDevices: getAsmVolumeDevices(instance, OracleRestartSpex), + Resources: corev1.ResourceRequirements{ + Requests: make(map[corev1.ResourceName]resource.Quantity), + }, + VolumeMounts: buildVolumeMountSpecForOracleRestart(instance, OracleRestartSpex), + ReadinessProbe: &corev1.Probe{ + // TODO: Investigate if it's ok to call status every 10 seconds + FailureThreshold: int32(failureThreshold), + PeriodSeconds: int32(periodSeconds), + InitialDelaySeconds: int32(initialDelaySeconds), + ProbeHandler: corev1.ProbeHandler{TCPSocket: &corev1.TCPSocketAction{Port: intstr.FromInt(int(oraLsnrPort))}}, + }, + } + if instance.Spec.Resources != nil { + containerSpec.Resources = *instance.Spec.Resources + } + + if len(OracleRestartSpex.EnvVars) > 0 { + containerSpec.Env = buildEnvVarsSpec(OracleRestartSpex.EnvVars) + } + + if instance.Spec.ReadinessProbe != nil { + containerSpec.ReadinessProbe = instance.Spec.ReadinessProbe + } + // building Complete Container Spec + containerSpec.Ports = buildContainerPortsDef(instance, OracleRestartSpex) + + result = []corev1.Container{ + containerSpec, + } + return result +} + +func getAsmVolumeDevices(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.VolumeDevice { + var result []corev1.VolumeDevice + + if instance.Spec.AsmStorageDetails != nil { + // Iterate over the DisksBySize slice + for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { + // For each disk in DiskNames, create a VolumeDevice + for _, diskName := range diskBySize.DiskNames { + // Create PVC name and append VolumeDevice to the result + pvcName := getAsmPvcName(len(result), instance.Name) + result = append(result, corev1.VolumeDevice{Name: pvcName + "-pvc", DevicePath: diskName}) + } + } + } + + return result +} + +// Function to build the init Container Spec +func buildInitContainerSpecForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.Container { + var result []corev1.Container + // building the init Container Spec + privFlag := true + var uid int64 = 0 + var scriptsCmd string + var scriptsLocation string + + if len(instance.Spec.ScriptsGetCmd) != 0 { + scriptsCmd = instance.Spec.ScriptsGetCmd + } else { + scriptsCmd = "/bin/true" + } + + if len(instance.Spec.ScriptsLocation) != 0 { + scriptsLocation = instance.Spec.ScriptsLocation + } else { + scriptsLocation = "NOLOC" + } + + init1spec := corev1.Container{ + Name: OracleRestartSpex.Name + "-init1", + Image: instance.Spec.Image, + SecurityContext: &corev1.SecurityContext{ + Privileged: &privFlag, + RunAsUser: &uid, + }, + Command: []string{ + "/bin/bash", + "-c", + getRacInitContainerCmd(scriptsCmd, instance.Name, scriptsLocation), + }, + VolumeMounts: buildVolumeMountSpecForOracleRestart(instance, OracleRestartSpex), + } + + // building Complete Init Container Spec + if instance.Spec.ImagePullPolicy != nil { + init1spec.ImagePullPolicy = *instance.Spec.ImagePullPolicy + } + result = []corev1.Container{ + init1spec, + } + return result +} + +func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.VolumeMount { + var result []corev1.VolumeMount + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-ssh-secretmap-vol", MountPath: instance.Spec.SshKeySecret.KeyMountLocation, ReadOnly: true}) + if instance.Spec.DbSecret != nil { + if instance.Spec.DbSecret.KeySecretName != "" { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-dbsecret-key-vol", MountPath: instance.Spec.DbSecret.KeyFileMountLocation, ReadOnly: true}) + } + if instance.Spec.DbSecret.Name != "" { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-dbsecret-pwd-vol", MountPath: instance.Spec.DbSecret.PwdFileMountLocation, ReadOnly: true}) + } + } + + if instance.Spec.TdeWalletSecret != nil { + if instance.Spec.TdeWalletSecret.Name != "" { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-tdesecret-pwd-vol", MountPath: instance.Spec.TdeWalletSecret.PwdFileMountLocation, ReadOnly: true}) + } + + if instance.Spec.TdeWalletSecret.KeySecretName != "" { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-tdesecret-key-vol", MountPath: instance.Spec.TdeWalletSecret.KeyFileMountLocation, ReadOnly: true}) + } + } + //result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-boot-vol", MountPath: oraBootVol}) + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradshm-vol", MountPath: utils.OraShm}) + + if len(instance.Spec.ConfigParams.GridResponseFile.ConfigMapName) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-girsp", MountPath: utils.OraGiRsp}) + } + + if len(instance.Spec.ConfigParams.DbResponseFile.ConfigMapName) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-dbrsp", MountPath: utils.OraDbRsp}) + } + + if len(OracleRestartSpex.EnvFile) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-envfile", MountPath: utils.OraEnvFile}) + } + + if len(instance.Spec.ScriptsLocation) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-scripts-vol", MountPath: instance.Spec.ScriptsLocation}) + } + if len(OracleRestartSpex.HostSwLocation) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-sw-vol", MountPath: instance.Spec.ConfigParams.SwMountLocation}) + } else if len(instance.Spec.StorageClass) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-sw-vol", MountPath: instance.Spec.ConfigParams.SwMountLocation}) + } else { + fmt.Println("No Location is passed for the software storage in" + OracleRestartSpex.Name) + } + + if instance.Spec.ConfigParams != nil { + if len(instance.Spec.ConfigParams.HostSwStageLocation) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-swstage-vol", MountPath: utils.OraSwStageLocation}) + } + } + + if instance.Spec.ConfigParams != nil { + if len(instance.Spec.ConfigParams.RuPatchLocation) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", MountPath: utils.OraRuPatchStageLocation}) + } + } + + if instance.Spec.ConfigParams != nil { + if len(instance.Spec.ConfigParams.OPatchLocation) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-opatch-vol", MountPath: utils.OraOPatchStageLocation}) + } + } + + if len(OracleRestartSpex.PvcName) != 0 { + for source, target := range OracleRestartSpex.PvcName { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-ora-vol-" + source, MountPath: target}) + } + } + + return result +} + +func VolumePVCForASM(instance *oraclerestart.OracleRestart, index int, diskName string, size int, asmStorage *oraclerestart.AsmDiskDetails, k8sClient client.Client) *corev1.PersistentVolumeClaim { + // Set volume mode to block + volumeBlock := corev1.PersistentVolumeBlock + + // Create PersistentVolumeClaim + asmPvc := &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: getAsmPvcName(index, instance.Name), // Use size to determine index + Namespace: instance.Namespace, + Labels: buildLabelsForOracleRestart(instance, "OracleRestart"), + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + VolumeMode: &volumeBlock, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(strconv.FormatInt(int64(size), 10) + "Gi")}, + }, + }, + } + + // Check if a StorageClass is defined and set it + if len(instance.Spec.StorageClass) != 0 { + asmPvc.Spec.StorageClassName = &instance.Spec.StorageClass + } else { + // If no StorageClass, use the LabelSelector based on disk size + asmPvc.Spec.Selector = &metav1.LabelSelector{MatchLabels: buildLabelsForAsmPv(instance, string(diskName), index)} + } + + return asmPvc +} + +func VolumePVForASM(instance *oraclerestart.OracleRestart, index int, diskName string, size int, asmStorage *oraclerestart.AsmDiskDetails, k8sClient client.Client) *corev1.PersistentVolume { + volumeBlock := corev1.PersistentVolumeBlock + + asmPvc := &corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: getAsmPvName(index, instance.Name), + Namespace: instance.Namespace, + Labels: buildLabelsForAsmPv(instance, diskName, index), + }, + Spec: corev1.PersistentVolumeSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + VolumeMode: &volumeBlock, + Capacity: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(strconv.FormatInt(int64(size), 10) + "Gi")}, + }, + } + + if len(instance.Spec.StorageClass) != 0 { + asmPvc.Spec.StorageClassName = instance.Spec.StorageClass + asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) + asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} + + } else { + asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) + asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} + } + + return asmPvc +} + +func BuildServiceDefForOracleRestart(instance *oraclerestart.OracleRestart, replicaCount int32, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, svctype string) *corev1.Service { + //service := &corev1.Service{} + service := &corev1.Service{ + TypeMeta: metav1.TypeMeta{Kind: "Service"}, + ObjectMeta: buildSvcObjectMetaForOracleRestart(instance, replicaCount, OracleRestartSpex, svctype), + Spec: corev1.ServiceSpec{}, + } + + // // Check if user want External Svc on each replica pod + if strings.ToUpper(svctype) == "VIP" || strings.ToUpper(svctype) == "LOCAL" { + service.Spec.ClusterIP = corev1.ClusterIPNone + service.Spec.Selector = getSvcLabelsForOracleRestart(replicaCount, OracleRestartSpex) + + } + + // if strings.ToUpper(svctype) == "SCAN" { + // service.Spec.ClusterIP = corev1.ClusterIPNone + // service.Spec.Selector = buildLabelsForOracleRestart(instance, "OracleRestart") + // } + + service.Spec.PublishNotReadyAddresses = true + + // build Service Ports Specs to be exposed. If the PortMappings is not set then default ports will be exposed. + + return service +} + +func BuildExternalServiceDefForOracleRestart(instance *oraclerestart.OracleRestart, index int32, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, svctype string, opType string) *corev1.Service { + //service := &corev1.Service{} + + var npSvc oraclerestart.OracleRestartNodePortSvc + + if OracleRestartSpex.PortMappings != nil { + npSvc = OracleRestartSpex.NodePortSvc[index] + npSvc.PortMappings = OracleRestartSpex.PortMappings + } + + service := &corev1.Service{ + ObjectMeta: buildSvcObjectMetaForOracleRestart(instance, index, OracleRestartSpex, opType), + Spec: corev1.ServiceSpec{}, + } + + if opType == "nodeport" { + // service.Spec.ClusterIP = string(corev1.ServiceTypeNodePort) + if strings.EqualFold(npSvc.SvcType, "vip") { + service.Spec.Selector = getSvcLabelsForOracleRestart(0, OracleRestartSpex) + } else if strings.EqualFold(npSvc.SvcType, "scan") { + service.Spec.Selector = buildLabelsForOracleRestart(instance, "OracleRestart") + } else { + service.Spec.Selector = getSvcLabelsForOracleRestart(0, OracleRestartSpex) + } + } + + service.Spec.Ports = buildOracleRestartSvcPortsDef(npSvc) + + if svctype == "nodeport" { + service.Spec.Type = corev1.ServiceTypeNodePort + } + + if svctype == "lbservice" { + service.Spec.Type = corev1.ServiceTypeClusterIP + } + + // build Service Ports Specs to be exposed. If the PortMappings is not set then default ports will be exposed. + + return service +} + +// Function to build Service ObjectMeta +func buildSvcObjectMetaForOracleRestart(instance *oraclerestart.OracleRestart, replicaCount int32, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, svctype string) metav1.ObjectMeta { + // building objectMeta + //var svcName string + + var labelStr map[string]string + if strings.ToUpper(svctype) == "VIP" || strings.ToUpper(svctype) == "LOCAL" || strings.ToUpper(svctype) == "ONSSVC" || strings.ToUpper(svctype) == "LSNRSVC" { + labelStr = getSvcLabelsForOracleRestart(replicaCount, OracleRestartSpex) + } else if strings.ToUpper(svctype) == "SCAN" { + labelStr = nil + } else { + labelStr = nil + } + + objmeta := metav1.ObjectMeta{ + Name: getOracleRestartSvcName(instance, OracleRestartSpex, svctype), + Namespace: instance.Namespace, + } + + if labelStr != nil { + objmeta.Labels = labelStr + } + + return objmeta +} + +func getOracleRestartSvcName(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, svcType string) string { + + switch svcType { + case "local": + return OracleRestartSpex.Name + "-0" + // case "vip": + // return OracleRestartSpex.VipSvcName + // case "scan": + // return instance.Spec.ScanSvcName + case "onssvc": + return OracleRestartSpex.Name + "-0-ons" + case "lsnrsvc": + return OracleRestartSpex.Name + "-0-lsnr" + // case "scansvc": + // return instance.Spec.ScanSvcName + "-lsnr" + default: + return OracleRestartSpex.Name + + } +} + +func getSvcLabelsForOracleRestart(replicaCount int32, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) map[string]string { + + var labelStr map[string]string = make(map[string]string) + if replicaCount == -1 { + labelStr["statefulset.kubernetes.io/pod-name"] = OracleRestartSpex.Name + "-0" + } else { + labelStr["statefulset.kubernetes.io/pod-name"] = OracleRestartSpex.Name + "-0" + } + + // fmt.Println("Service Selector String Specification", labelStr) + return labelStr +} + +// This function cleanup the shard from GSM +func OraCleanupForOracleRestart(instance *oraclerestart.OracleRestart, + OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, + oldReplicaSize int32, + newReplicaSize int32, +) string { + var err1 string + if oldReplicaSize > newReplicaSize { + for replicaCount := (oldReplicaSize - 1); replicaCount > (newReplicaSize - 1); replicaCount-- { + fmt.Println("Deleting the RAC " + OracleRestartSpex.Name + "-" + strconv.FormatInt(int64(replicaCount), 10)) + } + } + + err1 = "Test" + return err1 +} + +func UpdateProvForOracleRestart(instance *oraclerestart.OracleRestart, + OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, kClient client.Client, sfSet *appsv1.StatefulSet, gsmPod *corev1.Pod, logger logr.Logger, +) (ctrl.Result, error) { + + var msg string + var size int32 = 1 + var isUpdate bool = false + var err error + //var i int + + msg = "Inside the updateProvForOracleRestart" + LogMessages("DEBUG", msg, nil, instance, logger) + + // Ensure deployment replicas match the desired state + + if sfSet.Spec.Replicas != nil { + if *sfSet.Spec.Replicas != size { + msg = "Current StatefulSet replicas do not match configured Shard Replicas. Gsm is configured with only 1 but current replicas is set with " + strconv.FormatInt(int64(*sfSet.Spec.Replicas), 10) + LogMessages("DEBUG", msg, nil, instance, logger) + isUpdate = true + } + } + // Memory Check + //resources := corev1.Pod.Spec.Containers + + /** + + for i = 0; i < len(sfSet.Spec.VolumeClaimTemplates); i++ { + if sfSet.Spec.VolumeClaimTemplates[i].Name == OracleRestartSpex.Name+"-oradata-vol4" { + volumeSize := sfSet.Spec.VolumeClaimTemplates[i].Size() + if volumeSize != int(OracleRestartSpex.StorageSizeInGb) { + isUpdate = true + } + + } + } + + **/ + + if isUpdate { + err = kClient.Update(context.Background(), BuildStatefulSetForOracleRestart(instance, OracleRestartSpex, kClient)) + if err != nil { + msg = "Failed to update Shard StatefulSet " + "StatefulSet.Name : " + sfSet.Name + LogMessages("Error", msg, err, instance, logger) + return ctrl.Result{}, err + } + + } + + return ctrl.Result{}, nil +} + +func ConfigMapSpecs(instance *oraclerestart.OracleRestart, cmData map[string]string, cmName string) *corev1.ConfigMap { + //cm := &corev1.ConfigMap{} + + return &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: cmName, + Namespace: instance.Namespace, + Labels: map[string]string{ + "name": instance.Name + cmName, + }, + }, + Data: cmData, + } + +} + +func BuildDiskCheckDaemonSet(OracleRestart *oraclerestart.OracleRestart) *appsv1.DaemonSet { + labels := buildLabelsForOracleRestart(OracleRestart, "disk-check") + + // Prepare the volume devices based on the PVCs + var volumeDevices []corev1.VolumeDevice + var volumes []corev1.Volume + disks := flattenDisksBySize(&OracleRestart.Spec) + for index, diskPath := range disks { + pvcName := GetAsmPvcName(index, OracleRestart.Name) + volumeName := pvcName + "-pvc" + + volumeDevices = append(volumeDevices, corev1.VolumeDevice{ + Name: volumeName, + DevicePath: diskPath, + }) + + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvcName, + }, + }, + }) + } + + // Join the disk names into a space-separated string + // Flatten the DisksBySize map to get a single slice of all disk names + diskNamesSlice := flattenDisksBySize(&OracleRestart.Spec) + + // Join the flattened list of disk names into a single space-separated string + diskNames := strings.Join(diskNamesSlice, " ") + + return &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "disk-check-daemonset", + Namespace: OracleRestart.Namespace, + Labels: labels, + }, + Spec: appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: labels, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + }, + Spec: corev1.PodSpec{ + Affinity: &corev1.Affinity{ + NodeAffinity: &corev1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{ + { + MatchExpressions: []corev1.NodeSelectorRequirement{ + { + Key: "kubernetes.io/hostname", + Operator: corev1.NodeSelectorOpIn, + Values: OracleRestart.Spec.InstDetails.WorkerNode, + }, + }, + }, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "disk-check", + Image: OracleRestart.Spec.Image, + Command: []string{"/bin/bash", "-c"}, + Args: []string{ + "for disk in " + diskNames + "; do " + + "if [ ! -e $disk ]; then " + + "echo Disk $disk is not a valid block device; " + + "exit 1; " + + "else " + + "echo Disk $disk is valid; " + + "fi; " + + "done; " + + "sleep 3600", + }, + VolumeDevices: volumeDevices, + }, + }, + Volumes: volumes, + }, + }, + }, + } +} + +// Helper function to flatten DisksBySize into a single slice of disk names +func flattenDisksBySize(oraclerestartSpec *oraclerestart.OracleRestartSpec) []string { + disksBySize := oraclerestartSpec.AsmStorageDetails.DisksBySize + var allDisks []string + for _, diskBySize := range disksBySize { + allDisks = append(allDisks, diskBySize.DiskNames...) + } + return allDisks +} + +func CreateServiceAccountIfNotExists(instance *oraclerestart.OracleRestart, kClient client.Client) error { + ServiceAccountName := instance.Spec.SrvAccountName + if ServiceAccountName == "" { + ServiceAccountName = "default" + return nil + } + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: ServiceAccountName, + Namespace: instance.Namespace, + }, + } + + existingSA := &corev1.ServiceAccount{} + err := kClient.Get(context.TODO(), types.NamespacedName{Name: sa.Name, Namespace: sa.Namespace}, existingSA) + if err != nil { + if apierrors.IsNotFound(err) { + err = kClient.Create(context.TODO(), sa) + if err != nil { + return err + } + } else { + return err + } + } + return nil +} + +func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.OracleRestart) bool { + if instance.Spec.StorageClass != "" { + return false + } + + var scList storagev1.StorageClassList + if err := k8sClient.List(context.TODO(), &scList); err != nil { + return true // fallback to static if we can't query SCs + } + + for _, sc := range scList.Items { + if sc.Annotations["storageclass.kubernetes.io/is-default-class"] == "true" || + sc.Annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true" { + return false // dynamic provisioning is available + } + } + + return true // no default SC → use static +} +func VolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.PersistentVolumeClaim { + var claims []corev1.PersistentVolumeClaim + + // If user-provided PVC name exists, skip volume claim template creation + if len(OracleRestartSpex.PvcName) != 0 { + return claims + } + + index := 0 + for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { + for range diskBySize.DiskNames { + pvcName := GetAsmPvcName(index, instance.Name) + + claims = append(claims, corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvcName, + Namespace: instance.Namespace, + Labels: buildLabelsForOracleRestart(instance, "OracleRestart"), + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + StorageClassName: &instance.Spec.StorageClass, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(fmt.Sprintf("%dGi", diskBySize.StorageSizeInGb)), + }, + }, + }, + }) + index++ + } + } + + return claims +} diff --git a/commons/oraclerestart/oraclerestartstatus.go b/commons/oraclerestart/oraclerestartstatus.go new file mode 100644 index 00000000..480f68d2 --- /dev/null +++ b/commons/oraclerestart/oraclerestartstatus.go @@ -0,0 +1,344 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +import ( + "context" + "strings" + + "github.com/go-logr/logr" + oraclerestartdb "github.com/oracle/oracle-database-operator/apis/database/v4" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func UpdateOracleRestartInstStatusData( + OracleRestart *oraclerestartdb.OracleRestart, + ctx context.Context, + req ctrl.Request, + oraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, + state string, + kubeClient kubernetes.Interface, + kubeConfig clientcmd.ClientConfig, + logger logr.Logger, + kClient client.Client, +) { + // podName is constructed based on oraRestartSpex.Name + podName := oraRestartSpex.Name + "-0" + + oracleRestart := &oraclerestartdb.OracleRestartNodestatus{} + orestartNodeDetails := &oraclerestartdb.OracleRestartNodeDetailedStatus{} + strMap := make(map[string]string) + + if OracleRestart.Status.AsmDetails == nil { + OracleRestart.Status.AsmDetails = &oraclerestartdb.AsmInstanceStatus{} + } + + if OracleRestart.Status.ConfigParams == nil { + OracleRestart.Status.ConfigParams = &oraclerestartdb.InitParams{} + } + + if state == string(oraclerestartdb.OracleRestartUpdateState) { + OracleRestart.Status.State = state + } + if state == string(oraclerestartdb.OracleRestartProvisionState) { + OracleRestart.Status.State = state + } + if state == string(oraclerestartdb.OracleRestartFailedState) { + OracleRestart.Status.State = state + } + if state == string(oraclerestartdb.OracleRestartManualState) { + OracleRestart.Status.State = state + } + if state == string(oraclerestartdb.OracleRestartAvailableState) { + OracleRestart.Status.State = state + orestartNodeDetails.ClusterState = getClusterState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + orestartNodeDetails.PodState = state + orestartNodeDetails.State = "OPEN" + oracleRestart.Name = podName + + orestartNodeDetails.InstanceState = getDbInstState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + orestartNodeDetails.MountedDevices = getMountedDevices(podName, OracleRestart.Namespace, oracleRestart, oraRestartSpex, kClient, kubeConfig, logger, kubeClient) + oracleRestart.NodeDetails = orestartNodeDetails + addOracleRestartNodestatus(OracleRestart, ctx, req, oracleRestart, oraRestartSpex, 0, kubeClient, kubeConfig, logger) + + if len(oraRestartSpex.PvcName) > 0 { + orestartNodeDetails.PvcName = getPvcDetails(OracleRestart, oracleRestart, oraRestartSpex, kClient) + } + } + + // Update status based on the state + if state == string(oraclerestartdb.OracleRestartPodAvailableState) { + OracleRestart.Status.State = state + orestartNodeDetails.ClusterState = getClusterState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + orestartNodeDetails.PodState = state + orestartNodeDetails.State = "OPEN" + oracleRestart.Name = podName + + // orestartNodeDetails.VipDetails = getVipDetails(OracleRestart, oracleRestart, oraRestartSpex, kClient) + orestartNodeDetails.InstanceState = getDbInstState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + orestartNodeDetails.MountedDevices = getMountedDevices(podName, OracleRestart.Namespace, oracleRestart, oraRestartSpex, kClient, kubeConfig, logger, kubeClient) + oracleRestart.NodeDetails = orestartNodeDetails + OracleRestart.Status.ReleaseUpdate = "NOTAVAILABLE" + addOracleRestartNodestatus(OracleRestart, ctx, req, oracleRestart, oraRestartSpex, 0, kubeClient, kubeConfig, logger) + + if len(oraRestartSpex.PvcName) > 0 { + orestartNodeDetails.PvcName = getPvcDetails(OracleRestart, oracleRestart, oraRestartSpex, kClient) + } + if OracleRestart.Spec.ConfigParams.GridHome != "" { + OracleRestart.Status.ConfigParams.GridHome = OracleRestart.Spec.ConfigParams.GridHome + } + if OracleRestart.Spec.ConfigParams.DbHome != "" { + OracleRestart.Status.ConfigParams.DbHome = OracleRestart.Spec.ConfigParams.DbHome + } + // OracleRestart.Status.ConfigParams.CrsAsmDeviceList = OracleRestart.Spec.ConfigParams.CrsAsmDeviceList + OracleRestart.Status.ConfigParams.CrsAsmDeviceList = getcrsAsmDeviceList(OracleRestart, oracleRestart, oraRestartSpex, kClient, kubeConfig, logger, kubeClient) + + // OracleRestart.Status.ConfigParams.DbAsmDeviceList = OracleRestart.Spec.ConfigParams.DbAsmDeviceList + OracleRestart.Status.ConfigParams.DbAsmDeviceList = getdbAsmDeviceList(OracleRestart, oracleRestart, oraRestartSpex, kClient, kubeConfig, logger, kubeClient) + + } else if state == string(oraclerestartdb.OracleRestartStatefulSetNotFound) { + neworacleRestart := delOracleRestartNodestatus(OracleRestart, oraRestartSpex.Name+"-0") + OracleRestart.Status.OracleRestartNodes = neworacleRestart + OracleRestart.Status.ReleaseUpdate = "NOTAVAILABLE" + + } else if state == string(oraclerestartdb.PodNotFound) || state == string(oraclerestartdb.PodNotReadyState) || state == string(oraclerestartdb.PodFailureState) { + orestartNodeDetails.ClusterState = "NOTAVAILABLE" + orestartNodeDetails.PodState = state + // orestartNodeDetails.VipDetails = getVipDetails(OracleRestart, oracleRestart, oraRestartSpex, kClient) + orestartNodeDetails.InstanceState = "NOTAVAILABLE" + orestartNodeDetails.PvcName = strMap + OracleRestart.Status.ReleaseUpdate = "NOTAVAILABLE" + } +} + +func addOracleRestartNodestatus(instance *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, oracleRestart *oraclerestartdb.OracleRestartNodestatus, oraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, idx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger) { + + var racState string + podName := oraRestartSpex.Name + "-0" + idx, status := contains(instance, oracleRestart) + if status { + // racstate need to be read before overwriting the instance.Status.OracleRestartNodes[idx] = oracleRestart + racState = instance.Status.OracleRestartNodes[idx].NodeDetails.State + instance.Status.OracleRestartNodes[idx] = oracleRestart + instState := instance.Status.OracleRestartNodes[idx].NodeDetails.InstanceState + if instState == "OPEN" { + instance.Status.OracleRestartNodes[idx].NodeDetails.State = "AVAILABLE" + } else { + if (racState == "PENDING") || (racState == "ADDNODE") || (racState == "PROVISIONING") || (racState == "FAILED") || (racState == "UPDATE") { + instance.Status.OracleRestartNodes[idx].NodeDetails.State = getOracleRestartInstStateFile(podName, instance, 0, kubeClient, kubeConfig, logger) + } else { + instance.Status.OracleRestartNodes[idx].NodeDetails.State = "PENDING" + } + + } + + } else { + instance.Status.OracleRestartNodes = append(instance.Status.OracleRestartNodes, oracleRestart) + instance.Status.OracleRestartNodes[idx].NodeDetails.State = "PENDING" + } + +} + +func UpdateOracleRestartInstState(instance *oraclerestartdb.OracleRestart, podName string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger) { + + // if len(instance.Status.OracleRestartNodes) > 0 { + // for _, v := range instance.Status.OracleRestartNodes { + // if v.NodeDetails. + // } + //} + +} + +func UpdateoraclerestartdbTopologyState(instance *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, podName string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger) { + OracleRestart := &oraclerestartdb.OracleRestart{} + if len(instance.Status.OracleRestartNodes) > 0 { + state := map[string]struct{}{} + for _, v := range instance.Status.OracleRestartNodes { + inst_state := strings.ToLower(strings.TrimSpace(v.NodeDetails.State)) + state[inst_state] = struct{}{} + } + if len(state) == 0 { + OracleRestart.Status.State = string(oraclerestartdb.OracleRestartPendingState) + } else if _, curr_state := state["failed"]; curr_state { + OracleRestart.Status.State = string(oraclerestartdb.OracleRestartFailedState) + } else if _, curr_state := state["pending"]; curr_state { + OracleRestart.Status.State = string(oraclerestartdb.OracleRestartPendingState) + } else if _, curr_state := state["provisioning"]; curr_state { + OracleRestart.Status.State = string(oraclerestartdb.OracleRestartProvisionState) + } else if _, curr_state := state["update"]; curr_state { + OracleRestart.Status.State = string(oraclerestartdb.OracleRestartUpdateState) + } else if _, curr_state := state["addnode"]; curr_state { + OracleRestart.Status.State = string(oraclerestartdb.OracleRestartAddInstState) + } else if _, curr_state := state["podavailable"]; curr_state { + OracleRestart.Status.State = string(oraclerestartdb.OracleRestartPodAvailableState) + } else { + OracleRestart.Status.State = string(oraclerestartdb.OracleRestartAvailableState) + } + instance.Status.State = OracleRestart.Status.State + } + +} + +func UpdateoraclerestartdbStatusData(OracleRestart *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, podNames []string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, nodeDetails map[string]*corev1.Node, +) { + //mode := GetDbOpenMode(instance.Spec.Shard[0].Name+"-0", instance, kubeClient, kubeConfig, logger) + podName := podNames[len(podNames)-1] + OracleRestart.Status.DbState = getDbState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + OracleRestart.Status.Role = getDbRole(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + OracleRestart.Status.ReleaseUpdate = getDBVersion(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + OracleRestart.Status.ConnectString = getConnStr(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + OracleRestart.Status.PdbConnectString = getPdbConnStr(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + OracleRestart.Status.ClientEtcHost = getClientEtcHost(podNames, OracleRestart, 0, kubeClient, kubeConfig, logger, nodeDetails) + OracleRestart.Status.DbSecret = OracleRestart.Spec.DbSecret + OracleRestart.Status.AsmDetails = getAsmInstState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + + UpdateoraclerestartdbServiceStatus(OracleRestart, ctx, req, podName, kubeClient, kubeConfig, logger) + UpdateoraclerestartdbTopologyState(OracleRestart, ctx, req, podName, kubeClient, kubeConfig, logger) +} + +func UpdateoraclerestartdbServiceStatus(instance *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, podName string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) { + //This function update the instance.Status.ServiceDetails states + if instance.Spec.ServiceDetails.Name != "" { + instance.Status.ServiceDetails.Name = instance.Spec.ServiceDetails.Name + instance.Status.ServiceDetails.SvcState = getSvcState(podName, instance, 0, kubeClient, kubeConfig, logger) + } +} + +func contains(instance *oraclerestartdb.OracleRestart, oracleRestart *oraclerestartdb.OracleRestartNodestatus) (int, bool) { + var index int + if len(instance.Status.OracleRestartNodes) > 0 { + for index, v := range instance.Status.OracleRestartNodes { + if v.Name == oracleRestart.Name { + return index, true + } + } + } + + return index, false +} + +// func getVipDetails(instance *oraclerestartdb.OracleRestart, oracleRestart *oraclerestartdb.OracleRestartNodestatus, oraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, rclient client.Client) map[string]string { +// strMap := make(map[string]string) +// _, err := CheckRacSvc(instance, "vip", oraRestartSpex, oraRestartSpex.VipSvcName, rclient) +// if err == nil { +// strMap["Name"] = oraRestartSpex.VipSvcName +// // See if service already exists and create if it doesn't +// } + +// return strMap + +// } + +func getcrsAsmDeviceList(instance *oraclerestartdb.OracleRestart, oracleRestart *oraclerestartdb.OracleRestartNodestatus, oraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, rclient client.Client, kubeConfig clientcmd.ClientConfig, logger logr.Logger, kubeClient kubernetes.Interface) string { + asmList := "" + var err error + if len(instance.Status.OracleRestartNodes) > 0 { + asmList, err = CheckAsmList(instance.Status.OracleRestartNodes[0].Name, instance, kubeClient, kubeConfig, logger) + if err != nil { + return "" + } + + } + + return asmList + +} +func getdbAsmDeviceList(instance *oraclerestartdb.OracleRestart, oracleRestart *oraclerestartdb.OracleRestartNodestatus, oraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, rclient client.Client, kubeConfig clientcmd.ClientConfig, logger logr.Logger, kubeClient kubernetes.Interface) string { + dbasmList := "" + var err error + if len(instance.Status.OracleRestartNodes) > 0 { + dbasmList, err = CheckDbAsmList(instance.Status.OracleRestartNodes[0].Name, instance, kubeClient, kubeConfig, logger) + if err != nil { + return "" + } + + } + + return dbasmList + +} + +func getMountedDevices(podName, namespace string, oracleRestart *oraclerestartdb.OracleRestartNodestatus, oraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, rclient client.Client, kubeConfig clientcmd.ClientConfig, logger logr.Logger, kubeClient kubernetes.Interface) []string { + var asmList []string + + // Get the pod associated with the RAC node + pod, err := kubeClient.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{}) + if err != nil { + logger.Error(err, "Failed to get pod for RAC node", "PodName", podName, "Namespace", namespace) + return nil + } + + // Loop through the volume devices or mounted volumes in the pod spec + for _, container := range pod.Spec.Containers { + for _, volumeDevice := range container.VolumeDevices { + // Append the device path to the asmList + asmList = append(asmList, volumeDevice.DevicePath) + } + } + + return asmList +} + +func delOracleRestartNodestatus(instance *oraclerestartdb.OracleRestart, name string) []*oraclerestartdb.OracleRestartNodestatus { + neworacleRestart := []*oraclerestartdb.OracleRestartNodestatus{} + if len(instance.Status.OracleRestartNodes) > 0 { + for _, value := range instance.Status.OracleRestartNodes { + if ((value.Name) != (name)) && (value != nil) { + neworacleRestart = append(neworacleRestart, value) + } + } + } + return neworacleRestart +} + +func getPvcDetails(instance *oraclerestartdb.OracleRestart, oracleRestart *oraclerestartdb.OracleRestartNodestatus, oraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, rclient client.Client) map[string]string { + strMap := make(map[string]string) + if len(oraRestartSpex.PvcName) > 0 { + strMap = oraRestartSpex.PvcName + } + + return strMap + +} diff --git a/commons/oraclerestart/utils/utils.go b/commons/oraclerestart/utils/utils.go new file mode 100644 index 00000000..c196b9cb --- /dev/null +++ b/commons/oraclerestart/utils/utils.go @@ -0,0 +1,179 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +import ( + "os" + "strconv" + "strings" + + corev1 "k8s.io/api/core/v1" +) + +// Constants for RAC StatefulSet & Volumes +const ( + OraImagePullPolicy = corev1.PullAlways + OrainitCmd1 = "set -ex;" + "touch /tmp/test_cmd1.txt" + OrainitCmd5 = "set -ex;" + "[[ `hostname` =~ -([0-9]+)$ ]] || exit 1 ;" + "ordinal=${BASH_REMATCH[1]};" + "cp /mnt/config-map/envfile /mnt/conf.d/; cat /mnt/conf.d/envfile | awk -v env_var=$ordinal -F '=' '{print \"export \" $1\"=\"$2 env_var }' > /tmp/test.env; mv /tmp/test.env /mnt/conf.d/envfile" + OraConfigMapMount = "/mnt/config-map" + OraEnvFileMount = "/mnt/conf.d" + OraSubDomain = "racnode" + OraEnvFile = "/etc/rac_env_vars" + OraRacSshSecretMount = "/mnt/.ssh" + OraGiRsp = "/mnt/gridrsp" + OraDbRsp = "/mnt/dbrsp" + OraEnvVars = "/etc/rac_env_vars" + OraNodeKey = "kubernetes.io/hostname" + OraOperatorKey = "In" + OraShm = "/dev/shm" + OraRacDbPwdFileSecretMount = "/mnt/.dbsecrets" + OraRacDbKeyFileSecretMount = "/mnt/.dbsecrets" + OraRacTdePwdFileSecretMount = "/mnt/.tdesecrets" + OraRacTdeKeyFileSecretMount = "/mnt/.tdesecrets" + OraStage = "/mnt/stage" + OraBootVol = "/boot" + OraSwLocation = "/u01" + OraScriptMount = "/opt/scripts/startup/scripts" + OraSSHPrivKey = "/mnt/.ssh/ssh-privkey" + OraSSHPubKey = "/mnt/.ssh/ssh-pubkey" + OraSwStageLocation = "/mnt/stage/software" + OraRuPatchStageLocation = "/mnt/stage/rupatch" + OraOPatchStageLocation = "/mnt/stage/opatch" + OraDBPort = 1521 + OraLsnrPort = 1522 + OraLocalOnsPort = 6200 + OraSSHPort = 22 + OraOemPort = 8080 + OraDBUser = "oracle" + OraGridUser = "grid" +) + +// Fixed Array Values + +var serviceCardinality = [...]string{"UNIFORM", "SINGLETON", "DUPLEX"} +var tafPolicy = [...]string{"NONE", "BASIC", "PRECONNECT"} +var serviceRole = [...]string{"PRIMARY", "PHYSICAL_STANDBY", "LOGICAL_STANDBY", "SNAPSHOT_STANDBY"} +var servicePolicy = [...]string{"AUTOMATIC", "MANUAL"} +var serviceResetState = [...]string{"NONE", "LEVEL1"} +var ServiceFailoverType = [...]string{"NONE", "SESSION", "SELECT", "TRANSACTION", "AUTO"} + +/// ====== Getter Function Begins here ======= /// + +func GetServiceCardinality() []string { + return serviceCardinality[:] +} + +func GetTafPolicy() []string { + return tafPolicy[:] +} + +func ServiceRole() []string { + return serviceRole[:] +} + +func GetServiceRole() []string { + return serviceRole[:] +} + +func GetServiceResetState() []string { + return serviceResetState[:] +} + +func GetServiceFailoverType() []string { + return ServiceFailoverType[:] +} + +/// ====== Getter Function Ends here ======= /// + +func CheckStringInList(str1 string, arr []string) bool { + + // iterate using the for loop + for i := 0; i < len(arr); i++ { + // check + if strings.ToLower(arr[i]) == strings.ToLower(str1) { + // return true + return true + } + } + return false +} + +func CheckStatusFlag(flagStr string) bool { + + if strings.ToLower(flagStr) == "force" { + return true + } + + isTrueFlag, err := strconv.ParseBool(flagStr) + if err != nil { + return false + } + return isTrueFlag +} + +func GetWatchNamespaces() map[string]bool { + // Fetching the allowed namespaces from env variables + var watchNamespaceEnvVar = "WATCH_NAMESPACE" + ns, _ := os.LookupEnv(watchNamespaceEnvVar) + values := strings.Split(strings.TrimSpace(ns), ",") + namespaces := make(map[string]bool) + // put slice values into map + for _, s := range values { + namespaces[s] = true + } + return namespaces +} + +func GetValue(variable string, subkey string) string { + + str2 := "" + + str1 := strings.Split(variable, ",") + for _, item := range str1 { + str2 := strings.Split(item, "=") + if strings.ToLower(str2[0]) == strings.ToLower(subkey) { + return str2[1] + } + } + return str2 +} + +func GetDBUser() string { + return OraDBUser +} diff --git a/config/crd/bases/database.oracle.com_oraclerestartnews.yaml b/config/crd/bases/database.oracle.com_oraclerestartnews.yaml new file mode 100644 index 00000000..3aece252 --- /dev/null +++ b/config/crd/bases/database.oracle.com_oraclerestartnews.yaml @@ -0,0 +1,1468 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: oraclerestartnews.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestartNew + listKind: OracleRestartNewList + plural: oraclerestartnews + singular: oraclerestartnew + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.configParams.dbName + name: DbName + type: string + - jsonPath: .status.dbState + name: DbState + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + type: string + - jsonPath: .status.state + name: State + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + asmStorageDetails: + properties: + autoUpdate: + type: string + disksBySize: + items: + properties: + diskNames: + items: + type: string + type: array + storageSizeInGb: + type: integer + type: object + type: array + workerNodes: + items: + type: string + type: array + type: object + configParams: + properties: + cpuCount: + type: integer + crsAsmDeviceList: + type: string + crsAsmDiskDg: + type: string + crsAsmDiskDgRedundancy: + type: string + dbAsmDeviceList: + type: string + dbAsmDiskDgRedundancy: + type: string + dbBase: + type: string + dbCharSet: + type: string + dbConfigType: + type: string + dbDataFileDestDg: + type: string + dbHome: + type: string + dbName: + type: string + dbRecoveryFileDest: + type: string + dbRecoveryFileDestSize: + type: string + dbRedoFileSize: + type: string + dbResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + dbStorageType: + type: string + dbSwZipFile: + type: string + dbType: + type: string + dbUniqueName: + type: string + enableArchiveLog: + type: string + gridBase: + type: string + gridHome: + type: string + gridResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + gridSwZipFile: + type: string + hostSwStageLocation: + type: string + inventory: + type: string + opType: + type: string + pdbName: + type: string + pgaSize: + type: string + processes: + type: integer + recoAsmDeviceList: + type: string + recoAsmDiskDgRedudancy: + type: string + redoAsmDeviceList: + type: string + redoAsmDiskDgRedundancy: + type: string + sgaSize: + type: string + stagingSoftwareLocation: + type: string + swMountLocation: + type: string + type: object + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + externalSvcType: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecret: + type: string + instDetails: + items: + properties: + envFile: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + hostSwLocation: + type: string + isDelete: + type: string + isForceDelete: + type: string + isKeepPVC: + type: string + label: + type: string + lsnrLocalPort: + format: int32 + type: integer + lsnrTargetPort: + format: int32 + type: integer + name: + type: string + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + required: + - name + - svcType + type: object + type: array + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + workerNode: + items: + type: string + type: array + required: + - name + type: object + type: array + isDebug: + type: string + isDeleteOraPvc: + type: string + isDeleteTopology: + type: string + isFailed: + type: boolean + isManual: + type: boolean + nfsStorageDetails: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + scriptsGetCmd: + type: string + scriptsLocation: + type: string + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceDetails: + properties: + available: + items: + type: string + type: array + cardinality: + type: string + clbGoal: + type: string + commitOutComeFastPath: + type: string + commitOutcome: + type: string + drainTimeOut: + type: integer + dtp: + type: string + edition: + type: string + failBack: + type: string + failOverDelay: + type: integer + failOverRestore: + type: string + failOverRetry: + type: integer + failOverType: + type: string + name: + type: string + notification: + type: string + pdb: + type: string + preferred: + items: + type: string + type: array + retenion: + type: integer + rlbGoal: + type: string + role: + type: string + sessionState: + type: string + stopOption: + type: string + svcState: + type: string + tafPolicy: + type: string + required: + - name + type: object + sshKeySecret: + properties: + keyMountLocation: + type: string + name: + type: string + privKeySecretName: + type: string + pubKeySecretName: + type: string + required: + - name + type: object + storageClass: + type: string + storageSizeInGB: + type: integer + tdeWalletSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: + type: string + required: + - instDetails + - securityContext + type: object + status: + properties: + DbName: + type: string + OracleRestartNodes: + items: + properties: + name: + type: string + nodeDetails: + properties: + InstanceState: + type: string + PodState: + type: string + clusterState: + type: string + isDelete: + type: string + mountedDevices: + items: + type: string + type: array + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + required: + - name + - svcType + type: object + type: array + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + state: + type: string + workerNode: + type: string + type: object + type: object + type: array + asmDetails: + properties: + diskgroup: + items: + properties: + disks: + items: + type: string + type: array + name: + type: string + redundancy: + type: string + type: object + type: array + type: object + clientEtcHost: + items: + type: string + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + configParams: + properties: + cpuCount: + type: integer + crsAsmDeviceList: + type: string + crsAsmDiskDg: + type: string + crsAsmDiskDgRedundancy: + type: string + dbAsmDeviceList: + type: string + dbAsmDiskDgRedundancy: + type: string + dbBase: + type: string + dbCharSet: + type: string + dbConfigType: + type: string + dbDataFileDestDg: + type: string + dbHome: + type: string + dbName: + type: string + dbRecoveryFileDest: + type: string + dbRecoveryFileDestSize: + type: string + dbRedoFileSize: + type: string + dbResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + dbStorageType: + type: string + dbSwZipFile: + type: string + dbType: + type: string + dbUniqueName: + type: string + enableArchiveLog: + type: string + gridBase: + type: string + gridHome: + type: string + gridResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + gridSwZipFile: + type: string + hostSwStageLocation: + type: string + inventory: + type: string + opType: + type: string + pdbName: + type: string + pgaSize: + type: string + processes: + type: integer + recoAsmDeviceList: + type: string + recoAsmDiskDgRedudancy: + type: string + redoAsmDeviceList: + type: string + redoAsmDiskDgRedundancy: + type: string + sgaSize: + type: string + stagingSoftwareLocation: + type: string + swMountLocation: + type: string + type: object + connectString: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + dbState: + type: string + externalConnectString: + type: string + externalSvcType: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecret: + type: string + instDetails: + items: + properties: + envFile: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + hostSwLocation: + type: string + isDelete: + type: string + isForceDelete: + type: string + isKeepPVC: + type: string + label: + type: string + lsnrLocalPort: + format: int32 + type: integer + lsnrTargetPort: + format: int32 + type: integer + name: + type: string + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + required: + - name + - svcType + type: object + type: array + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + workerNode: + items: + type: string + type: array + required: + - name + type: object + type: array + installNode: + type: string + isDebug: + type: string + isDeleteOraPvc: + type: string + isDeleteTopology: + type: string + nfsStorageDetails: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + oldSpec: + type: string + pdbConnectString: + type: string + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + releaseUpdate: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + role: + type: string + scriptsGetCmd: + type: string + scriptsLocation: + type: string + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceDetails: + properties: + available: + items: + type: string + type: array + cardinality: + type: string + clbGoal: + type: string + commitOutComeFastPath: + type: string + commitOutcome: + type: string + drainTimeOut: + type: integer + dtp: + type: string + edition: + type: string + failBack: + type: string + failOverDelay: + type: integer + failOverRestore: + type: string + failOverRetry: + type: integer + failOverType: + type: string + name: + type: string + notification: + type: string + pdb: + type: string + preferred: + items: + type: string + type: array + retenion: + type: integer + rlbGoal: + type: string + role: + type: string + sessionState: + type: string + stopOption: + type: string + svcState: + type: string + tafPolicy: + type: string + required: + - name + type: object + sshKeySecret: + properties: + keyMountLocation: + type: string + name: + type: string + privKeySecretName: + type: string + pubKeySecretName: + type: string + required: + - name + type: object + state: + type: string + storageClass: + type: string + storageSizeInGB: + type: integer + tdeWalletSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml new file mode 100644 index 00000000..3a213643 --- /dev/null +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -0,0 +1,1419 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: oraclerestarts.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestart + listKind: OracleRestartList + plural: oraclerestarts + singular: oraclerestart + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.configParams.dbName + name: DbName + type: string + - jsonPath: .status.dbState + name: DbState + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + type: string + - jsonPath: .status.state + name: State + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + asmStorageDetails: + properties: + autoUpdate: + type: string + disksBySize: + items: + properties: + diskNames: + items: + type: string + type: array + storageSizeInGb: + type: integer + type: object + type: array + type: object + configParams: + properties: + cpuCount: + type: integer + crsAsmDeviceList: + type: string + crsAsmDiskDg: + type: string + crsAsmDiskDgRedundancy: + type: string + dbAsmDeviceList: + type: string + dbAsmDiskDgRedundancy: + type: string + dbBase: + type: string + dbCharSet: + type: string + dbConfigType: + type: string + dbDataFileDestDg: + type: string + dbHome: + type: string + dbName: + type: string + dbRecoveryFileDest: + type: string + dbRecoveryFileDestSize: + type: string + dbRedoFileSize: + type: string + dbResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + dbStorageType: + type: string + dbSwZipFile: + type: string + dbType: + type: string + dbUniqueName: + type: string + enableArchiveLog: + type: string + gridBase: + type: string + gridHome: + type: string + gridResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + gridSwZipFile: + type: string + hostSwStageLocation: + type: string + inventory: + type: string + oPatchLocation: + type: string + oPatchSwZipFile: + type: string + opType: + type: string + pdbName: + type: string + pgaSize: + type: string + processes: + type: integer + recoAsmDeviceList: + type: string + recoAsmDiskDgRedudancy: + type: string + redoAsmDeviceList: + type: string + redoAsmDiskDgRedundancy: + type: string + ruFolderName: + type: string + ruPatchLocation: + type: string + sgaSize: + type: string + stagingSoftwareLocation: + type: string + swMountLocation: + type: string + type: object + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + externalSvcType: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecret: + type: string + instDetails: + properties: + envFile: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + hostSwLocation: + type: string + isDelete: + type: string + isForceDelete: + type: string + isKeepPVC: + type: string + label: + type: string + name: + type: string + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + svcType: + type: string + type: object + type: array + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + workerNode: + items: + type: string + type: array + required: + - name + type: object + isDebug: + type: string + isDeleteOraPvc: + type: string + isDeleteTopology: + type: string + isFailed: + type: boolean + isManual: + type: boolean + nfsStorageDetails: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + scriptsGetCmd: + type: string + scriptsLocation: + type: string + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccountName: + type: string + serviceDetails: + properties: + available: + items: + type: string + type: array + cardinality: + type: string + clbGoal: + type: string + commitOutComeFastPath: + type: string + commitOutcome: + type: string + drainTimeOut: + type: integer + dtp: + type: string + edition: + type: string + failBack: + type: string + failOverDelay: + type: integer + failOverRestore: + type: string + failOverRetry: + type: integer + failOverType: + type: string + name: + type: string + notification: + type: string + pdb: + type: string + preferred: + items: + type: string + type: array + retenion: + type: integer + rlbGoal: + type: string + role: + type: string + sessionState: + type: string + stopOption: + type: string + svcState: + type: string + tafPolicy: + type: string + required: + - name + type: object + sshKeySecret: + properties: + keyMountLocation: + type: string + name: + type: string + privKeySecretName: + type: string + pubKeySecretName: + type: string + required: + - name + type: object + storageClass: + type: string + storageSizeInGB: + type: integer + tdeWalletSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: + type: string + required: + - instDetails + - securityContext + type: object + status: + properties: + DbName: + type: string + OracleRestartNodes: + items: + properties: + name: + type: string + nodeDetails: + properties: + InstanceState: + type: string + PodState: + type: string + clusterState: + type: string + isDelete: + type: string + mountedDevices: + items: + type: string + type: array + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + svcType: + type: string + type: object + type: array + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + state: + type: string + workerNode: + type: string + type: object + type: object + type: array + asmDetails: + properties: + diskgroup: + items: + properties: + disks: + items: + type: string + type: array + name: + type: string + redundancy: + type: string + type: object + type: array + type: object + clientEtcHost: + items: + type: string + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + configParams: + properties: + cpuCount: + type: integer + crsAsmDeviceList: + type: string + crsAsmDiskDg: + type: string + crsAsmDiskDgRedundancy: + type: string + dbAsmDeviceList: + type: string + dbAsmDiskDgRedundancy: + type: string + dbBase: + type: string + dbCharSet: + type: string + dbConfigType: + type: string + dbDataFileDestDg: + type: string + dbHome: + type: string + dbName: + type: string + dbRecoveryFileDest: + type: string + dbRecoveryFileDestSize: + type: string + dbRedoFileSize: + type: string + dbResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + dbStorageType: + type: string + dbSwZipFile: + type: string + dbType: + type: string + dbUniqueName: + type: string + enableArchiveLog: + type: string + gridBase: + type: string + gridHome: + type: string + gridResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + gridSwZipFile: + type: string + hostSwStageLocation: + type: string + inventory: + type: string + oPatchLocation: + type: string + oPatchSwZipFile: + type: string + opType: + type: string + pdbName: + type: string + pgaSize: + type: string + processes: + type: integer + recoAsmDeviceList: + type: string + recoAsmDiskDgRedudancy: + type: string + redoAsmDeviceList: + type: string + redoAsmDiskDgRedundancy: + type: string + ruFolderName: + type: string + ruPatchLocation: + type: string + sgaSize: + type: string + stagingSoftwareLocation: + type: string + swMountLocation: + type: string + type: object + connectString: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + dbState: + type: string + externalSvcType: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecret: + type: string + instDetails: + properties: + envFile: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + hostSwLocation: + type: string + isDelete: + type: string + isForceDelete: + type: string + isKeepPVC: + type: string + label: + type: string + name: + type: string + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + svcType: + type: string + type: object + type: array + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + workerNode: + items: + type: string + type: array + required: + - name + type: object + installNode: + type: string + isDebug: + type: string + isDeleteOraPvc: + type: string + isDeleteTopology: + type: string + nfsStorageDetails: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + oldSpec: + type: string + pdbConnectString: + type: string + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + releaseUpdate: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + role: + type: string + scriptsGetCmd: + type: string + scriptsLocation: + type: string + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceDetails: + properties: + available: + items: + type: string + type: array + cardinality: + type: string + clbGoal: + type: string + commitOutComeFastPath: + type: string + commitOutcome: + type: string + drainTimeOut: + type: integer + dtp: + type: string + edition: + type: string + failBack: + type: string + failOverDelay: + type: integer + failOverRestore: + type: string + failOverRetry: + type: integer + failOverType: + type: string + name: + type: string + notification: + type: string + pdb: + type: string + preferred: + items: + type: string + type: array + retenion: + type: integer + rlbGoal: + type: string + role: + type: string + sessionState: + type: string + stopOption: + type: string + svcState: + type: string + tafPolicy: + type: string + required: + - name + type: object + sshKeySecret: + properties: + keyMountLocation: + type: string + name: + type: string + privKeySecretName: + type: string + pubKeySecretName: + type: string + required: + - name + type: object + state: + type: string + storageClass: + type: string + storageSizeInGB: + type: integer + tdeWalletSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/database.oracle.com_oraclerestartservices.yaml b/config/crd/bases/database.oracle.com_oraclerestartservices.yaml new file mode 100644 index 00000000..3f798336 --- /dev/null +++ b/config/crd/bases/database.oracle.com_oraclerestartservices.yaml @@ -0,0 +1,1468 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: oraclerestartservices.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestartService + listKind: OracleRestartServiceList + plural: oraclerestartservices + singular: oraclerestartservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.configParams.dbName + name: DbName + type: string + - jsonPath: .status.dbState + name: DbState + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + type: string + - jsonPath: .status.state + name: State + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + asmStorageDetails: + properties: + autoUpdate: + type: string + disksBySize: + items: + properties: + diskNames: + items: + type: string + type: array + storageSizeInGb: + type: integer + type: object + type: array + workerNodes: + items: + type: string + type: array + type: object + configParams: + properties: + cpuCount: + type: integer + crsAsmDeviceList: + type: string + crsAsmDiskDg: + type: string + crsAsmDiskDgRedundancy: + type: string + dbAsmDeviceList: + type: string + dbAsmDiskDgRedundancy: + type: string + dbBase: + type: string + dbCharSet: + type: string + dbConfigType: + type: string + dbDataFileDestDg: + type: string + dbHome: + type: string + dbName: + type: string + dbRecoveryFileDest: + type: string + dbRecoveryFileDestSize: + type: string + dbRedoFileSize: + type: string + dbResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + dbStorageType: + type: string + dbSwZipFile: + type: string + dbType: + type: string + dbUniqueName: + type: string + enableArchiveLog: + type: string + gridBase: + type: string + gridHome: + type: string + gridResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + gridSwZipFile: + type: string + hostSwStageLocation: + type: string + inventory: + type: string + opType: + type: string + pdbName: + type: string + pgaSize: + type: string + processes: + type: integer + recoAsmDeviceList: + type: string + recoAsmDiskDgRedudancy: + type: string + redoAsmDeviceList: + type: string + redoAsmDiskDgRedundancy: + type: string + sgaSize: + type: string + stagingSoftwareLocation: + type: string + swMountLocation: + type: string + type: object + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + externalSvcType: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecret: + type: string + instDetails: + items: + properties: + envFile: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + hostSwLocation: + type: string + isDelete: + type: string + isForceDelete: + type: string + isKeepPVC: + type: string + label: + type: string + lsnrLocalPort: + format: int32 + type: integer + lsnrTargetPort: + format: int32 + type: integer + name: + type: string + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + required: + - name + - svcType + type: object + type: array + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + workerNode: + items: + type: string + type: array + required: + - name + type: object + type: array + isDebug: + type: string + isDeleteOraPvc: + type: string + isDeleteTopology: + type: string + isFailed: + type: boolean + isManual: + type: boolean + nfsStorageDetails: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + scriptsGetCmd: + type: string + scriptsLocation: + type: string + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceDetails: + properties: + available: + items: + type: string + type: array + cardinality: + type: string + clbGoal: + type: string + commitOutComeFastPath: + type: string + commitOutcome: + type: string + drainTimeOut: + type: integer + dtp: + type: string + edition: + type: string + failBack: + type: string + failOverDelay: + type: integer + failOverRestore: + type: string + failOverRetry: + type: integer + failOverType: + type: string + name: + type: string + notification: + type: string + pdb: + type: string + preferred: + items: + type: string + type: array + retenion: + type: integer + rlbGoal: + type: string + role: + type: string + sessionState: + type: string + stopOption: + type: string + svcState: + type: string + tafPolicy: + type: string + required: + - name + type: object + sshKeySecret: + properties: + keyMountLocation: + type: string + name: + type: string + privKeySecretName: + type: string + pubKeySecretName: + type: string + required: + - name + type: object + storageClass: + type: string + storageSizeInGB: + type: integer + tdeWalletSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: + type: string + required: + - instDetails + - securityContext + type: object + status: + properties: + DbName: + type: string + OracleRestartNodes: + items: + properties: + name: + type: string + nodeDetails: + properties: + InstanceState: + type: string + PodState: + type: string + clusterState: + type: string + isDelete: + type: string + mountedDevices: + items: + type: string + type: array + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + required: + - name + - svcType + type: object + type: array + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + state: + type: string + workerNode: + type: string + type: object + type: object + type: array + asmDetails: + properties: + diskgroup: + items: + properties: + disks: + items: + type: string + type: array + name: + type: string + redundancy: + type: string + type: object + type: array + type: object + clientEtcHost: + items: + type: string + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + configParams: + properties: + cpuCount: + type: integer + crsAsmDeviceList: + type: string + crsAsmDiskDg: + type: string + crsAsmDiskDgRedundancy: + type: string + dbAsmDeviceList: + type: string + dbAsmDiskDgRedundancy: + type: string + dbBase: + type: string + dbCharSet: + type: string + dbConfigType: + type: string + dbDataFileDestDg: + type: string + dbHome: + type: string + dbName: + type: string + dbRecoveryFileDest: + type: string + dbRecoveryFileDestSize: + type: string + dbRedoFileSize: + type: string + dbResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + dbStorageType: + type: string + dbSwZipFile: + type: string + dbType: + type: string + dbUniqueName: + type: string + enableArchiveLog: + type: string + gridBase: + type: string + gridHome: + type: string + gridResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + gridSwZipFile: + type: string + hostSwStageLocation: + type: string + inventory: + type: string + opType: + type: string + pdbName: + type: string + pgaSize: + type: string + processes: + type: integer + recoAsmDeviceList: + type: string + recoAsmDiskDgRedudancy: + type: string + redoAsmDeviceList: + type: string + redoAsmDiskDgRedundancy: + type: string + sgaSize: + type: string + stagingSoftwareLocation: + type: string + swMountLocation: + type: string + type: object + connectString: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + dbState: + type: string + externalConnectString: + type: string + externalSvcType: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecret: + type: string + instDetails: + items: + properties: + envFile: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + hostSwLocation: + type: string + isDelete: + type: string + isForceDelete: + type: string + isKeepPVC: + type: string + label: + type: string + lsnrLocalPort: + format: int32 + type: integer + lsnrTargetPort: + format: int32 + type: integer + name: + type: string + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + required: + - name + - svcType + type: object + type: array + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + workerNode: + items: + type: string + type: array + required: + - name + type: object + type: array + installNode: + type: string + isDebug: + type: string + isDeleteOraPvc: + type: string + isDeleteTopology: + type: string + nfsStorageDetails: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + oldSpec: + type: string + pdbConnectString: + type: string + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + releaseUpdate: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + role: + type: string + scriptsGetCmd: + type: string + scriptsLocation: + type: string + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceDetails: + properties: + available: + items: + type: string + type: array + cardinality: + type: string + clbGoal: + type: string + commitOutComeFastPath: + type: string + commitOutcome: + type: string + drainTimeOut: + type: integer + dtp: + type: string + edition: + type: string + failBack: + type: string + failOverDelay: + type: integer + failOverRestore: + type: string + failOverRetry: + type: integer + failOverType: + type: string + name: + type: string + notification: + type: string + pdb: + type: string + preferred: + items: + type: string + type: array + retenion: + type: integer + rlbGoal: + type: string + role: + type: string + sessionState: + type: string + stopOption: + type: string + svcState: + type: string + tafPolicy: + type: string + required: + - name + type: object + sshKeySecret: + properties: + keyMountLocation: + type: string + name: + type: string + privKeySecretName: + type: string + pubKeySecretName: + type: string + required: + - name + type: object + state: + type: string + storageClass: + type: string + storageSizeInGB: + type: integer + tdeWalletSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 73e00151..d73e069d 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -21,6 +21,7 @@ resources: - bases/database.oracle.com_lrests.yaml - bases/database.oracle.com_lrpdbs.yaml - bases/database.oracle.com_ordssrvs.yaml +- bases/database.oracle.com_oraclerestarts.yaml # +kubebuilder:scaffold:crdkustomizeresource patches: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 7a52fb17..2726acb4 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: container-registry.oracle.com/database/operator - newTag: 1.2.0 + newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database + newTag: restart-operator-sa diff --git a/config/rbac/database_oraclerestart_admin_role.yaml b/config/rbac/database_oraclerestart_admin_role.yaml new file mode 100644 index 00000000..a07ef7e9 --- /dev/null +++ b/config/rbac/database_oraclerestart_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project oracle-database-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over database.oracle.com. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: database-oraclerestart-admin-role +rules: +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts + verbs: + - '*' +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts/status + verbs: + - get diff --git a/config/rbac/database_oraclerestart_editor_role.yaml b/config/rbac/database_oraclerestart_editor_role.yaml new file mode 100644 index 00000000..ac68b530 --- /dev/null +++ b/config/rbac/database_oraclerestart_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project oracle-database-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the database.oracle.com. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: database-oraclerestart-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts/status + verbs: + - get diff --git a/config/rbac/database_oraclerestart_viewer_role.yaml b/config/rbac/database_oraclerestart_viewer_role.yaml new file mode 100644 index 00000000..fba2f80b --- /dev/null +++ b/config/rbac/database_oraclerestart_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project oracle-database-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to database.oracle.com resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: database-oraclerestart-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts/status + verbs: + - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 7a20231c..14a2bc9f 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -14,3 +14,11 @@ resources: - auth_proxy_role.yaml - auth_proxy_role_binding.yaml - auth_proxy_client_clusterrole.yaml +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the {{ .ProjectName }} itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- database_oraclerestart_admin_role.yaml +- database_oraclerestart_editor_role.yaml +- database_oraclerestart_viewer_role.yaml + diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 3a12386c..a87b3899 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -10,9 +10,11 @@ rules: - configmaps - containers - deployments + - endpoints - events - namespaces - persistentvolumeclaims + - persistentvolumes - pods - pods/exec - pods/log @@ -39,14 +41,6 @@ rules: - get - patch - update -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch - apiGroups: - "" resources: @@ -109,6 +103,7 @@ rules: - events - lrests - lrpdbs + - oraclerestarts - oraclerestdataservices - ordssrvs - pdbs @@ -133,6 +128,7 @@ rules: - dbcssystems/status - lrests/status - lrpdbs/status + - oraclerestarts/status - oraclerestdataservices/status - ordssrvs/status - pdbs/status @@ -177,6 +173,7 @@ rules: resources: - dbcssystems/finalizers - lrpdbs/finalizers + - oraclerestarts/finalizers - pdbs/finalizers - shardingdatabases/finalizers verbs: diff --git a/config/samples/database_v4_oraclerestart.yaml b/config/samples/database_v4_oraclerestart.yaml new file mode 100644 index 00000000..de72184a --- /dev/null +++ b/config/samples/database_v4_oraclerestart.yaml @@ -0,0 +1,9 @@ +apiVersion: database.oracle.com/v4 +kind: OracleRestart +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: oraclerestart-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 956027a3..f8d3cd70 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -14,4 +14,5 @@ resources: - observability/databaseobserver.yaml - sharding/sharding_v1alpha1_provshard.yaml - sidb/singleinstancedatabase_create.yaml +- database_v4_oraclerestart.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 0166160d..09317d60 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -107,6 +107,26 @@ webhooks: resources: - lrpdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-database-oracle-com-v4-oraclerestart + failurePolicy: Fail + name: moraclerestart.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - oraclerestarts + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -460,6 +480,27 @@ webhooks: resources: - lrpdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v4-oraclerestart + failurePolicy: Fail + name: voraclerestart.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - oraclerestarts + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go new file mode 100644 index 00000000..d383c56d --- /dev/null +++ b/controllers/database/oraclerestart_controller.go @@ -0,0 +1,3362 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package controllers + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "time" + + "github.com/go-logr/logr" + oraclerestartdb "github.com/oracle/oracle-database-operator/apis/database/v4" + oraclerestartcommon "github.com/oracle/oracle-database-operator/commons/oraclerestart" + utils "github.com/oracle/oracle-database-operator/commons/oraclerestart/utils" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// OracleRestartReconciler reconciles a OracleRestart object +type OracleRestartReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + Config *rest.Config + kubeClient kubernetes.Interface + kubeConfig clientcmd.ClientConfig + Recorder record.EventRecorder +} + +const oracleRestartFinalizer = "database.oracle.com/oraclerestartfinalizer" + +//+kubebuilder:rbac:groups="database.oracle.com",resources=oraclerestarts,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="database.oracle.com",resources=oraclerestarts/status,verbs=get;update;patch +//+kubebuilder:rbac:groups="database.oracle.com",resources=oraclerestarts/finalizers,verbs=get;create;update;patch;delete +//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;secrets;endpoints;services;events;configmaps;persistentvolumes;persistentvolumeclaims;namespaces,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="apps",resources=statefulsets,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups='',resources=statefulsets/finalizers,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the OracleRestart object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.6.4/pkg/reconcile +func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + //ctx := context.Background() + _ = r.Log.WithValues("oraclerestart", req.NamespacedName) + r.Log.Info("Reconcile requested") + var result ctrl.Result + var err error + completed := false + blocked := false + // var svcType string + var nilErr error = nil + var oracleRestartInst oraclerestartdb.OracleRestartInstDetailSpec + resultNq := ctrl.Result{Requeue: false} + resultQ := ctrl.Result{Requeue: true, RequeueAfter: 60 * time.Second} + + oracleRestart := &oraclerestartdb.OracleRestart{} + configMapData := make(map[string]string) + + // Execute for every reconcile + defer r.updateReconcileStatus(oracleRestart, ctx, req, &result, &err, &blocked, &completed) + + err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, oracleRestart) + if err != nil { + if apierrors.IsNotFound(err) { + r.Log.Info("Resource not found") + return requeueN, nil + } + r.Log.Error(err, err.Error()) + oracleRestart.Spec.IsFailed = true + return resultQ, err + } + oracleRestartInst = oracleRestart.Spec.InstDetails + + // Retrieve the old spec from annotations + oldSpec, err := r.GetOldSpec(oracleRestart) + if err != nil { + r.Log.Error(err, "Failed to update old spec annotation") + oracleRestart.Spec.IsFailed = true + return resultQ, nil + } + + // Initialize oracleRestart.Status if it's not already initialized + if oracleRestart.Status.ConfigParams == nil { + oracleRestart.Status.ConfigParams = &oraclerestartdb.InitParams{} + } + + // Initialize ConfigParams fields if they are not already initialized + if oracleRestart.Status.ConfigParams.DbHome == "" { + oracleRestart.Status.ConfigParams.DbHome = string(oraclerestartdb.OracleRestartFieldNotDefined) + } + if oracleRestart.Status.DbState == "" { + oracleRestart.Status.State = string(oraclerestartdb.OracleRestartPendingState) + oracleRestart.Status.DbState = string(oraclerestartdb.OracleRestartPendingState) + oracleRestart.Status.Role = string(oraclerestartdb.OracleRestartFieldNotDefined) + oracleRestart.Status.ConnectString = string(oraclerestartdb.OracleRestartFieldNotDefined) + oracleRestart.Status.PdbConnectString = string(oraclerestartdb.OracleRestartFieldNotDefined) + oracleRestart.Status.ReleaseUpdate = string(oraclerestartdb.OracleRestartFieldNotDefined) + oracleRestart.Status.ConfigParams.DbHome = string(oraclerestartdb.OracleRestartFieldNotDefined) + oracleRestart.Status.ConfigParams.GridHome = string(oraclerestartdb.OracleRestartFieldNotDefined) + oracleRestart.Status.ClientEtcHost = []string{string(oraclerestartdb.OracleRestartFieldNotDefined)} + r.Status().Update(ctx, oracleRestart) + } + + // Kube Client Config Setup + if r.kubeConfig == nil && r.kubeClient == nil { + r.kubeConfig, r.kubeClient, err = oraclerestartcommon.GetRacK8sClientConfig(r.Client) + if err != nil { + return ctrl.Result{}, err + } + } + + // Manage OracleRestart Deletion , if delete topology is called + err = r.manageOracleRestartDeletion(req, ctx, oracleRestart) + if err != nil { + result = resultNq + return result, err + } + + // cleanup RAC Instance + _, err = r.cleanupOracleRestartInstance(req, ctx, oracleRestart) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + return result, nilErr + } + // debugging + err = checkOracleRestartState(oracleRestart) + if err != nil { + result = resultQ + r.Log.Info("Oracle Restart object is in restricted state, returning back") + return result, nilErr + } + + // First Validate + err = r.validateSpex(oracleRestart, oldSpec, ctx) + if err != nil { + r.Log.Info("Spec validation failed") + result = resultQ + r.Log.Info(err.Error()) + return result, nilErr + } + + err = r.setDefaults(oracleRestart) + if err != nil { + // time.Sleep(30 * time.Second) + result = resultQ + r.Log.Info(err.Error()) + return result, nilErr + } + + // Update RAC ConfigParams + err = r.updateGiConfigParamStatus(oracleRestart) + if err != nil { + // time.Sleep(30 * time.Second) + result = resultQ + r.Log.Info(err.Error()) + return result, nilErr + } + + err = r.updateDbConfigParamStatus(oracleRestart) + if err != nil { + // time.Sleep(30 * time.Second) + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } + + if oracleRestart.Spec.ConfigParams != nil { + configMapData, err = r.generateConfigMap(oracleRestart) + if err != nil { + result = resultNq + return result, err + } + } + + result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildServiceDefForOracleRestart(oracleRestart, 0, oracleRestart.Spec.InstDetails, "local")) + if err != nil { + result = resultNq + return result, err + } + + if oracleRestartInst.NodePortSvc != nil { + for index := range oracleRestartInst.NodePortSvc { + result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildExternalServiceDefForOracleRestart(oracleRestart, int32(index), oracleRestart.Spec.InstDetails, "nodeport", "nodeport")) + if err != nil { + result = resultNq + return result, err + } + + } + } + + r.ensureAsmStorageStatus(oracleRestart) + isNewSetup := true + for _, diskgroup := range oracleRestart.Status.AsmDetails.Diskgroup { + if len(diskgroup.Disks) > 0 && oracleRestart.Status.AsmDetails.Diskgroup[0].Name != "Pending" { + isNewSetup = false + break + } + } + + isDiskChanged := false + addedAsmDisks := []string{} + removedAsmDisks := []string{} + if !isNewSetup { + if oldSpec != nil { // old spec required for comparison + // Check each RAC node for mounted devices + for index, _ := range oracleRestart.Status.OracleRestartNodes { + addedAsmDisks, removedAsmDisks = getAddedAndRemovedDisks(oracleRestart, oldSpec, index) + if len(addedAsmDisks) > 0 && len(removedAsmDisks) > 0 { + r.Log.Info("Detected Addition as well as Deletion, setup cannot run both together", "addedAsmDisks", addedAsmDisks, "removedAsmDisks", removedAsmDisks) + result = resultQ + return result, err + } + // You can now use the added and removed disks as needed + if len(addedAsmDisks) > 0 { + r.Log.Info("Detected Addition of ASM Disks:", "addedAsmDisks", addedAsmDisks) + } + + if len(removedAsmDisks) > 0 { + r.Log.Info("Detected Removal of ASM Disks:", "removedAsmDisks", removedAsmDisks) + } + if len(addedAsmDisks) > 0 || len(removedAsmDisks) > 0 { + + isDiskChanged = true + break // Exit loop once a difference is found + } + } + } + } + + var autoUpdate bool + // Initialize autoUpdate based on the AutoUpdate field in the specification + switch strings.ToLower(oracleRestart.Spec.AsmStorageDetails.AutoUpdate) { + case "false": + // If AutoUpdate is explicitly set to "false" by the user, set autoUpdate to false + autoUpdate = false + r.Log.Info("Initialized autoUpdate from provided specification", "autoUpdate", autoUpdate) + default: + // If AutoUpdate is not set or is set to any value other than "false", default to true + autoUpdate = true + r.Log.Info("Initialized autoUpdate as true (default)") + } + + // PV Creation + if isNewSetup || isDiskChanged { + if oracleRestart.Spec.AsmStorageDetails != nil { + for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for index, diskName := range diskBySize.DiskNames { + pvVolume := oraclerestartcommon.VolumePVForASM( + oracleRestart, + index, + diskName, + diskBySize.StorageSizeInGb, + oracleRestart.Spec.AsmStorageDetails, + r.Client, + ) + // if pvVolume == nil { + // r.Log.Info("VolumePVForASM returned nil for Dynamic Provisioning", "diskName", diskName, "index", index) + // continue // or return error + // } + _, result, err = r.createOrReplaceAsmPv(ctx, oracleRestart, pvVolume) + if err != nil { + result = resultNq + return result, err + } + } + } + } + } + + // PVC Creation + if isNewSetup || isDiskChanged { + if oracleRestart.Spec.AsmStorageDetails != nil { + for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for index, diskName := range diskBySize.DiskNames { + pvcVolume := oraclerestartcommon.VolumePVCForASM( + oracleRestart, + index, + diskName, + diskBySize.StorageSizeInGb, + oracleRestart.Spec.AsmStorageDetails, + r.Client, + ) + _, result, err = r.createOrReplaceAsmPvC(ctx, oracleRestart, pvcVolume) + if err != nil { + result = resultNq + return result, err + } + } + } + } + } + + index := 0 + isLast := true + oldState := oracleRestart.Status.State + if !utils.CheckStatusFlag(oracleRestart.Spec.InstDetails.IsDelete) { + switch { + case isNewSetup || !isDiskChanged: + cmName := oracleRestart.Spec.InstDetails.Name + oracleRestart.Name + "-cmap" + cm := oraclerestartcommon.ConfigMapSpecs(oracleRestart, configMapData, cmName) + result, err = r.createConfigMap(ctx, oracleRestart, cm) + if err != nil { + result = resultNq + return result, err + } + err = oraclerestartcommon.CreateServiceAccountIfNotExists(oracleRestart, r.Client) + if err != nil { + result = resultNq + return result, err + } + + oracleRestart.Spec.InstDetails.EnvFile = cmName + dep := oraclerestartcommon.BuildStatefulSetForOracleRestart(oracleRestart, oracleRestart.Spec.InstDetails, r.Client) + result, err = r.createOrReplaceSfs(ctx, req, oracleRestart, dep, index, isLast, oldState) + if err != nil { + result = resultNq + return result, err + } + + case isDiskChanged && !isNewSetup: + err = r.validateASMDisks(oracleRestart, ctx) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } + if ready, err := checkDaemonSetStatus(ctx, r, oracleRestart); err != nil || !ready { + r.Log.Info("Any of provided ASM Disks are invalid, pls check disk-check daemon set for logs. Fix the asm disk to the valid one and redeploy.") + err = r.cleanupDaemonSet(oracleRestart, ctx) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } + addedAsmDisksMap := make(map[string]bool) + for _, disk := range addedAsmDisks { + addedAsmDisksMap[disk] = true + } + for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for index, diskName := range diskBySize.DiskNames { + if _, ok := addedAsmDisksMap[diskName]; ok { + r.Log.Info("Found disk at index", "index", index) + + err = oraclerestartcommon.DelORestartPVC(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + if err != nil { + return resultQ, err + } + + err = oraclerestartcommon.DelORestartPv(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + if err != nil { + return resultQ, err + } + } + } + } + + if err = r.SetCurrentSpec(ctx, oracleRestart, req); err != nil { + r.Log.Error(err, "Failed to set current spec annotation") + oracleRestart.Spec.IsFailed = true + return resultQ, err + } + return resultQ, err + } else { + r.Log.Info("Provided ASM Disks are valid, proceeding further") + } + cmName := oracleRestart.Spec.InstDetails.Name + oracleRestart.Name + "-cmap" + configMapDataAutoUpdate, err := r.generateConfigMapAutoUpdate(ctx, oracleRestart, cmName) + if err != nil { + result = resultNq + return result, err + } + result, err = r.updateConfigMap(ctx, oracleRestart, configMapDataAutoUpdate, cmName) + if err != nil { + result = resultNq + return result, err + } + r.Log.Info("Config Map updated successfully with new asm details") + oracleRestart.Spec.InstDetails.EnvFile = cmName + result, err = r.createOrReplaceSfsAsm(ctx, req, oracleRestart, oraclerestartcommon.BuildStatefulSetForOracleRestart(oracleRestart, oracleRestart.Spec.InstDetails, r.Client), autoUpdate, index, isLast, oldSpec) + if err != nil { + result = resultNq + return result, err + } + } + } + err = r.cleanupDaemonSet(oracleRestart, ctx) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } + + completed = true + // // Update the current spec after successful reconciliation + if err = r.SetCurrentSpec(ctx, oracleRestart, req); err != nil { + r.Log.Error(err, "Failed to set current spec annotation") + oracleRestart.Spec.IsFailed = true + return resultQ, err + } + r.Log.Info("Reconcile completed. Requeuing....") + // uncomment this only to debugging null pointer exception + // r.updateReconcileStatus(OracleRestart, ctx, req, &result, &err, &blocked, &completed) + // time.Sleep(1 * time.Minute) + return resultQ, nil +} + +// Function to check the RAC topology state and return/dont proceed when matched. +func checkOracleRestartState(oracleRestart *oraclerestartdb.OracleRestart) error { + if oracleRestart.Status.State == string(oraclerestartdb.OracleRestartProvisionState) || + oracleRestart.Status.State == string(oraclerestartdb.OracleRestartUpdateState) || + oracleRestart.Status.State == string(oraclerestartdb.OracleRestartPodAvailableState) || + oracleRestart.Status.State == string(oraclerestartdb.OracleRestartAddInstState) || + oracleRestart.Status.State == string(oraclerestartdb.OracleRestartDeletingState) || + oracleRestart.Status.State == string(oraclerestartdb.OracleRestartFailedState) || + oracleRestart.Status.State == string(oraclerestartdb.OracleRestartManualState) || + oracleRestart.Spec.IsFailed || + oracleRestart.Spec.IsManual { + return errors.New(fmt.Sprintf("oracle restart database is in a restricted state: %s", oracleRestart.Status.State)) + } + return nil +} + +func (r *OracleRestartReconciler) generateConfigMapAutoUpdate(ctx context.Context, instance *oraclerestartdb.OracleRestart, cmName string) (map[string]string, error) { + // Fetch the existing ConfigMap + cm := &corev1.ConfigMap{} + err := r.Client.Get(ctx, types.NamespacedName{Name: cmName, Namespace: instance.Namespace}, cm) + if err != nil { + return nil, err + } + r.Log.Info("Updating existing configmap") + // Get the existing config map data + configMapData := cm.Data + envFileData := configMapData["envfile"] + envVars := make(map[string]string) + + // Parse the envfile into a map + lines := strings.Split(envFileData, "\r\n") + for _, line := range lines { + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 { + envVars[parts[0]] = parts[1] + } + } + + // Get latest ASM devices + asmDevices := oraclerestartcommon.GetAsmDevices(instance) + + // Update selective fields + if instance.Spec.ConfigParams.CrsAsmDeviceList != "" { + envVars["CRS_ASM_DEVICE_LIST"] = instance.Spec.ConfigParams.CrsAsmDeviceList + } else { + envVars["CRS_ASM_DEVICE_LIST"] = asmDevices + } + + if instance.Spec.ConfigParams.RecoAsmDeviceList != "" { + envVars["RECO_ASM_DEVICE_LIST"] = instance.Spec.ConfigParams.RecoAsmDeviceList + } else if instance.Status.ConfigParams != nil && instance.Status.ConfigParams.RecoAsmDeviceList != "" { + envVars["RECO_ASM_DEVICE_LIST"] = instance.Status.ConfigParams.RecoAsmDeviceList + } + + if instance.Spec.ConfigParams.RedoAsmDeviceList != "" { + envVars["REDO_ASM_DEVICE_LIST"] = instance.Spec.ConfigParams.RedoAsmDeviceList + } else if instance.Status.ConfigParams != nil && instance.Status.ConfigParams.RedoAsmDeviceList != "" { + envVars["REDO_ASM_DEVICE_LIST"] = instance.Status.ConfigParams.RedoAsmDeviceList + } + + // Convert the envVars map back to a single string + var updatedData []string + for key, value := range envVars { + updatedData = append(updatedData, fmt.Sprintf("%s=%s", key, value)) + } + configMapData["envfile"] = strings.Join(updatedData, "\r\n") + + return configMapData, nil +} + +func (r *OracleRestartReconciler) updateConfigMap(ctx context.Context, instance *oraclerestartdb.OracleRestart, configMapData map[string]string, cmName string) (ctrl.Result, error) { + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: cmName, + Namespace: instance.Namespace, + }, + Data: configMapData, + } + + err := r.Client.Update(ctx, cm) + if err != nil { + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +// ############################################################################# +// +// Update each reconcile condition/status +// +// ############################################################################# +func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, result *ctrl.Result, err *error, blocked *bool, completed *bool) { + const maxRetries = 5 + const retryDelay = 2 * time.Second + + // First update RAC topology + podNames, nodeDetails, err1 := r.updateOracleRestartInstTopologyStatus(oracleRestart, ctx, req) + + // Update RAC DB topology + if err1 == nil { + _ = r.updateoraclerestartdbTopologyStatus(oracleRestart, ctx, req, podNames, nodeDetails) + } else { + r.Log.Info("Error during RAC cluster update", "err1", err1) + } + + errMsg := func() string { + if *err != nil { + return (*err).Error() + } + return "no reconcile errors" + }() + var condition metav1.Condition + if *completed { + condition = metav1.Condition{ + Type: string(oraclerestartdb.CrdReconcileCompeleteState), + LastTransitionTime: metav1.Now(), + ObservedGeneration: oracleRestart.GetGeneration(), + Reason: string(oraclerestartdb.OracleRestartCrdReconcileCompleteReason), + Message: errMsg, + Status: metav1.ConditionTrue, + } + } else if *blocked { + condition = metav1.Condition{ + Type: string(oraclerestartdb.CrdReconcileWaitingState), + LastTransitionTime: metav1.Now(), + ObservedGeneration: oracleRestart.GetGeneration(), + Reason: string(oraclerestartdb.OracleRestartCrdReconcileWaitingReason), + Message: errMsg, + Status: metav1.ConditionTrue, + } + } else if result.Requeue { + condition = metav1.Condition{ + Type: string(oraclerestartdb.CrdReconcileQueuedState), + LastTransitionTime: metav1.Now(), + ObservedGeneration: oracleRestart.GetGeneration(), + Reason: string(oraclerestartdb.CrdReconcileQueuedReason), + Message: errMsg, + Status: metav1.ConditionTrue, + } + } else if err != nil && *err != nil { + condition = metav1.Condition{ + Type: string(oraclerestartdb.CrdReconcileErrorState), + LastTransitionTime: metav1.Now(), + ObservedGeneration: oracleRestart.GetGeneration(), + Reason: string(oraclerestartdb.CrdReconcileErrorReason), + Message: errMsg, + Status: metav1.ConditionTrue, + } + } else { + return + } + + if len(oracleRestart.Status.Conditions) > 0 { + meta.RemoveStatusCondition(&oracleRestart.Status.Conditions, condition.Type) + } + meta.SetStatusCondition(&oracleRestart.Status.Conditions, condition) + + if oracleRestart.Status.State == string(oraclerestartdb.OracleRestartPodAvailableState) && condition.Type == string(oraclerestartdb.CrdReconcileCompeleteState) { + r.Log.Info("All validations and updation are completed. Changing State to AVAILABLE") + oracleRestart.Status.State = string(oraclerestartdb.OracleRestartAvailableState) + } + for attempt := 0; attempt < maxRetries; attempt++ { + // Fetch the latest version of the object + latestInstance := &oraclerestartdb.OracleRestart{} + err := r.Client.Get(ctx, req.NamespacedName, latestInstance) + if err != nil { + r.Log.Error(err, "Failed to fetch the latest version of RAC instance, retrying...") + time.Sleep(retryDelay) + continue // Retry fetching the latest instance + } + + // Merge the instance fields into latestInstance + err = mergeInstancesFromLatest(oracleRestart, latestInstance) + if err != nil { + r.Log.Error(err, "Failed to merge instances, retrying...") + time.Sleep(retryDelay) + continue // Retry merging + } + + // Update the ResourceVersion of instance from latestInstance to avoid conflict + oracleRestart.ResourceVersion = latestInstance.ResourceVersion + err = r.Client.Status().Patch(ctx, oracleRestart, client.MergeFrom(latestInstance)) + + // patchData, err1 := json.Marshal(map[string]interface{}{ + // "status": oracleRestart.Status, + // }) + // if err1 != nil { + // r.Log.Error(err1, "Failed to marshal Status field") + // } + // err2 := r.Client.Status().Patch(ctx, OracleRestart, client.RawPatch(types.MergePatchType, patchData)) + if err != nil { + if apierrors.IsConflict(err) { + r.Log.Info("Conflict detected, retrying update...", "attempt", attempt+1) + time.Sleep(retryDelay) + continue // Retry on conflict + } + r.Log.Error(err, "Failed to update the RAC DB instance, retrying...") + time.Sleep(retryDelay) + continue // Retry on other errors + } + + // If update was successful, exit the loop + r.Log.Info("Updated RAC instance status successfully", "Instance", oracleRestart.Name) + break + } + + r.Log.Info("Returning from updateReconcileStatus") +} + +// ############################################################################# +// +// Validate the CRD specs +// +// ############################################################################# +func (r *OracleRestartReconciler) validateSpex(oracleRestart *oraclerestartdb.OracleRestart, oldSpec *oraclerestartdb.OracleRestartSpec, ctx context.Context) error { + var err error + eventReason := "Spec Error" + + //var eventMsgs []string + + r.Log.Info("Entering reconcile validation") + + //First check image pull secrets + if oracleRestart.Spec.ImagePullSecret != "" { + secret := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: oracleRestart.Spec.ImagePullSecret, Namespace: oracleRestart.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + // Secret not found + r.Recorder.Eventf(oracleRestart, corev1.EventTypeWarning, eventReason, err.Error()) + r.Log.Info(err.Error()) + return err + } + r.Log.Error(err, err.Error()) + return err + } + } + + // ======== Config Params Checks + // Checking Secret for ssh key + privKeyFlag, pubKeyFlag := oraclerestartcommon.GetSSHkey(oracleRestart, oracleRestart.Spec.SshKeySecret.Name, r.Client) + if !privKeyFlag { + return errors.New("private key name is not set to " + oracleRestart.Spec.SshKeySecret.PrivKeySecretName + " in SshKeySecret") + } + if !pubKeyFlag { + return errors.New("public key name is not set to " + oracleRestart.Spec.SshKeySecret.PubKeySecretName + " in SshKeySecret") + } + + // Checking Gi Responsefile + if oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName != "" { + giRspFlg, _ := oraclerestartcommon.GetGiResponseFile(oracleRestart, r.Client) + if !(giRspFlg) { + return errors.New("GridResponseFile name must be " + oracleRestart.Spec.ConfigParams.GridResponseFile.Name) + } + } + + if oracleRestart.Spec.ConfigParams.DbResponseFile.ConfigMapName != "" { + DbRspFlg, _ := oraclerestartcommon.GetDbResponseFile(oracleRestart, r.Client) + if !(DbRspFlg) { + return errors.New("DbResponseFile name must be " + oracleRestart.Spec.ConfigParams.DbResponseFile.Name) + } + } + r.ensureAsmStorageStatus(oracleRestart) + + specDisks := flattenDisksBySize(&oracleRestart.Spec) + + // Loop through all disk groups in Status.AsmDetails + for _, diskgroup := range oracleRestart.Status.AsmDetails.Diskgroup { + // Compare the number of disks in each diskgroup to the number of disks in Spec + if len(specDisks) < len(diskgroup.Disks) { + r.Log.Info("Validating Disk to remove for Diskgroup", "DiskgroupName", diskgroup.Name) + + // Call findDisksToRemove for this diskgroup to validate disk removal + _, err := findDisksToRemove(specDisks, diskgroup.Disks, oracleRestart) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New("required Disk is part of the disk group " + diskgroup.Name + " and cannot be removed. Review it manually.") + } + // else { + // r.Log.Info("Disks to be removed validated for Diskgroup", "DiskgroupName", diskgroup.Name) + // } + } + } + + // Validation to check if new ASM Disk is already part of POD; return error if it is. + // Loop through all disk groups in Status.AsmDetails + for _, diskgroup := range oracleRestart.Status.AsmDetails.Diskgroup { + // Compare the number of disks in each diskgroup to the number of disks in Spec + for _, diskgroupDisks := range diskgroup.Disks { + disks := strings.Split(diskgroupDisks, ",") + if len(specDisks) > len(disks) { + // r.Log.Info("Validating newly added Disk for Diskgroup", "DiskgroupName", diskgroup.Name) + + // Call findDisksToAdd to validate the newly added disks + _, err := findDisksToAdd(specDisks, diskgroup.Disks, oracleRestart, oldSpec) + if err != nil { + return err + } + // else { + // r.Log.Info("Disk to be added validated for Diskgroup", "DiskgroupName", diskgroup.Name, "Disk", fmt.Sprintf("%v", disk)) + // } + } + } + } + + // Checking the network cards in response files + + if oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName != "" { + _, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "networkInterfaceList", oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName, oracleRestart.Spec.ConfigParams.GridResponseFile.Name) + if err != nil { + oracleRestart.Spec.IsFailed = true + return err + } + + // Check if IsDelete is defined + switch isDeleteStr := oracleRestart.Spec.InstDetails.IsDelete; isDeleteStr { + case "true": + + r.Log.Info("Performing operation for IsDelete true") + + default: + // Validate network cards for both "false" and when IsDelete is not defined + if isDeleteStr != "" { + r.Log.Info("Unexpected value for IsDelete: " + isDeleteStr) + } + + } + + } + + r.Log.Info("Completed reconcile validation") + + return nil + +} + +// Helper function to flatten DisksBySize into a single slice of disk names +func flattenDisksBySize(oraclerestartdbSpec *oraclerestartdb.OracleRestartSpec) []string { + disksBySize := oraclerestartdbSpec.AsmStorageDetails.DisksBySize + var allDisks []string + for _, diskBySize := range disksBySize { + allDisks = append(allDisks, diskBySize.DiskNames...) + } + return allDisks +} + +// ############################################################################# +// +// Validate the CRD specs +// +// ############################################################################# +func (r *OracleRestartReconciler) validateASMDisks(oracleRestart *oraclerestartdb.OracleRestart, ctx context.Context) error { + //var eventMsgs []string + + r.Log.Info("Validate New ASM Disks") + desiredDaemonSet := oraclerestartcommon.BuildDiskCheckDaemonSet(oracleRestart) + + // Try to get the existing DaemonSet + existingDaemonSet := &appsv1.DaemonSet{} + err := r.Client.Get(ctx, types.NamespacedName{ + Name: desiredDaemonSet.Name, + Namespace: desiredDaemonSet.Namespace, + }, existingDaemonSet) + + if err != nil { + if apierrors.IsNotFound(err) { + // DaemonSet does not exist, so create it + r.Log.Info("Creating DaemonSet:", "desiredDaemonSet.Name", desiredDaemonSet.Name) + if err := r.Client.Create(ctx, desiredDaemonSet); err != nil { + oracleRestart.Spec.IsFailed = true + return err + } + } else { + // Some other error occurred in fetching the DaemonSet + oracleRestart.Spec.IsFailed = true + return err + } + } else { + // DaemonSet exists, so check if an update is necessary + if !reflect.DeepEqual(existingDaemonSet.Spec.Template.Spec.Volumes, desiredDaemonSet.Spec.Template.Spec.Volumes) { + // Update the existing DaemonSet with the desired state + r.Log.Info("Updating DaemonSet:", "desiredDaemonSet.Name", desiredDaemonSet.Name) + existingDaemonSet.Spec = desiredDaemonSet.Spec + if err := r.Client.Update(ctx, existingDaemonSet); err != nil { + return err + } + r.Log.Info("Updating Daemon set, takes upto 1 minute") + time.Sleep(1 * time.Second * 60) + //update takes times to terminate and recreate + } + } + + // r.Log.Info("Checking ASM DaemonSet Pod Status") + + return nil + +} + +func (r *OracleRestartReconciler) cleanupDaemonSet(OracleRestart *oraclerestartdb.OracleRestart, ctx context.Context) error { + // r.Log.Info("CleanupDaemonSet") + desiredDaemonSet := oraclerestartcommon.BuildDiskCheckDaemonSet(OracleRestart) + + // Try to get the existing DaemonSet + existingDaemonSet := &appsv1.DaemonSet{} + err := r.Client.Get(ctx, types.NamespacedName{ + Name: desiredDaemonSet.Name, + Namespace: desiredDaemonSet.Namespace, + }, existingDaemonSet) + + if err != nil { + if apierrors.IsNotFound(err) { + // DaemonSet does not exist, so nothing to delete + // r.Log.Info("No DaemonSet Found To Delete") + return nil + } + // Some other error occurred in fetching the DaemonSet + // r.Log.Error(err, "Some other error occurred in fetching the DaemonSet") + return err + } + + // DaemonSet exists, attempt to delete it + // r.Log.Info("Deleting DaemonSet", "DaemonSet.Name", existingDaemonSet.Name) + if err := r.Client.Delete(ctx, existingDaemonSet); err != nil { + r.Log.Error(err, "Failed to delete DaemonSet", "DaemonSet.Name", existingDaemonSet.Name) + return err + } + + // Poll for the DaemonSet to be deleted + timeout := 30 * time.Second + pollInterval := 5 * time.Second + startTime := time.Now() + + for { + // Check if we have exceeded the timeout + if time.Since(startTime) > timeout { + return fmt.Errorf("timeout waiting for DaemonSet %s to be deleted", existingDaemonSet.Name) + } + + // Check if the DaemonSet still exists + err = r.Client.Get(ctx, types.NamespacedName{ + Name: existingDaemonSet.Name, + Namespace: existingDaemonSet.Namespace, + }, existingDaemonSet) + + if err != nil { + if apierrors.IsNotFound(err) { + // DaemonSet no longer exists + r.Log.Info("DaemonSet deleted successfully", "DaemonSet.Name", existingDaemonSet.Name) + return nil + } + // Some other error occurred in fetching the DaemonSet + r.Log.Error(err, "Error checking for DaemonSet deletion", "DaemonSet.Name", existingDaemonSet.Name) + return err + } + + // Wait before checking again + time.Sleep(pollInterval) + } +} + +func findDisksToRemove(specDisks, statusDisks []string, instance *oraclerestartdb.OracleRestart) ([]string, error) { + // Convert specDisks to a set for fast lookups + specDiskSet := make(map[string]struct{}) + for _, disk := range specDisks { + specDiskSet[disk] = struct{}{} + } + + // Find disks in statusDisks that are not in specDiskSet + var disksToRemove []string + for _, disk := range statusDisks { + if _, found := specDiskSet[disk]; !found { + disksToRemove = append(disksToRemove, disk) + } + } + + // Validate that disks to be removed are not part of any other ASM device list + combinedList := strings.Join([]string{ + instance.Spec.ConfigParams.CrsAsmDeviceList, + instance.Spec.ConfigParams.RecoAsmDeviceList, + instance.Spec.ConfigParams.RedoAsmDeviceList, + instance.Spec.ConfigParams.DbAsmDeviceList, + }, ",") + combinedSet := make(map[string]struct{}) + for _, disk := range strings.Split(combinedList, ",") { + combinedSet[disk] = struct{}{} + } + + // Check for any disks to remove that are part of the combined ASM device list + var validatedDisks []string + for _, disk := range disksToRemove { + if _, found := combinedSet[disk]; found { + return nil, fmt.Errorf("disk %s to be removed is part of a disk group, hence cannot be removed", disk) + } + validatedDisks = append(validatedDisks, disk) + } + + return validatedDisks, nil +} + +func findDisksToAdd(newSpecDisks, statusDisks []string, instance *oraclerestartdb.OracleRestart, oldSpec *oraclerestartdb.OracleRestartSpec) ([]string, error) { + // Create a set for statusDisks to allow valid reuse of existing disks + // Step 1: Check for duplicates within newSpecDisks itself + oldAsmDisks := flattenDisksBySize(oldSpec) + + if len(oldAsmDisks) == len(newSpecDisks) { + oldDiskSet := make(map[string]struct{}) + for _, disk := range oldAsmDisks { + oldDiskSet[strings.TrimSpace(disk)] = struct{}{} + } + + allDisksMatch := true + for _, newDisk := range newSpecDisks { + if _, found := oldDiskSet[strings.TrimSpace(newDisk)]; !found { + allDisksMatch = false + break + } + } + + if allDisksMatch { + return nil, nil // No new disks to add + } + } + + seenDisks := make(map[string]struct{}) + for _, newDisk := range newSpecDisks { + trimmedDisk := strings.TrimSpace(newDisk) + + // Check if the disk is already in the seenDisks set, indicating a duplicate within newSpecDisks + if _, found := seenDisks[trimmedDisk]; found { + return nil, fmt.Errorf("disk '%s' is defined more than once in the new spec and cannot be added multiple times", trimmedDisk) + } + seenDisks[trimmedDisk] = struct{}{} + } + + // Step 2: Create a set for the actual statusDisks by splitting each entry + statusDiskSet := make(map[string]struct{}) + for _, diskEntry := range statusDisks { + // Split the disk entry by commas to handle multiple disks in a single string + for _, disk := range strings.Split(diskEntry, ",") { + statusDiskSet[strings.TrimSpace(disk)] = struct{}{} + } + } + + // Create sets for each of the individual ASM device lists + crsAsmDeviceSet := make(map[string]struct{}) + recoAsmDeviceSet := make(map[string]struct{}) + redoAsmDeviceSet := make(map[string]struct{}) + dbAsmDeviceSet := make(map[string]struct{}) + + // Step 4: Create a set to track newly added disks that are valid for addition + var validDisksToAdd []string + newDiskSet := make(map[string]struct{}) + + for _, newDisk := range newSpecDisks { + trimmedDisk := strings.TrimSpace(newDisk) + + // If the disk is already part of the statusDisks (existing disks), allow it to stay + if _, found := statusDiskSet[trimmedDisk]; found { + continue + } + + // Check if the disk is already part of any individual ASM device list + if _, found := crsAsmDeviceSet[trimmedDisk]; found { + return nil, fmt.Errorf("disk '%s' is already part of CRS ASM device list and cannot be added again", trimmedDisk) + } + if _, found := recoAsmDeviceSet[trimmedDisk]; found { + return nil, fmt.Errorf("disk '%s' is already part of RECO ASM device list and cannot be added again", trimmedDisk) + } + if _, found := redoAsmDeviceSet[trimmedDisk]; found { + return nil, fmt.Errorf("disk '%s' is already part of REDO ASM device list and cannot be added again", trimmedDisk) + } + if _, found := dbAsmDeviceSet[trimmedDisk]; found { + return nil, fmt.Errorf("disk '%s' is already part of DB ASM device list and cannot be added again", trimmedDisk) + } + + // Add the disk to newDiskSet and consider it valid for addition + newDiskSet[trimmedDisk] = struct{}{} + validDisksToAdd = append(validDisksToAdd, trimmedDisk) + } + + return validDisksToAdd, nil +} + +func (r *OracleRestartReconciler) setDefaults(oracleRestart *oraclerestartdb.OracleRestart) error { + + if oracleRestart.Spec.ImagePullPolicy == nil { + *oracleRestart.Spec.ImagePullPolicy = "Always" + } + + if oracleRestart.Spec.SshKeySecret != nil { + if oracleRestart.Spec.SshKeySecret.KeyMountLocation == "" { + oracleRestart.Spec.SshKeySecret.KeyMountLocation = utils.OraRacSshSecretMount + } + } + + if oracleRestart.Spec.DbSecret != nil { + if oracleRestart.Spec.DbSecret.Name != "" { + if oracleRestart.Spec.DbSecret.PwdFileMountLocation == "" { + oracleRestart.Spec.DbSecret.PwdFileMountLocation = utils.OraRacDbPwdFileSecretMount + } + if oracleRestart.Spec.DbSecret.KeyFileMountLocation == "" { + oracleRestart.Spec.DbSecret.KeyFileMountLocation = utils.OraRacDbKeyFileSecretMount + } + } + } + + if oracleRestart.Spec.TdeWalletSecret != nil { + if oracleRestart.Spec.TdeWalletSecret.Name != "" { + if oracleRestart.Spec.TdeWalletSecret.PwdFileMountLocation == "" { + oracleRestart.Spec.TdeWalletSecret.PwdFileMountLocation = utils.OraRacTdePwdFileSecretMount + } + if oracleRestart.Spec.TdeWalletSecret.KeyFileMountLocation == "" { + oracleRestart.Spec.TdeWalletSecret.KeyFileMountLocation = utils.OraRacTdeKeyFileSecretMount + } + } + } + + if oracleRestart.Spec.ConfigParams != nil { + if oracleRestart.Spec.ConfigParams.SwMountLocation == "" { + oracleRestart.Spec.ConfigParams.SwMountLocation = utils.OraSwLocation + } + + if oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName == "" { + if oracleRestart.Spec.ConfigParams.CrsAsmDiskDg == "" { + oracleRestart.Spec.ConfigParams.CrsAsmDiskDg = "+DATA" + } + + if oracleRestart.Spec.ConfigParams.CrsAsmDiskDgRedundancy == "" { + oracleRestart.Spec.ConfigParams.CrsAsmDiskDgRedundancy = "external" + } + } + + if oracleRestart.Spec.ConfigParams.DbResponseFile.ConfigMapName == "" { + if oracleRestart.Spec.ConfigParams.DbDataFileDestDg == "" { + oracleRestart.Spec.ConfigParams.DbDataFileDestDg = oracleRestart.Spec.ConfigParams.CrsAsmDiskDg + } + + if oracleRestart.Spec.ConfigParams.DbRecoveryFileDest == "" { + oracleRestart.Spec.ConfigParams.DbRecoveryFileDest = oracleRestart.Spec.ConfigParams.DbDataFileDestDg + } + + if oracleRestart.Spec.ConfigParams.DbCharSet == "" { + oracleRestart.Spec.ConfigParams.DbCharSet = "AL32UTF8" + } + } + + } + return nil + +} + +func (r *OracleRestartReconciler) updateGiConfigParamStatus(oracleRestart *oraclerestartdb.OracleRestart) error { + + //orestartPod := &corev1.Pod{} + + var cName, fName string + + if oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName != "" { + cName = oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName + } + if oracleRestart.Spec.ConfigParams.GridResponseFile.Name != "" { + fName = oracleRestart.Spec.ConfigParams.GridResponseFile.Name + } + + if oracleRestart.Status.ConfigParams == nil { + oracleRestart.Status.ConfigParams = new(oraclerestartdb.InitParams) + } + + if oracleRestart.Spec.ConfigParams != nil { + if oracleRestart.Status.ConfigParams.CrsAsmDeviceList == "" { + if oracleRestart.Spec.ConfigParams.CrsAsmDeviceList != "" { + oracleRestart.Status.ConfigParams.CrsAsmDeviceList = oracleRestart.Spec.ConfigParams.CrsAsmDeviceList + } else { + diskList, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "diskList", cName, fName) + if err != nil { + return errors.New(("error in responsefile, unable to read diskList")) + } + oracleRestart.Status.ConfigParams.CrsAsmDeviceList = diskList + } + } + if oracleRestart.Status.ConfigParams.Inventory == "" { + if oracleRestart.Spec.ConfigParams.Inventory != "" { + oracleRestart.Status.ConfigParams.Inventory = oracleRestart.Spec.ConfigParams.Inventory + } else { + invlocation, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "INVENTORY_LOCATION", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read inventory_location")) + } else { + oracleRestart.Status.ConfigParams.Inventory = invlocation + } + } + } + + if oracleRestart.Status.ConfigParams.GridBase == "" { + if oracleRestart.Spec.ConfigParams.GridBase != "" { + oracleRestart.Status.ConfigParams.GridBase = oracleRestart.Spec.ConfigParams.GridBase + } else { + gibase, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "ORACLE_BASE", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read oracle_base")) + } else { + oracleRestart.Status.ConfigParams.GridBase = gibase + } + } + } + if oracleRestart.Status.ConfigParams.GridHome == "NOT_DEFINED" { + if oracleRestart.Spec.ConfigParams.GridHome != "" { + oracleRestart.Status.ConfigParams.GridHome = oracleRestart.Spec.ConfigParams.GridHome + } else { + gihome, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "GRID_HOME", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read oracle_base")) + } else { + oracleRestart.Status.ConfigParams.GridHome = gihome + } + } + } + + // if oracleRestart.Status.ScanSvcName == "" { + // if oracleRestart.Spec.ScanSvcName != "" { + // oracleRestart.Status.ScanSvcName = oracleRestart.Spec.ScanSvcName + // } else { + // scanname, err := oraclerestartcommon.CheckRspData(OracleRestart, r.Client, "scanName", cName, fName) + // if err != nil { + // oracleRestart.Spec.IsFailed = true + // return errors.New(("error in responsefile, unable to read scanName")) + // } else { + // oracleRestart.Status.ScanSvcName = scanname + // } + // } + // } + + if oracleRestart.Status.ConfigParams.CrsAsmDiskDg == "" { + if oracleRestart.Spec.ConfigParams.CrsAsmDiskDg != "" { + oracleRestart.Status.ConfigParams.CrsAsmDiskDg = oracleRestart.Spec.ConfigParams.CrsAsmDiskDg + } else { + diskGroupName, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "diskGroupName", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read diskGroupName")) + } else { + oracleRestart.Status.ConfigParams.CrsAsmDiskDg = diskGroupName + } + } + } + + if oracleRestart.Status.ConfigParams.CrsAsmDiskDgRedundancy == "" { + if oracleRestart.Spec.ConfigParams.CrsAsmDiskDgRedundancy != "" { + oracleRestart.Status.ConfigParams.CrsAsmDiskDgRedundancy = oracleRestart.Spec.ConfigParams.CrsAsmDiskDgRedundancy + } else { + redundancy, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "redundancy", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read redundancy")) + } else { + oracleRestart.Status.ConfigParams.CrsAsmDiskDgRedundancy = redundancy + } + } + } + } + + return nil + +} + +func (r *OracleRestartReconciler) updateDbConfigParamStatus(oracleRestart *oraclerestartdb.OracleRestart) error { + + //orestartPod := &corev1.Pod{} + + var cName, fName string + + if oracleRestart.Spec.ConfigParams.DbResponseFile.ConfigMapName != "" { + cName = oracleRestart.Spec.ConfigParams.DbResponseFile.ConfigMapName + } + if oracleRestart.Spec.ConfigParams.DbResponseFile.Name != "" { + fName = oracleRestart.Spec.ConfigParams.DbResponseFile.Name + } + + if oracleRestart.Status.ConfigParams == nil { + oracleRestart.Status.ConfigParams = new(oraclerestartdb.InitParams) + } + + if oracleRestart.Spec.ConfigParams != nil { + if oracleRestart.Spec.ConfigParams.DbAsmDeviceList != "" { + oracleRestart.Status.ConfigParams.DbAsmDeviceList = oracleRestart.Spec.ConfigParams.DbAsmDeviceList + } + + if oracleRestart.Spec.ConfigParams.RecoAsmDeviceList != "" { + oracleRestart.Status.ConfigParams.RecoAsmDeviceList = oracleRestart.Spec.ConfigParams.RecoAsmDeviceList + } + if oracleRestart.Spec.ConfigParams.DBAsmDiskDgRedundancy != "" { + oracleRestart.Status.ConfigParams.DBAsmDiskDgRedundancy = oracleRestart.Spec.ConfigParams.DBAsmDiskDgRedundancy + } + + if oracleRestart.Status.ConfigParams.DbName == "" { + if oracleRestart.Spec.ConfigParams.DbName != "" { + oracleRestart.Status.ConfigParams.DbName = oracleRestart.Spec.ConfigParams.DbName + } else { + variable, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "variables=", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read variable")) + } + dbName := utils.GetValue(variable, "DB_NAME") + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read DB_NAME")) + } + oracleRestart.Status.ConfigParams.DbName = dbName + } + } + + if oracleRestart.Status.ConfigParams.DbDataFileDestDg == "" { + if oracleRestart.Spec.ConfigParams.DbDataFileDestDg != "" { + oracleRestart.Status.ConfigParams.DbDataFileDestDg = oracleRestart.Spec.ConfigParams.DbDataFileDestDg + if oracleRestart.Spec.ConfigParams.DbAsmDeviceList != "" { + oracleRestart.Status.ConfigParams.DbAsmDeviceList = oracleRestart.Spec.ConfigParams.DbAsmDeviceList + // Logic to validate and set Disk group using grid response file and dbca response file + var gcName, gfName string + + if oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName != "" { + gcName = oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName + } + if oracleRestart.Spec.ConfigParams.GridResponseFile.Name != "" { + gfName = oracleRestart.Spec.ConfigParams.GridResponseFile.Name + } + if gcName != "" && gfName != "" { + diskGroupName, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "diskGroupName", gcName, gfName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read diskGroupName")) + } + + if oracleRestart.Status.ConfigParams.DbDataFileDestDg != diskGroupName { + return nil + } else { + oracleRestart.Status.ConfigParams.DbDataFileDestDg = diskGroupName + } + } + } + } else { + // Logic to validate and set Disk group using grid response file and dbca response file + var gcName, gfName string + + if oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName != "" { + gcName = oracleRestart.Spec.ConfigParams.GridResponseFile.ConfigMapName + } + if oracleRestart.Spec.ConfigParams.GridResponseFile.Name != "" { + gfName = oracleRestart.Spec.ConfigParams.GridResponseFile.Name + } + if gcName != "" && gfName != "" { + diskGroupName, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "diskGroupName", gcName, gfName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in grid responsefile, unable to read diskGroupName to set DbDataFileDestDg")) + } + oracleRestart.Status.ConfigParams.DbDataFileDestDg = diskGroupName + dbdgloc, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "datafileDestination", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + oracleRestart.Status.ConfigParams.DbDataFileDestDg = diskGroupName + } else { + dbdg := strings.Split(dbdgloc, "/") + if len(dbdg) == 0 { + return errors.New("error in responsefile, unable to read datafileDestination diskgroup") + } + oracleRestart.Status.ConfigParams.DbDataFileDestDg = dbdg[0] + } + } else { + return errors.New("neither DbDataFileDestDg is set , nor grid response file is set. One of them is required") + } + } + } + + if oracleRestart.Status.ConfigParams.DbRecoveryFileDest == "" { + if oracleRestart.Spec.ConfigParams.DbRecoveryFileDest != "" { + oracleRestart.Status.ConfigParams.DbRecoveryFileDest = oracleRestart.Spec.ConfigParams.DbRecoveryFileDest + } else { + if cName != "" && fName != "" { + recodgloc, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "recoveryAreaDestination", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read recoveryAreaDestination")) + } + recodg := strings.Split(recodgloc, "/") + if len(recodg) == 0 { + return errors.New("error in responsefile, unable to read recoveryAreaDestination diskgroup") + } + oracleRestart.Status.ConfigParams.DbDataFileDestDg = recodg[0] + } + } + } + + if oracleRestart.Status.ConfigParams.DbBase == "" { + if oracleRestart.Spec.ConfigParams.DbBase != "" { + oracleRestart.Status.ConfigParams.DbBase = oracleRestart.Spec.ConfigParams.DbBase + } else { + variable, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "variables=", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read variable")) + } + obase := utils.GetValue(variable, "ORACLE_BASE") + if len(obase) == 0 { + return errors.New(("error in responsefile, unable to read ORACLE_BASE")) + } + oracleRestart.Status.ConfigParams.DbBase = obase + } + } + if oracleRestart.Status.ConfigParams.DbHome == "NOT_DEFINED" { + if oracleRestart.Spec.ConfigParams.DbHome != "" { + oracleRestart.Status.ConfigParams.DbHome = oracleRestart.Spec.ConfigParams.DbHome + } else { + variable, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "variables=", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read variable")) + } + ohome := utils.GetValue(variable, "ORACLE_HOME") + if len(ohome) == 0 { + return errors.New(("error in responsefile, unable to read ORACLE_BASE")) + } + oracleRestart.Status.ConfigParams.DbHome = ohome + } + } + + if oracleRestart.Status.ConfigParams.DbHome == "" { + if oracleRestart.Spec.ConfigParams.DbHome != "" { + oracleRestart.Status.ConfigParams.DbHome = oracleRestart.Spec.ConfigParams.DbHome + } else { + variable, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "variables=", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read variable")) + } + ohome := utils.GetValue(variable, "ORACLE_HOME") + if len(ohome) == 0 { + return errors.New(("error in responsefile, unable to read ORACLE_HOME")) + } + oracleRestart.Status.ConfigParams.DbHome = ohome + } + } + } + + if oracleRestart.Status.ConfigParams.GridHome == "" { + if oracleRestart.Spec.ConfigParams.GridHome != "" { + oracleRestart.Status.ConfigParams.GridHome = oracleRestart.Spec.ConfigParams.GridHome + } else { + variable, err := oraclerestartcommon.CheckRspData(oracleRestart, r.Client, "variables=", cName, fName) + if err != nil { + oracleRestart.Spec.IsFailed = true + return errors.New(("error in responsefile, unable to read variable")) + } + ghome := utils.GetValue(variable, "ORACLE_HOME") + if len(ghome) == 0 { + return errors.New(("error in responsefile, unable to read ORACLE_HOME")) + } + oracleRestart.Status.ConfigParams.GridHome = ghome + } + } + + return nil + +} + +func (r *OracleRestartReconciler) updateOracleRestartInstTopologyStatus(oracleRestart *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request) ([]string, map[string]*corev1.Node, error) { + + //orestartPod := &corev1.Pod{} + var podNames []string + nodeDetails := make(map[string]*corev1.Node) + + if strings.ToLower(oracleRestart.Spec.InstDetails.IsDelete) != "true" { + _, pod, err := r.validateOracleRestartInst(oracleRestart, ctx, req, oracleRestart.Spec.InstDetails, 0) + if err != nil { + return podNames, nodeDetails, err + } + podNames = append(podNames, pod.Name) + + // Get node details for the node where the pod is running + node, err := r.getNodeDetails(pod.Spec.NodeName) + if err != nil { + return podNames, nodeDetails, fmt.Errorf("failed to get node details for pod %s: %v", pod.Name, err) + } + nodeDetails[pod.Name] = node + } + + if len(podNames) == 0 || len(nodeDetails) == 0 { + oracleRestart.Spec.IsFailed = true + return podNames, nodeDetails, errors.New("error occurred while collecting RAC pod or node details") + } else { + oracleRestart.Spec.IsFailed = false + } + + return podNames, nodeDetails, nil +} + +func (r *OracleRestartReconciler) getNodeDetails(nodeName string) (*corev1.Node, error) { + node := &corev1.Node{} + err := r.Client.Get(context.TODO(), client.ObjectKey{ + Namespace: "", + Name: nodeName, + }, node) + if err != nil { + return nil, err + } + return node, nil +} + +func (r *OracleRestartReconciler) updateoraclerestartdbTopologyStatus(OracleRestart *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, podNames []string, nodeDetails map[string]*corev1.Node) error { + + //orestartPod := &corev1.Pod{} + var err error + _, _, err = r.validateoraclerestartdb(OracleRestart, ctx, req, podNames, nodeDetails) + if err != nil { + return err + } + return nil +} + +func (r *OracleRestartReconciler) validateoraclerestartdb(oracleRestart *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, podNames []string, nodeDetails map[string]*corev1.Node, +) (*appsv1.StatefulSet, *corev1.Pod, error) { + + orestartSfSet := &appsv1.StatefulSet{} + orestartPod := &corev1.Pod{} + const maxRetries = 5 + const retryDelay = 2 * time.Second + oraclerestartcommon.UpdateoraclerestartdbStatusData(oracleRestart, ctx, req, podNames, r.kubeClient, r.kubeConfig, r.Log, nodeDetails) + // Log the start of the status update process + r.Log.Info("Updating RAC instance status with validateoraclerestartdb", "Instance", oracleRestart.Name) + + for attempt := 0; attempt < maxRetries; attempt++ { + // // Fetch the latest version of the object + latestInstance := &oraclerestartdb.OracleRestart{} + err := r.Client.Get(ctx, req.NamespacedName, latestInstance) + if err != nil { + r.Log.Error(err, "Failed to fetch the latest version of RAC instance") + return orestartSfSet, orestartPod, err // Return the error if fetching the latest version fails + } + + // Merge the instance fields into latestInstance + err = mergeInstancesFromLatest(oracleRestart, latestInstance) + if err != nil { + r.Log.Error(err, "Failed to merge instances") + return orestartSfSet, orestartPod, err + } + + // Attempt to update the status of the instance directly + // err = r.Status().Update(ctx, instance) + + // Update the ResourceVersion of instance from latestInstance to avoid conflict + oracleRestart.ResourceVersion = latestInstance.ResourceVersion + err = r.Client.Status().Patch(ctx, oracleRestart, client.MergeFrom(latestInstance)) + + if err != nil { + if apierrors.IsConflict(err) { + // Handle the conflict and retry + r.Log.Info("Conflict detected in validateoraclerestartdb, retrying...", "attempt", attempt+1) + time.Sleep(retryDelay) + continue + // Retry + } + // For other errors, log and continue the retry loop + r.Log.Error(err, "Failed to update the RAC DB instance, retrying") + continue + } + + // If update was successful, exit the loop + r.Log.Info("Updated RAC instance status with validateoraclerestartdb", "Instance", oracleRestart.Name) + + return orestartSfSet, orestartPod, nil + } + + // If all retries fail, return an error + return orestartSfSet, orestartPod, fmt.Errorf("failed to update RAC DB Status after %d attempts", maxRetries) +} + +// ======= Function to validate Shard +func (r *OracleRestartReconciler) validateOracleRestartInst(oracleRestart *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, OraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, specId int) (*appsv1.StatefulSet, *corev1.Pod, error) { + + var err error + orestartSfSet := &appsv1.StatefulSet{} + orestartPod := &corev1.Pod{} + + orestartSfSet, err = oraclerestartcommon.CheckSfset(OraRestartSpex.Name, oracleRestart, r.Client) + if err != nil { + //msg := "Unable to find Rac statefulset " + oraclerestartcommon.GetFmtStr(OraRestartSpex.Name) + "." + //oraclerestartcommon.LogMessages("INFO", msg, nil, instance, r.Log) + r.updateOracleRestartInstStatus(oracleRestart, ctx, req, OraRestartSpex, string(oraclerestartdb.StatefulSetNotFound), r.Client, false) + return orestartSfSet, orestartPod, err + } + + podList, err := oraclerestartcommon.GetPodList(orestartSfSet.Name, oracleRestart, r.Client, OraRestartSpex) + if err != nil { + msg := "Unable to find any pod in statefulset " + oraclerestartcommon.GetFmtStr(orestartSfSet.Name) + "." + oraclerestartcommon.LogMessages("INFO", msg, nil, oracleRestart, r.Log) + r.updateOracleRestartInstStatus(oracleRestart, ctx, req, OraRestartSpex, string(oraclerestartdb.PodNotFound), r.Client, false) + return orestartSfSet, orestartPod, err + } + // Validate the pod list and get the list of not ready pods + isPodExist, orestartPod, notReadyPod := oraclerestartcommon.PodListValidation(podList, orestartSfSet.Name, oracleRestart, r.Client) + // Check if the pod is ready + if !isPodExist { + msg := "" + if notReadyPod != nil { + // Log the name of the first not ready pod + msg = "unable to validate RAC pod. The pod not ready is: " + notReadyPod.Name + oraclerestartcommon.LogMessages("INFO", msg, nil, oracleRestart, r.Log) + return orestartSfSet, orestartPod, fmt.Errorf(msg) + } else { + // Handle the case where no pods were found at all + msg = "unable to validate RAC pod. No pods matching the criteria were found" + oraclerestartcommon.LogMessages("INFO", msg, nil, oracleRestart, r.Log) + return orestartSfSet, orestartPod, fmt.Errorf(msg) + } + + } + // Update status when PODs are ready + state := oracleRestart.Status.State + if oracleRestart.Spec.IsManual { // if user changes spec to manual mode, lets change status column same as well + state = string(oraclerestartdb.OracleRestartManualState) + } + if oracleRestart.Spec.IsFailed { // if controller changes spec to failed mode, lets change status column same as well + state = string(oraclerestartdb.OracleRestartFailedState) + } + + switch { + case isPodExist && (state == string(oraclerestartdb.OracleRestartProvisionState) || + state == string(oraclerestartdb.OracleRestartUpdateState) || + state == string(oraclerestartdb.OracleRestartPendingState)): + // When previous update or provision is there, change to POD available intermittent state + state = string(oraclerestartdb.OracleRestartPodAvailableState) + case state == string(oraclerestartdb.OracleRestartFailedState): + // Failed state handling, remain in failed state or take specific action + state = string(oraclerestartdb.OracleRestartFailedState) + case state == string(oraclerestartdb.OracleRestartManualState): + // Manual state handling, e.g., do not modify state automatically + state = string(oraclerestartdb.OracleRestartManualState) + default: + // Continue with the current state for others, if no conditions are met + state = oracleRestart.Status.State + } + + r.updateOracleRestartInstStatus(oracleRestart, ctx, req, OraRestartSpex, state, r.Client, true) + r.Log.Info("Completed Update of Oracle Restart instance status") + return orestartSfSet, orestartPod, nil +} + +func (r *OracleRestartReconciler) updateOracleRestartInstStatus( + oracleRestart *oraclerestartdb.OracleRestart, + ctx context.Context, + req ctrl.Request, + OraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, + state string, + kClient client.Client, + mergingRequired bool, +) { + const maxRetries = 5 + const retryDelay = 2 * time.Second + + var lastErr error + var failedUpdate bool + // Get/Update RAC instance status data + oraclerestartcommon.UpdateOracleRestartInstStatusData(oracleRestart, ctx, req, OraRestartSpex, state, r.kubeClient, r.kubeConfig, r.Log, r.Client) + + for attempt := 0; attempt < maxRetries; attempt++ { + + // Fetch the latest version of the object + latestInstance := &oraclerestartdb.OracleRestart{} + err := r.Client.Get(ctx, req.NamespacedName, latestInstance) + if err != nil { + r.Log.Error(err, "Failed to fetch the latest version of RAC instance") + lastErr = err + continue // Continue to retry + } + latestInstance.Status.OracleRestartNodes = oracleRestart.Status.OracleRestartNodes + if mergingRequired { + + // Ensure latestInstance has the most recent version + r.ensureAsmStorageStatus(latestInstance) + + // Merge the instance fields into latestInstance + err = mergeInstancesFromLatest(oracleRestart, latestInstance) + if err != nil { + r.Log.Error(err, "Failed to merge instances") + } + } + + // Attempt to update the combined instance back to the Kubernetes API + // err = r.Status().Update(ctx, instance) + oracleRestart.ResourceVersion = latestInstance.ResourceVersion + + err = r.Status().Update(ctx, oracleRestart) + if err != nil { + if apierrors.IsConflict(err) { + r.Log.Info("Conflict detected in updateOracleRestartInstStatus, retrying...", "attempt", attempt+1) + time.Sleep(retryDelay) + failedUpdate = true + continue // Retry + } + // For other errors, log and return + r.Log.Error(err, "Failed to update the Oracle Restart instance") + lastErr = err + failedUpdate = true + continue // Continue to retry + } + r.Log.Info("Oracle Restart Object updated with updateOracleRestartInstStatus") + failedUpdate = false + break //break if its updated successfully + } + + // If we exhaust all retries, print the last error encountered + if failedUpdate { + r.Log.Info("failed to update Oracle Restart instance after 5 attempts", "lastErr", lastErr) + } +} + +// GetRestrictedFields returns a set of field names that are restricted from being updated. +func GetRestrictedFields() map[string]struct{} { + return map[string]struct{}{ + "ConfigParams.DbName": {}, + "ConfigParams.GridBase": {}, + "ConfigParams.GridHome": {}, + "ConfigParams.DbBase": {}, + "ConfigParams.DbHome": {}, + "ConfigParams.CrsAsmDiskDg": {}, + "ConfigParams.CrsAsmDiskDgRedundancy": {}, + "ConfigParams.DBAsmDiskDgRedundancy": {}, + "ConfigParams.DbCharSet": {}, + "ConfigParams.DbConfigType": {}, + "ConfigParams.DbDataFileDestDg": {}, + "ConfigParams.DbUniqueName": {}, + "ConfigParams.DbRecoveryFileDest": {}, + "ConfigParams.DbRedoFileSize": {}, + "ConfigParams.DbStorageType": {}, + "ConfigParams.DbSwZipFile": {}, + "ConfigParams.GridSwZipFile": {}, + "ConfigParams.GridResponseFile.ConfigMapName": {}, + "ConfigParams.GridResponseFile.Name": {}, + "ConfigParams.DbResponseFile.ConfigMapName": {}, + "ConfigParams.DbResponseFile.Name": {}, + } +} + +func mergeInstancesFromLatest(instance, latestInstance *oraclerestartdb.OracleRestart) error { + instanceVal := reflect.ValueOf(instance).Elem() + latestVal := reflect.ValueOf(latestInstance).Elem() + + // Assuming `Status` is a field in `OracleRestart` + instanceStatus := instanceVal.FieldByName("Status") + latestStatus := latestVal.FieldByName("Status") + + if !instanceStatus.IsValid() || !latestStatus.IsValid() { + return fmt.Errorf("status field is not valid in one of the instances") + } + + // Merge the Status field + return mergeStructFields(instanceStatus, latestStatus) +} + +func mergeStructFields(instanceField, latestField reflect.Value) error { + if instanceField.Kind() != reflect.Struct || latestField.Kind() != reflect.Struct { + return fmt.Errorf("fields to be merged must be of struct type") + } + + for i := 0; i < instanceField.NumField(); i++ { + subField := instanceField.Type().Field(i) + instanceSubField := instanceField.Field(i) + latestSubField := latestField.Field(i) + + if !isExported(subField) || !instanceSubField.CanSet() { + continue + } + + switch latestSubField.Kind() { + case reflect.Ptr: + if !latestSubField.IsNil() && instanceSubField.IsNil() { + instanceSubField.Set(latestSubField) + } + case reflect.String: + if latestSubField.String() != "" && latestSubField.String() != "NOT_DEFINED" && instanceSubField.String() == "" { + instanceSubField.Set(latestSubField) + } + case reflect.Struct: + if err := mergeStructFields(instanceSubField, latestSubField); err != nil { + return err + } + default: + if reflect.DeepEqual(instanceSubField.Interface(), reflect.Zero(instanceSubField.Type()).Interface()) { + instanceSubField.Set(latestSubField) + } + } + } + return nil +} + +func isExported(field reflect.StructField) bool { + return field.PkgPath == "" +} + +// Create Configmap +func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.OracleRestart) (map[string]string, error) { + configMapData := make(map[string]string, 0) + // new_crs_nodes, existing_crs_nodes_healthy, existing_crs_nodes_not_healthy, install_node, new_crs_nodes_list := oraclerestartcommon.GetCrsNodes(instance, r.kubeClient, r.kubeConfig, r.Log, r.Client) + install_node := instance.Spec.InstDetails.Name + "-0" + asm_devices := oraclerestartcommon.GetAsmDevices(instance) + var data []string + var addnodeFlag bool + + data = append(data, "OP_TYPE=setuprac") + data = append(data, "IGNORE_CRS_PREREQS=TRUE") + data = append(data, "IGNORE_DB_PREREQS=TRUE") + + // Service Parameters + if instance.Spec.ServiceDetails.Name != "" { + sparams := oraclerestartcommon.GetServiceParams(instance) + data = append(data, "DB_SERVICE="+sparams) + } + data = append(data, "CRS_GPC=true") + + if instance.Spec.ConfigParams.PdbName != "" { + data = append(data, "ORACLE_PDB="+instance.Spec.ConfigParams.PdbName) + } + + if instance.Spec.ConfigParams.DbHome != "" { + data = append(data, "DB_HOME="+instance.Spec.ConfigParams.DbHome) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.DbHome != "" { + data = append(data, "DB_HOME="+instance.Status.ConfigParams.DbHome) + } + } + } + + if instance.Spec.ConfigParams.DbBase != "" { + data = append(data, "DB_BASE="+instance.Spec.ConfigParams.DbBase) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.DbBase != "" { + data = append(data, "DB_BASE="+instance.Status.ConfigParams.DbBase) + } + } + } + + if instance.Spec.ConfigParams.GridBase != "" { + data = append(data, "GRID_BASE="+instance.Spec.ConfigParams.GridBase) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.GridBase != "" { + data = append(data, "GRID_BASE="+instance.Status.ConfigParams.GridBase) + } + } + } + + if instance.Spec.ConfigParams.GridHome != "" { + data = append(data, "GRID_HOME="+instance.Spec.ConfigParams.GridHome) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.GridHome != "" { + data = append(data, "GRID_HOME="+instance.Status.ConfigParams.GridHome) + } + } + } + + if instance.Spec.ConfigParams.Inventory != "" { + data = append(data, "INVENTORY="+instance.Spec.ConfigParams.Inventory) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.Inventory != "" { + data = append(data, "INVENTORY="+instance.Status.ConfigParams.Inventory) + } + } + } + + if instance.Spec.SshKeySecret.Name != " " { + //SecretMap check is done in ValidateSpex + data = append(data, "SSH_PRIVATE_KEY="+instance.Spec.SshKeySecret.KeyMountLocation+"/"+instance.Spec.SshKeySecret.PrivKeySecretName) + data = append(data, "SSH_PUBLIC_KEY="+instance.Spec.SshKeySecret.KeyMountLocation+"/"+instance.Spec.SshKeySecret.PubKeySecretName) + } + + if instance.Spec.DbSecret != nil { + if instance.Spec.DbSecret.Name != "" { + data = append(data, "SECRET_VOLUME="+instance.Spec.DbSecret.PwdFileMountLocation) + commonpassflag, pwdkeyflag, _ := oraclerestartcommon.GetDbSecret(instance, instance.Spec.DbSecret.Name, r.Client) + if commonpassflag && pwdkeyflag { + data = append(data, "DB_PWD_FILE="+instance.Spec.DbSecret.PwdFileName) + data = append(data, "PWD_KEY="+instance.Spec.DbSecret.KeyFileName) + } else { + data = append(data, "PASSWORD_FILE=pwdfile") + } + } + } + + if instance.Spec.TdeWalletSecret != nil { + if instance.Spec.TdeWalletSecret.Name != "" { + data = append(data, "TDE_SECRET_VOLUME="+instance.Spec.TdeWalletSecret.PwdFileMountLocation) + data = append(data, "SETUP_TDE_WALLET=true") + tdepassflag, tdepwdkeyflag, _ := oraclerestartcommon.GetTdeWalletSecret(instance, instance.Spec.TdeWalletSecret.Name, r.Client) + if tdepassflag && tdepwdkeyflag { + data = append(data, "TDE_PWD_FILE="+instance.Spec.TdeWalletSecret.PwdFileName) + data = append(data, "TDE_PWD_KEY="+instance.Spec.TdeWalletSecret.KeyFileName) + } else { + data = append(data, "PASSWORD_FILE=tdepwdfile") + } + } + } + + data = append(data, "PROFILE_FLAG=true") + // data = append(data, "SCAN_NAME="+scan_name) + + data = append(data, "INSTALL_NODE="+install_node) + + if instance.Spec.ConfigParams.DbName != "" { + data = append(data, "DB_NAME="+instance.Spec.ConfigParams.DbName) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.DbName != "" { + data = append(data, "DB_NAME="+instance.Status.ConfigParams.DbName) + } + } + } + + if instance.Spec.ConfigParams.DbUniqueName != "" { + // Configmap check is done in ValidateSpex + data = append(data, "DB_UNIQUE_NAME="+instance.Spec.ConfigParams.DbUniqueName) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.DbUniqueName != "" { + data = append(data, "DB_UNIQUE_NAME="+instance.Status.ConfigParams.DbUniqueName) + } + } + } + + if instance.Spec.ConfigParams.GridSwZipFile != "" { + data = append(data, "GRID_SW_ZIP_FILE="+instance.Spec.ConfigParams.GridSwZipFile) + //data = append(data, "COPY_GRID_SOFTWARE=true") + } + + data = append(data, "STAGING_SOFTWARE_LOC="+utils.OraSwStageLocation) + if instance.Spec.ConfigParams.RuPatchLocation != "" { + data = append(data, "APPLY_RU_LOCATION="+utils.OraRuPatchStageLocation) + } + if instance.Spec.ConfigParams.RuFolderName != "" { + data = append(data, "RU_FOLDER_NAME="+instance.Spec.ConfigParams.RuFolderName) + } + if instance.Spec.ConfigParams.OPatchLocation != "" { + data = append(data, "OPATCH_ZIP_FILE="+utils.OraOPatchStageLocation+"/"+instance.Spec.ConfigParams.OPatchSwZipFile) + } + + if instance.Spec.ConfigParams.DbSwZipFile != "" { + data = append(data, "DB_SW_ZIP_FILE="+instance.Spec.ConfigParams.DbSwZipFile) + //data = append(data, "COPY_DB_SOFTWARE=true") + } + + if instance.Spec.ConfigParams.CrsAsmDiskDg != "" { + data = append(data, "CRS_ASM_DISKGROUP="+instance.Spec.ConfigParams.CrsAsmDiskDg) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.CrsAsmDiskDg != "" { + data = append(data, "CRS_ASM_DISKGROUP="+instance.Status.ConfigParams.CrsAsmDiskDg) + } + } + } + + if instance.Spec.ConfigParams.DbAsmDeviceList != "" { + data = append(data, "DB_ASM_DEVICE_LIST="+instance.Spec.ConfigParams.DbAsmDeviceList) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.DbAsmDeviceList != "" { + data = append(data, "DB_ASM_DEVICE_LIST="+instance.Status.ConfigParams.DbAsmDeviceList) + } + } + } + + if instance.Spec.ConfigParams.CrsAsmDeviceList != "" { + data = append(data, "CRS_ASM_DEVICE_LIST="+instance.Spec.ConfigParams.CrsAsmDeviceList) + } else { + data = append(data, "CRS_ASM_DEVICE_LIST="+asm_devices) + } + + if instance.Spec.ConfigParams.RecoAsmDeviceList != "" { + data = append(data, "RECO_ASM_DEVICE_LIST="+instance.Spec.ConfigParams.RecoAsmDeviceList) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.RecoAsmDeviceList != "" { + data = append(data, "RECO_ASM_DEVICE_LIST="+instance.Status.ConfigParams.RecoAsmDeviceList) + } + } + } + + if instance.Spec.ConfigParams.RedoAsmDeviceList != "" { + data = append(data, "REDO_ASM_DEVICE_LIST="+instance.Spec.ConfigParams.RedoAsmDeviceList) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.RedoAsmDeviceList != "" { + data = append(data, "REDO_ASM_DEVICE_LIST="+instance.Status.ConfigParams.RedoAsmDeviceList) + } + } + } + + // Perform following if operation is not add node + if !addnodeFlag { + if instance.Spec.ConfigParams.DbDataFileDestDg != "" { + data = append(data, "DB_DATA_FILE_DEST="+instance.Spec.ConfigParams.DbDataFileDestDg) + } + + if instance.Spec.ConfigParams.DbStorageType != "" { + data = append(data, "DB_STORAGE_TYPE="+instance.Spec.ConfigParams.DbStorageType) + } + + if instance.Spec.ConfigParams.DbCharSet != "" { + data = append(data, "DB_CHARACTERSET="+instance.Spec.ConfigParams.DbCharSet) + } + + if instance.Spec.ConfigParams.DbRedoFileSize != "" { + data = append(data, "DB_REDO_FILE_SIZE="+instance.Spec.ConfigParams.DbRedoFileSize) + } + + if instance.Spec.ConfigParams.DbType != "" { + data = append(data, "DB_TYPE="+instance.Spec.ConfigParams.DbType) + } + + if instance.Spec.ConfigParams.DbConfigType != "" { + data = append(data, "DB_CONFIG_TYPE="+instance.Spec.ConfigParams.DbConfigType) + } + + if instance.Spec.ConfigParams.EnableArchiveLog != "" { + data = append(data, "ENABLE_ARCHIVELOG="+instance.Spec.ConfigParams.EnableArchiveLog) + } + + if instance.Spec.ConfigParams.GridResponseFile.ConfigMapName != "" { + // Configmap check is done in ValidateSpex + data = append(data, "GRID_RESPONSE_FILE="+utils.OraGiRsp+"/"+instance.Spec.ConfigParams.GridResponseFile.Name) + } + + if instance.Spec.ConfigParams.DbResponseFile.ConfigMapName != "" { + // Configmap check is done in ValidateSpex + data = append(data, "DBCA_RESPONSE_FILE="+utils.OraDbRsp+"/"+instance.Spec.ConfigParams.DbResponseFile.Name) + } + + // Getting DB Related paraeters + + if instance.Spec.ConfigParams.SgaSize != "" { + // Configmap check is done in ValidateSpex + data = append(data, "INIT_SGA_SIZE="+instance.Spec.ConfigParams.SgaSize) + } + + if instance.Spec.ConfigParams.PgaSize != "" { + // Configmap check is done in ValidateSpex + data = append(data, "INIT_PGA_SIZE="+instance.Spec.ConfigParams.PgaSize) + } + + if instance.Spec.ConfigParams.Processes > 0 { + // Configmap check is done in ValidateSpex + data = append(data, "INIT_PROCESSES="+strconv.Itoa(instance.Spec.ConfigParams.Processes)) + } + + if instance.Spec.ConfigParams.CpuCount > 0 { + // Configmap check is done in ValidateSpex + data = append(data, "CPU_COUNT="+strconv.Itoa(instance.Spec.ConfigParams.CpuCount)) + } + + if instance.Spec.ConfigParams.DbRecoveryFileDest != "" { + // Configmap check is done in ValidateSpex + data = append(data, "DB_RECOVERY_FILE_DEST="+instance.Spec.ConfigParams.DbRecoveryFileDest) + } + + if instance.Spec.ConfigParams.DbRecoveryFileDestSize != "" { + // Configmap check is done in ValidateSpex + data = append(data, "DB_RECOVERY_FILE_DEST_SIZE="+instance.Spec.ConfigParams.DbRecoveryFileDestSize) + } + if instance.Spec.ConfigParams.DBAsmDiskDgRedundancy != "" { + data = append(data, "DB_ASMDG_PROPERTIES="+"redudancy="+instance.Spec.ConfigParams.DBAsmDiskDgRedundancy) + } + + if instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy != "" { + data = append(data, "REDO_ASMDG_PROPETRIES="+"redudancy="+instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy) + } + + if instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy != "" { + data = append(data, "RECO_ASMDG_PROPETRIES="+"redudancy="+instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy) + } + + if instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy != "" { + data = append(data, "CRS_ASMDG_REDUNDANCY="+"redudancy="+instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy) + } + } + + configMapData["envfile"] = strings.Join(data, "\r\n") + + return configMapData, nil +} + +// ================================== CREATE FUNCTIONS ============================= + +// Create the configmap + +func (r *OracleRestartReconciler) createConfigMap(ctx context.Context, instance *oraclerestartdb.OracleRestart, cm *corev1.ConfigMap) (ctrl.Result, error) { + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + + found := &corev1.ConfigMap{} + + err := r.Get(ctx, types.NamespacedName{ + Name: cm.Name, + Namespace: instance.Namespace, + }, found) + + if err != nil && apierrors.IsNotFound(err) { + // Create the Service + reqLogger.Info("Creating Configmap Normally") + err = r.Create(ctx, cm) + if err != nil { + // Service creation failed + reqLogger.Error(err, "failed to create configmap", " namespace", instance.Namespace) + return ctrl.Result{}, nil + } else { + // Service creation was successful + return ctrl.Result{Requeue: true}, nil + } + } else if err != nil { + // Error that isn't due to the Service not existing + reqLogger.Error(err, "failed to find the configmap details") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil + +} + +// This function create a service based isExtern parameter set in the yaml file +func (r *OracleRestartReconciler) createOrReplaceService(ctx context.Context, instance *oraclerestartdb.OracleRestart, + dep *corev1.Service, +) (ctrl.Result, error) { + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + // See if Service already exists and create if it doesn't + + found := &corev1.Service{} + + err := r.Get(ctx, types.NamespacedName{ + Name: dep.Name, + Namespace: instance.Namespace, + }, found) + + jsn, _ := json.Marshal(dep) + oraclerestartcommon.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) + if err != nil && apierrors.IsNotFound(err) { + // Create the Service + reqLogger.Info("Creating a service") + err = r.Create(ctx, dep) + if err != nil { + // Service creation failed + instance.Spec.IsFailed = true + reqLogger.Error(err, "Failed to create Service", "Service.Namespace", dep.Namespace, "Service.Name", dep.Name) + return ctrl.Result{}, nil + } else { + // Service creation was successful + return ctrl.Result{Requeue: true}, nil + } + } else if err != nil { + // Error that isn't due to the Service not existing + reqLogger.Error(err, "Failed to find the Service details") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +// ================================== CREATE FUNCTIONS ============================= +// This function create a service based isExtern parameter set in the yaml file + +func (r *OracleRestartReconciler) createOrReplaceAsmPv( + ctx context.Context, + instance *oraclerestartdb.OracleRestart, + dep *corev1.PersistentVolume, +) (string, ctrl.Result, error) { + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + found := &corev1.PersistentVolume{} + if dep == nil { + reqLogger.Error(nil, "PersistentVolume spec (dep) is nil") + return "", ctrl.Result{}, fmt.Errorf("PV object is nil") + } + + // Fetch the existing PV + err := r.Get(context.TODO(), types.NamespacedName{ + Name: dep.Name, + }, found) + + jsn, _ := json.Marshal(dep) + oraclerestartcommon.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) + + if err != nil && apierrors.IsNotFound(err) { + // PV does not exist, create it + reqLogger.Info("Creating a new PV", "dep.Name", dep.Name) + err = r.Create(context.TODO(), dep) + if err != nil { + // PV creation failed + instance.Spec.IsFailed = true + reqLogger.Error(err, "Failed to create Persistent Volume", "PV.Name", dep.Name) + return "", ctrl.Result{}, err + } + return dep.Name, ctrl.Result{}, nil + } else if err != nil { + // Other errors fetching the PV + reqLogger.Error(err, "Failed to get Persistent Volume details") + return "", ctrl.Result{}, err + } + + // Check if the disk path or configuration differs from the existing PV + if !reflect.DeepEqual(dep.Spec.PersistentVolumeSource.Local, found.Spec.PersistentVolumeSource.Local) { + // Disk configuration has changed, delete the old PV and create a new one + reqLogger.Info("Detected existing PV with different disk details and as the configuration has changed, setup cannot continue", "dep.Name", dep.Name) + return "", ctrl.Result{}, fmt.Errorf("persistent volume %s has a different disk configuration. Please delete or update the existing PV to proceed", dep.Name) + } + + reqLogger.Info("PV Found", "dep.Name", dep.Name) + + return found.Name, ctrl.Result{}, nil +} + +// ================================== CREATE FUNCTIONS ============================= +// This function create a PVC set in the yaml file +func (r *OracleRestartReconciler) createOrReplaceAsmPvC(ctx context.Context, instance *oraclerestartdb.OracleRestart, + dep *corev1.PersistentVolumeClaim, +) (string, ctrl.Result, error) { + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + found := &corev1.PersistentVolumeClaim{} + + err := r.Get(ctx, types.NamespacedName{ + Name: dep.Name, + Namespace: instance.Namespace, + }, found) + + jsn, _ := json.Marshal(dep) + oraclerestartcommon.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) + if err != nil && apierrors.IsNotFound(err) { + // Create the Service + reqLogger.Info("Creating a PVC") + dep.Spec.Selector = nil + err = r.Create(ctx, dep) + if err != nil { + // Service creation failed + instance.Spec.IsFailed = true + reqLogger.Error(err, "Failed to create Persistent Volume", "PVC.Namespace", dep.Namespace, "PersistentVolume.Name", dep.Name) + return "", ctrl.Result{}, err + } else { + // Service creation was successful + return dep.Name, ctrl.Result{}, nil + } + } else if err != nil { + // Error that isn't due to the Service not existing + reqLogger.Error(err, "Failed to find the persistent volume Claim details") + return "", ctrl.Result{}, err + } + + return found.Name, ctrl.Result{}, nil +} + +// ensureAsmStorageStatus initializes AsmStorageDetails and AsmStorageStatus if they are nil +func (r *OracleRestartReconciler) ensureAsmStorageStatus(oracleRestart *oraclerestartdb.OracleRestart) { + // Check if AsmDetails is nil and initialize it if necessary + if oracleRestart.Status.AsmDetails == nil { + oracleRestart.Status.AsmDetails = &oraclerestartdb.AsmInstanceStatus{ + Diskgroup: []oraclerestartdb.AsmDiskgroupStatus{}, + } + } + +} + +func (r *OracleRestartReconciler) ensureStatefulSetUpdated(ctx context.Context, + reqLogger logr.Logger, + oracleRestart *oraclerestartdb.OracleRestart, + desired *appsv1.StatefulSet, + asmAutoUpdate bool, + // isDelete bool, + req ctrl.Request) error { + timeout := 15 * time.Minute // Set a timeout for the update wait + timeoutCtx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // Fetch the existing StatefulSet + existing := &appsv1.StatefulSet{} + err := r.Get(ctx, types.NamespacedName{ + Name: desired.Name, + Namespace: oracleRestart.Namespace, + }, existing) + if err != nil { + if apierrors.IsNotFound(err) { + // If the StatefulSet doesn't exist, create it + reqLogger.Info("StatefulSet not found, creating new one", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + return r.Create(ctx, desired) + } + reqLogger.Error(err, "Failed to get StatefulSet", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + return err + } + + // Compare the existing StatefulSet spec with the desired spec, sfs is replaced when ASM devices are added or removed + if len(existing.Spec.Template.Spec.Containers[0].VolumeDevices) != len(desired.Spec.Template.Spec.Containers[0].VolumeDevices) { + r.Log.Info("Change State to UPDATING") + + // Update status for each instance + r.updateOracleRestartInstStatus(oracleRestart, ctx, req, oracleRestart.Spec.InstDetails, string(oraclerestartdb.OracleRestartUpdateState), r.Client, true) + + reqLogger.Info("StatefulSet spec differs for volume devices, updating StatefulSet (pods may be recreated)", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + + // Perform the update + err := r.Update(ctx, desired) + if err != nil { + reqLogger.Error(err, "Failed to update StatefulSet", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + return err + } + + reqLogger.Info("StatefulSet update applied, waiting for pod recreation", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + // } else { + // r.Log.Info("Change State to UPDATING") + + // Wait for the update to be applied + for { + select { + case <-timeoutCtx.Done(): + reqLogger.Error(timeoutCtx.Err(), "Timed out waiting for StatefulSet update", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + return timeoutCtx.Err() + + default: + updated := &appsv1.StatefulSet{} + err := r.Get(ctx, client.ObjectKey{ + Name: desired.Name, + Namespace: oracleRestart.Namespace, + }, updated) + + if err != nil { + reqLogger.Error(err, "Failed to get StatefulSet after update", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + return err + } + + if reflect.DeepEqual(updated.Spec.Template.Spec.Containers[0].VolumeDevices, desired.Spec.Template.Spec.Containers[0].VolumeDevices) { + reqLogger.Info("StatefulSet update is applied successfully", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + return nil + } + + reqLogger.Info("Waiting for StatefulSet update to be applied", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + time.Sleep(5 * time.Second) + } + } + // } + } else { + reqLogger.Info("StatefulSet matches for ASM devices, SFS wont be updated", "StatefulSet.Namespace", oracleRestart.Namespace, "StatefulSet.Name", desired.Name) + return nil + } +} + +func executeDiskGroupCommand(podName string, cmd []string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *oraclerestartdb.OracleRestart, logger logr.Logger) (string, string, error) { + return oraclerestartcommon.ExecCommand(podName, cmd, kubeClient, kubeConfig, instance, logger) +} + +// Function to get the disk group name +func getDiskGroupName(deviceDg string, oracleRestart *oraclerestartdb.OracleRestart) string { + switch deviceDg { + case oracleRestart.Spec.ConfigParams.CrsAsmDeviceList: + return oracleRestart.Spec.ConfigParams.CrsAsmDiskDg + case oracleRestart.Spec.ConfigParams.DbAsmDeviceList: + return oracleRestart.Spec.ConfigParams.DbDataFileDestDg + default: + return "" + } +} + +// Function to check if a disk group exists +func (r *OracleRestartReconciler) diskGroupExists(podName, diskGroupName string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *oraclerestartdb.OracleRestart, logger logr.Logger) (bool, error) { + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + cmd := fmt.Sprintf("su - grid -c 'asmcmd lsdg | grep -w %s'", diskGroupName) + stdout, _, err := oraclerestartcommon.ExecCommand(podName, []string{"bash", "-c", cmd}, r.kubeClient, r.kubeConfig, instance, reqLogger) + if err != nil { + return false, err + } + if strings.Contains(stdout, diskGroupName) { + return true, nil + } + return false, nil +} + +// Function to add disks +func (r *OracleRestartReconciler) addDisks(ctx context.Context, podList *corev1.PodList, instance *oraclerestartdb.OracleRestart, diskGroupName string, deviceList []string) error { + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + // Remove '+' prefix if present + if strings.HasPrefix(diskGroupName, "+") { + diskGroupName = strings.TrimPrefix(diskGroupName, "+") + } + + for _, pod := range podList.Items { + podName := pod.Name + + // Check if the disk group exists before trying to add disks + exists, err := r.diskGroupExists(podName, diskGroupName, r.kubeClient, r.kubeConfig, instance, reqLogger) + if err != nil { + reqLogger.Error(err, "Failed to check if disk group exists", "Pod.Name", podName, "DiskGroup", diskGroupName) + return err + } + if !exists { + err = fmt.Errorf("disk group %s does not exist", diskGroupName) + reqLogger.Error(err, "Disk group does not exist", "Pod.Name", podName, "DiskGroup", diskGroupName) + return err + } + + for _, disk := range deviceList { + cmd := fmt.Sprintf("python3 /opt/scripts/startup/scripts/main.py --updateasmdevices=\"diskname=%s;diskgroup=%s;processtype=addition\"", disk, diskGroupName) + reqLogger.Info("Executing command to add disk", "Pod.Name", podName, "Command", cmd) + stdout, stderr, err := oraclerestartcommon.ExecCommand(podName, []string{"bash", "-c", cmd}, r.kubeClient, r.kubeConfig, instance, reqLogger) + if err != nil { + instance.Spec.IsFailed = true + reqLogger.Error(err, "Failed to execute command", "Pod.Name", podName, "Command", cmd, "Stdout", stdout, "Stderr", stderr) + return err + } + } + } + return nil +} + +// // Function to delete disks +// func (r *OracleRestartReconciler) deleteDisks(ctx context.Context, podList *corev1.PodList, instance *oraclerestartdb.OracleRestart, deviceListName string, deviceList []string) error { +// reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) +// diskGroupName := getDiskGroupName(deviceListName, instance) +// // Remove '+' prefix if present +// if strings.HasPrefix(diskGroupName, "+") { +// diskGroupName = strings.TrimPrefix(diskGroupName, "+") +// } + +// for _, pod := range podList.Items { +// podName := pod.Name + +// // Check if the disk group exists before trying to delete disks +// exists, err := r.diskGroupExists(podName, diskGroupName, r.kubeClient, r.kubeConfig, instance, reqLogger) +// if err != nil { +// reqLogger.Error(err, "Failed to check if disk group exists", "Pod.Name", podName, "DiskGroup", diskGroupName) +// return err +// } +// if !exists { +// err = fmt.Errorf("disk group %s does not exist", diskGroupName) +// reqLogger.Error(err, "Disk group does not exist", "Pod.Name", podName, "DiskGroup", diskGroupName) +// return err +// } + +// for _, disk := range deviceList { +// cmd := fmt.Sprintf("python3 /opt/scripts/startup/scripts/main.py --updateasmdevices=\"diskname=%s;diskgroup=%s;processtype=deletion\"", disk, diskGroupName) +// reqLogger.Info("Executing command to delete disk", "Pod.Name", podName, "Command", cmd) +// stdout, stderr, err := oraclerestartcommon.ExecCommand(podName, []string{"bash", "-c", cmd}, r.kubeClient, r.kubeConfig, instance, reqLogger) +// if err != nil { +// reqLogger.Error(err, "Failed to execute command", "Pod.Name", podName, "Command", cmd, "Stdout", stdout, "Stderr", stderr) +// return err +// } +// } +// } +// return nil +// } + +// Function to check DaemonSet status with retry, timeout, and log analysis +func checkDaemonSetStatus(ctx context.Context, r *OracleRestartReconciler, oracleRestart *oraclerestartdb.OracleRestart) (bool, error) { + timeout := time.After(2 * time.Minute) + tick := time.NewTicker(10 * time.Second) // Poll every 10 seconds + defer tick.Stop() + // Sleep for 60 seconds + for { + select { + case <-timeout: + // Timeout reached + ds := &appsv1.DaemonSet{} + err := r.Client.Get(ctx, types.NamespacedName{ + Name: "disk-check-daemonset", + Namespace: oracleRestart.Namespace, + }, ds) + if err != nil { + return false, err + } + + // Fetch the list of Pods managed by the DaemonSet + pods, err := r.kubeClient.CoreV1().Pods(oracleRestart.Namespace).List(ctx, metav1.ListOptions{ + LabelSelector: "app=disk-check", + }) + if err != nil { + return false, err + } + + // Check logs from each Pod + for _, pod := range pods.Items { + if pod.Status.Phase != corev1.PodRunning { + // Pod is not running, check for logs and errors + logs, err := r.kubeClient.CoreV1().Pods(oracleRestart.Namespace).GetLogs( + pod.Name, + &corev1.PodLogOptions{}, + ).DoRaw(ctx) + if err != nil { + return false, err + } + + if bytes.Contains(logs, []byte("not a valid block device")) { + // Disk validation failed + return false, nil + } + } + } + + // DaemonSet did not become ready or running within the timeout + return false, fmt.Errorf("DaemonSet %s/%s did not become ready or running within 5 minutes", oracleRestart.Namespace, "disk-check-daemonset") + + case <-tick.C: + // Check DaemonSet status + ds := &appsv1.DaemonSet{} + err := r.Client.Get(ctx, types.NamespacedName{ + Name: "disk-check-daemonset", + Namespace: oracleRestart.Namespace, + }, ds) + if err != nil { + return false, err + } + + // Check DaemonSet readiness + if ds.Status.NumberReady == ds.Status.DesiredNumberScheduled && ds.Status.NumberReady > 0 { + // DaemonSet is running and ready + return true, nil + } + + // If DaemonSet is not ready, fetch the list of Pods managed by the DaemonSet + pods, err := r.kubeClient.CoreV1().Pods(oracleRestart.Namespace).List(ctx, metav1.ListOptions{ + LabelSelector: "app=disk-check", + }) + if err != nil { + return false, err + } + + // Check logs from each Pod + for _, pod := range pods.Items { + // Pod is not running, check for logs and errors + logs, err := r.kubeClient.CoreV1().Pods(oracleRestart.Namespace).GetLogs( + pod.Name, + &corev1.PodLogOptions{}, + ).DoRaw(ctx) + if err != nil { + return false, err + } + + if bytes.Contains(logs, []byte("not a valid block device")) { + // Disk validation failed + return false, nil + } + + } + } + } +} + +// ================================== CREATE FUNCTIONS ============================= +// This function create a PVC set in the yaml file +func (r *OracleRestartReconciler) createOrReplaceSfs(ctx context.Context, req ctrl.Request, oracleRestart *oraclerestartdb.OracleRestart, + dep *appsv1.StatefulSet, index int, isLast bool, oldState string, +) (ctrl.Result, error) { + reqLogger := r.Log.WithValues("Instance.Namespace", oracleRestart.Namespace, "Instance.Name", oracleRestart.Name) + + found := &appsv1.StatefulSet{} + + err := r.Get(ctx, types.NamespacedName{ + Name: dep.Name, + Namespace: oracleRestart.Namespace, + }, found) + + jsn, _ := json.Marshal(dep) + oraclerestartcommon.LogMessages("DEBUG", string(jsn), nil, oracleRestart, r.Log) + if err != nil && apierrors.IsNotFound(err) { + // Create the StatefulSet + r.updateOracleRestartInstStatus(oracleRestart, ctx, req, oracleRestart.Spec.InstDetails, string(oraclerestartdb.OracleRestartProvisionState), r.Client, true) + reqLogger.Info("Creating a StatefulSet Normally", "StatefulSetName", dep.Name) + err = r.Create(ctx, dep) + + if err != nil { + // StatefulSet creation failed + oracleRestart.Spec.IsFailed = true + reqLogger.Error(err, "Failed to create StatefulSet", "StatefulSet.Namespace", dep.Namespace, "StatefulSet.Name", dep.Name) + return ctrl.Result{}, err + } else if !isLast { + // StatefulSet creation was successful + return ctrl.Result{}, nil + } + } else if err != nil { + // Error that isn't due to the StatefulSet not existing + reqLogger.Error(err, "Failed to find the StatefulSet details") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil + +} + +// ================================== CREATE FUNCTIONS ============================= +// This function create a PVC set in the yaml file +func (r *OracleRestartReconciler) createOrReplaceSfsAsm(ctx context.Context, req ctrl.Request, oracleRestart *oraclerestartdb.OracleRestart, + dep *appsv1.StatefulSet, asmAutoUpdate bool, index int, isLast bool, oldSpec *oraclerestartdb.OracleRestartSpec, +) (ctrl.Result, error) { + reqLogger := r.Log.WithValues("oracleRestart.Namespace", oracleRestart.Namespace, "oracleRestart.Name", oracleRestart.Name) + + found := &appsv1.StatefulSet{} + + // Check if the StatefulSet was found successfully + err := r.Get(ctx, types.NamespacedName{ + Name: dep.Name, + Namespace: oracleRestart.Namespace, + }, found) + if err != nil { + reqLogger.Error(err, "Failed to find existing StatefulSet to update") + return ctrl.Result{}, err + } + + addedAsmDisks, removedAsmDisks := getAddedAndRemovedDisks(oracleRestart, oldSpec, index) + + // Deletion Process execution + // isDelete := false + inUse := false + if len(removedAsmDisks) > 0 { + // Check if removed disks are in use in any diskgroup + asmInstanceStatus := oracleRestart.Status.AsmDetails + // isDelete = true + for _, removedAsmDisk := range removedAsmDisks { + for _, diskgroup := range asmInstanceStatus.Diskgroup { + for _, asmDiskStr := range diskgroup.Disks { + asmDisks := strings.Split(asmDiskStr, ",") + // Now compare each disk with removedAsmDisk + for _, asmDisk := range asmDisks { + if removedAsmDisk == asmDisk { + inUse = true + // / Disk is in use, return a message to the user and dont proceed further + err := fmt.Errorf("disk '%s' is part of diskgroup '%s' and must be manually removed before proceeding", removedAsmDisk, diskgroup.Name) + r.Log.Info("Disk is in use and cannot be removed. Must be manually removed before proceeding", "disk", removedAsmDisk, "diskgroup", diskgroup.Name) + return reconcile.Result{}, err + } + } + } + } + } + } + + r.ensureAsmStorageStatus(oracleRestart) + + // Ensure the StatefulSet is updated or re-created based on autoUpdate set to true/false + // err = r.ensureStatefulSetUpdated(ctx, reqLogger, OracleRestart, dep, autoUpdate, isDelete, req) + err = r.ensureStatefulSetUpdated(ctx, reqLogger, oracleRestart, dep, asmAutoUpdate, req) + if err != nil { + oracleRestart.Spec.IsFailed = true + reqLogger.Error(err, "Failed to ensure StatefulSet is updated or created") + r.updateOracleRestartInstStatus(oracleRestart, ctx, req, oracleRestart.Spec.InstDetails, string(oraclerestartdb.OracleRestartFailedState), r.Client, true) + return ctrl.Result{}, err + } + + // Wait for all Pods to be created and running + podList := &corev1.PodList{} + timeout := time.After(15 * time.Minute) // 15-minute timeout + ticker := time.NewTicker(1 * time.Minute) + defer ticker.Stop() + + allPodsRunning := false + +waitLoop: + for { + select { + case <-timeout: + reqLogger.Info("Timed out waiting for all Pods to be created and running", "StatefulSet.Namespace", dep.Namespace, "StatefulSet.Name", dep.Name) + return reconcile.Result{}, fmt.Errorf("timed out waiting for all Pods to be created and running") + case <-ticker.C: + err = r.List(ctx, podList, client.InNamespace(dep.Namespace), client.MatchingLabels(dep.Spec.Template.Labels)) + if err != nil { + reqLogger.Error(err, "Failed to list Pods", "StatefulSet.Namespace", dep.Namespace, "StatefulSet.Name", dep.Name) + return reconcile.Result{}, err + } + + allPodsRunning = true + for _, pod := range podList.Items { + if pod.Status.Phase != corev1.PodRunning { + allPodsRunning = false + reqLogger.Info("Waiting for Pod to be running", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name) + break + } + } + + if allPodsRunning { + reqLogger.Info("All Pods are running", "StatefulSet.Namespace", dep.Namespace, "StatefulSet.Name", dep.Name) + break waitLoop + } + } + } + + if allPodsRunning { + const ( + podCheckInterval = 15 * time.Second // Interval between pod readiness checks + podReadyTimeout = 15 * time.Minute // Maximum wait time for pod readiness + ) + + timeoutCtx, cancel := context.WithTimeout(ctx, podReadyTimeout) + defer cancel() + + // Wait for StatefulSet pods to be ready + for i := 0; i < len(dep.Spec.Template.Spec.Containers); i++ { + podName := fmt.Sprintf("%s-%d", dep.Name, i) + var isPodReady bool + + waitForPodASM: + for { + select { + case <-timeoutCtx.Done(): + reqLogger.Error(timeoutCtx.Err(), "Timed out waiting for pod to be ready", "Pod.Name", podName) + r.updateOracleRestartInstStatus(oracleRestart, ctx, req, oracleRestart.Spec.InstDetails, string(oraclerestartdb.OracleRestartFailedState), r.Client, true) + return ctrl.Result{}, timeoutCtx.Err() + default: + podList, err := oraclerestartcommon.GetPodList(dep.Name, oracleRestart, r.Client, oracleRestart.Spec.InstDetails) + if err != nil { + reqLogger.Error(err, "Failed to list pods") + return ctrl.Result{}, err + } + time.Sleep(podCheckInterval) + isPodReady, _, _ = oraclerestartcommon.PodListValidation(podList, dep.Name, oracleRestart, r.Client) + if isPodReady { + reqLogger.Info("Pod is ready", "Pod.Name", podName) + break waitForPodASM // Break out of the labeled loop + } else { + reqLogger.Info("Pod is not ready yet", "Pod.Name", podName) + time.Sleep(podCheckInterval) + } + } + } + } + + } + + // Disk is not in use, proceed with PV and PVC deletion in last stage + if isLast && !inUse { + // Use oraclerestartcommon.GetAsmPvcName and oraclerestartcommon.getAsmPvName to generate PVC and PV names + + // Find and delete the corresponding PVC + for index, diskName := range oracleRestart.Status.OracleRestartNodes[index].NodeDetails.MountedDevices { + for _, removedAsmDisk := range removedAsmDisks { + if diskName == removedAsmDisk { + pvcName := oraclerestartcommon.GetAsmPvcName(index, oracleRestart.Name) // Use the existing function + pvc := &corev1.PersistentVolumeClaim{} + err := r.Get(ctx, client.ObjectKey{ + Name: pvcName, + Namespace: oracleRestart.Namespace, + }, pvc) + if err != nil { + if !apierrors.IsNotFound(err) { + r.Log.Error(err, "Failed to get PVC", "PVC.Name", pvcName) + return reconcile.Result{}, err + } + // PVC already deleted + } else { + err = r.Delete(ctx, pvc) + if err != nil { + r.Log.Error(err, "Failed to delete PVC", "PVC.Name", pvcName) + return reconcile.Result{}, err + } + r.Log.Info("Successfully deleted PVC", "PVC.Name", pvcName) + } + + // Find and delete the corresponding PV + pvName := oraclerestartcommon.GetAsmPvName(index, oracleRestart.Name) // Use the existing function + pv := &corev1.PersistentVolume{} + err = r.Get(ctx, client.ObjectKey{ + Name: pvName, + }, pv) + if err != nil { + if !apierrors.IsNotFound(err) { + r.Log.Error(err, "Failed to get PV", "PV.Name", pvName) + return reconcile.Result{}, err + } + // PV already deleted + } else { + err = r.Delete(ctx, pv) + if err != nil { + r.Log.Error(err, "Failed to delete PV", "PV.Name", pvName) + return reconcile.Result{}, err + } + r.Log.Info("Successfully deleted PV", "PV.Name", pvName) + } + } + } + } + } + + if isLast && asmAutoUpdate { + // last iteration + // update status column with configParams + // Addition fo Disk Execution + // Check each new disk against CrsAsmDeviceList, DbAsmDeviceList, RecoAsmDeviceList, RedoAsmDeviceList + deviceDg := "" + for _, disk := range addedAsmDisks { + if isDiskInDeviceList(disk, oracleRestart.Spec.ConfigParams.CrsAsmDeviceList) { + reqLogger.Info("New disk to be added to CRS ASM device list ", "disk", disk) + deviceDg = oracleRestart.Spec.ConfigParams.CrsAsmDiskDg + } + if isDiskInDeviceList(disk, oracleRestart.Spec.ConfigParams.DbAsmDeviceList) { + reqLogger.Info("New disk to be added to DB ASM device list ", "disk", disk) + deviceDg = oracleRestart.Spec.ConfigParams.DbDataFileDestDg + } + if isDiskInDeviceList(disk, oracleRestart.Spec.ConfigParams.RecoAsmDeviceList) { + reqLogger.Info("New disk to be added to RECO ASM device list ", "disk", disk) + deviceDg = oracleRestart.Spec.ConfigParams.RecoAsmDiskDgRedundancy + } + if isDiskInDeviceList(disk, oracleRestart.Spec.ConfigParams.RedoAsmDeviceList) { + reqLogger.Info("New disk to be added to REDO ASM device list ", "disk", disk) + deviceDg = oracleRestart.Spec.ConfigParams.RedoAsmDiskDgRedudancy + } + } + if deviceDg != "" { + // Add disks after POD recreation + podList, err := oraclerestartcommon.GetPodList(dep.Name, oracleRestart, r.Client, oracleRestart.Spec.InstDetails) + if err != nil { + reqLogger.Error(err, "Failed to list pods") + return ctrl.Result{}, err + } + err = r.addDisks(ctx, podList, oracleRestart, deviceDg, addedAsmDisks) + if err != nil { + return ctrl.Result{}, err + } + reqLogger.Info("New Disks added to CRS Disks Group") + } + } + + return ctrl.Result{}, nil +} + +func getAddedAndRemovedDisks(oracleRestart *oraclerestartdb.OracleRestart, oldSpec *oraclerestartdb.OracleRestartSpec, index int) ([]string, []string) { + addedAsmDisks := []string{} + removedAsmDisks := []string{} + + // Helper function to compare desired and previous disk lists + compareDisks := func(newDisks, oldDisks []string) ([]string, []string) { + newDiskMap := make(map[string]bool) + oldDiskMap := make(map[string]bool) + + for _, disk := range newDisks { + if disk != "" { + newDiskMap[disk] = true + } + } + for _, disk := range oldDisks { + if disk != "" { + oldDiskMap[disk] = true + } + } + + // Initialize added and removed slices for this comparison + added := []string{} + removed := []string{} + + for _, disk := range newDisks { + if disk != "" && !oldDiskMap[disk] { + added = append(added, disk) + } + } + for _, disk := range oldDisks { + if disk != "" && !newDiskMap[disk] { + removed = append(removed, disk) + } + } + + return added, removed + } + + // Flatten the new desired ASM disk lists + desiredAsmDisks := flattenDisksBySize(&oracleRestart.Spec) + oldAsmDisks := flattenDisksBySize(oldSpec) + + // Additional device lists + newCrsAsmDisks := strings.Split(oracleRestart.Spec.ConfigParams.CrsAsmDeviceList, ",") + oldCrsAsmDisks := strings.Split(oldSpec.ConfigParams.CrsAsmDeviceList, ",") + + newDbAsmDisks := strings.Split(oracleRestart.Spec.ConfigParams.DbAsmDeviceList, ",") + oldDbAsmDisks := strings.Split(oldSpec.ConfigParams.DbAsmDeviceList, ",") + + newRecoAsmDisks := strings.Split(oracleRestart.Spec.ConfigParams.RecoAsmDeviceList, ",") + oldRecoAsmDisks := strings.Split(oldSpec.ConfigParams.RecoAsmDeviceList, ",") + + newRedoAsmDisks := strings.Split(oracleRestart.Spec.ConfigParams.RedoAsmDeviceList, ",") + oldRedoAsmDisks := strings.Split(oldSpec.ConfigParams.RedoAsmDeviceList, ",") + + // Track unique added and removed disks + addedDiskSet := make(map[string]bool) + removedDiskSet := make(map[string]bool) + + // Compare ASM and other device lists + for _, diskLists := range [][2][]string{ + {desiredAsmDisks, oldAsmDisks}, + {newCrsAsmDisks, oldCrsAsmDisks}, + {newDbAsmDisks, oldDbAsmDisks}, + {newRecoAsmDisks, oldRecoAsmDisks}, + {newRedoAsmDisks, oldRedoAsmDisks}, + } { + added, removed := compareDisks(diskLists[0], diskLists[1]) + for _, disk := range added { + addedDiskSet[disk] = true + } + for _, disk := range removed { + removedDiskSet[disk] = true + } + } + + // Convert sets back to slices + for disk := range addedDiskSet { + addedAsmDisks = append(addedAsmDisks, disk) + } + for disk := range removedDiskSet { + removedAsmDisks = append(removedAsmDisks, disk) + } + + // Return the final list of added and removed disks + return addedAsmDisks, removedAsmDisks +} + +// Function to check if a disk is part of a device list +func isDiskInDeviceList(disk string, deviceList string) bool { + devices := strings.Split(deviceList, ",") + for _, device := range devices { + if strings.TrimSpace(device) == disk { + return true + } + } + return false +} + +// ############################################################################# +// +// Manage Finalizer to cleanup before deletion of OracleRestart +// +// ############################################################################# + +// manageOracleRestartDeletion manages the deletion of the OracleRestart resource +func (r *OracleRestartReconciler) manageOracleRestartDeletion(req ctrl.Request, ctx context.Context, oracleRestart *oraclerestartdb.OracleRestart) error { + log := r.Log.WithValues("manageOracleRestartDeletion", req.NamespacedName) + + // Check if the OracleRestart instance is marked to be deleted + isOracleRestartMarkedToBeDeleted := oracleRestart.GetDeletionTimestamp() != nil + if isOracleRestartMarkedToBeDeleted { + if controllerutil.ContainsFinalizer(oracleRestart, oracleRestartFinalizer) { + // Run finalization logic + err := r.cleanupOracleRestart(req, oracleRestart) + if err != nil { + return err + } + + // Remove finalizer and update the resource + if err := r.patchFinalizer(ctx, oracleRestart, false); err != nil { + log.Error(err, "Failed to remove finalizer") + return err + } + log.Info("Successfully removed OracleRestart finalizer") + } + return errors.New("deletion pending") + } + + // Add finalizer for this CR if not present + if !controllerutil.ContainsFinalizer(oracleRestart, oracleRestartFinalizer) { + if err := r.patchFinalizer(ctx, oracleRestart, true); err != nil { + log.Error(err, "Failed to add finalizer") + return err + } + } + return nil +} + +// patchFinalizer updates the finalizer for the given resource +func (r *OracleRestartReconciler) patchFinalizer(ctx context.Context, cr *oraclerestartdb.OracleRestart, add bool) error { + var finalizers []string + if add { + finalizers = append(cr.GetFinalizers(), oracleRestartFinalizer) + } else { + for _, finalizer := range cr.GetFinalizers() { + if finalizer != oracleRestartFinalizer { + finalizers = append(finalizers, finalizer) + } + } + } + + // Prepare patch payload + patchData := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": finalizers, + }, + } + patchBytes, err := json.Marshal(patchData) + if err != nil { + return err + } + + patch := client.RawPatch(types.MergePatchType, patchBytes) + return r.Client.Patch(ctx, cr, patch, &client.PatchOptions{ + FieldManager: "rac-database-finalizer-manager", + }) +} + +// ############################################################################# +// +// Finalization logic for OracleRestartFinalizer +// +// ############################################################################# +func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, + oracleRestart *oraclerestartdb.OracleRestart) error { + log := r.Log.WithValues("cleanupOracleRestart", req.NamespacedName) + // Cleanup steps that the operator needs to do before the CR can be deleted. + + sfSetFound := &appsv1.StatefulSet{} + + var err error + + oraRestartSpex := oracleRestart.Spec.InstDetails + sfSetFound, err = oraclerestartcommon.CheckSfset(oraRestartSpex.Name, oracleRestart, r.Client) + if err == nil { + log.Info("Deleting ORestart Statefulset " + sfSetFound.Name) + if err := r.Client.Delete(context.Background(), sfSetFound); err != nil { + return err + } + } + + cmName := oraRestartSpex.Name + oracleRestart.Name + "-cmap" + configMapFound, err := oraclerestartcommon.CheckConfigMap(oracleRestart, cmName, r.Client) + if err == nil { + log.Info("Deleting Oracle Restart Configmap " + configMapFound.Name) + if err := r.Client.Delete(context.Background(), configMapFound); err != nil { + return err + } + } + + if err := oraclerestartcommon.DelRestartSwPvc(oracleRestart, oraRestartSpex, r.Client, r.Log); err != nil { + return err + } + + // // Deleting the DaemonSet + daemonSetName := "disk-check-daemonset" + daemonSet := &appsv1.DaemonSet{} + + // Attempt to get the DaemonSet + err = r.Client.Get(context.TODO(), types.NamespacedName{ + Name: daemonSetName, + Namespace: oracleRestart.Namespace, + }, daemonSet) + + if err != nil { + if apierrors.IsNotFound(err) { + r.Log.Info("DaemonSet not found, skipping deletion", "DaemonSet.Name", daemonSetName) + } else { + r.Log.Error(err, "Failed to get DaemonSet", "DaemonSet.Name", daemonSetName) + return err + } + } else { + // DaemonSet exists, attempt to delete it + // r.Log.Info("Deleting DaemonSet", "DaemonSet.Name", daemonSetName) + err = r.Client.Delete(context.TODO(), daemonSet) + if err != nil { + r.Log.Error(err, "Failed to delete DaemonSet", "DaemonSet.Name", daemonSetName) + return err + } + } + + if oracleRestart.Spec.AsmStorageDetails != nil { + // Delete PVCs for each disk in DisksBySize + for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for index, disk := range diskBySize.DiskNames { + err = oraclerestartcommon.DelORestartPVC(oracleRestart, index, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + if err != nil { + return err + } + } + } + } + + if oraclerestartcommon.IsStaticProvisioning(r.Client, oracleRestart) { + if oracleRestart.Spec.AsmStorageDetails != nil { + // Delete PVs for each disk in DisksBySize + for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for index, disk := range diskBySize.DiskNames { + err = oraclerestartcommon.DelORestartPv(oracleRestart, index, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + if err != nil { + return err + } + } + } + } + } + + svcTypes := []string{"local"} + for _, svcType := range svcTypes { + svcFound, err := oraclerestartcommon.CheckORestartSvc(oracleRestart, svcType, oraRestartSpex, "", r.Client) + if err == nil { + log.Info("Deleting ORestart Service " + svcFound.Name) + if err := r.Client.Delete(context.Background(), svcFound); err != nil { + return err + } + } + } + + if oraRestartSpex.NodePortSvc != nil { + for index := range oraRestartSpex.NodePortSvc { + svcFound, err := oraclerestartcommon.CheckORestartSvc(oracleRestart, "nodeport", oraRestartSpex, oraRestartSpex.NodePortSvc[index].SvcName, r.Client) + if err == nil { + log.Info("Deleting ORestart Service " + svcFound.Name) + if err := r.Client.Delete(context.Background(), svcFound); err != nil { + return err + } + } + } + } + + // Delete ConfigMap + + log.Info("Successfully cleaned up OracleRestart") + return nil +} + +// ############################################################################# +// +// CLeanup RAC Instance +// +// ############################################################################# +func (r *OracleRestartReconciler) cleanupOracleRestartInstance(req ctrl.Request, ctx context.Context, oracleRestart *oraclerestartdb.OracleRestart) (int32, error) { + log := r.Log.WithValues("cleanupOracleRestartInstance", req.NamespacedName) + // Cleanup steps that the operator needs to do before the CR can be deleted. + + var i int32 + var err error + + OraRestartSpex := oracleRestart.Spec.InstDetails + if utils.CheckStatusFlag(OraRestartSpex.IsDelete) { + if len(oracleRestart.Status.OracleRestartNodes) > 0 { + for _, oraRacSatus := range oracleRestart.Status.OracleRestartNodes { + if strings.ToUpper(oraRacSatus.Name) == (strings.ToUpper(OraRestartSpex.Name) + "-0") { + if !utils.CheckStatusFlag(oraRacSatus.NodeDetails.IsDelete) { + oraRacSatus.NodeDetails.IsDelete = "true" + log.Info("Setting RAC status instance " + oraRacSatus.Name + " delete flag true") + err = r.deleteOracleRestartInst(OraRestartSpex, req, ctx, oracleRestart) + oraRacSatus.NodeDetails.IsDelete = "false" + if err != nil { + log.Info("Error occurred RAC instance " + oraRacSatus.Name + " deletion") + return 0, err // return value should be adjusted according to the function signature + } + } + } + } + } + } + + return i, nil +} + +func (r *OracleRestartReconciler) deleteOracleRestartInst(OraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, req ctrl.Request, ctx context.Context, oracleRestart *oraclerestartdb.OracleRestart) error { + log := r.Log.WithValues("cleanupOracleRestartInstance", req.NamespacedName) + // delete steps that the operator needs to do before the CR can be deleted. + + //var i int32 + var nodeCount int + var err error + var cmName string + var healthyNode string + + nodeCount, err = oraclerestartcommon.GetHealthyNodeCounts(oracleRestart) + healthyNode, err = oraclerestartcommon.GetHealthyNode(oracleRestart) + if err != nil { + return fmt.Errorf("no healthy node found in the cluster to perform delete node operator. manual intervention required") + } + + // var endp string = "" + // _, endp, err = oraclerestartcommon.GetDBLsnrEndPoints(oracleRestart) + // if err != nil { + // return fmt.Errorf("endpoint generation error in delete block") + // } + + sfSetFound := &appsv1.StatefulSet{} + svcFound := &corev1.Service{} + configMapFound := &corev1.ConfigMap{} + + sfSetFound, err = oraclerestartcommon.CheckSfset(OraRestartSpex.Name, oracleRestart, r.Client) + if err == nil { + // See if StatefulSets already exists and create if it doesn't + if strings.ToLower(OraRestartSpex.IsDelete) != "force" { + err = oraclerestartcommon.DelOracleRestartNode(sfSetFound.Name+"-0", oracleRestart, r.kubeClient, r.kubeConfig, r.Log) + if err != nil { + return err + } + } + err = r.Client.Delete(context.Background(), sfSetFound) + if err != nil { + return err + } + } + if !utils.CheckStatusFlag(OraRestartSpex.IsKeepPVC) { + err = oraclerestartcommon.DelRestartSwPvc(oracleRestart, OraRestartSpex, r.Client, r.Log) + if err != nil { + return err + } + } + + //cmName = oracleRestart.Spec.InstDetails[i].Name + oracleRestart.Name + "-cmap" + cmName = OraRestartSpex.Name + oracleRestart.Name + "-cmap" + configMapFound, err = oraclerestartcommon.CheckConfigMap(oracleRestart, cmName, r.Client) + if err == nil { + + err = r.Client.Delete(context.Background(), configMapFound) + if err != nil { + return err + } + } + + svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "vip", OraRestartSpex, "", r.Client) + if err == nil { + // See if service already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err + } + } + + svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "local", OraRestartSpex, "", r.Client) + if err == nil { + // See if service already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err + } + } + + svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "onssvc", OraRestartSpex, "", r.Client) + if err == nil { + // See if service already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err + } + } + + svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "lsnrsvc", OraRestartSpex, "", r.Client) + if err == nil { + // See if service already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err + } + } + + //NodePort Service + if OraRestartSpex.NodePortSvc != nil { + for index := range OraRestartSpex.NodePortSvc { + svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "nodeport", OraRestartSpex, OraRestartSpex.NodePortSvc[index].SvcName, r.Client) + if err == nil { + // See if service already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err + } + } + } + } + + // This block will execute only if asm cardinailty is set to 3. + if nodeCount == 3 { + err = oraclerestartcommon.UpdateAsmCount(oracleRestart.Spec.ConfigParams.GridHome, healthyNode, oracleRestart, r.kubeClient, r.kubeConfig, r.Log) + if err != nil { + log.Info("erorr occurred while updating the asm count") + } else { + log.Info("Updated the asm cardinality successfully") + } + } + + log.Info("Successfully cleaned up OracleRestartInstance") + return nil +} + +func IsStaticProvisioningUsed(ctx context.Context, c client.Client, storageClassName string) bool { + if storageClassName != "" { + return false + } + + var scList storagev1.StorageClassList + err := c.List(ctx, &scList) + if err != nil { + // Can't determine SCs — safest to assume static provisioning + return true + } + + for _, sc := range scList.Items { + if sc.Annotations["storageclass.kubernetes.io/is-default-class"] == "true" || + sc.Annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true" { + return false + } + } + + // No default SC found → static provisioning is expected + return true +} + +// SetupWithManager sets up the controller with the Manager. +func (r *OracleRestartReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&oraclerestartdb.OracleRestart{}). + Owns(&corev1.ConfigMap{}). + Owns(&corev1.Secret{}). + Owns(&corev1.Service{}). + Owns(&corev1.Pod{}). + Owns(&appsv1.StatefulSet{}). + WithOptions(controller.Options{MaxConcurrentReconciles: 100}). //ReconcileHandler is never invoked concurrently with the same object. + Complete(r) +} + +const oldSpecAnnotation = "OracleRestarts.database.oracle.com/old-spec" + +// GetOldSpec retrieves the old spec from annotations. +// Returns nil, nil if the annotation does not exist. +func (r *OracleRestartReconciler) GetOldSpec(oracleRestart *oraclerestartdb.OracleRestart) (*oraclerestartdb.OracleRestartSpec, error) { + // Check if annotations exist + annotations := oracleRestart.GetAnnotations() + if annotations == nil { + r.Log.Info("No annotations found on OracleRestart") + return nil, nil + } + + // Retrieve the specific annotation + val, ok := annotations[oldSpecAnnotation] + if !ok { + r.Log.Info("Old spec annotation not found") + return nil, nil + } + + // Unmarshal the old spec JSON string + specBytes := []byte(val) + var oldSpec oraclerestartdb.OracleRestartSpec + if err := json.Unmarshal(specBytes, &oldSpec); err != nil { + r.Log.Error(err, "Failed to unmarshal old spec from annotation") + return nil, fmt.Errorf("failed to unmarshal old spec from annotation: %w", err) + } + + // r.Log.Info("Successfully retrieved old spec from annotation", "spec", oldSpec) + return &oldSpec, nil +} + +// SetCurrentSpec stores the current spec as an annotation with retry logic, updating only if the annotation value has changed. +func (r *OracleRestartReconciler) SetCurrentSpec(ctx context.Context, oracleRestart *oraclerestartdb.OracleRestart, req ctrl.Request) error { + // Marshal the current spec into JSON + currentSpecData, err := json.Marshal(oracleRestart.Spec) + if err != nil { + return fmt.Errorf("failed to marshal current spec: %w", err) + } + currentSpecStr := string(currentSpecData) + + // Ensure Annotations map is initialized + if oracleRestart.Annotations == nil { + oracleRestart.Annotations = make(map[string]string) + } + + // Check if the annotation value has changed + existingSpecStr, exists := oracleRestart.Annotations[oldSpecAnnotation] + if exists && existingSpecStr == currentSpecStr { + r.Log.Info("Annotations are already up to date. Skipping update.") + return nil // No update needed + } + + // Update the annotation with the new spec + oracleRestart.Annotations[oldSpecAnnotation] = currentSpecStr + + // // Create a patch to update only the annotations + patchData, err := json.Marshal(map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": oracleRestart.Annotations, + }, + }) + if err != nil { + r.Log.Error(err, "Failed to marshal annotation patch data") + return err + } + + // Apply the patch + err = r.Patch(ctx, oracleRestart, client.RawPatch(types.MergePatchType, patchData)) + if err != nil { + if apierrors.IsConflict(err) { + r.Log.Info("Conflict detected while updating annotations, retrying...") + return fmt.Errorf("conflict occurred while updating annotations: %w", err) + } + r.Log.Error(err, "Failed to update RAC instance annotations") + return fmt.Errorf("failed to update RAC instance annotations: %w", err) + } + + r.Log.Info("RAC Object annotations updated with current spec annotation") + return nil +} diff --git a/docs/config/samples/orestart/custom-kubeletconfig.yaml b/docs/config/samples/orestart/custom-kubeletconfig.yaml new file mode 100644 index 00000000..7549ae2a --- /dev/null +++ b/docs/config/samples/orestart/custom-kubeletconfig.yaml @@ -0,0 +1,13 @@ +apiVersion: machineconfiguration.openshift.io/v1 +kind: KubeletConfig +metadata: + name: enable-unsafe-sysctls +spec: + machineConfigPoolSelector: + matchLabels: + custom-kubelet: enable-unsafe-sysctls + kubeletConfig: + allowedUnsafeSysctls: + - "kernel.shm*" + - "kernel.sem" + - "net.*" \ No newline at end of file diff --git a/docs/config/samples/orestart/custom-scc.yaml b/docs/config/samples/orestart/custom-scc.yaml new file mode 100644 index 00000000..92facf67 --- /dev/null +++ b/docs/config/samples/orestart/custom-scc.yaml @@ -0,0 +1,50 @@ +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: custom-scc +allowPrivilegedContainer: true +allowedCapabilities: + - '*' +allowedUnsafeSysctls: + - kernel.shm* + - net.* + - kernel.sem +runAsUser: + type: RunAsAny +seLinuxContext: + type: RunAsAny +supplementalGroups: + type: RunAsAny +fsGroup: + type: RunAsAny +volumes: + - awsElasticBlockStore + - azureDisk + - azureFile + - cephFS + - cinder + - configMap + - csi + - downwardAPI + - emptyDir + - ephemeral + - fc + - flexVolume + - flocker + - gcePersistentDisk + - gitRepo + - glusterfs + - hostPath + - image + - iscsi + - nfs + - persistentVolumeClaim + - photonPersistentDisk + - portworxVolume + - projected + - quobyte + - rbd + - scaleIO + - secret + - storageOS + - vsphere \ No newline at end of file diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md new file mode 100644 index 00000000..09f43adb --- /dev/null +++ b/docs/oraclerestart/README.md @@ -0,0 +1,60 @@ +# Using Oracle Restart with Oracle Database Operator for Kubernetes + +Oracle Restart is an option to the award-winning Oracle Database Enterprise Edition. Oracle Restart is a feature introduced in Oracle 11gR2 that automatically restarts Oracle components, such as the database instance, listener, and Oracle ASM, after a failure or system reboot. It ensures that these components are started in the correct order and that they are managed by Oracle's High Availability Services (HAS). This enhances the availability of Oracle databases in a standalone server environment. Refer [this documentation](https://docs.oracle.com/cd/E18283_01/server.112/e17120/restart001.htm) + +For more information on Oracle Restart Database 19c refer to the [Oracle Database Documentation](http://docs.oracle.com/en/database/). + +Kubernetes provides infrastructure building blocks such as compute, storage and networks. Kubernetes makes the infrastructure available as code. It enables rapid provisioning of multi-node topolgies. Additionally, Kubernetes also provides statefulsets, which are the workload API objects that are used to manage stateful applications like Oracle Restarts, Single Instance Oracle Database, and other Oracle features and configurations. + +The Oracle Restart Database Controller in Oracle Database Operator deploys Oracle Restart as a statefulset in the Kubernetes Clusters, using Oracle Restart Slim Image. The Oracle Restart Database Controller manages the typical lifecycle of Oracle Restart Database in a Kubernetes cluster, as shown below: + +* Create Oracle Restart database + * Install and Configure Oracle Grid Infrastructure + * Install and Configure Oracle Restart Database +* Create Persistent Storage, along with Statefulset +* Create Services +* Oracle Restart Instances Cleanup + +The Oracle Restart Database Controller provides end-to-end automation of Oracle Restart Database Deployment in a Kubernetes Cluster. + +## Using Oracle Restart Database Controller + +To create a Oracle database, complete the steps in the following sections: + +1. [Prerequisites for running Oracle Restart Database Controller](#prerequisites-for-running-oracle-restart-database-controller) +2. [Provisioning Oracle Restart database in a Oracle Kubernetes Engine Environment](#provisioning-oracle-restart-database-in-a-oracle-kubernetes-engine-environment) +3. [Connecting to Oracle Restart Database](#connecting-to-oracle-restart-database) +4. [Known Issues](#known-issues) +5. [Debugging and Troubleshooting](#debugging-and-troubleshooting) + +**Note** Before proceeding to the next section, you must complete the instructions given in each section based on your enviornment. + +### Prerequisites for running Oracle Restart Database Controller + +**IMPORTANT :** You must make the changes specified in this section before you proceed to the next section. + +In order to become familiar with Oracle Restart on containers, you can refer [this documentation](https://github.com/oracle/docker-images/blob/main/OracleDatabase/RAC/OracleRealApplicationClusters/docs/orestart/README.md) before proceeding further. + +[Pre-requisites for running Oracle Restart Database Controller](./provisioning/prerequisites_oracle_restart_db.md) + +## Provisioning Oracle Restart database in a Oracle Kubernetes Engine Environment + +Deploy Oracle Restart Database YAML files using Kubernetes Cluster on your Oracle Kubernetes Engine Environment (OKE). There are multiple use case possible for deploying the Oracle Restart Database. + +[1. Provisioning an Oracle Restart Database](./provisioning/provisioning_oracle_restart_db.md) +[2. Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) +[3. Provisioning an Oracle Restart Database with RU Patch](./provisioning/provisioning_oracle_restart_db_rupatch.md) +[4. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) + +## Connecting to Oracle Restart Database + +After the Oracle Restart Database has been provisioned using the Oracle Restart Database Controller in Oracle Database Operator, you can follow the steps in this document to connect to the Oracle Restart Database: [Database Connectivity](./provisioning/database_connection.md) + +## Cleanup + +Steps to cleanup Oracle Restart Database Controller deployed using above document in Oracle Database Kubernetes Operator are documented in this page: [Cleanup](./provisioning/cleanup.md) + + +## Debugging and Troubleshooting + +To debug the Oracle Restart database provisioned using the Oracle Restart Database Controller in Oracle Database Kubernetes Operator, follow this document: [Debugging and troubleshooting](./provisioning/debugging.md) \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/cleanup.md b/docs/oraclerestart/provisioning/cleanup.md new file mode 100644 index 00000000..a438e37e --- /dev/null +++ b/docs/oraclerestart/provisioning/cleanup.md @@ -0,0 +1,17 @@ +# Cleanup an Oracle Restart Database Deployed using Oracle Database Operator + +In order to delete and cleanup the Oracle Restart Database deployed using Oracle Database Operator, run below command. + +This example uses `oraclerestart_prov.yaml` to cleanup an Oracle Restart Database which was initially used for deployment: + + +1. Delete the `oraclerestart_prov.yaml` file: + ```sh + kubectl delete -f oraclerestart_prov.yaml + oraclerestart.database.oracle.com/oraclerestart-sample deleted + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + ``` \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md new file mode 100644 index 00000000..5ce19953 --- /dev/null +++ b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md @@ -0,0 +1,25 @@ +# Create Kubernetes secret for db user + +Create a Kubernetes secret named `db-user-pass` using a password in a text file and then encrypt it using an `openssl` key. The text file will be removed after secret is created. You need to make sure openssl must be installed on worker nodes. + +```sh +mkdir /tmp/.secrets/ + +# Generate a random openssl key +echo Oracle_23ai > /tmp/.secrets/pwdfile.txt +openssl genrsa -out /tmp/.secrets/key.pem +openssl rsa -in /tmp/.secrets/key.pem -out /tmp/.secrets/key.pub -pubout + +# Encrypt the password file +openssl pkeyutl -in /tmp/.secrets/pwdfile.txt -out /tmp/.secrets/pwdfile.enc -pubin -inkey /tmp/.secrets/key.pub -encrypt +rm -rf /tmp/.secrets/pwdfile.txt + +# Deleting the exisitng secret +kubectl delete secret db-user-pass-pkutl -n orestart + +# Create the Kubernetes secret in namespace "rac" +kubectl create secret generic db-user-pass-pkutl --from-file=/tmp/.secrets/pwdfile.enc --from-file=/tmp/.secrets/key.pem -n orestart + +# Check the secret details +kubectl get secret -n orestart +``` diff --git a/docs/oraclerestart/provisioning/create_kubernetes_secret_for_ssh_setup.md b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_ssh_setup.md new file mode 100644 index 00000000..110e3542 --- /dev/null +++ b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_ssh_setup.md @@ -0,0 +1,17 @@ +# Create Kubernetes secret for db user using an SSH Key Pair + +Generate an SSH Key Pair using `ssh-keygen`. Then create a Kubernetes secret named `ssh-key-secret` using this key pair. + +```sh +mkdir /tmp/.secrets/ssh + +# Generate a private and public key +ssh-keygen -t rsa -C "your_email@example.info" -f /tmp/.secrets/ssh/id_rsa + + +# Create the Kubernetes secret in namespace "shns" +kubectl create secret generic ssh-key-secret --from-file=ssh-privkey=/tmp/.secrets/ssh/id_rsa --from-file=ssh-pubkey=/tmp/.secrets/ssh/id_rsa.pub -n orestart + +# Check the secret details +kubectl get secret -n orestart +``` diff --git a/docs/oraclerestart/provisioning/database_connection.md b/docs/oraclerestart/provisioning/database_connection.md new file mode 100644 index 00000000..c48d8e18 --- /dev/null +++ b/docs/oraclerestart/provisioning/database_connection.md @@ -0,0 +1,47 @@ +# Database Connectivity + +## Database Connection to Oracle Restart Database +## Database Connection to Oracle Restart Database with NodePort Service +The Oracle Database with NodePort service deployed by Oracle Restart Controller can be reached using the Worker Node IP and the Port of the Node Port service. Use the below steps: + +1. Get the Details of the deployment: +```sh +$ kubectl get all -n orestart -o wide +NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES +pod/dbmc1-0 1/1 Running 0 5h46m 10.244.0.52 10.0.10.58 + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR +service/dbmc1 NodePort 10.96.53.210 1521:30007/TCP 5h46m statefulset.kubernetes.io/pod-name=dbmc1-0 +service/dbmc1-0 ClusterIP None 171m statefulset.kubernetes.io/pod-name=dbmc1-0 + +NAME READY AGE CONTAINERS IMAGES +statefulset.apps/dbmc1 1/1 5h46m dbmc1 localhost/oracle/database-rac:19.3.0-slim +``` +In this case, the port 1521 from the pod is mapped to port 30007 on the worker node. To make the connection from outside, you will need to open the port 30007 on the worker node for INGRESS. + +2. For the above deployment, you will be able to make an SQLPLUS database connection to this Oracle Restart Database from a remote client as below: + +```sh +bash-4.4$ sqlplus system/@//:30007/PORCLCDB + +SQL*Plus: Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems on Sat Jul 19 04:02:48 2025 +Version 23.9.0.25.09 + +Copyright (c) 1982, 2025, Oracle. All rights reserved. + +Last Successful login time: Sat Jul 19 2025 00:20:14 +00:00 + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> +SQL> set lines 200 +SQL> col HOST_NAME format a40 +SQL> select INSTANCE_NAME,HOST_NAME, DATABASE_TYPE from v$instance; + +INSTANCE_NAME HOST_NAME DATABASE_TYPE +---------------- ---------------------------------------- --------------- +PORCLCDB dbmc1-0 SINGLE +``` + diff --git a/docs/oraclerestart/provisioning/debugging.md b/docs/oraclerestart/provisioning/debugging.md new file mode 100644 index 00000000..0e7a00de --- /dev/null +++ b/docs/oraclerestart/provisioning/debugging.md @@ -0,0 +1,41 @@ +# Debugging and Troubleshooting + +When the Oracle Restart Database is provisioned using the Oracle Restart controller, the debugging of an issue with the deployment depends on at which stage the issue has been seen. + +Below are the possible cases and the steps to debug such an issue: + +## Failure during the provisioning of Kubernetes Pods + +In case the failure occurs during the provisioning, we need to check the status of the Kubernetes Pod which has failed to deployed. + +Use the below command to check the logs of the Pod which has a failure. For example, for failure in case of Pod `pod/dbmc1-0`, use below command: + +```sh +kubectl logs -f pod/dbmc1-0 -n orestart +``` + +In case the Pod has failed to provision due to an issue with the Docker Image Pull, you will see the error `Error: ErrImagePull` in above logs. + +If the Pod has not yet got initialized, use the below command to find the reason for it: + +```sh +kubectl describe pod/dbmc1-0 -n orestart +``` + +You will need to further troubleshoot depending upon the issue/error seen from the above step. + +## Failure in the provisioning of the Oracle Database + +In case the failure occures after the Kubernetes Pods are created but during the execution of the scripts to create the Oracle database, you will need to trobleshoot that at the individual Pod level. + +Initially, check the logs of the Kubernetes Pod using the command like below (change the name of the Pod with the actual Pod) + +```sh +kubectl logs -f pod/dbmc1-0 -n orestart +``` + +To check the details of the CRS logs or the RDBMS instance logs at the host level, switch to the corresponding Kubernetes container using the command like below: + +```sh +kubectl exec -it dbmc1-0 -n orestart -- tail -f /tmp/orod/oracle_rac_setup.log +``` \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/known_issues.md b/docs/oraclerestart/provisioning/known_issues.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov.yaml new file mode 100644 index 00000000..074ba0d5 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov.yaml @@ -0,0 +1,92 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.108 # IP address of the target node for deployment + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Logical group size (in GB) for ASM disk group + diskNames: + - /dev/oracleoci/oraclevdd # ASM disk device path 1 + - /dev/oracleoci/oraclevde # ASM disk device path 2 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-rac:19.3.0-slim + # image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim # <-- Replace with your image registry + + # Policy to always pull image (useful during testing or if image might change) + imagePullPolicy: Always + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml new file mode 100644 index 00000000..9101cae6 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml @@ -0,0 +1,104 @@ +# API version for OracleRestart custom resource +apiVersion: database.oracle.com/v4 + +# The kind of Kubernetes object being defined +kind: OracleRestart + +# Metadata contains identifying information for the resource +metadata: + name: oraclerestart-sample # Name of the OracleRestart resource + namespace: orestart # Kubernetes namespace for deployment + +spec: + # Instance configuration for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Target worker node(s) for this instance + workerNode: + - 10.0.10.108 # IP address of the node where DB will run + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + - svcType: nodeport # Service type (NodePort to expose externally) + name: dbmc1-service-nodeport # Name of the Kubernetes Service + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + + # ASM (Automatic Storage Management) disk group configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # ASM group size + diskNames: + - /dev/oracleoci/oraclevdd # Device for ASM disk 1 + - /dev/oracleoci/oraclevde # Device for ASM disk 2 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Oracle RAC slim image to be used for container creation + image: localhost/oracle/database-rac:19.3.0-slim + # image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim # <-- Replace with your custom registry if applicable + + # Ensures the latest version of the image is always pulled + imagePullPolicy: Always + + # Optional: ServiceAccount for RBAC and security policies (mandatory in OpenShift) + # serviceAccountName: oraclerestart # <-- Uncomment for OpenShift environments + + # Oracle database service information + serviceDetails: + name: soepdb # Logical name of the Oracle service (CDB or PDB) + + # Resource requests and limits for CPU and memory + resources: + requests: + memory: "16Gi" # Minimum required memory + cpu: "2" # Minimum required CPU cores + limits: + memory: "16Gi" # Max allowed memory + cpu: "2" # Max allowed CPU cores + + # System-level kernel parameters needed for Oracle database + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration for Oracle Grid and Database software + configParams: + gridHome: "/u01/app/19c/grid" # Path for Oracle Grid Infrastructure home + gridBase: "/u01/app/grid" # Base path for Grid Infrastructure + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle Database software home + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Path to oraInventory + gridSwZipFile: "grid_home.zip" # Grid software ZIP file + dbSwZipFile: "db_home.zip" # Database software ZIP file + sgaSize: "3G" # Oracle SGA size + pgaSize: "1G" # Oracle PGA size + processes: 2000 # Number of Oracle background/user processes + cpuCount: 4 # Number of CPUs Oracle should use + dbName: "PORCLCDB" # Database name + hostSwStageLocation: /scratch/software/19c/HAS_19.28.0.0.0OCWRU_LINUX.X64_250626/ # Host software staging location diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml new file mode 100644 index 00000000..c107bcbb --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml @@ -0,0 +1,107 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Kind of Kubernetes object being defined +kind: OracleRestart + +# Metadata section: name and namespace of the OracleRestart object +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance configuration for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Target worker node(s) for this instance + workerNode: + - 10.0.10.108 # IP address of the node where DB will run + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + - svcType: nodeport # Service type (NodePort to expose externally) + name: dbmc1 # Name of the Kubernetes Service + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + + # ASM (Automatic Storage Management) disk group configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Size of each ASM disk + diskNames: + - /dev/oracleoci/oraclevdd # Device for ASM disk 1 + - /dev/oracleoci/oraclevde # Device for ASM disk 2 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart database container + # image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0703 + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0723 # Latest image to use + imagePullPolicy: Always # Always pull the image when starting the pod + + # Optional: ServiceAccount for RBAC and security policies (mandatory in OpenShift) + # serviceAccountName: oraclerestart <-- optional normally for normal kubernetes cluster but mandatory for Openshift cluster to allow permissions in security contexts + + # Service name exposed for the database + serviceDetails: + name: soepdb + + # Resource requests and limits for CPU and memory + resources: + requests: + memory: "16Gi" # Minimum required memory + cpu: "2" # Minimum required CPU cores + limits: + memory: "16Gi" # Max allowed memory + cpu: "2" # Max allowed CPU cores + + # Security context for kernel parameters tuning + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration for Oracle Grid and Database software + configParams: + gridHome: "/u01/app/19c/grid" # Path for Oracle Grid Infrastructure home + gridBase: "/u01/app/grid" # Base path for Grid Infrastructure + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle Database software home + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Path to oraInventory + gridSwZipFile: "grid_home.zip" # Grid software ZIP file + dbSwZipFile: "db_home.zip" # Database software ZIP file + sgaSize: "3G" # Size of SGA memory + pgaSize: "1G" # Size of PGA memory + processes: 2000 # Number of Oracle processes + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Database name + + # Location where Base Release Software .zip files are staged + hostSwStageLocation: /scratch/software/stage # Base Release Software Location + + # Directory containing the unzipped RU patch (includes PatchSearch.xml) + ruPatchLocation: /scratch/software/ru_patch #<--- RU patch unzipped folder diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml new file mode 100644 index 00000000..d0d3abdb --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml @@ -0,0 +1,105 @@ +# API version for the OracleRestart custom resource +apiVersion: database.oracle.com/v4 + +# Kind of the custom resource being defined +kind: OracleRestart + +# Metadata section: defines the name and namespace of the resource +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Namespace where it will be deployed + +# Specification of the OracleRestart instance +spec: + # Instance-level details for this OracleRestart deployment + instDetails: + name: dbmc1 # Unique name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + workerNode: + - 02-00-17-01-73-a0 # Node hostname where the pod should be scheduled + + # NodePort service configuration + nodePortSvc: + - svcType: nodeport # Type of Kubernetes service to expose the Oracle DB + name: dbmc1-service-nodeport # Name of the service + + # Port mapping for the database service + portMappings: + - port: 1521 # Internal port inside the pod + targetPort: 1521 # Target port of the container + protocol: TCP # Protocol used + nodePort: 30007 # NodePort exposed on the worker node + + # Custom Storage class to be used for dynamic provisioning (if applicable) + storageClass: "ocs-storagecluster-ceph-rbd" + + # ASM disk configuration, grouped by disk size + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/oracleoci/oraclevdd # First disk path for ASM + - /dev/oracleoci/oraclevde # Second disk path for ASM + + # SSH secrets for inter-node communication and remote execution + sshKeySecret: + name: ssh-key-secret # Base secret name + privKeySecretName: ssh-privkey # Name of secret containing the private key + pubKeySecretName: ssh-pubkey # Name of secret containing the public key + + # Secret containing encrypted DB password and key + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Name of the key file inside the secret + pwdFileName: pwdfile.enc # Name of the encrypted password file inside the secret + + # Oracle image used to create the container + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0703 + + # Image pull policy (Always pull the latest version) + imagePullPolicy: Always + + # Kubernetes service account to be used for running the pod + serviceAccountName: oraclerestart + + # Logical name of the database service inside Oracle + serviceDetails: + name: soepdb + + # Resource requests and limits for CPU and memory + resources: + requests: + memory: "16Gi" # Memory requested by the container + cpu: "2" # CPU requested by the container + limits: + memory: "16Gi" # Maximum memory container can use + cpu: "2" # Maximum CPU container can use + + # Kernel parameters to be set inside the pod + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters required for Oracle Grid and DB installation + configParams: + gridHome: "/u01/app/19c/grid" # Path to Grid home + gridBase: "/u01/app/grid" # Path to Grid base + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Path to DB home + dbBase: "/u01/app/oracle" # Path to DB base + crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # List of ASM disks + inventory: "/u01/app/oraInventory" # Oracle Inventory location + gridSwZipFile: "grid_home.zip" # Grid software zip filename + dbSwZipFile: "db_home.zip" # DB software zip filename + sgaSize: "3G" # Size of the SGA memory pool + pgaSize: "1G" # Size of the PGA memory pool + processes: 2000 # Maximum number of processes + cpuCount: 4 # CPU count for the instance + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Staging location for unzipping Oracle software diff --git a/docs/oraclerestart/provisioning/orestart_nodeport_object.txt b/docs/oraclerestart/provisioning/orestart_nodeport_object.txt new file mode 100644 index 00000000..88d2fc36 --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_nodeport_object.txt @@ -0,0 +1,154 @@ +kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"nodePortSvc":[{"name":"dbmc1-service-no... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-07-21T06:32:13Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 8946152 + UID: c861883d-285b-47ef-ae14-032540149729 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/19c/HAS_19.28.0.0.0OCWRU_LINUX.X64_250626/ + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0718 + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.108 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Name: DATA + Redundancy: EXTERN + Client Etc Host: + 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local + Conditions: + Last Transition Time: 2025-07-21T06:45:22Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-07-21T06:58:29Z + Message: no reconcile errors + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_object.txt b/docs/oraclerestart/provisioning/orestart_object.txt new file mode 100644 index 00000000..b1d9428c --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_object.txt @@ -0,0 +1,127 @@ +kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart + Name: oraclerestart-sample + Namespace: orestart + Labels: + Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"envFile":"dbmc1oraclerestart-sample-cma... + API Version: database.oracle.com/v4 + Kind: OracleRestart + Metadata: + Creation Timestamp: 2025-07-02T06:51:16Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 386393 + UID: d9faa000-bb7f-42b7-83d8-cf43f6bf30e9 + Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Db Base: /u01/app/oracle + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/19c/HAS_19.28.0.0.0OCWRU_LINUX.X64_250626/ + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Db Secret: + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Name: pwdfile.enc + Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0630 + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Worker Node: + 10.0.10.108 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey + Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: Pending + Mounted Devices: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + NOT READY + Name: NOT READY + Redundancy: NOT READY + Client Etc Host: + 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local + Conditions: + Last Transition Time: 2025-07-02T08:03:17Z + Message: no reconcile errors + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Name: pwdfile.enc + Db State: OPEN + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE + Events: \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_rupatch_object.txt b/docs/oraclerestart/provisioning/orestart_rupatch_object.txt new file mode 100644 index 00000000..f5d4344b --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_rupatch_object.txt @@ -0,0 +1,158 @@ +orestart/ $ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"nodePortSvc":[{"name":"dbmc1","svcType"... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-07-23T18:13:54Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 10086875 + UID: f33a787c-9414-4ed9-9233-839992d185b1 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/19c/19.3.0/ + Inventory: /u01/app/oraInventory + O Patch Location: /scratch/software/19c/ + O Patch Sw Zip File: p6880880_190000_Linux-x86-64.zip + Pga Size: 1G + Processes: 2000 + Ru Folder Name: 37957391 + Ru Patch Location: /scratch/software/19c/19.28/ + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0723 + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1 + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.108 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: NOT HEALTHY + Mounted Devices: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Name: DATA + Redundancy: EXTERN + Client Etc Host: + 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local + Conditions: + Last Transition Time: 2025-07-23T18:26:09Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-07-23T18:29:18Z + Message: no reconcile errors + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.3.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_storage_object.txt b/docs/oraclerestart/provisioning/orestart_storage_object.txt new file mode 100644 index 00000000..eb21b853 --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_storage_object.txt @@ -0,0 +1,166 @@ +kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/var/scratch/orestart/","workerNode":["02-00-17-01-73-a0"],"nodePortSvc":[{"name":"dbmc1-... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-07-22T07:13:32Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 38605572 + UID: 0585a6e0-1dea-4212-b8e2-919e3f84f6a8 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /var/scratch/software/stage/ + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0703 + Image Pull Policy: Always + Inst Details: + Host Sw Location: /var/scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 02-00-17-01-73-a0 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Account Name: oraclerestart + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey + Storage Class: ocs-storagecluster-ceph-rbd +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Name: DATA + Redundancy: EXTERN + Client Etc Host: + 10.0.26.45 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local + Conditions: + Last Transition Time: 2025-07-22T07:26:46Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-07-22T08:41:30Z + Message: no reconcile errors + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + +[root@ldboper-bastian orestart]# kubectl get pvc -n orestart +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-disk-0-oraclerestart-sample Bound asm-pv-disk-1-oraclerestart-sample 50Gi RWO ocs-storagecluster-ceph-rbd 6m21s +asm-pvc-disk-1-oraclerestart-sample Bound asm-pv-disk-0-oraclerestart-sample 50Gi RWO ocs-storagecluster-ceph-rbd 6m21s +[root@ldboper-bastian orestart]# kubectl get pv -n orestart +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE +asm-pv-disk-0-oraclerestart-sample 50Gi RWO Retain Bound orestart/asm-pvc-disk-1-oraclerestart-sample ocs-storagecluster-ceph-rbd 6m28s +asm-pv-disk-1-oraclerestart-sample 50Gi RWO Retain Bound orestart/asm-pvc-disk-0-oraclerestart-sample ocs-storagecluster-ceph-rbd 6m28s \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md new file mode 100644 index 00000000..dc2fdcf7 --- /dev/null +++ b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md @@ -0,0 +1,282 @@ +# Prerequisites for running Oracle Restart Database Controller + * [Kubernetes Cluster Requirements](#kubernetes-cluster-requirements) + * [Prerequisites for Oracle Restart on OKE](#prerequisites-for-oracle-restart-on-oke) + * [Preparing to Install Oracle Restart on OKE](#preparing-to-install-oracle-restart-on-oke) + * [Worker Node Preparation for Oracle Restart on OKE](#worker-node-preparation-for-oracle-restart-on-oke) + + [Download Oracle Grid Infrastructure and Oracle Database Software](#download-oracle-grid-infrastructure-and-oracle-database-software) + + [Prepare the Worker Node for Oracle Restart Deployment](#prepare-the-worker-node-for-oracle-restart-deployment) + + [Set up SELinux Module on Worker Nodes](#set-up-selinux-module-on-worker-nodes) + * [Create a namespace for the Oracle Restart Setup](#create-a-namespace-for-the-oracle-restart-setup) + * [Install Cert Manager and Setup Access Permissions](#install-cert-manager-and-setup-access-permissions) + * [OpenShift Security Context Constraints](#openshift-security-context-constraints) + * [Deploy Oracle Database Operator](#deploy-oracle-database-operator) + * [Oracle Restart Database Slim Image](#oracle-restart-database-slim-image) + * [Create a Kubernetes secret for the Oracle Restart Database installation owner for the Oracle Restart Database Deployment](#create-a-kubernetes-secret-for-the-oracle-restart-database-installation-owner-for-the-oracle-restart-database-deployment) + +To deploy Oracle Restart Database using Oracle Restart Database Controller in Oracle Database Operator, you need a Kubernetes Cluster like an Oracle Kubernetes Engine(OKE). + +If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will require: + + #### Kubernetes Cluster Requirements + You must ensure that your Kubernetes Cluster meets the necessary requirements for Oracle Restart Database deployment. The minimum required OKE cluster version is 1.33.1 or higher. Refer documentation for details [Oracle Kubernetes Engine](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm) cluster. + + #### Prerequisites for Oracle Restart on OKE + Before proceeding with the Oracle Restart Database deployment, ensure that you have completed the necessary prerequisites on your OKE cluster. This includes setting up the required infrastructure and configuring the necessary components. To use these instructions, you should have background knowledge of the technology and operating system. + * Verify that all necessary dependencies are installed and up-to-date. You should be familiar with the following technologies: + * Linux + * Kubernetes + * Oracle Kubernetes Engine Environment (OKE) + * Oracle Real Database installation for Oracle Restart + * Oracle Grid Infrastructure installation + * Oracle Automatic Storage Management (Oracle ASM) + + + #### Preparing to Install Oracle Restart on OKE + To prepare for the Oracle Restart Database installation, follow these steps: + * Ensure that your OKE cluster is properly configured and meets the necessary requirements. + Each pod that you deploy as part of your cluster must satisfy the minimum hardware requirements of the Oracle Restart Database and Oracle Grid Infrastructure software. If you are planning to install Oracle Grid Infrastructure and Oracle Restart database software on data volumes exposed from your environment, then you must have at least 50 GB space allocated for the Oracle Restart on OKE Pod. + + * In addition to the standard memory (RAM) required for Oracle Linux (Linux-x86-64), the Oracle Grid Infrastructure and Oracle Database instances, Oracle recommends that you provide an additional 2 GB of RAM to each Kubernetes node for the control plane. + + * Database storage for Oracle Database on OKE must use Oracle Automatic Storage Management (Oracle ASM) configured on block storage. + + * Oracle Database on Kubernetes is currently supported with the following releases: + * Oracle Grid Infrastructure Release 19.28 or later release updates + * Oracle Database Release 19.28 or later + * Oracle Kubernetes Engine Environment 1.33.1 or later + * Unbreakable Enterprise Kernel Release 7 UEKR7 (Kernel Release 5.15.0-202.135.2.el9uek.x86_64 ) or later updates + * Oracle Linux for Operator, control plane, and Worker nodes on Oracle Linux 8 (Linux-x86-64) Update 10 or later updates + + + #### Worker Node Preparation for Oracle Restart on OKE + * When configuring your Worker Nodes, follow these guidelines, and see the configuration Oracle used for testing. + + Each OKE worker node must have sufficient resources to support the intended number of Oracle Database Pods, each of which must meet at least the minimum requirements for Oracle Grid Infrastructure servers hosting an Oracle Database node. + + * The Oracle Database Pods in this example configuration were created on the machine worker-1 for Oracle Restart: + - Oracle Database Node + - Worker Node: worker-1 + - Container Pod: dbmc1-0 + + * Worker node has the following configuration: + - RAM:16GB + - Operating system disk: + - Ensure that your storage has at least the following available space: + - / (Root): 40 GB + - /scratch/orestart/: 80 GB (the worker node directory which will be used for /u01 to store Oracle Grid Infrastructure and Oracle Database homes) + - /var/lib/containers: 50 GB xfs + - /scratch/software/stage (the worker node directory for staging Oracle Grid Infrastructure and Oracle RDBMS software) + - Oracle Linux 8.10 with Unbreakable Enterprise Kernel Release UEKR7 (Kernel Release 5.15.0-308.179.6.el8uek.x86_64) or later + - Network Cards: + - ens3: Default network interface. The Oracle Restart pod will use this network interface for cluster public network. + + - Block devices: + - You can use any supported storage options for Oracle Grid Infrastructure. Ensure that your storage has at least the following space available: + - /dev/oracleoci/oraclevdd (50 GB) + - /dev/oracleoci/oraclevde (50 GB) + - **Make sure the devices you are using for ASM Storage are cleared of any data from a previous usage or installation.** + - **If you want to use the devices from the worker nodes for ASM storage, you will need to mark any default StorageClass as non-default in your Kubernetes Cluster.** + + + ##### Download Oracle Grid Infrastructure and Oracle Database Software + You need to download the Oracle Grid Infrastructure and Oracle Database software and stage it on the worker nodes. The Oracle Restart Database Controller will handle mounting the software inside the Pod. + * The Oracle Database Container does not contain any Oracle software binaries. Download the following software from the [Oracle Technology Network](https://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html). + - Oracle Grid Infrastructure 19c (19.28) for Linux x86-64 + - Oracle Database 19c (19.28) for Linux x86-64 + + ##### Prepare the Worker Node for Oracle Restart Deployment + To prepare the worker node for Oracle Restart Database deployment, follow these steps: + Before you install Oracle Restart inside the OKE Pods, you must update the system configuration. + + 1. Log in as root. + 2. Use the vim editor to update `/etc/sysctl.conf` parameters to the following values: + ```sh + fs.file-max = 6815744 + net.core.rmem_default = 262144 + net.core.rmem_max = 4194304 + net.core.wmem_default = 262144 + net.core.wmem_max = 1048576 + fs.aio-max-nr = 1048576 + ``` + 3. Run the following commands: + * `# sysctl -a` + * `# sysctl –p` + 4. Verify that the swap memory is disabled by running: + ```sh + [root@worker-1~]# free -m + ..... + Swap: 0 0 0 + ``` + 5. Enable kernel parameters at the Kubelet level, so that kernel parameters can be set at the Pod level. This is a one-time activity. + * In the `/etc/systemd/system/kubelet.service.d/00-default.conf` file of OKE Worker nodes, add below environment variable: + ```txt + Environment="KUBELET_EXTRA_ARGS=--fail-swap-on=false --allowed-unsafe-sysctls='kernel.shm*,net.*,kernel.sem'" + ``` + * Reload Configurations: `# systemctl daemon-reload` + * Restart Kubelet: `# systemctl restart kubelet` + * Check the Kubelet status: `# systemctl status kubelet` + + **Note: For openshift worker nodes**, path to edit is `/etc/systemd/system/kubelet.service.d/99-kubelet-extra-args.conf` and add below content in this file: + ```txt + [Service] + Environment="KUBELET_EXTRA_ARGS=--fail-swap-on=false --allowed-unsafe-sysctls='kernel.shm*,net.*,kernel.sem'" + ``` + * Reload Configurations: `# systemctl daemon-reload` + * Restart Kubelet: `# systemctl restart kubelet` + * Check the Kubelet status: `# systemctl status kubelet` + + 6. Create the mount points on the worker node to be mounted to Oracle Restart Pod for Oracle GI + RDBMS HOME and the Software Staging Location + * On worker node: + + `# mkdir -p /scratch/orestart/` + + `# mkdir -p /scratch/software/stage` + + 7. Download the Oracle Grid Infrastructure and Oracle RDBMS Software .zip files. Copy those files to the worker node at the staging location `/scratch/software/stage/` + + ##### Set up SELinux Module on Worker Nodes + Traditional Unix security uses discretionary access control (DAC). SELinux is an example of mandatory access control. SELinux restricts many commands from the Oracle Restart Pod that are not allowed to run, which results in permission denied errors. To avoid such errors, you must create an SELinux policy package to allow certain commands. + + To set up the SELinux module on the worker node, follow these steps as `root` user to create an SELinux policy package on the worker node: + + 1. Verify that SELinux is enabled on the Worker node. For example: + ```sh + [root@worker-1]# getenforce + enforcing + ``` + 2. Install the SELinux devel package on the Worker nodes: `[root@worker-1]# dnf install selinux-policy-devel` + 3. Create a file `oradb-oke.te` under `/var/opt` on the Worker nodes with the below content: + ```sh + module oradb-oke 1.0; + + require { + type kernel_t; + class system syslog_read; + type container_runtime_t; + type container_init_t; + class file getattr; + type container_file_t; + type lib_t; + type textrel_shlib_t; + type bin_t; + class file { execmod execute map setattr }; + } + + #============= container_init_t ============== + allow container_init_t container_runtime_t:file getattr; + allow container_init_t bin_t:file map; + allow container_init_t bin_t:file execute; + allow container_init_t container_file_t:file execmod; + allow container_init_t lib_t:file execmod; + allow container_init_t textrel_shlib_t:file setattr; + allow container_init_t kernel_t:system syslog_read; + ``` +4. Create and install the policy package: + * `# cd /var/opt` + * `make -f /usr/share/selinux/devel/Makefile oradb-oke.pp` + * `semodule -i oradb-oke.pp` + * `semodule -l | grep oradb-oke` +5. Configure the SELinux context for the required worker node directory and files: + * On worker-1: + + `# semanage fcontext -a -t container_file_t /scratch/orestart` + + `# sudo restorecon -vF /scratch/orestart` + + `# semanage fcontext -a -t container_file_t /scratch/software/stage/grid_home.zip` + + `# sudo restorecon -vF /scratch/software/stage/grid_home.zip` + + `# semanage fcontext -a -t container_file_t /scratch/software/stage/db_home.zip` + + `# sudo restorecon -vF /scratch/software/stage/db_home.zip` + +**Note**: Change these paths and file names as per location of your environment for setting Oracle Restart and names of the software .zip files. + +**Note**: To use Oracle Restart Database Controller, ensure that your system is provisioned with a supported Kubernetes release. Refer to the [Release Status Section](../../README.md#release-status). + +### Create a namespace for the Oracle Restart Setup + +Create a Kubernetes namespace named `orestart`. All the resources belonging to the Oracle Restart Database will be provisioned in this namespace named `orestart`. For example: + + ```sh + #### Create the namespace + kubectl create ns orestart + + #### Check the created namespace + kubectl get ns orestart + ``` +If you want, you can choose any name for the namespace to deploy Oracle Restart Database. + +### Install Cert Manager and Setup Access Permissions + +Before using Oracle Restart Database Controller, ensure you have completed the prerequisites which includes: + +* The installation of cert-manager +* Creation of Role Bindings for Access Management + +Refer to the section [Prerequisites](../../../README.md#prerequisites) + +### OpenShift Security Context Constraints + +OpenShift requires additional Security Context Constraints (SCC) for deploying and managing the `oraclerestarts.database.oracle.com` resource. To create the appropriate SCCs before deploying the `oraclerestarts.database.oracle.com` resource, complete these steps: + + 1. Apply the file [custom-kubeletconfig.yaml](../../config/samples/orestart/custom-kubeletconfig.yaml) with cluster-admin user privileges. + + ```sh + oc apply -f custom-kubeletconfig.yaml + ``` + Watch the worker MCP update: + ```sh + watch oc get mcp + # Wait for: UPDATING = False ,UPDATED = True ,DEGRADED = False + oc label mcp worker custom-kubelet=enable-unsafe-sysctls + oc get kubeletconfig + NAME AGE + enable-unsafe-sysctls 10m + ``` + + **Note:** OpenShift recommends that you should not deploy in namespaces starting with `kube`, `openshift` and the `default` namespace. + + 2. Create service account to be used for Openshift cluster to be used for `oraclerestarts.database.oracle.com` resource. + ```sh + oc create serviceaccount oraclerestart -n orestart + ``` + Note: We are using `oraclerestart` as service account name, you can change and make sure to use same in yaml file while creating `oraclerestarts.database.oracle.com` resource and step 3 below. + + 3. Apply the file [custom-scc.yaml](../../config/samples/orestart/custom-scc.yaml) with cluster-admin user privileges. + + ```sh + oc apply -f custom-scc.yaml + oc adm policy add-scc-to-user privileged -z oraclerestart -n orestart + ``` + +### Deploy Oracle Database Operator + +After you have completed the prerequisite steps, you can install the operator. To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the previous step. + +```sh +kubectl apply -f oracle-database-operator.yaml +``` + +For more details, please refer to [Install Oracle DB Operator](../../../README.md#install-oracle-db-operator) + +### Oracle Restart Database Slim Image +Choose one of the following deployment options: + + **Use Oracle-Supplied Container Images:** + The Oracle Restart Database Controller uses Oracle Restart Database Slim Image to provision the Oracle Restart Database. + + You can also download the pre-built Oracle Restart Database Slim Image `phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0630`. This image is functionally tested and evaluated with various use cases of Oracle Restart Database on an OKE Kubernetes Cluster. + + You can either download this image and push it to your Container Images Repository, or, if your Kubernetes cluster can reach OCR, you can download this image directly from OCR. + + **OR** + + **Build your own Oracle Restart Database Slim Image:** + You can build this image using instructions provided on Oracle's official OraHub Repositories: + * [Oracle Restart Database Image](https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images) + +After the image is ready, push it to your Container Images Repository, so that you can pull this image during Oracle Restart Database provisioning.. + +**Note**: In the Oracle Restart Database provisioning sample .yaml files, we are using Oracle Restart Database slim image available on `phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0630`. + +### Create a Kubernetes secret for the Oracle Restart Database installation owner for the Oracle Restart Database Deployment + +Create a Kubernetes secret named `db-user-pass` in `orestart` namespace using these steps: [Create Kubernetes Secret](./create_kubernetes_secret_for_db_user.md) + +Create a Kubernetes secret named `ssh-key-secret` in `orestart` namespace using these steps: [Create Kubernetes Secret for SSH Key](./create_kubernetes_secret_for_ssh_setup.md) + +After you have the above prerequsites completed, you can proceed to the next section for your environment to provision the Oracle Restart Database. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md new file mode 100644 index 00000000..85067ba5 --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md @@ -0,0 +1,44 @@ +# Provisioning an Oracle Restart Database + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is +generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. + +This example uses `oraclerestart_prov.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +In this example, + * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). + * When you are building the image yourself, update the image value in the `oraclerestart_prov.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + + +Use the file: [oraclerestart_prov.yaml](./oraclerestart_prov.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_object.txt) +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md new file mode 100644 index 00000000..5b1f0920 --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md @@ -0,0 +1,44 @@ +# Provisioning an Oracle Restart Database with NodePort Service + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is +generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. + +This example uses `oraclerestart_prov_nodeports.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +In this example, + * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). + * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + + +Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_nodeports.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_nodeports.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_nodeport_object.txt) +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md new file mode 100644 index 00000000..2aeb9db4 --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md @@ -0,0 +1,63 @@ +# Provisioning an Oracle Restart Database with RU Patch + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is +generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. + +This example uses `oraclerestart_prov_rupatch.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. +* Directory where the **Release Update (RU) patch** has been unzipped as `ruPatchLocation`. This folder **must contain `PatchSearch.xml`**, which is used by the installer to detect and apply the patch. Example: `/scratch/software/19c/19.28/` + +In this example, + * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch.yaml` file to point to your own container registry base container image. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + + +Use the file: [oraclerestart_prov_rupatch.yaml](./oraclerestart_prov_rupatch.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_rupatch.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_rupatch.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_rupatch_object.txt) +4. In this case, the port 1521 from the pod is mapped to port 30007 on the worker node. To make the connection from outside, you will need to open the port 30007 on the worker node for INGRESS. + +Once done, you will be able to make an SQLPLUS database connection to this Oracle Restart Database from a remote client as below +```sh +bash-4.4$ sqlplus system/Oracle_23ai@//129.146.0.149:30007/PORCLCDB + +SQL*Plus: Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems on Sat Jul 19 04:02:48 2025 +Version 23.9.0.25.09 +Copyright (c) 1982, 2025, Oracle. All rights reserved. +Last Successful login time: Sat Jul 19 2025 00:20:14 +00:00 +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 +SQL> +SQL> set lines 200 +SQL> col HOST_NAME format a40 +SQL> select INSTANCE_NAME,HOST_NAME, DATABASE_TYPE from v$instance; +INSTANCE_NAME HOST_NAME DATABASE_TYPE +---------------- ---------------------------------------- --------------- +PORCLCDB dbmc1-0 SINGLE +``` diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md new file mode 100644 index 00000000..7e697aef --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md @@ -0,0 +1,42 @@ +# Provisioning an Oracle Restart Database with Custom Storage Class + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller with Custom Storage Class. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. + +This example uses `oraclerestart_prov_storage.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. +* Name of Custom Storage Class is specified by `storageClass`. + + +In this example, + * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_storage.yaml` file to point to your own container registry base container image. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + + +Use the file: [oraclerestart_prov_storage.yaml](./oraclerestart_prov_storage.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_storage.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_storage.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_storage_object.txt) diff --git a/go.mod b/go.mod index 863f2e99..91ddd1d4 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,11 @@ module github.com/oracle/oracle-database-operator -go 1.23.3 +go 1.24 require ( github.com/go-logr/logr v1.4.2 + github.com/go-logr/zapr v1.3.0 + github.com/natefinch/lumberjack v2.0.0+incompatible github.com/onsi/ginkgo/v2 v2.20.2 github.com/onsi/gomega v1.34.2 github.com/oracle/oci-go-sdk/v65 v65.77.1 @@ -35,7 +37,6 @@ require ( github.com/fatih/camelcase v1.0.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect diff --git a/go.sum b/go.sum index d23debb8..6cfebee3 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= @@ -140,6 +141,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= @@ -293,6 +296,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go index 1b3db6d2..bbd3bfd4 100644 --- a/main.go +++ b/main.go @@ -43,10 +43,13 @@ import ( "flag" "fmt" "os" + "path/filepath" "strconv" "strings" "time" + "github.com/go-logr/zapr" + "github.com/natefinch/lumberjack" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "go.uber.org/zap/zapcore" @@ -58,7 +61,10 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log/zap" + + uberzap "go.uber.org/zap" + "sigs.k8s.io/controller-runtime/pkg/log" + ctrlzap "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" @@ -74,8 +80,7 @@ import ( ) var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") + scheme = runtime.NewScheme() ) func init() { @@ -99,12 +104,78 @@ func main() { flag.Parse() // Initialize new logger Opts - options := &zap.Options{ + options := &ctrlzap.Options{ Development: true, TimeEncoder: zapcore.RFC3339TimeEncoder, } - ctrl.SetLogger(zap.New(func(o *zap.Options) { *o = *options })) + ctrl.SetLogger(ctrlzap.New(func(o *ctrlzap.Options) { *o = *options })) + + var logFilePath string + // Set log directory from environment variable or default to /tmp + logDir := os.Getenv("LOG_DIR") + if logDir == "" { + logDir = "/tmp" + } + // Create log file name with date + currentDate := time.Now().Format("2006-01-02") + logFilePath = filepath.Join(logDir, fmt.Sprintf("controller-%s.log", currentDate)) + // Create symlink "controller.log" -> current date log file + symlinkPath := filepath.Join(logDir, "controller.log") + // Remove existing symlink if it exists + os.Remove(symlinkPath) + // Create new symlink + err := os.Symlink(logFilePath, symlinkPath) + if err != nil { + fmt.Printf("Failed to create symlink: %v\n", err) + } + + opts := ctrlzap.Options{ + Development: true, + } + + opts.BindFlags(flag.CommandLine) + flag.Parse() + + encoderConfig := zapcore.EncoderConfig{ + TimeKey: "timestamp", + LevelKey: "level", + NameKey: "logger", + CallerKey: "caller", + MessageKey: "message", + StacktraceKey: "stacktrace", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.CapitalLevelEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, + EncodeDuration: zapcore.StringDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } + + // File encoder (JSON) + fileEncoder := zapcore.NewJSONEncoder(encoderConfig) + + // Use lumberjack for log rotation + logFileWriter := &lumberjack.Logger{ + Filename: logFilePath, + MaxSize: 100, // megabytes + MaxBackups: 90, // number of old log files to keep + MaxAge: 30, // days to retain old log files + Compress: true, // compress old log files + } + + writer := zapcore.AddSync(logFileWriter) + + core := zapcore.NewTee( + zapcore.NewCore(fileEncoder, writer, zapcore.DebugLevel), + zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(os.Stdout), zapcore.DebugLevel), + ) + + // Create logger + logger := uberzap.New(core, uberzap.AddCaller(), uberzap.Development()) + + // Use logger + log.SetLogger(zapr.NewLogger(logger)) + setupLog := logger.Sugar() watchNamespaces, err := getWatchNamespace() if err != nil { @@ -133,10 +204,12 @@ func main() { // Get Cache cache := mgr.GetCache() + logger.Info("Logger initialized and writing to both file and stdout") + // ADB family controllers if err = (&databasecontroller.AutonomousDatabaseReconciler{ KubeClient: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("database").WithName("AutonomousDatabase"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("database").WithName("AutonomousDatabase"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("AutonomousDatabase"), }).SetupWithManager(mgr); err != nil { @@ -145,7 +218,7 @@ func main() { } if err = (&databasecontroller.AutonomousDatabaseBackupReconciler{ KubeClient: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseBackup"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("AutonomousDatabaseBackup"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("AutonomousDatabaseBackup"), }).SetupWithManager(mgr); err != nil { @@ -154,16 +227,16 @@ func main() { } if err = (&databasecontroller.AutonomousDatabaseRestoreReconciler{ KubeClient: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseRestore"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("AutonomousDatabaseRestore"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("AutonomousDatabaseRestore"), }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "AutonomousDatabaseRestore") + setupLog.Error("unable to create controller", "controller", "AutonomousDatabaseRestore", "error", err) os.Exit(1) } if err = (&databasecontroller.AutonomousContainerDatabaseReconciler{ KubeClient: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("AutonomousContainerDatabase"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("AutonomousContainerDatabase"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("AutonomousContainerDatabase"), }).SetupWithManager(mgr); err != nil { @@ -173,7 +246,7 @@ func main() { if err = (&databasecontroller.SingleInstanceDatabaseReconciler{ Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("database").WithName("SingleInstanceDatabase"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("database").WithName("SingleInstanceDatabase"), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), Recorder: mgr.GetEventRecorderFor("SingleInstanceDatabase"), @@ -183,7 +256,7 @@ func main() { } if err = (&databasecontroller.ShardingDatabaseReconciler{ Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("database").WithName("ShardingDatabase"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("database").WithName("ShardingDatabase"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("ShardingDatabase"), }).SetupWithManager(mgr); err != nil { @@ -192,7 +265,7 @@ func main() { } if err = (&databasecontroller.DbcsSystemReconciler{ KubeClient: mgr.GetClient(), - Logger: ctrl.Log.WithName("controllers").WithName("database").WithName("DbcsSystem"), + Logger: zapr.NewLogger(logger).WithName("controllers").WithName("database").WithName("DbcsSystem"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("DbcsSystem"), }).SetupWithManager(mgr); err != nil { @@ -201,7 +274,7 @@ func main() { } if err = (&databasecontroller.OracleRestDataServiceReconciler{ Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("OracleRestDataService"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("OracleRestDataService"), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), Recorder: mgr.GetEventRecorderFor("OracleRestDataService"), @@ -210,6 +283,16 @@ func main() { os.Exit(1) } + if err = (&databasecontroller.OracleRestartReconciler{ + Client: mgr.GetClient(), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("OracleRestart"), + Scheme: mgr.GetScheme(), + Config: mgr.GetConfig(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "OracleRestart") + os.Exit(1) + } + // Set RECONCILE_INTERVAL environment variable if you want to change the default value from 15 secs interval := os.Getenv("RECONCILE_INTERVAL") i, err := strconv.ParseInt(interval, 10, 64) @@ -324,13 +407,17 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") os.Exit(1) } + if err = (&databasev4.OracleRestart{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestart") + os.Exit(1) + } } // PDB Reconciler if err = (&databasecontroller.PDBReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("PDB"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("PDB"), Interval: time.Duration(i), Recorder: mgr.GetEventRecorderFor("PDB"), }).SetupWithManager(mgr); err != nil { @@ -342,7 +429,7 @@ func main() { if err = (&databasecontroller.LRPDBReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("LRPDB"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("LRPDB"), Interval: time.Duration(i), Recorder: mgr.GetEventRecorderFor("LRPDB"), }).SetupWithManager(mgr); err != nil { @@ -355,7 +442,7 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), - Log: ctrl.Log.WithName("controllers").WithName("CDB"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("CDB"), Interval: time.Duration(i), Recorder: mgr.GetEventRecorderFor("CDB"), }).SetupWithManager(mgr); err != nil { @@ -368,7 +455,7 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), - Log: ctrl.Log.WithName("controllers").WithName("LREST"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("LREST"), Interval: time.Duration(i), Recorder: mgr.GetEventRecorderFor("LREST"), }).SetupWithManager(mgr); err != nil { @@ -378,7 +465,7 @@ func main() { if err = (&dataguardcontroller.DataguardBrokerReconciler{ Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("dataguard").WithName("DataguardBroker"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("dataguard").WithName("DataguardBroker"), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), Recorder: mgr.GetEventRecorderFor("DataguardBroker"), @@ -399,13 +486,14 @@ func main() { // Observability DatabaseObserver Reconciler if err = (&observabilitycontroller.DatabaseObserverReconciler{ Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("observability").WithName("DatabaseObserver"), + Log: zapr.NewLogger(logger).WithName("controllers").WithName("observability").WithName("DatabaseObserver"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("DatabaseObserver"), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "DatabaseObserver") os.Exit(1) } + // +kubebuilder:scaffold:builder // Add index for PDB CR to enable mgr to cache PDBs diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index dc06e1c8..cce2cc75 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10375,6 +10375,1426 @@ spec: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: oraclerestarts.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestart + listKind: OracleRestartList + plural: oraclerestarts + singular: oraclerestart + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.configParams.dbName + name: DbName + type: string + - jsonPath: .status.dbState + name: DbState + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + type: string + - jsonPath: .status.state + name: State + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + asmStorageDetails: + properties: + autoUpdate: + type: string + disksBySize: + items: + properties: + diskNames: + items: + type: string + type: array + storageSizeInGb: + type: integer + type: object + type: array + type: object + configParams: + properties: + cpuCount: + type: integer + crsAsmDeviceList: + type: string + crsAsmDiskDg: + type: string + crsAsmDiskDgRedundancy: + type: string + dbAsmDeviceList: + type: string + dbAsmDiskDgRedundancy: + type: string + dbBase: + type: string + dbCharSet: + type: string + dbConfigType: + type: string + dbDataFileDestDg: + type: string + dbHome: + type: string + dbName: + type: string + dbRecoveryFileDest: + type: string + dbRecoveryFileDestSize: + type: string + dbRedoFileSize: + type: string + dbResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + dbStorageType: + type: string + dbSwZipFile: + type: string + dbType: + type: string + dbUniqueName: + type: string + enableArchiveLog: + type: string + gridBase: + type: string + gridHome: + type: string + gridResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + gridSwZipFile: + type: string + hostSwStageLocation: + type: string + inventory: + type: string + oPatchLocation: + type: string + oPatchSwZipFile: + type: string + opType: + type: string + pdbName: + type: string + pgaSize: + type: string + processes: + type: integer + recoAsmDeviceList: + type: string + recoAsmDiskDgRedudancy: + type: string + redoAsmDeviceList: + type: string + redoAsmDiskDgRedundancy: + type: string + ruFolderName: + type: string + ruPatchLocation: + type: string + sgaSize: + type: string + stagingSoftwareLocation: + type: string + swMountLocation: + type: string + type: object + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + externalSvcType: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecret: + type: string + instDetails: + properties: + envFile: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + hostSwLocation: + type: string + isDelete: + type: string + isForceDelete: + type: string + isKeepPVC: + type: string + label: + type: string + name: + type: string + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + svcType: + type: string + type: object + type: array + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + workerNode: + items: + type: string + type: array + required: + - name + type: object + isDebug: + type: string + isDeleteOraPvc: + type: string + isDeleteTopology: + type: string + isFailed: + type: boolean + isManual: + type: boolean + nfsStorageDetails: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + scriptsGetCmd: + type: string + scriptsLocation: + type: string + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccountName: + type: string + serviceDetails: + properties: + available: + items: + type: string + type: array + cardinality: + type: string + clbGoal: + type: string + commitOutComeFastPath: + type: string + commitOutcome: + type: string + drainTimeOut: + type: integer + dtp: + type: string + edition: + type: string + failBack: + type: string + failOverDelay: + type: integer + failOverRestore: + type: string + failOverRetry: + type: integer + failOverType: + type: string + name: + type: string + notification: + type: string + pdb: + type: string + preferred: + items: + type: string + type: array + retenion: + type: integer + rlbGoal: + type: string + role: + type: string + sessionState: + type: string + stopOption: + type: string + svcState: + type: string + tafPolicy: + type: string + required: + - name + type: object + sshKeySecret: + properties: + keyMountLocation: + type: string + name: + type: string + privKeySecretName: + type: string + pubKeySecretName: + type: string + required: + - name + type: object + storageClass: + type: string + storageSizeInGB: + type: integer + tdeWalletSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: + type: string + required: + - instDetails + - securityContext + type: object + status: + properties: + DbName: + type: string + OracleRestartNodes: + items: + properties: + name: + type: string + nodeDetails: + properties: + InstanceState: + type: string + PodState: + type: string + clusterState: + type: string + isDelete: + type: string + mountedDevices: + items: + type: string + type: array + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + svcType: + type: string + type: object + type: array + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + state: + type: string + workerNode: + type: string + type: object + type: object + type: array + asmDetails: + properties: + diskgroup: + items: + properties: + disks: + items: + type: string + type: array + name: + type: string + redundancy: + type: string + type: object + type: array + type: object + clientEtcHost: + items: + type: string + type: array + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + configParams: + properties: + cpuCount: + type: integer + crsAsmDeviceList: + type: string + crsAsmDiskDg: + type: string + crsAsmDiskDgRedundancy: + type: string + dbAsmDeviceList: + type: string + dbAsmDiskDgRedundancy: + type: string + dbBase: + type: string + dbCharSet: + type: string + dbConfigType: + type: string + dbDataFileDestDg: + type: string + dbHome: + type: string + dbName: + type: string + dbRecoveryFileDest: + type: string + dbRecoveryFileDestSize: + type: string + dbRedoFileSize: + type: string + dbResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + dbStorageType: + type: string + dbSwZipFile: + type: string + dbType: + type: string + dbUniqueName: + type: string + enableArchiveLog: + type: string + gridBase: + type: string + gridHome: + type: string + gridResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + gridSwZipFile: + type: string + hostSwStageLocation: + type: string + inventory: + type: string + oPatchLocation: + type: string + oPatchSwZipFile: + type: string + opType: + type: string + pdbName: + type: string + pgaSize: + type: string + processes: + type: integer + recoAsmDeviceList: + type: string + recoAsmDiskDgRedudancy: + type: string + redoAsmDeviceList: + type: string + redoAsmDiskDgRedundancy: + type: string + ruFolderName: + type: string + ruPatchLocation: + type: string + sgaSize: + type: string + stagingSoftwareLocation: + type: string + swMountLocation: + type: string + type: object + connectString: + type: string + dbSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + dbState: + type: string + externalSvcType: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecret: + type: string + instDetails: + properties: + envFile: + type: string + envVars: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + hostSwLocation: + type: string + isDelete: + type: string + isForceDelete: + type: string + isKeepPVC: + type: string + label: + type: string + name: + type: string + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + svcType: + type: string + type: object + type: array + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + workerNode: + items: + type: string + type: array + required: + - name + type: object + installNode: + type: string + isDebug: + type: string + isDeleteOraPvc: + type: string + isDeleteTopology: + type: string + nfsStorageDetails: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + oldSpec: + type: string + pdbConnectString: + type: string + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + releaseUpdate: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + role: + type: string + scriptsGetCmd: + type: string + scriptsLocation: + type: string + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceDetails: + properties: + available: + items: + type: string + type: array + cardinality: + type: string + clbGoal: + type: string + commitOutComeFastPath: + type: string + commitOutcome: + type: string + drainTimeOut: + type: integer + dtp: + type: string + edition: + type: string + failBack: + type: string + failOverDelay: + type: integer + failOverRestore: + type: string + failOverRetry: + type: integer + failOverType: + type: string + name: + type: string + notification: + type: string + pdb: + type: string + preferred: + items: + type: string + type: array + retenion: + type: integer + rlbGoal: + type: string + role: + type: string + sessionState: + type: string + stopOption: + type: string + svcState: + type: string + tafPolicy: + type: string + required: + - name + type: object + sshKeySecret: + properties: + keyMountLocation: + type: string + name: + type: string + privKeySecretName: + type: string + pubKeySecretName: + type: string + required: + - name + type: object + state: + type: string + storageClass: + type: string + storageSizeInGB: + type: integer + tdeWalletSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert @@ -13733,6 +15153,77 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: oracle-database-operator + name: oracle-database-operator-database-oraclerestart-admin-role +rules: +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts + verbs: + - '*' +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: oracle-database-operator + name: oracle-database-operator-database-oraclerestart-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: oracle-database-operator + name: oracle-database-operator-database-oraclerestart-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestarts/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole metadata: name: oracle-database-operator-manager-role rules: @@ -13742,9 +15233,11 @@ rules: - configmaps - containers - deployments + - endpoints - events - namespaces - persistentvolumeclaims + - persistentvolumes - pods - pods/exec - pods/log @@ -13771,14 +15264,6 @@ rules: - get - patch - update -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch - apiGroups: - "" resources: @@ -13841,6 +15326,7 @@ rules: - events - lrests - lrpdbs + - oraclerestarts - oraclerestdataservices - ordssrvs - pdbs @@ -13865,6 +15351,7 @@ rules: - dbcssystems/status - lrests/status - lrpdbs/status + - oraclerestarts/status - oraclerestdataservices/status - ordssrvs/status - pdbs/status @@ -13909,6 +15396,7 @@ rules: resources: - dbcssystems/finalizers - lrpdbs/finalizers + - oraclerestarts/finalizers - pdbs/finalizers - shardingdatabases/finalizers verbs: @@ -14193,6 +15681,26 @@ webhooks: resources: - lrpdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-database-oracle-com-v4-oraclerestart + failurePolicy: Fail + name: moraclerestart.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - oraclerestarts + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -14508,6 +16016,27 @@ webhooks: resources: - lrpdbs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-oraclerestart + failurePolicy: Fail + name: voraclerestart.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - oraclerestarts + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -14761,7 +16290,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: controller:latest + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:restart-operator-sa imagePullPolicy: Always name: manager ports: From 71c01150f8ec4f5038ccb228e8744fe7976b449a Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Mon, 28 Jul 2025 09:53:18 +0000 Subject: [PATCH 228/414] Psaini basedb v4 branch --- .gitlab-ci.yml | 2 +- README.md | 67 ++ apis/database/v4/dbcssystem_types.go | 147 ++- apis/database/v4/dbcssystem_webhook.go | 49 +- apis/database/v4/zz_generated.deepcopy.go | 257 ++++- commons/dbcssystem/dbcs_reconciler.go | 996 ++++++++++++++++-- commons/dbcssystem/dcommon.go | 28 +- .../database.oracle.com_dbcssystems.yaml | 157 ++- config/manager/kustomization.yaml | 2 +- config/webhook/manifests.yaml | 40 - controllers/database/dbcssystem_controller.go | 822 ++++++++++++--- docs/dbcs/README.md | 12 +- .../backup_database_sample_output.log | 100 ++ docs/dbcs/provisioning/backup_of_database.md | 35 + .../dbcs/provisioning/backup_of_database.yaml | 11 + ..._dbcs_system_from_backup_sample_output.log | 66 +- ...bcs_system_from_database_sample_output.log | 66 +- .../clone_dbcs_system_sample_output.log | 63 ++ .../provisioning/dataguard_in_database.yaml | 20 + .../dataguard_in_database_sample_output.log | 226 ++++ .../provisioning/dataguard_to_database.md | 33 + .../disable_dataguard_in_database.yaml | 12 + ...le_dataguard_in_database_sample_output.log | 141 +++ .../disable_dataguard_to_database.md | 33 + .../disabled_dataguard_to_database.md | 0 docs/dbcs/provisioning/patch_dbcs_system.yaml | 11 + .../patch_dbcs_system_sample_output.log | 0 docs/dbcs/provisioning/patching_database.md | 42 + .../restore_database_sample_output.log | 22 + docs/dbcs/provisioning/restore_of_database.md | 34 + .../provisioning/restore_of_database.yaml | 11 + .../scale_up_dbcs_system_shape.yaml | 12 +- .../provisioning/upgrade_dbcs_system.yaml | 12 + .../upgrade_dbcs_system_sample_output.log | 2 + docs/dbcs/provisioning/upgrading_database.md | 43 + oracle-database-operator.yaml | 203 +++- 36 files changed, 3389 insertions(+), 388 deletions(-) create mode 100644 docs/dbcs/provisioning/backup_database_sample_output.log create mode 100644 docs/dbcs/provisioning/backup_of_database.md create mode 100644 docs/dbcs/provisioning/backup_of_database.yaml create mode 100644 docs/dbcs/provisioning/dataguard_in_database.yaml create mode 100644 docs/dbcs/provisioning/dataguard_in_database_sample_output.log create mode 100644 docs/dbcs/provisioning/dataguard_to_database.md create mode 100644 docs/dbcs/provisioning/disable_dataguard_in_database.yaml create mode 100644 docs/dbcs/provisioning/disable_dataguard_in_database_sample_output.log create mode 100644 docs/dbcs/provisioning/disable_dataguard_to_database.md create mode 100644 docs/dbcs/provisioning/disabled_dataguard_to_database.md create mode 100644 docs/dbcs/provisioning/patch_dbcs_system.yaml create mode 100644 docs/dbcs/provisioning/patch_dbcs_system_sample_output.log create mode 100644 docs/dbcs/provisioning/patching_database.md create mode 100644 docs/dbcs/provisioning/restore_database_sample_output.log create mode 100644 docs/dbcs/provisioning/restore_of_database.md create mode 100644 docs/dbcs/provisioning/restore_of_database.yaml create mode 100644 docs/dbcs/provisioning/upgrade_dbcs_system.yaml create mode 100644 docs/dbcs/provisioning/upgrade_dbcs_system_sample_output.log create mode 100644 docs/dbcs/provisioning/upgrading_database.md diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d2cbc132..dff89e54 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,4 +35,4 @@ cleanup: stage: .post script: - echo "Clean up downloaded binaries" - - rm -rf bin/ + - rm -rf bin/ \ No newline at end of file diff --git a/README.md b/README.md index 2817ba72..1e96bec6 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,43 @@ In this v1.2.0 production release, `OraOperator` supports the following database * Oracle Multitenant Databases (CDB/PDBs) * Oracle Base Database Service (OBDS) on Oracle Cloud Infrastructure (OCI) * Oracle Data Guard +<<<<<<< HEAD +* Oracle Database Observability +* Oracle Database Rest Service (ORDS) instances + +## New Lifecycle Features in V1.2.0 Release (Controllers Enhancements) +* ORDSSERVICES + - Install on SIDB and ADB + - Provision and Delete ORDS instances +* SIDB + - Oracle Database 23ai Free support + - Oracle Database 23ai Free-lite support + - SIDB resource management + - True Cache support for Free SIDB databases (Preview) + - Observer for FastStartFailover with Data Guard + - Snapshot Standby support in Data Guard setup +* Globally Distributed Database : Support for Oracle Database 23ai Raft replication +* Autonomous Database: support for Database cloning +* Multitenant DB: + - ORDS-based Controller: assertive deletion policy. + - New LRES based Controller (ARM & AM) + - PDBs settings with init parameters config map + - Assertive deletion policy. +* Database Observability (preview) + - Support for Database Logs (in addition to Metrics) + - Support for the latest Exporter container images + +* Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation. +* Prometheus label config (bug fix) + +## New Product Features +*The Operator itself, as a product, brings the following new features: +* Published on `operatorhub.io` +* Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) +* Validated on Google Kubernetes Engine + +## Overall Features Summary +======= * Oracle Database Rest Service (ORDS) instances ## New Product Features in V1.2.0 @@ -54,17 +91,25 @@ The Operator itself, as a product, brings the following new features: * Published on `operatorhub.io` * Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) * Validated on Google Kubernetes Engine +>>>>>>> origin/master ## Overall Features Summary +<<<<<<< HEAD +======= As of release v1.2, the Oracle Database Operator for Kubernetes ( OraOperator) supports the following lifecycle operations: +>>>>>>> origin/master * ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning. * ACD: Provision, bind, restart, terminate (soft/hard) * SIDB: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (basic console), Oracle REST Data Service (ORDS) to support REST based SQL, PDB management, SQL Developer Web, Application Express (Apex), Resource management, True Cache, Observer for FastStartFailover (Data Guard), and Snapshot Standby (Data Guard) * ORDS Services: Provision and delete ORDS instances * Globally Distrib. (Sharded): Provision/deploy sharded databases and the shard topology, Add a new shard, Delete an existing shard, Raft replication. +<<<<<<< HEAD +* Oracle Multitenant Database (choice of controller): Bind to a CDB, Create a PDB, Plug a PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB, Assertive deletion policy +======= * Oracle Multitenant Database (choice of controller): Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB, Assertive deletion policy +>>>>>>> origin/master * Oracle Base Database Service (OBDS): Provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License, Cloning, PDB creation, using KMS Vaults on Oracle Cloud Infrastructure (OCI) * Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration * Oracle Database Observability: create, patch, delete `databaseObserver` resources (Logs and Metrics) @@ -82,6 +127,7 @@ The previous releases were tested on the following platforms: * [Oracle Linux Cloud Native Environment(OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) 1.6 * [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) * [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/) +* [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) * [Red Hat OKD](https://www.okd.io/) * [Minikube](https://minikube.sigs.k8s.io/docs/) with version v1.29.0 @@ -141,6 +187,10 @@ Oracle strongly recommends that you ensure your system meets the following [Prer kubectl apply -f oracle-database-operator.yaml ``` +<<<<<<< HEAD + +======= +>>>>>>> origin/master * ### ClusterRole and ClusterRoleBinding for NodePort services To expose services on each node's IP and port (the NodePort), apply the [`node-rbac.yaml`](./rbac/node-rbac.yaml). Note that this step is not required for LoadBalancer services. @@ -157,12 +207,15 @@ Oracle strongly recommends that you ensure your system meets the following [Prer To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the preceding step. +<<<<<<< HEAD +======= Run the following command ```sh kubectl apply -f oracle-database-operator.yaml ``` +>>>>>>> origin/master Ensure that the operator pods are up and running. For high availability, operator pod replicas are set to a default of 3. You can scale this setting up or down. ```sh @@ -199,8 +252,11 @@ The following quickstarts are designed for specific database configurations: * [Containerized Oracle Globally Distributed Database](./docs/sharding/README.md) * [Oracle Multitenant Database](./docs/multitenant/README.md) * [Oracle Base Database Service (OBDS)](./docs/dbcs/README.md) +<<<<<<< HEAD +======= * [ORDS Services (ORDSSRVS)](./docs/ordsservices/README.md) * [Oracle Restart Database](./docs/oraclerestart/README.md) +>>>>>>> origin/master The following quickstart is designed for non-database configurations: @@ -303,8 +359,19 @@ The following is an example of a YAML file fragment for specifying Oracle Cloud ``` Examples in this repository where passwords are entered on the command line are for demonstration purposes only. +<<<<<<< HEAD + +### Reporting a Security Issue + +See [Reporting security vulnerabilities](./SECURITY.md) +======= +>>>>>>> origin/master ## License Copyright (c) 2022, 2025 Oracle and/or its affiliates. +<<<<<<< HEAD +Released under the Universal Permissive License v1.0 as shown at [https://oss.oracle.com/licenses/upl/](https://oss.oracle.com/licenses/upl/) +======= Released under the Universal Permissive License v1.0 as shown at [https://oss.oracle.com/licenses/upl/](https://oss.oracle.com/licenses/upl/) +>>>>>>> origin/master diff --git a/apis/database/v4/dbcssystem_types.go b/apis/database/v4/dbcssystem_types.go index 9810a3b7..29535b6a 100644 --- a/apis/database/v4/dbcssystem_types.go +++ b/apis/database/v4/dbcssystem_types.go @@ -53,31 +53,36 @@ import ( // DbcsSystemSpec defines the desired state of DbcsSystem type DbcsSystemSpec struct { - DbSystem DbSystemDetails `json:"dbSystem,omitempty"` - Id *string `json:"id,omitempty"` - OCIConfigMap *string `json:"ociConfigMap"` - OCISecret *string `json:"ociSecret,omitempty"` - DbClone *DbCloneConfig `json:"dbClone,omitempty"` - HardLink bool `json:"hardLink,omitempty"` - PdbConfigs []PDBConfig `json:"pdbConfigs,omitempty"` - SetupDBCloning bool `json:"setupDBCloning,omitempty"` - DbBackupId *string `json:"dbBackupId,omitempty"` - DatabaseId *string `json:"databaseId,omitempty"` - KMSConfig KMSConfig `json:"kmsConfig,omitempty"` + DbSystem *DbSystemDetails `json:"dbSystem,omitempty"` + Id *string `json:"id,omitempty"` + OCIConfigMap *string `json:"ociConfigMap"` + OCISecret *string `json:"ociSecret,omitempty"` + DbClone *DbCloneConfig `json:"dbClone,omitempty"` + HardLink bool `json:"hardLink,omitempty"` + PdbConfigs []PDBConfig `json:"pdbConfigs,omitempty"` + SetupDBCloning bool `json:"setupDBCloning,omitempty"` + DbBackupId *string `json:"dbBackupId,omitempty"` + DatabaseId *string `json:"databaseId,omitempty"` + KMSConfig *KMSConfig `json:"kmsConfig,omitempty"` + EnableBackup bool `json:"enableBackup,omitempty"` + IsPatch bool `json:"isPatch,omitempty"` + IsUpgrade bool `json:"isUpgrade,omitempty"` + DataGuard DataGuardConfig `json:"dataGuard,omitempty"` } // DbSystemDetails Spec type DbSystemDetails struct { - CompartmentId string `json:"compartmentId"` - AvailabilityDomain string `json:"availabilityDomain"` - SubnetId string `json:"subnetId"` - Shape string `json:"shape"` + CompartmentId string `json:"compartmentId,omitempty"` + AvailabilityDomain string `json:"availabilityDomain,omitempty"` + SubnetId string `json:"subnetId,omitempty"` + Shape string `json:"shape,omitempty"` SshPublicKeys []string `json:"sshPublicKeys,omitempty"` - HostName string `json:"hostName"` + HostName string `json:"hostName,omitempty"` CpuCoreCount int `json:"cpuCoreCount,omitempty"` FaultDomains []string `json:"faultDomains,omitempty"` DisplayName string `json:"displayName,omitempty"` + BackupDisplayName string `json:"backupDisplayName,omitempty"` BackupSubnetId string `json:"backupSubnetId,omitempty"` TimeZone string `json:"timeZone,omitempty"` NodeCount *int `json:"nodeCount,omitempty"` @@ -85,8 +90,9 @@ type DbSystemDetails struct { Domain string `json:"domain,omitempty"` InitialDataStorageSizeInGB int `json:"initialDataStorageSizeInGB,omitempty"` ClusterName string `json:"clusterName,omitempty"` - DbAdminPasswordSecret string `json:"dbAdminPasswordSecret"` + DbAdminPasswordSecret string `json:"dbAdminPasswordSecret,omitempty"` DbName string `json:"dbName,omitempty"` + DbHomeId string `json:"dbHomeId,omitempty"` PdbName string `json:"pdbName,omitempty"` DbDomain string `json:"dbDomain,omitempty"` DbUniqueName string `json:"dbUniqueName,omitempty"` @@ -98,8 +104,31 @@ type DbSystemDetails struct { LicenseModel string `json:"licenseModel,omitempty"` TdeWalletPasswordSecret string `json:"tdeWalletPasswordSecret,omitempty"` Tags map[string]string `json:"tags,omitempty"` - DbBackupConfig Backupconfig `json:"dbBackupConfig,omitempty"` - KMSConfig KMSConfig `json:"kmsConfig,omitempty"` + DbBackupConfig *Backupconfig `json:"dbBackupConfig,omitempty"` + KMSConfig *KMSConfig `json:"kmsConfig,omitempty"` + RestoreConfig *RestoreConfig `json:"restoreConfig,omitempty"` + PatchOCID string `json:"dbPatchOcid,omitempty"` + UpgradeVersion string `json:"dbUpgradeVersion,omitempty"` +} + +type DataGuardConfig struct { + Enabled bool `json:"enabled,omitempty"` + ProtectionMode *string `json:"protectionMode,omitempty"` // Options: "MAXIMUM_PROTECTION", "MAXIMUM_AVAILABILITY", "MAXIMUM_PERFORMANCE" + TransportType *string `json:"transportType,omitempty"` // Options: "ASYNC", "SYNC" + PeerRole *string `json:"peerRole,omitempty"` // Options: "STANDBY", "PRIMARY" + DbAdminPasswordSecret *string `json:"dbAdminPasswordSecret,omitempty"` + DbName *string `json:"dbName,omitempty"` + HostName *string `json:"hostName,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + PeerSidPrefix *string `json:"sidPrefix,omitempty"` + PeerDbSystemId *string `json:"peerDbSystemId,omitempty"` + PeerDbHomeId *string `json:"peerDbHomeId,omitempty"` + PrimaryDatabaseId *string `json:"primaryDatabaseId,omitempty"` + AvailabilityDomain *string `json:"availabilityDomain,omitempty"` // Availability domain for the new DB system + SubnetId *string `json:"subnetId,omitempty"` // Subnet ID for the new DB system + Shape *string `json:"shape,omitempty"` // Shape of the new DB system + DbSystemFreeformTags map[string]string `json:"dbSystemFreeformTags,omitempty"` + IsDelete bool `json:"isDelete,omitempty"` } // DB Backup Config Network Struct @@ -110,6 +139,19 @@ type Backupconfig struct { BackupDestinationDetails *string `json:"backupDestinationDetails,omitempty"` } +// Manual backup information +type BackupInfo struct { + Name string `json:"name"` + BackupID string `json:"backupId"` + Timestamp string `json:"timestamp"` // Optional: for sorting, audit, GC +} + +type RestoreConfig struct { + Timestamp *metav1.Time `json:"timestamp,omitempty"` // Restore to specific point in time + SCN *string `json:"scn,omitempty"` // Restore to specific SCN (as string) + Latest bool `json:"latest,omitempty"` // Restore to latest state +} + // DbcsSystemStatus defines the observed state of DbcsSystem type DbcsSystemStatus struct { Id *string `json:"id,omitempty"` @@ -135,15 +177,20 @@ type DbcsSystemStatus struct { KMSDetailsStatus KMSDetailsStatus `json:"kmsDetailsStatus,omitempty"` DbCloneStatus DbCloneStatus `json:"dbCloneStatus,omitempty"` PdbDetailsStatus []PDBDetailsStatus `json:"pdbDetailsStatus,omitempty"` + DataGuardStatus DataGuardStatus `json:"dataGuardStatus,omitempty"` + Backups []BackupInfo `json:"backups,omitempty"` + Message string `json:"message,omitempty"` } // DbcsSystemStatus defines the observed state of DbcsSystem type DbStatus struct { - Id *string `json:"id,omitempty"` - DbName string `json:"dbName,omitempty"` - DbUniqueName string `json:"dbUniqueName,omitempty"` - DbWorkload string `json:"dbWorkload,omitempty"` - DbHomeId string `json:"dbHomeId,omitempty"` + Id *string `json:"id,omitempty"` + DbName string `json:"dbName,omitempty"` + DbUniqueName string `json:"dbUniqueName,omitempty"` + DbWorkload string `json:"dbWorkload,omitempty"` + DbHomeId string `json:"dbHomeId,omitempty"` + ConnectionString string `json:"connectionString,omitempty"` + ConnectionStringLong string `json:"connectionStringLong,omitempty"` } type DbWorkrequests struct { @@ -184,27 +231,52 @@ type DbCloneConfig struct { PrivateIp string `json:"privateIp,omitempty"` } +type DataGuardStatus struct { + Id *string `json:"id,omitempty"` + IsActiveDataGuardEnabled bool `json:"isActiveDataGuardEnabled,omitempty"` + PeerDbSystemId *string `json:"peerDbSystemId,omitempty"` + PeerDatabaseId *string `json:"peerDatabaseId,omitempty"` + DbName *string `json:"dbName,omitempty"` + DbWorkload string `json:"dbWorkload,omitempty"` + PeerDbHomeId *string `json:"peerDbHomeId,omitempty"` + PeerRole *string `json:"peerRole,omitempty"` // Options: "STANDBY", "PRIMARY" + Shape *string `json:"shape,omitempty"` + SubnetId *string `json:"subnetId,omitempty"` + PrimaryDatabaseId *string `json:"primaryDatabaseId,omitempty"` + DbAdminPasswordSecret *string `json:"dbAdminPasswordSecret,omitempty"` + TransportType *string `json:"transportType,omitempty"` + ProtectionMode *string `json:"protectionMode,omitempty"` // Options: "MAXIMUM_PROTECTION", "MAXIMUM_AVAILABILITY", "MAXIMUM_PERFORMANCE" + LifecycleState *string `json:"lifecycleState,omitempty"` + PeerDataGuardAssociationId *string `json:"peerDataGuardAssociationId,omitempty"` + LifecycleDetails *string `json:"lifecycleDetails,omitempty"` +} + // DbCloneStatus defines the observed state of DbClone type DbCloneStatus struct { - Id *string `json:"id,omitempty"` - DbAdminPaswordSecret string `json:"dbAdminPaswordSecret,omitempty"` - DbName string `json:"dbName,omitempty"` - HostName string `json:"hostName"` - DbUniqueName string `json:"dbDbUniqueName"` - DisplayName string `json:"displayName,omitempty"` - LicenseModel string `json:"licenseModel,omitempty"` - Domain string `json:"domain,omitempty"` - SshPublicKeys []string `json:"sshPublicKeys,omitempty"` - SubnetId string `json:"subnetId,omitempty"` + Id *string `json:"id,omitempty"` + DbAdminPasswordSecret string `json:"dbAdminPasswordSecret,omitempty"` + DbName string `json:"dbName,omitempty"` + HostName string `json:"hostName"` + DbUniqueName string `json:"dbDbUniqueName"` + DisplayName string `json:"displayName,omitempty"` + LicenseModel string `json:"licenseModel,omitempty"` + Domain string `json:"domain,omitempty"` + SshPublicKeys []string `json:"sshPublicKeys,omitempty"` + SubnetId string `json:"subnetId,omitempty"` } // +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:resource:path=dbcssystems,scope=Namespaced // +kubebuilder:storageversion -// +kubebuilder:storageversion - -// DbcsSystem is the Schema for the dbcssystems API +// +kubebuilder:printcolumn:name="Display Name",type="string",JSONPath=".status.displayName" +// +kubebuilder:printcolumn:name="DB Name",type="string",JSONPath=".status.dbInfo[0].dbName" +// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.state" +// +kubebuilder:printcolumn:name="OCPUs",type="integer",JSONPath=".status.cpuCoreCount" +// +kubebuilder:printcolumn:name="Storage (TB)",type="integer",JSONPath=".status.dataStorageSizeInGBs" +// +kubebuilder:printcolumn:name="Reco Storage (GB)",type="integer",JSONPath=".status.recoStorageSizeInGB" +// +kubebuilder:printcolumn:name="Storage Mgmt",type="string",JSONPath=".status.storageManagement" +// +kubebuilder:printcolumn:name="ConnString",type="string",JSONPath=".status.dbInfo[0].connectionString" type DbcsSystem struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -229,6 +301,7 @@ const ( Update LifecycleState = "UPDATING" Provision LifecycleState = "PROVISIONING" Terminate LifecycleState = "TERMINATED" + Upgrade LifecycleState = "UPGRADING" ) const lastSuccessfulSpec = "lastSuccessfulSpec" diff --git a/apis/database/v4/dbcssystem_webhook.go b/apis/database/v4/dbcssystem_webhook.go index c3ff8ddb..59f8315b 100644 --- a/apis/database/v4/dbcssystem_webhook.go +++ b/apis/database/v4/dbcssystem_webhook.go @@ -39,7 +39,13 @@ package v4 import ( + "context" + "fmt" + "reflect" + + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" @@ -51,7 +57,9 @@ var dbcssystemlog = logf.Log.WithName("dbcssystem-resource") func (r *DbcsSystem) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). - For(r). + For(&DbcsSystem{}). + WithDefaulter(r). + WithValidator(r). Complete() } @@ -59,38 +67,61 @@ func (r *DbcsSystem) SetupWebhookWithManager(mgr ctrl.Manager) error { //+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-dbcssystem,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=dbcssystems,verbs=create;update,versions=v4,name=mdbcssystemv4.kb.io,admissionReviewVersions=v1 -var _ webhook.Defaulter = &DbcsSystem{} +var _ webhook.CustomDefaulter = &DbcsSystem{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *DbcsSystem) Default() { +func (r *DbcsSystem) Default(ctx context.Context, obj runtime.Object) error { dbcssystemlog.Info("default", "name", r.Name) // TODO(user): fill in your defaulting logic. + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. // +kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv4.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &DbcsSystem{} +var _ webhook.CustomValidator = &DbcsSystem{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *DbcsSystem) ValidateCreate() (admission.Warnings, error) { +func (r *DbcsSystem) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { dbcssystemlog.Info("validate create", "name", r.Name) - // // TODO(user): fill in your validation logic upon object creation. return nil, nil } // // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *DbcsSystem) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *DbcsSystem) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { dbcssystemlog.Info("validate update", "name", r.Name) - // // TODO(user): fill in your validation logic upon object update. + // Type cast the old object to *DbcsSystem + oldDbcs, ok := old.(*DbcsSystem) + if !ok { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast old object to DbcsSystem")) + } + // Block spec updates in non-available states + blockedStates := map[string]bool{ + "UPDATING": true, + "FAILED": true, + } + + if blockedStates[string(r.Status.State)] { + if !reflect.DeepEqual(oldDbcs.Spec, r.Spec) { + return nil, apierrors.NewForbidden( + schema.GroupResource{ + Group: "database.oracle.com", + Resource: "DbcsSystem", + }, + r.Name, + fmt.Errorf("updates to DbcsSystem Spec are not allowed while resource is in state %q", r.Status.State), + ) + } + } + return nil, nil } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *DbcsSystem) ValidateDelete() (admission.Warnings, error) { +func (r *DbcsSystem) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { dbcssystemlog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index b99f2c83..a42aa607 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -745,6 +745,21 @@ func (in *AutonomousDatabaseStatus) DeepCopy() *AutonomousDatabaseStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupInfo) DeepCopyInto(out *BackupInfo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupInfo. +func (in *BackupInfo) DeepCopy() *BackupInfo { + if in == nil { + return nil + } + out := new(BackupInfo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Backupconfig) DeepCopyInto(out *Backupconfig) { *out = *in @@ -1129,6 +1144,188 @@ func (in *DBWalletSecret) DeepCopy() *DBWalletSecret { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataGuardConfig) DeepCopyInto(out *DataGuardConfig) { + *out = *in + if in.ProtectionMode != nil { + in, out := &in.ProtectionMode, &out.ProtectionMode + *out = new(string) + **out = **in + } + if in.TransportType != nil { + in, out := &in.TransportType, &out.TransportType + *out = new(string) + **out = **in + } + if in.PeerRole != nil { + in, out := &in.PeerRole, &out.PeerRole + *out = new(string) + **out = **in + } + if in.DbAdminPasswordSecret != nil { + in, out := &in.DbAdminPasswordSecret, &out.DbAdminPasswordSecret + *out = new(string) + **out = **in + } + if in.DbName != nil { + in, out := &in.DbName, &out.DbName + *out = new(string) + **out = **in + } + if in.HostName != nil { + in, out := &in.HostName, &out.HostName + *out = new(string) + **out = **in + } + if in.DisplayName != nil { + in, out := &in.DisplayName, &out.DisplayName + *out = new(string) + **out = **in + } + if in.PeerSidPrefix != nil { + in, out := &in.PeerSidPrefix, &out.PeerSidPrefix + *out = new(string) + **out = **in + } + if in.PeerDbSystemId != nil { + in, out := &in.PeerDbSystemId, &out.PeerDbSystemId + *out = new(string) + **out = **in + } + if in.PeerDbHomeId != nil { + in, out := &in.PeerDbHomeId, &out.PeerDbHomeId + *out = new(string) + **out = **in + } + if in.PrimaryDatabaseId != nil { + in, out := &in.PrimaryDatabaseId, &out.PrimaryDatabaseId + *out = new(string) + **out = **in + } + if in.AvailabilityDomain != nil { + in, out := &in.AvailabilityDomain, &out.AvailabilityDomain + *out = new(string) + **out = **in + } + if in.SubnetId != nil { + in, out := &in.SubnetId, &out.SubnetId + *out = new(string) + **out = **in + } + if in.Shape != nil { + in, out := &in.Shape, &out.Shape + *out = new(string) + **out = **in + } + if in.DbSystemFreeformTags != nil { + in, out := &in.DbSystemFreeformTags, &out.DbSystemFreeformTags + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataGuardConfig. +func (in *DataGuardConfig) DeepCopy() *DataGuardConfig { + if in == nil { + return nil + } + out := new(DataGuardConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DataGuardStatus) DeepCopyInto(out *DataGuardStatus) { + *out = *in + if in.Id != nil { + in, out := &in.Id, &out.Id + *out = new(string) + **out = **in + } + if in.PeerDbSystemId != nil { + in, out := &in.PeerDbSystemId, &out.PeerDbSystemId + *out = new(string) + **out = **in + } + if in.PeerDatabaseId != nil { + in, out := &in.PeerDatabaseId, &out.PeerDatabaseId + *out = new(string) + **out = **in + } + if in.DbName != nil { + in, out := &in.DbName, &out.DbName + *out = new(string) + **out = **in + } + if in.PeerDbHomeId != nil { + in, out := &in.PeerDbHomeId, &out.PeerDbHomeId + *out = new(string) + **out = **in + } + if in.PeerRole != nil { + in, out := &in.PeerRole, &out.PeerRole + *out = new(string) + **out = **in + } + if in.Shape != nil { + in, out := &in.Shape, &out.Shape + *out = new(string) + **out = **in + } + if in.SubnetId != nil { + in, out := &in.SubnetId, &out.SubnetId + *out = new(string) + **out = **in + } + if in.PrimaryDatabaseId != nil { + in, out := &in.PrimaryDatabaseId, &out.PrimaryDatabaseId + *out = new(string) + **out = **in + } + if in.DbAdminPasswordSecret != nil { + in, out := &in.DbAdminPasswordSecret, &out.DbAdminPasswordSecret + *out = new(string) + **out = **in + } + if in.TransportType != nil { + in, out := &in.TransportType, &out.TransportType + *out = new(string) + **out = **in + } + if in.ProtectionMode != nil { + in, out := &in.ProtectionMode, &out.ProtectionMode + *out = new(string) + **out = **in + } + if in.LifecycleState != nil { + in, out := &in.LifecycleState, &out.LifecycleState + *out = new(string) + **out = **in + } + if in.PeerDataGuardAssociationId != nil { + in, out := &in.PeerDataGuardAssociationId, &out.PeerDataGuardAssociationId + *out = new(string) + **out = **in + } + if in.LifecycleDetails != nil { + in, out := &in.LifecycleDetails, &out.LifecycleDetails + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataGuardStatus. +func (in *DataGuardStatus) DeepCopy() *DataGuardStatus { + if in == nil { + return nil + } + out := new(DataGuardStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DataguardBroker) DeepCopyInto(out *DataguardBroker) { *out = *in @@ -1334,8 +1531,21 @@ func (in *DbSystemDetails) DeepCopyInto(out *DbSystemDetails) { (*out)[key] = val } } - in.DbBackupConfig.DeepCopyInto(&out.DbBackupConfig) - out.KMSConfig = in.KMSConfig + if in.DbBackupConfig != nil { + in, out := &in.DbBackupConfig, &out.DbBackupConfig + *out = new(Backupconfig) + (*in).DeepCopyInto(*out) + } + if in.KMSConfig != nil { + in, out := &in.KMSConfig, &out.KMSConfig + *out = new(KMSConfig) + **out = **in + } + if in.RestoreConfig != nil { + in, out := &in.RestoreConfig, &out.RestoreConfig + *out = new(RestoreConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbSystemDetails. @@ -1435,7 +1645,11 @@ func (in *DbcsSystemList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DbcsSystemSpec) DeepCopyInto(out *DbcsSystemSpec) { *out = *in - in.DbSystem.DeepCopyInto(&out.DbSystem) + if in.DbSystem != nil { + in, out := &in.DbSystem, &out.DbSystem + *out = new(DbSystemDetails) + (*in).DeepCopyInto(*out) + } if in.Id != nil { in, out := &in.Id, &out.Id *out = new(string) @@ -1473,7 +1687,12 @@ func (in *DbcsSystemSpec) DeepCopyInto(out *DbcsSystemSpec) { *out = new(string) **out = **in } - out.KMSConfig = in.KMSConfig + if in.KMSConfig != nil { + in, out := &in.KMSConfig, &out.KMSConfig + *out = new(KMSConfig) + **out = **in + } + in.DataGuard.DeepCopyInto(&out.DataGuard) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemSpec. @@ -1538,6 +1757,12 @@ func (in *DbcsSystemStatus) DeepCopyInto(out *DbcsSystemStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.DataGuardStatus.DeepCopyInto(&out.DataGuardStatus) + if in.Backups != nil { + in, out := &in.Backups, &out.Backups + *out = make([]BackupInfo, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemStatus. @@ -3988,6 +4213,30 @@ func (in *ResponseFile) DeepCopy() *ResponseFile { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RestoreConfig) DeepCopyInto(out *RestoreConfig) { + *out = *in + if in.Timestamp != nil { + in, out := &in.Timestamp, &out.Timestamp + *out = (*in).DeepCopy() + } + if in.SCN != nil { + in, out := &in.SCN, &out.SCN + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreConfig. +func (in *RestoreConfig) DeepCopy() *RestoreConfig { + if in == nil { + return nil + } + out := new(RestoreConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretDetails) DeepCopyInto(out *SecretDetails) { *out = *in diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 6c498320..13f72e5e 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -65,11 +65,13 @@ import ( ) const ( - checkInterval = 30 * time.Second - timeout = 15 * time.Minute + checkInterval = 30 * time.Second + timeout = 15 * time.Minute + PatchHistoryEntrySummaryLifecycleStateInProgress database.PatchHistoryEntrySummaryLifecycleStateEnum = "IN_PROGRESS" + PatchHistoryEntrySummaryLifecycleStateSucceeded database.PatchHistoryEntrySummaryLifecycleStateEnum = "SUCCEEDED" ) -func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient, kmsDetails *databasev4.KMSDetailsStatus) (string, error) { +func CreateAndGetDbcsId(compartmentId string, logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient, kmsDetails *databasev4.KMSDetailsStatus) (string, error) { ctx := context.TODO() // Check if DBCS system already exists using the displayName @@ -104,7 +106,7 @@ func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient d licenceModel := getLicenceModel(dbcs) // Get DB Home Details - dbHomeReq, err := GetDbHomeDetails(kubeClient, dbClient, dbcs) + dbHomeReq, err := GetDbHomeDetails(kubeClient, dbClient, dbcs, "") if err != nil { return "", err } @@ -160,7 +162,7 @@ func CreateAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient d dbcs.Spec.Id = resp.DbSystem.Id // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { return "", statusErr } @@ -194,7 +196,7 @@ func convertLicenseModel(licenseModel database.DbSystemLicenseModelEnum) (databa } } -func CloneAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) (string, error) { +func CloneAndGetDbcsId(compartmentId string, logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) (string, error) { ctx := context.TODO() var err error dbAdminPassword := "" @@ -297,7 +299,7 @@ func CloneAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient da dbcs.Status.DbCloneStatus.Id = response.DbSystem.Id // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { return "", statusErr } @@ -310,9 +312,369 @@ func CloneAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient da return *response.DbSystem.Id, nil // return "", nil } +func PatchDBSystem( + ctx context.Context, + compartmentId string, + logger logr.Logger, + kubeClient client.Client, + dbClient database.DatabaseClient, + dbcs *databasev4.DbcsSystem, + nwClient core.VirtualNetworkClient, + wrClient workrequests.WorkRequestClient, + dbHomeId, patchId string) error { + + dbSystemId := dbcs.Spec.Id + logger.Info("Starting patch process for DB System", "DBSystemID", dbSystemId) + + patchesResp, err := dbClient.ListDbSystemPatches(ctx, database.ListDbSystemPatchesRequest{ + DbSystemId: dbSystemId, + }) + if err != nil { + logger.Error(err, "Failed to list patches for DB System", "DBSystemID", dbSystemId) + return fmt.Errorf("failed to list patches for DB System: %w", err) + } + + found := false + + if len(patchesResp.Items) == 0 { + logger.Info("No patches available for this DB System", "DBSystemID", dbSystemId) + } else { + logger.Info("Available patches for DB System", "count", len(patchesResp.Items)) + for _, patch := range patchesResp.Items { + logger.Info("Patch found", + "PatchID", *patch.Id, + "Description", *patch.Description, + "LifecycleState", patch.LifecycleState, + "ReleaseDate", patch.TimeReleased.String(), + ) + + // Check if patchId matches + if *patch.Id == patchId { + found = true + } + } + } + + if !found { + logger.Error(nil, "Patch ID not found in available patches", "PatchID", patchId) + return fmt.Errorf("patch ID %s not found in available DB System patches", patchId) + } + // Check if patch is already applied or in progress then return + historyResp, err := dbClient.ListDbSystemPatchHistoryEntries(ctx, database.ListDbSystemPatchHistoryEntriesRequest{ + DbSystemId: dbSystemId, + }) + if err != nil { + logger.Error(err, "Failed to get patch history entries", "DBSystemID", dbSystemId) + return fmt.Errorf("failed to get patch history entries: %w", err) + } + + for _, entry := range historyResp.Items { + if entry.PatchId != nil && *entry.PatchId == patchId { + if entry.LifecycleState == database.PatchHistoryEntrySummaryLifecycleStateSucceeded { + logger.Info("Patch already applied, skipping", "PatchID", patchId) + return nil + } + if entry.LifecycleState == database.PatchHistoryEntrySummaryLifecycleStateInProgress { + logger.Info("Patch already in progress, skipping", "PatchID", patchId) + return nil + } + } + } + + updateDetails := database.UpdateDbSystemDetails{ + Version: &database.PatchDetails{ + PatchId: common.String(patchId), + Action: database.PatchDetailsActionApply, + }, + } + + updateReq := database.UpdateDbSystemRequest{ + DbSystemId: dbSystemId, + UpdateDbSystemDetails: updateDetails, + } + + updateResp, err := dbClient.UpdateDbSystem(ctx, updateReq) + if err != nil { + logger.Error(err, "Failed to apply patch to DB System", "PatchID", patchId, "DBSystemID", dbSystemId) + return fmt.Errorf("failed to patch DB System: %w", err) + } + + logger.Info("Patch applied to DB System", "WorkRequestID", *updateResp.OpcWorkRequestId, "PatchID", patchId) + + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + logger.Error(statusErr, "Failed to update lifecycle state to Provisioning") + return statusErr + } + + // Check the state + _, err = CheckResourceState(logger, dbClient, *updateResp.DbSystem.Id, string(databasev4.Provision), string(databasev4.Available)) + if err != nil { + logger.Error(err, "Failed to verify DB System state post patching") + return err + } + + logger.Info("DB System patching process completed successfully", "DBSystemID", dbSystemId) + dbcs.Status.Message = "Patch applied successfully." + + return nil +} +func safeBool(ptr *bool) bool { + if ptr != nil { + return *ptr + } + return false +} +func UpgradeDatabaseVersion( + ctx context.Context, + compartmentId string, + logger logr.Logger, + kubeClient client.Client, + dbClient database.DatabaseClient, + dbcs *databasev4.DbcsSystem, + nwClient core.VirtualNetworkClient, + wrClient workrequests.WorkRequestClient, + databaseId, targetVersion string) error { + dbSystemId := dbcs.Spec.Id + logger.Info("Starting GI upgrade", "DbSystemID", dbSystemId, "TargetGI", targetVersion) + + // Step 1: Get current DB system details + getResp, err := dbClient.GetDbSystem(ctx, database.GetDbSystemRequest{ + DbSystemId: dbSystemId, + }) + if err != nil { + logger.Error(err, "Failed to get DB system details", "DbSystemID", dbSystemId) + return fmt.Errorf("failed to get DB system: %w", err) + } + currentGiVersion := getResp.DbSystem.Version + logger.Info("Current GI version", "CurrentGI", *currentGiVersion) + + // Step 1: Check current GI version + if *currentGiVersion == targetVersion { + logger.Info("GI already at target version. Proceeding...") + // Do NOT return — continue to DB upgrade + } else { + // Step 2: Check for ongoing GI upgrade + workReqsResp, err := wrClient.ListWorkRequests(ctx, workrequests.ListWorkRequestsRequest{ + CompartmentId: common.String(compartmentId), + ResourceId: dbSystemId, + }) + if err != nil { + logger.Error(err, "Failed to list work requests") + return fmt.Errorf("failed to list work requests: %w", err) + } + + for _, wr := range workReqsResp.Items { + if wr.OperationType != nil && *wr.OperationType == "Upgrade Db System" && + (wr.Status == workrequests.WorkRequestSummaryStatusAccepted || + wr.Status == workrequests.WorkRequestSummaryStatusInProgress) { + logger.Info("GI upgrade already in progress", "WorkRequestID", *wr.Id) + dbcs.Status.Message = "GI upgrade already in progress" + return nil // Skip further steps while upgrade is ongoing + } + } + // Step 3: Construct the upgrade request + upgradeReq := database.UpgradeDbSystemRequest{ + DbSystemId: dbSystemId, + UpgradeDbSystemDetails: database.UpgradeDbSystemDetails{ + Action: database.UpgradeDbSystemDetailsActionUpgrade, + NewGiVersion: common.String(targetVersion), + SnapshotRetentionPeriodInDays: common.Int(7), + IsSnapshotRetentionDaysForceUpdated: common.Bool(false), + }, + } + + // Step 4: Call the API + upgradeResp, err := dbClient.UpgradeDbSystem(ctx, upgradeReq) + if err != nil { + logger.Error(err, "Failed to initiate GI upgrade") + dbcs.Status.Message = "Failed to initiate GI upgrade" + + return fmt.Errorf("GI upgrade failed: %w", err) + } + + logger.Info("GI upgrade initiated", "WorkRequestID", *upgradeResp.OpcWorkRequestId) + + // Step 3: Update status to upgrading + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Upgrade, nwClient, wrClient); statusErr != nil { + logger.Error(statusErr, "Failed to update lifecycle state to Upgrading") + dbcs.Status.Message = "Failed to update lifecycle state to Upgrading" + + return statusErr + } + + // Step 4: Check status + _, err = CheckResourceState(logger, dbClient, databaseId, string(databasev4.Provision), string(databasev4.Available)) + if err != nil { + logger.Error(err, "Failed to verify database state post upgrade") + dbcs.Status.Message = "Failed to verify database state post upgrade" + + return err + } + + // Step 5: Wait for completion + workReqId := upgradeResp.OpcWorkRequestId + for { + time.Sleep(30 * time.Second) + + getWorkReqResp, err := wrClient.GetWorkRequest(ctx, workrequests.GetWorkRequestRequest{ + WorkRequestId: workReqId, + }) + if err != nil { + logger.Error(err, "Error fetching work request status", "WorkRequestID", *workReqId) + dbcs.Status.Message = "Error fetching work request status" + return fmt.Errorf("failed to check GI upgrade status: %w", err) + } + + status := getWorkReqResp.WorkRequest.Status + logger.Info("GI upgrade work request status", "Status", status) + + if status == workrequests.WorkRequestStatusSucceeded { + logger.Info("GI upgrade completed successfully") + dbcs.Status.Message = "GI upgrade completed successfully" + + break + } else if status == workrequests.WorkRequestStatusFailed { + logger.Error(nil, "GI upgrade failed", "WorkRequestID", *workReqId) + dbcs.Status.Message = "GI upgrade failed" + + return fmt.Errorf("GI upgrade failed: work request marked as failed") + } + } + } + + // Step 1: Check if DB is already on the target version + dbResp, err := dbClient.GetDatabase(ctx, database.GetDatabaseRequest{ + DatabaseId: common.String(databaseId), + }) + if err != nil { + logger.Error(err, "Failed to fetch database details", "DatabaseID", databaseId) + dbcs.Status.Message = "Failed to fetch database details" + return fmt.Errorf("failed to get database details: %w", err) + } + // fmt.Printf("%+v\n", dbResp.Database) + dbHomeId := *dbResp.Database.DbHomeId + dbHomeResp, err := dbClient.GetDbHome(ctx, database.GetDbHomeRequest{ + DbHomeId: &dbHomeId, + }) + if err != nil { + return fmt.Errorf("failed to get DB Home: %w", err) + dbcs.Status.Message = "Failed to get DB Home" + } + + currentDbVersion := dbHomeResp.DbHome.DbVersion + if currentDbVersion != nil && *currentDbVersion == targetVersion { + logger.Info("Database already at target version. Skipping database upgrade.", "Version", *currentDbVersion) + // Continue to post-upgrade or next steps if any + return nil + } + + // Step 2: Check for ongoing DB upgrade work requests + workReqsRespDb, err := wrClient.ListWorkRequests(ctx, workrequests.ListWorkRequestsRequest{ + CompartmentId: common.String(compartmentId), + ResourceId: common.String(databaseId), + }) + if err != nil { + logger.Error(err, "Failed to list database work requests") + dbcs.Status.Message = "Failed to list database work requests" + return fmt.Errorf("failed to list database work requests: %w", err) + } + + for _, wr := range workReqsRespDb.Items { + if wr.OperationType != nil && *wr.OperationType == "Upgrade Database" && + (wr.Status == workrequests.WorkRequestSummaryStatusAccepted || + wr.Status == workrequests.WorkRequestSummaryStatusInProgress) { + + logger.Info("Database upgrade already in progress, waiting for completion", "WorkRequestID", *wr.Id) + dbcs.Status.Message = "Database upgrade already in progress, waiting for completion." + + // Poll the work request status + for { + time.Sleep(60 * time.Second) + + workReqResp, err := wrClient.GetWorkRequest(ctx, workrequests.GetWorkRequestRequest{ + WorkRequestId: wr.Id, + }) + if err != nil { + logger.Error(err, "Failed to fetch work request status", "WorkRequestID", *wr.Id) + dbcs.Status.Message = "Failed to fetch work request status for database" + return fmt.Errorf("failed to get database upgrade work request status: %w", err) + } + + status := workReqResp.WorkRequest.Status + logger.Info("Database upgrade work request status. Checking again in 60 seconds.", "Status", status, "WorkRequestID", *wr.Id) + + if status == workrequests.WorkRequestStatusSucceeded { + logger.Info("Database upgrade completed successfully", "WorkRequestID", *wr.Id) + break + } else if status == workrequests.WorkRequestStatusFailed { + logger.Error(nil, "Database upgrade failed", "WorkRequestID", *wr.Id) + dbcs.Status.Message = "Database upgrade failed" + return fmt.Errorf("database upgrade failed: work request marked as failed") + } + // continue polling if still in progress + } + return nil + } + } + + logger.Info("Starting upgrade process for Database", "DatabaseID", databaseId, "TargetVersion", targetVersion) + + upgradeDetails := database.UpgradeDatabaseDetails{ + DatabaseUpgradeSourceDetails: database.DatabaseUpgradeWithDbVersionDetails{ + DbVersion: common.String(targetVersion), + }, + Action: database.UpgradeDatabaseDetailsActionUpgrade, + } + + // Step 3: Submit the upgrade request + upgradeReqDb := database.UpgradeDatabaseRequest{ + DatabaseId: common.String(databaseId), + UpgradeDatabaseDetails: upgradeDetails, + } + + upgradeRespDb, err := dbClient.UpgradeDatabase(ctx, upgradeReqDb) + if err != nil { + logger.Error(err, "Failed to upgrade database version", "DatabaseID", databaseId) + dbcs.Status.Message = "Failed to upgrade database version" + + return fmt.Errorf("failed to upgrade database: %w", err) + } + + logger.Info("Upgrade initiated", "WorkRequestID", *upgradeRespDb.OpcWorkRequestId) + + // Step 3: Update status to upgrading + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Upgrade, nwClient, wrClient); statusErr != nil { + logger.Error(statusErr, "Failed to update lifecycle state to Upgrading") + dbcs.Status.Message = "Failed to update lifecycle state to Upgrading" + + return statusErr + } + + // Step 4: Check status + _, err = CheckResourceState(logger, dbClient, databaseId, string(databasev4.Provision), string(databasev4.Available)) + if err != nil { + logger.Error(err, "Failed to verify database state post upgrade") + dbcs.Status.Message = "Upgrade Database Completed successfully." + + return err + } + + logger.Info("Database upgrade process completed successfully", "DatabaseID", databaseId) + dbcs.Status.Message = fmt.Sprintf("Database upgraded successfully to version %s", targetVersion) + + return nil +} // CloneFromBackupAndGetDbcsId clones a DB system from a backup and returns the new DB system's OCID. -func CloneFromBackupAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) (string, error) { +func CloneFromBackupAndGetDbcsId( + compartmentId string, + logger logr.Logger, + kubeClient client.Client, + dbClient database.DatabaseClient, + dbcs *databasev4.DbcsSystem, + nwClient core.VirtualNetworkClient, + wrClient workrequests.WorkRequestClient) (string, error) { ctx := context.TODO() var err error @@ -433,7 +795,7 @@ func CloneFromBackupAndGetDbcsId(logger logr.Logger, kubeClient client.Client, d dbcs.Status.DbCloneStatus.Id = response.DbSystem.Id // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { return "", statusErr } @@ -447,7 +809,7 @@ func CloneFromBackupAndGetDbcsId(logger logr.Logger, kubeClient client.Client, d } // Sync the DbcsSystem Database details -func CloneFromDatabaseAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) (string, error) { +func CloneFromDatabaseAndGetDbcsId(compartmentId string, logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) (string, error) { ctx := context.TODO() var err error dbAdminPassword := "" @@ -564,7 +926,7 @@ func CloneFromDatabaseAndGetDbcsId(logger logr.Logger, kubeClient client.Client, dbcs.Status.DbCloneStatus.Id = response.DbSystem.Id // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { return "", statusErr } @@ -751,7 +1113,7 @@ func DeleteDbcsSystemSystem(dbClient database.DatabaseClient, Id string) error { } // SetLifecycleState set status.state of the reosurce. -func SetLifecycleState(kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, state databasev4.LifecycleState, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { +func SetLifecycleState(compartmentId string, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, state databasev4.LifecycleState, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { maxRetries := 5 retryDelay := time.Second * 2 @@ -771,13 +1133,12 @@ func SetLifecycleState(kubeClient client.Client, dbClient database.DatabaseClien } // Set the status using the dbcs object - if statusErr := SetDBCSStatus(dbClient, dbcs, nwClient, wrClient); statusErr != nil { + if statusErr := SetDBCSStatus(state, compartmentId, dbClient, dbcs, nwClient, wrClient); statusErr != nil { return statusErr } // Update the ResourceVersion of dbcs from latestInstance to avoid conflict dbcs.ResourceVersion = latestInstance.ResourceVersion - // Attempt to patch the status of the instance err = kubeClient.Status().Patch(context.TODO(), dbcs, client.MergeFrom(latestInstance)) if err != nil { @@ -886,7 +1247,7 @@ func isExported(field reflect.StructField) bool { // SetDBCSSystem LifeCycle state when state is provisioning -func SetDBCSDatabaseLifecycleState(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { +func SetDBCSDatabaseLifecycleState(compartmentId string, logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { dbcsId := *dbcs.Spec.Id @@ -904,12 +1265,12 @@ func SetDBCSDatabaseLifecycleState(logger logr.Logger, kubeClient client.Client, return nil } else if string(resp.LifecycleState) == string(databasev4.Available) { // Change the phase to "Available" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Available, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Available, nwClient, wrClient); statusErr != nil { return statusErr } } else if string(resp.LifecycleState) == string(databasev4.Provision) { // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { return statusErr } // Check the State @@ -919,7 +1280,7 @@ func SetDBCSDatabaseLifecycleState(logger logr.Logger, kubeClient client.Client, } } else if string(resp.LifecycleState) == string(databasev4.Update) { // Change the phase to "Updating" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { return statusErr } // Check the State @@ -929,15 +1290,25 @@ func SetDBCSDatabaseLifecycleState(logger logr.Logger, kubeClient client.Client, } } else if string(resp.LifecycleState) == string(databasev4.Failed) { // Change the phase to "Updating" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Failed, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Failed, nwClient, wrClient); statusErr != nil { return statusErr } return fmt.Errorf("DbSystem is in Failed State") } else if string(resp.LifecycleState) == string(databasev4.Terminated) { // Change the phase to "Terminated" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Terminate, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Terminate, nwClient, wrClient); statusErr != nil { return statusErr } + } else if string(resp.LifecycleState) == string(databasev4.Upgrade) { + // Change the phase to "Upgrading" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Upgrade, nwClient, wrClient); statusErr != nil { + return statusErr + } + // Check the State + _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev4.Upgrade), string(databasev4.Available)) + if err != nil { + return err + } } return nil } @@ -954,43 +1325,48 @@ func GetDbSystemId(logger logr.Logger, dbClient database.DatabaseClient, dbcs *d return err } - dbcs.Spec.DbSystem.CompartmentId = *response.CompartmentId - if response.DisplayName != nil { - dbcs.Spec.DbSystem.DisplayName = *response.DisplayName + if dbcs.Spec.DbSystem == nil { + dbcs.Spec.DbSystem = &databasev4.DbSystemDetails{} + } + if response.DbSystem.CompartmentId != nil { + dbcs.Spec.DbSystem.CompartmentId = *response.DbSystem.CompartmentId + } + if response.DbSystem.DisplayName != nil { + dbcs.Spec.DbSystem.DisplayName = *response.DbSystem.DisplayName } - if response.Hostname != nil { - dbcs.Spec.DbSystem.HostName = *response.Hostname + if response.DbSystem.Hostname != nil { + dbcs.Spec.DbSystem.HostName = *response.DbSystem.Hostname } - if response.CpuCoreCount != nil { - dbcs.Spec.DbSystem.CpuCoreCount = *response.CpuCoreCount + if response.DbSystem.CpuCoreCount != nil { + dbcs.Spec.DbSystem.CpuCoreCount = *response.DbSystem.CpuCoreCount } - dbcs.Spec.DbSystem.NodeCount = response.NodeCount - if response.ClusterName != nil { - dbcs.Spec.DbSystem.ClusterName = *response.ClusterName + dbcs.Spec.DbSystem.NodeCount = response.DbSystem.NodeCount + if response.DbSystem.ClusterName != nil { + dbcs.Spec.DbSystem.ClusterName = *response.DbSystem.ClusterName } - //dbcs.Spec.DbSystem.DbUniqueName = *response.DbUniqueName + //dbcs.Spec.DbSystem.DbUniqueName = *response.DbSystem.DbUniqueName if string(response.DbSystem.DatabaseEdition) != "" { - dbcs.Spec.DbSystem.DbEdition = string(response.DatabaseEdition) + dbcs.Spec.DbSystem.DbEdition = string(response.DbSystem.DatabaseEdition) } - if string(response.DiskRedundancy) != "" { - dbcs.Spec.DbSystem.DiskRedundancy = string(response.DiskRedundancy) + if string(response.DbSystem.DiskRedundancy) != "" { + dbcs.Spec.DbSystem.DiskRedundancy = string(response.DbSystem.DiskRedundancy) } - //dbcs.Spec.DbSystem.DbVersion = *response. + //dbcs.Spec.DbSystem.DbVersion = *response.DbSystem. - if response.BackupSubnetId != nil { - dbcs.Spec.DbSystem.BackupSubnetId = *response.BackupSubnetId + if response.DbSystem.BackupSubnetId != nil { + dbcs.Spec.DbSystem.BackupSubnetId = *response.DbSystem.BackupSubnetId } - dbcs.Spec.DbSystem.Shape = *response.Shape - dbcs.Spec.DbSystem.SshPublicKeys = []string(response.SshPublicKeys) - if response.FaultDomains != nil { - dbcs.Spec.DbSystem.FaultDomains = []string(response.FaultDomains) + dbcs.Spec.DbSystem.Shape = *response.DbSystem.Shape + dbcs.Spec.DbSystem.SshPublicKeys = []string(response.DbSystem.SshPublicKeys) + if response.DbSystem.FaultDomains != nil { + dbcs.Spec.DbSystem.FaultDomains = []string(response.DbSystem.FaultDomains) } - dbcs.Spec.DbSystem.SubnetId = *response.SubnetId - dbcs.Spec.DbSystem.AvailabilityDomain = *response.AvailabilityDomain - if response.KmsKeyId != nil { - dbcs.Status.KMSDetailsStatus.KeyId = *response.KmsKeyId + dbcs.Spec.DbSystem.SubnetId = *response.DbSystem.SubnetId + dbcs.Spec.DbSystem.AvailabilityDomain = *response.DbSystem.AvailabilityDomain + if response.DbSystem.KmsKeyId != nil { + dbcs.Status.KMSDetailsStatus.KeyId = *response.DbSystem.KmsKeyId } err = PopulateDBDetails(logger, dbClient, dbcs) if err != nil { @@ -1024,7 +1400,26 @@ func PopulateDBDetails(logger logr.Logger, dbClient database.DatabaseClient, dbc func GetListDbHomeRsp(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem) (database.ListDbHomesResponse, error) { dbcsId := *dbcs.Spec.Id - CompartmentId := dbcs.Spec.DbSystem.CompartmentId + var CompartmentId string + + // Check if the CompartmentId is defined in dbcs.Spec.DbSystem.CompartmentId + if dbcs.Spec.DbSystem.CompartmentId != "" { + CompartmentId = dbcs.Spec.DbSystem.CompartmentId + } else { + // If not defined, call GetDbSystem to fetch the details + getRequest := database.GetDbSystemRequest{ + DbSystemId: &dbcsId, + } + + // Call GetDbSystem API using the existing dbClient + getResponse, err := dbClient.GetDbSystem(context.TODO(), getRequest) + if err != nil { + return database.ListDbHomesResponse{}, fmt.Errorf("failed to get DB system details: %v", err) + } + + // Extract the compartment ID from the DB system details + CompartmentId = *getResponse.DbSystem.CompartmentId + } dbHomeReq := database.ListDbHomesRequest{ DbSystemId: &dbcsId, @@ -1056,7 +1451,7 @@ func GetListDatabaseRsp(logger logr.Logger, dbClient database.DatabaseClient, db return response, nil } -func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, kubeClient client.Client, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient, databaseID string) error { +func UpdateDbcsSystemIdInst(compartmentId string, log logr.Logger, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, kubeClient client.Client, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient, databaseID string) error { // log.Info("Existing DB System Getting Updated with new details in UpdateDbcsSystemIdInst") var err error updateFlag := false @@ -1073,35 +1468,89 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d } else { log.Info("Details of oldSpec", "oldSpec", oldSpec) } + + if dbcs.Spec.DbSystem == nil { + dbcs.Spec.DbSystem = &databasev4.DbSystemDetails{} + } + if oldSpec.DbSystem == nil { + oldSpec.DbSystem = &databasev4.DbSystemDetails{} + } + + // Fetch latest DB System state from OCI + dbcsId := dbcs.Spec.Id + dbcsReq := database.GetDbSystemRequest{ + DbSystemId: dbcsId, + } + + response, err := dbClient.GetDbSystem(context.TODO(), dbcsReq) + if err != nil { + log.Error(err, "failed to fetch current DB system from OCI") + return err + } + + current := response.DbSystem // OCI's current state log.Info("Details of updateFlag -> " + fmt.Sprint(updateFlag)) + if dbcs.Spec.DbSystem == nil { + dbcs.Spec.DbSystem = &databasev4.DbSystemDetails{} + } + // Compare and update CPU Core Count + if dbcs.Spec.DbSystem.CpuCoreCount > 0 && + (dbcs.Spec.DbSystem.CpuCoreCount != oldSpec.DbSystem.CpuCoreCount || + dbcs.Spec.DbSystem.CpuCoreCount != *current.CpuCoreCount) { + + log.Info("CPU core count change detected", + "desired", dbcs.Spec.DbSystem.CpuCoreCount, + "old", oldSpec.DbSystem.CpuCoreCount, + "current", *current.CpuCoreCount) - if dbcs.Spec.DbSystem.CpuCoreCount > 0 && ((dbcs.Spec.DbSystem.CpuCoreCount != oldSpec.DbSystem.CpuCoreCount) || (dbcs.Spec.DbSystem.CpuCoreCount != *&dbcs.Status.CpuCoreCount)) { - log.Info("DB System cpu core count is: " + fmt.Sprint(dbcs.Spec.DbSystem.CpuCoreCount) + " DB System old cpu count is: " + fmt.Sprint(oldSpec.DbSystem.CpuCoreCount)) updateDbcsDetails.CpuCoreCount = common.Int(dbcs.Spec.DbSystem.CpuCoreCount) updateFlag = true } - if dbcs.Spec.DbSystem.Shape != "" && ((dbcs.Spec.DbSystem.Shape != oldSpec.DbSystem.Shape) || (dbcs.Spec.DbSystem.Shape != *dbcs.Status.Shape)) { - // log.Info("DB System desired shape is :" + string(dbcs.Spec.DbSystem.Shape) + "DB System old shape is " + string(oldSpec.DbSystem.Shape)) + + // Compare and update Shape + if dbcs.Spec.DbSystem.Shape != "" && + (dbcs.Spec.DbSystem.Shape != oldSpec.DbSystem.Shape || + dbcs.Spec.DbSystem.Shape != *current.Shape) { + + log.Info("Shape change detected", + "desired", dbcs.Spec.DbSystem.Shape, + "old", oldSpec.DbSystem.Shape, + "current", *current.Shape) + updateDbcsDetails.Shape = common.String(dbcs.Spec.DbSystem.Shape) updateFlag = true } - if dbcs.Spec.DbSystem.LicenseModel != "" && ((dbcs.Spec.DbSystem.LicenseModel != oldSpec.DbSystem.LicenseModel) || (dbcs.Spec.DbSystem.LicenseModel != *&dbcs.Status.LicenseModel)) { - licenceModel := getLicenceModel(dbcs) - // log.Info("DB System desired License Model is :" + string(dbcs.Spec.DbSystem.LicenseModel) + "DB Sytsem old License Model is " + string(oldSpec.DbSystem.LicenseModel)) - updateDbcsDetails.LicenseModel = database.UpdateDbSystemDetailsLicenseModelEnum(licenceModel) + // Compare and update License Model + if dbcs.Spec.DbSystem.LicenseModel != "" && + (dbcs.Spec.DbSystem.LicenseModel != oldSpec.DbSystem.LicenseModel || + dbcs.Spec.DbSystem.LicenseModel != string(current.LicenseModel)) { + + log.Info("License model change detected", + "desired", dbcs.Spec.DbSystem.LicenseModel, + "old", oldSpec.DbSystem.LicenseModel, + "current", current.LicenseModel) + + licenseModel := getLicenceModel(dbcs) + updateDbcsDetails.LicenseModel = database.UpdateDbSystemDetailsLicenseModelEnum(licenseModel) updateFlag = true } - if dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != 0 && dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != oldSpec.DbSystem.InitialDataStorageSizeInGB { - // log.Info("DB System desired Storage Size is :" + fmt.Sprint(dbcs.Spec.DbSystem.InitialDataStorageSizeInGB) + "DB System old Storage Size is " + fmt.Sprint(oldSpec.DbSystem.InitialDataStorageSizeInGB)) + // Compare Storage Size only against oldSpec (assumes no dynamic resizing supported) + if dbcs.Spec.DbSystem.InitialDataStorageSizeInGB > 0 && + dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != oldSpec.DbSystem.InitialDataStorageSizeInGB { + + log.Info("Data storage size change detected", + "desired", dbcs.Spec.DbSystem.InitialDataStorageSizeInGB, + "old", oldSpec.DbSystem.InitialDataStorageSizeInGB) + updateDbcsDetails.DataStorageSizeInGBs = &dbcs.Spec.DbSystem.InitialDataStorageSizeInGB updateFlag = true } // // Check and update KMS details if necessary - if (dbcs.Spec.KMSConfig != databasev4.KMSConfig{}) { - if dbcs.Spec.KMSConfig != oldSpec.DbSystem.KMSConfig { + if dbcs.Spec.KMSConfig != nil { + if !reflect.DeepEqual(dbcs.Spec.KMSConfig, oldSpec.DbSystem.KMSConfig) { log.Info("Updating KMS details in Existing Database") kmsKeyID := dbcs.Status.KMSDetailsStatus.KeyId @@ -1125,7 +1574,7 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d } // Assign all available fields to KMSConfig - dbcs.Spec.DbSystem.KMSConfig = databasev4.KMSConfig{ + dbcs.Spec.DbSystem.KMSConfig = &databasev4.KMSConfig{ VaultName: dbcs.Spec.KMSConfig.VaultName, CompartmentId: dbcs.Spec.KMSConfig.CompartmentId, KeyName: dbcs.Spec.KMSConfig.KeyName, @@ -1148,7 +1597,7 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d migrateRequest.AdminPassword = common.String(dbAdminPassword) } // Change the phase to "Updating" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { return statusErr } // Send the request @@ -1177,7 +1626,7 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d return err } // Change the phase to "Available" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Available, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Available, nwClient, wrClient); statusErr != nil { return statusErr } @@ -1186,7 +1635,13 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d } log.Info("Details of updateFlag after validations is " + fmt.Sprint(updateFlag)) + if updateFlag { + cdbId := *dbcs.Status.DbInfo[0].Id + // Ensure DB system is AVAILABLE + if err := waitForDbSystemAvailable(cdbId, dbClient, *dbcs.Spec.Id, 30*time.Minute, log); err != nil { + return fmt.Errorf("cannot update DB system within 30 minutes, wait failed: %w", err) + } updateDbcsRequest := database.UpdateDbSystemRequest{ DbSystemId: common.String(*dbcs.Spec.Id), UpdateDbSystemDetails: updateDbcsDetails, @@ -1197,7 +1652,7 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d } // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { return statusErr } // Check the State @@ -1210,6 +1665,48 @@ func UpdateDbcsSystemIdInst(log logr.Logger, dbClient database.DatabaseClient, d return nil } +func waitForDbSystemAvailable(cdbId string, dbClient database.DatabaseClient, dbSystemId string, maxWait time.Duration, log logr.Logger) error { + start := time.Now() + + for { + // 1. Check DB System lifecycle state + dbSysResp, err := dbClient.GetDbSystem(context.TODO(), database.GetDbSystemRequest{ + DbSystemId: &dbSystemId, + }) + if err != nil { + return fmt.Errorf("failed to get DB system: %w", err) + } + dbSysState := string(dbSysResp.DbSystem.LifecycleState) + + // 2. Check CDB lifecycle state + dbResp, err := dbClient.GetDatabase(context.TODO(), database.GetDatabaseRequest{ + DatabaseId: &cdbId, + }) + if err != nil { + return fmt.Errorf("failed to get CDB database: %w", err) + } + dbState := string(dbResp.Database.LifecycleState) + + log.Info("DBSystem state: %s, CDB state: %s", dbSysState, dbState) + + if dbSysState == "AVAILABLE" && dbState == "AVAILABLE" { + log.Info("Both Db System and Db home is in available state, proceeding with update") + return nil // both ready + } + + if time.Since(start) > maxWait { + return fmt.Errorf("timeout: DBSystem: %s, CDB: %s", dbSysState, dbState) + } + log.Info("Either of Db System or Db home is not in available state, rechecking in 30 seconds") + + time.Sleep(30 * time.Second) + } +} + +func isFieldUpdated[T comparable](specVal T, oldVal T, currentVal T) bool { + return specVal != oldVal || specVal != currentVal +} + func WaitForDatabaseState( log logr.Logger, dbClient database.DatabaseClient, @@ -1271,6 +1768,71 @@ func UpdateDbcsSystemId(kubeClient client.Client, dbcs *databasev4.DbcsSystem) e return kubeClient.Patch(context.TODO(), dbcs, patch) } +// CheckDataGuardAssociationState will check the lifecycle state of the Data Guard Association +// and wait until it reaches the expected state (e.g., "AVAILABLE"). +func CheckDataGuardAssociationState(logger logr.Logger, dbClient database.DatabaseClient, associationId string, currentState string, expectedState string, databaseId string) (string, error) { + // The DataGuard Association OCID is not available when provisioning is ongoing. + // Retry until the new Data Guard Association is ready. + + var state string + var err error + for { + state, err = GetDataGuardAssociationState(logger, dbClient, associationId, databaseId) + if err != nil { + logger.Info("Error occurred while collecting the resource lifecycle state") + return "", err + } + if string(state) == expectedState { + break + } else if string(state) != expectedState { + logger.Info("Data Guard Association current state is still:" + string(state) + ". Sleeping for 60 seconds.") + time.Sleep(60 * time.Second) + continue + } + } + + return "", nil +} + +// GetDataGuardAssociationState retrieves the lifecycle state of the Data Guard Association. +func GetDataGuardAssociationState(logger logr.Logger, dbClient database.DatabaseClient, associationId string, databaseID string) (string, error) { // Context with 2-hour timeout + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Hour) + defer cancel() + + request := database.GetDataGuardAssociationRequest{ + DatabaseId: common.String(databaseID), + DataGuardAssociationId: common.String(associationId), + } + desiredState := "AVAILABLE" + + ticker := time.NewTicker(1 * time.Minute) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + // 2-hour timeout reached + return "", context.DeadlineExceeded + case <-ticker.C: + logger.Info("Polling Data Guard association state", "DatabaseID", databaseID, "AssociationID", associationId) + + // Make the request using the context + response, err := dbClient.GetDataGuardAssociation(ctx, request) + if err != nil { + logger.Error(err, "Failed to get Data Guard association state") + return "", err + } + + state := string(response.LifecycleState) + logger.Info("Current state", "State", state) + + if state == desiredState { + return state, nil + } + } + } +} + func CheckResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id string, currentState string, expectedState string) (string, error) { // The database OCID is not available when the provisioning is onging. // Retry until the new DbcsSystem is ready. @@ -1316,7 +1878,7 @@ func GetResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id s return state, nil } -func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { +func SetDBCSStatus(state databasev4.LifecycleState, compartmentId string, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, nwClient core.VirtualNetworkClient, wrClient workrequests.WorkRequestClient) error { if dbcs.Spec.Id == nil { dbcs.Status.State = "FAILED" @@ -1358,12 +1920,16 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem dbcs.Status.Network.ListenerPort = resp.ListenerPort dbcs.Status.Network.HostName = *resp.Hostname dbcs.Status.Network.DomainName = *resp.Domain - if dbcs.Spec.KMSConfig.CompartmentId != "" { + if dbcs.Spec.KMSConfig != nil && dbcs.Spec.KMSConfig.CompartmentId != "" { dbcs.Status.KMSDetailsStatus.CompartmentId = dbcs.Spec.KMSConfig.CompartmentId dbcs.Status.KMSDetailsStatus.VaultName = dbcs.Spec.KMSConfig.VaultName } - dbcs.Status.State = databasev4.LifecycleState(resp.LifecycleState) - if dbcs.Spec.KMSConfig.CompartmentId != "" { + if state == "" { + dbcs.Status.State = databasev4.LifecycleState(resp.LifecycleState) + } else { + dbcs.Status.State = state + } + if dbcs.Spec.KMSConfig != nil && dbcs.Spec.KMSConfig.CompartmentId != "" { dbcs.Status.KMSDetailsStatus.CompartmentId = dbcs.Spec.KMSConfig.CompartmentId dbcs.Status.KMSDetailsStatus.VaultName = dbcs.Spec.KMSConfig.VaultName } @@ -1383,7 +1949,7 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem // Work Request Ststaus dbWorkRequest := databasev4.DbWorkrequests{} - dbWorks, err := getWorkRequest(*resp.OpcRequestId, wrClient, dbcs) + dbWorks, err := getWorkRequest(compartmentId, *resp.OpcRequestId, wrClient, dbcs) if err == nil { for _, dbWork := range dbWorks { //status := checkValue(dbcs, dbWork.Id) @@ -1418,11 +1984,11 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem dbcs.Status.DbInfo = dbcs.Status.DbInfo[:0] dbStatus := databasev4.DbStatus{} - dbHomes, err := getDbHomeList(dbClient, dbcs) + dbHomes, err := getDbHomeList(compartmentId, dbClient, dbcs) if err == nil { for _, dbHome := range dbHomes { - dbDetails, err := getDList(dbClient, dbcs, dbHome.Id) + dbDetails, err := getDList(compartmentId, dbClient, dbcs, dbHome.Id) for _, dbDetail := range dbDetails { if err == nil { dbStatus.Id = dbDetail.Id @@ -1430,6 +1996,13 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem dbStatus.DbName = *dbDetail.DbName dbStatus.DbUniqueName = *dbDetail.DbUniqueName dbStatus.DbWorkload = *dbDetail.DbWorkload + if dbDetail.ConnectionStrings != nil && + dbDetail.ConnectionStrings.CdbDefault != nil && + dbDetail.ConnectionStrings.CdbIpDefault != nil { + + dbStatus.ConnectionString = *dbDetail.ConnectionStrings.CdbDefault + dbStatus.ConnectionStringLong = *dbDetail.ConnectionStrings.CdbIpDefault + } } dbcs.Status.DbInfo = append(dbcs.Status.DbInfo, dbStatus) dbStatus = databasev4.DbStatus{} @@ -1439,14 +2012,14 @@ func SetDBCSStatus(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem return nil } -func getDbHomeList(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem) ([]database.DbHomeSummary, error) { +func getDbHomeList(compartmentId string, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem) ([]database.DbHomeSummary, error) { var items []database.DbHomeSummary dbcsId := *dbcs.Spec.Id dbcsReq := database.ListDbHomesRequest{ DbSystemId: &dbcsId, - CompartmentId: &dbcs.Spec.DbSystem.CompartmentId, + CompartmentId: &compartmentId, } resp, err := dbClient.ListDbHomes(context.TODO(), dbcsReq) @@ -1457,13 +2030,13 @@ func getDbHomeList(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem return resp.Items, nil } -func getDList(dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, dbHomeId *string) ([]database.DatabaseSummary, error) { +func getDList(compartmentId string, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, dbHomeId *string) ([]database.DatabaseSummary, error) { dbcsId := *dbcs.Spec.Id var items []database.DatabaseSummary dbcsReq := database.ListDatabasesRequest{ SystemId: &dbcsId, - CompartmentId: &dbcs.Spec.DbSystem.CompartmentId, + CompartmentId: &compartmentId, DbHomeId: dbHomeId, } @@ -1565,3 +2138,278 @@ func ValidateSpex(logger logr.Logger, kubeClient client.Client, dbClient databas return nil } + +func CreateDbcsBackup( + compartmentId string, + logger logr.Logger, + dbClient database.DatabaseClient, + dbcs *databasev4.DbcsSystem, + kubeClient client.Client, + nwClient core.VirtualNetworkClient, + wrClient workrequests.WorkRequestClient, +) (string, error) { + + ctx := context.TODO() + + // Safety check: ensure DB system OCID is set + if dbcs.Spec.Id == nil || *dbcs.Spec.Id == "" { + return "", fmt.Errorf("cannot create backup: DbcsSystem ID is not set") + } + // Get DB Home Details + listDbHomeRsp, err := GetListDbHomeRsp(logger, dbClient, dbcs) + if err != nil { + logger.Info("Error Occurred while getting List of DBHomes") + return "", err + } + dbHomeId := listDbHomeRsp.Items[0].Id + // Retrieve the list of databases in the DB system + listDbsRequest := database.ListDatabasesRequest{ + CompartmentId: &compartmentId, + SystemId: dbcs.Spec.Id, + DbHomeId: dbHomeId, + } + + listDbsResponse, err := dbClient.ListDatabases(ctx, listDbsRequest) + if err != nil { + logger.Error(err, "Failed to list databases for DB system", "DbcsSystemID", *dbcs.Spec.Id) + return "", err + } + + if len(listDbsResponse.Items) == 0 { + return "", fmt.Errorf("no databases found in DB system with ID: %s", *dbcs.Spec.Id) + } + + // Assume the first database is the one to back up (customize as needed) + databaseId := listDbsResponse.Items[0].Id + + // Generate a unique display name for the backup + // Determine the backup name + var backupPrefixName string + if dbcs.Spec.DbSystem.BackupDisplayName != "" { + backupPrefixName = dbcs.Spec.DbSystem.BackupDisplayName + } else { + backupPrefixName = "backup" + } + backupName := fmt.Sprintf("%s-%s", backupPrefixName, time.Now().Format("20060102-150405")) + + // Check if backup with prefix already exists + listBackupsReq := database.ListBackupsRequest{ + DatabaseId: databaseId, + } + listBackupsResp, err := dbClient.ListBackups(ctx, listBackupsReq) + if err != nil { + logger.Error(err, "Failed to list backups") + return "", err + } + + // Check if a backup with same name already tracked in status + for _, b := range dbcs.Status.Backups { + if strings.HasPrefix(b.Name, backupPrefixName) { + logger.Info("Backup already tracked in status", "BackupName", b.Name) + return b.BackupID, nil + } + } + + // Compare against latest backup stored in status or name prefix + for _, backup := range listBackupsResp.Items { + if backup.DisplayName != nil && strings.HasPrefix(*backup.DisplayName, backupPrefixName) { + logger.Info("Backup already exists, skipping new backup creation", "ExistingBackup", *backup.DisplayName) + return *backup.Id, nil + } + } + + // Build the CreateBackupRequest + createBackupReq := database.CreateBackupRequest{ + CreateBackupDetails: database.CreateBackupDetails{ + DatabaseId: databaseId, + DisplayName: common.String(backupName), + }, + } + + logger.Info("Creating manual backup for database", "DatabaseId", *databaseId, "BackupName", backupName) + + // Change the phase to "Updating" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { + return "", statusErr + } + // Send the request + createBackupResp, err := dbClient.CreateBackup(ctx, createBackupReq) + if err != nil { + logger.Error(err, "Failed to create backup", "DatabaseId", *databaseId) + // Change the phase to "Failed" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Failed, nwClient, wrClient); statusErr != nil { + return "", statusErr + } + return "", err + } + + logger.Info("Backup creation initiated", "BackupID", *createBackupResp.Backup.Id) + backupID := createBackupResp.Backup.Id + // ---- Wait for backup to complete ---- + logger.Info("Waiting for backup to reach ACTIVE state...") + + waitDuration := 60 * time.Minute + pollInterval := 30 * time.Second + timeout := time.After(waitDuration) + ticker := time.NewTicker(pollInterval) + defer ticker.Stop() + + for { + select { + case <-timeout: + return *backupID, fmt.Errorf("timeout: backup %s did not complete within %v", *backupID, waitDuration) + case <-ticker.C: + // Get current backup status + getBackupReq := database.GetBackupRequest{ + BackupId: backupID, + } + getBackupResp, err := dbClient.GetBackup(ctx, getBackupReq) + if err != nil { + logger.Error(err, "Failed to fetch backup status", "BackupID", *backupID) + continue + } + + lifecycle := getBackupResp.Backup.LifecycleState + logger.Info("Polling backup status", "BackupID", *backupID, "State", lifecycle) + + if lifecycle == database.BackupLifecycleStateActive { + logger.Info("Backup completed successfully", "BackupID", *backupID) + // After successful creation and backup becomes ACTIVE + listBackupsReq := database.ListBackupsRequest{ + DatabaseId: databaseId, + } + + listBackupsResp, err := dbClient.ListBackups(ctx, listBackupsReq) + if err != nil { + logger.Error(err, "Failed to list backups") + return *backupID, err + } + + // Reset and populate status.Backups with up-to-date backup list + dbcs.Status.Backups = []databasev4.BackupInfo{} + for _, b := range listBackupsResp.Items { + if b.Id != nil && b.DisplayName != nil && b.TimeStarted != nil { + dbcs.Status.Backups = append(dbcs.Status.Backups, databasev4.BackupInfo{ + Name: *b.DisplayName, + BackupID: *b.Id, + Timestamp: b.TimeStarted.Format(time.RFC3339), + }) + } + } + // Change the phase to "Available" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Available, nwClient, wrClient); statusErr != nil { + return "", statusErr + } + return *backupID, nil + } + + if lifecycle == database.BackupLifecycleStateFailed { + return *backupID, fmt.Errorf("backup failed: BackupID=%s", *backupID) + } + } + } + +} + +func RestoreDbcsToPoint( + compartmentId string, + logger logr.Logger, + dbClient database.DatabaseClient, + dbcs *databasev4.DbcsSystem, + restoreOpt databasev4.RestoreConfig, + kubeClient client.Client, + nwClient core.VirtualNetworkClient, + wrClient workrequests.WorkRequestClient, +) error { + ctx := context.TODO() + + if dbcs.Spec.Id == nil || *dbcs.Spec.Id == "" { + return fmt.Errorf("cannot restore: DbcsSystem ID is not set") + } + + // Get DB Home + dbHomeResp, err := GetListDbHomeRsp(logger, dbClient, dbcs) + if err != nil || len(dbHomeResp.Items) == 0 { + return fmt.Errorf("no DB Homes found for DB system: %v", err) + } + dbHomeId := dbHomeResp.Items[0].Id + + // Get DB + listDbsResp, err := dbClient.ListDatabases(ctx, database.ListDatabasesRequest{ + CompartmentId: &compartmentId, + SystemId: dbcs.Spec.Id, + DbHomeId: dbHomeId, + }) + if err != nil || len(listDbsResp.Items) == 0 { + return fmt.Errorf("no databases found to restore") + } + dbID := listDbsResp.Items[0].Id + + // Change the phase to "Updating" + logger.Info("Changing State to Updating for ", "DatabaseID", *dbID, "RestoreOption", restoreOpt) + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { + return statusErr + } + + restoreDetails := database.RestoreDatabaseDetails{} + + if restoreOpt.Latest { + restoreDetails.Latest = common.Bool(true) + } + if restoreOpt.Timestamp != nil { + restoreDetails.Timestamp = &common.SDKTime{Time: restoreOpt.Timestamp.Time} + } + if restoreOpt.SCN != nil { + restoreDetails.DatabaseSCN = restoreOpt.SCN + } + + restoreReq := database.RestoreDatabaseRequest{ + DatabaseId: dbID, + RestoreDatabaseDetails: restoreDetails, + } + + logger.Info("Initiating restore operation", "DatabaseID", *dbID, "RestoreOption", restoreOpt) + + restoreResp, err := dbClient.RestoreDatabase(ctx, restoreReq) + if err != nil { + logger.Error(err, "Failed to restore database") + SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Failed, nwClient, wrClient) + return err + } + + // Poll for completion + workRequestId := restoreResp.OpcWorkRequestId + logger.Info("Restore initiated", "WorkRequestID", *workRequestId) + + timeout := time.After(60 * time.Minute) + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + + for { + select { + case <-timeout: + return fmt.Errorf("restore timed out after 60 minutes") + case <-ticker.C: + dbStateResp, err := dbClient.GetDatabase(ctx, database.GetDatabaseRequest{DatabaseId: dbID}) + if err != nil { + logger.Error(err, "Polling error") + continue + } + state := dbStateResp.Database.LifecycleState + logger.Info("Polling Restore Operation", "DatabaseID", *dbID, "State", state) + + if state == database.DatabaseLifecycleStateAvailable { + logger.Info("Database restore completed", "DatabaseID", *dbID) + // Change the phase to "Available" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Available, nwClient, wrClient); statusErr != nil { + return statusErr + } + return nil + } else if state == database.DatabaseLifecycleStateRestoreFailed { + return fmt.Errorf("restore failed: DatabaseID=%s", *dbID) + } else if state == database.DatabaseLifecycleStateFailed { + return fmt.Errorf("restore failed: DatabaseID=%s", *dbID) + } + } + } +} diff --git a/commons/dbcssystem/dcommon.go b/commons/dbcssystem/dcommon.go index beaa7c38..2529d3aa 100644 --- a/commons/dbcssystem/dcommon.go +++ b/commons/dbcssystem/dcommon.go @@ -52,11 +52,11 @@ import ( databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" ) -func GetDbHomeDetails(kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem) (database.CreateDbHomeDetails, error) { +func GetDbHomeDetails(kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, id string) (database.CreateDbHomeDetails, error) { dbHomeDetails := database.CreateDbHomeDetails{} - dbHomeReq, err := GetDbLatestVersion(dbClient, dbcs, "") + dbHomeReq, err := GetDbLatestVersion(dbClient, dbcs, *dbcs.Spec.Id) if err != nil { return database.CreateDbHomeDetails{}, err } @@ -180,16 +180,18 @@ func GetDBDetails(kubeClient client.Client, dbcs *databasev4.DbcsSystem) (databa dbDetails.PdbName = &dbcs.Spec.DbSystem.PdbName } - //backup configuration - if dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupEnabled != nil { - if *dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupEnabled { - backupConfig, err := getBackupConfig(kubeClient, dbcs) - if err != nil { - return dbDetails, err - } else { - dbDetails.DbBackupConfig = &backupConfig - } + backupCfg := dbcs.Spec.DbSystem.DbBackupConfig + if dbcs != nil && + dbcs.Spec.DbSystem != nil && + backupCfg != nil && + backupCfg.AutoBackupEnabled != nil && + *backupCfg.AutoBackupEnabled { + + backupConfig, err := getBackupConfig(kubeClient, dbcs) + if err != nil { + return dbDetails, err } + dbDetails.DbBackupConfig = &backupConfig } return dbDetails, nil @@ -383,10 +385,10 @@ func GetDBbDiskRedundancy( return database.LaunchDbSystemDetailsDiskRedundancyNormal } -func getWorkRequest(workId string, wrClient workrequests.WorkRequestClient, dbcs *databasev4.DbcsSystem) ([]workrequests.WorkRequestSummary, error) { +func getWorkRequest(compartmentId string, workId string, wrClient workrequests.WorkRequestClient, dbcs *databasev4.DbcsSystem) ([]workrequests.WorkRequestSummary, error) { var workReq []workrequests.WorkRequestSummary - req := workrequests.ListWorkRequestsRequest{CompartmentId: &dbcs.Spec.DbSystem.CompartmentId, OpcRequestId: &workId, ResourceId: dbcs.Spec.Id} + req := workrequests.ListWorkRequestsRequest{CompartmentId: &compartmentId, OpcRequestId: &workId, ResourceId: dbcs.Spec.Id} resp, err := wrClient.ListWorkRequests(context.Background(), req) if err != nil { return workReq, err diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index 468d7612..7834a8bf 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -387,7 +387,32 @@ spec: storage: false subresources: status: {} - - name: v4 + - additionalPrinterColumns: + - jsonPath: .status.displayName + name: Display Name + type: string + - jsonPath: .status.dbInfo[0].dbName + name: DB Name + type: string + - jsonPath: .status.state + name: State + type: string + - jsonPath: .status.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .status.dataStorageSizeInGBs + name: Storage (TB) + type: integer + - jsonPath: .status.recoStorageSizeInGB + name: Reco Storage (GB) + type: integer + - jsonPath: .status.storageManagement + name: Storage Mgmt + type: string + - jsonPath: .status.dbInfo[0].connectionString + name: ConnString + type: string + name: v4 schema: openAPIV3Schema: properties: @@ -399,6 +424,45 @@ spec: type: object spec: properties: + dataGuard: + properties: + availabilityDomain: + type: string + dbAdminPasswordSecret: + type: string + dbName: + type: string + dbSystemFreeformTags: + additionalProperties: + type: string + type: object + displayName: + type: string + enabled: + type: boolean + hostName: + type: string + isDelete: + type: boolean + peerDbHomeId: + type: string + peerDbSystemId: + type: string + peerRole: + type: string + primaryDatabaseId: + type: string + protectionMode: + type: string + shape: + type: string + sidPrefix: + type: string + subnetId: + type: string + transportType: + type: string + type: object databaseId: type: string dbBackupId: @@ -448,6 +512,8 @@ spec: properties: availabilityDomain: type: string + backupDisplayName: + type: string backupSubnetId: type: string clusterName: @@ -473,10 +539,16 @@ spec: type: string dbEdition: type: string + dbHomeId: + type: string dbName: type: string + dbPatchOcid: + type: string dbUniqueName: type: string + dbUpgradeVersion: + type: string dbVersion: type: string dbWorkload: @@ -516,6 +588,16 @@ spec: type: string privateIp: type: string + restoreConfig: + properties: + latest: + type: boolean + scn: + type: string + timestamp: + format: date-time + type: string + type: object shape: type: string sshPublicKeys: @@ -534,18 +616,17 @@ spec: type: string timeZone: type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPasswordSecret - - hostName - - shape - - subnetId type: object + enableBackup: + type: boolean hardLink: type: boolean id: type: string + isPatch: + type: boolean + isUpgrade: + type: boolean kmsConfig: properties: compartmentId: @@ -599,15 +680,67 @@ spec: properties: availabilityDomain: type: string + backups: + items: + properties: + backupId: + type: string + name: + type: string + timestamp: + type: string + required: + - backupId + - name + - timestamp + type: object + type: array cpuCoreCount: type: integer + dataGuardStatus: + properties: + dbAdminPasswordSecret: + type: string + dbName: + type: string + dbWorkload: + type: string + id: + type: string + isActiveDataGuardEnabled: + type: boolean + lifecycleDetails: + type: string + lifecycleState: + type: string + peerDataGuardAssociationId: + type: string + peerDatabaseId: + type: string + peerDbHomeId: + type: string + peerDbSystemId: + type: string + peerRole: + type: string + primaryDatabaseId: + type: string + protectionMode: + type: string + shape: + type: string + subnetId: + type: string + transportType: + type: string + type: object dataStoragePercentage: type: integer dataStorageSizeInGBs: type: integer dbCloneStatus: properties: - dbAdminPaswordSecret: + dbAdminPasswordSecret: type: string dbDbUniqueName: type: string @@ -638,6 +771,10 @@ spec: dbInfo: items: properties: + connectionString: + type: string + connectionStringLong: + type: string dbHomeId: type: string dbName: @@ -675,6 +812,8 @@ spec: type: object licenseModel: type: string + message: + type: string network: properties: clientSubnet: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2726acb4..74303bea 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: restart-operator-sa + newTag: basedb-operator-sa diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 09317d60..7cc3a0fc 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -4,26 +4,6 @@ kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-configuration webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v4-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackupv4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -168,26 +148,6 @@ webhooks: resources: - shardingdatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackupv1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index 1fd94dde..7538d235 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -39,14 +39,16 @@ package controllers import ( + "bytes" "context" + "encoding/json" "fmt" "reflect" "strings" "time" databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" - dbcsv1 "github.com/oracle/oracle-database-operator/commons/dbcssystem" + dbcsv4 "github.com/oracle/oracle-database-operator/commons/dbcssystem" "github.com/oracle/oracle-database-operator/commons/finalizer" "github.com/oracle/oracle-database-operator/commons/oci" @@ -144,10 +146,23 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) result := resultNq return result, err } + + var compartmentId string + if dbcsInst.Spec.DbSystem != nil && dbcsInst.Spec.DbSystem.CompartmentId != "" { + compartmentId = dbcsInst.Spec.DbSystem.CompartmentId + } else if dbcsInst.Spec.Id != nil && *dbcsInst.Spec.Id != "" { + var err error + compartmentId, err = r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + } r.Logger.Info("OCI provider configured succesfully") /* - Using Finalizer for object deletion + Using Finalizer for object deletion */ if dbcsInst.ObjectMeta.DeletionTimestamp.IsZero() { @@ -162,10 +177,11 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } else { // The object is being deleted r.Logger.Info("Terminate DbcsSystem Database: " + dbcsInst.Spec.DbSystem.DisplayName) - if err := dbcsv1.DeleteDbcsSystemSystem(r.dbClient, *dbcsInst.Spec.Id); err != nil { + if err := dbcsv4.DeleteDbcsSystemSystem(r.dbClient, *dbcsInst.Spec.Id); err != nil { r.Logger.Error(err, "Fail to terminate DbcsSystem Instance") + dbcsInst.Status.Message = err.Error() // Change the status to Failed - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Terminate, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Terminate, r.nwClient, r.wrClient); statusErr != nil { result := resultNq return result, err } @@ -183,6 +199,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Call deletePluggableDatabase function dbSystemId := *dbcsInst.Spec.Id if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { + dbcsInst.Status.Message = err.Error() result := resultNq return result, err } @@ -200,7 +217,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } /* - Determine whether it's a provision or bind operation + Determine whether it's a provision or bind operation */ lastSuccessfullSpec, err := dbcsInst.GetLastSuccessfulSpec() if err != nil { @@ -217,7 +234,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) if lastSuccessfullKMSConfig == nil && lastSuccessfullKMSStatus == nil { - if dbcsInst.Spec.KMSConfig.KeyName != "" { + if dbcsInst.Spec.KMSConfig != nil && dbcsInst.Spec.KMSConfig.KeyName != "" { kmsVaultClient, err := keymanagement.NewKmsVaultClientWithConfigurationProvider(provider) @@ -236,6 +253,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) listResp, err := kmsVaultClient.ListVaults(ctx, getVaultReq) if err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, fmt.Errorf("error listing vaults: %v", err) } @@ -260,8 +278,9 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) if existingVaultId == nil { // Create the KMS vault - createResp, err := r.createKMSVault(ctx, &dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) + createResp, err := r.createKMSVault(ctx, dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) if err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, fmt.Errorf("error creating vault: %v", err) } existingVaultId = createResp.Id @@ -287,6 +306,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) listKeysResp, err := kmsClient.ListKeys(ctx, listKeysReq) if err != nil { r.Logger.Error(err, "Error listing keys in existing vault") + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } @@ -305,7 +325,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("Master key not found in existing vault, creating new key") // Create the KMS key in the existing vault - keyResponse, err := r.createKMSKey(ctx, &dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) + keyResponse, err := r.createKMSKey(ctx, dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) if err != nil { return ctrl.Result{}, err } @@ -324,15 +344,17 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("Creating new vault") // Create the new vault - vaultResponse, err := r.createKMSVault(ctx, &dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) + vaultResponse, err := r.createKMSVault(ctx, dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) if err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } dbcsInst.Status.KMSDetailsStatus.VaultId = *vaultResponse.Id dbcsInst.Status.KMSDetailsStatus.ManagementEndpoint = *vaultResponse.ManagementEndpoint // Create the KMS key in the newly created vault - keyResponse, err := r.createKMSKey(ctx, &dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) + keyResponse, err := r.createKMSKey(ctx, dbcsInst.Spec.KMSConfig, kmsClient, &dbcsInst.Status.KMSDetailsStatus) if err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } @@ -343,11 +365,132 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } } } - //debugging - // lastSuccessfullSpec = nil - // r.ensureDBSystemSpec(&dbcsInst.Spec.DbSystem) - // Check if cloning is needed, debugging - // *dbcsInst.Status.DbCloneStatus.Id = "" + // Backup Creation + if dbcsInst.Spec.EnableBackup { + var compartmentId string + if dbcsInst.Spec.DbSystem.CompartmentId != "" { + compartmentId = dbcsInst.Spec.DbSystem.CompartmentId + } else if dbcsInst.Spec.Id != nil && *dbcsInst.Spec.Id != "" { + var err error + compartmentId, err = r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + } else { + err := fmt.Errorf("compartment ID or DB system ID must be set") + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + backupId, err := dbcsv4.CreateDbcsBackup(compartmentId, r.Logger, r.dbClient, dbcsInst, r.KubeClient, r.nwClient, r.wrClient) + if err != nil { + r.Logger.Error(err, "Backup creation failed") + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, nil + } else { + + dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get DB Home ID: %v\n", err) + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + + databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, *dbcsInst.Spec.Id, compartmentId, dbHomeId) + if err != nil { + fmt.Printf("Failed to get database IDs: %v\n", err) + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + + // Assume the first database is the one to back up (customize as needed) + databaseId := databaseIds[0] + // After successful creation and backup becomes ACTIVE + listBackupsReq := database.ListBackupsRequest{ + DatabaseId: &databaseId, + } + + listBackupsResp, err := r.dbClient.ListBackups(ctx, listBackupsReq) + if err != nil { + r.Logger.Error(err, "Failed to list backups") + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, nil + } + + // Reset and populate status.Backups with up-to-date backup list + dbcsInst.Status.Backups = []databasev4.BackupInfo{} + for _, b := range listBackupsResp.Items { + if b.Id != nil && b.DisplayName != nil && b.TimeStarted != nil { + dbcsInst.Status.Backups = append(dbcsInst.Status.Backups, databasev4.BackupInfo{ + Name: *b.DisplayName, + BackupID: *b.Id, + Timestamp: b.TimeStarted.Format(time.RFC3339), + }) + } + } + r.Logger.Info("Backup Completed successfully", "BackupID", backupId) + } + } + restoreCount := 0 + var restoreInfo *databasev4.RestoreConfig + if dbcsInst.Spec.DbSystem != nil { + restoreInfo = dbcsInst.Spec.DbSystem.RestoreConfig + } + if restoreInfo != nil { + if restoreInfo.Latest { + restoreCount++ + } + if restoreInfo.Timestamp != nil { + restoreCount++ + } + if restoreInfo.SCN != nil { + restoreCount++ + } + + if restoreCount > 1 { + return ctrl.Result{}, fmt.Errorf("exactly one of Timestamp, SCN, or Latest must be specified for restore") + } + + aj, _ := json.Marshal(dbcsInst.Spec) + bj, _ := json.Marshal(lastSuccessfullSpec) + + if bytes.Equal(aj, bj) { + r.Logger.Info("Restore already applied — skipping", "DbcsSystem", dbcsInst.Name) + } else if restoreCount == 1 { // do restore when exactly one restore is new spec and defined correctly + switch { + case restoreInfo.Latest: + err = dbcsv4.RestoreDbcsToPoint(compartmentId, r.Logger, r.dbClient, dbcsInst, + databasev4.RestoreConfig{Latest: true}, + r.KubeClient, r.nwClient, r.wrClient) + + case restoreInfo.Timestamp != nil: + err = dbcsv4.RestoreDbcsToPoint(compartmentId, r.Logger, r.dbClient, dbcsInst, + databasev4.RestoreConfig{Timestamp: restoreInfo.Timestamp}, + r.KubeClient, r.nwClient, r.wrClient) + + case restoreInfo.SCN != nil: + err = dbcsv4.RestoreDbcsToPoint(compartmentId, r.Logger, r.dbClient, dbcsInst, + databasev4.RestoreConfig{SCN: restoreInfo.SCN}, + r.KubeClient, r.nwClient, r.wrClient) + } + + if err != nil { + r.Logger.Error(err, "Restore failed") + return ctrl.Result{}, err + } + + // STEP 4: Mark spec as successfully applied + if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + + r.Logger.Info("Restore Operation Completed and lastSuccessfulSpec updated", "DbcsSystem", dbcsInst.Name) + return ctrl.Result{}, nil + } + } + setupCloning := false // Check if SetupDBCloning is true and ensure one of the required fields is provided if dbcsInst.Spec.SetupDBCloning { @@ -355,6 +498,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) if dbcsInst.Spec.Id == nil && dbcsInst.Spec.DbBackupId == nil && dbcsInst.Spec.DatabaseId == nil { // If none of the required fields are set, log an error and exit the function r.Logger.Error(err, "SetupDBCloning is defined but other necessary details (Id, DbBackupId, DatabaseId) are not present. Refer README.md file for instructions.") + dbcsInst.Status.Message = "SetupDBCloning is defined but other necessary details (Id, DbBackupId, DatabaseId) are not present. Refer README.md file for instructions." return ctrl.Result{}, nil } // If the condition is met, proceed with cloning setup @@ -364,16 +508,91 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) setupCloning = false } + switch { + case dbcsInst.Spec.IsPatch && dbcsInst.Spec.IsUpgrade: + errMsg := "Both IsPatch and IsUpgrade are set. Only one operation can be performed at a time." + r.Logger.Error(nil, errMsg) + dbcsInst.Status.Message = errMsg + return ctrl.Result{}, nil + + case dbcsInst.Spec.IsPatch: + if dbcsInst.Spec.Id == nil || dbcsInst.Spec.DbSystem.PatchOCID == "" { + errMsg := "Patching is requested but Patch Version or DB System ID is missing." + r.Logger.Error(nil, errMsg) + dbcsInst.Status.Message = errMsg + return ctrl.Result{}, nil + } + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + + err = dbcsv4.PatchDBSystem(ctx, compartmentId, r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient, dbcsInst.Spec.DbSystem.DbHomeId, dbcsInst.Spec.DbSystem.PatchOCID) + if err != nil { + r.Logger.Error(err, "Fail to patch db system") + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, statusErr + } + + return ctrl.Result{}, nil + } + + case dbcsInst.Spec.IsUpgrade: + if dbcsInst.Spec.Id == nil || dbcsInst.Spec.DbSystem.UpgradeVersion == "" { + errMsg := "Upgrade is requested but Upgrade Version or DB System ID is missing." + r.Logger.Error(nil, errMsg) + dbcsInst.Status.Message = errMsg + return ctrl.Result{}, nil + } + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + + err = dbcsv4.UpgradeDatabaseVersion(ctx, compartmentId, r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient, *dbcsInst.Spec.DatabaseId, dbcsInst.Spec.DbSystem.UpgradeVersion) + if err != nil { + r.Logger.Error(err, "Failed to upgrade DB Home.") + dbcsInst.Status.Message = fmt.Sprintf("Upgrade failed: %v", err) + return ctrl.Result{}, err + } + + } + + isDeleteDataguard := false + setupDataguard := false + + // Check if DataGuard is marked for deletion + if dbcsInst.Spec.DataGuard.IsDelete { + isDeleteDataguard = true + // If marked for delete, skip DataGuard setup + setupDataguard = false + } else if dbcsInst.Spec.DataGuard.Enabled { + // Proceed to check required fields for DataGuard setup + if dbcsInst.Spec.DataGuard.PrimaryDatabaseId == nil || + dbcsInst.Spec.DataGuard.DbAdminPasswordSecret == nil || + dbcsInst.Spec.DataGuard.DisplayName == nil { + r.Logger.Error(err, "setupDataguard is defined but other necessary details are not present. Refer README.md file for instructions.") + return ctrl.Result{}, nil + } + // All required fields are present, enable setup + setupDataguard = true + } var dbSystemId string // Executing DB Cloning Process, if defined. Do not repeat cloning again when Status has Id present. if setupCloning && dbcsInst.Status.DbCloneStatus.Id == nil { switch { case dbcsInst.Spec.SetupDBCloning && dbcsInst.Spec.DbBackupId != nil: - dbSystemId, err = dbcsv1.CloneFromBackupAndGetDbcsId(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) + dbSystemId, err = dbcsv4.CloneFromBackupAndGetDbcsId(compartmentId, r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) if err != nil { r.Logger.Error(err, "Fail to clone db system from backup and get DbcsSystem System ID") - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, statusErr } @@ -382,10 +601,11 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("DB Cloning completed successfully from provided backup DB system") case dbcsInst.Spec.SetupDBCloning && dbcsInst.Spec.DatabaseId != nil: - dbSystemId, err = dbcsv1.CloneFromDatabaseAndGetDbcsId(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) + dbSystemId, err = dbcsv4.CloneFromDatabaseAndGetDbcsId(compartmentId, r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) if err != nil { r.Logger.Error(err, "Fail to clone db system from DatabaseID provided") - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, statusErr } @@ -394,31 +614,34 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("DB Cloning completed successfully from provided databaseId") case dbcsInst.Spec.SetupDBCloning && dbcsInst.Spec.DbBackupId == nil && dbcsInst.Spec.DatabaseId == nil: - dbSystemId, err = dbcsv1.CloneAndGetDbcsId(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) + dbSystemId, err = dbcsv4.CloneAndGetDbcsId(compartmentId, r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) if err != nil { r.Logger.Error(err, "Fail to clone db system and get DbcsSystem System ID") - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, statusErr } return ctrl.Result{}, nil } r.Logger.Info("DB Cloning completed successfully from provided db system") } - } else if !setupCloning { + } else if !setupCloning && !setupDataguard && !isDeleteDataguard { if dbcsInst.Spec.Id == nil && lastSuccessfullSpec == nil { // If no DbcsSystem ID specified, create a new DB System // ======================== Validate Specs ============== - err = dbcsv1.ValidateSpex(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.Recorder) + err = dbcsv4.ValidateSpex(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.Recorder) if err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } r.Logger.Info("DbcsSystem DBSystem provisioning") - dbcsID, err := dbcsv1.CreateAndGetDbcsId(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient, &dbcsInst.Status.KMSDetailsStatus) + dbcsID, err := dbcsv4.CreateAndGetDbcsId(compartmentId, r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient, &dbcsInst.Status.KMSDetailsStatus) if err != nil { + dbcsInst.Status.Message = err.Error() r.Logger.Error(err, "Fail to provision and get DbcsSystem System ID") // Change the status to Failed - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue @@ -428,16 +651,17 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) assignDBCSID(dbcsInst, dbcsID) // Check if KMSConfig is specified kmsConfig := dbcsInst.Spec.KMSConfig - if kmsConfig != (databasev4.KMSConfig{}) { + if kmsConfig != nil { // Check if KMSDetailsStatus is uninitialized (zero value) - if dbcsInst.Spec.DbSystem.KMSConfig != dbcsInst.Spec.KMSConfig { + if dbcsInst.Spec.DbSystem.KMSConfig != nil && dbcsInst.Spec.KMSConfig != nil && + *dbcsInst.Spec.DbSystem.KMSConfig != *dbcsInst.Spec.KMSConfig { dbcsInst.Spec.DbSystem.KMSConfig = dbcsInst.Spec.KMSConfig } } - if err := dbcsv1.UpdateDbcsSystemId(r.KubeClient, dbcsInst); err != nil { + if err := dbcsv4.UpdateDbcsSystemId(r.KubeClient, dbcsInst); err != nil { // Change the status to Failed assignDBCSID(dbcsInst, dbcsID) - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } return ctrl.Result{}, err @@ -446,28 +670,30 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("DbcsSystem system provisioned succesfully") assignDBCSID(dbcsInst, dbcsID) if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } assignDBCSID(dbcsInst, dbcsID) } else { - if lastSuccessfullSpec == nil { // first time after creation of DB - if err := dbcsv1.GetDbSystemId(r.Logger, r.dbClient, dbcsInst); err != nil { + if lastSuccessfullSpec == nil { // first time update after creation of DB + if err := dbcsv4.GetDbSystemId(r.Logger, r.dbClient, dbcsInst); err != nil { // Change the status to Failed - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } return ctrl.Result{}, err } - if err := dbcsv1.SetDBCSDatabaseLifecycleState(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient); err != nil { + if err := dbcsv4.SetDBCSDatabaseLifecycleState(compartmentId, r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient); err != nil { // Change the status to required state + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } dbSystemId := *dbcsInst.Spec.Id - if err := dbcsv1.UpdateDbcsSystemId(r.KubeClient, dbcsInst); err != nil { + if err := dbcsv4.UpdateDbcsSystemId(r.KubeClient, dbcsInst); err != nil { // Change the status to Failed assignDBCSID(dbcsInst, dbSystemId) - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } return ctrl.Result{}, err @@ -477,6 +703,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) dbSystemId = *dbcsInst.Spec.Id if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } assignDBCSID(dbcsInst, dbSystemId) @@ -489,46 +716,58 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) dbSystemId = *dbcsInst.Spec.Id } //debugging - // *dbcsInst.Spec.Id = "ocid1.dbsystem.oc1.iad.anuwcljsabf7htya55wz5vfil7ul3pkzpubnymp6zrp3fhgomv3fcdr2vtiq" + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Spec.Id) if err != nil { fmt.Printf("Failed to get compartment ID: %v\n", err) + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, *dbcsInst.Spec.Id) if err != nil { fmt.Printf("Failed to get DB Home ID: %v\n", err) + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, *dbcsInst.Spec.Id, compartmentId, dbHomeId) if err != nil { fmt.Printf("Failed to get database IDs: %v\n", err) + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } err = r.getPluggableDatabaseDetails(ctx, dbcsInst, *dbcsInst.Spec.Id, databaseIds) if err != nil { fmt.Printf("Failed to get pluggable database details: %v\n", err) + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } - if err := dbcsv1.UpdateDbcsSystemIdInst(r.Logger, r.dbClient, dbcsInst, r.KubeClient, r.nwClient, r.wrClient, databaseIds[0]); err != nil { + err = r.getDataGuardStatusAndUpdate(ctx, dbcsInst, databaseIds[0], *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get dataguard details: %v\n", err) + return ctrl.Result{}, err + } + + if err := dbcsv4.UpdateDbcsSystemIdInst(compartmentId, r.Logger, r.dbClient, dbcsInst, r.KubeClient, r.nwClient, r.wrClient, databaseIds[0]); err != nil { r.Logger.Error(err, "Fail to update DbcsSystem Id") + dbcsInst.Status.Message = err.Error() // Change the status to Failed - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue return ctrl.Result{}, nil } - if err := dbcsv1.SetDBCSDatabaseLifecycleState(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient); err != nil { + if err := dbcsv4.SetDBCSDatabaseLifecycleState(compartmentId, r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient); err != nil { // Change the status to required state return ctrl.Result{}, err } // Update Spec and Status result, err := r.updateSpecsAndStatus(ctx, dbcsInst, dbSystemId) if err != nil { + dbcsInst.Status.Message = err.Error() return result, err } } @@ -537,21 +776,66 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Update the Wallet Secret when the secret name is given //r.updateWalletSecret(dbcs) + // Dataguard enablement + switch { + case setupDataguard: + // Data Guard Creation Flow + if err := r.EnableDataGuard(ctx, r.Logger, r.dbClient, dbcsInst, r.KubeClient, r.nwClient, r.wrClient, *dbcsInst.Spec.DataGuard.PrimaryDatabaseId, &dbcsInst.Spec.DataGuard); err != nil { + r.Logger.Error(err, "Failed to enable Data Guard and update DbcsSystem ID") + + // Update status to Failed + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + return ctrl.Result{}, statusErr + } + return ctrl.Result{}, nil + } + + if err := r.KubeClient.Status().Update(ctx, dbcsInst); err != nil { + r.Logger.Error(err, "Failed to update DB status") + return reconcile.Result{}, err + } + + dbSystemId = *dbcsInst.Status.DataGuardStatus.PeerDbSystemId + if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { + return ctrl.Result{}, err + } + assignDBCSID(dbcsInst, dbSystemId) + + case isDeleteDataguard: + // Data Guard Deletion Flow + if err := r.DeleteDataGuard(ctx, r.Logger, r.dbClient, dbcsInst); err != nil { + r.Logger.Error(err, "Failed to delete Data Guard") + + // Update status to Failed + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + return ctrl.Result{}, statusErr + } + return ctrl.Result{}, nil + } + + default: + // Skip Data Guard handling + r.Logger.Info("Skipping Data Guard setup and deletion - no action required") + } // Update the last succesful spec if dbcsInst.Spec.Id != nil { dbSystemId = *dbcsInst.Spec.Id if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } } else if dbcsInst.Status.DbCloneStatus.Id != nil { dbSystemId = *dbcsInst.Status.DbCloneStatus.Id + } else if setupDataguard { + dbSystemId = *dbcsInst.Status.DataGuardStatus.PeerDbSystemId } - //assignDBCSID(dbcsInst,dbcsI) + //assignDBCSID(dbcsInst,dbcsI)k get pods -n $odo + // Change the phase to "Available" assignDBCSID(dbcsInst, dbSystemId) - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev4.Available, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Available, r.nwClient, r.wrClient); statusErr != nil { return ctrl.Result{}, statusErr } @@ -560,6 +844,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Check if specified PDB exists or needs to be created exists, err := r.validatePDBExistence(dbcsInst) if err != nil { + dbcsInst.Status.Message = err.Error() fmt.Printf("Failed to get PDB Details: %v\n", err) return ctrl.Result{}, err } @@ -572,17 +857,20 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Get Compartment ID by DB System ID compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) if err != nil { + dbcsInst.Status.Message = err.Error() fmt.Printf("Failed to get compartment ID: %v\n", err) return ctrl.Result{}, err } dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, dbSystemId) if err != nil { fmt.Printf("Failed to get DB Home ID: %v\n", err) + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, dbSystemId, compartmentId, dbHomeId) if err != nil { fmt.Printf("Failed to get database IDs: %v\n", err) + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } @@ -593,6 +881,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) if pdbConfig.IsDelete != nil && *pdbConfig.IsDelete { // Call deletePluggableDatabase function if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } // Continue to the next pdbConfig @@ -603,6 +892,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) pdbId, err := r.createPluggableDatabase(ctx, dbcsInst, pdbConfig, databaseIds[0], compartmentId, dbSystemId) if err != nil { // Handle error if required + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } @@ -638,10 +928,9 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Assign the updated slice to dbcsInst.Status.PdbDetailsStatus dbcsInst.Status.PdbDetailsStatus = updatedPdbDetailsStatus - // Update the status in Kubernetes - // Update the status subresource err = r.KubeClient.Status().Update(ctx, dbcsInst) if err != nil { + dbcsInst.Status.Message = err.Error() r.Logger.Error(err, "Failed to update DB status") return reconcile.Result{}, err } @@ -653,10 +942,6 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.Logger.Info("No change in PDB configurations or, already existed PDB Status.") } } - // } else { - // r.Logger.Info("No PDB configurations given.") - // } - // r.Logger.Info("DBInst after assignment", "dbcsInst:->", dbcsInst) // // Check if PDBConfig is defined and needs to be created or deleted pdbConfigs := dbcsInst.Spec.PdbConfigs if pdbConfigs != nil { @@ -667,16 +952,19 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Get Compartment ID by DB System ID compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) if err != nil { + dbcsInst.Status.Message = err.Error() fmt.Printf("Failed to get compartment ID: %v\n", err) return ctrl.Result{}, err } dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, dbSystemId) if err != nil { + dbcsInst.Status.Message = err.Error() fmt.Printf("Failed to get DB Home ID: %v\n", err) return ctrl.Result{}, err } databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, dbSystemId, compartmentId, dbHomeId) if err != nil { + dbcsInst.Status.Message = err.Error() fmt.Printf("Failed to get database IDs: %v\n", err) return ctrl.Result{}, err } @@ -688,6 +976,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) if pdbConfig.IsDelete != nil && *pdbConfig.IsDelete { // Call deletePluggableDatabase function if err := r.deletePluggableDatabase(ctx, pdbConfig, dbSystemId); err != nil { + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } // Continue to the next pdbConfig @@ -698,6 +987,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) _, err := r.createPluggableDatabase(ctx, dbcsInst, pdbConfig, databaseIds[0], compartmentId, dbSystemId) if err != nil { // Handle error if required + dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } } @@ -708,6 +998,118 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) return resultQ, nil } + +func (r *DbcsSystemReconciler) DeleteDataGuard( + ctx context.Context, + log logr.Logger, + dbClient database.DatabaseClient, + dbcsInst *databasev4.DbcsSystem, +) error { + dataGuardStatus := dbcsInst.Status.DataGuardStatus + var peerDbSystemId *string + if dataGuardStatus.PeerDbSystemId != nil { + peerDbSystemId = dataGuardStatus.PeerDbSystemId + } + + if dbcsInst.Spec.DataGuard.PeerDbSystemId != nil { + peerDbSystemId = dbcsInst.Spec.DataGuard.PeerDbSystemId + } + if peerDbSystemId == nil { + log.Info("Skipping Data Guard deletion — peer DB System ID is nil") + return nil + } + if peerDbSystemId != nil { + // / Fetch the DB system details to check its current lifecycle state + getDbSystemReq := database.GetDbSystemRequest{ + DbSystemId: peerDbSystemId, // The peer DB system ID we want to check + } + + getDbSystemResp, err := dbClient.GetDbSystem(ctx, getDbSystemReq) + if err != nil { + log.Error(err, "Failed to fetch peer DB system status") + return err + } + + // Check the lifecycle state of the peer DB system + if getDbSystemResp.DbSystem.LifecycleState == database.DbSystemLifecycleStateTerminated { + log.Info("Peer DB system is already terminated. No action needed.", "peerDbSystemId", *peerDbSystemId) + dbcsInst.Status.DataGuardStatus = databasev4.DataGuardStatus{} + if err := r.KubeClient.Status().Update(ctx, dbcsInst); err != nil { + log.Error(err, "Failed to update DB status after deleting Data Guard") + return err + } + return nil + } + } + + log.Info("Terminating peer DB system", "peerDbSystemId", *peerDbSystemId) + termSysReq := database.TerminateDbSystemRequest{ + DbSystemId: peerDbSystemId, + } + + termSysResp, err := dbClient.TerminateDbSystem(ctx, termSysReq) + if err != nil { + log.Error(err, "Failed to initiate termination of peer DB System") + return err + } + + if err := r.waitForWorkRequest(ctx, log, termSysResp.OpcWorkRequestId); err != nil { + log.Error(err, "Peer DB system termination failed or timed out") + return err + } + + // Clear out DataGuardStatus === + dbcsInst.Status.DataGuardStatus = databasev4.DataGuardStatus{} + if err := r.KubeClient.Status().Update(ctx, dbcsInst); err != nil { + log.Error(err, "Failed to update DB status after deleting Data Guard") + return err + } + log.Info("Successfully deleted Data Guard association") + return nil +} + +func (r *DbcsSystemReconciler) waitForWorkRequest( + ctx context.Context, + log logr.Logger, + workRequestID *string, +) error { + if workRequestID == nil { + return fmt.Errorf("missing WorkRequest ID") + } + + log.Info("Waiting for work request to complete", "workRequestID", *workRequestID) + timeout := time.After(60 * time.Minute) + pollInterval := 30 * time.Second + + for { + select { + case <-timeout: + return fmt.Errorf("timed out waiting for WorkRequest %s", *workRequestID) + default: + resp, err := r.wrClient.GetWorkRequest(ctx, workrequests.GetWorkRequestRequest{ + WorkRequestId: workRequestID, + }) + if err != nil { + log.Error(err, "Failed to get WorkRequest status", "workRequestID", *workRequestID) + return err + } + + status := resp.WorkRequest.Status + log.Info("Polling WorkRequest status", "status", status) + + switch status { + case workrequests.WorkRequestStatusSucceeded: + log.Info("WorkRequest succeeded", "workRequestID", *workRequestID) + return nil + case workrequests.WorkRequestStatusFailed: + return fmt.Errorf("work request %s failed", *workRequestID) + } + + time.Sleep(pollInterval) + } + } +} + func (r *DbcsSystemReconciler) updateSpecsAndStatus(ctx context.Context, dbcsInst *databasev4.DbcsSystem, dbSystemId string) (reconcile.Result, error) { // Retry mechanism for handling resource version conflicts @@ -913,7 +1315,7 @@ func (r *DbcsSystemReconciler) createPluggableDatabase(ctx context.Context, dbcs return "", err } // Change the status to Provisioning - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcs, databasev4.Provision, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcs, databasev4.Provision, r.nwClient, r.wrClient); statusErr != nil { r.Logger.Error(err, "Failed to set DBCS LifeCycle State to Provisioning") return "", statusErr } @@ -961,7 +1363,7 @@ func (r *DbcsSystemReconciler) createPluggableDatabase(ctx context.Context, dbcs if pdbStatus == database.PluggableDatabaseLifecycleStateAvailable { r.Logger.Info("Pluggable database successfully created", "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) // Change the status to Available - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcs, databasev4.Available, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcs, databasev4.Available, r.nwClient, r.wrClient); statusErr != nil { return "", statusErr } return *response.PluggableDatabase.Id, nil @@ -970,7 +1372,7 @@ func (r *DbcsSystemReconciler) createPluggableDatabase(ctx context.Context, dbcs if pdbStatus == database.PluggableDatabaseLifecycleStateFailed { r.Logger.Error(fmt.Errorf("pluggable database creation failed"), "PDBName", pdbConfig.PdbName, "PDBID", *pdbConfig.PluggableDatabaseId) // Change the status to Failed - if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcs, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcs, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { return "", statusErr } return "", fmt.Errorf("pluggable database creation failed") @@ -1079,6 +1481,59 @@ func (r *DbcsSystemReconciler) getPluggableDatabaseID(ctx context.Context, pdbCo return pdbID, nil } +func (r *DbcsSystemReconciler) getDataGuardStatusAndUpdate( + ctx context.Context, + dbcsInst *databasev4.DbcsSystem, + primaryDatabaseId string, + dbSystemId string, +) error { + log := r.Logger.WithValues("func", "getDataGuardStatusAndUpdate") + + // compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) + // if err != nil { + // log.Error(err, "Failed to get compartment ID for DB System") + // return err + // } + + listRequest := database.ListDataGuardAssociationsRequest{ + DatabaseId: common.String(primaryDatabaseId), + } + + listResp, err := r.dbClient.ListDataGuardAssociations(ctx, listRequest) + if err != nil { + log.Error(err, "Failed to list Data Guard associations") + return err + } + + if len(listResp.Items) == 0 { + log.Info("No Data Guard associations found") + dbcsInst.Status.DataGuardStatus = databasev4.DataGuardStatus{} // reset to empty + return nil + } + + // Assuming one-to-one association + dg := listResp.Items[0] + + status := databasev4.DataGuardStatus{ + Id: dg.Id, + PeerDatabaseId: dg.PeerDatabaseId, + PeerDbSystemId: dg.PeerDbSystemId, + PeerDbHomeId: dg.PeerDbHomeId, + PeerRole: (*string)(&dg.Role), // Cast enum to string pointer + PrimaryDatabaseId: dg.DatabaseId, + TransportType: (*string)(&dg.TransportType), + ProtectionMode: (*string)(&dg.ProtectionMode), + LifecycleState: (*string)(&dg.LifecycleState), + LifecycleDetails: dg.LifecycleDetails, + PeerDataGuardAssociationId: dg.Id, + } + + dbcsInst.Status.DataGuardStatus = status + + log.Info("Updated DataGuardStatus in CR", "DataGuardAssociationId", *dg.Id) + return nil +} + func (r *DbcsSystemReconciler) getPluggableDatabaseDetails(ctx context.Context, dbcsInst *databasev4.DbcsSystem, dbSystemId string, databaseIds []string) error { compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, dbSystemId) if err != nil { @@ -1186,6 +1641,179 @@ func (r *DbcsSystemReconciler) doesPluggableDatabaseExist(ctx context.Context, c return false, nil, nil } +// Enable Dataguard +func (r *DbcsSystemReconciler) EnableDataGuard( + ctx context.Context, + log logr.Logger, + dbClient database.DatabaseClient, + dbcsSystem *databasev4.DbcsSystem, + kubeClient client.Client, + nwClient core.VirtualNetworkClient, + wrClient workrequests.WorkRequestClient, + databaseID string, + dataGuardConfig *databasev4.DataGuardConfig, +) error { + + // Check if the `Id` field is set + if databaseID == "" { + return fmt.Errorf("DbcsSystem.Spec.DataGuard.peerDBSytemID is not set") + } + + // Extract last successful spec for comparison (optional) + oldSpec, err := dbcsSystem.GetLastSuccessfulSpecWithLog(log) + if err != nil { + log.Error(err, "Failed to get last successful spec for DbcsSystem") + return err + } + + // Compare DataGuard configurations to determine if an update is required + updateFlag := false + + if oldSpec == nil || !reflect.DeepEqual(oldSpec.DataGuard, dataGuardConfig) { + updateFlag = true + } + + databaseAdminPassword, err := r.getSecret(ctx, dbcsSystem.Namespace, *dataGuardConfig.DbAdminPasswordSecret) + // Trim newline character from the password + databaseAdminPassword = strings.TrimSpace(databaseAdminPassword) + if updateFlag { + + exists, err := r.checkExistingDataGuardAssociation(ctx, dbClient, dbcsSystem, databaseID) + if err != nil { + return err + } + + if exists { + log.Info("Skipping Data Guard creation as it already exists for the database", "DatabaseID", databaseID) + return nil + } + + request := database.CreateDataGuardAssociationRequest{ + DatabaseId: common.String(databaseID), + CreateDataGuardAssociationDetails: database.CreateDataGuardAssociationWithNewDbSystemDetails{ + // CreationType: common.String("NewDbSystem"), + DatabaseAdminPassword: common.String(databaseAdminPassword), + ProtectionMode: database.CreateDataGuardAssociationDetailsProtectionModeEnum(*dataGuardConfig.ProtectionMode), + TransportType: database.CreateDataGuardAssociationDetailsTransportTypeEnum(*dataGuardConfig.TransportType), + AvailabilityDomain: dataGuardConfig.AvailabilityDomain, + DisplayName: dataGuardConfig.DisplayName, + Hostname: dataGuardConfig.HostName, + Shape: dataGuardConfig.Shape, + SubnetId: dataGuardConfig.SubnetId, + }, + } + + response, err := dbClient.CreateDataGuardAssociation(ctx, request) + // fmt.Printf("Response:\n%+v\n", response) + if err != nil { + r.Logger.Error(err, "DbcsSystem did not reach desired state after DataGuard update") + return err + } + + r.Logger.Info("Data Guard association created successfully.") + + // Extract the DataGuardAssociation ID from the response + associationId := *response.DataGuardAssociation.Id + + // Wait for the update to be applied and resource state to become "AVAILABLE" + // _, err = dbcsv4.CheckResourceState(log, dbClient, *dbcsSystem.Spec.Id, "UPDATING", "AVAILABLE") + _, err = dbcsv4.CheckDataGuardAssociationState(log, dbClient, associationId, "UPDATING", "AVAILABLE", databaseID) + if err != nil { + r.Logger.Error(err, "Error checking Data Guard Association state") + } + + r.Logger.Info("Data Guard Association is now in the 'AVAILABLE' state.") + + r.Logger.Info("DataGuard update successful", "dbSystemId", *dbcsSystem.Spec.Id) + } else { + r.Logger.Info("No DataGuard update required; configurations match") + } + + _, err = r.checkExistingDataGuardAssociation(ctx, dbClient, dbcsSystem, databaseID) + if err != nil { + return err + } + + return nil +} + +// Get Dataguard Details +func (r *DbcsSystemReconciler) checkExistingDataGuardAssociation(ctx context.Context, dbClient database.DatabaseClient, dbcsSystem *databasev4.DbcsSystem, databaseID string) (bool, error) { + + request := database.ListDataGuardAssociationsRequest{ + DatabaseId: common.String(databaseID), + } + + response, err := dbClient.ListDataGuardAssociations(ctx, request) + if err != nil { + return false, fmt.Errorf("failed to list Data Guard associations: %w", err) + } + + // Check if any Data Guard associations are present + if len(response.Items) > 0 { + r.Logger.Info("Data Guard association found for the database", "DatabaseID", databaseID) + + item := response.Items[0] + status := &dbcsSystem.Status.DataGuardStatus + + if item.PeerDbSystemId != nil { + status.PeerDbSystemId = item.PeerDbSystemId + } + + if item.DatabaseId != nil { + status.PrimaryDatabaseId = item.DatabaseId + } + + status.DbAdminPasswordSecret = dbcsSystem.Spec.DataGuard.DbAdminPasswordSecret + + if item.IsActiveDataGuardEnabled != nil { + status.IsActiveDataGuardEnabled = *item.IsActiveDataGuardEnabled + } + + if item.PeerRole != "" { + status.PeerRole = (*string)(&item.PeerRole) + } + + if item.PeerDbHomeId != nil { + status.PeerDbHomeId = item.PeerDbHomeId + } + + if item.ProtectionMode != "" { + status.ProtectionMode = (*string)(&item.ProtectionMode) + } + + if item.TransportType != "" { + status.TransportType = (*string)(&item.TransportType) + } + + if item.LifecycleState != "" { + status.LifecycleState = (*string)(&item.LifecycleState) + } + + if item.PeerDataGuardAssociationId != nil { + status.PeerDataGuardAssociationId = item.PeerDataGuardAssociationId + } + + if item.LifecycleDetails != nil { + status.LifecycleDetails = item.LifecycleDetails + } + + if item.Id != nil { + status.Id = item.Id + } + + if item.PeerDatabaseId != nil { + status.PeerDatabaseId = item.PeerDatabaseId + } + + return true, nil + } + + r.Logger.Info("No Data Guard association found for the database", "DatabaseID", databaseID) + return false, nil + +} + // Function to create KMS vault func (r *DbcsSystemReconciler) createKMSVault(ctx context.Context, kmsConfig *databasev4.KMSConfig, kmsClient keymanagement.KmsManagementClient, kmsInst *databasev4.KMSDetailsStatus) (*keymanagement.CreateVaultResponse, error) { // Dereference the ConfigurationProvider pointer @@ -1327,100 +1955,6 @@ func (r *DbcsSystemReconciler) getSecret(ctx context.Context, namespace, secretN return "", fmt.Errorf("secret %s is empty", secretName) } -// func (r *DbcsSystemReconciler) cloneDbSystem(ctx context.Context, dbcsInst *databasev4.DbcsSystem, provider common.ConfigurationProvider) error { - -// // Initialize OCI clients -// dbClient, err := database.NewDatabaseClientWithConfigurationProvider(provider) -// if err != nil { -// return fmt.Errorf("failed to create OCI database client: %v", err) -// } - -// // Get DB System details -// compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Status.Id) -// if err != nil { -// fmt.Printf("Failed to get compartment ID: %v\n", err) -// return err -// } - -// dbHomeId, err := r.getDbHomeIdByDbSystemID(ctx, compartmentId, *dbcsInst.Status.Id) -// if err != nil { -// fmt.Printf("Failed to get DB Home ID: %v\n", err) -// return err -// } - -// databaseIds, err := r.getDatabaseIDByDbSystemID(ctx, *dbcsInst.Status.Id, compartmentId, dbHomeId) -// if err != nil { -// fmt.Printf("Failed to get database IDs: %v\n", err) -// return err -// } - -// // Use the first database ID for cloning -// if len(databaseIds) == 0 { -// return fmt.Errorf("no databases found in the DB system") -// } - -// // Retrieve details of the database to clone -// sourceDatabaseId := databaseIds[0] -// _, err = dbClient.GetDatabase(ctx, database.GetDatabaseRequest{ -// DatabaseId: common.String(sourceDatabaseId), -// }) -// if err != nil { -// return fmt.Errorf("failed to get source database details: %v", err) -// } - -// // adminPassword, err := dbcsv1.GetAdminPassword(kubeClient, dbcsInstance) -// // if err != nil { -// // log.Fatalf("Error getting admin password: %v", err) -// // } - -// // tdePassword, err := GetTdePassword(kubeClient, dbcsInstance) -// // if err != nil { -// // log.Fatalf("Error getting TDE password: %v", err) -// // } - -// // Define the details for creating the database from the existing DB system -// // createDatabaseDetails := CreateDatabaseBaseWrapper{ -// // CreateDatabaseFromDbSystemDetails: database.CreateDatabaseFromDbSystemDetails{ -// // AdminPassword: common.String(adminPassword), // Replace with actual admin password -// // DbName: common.String(dbcsInst.Spec.DbSystem.DbName), // Use the dbName from DbcsSystemSpec -// // DbDomain: common.String(dbcsInst.Spec.DbSystem.DbDomain), // Use the dbDomain from DbcsSystemSpec -// // DbUniqueName: common.String(dbcsInst.Spec.DbSystem.DbUniqueName), // Use the dbUniqueName from DbcsSystemSpec -// // DbBackupConfig: &database.DbBackupConfig{ -// // AutoBackupEnabled: dbcsInst.Spec.DbSystem.DbBackupConfig.AutoBackupEnabled, -// // RecoveryWindowInDays: dbcsInst.Spec.DbSystem.DbBackupConfig.RecoveryWindowsInDays, -// // }, -// // FreeformTags: dbcsInst.Spec.DbSystem.Tags, -// // DefinedTags: map[string]map[string]interface{}{ -// // "Namespace": { -// // "TagKey": "TagValue", // Replace with actual defined tags if needed -// // }, -// // }, -// // }, -// // } -// // createDatabaseRequest := database.CreateDatabaseRequest{ -// // CreateNewDatabaseDetails: &createDatabaseDetails, -// // } - -// // createDatabaseResponse, err := dbClient.CreateDatabase(ctx, createDatabaseRequest) -// // if err != nil { -// // return fmt.Errorf("failed to create database from DB system: %v", err) -// // } - -// // // Update instance status with the new database ID -// // dbcsInst.Status.DbInfo = append(dbcsInst.Status.DbInfo, databasev4.DbStatus{ -// // Id: createDatabaseResponse.Database.Id, -// // DbName: dbcsInst.Spec.DbSystem.DbName, -// // DbUniqueName: dbcsInst.Spec.DbSystem.DbUniqueName, -// // }) - -// // err = r.KubeClient.Status().Update(ctx, dbcsInst) -// // if err != nil { -// // return fmt.Errorf("failed to update instance status with database ID: %v", err) -// // } - -// return nil -// } - // Convert DbBackupConfigAutoBackupWindowEnum to *string func autoBackupWindowEnumToStringPtr(enum *database.DbBackupConfigAutoBackupWindowEnum) *string { if enum == nil { diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md index 2c06511c..596b194d 100644 --- a/docs/dbcs/README.md +++ b/docs/dbcs/README.md @@ -44,7 +44,8 @@ default Active 118d kube-node-lease Active 118d kube-public Active 118d kube-system Active 118d -oracle-database-operator-system Active 10m <<<< namespace to deploy the Oracle Database Operator +oracle-database-operator-system Active 10m # <<<< NAMESPACE TO DEPLOY ORACLE DB OPERATOR + [root@test-server oracle-database-operator]# kubectl get all -n oracle-database-operator-system @@ -73,7 +74,7 @@ certificaterequests.cert-manager.io 2022-02-22T23:21:35Z certificates.cert-manager.io 2022-02-22T23:21:36Z challenges.acme.cert-manager.io 2022-02-22T23:21:36Z clusterissuers.cert-manager.io 2022-02-22T23:21:36Z -dbcssystems.database.oracle.com 2022-02-22T23:23:25Z <<<< CRD for OBDS Controller +dbcssystems.database.oracle.com 2022-02-22T23:23:25Z # <<<< CRD for OBDS Controller issuers.cert-manager.io 2022-02-22T23:21:36Z orders.acme.cert-manager.io 2022-02-22T23:21:37Z shardingdatabases.database.oracle.com 2022-02-22T23:23:25Z @@ -174,6 +175,13 @@ For more informatoin about the multiple use cases available to you to deploy and [14. Migrate to KMS vault from TDE Wallet password encryption of an existing OBDS System already deployed in OCI Base OBDS Service](./provisioning/migrate_to_kms.md) [15. Clone DB System from Existing DB System in OCI OBDS Service](./provisioning/clone_from_existing_dbcs.md) [16. Clone DB System from Backup of Existing DB System in OCI OBDS Service](./provisioning/clone_from_backup_dbcs.md) +[17. Clone DB System from Existing Database of DB System in OCI OBDS Service](./provisioning/clone_from_database.md) +[18. Create Backup of Existing Database of DB System in OCI OBDS Service](./provisioning/backup_of_database.md) +[19. Restore from Backup of Existing Database of DB System in OCI OBDS Service](./provisioning/restore_of_database.md) +[20. Setup Dataguard Association to Existing Database of DB System in OCI Base DBCS Service](./provisioning/dataguard_to_database.md) +[21. Disable Dataguard Association to Existing Database of DB System and Terminate Peer DB System in OCI Base DBCS Service](./provisioning/disable_dataguard_to_database.md) +[22. Patching Existing Database of DB System in OCI Base DBCS Service](./provisioning/patching_database.md) +[23. Upgrading Existing Database of DB System in OCI Base DBCS Service](./provisioning/upgrading_database.md) [17. Clone DB System from Existing Database of DB System in OCI OBDS Service](./provisioning/clone_from_database.md) ## Connecting to OCI OBDS database deployed using Oracle DB Operator OBDS Controller diff --git a/docs/dbcs/provisioning/backup_database_sample_output.log b/docs/dbcs/provisioning/backup_database_sample_output.log new file mode 100644 index 00000000..6d76dc4c --- /dev/null +++ b/docs/dbcs/provisioning/backup_database_sample_output.log @@ -0,0 +1,100 @@ +2025-04-09T08:57:48Z INFO Backup creation initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga"} +2025-04-09T08:57:48Z INFO Waiting for backup to reach ACTIVE state... {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9"} +2025-04-09T08:58:19Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T08:58:48Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T08:59:18Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T08:59:48Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T09:00:18Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T09:00:48Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T09:09:48Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T09:10:18Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T09:10:48Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T09:11:18Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T09:11:48Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T09:12:18Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "CREATING"} +2025-04-09T09:12:48Z INFO Polling backup status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga", "State": "ACTIVE"} +2025-04-09T09:12:48Z INFO Backup completed successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga"} +2025-04-09T09:12:51Z INFO Backup Completed successfully {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "a1edd2a5-7a6a-4dde-a071-e2a634b4b3b9", "BackupID": "ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga"} + + +kubectl describe dbcssystems.database.oracle.com/dbcssystem-existing +Warning: To increase security of your API key located at /home/sauahuja/.oci/oci_api_key.pem, append an extra line with 'OCI_API_KEY' at the end. For more information, refer to https://docs.oracle.com/iaas/Content/API/Concepts/apisigningkey.htm. To suppress the warning, set the env variable SUPPRESS_LABEL_WARNING=True +Name: dbcssystem-existing +Namespace: default +Labels: +Annotations: lastSuccessfulSpec: + {"dbSystem":{"compartmentId":"","availabilityDomain":"","subnetId":"","shape":"","hostName":"","backupDisplayName":"Full-Backup","dbAdminP... +API Version: database.oracle.com/v4 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2025-04-09T08:57:34Z + Generation: 2 + Resource Version: 168679344 + UID: 0633f774-92a9-4ab0-b65e-616be3fc7adb +Spec: + Db System: + Availability Domain: + Backup Display Name: Full-Backup + Compartment Id: + Db Admin Password Secret: + Db Backup Config: + Host Name: + Kms Config: + Shape: + Subnet Id: + Enable Backup: true + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyaezbusohovqidnjud2w5wmeisqydsmkqlzcghctvrscfq + Kms Config: + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey +Status: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Backups: + Backup Id: ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyadzfhqva7rs7ttlz3ql3vwexfl4yoghzsllg7hy7oxdga + Name: Full-Backup-20250409-085744 + Timestamp: 2025-04-09T08:57:53Z + Backup Id: ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyazmu2w7bjtivgbjj37aym6cfsnutruzyrb2t7uiuidksq + Name: Saurabh-JustInTime-manual-20250408-144538 + Timestamp: 2025-04-08T14:45:48Z + Backup Id: ocid1.dbbackup.oc1.ap-mumbai-1.anrg6ljrabf7htyab6cgmv5vs6rtyfusfw3w7dj5e5ck5h6x32xuz253qcla + Name: backup-20250408-142512 + Timestamp: 2025-04-08T14:25:22Z + Cpu Core Count: 1 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 256 + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Display Name: dbsystem123 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyaezbusohovqidnjud2w5wmeisqydsmkqlzcghctvrscfq + Kms Details Status: + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host123 + Listener Port: 1521 + Scan Dns Name: host123-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Pdb Details Status: + Pdb Config Status: + Pdb Name: cdb123_pdb1 + Pdb State: AVAILABLE + Pluggable Database Id: ocid1.pluggabledatabase.oc1.ap-mumbai-1.anrg6ljrabf7htyao76wku3parutii5ojxmejzfbevloxcizvt3lt5pyzvja + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrjajnyzhsjbuaxu76mc3ku2xr2itlip45hj75rhyd7xtdcvtar77q + Operation Type: Create DB System + Percent Complete: 100 + Time Accepted: 2025-04-06 04:55:35.586 +0000 UTC + Time Finished: 2025-04-06 07:24:02.505 +0000 UTC + Time Started: 2025-04-06 04:55:39.439 +0000 UTC +Events: +basedb/ $ \ No newline at end of file diff --git a/docs/dbcs/provisioning/backup_of_database.md b/docs/dbcs/provisioning/backup_of_database.md new file mode 100644 index 00000000..ed948e02 --- /dev/null +++ b/docs/dbcs/provisioning/backup_of_database.md @@ -0,0 +1,35 @@ +# Create Backup of Existing Database of DB System in OCI OBDS Service + +In this use case, an existing OCI OBDS system deployed earlier with existing Database is going to have full manual backup in OCI Base OBDS Service using existing Compartment ID and DB System Id. + +As an pre-requisite, get the details of OCID of database of an existing OBDS System which you want to backup. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +This example uses `backup_of_database.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +- OCID of existing as DB System as `id` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- enableBackup: as `true` +- Specification of backup prefix as - Details of `backupDisplayName`. This is optional field. If it is not provided, controller will use keyword `backup` as prefix. In both cases of displayName whether its provided or not, suffix of backup name is timestamp to have uniqueness of backup name of database created. +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [backup_of_database.yaml](./backup_of_database.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server OBDS]# kubectl apply -f backup_of_database.yaml +dbcssystem.database.oracle.com/dbcssystem-backup configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./backup_database_sample_output.log) is the sample output for backup an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. \ No newline at end of file diff --git a/docs/dbcs/provisioning/backup_of_database.yaml b/docs/dbcs/provisioning/backup_of_database.yaml new file mode 100644 index 00000000..f9edd741 --- /dev/null +++ b/docs/dbcs/provisioning/backup_of_database.yaml @@ -0,0 +1,11 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-backup +spec: + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyaezbusohovqidnjud2w5wmeisqydsmkqlzcghctvrscfq" + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + enableBackup: true + dbSystem: + backupDisplayName: "Full-Backup" \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log b/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log index 82531993..43480edc 100644 --- a/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log @@ -72,4 +72,68 @@ 2024-09-18T14:05:43Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} 2024-09-18T14:06:44Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} 2024-09-18T14:07:45Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} -2024-09-18T14:08:46Z INFO DB Cloning completed successfully from provided backup DB system. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} \ No newline at end of file +2024-09-18T14:08:46Z INFO DB Cloning completed successfully from provided backup DB system. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "a299bb7c-22eb-4db4-9037-ce81897345df"} + + +#kubectl describe dbcssystems/dbcssystem-clone +Name: dbcssystem-clone +Namespace: default +Labels: +Annotations: +API Version: database.oracle.com/v4 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2024-12-10T14:05:34Z + Generation: 1 + Resource Version: 118098180 + UID: 6b96d17c-b788-4bb7-bc0e-098ade53100c +Spec: + Db Clone: + Db Admin Pasword Secret: admin-password + Db Db Unique Name: + Db Name: db1212 + Display Name: dbsystem01312 + Domain: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1213 + License Model: BRING_YOUR_OWN_LICENSE + Ssh Public Keys: + oci-publickey + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Db System: + Availability Domain: + Compartment Id: + Db Admin Pasword Secret: + Db Backup Config: + Host Name: + Kms Config: + Shape: + Subnet Id: + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Config: + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey + Setup DB Cloning: true +Status: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 1 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 512 + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Display Name: dbsystem1234 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Listener Port: 1521 + Scan Dns Name: host1234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log b/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log index 2881051d..9889daa2 100644 --- a/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log @@ -36,4 +36,68 @@ .oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone" , "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} 2024-09-20T10:00:46Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} -2024-09-20T10:01:47Z INFO DB Cloning completed successfully from provided backup DB system {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} \ No newline at end of file +2024-09-20T10:01:47Z INFO DB Cloning completed successfully from provided backup DB system {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone", "reconcileID": "3ea6b5b1-1196-4279-bf8f-9e663f9a5543"} + + +#kubectl describe dbcssystems/dbcssystem-clone +Name: dbcssystem-clone +Namespace: default +Labels: +Annotations: +API Version: database.oracle.com/v4 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2024-12-10T14:05:34Z + Generation: 1 + Resource Version: 118098180 + UID: 6b96d17c-b788-4bb7-bc0e-098ade53100c +Spec: + Db Clone: + Db Admin Pasword Secret: admin-password + Db Db Unique Name: + Db Name: db1212 + Display Name: dbsystem01312 + Domain: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1213 + License Model: BRING_YOUR_OWN_LICENSE + Ssh Public Keys: + oci-publickey + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Db System: + Availability Domain: + Compartment Id: + Db Admin Pasword Secret: + Db Backup Config: + Host Name: + Kms Config: + Shape: + Subnet Id: + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Config: + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey + Setup DB Cloning: true +Status: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 1 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 512 + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Display Name: dbsystem1234 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Listener Port: 1521 + Scan Dns Name: host1234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log b/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log index 22d86e1e..b85588db 100644 --- a/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log +++ b/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log @@ -58,3 +58,66 @@ 2024-09-17T12:35:21Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} 2024-09-17T12:36:22Z INFO DB System current state is still:PROVISIONING. Sleeping for 60 seconds. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} 2024-09-17T12:36:22Z INFO DB Cloning completed successfully from provided db system {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-clone.yaml","namespace":"default"}, "namespace": "default", "name": "dbcssystem-clone.yaml", "reconcileID": "bf0c98c6-13b6-4c15-938a-3594cd4cf1f3"} + +#kubectl describe dbcssystems/dbcssystem-clone +Name: dbcssystem-clone +Namespace: default +Labels: +Annotations: +API Version: database.oracle.com/v4 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2024-12-10T14:05:34Z + Generation: 1 + Resource Version: 118098180 + UID: 6b96d17c-b788-4bb7-bc0e-098ade53100c +Spec: + Db Clone: + Db Admin Pasword Secret: admin-password + Db Db Unique Name: + Db Name: db1212 + Display Name: dbsystem01312 + Domain: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1213 + License Model: BRING_YOUR_OWN_LICENSE + Ssh Public Keys: + oci-publickey + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Db System: + Availability Domain: + Compartment Id: + Db Admin Pasword Secret: + Db Backup Config: + Host Name: + Kms Config: + Shape: + Subnet Id: + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + Kms Config: + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey + Setup DB Cloning: true +Status: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 1 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 512 + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Display Name: dbsystem1234 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-nodesubnet-quick-cluster1-2bebe95db-regional + Domain Name: subdda0b5eaa.cluster1.oraclevcn.com + Host Name: host1234 + Listener Port: 1521 + Scan Dns Name: host1234-scan.subdda0b5eaa.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq + Time Zone: UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/dataguard_in_database.yaml b/docs/dbcs/provisioning/dataguard_in_database.yaml new file mode 100644 index 00000000..25fa8dd1 --- /dev/null +++ b/docs/dbcs/provisioning/dataguard_in_database.yaml @@ -0,0 +1,20 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-existing +spec: + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq" + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + dataGuard: + primaryDatabaseId: "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a" + dbAdminPasswordSecret: "admin-password" + protectionMode: "MAXIMUM_PERFORMANCE" + transportType: "ASYNC" + enabled: true + displayName: "standbydbsystem2" + availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" + shape: "VM.Standard2.1" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaaklvzufcgma65ibn4al7xukjmyfz7nnrpaewfzbffz5zrnz3htweq" + hostName: "shost1234" + diff --git a/docs/dbcs/provisioning/dataguard_in_database_sample_output.log b/docs/dbcs/provisioning/dataguard_in_database_sample_output.log new file mode 100644 index 00000000..e21c3130 --- /dev/null +++ b/docs/dbcs/provisioning/dataguard_in_database_sample_output.log @@ -0,0 +1,226 @@ +2025-06-18T11:52:25Z INFO No Data Guard association found for the database {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a"} +2025-06-18T11:52:28Z INFO Data Guard association created successfully. {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62"} +2025-06-18T11:53:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T11:53:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T11:54:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T11:54:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T11:55:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T11:55:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T11:56:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T11:56:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T11:57:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T11:57:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T11:58:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T11:58:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T11:59:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T11:59:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:00:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:00:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:01:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:01:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:02:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:02:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:03:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:03:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:04:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:04:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:05:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:05:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:06:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:06:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:07:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:07:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:08:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:08:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:09:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:09:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:10:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:10:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:11:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:11:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:12:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:12:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:13:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:13:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:14:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:14:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:15:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:15:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:16:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:16:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:17:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:17:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:18:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:18:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:19:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:19:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:20:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:20:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:21:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:21:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:22:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:22:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:23:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:23:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:24:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:24:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:25:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:25:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:26:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:26:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:27:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:27:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:28:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:28:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:29:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:29:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:30:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:30:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:31:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:31:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:32:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:32:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:33:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:33:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:34:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:34:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:35:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:35:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:36:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:36:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:37:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:37:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:38:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:38:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:39:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:39:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:40:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:40:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:41:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:41:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:42:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:42:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:43:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:43:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:44:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T12:44:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T12:45:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T13:42:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T13:42:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T13:43:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T13:43:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T13:44:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T13:44:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} +2025-06-18T13:45:28Z INFO Polling Data Guard association state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a", "AssociationID": "ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra"} +2025-06-18T13:45:29Z INFO Current state {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "60685efa-37f7-4185-b109-9b25c334ec62", "State": "PROVISIONING"} + + + + kubectl describe dbcssystems.database.oracle.com dbcssystem-existing +Name: dbcssystem-existing +Namespace: default +Labels: +Annotations: lastSuccessfulSpec: + {"dbSystem":{"compartmentId":"","availabilityDomain":"","subnetId":"","shape":"","hostName":"","dbAdminPasswordSecret":"","dbBackupConfig"... +API Version: database.oracle.com/v4 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2025-06-18T11:47:51Z + Generation: 3 + Resource Version: 198491318 + UID: dd584c17-bed5-477c-b1ae-90deed556ee9 +Spec: + Data Guard: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Db Admin Password Secret: admin-password + Display Name: sdbsystem1234 + Enabled: true + Host Name: shost1234 + Primary Database Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a + Protection Mode: MAXIMUM_PERFORMANCE + Shape: VM.Standard2.1 + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaaklvzufcgma65ibn4al7xukjmyfz7nnrpaewfzbffz5zrnz3htweq + Transport Type: ASYNC + Db System: + Availability Domain: + Compartment Id: + Db Admin Password Secret: + Db Backup Config: + Host Name: + Kms Config: + Shape: + Subnet Id: + Kms Config: + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey +Status: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 1 + Data Guard Status: + Db Admin Password Secret: admin-password + Id: ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyaqnw7fmovwf6hn4ja2zw6lk663tmq22bl6333m53q36ra + Lifecycle State: AVAILABLE + Peer Data Guard Association Id: ocid1.dgassociation.oc1.ap-mumbai-1.anrg6ljrabf7htyabee2lili5bw2fhfykp6ex762nvg5vihc4zl33oxvzp4q + Peer Database Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya5zaiiz6ydeomvqysoin5cylevcy5z5ritblpnl3qjhga + Peer Db Home Id: ocid1.dbhome.oc1.ap-mumbai-1.anrg6ljrqlb5nxiaewh5smvdfbv7t5xoz4pjagwcm5py72eoyoejlxfcyhzq + Peer Db System Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyaywwjkya5wcimrcsxip777dld3fluu2nlb3kf6mjjtnuq + Peer Role: STANDBY + Primary Database Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a + Protection Mode: MAXIMUM_PERFORMANCE + Transport Type: ASYNC + Data Storage Percentage: 80 + Data Storage Size In G Bs: 256 + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Display Name: sdbsystem1234 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyaywwjkya5wcimrcsxip777dld3fluu2nlb3kf6mjjtnuq + Kms Details Status: + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-k8sApiEndpoint-subnet-quick-cluster1-2bebe95db-regional + Domain Name: sub0ea2e07ef.cluster1.oraclevcn.com + Host Name: shost1234 + Listener Port: 1521 + Scan Dns Name: shost1234-scan.sub0ea2e07ef.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaaklvzufcgma65ibn4al7xukjmyfz7nnrpaewfzbffz5zrnz3htweq + Time Zone: UTC + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr745m6kvhdlchnbfpgfi53oabqpbp6edr4sihrrlhh3vpx272q32a + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-18 11:23:18.919 +0000 UTC + Time Finished: 2025-06-18 11:28:17.598 +0000 UTC + Time Started: 2025-06-18 11:23:28.016 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrxow6rzn63igski66j7264ft3bnnjkvg74vd6xf77rqg6tkakrlkq + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-18 11:15:30.139 +0000 UTC + Time Finished: 2025-06-18 11:22:04.763 +0000 UTC + Time Started: 2025-06-18 11:15:36.599 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrzj4cnxwitxjjlcwwymbpby7chckrq5vtm2hpykprgxrm5rtw2eqq + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-18 10:59:14.372 +0000 UTC + Time Finished: 2025-06-18 11:15:15.936 +0000 UTC + Time Started: 2025-06-18 10:59:20.854 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrsfschtlkucf65m575jyswb6xsk3mpfx26gsrumrfa6huzhjswbxq + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-18 07:20:00.043 +0000 UTC + Time Finished: 2025-06-18 07:43:54.273 +0000 UTC + Time Started: 2025-06-18 07:20:07.169 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljryitxrazcuutk3ttxscpudqtaeq7o3havhhh3aov5tdtvuhmnclma + Operation Type: Create DB System + Percent Complete: 100 + Time Accepted: 2025-06-13 11:53:36.483 +0000 UTC + Time Finished: 2025-06-13 14:10:52.388 +0000 UTC + Time Started: 2025-06-13 11:53:43.916 +0000 UTC +Events: diff --git a/docs/dbcs/provisioning/dataguard_to_database.md b/docs/dbcs/provisioning/dataguard_to_database.md new file mode 100644 index 00000000..a8bc1096 --- /dev/null +++ b/docs/dbcs/provisioning/dataguard_to_database.md @@ -0,0 +1,33 @@ +# Setup Dataguard Association to Existing Database of DB System in OCI Base DBCS Service + +In this use case, an existing OCI DBCS system deployed earlier with existing Database is going to have dataguard association in OCI Base DBCS Service using existing Database ID. + +As an pre-requisite, get the details of OCID of database of an existing DBCS System which you want to clone. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +This example uses `dataguard_in_database.yaml` to clone a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- Specification of dataGuard as - Details for dataguard setup `primaryDatabaseId`,`dbAdminPasswordSecret`, `protectionMode`,`transportType`,`displayName`,`availabilityDomain`,`shape`,`subnetId`,`hostName`. +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [dataguard_in_database.yaml](./dataguard_in_database.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f dataguard_in_database.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./dataguard_in_database_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/disable_dataguard_in_database.yaml b/docs/dbcs/provisioning/disable_dataguard_in_database.yaml new file mode 100644 index 00000000..f8cb8463 --- /dev/null +++ b/docs/dbcs/provisioning/disable_dataguard_in_database.yaml @@ -0,0 +1,12 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-existing +spec: + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq" + ociConfigMap: "oci-cred-mumbai" + ociSecret: "oci-privatekey" + dataGuard: + enabled: false # <=== Mark for deletion (don't enable) + isDelete: true + peerDbSystemId: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyakjqrailvboa6efrypg3oxaqis44433rjd3hjuzeygcuq \ No newline at end of file diff --git a/docs/dbcs/provisioning/disable_dataguard_in_database_sample_output.log b/docs/dbcs/provisioning/disable_dataguard_in_database_sample_output.log new file mode 100644 index 00000000..af0c1e61 --- /dev/null +++ b/docs/dbcs/provisioning/disable_dataguard_in_database_sample_output.log @@ -0,0 +1,141 @@ +2025-06-20T16:40:02Z INFO Terminating peer DB system {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "peerDbSystemId": "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyakjqrailvboa6efrypg3oxaqis44433rjd3hjuzeygcuq"} +2025-06-20T16:40:03Z INFO Waiting for work request to complete {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "workRequestID": "ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrm6qzswsn5x3payealiktawjfpnfggonbbznu3ylxzth2nofn3p4a"} +2025-06-20T16:40:04Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "ACCEPTED"} +2025-06-20T16:40:35Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:41:06Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:41:36Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:42:07Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:42:38Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:43:09Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:43:40Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:44:10Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:44:41Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:45:12Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:45:43Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:46:14Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:46:45Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:47:15Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:47:46Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:48:17Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:48:48Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:49:19Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:49:50Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:50:21Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:50:52Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:51:22Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:51:53Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:52:24Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "IN_PROGRESS"} +2025-06-20T16:52:55Z INFO Polling WorkRequest status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "status": "SUCCEEDED"} +2025-06-20T16:52:55Z INFO WorkRequest succeeded {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36", "workRequestID": "ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrm6qzswsn5x3payealiktawjfpnfggonbbznu3ylxzth2nofn3p4a"} +2025-06-20T16:52:55Z INFO Successfully deleted Data Guard association {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-existing","namespace":"default"}, "namespace": "default", "name": "dbcssystem-existing", "reconcileID": "61f3d6fa-6bc2-480f-a16b-2bf3f0262c36"} + +kubectl describe dbcssystems.database.oracle.com dbcssystem-existing +Name: dbcssystem-existing +Namespace: default +Labels: +Annotations: lastSuccessfulSpec: + {"id":"ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq","ociConfigMap":"oci-cred-mumbai","ociS... +API Version: database.oracle.com/v4 +Kind: DbcsSystem +Metadata: + Creation Timestamp: 2025-06-20T16:39:48Z + Generation: 2 + Resource Version: 199426196 + UID: 36dedd91-369a-4f7f-bde7-3dcd0fb80bc0 +Spec: + Data Guard: + Is Delete: true + Peer Db System Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyakjqrailvboa6efrypg3oxaqis44433rjd3hjuzeygcuq + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq + Oci Config Map: oci-cred-mumbai + Oci Secret: oci-privatekey +Status: + Availability Domain: OLou:AP-MUMBAI-1-AD-1 + Cpu Core Count: 1 + Data Guard Status: + Data Storage Percentage: 80 + Data Storage Size In G Bs: 256 + Db Clone Status: + Db Db Unique Name: + Host Name: + Db Edition: ENTERPRISE_EDITION_HIGH_PERFORMANCE + Db Info: + Db Home Id: ocid1.dbhome.oc1.ap-mumbai-1.anrg6ljrqlb5nxia62a4renaws56iay2nboazrndrw2p5fxgdtxbjggjkjbq + Db Name: cdb1234 + Db Unique Name: cdb1234_dgk_bom + Db Workload: OLTP + Id: ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a + Display Name: dbsystem123 + Id: ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq + Kms Details Status: + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: oke-k8sApiEndpoint-subnet-quick-cluster1-2bebe95db-regional + Domain Name: sub0ea2e07ef.cluster1.oraclevcn.com + Host Name: host123 + Listener Port: 1521 + Scan Dns Name: host123-scan.sub0ea2e07ef.cluster1.oraclevcn.com + Vcn Name: oke-vcn-quick-cluster1-2bebe95db + Node Count: 1 + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaaklvzufcgma65ibn4al7xukjmyfz7nnrpaewfzbffz5zrnz3htweq + Time Zone: UTC + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrezh3gctuqydvhcwp3s7ciihu2wnjzzrmz7rfw5jnkiwarjxbz6ia + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-20 11:51:32.803 +0000 UTC + Time Finished: 2025-06-20 14:07:19.763 +0000 UTC + Time Started: 2025-06-20 11:51:39.534 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljromubu2srfzznxbgv6ussbgbii6ln6t7sb6nkpwkab4meqxoionjq + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-19 16:39:05.542 +0000 UTC + Time Finished: 2025-06-19 18:37:19.007 +0000 UTC + Time Started: 2025-06-19 16:39:13.226 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr7oy4dlwckl7r6ukwg5mxxnjjocp5wqp5ehsx3zblow2zza6h7j4q + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-19 09:30:22.587 +0000 UTC + Time Finished: 2025-06-19 11:51:06.723 +0000 UTC + Time Started: 2025-06-19 09:30:30.03 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrlansxzbmlry2uw3f2wlg3c32poxxpqqxiqvs4jeza5kp3a3zjdaa + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-18 11:52:28.133 +0000 UTC + Time Finished: 2025-06-18 13:54:02.717 +0000 UTC + Time Started: 2025-06-18 11:52:31.256 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr745m6kvhdlchnbfpgfi53oabqpbp6edr4sihrrlhh3vpx272q32a + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-18 11:23:18.919 +0000 UTC + Time Finished: 2025-06-18 11:28:17.598 +0000 UTC + Time Started: 2025-06-18 11:23:28.016 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrxow6rzn63igski66j7264ft3bnnjkvg74vd6xf77rqg6tkakrlkq + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-18 11:15:30.139 +0000 UTC + Time Finished: 2025-06-18 11:22:04.763 +0000 UTC + Time Started: 2025-06-18 11:15:36.599 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrzj4cnxwitxjjlcwwymbpby7chckrq5vtm2hpykprgxrm5rtw2eqq + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-18 10:59:14.372 +0000 UTC + Time Finished: 2025-06-18 11:15:15.936 +0000 UTC + Time Started: 2025-06-18 10:59:20.854 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrsfschtlkucf65m575jyswb6xsk3mpfx26gsrumrfa6huzhjswbxq + Operation Type: Create Data Guard + Percent Complete: 100 + Time Accepted: 2025-06-18 07:20:00.043 +0000 UTC + Time Finished: 2025-06-18 07:43:54.273 +0000 UTC + Time Started: 2025-06-18 07:20:07.169 +0000 UTC + Operation Id: ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljryitxrazcuutk3ttxscpudqtaeq7o3havhhh3aov5tdtvuhmnclma + Operation Type: Create DB System + Percent Complete: 100 + Time Accepted: 2025-06-13 11:53:36.483 +0000 UTC + Time Finished: 2025-06-13 14:10:52.388 +0000 UTC + Time Started: 2025-06-13 11:53:43.916 +0000 UTC +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/disable_dataguard_to_database.md b/docs/dbcs/provisioning/disable_dataguard_to_database.md new file mode 100644 index 00000000..acf4287c --- /dev/null +++ b/docs/dbcs/provisioning/disable_dataguard_to_database.md @@ -0,0 +1,33 @@ +# Disable Dataguard Association to Existing Database of DB System and Terminate Peer DB System in OCI Base DBCS Service + +In this use case, we are going to disable Dataguard and Terminate Peer DB System of an existing OCI OCS system deployed earlier with existing Database and dataguard association. Note: Both disable Dataguard and Terminate Peer DB System will happen together. + +As an pre-requisite, get the details of OCID of database of an existing DBCS System which you want to clone. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +This example uses `dataguard_in_database.yaml` to clone a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- Specification of dataGuard as - Details for dataguard setup `peerDbSystemId`, `enabled: false` and `isDelete: true`. +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [disable_dataguard_in_database.yaml](./disable_dataguard_in_database.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f disable_dataguard_in_database.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./disable_dataguard_in_database_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/disabled_dataguard_to_database.md b/docs/dbcs/provisioning/disabled_dataguard_to_database.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/dbcs/provisioning/patch_dbcs_system.yaml b/docs/dbcs/provisioning/patch_dbcs_system.yaml new file mode 100644 index 00000000..c9b44a8f --- /dev/null +++ b/docs/dbcs/provisioning/patch_dbcs_system.yaml @@ -0,0 +1,11 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-existing +spec: + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq" + ociConfigMap: "oci-cred-mumbai" + ociSecret: "oci-privatekey" + isPatch: true + dbSystem: + dbPatchOcid: "ocid1.dbupdate.oc1.ap-mumbai-1.anrg6ljrt5t4sqqa2z6zz3vxd4u2gmkvrofeatpufsbw332yksvuzc2xmgxq" \ No newline at end of file diff --git a/docs/dbcs/provisioning/patch_dbcs_system_sample_output.log b/docs/dbcs/provisioning/patch_dbcs_system_sample_output.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/dbcs/provisioning/patching_database.md b/docs/dbcs/provisioning/patching_database.md new file mode 100644 index 00000000..89422adc --- /dev/null +++ b/docs/dbcs/provisioning/patching_database.md @@ -0,0 +1,42 @@ +# Patching Existing Database of DB System in OCI Base DBCS Service + +In this use case, an existing OCI OBDS system deployed earlier is going to be patched in OCI Oracle Base Database System (OBDS). Its a 2 Step operation. + +In order to patch OBDS to an existing OBDS system, get the OCID of DB System ID you want to patch. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +```bash +kubectl get dbcssystems +NAME AGE +dbcssystem-existing 3m33s +``` + +Step 2 uses `patch_dbcs_system.yaml` to patch a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: + +- OCID of existing VMDB as `id` to be patched. +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- `isPatch` as true +- Specification of DB System been patched as `dbPatchOcid`. These must be unique and new details for new patched DB system to be created. +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [patch_dbcs_system.yaml](./patch_dbcs_system.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f patch_dbcs_system.yaml +dbcssystem.database.oracle.com/dbcssystem-patch configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./patch_dbcs_system_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/restore_database_sample_output.log b/docs/dbcs/provisioning/restore_database_sample_output.log new file mode 100644 index 00000000..8b5b738a --- /dev/null +++ b/docs/dbcs/provisioning/restore_database_sample_output.log @@ -0,0 +1,22 @@ +2025-04-15T10:28:10Z INFO Initiating restore operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "RestoreOption": {"latest":true}} +2025-04-15T10:28:11Z INFO Restore initiated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "WorkRequestID": "ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljrr3kjbgleo3d3w75q3flj5mk2l4feiptvbzqxlpmdnzgld6jqqyaq"} +2025-04-15T10:28:42Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:29:11Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:29:41Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:30:11Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:30:41Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:31:11Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:31:41Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:32:11Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:32:41Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:33:11Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:33:41Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:42:41Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:43:11Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:43:41Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:44:11Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "UPDATING"} +2025-04-15T10:44:41Z INFO Polling Restore Operation {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq", "State": "AVAILABLE"} +2025-04-15T10:44:41Z INFO Database restore completed {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DatabaseID": "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htyarrvtdd42dwcncvrj3bic2yds2es2ubic3bjiwq4omgxq"} +2025-04-15T10:44:44Z INFO dbcssystem-resource default {"name": "dbcssystem-restore"} +2025-04-15T10:44:44Z INFO dbcssystem-resource default {"name": "dbcssystem-restore"} +2025-04-15T10:44:44Z INFO Restore Operation Completed and lastSuccessfulSpec updated {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-restore","namespace":"default"}, "namespace": "default", "name": "dbcssystem-restore", "reconcileID": "0777d0ff-0746-447e-adac-6d59e37958eb", "DbcsSystem": "dbcssystem-restore"} diff --git a/docs/dbcs/provisioning/restore_of_database.md b/docs/dbcs/provisioning/restore_of_database.md new file mode 100644 index 00000000..4218d0f5 --- /dev/null +++ b/docs/dbcs/provisioning/restore_of_database.md @@ -0,0 +1,34 @@ +# Restore from Backup of Existing Database of DB System in OCI OBDS Service + +In this use case, an existing OCI OBDS system deployed earlier with existing backup of Database is going to have restore in OCI Base OBDS Service using existing DB System Id. + +As an pre-requisite, get the details of OCID of database of an existing OBDS System which you want to backup. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +This example uses `restore_of_database.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +- OCID of existing as DB System as `id` +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- Restore Configuration of taking restore from a backup. Provided one of `latest` , `scn` and `timestamp` under `restoreConfig` to restore to. Do not provided more than one option to restore from. +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [restore_of_database.yaml](./restore_of_database.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server OBDS]# kubectl apply -f restore_of_database.yaml +dbcssystem.database.oracle.com/dbcssystem-restore configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./restore_database_sample_output.log) is the sample output of restore from an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. \ No newline at end of file diff --git a/docs/dbcs/provisioning/restore_of_database.yaml b/docs/dbcs/provisioning/restore_of_database.yaml new file mode 100644 index 00000000..af0bbe64 --- /dev/null +++ b/docs/dbcs/provisioning/restore_of_database.yaml @@ -0,0 +1,11 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-restore +spec: + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyaezbusohovqidnjud2w5wmeisqydsmkqlzcghctvrscfq" + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + dbSystem: + restoreConfig: + latest: true \ No newline at end of file diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml index 0be84c53..8d78fc5d 100644 --- a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml @@ -3,16 +3,24 @@ kind: DbcsSystem metadata: name: dbcssystem-existing spec: +<<<<<<< HEAD + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyamvvn4n2yg6gv6s5c42qgfhtxrzcbdootuiki4wb3s5yq" +======= id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" +>>>>>>> origin/master ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPasswordSecret: "admin-password" +<<<<<<< HEAD + hostName: "host12345" +======= hostName: "host1234" +>>>>>>> origin/master shape: "VM.Standard2.2" - domain: "subdda0b5eaa.cluster1.oraclevcn.com" + domain: "sub0ea2e07ef.cluster1.oraclevcn.com" sshPublicKeys: - "oci-publickey" - subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq" + subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaaklvzufcgma65ibn4al7xukjmyfz7nnrpaewfzbffz5zrnz3htweq" diff --git a/docs/dbcs/provisioning/upgrade_dbcs_system.yaml b/docs/dbcs/provisioning/upgrade_dbcs_system.yaml new file mode 100644 index 00000000..6a100180 --- /dev/null +++ b/docs/dbcs/provisioning/upgrade_dbcs_system.yaml @@ -0,0 +1,12 @@ +apiVersion: database.oracle.com/v4 +kind: DbcsSystem +metadata: + name: dbcssystem-existing +spec: + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq" + ociConfigMap: "oci-cred-mumbai" + ociSecret: "oci-privatekey" + isUpgrade: true + databaseId: "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a" + dbSystem: + dbUpgradeVersion: "23.6.0.24.10" \ No newline at end of file diff --git a/docs/dbcs/provisioning/upgrade_dbcs_system_sample_output.log b/docs/dbcs/provisioning/upgrade_dbcs_system_sample_output.log new file mode 100644 index 00000000..9f83ebf5 --- /dev/null +++ b/docs/dbcs/provisioning/upgrade_dbcs_system_sample_output.log @@ -0,0 +1,2 @@ +2025-07-15T14:13:15Z INFO Database upgrade work request status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-upgrade","namespace":"default"}, "namespace": "default", "name": "dbcssystem-upgrade", "reconcileID": "a07a5fe3-cbf0-4094-a603-51bc8cbb1caf", "Status": "IN_PROGRESS", "WorkRequestID": "ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr4cqbpeuh6rhrrn2anmz3bsvk5b2jp4jxbebr6c7lr5ybmzt6unkq"} +2025-07-15T14:14:16Z INFO Database upgrade work request status {"controller": "dbcssystem", "controllerGroup": "database.oracle.com", "controllerKind": "DbcsSystem", "DbcsSystem": {"name":"dbcssystem-upgrade","namespace":"default"}, "namespace": "default", "name": "dbcssystem-upgrade", "reconcileID": "a07a5fe3-cbf0-4094-a603-51bc8cbb1caf", "Status": "IN_PROGRESS", "WorkRequestID": "ocid1.coreservicesworkrequest.oc1.ap-mumbai-1.abrg6ljr4cqbpeuh6rhrrn2anmz3bsvk5b2jp4jxbebr6c7lr5ybmzt6unkq"} \ No newline at end of file diff --git a/docs/dbcs/provisioning/upgrading_database.md b/docs/dbcs/provisioning/upgrading_database.md new file mode 100644 index 00000000..041c352c --- /dev/null +++ b/docs/dbcs/provisioning/upgrading_database.md @@ -0,0 +1,43 @@ +# Upgrade Existing Database of DB System in OCI Base DBCS Service + +In this use case, an existing OCI OBDS system deployed earlier is going to be upgraded in OCI Oracle Base Database System (OBDS). Its a 2 Step operation. + +In order to upgrade OBDS to an existing OBDS system, get the OCID of DB System ID you want to upgrade. + +**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. + +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +```bash +kubectl get dbcssystems +NAME AGE +dbcssystem-existing 3m33s +``` + +Step 2 uses `upgrade_dbcs_system.yaml` to upgrade a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: + +- OCID of existing VMDB as `id` to be upgraded. +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- `isupgrade` as true +- Specification of DB System been upgraded as `dbUpgradeVersion`. These must be unique and new details for new upgraded DB system to be created. +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [upgrade_dbcs_system.yaml](./upgrade_dbcs_system.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f upgrade_dbcs_system.yaml +dbcssystem.database.oracle.com/dbcssystem-existing configured +``` + +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. + +NOTE: Check the DB Operator Pod name in your environment. + +``` +[root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system +``` + +## Sample Output + +[Here](./upgrade_dbcs_system_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index cce2cc75..17557147 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -673,6 +673,8 @@ spec: - Start - Terminate - Clone + - Switchover + - Failover type: string clone: properties: @@ -1006,6 +1008,8 @@ spec: - Start - Terminate - Clone + - Switchover + - Failover type: string clone: properties: @@ -9378,7 +9382,32 @@ spec: storage: false subresources: status: {} - - name: v4 + - additionalPrinterColumns: + - jsonPath: .status.displayName + name: Display Name + type: string + - jsonPath: .status.dbInfo[0].dbName + name: DB Name + type: string + - jsonPath: .status.state + name: State + type: string + - jsonPath: .status.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .status.dataStorageSizeInGBs + name: Storage (TB) + type: integer + - jsonPath: .status.recoStorageSizeInGB + name: Reco Storage (GB) + type: integer + - jsonPath: .status.storageManagement + name: Storage Mgmt + type: string + - jsonPath: .status.dbInfo[0].connectionString + name: ConnString + type: string + name: v4 schema: openAPIV3Schema: properties: @@ -9390,6 +9419,45 @@ spec: type: object spec: properties: + dataGuard: + properties: + availabilityDomain: + type: string + dbAdminPasswordSecret: + type: string + dbName: + type: string + dbSystemFreeformTags: + additionalProperties: + type: string + type: object + displayName: + type: string + enabled: + type: boolean + hostName: + type: string + isDelete: + type: boolean + peerDbHomeId: + type: string + peerDbSystemId: + type: string + peerRole: + type: string + primaryDatabaseId: + type: string + protectionMode: + type: string + shape: + type: string + sidPrefix: + type: string + subnetId: + type: string + transportType: + type: string + type: object databaseId: type: string dbBackupId: @@ -9439,6 +9507,8 @@ spec: properties: availabilityDomain: type: string + backupDisplayName: + type: string backupSubnetId: type: string clusterName: @@ -9464,10 +9534,16 @@ spec: type: string dbEdition: type: string + dbHomeId: + type: string dbName: type: string + dbPatchOcid: + type: string dbUniqueName: type: string + dbUpgradeVersion: + type: string dbVersion: type: string dbWorkload: @@ -9507,6 +9583,16 @@ spec: type: string privateIp: type: string + restoreConfig: + properties: + latest: + type: boolean + scn: + type: string + timestamp: + format: date-time + type: string + type: object shape: type: string sshPublicKeys: @@ -9525,18 +9611,17 @@ spec: type: string timeZone: type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPasswordSecret - - hostName - - shape - - subnetId type: object + enableBackup: + type: boolean hardLink: type: boolean id: type: string + isPatch: + type: boolean + isUpgrade: + type: boolean kmsConfig: properties: compartmentId: @@ -9590,15 +9675,67 @@ spec: properties: availabilityDomain: type: string + backups: + items: + properties: + backupId: + type: string + name: + type: string + timestamp: + type: string + required: + - backupId + - name + - timestamp + type: object + type: array cpuCoreCount: type: integer + dataGuardStatus: + properties: + dbAdminPasswordSecret: + type: string + dbName: + type: string + dbWorkload: + type: string + id: + type: string + isActiveDataGuardEnabled: + type: boolean + lifecycleDetails: + type: string + lifecycleState: + type: string + peerDataGuardAssociationId: + type: string + peerDatabaseId: + type: string + peerDbHomeId: + type: string + peerDbSystemId: + type: string + peerRole: + type: string + primaryDatabaseId: + type: string + protectionMode: + type: string + shape: + type: string + subnetId: + type: string + transportType: + type: string + type: object dataStoragePercentage: type: integer dataStorageSizeInGBs: type: integer dbCloneStatus: properties: - dbAdminPaswordSecret: + dbAdminPasswordSecret: type: string dbDbUniqueName: type: string @@ -9629,6 +9766,10 @@ spec: dbInfo: items: properties: + connectionString: + type: string + connectionStringLong: + type: string dbHomeId: type: string dbName: @@ -9666,6 +9807,8 @@ spec: type: object licenseModel: type: string + message: + type: string network: properties: clientSubnet: @@ -15578,26 +15721,6 @@ metadata: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert name: oracle-database-operator-mutating-webhook-configuration webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackupv4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -15742,26 +15865,6 @@ webhooks: resources: - shardingdatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackupv1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -16290,7 +16393,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:restart-operator-sa + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa imagePullPolicy: Always name: manager ports: From 280b4c2e62d954f0f0db8b11e5de30fa5cb9b70d Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Mon, 28 Jul 2025 09:56:10 +0000 Subject: [PATCH 229/414] new changes for oracle restart --- apis/database/v4/oraclerestart_webhook.go | 14 ++ commons/oraclerestart/oraclerestartprov.go | 88 ++++----- commons/oraclerestart/utils/utils.go | 10 ++ config/webhook/manifests.yaml | 40 ----- .../database/oraclerestart_controller.go | 16 +- docs/oraclerestart/README.md | 3 +- .../oraclerestart_prov_rupatch_pvc.yaml | 152 ++++++++++++++++ .../orestart_rupatch_pvc_object.txt | 169 ++++++++++++++++++ .../provisioning_oracle_restart_db_rupatch.md | 2 +- ...provisioning_oracle_restart_rupatch_pvc.md | 72 ++++++++ oracle-database-operator.yaml | 44 +---- 11 files changed, 486 insertions(+), 124 deletions(-) create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml create mode 100644 docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index f9660314..59e7095e 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -553,6 +553,20 @@ func (r *OracleRestart) validateGeneric() field.ErrorList { } } + if r.Spec.ConfigParams.RuPatchLocation != "" { + _, isPVCKey := r.Spec.InstDetails.PvcName[r.Spec.ConfigParams.RuPatchLocation] + if !isPVCKey { + // Not found in PVC map, treat as direct path — validate format + if !strings.HasPrefix(r.Spec.ConfigParams.RuPatchLocation, "/") { + validationErrs = append(validationErrs, + field.Invalid( + field.NewPath("spec").Child("configParams").Child("ruPatchLocation"), + r.Spec.ConfigParams.RuPatchLocation, + "ruPatchLocation must be either a key in instDetails.pvcName or an absolute path starting with '/'")) + } + } + } + if r.Spec.Image == "" { validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("Image"), r.Spec.Image, diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 86f2232c..10b0ef87 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -312,9 +312,16 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: OracleRestartSpex.HostSwLocation}}}) } - if instance.Spec.ConfigParams != nil { - if instance.Spec.ConfigParams.HostSwStageLocation != "" { - result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-swstage-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: instance.Spec.ConfigParams.HostSwStageLocation}}}) + if instance.Spec.ConfigParams != nil && instance.Spec.ConfigParams.HostSwStageLocation != "" { + if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.HostSwStageLocation]; !exists { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-swstage-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.HostSwStageLocation, + }, + }, + }) } } @@ -325,26 +332,30 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac } if instance.Spec.ConfigParams != nil { - if len(instance.Spec.ConfigParams.RuPatchLocation) != 0 { - result = append(result, corev1.Volume{ - Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: instance.Spec.ConfigParams.RuPatchLocation, + if instance.Spec.ConfigParams.RuPatchLocation != "" { + if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.RuPatchLocation]; !exists { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.RuPatchLocation, + }, }, - }, - }) + }) + } } - if len(instance.Spec.ConfigParams.OPatchLocation) != 0 { - result = append(result, corev1.Volume{ - Name: OracleRestartSpex.Name + "-oradata-opatch-vol", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: instance.Spec.ConfigParams.OPatchLocation, + if instance.Spec.ConfigParams.OPatchLocation != "" { + if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.OPatchLocation]; !exists { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-opatch-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.OPatchLocation, + }, }, - }, - }) + }) + } } } @@ -538,21 +549,34 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, fmt.Println("No Location is passed for the software storage in" + OracleRestartSpex.Name) } + // Check if ConfigParams is not nil if instance.Spec.ConfigParams != nil { - if len(instance.Spec.ConfigParams.HostSwStageLocation) != 0 { + + // Check if HostSwStageLocation is provided in ConfigParams + if len(instance.Spec.ConfigParams.HostSwStageLocation) != 0 && len(OracleRestartSpex.PvcName) == 0 { result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-swstage-vol", MountPath: utils.OraSwStageLocation}) } } if instance.Spec.ConfigParams != nil { - if len(instance.Spec.ConfigParams.RuPatchLocation) != 0 { - result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", MountPath: utils.OraRuPatchStageLocation}) + if instance.Spec.ConfigParams.RuPatchLocation != "" { + if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.RuPatchLocation]; !exists { + result = append(result, corev1.VolumeMount{ + Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", + MountPath: utils.OraRuPatchStageLocation, + }) + } } } if instance.Spec.ConfigParams != nil { - if len(instance.Spec.ConfigParams.OPatchLocation) != 0 { - result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-opatch-vol", MountPath: utils.OraOPatchStageLocation}) + if instance.Spec.ConfigParams.OPatchLocation != "" { + if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.OPatchLocation]; !exists { + result = append(result, corev1.VolumeMount{ + Name: OracleRestartSpex.Name + "-oradata-opatch-vol", + MountPath: utils.OraOPatchStageLocation, + }) + } } } @@ -798,22 +822,6 @@ func UpdateProvForOracleRestart(instance *oraclerestart.OracleRestart, isUpdate = true } } - // Memory Check - //resources := corev1.Pod.Spec.Containers - - /** - - for i = 0; i < len(sfSet.Spec.VolumeClaimTemplates); i++ { - if sfSet.Spec.VolumeClaimTemplates[i].Name == OracleRestartSpex.Name+"-oradata-vol4" { - volumeSize := sfSet.Spec.VolumeClaimTemplates[i].Size() - if volumeSize != int(OracleRestartSpex.StorageSizeInGb) { - isUpdate = true - } - - } - } - - **/ if isUpdate { err = kClient.Update(context.Background(), BuildStatefulSetForOracleRestart(instance, OracleRestartSpex, kClient)) diff --git a/commons/oraclerestart/utils/utils.go b/commons/oraclerestart/utils/utils.go index c196b9cb..1d7580be 100644 --- a/commons/oraclerestart/utils/utils.go +++ b/commons/oraclerestart/utils/utils.go @@ -177,3 +177,13 @@ func GetValue(variable string, subkey string) string { func GetDBUser() string { return OraDBUser } + +// Contains checks if a string is present in a slice. +func Contains(slice []string, str string) bool { + for _, item := range slice { + if item == str { + return true + } + } + return false +} diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 09317d60..7cc3a0fc 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -4,26 +4,6 @@ kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-configuration webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v4-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackupv4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -168,26 +148,6 @@ webhooks: resources: - shardingdatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackupv1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index d383c56d..9b92b8bc 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -1914,10 +1914,22 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or //data = append(data, "COPY_GRID_SOFTWARE=true") } - data = append(data, "STAGING_SOFTWARE_LOC="+utils.OraSwStageLocation) + if instance.Spec.ConfigParams.HostSwStageLocation != "" { + if swStagePath, ok := instance.Spec.InstDetails.PvcName[instance.Spec.ConfigParams.HostSwStageLocation]; ok { + data = append(data, "STAGING_SOFTWARE_LOC="+swStagePath) + } else { + data = append(data, "STAGING_SOFTWARE_LOC="+utils.OraSwStageLocation) + } + } + if instance.Spec.ConfigParams.RuPatchLocation != "" { - data = append(data, "APPLY_RU_LOCATION="+utils.OraRuPatchStageLocation) + if ruPatchPath, ok := instance.Spec.InstDetails.PvcName[instance.Spec.ConfigParams.RuPatchLocation]; ok { + data = append(data, "APPLY_RU_LOCATION="+ruPatchPath) + } else { + data = append(data, "APPLY_RU_LOCATION="+utils.OraRuPatchStageLocation) + } } + if instance.Spec.ConfigParams.RuFolderName != "" { data = append(data, "RU_FOLDER_NAME="+instance.Spec.ConfigParams.RuFolderName) } diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md index 09f43adb..2eefadca 100644 --- a/docs/oraclerestart/README.md +++ b/docs/oraclerestart/README.md @@ -43,8 +43,9 @@ Deploy Oracle Restart Database YAML files using Kubernetes Cluster on your Oracl [1. Provisioning an Oracle Restart Database](./provisioning/provisioning_oracle_restart_db.md) [2. Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) -[3. Provisioning an Oracle Restart Database with RU Patch](./provisioning/provisioning_oracle_restart_db_rupatch.md) +[3. Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning/provisioning_oracle_restart_db_rupatch.md) [4. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) +[4. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) ## Connecting to Oracle Restart Database diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml new file mode 100644 index 00000000..a08fc19c --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml @@ -0,0 +1,152 @@ +# API group and version of the custom resource +apiVersion: database.oracle.com/v4 + +# Specifies the kind of custom resource +kind: OracleRestart + +# Metadata about the custom resource +metadata: + # Name of the OracleRestart instance + name: oraclerestart-sample + # Namespace in which the instance will be deployed + namespace: orestart + +# Specification of the OracleRestart instance +spec: + # Instance-specific configuration + instDetails: + # Logical name of the OracleRestart DB instance + name: dbmc1 + # Base path on the host where software will be installed + hostSwLocation: /scratch/orestart/ + # IP address of the worker node where the instance should run + workerNode: + - 10.0.10.108 + # Service definition to expose the DB + nodePortSvc: + - # Type of Kubernetes service + svcType: nodeport + # Name of the service + name: dbmc1 + # Port mappings for the service + portMappings: + - # Port exposed by the service + port: 1521 + # Container port + targetPort: 1521 + # Protocol used for communication + protocol: TCP + # Node port exposed on the worker node + nodePort: 30007 + + # Optional: Set this to enable dynamic provisioning of PVCs + # storageClass: "oci-bv" + + # PVCs for RU patch and staged host software + pvcName: + # Mount path for the RU patch files + rupatch-location-pvc: "/scratch/software/19c/19.28/" + # Mount path for the staged host software + hostsw-stage-pvc: "/scratch/stage/software" + + # ASM disk configuration + asmStorageDetails: + # Disks grouped by size + disksBySize: + - # Size (in Gi) of each ASM disk + storageSizeInGb: 50 + # List of block devices to be used as ASM disks + diskNames: + - /dev/oracleoci/oraclevdd + - /dev/oracleoci/oraclevde + + # SSH key secret configuration + sshKeySecret: + # Name of the secret containing SSH keys + name: ssh-key-secret + # Private key filename inside the secret + privKeySecretName: ssh-privkey + # Public key filename inside the secret + pubKeySecretName: ssh-pubkey + + # Secret for database passwords and key files + dbSecret: + # Name of the Kubernetes secret + name: db-user-pass-pkutl + # Encrypted key file name inside the secret + keyFileName: key.pem + # Encrypted password file name inside the secret + pwdFileName: pwdfile.enc + + # Podman image to use for the Oracle database + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0725 + + # Always pull the latest image + imagePullPolicy: Always + + # Service details for database registration + serviceDetails: + # Name of the database service + name: soepdb + + # Resource requests and limits for the pod + resources: + requests: + # Minimum memory allocation + memory: "16Gi" + # Minimum CPU allocation + cpu: "2" + limits: + # Maximum memory allocation + memory: "16Gi" + # Maximum CPU allocation + cpu: "2" + + # Security settings and kernel parameters + securityContext: + sysctls: + - # Shared memory pages + name: kernel.shmall + value: "2097152" + - # Semaphore settings + name: kernel.sem + value: "250 32000 100 128" + - # Maximum shared memory segment size + name: kernel.shmmax + value: "8589934592" + - # Number of shared memory segments + name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and DB + configParams: + # Path to the Oracle Grid home + gridHome: "/u01/app/19c/grid" + # Path to the Oracle Grid base + gridBase: "/u01/app/grid" + # Path to the Oracle DB home + dbHome: "/u01/app/oracle/product/19c/dbhome_1" + # Path to the Oracle DB base + dbBase: "/u01/app/oracle" + # Comma-separated list of block devices for ASM + crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + # Path to Oracle Inventory + inventory: "/u01/app/oraInventory" + # Filename of the Grid software ZIP + gridSwZipFile: "grid_home.zip" + # Filename of the DB software ZIP + dbSwZipFile: "db_home.zip" + # SGA memory size + sgaSize: "3G" + # PGA memory size + pgaSize: "1G" + # Number of database processes + processes: 2000 + # Number of CPUs Oracle will see + cpuCount: 4 + # Name of the Oracle database + dbName: "PORCLCDB" + # Reference to PVC for software staging + hostSwStageLocation: "hostsw-stage-pvc" + # Reference to PVC for patch files + ruPatchLocation: "rupatch-location-pvc" diff --git a/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt b/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt new file mode 100644 index 00000000..acf97279 --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt @@ -0,0 +1,169 @@ +orestart/ $ kubectl get pvc -n orestart +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-disk-0-oraclerestart-sample Bound csi-4afa96ce-0222-4108-9202-6dea85fde52a 50Gi RWO oci-bv 24s +asm-pvc-disk-1-oraclerestart-sample Bound csi-b7a2cd8b-972f-4244-b047-80db0c4132f1 50Gi RWO oci-bv 24s +hostsw-stage-pvc Bound hostsw-stage-pv 50Gi RWX hostsw-stage-class 15h +rupatch-location-pvc Bound rupatch-location-pv 50Gi RWX rupatch-location-class 15h +orestart/ $ + +orestart/ $ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"pvcName":{"hostsw-stage-pvc":"/scratch/... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-07-26T05:49:37Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 11192638 + UID: 85a6418a-bac0-4808-99d5-8d2dc48ee836 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: hostsw-stage-pvc + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Ru Patch Location: rupatch-location-pvc + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0725 + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1 + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Pvc Name: + Hostsw - Stage - Pvc: /scratch/stage/software + Rupatch - Location - Pvc: /scratch/software/19c/19.28/ + Worker Node: + 10.0.10.108 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: NOT HEALTHY + Mounted Devices: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + Pvc Name: + Hostsw - Stage - Pvc: /scratch/stage/software + Rupatch - Location - Pvc: /scratch/software/19c/19.28/ + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Name: DATA + Redundancy: EXTERN + Client Etc Host: + 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local + Conditions: + Last Transition Time: 2025-07-26T05:56:51Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-07-26T06:27:52Z + Message: no reconcile errors + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.3.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md index 2aeb9db4..c70052a1 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md @@ -1,4 +1,4 @@ -# Provisioning an Oracle Restart Database with RU Patch +# Provisioning an Oracle Restart Database with RU Patch on FileSystem In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md new file mode 100644 index 00000000..6867e4e8 --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md @@ -0,0 +1,72 @@ +# Provisioning an Oracle Restart Database with RU Patch on Existing PVC + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is +generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. In this case, ru patches already have associated pv and pvcs created bind together. E.g +```sh +orestart/ $ kubectl get pvc -n orestart +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +hostsw-stage-pvc Bound hostsw-stage-pv 50Gi RWX hostsw-stage-class 9m15s +rupatch-location-pvc Bound rupatch-location-pv 50Gi RWX rupatch-location-class 9m15s +orestart/ $ kubectl get pv -n orestart +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE +hostsw-stage-pv 50Gi RWX Retain Bound orestart/hostsw-stage-pvc hostsw-stage-class 16m +rupatch-location-pv 50Gi RWX Retain Bound orestart/rupatch-location-pvc rupatch-location-class 16m +``` + +This example uses `oraclerestart_prov_rupatch_pvc.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostsw-stage-pvc`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. +* Directory where the **Release Update (RU) patch** has been unzipped is refered here with pvc name `rupatch-location-pvc`. This pvc bind location **must contain `PatchSearch.xml`**, which is used by the installer to detect and apply the patch. + +In this example, + * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch_pvc.yaml` file to point to your own container registry base container image. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + + +Use the file: [oraclerestart_prov_rupatch_pvc.yaml](./oraclerestart_prov_rupatch_pvc.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_rupatch_pvc.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_rupatch_pvc.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_rupatch_pvc_object.txt) +4. In this case, the port 1521 from the pod is mapped to port 30007 on the worker node. To make the connection from outside, you will need to open the port 30007 on the worker node for INGRESS. + +Once done, you will be able to make an SQLPLUS database connection to this Oracle Restart Database from a remote client as below +```sh +bash-4.4$ sqlplus system/Oracle_23ai@//129.146.0.149:30007/PORCLCDB + +SQL*Plus: Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems on Sat Jul 19 04:02:48 2025 +Version 23.9.0.25.09 +Copyright (c) 1982, 2025, Oracle. All rights reserved. +Last Successful login time: Sat Jul 19 2025 00:20:14 +00:00 +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 +SQL> +SQL> set lines 200 +SQL> col HOST_NAME format a40 +SQL> select INSTANCE_NAME,HOST_NAME, DATABASE_TYPE from v$instance; +INSTANCE_NAME HOST_NAME DATABASE_TYPE +---------------- ---------------------------------------- --------------- +PORCLCDB dbmc1-0 SINGLE +``` diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index cce2cc75..18b54ca2 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -673,6 +673,8 @@ spec: - Start - Terminate - Clone + - Switchover + - Failover type: string clone: properties: @@ -1006,6 +1008,8 @@ spec: - Start - Terminate - Clone + - Switchover + - Failover type: string clone: properties: @@ -15578,26 +15582,6 @@ metadata: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert name: oracle-database-operator-mutating-webhook-configuration webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackupv4.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -15742,26 +15726,6 @@ webhooks: resources: - shardingdatabases sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-autonomousdatabasebackup - failurePolicy: Fail - name: mautonomousdatabasebackupv1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - autonomousdatabasebackups - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 From 68caf0a369724dd05318759e29a43d0a14f037e2 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Mon, 28 Jul 2025 10:10:46 +0000 Subject: [PATCH 230/414] documentation changes --- README.md | 44 +++++++++----------------------------------- docs/dbcs/README.md | 25 ++++++++++++------------- 2 files changed, 21 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 1e96bec6..ba10691d 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released the _Oracle Database Operator for Kubernetes_ (`OraOperator` or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating the management of the Oracle Database lifecycle. -## Supported Database Configurations in V1.2.0 -In this v1.2.0 production release, `OraOperator` supports the following database configurations, and controllers: +## Supported Database Configurations in V2.0 +In this v2.0 production release, `OraOperator` supports the following database configurations, and controllers: * Oracle Autonomous Database: * Oracle Autonomous Database shared Oracle Cloud Infrastructure (OCI) (ADB-S) @@ -16,11 +16,11 @@ In this v1.2.0 production release, `OraOperator` supports the following database * Oracle Multitenant Databases (CDB/PDBs) * Oracle Base Database Service (OBDS) on Oracle Cloud Infrastructure (OCI) * Oracle Data Guard -<<<<<<< HEAD * Oracle Database Observability * Oracle Database Rest Service (ORDS) instances +* Oracle Restart -## New Lifecycle Features in V1.2.0 Release (Controllers Enhancements) +## New Lifecycle Features in V2.0 Release (Controllers Enhancements) * ORDSSERVICES - Install on SIDB and ADB - Provision and Delete ORDS instances @@ -42,7 +42,7 @@ In this v1.2.0 production release, `OraOperator` supports the following database - Support for Database Logs (in addition to Metrics) - Support for the latest Exporter container images -* Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation. +* Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation, Clone, Backup, Restore, Dataguard Setup, Patching and Upgrade. * Prometheus label config (bug fix) ## New Product Features @@ -52,17 +52,16 @@ In this v1.2.0 production release, `OraOperator` supports the following database * Validated on Google Kubernetes Engine ## Overall Features Summary -======= * Oracle Database Rest Service (ORDS) instances -## New Product Features in V1.2.0 +## New Product Features in V2.0 The Operator itself, as a product, brings the following new features: * Upgraded Kubernetes API version to V4 * Published on `operatorhub.io` * Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) * Validated on Google Kubernetes Engine -## New Services and Lifecycle Enhancements (Controllers Enhancements) in V1.2.0 +## New Services and Lifecycle Enhancements (Controllers Enhancements) in V2.0 * ORDSSERVICES - Install on SIDB and ADB - Provision and Delete ORDS instances @@ -91,26 +90,18 @@ The Operator itself, as a product, brings the following new features: * Published on `operatorhub.io` * Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) * Validated on Google Kubernetes Engine ->>>>>>> origin/master ## Overall Features Summary -<<<<<<< HEAD -======= -As of release v1.2, the Oracle Database Operator for Kubernetes ( OraOperator) supports the following lifecycle operations: +As of release v2.0, the Oracle Database Operator for Kubernetes ( OraOperator) supports the following lifecycle operations: ->>>>>>> origin/master * ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning. * ACD: Provision, bind, restart, terminate (soft/hard) * SIDB: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (basic console), Oracle REST Data Service (ORDS) to support REST based SQL, PDB management, SQL Developer Web, Application Express (Apex), Resource management, True Cache, Observer for FastStartFailover (Data Guard), and Snapshot Standby (Data Guard) * ORDS Services: Provision and delete ORDS instances * Globally Distrib. (Sharded): Provision/deploy sharded databases and the shard topology, Add a new shard, Delete an existing shard, Raft replication. -<<<<<<< HEAD -* Oracle Multitenant Database (choice of controller): Bind to a CDB, Create a PDB, Plug a PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB, Assertive deletion policy -======= * Oracle Multitenant Database (choice of controller): Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB, Assertive deletion policy ->>>>>>> origin/master -* Oracle Base Database Service (OBDS): Provision, bind, scale shape Up/Down, Scale Storage Up, Terminate and Update License, Cloning, PDB creation, using KMS Vaults on Oracle Cloud Infrastructure (OCI) +* Oracle Base Database Service (OBDS): Provision, Scale Shape Up/Down, Scale Storage Up, Terminate and Update License, Cloning, PDB creation, using KMS Vaults on Oracle Cloud Infrastructure (OCI), Clone, Backup, Restore, Dataguard Setup, Patching and Upgrade * Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration * Oracle Database Observability: create, patch, delete `databaseObserver` resources (Logs and Metrics) * Watch over a set of namespaces or all the namespaces in the cluster using the `WATCH_NAMESPACE` environment variable of the operator deployment @@ -187,10 +178,6 @@ Oracle strongly recommends that you ensure your system meets the following [Prer kubectl apply -f oracle-database-operator.yaml ``` -<<<<<<< HEAD - -======= ->>>>>>> origin/master * ### ClusterRole and ClusterRoleBinding for NodePort services To expose services on each node's IP and port (the NodePort), apply the [`node-rbac.yaml`](./rbac/node-rbac.yaml). Note that this step is not required for LoadBalancer services. @@ -207,15 +194,12 @@ Oracle strongly recommends that you ensure your system meets the following [Prer To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the preceding step. -<<<<<<< HEAD -======= Run the following command ```sh kubectl apply -f oracle-database-operator.yaml ``` ->>>>>>> origin/master Ensure that the operator pods are up and running. For high availability, operator pod replicas are set to a default of 3. You can scale this setting up or down. ```sh @@ -252,11 +236,8 @@ The following quickstarts are designed for specific database configurations: * [Containerized Oracle Globally Distributed Database](./docs/sharding/README.md) * [Oracle Multitenant Database](./docs/multitenant/README.md) * [Oracle Base Database Service (OBDS)](./docs/dbcs/README.md) -<<<<<<< HEAD -======= * [ORDS Services (ORDSSRVS)](./docs/ordsservices/README.md) * [Oracle Restart Database](./docs/oraclerestart/README.md) ->>>>>>> origin/master The following quickstart is designed for non-database configurations: @@ -359,19 +340,12 @@ The following is an example of a YAML file fragment for specifying Oracle Cloud ``` Examples in this repository where passwords are entered on the command line are for demonstration purposes only. -<<<<<<< HEAD ### Reporting a Security Issue See [Reporting security vulnerabilities](./SECURITY.md) -======= ->>>>>>> origin/master ## License Copyright (c) 2022, 2025 Oracle and/or its affiliates. -<<<<<<< HEAD -Released under the Universal Permissive License v1.0 as shown at [https://oss.oracle.com/licenses/upl/](https://oss.oracle.com/licenses/upl/) -======= Released under the Universal Permissive License v1.0 as shown at [https://oss.oracle.com/licenses/upl/](https://oss.oracle.com/licenses/upl/) ->>>>>>> origin/master diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md index 596b194d..9b64d30a 100644 --- a/docs/dbcs/README.md +++ b/docs/dbcs/README.md @@ -162,27 +162,26 @@ For more informatoin about the multiple use cases available to you to deploy and [1. Deploy a DB System using OCI OBDS Service with minimal parameters](./provisioning/dbcs_service_with_minimal_parameters.md) [2. Binding to an existing OBDS System already deployed in OCI Oracle Base Database Service](./provisioning/bind_to_existing_dbcs_system.md) [3. Scale UP the shape of an existing OBDS System](./provisioning/scale_up_dbcs_system_shape.md) -[4. Scale DOWN the shape of an existing OBDS System](./provisioning/scale_down_dbcs_system_shape.md) -[5. Scale UP the storage of an existing OBDS System](./provisioning/scale_up_storage.md) -[6. Update License type of an existing OBDS System](./provisioning/update_license.md) +[4. Scale DOWN the shape of an existing OBDS System](./provisioning/scale_down_dbcs_system_shape.md) +[5. Scale UP the storage of an existing OBDS System](./provisioning/scale_up_storage.md) +[6. Update License type of an existing OBDS System](./provisioning/update_license.md) [7. Terminate an existing OBDS System](./provisioning/terminate_dbcs_system.md) [8. Create OBDS with All Parameters with Storage Management as LVM](./provisioning/dbcs_service_with_all_parameters_lvm.md) [9. Create OBDS with All Parameters with Storage Management as ASM](./provisioning/dbcs_service_with_all_parameters_asm.md) [10. Deploy a 2 Node RAC DB System using OCI OBDS Service](./provisioning/dbcs_service_with_2_node_rac.md) -[11. Create PDB to an existing OBDS System already deployed in OCI OBDS Service](./provisioning/create_pdb_to_existing_dbcs_system.md) -[12. Create OBDS with PDB in OCI](./provisioning/create_dbcs_with_pdb.md) -[13. Create OBDS with KMS Vault Encryption in OCI](./provisioning/create_dbcs_with_kms.md) +[11. Create PDB to an existing OBDS System already deployed in OCI OBDS Service](./provisioning/create_pdb_to_existing_dbcs_system.md) +[12. Create OBDS with PDB in OCI](./provisioning/create_dbcs_with_pdb.md) +[13. Create OBDS with KMS Vault Encryption in OCI](./provisioning/create_dbcs_with_kms.md) [14. Migrate to KMS vault from TDE Wallet password encryption of an existing OBDS System already deployed in OCI Base OBDS Service](./provisioning/migrate_to_kms.md) [15. Clone DB System from Existing DB System in OCI OBDS Service](./provisioning/clone_from_existing_dbcs.md) [16. Clone DB System from Backup of Existing DB System in OCI OBDS Service](./provisioning/clone_from_backup_dbcs.md) -[17. Clone DB System from Existing Database of DB System in OCI OBDS Service](./provisioning/clone_from_database.md) -[18. Create Backup of Existing Database of DB System in OCI OBDS Service](./provisioning/backup_of_database.md) -[19. Restore from Backup of Existing Database of DB System in OCI OBDS Service](./provisioning/restore_of_database.md) -[20. Setup Dataguard Association to Existing Database of DB System in OCI Base DBCS Service](./provisioning/dataguard_to_database.md) -[21. Disable Dataguard Association to Existing Database of DB System and Terminate Peer DB System in OCI Base DBCS Service](./provisioning/disable_dataguard_to_database.md) -[22. Patching Existing Database of DB System in OCI Base DBCS Service](./provisioning/patching_database.md) -[23. Upgrading Existing Database of DB System in OCI Base DBCS Service](./provisioning/upgrading_database.md) [17. Clone DB System from Existing Database of DB System in OCI OBDS Service](./provisioning/clone_from_database.md) +[18. Create Backup of Existing Database of DB System in OCI OBDS Service](./provisioning/backup_of_database.md) +[19. Restore from Backup of Existing Database of DB System in OCI OBDS Service](./provisioning/restore_of_database.md) +[20. Setup Dataguard Association to Existing Database of DB System in OCI Base DBCS Service](./provisioning/dataguard_to_database.md) +[21. Disable Dataguard Association to Existing Database of DB System and Terminate Peer DB System in OCI Base DBCS Service](./provisioning/disable_dataguard_to_database.md) +[22. Patching Existing Database of DB System in OCI Base DBCS Service](./provisioning/patching_database.md) +[23. Upgrading Existing Database of DB System in OCI Base DBCS Service](./provisioning/upgrading_database.md) ## Connecting to OCI OBDS database deployed using Oracle DB Operator OBDS Controller From ccf976a935423ea7b5737ee620c0f3d3b58c16cf Mon Sep 17 00:00:00 2001 From: racpack Date: Mon, 28 Jul 2025 16:47:06 +0000 Subject: [PATCH 231/414] Update 19 files --- Makefile | 2 +- PROJECT | 17 + .../v1/databaseobserver_webhook.go | 3 +- .../v1alpha1/databaseobserver_webhook.go | 3 +- .../v4/databaseobserver_webhook.go | 3 +- apis/omlai/v4/groupversion_info.go | 58 ++ apis/omlai/v4/privateai_types.go | 208 ++++++ apis/omlai/v4/privateai_webhook.go | 197 ++++++ apis/omlai/v4/privateai_webhook_test.go | 107 +++ apis/omlai/v4/webhook_suite_test.go | 184 ++++++ apis/omlai/v4/zz_generated.deepcopy.go | 315 +++++++++ commons/omlai/acommon.go | 225 +++++++ commons/omlai/aibuilder.go | 471 +++++++++++++ commons/omlai/exec.go | 125 ++++ config/certmanager/certificate-metrics.yaml | 20 + config/certmanager/certificate-webhook.yaml | 20 + config/certmanager/issuer.yaml | 13 + .../bases/omlai.oracle.com_privateais.yaml | 298 +++++++++ config/crd/kustomization.yaml | 1 + .../network-policy/allow-webhook-traffic.yaml | 27 + config/rbac/kustomization.yaml | 4 +- config/rbac/omlai_privateai_admin_role.yaml | 27 + config/rbac/omlai_privateai_editor_role.yaml | 33 + config/rbac/omlai_privateai_viewer_role.yaml | 29 + config/rbac/role.yaml | 26 + config/samples/kustomization.yaml | 1 + config/samples/omlai_v4_privateai.yaml | 9 + config/webhook/manifests.yaml | 40 ++ controllers/omlai/privateai_controller.go | 623 ++++++++++++++++++ .../omlai/privateai_controller_test.go | 106 +++ controllers/omlai/suite_test.go | 138 ++++ docs/privateai/README.md | 121 ++++ docs/privateai/access_privateai.md | 138 ++++ docs/privateai/api_endpoint.md | 16 + docs/privateai/config.json | 16 + docs/privateai/debug_privateai.md | 21 + docs/privateai/deploy_privateai.md | 23 + docs/privateai/pai_sample.yaml | 25 + docs/privateai/pai_sample.yaml.bkp | 27 + docs/privateai/pai_sample_scale_in.yaml | 28 + docs/privateai/pai_sample_scale_up.yaml | 28 + docs/privateai/pai_secret.sh | 26 + docs/privateai/scale_in_privateai.md | 25 + docs/privateai/scale_up_privateai.md | 25 + go.mod | 2 +- main.go | 111 +--- oracle-database-operator.yaml | 436 ++++++++++-- 47 files changed, 4230 insertions(+), 171 deletions(-) create mode 100644 apis/omlai/v4/groupversion_info.go create mode 100644 apis/omlai/v4/privateai_types.go create mode 100644 apis/omlai/v4/privateai_webhook.go create mode 100644 apis/omlai/v4/privateai_webhook_test.go create mode 100644 apis/omlai/v4/webhook_suite_test.go create mode 100644 apis/omlai/v4/zz_generated.deepcopy.go create mode 100644 commons/omlai/acommon.go create mode 100644 commons/omlai/aibuilder.go create mode 100644 commons/omlai/exec.go create mode 100644 config/certmanager/certificate-metrics.yaml create mode 100644 config/certmanager/certificate-webhook.yaml create mode 100644 config/certmanager/issuer.yaml create mode 100644 config/crd/bases/omlai.oracle.com_privateais.yaml create mode 100644 config/network-policy/allow-webhook-traffic.yaml create mode 100644 config/rbac/omlai_privateai_admin_role.yaml create mode 100644 config/rbac/omlai_privateai_editor_role.yaml create mode 100644 config/rbac/omlai_privateai_viewer_role.yaml create mode 100644 config/samples/omlai_v4_privateai.yaml create mode 100644 controllers/omlai/privateai_controller.go create mode 100644 controllers/omlai/privateai_controller_test.go create mode 100644 controllers/omlai/suite_test.go create mode 100644 docs/privateai/README.md create mode 100644 docs/privateai/access_privateai.md create mode 100644 docs/privateai/api_endpoint.md create mode 100644 docs/privateai/config.json create mode 100644 docs/privateai/debug_privateai.md create mode 100644 docs/privateai/deploy_privateai.md create mode 100644 docs/privateai/pai_sample.yaml create mode 100644 docs/privateai/pai_sample.yaml.bkp create mode 100644 docs/privateai/pai_sample_scale_in.yaml create mode 100644 docs/privateai/pai_sample_scale_up.yaml create mode 100644 docs/privateai/pai_secret.sh create mode 100644 docs/privateai/scale_in_privateai.md create mode 100644 docs/privateai/scale_up_privateai.md diff --git a/Makefile b/Makefile index 2aa6af9a..f50d879a 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go GOLANG_VERSION ?= 1.24.4 -DOCKER ?= docker +DOCKER ?= podman ## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. ## Otherwise, use golang image from docker hub as the builder. ifeq ($(BUILD_INTERNAL), true) diff --git a/PROJECT b/PROJECT index d51d3f7c..6d4826f8 100644 --- a/PROJECT +++ b/PROJECT @@ -259,6 +259,19 @@ resources: webhooks: conversion: true webhookVersion: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: oracle.com + group: omlai + kind: PrivateAi + path: github.com/oracle/oracle-database-operator/api/omlai/v4 + version: v4 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 - api: crdVersion: v1 namespaced: true @@ -268,4 +281,8 @@ resources: kind: OracleRestart path: github.com/oracle/oracle-database-operator/api/database/v4 version: v4 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 version: "3" diff --git a/apis/observability/v1/databaseobserver_webhook.go b/apis/observability/v1/databaseobserver_webhook.go index 286d6ed6..05ec3290 100644 --- a/apis/observability/v1/databaseobserver_webhook.go +++ b/apis/observability/v1/databaseobserver_webhook.go @@ -39,6 +39,8 @@ package v1 import ( + "strings" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -48,7 +50,6 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "strings" ) // log is for logging in this package. diff --git a/apis/observability/v1alpha1/databaseobserver_webhook.go b/apis/observability/v1alpha1/databaseobserver_webhook.go index 585ad3bf..70fdec0b 100644 --- a/apis/observability/v1alpha1/databaseobserver_webhook.go +++ b/apis/observability/v1alpha1/databaseobserver_webhook.go @@ -39,6 +39,8 @@ package v1alpha1 import ( + "strings" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -48,7 +50,6 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "strings" ) // log is for logging in this package. diff --git a/apis/observability/v4/databaseobserver_webhook.go b/apis/observability/v4/databaseobserver_webhook.go index c0a5d8b7..e25902ad 100644 --- a/apis/observability/v4/databaseobserver_webhook.go +++ b/apis/observability/v4/databaseobserver_webhook.go @@ -39,6 +39,8 @@ package v4 import ( + "strings" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -48,7 +50,6 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "strings" ) // log is for logging in this package. diff --git a/apis/omlai/v4/groupversion_info.go b/apis/omlai/v4/groupversion_info.go new file mode 100644 index 00000000..1ea9b97b --- /dev/null +++ b/apis/omlai/v4/groupversion_info.go @@ -0,0 +1,58 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Package v4 contains API Schema definitions for the omlai v4 API group. +// +kubebuilder:object:generate=true +// +groupName=omlai.oracle.com +package v4 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "omlai.oracle.com", Version: "v4"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/omlai/v4/privateai_types.go b/apis/omlai/v4/privateai_types.go new file mode 100644 index 00000000..368ba6d9 --- /dev/null +++ b/apis/omlai/v4/privateai_types.go @@ -0,0 +1,208 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// PrivateAiSpec defines the desired state of PrivateAi. +type PrivateAiSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + PaiConfigFile *PaiConfigMap `json:"paiConfigFile,omitempty"` + PaiEnableAuthentication bool `json:"paiEnableAuthentication,omitempty"` + PaiSecret *PaiSecretSpec `json:"paiSecret,omitempty"` + IsExternalSvc bool `json:"isExternalSvc,omitempty"` + StorageClass string `json:"storageClass,omitempty"` + PvcList map[string]string `json:"pvcList,omitempty"` + PaiImage string `json:"paiImage,omitempty"` + PaiImagePullSecret string `json:"paiImagePullSecret,omitempty"` + IsDebug bool `json:"isDebug,omitempty"` + ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` + LivenessCheckPeriod int `json:"livenessCheckPeriod,omitempty"` + IsDownloadScripts bool `json:"isDownloadScripts,omitempty"` + PaiService PaiServiceSpec `json:"paiService,omitempty"` + StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` + EnvVars []EnvironmentVariable `json:"envVars,omitempty"` + Replicas int32 `json:"replicas,omitempty"` + Resources *corev1.ResourceRequirements `json:"resources,omitempty"` + NodePortSvc []PaiNodePortSvc `json:"nodePortSvc,omitempty"` + PortMappings []PaiPortMapping `json:"portMappings,omitempty"` + IsDeleteOraPvc bool `json:"isDeleteOraPvc,omitempty"` + PaiLogLocation string `json:"paiLogLocation,omitempty"` + PaiHTTPEnabled bool `json:"paiHTTPEnabled,omitempty"` + PaiHTTPSEnabled bool `json:"paiHTTPSEnabled,omitempty"` + PaiHTTPPort int32 `json:"paiHTTPPort,omitempty"` + PaiHTTPSPort int32 `json:"paiHTTPSPort,omitempty"` + PaiAuthentication bool `json:"paiAuthentication,omitempty"` + PaiLBPort int32 `json:"paiLBPort,omitempty"` + PaiLBIP string `json:"paiLBIP,omitempty"` +} + +// Secret Details +type PaiSecretSpec struct { + Name string `json:"name,omitempty"` + MountLocation string `json:"mountLocation,omitempty"` +} + +// Env Variable +type EnvironmentVariable struct { + Name string `json:"name"` // Name of the variable. Must be a C_IDENTIFIER. + Value string `json:"value"` // Value of the variable, as defined in Kubernetes core API. +} + +// Service Spec +type PaiServiceSpec struct { + PortMappings []PaiPortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created + SvcName string `json:"name,omitempty"` + SvcType string `json:"svcType,omitempty"` +} + +// Config Map +type PaiConfigMap struct { + Name string `json:"name,omitempty"` + MountLocation string `json:"mountLocation,omitempty"` +} + +// Node Port Svc +type PaiNodePortSvc struct { + PortMappings []PaiPortMapping `json:"portMappings,omitempty"` // Port mappings for the service + SvcName string `json:"name,omitempty"` + SvcType string `json:"svcType,omitempty"` +} + +// Port Mapping +type PaiPortMapping struct { + Port int32 `json:"port"` + TargetPort int32 `json:"targetPort"` // Docker image port for the application + Protocol corev1.Protocol `json:"protocol"` // IP protocol for the mapping, e.g., "TCP" or "UDP" + NodePort int32 `json:"nodePort,omitempty"` +} + +// PrivateAiStatus defines the observed state of PrivateAi. +type PrivateAiStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + Status string `json:"status,omitempty"` + Replicas int `json:"replicas,omitempty"` + ReleaseUpdate string `json:"releaseUpdate,omitempty"` + ApiKey string `json:"apiKey,omitempty"` + Certpem string `json:"certpem,omitempty"` + LoadBalancerIP string `json:"loadBalancerIP,omitempty"` + PodIP string `json:"podIP,omitempty"` + NodeIP string `json:"NodeIP,omitempty"` + ClusterIP string `json:"clusterIP,omitempty"` + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +//+kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string +//+kubebuilder:printcolumn:JSONPath=".status.replicas",name="Replicas",type=number +//+kubebuilder:printcolumn:JSONPath=".status.apikey",name="ApiKey",type=string +//+kubebuilder:printcolumn:JSONPath=".status.podip",name="PodIP",type=string +//+kubebuilder:printcolumn:JSONPath=".status.loadbalancerip",name="LbIP",type=string +//+kubebuilder:printcolumn:JSONPath=".status.ReleaseUpdate",name="ReleaseUpdate",type=string,priority=1 + +// PrivateAi is the Schema for the privateais API. +type PrivateAi struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PrivateAiSpec `json:"spec,omitempty"` + Status PrivateAiStatus `json:"status,omitempty"` +} + +const ReconcileError string = "ReconcileError" + +const ReconcileErrorReason string = "LastReconcileCycleFailed" + +const ReconcileQueued string = "ReconcileQueued" + +const ReconcileQueuedReason string = "LastReconcileCycleQueued" + +const ReconcileCompelete string = "ReconcileComplete" + +const ReconcileCompleteReason string = "LastReconcileCycleCompleted" + +const ReconcileBlocked string = "ReconcileBlocked" + +const ReconcileBlockedReason string = "LastReconcileCycleBlocked" + +const StatusPending string = "Pending" + +const StatusCreating string = "Creating" + +const StatusNotReady string = "Unhealthy" + +const StatusPatching string = "Patching" + +const StatusUpdating string = "Updating" + +const StatusReady string = "Healthy" + +const StatusError string = "Error" + +const StatusUnknown string = "Unknown" + +const ValueUnavailable string = "Unavailable" + +const NoExternalIp string = "Node ExternalIP unavailable" + +// +kubebuilder:object:root=true + +// PrivateAiList contains a list of PrivateAi. +type PrivateAiList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PrivateAi `json:"items"` +} + +func init() { + SchemeBuilder.Register(&PrivateAi{}, &PrivateAiList{}) +} diff --git a/apis/omlai/v4/privateai_webhook.go b/apis/omlai/v4/privateai_webhook.go new file mode 100644 index 00000000..0d8e82f0 --- /dev/null +++ b/apis/omlai/v4/privateai_webhook.go @@ -0,0 +1,197 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// nolint:unused +// log is for logging in this package. +var privateailog = logf.Log.WithName("privateai-resource") + +// SetupPrivateAiWebhookWithManager registers the webhook for PrivateAi in the manager. +func SetupPrivateAiWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&PrivateAi{}). + WithValidator(&PrivateAiCustomValidator{}). + WithDefaulter(&PrivateAiCustomDefaulter{}). + Complete() +} + +// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +// +kubebuilder:webhook:path=/mutate-omlai-oracle-com-v4-privateai,mutating=true,failurePolicy=fail,sideEffects=None,groups=omlai.oracle.com,resources=privateais,verbs=create;update,versions=v4,name=mprivateai-v4.kb.io,admissionReviewVersions=v1 + +// PrivateAiCustomDefaulter struct is responsible for setting default values on the custom resource of the +// Kind PrivateAi when those are created or updated. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as it is used only for temporary operations and does not need to be deeply copied. +type PrivateAiCustomDefaulter struct { + // TODO(user): Add more fields as needed for defaulting +} + +var _ webhook.CustomDefaulter = &PrivateAiCustomDefaulter{} + +// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind PrivateAi. +func (d *PrivateAiCustomDefaulter) Default(ctx context.Context, obj runtime.Object) error { + privateai, ok := obj.(*PrivateAi) + + if !ok { + return fmt.Errorf("expected an PrivateAi object but got %T", obj) + } + privateailog.Info("Defaulting for PrivateAi", "name", privateai.GetName()) + + // TODO(user): fill in your defaulting logic. + + if !privateai.Spec.PaiHTTPEnabled && !privateai.Spec.PaiHTTPSEnabled { + privateai.Spec.PaiHTTPSEnabled = true + } + if privateai.Spec.PaiHTTPEnabled && privateai.Spec.PaiHTTPSEnabled { + return fmt.Errorf("paiHTTPEnabled and PaiHTTPSEnabled cannot be true") + } + if privateai.Spec.PaiHTTPEnabled { + if privateai.Spec.PaiHTTPPort == 0 { + privateai.Spec.PaiHTTPPort = 8080 + } + privateai.Spec.PaiHTTPSEnabled = false + privateai.Spec.PaiHTTPSPort = 0 + + } else { + privateai.Spec.PaiHTTPSEnabled = true + if privateai.Spec.PaiHTTPSPort == 0 { + privateai.Spec.PaiHTTPSPort = 8443 + } + privateai.Spec.PaiHTTPEnabled = false + privateai.Spec.PaiHTTPPort = 0 + } + + if privateai.Spec.PaiAuthentication { + if privateai.Spec.PaiSecret.Name == "" { + return fmt.Errorf("PaiAuthentication is ture but paisecret is empty") + } + } + + if len(privateai.Spec.PaiService.PortMappings) == 0 { + portInfo := PaiPortMapping{} + + portInfo.Port = 443 + + if privateai.Spec.PaiHTTPEnabled { + portInfo.TargetPort = privateai.Spec.PaiHTTPPort + } else { + portInfo.TargetPort = privateai.Spec.PaiHTTPSPort + } + portInfo.Protocol = "TCP" + + privateai.Spec.PaiService.PortMappings = append(privateai.Spec.PaiService.PortMappings, portInfo) + } + // set default MountLocation for PaiConfigFile + if privateai.Spec.PaiConfigFile.MountLocation == "" { + privateai.Spec.PaiConfigFile.MountLocation = "/oml/config" + } + // set default MountLocation for PaiSecret + if privateai.Spec.PaiSecret.MountLocation == "" { + privateai.Spec.PaiSecret.MountLocation = "/oml/ssl" + } + + return nil +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here. +// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook. +// +kubebuilder:webhook:path=/validate-omlai-oracle-com-v4-privateai,mutating=false,failurePolicy=fail,sideEffects=None,groups=omlai.oracle.com,resources=privateais,verbs=create;update,versions=v4,name=vprivateai-v4.kb.io,admissionReviewVersions=v1 + +// PrivateAiCustomValidator struct is responsible for validating the PrivateAi resource +// when it is created, updated, or deleted. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as this struct is used only for temporary operations and does not need to be deeply copied. +type PrivateAiCustomValidator struct { + // TODO(user): Add more fields as needed for validation +} + +var _ webhook.CustomValidator = &PrivateAiCustomValidator{} + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type PrivateAi. +func (v *PrivateAiCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + privateai, ok := obj.(*PrivateAi) + if !ok { + return nil, fmt.Errorf("expected a PrivateAi object but got %T", obj) + } + privateailog.Info("Validation for PrivateAi upon creation", "name", privateai.GetName()) + + // TODO(user): fill in your validation logic upon object creation. + + return nil, nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type PrivateAi. +func (v *PrivateAiCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + privateai, ok := newObj.(*PrivateAi) + if !ok { + return nil, fmt.Errorf("expected a PrivateAi object for the newObj but got %T", newObj) + } + privateailog.Info("Validation for PrivateAi upon update", "name", privateai.GetName()) + + // TODO(user): fill in your validation logic upon object update. + + return nil, nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type PrivateAi. +func (v *PrivateAiCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + privateai, ok := obj.(*PrivateAi) + if !ok { + return nil, fmt.Errorf("expected a PrivateAi object but got %T", obj) + } + privateailog.Info("Validation for PrivateAi upon deletion", "name", privateai.GetName()) + + // TODO(user): fill in your validation logic upon object deletion. + + return nil, nil +} diff --git a/apis/omlai/v4/privateai_webhook_test.go b/apis/omlai/v4/privateai_webhook_test.go new file mode 100644 index 00000000..c9bb59a2 --- /dev/null +++ b/apis/omlai/v4/privateai_webhook_test.go @@ -0,0 +1,107 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + // TODO (user): Add any additional imports if needed +) + +var _ = Describe("PrivateAi Webhook", func() { + var ( + obj *PrivateAi + oldObj *PrivateAi + validator PrivateAiCustomValidator + defaulter PrivateAiCustomDefaulter + ) + + BeforeEach(func() { + obj = &PrivateAi{} + oldObj = &PrivateAi{} + validator = PrivateAiCustomValidator{} + Expect(validator).NotTo(BeNil(), "Expected validator to be initialized") + defaulter = PrivateAiCustomDefaulter{} + Expect(defaulter).NotTo(BeNil(), "Expected defaulter to be initialized") + Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized") + Expect(obj).NotTo(BeNil(), "Expected obj to be initialized") + // TODO (user): Add any setup logic common to all tests + }) + + AfterEach(func() { + // TODO (user): Add any teardown logic common to all tests + }) + + Context("When creating PrivateAi under Defaulting Webhook", func() { + // TODO (user): Add logic for defaulting webhooks + // Example: + // It("Should apply defaults when a required field is empty", func() { + // By("simulating a scenario where defaults should be applied") + // obj.SomeFieldWithDefault = "" + // By("calling the Default method to apply defaults") + // defaulter.Default(ctx, obj) + // By("checking that the default values are set") + // Expect(obj.SomeFieldWithDefault).To(Equal("default_value")) + // }) + }) + + Context("When creating or updating PrivateAi under Validating Webhook", func() { + // TODO (user): Add logic for validating webhooks + // Example: + // It("Should deny creation if a required field is missing", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "" + // Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred()) + // }) + // + // It("Should admit creation if all required fields are present", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "valid_value" + // Expect(validator.ValidateCreate(ctx, obj)).To(BeNil()) + // }) + // + // It("Should validate updates correctly", func() { + // By("simulating a valid update scenario") + // oldObj.SomeRequiredField = "updated_value" + // obj.SomeRequiredField = "updated_value" + // Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil()) + // }) + }) + +}) diff --git a/apis/omlai/v4/webhook_suite_test.go b/apis/omlai/v4/webhook_suite_test.go new file mode 100644 index 00000000..7473e9e4 --- /dev/null +++ b/apis/omlai/v4/webhook_suite_test.go @@ -0,0 +1,184 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package v4 + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "os" + "path/filepath" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + ctx context.Context + cancel context.CancelFunc + k8sClient client.Client + cfg *rest.Config + testEnv *envtest.Environment +) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Webhook Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + var err error + err = AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: false, + + WebhookInstallOptions: envtest.WebhookInstallOptions{ + Paths: []string{filepath.Join("..", "..", "..", "..", "config", "webhook")}, + }, + } + + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + // start webhook server using Manager. + webhookInstallOptions := &testEnv.WebhookInstallOptions + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + WebhookServer: webhook.NewServer(webhook.Options{ + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), + LeaderElection: false, + Metrics: metricsserver.Options{BindAddress: "0"}, + }) + Expect(err).NotTo(HaveOccurred()) + + err = SetupPrivateAiWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:webhook + + go func() { + defer GinkgoRecover() + err = mgr.Start(ctx) + Expect(err).NotTo(HaveOccurred()) + }() + + // wait for the webhook server to get ready. + dialer := &net.Dialer{Timeout: time.Second} + addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) + Eventually(func() error { + conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) + if err != nil { + return err + } + + return conn.Close() + }).Should(Succeed()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/apis/omlai/v4/zz_generated.deepcopy.go b/apis/omlai/v4/zz_generated.deepcopy.go new file mode 100644 index 00000000..6781d85b --- /dev/null +++ b/apis/omlai/v4/zz_generated.deepcopy.go @@ -0,0 +1,315 @@ +//go:build !ignore_autogenerated + +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package v4 + +import ( + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentVariable) DeepCopyInto(out *EnvironmentVariable) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentVariable. +func (in *EnvironmentVariable) DeepCopy() *EnvironmentVariable { + if in == nil { + return nil + } + out := new(EnvironmentVariable) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PaiConfigMap) DeepCopyInto(out *PaiConfigMap) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiConfigMap. +func (in *PaiConfigMap) DeepCopy() *PaiConfigMap { + if in == nil { + return nil + } + out := new(PaiConfigMap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PaiNodePortSvc) DeepCopyInto(out *PaiNodePortSvc) { + *out = *in + if in.PortMappings != nil { + in, out := &in.PortMappings, &out.PortMappings + *out = make([]PaiPortMapping, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiNodePortSvc. +func (in *PaiNodePortSvc) DeepCopy() *PaiNodePortSvc { + if in == nil { + return nil + } + out := new(PaiNodePortSvc) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PaiPortMapping) DeepCopyInto(out *PaiPortMapping) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiPortMapping. +func (in *PaiPortMapping) DeepCopy() *PaiPortMapping { + if in == nil { + return nil + } + out := new(PaiPortMapping) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PaiSecretSpec) DeepCopyInto(out *PaiSecretSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiSecretSpec. +func (in *PaiSecretSpec) DeepCopy() *PaiSecretSpec { + if in == nil { + return nil + } + out := new(PaiSecretSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PaiServiceSpec) DeepCopyInto(out *PaiServiceSpec) { + *out = *in + if in.PortMappings != nil { + in, out := &in.PortMappings, &out.PortMappings + *out = make([]PaiPortMapping, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiServiceSpec. +func (in *PaiServiceSpec) DeepCopy() *PaiServiceSpec { + if in == nil { + return nil + } + out := new(PaiServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrivateAi) DeepCopyInto(out *PrivateAi) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAi. +func (in *PrivateAi) DeepCopy() *PrivateAi { + if in == nil { + return nil + } + out := new(PrivateAi) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PrivateAi) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrivateAiCustomDefaulter) DeepCopyInto(out *PrivateAiCustomDefaulter) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiCustomDefaulter. +func (in *PrivateAiCustomDefaulter) DeepCopy() *PrivateAiCustomDefaulter { + if in == nil { + return nil + } + out := new(PrivateAiCustomDefaulter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrivateAiCustomValidator) DeepCopyInto(out *PrivateAiCustomValidator) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiCustomValidator. +func (in *PrivateAiCustomValidator) DeepCopy() *PrivateAiCustomValidator { + if in == nil { + return nil + } + out := new(PrivateAiCustomValidator) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrivateAiList) DeepCopyInto(out *PrivateAiList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PrivateAi, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiList. +func (in *PrivateAiList) DeepCopy() *PrivateAiList { + if in == nil { + return nil + } + out := new(PrivateAiList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PrivateAiList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrivateAiSpec) DeepCopyInto(out *PrivateAiSpec) { + *out = *in + if in.PaiConfigFile != nil { + in, out := &in.PaiConfigFile, &out.PaiConfigFile + *out = new(PaiConfigMap) + **out = **in + } + if in.PaiSecret != nil { + in, out := &in.PaiSecret, &out.PaiSecret + *out = new(PaiSecretSpec) + **out = **in + } + if in.PvcList != nil { + in, out := &in.PvcList, &out.PvcList + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.PaiService.DeepCopyInto(&out.PaiService) + if in.EnvVars != nil { + in, out := &in.EnvVars, &out.EnvVars + *out = make([]EnvironmentVariable, len(*in)) + copy(*out, *in) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(v1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.NodePortSvc != nil { + in, out := &in.NodePortSvc, &out.NodePortSvc + *out = make([]PaiNodePortSvc, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.PortMappings != nil { + in, out := &in.PortMappings, &out.PortMappings + *out = make([]PaiPortMapping, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiSpec. +func (in *PrivateAiSpec) DeepCopy() *PrivateAiSpec { + if in == nil { + return nil + } + out := new(PrivateAiSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrivateAiStatus) DeepCopyInto(out *PrivateAiStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiStatus. +func (in *PrivateAiStatus) DeepCopy() *PrivateAiStatus { + if in == nil { + return nil + } + out := new(PrivateAiStatus) + in.DeepCopyInto(out) + return out +} diff --git a/commons/omlai/acommon.go b/commons/omlai/acommon.go new file mode 100644 index 00000000..282e8eb0 --- /dev/null +++ b/commons/omlai/acommon.go @@ -0,0 +1,225 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +import ( + "context" + "fmt" + "strings" + + "github.com/go-logr/logr" + omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/rand" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + piDataMount = "/stage" + defaultLogMount = "/oml/logs" +) + +func LogMessages(msgtype string, msg string, err error, instance *omlaiv4.PrivateAi, logger logr.Logger) { + // setting logrus formatter + //logrus.SetFormatter(&logrus.JSONFormatter{}) + //logrus.SetOutput(os.Stdout) + + if msgtype == "DEBUG" && instance.Spec.IsDebug == true { + if err != nil { + logger.Error(err, msg) + } else { + logger.Info(msg) + } + } else if msgtype == "INFO" { + logger.Info(msg) + } else if msgtype == "Error" { + logger.Error(err, msg) + } +} + +func getOwnerRef(instance *omlaiv4.PrivateAi, +) []metav1.OwnerReference { + + var ownerRef []metav1.OwnerReference + ownerRef = append(ownerRef, metav1.OwnerReference{Kind: instance.GroupVersionKind().Kind, APIVersion: instance.APIVersion, Name: instance.Name, UID: types.UID(instance.UID)}) + return ownerRef +} + +// FUnction to build the svc definition for catalog/shard and GSM +func buildSvcPortsDef(instance *omlaiv4.PrivateAi) []corev1.ServicePort { + var result []corev1.ServicePort + if len(instance.Spec.PaiService.PortMappings) > 0 { + for _, portMapping := range instance.Spec.PaiService.PortMappings { + servicePort := + corev1.ServicePort{ + Protocol: portMapping.Protocol, + Port: portMapping.Port, + Name: generatePortMapping(portMapping), + TargetPort: intstr.IntOrString{ + Type: intstr.Int, + IntVal: portMapping.TargetPort, + }, + } + result = append(result, servicePort) + } + } + return result +} + +// Function to generate the port mapping +func generatePortMapping(portMapping omlaiv4.PaiPortMapping) string { + return generateName(fmt.Sprintf("%s-%d-%d-", "tcp", + portMapping.Port, portMapping.TargetPort)) +} + +// Function to generate the Name +func generateName(base string) string { + maxNameLength := 50 + randomLength := 5 + maxGeneratedLength := maxNameLength - randomLength + if len(base) > maxGeneratedLength { + base = base[:maxGeneratedLength] + } + return fmt.Sprintf("%s%s", base, rand.String(randomLength)) +} + +func GetFmtStr(pstr string, +) string { + return "[" + pstr + "]" +} + +func CheckDepSet(instance *omlaiv4.PrivateAi, kClient client.Client) (*appsv1.Deployment, error) { + sfSetFound := &appsv1.Deployment{} + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: instance.Name, + Namespace: instance.Namespace, + }, sfSetFound) + if err != nil { + return sfSetFound, err + } + return sfSetFound, nil +} + +func DelPvc(pvcName string, instance *omlaiv4.PrivateAi, kClient client.Client, logger logr.Logger) error { + + LogMessages("DEBUG", "Inside the delPvc and received param: "+GetFmtStr(pvcName), nil, instance, logger) + pvcFound, err := checkPvc(pvcName, instance, kClient) + if err != nil { + LogMessages("DEBUG", "Error occurred in finding the pvc claim!", nil, instance, logger) + return err + } + err = kClient.Delete(context.Background(), pvcFound) + if err != nil { + LogMessages("DEBUG", "Error occurred in deleting the pvc claim!", nil, instance, logger) + return err + } + return nil +} + +func CheckSvc(svcName string, instance *omlaiv4.PrivateAi, kClient client.Client) (*corev1.Service, error) { + // If this is a PrivateAi instance + if instance.Kind == "PrivateAi" && !strings.HasSuffix(svcName, "-svc") { + svcName = instance.Name + "-svc" + } + + svcFound := &corev1.Service{} + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: svcName, + Namespace: instance.Namespace, + }, svcFound) + if err != nil { + return svcFound, err + } + return svcFound, nil +} + +func checkPvc(pvcName string, instance *omlaiv4.PrivateAi, kClient client.Client) (*corev1.PersistentVolumeClaim, error) { + pvcFound := &corev1.PersistentVolumeClaim{} + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: pvcName, + Namespace: instance.Namespace, + }, pvcFound) + if err != nil { + return pvcFound, err + } + return pvcFound, nil +} + +func ReadSecret(secName string, instance *omlaiv4.PrivateAi, kClient client.Client, logger logr.Logger, +) (string, string) { + + var apiKeyVal string + var certPemVal string + sc := &corev1.Secret{} + //var err error + + // Reading a Secret + var err error = kClient.Get(context.TODO(), types.NamespacedName{ + Name: secName, + Namespace: instance.Namespace, + }, sc) + + if err != nil { + return "NONE", "NONE" + } + + // Secret Evaluation + for k, val := range sc.Data { + if k == "api-key" { + apiKeyVal = string(val) + LogMessages("DEBUG", "Key : "+GetFmtStr(k)+" Value : "+GetFmtStr(apiKeyVal)+" Val: "+GetFmtStr(string(val)), nil, instance, logger) + } + if k == "cert.pem" { + certPemVal = string(val) + LogMessages("DEBUG", "Key : "+GetFmtStr(k)+" Value : "+GetFmtStr(certPemVal)+" Val: "+GetFmtStr(string(val)), nil, instance, logger) + } + } + if apiKeyVal == "" { + apiKeyVal = "NONE" + } + if certPemVal == "" { + certPemVal = "NONE" + } + + return apiKeyVal, certPemVal +} diff --git a/commons/omlai/aibuilder.go b/commons/omlai/aibuilder.go new file mode 100644 index 00000000..0b1e7d3e --- /dev/null +++ b/commons/omlai/aibuilder.go @@ -0,0 +1,471 @@ +package commons + +import ( + "context" + "reflect" + "strconv" + + "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + privateaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" // TODO +) + +func buildLabelsForPrivateAi(instance *privateaiv4.PrivateAi, label, name string) map[string]string { + return map[string]string{ + "app.kubernetes.io/instance": "PrivateAi-" + instance.Name, + "app.kubernetes.io/name": instance.Name, + "app.kubernetes.io/component": getLabelForPrivateAI(instance), + "app.kubernetes.io/managed-by": "Oracle-Database-Operator", + "app.kubernetes.io/offline-status": "false", + } +} + +func getLabelForPrivateAI(instance *privateaiv4.PrivateAi) string { + return "Oml-" + "PrivateAi-" + instance.Name +} + +func BuildDeploySetForPrivateAI(instance *privateaiv4.PrivateAi) *appsv1.Deployment { + return &appsv1.Deployment{ + TypeMeta: buildTypeMetaForPrivateAI(), + ObjectMeta: buildObjectMetaForPrivateAI(instance), + Spec: *buildDeploymentSpecForPrivateAI(instance), + } +} + +func buildTypeMetaForPrivateAI() metav1.TypeMeta { + return metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + } +} + +func buildObjectMetaForPrivateAI(instance *privateaiv4.PrivateAi) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Name: instance.Name, + Namespace: instance.Namespace, + OwnerReferences: getOwnerRefPrivateAI(instance), + Labels: buildLabelsForPrivateAi(instance, "privateai", instance.Name), + } +} + +func buildDeploymentSpecForPrivateAI(instance *privateaiv4.PrivateAi) *appsv1.DeploymentSpec { + var replicas int32 = 1 + if instance.Spec.Replicas > 0 { + replicas = instance.Spec.Replicas + } else { + replicas = 1 + } + + return &appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: buildLabelsForPrivateAi(instance, "privateai", instance.Name), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: buildLabelsForPrivateAi(instance, "privateai", instance.Name), + }, + Spec: *buildPodSpecForPrivateAI(instance), + }, + } +} + +func int64Ptr(i int64) *int64 { + return &i +} + +func buildPodSpecForPrivateAI(instance *privateaiv4.PrivateAi) *corev1.PodSpec { + spec := &corev1.PodSpec{ + // SecurityContext: &corev1.PodSecurityContext{FsGroup: int64(2001), RunAsUser: int64(2001), RunAsGroup(2001)}, + SecurityContext: &corev1.PodSecurityContext{ + FSGroup: int64Ptr(2001), + RunAsUser: int64Ptr(2001), + RunAsGroup: int64Ptr(2001), + }, + Containers: buildContainerSpecForPrivateAI(instance), + Volumes: buildVolumeSpecForPrivateAI(instance), + } + + if len(instance.Spec.PaiImagePullSecret) > 0 { + spec.ImagePullSecrets = []corev1.LocalObjectReference{ + {Name: instance.Spec.PaiImagePullSecret}, + } + } + + // Add NodeSelector if provided ?? TODO + // if len(paiSpec.NodeSelector) > 0 { + // spec.NodeSelector = paiSpec.NodeSelector + // } + + return spec +} + +func buildContainerSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.Container { + container := corev1.Container{ + Name: instance.Name, + Image: instance.Spec.PaiImage, + Resources: corev1.ResourceRequirements{}, + VolumeMounts: buildVolumeMountSpecForPrivateAI(instance), + Env: buildEnvVarsForPrivateAI(instance, instance.Spec.EnvVars), + ImagePullPolicy: corev1.PullIfNotPresent, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"sh", "-c", "! /bin/test -f /tmp/unhealthy"}, + }, + }, + InitialDelaySeconds: 5, + PeriodSeconds: 5, + }, + } + var appPort int32 + if instance.Spec.Resources != nil { + container.Resources = *instance.Spec.Resources + } + if instance.Spec.PaiHTTPEnabled { + container.Env = append(container.Env, corev1.EnvVar{Name: "OML_HTTP_ENABLED", Value: "true"}, corev1.EnvVar{Name: "OML_HTTPS_ENABLED", Value: "false"}) + appPort = instance.Spec.PaiHTTPPort + + } else { + // HTTPS + container.Env = append(container.Env, corev1.EnvVar{Name: "OML_HTTP_ENABLED", Value: "false"}, corev1.EnvVar{Name: "OML_HTTPS_ENABLED", Value: "true"}) + appPort = instance.Spec.PaiHTTPSPort + } + + container.LivenessProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt(int(appPort)), + }, + }, + InitialDelaySeconds: 60, + TimeoutSeconds: 3, + SuccessThreshold: 1, + FailureThreshold: 3, + PeriodSeconds: 30, + } + container.Ports = []corev1.ContainerPort{ + { + ContainerPort: appPort, + Protocol: corev1.ProtocolTCP, + }, + } + return []corev1.Container{container} +} + +func buildVolumeSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.Volume { + var vols []corev1.Volume + if instance.Spec.PaiSecret != nil && instance.Spec.PaiSecret.Name != "" { + vols = append(vols, corev1.Volume{ + Name: instance.Name + "secret-vol", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{SecretName: instance.Spec.PaiSecret.Name}, + }, + }) + } + + if instance.Spec.PaiConfigFile != nil && instance.Spec.PaiConfigFile.Name != "" { + vols = append(vols, corev1.Volume{ + Name: instance.Name + "configmap-vol", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: instance.Spec.PaiConfigFile.Name}}, + }, + }) + } + + if instance.Spec.StorageClass != "" { + vols = append(vols, corev1.Volume{ + Name: instance.Name + "-oradata-vol4", + VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: instance.Name + "-oradata-vol4"}}, + }) + } + + vols = append(vols, corev1.Volume{ + Name: instance.Name + "-logs-vol", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }) + + return vols +} + +func buildVolumeMountSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.VolumeMount { + var vms []corev1.VolumeMount + + // Main data PVC mount + if instance.Spec.StorageClass != "" { + vms = append(vms, corev1.VolumeMount{ + Name: instance.Name + "-oradata-vol4", + MountPath: piDataMount, + }) + } + + if instance.Spec.PaiSecret != nil { + vms = append(vms, corev1.VolumeMount{ + Name: instance.Name + "secret-vol", + MountPath: instance.Spec.PaiSecret.MountLocation, + ReadOnly: true, + }) + } + + if instance.Spec.PaiConfigFile != nil { + vms = append(vms, corev1.VolumeMount{ + Name: instance.Name + "configmap-vol", + MountPath: instance.Spec.PaiConfigFile.MountLocation, + ReadOnly: true, + }) + } + + // NEW: Logs mount + logMount := defaultLogMount + if instance.Spec.PaiLogLocation != "" { + logMount = instance.Spec.PaiLogLocation + } + + vms = append(vms, corev1.VolumeMount{ + Name: instance.Name + "-logs-vol", + MountPath: logMount, + }) + + return vms +} + +func VolumeClaimTemplatesForPrivateAi(instance *privateaiv4.PrivateAi) []corev1.PersistentVolumeClaim { + + var claims []corev1.PersistentVolumeClaim + + claims = []corev1.PersistentVolumeClaim{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: instance.Name + "-oradata-vol4", + Namespace: instance.Namespace, + OwnerReferences: getOwnerRef(instance), + Labels: buildLabelsForPrivateAi(instance, "privateai", instance.Name), + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + StorageClassName: &instance.Spec.StorageClass, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(strconv.FormatInt(int64(instance.Spec.StorageSizeInGb), 10) + "Gi"), + }, + }, + }, + }, + } + + // if len(instance.Spec.PvcList) != 0 { + + //claims = append(claims, slices.Collect(maps.Values(instance.Spec.PvcList))) + // } + + return claims +} + +func buildEnvVarsForPrivateAI(instance *privateaiv4.PrivateAi, envVars []privateaiv4.EnvironmentVariable) []corev1.EnvVar { + var envs []corev1.EnvVar + var omlConfigFileFlag bool + var omlSecretFlag bool + + for _, ev := range envVars { + if ev.Name == "OML_CONFIG_FILE" { + omlConfigFileFlag = true + } + if ev.Name == "OML_SECRETS_MOUNTPOINT" { + omlSecretFlag = true + } + envs = append(envs, corev1.EnvVar{ + Name: ev.Name, + Value: ev.Value, + }) + } + + if !omlConfigFileFlag { + if instance.Spec.PaiConfigFile.MountLocation != "" && instance.Spec.PaiConfigFile.Name != "" { + envs = append(envs, corev1.EnvVar{ + Name: "OML_CONFIG_FILE", + Value: instance.Spec.PaiConfigFile.MountLocation + "/" + "config.json", + }) + } + } + + if !omlSecretFlag { + if instance.Spec.PaiSecret.Name != "" && instance.Spec.PaiSecret.MountLocation != "" { + envs = append(envs, corev1.EnvVar{ + Name: "OML_SECRETS_MOUNTPOINT", + Value: instance.Spec.PaiSecret.MountLocation, + }) + } + } + + return envs +} + +func getOwnerRefPrivateAI(instance *privateaiv4.PrivateAi) []metav1.OwnerReference { + return []metav1.OwnerReference{ + *metav1.NewControllerRef(instance, privateaiv4.GroupVersion.WithKind("PrivateAI")), + } +} + +func BuildServiceDefForPrivateAi(instance *privateaiv4.PrivateAi, svctype string) *corev1.Service { + //service := &corev1.Service{} + service := &corev1.Service{ + ObjectMeta: buildSvcObjectMetaForPrivateAi(instance, svctype), + Spec: corev1.ServiceSpec{}, + } + + // Check if user want External Svc on each replica pod + if svctype == "external" { + service.Spec.Type = corev1.ServiceTypeLoadBalancer + service.Spec.Selector = buildLabelsForPrivateAi(instance, "privateai", instance.Name) + } + + if svctype == "local" { + service.Spec.ClusterIP = corev1.ClusterIPNone + service.Spec.Selector = buildLabelsForPrivateAi(instance, "privateai", instance.Name) + } + + // build Service Ports Specs to be exposed. If the PortMappings is not set then default ports will be exposed. + service.Spec.Ports = buildSvcPortsDef(instance) + if instance.Spec.PaiLBIP != "" { + service.Spec.LoadBalancerIP = instance.Spec.PaiLBIP + } + return service +} + +// Function to build Service ObjectMeta +func buildSvcObjectMetaForPrivateAi(instance *privateaiv4.PrivateAi, svctype string) metav1.ObjectMeta { + // building objectMeta + var svcName string + if svctype == "local" { + svcName = instance.Name + } + + if svctype == "external" { + svcName = instance.Name + "-svc" // consistent single svc name + } + + objmeta := metav1.ObjectMeta{ + Name: svcName, + Namespace: instance.Namespace, + OwnerReferences: getOwnerRef(instance), + Labels: buildSvcLabelsForPrivateAi(instance, svctype, instance.Name), + } + return objmeta +} + +func buildSvcLabelsForPrivateAi(instance *privateaiv4.PrivateAi, svctype, name string) map[string]string { + labelMap := buildLabelsForPrivateAi(instance, "privateai", instance.Name) + labelMap["app.kubernetes.io/servicetype"] = svctype + + return labelMap +} + +// Update Section +func ManageReplicas( + r client.Reader, + instance *privateaiv4.PrivateAi, + kClient client.Client, + Config *rest.Config, + deploy *appsv1.Deployment, + podList *corev1.PodList, + ctx context.Context, + req ctrl.Request, + logger logr.Logger, +) (ctrl.Result, error) { + var msg string + var desired int32 = 1 + + if instance.Spec.Replicas > 0 { + desired = instance.Spec.Replicas + } + + current := *deploy.Spec.Replicas + + //var isUpdate bool + + if deploy.Spec.Replicas != nil && current != desired { + msg = "Current Deployment replicas do not match configured PrivateAI Replicas." + LogMessages("DEBUG", msg, nil, instance, logger) + //isUpdate = true + //isUpdate = true + } + + diff := current - desired + + if diff > 0 && len(podList.Items) > 0 { + count := int32(0) + for i := range podList.Items { + if count >= diff { + break + } + pod := &podList.Items[i] + // var touchFileCmd []string = []string{"/bin/touch /tmp/unhealthy"} + var touchFileCmd []string = []string{"/bin/touch", "/tmp/unhealthy"} + + _, err := ExecCommand( + r, + Config, + pod.Name, + pod.Namespace, + pod.Spec.Containers[0].Name, + ctx, + req, + false, + touchFileCmd, + ) + if err != nil { + LogMessages("ERROR", "Failed to exec command", err, instance, logger) + return ctrl.Result{}, err + } + err = kClient.Update(context.Background(), pod) + if err != nil { + msg = "Failed to update Pod Label: " + pod.Name + return ctrl.Result{}, err + } + count++ + + } + } + + return ctrl.Result{}, nil +} + +// Update Section +func UpdateDeploySetForPrivateAI(instance *privateaiv4.PrivateAi, paiSpec privateaiv4.PrivateAiSpec, kClient client.Client, Config *rest.Config, deploy *appsv1.Deployment, pod *corev1.Pod, logger logr.Logger) (ctrl.Result, error) { + //var msg string + + // var isUpdate bool + + for i := range pod.Spec.Containers { + if pod.Spec.Containers[i].Name == deploy.Name { + contRes := pod.Spec.Containers[i].Resources + paiRes := paiSpec.Resources + if !reflect.DeepEqual(contRes, paiRes) { + // isUpdate = true + LogMessages("DEBUG", "Container ", nil, instance, logger) + } + } + } + + // if isUpdate { + // err := kClient.Update(context.Background(), BuildDeploySetForPrivateAI(instance)) + // if err != nil { + // //msg = "Failed to update PrivateAI Deployment: " + deploy.Name + // return ctrl.Result{}, err + // } + // } + + return ctrl.Result{}, nil +} diff --git a/commons/omlai/exec.go b/commons/omlai/exec.go new file mode 100644 index 00000000..6973490d --- /dev/null +++ b/commons/omlai/exec.go @@ -0,0 +1,125 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package commons + +import ( + "bytes" + "context" + "fmt" + "strings" + + // TODO + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/remotecommand" +) + +// ExecCMDInContainer execute command in first container of a pod +func ExecCommand( + r client.Reader, + config *rest.Config, + podName string, + namespace string, + containerName string, + ctx context.Context, + req ctrl.Request, + nologCommand bool, + command []string, +) (string, error) { + + log := ctrl.Log.WithValues("ExecCommand", req.NamespacedName) + + if !nologCommand { + log.Info("Executing Command:") + log.Info(strings.Join(command, " ")) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return "", fmt.Errorf("failed to build clientset: %w", err) + } + + restClient := clientset.CoreV1().RESTClient() + if restClient == nil { + return "", fmt.Errorf("failed to get RESTClient") + } + + // Get the pod to ensure it exists. + pod := &corev1.Pod{} + err = r.Get(ctx, client.ObjectKey{Namespace: namespace, Name: podName}, pod) + if err != nil { + return "", fmt.Errorf("could not find pod to exec in: %w", err) + } + + // Build the exec request + reqExec := restClient.Post(). + Resource("pods"). + Name(podName). + Namespace(namespace). + SubResource("exec"). + VersionedParams(&corev1.PodExecOptions{ + Command: command, + Container: containerName, + Stdout: true, + Stderr: true, + }, scheme.ParameterCodec) + + exec, err := remotecommand.NewSPDYExecutor(config, "POST", reqExec.URL()) + if err != nil { + return "", fmt.Errorf("failed to create executor: %w", err) + } + + var stdout, stderr bytes.Buffer + err = exec.Stream(remotecommand.StreamOptions{ + Stdout: &stdout, + Stderr: &stderr, + Tty: false, + }) + if err != nil { + return stdout.String(), fmt.Errorf("stream error: %w, stderr: %s", err, stderr.String()) + } + + return stdout.String(), nil +} diff --git a/config/certmanager/certificate-metrics.yaml b/config/certmanager/certificate-metrics.yaml new file mode 100644 index 00000000..c66f3fe6 --- /dev/null +++ b/config/certmanager/certificate-metrics.yaml @@ -0,0 +1,20 @@ +# The following manifests contain a self-signed issuer CR and a metrics certificate CR. +# More document can be found at https://docs.cert-manager.io +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: metrics-certs # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + dnsNames: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. + - SERVICE_NAME.SERVICE_NAMESPACE.svc + - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: metrics-server-cert diff --git a/config/certmanager/certificate-webhook.yaml b/config/certmanager/certificate-webhook.yaml new file mode 100644 index 00000000..1863f13b --- /dev/null +++ b/config/certmanager/certificate-webhook.yaml @@ -0,0 +1,20 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. + dnsNames: + - SERVICE_NAME.SERVICE_NAMESPACE.svc + - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert diff --git a/config/certmanager/issuer.yaml b/config/certmanager/issuer.yaml new file mode 100644 index 00000000..0518b674 --- /dev/null +++ b/config/certmanager/issuer.yaml @@ -0,0 +1,13 @@ +# The following manifest contains a self-signed issuer CR. +# More information can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} diff --git a/config/crd/bases/omlai.oracle.com_privateais.yaml b/config/crd/bases/omlai.oracle.com_privateais.yaml new file mode 100644 index 00000000..f91ca54b --- /dev/null +++ b/config/crd/bases/omlai.oracle.com_privateais.yaml @@ -0,0 +1,298 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: privateais.omlai.oracle.com +spec: + group: omlai.oracle.com + names: + kind: PrivateAi + listKind: PrivateAiList + plural: privateais + singular: privateai + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.replicas + name: Replicas + type: number + - jsonPath: .status.apikey + name: ApiKey + type: string + - jsonPath: .status.podip + name: PodIP + type: string + - jsonPath: .status.loadbalancerip + name: LbIP + type: string + - jsonPath: .status.ReleaseUpdate + name: ReleaseUpdate + priority: 1 + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + livenessCheckPeriod: + type: integer + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + type: object + type: array + paiAuthentication: + type: boolean + paiConfigFile: + properties: + mountLocation: + type: string + name: + type: string + type: object + paiEnableAuthentication: + type: boolean + paiHTTPEnabled: + type: boolean + paiHTTPPort: + format: int32 + type: integer + paiHTTPSEnabled: + type: boolean + paiHTTPSPort: + format: int32 + type: integer + paiImage: + type: string + paiImagePullSecret: + type: string + paiLBIP: + type: string + paiLBPort: + format: int32 + type: integer + paiLogLocation: + type: string + paiSecret: + properties: + mountLocation: + type: string + name: + type: string + type: object + paiService: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + type: object + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + pvcList: + additionalProperties: + type: string + type: object + readinessCheckPeriod: + type: integer + replicas: + format: int32 + type: integer + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageClass: + type: string + storageSizeInGb: + format: int32 + type: integer + type: object + status: + properties: + NodeIP: + type: string + apiKey: + type: string + certpem: + type: string + clusterIP: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancerIP: + type: string + podIP: + type: string + releaseUpdate: + type: string + replicas: + type: integer + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index d73e069d..8f76c549 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -21,6 +21,7 @@ resources: - bases/database.oracle.com_lrests.yaml - bases/database.oracle.com_lrpdbs.yaml - bases/database.oracle.com_ordssrvs.yaml +- bases/omlai.oracle.com_privateais.yaml - bases/database.oracle.com_oraclerestarts.yaml # +kubebuilder:scaffold:crdkustomizeresource diff --git a/config/network-policy/allow-webhook-traffic.yaml b/config/network-policy/allow-webhook-traffic.yaml new file mode 100644 index 00000000..7acb3a39 --- /dev/null +++ b/config/network-policy/allow-webhook-traffic.yaml @@ -0,0 +1,27 @@ +# This NetworkPolicy allows ingress traffic to your webhook server running +# as part of the controller-manager from specific namespaces and pods. CR(s) which uses webhooks +# will only work when applied in namespaces labeled with 'webhook: enabled' +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: allow-webhook-traffic + namespace: system +spec: + podSelector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: oracle-database-operator + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label webhook: enabled + - from: + - namespaceSelector: + matchLabels: + webhook: enabled # Only from namespaces with this label + ports: + - port: 443 + protocol: TCP diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 14a2bc9f..e9c0caa0 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -18,7 +18,5 @@ resources: # default, aiding admins in cluster management. Those roles are # not used by the {{ .ProjectName }} itself. You can comment the following lines # if you do not want those helpers be installed with your Project. -- database_oraclerestart_admin_role.yaml -- database_oraclerestart_editor_role.yaml -- database_oraclerestart_viewer_role.yaml + diff --git a/config/rbac/omlai_privateai_admin_role.yaml b/config/rbac/omlai_privateai_admin_role.yaml new file mode 100644 index 00000000..5de0e698 --- /dev/null +++ b/config/rbac/omlai_privateai_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project oracle-database-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over omlai.oracle.com. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: omlai-privateai-admin-role +rules: +- apiGroups: + - omlai.oracle.com + resources: + - privateais + verbs: + - '*' +- apiGroups: + - omlai.oracle.com + resources: + - privateais/status + verbs: + - get diff --git a/config/rbac/omlai_privateai_editor_role.yaml b/config/rbac/omlai_privateai_editor_role.yaml new file mode 100644 index 00000000..a41b49f0 --- /dev/null +++ b/config/rbac/omlai_privateai_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project oracle-database-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the omlai.oracle.com. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: omlai-privateai-editor-role +rules: +- apiGroups: + - omlai.oracle.com + resources: + - privateais + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - omlai.oracle.com + resources: + - privateais/status + verbs: + - get diff --git a/config/rbac/omlai_privateai_viewer_role.yaml b/config/rbac/omlai_privateai_viewer_role.yaml new file mode 100644 index 00000000..7b255cd5 --- /dev/null +++ b/config/rbac/omlai_privateai_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project oracle-database-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to omlai.oracle.com resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: omlai-privateai-viewer-role +rules: +- apiGroups: + - omlai.oracle.com + resources: + - privateais + verbs: + - get + - list + - watch +- apiGroups: + - omlai.oracle.com + resources: + - privateais/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index a87b3899..e1fc03bd 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -220,6 +220,32 @@ rules: - get - patch - update +- apiGroups: + - omlai.oracle.com + resources: + - privateais + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - omlai.oracle.com + resources: + - privateais/finalizers + verbs: + - update +- apiGroups: + - omlai.oracle.com + resources: + - privateais/status + verbs: + - get + - patch + - update - apiGroups: - storage.k8s.io resources: diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index f8d3cd70..da2a839d 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -14,5 +14,6 @@ resources: - observability/databaseobserver.yaml - sharding/sharding_v1alpha1_provshard.yaml - sidb/singleinstancedatabase_create.yaml +- omlai_v4_privateai.yaml - database_v4_oraclerestart.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/omlai_v4_privateai.yaml b/config/samples/omlai_v4_privateai.yaml new file mode 100644 index 00000000..ea495ac0 --- /dev/null +++ b/config/samples/omlai_v4_privateai.yaml @@ -0,0 +1,9 @@ +apiVersion: omlai.oracle.com/v4 +kind: PrivateAi +metadata: + labels: + app.kubernetes.io/name: oracle-database-operator + app.kubernetes.io/managed-by: kustomize + name: privateai-sample +spec: + # TODO(user): Add fields here diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 7cc3a0fc..ba18e3af 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -311,6 +311,26 @@ webhooks: resources: - databaseobservers sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-omlai-oracle-com-v4-privateai + failurePolicy: Fail + name: mprivateai-v4.kb.io + rules: + - apiGroups: + - omlai.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - privateais + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -728,3 +748,23 @@ webhooks: resources: - databaseobservers sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-omlai-oracle-com-v4-privateai + failurePolicy: Fail + name: vprivateai-v4.kb.io + rules: + - apiGroups: + - omlai.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - privateais + sideEffects: None diff --git a/controllers/omlai/privateai_controller.go b/controllers/omlai/privateai_controller.go new file mode 100644 index 00000000..6e8a370e --- /dev/null +++ b/controllers/omlai/privateai_controller.go @@ -0,0 +1,623 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package omlai + +import ( + "context" + "encoding/json" + "fmt" + + // "strconv" + "time" + + "github.com/go-logr/logr" + omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" + aicommons "github.com/oracle/oracle-database-operator/commons/omlai" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// PrivateAiReconciler reconciles a PrivateAi object +type PrivateAiReconciler struct { + client.Client + Scheme *runtime.Scheme + Log logr.Logger + Config *rest.Config + Recorder record.EventRecorder +} + +// To requeue after 15 secs allowing graceful state changes +var requeueY ctrl.Result = ctrl.Result{Requeue: true, RequeueAfter: 15 * time.Second} +var requeueN ctrl.Result = ctrl.Result{} + +var resultNq = ctrl.Result{Requeue: false} +var resultQ = ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second} + +const PrivateAiFinalizer = "omlai.oracle.com/privateaifinalizer" + +// +kubebuilder:rbac:groups=omlai.oracle.com,resources=privateais,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=omlai.oracle.com,resources=privateais/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=omlai.oracle.com,resources=privateais/finalizers,verbs=update +// +kubebuilder:rbac:groups=core,resources=pods;pods/log;pods/exec;secrets;containers;services;events;configmaps;persistentvolumeclaims;namespaces,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=pods/exec,verbs=create + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the PrivateAi object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.4/pkg/reconcile +func (r *PrivateAiReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = logf.FromContext(ctx) + + // TODO(user): your logic here + r.Log.Info("Reconcile requested") + var result ctrl.Result + var err error + completed := false + blocked := false + + privateAiInst := &omlaiv4.PrivateAi{} + defer r.updateReconcileStatus(privateAiInst, ctx, &result, &err, &blocked, &completed) + + err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, privateAiInst) + if err != nil { + if apierrors.IsNotFound(err) { + r.Log.Info("Resource not found") + return requeueN, nil + } + r.Log.Error(err, err.Error()) + return requeueY, err + } + + /* Initialize Status */ + if privateAiInst.Status.Status == "" { + privateAiInst.Status.Status = omlaiv4.StatusPending + privateAiInst.Status.Replicas = 0 + privateAiInst.Status.ReleaseUpdate = omlaiv4.ValueUnavailable + r.Status().Update(ctx, privateAiInst) + } + // Validation Http/Https + if privateAiInst.Spec.PaiHTTPEnabled && privateAiInst.Spec.PaiHTTPSEnabled { + return requeueN, fmt.Errorf("paiHTTPEnabled and PaiHTTPSEnabled cannot be true") + } + if !privateAiInst.Spec.PaiHTTPEnabled && !privateAiInst.Spec.PaiHTTPSEnabled { + privateAiInst.Spec.PaiHTTPEnabled = true + } + + // Validation PaiAuthentication + if privateAiInst.Spec.PaiEnableAuthentication { + if privateAiInst.Spec.PaiSecret.Name == "" { + return requeueN, fmt.Errorf("PaiAuthentication is enabled but no PaiSecret is defined") + } + apiKey, certPem := aicommons.ReadSecret(privateAiInst.Spec.PaiSecret.Name, privateAiInst, r.Client, r.Log) + if apiKey == "" || apiKey == "NONE" { + return requeueN, fmt.Errorf("PaiAuthentication is enabled but apikey is not found in secret") + } + if certPem == "NONE" { + r.Log.Info("PaiAuthentication is enabled but cert.pem not found in secret") // optional soft warning + } + privateAiInst.Status.ApiKey = apiKey + privateAiInst.Status.Certpem = certPem + r.Status().Update(ctx, privateAiInst) + } + + // Manage SingleInstanceDatabase Deletion + err, isPrivateAiDelTrue := r.managePrivateAiDeletion(privateAiInst) + if err != nil { + //r.setCrdLifeCycleState(instance, &result, &err, stateType) + result = resultNq + if isPrivateAiDelTrue == true { + err = nil + return result, err + } else { + return result, err + } + } + + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + if err != nil { + r.Log.Error(err, err.Error()) + return result, err + } + + // First validate + result, err = r.validate(privateAiInst, ctx, req) + if result.Requeue { + r.Log.Info("Spec validation failed, Reconcile queued") + return result, nil + } + if err != nil { + r.Log.Info("Spec validation failed") + return result, nil + } + + // Create PVC + claims := aicommons.VolumeClaimTemplatesForPrivateAi(privateAiInst) + for i := 0; i < len(claims); i++ { + result, err = r.createPvc(privateAiInst, &claims[i]) + if err != nil { + result = resultNq + return result, err + } + } + + // ========================= Service Setup For Catalog=================== + // Following check and loop will make sure to create the service + result, err = r.createService(privateAiInst, aicommons.BuildServiceDefForPrivateAi(privateAiInst, "local")) + if err != nil { + result = resultNq + return result, err + } + if privateAiInst.Spec.IsExternalSvc { + result, err = r.createService(privateAiInst, aicommons.BuildServiceDefForPrivateAi(privateAiInst, "external")) + if err != nil { + result = resultNq + return result, err + } + } + + // See if DeploymentSets already exists and create if it doesn't + desiredDeploy := aicommons.BuildDeploySetForPrivateAI(privateAiInst) + + foundDeploy := &appsv1.Deployment{} + err = r.Client.Get(ctx, types.NamespacedName{ + Name: desiredDeploy.Name, + Namespace: desiredDeploy.Namespace, + }, foundDeploy) + + if err != nil && apierrors.IsNotFound(err) { + // didn't found create New deployment + result, err = r.deployPrivateAiDeploymentSet(privateAiInst, desiredDeploy) + if err != nil { + return resultNq, err + } + + } else if err == nil { + //if exists check Pods & run + podList := &corev1.PodList{} + err = r.Client.List(ctx, podList, + client.InNamespace(privateAiInst.Namespace), + client.MatchingLabels(foundDeploy.Spec.Selector.MatchLabels), + ) + if err != nil { + return resultNq, err + } + + var foundPod *corev1.Pod + if len(podList.Items) > 0 { + foundPod = &podList.Items[0] + } else { + foundPod = &corev1.Pod{} + } + + // _, err = aicommons.ManageReplicas(privateAiInst, r.Client, r.Config, foundDeploy, podList, r.Log) + _, err = aicommons.ManageReplicas(r, privateAiInst, r.Client, r.Config, foundDeploy, podList, ctx, req, r.Log) + + if err != nil { + return resultNq, err + } + + _, err = aicommons.UpdateDeploySetForPrivateAI(privateAiInst, privateAiInst.Spec, r.Client, r.Config, foundDeploy, foundPod, r.Log) + if err != nil { + return resultNq, err + } + + } else { + return resultNq, err + } + + completed = true + r.Log.Info("Reconcile completed") + + return requeueN, nil +} + +// ############################################################################# +// +// Update each reconcile condtion/status +// +// ############################################################################# +func (r *PrivateAiReconciler) updateReconcileStatus(m *omlaiv4.PrivateAi, ctx context.Context, + result *ctrl.Result, err *error, blocked *bool, completed *bool) { + + // Always refresh status before a reconcile + defer r.Status().Update(ctx, m) + + m.Status.Replicas = int(m.Spec.Replicas) + m.Status.Status = omlaiv4.StatusReady + m.Status.ReleaseUpdate = "V2.0" + //m.Status.ApiKey = m.Spec.PaiSecret.Name + //m.Status.PodIP = + // m.Status.LoadBalancerIP = + + errMsg := func() string { + if *err != nil { + return (*err).Error() + } + return "no reconcile errors" + }() + var condition metav1.Condition + if *completed { + condition = metav1.Condition{ + Type: omlaiv4.ReconcileCompelete, + LastTransitionTime: metav1.Now(), + ObservedGeneration: m.GetGeneration(), + Reason: omlaiv4.ReconcileCompleteReason, + Message: errMsg, + Status: metav1.ConditionTrue, + } + } else if *blocked { + condition = metav1.Condition{ + Type: omlaiv4.ReconcileBlocked, + LastTransitionTime: metav1.Now(), + ObservedGeneration: m.GetGeneration(), + Reason: omlaiv4.ReconcileBlockedReason, + Message: errMsg, + Status: metav1.ConditionTrue, + } + } else if result.Requeue { + condition = metav1.Condition{ + Type: omlaiv4.ReconcileQueued, + LastTransitionTime: metav1.Now(), + ObservedGeneration: m.GetGeneration(), + Reason: omlaiv4.ReconcileQueuedReason, + Message: errMsg, + Status: metav1.ConditionTrue, + } + } else if *err != nil { + condition = metav1.Condition{ + Type: omlaiv4.ReconcileError, + LastTransitionTime: metav1.Now(), + ObservedGeneration: m.GetGeneration(), + Reason: omlaiv4.ReconcileErrorReason, + Message: errMsg, + Status: metav1.ConditionTrue, + } + } else { + return + } + if len(m.Status.Conditions) > 0 { + meta.RemoveStatusCondition(&m.Status.Conditions, condition.Type) + } + meta.SetStatusCondition(&m.Status.Conditions, condition) +} + +// ############################################################################# +// +// Validate the CRD specs +// +// ############################################################################# +func (r *PrivateAiReconciler) validate(m *omlaiv4.PrivateAi, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + //var err error + //eventReason := "Spec Error" + //var eventMsgs []string + + r.Log.Info("Entering reconcile validation") + + r.Log.Info("Completed reconcile validation") + + return requeueN, nil + +} + +// SetupWithManager sets up the controller with the Manager. +func (r *PrivateAiReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&omlaiv4.PrivateAi{}). + Named("omlai-privateai"). + Owns(&appsv1.Deployment{}). + Owns(&corev1.Service{}). + Owns(&corev1.Secret{}). + WithOptions(controller.Options{MaxConcurrentReconciles: 50}). + Complete(r) +} + +// This function deploy the DeploymentSet +func (r *PrivateAiReconciler) deployPrivateAiDeploymentSet(instance *omlaiv4.PrivateAi, + dep *appsv1.Deployment, +) (ctrl.Result, error) { + + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + message := "Inside the deployDeploymentSet function" + aicommons.LogMessages("DEBUG", message, nil, instance, r.Log) + // See if DeploymentSets already exists and create if it doesn't + // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) + // This happens during unit test cases + for i := 0; i < 5; i++ { + if r.Scheme == nil { + time.Sleep(time.Second * 40) + } else { + break + } + } + controllerutil.SetControllerReference(instance, dep, r.Scheme) + found := &appsv1.Deployment{} + err := r.Client.Get(context.TODO(), types.NamespacedName{ + Name: dep.Name, + Namespace: instance.Namespace, + }, found) + jsn, _ := json.Marshal(dep) + aicommons.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) + + if err != nil && errors.IsNotFound(err) { + + // Create the DeploymentSet + reqLogger.Info("Creating Deployment Shard") + err = r.Client.Create(context.TODO(), dep) + + message := "Inside the create Deployment set block to create DeploymentSet " + aicommons.GetFmtStr(dep.Name) + aicommons.LogMessages("DEBUG", message, nil, instance, r.Log) + + if err != nil { + // DeploymentSet failed + reqLogger.Error(err, "Failed to create DeploymentSet", "DeploymentSet.space", dep.Namespace, "DeploymentSet.Name", dep.Name) + //instance.Status.ShardStatus[dep.Name] = "Deployment Failed" + return ctrl.Result{}, err + } + } else if err != nil { + // Error that isn't due to the StaefulSet not existing + reqLogger.Error(err, "Failed to get DeploymentSet") + return ctrl.Result{}, err + } + + message = "DeploymentSet Exist " + aicommons.GetFmtStr(dep.Name) + " already exist" + aicommons.LogMessages("DEBUG", message, nil, instance, r.Log) + + return ctrl.Result{}, nil +} + +// ================================== CREATE FUNCTIONS ============================= +// This function create a service based isExtern parameter set in the yaml file +func (r *PrivateAiReconciler) createService(instance *omlaiv4.PrivateAi, + dep *corev1.Service, +) (ctrl.Result, error) { + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + // See if Service already exists and create if it doesn't + // We are getting error on nil pointer segment when r.scheme is null + // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) + // This happens during unit test cases + for i := 0; i < 5; i++ { + if r.Scheme == nil { + time.Sleep(time.Second * 40) + } else { + break + } + } + controllerutil.SetControllerReference(instance, dep, r.Scheme) + found := &corev1.Service{} + + err := r.Client.Get(context.TODO(), types.NamespacedName{ + Name: dep.Name, + Namespace: instance.Namespace, + }, found) + + jsn, _ := json.Marshal(dep) + aicommons.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) + if err != nil && errors.IsNotFound(err) { + // Create the Service + reqLogger.Info("Creating a service") + err = r.Client.Create(context.TODO(), dep) + if err != nil { + // Service creation failed + reqLogger.Error(err, "Failed to create Service", "Service.space", dep.Namespace, "Service.Name", dep.Name) + return ctrl.Result{}, err + } else { + // Service creation was successful + return ctrl.Result{Requeue: true}, nil + } + } else if err != nil { + // Error that isn't due to the Service not existing + reqLogger.Error(err, "Failed to find the Service details") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +// ================================== CREATE FUNCTIONS ============================= +// This function create a PVC based isExtern parameter set in the yaml file +func (r *PrivateAiReconciler) createPvc(instance *omlaiv4.PrivateAi, + dep *corev1.PersistentVolumeClaim, +) (ctrl.Result, error) { + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + // See if Service already exists and create if it doesn't + // We are getting error on nil pointer segment when r.scheme is null + // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) + // This happens during unit test cases + for i := 0; i < 5; i++ { + if r.Scheme == nil { + time.Sleep(time.Second * 40) + } else { + break + } + } + controllerutil.SetControllerReference(instance, dep, r.Scheme) + found := &corev1.PersistentVolumeClaim{} + + err := r.Client.Get(context.TODO(), types.NamespacedName{ + Name: dep.Name, + Namespace: instance.Namespace, + }, found) + + jsn, _ := json.Marshal(dep) + aicommons.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) + if err != nil && errors.IsNotFound(err) { + // Create the Service + reqLogger.Info("Creating PVC") + err = r.Client.Create(context.TODO(), dep) + if err != nil { + // Service creation failed + reqLogger.Error(err, "Failed to create PVC", "PVC.namespace", dep.Namespace, "PVC.Name", dep.Name) + return ctrl.Result{}, err + } else { + // Service creation was successful + return ctrl.Result{Requeue: true}, nil + } + } else if err != nil { + // Error that isn't due to the Service not existing + reqLogger.Error(err, "Failed to find the Service details") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +// FInalizer + +// ================== Function to check insytance deletion timestamp and activate the finalizer code ======== +func (r *PrivateAiReconciler) managePrivateAiDeletion(instance *omlaiv4.PrivateAi) (error, bool) { + + isPrivateToBeDeleted := instance.GetDeletionTimestamp() != nil + if isPrivateToBeDeleted { + if controllerutil.ContainsFinalizer(instance, PrivateAiFinalizer) { + // Run finalization logic for finalizer. If the + // finalization logic fails, don't remove the finalizer so + // that we can retry during the next reconciliation. + if err := r.finalizePrivateAi(instance); err != nil { + return err, false + } + + // Remove finalizer. Once all finalizers have been + // removed, the object will be deleted. + controllerutil.RemoveFinalizer(instance, PrivateAiFinalizer) + err := r.Client.Update(context.TODO(), instance) + if err != nil { + return err, false + } + } + // Send true because delete is in progress and it is a custom delete message + // We don't need to print custom err stack as we are deleting the topology + return fmt.Errorf("delete of the sharding topology is in progress"), true + } + + // Add finalizer for this CR + if instance.DeletionTimestamp == nil { + if !controllerutil.ContainsFinalizer(instance, PrivateAiFinalizer) { + if err := r.addFinalizer(instance); err != nil { + return err, false + } + } + } + + return nil, false +} + +// ========================== FInalizer Section =================== +func (r *PrivateAiReconciler) addFinalizer(instance *omlaiv4.PrivateAi) error { + reqLogger := r.Log.WithValues("instance.Namespace", instance.Namespace, "instance.Name", instance.Name) + controllerutil.AddFinalizer(instance, PrivateAiFinalizer) + + // Update CR + err := r.Client.Update(context.TODO(), instance) + if err != nil { + reqLogger.Error(err, "Failed to update Sharding Database with finalizer") + return err + } + return nil +} + +func (r *PrivateAiReconciler) finalizePrivateAi(instance *omlaiv4.PrivateAi) error { + // TODO(user): Add the cleanup steps that the operator needs to do before the CR + // can be deleted. Examples of finalizers include performing backups and deleting + // resources that are not owned by this CR, like a PVC. + + var err error + var pvcName string + + //r.checkProvInstance(instance) + depSetFound := &appsv1.Deployment{} + svcFound := &corev1.Service{} + depSetFound, err = aicommons.CheckDepSet(instance, r.Client) + if err == nil { + // See if StatefulSets already exists and create if it doesn't + err = r.Client.Delete(context.Background(), depSetFound) + if err != nil { + return err + } + if instance.Spec.IsDeleteOraPvc && len(instance.Spec.StorageClass) > 0 { + pvcName = instance.Name + "-oradata-vol4-" + instance.Name + "-0" + err = aicommons.DelPvc(pvcName, instance, r.Client, r.Log) + if err != nil { + return err + } + } + + if instance.Spec.IsExternalSvc { + // svcFound, err = aicommons.CheckSvc(instance.Name+strconv.FormatInt(int64(0), 10)+"-svc", instance, r.Client) + svcFound, err = aicommons.CheckSvc(instance.Name+"-svc", instance, r.Client) + + if err == nil { + // See if StatefulSets already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err + } + } + } + svcFound, err = aicommons.CheckSvc(instance.Name, instance, r.Client) + if err == nil { + // See if StatefulSets already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err + } + } + } + return nil +} diff --git a/controllers/omlai/privateai_controller_test.go b/controllers/omlai/privateai_controller_test.go new file mode 100644 index 00000000..70364c7e --- /dev/null +++ b/controllers/omlai/privateai_controller_test.go @@ -0,0 +1,106 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package omlai + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" +) + +var _ = Describe("PrivateAi Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + privateai := &omlaiv4.PrivateAi{} + + BeforeEach(func() { + By("creating the custom resource for the Kind PrivateAi") + err := k8sClient.Get(ctx, typeNamespacedName, privateai) + if err != nil && errors.IsNotFound(err) { + resource := &omlaiv4.PrivateAi{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &omlaiv4.PrivateAi{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance PrivateAi") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &PrivateAiReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/controllers/omlai/suite_test.go b/controllers/omlai/suite_test.go new file mode 100644 index 00000000..c1d5dda7 --- /dev/null +++ b/controllers/omlai/suite_test.go @@ -0,0 +1,138 @@ +/* +** Copyright (c) 2022 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package omlai + +import ( + "context" + "os" + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + ctx context.Context + cancel context.CancelFunc + testEnv *envtest.Environment + cfg *rest.Config + k8sClient client.Client +) + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + var err error + err = omlaiv4.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/docs/privateai/README.md b/docs/privateai/README.md new file mode 100644 index 00000000..388e5b31 --- /dev/null +++ b/docs/privateai/README.md @@ -0,0 +1,121 @@ +# Using Oracle PrivateAI Controller with Oracle Database Operator for Kubernetes + +Oracle PrivateAI Controller automates the deployment and usage of the Oracle PrivateAI Container. AI Container project aims to deliver to customers a lightweight containerized web service that provides an interface for performing inference on ONNX format models via REST. The AI container will help offload expensive AI computation (e.g., embedding generation) outside the database. This addresses requests made by AI Vector Search customers who would prefer to use the database compute primarily for indexing and search. + +Kubernetes provides infrastructure building blocks, such as compute, storage, and networks. Kubernetes makes the infrastructure available as code. which are a key resource object for managing and updating applications. Deployments enable declarative updates, ensuring a specified number of application replicas are running, and handle scaling, rolling updates, and rollbacks automatically. + + +The PrivateAI controller in Oracle Database Operator deploys Oracle PrivateAI container as a deploymentset in the Kubernetes clusters, using Oracle PrivateAI Container image. The Oracle PrivateAI controller provides end-to-end automation of Oracle PrivateAI Container deployment in Kubernetes clusters. + +## Using Oracle Database Operator PrivateAI Controller + +Following sections provide the details for deploying Oracle PrivateAI container using Oracle Database Operator PrivateAI Controller with different use cases: + +* [Prerequisites for running Oracle PrivateAI Controller](#prerequisites-for-running-oracle-privartai-controller) +* [Quick Start](#quick-start) +* [Accessing PrivateAI Container Pods](#accessing-the-privateai-container-pod-in-kubernetes) +* [Debugging and Troubleshooting](#debugging-and-troubleshooting) + +**Note:** Before proceeding to the next section, you must complete the instructions given in each section, based on your enviornment, before proceeding to next section. + +## Prerequisites for running Oracle PrivartAI Controller + +**IMPORTANT:** You must make the changes specified in this section before you proceed to the next section. + +### 1. Kubernetes Cluster: To deploy Oracle PrivateAI controller with Oracle Database Operator, you need a Kubernetes Cluster which can be one of the following: + +* A Cloud-based Kubernetes cluster, such as [OCI on Container Engine for Kubernetes (OKE)](https://www.oracle.com/cloud-native/container-engine-kubernetes/) or +* An On-Premises Kubernetes Cluster, such as [Oracle Linux Cloud Native Environment (OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) cluster. + +To use Oracle PrivateAI Controller, ensure that your system is provisioned with a supported Kubernetes release. Refer to the [Release Status Section](../../README.md#release-status). + +#### Mandatory roles and privileges requirements for Oracle PrivateAI Controller + + Oracle PrivateAI Controller uses Kubernetes objects such as :- + + | Resources | Verbs | + | --- | --- | + | Pods | create delete get list patch update watch | + | Containers | create delete get list patch update watch | + | PersistentVolumeClaims | create delete get list patch update watch | + | Services | create delete get list patch update watch | + | Secrets | create delete get list patch update watch | + | Events | create patch | + +### 2. Deploy Oracle Database Operator + +To deploy Oracle Database Operator in a Kubernetes cluster, go to the section [Install Oracle DB Operator](../../README.md#install-oracle-db-operator) in the README.md, and complete the operator deployment before you proceed further. If you have already deployed the operator, then proceed to the next section. + +**IMPORTANT:** Make sure you have completed the steps for [Role Binding for access management](../../README.md#role-binding-for-access-management) as well before installing the Oracle DB Operator. + +### 3. Oracle PrivateAI Container Image +The pre-built preivateAI container image is available on [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::) and fully supported by Oracle for production uses. + +### 4. Create a namespace for the Oracle PrivateAI Setup + + Create a Kubernetes namespace named `pai`. All the resources belonging to the Oracle PrivateAI topology setup will be provisioned in this namespace named `pai`. For example: + + ```sh + #### Create the namespace + kubectl create ns pai + + #### Check the created namespace + kubectl get ns + ``` + +### 5. Create a configmap for the Oracle PrivateAI Deployment + +Create a `config.json` file. This file has the link for the AI Model File. [config.json](./config.json) is an example of this file. + +Create a configmap using the above file as below: +```sh +kubectl create configmap omlconfigjson --from-file=config.json -n pai +``` + +You can check the details of the configmap as below: +```sh +kubectl get configmap -n pai +``` + +### 6. Reserve LoadBalancer Public IP + +- The SSL certificate used during the PrivateAI Container Deployment will a common name(hostname or IP) to be specified. +- Later, for a secure communication with the PrivateAI Container Deployed in a Kuberentes Cluster, the client will use the same `cert.pem` file and will send the connection request to same hostname or IP. +- If you are deploying PrivateAI Container on an OKE cluster, you will need to reserve a Public IP in OCI. Refer to [OCI Load Balancer Documentation](https://docs.public.oneportal.content.oci.oraclecloud.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm) for the details. +- Once you have reserved the Public IP, use this Public IP as `Common Name` while generating the openssl certificate in the next step. + +### 7. Create Kubernetes secret for the Oracle PrivateAI Deployment + +**IMPORTANT:** Make sure the version of `openssl` in the Oracle PrivateAI image is compatible with the `openssl` version on the machine where you will run the openssl commands to generate the encrypted password file during the deployment. + +Create a file `oml-ssl-pwd` with the password you want to use. This password will be used in the next step. The script [pai_secret.sh](./pai_secret.sh) has the command to generate the required keys and an SSL certificate. + +Use the Shell Script `pai_secret.sh` to create the required secrets for the Oracle PrivateAI Container Deployment. Run this file as below and enter the password when prompted: + +```sh +./pai_secret.sh +``` + +Use below command to check the Kubernetes Secret Created: + +```sh +kubectl get secret -n pai +kubectl describe secret paisecret -n pai +``` + +After you have the above prerequisites completed, you can proceed to the next section for your environment to deploy the Oracle PrivateAI Controller. + + +## Quick Start + +Please refer to [this page](./deploy_privateai.md) for the details to deploy the PrivateAI Container Pod in Kubernetes. + +## Accessing the PrivateAI Container Pod in Kubernetes + +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace in Kuberentes Cluster and you have the Reserved Public IP of the LoadBalancer. + +Please refer to [this page](./access_privateai.md) for the details to access the PrivateAI Container Pod in Kubernetes. + +## Debugging and Troubleshooting + +Please refer to [this page](./debug_privateai.md) for the details to access the PrivateAI Container Pod in Kubernetes. diff --git a/docs/privateai/access_privateai.md b/docs/privateai/access_privateai.md new file mode 100644 index 00000000..55658738 --- /dev/null +++ b/docs/privateai/access_privateai.md @@ -0,0 +1,138 @@ +# Accessing the PrivateAI Container Pod in Kubernetes + +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace and you have the Reserved Public IP of the LoadBalancer. + +## Accessing the PrivateAI Container in Kubernetes using REST Calls +Please follow the below steps to access: + +1. Retrieve API Key + +When you have used the file [pai_secret.sh](./pai_secret.sh) during the deployment, you will have the file named `api-key` generated in the same location. Copy this file `api-key` to the machine where you want to run the API Call to the Model Endpoint. + +2. Keep the LoadBalancer Reserved Public IP ready. You can use this IP in the API Endpoint call in the next step. + +3. Assume the Loadbalancer Reserved Public IP from the last step is `129.xxx.xxx.xxx`, you can use the below command to make an API Endpoint Call: + ```sh + curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /home/opc/api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score + ``` + +## Accessing the PrivateAI Container using REST Calls with SSL certificate + +In case you want to use SSL authentication while accessing the PrivateAI Container in Kubernetes using SSL certificate, then you will need to follow below additional steps: + +1. Copy the `cert.pem` generated when you had run `pai_secret.sh` script, to the machine where you want to run the API Call to the Model Endpoint. +2. Use this key file while running the below modified command to make an API Endpoint Call: + ```sh + curl --cacert cert.pem --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /home/opc/api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score + ``` + +## Accessing the PrivateAI Container using PLSQL Commands from an Oracle Database + +- The access to the PrivateAI Container deployed in Kubernetes Cluster is provided via the utl_to_embedding(s) apis which are part of the dbms_vector_chain package. +- When using the database as a client via the utl_to_embedding or utl_to_embeddings functions, a user will first need to create a credential containing the key using the CREATE_CREDENTIAL procedure. This credential is then referenced when registering the AI Container as a provider for generating embeddings. + +We will follow the below steps to access the PrivateAI Container using PLSQL Commands from an Oracle Database: + +- Copy the `cert.pem` file, which was generated during the SSL certificate creation at the time of the deploying PrivateAI Container in the Kubernetes Cluster, to the Oracle Database host. +- Create a wallet location, create the wallet and add `cert.pem` to this wallet: + ```sh + mkdir -p /home/oracle/wallet/ + orapki wallet create -wallet /home/oracle/wallet/ -pwd + orapki wallet add -wallet /home/oracle/wallet/ -trusted_cert -cert cert.pem -pwd + ``` + +- Add permission to connect as well as to use client certificates. You need to change principal_name to your user/schema("vectordb" in this case). +- Also, use the same `api-key` which was generated when you using the script `pai_secret.sh` + ```sh + connect / as sysdba + + BEGIN + DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE( + host => '*', + ace => xs$ace_type(privilege_list => xs$name_list('connect'), + principal_name => 'vectordb', + principal_type => xs_acl.ptype_db)); + END; + / + + BEGIN + DBMS_NETWORK_ACL_ADMIN.APPEND_WALLET_ACE( + wallet_path => 'file:/home/oracle/wallet', + ace => xs$ace_type( + privilege_list => xs$name_list('use_client_certificates', 'use_passwords'), + principal_name => 'vectordb', + principal_type => xs_acl.ptype_db)); + END; + / + ``` + +- Login as the user and create credential: + ```sh + connect vectordb/@ + exec dbms_vector.drop_credential('PRIVATEAI_CRED'); + + declare + jo json_object_t; + begin + jo := json_object_t(); + jo.put('access_token', ''); + dbms_vector.create_credential( + credential_name => 'PRIVATEAI_CRED', + params => json(jo.to_string)); + end; + / + ``` +- If required, set proxy and no proxy uising "utl_http.set_proxy()" + +- Set the wallet + ```sh + exec utl_http.set_wallet('file:/home/oracle/wallet/', ''); + ``` + +- Declare embedding parameters: + ```sh + var params clob; + + begin + :params := ' + { + "provider": "oracle_ai_instance", + "credential_name": "PRIVATEAI_CRED", + "url": "https://HOSTNAME_OR_IP_ADDRESS_AI_CONTAINER:port_number/omlmodels/all_minilm_v6/score", + "model": "all_minilm_v6", + }'; + end; + / + ``` + +- Get the embeddings + ```sh + select dbms_vector.utl_to_embedding('Hello world', json(:params)) from dual; + ``` + +- To list the models + ```sh + declare + preferences clob; + input clob; + output clob; + begin + preferences := '{ + "provider": "oracle_ai_instance", + "url": "https://HOSTNAME_OR_IP_ADDRESS_AI_CONTAINER:port_number/omlmodels", + "credential_name": "PRIVATEAI_CRED", + }'; + + output := dbms_vector_chain.list_models(json(preferences)); + if length(output) > 5000 then + dbms_output.put_line(dbms_lob.substr(output, 5000)); + else + dbms_output.put_line(json_query(output, '$' returning clob pretty)); + end if; + + if output is not null then + dbms_lob.freetemporary(output); + end if; + end; + / + ``` \ No newline at end of file diff --git a/docs/privateai/api_endpoint.md b/docs/privateai/api_endpoint.md new file mode 100644 index 00000000..94dca74a --- /dev/null +++ b/docs/privateai/api_endpoint.md @@ -0,0 +1,16 @@ +# Test Oracle PrivateAI Container Deployment using API Endpoint + +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace. + +Please follow the below steps to test: + +1. Retrieve API Key + +When you have used the file [pai_secret.sh](./pai_secret.sh) during the deployment, you will have the file named `api-key` generated in the same location. Copy this file `api-key` to the machine where you want to run the API Call to the Model Endpoint. + +2. Get the Loadbalancer External IP from the existing deployment of Oracle PrivateAI Container for the service `service/pai-sample-svc`. You can use this IP in the API Endpoint call in the next step. + +3. Assume the Loadbalancer Extenral IP from the last step is `141.xxx.xxx.xxx`, you can use the below command to make an API Endpoint Call: + ```sh + curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /home/opc/api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://141.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score + ``` \ No newline at end of file diff --git a/docs/privateai/config.json b/docs/privateai/config.json new file mode 100644 index 00000000..b5b8b5d9 --- /dev/null +++ b/docs/privateai/config.json @@ -0,0 +1,16 @@ +{ + "environment":{ + "OML_ENABLE_AUTHENTICATION":true + }, + "models": [ + { + "modelname":"all_minilm_v6", + "modelfile":"https://objectstorage.us-ashburn-1.oraclecloud.com/p/wBcvPtKxMfxmf7BMEcWbRWaXb249Tt1OVR6UPJRXzejXFlHu9XMkh7H6G0HV0uq9/n/intsanjaysingh/b/onnxmodel/o/ALL-MINILM-L6-V2.onnxALL-MINILM-L6-V2.onnx", + "modeltype":"ONNX_TXT", + "metadata":{ + "function":"feature_extraction" + }, + "loadOnStartup":true + } + ] +} diff --git a/docs/privateai/debug_privateai.md b/docs/privateai/debug_privateai.md new file mode 100644 index 00000000..3115a4c4 --- /dev/null +++ b/docs/privateai/debug_privateai.md @@ -0,0 +1,21 @@ +# Debug and Triubleshoot the PrivateAI Container Pod in Kubernetes + +You can use the below commands to debug and troubleshoot the issues listed in this document: + +## To check the logs of the PrivateAI Container Pod + +Use the below command to get the logs of the PrivateAI Container Pod deployed in the Kubernetes Cluster using PrivateAI Controller: + ```sh + - Get the name of the PrivateAI Container Pod deployed in the namespace "pai" + kubectl get pod -n pai + + - Get the logs of the PrivateAI Container Pod deployed in the namespace "pai" + kubectl logs -f pod/ -n pai + ``` + +## Details for HTTP request error + +If you get an HTTP request error when you make utl_to_embedding call, you can run the following to get more details: + ```sh + select utl_http.get_detailed_sqlerrm from dual; + ``` \ No newline at end of file diff --git a/docs/privateai/deploy_privateai.md b/docs/privateai/deploy_privateai.md new file mode 100644 index 00000000..a3c913f4 --- /dev/null +++ b/docs/privateai/deploy_privateai.md @@ -0,0 +1,23 @@ +# Deploying Oracle PrivateAI Container + +Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. + +**IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. + +**NOTE:** Modify the file `pai_sample.yaml` with the actual Reserved Public IP before deployment. + +Use the file: [pai_sample.yaml](./pai_sample.yaml) for this use case as below: + +1. Deploy the `pai_sample.yaml` file: + ```sh + kubectl apply -f pai_sample.yaml + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n pai + + # Check the logs of a particular pod. For example, to check status of pod "pai-sample-b669d7897-nkkhz": + kubectl logs pod/pai-sample-b669d7897-nkkhz -n pai + ``` + \ No newline at end of file diff --git a/docs/privateai/pai_sample.yaml b/docs/privateai/pai_sample.yaml new file mode 100644 index 00000000..feb4a481 --- /dev/null +++ b/docs/privateai/pai_sample.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: omlai.oracle.com/v4 +kind: PrivateAi +metadata: + name: pai-sample + namespace: pai +spec: + paiConfigFile: + name: omlconfigjson + mountLocation: /oml/config + paiSecret: + name: paisecret + mountLocation: /oml/ssl + isExternalSvc: true + storageClass: oci-bv + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 + paiService: + name: paisvc + paiLBIP: 129.xxx.xxx.xxx + storageSizeInGb: 30 + replicas: 1 diff --git a/docs/privateai/pai_sample.yaml.bkp b/docs/privateai/pai_sample.yaml.bkp new file mode 100644 index 00000000..b74c1c68 --- /dev/null +++ b/docs/privateai/pai_sample.yaml.bkp @@ -0,0 +1,27 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: omlai.oracle.com/v4 +kind: PrivateAi +metadata: + name: pai-sample + namespace: pai +spec: + paiConfigFile: + name: omlconfigjson + mountLocation: /oml/config + paiSecret: + name: paisecret + mountLocation: /oml/ssl + isExternalSvc: true + storageClass: oci-bv + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + storageSizeInGb: 30 diff --git a/docs/privateai/pai_sample_scale_in.yaml b/docs/privateai/pai_sample_scale_in.yaml new file mode 100644 index 00000000..0ab57ba0 --- /dev/null +++ b/docs/privateai/pai_sample_scale_in.yaml @@ -0,0 +1,28 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: omlai.oracle.com/v4 +kind: PrivateAi +metadata: + name: pai-sample + namespace: pai +spec: + paiConfigFile: + name: omlconfigjson + mountLocation: /oml/config + paiSecret: + name: paisecret + mountLocation: /oml/ssl + isExternalSvc: true + storageClass: oci-bv + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + storageSizeInGb: 30 + replicas: 3 diff --git a/docs/privateai/pai_sample_scale_up.yaml b/docs/privateai/pai_sample_scale_up.yaml new file mode 100644 index 00000000..0ab57ba0 --- /dev/null +++ b/docs/privateai/pai_sample_scale_up.yaml @@ -0,0 +1,28 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +apiVersion: omlai.oracle.com/v4 +kind: PrivateAi +metadata: + name: pai-sample + namespace: pai +spec: + paiConfigFile: + name: omlconfigjson + mountLocation: /oml/config + paiSecret: + name: paisecret + mountLocation: /oml/ssl + isExternalSvc: true + storageClass: oci-bv + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + storageSizeInGb: 30 + replicas: 3 diff --git a/docs/privateai/pai_secret.sh b/docs/privateai/pai_secret.sh new file mode 100644 index 00000000..377dbe71 --- /dev/null +++ b/docs/privateai/pai_secret.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +CURR_DIR=`pwd` +KEYSTORE_SECRET=keystore +API_SECRET=api-key +OMLSECRET=omlsslpwd +NAMESPACE=pai +SECRET_NAME=paisecret + +rm -f ${CURR_DIR}/key.pem +rm -f ${CURR_DIR}/cert.pem +rm -f ${CURR_DIR}/api-key +rm -f ${CURR_DIR}/key.pub + +head -c 32 /dev/urandom | xxd -p | tr -d '\n' | head -c 64 > api-key +openssl genrsa -out ${CURR_DIR}/key.pem +openssl rsa -in ${CURR_DIR}/key.pem -out ${CURR_DIR}/key.pub -pubout +openssl req -new -x509 -key ${CURR_DIR}/key.pem -out ${CURR_DIR}/cert.pem -days 365 + +# Generate keystore. Enter the password when prompted, and be sure to remember it. +openssl pkcs12 -export -inkey ${CURR_DIR}/key.pem -in ${CURR_DIR}/cert.pem -name mykey -out ${CURR_DIR}/keystore + +kubectl delete secret $SECRET_NAME -n $NAMESPACE +kubectl create secret generic $SECRET_NAME --from-file=keystore --from-file=api-key --from-file=oml-ssl-pwd -n $NAMESPACE +#kubectl create secret generic $API_SECRET --from-file=api-key.txt -n $NAMESPACE +#kubectl create secret generic $OMLSECRET --from-file=oml-ssl-pwd -n $NAMESPACE \ No newline at end of file diff --git a/docs/privateai/scale_in_privateai.md b/docs/privateai/scale_in_privateai.md new file mode 100644 index 00000000..a083a58f --- /dev/null +++ b/docs/privateai/scale_in_privateai.md @@ -0,0 +1,25 @@ +# Scale-In an existing deployment of Oracle PrivateAI Container + +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=3` + +In this example, we will Scale In an existing deployment with `replicas=3` to `replicas=2`. + +Use the file: [pai-sample-scale-in.yaml](./pai-sample-scale-in.yaml) for this use case as below: + +1. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n pai + ``` +2. Apply the `pai-sample-scale-in.yaml` file to scale in: + ```sh + kubectl apply -f pai-sample-scale-in.yaml + ``` +3. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n pai + ``` + + You will see, Kubernetes Pods are reduced in number once the scale in is done automatically. + \ No newline at end of file diff --git a/docs/privateai/scale_up_privateai.md b/docs/privateai/scale_up_privateai.md new file mode 100644 index 00000000..54ec7090 --- /dev/null +++ b/docs/privateai/scale_up_privateai.md @@ -0,0 +1,25 @@ +# Scale-Up an existing deployment of Oracle PrivateAI Container + +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=1` + +In this example, we will Scale Up an existing deployment with `replicas=1` to `replicas=3`. + +Use the file: [pai-sample-scale-up.yaml](./pai-sample-scale-up.yaml) for this use case as below: + +1. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n pai + ``` +2. Deploy the `pai-sample-scale-up.yaml` file to Scale Up: + ```sh + kubectl apply -f pai-sample-scale-up.yaml + ``` +3. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n pai + ``` + + You will see, additional Kubernetes Pods getting deployed once the scale up is done automatically. + \ No newline at end of file diff --git a/go.mod b/go.mod index 91ddd1d4..3708041c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/oracle/oracle-database-operator -go 1.24 +go 1.24.4 require ( github.com/go-logr/logr v1.4.2 diff --git a/main.go b/main.go index bbd3bfd4..24fdc4a1 100644 --- a/main.go +++ b/main.go @@ -75,7 +75,10 @@ import ( observabilityv1 "github.com/oracle/oracle-database-operator/apis/observability/v1" observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" observabilityv4 "github.com/oracle/oracle-database-operator/apis/observability/v4" + omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" + webhookomlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" observabilitycontroller "github.com/oracle/oracle-database-operator/controllers/observability" + omlaicontroller "github.com/oracle/oracle-database-operator/controllers/omlai" // +kubebuilder:scaffold:imports ) @@ -91,6 +94,7 @@ func init() { utilruntime.Must(databasev4.AddToScheme(scheme)) utilruntime.Must(observabilityv1.AddToScheme(scheme)) utilruntime.Must(observabilityv4.AddToScheme(scheme)) + utilruntime.Must(omlaiv4.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -303,98 +307,7 @@ func main() { // Set ENABLE_WEBHOOKS=false when we run locally to skip webhook part when testing just the controller. Not to be used in production. if os.Getenv("ENABLE_WEBHOOKS") != "false" { - if err = (&databasev1alpha1.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") - os.Exit(1) - } - if err = (&databasev1alpha1.OracleRestDataService{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") - os.Exit(1) - } - if err = (&databasev4.PDB{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "PDB") - os.Exit(1) - } - if err = (&databasev4.LRPDB{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "LRPDB") - os.Exit(1) - } - if err = (&databasev4.CDB{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "CDB") - os.Exit(1) - } - if err = (&databasev4.LREST{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "LREST") - os.Exit(1) - } - if err = (&databasev1alpha1.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") - os.Exit(1) - } - if err = (&databasev1alpha1.AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseBackup") - os.Exit(1) - } - if err = (&databasev1alpha1.AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseRestore") - os.Exit(1) - } - if err = (&databasev1alpha1.AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") - os.Exit(1) - } - if err = (&databasev4.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") - os.Exit(1) - } - if err = (&databasev4.AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseBackup") - os.Exit(1) - } - if err = (&databasev4.AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseRestore") - os.Exit(1) - } - if err = (&databasev4.AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") - os.Exit(1) - } - if err = (&databasev1alpha1.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") - os.Exit(1) - } - if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") - os.Exit(1) - } - if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") - os.Exit(1) - } - if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") - } - if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") - os.Exit(1) - } - if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") - os.Exit(1) - } - if err = (&databasev4.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") - os.Exit(1) - } - if err = (&observabilityv1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") - os.Exit(1) - } - if err = (&observabilityv4.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") - os.Exit(1) - } if err = (&databasev4.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") os.Exit(1) @@ -493,7 +406,21 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "DatabaseObserver") os.Exit(1) } - + if err = (&omlaicontroller.PrivateAiReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Config: mgr.GetConfig(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "PrivateAi") + os.Exit(1) + } + // nolint:goconst + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err = webhookomlaiv4.SetupPrivateAiWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "PrivateAi") + os.Exit(1) + } + } // +kubebuilder:scaffold:builder // Add index for PDB CR to enable mgr to cache PDBs diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 17557147..a76c401b 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -13427,6 +13427,305 @@ spec: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: privateais.omlai.oracle.com +spec: + group: omlai.oracle.com + names: + kind: PrivateAi + listKind: PrivateAiList + plural: privateais + singular: privateai + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.replicas + name: Replicas + type: number + - jsonPath: .status.apikey + name: ApiKey + type: string + - jsonPath: .status.podip + name: PodIP + type: string + - jsonPath: .status.loadbalancerip + name: LbIP + type: string + - jsonPath: .status.ReleaseUpdate + name: ReleaseUpdate + priority: 1 + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + envVars: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + isDebug: + type: boolean + isDeleteOraPvc: + type: boolean + isDownloadScripts: + type: boolean + isExternalSvc: + type: boolean + livenessCheckPeriod: + type: integer + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + type: object + type: array + paiAuthentication: + type: boolean + paiConfigFile: + properties: + mountLocation: + type: string + name: + type: string + type: object + paiEnableAuthentication: + type: boolean + paiHTTPEnabled: + type: boolean + paiHTTPPort: + format: int32 + type: integer + paiHTTPSEnabled: + type: boolean + paiHTTPSPort: + format: int32 + type: integer + paiImage: + type: string + paiImagePullSecret: + type: string + paiLBIP: + type: string + paiLBPort: + format: int32 + type: integer + paiLogLocation: + type: string + paiSecret: + properties: + mountLocation: + type: string + name: + type: string + type: object + paiService: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + svcType: + type: string + type: object + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + required: + - port + - protocol + - targetPort + type: object + type: array + pvcList: + additionalProperties: + type: string + type: object + readinessCheckPeriod: + type: integer + replicas: + format: int32 + type: integer + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + storageClass: + type: string + storageSizeInGb: + format: int32 + type: integer + type: object + status: + properties: + NodeIP: + type: string + apiKey: + type: string + certpem: + type: string + clusterIP: + type: string + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancerIP: + type: string + podIP: + type: string + releaseUpdate: + type: string + replicas: + type: integer + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert @@ -15296,77 +15595,6 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: oracle-database-operator - name: oracle-database-operator-database-oraclerestart-admin-role -rules: -- apiGroups: - - database.oracle.com - resources: - - oraclerestarts - verbs: - - '*' -- apiGroups: - - database.oracle.com - resources: - - oraclerestarts/status - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: oracle-database-operator - name: oracle-database-operator-database-oraclerestart-editor-role -rules: -- apiGroups: - - database.oracle.com - resources: - - oraclerestarts - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - oraclerestarts/status - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: oracle-database-operator - name: oracle-database-operator-database-oraclerestart-viewer-role -rules: -- apiGroups: - - database.oracle.com - resources: - - oraclerestarts - verbs: - - get - - list - - watch -- apiGroups: - - database.oracle.com - resources: - - oraclerestarts/status - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole metadata: name: oracle-database-operator-manager-role rules: @@ -15586,6 +15814,32 @@ rules: - get - patch - update +- apiGroups: + - omlai.oracle.com + resources: + - privateais + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - omlai.oracle.com + resources: + - privateais/finalizers + verbs: + - update +- apiGroups: + - omlai.oracle.com + resources: + - privateais/status + verbs: + - get + - patch + - update - apiGroups: - storage.k8s.io resources: @@ -15988,6 +16242,26 @@ webhooks: resources: - databaseobservers sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /mutate-omlai-oracle-com-v4-privateai + failurePolicy: Fail + name: mprivateai-v4.kb.io + rules: + - apiGroups: + - omlai.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - privateais + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -16367,6 +16641,26 @@ webhooks: resources: - databaseobservers sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-omlai-oracle-com-v4-privateai + failurePolicy: Fail + name: vprivateai-v4.kb.io + rules: + - apiGroups: + - omlai.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - privateais + sideEffects: None --- apiVersion: apps/v1 kind: Deployment From c64107459cac8f958847e6ce4ae0a71f4433ded8 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Mon, 28 Jul 2025 20:59:20 +0000 Subject: [PATCH 232/414] Updated webhooks for Observability --- .../v1/databaseobserver_webhook.go | 25 +++++++++++------- .../v1alpha1/databaseobserver_webhook.go | 25 +++++++++++------- .../v4/databaseobserver_webhook.go | 26 ++++++++++++------- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/apis/observability/v1/databaseobserver_webhook.go b/apis/observability/v1/databaseobserver_webhook.go index 05ec3290..1aff3d90 100644 --- a/apis/observability/v1/databaseobserver_webhook.go +++ b/apis/observability/v1/databaseobserver_webhook.go @@ -39,6 +39,7 @@ package v1 import ( + "context" "strings" dbcommons "github.com/oracle/oracle-database-operator/commons/database" @@ -68,6 +69,9 @@ const ( func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + For(&DatabaseObserver{}). + WithDefaulter(r). + WithValidator(r). Complete() } @@ -75,22 +79,23 @@ func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { //+kubebuilder:webhook:path=/mutate-observability-oracle-com-v1-databaseobserver,mutating=true,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,verbs=create;update,versions=v1,name=mdatabaseobserver.kb.io,admissionReviewVersions=v1 -var _ webhook.Defaulter = &DatabaseObserver{} +var _ webhook.CustomDefaulter = &DatabaseObserver{} -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *DatabaseObserver) Default() { +// Default implements webhook.CustomDefaulter so a webhook will be registered for the type +func (r *DatabaseObserver) Default(ctx context.Context, obj runtime.Object) error { databaseobserverlog.Info("default", "name", r.Name) // TODO(user): fill in your defaulting logic. + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:verbs=create;update,path=/validate-observability-oracle-com-v1-databaseobserver,mutating=false,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,versions=v1,name=vdatabaseobserver.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &DatabaseObserver{} +var _ webhook.CustomValidator = &DatabaseObserver{} -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { databaseobserverlog.Info("validate create", "name", r.Name) var e field.ErrorList @@ -160,8 +165,8 @@ func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { } -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { databaseobserverlog.Info("validate update", "name", r.Name) var e field.ErrorList @@ -178,8 +183,8 @@ func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warning return nil, nil } -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *DatabaseObserver) ValidateDelete() (admission.Warnings, error) { +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { databaseobserverlog.Info("validate delete", "name", r.Name) return nil, nil diff --git a/apis/observability/v1alpha1/databaseobserver_webhook.go b/apis/observability/v1alpha1/databaseobserver_webhook.go index 70fdec0b..b6c2c2fe 100644 --- a/apis/observability/v1alpha1/databaseobserver_webhook.go +++ b/apis/observability/v1alpha1/databaseobserver_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + "context" "strings" dbcommons "github.com/oracle/oracle-database-operator/commons/database" @@ -68,6 +69,9 @@ const ( func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + For(&DatabaseObserver{}). + WithDefaulter(r). + WithValidator(r). Complete() } @@ -75,22 +79,23 @@ func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { //+kubebuilder:webhook:path=/mutate-observability-oracle-com-v1alpha1-databaseobserver,mutating=true,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,verbs=create;update,versions=v1alpha1,name=mdatabaseobserver.kb.io,admissionReviewVersions=v1 -var _ webhook.Defaulter = &DatabaseObserver{} +var _ webhook.CustomDefaulter = &DatabaseObserver{} -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *DatabaseObserver) Default() { +// Default implements webhook.CustomDefaulter so a webhook will be registered for the type +func (r *DatabaseObserver) Default(ctx context.Context, obj runtime.Object) error { databaseobserverlog.Info("default", "name", r.Name) // TODO(user): fill in your defaulting logic. + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:verbs=create;update,path=/validate-observability-oracle-com-v1alpha1-databaseobserver,mutating=false,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,versions=v1alpha1,name=vdatabaseobserver.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &DatabaseObserver{} +var _ webhook.CustomValidator = &DatabaseObserver{} -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { databaseobserverlog.Info("validate create", "name", r.Name) var e field.ErrorList @@ -160,8 +165,8 @@ func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { } -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { databaseobserverlog.Info("validate update", "name", r.Name) var e field.ErrorList @@ -178,8 +183,8 @@ func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warning return nil, nil } -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *DatabaseObserver) ValidateDelete() (admission.Warnings, error) { +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { databaseobserverlog.Info("validate delete", "name", r.Name) return nil, nil diff --git a/apis/observability/v4/databaseobserver_webhook.go b/apis/observability/v4/databaseobserver_webhook.go index e25902ad..94f906cd 100644 --- a/apis/observability/v4/databaseobserver_webhook.go +++ b/apis/observability/v4/databaseobserver_webhook.go @@ -39,6 +39,7 @@ package v4 import ( + "context" "strings" dbcommons "github.com/oracle/oracle-database-operator/commons/database" @@ -68,26 +69,31 @@ const ( func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + For(&DatabaseObserver{}). + WithDefaulter(r). + WithValidator(r). Complete() } //+kubebuilder:webhook:path=/mutate-observability-oracle-com-v4-databaseobserver,mutating=true,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,verbs=create;update,versions=v4,name=mdatabaseobserver.kb.io,admissionReviewVersions=v1 -var _ webhook.Defaulter = &DatabaseObserver{} +var _ webhook.CustomDefaulter = &DatabaseObserver{} // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *DatabaseObserver) Default() { +// Default implements webhook.CustomDefaulter so a webhook will be registered for the type +func (r *DatabaseObserver) Default(ctx context.Context, obj runtime.Object) error { databaseobserverlog.Info("default", "name", r.Name) + + return nil } //+kubebuilder:webhook:verbs=create;update,path=/validate-observability-oracle-com-v4-databaseobserver,mutating=false,sideEffects=none,failurePolicy=fail,groups=observability.oracle.com,resources=databaseobservers,versions=v4,name=vdatabaseobserver.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &DatabaseObserver{} +var _ webhook.CustomValidator = &DatabaseObserver{} -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { databaseobserverlog.Info("validate create", "name", r.Name) var e field.ErrorList @@ -157,8 +163,8 @@ func (r *DatabaseObserver) ValidateCreate() (admission.Warnings, error) { } -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { databaseobserverlog.Info("validate update", "name", r.Name) var e field.ErrorList @@ -175,8 +181,8 @@ func (r *DatabaseObserver) ValidateUpdate(old runtime.Object) (admission.Warning return nil, nil } -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *DatabaseObserver) ValidateDelete() (admission.Warnings, error) { +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type +func (r *DatabaseObserver) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { databaseobserverlog.Info("validate delete", "name", r.Name) return nil, nil From 75ecddb8938437d60030363bce9cc8735d302451 Mon Sep 17 00:00:00 2001 From: "geela.rajahimansh@oracle.com" Date: Tue, 29 Jul 2025 09:45:33 +0000 Subject: [PATCH 233/414] few changes in replica --- commons/omlai/aibuilder.go | 90 +++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/commons/omlai/aibuilder.go b/commons/omlai/aibuilder.go index 0b1e7d3e..316ad7b1 100644 --- a/commons/omlai/aibuilder.go +++ b/commons/omlai/aibuilder.go @@ -384,58 +384,48 @@ func ManageReplicas( req ctrl.Request, logger logr.Logger, ) (ctrl.Result, error) { - var msg string var desired int32 = 1 - if instance.Spec.Replicas > 0 { desired = instance.Spec.Replicas } current := *deploy.Spec.Replicas - //var isUpdate bool - if deploy.Spec.Replicas != nil && current != desired { - msg = "Current Deployment replicas do not match configured PrivateAI Replicas." - LogMessages("DEBUG", msg, nil, instance, logger) - //isUpdate = true - //isUpdate = true + LogMessages("DEBUG", "Deployment replicas mismatch. Updating deployment...", nil, instance, logger) + + newDeploy := BuildDeploySetForPrivateAI(instance) + newDeploy.Spec.Replicas = &desired + err := kClient.Update(context.Background(), newDeploy) + if err != nil { + LogMessages("ERROR", "Failed to update Deployment with new replica count", err, instance, logger) + return ctrl.Result{}, err + } } + // Re-mark pods based on diff diff := current - desired - if diff > 0 && len(podList.Items) > 0 { - count := int32(0) - for i := range podList.Items { - if count >= diff { - break - } - pod := &podList.Items[i] - // var touchFileCmd []string = []string{"/bin/touch /tmp/unhealthy"} - var touchFileCmd []string = []string{"/bin/touch", "/tmp/unhealthy"} - - _, err := ExecCommand( - r, - Config, - pod.Name, - pod.Namespace, - pod.Spec.Containers[0].Name, - ctx, - req, - false, - touchFileCmd, - ) + for i := range podList.Items { + pod := &podList.Items[i] + + if diff > 0 { + // Mark pod as unhealthy + touchCmd := []string{"/bin/touch", "/tmp/unhealthy"} + _, err := ExecCommand(r, Config, pod.Name, pod.Namespace, pod.Spec.Containers[0].Name, ctx, req, false, touchCmd) if err != nil { - LogMessages("ERROR", "Failed to exec command", err, instance, logger) + LogMessages("ERROR", "Failed to mark pod as unhealthy", err, instance, logger) return ctrl.Result{}, err } - err = kClient.Update(context.Background(), pod) + diff-- + } else { + // Heal pod if previously unready + removeCmd := []string{"rm", "-f", "/tmp/unhealthy"} + _, err := ExecCommand(r, Config, pod.Name, pod.Namespace, pod.Spec.Containers[0].Name, ctx, req, false, removeCmd) if err != nil { - msg = "Failed to update Pod Label: " + pod.Name + LogMessages("ERROR", "Failed to heal pod back to ready state", err, instance, logger) return ctrl.Result{}, err } - count++ - } } @@ -443,29 +433,31 @@ func ManageReplicas( } // Update Section -func UpdateDeploySetForPrivateAI(instance *privateaiv4.PrivateAi, paiSpec privateaiv4.PrivateAiSpec, kClient client.Client, Config *rest.Config, deploy *appsv1.Deployment, pod *corev1.Pod, logger logr.Logger) (ctrl.Result, error) { - //var msg string - - // var isUpdate bool - +func UpdateDeploySetForPrivateAI( + instance *privateaiv4.PrivateAi, + paiSpec privateaiv4.PrivateAiSpec, + kClient client.Client, + Config *rest.Config, + deploy *appsv1.Deployment, + pod *corev1.Pod, + logger logr.Logger, +) (ctrl.Result, error) { for i := range pod.Spec.Containers { if pod.Spec.Containers[i].Name == deploy.Name { contRes := pod.Spec.Containers[i].Resources paiRes := paiSpec.Resources if !reflect.DeepEqual(contRes, paiRes) { - // isUpdate = true - LogMessages("DEBUG", "Container ", nil, instance, logger) + LogMessages("DEBUG", "Container resources have changed. Updating deployment...", nil, instance, logger) + + // Update the deployment with new spec + err := kClient.Update(context.Background(), BuildDeploySetForPrivateAI(instance)) + if err != nil { + LogMessages("ERROR", "Failed to update deployment with new spec", err, instance, logger) + return ctrl.Result{}, err + } } } } - // if isUpdate { - // err := kClient.Update(context.Background(), BuildDeploySetForPrivateAI(instance)) - // if err != nil { - // //msg = "Failed to update PrivateAI Deployment: " + deploy.Name - // return ctrl.Result{}, err - // } - // } - return ctrl.Result{}, nil } From 5aea1bd645233d0cf8017e0aaf944108a2c5a671 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Tue, 29 Jul 2025 16:15:23 +0000 Subject: [PATCH 234/414] ons support in oracle restart --- apis/database/v4/dbcssystem_types.go | 4 +- apis/database/v4/oraclerestart_types.go | 2 + apis/database/v4/zz_generated.deepcopy.go | 10 ++ commons/oraclerestart/oraclerestartcommon.go | 2 +- .../oraclerestart/oraclerestartconstants.go | 16 +- commons/oraclerestart/oraclerestartprov.go | 20 +++ .../database.oracle.com_dbcssystems.yaml | 3 - .../database.oracle.com_oraclerestarts.yaml | 12 ++ config/manager/kustomization.yaml | 2 +- .../database/oraclerestart_controller.go | 111 +++++++----- docs/oraclerestart/README.md | 7 +- .../oraclerestart_prov_onsport.yaml | 105 ++++++++++++ .../provisioning/orestart_ons_object.txt | 162 ++++++++++++++++++ ...provisioning_oracle_restart_db_nodeport.md | 5 +- .../provisioning_oracle_restart_db_onsport.md | 83 +++++++++ oracle-database-operator.yaml | 17 +- 16 files changed, 494 insertions(+), 67 deletions(-) create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml create mode 100644 docs/oraclerestart/provisioning/orestart_ons_object.txt create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md diff --git a/apis/database/v4/dbcssystem_types.go b/apis/database/v4/dbcssystem_types.go index 29535b6a..a2de4a8a 100644 --- a/apis/database/v4/dbcssystem_types.go +++ b/apis/database/v4/dbcssystem_types.go @@ -194,8 +194,8 @@ type DbStatus struct { } type DbWorkrequests struct { - OperationType *string `json:"operationType,omitmpty"` - OperationId *string `json:"operationId,omitemty"` + OperationType *string `json:"operationType,omitempty"` + OperationId *string `json:"operationId,omitempty"` PercentComplete string `json:"percentComplete,omitempty"` TimeAccepted string `json:"timeAccepted,omitempty"` TimeStarted string `json:"timeStarted,omitempty"` diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 57601030..e634212f 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -151,6 +151,8 @@ type OracleRestartInstDetailSpec struct { NodePortSvc []OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least EnvFile string `json:"envFile,omitempty"` + OnsTargetPort *int32 `json:"onsTargetPort,omitempty"` // Port that will be exposed on the service. + OnsLocalPort *int32 `json:"onsLocalPort,omitempty"` // Port that will be exposed on the service. } // Responsefile Name diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index a42aa607..52ad8bc6 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -3155,6 +3155,16 @@ func (in *OracleRestartInstDetailSpec) DeepCopyInto(out *OracleRestartInstDetail *out = make([]OracleRestartPortMapping, len(*in)) copy(*out, *in) } + if in.OnsTargetPort != nil { + in, out := &in.OnsTargetPort, &out.OnsTargetPort + *out = new(int32) + **out = **in + } + if in.OnsLocalPort != nil { + in, out := &in.OnsLocalPort, &out.OnsLocalPort + *out = new(int32) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartInstDetailSpec. diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 1093683c..071cbfce 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -1216,7 +1216,7 @@ func ValidateNetInterface(net string, instance *oraclerestart.OracleRestart, rsp if net != "" { if !strings.Contains(rspNetData, net) { - err = fmt.Errorf("Error occurred during retreiving network card detail from grid responsefile: %s", "The key does not exist") + err = fmt.Errorf("error occurred during retreiving network card detail from grid responsefile: %s", "The key does not exist") } } diff --git a/commons/oraclerestart/oraclerestartconstants.go b/commons/oraclerestart/oraclerestartconstants.go index 2e4ea565..b91d9cbf 100644 --- a/commons/oraclerestart/oraclerestartconstants.go +++ b/commons/oraclerestart/oraclerestartconstants.go @@ -100,14 +100,14 @@ func getDBServiceStatus(dbhome string, dbname string, svcname string) []string { return oraRacVersionCmd } -func modifyDBServiceStatus(dbhome string, dbname string, svcname string) []string { - oraDBUser := getOraDbUser() - - //oraGiUser := getOraGiUser() - svcModifyCmd := "su " + oraDBUser + " -c \"" + oraDBUser + " ;srvctl modify service -s " + svcname + " -d " + dbname + " " + dbhome + "\"" - var oraModifySvcCmd = []string{svcModifyCmd} - return oraModifySvcCmd -} +// func modifyDBServiceStatus(dbhome string, dbname string, svcname string) []string { +// oraDBUser := getOraDbUser() + +// //oraGiUser := getOraGiUser() +// svcModifyCmd := "su " + oraDBUser + " -c \"" + oraDBUser + " ;srvctl modify service -s " + svcname + " -d " + dbname + " " + dbhome + "\"" +// var oraModifySvcCmd = []string{svcModifyCmd} +// return oraModifySvcCmd +// } func getAsmDiskgroupCmd() []string { oraScriptMount1 := getOraScriptMount() diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 10b0ef87..4afea226 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -696,6 +696,8 @@ func BuildExternalServiceDefForOracleRestart(instance *oraclerestart.OracleResta ObjectMeta: buildSvcObjectMetaForOracleRestart(instance, index, OracleRestartSpex, opType), Spec: corev1.ServiceSpec{}, } + var exSvcPorts oraclerestart.OracleRestartPortMapping + var exSvc oraclerestart.OracleRestartNodePortSvc if opType == "nodeport" { // service.Spec.ClusterIP = string(corev1.ServiceTypeNodePort) @@ -707,6 +709,20 @@ func BuildExternalServiceDefForOracleRestart(instance *oraclerestart.OracleResta service.Spec.Selector = getSvcLabelsForOracleRestart(0, OracleRestartSpex) } } + if opType == "onssvc" { + //exSvc.SvcName = OracleRestartSpex.Name + "-0-ons" + exSvc.SvcType = svctype + exSvcPorts.NodePort = *OracleRestartSpex.OnsTargetPort + if OracleRestartSpex.OnsLocalPort != nil { + exSvcPorts.Port = *OracleRestartSpex.OnsLocalPort + } else { + exSvcPorts.Port = 6200 + } + + exSvc.PortMappings = append(exSvc.PortMappings, exSvcPorts) + service.Spec.Selector = getSvcLabelsForOracleRestart(0, OracleRestartSpex) + npSvc = exSvc + } service.Spec.Ports = buildOracleRestartSvcPortsDef(npSvc) @@ -957,6 +973,10 @@ func flattenDisksBySize(oraclerestartSpec *oraclerestart.OracleRestartSpec) []st } func CreateServiceAccountIfNotExists(instance *oraclerestart.OracleRestart, kClient client.Client) error { + if instance.Spec.SrvAccountName == "" { + return nil + } + ServiceAccountName := instance.Spec.SrvAccountName if ServiceAccountName == "" { ServiceAccountName = "default" diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index 7834a8bf..8dc0f1ea 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -882,9 +882,6 @@ spec: type: string timeStarted: type: string - required: - - operationId - - operationType type: object type: array required: diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index 3a213643..f9aecea2 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -290,6 +290,12 @@ spec: type: string type: object type: array + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer portMappings: items: properties: @@ -1033,6 +1039,12 @@ spec: type: string type: object type: array + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer portMappings: items: properties: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 74303bea..2726acb4 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: basedb-operator-sa + newTag: restart-operator-sa diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 9b92b8bc..f3dafb34 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -113,6 +113,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques // var svcType string var nilErr error = nil var oracleRestartInst oraclerestartdb.OracleRestartInstDetailSpec + resultNq := ctrl.Result{Requeue: false} resultQ := ctrl.Result{Requeue: true, RequeueAfter: 60 * time.Second} @@ -236,7 +237,13 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques return result, err } } + var svcType string + if oracleRestart.Spec.ExternalSvcType != nil { + svcType = *oracleRestart.Spec.ExternalSvcType + } else { + svcType = "nodeport" + } result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildServiceDefForOracleRestart(oracleRestart, 0, oracleRestart.Spec.InstDetails, "local")) if err != nil { result = resultNq @@ -245,7 +252,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques if oracleRestartInst.NodePortSvc != nil { for index := range oracleRestartInst.NodePortSvc { - result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildExternalServiceDefForOracleRestart(oracleRestart, int32(index), oracleRestart.Spec.InstDetails, "nodeport", "nodeport")) + result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildExternalServiceDefForOracleRestart(oracleRestart, int32(index), oracleRestart.Spec.InstDetails, svcType, "nodeport")) if err != nil { result = resultNq return result, err @@ -254,6 +261,15 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } } + if oracleRestartInst.OnsTargetPort != nil { + result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildExternalServiceDefForOracleRestart(oracleRestart, 0, oracleRestart.Spec.InstDetails, svcType, "onssvc")) + if err != nil { + result = resultNq + return result, err + } + + } + r.ensureAsmStorageStatus(oracleRestart) isNewSetup := true for _, diskgroup := range oracleRestart.Status.AsmDetails.Diskgroup { @@ -459,6 +475,29 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques return result, err } + if oracleRestartInst.OnsTargetPort != nil { + + OraRestartSpex := oracleRestart.Spec.InstDetails + orestartSfSet, err := oraclerestartcommon.CheckSfset(OraRestartSpex.Name, oracleRestart, r.Client) + if err != nil { + //msg := "Unable to find Oracle Restart statefulset " + oraclerestartcommon.GetFmtStr(OraRestartSpex.Name) + "." + r.updateOracleRestartInstStatus(oracleRestart, ctx, req, OraRestartSpex, string(oraclerestartdb.StatefulSetNotFound), r.Client, false) + return ctrl.Result{}, err + } + + podList, err := oraclerestartcommon.GetPodList(orestartSfSet.Name, oracleRestart, r.Client, oracleRestart.Spec.InstDetails) + if err != nil { + r.Log.Error(err, "Failed to list pods") + return ctrl.Result{}, err + } + err = r.updateONS(ctx, podList, oracleRestart, "start") + if err != nil { + return ctrl.Result{}, err + } + r.Log.Info("ONS Started") + + } + completed = true // // Update the current spec after successful reconciliation if err = r.SetCurrentSpec(ctx, oracleRestart, req); err != nil { @@ -576,7 +615,7 @@ func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oracleres if err1 == nil { _ = r.updateoraclerestartdbTopologyStatus(oracleRestart, ctx, req, podNames, nodeDetails) } else { - r.Log.Info("Error during RAC cluster update", "err1", err1) + r.Log.Info("Error during Oracle Restart update", "err1", err1) } errMsg := func() string { @@ -1562,7 +1601,7 @@ func (r *OracleRestartReconciler) validateOracleRestartInst(oracleRestart *oracl orestartSfSet, err = oraclerestartcommon.CheckSfset(OraRestartSpex.Name, oracleRestart, r.Client) if err != nil { - //msg := "Unable to find Rac statefulset " + oraclerestartcommon.GetFmtStr(OraRestartSpex.Name) + "." + //msg := "Unable to find Oracle Restart statefulset " + oraclerestartcommon.GetFmtStr(OraRestartSpex.Name) + "." //oraclerestartcommon.LogMessages("INFO", msg, nil, instance, r.Log) r.updateOracleRestartInstStatus(oracleRestart, ctx, req, OraRestartSpex, string(oraclerestartdb.StatefulSetNotFound), r.Client, false) return orestartSfSet, orestartPod, err @@ -2405,43 +2444,6 @@ func (r *OracleRestartReconciler) addDisks(ctx context.Context, podList *corev1. return nil } -// // Function to delete disks -// func (r *OracleRestartReconciler) deleteDisks(ctx context.Context, podList *corev1.PodList, instance *oraclerestartdb.OracleRestart, deviceListName string, deviceList []string) error { -// reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) -// diskGroupName := getDiskGroupName(deviceListName, instance) -// // Remove '+' prefix if present -// if strings.HasPrefix(diskGroupName, "+") { -// diskGroupName = strings.TrimPrefix(diskGroupName, "+") -// } - -// for _, pod := range podList.Items { -// podName := pod.Name - -// // Check if the disk group exists before trying to delete disks -// exists, err := r.diskGroupExists(podName, diskGroupName, r.kubeClient, r.kubeConfig, instance, reqLogger) -// if err != nil { -// reqLogger.Error(err, "Failed to check if disk group exists", "Pod.Name", podName, "DiskGroup", diskGroupName) -// return err -// } -// if !exists { -// err = fmt.Errorf("disk group %s does not exist", diskGroupName) -// reqLogger.Error(err, "Disk group does not exist", "Pod.Name", podName, "DiskGroup", diskGroupName) -// return err -// } - -// for _, disk := range deviceList { -// cmd := fmt.Sprintf("python3 /opt/scripts/startup/scripts/main.py --updateasmdevices=\"diskname=%s;diskgroup=%s;processtype=deletion\"", disk, diskGroupName) -// reqLogger.Info("Executing command to delete disk", "Pod.Name", podName, "Command", cmd) -// stdout, stderr, err := oraclerestartcommon.ExecCommand(podName, []string{"bash", "-c", cmd}, r.kubeClient, r.kubeConfig, instance, reqLogger) -// if err != nil { -// reqLogger.Error(err, "Failed to execute command", "Pod.Name", podName, "Command", cmd, "Stdout", stdout, "Stderr", stderr) -// return err -// } -// } -// } -// return nil -// } - // Function to check DaemonSet status with retry, timeout, and log analysis func checkDaemonSetStatus(ctx context.Context, r *OracleRestartReconciler, oracleRestart *oraclerestartdb.OracleRestart) (bool, error) { timeout := time.After(2 * time.Minute) @@ -2772,9 +2774,6 @@ waitLoop: if isLast && asmAutoUpdate { // last iteration - // update status column with configParams - // Addition fo Disk Execution - // Check each new disk against CrsAsmDeviceList, DbAsmDeviceList, RecoAsmDeviceList, RedoAsmDeviceList deviceDg := "" for _, disk := range addedAsmDisks { if isDiskInDeviceList(disk, oracleRestart.Spec.ConfigParams.CrsAsmDeviceList) { @@ -3372,3 +3371,29 @@ func (r *OracleRestartReconciler) SetCurrentSpec(ctx context.Context, oracleRest r.Log.Info("RAC Object annotations updated with current spec annotation") return nil } + +func (r *OracleRestartReconciler) updateONS(ctx context.Context, podList *corev1.PodList, instance *oraclerestartdb.OracleRestart, onsState string) error { + reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) + + for _, pod := range podList.Items { + podName := pod.Name + + cmd := fmt.Sprintf("python3 /opt/scripts/startup/scripts/main.py --ons=%s", onsState) + reqLogger.Info("Executing command to update ONS", "Pod.Name", podName, "Command", cmd) + + stdout, stderr, err := oraclerestartcommon.ExecCommand( + podName, + []string{"bash", "-c", cmd}, + r.kubeClient, + r.kubeConfig, + instance, + reqLogger, + ) + if err != nil { + instance.Spec.IsFailed = true + reqLogger.Error(err, "Failed to execute command", "Pod.Name", podName, "Command", cmd, "Stdout", stdout, "Stderr", stderr) + return err + } + } + return nil +} diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md index 2eefadca..814413be 100644 --- a/docs/oraclerestart/README.md +++ b/docs/oraclerestart/README.md @@ -43,9 +43,10 @@ Deploy Oracle Restart Database YAML files using Kubernetes Cluster on your Oracl [1. Provisioning an Oracle Restart Database](./provisioning/provisioning_oracle_restart_db.md) [2. Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) -[3. Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning/provisioning_oracle_restart_db_rupatch.md) -[4. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) -[4. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) +[3. Provisioning an Oracle Restart Database with OnsPort Service](./provisioning/provisioning_oracle_restart_db_onsport.md) +[4. Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning/provisioning_oracle_restart_db_rupatch.md) +[5. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) +[6. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) ## Connecting to Oracle Restart Database diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml new file mode 100644 index 00000000..72235212 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml @@ -0,0 +1,105 @@ +# API version for the custom resource definition +apiVersion: database.oracle.com/v4 + +# Kind of resource being defined +kind: OracleRestart + +# Metadata section includes the name and namespace of the resource +metadata: + # Name of this OracleRestart resource instance + name: oraclerestart-sample + # Kubernetes namespace where this resource resides + namespace: orestart + +# Specification for the OracleRestart resource +spec: + # Details about the Oracle instance + instDetails: + # Name of the Oracle instance + name: dbmc1 + # Path where Oracle software will be installed on the host + hostSwLocation: /scratch/orestart/ + # Target port for Oracle Notification Services (ONS) + onsTargetPort: 30200 + # List of worker node IPs where instance will be deployed + workerNode: + - 10.0.10.108 + # NodePort service configuration + nodePortSvc: + - svcType: nodeport # Type of service (NodePort) + name: dbmc1 # Name of the NodePort service + # Port mappings between container and host + portMappings: + - port: 1521 # Internal port for Oracle DB + targetPort: 1521 # Target port inside the container + protocol: TCP # Protocol used + nodePort: 30007 # Exposed NodePort on host + + # ASM (Automatic Storage Management) disk configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Total size of disk group in GB + diskNames: # List of physical disk paths to be used + - /dev/oracleoci/oraclevdd + - /dev/oracleoci/oraclevde + + # SSH key secret references used for host access + sshKeySecret: + name: ssh-key-secret # Name of the Kubernetes secret + privKeySecretName: ssh-privkey # Private SSH key secret + pubKeySecretName: ssh-pubkey # Public SSH key secret + + # Database user credentials secret + dbSecret: + name: db-user-pass-pkutl # Name of secret containing DB credentials + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Container image for Oracle Restart + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0729 + + # Policy to always pull the image when starting + imagePullPolicy: Always + + # Details of the database service + serviceDetails: + name: soepdb # Name of the database service + + # Kubernetes resource requests and limits + resources: + requests: + memory: "16Gi" # Memory requested by container + cpu: "2" # CPUs requested by container + limits: + memory: "16Gi" # Maximum memory allowed + cpu: "2" # Maximum CPUs allowed + + # OS-level kernel parameters to be set in the container + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" # Shared memory pages + - name: kernel.sem + value: "250 32000 100 128" # Semaphore settings + - name: kernel.shmmax + value: "8589934592" # Max shared memory segment (8GB) + - name: kernel.shmmni + value: "4096" # Number of shared memory segments + + # Configuration parameters for Oracle software + configParams: + gridHome: "/u01/app/19c/grid" # Path to Grid Infrastructure home + gridBase: "/u01/app/grid" # Base directory for Grid Infrastructure + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle Database home + dbBase: "/u01/app/oracle" # Oracle Database base directory + crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # ASM device list + inventory: "/u01/app/oraInventory" # Oracle inventory location + gridSwZipFile: "grid_home.zip" # Grid software zip filename + dbSwZipFile: "db_home.zip" # DB software zip filename + sgaSize: "3G" # Size of SGA (System Global Area) + pgaSize: "1G" # Size of PGA (Program Global Area) + processes: 2000 # Maximum number of DB processes + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Name of the Oracle database + hostSwStageLocation: "/scratch/software/19c/19.3.0/" # Location to stage installation software + ruPatchLocation: /scratch/software/19c/19.28/ # Location for Release Update patch \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_ons_object.txt b/docs/oraclerestart/provisioning/orestart_ons_object.txt new file mode 100644 index 00000000..254adb43 --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_ons_object.txt @@ -0,0 +1,162 @@ +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"nodePortSvc":[{"name":"dbmc1","svcType"... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-07-29T15:21:49Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 12701496 + UID: 5c0506e6-3674-4284-b828-d462b8c17b64 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/19c/19.3.0/ + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Ru Patch Location: /scratch/software/19c/19.28/ + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0729 + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1 + Svc Type: nodeport + Ons Target Port: 30200 + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.108 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: NOTAVAILABLE + Pod State: PODAVAILABLE + Cluster State: NOT HEALTHY + Mounted Devices: + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + State: FAILED + Asm Details: + Diskgroup: + Disks: + /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Name: DATA + Redundancy: EXTERN + Client Etc Host: + 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local + Conditions: + Last Transition Time: 2025-07-29T16:11:50Z + Message: oracle restart database is in a restricted state: FAILED + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Config Params: + Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.3.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: FAILED +Events: + +orestart/ $ kubectl get all -n orestart +NAME READY STATUS RESTARTS AGE +pod/dbmc1-0 1/1 Running 0 52m + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/dbmc1 NodePort 10.96.177.66 1521:30007/TCP 52m +service/dbmc1-0 ClusterIP None 52m +service/dbmc1-0-ons NodePort 10.96.146.166 6200:30200/TCP 5h5m + +NAME READY AGE +statefulset.apps/dbmc1 1/1 52m \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md index 5b1f0920..96560cdc 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md @@ -1,8 +1,9 @@ # Provisioning an Oracle Restart Database with NodePort Service -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is -generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the controller based on input parameters specified in the .yaml file. +In this use case, we are deploying the Oracle Restart with Node Port Service. +A node port exposes the service on a static port on the node IP address and NodePorts are in the 30000-32767 range by default. This example uses `oraclerestart_prov_nodeports.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: * 1 Node Oracle Restart diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md new file mode 100644 index 00000000..ef58f5ae --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md @@ -0,0 +1,83 @@ +# Provisioning an Oracle Restart Database with NodePort Service + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. +The responsefile is generated by the controller based on input parameters specified in the .yaml file. In this use case, we are deploying the Oracle Restart with Node Port Service for the ONS. + +This example uses `oraclerestart_prov_onsport.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +In this example, + * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). + * When you are building the image yourself, update the image value in the `oraclerestart_prov_onsport.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + + +Use the file: [oraclerestart_prov_onsport.yaml](./oraclerestart_prov_onsport.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_onsport.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_onsport.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_ons_object.txt) +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +In this example, + * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). + * When you are building the image yourself, update the image value in the `oraclerestart_prov_onsport.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + + +Use the file: [oraclerestart_prov_onsport.yaml](./oraclerestart_prov_onsport.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_onsport.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_onsport.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_nodeport_object.txt) +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index a76c401b..0bc7eba3 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -9877,9 +9877,6 @@ spec: type: string timeStarted: type: string - required: - - operationId - - operationType type: object type: array required: @@ -10808,6 +10805,12 @@ spec: type: string type: object type: array + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer portMappings: items: properties: @@ -11551,6 +11554,12 @@ spec: type: string type: object type: array + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer portMappings: items: properties: @@ -16687,7 +16696,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:restart-operator-sa imagePullPolicy: Always name: manager ports: From bcd3460674bc58405ff1f50a7467a204db9545a3 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Wed, 30 Jul 2025 10:50:46 +0000 Subject: [PATCH 235/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 31 +++--- commons/oraclerestart/oraclerestartprov.go | 121 +++++++++++++++------ 2 files changed, 105 insertions(+), 47 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index e634212f..c6b8457a 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -138,21 +138,22 @@ type InitParams struct { } type OracleRestartInstDetailSpec struct { - Name string `json:"name"` - HostSwLocation string `json:"hostSwLocation,omitempty"` - WorkerNode []string `json:"workerNode,omitempty"` - EnvVars []corev1.EnvVar `json:"envVars,omitempty"` //Optional Env variables for Shards - Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` //Optional resource requiremen - Label string `json:"label,omitempty"` - IsDelete string `json:"isDelete,omitempty"` - IsForceDelete string `json:"isForceDelete,omitempty"` - IsKeepPVC string `json:"isKeepPVC,omitempty"` - PvcName map[string]string `json:"pvcName,omitempty"` - NodePortSvc []OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if - PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least - EnvFile string `json:"envFile,omitempty"` - OnsTargetPort *int32 `json:"onsTargetPort,omitempty"` // Port that will be exposed on the service. - OnsLocalPort *int32 `json:"onsLocalPort,omitempty"` // Port that will be exposed on the service. + Name string `json:"name"` + HostSwLocation string `json:"hostSwLocation,omitempty"` + SwLocStorageSizeInGb int `json:"swLocStorageSizeInGb,omitempty"` + WorkerNode []string `json:"workerNode,omitempty"` + EnvVars []corev1.EnvVar `json:"envVars,omitempty"` //Optional Env variables for Shards + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` //Optional resource requiremen + Label string `json:"label,omitempty"` + IsDelete string `json:"isDelete,omitempty"` + IsForceDelete string `json:"isForceDelete,omitempty"` + IsKeepPVC string `json:"isKeepPVC,omitempty"` + PvcName map[string]string `json:"pvcName,omitempty"` + NodePortSvc []OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if + PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least + EnvFile string `json:"envFile,omitempty"` + OnsTargetPort *int32 `json:"onsTargetPort,omitempty"` // Port that will be exposed on the service. + OnsLocalPort *int32 `json:"onsLocalPort,omitempty"` // Port that will be exposed on the service. } // Responsefile Name diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 4afea226..1612ff3e 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -44,6 +44,8 @@ import ( "strconv" "strings" + "path/filepath" + "github.com/go-logr/logr" oraclerestart "github.com/oracle/oracle-database-operator/apis/database/v4" utils "github.com/oracle/oracle-database-operator/commons/oraclerestart/utils" @@ -138,17 +140,20 @@ func buildStatefulSpecForOracleRestart( Spec: *podSpec, // dereference after modification }, } - // Add volume claim templates if a storage class is specified if len(instance.Spec.StorageClass) != 0 && !asmPvcsExist(instance, kClient) { - sfsetspec.VolumeClaimTemplates = VolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex) + sfsetspec.VolumeClaimTemplates = ASMVolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex) } + if len(instance.Spec.StorageClass) != 0 && len(instance.Spec.InstDetails.HostSwLocation) == 0 { + sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, SwVolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex)) + } // Add annotations to the Pod template // sfsetspec.Template.Annotations = generateNetworkDetails(instance, OracleRestartSpex, kClient) return sfsetspec } + func asmPvcsExist(instance *oraclerestart.OracleRestart, kClient client.Client) bool { for i := range instance.Spec.AsmStorageDetails.DisksBySize { pvcName := GetAsmPvcName(i, instance.Name) @@ -310,18 +315,24 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac if len(OracleRestartSpex.HostSwLocation) != 0 { result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: OracleRestartSpex.HostSwLocation}}}) + } else { + if instance.Spec.StorageClass != "" { + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OracleRestartSpex.Name + "-oradata-sw-vol-pvc"}}}) + } } if instance.Spec.ConfigParams != nil && instance.Spec.ConfigParams.HostSwStageLocation != "" { - if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.HostSwStageLocation]; !exists { - result = append(result, corev1.Volume{ - Name: OracleRestartSpex.Name + "-oradata-swstage-vol", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: instance.Spec.ConfigParams.HostSwStageLocation, + if !filepath.IsAbs(instance.Spec.ConfigParams.HostSwStageLocation) { + if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.HostSwStageLocation]; !exists { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-swstage-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.HostSwStageLocation, + }, }, - }, - }) + }) + } } } @@ -332,29 +343,33 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac } if instance.Spec.ConfigParams != nil { - if instance.Spec.ConfigParams.RuPatchLocation != "" { - if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.RuPatchLocation]; !exists { - result = append(result, corev1.Volume{ - Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: instance.Spec.ConfigParams.RuPatchLocation, + if !filepath.IsAbs(instance.Spec.ConfigParams.RuPatchLocation) { + if instance.Spec.ConfigParams.RuPatchLocation != "" { + if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.RuPatchLocation]; !exists { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.RuPatchLocation, + }, }, - }, - }) + }) + } } } if instance.Spec.ConfigParams.OPatchLocation != "" { - if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.OPatchLocation]; !exists { - result = append(result, corev1.Volume{ - Name: OracleRestartSpex.Name + "-oradata-opatch-vol", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: instance.Spec.ConfigParams.OPatchLocation, + if !filepath.IsAbs(instance.Spec.ConfigParams.OPatchLocation) { + if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.OPatchLocation]; !exists { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-opatch-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.OPatchLocation, + }, }, - }, - }) + }) + } } } } @@ -548,33 +563,50 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, } else { fmt.Println("No Location is passed for the software storage in" + OracleRestartSpex.Name) } - + var mountLoc string + if filepath.IsAbs(instance.Spec.ConfigParams.HostSwStageLocation) { + mountLoc = instance.Spec.ConfigParams.HostSwStageLocation + } else { + mountLoc = utils.OraSwStageLocation + } // Check if ConfigParams is not nil if instance.Spec.ConfigParams != nil { // Check if HostSwStageLocation is provided in ConfigParams if len(instance.Spec.ConfigParams.HostSwStageLocation) != 0 && len(OracleRestartSpex.PvcName) == 0 { - result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-swstage-vol", MountPath: utils.OraSwStageLocation}) + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-swstage-vol", MountPath: mountLoc}) } } + if filepath.IsAbs(instance.Spec.ConfigParams.RuPatchLocation) { + mountLoc = instance.Spec.ConfigParams.RuPatchLocation + } else { + mountLoc = utils.OraRuPatchStageLocation + } + if instance.Spec.ConfigParams != nil { if instance.Spec.ConfigParams.RuPatchLocation != "" { if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.RuPatchLocation]; !exists { result = append(result, corev1.VolumeMount{ Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", - MountPath: utils.OraRuPatchStageLocation, + MountPath: mountLoc, }) } } } + if filepath.IsAbs(instance.Spec.ConfigParams.OPatchLocation) { + mountLoc = instance.Spec.ConfigParams.OPatchLocation + } else { + mountLoc = utils.OraOPatchStageLocation + } + if instance.Spec.ConfigParams != nil { if instance.Spec.ConfigParams.OPatchLocation != "" { if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.OPatchLocation]; !exists { result = append(result, corev1.VolumeMount{ Name: OracleRestartSpex.Name + "-oradata-opatch-vol", - MountPath: utils.OraOPatchStageLocation, + MountPath: mountLoc, }) } } @@ -1023,7 +1055,32 @@ func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.Oracl return true // no default SC → use static } -func VolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.PersistentVolumeClaim { + +func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) corev1.PersistentVolumeClaim { + + // If user-provided PVC name exists, skip volume claim template creation + pvcName := OracleRestartSpex.Name + "-oradata-sw-vol-pvc" + return corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvcName, + Namespace: instance.Namespace, + Labels: buildLabelsForOracleRestart(instance, "OracleRestart"), + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + StorageClassName: &instance.Spec.StorageClass, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(fmt.Sprintf("%dGi", OracleRestartSpex.SwLocStorageSizeInGb)), + }, + }, + }, + } +} + +func ASMVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.PersistentVolumeClaim { var claims []corev1.PersistentVolumeClaim // If user-provided PVC name exists, skip volume claim template creation From 4f0c86979096a587b8b85e10aa1377f3e8cb8824 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Wed, 30 Jul 2025 12:35:00 +0000 Subject: [PATCH 236/414] Added fixes --- commons/oraclerestart/oraclerestartcommon.go | 5 +++++ commons/oraclerestart/oraclerestartprov.go | 1 + config/crd/bases/database.oracle.com_oraclerestarts.yaml | 4 ++++ config/manager/kustomization.yaml | 2 +- oracle-database-operator.yaml | 6 +++++- 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 071cbfce..560ef924 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -43,6 +43,7 @@ import ( "context" "errors" "fmt" + "path/filepath" "strconv" oraclerestart "github.com/oracle/oracle-database-operator/apis/database/v4" @@ -79,6 +80,10 @@ func buildEnvVarsSpec(envVariables []corev1.EnvVar) []corev1.EnvVar { return result } +func checkAbsPath(location string) bool { + return filepath.IsAbs(location) +} + // FUnction to build the svc definition for RAC func buildContainerPortsDef(instance *oraclerestart.OracleRestart, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.ContainerPort { var result []corev1.ContainerPort diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 1612ff3e..4633004c 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -563,6 +563,7 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, } else { fmt.Println("No Location is passed for the software storage in" + OracleRestartSpex.Name) } + var mountLoc string if filepath.IsAbs(instance.Spec.ConfigParams.HostSwStageLocation) { mountLoc = instance.Spec.ConfigParams.HostSwStageLocation diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index f9aecea2..87a5c143 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -349,6 +349,8 @@ spec: x-kubernetes-int-or-string: true type: object type: object + swLocStorageSizeInGb: + type: integer workerNode: items: type: string @@ -1098,6 +1100,8 @@ spec: x-kubernetes-int-or-string: true type: object type: object + swLocStorageSizeInGb: + type: integer workerNode: items: type: string diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2726acb4..1d870700 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: restart-operator-sa + newTag: orestart-operator-mltp1 diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 0bc7eba3..34d28b5d 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10864,6 +10864,8 @@ spec: x-kubernetes-int-or-string: true type: object type: object + swLocStorageSizeInGb: + type: integer workerNode: items: type: string @@ -11613,6 +11615,8 @@ spec: x-kubernetes-int-or-string: true type: object type: object + swLocStorageSizeInGb: + type: integer workerNode: items: type: string @@ -16696,7 +16700,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:restart-operator-sa + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-mltp1 imagePullPolicy: Always name: manager ports: From 80a1f8510fce5c92ec1fff83cff0194f187fdf44 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Wed, 30 Jul 2025 16:16:03 +0000 Subject: [PATCH 237/414] Update for v2.0.0 --- .../v1/databaseobserver_types.go | 8 ++-- .../v1alpha1/databaseobserver_types.go | 9 ++-- .../v4/databaseobserver_types.go | 8 ++-- commons/observability/constants.go | 3 +- commons/observability/utils.go | 46 ++++++++++--------- .../observability/databaseobserver.yaml | 29 ++---------- .../databaseobserver_custom_config.yaml | 28 ----------- .../databaseobserver_minimal.yaml | 22 --------- .../observability/databaseobserver_vault.yaml | 25 ---------- ...databaseobserver_customization_fields.yaml | 2 +- .../v1/databaseobserver_logs_promtail.yaml | 7 +-- .../databaseobserver_logs_promtail.yaml | 5 +- .../v1alpha1/databaseobserver_minimal.yaml | 5 +- .../v4/databaseobserver_logs_promtail.yaml | 5 +- .../v4/databaseobserver_minimal.yaml | 5 +- docs/observability/README.md | 43 +++++++++-------- 16 files changed, 71 insertions(+), 179 deletions(-) delete mode 100644 config/samples/observability/databaseobserver_custom_config.yaml delete mode 100644 config/samples/observability/databaseobserver_minimal.yaml delete mode 100644 config/samples/observability/databaseobserver_vault.yaml diff --git a/apis/observability/v1/databaseobserver_types.go b/apis/observability/v1/databaseobserver_types.go index 642ff18b..0d04af0e 100644 --- a/apis/observability/v1/databaseobserver_types.go +++ b/apis/observability/v1/databaseobserver_types.go @@ -62,13 +62,13 @@ type DatabaseObserverSpec struct { // LogConfig defines the configuration details relation to the logs of DatabaseObserver type LogConfig struct { - Path string `json:"path,omitempty"` - Filename string `json:"filename,omitempty"` - Volume LogVolume `json:"volume,omitempty"` + Disable bool `json:"disable,omitempty"` + Destination string `json:"destination,omitempty"` + Filename string `json:"filename,omitempty"` + Volume LogVolume `json:"volume,omitempty"` } type LogVolume struct { - Name string `json:"name,omitempty"` PersistentVolumeClaim LogVolumePVClaim `json:"persistentVolumeClaim,omitempty"` } diff --git a/apis/observability/v1alpha1/databaseobserver_types.go b/apis/observability/v1alpha1/databaseobserver_types.go index f4c62900..53bc32e7 100644 --- a/apis/observability/v1alpha1/databaseobserver_types.go +++ b/apis/observability/v1alpha1/databaseobserver_types.go @@ -62,13 +62,14 @@ type DatabaseObserverSpec struct { // LogConfig defines the configuration details relation to the logs of DatabaseObserver type LogConfig struct { - Path string `json:"path,omitempty"` - Filename string `json:"filename,omitempty"` - Volume LogVolume `json:"volume,omitempty"` + Disable bool `json:"disable,omitempty"` + Destination string `json:"destination,omitempty"` + Filename string `json:"filename,omitempty"` + Volume LogVolume `json:"volume,omitempty"` } + type LogVolume struct { - Name string `json:"name,omitempty"` PersistentVolumeClaim LogVolumePVClaim `json:"persistentVolumeClaim,omitempty"` } diff --git a/apis/observability/v4/databaseobserver_types.go b/apis/observability/v4/databaseobserver_types.go index 2b9df606..0e12c854 100644 --- a/apis/observability/v4/databaseobserver_types.go +++ b/apis/observability/v4/databaseobserver_types.go @@ -62,13 +62,13 @@ type DatabaseObserverSpec struct { // LogConfig defines the configuration details relation to the logs of DatabaseObserver type LogConfig struct { - Path string `json:"path,omitempty"` - Filename string `json:"filename,omitempty"` - Volume LogVolume `json:"volume,omitempty"` + Disable bool `json:"disable,omitempty"` + Destination string `json:"destination,omitempty"` + Filename string `json:"filename,omitempty"` + Volume LogVolume `json:"volume,omitempty"` } type LogVolume struct { - Name string `json:"name,omitempty"` PersistentVolumeClaim LogVolumePVClaim `json:"persistentVolumeClaim,omitempty"` } diff --git a/commons/observability/constants.go b/commons/observability/constants.go index 45f06e49..217f6bdd 100644 --- a/commons/observability/constants.go +++ b/commons/observability/constants.go @@ -32,6 +32,7 @@ const ( DefaultConfigVolumeString = "config-volume" DefaultLogFilename = "alert.log" DefaultLogVolumeString = "log-volume" + DefaultLogDestination = "/log" DefaultWalletVolumeString = "creds" DefaultOCIPrivateKeyVolumeString = "ocikey" DefaultOCIConfigFingerprintKey = "fingerprint" @@ -39,7 +40,7 @@ const ( DefaultOCIConfigTenancyKey = "tenancy" DefaultOCIConfigUserKey = "user" - DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:1.5.2" + DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:1.6.1" DefaultServicePort = 9161 DefaultServiceTargetPort = 9161 DefaultAppPort = 8080 diff --git a/commons/observability/utils.go b/commons/observability/utils.go index 6eccb261..12d72b03 100644 --- a/commons/observability/utils.go +++ b/commons/observability/utils.go @@ -203,12 +203,18 @@ func GetExporterDeploymentVolumeMounts(a *api.DatabaseObserver) []corev1.VolumeM }) } - // a.Spec.Log.Path path to mount for a custom log path, a volume is required - if rLogPath := a.Spec.Log.Path; rLogPath != "" { - vName := GetLogName(a) + // a.Spec.Log.Destination path to mount for a custom log path, a volume is required + if disabled := a.Spec.Log.Disable; !disabled { + vName := DefaultLogVolumeString + + vDestination := a.Spec.Log.Destination + if vDestination == "" { + vDestination = DefaultLogDestination + } + volM = append(volM, corev1.VolumeMount{ Name: vName, - MountPath: rLogPath, + MountPath: vDestination, }) } @@ -272,9 +278,9 @@ func GetExporterDeploymentVolumes(a *api.DatabaseObserver) []corev1.Volume { } // log-volume Volume - if rLogPath := a.Spec.Log.Path; rLogPath != "" { + if disabled := a.Spec.Log.Disable; !disabled { vs := GetLogVolumeSource(a) - vName := GetLogName(a) + vName := DefaultLogVolumeString vol = append(vol, corev1.Volume{ Name: vName, @@ -296,13 +302,6 @@ func GetExporterConfig(a *api.DatabaseObserver) string { return configName } -func GetLogName(a *api.DatabaseObserver) string { - if name := a.Spec.Log.Volume.Name; name != "" { - return name - } - return DefaultLogVolumeString -} - // GetLogVolumeSource function retrieves the source to help GetExporterDeploymentVolumes func GetLogVolumeSource(a *api.DatabaseObserver) corev1.VolumeSource { @@ -355,8 +354,6 @@ func GetExporterEnvs(a *api.DatabaseObserver) []corev1.EnvVar { rDBUserSKey := a.Spec.Database.DBUser.Key rDBUserSName := a.Spec.Database.DBUser.SecretName rOCIConfigCMName := a.Spec.OCIConfig.ConfigMapName - rLogPath := a.Spec.Log.Path - rLogFilename := a.Spec.Log.Filename rCustomEnvs := a.Spec.Exporter.Deployment.ExporterEnvs var env = make([]corev1.EnvVar, 0) @@ -462,15 +459,20 @@ func GetExporterEnvs(a *api.DatabaseObserver) []corev1.EnvVar { env = AddEnv(env, rCustomEnvs, EnvVarOracleHome, DefaultOracleHome) env = AddEnv(env, rCustomEnvs, EnvVarTNSAdmin, DefaultOracleTNSAdmin) - // LOG_DESTINATION environment variable - if rLogPath != "" { - if rLogFilename == "" { - rLogFilename = DefaultLogFilename + // LOG_DESTINATION environment variable4 + if disabled := a.Spec.Log.Disable; !disabled { + d := a.Spec.Log.Destination + if d == "" { + d = DefaultLogDestination } - d := filepath.Join(rLogPath, rLogFilename) - env = AddEnv(env, rCustomEnvs, EnvVarDataSourceLogDestination, d) - } + f := a.Spec.Log.Filename + if f == "" { + f = DefaultLogFilename + } + ld := filepath.Join(d, f) + env = AddEnv(env, rCustomEnvs, EnvVarDataSourceLogDestination, ld) + } return env } diff --git a/config/samples/observability/databaseobserver.yaml b/config/samples/observability/databaseobserver.yaml index b3140549..5962a072 100644 --- a/config/samples/observability/databaseobserver.yaml +++ b/config/samples/observability/databaseobserver.yaml @@ -1,44 +1,23 @@ # example -apiVersion: observability.oracle.com/v1alpha1 +apiVersion: observability.oracle.com/v4 kind: DatabaseObserver metadata: name: obs-sample spec: database: dbUser: - key: "username" secret: db-secret dbPassword: - key: "password" secret: db-secret dbConnectionString: - key: "connection" secret: db-secret dbWallet: secret: instance-wallet - exporter: - image: "container-registry.oracle.com/database/observability-exporter:latest" - configuration: - configmap: - key: "config.toml" - configmapName: "devcm-oradevdb-config" - - service: - port: 9161 - prometheus: - port: metrics - labels: - app: app-sample-label - - replicas: 1 - - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey - - + serviceMonitor: + labels: + release: prometheus diff --git a/config/samples/observability/databaseobserver_custom_config.yaml b/config/samples/observability/databaseobserver_custom_config.yaml deleted file mode 100644 index 1e9fff47..00000000 --- a/config/samples/observability/databaseobserver_custom_config.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# example -apiVersion: observability.oracle.com/v1alpha1 -kind: DatabaseObserver -metadata: - name: obs-sample - namespace: observer -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - key: "password" - secret: db-secret - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallet - - exporter: - configuration: - configmap: - key: "config.toml" - configmapName: "devcm-oradevdb-config" \ No newline at end of file diff --git a/config/samples/observability/databaseobserver_minimal.yaml b/config/samples/observability/databaseobserver_minimal.yaml deleted file mode 100644 index 2eeaf3ab..00000000 --- a/config/samples/observability/databaseobserver_minimal.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# example -apiVersion: observability.oracle.com/v1alpha1 -kind: DatabaseObserver -metadata: - name: obs-sample - namespace: observer -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - key: "password" - secret: db-secret - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallets \ No newline at end of file diff --git a/config/samples/observability/databaseobserver_vault.yaml b/config/samples/observability/databaseobserver_vault.yaml deleted file mode 100644 index fa2e09d4..00000000 --- a/config/samples/observability/databaseobserver_vault.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# example -apiVersion: observability.oracle.com/v1alpha1 -kind: DatabaseObserver -metadata: - name: obs-sample -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - vaultSecretName: sample_secret - vaultOCID: ocid1.vault.oc1.. - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallet - - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/observability/v1/databaseobserver_customization_fields.yaml b/config/samples/observability/v1/databaseobserver_customization_fields.yaml index d88caec4..d68d1059 100644 --- a/config/samples/observability/v1/databaseobserver_customization_fields.yaml +++ b/config/samples/observability/v1/databaseobserver_customization_fields.yaml @@ -18,7 +18,7 @@ spec: secret: db-secret dbWallet: - secret: instance-wallets + secret: instance-wallet exporter: deployment: diff --git a/config/samples/observability/v1/databaseobserver_logs_promtail.yaml b/config/samples/observability/v1/databaseobserver_logs_promtail.yaml index 8130f487..40379ec2 100644 --- a/config/samples/observability/v1/databaseobserver_logs_promtail.yaml +++ b/config/samples/observability/v1/databaseobserver_logs_promtail.yaml @@ -61,11 +61,8 @@ spec: release: prometheus log: - filename: "alert.log" - path: "/log" - - volume: - name: log-volume + destination: "/log" + filename: "alert.log" replicas: 1 diff --git a/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml b/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml index 28592cb0..b8ada151 100644 --- a/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml @@ -61,11 +61,8 @@ spec: release: prometheus log: + destination: "/log" filename: "alert.log" - path: "/log" - - volume: - name: log-volume replicas: 1 diff --git a/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml b/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml index 74620ac7..27520061 100644 --- a/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml @@ -6,19 +6,16 @@ metadata: spec: database: dbUser: - key: "username" secret: db-secret dbPassword: - key: "password" secret: db-secret dbConnectionString: - key: "connection" secret: db-secret dbWallet: - secret: instance-wallets + secret: instance-wallet prometheus: serviceMonitor: diff --git a/config/samples/observability/v4/databaseobserver_logs_promtail.yaml b/config/samples/observability/v4/databaseobserver_logs_promtail.yaml index 26a747a3..ca0f413d 100644 --- a/config/samples/observability/v4/databaseobserver_logs_promtail.yaml +++ b/config/samples/observability/v4/databaseobserver_logs_promtail.yaml @@ -61,11 +61,8 @@ spec: release: prometheus log: + destination: "/log" filename: "alert.log" - path: "/log" - - volume: - name: log-volume replicas: 1 diff --git a/config/samples/observability/v4/databaseobserver_minimal.yaml b/config/samples/observability/v4/databaseobserver_minimal.yaml index cc14fbea..5962a072 100644 --- a/config/samples/observability/v4/databaseobserver_minimal.yaml +++ b/config/samples/observability/v4/databaseobserver_minimal.yaml @@ -6,19 +6,16 @@ metadata: spec: database: dbUser: - key: "username" secret: db-secret dbPassword: - key: "password" secret: db-secret dbConnectionString: - key: "connection" secret: db-secret dbWallet: - secret: instance-wallets + secret: instance-wallet prometheus: serviceMonitor: diff --git a/docs/observability/README.md b/docs/observability/README.md index 5a281c9c..cc76f896 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -36,20 +36,27 @@ of the controller. ## Prerequisites The `DatabaseObserver` custom resource has the following prerequisites: -1. Prometheus and its `servicemonitor` custom resource definition must be installed on the cluster. +1. Installation of Prometheus `servicemonitor` custom resource definition (CRD) on the cluster. - The Observability controller creates multiple Kubernetes resources that include a Prometheus `servicemonitor`. For the controller - to create ServiceMonitors, the ServiceMonitor custom resource must exist. + to create ServiceMonitors, the ServiceMonitor custom resource must exist. For example, to install + Prometheus CRDs using the [Kube Prometheus Stack helm chart](https://prometheus-community.github.io/helm-charts/), run the following helm commands: + ```bash + helm repo add prometheus https://prometheus-community.github.io/helm-charts + helm repo update + helm upgrade --install prometheus prometheus/kube-prometheus-stack -n prometheus --create-namespace + ``` + +2. A pre-existing Oracle Database and the proper database grants and privileges. -2. A preexisting Oracle Database and the proper database grants and privileges. - The controller exports metrics through SQL queries that the user can control and specify through a _toml_ file. The necessary access privileges to the tables used in the queries are not provided and applied automatically. ## The DatabaseObserver Custom Resource -The Oracle Database Operator (__v1.2.0__ or later) includes the Oracle Database Observability controller, which automates +The Oracle Database Operator (__v2.0.0__ or later) includes the Oracle Database Observability controller, which automates the deployment and setting up of the Oracle Database exporter and the related resources to make Oracle Databases observable. In the example YAML file found in @@ -84,9 +91,9 @@ the databaseObserver custom resource provides the following configurable propert | `spec.prometheus.serviceMonitor.labels` | map | - | Yes | _release: prometheus_ | | `spec.prometheus.serviceMonitor.namespaceSelector` | - | - | Yes | - | | `spec.prometheus.serviceMonitor.endpoints` | array | - | Optional | - | -| `spec.log.filename` | string | alert.log | Optional | _alert.log_ | -| `spec.log.path` | string | /log | Optional | _/log_ | -| `spec.log.volume.name` | string | log-volume | Optional | _my-persistent-volume_ | +| `spec.log.destination` | string | alert.log | Optional | _alert.log_ | +| `spec.log.filename` | string | /log | Optional | _/log_ | +| `spec.log.disable` | bool | - | Optional | true | | `spec.log.volume.persistentVolumeClaim.claimName` | string | - | Optional | _my-pvc_ | | `spec.replicas` | number | 1 | Optional | _1_ | | `spec.inheritLabels` | array | - | Optional | _- environment: dev_
- app.kubernetes.io/name: observer | @@ -115,9 +122,9 @@ The `databaseObserver` Resource provides the remaining multiple fields that are * `spec.prometheus.serviceMonitor.namespaceSelector` - ServiceMonitor namespace selector * `spec.sidecars` - List of containers to run as a sidecar container with the observability exporter container image * `spec.sidecarVolumes` - Volumes of any sidecar containers -* `spec.log.path` - Custom path to create +* `spec.log.disable` - Disables Log volume creation * `spec.log.filename` - Custom filename for the log file -* `spec.log.volume.name` - Custom name for the log volume +* `spec.log.destination` - Custom destination for the log volume * `spec.log.volume.persistentVolumeClaim.claimName` - A volume in which to place the log to be shared by the containers. If not specified, an EmptyDir is used by default. * `spec.configuration.configMap.key` - Configuration filename inside the container and the configmap * `spec.configuration.configMap.name` - Name of the `configMap` that holds the custom metrics configuration @@ -220,7 +227,7 @@ To obtain a quick status, use the following command as an example: ```sh $ kubectl get databaseobserver obs-sample NAME EXPORTERCONFIG STATUS VERSION -obs-sample DEFAULT READY 1.5.1 +obs-sample DEFAULT READY 1.6.0 ``` @@ -237,7 +244,7 @@ deployment of the `databaseObserver` resource object should display `READY` as t ### Patch Resource The Observability controller currently supports updates for most of the fields in the manifest. The following is an example of patching the `databaseObserver` resource: ```bash -kubectl --type=merge -p '{"spec":{"exporter":{"image":"container-registry.oracle.com/database/observability-exporter:1.5.0"}}}' patch databaseobserver obs-sample +kubectl --type=merge -p '{"spec":{"exporter":{"image":"container-registry.oracle.com/database/observability-exporter:1.6.0"}}}' patch databaseobserver obs-sample ``` ### Delete Resource @@ -328,13 +335,13 @@ You can find an example in the `samples` directory, which deploys a Promtail sid ### Custom Log Location with PersistentVolumes -The fields `spec.log.filename` and `spec.log.path` enable you to configure a custom location and filename for the log. +The fields `spec.log.filename` and `spec.log.destination` enable you to configure a custom location and filename for the log. Using a custom location enables you to control where to place the logfile, such as a `persistentVolume`. ```yaml log: filename: "alert.log" - path: "/log" + destination: "/log" ``` To configure the `databaseObserver` resource to put the log file in a `persistentVolume`, you can set the following fields @@ -349,7 +356,6 @@ If `spec.log.volume.persistentVolumeClaim.claimName` is not specified, then an ` ```yaml log: volume: - name: my-log-volume persistentVolumeClaim: claimName: "my-pvc" ``` @@ -495,7 +501,7 @@ container image. spec: exporter: deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.5.3" + image: "container-registry.oracle.com/database/observability-exporter:1.6.0" ``` ### Custom Environment Variables, Arguments and Commands @@ -599,12 +605,5 @@ Follow these steps to check the logs. kubectl logs deployment.apps/oracle-database-operator-controller-manager -n oracle-database-operator-system ``` -## Known Potential Issues - -| Issue | Example error | Potential Workaround | -|---------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| -| Pod may encounter error Permission denied when creating log file. Pod cannot access file system due to insufficient permissions | ```level=error msg="Failed to create the log file: /log/alert.log"``` | Configure securityContext in the spec, add your group ID to the `supplementalgroups` inside `spec.exporter.deployment.podTemplate.securityContext` field. | - - ## Resources - [GitHub - Unified Observability for Oracle Database Project](https://github.com/oracle/oracle-db-appdev-monitoring) From 6ed12f7673b4abf8a98c6eca77d7a8e1be2184ad Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Wed, 30 Jul 2025 17:41:01 +0000 Subject: [PATCH 238/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 2 + commons/oraclerestart/oraclerestartprov.go | 115 +++++++----------- .../database/oraclerestart_controller.go | 17 ++- 3 files changed, 54 insertions(+), 80 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index c6b8457a..5609b3db 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -135,6 +135,8 @@ type InitParams struct { RuPatchLocation string `json:"ruPatchLocation,omitempty"` RuFolderName string `json:"ruFolderName,omitempty"` OPatchLocation string `json:"oPatchLocation,omitempty"` + SwStagePvc string `json:"swStagePvc,omitempty"` + SwStagePvcMountLocation string `json:"swStagePvcMountLocation,omitempty"` } type OracleRestartInstDetailSpec struct { diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 4633004c..aa646691 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -44,8 +44,6 @@ import ( "strconv" "strings" - "path/filepath" - "github.com/go-logr/logr" oraclerestart "github.com/oracle/oracle-database-operator/apis/database/v4" utils "github.com/oracle/oracle-database-operator/commons/oraclerestart/utils" @@ -321,19 +319,46 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac } } - if instance.Spec.ConfigParams != nil && instance.Spec.ConfigParams.HostSwStageLocation != "" { - if !filepath.IsAbs(instance.Spec.ConfigParams.HostSwStageLocation) { - if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.HostSwStageLocation]; !exists { + // Following block checks for HostSwStageLocation, RUPatchLocation nd OpatchLocation + if instance.Spec.ConfigParams != nil && len(instance.Spec.ConfigParams.SwStagePvc) != 0 { + // FIrst Check + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-swstagepvc-vol", + VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: instance.Spec.ConfigParams.SwStagePvc}}, + }) + } else { + if len(instance.Spec.ConfigParams.HostSwStageLocation) != 0 { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-swstage-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.HostSwStageLocation, + }, + }, + }) + } + if instance.Spec.ConfigParams.RuPatchLocation != "" { + if len(instance.Spec.ConfigParams.RuPatchLocation) == 0 { result = append(result, corev1.Volume{ - Name: OracleRestartSpex.Name + "-oradata-swstage-vol", + Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: instance.Spec.ConfigParams.HostSwStageLocation, + Path: instance.Spec.ConfigParams.RuPatchLocation, }, }, }) } } + if instance.Spec.ConfigParams.OPatchLocation != "" { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-opatch-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.OPatchLocation, + }, + }, + }) + } } if len(OracleRestartSpex.PvcName) != 0 { @@ -342,38 +367,6 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac } } - if instance.Spec.ConfigParams != nil { - if !filepath.IsAbs(instance.Spec.ConfigParams.RuPatchLocation) { - if instance.Spec.ConfigParams.RuPatchLocation != "" { - if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.RuPatchLocation]; !exists { - result = append(result, corev1.Volume{ - Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: instance.Spec.ConfigParams.RuPatchLocation, - }, - }, - }) - } - } - } - - if instance.Spec.ConfigParams.OPatchLocation != "" { - if !filepath.IsAbs(instance.Spec.ConfigParams.OPatchLocation) { - if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.OPatchLocation]; !exists { - result = append(result, corev1.Volume{ - Name: OracleRestartSpex.Name + "-oradata-opatch-vol", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: instance.Spec.ConfigParams.OPatchLocation, - }, - }, - }) - } - } - } - } - if instance.Spec.AsmStorageDetails != nil { // Iterate over the DisksBySize slice for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { @@ -565,46 +558,28 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, } var mountLoc string - if filepath.IsAbs(instance.Spec.ConfigParams.HostSwStageLocation) { - mountLoc = instance.Spec.ConfigParams.HostSwStageLocation - } else { - mountLoc = utils.OraSwStageLocation - } + // Check if ConfigParams is not nil if instance.Spec.ConfigParams != nil { - // Check if HostSwStageLocation is provided in ConfigParams - if len(instance.Spec.ConfigParams.HostSwStageLocation) != 0 && len(OracleRestartSpex.PvcName) == 0 { - result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-swstage-vol", MountPath: mountLoc}) - } - } - - if filepath.IsAbs(instance.Spec.ConfigParams.RuPatchLocation) { - mountLoc = instance.Spec.ConfigParams.RuPatchLocation - } else { - mountLoc = utils.OraRuPatchStageLocation - } + if len(instance.Spec.ConfigParams.SwStagePvc) != 0 { + result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-swstagepvc-vol", MountPath: instance.Spec.ConfigParams.SwStagePvcMountLocation}) + } else { + if instance.Spec.ConfigParams.HostSwStageLocation != "" { + result = append(result, corev1.VolumeMount{ + Name: OracleRestartSpex.Name + "-oradata-swstage-vol", + MountPath: instance.Spec.ConfigParams.HostSwStageLocation, + }) + } - if instance.Spec.ConfigParams != nil { - if instance.Spec.ConfigParams.RuPatchLocation != "" { - if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.RuPatchLocation]; !exists { + if instance.Spec.ConfigParams.RuPatchLocation != "" { result = append(result, corev1.VolumeMount{ Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", - MountPath: mountLoc, + MountPath: instance.Spec.ConfigParams.RuPatchLocation, }) } - } - } - - if filepath.IsAbs(instance.Spec.ConfigParams.OPatchLocation) { - mountLoc = instance.Spec.ConfigParams.OPatchLocation - } else { - mountLoc = utils.OraOPatchStageLocation - } - if instance.Spec.ConfigParams != nil { - if instance.Spec.ConfigParams.OPatchLocation != "" { - if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.OPatchLocation]; !exists { + if instance.Spec.ConfigParams.OPatchLocation != "" { result = append(result, corev1.VolumeMount{ Name: OracleRestartSpex.Name + "-oradata-opatch-vol", MountPath: mountLoc, diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index f3dafb34..180efd21 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -1954,24 +1954,21 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or } if instance.Spec.ConfigParams.HostSwStageLocation != "" { - if swStagePath, ok := instance.Spec.InstDetails.PvcName[instance.Spec.ConfigParams.HostSwStageLocation]; ok { - data = append(data, "STAGING_SOFTWARE_LOC="+swStagePath) - } else { - data = append(data, "STAGING_SOFTWARE_LOC="+utils.OraSwStageLocation) - } + data = append(data, "STAGING_SOFTWARE_LOC="+instance.Spec.ConfigParams.HostSwStageLocation) + } else { + data = append(data, "STAGING_SOFTWARE_LOC="+utils.OraSwStageLocation) } if instance.Spec.ConfigParams.RuPatchLocation != "" { - if ruPatchPath, ok := instance.Spec.InstDetails.PvcName[instance.Spec.ConfigParams.RuPatchLocation]; ok { - data = append(data, "APPLY_RU_LOCATION="+ruPatchPath) - } else { - data = append(data, "APPLY_RU_LOCATION="+utils.OraRuPatchStageLocation) - } + data = append(data, "APPLY_RU_LOCATION="+instance.Spec.ConfigParams.RuPatchLocation) + } else { + data = append(data, "APPLY_RU_LOCATION="+utils.OraRuPatchStageLocation) } if instance.Spec.ConfigParams.RuFolderName != "" { data = append(data, "RU_FOLDER_NAME="+instance.Spec.ConfigParams.RuFolderName) } + if instance.Spec.ConfigParams.OPatchLocation != "" { data = append(data, "OPATCH_ZIP_FILE="+utils.OraOPatchStageLocation+"/"+instance.Spec.ConfigParams.OPatchSwZipFile) } From 63b6b9256330743238fa187d5dc3918258a173d0 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Wed, 30 Jul 2025 18:22:12 +0000 Subject: [PATCH 239/414] Added fixes --- commons/oraclerestart/oraclerestartprov.go | 2 +- config/crd/bases/database.oracle.com_oraclerestarts.yaml | 8 ++++++++ oracle-database-operator.yaml | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index aa646691..e305c25c 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -315,7 +315,7 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: OracleRestartSpex.HostSwLocation}}}) } else { if instance.Spec.StorageClass != "" { - result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OracleRestartSpex.Name + "-oradata-sw-vol-pvc"}}}) + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OracleRestartSpex.Name + "-oradata-sw-vol-pvc" + OracleRestartSpex.Name + "-0"}}}) } } diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index 87a5c143..40b3af8d 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -157,6 +157,10 @@ spec: type: string swMountLocation: type: string + swStagePvc: + type: string + swStagePvcMountLocation: + type: string type: object dbSecret: properties: @@ -904,6 +908,10 @@ spec: type: string swMountLocation: type: string + swStagePvc: + type: string + swStagePvcMountLocation: + type: string type: object connectString: type: string diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 34d28b5d..72f93cd1 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10672,6 +10672,10 @@ spec: type: string swMountLocation: type: string + swStagePvc: + type: string + swStagePvcMountLocation: + type: string type: object dbSecret: properties: @@ -11419,6 +11423,10 @@ spec: type: string swMountLocation: type: string + swStagePvc: + type: string + swStagePvcMountLocation: + type: string type: object connectString: type: string From f2f1dc34211ec600cc33d1d51c42d4c4473c77b3 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Wed, 30 Jul 2025 18:32:31 +0000 Subject: [PATCH 240/414] Added fixes --- commons/oraclerestart/oraclerestartprov.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index e305c25c..3aef602f 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -315,7 +315,7 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: OracleRestartSpex.HostSwLocation}}}) } else { if instance.Spec.StorageClass != "" { - result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OracleRestartSpex.Name + "-oradata-sw-vol-pvc" + OracleRestartSpex.Name + "-0"}}}) + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OracleRestartSpex.Name + "-oradata-sw-vol-pvc-" + OracleRestartSpex.Name + "-0"}}}) } } From ff8965264ed60fca049325c7f8f05d2bc5cf312b Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Wed, 30 Jul 2025 19:01:57 +0000 Subject: [PATCH 241/414] Added fixes --- commons/oraclerestart/oraclerestartprov.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 3aef602f..8d28996c 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -1058,7 +1058,7 @@ func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestar func ASMVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.PersistentVolumeClaim { var claims []corev1.PersistentVolumeClaim - + mode := corev1.PersistentVolumeBlock // If user-provided PVC name exists, skip volume claim template creation if len(OracleRestartSpex.PvcName) != 0 { return claims @@ -1079,6 +1079,7 @@ func ASMVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleResta AccessModes: []corev1.PersistentVolumeAccessMode{ corev1.ReadWriteOnce, }, + VolumeMode: &mode, StorageClassName: &instance.Spec.StorageClass, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ From 708217fe75ddbde5e7aeee36b2a14c03cc8eee96 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 31 Jul 2025 06:18:45 +0000 Subject: [PATCH 242/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 6 ++-- commons/oraclerestart/oraclerestartprov.go | 35 +++++++++++----------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 5609b3db..50cf1031 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -82,8 +82,9 @@ type OracleRestartSpec struct { } type AsmDiskDetails struct { - DisksBySize []DiskBySize `json:"disksBySize,omitempty"` - AutoUpdate string `json:"autoUpdate,omitempty"` + DisksBySize []DiskBySize `json:"disksBySize,omitempty"` + AutoUpdate string `json:"autoUpdate,omitempty"` + AsmStorageClass string `json:"asmStorageClass,omitempty"` } // DiskBySize represents a list of disks grouped by size @@ -156,6 +157,7 @@ type OracleRestartInstDetailSpec struct { EnvFile string `json:"envFile,omitempty"` OnsTargetPort *int32 `json:"onsTargetPort,omitempty"` // Port that will be exposed on the service. OnsLocalPort *int32 `json:"onsLocalPort,omitempty"` // Port that will be exposed on the service. + SwStorageClass string `json:"swStorageClass,omitempty"` } // Responsefile Name diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 8d28996c..9d9950aa 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -139,11 +139,11 @@ func buildStatefulSpecForOracleRestart( }, } // Add volume claim templates if a storage class is specified - if len(instance.Spec.StorageClass) != 0 && !asmPvcsExist(instance, kClient) { + if len(instance.Spec.AsmStorageDetails.AsmStorageClass) != 0 && !asmPvcsExist(instance, kClient) { sfsetspec.VolumeClaimTemplates = ASMVolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex) } - if len(instance.Spec.StorageClass) != 0 && len(instance.Spec.InstDetails.HostSwLocation) == 0 { + if len(instance.Spec.InstDetails.SwStorageClass) != 0 && len(instance.Spec.InstDetails.HostSwLocation) == 0 { sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, SwVolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex)) } // Add annotations to the Pod template @@ -314,7 +314,7 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac if len(OracleRestartSpex.HostSwLocation) != 0 { result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: OracleRestartSpex.HostSwLocation}}}) } else { - if instance.Spec.StorageClass != "" { + if instance.Spec.InstDetails.SwStorageClass != "" { result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OracleRestartSpex.Name + "-oradata-sw-vol-pvc-" + OracleRestartSpex.Name + "-0"}}}) } } @@ -551,7 +551,7 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, } if len(OracleRestartSpex.HostSwLocation) != 0 { result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-sw-vol", MountPath: instance.Spec.ConfigParams.SwMountLocation}) - } else if len(instance.Spec.StorageClass) != 0 { + } else if len(instance.Spec.InstDetails.SwStorageClass) != 0 { result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-sw-vol", MountPath: instance.Spec.ConfigParams.SwMountLocation}) } else { fmt.Println("No Location is passed for the software storage in" + OracleRestartSpex.Name) @@ -621,8 +621,8 @@ func VolumePVCForASM(instance *oraclerestart.OracleRestart, index int, diskName } // Check if a StorageClass is defined and set it - if len(instance.Spec.StorageClass) != 0 { - asmPvc.Spec.StorageClassName = &instance.Spec.StorageClass + if len(instance.Spec.AsmStorageDetails.AsmStorageClass) != 0 { + asmPvc.Spec.StorageClassName = &instance.Spec.AsmStorageDetails.AsmStorageClass } else { // If no StorageClass, use the LabelSelector based on disk size asmPvc.Spec.Selector = &metav1.LabelSelector{MatchLabels: buildLabelsForAsmPv(instance, string(diskName), index)} @@ -650,15 +650,16 @@ func VolumePVForASM(instance *oraclerestart.OracleRestart, index int, diskName s }, } - if len(instance.Spec.StorageClass) != 0 { - asmPvc.Spec.StorageClassName = instance.Spec.StorageClass - asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) - asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} + /* + if len(instance.Spec.StorageClass) != 0 { + asmPvc.Spec.StorageClassName = instance.Spec.StorageClass + asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) + asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} - } else { - asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) - asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} - } + } else { */ + + asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) + asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} return asmPvc } @@ -1013,7 +1014,7 @@ func CreateServiceAccountIfNotExists(instance *oraclerestart.OracleRestart, kCli } func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.OracleRestart) bool { - if instance.Spec.StorageClass != "" { + if instance.Spec.InstDetails.SwStorageClass != "" { return false } @@ -1046,7 +1047,7 @@ func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestar AccessModes: []corev1.PersistentVolumeAccessMode{ corev1.ReadWriteOnce, }, - StorageClassName: &instance.Spec.StorageClass, + StorageClassName: &instance.Spec.InstDetails.SwStorageClass, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(fmt.Sprintf("%dGi", OracleRestartSpex.SwLocStorageSizeInGb)), @@ -1080,7 +1081,7 @@ func ASMVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleResta corev1.ReadWriteOnce, }, VolumeMode: &mode, - StorageClassName: &instance.Spec.StorageClass, + StorageClassName: &instance.Spec.AsmStorageDetails.AsmStorageClass, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(fmt.Sprintf("%dGi", diskBySize.StorageSizeInGb)), From 521a4809fd05140e4ef3c706aa8386ace6918242 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 31 Jul 2025 06:50:19 +0000 Subject: [PATCH 243/414] Added fixes --- apis/database/v4/oraclerestart_webhook.go | 4 ++-- config/crd/bases/database.oracle.com_oraclerestarts.yaml | 6 ++++++ oracle-database-operator.yaml | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index 59e7095e..d6549698 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -433,10 +433,10 @@ func (r *OracleRestart) validateGeneric() field.ErrorList { "Name must contain only alphanumeric characters")) } - if r.Spec.InstDetails.HostSwLocation == "" && r.Spec.StorageClass == "" { + if r.Spec.InstDetails.HostSwLocation == "" && r.Spec.InstDetails.SwStorageClass == "" { validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("InstDetails").Child("HostSwLocation"), r.Spec.InstDetails.HostSwLocation, - "Either HostSwLocation or StorageClass must be specified")) + "Either HostSwLocation or SwStorageClass must be specified")) } } diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index 40b3af8d..530368f5 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -47,6 +47,8 @@ spec: properties: asmStorageDetails: properties: + asmStorageClass: + type: string autoUpdate: type: string disksBySize: @@ -355,6 +357,8 @@ spec: type: object swLocStorageSizeInGb: type: integer + swStorageClass: + type: string workerNode: items: type: string @@ -1110,6 +1114,8 @@ spec: type: object swLocStorageSizeInGb: type: integer + swStorageClass: + type: string workerNode: items: type: string diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 72f93cd1..f49402bf 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10562,6 +10562,8 @@ spec: properties: asmStorageDetails: properties: + asmStorageClass: + type: string autoUpdate: type: string disksBySize: @@ -10870,6 +10872,8 @@ spec: type: object swLocStorageSizeInGb: type: integer + swStorageClass: + type: string workerNode: items: type: string @@ -11625,6 +11629,8 @@ spec: type: object swLocStorageSizeInGb: type: integer + swStorageClass: + type: string workerNode: items: type: string From 8e012f801d148ca914cc6f448fc8b9d6dc001b7b Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 31 Jul 2025 10:03:37 +0000 Subject: [PATCH 244/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 8 ++--- apis/database/v4/oraclerestart_webhook.go | 2 +- commons/oraclerestart/oraclerestartprov.go | 36 +++++++++---------- .../database/oraclerestart_controller.go | 1 + 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 50cf1031..dd8b9ab8 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -58,7 +58,6 @@ type OracleRestartSpec struct { AsmStorageDetails *AsmDiskDetails `json:"asmStorageDetails,omitempty"` NfsStorageDetails *corev1.NFSVolumeSource `json:"nfsStorageDetails,omitempty"` UseNfsforSwStorage string `json:"useNfsforSwStorage,omitempty"` - StorageClass string `json:"storageClass,omitempty"` StorageSizeInGB int `json:"storageSizeInGB,omitempty"` Image string `json:"image,omitempty"` ImagePullSecret string `json:"imagePullSecret,omitempty"` @@ -79,12 +78,12 @@ type OracleRestartSpec struct { IsFailed bool `json:"isFailed,omitempty"` IsManual bool `json:"isManual,omitempty"` SrvAccountName string `json:"serviceAccountName,omitempty"` + StorageClass string `json:"storageClass,omitempty"` } type AsmDiskDetails struct { - DisksBySize []DiskBySize `json:"disksBySize,omitempty"` - AutoUpdate string `json:"autoUpdate,omitempty"` - AsmStorageClass string `json:"asmStorageClass,omitempty"` + DisksBySize []DiskBySize `json:"disksBySize,omitempty"` + AutoUpdate string `json:"autoUpdate,omitempty"` } // DiskBySize represents a list of disks grouped by size @@ -157,7 +156,6 @@ type OracleRestartInstDetailSpec struct { EnvFile string `json:"envFile,omitempty"` OnsTargetPort *int32 `json:"onsTargetPort,omitempty"` // Port that will be exposed on the service. OnsLocalPort *int32 `json:"onsLocalPort,omitempty"` // Port that will be exposed on the service. - SwStorageClass string `json:"swStorageClass,omitempty"` } // Responsefile Name diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index d6549698..29e3e453 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -433,7 +433,7 @@ func (r *OracleRestart) validateGeneric() field.ErrorList { "Name must contain only alphanumeric characters")) } - if r.Spec.InstDetails.HostSwLocation == "" && r.Spec.InstDetails.SwStorageClass == "" { + if r.Spec.InstDetails.HostSwLocation == "" && r.Spec.StorageClass == "" { validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("InstDetails").Child("HostSwLocation"), r.Spec.InstDetails.HostSwLocation, "Either HostSwLocation or SwStorageClass must be specified")) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 9d9950aa..b6aaab81 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -139,11 +139,11 @@ func buildStatefulSpecForOracleRestart( }, } // Add volume claim templates if a storage class is specified - if len(instance.Spec.AsmStorageDetails.AsmStorageClass) != 0 && !asmPvcsExist(instance, kClient) { + if len(instance.Spec.StorageClass) != 0 && !asmPvcsExist(instance, kClient) { sfsetspec.VolumeClaimTemplates = ASMVolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex) } - if len(instance.Spec.InstDetails.SwStorageClass) != 0 && len(instance.Spec.InstDetails.HostSwLocation) == 0 { + if len(instance.Spec.StorageClass) != 0 && len(instance.Spec.InstDetails.HostSwLocation) == 0 { sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, SwVolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex)) } // Add annotations to the Pod template @@ -314,7 +314,7 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac if len(OracleRestartSpex.HostSwLocation) != 0 { result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: OracleRestartSpex.HostSwLocation}}}) } else { - if instance.Spec.InstDetails.SwStorageClass != "" { + if instance.Spec.StorageClass != "" { result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OracleRestartSpex.Name + "-oradata-sw-vol-pvc-" + OracleRestartSpex.Name + "-0"}}}) } } @@ -551,7 +551,7 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, } if len(OracleRestartSpex.HostSwLocation) != 0 { result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-sw-vol", MountPath: instance.Spec.ConfigParams.SwMountLocation}) - } else if len(instance.Spec.InstDetails.SwStorageClass) != 0 { + } else if len(instance.Spec.StorageClass) != 0 { result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-sw-vol", MountPath: instance.Spec.ConfigParams.SwMountLocation}) } else { fmt.Println("No Location is passed for the software storage in" + OracleRestartSpex.Name) @@ -621,11 +621,12 @@ func VolumePVCForASM(instance *oraclerestart.OracleRestart, index int, diskName } // Check if a StorageClass is defined and set it - if len(instance.Spec.AsmStorageDetails.AsmStorageClass) != 0 { - asmPvc.Spec.StorageClassName = &instance.Spec.AsmStorageDetails.AsmStorageClass + if len(instance.Spec.StorageClass) != 0 { + asmPvc.Spec.StorageClassName = &instance.Spec.StorageClass } else { // If no StorageClass, use the LabelSelector based on disk size asmPvc.Spec.Selector = &metav1.LabelSelector{MatchLabels: buildLabelsForAsmPv(instance, string(diskName), index)} + asmPvc.Spec.StorageClassName = nil } return asmPvc @@ -650,17 +651,16 @@ func VolumePVForASM(instance *oraclerestart.OracleRestart, index int, diskName s }, } - /* - if len(instance.Spec.StorageClass) != 0 { - asmPvc.Spec.StorageClassName = instance.Spec.StorageClass - asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) - asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} + if len(instance.Spec.StorageClass) != 0 { + asmPvc.Spec.StorageClassName = instance.Spec.StorageClass + asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) + asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} - } else { */ - - asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) - asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} + } else { + asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) + asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} + } return asmPvc } @@ -1014,7 +1014,7 @@ func CreateServiceAccountIfNotExists(instance *oraclerestart.OracleRestart, kCli } func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.OracleRestart) bool { - if instance.Spec.InstDetails.SwStorageClass != "" { + if instance.Spec.StorageClass != "" { return false } @@ -1047,7 +1047,7 @@ func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestar AccessModes: []corev1.PersistentVolumeAccessMode{ corev1.ReadWriteOnce, }, - StorageClassName: &instance.Spec.InstDetails.SwStorageClass, + StorageClassName: &instance.Spec.StorageClass, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(fmt.Sprintf("%dGi", OracleRestartSpex.SwLocStorageSizeInGb)), @@ -1081,7 +1081,7 @@ func ASMVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleResta corev1.ReadWriteOnce, }, VolumeMode: &mode, - StorageClassName: &instance.Spec.AsmStorageDetails.AsmStorageClass, + StorageClassName: &instance.Spec.StorageClass, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(fmt.Sprintf("%dGi", diskBySize.StorageSizeInGb)), diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 180efd21..67bdd7c5 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -362,6 +362,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques oracleRestart.Spec.AsmStorageDetails, r.Client, ) + _, result, err = r.createOrReplaceAsmPvC(ctx, oracleRestart, pvcVolume) if err != nil { result = resultNq From e41209ab9a2b689c30116cba5bbbbca7cac1cea6 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 31 Jul 2025 10:21:57 +0000 Subject: [PATCH 245/414] Added fixes --- commons/oraclerestart/oraclerestartprov.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index b6aaab81..d51f28a9 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -338,16 +338,14 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac }) } if instance.Spec.ConfigParams.RuPatchLocation != "" { - if len(instance.Spec.ConfigParams.RuPatchLocation) == 0 { - result = append(result, corev1.Volume{ - Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: instance.Spec.ConfigParams.RuPatchLocation, - }, + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-rupatch-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.RuPatchLocation, }, - }) - } + }, + }) } if instance.Spec.ConfigParams.OPatchLocation != "" { result = append(result, corev1.Volume{ From 318363ffde7bf7fd5f14d0fdea10e4eb901b8677 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 31 Jul 2025 11:48:20 +0000 Subject: [PATCH 246/414] Added fixes --- .../database.oracle.com_oraclerestarts.yaml | 6 --- .../database/oraclerestart_controller.go | 42 ++++++++++--------- oracle-database-operator.yaml | 6 --- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index 530368f5..40b3af8d 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -47,8 +47,6 @@ spec: properties: asmStorageDetails: properties: - asmStorageClass: - type: string autoUpdate: type: string disksBySize: @@ -357,8 +355,6 @@ spec: type: object swLocStorageSizeInGb: type: integer - swStorageClass: - type: string workerNode: items: type: string @@ -1114,8 +1110,6 @@ spec: type: object swLocStorageSizeInGb: type: integer - swStorageClass: - type: string workerNode: items: type: string diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 67bdd7c5..0b5fb27e 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -323,26 +323,28 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // PV Creation - if isNewSetup || isDiskChanged { - if oracleRestart.Spec.AsmStorageDetails != nil { - for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for index, diskName := range diskBySize.DiskNames { - pvVolume := oraclerestartcommon.VolumePVForASM( - oracleRestart, - index, - diskName, - diskBySize.StorageSizeInGb, - oracleRestart.Spec.AsmStorageDetails, - r.Client, - ) - // if pvVolume == nil { - // r.Log.Info("VolumePVForASM returned nil for Dynamic Provisioning", "diskName", diskName, "index", index) - // continue // or return error - // } - _, result, err = r.createOrReplaceAsmPv(ctx, oracleRestart, pvVolume) - if err != nil { - result = resultNq - return result, err + if len(oracleRestart.Spec.StorageClass) == 0 { + if isNewSetup || isDiskChanged { + if oracleRestart.Spec.AsmStorageDetails != nil { + for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for index, diskName := range diskBySize.DiskNames { + pvVolume := oraclerestartcommon.VolumePVForASM( + oracleRestart, + index, + diskName, + diskBySize.StorageSizeInGb, + oracleRestart.Spec.AsmStorageDetails, + r.Client, + ) + // if pvVolume == nil { + // r.Log.Info("VolumePVForASM returned nil for Dynamic Provisioning", "diskName", diskName, "index", index) + // continue // or return error + // } + _, result, err = r.createOrReplaceAsmPv(ctx, oracleRestart, pvVolume) + if err != nil { + result = resultNq + return result, err + } } } } diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index f49402bf..72f93cd1 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10562,8 +10562,6 @@ spec: properties: asmStorageDetails: properties: - asmStorageClass: - type: string autoUpdate: type: string disksBySize: @@ -10872,8 +10870,6 @@ spec: type: object swLocStorageSizeInGb: type: integer - swStorageClass: - type: string workerNode: items: type: string @@ -11629,8 +11625,6 @@ spec: type: object swLocStorageSizeInGb: type: integer - swStorageClass: - type: string workerNode: items: type: string From 80d435ce8edb8195798c9587a05fa71acb380ba4 Mon Sep 17 00:00:00 2001 From: Geela Rajahimansh Date: Thu, 31 Jul 2025 13:02:37 +0000 Subject: [PATCH 247/414] Psaini privateai update1 --- apis/omlai/v4/privateai_types.go | 2 ++ apis/omlai/v4/zz_generated.deepcopy.go | 7 +++++++ commons/omlai/aibuilder.go | 21 +++++++++++++++++++ .../bases/omlai.oracle.com_privateais.yaml | 6 ++++++ oracle-database-operator.yaml | 6 ++++++ 5 files changed, 42 insertions(+) diff --git a/apis/omlai/v4/privateai_types.go b/apis/omlai/v4/privateai_types.go index 368ba6d9..d3d0ecf4 100644 --- a/apis/omlai/v4/privateai_types.go +++ b/apis/omlai/v4/privateai_types.go @@ -78,6 +78,8 @@ type PrivateAiSpec struct { PaiAuthentication bool `json:"paiAuthentication,omitempty"` PaiLBPort int32 `json:"paiLBPort,omitempty"` PaiLBIP string `json:"paiLBIP,omitempty"` + PaiInternalLB bool `json:"paiInternalLB,omitempty"` + PailbAnnotation map[string]string `json:"pailbAnnotation,omitempty"` } // Secret Details diff --git a/apis/omlai/v4/zz_generated.deepcopy.go b/apis/omlai/v4/zz_generated.deepcopy.go index 6781d85b..061a7344 100644 --- a/apis/omlai/v4/zz_generated.deepcopy.go +++ b/apis/omlai/v4/zz_generated.deepcopy.go @@ -280,6 +280,13 @@ func (in *PrivateAiSpec) DeepCopyInto(out *PrivateAiSpec) { *out = make([]PaiPortMapping, len(*in)) copy(*out, *in) } + if in.PailbAnnotation != nil { + in, out := &in.PailbAnnotation, &out.PailbAnnotation + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiSpec. diff --git a/commons/omlai/aibuilder.go b/commons/omlai/aibuilder.go index 316ad7b1..77b4b5c1 100644 --- a/commons/omlai/aibuilder.go +++ b/commons/omlai/aibuilder.go @@ -329,6 +329,27 @@ func BuildServiceDefForPrivateAi(instance *privateaiv4.PrivateAi, svctype string if svctype == "external" { service.Spec.Type = corev1.ServiceTypeLoadBalancer service.Spec.Selector = buildLabelsForPrivateAi(instance, "privateai", instance.Name) + // internal LB condition + if instance.Spec.PaiInternalLB { + if service.ObjectMeta.Annotations == nil { + service.ObjectMeta.Annotations = make(map[string]string) + } + + // Use custom annotations if provided + if len(instance.Spec.PailbAnnotation) > 0 { + for k, v := range instance.Spec.PailbAnnotation { + service.ObjectMeta.Annotations[k] = v + } + } + + // default internal LB annotations if not already set + if _, ok := service.ObjectMeta.Annotations["oci.oraclecloud.com/load-balancer-type"]; !ok { + service.ObjectMeta.Annotations["oci.oraclecloud.com/load-balancer-type"] = "lb" + } + if _, ok := service.ObjectMeta.Annotations["service.beta.kubernetes.io/oci-load-balancer-internal"]; !ok { + service.ObjectMeta.Annotations["service.beta.kubernetes.io/oci-load-balancer-internal"] = "true" + } + } } if svctype == "local" { diff --git a/config/crd/bases/omlai.oracle.com_privateais.yaml b/config/crd/bases/omlai.oracle.com_privateais.yaml index f91ca54b..bb044b2d 100644 --- a/config/crd/bases/omlai.oracle.com_privateais.yaml +++ b/config/crd/bases/omlai.oracle.com_privateais.yaml @@ -122,6 +122,8 @@ spec: type: string paiImagePullSecret: type: string + paiInternalLB: + type: boolean paiLBIP: type: string paiLBPort: @@ -163,6 +165,10 @@ spec: svcType: type: string type: object + pailbAnnotation: + additionalProperties: + type: string + type: object portMappings: items: properties: diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 0bc7eba3..9e45fe8c 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -13558,6 +13558,8 @@ spec: type: string paiImagePullSecret: type: string + paiInternalLB: + type: boolean paiLBIP: type: string paiLBPort: @@ -13599,6 +13601,10 @@ spec: svcType: type: string type: object + pailbAnnotation: + additionalProperties: + type: string + type: object portMappings: items: properties: From fb943f6d3bdd6e03aaa324581b881807b514377b Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Sat, 2 Aug 2025 05:26:38 +0000 Subject: [PATCH 248/414] private ai documentation changes --- docs/privateai/README.md | 19 ++- docs/privateai/access_privateai.md | 128 ++---------------- docs/privateai/deploy_privateai_internallb.md | 46 +++++++ ...vateai.md => deploy_privateai_publiclb.md} | 10 +- docs/privateai/pai_sample.yaml | 25 ---- docs/privateai/pai_sample.yaml.bkp | 27 ---- docs/privateai/pai_sample_internallb.yaml | 61 +++++++++ .../pai_sample_internallb_replace_cert.yaml | 65 +++++++++ docs/privateai/pai_sample_publiclb.yaml | 61 +++++++++ docs/privateai/pai_sample_scale_in.yaml | 35 ++++- docs/privateai/pai_sample_scale_up.yaml | 33 +++++ docs/privateai/pai_secret_new.sh | 26 ++++ docs/privateai/scale_in_privateai.md | 8 +- docs/privateai/scale_up_privateai.md | 8 +- 14 files changed, 365 insertions(+), 187 deletions(-) create mode 100644 docs/privateai/deploy_privateai_internallb.md rename docs/privateai/{deploy_privateai.md => deploy_privateai_publiclb.md} (65%) delete mode 100644 docs/privateai/pai_sample.yaml delete mode 100644 docs/privateai/pai_sample.yaml.bkp create mode 100644 docs/privateai/pai_sample_internallb.yaml create mode 100644 docs/privateai/pai_sample_internallb_replace_cert.yaml create mode 100644 docs/privateai/pai_sample_publiclb.yaml create mode 100644 docs/privateai/pai_secret_new.sh diff --git a/docs/privateai/README.md b/docs/privateai/README.md index 388e5b31..d68a7c58 100644 --- a/docs/privateai/README.md +++ b/docs/privateai/README.md @@ -79,23 +79,32 @@ kubectl get configmap -n pai ### 6. Reserve LoadBalancer Public IP -- The SSL certificate used during the PrivateAI Container Deployment will a common name(hostname or IP) to be specified. +- The SSL certificate used during the PrivateAI Container Deployment will need a common name(hostname or IP) to be specified during the certificate creation. - Later, for a secure communication with the PrivateAI Container Deployed in a Kuberentes Cluster, the client will use the same `cert.pem` file and will send the connection request to same hostname or IP. -- If you are deploying PrivateAI Container on an OKE cluster, you will need to reserve a Public IP in OCI. Refer to [OCI Load Balancer Documentation](https://docs.public.oneportal.content.oci.oraclecloud.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm) for the details. +- If you are deploying PrivateAI Container on an OKE cluster, you will need to reserve a Public IP in OCI. - OCI allows provisioning a Public LoadBalancer and assigning a reserved public ip to it. Please the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). +- To reserve a Public IP in OCI, refer to [OCI LoadBalancer Documentation](https://docs.public.oneportal.content.oci.oraclecloud.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm) for the details. - Once you have reserved the Public IP, use this Public IP as `Common Name` while generating the openssl certificate in the next step. +**NOTE:** This step is required only if you are going to deploy the PrivateAI Container on OKE Cluster using OCI Public LoadBalancer. + +**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available as of now. Please check the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). + ### 7. Create Kubernetes secret for the Oracle PrivateAI Deployment **IMPORTANT:** Make sure the version of `openssl` in the Oracle PrivateAI image is compatible with the `openssl` version on the machine where you will run the openssl commands to generate the encrypted password file during the deployment. Create a file `oml-ssl-pwd` with the password you want to use. This password will be used in the next step. The script [pai_secret.sh](./pai_secret.sh) has the command to generate the required keys and an SSL certificate. -Use the Shell Script `pai_secret.sh` to create the required secrets for the Oracle PrivateAI Container Deployment. Run this file as below and enter the password when prompted: +Use the Shell Script `pai_secret.sh` to create the required secrets for the Oracle PrivateAI Container Deployment. Run this file as below and enter the password when prompted. + +In case of the Public LoadBalancer, use the reserved Public IP as the `common name`. ```sh ./pai_secret.sh ``` +**NOTE:** In case of the Internal LoadBalancer, we can not use a reserved Private IP. In this case, you can leave `common name` empty. + Use below command to check the Kubernetes Secret Created: ```sh @@ -108,7 +117,9 @@ After you have the above prerequisites completed, you can proceed to the next se ## Quick Start -Please refer to [this page](./deploy_privateai.md) for the details to deploy the PrivateAI Container Pod in Kubernetes. +Please refer to [Deploy PrivateAI in OKE cluster using Public LB](./deploy_privateai_publiclb.md) for the details to deploy the PrivateAI Container Pod in an OKE Cluster using OCI Public LoadBalancer. + +Please refer to [Deploy PrivateAI in OKE cluster using Internal LB](./deploy_privateai_internallb.md) for the details to deploy the PrivateAI Container Pod in an OKE Cluster using OCI Internal LoadBalancer. ## Accessing the PrivateAI Container Pod in Kubernetes diff --git a/docs/privateai/access_privateai.md b/docs/privateai/access_privateai.md index 55658738..ec0525f9 100644 --- a/docs/privateai/access_privateai.md +++ b/docs/privateai/access_privateai.md @@ -1,6 +1,8 @@ # Accessing the PrivateAI Container Pod in Kubernetes -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace and you have the Reserved Public IP of the LoadBalancer. +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace and you have: +- The Reserved Public IP in case of the Public LoadBalancer +- The Private IP in case of case of the Internal LoadBalancer ## Accessing the PrivateAI Container in Kubernetes using REST Calls Please follow the below steps to access: @@ -9,130 +11,22 @@ Please follow the below steps to access: When you have used the file [pai_secret.sh](./pai_secret.sh) during the deployment, you will have the file named `api-key` generated in the same location. Copy this file `api-key` to the machine where you want to run the API Call to the Model Endpoint. -2. Keep the LoadBalancer Reserved Public IP ready. You can use this IP in the API Endpoint call in the next step. +2. Keep the LoadBalancer Reserved Public IP or the Private IP ready. You can use this IP in the API Endpoint call in the next step. -3. Assume the Loadbalancer Reserved Public IP from the last step is `129.xxx.xxx.xxx`, you can use the below command to make an API Endpoint Call: +3. Assume the Loadbalancer Reserved Public IP from the last step is `129.xxx.xxx.xxx`, you can use the below example command to make an API Endpoint Call: ```sh - curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /home/opc/api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score + curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score ``` +**NOTE:** In case of the Private LoadBalancer, use the Internal IP in place of the IP `129.xxx.xxx.xxx` in above example. + ## Accessing the PrivateAI Container using REST Calls with SSL certificate In case you want to use SSL authentication while accessing the PrivateAI Container in Kubernetes using SSL certificate, then you will need to follow below additional steps: 1. Copy the `cert.pem` generated when you had run `pai_secret.sh` script, to the machine where you want to run the API Call to the Model Endpoint. -2. Use this key file while running the below modified command to make an API Endpoint Call: - ```sh - curl --cacert cert.pem --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /home/opc/api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score - ``` - -## Accessing the PrivateAI Container using PLSQL Commands from an Oracle Database - -- The access to the PrivateAI Container deployed in Kubernetes Cluster is provided via the utl_to_embedding(s) apis which are part of the dbms_vector_chain package. -- When using the database as a client via the utl_to_embedding or utl_to_embeddings functions, a user will first need to create a credential containing the key using the CREATE_CREDENTIAL procedure. This credential is then referenced when registering the AI Container as a provider for generating embeddings. - -We will follow the below steps to access the PrivateAI Container using PLSQL Commands from an Oracle Database: - -- Copy the `cert.pem` file, which was generated during the SSL certificate creation at the time of the deploying PrivateAI Container in the Kubernetes Cluster, to the Oracle Database host. -- Create a wallet location, create the wallet and add `cert.pem` to this wallet: - ```sh - mkdir -p /home/oracle/wallet/ - orapki wallet create -wallet /home/oracle/wallet/ -pwd - orapki wallet add -wallet /home/oracle/wallet/ -trusted_cert -cert cert.pem -pwd - ``` - -- Add permission to connect as well as to use client certificates. You need to change principal_name to your user/schema("vectordb" in this case). -- Also, use the same `api-key` which was generated when you using the script `pai_secret.sh` - ```sh - connect / as sysdba - - BEGIN - DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE( - host => '*', - ace => xs$ace_type(privilege_list => xs$name_list('connect'), - principal_name => 'vectordb', - principal_type => xs_acl.ptype_db)); - END; - / - - BEGIN - DBMS_NETWORK_ACL_ADMIN.APPEND_WALLET_ACE( - wallet_path => 'file:/home/oracle/wallet', - ace => xs$ace_type( - privilege_list => xs$name_list('use_client_certificates', 'use_passwords'), - principal_name => 'vectordb', - principal_type => xs_acl.ptype_db)); - END; - / - ``` - -- Login as the user and create credential: +2. Use this key file while running the below modified example command to make an API Endpoint Call: ```sh - connect vectordb/@ - exec dbms_vector.drop_credential('PRIVATEAI_CRED'); - - declare - jo json_object_t; - begin - jo := json_object_t(); - jo.put('access_token', ''); - dbms_vector.create_credential( - credential_name => 'PRIVATEAI_CRED', - params => json(jo.to_string)); - end; - / + curl --cacert cert.pem --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score ``` -- If required, set proxy and no proxy uising "utl_http.set_proxy()" - -- Set the wallet - ```sh - exec utl_http.set_wallet('file:/home/oracle/wallet/', ''); - ``` - -- Declare embedding parameters: - ```sh - var params clob; - - begin - :params := ' - { - "provider": "oracle_ai_instance", - "credential_name": "PRIVATEAI_CRED", - "url": "https://HOSTNAME_OR_IP_ADDRESS_AI_CONTAINER:port_number/omlmodels/all_minilm_v6/score", - "model": "all_minilm_v6", - }'; - end; - / - ``` - -- Get the embeddings - ```sh - select dbms_vector.utl_to_embedding('Hello world', json(:params)) from dual; - ``` - -- To list the models - ```sh - declare - preferences clob; - input clob; - output clob; - begin - preferences := '{ - "provider": "oracle_ai_instance", - "url": "https://HOSTNAME_OR_IP_ADDRESS_AI_CONTAINER:port_number/omlmodels", - "credential_name": "PRIVATEAI_CRED", - }'; - - output := dbms_vector_chain.list_models(json(preferences)); - if length(output) > 5000 then - dbms_output.put_line(dbms_lob.substr(output, 5000)); - else - dbms_output.put_line(json_query(output, '$' returning clob pretty)); - end if; - - if output is not null then - dbms_lob.freetemporary(output); - end if; - end; - / - ``` \ No newline at end of file +**NOTE:** In case of the Private LoadBalancer, use the Internal IP in place of the IP `129.xxx.xxx.xxx` in above example. \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_internallb.md b/docs/privateai/deploy_privateai_internallb.md new file mode 100644 index 00000000..ddc6a7e2 --- /dev/null +++ b/docs/privateai/deploy_privateai_internallb.md @@ -0,0 +1,46 @@ +# Deploy PrivateAI in OKE cluster using Internal LB + +Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. + +**IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. + +**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available as of now. Please check the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). + +If you want to use the OCI Internal LoadBalancer, then you will need to follow the below steps: + +1. Deploy the [pai_sample_internallb.yaml](./pai_sample_internallb.yaml) file: + ```sh + kubectl apply -f pai_sample_internallb.yaml + ``` + This will provision the PrivateAI Container in the OKE cluster using Internal LoadBalancer with Ephemeral Private IP. + +2. Check the status of the deployment and note the IP under field `EXTERNAL-IP` for `service/pai-sample-svc`. + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n pai + + # Check the logs of a particular pod. For example, to check status of pod "pai-sample-b669d7897-nkkhz": + kubectl logs pod/pai-sample-b669d7897-nkkhz -n pai + ``` + +In this case, the internal LoadBalancer is created as an OCI load balancer with a private IP address, hosted on the subnet specified for load balancers when the OKE cluster was created. + +In case, you want the internal LoadBalancer to be created as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created, you need to add following annotations in the above .yaml file: + +```sh + pailbAnnotation: + # Specify the OCID of the alternate Subnet + service.beta.kubernetes.io/oci-load-balancer-subnet1: "ocid1.subnet.oc1..aaaaaa....vdfw" +``` + +**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. In order to avoid a hostname mismatch error while using the `cert.pem` file to make a authenicated connection, we will need to replace this SSL certificate with a new certificate which has the `common name` set to the IP of the Internal LoadBalancer. + +3. Use the file [pai_secret_new.sh](./pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While using this script, use the IP noted in Step 2 for `common name` while generating the SSL certificate. + +4. Apply the modified file [pai_sample_internallb_replace_cert.yaml](./pai_sample_internallb_replace_cert.yaml) to replace the Internal LoadBalancer Certificate: + ```sh + kubectl apply -f pai_sample_internallb_replace_cert.yaml + ``` +**NOTE:** This step will result in termination of the existing PrivateAI Container Pod and creation of new Pod while the Internal LoadBalancer IP will not change. + +5. After this change, you will be able to access the PrivateAI Container using the Internal LoadBalancer IP using an authenticated connection using the `cert.pem` file from the new SSL certificate. \ No newline at end of file diff --git a/docs/privateai/deploy_privateai.md b/docs/privateai/deploy_privateai_publiclb.md similarity index 65% rename from docs/privateai/deploy_privateai.md rename to docs/privateai/deploy_privateai_publiclb.md index a3c913f4..77650048 100644 --- a/docs/privateai/deploy_privateai.md +++ b/docs/privateai/deploy_privateai_publiclb.md @@ -1,16 +1,16 @@ -# Deploying Oracle PrivateAI Container +# Deploying Oracle PrivateAI Container using Public LoadBalancer Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. **IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. -**NOTE:** Modify the file `pai_sample.yaml` with the actual Reserved Public IP before deployment. +**NOTE:** Modify the file `pai_sample_publiclb.yaml` with the actual Reserved Public IP before deployment. -Use the file: [pai_sample.yaml](./pai_sample.yaml) for this use case as below: +Use the file: [pai_sample_publiclb.yaml](./pai_sample_publiclb.yaml) for this use case as below: -1. Deploy the `pai_sample.yaml` file: +1. Deploy the `pai_sample_publiclb.yaml` file: ```sh - kubectl apply -f pai_sample.yaml + kubectl apply -f pai_sample_publiclb.yaml ``` 2. Check the status of the deployment: ```sh diff --git a/docs/privateai/pai_sample.yaml b/docs/privateai/pai_sample.yaml deleted file mode 100644 index feb4a481..00000000 --- a/docs/privateai/pai_sample.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: omlai.oracle.com/v4 -kind: PrivateAi -metadata: - name: pai-sample - namespace: pai -spec: - paiConfigFile: - name: omlconfigjson - mountLocation: /oml/config - paiSecret: - name: paisecret - mountLocation: /oml/ssl - isExternalSvc: true - storageClass: oci-bv - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 - paiService: - name: paisvc - paiLBIP: 129.xxx.xxx.xxx - storageSizeInGb: 30 - replicas: 1 diff --git a/docs/privateai/pai_sample.yaml.bkp b/docs/privateai/pai_sample.yaml.bkp deleted file mode 100644 index b74c1c68..00000000 --- a/docs/privateai/pai_sample.yaml.bkp +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: omlai.oracle.com/v4 -kind: PrivateAi -metadata: - name: pai-sample - namespace: pai -spec: - paiConfigFile: - name: omlconfigjson - mountLocation: /oml/config - paiSecret: - name: paisecret - mountLocation: /oml/ssl - isExternalSvc: true - storageClass: oci-bv - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - storageSizeInGb: 30 diff --git a/docs/privateai/pai_sample_internallb.yaml b/docs/privateai/pai_sample_internallb.yaml new file mode 100644 index 00000000..0cd265a7 --- /dev/null +++ b/docs/privateai/pai_sample_internallb.yaml @@ -0,0 +1,61 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +# Specifies the custom resource API version +apiVersion: omlai.oracle.com/v4 + +# Defines the type of resource (Private AI) +kind: PrivateAi + +# Provide identification and scoping metadata +metadata: + # Name of Private AI resource + name: pai-sample + # Kubernetes Namespace where this resource will be deployed + namespace: pai + +# Main specification for Private AI +spec: + + # Configuration for Private AI + paiConfigFile: + # Configmap having the link for the AI Model File + name: omlconfigjson + # Location inside the Pod to which the file containining the configmap will be mounted to + mountLocation: /oml/config + + # Specify the Kubernetes Secret Details + paiSecret: + name: paisecret + mountLocation: /oml/ssl + + # If you want to enable Authentication for the Private AI Access + paiEnableAuthentication: true + + # If LoadBalancer will be used + isExternalSvc: true + + # OCI Cloud Storage Class + storageClass: oci-bv + + # Container Image of the Private AI Container + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 + + # Name and port details of the Private AI Service once deployed + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + + # If Internal Load Balancer will be used + paiInternalLB: true + + # Storage Allocation for the Pod + storageSizeInGb: 30 + + # Replica Count + replicas: 1 diff --git a/docs/privateai/pai_sample_internallb_replace_cert.yaml b/docs/privateai/pai_sample_internallb_replace_cert.yaml new file mode 100644 index 00000000..93b680b1 --- /dev/null +++ b/docs/privateai/pai_sample_internallb_replace_cert.yaml @@ -0,0 +1,65 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +# Specifies the custom resource API version +apiVersion: omlai.oracle.com/v4 + +# Defines the type of resource (Private AI) +kind: PrivateAi + +# Provide identification and scoping metadata +metadata: + # Name of Private AI resource + name: pai-sample + # Kubernetes Namespace where this resource will be deployed + namespace: pai + +# Main specification for Private AI +spec: + + # Configuration for Private AI + paiConfigFile: + # Configmap having the link for the AI Model File + name: omlconfigjson + # Location inside the Pod to which the file containining the configmap will be mounted to + mountLocation: /oml/config + + # Specify the Kubernetes Secret Details + paiSecret: + name: paisecretnew + mountLocation: /oml/ssl + + # If you want to enable Authentication for the Private AI Access + paiEnableAuthentication: true + + # If LoadBalancer will be used + isExternalSvc: true + + # OCI Cloud Storage Class + storageClass: oci-bv + + # Container Image of the Private AI Container + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 + + # Name and port details of the Private AI Service once deployed + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + + # annotation to replace the certificate for the OCI Internal LoadBalancer + pailbAnnotation: + service.beta.kubernetes.io/oci-load-balancer-tls-secret: paisecretnew + + # If Internal Load Balancer will be used + paiInternalLB: true + + # Storage Allocation for the Pod + storageSizeInGb: 30 + + # Replica Count + replicas: 1 diff --git a/docs/privateai/pai_sample_publiclb.yaml b/docs/privateai/pai_sample_publiclb.yaml new file mode 100644 index 00000000..d205cb64 --- /dev/null +++ b/docs/privateai/pai_sample_publiclb.yaml @@ -0,0 +1,61 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +# Specifies the custom resource API version +apiVersion: omlai.oracle.com/v4 + +# Defines the type of resource (Private AI) +kind: PrivateAi + +# Provide identification and scoping metadata +metadata: + # Name of Private AI resource + name: pai-sample + # Kubernetes Namespace where this resource will be deployed + namespace: pai + +# Main specification for Private AI +spec: + + # Configuration for Private AI + paiConfigFile: + # Configmap having the link for the AI Model File + name: omlconfigjson + # Location inside the Pod to which the file containining the configmap will be mounted to + mountLocation: /oml/config + + # Specify the Kubernetes Secret Details + paiSecret: + name: paisecret + mountLocation: /oml/ssl + + # If you want to enable Authentication for the Private AI Access + paiEnableAuthentication: true + + # If LoadBalancer will be used + isExternalSvc: true + + # OCI Cloud Storage Class + storageClass: oci-bv + + # Container Image of the Private AI Container + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 + + # Name and port details of the Private AI Service once deployed + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + + # Public Reserved IP of the Public LoadBalancer + paiLBIP: 129.xxx.xxx.xxx + + # Storage Allocation for the Pod + storageSizeInGb: 30 + + # Replica Count + replicas: 1 diff --git a/docs/privateai/pai_sample_scale_in.yaml b/docs/privateai/pai_sample_scale_in.yaml index 0ab57ba0..87aa4708 100644 --- a/docs/privateai/pai_sample_scale_in.yaml +++ b/docs/privateai/pai_sample_scale_in.yaml @@ -3,26 +3,59 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# Specifies the custom resource API version apiVersion: omlai.oracle.com/v4 + +# Defines the type of resource (Private AI) kind: PrivateAi + +# Provide identification and scoping metadata metadata: + # Name of Private AI resource name: pai-sample + # Kubernetes Namespace where this resource will be deployed namespace: pai + +# Main specification for Private AI spec: + + # Configuration for Private AI paiConfigFile: + # Configmap having the link for the AI Model File name: omlconfigjson + # Location inside the Pod to which the file containining the configmap will be mounted to mountLocation: /oml/config + + # Specify the Kubernetes Secret Details paiSecret: name: paisecret mountLocation: /oml/ssl + + # If you want to enable Authentication for the Private AI Access + paiEnableAuthentication: true + + # If LoadBalancer will be used isExternalSvc: true + + # OCI Cloud Storage Class storageClass: oci-bv + + # Container Image of the Private AI Container paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 + + # Name and port details of the Private AI Service once deployed paiService: name: paisvc portMappings: - port: 443 targetPort: 8443 protocol: TCP + + # Public Reserved IP of the Public LoadBalancer + paiLBIP: 129.xxx.xxx.xxx + + # Storage Allocation for the Pod storageSizeInGb: 30 - replicas: 3 + + # Replica Count + replicas: 2 diff --git a/docs/privateai/pai_sample_scale_up.yaml b/docs/privateai/pai_sample_scale_up.yaml index 0ab57ba0..0bc25223 100644 --- a/docs/privateai/pai_sample_scale_up.yaml +++ b/docs/privateai/pai_sample_scale_up.yaml @@ -3,26 +3,59 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# Specifies the custom resource API version apiVersion: omlai.oracle.com/v4 + +# Defines the type of resource (Private AI) kind: PrivateAi + +# Provide identification and scoping metadata metadata: + # Name of Private AI resource name: pai-sample + # Kubernetes Namespace where this resource will be deployed namespace: pai + +# Main specification for Private AI spec: + + # Configuration for Private AI paiConfigFile: + # Configmap having the link for the AI Model File name: omlconfigjson + # Location inside the Pod to which the file containining the configmap will be mounted to mountLocation: /oml/config + + # Specify the Kubernetes Secret Details paiSecret: name: paisecret mountLocation: /oml/ssl + + # If you want to enable Authentication for the Private AI Access + paiEnableAuthentication: true + + # If LoadBalancer will be used isExternalSvc: true + + # OCI Cloud Storage Class storageClass: oci-bv + + # Container Image of the Private AI Container paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 + + # Name and port details of the Private AI Service once deployed paiService: name: paisvc portMappings: - port: 443 targetPort: 8443 protocol: TCP + + # Public Reserved IP of the Public LoadBalancer + paiLBIP: 129.xxx.xxx.xxx + + # Storage Allocation for the Pod storageSizeInGb: 30 + + # Replica Count replicas: 3 diff --git a/docs/privateai/pai_secret_new.sh b/docs/privateai/pai_secret_new.sh new file mode 100644 index 00000000..a59e2dbb --- /dev/null +++ b/docs/privateai/pai_secret_new.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +CURR_DIR=`pwd` +KEYSTORE_SECRET=keystore +API_SECRET=api-key +OMLSECRET=omlsslpwd +NAMESPACE=pai +SECRET_NAME=paisecretnew + +rm -f ${CURR_DIR}/key.pem +rm -f ${CURR_DIR}/cert.pem +rm -f ${CURR_DIR}/api-key +rm -f ${CURR_DIR}/key.pub + +head -c 32 /dev/urandom | xxd -p | tr -d '\n' | head -c 64 > api-key +openssl genrsa -out ${CURR_DIR}/key.pem +openssl rsa -in ${CURR_DIR}/key.pem -out ${CURR_DIR}/key.pub -pubout +openssl req -new -x509 -key ${CURR_DIR}/key.pem -out ${CURR_DIR}/cert.pem -days 365 + +# Generate keystore. Enter the password when prompted, and be sure to remember it. +openssl pkcs12 -export -inkey ${CURR_DIR}/key.pem -in ${CURR_DIR}/cert.pem -name mykey -out ${CURR_DIR}/keystore + +kubectl delete secret $SECRET_NAME -n $NAMESPACE +kubectl create secret generic $SECRET_NAME --from-file=keystore --from-file=api-key --from-file=oml-ssl-pwd -n $NAMESPACE +#kubectl create secret generic $API_SECRET --from-file=api-key.txt -n $NAMESPACE +#kubectl create secret generic $OMLSECRET --from-file=oml-ssl-pwd -n $NAMESPACE diff --git a/docs/privateai/scale_in_privateai.md b/docs/privateai/scale_in_privateai.md index a083a58f..817c79b4 100644 --- a/docs/privateai/scale_in_privateai.md +++ b/docs/privateai/scale_in_privateai.md @@ -1,19 +1,19 @@ # Scale-In an existing deployment of Oracle PrivateAI Container -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=3` +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=3` using the file [pai_sample_scale_up.yaml](./pai_sample_scale_up.yaml) In this example, we will Scale In an existing deployment with `replicas=3` to `replicas=2`. -Use the file: [pai-sample-scale-in.yaml](./pai-sample-scale-in.yaml) for this use case as below: +Use the file: [pai_sample_scale_in.yaml](./pai_sample_scale_in.yaml) for this use case as below: 1. Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n pai ``` -2. Apply the `pai-sample-scale-in.yaml` file to scale in: +2. Apply the `pai_sample_scale_in.yaml` file to scale in: ```sh - kubectl apply -f pai-sample-scale-in.yaml + kubectl apply -f pai_sample_scale_in.yaml ``` 3. Check the status of the deployment: ```sh diff --git a/docs/privateai/scale_up_privateai.md b/docs/privateai/scale_up_privateai.md index 54ec7090..c480c70f 100644 --- a/docs/privateai/scale_up_privateai.md +++ b/docs/privateai/scale_up_privateai.md @@ -1,19 +1,19 @@ # Scale-Up an existing deployment of Oracle PrivateAI Container -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=1` +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=1` using the file [pai_sample_publiclb.yaml](./pai_sample_publiclb.yaml) In this example, we will Scale Up an existing deployment with `replicas=1` to `replicas=3`. -Use the file: [pai-sample-scale-up.yaml](./pai-sample-scale-up.yaml) for this use case as below: +Use the file: [pai_sample_scale_up.yaml](./pai_sample_scale_up.yaml) for this use case as below: 1. Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n pai ``` -2. Deploy the `pai-sample-scale-up.yaml` file to Scale Up: +2. Deploy the `pai_sample_scale_up.yaml` file to Scale Up: ```sh - kubectl apply -f pai-sample-scale-up.yaml + kubectl apply -f pai_sample_scale_up.yaml ``` 3. Check the status of the deployment: ```sh From 0d1e97186f0fb962c6a0da1b55f105df03286fca Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Mon, 4 Aug 2025 16:48:01 +0000 Subject: [PATCH 249/414] Update debug_privateai.md --- docs/privateai/debug_privateai.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/privateai/debug_privateai.md b/docs/privateai/debug_privateai.md index 3115a4c4..c02a2204 100644 --- a/docs/privateai/debug_privateai.md +++ b/docs/privateai/debug_privateai.md @@ -11,11 +11,4 @@ Use the below command to get the logs of the PrivateAI Container Pod deployed in - Get the logs of the PrivateAI Container Pod deployed in the namespace "pai" kubectl logs -f pod/ -n pai - ``` - -## Details for HTTP request error - -If you get an HTTP request error when you make utl_to_embedding call, you can run the following to get more details: - ```sh - select utl_http.get_detailed_sqlerrm from dual; ``` \ No newline at end of file From 92548fa521254381a640cb941193eac9a539048b Mon Sep 17 00:00:00 2001 From: Ting-Lan Wang Date: Mon, 4 Aug 2025 15:26:34 -0400 Subject: [PATCH 250/414] Merge cmd/main.go into main.go --- cmd/main.go | 471 ---------------------------------------------------- main.go | 91 ++++++++++ 2 files changed, 91 insertions(+), 471 deletions(-) delete mode 100644 cmd/main.go diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index ad286a58..00000000 --- a/cmd/main.go +++ /dev/null @@ -1,471 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package main - -import ( - "context" - "flag" - "fmt" - "os" - "strconv" - "strings" - "time" - - monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" - - "go.uber.org/zap/zapcore" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - "k8s.io/client-go/rest" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" - observabilityv1 "github.com/oracle/oracle-database-operator/apis/observability/v1" - observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" - observabilityv4 "github.com/oracle/oracle-database-operator/apis/observability/v4" - databasecontroller "github.com/oracle/oracle-database-operator/controllers/database" - dataguardcontroller "github.com/oracle/oracle-database-operator/controllers/dataguard" - observabilitycontroller "github.com/oracle/oracle-database-operator/controllers/observability" - // +kubebuilder:scaffold:imports -) - -var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") -) - -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(observabilityv1alpha1.AddToScheme(scheme)) - utilruntime.Must(monitorv1.AddToScheme(scheme)) - utilruntime.Must(databasev1alpha1.AddToScheme(scheme)) - utilruntime.Must(databasev4.AddToScheme(scheme)) - utilruntime.Must(observabilityv1.AddToScheme(scheme)) - utilruntime.Must(observabilityv4.AddToScheme(scheme)) - // +kubebuilder:scaffold:scheme -} - -func main() { - var metricsAddr string - var enableLeaderElection bool - flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") - flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, - "Enable leader election for controller manager. "+ - "Enabling this will ensure there is only one active controller manager.") - flag.Parse() - - // Initialize new logger Opts - options := &zap.Options{ - Development: true, - TimeEncoder: zapcore.RFC3339TimeEncoder, - } - - ctrl.SetLogger(zap.New(func(o *zap.Options) { *o = *options })) - - watchNamespaces, err := getWatchNamespace() - if err != nil { - setupLog.Error(err, "Failed to get watch namespaces") - os.Exit(1) - } - opt := ctrl.Options{ - Scheme: scheme, - Metrics: metricsserver.Options{ - BindAddress: metricsAddr, - }, - LeaderElection: enableLeaderElection, - LeaderElectionID: "a9d608ea.oracle.com", - NewCache: func(config *rest.Config, opts cache.Options) (cache.Cache, error) { - opts.DefaultNamespaces = watchNamespaces - return cache.New(config, opts) - }, - } - - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), opt) - if err != nil { - setupLog.Error(err, "unable to start manager") - os.Exit(1) - } - - // Get Cache - cache := mgr.GetCache() - - // ADB family controllers - if err = (&databasecontroller.AutonomousDatabaseReconciler{ - KubeClient: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("database").WithName("AutonomousDatabase"), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("AutonomousDatabase"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "AutonomousDatabase") - os.Exit(1) - } - if err = (&databasecontroller.AutonomousDatabaseBackupReconciler{ - KubeClient: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseBackup"), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("AutonomousDatabaseBackup"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "AutonomousDatabaseBackup") - os.Exit(1) - } - if err = (&databasecontroller.AutonomousDatabaseRestoreReconciler{ - KubeClient: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseRestore"), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("AutonomousDatabaseRestore"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "AutonomousDatabaseRestore") - os.Exit(1) - } - if err = (&databasecontroller.AutonomousContainerDatabaseReconciler{ - KubeClient: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("AutonomousContainerDatabase"), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("AutonomousContainerDatabase"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "AutonomousContainerDatabase") - os.Exit(1) - } - - if err = (&databasecontroller.SingleInstanceDatabaseReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("database").WithName("SingleInstanceDatabase"), - Scheme: mgr.GetScheme(), - Config: mgr.GetConfig(), - Recorder: mgr.GetEventRecorderFor("SingleInstanceDatabase"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "SingleInstanceDatabase") - os.Exit(1) - } - if err = (&databasecontroller.ShardingDatabaseReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("database").WithName("ShardingDatabase"), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("ShardingDatabase"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "ShardingDatabase") - os.Exit(1) - } - if err = (&databasecontroller.DbcsSystemReconciler{ - KubeClient: mgr.GetClient(), - Logger: ctrl.Log.WithName("controllers").WithName("database").WithName("DbcsSystem"), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("DbcsSystem"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "DbcsSystem") - os.Exit(1) - } - if err = (&databasecontroller.OracleRestDataServiceReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("OracleRestDataService"), - Scheme: mgr.GetScheme(), - Config: mgr.GetConfig(), - Recorder: mgr.GetEventRecorderFor("OracleRestDataService"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "OracleRestDataService") - os.Exit(1) - } - - if err = (&databasecontroller.OracleRestartReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "OracleRestart") - os.Exit(1) - } - - // Set RECONCILE_INTERVAL environment variable if you want to change the default value from 15 secs - interval := os.Getenv("RECONCILE_INTERVAL") - i, err := strconv.ParseInt(interval, 10, 64) - if err != nil { - i = 15 - setupLog.Info("Setting default reconcile period for database-controller", "Secs", i) - } - - // Set ENABLE_WEBHOOKS=false when we run locally to skip webhook part when testing just the controller. Not to be used in production. - if os.Getenv("ENABLE_WEBHOOKS") != "false" { - if err = (&databasev1alpha1.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") - os.Exit(1) - } - if err = (&databasev1alpha1.OracleRestDataService{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") - os.Exit(1) - } - if err = (&databasev4.PDB{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "PDB") - os.Exit(1) - } - if err = (&databasev4.LRPDB{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "LRPDB") - os.Exit(1) - } - if err = (&databasev4.CDB{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "CDB") - os.Exit(1) - } - if err = (&databasev4.LREST{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "LREST") - os.Exit(1) - } - if err = (&databasev1alpha1.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") - os.Exit(1) - } - if err = (&databasev1alpha1.AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseBackup") - os.Exit(1) - } - if err = (&databasev1alpha1.AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseRestore") - os.Exit(1) - } - if err = (&databasev1alpha1.AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") - os.Exit(1) - } - if err = (&databasev4.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") - os.Exit(1) - } - if err = (&databasev4.AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseBackup") - os.Exit(1) - } - if err = (&databasev4.AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseRestore") - os.Exit(1) - } - if err = (&databasev4.AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") - os.Exit(1) - } - if err = (&databasev1alpha1.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") - os.Exit(1) - } - if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") - os.Exit(1) - } - if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") - os.Exit(1) - } - if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") - } - if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") - os.Exit(1) - } - if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") - os.Exit(1) - } - if err = (&databasev4.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") - os.Exit(1) - } - if err = (&observabilityv1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") - os.Exit(1) - } - - if err = (&observabilityv4.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") - os.Exit(1) - } - if err = (&databasev4.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") - os.Exit(1) - } - if err = (&databasev4.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") - os.Exit(1) - } - if err = (&databasev4.OracleRestDataService{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") - os.Exit(1) - } - // PDB Reconciler - if err = (&databasecontroller.PDBReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("PDB"), - Interval: time.Duration(i), - Recorder: mgr.GetEventRecorderFor("PDB"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "PDB") - os.Exit(1) - } - - // LRPDBR Reconciler - if err = (&databasecontroller.LRPDBReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("LRPDB"), - Interval: time.Duration(i), - Recorder: mgr.GetEventRecorderFor("LRPDB"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "LRPDB") - os.Exit(1) - } - - // CDB Reconciler - if err = (&databasecontroller.CDBReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Config: mgr.GetConfig(), - Log: ctrl.Log.WithName("controllers").WithName("CDB"), - Interval: time.Duration(i), - Recorder: mgr.GetEventRecorderFor("CDB"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "CDB") - os.Exit(1) - } - - // LREST Reconciler - if err = (&databasecontroller.LRESTReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Config: mgr.GetConfig(), - Log: ctrl.Log.WithName("controllers").WithName("LREST"), - Interval: time.Duration(i), - Recorder: mgr.GetEventRecorderFor("LREST"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "LREST") - os.Exit(1) - } - - if err = (&dataguardcontroller.DataguardBrokerReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("dataguard").WithName("DataguardBroker"), - Scheme: mgr.GetScheme(), - Config: mgr.GetConfig(), - Recorder: mgr.GetEventRecorderFor("DataguardBroker"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "DataguardBroker") - os.Exit(1) - } - - if err = (&databasecontroller.OrdsSrvsReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - // Config: mgr.GetConfig(), - Recorder: mgr.GetEventRecorderFor("OrdsSrvs"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "OrdsSrvs") - } - - // Observability DatabaseObserver Reconciler - if err = (&observabilitycontroller.DatabaseObserverReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("observability").WithName("DatabaseObserver"), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("DatabaseObserver"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "DatabaseObserver") - os.Exit(1) - } - // +kubebuilder:scaffold:builder - - // Add index for PDB CR to enable mgr to cache PDBs - indexFunc := func(obj client.Object) []string { - return []string{obj.(*databasev4.PDB).Spec.PDBName} - } - if err = cache.IndexField(context.TODO(), &databasev4.PDB{}, "spec.pdbName", indexFunc); err != nil { - setupLog.Error(err, "unable to create index function for ", "controller", "PDB") - os.Exit(1) - } - - indexFunc2 := func(obj client.Object) []string { - return []string{obj.(*databasev4.LRPDB).Spec.LRPDBName} - } - if err = cache.IndexField(context.TODO(), &databasev4.LRPDB{}, "spec.pdbName", indexFunc2); err != nil { - setupLog.Error(err, "unable to create index function for ", "controller", "LRPDB") - os.Exit(1) - } - } - - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) - } -} - -func getWatchNamespace() (map[string]cache.Config, error) { - // WatchNamespaceEnvVar is the constant for env variable WATCH_NAMESPACE - // which specifies the Namespace to watch. - // An empty value means the operator is running with cluster scope. - - var watchNamespaceEnvVar = "WATCH_NAMESPACE" - var nsmap map[string]cache.Config - ns, found := os.LookupEnv(watchNamespaceEnvVar) - values := strings.Split(ns, ",") - if len(values) == 1 && values[0] == "" { - fmt.Printf(":CLUSTER SCOPED:\n") - return nil, nil - } - fmt.Printf(":NAMESPACE SCOPED:\n") - fmt.Printf("WATCH LIST=%s\n", values) - nsmap = make(map[string]cache.Config, len(values)) - if !found { - return nsmap, fmt.Errorf("%s must be set", watchNamespaceEnvVar) - } - - if ns == "" { - return nil, nil - } - - for _, ns := range values { - nsmap[ns] = cache.Config{} - } - - return nsmap, nil - -} diff --git a/main.go b/main.go index 24fdc4a1..1ebef2ec 100644 --- a/main.go +++ b/main.go @@ -307,7 +307,98 @@ func main() { // Set ENABLE_WEBHOOKS=false when we run locally to skip webhook part when testing just the controller. Not to be used in production. if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err = (&databasev1alpha1.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") + os.Exit(1) + } + if err = (&databasev1alpha1.OracleRestDataService{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") + os.Exit(1) + } + if err = (&databasev4.PDB{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "PDB") + os.Exit(1) + } + if err = (&databasev4.LRPDB{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "LRPDB") + os.Exit(1) + } + if err = (&databasev4.CDB{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "CDB") + os.Exit(1) + } + if err = (&databasev4.LREST{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "LREST") + os.Exit(1) + } + if err = (&databasev1alpha1.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") + os.Exit(1) + } + if err = (&databasev1alpha1.AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseBackup") + os.Exit(1) + } + if err = (&databasev1alpha1.AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseRestore") + os.Exit(1) + } + if err = (&databasev1alpha1.AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") + os.Exit(1) + } + if err = (&databasev4.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") + os.Exit(1) + } + if err = (&databasev4.AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseBackup") + os.Exit(1) + } + if err = (&databasev4.AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabaseRestore") + os.Exit(1) + } + if err = (&databasev4.AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousContainerDatabase") + os.Exit(1) + } + if err = (&databasev1alpha1.DataguardBroker{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DataguardBroker") + os.Exit(1) + } + if err = (&databasev1alpha1.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + os.Exit(1) + } + if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } + if err = (&databasev4.ShardingDatabase{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ShardingDatabase") + } + if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + os.Exit(1) + } + if err = (&databasev1alpha1.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } + if err = (&databasev4.DbcsSystem{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DbcsSystem") + os.Exit(1) + } + if err = (&observabilityv1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + os.Exit(1) + } + if err = (&observabilityv4.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + os.Exit(1) + } if err = (&databasev4.SingleInstanceDatabase{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "SingleInstanceDatabase") os.Exit(1) From e6390f4e0b7f7e47c523ecb7d9b086ac8701fa86 Mon Sep 17 00:00:00 2001 From: Ting-Lan Wang Date: Mon, 4 Aug 2025 17:45:39 -0400 Subject: [PATCH 251/414] update adb docs --- .../samples/adb/autonomousdatabase_clone.yaml | 2 +- .../adb/autonomousdatabase_create.yaml | 23 ++--- .../adb/autonomousdatabase_failover.yaml | 17 ++++ .../samples/adb/autonomousdatabase_scale.yaml | 4 +- .../adb/autonomousdatabase_switchover.yaml | 17 ++++ docs/adb/README.md | 84 ++++++++++++++++++- 6 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 config/samples/adb/autonomousdatabase_failover.yaml create mode 100644 config/samples/adb/autonomousdatabase_switchover.yaml diff --git a/config/samples/adb/autonomousdatabase_clone.yaml b/config/samples/adb/autonomousdatabase_clone.yaml index c1216d64..124051c9 100644 --- a/config/samples/adb/autonomousdatabase_clone.yaml +++ b/config/samples/adb/autonomousdatabase_clone.yaml @@ -16,7 +16,7 @@ spec: # The dbName must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. dbName: ClonedADB displayName: ClonedADB - cpuCoreCount: 1 + computeCount: 1 adminPassword: # Comment out k8sSecret and uncomment ociSecret if you pass the admin password using OCI Secret. k8sSecret: diff --git a/config/samples/adb/autonomousdatabase_create.yaml b/config/samples/adb/autonomousdatabase_create.yaml index 776b9bff..3abfb329 100644 --- a/config/samples/adb/autonomousdatabase_create.yaml +++ b/config/samples/adb/autonomousdatabase_create.yaml @@ -14,7 +14,7 @@ spec: # The dbName must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. dbName: NewADB displayName: NewADB - cpuCoreCount: 1 + computeCount: 1 adminPassword: # Comment out k8sSecret and uncomment ociSecret if you pass the admin password using OCI Secret. k8sSecret: @@ -25,37 +25,30 @@ spec: # ocid: ocid1.vaultsecret... dataStorageSizeInTBs: 1 - # networkAccess: - # # Uncomment this block to configure the network access type with the PUBLIC option, which allows secure access from everywhere. - # accessType: PUBLIC - # # Uncomment this block to configure the network access type with the RESTRICTED option. # # This option lets you restrict access by defining access control rules in an Access Control List (ACL). # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. # # Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs. - # accessType: RESTRICTED - # accessControlList: + # whitelistedIps: # - 1.1.1.1 # - 1.1.0.0/16 # - ocid1.vcn... # - ocid1.vcn...;1.1.1.1 # - ocid1.vcn...;1.1.0.0/16 - # isMTLSConnectionRequired: true + # isMtlsConnectionRequired: true # # Uncomment this block to configure the network access type with the PRIVATE option. # # This option assigns a private endpoint, private IP, and hostname to your database. # # Specifying this option allows traffic only from the VCN you specify. # # This allows you to define security rules, ingress/egress, at the Network Security Group (NSG) level and to control traffic to your Autonomous Database. - # accessType: PRIVATE - # privateEndpoint: - # subnetOCID: ocid1.subnet... - # nsgOCIDs: - # - ocid1.networksecuritygroup... - # isMTLSConnectionRequired: true + # subnetId: ocid1.subnet... + # nsgIds: + # - ocid1.networksecuritygroup... + # isMtlsConnectionRequired: true # # Uncomment this block to configure the network access of an dedicated Autonomous Database (ADB-D) with an access control list. # isAccessControlEnabled: true - # accessControlList: + # whitelistedIps: # - 1.1.1.1 # - 1.1.0.0/16 diff --git a/config/samples/adb/autonomousdatabase_failover.yaml b/config/samples/adb/autonomousdatabase_failover.yaml new file mode 100644 index 00000000..3182d607 --- /dev/null +++ b/config/samples/adb/autonomousdatabase_failover.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v4 +kind: AutonomousDatabase +metadata: + name: autonomousdatabase-sample +spec: + action: Failover + details: + id: ocid1.autonomousdatabase... + # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. + ociConfig: + configMapName: oci-cred + # Comment out secretName if using OKE workload identity + secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/adb/autonomousdatabase_scale.yaml b/config/samples/adb/autonomousdatabase_scale.yaml index 33fe6938..3698bda0 100644 --- a/config/samples/adb/autonomousdatabase_scale.yaml +++ b/config/samples/adb/autonomousdatabase_scale.yaml @@ -10,8 +10,8 @@ spec: action: Update details: id: ocid1.autonomousdatabase... - # Your database's OPCU core count - cpuCoreCount: 2 + # Your database's compute count + computeCount: 2 # Your database's storage size in TB dataStorageSizeInTBs: 2 # Enable/Disable auto scaling for your database diff --git a/config/samples/adb/autonomousdatabase_switchover.yaml b/config/samples/adb/autonomousdatabase_switchover.yaml new file mode 100644 index 00000000..8cd1fce1 --- /dev/null +++ b/config/samples/adb/autonomousdatabase_switchover.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +apiVersion: database.oracle.com/v4 +kind: AutonomousDatabase +metadata: + name: autonomousdatabase-sample +spec: + action: Switchover + details: + id: ocid1.autonomousdatabase... + # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. + ociConfig: + configMapName: oci-cred + # Comment out secretName if using OKE workload identity + secretName: oci-privatekey \ No newline at end of file diff --git a/docs/adb/README.md b/docs/adb/README.md index e8164697..ce21252c 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -26,6 +26,8 @@ After you create the resource, you can use the operator to perform the following * [Stop/Start/Terminate](#stopstartterminate) an Autonomous Database * [Delete the resource](#delete-the-resource) from the cluster * [Clone](#clone-an-existing-autonomous-database) an existing Autonomous Database +* [Switchover](#switchover-an-existing-autonomous-database) an existing Autonomous Database +* [Perform Manual Failover](#manually-failover-an-existing-autonomous-database) to an existing Autonomous Database To debug the Oracle Autonomous Databases with Oracle Database Operator, see [Debugging and troubleshooting](#debugging-and-troubleshooting) @@ -113,7 +115,8 @@ To provision an Autonomous Database that will map objects in your cluster, compl compartmentId: ocid1.compartment... dbName: NewADB displayName: NewADB - cpuCoreCount: 1 + computeModel: ECPU + computeCount: 1 adminPassword: k8sSecret: name: admin-password # use the name of the secret from step 2 @@ -187,7 +190,7 @@ The operator also generates the `AutonomousBackup` custom resources if a databas > Note: this operation requires an `AutonomousDatabase` object to be in your cluster. To use this example, either the provision operation or the bind operation must be completed, and the operator must be authorized with API Key Authentication. -You can scale up or scale down the Oracle Autonomous Database OCPU core count or storage by updating the `cpuCoreCount` and `dataStorageSizeInTBs` parameters. The `isAutoScalingEnabled` indicates whether auto scaling is enabled. In this example, the CPU count and storage size (TB) are scaled up to 2 and the auto-scaling is turned off by updating the `autonomousdatabase-sample` custom resource. +You can scale up or scale down the Oracle Autonomous Database OCPU core count or storage by updating the `computeCount` and `dataStorageSizeInTBs` parameters. The `isAutoScalingEnabled` indicates whether auto scaling is enabled. In this example, the CPU count and storage size (TB) are scaled up to 2 and the auto-scaling is turned off by updating the `autonomousdatabase-sample` custom resource. 1. An example YAML file is available here: [config/samples/adb/autonomousdatabase_scale.yaml](./../../config/samples/adb/autonomousdatabase_scale.yaml) @@ -201,7 +204,7 @@ You can scale up or scale down the Oracle Autonomous Database OCPU core count or action: Update details: id: ocid1.autonomousdatabase... - cpuCoreCount: 2 + computeCount: 2 dataStorageSizeInTBs: 2 isAutoScalingEnabled: false ociConfig: @@ -487,7 +490,8 @@ To clone an existing Autonomous Database, complete these steps: compartmentId: ocid1.compartment... OR ocid1.tenancy... dbName: ClonedADB displayName: ClonedADB - cpuCoreCount: 1 + computeModel: ECPU + computeCount: 1 adminPassword: k8sSecret: name: admin-password @@ -508,6 +512,78 @@ To clone an existing Autonomous Database, complete these steps: Now, you can verify that a cloned database with name "ClonedADB" is being provisioned on the Cloud Console. +## Switchover an existing Autonomous Database + +> Note: this operation requires an `AutonomousDatabase` object to be in your cluster. This example assumes the provision operation or the bind operation has been done by the users and the operator is authorized with API Key Authentication. + +To switchover an existing Autonomous Database, complete these steps: + +1. Add the following fields to the AutonomousDatabase resource definition. An example YAML file is available here: [config/samples/adb/autonomousdatabase_switchover.yaml](./../../config/samples/adb/autonomousdatabase_switchover.yaml) + | Attribute | Type | Description | Required? | + |----|----|----|----| + | `spec.details.id` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the source Autonomous Database that you will clone to create a new Autonomous Database. | Yes | + | `spec.ociConfig` | dictionary | Not required when the Operator is authorized with [Instance Principal](./ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from the [Authorized with API Key Authentication](./ADB_PREREQUISITES.md#authorized-with-api-key-authentication) section. | Conditional | + | `spec.ociConfig.configMapName` | string | Name of the ConfigMap that holds the local OCI configuration | Conditional | + | `spec.ociConfig.secretName`| string | Name of the K8s Secret that holds the private key value | Conditional | + + ```yaml + --- + apiVersion: database.oracle.com/v4 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + action: Switchover + details: + id: ocid1.autonomousdatabase... + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml + + ```sh + kubectl apply -f config/samples/adb/autonomousdatabase_switchover.yaml + autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured + ``` + +## Manually failover an existing Autonomous Database + +> Note: this operation requires an `AutonomousDatabase` object to be in your cluster. This example assumes the provision operation or the bind operation has been done by the users and the operator is authorized with API Key Authentication. + +To manually failover an existing Autonomous Database, complete these steps: + +1. Add the following fields to the AutonomousDatabase resource definition. An example YAML file is available here: [config/samples/adb/autonomousdatabase_failover.yaml](./../../config/samples/adb/autonomousdatabase_failover.yaml) + | Attribute | Type | Description | Required? | + |----|----|----|----| + | `spec.details.id` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the source Autonomous Database that you will clone to create a new Autonomous Database. | Yes | + | `spec.ociConfig` | dictionary | Not required when the Operator is authorized with [Instance Principal](./ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from the [Authorized with API Key Authentication](./ADB_PREREQUISITES.md#authorized-with-api-key-authentication) section. | Conditional | + | `spec.ociConfig.configMapName` | string | Name of the ConfigMap that holds the local OCI configuration | Conditional | + | `spec.ociConfig.secretName`| string | Name of the K8s Secret that holds the private key value | Conditional | + + ```yaml + --- + apiVersion: database.oracle.com/v4 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + action: Failover + details: + id: ocid1.autonomousdatabase... + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml + + ```sh + kubectl apply -f config/samples/adb/autonomousdatabase_failover.yaml + autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured + ``` + ## Roles and Privileges requirements for Oracle Autonomous Database Controller Autonomous Database controller uses Kubernetes objects such as: From a4b7bc9857cf587982d040cba8c99b0ed6b72bb0 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Tue, 5 Aug 2025 15:39:37 +0000 Subject: [PATCH 252/414] Fix missing adb validating webhook and update docs --- .../v1alpha1/autonomousdatabase_types.go | 2 +- .../v1alpha1/autonomousdatabase_webhook.go | 1 + apis/database/v4/autonomousdatabase_types.go | 2 +- .../database/v4/autonomousdatabase_webhook.go | 2 + .../v1/databaseobserver_webhook.go | 1 - .../v1alpha1/databaseobserver_webhook.go | 1 - .../v4/databaseobserver_webhook.go | 1 - ...tabase.oracle.com_autonomousdatabases.yaml | 12 ++--- ...vability.oracle.com_databaseobservers.yaml | 24 +++++----- .../samples/adb/autonomousdatabase_clone.yaml | 1 + .../adb/autonomousdatabase_create.yaml | 47 +++++++++---------- ...> autonomousdatabase_download_wallet.yaml} | 0 ...> autonomousdatabase_manual_failover.yaml} | 0 ...tonomousdatabase_stop_start_terminate.yaml | 2 +- .../adb/autonomousdatabase_update_mtls.yaml | 2 +- ...onomousdatabase_update_network_access.yaml | 20 ++++---- config/webhook/manifests.yaml | 20 ++++++++ docs/adb/NETWORK_ACCESS_OPTIONS.md | 19 ++++---- docs/adb/README.md | 4 +- 19 files changed, 89 insertions(+), 72 deletions(-) rename config/samples/adb/{autonomousdatabase_wallet.yaml => autonomousdatabase_download_wallet.yaml} (100%) rename config/samples/adb/{autonomousdatabase_failover.yaml => autonomousdatabase_manual_failover.yaml} (100%) diff --git a/apis/database/v1alpha1/autonomousdatabase_types.go b/apis/database/v1alpha1/autonomousdatabase_types.go index ba0eb4e6..5cb1aa80 100644 --- a/apis/database/v1alpha1/autonomousdatabase_types.go +++ b/apis/database/v1alpha1/autonomousdatabase_types.go @@ -194,7 +194,7 @@ type ConnectionStringSpec struct { // +kubebuilder:printcolumn:JSONPath=".spec.details.dbName",name="Db Name",type=string // +kubebuilder:printcolumn:JSONPath=".status.lifecycleState",name="State",type=string // +kubebuilder:printcolumn:JSONPath=".spec.details.isDedicated",name="Dedicated",type=string -// +kubebuilder:printcolumn:JSONPath=".spec.details.cpuCoreCount",name="OCPUs",type=integer +// +kubebuilder:printcolumn:JSONPath=".spec.details.computeCount",name="Compute Count",type=number // +kubebuilder:printcolumn:JSONPath=".spec.details.dataStorageSizeInTBs",name="Storage (TB)",type=integer // +kubebuilder:printcolumn:JSONPath=".spec.details.dbWorkload",name="Workload Type",type=string // +kubebuilder:printcolumn:JSONPath=".status.timeCreated",name="Created",type=string diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go index f33444a1..8f5c551a 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -58,6 +58,7 @@ var autonomousdatabaselog = logf.Log.WithName("autonomousdatabase-resource") func (r *AutonomousDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithValidator(&AutonomousDatabase{}). Complete() } diff --git a/apis/database/v4/autonomousdatabase_types.go b/apis/database/v4/autonomousdatabase_types.go index 9db63c72..6643e55c 100644 --- a/apis/database/v4/autonomousdatabase_types.go +++ b/apis/database/v4/autonomousdatabase_types.go @@ -197,7 +197,7 @@ type ConnectionStringSpec struct { // +kubebuilder:printcolumn:JSONPath=".spec.details.dbName",name="Db Name",type=string // +kubebuilder:printcolumn:JSONPath=".status.lifecycleState",name="State",type=string // +kubebuilder:printcolumn:JSONPath=".spec.details.isDedicated",name="Dedicated",type=string -// +kubebuilder:printcolumn:JSONPath=".spec.details.cpuCoreCount",name="OCPUs",type=integer +// +kubebuilder:printcolumn:JSONPath=".spec.details.computeCount",name="Compute Count",type=number // +kubebuilder:printcolumn:JSONPath=".spec.details.dataStorageSizeInTBs",name="Storage (TB)",type=integer // +kubebuilder:printcolumn:JSONPath=".spec.details.dbWorkload",name="Workload Type",type=string // +kubebuilder:printcolumn:JSONPath=".status.timeCreated",name="Created",type=string diff --git a/apis/database/v4/autonomousdatabase_webhook.go b/apis/database/v4/autonomousdatabase_webhook.go index 4daa920f..bc025691 100644 --- a/apis/database/v4/autonomousdatabase_webhook.go +++ b/apis/database/v4/autonomousdatabase_webhook.go @@ -58,10 +58,12 @@ var autonomousdatabaselog = logf.Log.WithName("autonomousdatabase-resource") func (r *AutonomousDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithValidator(r). Complete() } // +kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v4-autonomousdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v4,name=vautonomousdatabasev4.kb.io,admissionReviewVersions=v1 + var _ webhook.CustomValidator = &AutonomousDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/observability/v1/databaseobserver_webhook.go b/apis/observability/v1/databaseobserver_webhook.go index 1aff3d90..725069cb 100644 --- a/apis/observability/v1/databaseobserver_webhook.go +++ b/apis/observability/v1/databaseobserver_webhook.go @@ -69,7 +69,6 @@ const ( func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). - For(&DatabaseObserver{}). WithDefaulter(r). WithValidator(r). Complete() diff --git a/apis/observability/v1alpha1/databaseobserver_webhook.go b/apis/observability/v1alpha1/databaseobserver_webhook.go index b6c2c2fe..c5080445 100644 --- a/apis/observability/v1alpha1/databaseobserver_webhook.go +++ b/apis/observability/v1alpha1/databaseobserver_webhook.go @@ -69,7 +69,6 @@ const ( func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). - For(&DatabaseObserver{}). WithDefaulter(r). WithValidator(r). Complete() diff --git a/apis/observability/v4/databaseobserver_webhook.go b/apis/observability/v4/databaseobserver_webhook.go index 94f906cd..832f15dc 100644 --- a/apis/observability/v4/databaseobserver_webhook.go +++ b/apis/observability/v4/databaseobserver_webhook.go @@ -69,7 +69,6 @@ const ( func (r *DatabaseObserver) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). - For(&DatabaseObserver{}). WithDefaulter(r). WithValidator(r). Complete() diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index c86ced26..b48a7543 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -30,9 +30,9 @@ spec: - jsonPath: .spec.details.isDedicated name: Dedicated type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer + - jsonPath: .spec.details.computeCount + name: computeCount + type: number - jsonPath: .spec.details.dataStorageSizeInTBs name: Storage (TB) type: integer @@ -365,9 +365,9 @@ spec: - jsonPath: .spec.details.isDedicated name: Dedicated type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer + - jsonPath: .spec.details.computeCount + name: computeCount + type: number - jsonPath: .spec.details.dataStorageSizeInTBs name: Storage (TB) type: integer diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index 298f9d4e..925d7e54 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -302,14 +302,14 @@ spec: type: array log: properties: - filename: + destination: type: string - path: + disable: + type: boolean + filename: type: string volume: properties: - name: - type: string persistentVolumeClaim: properties: claimName: @@ -2625,14 +2625,14 @@ spec: type: array log: properties: - filename: + destination: type: string - path: + disable: + type: boolean + filename: type: string volume: properties: - name: - type: string persistentVolumeClaim: properties: claimName: @@ -4948,14 +4948,14 @@ spec: type: array log: properties: - filename: + destination: type: string - path: + disable: + type: boolean + filename: type: string volume: properties: - name: - type: string persistentVolumeClaim: properties: claimName: diff --git a/config/samples/adb/autonomousdatabase_clone.yaml b/config/samples/adb/autonomousdatabase_clone.yaml index 124051c9..3a01d606 100644 --- a/config/samples/adb/autonomousdatabase_clone.yaml +++ b/config/samples/adb/autonomousdatabase_clone.yaml @@ -16,6 +16,7 @@ spec: # The dbName must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. dbName: ClonedADB displayName: ClonedADB + computeModel: ECPU computeCount: 1 adminPassword: # Comment out k8sSecret and uncomment ociSecret if you pass the admin password using OCI Secret. diff --git a/config/samples/adb/autonomousdatabase_create.yaml b/config/samples/adb/autonomousdatabase_create.yaml index 3abfb329..682a271d 100644 --- a/config/samples/adb/autonomousdatabase_create.yaml +++ b/config/samples/adb/autonomousdatabase_create.yaml @@ -14,6 +14,7 @@ spec: # The dbName must begin with an alphabetic character and can contain a maximum of 14 alphanumeric characters. Special characters are not permitted. The database name must be unique in the tenancy. dbName: NewADB displayName: NewADB + computeModel: ECPU computeCount: 1 adminPassword: # Comment out k8sSecret and uncomment ociSecret if you pass the admin password using OCI Secret. @@ -25,32 +26,30 @@ spec: # ocid: ocid1.vaultsecret... dataStorageSizeInTBs: 1 - # # Uncomment this block to configure the network access type with the RESTRICTED option. - # # This option lets you restrict access by defining access control rules in an Access Control List (ACL). - # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. - # # Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs. - # whitelistedIps: - # - 1.1.1.1 - # - 1.1.0.0/16 - # - ocid1.vcn... - # - ocid1.vcn...;1.1.1.1 - # - ocid1.vcn...;1.1.0.0/16 - # isMtlsConnectionRequired: true + # # Uncomment this block to configure the network access type with the RESTRICTED option. + # # This option lets you restrict access by defining access control rules in an Access Control List (ACL). + # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. + # # Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs. + # whitelistedIps: + # - 1.1.1.1 + # - 1.1.0.0/16 + # - ocid1.vcn... + # - ocid1.vcn...;1.1.1.1 + # - ocid1.vcn...;1.1.0.0/16 + # isMtlsConnectionRequired: true - # # Uncomment this block to configure the network access type with the PRIVATE option. - # # This option assigns a private endpoint, private IP, and hostname to your database. - # # Specifying this option allows traffic only from the VCN you specify. - # # This allows you to define security rules, ingress/egress, at the Network Security Group (NSG) level and to control traffic to your Autonomous Database. - # subnetId: ocid1.subnet... - # nsgIds: - # - ocid1.networksecuritygroup... - # isMtlsConnectionRequired: true + # # Uncomment this block to configure the network access type with the PRIVATE option. + # # This option assigns a private endpoint, private IP, and hostname to your database. + # # Specifying this option allows traffic only from the VCN you specify. + # # This allows you to define security rules, ingress/egress, at the Network Security Group (NSG) level and to control traffic to your Autonomous Database. + # subnetId: ocid1.subnet... + # isMtlsConnectionRequired: true - # # Uncomment this block to configure the network access of an dedicated Autonomous Database (ADB-D) with an access control list. - # isAccessControlEnabled: true - # whitelistedIps: - # - 1.1.1.1 - # - 1.1.0.0/16 + # # Uncomment this block to configure the network access of an dedicated Autonomous Database (ADB-D) with an access control list. + # isAccessControlEnabled: true + # whitelistedIps: + # - 1.1.1.1 + # - 1.1.0.0/16 # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: diff --git a/config/samples/adb/autonomousdatabase_wallet.yaml b/config/samples/adb/autonomousdatabase_download_wallet.yaml similarity index 100% rename from config/samples/adb/autonomousdatabase_wallet.yaml rename to config/samples/adb/autonomousdatabase_download_wallet.yaml diff --git a/config/samples/adb/autonomousdatabase_failover.yaml b/config/samples/adb/autonomousdatabase_manual_failover.yaml similarity index 100% rename from config/samples/adb/autonomousdatabase_failover.yaml rename to config/samples/adb/autonomousdatabase_manual_failover.yaml diff --git a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml index 56630045..f6daf426 100644 --- a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml +++ b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml @@ -8,7 +8,7 @@ metadata: name: autonomousdatabase-sample spec: - action: Stop # Use the value "Start" to start the database + action: Stop # Use the value "Start" to start the database, or "Terminate" to terminate the database details: id: ocid1.autonomousdatabase... # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. diff --git a/config/samples/adb/autonomousdatabase_update_mtls.yaml b/config/samples/adb/autonomousdatabase_update_mtls.yaml index 988da9a6..87c98bbc 100644 --- a/config/samples/adb/autonomousdatabase_update_mtls.yaml +++ b/config/samples/adb/autonomousdatabase_update_mtls.yaml @@ -11,7 +11,7 @@ spec: details: id: ocid1.autonomousdatabase... # Set the patameter to false to allow both TLS and mutual TLS (mTLS) authentication, or true to require mTLS connections and disallow TLS connections. - isMTLSConnectionRequired: true + isMtlsConnectionRequired: true # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: diff --git a/config/samples/adb/autonomousdatabase_update_network_access.yaml b/config/samples/adb/autonomousdatabase_update_network_access.yaml index a8aeb251..2c704d53 100644 --- a/config/samples/adb/autonomousdatabase_update_network_access.yaml +++ b/config/samples/adb/autonomousdatabase_update_network_access.yaml @@ -11,15 +11,13 @@ spec: details: id: ocid1.autonomousdatabase... # # Allow secure access from everywhere. Uncomment one of the following field depends on your network access configuration. - # accessControlList: - # - - # privateEndpoint: "" + # privateEndpointLabel: "" # # Uncomment this block to configure the network access type with the RESTRICTED option. # # This option lets you restrict access by defining access control rules in an Access Control List (ACL). - # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. + # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. # # Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs. - # accessControlList: + # whitelistedIps: # - 1.1.1.1 # - 1.1.0.0/16 # - ocid1.vcn... @@ -27,16 +25,16 @@ spec: # - ocid1.vcn...;1.1.0.0/16 # # Uncomment this block to configure the network access type with the PRIVATE option. - # # This option assigns a private endpoint, private IP, and hostname to your database. + # # This option assigns a private endpoint, private IP, and hostname to your database. # # Specifying this option allows traffic only from the VCN you specify. # # This allows you to define security rules, ingress/egress, at the Network Security Group (NSG) level and to control traffic to your Autonomous Database. - # privateEndpoint: - # subnetOCID: ocid1.subnet... - # nsgOCIDs: # Optional - # - ocid1.networksecuritygroup... + # subnetId: ocid1.subnet... + # nsgIds: + # - ocid1.networksecuritygroup... # # Uncomment this block to configure the network access of an dedicated Autonomous Database (ADB-D) with an access control list. - # accessControlList: + # isAccessControlEnabled: true + # whitelistedIps: # - 1.1.1.1 # - 1.1.0.0/16 diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index ba18e3af..f73e7b8a 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -397,6 +397,26 @@ webhooks: resources: - autonomousdatabaserestores sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v4-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/docs/adb/NETWORK_ACCESS_OPTIONS.md b/docs/adb/NETWORK_ACCESS_OPTIONS.md index e7eb0a56..e945df04 100644 --- a/docs/adb/NETWORK_ACCESS_OPTIONS.md +++ b/docs/adb/NETWORK_ACCESS_OPTIONS.md @@ -17,7 +17,7 @@ Review the following options available to you with Autonomous Database. ## Configuring Network Access with Allowing Secure Access from Anywhere -Before changing the Network Access to Allowing Secure Access from Anywhere, ensure that your network security protocol requries only mTLS (Mutual TLS) authentication. For more details, see: [Allow both TLS and mutual TLS (mTLS) authentication](#allow-both-tls-and-mutual-tls-mtls-authentication). If mTLS enforcement is already enabled on your Autonomous Database, then you can skip this step. +Before changing the Network Access to Allowing Secure Access from Anywhere, ensure that the enforcement mTLS (Mutual TLS) authentication is set to **true** in your network security protocol. For more details, see: [Allow both TLS and mutual TLS (mTLS) authentication](#allow-both-tls-and-mutual-tls-mtls-authentication). If mTLS enforcement is already enabled on your Autonomous Database, then you can skip this step. To specify that Autonomous Database can be connected from any location with a valid credential, complete one of the following procedures, based on your network access configuration. @@ -58,7 +58,7 @@ To specify that Autonomous Database can be connected from any location with a va | Attribute | Type | Description | |----|----|----| - | `privateEndpointLabel` | string | The hostname prefix for the resource. | + | `privateEndpointLabel` | string | The resource's private endpoint label.
- Setting the endpoint label to a non-empty string creates a private endpoint database.
- Resetting the endpoint label to an empty string, after the creation of the private endpoint database, changes the private endpoint database to a public endpoint database.
- Setting the endpoint label to a non-empty string value, updates to a new private endpoint database, when the database is disabled and re-enabled.

This setting cannot be updated in parallel with any of the following: licenseModel, dbEdition, cpuCoreCount, computeCount, computeModel, adminPassword, whitelistedIps, isMTLSConnectionRequired, dbWorkload, dbVersion, isRefreshable, dbName, scheduledOperations, dbToolsDetails, or isFreeTier. | ```yaml --- @@ -104,14 +104,13 @@ To configure Network Access with ACLs, complete this procedure. action: Update details: autonomousDatabaseOCID: ocid1.autonomousdatabase... - networkAccess: - # Restrict access by defining access control rules in an Access Control List (ACL). - whitelistedIps: - - 1.1.1.1 - - 1.1.0.0/16 - - ocid1.vcn... - - ocid1.vcn...;1.1.1.1 - - ocid1.vcn...;1.1.0.0/16 + # Restrict access by defining access control rules in an Access Control List (ACL). + whitelistedIps: + - 1.1.1.1 + - 1.1.0.0/16 + - ocid1.vcn... + - ocid1.vcn...;1.1.1.1 + - ocid1.vcn...;1.1.0.0/16 ociConfig: configMapName: oci-cred secretName: oci-privatekey diff --git a/docs/adb/README.md b/docs/adb/README.md index ce21252c..e23c3c7b 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -313,7 +313,7 @@ A client Wallet is required to connect to a shared Oracle Autonomous Database. U \* The password must be at least 8 characters long and must include at least 1 letter and either 1 numeric character or 1 special character. -2. Update the example [config/samples/adb/autonomousdatabase_wallet.yaml](./../../config/samples/adb/autonomousdatabase_wallet.yaml) +2. Update the example [config/samples/adb/autonomousdatabase_download_wallet.yaml](./../../config/samples/adb/autonomousdatabase_download_wallet.yaml) ```yaml --- @@ -554,7 +554,7 @@ To switchover an existing Autonomous Database, complete these steps: To manually failover an existing Autonomous Database, complete these steps: -1. Add the following fields to the AutonomousDatabase resource definition. An example YAML file is available here: [config/samples/adb/autonomousdatabase_failover.yaml](./../../config/samples/adb/autonomousdatabase_failover.yaml) +1. Add the following fields to the AutonomousDatabase resource definition. An example YAML file is available here: [config/samples/adb/autonomousdatabase_manual_failover.yaml](./../../config/samples/adb/autonomousdatabase_manual_failover.yaml) | Attribute | Type | Description | Required? | |----|----|----|----| | `spec.details.id` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the source Autonomous Database that you will clone to create a new Autonomous Database. | Yes | From d4b709c1967d2167afaf2e9372e74e74a34c2b77 Mon Sep 17 00:00:00 2001 From: Ting-Lan Wang Date: Tue, 5 Aug 2025 12:43:18 -0400 Subject: [PATCH 253/414] Include zip in the wallet secret --- .../autonomouscontainerdatabase_webhook.go | 1 + .../v1alpha1/autonomousdatabase_conversion.go | 8 +++---- .../autonomousdatabasebackup_webhook.go | 1 + .../autonomousdatabaserestore_webhook.go | 1 + apis/database/v4/adbfamily_common_spec.go | 2 +- .../v4/autonomouscontainerdatabase_webhook.go | 1 + .../v4/autonomousdatabasebackup_webhook.go | 9 ++++---- .../v4/autonomousdatabaserestore_webhook.go | 5 ++-- apis/database/v4/zz_generated.deepcopy.go | 4 ++-- commons/adb_family/utils.go | 2 +- commons/k8s/create.go | 3 +++ commons/oci/wallet.go | 6 ++--- ...tabase.oracle.com_autonomousdatabases.yaml | 4 ++-- .../database/autonomousdatabase_controller.go | 23 ++++++++----------- .../autonomousdatabasebackup_controller.go | 4 ++-- .../autonomousdatabaserestore_controller.go | 4 ++-- 16 files changed, 42 insertions(+), 36 deletions(-) diff --git a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go index e90dfd40..b71600db 100644 --- a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go @@ -58,6 +58,7 @@ var autonomouscontainerdatabaselog = logf.Log.WithName("autonomouscontainerdatab func (r *AutonomousContainerDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithValidator(r). Complete() } diff --git a/apis/database/v1alpha1/autonomousdatabase_conversion.go b/apis/database/v1alpha1/autonomousdatabase_conversion.go index ffccc181..bf7f4fd8 100644 --- a/apis/database/v1alpha1/autonomousdatabase_conversion.go +++ b/apis/database/v1alpha1/autonomousdatabase_conversion.go @@ -224,7 +224,7 @@ func (src *AutonomousDatabaseBackup) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*v4.AutonomousDatabaseBackup) dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name - dst.Spec.Target.OciAdb.OCID = src.Spec.Target.OciAdb.Ocid + dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.Ocid dst.Spec.DisplayName = src.Spec.DisplayName dst.Spec.AutonomousDatabaseBackupOCID = src.Spec.AutonomousDatabaseBackupOCID dst.Spec.IsLongTermBackup = src.Spec.IsLongTermBackup @@ -250,7 +250,7 @@ func (dst *AutonomousDatabaseBackup) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*v4.AutonomousDatabaseBackup) dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name - dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.OCID + dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.Ocid dst.Spec.DisplayName = src.Spec.DisplayName dst.Spec.AutonomousDatabaseBackupOCID = src.Spec.AutonomousDatabaseBackupOCID dst.Spec.IsLongTermBackup = src.Spec.IsLongTermBackup @@ -276,7 +276,7 @@ func (src *AutonomousDatabaseRestore) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*v4.AutonomousDatabaseRestore) dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name - dst.Spec.Target.OciAdb.OCID = src.Spec.Target.OciAdb.Ocid + dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.Ocid dst.Spec.Source.K8sAdbBackup.Name = src.Spec.Source.K8sAdbBackup.Name dst.Spec.Source.PointInTime.Timestamp = src.Spec.Source.PointInTime.Timestamp dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName @@ -298,7 +298,7 @@ func (dst *AutonomousDatabaseRestore) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*v4.AutonomousDatabaseRestore) dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name - dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.OCID + dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.Ocid dst.Spec.Source.K8sAdbBackup.Name = src.Spec.Source.K8sAdbBackup.Name dst.Spec.Source.PointInTime.Timestamp = src.Spec.Source.PointInTime.Timestamp dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go index 9feb6440..2ee6dc22 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go @@ -58,6 +58,7 @@ var autonomousdatabasebackuplog = logf.Log.WithName("autonomousdatabasebackup-re func (r *AutonomousDatabaseBackup) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithValidator(r). Complete() } diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go index 018d5521..b1f7278f 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go @@ -59,6 +59,7 @@ var autonomousdatabaserestorelog = logf.Log.WithName("autonomousdatabaserestore- func (r *AutonomousDatabaseRestore) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithValidator(r). Complete() } diff --git a/apis/database/v4/adbfamily_common_spec.go b/apis/database/v4/adbfamily_common_spec.go index 87434852..1eb0f323 100644 --- a/apis/database/v4/adbfamily_common_spec.go +++ b/apis/database/v4/adbfamily_common_spec.go @@ -57,7 +57,7 @@ type K8sAdbSpec struct { } type OciAdbSpec struct { - OCID *string `json:"ocid,omitempty"` + Ocid *string `json:"ocid,omitempty"` } // TargetSpec defines the spec of the target for backup/restore runs. diff --git a/apis/database/v4/autonomouscontainerdatabase_webhook.go b/apis/database/v4/autonomouscontainerdatabase_webhook.go index 5187ffab..f5b6a8d8 100644 --- a/apis/database/v4/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v4/autonomouscontainerdatabase_webhook.go @@ -57,6 +57,7 @@ var autonomouscontainerdatabaselog = logf.Log.WithName("autonomouscontainerdatab func (r *AutonomousContainerDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithValidator(r). Complete() } diff --git a/apis/database/v4/autonomousdatabasebackup_webhook.go b/apis/database/v4/autonomousdatabasebackup_webhook.go index 0cf1adc2..cb409660 100644 --- a/apis/database/v4/autonomousdatabasebackup_webhook.go +++ b/apis/database/v4/autonomousdatabasebackup_webhook.go @@ -58,6 +58,7 @@ var autonomousdatabasebackuplog = logf.Log.WithName("autonomousdatabasebackup-re func (r *AutonomousDatabaseBackup) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithValidator(r). Complete() } @@ -84,12 +85,12 @@ func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runti } } - if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.OCID == nil { + if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.Ocid == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.OCID != nil { + if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.Ocid != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB or ociADB, but not both")) } @@ -122,8 +123,8 @@ func (r *AutonomousDatabaseBackup) ValidateUpdate(ctx context.Context, oldObj, n field.Forbidden(field.NewPath("spec").Child("target").Child("k8sADB").Child("name"), "cannot assign a new name to the target")) } - if oldBackup.Spec.Target.OciAdb.OCID != nil && r.Spec.Target.OciAdb.OCID != nil && - *oldBackup.Spec.Target.OciAdb.OCID != *r.Spec.Target.OciAdb.OCID { + if oldBackup.Spec.Target.OciAdb.Ocid != nil && r.Spec.Target.OciAdb.Ocid != nil && + *oldBackup.Spec.Target.OciAdb.Ocid != *r.Spec.Target.OciAdb.Ocid { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target").Child("ociADB").Child("ocid"), "cannot assign a new ocid to the target")) } diff --git a/apis/database/v4/autonomousdatabaserestore_webhook.go b/apis/database/v4/autonomousdatabaserestore_webhook.go index dd59f358..f9f18528 100644 --- a/apis/database/v4/autonomousdatabaserestore_webhook.go +++ b/apis/database/v4/autonomousdatabaserestore_webhook.go @@ -58,6 +58,7 @@ var autonomousdatabaserestorelog = logf.Log.WithName("autonomousdatabaserestore- func (r *AutonomousDatabaseRestore) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithValidator(r). Complete() } @@ -85,12 +86,12 @@ func (r *AutonomousDatabaseRestore) ValidateCreate(ctx context.Context, obj runt } // Validate the target ADB - if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.OCID == nil { + if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.Ocid == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.OCID != nil { + if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.Ocid != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB.name or ociADB.ocid, but not both")) } diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 52ad8bc6..37e36d3b 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -2829,8 +2829,8 @@ func (in *OciAcdSpec) DeepCopy() *OciAcdSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OciAdbSpec) DeepCopyInto(out *OciAdbSpec) { *out = *in - if in.OCID != nil { - in, out := &in.OCID, &out.OCID + if in.Ocid != nil { + in, out := &in.Ocid, &out.Ocid *out = new(string) **out = **in } diff --git a/commons/adb_family/utils.go b/commons/adb_family/utils.go index 591b3130..67dc673a 100644 --- a/commons/adb_family/utils.go +++ b/commons/adb_family/utils.go @@ -62,7 +62,7 @@ func VerifyTargetAdb(kubeClient client.Client, target dbv4.TargetSpec, namespace } else { // Find the target ADB using the ADB OCID - ownerAdb, err = k8s.FetchAutonomousDatabaseWithOCID(kubeClient, namespace, *target.OciAdb.OCID) + ownerAdb, err = k8s.FetchAutonomousDatabaseWithOCID(kubeClient, namespace, *target.OciAdb.Ocid) if err != nil { return nil, err } diff --git a/commons/k8s/create.go b/commons/k8s/create.go index cd836af7..ddfcb485 100644 --- a/commons/k8s/create.go +++ b/commons/k8s/create.go @@ -94,6 +94,9 @@ func CreateAutonomousBackup(kubeClient client.Client, K8sAdb: dbv4.K8sAdbSpec{ Name: common.String(ownerAdb.Name), }, + OciAdb: dbv4.OciAdbSpec{ + Ocid: backupSummary.AutonomousDatabaseId, + }, }, DisplayName: backupSummary.DisplayName, AutonomousDatabaseBackupOCID: backupSummary.Id, diff --git a/commons/oci/wallet.go b/commons/oci/wallet.go index 076460b1..0cc2b434 100644 --- a/commons/oci/wallet.go +++ b/commons/oci/wallet.go @@ -41,7 +41,7 @@ package oci import ( "archive/zip" "io" - "io/ioutil" + "os" "strings" ) @@ -62,7 +62,7 @@ func ExtractWallet(content io.ReadCloser) (map[string][]byte, error) { func saveWalletZip(content io.ReadCloser) (string, error) { // Create a temp file wallet*.zip const walletFileName = "wallet*.zip" - outZip, err := ioutil.TempFile("", walletFileName) + outZip, err := os.CreateTemp("", walletFileName) if err != nil { return "", err } @@ -91,7 +91,7 @@ func unzipWallet(path string) (map[string][]byte, error) { return files, err } - content, err := ioutil.ReadAll(reader) + content, err := io.ReadAll(reader) if err != nil { return files, err } diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index b48a7543..75aa6644 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -31,7 +31,7 @@ spec: name: Dedicated type: string - jsonPath: .spec.details.computeCount - name: computeCount + name: Compute Count type: number - jsonPath: .spec.details.dataStorageSizeInTBs name: Storage (TB) @@ -366,7 +366,7 @@ spec: name: Dedicated type: string - jsonPath: .spec.details.computeCount - name: computeCount + name: Compute Count type: number - jsonPath: .spec.details.dataStorageSizeInTBs name: Storage (TB) diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index db09de83..5d982a36 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -39,9 +39,11 @@ package controllers import ( + "bytes" "context" "errors" "fmt" + "io" "reflect" "regexp" "strings" @@ -301,18 +303,6 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R specChanged = true } - /****************************************************************** - * Sync AutonomousDatabase Backups from OCI. - * The backups will not be synced when the lifecycle state is - * TERMINATING or TERMINATED. - ******************************************************************/ - if desiredAdb.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminating && - desiredAdb.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminated { - if err := r.syncBackupResources(logger, desiredAdb); err != nil { - return r.manageError(logger.WithName("syncBackupResources"), desiredAdb, err) - } - } - /***************************************************** * Validate Wallet *****************************************************/ @@ -721,11 +711,18 @@ func (r *AutonomousDatabaseReconciler) validateWallet(logger logr.Logger, adb *d return err } - data, err := oci.ExtractWallet(resp.Content) + walletBytes, err := io.ReadAll(resp.Content) + + data, err := oci.ExtractWallet(io.NopCloser(bytes.NewReader(walletBytes))) if err != nil { return err } + // Include the unextracted zip file + // https://github.com/oracle/oracle-database-operator/issues/97 + zipFileName := fmt.Sprintf("Wallet_%s.zip", *adb.Spec.Details.DbName) + data[zipFileName] = walletBytes + adb.Status.WalletExpiringDate = oci.WalletExpiringDate(data) label := map[string]string{"app": adb.GetName()} diff --git a/controllers/database/autonomousdatabasebackup_controller.go b/controllers/database/autonomousdatabasebackup_controller.go index 9744f3fb..1a806276 100644 --- a/controllers/database/autonomousdatabasebackup_controller.go +++ b/controllers/database/autonomousdatabasebackup_controller.go @@ -227,8 +227,8 @@ func (r *AutonomousDatabaseBackupReconciler) verifyTargetAdb(backup *dbv4.Autono } } - if backup.Spec.Target.OciAdb.OCID != nil { - return *backup.Spec.Target.OciAdb.OCID, nil + if backup.Spec.Target.OciAdb.Ocid != nil { + return *backup.Spec.Target.OciAdb.Ocid, nil } if ownerAdb != nil && ownerAdb.Spec.Details.Id != nil { return *ownerAdb.Spec.Details.Id, nil diff --git a/controllers/database/autonomousdatabaserestore_controller.go b/controllers/database/autonomousdatabaserestore_controller.go index 61b84c5d..66871c72 100644 --- a/controllers/database/autonomousdatabaserestore_controller.go +++ b/controllers/database/autonomousdatabaserestore_controller.go @@ -236,8 +236,8 @@ func (r *AutonomousDatabaseRestoreReconciler) verifyTargetAdb(restore *dbv4.Auto } } - if restore.Spec.Target.OciAdb.OCID != nil { - return *restore.Spec.Target.OciAdb.OCID, nil + if restore.Spec.Target.OciAdb.Ocid != nil { + return *restore.Spec.Target.OciAdb.Ocid, nil } if ownerAdb != nil && ownerAdb.Spec.Details.Id != nil { return *ownerAdb.Spec.Details.Id, nil From ac0b9afbed0f4264efe3f70a57dbf750a86de7a9 Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Thu, 7 Aug 2025 12:23:32 +0000 Subject: [PATCH 254/414] Patchfix --- apis/database/v4/oraclerestart_types.go | 2 + commons/oraclerestart/oraclerestartprov.go | 12 ++ .../database.oracle.com_oraclerestarts.yaml | 8 ++ .../database/oraclerestart_controller.go | 16 ++- docs/oraclerestart/README.md | 6 +- ...sm_disk_to_an_existing_restart_database.md | 85 ++++++++++++ ...disks_from_an_existing_restart_database.md | 67 +++++++++ .../orestart_prov_asm_disk_addition.yaml | 127 ++++++++++++++++++ .../orestart_prov_asm_disk_deletion.yaml | 125 +++++++++++++++++ .../provisioning_oracle_restart_db.md | 2 +- ...provisioning_oracle_restart_db_nodeport.md | 2 +- .../provisioning_oracle_restart_db_onsport.md | 4 +- .../provisioning_oracle_restart_db_rupatch.md | 2 +- ...provisioning_oracle_restart_rupatch_pvc.md | 2 +- ...ovisioning_oracle_restart_storage_class.md | 2 +- go.mod | 3 +- go.sum | 5 - main.go | 112 +++------------ oracle-database-operator.yaml | 32 +++-- 19 files changed, 493 insertions(+), 121 deletions(-) create mode 100644 docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md create mode 100644 docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md create mode 100644 docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml create mode 100644 docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index e634212f..badecba9 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -135,6 +135,8 @@ type InitParams struct { RuPatchLocation string `json:"ruPatchLocation,omitempty"` RuFolderName string `json:"ruFolderName,omitempty"` OPatchLocation string `json:"oPatchLocation,omitempty"` + OneOffLocation string `json:"oneOffLocation,omitempty"` + OneOffIds string `json:"oneOffIds,omitempty"` } type OracleRestartInstDetailSpec struct { diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 4afea226..13471658 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -357,6 +357,18 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac }) } } + if instance.Spec.ConfigParams.OneOffLocation != "" { + if _, exists := OracleRestartSpex.PvcName[instance.Spec.ConfigParams.OneOffLocation]; !exists { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-oneoff-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.OneOffLocation, + }, + }, + }) + } + } } if instance.Spec.AsmStorageDetails != nil { diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index f9aecea2..4d10607f 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -131,6 +131,10 @@ spec: type: string oPatchSwZipFile: type: string + oneOffIds: + type: string + oneOffLocation: + type: string opType: type: string pdbName: @@ -876,6 +880,10 @@ spec: type: string oPatchSwZipFile: type: string + oneOffIds: + type: string + oneOffLocation: + type: string opType: type: string pdbName: diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index f3dafb34..2fba19de 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -1975,6 +1975,12 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or if instance.Spec.ConfigParams.OPatchLocation != "" { data = append(data, "OPATCH_ZIP_FILE="+utils.OraOPatchStageLocation+"/"+instance.Spec.ConfigParams.OPatchSwZipFile) } + if instance.Spec.ConfigParams.OneOffLocation != "" { + data = append(data, "ONEOFF_FOLDER_NAME="+instance.Spec.ConfigParams.OneOffLocation) + } + if instance.Spec.ConfigParams.OneOffIds != "" { + data = append(data, "ONEOFF_IDS="+instance.Spec.ConfigParams.OneOffIds) + } if instance.Spec.ConfigParams.DbSwZipFile != "" { data = append(data, "DB_SW_ZIP_FILE="+instance.Spec.ConfigParams.DbSwZipFile) @@ -2396,7 +2402,7 @@ func getDiskGroupName(deviceDg string, oracleRestart *oraclerestartdb.OracleRest // Function to check if a disk group exists func (r *OracleRestartReconciler) diskGroupExists(podName, diskGroupName string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *oraclerestartdb.OracleRestart, logger logr.Logger) (bool, error) { reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) - cmd := fmt.Sprintf("su - grid -c 'asmcmd lsdg | grep -w %s'", diskGroupName) + cmd := "python3 /opt/scripts/startup/scripts/main.py --getasmdiskgroup=true" stdout, _, err := oraclerestartcommon.ExecCommand(podName, []string{"bash", "-c", cmd}, r.kubeClient, r.kubeConfig, instance, reqLogger) if err != nil { return false, err @@ -3079,6 +3085,14 @@ func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, } } } + svcFound, err := oraclerestartcommon.CheckORestartSvc(oracleRestart, "onssvc", oracleRestart.Spec.InstDetails, "", r.Client) + if err == nil { + // See if service already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err + } + } if oraRestartSpex.NodePortSvc != nil { for index := range oraRestartSpex.NodePortSvc { diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md index 814413be..0172b35b 100644 --- a/docs/oraclerestart/README.md +++ b/docs/oraclerestart/README.md @@ -45,8 +45,10 @@ Deploy Oracle Restart Database YAML files using Kubernetes Cluster on your Oracl [2. Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) [3. Provisioning an Oracle Restart Database with OnsPort Service](./provisioning/provisioning_oracle_restart_db_onsport.md) [4. Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning/provisioning_oracle_restart_db_rupatch.md) -[5. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) -[6. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) +[5. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) +[6. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) +[7. Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database](./provisioning/add_asm_disk_to_an_existing_restart_database.md) +[8. Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database](./provisioning/delete_asm_disks_from_an_existing_restart_database.md) ## Connecting to Oracle Restart Database diff --git a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md new file mode 100644 index 00000000..214bfd28 --- /dev/null +++ b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md @@ -0,0 +1,85 @@ +# Adding ASM Disk - Add an ASM Disk to an existing Oracle Restart Database + +This use case demonstrates adding a new ASM Disks to an existing Oracle Restart Database provisioned earlier using Oracle Restart Database Controller. + +In this use case, the existing Oracle Restart Database Deployed on a Kubernetes Cluster is having: + +This example uses `oraclerestart_prov.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + +This use case will be adding a new ASM Disks which will added to the existing Oracle Restart Database. The existing Oracle Restart Database has been deployed using the file [./oraclerestart_prov_rupatch.yaml](././oraclerestart_prov_rupatch.yaml) from Case 4 [Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning_oracle_restart_db_rupatch.md) + +In this example, + * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch.yaml` file to point to your own container registry base container image. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` , `/dev/oracleoci/oraclevde` and new scaled out disk ``/dev/oracleoci/oraclevdf` + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + * Similar settings for `hostSwStageLocation` and `hostSwLocation` also apply to the worker node. + +Use the file: [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml) for this use case as below: + +1. Deploy the `orestart_prov_asm_disk_addition.yaml` file: + ```sh + kubectl apply -f orestart_prov_asm_disk_addition.yaml + ``` +Note: + - Default value in yaml file is `autoUpdate: "true"`, which will delete and recreate pods with updated ASM disks in the Oracle Restart Database. In this case, the new disks will be added to the existing Diskgroup in the Oracle Restart Database. + +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + ``` + 3. Samples logs in [logs](./logs/asm_addition_logs.txt) when the [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml) file is applied with option `autoUpdate: true`. + + Describe the Restart Object to see new ASM Disks in Status: + + ```bash + $kubectl get oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart -o json | jq '.status.asmDetails.diskgroup' + [ + { + "disks": [ + "/dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde,/dev/oracleoci/oraclevdy" + ], + "name": "DATA", + "redundancy": "EXTERN" + } + ] + + [grid@dbmc1-0 ~]$ export ORACLE_HOME=/u01/app/19c/grid + [grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM + [grid@dbmc1-0 ~]$ export PATH=$ORACLE_HOME/bin:$PATH + [grid@dbmc1-0 ~]$ + [grid@dbmc1-0 ~]$ /u01/app/19c/grid/bin/asmcmd lsdsk + Path + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + /dev/oracleoci/oraclevdy + [grid@dbmc1-0 ~]$ + ``` + +4. Sample [logs](./logs/asm_addition_disable_autoupdate_log.txt) when the [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml) file is applied by changing option as `autoUpdate: "false"` from default `autoUpdate: "true"` + + **Note:** New disk `/dev/disk/by-partlabel/ocne_asm_disk_03` is added to Restart Object Statefulset and Pods are recreated, but this disk is not added to the ASM Disk Group. + + ```bash + [grid@dbmc1-0 ~]$ export ORACLE_HOME=/u01/app/19c/grid + [grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM + [grid@dbmc1-0 ~]$ export PATH=$ORACLE_HOME/bin:$PATH + [grid@dbmc1-0 ~]$ + [grid@dbmc1-0 ~]$ /u01/app/19c/grid/bin/asmcmd lsdsk + Path + /dev/oracleoci/oraclevdd + /dev/oracleoci/oraclevde + [grid@dbmc1-0 ~]$ + ``` \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md b/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md new file mode 100644 index 00000000..94d4d719 --- /dev/null +++ b/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md @@ -0,0 +1,67 @@ +# Delete ASM Disk - Delete an ASM Disk to an existing Oracle Restart Database + +This use case demonstrates deleting a ASM Disk from an existing Oracle Restart Database provisioned earlier using Oracle Restart Database Controller. + +In this use case, the existing Oracle Restart Database Deployed on a Kubernetes Cluster is having: + +This example uses `oraclerestart_prov.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + +This use case will be deleting a ASM Disk which will deleted to the existing Oracle Restart Database. The existing Oracle Restart Database has been deployed using the file [./oraclerestart_prov_rupatch.yaml](././oraclerestart_prov_rupatch.yaml) from Case 4 [Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning_oracle_restart_db_rupatch.md) + +In this example, + * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch.yaml` file to point to your own container registry base container image. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd`. Disk `/dev/oracleoci/oraclevde` is removed manually and not in use. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + * Similar settings for `hostSwStageLocation` and `hostSwLocation` also apply to the worker node. + +Use the file: [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) for this use case as below: + +1. Deploy the `orestart_prov_asm_disk_deletion.yaml` file: + ```sh + kubectl apply -f orestart_prov_asm_disk_deletion.yaml + ``` +Note: + - Default value in yaml file is `autoUpdate: "true"`, which will delete and recreate pods with updated ASM disks in the Oracle Restart Database. In this case, the new disks will be added to the existing Diskgroup in the Oracle Restart Database. + +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + ``` + 3. Samples logs in [logs](./logs/asm_addition_logs.txt) when the [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) file is applied where `/dev/oracleoci/oraclevde` is removed and not in use. If its in use, operator will show error in logs and it wont be able to remove it. + + Describe the Restart Object to see new ASM Disks in Status: + + ```bash + $kubectl get oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart -o json | jq '.status.asmDetails.diskgroup' + [ + { + "disks": [ + "/dev/oracleoci/oraclevdd" + ], + "name": "DATA", + "redundancy": "EXTERN" + } + ] + + [grid@dbmc1-0 ~]$ export ORACLE_HOME=/u01/app/19c/grid + [grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM + [grid@dbmc1-0 ~]$ export PATH=$ORACLE_HOME/bin:$PATH + [grid@dbmc1-0 ~]$ + [grid@dbmc1-0 ~]$ /u01/app/19c/grid/bin/asmcmd lsdsk + Path + /dev/oracleoci/oraclevdd + [grid@dbmc1-0 ~]$ + ``` diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml new file mode 100644 index 00000000..ffe264e6 --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml @@ -0,0 +1,127 @@ +# API version of the custom resource definition +apiVersion: database.oracle.com/v4 + +# Kind of custom resource — OracleRestart for single-instance DB with ASM +kind: OracleRestart + +# Metadata section: name and namespace of the resource +metadata: + # Name of the OracleRestart custom resource + name: oraclerestart-sample + # Kubernetes namespace where it's deployed + namespace: orestart + +spec: + # Details about the Oracle instance and where it will be deployed + instDetails: + # Name of the instance or logical host name + name: dbmc1 + # Path on the host for staging software + hostSwLocation: /scratch/orestart/ + # List of worker node IPs where Oracle will run + workerNode: + - 10.0.10.108 + # Port used for ONS (Oracle Notification Service) + onsTargetPort: 30200 + # Kubernetes NodePort service details for listener access + nodePortSvc: + - svcType: nodeport # Service type exposed via NodePort + name: dbmc1 # Name of the service + + # Port mappings for Oracle listener + portMappings: + - port: 1521 # Port used inside the container + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol used + nodePort: 30007 # Exposed NodePort on Kubernetes + + # ASM disk configuration — list of disks and sizes + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Size of each disk group in GB + diskNames: # Physical disk device paths + - /dev/oracleoci/oraclevdd + - /dev/oracleoci/oraclevde + - /dev/oracleoci/oraclevdy + + # SSH key secret used for secure access during automation or install + sshKeySecret: + name: ssh-key-secret # Name of the secret containing SSH keys + privKeySecretName: ssh-privkey # Private key name in the secret + pubKeySecretName: ssh-pubkey # Public key name in the secret + + # Database user secret containing wallet and credentials + dbSecret: + name: db-user-pass-pkutl # Name of the secret with DB credentials + keyFileName: key.pem # Key file name in the secret + pwdFileName: pwdfile.enc # Password file (encrypted) + + # Container image for Oracle software + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0803 + + # Always pull the latest image + imagePullPolicy: Always + + # Optional: Service account name, required for OpenShift due to SCC enforcement + # serviceAccountName: oraclerestart + + # Configuration for the PDB service inside the DB + serviceDetails: + name: soepdb # Name of the PDB service + + # Resource limits and requests for CPU and memory + resources: + requests: + memory: "16Gi" # Minimum guaranteed memory + cpu: "2" # Minimum guaranteed CPU + limits: + memory: "16Gi" # Max memory limit + cpu: "2" # Max CPU limit + + # Security-related kernel parameters required by Oracle + securityContext: + sysctls: + - name: kernel.shmall # Total shared memory (pages) + value: "2097152" + - name: kernel.sem # Semaphore settings + value: "250 32000 100 128" + - name: kernel.shmmax # Max size of one shared memory segment (bytes) + value: "8589934592" + - name: kernel.shmmni # Max number of shared memory segments + value: "4096" + + # Oracle Grid and Database configuration parameters + configParams: + gridHome: "/u01/app/19c/grid" # ORACLE_HOME for Grid Infrastructure + gridBase: "/u01/app/grid" # Base location for Grid + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # ORACLE_HOME for Database + dbBase: "/u01/app/oracle" # Base location for Database + + # ASM disk devices for CRS/ASM configuration + crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde,/dev/oracleoci/oraclevdy + + # Oracle Inventory location + inventory: "/u01/app/oraInventory" + + # Software zip files to be staged and extracted during install + gridSwZipFile: "grid_home.zip" + dbSwZipFile: "db_home.zip" + oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" + + # Memory and process settings for the database + sgaSize: "3G" # Shared Global Area size + pgaSize: "1G" # Program Global Area size + processes: 2000 # Max number of Oracle processes + cpuCount: 4 # Number of CPUs to configure inside DB + + # Database name (CDB) + dbName: "PORCLCDB" + + # Path to staged Oracle software + hostSwStageLocation: "/scratch/software/19c/19.3.0/" + + # Location for RU patching (Release Update) + ruPatchLocation: "/scratch/software/19c/19.28/37957391" + + # Location where OPatch utility is available + oPatchLocation: "/scratch/software/19c" diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml new file mode 100644 index 00000000..0fb8087d --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml @@ -0,0 +1,125 @@ +# API version of the custom resource definition +apiVersion: database.oracle.com/v4 + +# Kind of custom resource — OracleRestart for single-instance DB with ASM +kind: OracleRestart + +# Metadata section: name and namespace of the resource +metadata: + # Name of the OracleRestart custom resource + name: oraclerestart-sample + # Kubernetes namespace where it's deployed + namespace: orestart + +spec: + # Details about the Oracle instance and where it will be deployed + instDetails: + # Name of the instance or logical host name + name: dbmc1 + # Path on the host for staging software + hostSwLocation: /scratch/orestart/ + # List of worker node IPs where Oracle will run + workerNode: + - 10.0.10.108 + # Port used for ONS (Oracle Notification Service) + onsTargetPort: 30200 + # Kubernetes NodePort service details for listener access + nodePortSvc: + - svcType: nodeport # Service type exposed via NodePort + name: dbmc1 # Name of the service + + # Port mappings for Oracle listener + portMappings: + - port: 1521 # Port used inside the container + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol used + nodePort: 30007 # Exposed NodePort on Kubernetes + + # ASM disk configuration — list of disks and sizes + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Size of each disk group in GB + diskNames: # Physical disk device paths + - /dev/oracleoci/oraclevdd + + # SSH key secret used for secure access during automation or install + sshKeySecret: + name: ssh-key-secret # Name of the secret containing SSH keys + privKeySecretName: ssh-privkey # Private key name in the secret + pubKeySecretName: ssh-pubkey # Public key name in the secret + + # Database user secret containing wallet and credentials + dbSecret: + name: db-user-pass-pkutl # Name of the secret with DB credentials + keyFileName: key.pem # Key file name in the secret + pwdFileName: pwdfile.enc # Password file (encrypted) + + # Container image for Oracle software + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0803 + + # Always pull the latest image + imagePullPolicy: Always + + # Optional: Service account name, required for OpenShift due to SCC enforcement + # serviceAccountName: oraclerestart + + # Configuration for the PDB service inside the DB + serviceDetails: + name: soepdb # Name of the PDB service + + # Resource limits and requests for CPU and memory + resources: + requests: + memory: "16Gi" # Minimum guaranteed memory + cpu: "2" # Minimum guaranteed CPU + limits: + memory: "16Gi" # Max memory limit + cpu: "2" # Max CPU limit + + # Security-related kernel parameters required by Oracle + securityContext: + sysctls: + - name: kernel.shmall # Total shared memory (pages) + value: "2097152" + - name: kernel.sem # Semaphore settings + value: "250 32000 100 128" + - name: kernel.shmmax # Max size of one shared memory segment (bytes) + value: "8589934592" + - name: kernel.shmmni # Max number of shared memory segments + value: "4096" + + # Oracle Grid and Database configuration parameters + configParams: + gridHome: "/u01/app/19c/grid" # ORACLE_HOME for Grid Infrastructure + gridBase: "/u01/app/grid" # Base location for Grid + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # ORACLE_HOME for Database + dbBase: "/u01/app/oracle" # Base location for Database + + # ASM disk devices for CRS/ASM configuration + crsAsmDeviceList: /dev/oracleoci/oraclevdd + + # Oracle Inventory location + inventory: "/u01/app/oraInventory" + + # Software zip files to be staged and extracted during install + gridSwZipFile: "grid_home.zip" + dbSwZipFile: "db_home.zip" + oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" + + # Memory and process settings for the database + sgaSize: "3G" # Shared Global Area size + pgaSize: "1G" # Program Global Area size + processes: 2000 # Max number of Oracle processes + cpuCount: 4 # Number of CPUs to configure inside DB + + # Database name (CDB) + dbName: "PORCLCDB" + + # Path to staged Oracle software + hostSwStageLocation: "/scratch/software/19c/19.3.0/" + + # Location for RU patching (Release Update) + ruPatchLocation: "/scratch/software/19c/19.28/37957391" + + # Location where OPatch utility is available + oPatchLocation: "/scratch/software/19c" diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md index 85067ba5..868db290 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md @@ -35,7 +35,7 @@ Use the file: [oraclerestart_prov.yaml](./oraclerestart_prov.yaml) for this use kubectl get all -n orestart # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" =============================== ORACLE DATABASE IS READY TO USE =============================== diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md index 96560cdc..23d97944 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md @@ -36,7 +36,7 @@ Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports kubectl get all -n orestart # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" =============================== ORACLE DATABASE IS READY TO USE =============================== diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md index ef58f5ae..c31b37c7 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md @@ -35,7 +35,7 @@ Use the file: [oraclerestart_prov_onsport.yaml](./oraclerestart_prov_onsport.yam kubectl get all -n orestart # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" =============================== ORACLE DATABASE IS READY TO USE =============================== @@ -74,7 +74,7 @@ Use the file: [oraclerestart_prov_onsport.yaml](./oraclerestart_prov_onsport.yam kubectl get all -n orestart # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" =============================== ORACLE DATABASE IS READY TO USE =============================== diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md index c70052a1..3bffedc4 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md @@ -34,7 +34,7 @@ Use the file: [oraclerestart_prov_rupatch.yaml](./oraclerestart_prov_rupatch.yam kubectl get all -n orestart # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" =============================== ORACLE DATABASE IS READY TO USE =============================== diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md index 6867e4e8..4c43c0e8 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md @@ -43,7 +43,7 @@ Use the file: [oraclerestart_prov_rupatch_pvc.yaml](./oraclerestart_prov_rupatch kubectl get all -n orestart # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" =============================== ORACLE DATABASE IS READY TO USE =============================== diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md index 7e697aef..43a0c35b 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md @@ -34,7 +34,7 @@ Use the file: [oraclerestart_prov_storage.yaml](./oraclerestart_prov_storage.yam kubectl get all -n orestart # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_rac_setup.log" + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" =============================== ORACLE DATABASE IS READY TO USE =============================== diff --git a/go.mod b/go.mod index 3708041c..8a12e31f 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,6 @@ go 1.24.4 require ( github.com/go-logr/logr v1.4.2 - github.com/go-logr/zapr v1.3.0 - github.com/natefinch/lumberjack v2.0.0+incompatible github.com/onsi/ginkgo/v2 v2.20.2 github.com/onsi/gomega v1.34.2 github.com/oracle/oci-go-sdk/v65 v65.77.1 @@ -37,6 +35,7 @@ require ( github.com/fatih/camelcase v1.0.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect diff --git a/go.sum b/go.sum index 6cfebee3..d23debb8 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= @@ -141,8 +140,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= -github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= @@ -296,8 +293,6 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go index 1ebef2ec..818b8508 100644 --- a/main.go +++ b/main.go @@ -43,13 +43,10 @@ import ( "flag" "fmt" "os" - "path/filepath" "strconv" "strings" "time" - "github.com/go-logr/zapr" - "github.com/natefinch/lumberjack" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "go.uber.org/zap/zapcore" @@ -62,8 +59,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" - uberzap "go.uber.org/zap" - "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" ctrlzap "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" @@ -83,7 +79,8 @@ import ( ) var ( - scheme = runtime.NewScheme() + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") ) func init() { @@ -113,74 +110,7 @@ func main() { TimeEncoder: zapcore.RFC3339TimeEncoder, } - ctrl.SetLogger(ctrlzap.New(func(o *ctrlzap.Options) { *o = *options })) - - var logFilePath string - // Set log directory from environment variable or default to /tmp - logDir := os.Getenv("LOG_DIR") - if logDir == "" { - logDir = "/tmp" - } - // Create log file name with date - currentDate := time.Now().Format("2006-01-02") - logFilePath = filepath.Join(logDir, fmt.Sprintf("controller-%s.log", currentDate)) - // Create symlink "controller.log" -> current date log file - symlinkPath := filepath.Join(logDir, "controller.log") - // Remove existing symlink if it exists - os.Remove(symlinkPath) - // Create new symlink - err := os.Symlink(logFilePath, symlinkPath) - if err != nil { - fmt.Printf("Failed to create symlink: %v\n", err) - } - - opts := ctrlzap.Options{ - Development: true, - } - - opts.BindFlags(flag.CommandLine) - flag.Parse() - - encoderConfig := zapcore.EncoderConfig{ - TimeKey: "timestamp", - LevelKey: "level", - NameKey: "logger", - CallerKey: "caller", - MessageKey: "message", - StacktraceKey: "stacktrace", - LineEnding: zapcore.DefaultLineEnding, - EncodeLevel: zapcore.CapitalLevelEncoder, - EncodeTime: zapcore.ISO8601TimeEncoder, - EncodeDuration: zapcore.StringDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, - } - - // File encoder (JSON) - fileEncoder := zapcore.NewJSONEncoder(encoderConfig) - - // Use lumberjack for log rotation - logFileWriter := &lumberjack.Logger{ - Filename: logFilePath, - MaxSize: 100, // megabytes - MaxBackups: 90, // number of old log files to keep - MaxAge: 30, // days to retain old log files - Compress: true, // compress old log files - } - - writer := zapcore.AddSync(logFileWriter) - - core := zapcore.NewTee( - zapcore.NewCore(fileEncoder, writer, zapcore.DebugLevel), - zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(os.Stdout), zapcore.DebugLevel), - ) - - // Create logger - logger := uberzap.New(core, uberzap.AddCaller(), uberzap.Development()) - - // Use logger - log.SetLogger(zapr.NewLogger(logger)) - setupLog := logger.Sugar() - + ctrl.SetLogger(zap.New(func(o *zap.Options) { *o = *options })) watchNamespaces, err := getWatchNamespace() if err != nil { setupLog.Error(err, "Failed to get watch namespaces") @@ -208,12 +138,10 @@ func main() { // Get Cache cache := mgr.GetCache() - logger.Info("Logger initialized and writing to both file and stdout") - // ADB family controllers if err = (&databasecontroller.AutonomousDatabaseReconciler{ KubeClient: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("database").WithName("AutonomousDatabase"), + Log: ctrl.Log.WithName("controllers").WithName("database").WithName("AutonomousDatabase"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("AutonomousDatabase"), }).SetupWithManager(mgr); err != nil { @@ -222,7 +150,7 @@ func main() { } if err = (&databasecontroller.AutonomousDatabaseBackupReconciler{ KubeClient: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("AutonomousDatabaseBackup"), + Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseBackup"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("AutonomousDatabaseBackup"), }).SetupWithManager(mgr); err != nil { @@ -231,16 +159,16 @@ func main() { } if err = (&databasecontroller.AutonomousDatabaseRestoreReconciler{ KubeClient: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("AutonomousDatabaseRestore"), + Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseRestore"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("AutonomousDatabaseRestore"), }).SetupWithManager(mgr); err != nil { - setupLog.Error("unable to create controller", "controller", "AutonomousDatabaseRestore", "error", err) + setupLog.Error(err, "unable to create controller", "controller", "AutonomousDatabaseRestore") os.Exit(1) } if err = (&databasecontroller.AutonomousContainerDatabaseReconciler{ KubeClient: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("AutonomousContainerDatabase"), + Log: ctrl.Log.WithName("controllers").WithName("AutonomousContainerDatabase"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("AutonomousContainerDatabase"), }).SetupWithManager(mgr); err != nil { @@ -250,7 +178,7 @@ func main() { if err = (&databasecontroller.SingleInstanceDatabaseReconciler{ Client: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("database").WithName("SingleInstanceDatabase"), + Log: ctrl.Log.WithName("controllers").WithName("database").WithName("SingleInstanceDatabase"), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), Recorder: mgr.GetEventRecorderFor("SingleInstanceDatabase"), @@ -260,7 +188,7 @@ func main() { } if err = (&databasecontroller.ShardingDatabaseReconciler{ Client: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("database").WithName("ShardingDatabase"), + Log: ctrl.Log.WithName("controllers").WithName("database").WithName("ShardingDatabase"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("ShardingDatabase"), }).SetupWithManager(mgr); err != nil { @@ -269,7 +197,7 @@ func main() { } if err = (&databasecontroller.DbcsSystemReconciler{ KubeClient: mgr.GetClient(), - Logger: zapr.NewLogger(logger).WithName("controllers").WithName("database").WithName("DbcsSystem"), + Logger: ctrl.Log.WithName("controllers").WithName("database").WithName("DbcsSystem"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("DbcsSystem"), }).SetupWithManager(mgr); err != nil { @@ -278,7 +206,7 @@ func main() { } if err = (&databasecontroller.OracleRestDataServiceReconciler{ Client: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("OracleRestDataService"), + Log: ctrl.Log.WithName("controllers").WithName("OracleRestDataService"), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), Recorder: mgr.GetEventRecorderFor("OracleRestDataService"), @@ -289,7 +217,7 @@ func main() { if err = (&databasecontroller.OracleRestartReconciler{ Client: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("OracleRestart"), + Log: ctrl.Log.WithName("controllers").WithName("OracleRestart"), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), }).SetupWithManager(mgr); err != nil { @@ -421,7 +349,7 @@ func main() { if err = (&databasecontroller.PDBReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("PDB"), + Log: ctrl.Log.WithName("controllers").WithName("PDB"), Interval: time.Duration(i), Recorder: mgr.GetEventRecorderFor("PDB"), }).SetupWithManager(mgr); err != nil { @@ -433,7 +361,7 @@ func main() { if err = (&databasecontroller.LRPDBReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("LRPDB"), + Log: ctrl.Log.WithName("controllers").WithName("LRPDB"), Interval: time.Duration(i), Recorder: mgr.GetEventRecorderFor("LRPDB"), }).SetupWithManager(mgr); err != nil { @@ -446,7 +374,7 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("CDB"), + Log: ctrl.Log.WithName("controllers").WithName("CDB"), Interval: time.Duration(i), Recorder: mgr.GetEventRecorderFor("CDB"), }).SetupWithManager(mgr); err != nil { @@ -459,7 +387,7 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("LREST"), + Log: ctrl.Log.WithName("controllers").WithName("LREST"), Interval: time.Duration(i), Recorder: mgr.GetEventRecorderFor("LREST"), }).SetupWithManager(mgr); err != nil { @@ -469,7 +397,7 @@ func main() { if err = (&dataguardcontroller.DataguardBrokerReconciler{ Client: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("dataguard").WithName("DataguardBroker"), + Log: ctrl.Log.WithName("controllers").WithName("dataguard").WithName("DataguardBroker"), Scheme: mgr.GetScheme(), Config: mgr.GetConfig(), Recorder: mgr.GetEventRecorderFor("DataguardBroker"), @@ -490,7 +418,7 @@ func main() { // Observability DatabaseObserver Reconciler if err = (&observabilitycontroller.DatabaseObserverReconciler{ Client: mgr.GetClient(), - Log: zapr.NewLogger(logger).WithName("controllers").WithName("observability").WithName("DatabaseObserver"), + Log: ctrl.Log.WithName("controllers").WithName("observability").WithName("DatabaseObserver"), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("DatabaseObserver"), }).SetupWithManager(mgr); err != nil { diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 9e45fe8c..6e391fbc 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -2094,14 +2094,14 @@ spec: type: array log: properties: - filename: + destination: type: string - path: + disable: + type: boolean + filename: type: string volume: properties: - name: - type: string persistentVolumeClaim: properties: claimName: @@ -4417,14 +4417,14 @@ spec: type: array log: properties: - filename: + destination: type: string - path: + disable: + type: boolean + filename: type: string volume: properties: - name: - type: string persistentVolumeClaim: properties: claimName: @@ -6740,14 +6740,14 @@ spec: type: array log: properties: - filename: + destination: type: string - path: + disable: + type: boolean + filename: type: string volume: properties: - name: - type: string persistentVolumeClaim: properties: claimName: @@ -10646,6 +10646,10 @@ spec: type: string oPatchSwZipFile: type: string + oneOffIds: + type: string + oneOffLocation: + type: string opType: type: string pdbName: @@ -11391,6 +11395,10 @@ spec: type: string oPatchSwZipFile: type: string + oneOffIds: + type: string + oneOffLocation: + type: string opType: type: string pdbName: From 33cda59df3fc7489d80d93d45b6e4859aa988667 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Thu, 7 Aug 2025 14:45:41 +0000 Subject: [PATCH 255/414] Update lib versions for release v2.0 --- .../v1alpha1/dataguardbroker_webhook.go | 21 +- apis/database/v1alpha1/dbcssystem_webhook.go | 17 +- .../v1alpha1/oraclerestdataservice_webhook.go | 20 +- .../v1alpha1/shardingdatabase_webhook.go | 16 +- .../singleinstancedatabase_webhook.go | 21 +- apis/database/v4/cdb_webhook.go | 17 +- apis/database/v4/lrest_webhook.go | 17 +- apis/database/v4/pdb_webhook.go | 17 +- apis/database/v4/shardingdatabase_webhook.go | 16 +- commons/oci/containerdatabase.go | 2 +- go.mod | 107 +++--- go.sum | 322 +++++++++--------- test/e2e/util/oci_acd_request.go | 5 +- test/e2e/util/util.go | 4 +- 14 files changed, 312 insertions(+), 290 deletions(-) diff --git a/apis/database/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go index 89a9d3fd..33ea4d73 100644 --- a/apis/database/v1alpha1/dataguardbroker_webhook.go +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + "context" "strconv" "strings" @@ -59,6 +60,8 @@ var dataguardbrokerlog = logf.Log.WithName("dataguardbroker-resource") func (r *DataguardBroker) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(r). + WithValidator(r). Complete() } @@ -66,10 +69,10 @@ func (r *DataguardBroker) SetupWebhookWithManager(mgr ctrl.Manager) error { //+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-dataguardbroker,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dataguardbrokers,verbs=create;update,versions=v1alpha1,name=mdataguardbroker.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Defaulter = &DataguardBroker{} +var _ webhook.CustomDefaulter = &DataguardBroker{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *DataguardBroker) Default() { +func (r *DataguardBroker) Default(ctx context.Context, obj runtime.Object) error { dataguardbrokerlog.Info("default", "name", r.Name) if r.Spec.LoadBalancer { @@ -94,15 +97,17 @@ func (r *DataguardBroker) Default() { if r.Spec.SetAsPrimaryDatabase != "" { r.Spec.SetAsPrimaryDatabase = strings.ToUpper(r.Spec.SetAsPrimaryDatabase) } + + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-dataguardbroker,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dataguardbrokers,versions=v1alpha1,name=vdataguardbroker.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Validator = &DataguardBroker{} +var _ webhook.CustomValidator = &DataguardBroker{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *DataguardBroker) ValidateCreate() (admission.Warnings, error) { +func (r *DataguardBroker) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { dataguardbrokerlog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -125,21 +130,21 @@ func (r *DataguardBroker) ValidateCreate() (admission.Warnings, error) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *DataguardBroker) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *DataguardBroker) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { dataguardbrokerlog.Info("validate update", "name", r.Name) dataguardbrokerlog.Info("validate update", "name", r.Name) var allErrs field.ErrorList // check creation validations first - _, err := r.ValidateCreate() + _, err := r.ValidateCreate(ctx, newObj) if err != nil { return nil, err } // Validate Deletion if r.GetDeletionTimestamp() != nil { - warnings, err := r.ValidateDelete() + warnings, err := r.ValidateDelete(ctx, newObj) if err != nil { return warnings, err } @@ -175,7 +180,7 @@ func (r *DataguardBroker) ValidateUpdate(old runtime.Object) (admission.Warnings } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *DataguardBroker) ValidateDelete() (admission.Warnings, error) { +func (r *DataguardBroker) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { dataguardbrokerlog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. diff --git a/apis/database/v1alpha1/dbcssystem_webhook.go b/apis/database/v1alpha1/dbcssystem_webhook.go index dc9f8934..0f9fe27c 100644 --- a/apis/database/v1alpha1/dbcssystem_webhook.go +++ b/apis/database/v1alpha1/dbcssystem_webhook.go @@ -39,6 +39,8 @@ package v1alpha1 import ( + "context" + "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -52,6 +54,8 @@ var dbcssystemlog = logf.Log.WithName("dbcssystem-resource") func (r *DbcsSystem) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(r). + WithValidator(r). Complete() } @@ -59,22 +63,23 @@ func (r *DbcsSystem) SetupWebhookWithManager(mgr ctrl.Manager) error { //+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-dbcssystem,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=dbcssystems,verbs=create;update,versions=v4,name=mdbcssystemv1alpha1.kb.io,admissionReviewVersions=v1 -var _ webhook.Defaulter = &DbcsSystem{} +var _ webhook.CustomDefaulter = &DbcsSystem{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *DbcsSystem) Default() { +func (r *DbcsSystem) Default(ctx context.Context, obj runtime.Object) error { dbcssystemlog.Info("default", "name", r.Name) // TODO(user): fill in your defaulting logic. + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. // +kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv1alpha1.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &DbcsSystem{} +var _ webhook.CustomValidator = &DbcsSystem{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *DbcsSystem) ValidateCreate() (admission.Warnings, error) { +func (r *DbcsSystem) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { dbcssystemlog.Info("validate create", "name", r.Name) // // TODO(user): fill in your validation logic upon object creation. @@ -82,7 +87,7 @@ func (r *DbcsSystem) ValidateCreate() (admission.Warnings, error) { } // // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *DbcsSystem) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *DbcsSystem) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { dbcssystemlog.Info("validate update", "name", r.Name) // // TODO(user): fill in your validation logic upon object update. @@ -90,7 +95,7 @@ func (r *DbcsSystem) ValidateUpdate(old runtime.Object) (admission.Warnings, err } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *DbcsSystem) ValidateDelete() (admission.Warnings, error) { +func (r *DbcsSystem) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { dbcssystemlog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. diff --git a/apis/database/v1alpha1/oraclerestdataservice_webhook.go b/apis/database/v1alpha1/oraclerestdataservice_webhook.go index c5ecde1c..5d000b8d 100644 --- a/apis/database/v1alpha1/oraclerestdataservice_webhook.go +++ b/apis/database/v1alpha1/oraclerestdataservice_webhook.go @@ -39,6 +39,8 @@ package v1alpha1 import ( + "context" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -56,6 +58,8 @@ var oraclerestdataservicelog = logf.Log.WithName("oraclerestdataservice-resource func (r *OracleRestDataService) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(r). + WithValidator(r). Complete() } @@ -63,10 +67,10 @@ func (r *OracleRestDataService) SetupWebhookWithManager(mgr ctrl.Manager) error //+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-oraclerestdataservice,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=oraclerestdataservices,verbs=create;update,versions=v1alpha1,name=moraclerestdataservice.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Defaulter = &OracleRestDataService{} +var _ webhook.CustomDefaulter = &OracleRestDataService{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *OracleRestDataService) Default() { +func (r *OracleRestDataService) Default(ctx context.Context, obj runtime.Object) error { oraclerestdataservicelog.Info("default", "name", r.Name) // OracleRestDataService Currently supports single replica r.Spec.Replicas = 1 @@ -77,15 +81,17 @@ func (r *OracleRestDataService) Default() { if r.Spec.AdminPassword.KeepSecret == nil { r.Spec.AdminPassword.KeepSecret = &keepSecret } + + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:verbs=create;update,path=/validate-database-oracle-com-v1alpha1-oraclerestdataservice,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=oraclerestdataservices,versions=v1alpha1,name=voraclerestdataservice.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Validator = &OracleRestDataService{} +var _ webhook.CustomValidator = &OracleRestDataService{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *OracleRestDataService) ValidateCreate() (admission.Warnings, error) { +func (r *OracleRestDataService) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { oraclerestdataservicelog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -137,13 +143,13 @@ func (r *OracleRestDataService) ValidateCreate() (admission.Warnings, error) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *OracleRestDataService) ValidateUpdate(oldRuntimeObject runtime.Object) (admission.Warnings, error) { +func (r *OracleRestDataService) ValidateUpdate(ctx context.Context, oldRuntimeObject, newRuntimeObject runtime.Object) (admission.Warnings, error) { oraclerestdataservicelog.Info("validate update", "name", r.Name) var allErrs field.ErrorList // check creation validations first - warnings, err := r.ValidateCreate() + warnings, err := r.ValidateCreate(ctx, newRuntimeObject) if err != nil { return warnings, err } @@ -173,7 +179,7 @@ func (r *OracleRestDataService) ValidateUpdate(oldRuntimeObject runtime.Object) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *OracleRestDataService) ValidateDelete() (admission.Warnings, error) { +func (r *OracleRestDataService) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { oraclerestdataservicelog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. diff --git a/apis/database/v1alpha1/shardingdatabase_webhook.go b/apis/database/v1alpha1/shardingdatabase_webhook.go index 4e7ea2e7..158d4ad0 100644 --- a/apis/database/v1alpha1/shardingdatabase_webhook.go +++ b/apis/database/v1alpha1/shardingdatabase_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + "context" "strings" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -57,6 +58,8 @@ var shardingdatabaselog = logf.Log.WithName("shardingdatabase-resource") func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(r). + WithValidator(r). Complete() } @@ -64,10 +67,10 @@ func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { //+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v1alpha1,name=mshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 -var _ webhook.Defaulter = &ShardingDatabase{} +var _ webhook.CustomDefaulter = &ShardingDatabase{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *ShardingDatabase) Default() { +func (r *ShardingDatabase) Default(ctx context.Context, obj runtime.Object) error { shardingdatabaselog.Info("default", "name", r.Name) // TODO(user): fill in your defaulting logic. @@ -84,15 +87,16 @@ func (r *ShardingDatabase) Default() { } } + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v1alpha1-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v1alpha1,name=vshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &ShardingDatabase{} +var _ webhook.CustomValidator = &ShardingDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { +func (r *ShardingDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { shardingdatabaselog.Info("validate create", "name", r.Name) // TODO(user): fill in your validation logic upon object creation. @@ -198,7 +202,7 @@ func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *ShardingDatabase) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { shardingdatabaselog.Info("validate update", "name", r.Name) // TODO(user): fill in your validation logic upon object update. @@ -206,7 +210,7 @@ func (r *ShardingDatabase) ValidateUpdate(old runtime.Object) (admission.Warning } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateDelete() (admission.Warnings, error) { +func (r *ShardingDatabase) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { shardingdatabaselog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index bc095f7c..d8e894a2 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + "context" "strconv" "strings" "time" @@ -61,6 +62,8 @@ var singleinstancedatabaselog = logf.Log.WithName("singleinstancedatabase-resour func (r *SingleInstanceDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(r). + WithValidator(r). Complete() } @@ -68,10 +71,10 @@ func (r *SingleInstanceDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error //+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-singleinstancedatabase,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=singleinstancedatabases,verbs=create;update,versions=v1alpha1,name=msingleinstancedatabase.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Defaulter = &SingleInstanceDatabase{} +var _ webhook.CustomDefaulter = &SingleInstanceDatabase{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *SingleInstanceDatabase) Default() { +func (r *SingleInstanceDatabase) Default(ctx context.Context, obj runtime.Object) error { singleinstancedatabaselog.Info("default", "name", r.Name) if r.Spec.LoadBalancer { @@ -139,15 +142,17 @@ func (r *SingleInstanceDatabase) Default() { if r.Spec.TrueCacheServices == nil { r.Spec.TrueCacheServices = make([]string, 0) } + + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v1alpha1-singleinstancedatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=singleinstancedatabases,versions=v1alpha1,name=vsingleinstancedatabase.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Validator = &SingleInstanceDatabase{} +var _ webhook.CustomValidator = &SingleInstanceDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { +func (r *SingleInstanceDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { singleinstancedatabaselog.Info("validate create", "name", r.Name) var allErrs field.ErrorList @@ -405,19 +410,19 @@ func (r *SingleInstanceDatabase) ValidateCreate() (admission.Warnings, error) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) (admission.Warnings, error) { +func (r *SingleInstanceDatabase) ValidateUpdate(ctx context.Context, oldRuntimeObject, newRuntimeObj runtime.Object) (admission.Warnings, error) { singleinstancedatabaselog.Info("validate update", "name", r.Name) var allErrs field.ErrorList // check creation validations first - warnings, err := r.ValidateCreate() + warnings, err := r.ValidateCreate(ctx, newRuntimeObj) if err != nil { return warnings, err } // Validate Deletion if r.GetDeletionTimestamp() != nil { - warnings, err := r.ValidateDelete() + warnings, err := r.ValidateDelete(ctx, newRuntimeObj) if err != nil { return warnings, err } @@ -537,7 +542,7 @@ func (r *SingleInstanceDatabase) ValidateUpdate(oldRuntimeObject runtime.Object) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *SingleInstanceDatabase) ValidateDelete() (admission.Warnings, error) { +func (r *SingleInstanceDatabase) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { singleinstancedatabaselog.Info("validate delete", "name", r.Name) var allErrs field.ErrorList if r.Status.OrdsReference != "" { diff --git a/apis/database/v4/cdb_webhook.go b/apis/database/v4/cdb_webhook.go index 235b2627..e736ece1 100644 --- a/apis/database/v4/cdb_webhook.go +++ b/apis/database/v4/cdb_webhook.go @@ -39,6 +39,7 @@ package v4 import ( + "context" "reflect" "strings" @@ -58,15 +59,17 @@ var cdblog = logf.Log.WithName("cdb-webhook") func (r *CDB) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(r). + WithValidator(r). Complete() } //+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-cdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v4,name=mcdb.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Defaulter = &CDB{} +var _ webhook.CustomDefaulter = &CDB{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *CDB) Default() { +func (r *CDB) Default(ctx context.Context, obj runtime.Object) error { cdblog.Info("Setting default values in CDB spec for : " + r.Name) if r.Spec.ORDSPort == 0 { @@ -76,15 +79,17 @@ func (r *CDB) Default() { if r.Spec.Replicas == 0 { r.Spec.Replicas = 1 } + + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:path=/validate-database-oracle-com-v4-cdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v4,name=vcdb.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Validator = &CDB{} +var _ webhook.CustomValidator = &CDB{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateCreate() (admission.Warnings, error) { +func (r *CDB) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { cdblog.Info("ValidateCreate", "name", r.Name) var allErrs field.ErrorList @@ -173,7 +178,7 @@ func (r *CDB) ValidateCreate() (admission.Warnings, error) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *CDB) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { cdblog.Info("validate update", "name", r.Name) isCDBMarkedToBeDeleted := r.GetDeletionTimestamp() != nil @@ -216,7 +221,7 @@ func (r *CDB) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateDelete() (admission.Warnings, error) { +func (r *CDB) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { cdblog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. diff --git a/apis/database/v4/lrest_webhook.go b/apis/database/v4/lrest_webhook.go index 9d65a1d6..74320388 100644 --- a/apis/database/v4/lrest_webhook.go +++ b/apis/database/v4/lrest_webhook.go @@ -39,6 +39,7 @@ package v4 import ( + "context" "reflect" "strings" @@ -58,15 +59,17 @@ var lrestlog = logf.Log.WithName("lrest-webhook") func (r *LREST) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(r). + WithValidator(r). Complete() } //+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-lrest,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=lrests,verbs=create;update,versions=v4,name=mlrest.kb.io,admissionReviewVersions={v4,v1beta1} -var _ webhook.Defaulter = &LREST{} +var _ webhook.CustomDefaulter = &LREST{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *LREST) Default() { +func (r *LREST) Default(ctx context.Context, obj runtime.Object) error { lrestlog.Info("Setting default values in LREST spec for : " + r.Name) if r.Spec.LRESTPort == 0 { @@ -76,15 +79,17 @@ func (r *LREST) Default() { if r.Spec.Replicas == 0 { r.Spec.Replicas = 1 } + + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:path=/validate-database-oracle-com-v4-lrest,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=lrests,verbs=create;update,versions=v4,name=vlrest.kb.io,admissionReviewVersions={v4,v1beta1} -var _ webhook.Validator = &LREST{} +var _ webhook.CustomValidator = &LREST{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *LREST) ValidateCreate() (admission.Warnings, error) { +func (r *LREST) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { lrestlog.Info("ValidateCreate", "name", r.Name) var allErrs field.ErrorList @@ -168,7 +173,7 @@ func (r *LREST) ValidateCreate() (admission.Warnings, error) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *LREST) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *LREST) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { lrestlog.Info("validate update", "name", r.Name) isLRESTMarkedToBeDeleted := r.GetDeletionTimestamp() != nil @@ -211,7 +216,7 @@ func (r *LREST) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *LREST) ValidateDelete() (admission.Warnings, error) { +func (r *LREST) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { lrestlog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. diff --git a/apis/database/v4/pdb_webhook.go b/apis/database/v4/pdb_webhook.go index f651accf..d447fbd8 100644 --- a/apis/database/v4/pdb_webhook.go +++ b/apis/database/v4/pdb_webhook.go @@ -43,6 +43,7 @@ package v4 import ( + "context" "reflect" "strconv" "strings" @@ -63,15 +64,17 @@ var pdblog = logf.Log.WithName("pdb-webhook") func (r *PDB) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(r). + WithValidator(r). Complete() } //+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-pdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v4,name=mpdb.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Defaulter = &PDB{} +var _ webhook.CustomDefaulter = &PDB{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *PDB) Default() { +func (r *PDB) Default(ctx context.Context, obj runtime.Object) error { pdblog.Info("Setting default values in PDB spec for : " + r.Name) action := strings.ToUpper(r.Spec.Action) @@ -115,15 +118,17 @@ func (r *PDB) Default() { *r.Spec.GetScript = false pdblog.Info(" - getScript : " + strconv.FormatBool(*(r.Spec.GetScript))) } + + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:path=/validate-database-oracle-com-v4-pdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v4,name=vpdb.kb.io,admissionReviewVersions={v1,v1beta1} -var _ webhook.Validator = &PDB{} +var _ webhook.CustomValidator = &PDB{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateCreate() (admission.Warnings, error) { +func (r *PDB) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { pdblog.Info("ValidateCreate-Validating PDB spec for : " + r.Name) var allErrs field.ErrorList @@ -277,7 +282,7 @@ func (r *PDB) validateAction(allErrs *field.ErrorList) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *PDB) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { pdblog.Info("ValidateUpdate-Validating PDB spec for : " + r.Name) isPDBMarkedToBeDeleted := r.GetDeletionTimestamp() != nil @@ -315,7 +320,7 @@ func (r *PDB) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateDelete() (admission.Warnings, error) { +func (r *PDB) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { pdblog.Info("ValidateDelete-Validating PDB spec for : " + r.Name) // TODO(user): fill in your validation logic upon object deletion. diff --git a/apis/database/v4/shardingdatabase_webhook.go b/apis/database/v4/shardingdatabase_webhook.go index 1ac74d08..2115b57b 100644 --- a/apis/database/v4/shardingdatabase_webhook.go +++ b/apis/database/v4/shardingdatabase_webhook.go @@ -39,6 +39,7 @@ package v4 import ( + "context" "strings" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -57,6 +58,8 @@ var shardingdatabaselog = logf.Log.WithName("shardingdatabase-resource") func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). + WithDefaulter(r). + WithValidator(r). Complete() } @@ -64,10 +67,10 @@ func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { //+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v4,name=mshardingdatabasev4.kb.io,admissionReviewVersions=v1 -var _ webhook.Defaulter = &ShardingDatabase{} +var _ webhook.CustomDefaulter = &ShardingDatabase{} // Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *ShardingDatabase) Default() { +func (r *ShardingDatabase) Default(ctx context.Context, obj runtime.Object) error { shardingdatabaselog.Info("default", "name", r.Name) // TODO(user): fill in your defaulting logic. @@ -84,15 +87,16 @@ func (r *ShardingDatabase) Default() { } } + return nil } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. //+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v4,name=vshardingdatabasev4.kb.io,admissionReviewVersions={v1} -var _ webhook.Validator = &ShardingDatabase{} +var _ webhook.CustomValidator = &ShardingDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { +func (r *ShardingDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { shardingdatabaselog.Info("validate create", "name", r.Name) // TODO(user): fill in your validation logic upon object creation. @@ -198,7 +202,7 @@ func (r *ShardingDatabase) ValidateCreate() (admission.Warnings, error) { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { +func (r *ShardingDatabase) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { shardingdatabaselog.Info("validate update", "name", r.Name) // TODO(user): fill in your validation logic upon object update. @@ -206,7 +210,7 @@ func (r *ShardingDatabase) ValidateUpdate(old runtime.Object) (admission.Warning } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateDelete() (admission.Warnings, error) { +func (r *ShardingDatabase) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { shardingdatabaselog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. diff --git a/commons/oci/containerdatabase.go b/commons/oci/containerdatabase.go index 9391d6f8..28b31353 100644 --- a/commons/oci/containerdatabase.go +++ b/commons/oci/containerdatabase.go @@ -56,7 +56,7 @@ func (d *DatabaseService) CreateAutonomousContainerDatabase(acd *dbv4.Autonomous CompartmentId: acd.Spec.CompartmentOCID, DisplayName: acd.Spec.DisplayName, CloudAutonomousVmClusterId: acd.Spec.AutonomousExadataVMClusterOCID, - PatchModel: database.CreateAutonomousContainerDatabaseDetailsPatchModelUpdates, + PatchModel: database.CreateAutonomousContainerDatabaseBasePatchModelUpdates, }, } diff --git a/go.mod b/go.mod index 8a12e31f..4bd74705 100644 --- a/go.mod +++ b/go.mod @@ -3,25 +3,24 @@ module github.com/oracle/oracle-database-operator go 1.24.4 require ( - github.com/go-logr/logr v1.4.2 - github.com/onsi/ginkgo/v2 v2.20.2 - github.com/onsi/gomega v1.34.2 - github.com/oracle/oci-go-sdk/v65 v65.77.1 - github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.78.2 - go.uber.org/zap v1.26.0 - golang.org/x/text v0.19.0 - gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/cli-runtime v0.31.3 - k8s.io/client-go v0.31.3 - k8s.io/kubectl v0.31.3 - sigs.k8s.io/controller-runtime v0.19.3 - sigs.k8s.io/yaml v1.4.0 + github.com/go-logr/logr v1.4.3 + github.com/onsi/ginkgo/v2 v2.23.4 + github.com/onsi/gomega v1.37.0 + github.com/oracle/oci-go-sdk/v65 v65.95.0 + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0 + go.uber.org/zap v1.27.0 + golang.org/x/text v0.27.0 + k8s.io/api v0.33.3 + k8s.io/apimachinery v0.33.3 + k8s.io/cli-runtime v0.33.3 + k8s.io/client-go v0.33.3 + k8s.io/kubectl v0.33.3 + sigs.k8s.io/controller-runtime v0.21.0 + sigs.k8s.io/yaml v1.5.0 ) require ( - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect @@ -30,37 +29,34 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.9.0 // indirect - github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/camelcase v1.0.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/imdario/mergo v0.3.6 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/moby/spdystream v0.4.0 // indirect + github.com/moby/spdystream v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -69,9 +65,9 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sony/gobreaker v0.5.0 // indirect @@ -79,28 +75,33 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.2.0 // indirect - go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.24.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.3 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.34.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.31.2 // indirect - k8s.io/component-base v0.31.3 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.33.1 // indirect + k8s.io/component-base v0.33.3 // indirect + k8s.io/component-helpers v0.33.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.17.2 // indirect - sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/kustomize/api v0.19.0 // indirect + sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect ) diff --git a/go.sum b/go.sum index d23debb8..5a933f26 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -10,15 +8,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -29,79 +22,58 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= -github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= +github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -110,6 +82,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -117,6 +91,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= @@ -125,8 +101,8 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= -github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= +github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -140,32 +116,32 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= -github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= -github.com/oracle/oci-go-sdk/v65 v65.77.1 h1:gqjTXIUWvTihkn470AclxSAMcR1JecqjD2IUtp+sDIU= -github.com/oracle/oci-go-sdk/v65 v65.77.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= +github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/oracle/oci-go-sdk/v65 v65.95.0 h1:fI+/mfJOS2DkQ+/AFSyJAfn1XFR4TTGm2AhN6xbsi00= +github.com/oracle/oci-go-sdk/v65 v65.95.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.78.2 h1:SyoVBXD/r0PntR1rprb90ClI32FSUNOCWqqTatnipHM= -github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.78.2/go.mod h1:SvsRXw4m1F2vk7HquU5h475bFpke27mIUswfyw9u3ug= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0 h1:j9Ce3W6X6Tzi0QnSap+YzGwpqJLJGP/7xV6P9f86jjM= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0/go.mod h1:sSxwdmprUfmRfTknPc4KIjUd2ZIc/kirw4UdXNhOauM= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= @@ -178,114 +154,119 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -293,43 +274,44 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= -k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0= -k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/cli-runtime v0.31.3 h1:fEQD9Xokir78y7pVK/fCJN090/iYNrLHpFbGU4ul9TI= -k8s.io/cli-runtime v0.31.3/go.mod h1:Q2jkyTpl+f6AtodQvgDI8io3jrfr+Z0LyQBPJJ2Btq8= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= -k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= -k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/apiextensions-apiserver v0.33.1 h1:N7ccbSlRN6I2QBcXevB73PixX2dQNIW0ZRuguEE91zI= +k8s.io/apiextensions-apiserver v0.33.1/go.mod h1:uNQ52z1A1Gu75QSa+pFK5bcXc4hq7lpOXbweZgi4dqA= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/cli-runtime v0.33.3 h1:Dgy4vPjNIu8LMJBSvs8W0LcdV0PX/8aGG1DA1W8lklA= +k8s.io/cli-runtime v0.33.3/go.mod h1:yklhLklD4vLS8HNGgC9wGiuHWze4g7x6XQZ+8edsKEo= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/component-base v0.33.3 h1:mlAuyJqyPlKZM7FyaoM/LcunZaaY353RXiOd2+B5tGA= +k8s.io/component-base v0.33.3/go.mod h1:ktBVsBzkI3imDuxYXmVxZ2zxJnYTZ4HAsVj9iF09qp4= +k8s.io/component-helpers v0.33.3 h1:fjWVORSQfI0WKzPeIFSju/gMD9sybwXBJ7oPbqQu6eM= +k8s.io/component-helpers v0.33.3/go.mod h1:7iwv+Y9Guw6X4RrnNQOyQlXcvJrVjPveHVqUA5dm31c= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/kubectl v0.31.3 h1:3r111pCjPsvnR98oLLxDMwAeM6OPGmPty6gSKaLTQes= -k8s.io/kubectl v0.31.3/go.mod h1:lhMECDCbJN8He12qcKqs2QfmVo9Pue30geovBVpH5fs= -k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3 h1:b2FmK8YH+QEwq/Sy2uAEhmqL5nPfGYbJOcaqjeYYZoA= -k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw= -sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= -sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= -sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ= -sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/kubectl v0.33.3 h1:r/phHvH1iU7gO/l7tTjQk2K01ER7/OAJi8uFHHyWSac= +k8s.io/kubectl v0.33.3/go.mod h1:euj2bG56L6kUGOE/ckZbCoudPwuj4Kud7BR0GzyNiT0= +k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= +k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ= +sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o= +sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA= +sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ= +sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4= diff --git a/test/e2e/util/oci_acd_request.go b/test/e2e/util/oci_acd_request.go index f0a3a841..9230f8f6 100644 --- a/test/e2e/util/oci_acd_request.go +++ b/test/e2e/util/oci_acd_request.go @@ -43,9 +43,6 @@ import ( "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/database" - // "io" - // "io/ioutil" - // "time" ) func CreateAutonomousContainerDatabase(dbClient database.DatabaseClient, compartmentId *string, acdName *string, exadataVmClusterID *string) (response database.CreateAutonomousContainerDatabaseResponse, err error) { @@ -53,7 +50,7 @@ func CreateAutonomousContainerDatabase(dbClient database.DatabaseClient, compart DisplayName: acdName, CloudAutonomousVmClusterId: exadataVmClusterID, CompartmentId: compartmentId, - PatchModel: database.CreateAutonomousContainerDatabaseDetailsPatchModelUpdates, + PatchModel: database.CreateAutonomousContainerDatabaseBasePatchModelUpdates, } createACDRequest := database.CreateAutonomousContainerDatabaseRequest{ diff --git a/test/e2e/util/util.go b/test/e2e/util/util.go index b9e76aa7..38867395 100644 --- a/test/e2e/util/util.go +++ b/test/e2e/util/util.go @@ -44,8 +44,6 @@ import ( "strings" "time" - goyaml "gopkg.in/yaml.v3" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" corev1 "k8s.io/api/core/v1" @@ -144,7 +142,7 @@ func GetTestConfig(filename string) (*testConfiguration, error) { return nil, err } - if err := goyaml.Unmarshal(yamlBytes, config); err != nil { + if err := yaml.Unmarshal(yamlBytes, config); err != nil { return nil, err } From 9878ec47ec2acc828adcd1f3808947c55725dc33 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 8 Aug 2025 14:43:44 +0000 Subject: [PATCH 256/414] master merged with mmalvezz_refactoring_2.0 --- apis/database/v4/lrest_types.go | 37 +- apis/database/v4/lrest_webhook.go | 35 +- apis/database/v4/lrpdb_types.go | 79 +- apis/database/v4/lrpdb_webhook.go | 144 +- apis/database/v4/zz_generated.deepcopy.go | 1 + commons/k8s/utils.go | 9 + commons/multitenant/lrest/common.go | 172 +- .../crd/bases/database.oracle.com_lrests.yaml | 31 + .../crd/bases/database.oracle.com_lrpdbs.yaml | 64 +- .../database.oracle.com_oraclerestarts.yaml | 4 + ...vability.oracle.com_databaseobservers.yaml | 18 +- config/manager/kustomization.yaml | 4 +- controllers/database/lrest_controller.go | 966 ++++- controllers/database/lrpdb_controller.go | 3755 ++++++++++------- controllers/database/pdb_controller.go | 8 +- docs/multitenant/lrest-based/usecase/makefile | 385 +- main.go | 5 + oracle-database-operator.yaml | 153 +- 18 files changed, 3993 insertions(+), 1877 deletions(-) diff --git a/apis/database/v4/lrest_types.go b/apis/database/v4/lrest_types.go index 421a3ea1..ebe61704 100644 --- a/apis/database/v4/lrest_types.go +++ b/apis/database/v4/lrest_types.go @@ -51,22 +51,26 @@ type LRESTSpec struct { LRESTName string `json:"cdbName,omitempty"` // Name of the LREST Service ServiceName string `json:"serviceName,omitempty"` - // Password for the LREST System Administrator SysAdminPwd LRESTSysAdminPassword `json:"sysAdminPwd,omitempty"` // User in the root container with sysdba priviledges to manage PDB lifecycle LRESTAdminUser LRESTAdminUser `json:"cdbAdminUser,omitempty"` // Password for the LREST Administrator to manage PDB lifecycle LRESTAdminPwd LRESTAdminPassword `json:"cdbAdminPwd,omitempty"` - + // Secret: tls.key LRESTTlsKey LRESTTLSKEY `json:"cdbTlsKey,omitempty"` + // Secret: tls.crt LRESTTlsCrt LRESTTLSCRT `json:"cdbTlsCrt,omitempty"` + // Secret: Pub.key LRESTPubKey LRESTPUBKEY `json:"cdbPubKey,omitempty"` + // Secret: Priv.key LRESTPriKey LRESTPRVKEY `json:"cdbPrvKey,omitempty"` - + // Secret: Tls.cat + LRESTTlsCat LRPDBTLSCAT `json:"cdbTlsCat,omitempty"` // Password for user LREST_PUBLIC_USER LRESTPwd LRESTPassword `json:"lrestPwd,omitempty"` // LREST server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. + // +kubebuilder:default=8888 LRESTPort int `json:"lrestPort,omitempty"` // LREST Image Name LRESTImage string `json:"lrestImage,omitempty"` @@ -86,9 +90,30 @@ type LRESTSpec struct { // DB server port DBPort int `json:"dbPort,omitempty"` // Node Selector for running the Pod - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - DBTnsurl string `json:"dbTnsurl,omitempty"` - DeletePDBCascade bool `json:"deletePdbCascade,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // Container database connect string + DBTnsurl string `json:"dbTnsurl,omitempty"` + // lrest server deletion automatically triggers associated pdb deletion + DeletePDBCascade bool `json:"deletePdbCascade,omitempty"` + // Name of the service account + SrvAccountName string `json:"serviceAccountName,omitempty"` + // Detection of pdb created manually via sqlplus or other cli db interface + // It automatically associates a resource to the new pdb + PdbAutoDiscover bool `json:"autodiscover,omitempty"` + // The namespace assigned by default to the new resource when autodiscover is turned on + NamesSpaceAutoDiscover string `json:"namespaceAutoDiscover,omitempty"` + // Specify if cluster ip is required when corev1.Service starts. Note the lrest server + // it's an internal component that should never be visible from outside. Use this parameter + // only if you need to run the operator local. + // +kubebuilder:default=false + ClusterIP bool `json:"clusterIp,omitempty"` + // Create a load balancer: Use this parameter in conjunction with ClusterIP only if you need + // to run the operator local + // +kubebuilder:default=false + LoadBalancer bool `json:"loadBalancer,omitempty"` + // Turn on the sqlnet.trace_level_client + // +kubebuilder:default=0 + SqlNetTrace int `json:"trace_level_client,omitempty"` } // LRESTSecret defines the secretName diff --git a/apis/database/v4/lrest_webhook.go b/apis/database/v4/lrest_webhook.go index 74320388..2a91a17d 100644 --- a/apis/database/v4/lrest_webhook.go +++ b/apis/database/v4/lrest_webhook.go @@ -91,20 +91,21 @@ var _ webhook.CustomValidator = &LREST{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *LREST) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { lrestlog.Info("ValidateCreate", "name", r.Name) + lrest := obj.(*LREST) var allErrs field.ErrorList - if r.Spec.ServiceName == "" && r.Spec.DBServer != "" { + if lrest.Spec.ServiceName == "" && lrest.Spec.DBServer != "" { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("serviceName"), "Please specify LREST Service name")) } - if reflect.ValueOf(r.Spec.LRESTTlsKey).IsZero() { + if reflect.ValueOf(lrest.Spec.LRESTTlsKey).IsZero() { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("lrestTlsKey"), "Please specify LREST Tls key(secret)")) } - if reflect.ValueOf(r.Spec.LRESTTlsCrt).IsZero() { + if reflect.ValueOf(lrest.Spec.LRESTTlsCrt).IsZero() { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("lrestTlsCrt"), "Please specify LREST Tls Certificate(secret)")) } @@ -114,41 +115,41 @@ func (r *LREST) ValidateCreate(ctx context.Context, obj runtime.Object) (admissi field.Required(field.NewPath("spec").Child("scanName"), "Please specify SCAN Name for LREST")) }*/ - if (r.Spec.DBServer == "" && r.Spec.DBTnsurl == "") || (r.Spec.DBServer != "" && r.Spec.DBTnsurl != "") { + if (lrest.Spec.DBServer == "" && lrest.Spec.DBTnsurl == "") || (lrest.Spec.DBServer != "" && lrest.Spec.DBTnsurl != "") { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("dbServer"), "Please specify Database Server Name/IP Address or tnsalias string")) } - if r.Spec.DBTnsurl != "" && (r.Spec.DBServer != "" || r.Spec.DBPort != 0 || r.Spec.ServiceName != "") { + if lrest.Spec.DBTnsurl != "" && (lrest.Spec.DBServer != "" || lrest.Spec.DBPort != 0 || lrest.Spec.ServiceName != "") { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("dbServer"), "DBtnsurl is orthogonal to (DBServer,DBport,Services)")) } - if r.Spec.DBPort == 0 && r.Spec.DBServer != "" { + if lrest.Spec.DBPort == 0 && lrest.Spec.DBServer != "" { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("dbPort"), "Please specify DB Server Port")) } - if r.Spec.DBPort < 0 && r.Spec.DBServer != "" { + if lrest.Spec.DBPort < 0 && lrest.Spec.DBServer != "" { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("dbPort"), "Please specify a valid DB Server Port")) } - if r.Spec.LRESTPort < 0 { + if lrest.Spec.LRESTPort < 0 { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("ordsPort"), "Please specify a valid LREST Port")) } - if r.Spec.Replicas < 0 { + if lrest.Spec.Replicas < 0 { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("replicas"), "Please specify a valid value for Replicas")) } - if r.Spec.LRESTImage == "" { + if lrest.Spec.LRESTImage == "" { allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("ordsImage"), "Please specify name of LREST Image to be used")) + field.Required(field.NewPath("spec").Child("lrestImage"), "Please specify name of LREST Image to be used")) } - if reflect.ValueOf(r.Spec.LRESTAdminUser).IsZero() { + if reflect.ValueOf(lrest.Spec.LRESTAdminUser).IsZero() { allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("lrestAdminUser"), "Please specify user in the root container with sysdba priviledges to manage PDB lifecycle")) + field.Required(field.NewPath("spec").Child("cdbAdminUser"), "Please specify user in the root container with sysdba priviledges to manage PDB lifecycle")) } - if reflect.ValueOf(r.Spec.LRESTAdminPwd).IsZero() { + if reflect.ValueOf(lrest.Spec.LRESTAdminPwd).IsZero() { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("lrestAdminPwd"), "Please specify password for the LREST Administrator to manage PDB lifecycle")) } @@ -156,11 +157,11 @@ func (r *LREST) ValidateCreate(ctx context.Context, obj runtime.Object) (admissi allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("ordsPwd"), "Please specify password for user LREST_PUBLIC_USER")) } */ - if reflect.ValueOf(r.Spec.WebLrestServerUser).IsZero() { + if reflect.ValueOf(lrest.Spec.WebLrestServerUser).IsZero() { allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("webLrestServerUser"), "Please specify the Web Server User having SQL Administrator role")) + field.Required(field.NewPath("spec").Child("webServerUser"), "Please specify the Web Server User having SQL Administrator role")) } - if reflect.ValueOf(r.Spec.WebLrestServerPwd).IsZero() { + if reflect.ValueOf(lrest.Spec.WebLrestServerPwd).IsZero() { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("webServerPwd"), "Please specify password for the Web Server User having SQL Administrator role")) } diff --git a/apis/database/v4/lrpdb_types.go b/apis/database/v4/lrpdb_types.go index d37bebdc..4a6467de 100644 --- a/apis/database/v4/lrpdb_types.go +++ b/apis/database/v4/lrpdb_types.go @@ -44,14 +44,14 @@ import ( // LRPDBSpec defines the desired state of LRPDB type LRPDBSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - + // Secret: tls.key LRPDBTlsKey LRPDBTLSKEY `json:"lrpdbTlsKey,omitempty"` + // Secret: tls.crt LRPDBTlsCrt LRPDBTLSCRT `json:"lrpdbTlsCrt,omitempty"` + // Secret: ca.crt LRPDBTlsCat LRPDBTLSCAT `json:"lrpdbTlsCat,omitempty"` + // Secret for private key LRPDBPriKey LRPDBPRVKEY `json:"cdbPrvKey,omitempty"` - // Namespace of the rest server CDBNamespace string `json:"cdbNamespace,omitempty"` // Name of the CDB Custom Resource that runs the LREST container @@ -66,10 +66,11 @@ type LRPDBSpec struct { AdminName LRPDBAdminName `json:"adminName,omitempty"` // The administrator password for the new LRPDB. This property is required when the Action property is Create. AdminPwd LRPDBAdminPassword `json:"adminPwd,omitempty"` - // Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. + // PDB Admin user AdminpdbUser AdminpdbUser `json:"adminpdbUser,omitempty"` + // PDB Admin user password AdminpdbPass AdminpdbPass `json:"adminpdbPass,omitempty"` - + // Use this parameter on non ASM storage '....path....','pdbname' e.g. '/u01/oradata','dborcl' FileNameConversions string `json:"fileNameConversions,omitempty"` // This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. SourceFileNameConversions string `json:"sourceFileNameConversions,omitempty"` @@ -84,8 +85,10 @@ type LRPDBSpec struct { // A Path specified for sparse clone snapshot copy. (Optional) SparseClonePath string `json:"sparseClonePath,omitempty"` // Whether to reuse temp file + // +kubebuilder:default=true ReuseTempFile *bool `json:"reuseTempFile,omitempty"` // Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. + // +kubebuilder:default=true UnlimitedStorage *bool `json:"unlimitedStorage,omitempty"` // Indicate if 'AS CLONE' option should be used in the command to plug in a LRPDB. This property is applicable when the Action property is PLUG but not required. AsClone *bool `json:"asClone,omitempty"` @@ -96,41 +99,60 @@ type LRPDBSpec struct { // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints WebLrpdbServerUser WebLrpdbServerUser `json:"webServerUser,omitempty"` // Password for the Web Server User - WebLrpdbServerPwd WebLrpdbServerPassword `json:"webServerPwd,omitempt"` + WebLrpdbServerPwd WebLrpdbServerPassword `json:"webServerPwd,omitempty"` // TDE import for plug operations + // +hidefromdoc LTDEImport *bool `json:"tdeImport,omitempty"` // LTDE export for unplug operations + // +hidefromdoc LTDEExport *bool `json:"tdeExport,omitempty"` // TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations + // +hidefromdoc LTDEPassword LTDEPwd `json:"tdePassword,omitempty"` // LTDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + // +hidefromdoc LTDEKeystorePath string `json:"tdeKeystorePath,omitempty"` // LTDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. + // +hidefromdoc LTDESecret LTDESecret `json:"tdeSecret,omitempty"` - // Whether you need the script only or execute the script + // Whether you need the script only or execute the script - legacy parameter + // +kubebuilder:default=false GetScript *bool `json:"getScript,omitempty"` // Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map/Alter. Map is used to map a Databse LRPDB to a Kubernetes LRPDB CR. - // +kubebuilder:validation:Enum=Create;Clone;Plug;Unplug;Delete;Modify;Status;Map;Alter;Noaction - Action string `json:"action"` + // Mainted for backward compatibility. No longer need + Action string `json:"action,omitempty"` // Extra options for opening and closing a LRPDB // +kubebuilder:validation:Enum=IMMEDIATE;NORMAL;READ ONLY;READ WRITE;RESTRICTED ModifyOption string `json:"modifyOption,omitempty"` + // Modify Option2 of the LRPDB + // +kubebuilder:default=NONE + ModifyOption2 string `json:"modifyOption2,omitempty"` // to be used with ALTER option - obsolete do not use AlterSystem string `json:"alterSystem,omitempty"` - // to be used with ALTER option - the name of the parameter - AlterSystemParameter string `json:"alterSystemParameter"` - // to be used with ALTER option - the value of the parameter - AlterSystemValue string `json:"alterSystemValue"` - // parameter scope + // To be used with ALTER option - the name of the parameter + AlterSystemParameter string `json:"alterSystemParameter,omitempty"` + // To be used with ALTER option - the value of the parameter + AlterSystemValue string `json:"alterSystemValue,omitempty"` + // Init parameter scope ParameterScope string `json:"parameterScope,omitempty"` // The target state of the LRPDB - // +kubebuilder:validation:Enum=OPEN;CLOSE;ALTER + // +kubebuilder:validation:Enum=OPEN;CLOSE;ALTER;DELETE;UNPLUG;PLUG;CLONE;RESET;NONE LRPDBState string `json:"pdbState,omitempty"` - // turn on the assertive approach to delete pdb resource - // kubectl delete pdb ..... automatically triggers the pluggable database + // Turn on the imperative approach to delete pdb resource + // kubectl delete pdb command automatically triggers the pluggable database // deletion - AssertiveLrpdbDeletion bool `json:"assertiveLrpdbDeletion,omitempty"` - PDBConfigMap string `json:"pdbconfigmap,omitempty"` + ImperativeLrpdbDeletion bool `json:"imperativeLrpdbDeletion,omitempty"` + // Config map containing the pdb parameters + PDBConfigMap string `json:"pdbconfigmap,omitempty"` + // Config map containing sql(ddl)/plsql code + PLSQLBlock string `json:"codeconfigmap,omitempty"` + // Spare filed not used + PLSQLExecMode int `json:"plsqlexemode,omitempty"` + // For future use - rest bitmask status + // ++kubebuilder:default=0 + PDBBitMask int `json:"reststate,omitempty"` + // Debug option , not yet implemented + Debug int `json:"debug,omitempty"` } // LRPDBAdminName defines the secret containing Sys Admin User mapped to key 'adminName' for LRPDB @@ -208,16 +230,21 @@ type LRPDBStatus struct { OpenMode string `json:"openMode,omitempty"` // Modify Option of the LRPDB ModifyOption string `json:"modifyOption,omitempty"` + // Restricted + Restricted string `json:"restricted,omitempty"` // Message Msg string `json:"msg,omitempty"` // Last Completed Action Action string `json:"action,omitempty"` // Last Completed alter system - AlterSystem string `json:"alterSystem,omitempty"` + PDBBitMask int `json:"pdbBitMask,omitempty"` + PDBBitMaskStr string `json:"pdbBitMaskStr,omitempty"` + AlterSystem string `json:"alterSystem,omitempty"` // Last ORA- - SqlCode int `json:"sqlCode"` - Bitstat int `json:"bitstat,omitempty"` /* Bitmask */ - BitStatStr string `json:"bitstatstr,omitempty"` /* Decoded bitmask */ + SqlCode int `json:"sqlCode"` + LastPLSQL string `json:"lastplsql,omitempty"` + CmBitstat int `json:"bitstat,omitempty"` /* Bitmask */ + CmBitStatStr string `json:"bitstatstr,omitempty"` /* Decoded bitmask */ } // +kubebuilder:object:root=true @@ -226,9 +253,11 @@ type LRPDBStatus struct { // +kubebuilder:printcolumn:JSONPath=".spec.pdbName",name="PDB Name",type="string",description="Name of the PDB" // +kubebuilder:printcolumn:JSONPath=".status.openMode",name="PDB State",type="string",description="PDB Open Mode" // +kubebuilder:printcolumn:JSONPath=".status.totalSize",name="PDB Size",type="string",description="Total Size of the PDB" -// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the LRPDB Resource" // +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" +// +kubebuilder:printcolumn:JSONPath=".status.restricted",name="Restricted",type="string",description="open restricted" // +kubebuilder:printcolumn:JSONPath=".status.sqlCode",name="last sqlcode",type="integer",description="last sqlcode" +// +kubebuilder:printcolumn:JSONPath=".status.lastplsql",name="last PLSQL",type="string",description="last plsql applied" +// +kubebuilder:printcolumn:JSONPath=".status.pdbBitMaskStr",name="BITMASK STATUS",type="string",description="Bitmask status" // +kubebuilder:printcolumn:JSONPath=".status.connString",name="Connect_String",type="string",description="The connect string to be used" // +kubebuilder:resource:path=lrpdbs,scope=Namespaced // +kubebuilder:storageversion diff --git a/apis/database/v4/lrpdb_webhook.go b/apis/database/v4/lrpdb_webhook.go index d6807926..2e1b923d 100644 --- a/apis/database/v4/lrpdb_webhook.go +++ b/apis/database/v4/lrpdb_webhook.go @@ -49,6 +49,7 @@ import ( "strconv" "strings" + . "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -150,39 +151,18 @@ func (r *LRPDB) ValidateCreate(ctx context.Context, obj runtime.Object) (admissi return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "LRPDB"}, r.Name, allErrs) - return nil, nil } // Validate Action for required parameters func (r *LRPDB) validateAction(allErrs *field.ErrorList, ctx context.Context, pdb LRPDB) { - action := strings.ToUpper(pdb.Spec.Action) - - lrpdblog.Info("Valdiating LRPDB Resource Action : " + action) - - if reflect.ValueOf(pdb.Spec.LRPDBTlsKey).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("lrpdbTlsKey"), "Please specify LRPDB Tls Key(secret)")) - } - - if reflect.ValueOf(pdb.Spec.LRPDBTlsCrt).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("lrpdbTlsCrt"), "Please specify LRPDB Tls Certificate(secret)")) - } - if reflect.ValueOf(pdb.Spec.LRPDBTlsCat).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("lrpdbTlsCat"), "Please specify LRPDB Tls Certificate Authority(secret)")) - } + pdbstate := strings.ToUpper(pdb.Spec.LRPDBState) + scrdatabase := strings.ToUpper(pdb.Spec.SrcLRPDBName) + //plsql := strings.ToUpper(pdb.Spec.PLSQLBlock) - switch action { - case "DELETE": - /* BUG 36752336 - LREST OPERATOR - DELETE NON-EXISTENT PDB SHOWS LRPDB CREATED MESSAGE */ - if pdb.Status.OpenMode == "READ WRITE" { - lrpdblog.Info("Cannot delete: pdb is open ") - *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+pdb.Spec.LRPDBName+" "+pdb.Status.OpenMode)) - } - r.CheckObjExistence("DELETE", allErrs, ctx, pdb) - case "CREATE": + lrpdblog.Info("Valdiating LRPDB Resource ") + /* Parameters required by the creation */ + if Bit(pdb.Status.PDBBitMask, PDBCRT) == false { if reflect.ValueOf(pdb.Spec.AdminpdbUser).IsZero() { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("adminpdbUser"), "Please specify LRPDB System Administrator user")) @@ -199,32 +179,32 @@ func (r *LRPDB) validateAction(allErrs *field.ErrorList, ctx context.Context, pd *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("totalSize"), "When the storage is not UNLIMITED the Total Size must be specified")) } - if pdb.Spec.TempSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) - } - if *(pdb.Spec.LTDEImport) { - r.validateTDEInfo(allErrs, ctx, pdb) - } - case "CLONE": - // Sample Err: The LRPDB "lrpdb1-clone" is invalid: spec.srcPdbName: Required value: Please specify source LRPDB for Cloning - if pdb.Spec.SrcLRPDBName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("srcPdbName"), "Please specify source LRPDB name for Cloning")) - } - if pdb.Spec.TotalSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("totalSize"), "When the storage is not UNLIMITED the Total Size must be specified")) - } - if pdb.Spec.TempSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) - } - if pdb.Status.OpenMode == "MOUNT" { - lrpdblog.Info("Cannot clone: pdb is mount ") - *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+pdb.Spec.LRPDBName+" "+pdb.Status.OpenMode)) - } - case "PLUG": + /* + if pdb.Spec.TempSize == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) + } + if *(pdb.Spec.LTDEImport) { + r.validateTDEInfo(allErrs, ctx, pdb) + } + */ + + } + + /* We cannot open|close|delete|unplug a non existing pdb */ + if (pdbstate == "OPEN" || pdbstate == "CLOSE" || pdbstate == "DELETE" || pdbstate == "UNPLUG") && Bit(pdb.Status.PDBBitMask, PDBCRT) == false { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("LRPDBState"), "PDB does not exists")) + } + + /* Database already exists + if scrdatabase != "" && Bit(pdb.Status.PDBBitMask, PDBCRT) == true { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("SrcLRPDBName"), "PDB already exists/Cannot clone")) + } + */ + + if pdbstate == "PLUG" && pdb.Spec.XMLFileName != "" && Bit(pdb.Status.PDBBitMask, PDBCRT) == false && Bit(pdb.Status.PDBBitMask, PDBPLE) == false { if pdb.Spec.XMLFileName == "" { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("xmlFileName"), "Please specify XML metadata filename")) @@ -244,7 +224,10 @@ func (r *LRPDB) validateAction(allErrs *field.ErrorList, ctx context.Context, pd if *(pdb.Spec.LTDEImport) { r.validateTDEInfo(allErrs, ctx, pdb) } - case "UNPLUG": + + } + + if pdbstate == "UNPLUG" && pdb.Spec.XMLFileName != "" && Bit(pdb.Status.PDBBitMask, PDBCRT) == true && Bit(pdb.Status.PDBBitMask, FNALAZ) == true && Bit(pdb.Status.PDBBitMask, PDBUPE) == false { if pdb.Spec.XMLFileName == "" { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("xmlFileName"), "Please specify XML metadata filename")) @@ -257,17 +240,53 @@ func (r *LRPDB) validateAction(allErrs *field.ErrorList, ctx context.Context, pd *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+pdb.Spec.LRPDBName+" "+pdb.Status.OpenMode)) } r.CheckObjExistence("UNPLUG", allErrs, ctx, pdb) - case "MODIFY": + } + + if reflect.ValueOf(pdb.Spec.LRPDBTlsKey).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("lrpdbTlsKey"), "Please specify LRPDB Tls Key(secret)")) + } - if pdb.Spec.LRPDBState == "" { + if reflect.ValueOf(pdb.Spec.LRPDBTlsCrt).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("lrpdbTlsCrt"), "Please specify LRPDB Tls Certificate(secret)")) + } + + if reflect.ValueOf(pdb.Spec.LRPDBTlsCat).IsZero() { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("lrpdbTlsCat"), "Please specify LRPDB Tls Certificate Authority(secret)")) + } + + /* Check clone parameters */ + if scrdatabase != "" && Bit(pdb.Status.PDBBitMask, PDBCRT|FNALAZ|PDBCRE) == false { + if pdb.Spec.TotalSize == "" { *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("lrpdbState"), "Please specify target state of LRPDB")) + field.Required(field.NewPath("spec").Child("totalSize"), "When the storage is not UNLIMITED the Total Size must be specified")) } - if pdb.Spec.ModifyOption == "" && pdb.Spec.AlterSystem == "" { + if pdb.Spec.TempSize == "" { *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("modifyOption"), "Please specify an option for opening/closing a LRPDB or alter system parameter")) + field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) + } + if pdb.Status.OpenMode == "MOUNT" { + lrpdblog.Info("Cannot clone: pdb is mount ") + *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+pdb.Spec.LRPDBName+" "+pdb.Status.OpenMode)) } - r.CheckObjExistence("MODIFY", allErrs, ctx, pdb) + + } + + if pdbstate == "UNPLUG" { + if pdb.Spec.XMLFileName == "" { + *allErrs = append(*allErrs, + field.Required(field.NewPath("spec").Child("xmlFileName"), "Please specify XML metadata filename")) + } + if *(pdb.Spec.LTDEExport) { + r.validateTDEInfo(allErrs, ctx, pdb) + } + if pdb.Status.OpenMode == "READ WRITE" { + lrpdblog.Info("Cannot unplug: pdb is open ") + *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+pdb.Spec.LRPDBName+" "+pdb.Status.OpenMode)) + } + r.CheckObjExistence("UNPLUG", allErrs, ctx, pdb) } } @@ -317,7 +336,6 @@ func (r *LRPDB) ValidateUpdate(ctx context.Context, obj runtime.Object, old runt return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "LRPDB"}, r.Name, allErrs) - return nil, nil } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type @@ -332,10 +350,10 @@ func (r *LRPDB) ValidateDelete(ctx context.Context, obj runtime.Object) (admissi func (r *LRPDB) validateCommon(allErrs *field.ErrorList, ctx context.Context, pdb LRPDB) { lrpdblog.Info("validateCommon", "name", pdb.Name) - if pdb.Spec.Action == "" { + /* if pdb.Spec.Action == "" { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("action"), "Please specify LRPDB operation to be performed")) - } + } */ if pdb.Spec.CDBResName == "" { *allErrs = append(*allErrs, field.Required(field.NewPath("spec").Child("cdbResName"), "Please specify the name of the CDB Kubernetes resource to use for LRPDB operations")) diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 37e36d3b..e1314d38 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -2411,6 +2411,7 @@ func (in *LRESTSpec) DeepCopyInto(out *LRESTSpec) { out.LRESTTlsCrt = in.LRESTTlsCrt out.LRESTPubKey = in.LRESTPubKey out.LRESTPriKey = in.LRESTPriKey + out.LRESTTlsCat = in.LRESTTlsCat out.LRESTPwd = in.LRESTPwd out.WebLrestServerUser = in.WebLrestServerUser out.WebLrestServerPwd = in.WebLrestServerPwd diff --git a/commons/k8s/utils.go b/commons/k8s/utils.go index 37ec1a3f..70b2f810 100644 --- a/commons/k8s/utils.go +++ b/commons/k8s/utils.go @@ -84,3 +84,12 @@ func Patch(kubeClient client.Client, obj client.Object, path string, value inter patch := client.RawPatch(types.JSONPatchType, payloadBytes) return kubeClient.Patch(context.TODO(), obj, patch) } + +func Int64Pointer(d int64) *int64 { + return &d +} + +func BoolPointer(d bool) *bool { + return &d +} + diff --git a/commons/multitenant/lrest/common.go b/commons/multitenant/lrest/common.go index e72e85b0..4ae7b627 100644 --- a/commons/multitenant/lrest/common.go +++ b/commons/multitenant/lrest/common.go @@ -52,7 +52,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" ) -func CommonDecryptWithPrivKey(Key string, Buffer string, req ctrl.Request) (string, error) { +func CommonDecryptWithPrivKey2(Key string, Buffer string, req ctrl.Request) (string, error) { Debug := 0 block, _ := pem.Decode([]byte(Key)) @@ -111,3 +111,173 @@ func ParseConfigMapData(cfgmap *corev1.ConfigMap) []string { return tokens } + +// * STATE TABLE *// +const ( + PDBCRT = 0x00000001 /* Create pdb */ + PDBOPN = 0x00000002 /* Open pdb read write */ + PDBCLS = 0x00000004 /* Close pdb */ + PDBDIC = 0x00000008 /* Drop pdb include datafiles */ + OCIHDL = 0x00000010 /* OCI handle allocation */ + OCICON = 0x00000020 /* Rdbms connection */ + FNALAZ = 0x00000040 /* Finalizer configured */ + PDBUPL = 0x00000080 /* Unplug pdb */ + PDBPLG = 0x00000100 /* plug pdb */ + /* Error section */ + PDBCRE = 0x00001000 /* PDB creation error */ + PDBOPE = 0x00002000 /* PDB open error */ + PDBCLE = 0x00004000 /* PDB close error */ + OCIHDE = 0x00008000 /* Allocation Handle Error */ + OCICOE = 0x00010000 /* CDD connection Error */ + FNALAE = 0x00020000 + PDBUPE = 0x00040000 /* Unplug Error */ + PDBPLE = 0x00080000 /* Plug Error */ + PDBPLW = 0x00100000 /* Plug Warining */ + /* Autodiscover */ + PDBAUT = 0x01000000 /* Autodisover */ +) + +// * CONFIG MAP STATUS * // +const ( + MPAPPL = 0x00000001 /* The map config has been applyed */ + MPSYNC = 0x00000002 /* The map config is in sync with v$parameters where is default=flase */ + MPEMPT = 0x00000004 /* The map is empty - not specify */ + MPWARN = 0x00000008 /* Map applied with warnings */ + MPINIT = 0x00000010 /* Config map init */ + SPARE3 = 0x00000020 +) + +func ParseTnsAlias2(tns *string, lrpdbsrv *string) { + fmt.Printf("Analyzing string [%s]\n", *tns) + fmt.Printf("Relacing srv [%s]\n", *lrpdbsrv) + var swaptns string + + if strings.Contains(strings.ToUpper(*tns), "SERVICE_NAME") == false { + fmt.Print("Cannot generate tns alias for pdb") + return + } + + if strings.Contains(strings.ToUpper(*tns), "ORACLE_SID") == true { + fmt.Print("Cannot generate tns alias for pdb") + return + } + + *tns = strings.ReplaceAll(*tns, " ", "") + + swaptns = fmt.Sprintf("SERVICE_NAME=%s", *lrpdbsrv) + tnsreg := regexp.MustCompile(`SERVICE_NAME=\w+`) + *tns = tnsreg.ReplaceAllString(*tns, swaptns) + + fmt.Printf("Newstring [%s]\n", *tns) + +} + +func Bid(bitmask int, bitval int) int { + bitmask ^= ((bitval) & (bitmask)) + return bitmask +} + +func Bit(bitmask int, bitval int) bool { + if ((bitmask) & (bitval)) != 0 { + return true + } else { + return false + } +} + +func Bis(bitmask int, bitval int) int { + bitmask = ((bitmask) | (bitval)) + return bitmask +} + +func Bitmaskprint(bitmask int) string { + BitRead := "|" + if Bit(bitmask, PDBCRT) { + BitRead = strings.Join([]string{BitRead, "PDBCRT|"}, "") + } + if Bit(bitmask, PDBOPN) { + BitRead = strings.Join([]string{BitRead, "PDBOPN|"}, "") + } + if Bit(bitmask, PDBCLS) { + BitRead = strings.Join([]string{BitRead, "PDBCLS|"}, "") + } + if Bit(bitmask, PDBDIC) { + BitRead = strings.Join([]string{BitRead, "PDBDIC|"}, "") + } + if Bit(bitmask, OCIHDL) { + BitRead = strings.Join([]string{BitRead, "OCIHDL|"}, "") + } + if Bit(bitmask, OCICON) { + BitRead = strings.Join([]string{BitRead, "OCICON|"}, "") + } + if Bit(bitmask, FNALAZ) { + BitRead = strings.Join([]string{BitRead, "FNALAZ|"}, "") + } + if Bit(bitmask, PDBUPL) { + BitRead = strings.Join([]string{BitRead, "PDBUPL|"}, "") + } + if Bit(bitmask, PDBPLG) { + BitRead = strings.Join([]string{BitRead, "PDBPLG|"}, "") + } + + if Bit(bitmask, PDBCRE) { + BitRead = strings.Join([]string{BitRead, "PDBCRE|"}, "") + } + if Bit(bitmask, PDBOPE) { + BitRead = strings.Join([]string{BitRead, "PDBOPE|"}, "") + } + if Bit(bitmask, PDBCLE) { + BitRead = strings.Join([]string{BitRead, "PDBCLE|"}, "") + } + if Bit(bitmask, OCIHDE) { + BitRead = strings.Join([]string{BitRead, "OCIHDE|"}, "") + } + if Bit(bitmask, OCICOE) { + BitRead = strings.Join([]string{BitRead, "OCICOE|"}, "") + } + if Bit(bitmask, FNALAE) { + BitRead = strings.Join([]string{BitRead, "FNALAE|"}, "") + } + if Bit(bitmask, PDBUPE) { + BitRead = strings.Join([]string{BitRead, "PDBUPE|"}, "") + } + if Bit(bitmask, PDBPLE) { + BitRead = strings.Join([]string{BitRead, "PDBPLE|"}, "") + } + if Bit(bitmask, PDBPLW) { + BitRead = strings.Join([]string{BitRead, "PDBPLW|"}, "") + } + if Bit(bitmask, PDBAUT) { + BitRead = strings.Join([]string{BitRead, "PDBAUT|"}, "") + } + + BitRead = fmt.Sprintf("[%d]%s", bitmask, BitRead) + return BitRead +} + +func CMBitmaskprint(bitmask int) string { + + BitRead := "|" + /*** Bit mask for config map ***/ + if Bit(bitmask, MPAPPL) { + BitRead = strings.Join([]string{BitRead, "MPAPPL|"}, "") + } + if Bit(bitmask, MPSYNC) { + BitRead = strings.Join([]string{BitRead, "MPSYNC|"}, "") + } + if Bit(bitmask, MPEMPT) { + BitRead = strings.Join([]string{BitRead, "MPEMPT|"}, "") + } + if Bit(bitmask, MPWARN) { + BitRead = strings.Join([]string{BitRead, "MPWARN|"}, "") + } + if Bit(bitmask, MPINIT) { + BitRead = strings.Join([]string{BitRead, "MPINIT|"}, "") + } + if Bit(bitmask, SPARE3) { + BitRead = strings.Join([]string{BitRead, "SPARE3|"}, "") + } + + BitRead = fmt.Sprintf("[%d]%s", bitmask, BitRead) + return BitRead +} diff --git a/config/crd/bases/database.oracle.com_lrests.yaml b/config/crd/bases/database.oracle.com_lrests.yaml index c20356e7..14d59978 100644 --- a/config/crd/bases/database.oracle.com_lrests.yaml +++ b/config/crd/bases/database.oracle.com_lrests.yaml @@ -55,6 +55,8 @@ spec: type: object spec: properties: + autodiscover: + type: boolean cdbAdminPwd: properties: secret: @@ -117,6 +119,21 @@ spec: required: - secret type: object + cdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object cdbTlsCrt: properties: secret: @@ -147,6 +164,9 @@ spec: required: - secret type: object + clusterIp: + default: false + type: boolean dbPort: type: integer dbServer: @@ -155,6 +175,9 @@ spec: type: string deletePdbCascade: type: boolean + loadBalancer: + default: false + type: boolean lrestImage: type: string lrestImagePullPolicy: @@ -165,6 +188,7 @@ spec: lrestImagePullSecret: type: string lrestPort: + default: 8888 type: integer lrestPwd: properties: @@ -181,12 +205,16 @@ spec: required: - secret type: object + namespaceAutoDiscover: + type: string nodeSelector: additionalProperties: type: string type: object replicas: type: integer + serviceAccountName: + type: string serviceName: type: string sysAdminPwd: @@ -204,6 +232,9 @@ spec: required: - secret type: object + trace_level_client: + default: 0 + type: integer webServerPwd: properties: secret: diff --git a/config/crd/bases/database.oracle.com_lrpdbs.yaml b/config/crd/bases/database.oracle.com_lrpdbs.yaml index 14ad7f29..a9449ab3 100644 --- a/config/crd/bases/database.oracle.com_lrpdbs.yaml +++ b/config/crd/bases/database.oracle.com_lrpdbs.yaml @@ -31,18 +31,26 @@ spec: jsonPath: .status.totalSize name: PDB Size type: string - - description: Status of the LRPDB Resource - jsonPath: .status.phase - name: Status - type: string - description: Error message, if any jsonPath: .status.msg name: Message type: string + - description: open restricted + jsonPath: .status.restricted + name: Restricted + type: string - description: last sqlcode jsonPath: .status.sqlCode name: last sqlcode type: integer + - description: last plsql applied + jsonPath: .status.lastplsql + name: last PLSQL + type: string + - description: Bitmask status + jsonPath: .status.pdbBitMaskStr + name: BITMASK STATUS + type: string - description: The connect string to be used jsonPath: .status.connString name: Connect_String @@ -60,17 +68,6 @@ spec: spec: properties: action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - - Alter - - Noaction type: string adminName: properties: @@ -140,8 +137,6 @@ spec: type: string asClone: type: boolean - assertiveLrpdbDeletion: - type: boolean cdbName: type: string cdbNamespace: @@ -163,12 +158,16 @@ spec: type: object cdbResName: type: string + codeconfigmap: + type: string copyAction: enum: - COPY - NOCOPY - MOVE type: string + debug: + type: integer dropAction: enum: - INCLUDING @@ -177,6 +176,9 @@ spec: fileNameConversions: type: string getScript: + default: false + type: boolean + imperativeLrpdbDeletion: type: boolean lrpdbTlsCat: properties: @@ -231,6 +233,9 @@ spec: - READ WRITE - RESTRICTED type: string + modifyOption2: + default: NONE + type: string parameterScope: type: string pdbName: @@ -240,10 +245,21 @@ spec: - OPEN - CLOSE - ALTER + - DELETE + - UNPLUG + - PLUG + - CLONE + - RESET + - NONE type: string pdbconfigmap: type: string + plsqlexemode: + type: integer + reststate: + type: integer reuseTempFile: + default: true type: boolean sourceFileNameConversions: type: string @@ -292,6 +308,7 @@ spec: totalSize: type: string unlimitedStorage: + default: true type: boolean webServerPwd: properties: @@ -325,11 +342,6 @@ spec: type: object xmlFileName: type: string - required: - - action - - alterSystemParameter - - alterSystemValue - - webServerPwd type: object status: properties: @@ -343,14 +355,22 @@ spec: type: string connString: type: string + lastplsql: + type: string modifyOption: type: string msg: type: string openMode: type: string + pdbBitMask: + type: integer + pdbBitMaskStr: + type: string phase: type: string + restricted: + type: string sqlCode: type: integer status: diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index 4d10607f..5611e3bb 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -524,6 +524,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -1283,6 +1285,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index 925d7e54..a0e9059c 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -136,6 +136,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -524,7 +526,7 @@ spec: proxyFromEnvironment: type: boolean proxyUrl: - pattern: ^http(s)?://.+$ + pattern: ^(http|https|socks5)://.+$ type: string scopes: items: @@ -1837,6 +1839,8 @@ spec: - port type: object type: object + stopSignal: + type: string type: object livenessProbe: properties: @@ -2459,6 +2463,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -2847,7 +2853,7 @@ spec: proxyFromEnvironment: type: boolean proxyUrl: - pattern: ^http(s)?://.+$ + pattern: ^(http|https|socks5)://.+$ type: string scopes: items: @@ -4160,6 +4166,8 @@ spec: - port type: object type: object + stopSignal: + type: string type: object livenessProbe: properties: @@ -4782,6 +4790,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -5170,7 +5180,7 @@ spec: proxyFromEnvironment: type: boolean proxyUrl: - pattern: ^http(s)?://.+$ + pattern: ^(http|https|socks5)://.+$ type: string scopes: items: @@ -6483,6 +6493,8 @@ spec: - port type: object type: object + stopSignal: + type: string type: object livenessProbe: properties: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2726acb4..eb2cad70 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: restart-operator-sa + newName: lin.ocir.io/intsanjaysingh/operator/dboperator_master_250730 + newTag: latest diff --git a/controllers/database/lrest_controller.go b/controllers/database/lrest_controller.go index 91c883e1..f3dd313f 100644 --- a/controllers/database/lrest_controller.go +++ b/controllers/database/lrest_controller.go @@ -39,10 +39,17 @@ package controllers import ( + "bytes" "context" + "crypto/tls" + "crypto/x509" "encoding/json" "errors" "fmt" + "io/ioutil" + "net" + "net/http" + "slices" //"fmt" "strconv" @@ -52,10 +59,15 @@ import ( "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" @@ -68,6 +80,9 @@ import ( dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" dbcommons "github.com/oracle/oracle-database-operator/commons/database" + "github.com/oracle/oracle-database-operator/commons/k8s" + . "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" + lrcommons "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" //lrcommons "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" ) @@ -90,6 +105,8 @@ var ( lrestPhaseReady = "Ready" lrestPhaseDelete = "Deleting" lrestPhaseFail = "Failed" + lrestHealthy = "Healthy" + lrestUnHealthy = "Unhealthy" ) const LRESTFinalizer = "database.oracle.com/LRESTfinalizer" @@ -97,7 +114,7 @@ const LRESTFinalizer = "database.oracle.com/LRESTfinalizer" //+kubebuilder:rbac:groups=database.oracle.com,resources=lrests,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=database.oracle.com,resources=lrests/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=lrests/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;services;configmaps;events;replicasets,verbs=create;delete;get;list;patch;update;watch +//+kubebuilder:rbac:groups="",resources=pods;pods/log;services;configmaps;events;replicasets,verbs=create;delete;get;list;patch;update;watch //+kubebuilder:rbac:groups=core,resources=pods;secrets;services;configmaps;namespaces,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=apps,resources=replicasets,verbs=get;list;watch;create;update;patch;delete @@ -158,6 +175,13 @@ func (r *LRESTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl // If post-creation, LREST spec is changed, check and take appropriate action if (lrest.Status.Phase == lrestPhaseReady) && lrest.Status.Status { r.evaluateSpecChange(ctx, req, lrest) + r.lrestHealthCheck(ctx, req, lrest) + } + + // Auto discover functionality looks for pdb with no crd + if lrest.Spec.PdbAutoDiscover == true && lrest.Status.Status == true { + log.Info("PDB auto discover turned on") + r.PdbAutoDiscover(ctx, req, lrest) } if !lrest.Status.Status { @@ -179,10 +203,10 @@ func (r *LRESTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl log.Info("Reconcile queued") return requeueY, nil } - lrest.Status.Phase = lrestPhaseValPod + lrest.Status.Phase = lrestPhaseService case lrestPhaseValPod: // Validate LREST PODs - err = r.validateLRESTPods(ctx, req, lrest) + err = r.validateLRESTPods2(ctx, req, lrest) if err != nil { if lrest.Status.Phase == lrestPhaseFail { return requeueN, nil @@ -190,7 +214,7 @@ func (r *LRESTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl log.Info("Reconcile queued") return requeueY, nil } - lrest.Status.Phase = lrestPhaseService + lrest.Status.Phase = lrestPhaseReady case lrestPhaseService: // Create LREST Service err = r.createLRESTSVC(ctx, req, lrest) @@ -199,7 +223,7 @@ func (r *LRESTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl return requeueY, nil } //lrest.Status.Phase = lrestPhaseSecrets - lrest.Status.Phase = lrestPhaseReady + lrest.Status.Phase = lrestPhaseValPod case lrestPhaseSecrets: // Delete LREST Secrets //r.deleteSecrets(ctx, req, lrest) @@ -208,7 +232,7 @@ func (r *LRESTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl case lrestPhaseReady: lrest.Status.Status = true r.Status().Update(ctx, lrest) - return requeueN, nil + return requeueY, nil default: lrest.Status.Phase = lrestPhaseInit log.Info("DEFAULT:", "Name", lrest.Name, "Phase", phase, "Status", strconv.FormatBool(lrest.Status.Status)) @@ -217,11 +241,12 @@ func (r *LRESTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl if err := r.Status().Update(ctx, lrest); err != nil { log.Error(err, "Failed to update status for :"+lrest.Name, "err", err.Error()) } + return requeueY, nil } log.Info("Reconcile completed") - return requeueN, nil + return requeueY, nil } /* @@ -262,6 +287,23 @@ func (r *LRESTReconciler) createLRESTInstances(ctx context.Context, req ctrl.Req - Validate LREST Pod. Check if there are any errors /*********************************************** */ +func (r *LRESTReconciler) validateLRESTPods2(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + log := r.Log.WithValues("validateLRESTPod2", req.NamespacedName) + log.Info("Validating Pod creation for :" + lrest.Name) + + /* Just check the number of pdbs to verify lrest server status */ + _, err := r.SelectFromVpdbs(ctx, req, lrest) + if err != nil { + log.Info("LREST is not ready ", "Namespace", req.Namespace) + lrest.Status.Msg = "Waiting for LREST Pod(s) to be read" + return errors.New("Waiting for LREST pods to be ready") + } + + lrest.Status.Msg = "" + return nil + +} + func (r *LRESTReconciler) validateLRESTPods(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { log := r.Log.WithValues("validateLRESTPod", req.NamespacedName) @@ -328,97 +370,16 @@ func (r *LRESTReconciler) validateLRESTPods(ctx context.Context, req ctrl.Reques func (r *LRESTReconciler) createPodSpec(lrest *dbapi.LREST) corev1.PodSpec { podSpec := corev1.PodSpec{ - Volumes: []corev1.Volume{{ - Name: "secrets", - VolumeSource: corev1.VolumeSource{ - Projected: &corev1.ProjectedVolumeSource{ - DefaultMode: func() *int32 { i := int32(0666); return &i }(), - Sources: []corev1.VolumeProjection{ - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: lrest.Spec.LRESTPubKey.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: lrest.Spec.LRESTPubKey.Secret.Key, - Path: lrest.Spec.LRESTPubKey.Secret.Key, - }, - }, - }, - }, - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: lrest.Spec.LRESTPriKey.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: lrest.Spec.LRESTPriKey.Secret.Key, - Path: lrest.Spec.LRESTPriKey.Secret.Key, - }, - }, - }, - }, - - /***/ - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: lrest.Spec.LRESTTlsKey.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: lrest.Spec.LRESTTlsKey.Secret.Key, - Path: lrest.Spec.LRESTTlsKey.Secret.Key, - }, - }, - }, - }, - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: lrest.Spec.LRESTTlsCrt.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: lrest.Spec.LRESTTlsCrt.Secret.Key, - Path: lrest.Spec.LRESTTlsCrt.Secret.Key, - }, - }, - }, - }, - }, - }, - }, - }}, + Volumes: PodVolumes(lrest), /* Volumes */ SecurityContext: &corev1.PodSecurityContext{ - RunAsNonRoot: &[]bool{true}[0], - FSGroup: &[]int64{54321}[0], - SeccompProfile: &corev1.SeccompProfile{ - Type: corev1.SeccompProfileTypeRuntimeDefault, - }, + RunAsNonRoot: k8s.BoolPointer(true), + RunAsUser: k8s.Int64Pointer(dbcommons.ORACLE_UID), + RunAsGroup: k8s.Int64Pointer(dbcommons.ORACLE_GUID), + FSGroup: k8s.Int64Pointer(dbcommons.ORACLE_GUID), + //SeccompProfile: &corev1.SeccompProfile{ + // Type: corev1.SeccompProfileTypeRuntimeDefault, + //}, }, - /*InitContainers: []corev1.Container{{ - Image: lrest.Spec.LRESTImage, - Name: lrest.Name + "-init", - ImagePullPolicy: corev1.PullIfNotPresent, - SecurityContext: securityContextDefineLrest(), - Command: []string{"echo test > /opt/oracle/lrest/certificates/tests"}, - Env: func() []corev1.EnvVar { - return []corev1.EnvVar{ - { - Name: "ORACLE_HOST", - Value: lrest.Spec.DBTnsurl, - }} - }(), - VolumeMounts: []corev1.VolumeMount{ - { - MountPath: "/opt/oracle/lrest/certificates", - Name: "secrets", - ReadOnly: false, - }}, - }},*/ Containers: []corev1.Container{{ Image: lrest.Spec.LRESTImage, Name: lrest.Name + "-lrest", @@ -431,90 +392,7 @@ func (r *LRESTReconciler) createPodSpec(lrest *dbapi.LREST) corev1.PodSpec { ReadOnly: true, }, }, - Env: func() []corev1.EnvVar { - return []corev1.EnvVar{ - { - Name: "ORACLE_HOST", - Value: lrest.Spec.DBServer, - }, - { - Name: "DBTNSURL", - Value: lrest.Spec.DBTnsurl, - }, - { - Name: "TLSCRT", - Value: lrest.Spec.LRESTTlsCrt.Secret.Key, - }, - { - Name: "TLSKEY", - Value: lrest.Spec.LRESTTlsKey.Secret.Key, - }, - { - Name: "PUBKEY", - Value: lrest.Spec.LRESTPubKey.Secret.Key, - }, - { - Name: "PRVKEY", - Value: lrest.Spec.LRESTPriKey.Secret.Key, - }, - { - Name: "ORACLE_PORT", - Value: strconv.Itoa(lrest.Spec.DBPort), - }, - { - Name: "LREST_PORT", - Value: strconv.Itoa(lrest.Spec.LRESTPort), - }, - { - Name: "ORACLE_SERVICE", - Value: lrest.Spec.ServiceName, - }, - { - Name: "R1", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: lrest.Spec.LRESTAdminUser.Secret.SecretName, - }, - Key: lrest.Spec.LRESTAdminUser.Secret.Key, - }, - }, - }, - { - Name: "R2", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: lrest.Spec.LRESTAdminPwd.Secret.SecretName, - }, - Key: lrest.Spec.LRESTAdminPwd.Secret.Key, - }, - }, - }, - { - Name: "R3", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: lrest.Spec.WebLrestServerUser.Secret.SecretName, - }, - Key: lrest.Spec.WebLrestServerUser.Secret.Key, - }, - }, - }, - { - Name: "R4", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: lrest.Spec.WebLrestServerPwd.Secret.SecretName, - }, - Key: lrest.Spec.WebLrestServerPwd.Secret.Key, - }, - }, - }, - } - }(), + Env: ContainerEnv(lrest), /* Environment Variables */ }}, NodeSelector: func() map[string]string { @@ -526,6 +404,8 @@ func (r *LRESTReconciler) createPodSpec(lrest *dbapi.LREST) corev1.PodSpec { } return ns }(), + + ServiceAccountName: lrest.Spec.SrvAccountName, } if len(lrest.Spec.LRESTImagePullSecret) > 0 { @@ -708,7 +588,7 @@ func (r *LRESTReconciler) createLRESTSVC(ctx context.Context, req ctrl.Request, foundSvc := &corev1.Service{} err := r.Get(context.TODO(), types.NamespacedName{Name: lrest.Name + "-lrest", Namespace: lrest.Namespace}, foundSvc) if err != nil && apierrors.IsNotFound(err) { - svc := r.createSvcSpec(lrest) + svc := r.createCoreService(lrest) log.Info("Creating a new Cluster Service for: "+lrest.Name, "Svc.Namespace", svc.Namespace, "Service.Name", svc.Name) err := r.Create(ctx, svc) @@ -731,7 +611,34 @@ func (r *LRESTReconciler) createLRESTSVC(ctx context.Context, req ctrl.Request, - Create Service spec /*********************** */ -func (r *LRESTReconciler) createSvcSpec(lrest *dbapi.LREST) *corev1.Service { + +func (r *LRESTReconciler) createCoreService(lrest *dbapi.LREST) *corev1.Service { + var portLrest int32 + fmt.Sscan(fmt.Sprintf("%d", lrest.Spec.LRESTPort), &portLrest) // 64->32 + svcspecIp := corev1.ServiceSpec{} + svcspecIp.Selector = map[string]string{"name": lrest.Name + "-lrest"} + + if lrest.Spec.ClusterIP == false { + svcspecIp.ClusterIP = corev1.ClusterIPNone + } else { + svcspecIp.Ports = []v1.ServicePort{ + { + Protocol: v1.ProtocolTCP, + Port: 443, + TargetPort: intstr.FromInt(443), + Name: "https", + }, + { + Protocol: v1.ProtocolTCP, + Port: portLrest, + TargetPort: intstr.FromInt(lrest.Spec.LRESTPort), + Name: "lrest-port", + }, + } + if lrest.Spec.LoadBalancer == true { + svcspecIp.Type = v1.ServiceTypeLoadBalancer + } + } svc := &corev1.Service{ TypeMeta: metav1.TypeMeta{ @@ -741,12 +648,7 @@ func (r *LRESTReconciler) createSvcSpec(lrest *dbapi.LREST) *corev1.Service { Name: lrest.Name + "-lrest", Namespace: lrest.Namespace, }, - Spec: corev1.ServiceSpec{ - Selector: map[string]string{ - "name": lrest.Name + "-lrest", - }, - ClusterIP: corev1.ClusterIPNone, - }, + Spec: svcspecIp, } // Set LREST instance as the owner and controller ctrl.SetControllerReference(lrest, svc, r.Scheme) @@ -772,7 +674,7 @@ func (r *LRESTReconciler) manageLRESTDeletion(ctx context.Context, req ctrl.Requ } } else { - log.Info("lrest set to be deleted") + log.Info("lrest mark to be delited") lrest.Status.Phase = lrestPhaseDelete lrest.Status.Status = true r.Status().Update(ctx, lrest) @@ -993,8 +895,9 @@ func (r *LRESTReconciler) SetupWithManager(mgr ctrl.Manager) error { func securityContextDefineLrest() *corev1.SecurityContext { return &corev1.SecurityContext{ - RunAsNonRoot: &[]bool{true}[0], - RunAsUser: &[]int64{54321}[0], + RunAsNonRoot: k8s.BoolPointer(true), + RunAsUser: k8s.Int64Pointer(dbcommons.ORACLE_UID), + RunAsGroup: k8s.Int64Pointer(dbcommons.ORACLE_GUID), AllowPrivilegeEscalation: &[]bool{false}[0], Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{ @@ -1004,6 +907,166 @@ func securityContextDefineLrest() *corev1.SecurityContext { } } +func ContainerEnv(lrest *dbapi.LREST) []corev1.EnvVar { + EnvVar := []corev1.EnvVar{ + { + Name: "ORACLE_HOST", + Value: lrest.Spec.DBServer, + }, + { + Name: "DBTNSURL", + Value: lrest.Spec.DBTnsurl, + }, + { + Name: "TLSCRT", + Value: lrest.Spec.LRESTTlsCrt.Secret.Key, + }, + { + Name: "TLSKEY", + Value: lrest.Spec.LRESTTlsKey.Secret.Key, + }, + { + Name: "PUBKEY", + Value: lrest.Spec.LRESTPubKey.Secret.Key, + }, + { + Name: "PRVKEY", + Value: lrest.Spec.LRESTPriKey.Secret.Key, + }, + { + Name: "ORACLE_PORT", + Value: strconv.Itoa(lrest.Spec.DBPort), + }, + { + Name: "LREST_PORT", + Value: strconv.Itoa(lrest.Spec.LRESTPort), + }, + { + Name: "ORACLE_SERVICE", + Value: lrest.Spec.ServiceName, + }, + { + Name: "TRACE_LEVEL_CLIENT", + Value: strconv.Itoa(lrest.Spec.SqlNetTrace), + }, + { + Name: "R1", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTAdminUser.Secret.SecretName, + }, + Key: lrest.Spec.LRESTAdminUser.Secret.Key, + }, + }, + }, + { + Name: "R2", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTAdminPwd.Secret.SecretName, + }, + Key: lrest.Spec.LRESTAdminPwd.Secret.Key, + }, + }, + }, + { + Name: "R3", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.WebLrestServerUser.Secret.SecretName, + }, + Key: lrest.Spec.WebLrestServerUser.Secret.Key, + }, + }, + }, + { + Name: "R4", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.WebLrestServerPwd.Secret.SecretName, + }, + Key: lrest.Spec.WebLrestServerPwd.Secret.Key, + }, + }, + }, + } + + return EnvVar +} + +func PodVolumes(lrest *dbapi.LREST) []corev1.Volume { + + Volumes := []corev1.Volume{{ + Name: "secrets", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + DefaultMode: func() *int32 { i := int32(0666); return &i }(), + Sources: []corev1.VolumeProjection{ + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTPubKey.Secret.SecretName, + }, + Items: []corev1.KeyToPath{ + { + Key: lrest.Spec.LRESTPubKey.Secret.Key, + Path: lrest.Spec.LRESTPubKey.Secret.Key, + }, + }, + }, + }, + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTPriKey.Secret.SecretName, + }, + Items: []corev1.KeyToPath{ + { + Key: lrest.Spec.LRESTPriKey.Secret.Key, + Path: lrest.Spec.LRESTPriKey.Secret.Key, + }, + }, + }, + }, + + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTTlsKey.Secret.SecretName, + }, + Items: []corev1.KeyToPath{ + { + Key: lrest.Spec.LRESTTlsKey.Secret.Key, + Path: lrest.Spec.LRESTTlsKey.Secret.Key, + }, + }, + }, + }, + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lrest.Spec.LRESTTlsCrt.Secret.SecretName, + }, + Items: []corev1.KeyToPath{ + { + Key: lrest.Spec.LRESTTlsCrt.Secret.Key, + Path: lrest.Spec.LRESTTlsCrt.Secret.Key, + }, + }, + }, + }, + }, + }, + }, + }} + + return Volumes +} + func (r *LRESTReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { log := r.Log.WithValues("DeletePDBS", req.NamespacedName) @@ -1046,7 +1109,7 @@ func (r *LRESTReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, lres } pdbitem.Status.SqlCode = int(objmap["sqlcode"].(float64)) - log.Info("pdb closure.......:", "sqlcode", pdbitem.Status.SqlCode) + log.Info("pdb closuer.......:", "sqlcode", pdbitem.Status.SqlCode) if errapi != nil { log.Error(err, "callAPI cannot close pdb "+pdbitem.Spec.LRPDBName, "err", err.Error()) @@ -1061,7 +1124,7 @@ func (r *LRESTReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, lres values = map[string]string{ "action": "INCLUDING", } - respData, errapi := NewCallLAPI(r, ctx, req, &pdbitem, url, values, "DELETE") + respData, errapi := NewCallAPISQL(r, ctx, req, &pdbitem, url, values, "DELETE") if err := json.Unmarshal([]byte(respData), &objmap); err != nil { log.Error(err, "failed to get respData from callAPI", "err", err.Error()) @@ -1076,7 +1139,6 @@ func (r *LRESTReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, lres return err } r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "drop pdb", "pdbname=%s", pdbitem.Spec.LRPDBName) - /* remove finalizer */ if controllerutil.ContainsFinalizer(&pdbitem, LRPDBFinalizer) { @@ -1100,6 +1162,520 @@ func (r *LRESTReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, lres } } - /* ================================================ */ return nil } + +func SearchElementInDbList(element string, TheList []string) bool { + var inthelist bool + inthelist = false + for idx := range TheList { + if strings.ToLower(element) == strings.ToLower(TheList[idx]) { + inthelist = true + return inthelist + } + } + return inthelist +} + +func (r *LRESTReconciler) SelectFromVpdbs(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) ([]interface{}, error) { + log := r.Log.WithValues("SelectFromVpdbs", req.NamespacedName) + url := "https://" + lrest.Name + "-lrest." + lrest.Namespace + ":" + strconv.Itoa(lrest.Spec.LRESTPort) + "/database/pdbs/" + + output, err := NewCallAPIAllPdbs(r, ctx, req, lrest, url, nil, "GET") + if err != nil { + log.Info("NewCallAPIAllPdbs Error") + } + + data := []byte(` {"PDBS":` + output + `}`) + var idata interface{} + err = json.Unmarshal(data, &idata) + if err != nil { + log.Info("error json.Unmarshal") + return nil, err + } + + mdata := idata.(map[string]interface{}) + ndata := mdata["PDBS"].([]interface{}) + + return ndata, nil + +} + +func (r *LRESTReconciler) LrpdbCreation(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST, dbinfo []interface{}, idx int) error { + log := r.Log.WithValues("LrpdbCreation", req.NamespacedName) + log.Info("Creating LRPDB for :" + dbinfo[idx].(map[string]interface{})["name"].(string)) + + cln, err := dynamic.NewForConfig(r.Config) + if err != nil { + log.Error(err, "Kubernetes Config Error") + return err + } + + TlsCrtecobj := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "secret": map[string]interface{}{ + "key": lrest.Spec.LRESTTlsCrt.Secret.Key, + "secretName": lrest.Spec.LRESTTlsCrt.Secret.SecretName, + }, + }, + } + + TlsCatecobj := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "secret": map[string]interface{}{ + "key": lrest.Spec.LRESTTlsCat.Secret.Key, + "secretName": lrest.Spec.LRESTTlsCat.Secret.SecretName, + }, + }, + } + + TlsKeyecobj := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "secret": map[string]interface{}{ + "key": lrest.Spec.LRESTTlsKey.Secret.Key, + "secretName": lrest.Spec.LRESTTlsKey.Secret.SecretName, + }, + }, + } + + WebUseObj := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "secret": map[string]interface{}{ + "key": lrest.Spec.WebLrestServerUser.Secret.Key, + "secretName": lrest.Spec.WebLrestServerUser.Secret.SecretName, + }, + }, + } + + WebPasObj := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "secret": map[string]interface{}{ + "key": lrest.Spec.WebLrestServerPwd.Secret.Key, + "secretName": lrest.Spec.WebLrestServerPwd.Secret.SecretName, + }, + }, + } + + CdbPrvKeyObj := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "secret": map[string]interface{}{ + "key": lrest.Spec.LRESTPriKey.Secret.Key, + "secretName": lrest.Spec.LRESTPriKey.Secret.SecretName, + }, + }, + } + + TotSzStr := fmt.Sprintf("%f", dbinfo[idx].(map[string]interface{})["total_size"].(float64)) + + log.Info("secretName:" + lrest.Spec.WebLrestServerUser.Secret.SecretName) + log.Info("secretName:" + lrest.Spec.WebLrestServerPwd.Secret.SecretName) + log.Info("DEBUGSIZE::" + TotSzStr) + + var NamesSpaceAutoDiscover string + if lrest.Spec.NamesSpaceAutoDiscover != "" { + NamesSpaceAutoDiscover = lrest.Spec.NamesSpaceAutoDiscover + } else { + NamesSpaceAutoDiscover = lrest.Namespace + } + log.Info("NamesSpaceAutoDiscover := " + NamesSpaceAutoDiscover) + + obj := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "database.oracle.com/v4", + "kind": "LRPDB", + "metadata": map[string]interface{}{ + "name": "atd-" + strings.ToLower(dbinfo[idx].(map[string]interface{})["name"].(string)), + "namespace": NamesSpaceAutoDiscover, + }, + "spec": map[string]interface{}{ + "pdbName": dbinfo[idx].(map[string]interface{})["name"].(string), + "cdbNamespace": lrest.Namespace, + "cdbResName": lrest.Name, + "cdbName": lrest.Spec.LRESTName, + "totalSize": TotSzStr, + "lrpdbTlsCrt": TlsCrtecobj, + "lrpdbTlsCat": TlsCatecobj, + "lrpdbTlsKey": TlsKeyecobj, + "cdbPrvKey": CdbPrvKeyObj, + "webServerUser": WebUseObj, + "webServerPwd": WebPasObj, + "adminName": WebUseObj, /* Place holder */ + "adminPwd": WebUseObj, /* Place holder */ + "adminpdbUser": WebUseObj, /* Place holder */ + "adminpdbPass": WebUseObj, /* Place holder */ + "fileNameConversions": "NONE", + "imperativeLrpdbDeletion": true, + "pdbBitMask": PDBCRT, + }, + }, + } + + obj.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "database.oracle.com", + Version: "v4", + Kind: "LRPDB", + }) + result, err := cln.Resource(schema.GroupVersionResource{ + Group: "database.oracle.com", + Version: "v4", + Resource: "lrpdbs", + }).Namespace(NamesSpaceAutoDiscover).Create(context.TODO(), obj, metav1.CreateOptions{}) + + if err != nil { + log.Error(err, "Error creating custom resource: ") + } else { + log.Info("Custom resource created successfully ") + fmt.Printf("obj:%s\n", result) + r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "LrpdbCreation", "created lrpdb:%s", "atd-"+dbinfo[idx].(map[string]interface{})["name"].(string)) + + } + + var lrpdb dbapi.LRPDB + + err = r.Get(context.Background(), client.ObjectKey{ + Namespace: NamesSpaceAutoDiscover, + Name: "atd-" + strings.ToLower(dbinfo[idx].(map[string]interface{})["name"].(string)), + }, &lrpdb) + + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBAUT) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + + err = r.Status().Update(context.Background(), &lrpdb) + + return nil +} + +func (r *LRESTReconciler) PdbAutoDiscover(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { + log := r.Log.WithValues("PdbAutoDiscover", req.NamespacedName) + + // SELECT * FROM V$PDBS + ndata, err := r.SelectFromVpdbs(ctx, req, lrest) + + // LIST OF ALL LRPDB + log.Info("Get list of lrpdb resources\n") + var pdbNameList []string /* the list of pdb name */ + lrpdbList := &dbapi.LRPDBList{} + listOpts := []client.ListOption{} + err = r.List(ctx, lrpdbList, listOpts...) + if err != nil { + log.Info("Failed to get the list of pdbs") + return err + } + + for _, pdbitem := range lrpdbList.Items { + if (pdbitem.Spec.CDBName == lrest.Spec.LRESTName) && Bit(pdbitem.Status.PDBBitMask, PDBCRT) == true { + log.Info("CRD(lrpdb): " + pdbitem.Name + ":" + pdbitem.Spec.LRPDBName) + pdbNameList = slices.Insert(pdbNameList, len(pdbNameList), pdbitem.Spec.LRPDBName) + } + } + + lrpdbList01 := &dbapi.LRPDBList{} + for idx := range ndata { + name := ndata[idx].(map[string]interface{})["name"].(string) + log.Info("PDB:" + name) + if name != "PDB$SEED" { + InTheList := SearchElementInDbList(name, pdbNameList) + if InTheList == false { + log.Info("Orphan PDB:[" + name + "]") + /*** Final check ***/ + listOpts01 := []client.ListOption{client.MatchingFields{"spec.pdbName": strings.ToLower(name)}} + err = r.List(ctx, lrpdbList01, listOpts01...) + if err != nil { + log.Info("Failed to get the list02 of pdbs") + + return err + } + if len(lrpdbList01.Items) != 0 { + log.Info("Db gets crd in the meantime.....") + return nil + } + + err := r.LrpdbCreation(ctx, req, lrest, ndata, idx) + if err != nil { + log.Error(err, "error calling r.LrpdbCreation") + } + } + } + } + + return nil + +} + +func NewCallAPIAllPdbs(intr interface{}, ctx context.Context, req ctrl.Request, lrest *dbapi.LREST, url string, payload map[string]string, action string) (string, error) { + var c client.Client + var r logr.Logger + var e record.EventRecorder + var err error + + recpdb, ok1 := intr.(*LRPDBReconciler) + if ok1 { + fmt.Printf("func NewCallLApi ((*PDBReconciler),......)\n") + c = recpdb.Client + e = recpdb.Recorder + r = recpdb.Log + } + + reccdb, ok2 := intr.(*LRESTReconciler) + if ok2 { + fmt.Printf("func NewCallLApi ((*CDBReconciler),......)\n") + c = reccdb.Client + e = reccdb.Recorder + r = reccdb.Log + } + + log := r.WithValues("NewCallAPIAllPdbs", req.NamespacedName) + + secret := &corev1.Secret{} + + err = c.Get(ctx, types.NamespacedName{Name: lrest.Spec.LRESTTlsKey.Secret.SecretName, Namespace: lrest.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrest.Spec.LRESTTlsKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + rsaKeyPEM := secret.Data[lrest.Spec.LRESTTlsKey.Secret.Key] + + err = c.Get(ctx, types.NamespacedName{Name: lrest.Spec.LRESTTlsCrt.Secret.SecretName, Namespace: lrest.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrest.Spec.LRESTTlsCrt.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + rsaCertPEM := secret.Data[lrest.Spec.LRESTTlsCrt.Secret.Key] + + err = c.Get(ctx, types.NamespacedName{Name: lrest.Spec.LRESTTlsCat.Secret.SecretName, Namespace: lrest.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrest.Spec.LRESTTlsCat.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + caCert := secret.Data[lrest.Spec.LRESTTlsCat.Secret.Key] + + certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) + if err != nil { + lrest.Status.Msg = "Error tls.X509KeyPair" + return "", err + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + /* + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool} + */ + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool, + //MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + PreferServerCipherSuites: true, + CipherSuites: []uint16{ + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_RSA_WITH_AES_256_CBC_SHA, + }, + } + + tr := &http.Transport{TLSClientConfig: tlsConf} + + httpclient := &http.Client{Transport: tr} + + log.Info("Issuing REST call", "URL", url, "Action", action) + + // Get Web Server User + //secret := &corev1.Secret{} + err = c.Get(ctx, types.NamespacedName{Name: lrest.Spec.WebLrestServerUser.Secret.SecretName, Namespace: lrest.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrest.Spec.WebLrestServerUser.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + webUserEnc := string(secret.Data[lrest.Spec.WebLrestServerUser.Secret.Key]) + webUserEnc = strings.TrimSpace(webUserEnc) + + err = c.Get(ctx, types.NamespacedName{Name: lrest.Spec.LRESTPriKey.Secret.SecretName, Namespace: lrest.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrest.Spec.LRESTPriKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + privKey := string(secret.Data[lrest.Spec.LRESTPriKey.Secret.Key]) + webUser, err := lrcommons.CommonDecryptWithPrivKey2(privKey, webUserEnc, req) + + // Get Web Server User Password + secret = &corev1.Secret{} + err = c.Get(ctx, types.NamespacedName{Name: lrest.Spec.WebLrestServerPwd.Secret.SecretName, Namespace: lrest.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrest.Spec.WebLrestServerPwd.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + webUserPwdEnc := string(secret.Data[lrest.Spec.WebLrestServerPwd.Secret.Key]) + webUserPwdEnc = strings.TrimSpace(webUserPwdEnc) + webUserPwd, err := lrcommons.CommonDecryptWithPrivKey2(privKey, webUserPwdEnc, req) + + var httpreq *http.Request + if action == "GET" { + httpreq, err = http.NewRequest(action, url, nil) + } else { + jsonValue, _ := json.Marshal(payload) + httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) + } + + if err != nil { + log.Info("Unable to create HTTP Request for LRPDB : "+lrest.Name, "err", err.Error()) + return "", err + } + + httpreq.Header.Add("Accept", "application/json") + httpreq.Header.Add("Content-Type", "application/json") + httpreq.SetBasicAuth(webUser, webUserPwd) + + resp, err := httpclient.Do(httpreq) + if err != nil { + log.Info("Rest server temporary unavailable") + errmsg := err.Error() + log.Error(err, "Failed - Could not connect to LREST Pod", "err", err.Error()) + lrest.Status.Msg = "Error: Could not connect to LREST Pod" + e.Eventf(lrest, corev1.EventTypeWarning, "LRESTError", errmsg) + return "", err + } + + e.Eventf(lrest, corev1.EventTypeWarning, "Done", lrest.Spec.LRESTName) + if resp.StatusCode != http.StatusOK { + bb, _ := ioutil.ReadAll(resp.Body) + + if resp.StatusCode == 404 { + log.Info("error 404") + + } else { + if flood_control == false { + lrest.Status.Msg = "LREST Error - HTTP Status Code:" + strconv.Itoa(resp.StatusCode) + } + } + + if flood_control == false { + log.Info("LREST Error - HTTP Status Code :"+strconv.Itoa(resp.StatusCode), "Err", string(bb)) + } + + var apiErr LRESTError + json.Unmarshal([]byte(bb), &apiErr) + if flood_control == false { + e.Eventf(lrest, corev1.EventTypeWarning, "LRESTError", "Failed: %s", apiErr.Message) + } + fmt.Printf("\n================== APIERR ======================\n") + fmt.Printf("%+v \n", apiErr) + fmt.Printf("URL=%s\n", url) + fmt.Printf("resp.StatusCode=%s\n", strconv.Itoa(resp.StatusCode)) + fmt.Printf("\n================== APIERR ======================\n") + flood_control = true + return "", errors.New("LREST Error") + } + flood_control = false + + defer resp.Body.Close() + + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Print(err.Error()) + } + respData := string(bodyBytes) + fmt.Print("CALL API return msg.....:") + fmt.Println(string(bodyBytes)) + + var apiResponse restSQLCollection + json.Unmarshal([]byte(bodyBytes), &apiResponse) + fmt.Printf("===> %#v\n", apiResponse) + fmt.Printf("===> %+v\n", apiResponse) + + errFound := false + for _, sqlItem := range apiResponse.Items { + if sqlItem.ErrorDetails != "" { + log.Info("LREST Error - Oracle Error Code :" + strconv.Itoa(sqlItem.ErrorCode)) + if !errFound { + lrest.Status.Msg = sqlItem.ErrorDetails + } + e.Eventf(lrest, corev1.EventTypeWarning, "OraError", "%s", sqlItem.ErrorDetails) + errFound = true + } + } + + if errFound { + return "", errors.New("Oracle Error") + } + + return respData, nil +} + +func (r *LRESTReconciler) lrestHealthCheck(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) { + log := r.Log.WithValues("lrestHealthCheck", req.NamespacedName) + + //* Check port status *// + + // lrestHealthy = "Healthy" + // lrestUnHealthy = "Unhealthy" + + log.Info("starting lrest health check") + lrest.Status.Msg = lrestHealthy + RestPort := lrest.Spec.LRESTPort + RestName := lrest.Name + "-lrest" + RestNmsp := lrest.Namespace + Ip := RestName + "." + RestNmsp + ":" + strconv.Itoa(RestPort) + _, err := net.DialTimeout("tcp", Ip, time.Duration(300)*time.Millisecond) + + if err != nil { + log.Error(err, "net.DialTimeout", "err", err.Error()) + if lrest.Status.Msg == lrestHealthy { + // Sent event only if we go from Healthy to unHealthy + r.Recorder.Eventf(lrest, corev1.EventTypeWarning, "net.DialTimeout ", "lrest=%s", lrest.Name+"."+lrest.Namespace) + } + + lrest.Status.Msg = lrestUnHealthy + + } + + //* Check rdbms availability *// + // We can check the pdb$seed status to verify that cdb is aliave + // in the future we can expose a rest call for OCIPing + + url := "https://" + Ip + "/database/pdbs/PDB$SEED/status/" + _, err = NewCallAPIAllPdbs(r, ctx, req, lrest, url, nil, "GET") + if err != nil { + log.Info("NewCallAPIAllPdbs Error") + if lrest.Status.Msg == lrestHealthy { + // Sent event only if we go from Healthy to unHealthy + r.Recorder.Eventf(lrest, corev1.EventTypeWarning, "RDBMS issue ", "lrest=%s", lrest.Name+"."+lrest.Namespace) + } + + lrest.Status.Msg = lrestUnHealthy + } + + if err := r.Status().Update(ctx, lrest); err != nil { + log.Error(err, "Failed to update status for :"+lrest.Name, "err", err.Error()) + } + +} diff --git a/controllers/database/lrpdb_controller.go b/controllers/database/lrpdb_controller.go index 1aadf65b..a0b6f874 100644 --- a/controllers/database/lrpdb_controller.go +++ b/controllers/database/lrpdb_controller.go @@ -47,6 +47,8 @@ import ( "encoding/base64" "encoding/json" "encoding/pem" + "reflect" + "sort" //"encoding/pem" "errors" @@ -58,9 +60,10 @@ import ( "strings" "time" + databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" "github.com/oracle/oracle-database-operator/commons/k8s" - lrcommons "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" + . "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" @@ -80,14 +83,50 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" ) +/* + + BITMASK STATUS RECAP. + ~~~~~~~~~~~~~~~~~~~~ + PDBCRT = 0x00000001 -- Create pdb + PDBOPN = 0x00000002 -- Open pdb read write + PDBCLS = 0x00000004 -- Close pdb + PDBDIC = 0x00000008 -- Drop pdb include datafiles + OCIHDL = 0x00000010 -- OCI handle allocation + OCICON = 0x00000020 -- Rdbms connection + FNALAZ = 0x00000040 -- Finalizer configured + PDBUPL = 0x00000080 -- Unplug pdb + PDBPLG = 0x00000100 -- plug pdb + -- Error section -- + PDBCRE = 0x00001000 -- PDB creation error + PDBOPE = 0x00002000 -- PDB open error + PDBCLE = 0x00004000 -- PDB close error + OCIHDE = 0x00008000 -- Allocation Handle Error + OCICOE = 0x00010000 -- CDD connection Error + FNALAE = 0x00020000 -- Finalizer error + PDBUPE = 0x00040000 -- Unplug Error + PDBPLE = 0x00080000 -- Plug Error + PDBPLW = 0x00100000 -- Plug Warining + -- Autodiscover + PDBAUT = 0x01000000 -- Autodisover + + + BITMASK CONFIGMAP PARAMETER RECAP. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + MPAPPL = 0x00000001 -- The map config has been applyed + MPSYNC = 0x00000002 -- The map config is in sync with v$parameters where is default=flase + MPEMPT = 0x00000004 -- The map is empty - not specify + MPWARN = 0x00000008 -- Map applied with warnings + MPINIT = 0x00000010 -- Config map init + SPARE3 = 0x00000020 -- + + +*/ + // Bitmask functions + const ( - MPAPPL = 0x00000001 /* The map config has been applyed */ - MPSYNC = 0x00000002 /* The map config is in sync with v$parameters where is default=flase */ - MPEMPT = 0x00000004 /* The map is empty - not specify */ - MPWARN = 0x00000008 /* Map applied with warnings */ - MPINIT = 0x00000010 /* Config map init */ - SPARE3 = 0x00000020 + DBGAPI = 0x00000001 /* Debug NewcallApi */ + DBGCRT = 0x00000002 /* Debug pdb creation */ ) func bis(bitmask int, bitval int) int { @@ -108,30 +147,6 @@ func bid(bitmask int, bitval int) int { return bitmask } -func bitmaskprint(bitmask int) string { - BitRead := "|" - if bit(bitmask, MPAPPL) { - BitRead = strings.Join([]string{BitRead, "MPAPPL|"}, "") - } - if bit(bitmask, MPSYNC) { - BitRead = strings.Join([]string{BitRead, "MPSYNC|"}, "") - } - if bit(bitmask, MPEMPT) { - BitRead = strings.Join([]string{BitRead, "MPEMPT|"}, "") - } - if bit(bitmask, MPWARN) { - BitRead = strings.Join([]string{BitRead, "MPWARN|"}, "") - } - if bit(bitmask, MPINIT) { - BitRead = strings.Join([]string{BitRead, "MPINIT|"}, "") - } - if bit(bitmask, SPARE3) { - BitRead = strings.Join([]string{BitRead, "SPARE3|"}, "") - } - - return BitRead -} - // LRPDBReconciler reconciles a LRPDB object type LRPDBReconciler struct { client.Client @@ -165,6 +180,11 @@ type LRESTError struct { Instance string `json:"instance,omitempty"` } +type PLSQLPayLoad struct { + Values map[string]string + Sqltokens []string +} + var ( lrpdbPhaseCreate = "Creating" lrpdbPhasePlug = "Plugging" @@ -179,6 +199,7 @@ var ( lrpdbPhaseFail = "Failed" lrpdbPhaseAlterPlug = "AlterPlugDb" lrpdbPhaseSpare = "NoAction" + lrpdbPhaseApplySql = "ApplySqlCode" ) const LRPDBFinalizer = "database.oracle.com/LRPDBfinalizer" @@ -186,7 +207,7 @@ const LRPDBFinalizer = "database.oracle.com/LRPDBfinalizer" var tde_Password string var tde_Secret string var flood_control bool = false -var assertiveLpdbDeletion bool = false /* Global variable for assertive pdb deletion */ +var imperativeLpdbDeletion bool = false /* Global variable for imperative pdb deletion */ /* We need to record the config map name after pdb creation in order to use it during open and clone op if config map @@ -210,786 +231,625 @@ var globalsqlcode int // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile + +/**** RECONCILIATION LOOP ****/ func (r *LRPDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithValues("multitenantoperator", req.NamespacedName) log.Info("Reconcile requested") reconcilePeriod := r.Interval * time.Second requeueY := ctrl.Result{Requeue: true, RequeueAfter: reconcilePeriod} - requeueN := ctrl.Result{} + //requeueN := ctrl.Result{} var err error lrpdb := &dbapi.LRPDB{} - // Execute for every reconcile - defer func() { - //log.Info("DEFER LRPDB", "Name", lrpdb.Name, "Phase", lrpdb.Status.Phase, "Status", strconv.FormatBool(lrpdb.Status.Status)) - if !lrpdb.Status.Status { - if lrpdb.Status.Phase == lrpdbPhaseReady { - lrpdb.Status.Status = true - } - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) - } - } - }() - + /**** GET CLIENT ****/ err = r.Client.Get(context.TODO(), req.NamespacedName, lrpdb) if err != nil { if apierrors.IsNotFound(err) { - log.Info("LRPDB Resource Not found", "Name", lrpdb.Name) - // Request object not found, could have been deleted after reconcile req. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - lrpdb.Status.Status = true + log.Info("PDB resource not found", "Pdb", lrpdb.Spec.LRPDBName) return requeueN, nil } - // Error reading the object - requeue the req. - return requeueY, err + log.Info("Client.Get Error") + return requeueN, err } - // Finalizer section - err = r.manageLRPDBDeletion2(ctx, req, lrpdb) - if err != nil { - log.Info("Reconcile queued") - return requeueY, nil - } - - // Check for Duplicate LRPDB - if !lrpdb.Status.Status { - err = r.checkDuplicateLRPDB(ctx, req, lrpdb) + /**** CREATE ****/ + if Bit(lrpdb.Status.PDBBitMask, PDBCRT|PDBCRE) == false && lrpdb.Spec.SrcLRPDBName == "" && lrpdb.Spec.XMLFileName == "" { + log.Info("REC. LOOP: create pdb") + err = r.CreateLRPDB(ctx, req, lrpdb) if err != nil { - return requeueN, nil + log.Error(err, err.Error()) + return requeueN, err } + } - action := strings.ToUpper(lrpdb.Spec.Action) - /* - Bug 36714702 - LREST OPERATOR - POST ALTER PDB OPTION LRPDB STATUS INTERMITTENTLY - SHOWS "WAITING FOR LRPDB PARAMETER TO BE MODIFIED" - introducing additional check to avoid alter system repetition during - reconciliation loop - */ - if lrpdb.Status.Phase == lrpdbPhaseReady { - if (lrpdb.Status.Action != "" || action != "NOACTION") && (action == "ALTER" || action == "MODIFY" || action == "STATUS" || lrpdb.Status.Action != action) { - lrpdb.Status.Status = false - } else { - err = r.getLRPDBState(ctx, req, lrpdb) - if err != nil { - lrpdb.Status.Phase = lrpdbPhaseFail - } else { - lrpdb.Status.Phase = lrpdbPhaseReady - lrpdb.Status.Msg = "Success" + /*** INIT CONFIG MAP ***/ + if Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true && Bit(lrpdb.Status.CmBitstat, MPINIT) == false { + log.Info("REC. LOOP: init config map") + r.InitConfigMap(ctx, req, lrpdb) + } + + /*** FINALYZER ***/ + if Bit(lrpdb.Status.PDBBitMask, FNALAZ) == false && Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true { + if lrpdb.ObjectMeta.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + log.Info("REC. LOOP: add finalizer") + controllerutil.AddFinalizer(lrpdb, LRPDBFinalizer) + if err := r.Update(ctx, lrpdb); err != nil { + log.Info("Cannot add finalizer") + return requeueN, err + + } + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, FNALAZ) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) } - r.Status().Update(ctx, lrpdb) } } - if !lrpdb.Status.Status { - r.validatePhase(ctx, req, lrpdb) - phase := lrpdb.Status.Phase - log.Info("LRPDB:", "Name", lrpdb.Name, "Phase", phase, "Status", strconv.FormatBool(lrpdb.Status.Status)) - - switch phase { - case lrpdbPhaseCreate: - err = r.createLRPDB(ctx, req, lrpdb) - case lrpdbPhaseClone: - err = r.cloneLRPDB(ctx, req, lrpdb) - case lrpdbPhasePlug: - err = r.plugLRPDB(ctx, req, lrpdb) - case lrpdbPhaseUnplug: - err = r.unplugLRPDB(ctx, req, lrpdb) - case lrpdbPhaseModify: - err = r.modifyLRPDB(ctx, req, lrpdb) - case lrpdbPhaseDelete: - err = r.deleteLRPDB(ctx, req, lrpdb) - case lrpdbPhaseStatus: - err = r.getLRPDBState(ctx, req, lrpdb) - case lrpdbPhaseMap: - err = r.mapLRPDB(ctx, req, lrpdb) - case lrpdbPhaseFail: - err = r.mapLRPDB(ctx, req, lrpdb) - case lrpdbPhaseAlterPlug: - err = r.alterSystemLRPDB(ctx, req, lrpdb) - default: - log.Info("DEFAULT:", "Name", lrpdb.Name, "Phase", phase, "Status", strconv.FormatBool(lrpdb.Status.Status)) - return requeueN, nil - } - lrpdb.Status.Action = strings.ToUpper(lrpdb.Spec.Action) + /**** OPEN ****/ + if lrpdb.Spec.LRPDBState == "OPEN" && Bit(lrpdb.Status.PDBBitMask, PDBOPN|PDBOPE) == false { + log.Info("REC. LOOP: open pdb") + err = r.OpenLRPDB(ctx, req, lrpdb) if err != nil { - lrpdb.Status.Phase = lrpdbPhaseFail - lrpdb.Status.SqlCode = globalsqlcode - } else { - lrpdb.Status.Phase = lrpdbPhaseReady - lrpdb.Status.Msg = "Success" + log.Error(err, err.Error()) + return requeueN, err } } - r.ManageConfigMapForCloningAndPlugin(ctx, req, lrpdb) - lrpdb.Status.BitStatStr = bitmaskprint(lrpdb.Status.Bitstat) + /**** CLOSE ****/ + if lrpdb.Spec.LRPDBState == "CLOSE" && Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true { + log.Info("REC. LOOP: open pdb") + err = r.CloseLRPDB(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err + } + } - log.Info("Reconcile completed") - return requeueY, nil -} + /**** DELETE (imperative approach) ****/ + if !lrpdb.ObjectMeta.DeletionTimestamp.IsZero() && Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true && Bit(lrpdb.Status.PDBBitMask, FNALAZ) == true && Bit(lrpdb.Status.PDBBitMask, PDBDIC) == false { + log.Info("REC. LOOP: delete pdb - imperative approach") + log.Info(" ObjectMeta.DeletionTimestamp.IsZero is not null") + err = r.DeleteLRPDB(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err + } -/* -************************************************ - - Validate the LRPDB Spec - /*********************************************** -*/ -func (r *LRPDBReconciler) validatePhase(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) { - - log := r.Log.WithValues("validatePhase", req.NamespacedName) - - action := strings.ToUpper(lrpdb.Spec.Action) - - log.Info("Validating LRPDB phase for: "+lrpdb.Name, "Action", action) - - switch action { - case "CREATE": - lrpdb.Status.Phase = lrpdbPhaseCreate - case "CLONE": - lrpdb.Status.Phase = lrpdbPhaseClone - case "PLUG": - lrpdb.Status.Phase = lrpdbPhasePlug - case "UNPLUG": - lrpdb.Status.Phase = lrpdbPhaseUnplug - case "MODIFY": - lrpdb.Status.Phase = lrpdbPhaseModify - case "DELETE": - lrpdb.Status.Phase = lrpdbPhaseDelete - case "STATUS": - lrpdb.Status.Phase = lrpdbPhaseStatus - case "MAP": - lrpdb.Status.Phase = lrpdbPhaseMap - case "ALTER": - lrpdb.Status.Phase = lrpdbPhaseAlterPlug - case "NOACTION": - lrpdb.Status.Phase = lrpdbPhaseStatus - - } - - log.Info("Validation complete") -} + } -/* - This function scans the list of crd - pdb to verify the existence of the - pdb (crd) that we want to clone. - Bug 36752925 - LREST OPERATOR - CLONE NON-EXISTENT - PDB CREATES A LRPDB WITH STATUS FAILED + /**** DELETE (declarative approach) ****/ + if lrpdb.Spec.LRPDBState == "DELETE" && Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true && Bit(lrpdb.Status.PDBBitMask, FNALAZ) == true && Bit(lrpdb.Status.PDBBitMask, PDBDIC) == false { + log.Info("REC. LOOP: delete pdb - imperative approach") + err = r.DeleteLRPDBDeclarative(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err + } - return 1 - CRD found - return 0 - CRD not found / Stop clone process + } - Bug 36753107 - LREST OPERATOR - CLONE - CLOSED PDB SUCCESSFULLY CLONES + /**** CLONE *****/ + if lrpdb.Spec.SrcLRPDBName != "" && Bit(lrpdb.Status.PDBBitMask, PDBCRT|FNALAZ|PDBCRE) == false { + log.Info("REC. LOOP: clone pdb ") + err = r.CloneLRPDB(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err + } -*/ + } -func (r *LRPDBReconciler) checkPDBforCloninig(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, targetPdbName string) (int, error) { - log := r.Log.WithValues("checkDuplicateLRPDB", req.NamespacedName) - var pdbCounter int - pdbCounter = 0 + /**** UNPLUG AND PLUG SECTION ****/ + if lrpdb.Spec.LRPDBState == "UNPLUG" && lrpdb.Spec.XMLFileName != "" && Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true && Bit(lrpdb.Status.PDBBitMask, FNALAZ) == true && Bit(lrpdb.Status.PDBBitMask, PDBUPE) == false { + log.Info("REC. LOOP: unplug pdb ") + err = r.UnplugLRPDB(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err + } - lrpdbList := &dbapi.LRPDBList{} - listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingFields{"spec.pdbName": targetPdbName}} - err := r.List(ctx, lrpdbList, listOpts...) - if err != nil { - log.Info("Failed to list lrpdbs", "Namespace", req.Namespace, "Error", err) - return 0, err - } - if len(lrpdbList.Items) == 0 { - log.Info("No pdbs available") - return pdbCounter, err } - for _, p := range lrpdbList.Items { - fmt.Printf("DEBUGCLONE %s %s %i\n", p.Spec.LRPDBName, targetPdbName, pdbCounter) - if p.Spec.LRPDBName == targetPdbName { - log.Info("Found " + targetPdbName + " in the crd list") - if p.Status.OpenMode == "MOUNTED" { - log.Info("Cannot clone a mounted pdb") - return pdbCounter, err - } - pdbCounter++ - fmt.Printf("DEBUGCLONE %s %s %i\n", p.Spec.LRPDBName, targetPdbName, pdbCounter) - return pdbCounter, err + if lrpdb.Spec.LRPDBState == "PLUG" && lrpdb.Spec.XMLFileName != "" && Bit(lrpdb.Status.PDBBitMask, PDBCRT) == false && Bit(lrpdb.Status.PDBBitMask, PDBPLE) == false { + log.Info("REC. LOOP: plug pdb ") + err = r.PlugLRPDB(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err } } - return pdbCounter, err -} -/* -*************************************************************** - - Check for Duplicate LRPDB. Same LRPDB name on the same LREST resource. + /**** APPLY CONFIG MAP PARAMETER ****/ + if lrpdb.Spec.PDBConfigMap != "" && Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true && Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true && Bit(lrpdb.Status.CmBitstat, MPAPPL) == false && lrpdb.Spec.LRPDBState != "UNPLUG" { + log.Info("REC. LOOP: plug pdb ") + log.Info("Apply configmap:" + lrpdb.Spec.PDBConfigMap) + Cardinality, err := r.ApplyConfigMap(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err + } + log.Info("Config. Map Cardinality:" + strconv.FormatInt(int64(Cardinality), 10)) -/************************************************************** -*/ -func (r *LRPDBReconciler) checkDuplicateLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + } - log := r.Log.WithValues("checkDuplicateLRPDB", req.NamespacedName) + /**** APPLY PLSQL/SQL SCRIPT *****/ + if lrpdb.Spec.PLSQLBlock != "" && Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true && Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true && lrpdb.Spec.LRPDBState != "UNPLUG" && Bit(lrpdb.Status.CmBitstat, MPINIT) == true && Bit(lrpdb.Status.PDBBitMask, FNALAZ) == true { + log.Info("REC. LOOP: apply plsql/sql") + err = r.execPLSQL(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err + } - // Name of the LREST CR that holds the LREST container - lrestResName := lrpdb.Spec.CDBResName - //lrestame := lrpdb.Spec.LRESTName + } - // Name of the LRPDB resource - lrpdbResName := lrpdb.Spec.LRPDBName + /**** ALTER SYSTEM ****/ + if lrpdb.Spec.AlterSystemValue != "" && lrpdb.Spec.AlterSystemParameter != "" && Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true && Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true && lrpdb.Spec.LRPDBState != "UNPLUG" && Bit(lrpdb.Status.CmBitstat, MPINIT) == true && Bit(lrpdb.Status.PDBBitMask, FNALAZ) == true && lrpdb.Spec.PLSQLBlock == "" { + log.Info("REC. LOOP: Alter system ") + err = r.alterSystemLRPDB(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err + } - lrpdbList := &dbapi.LRPDBList{} + } - listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingFields{"spec.pdbName": lrpdbResName}} + /**** MONITOR PDB *****/ + if Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true && Bit(lrpdb.Status.PDBBitMask, FNALAZ) == true && lrpdb.Spec.PLSQLBlock == "" && lrpdb.Spec.AlterSystemValue == "" && lrpdb.Spec.XMLFileName == "" && Bit(lrpdb.Status.CmBitstat, MPINIT) == true { + log.Info("REC. LOOP: Monitor PDB") + err = r.MonitorLRPDB(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err + } - // List retrieves list of objects for a given namespace and list options. - err := r.List(ctx, lrpdbList, listOpts...) - if err != nil { - log.Info("Failed to list lrpdbs", "Namespace", req.Namespace, "Error", err) - return err } - if len(lrpdbList.Items) == 0 { - log.Info("No lrpdbs found for LRPDBName: "+lrpdbResName, "CDBResName", lrestResName) - return nil - } + /* REST STAT */ + if lrpdb.Spec.PDBBitMask != 0 && lrpdb.Spec.LRPDBState == "RESET" { + log.Info("REC. LOOP: reset state") + lrpdb.Status.PDBBitMask = lrpdb.Spec.PDBBitMask + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) + lrpdb.Spec.PDBBitMask = 0 + lrpdb.Spec.LRPDBState = "NONE" - for _, p := range lrpdbList.Items { - log.Info("Found LRPDB: " + p.Name) - if (p.Name != lrpdb.Name) && (p.Spec.CDBResName == lrestResName) { - log.Info("Duplicate LRPDB found") - lrpdb.Status.Msg = "LRPDB Resource already exists" - lrpdb.Status.Status = false - lrpdb.Status.Phase = lrpdbPhaseFail - return errors.New("Duplicate LRPDB found") + err = r.Update(ctx, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return requeueN, err } } - return nil + + return requeueY, nil } /* -*************************************************************** - - Get the Custom Resource for the LREST mentioned in the LRPDB Spec - /************************************************************** -*/ -func (r *LRPDBReconciler) getLRESTResource(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (dbapi.LREST, error) { - - log := r.Log.WithValues("getLRESTResource", req.NamespacedName) +********************************************************************* + - MONITOR PDB - var lrest dbapi.LREST // LREST CR corresponding to the LREST name specified in the LRPDB spec +********************************************************************* +*/ - // Name of the LREST CR that holds the LREST container - lrestResName := lrpdb.Spec.CDBResName - lrestNamespace := lrpdb.Spec.CDBNamespace +func (r *LRPDBReconciler) MonitorLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("MonitorLRPDB ", req.NamespacedName) + r.getLRPDBState(ctx, req, lrpdb) - log.Info("lrestResName...........:" + lrestResName) - log.Info("lrestNamespace.........:" + lrestNamespace) + /* Check open mode consistency */ + if Bit(lrpdb.Status.PDBBitMask, PDBCLS) == true && lrpdb.Status.OpenMode == "READ WRITE" { + log.Info("Open mode inconsistency.......:target:close - status read write") + log.Info("Fix inconsistency.............:call(r.CloseLRPDB(ctx, req, lrpdb) )") + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "open mode inconsistency", "Target:[PDBCLS] Status:['%s']", lrpdb.Status.OpenMode) + err := r.CloseLRPDB(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return err + } - // Get LREST CR corresponding to the LREST name specified in the LRPDB spec - err := r.Get(context.Background(), client.ObjectKey{ - Namespace: lrestNamespace, - Name: lrestResName, - }, &lrest) + return nil + } - if err != nil { - log.Info("Failed to get CRD for LREST", "Name", lrestResName, "Namespace", lrestNamespace, "Error", err.Error()) - lrpdb.Status.Msg = "Unable to get CRD for LREST : " + lrestResName - r.Status().Update(ctx, lrpdb) - return lrest, err + if Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true && lrpdb.Status.OpenMode == "MOUNTED" { + log.Info("Open mode inconsistency.......:target:read write - status mounted") + log.Info("Fix inconsistency.............:call(r.OpenLRPDB(ctx, req, lrpdb) )") + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "open mode inconsistency", "Target:[PDBOPN] Status:['%s']", lrpdb.Status.OpenMode) + err := r.OpenLRPDB(ctx, req, lrpdb) + if err != nil { + log.Error(err, err.Error()) + return err + } + return nil } - log.Info("Found CR for LREST", "Name", lrestResName, "CR Name", lrest.Name) - return lrest, nil + return nil } /* -*************************************************************** - - Get the LREST Pod for the LREST mentioned in the LRPDB Spec - /************************************************************** -*/ -func (r *LRPDBReconciler) getLRESTPod(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (corev1.Pod, error) { +********************************************************************* + - PLUG PDB - log := r.Log.WithValues("getLRESTPod", req.NamespacedName) +********************************************************************* +*/ - var lrestPod corev1.Pod // LREST Pod container with connection to the concerned LREST +func (r *LRPDBReconciler) PlugLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - // Name of the LREST CR that holds the LREST container - lrestResName := lrpdb.Spec.CDBResName + log := r.Log.WithValues("PlugLRPDB", req.NamespacedName) + globalsqlcode = 0 - // Get LREST Pod associated with the LREST Name specified in the LRPDB Spec - err := r.Get(context.Background(), client.ObjectKey{ - Namespace: req.Namespace, - Name: lrestResName + "-lrest", - }, &lrestPod) + var err error + // var tde_Password string + // var tde_Secret string + lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { - log.Info("Failed to get Pod for LREST", "Name", lrestResName, "Namespace", req.Namespace, "Error", err.Error()) - lrpdb.Status.Msg = "Unable to get LREST Pod for LREST : " + lrestResName - return lrestPod, err + return err } - log.Info("Found LREST Pod for LREST", "Name", lrestResName, "Pod Name", lrestPod.Name, "LREST Container hostname", lrestPod.Spec.Hostname) - return lrestPod, nil -} + values := map[string]string{ + "method": "PLUG", + "xmlFileName": lrpdb.Spec.XMLFileName, + "pdb_name": lrpdb.Spec.LRPDBName, + "sourceFileNameConversions": lrpdb.Spec.SourceFileNameConversions, + "copyAction": lrpdb.Spec.CopyAction, + "fileNameConversions": lrpdb.Spec.FileNameConversions, + "unlimitedStorage": strconv.FormatBool(*(lrpdb.Spec.UnlimitedStorage)), + "reuseTempFile": strconv.FormatBool(*(lrpdb.Spec.ReuseTempFile)), + "totalSize": lrpdb.Spec.TotalSize, + "tempSize": lrpdb.Spec.TempSize, + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} -/* -************************************************ - - Get Secret Key for a Secret Name - /*********************************************** -*/ -func (r *LRPDBReconciler) getSecret(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, secretName string, keyName string) (string, error) { + /* + if *(lrpdb.Spec.LTDEImport) { + tde_Password, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDEPassword.Secret.SecretName, lrpdb.Spec.LTDEPassword.Secret.Key) + if err != nil { + return err + } + tde_Secret, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDESecret.Secret.SecretName, lrpdb.Spec.LTDESecret.Secret.Key) + if err != nil { + return err + } - log := r.Log.WithValues("getSecret", req.NamespacedName) + tde_Secret = tde_Secret[:len(tde_Secret)-1] + tde_Password = tde_Secret[:len(tde_Password)-1] + values["tde_Password"] = tde_Password + values["tdeKeystorePath"] = lrpdb.Spec.LTDEKeystorePath + values["tde_Secret"] = tde_Secret + values["tdeImport"] = strconv.FormatBool(*(lrpdb.Spec.LTDEImport)) + } - secret := &corev1.Secret{} - err := r.Get(ctx, types.NamespacedName{Name: secretName, Namespace: lrpdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + secretName) - lrpdb.Status.Msg = "Secret not found:" + secretName - return "", err + if *(lrpdb.Spec.AsClone) { + values["asClone"] = strconv.FormatBool(*(lrpdb.Spec.AsClone)) } - log.Error(err, "Unable to get the secret.") - return "", err - } - - return string(secret.Data[keyName]), nil -} - -/* -************************************************ - - Issue a REST API Call to the LREST container - /*********************************************** -*/ -func (r *LRPDBReconciler) callAPI(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, url string, payload map[string]string, action string) (string, error) { - log := r.Log.WithValues("callAPI", req.NamespacedName) - - var err error + */ - secret := &corev1.Secret{} + lrpdb.Status.Msg = "plug:[op. in progress]" + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBPLG) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) - err = r.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsKey.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + url := r.BaseUrl(ctx, req, lrpdb, lrest) + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "POST") if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsKey.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + return err } - rsaKeyPEM := secret.Data[lrpdb.Spec.LRPDBTlsKey.Secret.Key] - err = r.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - - rsaCertPEM := secret.Data[lrpdb.Spec.LRPDBTlsCrt.Secret.Key] - - err = r.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCat.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + if lrpdb.Status.SqlCode != 0 { + globalsqlcode = lrpdb.Status.SqlCode + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBPLE) /* Upplug error */ + lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBPLG) /* Remove unplug flag */ + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) /* Print the oracle error */ + lrpdb.Status.Msg = "close:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + return errors.New(oer) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCat.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err } - caCert := secret.Data[lrpdb.Spec.LRPDBTlsCat.Secret.Key] - /* - r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaKeyPEM)) - r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaCertPEM)) - r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(caCert)) - */ + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Created", "LRPDB '%s' plugged successfully", lrpdb.Spec.LRPDBName) - certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) - if err != nil { - lrpdb.Status.Msg = "Error tls.X509KeyPair" - return "", err + if lrest.Spec.DBServer != "" { + lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName + } else { + log.Info("Parsing connectstring") + lrpdb.Status.ConnString = lrest.Spec.DBTnsurl + parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) } - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) - /* - tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, - RootCAs: caCertPool} - */ - tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, - RootCAs: caCertPool, - //MinVersion: tls.VersionTLS12, - CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, - PreferServerCipherSuites: true, - CipherSuites: []uint16{ - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_256_CBC_SHA, - }, + imperativeLpdbDeletion = lrpdb.Spec.ImperativeLrpdbDeletion + if lrpdb.Spec.ImperativeLrpdbDeletion == true { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Plug", "PDB '%s' imperative pdb deletion turned on", lrpdb.Spec.LRPDBName) } - tr := &http.Transport{TLSClientConfig: tlsConf} + r.getLRPDBState(ctx, req, lrpdb) - httpclient := &http.Client{Transport: tr} + lrpdb.Status.Msg = "plug:[op. completed]" + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCRT) /* Set the creation flag */ + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBOPN) /* Set the creation flag */ + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) - log.Info("Issuing REST call", "URL", url, "Action", action) + log.Info("Successfully plugged LRPDB", "LRPDB Name", lrpdb.Spec.LRPDBName) + return nil +} - webUser, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.WebLrpdbServerUser.Secret.SecretName, lrpdb.Spec.WebLrpdbServerUser.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) - if err != nil { - log.Error(err, "Unable to get webuser account name ") - return "", err - } +/* +********************************************************************* + - UNPLUG PDB - webUserPwd, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.WebLrpdbServerPwd.Secret.SecretName, lrpdb.Spec.WebLrpdbServerPwd.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) - if err != nil { - log.Error(err, "Unable to get webuser account password ") - return "", err - } +********************************************************************* +*/ - var httpreq *http.Request - if action == "GET" { - httpreq, err = http.NewRequest(action, url, nil) - } else { - jsonValue, _ := json.Marshal(payload) - httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) - } +func (r *LRPDBReconciler) UnplugLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - if err != nil { - log.Info("Unable to create HTTP Request for LRPDB : "+lrpdb.Name, "err", err.Error()) - return "", err - } + log := r.Log.WithValues("unplugLRPDB", req.NamespacedName) + globalsqlcode = 0 - httpreq.Header.Add("Accept", "application/json") - httpreq.Header.Add("Content-Type", "application/json") - httpreq.SetBasicAuth(webUser, webUserPwd) + var err error + //var tde_Password string + //var tde_Secret string - resp, err := httpclient.Do(httpreq) + lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { - errmsg := err.Error() - log.Error(err, "Failed - Could not connect to LREST Pod", "err", err.Error()) - lrpdb.Status.Msg = "Error: Could not connect to LREST Pod" - r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", errmsg) - return "", err + return err } - r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "Done", lrpdb.Spec.CDBResName) - if resp.StatusCode != http.StatusOK { - bb, _ := ioutil.ReadAll(resp.Body) - - if resp.StatusCode == 404 { - lrpdb.Status.ConnString = "" - lrpdb.Status.Msg = lrpdb.Spec.LRPDBName + " not found" + values := map[string]string{ + "method": "UNPLUG", + "xmlFileName": lrpdb.Spec.XMLFileName, + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - } else { - if flood_control == false { - lrpdb.Status.Msg = "LREST Error - HTTP Status Code:" + strconv.Itoa(resp.StatusCode) + /* + if *(lrpdb.Spec.LTDEExport) { + tde_Password, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDEPassword.Secret.SecretName, lrpdb.Spec.LTDEPassword.Secret.Key) + if err != nil { + return err + } + tde_Secret, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDESecret.Secret.SecretName, lrpdb.Spec.LTDESecret.Secret.Key) + if err != nil { + return err } - } - if flood_control == false { - log.Info("LREST Error - HTTP Status Code :"+strconv.Itoa(resp.StatusCode), "Err", string(bb)) + tde_Secret = tde_Secret[:len(tde_Secret)-1] + tde_Password = tde_Secret[:len(tde_Password)-1] + values["tde_Password"] = tde_Password + values["tdeKeystorePath"] = lrpdb.Spec.LTDEKeystorePath + values["tde_Secret"] = tde_Secret + values["tdeExport"] = strconv.FormatBool(*(lrpdb.Spec.LTDEExport)) } + */ - var apiErr LRESTError - json.Unmarshal([]byte(bb), &apiErr) - if flood_control == false { - r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", "Failed: %s", apiErr.Message) - } - fmt.Printf("\n================== APIERR ======================\n") - fmt.Printf("%+v \n", apiErr) - fmt.Printf(string(bb)) - fmt.Printf("URL=%s\n", url) - fmt.Printf("resp.StatusCode=%s\n", strconv.Itoa(resp.StatusCode)) - fmt.Printf("\n================== APIERR ======================\n") - flood_control = true - return "", errors.New("LREST Error") + lrpdb.Status.Msg = "unplug:[op. in progress]" + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBUPL) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + + if Bit(lrpdb.Status.PDBBitMask, PDBPLG) { /*database already plugged in the past */ + lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBPLG) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) } - flood_control = false - defer resp.Body.Close() + r.UpdateStatus(ctx, req, lrpdb) + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdb.Spec.LRPDBName - bodyBytes, err := ioutil.ReadAll(resp.Body) + log.Info("CallAPI(url)", "url", url) + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "POST") if err != nil { - fmt.Print(err.Error()) + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + return err } - respData := string(bodyBytes) - fmt.Print("CALL API return msg.....:") - fmt.Println(string(bodyBytes)) - var apiResponse restSQLCollection - json.Unmarshal([]byte(bodyBytes), &apiResponse) - fmt.Printf("===> %#v\n", apiResponse) - fmt.Printf("===> %+v\n", apiResponse) + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + r.UpdateStatus(ctx, req, lrpdb) + + if lrpdb.Status.SqlCode != 0 { + globalsqlcode = lrpdb.Status.SqlCode + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBUPE) /* Upplug error */ + lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBUPL) /* Remove unplug flag */ + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) /* Print the oracle error */ + lrpdb.Status.Msg = "close:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + return errors.New(oer) - errFound := false - for _, sqlItem := range apiResponse.Items { - if sqlItem.ErrorDetails != "" { - log.Info("LREST Error - Oracle Error Code :" + strconv.Itoa(sqlItem.ErrorCode)) - if !errFound { - lrpdb.Status.Msg = sqlItem.ErrorDetails - } - r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "OraError", "%s", sqlItem.ErrorDetails) - errFound = true - } } - if errFound { - return "", errors.New("Oracle Error") + /*... CRD is going to be delete... loging message in the logfile */ + lrpdb.Status.Msg = "unplug:[op. completed]" + r.UpdateStatus(ctx, req, lrpdb) + log.Info("unplug:[op. completed]") + + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + log.Info("Removing finalizer") + controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) + err = r.Update(ctx, lrpdb) + if err != nil { + log.Info("Could not remove finalizer", "err", err.Error()) + return err + } + lrpdb.Status.Status = true + err = r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + if err != nil { + log.Info("Could not delete LRPDB resource", "err", err.Error()) + return err + } } - return respData, nil + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Unplugged", "LRPDB '%s' unplugged successfully", lrpdb.Spec.LRPDBName) + globalsqlcode = 0 + log.Info("Successfully unplugged LRPDB resource") + return nil } /* -************************************************ - - Create a LRPDB +********************************************************************* + - OPEN PDB -*********************************************** +********************************************************************* */ -func (r *LRPDBReconciler) createLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { +func (r *LRPDBReconciler) OpenLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - log := r.Log.WithValues("createLRPDB", req.NamespacedName) - - var err error - var tde_Password string - var tde_Secret string + log := r.Log.WithValues("OpenLRPDB", req.NamespacedName) + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Modify", "Info:'%s %s %s' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.LRPDBState, lrpdb.Status.ModifyOption) - log.Info("call getLRESTResource \n") lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { + log.Info("Failure: Cannot get lrest info") return err } - lrpdbAdminName, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.AdminpdbUser.Secret.SecretName, lrpdb.Spec.AdminpdbUser.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) - if err != nil { - log.Error(err, "Unable to find pdb admin user ") - return err - } - - lrpdbAdminPwd, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.AdminpdbPass.Secret.SecretName, lrpdb.Spec.AdminpdbPass.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) + values := map[string]string{} + values = map[string]string{ + "state": lrpdb.Spec.LRPDBState, + "modifyOption": lrpdb.Spec.ModifyOption, + "modifyOption2": lrpdb.Spec.ModifyOption2, + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - if err != nil { - log.Error(err, "Unable to find pdb admin password ") - return err + if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { + log.Info("MODIFY LRPDB", "lrpdb.Spec.LRPDBState=", lrpdb.Spec.LRPDBState, "lrpdb.Spec.ModifyOption=", lrpdb.Spec.ModifyOption) + log.Info("LRPDB STATUS OPENMODE", "lrpdb.Status.OpenMode=", lrpdb.Status.OpenMode) } - err = r.getLRPDBState(ctx, req, lrpdb) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Check LRPDB not existence completed", "LRPDB Name", lrpdb.Spec.LRPDBName) - } + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName - } else { + lrpdb.Status.Msg = "open:[op in progress]" + r.UpdateStatus(ctx, req, lrpdb) - lrpdb.Status.Phase = lrpdbPhaseFail - lrpdb.Status.Msg = "PDB " + lrpdb.Spec.LRPDBName + " already exists " - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) - } - log.Info("Database already exists ", "LRPDB Name", lrpdb.Spec.LRPDBName) - err := fmt.Errorf("%v", 65012) + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "POST") + if err != nil { + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) return err } - values := map[string]string{ - "method": "CREATE", - "pdb_name": lrpdb.Spec.LRPDBName, - "adminName": lrpdbAdminName, - "adminPwd": lrpdbAdminPwd, - "fileNameConversions": lrpdb.Spec.FileNameConversions, - "reuseTempFile": strconv.FormatBool(*(lrpdb.Spec.ReuseTempFile)), - "unlimitedStorage": strconv.FormatBool(*(lrpdb.Spec.UnlimitedStorage)), - "totalSize": lrpdb.Spec.TotalSize, - "tempSize": lrpdb.Spec.TempSize, - "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - - fmt.Printf("===== PAYLOAD ===\n") - fmt.Print(" method ", values["method"], "\n") - fmt.Print(" pdb_name ", values["pdb_name"], "\n") - fmt.Print(" adminName ", values["adminName"], "\n") - fmt.Print(" adminPwd --------------\n") - fmt.Print(" fileNameConversions ", values["fileNameConversions"], "\n") - fmt.Print(" unlimitedStorage ", values["unlimitedStorage"], "\n") - fmt.Print(" reuseTempFile ", values["reuseTempFile"], "\n") - fmt.Print(" tempSize ", values["tempSize"], "\n") - fmt.Print(" totalSize ", values["totalSize"], "\n") - fmt.Print(" getScript ", values["getScript"], "\n") - - if *(lrpdb.Spec.LTDEImport) { - tde_Password, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDEPassword.Secret.SecretName, lrpdb.Spec.LTDEPassword.Secret.Key) - if err != nil { - return err - } - tde_Secret, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDESecret.Secret.SecretName, lrpdb.Spec.LTDESecret.Secret.Key) - if err != nil { - return err - } - - tde_Secret = tde_Secret[:len(tde_Secret)-1] - tde_Password = tde_Secret[:len(tde_Password)-1] - values["tde_Password"] = tde_Password - values["tdeKeystorePath"] = lrpdb.Spec.LTDEKeystorePath - values["tde_Secret"] = tde_Secret + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + /* if sqlcode is zero then unset the closebit */ + if lrpdb.Status.SqlCode == 0 { + lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBCLS) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) } - //url := "https://" + lrpdb.Spec.CDBResName + "-lrest:" + strconv.Itoa(lrest.Spec.LRESTPort) + "/database/pdbs/" - url := r.BaseUrl(ctx, req, lrpdb, lrest) - fmt.Print("============================================================\n") - fmt.Print(url) - fmt.Print("\n============================================================\n") - lrpdb.Status.TotalSize = lrpdb.Spec.TotalSize - lrpdb.Status.Phase = lrpdbPhaseCreate - lrpdb.Status.Msg = "Waiting for LRPDB to be created" - - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) - } + r.UpdateStatus(ctx, req, lrpdb) - respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") - if err != nil { - log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) - return err + /* Return Error if sqlcode != */ + if lrpdb.Status.SqlCode != 0 { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBOPE) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "open:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + return errors.New(oer) } - r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) globalsqlcode = lrpdb.Status.SqlCode - if lrpdb.Status.SqlCode != 0 { - err := fmt.Errorf("%v", lrpdb.Status.SqlCode) - return err - } + r.getLRPDBState(ctx, req, lrpdb) - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, - "Created", "LRPDB '%s' created successfully", lrpdb.Spec.LRPDBName) + if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Modified", " '%s' modified successfully '%s'", lrpdb.Spec.LRPDBName, lrpdb.Spec.LRPDBState) + } if lrest.Spec.DBServer != "" { - lrpdb.Status.ConnString = - lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName + lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName } else { - log.Info("Parsing connectstring") lrpdb.Status.ConnString = lrest.Spec.DBTnsurl parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) - } - assertiveLpdbDeletion = lrpdb.Spec.AssertiveLrpdbDeletion - if lrpdb.Spec.AssertiveLrpdbDeletion == true { - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Created", "PDB '%s' assertive pdb deletion turned on", lrpdb.Spec.LRPDBName) } - r.getLRPDBState(ctx, req, lrpdb) - log.Info("Created LRPDB Resource", "LRPDB Name", lrpdb.Spec.LRPDBName) + lrpdb.Status.Msg = "open:[op. completed]" - if bit(lrpdb.Status.Bitstat, MPINIT) == false { - r.InitConfigMap(ctx, req, lrpdb) - Cardinality, _ := r.ApplyConfigMap(ctx, req, lrpdb) - log.Info("Config Map Cardinality " + strconv.Itoa(int(Cardinality))) - } + log.Info("Successfully modified LRPDB state", "LRPDB Name", lrpdb.Spec.LRPDBName) - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + /* After database openining we reapply the config map if warning is present */ + if lrpdb.Spec.LRPDBState == "OPEN" { + if bit(lrpdb.Status.CmBitstat, MPWARN|MPINIT) { + log.Info("re-apply config map") + r.ApplyConfigMap(ctx, req, lrpdb) + + } } + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBOPN) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) return nil } /* -************************************************ - - Clone a LRPDB - /*********************************************** -*/ -func (r *LRPDBReconciler) cloneLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - - if lrpdb.Spec.LRPDBName == lrpdb.Spec.SrcLRPDBName { - return nil - } +********************************************************************* + - CLOSE PDB - log := r.Log.WithValues("cloneLRPDB", req.NamespacedName) +********************************************************************* +*/ +func (r *LRPDBReconciler) CloseLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - globalsqlcode = 0 - var err error + log := r.Log.WithValues("CloseLRPDB", req.NamespacedName) + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Close", "Info:'%s %s %s' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.LRPDBState, lrpdb.Status.ModifyOption) lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { + log.Info("Failure: Cannot get lrest info") return err } - /* Prevent cloning an existing lrpdb */ - err = r.getLRPDBState(ctx, req, lrpdb) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Check LRPDB not existence completed", "LRPDB Name", lrpdb.Spec.LRPDBName) - } - - } else { - log.Info("Database already exists ", "LRPDB Name", lrpdb.Spec.LRPDBName) - return nil - } - - values := map[string]string{ - "method": "CLONE", - "pdb_name": lrpdb.Spec.LRPDBName, - "srcPdbName": lrpdb.Spec.SrcLRPDBName, - "reuseTempFile": strconv.FormatBool(*(lrpdb.Spec.ReuseTempFile)), - "unlimitedStorage": strconv.FormatBool(*(lrpdb.Spec.UnlimitedStorage)), - "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + values := map[string]string{} + values = map[string]string{ + "state": lrpdb.Spec.LRPDBState, + "modifyOption": lrpdb.Spec.ModifyOption, + "modifyOption2": lrpdb.Spec.ModifyOption2, + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - //* check the existence of lrpdb.Spec.SrcLRPDBName // - var allErrs field.ErrorList - pdbCounter, _ := r.checkPDBforCloninig(ctx, req, lrpdb, lrpdb.Spec.SrcLRPDBName) - if pdbCounter == 0 { - log.Info("target pdb " + lrpdb.Spec.SrcLRPDBName + " does not exists or is not open") - allErrs = append(allErrs, field.NotFound(field.NewPath("Spec").Child("LRPDBName"), " "+lrpdb.Spec.LRPDBName+" does not exist : failure")) - r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) - return nil + if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { + log.Info("MODIFY LRPDB", "lrpdb.Spec.LRPDBState=", lrpdb.Spec.LRPDBState, "lrpdb.Spec.ModifyOption=", lrpdb.Spec.ModifyOption) + log.Info("LRPDB STATUS OPENMODE", "lrpdb.Status.OpenMode=", lrpdb.Status.OpenMode) } - if lrpdb.Spec.SparseClonePath != "" { - values["sparseClonePath"] = lrpdb.Spec.SparseClonePath - } - if lrpdb.Spec.FileNameConversions != "" { - values["fileNameConversions"] = lrpdb.Spec.FileNameConversions - } - if lrpdb.Spec.TotalSize != "" { - values["totalSize"] = lrpdb.Spec.TotalSize - } - if lrpdb.Spec.TempSize != "" { - values["tempSize"] = lrpdb.Spec.TempSize - } + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdb.Spec.LRPDBName + "/" + lrpdb.Status.Msg = "close:[op. in progress]" + r.UpdateStatus(ctx, req, lrpdb) - lrpdb.Status.Phase = lrpdbPhaseClone - lrpdb.Status.Msg = "Waiting for LRPDB to be cloned" - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) - } - respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "POST") if err != nil { - log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) return err } r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) - globalsqlcode = lrpdb.Status.SqlCode + /* if sqlcode is zero then unset the openbit */ + if lrpdb.Status.SqlCode == 0 { + lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBOPN) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + } + + r.UpdateStatus(ctx, req, lrpdb) + /* Return Error if sqlcode != */ if lrpdb.Status.SqlCode != 0 { - errclone := errors.New("Cannot clone database: ora-" + strconv.Itoa(lrpdb.Status.SqlCode)) - log.Info("Cannot clone database ora-" + strconv.Itoa(lrpdb.Status.SqlCode)) - lrpdb.Status.Msg = lrpdb.Spec.SrcLRPDBName + " is open in mount cannot clone " - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) - } - return errclone + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCLE) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "close:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + return errors.New(oer) } - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Created", "LRPDB '%s' cloned successfully", lrpdb.Spec.LRPDBName) + globalsqlcode = lrpdb.Status.SqlCode + r.getLRPDBState(ctx, req, lrpdb) + + if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Modified", " '%s' modified successfully '%s'", lrpdb.Spec.LRPDBName, lrpdb.Spec.LRPDBState) + } if lrest.Spec.DBServer != "" { lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName @@ -998,1133 +858,2007 @@ func (r *LRPDBReconciler) cloneLRPDB(ctx context.Context, req ctrl.Request, lrpd parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) } - assertiveLpdbDeletion = lrpdb.Spec.AssertiveLrpdbDeletion - if lrpdb.Spec.AssertiveLrpdbDeletion == true { - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Clone", "PDB '%s' assertive pdb deletion turned on", lrpdb.Spec.LRPDBName) - } - log.Info("Cloned LRPDB successfully", "Source LRPDB Name", lrpdb.Spec.SrcLRPDBName, "Clone LRPDB Name", lrpdb.Spec.LRPDBName) - r.getLRPDBState(ctx, req, lrpdb) + lrpdb.Status.Msg = "close:[op. completed]" + log.Info("Successfully modified LRPDB state", "LRPDB Name", lrpdb.Spec.LRPDBName) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCLS) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) + return nil } /* -************************************************ - - Plug a LRPDB +********************************************************************* + - DELETE PDB - IMPERATIVE APPROAC -*********************************************** +********************************************************************* */ -func (r *LRPDBReconciler) plugLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - - log := r.Log.WithValues("plugLRPDB", req.NamespacedName) - globalsqlcode = 0 +func (r *LRPDBReconciler) DeleteLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("deleteLRPDB", req.NamespacedName) var err error - var tde_Password string - var tde_Secret string lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { + log.Info("Failure: Cannot get lrest info") return err } - values := map[string]string{ - "method": "PLUG", - "xmlFileName": lrpdb.Spec.XMLFileName, - "pdb_name": lrpdb.Spec.LRPDBName, - "sourceFileNameConversions": lrpdb.Spec.SourceFileNameConversions, - "copyAction": lrpdb.Spec.CopyAction, - "fileNameConversions": lrpdb.Spec.FileNameConversions, - "unlimitedStorage": strconv.FormatBool(*(lrpdb.Spec.UnlimitedStorage)), - "reuseTempFile": strconv.FormatBool(*(lrpdb.Spec.ReuseTempFile)), - "totalSize": lrpdb.Spec.TotalSize, - "tempSize": lrpdb.Spec.TempSize, - "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - - if *(lrpdb.Spec.LTDEImport) { - tde_Password, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDEPassword.Secret.SecretName, lrpdb.Spec.LTDEPassword.Secret.Key) - if err != nil { - return err + /* Close the pdb if it's open */ + if Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true { + valuesclose := map[string]string{ + "state": "CLOSE", + "modifyOption": "IMMEDIATE", + "getScript": "FALSE"} + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, valuesclose, "POST") + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + if lrpdb.Status.SqlCode != 0 { + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "close:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) } - tde_Secret, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDESecret.Secret.SecretName, lrpdb.Spec.LTDESecret.Secret.Key) if err != nil { - return err + log.Info("Warning error closing lrpdb continue anyway") + } + lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBOPN) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCLS) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) - tde_Secret = tde_Secret[:len(tde_Secret)-1] - tde_Password = tde_Secret[:len(tde_Password)-1] - values["tde_Password"] = tde_Password - values["tdeKeystorePath"] = lrpdb.Spec.LTDEKeystorePath - values["tde_Secret"] = tde_Secret - values["tdeImport"] = strconv.FormatBool(*(lrpdb.Spec.LTDEImport)) - } - if *(lrpdb.Spec.AsClone) { - values["asClone"] = strconv.FormatBool(*(lrpdb.Spec.AsClone)) } - url := r.BaseUrl(ctx, req, lrpdb, lrest) + values := map[string]string{ + "action": "INCLUDING", + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - lrpdb.Status.TotalSize = lrpdb.Spec.TotalSize - lrpdb.Status.Phase = lrpdbPhasePlug - lrpdb.Status.Msg = "Waiting for LRPDB to be plugged" - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + if lrpdb.Spec.DropAction != "" { + values["action"] = lrpdb.Spec.DropAction } - respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "DELETE") if err != nil { - log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) return err } r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) globalsqlcode = lrpdb.Status.SqlCode - if lrpdb.Status.SqlCode != 0 { - log.Info("Plug database failure........:" + strconv.Itoa(lrpdb.Status.SqlCode)) - err = fmt.Errorf("%v", lrpdb.Status.SqlCode) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, FNALAE) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "delete:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) return err - } - - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Created", "LRPDB '%s' plugged successfully", lrpdb.Spec.LRPDBName) - - if lrest.Spec.DBServer != "" { - lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName } else { - log.Info("Parsing connectstring") - lrpdb.Status.ConnString = lrest.Spec.DBTnsurl - parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBDIC) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) } - assertiveLpdbDeletion = lrpdb.Spec.AssertiveLrpdbDeletion - if lrpdb.Spec.AssertiveLrpdbDeletion == true { - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Plug", "PDB '%s' assertive pdb deletion turned on", lrpdb.Spec.LRPDBName) + log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdbName) + + controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) + if err := r.Update(ctx, lrpdb); err != nil { + log.Info("Cannot remove finalizer") + return err } - log.Info("Successfully plugged LRPDB", "LRPDB Name", lrpdb.Spec.LRPDBName) - r.getLRPDBState(ctx, req, lrpdb) return nil } /* -************************************************ - - Unplug a LRPDB +********************************************************************* + - DELETE PDB - DECLARATIVE APPROACH -*********************************************** +********************************************************************* */ -func (r *LRPDBReconciler) unplugLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - log := r.Log.WithValues("unplugLRPDB", req.NamespacedName) - globalsqlcode = 0 +func (r *LRPDBReconciler) DeleteLRPDBDeclarative(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("deleteLRPDBDeclaratve", req.NamespacedName) var err error - var tde_Password string - var tde_Secret string lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { + log.Info("Failure: Cannot get lrest info") return err } - values := map[string]string{ - "method": "UNPLUG", - "xmlFileName": lrpdb.Spec.XMLFileName, - "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - - if *(lrpdb.Spec.LTDEExport) { - // Get the TDE Password - tde_Password, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDEPassword.Secret.SecretName, lrpdb.Spec.LTDEPassword.Secret.Key) - if err != nil { - return err + /* Close the pdb if it's open */ + if Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true { + valuesclose := map[string]string{ + "state": "CLOSE", + "modifyOption": "IMMEDIATE", + "getScript": "FALSE"} + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, valuesclose, "POST") + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + if lrpdb.Status.SqlCode != 0 { + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "close:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) } - tde_Secret, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDESecret.Secret.SecretName, lrpdb.Spec.LTDESecret.Secret.Key) if err != nil { - return err + log.Info("Warning error closing lrpdb continue anyway") + } + lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBOPN) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCLS) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) - tde_Secret = tde_Secret[:len(tde_Secret)-1] - tde_Password = tde_Secret[:len(tde_Password)-1] - values["tde_Password"] = tde_Password - values["tdeKeystorePath"] = lrpdb.Spec.LTDEKeystorePath - values["tde_Secret"] = tde_Secret - values["tdeExport"] = strconv.FormatBool(*(lrpdb.Spec.LTDEExport)) } - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdb.Spec.LRPDBName + "/" + values := map[string]string{ + "action": "INCLUDING", + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - log.Info("CallAPI(url)", "url", url) - lrpdb.Status.Phase = lrpdbPhaseUnplug - lrpdb.Status.Msg = "Waiting for LRPDB to be unplugged" - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + if lrpdb.Spec.DropAction != "" { + values["action"] = lrpdb.Spec.DropAction } - respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "DELETE") if err != nil { - log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) return err } r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) - + globalsqlcode = lrpdb.Status.SqlCode if lrpdb.Status.SqlCode != 0 { - globalsqlcode = lrpdb.Status.SqlCode - - lrpdb.Status.Msg = lrpdb.Spec.LRPDBName + " database cannot be unplugged " - log.Info(lrpdb.Spec.LRPDBName + " database cannot be unplugged ") - if lrpdb.Status.SqlCode == 65170 { - log.Info(lrpdb.Spec.XMLFileName + " xml file already exists ") - } - - /* - err := r.Update(ctx, lrpdb) - if err != nil { - log.Info("Fail to update crd", "err", err.Error()) - return err - } - - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status"+lrpdb.Name, "err", err.Error()) - return err - } - */ - - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Unplugged", " ORA-%s ", strconv.Itoa(lrpdb.Status.SqlCode)) - err = fmt.Errorf("%v", lrpdb.Status.SqlCode) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, FNALAE) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "delete:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) return err + } else { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBDIC) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) } + log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdbName) + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { log.Info("Removing finalizer") controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) - err = r.Update(ctx, lrpdb) + err := r.Update(ctx, lrpdb) if err != nil { log.Info("Could not remove finalizer", "err", err.Error()) return err } - lrpdb.Status.Status = true - err = r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) - if err != nil { - log.Info("Could not delete LRPDB resource", "err", err.Error()) - return err - } } - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Unplugged", "LRPDB '%s' unplugged successfully", lrpdb.Spec.LRPDBName) - globalsqlcode = 0 - log.Info("Successfully unplugged LRPDB resource") + err = r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + if err != nil { + log.Info("Could not delete LRPDB resource", "err", err.Error()) + return err + } + return nil } -/************************************************** -Alter system LRPDB -**************************************************/ - -/**just push the trasnsaction **/ -func (r *LRPDBReconciler) alterSystemLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { +/* +********************************************************************* + - CHECK BEFORE CLONING - log := r.Log.WithValues("alterSystemLRPDB", req.NamespacedName) - globalsqlcode = 0 +********************************************************************* +*/ +func (r *LRPDBReconciler) checkPDBforCloninig(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, targetPdbName string) (int, error) { + log := r.Log.WithValues("checkPDBforCloninig", req.NamespacedName) + var pdbCounter int + pdbCounter = 0 - var err error - err = r.getLRPDBState(ctx, req, lrpdb) + lrpdbList := &dbapi.LRPDBList{} + listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingFields{"spec.pdbName": targetPdbName}} + err := r.List(ctx, lrpdbList, listOpts...) if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Warning LRPDB does not exist", "LRPDB Name", lrpdb.Spec.LRPDBName) - return nil - } - return err + log.Info("Failed to list lrpdbs", "Namespace", req.Namespace, "Error", err) + return 0, err } - - lrest, err := r.getLRESTResource(ctx, req, lrpdb) - if err != nil { - log.Info("Cannot find LREST server") - return err + if len(lrpdbList.Items) == 0 { + log.Info("No pdbs available") + return pdbCounter, err } - /* alter system payload */ + for _, p := range lrpdbList.Items { + fmt.Printf("DEBUGCLONE %s %s %d\n", p.Spec.LRPDBName, targetPdbName, pdbCounter) + if p.Spec.LRPDBName == targetPdbName { + log.Info("Found " + targetPdbName + " in the crd list") + if p.Status.OpenMode == "MOUNTED" { + log.Info("Cannot clone a mounted pdb") + return pdbCounter, err + } + pdbCounter++ + fmt.Printf("DEBUGCLONE %s %s %d\n", p.Spec.LRPDBName, targetPdbName, pdbCounter) + return pdbCounter, err + } - values := map[string]string{ - "state": "ALTER", - "alterSystemParameter": lrpdb.Spec.AlterSystemParameter, - "alterSystemValue": lrpdb.Spec.AlterSystemValue, - "parameterScope": lrpdb.Spec.ParameterScope, } + return pdbCounter, err +} - lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName - log.Info("alter system payload...:", "lrpdb.Spec.AlterSystemValue=", lrpdb.Spec.AlterSystemValue) - log.Info("alter system payload...:", "lrpdb.Spec.AlterSystemParameter=", lrpdb.Spec.AlterSystemParameter) - log.Info("alter system payload...:", "lrpdb.Spec.ParameterScope=", lrpdb.Spec.ParameterScope) - log.Info("alter system path.......:", "url=", url) +/* +********************************************************************* + - CLONE PDB - lrpdb.Status.Phase = lrpdbPhaseAlterPlug - lrpdb.Status.ModifyOption = lrpdb.Spec.AlterSystem + " " + lrpdb.Spec.ParameterScope - lrpdb.Status.Msg = "Waiting for LRPDB parameter to be modified" - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update lrpdb parameter :"+lrpdb.Name, "err", err.Error()) - return err +********************************************************************* +*/ +func (r *LRPDBReconciler) CloneLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("CloneLRPDB", req.NamespacedName) + if lrpdb.Spec.LRPDBName == lrpdb.Spec.SrcLRPDBName { + log.Info("Invalid Name") + return nil } - respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + globalsqlcode = 0 + var err error + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { - log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) return err } - r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) - globalsqlcode = lrpdb.Status.SqlCode - - if lrpdb.Status.SqlCode == 0 { - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Altered", "LRPDB(name,cmd,sqlcode) '%s %s %d' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.AlterSystem, lrpdb.Status.SqlCode) - lrpdb.Status.Phase = lrpdbPhaseReady - lrpdb.Spec.Action = "Noaction" - lrpdb.Status.Action = "Noaction" - lrpdb.Status.Status = true - - if err := r.Update(ctx, lrpdb); err != nil { - log.Error(err, "Cannot rest lrpdb Spec :"+lrpdb.Name, "err", err.Error()) - return err - } - - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update lrpdb parameter :"+lrpdb.Name, "err", err.Error()) - return err - } - return nil - - } - - if lrpdb.Status.SqlCode != 0 { - r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "alter system failure", "LRPDB(name,cmd,sqlcode) '%s %s %d' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.AlterSystem, lrpdb.Status.SqlCode) - erralter := errors.New("Error: cannot modify parameter") - - lrpdb.Status.ModifyOption = lrpdb.Spec.AlterSystem + " " + lrpdb.Spec.ParameterScope - lrpdb.Status.Msg = "Failed: cannot modify system parameter" - lrpdb.Status.Phase = lrpdbPhaseStatus - lrpdb.Spec.AlterSystem = "" - lrpdb.Spec.ParameterScope = "" - lrpdb.Spec.Action = "Noaction" - if err := r.Update(ctx, lrpdb); err != nil { - log.Error(err, "Cannot rest lrpdb Spec :"+lrpdb.Name, "err", err.Error()) - return err - } - - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update lrpdb parameter :"+lrpdb.Name, "err", err.Error()) - return err - } - return erralter - } - - lrpdb.Status.Status = false - - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update lrpdb parameter :"+lrpdb.Name, "err", err.Error()) - return err - } - return nil -} - -/************************************************* - * Modify a LRPDB state - ***********************************************/ -func (r *LRPDBReconciler) modifyLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - - log := r.Log.WithValues("modifyLRPDB", req.NamespacedName) - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Modify", "Info:'%s %s %s' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.LRPDBState, lrpdb.Status.ModifyOption) - - var err error + /* Prevent cloning an existing lrpdb */ err = r.getLRPDBState(ctx, req, lrpdb) if err != nil { - if lrpdb.Status.SqlCode == 1403 { - // BUG 36752465 - // We have to handle to verify a non existings results using both - log.Info("Database does not exists ") - r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) - return nil - } if apierrors.IsNotFound(err) { - log.Info("Warning LRPDB does not exist", "LRPDB Name", lrpdb.Spec.LRPDBName) - r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) - return nil + log.Info("Check LRPDB not existence completed", "LRPDB Name", lrpdb.Spec.LRPDBName) } - return err - } - /* This scenario is managed by webhook acceptance test ... leave it here anyway */ - if lrpdb.Status.OpenMode == "READ WRITE" && lrpdb.Spec.LRPDBState == "OPEN" && lrpdb.Spec.ModifyOption == "READ WRITE" { - /* Database is already open no action required */ + } else { + log.Info("Database already exists ", "LRPDB Name", lrpdb.Spec.LRPDBName) return nil } - if lrpdb.Status.OpenMode == "MOUNTED" && lrpdb.Spec.LRPDBState == "CLOSE" && lrpdb.Spec.ModifyOption == "IMMEDIATE" { - /* Database is already close no action required */ + values := map[string]string{ + "method": "CLONE", + "pdb_name": lrpdb.Spec.LRPDBName, + "srcPdbName": lrpdb.Spec.SrcLRPDBName, + "reuseTempFile": strconv.FormatBool(*(lrpdb.Spec.ReuseTempFile)), + "unlimitedStorage": strconv.FormatBool(*(lrpdb.Spec.UnlimitedStorage)), + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + + //* check the existence of lrpdb.Spec.SrcLRPDBName // + var allErrs field.ErrorList + pdbCounter, _ := r.checkPDBforCloninig(ctx, req, lrpdb, lrpdb.Spec.SrcLRPDBName) + if pdbCounter == 0 { + log.Info("target pdb " + lrpdb.Spec.SrcLRPDBName + " does not exists or is not open") + allErrs = append(allErrs, field.NotFound(field.NewPath("Spec").Child("LRPDBName"), " "+lrpdb.Spec.LRPDBName+" does not exist : failure")) + r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) return nil } - lrest, err := r.getLRESTResource(ctx, req, lrpdb) - if err != nil { - return err + if lrpdb.Spec.SparseClonePath != "" { + values["sparseClonePath"] = lrpdb.Spec.SparseClonePath } - - values := map[string]string{} - if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { - values = map[string]string{ - "state": lrpdb.Spec.LRPDBState, - "modifyOption": lrpdb.Spec.ModifyOption, - "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { - log.Info("MODIFY LRPDB", "lrpdb.Spec.LRPDBState=", lrpdb.Spec.LRPDBState, "lrpdb.Spec.ModifyOption=", lrpdb.Spec.ModifyOption) - log.Info("LRPDB STATUS OPENMODE", "lrpdb.Status.OpenMode=", lrpdb.Status.OpenMode) - } + if lrpdb.Spec.FileNameConversions != "" { + values["fileNameConversions"] = lrpdb.Spec.FileNameConversions } - - lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" - - lrpdb.Status.Phase = lrpdbPhaseModify - if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { - lrpdb.Status.ModifyOption = lrpdb.Spec.LRPDBState + "-" + lrpdb.Spec.ModifyOption + if lrpdb.Spec.TotalSize != "" { + values["totalSize"] = lrpdb.Spec.TotalSize } - - lrpdb.Status.Msg = "Waiting for LRPDB to be modified" - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + if lrpdb.Spec.TempSize != "" { + values["tempSize"] = lrpdb.Spec.TempSize } - respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "POST") + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdb.Spec.LRPDBName + "/" + + lrpdb.Status.Msg = "clone:[op. in progress]" + r.UpdateStatus(ctx, req, lrpdb) + + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "POST") if err != nil { - log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) return err } r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) globalsqlcode = lrpdb.Status.SqlCode + r.UpdateStatus(ctx, req, lrpdb) + + if lrpdb.Status.SqlCode != 0 { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCRE) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "open:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + return errors.New(oer) - if lrpdb.Spec.LRPDBState == "OPEN" || lrpdb.Spec.LRPDBState == "CLOSE" { - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Modified", " '%s' modified successfully '%s'", lrpdb.Spec.LRPDBName, lrpdb.Spec.LRPDBState) } + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Created", "LRPDB '%s' cloned successfully", lrpdb.Spec.LRPDBName) + lrpdb.Status.TotalSize = r.GetPdbSize(ctx, req, lrpdb, lrpdb.Spec.SrcLRPDBName) + if lrest.Spec.DBServer != "" { lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName } else { - lrpdb.Status.ConnString = lrest.Spec.DBTnsurl + lrpdb.Status.ConnString = strings.TrimSpace(lrest.Spec.DBTnsurl) parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) } + imperativeLpdbDeletion = lrpdb.Spec.ImperativeLrpdbDeletion + if lrpdb.Spec.ImperativeLrpdbDeletion == true { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Clone", "PDB '%s' imperative pdb deletion turned on", lrpdb.Spec.LRPDBName) + } - lrpdb.Status.Msg = "alter lrpdb completed" - lrpdb.Status.Status = false - lrpdb.Status.Phase = lrpdbPhaseReady - - log.Info("Successfully modified LRPDB state", "LRPDB Name", lrpdb.Spec.LRPDBName) + log.Info("Cloned LRPDB successfully", "Source LRPDB Name", lrpdb.Spec.SrcLRPDBName, "Clone LRPDB Name", lrpdb.Spec.LRPDBName) + r.getLRPDBState(ctx, req, lrpdb) - /* After database openining we reapply the config map if warning is present */ - if lrpdb.Spec.LRPDBState == "OPEN" { - if bit(lrpdb.Status.Bitstat, MPWARN|MPINIT) { - log.Info("re-apply config map") - r.ApplyConfigMap(ctx, req, lrpdb) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCRT) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + lrpdb.Status.Msg = "clone:[op. completed]" + r.UpdateStatus(ctx, req, lrpdb) - } - } - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + /* If we clone we don't have to re-exec sql/plsql */ + lrpdb.Spec.PLSQLBlock = "" + if err := r.Update(ctx, lrpdb); err != nil { + log.Error(err, "Failred to update lrpdb Spec :"+lrpdb.Name, "err", err.Error()) + return err } + log.Info("plsql block reset :[" + lrpdb.Spec.PLSQLBlock + "]") - //r.getLRPDBState(ctx, req, lrpdb) return nil } /* -************************************************ - - Get LRPDB State - /*********************************************** +************************************************************** + - Check for Duplicate LRPDB. Same LRPDB name on the same LREST resource. + +************************************************************** */ -func (r *LRPDBReconciler) getLRPDBState(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { +func (r *LRPDBReconciler) checkDuplicateLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - log := r.Log.WithValues("getLRPDBState", req.NamespacedName) + log := r.Log.WithValues("checkDuplicateLRPDB", req.NamespacedName) - var err error + // Name of the LREST CR that holds the LREST container + lrestResName := lrpdb.Spec.CDBResName + //lrestame := lrpdb.Spec.LRESTName - lrest, err := r.getLRESTResource(ctx, req, lrpdb) - if err != nil { - return err - } + // Name of the LRPDB resource + lrpdbResName := lrpdb.Spec.LRPDBName - lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" + lrpdbList := &dbapi.LRPDBList{} - lrpdb.Status.Msg = "Getting LRPDB state" - fmt.Print("============================\n") - fmt.Println(lrpdb.Status) - fmt.Print("============================\n") - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) - } + listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingFields{"spec.pdbName": lrpdbResName}} - respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, nil, "GET") + // List retrieves list of objects for a given namespace and list options. + err := r.List(ctx, lrpdbList, listOpts...) if err != nil { - log.Info("Begin respData") - log.Info(respData) - log.Info("End respData") - lrpdb.Status.Msg = "getLRPDBState failure : check lrpdb status" - lrpdb.Status.Status = false - log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + log.Info("Failed to list lrpdbs", "Namespace", req.Namespace, "Error", err) return err } - r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) - globalsqlcode = lrpdb.Status.SqlCode - - if lrpdb.Status.SqlCode == 1403 { - lrpdb.Status.OpenMode = "unknown" - lrpdb.Status.Msg = "check lrpdb status" - lrpdb.Status.Status = false - return errors.New("NO_DATA_FOUND") + if len(lrpdbList.Items) == 0 { + log.Info("No lrpdbs found for LRPDBName: "+lrpdbResName, "CDBResName", lrestResName) + return nil } - var objmap map[string]interface{} - if err := json.Unmarshal([]byte(respData), &objmap); err != nil { - log.Error(err, "Failed to get state of LRPDB :"+lrpdbName, "err", err.Error()) + for _, p := range lrpdbList.Items { + log.Info("Found LRPDB: " + p.Name) + if (p.Name != lrpdb.Name) && (p.Spec.CDBResName == lrestResName) { + log.Info("Duplicate LRPDB found") + lrpdb.Status.Msg = "LRPDB Resource already exists" + lrpdb.Status.Status = false + return errors.New("Duplicate LRPDB found") + } } - lrpdb.Status.OpenMode = objmap["open_mode"].(string) + return nil +} + +/* +********************************************************************* + - GET THE CUSTOM RESOURCE FOR THE LREST MENTIONED IN THE LRPDB SPEC - /* if lrpdb.Status.Phase == lrpdbPhaseCreate && sqlcode == 1403 { +********************************************************************* +*/ +func (r *LRPDBReconciler) getLRESTResource(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (dbapi.LREST, error) { - if lrpdb.Status.OpenMode == "READ WRITE" { - err := r.mapLRPDB(ctx, req, lrpdb) - if err != nil { - log.Info("Fail to Map resource getting LRPDB state") - } - } + log := r.Log.WithValues("getLRESTResource", req.NamespacedName) - if lrpdb.Status.OpenMode == "MOUNTED" { - err := r.mapLRPDB(ctx, req, lrpdb) - if err != nil { - log.Info("Fail to Map resource getting LRPDB state") - } - } - }*/ + var lrest dbapi.LREST // LREST CR corresponding to the LREST name specified in the LRPDB spec - lrpdb.Status.Msg = "check lrpdb ok" - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + // Name of the LREST CR that holds the LREST container + lrestResName := lrpdb.Spec.CDBResName + lrestNamespace := lrpdb.Spec.CDBNamespace + + log.Info("lrestResName...........:" + lrestResName) + log.Info("lrestNamespace.........:" + lrestNamespace) + + // Get LREST CR corresponding to the LREST name specified in the LRPDB spec + err := r.Get(context.Background(), client.ObjectKey{ + Namespace: lrestNamespace, + Name: lrestResName, + }, &lrest) + + if err != nil { + log.Info("Failed to get CRD for LREST", "Name", lrestResName, "Namespace", lrestNamespace, "Error", err.Error()) + lrpdb.Status.Msg = "Unable to get CRD for LREST : " + lrestResName + r.Status().Update(ctx, lrpdb) + return lrest, err } - log.Info("Successfully obtained LRPDB state", "LRPDB Name", lrpdb.Spec.LRPDBName, "State", objmap["open_mode"].(string)) - return nil + return lrest, nil } /* -************************************************ - - Map Database LRPDB to Kubernetes LRPDB CR +********************************************************************* + - GET THE LREST POD FOR THE LREST MENTIONED IN THE LRPDB SPEC -/*********************************************** +********************************************************************* */ -func (r *LRPDBReconciler) mapLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - - log := r.Log.WithValues("mapLRPDB", req.NamespacedName) +func (r *LRPDBReconciler) getLRESTPod(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (corev1.Pod, error) { - var err error + log := r.Log.WithValues("getLRESTPod", req.NamespacedName) - lrest, err := r.getLRESTResource(ctx, req, lrpdb) - if err != nil { - return err - } + var lrestPod corev1.Pod // LREST Pod container with connection to the concerned LREST - log.Info("callapi get to map lrpdb") + // Name of the LREST CR that holds the LREST container + lrestResName := lrpdb.Spec.CDBResName - lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName - log.Info("DEBUG NEW URL " + url) + // Get LREST Pod associated with the LREST Name specified in the LRPDB Spec + err := r.Get(context.Background(), client.ObjectKey{ + Namespace: req.Namespace, + Name: lrestResName + "-lrest", + }, &lrestPod) - respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, nil, "GET") if err != nil { - log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) - return err + log.Info("Failed to get Pod for LREST", "Name", lrestResName, "Namespace", req.Namespace, "Error", err.Error()) + lrpdb.Status.Msg = "Unable to get LREST Pod for LREST : " + lrestResName + return lrestPod, err } - var objmap map[string]interface{} - if err := json.Unmarshal([]byte(respData), &objmap); err != nil { - log.Error(err, "Failed json.Unmarshal :"+lrpdbName, "err", err.Error()) - } + log.Info("Found LREST Pod for LREST", "Name", lrestResName, "Pod Name", lrestPod.Name, "LREST Container hostname", lrestPod.Spec.Hostname) + return lrestPod, nil +} - //fmt.Printf("%+v\n", objmap) - totSizeInBytes := objmap["total_size"].(float64) - totSizeInGB := totSizeInBytes / 1024 / 1024 / 1024 +/* +********************************************************************* + - GET SECRET KEY FOR A SECRET NAME - lrpdb.Status.OpenMode = objmap["open_mode"].(string) - lrpdb.Status.TotalSize = fmt.Sprintf("%4.2f", totSizeInGB) + "G" - assertiveLpdbDeletion = lrpdb.Spec.AssertiveLrpdbDeletion - if lrpdb.Spec.AssertiveLrpdbDeletion == true { - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Map", "PDB '%s' assertive pdb deletion turned on", lrpdb.Spec.LRPDBName) - } +********************************************************************* +*/ +func (r *LRPDBReconciler) getSecret(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, secretName string, keyName string) (string, error) { - if lrest.Spec.DBServer != "" { - lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName - } else { - lrpdb.Status.ConnString = lrest.Spec.DBTnsurl - parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) - } - - lrpdb.Status.Phase = lrpdbPhaseReady + log := r.Log.WithValues("getSecret", req.NamespacedName) - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + secret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: secretName, Namespace: lrpdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + secretName) + lrpdb.Status.Msg = "Secret not found:" + secretName + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err } - log.Info("Successfully mapped LRPDB to Kubernetes resource", "LRPDB Name", lrpdb.Spec.LRPDBName) - lrpdb.Status.Status = true - return nil + return string(secret.Data[keyName]), nil } /* -************************************************ - - Delete a LRPDB - /*********************************************** -*/ -func (r *LRPDBReconciler) deleteLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - - log := r.Log.WithValues("deleteLRPDB", req.NamespacedName) +********************************************************************* + - CREATE PDB - errstate := r.getLRPDBState(ctx, req, lrpdb) - if errstate != nil { - if lrpdb.Status.SqlCode == 1403 { - // BUG 36752336: - log.Info("Database does not exists ") - r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) - return nil - } - if apierrors.IsNotFound(errstate) { - log.Info("Warning LRPDB does not exist", "LRPDB Name", lrpdb.Spec.LRPDBName) - r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) - return nil - } - log.Error(errstate, "Failed to update status for :"+lrpdb.Name, "err", errstate.Error()) - return errstate - //* if the pdb does not exists delete the crd *// +********************************************************************* +*/ +func (r *LRPDBReconciler) CreateLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("CreateLRPDB", req.NamespacedName) + log.Info("call getLRESTResource \n") + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err } + /* If it's not created by lrest autodiscover */ + if Bit(lrpdb.Status.PDBBitMask, PDBAUT) == false { - if lrpdb.Status.OpenMode == "READ WRITE" { - - errdel := errors.New("pdb is open cannot delete it") - log.Info("LRPDB is open in read write cannot drop ") - lrpdb.Status.Msg = "LRPDB is open in read write cannot drop " - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) - } + var err error + var tde_Password string + var tde_Secret string - return errdel - } + AutoDiscover := lrest.Spec.PdbAutoDiscover + err = r.AutoDiscoverActivation(ctx, req, lrpdb, false) - err := r.deleteLRPDBInstance(req, ctx, lrpdb) - if err != nil { - log.Info("Could not delete LRPDB", "LRPDB Name", lrpdb.Spec.LRPDBName, "err", err.Error()) - return err - } + /*** reset sqlcode***/ + lrpdb.Status.SqlCode = 0 - if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { - log.Info("Removing finalizer") - controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) - err := r.Update(ctx, lrpdb) + lrpdbAdminName, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.AdminpdbUser.Secret.SecretName, lrpdb.Spec.AdminpdbUser.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) if err != nil { - log.Info("Could not remove finalizer", "err", err.Error()) + log.Error(err, "Unable to find pdb admin user ") + _ = r.AutoDiscoverActivation(ctx, req, lrpdb, AutoDiscover) return err } - lrpdb.Status.Status = true - err = r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + + lrpdbAdminPwd, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.AdminpdbPass.Secret.SecretName, lrpdb.Spec.AdminpdbPass.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) + if err != nil { - log.Info("Could not delete LRPDB resource", "err", err.Error()) + log.Error(err, "Unable to find pdb admin password ") + _ = r.AutoDiscoverActivation(ctx, req, lrpdb, AutoDiscover) return err } - } - - r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Deleted", "LRPDB '%s' dropped successfully", lrpdb.Spec.LRPDBName) - - log.Info("Successfully deleted LRPDB resource") - return nil -} - -/* -************************************************ - - Check LRPDB deletion - /*********************************************** -*/ -func (r *LRPDBReconciler) manageLRPDBDeletion(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - log := r.Log.WithValues("manageLRPDBDeletion", req.NamespacedName) - - // Check if the LRPDB instance is marked to be deleted, which is - // indicated by the deletion timestamp being set. - isLRPDBMarkedToBeDeleted := lrpdb.GetDeletionTimestamp() != nil - if isLRPDBMarkedToBeDeleted { - log.Info("Marked to be deleted") - lrpdb.Status.Phase = lrpdbPhaseDelete - lrpdb.Status.Status = true - r.Status().Update(ctx, lrpdb) - if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { - // Remove LRPDBFinalizer. Once all finalizers have been - // removed, the object will be deleted. - log.Info("Removing finalizer") - controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) - err := r.Update(ctx, lrpdb) + values := map[string]string{ + "method": "CREATE", + "pdb_name": lrpdb.Spec.LRPDBName, + "adminName": lrpdbAdminName, + "adminPwd": lrpdbAdminPwd, + "fileNameConversions": lrpdb.Spec.FileNameConversions, + "reuseTempFile": strconv.FormatBool(*(lrpdb.Spec.ReuseTempFile)), + "unlimitedStorage": strconv.FormatBool(*(lrpdb.Spec.UnlimitedStorage)), + "totalSize": lrpdb.Spec.TotalSize, + "tempSize": lrpdb.Spec.TempSize, + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + + fmt.Printf("===== PAYLOAD ===\n") + fmt.Print(" method ", values["method"], "\n") + fmt.Print(" pdb_name ", values["pdb_name"], "\n") + fmt.Print(" adminName ", values["adminName"], "\n") + fmt.Print(" adminPwd --------------\n") + fmt.Print(" fileNameConversions ", values["fileNameConversions"], "\n") + fmt.Print(" unlimitedStorage ", values["unlimitedStorage"], "\n") + fmt.Print(" reuseTempFile ", values["reuseTempFile"], "\n") + fmt.Print(" tempSize ", values["tempSize"], "\n") + fmt.Print(" totalSize ", values["totalSize"], "\n") + fmt.Print(" getScript ", values["getScript"], "\n") + + if *(lrpdb.Spec.LTDEImport) { + tde_Password, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDEPassword.Secret.SecretName, lrpdb.Spec.LTDEPassword.Secret.Key) if err != nil { - log.Info("Could not remove finalizer", "err", err.Error()) + _ = r.AutoDiscoverActivation(ctx, req, lrpdb, AutoDiscover) return err } - log.Info("Successfully removed LRPDB resource") - return nil + tde_Secret, err = r.getSecret(ctx, req, lrpdb, lrpdb.Spec.LTDESecret.Secret.SecretName, lrpdb.Spec.LTDESecret.Secret.Key) + if err != nil { + _ = r.AutoDiscoverActivation(ctx, req, lrpdb, AutoDiscover) + return err + } + + tde_Secret = tde_Secret[:len(tde_Secret)-1] + tde_Password = tde_Secret[:len(tde_Password)-1] + values["tde_Password"] = tde_Password + values["tdeKeystorePath"] = lrpdb.Spec.LTDEKeystorePath + values["tde_Secret"] = tde_Secret } - } - // Add finalizer for this CR - if !controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { - log.Info("Adding finalizer") - controllerutil.AddFinalizer(lrpdb, LRPDBFinalizer) - err := r.Update(ctx, lrpdb) + url := r.BaseUrl(ctx, req, lrpdb, lrest) + fmt.Print("============================================================\n") + fmt.Print(url) + fmt.Print("\n============================================================\n") + lrpdb.Status.Msg = "create:[op in progress]" + + r.UpdateStatus(ctx, req, lrpdb) + + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "POST") if err != nil { - log.Info("Could not add finalizer", "err", err.Error()) + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + _ = r.AutoDiscoverActivation(ctx, req, lrpdb, AutoDiscover) return err } - lrpdb.Status.Status = false + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + if lrpdb.Status.SqlCode != 0 { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCRE) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "create:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + return errors.New(oer) + } else { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCRT) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) + } + + r.getLRPDBState(ctx, req, lrpdb) + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, + "Created", "LRPDB '%s' created successfully", lrpdb.Spec.LRPDBName) + + log.Info("Parsing connectstring") + lrpdb.Status.ConnString = strings.TrimSpace(lrest.Spec.DBTnsurl) + parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) + r.UpdateStatus(ctx, req, lrpdb) + + imperativeLpdbDeletion = lrpdb.Spec.ImperativeLrpdbDeletion + if lrpdb.Spec.ImperativeLrpdbDeletion == true { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Created", "PDB '%s' imperative pdb deletion turned on", lrpdb.Spec.LRPDBName) + } + + _ = r.AutoDiscoverActivation(ctx, req, lrpdb, AutoDiscover) + + lrpdb.Status.Msg = "create:[op completed]" + r.UpdateStatus(ctx, req, lrpdb) + } else { + log.Info("CRD created by autodiscover") + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCRT) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + lrpdb.Status.ConnString = strings.TrimSpace(lrest.Spec.DBTnsurl) + parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) + lrpdb.Status.Msg = "autodiscover:[op completed]" + r.UpdateStatus(ctx, req, lrpdb) } return nil } -/* -************************************************ - - Finalization logic for LRPDBFinalizer +/************************************************** +ALTER SYSTEM lRPDB +**************************************************/ -*********************************************** -*/ -func (r *LRPDBReconciler) deleteLRPDBInstance(req ctrl.Request, ctx context.Context, lrpdb *dbapi.LRPDB) error { +/**just push the trasnsaction **/ +func (r *LRPDBReconciler) alterSystemLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - log := r.Log.WithValues("deleteLRPDBInstance", req.NamespacedName) + log := r.Log.WithValues("alterSystemLRPDB", req.NamespacedName) + globalsqlcode = 0 var err error + err = r.getLRPDBState(ctx, req, lrpdb) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Warning LRPDB does not exist", "LRPDB Name", lrpdb.Spec.LRPDBName) + return nil + } + return err + } lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { + log.Info("Cannot find LREST server") return err } - values := map[string]string{ - "action": "KEEP", - "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + /* alter system payload */ - if lrpdb.Spec.DropAction != "" { - values["action"] = lrpdb.Spec.DropAction + values := map[string]string{ + "state": "ALTER", + "alterSystemParameter": lrpdb.Spec.AlterSystemParameter, + "alterSystemValue": lrpdb.Spec.AlterSystemValue, + "parameterScope": lrpdb.Spec.ParameterScope, } lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/" + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + log.Info("alter system payload...:", "lrpdb.Spec.AlterSystemValue=", lrpdb.Spec.AlterSystemValue) + log.Info("alter system payload...:", "lrpdb.Spec.AlterSystemParameter=", lrpdb.Spec.AlterSystemParameter) + log.Info("alter system payload...:", "lrpdb.Spec.ParameterScope=", lrpdb.Spec.ParameterScope) + log.Info("alter system path.......:", "url=", url) - lrpdb.Status.Phase = lrpdbPhaseDelete - lrpdb.Status.Msg = "Waiting for LRPDB to be deleted" - if err := r.Status().Update(ctx, lrpdb); err != nil { - log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) - } + lrpdb.Status.ModifyOption = lrpdb.Spec.AlterSystem + " " + lrpdb.Spec.ParameterScope + lrpdb.Status.Msg = "alter system:[op. in progress]" + r.UpdateStatus(ctx, req, lrpdb) - respData, err := NewCallLAPI(r, ctx, req, lrpdb, url, values, "DELETE") + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "POST") if err != nil { - log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) return err } r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) globalsqlcode = lrpdb.Status.SqlCode - log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdbName) - return nil -} + if lrpdb.Status.SqlCode == 0 { -/* -*********************************************************** - - SetupWithManager sets up the controller with the Manager + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Altered", "LRPDB(name,cmd,sqlcode) '%s %s %d' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.AlterSystem, lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "alter system:[op. completed]" + r.UpdateStatus(ctx, req, lrpdb) -************************************************************ -*/ -func (r *LRPDBReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&dbapi.LRPDB{}). - WithEventFilter(predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - // Ignore updates to CR status in which case metadata.Generation does not change - return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() - }, - DeleteFunc: func(e event.DeleteEvent) bool { - // Evaluates to false if the object has been confirmed deleted. - //return !e.DeleteStateUnknown - return false - }, - }). - WithOptions(controller.Options{MaxConcurrentReconciles: 100}). - Complete(r) -} + /* Reset parameters */ + lrpdb.Spec.AlterSystemValue = "" + lrpdb.Spec.AlterSystemParameter = "" + lrpdb.Spec.ParameterScope = "" -/************************************************************* -Enh 35357707 - PROVIDE THE LRPDB TNSALIAS INFORMATION -**************************************************************/ + if err := r.Update(ctx, lrpdb); err != nil { + log.Error(err, "Cannot rest lrpdb Spec :"+lrpdb.Name, "err", err.Error()) + return err + } -func parseTnsAlias(tns *string, lrpdbsrv *string) { - fmt.Printf("Analyzing string [%s]\n", *tns) - fmt.Printf("Relacing srv [%s]\n", *lrpdbsrv) - var swaptns string + return nil - if strings.Contains(strings.ToUpper(*tns), "SERVICE_NAME") == false { - fmt.Print("Cannot generate tns alias for lrpdb") - return } - if strings.Contains(strings.ToUpper(*tns), "ORACLE_SID") == true { - fmt.Print("Cannot generate tns alias for lrpdb") - return - } + if lrpdb.Status.SqlCode != 0 { + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "alter system failure", "LRPDB(name,cmd,sqlcode) '%s %s %d' ", lrpdb.Spec.LRPDBName, lrpdb.Spec.AlterSystem, lrpdb.Status.SqlCode) + erralter := errors.New("Error: cannot modify parameter") - swaptns = fmt.Sprintf("SERVICE_NAME=%s", *lrpdbsrv) - tnsreg := regexp.MustCompile(`SERVICE_NAME=\w+`) - *tns = tnsreg.ReplaceAllString(*tns, swaptns) + lrpdb.Status.Msg = "alter system:[op. failure]" + r.UpdateStatus(ctx, req, lrpdb) - fmt.Printf("Newstring [%s]\n", *tns) + lrpdb.Spec.AlterSystem = "" + lrpdb.Spec.ParameterScope = "" + lrpdb.Spec.ParameterScope = "" -} + if err := r.Update(ctx, lrpdb); err != nil { + log.Error(err, "Cannot rest lrpdb Spec :"+lrpdb.Name, "err", err.Error()) + return err + } -// Compose url -func (r *LRPDBReconciler) BaseUrl(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, lrest dbapi.LREST) string { - log := r.Log.WithValues("BaseUrl", req.NamespacedName) - baseurl := "https://" + lrpdb.Spec.CDBResName + "-lrest." + lrpdb.Spec.CDBNamespace + ":" + strconv.Itoa(lrest.Spec.LRESTPort) + "/database/pdbs/" - log.Info("Baseurl:" + baseurl) - return baseurl + return erralter + } + + return nil } -func (r *LRPDBReconciler) DecryptWithPrivKey(Key string, Buffer string, req ctrl.Request) (string, error) { - log := r.Log.WithValues("DecryptWithPrivKey", req.NamespacedName) - Debug := 0 - block, _ := pem.Decode([]byte(Key)) - pkcs8PrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - log.Error(err, "Failed to parse private key - "+err.Error()) - return "", err - } - if Debug == 1 { - fmt.Printf("======================================\n") - fmt.Printf("%s\n", Key) - fmt.Printf("======================================\n") - } +func (r *LRPDBReconciler) execPLSQL(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("execPLSQL", req.NamespacedName) - encString64, err := base64.StdEncoding.DecodeString(string(Buffer)) + // TO BE DONE Ad control for the pdb existence + lrpdbName := lrpdb.Spec.LRPDBName + + log.Info("Reading code config Map ") + configmap, err := r.GetConfigMapCode(ctx, req, lrpdb) if err != nil { - log.Error(err, "Failed to decode encrypted string to base64 - "+err.Error()) - return "", err + log.Error(err, "Fail to fetch code configmap", "err", err.Error()) + return err } - decryptedB, err := rsa.DecryptPKCS1v15(nil, pkcs8PrivateKey.(*rsa.PrivateKey), encString64) + lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { - log.Error(err, "Failed to decrypt string - "+err.Error()) - return "", err + return err } - if Debug == 1 { - fmt.Printf("[%s]\n", string(decryptedB)) + + lrpdb.Status.Msg = "plsql/sql apply[op. in progress]" + r.UpdateStatus(ctx, req, lrpdb) + + var tokens []string + var CodeSize int + /** Sort keys **/ + keys := reflect.ValueOf(configmap.Data).MapKeys() + keysOrder := func(i, j int) bool { return keys[i].Interface().(string) < keys[j].Interface().(string) } + sort.Slice(keys, keysOrder) + /** End of sort section **/ + + for _, key := range keys { + Value := configmap.Data[key.Interface().(string)] + fmt.Printf("Code Block Name (SQL/PLSQL):%s\n", key) + tokens = strings.Split(Value, "\n") + /* Debug Section */ + for cnt := range tokens { + fmt.Printf("line[%d]:%s\n", cnt, tokens[cnt]) + CodeSize += len(tokens[cnt]) + } + + //* removing laste null emlements + if len(tokens) > 0 { + tokens = tokens[:len(tokens)-1] + } + + fmt.Printf("call to restsertver (%s,%d)\n", key, CodeSize) + + jsonpayload := &PLSQLPayLoad{Values: map[string]string{"method": "APPLYSQL"}, Sqltokens: tokens} + //* Debug section **// + + encjson, _ := json.Marshal(jsonpayload) + fmt.Println(string(encjson)) + + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, jsonpayload, "POST") + if err != nil { + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + return err + } + if err != nil { + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + + EvLevel := corev1.EventTypeNormal + skey := fmt.Sprintf("[%s]", key) + if lrpdb.Status.SqlCode != 0 { + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = skey + ":[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + EvLevel = corev1.EventTypeWarning + } + /* + Add the timestamp to the event + */ + t := time.Now() + formatted := fmt.Sprintf("APPLYSQL-%02d%02d%02d", t.Hour(), t.Minute(), t.Second()) + r.Recorder.Eventf(lrpdb, EvLevel, formatted, " CODE:SQLCODE '%s':'%d'", skey, lrpdb.Status.SqlCode) + + /* sql execution complete successfully than report the name of the tag */ + if lrpdb.Status.SqlCode == 0 && err == nil { + lrpdb.Status.LastPLSQL = skey + r.UpdateStatus(ctx, req, lrpdb) + /* reset code buffer */ + } + tokens = nil + CodeSize = 0 } - return strings.TrimSpace(string(decryptedB)), err + lrpdb.Spec.PLSQLBlock = "" /* rest block */ + if err := r.Update(ctx, lrpdb); err != nil { + log.Error(err, "Failred to update lrpdb Spec :"+lrpdb.Name, "err", err.Error()) + return err + } + lrpdb.Status.Msg = "plsql/sql apply[op. completed]" + r.UpdateStatus(ctx, req, lrpdb) + log.Info("plsql block reset :[" + lrpdb.Spec.PLSQLBlock + "]") + return nil } -// New function to decrypt credential using private key -func (r *LRPDBReconciler) getEncriptedSecret(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, secretName string, keyName string, secretNamePk string, keyNamePk string) (string, error) { +/* +************************************************ + - Get LRPDB State - log := r.Log.WithValues("getEncriptedSecret", req.NamespacedName) +************************************************ +*/ +func (r *LRPDBReconciler) getLRPDBState(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - log.Info("getEncriptedSecret :" + secretName) - secret1 := &corev1.Secret{} - err := r.Get(ctx, types.NamespacedName{Name: secretName, Namespace: lrpdb.Namespace}, secret1) + log := r.Log.WithValues("getLRPDBState", req.NamespacedName) + + var err error + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + secretName) - lrpdb.Status.Msg = "Secret not found:" + secretName - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err + return err } - secret2 := &corev1.Secret{} - err = r.Get(ctx, types.NamespacedName{Name: secretNamePk, Namespace: lrpdb.Namespace}, secret2) + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" + + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, nil, "GET") if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + secretNamePk) - lrpdb.Status.Msg = "Secret not found:" + secretNamePk - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err + lrpdb.Status.Msg = "getLRPDBState failure : check lrpdb status" + log.Error(err, "Failure NewCallLAPI( "+url+")", "err", err.Error()) + r.UpdateStatus(ctx, req, lrpdb) + return err } - Encval := string(secret1.Data[keyName]) - Encval = strings.TrimSpace(Encval) + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + if lrpdb.Status.SqlCode == 1403 { + lrpdb.Status.OpenMode = "N/A" + lrpdb.Status.Msg = "N/A ORA-1403" + return errors.New("NO_DATA_FOUND") + } - privKey := string(secret2.Data[keyNamePk]) - privKey = strings.TrimSpace(privKey) + r.GetOpenMode(respData, &(lrpdb.Status.OpenMode)) + r.GetRestricted(respData, &(lrpdb.Status.Restricted)) + r.GetPdbSize2(respData, &(lrpdb.Status.TotalSize)) - /* Debuug info for dev phase - fmt.Printf("DEBUG Secretename:secretName :%s\n", secretName) - fmt.Printf("DEBUG privKey :%s\n", privKey) - fmt.Printf("DEBUG Encval :%s\n", Encval) + r.UpdateStatus(ctx, req, lrpdb) + + /* lrpdb.Status.Msg = "check lrpdb ok" + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } */ - DecVal, err := r.DecryptWithPrivKey(privKey, Encval, req) + log.Info("Successfully obtained LRPDB state", "LRPDB Name", lrpdb.Spec.LRPDBName, "State", lrpdb.Status.OpenMode) + return nil +} + +/* +************************************************ + - Map Database LRPDB to Kubernetes LRPDB CR + +/*********************************************** +*/ +func (r *LRPDBReconciler) mapLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("mapLRPDB", req.NamespacedName) + + var err error + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) if err != nil { - log.Error(err, "Fail to decrypt secret:"+secretName) - lrpdb.Status.Msg = " Fail to decrypt secret:" + secretName - return "", err + return err } - return DecVal, nil -} -func (r *LRPDBReconciler) manageLRPDBDeletion2(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - log := r.Log.WithValues("manageLRPDBDeletion", req.NamespacedName) - if lrpdb.ObjectMeta.DeletionTimestamp.IsZero() { - if !controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { - controllerutil.AddFinalizer(lrpdb, LRPDBFinalizer) - if err := r.Update(ctx, lrpdb); err != nil { - return err - } - } - } else { - log.Info("Pdb marked to be delted") - if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { - if assertiveLpdbDeletion == true { - log.Info("Deleting lrpdb CRD: Assertive approach is turned on ") - lrest, err := r.getLRESTResource(ctx, req, lrpdb) - if err != nil { - log.Error(err, "Cannont find cdb resource ", "err", err.Error()) - return err - } + log.Info("callapi get to map lrpdb") - lrpdbName := lrpdb.Spec.LRPDBName - if lrpdb.Status.OpenMode == "READ WRITE" { - valuesclose := map[string]string{ - "state": "CLOSE", - "modifyOption": "IMMEDIATE", - "getScript": "FALSE"} - lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" - _, errclose := r.callAPI(ctx, req, lrpdb, url, valuesclose, "POST") - if errclose != nil { - log.Info("Warning error closing lrpdb continue anyway") - } - } + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" + log.Info("DEBUG NEW URL " + url) - valuesdrop := map[string]string{ - "action": "INCLUDING", - "getScript": "FALSE"} - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/" + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, nil, "GET") + if err != nil { + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + return err + } - log.Info("Call Delete()") - _, errdelete := r.callAPI(ctx, req, lrpdb, url, valuesdrop, "DELETE") - if errdelete != nil { - log.Error(errdelete, "Fail to delete lrpdb :"+lrpdb.Name, "err", err.Error()) - return errdelete - } - } /* END OF ASSERTIVE SECTION */ + var objmap map[string]interface{} + if err := json.Unmarshal([]byte(respData), &objmap); err != nil { + log.Error(err, "Failed json.Unmarshal :"+lrpdbName, "err", err.Error()) + } - log.Info("Marked to be deleted") - lrpdb.Status.Phase = lrpdbPhaseDelete - lrpdb.Status.Status = true - r.Status().Update(ctx, lrpdb) + //fmt.Printf("%+v\n", objmap) + totSizeInBytes := objmap["total_size"].(float64) + totSizeInGB := totSizeInBytes / 1024 / 1024 / 1024 - controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) - if err := r.Update(ctx, lrpdb); err != nil { - log.Info("Cannot remove finalizer") - return err - } + lrpdb.Status.OpenMode = objmap["open_mode"].(string) + lrpdb.Status.TotalSize = fmt.Sprintf("%4.2f", totSizeInGB) + "G" + imperativeLpdbDeletion = lrpdb.Spec.ImperativeLrpdbDeletion + if lrpdb.Spec.ImperativeLrpdbDeletion == true { + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Map", "PDB '%s' imperative pdb deletion turned on", lrpdb.Spec.LRPDBName) + } - } + if lrest.Spec.DBServer != "" { + lrpdb.Status.ConnString = lrest.Spec.DBServer + ":" + strconv.Itoa(lrest.Spec.DBPort) + "/" + lrpdb.Spec.LRPDBName + } else { + lrpdb.Status.ConnString = lrest.Spec.DBTnsurl + parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) + } - return nil + lrpdb.Status.Status = true + + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) } + log.Info("Successfully mapped LRPDB to Kubernetes resource", "LRPDB Name", lrpdb.Spec.LRPDBName) return nil } -func (r *LRPDBReconciler) InitConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) *corev1.ConfigMap { - log := r.Log.WithValues("InitConfigMap", req.NamespacedName) - log.Info("ConfigMap..............:" + "ConfigMap" + lrpdb.Name) - log.Info("ConfigMap nmsp.........:" + lrpdb.Namespace) - /* - * PDB SYSTEM PARAMETER - * record [name,value=[paramete_val|reset],level=[session|system]] - */ - - if lrpdb.Spec.PDBConfigMap == "" { - /* if users does not specify a config map - we generate an empty new one for possible - future pdb parameter modification */ +/* +************************************************ + - Delete a LRPDB + /*********************************************** +*/ +func (r *LRPDBReconciler) deleteLRPDB2(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - var SystemParameters map[string]string + log := r.Log.WithValues("deleteLRPDB2", req.NamespacedName) - log.Info("Generating an empty configmap") - globalconfigmap = "configmap-" + lrpdb.Spec.LRPDBName + "-default" - DbParameters := &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "configmap", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: globalconfigmap, - Namespace: lrpdb.Namespace, - }, - Data: SystemParameters, + errstate := r.getLRPDBState(ctx, req, lrpdb) + if errstate != nil { + if lrpdb.Status.SqlCode == 1403 { + // BUG 36752336: + log.Info("Database does not exists ") + r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + return nil } - - if err := ctrl.SetControllerReference(lrpdb, DbParameters, r.Scheme); err != nil { - log.Error(err, "Fail to set SetControllerReference", "err", err.Error()) + if apierrors.IsNotFound(errstate) { + log.Info("Warning LRPDB does not exist", "LRPDB Name", lrpdb.Spec.LRPDBName) + r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) return nil } + log.Error(errstate, "Failed to update status for :"+lrpdb.Name, "err", errstate.Error()) + return errstate + //* if the pdb does not exists delete the crd *// - /* Update Spec.PDBConfigMap */ + } + + if lrpdb.Status.OpenMode == "READ WRITE" { + + errdel := errors.New("pdb is open cannot delete it") + log.Info("LRPDB is open in read write cannot drop ") + lrpdb.Status.Msg = "LRPDB is open in read write cannot drop " + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + return errdel + } + + err := r.deleteLRPDBInstance(req, ctx, lrpdb) + if err != nil { + log.Info("Could not delete LRPDB", "LRPDB Name", lrpdb.Spec.LRPDBName, "err", err.Error()) + return err + } + + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + log.Info("Removing finalizer") + controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) + err := r.Update(ctx, lrpdb) + if err != nil { + log.Info("Could not remove finalizer", "err", err.Error()) + return err + } + lrpdb.Status.Status = true + err = r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) + if err != nil { + log.Info("Could not delete LRPDB resource", "err", err.Error()) + return err + } + } + + r.Recorder.Eventf(lrpdb, corev1.EventTypeNormal, "Deleted", "LRPDB '%s' dropped successfully", lrpdb.Spec.LRPDBName) + + log.Info("Successfully deleted LRPDB resource") + return nil +} + +/* +************************************************ + - Check LRPDB deletion + /*********************************************** +*/ +func (r *LRPDBReconciler) manageLRPDBDeletion(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("manageLRPDBDeletion", req.NamespacedName) + + // Check if the LRPDB instance is marked to be deleted, which is + // indicated by the deletion timestamp being set. + isLRPDBMarkedToBeDeleted := lrpdb.GetDeletionTimestamp() != nil + if isLRPDBMarkedToBeDeleted { + log.Info("Marked to be deleted") + lrpdb.Status.Status = true + r.Status().Update(ctx, lrpdb) + + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + // Remove LRPDBFinalizer. Once all finalizers have been + // removed, the object will be deleted. + log.Info("Removing finalizer") + controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) + err := r.Update(ctx, lrpdb) + if err != nil { + log.Info("Could not remove finalizer", "err", err.Error()) + return err + } + log.Info("Successfully removed LRPDB resource") + return nil + } + } + + // Add finalizer for this CR + if !controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + log.Info("Adding finalizer") + controllerutil.AddFinalizer(lrpdb, LRPDBFinalizer) + err := r.Update(ctx, lrpdb) + if err != nil { + log.Info("Could not add finalizer", "err", err.Error()) + return err + } + lrpdb.Status.Status = false + } + return nil +} + +/* +************************************************ + - Finalization logic for LRPDBFinalizer + +*********************************************** +*/ +func (r *LRPDBReconciler) deleteLRPDBInstance(req ctrl.Request, ctx context.Context, lrpdb *dbapi.LRPDB) error { + + log := r.Log.WithValues("deleteLRPDBInstance", req.NamespacedName) + + var err error + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + return err + } + + values := map[string]string{ + "action": "KEEP", + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + + if lrpdb.Spec.DropAction != "" { + values["action"] = lrpdb.Spec.DropAction + } + + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + + lrpdb.Status.Msg = "Waiting for LRPDB to be deleted" + if err := r.Status().Update(ctx, lrpdb); err != nil { + log.Error(err, "Failed to update status for :"+lrpdb.Name, "err", err.Error()) + } + + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "DELETE") + if err != nil { + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + return err + } + + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + + log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdbName) + return nil +} + +/* +*********************************************************** + - SetupWithManager sets up the controller with the Manager + +************************************************************ +*/ +func (r *LRPDBReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbapi.LRPDB{}). + WithEventFilter(predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + // Ignore updates to CR status in which case metadata.Generation does not change + return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() + }, + DeleteFunc: func(e event.DeleteEvent) bool { + // Evaluates to false if the object has been confirmed deleted. + //return !e.DeleteStateUnknown + return false + }, + }). + WithOptions(controller.Options{MaxConcurrentReconciles: 100}). + Complete(r) +} + +/************************************************************* +Enh 35357707 - PROVIDE THE LRPDB TNSALIAS INFORMATION +**************************************************************/ + +func parseTnsAlias(tns *string, lrpdbsrv *string) { + fmt.Printf("Analyzing string [%s]\n", *tns) + fmt.Printf("Relacing srv [%s]\n", *lrpdbsrv) + var swaptns string + + if strings.Contains(strings.ToUpper(*tns), "SERVICE_NAME") == false { + fmt.Print("Cannot generate tns alias for lrpdb") + return + } + + if strings.Contains(strings.ToUpper(*tns), "ORACLE_SID") == true { + fmt.Print("Cannot generate tns alias for lrpdb") + return + } + + *tns = strings.ReplaceAll(*tns, " ", "") + + swaptns = fmt.Sprintf("SERVICE_NAME=%s", *lrpdbsrv) + tnsreg := regexp.MustCompile(`SERVICE_NAME=\w+`) + *tns = tnsreg.ReplaceAllString(*tns, swaptns) + + fmt.Printf("Newstring [%s]\n", *tns) + +} + +// Compose url +func (r *LRPDBReconciler) BaseUrl(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, lrest dbapi.LREST) string { + log := r.Log.WithValues("BaseUrl", req.NamespacedName) + baseurl := "https://" + lrpdb.Spec.CDBResName + "-lrest." + lrpdb.Spec.CDBNamespace + ":" + strconv.Itoa(lrest.Spec.LRESTPort) + "/database/pdbs/" + log.Info("Baseurl:" + baseurl) + return baseurl +} + +func (r *LRPDBReconciler) DecryptWithPrivKey(Key string, Buffer string, req ctrl.Request) (string, error) { + log := r.Log.WithValues("DecryptWithPrivKey", req.NamespacedName) + Debug := 0 + block, _ := pem.Decode([]byte(Key)) + pkcs8PrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + log.Error(err, "Failed to parse private key - "+err.Error()) + return "", err + } + if Debug == 1 { + fmt.Printf("======================================\n") + fmt.Printf("%s\n", Key) + fmt.Printf("======================================\n") + } + + encString64, err := base64.StdEncoding.DecodeString(string(Buffer)) + if err != nil { + log.Error(err, "Failed to decode encrypted string to base64 - "+err.Error()) + return "", err + } + + decryptedB, err := rsa.DecryptPKCS1v15(nil, pkcs8PrivateKey.(*rsa.PrivateKey), encString64) + if err != nil { + log.Error(err, "Failed to decrypt string - "+err.Error()) + return "", err + } + if Debug == 1 { + fmt.Printf("[%s]\n", string(decryptedB)) + } + return strings.TrimSpace(string(decryptedB)), err + +} + +// New function to decrypt credential using private key +func (r *LRPDBReconciler) getEncriptedSecret(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, secretName string, keyName string, secretNamePk string, keyNamePk string) (string, error) { + + log := r.Log.WithValues("getEncriptedSecret", req.NamespacedName) + + log.Info("getEncriptedSecret :" + secretName) + secret1 := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: secretName, Namespace: lrpdb.Namespace}, secret1) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + secretName) + lrpdb.Status.Msg = "Secret not found:" + secretName + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + secret2 := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: secretNamePk, Namespace: lrpdb.Namespace}, secret2) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + secretNamePk) + lrpdb.Status.Msg = "Secret not found:" + secretNamePk + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + Encval := string(secret1.Data[keyName]) + Encval = strings.TrimSpace(Encval) + + privKey := string(secret2.Data[keyNamePk]) + privKey = strings.TrimSpace(privKey) + + /* Debuug info for dev phase + fmt.Printf("DEBUG Secretename:secretName :%s\n", secretName) + fmt.Printf("DEBUG privKey :%s\n", privKey) + fmt.Printf("DEBUG Encval :%s\n", Encval) + */ + + DecVal, err := r.DecryptWithPrivKey(privKey, Encval, req) + if err != nil { + log.Error(err, "Fail to decrypt secret:"+secretName) + lrpdb.Status.Msg = " Fail to decrypt secret:" + secretName + return "", err + } + return DecVal, nil +} + +func (r *LRPDBReconciler) manageLRPDBDeletion2(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("manageLRPDBDeletion", req.NamespacedName) + if lrpdb.ObjectMeta.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + controllerutil.AddFinalizer(lrpdb, LRPDBFinalizer) + if err := r.Update(ctx, lrpdb); err != nil { + return err + } + } + } else { + log.Info("Pdb marked to be delted") + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + if imperativeLpdbDeletion == true { + log.Info("Deleting lrpdb CRD: Imperative approach is turned on ") + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + log.Error(err, "Cannont find cdb resource ", "err", err.Error()) + return err + } + + lrpdbName := lrpdb.Spec.LRPDBName + if lrpdb.Status.OpenMode == "READ WRITE" { + valuesclose := map[string]string{ + "state": "CLOSE", + "modifyOption": "IMMEDIATE", + "getScript": "FALSE"} + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" + _, errclose := r.callAPI(ctx, req, lrpdb, url, valuesclose, "POST") + if errclose != nil { + log.Info("Warning error closing lrpdb continue anyway") + } + } + + valuesdrop := map[string]string{ + "action": "INCLUDING", + "getScript": "FALSE"} + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + + log.Info("Call Delete()") + _, errdelete := r.callAPI(ctx, req, lrpdb, url, valuesdrop, "DELETE") + if errdelete != nil { + log.Error(errdelete, "Fail to delete lrpdb :"+lrpdb.Name, "err", err.Error()) + return errdelete + } + } /* END OF ASSERTIVE SECTION */ + + log.Info("Marked to be deleted") + lrpdb.Status.Status = true + r.Status().Update(ctx, lrpdb) + + controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) + if err := r.Update(ctx, lrpdb); err != nil { + log.Info("Cannot remove finalizer") + return err + } + + } + + return nil + } + + return nil +} + +func (r *LRPDBReconciler) InitConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) *corev1.ConfigMap { + log := r.Log.WithValues("InitConfigMap", req.NamespacedName) + log.Info("ConfigMap..............:" + "ConfigMap" + lrpdb.Name) + log.Info("ConfigMap nmsp.........:" + lrpdb.Namespace) + /* + * PDB SYSTEM PARAMETER + * record [name,value=[paramete_val|reset],level=[session|system]] + */ + + if lrpdb.Spec.PDBConfigMap == "" { + /* if users does not specify a config map + we generate an empty new one for possible + future pdb parameter modification */ + + var SystemParameters map[string]string + + log.Info("Generating an empty configmap") + globalconfigmap = "configmap-" + lrpdb.Spec.LRPDBName + "-default" + DbParameters := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "configmap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: globalconfigmap, + Namespace: lrpdb.Namespace, + }, + Data: SystemParameters, + } + + if err := ctrl.SetControllerReference(lrpdb, DbParameters, r.Scheme); err != nil { + log.Error(err, "Fail to set SetControllerReference", "err", err.Error()) + return nil + } + + /* Update Spec.PDBConfigMap */ lrpdb.Spec.PDBConfigMap = "configmap" + lrpdb.Spec.LRPDBName + "default" if err := r.Update(ctx, lrpdb); err != nil { log.Error(err, "Failure updating Spec.PDBConfigMap ", "err", err.Error()) return nil } - lrpdb.Status.Bitstat = bis(lrpdb.Status.Bitstat, MPEMPT) - return DbParameters + lrpdb.Status.CmBitstat = bis(lrpdb.Status.CmBitstat, MPEMPT) + lrpdb.Status.CmBitStatStr = CMBitmaskprint(lrpdb.Status.CmBitstat) + r.UpdateStatus(ctx, req, lrpdb) + return DbParameters + + } else { + + lrpdb.Status.CmBitstat = bis(lrpdb.Status.CmBitstat, MPINIT) + lrpdb.Status.CmBitStatStr = CMBitmaskprint(lrpdb.Status.CmBitstat) + globalconfigmap = lrpdb.Spec.PDBConfigMap + DbParameters, err := r.GetConfigMap(ctx, req, lrpdb) + if err != nil { + log.Error(err, "Fail to fetch configmap ", "err", err.Error()) + return nil + } + + //ParseConfigMapData(DbParameters) + + r.UpdateStatus(ctx, req, lrpdb) + return DbParameters + } + + // return nil +} + +func (r *LRPDBReconciler) GetConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (*corev1.ConfigMap, error) { + log := r.Log.WithValues("GetConfigMap", req.NamespacedName) + log.Info("ConfigMapGlobal.............:" + globalconfigmap) + DbParameters, err := k8s.FetchConfigMap(r.Client, lrpdb.Namespace, globalconfigmap) + if err != nil { + log.Error(err, "Fail to fetch configmap", "err", err.Error()) + return nil, err + } + + return DbParameters, nil +} + +func (r *LRPDBReconciler) GetConfigMapCode(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (*corev1.ConfigMap, error) { + log := r.Log.WithValues("GetConfigMapCode", req.NamespacedName) + log.Info("CodeMapGlobal.............:" + lrpdb.Spec.PLSQLBlock) + CodeBlock, err := k8s.FetchConfigMap(r.Client, lrpdb.Namespace, lrpdb.Spec.PLSQLBlock) + if err != nil { + log.Error(err, "Fail to fetch configmap", "err", err.Error()) + return nil, err + } + + return CodeBlock, nil +} + +func (r *LRPDBReconciler) ApplyConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (int32, error) { + log := r.Log.WithValues("ApplyConfigMap", req.NamespacedName) + /* We read the config map and apply the setting to the pdb */ + + log.Info("Starting Apply Config Map Process") + configmap, err := r.GetConfigMap(ctx, req, lrpdb) + if err != nil { + log.Info("Cannot get config map in the open yaml file") + return 0, nil + } + Cardinality := int32(len(configmap.Data)) + if Cardinality == 0 { + log.Info("Empty config map... nothing to do ") + return 0, nil + } + log.Info("GetConfigMap completed") + + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + log.Info("Cannot find lrest server") + return 0, nil + } + tokens := ParseConfigMapData(configmap) + for cnt := range tokens { + if len(tokens[cnt]) != 0 { + /* avoid null token and check malformed value */ + fmt.Printf("token=[%s]\n", tokens[cnt]) + Parameter := strings.Split(tokens[cnt], " ") + if len(Parameter) != 3 { + log.Info("WARNING malformed value in the configmap") + } else { + fmt.Printf("alter system set %s=%s scope=%s instances=all\n", Parameter[0], Parameter[1], Parameter[2]) + /* Preparing PayLoad + ----------------- + WARNING: event setting is not yet supported. It will be implemented in future release + */ + AlterSystemPayload := map[string]string{ + "state": "ALTER", + "alterSystemParameter": Parameter[0], + "alterSystemValue": Parameter[1], + "parameterScope": Parameter[2], + } + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdb.Spec.LRPDBName + respData, err := r.callAPI(ctx, req, lrpdb, url, AlterSystemPayload, "POST") + if err != nil { + log.Error(err, "callAPI failure durring Apply Config Map", "err", err.Error()) + return 0, err + } + /* check sql code execution */ + var retJson map[string]interface{} + if err := json.Unmarshal([]byte(respData), &retJson); err != nil { + log.Error(err, "failed to get Data from callAPI", "err", err.Error()) + return 0, err + } + /* We do not the execution if something goes wrong for a single parameter + just report the error in the event queue */ + SqlCode := strconv.Itoa(int(retJson["sqlcode"].(float64))) + AlterMsg := fmt.Sprintf("pdb=%s:%s:%s:%s:%s", lrpdb.Spec.LRPDBName, Parameter[0], Parameter[1], Parameter[2], SqlCode) + log.Info("Config Map Apply:......." + AlterMsg) + + if SqlCode != "0" { + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "lrpdbinfo", AlterMsg) + lrpdb.Status.CmBitstat = bis(lrpdb.Status.CmBitstat, MPWARN) + } + + } + } + + } + + lrpdb.Status.CmBitstat = bis(lrpdb.Status.CmBitstat, MPAPPL) + lrpdb.Status.CmBitStatStr = CMBitmaskprint(lrpdb.Status.CmBitstat) + + return Cardinality, nil +} + +func (r *LRPDBReconciler) ManageConfigMapForCloningAndPlugin(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { + log := r.Log.WithValues("ManageConfigMapForCloningAndPlugin", req.NamespacedName) + log.Info("Frame:") + /* + If configmap parameter is set and init flag is not set + then we need to iniialized the init mask. This is the case for + pdb generated by clone and plug action + */ + if lrpdb.Spec.Action != "CREATE" && lrpdb.Spec.Action != "APPLYSQL" && lrpdb.Spec.PDBConfigMap != "" && bit(lrpdb.Status.CmBitstat, MPINIT) == false { + if r.InitConfigMap(ctx, req, lrpdb) == nil { + log.Info("Cannot initialize config map for pdb.........:" + lrpdb.Spec.LRPDBName) + return nil + } + log.Info("Call...........:ApplyConfigMap(ctx, req, lrpdb)") + Cardinality, _ := r.ApplyConfigMap(ctx, req, lrpdb) + log.Info("Cardnality:....:" + strconv.Itoa(int(Cardinality))) + if Cardinality == 0 { + return nil + } + + } + return nil +} + +func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, url string, payload map[string]string, action string) (string, error) { + var c client.Client + var r logr.Logger + var e record.EventRecorder + var err error + + recpdb, ok1 := intr.(*LRPDBReconciler) + if ok1 { + fmt.Printf("func NewCallLApi ((*PDBReconciler),......)\n") + c = recpdb.Client + e = recpdb.Recorder + r = recpdb.Log + } + + reccdb, ok2 := intr.(*LRESTReconciler) + if ok2 { + fmt.Printf("func NewCallLApi ((*CDBReconciler),......)\n") + c = reccdb.Client + e = reccdb.Recorder + r = reccdb.Log + } + + log := r.WithValues("NewCallAPISQL", req.NamespacedName) + + secret := &corev1.Secret{} + + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsKey.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + rsaKeyPEM := secret.Data[lrpdb.Spec.LRPDBTlsKey.Secret.Key] + + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + rsaCertPEM := secret.Data[lrpdb.Spec.LRPDBTlsCrt.Secret.Key] + + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCat.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCat.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + caCert := secret.Data[lrpdb.Spec.LRPDBTlsCat.Secret.Key] + /* + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaKeyPEM)) + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaCertPEM)) + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(caCert)) + */ + + certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) + if err != nil { + lrpdb.Status.Msg = "Error tls.X509KeyPair" + return "", err + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + /* + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool} + */ + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool, + //MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + PreferServerCipherSuites: true, + CipherSuites: []uint16{ + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_RSA_WITH_AES_256_CBC_SHA, + }, + } + + tr := &http.Transport{TLSClientConfig: tlsConf} + + httpclient := &http.Client{Transport: tr} + + log.Info("Issuing REST call", "URL", url, "Action", action) + + // Get Web Server User + //secret := &corev1.Secret{} + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.WebLrpdbServerUser.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.WebLrpdbServerUser.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + webUserEnc := string(secret.Data[lrpdb.Spec.WebLrpdbServerUser.Secret.Key]) + webUserEnc = strings.TrimSpace(webUserEnc) + + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBPriKey.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBPriKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + privKey := string(secret.Data[lrpdb.Spec.LRPDBPriKey.Secret.Key]) + webUser, err := CommonDecryptWithPrivKey2(privKey, webUserEnc, req) + + // Get Web Server User Password + secret = &corev1.Secret{} + err = c.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.WebLrpdbServerPwd.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.WebLrpdbServerPwd.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + webUserPwdEnc := string(secret.Data[lrpdb.Spec.WebLrpdbServerPwd.Secret.Key]) + webUserPwdEnc = strings.TrimSpace(webUserPwdEnc) + webUserPwd, err := CommonDecryptWithPrivKey2(privKey, webUserPwdEnc, req) + + var httpreq *http.Request + if action == "GET" { + httpreq, err = http.NewRequest(action, url, nil) + } else { + jsonValue, _ := json.Marshal(payload) + httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) + } + + if err != nil { + log.Info("Unable to create HTTP Request for LRPDB : "+lrpdb.Name, "err", err.Error()) + return "", err + } + + httpreq.Header.Add("Accept", "application/json") + httpreq.Header.Add("Content-Type", "application/json") + httpreq.SetBasicAuth(webUser, webUserPwd) + + resp, err := httpclient.Do(httpreq) + if err != nil { + errmsg := err.Error() + log.Error(err, "Failed - Could not connect to LREST Pod", "err", err.Error()) + lrpdb.Status.Msg = "Error: Could not connect to LREST Pod" + e.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", errmsg) + return "", err + } + + if resp.StatusCode != http.StatusOK { + bb, _ := ioutil.ReadAll(resp.Body) + + if resp.StatusCode == 404 { + lrpdb.Status.ConnString = "" + lrpdb.Status.Msg = lrpdb.Spec.LRPDBName + " not found" + + } else { + if flood_control == false { + lrpdb.Status.Msg = "LREST Error - HTTP Status Code:" + strconv.Itoa(resp.StatusCode) + } + } + + if flood_control == false { + log.Info("LREST Error - HTTP Status Code :"+strconv.Itoa(resp.StatusCode), "Err", string(bb)) + } + + var apiErr LRESTError + json.Unmarshal([]byte(bb), &apiErr) + if flood_control == false { + e.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", "Failed: %s", apiErr.Message) + } + fmt.Printf("\n================== APIERR ======================\n") + fmt.Printf("URL=%s\n", url) + fmt.Printf("resp.StatusCode=%s\n", strconv.Itoa(resp.StatusCode)) + fmt.Printf("\n================== APIERR ======================\n") + flood_control = true + return "", errors.New("LREST Error") + } + flood_control = false + + defer resp.Body.Close() + + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Print(err.Error()) + } + respData := string(bodyBytes) + fmt.Print("CALL API return msg.....:") + fmt.Println(string(bodyBytes)) + + var apiResponse restSQLCollection + json.Unmarshal([]byte(bodyBytes), &apiResponse) + fmt.Printf("===> %#v\n", apiResponse) + fmt.Printf("===> %+v\n", apiResponse) + + errFound := false + for _, sqlItem := range apiResponse.Items { + if sqlItem.ErrorDetails != "" { + log.Info("LREST Error - Oracle Error Code :" + strconv.Itoa(sqlItem.ErrorCode)) + if !errFound { + lrpdb.Status.Msg = sqlItem.ErrorDetails + } + e.Eventf(lrpdb, corev1.EventTypeWarning, "OraError", "%s", sqlItem.ErrorDetails) + errFound = true + } + } + + if errFound { + return "", errors.New("Oracle Error") + } + + return respData, nil +} + +func (r *LRPDBReconciler) GetSqlCode(rsp string, sqlcode *int) error { + log := r.Log.WithValues("GetSqlCode", "callAPI(...)") + + var objmap map[string]interface{} + if err := json.Unmarshal([]byte(rsp), &objmap); err != nil { + log.Error(err, "failed to get respData from callAPI", "err", err.Error()) + return err + } + + *sqlcode = int(objmap["sqlcode"].(float64)) + log.Info("sqlcode.......:ora-" + strconv.Itoa(*sqlcode)) + if *sqlcode != 0 { + switch strconv.Itoa(*sqlcode) { + case "65019": /* already open */ + return nil + case "65020": /* already closed */ + return nil + } + err := fmt.Errorf("%v", sqlcode) + return err + } + return nil +} + +func (r *LRPDBReconciler) GetRestricted(rsp string, restrictmode *string) error { + log := r.Log.WithValues("GetRestriced", "callAPI(...)") + + var objmap map[string]interface{} + if err := json.Unmarshal([]byte(rsp), &objmap); err != nil { + log.Error(err, "failed to get respData from callAPI", "err", err.Error()) + return err + } + + *restrictmode = string(objmap["restricted"].(string)) + + return nil +} + +func (r *LRPDBReconciler) GetPdbSize2(rsp string, pdbsize *string) error { + log := r.Log.WithValues("GetPdbSize2", "callAPI(...)") + var objmap map[string]interface{} + if err := json.Unmarshal([]byte(rsp), &objmap); err != nil { + log.Error(err, "failed to get respData from callAPI", "err", err.Error()) + return err + } + *pdbsize = fmt.Sprintf("%4.2f", ((objmap["total_size"].(float64))/1024/1024/1024)) + "G" + return nil +} + +func (r *LRPDBReconciler) GetOpenMode(rsp string, openmode *string) error { + log := r.Log.WithValues("GetRestriced", "callAPI(...)") + + var objmap map[string]interface{} + if err := json.Unmarshal([]byte(rsp), &objmap); err != nil { + log.Error(err, "failed to get respData from callAPI", "err", err.Error()) + return err + } + + *openmode = string(objmap["open_mode"].(string)) + + return nil +} + +/* +************************************************ + - Issue a REST API Call to the LREST container + /*********************************************** +*/ +func (r *LRPDBReconciler) callAPI(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, url string, payload map[string]string, action string) (string, error) { + log := r.Log.WithValues("callAPI", req.NamespacedName) + + var err error + + secret := &corev1.Secret{} + + err = r.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsKey.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsKey.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + rsaKeyPEM := secret.Data[lrpdb.Spec.LRPDBTlsKey.Secret.Key] + + err = r.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCrt.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + rsaCertPEM := secret.Data[lrpdb.Spec.LRPDBTlsCrt.Secret.Key] + + err = r.Get(ctx, types.NamespacedName{Name: lrpdb.Spec.LRPDBTlsCat.Secret.SecretName, Namespace: lrpdb.Namespace}, secret) + + if err != nil { + if apierrors.IsNotFound(err) { + log.Info("Secret not found:" + lrpdb.Spec.LRPDBTlsCat.Secret.SecretName) + return "", err + } + log.Error(err, "Unable to get the secret.") + return "", err + } + + caCert := secret.Data[lrpdb.Spec.LRPDBTlsCat.Secret.Key] + /* + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaKeyPEM)) + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(rsaCertPEM)) + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", string(caCert)) + */ + + certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) + if err != nil { + lrpdb.Status.Msg = "Error tls.X509KeyPair" + return "", err + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + /* + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool} + */ + tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, + RootCAs: caCertPool, + //MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + PreferServerCipherSuites: true, + CipherSuites: []uint16{ + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_RSA_WITH_AES_256_CBC_SHA, + }, + } - } else { + tr := &http.Transport{TLSClientConfig: tlsConf} - lrpdb.Status.Bitstat = bis(lrpdb.Status.Bitstat, MPINIT) - globalconfigmap = lrpdb.Spec.PDBConfigMap - DbParameters, err := r.GetConfigMap(ctx, req, lrpdb) - if err != nil { - log.Error(err, "Fail to fetch configmap ", "err", err.Error()) - return nil - } + httpclient := &http.Client{Transport: tr} - //ParseConfigMapData(DbParameters) + log.Info("Issuing REST call", "URL", url, "Action", action) - return DbParameters + webUser, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.WebLrpdbServerUser.Secret.SecretName, lrpdb.Spec.WebLrpdbServerUser.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) + if err != nil { + log.Error(err, "Unable to get webuser account name ") + return "", err } - return nil -} - -func (r *LRPDBReconciler) GetConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (*corev1.ConfigMap, error) { - log := r.Log.WithValues("GetConfigMap", req.NamespacedName) - log.Info("ConfigMapGlobal.............:" + globalconfigmap) - DbParameters, err := k8s.FetchConfigMap(r.Client, lrpdb.Namespace, globalconfigmap) + webUserPwd, err := r.getEncriptedSecret(ctx, req, lrpdb, lrpdb.Spec.WebLrpdbServerPwd.Secret.SecretName, lrpdb.Spec.WebLrpdbServerPwd.Secret.Key, lrpdb.Spec.LRPDBPriKey.Secret.SecretName, lrpdb.Spec.LRPDBPriKey.Secret.Key) if err != nil { - log.Error(err, "Fail to fetch configmap", "err", err.Error()) - return nil, err + log.Error(err, "Unable to get webuser account password ") + return "", err } - return DbParameters, nil -} - -func (r *LRPDBReconciler) ApplyConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (int32, error) { - log := r.Log.WithValues("ApplyConfigMap", req.NamespacedName) - /* We read the config map and apply the setting to the pdb */ + var httpreq *http.Request + if action == "GET" { + httpreq, err = http.NewRequest(action, url, nil) + } else { + jsonValue, _ := json.Marshal(payload) + httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) + } - log.Info("Starting Apply Config Map Process") - configmap, err := r.GetConfigMap(ctx, req, lrpdb) if err != nil { - log.Info("Cannot get config map in the open yaml file") - return 0, nil - } - Cardinality := int32(len(configmap.Data)) - if Cardinality == 0 { - log.Info("Empty config map... nothing to do ") - return 0, nil + log.Info("Unable to create HTTP Request for LRPDB : "+lrpdb.Name, "err", err.Error()) + return "", err } - log.Info("GetConfigMap completed") - lrest, err := r.getLRESTResource(ctx, req, lrpdb) + httpreq.Header.Add("Accept", "application/json") + httpreq.Header.Add("Content-Type", "application/json") + httpreq.SetBasicAuth(webUser, webUserPwd) + + resp, err := httpclient.Do(httpreq) if err != nil { - log.Info("Cannot find lrest server") - return 0, nil + errmsg := err.Error() + log.Error(err, "Failed - Could not connect to LREST Pod", "err", err.Error()) + lrpdb.Status.Msg = "Error: Could not connect to LREST Pod" + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", errmsg) + return "", err } - tokens := lrcommons.ParseConfigMapData(configmap) - for cnt := range tokens { - if len(tokens[cnt]) != 0 { - /* avoid null token and check malformed value */ - fmt.Printf("token=[%s]\n", tokens[cnt]) - Parameter := strings.Split(tokens[cnt], " ") - if len(Parameter) != 3 { - log.Info("WARNING malformed value in the configmap") - } else { - fmt.Printf("alter system set %s=%s scope=%s instances=all\n", Parameter[0], Parameter[1], Parameter[2]) - /* Preparing PayLoad - ----------------- - WARNING: event setting is not yet supported. It will be implemented in future release - */ - AlterSystemPayload := map[string]string{ - "state": "ALTER", - "alterSystemParameter": Parameter[0], - "alterSystemValue": Parameter[1], - "parameterScope": Parameter[2], - } - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdb.Spec.LRPDBName - respData, err := r.callAPI(ctx, req, lrpdb, url, AlterSystemPayload, "POST") - if err != nil { - log.Error(err, "callAPI failure durring Apply Config Map", "err", err.Error()) - return 0, err - } - /* check sql code execution */ - var retJson map[string]interface{} - if err := json.Unmarshal([]byte(respData), &retJson); err != nil { - log.Error(err, "failed to get Data from callAPI", "err", err.Error()) - return 0, err - } - /* We do not the execution if something goes wrong for a single parameter - just report the error in the event queue */ - SqlCode := strconv.Itoa(int(retJson["sqlcode"].(float64))) - AlterMsg := fmt.Sprintf("pdb=%s:%s:%s:%s:%s", lrpdb.Spec.LRPDBName, Parameter[0], Parameter[1], Parameter[2], SqlCode) - log.Info("Config Map Apply:......." + AlterMsg) - if SqlCode != "0" { - r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTINFO", AlterMsg) - lrpdb.Status.Bitstat = bis(lrpdb.Status.Bitstat, MPWARN) - } + if resp.StatusCode != http.StatusOK { + bb, _ := ioutil.ReadAll(resp.Body) + + if resp.StatusCode == 404 { + lrpdb.Status.ConnString = "" + lrpdb.Status.Msg = lrpdb.Spec.LRPDBName + " not found" + } else { + if flood_control == false { + lrpdb.Status.Msg = "LREST Error - HTTP Status Code:" + strconv.Itoa(resp.StatusCode) } } + if flood_control == false { + log.Info("LREST Error - HTTP Status Code :"+strconv.Itoa(resp.StatusCode), "Err", string(bb)) + } + + var apiErr LRESTError + json.Unmarshal([]byte(bb), &apiErr) + if flood_control == false { + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "LRESTError", "Failed: %s", apiErr.Message) + } + fmt.Printf("\n================== APIERR ======================\n") + fmt.Printf("%+v \n", apiErr) + fmt.Printf("URL=%s\n", url) + fmt.Printf("resp.StatusCode=%s\n", strconv.Itoa(resp.StatusCode)) + fmt.Printf("\n================== APIERR ======================\n") + flood_control = true + return "", errors.New("LREST Error") } + flood_control = false - lrpdb.Status.Bitstat = bis(lrpdb.Status.Bitstat, MPAPPL) + defer resp.Body.Close() - return Cardinality, nil -} + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Print(err.Error()) + } + respData := string(bodyBytes) + fmt.Print("CALL API return msg.....:") + fmt.Println(string(bodyBytes)) -func (r *LRPDBReconciler) ManageConfigMapForCloningAndPlugin(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { - log := r.Log.WithValues("ManageConfigMapForCloningAndPlugin", req.NamespacedName) - log.Info("Frame:") - /* - If configmap parameter is set and init flag is not set - then we need to iniialized the init mask. This is the case for - pdb generated by clone and plug action - */ - if lrpdb.Spec.Action != "CREATE" && lrpdb.Spec.PDBConfigMap != "" && bit(lrpdb.Status.Bitstat, MPINIT) == false { - if r.InitConfigMap(ctx, req, lrpdb) == nil { - log.Info("Cannot initialize config map for pdb.........:" + lrpdb.Spec.LRPDBName) - return nil - } - log.Info("Call...........:ApplyConfigMap(ctx, req, lrpdb)") - Cardinality, _ := r.ApplyConfigMap(ctx, req, lrpdb) - log.Info("Cardnality:....:" + strconv.Itoa(int(Cardinality))) - if Cardinality == 0 { - return nil + var apiResponse restSQLCollection + json.Unmarshal([]byte(bodyBytes), &apiResponse) + fmt.Printf("===> %#v\n", apiResponse) + fmt.Printf("===> %+v\n", apiResponse) + + errFound := false + for _, sqlItem := range apiResponse.Items { + if sqlItem.ErrorDetails != "" { + log.Info("LREST Error - Oracle Error Code :" + strconv.Itoa(sqlItem.ErrorCode)) + if !errFound { + lrpdb.Status.Msg = sqlItem.ErrorDetails + } + r.Recorder.Eventf(lrpdb, corev1.EventTypeWarning, "OraError", "%s", sqlItem.ErrorDetails) + errFound = true } + } + if errFound { + return "", errors.New("Oracle Error") } - return nil + + return respData, nil } -func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, url string, payload map[string]string, action string) (string, error) { +func NewCallAPISQL(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, url string, payload interface{}, action string) (string, error) { var c client.Client var r logr.Logger var e record.EventRecorder + var TestBuffer string + var jsonMap map[string]interface{} var err error recpdb, ok1 := intr.(*LRPDBReconciler) @@ -2143,7 +2877,7 @@ func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb r = reccdb.Log } - log := r.WithValues("NewCallLAPI", req.NamespacedName) + log := r.WithValues("NewCallAPISQL", req.NamespacedName) secret := &corev1.Secret{} @@ -2203,8 +2937,7 @@ func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb RootCAs: caCertPool} */ tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, - RootCAs: caCertPool, - //MinVersion: tls.VersionTLS12, + RootCAs: caCertPool, CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, PreferServerCipherSuites: true, CipherSuites: []uint16{ @@ -2245,7 +2978,7 @@ func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb return "", err } privKey := string(secret.Data[lrpdb.Spec.LRPDBPriKey.Secret.Key]) - webUser, err := lrcommons.CommonDecryptWithPrivKey(privKey, webUserEnc, req) + webUser, err := CommonDecryptWithPrivKey2(privKey, webUserEnc, req) // Get Web Server User Password secret = &corev1.Secret{} @@ -2260,26 +2993,52 @@ func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb } webUserPwdEnc := string(secret.Data[lrpdb.Spec.WebLrpdbServerPwd.Secret.Key]) webUserPwdEnc = strings.TrimSpace(webUserPwdEnc) - webUserPwd, err := lrcommons.CommonDecryptWithPrivKey(privKey, webUserPwdEnc, req) + webUserPwd, err := CommonDecryptWithPrivKey2(privKey, webUserPwdEnc, req) + + var Httpreq *http.Request - var httpreq *http.Request if action == "GET" { - httpreq, err = http.NewRequest(action, url, nil) + Httpreq, err = http.NewRequest(action, url, nil) } else { - jsonValue, _ := json.Marshal(payload) - httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) - } - - if err != nil { - log.Info("Unable to create HTTP Request for LRPDB : "+lrpdb.Name, "err", err.Error()) - return "", err + /* Section to execute sql and plsql code */ + payloadsql, ok4 := payload.(*PLSQLPayLoad) + if ok4 { + TestBuffer = ParseSQLPayload(payloadsql) + json.Unmarshal([]byte(TestBuffer), &jsonMap) + jsonValue, _ := json.Marshal(jsonMap) + Httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) + if bit(lrpdb.Spec.Debug, DBGAPI) { + fmt.Println("=========================PLSQLDEBUG==============================") + fmt.Println(string(jsonValue)) + fmt.Println("=========================PLSQLDEBUG==============================") + } + if err != nil { + log.Info("Unable to create HTTP Request for LRPDB : "+lrpdb.Name, "err", err.Error()) + return "", err + } + } + /* Section to execute standard pdb operation */ + payloadpdb, ok3 := payload.(map[string]string) + if ok3 { + jsonValue, _ := json.Marshal(payloadpdb) + Httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) + if bit(lrpdb.Spec.Debug, DBGAPI) { + fmt.Println("=========================PLSQLDEBUG==============================") + fmt.Println(string(jsonValue)) + fmt.Println("=========================PLSQLDEBUG==============================") + } + if err != nil { + log.Info("Unable to create HTTP Request for LRPDB : "+lrpdb.Name, "err", err.Error()) + return "", err + } + } } - httpreq.Header.Add("Accept", "application/json") - httpreq.Header.Add("Content-Type", "application/json") - httpreq.SetBasicAuth(webUser, webUserPwd) + Httpreq.Header.Add("Accept", "application/json") + Httpreq.Header.Add("Content-Type", "application/json") + Httpreq.SetBasicAuth(webUser, webUserPwd) - resp, err := httpclient.Do(httpreq) + resp, err := httpclient.Do(Httpreq) if err != nil { errmsg := err.Error() log.Error(err, "Failed - Could not connect to LREST Pod", "err", err.Error()) @@ -2288,7 +3047,6 @@ func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb return "", err } - e.Eventf(lrpdb, corev1.EventTypeWarning, "Done", lrpdb.Spec.CDBResName) if resp.StatusCode != http.StatusOK { bb, _ := ioutil.ReadAll(resp.Body) @@ -2313,7 +3071,6 @@ func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb } fmt.Printf("\n================== APIERR ======================\n") fmt.Printf("%+v \n", apiErr) - fmt.Printf(string(bb)) fmt.Printf("URL=%s\n", url) fmt.Printf("resp.StatusCode=%s\n", strconv.Itoa(resp.StatusCode)) fmt.Printf("\n================== APIERR ======================\n") @@ -2330,6 +3087,8 @@ func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb } respData := string(bodyBytes) fmt.Print("CALL API return msg.....:") + fmt.Printf("%s\n", respData) + fmt.Println(string(bodyBytes)) var apiResponse restSQLCollection @@ -2356,26 +3115,90 @@ func NewCallLAPI(intr interface{}, ctx context.Context, req ctrl.Request, lrpdb return respData, nil } -func (r *LRPDBReconciler) GetSqlCode(rsp string, sqlcode *int) error { - log := r.Log.WithValues("GetSqlCode", "callAPI(...)") +func ParseSQLPayload(payload *PLSQLPayLoad) string { + var Buffer string - var objmap map[string]interface{} - if err := json.Unmarshal([]byte(rsp), &objmap); err != nil { - log.Error(err, "failed to get respData from callAPI", "err", err.Error()) - return err + cnt := 0 + Buffer = "{" + for key, value := range payload.Values { + Buffer += "\"" + key + "\" : \"" + value + "\"," } - *sqlcode = int(objmap["sqlcode"].(float64)) - log.Info("sqlcode.......:ora-" + strconv.Itoa(*sqlcode)) - if *sqlcode != 0 { - switch strconv.Itoa(*sqlcode) { - case "65019": /* already open */ - return nil - case "65020": /* already closed */ - return nil + Nelem := len(payload.Sqltokens) + fmt.Printf("ParseSQLPayload :: Num tokens %d\n", Nelem) + Buffer += "\"Sqltokens\":[" + for _, value := range payload.Sqltokens { + Buffer += "\"" + value + "\"" + if cnt < (Nelem - 1) { + Buffer += "," } - err := fmt.Errorf("%v", sqlcode) + cnt++ + } + + Buffer += "]}" + fmt.Printf("ParseSQLPayload :: %s\n", Buffer) + return Buffer +} + +func (r *LRPDBReconciler) AutoDiscoverActivation(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, active bool) error { + + log := r.Log.WithValues("AutoDiscoverActivation", req.NamespacedName) + if active == false { + log.Info("Disable autodiscover") + } else { + log.Info("Enable autodiscover") + } + + var lrest dbapi.LREST + lrestResName := lrpdb.Spec.CDBResName + lrestNamespace := lrpdb.Spec.CDBNamespace + err := r.Get(context.Background(), client.ObjectKey{ + Namespace: lrestNamespace, + Name: lrestResName, + }, &lrest) + lrest.Spec.PdbAutoDiscover = active + err = r.Update(context.TODO(), &lrest) + if err != nil { return err } + return nil } + +func (r *LRPDBReconciler) GetPdbSize(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, pdbaname string) string { + log := r.Log.WithValues("GetPdbSize", req.NamespacedName) + var PdbSize string + // if we cannot get the pdbsize ,whatever reason, we return "undefined" size + lrest, err := r.getLRESTResource(ctx, req, lrpdb) + if err != nil { + log.Info("Cannot get lrest server") + return "undefined" + } + + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + "/status/" + + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, nil, "GET") + if err != nil { + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + return "undefined" + } + + var objmap map[string]interface{} + if err := json.Unmarshal([]byte(respData), &objmap); err != nil { + log.Error(err, "Failed json.Unmarshal :"+lrpdbName, "err", err.Error()) + return "undefined" + } + + PdbSize = fmt.Sprintf("%4.2f", ((objmap["total_size"].(float64))/1024/1024/1024)) + "G" + return PdbSize +} + +func (r *LRPDBReconciler) UpdateStatus(ctx context.Context, req ctrl.Request, lrpdb *databasev4.LRPDB) { + log := r.Log.WithValues("UpdateStatus", req.NamespacedName) + err := r.Status().Update(ctx, lrpdb) + if err != nil { + fmt.Printf("[1]Error updating status\n") + log.Error(err, err.Error()) + } +} diff --git a/controllers/database/pdb_controller.go b/controllers/database/pdb_controller.go index a2ca0f85..c61c3073 100644 --- a/controllers/database/pdb_controller.go +++ b/controllers/database/pdb_controller.go @@ -663,7 +663,7 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db return err } privKey := string(secret.Data[pdb.Spec.PDBPriKey.Secret.Key]) - pdbAdminName, err := lrcommons.CommonDecryptWithPrivKey(privKey, pdbAdminNameEnc, req) + pdbAdminName, err := lrcommons.CommonDecryptWithPrivKey2(privKey, pdbAdminNameEnc, req) // Get Web Server User Password secret = &corev1.Secret{} @@ -678,7 +678,7 @@ func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *db } pdbAdminPwdEnc := string(secret.Data[pdb.Spec.AdminPwd.Secret.Key]) pdbAdminPwdEnc = strings.TrimSpace(pdbAdminPwdEnc) - pdbAdminPwd, err := lrcommons.CommonDecryptWithPrivKey(privKey, pdbAdminPwdEnc, req) + pdbAdminPwd, err := lrcommons.CommonDecryptWithPrivKey2(privKey, pdbAdminPwdEnc, req) pdbAdminName = strings.TrimSuffix(pdbAdminName, "\n") pdbAdminPwd = strings.TrimSuffix(pdbAdminPwd, "\n") /*** END GET ENCPASS ***/ @@ -1523,7 +1523,7 @@ func NewCallApi(intr interface{}, ctx context.Context, req ctrl.Request, pdb *db return "", err } privKey := string(secret.Data[pdb.Spec.PDBPriKey.Secret.Key]) - webUser, err := lrcommons.CommonDecryptWithPrivKey(privKey, webUserEnc, req) + webUser, err := lrcommons.CommonDecryptWithPrivKey2(privKey, webUserEnc, req) // Get Web Server User Password secret = &corev1.Secret{} @@ -1538,7 +1538,7 @@ func NewCallApi(intr interface{}, ctx context.Context, req ctrl.Request, pdb *db } webUserPwdEnc := string(secret.Data[pdb.Spec.WebServerPwd.Secret.Key]) webUserPwdEnc = strings.TrimSpace(webUserPwdEnc) - webUserPwd, err := lrcommons.CommonDecryptWithPrivKey(privKey, webUserPwdEnc, req) + webUserPwd, err := lrcommons.CommonDecryptWithPrivKey2(privKey, webUserPwdEnc, req) /////////////////////////////////////////////////////////////////////////////////// var httpreq *http.Request diff --git a/docs/multitenant/lrest-based/usecase/makefile b/docs/multitenant/lrest-based/usecase/makefile index 1de320ad..5a74c669 100644 --- a/docs/multitenant/lrest-based/usecase/makefile +++ b/docs/multitenant/lrest-based/usecase/makefile @@ -52,6 +52,7 @@ # map_pdb1_resource.yaml : map the first pluggable database # config_map.yam : pdb parameters array # altersystem_pdb1_resource.yaml : chage cpu_count count parameter for the first pdb +# config_map_plsql.yaml : plsql code # DATE := `date "+%y%m%d%H%M%S"` ###################### @@ -71,6 +72,9 @@ export LRSNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep LRSNAMESPACE|cut export LRESTIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep LRESTIMG|cut -d : -f 2,3) export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) +export SERVICENAM=$(shell cat $(PARAMETERS)|grep -v ^\#|grep SERVICENAMEACCOUNT|cut -d : -f 2) +export OPENSHIFT=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPENSHIFT|cut -d : -f 2) +export PLSQLMAP=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PLSQLMAP|cut -d : -f 2) export OPRNAMESPACE=oracle-database-operator-system export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml export TEST_EXEC_TIMEOUT=3m @@ -89,6 +93,12 @@ WBPASSFILE=wbpass.txt PDBUSRFILE=pdbusr.txt PDBPWDFILE=pdbpwd.txt +################# +## PARAMETERS ## +################# + +export ASSERTDELETION=true + ################# ### FILE LIST ### ################# @@ -124,8 +134,11 @@ export LRPDBMAP1=map_pdb1_resource.yaml export LRPDBMAP2=map_pdb2_resource.yaml export LRPDBMAP3=map_pdb3_resource.yaml +export SECURITYCTX=security_context.yaml + export ALTERSYSTEMYAML=altersystem_pdb1_resource.yaml export CONFIG_MAP=config_map_pdb.yaml +export CONFIG_MAP_SQL=config_map_plsql.yaml @@ -139,6 +152,8 @@ CP=/usr/bin/cp TAR=/usr/bin/tar MKDIR=/usr/bin/mkdir SED=/usr/bin/sed +MAKE=/usr/bin/make +MAKEFILE=./makefile check: @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) @@ -151,7 +166,9 @@ check: @printf "PDBNAMESPACE...........:%s\n" $(PDBNAMESPACE) @printf "LRSNAMESPACE...........:%s\n" $(LRSNAMESPACE) @printf "COMPANY................:%s\n" $(COMPANY) + @printf "SERVICENAMEACCOUNT.....:%s\n" $(SERVICENAM) @printf "APIVERSION.............:%s\n" $(APIVERSION) + @printf "PLSQLMAP...............:%s\n" $(PLSQLMAP) define msg @printf "\033[31;7m%s\033[0m\r" "......................................]" @@ -336,6 +353,20 @@ spec: dbTnsurl : ${TNSALIAS} replicas: 1 deletePdbCascade: true + autodiscover: true + namespaceAutoDiscover: ${PDBNAMESPACE} + clusterIp: false + loadBalancer: false + trace_level_client : 16 +EOF + +if [[ ${OPENSHIFT} == "true" ]];then +cat <> ${LREST_POD} + serviceAccountName: ${SERVICENAM} +EOF +fi + +cat <> ${LREST_POD} cdbAdminUser: secret: secretName: "dbuser" @@ -360,6 +391,10 @@ spec: secret: secretName: "db-tls" key: "tls.crt" + cdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" cdbPubKey: secret: secretName: "pubkey" @@ -389,14 +424,13 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbdev" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: ${ASSERTDELETION} fileNameConversions: "NONE" unlimitedStorage: false pdbconfigmap: "config-map-pdb" tdeImport: false totalSize: "2G" tempSize: "800M" - action: "Create" EOF cat < ${LRPDBCRE2} @@ -412,14 +446,13 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbprd" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: ${ASSERTDELETION} fileNameConversions: "NONE" unlimitedStorage: false pdbconfigmap: "config-map-pdb" tdeImport: false totalSize: "2G" tempSize: "800M" - action: "Create" EOF cat <${LRPDBOPEN1} @@ -435,7 +468,6 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbdev" - action: "Modify" pdbState: "OPEN" modifyOption: "READ WRITE" EOF @@ -453,7 +485,6 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbprd" - action: "Modify" pdbState: "OPEN" modifyOption: "READ WRITE" EOF @@ -471,7 +502,6 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "new_clone" - action: "Modify" pdbState: "OPEN" modifyOption: "READ WRITE" EOF @@ -491,7 +521,6 @@ spec: pdbName: "pdbdev" pdbState: "CLOSE" modifyOption: "IMMEDIATE" - action: "Modify" EOF cat <${LRPDBCLOSE2} @@ -509,7 +538,6 @@ spec: pdbName: "pdbprd" pdbState: "CLOSE" modifyOption: "IMMEDIATE" - action: "Modify" EOF cat <${LRPDBCLOSE3} @@ -527,7 +555,6 @@ spec: pdbName: "new_clone" pdbState: "CLOSE" modifyOption: "IMMEDIATE" - action: "Modify" EOF cat < ${LRPDBCLONE1} @@ -548,8 +575,7 @@ spec: totalSize: "UNLIMITED" tempSize: "UNLIMITED" pdbconfigmap: "config-map-pdb" - assertiveLrpdbDeletion: true - action: "Clone" + imperativeLrpdbDeletion: true EOF cat < ${LRPDBCLONE2} @@ -570,8 +596,7 @@ spec: totalSize: "UNLIMITED" tempSize: "UNLIMITED" pdbconfigmap: "config-map-pdb" - assertiveLrpdbDeletion: true - action: "Clone" + imperativeLrpdbDeletion: ${ASSERTDELETION} EOF cat < ${LRPDBDELETE1} @@ -586,7 +611,7 @@ spec: cdbResName: "cdb-dev" cdbNamespace: "${LRSNAMESPACE}" pdbName: "pdbdev" - action: "Delete" + pdbState: "DELETE" dropAction: "INCLUDING" EOF @@ -602,7 +627,22 @@ spec: cdbResName: "cdb-dev" cdbNamespace: "${LRSNAMESPACE}" pdbName: "pdbprd" - action: "Delete" + pdbState: "DELETE" + dropAction: "INCLUDING" +EOF + +cat < ${LRPDBDELETE3} +apiVersion: database.oracle.com/${APIVERSION} +kind: LRPDB +metadata: + name: pdb3 + namespace: ${PDBNAMESPACE} + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "${LRSNAMESPACE}" + pdbName: "new_clone" dropAction: "INCLUDING" EOF @@ -619,8 +659,8 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbdev" + pdbState: "UNPLUG" xmlFileName: "/var/tmp/pdb.$$.xml" - action: "Unplug" EOF cat <${LRPDBPLUG1} @@ -636,16 +676,15 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbdev" + pdbState: "PLUG" xmlFileName: "/var/tmp/pdb.$$.xml" - action: "plug" fileNameConversions: "NONE" sourceFileNameConversions: "NONE" copyAction: "MOVE" totalSize: "1G" tempSize: "100M" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true pdbconfigmap: "config-map-pdb" - action: "Plug" EOF cat <${LRPDBMAP1} @@ -661,11 +700,10 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbdev" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true fileNameConversions: "NONE" totalSize: "1G" tempSize: "100M" - action: "Map" EOF cat <${LRPDBMAP2} @@ -681,11 +719,10 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbprd" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true fileNameConversions: "NONE" totalSize: "1G" tempSize: "100M" - action: "Map" EOF @@ -702,11 +739,10 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "new_clone" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true fileNameConversions: "NONE" totalSize: "1G" tempSize: "100M" - action: "Map" EOF cat <${CONFIG_MAP} @@ -737,16 +773,47 @@ spec: cdbNamespace: "${LRSNAMESPACE}" cdbName: "DB12" pdbName: "pdbdev" - action: "Alter" alterSystemParameter : "cpu_count" alterSystemValue : "3" parameterScope : "memory" +EOF +cat < ${CONFIG_MAP_SQL} +apiVersion: v1 +kind: ConfigMap +metadata: + name: ${PLSQLMAP} + namespace: ${PDBNAMESPACE} +data: + plblock1.sql: | + create user k8stestuser identified by tkstestuser + plblock2.sql: | + grant dba to k8stestuser + plblock3.sql: | + create table k8stestuser.rndnum (c1 number ,c2 number) + plblock4.sql: | + alter session set events '942 trace name errorstack level 3' + plblock5.sql: | + create or replace procedure k8stestuser.gennum + as + begin + for cnt in 1..100 + loop + insert into k8stestuser.rndnum values (dbms_random.value(1,100), + dbms_random.value(1,100)); + end loop; + commit; + end; + plblock6.sql: | + begin + k8stestuser.gennum; + end; EOF + ## Auth information -for _file in ${LRPDBCRE1} ${LRPDBCRE2} ${LRPDBOPEN1} ${LRPDBOPEN2} ${LRPDBOPEN3} ${LRPDBCLOSE1} ${LRPDBCLOSE2} ${LRPDBCLOSE3} ${LRPDBCLONE1} ${LRPDBCLONE2} ${LRPDBDELETE1} ${LRPDBDELETE2} ${LRPDBUNPLUG1} ${LRPDBPLUG1} ${LRPDBMAP1} ${LRPDBMAP2} ${LRPDBMAP3} ${ALTERSYSTEMYAML} +for _file in ${LRPDBCRE1} ${LRPDBCRE2} ${LRPDBOPEN1} ${LRPDBOPEN2} ${LRPDBOPEN3} ${LRPDBCLOSE1} ${LRPDBCLOSE2} ${LRPDBCLOSE3} ${LRPDBCLONE1} ${LRPDBCLONE2} ${LRPDBDELETE1} ${LRPDBDELETE2} ${LRPDBDELETE3} ${LRPDBUNPLUG1} ${LRPDBPLUG1} ${LRPDBMAP1} ${LRPDBMAP2} ${LRPDBMAP3} ${ALTERSYSTEMYAML} do ls -ltr ${_file} cat authsection.yaml >> ${_file} @@ -754,14 +821,115 @@ done rm authsection.yaml endef +define _script03 +cat <${SECURITYCTX} +# +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- + +# Create a Security Context Contraint +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + name: lrest-scc +allowPrivilegedContainer: false +allowedCapabilities: + - SYS_NICE +runAsUser: + type: MustRunAs + uid: 54321 +seLinuxContext: + type: RunAsAny +fsGroup: + type: MustRunAs + ranges: + - min: 54321 + max: 54321 +supplementalGroups: + type: MustRunAs + ranges: + - min: 54321 + max: 54321 +--- + +# Create a Security Context Contraint +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + name: lrest-root-user-scc +allowPrivilegedContainer: false +allowedCapabilities: + - SYS_NICE +runAsUser: + type: MustRunAsRange + uidRangeMin: 0 + uidRangeMax: 54321 +seLinuxContext: + type: RunAsAny +fsGroup: + type: MustRunAs + ranges: + - min: 0 + max: 54325 +supplementalGroups: + type: MustRunAs + ranges: + - min: 0 + max: 54321 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: lrest-sa + namespace: ${LRSNAMESPACE} +--- + +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: use-lrest-scc + namespace: ${LRSNAMESPACE} +rules: + - apiGroups: + - security.openshift.io + verbs: + - use + resources: + - securitycontextconstraints + resourceNames: + - lrest-scc + - lrest-root-user-scc +--- + +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: use-lrest-scc + namespace: ${LRSNAMESPACE} +subjects: + - kind: ServiceAccount + name: lrest-sa + namespace: ${LRSNAMESPACE} +roleRef: + kind: Role + name: use-lrest-scc + apiGroup: rbac.authorization.k8s.io +EOF + +endef + export script02 = $(value _script02) +export script03 = $(value _script03) genyaml: secyaml @ eval "$$script01" @ eval "$$script02" + @ eval "$$script03" cleanyaml: - - $(RM) $(LRPDBMAP3) $(LRPDBMAP2) $(LRPDBMAP1) $(LRPDBPLUG1) $(LRPDBUNPLUG1) $(LRPDBDELETE2) $(LRPDBDELETE1) $(LRPDBCLONE2) $(LRPDBCLONE1) $(LRPDBCLOSE3) $(LRPDBCLOSE2) $(LRPDBCLOSE1) $(LRPDBOPEN3) $(LRPDBOPEN2) $(LRPDBOPEN1) $(LRPDBCRE2) $(LRPDBCRE1) $(LREST_POD) ${ALTERSYSTEMYAML} + - $(RM) $(LRPDBMAP3) $(LRPDBMAP2) $(LRPDBMAP1) $(LRPDBPLUG1) $(LRPDBUNPLUG1) $(LRPDBDELETE2) $(LRPDBDELETE1) $(LRPDBDELETE3) $(LRPDBCLONE2) $(LRPDBCLONE1) $(LRPDBCLOSE3) $(LRPDBCLOSE2) $(LRPDBCLOSE1) $(LRPDBOPEN3) $(LRPDBOPEN2) $(LRPDBOPEN1) $(LRPDBCRE2) $(LRPDBCRE1) $(LREST_POD) ${ALTERSYSTEMYAML} - $(RM) ${CONFIG_MAP} ${PDBNAMESPACE}_binding.yaml ${LRSNAMESPACE}_binding.yaml @@ -806,9 +974,21 @@ dump: $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) +listimage: + $(KUBECTL) get pods --all-namespaces -o jsonpath="{.items[*].spec['initContainers', 'containers'][*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c + +getvaliduid: + $(KUBECTL) get namespace $(LRSNAMESPACE) -ojson | jq '.metadata.annotations | with_entries(select(.key | contains("sa.scc")))' + + ####################################################### #### TEST SECTION #### ####################################################### +CREATEMSG="create:[op completed]" +OPENMSG="open:[op. completed]" +CLOSEMSG="close:[op. completed]" +CLONEMSG="clone:[op. completed]" +PLUGMSG="plug:[op. completed]" run00: @$(call msg,"lrest pod creation") @@ -822,28 +1002,79 @@ run00: run01.1: @$(call msg,"lrpdb pdb1 creation") $(KUBECTL) apply -f $(LRPDBCRE1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(CREATEMSG) lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg, "lrpdb pdb1 creation completed") $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) run01.2: @$(call msg, "lrpdb pdb2 creation") $(KUBECTL) apply -f $(LRPDBCRE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(CREATEMSG) lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg, "lrpdb pdb2 creation completed") $(KUBECTL) get lrpdb pdb2 -n $(PDBNAMESPACE) +open: + @$(call msg, "lrpdb $(LRPDBNAME) open") + $(KUBECTL) patch lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) -p \ + '{"spec":{"pdbState":"OPEN","modifyOption":"READ WRITE","modifyOption2":"NONE"}}' --type=merge + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(OPENMSG) lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "lrpdb $(LRPDBNAME) completed") + $(KUBECTL) get lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) + +openrestricted: + @$(call msg, "lrpdb $(LRPDBNAME) open") + $(KUBECTL) patch lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) -p \ + '{"spec":{"pdbState":"OPEN","modifyOption":"READ WRITE","modifyOption2":"RESTRICTED"}}' --type=merge + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(OPENMSG) lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "lrpdb $(LRPDBNAME) completed") + $(KUBECTL) get lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) + + +close: + @$(call msg, "lrpdb $(LRPDBNAME) close") + $(KUBECTL) patch lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) -p \ + '{"spec":{"pdbState":"CLOSE","modifyOption":"IMMEDIATE"}}' --type=merge + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(CLOSEMSG) lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg, "lrpdb $(LRPDBNAME) completed") + $(KUBECTL) get lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) + +openpdb1: + $(MAKE) -f $(MAKEFILE) open LRPDBNAME=pdb1 + +openpdb2: + $(MAKE) -f $(MAKEFILE) open LRPDBNAME=pdb2 + +openpdb3: + $(MAKE) -f $(MAKEFILE) open LRPDBNAME=pdb3 + +openpdb1rs: + $(MAKE) -f $(MAKEFILE) openrestricted LRPDBNAME=pdb1 + +openpdb2rs: + $(MAKE) -f $(MAKEFILE) openrestricted LRPDBNAME=pdb2 + +closepdb1: + $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb1 + +closepdb2: + $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb2 + +closepdb3: + $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb3 + + run02.1: @$(call msg, "lrpdb pdb1 open") $(KUBECTL) apply -f $(LRPDBOPEN1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(OPENMSG) lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg, "lrpdb pdb1 open completed") $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) + run02.2: @$(call msg,"lrpdb pdb2 open") $(KUBECTL) apply -f $(LRPDBOPEN2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(OPENMSG) lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg,"lrpdb pdb2 open completed") $(KUBECTL) get lrpdb pdb2 -n $(PDBNAMESPACE) @@ -851,7 +1082,7 @@ run02.2: run03.1: @$(call msg,"clone pdb1-->pdb3") $(KUBECTL) apply -f $(LRPDBCLONE1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb3 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(CLONEMSG) lrpdb pdb3 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg,"clone pdb1-->pdb3 completed") $(KUBECTL) get lrpdb pdb3 -n $(PDBNAMESPACE) @@ -859,7 +1090,7 @@ run03.1: run03.2: @$(call msg,"clone pdb2-->pdb4") $(KUBECTL) apply -f $(LRPDBCLONE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(CLONEMSG) lrpdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg,"clone pdb2-->pdb4 completed") $(KUBECTL) get lrpdb pdb3 -n $(PDBNAMESPACE) @@ -867,14 +1098,14 @@ run03.2: run04.1: @$(call msg,"lrpdb pdb1 close") $(KUBECTL) apply -f $(LRPDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(CLOSEMSG) lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg, "lrpdb pdb1 close completed") $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) run04.2: @$(call msg,"lrpdb pdb2 close") $(KUBECTL) apply -f $(LRPDBCLOSE2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(CLOSEMSG) lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg,"lrpdb pdb2 close completed") $(KUBECTL) get lrpdb pdb2 -n $(PDBNAMESPACE) @@ -887,14 +1118,14 @@ run05.1: run06.1: @$(call msg, "lrpdb pdb1 plug") $(KUBECTL) apply -f $(LRPDBPLUG1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(PLUGMSG) lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg, "lrpdb pdb1 plug completed") $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) run07.1: @$(call msg,"lrpdb pdb1 delete ") - $(KUBECTL) apply -f $(LRPDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.status.msg'}=$(CLOSEMSG) lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) $(KUBECTL) apply -f $(LRPDBDELETE1) $(KUBECTL) wait --for=delete lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg,"lrpdb pdb1 delete") @@ -906,6 +1137,80 @@ run99.1: $(KUBECTL) get lrest -n $(LRSNAMESPACE) $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) -runall01: run00 run01.1 run01.2 run02.1 run02.2 run03.1 run03.2 run04.1 run04.2 run05.1 run06.1 run07.1 + +tklifecycle: runall01 + +tkonepdbcre: runall02 + +tkapplyinit: + @$(call msg," init config map creation") + -- $(KUBECTL) delete -f $(CONFIG_MAP) -n $(PDBNAMESPACE) + $(KUBECTL) apply -f $(CONFIG_MAP) -n $(PDBNAMESPACE) + +tkplsqlexec: + @$(call msg,"==>PLSQL TESTING<==") + $(MAKE) -f $(MAKEFILE) run00 run01.1 run02.1 + @$(call msg,"delete config map ") + -- $(KUBECTL) delete configmap $(PLSQLMAP) -n $(PDBNAMESPACE) + @$(call msg,"recreating config sql map") + $(KUBECTL) apply -f $(CONFIG_MAP_SQL) + $(KUBECTL) describe configmap $(PLSQLMAP) -n $(PDBNAMESPACE) + @$(call msg,"applying config sql map") + $(KUBECTL) patch lrpdb pdb1 -n $(PDBNAMESPACE) -p \ + '{"spec":{"codeconfigmap":"$(PLSQLMAP)"}}' --type=merge + sleep 10 + $(KUBECTL) get events --sort-by='.lastTimestamp' -n $(PDBNAMESPACE) + $(KUBECTL) get events -n $(PDBNAMESPACE) --sort-by='.lastTimestamp' --field-selector involvedObject.name=pdb1|grep APPLYSQL + $(KUBECTL) get events -n $(PDBNAMESPACE) --sort-by='.lastTimestamp' --field-selector involvedObject.name=pdb1|grep APPLYSQL + + + +tkaudosicov: + @$(call msg,"==>AUTODISCOVER TESTING<==") + $(MAKE) -f $(MAKEFILE) genyaml ASSERTDELETION=false + $(MAKE) -f $(MAKEFILE) run00 run01.1 run02.1 + @$(call msg,"deleting pdb1 (imperativedeletion=false)") + $(KUBECTL) delete lrpdb pdb1 -n $(PDBNAMESPACE) + $(KUBECTL) wait --for=delete lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"checking lrpdb pdb1 recreation") + $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" \ + lrpdb atd-pdbdev -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(MAKE) -f $(MAKEFILE) genyaml ASSERTDELETION=true + +pdbsize: + $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.pdbName}{"\t"}{.spec.totalSize}{"\t"}{.status.totalSize}{"\n"}{end}' + + + +# tkapplyinit - config map creation +# run00 - lrest pod creation +# run01.1 - pdb1 creation +# run01.2 - pdb2 creation +# run02.1 - pdb1 open - declarative +# run02.2 - pdb2 open - declarative +# run03.1 - pdb1 clone - declarative +# run04.1 - pdb1 close - declarative +# run04.2 - pdb2 close - declatative +# run05.1 - pdb1 unplug - declarative +# run06.1 - pdb1 plug - declarative +# openpdb1 - pdb1 open - imperative +# openpdb2 - pdb2 open - imperative +# closepdb1 - pdb1 close - imperative +# closepdb2 - pdb2 close - imperative +# openpdb1rs - pdb1 open restrict - imperative +# openpdb2rs - pdb2 open restrict - imperative +# + +runall01: tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run03.1 run03.2 run04.1 run04.2 run05.1 run06.1 run07.1 +runall02: tkapplyinit run00 run01.1 openpdb1 +runall03: tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 openpdb1rs + $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb1 + $(MAKE) -f $(MAKEFILE) open LRPDBNAME=pdb1 + $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb1 + $(MAKE) -f $(MAKEFILE) run05.1 run06.1 + +runall04: tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 run05.1 run06.1 closepdb1 + + diff --git a/main.go b/main.go index 818b8508..38f0e994 100644 --- a/main.go +++ b/main.go @@ -55,6 +55,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" @@ -127,6 +128,10 @@ func main() { opts.DefaultNamespaces = watchNamespaces return cache.New(config, opts) }, + EventBroadcaster: record.NewBroadcasterWithCorrelatorOptions(record.CorrelatorOptions{ + BurstSize: 10, + QPS: 1, + }), } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), opt) diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 6e391fbc..f8d29401 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -639,9 +639,9 @@ spec: - jsonPath: .spec.details.isDedicated name: Dedicated type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer + - jsonPath: .spec.details.computeCount + name: Compute Count + type: number - jsonPath: .spec.details.dataStorageSizeInTBs name: Storage (TB) type: integer @@ -974,9 +974,9 @@ spec: - jsonPath: .spec.details.isDedicated name: Dedicated type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer + - jsonPath: .spec.details.computeCount + name: Compute Count + type: number - jsonPath: .spec.details.dataStorageSizeInTBs name: Storage (TB) type: integer @@ -1928,6 +1928,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -2316,7 +2318,7 @@ spec: proxyFromEnvironment: type: boolean proxyUrl: - pattern: ^http(s)?://.+$ + pattern: ^(http|https|socks5)://.+$ type: string scopes: items: @@ -3629,6 +3631,8 @@ spec: - port type: object type: object + stopSignal: + type: string type: object livenessProbe: properties: @@ -4251,6 +4255,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -4639,7 +4645,7 @@ spec: proxyFromEnvironment: type: boolean proxyUrl: - pattern: ^http(s)?://.+$ + pattern: ^(http|https|socks5)://.+$ type: string scopes: items: @@ -5952,6 +5958,8 @@ spec: - port type: object type: object + stopSignal: + type: string type: object livenessProbe: properties: @@ -6574,6 +6582,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -6962,7 +6972,7 @@ spec: proxyFromEnvironment: type: boolean proxyUrl: - pattern: ^http(s)?://.+$ + pattern: ^(http|https|socks5)://.+$ type: string scopes: items: @@ -8275,6 +8285,8 @@ spec: - port type: object type: object + stopSignal: + type: string type: object livenessProbe: properties: @@ -9945,6 +9957,8 @@ spec: type: object spec: properties: + autodiscover: + type: boolean cdbAdminPwd: properties: secret: @@ -10007,6 +10021,21 @@ spec: required: - secret type: object + cdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object cdbTlsCrt: properties: secret: @@ -10037,6 +10066,9 @@ spec: required: - secret type: object + clusterIp: + default: false + type: boolean dbPort: type: integer dbServer: @@ -10045,6 +10077,9 @@ spec: type: string deletePdbCascade: type: boolean + loadBalancer: + default: false + type: boolean lrestImage: type: string lrestImagePullPolicy: @@ -10055,6 +10090,7 @@ spec: lrestImagePullSecret: type: string lrestPort: + default: 8888 type: integer lrestPwd: properties: @@ -10071,12 +10107,16 @@ spec: required: - secret type: object + namespaceAutoDiscover: + type: string nodeSelector: additionalProperties: type: string type: object replicas: type: integer + serviceAccountName: + type: string serviceName: type: string sysAdminPwd: @@ -10094,6 +10134,9 @@ spec: required: - secret type: object + trace_level_client: + default: 0 + type: integer webServerPwd: properties: secret: @@ -10176,18 +10219,26 @@ spec: jsonPath: .status.totalSize name: PDB Size type: string - - description: Status of the LRPDB Resource - jsonPath: .status.phase - name: Status - type: string - description: Error message, if any jsonPath: .status.msg name: Message type: string + - description: open restricted + jsonPath: .status.restricted + name: Restricted + type: string - description: last sqlcode jsonPath: .status.sqlCode name: last sqlcode type: integer + - description: last plsql applied + jsonPath: .status.lastplsql + name: last PLSQL + type: string + - description: Bitmask status + jsonPath: .status.pdbBitMaskStr + name: BITMASK STATUS + type: string - description: The connect string to be used jsonPath: .status.connString name: Connect_String @@ -10205,17 +10256,6 @@ spec: spec: properties: action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - - Alter - - Noaction type: string adminName: properties: @@ -10285,8 +10325,6 @@ spec: type: string asClone: type: boolean - assertiveLrpdbDeletion: - type: boolean cdbName: type: string cdbNamespace: @@ -10308,12 +10346,16 @@ spec: type: object cdbResName: type: string + codeconfigmap: + type: string copyAction: enum: - COPY - NOCOPY - MOVE type: string + debug: + type: integer dropAction: enum: - INCLUDING @@ -10322,6 +10364,9 @@ spec: fileNameConversions: type: string getScript: + default: false + type: boolean + imperativeLrpdbDeletion: type: boolean lrpdbTlsCat: properties: @@ -10376,6 +10421,9 @@ spec: - READ WRITE - RESTRICTED type: string + modifyOption2: + default: NONE + type: string parameterScope: type: string pdbName: @@ -10385,10 +10433,21 @@ spec: - OPEN - CLOSE - ALTER + - DELETE + - UNPLUG + - PLUG + - CLONE + - RESET + - NONE type: string pdbconfigmap: type: string + plsqlexemode: + type: integer + reststate: + type: integer reuseTempFile: + default: true type: boolean sourceFileNameConversions: type: string @@ -10437,6 +10496,7 @@ spec: totalSize: type: string unlimitedStorage: + default: true type: boolean webServerPwd: properties: @@ -10470,11 +10530,6 @@ spec: type: object xmlFileName: type: string - required: - - action - - alterSystemParameter - - alterSystemValue - - webServerPwd type: object status: properties: @@ -10488,14 +10543,22 @@ spec: type: string connString: type: string + lastplsql: + type: string modifyOption: type: string msg: type: string openMode: type: string + pdbBitMask: + type: integer + pdbBitMaskStr: + type: string phase: type: string + restricted: + type: string sqlCode: type: integer status: @@ -11039,6 +11102,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -11798,6 +11863,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -16353,6 +16420,26 @@ webhooks: resources: - autonomousdatabaserestores sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -16709,8 +16796,8 @@ spec: - /manager env: - name: WATCH_NAMESPACE - value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:restart-operator-sa + value: "oracle-database-operator-system,cdbnamespace,pdbnamespace" + image: lin.ocir.io/intsanjaysingh/operator/dboperator_master_250730:latest imagePullPolicy: Always name: manager ports: From f5c0b41e5dfe7442e18dbd29de7c3312950398b3 Mon Sep 17 00:00:00 2001 From: Marco Stefanetti Date: Mon, 11 Aug 2025 09:32:40 +0000 Subject: [PATCH 257/414] OrdsSrvs 2.0 --- Dockerfile | 1 + apis/database/v4/ordssrvs_types.go | 66 ++- apis/database/v4/zz_generated.deepcopy.go | 102 +--- .../bases/database.oracle.com_ordssrvs.yaml | 75 +-- config/manager/kustomization.yaml | 4 +- config/rbac/role.yaml | 2 + controllers/database/ordssrvs_controller.go | 455 ++++++++++----- controllers/database/ordssrvs_ordsconfig.go | 26 +- docs/ordsservices/README.md | 5 +- docs/ordsservices/api.md | 22 +- docs/ordsservices/autoupgrade.md | 21 +- docs/ordsservices/examples/adb.md | 4 +- docs/ordsservices/examples/adb_oraoper.md | 4 +- docs/ordsservices/examples/existing_db.md | 4 +- docs/ordsservices/examples/mongo_api.md | 5 +- docs/ordsservices/examples/multi_pool.md | 7 +- docs/ordsservices/examples/sidb_container.md | 9 +- docs/ordsservices/usecase01/makefile | 11 +- oracle-database-operator.yaml | 81 +-- ords/ords_init.sh | 524 +++++++++++++----- ords/ords_start.sh | 26 + 21 files changed, 966 insertions(+), 488 deletions(-) create mode 100644 ords/ords_start.sh diff --git a/Dockerfile b/Dockerfile index 92952363..c41063c2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,7 @@ ENV COMMIT_SHA=${CI_COMMIT_SHA} \ WORKDIR / COPY --from=builder /workspace/manager . COPY ords/ords_init.sh . +COPY ords/ords_start.sh . RUN useradd -u 1002 nonroot USER nonroot diff --git a/apis/database/v4/ordssrvs_types.go b/apis/database/v4/ordssrvs_types.go index 1fbf820a..d7e5156a 100644 --- a/apis/database/v4/ordssrvs_types.go +++ b/apis/database/v4/ordssrvs_types.go @@ -39,8 +39,6 @@ package v4 import ( - "time" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -74,7 +72,8 @@ type OrdsSrvsSpec struct { EncPrivKey PasswordSecret `json:"encPrivKey,omitempty"` PoolSettings []*PoolSettings `json:"poolSettings,omitempty"` // +k8s:openapi-gen=true - + // ServiceAccount of the OrdsSrvs Pod + ServiceAccountName string `json:"serviceAccountName,omitempty"` } type GlobalSettings struct { @@ -82,15 +81,14 @@ type GlobalSettings struct { CacheMetadataEnabled *bool `json:"cache.metadata.enabled,omitempty"` // Specifies the duration after a GraphQL schema is not accessed from the cache that it expires. - CacheMetadataGraphQLExpireAfterAccess *time.Duration `json:"cache.metadata.graphql.expireAfterAccess,omitempty"` + CacheMetadataGraphQLExpireAfterAccess string `json:"cache.metadata.graphql.expireAfterAccess,omitempty"` // Specifies the duration after a GraphQL schema is cached that it expires and has to be loaded again. - CacheMetadataGraphQLExpireAfterWrite *time.Duration `json:"cache.metadata.graphql.expireAfterWrite,omitempty"` + CacheMetadataGraphQLExpireAfterWrite string `json:"cache.metadata.graphql.expireAfterWrite,omitempty"` // Specifies the setting to determine for how long a metadata record remains in the cache. // Longer duration means, it takes longer to view the applied changes. - // The formats accepted are based on the ISO-8601 duration format. - CacheMetadataTimeout *time.Duration `json:"cache.metadata.timeout,omitempty"` + CacheMetadataTimeout string `json:"cache.metadata.timeout,omitempty"` // Specifies the setting to enable or disable JWKS caching. CacheMetadataJWKSEnabled *bool `json:"cache.metadata.jwks.enabled,omitempty"` @@ -103,10 +101,10 @@ type GlobalSettings struct { // Specifies the duration after a JWK is not accessed from the cache that it expires. // By default this is disabled. - CacheMetadataJWKSExpireAfterAccess *time.Duration `json:"cache.metadata.jwks.expireAfterAccess,omitempty"` + CacheMetadataJWKSExpireAfterAccess string `json:"cache.metadata.jwks.expireAfterAccess,omitempty"` // Specifies the duration after a JWK is cached, that is, it expires and has to be loaded again. - CacheMetadataJWKSExpireAfterWrite *time.Duration `json:"cache.metadata.jwks.expireAfterWrite,omitempty"` + CacheMetadataJWKSExpireAfterWrite string `json:"cache.metadata.jwks.expireAfterWrite,omitempty"` // Specifies whether the Database API is enabled. DatabaseAPIEnabled *bool `json:"database.api.enabled,omitempty"` @@ -116,7 +114,7 @@ type GlobalSettings struct { DatabaseAPIManagementServicesDisabled *bool `json:"database.api.management.services.disabled,omitempty"` // Specifies how long to wait before retrying an invalid pool. - DBInvalidPoolTimeout *time.Duration `json:"db.invalidPoolTimeout,omitempty"` + DBInvalidPoolTimeout string `json:"db.invalidPoolTimeout,omitempty"` // Specifies the maximum join nesting depth limit for GraphQL queries. FeatureGraphQLMaxNestingDepth *int32 `json:"feature.grahpql.max.nesting.depth,omitempty"` @@ -131,7 +129,7 @@ type GlobalSettings struct { SecurityCredentialsAttempts *int32 `json:"security.credentials.attempts,omitempty"` // Specifies the period to lock the account that has exceeded maximum attempts. - SecurityCredentialsLockTime *time.Duration `json:"security.credentials.lock.time,omitempty"` + SecurityCredentialsLockTime string `json:"security.credentials.lock.time,omitempty"` // Specifies the HTTP listen port. //+kubebuilder:default:=8080 @@ -145,7 +143,7 @@ type GlobalSettings struct { StandaloneHTTPSPort *int32 `json:"standalone.https.port,omitempty"` // Specifies the period for Standalone Mode to wait until it is gracefully shutdown. - StandaloneStopTimeout *time.Duration `json:"standalone.stop.timeout,omitempty"` + StandaloneStopTimeout string `json:"standalone.stop.timeout,omitempty"` // Specifies whether to display error messages on the browser. DebugPrintDebugToScreen *bool `json:"debug.printDebugToScreen,omitempty"` @@ -181,10 +179,10 @@ type GlobalSettings struct { MongoPort *int32 `json:"mongo.port,omitempty"` // Specifies the maximum idle time for a Mongo connection in milliseconds. - MongoIdleTimeout *time.Duration `json:"mongo.idle.timeout,omitempty"` + MongoIdleTimeout string `json:"mongo.idle.timeout,omitempty"` // Specifies the maximum time for a Mongo database operation in milliseconds. - MongoOpTimeout *time.Duration `json:"mongo.op.timeout,omitempty"` + MongoOpTimeout string `json:"mongo.op.timeout,omitempty"` // If this value is set to true, then the Oracle REST Data Services internal exclusion list is not enforced. // Oracle recommends that you do not set this value to true. @@ -207,6 +205,19 @@ type GlobalSettings struct { //+kubebuilder:default:="/ords" StandaloneContextPath string `json:"standalone.context.path,omitempty"` + // Specify whether to download APEX installation files + // This setting will be ignored for ADB + //+kubebuilder:default:=false + APEXDownload bool `json:"apex.download,omitempty"` + + // Specify the url to download APEX installation files + // This setting will be ignored for ADB + //+kubebuilder:default:="https://download.oracle.com/otn_software/apex/apex-latest.zip" + APEXDownloadUrl string `json:"apex.download.url,omitempty"` + + // Specify the storage attributes for PersistenceVolume and PersistenceVolumeClaim + APEXInstallationPersistence Persistence `json:"apex.installation.persistence,omitempty"` + /************************************************* * Undocumented /************************************************/ @@ -303,6 +314,19 @@ type GlobalSettings struct { // HARDCODED to global/logs } +// Specify storage attributes of PV and PVC +type Persistence struct { + //+kubebuilder:default="1Gi" + Size string `json:"size,omitempty"` + StorageClass string `json:"storageClass,omitempty"` + //+kubebuilder:validation:Enum=ReadWriteOnce;ReadWriteMany + //+kubebuilder:default=ReadWriteOnce + AccessMode string `json:"accessMode,omitempty"` + VolumeName string `json:"volumeName,omitempty"` + //VolumeClaimAnnotation string `json:"volumeClaimAnnotation,omitempty"` + //SetWritePermissions *bool `json:"setWritePermissions,omitempty"` +} + type PoolSettings struct { // Specifies the Pool Name PoolName string `json:"poolName"` @@ -372,7 +396,7 @@ type PoolSettings struct { DBCredentialsSource string `json:"db.credentialsSource,omitempty"` // Indicates how long to wait to gracefully destroy a pool before moving to forcefully destroy all connections including borrowed ones. - DBPoolDestroyTimeout *time.Duration `json:"db.poolDestroyTimeout,omitempty"` + DBPoolDestroyTimeout string `json:"db.poolDestroyTimeout,omitempty"` // Specifies to enable tracking of JDBC resources. // If not released causes in resource leaks or exhaustion in the database. @@ -411,21 +435,21 @@ type PoolSettings struct { SecurityJWKSSize *int32 `json:"security.jwks.size,omitempty"` // Specifies the maximum amount of time before timing-out when accessing a JWK url. - SecurityJWKSConnectionTimeout *time.Duration `json:"security.jwks.connection.timeout,omitempty"` + SecurityJWKSConnectionTimeout string `json:"security.jwks.connection.timeout,omitempty"` // Specifies the maximum amount of time reading a response from the JWK url before timing-out. - SecurityJWKSReadTimeout *time.Duration `json:"security.jwks.read.timeout,omitempty"` + SecurityJWKSReadTimeout string `json:"security.jwks.read.timeout,omitempty"` // Specifies the minimum interval between refreshing the JWK cached value. - SecurityJWKSRefreshInterval *time.Duration `json:"security.jwks.refresh.interval,omitempty"` + SecurityJWKSRefreshInterval string `json:"security.jwks.refresh.interval,omitempty"` // Specifies the maximum skew the JWT time claims are accepted. // This is useful if the clock on the JWT issuer and ORDS differs by a few seconds. - SecurityJWTAllowedSkew *time.Duration `json:"security.jwt.allowed.skew,omitempty"` + SecurityJWTAllowedSkew string `json:"security.jwt.allowed.skew,omitempty"` // Specifies the maximum allowed age of a JWT in seconds, regardless of expired claim. // The age of the JWT is taken from the JWT issued at claim. - SecurityJWTAllowedAge *time.Duration `json:"security.jwt.allowed.age,omitempty"` + SecurityJWTAllowedAge string `json:"security.jwt.allowed.age,omitempty"` // Indicates the type of security.requestValidationFunction: javascript or plsql. //+kubebuilder:validation:Enum=plsql;javascript @@ -471,7 +495,7 @@ type PoolSettings struct { JDBCMaxConnectionReuseCount *int32 `json:"jdbc.MaxConnectionReuseCount,omitempty"` // Sets the maximum connection reuse time property. - JDBCMaxConnectionReuseTime *int32 `json:"jdbc.MaxConnectionReuseTime,omitempty"` + JDBCMaxConnectionReuseTime string `json:"jdbc.MaxConnectionReuseTime,omitempty"` // Sets the time in seconds to trust an idle connection to skip a validation test. JDBCSecondsToTrustIdleConnection *int32 `json:"jdbc.SecondsToTrustIdleConnection,omitempty"` diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index e1314d38..512ec41b 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -46,7 +46,6 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - timex "time" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -1818,21 +1817,6 @@ func (in *GlobalSettings) DeepCopyInto(out *GlobalSettings) { *out = new(bool) **out = **in } - if in.CacheMetadataGraphQLExpireAfterAccess != nil { - in, out := &in.CacheMetadataGraphQLExpireAfterAccess, &out.CacheMetadataGraphQLExpireAfterAccess - *out = new(timex.Duration) - **out = **in - } - if in.CacheMetadataGraphQLExpireAfterWrite != nil { - in, out := &in.CacheMetadataGraphQLExpireAfterWrite, &out.CacheMetadataGraphQLExpireAfterWrite - *out = new(timex.Duration) - **out = **in - } - if in.CacheMetadataTimeout != nil { - in, out := &in.CacheMetadataTimeout, &out.CacheMetadataTimeout - *out = new(timex.Duration) - **out = **in - } if in.CacheMetadataJWKSEnabled != nil { in, out := &in.CacheMetadataJWKSEnabled, &out.CacheMetadataJWKSEnabled *out = new(bool) @@ -1848,16 +1832,6 @@ func (in *GlobalSettings) DeepCopyInto(out *GlobalSettings) { *out = new(int32) **out = **in } - if in.CacheMetadataJWKSExpireAfterAccess != nil { - in, out := &in.CacheMetadataJWKSExpireAfterAccess, &out.CacheMetadataJWKSExpireAfterAccess - *out = new(timex.Duration) - **out = **in - } - if in.CacheMetadataJWKSExpireAfterWrite != nil { - in, out := &in.CacheMetadataJWKSExpireAfterWrite, &out.CacheMetadataJWKSExpireAfterWrite - *out = new(timex.Duration) - **out = **in - } if in.DatabaseAPIEnabled != nil { in, out := &in.DatabaseAPIEnabled, &out.DatabaseAPIEnabled *out = new(bool) @@ -1868,11 +1842,6 @@ func (in *GlobalSettings) DeepCopyInto(out *GlobalSettings) { *out = new(bool) **out = **in } - if in.DBInvalidPoolTimeout != nil { - in, out := &in.DBInvalidPoolTimeout, &out.DBInvalidPoolTimeout - *out = new(timex.Duration) - **out = **in - } if in.FeatureGraphQLMaxNestingDepth != nil { in, out := &in.FeatureGraphQLMaxNestingDepth, &out.FeatureGraphQLMaxNestingDepth *out = new(int32) @@ -1883,11 +1852,6 @@ func (in *GlobalSettings) DeepCopyInto(out *GlobalSettings) { *out = new(int32) **out = **in } - if in.SecurityCredentialsLockTime != nil { - in, out := &in.SecurityCredentialsLockTime, &out.SecurityCredentialsLockTime - *out = new(timex.Duration) - **out = **in - } if in.StandaloneHTTPPort != nil { in, out := &in.StandaloneHTTPPort, &out.StandaloneHTTPPort *out = new(int32) @@ -1898,11 +1862,6 @@ func (in *GlobalSettings) DeepCopyInto(out *GlobalSettings) { *out = new(int32) **out = **in } - if in.StandaloneStopTimeout != nil { - in, out := &in.StandaloneStopTimeout, &out.StandaloneStopTimeout - *out = new(timex.Duration) - **out = **in - } if in.DebugPrintDebugToScreen != nil { in, out := &in.DebugPrintDebugToScreen, &out.DebugPrintDebugToScreen *out = new(bool) @@ -1923,16 +1882,6 @@ func (in *GlobalSettings) DeepCopyInto(out *GlobalSettings) { *out = new(int32) **out = **in } - if in.MongoIdleTimeout != nil { - in, out := &in.MongoIdleTimeout, &out.MongoIdleTimeout - *out = new(timex.Duration) - **out = **in - } - if in.MongoOpTimeout != nil { - in, out := &in.MongoOpTimeout, &out.MongoOpTimeout - *out = new(timex.Duration) - **out = **in - } if in.SecurityDisableDefaultExclusionList != nil { in, out := &in.SecurityDisableDefaultExclusionList, &out.SecurityDisableDefaultExclusionList *out = new(bool) @@ -1948,6 +1897,7 @@ func (in *GlobalSettings) DeepCopyInto(out *GlobalSettings) { *out = new(bool) **out = **in } + out.APEXInstallationPersistence = in.APEXInstallationPersistence if in.CertSecret != nil { in, out := &in.CertSecret, &out.CertSecret *out = new(CertificateSecret) @@ -4025,17 +3975,27 @@ func (in *PasswordSpec) DeepCopy() *PasswordSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Persistence) DeepCopyInto(out *Persistence) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Persistence. +func (in *Persistence) DeepCopy() *Persistence { + if in == nil { + return nil + } + out := new(Persistence) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PoolSettings) DeepCopyInto(out *PoolSettings) { *out = *in out.DBSecret = in.DBSecret out.DBAdminUserSecret = in.DBAdminUserSecret out.DBCDBAdminUserSecret = in.DBCDBAdminUserSecret - if in.DBPoolDestroyTimeout != nil { - in, out := &in.DBPoolDestroyTimeout, &out.DBPoolDestroyTimeout - *out = new(timex.Duration) - **out = **in - } if in.DebugTrackResources != nil { in, out := &in.DebugTrackResources, &out.DebugTrackResources *out = new(bool) @@ -4066,31 +4026,6 @@ func (in *PoolSettings) DeepCopyInto(out *PoolSettings) { *out = new(int32) **out = **in } - if in.SecurityJWKSConnectionTimeout != nil { - in, out := &in.SecurityJWKSConnectionTimeout, &out.SecurityJWKSConnectionTimeout - *out = new(timex.Duration) - **out = **in - } - if in.SecurityJWKSReadTimeout != nil { - in, out := &in.SecurityJWKSReadTimeout, &out.SecurityJWKSReadTimeout - *out = new(timex.Duration) - **out = **in - } - if in.SecurityJWKSRefreshInterval != nil { - in, out := &in.SecurityJWKSRefreshInterval, &out.SecurityJWKSRefreshInterval - *out = new(timex.Duration) - **out = **in - } - if in.SecurityJWTAllowedSkew != nil { - in, out := &in.SecurityJWTAllowedSkew, &out.SecurityJWTAllowedSkew - *out = new(timex.Duration) - **out = **in - } - if in.SecurityJWTAllowedAge != nil { - in, out := &in.SecurityJWTAllowedAge, &out.SecurityJWTAllowedAge - *out = new(timex.Duration) - **out = **in - } if in.DBPort != nil { in, out := &in.DBPort, &out.DBPort *out = new(int32) @@ -4111,11 +4046,6 @@ func (in *PoolSettings) DeepCopyInto(out *PoolSettings) { *out = new(int32) **out = **in } - if in.JDBCMaxConnectionReuseTime != nil { - in, out := &in.JDBCMaxConnectionReuseTime, &out.JDBCMaxConnectionReuseTime - *out = new(int32) - **out = **in - } if in.JDBCSecondsToTrustIdleConnection != nil { in, out := &in.JDBCSecondsToTrustIdleConnection, &out.JDBCSecondsToTrustIdleConnection *out = new(int32) diff --git a/config/crd/bases/database.oracle.com_ordssrvs.yaml b/config/crd/bases/database.oracle.com_ordssrvs.yaml index 9c4ab88f..ec7ba279 100644 --- a/config/crd/bases/database.oracle.com_ordssrvs.yaml +++ b/config/crd/bases/database.oracle.com_ordssrvs.yaml @@ -68,22 +68,40 @@ spec: type: boolean globalSettings: properties: + apex.download: + default: false + type: boolean + apex.download.url: + default: https://download.oracle.com/otn_software/apex/apex-latest.zip + type: string + apex.installation.persistence: + properties: + accessMode: + default: ReadWriteOnce + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + default: 1Gi + type: string + storageClass: + type: string + volumeName: + type: string + type: object cache.metadata.enabled: type: boolean cache.metadata.graphql.expireAfterAccess: - format: int64 - type: integer + type: string cache.metadata.graphql.expireAfterWrite: - format: int64 - type: integer + type: string cache.metadata.jwks.enabled: type: boolean cache.metadata.jwks.expireAfterAccess: - format: int64 - type: integer + type: string cache.metadata.jwks.expireAfterWrite: - format: int64 - type: integer + type: string cache.metadata.jwks.initialCapacity: format: int32 type: integer @@ -91,8 +109,7 @@ spec: format: int32 type: integer cache.metadata.timeout: - format: int64 - type: integer + type: string certSecret: properties: cert: @@ -111,8 +128,7 @@ spec: database.api.management.services.disabled: type: boolean db.invalidPoolTimeout: - format: int64 - type: integer + type: string debug.printDebugToScreen: type: boolean enable.mongo.access.log: @@ -139,11 +155,9 @@ spec: mongo.enabled: type: boolean mongo.idle.timeout: - format: int64 - type: integer + type: string mongo.op.timeout: - format: int64 - type: integer + type: string mongo.port: default: 27017 format: int32 @@ -154,8 +168,7 @@ spec: format: int32 type: integer security.credentials.lock.time: - format: int64 - type: integer + type: string security.disableDefaultExclusionList: type: boolean security.exclusionList: @@ -187,8 +200,7 @@ spec: format: int32 type: integer standalone.stop.timeout: - format: int64 - type: integer + type: string type: object image: type: string @@ -254,8 +266,7 @@ spec: db.hostname: type: string db.poolDestroyTimeout: - format: int64 - type: integer + type: string db.port: format: int32 type: integer @@ -313,8 +324,7 @@ spec: format: int32 type: integer jdbc.MaxConnectionReuseTime: - format: int32 - type: integer + type: string jdbc.MaxLimit: format: int32 type: integer @@ -360,23 +370,18 @@ spec: restEnabledSql.active: type: boolean security.jwks.connection.timeout: - format: int64 - type: integer + type: string security.jwks.read.timeout: - format: int64 - type: integer + type: string security.jwks.refresh.interval: - format: int64 - type: integer + type: string security.jwks.size: format: int32 type: integer security.jwt.allowed.age: - format: int64 - type: integer + type: string security.jwt.allowed.skew: - format: int64 - type: integer + type: string security.jwt.profile.enabled: type: boolean security.requestAuthenticationFunction: @@ -410,6 +415,8 @@ spec: format: int32 minimum: 1 type: integer + serviceAccountName: + type: string workloadType: default: Deployment enum: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index eb2cad70..15893aad 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lin.ocir.io/intsanjaysingh/operator/dboperator_master_250730 - newTag: latest + newName: lin.ocir.io/intsanjaysingh/marcstef/oo + newTag: 1.2.0.8 diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e1fc03bd..576945f8 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -13,6 +13,7 @@ rules: - endpoints - events - namespaces + - persistentvolumeclaim - persistentvolumeclaims - persistentvolumes - pods @@ -35,6 +36,7 @@ rules: - configmaps/status - daemonsets/status - deployments/status + - persistentvolumeclaim/status - services/status - statefulsets/status verbs: diff --git a/controllers/database/ordssrvs_controller.go b/controllers/database/ordssrvs_controller.go index 14c7f46e..b85b2b60 100644 --- a/controllers/database/ordssrvs_controller.go +++ b/controllers/database/ordssrvs_controller.go @@ -59,6 +59,7 @@ import ( "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -69,23 +70,29 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" // dbapi "example.com/oracle-ords-operator/api/v1" + "github.com/go-logr/logr" dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + "github.com/oracle/oracle-database-operator/commons/k8s" ) // Definitions of Standards const ( - ordsSABase = "/opt/oracle/sa" - serviceHTTPPortName = "svc-http-port" - serviceHTTPSPortName = "svc-https-port" - serviceMongoPortName = "svc-mongo-port" - targetHTTPPortName = "pod-http-port" - targetHTTPSPortName = "pod-https-port" - targetMongoPortName = "pod-mongo-port" - globalConfigMapName = "settings-global" - poolConfigPreName = "settings-" // Append PoolName - controllerLabelKey = "oracle.com/ords-operator-filter" - controllerLabelVal = "oracle-database-operator" - specHashLabel = "oracle.com/ords-operator-spec-hash" + ordsSABase = "/opt/oracle/sa" + serviceHTTPPortName = "svc-http-port" + serviceHTTPSPortName = "svc-https-port" + serviceMongoPortName = "svc-mongo-port" + targetHTTPPortName = "pod-http-port" + targetHTTPSPortName = "pod-https-port" + targetMongoPortName = "pod-mongo-port" + globalConfigMapName = "settings-global" + poolConfigPreName = "settings-" // Append PoolName + controllerLabelKey = "oracle.com/ords-operator-filter" + controllerLabelVal = "oracle-database-operator" + specHashLabel = "oracle.com/ords-operator-spec-hash" + APEXInstallationPV = "apex-installation-pv" + APEXInstallationPVC = "apex-installation-pvc" + APEXInstallationMount = "/opt/oracle/apex" ) // Definitions to manage status conditions @@ -96,6 +103,12 @@ const ( typeUnsyncedORDS = "Unsynced" ) +// Definitions used in the controller +var ordsInitScript string = "" +var ordsStartScript string = "" +var ordsGlobalConfig string = "" +var APEXInstallationExternal string = "false" + // Trigger a restart of Pods on Config Changes var RestartPods bool = false @@ -104,6 +117,7 @@ type OrdsSrvsReconciler struct { client.Client Scheme *runtime.Scheme Recorder record.EventRecorder + Log logr.Logger } //+kubebuilder:rbac:groups=database.oracle.com,resources=ordssrvs,verbs=get;list;watch;create;update;patch;delete @@ -116,6 +130,8 @@ type OrdsSrvsReconciler struct { //+kubebuilder:rbac:groups=core,resources=secrets/status,verbs=get //+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=core,resources=services/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=core,resources=persistentvolumeclaim,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=persistentvolumeclaim/status,verbs=get;update;patch //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=core,resources=deployments/status,verbs=get;update;patch //+kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=get;list;watch;create;update;patch;delete @@ -133,63 +149,92 @@ func (r *OrdsSrvsReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&appsv1.StatefulSet{}). Owns(&appsv1.DaemonSet{}). Owns(&corev1.Service{}). + Owns(&corev1.PersistentVolumeClaim{}). Complete(r) } func (r *OrdsSrvsReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - logr := log.FromContext(ctx) - ords := &dbapi.OrdsSrvs{} + logger := log.FromContext(ctx) + ordssrvs := &dbapi.OrdsSrvs{} // Check if resource exists or was deleted - if err := r.Get(ctx, req.NamespacedName, ords); err != nil { + if err := r.Get(ctx, req.NamespacedName, ordssrvs); err != nil { if apierrors.IsNotFound(err) { - logr.Info("Resource deleted") + logger.Info("Resource deleted") return ctrl.Result{}, nil } - logr.Error(err, "Error retrieving resource") + logger.Error(err, "Error retrieving resource") return ctrl.Result{Requeue: true, RequeueAfter: time.Minute}, err } + ordsInitScript = ordssrvs.Name + "-init-script" + ordsStartScript = ordssrvs.Name + "-start-script" + ordsGlobalConfig = ordssrvs.Name + "-" + globalConfigMapName + + // APEXInstallationExternal + // true: persistence volume + // false: no persistence volume + if ordssrvs.Spec.GlobalSettings.APEXInstallationPersistence.VolumeName != "" || ordssrvs.Spec.GlobalSettings.APEXInstallationPersistence.StorageClass != "" { + APEXInstallationExternal = "true" + } + logger.Info("Setting env external_apex to " + APEXInstallationExternal) + // Set the status as Unknown when no status are available - if ords.Status.Conditions == nil || len(ords.Status.Conditions) == 0 { + if len(ordssrvs.Status.Conditions) == 0 { condition := metav1.Condition{Type: typeUnsyncedORDS, Status: metav1.ConditionUnknown, Reason: "Reconciling", Message: "Starting reconciliation"} - if err := r.SetStatus(ctx, req, ords, condition); err != nil { + if err := r.SetStatus(ctx, req, ordssrvs, condition); err != nil { return ctrl.Result{}, err } } // ConfigMap - Init Script - if err := r.ConfigMapReconcile(ctx, ords, ords.Name+"-"+"init-script", 0); err != nil { - logr.Error(err, "Error in ConfigMapReconcile (init-script)") + if err := r.ConfigMapReconcile(ctx, ordssrvs, ordsInitScript, 0); err != nil { + logger.Error(err, "Error in ConfigMapReconcile (init-script)") return ctrl.Result{}, err } + // ConfigMap - Start Script + if err := r.ConfigMapReconcile(ctx, ordssrvs, ordsStartScript, 0); err != nil { + logger.Error(err, "Error in ConfigMapReconcile (start-script)") + return ctrl.Result{}, err + } + + if APEXInstallationExternal == "true" { + // ApexInstallation PVC + if err := r.ApexInstallationPVCReconcile(ctx, ordssrvs); err != nil { + logger.Error(err, "Error in ApexInstallation PVC reconcile") + return ctrl.Result{}, err + } + } else { + logger.Info("ApexInstallation PVC not defined, no external APEX installation files") + } + // ConfigMap - Global Settings - if err := r.ConfigMapReconcile(ctx, ords, ords.Name+"-"+globalConfigMapName, 0); err != nil { - logr.Error(err, "Error in ConfigMapReconcile (Global)") + if err := r.ConfigMapReconcile(ctx, ordssrvs, ordsGlobalConfig, 0); err != nil { + logger.Error(err, "Error in ConfigMapReconcile (Global)") return ctrl.Result{}, err } // ConfigMap - Pool Settings definedPools := make(map[string]bool) - for i := 0; i < len(ords.Spec.PoolSettings); i++ { - poolName := strings.ToLower(ords.Spec.PoolSettings[i].PoolName) - poolConfigMapName := ords.Name + "-" + poolConfigPreName + poolName + for i := 0; i < len(ordssrvs.Spec.PoolSettings); i++ { + poolName := strings.ToLower(ordssrvs.Spec.PoolSettings[i].PoolName) + poolConfigMapName := ordssrvs.Name + "-" + poolConfigPreName + poolName if definedPools[poolConfigMapName] { return ctrl.Result{}, errors.New("poolName: " + poolName + " is not unique") } definedPools[poolConfigMapName] = true - if err := r.ConfigMapReconcile(ctx, ords, poolConfigMapName, i); err != nil { - logr.Error(err, "Error in ConfigMapReconcile (Pools)") + if err := r.ConfigMapReconcile(ctx, ordssrvs, poolConfigMapName, i); err != nil { + logger.Error(err, "Error in ConfigMapReconcile (Pools)") return ctrl.Result{}, err } } - if err := r.ConfigMapDelete(ctx, req, ords, definedPools); err != nil { - logr.Error(err, "Error in ConfigMapDelete (Pools)") + if err := r.ConfigMapDelete(ctx, req, ordssrvs, definedPools); err != nil { + logger.Error(err, "Error in ConfigMapDelete (Pools)") return ctrl.Result{}, err } - if err := r.Get(ctx, req.NamespacedName, ords); err != nil { - logr.Error(err, "Failed to re-fetch") + if err := r.Get(ctx, req.NamespacedName, ordssrvs); err != nil { + logger.Error(err, "Failed to re-fetch") return ctrl.Result{}, err } @@ -204,40 +249,40 @@ func (r *OrdsSrvsReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // Set the Type as Unsynced when a pod restart is required if RestartPods { condition := metav1.Condition{Type: typeUnsyncedORDS, Status: metav1.ConditionTrue, Reason: "Unsynced", Message: "Configurations have changed"} - if err := r.SetStatus(ctx, req, ords, condition); err != nil { + if err := r.SetStatus(ctx, req, ordssrvs, condition); err != nil { return ctrl.Result{}, err } } // Workloads - if err := r.WorkloadReconcile(ctx, req, ords, ords.Spec.WorkloadType); err != nil { - logr.Error(err, "Error in WorkloadReconcile") + if err := r.WorkloadReconcile(ctx, req, ordssrvs, ordssrvs.Spec.WorkloadType); err != nil { + logger.Error(err, "Error in WorkloadReconcile") return ctrl.Result{}, err } - if err := r.WorkloadDelete(ctx, req, ords, ords.Spec.WorkloadType); err != nil { - logr.Error(err, "Error in WorkloadDelete") + if err := r.WorkloadDelete(ctx, req, ordssrvs, ordssrvs.Spec.WorkloadType); err != nil { + logger.Error(err, "Error in WorkloadDelete") return ctrl.Result{}, err } - if err := r.Get(ctx, req.NamespacedName, ords); err != nil { - logr.Error(err, "Failed to re-fetch") + if err := r.Get(ctx, req.NamespacedName, ordssrvs); err != nil { + logger.Error(err, "Failed to re-fetch") return ctrl.Result{}, err } // Service - if err := r.ServiceReconcile(ctx, ords); err != nil { - logr.Error(err, "Error in ServiceReconcile") + if err := r.ServiceReconcile(ctx, ordssrvs); err != nil { + logger.Error(err, "Error in ServiceReconcile") return ctrl.Result{}, err } // Set the Type as Available when a pod restart is not required if !RestartPods { condition := metav1.Condition{Type: typeAvailableORDS, Status: metav1.ConditionTrue, Reason: "Available", Message: "Workload in Sync"} - if err := r.SetStatus(ctx, req, ords, condition); err != nil { + if err := r.SetStatus(ctx, req, ordssrvs, condition); err != nil { return ctrl.Result{}, err } } - if err := r.Get(ctx, req.NamespacedName, ords); err != nil { - logr.Error(err, "Failed to re-fetch") + if err := r.Get(ctx, req.NamespacedName, ordssrvs); err != nil { + logger.Error(err, "Failed to re-fetch") return ctrl.Result{}, err } @@ -284,12 +329,13 @@ func (r *OrdsSrvsReconciler) SetStatus(ctx context.Context, req ctrl.Request, or } var workloadStatus string - if readyWorkload == 0 { + switch readyWorkload { + case 0: workloadStatus = "Preparing" - } else if readyWorkload == desiredWorkload { + case desiredWorkload: workloadStatus = "Healthy" ords.Status.OrdsInstalled = true - } else { + default: workloadStatus = "Progressing" } @@ -313,25 +359,58 @@ func (r *OrdsSrvsReconciler) SetStatus(ctx context.Context, req ctrl.Request, or return nil } +/************************************************ + * APEX Installation PVC Reconcile + *************************************************/ +func (r *OrdsSrvsReconciler) ApexInstallationPVCReconcile(ctx context.Context, ordssrvs *dbapi.OrdsSrvs) (err error) { + logr := log.FromContext(ctx).WithName("ApexInstallationPVCReconcile") + + if ordssrvs.Spec.GlobalSettings.APEXInstallationPersistence.Size == "" { + msg := "APEX Installation PVC Size not defined" + err = r.Create(ctx, ordssrvs) + logr.Error(err, msg) + return err + } + + pvc := &corev1.PersistentVolumeClaim{} + err = r.Get(ctx, types.NamespacedName{Name: APEXInstallationPVC, Namespace: ordssrvs.Namespace}, pvc) + if err == nil { + logr.Info("Found APEX Installation PVC : " + APEXInstallationPVC) + return nil + } + + volumeName := ordssrvs.Spec.GlobalSettings.APEXInstallationPersistence.VolumeName + pvc = r.APEXInstallationPVCDefine(ctx, ordssrvs) + + message := fmt.Sprintf("APEX Installation PVC : %s for PV : %s", APEXInstallationPVC, volumeName) + logr.Info("Creating " + message) + err = r.Create(ctx, pvc) + if err != nil { + logr.Error(err, "Failed to create "+message) + } + + return err +} + /************************************************ * ConfigMaps *************************************************/ -func (r *OrdsSrvsReconciler) ConfigMapReconcile(ctx context.Context, ords *dbapi.OrdsSrvs, configMapName string, poolIndex int) (err error) { +func (r *OrdsSrvsReconciler) ConfigMapReconcile(ctx context.Context, ordssrvs *dbapi.OrdsSrvs, configMapName string, poolIndex int) (err error) { logr := log.FromContext(ctx).WithName("ConfigMapReconcile") - desiredConfigMap := r.ConfigMapDefine(ctx, ords, configMapName, poolIndex) + desiredConfigMap := r.ConfigMapDefine(ctx, ordssrvs, configMapName, poolIndex) // Create if ConfigMap not found definedConfigMap := &corev1.ConfigMap{} - if err = r.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: ords.Namespace}, definedConfigMap); err != nil { + if err = r.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: ordssrvs.Namespace}, definedConfigMap); err != nil { if apierrors.IsNotFound(err) { if err := r.Create(ctx, desiredConfigMap); err != nil { return err } logr.Info("Created: " + configMapName) RestartPods = true - r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Create", "ConfigMap %s Created", configMapName) + r.Recorder.Eventf(ordssrvs, corev1.EventTypeNormal, "Create", "ConfigMap %s Created", configMapName) // Requery for comparison - if err := r.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: ords.Namespace}, definedConfigMap); err != nil { + if err := r.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: ordssrvs.Namespace}, definedConfigMap); err != nil { return err } } else { @@ -344,7 +423,7 @@ func (r *OrdsSrvsReconciler) ConfigMapReconcile(ctx context.Context, ords *dbapi } logr.Info("Updated: " + configMapName) RestartPods = true - r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Update", "ConfigMap %s Updated", configMapName) + r.Recorder.Eventf(ordssrvs, corev1.EventTypeNormal, "Update", "ConfigMap %s Updated", configMapName) } return nil } @@ -387,22 +466,24 @@ func (r *OrdsSrvsReconciler) ConfigMapReconcile(ctx context.Context, ords *dbapi /************************************************ * Workloads *************************************************/ -func (r *OrdsSrvsReconciler) WorkloadReconcile(ctx context.Context, req ctrl.Request, ords *dbapi.OrdsSrvs, kind string) (err error) { +func (r *OrdsSrvsReconciler) WorkloadReconcile(ctx context.Context, req ctrl.Request, ordssrvs *dbapi.OrdsSrvs, kind string) (err error) { logr := log.FromContext(ctx).WithName("WorkloadReconcile") - objectMeta := objectMetaDefine(ords, ords.Name) - selector := selectorDefine(ords) - template := r.podTemplateSpecDefine(ords, ctx, req) + objectMeta := objectMetaDefine(ordssrvs, ordssrvs.Name) + selector := selectorDefine(ordssrvs) + template := r.podTemplateSpecDefine(ordssrvs, ctx, req) var desiredWorkload client.Object var desiredSpecHash string var definedSpecHash string + var ProgressDeadlineSeconds int32 = 3600 + switch kind { case "StatefulSet": desiredWorkload = &appsv1.StatefulSet{ ObjectMeta: objectMeta, Spec: appsv1.StatefulSetSpec{ - Replicas: &ords.Spec.Replicas, + Replicas: &ordssrvs.Spec.Replicas, Selector: &selector, Template: template, }, @@ -423,37 +504,38 @@ func (r *OrdsSrvsReconciler) WorkloadReconcile(ctx context.Context, req ctrl.Req desiredWorkload = &appsv1.Deployment{ ObjectMeta: objectMeta, Spec: appsv1.DeploymentSpec{ - Replicas: &ords.Spec.Replicas, - Selector: &selector, - Template: template, + Replicas: &ordssrvs.Spec.Replicas, + Selector: &selector, + Template: template, + ProgressDeadlineSeconds: &ProgressDeadlineSeconds, }, } desiredSpecHash = generateSpecHash(desiredWorkload.(*appsv1.Deployment).Spec) desiredWorkload.(*appsv1.Deployment).ObjectMeta.Labels[specHashLabel] = desiredSpecHash } - if err := ctrl.SetControllerReference(ords, desiredWorkload, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(ordssrvs, desiredWorkload, r.Scheme); err != nil { return err } definedWorkload := reflect.New(reflect.TypeOf(desiredWorkload).Elem()).Interface().(client.Object) - if err = r.Get(ctx, types.NamespacedName{Name: ords.Name, Namespace: ords.Namespace}, definedWorkload); err != nil { + if err = r.Get(ctx, types.NamespacedName{Name: ordssrvs.Name, Namespace: ordssrvs.Namespace}, definedWorkload); err != nil { if apierrors.IsNotFound(err) { if err := r.Create(ctx, desiredWorkload); err != nil { condition := metav1.Condition{ Type: typeAvailableORDS, Status: metav1.ConditionFalse, Reason: "Reconciling", - Message: fmt.Sprintf("Failed to create %s for the custom resource (%s): (%s)", kind, ords.Name, err), + Message: fmt.Sprintf("Failed to create %s for the custom resource (%s): (%s)", kind, ordssrvs.Name, err), } - if statusErr := r.SetStatus(ctx, req, ords, condition); statusErr != nil { + if statusErr := r.SetStatus(ctx, req, ordssrvs, condition); statusErr != nil { return statusErr } return err } logr.Info("Created: " + kind) RestartPods = false - r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Create", "Created %s", kind) + r.Recorder.Eventf(ordssrvs, corev1.EventTypeNormal, "Create", "Created %s", kind) return nil } else { @@ -477,10 +559,10 @@ func (r *OrdsSrvsReconciler) WorkloadReconcile(ctx context.Context, req ctrl.Req return err } RestartPods = true - r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Update", "Updated %s", kind) + r.Recorder.Eventf(ordssrvs, corev1.EventTypeNormal, "Update", "Updated %s", kind) } - if RestartPods && ords.Spec.ForceRestart { + if RestartPods && ordssrvs.Spec.ForceRestart { logr.Info("Cycling: " + kind) labelsField := reflect.ValueOf(desiredWorkload).Elem().FieldByName("Spec").FieldByName("Template").FieldByName("ObjectMeta").FieldByName("Labels") if labelsField.IsValid() { @@ -490,7 +572,7 @@ func (r *OrdsSrvsReconciler) WorkloadReconcile(ctx context.Context, req ctrl.Req if err := r.Update(ctx, desiredWorkload); err != nil { return err } - r.Recorder.Eventf(ords, corev1.EventTypeNormal, "Restart", "Restarted %s", kind) + r.Recorder.Eventf(ordssrvs, corev1.EventTypeNormal, "Restart", "Restarted %s", kind) RestartPods = false } } @@ -588,9 +670,9 @@ func selectorDefine(ords *dbapi.OrdsSrvs) metav1.LabelSelector { } } -func (r *OrdsSrvsReconciler) podTemplateSpecDefine(ords *dbapi.OrdsSrvs, ctx context.Context, req ctrl.Request) corev1.PodTemplateSpec { +func (r *OrdsSrvsReconciler) podTemplateSpecDefine(ords *dbapi.OrdsSrvs, ctx context.Context, _ ctrl.Request) corev1.PodTemplateSpec { labels := getLabels(ords.Name) - specVolumes, specVolumeMounts := VolumesDefine(ords) + specVolumes, specVolumeMounts := VolumesDefine(ctx, ords) envPorts := []corev1.ContainerPort{ { @@ -611,27 +693,20 @@ func (r *OrdsSrvsReconciler) podTemplateSpecDefine(ords *dbapi.OrdsSrvs, ctx con envPorts = append(envPorts, mongoPort) } - // Environment From Source podSpecTemplate := corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, }, Spec: corev1.PodSpec{ - Volumes: specVolumes, - SecurityContext: &corev1.PodSecurityContext{ - RunAsNonRoot: &[]bool{true}[0], - FSGroup: &[]int64{54321}[0], - SeccompProfile: &corev1.SeccompProfile{ - Type: corev1.SeccompProfileTypeRuntimeDefault, - }, - }, + Volumes: specVolumes, + SecurityContext: podSecurityContextDefine(), InitContainers: []corev1.Container{{ Image: ords.Spec.Image, Name: ords.Name + "-init", ImagePullPolicy: corev1.PullIfNotPresent, SecurityContext: securityContextDefine(), - Command: []string{"sh", "-c", ordsSABase + "/bin/init_script.sh"}, + Command: []string{"/bin/bash", "-c", ordsSABase + "/init/ords_init.sh"}, Env: r.envDefine(ords, true, ctx), VolumeMounts: specVolumeMounts, }}, @@ -641,28 +716,44 @@ func (r *OrdsSrvsReconciler) podTemplateSpecDefine(ords *dbapi.OrdsSrvs, ctx con ImagePullPolicy: corev1.PullIfNotPresent, SecurityContext: securityContextDefine(), Ports: envPorts, - Command: []string{"/bin/bash", "-c", "ords --config $ORDS_CONFIG serve --apex-images /opt/oracle/apex/$APEX_VER/images --debug"}, + Command: []string{"/bin/bash", "-c", ordsSABase + "/start/ords_start.sh"}, Env: r.envDefine(ords, false, ctx), VolumeMounts: specVolumeMounts, - }}}, + }}, + ServiceAccountName: ords.Spec.ServiceAccountName, + }, } return podSpecTemplate } // Volumes -func VolumesDefine(ords *dbapi.OrdsSrvs) ([]corev1.Volume, []corev1.VolumeMount) { +func VolumesDefine(ctx context.Context, ords *dbapi.OrdsSrvs) ([]corev1.Volume, []corev1.VolumeMount) { // Initialize the slice to hold specifications var volumes []corev1.Volume var volumeMounts []corev1.VolumeMount // SecretHelper - secretHelperVolume := volumeBuild(ords.Name+"-"+"init-script", "ConfigMap", 0770) - secretHelperVolumeMount := volumeMountBuild(ords.Name+"-"+"init-script", ordsSABase+"/bin", true) - + secretHelperVolume := volumeBuild(ordsInitScript, "ConfigMap", 0770) + secretHelperVolumeMount := volumeMountBuild(ordsInitScript, ordsSABase+"/init", true) volumes = append(volumes, secretHelperVolume) volumeMounts = append(volumeMounts, secretHelperVolumeMount) + // start-script + startScriptVolume := volumeBuild(ordsStartScript, "ConfigMap", 0770) + startScriptVolumeMount := volumeMountBuild(ordsStartScript, ordsSABase+"/start", true) + volumes = append(volumes, startScriptVolume) + volumeMounts = append(volumeMounts, startScriptVolumeMount) + + if APEXInstallationExternal == "true" { + // volume for APEX installation, same optional folder as for ORDS image + apexInstallationVolume := APEXInstallationVolumeDefine(ctx, ords) + apexInstallationReadOnly := false + apexInstallationVolumeMount := volumeMountBuild(APEXInstallationPV, APEXInstallationMount, apexInstallationReadOnly) + volumes = append(volumes, apexInstallationVolume) + volumeMounts = append(volumeMounts, apexInstallationVolumeMount) + } + // Build volume specifications for globalSettings standaloneVolume := volumeBuild("standalone", "EmptyDir") standaloneVolumeMount := volumeMountBuild("standalone", ordsSABase+"/config/global/standalone/", false) @@ -673,8 +764,8 @@ func VolumesDefine(ords *dbapi.OrdsSrvs) ([]corev1.Volume, []corev1.VolumeMount) globalLogVolume := volumeBuild("sa-log-global", "EmptyDir") globalLogVolumeMount := volumeMountBuild("sa-log-global", ordsSABase+"/log/global/", false) - globalConfigVolume := volumeBuild(ords.Name+"-"+globalConfigMapName, "ConfigMap") - globalConfigVolumeMount := volumeMountBuild(ords.Name+"-"+globalConfigMapName, ordsSABase+"/config/global/", true) + globalConfigVolume := volumeBuild(ordsGlobalConfig, "ConfigMap") + globalConfigVolumeMount := volumeMountBuild(ordsGlobalConfig, ordsSABase+"/config/global/", true) globalDocRootVolume := volumeBuild("sa-doc-root", "EmptyDir") globalDocRootVolumeMount := volumeMountBuild("sa-doc-root", ordsSABase+"/config/global/doc_root/", false) @@ -826,81 +917,165 @@ func (r *OrdsSrvsReconciler) ServiceDefine(ctx context.Context, ords *dbapi.Ords return def } +func podSecurityContextDefine() *corev1.PodSecurityContext { + + return &corev1.PodSecurityContext{ + RunAsNonRoot: k8s.BoolPointer(true), + RunAsUser: k8s.Int64Pointer(dbcommons.ORACLE_UID), + RunAsGroup: k8s.Int64Pointer(dbcommons.ORACLE_GUID), + FSGroup: k8s.Int64Pointer(dbcommons.ORACLE_GUID), + } + +} + func securityContextDefine() *corev1.SecurityContext { + return &corev1.SecurityContext{ - RunAsNonRoot: &[]bool{true}[0], - RunAsUser: &[]int64{54321}[0], - AllowPrivilegeEscalation: &[]bool{false}[0], + RunAsNonRoot: k8s.BoolPointer(true), + RunAsUser: k8s.Int64Pointer(dbcommons.ORACLE_UID), + RunAsGroup: k8s.Int64Pointer(dbcommons.ORACLE_GUID), + AllowPrivilegeEscalation: k8s.BoolPointer(false), Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{ "ALL", }, }, } + } -func (r *OrdsSrvsReconciler) envDefine(ords *dbapi.OrdsSrvs, initContainer bool, ctx context.Context) []corev1.EnvVar { - envVarSecrets := []corev1.EnvVar{ - { - Name: "ORDS_CONFIG", - Value: ordsSABase + "/config", - }, - { - Name: "JAVA_TOOL_OPTIONS", - Value: "-Doracle.ml.version_check=false", - }, +func addEnvVar(envVars []corev1.EnvVar, name string, value string) []corev1.EnvVar { + newEnvVar := corev1.EnvVar{ + Name: name, + Value: value, } + return append(envVars, newEnvVar) +} + +// Sets environment variables in the containers +func (r *OrdsSrvsReconciler) envDefine(ordssrvs *dbapi.OrdsSrvs, initContainer bool, ctx context.Context) []corev1.EnvVar { + logger := log.FromContext(ctx).WithName("envDefine") + + envVars := []corev1.EnvVar{} + + // ORDS_CONFIG + ORDS_CONFIG := ordsSABase + "/config" + logger.Info("Setting ORDS_CONFIG to " + ORDS_CONFIG) + envVars = addEnvVar(envVars, "ORDS_CONFIG", ORDS_CONFIG) + + // avoid Java warning about JAVA_TOOL_OPTIONS + envVars = addEnvVar(envVars, "JAVA_TOOL_OPTIONS", "-Doracle.ml.version_check=false") // Limitation case for ADB/mTLS/OraOper edge - if len(ords.Spec.PoolSettings) == 1 { - poolName := strings.ToLower(ords.Spec.PoolSettings[0].PoolName) - tnsAdmin := corev1.EnvVar{ - Name: "TNS_ADMIN", - Value: ordsSABase + "/config/databases/" + poolName + "/network/admin/", - } - envVarSecrets = append(envVarSecrets, tnsAdmin) + if len(ordssrvs.Spec.PoolSettings) == 1 { + poolName := strings.ToLower(ordssrvs.Spec.PoolSettings[0].PoolName) + tnsAdmin := ordsSABase + "/config/databases/" + poolName + "/network/admin/" + envVars = addEnvVar(envVars, "TNS_ADMIN", tnsAdmin) } + + // passwords are set for the init container only if initContainer { - for i := 0; i < len(ords.Spec.PoolSettings); i++ { - poolName := strings.ReplaceAll(strings.ToLower(ords.Spec.PoolSettings[i].PoolName), "-", "_") + envVars = addEnvVar(envVars, "download_apex", strconv.FormatBool(ordssrvs.Spec.GlobalSettings.APEXDownload)) + envVars = addEnvVar(envVars, "download_url_apex", ordssrvs.Spec.GlobalSettings.APEXDownloadUrl) + envVars = addEnvVar(envVars, "external_apex", APEXInstallationExternal) + + for i := 0; i < len(ordssrvs.Spec.PoolSettings); i++ { + poolName := strings.ReplaceAll(strings.ToLower(ordssrvs.Spec.PoolSettings[i].PoolName), "-", "_") + + // dbpassword + secretName := poolName + "_dbpassword" + secretValue := r.CommonDecryptWithPrivKey3(ordssrvs, ordssrvs.Spec.PoolSettings[i].DBSecret.SecretName, ordssrvs.Spec.PoolSettings[i].DBSecret.PasswordKey, ctx) + envVars = addEnvVar(envVars, secretName, secretValue) + + // dbadminuserpassword + if ordssrvs.Spec.PoolSettings[i].DBAdminUserSecret.SecretName != "" { + envVars = addEnvVar(envVars, poolName+"_autoupgrade_ords", strconv.FormatBool(ordssrvs.Spec.PoolSettings[i].AutoUpgradeORDS)) + envVars = addEnvVar(envVars, poolName+"_autoupgrade_apex", strconv.FormatBool(ordssrvs.Spec.PoolSettings[i].AutoUpgradeAPEX)) + dbAdminUserPassword := r.CommonDecryptWithPrivKey3(ordssrvs, ordssrvs.Spec.PoolSettings[i].DBAdminUserSecret.SecretName, ordssrvs.Spec.PoolSettings[i].DBAdminUserSecret.PasswordKey, ctx) + envVars = addEnvVar(envVars, poolName+"_dbadminuserpassword", dbAdminUserPassword) + } - dbSecret := corev1.EnvVar{ - Name: poolName + "_dbsecret", - Value: r.CommonDecryptWithPrivKey3(ords, ords.Spec.PoolSettings[i].DBSecret.SecretName, ords.Spec.PoolSettings[i].DBSecret.PasswordKey, ctx), + // dbcdbadminuserpassword + if ordssrvs.Spec.PoolSettings[i].DBCDBAdminUserSecret.SecretName != "" { + dbCDBAdminUserPassword := r.CommonDecryptWithPrivKey3(ordssrvs, ordssrvs.Spec.PoolSettings[i].DBCDBAdminUserSecret.SecretName, ordssrvs.Spec.PoolSettings[i].DBCDBAdminUserSecret.PasswordKey, ctx) + envVars = addEnvVar(envVars, poolName+"_dbcdbadminuserpassword", dbCDBAdminUserPassword) } + } + } - envVarSecrets = append(envVarSecrets, dbSecret) + return envVars +} - if ords.Spec.PoolSettings[i].DBAdminUserSecret.SecretName != "" { - autoUpgradeORDSEnv := corev1.EnvVar{ - Name: poolName + "_autoupgrade_ords", - Value: strconv.FormatBool(ords.Spec.PoolSettings[i].AutoUpgradeORDS), - } - autoUpgradeAPEXEnv := corev1.EnvVar{ - Name: poolName + "_autoupgrade_apex", - Value: strconv.FormatBool(ords.Spec.PoolSettings[i].AutoUpgradeAPEX), - } +func APEXInstallationVolumeDefine(ctx context.Context, ordssrvs *dbapi.OrdsSrvs) corev1.Volume { + logger := log.FromContext(ctx).WithName("APEXInstallationVolumeDefine") - dbAdminUserSecret := corev1.EnvVar{ - Name: poolName + "_dbadminusersecret", - Value: r.CommonDecryptWithPrivKey3(ords, ords.Spec.PoolSettings[i].DBAdminUserSecret.SecretName, ords.Spec.PoolSettings[i].DBAdminUserSecret.PasswordKey, ctx), - } - envVarSecrets = append(envVarSecrets, dbAdminUserSecret, autoUpgradeORDSEnv, autoUpgradeAPEXEnv) - } + var vs corev1.VolumeSource + if APEXInstallationExternal == "false" { + vs = corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + } + logger.Info("APEX installation on empty dir") + } else { - if ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.SecretName != "" { + vs = corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: APEXInstallationPVC, + ReadOnly: false, + }, + } + logger.Info("APEX installation PVC : " + APEXInstallationPVC) + } - dbCDBAdminUserSecret := corev1.EnvVar{ - Name: poolName + "_dbcdbadminusersecret", - Value: r.CommonDecryptWithPrivKey3(ords, ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.SecretName, ords.Spec.PoolSettings[i].DBCDBAdminUserSecret.PasswordKey, ctx), - } + volume := corev1.Volume{ + Name: APEXInstallationPV, + VolumeSource: vs, + } + + return volume +} + +func (r *OrdsSrvsReconciler) APEXInstallationPVCDefine(ctx context.Context, ordssrvs *dbapi.OrdsSrvs) *corev1.PersistentVolumeClaim { + logger := log.FromContext(ctx).WithName("APEXInstallationPVCDefine") + + size := ordssrvs.Spec.GlobalSettings.APEXInstallationPersistence.Size + + if size == "" { - envVarSecrets = append(envVarSecrets, dbCDBAdminUserSecret) - } - } } - return envVarSecrets + volumeName := ordssrvs.Spec.GlobalSettings.APEXInstallationPersistence.VolumeName + storageClassName := ordssrvs.Spec.GlobalSettings.APEXInstallationPersistence.StorageClass + accessMode := ordssrvs.Spec.GlobalSettings.APEXInstallationPersistence.AccessMode + + message := fmt.Sprintf("Preparing PVC definition, volumeName %s, storageClass %s, size %s, accessMode %s", volumeName, storageClassName, size, accessMode) + logger.Info(message) + + // PVC Definition + pvc := &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: APEXInstallationPVC, + Namespace: ordssrvs.Namespace, + Labels: getLabels(ordssrvs.Name), + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.PersistentVolumeAccessMode(accessMode)}, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(size), + }, + }, + VolumeName: volumeName, + StorageClassName: &storageClassName, + }, + } + + // Set the ownerRef + if err := ctrl.SetControllerReference(ordssrvs, pvc, r.Scheme); err != nil { + return nil + } + + return pvc + } /************************************************* @@ -918,7 +1093,7 @@ func (r *OrdsSrvsReconciler) ConfigMapDelete(ctx context.Context, req ctrl.Reque } for _, configMap := range configMapList.Items { - if configMap.Name == ords.Name+"-"+globalConfigMapName || configMap.Name == ords.Name+"-init-script" { + if configMap.Name == ordsGlobalConfig || configMap.Name == ordsInitScript || configMap.Name == ordsStartScript { continue } if _, exists := definedPools[configMap.Name]; !exists { diff --git a/controllers/database/ordssrvs_ordsconfig.go b/controllers/database/ordssrvs_ordsconfig.go index edb2e0f6..a078fec7 100644 --- a/controllers/database/ordssrvs_ordsconfig.go +++ b/controllers/database/ordssrvs_ordsconfig.go @@ -49,20 +49,38 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" ) func (r *OrdsSrvsReconciler) ConfigMapDefine(ctx context.Context, ords *dbapi.OrdsSrvs, configMapName string, poolIndex int) *corev1.ConfigMap { + + log := ctrllog.FromContext(ctx).WithName("ConfigMapDefine") + var defData map[string]string - if configMapName == ords.Name+"-init-script" { + switch configMapName { + case ords.Name + "-init-script": // Read the file from controller's filesystem filePath := "/ords_init.sh" scriptData, err := os.ReadFile(filePath) if err != nil { + log.Error(err, "Error reading /ords_init.sh") return nil } + log.Info("adding ords_init.sh") defData = map[string]string{ - "init_script.sh": string(scriptData)} - } else if configMapName == ords.Name+"-"+globalConfigMapName { + "ords_init.sh": string(scriptData)} + case ords.Name + "-start-script": + // Read the file from controller's filesystem + filePath := "/ords_start.sh" + scriptData, err := os.ReadFile(filePath) + if err != nil { + log.Error(err, "Error reading /ords_start.sh") + return nil + } + log.Info("adding ords_start.sh") + defData = map[string]string{ + "ords_start.sh": string(scriptData)} + case ords.Name + "-" + globalConfigMapName: // GlobalConfigMap var defStandaloneAccessLog string if ords.Spec.GlobalSettings.EnableStandaloneAccessLog { @@ -139,7 +157,7 @@ func (r *OrdsSrvsReconciler) ConfigMapDefine(ctx context.Context, ords *dbapi.Or `java.util.logging.FileHandler.pattern = ` + ordsSABase + `/log/global/debug.log` + "\n" + `java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter`), } - } else { + default: // PoolConfigMap poolName := strings.ToLower(ords.Spec.PoolSettings[poolIndex].PoolName) var defDBNetworkPath string diff --git a/docs/ordsservices/README.md b/docs/ordsservices/README.md index e2fa97be..3117eb00 100644 --- a/docs/ordsservices/README.md +++ b/docs/ordsservices/README.md @@ -25,8 +25,7 @@ It supports the majority of ORDS configuration settings as per the [API Document The ORDS and APEX schemas can be [automatically installed/upgraded](./autoupgrade.md) into the Oracle Database by the ORDS controller. ORDS Version support: -* 24.1.1 -(Newer versions of ORDS will be supported in the next update of OraOperator) +* 25.1.0+ Oracle Database Version: * 19c @@ -49,7 +48,7 @@ Oracle Database Version: kubectl create namespace ordsnamespace ``` - Apply namespace role binding [ordsnamespace-role-binding.yaml](./examples/ordsnamespace-role-binding.yaml): + Apply namespace role binding [ordsnamespace-role-binding.yaml](./ordsnamespace-role-binding.yaml): ```bash kubectl apply -f ordsnamespace-role-binding.yaml ``` diff --git a/docs/ordsservices/api.md b/docs/ordsservices/api.md index da4db09c..4268e98c 100644 --- a/docs/ordsservices/api.md +++ b/docs/ordsservices/api.md @@ -291,14 +291,32 @@ Contains settings that are configured across the entire ORDS instance. Format: int64
false - + + debug.printDebugToScreen boolean Specifies whether to display error messages on the browser.
false - + + + downloadAPEX + boolean + + Specifies whether to downloan APEX installation files.
+ + false + + + downloadUrlAPEX + string + + Specifies the URL to downloan APEX installation files.
+ + https://download.oracle.com/otn_software/apex/apex-latest.zip + + enable.mongo.access.log boolean diff --git a/docs/ordsservices/autoupgrade.md b/docs/ordsservices/autoupgrade.md index fddc30b3..7f99f349 100644 --- a/docs/ordsservices/autoupgrade.md +++ b/docs/ordsservices/autoupgrade.md @@ -1,10 +1,13 @@ # AutoUpgrade Each pool can be configured to automatically install and upgrade the ORDS and/or APEX schemas in the database. -The ORDS and APEX version is based on the ORDS image used for the RestDataServices resource. +The ORDS version is based on the ORDS image used for the RestDataServices resource. +To get the APEX installation files, you can choose to download them from the latest version available or use the provided URL. For example, in the below manifest: -* `Pool: pdb1` is configured to automatically install/ugrade both ORDS and APEX to version 24.1.0 +* APEX installation files will be downloaded from latest version. +* `Pool: pdb1` is configured to automatically install/ugrade both ORDS and APEX to version 25.1.0 +* `Pool: pdb2` will install or upgrade ORDS * `Pool: pdb2` will not install or upgrade ORDS/APEX As an additional requirement for `Pool: pdb1`, the `spec.poolSettings.db.adminUser` and `spec.poolSettings.db.adminUser.secret` @@ -16,10 +19,12 @@ kind: OrdsSrvs metadata: name: ordspoc-server spec: - image: container-registry.oracle.com/database/ords:24.1.0 + image: container-registry.oracle.com/database/ords:25.1.0 forceRestart: true globalSettings: database.api.enabled: true + downloadAPEX : true + downloadUrlAPEX : https://download.oracle.com/otn_software/apex/apex_24.2.zip encPrivKey: secretName: prvkey passwordKey: privateKey @@ -35,12 +40,22 @@ spec: db.adminUser.secret: secretName: pdb1-sys-auth-enc - poolName: pdb2 + autoUpgradeORDS: true db.connectionType: customurl db.customURL: jdbc:oracle:thin:@//localhost:1521/PDB2 db.secret: secretName: pdb2-ords-auth-enc + - poolName: pdb3 + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//localhost:1521/PDB3 + db.secret: + secretName: pdb3-ords-auth-enc ``` +If you don't specify a download URL (downloadUrlAPEX), the default value will be used: +https://download.oracle.com/otn_software/apex/apex-latest.zip + + ## Minimum Privileges for Admin User The `db.adminUser` must have privileges to create users and objects in the database. For Oracle Autonomous Database (ADB), this could be `ADMIN` while for diff --git a/docs/ordsservices/examples/adb.md b/docs/ordsservices/examples/adb.md index 90a21b5c..076a2a8e 100644 --- a/docs/ordsservices/examples/adb.md +++ b/docs/ordsservices/examples/adb.md @@ -46,7 +46,7 @@ rm adb-db-auth-enc e_adb-db-auth-enc name: ords-adb namespace: ordsnamespace spec: - image: container-registry.oracle.com/database/ords:24.1.1 + image: container-registry.oracle.com/database/ords:25.1.0 forceRestart: true encPrivKey: secretName: prvkey @@ -71,7 +71,7 @@ rm adb-db-auth-enc e_adb-db-auth-enc db.adminUser.secret: secretName: adb-oraoper-db-auth-enc ``` - latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + latest container-registry.oracle.com/database/ords version, **25.1.0**, valid as of **26-May-2025** 1. Watch the restdataservices resource until the status is **Healthy**: ```bash diff --git a/docs/ordsservices/examples/adb_oraoper.md b/docs/ordsservices/examples/adb_oraoper.md index 253365c5..718afa11 100644 --- a/docs/ordsservices/examples/adb_oraoper.md +++ b/docs/ordsservices/examples/adb_oraoper.md @@ -103,7 +103,7 @@ rm adb-db-auth-enc e_adb-db-auth-enc name: ords-adb-oraoper namespace: ordsnamespace spec: - image: container-registry.oracle.com/database/ords:24.1.1 + image: container-registry.oracle.com/database/ords:25.1.0 forceRestart: true encPrivKey: secretName: prvkey @@ -126,7 +126,7 @@ rm adb-db-auth-enc e_adb-db-auth-enc db.adminUser.secret: secretName: adb-oraoper-db-auth-enc ``` - latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + latest container-registry.oracle.com/database/ords version, **25.1.0**, valid as of **26-May-2025** 1. Watch the ordssrvs resource until the status is **Healthy**: ```bash diff --git a/docs/ordsservices/examples/existing_db.md b/docs/ordsservices/examples/existing_db.md index 6d4791ae..a4e6511f 100644 --- a/docs/ordsservices/examples/existing_db.md +++ b/docs/ordsservices/examples/existing_db.md @@ -49,7 +49,7 @@ rm db-auth e_db-auth-enc name: ords-db namespace: ordsnamespace spec: - image: container-registry.oracle.com/database/ords:24.1.1 + image: container-registry.oracle.com/database/ords:25.1.0 forceRestart: true encPrivKey: secretName: prvkey @@ -74,7 +74,7 @@ rm db-auth e_db-auth-enc kubectl apply -f ords-db.yaml ``` - latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + latest container-registry.oracle.com/database/ords version, **25.1.0**, valid as of **26-May-2025** 1. Watch the restdataservices resource until the status is **Healthy**: ```bash diff --git a/docs/ordsservices/examples/mongo_api.md b/docs/ordsservices/examples/mongo_api.md index f0fd0cf5..87f3154b 100644 --- a/docs/ordsservices/examples/mongo_api.md +++ b/docs/ordsservices/examples/mongo_api.md @@ -74,7 +74,7 @@ rm sidb-db-auth-enc e_sidb-db-auth-enc name: ords-sidb namespace: ordsnamespace spec: - image: container-registry.oracle.com/database/ords:24.1.1 + image: container-registry.oracle.com/database/ords:25.1.0 forceRestart: true encPrivKey: secretName: prvkey @@ -101,7 +101,8 @@ rm sidb-db-auth-enc e_sidb-db-auth-enc db.adminUser.secret: secretName: sidb-db-auth-enc" | kubectl apply -f - ``` - latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + latest container-registry.oracle.com/database/ords version, **25.1.0**, valid as of **26-May-2025** + 1. Watch the restdataservices resource until the status is **Healthy**: ```bash diff --git a/docs/ordsservices/examples/multi_pool.md b/docs/ordsservices/examples/multi_pool.md index ffb537bf..ba77093d 100644 --- a/docs/ordsservices/examples/multi_pool.md +++ b/docs/ordsservices/examples/multi_pool.md @@ -102,7 +102,7 @@ rm syspwdfile e_syspwdfile name: ords-multi-pool namespace: ordsnamespace spec: - image: container-registry.oracle.com/database/ords:24.1.1 + image: container-registry.oracle.com/database/ords:25.1.0 forceRestart: true encPrivKey: secretName: prvkey @@ -112,7 +112,6 @@ rm syspwdfile e_syspwdfile poolSettings: - poolName: pdb1 autoUpgradeORDS: true - autoUpgradeAPEX: true db.connectionType: tns db.tnsAliasName: PDB1 tnsAdminSecret: @@ -160,7 +159,8 @@ rm syspwdfile e_syspwdfile db.secret: secretName: multi-ords-auth-enc ``` - latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + latest container-registry.oracle.com/database/ords version, **25.1.0**, valid as of **26-May-2025** + 1. Apply the yaml file: ```bash @@ -196,5 +196,4 @@ This example has multiple pools, named `pdb1`, `pdb2`, `pdb3`, and `pdb4`. * They all share the same `tnsAdminSecret` to connect using thier individual `db.tnsAliasName` * They will all automatically restart when the configuration changes: `forceRestart: true` * Only the `pdb1` pool will automatically install/update ORDS on startup, if required: `autoUpgradeORDS: true` -* Only the `pdb1` pool will automatically install/update APEX on startup, if required: `autoUpgradeAPEX: true` * The `passwordKey` has been ommitted from both `db.secret` and `db.adminUser.secret` as the password was stored in the default key (`password`) diff --git a/docs/ordsservices/examples/sidb_container.md b/docs/ordsservices/examples/sidb_container.md index 3cda09ea..da515332 100644 --- a/docs/ordsservices/examples/sidb_container.md +++ b/docs/ordsservices/examples/sidb_container.md @@ -12,7 +12,7 @@ Refer to Single Instance Database (SIDB) [README](https://github.com/oracle/orac ```bash DB_PWD= - kubectl create secret generic sidb-db-auth --from-literal=password=${DB_PWD} --namespace ordsnamespace + kubectl create secret generic sidb-db-auth --from-literal=oracle_pwd=${DB_PWD} --namespace ordsnamespace ``` 1. Create a manifest for the containerised Oracle Database. @@ -73,7 +73,6 @@ rm sidb-db-auth e_sidb-db-auth As the DB in the Free image does not contain ORDS (or APEX), the following additional keys are specified for the pool: * `autoUpgradeORDS` - Boolean; when true the ORDS will be installed/upgraded in the database - * `autoUpgradeAPEX` - Boolean; when true the APEX will be installed/upgraded in the database * `db.adminUser` - User with privileges to install, upgrade or uninstall ORDS in the database (SYS). * `db.adminUser.secret` - Secret containing the password for `db.adminUser` (created in the first step) @@ -87,7 +86,7 @@ rm sidb-db-auth e_sidb-db-auth name: ords-sidb namespace: ordsnamespace spec: - image: container-registry.oracle.com/database/ords:24.1.1 + image: container-registry.oracle.com/database/ords:25.1.0 forceRestart: true encPrivKey: secretName: prvkey @@ -97,7 +96,6 @@ rm sidb-db-auth e_sidb-db-auth poolSettings: - poolName: default autoUpgradeORDS: true - autoUpgradeAPEX: true restEnabledSql.active: true plsql.gateway.mode: direct db.connectionType: customurl @@ -112,7 +110,7 @@ rm sidb-db-auth e_sidb-db-auth kubectl apply -f ords-sidb.yaml ``` - latest container-registry.oracle.com/database/ords version, **24.1.1**, valid as of **30-May-2024** + latest container-registry.oracle.com/database/ords version, **25.1.0**, valid as of **26-May-2025** 1. Watch the ordssrvs resource until the status is **Healthy**: ```bash @@ -147,6 +145,5 @@ This example has a single database pool, named `default`. It is set to: * Automatically restart when the configuration changes: `forceRestart: true` * Automatically install/update ORDS on startup, if required: `autoUpgradeORDS: true` -* Automatically install/update APEX on startup, if required: `autoUpgradeAPEX: true` * Use a basic connection string to connect to the database: `db.customURL: jdbc:oracle:thin:@//${CONN_STRING}` * The `passwordKey` has been ommitted from both `db.secret` and `db.adminUser.secret` as the password was stored in the default key (`password`) diff --git a/docs/ordsservices/usecase01/makefile b/docs/ordsservices/usecase01/makefile index 76b47210..fd4b68f9 100644 --- a/docs/ordsservices/usecase01/makefile +++ b/docs/ordsservices/usecase01/makefile @@ -80,9 +80,9 @@ export PDB1_PRIV_AUTH_SECRET=pdb1-priv-auth-enc export PDB2_PRIV_AUTH_SECRET=pdb2-priv-auth-enc -export SIDB_IMAGE=container-registry.oracle.com/database/free:23.4.0.0 -export ORDS_IMAGE=container-registry.oracle.com/database/ords:24.1.0 -export ORDS_IMAGE.1=container-registry.oracle.com/database/ords:24.1.1 +export SIDB_IMAGE=container-registry.oracle.com/database/free:23.7.0.0 +export ORDS_IMAGE=container-registry.oracle.com/database/ords:25.1.0 +export ORDS_IMAGE.1=container-registry.oracle.com/database/ords:25.1.0 export SECRET_CONTAINER_REGISTRY=oracle-container-registry-secret export ORACLE_CONTAINER_REGISTRY=container-registry.oracle.com export REST_SERVER_NAME=ords-sidb @@ -467,7 +467,6 @@ cat<$(REST_SERVER_CREATION) # poolSettings: # - poolName: default # autoUpgradeORDS: true -# autoUpgradeAPEX: true # restEnabledSql.active: true # plsql.gateway.mode: direct # db.connectionType: customurl @@ -698,7 +697,7 @@ cat <$(MULTISRV_MANIFEST) # name: ords-multi-pool # namespace: $(ORDSNAMESPACE) #spec: -# image: container-registry.oracle.com/database/ords:24.1.1 +# image: $(ORDS_IMAGE.1) # forceRestart: true # encPrivKey: # secretName: prvkey @@ -707,7 +706,6 @@ cat <$(MULTISRV_MANIFEST) # database.api.enabled: true # poolSettings: # - poolName: pdb1 -# autoUpgradeAPEX: false # autoUpgradeORDS: false # db.connectionType: tns # db.tnsAliasName: pdb1 @@ -723,7 +721,6 @@ cat <$(MULTISRV_MANIFEST) # db.adminUser.secret: # secretName: $(PDB1_PRIV_AUTH_SECRET) # - poolName: pdb2 -# autoUpgradeAPEX: false # autoUpgradeORDS: false # db.connectionType: tns # db.tnsAliasName: PDB2 diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index f8d29401..876c1ae9 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -12455,22 +12455,40 @@ spec: type: boolean globalSettings: properties: + apex.download: + default: false + type: boolean + apex.download.url: + default: https://download.oracle.com/otn_software/apex/apex-latest.zip + type: string + apex.installation.persistence: + properties: + accessMode: + default: ReadWriteOnce + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + size: + default: 1Gi + type: string + storageClass: + type: string + volumeName: + type: string + type: object cache.metadata.enabled: type: boolean cache.metadata.graphql.expireAfterAccess: - format: int64 - type: integer + type: string cache.metadata.graphql.expireAfterWrite: - format: int64 - type: integer + type: string cache.metadata.jwks.enabled: type: boolean cache.metadata.jwks.expireAfterAccess: - format: int64 - type: integer + type: string cache.metadata.jwks.expireAfterWrite: - format: int64 - type: integer + type: string cache.metadata.jwks.initialCapacity: format: int32 type: integer @@ -12478,8 +12496,7 @@ spec: format: int32 type: integer cache.metadata.timeout: - format: int64 - type: integer + type: string certSecret: properties: cert: @@ -12498,8 +12515,7 @@ spec: database.api.management.services.disabled: type: boolean db.invalidPoolTimeout: - format: int64 - type: integer + type: string debug.printDebugToScreen: type: boolean enable.mongo.access.log: @@ -12526,11 +12542,9 @@ spec: mongo.enabled: type: boolean mongo.idle.timeout: - format: int64 - type: integer + type: string mongo.op.timeout: - format: int64 - type: integer + type: string mongo.port: default: 27017 format: int32 @@ -12541,8 +12555,7 @@ spec: format: int32 type: integer security.credentials.lock.time: - format: int64 - type: integer + type: string security.disableDefaultExclusionList: type: boolean security.exclusionList: @@ -12574,8 +12587,7 @@ spec: format: int32 type: integer standalone.stop.timeout: - format: int64 - type: integer + type: string type: object image: type: string @@ -12641,8 +12653,7 @@ spec: db.hostname: type: string db.poolDestroyTimeout: - format: int64 - type: integer + type: string db.port: format: int32 type: integer @@ -12700,8 +12711,7 @@ spec: format: int32 type: integer jdbc.MaxConnectionReuseTime: - format: int32 - type: integer + type: string jdbc.MaxLimit: format: int32 type: integer @@ -12747,23 +12757,18 @@ spec: restEnabledSql.active: type: boolean security.jwks.connection.timeout: - format: int64 - type: integer + type: string security.jwks.read.timeout: - format: int64 - type: integer + type: string security.jwks.refresh.interval: - format: int64 - type: integer + type: string security.jwks.size: format: int32 type: integer security.jwt.allowed.age: - format: int64 - type: integer + type: string security.jwt.allowed.skew: - format: int64 - type: integer + type: string security.jwt.profile.enabled: type: boolean security.requestAuthenticationFunction: @@ -12797,6 +12802,8 @@ spec: format: int32 minimum: 1 type: integer + serviceAccountName: + type: string workloadType: default: Deployment enum: @@ -15697,6 +15704,7 @@ rules: - endpoints - events - namespaces + - persistentvolumeclaim - persistentvolumeclaims - persistentvolumes - pods @@ -15719,6 +15727,7 @@ rules: - configmaps/status - daemonsets/status - deployments/status + - persistentvolumeclaim/status - services/status - statefulsets/status verbs: @@ -16796,8 +16805,8 @@ spec: - /manager env: - name: WATCH_NAMESPACE - value: "oracle-database-operator-system,cdbnamespace,pdbnamespace" - image: lin.ocir.io/intsanjaysingh/operator/dboperator_master_250730:latest + value: "" + image: lin.ocir.io/intsanjaysingh/marcstef/oo:1.2.0.8 imagePullPolicy: Always name: manager ports: diff --git a/ords/ords_init.sh b/ords/ords_init.sh index 0994dceb..fc13912b 100644 --- a/ords/ords_init.sh +++ b/ords/ords_init.sh @@ -35,6 +35,9 @@ ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## SOFTWARE. +# not used, avoid messages +unset JAVA_TOOL_OPTIONS + dump_stack(){ _log_date=`date "+%y:%m:%d %H:%M:%S"` local frame=0 @@ -54,6 +57,8 @@ _log_date=`date "+%y:%m:%d %H:%M:%S"` get_conn_string() { local -n _conn_string="${1}" + echo "== Prepare connect string" + local -r _admin_user=$($ords_cfg_cmd get --secret db.adminUser | tail -1) local _conn_type=$($ords_cfg_cmd get db.connectionType |tail -1) if [[ $_conn_type == "customurl" ]]; then @@ -81,7 +86,7 @@ get_conn_string() { if [[ -n ${_conn} ]]; then echo "Connection String (${_conn_type}): ${_conn}" - _conn_string="${_admin_user%%/ *}/${config["dbadminusersecret"]}@${_conn}" + _conn_string="${_admin_user%%/ *}/${config["dbadminuserpassword"]}@${_conn}" if [[ ${_admin_user%%/ *} == "SYS" ]]; then _conn_string="${_conn_string=} AS SYSDBA" fi @@ -89,6 +94,32 @@ get_conn_string() { } #------------------------------------------------------------------------------ +function setup_sql(){ + local -r _conn_string="${1}" + + echo "== Configuring sql environment" + + ## Get TNS_ADMIN location + local -r _tns_admin=$($ords_cfg_cmd get db.tnsDirectory 2>&1| tail -1) + if [[ ! $_tns_admin =~ "Cannot get setting" ]]; then + echo "Setting: TNS_ADMIN=${_tns_admin}" + export TNS_ADMIN=${_tns_admin} + fi + + ## Get ADB Wallet + #echo "Checking db.wallet.zip.path" + #echo "$ords_cfg_cmd get db.wallet.zip.path" + local -r _wallet_zip_path=$($ords_cfg_cmd get db.wallet.zip.path 2>&1| tail -1) + #echo "wallet_zip_path : \"${_wallet_zip_path}\"" + if [[ ! $_wallet_zip_path =~ "Cannot get setting" ]]; then + echo "Using: set cloudconfig ${_wallet_zip_path}" + local -r _cloudconfig="set cloudconfig ${_wallet_zip_path}" + fi + + return 0 + +} + function run_sql { local -r _conn_string="${1}" local -r _sql="${2}" @@ -97,26 +128,13 @@ function run_sql { if [[ -z ${_sql} ]]; then dump_stack - echo "FATAL: Dear Developer.. you've got a bug calling run_sql" && exit 1 - fi - ## Get TNS_ADMIN location - local -r _tns_admin=$($ords_cfg_cmd get db.tnsDirectory | tail -1) - if [[ ! $_tns_admin =~ "Cannot get setting" ]]; then - echo "Setting: TNS_ADMIN=${_tns_admin}" - export TNS_ADMIN=${_tns_admin} + echo "FATAL: missing SQL calling run_sql" && exit 1 fi - ## Get ADB Wallet - local -r _wallet_zip_path=$($ords_cfg_cmd get db.wallet.zip.path | tail -1) - if [[ ! $_wallet_zip_path =~ "Cannot get setting" ]]; then - echo "Using: set cloudconfig ${_wallet_zip_path}" - local -r _cloudconfig="set cloudconfig ${_wallet_zip_path}" - fi + echo "Running SQL" # NOTE to maintainer; the heredoc must be TAB indented - echo "Running SQL..." - #_output=$(cd ${APEX_HOME}/${APEX_VER} && sql -S /nolog <<-EOSQL - _output=$(cd ${APEX_HOME}/${APEX_VER} && sql -S -nohistory -noupdates /nolog <<-EOSQL + _output=$(sql -S -nohistory -noupdates /nolog <<-EOSQL WHENEVER SQLERROR EXIT 1 WHENEVER OSERROR EXIT 1 ${_cloudconfig} @@ -143,6 +161,8 @@ function check_adb() { local -r _conn_string=$1 local -n _is_adb=$2 + echo "== ADB check" + local -r _adb_chk_sql=" DECLARE invalid_column exception; @@ -164,10 +184,14 @@ function check_adb() { if (( ${_rc} == 0 )); then _adb_check=${_adb_check//[[:space:]]/} - echo "ADB Check: ${_adb_check}" if (( ${_adb_check} == 1 )); then _is_adb=${_adb_check//[[:space:]]/} + echo "ADB : yes" + else + echo "ADB : no" fi + else + echo "ADB check failed" fi return ${_rc} @@ -180,7 +204,7 @@ function create_adb_user() { local _config_user=$($ords_cfg_cmd get db.username | tail -1) if [[ -z ${_config_user} ]] || [[ ${_config_user} == "ORDS_PUBLIC_USER" ]]; then - echo "FATAL: You must specify a db.username <> ORDS_PUBLIC_USER in pool ${_pool_name}" + echo "FATAL: You must specify a db.username <> ORDS_PUBLIC_USER in pool \"${_pool_name}\"" dump_stack return 1 fi @@ -193,11 +217,11 @@ function create_adb_user() { BEGIN SELECT USERNAME INTO l_user FROM DBA_USERS WHERE USERNAME='${_config_user}'; EXECUTE IMMEDIATE 'ALTER USER \"${_config_user}\" PROFILE ORA_APP_PROFILE'; - EXECUTE IMMEDIATE 'ALTER USER \"${_config_user}\" IDENTIFIED BY \"${config["dbsecret"]}\"'; + EXECUTE IMMEDIATE 'ALTER USER \"${_config_user}\" IDENTIFIED BY \"${config["dbpassword"]}\"'; DBMS_OUTPUT.PUT_LINE('${_config_user} Exists - Password reset'); EXCEPTION WHEN NO_DATA_FOUND THEN - EXECUTE IMMEDIATE 'CREATE USER \"${_config_user}\" IDENTIFIED BY \"${config["dbsecret"]}\" PROFILE ORA_APP_PROFILE'; + EXECUTE IMMEDIATE 'CREATE USER \"${_config_user}\" IDENTIFIED BY \"${config["dbpassword"]}\" PROFILE ORA_APP_PROFILE'; DBMS_OUTPUT.PUT_LINE('${_config_user} Created'); END; EXECUTE IMMEDIATE 'GRANT CONNECT TO \"${_config_user}\"'; @@ -247,10 +271,13 @@ function create_adb_user() { } #------------------------------------------------------------------------------ -function compare_versions() { +function apex_compare_versions() { local _db_ver=$1 local _im_ver=$2 + echo "database APEX version : $_db_ver" + echo "install APEX version : $_im_ver" + IFS='.' read -r -a _db_ver_array <<< "$_db_ver" IFS='.' read -r -a _im_ver_array <<< "$_im_ver" @@ -270,95 +297,287 @@ function compare_versions() { } #------------------------------------------------------------------------------ -set_secret() { +ords_client_version(){ + echo "== ORDS client version" + echo "ords_client_version : \"$(ords --config $ORDS_CONFIG --version 2>&1 | tail -1)\"" +} + +#------------------------------------------------------------------------------ +set_ords_secret() { local -r _pool_name="${1}" local -r _config_key="${2}" local -r _config_val="${3}" local -i _rc=0 if [[ -n "${_config_val}" ]]; then - ords --config "$ORDS_CONFIG" config --db-pool "${_pool_name}" secret --password-stdin "${_config_key}" <<< "${_config_val}" + echo "Setting ${_config_key} in pool \"${_pool_name}\"" + ords --config "$ORDS_CONFIG" config --db-pool "${_pool_name}" secret --password-stdin "${_config_key}" 2>&1 <<< "${_config_val}"|tail -1 _rc=$? - echo "${_config_key} in pool ${_pool_name} set" else - echo "${_config_key} in pool ${_pool_name}, not defined" + echo "${_config_key} in pool \"${_pool_name}\" is not defined" _rc=0 fi return ${_rc} } +#------------------------------------------------------------------------------ +read_passwords(){ + echo "== Reading passwords" + for key in dbpassword dbadminuserpassword dbcdbadminuserpassword; do + var_key="${pool_name//-/_}_${key}" + echo "Obtaining value from initContainer variable: ${var_key}" + var_val="${!var_key}" + config[${key}]="${var_val}" + done + + # Set ORDS Secrets + set_ords_secret "${pool_name}" "db.password" "${config["dbpassword"]}" + rc=$((rc + $?)) + set_ords_secret "${pool_name}" "db.adminUser.password" "${config["dbadminuserpassword"]}" + rc=$((rc + $?)) + set_ords_secret "${pool_name}" "db.cdb.adminUser.password" "${config["dbcdbadminuserpassword"]}" + rc=$((rc + $?)) + + if (( ${rc} > 0 )); then + echo "FATAL: Unable to set configuration for pool \"${pool_name}\"" + return 1 + elif [[ -z ${config["dbpassword"]} ]]; then + echo "FATAL: db.password must be specified for pool \"${pool_name}\"" + return 1 + elif [[ -z ${config["dbadminuserpassword"]} ]]; then + echo "INFO: No additional configuration for pool \"${pool_name}\"" + fi + + return 0 +} + #------------------------------------------------------------------------------ ords_upgrade() { - local -r _pool_name="${1}" - local -r _upgrade_key="${2}" - local -i _rc=0 - - if [[ -n "${config["dbadminusersecret"]}" ]]; then - # Get usernames - local -r ords_user=$($ords_cfg_cmd get db.username | tail -1) - local -r ords_admin=$($ords_cfg_cmd get db.adminUser | tail -1) - - echo "Performing ORDS install/upgrade as $ords_admin into $ords_user on pool ${_pool_name}" - if [[ ${_pool_name} == "default" ]]; then - ords --config "$ORDS_CONFIG" install --db-only \ - --admin-user "$ords_admin" --password-stdin <<< "${config["dbadminusersecret"]}" - _rc=$? - else - ords --config "$ORDS_CONFIG" install --db-pool "${_pool_name}" --db-only \ - --admin-user "$ords_admin" --password-stdin <<< "${config["dbadminusersecret"]}" - _rc=$? - fi + local -r _pool_name="${1}" + local -r _upgrade_key="${2}" + local -i _rc=0 + + echo "== ORDS install/upgrade" + + if [[ -n "${config["dbadminuserpassword"]}" ]]; then + # Get usernames + local -r ords_user=$($ords_cfg_cmd get db.username | tail -1) + local -r ords_admin=$($ords_cfg_cmd get db.adminUser | tail -1) + + echo "Performing ORDS install/upgrade as $ords_admin into $ords_user on pool \"${_pool_name}\"" + if [[ ${_pool_name} == "default" ]]; then + ords --config "$ORDS_CONFIG" install --db-only \ + --admin-user "$ords_admin" --password-stdin <<< "${config["dbadminuserpassword"]}" + _rc=$? + else + ords --config "$ORDS_CONFIG" install --db-pool "${_pool_name}" --db-only \ + --admin-user "$ords_admin" --password-stdin <<< "${config["dbadminuserpassword"]}" + _rc=$? + fi + + # Dar be bugs below deck with --db-user so using the above + # ords --config "$ORDS_CONFIG" install --db-pool "$1" --db-only \ + # --admin-user "$ords_admin" --db-user "$ords_user" --password-stdin <<< "${!2}" + fi + + return $_rc +} - # Dar be bugs below deck with --db-user so using the above - # ords --config "$ORDS_CONFIG" install --db-pool "$1" --db-only \ - # --admin-user "$ords_admin" --db-user "$ords_user" --password-stdin <<< "${!2}" - fi - return $_rc +#------------------------------------------------------------------------------ +function global_parameters(){ + + APEX_INSTALL=/opt/oracle/apex + # backward compatibility for ORDS images prior to 24.1.x (included) + # APEX_HOME is used only here and it is set on images <= 24.1.x + if [[ -n ${APEX_HOME} ]]; then + APEX_INSTALL=${APEX_HOME}/${APEX_VER} + echo "WARNING: APEX installation ${APEX_INSTALL}, ORDS image probably older than 24.2" + fi + APEXINS=${APEX_INSTALL}/apexins.sql + APEX_IMAGES=${APEX_INSTALL}/images + APEX_VERSION_TXT=${APEX_IMAGES}/apex_version.txt + + echo "== global parameters" + echo "external_apex : ${external_apex}" + echo "download_apex : ${download_apex}" + echo "download_url_apex : ${download_url_apex}" + echo "APEX_INSTALL : $APEX_INSTALL" + echo "APEX_IMAGES : $APEX_IMAGES" + } + #------------------------------------------------------------------------------ function get_apex_version() { local -r _conn_string="${1}" - local -n _action="${2}" + local -n _db_apex_version="${2}" local -i _rc=0 + echo "== APEX version check" + local -r _ver_sql="SELECT VERSION FROM DBA_REGISTRY WHERE COMP_ID='APEX';" + #local -r _ver_sql="SELECT SCHEMA FROM DBA_REGISTRY WHERE COMP_ID='APEX';" run_sql "${_conn_string}" "${_ver_sql}" "_db_apex_version" _rc=$? if (( $_rc > 0 )); then - echo "FATAL: Unable to connect to ${_conn_string} to get APEX version" + echo "FATAL: Unable to get APEX version" dump_stack return $_rc fi - local -r _db_apex_version=${_db_apex_version//[^0-9.]/} - echo "Database APEX Version: ${_db_apex_version:-Not Installed}" - - _action="none" + _db_apex_version=${_db_apex_version//[^0-9.]/} + #_db_apex_version="${_db_apex_version//[[:space:]]}" if [[ -z "${_db_apex_version}" ]]; then - echo "Installing APEX ${APEX_VER}" - _action="install" - elif compare_versions ${_db_apex_version} ${APEX_VER}; then - echo "Upgrading from ${_db_apex_version} to ${APEX_VER}" - _action="upgrade" - else - echo "No Installation/Upgrade Required" - fi + _db_apex_version="NotInstalled" + fi + echo "Database APEX Version: ${_db_apex_version}" + + return $_rc +} + +#------------------------------------------------------------------------------ +function get_apex_action(){ + local -r _conn_string="${1}" + local -r _db_apex_version="${2}" + local -n _action="${3}" + local -i _rc=0 + + echo "== APEX installation check" + + _action="error" + if [[ ( -z "${_db_apex_version}" ) || ( "${_db_apex_version}" == "NotInstalled" ) ]]; then + echo "Installing APEX ${APEX_VER}" + _action="install" + elif apex_compare_versions ${_db_apex_version} ${APEX_VER}; then + echo "Upgrading from ${_db_apex_version} to ${APEX_VER}" + _action="upgrade" + else + echo "No Installation/Upgrade Required" + _action="none" + fi return $_rc } -apex_upgrade() { +#------------------------------------------------------------------------------ +function check_apex_installation_version(){ + + echo "== APEX installation files" + + if [[ !(-f ${APEX_VERSION_TXT}) ]]; then + echo "ERROR: ${APEX_VERSION_TXT} not found, APEX installation not found" + return 1 + fi + + APEX_VER=$(cat ${APEX_VERSION_TXT}|grep Version|cut -f2 -d\:|tr -d '[:space:]') + echo "APEX_VER: ${APEX_VER}" +} + +#------------------------------------------------------------------------------ +function apex_external(){ + echo "== APEX external" + + if [[ ${external_apex} != "true" ]]; then + echo "APEX external disabled" + return 0 + fi + + id + df -h $APEX_INSTALL + ls -ld $APEX_INSTALL + ls -l $APEX_INSTALL + ls -l ${APEX_VERSION_TXT} + echo test >> $APEX_INSTALL/test.txt + ls -l $APEX_INSTALL/test.txt + + while true + do + date +"%Y-%m-%d %H:%M:%S" + if [[ -f ${APEX_VERSION_TXT} ]] + then + echo Found images/apex_version.txt + break + else + if [[ -f ${APEX_INSTALL}/apex.zip ]] + then + date + echo Found ${APEX_INSTALL}/apex.zip, extracting ... + cd ${APEX_INSTALL} + jar xf apex.zip + mv ${APEX_INSTALL}/apex/* ${APEX_INSTALL} + date + echo completed + else + date + echo "Missing ${APEX_INSTALL}/apex.zip, manually copy apex.zip in ${APEX_INSTALL} on the init container of the pod" + echo "e.g. kubectl cp /tmp/apex.zip ordssrvs-697c5698d9-q8gxf:/opt/oracle/apex/ -n testcase -c ordssrvs-init" + sleep 5 + fi + fi + done + +} + +#------------------------------------------------------------------------------ +function apex_download(){ + + echo "== APEX download" + + if [[ ${download_apex} != "true" ]]; then + echo "APEX download disabled" + return 0 + fi + + mkdir -p ${APEX_INSTALL} + rm -rf ${APEX_INSTALL}/* + cd /tmp + echo "Downloading ${download_url_apex}" + curl -o apex.zip ${download_url_apex} + echo "Extracting apex.zip" + jar xf apex.zip + mv /tmp/apex/* ${APEX_INSTALL} + + if [[ !(-f $APEXINS) ]]; then + echo "ERROR: ${APEXINS} not found, APEX download failed" + return 1 + fi + echo "APEX_INSTALL: ${APEX_INSTALL}" + + # config can be read-only, it will be set again at command-line ords start + echo "== Configuring ORDS images" + echo "APEX_IMAGES: ${APEX_IMAGES}" + ords config set standalone.static.path ${APEX_IMAGES} + + return 0 +} + +#------------------------------------------------------------------------------ +function apex_upgrade() { local -r _conn_string="${1}" local -r _upgrade_key="${2}" local -i _rc=0 - if [[ -f ${APEX_HOME}/${APEX_VER}/apexins.sql ]] && [[ "${!_upgrade_key}" = "true" ]]; then - echo "Starting Installation of APEX ${APEX_VER}" - local -r _install_sql="@apxsilentins.sql SYSAUX SYSAUX TEMP /i/ ${config["dbsecret"]} ${config["dbsecret"]} ${config["dbsecret"]} ${config["dbsecret"]}" + echo "== APEX Installation/Upgrade" + + if [[ -z ${APEX_INSTALL} ]]; then + echo "ERROR: APEX_INSTALL not set" + return 1 + fi + + if [[ !( -f ${APEX_INSTALL}/apexins.sql ) ]]; then + echo "ERROR: ${APEX_INSTALL}/apexins.sql not found" + return 1 + fi + + + if [[ "${!_upgrade_key}" = "true" ]]; then + echo "Starting Installation of APEX" + cd ${APEX_INSTALL} + SEC=${config["dbpassword"]} + local -r _install_sql="@apxsilentins.sql SYSAUX SYSAUX TEMP /i/ $SEC $SEC $SEC $SEC" run_sql "${_conn_string}" "${_install_sql}" "_install_output" _rc=$? echo "Installation Output: ${_install_output}" @@ -367,56 +586,110 @@ apex_upgrade() { return $_rc } +#------------------------------------------------------------------------------ +function apex_housekeeping(){ + + echo "== APEX " + + # check database APEX version regardless of APEX parameters + get_apex_version "${conn_string}" "db_apex_version" + if [[ -z ${db_apex_version} ]]; then + echo "FATAL: Unable to get APEX Version for pool \"${pool_name}\"" + return 1 + fi + + # check if apex upgrade is enabled + if [[ ${apex_upgrade} != "true" ]]; then + echo "APEX Install/Upgrade not requested for pool \"${pool_name}\"" + return 0 + fi + + # get suggested action + get_apex_action "${conn_string}" "${db_apex_version}" "db_apex_action" + if [[ -z ${db_apex_action} ]]; then + echo "FATAL: Unable to get APEX suggested action for pool \"${pool_name}\"" + return 1 + fi + + # upgrade + echo "APEX version : \"${db_apex_version}\"" + echo "APEX suggested action : $db_apex_action" + if [[ ${db_apex_action} != "none" ]]; then + apex_upgrade "${conn_string}" "${pool_name}_autoupgrade_apex" + if (( $? > 0 )); then + echo "FATAL: Unable to ${db_apex_action} APEX for pool \"${pool_name}\"" + return 1 + fi + fi + + +} + + + +#------------------------------------------------------------------------------ +function pool_parameters(){ + + echo "== pool parameters" + apex_upgrade_var=${pool_name}_autoupgrade_apex + apex_upgrade=${!apex_upgrade_var} + [[ -z $apex_upgrade ]] && apex_upgrade=false + echo "${pool_name} - autoupgrade_apex : ${apex_upgrade}" + + ords_upgrade_var=${pool_name}_autoupgrade_ords + ords_upgrade=${!ords_upgrade_var} + [[ -z $ords_upgrade ]] && ords_upgrade=false + echo "${pool_name} - autoupgrade_ords : ${ords_upgrade}" + +} + + #------------------------------------------------------------------------------ # INIT #------------------------------------------------------------------------------ declare -A pool_exit +echo "=== ORDSSRVS init ===" +global_parameters +ords_client_version +apex_download +apex_external + +# check APEX installation files version, downloaded or mounted by PVC +check_apex_installation_version + for pool in "$ORDS_CONFIG"/databases/*; do rc=0 pool_name=$(basename "$pool") pool_exit[${pool_name}]=0 ords_cfg_cmd="ords --config $ORDS_CONFIG config --db-pool ${pool_name}" - echo "Found Pool: $pool_name..." - - declare -A config - for key in dbsecret dbadminusersecret dbcdbadminusersecret; do - var_key="${pool_name//-/_}_${key}" - echo "Obtaining value from initContainer variable: ${var_key}" - var_val="${!var_key}" - config[${key}]="${var_val}" - done + echo "==========================================================================" + echo "Pool: $pool_name" + declare -A config - # Set Secrets - set_secret "${pool_name}" "db.password" "${config["dbsecret"]}" - rc=$((rc + $?)) - set_secret "${pool_name}" "db.adminUser.password" "${config["dbadminusersecret"]}" - rc=$((rc + $?)) - set_secret "${pool_name}" "db.cdb.adminUser.password" "${config["dbcdbadminusersecret"]}" - rc=$((rc + $?)) + pool_parameters + read_passwords + rc=$? if (( ${rc} > 0 )); then - echo "FATAL: Unable to set configuration for pool ${pool_name}" - dump_stack - pool_exit[${pool_name}]=1 - continue - elif [[ -z ${config["dbsecret"]} ]]; then - echo "FATAL: db.password must be specified for ${pool_name}" - dump_stack - pool_exit[${pool_name}]=1 - continue - elif [[ -z ${config["dbadminusersecret"]} ]]; then - echo "INFO: No additional configuration for ${pool_name}" - continue - fi + pool_exit[${pool_name}]=1 + continue + fi get_conn_string "conn_string" if [[ -z ${conn_string} ]]; then - echo "FATAL: Unable to get ${pool_name} database connect string" + echo "FATAL: Unable to get database connect string for pool \"${pool_name}\"" dump_stack pool_exit[${pool_name}]=1 continue fi + setup_sql "${conn_string}" + rc=$? + if (( ${rc} > 0 )); then + pool_exit[${pool_name}]=1 + continue + fi + check_adb "${conn_string}" "is_adb" rc=$? if (( ${rc} > 0 )); then @@ -428,51 +701,38 @@ for pool in "$ORDS_CONFIG"/databases/*; do # Create ORDS User echo "Processing ADB in Pool: ${pool_name}" create_adb_user "${conn_string}" "${pool_name}" - else - # APEX Upgrade - echo "---------------------------------------------------" - apex_upgrade_var=${pool_name}_autoupgrade_apex - if [[ ${!apex_upgrade_var} != "true" ]]; then - echo "APEX Install/Upgrade not requested for ${pool_name}" - continue - fi - - get_apex_version "${conn_string}" "action" - if [[ -z ${action} ]]; then - echo "FATAL: Unable to get ${pool_name} APEX Version" - dump_stack - pool_exit[${pool_name}]=1 - continue - fi + continue + fi - if [[ ${action} != "none" ]]; then - apex_upgrade "${conn_string}" "${pool_name}_autoupgrade_apex" - if (( $? > 0 )); then - echo "FATAL: Unable to ${action} APEX for ${pool_name}" - dump_stack - pool_exit[${pool_name}]=1 - continue - fi - fi + # not ADB - # ORDS Upgrade - ords_upgrade_var=${pool_name}_autoupgrade_ords - if [[ ${!ords_upgrade_var} != "true" ]]; then - echo "ORDS Install/Upgrade not requested for ${pool_name}" - continue - fi + # APEX + apex_housekeeping + rc=$? + if (( ${rc} > 0 )); then + echo "FATAL: unable to manage APEX configuration for pool \"${pool_name}\"" + dump_stack + pool_exit[${pool_name}]=1 + continue + fi + # database ORDS + echo "== ORDS" + if [[ ${ords_upgrade} == "true" ]]; then ords_upgrade "${pool_name}" "${pool_name}_autoupgrade_ords" rc=$? if (( $rc > 0 )); then - echo "FATAL: Unable to preform requested ORDS install/upgrade on ${pool_name}" + echo "FATAL: Unable to perform requested ORDS install/upgrade on pool \"${pool_name}\"" pool_exit[${pool_name}]=1 dump_stack - continue - fi + fi + else + echo "ORDS Install/Upgrade not requested for pool \"${pool_name}\"" fi done +echo "==========================================================================" +echo "Exit codes" for key in "${!pool_exit[@]}"; do echo "Pool: $key, Exit Code: ${pool_exit[$key]}" if (( ${pool_exit[$key]} > 0 )); then diff --git a/ords/ords_start.sh b/ords/ords_start.sh new file mode 100644 index 00000000..acdedd98 --- /dev/null +++ b/ords/ords_start.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +date +"%Y-%m-%d %H:%M:%S" +echo "=== ORDS start ===" +echo "ORDS_CONFIG: ${ORDS_CONFIG}" + +unset APEX_IMAGES + +# old path, until ORDS image 24.1.1 +if [[ !(-z ${APEX_BASE}) && !(-z ${APEX_VER}) && (-d ${APEX_BASE}/${APEX_VER}/images) ]]; then + APEX_IMAGES=${APEX_BASE}/${APEX_VER}/images +fi + +# downloaded image path +if [[ -d /opt/oracle/apex/images ]]; then + APEX_IMAGES=/opt/oracle/apex/images +fi + +if [[ -z ${APEX_IMAGES} ]]; then + echo "APEX_IMAGES not found" + ords --config ${ORDS_CONFIG} serve +else + echo "APEX_IMAGES: ${APEX_IMAGES}" + ords --config ${ORDS_CONFIG} serve --apex-images ${APEX_IMAGES} +fi + From 696be453c54873f30371a70fc393274e45b10b86 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Mon, 11 Aug 2025 18:57:49 +0000 Subject: [PATCH 258/414] Observability/feature/exporter v2.0.2 --- .../v1/databaseobserver_types.go | 178 +- .../v1/databaseobserver_webhook.go | 61 +- .../observability/v1/zz_generated.deepcopy.go | 422 +- .../v1alpha1/databaseobserver_types.go | 179 +- .../v1alpha1/databaseobserver_webhook.go | 60 +- .../v1alpha1/zz_generated.deepcopy.go | 422 +- .../v4/databaseobserver_types.go | 178 +- .../v4/databaseobserver_webhook.go | 60 +- .../observability/v4/zz_generated.deepcopy.go | 422 +- commons/observability/constants.go | 91 +- commons/observability/utils.go | 504 +- ...vability.oracle.com_databaseobservers.yaml | 11196 ++++++++-------- .../observability/databaseobserver.yaml | 10 +- .../observability/v1/databaseobserver.yaml | 75 +- ...databaseobserver_customization_fields.yaml | 58 +- .../v1/databaseobserver_logs_promtail.yaml | 65 +- .../v1alpha1/databaseobserver.yaml | 71 +- .../databaseobserver_custom_config.yaml | 35 +- .../databaseobserver_logs_promtail.yaml | 74 +- .../v1alpha1/databaseobserver_minimal.yaml | 12 +- .../v1alpha1/databaseobserver_vault.yaml | 30 - .../v1alpha1/databaseobserver_vault_oci.yaml | 29 + .../observability/v4/databaseobserver.yaml | 69 +- .../v4/databaseobserver_custom_config.yaml | 30 +- ...databaseobserver_exporter_config_file.yaml | 42 + .../v4/databaseobserver_logs_promtail.yaml | 65 +- .../v4/databaseobserver_minimal.yaml | 12 +- .../v4/databaseobserver_vault.yaml | 39 - .../v4/databaseobserver_vault_azure.yaml | 24 + .../v4/databaseobserver_vault_oci.yaml | 29 + .../databaseobserver_controller.go | 79 +- .../databaseobserver_resource.go | 8 +- docs/observability/README.md | 1044 +- 33 files changed, 8607 insertions(+), 7066 deletions(-) delete mode 100644 config/samples/observability/v1alpha1/databaseobserver_vault.yaml create mode 100644 config/samples/observability/v1alpha1/databaseobserver_vault_oci.yaml create mode 100644 config/samples/observability/v4/databaseobserver_exporter_config_file.yaml delete mode 100644 config/samples/observability/v4/databaseobserver_vault.yaml create mode 100644 config/samples/observability/v4/databaseobserver_vault_azure.yaml create mode 100644 config/samples/observability/v4/databaseobserver_vault_oci.yaml diff --git a/apis/observability/v1/databaseobserver_types.go b/apis/observability/v1/databaseobserver_types.go index 0d04af0e..10a6c913 100644 --- a/apis/observability/v1/databaseobserver_types.go +++ b/apis/observability/v1/databaseobserver_types.go @@ -48,78 +48,107 @@ type StatusEnum string // DatabaseObserverSpec defines the desired state of DatabaseObserver type DatabaseObserverSpec struct { - Database DatabaseObserverDatabase `json:"database,omitempty"` - Exporter DatabaseObserverExporterConfig `json:"exporter,omitempty"` - ExporterConfig DatabaseObserverConfigMap `json:"configuration,omitempty"` - Prometheus PrometheusConfig `json:"prometheus,omitempty"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` - Replicas int32 `json:"replicas,omitempty"` - Log LogConfig `json:"log,omitempty"` - InheritLabels []string `json:"inheritLabels,omitempty"` - ExporterSidecars []corev1.Container `json:"sidecars,omitempty"` - SideCarVolumes []corev1.Volume `json:"sidecarVolumes,omitempty"` + Database DatabaseConfig `json:"database,omitempty"` + Databases map[string]MultiDatabaseConfig `json:"databases,omitempty"` + Wallet WalletSecret `json:"wallet,omitempty"` + Deployment ExporterDeployment `json:"deployment,omitempty"` + Service ExporterService `json:"service,omitempty"` + ServiceMonitor ExporterServiceMonitor `json:"serviceMonitor,omitempty"` + ExporterConfig ExporterConfig `json:"exporterConfig,omitempty"` + OCIConfig OCIConfig `json:"ociConfig,omitempty"` + AzureConfig AzureConfig `json:"azureConfig,omitempty"` + Metrics MetricsConfig `json:"metrics,omitempty"` + Log LogConfig `json:"log,omitempty"` + InheritLabels []string `json:"inheritLabels,omitempty"` + Sidecar SidecarConfig `json:"sidecar,omitempty"` + Replicas int32 `json:"replicas,omitempty"` +} + +// SidecarConfig defines sidecar containers and volumes to add +type SidecarConfig struct { + Containers []corev1.Container `json:"containers,omitempty"` + Volumes []corev1.Volume `json:"volumes,omitempty"` +} + +// ExporterConfig defines configMap used for exporter configuration +type ExporterConfig struct { + ConfigMap ConfigMapDetails `json:"configMap,omitempty"` + MountPath string `json:"mountPath,omitempty"` } // LogConfig defines the configuration details relation to the logs of DatabaseObserver type LogConfig struct { - Disable bool `json:"disable,omitempty"` - Destination string `json:"destination,omitempty"` - Filename string `json:"filename,omitempty"` - Volume LogVolume `json:"volume,omitempty"` + Disable bool `json:"disable,omitempty"` + Destination string `json:"destination,omitempty"` + Filename string `json:"filename,omitempty"` + Volume LogVolume `json:"volume,omitempty"` } +// LogVolume defines the shared volume between the exporter container and other containers type LogVolume struct { - PersistentVolumeClaim LogVolumePVClaim `json:"persistentVolumeClaim,omitempty"` + Name string `json:"name,omitempty"` + PersistentVolumeClaim LogVolumePVC `json:"persistentVolumeClaim,omitempty"` } -type LogVolumePVClaim struct { +// LogVolumePVC defines the PVC in which to store the logs +type LogVolumePVC struct { ClaimName string `json:"claimName,omitempty"` } -// DatabaseObserverDatabase defines the database details used for DatabaseObserver -type DatabaseObserverDatabase struct { - DBUser DBSecret `json:"dbUser,omitempty"` - DBPassword DBSecretWithVault `json:"dbPassword,omitempty"` - DBWallet DBSecret `json:"dbWallet,omitempty"` - DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` +// DatabaseConfig defines the database details used for DatabaseObserver +type DatabaseConfig struct { + DBUser DBSecret `json:"dbUser,omitempty"` + DBPassword DBSecret `json:"dbPassword,omitempty"` + DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` + OCIVault DBOCIVault `json:"oci,omitempty"` + AzureVault DBAzureVault `json:"azure,omitempty"` } -// DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver -type DatabaseObserverExporterConfig struct { - Deployment DatabaseObserverDeployment `json:"deployment,omitempty"` - Service DatabaseObserverService `json:"service,omitempty"` +// MultiDatabaseConfig defines each database details used for DatabaseObserver +type MultiDatabaseConfig struct { + DBUser DBSecret `json:"dbUser,omitempty"` + DBPassword DBSecret `json:"dbPassword,omitempty"` + DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` } -// DatabaseObserverDeployment defines the exporter deployment component of DatabaseObserver -type DatabaseObserverDeployment struct { - ExporterImage string `json:"image,omitempty"` - SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` - ExporterArgs []string `json:"args,omitempty"` - ExporterCommands []string `json:"commands,omitempty"` - ExporterEnvs map[string]string `json:"env,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` +// DBAzureVault defines Azure Vault details +type DBAzureVault struct { + VaultID string `json:"vaultID,omitempty"` + VaultUsernameSecret string `json:"vaultUsernameSecret,omitempty"` + VaultPasswordSecret string `json:"vaultPasswordSecret,omitempty"` +} + +// DBOCIVault defines OCI Vault details +type DBOCIVault struct { + VaultID string `json:"vaultID,omitempty"` + VaultPasswordSecret string `json:"vaultPasswordSecret,omitempty"` +} + +// ExporterDeployment defines the exporter deployment component of DatabaseObserver +type ExporterDeployment struct { + ExporterImage string `json:"image,omitempty"` + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"` + ExporterArgs []string `json:"args,omitempty"` + ExporterCommands []string `json:"commands,omitempty"` + ExporterEnvs map[string]string `json:"env,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` } // DeploymentPodTemplate defines the labels for the DatabaseObserver pods component of a deployment type DeploymentPodTemplate struct { - SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` - Labels map[string]string `json:"labels,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } -// DatabaseObserverService defines the exporter service component of DatabaseObserver -type DatabaseObserverService struct { +// ExporterService defines the exporter service component of DatabaseObserver +type ExporterService struct { Ports []corev1.ServicePort `json:"ports,omitempty"` Labels map[string]string `json:"labels,omitempty"` } -// PrometheusConfig defines the generated resources for Prometheus -type PrometheusConfig struct { - ServiceMonitor PrometheusServiceMonitor `json:"serviceMonitor,omitempty"` -} - -// PrometheusServiceMonitor defines DatabaseObserver servicemonitor spec -type PrometheusServiceMonitor struct { +// ExporterServiceMonitor defines DatabaseObserver servicemonitor spec +type ExporterServiceMonitor struct { Labels map[string]string `json:"labels,omitempty"` NamespaceSelector *monitorv1.NamespaceSelector `json:"namespaceSelector,omitempty"` Endpoints []monitorv1.Endpoint `json:"endpoints,omitempty"` @@ -129,40 +158,57 @@ type PrometheusServiceMonitor struct { type DBSecret struct { Key string `json:"key,omitempty"` SecretName string `json:"secret,omitempty"` + EnvName string `json:"envName,omitempty"` +} + +// WalletSecret defines secret and where the wallet will be mounted if provided +type WalletSecret struct { + SecretName string `json:"secret,omitempty"` + MountPath string `json:"mountPath,omitempty"` + AdditionalWallets []AdditionalWalletSecrets `json:"additional,omitempty"` } -// DBSecretWithVault defines secrets used in reference with vault fields -type DBSecretWithVault struct { - Key string `json:"key,omitempty"` - SecretName string `json:"secret,omitempty"` - VaultOCID string `json:"vaultOCID,omitempty"` - VaultSecretName string `json:"vaultSecretName,omitempty"` +// AdditionalWalletSecrets defines multiple other secrets and where the wallet will be mounted if provided +type AdditionalWalletSecrets struct { + Name string `json:"name,omitempty"` + SecretName string `json:"secret,omitempty"` + MountPath string `json:"mountPath,omitempty"` } -// DatabaseObserverConfigMap defines configMap used for metrics configuration -type DatabaseObserverConfigMap struct { - Configmap ConfigMapDetails `json:"configMap,omitempty"` +// MetricsConfig defines configMap used for multiple metrics TOML configuration +type MetricsConfig struct { + Configmap []ConfigMapDetails `json:"configMap,omitempty"` } -// ConfigMapDetails defines the configmap name +// ConfigMapDetails defines the configmap name used by the exporterConfig and metricsConfig type ConfigMapDetails struct { Key string `json:"key,omitempty"` Name string `json:"name,omitempty"` } -// OCIConfigSpec defines the configmap name and secret name used for connecting to OCI -type OCIConfigSpec struct { - ConfigMapName string `json:"configMapName,omitempty"` - SecretName string `json:"secretName,omitempty"` +// OCIConfig defines the configmap name and secret name used for connecting to OCI +type OCIConfig struct { + ConfigMap ConfigMapDetails `json:"configMap,omitempty"` + PrivateKey ConfigPrivateKey `json:"privateKey,omitempty"` + MountPath string `json:"mountPath,omitempty"` +} + +type ConfigPrivateKey struct { + SecretName string `json:"secret,omitempty"` +} + +// AzureConfig defines the configmap name and secret name used for connecting to Azure +type AzureConfig struct { + ConfigMap ConfigMapDetails `json:"configMap,omitempty"` } // DatabaseObserverStatus defines the observed state of DatabaseObserver type DatabaseObserverStatus struct { - Conditions []metav1.Condition `json:"conditions"` - Status string `json:"status,omitempty"` - ExporterConfig string `json:"exporterConfig"` - Version string `json:"version"` - Replicas int `json:"replicas,omitempty"` + Conditions []metav1.Condition `json:"conditions"` + Status string `json:"status,omitempty"` + MetricsConfig string `json:"metricsConfig"` + Version string `json:"version"` + Replicas int `json:"replicas,omitempty"` } //+kubebuilder:object:root=true @@ -170,7 +216,7 @@ type DatabaseObserverStatus struct { // +kubebuilder:resource:shortName="dbobserver";"dbobservers" // DatabaseObserver is the Schema for the databaseobservers API -// +kubebuilder:printcolumn:JSONPath=".status.exporterConfig",name="ExporterConfig",type=string +// +kubebuilder:printcolumn:JSONPath=".status.metricsConfig",name="MetricsConfig",type=string // +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string // +kubebuilder:printcolumn:JSONPath=".status.version",name="Version",type=string type DatabaseObserver struct { diff --git a/apis/observability/v1/databaseobserver_webhook.go b/apis/observability/v1/databaseobserver_webhook.go index 725069cb..4ec6a8ab 100644 --- a/apis/observability/v1/databaseobserver_webhook.go +++ b/apis/observability/v1/databaseobserver_webhook.go @@ -60,7 +60,7 @@ const ( AllowedExporterImage = "container-registry.oracle.com/database/observability-exporter" ErrorSpecValidationMissingConnString = "a required field for database connection string secret is missing or does not have a value" ErrorSpecValidationMissingDBUser = "a required field for database user secret is missing or does not have a value" - ErrorSpecValidationMissingDBVaultField = "a field for the OCI vault has a value but the other required field is missing or does not have a value" + ErrorSpecValidationMissingVaultField = "a field for configuring the vault has a value but the other required field(s) is missing or does not have a value" ErrorSpecValidationMissingOCIConfig = "a field(s) for the OCI Config is missing or does not have a value when fields for the OCI vault has values" ErrorSpecValidationMissingDBPasswordSecret = "a required field for the database password secret is missing or does not have a value" ErrorSpecExporterImageNotAllowed = "a different exporter image was found, only official database exporter container images are currently supported" @@ -107,55 +107,32 @@ func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Objec "Oracle database operator doesn't watch over this namespace")) } - // Check required secret for db user has value - if r.Spec.Database.DBUser.SecretName == "" { - e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbUser").Child("secret"), r.Spec.Database.DBUser.SecretName, - ErrorSpecValidationMissingDBUser)) - } - - // Check required secret for db connection string has value - if r.Spec.Database.DBConnectionString.SecretName == "" { - e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbConnectionString").Child("secret"), r.Spec.Database.DBConnectionString.SecretName, - ErrorSpecValidationMissingConnString)) - } - // The other vault field must have value if one does - if (r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName == "") || - (r.Spec.Database.DBPassword.VaultSecretName != "" && r.Spec.Database.DBPassword.VaultOCID == "") { + if (r.Spec.Database.OCIVault.VaultID != "" && r.Spec.Database.OCIVault.VaultPasswordSecret == "") || + (r.Spec.Database.OCIVault.VaultPasswordSecret != "" && r.Spec.Database.OCIVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword"), r.Spec.Database.DBPassword, - ErrorSpecValidationMissingDBVaultField)) + field.Invalid(field.NewPath("spec").Child("database").Child("oci"), r.Spec.Database.OCIVault, + ErrorSpecValidationMissingVaultField)) } - // if vault fields have value, ociConfig must have values - if r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName != "" && - (r.Spec.OCIConfig.SecretName == "" || r.Spec.OCIConfig.ConfigMapName == "") { - - e = append(e, - field.Invalid(field.NewPath("spec").Child("ociConfig"), r.Spec.OCIConfig, - ErrorSpecValidationMissingOCIConfig)) - } - - // If all of {DB Password Secret Name and vaultOCID+vaultSecretName} have no value, then error out - if r.Spec.Database.DBPassword.SecretName == "" && - r.Spec.Database.DBPassword.VaultOCID == "" && - r.Spec.Database.DBPassword.VaultSecretName == "" { + // The other vault field must have value if one does + if (r.Spec.Database.AzureVault.VaultID != "" && (r.Spec.Database.AzureVault.VaultPasswordSecret == "" && r.Spec.Database.AzureVault.VaultUsernameSecret == "")) || + (r.Spec.Database.AzureVault.VaultPasswordSecret != "" && r.Spec.Database.AzureVault.VaultID == "") || + (r.Spec.Database.AzureVault.VaultUsernameSecret != "" && r.Spec.Database.AzureVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword").Child("secret"), r.Spec.Database.DBPassword.SecretName, - ErrorSpecValidationMissingDBPasswordSecret)) + field.Invalid(field.NewPath("spec").Child("database").Child("azure"), r.Spec.Database.AzureVault, + ErrorSpecValidationMissingVaultField)) } // disallow usage of any other image than the observability-exporter - if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { - e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, - ErrorSpecExporterImageNotAllowed)) - } - + // temporarily disabled + //if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { + // e = append(e, + // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, + // ErrorSpecExporterImageNotAllowed)) + //} // Return if any errors if len(e) > 0 { return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) @@ -170,9 +147,9 @@ func (r *DatabaseObserver) ValidateUpdate(ctx context.Context, oldObj, newObj ru var e field.ErrorList // disallow usage of any other image than the observability-exporter - if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { + if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, ErrorSpecExporterImageNotAllowed)) } // Return if any errors diff --git a/apis/observability/v1/zz_generated.deepcopy.go b/apis/observability/v1/zz_generated.deepcopy.go index 4924216f..0991c186 100644 --- a/apis/observability/v1/zz_generated.deepcopy.go +++ b/apis/observability/v1/zz_generated.deepcopy.go @@ -50,174 +50,158 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConfigMapDetails) DeepCopyInto(out *ConfigMapDetails) { +func (in *AdditionalWalletSecrets) DeepCopyInto(out *AdditionalWalletSecrets) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapDetails. -func (in *ConfigMapDetails) DeepCopy() *ConfigMapDetails { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalWalletSecrets. +func (in *AdditionalWalletSecrets) DeepCopy() *AdditionalWalletSecrets { if in == nil { return nil } - out := new(ConfigMapDetails) + out := new(AdditionalWalletSecrets) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DBSecret) DeepCopyInto(out *DBSecret) { +func (in *AzureConfig) DeepCopyInto(out *AzureConfig) { *out = *in + out.ConfigMap = in.ConfigMap } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecret. -func (in *DBSecret) DeepCopy() *DBSecret { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureConfig. +func (in *AzureConfig) DeepCopy() *AzureConfig { if in == nil { return nil } - out := new(DBSecret) + out := new(AzureConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DBSecretWithVault) DeepCopyInto(out *DBSecretWithVault) { +func (in *ConfigMapDetails) DeepCopyInto(out *ConfigMapDetails) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecretWithVault. -func (in *DBSecretWithVault) DeepCopy() *DBSecretWithVault { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapDetails. +func (in *ConfigMapDetails) DeepCopy() *ConfigMapDetails { if in == nil { return nil } - out := new(DBSecretWithVault) + out := new(ConfigMapDetails) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserver) DeepCopyInto(out *DatabaseObserver) { +func (in *ConfigPrivateKey) DeepCopyInto(out *ConfigPrivateKey) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserver. -func (in *DatabaseObserver) DeepCopy() *DatabaseObserver { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigPrivateKey. +func (in *ConfigPrivateKey) DeepCopy() *ConfigPrivateKey { if in == nil { return nil } - out := new(DatabaseObserver) + out := new(ConfigPrivateKey) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DatabaseObserver) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBAzureVault) DeepCopyInto(out *DBAzureVault) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBAzureVault. +func (in *DBAzureVault) DeepCopy() *DBAzureVault { + if in == nil { + return nil } - return nil + out := new(DBAzureVault) + in.DeepCopyInto(out) + return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverConfigMap) DeepCopyInto(out *DatabaseObserverConfigMap) { +func (in *DBOCIVault) DeepCopyInto(out *DBOCIVault) { *out = *in - out.Configmap = in.Configmap } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverConfigMap. -func (in *DatabaseObserverConfigMap) DeepCopy() *DatabaseObserverConfigMap { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBOCIVault. +func (in *DBOCIVault) DeepCopy() *DBOCIVault { if in == nil { return nil } - out := new(DatabaseObserverConfigMap) + out := new(DBOCIVault) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverDatabase) DeepCopyInto(out *DatabaseObserverDatabase) { +func (in *DBSecret) DeepCopyInto(out *DBSecret) { *out = *in - out.DBUser = in.DBUser - out.DBPassword = in.DBPassword - out.DBWallet = in.DBWallet - out.DBConnectionString = in.DBConnectionString } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDatabase. -func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecret. +func (in *DBSecret) DeepCopy() *DBSecret { if in == nil { return nil } - out := new(DatabaseObserverDatabase) + out := new(DBSecret) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverDeployment) DeepCopyInto(out *DatabaseObserverDeployment) { +func (in *DatabaseConfig) DeepCopyInto(out *DatabaseConfig) { *out = *in - if in.SecurityContext != nil { - in, out := &in.SecurityContext, &out.SecurityContext - *out = new(corev1.SecurityContext) - (*in).DeepCopyInto(*out) - } - if in.ExporterArgs != nil { - in, out := &in.ExporterArgs, &out.ExporterArgs - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ExporterCommands != nil { - in, out := &in.ExporterCommands, &out.ExporterCommands - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ExporterEnvs != nil { - in, out := &in.ExporterEnvs, &out.ExporterEnvs - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.DeploymentPodTemplate.DeepCopyInto(&out.DeploymentPodTemplate) + out.DBUser = in.DBUser + out.DBPassword = in.DBPassword + out.DBConnectionString = in.DBConnectionString + out.OCIVault = in.OCIVault + out.AzureVault = in.AzureVault } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDeployment. -func (in *DatabaseObserverDeployment) DeepCopy() *DatabaseObserverDeployment { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseConfig. +func (in *DatabaseConfig) DeepCopy() *DatabaseConfig { if in == nil { return nil } - out := new(DatabaseObserverDeployment) + out := new(DatabaseConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverExporterConfig) DeepCopyInto(out *DatabaseObserverExporterConfig) { +func (in *DatabaseObserver) DeepCopyInto(out *DatabaseObserver) { *out = *in - in.Deployment.DeepCopyInto(&out.Deployment) - in.Service.DeepCopyInto(&out.Service) + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverExporterConfig. -func (in *DatabaseObserverExporterConfig) DeepCopy() *DatabaseObserverExporterConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserver. +func (in *DatabaseObserver) DeepCopy() *DatabaseObserver { if in == nil { return nil } - out := new(DatabaseObserverExporterConfig) + out := new(DatabaseObserver) in.DeepCopyInto(out) return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatabaseObserver) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatabaseObserverList) DeepCopyInto(out *DatabaseObserverList) { *out = *in @@ -251,15 +235,68 @@ func (in *DatabaseObserverList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { +func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { *out = *in - if in.Ports != nil { - in, out := &in.Ports, &out.Ports - *out = make([]corev1.ServicePort, len(*in)) + out.Database = in.Database + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make(map[string]MultiDatabaseConfig, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Wallet.DeepCopyInto(&out.Wallet) + in.Deployment.DeepCopyInto(&out.Deployment) + in.Service.DeepCopyInto(&out.Service) + in.ServiceMonitor.DeepCopyInto(&out.ServiceMonitor) + out.ExporterConfig = in.ExporterConfig + out.OCIConfig = in.OCIConfig + out.AzureConfig = in.AzureConfig + in.Metrics.DeepCopyInto(&out.Metrics) + out.Log = in.Log + if in.InheritLabels != nil { + in, out := &in.InheritLabels, &out.InheritLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Sidecar.DeepCopyInto(&out.Sidecar) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. +func (in *DatabaseObserverSpec) DeepCopy() *DatabaseObserverSpec { + if in == nil { + return nil + } + out := new(DatabaseObserverSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverStatus. +func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { + if in == nil { + return nil + } + out := new(DatabaseObserverStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { + *out = *in if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -269,86 +306,114 @@ func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverService. -func (in *DatabaseObserverService) DeepCopy() *DatabaseObserverService { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentPodTemplate. +func (in *DeploymentPodTemplate) DeepCopy() *DeploymentPodTemplate { if in == nil { return nil } - out := new(DatabaseObserverService) + out := new(DeploymentPodTemplate) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { +func (in *ExporterConfig) DeepCopyInto(out *ExporterConfig) { *out = *in - out.Database = in.Database - in.Exporter.DeepCopyInto(&out.Exporter) - out.ExporterConfig = in.ExporterConfig - in.Prometheus.DeepCopyInto(&out.Prometheus) - out.OCIConfig = in.OCIConfig - out.Log = in.Log - if in.InheritLabels != nil { - in, out := &in.InheritLabels, &out.InheritLabels + out.ConfigMap = in.ConfigMap +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterConfig. +func (in *ExporterConfig) DeepCopy() *ExporterConfig { + if in == nil { + return nil + } + out := new(ExporterConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExporterDeployment) DeepCopyInto(out *ExporterDeployment) { + *out = *in + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.SecurityContext) + (*in).DeepCopyInto(*out) + } + if in.PodSecurityContext != nil { + in, out := &in.PodSecurityContext, &out.PodSecurityContext + *out = new(corev1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.ExporterArgs != nil { + in, out := &in.ExporterArgs, &out.ExporterArgs *out = make([]string, len(*in)) copy(*out, *in) } - if in.ExporterSidecars != nil { - in, out := &in.ExporterSidecars, &out.ExporterSidecars - *out = make([]corev1.Container, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) + if in.ExporterCommands != nil { + in, out := &in.ExporterCommands, &out.ExporterCommands + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterEnvs != nil { + in, out := &in.ExporterEnvs, &out.ExporterEnvs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val } } - if in.SideCarVolumes != nil { - in, out := &in.SideCarVolumes, &out.SideCarVolumes - *out = make([]corev1.Volume, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val } } + in.DeploymentPodTemplate.DeepCopyInto(&out.DeploymentPodTemplate) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. -func (in *DatabaseObserverSpec) DeepCopy() *DatabaseObserverSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterDeployment. +func (in *ExporterDeployment) DeepCopy() *ExporterDeployment { if in == nil { return nil } - out := new(DatabaseObserverSpec) + out := new(ExporterDeployment) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { +func (in *ExporterService) DeepCopyInto(out *ExporterService) { *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]corev1.ServicePort, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverStatus. -func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterService. +func (in *ExporterService) DeepCopy() *ExporterService { if in == nil { return nil } - out := new(DatabaseObserverStatus) + out := new(ExporterService) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { +func (in *ExporterServiceMonitor) DeepCopyInto(out *ExporterServiceMonitor) { *out = *in - if in.SecurityContext != nil { - in, out := &in.SecurityContext, &out.SecurityContext - *out = new(corev1.PodSecurityContext) - (*in).DeepCopyInto(*out) - } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -356,14 +421,26 @@ func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { (*out)[key] = val } } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(monitoringv1.NamespaceSelector) + (*in).DeepCopyInto(*out) + } + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]monitoringv1.Endpoint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentPodTemplate. -func (in *DeploymentPodTemplate) DeepCopy() *DeploymentPodTemplate { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterServiceMonitor. +func (in *ExporterServiceMonitor) DeepCopy() *ExporterServiceMonitor { if in == nil { return nil } - out := new(DeploymentPodTemplate) + out := new(ExporterServiceMonitor) in.DeepCopyInto(out) return out } @@ -401,81 +478,120 @@ func (in *LogVolume) DeepCopy() *LogVolume { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LogVolumePVClaim) DeepCopyInto(out *LogVolumePVClaim) { +func (in *LogVolumePVC) DeepCopyInto(out *LogVolumePVC) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolumePVClaim. -func (in *LogVolumePVClaim) DeepCopy() *LogVolumePVClaim { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolumePVC. +func (in *LogVolumePVC) DeepCopy() *LogVolumePVC { if in == nil { return nil } - out := new(LogVolumePVClaim) + out := new(LogVolumePVC) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { +func (in *MetricsConfig) DeepCopyInto(out *MetricsConfig) { *out = *in + if in.Configmap != nil { + in, out := &in.Configmap, &out.Configmap + *out = make([]ConfigMapDetails, len(*in)) + copy(*out, *in) + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfigSpec. -func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsConfig. +func (in *MetricsConfig) DeepCopy() *MetricsConfig { if in == nil { return nil } - out := new(OCIConfigSpec) + out := new(MetricsConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrometheusConfig) DeepCopyInto(out *PrometheusConfig) { +func (in *MultiDatabaseConfig) DeepCopyInto(out *MultiDatabaseConfig) { *out = *in - in.ServiceMonitor.DeepCopyInto(&out.ServiceMonitor) + out.DBUser = in.DBUser + out.DBPassword = in.DBPassword + out.DBConnectionString = in.DBConnectionString } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusConfig. -func (in *PrometheusConfig) DeepCopy() *PrometheusConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiDatabaseConfig. +func (in *MultiDatabaseConfig) DeepCopy() *MultiDatabaseConfig { if in == nil { return nil } - out := new(PrometheusConfig) + out := new(MultiDatabaseConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrometheusServiceMonitor) DeepCopyInto(out *PrometheusServiceMonitor) { +func (in *OCIConfig) DeepCopyInto(out *OCIConfig) { *out = *in - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + out.ConfigMap = in.ConfigMap + out.PrivateKey = in.PrivateKey +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfig. +func (in *OCIConfig) DeepCopy() *OCIConfig { + if in == nil { + return nil } - if in.NamespaceSelector != nil { - in, out := &in.NamespaceSelector, &out.NamespaceSelector - *out = new(monitoringv1.NamespaceSelector) - (*in).DeepCopyInto(*out) + out := new(OCIConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SidecarConfig) DeepCopyInto(out *SidecarConfig) { + *out = *in + if in.Containers != nil { + in, out := &in.Containers, &out.Containers + *out = make([]corev1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } - if in.Endpoints != nil { - in, out := &in.Endpoints, &out.Endpoints - *out = make([]monitoringv1.Endpoint, len(*in)) + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]corev1.Volume, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusServiceMonitor. -func (in *PrometheusServiceMonitor) DeepCopy() *PrometheusServiceMonitor { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidecarConfig. +func (in *SidecarConfig) DeepCopy() *SidecarConfig { + if in == nil { + return nil + } + out := new(SidecarConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WalletSecret) DeepCopyInto(out *WalletSecret) { + *out = *in + if in.AdditionalWallets != nil { + in, out := &in.AdditionalWallets, &out.AdditionalWallets + *out = make([]AdditionalWalletSecrets, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WalletSecret. +func (in *WalletSecret) DeepCopy() *WalletSecret { if in == nil { return nil } - out := new(PrometheusServiceMonitor) + out := new(WalletSecret) in.DeepCopyInto(out) return out } diff --git a/apis/observability/v1alpha1/databaseobserver_types.go b/apis/observability/v1alpha1/databaseobserver_types.go index 53bc32e7..8d72b7b4 100644 --- a/apis/observability/v1alpha1/databaseobserver_types.go +++ b/apis/observability/v1alpha1/databaseobserver_types.go @@ -48,79 +48,107 @@ type StatusEnum string // DatabaseObserverSpec defines the desired state of DatabaseObserver type DatabaseObserverSpec struct { - Database DatabaseObserverDatabase `json:"database,omitempty"` - Exporter DatabaseObserverExporterConfig `json:"exporter,omitempty"` - ExporterConfig DatabaseObserverConfigMap `json:"configuration,omitempty"` - Prometheus PrometheusConfig `json:"prometheus,omitempty"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` - Replicas int32 `json:"replicas,omitempty"` - Log LogConfig `json:"log,omitempty"` - InheritLabels []string `json:"inheritLabels,omitempty"` - ExporterSidecars []corev1.Container `json:"sidecars,omitempty"` - SideCarVolumes []corev1.Volume `json:"sidecarVolumes,omitempty"` + Database DatabaseConfig `json:"database,omitempty"` + Databases map[string]MultiDatabaseConfig `json:"databases,omitempty"` + Wallet WalletSecret `json:"wallet,omitempty"` + Deployment ExporterDeployment `json:"deployment,omitempty"` + Service ExporterService `json:"service,omitempty"` + ServiceMonitor ExporterServiceMonitor `json:"serviceMonitor,omitempty"` + ExporterConfig ExporterConfig `json:"exporterConfig,omitempty"` + OCIConfig OCIConfig `json:"ociConfig,omitempty"` + AzureConfig AzureConfig `json:"azureConfig,omitempty"` + Metrics MetricsConfig `json:"metrics,omitempty"` + InheritLabels []string `json:"inheritLabels,omitempty"` + Log LogConfig `json:"log,omitempty"` + Sidecar SidecarConfig `json:"sidecar,omitempty"` + Replicas int32 `json:"replicas,omitempty"` +} + +// SidecarConfig defines sidecar containers and volumes to add +type SidecarConfig struct { + Containers []corev1.Container `json:"containers,omitempty"` + Volumes []corev1.Volume `json:"volumes,omitempty"` +} + +// ExporterConfig defines configMap used for exporter configuration +type ExporterConfig struct { + ConfigMap ConfigMapDetails `json:"configMap,omitempty"` + MountPath string `json:"mountPath,omitempty"` } // LogConfig defines the configuration details relation to the logs of DatabaseObserver type LogConfig struct { - Disable bool `json:"disable,omitempty"` - Destination string `json:"destination,omitempty"` - Filename string `json:"filename,omitempty"` - Volume LogVolume `json:"volume,omitempty"` + Disable bool `json:"disable,omitempty"` + Destination string `json:"destination,omitempty"` + Filename string `json:"filename,omitempty"` + Volume LogVolume `json:"volume,omitempty"` } - +// LogVolume defines the shared volume between the exporter container and other containers type LogVolume struct { - PersistentVolumeClaim LogVolumePVClaim `json:"persistentVolumeClaim,omitempty"` + Name string `json:"name,omitempty"` + PersistentVolumeClaim LogVolumePVC `json:"persistentVolumeClaim,omitempty"` } -type LogVolumePVClaim struct { +// LogVolumePVC defines the PVC in which to store the logs +type LogVolumePVC struct { ClaimName string `json:"claimName,omitempty"` } -// DatabaseObserverDatabase defines the database details used for DatabaseObserver -type DatabaseObserverDatabase struct { - DBUser DBSecret `json:"dbUser,omitempty"` - DBPassword DBSecretWithVault `json:"dbPassword,omitempty"` - DBWallet DBSecret `json:"dbWallet,omitempty"` - DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` +// DatabaseConfig defines the database details used for DatabaseObserver +type DatabaseConfig struct { + DBUser DBSecret `json:"dbUser,omitempty"` + DBPassword DBSecret `json:"dbPassword,omitempty"` + DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` + OCIVault DBOCIVault `json:"oci,omitempty"` + AzureVault DBAzureVault `json:"azure,omitempty"` +} + +// MultiDatabaseConfig defines each database details used for DatabaseObserver +type MultiDatabaseConfig struct { + DBUser DBSecret `json:"dbUser,omitempty"` + DBPassword DBSecret `json:"dbPassword,omitempty"` + DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` +} + +// DBAzureVault defines Azure Vault details +type DBAzureVault struct { + VaultID string `json:"vaultID,omitempty"` + VaultUsernameSecret string `json:"vaultUsernameSecret,omitempty"` + VaultPasswordSecret string `json:"vaultPasswordSecret,omitempty"` } -// DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver -type DatabaseObserverExporterConfig struct { - Deployment DatabaseObserverDeployment `json:"deployment,omitempty"` - Service DatabaseObserverService `json:"service,omitempty"` +// DBOCIVault defines OCI Vault details +type DBOCIVault struct { + VaultID string `json:"vaultID,omitempty"` + VaultPasswordSecret string `json:"vaultPasswordSecret,omitempty"` } -// DatabaseObserverDeployment defines the exporter deployment component of DatabaseObserver -type DatabaseObserverDeployment struct { - ExporterImage string `json:"image,omitempty"` - SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` - ExporterArgs []string `json:"args,omitempty"` - ExporterCommands []string `json:"commands,omitempty"` - ExporterEnvs map[string]string `json:"env,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` +// ExporterDeployment defines the exporter deployment component of DatabaseObserver +type ExporterDeployment struct { + ExporterImage string `json:"image,omitempty"` + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"` + ExporterArgs []string `json:"args,omitempty"` + ExporterCommands []string `json:"commands,omitempty"` + ExporterEnvs map[string]string `json:"env,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` } // DeploymentPodTemplate defines the labels for the DatabaseObserver pods component of a deployment type DeploymentPodTemplate struct { - SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` - Labels map[string]string `json:"labels,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } -// DatabaseObserverService defines the exporter service component of DatabaseObserver -type DatabaseObserverService struct { +// ExporterService defines the exporter service component of DatabaseObserver +type ExporterService struct { Ports []corev1.ServicePort `json:"ports,omitempty"` Labels map[string]string `json:"labels,omitempty"` } -// PrometheusConfig defines the generated resources for Prometheus -type PrometheusConfig struct { - ServiceMonitor PrometheusServiceMonitor `json:"serviceMonitor,omitempty"` -} - -// PrometheusServiceMonitor defines DatabaseObserver servicemonitor spec -type PrometheusServiceMonitor struct { +// ExporterServiceMonitor defines DatabaseObserver servicemonitor spec +type ExporterServiceMonitor struct { Labels map[string]string `json:"labels,omitempty"` NamespaceSelector *monitorv1.NamespaceSelector `json:"namespaceSelector,omitempty"` Endpoints []monitorv1.Endpoint `json:"endpoints,omitempty"` @@ -130,40 +158,57 @@ type PrometheusServiceMonitor struct { type DBSecret struct { Key string `json:"key,omitempty"` SecretName string `json:"secret,omitempty"` + EnvName string `json:"envName,omitempty"` } -// DBSecretWithVault defines secrets used in reference with vault fields -type DBSecretWithVault struct { - Key string `json:"key,omitempty"` - SecretName string `json:"secret,omitempty"` - VaultOCID string `json:"vaultOCID,omitempty"` - VaultSecretName string `json:"vaultSecretName,omitempty"` +// WalletSecret defines secret and where the wallet will be mounted if provided +type WalletSecret struct { + SecretName string `json:"secret,omitempty"` + MountPath string `json:"mountPath,omitempty"` + AdditionalWallets []AdditionalWalletSecrets `json:"additional,omitempty"` } -// DatabaseObserverConfigMap defines configMap used for metrics configuration -type DatabaseObserverConfigMap struct { - Configmap ConfigMapDetails `json:"configMap,omitempty"` +// AdditionalWalletSecrets defines multiple other secrets and where the wallet will be mounted if provided +type AdditionalWalletSecrets struct { + Name string `json:"name,omitempty"` + SecretName string `json:"secret,omitempty"` + MountPath string `json:"mountPath,omitempty"` } -// ConfigMapDetails defines the configmap name +// MetricsConfig defines configMap used for multiple metrics TOML configuration +type MetricsConfig struct { + Configmap []ConfigMapDetails `json:"configMap,omitempty"` +} + +// ConfigMapDetails defines the configmap name used by the exporterConfig and metricsConfig type ConfigMapDetails struct { Key string `json:"key,omitempty"` Name string `json:"name,omitempty"` } -// OCIConfigSpec defines the configmap name and secret name used for connecting to OCI -type OCIConfigSpec struct { - ConfigMapName string `json:"configMapName,omitempty"` - SecretName string `json:"secretName,omitempty"` +// OCIConfig defines the configmap name and secret name used for connecting to OCI +type OCIConfig struct { + ConfigMap ConfigMapDetails `json:"configMap,omitempty"` + PrivateKey ConfigPrivateKey `json:"privateKey,omitempty"` + MountPath string `json:"mountPath,omitempty"` +} + +type ConfigPrivateKey struct { + SecretName string `json:"secret,omitempty"` +} + +// AzureConfig defines the configmap name and secret name used for connecting to Azure +type AzureConfig struct { + ConfigMap ConfigMapDetails `json:"configMap,omitempty"` } // DatabaseObserverStatus defines the observed state of DatabaseObserver type DatabaseObserverStatus struct { - Conditions []metav1.Condition `json:"conditions"` - Status string `json:"status,omitempty"` - ExporterConfig string `json:"exporterConfig"` - Version string `json:"version"` - Replicas int `json:"replicas,omitempty"` + Conditions []metav1.Condition `json:"conditions"` + Status string `json:"status,omitempty"` + MetricsConfig string `json:"metricsConfig"` + Version string `json:"version"` + Replicas int `json:"replicas,omitempty"` } //+kubebuilder:object:root=true @@ -171,7 +216,7 @@ type DatabaseObserverStatus struct { // +kubebuilder:resource:shortName="dbobserver";"dbobservers" // DatabaseObserver is the Schema for the databaseobservers API -// +kubebuilder:printcolumn:JSONPath=".status.exporterConfig",name="ExporterConfig",type=string +// +kubebuilder:printcolumn:JSONPath=".status.metricsConfig",name="MetricsConfig",type=string // +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string // +kubebuilder:printcolumn:JSONPath=".status.version",name="Version",type=string type DatabaseObserver struct { diff --git a/apis/observability/v1alpha1/databaseobserver_webhook.go b/apis/observability/v1alpha1/databaseobserver_webhook.go index c5080445..9a11a0f3 100644 --- a/apis/observability/v1alpha1/databaseobserver_webhook.go +++ b/apis/observability/v1alpha1/databaseobserver_webhook.go @@ -60,7 +60,7 @@ const ( AllowedExporterImage = "container-registry.oracle.com/database/observability-exporter" ErrorSpecValidationMissingConnString = "a required field for database connection string secret is missing or does not have a value" ErrorSpecValidationMissingDBUser = "a required field for database user secret is missing or does not have a value" - ErrorSpecValidationMissingDBVaultField = "a field for the OCI vault has a value but the other required field is missing or does not have a value" + ErrorSpecValidationMissingVaultField = "a field for configuring the vault has a value but the other required field(s) is missing or does not have a value" ErrorSpecValidationMissingOCIConfig = "a field(s) for the OCI Config is missing or does not have a value when fields for the OCI vault has values" ErrorSpecValidationMissingDBPasswordSecret = "a required field for the database password secret is missing or does not have a value" ErrorSpecExporterImageNotAllowed = "a different exporter image was found, only official database exporter container images are currently supported" @@ -107,54 +107,32 @@ func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Objec "Oracle database operator doesn't watch over this namespace")) } - // Check required secret for db user has value - if r.Spec.Database.DBUser.SecretName == "" { - e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbUser").Child("secret"), r.Spec.Database.DBUser.SecretName, - ErrorSpecValidationMissingDBUser)) - } - - // Check required secret for db connection string has value - if r.Spec.Database.DBConnectionString.SecretName == "" { - e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbConnectionString").Child("secret"), r.Spec.Database.DBConnectionString.SecretName, - ErrorSpecValidationMissingConnString)) - } - // The other vault field must have value if one does - if (r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName == "") || - (r.Spec.Database.DBPassword.VaultSecretName != "" && r.Spec.Database.DBPassword.VaultOCID == "") { - - e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword"), r.Spec.Database.DBPassword, - ErrorSpecValidationMissingDBVaultField)) - } - - // if vault fields have value, ociConfig must have values - if r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName != "" && - (r.Spec.OCIConfig.SecretName == "" || r.Spec.OCIConfig.ConfigMapName == "") { + if (r.Spec.Database.OCIVault.VaultID != "" && r.Spec.Database.OCIVault.VaultPasswordSecret == "") || + (r.Spec.Database.OCIVault.VaultPasswordSecret != "" && r.Spec.Database.OCIVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("ociConfig"), r.Spec.OCIConfig, - ErrorSpecValidationMissingOCIConfig)) + field.Invalid(field.NewPath("spec").Child("database").Child("oci"), r.Spec.Database.OCIVault, + ErrorSpecValidationMissingVaultField)) } - // If all of {DB Password Secret Name and vaultOCID+vaultSecretName} have no value, then error out - if r.Spec.Database.DBPassword.SecretName == "" && - r.Spec.Database.DBPassword.VaultOCID == "" && - r.Spec.Database.DBPassword.VaultSecretName == "" { + // The other vault field must have value if one does + if (r.Spec.Database.AzureVault.VaultID != "" && (r.Spec.Database.AzureVault.VaultPasswordSecret == "" && r.Spec.Database.AzureVault.VaultUsernameSecret == "")) || + (r.Spec.Database.AzureVault.VaultPasswordSecret != "" && r.Spec.Database.AzureVault.VaultID == "") || + (r.Spec.Database.AzureVault.VaultUsernameSecret != "" && r.Spec.Database.AzureVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword").Child("secret"), r.Spec.Database.DBPassword.SecretName, - ErrorSpecValidationMissingDBPasswordSecret)) + field.Invalid(field.NewPath("spec").Child("database").Child("azure"), r.Spec.Database.AzureVault, + ErrorSpecValidationMissingVaultField)) } // disallow usage of any other image than the observability-exporter - if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { - e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, - ErrorSpecExporterImageNotAllowed)) - } + // temporarily disabled + //if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { + // e = append(e, + // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, + // ErrorSpecExporterImageNotAllowed)) + //} // Return if any errors if len(e) > 0 { @@ -170,9 +148,9 @@ func (r *DatabaseObserver) ValidateUpdate(ctx context.Context, oldObj, newObj ru var e field.ErrorList // disallow usage of any other image than the observability-exporter - if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { + if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, ErrorSpecExporterImageNotAllowed)) } // Return if any errors diff --git a/apis/observability/v1alpha1/zz_generated.deepcopy.go b/apis/observability/v1alpha1/zz_generated.deepcopy.go index 4b2a29b0..c5e76104 100644 --- a/apis/observability/v1alpha1/zz_generated.deepcopy.go +++ b/apis/observability/v1alpha1/zz_generated.deepcopy.go @@ -50,174 +50,158 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConfigMapDetails) DeepCopyInto(out *ConfigMapDetails) { +func (in *AdditionalWalletSecrets) DeepCopyInto(out *AdditionalWalletSecrets) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapDetails. -func (in *ConfigMapDetails) DeepCopy() *ConfigMapDetails { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalWalletSecrets. +func (in *AdditionalWalletSecrets) DeepCopy() *AdditionalWalletSecrets { if in == nil { return nil } - out := new(ConfigMapDetails) + out := new(AdditionalWalletSecrets) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DBSecret) DeepCopyInto(out *DBSecret) { +func (in *AzureConfig) DeepCopyInto(out *AzureConfig) { *out = *in + out.ConfigMap = in.ConfigMap } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecret. -func (in *DBSecret) DeepCopy() *DBSecret { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureConfig. +func (in *AzureConfig) DeepCopy() *AzureConfig { if in == nil { return nil } - out := new(DBSecret) + out := new(AzureConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DBSecretWithVault) DeepCopyInto(out *DBSecretWithVault) { +func (in *ConfigMapDetails) DeepCopyInto(out *ConfigMapDetails) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecretWithVault. -func (in *DBSecretWithVault) DeepCopy() *DBSecretWithVault { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapDetails. +func (in *ConfigMapDetails) DeepCopy() *ConfigMapDetails { if in == nil { return nil } - out := new(DBSecretWithVault) + out := new(ConfigMapDetails) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserver) DeepCopyInto(out *DatabaseObserver) { +func (in *ConfigPrivateKey) DeepCopyInto(out *ConfigPrivateKey) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserver. -func (in *DatabaseObserver) DeepCopy() *DatabaseObserver { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigPrivateKey. +func (in *ConfigPrivateKey) DeepCopy() *ConfigPrivateKey { if in == nil { return nil } - out := new(DatabaseObserver) + out := new(ConfigPrivateKey) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DatabaseObserver) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBAzureVault) DeepCopyInto(out *DBAzureVault) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBAzureVault. +func (in *DBAzureVault) DeepCopy() *DBAzureVault { + if in == nil { + return nil } - return nil + out := new(DBAzureVault) + in.DeepCopyInto(out) + return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverConfigMap) DeepCopyInto(out *DatabaseObserverConfigMap) { +func (in *DBOCIVault) DeepCopyInto(out *DBOCIVault) { *out = *in - out.Configmap = in.Configmap } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverConfigMap. -func (in *DatabaseObserverConfigMap) DeepCopy() *DatabaseObserverConfigMap { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBOCIVault. +func (in *DBOCIVault) DeepCopy() *DBOCIVault { if in == nil { return nil } - out := new(DatabaseObserverConfigMap) + out := new(DBOCIVault) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverDatabase) DeepCopyInto(out *DatabaseObserverDatabase) { +func (in *DBSecret) DeepCopyInto(out *DBSecret) { *out = *in - out.DBUser = in.DBUser - out.DBPassword = in.DBPassword - out.DBWallet = in.DBWallet - out.DBConnectionString = in.DBConnectionString } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDatabase. -func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecret. +func (in *DBSecret) DeepCopy() *DBSecret { if in == nil { return nil } - out := new(DatabaseObserverDatabase) + out := new(DBSecret) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverDeployment) DeepCopyInto(out *DatabaseObserverDeployment) { +func (in *DatabaseConfig) DeepCopyInto(out *DatabaseConfig) { *out = *in - if in.SecurityContext != nil { - in, out := &in.SecurityContext, &out.SecurityContext - *out = new(v1.SecurityContext) - (*in).DeepCopyInto(*out) - } - if in.ExporterArgs != nil { - in, out := &in.ExporterArgs, &out.ExporterArgs - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ExporterCommands != nil { - in, out := &in.ExporterCommands, &out.ExporterCommands - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ExporterEnvs != nil { - in, out := &in.ExporterEnvs, &out.ExporterEnvs - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.DeploymentPodTemplate.DeepCopyInto(&out.DeploymentPodTemplate) + out.DBUser = in.DBUser + out.DBPassword = in.DBPassword + out.DBConnectionString = in.DBConnectionString + out.OCIVault = in.OCIVault + out.AzureVault = in.AzureVault } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDeployment. -func (in *DatabaseObserverDeployment) DeepCopy() *DatabaseObserverDeployment { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseConfig. +func (in *DatabaseConfig) DeepCopy() *DatabaseConfig { if in == nil { return nil } - out := new(DatabaseObserverDeployment) + out := new(DatabaseConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverExporterConfig) DeepCopyInto(out *DatabaseObserverExporterConfig) { +func (in *DatabaseObserver) DeepCopyInto(out *DatabaseObserver) { *out = *in - in.Deployment.DeepCopyInto(&out.Deployment) - in.Service.DeepCopyInto(&out.Service) + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverExporterConfig. -func (in *DatabaseObserverExporterConfig) DeepCopy() *DatabaseObserverExporterConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserver. +func (in *DatabaseObserver) DeepCopy() *DatabaseObserver { if in == nil { return nil } - out := new(DatabaseObserverExporterConfig) + out := new(DatabaseObserver) in.DeepCopyInto(out) return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatabaseObserver) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatabaseObserverList) DeepCopyInto(out *DatabaseObserverList) { *out = *in @@ -251,15 +235,68 @@ func (in *DatabaseObserverList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { +func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { *out = *in - if in.Ports != nil { - in, out := &in.Ports, &out.Ports - *out = make([]v1.ServicePort, len(*in)) + out.Database = in.Database + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make(map[string]MultiDatabaseConfig, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Wallet.DeepCopyInto(&out.Wallet) + in.Deployment.DeepCopyInto(&out.Deployment) + in.Service.DeepCopyInto(&out.Service) + in.ServiceMonitor.DeepCopyInto(&out.ServiceMonitor) + out.ExporterConfig = in.ExporterConfig + out.OCIConfig = in.OCIConfig + out.AzureConfig = in.AzureConfig + in.Metrics.DeepCopyInto(&out.Metrics) + if in.InheritLabels != nil { + in, out := &in.InheritLabels, &out.InheritLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + out.Log = in.Log + in.Sidecar.DeepCopyInto(&out.Sidecar) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. +func (in *DatabaseObserverSpec) DeepCopy() *DatabaseObserverSpec { + if in == nil { + return nil + } + out := new(DatabaseObserverSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverStatus. +func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { + if in == nil { + return nil + } + out := new(DatabaseObserverStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { + *out = *in if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -269,86 +306,114 @@ func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverService. -func (in *DatabaseObserverService) DeepCopy() *DatabaseObserverService { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentPodTemplate. +func (in *DeploymentPodTemplate) DeepCopy() *DeploymentPodTemplate { if in == nil { return nil } - out := new(DatabaseObserverService) + out := new(DeploymentPodTemplate) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { +func (in *ExporterConfig) DeepCopyInto(out *ExporterConfig) { *out = *in - out.Database = in.Database - in.Exporter.DeepCopyInto(&out.Exporter) - out.ExporterConfig = in.ExporterConfig - in.Prometheus.DeepCopyInto(&out.Prometheus) - out.OCIConfig = in.OCIConfig - out.Log = in.Log - if in.InheritLabels != nil { - in, out := &in.InheritLabels, &out.InheritLabels + out.ConfigMap = in.ConfigMap +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterConfig. +func (in *ExporterConfig) DeepCopy() *ExporterConfig { + if in == nil { + return nil + } + out := new(ExporterConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExporterDeployment) DeepCopyInto(out *ExporterDeployment) { + *out = *in + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.SecurityContext) + (*in).DeepCopyInto(*out) + } + if in.PodSecurityContext != nil { + in, out := &in.PodSecurityContext, &out.PodSecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.ExporterArgs != nil { + in, out := &in.ExporterArgs, &out.ExporterArgs *out = make([]string, len(*in)) copy(*out, *in) } - if in.ExporterSidecars != nil { - in, out := &in.ExporterSidecars, &out.ExporterSidecars - *out = make([]v1.Container, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) + if in.ExporterCommands != nil { + in, out := &in.ExporterCommands, &out.ExporterCommands + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterEnvs != nil { + in, out := &in.ExporterEnvs, &out.ExporterEnvs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val } } - if in.SideCarVolumes != nil { - in, out := &in.SideCarVolumes, &out.SideCarVolumes - *out = make([]v1.Volume, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val } } + in.DeploymentPodTemplate.DeepCopyInto(&out.DeploymentPodTemplate) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. -func (in *DatabaseObserverSpec) DeepCopy() *DatabaseObserverSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterDeployment. +func (in *ExporterDeployment) DeepCopy() *ExporterDeployment { if in == nil { return nil } - out := new(DatabaseObserverSpec) + out := new(ExporterDeployment) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { +func (in *ExporterService) DeepCopyInto(out *ExporterService) { *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]v1.ServicePort, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverStatus. -func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterService. +func (in *ExporterService) DeepCopy() *ExporterService { if in == nil { return nil } - out := new(DatabaseObserverStatus) + out := new(ExporterService) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { +func (in *ExporterServiceMonitor) DeepCopyInto(out *ExporterServiceMonitor) { *out = *in - if in.SecurityContext != nil { - in, out := &in.SecurityContext, &out.SecurityContext - *out = new(v1.PodSecurityContext) - (*in).DeepCopyInto(*out) - } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -356,14 +421,26 @@ func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { (*out)[key] = val } } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(monitoringv1.NamespaceSelector) + (*in).DeepCopyInto(*out) + } + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]monitoringv1.Endpoint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentPodTemplate. -func (in *DeploymentPodTemplate) DeepCopy() *DeploymentPodTemplate { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterServiceMonitor. +func (in *ExporterServiceMonitor) DeepCopy() *ExporterServiceMonitor { if in == nil { return nil } - out := new(DeploymentPodTemplate) + out := new(ExporterServiceMonitor) in.DeepCopyInto(out) return out } @@ -401,81 +478,120 @@ func (in *LogVolume) DeepCopy() *LogVolume { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LogVolumePVClaim) DeepCopyInto(out *LogVolumePVClaim) { +func (in *LogVolumePVC) DeepCopyInto(out *LogVolumePVC) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolumePVClaim. -func (in *LogVolumePVClaim) DeepCopy() *LogVolumePVClaim { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolumePVC. +func (in *LogVolumePVC) DeepCopy() *LogVolumePVC { if in == nil { return nil } - out := new(LogVolumePVClaim) + out := new(LogVolumePVC) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { +func (in *MetricsConfig) DeepCopyInto(out *MetricsConfig) { *out = *in + if in.Configmap != nil { + in, out := &in.Configmap, &out.Configmap + *out = make([]ConfigMapDetails, len(*in)) + copy(*out, *in) + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfigSpec. -func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsConfig. +func (in *MetricsConfig) DeepCopy() *MetricsConfig { if in == nil { return nil } - out := new(OCIConfigSpec) + out := new(MetricsConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrometheusConfig) DeepCopyInto(out *PrometheusConfig) { +func (in *MultiDatabaseConfig) DeepCopyInto(out *MultiDatabaseConfig) { *out = *in - in.ServiceMonitor.DeepCopyInto(&out.ServiceMonitor) + out.DBUser = in.DBUser + out.DBPassword = in.DBPassword + out.DBConnectionString = in.DBConnectionString } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusConfig. -func (in *PrometheusConfig) DeepCopy() *PrometheusConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiDatabaseConfig. +func (in *MultiDatabaseConfig) DeepCopy() *MultiDatabaseConfig { if in == nil { return nil } - out := new(PrometheusConfig) + out := new(MultiDatabaseConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrometheusServiceMonitor) DeepCopyInto(out *PrometheusServiceMonitor) { +func (in *OCIConfig) DeepCopyInto(out *OCIConfig) { *out = *in - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + out.ConfigMap = in.ConfigMap + out.PrivateKey = in.PrivateKey +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfig. +func (in *OCIConfig) DeepCopy() *OCIConfig { + if in == nil { + return nil } - if in.NamespaceSelector != nil { - in, out := &in.NamespaceSelector, &out.NamespaceSelector - *out = new(monitoringv1.NamespaceSelector) - (*in).DeepCopyInto(*out) + out := new(OCIConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SidecarConfig) DeepCopyInto(out *SidecarConfig) { + *out = *in + if in.Containers != nil { + in, out := &in.Containers, &out.Containers + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } - if in.Endpoints != nil { - in, out := &in.Endpoints, &out.Endpoints - *out = make([]monitoringv1.Endpoint, len(*in)) + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusServiceMonitor. -func (in *PrometheusServiceMonitor) DeepCopy() *PrometheusServiceMonitor { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidecarConfig. +func (in *SidecarConfig) DeepCopy() *SidecarConfig { + if in == nil { + return nil + } + out := new(SidecarConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WalletSecret) DeepCopyInto(out *WalletSecret) { + *out = *in + if in.AdditionalWallets != nil { + in, out := &in.AdditionalWallets, &out.AdditionalWallets + *out = make([]AdditionalWalletSecrets, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WalletSecret. +func (in *WalletSecret) DeepCopy() *WalletSecret { if in == nil { return nil } - out := new(PrometheusServiceMonitor) + out := new(WalletSecret) in.DeepCopyInto(out) return out } diff --git a/apis/observability/v4/databaseobserver_types.go b/apis/observability/v4/databaseobserver_types.go index 0e12c854..c37c67e2 100644 --- a/apis/observability/v4/databaseobserver_types.go +++ b/apis/observability/v4/databaseobserver_types.go @@ -48,78 +48,107 @@ type StatusEnum string // DatabaseObserverSpec defines the desired state of DatabaseObserver type DatabaseObserverSpec struct { - Database DatabaseObserverDatabase `json:"database,omitempty"` - Exporter DatabaseObserverExporterConfig `json:"exporter,omitempty"` - ExporterConfig DatabaseObserverConfigMap `json:"configuration,omitempty"` - Prometheus PrometheusConfig `json:"prometheus,omitempty"` - OCIConfig OCIConfigSpec `json:"ociConfig,omitempty"` - Replicas int32 `json:"replicas,omitempty"` - Log LogConfig `json:"log,omitempty"` - InheritLabels []string `json:"inheritLabels,omitempty"` - ExporterSidecars []corev1.Container `json:"sidecars,omitempty"` - SideCarVolumes []corev1.Volume `json:"sidecarVolumes,omitempty"` + Database DatabaseConfig `json:"database,omitempty"` + Databases map[string]MultiDatabaseConfig `json:"databases,omitempty"` + Wallet WalletSecret `json:"wallet,omitempty"` + Deployment ExporterDeployment `json:"deployment,omitempty"` + Service ExporterService `json:"service,omitempty"` + ServiceMonitor ExporterServiceMonitor `json:"serviceMonitor,omitempty"` + ExporterConfig ExporterConfig `json:"exporterConfig,omitempty"` + OCIConfig OCIConfig `json:"ociConfig,omitempty"` + AzureConfig AzureConfig `json:"azureConfig,omitempty"` + Metrics MetricsConfig `json:"metrics,omitempty"` + Log LogConfig `json:"log,omitempty"` + InheritLabels []string `json:"inheritLabels,omitempty"` + Sidecar SidecarConfig `json:"sidecar,omitempty"` + Replicas int32 `json:"replicas,omitempty"` +} + +// SidecarConfig defines sidecar containers and volumes to add +type SidecarConfig struct { + Containers []corev1.Container `json:"containers,omitempty"` + Volumes []corev1.Volume `json:"volumes,omitempty"` +} + +// ExporterConfig defines configMap used for exporter configuration +type ExporterConfig struct { + ConfigMap ConfigMapDetails `json:"configMap,omitempty"` + MountPath string `json:"mountPath,omitempty"` } // LogConfig defines the configuration details relation to the logs of DatabaseObserver type LogConfig struct { - Disable bool `json:"disable,omitempty"` - Destination string `json:"destination,omitempty"` - Filename string `json:"filename,omitempty"` - Volume LogVolume `json:"volume,omitempty"` + Disable bool `json:"disable,omitempty"` + Destination string `json:"destination,omitempty"` + Filename string `json:"filename,omitempty"` + Volume LogVolume `json:"volume,omitempty"` } +// LogVolume defines the shared volume between the exporter container and other containers type LogVolume struct { - PersistentVolumeClaim LogVolumePVClaim `json:"persistentVolumeClaim,omitempty"` + Name string `json:"name,omitempty"` + PersistentVolumeClaim LogVolumePVC `json:"persistentVolumeClaim,omitempty"` } -type LogVolumePVClaim struct { +// LogVolumePVC defines the PVC in which to store the logs +type LogVolumePVC struct { ClaimName string `json:"claimName,omitempty"` } -// DatabaseObserverDatabase defines the database details used for DatabaseObserver -type DatabaseObserverDatabase struct { - DBUser DBSecret `json:"dbUser,omitempty"` - DBPassword DBSecretWithVault `json:"dbPassword,omitempty"` - DBWallet DBSecret `json:"dbWallet,omitempty"` - DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` +// DatabaseConfig defines the database details used for DatabaseObserver +type DatabaseConfig struct { + DBUser DBSecret `json:"dbUser,omitempty"` + DBPassword DBSecret `json:"dbPassword,omitempty"` + DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` + OCIVault DBOCIVault `json:"oci,omitempty"` + AzureVault DBAzureVault `json:"azure,omitempty"` } -// DatabaseObserverExporterConfig defines the configuration details related to the exporters of DatabaseObserver -type DatabaseObserverExporterConfig struct { - Deployment DatabaseObserverDeployment `json:"deployment,omitempty"` - Service DatabaseObserverService `json:"service,omitempty"` +// MultiDatabaseConfig defines each database details used for DatabaseObserver +type MultiDatabaseConfig struct { + DBUser DBSecret `json:"dbUser,omitempty"` + DBPassword DBSecret `json:"dbPassword,omitempty"` + DBConnectionString DBSecret `json:"dbConnectionString,omitempty"` } -// DatabaseObserverDeployment defines the exporter deployment component of DatabaseObserver -type DatabaseObserverDeployment struct { - ExporterImage string `json:"image,omitempty"` - SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` - ExporterArgs []string `json:"args,omitempty"` - ExporterCommands []string `json:"commands,omitempty"` - ExporterEnvs map[string]string `json:"env,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` +// DBAzureVault defines Azure Vault details +type DBAzureVault struct { + VaultID string `json:"vaultID,omitempty"` + VaultUsernameSecret string `json:"vaultUsernameSecret,omitempty"` + VaultPasswordSecret string `json:"vaultPasswordSecret,omitempty"` +} + +// DBOCIVault defines OCI Vault details +type DBOCIVault struct { + VaultID string `json:"vaultID,omitempty"` + VaultPasswordSecret string `json:"vaultPasswordSecret,omitempty"` +} + +// ExporterDeployment defines the exporter deployment component of DatabaseObserver +type ExporterDeployment struct { + ExporterImage string `json:"image,omitempty"` + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"` + ExporterArgs []string `json:"args,omitempty"` + ExporterCommands []string `json:"commands,omitempty"` + ExporterEnvs map[string]string `json:"env,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + DeploymentPodTemplate DeploymentPodTemplate `json:"podTemplate,omitempty"` } // DeploymentPodTemplate defines the labels for the DatabaseObserver pods component of a deployment type DeploymentPodTemplate struct { - SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` - Labels map[string]string `json:"labels,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } -// DatabaseObserverService defines the exporter service component of DatabaseObserver -type DatabaseObserverService struct { +// ExporterService defines the exporter service component of DatabaseObserver +type ExporterService struct { Ports []corev1.ServicePort `json:"ports,omitempty"` Labels map[string]string `json:"labels,omitempty"` } -// PrometheusConfig defines the generated resources for Prometheus -type PrometheusConfig struct { - ServiceMonitor PrometheusServiceMonitor `json:"serviceMonitor,omitempty"` -} - -// PrometheusServiceMonitor defines DatabaseObserver servicemonitor spec -type PrometheusServiceMonitor struct { +// ExporterServiceMonitor defines DatabaseObserver servicemonitor spec +type ExporterServiceMonitor struct { Labels map[string]string `json:"labels,omitempty"` NamespaceSelector *monitorv1.NamespaceSelector `json:"namespaceSelector,omitempty"` Endpoints []monitorv1.Endpoint `json:"endpoints,omitempty"` @@ -129,40 +158,57 @@ type PrometheusServiceMonitor struct { type DBSecret struct { Key string `json:"key,omitempty"` SecretName string `json:"secret,omitempty"` + EnvName string `json:"envName,omitempty"` +} + +// WalletSecret defines secret and where the wallet will be mounted if provided +type WalletSecret struct { + SecretName string `json:"secret,omitempty"` + MountPath string `json:"mountPath,omitempty"` + AdditionalWallets []AdditionalWalletSecrets `json:"additional,omitempty"` } -// DBSecretWithVault defines secrets used in reference with vault fields -type DBSecretWithVault struct { - Key string `json:"key,omitempty"` - SecretName string `json:"secret,omitempty"` - VaultOCID string `json:"vaultOCID,omitempty"` - VaultSecretName string `json:"vaultSecretName,omitempty"` +// AdditionalWalletSecrets defines multiple other secrets and where the wallet will be mounted if provided +type AdditionalWalletSecrets struct { + Name string `json:"name,omitempty"` + SecretName string `json:"secret,omitempty"` + MountPath string `json:"mountPath,omitempty"` } -// DatabaseObserverConfigMap defines configMap used for metrics configuration -type DatabaseObserverConfigMap struct { - Configmap ConfigMapDetails `json:"configMap,omitempty"` +// MetricsConfig defines configMap used for multiple metrics TOML configuration +type MetricsConfig struct { + Configmap []ConfigMapDetails `json:"configMap,omitempty"` } -// ConfigMapDetails defines the configmap name +// ConfigMapDetails defines the configmap name used by the exporterConfig and metricsConfig type ConfigMapDetails struct { Key string `json:"key,omitempty"` Name string `json:"name,omitempty"` } -// OCIConfigSpec defines the configmap name and secret name used for connecting to OCI -type OCIConfigSpec struct { - ConfigMapName string `json:"configMapName,omitempty"` - SecretName string `json:"secretName,omitempty"` +// OCIConfig defines the configmap name and secret name used for connecting to OCI +type OCIConfig struct { + ConfigMap ConfigMapDetails `json:"configMap,omitempty"` + PrivateKey ConfigPrivateKey `json:"privateKey,omitempty"` + MountPath string `json:"mountPath,omitempty"` +} + +type ConfigPrivateKey struct { + SecretName string `json:"secret,omitempty"` +} + +// AzureConfig defines the configmap name and secret name used for connecting to Azure +type AzureConfig struct { + ConfigMap ConfigMapDetails `json:"configMap,omitempty"` } // DatabaseObserverStatus defines the observed state of DatabaseObserver type DatabaseObserverStatus struct { - Conditions []metav1.Condition `json:"conditions"` - Status string `json:"status,omitempty"` - ExporterConfig string `json:"exporterConfig"` - Version string `json:"version"` - Replicas int `json:"replicas,omitempty"` + Conditions []metav1.Condition `json:"conditions"` + Status string `json:"status,omitempty"` + MetricsConfig string `json:"metricsConfig"` + Version string `json:"version"` + Replicas int `json:"replicas,omitempty"` } //+kubebuilder:object:root=true @@ -170,7 +216,7 @@ type DatabaseObserverStatus struct { // +kubebuilder:resource:shortName="dbobserver";"dbobservers" // DatabaseObserver is the Schema for the databaseobservers API -// +kubebuilder:printcolumn:JSONPath=".status.exporterConfig",name="ExporterConfig",type=string +// +kubebuilder:printcolumn:JSONPath=".status.metricsConfig",name="MetricsConfig",type=string // +kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string // +kubebuilder:printcolumn:JSONPath=".status.version",name="Version",type=string // +kubebuilder:storageversion diff --git a/apis/observability/v4/databaseobserver_webhook.go b/apis/observability/v4/databaseobserver_webhook.go index 832f15dc..91418c7d 100644 --- a/apis/observability/v4/databaseobserver_webhook.go +++ b/apis/observability/v4/databaseobserver_webhook.go @@ -60,7 +60,7 @@ const ( AllowedExporterImage = "container-registry.oracle.com/database/observability-exporter" ErrorSpecValidationMissingConnString = "a required field for database connection string secret is missing or does not have a value" ErrorSpecValidationMissingDBUser = "a required field for database user secret is missing or does not have a value" - ErrorSpecValidationMissingDBVaultField = "a field for the OCI vault has a value but the other required field is missing or does not have a value" + ErrorSpecValidationMissingVaultField = "a field for configuring the vault has a value but the other required field(s) is missing or does not have a value" ErrorSpecValidationMissingOCIConfig = "a field(s) for the OCI Config is missing or does not have a value when fields for the OCI vault has values" ErrorSpecValidationMissingDBPasswordSecret = "a required field for the database password secret is missing or does not have a value" ErrorSpecExporterImageNotAllowed = "a different exporter image was found, only official database exporter container images are currently supported" @@ -105,54 +105,32 @@ func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Objec "Oracle database operator doesn't watch over this namespace")) } - // Check required secret for db user has value - if r.Spec.Database.DBUser.SecretName == "" { - e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbUser").Child("secret"), r.Spec.Database.DBUser.SecretName, - ErrorSpecValidationMissingDBUser)) - } - - // Check required secret for db connection string has value - if r.Spec.Database.DBConnectionString.SecretName == "" { - e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbConnectionString").Child("secret"), r.Spec.Database.DBConnectionString.SecretName, - ErrorSpecValidationMissingConnString)) - } - // The other vault field must have value if one does - if (r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName == "") || - (r.Spec.Database.DBPassword.VaultSecretName != "" && r.Spec.Database.DBPassword.VaultOCID == "") { - - e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword"), r.Spec.Database.DBPassword, - ErrorSpecValidationMissingDBVaultField)) - } - - // if vault fields have value, ociConfig must have values - if r.Spec.Database.DBPassword.VaultOCID != "" && r.Spec.Database.DBPassword.VaultSecretName != "" && - (r.Spec.OCIConfig.SecretName == "" || r.Spec.OCIConfig.ConfigMapName == "") { + if (r.Spec.Database.OCIVault.VaultID != "" && r.Spec.Database.OCIVault.VaultPasswordSecret == "") || + (r.Spec.Database.OCIVault.VaultPasswordSecret != "" && r.Spec.Database.OCIVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("ociConfig"), r.Spec.OCIConfig, - ErrorSpecValidationMissingOCIConfig)) + field.Invalid(field.NewPath("spec").Child("database").Child("oci"), r.Spec.Database.OCIVault, + ErrorSpecValidationMissingVaultField)) } - // If all of {DB Password Secret Name and vaultOCID+vaultSecretName} have no value, then error out - if r.Spec.Database.DBPassword.SecretName == "" && - r.Spec.Database.DBPassword.VaultOCID == "" && - r.Spec.Database.DBPassword.VaultSecretName == "" { + // The other vault field must have value if one does + if (r.Spec.Database.AzureVault.VaultID != "" && (r.Spec.Database.AzureVault.VaultPasswordSecret == "" && r.Spec.Database.AzureVault.VaultUsernameSecret == "")) || + (r.Spec.Database.AzureVault.VaultPasswordSecret != "" && r.Spec.Database.AzureVault.VaultID == "") || + (r.Spec.Database.AzureVault.VaultUsernameSecret != "" && r.Spec.Database.AzureVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("dbPassword").Child("secret"), r.Spec.Database.DBPassword.SecretName, - ErrorSpecValidationMissingDBPasswordSecret)) + field.Invalid(field.NewPath("spec").Child("database").Child("azure"), r.Spec.Database.AzureVault, + ErrorSpecValidationMissingVaultField)) } // disallow usage of any other image than the observability-exporter - if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { - e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, - ErrorSpecExporterImageNotAllowed)) - } + // temporarily disabled + //if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { + // e = append(e, + // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, + // ErrorSpecExporterImageNotAllowed)) + //} // Return if any errors if len(e) > 0 { @@ -168,9 +146,9 @@ func (r *DatabaseObserver) ValidateUpdate(ctx context.Context, oldObj, newObj ru var e field.ErrorList // disallow usage of any other image than the observability-exporter - if r.Spec.Exporter.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Exporter.Deployment.ExporterImage, AllowedExporterImage) { + if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Exporter.Deployment.ExporterImage, + field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, ErrorSpecExporterImageNotAllowed)) } // Return if any errors diff --git a/apis/observability/v4/zz_generated.deepcopy.go b/apis/observability/v4/zz_generated.deepcopy.go index d9892643..1d3e1c11 100644 --- a/apis/observability/v4/zz_generated.deepcopy.go +++ b/apis/observability/v4/zz_generated.deepcopy.go @@ -50,174 +50,158 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConfigMapDetails) DeepCopyInto(out *ConfigMapDetails) { +func (in *AdditionalWalletSecrets) DeepCopyInto(out *AdditionalWalletSecrets) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapDetails. -func (in *ConfigMapDetails) DeepCopy() *ConfigMapDetails { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalWalletSecrets. +func (in *AdditionalWalletSecrets) DeepCopy() *AdditionalWalletSecrets { if in == nil { return nil } - out := new(ConfigMapDetails) + out := new(AdditionalWalletSecrets) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DBSecret) DeepCopyInto(out *DBSecret) { +func (in *AzureConfig) DeepCopyInto(out *AzureConfig) { *out = *in + out.ConfigMap = in.ConfigMap } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecret. -func (in *DBSecret) DeepCopy() *DBSecret { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureConfig. +func (in *AzureConfig) DeepCopy() *AzureConfig { if in == nil { return nil } - out := new(DBSecret) + out := new(AzureConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DBSecretWithVault) DeepCopyInto(out *DBSecretWithVault) { +func (in *ConfigMapDetails) DeepCopyInto(out *ConfigMapDetails) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecretWithVault. -func (in *DBSecretWithVault) DeepCopy() *DBSecretWithVault { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapDetails. +func (in *ConfigMapDetails) DeepCopy() *ConfigMapDetails { if in == nil { return nil } - out := new(DBSecretWithVault) + out := new(ConfigMapDetails) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserver) DeepCopyInto(out *DatabaseObserver) { +func (in *ConfigPrivateKey) DeepCopyInto(out *ConfigPrivateKey) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserver. -func (in *DatabaseObserver) DeepCopy() *DatabaseObserver { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigPrivateKey. +func (in *ConfigPrivateKey) DeepCopy() *ConfigPrivateKey { if in == nil { return nil } - out := new(DatabaseObserver) + out := new(ConfigPrivateKey) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DatabaseObserver) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBAzureVault) DeepCopyInto(out *DBAzureVault) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBAzureVault. +func (in *DBAzureVault) DeepCopy() *DBAzureVault { + if in == nil { + return nil } - return nil + out := new(DBAzureVault) + in.DeepCopyInto(out) + return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverConfigMap) DeepCopyInto(out *DatabaseObserverConfigMap) { +func (in *DBOCIVault) DeepCopyInto(out *DBOCIVault) { *out = *in - out.Configmap = in.Configmap } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverConfigMap. -func (in *DatabaseObserverConfigMap) DeepCopy() *DatabaseObserverConfigMap { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBOCIVault. +func (in *DBOCIVault) DeepCopy() *DBOCIVault { if in == nil { return nil } - out := new(DatabaseObserverConfigMap) + out := new(DBOCIVault) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverDatabase) DeepCopyInto(out *DatabaseObserverDatabase) { +func (in *DBSecret) DeepCopyInto(out *DBSecret) { *out = *in - out.DBUser = in.DBUser - out.DBPassword = in.DBPassword - out.DBWallet = in.DBWallet - out.DBConnectionString = in.DBConnectionString } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDatabase. -func (in *DatabaseObserverDatabase) DeepCopy() *DatabaseObserverDatabase { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBSecret. +func (in *DBSecret) DeepCopy() *DBSecret { if in == nil { return nil } - out := new(DatabaseObserverDatabase) + out := new(DBSecret) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverDeployment) DeepCopyInto(out *DatabaseObserverDeployment) { +func (in *DatabaseConfig) DeepCopyInto(out *DatabaseConfig) { *out = *in - if in.SecurityContext != nil { - in, out := &in.SecurityContext, &out.SecurityContext - *out = new(v1.SecurityContext) - (*in).DeepCopyInto(*out) - } - if in.ExporterArgs != nil { - in, out := &in.ExporterArgs, &out.ExporterArgs - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ExporterCommands != nil { - in, out := &in.ExporterCommands, &out.ExporterCommands - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ExporterEnvs != nil { - in, out := &in.ExporterEnvs, &out.ExporterEnvs - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.DeploymentPodTemplate.DeepCopyInto(&out.DeploymentPodTemplate) + out.DBUser = in.DBUser + out.DBPassword = in.DBPassword + out.DBConnectionString = in.DBConnectionString + out.OCIVault = in.OCIVault + out.AzureVault = in.AzureVault } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverDeployment. -func (in *DatabaseObserverDeployment) DeepCopy() *DatabaseObserverDeployment { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseConfig. +func (in *DatabaseConfig) DeepCopy() *DatabaseConfig { if in == nil { return nil } - out := new(DatabaseObserverDeployment) + out := new(DatabaseConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverExporterConfig) DeepCopyInto(out *DatabaseObserverExporterConfig) { +func (in *DatabaseObserver) DeepCopyInto(out *DatabaseObserver) { *out = *in - in.Deployment.DeepCopyInto(&out.Deployment) - in.Service.DeepCopyInto(&out.Service) + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverExporterConfig. -func (in *DatabaseObserverExporterConfig) DeepCopy() *DatabaseObserverExporterConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserver. +func (in *DatabaseObserver) DeepCopy() *DatabaseObserver { if in == nil { return nil } - out := new(DatabaseObserverExporterConfig) + out := new(DatabaseObserver) in.DeepCopyInto(out) return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatabaseObserver) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatabaseObserverList) DeepCopyInto(out *DatabaseObserverList) { *out = *in @@ -251,15 +235,68 @@ func (in *DatabaseObserverList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { +func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { *out = *in - if in.Ports != nil { - in, out := &in.Ports, &out.Ports - *out = make([]v1.ServicePort, len(*in)) + out.Database = in.Database + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make(map[string]MultiDatabaseConfig, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Wallet.DeepCopyInto(&out.Wallet) + in.Deployment.DeepCopyInto(&out.Deployment) + in.Service.DeepCopyInto(&out.Service) + in.ServiceMonitor.DeepCopyInto(&out.ServiceMonitor) + out.ExporterConfig = in.ExporterConfig + out.OCIConfig = in.OCIConfig + out.AzureConfig = in.AzureConfig + in.Metrics.DeepCopyInto(&out.Metrics) + out.Log = in.Log + if in.InheritLabels != nil { + in, out := &in.InheritLabels, &out.InheritLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Sidecar.DeepCopyInto(&out.Sidecar) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. +func (in *DatabaseObserverSpec) DeepCopy() *DatabaseObserverSpec { + if in == nil { + return nil + } + out := new(DatabaseObserverSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverStatus. +func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { + if in == nil { + return nil + } + out := new(DatabaseObserverStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { + *out = *in if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -269,86 +306,114 @@ func (in *DatabaseObserverService) DeepCopyInto(out *DatabaseObserverService) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverService. -func (in *DatabaseObserverService) DeepCopy() *DatabaseObserverService { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentPodTemplate. +func (in *DeploymentPodTemplate) DeepCopy() *DeploymentPodTemplate { if in == nil { return nil } - out := new(DatabaseObserverService) + out := new(DeploymentPodTemplate) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverSpec) DeepCopyInto(out *DatabaseObserverSpec) { +func (in *ExporterConfig) DeepCopyInto(out *ExporterConfig) { *out = *in - out.Database = in.Database - in.Exporter.DeepCopyInto(&out.Exporter) - out.ExporterConfig = in.ExporterConfig - in.Prometheus.DeepCopyInto(&out.Prometheus) - out.OCIConfig = in.OCIConfig - out.Log = in.Log - if in.InheritLabels != nil { - in, out := &in.InheritLabels, &out.InheritLabels + out.ConfigMap = in.ConfigMap +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterConfig. +func (in *ExporterConfig) DeepCopy() *ExporterConfig { + if in == nil { + return nil + } + out := new(ExporterConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExporterDeployment) DeepCopyInto(out *ExporterDeployment) { + *out = *in + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.SecurityContext) + (*in).DeepCopyInto(*out) + } + if in.PodSecurityContext != nil { + in, out := &in.PodSecurityContext, &out.PodSecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.ExporterArgs != nil { + in, out := &in.ExporterArgs, &out.ExporterArgs *out = make([]string, len(*in)) copy(*out, *in) } - if in.ExporterSidecars != nil { - in, out := &in.ExporterSidecars, &out.ExporterSidecars - *out = make([]v1.Container, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) + if in.ExporterCommands != nil { + in, out := &in.ExporterCommands, &out.ExporterCommands + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExporterEnvs != nil { + in, out := &in.ExporterEnvs, &out.ExporterEnvs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val } } - if in.SideCarVolumes != nil { - in, out := &in.SideCarVolumes, &out.SideCarVolumes - *out = make([]v1.Volume, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val } } + in.DeploymentPodTemplate.DeepCopyInto(&out.DeploymentPodTemplate) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverSpec. -func (in *DatabaseObserverSpec) DeepCopy() *DatabaseObserverSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterDeployment. +func (in *ExporterDeployment) DeepCopy() *ExporterDeployment { if in == nil { return nil } - out := new(DatabaseObserverSpec) + out := new(ExporterDeployment) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseObserverStatus) DeepCopyInto(out *DatabaseObserverStatus) { +func (in *ExporterService) DeepCopyInto(out *ExporterService) { *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]v1.ServicePort, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseObserverStatus. -func (in *DatabaseObserverStatus) DeepCopy() *DatabaseObserverStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterService. +func (in *ExporterService) DeepCopy() *ExporterService { if in == nil { return nil } - out := new(DatabaseObserverStatus) + out := new(ExporterService) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { +func (in *ExporterServiceMonitor) DeepCopyInto(out *ExporterServiceMonitor) { *out = *in - if in.SecurityContext != nil { - in, out := &in.SecurityContext, &out.SecurityContext - *out = new(v1.PodSecurityContext) - (*in).DeepCopyInto(*out) - } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -356,14 +421,26 @@ func (in *DeploymentPodTemplate) DeepCopyInto(out *DeploymentPodTemplate) { (*out)[key] = val } } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(monitoringv1.NamespaceSelector) + (*in).DeepCopyInto(*out) + } + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]monitoringv1.Endpoint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentPodTemplate. -func (in *DeploymentPodTemplate) DeepCopy() *DeploymentPodTemplate { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterServiceMonitor. +func (in *ExporterServiceMonitor) DeepCopy() *ExporterServiceMonitor { if in == nil { return nil } - out := new(DeploymentPodTemplate) + out := new(ExporterServiceMonitor) in.DeepCopyInto(out) return out } @@ -401,81 +478,120 @@ func (in *LogVolume) DeepCopy() *LogVolume { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LogVolumePVClaim) DeepCopyInto(out *LogVolumePVClaim) { +func (in *LogVolumePVC) DeepCopyInto(out *LogVolumePVC) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolumePVClaim. -func (in *LogVolumePVClaim) DeepCopy() *LogVolumePVClaim { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogVolumePVC. +func (in *LogVolumePVC) DeepCopy() *LogVolumePVC { if in == nil { return nil } - out := new(LogVolumePVClaim) + out := new(LogVolumePVC) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { +func (in *MetricsConfig) DeepCopyInto(out *MetricsConfig) { *out = *in + if in.Configmap != nil { + in, out := &in.Configmap, &out.Configmap + *out = make([]ConfigMapDetails, len(*in)) + copy(*out, *in) + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfigSpec. -func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsConfig. +func (in *MetricsConfig) DeepCopy() *MetricsConfig { if in == nil { return nil } - out := new(OCIConfigSpec) + out := new(MetricsConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrometheusConfig) DeepCopyInto(out *PrometheusConfig) { +func (in *MultiDatabaseConfig) DeepCopyInto(out *MultiDatabaseConfig) { *out = *in - in.ServiceMonitor.DeepCopyInto(&out.ServiceMonitor) + out.DBUser = in.DBUser + out.DBPassword = in.DBPassword + out.DBConnectionString = in.DBConnectionString } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusConfig. -func (in *PrometheusConfig) DeepCopy() *PrometheusConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiDatabaseConfig. +func (in *MultiDatabaseConfig) DeepCopy() *MultiDatabaseConfig { if in == nil { return nil } - out := new(PrometheusConfig) + out := new(MultiDatabaseConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrometheusServiceMonitor) DeepCopyInto(out *PrometheusServiceMonitor) { +func (in *OCIConfig) DeepCopyInto(out *OCIConfig) { *out = *in - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + out.ConfigMap = in.ConfigMap + out.PrivateKey = in.PrivateKey +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIConfig. +func (in *OCIConfig) DeepCopy() *OCIConfig { + if in == nil { + return nil } - if in.NamespaceSelector != nil { - in, out := &in.NamespaceSelector, &out.NamespaceSelector - *out = new(monitoringv1.NamespaceSelector) - (*in).DeepCopyInto(*out) + out := new(OCIConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SidecarConfig) DeepCopyInto(out *SidecarConfig) { + *out = *in + if in.Containers != nil { + in, out := &in.Containers, &out.Containers + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } - if in.Endpoints != nil { - in, out := &in.Endpoints, &out.Endpoints - *out = make([]monitoringv1.Endpoint, len(*in)) + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusServiceMonitor. -func (in *PrometheusServiceMonitor) DeepCopy() *PrometheusServiceMonitor { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidecarConfig. +func (in *SidecarConfig) DeepCopy() *SidecarConfig { + if in == nil { + return nil + } + out := new(SidecarConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WalletSecret) DeepCopyInto(out *WalletSecret) { + *out = *in + if in.AdditionalWallets != nil { + in, out := &in.AdditionalWallets, &out.AdditionalWallets + *out = make([]AdditionalWalletSecrets, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WalletSecret. +func (in *WalletSecret) DeepCopy() *WalletSecret { if in == nil { return nil } - out := new(PrometheusServiceMonitor) + out := new(WalletSecret) in.DeepCopyInto(out) return out } diff --git a/commons/observability/constants.go b/commons/observability/constants.go index 217f6bdd..9146fde4 100644 --- a/commons/observability/constants.go +++ b/commons/observability/constants.go @@ -9,6 +9,13 @@ const ( DefaultValue = "DEFAULT" ) +// Signals +const ( + VaultUsernameInUse = "VAULT_USERNAME" + VaultPasswordInUse = "VAULT_PASSWORD" + VaultIDProvided = "VAULT_ID_PROVIDED" +) + // Observability Status const ( StatusObservabilityPending v4.StatusEnum = "PENDING" @@ -29,62 +36,64 @@ const ( DefaultDbUserKey = "username" DefaultDBPasswordKey = "password" DefaultDBConnectionStringKey = "connection" - DefaultConfigVolumeString = "config-volume" + DefaultConfigVolumeString = "metrics-volume" DefaultLogFilename = "alert.log" DefaultLogVolumeString = "log-volume" - DefaultLogDestination = "/log" + DefaultLogDestination = "/log" + DefaultConfigMountPath = "/config" + DefaultConfigVolumeName = "config-volume" DefaultWalletVolumeString = "creds" - DefaultOCIPrivateKeyVolumeString = "ocikey" + DefaultOCIConfigVolumeName = "oci-config-volume" DefaultOCIConfigFingerprintKey = "fingerprint" DefaultOCIConfigRegionKey = "region" DefaultOCIConfigTenancyKey = "tenancy" DefaultOCIConfigUserKey = "user" - - DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:1.6.1" - DefaultServicePort = 9161 - DefaultServiceTargetPort = 9161 - DefaultAppPort = 8080 - DefaultPrometheusPort = "metrics" - DefaultServiceType = "ClusterIP" - DefaultReplicaCount = 1 - DefaultExporterConfigMountRootPath = "/oracle/observability" - DefaultOracleHome = "/lib/oracle/21/client64/lib" - DefaultOracleTNSAdmin = DefaultOracleHome + "/network/admin" - DefaultExporterConfigmapFilename = "config.toml" - DefaultVaultPrivateKeyRootPath = "/oracle/config" - DefaultPrivateKeyFileKey = "privatekey" - DefaultPrivateKeyFileName = "private.pem" - DefaultVaultPrivateKeyAbsolutePath = DefaultVaultPrivateKeyRootPath + "/" + DefaultPrivateKeyFileName - DefaultExporterConfigmapAbsolutePath = DefaultExporterConfigMountRootPath + "/" + DefaultExporterConfigmapFilename -) - -// labeling -const ( - DefaultSelectorLabelKey = "app" - DefaultReleaseLabelKey = "release" -) - -// default resource -const ( - DefaultExporterContainerName = "observability-exporter" + DefaultAzureConfigTenantId = "tenantId" + DefaultAzureConfigClientId = "clientId" + DefaultAzureConfigClientSecret = "clientSecret" + DefaultEnvPasswordSuffix = "_PASSWORD" + DefaultEnvUserSuffix = "_USERNAME" + DefaultEnvConnectionStringSuffix = "_CONNECT_STRING" + + DefaultExporterImage = "container-registry.oracle.com/database/observability-exporter:2.0.2" + DefaultServicePort = 9161 + DefaultServiceTargetPort = 9161 + DefaultAppPort = 8080 + DefaultPrometheusPort = "metrics" + DefaultServiceType = "ClusterIP" + DefaultReplicaCount = 1 + DefaultExporterConfigMountRootPath = "/oracle/observability" + DefaultOracleHome = "/lib/oracle/21/client64/lib" + DefaultOracleTNSAdmin = DefaultOracleHome + "/network/admin" + DefaultOCIConfigPath = "/.oci" + DefaultPrivateKeyFileName = "private.pem" + DefaultVaultPrivateKeyAbsolutePath = DefaultOCIConfigPath + "/" + DefaultPrivateKeyFileName + DefaultExporterContainerName = "exporter" + DefaultSelectorLabelKey = "app" ) // Known environment variables const ( EnvVarOracleHome = "ORACLE_HOME" - EnvVarDataSourceUser = "DB_USERNAME" + EnvVarDataSourceUsername = "DB_USERNAME" EnvVarDataSourcePassword = "DB_PASSWORD" EnvVarDataSourceConnectString = "DB_CONNECT_STRING" EnvVarDataSourceLogDestination = "LOG_DESTINATION" - EnvVarDataSourcePwdVaultSecretName = "VAULT_SECRET_NAME" - EnvVarDataSourcePwdVaultId = "VAULT_ID" + EnvVarDataSourcePwdVaultSecretName = "OCI_VAULT_SECRET_NAME" + EnvVarDataSourcePwdVaultId = "OCI_VAULT_ID" EnvVarCustomConfigmap = "CUSTOM_METRICS" EnvVarTNSAdmin = "TNS_ADMIN" - EnvVarVaultTenancyOCID = "vault_tenancy_ocid" - EnvVarVaultUserOCID = "vault_user_ocid" - EnvVarVaultFingerprint = "vault_fingerprint" - EnvVarVaultPrivateKeyPath = "vault_private_key_path" - EnvVarVaultRegion = "vault_region" + EnvVarOCIVaultTenancyOCID = "OCI_CLI_TENANCY" + EnvVarOCIVaultUserOCID = "OCI_CLI_USER" + EnvVarOCIVaultFingerprint = "OCI_CLI_FINGERPRINT" + EnvVarOCIVaultPrivateKeyPath = "OCI_CLI_KEY_FILE" + EnvVarOCIVaultRegion = "OCI_CLI_REGION" + EnvVarAzureVaultPasswordSecret = "AZ_VAULT_PASSWORD_SECRET" + EnvVarAzureVaultUsernameSecret = "AZ_VAULT_USERNAME_SECRET" + EnvVarAzureVaultID = "AZ_VAULT_ID" + EnvVarAzureTenantID = "AZURE_TENANT_ID" + EnvVarAzureClientID = "AZURE_CLIENT_ID" + EnvVarAzureClientSecret = "AZURE_CLIENT_SECRET" ) // Positive ConditionTypes @@ -166,10 +175,10 @@ const ( EventMessageFailedCRRetrieval = "Encountered error retrieving databaseObserver instance" EventReasonSpecError = "DeploymentSpecValidationFailed" - EventMessageSpecErrorDBPasswordSecretMissing = "Spec validation failed due to required dbPassword secret not found" + EventMessageSpecErrorConfigMapSpecifiedMissing = "Spec validation failed due to referenced configMap not found" EventMessageSpecErrorDBConnectionStringSecretMissing = "Spec validation failed due to required dbConnectionString secret not found" EventMessageSpecErrorDBPUserSecretMissing = "Spec validation failed due to dbUser secret not found" - EventMessageSpecErrorConfigmapMissing = "Spec validation failed due to custom config configmap not found" + EventMessageSpecErrorDBPwdSecretMissing = "Spec validation failed due to dbPassword secret not found" EventMessageSpecErrorDBWalletSecretMissing = "Spec validation failed due to provided dbWallet secret not found" EventReasonUpdateSucceeded = "ExporterDeploymentUpdated" diff --git a/commons/observability/utils.go b/commons/observability/utils.go index 12d72b03..2a227226 100644 --- a/commons/observability/utils.go +++ b/commons/observability/utils.go @@ -11,21 +11,19 @@ import ( func AddSidecarContainers(a *api.DatabaseObserver, listing *[]corev1.Container) { - if containers := a.Spec.ExporterSidecars; len(containers) > 0 { + if containers := a.Spec.Sidecar.Containers; len(containers) > 0 { for _, container := range containers { *listing = append(*listing, container) } - } } func AddSidecarVolumes(a *api.DatabaseObserver, listing *[]corev1.Volume) { - if volumes := a.Spec.SideCarVolumes; len(volumes) > 0 { + if volumes := a.Spec.Sidecar.Volumes; len(volumes) > 0 { for _, v := range volumes { *listing = append(*listing, v) } - } } @@ -67,7 +65,7 @@ func GetSelectorLabel(a *api.DatabaseObserver) map[string]string { func GetExporterVersion(a *api.DatabaseObserver) string { appVersion := "latest" whichImage := DefaultExporterImage - if img := a.Spec.Exporter.Deployment.ExporterImage; img != "" { + if img := a.Spec.Deployment.ExporterImage; img != "" { whichImage = img } @@ -80,7 +78,7 @@ func GetExporterVersion(a *api.DatabaseObserver) string { // GetExporterArgs retrieves args func GetExporterArgs(a *api.DatabaseObserver) []string { - if args := a.Spec.Exporter.Deployment.ExporterArgs; args != nil || len(args) > 0 { + if args := a.Spec.Deployment.ExporterArgs; args != nil || len(args) > 0 { return args } return nil @@ -88,7 +86,7 @@ func GetExporterArgs(a *api.DatabaseObserver) []string { // GetExporterDeploymentSecurityContext retrieves security context for container func GetExporterDeploymentSecurityContext(a *api.DatabaseObserver) *corev1.SecurityContext { - if sc := a.Spec.Exporter.Deployment.SecurityContext; sc != nil { + if sc := a.Spec.Deployment.SecurityContext; sc != nil { return sc } return &corev1.SecurityContext{} @@ -96,7 +94,7 @@ func GetExporterDeploymentSecurityContext(a *api.DatabaseObserver) *corev1.Secur // GetExporterPodSecurityContext retrieves security context for pods func GetExporterPodSecurityContext(a *api.DatabaseObserver) *corev1.PodSecurityContext { - if sc := a.Spec.Exporter.Deployment.DeploymentPodTemplate.SecurityContext; sc != nil { + if sc := a.Spec.Deployment.PodSecurityContext; sc != nil { return sc } return &corev1.PodSecurityContext{} @@ -104,7 +102,7 @@ func GetExporterPodSecurityContext(a *api.DatabaseObserver) *corev1.PodSecurityC // GetExporterCommands retrieves commands func GetExporterCommands(a *api.DatabaseObserver) []string { - if c := a.Spec.Exporter.Deployment.ExporterCommands; c != nil || len(c) > 0 { + if c := a.Spec.Deployment.ExporterCommands; c != nil || len(c) > 0 { return c } return nil @@ -116,7 +114,7 @@ func GetExporterServicePort(a *api.DatabaseObserver) []corev1.ServicePort { servicePorts := make([]corev1.ServicePort, 0) // get service ports - if ports := a.Spec.Exporter.Service.Ports; len(ports) > 0 { + if ports := a.Spec.Service.Ports; len(ports) > 0 { for _, port := range ports { servicePorts = append(servicePorts, port) } @@ -140,7 +138,7 @@ func GetEndpoints(a *api.DatabaseObserver) []monitorv1.Endpoint { endpoints := make([]monitorv1.Endpoint, 0) // get endpoints - if es := a.Spec.Prometheus.ServiceMonitor.Endpoints; len(es) > 0 { + if es := a.Spec.ServiceMonitor.Endpoints; len(es) > 0 { for _, e := range es { endpoints = append(endpoints, e) } @@ -157,8 +155,8 @@ func GetEndpoints(a *api.DatabaseObserver) []monitorv1.Endpoint { func AddNamespaceSelector(a *api.DatabaseObserver, spec *monitorv1.ServiceMonitorSpec) { - if ns := a.Spec.Prometheus.ServiceMonitor.NamespaceSelector; ns != nil { - a.Spec.Prometheus.ServiceMonitor.NamespaceSelector.DeepCopyInto(&spec.NamespaceSelector) + if ns := a.Spec.ServiceMonitor.NamespaceSelector; ns != nil { + a.Spec.ServiceMonitor.NamespaceSelector.DeepCopyInto(&spec.NamespaceSelector) } } @@ -168,25 +166,21 @@ func GetExporterDeploymentVolumeMounts(a *api.DatabaseObserver) []corev1.VolumeM volM := make([]corev1.VolumeMount, 0) - if cVolumeSourceName := a.Spec.ExporterConfig.Configmap.Name; cVolumeSourceName != "" { + if len(a.Spec.Metrics.Configmap) > 0 { volM = append(volM, corev1.VolumeMount{ Name: DefaultConfigVolumeString, MountPath: DefaultExporterConfigMountRootPath, }) } - // a.Spec.Database.DBWallet.SecretName optional + // a.Spec.Wallet.SecretName optional // if null, consider the database NON-ADB and connect as such - if secretName := a.Spec.Database.DBWallet.SecretName; secretName != "" { + if secretName := a.Spec.Wallet.SecretName; secretName != "" { - p := DefaultOracleTNSAdmin - - // Determine what the value of TNS_ADMIN - // if custom TNS_ADMIN environment variable is set and found, use that instead as the path - if rCustomEnvs := a.Spec.Exporter.Deployment.ExporterEnvs; rCustomEnvs != nil { - if v, f := rCustomEnvs[EnvVarTNSAdmin]; f { - p = v - } + // Determine where to mount + p := a.Spec.Wallet.MountPath + if p == "" { + p = DefaultOracleTNSAdmin } volM = append(volM, corev1.VolumeMount{ @@ -195,17 +189,40 @@ func GetExporterDeploymentVolumeMounts(a *api.DatabaseObserver) []corev1.VolumeM }) } - // a.Spec.OCIConfig.SecretName required if vault is used - if secretName := a.Spec.OCIConfig.SecretName; secretName != "" { + // a.Spec.Wallet.AdditionalWallets + if add := a.Spec.Wallet.AdditionalWallets; add != nil && len(add) > 0 { + for _, w := range add { + // Determine where to mount + volM = append(volM, corev1.VolumeMount{ + Name: w.Name, + MountPath: w.MountPath, + }) + } + + } + + // a.Spec.OCIConfig.ConfigMap + // a.Spec.OCIConfig.SecretName + if oci := a.Spec.OCIConfig; oci.ConfigMap.Name != "" { + + p := oci.MountPath + if p == "" { // overwrite + p = DefaultOCIConfigPath + } + volM = append(volM, corev1.VolumeMount{ - Name: DefaultOCIPrivateKeyVolumeString, - MountPath: DefaultVaultPrivateKeyRootPath, + Name: DefaultOCIConfigVolumeName, + MountPath: p, }) } // a.Spec.Log.Destination path to mount for a custom log path, a volume is required if disabled := a.Spec.Log.Disable; !disabled { - vName := DefaultLogVolumeString + + vName := a.Spec.Log.Volume.Name + if vName == "" { + vName = DefaultLogVolumeString + } vDestination := a.Spec.Log.Destination if vDestination == "" { @@ -218,6 +235,19 @@ func GetExporterDeploymentVolumeMounts(a *api.DatabaseObserver) []corev1.VolumeM }) } + // ObserverConfig VolumeMount + if volumeName := a.Spec.ExporterConfig.ConfigMap.Name; volumeName != "" { + mp := a.Spec.ExporterConfig.MountPath + if mp == "" { + mp = DefaultConfigMountPath + } + + volM = append(volM, corev1.VolumeMount{ + Name: DefaultConfigVolumeName, + MountPath: mp, + }) + } + return volM } @@ -226,28 +256,48 @@ func GetExporterDeploymentVolumes(a *api.DatabaseObserver) []corev1.Volume { vol := make([]corev1.Volume, 0) - // config-volume Volume - // if null, the exporter uses the default built-in config - if cVolumeSourceName := a.Spec.ExporterConfig.Configmap.Name; cVolumeSourceName != "" { + // metrics-volume Volume + // if null, the exporter uses the default built-in metrics config + if len(a.Spec.Metrics.Configmap) > 1 { - cVolumeSourceKey := a.Spec.ExporterConfig.Configmap.Key - cMSource := &corev1.ConfigMapVolumeSource{ + vp := make([]corev1.VolumeProjection, 0) + for _, cm := range a.Spec.Metrics.Configmap { + cmSource := &corev1.ConfigMapProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cm.Name, + }, + } + + vp = append(vp, corev1.VolumeProjection{ConfigMap: cmSource}) + } + + vol = append(vol, corev1.Volume{ + Name: DefaultConfigVolumeString, + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: vp, + }, + }}) + + } else if len(a.Spec.Metrics.Configmap) == 1 { + + cm := a.Spec.Metrics.Configmap[0] + cmSource := &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ - Name: cVolumeSourceName, + Name: cm.Name, }, - Items: []corev1.KeyToPath{{ - Key: cVolumeSourceKey, - Path: DefaultExporterConfigmapFilename, - }}, } - vol = append(vol, corev1.Volume{Name: DefaultConfigVolumeString, VolumeSource: corev1.VolumeSource{ConfigMap: cMSource}}) + vol = append(vol, corev1.Volume{ + Name: DefaultConfigVolumeString, + VolumeSource: corev1.VolumeSource{ConfigMap: cmSource}, + }) } // creds Volume // a.Spec.Database.DBWallet.SecretName optional // if null, consider the database NON-ADB and connect as such - if secretName := a.Spec.Database.DBWallet.SecretName; secretName != "" { + if secretName := a.Spec.Wallet.SecretName; secretName != "" { vol = append(vol, corev1.Volume{ Name: DefaultWalletVolumeString, @@ -259,28 +309,57 @@ func GetExporterDeploymentVolumes(a *api.DatabaseObserver) []corev1.Volume { }) } - // ocikey Volume - // a.Spec.Database.DBWallet.SecretName optional - if secretName := a.Spec.OCIConfig.SecretName; secretName != "" { + // a.Spec.Wallet.AdditionalWallets + if add := a.Spec.Wallet.AdditionalWallets; add != nil && len(add) > 0 { + for _, w := range add { - OCIConfigSource := &corev1.SecretVolumeSource{ - SecretName: secretName, - Items: []corev1.KeyToPath{{ - Key: DefaultPrivateKeyFileKey, - Path: DefaultPrivateKeyFileName, - }}, + vol = append(vol, corev1.Volume{ + Name: w.Name, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: w.SecretName, + }, + }, + }) } + } + + // oci-config-volume Volume + // a.Spec.OCIConfig.PrivateKey.SecretName optional + if oci := a.Spec.OCIConfig; oci.ConfigMap.Name != "" && oci.PrivateKey.SecretName != "" { + + vp := make([]corev1.VolumeProjection, 0) + + cmSource := &corev1.ConfigMapProjection{ + LocalObjectReference: corev1.LocalObjectReference{Name: oci.ConfigMap.Name}, + } + secretSource := &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{Name: oci.PrivateKey.SecretName}, + } + + vp = append(vp, corev1.VolumeProjection{ConfigMap: cmSource}) + vp = append(vp, corev1.VolumeProjection{Secret: secretSource}) + vol = append(vol, corev1.Volume{ - Name: DefaultOCIPrivateKeyVolumeString, - VolumeSource: corev1.VolumeSource{Secret: OCIConfigSource}, + Name: DefaultOCIConfigVolumeName, + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: vp, + }, + }, }) + } // log-volume Volume if disabled := a.Spec.Log.Disable; !disabled { vs := GetLogVolumeSource(a) - vName := DefaultLogVolumeString + + vName := a.Spec.Log.Volume.Name + if vName == "" { + vName = DefaultLogVolumeString + } vol = append(vol, corev1.Volume{ Name: vName, @@ -288,18 +367,35 @@ func GetExporterDeploymentVolumes(a *api.DatabaseObserver) []corev1.Volume { }) } + // ObserverConfig Volume + if volumeName := a.Spec.ExporterConfig.ConfigMap.Name; volumeName != "" { + cMSource := &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: volumeName, + }, + } + + vol = append(vol, corev1.Volume{Name: DefaultConfigVolumeName, VolumeSource: corev1.VolumeSource{ConfigMap: cMSource}}) + } + return vol } -// GetExporterConfig function retrieves config name for status -func GetExporterConfig(a *api.DatabaseObserver) string { +// GetMetricsConfig function retrieves config name for status +func GetMetricsConfig(a *api.DatabaseObserver) string { - configName := DefaultValue - if cmName := a.Spec.ExporterConfig.Configmap.Name; cmName != "" { - configName = cmName - } + cms := a.Spec.Metrics.Configmap + if len(cms) > 1 { + metricsConfigList := make([]string, 0) + for _, cm := range cms { + metricsConfigList = append(metricsConfigList, cm.Name) + } + return strings.Join(metricsConfigList, ",") - return configName + } else if len(cms) == 1 { + return cms[0].Name + } + return DefaultValue } // GetLogVolumeSource function retrieves the source to help GetExporterDeploymentVolumes @@ -327,137 +423,226 @@ func AddEnv(env []corev1.EnvVar, existing map[string]string, name string, v stri // Evaluate if env already exists if _, f := existing[name]; !f { env = append(env, corev1.EnvVar{Name: name, Value: v}) + existing[name] = "" } return env } -// AddEnvFrom is a helper method that appends an Env Var value source -func AddEnvFrom(env []corev1.EnvVar, existing map[string]string, name string, v *corev1.EnvVarSource) []corev1.EnvVar { - +func AddEnvFromConfigMap(env []corev1.EnvVar, existing map[string]string, environmentName string, key string, configMap string) []corev1.EnvVar { // Evaluate if env already exists - if _, f := existing[name]; !f { - env = append(env, corev1.EnvVar{Name: name, ValueFrom: v}) + if _, f := existing[environmentName]; !f { + + optional := true + cm := &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + Key: key, + LocalObjectReference: corev1.LocalObjectReference{Name: configMap}, + Optional: &optional, + }, + } + env = append(env, corev1.EnvVar{Name: environmentName, ValueFrom: cm}) } return env } -// GetExporterEnvs function retrieves env from a or provides default -func GetExporterEnvs(a *api.DatabaseObserver) []corev1.EnvVar { - - optional := true - rDBPasswordKey := a.Spec.Database.DBPassword.Key - rDBPasswordName := a.Spec.Database.DBPassword.SecretName - rDBConnectStrKey := a.Spec.Database.DBConnectionString.Key - rDBConnectStrName := a.Spec.Database.DBConnectionString.SecretName - rDBVaultSecretName := a.Spec.Database.DBPassword.VaultSecretName - rDBVaultOCID := a.Spec.Database.DBPassword.VaultOCID - rDBUserSKey := a.Spec.Database.DBUser.Key - rDBUserSName := a.Spec.Database.DBUser.SecretName - rOCIConfigCMName := a.Spec.OCIConfig.ConfigMapName - rCustomEnvs := a.Spec.Exporter.Deployment.ExporterEnvs +func AddEnvFromSecret(env []corev1.EnvVar, existing map[string]string, environmentName string, key string, secretName string) []corev1.EnvVar { + // Evaluate if env already exists + if _, f := existing[environmentName]; !f { - var env = make([]corev1.EnvVar, 0) + optional := true + e := &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: key, + LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, + Optional: &optional, + }} - // add CustomEnvs - if rCustomEnvs != nil { - for k, v := range rCustomEnvs { - env = append(env, corev1.EnvVar{Name: k, Value: v}) - } + env = append(env, corev1.EnvVar{Name: environmentName, ValueFrom: e}) } + return env +} + +func AddSingleDatabaseEnvs(a *api.DatabaseObserver, e map[string]string, source []corev1.EnvVar) []corev1.EnvVar { + + u := a.Spec.Database.DBUser + c := a.Spec.Database.DBConnectionString + p := a.Spec.Database.DBPassword + o := a.Spec.Database.OCIVault + z := a.Spec.Database.AzureVault // DB_USERNAME environment variable - if rDBUserSKey == "" { // overwrite - rDBUserSKey = DefaultDbUserKey + if IsUsingAzureVault(z, VaultUsernameInUse) { + source = AddEnv(source, e, EnvVarAzureVaultUsernameSecret, z.VaultUsernameSecret) + source = AddEnv(source, e, EnvVarAzureVaultID, z.VaultID) + + } else if u.SecretName != "" { + uKey := u.Key + if uKey == "" { // overwrite + uKey = DefaultDbUserKey + } + source = AddEnvFromSecret(source, e, EnvVarDataSourceUsername, uKey, u.SecretName) } - envUser := &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - Key: rDBUserSKey, - LocalObjectReference: corev1.LocalObjectReference{Name: rDBUserSName}, - Optional: &optional, - }} - env = AddEnvFrom(env, rCustomEnvs, EnvVarDataSourceUser, envUser) // DB_CONNECT_STRING environment variable - if rDBConnectStrKey == "" { - rDBConnectStrKey = DefaultDBConnectionStringKey + if c.SecretName != "" { + cKey := c.Key + if cKey == "" { + cKey = DefaultDBConnectionStringKey + } + source = AddEnvFromSecret(source, e, EnvVarDataSourceConnectString, cKey, c.SecretName) } - envConnectStr := &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - Key: rDBConnectStrKey, - LocalObjectReference: corev1.LocalObjectReference{Name: rDBConnectStrName}, - Optional: &optional, - }} - env = AddEnvFrom(env, rCustomEnvs, EnvVarDataSourceConnectString, envConnectStr) // DB_PASSWORD environment variable - // if useVault, add environment variables for Vault ID and Vault Secret Name - useVault := rDBVaultSecretName != "" && rDBVaultOCID != "" - if useVault { + if IsUsingOCIVault(o) { + source = AddEnv(source, e, EnvVarDataSourcePwdVaultSecretName, o.VaultPasswordSecret) + source = AddEnv(source, e, EnvVarDataSourcePwdVaultId, o.VaultID) + + } else if IsUsingAzureVault(z, VaultPasswordInUse) { + source = AddEnv(source, e, EnvVarAzureVaultPasswordSecret, z.VaultPasswordSecret) + source = AddEnv(source, e, EnvVarAzureVaultID, z.VaultID) + + } else if p.SecretName != "" { + pKey := p.Key + if pKey == "" { // overwrite + pKey = DefaultDBPasswordKey + } + env := p.EnvName + if env == "" { // overwrite + env = EnvVarDataSourcePassword + } + source = AddEnvFromSecret(source, e, env, pKey, p.SecretName) + } - env = AddEnv(env, rCustomEnvs, EnvVarDataSourcePwdVaultSecretName, rDBVaultSecretName) - env = AddEnv(env, rCustomEnvs, EnvVarDataSourcePwdVaultId, rDBVaultOCID) + // Add OCI Vault Required Values + if IsUsingOCIVault(o) && a.Spec.OCIConfig.ConfigMap.Name != "" { + ociConfig := a.Spec.OCIConfig.ConfigMap.Name + source = AddEnv(source, e, EnvVarOCIVaultPrivateKeyPath, DefaultVaultPrivateKeyAbsolutePath) + source = AddEnvFromConfigMap(source, e, EnvVarOCIVaultFingerprint, DefaultOCIConfigFingerprintKey, ociConfig) + source = AddEnvFromConfigMap(source, e, EnvVarOCIVaultUserOCID, DefaultOCIConfigUserKey, ociConfig) + source = AddEnvFromConfigMap(source, e, EnvVarOCIVaultTenancyOCID, DefaultOCIConfigTenancyKey, ociConfig) + source = AddEnvFromConfigMap(source, e, EnvVarOCIVaultRegion, DefaultOCIConfigRegionKey, ociConfig) + } - // Configuring the configProvider prefixed with vault_ - // https://github.com/oracle/oracle-db-appdev-monitoring/blob/main/vault/vault.go - configSourceFingerprintValue := &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - Key: DefaultOCIConfigFingerprintKey, - LocalObjectReference: corev1.LocalObjectReference{Name: rOCIConfigCMName}, - Optional: &optional, - }, - } - configSourceRegionValue := &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - Key: DefaultOCIConfigRegionKey, - LocalObjectReference: corev1.LocalObjectReference{Name: rOCIConfigCMName}, - Optional: &optional, - }, + // Add OCI Vault Required Values + if IsUsingAzureVault(z, VaultIDProvided) && a.Spec.AzureConfig.ConfigMap.Name != "" { + azureConfig := a.Spec.AzureConfig.ConfigMap.Name + source = AddEnvFromConfigMap(source, e, EnvVarAzureTenantID, DefaultAzureConfigTenantId, azureConfig) + source = AddEnvFromConfigMap(source, e, EnvVarAzureClientID, DefaultAzureConfigClientId, azureConfig) + source = AddEnvFromConfigMap(source, e, EnvVarAzureClientSecret, DefaultAzureConfigClientSecret, azureConfig) + } + + return source +} + +func AddMultiDatabaseEnvs(a *api.DatabaseObserver, e map[string]string, source []corev1.EnvVar) []corev1.EnvVar { + + for key, db := range a.Spec.Databases { + u := db.DBUser + c := db.DBConnectionString + p := db.DBPassword + + // DB_USERNAME environment variable, if secret is defined + if u.SecretName != "" { + uKey := u.Key + if uKey == "" { // overwrite + uKey = DefaultDbUserKey + } + envUsername := u.EnvName + if envUsername == "" { + envUsername = key + DefaultEnvUserSuffix + } + source = AddEnvFromSecret(source, e, envUsername, uKey, u.SecretName) } - configSourceTenancyValue := &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - Key: DefaultOCIConfigTenancyKey, - LocalObjectReference: corev1.LocalObjectReference{Name: rOCIConfigCMName}, - Optional: &optional, - }, + + // DB_CONNECT_STRING environment variable, if secret is defined + if c.SecretName != "" { + cKey := c.Key + if cKey == "" { + cKey = key + DefaultEnvConnectionStringSuffix + } + envConnectionString := c.EnvName + if envConnectionString == "" { + envConnectionString = key + DefaultEnvConnectionStringSuffix + } + source = AddEnvFromSecret(source, e, envConnectionString, cKey, c.SecretName) } - configSourceUserValue := &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - Key: DefaultOCIConfigUserKey, - LocalObjectReference: corev1.LocalObjectReference{Name: rOCIConfigCMName}, - Optional: &optional, - }, + + // DB_PASSWORD environment variable, if secret is defined + if p.SecretName != "" { + pKey := p.Key + if pKey == "" { // overwrite + pKey = DefaultDBPasswordKey + } + env := p.EnvName + if env == "" { // overwrite + env = key + DefaultEnvPasswordSuffix + } + source = AddEnvFromSecret(source, e, env, pKey, p.SecretName) } - env = AddEnvFrom(env, rCustomEnvs, EnvVarVaultFingerprint, configSourceFingerprintValue) - env = AddEnvFrom(env, rCustomEnvs, EnvVarVaultUserOCID, configSourceUserValue) - env = AddEnvFrom(env, rCustomEnvs, EnvVarVaultTenancyOCID, configSourceTenancyValue) - env = AddEnvFrom(env, rCustomEnvs, EnvVarVaultRegion, configSourceRegionValue) - env = AddEnv(env, rCustomEnvs, EnvVarVaultPrivateKeyPath, DefaultVaultPrivateKeyAbsolutePath) - } else { + } + return source +} - if rDBPasswordKey == "" { // overwrite - rDBPasswordKey = DefaultDBPasswordKey - } - dbPassword := &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - Key: rDBPasswordKey, - LocalObjectReference: corev1.LocalObjectReference{Name: rDBPasswordName}, - Optional: &optional, - }} +func IsUsingOCIVault(f api.DBOCIVault) bool { + return f.VaultPasswordSecret != "" && f.VaultID != "" +} - env = AddEnvFrom(env, rCustomEnvs, EnvVarDataSourcePassword, dbPassword) +func IsUsingAzureVault(f api.DBAzureVault, v string) bool { + if v == VaultPasswordInUse { + return f.VaultID != "" && f.VaultPasswordSecret != "" + } + if v == VaultUsernameInUse { + return f.VaultID != "" && f.VaultUsernameSecret != "" + } + if v == VaultIDProvided { + return f.VaultID != "" } - // CUSTOM_METRICS environment variable - if customMetricsName := a.Spec.ExporterConfig.Configmap.Name; customMetricsName != "" { - customMetrics := DefaultExporterConfigmapAbsolutePath + return false + +} - env = AddEnv(env, rCustomEnvs, EnvVarCustomConfigmap, customMetrics) +func IsMultipleDatabasesDefined(a *api.DatabaseObserver) bool { + return a.Spec.Databases != nil && len(a.Spec.Databases) > 0 +} + +// GetExporterEnvs function retrieves env from a or provides default +func GetExporterEnvs(a *api.DatabaseObserver) []corev1.EnvVar { + + // create slices + var env = make([]corev1.EnvVar, 0) + + // First, add all environment variables provided + e := a.Spec.Deployment.ExporterEnvs + if e != nil { + for k, v := range e { + env = append(env, corev1.EnvVar{Name: k, Value: v}) + } + } else { + e = make(map[string]string) } - env = AddEnv(env, rCustomEnvs, EnvVarOracleHome, DefaultOracleHome) - env = AddEnv(env, rCustomEnvs, EnvVarTNSAdmin, DefaultOracleTNSAdmin) + // Add database environment variables based on single or multi DB configuration + if IsMultipleDatabasesDefined(a) { + env = AddMultiDatabaseEnvs(a, e, env) + } else { + env = AddSingleDatabaseEnvs(a, e, env) + } + + // CUSTOM_METRICS environment variable + if cms := a.Spec.Metrics.Configmap; cms != nil && len(cms) > 0 { + metricsConfigList := make([]string, 0) + for _, cm := range cms { + metricsConfigList = append(metricsConfigList, DefaultExporterConfigMountRootPath+"/"+cm.Key) + } + customMetrics := strings.Join(metricsConfigList, ",") + env = AddEnv(env, e, EnvVarCustomConfigmap, customMetrics) + } + + env = AddEnv(env, e, EnvVarOracleHome, DefaultOracleHome) + env = AddEnv(env, e, EnvVarTNSAdmin, DefaultOracleTNSAdmin) // LOG_DESTINATION environment variable4 if disabled := a.Spec.Log.Disable; !disabled { @@ -471,7 +656,7 @@ func GetExporterEnvs(a *api.DatabaseObserver) []corev1.EnvVar { f = DefaultLogFilename } ld := filepath.Join(d, f) - env = AddEnv(env, rCustomEnvs, EnvVarDataSourceLogDestination, ld) + env = AddEnv(env, e, EnvVarDataSourceLogDestination, ld) } return env } @@ -486,10 +671,9 @@ func GetExporterReplicas(a *api.DatabaseObserver) int32 { // GetExporterImage function retrieves image from a or provides default func GetExporterImage(a *api.DatabaseObserver) string { - if img := a.Spec.Exporter.Deployment.ExporterImage; img != "" { + if img := a.Spec.Deployment.ExporterImage; img != "" { return img } - return DefaultExporterImage } diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index a0e9059c..af3dc9f3 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -18,8 +18,8 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig + - jsonPath: .status.metricsConfig + name: MetricsConfig type: string - jsonPath: .status.status name: Status @@ -39,7 +39,7 @@ spec: type: object spec: properties: - configuration: + azureConfig: properties: configMap: properties: @@ -51,8 +51,19 @@ spec: type: object database: properties: + azure: + properties: + vaultID: + type: string + vaultPasswordSecret: + type: string + vaultUsernameSecret: + type: string + type: object dbConnectionString: properties: + envName: + type: string key: type: string secret: @@ -60,243 +71,250 @@ spec: type: object dbPassword: properties: + envName: + type: string key: type: string secret: type: string - vaultOCID: - type: string - vaultSecretName: - type: string type: object dbUser: properties: + envName: + type: string key: type: string secret: type: string type: object - dbWallet: + oci: properties: - key: + vaultID: type: string - secret: + vaultPasswordSecret: type: string type: object type: object - exporter: - properties: - deployment: - properties: - args: - items: + databases: + additionalProperties: + properties: + dbConnectionString: + properties: + envName: type: string - type: array - commands: - items: + key: type: string - type: array - env: - additionalProperties: + secret: + type: string + type: object + dbPassword: + properties: + envName: type: string + key: + type: string + secret: + type: string + type: object + dbUser: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + type: object + type: object + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podSecurityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type type: object - image: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: type: string - labels: - additionalProperties: - type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string type: object - podTemplate: + seccompProfile: properties: - labels: - additionalProperties: - type: string - type: object - securityContext: - properties: - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxChangePolicy: - type: string - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - x-kubernetes-list-type: atomic - supplementalGroupsPolicy: - type: string - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object + localhostProfile: + type: string + type: + type: string + required: + - type type: object - securityContext: + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - capabilities: - properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - privileged: - type: boolean - procMount: + gmsaCredentialSpec: type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: + gmsaCredentialSpecName: + type: string + hostProcess: type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object + runAsUserName: + type: string type: object type: object - service: + podTemplate: properties: labels: additionalProperties: type: string type: object - ports: - items: - properties: - appProtocol: - type: string - name: + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP + type: array + x-kubernetes-list-type: atomic + drop: + items: type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + exporterConfig: + properties: + configMap: + properties: + key: + type: string + name: + type: string type: object + mountPath: + type: string type: object inheritLabels: items: @@ -312,6 +330,8 @@ spec: type: string volume: properties: + name: + type: string persistentVolumeClaim: properties: claimName: @@ -319,41 +339,202 @@ spec: type: object type: object type: object + metrics: + properties: + configMap: + items: + properties: + key: + type: string + name: + type: string + type: object + type: array + type: object ociConfig: properties: - configMapName: - type: string - secretName: + configMap: + properties: + key: + type: string + name: + type: string + type: object + mountPath: type: string + privateKey: + properties: + secret: + type: string + type: object type: object - prometheus: + replicas: + format: int32 + type: integer + service: properties: - serviceMonitor: - properties: - endpoints: - items: - properties: - authorization: - properties: - credentials: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - type: string - type: object - basicAuth: - properties: - password: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: properties: key: type: string @@ -366,7 +547,7 @@ spec: - key type: object x-kubernetes-map-type: atomic - username: + secret: properties: key: type: string @@ -380,9 +561,7 @@ spec: type: object x-kubernetes-map-type: atomic type: object - bearerTokenFile: - type: string - bearerTokenSecret: + clientSecret: properties: key: type: string @@ -395,98 +574,15 @@ spec: - key type: object x-kubernetes-map-type: atomic - enableHttp2: - type: boolean - filterRunning: - type: boolean - followRedirects: - type: boolean - honorLabels: - type: boolean - honorTimestamps: - type: boolean - interval: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + endpointParams: + additionalProperties: + type: string + type: object + noProxy: type: string - metricRelabelings: - items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object - type: array - oauth2: - properties: - clientId: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: + proxyConnectHeader: + additionalProperties: + items: properties: key: type: string @@ -499,211 +595,18 @@ spec: - key type: object x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - type: object - noProxy: - type: string - proxyConnectHeader: - additionalProperties: - items: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: array - type: object - x-kubernetes-map-type: atomic - proxyFromEnvironment: - type: boolean - proxyUrl: - pattern: ^(http|https|socks5)://.+$ - type: string - scopes: - items: - type: string - type: array - tlsConfig: - properties: - ca: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - type: boolean - keySecret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - maxVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - serverName: - type: string - type: object - tokenUrl: - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - params: - additionalProperties: - items: - type: string type: array type: object - path: - type: string - port: - type: string + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^http(s)?://.+$ type: string - relabelings: + scopes: items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object + type: string type: array - scheme: - enum: - - http - - https - type: string - scrapeTimeout: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true tlsConfig: properties: ca: @@ -735,8 +638,6 @@ spec: type: object x-kubernetes-map-type: atomic type: object - caFile: - type: string cert: properties: configMap: @@ -766,12 +667,8 @@ spec: type: object x-kubernetes-map-type: atomic type: object - certFile: - type: string insecureSkipVerify: type: boolean - keyFile: - type: string keySecret: properties: key: @@ -802,573 +699,291 @@ spec: serverName: type: string type: object - trackTimestampsStaleness: - type: boolean + tokenUrl: + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl type: object - type: array - labels: - additionalProperties: - type: string - type: object - namespaceSelector: - properties: - any: - type: boolean - matchNames: + params: + additionalProperties: items: type: string type: array - type: object - type: object - type: object - replicas: - format: int32 - type: integer - sidecarVolumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - default: ext4 - type: string - kind: - type: string - readOnly: - default: false - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - default: "" - type: string type: object - x-kubernetes-map-type: atomic - user: + path: type: string - required: - - monitors - type: object - cinder: - properties: - fsType: + port: type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - volumeID: + proxyUrl: type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: + relabelings: items: properties: - key: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual type: string - mode: - format: int32 + modulus: + format: int64 type: integer - path: + regex: type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 - type: integer - path: + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path type: object type: array - x-kubernetes-list-type: atomic - type: object - emptyDir: - properties: - medium: + scheme: + enum: + - http + - https + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ type: string - sizeLimit: + targetPort: anyOf: - type: integer - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - volumeClaimTemplate: + tlsConfig: properties: - metadata: - type: object - spec: + ca: properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: + configMap: properties: - apiGroup: - type: string - kind: + key: type: string name: + default: "" type: string + optional: + type: boolean required: - - kind - - name + - key type: object x-kubernetes-map-type: atomic - dataSourceRef: + secret: properties: - apiGroup: - type: string - kind: + key: type: string name: + default: "" type: string - namespace: - type: string + optional: + type: boolean required: - - kind - - name + - key type: object - resources: + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - selector: + x-kubernetes-map-type: atomic + secret: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: type: string - volumeName: + name: + default: "" type: string + optional: + type: boolean + required: + - key type: object - required: - - spec + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string type: object + trackTimestampsStaleness: + type: boolean type: object - fc: - properties: - fsType: + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: + type: array + type: object + type: object + sidecar: + properties: + containers: + items: + properties: + args: items: type: string type: array x-kubernetes-list-type: atomic - wwids: + command: items: type: string type: array x-kubernetes-list-type: atomic - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - image: - properties: - pullPolicy: - type: string - reference: - type: string - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - default: default - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - x-kubernetes-list-type: atomic - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: + env: items: properties: - clusterTrustBundle: + name: + type: string + value: + type: string + valueFrom: properties: - labelSelector: + configMapKeyRef: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic - name: - type: string - optional: - type: boolean - path: - type: string - signerName: - type: string - required: - - path type: object - configMap: + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic name: default: "" type: string @@ -1376,66 +991,10 @@ spec: type: boolean type: object x-kubernetes-map-type: atomic - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - x-kubernetes-list-type: atomic - type: object - secret: + prefix: + type: string + secretRef: properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic name: default: "" type: string @@ -1443,283 +1002,137 @@ spec: type: boolean type: object x-kubernetes-map-type: atomic - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object type: object type: array x-kubernetes-list-type: atomic - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string image: type: string - keyring: - default: /etc/ceph/keyring - type: string - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - pool: - default: rbd + imagePullPolicy: type: string - readOnly: - type: boolean - secretRef: + lifecycle: properties: - name: - default: "" - type: string + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object type: object - x-kubernetes-map-type: atomic - user: - default: admin - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - default: xfs - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - sslEnabled: - type: boolean - storageMode: - default: ThinProvisioned - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - sidecars: - items: - properties: - args: - items: - type: string - type: array - x-kubernetes-list-type: atomic - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - envFrom: - items: - properties: - configMapRef: - properties: - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - type: string - secretRef: - properties: - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - type: object - type: array - x-kubernetes-list-type: atomic - image: - type: string - imagePullPolicy: - type: string - lifecycle: - properties: - postStart: + livenessProbe: properties: exec: properties: @@ -1729,6 +1142,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -1758,14 +1185,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -1778,8 +1206,40 @@ spec: required: - port type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer type: object - preStop: + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: properties: exec: properties: @@ -1789,6 +1249,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -1818,14 +1292,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -1838,2215 +1313,2350 @@ spec: required: - port type: object - type: object - stopSignal: - type: string - type: object - livenessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - failureThreshold: - format: int32 - type: integer - grpc: - properties: - port: + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: format: int32 type: integer - service: - default: "" - type: string - required: - - port type: object - httpGet: + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: properties: - host: - type: string - httpHeaders: + claims: items: properties: name: type: string - value: + request: type: string required: - name - - value type: object type: array - x-kubernetes-list-type: atomic - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + restartPolicy: + type: string + securityContext: properties: - host: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - name: - type: string - ports: - items: - properties: - containerPort: - format: int32 - type: integer - hostIP: - type: string - hostPort: - format: int32 - type: integer - name: - type: string - protocol: - default: TCP - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - failureThreshold: - format: int32 - type: integer - grpc: - properties: - port: - format: int32 + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 type: integer - service: - default: "" - type: string - required: - - port + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object - httpGet: + startupProbe: properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: + exec: + properties: + command: + items: type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - resizePolicy: - items: - properties: - resourceName: - type: string - restartPolicy: - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - properties: - claims: + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: items: properties: + mountPath: + type: string + mountPropagation: + type: string name: type: string - request: + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: type: string required: + - mountPath - name type: object type: array x-kubernetes-list-map-keys: - - name + - mountPath x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object + workingDir: + type: string + required: + - name type: object - restartPolicy: - type: string - securityContext: + type: array + volumes: + items: properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: + awsElasticBlockStore: properties: - localhostProfile: + fsType: type: string - type: + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: type: string required: - - type - type: object - capabilities: - properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: - items: - type: string - type: array - x-kubernetes-list-type: atomic + - volumeID type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: + azureDisk: properties: - level: + cachingMode: type: string - role: - type: string - type: + diskName: type: string - user: + diskURI: type: string - type: object - seccompProfile: - properties: - localhostProfile: + fsType: + default: ext4 type: string - type: + kind: type: string + readOnly: + default: false + type: boolean required: - - type + - diskName + - diskURI type: object - windowsOptions: + azureFile: properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: + readOnly: type: boolean - runAsUserName: + secretName: + type: string + shareName: type: string + required: + - secretName + - shareName type: object - type: object - startupProbe: - properties: - exec: + cephfs: properties: - command: + monitors: items: type: string type: array x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors type: object - failureThreshold: - format: int32 - type: integer - grpc: + cinder: properties: - port: - format: int32 - type: integer - service: - default: "" + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: type: string required: - - port + - volumeID type: object - httpGet: + configMap: properties: - host: - type: string - httpHeaders: + defaultMode: + format: int32 + type: integer + items: items: properties: - name: + key: type: string - value: + mode: + format: int32 + type: integer + path: type: string required: - - name - - value + - key + - path type: object type: array x-kubernetes-list-type: atomic - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - stdin: - type: boolean - stdinOnce: - type: boolean - terminationMessagePath: - type: string - terminationMessagePolicy: - type: string - tty: - type: boolean - volumeDevices: - items: - properties: - devicePath: - type: string - name: - type: string - required: - - devicePath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - devicePath - x-kubernetes-list-type: map - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - recursiveReadOnly: - type: string - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - mountPath - x-kubernetes-list-type: map - workingDir: - type: string - required: - - name - type: object - type: array - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - exporterConfig: - type: string - replicas: - type: integer - status: - type: string - version: - type: string - required: - - conditions - - exporterConfig - - version - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.version - name: Version - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - configuration: - properties: - configMap: - properties: - key: - type: string - name: - type: string - type: object - type: object - database: - properties: - dbConnectionString: - properties: - key: - type: string - secret: - type: string - type: object - dbPassword: - properties: - key: - type: string - secret: - type: string - vaultOCID: - type: string - vaultSecretName: - type: string - type: object - dbUser: - properties: - key: - type: string - secret: - type: string - type: object - dbWallet: - properties: - key: - type: string - secret: - type: string - type: object - type: object - exporter: - properties: - deployment: - properties: - args: - items: - type: string - type: array - commands: - items: - type: string - type: array - env: - additionalProperties: - type: string - type: object - image: - type: string - labels: - additionalProperties: - type: string - type: object - podTemplate: - properties: - labels: - additionalProperties: - type: string - type: object - securityContext: - properties: - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxChangePolicy: - type: string - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - x-kubernetes-list-type: atomic - supplementalGroupsPolicy: - type: string - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object - type: object - securityContext: - properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - capabilities: - properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object - type: object - service: - properties: - labels: - additionalProperties: - type: string - type: object - ports: - items: - properties: - appProtocol: - type: string name: + default: "" type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port + optional: + type: boolean type: object - type: array - type: object - type: object - inheritLabels: - items: - type: string - type: array - log: - properties: - destination: - type: string - disable: - type: boolean - filename: - type: string - volume: - properties: - persistentVolumeClaim: - properties: - claimName: - type: string - type: object - type: object - type: object - ociConfig: - properties: - configMapName: - type: string - secretName: - type: string - type: object - prometheus: - properties: - serviceMonitor: - properties: - endpoints: - items: + x-kubernetes-map-type: atomic + csi: properties: - authorization: - properties: - credentials: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - type: string - type: object - basicAuth: - properties: - password: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenFile: + driver: type: string - bearerTokenSecret: + fsType: + type: string + nodePublishSecretRef: properties: - key: - type: string name: default: "" type: string - optional: - type: boolean - required: - - key type: object x-kubernetes-map-type: atomic - enableHttp2: - type: boolean - filterRunning: - type: boolean - followRedirects: + readOnly: type: boolean - honorLabels: - type: boolean - honorTimestamps: - type: boolean - interval: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - metricRelabelings: + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: items: properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: + path: type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path type: object type: array - oauth2: + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: properties: - clientId: + metadata: + type: object + spec: properties: - configMap: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: properties: - key: + apiGroup: + type: string + kind: type: string name: - default: "" type: string - optional: - type: boolean required: - - key + - kind + - name type: object x-kubernetes-map-type: atomic - secret: + dataSourceRef: properties: - key: + apiGroup: + type: string + kind: type: string name: - default: "" type: string - optional: - type: boolean + namespace: + type: string required: - - key + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object type: object x-kubernetes-map-type: atomic - type: object - clientSecret: - properties: - key: + storageClassName: type: string - name: - default: "" + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string type: object - noProxy: + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" type: string - proxyConnectHeader: - additionalProperties: - items: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: array - type: object - x-kubernetes-map-type: atomic - proxyFromEnvironment: - type: boolean - proxyUrl: - pattern: ^(http|https|socks5)://.+$ + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" type: string - scopes: - items: - type: string - type: array - tlsConfig: - properties: - ca: - properties: - configMap: - properties: - key: - type: string - name: - default: "" + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: properties: key: type: string - name: - default: "" + mode: + format: int32 + type: integer + path: type: string - optional: - type: boolean required: - key + - path type: object - x-kubernetes-map-type: atomic - type: object - cert: - properties: - configMap: + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: properties: - key: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: type: string - name: - default: "" - type: string - optional: - type: boolean + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic required: - - key + - path type: object - x-kubernetes-map-type: atomic - secret: + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: properties: key: type: string - name: - default: "" + mode: + format: int32 + type: integer + path: type: string - optional: - type: boolean required: - key + - path type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - type: boolean - keySecret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - maxVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - serverName: - type: string - type: object - tokenUrl: - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - params: - additionalProperties: - items: - type: string - type: array - type: object - path: - type: string - port: - type: string - proxyUrl: - type: string - relabelings: - items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object type: object type: array - scheme: - enum: - - http - - https - type: string - scrapeTimeout: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - tlsConfig: - properties: - ca: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - caFile: - type: string - cert: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - certFile: - type: string - insecureSkipVerify: - type: boolean - keyFile: - type: string - keySecret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - maxVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - serverName: - type: string - type: object - trackTimestampsStaleness: + readOnly: type: boolean - type: object - type: array - labels: - additionalProperties: - type: string - type: object - namespaceSelector: - properties: - any: - type: boolean - matchNames: - items: + registry: type: string - type: array - type: object - type: object - type: object - replicas: - format: int32 - type: integer - sidecarVolumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - default: ext4 - type: string - kind: - type: string - readOnly: - default: false - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - default: "" + tenant: type: string - type: object - x-kubernetes-map-type: atomic - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" + user: type: string + volume: + type: string + required: + - registry + - volume type: object - x-kubernetes-map-type: atomic - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: + rbd: properties: - name: - default: "" + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin type: string + required: + - image + - monitors type: object - x-kubernetes-map-type: atomic - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: properties: - containerName: + key: type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: + mode: + format: int32 + type: integer + path: type: string required: - - resource + - key + - path type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - x-kubernetes-list-type: atomic - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - volumeClaimTemplate: + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: properties: - metadata: - type: object - spec: + fsType: + type: string + readOnly: + type: boolean + secretRef: properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: + name: + default: "" type: string type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string required: - - spec + - volumePath type: object + required: + - name + type: object + type: array + type: object + wallet: + properties: + additional: + items: + properties: + mountPath: + type: string + name: + type: string + secret: + type: string type: object - fc: + type: array + mountPath: + type: string + secret: + type: string + type: object + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + metricsConfig: + type: string + replicas: + type: integer + status: + type: string + version: + type: string + required: + - conditions + - metricsConfig + - version + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.metricsConfig + name: MetricsConfig + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.version + name: Version + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + azureConfig: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + type: object + database: + properties: + azure: + properties: + vaultID: + type: string + vaultPasswordSecret: + type: string + vaultUsernameSecret: + type: string + type: object + dbConnectionString: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + dbUser: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + oci: + properties: + vaultID: + type: string + vaultPasswordSecret: + type: string + type: object + type: object + databases: + additionalProperties: + properties: + dbConnectionString: properties: - fsType: + envName: type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - x-kubernetes-list-type: atomic - wwids: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - flexVolume: - properties: - driver: + key: type: string - fsType: + secret: type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - required: - - driver type: object - flocker: + dbPassword: properties: - datasetName: - type: string - datasetUUID: + envName: type: string - type: object - gcePersistentDisk: - properties: - fsType: + key: type: string - partition: - format: int32 - type: integer - pdName: + secret: type: string - readOnly: - type: boolean - required: - - pdName type: object - gitRepo: + dbUser: properties: - directory: + envName: type: string - repository: + key: type: string - revision: + secret: type: string - required: - - repository type: object - glusterfs: - properties: - endpoints: - type: string - path: + type: object + type: object + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podSecurityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + podTemplate: + properties: + labels: + additionalProperties: type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + exporterConfig: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + mountPath: + type: string + type: object + inheritLabels: + items: + type: string + type: array + log: + properties: + destination: + type: string + disable: + type: boolean + filename: + type: string + volume: + properties: + name: + type: string + persistentVolumeClaim: + properties: + claimName: + type: string + type: object + type: object + type: object + metrics: + properties: + configMap: + items: properties: - path: + key: type: string - type: + name: type: string - required: - - path type: object - image: + type: array + type: object + ociConfig: + properties: + configMap: + properties: + key: + type: string + name: + type: string + type: object + mountPath: + type: string + privateKey: + properties: + secret: + type: string + type: object + type: object + replicas: + format: int32 + type: integer + service: + properties: + labels: + additionalProperties: + type: string + type: object + ports: + items: properties: - pullPolicy: + appProtocol: + type: string + name: type: string - reference: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port type: object - iscsi: + type: array + type: object + serviceMonitor: + properties: + endpoints: + items: properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - default: default + authorization: + properties: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - x-kubernetes-list-type: atomic - readOnly: - type: boolean - secretRef: + bearerTokenSecret: properties: + key: + type: string name: default: "" type: string + optional: + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: + enableHttp2: type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: + filterRunning: type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: + followRedirects: + type: boolean + honorLabels: type: boolean - volumeID: + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: + metricRelabelings: items: properties: - clusterTrustBundle: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - name: - type: string - optional: - type: boolean - path: - type: string - signerName: - type: string - required: - - path - type: object - configMap: - properties: - items: - items: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean + proxyUrl: + pattern: ^http(s)?://.+$ + type: string + scopes: + items: + type: string + type: array + tlsConfig: + properties: + ca: + properties: + configMap: properties: key: type: string - mode: - format: int32 - type: integer - path: + name: + default: "" type: string + optional: + type: boolean required: - key - - path type: object - type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - downwardAPI: - properties: - items: - items: + x-kubernetes-map-type: atomic + secret: properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 - type: integer - path: + key: type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic + name: + default: "" + type: string + optional: + type: boolean required: - - path + - key type: object - type: array - x-kubernetes-list-type: atomic - type: object - secret: - properties: - items: - items: + x-kubernetes-map-type: atomic + type: object + cert: + properties: + configMap: properties: key: type: string - mode: - format: int32 - type: integer - path: + name: + default: "" type: string + optional: + type: boolean required: - key - - path type: object - type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - x-kubernetes-list-type: atomic - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - default: /etc/ceph/keyring - type: string - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - pool: - default: rbd - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + tokenUrl: + minLength: 1 type: string + required: + - clientId + - clientSecret + - tokenUrl type: object - x-kubernetes-map-type: atomic - user: - default: admin - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - default: xfs - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" + params: + additionalProperties: + items: type: string + type: array type: object - x-kubernetes-map-type: atomic - sslEnabled: - type: boolean - storageMode: - default: ThinProvisioned - type: string - storagePool: + path: type: string - system: + port: type: string - volumeName: + proxyUrl: type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: + relabelings: items: properties: - key: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual type: string - mode: - format: int32 + modulus: + format: int64 type: integer - path: + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: type: string - required: - - key - - path type: object type: array - x-kubernetes-list-type: atomic - optional: - type: boolean - secretName: + scheme: + enum: + - http + - https type: string - type: object - storageos: - properties: - fsType: + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ type: string - readOnly: - type: boolean - secretRef: + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: properties: - name: - default: "" + ca: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: type: string type: object - x-kubernetes-map-type: atomic - volumeName: - type: string - volumeNamespace: - type: string + trackTimestampsStaleness: + type: boolean type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - sidecars: - items: - properties: - args: - items: - type: string - type: array - x-kubernetes-list-type: atomic - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic - env: - items: - properties: - name: + type: array + type: object + type: object + sidecar: + properties: + containers: + items: + properties: + args: + items: type: string - value: + type: array + x-kubernetes-list-type: atomic + command: + items: type: string - valueFrom: + type: array + x-kubernetes-list-type: atomic + env: + items: properties: - configMapKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: + name: + type: string + value: + type: string + valueFrom: properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic type: object - x-kubernetes-map-type: atomic - resourceFieldRef: + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: + name: + default: "" type: string - required: - - resource + optional: + type: boolean type: object x-kubernetes-map-type: atomic - secretKeyRef: + prefix: + type: string + secretRef: properties: - key: - type: string name: default: "" type: string optional: type: boolean - required: - - key type: object x-kubernetes-map-type: atomic type: object - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - envFrom: - items: - properties: - configMapRef: + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: properties: - name: - default: "" + containerPort: + format: int32 + type: integer + hostIP: type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - type: string - secretRef: - properties: + hostPort: + format: int32 + type: integer name: - default: "" type: string - optional: - type: boolean + protocol: + default: TCP + type: string + required: + - containerPort type: object - x-kubernetes-map-type: atomic - type: object - type: array - x-kubernetes-list-type: atomic - image: - type: string - imagePullPolicy: - type: string - lifecycle: - properties: - postStart: + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: properties: exec: properties: @@ -4056,6 +3666,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -4082,31 +3706,158 @@ spec: x-kubernetes-int-or-string: true scheme: type: string - required: - - port + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string type: object - sleep: + seccompProfile: properties: - seconds: - format: int64 - type: integer + localhostProfile: + type: string + type: + type: string required: - - seconds + - type type: object - tcpSocket: + windowsOptions: properties: - host: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port type: object type: object - preStop: + startupProbe: properties: exec: properties: @@ -4116,6 +3867,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -4145,14 +3910,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -4163,457 +3929,871 @@ spec: - type: string x-kubernetes-int-or-string: true required: - - port + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec type: object type: object - stopSignal: - type: string - type: object - livenessProbe: - properties: - exec: + fc: properties: - command: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: items: type: string type: array x-kubernetes-list-type: atomic type: object - failureThreshold: - format: int32 - type: integer - grpc: + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: properties: - port: + fsType: + type: string + partition: format: int32 type: integer - service: - default: "" + pdName: type: string + readOnly: + type: boolean required: - - port + - pdName type: object - httpGet: + gitRepo: properties: - host: + directory: type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - path: + repository: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + revision: type: string required: - - port + - repository type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + glusterfs: properties: - host: + endpoints: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + path: + type: string + readOnly: + type: boolean required: - - port + - endpoints + - path type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - name: - type: string - ports: - items: - properties: - containerPort: - format: int32 - type: integer - hostIP: - type: string - hostPort: - format: int32 - type: integer - name: - type: string - protocol: - default: TCP - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - properties: - exec: + hostPath: properties: - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic + path: + type: string + type: + type: string + required: + - path type: object - failureThreshold: - format: int32 - type: integer - grpc: + image: properties: - port: - format: int32 - type: integer - service: - default: "" + pullPolicy: + type: string + reference: type: string - required: - - port type: object - httpGet: + iscsi: properties: - host: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: type: string - httpHeaders: + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object + type: string type: array x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: path: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + readOnly: + type: boolean + server: type: string required: - - port + - path + - server type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + persistentVolumeClaim: properties: - host: + claimName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + readOnly: + type: boolean required: - - port - type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - resizePolicy: - items: - properties: - resourceName: - type: string - restartPolicy: - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + - claimName type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID type: object - type: object - restartPolicy: - type: string - securityContext: - properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: + portworxVolume: properties: - localhostProfile: + fsType: type: string - type: + readOnly: + type: boolean + volumeID: type: string required: - - type + - volumeID type: object - capabilities: + projected: properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: + defaultMode: + format: int32 + type: integer + sources: items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: properties: - level: + group: type: string - role: + readOnly: + type: boolean + registry: type: string - type: + tenant: type: string user: type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: + volume: type: string required: - - type + - registry + - volume type: object - windowsOptions: + rbd: properties: - gmsaCredentialSpec: + fsType: type: string - gmsaCredentialSpecName: + image: type: string - hostProcess: - type: boolean - runAsUserName: + keyring: + default: /etc/ceph/keyring type: string - type: object - type: object - startupProbe: - properties: - exec: - properties: - command: + monitors: items: type: string type: array x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors type: object - failureThreshold: - format: int32 - type: integer - grpc: + scaleIO: properties: - port: - format: int32 - type: integer - service: - default: "" + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: type: string required: - - port + - gateway + - secretRef + - system type: object - httpGet: + secret: properties: - host: - type: string - httpHeaders: + defaultMode: + format: int32 + type: integer + items: items: properties: - name: + key: type: string - value: + mode: + format: int32 + type: integer + path: type: string required: - - name - - value + - key + - path type: object type: array x-kubernetes-list-type: atomic - path: + optional: + type: boolean + secretName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: type: string - required: - - port type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + vsphereVolume: properties: - host: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true required: - - port + - volumePath type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer + required: + - name type: object - stdin: - type: boolean - stdinOnce: - type: boolean - terminationMessagePath: - type: string - terminationMessagePolicy: - type: string - tty: - type: boolean - volumeDevices: - items: - properties: - devicePath: - type: string - name: - type: string - required: - - devicePath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - devicePath - x-kubernetes-list-type: map - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - recursiveReadOnly: - type: string - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - mountPath - x-kubernetes-list-type: map - workingDir: - type: string - required: - - name - type: object - type: array + type: array + type: object + wallet: + properties: + additional: + items: + properties: + mountPath: + type: string + name: + type: string + secret: + type: string + type: object + type: array + mountPath: + type: string + secret: + type: string + type: object type: object status: properties: @@ -4653,7 +4833,7 @@ spec: - type type: object type: array - exporterConfig: + metricsConfig: type: string replicas: type: integer @@ -4663,7 +4843,7 @@ spec: type: string required: - conditions - - exporterConfig + - metricsConfig - version type: object type: object @@ -4672,8 +4852,8 @@ spec: subresources: status: {} - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig + - jsonPath: .status.metricsConfig + name: MetricsConfig type: string - jsonPath: .status.status name: Status @@ -4693,7 +4873,7 @@ spec: type: object spec: properties: - configuration: + azureConfig: properties: configMap: properties: @@ -4705,8 +4885,19 @@ spec: type: object database: properties: + azure: + properties: + vaultID: + type: string + vaultPasswordSecret: + type: string + vaultUsernameSecret: + type: string + type: object dbConnectionString: properties: + envName: + type: string key: type: string secret: @@ -4714,243 +4905,250 @@ spec: type: object dbPassword: properties: + envName: + type: string key: type: string secret: type: string - vaultOCID: - type: string - vaultSecretName: - type: string type: object dbUser: properties: + envName: + type: string key: type: string secret: type: string type: object - dbWallet: + oci: properties: - key: + vaultID: type: string - secret: + vaultPasswordSecret: type: string type: object type: object - exporter: - properties: - deployment: - properties: - args: - items: + databases: + additionalProperties: + properties: + dbConnectionString: + properties: + envName: type: string - type: array - commands: - items: + key: type: string - type: array - env: - additionalProperties: + secret: type: string - type: object - image: - type: string - labels: - additionalProperties: + type: object + dbPassword: + properties: + envName: type: string - type: object - podTemplate: - properties: - labels: - additionalProperties: - type: string - type: object - securityContext: - properties: - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxChangePolicy: - type: string - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - x-kubernetes-list-type: atomic - supplementalGroupsPolicy: - type: string - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object - type: object - securityContext: - properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - capabilities: - properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object + key: + type: string + secret: + type: string + type: object + dbUser: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + type: object + type: object + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string type: object - service: + podSecurityContext: properties: - labels: - additionalProperties: - type: string + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type type: object - ports: + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: items: properties: - appProtocol: - type: string name: type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP + value: type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true required: - - port + - name + - value type: object type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + podTemplate: + properties: + labels: + additionalProperties: + type: string + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + exporterConfig: + properties: + configMap: + properties: + key: + type: string + name: + type: string type: object + mountPath: + type: string type: object inheritLabels: items: @@ -4966,6 +5164,8 @@ spec: type: string volume: properties: + name: + type: string persistentVolumeClaim: properties: claimName: @@ -4973,70 +5173,109 @@ spec: type: object type: object type: object + metrics: + properties: + configMap: + items: + properties: + key: + type: string + name: + type: string + type: object + type: array + type: object ociConfig: properties: - configMapName: - type: string - secretName: + configMap: + properties: + key: + type: string + name: + type: string + type: object + mountPath: type: string + privateKey: + properties: + secret: + type: string + type: object type: object - prometheus: + replicas: + format: int32 + type: integer + service: properties: - serviceMonitor: - properties: - endpoints: - items: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: properties: - authorization: + credentials: properties: - credentials: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: + key: + type: string + name: + default: "" type: string + optional: + type: boolean + required: + - key type: object - basicAuth: + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: properties: - password: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - bearerTokenFile: - type: string - bearerTokenSecret: + x-kubernetes-map-type: atomic + username: properties: key: type: string @@ -5049,98 +5288,87 @@ spec: - key type: object x-kubernetes-map-type: atomic - enableHttp2: - type: boolean - filterRunning: - type: boolean - followRedirects: - type: boolean - honorLabels: - type: boolean - honorTimestamps: - type: boolean - interval: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: type: string - metricRelabelings: - items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object - type: array - oauth2: - properties: - clientId: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: properties: key: type: string @@ -5153,211 +5381,66 @@ spec: - key type: object x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - type: object - noProxy: - type: string - proxyConnectHeader: - additionalProperties: - items: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: array - type: object - x-kubernetes-map-type: atomic - proxyFromEnvironment: - type: boolean - proxyUrl: - pattern: ^(http|https|socks5)://.+$ - type: string - scopes: - items: - type: string - type: array - tlsConfig: + secret: properties: - ca: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - type: boolean - keySecret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - maxVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 + key: type: string - serverName: + name: + default: "" type: string + optional: + type: boolean + required: + - key type: object - tokenUrl: - minLength: 1 + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + default: "" type: string + optional: + type: boolean required: - - clientId - - clientSecret - - tokenUrl + - key type: object - params: + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + type: object + noProxy: + type: string + proxyConnectHeader: additionalProperties: items: - type: string + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic type: array type: object - path: - type: string - port: - type: string + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^http(s)?://.+$ type: string - relabelings: + scopes: items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object + type: string type: array - scheme: - enum: - - http - - https - type: string - scrapeTimeout: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true tlsConfig: properties: ca: @@ -5389,8 +5472,6 @@ spec: type: object x-kubernetes-map-type: atomic type: object - caFile: - type: string cert: properties: configMap: @@ -5420,12 +5501,8 @@ spec: type: object x-kubernetes-map-type: atomic type: object - certFile: - type: string insecureSkipVerify: type: boolean - keyFile: - type: string keySecret: properties: key: @@ -5456,573 +5533,291 @@ spec: serverName: type: string type: object - trackTimestampsStaleness: - type: boolean - type: object - type: array - labels: - additionalProperties: - type: string - type: object - namespaceSelector: - properties: - any: - type: boolean - matchNames: - items: - type: string - type: array - type: object - type: object - type: object - replicas: - format: int32 - type: integer - sidecarVolumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - default: ext4 - type: string - kind: - type: string - readOnly: - default: false - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - default: "" + tokenUrl: + minLength: 1 type: string + required: + - clientId + - clientSecret + - tokenUrl type: object - x-kubernetes-map-type: atomic - readOnly: - type: boolean - volumeAttributes: + params: additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: + items: + type: string + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: items: properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 type: integer - path: + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path type: object type: array - x-kubernetes-list-type: atomic - type: object - emptyDir: - properties: - medium: + scheme: + enum: + - http + - https type: string - sizeLimit: + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: anyOf: - type: integer - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - volumeClaimTemplate: + tlsConfig: properties: - metadata: - type: object - spec: + ca: properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: + configMap: properties: - apiGroup: - type: string - kind: + key: type: string name: + default: "" type: string + optional: + type: boolean required: - - kind - - name + - key type: object x-kubernetes-map-type: atomic - dataSourceRef: + secret: properties: - apiGroup: - type: string - kind: + key: type: string name: + default: "" type: string - namespace: - type: string + optional: + type: boolean required: - - kind - - name + - key type: object - resources: + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - selector: + x-kubernetes-map-type: atomic + secret: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: type: string - volumeName: + name: + default: "" type: string + optional: + type: boolean + required: + - key type: object - required: - - spec + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string type: object + trackTimestampsStaleness: + type: boolean type: object - fc: - properties: - fsType: + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - x-kubernetes-list-type: atomic - wwids: + type: array + type: object + type: object + sidecar: + properties: + containers: + items: + properties: + args: items: type: string type: array x-kubernetes-list-type: atomic - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - image: - properties: - pullPolicy: - type: string - reference: - type: string - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - default: default - type: string - lun: - format: int32 - type: integer - portals: + command: items: type: string type: array x-kubernetes-list-type: atomic - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: + env: items: properties: - clusterTrustBundle: + name: + type: string + value: + type: string + valueFrom: properties: - labelSelector: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic - name: - type: string - optional: - type: boolean - path: - type: string - signerName: - type: string - required: - - path type: object - configMap: + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic name: default: "" type: string @@ -6030,66 +5825,10 @@ spec: type: boolean type: object x-kubernetes-map-type: atomic - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - x-kubernetes-list-type: atomic - type: object - secret: + prefix: + type: string + secretRef: properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic name: default: "" type: string @@ -6097,283 +5836,244 @@ spec: type: boolean type: object x-kubernetes-map-type: atomic - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object type: object type: array x-kubernetes-list-type: atomic - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string image: type: string - keyring: - default: /etc/ceph/keyring - type: string - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - pool: - default: rbd + imagePullPolicy: type: string - readOnly: - type: boolean - secretRef: + lifecycle: properties: - name: - default: "" - type: string + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object type: object - x-kubernetes-map-type: atomic - user: - default: admin - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - default: xfs - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: + livenessProbe: properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - sslEnabled: - type: boolean - storageMode: - default: ThinProvisioned - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: items: properties: - key: - type: string - mode: + containerPort: format: int32 type: integer - path: + hostIP: type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - sidecars: - items: - properties: - args: - items: - type: string - type: array - x-kubernetes-list-type: atomic - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - envFrom: - items: - properties: - configMapRef: - properties: + hostPort: + format: int32 + type: integer name: - default: "" type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - type: string - secretRef: - properties: - name: - default: "" + protocol: + default: TCP type: string - optional: - type: boolean + required: + - containerPort type: object - x-kubernetes-map-type: atomic - type: object - type: array - x-kubernetes-list-type: atomic - image: - type: string - imagePullPolicy: - type: string - lifecycle: - properties: - postStart: + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: properties: exec: properties: @@ -6383,6 +6083,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -6412,14 +6126,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -6432,8 +6147,134 @@ spec: required: - port type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object - preStop: + startupProbe: properties: exec: properties: @@ -6443,6 +6284,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -6472,14 +6327,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -6492,455 +6348,869 @@ spec: required: - port type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID type: object - stopSignal: - type: string - type: object - livenessProbe: - properties: - exec: + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: properties: - command: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: items: type: string type: array x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors type: object - failureThreshold: - format: int32 - type: integer - grpc: + cinder: properties: - port: - format: int32 - type: integer - service: - default: "" + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: type: string required: - - port + - volumeID type: object - httpGet: + configMap: properties: - host: - type: string - httpHeaders: + defaultMode: + format: int32 + type: integer + items: items: properties: - name: + key: type: string - value: + mode: + format: int32 + type: integer + path: type: string required: - - name - - value + - key + - path type: object type: array x-kubernetes-list-type: atomic - path: + name: + default: "" type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object required: - - port + - driver type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: properties: - host: + medium: type: string - port: + sizeLimit: anyOf: - type: integer - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - required: - - port type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - name: - type: string - ports: - items: - properties: - containerPort: - format: int32 - type: integer - hostIP: - type: string - hostPort: - format: int32 - type: integer - name: - type: string - protocol: - default: TCP - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - properties: - exec: + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: properties: - command: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: items: type: string type: array x-kubernetes-list-type: atomic type: object - failureThreshold: - format: int32 - type: integer - grpc: + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: properties: - port: + fsType: + type: string + partition: format: int32 type: integer - service: - default: "" + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: type: string required: - - port + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string type: object - httpGet: + iscsi: properties: - host: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: type: string - httpHeaders: + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object + type: string type: array x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: path: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + readOnly: + type: boolean + server: type: string required: - - port + - path + - server type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + persistentVolumeClaim: properties: - host: + claimName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + readOnly: + type: boolean required: - - port - type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - resizePolicy: - items: - properties: - resourceName: - type: string - restartPolicy: - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + - claimName type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID type: object - type: object - restartPolicy: - type: string - securityContext: - properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: + portworxVolume: properties: - localhostProfile: + fsType: type: string - type: + readOnly: + type: boolean + volumeID: type: string required: - - type + - volumeID type: object - capabilities: + projected: properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: + defaultMode: + format: int32 + type: integer + sources: items: - type: string + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object type: array x-kubernetes-list-type: atomic type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: + quobyte: properties: - level: + group: type: string - role: + readOnly: + type: boolean + registry: type: string - type: + tenant: type: string user: type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: + volume: type: string required: - - type + - registry + - volume type: object - windowsOptions: + rbd: properties: - gmsaCredentialSpec: + fsType: type: string - gmsaCredentialSpecName: + image: type: string - hostProcess: - type: boolean - runAsUserName: + keyring: + default: /etc/ceph/keyring type: string - type: object - type: object - startupProbe: - properties: - exec: - properties: - command: + monitors: items: type: string type: array x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors type: object - failureThreshold: - format: int32 - type: integer - grpc: + scaleIO: properties: - port: - format: int32 - type: integer - service: - default: "" + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: type: string required: - - port + - gateway + - secretRef + - system type: object - httpGet: + secret: properties: - host: - type: string - httpHeaders: + defaultMode: + format: int32 + type: integer + items: items: properties: - name: + key: type: string - value: + mode: + format: int32 + type: integer + path: type: string required: - - name - - value + - key + - path type: object type: array x-kubernetes-list-type: atomic - path: + optional: + type: boolean + secretName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: type: string - required: - - port type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + vsphereVolume: properties: - host: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true required: - - port + - volumePath type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer + required: + - name type: object - stdin: - type: boolean - stdinOnce: - type: boolean - terminationMessagePath: - type: string - terminationMessagePolicy: - type: string - tty: - type: boolean - volumeDevices: - items: - properties: - devicePath: - type: string - name: - type: string - required: - - devicePath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - devicePath - x-kubernetes-list-type: map - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - recursiveReadOnly: - type: string - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - mountPath - x-kubernetes-list-type: map - workingDir: - type: string - required: - - name - type: object - type: array + type: array + type: object + wallet: + properties: + additional: + items: + properties: + mountPath: + type: string + name: + type: string + secret: + type: string + type: object + type: array + mountPath: + type: string + secret: + type: string + type: object type: object status: properties: @@ -6980,7 +7250,7 @@ spec: - type type: object type: array - exporterConfig: + metricsConfig: type: string replicas: type: integer @@ -6990,7 +7260,7 @@ spec: type: string required: - conditions - - exporterConfig + - metricsConfig - version type: object type: object diff --git a/config/samples/observability/databaseobserver.yaml b/config/samples/observability/databaseobserver.yaml index 5962a072..8b5245cc 100644 --- a/config/samples/observability/databaseobserver.yaml +++ b/config/samples/observability/databaseobserver.yaml @@ -6,18 +6,20 @@ metadata: spec: database: dbUser: + key: "username" secret: db-secret dbPassword: + key: "password" secret: db-secret dbConnectionString: + key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet + wallet: + secret: instance-wallet - prometheus: - serviceMonitor: + serviceMonitor: labels: release: prometheus diff --git a/config/samples/observability/v1/databaseobserver.yaml b/config/samples/observability/v1/databaseobserver.yaml index 82a7e89e..26ffe3ff 100644 --- a/config/samples/observability/v1/databaseobserver.yaml +++ b/config/samples/observability/v1/databaseobserver.yaml @@ -6,7 +6,7 @@ metadata: labels: app.kubernetes.io/name: observability-exporter app.kubernetes.io/instance: obs-sample - app.kubernetes.io/version: 1.5.1 + app.kubernetes.io/version: 2.0.2 spec: database: dbUser: @@ -21,61 +21,56 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet - - inheritLabels: - - app.kubernetes.io/name - - app.kubernetes.io/instance - - app.kubernetes.io/version - - sidecars: [ ] - sidecarVolumes: [ ] - - exporter: - deployment: - env: - TNS_ADMIN: /some/custom/path - ORACLE_HOME: /some/custom/path - DB_ROLE: SYSDBA - image: "container-registry.oracle.com/database/observability-exporter:1.5.1" - args: [ "--log.level=info" ] - commands: [ "/oracledb_exporter" ] - - labels: - environment: dev - podTemplate: - labels: - environment: dev - - service: + wallet: + secret: instance-wallet + + sidecar: + containers: [ ] + volumes: [ ] + + deployment: + env: + TNS_ADMIN: /some/custom/path + ORACLE_HOME: /some/custom/path + DB_ROLE: SYSDBA + image: "container-registry.oracle.com/database/observability-exporter:2.0.2" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + labels: + environment: dev + podTemplate: labels: environment: dev - configuration: - configMap: - key: "config.toml" - name: "devcm-oradevdb-config" + service: + labels: + environment: dev prometheus: serviceMonitor: labels: release: prometheus + metrics: + configMap: + - name: "devcm-oradevdb-config" + key: "config.toml" log: filename: "alert.log" - path: "/log" - + destination: "/log" volume: - name: volume persistentVolumeClaim: claimName: "my-pvc" - replicas: 1 - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey + configMap: + key: config + name: oci-cred + privateKey: + secret: oci-privatekey + mountPath: "/.oci" + replicas: 1 diff --git a/config/samples/observability/v1/databaseobserver_customization_fields.yaml b/config/samples/observability/v1/databaseobserver_customization_fields.yaml index d68d1059..5cc5d319 100644 --- a/config/samples/observability/v1/databaseobserver_customization_fields.yaml +++ b/config/samples/observability/v1/databaseobserver_customization_fields.yaml @@ -17,38 +17,40 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet - - exporter: - deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.5.1" - args: - - "--log.level=info" - commands: - - "/oracledb_exporter" - env: - TNS_ADMIN: /some/custom/path + wallet: + secret: instance-wallet + + + + deployment: + image: "container-registry.oracle.com/database/observability-exporter:2.0.2" + args: + - "--log.level=info" + commands: + - "/oracledb_exporter" + env: + TNS_ADMIN: /some/custom/path + labels: + environment: dev + podTemplate: labels: environment: dev - podTemplate: - labels: - environment: dev - service: - ports: + + service: + ports: - name: "metrics" port: 9161 targetPort: 9161 - labels: - environment: dev + labels: + environment: dev - prometheus: - serviceMonitor: - endpoints: - - bearerTokenSecret: - key: '' - interval: 15s - port: metrics - labels: - release: prometheus + + serviceMonitor: + endpoints: + - bearerTokenSecret: + key: '' + interval: 15s + port: metrics + labels: + release: prometheus diff --git a/config/samples/observability/v1/databaseobserver_logs_promtail.yaml b/config/samples/observability/v1/databaseobserver_logs_promtail.yaml index 40379ec2..31a56573 100644 --- a/config/samples/observability/v1/databaseobserver_logs_promtail.yaml +++ b/config/samples/observability/v1/databaseobserver_logs_promtail.yaml @@ -6,7 +6,7 @@ metadata: labels: app.kubernetes.io/name: observability-exporter app.kubernetes.io/instance: obs-sample - app.kubernetes.io/version: 1.5.1 + app.kubernetes.io/version: 2.0.2 spec: database: dbUser: @@ -21,51 +21,48 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet + wallet: + secret: instance-wallet + inheritLabels: - app.kubernetes.io/name - app.kubernetes.io/instance - app.kubernetes.io/version - sidecars: - - name: promtail - image: grafana/promtail - args: - - -config.file=/etc/promtail/promtail.yaml - volumeMounts: - - name: config - mountPath: /etc/promtail - - name: log-volume - mountPath: /log + sidecar: + containers: + - name: promtail + image: grafana/promtail + args: + - -config.file=/etc/promtail/promtail.yaml + volumeMounts: + - name: config + mountPath: /etc/promtail + - name: log-volume + mountPath: /log + volumes: + - name: config + configMap: + name: promtail-sidecar-config - sidecarVolumes: - - name: config - configMap: - name: promtail-sidecar-config - exporter: - deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.5.1" - args: [ "--log.level=info" ] - commands: [ "/oracledb_exporter" ] + deployment: + image: "container-registry.oracle.com/database/observability-exporter:2.0.2" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] - configuration: + metrics: configMap: - key: "config.toml" - name: "devcm-oradevdb-config" + - name: "devcm-oradevdb-config" + key: "config.toml" + - prometheus: - serviceMonitor: - labels: - release: prometheus + serviceMonitor: + labels: + release: prometheus log: destination: "/log" - filename: "alert.log" + filename: "alert.log" replicas: 1 - - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey diff --git a/config/samples/observability/v1alpha1/databaseobserver.yaml b/config/samples/observability/v1alpha1/databaseobserver.yaml index 24672d8b..077d4941 100644 --- a/config/samples/observability/v1alpha1/databaseobserver.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver.yaml @@ -6,7 +6,7 @@ metadata: labels: app.kubernetes.io/name: observability-exporter app.kubernetes.io/instance: obs-sample - app.kubernetes.io/version: 1.5.1 + app.kubernetes.io/version: 2.0.2 spec: database: dbUser: @@ -21,8 +21,9 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet + wallet: + secret: instance-wallet + inheritLabels: @@ -30,51 +31,57 @@ spec: - app.kubernetes.io/instance - app.kubernetes.io/version - sidecars: [ ] - sidecarVolumes: [ ] - - exporter: - deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.5.1" - args: [ "--log.level=info" ] - commands: [ "/oracledb_exporter" ] - env: - TNS_ADMIN: /some/custom/path - ORACLE_HOME: /some/custom/path - labels: - environment: dev - podTemplate: - labels: - environment: dev + sidecar: + containers: [ ] + volumes: [ ] + + + deployment: + env: + TNS_ADMIN: /some/custom/path + ORACLE_HOME: /some/custom/path + DB_ROLE: SYSDBA + image: "container-registry.oracle.com/database/observability-exporter:2.0.2" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] - service: + labels: + environment: dev + podTemplate: labels: environment: dev - configuration: + service: + labels: + environment: dev + + metrics: configMap: - key: "config.toml" - name: "devcm-oradevdb-config" + - name: "devcm-oradevdb-config" + key: "config.toml" - prometheus: - serviceMonitor: - labels: - release: prometheus + serviceMonitor: + labels: + release: prometheus log: filename: "alert.log" - path: "/log" + destination: "/log" volume: - name: volume persistentVolumeClaim: claimName: "my-pvc" + ociConfig: + configMap: + key: config + name: oci-cred + privateKey: + secret: oci-privatekey + mountPath: "/.oci" + replicas: 1 - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey diff --git a/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml b/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml index 8e0d0623..e0ad4e9c 100644 --- a/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_custom_config.yaml @@ -3,10 +3,6 @@ apiVersion: observability.oracle.com/v1alpha1 kind: DatabaseObserver metadata: name: obs-sample - labels: - app.kubernetes.io/name: observability-exporter - app.kubernetes.io/instance: obs-sample - app.kubernetes.io/version: 1.5.1 spec: database: dbUser: @@ -21,26 +17,21 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet + wallet: + secret: instance-wallet - inherit_labels: - - app.kubernetes.io/name - - app.kubernetes.io/instance - - app.kubernetes.io/version - exporter: - deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.5.1" - args: [ "--log.level=info" ] - commands: [ "/oracledb_exporter" ] + deployment: + image: "container-registry.oracle.com/database/observability-exporter:2.0.2" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] - prometheus: - serviceMonitor: - labels: - release: prometheus - configuration: + serviceMonitor: + labels: + release: prometheus + + metrics: configMap: - key: "config.toml" - name: "devcm-oradevdb-config" \ No newline at end of file + - name: "devcm-oradevdb-config" + key: "config.toml" diff --git a/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml b/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml index b8ada151..f2345bee 100644 --- a/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_logs_promtail.yaml @@ -3,10 +3,6 @@ apiVersion: observability.oracle.com/v1alpha1 kind: DatabaseObserver metadata: name: obs-sample - labels: - app.kubernetes.io/name: observability-exporter - app.kubernetes.io/instance: obs-sample - app.kubernetes.io/version: 1.5.1 spec: database: dbUser: @@ -21,44 +17,37 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet - - inheritLabels: - - app.kubernetes.io/name - - app.kubernetes.io/instance - - app.kubernetes.io/version - - sidecars: - - name: promtail - image: grafana/promtail - args: - - -config.file=/etc/promtail/promtail.yaml - volumeMounts: - - name: config - mountPath: /etc/promtail - - name: log-volume - mountPath: /log - - sidecarVolumes: - - name: config - configMap: - name: promtail-sidecar-config - exporter: - deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.5.1" - args: [ "--log.level=info" ] - commands: [ "/oracledb_exporter" ] - - configuration: + wallet: + secret: instance-wallet + + sidecar: + containers: + - name: promtail + image: grafana/promtail + args: + - -config.file=/etc/promtail/promtail.yaml + volumeMounts: + - name: config + mountPath: /etc/promtail + - name: log-volume + mountPath: /log + volumes: + - name: config + configMap: + name: promtail-sidecar-config + + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + serviceMonitor: + labels: + release: prometheus + metrics: configMap: - key: "config.toml" - name: "devcm-oradevdb-config" - - prometheus: - serviceMonitor: - labels: - release: prometheus + - name: "devcm-oradevdb-config" + key: "config.toml" log: destination: "/log" @@ -66,6 +55,3 @@ spec: replicas: 1 - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey diff --git a/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml b/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml index 27520061..9c2044bf 100644 --- a/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml @@ -14,10 +14,10 @@ spec: dbConnectionString: secret: db-secret - dbWallet: - secret: instance-wallet + wallet: + secret: instance-wallet - prometheus: - serviceMonitor: - labels: - release: prometheus \ No newline at end of file + + serviceMonitor: + labels: + release: prometheus \ No newline at end of file diff --git a/config/samples/observability/v1alpha1/databaseobserver_vault.yaml b/config/samples/observability/v1alpha1/databaseobserver_vault.yaml deleted file mode 100644 index 2fc3c9f0..00000000 --- a/config/samples/observability/v1alpha1/databaseobserver_vault.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# example -apiVersion: observability.oracle.com/v1alpha1 -kind: DatabaseObserver -metadata: - name: obs-sample -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - vaultSecretName: sample_secret - vaultOCID: ocid1.vault.oc1.. - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallet - - prometheus: - serviceMonitor: - labels: - release: prometheus - - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/observability/v1alpha1/databaseobserver_vault_oci.yaml b/config/samples/observability/v1alpha1/databaseobserver_vault_oci.yaml new file mode 100644 index 00000000..5efb9bff --- /dev/null +++ b/config/samples/observability/v1alpha1/databaseobserver_vault_oci.yaml @@ -0,0 +1,29 @@ +# example +apiVersion: observability.oracle.com/v1alpha1 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + database: + dbUser: + secret: db-secret + dbConnectionString: + secret: db-secret + oci: + vaultID: ocid1.vault.oc1.. + vaultPasswordSecret: sample_secret + + wallet: + secret: instance-wallet + + serviceMonitor: + labels: + release: prometheus + + ociConfig: + configMap: + key: config + name: oci-cred + privateKey: + secret: oci-privatekey + mountPath: "/.oci" \ No newline at end of file diff --git a/config/samples/observability/v4/databaseobserver.yaml b/config/samples/observability/v4/databaseobserver.yaml index f7b310f7..1abcc721 100644 --- a/config/samples/observability/v4/databaseobserver.yaml +++ b/config/samples/observability/v4/databaseobserver.yaml @@ -21,59 +21,60 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet + wallet: + secret: instance-wallet inheritLabels: - app.kubernetes.io/name - app.kubernetes.io/instance - app.kubernetes.io/version - sidecars: [ ] - sidecarVolumes: [ ] - - exporter: - deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.5.1" - args: [ "--log.level=info" ] - commands: [ "/oracledb_exporter" ] - env: - TNS_ADMIN: /some/custom/path - ORACLE_HOME: /some/custom/path + sidecar: + containers: [ ] + volumes: [ ] + + deployment: + env: + TNS_ADMIN: /some/custom/path + ORACLE_HOME: /some/custom/path + DB_ROLE: SYSDBA + image: "container-registry.oracle.com/database/observability-exporter:2.0.2" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + labels: + environment: dev + podTemplate: labels: environment: dev - podTemplate: - labels: - environment: dev - service: - labels: - environment: dev + service: + labels: + environment: dev + serviceMonitor: + labels: + release: prometheus - configuration: + metrics: configMap: - key: "config.toml" - name: "devcm-oradevdb-config" - - prometheus: - serviceMonitor: - labels: - release: prometheus + - name: "devcm-oradevdb-config" + key: "config.toml" log: filename: "alert.log" - path: "/log" + destination: "/log" volume: - name: volume persistentVolumeClaim: claimName: "my-pvc" - replicas: 1 - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey - + configMap: + key: config + name: oci-cred + privateKey: + secret: oci-privatekey + mountPath: "/.oci" + replicas: 1 diff --git a/config/samples/observability/v4/databaseobserver_custom_config.yaml b/config/samples/observability/v4/databaseobserver_custom_config.yaml index dd2e3da5..e82f7478 100644 --- a/config/samples/observability/v4/databaseobserver_custom_config.yaml +++ b/config/samples/observability/v4/databaseobserver_custom_config.yaml @@ -6,7 +6,7 @@ metadata: labels: app.kubernetes.io/name: observability-exporter app.kubernetes.io/instance: obs-sample - app.kubernetes.io/version: latest + app.kubernetes.io/version: 2.0.2 spec: database: dbUser: @@ -21,26 +21,26 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet + wallet: + secret: instance-wallet inherit_labels: - app.kubernetes.io/name - app.kubernetes.io/instance - app.kubernetes.io/version - exporter: - deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.5.1" - args: [ "--log.level=info" ] - commands: [ "/oracledb_exporter" ] - configuration: + deployment: + image: "container-registry.oracle.com/database/observability-exporter:1.5.1" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + metrics: configMap: - key: "config.toml" - name: "devcm-oradevdb-config" + - name: "devcm-oradevdb-config" + key: "config.toml" + - prometheus: - serviceMonitor: - labels: - release: prometheus \ No newline at end of file + serviceMonitor: + labels: + release: prometheus \ No newline at end of file diff --git a/config/samples/observability/v4/databaseobserver_exporter_config_file.yaml b/config/samples/observability/v4/databaseobserver_exporter_config_file.yaml new file mode 100644 index 00000000..fac3359e --- /dev/null +++ b/config/samples/observability/v4/databaseobserver_exporter_config_file.yaml @@ -0,0 +1,42 @@ +# example +apiVersion: observability.oracle.com/v4 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + + deployment: + args: + - "--config.file=/config/config.yaml" + + # alternatively, the CONFIG_FILE environment variable can be set + #env: + # CONFIG_FILE: "/config/config.yaml" + + exporterConfig: + configMap: + key: config.yaml + name: exporter-config-file + + databases: + db1: + dbUser: + secret: db1-secret + dbPassword: + secret: db1-secret + dbConnectionString: + secret: db1-secret + db2: + dbUser: + secret: db2-secret + dbPassword: + secret: db2-secret + dbConnectionString: + secret: db2-secret + wallet: + secret: instance-wallet + + + serviceMonitor: + labels: + release: prometheus diff --git a/config/samples/observability/v4/databaseobserver_logs_promtail.yaml b/config/samples/observability/v4/databaseobserver_logs_promtail.yaml index ca0f413d..ae639308 100644 --- a/config/samples/observability/v4/databaseobserver_logs_promtail.yaml +++ b/config/samples/observability/v4/databaseobserver_logs_promtail.yaml @@ -6,7 +6,7 @@ metadata: labels: app.kubernetes.io/name: observability-exporter app.kubernetes.io/instance: obs-sample - app.kubernetes.io/version: latest + app.kubernetes.io/version: 2.0.2 spec: database: dbUser: @@ -21,53 +21,54 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: instance-wallet + wallet: + secret: instance-wallet inheritLabels: - app.kubernetes.io/name - app.kubernetes.io/instance - app.kubernetes.io/version - sidecars: - - name: promtail - image: grafana/promtail - args: - - -config.file=/etc/promtail/promtail.yaml - volumeMounts: - - name: config - mountPath: /etc/promtail - - name: log-volume - mountPath: /log + sidecar: + containers: + - name: promtail + image: grafana/promtail + args: + - -config.file=/etc/promtail/promtail.yaml + volumeMounts: + - name: config + mountPath: /etc/promtail + - name: log-volume + mountPath: /log + volumes: + - name: config + configMap: + name: promtail-sidecar-config - sidecarVolumes: - - name: config - configMap: - name: promtail-sidecar-config - exporter: - deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.5.1" - args: [ "--log.level=info" ] - commands: [ "/oracledb_exporter" ] - configuration: - configMap: - key: "config.toml" - name: "devcm-oradevdb-config" + deployment: + image: "container-registry.oracle.com/database/observability-exporter:2.0.2" + args: [ "--log.level=info" ] + commands: [ "/oracledb_exporter" ] + + + + serviceMonitor: + labels: + release: prometheus - prometheus: - serviceMonitor: - labels: - release: prometheus + metrics: + configMap: + - name: "devcm-oradevdb-config" + key: "config.toml" log: destination: "/log" filename: "alert.log" - replicas: 1 ociConfig: configMapName: oci-cred secretName: oci-privatekey - + replicas: 1 diff --git a/config/samples/observability/v4/databaseobserver_minimal.yaml b/config/samples/observability/v4/databaseobserver_minimal.yaml index 5962a072..9f913058 100644 --- a/config/samples/observability/v4/databaseobserver_minimal.yaml +++ b/config/samples/observability/v4/databaseobserver_minimal.yaml @@ -14,10 +14,10 @@ spec: dbConnectionString: secret: db-secret - dbWallet: - secret: instance-wallet + wallet: + secret: instance-wallet - prometheus: - serviceMonitor: - labels: - release: prometheus + + serviceMonitor: + labels: + release: prometheus diff --git a/config/samples/observability/v4/databaseobserver_vault.yaml b/config/samples/observability/v4/databaseobserver_vault.yaml deleted file mode 100644 index 4f5845f6..00000000 --- a/config/samples/observability/v4/databaseobserver_vault.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# example -apiVersion: observability.oracle.com/v4 -kind: DatabaseObserver -metadata: - name: obs-sample - labels: - app.kubernetes.io/name: observability-exporter - app.kubernetes.io/instance: obs-sample - app.kubernetes.io/version: latest -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - vaultSecretName: sample_secret - vaultOCID: ocid1.vault.oc1.. - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallet - - inherit_labels: - - app.kubernetes.io/name - - app.kubernetes.io/instance - - app.kubernetes.io/version - - prometheus: - serviceMonitor: - labels: - release: prometheus - - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/observability/v4/databaseobserver_vault_azure.yaml b/config/samples/observability/v4/databaseobserver_vault_azure.yaml new file mode 100644 index 00000000..2c83ccc8 --- /dev/null +++ b/config/samples/observability/v4/databaseobserver_vault_azure.yaml @@ -0,0 +1,24 @@ +# example +apiVersion: observability.oracle.com/v4 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + database: + dbConnectionString: + secret: db-secret + azure: + vaultID: "..." + vaultUsernameSecret: sample_un_secret + vaultPasswordSecret: sample_pwd_secret + + wallet: + secret: instance-wallet + + serviceMonitor: + labels: + release: prometheus + + azureConfig: + configMap: + name: azure-cred \ No newline at end of file diff --git a/config/samples/observability/v4/databaseobserver_vault_oci.yaml b/config/samples/observability/v4/databaseobserver_vault_oci.yaml new file mode 100644 index 00000000..9e60c16f --- /dev/null +++ b/config/samples/observability/v4/databaseobserver_vault_oci.yaml @@ -0,0 +1,29 @@ +# example +apiVersion: observability.oracle.com/v4 +kind: DatabaseObserver +metadata: + name: obs-sample +spec: + database: + dbUser: + secret: db-secret + dbConnectionString: + secret: db-secret + oci: + vaultID: ocid1.vault.oc1.. + vaultPasswordSecret: sample_secret + + wallet: + secret: instance-wallet + + serviceMonitor: + labels: + release: prometheus + + ociConfig: + configMap: + key: config + name: oci-cred + privateKey: + secret: oci-privatekey + mountPath: "/.oci" \ No newline at end of file diff --git a/controllers/observability/databaseobserver_controller.go b/controllers/observability/databaseobserver_controller.go index e17ee0b3..468f35c5 100644 --- a/controllers/observability/databaseobserver_controller.go +++ b/controllers/observability/databaseobserver_controller.go @@ -197,7 +197,7 @@ func (r *DatabaseObserverReconciler) initialize(ctx context.Context, a *api.Data }) a.Status.Status = string(constants.StatusObservabilityPending) - a.Status.ExporterConfig = constants.UnknownValue + a.Status.MetricsConfig = constants.UnknownValue a.Status.Version = constants.UnknownValue if e := r.Status().Update(ctx, a); e != nil { r.Log.WithName(constants.LogReconcile).Error(e, constants.ErrorStatusUpdate) @@ -212,42 +212,74 @@ func (r *DatabaseObserverReconciler) initialize(ctx context.Context, a *api.Data // validateSpecs method checks the values and secrets passed in the spec func (r *DatabaseObserverReconciler) validateSpecs(a *api.DatabaseObserver) error { - // If either Vault Fields are empty, then assume a DBPassword secret is supplied. If the DBPassword secret not found, then error out - if a.Spec.Database.DBPassword.VaultOCID == "" || a.Spec.Database.DBPassword.VaultSecretName == "" { - dbSecret := &corev1.Secret{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: a.Spec.Database.DBPassword.SecretName, Namespace: a.Namespace}, dbSecret); e != nil { - r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPasswordSecretMissing) + // if Database Wallet is defined, validate resource + + // if Sidecar volumes set, validate volumeSource + + // if Custom Exporter is defined, validate resource + if exporterConfig := a.Spec.ExporterConfig.ConfigMap.Name; exporterConfig != "" { + configMap := &corev1.ConfigMap{} + resource := types.NamespacedName{Name: exporterConfig, Namespace: a.Namespace} + + if e := r.Get(context.TODO(), resource, configMap); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorConfigMapSpecifiedMissing) + return e + } + } + + // if metrics custom is set, check configMaps + for _, cm := range a.Spec.Metrics.Configmap { + configMap := &corev1.ConfigMap{} + resource := types.NamespacedName{Name: cm.Name, Namespace: a.Namespace} + + if e := r.Get(context.TODO(), resource, configMap); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorConfigMapSpecifiedMissing) return e } } + checked := map[string]bool{} // Does DB Connection String Secret Name actually exist - dbConnectSecret := &corev1.Secret{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: a.Spec.Database.DBConnectionString.SecretName, Namespace: a.Namespace}, dbConnectSecret); e != nil { - r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBConnectionStringSecretMissing) - return e + if connectionString := a.Spec.Database.DBConnectionString.SecretName; connectionString != "" { + secret := &corev1.Secret{} + resource := types.NamespacedName{Name: connectionString, Namespace: a.Namespace} + + if e := r.Get(context.TODO(), resource, secret); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBConnectionStringSecretMissing) + return e + } + checked[connectionString] = true } // Does DB User String Secret Name actually exist - dbUserSecret := &corev1.Secret{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: a.Spec.Database.DBUser.SecretName, Namespace: a.Namespace}, dbUserSecret); e != nil { - r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPUserSecretMissing) - return e + if usernameString := a.Spec.Database.DBUser.SecretName; usernameString != "" && !checked[usernameString] { + secret := &corev1.Secret{} + resource := types.NamespacedName{Name: usernameString, Namespace: a.Namespace} + + if e := r.Get(context.TODO(), resource, secret); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPUserSecretMissing) + return e + } + checked[usernameString] = true } - // Does a custom configuration configmap actually exist, if provided - if configurationCMName := a.Spec.ExporterConfig.Configmap.Name; configurationCMName != "" { - configurationCM := &corev1.ConfigMap{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: configurationCMName, Namespace: a.Namespace}, configurationCM); e != nil { - r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorConfigmapMissing) + // Does DB Password String Secret Name actually exist + if passwordString := a.Spec.Database.DBPassword.SecretName; passwordString != "" && !checked[passwordString] { + secret := &corev1.Secret{} + resource := types.NamespacedName{Name: passwordString, Namespace: a.Namespace} + + if e := r.Get(context.TODO(), resource, secret); e != nil { + r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBPwdSecretMissing) return e } + checked[passwordString] = true } // Does DBWallet actually exist, if provided - if dbWalletSecretName := a.Spec.Database.DBWallet.SecretName; dbWalletSecretName != "" { - dbWalletSecret := &corev1.Secret{} - if e := r.Get(context.TODO(), types.NamespacedName{Name: dbWalletSecretName, Namespace: a.Namespace}, dbWalletSecret); e != nil { + if walletString := a.Spec.Wallet.SecretName; walletString != "" { + secret := &corev1.Secret{} + resource := types.NamespacedName{Name: walletString, Namespace: a.Namespace} + if e := r.Get(context.TODO(), resource, secret); e != nil { r.Recorder.Event(a, corev1.EventTypeWarning, constants.EventReasonSpecError, constants.EventMessageSpecErrorDBWalletSecretMissing) return e } @@ -446,7 +478,7 @@ func (r *DatabaseObserverReconciler) validateDeploymentReadiness(a *api.Database Message: constants.MessageExporterDeploymentSuccessful, }) a.Status.Version = constants.GetExporterVersion(a) - a.Status.ExporterConfig = constants.GetExporterConfig(a) + a.Status.MetricsConfig = constants.GetMetricsConfig(a) return ctrl.Result{}, nil } @@ -471,6 +503,7 @@ func (r *DatabaseObserverReconciler) validateCustomResourceReadiness(ctx context Message: constants.MessageCRValidationWaiting, }) a.Status.Status = string(constants.StatusObservabilityPending) + } else if meta.IsStatusConditionFalse(a.Status.Conditions, constants.IsExporterDeploymentReady) || meta.IsStatusConditionFalse(a.Status.Conditions, constants.IsExporterServiceReady) || meta.IsStatusConditionFalse(a.Status.Conditions, constants.IsExporterServiceMonitorReady) { diff --git a/controllers/observability/databaseobserver_resource.go b/controllers/observability/databaseobserver_resource.go index 6be6f693..82febe43 100644 --- a/controllers/observability/databaseobserver_resource.go +++ b/controllers/observability/databaseobserver_resource.go @@ -40,8 +40,8 @@ func (resource *ObservabilityDeploymentResource) generate(a *api.DatabaseObserve rReplicas := constants.GetExporterReplicas(a) rEnvs := constants.GetExporterEnvs(a) - rLabels := constants.GetLabels(a, a.Spec.Exporter.Deployment.Labels) - rPodLabels := constants.GetLabels(a, a.Spec.Exporter.Deployment.DeploymentPodTemplate.Labels) + rLabels := constants.GetLabels(a, a.Spec.Deployment.Labels) + rPodLabels := constants.GetLabels(a, a.Spec.Deployment.DeploymentPodTemplate.Labels) rSelector := constants.GetSelectorLabel(a) rDeploymentSecurityContext := constants.GetExporterDeploymentSecurityContext(a) @@ -108,7 +108,7 @@ func (resource *ObservabilityDeploymentResource) generate(a *api.DatabaseObserve func (resource *ObservabilityServiceResource) generate(a *api.DatabaseObserver, scheme *runtime.Scheme) (*unstructured.Unstructured, error) { rServiceName := a.Name - rLabels := constants.GetLabels(a, a.Spec.Exporter.Service.Labels) + rLabels := constants.GetLabels(a, a.Spec.Service.Labels) rSelector := constants.GetSelectorLabel(a) rPorts := constants.GetExporterServicePort(a) @@ -141,7 +141,7 @@ func (resource *ObservabilityServiceMonitorResource) generate(a *api.DatabaseObs rEndpoints := constants.GetEndpoints(a) rSelector := constants.GetSelectorLabel(a) - rLabels := constants.GetLabels(a, a.Spec.Prometheus.ServiceMonitor.Labels) + rLabels := constants.GetLabels(a, a.Spec.ServiceMonitor.Labels) smSpec := monitorv1.ServiceMonitorSpec{ Endpoints: rEndpoints, diff --git a/docs/observability/README.md b/docs/observability/README.md index cc76f896..1a0b7d71 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -2,162 +2,305 @@ Oracle Database Operator for Kubernetes (`OraOperator`) includes the Observability controller for Oracle Databases and adds the `DatabaseObserver` CRD, which enables users to observe -Oracle Databases by scraping database metrics using SQL queries and observe logs in the Database _alert.log_. The controller -automates the deployment and maintenance of the metrics exporter container image, -metrics exporter service and Prometheus servicemonitor. +Oracle Databases by scraping database metrics using SQL queries and observe logs in the Database _alert.log_. +The controller automates the deployment and maintenance of the [database observability exporter](https://github.com/oracle/oracle-db-appdev-monitoring), +metrics exporter service and Prometheus ServiceMonitor. The following sections explains the configuration and functionality of the controller. * [Prerequisites](#prerequisites) * [The DatabaseObserver Custom Resource Definition](#the-databaseobserver-custom-resource) - * [Configuration Options](#configuration-options) - * [Resources Managed by the Controller](#resources-managed-by-the-controller) + * [Configuring Database Credentials](#configuration-fields-related-to-managing-database-credentials) + * [Configuring Cloud Provider Vaults for Database credentials](#configuration-fields-related-to-vault-usage) + * [Configuring the Managed Resources](#configuration-fields-related-to-the-deployment-pod-service-and-servicemonitor-resources) + * [Configuring Export of Database Metrics](#configuration-fields-related-to-metrics-export) + * [Configuring Export of Database Logs](#configuration-fields-related-to-logs-export) + * [Configuring the Exporter Config File](#configuration-fields-related-to-the-exporter-config-file) + * [DatabaseObserver Operations](#databaseobserver-operations) * [Create](#create-resource) * [List](#list-resource) * [Get Status](#get-detailed-status) * [Update](#patch-resource) * [Delete](#delete-resource) -* [Configuration Options for Scraping Metrics](#scraping-metrics) + +* [Connecting to the Database](#connecting-to-the-database) + * [Default DB Configuration](#default-database-configuration) + * [Multiple DB Configuration](#multiple-database-configuration) + +* [Vault Configuration](#database-authentication-with-vaults-in-the-cloud) + * [Using OCI Vault](#oci-vault-configuration) + * [Using Azure Vault](#azure-vault-configuration) + +* [Setting the Exporter Config File](#defining-an-exporter-config-file) + +* [Scraping Metrics](#scraping-metrics) * [Custom Metrics Config](#custom-metrics-config) * [Prometheus Release](#prometheus-release) -* [Configuration Options for Scraping Logs](#scraping-logs) + +* [Scraping Logs](#scraping-logs) * [Custom Log Location with PersistentVolumes](#custom-log-location-with-persistentvolumes) * [Example Working with Sidecars and Promtail](#working-with-sidecars-to-deploy-promtail) - * [Promtail Config Example](#Promtail-Config-Example) -* [Other Configuration Options](#other-configuration-options) - * [Labels](#labels) - * [Custom Exporter Image or Version](#custom-exporter-image-or-version) + + +* [Customizing Resources](#customizing-resources-and-available-configuration-options) + * [Environment Variables and Default Values](#environment-variables-and-default-values) + * [Managing Labels](#managing-labels) + * [Custom Exporter Image or Version](#custom-environment-variables-arguments-and-commands) + * [Security Contexts](#security-contexts) + * [Custom Service Ports](#custom-service-ports) + * [Custom ServiceMonitor](#custom-servicemonitor-endpoints) + * [Mandatory Roles and Privileges](#mandatory-roles-and-privileges-requirements-for-observability-controller) + * [Debugging and troubleshooting](#debugging-and-troubleshooting) * [Known Issues](#known-issues) +* [Resources](#resources) ## Prerequisites The `DatabaseObserver` custom resource has the following prerequisites: 1. Installation of Prometheus `servicemonitor` custom resource definition (CRD) on the cluster. -- The Observability controller creates multiple Kubernetes resources that include - a Prometheus `servicemonitor`. For the controller - to create ServiceMonitors, the ServiceMonitor custom resource must exist. For example, to install - Prometheus CRDs using the [Kube Prometheus Stack helm chart](https://prometheus-community.github.io/helm-charts/), run the following helm commands: - ```bash - helm repo add prometheus https://prometheus-community.github.io/helm-charts - helm repo update - helm upgrade --install prometheus prometheus/kube-prometheus-stack -n prometheus --create-namespace - ``` + - The Observability controller creates multiple Kubernetes resources that include + a Prometheus `servicemonitor`. For the controller + to create ServiceMonitors, the ServiceMonitor custom resource must exist. For __example__, to install + Prometheus CRDs using the [Kube Prometheus Stack helm chart](https://prometheus-community.github.io/helm-charts/), run the following helm commands: + ```bash + helm repo add prometheus https://prometheus-community.github.io/helm-charts + helm repo update + helm upgrade --install prometheus prometheus/kube-prometheus-stack -n prometheus --create-namespace + ``` + - You can check if the ServiceMonitor API exists in your cluster by running the following command: + ```bash + kubectl api-resources | grep smon + ``` 2. A pre-existing Oracle Database and the proper database grants and privileges. - -- The controller exports metrics through SQL queries that the user can control - and specify through a _toml_ file. The necessary access privileges to the tables used in the queries - are not provided and applied automatically. + - The controller exports metrics through SQL queries that the user can control + and specify through _toml_ files. The necessary access privileges to the tables used in the queries + are not provided and applied automatically. ## The DatabaseObserver Custom Resource -The Oracle Database Operator (__v2.0.0__ or later) includes the Oracle Database Observability controller, which automates +The Oracle Database Operator (__v1.0.0__ or later) includes the Oracle Database Observability controller, which automates the deployment and setting up of the Oracle Database exporter and the related resources to make Oracle Databases observable. +The Observability Controller introduces the `databaseobserver` APIs. + +To list the available APIs included in the +Database Operator, you can run the following command: + +```bash +kubectl api-resources | grep oracle +``` + +Learn about the different and configurable fields available in this release of the DatabaseObserver APIs in the following sections. + +> In this release, the controller deploys the Database Observability Exporter ([v2.0.2](https://github.com/oracle/oracle-db-appdev-monitoring/releases/tag/2.0.2)). + + + +### Configuration Fields Related to Managing Database Credentials +The following fields below are available for configuring the exporter to successfully connect to your database(s). + +| Attribute | Type | Required? | Example | +|---------------------------------------------------|--------|:------------|----------------------| +| `spec.database.dbUser.key` | string | No | _username_ | +| `spec.database.dbPassword.key` | string | No | _password_ | +| `spec.database.dbConnectionString.key` | string | No | _connection_ | +| `spec.database.dbUser.secret` | string | Yes | _db-secret_ | +| `spec.database.dbPassword.secret` | string | Yes | _db-secret_ | +| `spec.database.dbConnectionString.secret` | string | Yes | _db-secret_ | +| `spec.database.dbUser.envName` | string | No | _DB_USERNAME_ | +| `spec.database.dbPassword.envName` | string | No | _DB_PASSWORD_ | +| `spec.database.dbConnectionString.envName` | string | No | _DB_CONN_STRING_ | +| `spec.databases..dbUser.key` | string | No | _username_ | +| `spec.databases..dbPassword.key` | string | No | _password_ | +| `spec.databases..dbConnectionString.key` | string | No | _connection_ | +| `spec.databases..dbUser.secret` | string | Yes | _db02-secret_ | +| `spec.databases..dbPassword.secret` | string | Yes | _db02-secret_ | +| `spec.databases..dbConnectionString.secret` | string | Yes | _db02-secret_ | +| `spec.databases..dbUser.envName` | string | No | _DB2_USERNAME_ | +| `spec.databases..dbPassword.envName` | string | No | _DB2_PASSWORD_ | +| `spec.databases..dbConnectionString.envName` | string | No | _DB2_CONN_STRING_ | +| `spec.wallet.secret` | string | Conditional | _combined-wallet_ | +| `spec.wallet.mountPath` | string | No | _"/wallet/combined"_ | +| `spec.wallet.additional[].name` | string | Conditional | _db02_ | +| `spec.wallet.additional[].secret` | string | Conditional | _db02-wallet_ | +| `spec.wallet.additional[].mountPath` | string | Conditional | _"/wallet/db02"_ | + +These fields enable you to define connection details for a single or multiple databases. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). + +1. About `spec.database` - For configuring the database username, password and connection string. +The environment variables set by the controller can be customized through the `envName` field. +Both `envName` and `key` fields are optional. + + +2. About `spec.databases` - For configuring multiple database credentials. The keys are used as a prefix for environment +variables, for example, a key of `MYDB` will produce environment variables `MYDB_USERNAME` and `MYDB_PASSWORD`. + + +3. About `spec.wallet` - You can configure the wallet with which to connect to the Oracle database, if applicable. The field `mountPath` +allows you to control where the wallet is to be mounted. Meanwhile, additional wallets can be mounted in the array `additional`, used for multi +database configuration. + +To learn more about configuring the database connection, proceed to the section on [Connecting to the Database](#connecting-to-the-database). + +### Configuration Fields Related to Vault Usage +The following fields below are available for configuring the exporter to use the vault when connecting to your database(s). + +| Attribute | Type | Required? | Example | +|-------------------------------------------|--------|:------------|-----------------------------------------| +| `spec.database.oci.vaultID` | string | Conditional | _ocid1.vault.oc1.._ | +| `spec.database.oci.vaultPasswordSecret` | string | Conditional | _sample_secret_ | +| `spec.database.azure.vaultID` | string | Conditional | - | +| `spec.database.azure.vaultUsernameSecret` | string | Conditional | _sample_usn_secret | +| `spec.database.azure.vaultPasswordSecret` | string | Conditional | _sample_pwd_secret_ | +| `spec.ociConfig.configMap.key` | string | No | _config_ | +| `spec.ociConfig.configMap.name` | string | Conditional | _oci-config-file_ | +| `spec.ociConfig.privateKey.key` | string | No | _private.pem_ | +| `spec.ociConfig.privateKey.secret` | string | Conditional | _oci-privatekey_ | +| `spec.ociConfig.mountPath` | string | No | _"/.oci"_ | +| `spec.azureConfig.configMap.name` | string | Conditional | _azure-configmap_ | + + +These fields enable you to define vault details for OCI or Azure. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). +1. About `spec.database.oci` - for configuring the OCI Vault details for the default database. + +> Note: For multiple database configuration with Vault integration, use the exporter config file. See instructions on the [Exporter Config File](#defining-an-exporter-config-file). + +2. About `spec.database.azure.` - for configuring the Azure Vault details for the default database. + -In the example YAML file found in -[`./config/samples/observability/v4/databaseobserver.yaml`](../../config/samples/observability/v4/databaseobserver.yaml), -the databaseObserver custom resource provides the following configurable properties: - -| Attribute | Type | Default | Required? | Example | -|--------------------------------------------------------|--------|---------------------------------------------------------------------|:------------|-----------------------------------------------------------------------| -| `spec.database.dbUser.key` | string | user | Optional | _username_ | -| `spec.database.dbUser.secret` | string | - | Yes | _db-secret_ | -| `spec.database.dbPassword.key` | string | password | Optional | _admin-password_ | -| `spec.database.dbPassword.secret` | string | - | Conditional | _db-secret_ | -| `spec.database.dbPassword.vaultOCID` | string | - | Conditional | _ocid1.vault.oc1..._ | -| `spec.database.dbPassword.vaultSecretName` | string | - | Conditional | _db-vault_ | -| `spec.database.dbWallet.secret` | string | - | Conditional | _devsec-oradevdb-wallet_ | -| `spec.database.dbConnectionString.key` | string | connection | Optional | _connection_ | -| `spec.database.dbConnectionString.secret` | string | - | Yes | _db-secretg_ | -| `spec.sidecars` | array | - | Optional | - | -| `spec.sidecarVolumes` | array | - | Optional | - | -| `spec.exporter.deployment.securityContext` | object | | Optional | _ | -| `spec.exporter.deployment.env` | map | - | Optional | _DB_ROLE: "SYSDBA"_ | -| `spec.exporter.deployment.image` | string | container-registry.oracle.com/database/observability-exporter:1.5.1 | Optional | _container-registry.oracle.com/database/observability-exporter:1.3.0_ | -| `spec.exporter.deployment.args` | array | - | Optional | _[ "--log.level=info" ]_ | -| `spec.exporter.deployment.commands` | array | - | Optional | _[ "/oracledb_exporter" ]_ | -| `spec.exporter.deployment.labels` | map | - | Optional | _environment: dev_ | -| `spec.exporter.deployment.podTemplate.labels` | map | - | Optional | _environment: dev_ | -| `spec.exporter.deployment.podTemplate.securityContext` | object | - | Optional | _ | -| `spec.exporter.service.ports` | array | - | Optional | - | -| `spec.exporter.service.labels` | map | - | Optional | _environment: dev_ | | -| `spec.configuration.configMap.key` | string | config.toml | Optional | _config.toml_ | -| `spec.configuration.configMap.name` | string | - | Optional | _devcm-oradevdb-config_ | -| `spec.prometheus.serviceMonitor.labels` | map | - | Yes | _release: prometheus_ | -| `spec.prometheus.serviceMonitor.namespaceSelector` | - | - | Yes | - | -| `spec.prometheus.serviceMonitor.endpoints` | array | - | Optional | - | -| `spec.log.destination` | string | alert.log | Optional | _alert.log_ | -| `spec.log.filename` | string | /log | Optional | _/log_ | -| `spec.log.disable` | bool | - | Optional | true | -| `spec.log.volume.persistentVolumeClaim.claimName` | string | - | Optional | _my-pvc_ | -| `spec.replicas` | number | 1 | Optional | _1_ | -| `spec.inheritLabels` | array | - | Optional | _- environment: dev_
- app.kubernetes.io/name: observer | -| `spec.ociConfig.configMapName` | string | - | Conditional | _oci-cred_ | -| `spec.ociConfig.secretName` | string | - | Conditional | _oci-privatekey_ | - - -### Configuration Options -The `databaseObserver` Custom resource has the following fields for all configurations that are required: -* `spec.database.dbUser.secret` - Secret containing the database username. The corresponding key can be any value but must match the key in the secret provided. -* `spec.database.dbPassword.secret` - Secret containing the database password (if `vault` is NOT used). The corresponding key field can be any value, but must match the key in the Secret provided -* `spec.database.dbConnectionString.secret` - Secret containing the database connection string. The corresponding key field can be any value but must match the key in the Secret provided -* `spec.prometheus.serviceMonitor.labels` - Custom labels to add to the service monitors labels. A label is required for your serviceMonitor to be discovered. This label must match what is set in the serviceMonitorSelector of your Prometheus configuration - -If a database wallet is required to connect, then the following field containing the wallet secret is required: -* `spec.database.dbWallet.secret` - Secret containing the database wallet. The filenames inside the wallet must be used as keys - -If vault is used to store the database password instead, then the following fields are required: -* `spec.database.dbPassword.vaultOCID` - OCID of the vault used -* `spec.database.dbPassword.vaultSecretName` - Name of the secret inside the desired vault -* `spec.ociConfig.configMapName` - Holds the rest of the information of the OCI API signing key. The following keys must be used: `fingerprint`, `region`, `tenancy` and `user` -* `spec.ociConfig.secretName` - Holds the private key of the OCI API signing key. The key to the file containing the user private key must be: `privatekey` - -The `databaseObserver` Resource provides the remaining multiple fields that are optional: -* `spec.prometheus.serviceMonitor.endpoints` - ServiceMonitor endpoints -* `spec.prometheus.serviceMonitor.namespaceSelector` - ServiceMonitor namespace selector -* `spec.sidecars` - List of containers to run as a sidecar container with the observability exporter container image -* `spec.sidecarVolumes` - Volumes of any sidecar containers -* `spec.log.disable` - Disables Log volume creation -* `spec.log.filename` - Custom filename for the log file -* `spec.log.destination` - Custom destination for the log volume -* `spec.log.volume.persistentVolumeClaim.claimName` - A volume in which to place the log to be shared by the containers. If not specified, an EmptyDir is used by default. -* `spec.configuration.configMap.key` - Configuration filename inside the container and the configmap -* `spec.configuration.configMap.name` - Name of the `configMap` that holds the custom metrics configuration -* `spec.replicas` - Number of replicas to deploy -* `spec.exporter.service.ports` - Port number for the generated service to use -* `spec.exporter.service.labels` - Custom labels to add to service labels -* `spec.exporter.deployment.image` - Image version of observability exporter to use -* `spec.exporter.deployment.env` - Custom environment variables for the observability exporter -* `spec.exporter.deployment.labels` - Custom labels to add to deployment labels -* `spec.exporter.deployment.podTemplate.labels` - Custom labels to add to pod labels -* `spec.exporter.deployment.podTemplate.securityContext` - Configures pod securityContext -* `spec.exporter.deployment.args` - Additional arguments to provide the observability-exporter -* `spec.exporter.deployment.commands` - Commands to supply to the observability-exporter -* `spec.exporter.deployment.securityContext` - Configures container securityContext -* `spec.inheritLabels` - Keys of inherited labels from the databaseObserver resource. These labels are applied to generated resources. - -### Resources Managed by the Controller +3. About `spec.ociConfig` - for configuring the authentication of requests made to the Oracle Cloud done by the exporter. The configMap should contain +the actual _config_ file use by the OCI CLI (usually found in ~/.oci/config). See the official documentation on [configuring the OCI CLI (external link)](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliconfigure.htm#Configuring_the_CLI). +> Note: The OCI profile used is [DEFAULT]. Under the profile DEFAULT, set `key_file=/.oci/.pem`. + +4. About `spec.azureConfig` - for configuring the authentication of requests made to the Azure Cloud done by the exporter. +The configMap should contain the keys `tenantId`, `clientId` and `clientSecret`, which are used to create the related environment variables. + +To learn more about configuring an integration with a vault for database authentication, proceed to the section on [Authenticating with Vaults](#database-authentication-with-vaults-in-the-cloud). + +### Configuration Fields Related to the Deployment, Pod, Service and ServiceMonitor Resources When you create a `DatabaseObserver` resource, the controller creates and manages the following resources: +1. Deployment +2. Service +3. Prometheus ServiceMonitor +The following fields below are available for configuring the controller-managed resources for a custom deployment, pod, service and servicemonitor. + +| Attribute | Type | Required? | Example | +|-----------------------------------------|--------|:----------|-----------------------------------------------------------------------| +| `spec.deployment.securityContext` | object | No | - | +| `spec.deployment.podSecurityContext` | object | No | - | +| `spec.deployment.env` | map | No | _DB_ROLE: "SYSDBA"_ | +| `spec.deployment.image` | string | No | _container-registry.oracle.com/database/observability-exporter:1.3.0_ | +| `spec.deployment.args` | array | No | _[ "--log.level=info" ]_ | +| `spec.deployment.commands` | array | No | _[ "/oracledb_exporter" ]_ | +| `spec.deployment.labels` | map | No | _environment: dev_ | +| `spec.deployment.podTemplate.labels` | map | No | _environment: dev_ | +| `spec.service.ports` | array | No | - | +| `spec.service.labels` | map | No | _environment: dev_ | +| `spec.serviceMonitor.labels` | map | Yes | _release: prometheus_ | +| `spec.serviceMonitor.endpoints` | array | No | - | +| `spec.serviceMonitor.namespaceSelector` | - | No | - | +| `spec.replicas` | number | No | _1_ | +| `spec.inheritLabels` | array | No | _- environment: dev_
- app.kubernetes.io/name: observer | + +These fields enable you to define deployment, service and serviceMonitor details. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). + +1. About `spec.deployment` - for configuring deployment details such as custom environment variables through `env` and arguments through `args`. +The container image can also be replaced with a later or older version of the exporter using the `image` field. For security related configurations, +the `securityContext` and `podSecurityContext` are available. + +2. About `spec.service` - for configuring a customized service resource. + + +3. About `spec.serviceMonitor` - for configuring the serviceMonitor resource. + +> Note: It is important and required to set the prometheus label inside `serviceMonitor.labels`. Make sure the label set is included in the serviceMonitorSelector field of your Prometheus CR. + + +5. About `spec.replicas` - for configuring the number of replicas to deploy + + +6. About `spec.inheritLabels` - for configuring all resources created and managed to inherit the labels from databaseobserver CR. + + +To learn more about configuring the managed resources like the Deployment, Service and more, proceed to the section on [Customizing Resources and Available Configuration Options](#customizing-resources-and-available-configuration-options) + +### Configuration Fields Related to Metrics Export +The following fields below are available for configuring the metrics exported from the database. + +| Attribute | Type | Required? | Example | +|---------------------------------|--------|:------------|-------------------------| +| `spec.metrics.configMap[].key` | string | No | _config.toml_ || | | | | +| `spec.metrics.configMap[].name` | string | Conditional | _custom-metrics-config_ | + +These fields enable you to define configMap sources for metrics. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). +1. About `metrics.configMap[]` - for configuring an array of configs which contain the TOML files. This creates a volume with multiple source files mounted in the same directory. + +To learn more about configuring the metrics export, proceed to the section on [Defining an Exporter Config File](#defining-an-exporter-config-file). + +### Configuration Fields Related to Logs Export +The following fields below are available for configuring how the alert.log is exported from the database. + +| Attribute | Type | Required? | Example | +|--------------------------------------------------|--------|:------------|--------------| +| `spc.log.destination` | string | No | _alert.log_ | +| `spc.log.filename` | string | No | _/log_ | +| `spc.log.disable` | bool | No | true | +| `spc.log.volume.name` | string | No | _log-volume_ | | +| `spc.log.volume.persistentVolumeClaim.claimName` | string | Conditional | _my-pvc_ | +| `spc.sidecar.containers[]` | array | Conditional | - | +| `spc.sidecar.volumes[]` | array | Conditional | - | + +These fields enable you to define log details and sidecar resources. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). +1. About `spec.sidecar.containers` - for configuring an array of containers as a sidecar to the observability +exporter container, such as promtail. The field `sidecar.containers` enable you to list containers as you would normally for deployments. + + +2. About `spec.sidecar.volumes` - for configuring extra volumes related to your sidecar containers. + + +3. About `spec.log.disable` - for disabling the log volume creation + + +4. About `spec.log.filename` - for specifying a custom filename for the log file + + +5. About `spec.log.destination` - for configuring a custom destination for the log volume. + -1. __Deployment__ - The deployment will have the same name as the `databaseObserver` resource - - Deploys a container named `observability-exporter` - - The default container image version of the `container-registry.oracle.com/database/observability-exporter` supported is __[v1.5.1](https://github.com/oracle/oracle-db-appdev-monitoring/releases/tag/1.5.1)__ +6. About `spec.log.volume` - for configuring the log volume in which the logs are extracted into by the exporter and read from. +If a persistentVolumeClaim is not provided, an emptyDir is created instead. Meanwhile, the field `log.volume.name` can be used to define the name of the volume +to reference. -2. __Service__ - The service will have the same name as the databaseObserver - - The service is of type `ClusterIP` +To learn more about configuring the log export, proceed to the section on [Scraping Logs](#scraping-logs). -3. __Prometheus ServiceMonitor__ - The serviceMonitor will have the same name as the `databaseObserver` +### Configuration Fields Related to the Exporter Config File +The following fields below are available for configuring the exporter through a config-file. + +| Attribute | Type | Required? | Example | +|-------------------------------------|--------|:------------|-------------------| +| `spec.exporterConfig.configMapName` | string | Conditional | _exporter-config_ | +| `spec.exporterConfig.mountPath` | string | No | _/config_ | + +These fields enable you to define the exporter config-file. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). + +1. About `spec.exporterConfig` - for configuring the exporter with a config file containing database, log and metrics details. You can use +the `mountPath` field to define a custom location for the config file. + +> Note: The CONFIG_FILE environment variable or the --config.file args must be set to the desired location of the config file. + +To learn more about configuring the config-file for the exporter, proceed to the section on [Defining an Exporter Config File](#defining-an-exporter-config-file). ## DatabaseObserver Operations ### Create Resource Follow the steps below to create a new `databaseObserver` resource object. -1. To begin, creating a `databaseObserver` requires you to create and provide Kubernetes Secrets to provide connection details: +1. To begin, creating a `databaseObserver` requires you to create and to provide Kubernetes Secrets containing your database connection details. Replace the values and +create a single secret by running the following command: ```bash kubectl create secret generic db-secret \ --from-literal=username='username' \ @@ -165,14 +308,15 @@ kubectl create secret generic db-secret \ --from-literal=connection='dbsample_tp' ``` -2. (Conditional) Create a Kubernetes Secret for the wallet (if a wallet is required to connect to the database). +2. (Conditional) Create a Kubernetes Secret for the wallet (if a wallet is required to connect to the database). -You can create this Secret by using a command similar to the example that follows. -If you are connecting to an Autunomous Database, and the operator is used to manage the Oracle Autonomous Database, then a client wallet can also be downloaded as a Secret through `kubectl` commands. See the ADB README section on [Download Wallets](../../docs/adb/README.md#download-wallets). +If you are connecting to an Autonomous Database, and the operator is used to manage the Oracle Autonomous Database, +then a client wallet can also be downloaded as a Secret through `kubectl` commands. +See the ADB README section on [Download Wallets](../../docs/adb/README.md#download-wallets). You can also choose to create the wallet secret from a local directory containing the wallet files: ```bash -kubectl create secret generic db-wallet --from-file=wallet_dir +kubectl create secret generic db-wallet --from-file= ``` 3. Finally, update the `databaseObserver` manifest with the resources you have created. You can use the example _minimal_ manifest @@ -199,15 +343,15 @@ spec: key: "connection" secret: db-secret - dbWallet: - secret: db-wallet + wallet: + secret: db-wallet - prometheus: - serviceMonitor: - labels: - release: prometheus + serviceMonitor: + labels: + release: prometheus ``` +To create the resource: ```bash kubectl apply -f databaseobserver.yaml ``` @@ -215,19 +359,19 @@ spec: ### List Resource To list the Observability custom resources, use the following command as an example: ```bash -kubectl get databaseobserver -A +kubectl get dbobserver -A ``` ### Get Detailed Status -To obtain a quick status, use the following command as an example: +To obtain a quick status, use the following command as an example: > Note: The databaseobserver custom resource is named `obs-sample` in the next following sections. > We will use this name as an example. ```sh $ kubectl get databaseobserver obs-sample -NAME EXPORTERCONFIG STATUS VERSION -obs-sample DEFAULT READY 1.6.0 +NAME METRICSCONFIG STATUS VERSION +obs-sample DEFAULT READY 2.0.2 ``` @@ -238,13 +382,16 @@ kubectl describe databaseobserver obs-sample ``` This command displays details of the current state of your `databaseObserver` resource object. A successful -deployment of the `databaseObserver` resource object should display `READY` as the status, and all conditions should display with a `True` value for every ConditionType. +deployment of the `databaseObserver` resource object should display `READY` as the status, and all conditions should +display with a `True` value for every ConditionType. ### Patch Resource -The Observability controller currently supports updates for most of the fields in the manifest. The following is an example of patching the `databaseObserver` resource: +The Observability controller currently supports updates for most of the fields in the manifest. The following is an example +of patching the `databaseObserver` resource: + ```bash -kubectl --type=merge -p '{"spec":{"exporter":{"image":"container-registry.oracle.com/database/observability-exporter:1.6.0"}}}' patch databaseobserver obs-sample +kubectl --type=merge -p '{"spec":{"exporter":{"image":"container-registry.oracle.com/database/observability-exporter:2.0.1"}}}' patch databaseobserver obs-sample ``` ### Delete Resource @@ -255,16 +402,368 @@ To delete the `databaseObserver` custom resource and all related resources, use kubectl delete databaseobserver obs-sample ``` +## Connecting to the Database + + +### Default Database Configuration +To configure the observability exporter to export from a single Oracle Database, use the field `spec.database` +to define the details of the database. If the wallet is applicable, `spec.wallet` allows you to define a secret containing the wallet +and where the wallet is to be mounted as a volume. + +```yaml +spec: + database: + dbUser: + key: "username" + secret: db-secret + + dbPassword: + key: "password" + secret: db-secret + + dbConnectionString: + key: "connection" + secret: db-secret + + wallet: + secret: db-wallet + mountPath: /oracle/wallet + #... +``` +Alternatively, the default database can be defined and configured through the exporter config-file. In this case, the config-file must be defined, and the +credentials are set only through the exporter YAML file as kubernetes secrets. As an example, using the same secret with the same keys, you can define the relevant secrets +in the following YAML file: + +```yaml + +spec: + database: + dbUser: + secret: db-secret + dbPassword: + secret: db-secret + + wallet: + secret: db-wallet + mountPath: /oracle/wallet + + deployment: + env: + CONFIG_FILE: "/oracle/exporter/config.yaml" + + exporterConfig: + configMap: + name: config-file +``` + +In the config-file, we reference the environment variables DB_USERNAME and DB_PASSWORD which are set by default by the observability controller +when `spec.database.dbUser` and `spec.database.dbPassword` are defined. The TNS_ADMIN is also set through the config-file, and the +connection string provided through the config file. + +```yaml +# config.yaml + +# Environment variables of the form ${VAR_NAME} will be expanded. +databases: + default: + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + url: oracledb_tp + + ## Path to Oracle Database wallet, if using wallet + tnsAdmin: /oracle/wallet +``` +To create the configmap: +```bash +kubectl create cm config-file --from-file=config.yaml +``` + +### Multiple Database Configuration +To configure the observability exporter to export metrics and logs from multiple Oracle Databases, it is required to use an exporter config file, +configure the _databaseobserver_ YAML file with a combined wallet (if applicable) and use the +`spec.databases` field __instead__ of `spec.database`. The field `spec.databases` is a map with keys used for naming environment variables +and identifying groups of credentials. + +For example, to define two databases, in the _databaseobserver_ YAML file, you can have the following configuration: + +```yaml +spec: + databases: + db01: + dbUser: + secret: adb01-secret + dbPassword: + secret: adb01-secret + db02: + dbUser: + secret: adb02-secret + envName: "DB2_USN" + dbPassword: + secret: adb02-secret + envName: "DB2_PWD" + + wallet: + secret: combined + mountPath: "/example_dbwallet/combined" + additional: + - name: db01 + secret: db01-wallet + mountPath: "/example_dbwallet/db01" + - name: db02 + secret: db02-wallet + mountPath: "/example_dbwallet/db02" + + deployment: + env: + TNS_ADMIN: /example_dbwallet/combined + CONFIG_FILE: "/config/config.yaml" + + exporterConfig: + configMap: + name: config-file +``` +Each database is configured under `spec.databases` and multiple wallets are defined in the shared directory `/dbwallet` +as an example. See the next section on [configuring a combined wallet](#configuring-wallets-for-multiple-databases). + +In the configuration file _config-file_, db1 and db2 are configured with the credentials provided as environment variables through the +__databaseobserver__ YAML file as secrets. + +```yaml +# config.yaml +# Environment variables of the form ${VAR_NAME} will be expanded. +databases: + db1: + username: ${db01_USERNAME} + password: ${db01_PASSWORD} + url: db1_tp + + db2: + username: ${DB2_USN} + password: ${DB2_PWD} + url: db2_tp +``` + +Run the following command, to create the configMap: + +```bash +kubectl create cm config-file --from-file=config.yaml +``` + +To learn more about the config file, you can consult the [official documentations of the exporter](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#standalone-binary). + +#### Configuring Wallets for Multiple Databases +In configuring multiple databases where each connection requires a database wallet, a combined wallet is required and can be configured through the databaseobserver YAML file. To +create a combined wallet: + +1. Copy TNS aliases from every tnsnames.ora and combined them into one tnsnames.ora file. + + +2. Set the wallet directory for the aliases inside security, with the following snippet pointing to each database wallet location: `(MY_WALLET_DIRECTORY=/example_dbwallet/db01)`, for example: + +``` +...)(security=(MY_WALLET_DIRECTORY=)(ssl_server_dn_match=...))) +``` + + +3. Place the combined tnsnames.ora file and one of the sqlnet.ora inside a combined directory. + + +4. Take wallet files (.sso, .p12, .pem) and place them in separate directories. + + +The resulting wallet directory structure should look like the following, where wallet files for each database are in separate directories: +```bash +# +example_dbwallet +├── combined +│ ├── sqlnet.ora +│ └── tnsnames.ora # Combined tnsnames.ora +├── db01 +│ ├── cwallet.sso +│ ├── ewallet.p12 +│ └── ewallet.pem +└── db02 + ├── cwallet.sso + ├── ewallet.p12 + └── ewallet.pem +``` +Now returning to the YAML file, for this example: + +5. Set the combined _tnsnames.ora_ is set under `spec.wallet.secret` and the +specific database wallet files (.sso, .p12, .pem) are set under `.spec.wallet.additional`. + +6. Finally, set the TNS_ADMIN to the location of the tnsnames.ora. + +> Note: When setting the name under `spec.wallets.additional[].name`, provide a unique name other than `creds`, as this is the default volume name. + +To learn more about this requirement, you can consult the [official documentation of the exporter](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#configuring-connections-for-multiple-databases-using-oracle-database-wallets). + + +## Database Authentication with Vaults in the Cloud +Cloud vault resources can be used for storing sensitive database credentials. In this release, the following vaults are supported: +- OCI Vault +- Azure Vault + +### OCI Vault Configuration +The OCI Vault can be used to store the database credentials. This release supports storing the Oracle Database password in the OCI Vault. + +Now when configuring the vault, the following are required for you to provide: +- Vault details: + - The string OCID of the Vault used + - The string name of the OCI Vault Secret containing the password +- Authentication details (if applicable): + - Kubernetes Secret containing the [OCI CLI Config file](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliconfigure.htm) + - Kubernetes Secret containing the user's OCI CLI Private Key + +The observability exporter needs to authenticate requests to retrieve the database password from the OCI Vault. When configuring API Key authentication, +the OCI CLI config file and the __DEFAULT profile is used__. +> Note: The exporter uses the DEFAULT profile. + +For example, the OCI CLI config file could appear as follows: +```bash +[DEFAULT] +user= +fingerprint= +key_file=/.oci/private.pem +tenancy= +region= +``` + +> Note: Ensure the key_file is in `/.oci` and the private key file matches. + +You can create the configmap with the following command: +```bash +kubectl create cm oci-cred --from-file=config +``` + +You can create the secret with the following command: +```bash +kubectl create secret generic oci-privatekey --from-file=private.pem +``` + +Finally, to configure the exporter to use OCI Vault for retrieving the password, you can configure the following fields in the YAML file: +```yaml +spec: + database: + oci: + vaultID: ocid1.vault.oc1.. + vaultPasswordSecret: sample_secret + + # ... + + ociConfig: + configMap: + name: oci-cred + privateKey: + name: oci-privatekey +``` + +### Azure Vault Configuration +The Azure Vault can be used to store the database credentials. This release supports storing the database username and password in the Azure Vault. + +To configure the exporter to use Azure Vault for retrieving database credentials, you can configure the following fields: +```yaml +spec: + database: + azure: + vaultID: "..." + vaultUsernameSecret: sample_usn_secret + vaultPasswordSecret: sample_pwd_secret + + # ... + + azureConfig: + configMap: + name: azure-cred +``` + +The `spec.azureConfig` field allows you to provide the environment variables through a configMap: +- AZURE_TENANT_ID +- AZURE_CLIENT_ID +- AZURE_CLIENT_SECRET + +You can then create the following configmap referenced in the YAML file above with your desired values: +```bash +kubectl create configmap azure-cred \ +--from-literal=tenantId= \ +--from-literal=clientId= \ +--from-literal=clientSecret= +``` + +## Defining an Exporter Config File +A YAML configuration file for the exporter can be provided by setting the `--config.file=` +command-line argument. It is recommended to use the configuration file from the 2.0.0 release of the exporter +and onwards. + +To configure the exporter config file, set the path to the YAML file +under `spec.deployment.args` from which to read the config from. The configMap +containing the exporter settings and configurations is set through `spec.exporterConfig`. +Specifying `spec.exporterConfig.mountPath` allows you to control the location where +the volume will be mounted. +```yaml +spec: + # ... + + deployment: + args: "--config.file=/config/exporter-config.yaml" + + exporterConfig: + mountPath: "/config" + configMap: + key: exporter-config.yaml + name: exporter-config-file +``` + +Create the _exporter-config-file_ configMap. For example, using the following example configuration. +```yaml +# exporter-config.yaml + +# Example Oracle Database Metrics Exporter Configuration file. +# Environment variables of the form ${VAR_NAME} will be expanded. +databases: + default: + ## Database username + username: ${DB_USERNAME} + ## Database password + password: ${DB_PASSWORD} + ## Database connection url + url: localhost:1521/freepdb1 + + ## Metrics query timeout for this database, in seconds + queryTimeout: 5 + + ### Connection pooling settings for the go-sql connection pool + ## Max open connections for this database using go-sql connection pool + maxOpenConns: 10 + ## Max idle connections for this database using go-sql connection pool + maxIdleConns: 10 +``` +For more information on the configuration fields, see the following [examples](https://github.com/oracle/oracle-db-appdev-monitoring/blob/main/example-config.yaml). You can then create the configMap that is referenced in the _databaseobserver_ YAML file with the following command: +```bash +kubectl create cm exporter-config-file --from-file=exporter-config.yaml +``` + +Note that in the above configuration file, the environment variables `DB_USERNAME` and `DB_PASSWORD` will be expanded +by the exporter. These environment variables +are one of the default environment variables set by the DatabaseObserver controller. In the DatabaseObserver YAML file, you +can set the following details: + +```yaml +spec: + database: + dbUser: + secret: db-secret + dbPassword: + secret: db-secret + + # ... +``` + ## Scraping Metrics -The `databaseObserve`r resource deploys the Observability exporter container. This container connects to an Oracle Database and +The `databaseObserver` resource deploys the Observability exporter container. This container connects to an Oracle Database and scrapes metrics using SQL queries. By default, the exporter provides standard metrics, which are listed in the [official GitHub page of the Observability Exporter](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#standard-metrics). To define custom metrics in Oracle Database for scraping, a TOML file that lists your custom queries and properties is required. -The file will have metric sections with the following parts: -- a context -- a request, which contains the SQL query -- a map between the field(s) in the request and comment(s) - For example, the code snippet that follows shows how you can define custom metrics: ```toml [[metric]] @@ -284,35 +783,46 @@ oracledb_test_value_2 2 You can find more information in the [__Custom Metrics__](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#custom-metrics) section of the Official GitHub page. - - ### Custom Metrics Config -When configuring a `databaseObserver` resource, you can use the field `spec.configuration.configMap` to provide a -custom metrics file as a `configMap`. +When configuring a `databaseObserver` resource, you can use the field `spec.metrics.configMap[]` to provide one or more +custom metrics files as a `configMap`. You can create the `configMap` by running the following command: ```bash -kubectl create cm custom-metrics-cm --from-file=metrics.toml +kubectl create cm custom-metrics --from-file=metrics.toml ``` Finally, when creating or updating a `databaseObserver` resource, if we assume using the example above, you can set the fields in your YAML file as follows: ```yaml spec: - configuration: + metrics: + configMap: + - name: custom-metrics + key: "metrics.toml" +``` + +When configuring multiple configurations with different configmaps, you can do so by listing them under `spec.metrics.configMap`: +```yaml +spec: + metrics: configMap: - key: "metrics.toml" - name: "custom-metrics-cm" + - name: custom-metrics + key: "metrics.toml" + - name: txeventq-metrics + key: "txeventq.toml" ``` +> Note: This mounts a volume named `metrics-volume` and in one mounted directory located inside the container `/oracle/observability` will include all +the provided TOML files. + ### Prometheus Release -To enable your Prometheus configuration to find and include the `ServiceMonitor` created by the `databaseObserver` resource, the field `spec.prometheus.serviceMonitor.labels` is an __important__ and __required__ field. The label on the ServiceMonitor +To enable your Prometheus configuration to find and include the `ServiceMonitor` created by the `databaseObserver` resource, the field `spec.serviceMonitor.labels` is an __important__ and __required__ field. The label on the ServiceMonitor must match the `spec.serviceMonitorSelector` field in your Prometheus configuration. ```yaml - prometheus: - serviceMonitor: - labels: - release: stable + serviceMonitor: + labels: + release: prometheus ``` ## Scraping Logs @@ -326,8 +836,9 @@ In the following example, `Promtail` is used as a sidecar container that ships t To configure the `databaseObserver` resource with a sidecar, two fields can be used: ```yaml spec: - sidecars: [] - sidecarVolumes: [] + sidecar: + containers: [] + volumes: [] ``` You can find an example in the `samples` directory, which deploys a Promtail sidecar container as an example: @@ -356,31 +867,11 @@ If `spec.log.volume.persistentVolumeClaim.claimName` is not specified, then an ` ```yaml log: volume: + name: log-volume persistentVolumeClaim: claimName: "my-pvc" ``` -The security context defines privilege and access control settings for a pod container, If these privileges and access control settingrs need to be updated in the pod, then the same field is available on the `databaseObserver` spec. You can set this object under deployment: `spec.exporter.deployment.securityContext`. - -```yaml -spec: - exporter: - deployment: - runAsUser: 1000 -``` - -Configuring security context under the PodTemplate is also possible. You can set this object under: `spec.exporter.deployment.podTemplate.securityContext` - -```yaml -spec: - exporter: - deployment: - podTemplate: - securityContext: - supplementalGroups: [1000] -``` - - ### Working with Sidecars to deploy Promtail The fields `spec.sidecars` and `spec.sidecarVolumes` provide the ability to deploy container images as a sidecar container alongside the `observability-exporter` container. @@ -390,7 +881,8 @@ You can specify container images to deploy inside `spec.sidecars` as you would n For example, to deploy a Grafana Promtail image, you can specify the container and its details as an element to the array, `spec.sidecars`. ```yaml - sidecars: + sidecar: + containers: - name: promtail image: grafana/promtail args: @@ -398,19 +890,19 @@ For example, to deploy a Grafana Promtail image, you can specify the container a volumeMounts: - name: promtail-config-volume mountPath: /etc/promtail - - name: my-log-volume + - name: log-volume mountPath: /log ``` -> Important Note: Make sure the volumeMount name matches the actual name of the volumes referenced. In this case, `my-log-volume` is referenced in `spec.log.volume.name`. +> Important Note: The log volume is set by the controller with the name `log-volume` by default, unless set in `spec.log.volume.name`. -In the field `spec.sidecarVolumes`, you can specify and list the volumes you need in your sidecar containers. The field -`spec.sidecarVolumes` is an array of Volumes (`[]corev1.Volume`). +In the field `spec.sidecar.volumes`, you can specify and list the volumes you need in your sidecar containers. The field +`spec.sidecar.volumes` is an array of Volumes (`[]corev1.Volume`). For example, when deploying the Promtail container, you can specify in the field any volume that needs to be mounted in the sidecar container above. ```yaml - sidecarVolumes: + volumes: - name: promtail-config-volume configMap: name: promtail-config-file @@ -447,10 +939,73 @@ To create the `configmap`, you can run the following command: kubectl create cm promtail-config-file --from-file=config.yaml ``` +## Customizing Resources and Available Configuration Options + +### Environment Variables and Default Values +The following environment variables are set and provided by the controller by default: + +| Environment Variable | Related Field | Default | Details | +|----------------------------|-----------------------------------------|-------------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| `ORACLE_HOME` | - | /lib/oracle/21/client64/lib | Location of the Oracle Instant Client | +| `TNS_ADMIN` | - | /lib/oracle/21/client64/lib/network/admin | Location of your (unzipped) wallet | +| `DB_USERNAME` | database.dbUser | - | Database username, retrieved from a Kubernetes secret, not set or used for multi-databases | +| `DB_PASSWORD` | database.dbPassword | - | Database password, retrieved from a Kubernetes secret, not set or used for multi-databases | +| `DB_CONNECT_STRING` | database.dbConnectionString | - | Database connection string, retrieved from a Kubernetes secret, not set or used for multi-databases | +| `_USERNAME` | databases.<key>.dbUser | - | Database username, Environment Variable can be completely renamed through `dbUser.envName` | +| `_PASSWORD` | databases.<key>.dbPassword | - | Database password, Environment Variable can be completely renamed through `dbPassword.envName` | +| `_CONNECT_STRING` | database.<key>.dbConnectionString | - | Database connection string, Environment Variable can be completely renamed through `dbConnectionString.envName` | +| `OCI_VAULT_ID` | database.oci.vaultID | - | Vault OCID containing the OCI Vault Secret referenced | +| `OCI_VAULT_SECRET_NAME` | database.oci.vaultPasswordSecret | - | Vault Secret name containing the DB Password secret | +| `AZ_VAULT_ID` | database.azure.vaultID | - | Vault ID containing the Azure Vault Secret referenced | +| `AZ_VAULT_PASSWORD_SECRET` | database.azure.vaultPasswordSecret | - | Vault Secret name containing the DB Password secret | +| `AZ_VAULT_USERNAME_SECRET` | database.azure.vaultUsernameSecret | - | Vault Secret name containing the DB Username secret | +| `AZURE_TENANT_ID` | azureConfig.configMap.name | - | Your Azure cloud Tenant ID | +| `AZURE_CLIENT_ID` | azureConfig.configMap.name | - | Your Azure cloud client ID | +| `AZURE_CLIENT_SECRET` | azureConfig.configMap.name | - | Your Azure cloud client secret | +| `LOG_DESTINATION` | log.destination | /log/alert.log | Location of where alert.log will be placed | +| `CUSTOM_METRICS` | metrics.ConfigMap[] | - | List of paths where the TOML files are located | + +The following default values or behavior is set: + +| Usage | Related Field(s) | Default | Details | +|---------------------------------------------|----------------------------------------------|---------------------------------------------------------------------|---------| +| Database Username Key | `database.dbUser.key` | username | | +| Database Password Key | `database.dbPassword.key` | password | | +| Database Connection String Key | `database.dbConnectionString.key` | connection | | +| Wallet MountPath | `wallet.MountPath` | /lib/oracle/21/client64/lib/network/admin | | +| Env Var for Database Username | `databases..dbUser.envName` | <key>_USERNAME | | +| Env Var for Database Password | `databases..dbPassword.envName` | <key>_PASSWORD | | +| Env Var for Database Connection String | `databases..dbConnectionString.envName` | <key>_CONNECT_STRING | | +| Exporter Image | `deployment.image` | container-registry.oracle.com/database/observability-exporter:2.0.2 | | +| Volume name of OCI Config file mounted | `ociConfig.configMap.name` | oci-config-volume | | +| Volume name of Metrics Config files mounted | `metrics.configMap.name` | metrics-volume | | +| Volume name of Log volume mounted | `log.volume.name` | log-volume | | +| Volume name of Exporter Config mounted | `exporterConfig.configMap.name` | config-volume | | +| Volume name of Wallet | `wallet` | | | +| Log config filename | `log.filename` | alert.log | | +| Log config file location for mounting | `log.destination` | /log | | +| Metrics config files location for mounting | `metrics` | /oracle/observability | | +| Exporter config file location for mounting | `exporterConfig.mountPath` | /config | | +| OCI Config file location for mounting | `ociConfig.mountPath` | /.oci | | + +Overwriting environment variables can be managed by configuring the fields `spec.deployment.envs` or `spec.deployment.args` or through the [exporter config file](#defining-an-exporter-config-file): +```yaml +spec: + deployment: + args: + - "--config.file=/location" + env: + TNS_ADMIN: /path/to/new/location +``` +These variables and configuration values can be set explicitly, variables such as: +- DB_ROLE +- DATABASE_MAXIDLECONNS +- DATABASE_MAXOPENCONNS +- DATABASE_POOLINCREMENT +- DATABASE_POOLMAXCONNECTIONS +- DATABASE_POOLMINCONNECTIONS -## Other Configuration Options - -### Labels +### Managing Labels __About the Default Label__ - The resources created by the Observability Controller will automatically be labelled with: - `app`: `` @@ -481,78 +1036,92 @@ spec: Meanwhile, you can provide extra labels to the resources created by the `databaseObserver` controller, such as the Deployment, Pods, Service and ServiceMonitor. ```yaml spec: - exporter: - deployment: - labels: - podTemplate: - labels: - service: - labels: - prometheus: - serviceMonitor: + deployment: + labels: + podTemplate: labels: + service: + labels: + serviceMonitor: + labels: ``` ### Custom Exporter Image or Version -The field `spec.exporter.deployment.image` is provided to enable you to make use of a newer or older version of the [observability-exporter](https://github.com/oracle/oracle-db-appdev-monitoring) +The field `spec.deployment.image` is provided to enable you to make use of a newer or older version of the [observability-exporter](https://github.com/oracle/oracle-db-appdev-monitoring) container image. ```yaml spec: - exporter: - deployment: - image: "container-registry.oracle.com/database/observability-exporter:1.6.0" + deployment: + image: "container-registry.oracle.com/database/observability-exporter:2.0.1" ``` ### Custom Environment Variables, Arguments and Commands -The fields `spec.exporter.deployment.env`, `spec.exporter.deployment.args` and `spec.exporter.deployment.commands` are provided for adding custom environment variables, arguments (`args`) and commands to the containers. +The fields `spec.deployment.env`, `spec.deployment.args` and `spec.deployment.commands` are provided for adding custom environment variables, arguments (`args`) and commands to the containers. Any custom environment variable will overwrite environment variables set by the controller. ```yaml spec: - exporter: - deployment: - env: - DB_ROLE: "" - TNS_ADMIN: "" - args: - - "--log.level=info" - commands: - - "/oracledb_exporter" + deployment: + env: + DB_ROLE: "SYSDBA" + TNS_ADMIN: "/path/to/wallet" + args: + - "--log.level=info" + commands: + - "/oracledb_exporter" +``` + + +### Security Contexts +The security context defines privilege and access control settings for a pod container, If these privileges and access control setting need to be updated in the pod, then the same field is available on the `databaseObserver` spec. You can set this object under deployment: `spec.deployment.securityContext`. + +```yaml +spec: + deployment: + securityContext: + runAsUser: 1000 ``` +Configuring security context under the PodTemplate is also possible. You can set this object under: `spec.deployment.podTemplate.securityContext` + +```yaml +spec: + deployment: + podSecurityContext: + supplementalGroups: [1000] + +``` ### Custom Service Ports -The field `spec.exporter.service.ports` is provided to enable setting the ports of the service. If not set, then the following definition is set by default. +The field `spec.service.ports` is provided to enable setting the ports of the service. If not set, then the following definition is set by default. ```yaml spec: - exporter: - service: - ports: - - name: metrics - port: 9161 - targetPort: 9161 + service: + ports: + - name: metrics + port: 9161 + targetPort: 9161 ``` ### Custom ServiceMonitor Endpoints -The field `spec.prometheus.serviceMonitor.endpoints` is provided for providing custom endpoints for the ServiceMonitor resource created by the `databaseObserver`: +The field `spec.serviceMonitor.endpoints` is provided for providing custom endpoints for the ServiceMonitor resource created by the `databaseObserver`: ```yaml spec: - prometheus: - serviceMonitor: - endpoints: - - bearerTokenSecret: - key: '' - interval: 20s - port: metrics - relabelings: - - action: replace - sourceLabels: - - __meta_kubernetes_endpoints_label_app - targetLabel: instance + serviceMonitor: + endpoints: + - bearerTokenSecret: + key: '' + interval: 20s + port: metrics + relabelings: + - action: replace + sourceLabels: + - __meta_kubernetes_endpoints_label_app + targetLabel: instance ``` ## Mandatory roles and privileges requirements for Observability Controller @@ -578,6 +1147,8 @@ and gets and lists configmaps and secrets. | configmaps.apps | get list | | databaseobservers.observability.oracle.com/finalizers | update | + + ## Debugging and troubleshooting ### Show the details of the resource @@ -588,7 +1159,7 @@ kubectl describe databaseobserver/database-observer-sample ``` If any error occurs during the reconciliation loop, then the Operator either reports -the error using the resource's event stream, or it will show the error under conditions. +the error using the resource's event stream, or the resource's Conditions. ### Check the logs of the pod where the operator deploys Follow these steps to check the logs. @@ -605,5 +1176,24 @@ Follow these steps to check the logs. kubectl logs deployment.apps/oracle-database-operator-controller-manager -n oracle-database-operator-system ``` +## Known Issues + +### Using the OCI Vault (or Azure Vault) causes an error + +> The development team has identified an issue with v2.0.2 of the exporter when using the OCI vault to store only the username or only the password, leading to an error. +> Using the Azure Vault to store only the username or the password will likely produce the same error. + +__WORKAROUND:__
+As the OCI Vault feature is not functioning for v2 to v2.0.2 of the exporter, please use kubernetes secrets. +For Azure users, only retrieval of both username and password from the vault is supported. +Retrieving only one (username or password) of the credentials will lead to an error. + +__WORKAROUND AFTER V2.0.2__: +Once a new version of the exporter is released with the fix, set the field `deployment.image` to the new version of the exporter. + ## Resources +For further information on the Oracle Databases logs and metrics Exporter container image, +consult the official repository documentations: - [GitHub - Unified Observability for Oracle Database Project](https://github.com/oracle/oracle-db-appdev-monitoring) + + From 662b8f3c8344f7d296f02d97faf0db24bc7857b3 Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Mon, 11 Aug 2025 21:57:28 +0000 Subject: [PATCH 259/414] Update webhooks of restore and backup controllers --- .../v1alpha1/adbfamily_common_spec.go | 6 +- .../autonomouscontainerdatabase_webhook.go | 19 +++--- .../v1alpha1/autonomousdatabase_conversion.go | 8 +-- .../v1alpha1/autonomousdatabase_webhook.go | 62 ++++--------------- .../autonomousdatabasebackup_webhook.go | 47 +++++++------- .../autonomousdatabasebackup_webhook_test.go | 16 ++--- .../autonomousdatabaserestore_types.go | 2 +- .../autonomousdatabaserestore_webhook.go | 31 +++++----- .../autonomousdatabaserestore_webhook_test.go | 10 +-- .../v1alpha1/zz_generated.deepcopy.go | 4 +- apis/database/v4/adbfamily_common_spec.go | 6 +- .../v4/autonomouscontainerdatabase_webhook.go | 19 +++--- .../database/v4/autonomousdatabase_webhook.go | 62 ++++--------------- .../v4/autonomousdatabasebackup_types.go | 6 +- .../v4/autonomousdatabasebackup_webhook.go | 46 ++++++++------ .../v4/autonomousdatabaserestore_types.go | 4 +- .../v4/autonomousdatabaserestore_webhook.go | 32 +++++----- apis/database/v4/zz_generated.deepcopy.go | 20 +++--- commons/adb_family/utils.go | 2 +- commons/k8s/create.go | 2 +- ....oracle.com_autonomousdatabasebackups.yaml | 12 ++-- ...oracle.com_autonomousdatabaserestores.yaml | 16 ++--- .../adb/autonomousdatabase_backup.yaml | 6 +- .../adb/autonomousdatabase_restore.yaml | 8 +-- .../autonomousdatabasebackup_controller.go | 4 +- .../autonomousdatabaserestore_controller.go | 4 +- docs/adb/ADB_LONG_TERM_BACKUP.md | 10 +-- docs/adb/ADB_RESTORE.md | 16 ++--- ...tonomousdatabase_controller_create_test.go | 2 +- 29 files changed, 217 insertions(+), 265 deletions(-) diff --git a/apis/database/v1alpha1/adbfamily_common_spec.go b/apis/database/v1alpha1/adbfamily_common_spec.go index 74eb9f94..e7a9b8c4 100644 --- a/apis/database/v1alpha1/adbfamily_common_spec.go +++ b/apis/database/v1alpha1/adbfamily_common_spec.go @@ -57,11 +57,11 @@ type K8sAdbSpec struct { } type OciAdbSpec struct { - Ocid *string `json:"ocid,omitempty"` + Id *string `json:"id,omitempty"` } // TargetSpec defines the spec of the target for backup/restore runs. type TargetSpec struct { - K8sAdb K8sAdbSpec `json:"k8sADB,omitempty"` - OciAdb OciAdbSpec `json:"ociADB,omitempty"` + K8sAdb K8sAdbSpec `json:"k8sAdb,omitempty"` + OciAdb OciAdbSpec `json:"ociAdb,omitempty"` } diff --git a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go index b71600db..fc46cfb3 100644 --- a/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go @@ -73,24 +73,27 @@ func (r *AutonomousContainerDatabase) ValidateCreate(ctx context.Context, obj ru // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousContainerDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - var allErrs field.ErrorList - var oldACD *AutonomousContainerDatabase = oldObj.(*AutonomousContainerDatabase) + var ( + allErrs field.ErrorList + oldAcd *AutonomousContainerDatabase = oldObj.(*AutonomousContainerDatabase) + newAcd *AutonomousContainerDatabase = newObj.(*AutonomousContainerDatabase) + ) - autonomouscontainerdatabaselog.Info("validate update", "name", r.Name) + autonomouscontainerdatabaselog.Info("validate update", "name", newAcd.Name) // skip the update of adding ADB OCID or binding - if oldACD.Status.LifecycleState == "" { + if oldAcd.Status.LifecycleState == "" { return nil, nil } // cannot update when the old state is in intermediate state, except for the terminate operatrion - var copiedSpec *AutonomousContainerDatabaseSpec = r.Spec.DeepCopy() - changed, err := dbv4.RemoveUnchangedFields(oldACD.Spec, copiedSpec) + var copiedSpec *AutonomousContainerDatabaseSpec = newAcd.Spec.DeepCopy() + changed, err := dbv4.RemoveUnchangedFields(oldAcd.Spec, copiedSpec) if err != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), err.Error())) } - if dbv4.IsACDIntermediateState(oldACD.Status.LifecycleState) && changed { + if dbv4.IsACDIntermediateState(oldAcd.Status.LifecycleState) && changed { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "cannot change the spec when the lifecycleState is in an intermdeiate state")) @@ -101,7 +104,7 @@ func (r *AutonomousContainerDatabase) ValidateUpdate(ctx context.Context, oldObj } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousContainerDatabase"}, - r.Name, allErrs) + newAcd.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v1alpha1/autonomousdatabase_conversion.go b/apis/database/v1alpha1/autonomousdatabase_conversion.go index bf7f4fd8..fe3cbc7f 100644 --- a/apis/database/v1alpha1/autonomousdatabase_conversion.go +++ b/apis/database/v1alpha1/autonomousdatabase_conversion.go @@ -224,7 +224,7 @@ func (src *AutonomousDatabaseBackup) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*v4.AutonomousDatabaseBackup) dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name - dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.Ocid + dst.Spec.Target.OciAdb.Id = src.Spec.Target.OciAdb.Id dst.Spec.DisplayName = src.Spec.DisplayName dst.Spec.AutonomousDatabaseBackupOCID = src.Spec.AutonomousDatabaseBackupOCID dst.Spec.IsLongTermBackup = src.Spec.IsLongTermBackup @@ -250,7 +250,7 @@ func (dst *AutonomousDatabaseBackup) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*v4.AutonomousDatabaseBackup) dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name - dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.Ocid + dst.Spec.Target.OciAdb.Id = src.Spec.Target.OciAdb.Id dst.Spec.DisplayName = src.Spec.DisplayName dst.Spec.AutonomousDatabaseBackupOCID = src.Spec.AutonomousDatabaseBackupOCID dst.Spec.IsLongTermBackup = src.Spec.IsLongTermBackup @@ -276,7 +276,7 @@ func (src *AutonomousDatabaseRestore) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*v4.AutonomousDatabaseRestore) dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name - dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.Ocid + dst.Spec.Target.OciAdb.Id = src.Spec.Target.OciAdb.Id dst.Spec.Source.K8sAdbBackup.Name = src.Spec.Source.K8sAdbBackup.Name dst.Spec.Source.PointInTime.Timestamp = src.Spec.Source.PointInTime.Timestamp dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName @@ -298,7 +298,7 @@ func (dst *AutonomousDatabaseRestore) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*v4.AutonomousDatabaseRestore) dst.Spec.Target.K8sAdb.Name = src.Spec.Target.K8sAdb.Name - dst.Spec.Target.OciAdb.Ocid = src.Spec.Target.OciAdb.Ocid + dst.Spec.Target.OciAdb.Id = src.Spec.Target.OciAdb.Id dst.Spec.Source.K8sAdbBackup.Name = src.Spec.Source.K8sAdbBackup.Name dst.Spec.Source.PointInTime.Timestamp = src.Spec.Source.PointInTime.Timestamp dst.Spec.OCIConfig.ConfigMapName = src.Spec.OCIConfig.ConfigMapName diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go index 8f5c551a..8ab9b5e8 100644 --- a/apis/database/v1alpha1/autonomousdatabase_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -71,17 +71,19 @@ var _ webhook.CustomValidator = &AutonomousDatabase{} func (r *AutonomousDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList - autonomousdatabaselog.Info("validate create", "name", r.Name) + adb := obj.(*AutonomousDatabase) + + autonomousdatabaselog.Info("validate create", "name", adb.Name) namespaces := dbcommons.GetWatchNamespaces() _, hasEmptyString := namespaces[""] isClusterScoped := len(namespaces) == 1 && hasEmptyString if !isClusterScoped { - _, containsNamespace := namespaces[r.Namespace] + _, containsNamespace := namespaces[adb.Namespace] // Check if the allowed namespaces maps contains the required namespace if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), adb.Namespace, "Oracle database operator doesn't watch over this namespace")) } } @@ -91,47 +93,17 @@ func (r *AutonomousDatabase) ValidateCreate(ctx context.Context, obj runtime.Obj } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabase"}, - r.Name, allErrs) + adb.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - var allErrs field.ErrorList - var oldAdb *AutonomousDatabase = oldObj.(*AutonomousDatabase) - - autonomousdatabaselog.Info("validate update", "name", r.Name) - - // skip the update of adding ADB OCID or binding - // if oldAdb.Status.LifecycleState == "" { - // return nil, nil - // } - - // cannot update when the old state is in intermediate, except for the change to the hardLink or the terminate operatrion during valid lifecycleState - // var copySpec *AutonomousDatabaseSpec = r.Spec.DeepCopy() - // specChanged, err := dbv4.RemoveUnchangedFields(oldAdb.Spec, copySpec) - // if err != nil { - // allErrs = append(allErrs, - // field.Forbidden(field.NewPath("spec"), err.Error())) - // } - - // hardLinkChanged := copySpec.HardLink != nil + var ( + allErrs field.ErrorList + newAdb = newObj.(*AutonomousDatabase) + ) - // isTerminateOp := dbv4.CanBeTerminated(oldAdb.Status.LifecycleState) && copySpec.Action == "Terminate" - - // if specChanged && dbv4.IsAdbIntermediateState(oldAdb.Status.LifecycleState) && !isTerminateOp && !hardLinkChanged { - // allErrs = append(allErrs, - // field.Forbidden(field.NewPath("spec"), - // "cannot change the spec when the lifecycleState is in an intermdeiate state")) - // } - - // cannot modify autonomousDatabaseOCID - if r.Spec.Details.Id != nil && - oldAdb.Spec.Details.Id != nil && - *r.Spec.Details.Id != *oldAdb.Spec.Details.Id { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("autonomousDatabaseOCID"), - "autonomousDatabaseOCID cannot be modified")) - } + autonomousdatabaselog.Info("validate update", "name", newAdb.Name) allErrs = validateCommon(r, allErrs) @@ -140,7 +112,7 @@ func (r *AutonomousDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabase"}, - r.Name, allErrs) + newAdb.Name, allErrs) } func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.ErrorList { @@ -153,7 +125,7 @@ func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.Erro if adb.Spec.Wallet.Password.K8sSecret.Name != nil && adb.Spec.Wallet.Password.OciSecret.Id != nil { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("wallet").Child("password"), + field.Forbidden(field.NewPath("spec").Child("wallet").Child("password"), "cannot apply k8sSecret.name and ociSecret.ocid at the same time")) } @@ -162,13 +134,5 @@ func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.Erro // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabase) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - autonomousdatabaselog.Info("validate delete", "name", r.Name) return nil, nil } - -// Returns true if AutonomousContainerDatabaseOCID has value. -// We don't use Details.IsDedicated because the parameter might be null when it's a provision operation. -func isDedicated(adb *AutonomousDatabase) bool { - return adb.Spec.Details.AutonomousContainerDatabase.K8sAcd.Name != nil || - adb.Spec.Details.AutonomousContainerDatabase.OciAcd.Id != nil -} diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go index 2ee6dc22..004d6f92 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go @@ -68,7 +68,9 @@ var _ webhook.CustomValidator = &AutonomousDatabaseBackup{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - autonomousdatabasebackuplog.Info("validate create", "name", r.Name) + backup := obj.(*AutonomousDatabaseBackup) + + autonomousdatabasebackuplog.Info("validate create", "name", backup.Name) var allErrs field.ErrorList @@ -76,23 +78,23 @@ func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runti _, hasEmptyString := namespaces[""] isClusterScoped := len(namespaces) == 1 && hasEmptyString if !isClusterScoped { - _, containsNamespace := namespaces[r.Namespace] + _, containsNamespace := namespaces[backup.Namespace] // Check if the allowed namespaces maps contains the required namespace if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), backup.Namespace, "Oracle database operator doesn't watch over this namespace")) } } - if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.Ocid == nil { + if backup.Spec.Target.K8sAdb.Name == nil && backup.Spec.Target.OciAdb.Id == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.Ocid != nil { + if backup.Spec.Target.K8sAdb.Name != nil && backup.Spec.Target.OciAdb.Id != nil { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB or ociADB, but not both")) + field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sAdb or ociAdb, but not both")) } if len(allErrs) == 0 { @@ -100,37 +102,40 @@ func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runti } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseBackup"}, - r.Name, allErrs) + backup.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabaseBackup) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - autonomousdatabasebackuplog.Info("validate update", "name", r.Name) + var ( + allErrs field.ErrorList + oldBackup = oldObj.(*AutonomousDatabaseBackup) + newBackup = oldObj.(*AutonomousDatabaseBackup) + ) - var allErrs field.ErrorList - oldBackup := oldObj.(*AutonomousDatabaseBackup) + autonomousdatabasebackuplog.Info("validate update", "name", newBackup.Name) - if oldBackup.Spec.AutonomousDatabaseBackupOCID != nil && r.Spec.AutonomousDatabaseBackupOCID != nil && - *oldBackup.Spec.AutonomousDatabaseBackupOCID != *r.Spec.AutonomousDatabaseBackupOCID { + if oldBackup.Spec.AutonomousDatabaseBackupOCID != nil && newBackup.Spec.AutonomousDatabaseBackupOCID != nil && + *oldBackup.Spec.AutonomousDatabaseBackupOCID != *newBackup.Spec.AutonomousDatabaseBackupOCID { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("autonomousDatabaseBackupOCID"), "cannot assign a new autonomousDatabaseBackupOCID to this backup")) } - if oldBackup.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.K8sAdb.Name != nil && - *oldBackup.Spec.Target.K8sAdb.Name != *r.Spec.Target.K8sAdb.Name { + if oldBackup.Spec.Target.K8sAdb.Name != nil && newBackup.Spec.Target.K8sAdb.Name != nil && + *oldBackup.Spec.Target.K8sAdb.Name != *newBackup.Spec.Target.K8sAdb.Name { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("target").Child("k8sADB").Child("name"), "cannot assign a new name to the target")) + field.Forbidden(field.NewPath("spec").Child("target").Child("k8sAdb").Child("name"), "cannot assign a new name to the target")) } - if oldBackup.Spec.Target.OciAdb.Ocid != nil && r.Spec.Target.OciAdb.Ocid != nil && - *oldBackup.Spec.Target.OciAdb.Ocid != *r.Spec.Target.OciAdb.Ocid { + if oldBackup.Spec.Target.OciAdb.Id != nil && newBackup.Spec.Target.OciAdb.Id != nil && + *oldBackup.Spec.Target.OciAdb.Id != *newBackup.Spec.Target.OciAdb.Id { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("target").Child("ociADB").Child("ocid"), "cannot assign a new ocid to the target")) + field.Forbidden(field.NewPath("spec").Child("target").Child("ociAdb").Child("id"), "cannot assign a new ocid to the target")) } - if oldBackup.Spec.DisplayName != nil && r.Spec.DisplayName != nil && - *oldBackup.Spec.DisplayName != *r.Spec.DisplayName { + if oldBackup.Spec.DisplayName != nil && newBackup.Spec.DisplayName != nil && + *oldBackup.Spec.DisplayName != *newBackup.Spec.DisplayName { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("displayName"), "cannot assign a new displayName to this backup")) } @@ -140,7 +145,7 @@ func (r *AutonomousDatabaseBackup) ValidateUpdate(ctx context.Context, oldObj, n } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseBackup"}, - r.Name, allErrs) + newBackup.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go index 87eb1618..d9a254a7 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go @@ -72,20 +72,20 @@ var _ = Describe("test AutonomousDatabaseBackup webhook", func() { } }) - It("Should specify at least one of the k8sADB and ociADB", func() { + It("Should specify at least one of the k8sAdb and ociAdb", func() { var errMsg string = "target ADB is empty" backup.Spec.Target.K8sAdb.Name = nil - backup.Spec.Target.OciAdb.Ocid = nil + backup.Spec.Target.OciAdb.Id = nil validateInvalidTest(backup, false, errMsg) }) - It("Should specify either k8sADB or ociADB, but not both", func() { - var errMsg string = "specify either k8sADB or ociADB, but not both" + It("Should specify either k8sAdb or ociAdb, but not both", func() { + var errMsg string = "specify either k8sAdb or ociAdb, but not both" backup.Spec.Target.K8sAdb.Name = common.String("fake-target-adb") - backup.Spec.Target.OciAdb.Ocid = common.String("fake.ocid1.autonomousdatabase.oc1...") + backup.Spec.Target.OciAdb.Id = common.String("fake.ocid1.autonomousdatabase.oc1...") validateInvalidTest(backup, false, errMsg) }) @@ -155,15 +155,15 @@ var _ = Describe("test AutonomousDatabaseBackup webhook", func() { }) }) - Context("The bakcup is using target.ociADB.ocid", func() { + Context("The bakcup is using target.ociAdb.id", func() { BeforeEach(func() { - backup.Spec.Target.OciAdb.Ocid = common.String("fake.ocid1.autonomousdatabase.oc1...") + backup.Spec.Target.OciAdb.Id = common.String("fake.ocid1.autonomousdatabase.oc1...") }) It("Cannot assign a new ocid to the target", func() { var errMsg string = "cannot assign a new ocid to the target" - backup.Spec.Target.OciAdb.Ocid = common.String("modified.ocid1.autonomousdatabase.oc1...") + backup.Spec.Target.OciAdb.Id = common.String("modified.ocid1.autonomousdatabase.oc1...") validateInvalidTest(backup, true, errMsg) }) diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_types.go b/apis/database/v1alpha1/autonomousdatabaserestore_types.go index ef8698b2..74eb2eda 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_types.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_types.go @@ -61,7 +61,7 @@ type PitSpec struct { } type SourceSpec struct { - K8sAdbBackup K8sAdbBackupSpec `json:"k8sADBBackup,omitempty"` + K8sAdbBackup K8sAdbBackupSpec `json:"k8sAdbBackup,omitempty"` PointInTime PitSpec `json:"pointInTime,omitempty"` } diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go index b1f7278f..e8696045 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go @@ -69,50 +69,53 @@ var _ webhook.CustomValidator = &AutonomousDatabaseRestore{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabaseRestore) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - autonomousdatabaserestorelog.Info("validate create", "name", r.Name) + var ( + allErrs field.ErrorList + restore = obj.(*AutonomousDatabaseRestore) + ) - var allErrs field.ErrorList + autonomousdatabaserestorelog.Info("validate create", "name", restore.Name) namespaces := dbcommons.GetWatchNamespaces() _, hasEmptyString := namespaces[""] isClusterScoped := len(namespaces) == 1 && hasEmptyString if !isClusterScoped { - _, containsNamespace := namespaces[r.Namespace] + _, containsNamespace := namespaces[restore.Namespace] // Check if the allowed namespaces maps contains the required namespace if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), restore.Namespace, "Oracle database operator doesn't watch over this namespace")) } } // Validate the target ADB - if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.Ocid == nil { + if restore.Spec.Target.K8sAdb.Name == nil && restore.Spec.Target.OciAdb.Id == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.Ocid != nil { + if restore.Spec.Target.K8sAdb.Name != nil && restore.Spec.Target.OciAdb.Id != nil { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB.name or ociADB.ocid, but not both")) + field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sAdb.name or ociAdb.id, but not both")) } // Validate the restore source - if r.Spec.Source.K8sAdbBackup.Name == nil && - r.Spec.Source.PointInTime.Timestamp == nil { + if restore.Spec.Source.K8sAdbBackup.Name == nil && + restore.Spec.Source.PointInTime.Timestamp == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source"), "retore source is empty")) } - if r.Spec.Source.K8sAdbBackup.Name != nil && - r.Spec.Source.PointInTime.Timestamp != nil { + if restore.Spec.Source.K8sAdbBackup.Name != nil && + restore.Spec.Source.PointInTime.Timestamp != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source"), "cannot apply backupName and the PITR parameters at the same time")) } // Verify the timestamp format if it's PITR - if r.Spec.Source.PointInTime.Timestamp != nil { - _, err := dbv4.ParseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) + if restore.Spec.Source.PointInTime.Timestamp != nil { + _, err := dbv4.ParseDisplayTime(*restore.Spec.Source.PointInTime.Timestamp) if err != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source").Child("pointInTime").Child("timestamp"), "invalid timestamp format")) @@ -124,7 +127,7 @@ func (r *AutonomousDatabaseRestore) ValidateCreate(ctx context.Context, obj runt } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseRestore"}, - r.Name, allErrs) + restore.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go index 0cc9b692..ae10816f 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go @@ -68,20 +68,20 @@ var _ = Describe("test AutonomousDatabaseRestore webhook", func() { } }) - It("Should specify at least one of the k8sADB and ociADB", func() { + It("Should specify at least one of the k8sAdb and ociAdb", func() { var errMsg string = "target ADB is empty" restore.Spec.Target.K8sAdb.Name = nil - restore.Spec.Target.OciAdb.Ocid = nil + restore.Spec.Target.OciAdb.Id = nil validateInvalidTest(restore, false, errMsg) }) - It("Should specify either k8sADB.name or ociADB.ocid, but not both", func() { - var errMsg string = "specify either k8sADB.name or ociADB.ocid, but not both" + It("Should specify either k8sAdb.name or ociAdb.id, but not both", func() { + var errMsg string = "specify either k8sAdb.name or ociAdb.id, but not both" restore.Spec.Target.K8sAdb.Name = common.String("fake-target-adb") - restore.Spec.Target.OciAdb.Ocid = common.String("fake.ocid1.autonomousdatabase.oc1...") + restore.Spec.Target.OciAdb.Id = common.String("fake.ocid1.autonomousdatabase.oc1...") validateInvalidTest(restore, false, errMsg) }) diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index b20cf834..661fb197 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1742,8 +1742,8 @@ func (in *OciAcdSpec) DeepCopy() *OciAcdSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OciAdbSpec) DeepCopyInto(out *OciAdbSpec) { *out = *in - if in.Ocid != nil { - in, out := &in.Ocid, &out.Ocid + if in.Id != nil { + in, out := &in.Id, &out.Id *out = new(string) **out = **in } diff --git a/apis/database/v4/adbfamily_common_spec.go b/apis/database/v4/adbfamily_common_spec.go index 1eb0f323..57b80352 100644 --- a/apis/database/v4/adbfamily_common_spec.go +++ b/apis/database/v4/adbfamily_common_spec.go @@ -57,11 +57,11 @@ type K8sAdbSpec struct { } type OciAdbSpec struct { - Ocid *string `json:"ocid,omitempty"` + Id *string `json:"id,omitempty"` } // TargetSpec defines the spec of the target for backup/restore runs. type TargetSpec struct { - K8sAdb K8sAdbSpec `json:"k8sADB,omitempty"` - OciAdb OciAdbSpec `json:"ociADB,omitempty"` + K8sAdb K8sAdbSpec `json:"k8sAdb,omitempty"` + OciAdb OciAdbSpec `json:"ociAdb,omitempty"` } diff --git a/apis/database/v4/autonomouscontainerdatabase_webhook.go b/apis/database/v4/autonomouscontainerdatabase_webhook.go index f5b6a8d8..1cd8b2b4 100644 --- a/apis/database/v4/autonomouscontainerdatabase_webhook.go +++ b/apis/database/v4/autonomouscontainerdatabase_webhook.go @@ -72,24 +72,27 @@ func (r *AutonomousContainerDatabase) ValidateCreate(ctx context.Context, obj ru // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousContainerDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - var allErrs field.ErrorList - var oldACD *AutonomousContainerDatabase = oldObj.(*AutonomousContainerDatabase) + var ( + allErrs field.ErrorList + oldAcd *AutonomousContainerDatabase = oldObj.(*AutonomousContainerDatabase) + newAcd *AutonomousContainerDatabase = newObj.(*AutonomousContainerDatabase) + ) - autonomouscontainerdatabaselog.Info("validate update", "name", r.Name) + autonomouscontainerdatabaselog.Info("validate update", "name", newAcd.Name) // skip the update of adding ADB OCID or binding - if oldACD.Status.LifecycleState == "" { + if oldAcd.Status.LifecycleState == "" { return nil, nil } // cannot update when the old state is in intermediate state, except for the terminate operatrion - var copiedSpec *AutonomousContainerDatabaseSpec = r.Spec.DeepCopy() - changed, err := RemoveUnchangedFields(oldACD.Spec, copiedSpec) + var copiedSpec *AutonomousContainerDatabaseSpec = newAcd.Spec.DeepCopy() + changed, err := RemoveUnchangedFields(oldAcd.Spec, copiedSpec) if err != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), err.Error())) } - if IsACDIntermediateState(oldACD.Status.LifecycleState) && changed { + if IsACDIntermediateState(oldAcd.Status.LifecycleState) && changed { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "cannot change the spec when the lifecycleState is in an intermdeiate state")) @@ -100,7 +103,7 @@ func (r *AutonomousContainerDatabase) ValidateUpdate(ctx context.Context, oldObj } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousContainerDatabase"}, - r.Name, allErrs) + newAcd.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v4/autonomousdatabase_webhook.go b/apis/database/v4/autonomousdatabase_webhook.go index bc025691..d925fd12 100644 --- a/apis/database/v4/autonomousdatabase_webhook.go +++ b/apis/database/v4/autonomousdatabase_webhook.go @@ -71,17 +71,19 @@ var _ webhook.CustomValidator = &AutonomousDatabase{} func (r *AutonomousDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList - autonomousdatabaselog.Info("validate create", "name", r.Name) + adb := obj.(*AutonomousDatabase) + + autonomousdatabaselog.Info("validate create", "name", adb.Name) namespaces := dbcommons.GetWatchNamespaces() _, hasEmptyString := namespaces[""] isClusterScoped := len(namespaces) == 1 && hasEmptyString if !isClusterScoped { - _, containsNamespace := namespaces[r.Namespace] + _, containsNamespace := namespaces[adb.Namespace] // Check if the allowed namespaces maps contains the required namespace if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), adb.Namespace, "Oracle database operator doesn't watch over this namespace")) } } @@ -91,47 +93,17 @@ func (r *AutonomousDatabase) ValidateCreate(ctx context.Context, obj runtime.Obj } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabase"}, - r.Name, allErrs) + adb.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - var allErrs field.ErrorList - var oldADB *AutonomousDatabase = oldObj.(*AutonomousDatabase) - - autonomousdatabaselog.Info("validate update", "name", r.Name) - - // skip the verification of adding ADB OCID or binding - // if oldADB.Status.LifecycleState == "" { - // return nil, nil - // } - - // cannot update when the old state is in intermediate, except for the change to the hardLink or the terminate operatrion during valid lifecycleState - // var copySpec *AutonomousDatabaseSpec = r.Spec.DeepCopy() - // specChanged, err := RemoveUnchangedFields(oldADB.Spec, copySpec) - // if err != nil { - // allErrs = append(allErrs, - // field.Forbidden(field.NewPath("spec"), err.Error())) - // } - - // hardLinkChanged := copySpec.HardLink != nil + var ( + allErrs field.ErrorList + newAdb = newObj.(*AutonomousDatabase) + ) - // isTerminateOp := CanBeTerminated(oldADB.Status.LifecycleState) && copySpec.Action == "Terminate" - - // if specChanged && IsAdbIntermediateState(oldADB.Status.LifecycleState) && !isTerminateOp && !hardLinkChanged { - // allErrs = append(allErrs, - // field.Forbidden(field.NewPath("spec"), - // "cannot change the spec when the lifecycleState is in an intermdeiate state")) - // } - - // cannot modify autonomousDatabaseOCID - if r.Spec.Details.Id != nil && - oldADB.Spec.Details.Id != nil && - *r.Spec.Details.Id != *oldADB.Spec.Details.Id { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("autonomousDatabaseOCID"), - "autonomousDatabaseOCID cannot be modified")) - } + autonomousdatabaselog.Info("validate update", "name", newAdb.Name) allErrs = validateCommon(r, allErrs) @@ -140,7 +112,7 @@ func (r *AutonomousDatabase) ValidateUpdate(ctx context.Context, oldObj, newObj } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabase"}, - r.Name, allErrs) + newAdb.Name, allErrs) } func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.ErrorList { @@ -153,7 +125,7 @@ func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.Erro if adb.Spec.Wallet.Password.K8sSecret.Name != nil && adb.Spec.Wallet.Password.OciSecret.Id != nil { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("details").Child("wallet").Child("password"), + field.Forbidden(field.NewPath("spec").Child("wallet").Child("password"), "cannot apply k8sSecret.name and ociSecret.ocid at the same time")) } @@ -162,13 +134,5 @@ func validateCommon(adb *AutonomousDatabase, allErrs field.ErrorList) field.Erro // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabase) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - autonomousdatabaselog.Info("validate delete", "name", r.Name) return nil, nil } - -// Returns true if AutonomousContainerDatabaseOCID has value. -// We don't use Details.IsDedicated because the parameter might be null when it's a provision operation. -func isDedicated(adb *AutonomousDatabase) bool { - return adb.Spec.Details.AutonomousContainerDatabase.K8sAcd.Name != nil || - adb.Spec.Details.AutonomousContainerDatabase.OciAcd.Id != nil -} diff --git a/apis/database/v4/autonomousdatabasebackup_types.go b/apis/database/v4/autonomousdatabasebackup_types.go index 925256c0..c38c079b 100644 --- a/apis/database/v4/autonomousdatabasebackup_types.go +++ b/apis/database/v4/autonomousdatabasebackup_types.go @@ -108,7 +108,7 @@ func init() { // Implement conversion.Hub interface, which means any resource version can convert into v4 func (*AutonomousDatabaseBackup) Hub() {} -func (b *AutonomousDatabaseBackup) UpdateStatusFromOCIBackup(ociBackup database.AutonomousDatabaseBackup, ociADB database.AutonomousDatabase) { +func (b *AutonomousDatabaseBackup) UpdateStatusFromOCIBackup(ociBackup database.AutonomousDatabaseBackup, ociAdb database.AutonomousDatabase) { b.Status.AutonomousDatabaseOCID = *ociBackup.AutonomousDatabaseId b.Status.CompartmentOCID = *ociBackup.CompartmentId b.Status.Type = ociBackup.Type @@ -119,8 +119,8 @@ func (b *AutonomousDatabaseBackup) UpdateStatusFromOCIBackup(ociBackup database. b.Status.TimeStarted = FormatSDKTime(ociBackup.TimeStarted) b.Status.TimeEnded = FormatSDKTime(ociBackup.TimeEnded) - b.Status.DBDisplayName = *ociADB.DisplayName - b.Status.DBName = *ociADB.DbName + b.Status.DBDisplayName = *ociAdb.DisplayName + b.Status.DBName = *ociAdb.DbName } // GetTimeEnded returns the status.timeEnded in SDKTime format diff --git a/apis/database/v4/autonomousdatabasebackup_webhook.go b/apis/database/v4/autonomousdatabasebackup_webhook.go index cb409660..1ab8cfc1 100644 --- a/apis/database/v4/autonomousdatabasebackup_webhook.go +++ b/apis/database/v4/autonomousdatabasebackup_webhook.go @@ -59,6 +59,7 @@ func (r *AutonomousDatabaseBackup) SetupWebhookWithManager(mgr ctrl.Manager) err return ctrl.NewWebhookManagedBy(mgr). For(r). WithValidator(r). + WithValidator(r). Complete() } @@ -68,7 +69,9 @@ var _ webhook.CustomValidator = &AutonomousDatabaseBackup{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - autonomousdatabasebackuplog.Info("validate create", "name", r.Name) + backup := obj.(*AutonomousDatabaseBackup) + + autonomousdatabasebackuplog.Info("validate create", "name", backup.Name) var allErrs field.ErrorList @@ -76,23 +79,23 @@ func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runti _, hasEmptyString := namespaces[""] isClusterScoped := len(namespaces) == 1 && hasEmptyString if !isClusterScoped { - _, containsNamespace := namespaces[r.Namespace] + _, containsNamespace := namespaces[backup.Namespace] // Check if the allowed namespaces maps contains the required namespace if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), backup.Namespace, "Oracle database operator doesn't watch over this namespace")) } } - if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.Ocid == nil { + if backup.Spec.Target.K8sAdb.Name == nil && backup.Spec.Target.OciAdb.Id == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.Ocid != nil { + if backup.Spec.Target.K8sAdb.Name != nil && backup.Spec.Target.OciAdb.Id != nil { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB or ociADB, but not both")) + field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sAdb or ociAdb, but not both")) } if len(allErrs) == 0 { @@ -100,37 +103,40 @@ func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runti } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseBackup"}, - r.Name, allErrs) + backup.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabaseBackup) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - autonomousdatabasebackuplog.Info("validate update", "name", r.Name) + var ( + allErrs field.ErrorList + oldBackup = oldObj.(*AutonomousDatabaseBackup) + newBackup = oldObj.(*AutonomousDatabaseBackup) + ) - var allErrs field.ErrorList - oldBackup := oldObj.(*AutonomousDatabaseBackup) + autonomousdatabasebackuplog.Info("validate update", "name", newBackup.Name) - if oldBackup.Spec.AutonomousDatabaseBackupOCID != nil && r.Spec.AutonomousDatabaseBackupOCID != nil && - *oldBackup.Spec.AutonomousDatabaseBackupOCID != *r.Spec.AutonomousDatabaseBackupOCID { + if oldBackup.Spec.AutonomousDatabaseBackupOCID != nil && newBackup.Spec.AutonomousDatabaseBackupOCID != nil && + *oldBackup.Spec.AutonomousDatabaseBackupOCID != *newBackup.Spec.AutonomousDatabaseBackupOCID { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("autonomousDatabaseBackupOCID"), "cannot assign a new autonomousDatabaseBackupOCID to this backup")) } - if oldBackup.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.K8sAdb.Name != nil && - *oldBackup.Spec.Target.K8sAdb.Name != *r.Spec.Target.K8sAdb.Name { + if oldBackup.Spec.Target.K8sAdb.Name != nil && newBackup.Spec.Target.K8sAdb.Name != nil && + *oldBackup.Spec.Target.K8sAdb.Name != *newBackup.Spec.Target.K8sAdb.Name { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("target").Child("k8sADB").Child("name"), "cannot assign a new name to the target")) + field.Forbidden(field.NewPath("spec").Child("target").Child("k8sAdb").Child("name"), "cannot assign a new name to the target")) } - if oldBackup.Spec.Target.OciAdb.Ocid != nil && r.Spec.Target.OciAdb.Ocid != nil && - *oldBackup.Spec.Target.OciAdb.Ocid != *r.Spec.Target.OciAdb.Ocid { + if oldBackup.Spec.Target.OciAdb.Id != nil && newBackup.Spec.Target.OciAdb.Id != nil && + *oldBackup.Spec.Target.OciAdb.Id != *newBackup.Spec.Target.OciAdb.Id { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target").Child("ociADB").Child("ocid"), "cannot assign a new ocid to the target")) } - if oldBackup.Spec.DisplayName != nil && r.Spec.DisplayName != nil && - *oldBackup.Spec.DisplayName != *r.Spec.DisplayName { + if oldBackup.Spec.DisplayName != nil && newBackup.Spec.DisplayName != nil && + *oldBackup.Spec.DisplayName != *newBackup.Spec.DisplayName { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("displayName"), "cannot assign a new displayName to this backup")) } @@ -140,7 +146,7 @@ func (r *AutonomousDatabaseBackup) ValidateUpdate(ctx context.Context, oldObj, n } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseBackup"}, - r.Name, allErrs) + newBackup.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v4/autonomousdatabaserestore_types.go b/apis/database/v4/autonomousdatabaserestore_types.go index 3337c983..f88b7f79 100644 --- a/apis/database/v4/autonomousdatabaserestore_types.go +++ b/apis/database/v4/autonomousdatabaserestore_types.go @@ -50,7 +50,7 @@ import ( // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. -type K8sADBBackupSpec struct { +type K8sAdbBackupSpec struct { Name *string `json:"name,omitempty"` } @@ -60,7 +60,7 @@ type PITSpec struct { } type SourceSpec struct { - K8sAdbBackup K8sADBBackupSpec `json:"k8sADBBackup,omitempty"` + K8sAdbBackup K8sAdbBackupSpec `json:"k8sAdbBackup,omitempty"` PointInTime PITSpec `json:"pointInTime,omitempty"` } diff --git a/apis/database/v4/autonomousdatabaserestore_webhook.go b/apis/database/v4/autonomousdatabaserestore_webhook.go index f9f18528..0df1288d 100644 --- a/apis/database/v4/autonomousdatabaserestore_webhook.go +++ b/apis/database/v4/autonomousdatabaserestore_webhook.go @@ -59,6 +59,7 @@ func (r *AutonomousDatabaseRestore) SetupWebhookWithManager(mgr ctrl.Manager) er return ctrl.NewWebhookManagedBy(mgr). For(r). WithValidator(r). + WithValidator(r). Complete() } @@ -68,50 +69,53 @@ var _ webhook.CustomValidator = &AutonomousDatabaseRestore{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *AutonomousDatabaseRestore) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - autonomousdatabaserestorelog.Info("validate create", "name", r.Name) + var ( + allErrs field.ErrorList + restore = obj.(*AutonomousDatabaseRestore) + ) - var allErrs field.ErrorList + autonomousdatabaserestorelog.Info("validate create", "name", restore.Name) namespaces := dbcommons.GetWatchNamespaces() _, hasEmptyString := namespaces[""] isClusterScoped := len(namespaces) == 1 && hasEmptyString if !isClusterScoped { - _, containsNamespace := namespaces[r.Namespace] + _, containsNamespace := namespaces[restore.Namespace] // Check if the allowed namespaces maps contains the required namespace if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), restore.Namespace, "Oracle database operator doesn't watch over this namespace")) } } // Validate the target ADB - if r.Spec.Target.K8sAdb.Name == nil && r.Spec.Target.OciAdb.Ocid == nil { + if restore.Spec.Target.K8sAdb.Name == nil && restore.Spec.Target.OciAdb.Id == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if r.Spec.Target.K8sAdb.Name != nil && r.Spec.Target.OciAdb.Ocid != nil { + if restore.Spec.Target.K8sAdb.Name != nil && restore.Spec.Target.OciAdb.Id != nil { allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sADB.name or ociADB.ocid, but not both")) + field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sAdb.name or ociAdb.id, but not both")) } // Validate the restore source - if r.Spec.Source.K8sAdbBackup.Name == nil && - r.Spec.Source.PointInTime.Timestamp == nil { + if restore.Spec.Source.K8sAdbBackup.Name == nil && + restore.Spec.Source.PointInTime.Timestamp == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source"), "retore source is empty")) } - if r.Spec.Source.K8sAdbBackup.Name != nil && - r.Spec.Source.PointInTime.Timestamp != nil { + if restore.Spec.Source.K8sAdbBackup.Name != nil && + restore.Spec.Source.PointInTime.Timestamp != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source"), "cannot apply backupName and the PITR parameters at the same time")) } // Verify the timestamp format if it's PITR - if r.Spec.Source.PointInTime.Timestamp != nil { - _, err := ParseDisplayTime(*r.Spec.Source.PointInTime.Timestamp) + if restore.Spec.Source.PointInTime.Timestamp != nil { + _, err := ParseDisplayTime(*restore.Spec.Source.PointInTime.Timestamp) if err != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("source").Child("pointInTime").Child("timestamp"), "invalid timestamp format")) @@ -123,7 +127,7 @@ func (r *AutonomousDatabaseRestore) ValidateCreate(ctx context.Context, obj runt } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "AutonomousDatabaseRestore"}, - r.Name, allErrs) + restore.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 512ec41b..77e05585 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -2088,7 +2088,7 @@ func (in *InitParams) DeepCopy() *InitParams { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *K8sADBBackupSpec) DeepCopyInto(out *K8sADBBackupSpec) { +func (in *K8sAcdSpec) DeepCopyInto(out *K8sAcdSpec) { *out = *in if in.Name != nil { in, out := &in.Name, &out.Name @@ -2097,18 +2097,18 @@ func (in *K8sADBBackupSpec) DeepCopyInto(out *K8sADBBackupSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sADBBackupSpec. -func (in *K8sADBBackupSpec) DeepCopy() *K8sADBBackupSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sAcdSpec. +func (in *K8sAcdSpec) DeepCopy() *K8sAcdSpec { if in == nil { return nil } - out := new(K8sADBBackupSpec) + out := new(K8sAcdSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *K8sAcdSpec) DeepCopyInto(out *K8sAcdSpec) { +func (in *K8sAdbBackupSpec) DeepCopyInto(out *K8sAdbBackupSpec) { *out = *in if in.Name != nil { in, out := &in.Name, &out.Name @@ -2117,12 +2117,12 @@ func (in *K8sAcdSpec) DeepCopyInto(out *K8sAcdSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sAcdSpec. -func (in *K8sAcdSpec) DeepCopy() *K8sAcdSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sAdbBackupSpec. +func (in *K8sAdbBackupSpec) DeepCopy() *K8sAdbBackupSpec { if in == nil { return nil } - out := new(K8sAcdSpec) + out := new(K8sAdbBackupSpec) in.DeepCopyInto(out) return out } @@ -2780,8 +2780,8 @@ func (in *OciAcdSpec) DeepCopy() *OciAcdSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OciAdbSpec) DeepCopyInto(out *OciAdbSpec) { *out = *in - if in.Ocid != nil { - in, out := &in.Ocid, &out.Ocid + if in.Id != nil { + in, out := &in.Id, &out.Id *out = new(string) **out = **in } diff --git a/commons/adb_family/utils.go b/commons/adb_family/utils.go index 67dc673a..270e93d1 100644 --- a/commons/adb_family/utils.go +++ b/commons/adb_family/utils.go @@ -62,7 +62,7 @@ func VerifyTargetAdb(kubeClient client.Client, target dbv4.TargetSpec, namespace } else { // Find the target ADB using the ADB OCID - ownerAdb, err = k8s.FetchAutonomousDatabaseWithOCID(kubeClient, namespace, *target.OciAdb.Ocid) + ownerAdb, err = k8s.FetchAutonomousDatabaseWithOCID(kubeClient, namespace, *target.OciAdb.Id) if err != nil { return nil, err } diff --git a/commons/k8s/create.go b/commons/k8s/create.go index ddfcb485..82e100a1 100644 --- a/commons/k8s/create.go +++ b/commons/k8s/create.go @@ -95,7 +95,7 @@ func CreateAutonomousBackup(kubeClient client.Client, Name: common.String(ownerAdb.Name), }, OciAdb: dbv4.OciAdbSpec{ - Ocid: backupSummary.AutonomousDatabaseId, + Id: backupSummary.AutonomousDatabaseId, }, }, DisplayName: backupSummary.DisplayName, diff --git a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml index b0d6f8ed..86abc9e7 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml @@ -62,14 +62,14 @@ spec: type: integer target: properties: - k8sADB: + k8sAdb: properties: name: type: string type: object - ociADB: + ociAdb: properties: - ocid: + id: type: string type: object type: object @@ -153,14 +153,14 @@ spec: type: integer target: properties: - k8sADB: + k8sAdb: properties: name: type: string type: object - ociADB: + ociAdb: properties: - ocid: + id: type: string type: object type: object diff --git a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml index 3bfc5a4e..edcac8ce 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml @@ -48,7 +48,7 @@ spec: type: object source: properties: - k8sADBBackup: + k8sAdbBackup: properties: name: type: string @@ -61,14 +61,14 @@ spec: type: object target: properties: - k8sADB: + k8sAdb: properties: name: type: string type: object - ociADB: + ociAdb: properties: - ocid: + id: type: string type: object type: object @@ -134,7 +134,7 @@ spec: type: object source: properties: - k8sADBBackup: + k8sAdbBackup: properties: name: type: string @@ -147,14 +147,14 @@ spec: type: object target: properties: - k8sADB: + k8sAdb: properties: name: type: string type: object - ociADB: + ociAdb: properties: - ocid: + id: type: string type: object type: object diff --git a/config/samples/adb/autonomousdatabase_backup.yaml b/config/samples/adb/autonomousdatabase_backup.yaml index cf633b5d..448481ee 100644 --- a/config/samples/adb/autonomousdatabase_backup.yaml +++ b/config/samples/adb/autonomousdatabase_backup.yaml @@ -10,11 +10,11 @@ spec: # Before you can create on-demand backups, you must have an Object Storage bucket and your database must be configured to connect to it. This is a one-time operation. # See https://docs.oracle.com/en-us/iaas/Content/Database/Tasks/adbbackingup.htm#creatingbucket target: - k8sADB: + k8sAdb: name: autonomousdatabase-sample # # Uncomment the below block if you use ADB OCID as the input of the target ADB - # ociADB: - # ocid: ocid1.autonomousdatabase... + # ociAdb: + # id: ocid1.autonomousdatabase... displayName: autonomousdatabasebackup-sample isLongTermBackup: true retentionPeriodInDays: 90 # minimum retention period is 90 days diff --git a/config/samples/adb/autonomousdatabase_restore.yaml b/config/samples/adb/autonomousdatabase_restore.yaml index 7d3164c4..98d9a827 100644 --- a/config/samples/adb/autonomousdatabase_restore.yaml +++ b/config/samples/adb/autonomousdatabase_restore.yaml @@ -10,13 +10,13 @@ spec: # Restore the database either from a backup or using point-in-time restore # The name of your AutonomousDatabaseBackup resource target: - k8sADB: + k8sAdb: name: autonomousdatabase-sample # # Uncomment the below block if you use ADB OCID as the input of the target ADB - # ociADB: - # ocid: ocid1.autonomousdatabase... + # ociAdb: + # id: ocid1.autonomousdatabase... source: - k8sADBBackup: + k8sAdbBackup: name: autonomousdatabasebackup-sample # # Uncomment the following field to perform point-in-time restore # pointInTime: diff --git a/controllers/database/autonomousdatabasebackup_controller.go b/controllers/database/autonomousdatabasebackup_controller.go index 1a806276..b647ac4c 100644 --- a/controllers/database/autonomousdatabasebackup_controller.go +++ b/controllers/database/autonomousdatabasebackup_controller.go @@ -227,8 +227,8 @@ func (r *AutonomousDatabaseBackupReconciler) verifyTargetAdb(backup *dbv4.Autono } } - if backup.Spec.Target.OciAdb.Ocid != nil { - return *backup.Spec.Target.OciAdb.Ocid, nil + if backup.Spec.Target.OciAdb.Id != nil { + return *backup.Spec.Target.OciAdb.Id, nil } if ownerAdb != nil && ownerAdb.Spec.Details.Id != nil { return *ownerAdb.Spec.Details.Id, nil diff --git a/controllers/database/autonomousdatabaserestore_controller.go b/controllers/database/autonomousdatabaserestore_controller.go index 66871c72..93f1c57e 100644 --- a/controllers/database/autonomousdatabaserestore_controller.go +++ b/controllers/database/autonomousdatabaserestore_controller.go @@ -236,8 +236,8 @@ func (r *AutonomousDatabaseRestoreReconciler) verifyTargetAdb(restore *dbv4.Auto } } - if restore.Spec.Target.OciAdb.Ocid != nil { - return *restore.Spec.Target.OciAdb.Ocid, nil + if restore.Spec.Target.OciAdb.Id != nil { + return *restore.Spec.Target.OciAdb.Id, nil } if ownerAdb != nil && ownerAdb.Spec.Details.Id != nil { return *ownerAdb.Spec.Details.Id, nil diff --git a/docs/adb/ADB_LONG_TERM_BACKUP.md b/docs/adb/ADB_LONG_TERM_BACKUP.md index 312dac0d..37c8611e 100644 --- a/docs/adb/ADB_LONG_TERM_BACKUP.md +++ b/docs/adb/ADB_LONG_TERM_BACKUP.md @@ -14,8 +14,8 @@ To back up an Autonomous Database, complete this procedure. | `spec.displayName` | string | The user-friendly name for the backup. This name does not have to be unique. | Yes | | `spec.isLongTermBackup` | boolean | Indicates whether the backup is long-term. | Yes | | `spec.retentionPeriodInDays` | string | Retention period, in days, for long-term backups. Minimum retention period is 90 days. | Yes | - | `spec.target.k8sADB.name` | string | The name of custom resource of the target Autonomous Database. Choose either the `spec.target.k8sADB.name` or the `spec.target.ociADB.ocid`, but not both. | Conditional | - | `spec.target.ociADB.ocid` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the target AutonomousDatabase. Choose either the `spec.target.k8sADB.name` or the `spec.target.ociADB.ocid`, but not both. | Conditional | + | `spec.target.k8sAdb.name` | string | The name of custom resource of the target Autonomous Database. Choose either the `spec.target.k8sAdb.name` or the `spec.target.ociAdb.id`, but not both. | Conditional | + | `spec.target.ociAdb.id` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the target AutonomousDatabase. Choose either the `spec.target.k8sAdb.name` or the `spec.target.ociAdb.id`, but not both. | Conditional | | `spec.ociConfig` | dictionary | Not required when the Operator is authorized with [Instance Principal](./ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from this section: [Authorized with API Key Authentication](./ADB_PREREQUISITES.md#authorized-with-api-key-authentication). | Conditional | | `spec.ociConfig.configMapName` | string | Name of the ConfigMap that holds the local OCI configuration | Conditional | | `spec.ociConfig.secretName`| string | Name of the Kubernetes (K8s) Secret that holds the private key value | Conditional | @@ -28,11 +28,11 @@ To back up an Autonomous Database, complete this procedure. name: autonomousdatabasebackup-sample spec: target: - k8sADB: + k8sAdb: name: autonomousdatabase-sample # # Uncomment the below block if you use ADB OCID as the input of the target ADB - # ociADB: - # ocid: ocid1.autonomousdatabase... + # ociAdb: + # id: ocid1.autonomousdatabase... displayName: autonomousdatabasebackup-sample isLongTermBackup: true retentionPeriodInDays: 90 diff --git a/docs/adb/ADB_RESTORE.md b/docs/adb/ADB_RESTORE.md index 7a80090a..9d607787 100644 --- a/docs/adb/ADB_RESTORE.md +++ b/docs/adb/ADB_RESTORE.md @@ -11,10 +11,10 @@ To restore an Autonomous Database from a backup, or by using point-in-time resto 1. Add the following fields to the AutonomousDatabaseBackup resource definition. An example `.yaml` file is available here: [`config/samples/adb/autonomousdatabase_restore.yaml`](./../../config/samples/adb/autonomousdatabase_restore.yaml) | Attribute | Type | Description | Required? | |----|----|----|----| - | `spec.target.k8sADB.name` | string | The name of custom resource of the target Autonomous Database (`AutonomousDatabase`). Choose either the `spec.target.k8sADB.name` or the `spec.target.ociADB.ocid`, but not both. | Conditional | - | `spec.target.ociADB.ocid` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the target `AutonomousDatabase`. Choose either the `spec.target.k8sADB.name` or the `spec.target.ociADB.ocid`, but not both. | Conditional | - | `spec.source.k8sADBBackup.name` | string | The name of custom resource of the `AutonomousDatabaseBackup` that you want to restore from. Choose either the `spec.source.k8sADBBackup.name` or the `spec.source.pointInTime.timestamp`, but not both. | Conditional | - | `spec.source.pointInTime.timestamp` | string | The timestamp to specify the point in time to which you want the database restored. Your Autonomous Database identifies which backup to use for the fastest restore. The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT. Choose either the `spec.source.k8sADBBackup.name` or the `spec.source.pointInTime.timestamp`, but not both. | Conditional | + | `spec.target.k8sAdb.name` | string | The name of custom resource of the target Autonomous Database (`AutonomousDatabase`). Choose either the `spec.target.k8sAdb.name` or the `spec.target.ociAdb.id`, but not both. | Conditional | + | `spec.target.ociAdb.id` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the target `AutonomousDatabase`. Choose either the `spec.target.k8sAdb.name` or the `spec.target.ociAdb.id`, but not both. | Conditional | + | `spec.source.k8sAdbBackup.name` | string | The name of custom resource of the `AutonomousDatabaseBackup` that you want to restore from. Choose either the `spec.source.k8sAdbBackup.name` or the `spec.source.pointInTime.timestamp`, but not both. | Conditional | + | `spec.source.pointInTime.timestamp` | string | The timestamp to specify the point in time to which you want the database restored. Your Autonomous Database identifies which backup to use for the fastest restore. The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT. Choose either the `spec.source.k8sAdbBackup.name` or the `spec.source.pointInTime.timestamp`, but not both. | Conditional | | `spec.ociConfig` | dictionary | Not required when the Operator is authorized with [Instance Principal](./ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from this section: [Authorized with API Key Authentication](./ADB_PREREQUISITES.md#authorized-with-api-key-authentication). | Conditional | | `spec.ociConfig.configMapName` | string | Name of the `ConfigMap` that holds the local OCI configuration | Conditional | | `spec.ociConfig.secretName`| string | Name of the Kubernetes (K8s) Secret that holds the private key value | Conditional | @@ -27,13 +27,13 @@ To restore an Autonomous Database from a backup, or by using point-in-time resto name: autonomousdatabaserestore-sample spec: target: - k8sADB: + k8sAdb: name: autonomousdatabase-sample # # Uncomment the below block if you use ADB OCID as the input of the target ADB - # ociADB: - # ocid: ocid1.autonomousdatabase... + # ociAdb: + # id: ocid1.autonomousdatabase... source: - k8sADBBackup: + k8sAdbBackup: name: autonomousdatabasebackup-sample # # Uncomment the following field to perform point-in-time restore # pointInTime: diff --git a/test/e2e/autonomousdatabase_controller_create_test.go b/test/e2e/autonomousdatabase_controller_create_test.go index cc7fd288..bb63c8ad 100644 --- a/test/e2e/autonomousdatabase_controller_create_test.go +++ b/test/e2e/autonomousdatabase_controller_create_test.go @@ -200,7 +200,7 @@ var _ = Describe("test ADB provisioning", func() { Spec: dbv1alpha1.AutonomousDatabaseBackupSpec{ Target: dbv1alpha1.TargetSpec{ OciAdb: dbv1alpha1.OciAdbSpec{ - Ocid: common.String(*databaseOCID), + Id: common.String(*databaseOCID), }, }, DisplayName: common.String(backupName), From caf61a90fc14afece913d5cd49ad8002188ff26a Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Tue, 12 Aug 2025 05:00:36 +0000 Subject: [PATCH 260/414] dbcs fix --- commons/dbcssystem/dcommon.go | 10 ++++++++-- config/manager/kustomization.yaml | 4 ++-- oracle-database-operator.yaml | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/commons/dbcssystem/dcommon.go b/commons/dbcssystem/dcommon.go index 2529d3aa..bef676d9 100644 --- a/commons/dbcssystem/dcommon.go +++ b/commons/dbcssystem/dcommon.go @@ -55,11 +55,17 @@ import ( func GetDbHomeDetails(kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev4.DbcsSystem, id string) (database.CreateDbHomeDetails, error) { dbHomeDetails := database.CreateDbHomeDetails{} - - dbHomeReq, err := GetDbLatestVersion(dbClient, dbcs, *dbcs.Spec.Id) + dbHomeReq, err := GetDbLatestVersion(dbClient, dbcs, "") if err != nil { return database.CreateDbHomeDetails{}, err } + if dbcs.Spec.Id != nil { + dbHomeReq, err = GetDbLatestVersion(dbClient, dbcs, *dbcs.Spec.Id) + if err != nil { + return database.CreateDbHomeDetails{}, err + } + } + dbHomeDetails.DbVersion = &dbHomeReq dbDetailsReq, err := GetDBDetails(kubeClient, dbcs) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 15893aad..74303bea 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lin.ocir.io/intsanjaysingh/marcstef/oo - newTag: 1.2.0.8 + newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database + newTag: basedb-operator-sa diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 876c1ae9..31951dfc 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -16806,7 +16806,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: lin.ocir.io/intsanjaysingh/marcstef/oo:1.2.0.8 + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa imagePullPolicy: Always name: manager ports: From 49218b28a674dfd8c60c19b638b36a3c35903fa9 Mon Sep 17 00:00:00 2001 From: Geela Rajahimansh Date: Tue, 12 Aug 2025 05:09:04 +0000 Subject: [PATCH 261/414] Hotfix/update replica count --- commons/omlai/aibuilder.go | 4 +++- oracle-database-operator.yaml | 32 ++++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/commons/omlai/aibuilder.go b/commons/omlai/aibuilder.go index 77b4b5c1..2b6d7a45 100644 --- a/commons/omlai/aibuilder.go +++ b/commons/omlai/aibuilder.go @@ -12,6 +12,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/rest" + "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -65,7 +66,8 @@ func buildDeploymentSpecForPrivateAI(instance *privateaiv4.PrivateAi) *appsv1.De } return &appsv1.DeploymentSpec{ - Replicas: &replicas, + Replicas: &replicas, + RevisionHistoryLimit: pointer.Int32(0), Selector: &metav1.LabelSelector{ MatchLabels: buildLabelsForPrivateAi(instance, "privateai", instance.Name), }, diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 6e391fbc..e5d2bd88 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -639,9 +639,9 @@ spec: - jsonPath: .spec.details.isDedicated name: Dedicated type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer + - jsonPath: .spec.details.computeCount + name: Compute Count + type: number - jsonPath: .spec.details.dataStorageSizeInTBs name: Storage (TB) type: integer @@ -974,9 +974,9 @@ spec: - jsonPath: .spec.details.isDedicated name: Dedicated type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer + - jsonPath: .spec.details.computeCount + name: Compute Count + type: number - jsonPath: .spec.details.dataStorageSizeInTBs name: Storage (TB) type: integer @@ -16353,6 +16353,26 @@ webhooks: resources: - autonomousdatabaserestores sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-autonomousdatabase + failurePolicy: Fail + name: vautonomousdatabasev4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + resources: + - autonomousdatabases + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 From 2045254f479f100936d3dbf8fee5970669bc45a0 Mon Sep 17 00:00:00 2001 From: Rahul Vats Date: Thu, 14 Aug 2025 06:33:41 +0000 Subject: [PATCH 262/414] Update webhook for sidb, ords and dg controllers --- .../v1alpha1/dataguardbroker_webhook.go | 69 ++-- .../v1alpha1/oraclerestdataservice_webhook.go | 69 ++-- .../singleinstancedatabase_webhook.go | 329 +++++++++--------- 3 files changed, 255 insertions(+), 212 deletions(-) diff --git a/apis/database/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go index 33ea4d73..c9cc7bc7 100644 --- a/apis/database/v1alpha1/dataguardbroker_webhook.go +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -42,6 +42,7 @@ import ( "context" "strconv" "strings" + "fmt" dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -73,29 +74,33 @@ var _ webhook.CustomDefaulter = &DataguardBroker{} // Default implements webhook.Defaulter so a webhook will be registered for the type func (r *DataguardBroker) Default(ctx context.Context, obj runtime.Object) error { - dataguardbrokerlog.Info("default", "name", r.Name) + dg, ok := obj.(*DataguardBroker) + if !ok { + return apierrors.NewInternalError(fmt.Errorf("failed to cast obj object to DataguardBroker")) + } + dataguardbrokerlog.Info("default", "name", dg.Name) - if r.Spec.LoadBalancer { - if r.Spec.ServiceAnnotations == nil { - r.Spec.ServiceAnnotations = make(map[string]string) + if dg.Spec.LoadBalancer { + if dg.Spec.ServiceAnnotations == nil { + dg.Spec.ServiceAnnotations = make(map[string]string) } // Annotations required for a flexible load balancer on oci - _, ok := r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] + _, ok := dg.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] if !ok { - r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] = "flexible" + dg.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] = "flexible" } - _, ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] + _, ok = dg.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] if !ok { - r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] = "10" + dg.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] = "10" } - _, ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] + _, ok = dg.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] if !ok { - r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] = "100" + dg.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] = "100" } } - if r.Spec.SetAsPrimaryDatabase != "" { - r.Spec.SetAsPrimaryDatabase = strings.ToUpper(r.Spec.SetAsPrimaryDatabase) + if dg.Spec.SetAsPrimaryDatabase != "" { + dg.Spec.SetAsPrimaryDatabase = strings.ToUpper(dg.Spec.SetAsPrimaryDatabase) } return nil @@ -108,15 +113,18 @@ var _ webhook.CustomValidator = &DataguardBroker{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *DataguardBroker) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - - dataguardbrokerlog.Info("validate create", "name", r.Name) + dg, ok := obj.(*DataguardBroker) + if !ok { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast obj object to DataguardBroker")) + } + dataguardbrokerlog.Info("validate create", "name", dg.Name) var allErrs field.ErrorList namespaces := dbcommons.GetWatchNamespaces() - _, containsNamespace := namespaces[r.Namespace] + _, containsNamespace := namespaces[dg.Namespace] // Check if the allowed namespaces maps contains the required namespace if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), dg.Namespace, "Oracle database operator doesn't watch over this namespace")) } @@ -126,46 +134,49 @@ func (r *DataguardBroker) ValidateCreate(ctx context.Context, obj runtime.Object return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "Dataguard"}, - r.Name, allErrs) + dg.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *DataguardBroker) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { - dataguardbrokerlog.Info("validate update", "name", r.Name) + new, ok := newObj.(*DataguardBroker) + if !ok { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast newObj object to DataguardBroker")) + } - dataguardbrokerlog.Info("validate update", "name", r.Name) + dataguardbrokerlog.Info("validate update", "name", new.Name) var allErrs field.ErrorList // check creation validations first - _, err := r.ValidateCreate(ctx, newObj) + _, err := new.ValidateCreate(ctx, newObj) if err != nil { return nil, err } // Validate Deletion - if r.GetDeletionTimestamp() != nil { - warnings, err := r.ValidateDelete(ctx, newObj) + if new.GetDeletionTimestamp() != nil { + warnings, err := new.ValidateDelete(ctx, newObj) if err != nil { return warnings, err } } // Now check for updation errors - oldObj, ok := old.(*DataguardBroker) - if !ok { - return nil, nil + oldObj, okay := old.(*DataguardBroker) + if !okay { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast old object to DataguardBroker")) } - if oldObj.Status.ProtectionMode != "" && !strings.EqualFold(r.Spec.ProtectionMode, oldObj.Status.ProtectionMode) { + if oldObj.Status.ProtectionMode != "" && !strings.EqualFold(new.Spec.ProtectionMode, oldObj.Status.ProtectionMode) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("protectionMode"), "cannot be changed")) } - if oldObj.Status.PrimaryDatabaseRef != "" && !strings.EqualFold(oldObj.Status.PrimaryDatabaseRef, r.Spec.PrimaryDatabaseRef) { + if oldObj.Status.PrimaryDatabaseRef != "" && !strings.EqualFold(oldObj.Status.PrimaryDatabaseRef, new.Spec.PrimaryDatabaseRef) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("primaryDatabaseRef"), "cannot be changed")) } fastStartFailoverStatus, _ := strconv.ParseBool(oldObj.Status.FastStartFailover) - if (fastStartFailoverStatus || r.Spec.FastStartFailover) && r.Spec.SetAsPrimaryDatabase != "" { + if (fastStartFailoverStatus || new.Spec.FastStartFailover) && new.Spec.SetAsPrimaryDatabase != "" { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("setAsPrimaryDatabase"), "switchover not supported when fastStartFailover is true")) } @@ -175,7 +186,7 @@ func (r *DataguardBroker) ValidateUpdate(ctx context.Context, old, newObj runtim } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "DataguardBroker"}, - r.Name, allErrs) + new.Name, allErrs) } diff --git a/apis/database/v1alpha1/oraclerestdataservice_webhook.go b/apis/database/v1alpha1/oraclerestdataservice_webhook.go index 5d000b8d..72dcedea 100644 --- a/apis/database/v1alpha1/oraclerestdataservice_webhook.go +++ b/apis/database/v1alpha1/oraclerestdataservice_webhook.go @@ -40,6 +40,7 @@ package v1alpha1 import ( "context" + "fmt" dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -71,15 +72,19 @@ var _ webhook.CustomDefaulter = &OracleRestDataService{} // Default implements webhook.Defaulter so a webhook will be registered for the type func (r *OracleRestDataService) Default(ctx context.Context, obj runtime.Object) error { - oraclerestdataservicelog.Info("default", "name", r.Name) + ords, ok := obj.(*OracleRestDataService) + if !ok { + return apierrors.NewInternalError(fmt.Errorf("failed to cast obj object to OracleRestDataService")) + } + oraclerestdataservicelog.Info("default", "name", ords.Name) // OracleRestDataService Currently supports single replica - r.Spec.Replicas = 1 + ords.Spec.Replicas = 1 keepSecret := true - if r.Spec.OrdsPassword.KeepSecret == nil { - r.Spec.OrdsPassword.KeepSecret = &keepSecret + if ords.Spec.OrdsPassword.KeepSecret == nil { + ords.Spec.OrdsPassword.KeepSecret = &keepSecret } - if r.Spec.AdminPassword.KeepSecret == nil { - r.Spec.AdminPassword.KeepSecret = &keepSecret + if ords.Spec.AdminPassword.KeepSecret == nil { + ords.Spec.AdminPassword.KeepSecret = &keepSecret } return nil @@ -92,45 +97,49 @@ var _ webhook.CustomValidator = &OracleRestDataService{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *OracleRestDataService) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - oraclerestdataservicelog.Info("validate create", "name", r.Name) + ords, ok := obj.(*OracleRestDataService) + if !ok { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast obj object to OracleRestDataService")) + } + oraclerestdataservicelog.Info("validate create", "name", ords.Name) var allErrs field.ErrorList namespaces := dbcommons.GetWatchNamespaces() - _, containsNamespace := namespaces[r.Namespace] + _, containsNamespace := namespaces[ords.Namespace] // Check if the allowed namespaces maps contains the required namespace if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), ords.Namespace, "Oracle database operator doesn't watch over this namespace")) } // Persistence spec validation - if r.Spec.Persistence.Size == "" && (r.Spec.Persistence.AccessMode != "" || - r.Spec.Persistence.StorageClass != "" || r.Spec.Persistence.VolumeName != "") { + if ords.Spec.Persistence.Size == "" && (ords.Spec.Persistence.AccessMode != "" || + ords.Spec.Persistence.StorageClass != "" || ords.Spec.Persistence.VolumeName != "") { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), r.Spec.Persistence, + field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), ords.Spec.Persistence, "invalid persistence specification, specify required size")) } - if r.Spec.Persistence.Size != "" { - if r.Spec.Persistence.AccessMode == "" { + if ords.Spec.Persistence.Size != "" { + if ords.Spec.Persistence.AccessMode == "" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), r.Spec.Persistence, + field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), ords.Spec.Persistence, "invalid persistence specification, specify accessMode")) } - if r.Spec.Persistence.AccessMode != "ReadWriteMany" && r.Spec.Persistence.AccessMode != "ReadWriteOnce" { + if ords.Spec.Persistence.AccessMode != "ReadWriteMany" && ords.Spec.Persistence.AccessMode != "ReadWriteOnce" { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("persistence").Child("accessMode"), - r.Spec.Persistence.AccessMode, "should be either \"ReadWriteOnce\" or \"ReadWriteMany\"")) + ords.Spec.Persistence.AccessMode, "should be either \"ReadWriteOnce\" or \"ReadWriteMany\"")) } } // Validating databaseRef and ORDS kind name not to be same - if r.Spec.DatabaseRef == r.Name { + if ords.Spec.DatabaseRef == ords.Name { allErrs = append(allErrs, field.Forbidden(field.NewPath("Name"), - "cannot be same as DatabaseRef: "+r.Spec.DatabaseRef)) + "cannot be same as DatabaseRef: "+ords.Spec.DatabaseRef)) } @@ -139,32 +148,36 @@ func (r *OracleRestDataService) ValidateCreate(ctx context.Context, obj runtime. } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "OracleRestDataService"}, - r.Name, allErrs) + ords.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *OracleRestDataService) ValidateUpdate(ctx context.Context, oldRuntimeObject, newRuntimeObject runtime.Object) (admission.Warnings, error) { - oraclerestdataservicelog.Info("validate update", "name", r.Name) + new, ok := newRuntimeObject.(*OracleRestDataService) + if !ok { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast newRuntimeObject object to OracleRestDataService")) + } + oraclerestdataservicelog.Info("validate update", "name", new.Name) var allErrs field.ErrorList // check creation validations first - warnings, err := r.ValidateCreate(ctx, newRuntimeObject) + warnings, err := new.ValidateCreate(ctx, newRuntimeObject) if err != nil { return warnings, err } // Now check for updation errors - old, ok := oldRuntimeObject.(*OracleRestDataService) - if !ok { - return nil, nil + old, okay := oldRuntimeObject.(*OracleRestDataService) + if !okay { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast oldRuntimeObject object to OracleRestDataService")) } - if old.Status.DatabaseRef != "" && old.Status.DatabaseRef != r.Spec.DatabaseRef { + if old.Status.DatabaseRef != "" && old.Status.DatabaseRef != new.Spec.DatabaseRef { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("databaseRef"), "cannot be changed")) } - if old.Status.Image.PullFrom != "" && old.Status.Image != r.Spec.Image { + if old.Status.Image.PullFrom != "" && old.Status.Image != new.Spec.Image { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("image"), "cannot be changed")) } @@ -174,7 +187,7 @@ func (r *OracleRestDataService) ValidateUpdate(ctx context.Context, oldRuntimeOb } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "OracleRestDataService"}, - r.Name, allErrs) + new.Name, allErrs) } diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index d8e894a2..d0cd88aa 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -43,6 +43,7 @@ import ( "strconv" "strings" "time" + "fmt" dbcommons "github.com/oracle/oracle-database-operator/commons/database" @@ -75,72 +76,77 @@ var _ webhook.CustomDefaulter = &SingleInstanceDatabase{} // Default implements webhook.Defaulter so a webhook will be registered for the type func (r *SingleInstanceDatabase) Default(ctx context.Context, obj runtime.Object) error { - singleinstancedatabaselog.Info("default", "name", r.Name) + sidb, ok := obj.(*SingleInstanceDatabase) + if !ok { + return apierrors.NewInternalError(fmt.Errorf("failed to cast obj object to SingleInstanceDatabase")) + } + + singleinstancedatabaselog.Info("default", "name", sidb.Name) - if r.Spec.LoadBalancer { + if sidb.Spec.LoadBalancer { // Annotations required for a flexible load balancer on oci - if r.Spec.ServiceAnnotations == nil { - r.Spec.ServiceAnnotations = make(map[string]string) + if sidb.Spec.ServiceAnnotations == nil { + sidb.Spec.ServiceAnnotations = make(map[string]string) } - _, ok := r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] + _, ok := sidb.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] if !ok { - r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] = "flexible" + sidb.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] = "flexible" } - _, ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] + _, ok = sidb.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] if !ok { - r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] = "10" + sidb.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-min"] = "10" } - _, ok = r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] + _, ok = sidb.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] if !ok { - r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] = "100" + sidb.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] = "100" } } - if r.Spec.AdminPassword.KeepSecret == nil { + if sidb.Spec.AdminPassword.KeepSecret == nil { keepSecret := true - r.Spec.AdminPassword.KeepSecret = &keepSecret + sidb.Spec.AdminPassword.KeepSecret = &keepSecret } - if r.Spec.Edition == "" { - if r.Spec.CreateAs == "clone" && !r.Spec.Image.PrebuiltDB { - r.Spec.Edition = "enterprise" + if sidb.Spec.Edition == "" { + if sidb.Spec.CreateAs == "clone" && !sidb.Spec.Image.PrebuiltDB { + sidb.Spec.Edition = "enterprise" } } - if r.Spec.CreateAs == "" { - r.Spec.CreateAs = "primary" + if sidb.Spec.CreateAs == "" { + sidb.Spec.CreateAs = "primary" } - if r.Spec.Sid == "" { - if r.Spec.Edition == "express" { - r.Spec.Sid = "XE" - } else if r.Spec.Edition == "free" { - r.Spec.Sid = "FREE" + if sidb.Spec.Sid == "" { + if sidb.Spec.Edition == "express" { + sidb.Spec.Sid = "XE" + } else if sidb.Spec.Edition == "free" { + sidb.Spec.Sid = "FREE" } else { - r.Spec.Sid = "ORCLCDB" + sidb.Spec.Sid = "ORCLCDB" } } - if r.Spec.Pdbname == "" { - if r.Spec.Edition == "express" { - r.Spec.Pdbname = "XEPDB1" - } else if r.Spec.Edition == "free" { - r.Spec.Pdbname = "FREEPDB1" + if sidb.Spec.Pdbname == "" { + if sidb.Spec.Edition == "express" { + sidb.Spec.Pdbname = "XEPDB1" + } else if sidb.Spec.Edition == "free" { + sidb.Spec.Pdbname = "FREEPDB1" } else { - r.Spec.Pdbname = "ORCLPDB1" + sidb.Spec.Pdbname = "ORCLPDB1" } } - if r.Spec.Edition == "express" || r.Spec.Edition == "free" { + if sidb.Spec.Edition == "express" || sidb.Spec.Edition == "free" { // Allow zero replicas as a means to bounce the DB - if r.Status.Replicas == 1 && r.Spec.Replicas > 1 { + if sidb.Status.Replicas == 1 && sidb.Spec.Replicas > 1 { // If not zero, default the replicas to 1 - r.Spec.Replicas = 1 + sidb.Spec.Replicas = 1 } } - if r.Spec.TrueCacheServices == nil { - r.Spec.TrueCacheServices = make([]string, 0) + if sidb.Spec.TrueCacheServices == nil { + sidb.Spec.TrueCacheServices = make([]string, 0) } return nil @@ -153,250 +159,254 @@ var _ webhook.CustomValidator = &SingleInstanceDatabase{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *SingleInstanceDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - singleinstancedatabaselog.Info("validate create", "name", r.Name) + sidb, ok := obj.(*SingleInstanceDatabase) + if !ok { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast obj object to SingleInstanceDatabase")) + } + singleinstancedatabaselog.Info("validate create", "name", sidb.Name) var allErrs field.ErrorList namespaces := dbcommons.GetWatchNamespaces() - _, containsNamespace := namespaces[r.Namespace] + _, containsNamespace := namespaces[sidb.Namespace] // Check if the allowed namespaces maps contains the required namespace if len(namespaces) != 0 && !containsNamespace { allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), sidb.Namespace, "Oracle database operator doesn't watch over this namespace")) } // Persistence spec validation - if r.Spec.Persistence.Size == "" && (r.Spec.Persistence.AccessMode != "" || - r.Spec.Persistence.StorageClass != "" || r.Spec.Persistence.DatafilesVolumeName != "") { + if sidb.Spec.Persistence.Size == "" && (sidb.Spec.Persistence.AccessMode != "" || + sidb.Spec.Persistence.StorageClass != "" || sidb.Spec.Persistence.DatafilesVolumeName != "") { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), r.Spec.Persistence, + field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), sidb.Spec.Persistence, "invalid persistence specification, specify required size")) } - if r.Spec.Persistence.Size != "" { - if r.Spec.Persistence.AccessMode == "" { + if sidb.Spec.Persistence.Size != "" { + if sidb.Spec.Persistence.AccessMode == "" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), r.Spec.Persistence, + field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), sidb.Spec.Persistence, "invalid persistence specification, specify accessMode")) } - if r.Spec.Persistence.AccessMode != "ReadWriteMany" && r.Spec.Persistence.AccessMode != "ReadWriteOnce" { + if sidb.Spec.Persistence.AccessMode != "ReadWriteMany" && sidb.Spec.Persistence.AccessMode != "ReadWriteOnce" { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("persistence").Child("accessMode"), - r.Spec.Persistence.AccessMode, "should be either \"ReadWriteOnce\" or \"ReadWriteMany\"")) + sidb.Spec.Persistence.AccessMode, "should be either \"ReadWriteOnce\" or \"ReadWriteMany\"")) } } - if r.Spec.CreateAs == "standby" { - if r.Spec.ArchiveLog != nil { + if sidb.Spec.CreateAs == "standby" { + if sidb.Spec.ArchiveLog != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("archiveLog"), - r.Spec.ArchiveLog, "archiveLog cannot be specified for standby databases")) + sidb.Spec.ArchiveLog, "archiveLog cannot be specified for standby databases")) } - if r.Spec.FlashBack != nil { + if sidb.Spec.FlashBack != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("flashBack"), - r.Spec.FlashBack, "flashBack cannot be specified for standby databases")) + sidb.Spec.FlashBack, "flashBack cannot be specified for standby databases")) } - if r.Spec.ForceLogging != nil { + if sidb.Spec.ForceLogging != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("forceLog"), - r.Spec.ForceLogging, "forceLog cannot be specified for standby databases")) + sidb.Spec.ForceLogging, "forceLog cannot be specified for standby databases")) } - if r.Spec.InitParams != nil { + if sidb.Spec.InitParams != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("initParams"), - r.Spec.InitParams, "initParams cannot be specified for standby databases")) + sidb.Spec.InitParams, "initParams cannot be specified for standby databases")) } - if r.Spec.Persistence.ScriptsVolumeName != "" { + if sidb.Spec.Persistence.ScriptsVolumeName != "" { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("persistence").Child("scriptsVolumeName"), - r.Spec.Persistence.ScriptsVolumeName, "scriptsVolumeName cannot be specified for standby databases")) + sidb.Spec.Persistence.ScriptsVolumeName, "scriptsVolumeName cannot be specified for standby databases")) } - if r.Spec.EnableTCPS { + if sidb.Spec.EnableTCPS { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("enableTCPS"), - r.Spec.EnableTCPS, "enableTCPS cannot be specified for standby databases")) + sidb.Spec.EnableTCPS, "enableTCPS cannot be specified for standby databases")) } } // Replica validation - if r.Spec.Replicas > 1 { + if sidb.Spec.Replicas > 1 { valMsg := "" - if r.Spec.Edition == "express" || r.Spec.Edition == "free" { - valMsg = "should be 1 for " + r.Spec.Edition + " edition" + if sidb.Spec.Edition == "express" || sidb.Spec.Edition == "free" { + valMsg = "should be 1 for " + sidb.Spec.Edition + " edition" } - if r.Spec.Persistence.Size == "" { + if sidb.Spec.Persistence.Size == "" { valMsg = "should be 1 if no persistence is specified" } if valMsg != "" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("replicas"), r.Spec.Replicas, valMsg)) + field.Invalid(field.NewPath("spec").Child("replicas"), sidb.Spec.Replicas, valMsg)) } } - if (r.Spec.CreateAs == "clone" || r.Spec.CreateAs == "standby") && r.Spec.PrimaryDatabaseRef == "" { + if (sidb.Spec.CreateAs == "clone" || sidb.Spec.CreateAs == "standby") && sidb.Spec.PrimaryDatabaseRef == "" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("primaryDatabaseRef"), r.Spec.PrimaryDatabaseRef, "Primary Database reference cannot be null for a secondary database")) + field.Invalid(field.NewPath("spec").Child("primaryDatabaseRef"), sidb.Spec.PrimaryDatabaseRef, "Primary Database reference cannot be null for a secondary database")) } - if r.Spec.Edition == "express" || r.Spec.Edition == "free" { - if r.Spec.CreateAs == "clone" { + if sidb.Spec.Edition == "express" || sidb.Spec.Edition == "free" { + if sidb.Spec.CreateAs == "clone" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("createAs"), r.Spec.CreateAs, - "Cloning not supported for "+r.Spec.Edition+" edition")) + field.Invalid(field.NewPath("spec").Child("createAs"), sidb.Spec.CreateAs, + "Cloning not supported for "+sidb.Spec.Edition+" edition")) } - if r.Spec.CreateAs == "standby" { + if sidb.Spec.CreateAs == "standby" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("createAs"), r.Spec.CreateAs, - "Physical Standby Database creation is not supported for "+r.Spec.Edition+" edition")) + field.Invalid(field.NewPath("spec").Child("createAs"), sidb.Spec.CreateAs, + "Physical Standby Database creation is not supported for "+sidb.Spec.Edition+" edition")) } - if r.Spec.Edition == "express" && strings.ToUpper(r.Spec.Sid) != "XE" { + if sidb.Spec.Edition == "express" && strings.ToUpper(sidb.Spec.Sid) != "XE" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("sid"), r.Spec.Sid, + field.Invalid(field.NewPath("spec").Child("sid"), sidb.Spec.Sid, "Express edition SID must only be XE")) } - if r.Spec.Edition == "free" && strings.ToUpper(r.Spec.Sid) != "FREE" { + if sidb.Spec.Edition == "free" && strings.ToUpper(sidb.Spec.Sid) != "FREE" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("sid"), r.Spec.Sid, + field.Invalid(field.NewPath("spec").Child("sid"), sidb.Spec.Sid, "Free edition SID must only be FREE")) } - if r.Spec.Edition == "express" && strings.ToUpper(r.Spec.Pdbname) != "XEPDB1" { + if sidb.Spec.Edition == "express" && strings.ToUpper(sidb.Spec.Pdbname) != "XEPDB1" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("pdbName"), r.Spec.Pdbname, + field.Invalid(field.NewPath("spec").Child("pdbName"), sidb.Spec.Pdbname, "Express edition PDB must be XEPDB1")) } - if r.Spec.Edition == "free" && strings.ToUpper(r.Spec.Pdbname) != "FREEPDB1" { + if sidb.Spec.Edition == "free" && strings.ToUpper(sidb.Spec.Pdbname) != "FREEPDB1" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("pdbName"), r.Spec.Pdbname, + field.Invalid(field.NewPath("spec").Child("pdbName"), sidb.Spec.Pdbname, "Free edition PDB must be FREEPDB1")) } - if r.Spec.InitParams != nil { + if sidb.Spec.InitParams != nil { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("initParams"), *r.Spec.InitParams, - r.Spec.Edition+" edition does not support changing init parameters")) + field.Invalid(field.NewPath("spec").Child("initParams"), *sidb.Spec.InitParams, + sidb.Spec.Edition+" edition does not support changing init parameters")) } } else { - if r.Spec.Sid == "XE" { + if sidb.Spec.Sid == "XE" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("sid"), r.Spec.Sid, + field.Invalid(field.NewPath("spec").Child("sid"), sidb.Spec.Sid, "XE is reserved as the SID for Express edition of the database")) } - if r.Spec.Sid == "FREE" { + if sidb.Spec.Sid == "FREE" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("sid"), r.Spec.Sid, + field.Invalid(field.NewPath("spec").Child("sid"), sidb.Spec.Sid, "FREE is reserved as the SID for FREE edition of the database")) } } - if r.Spec.CreateAs == "clone" { - if r.Spec.Image.PrebuiltDB { + if sidb.Spec.CreateAs == "clone" { + if sidb.Spec.Image.PrebuiltDB { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("createAs"), r.Spec.CreateAs, + field.Invalid(field.NewPath("spec").Child("createAs"), sidb.Spec.CreateAs, "cannot clone to create a prebuilt db")) - } else if strings.Contains(r.Spec.PrimaryDatabaseRef, ":") && strings.Contains(r.Spec.PrimaryDatabaseRef, "/") && r.Spec.Edition == "" { + } else if strings.Contains(sidb.Spec.PrimaryDatabaseRef, ":") && strings.Contains(sidb.Spec.PrimaryDatabaseRef, "/") && sidb.Spec.Edition == "" { //Edition must be passed when cloning from a source database other than same k8s cluster allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("edition"), r.Spec.CreateAs, + field.Invalid(field.NewPath("spec").Child("edition"), sidb.Spec.CreateAs, "Edition must be passed when cloning from a source database other than same k8s cluster")) } } - if r.Spec.CreateAs != "truecache" { - if len(r.Spec.TrueCacheServices) > 0 { + if sidb.Spec.CreateAs != "truecache" { + if len(sidb.Spec.TrueCacheServices) > 0 { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("trueCacheServices"), r.Spec.TrueCacheServices, + field.Invalid(field.NewPath("spec").Child("trueCacheServices"), sidb.Spec.TrueCacheServices, "Creation of trueCacheServices only supported with True Cache instances")) } } - if r.Status.FlashBack == "true" && r.Spec.FlashBack != nil && *r.Spec.FlashBack { - if r.Spec.ArchiveLog != nil && !*r.Spec.ArchiveLog { + if sidb.Status.FlashBack == "true" && sidb.Spec.FlashBack != nil && *sidb.Spec.FlashBack { + if sidb.Spec.ArchiveLog != nil && !*sidb.Spec.ArchiveLog { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("archiveLog"), r.Spec.ArchiveLog, + field.Invalid(field.NewPath("spec").Child("archiveLog"), sidb.Spec.ArchiveLog, "Cannot disable Archivelog. Please disable Flashback first.")) } } - if r.Status.ArchiveLog == "false" && r.Spec.ArchiveLog != nil && !*r.Spec.ArchiveLog { - if *r.Spec.FlashBack { + if sidb.Status.ArchiveLog == "false" && sidb.Spec.ArchiveLog != nil && !*sidb.Spec.ArchiveLog { + if sidb.Spec.FlashBack != nil && *sidb.Spec.FlashBack { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("flashBack"), r.Spec.FlashBack, + field.Invalid(field.NewPath("spec").Child("flashBack"), sidb.Spec.FlashBack, "Cannot enable Flashback. Please enable Archivelog first.")) } } - if r.Spec.Persistence.VolumeClaimAnnotation != "" { - strParts := strings.Split(r.Spec.Persistence.VolumeClaimAnnotation, ":") + if sidb.Spec.Persistence.VolumeClaimAnnotation != "" { + strParts := strings.Split(sidb.Spec.Persistence.VolumeClaimAnnotation, ":") if len(strParts) != 2 { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("persistence").Child("volumeClaimAnnotation"), r.Spec.Persistence.VolumeClaimAnnotation, + field.Invalid(field.NewPath("spec").Child("persistence").Child("volumeClaimAnnotation"), sidb.Spec.Persistence.VolumeClaimAnnotation, "volumeClaimAnnotation should be in : format.")) } } // servicePort and tcpServicePort validation - if !r.Spec.LoadBalancer { + if !sidb.Spec.LoadBalancer { // NodePort service is expected. In this case servicePort should be in range 30000-32767 - if r.Spec.ListenerPort != 0 && (r.Spec.ListenerPort < 30000 || r.Spec.ListenerPort > 32767) { + if sidb.Spec.ListenerPort != 0 && (sidb.Spec.ListenerPort < 30000 || sidb.Spec.ListenerPort > 32767) { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("listenerPort"), r.Spec.ListenerPort, + field.Invalid(field.NewPath("spec").Child("listenerPort"), sidb.Spec.ListenerPort, "listenerPort should be in 30000-32767 range.")) } - if r.Spec.EnableTCPS && r.Spec.TcpsListenerPort != 0 && (r.Spec.TcpsListenerPort < 30000 || r.Spec.TcpsListenerPort > 32767) { + if sidb.Spec.EnableTCPS && sidb.Spec.TcpsListenerPort != 0 && (sidb.Spec.TcpsListenerPort < 30000 || sidb.Spec.TcpsListenerPort > 32767) { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("tcpsListenerPort"), r.Spec.TcpsListenerPort, + field.Invalid(field.NewPath("spec").Child("tcpsListenerPort"), sidb.Spec.TcpsListenerPort, "tcpsListenerPort should be in 30000-32767 range.")) } } else { // LoadBalancer Service is expected. - if r.Spec.EnableTCPS && r.Spec.TcpsListenerPort == 0 && r.Spec.ListenerPort == int(dbcommons.CONTAINER_TCPS_PORT) { + if sidb.Spec.EnableTCPS && sidb.Spec.TcpsListenerPort == 0 && sidb.Spec.ListenerPort == int(dbcommons.CONTAINER_TCPS_PORT) { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("listenerPort"), r.Spec.ListenerPort, + field.Invalid(field.NewPath("spec").Child("listenerPort"), sidb.Spec.ListenerPort, "listenerPort can not be 2484 as the default port for tcpsListenerPort is 2484.")) } } - if r.Spec.EnableTCPS && r.Spec.ListenerPort != 0 && r.Spec.TcpsListenerPort != 0 && r.Spec.ListenerPort == r.Spec.TcpsListenerPort { + if sidb.Spec.EnableTCPS && sidb.Spec.ListenerPort != 0 && sidb.Spec.TcpsListenerPort != 0 && sidb.Spec.ListenerPort == sidb.Spec.TcpsListenerPort { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("tcpsListenerPort"), r.Spec.TcpsListenerPort, + field.Invalid(field.NewPath("spec").Child("tcpsListenerPort"), sidb.Spec.TcpsListenerPort, "listenerPort and tcpsListenerPort can not be equal.")) } // Certificate Renew Duration Validation - if r.Spec.EnableTCPS && r.Spec.TcpsCertRenewInterval != "" { - duration, err := time.ParseDuration(r.Spec.TcpsCertRenewInterval) + if sidb.Spec.EnableTCPS && sidb.Spec.TcpsCertRenewInterval != "" { + duration, err := time.ParseDuration(sidb.Spec.TcpsCertRenewInterval) if err != nil { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("tcpsCertRenewInterval"), r.Spec.TcpsCertRenewInterval, + field.Invalid(field.NewPath("spec").Child("tcpsCertRenewInterval"), sidb.Spec.TcpsCertRenewInterval, "Please provide valid string to parse the tcpsCertRenewInterval.")) } maxLimit, _ := time.ParseDuration("8760h") minLimit, _ := time.ParseDuration("24h") if duration > maxLimit || duration < minLimit { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("tcpsCertRenewInterval"), r.Spec.TcpsCertRenewInterval, + field.Invalid(field.NewPath("spec").Child("tcpsCertRenewInterval"), sidb.Spec.TcpsCertRenewInterval, "Please specify tcpsCertRenewInterval in the range: 24h to 8760h")) } } // tcpsTlsSecret validations - if !r.Spec.EnableTCPS && r.Spec.TcpsTlsSecret != "" { + if !sidb.Spec.EnableTCPS && sidb.Spec.TcpsTlsSecret != "" { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("tcpsTlsSecret"), " is allowed only if enableTCPS is true")) } - if r.Spec.TcpsTlsSecret != "" && r.Spec.TcpsCertRenewInterval != "" { + if sidb.Spec.TcpsTlsSecret != "" && sidb.Spec.TcpsCertRenewInterval != "" { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("tcpsCertRenewInterval"), " is applicable only for self signed certs")) } - if r.Spec.InitParams != nil { - if (r.Spec.InitParams.PgaAggregateTarget != 0 && r.Spec.InitParams.SgaTarget == 0) || (r.Spec.InitParams.PgaAggregateTarget == 0 && r.Spec.InitParams.SgaTarget != 0) { + if sidb.Spec.InitParams != nil { + if (sidb.Spec.InitParams.PgaAggregateTarget != 0 && sidb.Spec.InitParams.SgaTarget == 0) || (sidb.Spec.InitParams.PgaAggregateTarget == 0 && sidb.Spec.InitParams.SgaTarget != 0) { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("initParams"), - r.Spec.InitParams, "initParams value invalid : Provide values for both pgaAggregateTarget and SgaTarget")) + sidb.Spec.InitParams, "initParams value invalid : Provide values for both pgaAggregateTarget and SgaTarget")) } } @@ -406,41 +416,45 @@ func (r *SingleInstanceDatabase) ValidateCreate(ctx context.Context, obj runtime return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "SingleInstanceDatabase"}, - r.Name, allErrs) + sidb.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *SingleInstanceDatabase) ValidateUpdate(ctx context.Context, oldRuntimeObject, newRuntimeObj runtime.Object) (admission.Warnings, error) { - singleinstancedatabaselog.Info("validate update", "name", r.Name) + new, ok := newRuntimeObj.(*SingleInstanceDatabase) + if !ok { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast newRuntimeObj object to SingleInstanceDatabase")) + } + singleinstancedatabaselog.Info("validate update", "name", new.Name) var allErrs field.ErrorList // check creation validations first - warnings, err := r.ValidateCreate(ctx, newRuntimeObj) + warnings, err := new.ValidateCreate(ctx, newRuntimeObj) if err != nil { return warnings, err } // Validate Deletion - if r.GetDeletionTimestamp() != nil { - warnings, err := r.ValidateDelete(ctx, newRuntimeObj) + if new.GetDeletionTimestamp() != nil { + warnings, err := new.ValidateDelete(ctx, newRuntimeObj) if err != nil { return warnings, err } } // Now check for updation errors - old, ok := oldRuntimeObject.(*SingleInstanceDatabase) - if !ok { - return nil, nil + old, okay := oldRuntimeObject.(*SingleInstanceDatabase) + if !okay { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast oldRuntimeObject object to SingleInstanceDatabase")) } if old.Status.CreatedAs == "clone" { - if r.Spec.Edition != "" && old.Status.Edition != "" && !strings.EqualFold(old.Status.Edition, r.Spec.Edition) { + if new.Spec.Edition != "" && old.Status.Edition != "" && !strings.EqualFold(old.Status.Edition, new.Spec.Edition) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("edition"), "Edition of a cloned singleinstancedatabase cannot be changed post creation")) } - if !strings.EqualFold(old.Status.PrimaryDatabase, r.Spec.PrimaryDatabaseRef) { + if !strings.EqualFold(old.Status.PrimaryDatabase, new.Spec.PrimaryDatabaseRef) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("primaryDatabaseRef"), "Primary database of a cloned singleinstancedatabase cannot be changed post creation")) } @@ -449,36 +463,36 @@ func (r *SingleInstanceDatabase) ValidateUpdate(ctx context.Context, oldRuntimeO if old.Status.Role != dbcommons.ValueUnavailable && old.Status.Role != "PRIMARY" { // Restriciting Patching of secondary databases archiveLog, forceLog, flashBack statusArchiveLog, _ := strconv.ParseBool(old.Status.ArchiveLog) - if r.Spec.ArchiveLog != nil && (statusArchiveLog != *r.Spec.ArchiveLog) { + if new.Spec.ArchiveLog != nil && (statusArchiveLog != *new.Spec.ArchiveLog) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("archiveLog"), "cannot be changed")) } statusFlashBack, _ := strconv.ParseBool(old.Status.FlashBack) - if r.Spec.FlashBack != nil && (statusFlashBack != *r.Spec.FlashBack) { + if new.Spec.FlashBack != nil && (statusFlashBack != *new.Spec.FlashBack) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("flashBack"), "cannot be changed")) } statusForceLogging, _ := strconv.ParseBool(old.Status.ForceLogging) - if r.Spec.ForceLogging != nil && (statusForceLogging != *r.Spec.ForceLogging) { + if new.Spec.ForceLogging != nil && (statusForceLogging != *new.Spec.ForceLogging) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("forceLog"), "cannot be changed")) } // Restriciting Patching of secondary databases InitParams - if r.Spec.InitParams != nil { - if old.Status.InitParams.SgaTarget != r.Spec.InitParams.SgaTarget { + if new.Spec.InitParams != nil { + if old.Status.InitParams.SgaTarget != new.Spec.InitParams.SgaTarget { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("initParams").Child("sgaTarget"), "cannot be changed")) } - if old.Status.InitParams.PgaAggregateTarget != r.Spec.InitParams.PgaAggregateTarget { + if old.Status.InitParams.PgaAggregateTarget != new.Spec.InitParams.PgaAggregateTarget { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("initParams").Child("pgaAggregateTarget"), "cannot be changed")) } - if old.Status.InitParams.CpuCount != r.Spec.InitParams.CpuCount { + if old.Status.InitParams.CpuCount != new.Spec.InitParams.CpuCount { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("initParams").Child("cpuCount"), "cannot be changed")) } - if old.Status.InitParams.Processes != r.Spec.InitParams.Processes { + if old.Status.InitParams.Processes != new.Spec.InitParams.Processes { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("initParams").Child("processes"), "cannot be changed")) } @@ -486,7 +500,7 @@ func (r *SingleInstanceDatabase) ValidateUpdate(ctx context.Context, oldRuntimeO } // if Db is in a dataguard configuration or referred by Standby databases then Restrict enabling Tcps on the Primary DB - if r.Spec.EnableTCPS { + if new.Spec.EnableTCPS { if old.Status.DgBroker != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("enableTCPS"), "cannot enable tcps as database is in a dataguard configuration")) @@ -496,38 +510,38 @@ func (r *SingleInstanceDatabase) ValidateUpdate(ctx context.Context, oldRuntimeO } } - if old.Status.DatafilesCreated == "true" && (old.Status.PrebuiltDB != r.Spec.Image.PrebuiltDB) { + if old.Status.DatafilesCreated == "true" && (old.Status.PrebuiltDB != new.Spec.Image.PrebuiltDB) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("image").Child("prebuiltDB"), "cannot be changed")) } - if r.Spec.Edition != "" && old.Status.Edition != "" && !strings.EqualFold(old.Status.Edition, r.Spec.Edition) { + if new.Spec.Edition != "" && old.Status.Edition != "" && !strings.EqualFold(old.Status.Edition, new.Spec.Edition) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("edition"), "cannot be changed")) } - if old.Status.Charset != "" && !strings.EqualFold(old.Status.Charset, r.Spec.Charset) { + if old.Status.Charset != "" && !strings.EqualFold(old.Status.Charset, new.Spec.Charset) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("charset"), "cannot be changed")) } - if old.Status.Sid != "" && !strings.EqualFold(r.Spec.Sid, old.Status.Sid) { + if old.Status.Sid != "" && !strings.EqualFold(new.Spec.Sid, old.Status.Sid) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("sid"), "cannot be changed")) } - if old.Status.Pdbname != "" && !strings.EqualFold(old.Status.Pdbname, r.Spec.Pdbname) { + if old.Status.Pdbname != "" && !strings.EqualFold(old.Status.Pdbname, new.Spec.Pdbname) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("pdbname"), "cannot be changed")) } if old.Status.CreatedAs == "clone" && - (old.Status.PrimaryDatabase == dbcommons.ValueUnavailable && r.Spec.PrimaryDatabaseRef != "" || - old.Status.PrimaryDatabase != dbcommons.ValueUnavailable && old.Status.PrimaryDatabase != r.Spec.PrimaryDatabaseRef) { + (old.Status.PrimaryDatabase == dbcommons.ValueUnavailable && new.Spec.PrimaryDatabaseRef != "" || + old.Status.PrimaryDatabase != dbcommons.ValueUnavailable && old.Status.PrimaryDatabase != new.Spec.PrimaryDatabaseRef) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("primaryDatabaseRef"), "cannot be changed")) } - if old.Status.OrdsReference != "" && r.Status.Persistence != r.Spec.Persistence { + if old.Status.OrdsReference != "" && new.Status.Persistence != new.Spec.Persistence { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("persistence"), "uninstall ORDS to change Persistence")) } - if old.Status.Replicas != r.Spec.Replicas && old.Status.DgBroker != nil { + if old.Status.Replicas != new.Spec.Replicas && old.Status.DgBroker != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("replicas"), "cannot be updated for a database in a Data Guard configuration")) } @@ -537,22 +551,27 @@ func (r *SingleInstanceDatabase) ValidateUpdate(ctx context.Context, oldRuntimeO } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "SingleInstanceDatabase"}, - r.Name, allErrs) + new.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *SingleInstanceDatabase) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - singleinstancedatabaselog.Info("validate delete", "name", r.Name) + sidb, ok := obj.(*SingleInstanceDatabase) + if !ok { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast obj object to SingleInstanceDatabase")) + } + + singleinstancedatabaselog.Info("validate delete", "name", sidb.Name) var allErrs field.ErrorList - if r.Status.OrdsReference != "" { + if sidb.Status.OrdsReference != "" { allErrs = append(allErrs, - field.Forbidden(field.NewPath("status").Child("ordsReference"), "delete "+r.Status.OrdsReference+" to cleanup this SIDB")) + field.Forbidden(field.NewPath("status").Child("ordsReference"), "delete "+sidb.Status.OrdsReference+" to cleanup this SIDB")) } if len(allErrs) == 0 { return nil, nil } return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "SingleInstanceDatabase"}, - r.Name, allErrs) + sidb.Name, allErrs) } From 31a2f698f3cb02ca41db6ca8d823d9b9127e58a0 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 14 Aug 2025 15:55:48 +0000 Subject: [PATCH 263/414] Added fixes --- .../database/oraclerestart_controller.go | 102 +++++++++--------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 0b5fb27e..4f327191 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -410,45 +410,47 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques err = nilErr return result, err } - if ready, err := checkDaemonSetStatus(ctx, r, oracleRestart); err != nil || !ready { - r.Log.Info("Any of provided ASM Disks are invalid, pls check disk-check daemon set for logs. Fix the asm disk to the valid one and redeploy.") - err = r.cleanupDaemonSet(oracleRestart, ctx) - if err != nil { - result = resultQ - r.Log.Info(err.Error()) - err = nilErr - return result, err - } - addedAsmDisksMap := make(map[string]bool) - for _, disk := range addedAsmDisks { - addedAsmDisksMap[disk] = true - } - for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for index, diskName := range diskBySize.DiskNames { - if _, ok := addedAsmDisksMap[diskName]; ok { - r.Log.Info("Found disk at index", "index", index) - - err = oraclerestartcommon.DelORestartPVC(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) - if err != nil { - return resultQ, err - } - - err = oraclerestartcommon.DelORestartPv(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) - if err != nil { - return resultQ, err + if len(oracleRestart.Spec.StorageClass) == 0 { + if ready, err := checkDaemonSetStatus(ctx, r, oracleRestart); err != nil || !ready { + r.Log.Info("Any of provided ASM Disks are invalid, pls check disk-check daemon set for logs. Fix the asm disk to the valid one and redeploy.") + err = r.cleanupDaemonSet(oracleRestart, ctx) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } + addedAsmDisksMap := make(map[string]bool) + for _, disk := range addedAsmDisks { + addedAsmDisksMap[disk] = true + } + for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for index, diskName := range diskBySize.DiskNames { + if _, ok := addedAsmDisksMap[diskName]; ok { + r.Log.Info("Found disk at index", "index", index) + + err = oraclerestartcommon.DelORestartPVC(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + if err != nil { + return resultQ, err + } + + err = oraclerestartcommon.DelORestartPv(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + if err != nil { + return resultQ, err + } } } } - } - if err = r.SetCurrentSpec(ctx, oracleRestart, req); err != nil { - r.Log.Error(err, "Failed to set current spec annotation") - oracleRestart.Spec.IsFailed = true + if err = r.SetCurrentSpec(ctx, oracleRestart, req); err != nil { + r.Log.Error(err, "Failed to set current spec annotation") + oracleRestart.Spec.IsFailed = true + return resultQ, err + } return resultQ, err + } else { + r.Log.Info("Provided ASM Disks are valid, proceeding further") } - return resultQ, err - } else { - r.Log.Info("Provided ASM Disks are valid, proceeding further") } cmName := oracleRestart.Spec.InstDetails.Name + oracleRestart.Name + "-cmap" configMapDataAutoUpdate, err := r.generateConfigMapAutoUpdate(ctx, oracleRestart, cmName) @@ -682,7 +684,7 @@ func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oracleres latestInstance := &oraclerestartdb.OracleRestart{} err := r.Client.Get(ctx, req.NamespacedName, latestInstance) if err != nil { - r.Log.Error(err, "Failed to fetch the latest version of RAC instance, retrying...") + r.Log.Error(err, "Failed to fetch the latest version of Oracle Restart instance, retrying...") time.Sleep(retryDelay) continue // Retry fetching the latest instance } @@ -712,13 +714,13 @@ func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oracleres time.Sleep(retryDelay) continue // Retry on conflict } - r.Log.Error(err, "Failed to update the RAC DB instance, retrying...") + r.Log.Error(err, "Failed to update the Oracle Restart DB instance, retrying...") time.Sleep(retryDelay) continue // Retry on other errors } // If update was successful, exit the loop - r.Log.Info("Updated RAC instance status successfully", "Instance", oracleRestart.Name) + r.Log.Info("Updated Oracle Restart instance status successfully", "Instance", oracleRestart.Name) break } @@ -1507,7 +1509,7 @@ func (r *OracleRestartReconciler) updateOracleRestartInstTopologyStatus(oracleRe if len(podNames) == 0 || len(nodeDetails) == 0 { oracleRestart.Spec.IsFailed = true - return podNames, nodeDetails, errors.New("error occurred while collecting RAC pod or node details") + return podNames, nodeDetails, errors.New("error occurred while collecting Oracle Restart pod or node details") } else { oracleRestart.Spec.IsFailed = false } @@ -1547,14 +1549,14 @@ func (r *OracleRestartReconciler) validateoraclerestartdb(oracleRestart *oracler const retryDelay = 2 * time.Second oraclerestartcommon.UpdateoraclerestartdbStatusData(oracleRestart, ctx, req, podNames, r.kubeClient, r.kubeConfig, r.Log, nodeDetails) // Log the start of the status update process - r.Log.Info("Updating RAC instance status with validateoraclerestartdb", "Instance", oracleRestart.Name) + r.Log.Info("Updating Oracle Restart instance status with validateoraclerestartdb", "Instance", oracleRestart.Name) for attempt := 0; attempt < maxRetries; attempt++ { // // Fetch the latest version of the object latestInstance := &oraclerestartdb.OracleRestart{} err := r.Client.Get(ctx, req.NamespacedName, latestInstance) if err != nil { - r.Log.Error(err, "Failed to fetch the latest version of RAC instance") + r.Log.Error(err, "Failed to fetch the latest version of Oracle Restart instance") return orestartSfSet, orestartPod, err // Return the error if fetching the latest version fails } @@ -1581,18 +1583,18 @@ func (r *OracleRestartReconciler) validateoraclerestartdb(oracleRestart *oracler // Retry } // For other errors, log and continue the retry loop - r.Log.Error(err, "Failed to update the RAC DB instance, retrying") + r.Log.Error(err, "Failed to update the Oracle Restart DB instance, retrying") continue } // If update was successful, exit the loop - r.Log.Info("Updated RAC instance status with validateoraclerestartdb", "Instance", oracleRestart.Name) + r.Log.Info("Updated Oracle Restart instance status with validateoraclerestartdb", "Instance", oracleRestart.Name) return orestartSfSet, orestartPod, nil } // If all retries fail, return an error - return orestartSfSet, orestartPod, fmt.Errorf("failed to update RAC DB Status after %d attempts", maxRetries) + return orestartSfSet, orestartPod, fmt.Errorf("failed to update Oracle Restart DB Status after %d attempts", maxRetries) } // ======= Function to validate Shard @@ -1624,12 +1626,12 @@ func (r *OracleRestartReconciler) validateOracleRestartInst(oracleRestart *oracl msg := "" if notReadyPod != nil { // Log the name of the first not ready pod - msg = "unable to validate RAC pod. The pod not ready is: " + notReadyPod.Name + msg = "unable to validate Oracle Restart pod. The pod not ready is: " + notReadyPod.Name oraclerestartcommon.LogMessages("INFO", msg, nil, oracleRestart, r.Log) return orestartSfSet, orestartPod, fmt.Errorf(msg) } else { // Handle the case where no pods were found at all - msg = "unable to validate RAC pod. No pods matching the criteria were found" + msg = "unable to validate Oracle Restart pod. No pods matching the criteria were found" oraclerestartcommon.LogMessages("INFO", msg, nil, oracleRestart, r.Log) return orestartSfSet, orestartPod, fmt.Errorf(msg) } @@ -1689,7 +1691,7 @@ func (r *OracleRestartReconciler) updateOracleRestartInstStatus( latestInstance := &oraclerestartdb.OracleRestart{} err := r.Client.Get(ctx, req.NamespacedName, latestInstance) if err != nil { - r.Log.Error(err, "Failed to fetch the latest version of RAC instance") + r.Log.Error(err, "Failed to fetch the latest version of Oracle Restart instance") lastErr = err continue // Continue to retry } @@ -3117,11 +3119,11 @@ func (r *OracleRestartReconciler) cleanupOracleRestartInstance(req ctrl.Request, if strings.ToUpper(oraRacSatus.Name) == (strings.ToUpper(OraRestartSpex.Name) + "-0") { if !utils.CheckStatusFlag(oraRacSatus.NodeDetails.IsDelete) { oraRacSatus.NodeDetails.IsDelete = "true" - log.Info("Setting RAC status instance " + oraRacSatus.Name + " delete flag true") + log.Info("Setting Oracle Restart status instance " + oraRacSatus.Name + " delete flag true") err = r.deleteOracleRestartInst(OraRestartSpex, req, ctx, oracleRestart) oraRacSatus.NodeDetails.IsDelete = "false" if err != nil { - log.Info("Error occurred RAC instance " + oraRacSatus.Name + " deletion") + log.Info("Error occurred Oracle Restart instance " + oraRacSatus.Name + " deletion") return 0, err // return value should be adjusted according to the function signature } } @@ -3364,11 +3366,11 @@ func (r *OracleRestartReconciler) SetCurrentSpec(ctx context.Context, oracleRest r.Log.Info("Conflict detected while updating annotations, retrying...") return fmt.Errorf("conflict occurred while updating annotations: %w", err) } - r.Log.Error(err, "Failed to update RAC instance annotations") - return fmt.Errorf("failed to update RAC instance annotations: %w", err) + r.Log.Error(err, "Failed to update Oracle Restart instance annotations") + return fmt.Errorf("failed to update Oracle Restart instance annotations: %w", err) } - r.Log.Info("RAC Object annotations updated with current spec annotation") + r.Log.Info("Oracle Restart Object annotations updated with current spec annotation") return nil } From 09af2d68b0d8a06c30dfe76648e60a77edd156ca Mon Sep 17 00:00:00 2001 From: racpack Date: Thu, 14 Aug 2025 20:02:45 +0000 Subject: [PATCH 264/414] Psaini sharding branch --- .gitignore | 1 + .../v1alpha1/shardingdatabase_webhook.go | 264 - apis/database/v4/shardingdatabase_types.go | 69 +- apis/database/v4/shardingdatabase_webhook.go | 186 +- apis/database/v4/zz_generated.deepcopy.go | 92 + commons/sharding/catalog.go | 38 + commons/sharding/gsm.go | 23 + commons/sharding/scommon.go | 49 +- commons/sharding/shard.go | 44 + ...database.oracle.com_shardingdatabases.yaml | 70 +- ...vability.oracle.com_databaseobservers.yaml | 18 +- config/crd/kustomization.yaml | 16 + config/manager/kustomization.yaml | 2 +- config/webhook/manifests.yaml | 41 - .../database/shardingdatabase_controller.go | 193 +- oracle-database-operator.yaml | 10993 ++++++++-------- 16 files changed, 6239 insertions(+), 5860 deletions(-) diff --git a/.gitignore b/.gitignore index 8ac4ec17..4cfc4ddc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ operator.tgz cover.out bin +bin/* testbin/* onpremtest/* ords/*zip diff --git a/apis/database/v1alpha1/shardingdatabase_webhook.go b/apis/database/v1alpha1/shardingdatabase_webhook.go index 158d4ad0..f5f25ecc 100644 --- a/apis/database/v1alpha1/shardingdatabase_webhook.go +++ b/apis/database/v1alpha1/shardingdatabase_webhook.go @@ -39,17 +39,9 @@ package v1alpha1 import ( - "context" - "strings" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -58,261 +50,5 @@ var shardingdatabaselog = logf.Log.WithName("shardingdatabase-resource") func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(r). - WithDefaulter(r). - WithValidator(r). Complete() } - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v1alpha1-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v1alpha1,name=mshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 - -var _ webhook.CustomDefaulter = &ShardingDatabase{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *ShardingDatabase) Default(ctx context.Context, obj runtime.Object) error { - shardingdatabaselog.Info("default", "name", r.Name) - - // TODO(user): fill in your defaulting logic. - if r.Spec.GsmDevMode != "" { - r.Spec.GsmDevMode = "dev" - } - - if r.Spec.IsTdeWallet == "" { - r.Spec.IsTdeWallet = "disable" - } - for pindex := range r.Spec.Shard { - if strings.ToLower(r.Spec.Shard[pindex].IsDelete) == "" { - r.Spec.Shard[pindex].IsDelete = "disable" - } - } - - return nil -} - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v1alpha1-shardingdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=shardingdatabases,versions=v1alpha1,name=vshardingdatabasev1alpha1.kb.io,admissionReviewVersions=v1 - -var _ webhook.CustomValidator = &ShardingDatabase{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - shardingdatabaselog.Info("validate create", "name", r.Name) - - // TODO(user): fill in your validation logic upon object creation. - // Check Secret configuration - var validationErr field.ErrorList - var validationErrs1 field.ErrorList - - //namespaces := db.GetWatchNamespaces() - //_, containsNamespace := namespaces[r.Namespace] - // Check if the allowed namespaces maps contains the required namespace - // if len(namespaces) != 0 && !containsNamespace { - // validationErr = append(validationErr, - // field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, - // "Oracle database operator doesn't watch over this namespace")) - //} - - if r.Spec.DbSecret == nil { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret"), r.Spec.DbSecret, - "DbSecret cannot be set to nil")) - } else { - if len(r.Spec.DbSecret.Name) == 0 { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("Name"), r.Spec.DbSecret.Name, - "Secret name cannot be set empty")) - } - if len(r.Spec.DbSecret.PwdFileName) == 0 { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileName"), r.Spec.DbSecret.PwdFileName, - "Password file name cannot be set empty")) - } - if strings.ToLower(r.Spec.DbSecret.EncryptionType) != "base64" { - if strings.ToLower(r.Spec.DbSecret.KeyFileName) == "" { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileName"), r.Spec.DbSecret.KeyFileName, - "Key file name cannot be empty")) - } - } - - /** - if len(r.Spec.DbSecret.PwdFileMountLocation) == 0 { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileMountLocation"), r.Spec.DbSecret.PwdFileMountLocation, - "Password file mount location cannot be empty")) - } - - if len(r.Spec.DbSecret.KeyFileMountLocation) == 0 { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileMountLocation"), r.Spec.DbSecret.KeyFileMountLocation, - "KeyFileMountLocation file mount location cannot be empty")) - } - **/ - } - - if r.Spec.IsTdeWallet == "enable" { - if (len(r.Spec.FssStorageClass) == 0) && (len(r.Spec.TdeWalletPvc) == 0) { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("FssStorageClass"), r.Spec.FssStorageClass, - "FssStorageClass or TdeWalletPvc cannot be set empty if isTdeWallet set to true")) - - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("TdeWalletPvc"), r.Spec.TdeWalletPvc, - "FssStorageClass or TdeWalletPvc cannot be set empty if isTdeWallet set to true")) - } - } - - if r.Spec.IsTdeWallet != "" { - if (strings.ToLower(strings.TrimSpace(r.Spec.IsTdeWallet)) != "enable") && (strings.ToLower(strings.TrimSpace(r.Spec.IsTdeWallet)) != "disable") { - validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("isTdeWallet"), r.Spec.IsTdeWallet, - "isTdeWallet can be set to only \"enable\" or \"disable\"")) - } - } - - validationErrs1 = r.validateShardIsDelete() - if validationErrs1 != nil { - validationErr = append(validationErr, validationErrs1...) - } - - validationErrs1 = r.validateFreeEdition() - if validationErrs1 != nil { - validationErr = append(validationErr, validationErrs1...) - } - - validationErrs1 = r.validateCatalogName() - if validationErrs1 != nil { - validationErr = append(validationErr, validationErrs1...) - } - - validationErrs1 = r.validateShardName() - if validationErrs1 != nil { - validationErr = append(validationErr, validationErrs1...) - } - - // TODO(user): fill in your validation logic upon object creation. - if len(validationErr) == 0 { - return nil, nil - } - - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "ShardingDatabase"}, - r.Name, validationErr) -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { - shardingdatabaselog.Info("validate update", "name", r.Name) - - // TODO(user): fill in your validation logic upon object update. - return nil, nil -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *ShardingDatabase) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - shardingdatabaselog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. - return nil, nil -} - -// ###### Vlaidation Block ################# - -func (r *ShardingDatabase) validateShardIsDelete() field.ErrorList { - - var validationErrs field.ErrorList - - for pindex := range r.Spec.Shard { - if (strings.ToLower(strings.TrimSpace(r.Spec.Shard[pindex].IsDelete)) != "enable") && (strings.ToLower(strings.TrimSpace(r.Spec.Shard[pindex].IsDelete)) != "disable") && (strings.ToLower(strings.TrimSpace(r.Spec.Shard[pindex].IsDelete)) != "failed") { - validationErrs = append(validationErrs, - field.Invalid(field.NewPath("spec").Child("shard").Child("isDelete"), r.Spec.Shard[pindex].IsDelete, - "r.Spec.Shard[pindex].IsDelete can be set to only enable|disable|failed")) - } - } - - if len(validationErrs) > 0 { - return validationErrs - } - return nil -} - -func (r *ShardingDatabase) validateFreeEdition() field.ErrorList { - - var validationErrs field.ErrorList - if strings.ToLower(r.Spec.DbEdition) == "free" { - // Shard Spec Checks - for i := 0; i < len(r.Spec.Shard); i++ { - for index, variable := range r.Spec.Shard[i].EnvVars { - if variable.Name == "ORACLE_SID" { - if strings.ToLower(variable.Value) != "free" { - validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("shard").Child("EnvVars"), r.Spec.Shard[i].EnvVars[index].Name, - "r.Spec.Shard[i].EnvVars[index].Name ORACLE_SID value can only be set to free")) - } - } - if variable.Name == "ORACLE_PDB" { - if strings.ToLower(variable.Value) != "freepdb" { - validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("shard").Child("EnvVars"), r.Spec.Shard[i].EnvVars[index].Name, - "r.Spec.Shard[i].EnvVars[index].Name ORACLE_PDB value can only be set to freepdb")) - } - } - } - } - // Catalog Spec Checks - for i := 0; i < len(r.Spec.Catalog); i++ { - for index, variable := range r.Spec.Catalog[i].EnvVars { - if variable.Name == "ORACLE_SID" { - if strings.ToLower(variable.Value) != "free" { - validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("catalog").Child("EnvVars"), r.Spec.Catalog[i].EnvVars[index].Name, - "r.Spec.Catalog[i].EnvVars[index].Name ORACLE_SID value can only be set to free")) - } - } - if variable.Name == "ORACLE_PDB" { - if strings.ToLower(variable.Value) != "freepdb" { - validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("catalog").Child("EnvVars"), r.Spec.Catalog[i].EnvVars[index].Name, - "r.Spec.Catalog[i].EnvVars[index].Name ORACLE_PDB value can only be set to freepdb")) - } - } - } - } - } - - if len(validationErrs) > 0 { - return validationErrs - } - return nil -} - -func (r *ShardingDatabase) validateShardName() field.ErrorList { - var validationErrs field.ErrorList - - for pindex := range r.Spec.Shard { - if len(r.Spec.Shard[pindex].Name) > 9 { - validationErrs = append(validationErrs, - field.Invalid(field.NewPath("spec").Child("shard").Child("Name"), r.Spec.Shard[pindex].Name, - "Shard Name cannot be greater than 9 characters.")) - } - } - - if len(validationErrs) > 0 { - return validationErrs - } - return nil -} - -func (r *ShardingDatabase) validateCatalogName() field.ErrorList { - var validationErrs field.ErrorList - - for pindex := range r.Spec.Catalog { - if len(r.Spec.Catalog[pindex].Name) > 9 { - validationErrs = append(validationErrs, - field.Invalid(field.NewPath("spec").Child("catalog").Child("Name"), r.Spec.Catalog[pindex].Name, - "Catalog Name cannot be greater than 9 characters.")) - } - } - - if len(validationErrs) > 0 { - return validationErrs - } - return nil -} diff --git a/apis/database/v4/shardingdatabase_types.go b/apis/database/v4/shardingdatabase_types.go index cc01b24d..f42b9bbe 100644 --- a/apis/database/v4/shardingdatabase_types.go +++ b/apis/database/v4/shardingdatabase_types.go @@ -58,7 +58,7 @@ import ( type ShardingDatabaseSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Shard []ShardSpec `json:"shard"` + Shard []ShardSpec `json:"shard,omitempty"` Catalog []CatalogSpec `json:"catalog"` // The catalogSpes accept all the catalog parameters Gsm []GsmSpec `json:"gsm"` // The GsmSpec will accept all the Gsm parameter StorageClass string `json:"storageClass,omitempty"` // Optional Accept storage class name @@ -95,6 +95,8 @@ type ShardingDatabaseSpec struct { TdeWalletPvcMountLocation string `json:"tdeWalletPvcMountLocation,omitempty"` DbEdition string `json:"dbEdition,omitempty"` TopicId string `json:"topicId,omitempty"` + SrvAccountName string `json:"serviceAccountName,omitempty"` + ShardInfo []ShardingDetails `json:"shardInfo,omitempty"` } // To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 @@ -157,9 +159,9 @@ type ShardingDatabase struct { Status ShardingDatabaseStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true - +// +kubebuilder:object:root=true // ShardingDatabaseList contains a list of ShardingDatabase +// +kubebuilder:storageversion type ShardingDatabaseList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` @@ -167,7 +169,6 @@ type ShardingDatabaseList struct { } // ShardSpec is a specification of Shards for an application deployment. -// +k8s:openapi-gen=true type ShardSpec struct { Name string `json:"name"` // Shard name that will be used deploy StatefulSet StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` // Optional Shard Storage Size @@ -185,26 +186,26 @@ type ShardSpec struct { ShardGroup string `json:"shardGroup,omitempty"` ShardRegion string `json:"shardRegion,omitempty"` DeployAs string `json:"deployAs,omitempty"` + ShardConfigData *ConfigMapData `json:"shardConfigData,omitempty"` } // CatalogSpec defines the desired state of CatalogSpec -// +k8s:openapi-gen=true type CatalogSpec struct { - Name string `json:"name"` // Catalog name that will be used deploy StatefulSet - StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` // Optional Catalog Storage Size and This parameter will not be used if you use PvcName - EnvVars []EnvironmentVariable `json:"envVars,omitempty"` //Optional Env variables for Catalog - Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` // Optional resource requirement for the container. - PvcName string `json:"pvcName,omitempty"` - Label string `json:"label,omitempty"` - IsDelete string `json:"isDelete,omitempty"` - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` - PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` - ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + Name string `json:"name"` // Catalog name that will be used deploy StatefulSet + StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` // Optional Catalog Storage Size and This parameter will not be used if you use PvcName + EnvVars []EnvironmentVariable `json:"envVars,omitempty"` //Optional Env variables for Catalog + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` // Optional resource requirement for the container. + PvcName string `json:"pvcName,omitempty"` + Label string `json:"label,omitempty"` + IsDelete string `json:"isDelete,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + PvAnnotations map[string]string `json:"pvAnnotations,omitempty"` + PvMatchLabels map[string]string `json:"pvMatchLabels,omitempty"` + ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + CatalogConfigData *ConfigMapData `json:"catalogConfigData,omitempty"` } // GsmSpec defines the desired state of GsmSpec -// +k8s:openapi-gen=true type GsmSpec struct { Name string `json:"name"` // Gsm name that will be used deploy StatefulSet @@ -221,10 +222,10 @@ type GsmSpec struct { ImagePulllPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` Region string `json:"region,omitempty"` DirectorName string `json:"directorName,omitempty"` + GsmConfigData *ConfigMapData `json:"gsmConfigData,omitempty"` } // ShardGroupSpec Specification - type GsmShardGroupSpec struct { Name string `json:"name"` // Name of the shardgroup. Region string `json:"region,omitempty"` @@ -283,17 +284,17 @@ type SecretDetails struct { KeyFileMountLocation string `json:"keyFileMountLocation,omitempty"` KeySecretName string `json:"keySecretName,omitempty"` EncryptionType string `json:"encryptionType,omitempty"` + TdeKeyFileName string `json:"tdeKeyFileName,omitempty"` // Name of the key. + TdePwdFileName string `json:"tdePwdFileName"` } // EnvironmentVariable represents a named variable accessible for containers. -// +k8s:openapi-gen=true type EnvironmentVariable struct { Name string `json:"name"` // Name of the variable. Must be a C_IDENTIFIER. Value string `json:"value"` // Value of the variable, as defined in Kubernetes core API. } // PortMapping is a specification of port mapping for an application deployment. -// +k8s:openapi-gen=true type PortMapping struct { Port int32 `json:"port"` // Port that will be exposed on the service. TargetPort int32 `json:"targetPort"` // Docker image port for the application. @@ -308,6 +309,34 @@ const ( ShardingDelLabelFalseValue SfsetLabel = "false" ) +type ConfigMapData struct { + Name string `json:"name,omitempty"` + MountPath string `json:"mountPath,omitempty"` +} + +// Shard structures based on managed Replicas +type ShardingDetails struct { + ShardPreFixName string `json:"shardPreFixName"` + Shape string `json:"shape,omitempty"` + Replicas int32 `json:"replicas,omitempty"` + StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` + ShardGroupDetails *ShardGroup `json:"shardGroupDetails,omitempty"` + ShardSpaceDetails *ShardSpace `json:"shardSpaceDetails,omitempty"` +} +type ShardGroup struct { + ShardGroupName string `json:"shardGroupName,omitempty"` + Region string `json:"region,omitempty"` + RepFactor int `json:"repFactor,omitempty"` + ShardSpace string `json:"ShardSpace,omitempty"` + DeployAs string `json:"deployAs,omitempty"` + IsDelete string `json:"isDelete,omitempty"` +} +type ShardSpace struct { + ShardSpaceName string `json:"shardSpaceName,omitempty"` + Chunks int `json:"Chnuks,omitempty"` + ProtectMode string `json:"protectMode,omitempty"` //Possible Values MAXPROTECTION, MAXAVAILABILITY,MAXPERFORMANCE +} + type ShardStatusMapKeys string const ( diff --git a/apis/database/v4/shardingdatabase_webhook.go b/apis/database/v4/shardingdatabase_webhook.go index 2115b57b..56922a7b 100644 --- a/apis/database/v4/shardingdatabase_webhook.go +++ b/apis/database/v4/shardingdatabase_webhook.go @@ -40,8 +40,11 @@ package v4 import ( "context" + "fmt" + "strconv" "strings" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -52,41 +55,74 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) +var totalShard int32 = 0 + // log is for logging in this package. var shardingdatabaselog = logf.Log.WithName("shardingdatabase-resource") func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). - For(r). - WithDefaulter(r). + For(&ShardingDatabase{}). + WithDefaulter(r). WithValidator(r). Complete() } +var _ webhook.CustomDefaulter = &ShardingDatabase{} + + // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! //+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v4,name=mshardingdatabasev4.kb.io,admissionReviewVersions=v1 -var _ webhook.CustomDefaulter = &ShardingDatabase{} - // Default implements webhook.Defaulter so a webhook will be registered for the type func (r *ShardingDatabase) Default(ctx context.Context, obj runtime.Object) error { - shardingdatabaselog.Info("default", "name", r.Name) + + cr , ok := obj.(*ShardingDatabase) + + if !ok { + return fmt.Errorf("xpected obj.*ShardingDatabase but got %T", obj) + } + + shardingdatabaselog.Info("default", "name", cr.Name) + + var replicas int32 + // TODO(user): fill in your defaulting logic. - if r.Spec.GsmDevMode != "" { - r.Spec.GsmDevMode = "dev" + if cr.Spec.GsmDevMode != "" { + cr.Spec.GsmDevMode = "dev" } - if r.Spec.IsTdeWallet == "" { - r.Spec.IsTdeWallet = "disable" + if cr.Spec.IsTdeWallet == "" { + cr.Spec.IsTdeWallet = "disable" } - for pindex := range r.Spec.Shard { - if strings.ToLower(r.Spec.Shard[pindex].IsDelete) == "" { - r.Spec.Shard[pindex].IsDelete = "disable" + for pindex := range cr.Spec.Shard { + if strings.ToLower(cr.Spec.Shard[pindex].IsDelete) == "" { + cr.Spec.Shard[pindex].IsDelete = "disable" } } + for pindex := range cr.Spec.ShardInfo { + if strings.ToLower(cr.Spec.ShardInfo[pindex].ShardGroupDetails.IsDelete) == "" { + cr.Spec.ShardInfo[pindex].ShardGroupDetails.IsDelete = "disable" + } + } + + totalShard = 0 + for pindex := range cr.Spec.ShardInfo { + replicas = 2 + if cr.Spec.ShardInfo[pindex].Replicas != 0 { + replicas = cr.Spec.ShardInfo[pindex].Replicas + } + totalShard = totalShard + replicas + } + + if totalShard > 0 { + cr.Spec.Shard = make([]ShardSpec, totalShard) + cr.initShardsSpec() + } + return nil } @@ -103,6 +139,16 @@ func (r *ShardingDatabase) ValidateCreate(ctx context.Context, obj runtime.Objec // Check Secret configuration var validationErr field.ErrorList var validationErrs1 field.ErrorList + cr , ok := obj.(*ShardingDatabase) + + if !ok { +// return fmt.Errorf("xpected obj.*ShardingDatabase but got %T", obj) + validationErr = append(validationErr,field.Invalid(field.NewPath("obj"),"obj","Expected obj.*ShardingDatabase.")) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "ShardingDatabase"}, + cr.Name, validationErr) + } + //namespaces := db.GetWatchNamespaces() //_, containsNamespace := namespaces[r.Namespace] @@ -113,25 +159,25 @@ func (r *ShardingDatabase) ValidateCreate(ctx context.Context, obj runtime.Objec // "Oracle database operator doesn't watch over this namespace")) //} - if r.Spec.DbSecret == nil { + if cr.Spec.DbSecret == nil { validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret"), r.Spec.DbSecret, + field.Invalid(field.NewPath("spec").Child("DbSecret"), cr.Spec.DbSecret, "DbSecret cannot be set to nil")) } else { - if len(r.Spec.DbSecret.Name) == 0 { + if len(cr.Spec.DbSecret.Name) == 0 { validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("Name"), r.Spec.DbSecret.Name, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("Name"), cr.Spec.DbSecret.Name, "Secret name cannot be set empty")) } - if len(r.Spec.DbSecret.PwdFileName) == 0 { + if len(cr.Spec.DbSecret.PwdFileName) == 0 { validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileName"), r.Spec.DbSecret.PwdFileName, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("PwdFileName"), cr.Spec.DbSecret.PwdFileName, "Password file name cannot be set empty")) } - if strings.ToLower(r.Spec.DbSecret.EncryptionType) != "base64" { - if strings.ToLower(r.Spec.DbSecret.KeyFileName) == "" { + if strings.ToLower(cr.Spec.DbSecret.EncryptionType) != "base64" { + if strings.ToLower(cr.Spec.DbSecret.KeyFileName) == "" { validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileName"), r.Spec.DbSecret.KeyFileName, + field.Invalid(field.NewPath("spec").Child("DbSecret").Child("KeyFileName"), cr.Spec.DbSecret.KeyFileName, "Key file name cannot be empty")) } } @@ -151,46 +197,55 @@ func (r *ShardingDatabase) ValidateCreate(ctx context.Context, obj runtime.Objec **/ } - if r.Spec.IsTdeWallet == "enable" { - if (len(r.Spec.FssStorageClass) == 0) && (len(r.Spec.TdeWalletPvc) == 0) { + if cr.Spec.IsTdeWallet == "enable" { + if (len(cr.Spec.FssStorageClass) == 0) && (len(cr.Spec.TdeWalletPvc) == 0) { validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("FssStorageClass"), r.Spec.FssStorageClass, + field.Invalid(field.NewPath("spec").Child("FssStorageClass"), cr.Spec.FssStorageClass, "FssStorageClass or TdeWalletPvc cannot be set empty if isTdeWallet set to true")) validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("TdeWalletPvc"), r.Spec.TdeWalletPvc, + field.Invalid(field.NewPath("spec").Child("TdeWalletPvc"), cr.Spec.TdeWalletPvc, "FssStorageClass or TdeWalletPvc cannot be set empty if isTdeWallet set to true")) } } - if r.Spec.IsTdeWallet != "" { - if (strings.ToLower(strings.TrimSpace(r.Spec.IsTdeWallet)) != "enable") && (strings.ToLower(strings.TrimSpace(r.Spec.IsTdeWallet)) != "disable") { + if cr.Spec.IsTdeWallet != "" { + if (strings.ToLower(strings.TrimSpace(cr.Spec.IsTdeWallet)) != "enable") && (strings.ToLower(strings.TrimSpace(cr.Spec.IsTdeWallet)) != "disable") { validationErr = append(validationErr, - field.Invalid(field.NewPath("spec").Child("isTdeWallet"), r.Spec.IsTdeWallet, + field.Invalid(field.NewPath("spec").Child("isTdeWallet"), cr.Spec.IsTdeWallet, "isTdeWallet can be set to only \"enable\" or \"disable\"")) } } - validationErrs1 = r.validateShardIsDelete() + validationErrs1 = cr.validateShardIsDelete() if validationErrs1 != nil { validationErr = append(validationErr, validationErrs1...) } - validationErrs1 = r.validateFreeEdition() + validationErrs1 = cr.validateFreeEdition() if validationErrs1 != nil { validationErr = append(validationErr, validationErrs1...) } - validationErrs1 = r.validateCatalogName() + validationErrs1 = cr.validateCatalogName() if validationErrs1 != nil { validationErr = append(validationErr, validationErrs1...) } - validationErrs1 = r.validateShardName() + // validationErrs1 = r.validateShardName() + // if validationErrs1 != nil { + // validationErr = append(validationErr, validationErrs1...) + // } + + validationErrs1 = cr.validateShardInfo() if validationErrs1 != nil { validationErr = append(validationErr, validationErrs1...) } + fmt.Println("TotalShard=[" + strconv.Itoa(int(totalShard)) + "]") + fmt.Println("Original shard buffer len=[" + strconv.Itoa(len(cr.Spec.Shard)) + "]") + fmt.Println("Original shard buffer capacity=[" + strconv.Itoa(cap(cr.Spec.Shard)) + "]") + // TODO(user): fill in your validation logic upon object creation. if len(validationErr) == 0 { return nil, nil @@ -198,7 +253,7 @@ func (r *ShardingDatabase) ValidateCreate(ctx context.Context, obj runtime.Objec return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "ShardingDatabase"}, - r.Name, validationErr) + cr.Name, validationErr) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type @@ -316,3 +371,68 @@ func (r *ShardingDatabase) validateCatalogName() field.ErrorList { } return nil } + +func (r *ShardingDatabase) validateShardInfo() field.ErrorList { + var validationErrs field.ErrorList + var replicas int32 + + totalShard = 0 + for pindex := range r.Spec.ShardInfo { + replicas = 2 + if r.Spec.ShardInfo[pindex].Replicas != 0 { + replicas = r.Spec.ShardInfo[pindex].Replicas + } else { + r.Spec.ShardInfo[pindex].Replicas = replicas + } + + totalShard = totalShard + replicas + if r.Spec.ShardInfo[pindex].ShardGroupDetails != nil { + if r.Spec.ShardInfo[pindex].ShardGroupDetails.DeployAs == "" { + r.Spec.ShardInfo[pindex].ShardGroupDetails.DeployAs = "primary" + } + if (r.Spec.ShardInfo[pindex].ShardGroupDetails.DeployAs == "primary") && (replicas > 1) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("shardInfo").Child("replicas"), r.Spec.ShardInfo[pindex].Replicas, + "Primary++ Shard Group can have only one replicas")) + } + } else { + if replicas > 1 { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("shardInfo").Child("replicas"), r.Spec.ShardInfo[pindex].Replicas, + "Primary!! Shard Group can have only one replicas")) + } + } + } + + if len(validationErrs) > 0 { + return validationErrs + } + return nil +} + +func (r *ShardingDatabase) initShardsSpec() error { + var shardIndex int + + shardIndex = 0 + for pindex := range r.Spec.ShardInfo { + for i := 0; i < int(r.Spec.ShardInfo[pindex].Replicas); i++ { + r.Spec.Shard[shardIndex].Name = r.Spec.ShardInfo[pindex].ShardPreFixName + strconv.Itoa(shardIndex+1) + r.Spec.Shard[shardIndex].StorageSizeInGb = r.Spec.ShardInfo[pindex].StorageSizeInGb + r.Spec.Shard[shardIndex].ShardGroup = r.Spec.ShardInfo[pindex].ShardGroupDetails.ShardGroupName + r.Spec.Shard[shardIndex].ShardRegion = r.Spec.ShardInfo[pindex].ShardGroupDetails.Region + r.Spec.Shard[shardIndex].DeployAs = r.Spec.ShardInfo[pindex].ShardGroupDetails.DeployAs + r.Spec.Shard[shardIndex].IsDelete = r.Spec.ShardInfo[pindex].ShardGroupDetails.IsDelete + r.Spec.Shard[shardIndex].ImagePulllPolicy = new(corev1.PullPolicy) + *(r.Spec.Shard[shardIndex].ImagePulllPolicy) = corev1.PullPolicy("Always") + fmt.Println("ShardName=[" + r.Spec.Shard[shardIndex].Name + "]") + if r.Spec.ShardInfo[pindex].ShardSpaceDetails != nil { + r.Spec.Shard[shardIndex].ShardSpace = r.Spec.ShardInfo[pindex].ShardSpaceDetails.ShardSpaceName + } + + shardIndex++ + } + + } + + return nil +} diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 77e05585..3b94bb70 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -1066,6 +1066,11 @@ func (in *CatalogSpec) DeepCopyInto(out *CatalogSpec) { *out = new(corev1.PullPolicy) **out = **in } + if in.CatalogConfigData != nil { + in, out := &in.CatalogConfigData, &out.CatalogConfigData + *out = new(ConfigMapData) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CatalogSpec. @@ -1093,6 +1098,21 @@ func (in *CertificateSecret) DeepCopy() *CertificateSecret { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapData) DeepCopyInto(out *ConfigMapData) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapData. +func (in *ConfigMapData) DeepCopy() *ConfigMapData { + if in == nil { + return nil + } + out := new(ConfigMapData) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ConnectionStringProfile) DeepCopyInto(out *ConnectionStringProfile) { *out = *in @@ -2014,6 +2034,11 @@ func (in *GsmSpec) DeepCopyInto(out *GsmSpec) { *out = new(corev1.PullPolicy) **out = **in } + if in.GsmConfigData != nil { + in, out := &in.GsmConfigData, &out.GsmConfigData + *out = new(ConfigMapData) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmSpec. @@ -4218,6 +4243,36 @@ func (in *ServiceSpec) DeepCopy() *ServiceSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardGroup) DeepCopyInto(out *ShardGroup) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardGroup. +func (in *ShardGroup) DeepCopy() *ShardGroup { + if in == nil { + return nil + } + out := new(ShardGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardSpace) DeepCopyInto(out *ShardSpace) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardSpace. +func (in *ShardSpace) DeepCopy() *ShardSpace { + if in == nil { + return nil + } + out := new(ShardSpace) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ShardSpec) DeepCopyInto(out *ShardSpec) { *out = *in @@ -4257,6 +4312,11 @@ func (in *ShardSpec) DeepCopyInto(out *ShardSpec) { *out = new(corev1.PullPolicy) **out = **in } + if in.ShardConfigData != nil { + in, out := &in.ShardConfigData, &out.ShardConfigData + *out = new(ConfigMapData) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardSpec. @@ -4382,6 +4442,13 @@ func (in *ShardingDatabaseSpec) DeepCopyInto(out *ShardingDatabaseSpec) { *out = new(SecretDetails) **out = **in } + if in.ShardInfo != nil { + in, out := &in.ShardInfo, &out.ShardInfo + *out = make([]ShardingDetails, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardingDatabaseSpec. @@ -4431,6 +4498,31 @@ func (in *ShardingDatabaseStatus) DeepCopy() *ShardingDatabaseStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardingDetails) DeepCopyInto(out *ShardingDetails) { + *out = *in + if in.ShardGroupDetails != nil { + in, out := &in.ShardGroupDetails, &out.ShardGroupDetails + *out = new(ShardGroup) + **out = **in + } + if in.ShardSpaceDetails != nil { + in, out := &in.ShardSpaceDetails, &out.ShardSpaceDetails + *out = new(ShardSpace) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardingDetails. +func (in *ShardingDetails) DeepCopy() *ShardingDetails { + if in == nil { + return nil + } + out := new(ShardingDetails) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SingleInstanceDatabase) DeepCopyInto(out *SingleInstanceDatabase) { *out = *in diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index 646c89b8..db7ae50f 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -50,6 +50,8 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -137,11 +139,14 @@ func buildPodSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpe group := oraFsGroup spec := &corev1.PodSpec{ SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: BoolPointer(true), RunAsUser: &user, + RunAsGroup: &group, FSGroup: &group, }, Containers: buildContainerSpecForCatalog(instance, OraCatalogSpex), Volumes: buildVolumeSpecForCatalog(instance, OraCatalogSpex), + ServiceAccountName: instance.Spec.SrvAccountName, } if (instance.Spec.IsDownloadScripts) && (instance.Spec.ScriptsLocation != "") { @@ -185,6 +190,10 @@ func buildVolumeSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalog }, } + if OraCatalogSpex.CatalogConfigData != nil && len(OraCatalogSpex.CatalogConfigData.Name) != 0 { + result = append(result, corev1.Volume{Name: OraCatalogSpex.Name + "-oradata-configdata", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OraCatalogSpex.CatalogConfigData.Name}}}}) + } + if len(OraCatalogSpex.PvcName) != 0 { result = append(result, corev1.Volume{Name: OraCatalogSpex.Name + "oradata-vol4", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OraCatalogSpex.PvcName}}}) } @@ -210,12 +219,19 @@ func buildVolumeSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalog func buildContainerSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpex databasev4.CatalogSpec) []corev1.Container { // building Continer spec var result []corev1.Container + user := oraRunAsUser + group := oraFsGroup containerSpec := corev1.Container{ Name: OraCatalogSpex.Name, Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ + RunAsNonRoot: BoolPointer(true), + RunAsUser: &user, + RunAsGroup: &group, + AllowPrivilegeEscalation: BoolPointer(false), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("SYS_NICE")}, + Drop: []corev1.Capability{"ALL",}, }, }, Resources: corev1.ResourceRequirements{ @@ -299,8 +315,13 @@ func buildInitContainerSpecForCatalog(instance *databasev4.ShardingDatabase, Ora Name: OraCatalogSpex.Name + "-init1", Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ + RunAsNonRoot: BoolPointer(true), + AllowPrivilegeEscalation: BoolPointer(false), Privileged: &privFlag, RunAsUser: &uid, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL",}, + }, }, Command: []string{ "/bin/bash", @@ -329,6 +350,10 @@ func buildVolumeMountSpecForCatalog(instance *databasev4.ShardingDatabase, OraCa } result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "oradshm-vol6", MountPath: oraShm}) + if OraCatalogSpex.CatalogConfigData != nil && len(OraCatalogSpex.CatalogConfigData.Name) != 0 { + result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "-oradata-configdata", MountPath: OraCatalogSpex.CatalogConfigData.MountPath}) + } + if len(instance.Spec.StagePvcName) != 0 { result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "orastage-vol7", MountPath: oraStage}) } @@ -526,3 +551,16 @@ func UpdateProvForCatalog(instance *databasev4.ShardingDatabase, return ctrl.Result{}, nil } + +func ExportTDEKey(podName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger,) error { + var msg string + + msg = "" + _, _, err := ExecCommand(podName, getExportTDEKeyCmd(sparams), kubeClient, kubeconfig, instance, logger) + if err != nil { + msg = "Error executing getExportTDEKeyCmd : podName=[" + podName + "]. errMsg=" + err.Error() + LogMessages("INFO", msg, nil, instance, logger) + return err + } + return nil +} diff --git a/commons/sharding/gsm.go b/commons/sharding/gsm.go index e6be8770..5e0d5c46 100644 --- a/commons/sharding/gsm.go +++ b/commons/sharding/gsm.go @@ -142,11 +142,14 @@ func buildPodSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databa group := oraFsGroup spec := &corev1.PodSpec{ SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: BoolPointer(true), RunAsUser: &user, + RunAsGroup: &group, FSGroup: &group, }, Containers: buildContainerSpecForGsm(instance, OraGsmSpex), Volumes: buildVolumeSpecForGsm(instance, OraGsmSpex), + ServiceAccountName: instance.Spec.SrvAccountName, } if (instance.Spec.IsDownloadScripts) && (instance.Spec.ScriptsLocation != "") { @@ -189,6 +192,10 @@ func buildVolumeSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex dat }, } + if OraGsmSpex.GsmConfigData != nil && len(OraGsmSpex.GsmConfigData.Name) != 0 { + result = append(result, corev1.Volume{Name: OraGsmSpex.Name + "-oradata-configdata", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OraGsmSpex.GsmConfigData.Name}}}}) + } + if len(OraGsmSpex.PvcName) != 0 { result = append(result, corev1.Volume{Name: OraGsmSpex.Name + "oradata-vol4", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OraGsmSpex.PvcName}}}) } @@ -209,6 +216,8 @@ func buildContainerSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex var result []corev1.Container var masterGsmFlag = false var idx int + user := oraRunAsUser + group := oraFsGroup // Get the Idx if instance.Spec.Gsm[0].Name == OraGsmSpex.Name { masterGsmFlag = true @@ -223,8 +232,13 @@ func buildContainerSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex Name: OraGsmSpex.Name, Image: instance.Spec.GsmImage, SecurityContext: &corev1.SecurityContext{ + RunAsNonRoot: BoolPointer(true), + RunAsUser: &user, + RunAsGroup: &group, + AllowPrivilegeEscalation: BoolPointer(false), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_RAW"}, + Drop: []corev1.Capability{"ALL",}, }, }, Resources: corev1.ResourceRequirements{ @@ -288,8 +302,13 @@ func buildInitContainerSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmS Name: OraGsmSpex.Name + "-init1", Image: instance.Spec.GsmImage, SecurityContext: &corev1.SecurityContext{ + RunAsNonRoot: BoolPointer(true), + AllowPrivilegeEscalation: BoolPointer(false), Privileged: &privFlag, RunAsUser: &uid, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL",}, + }, }, Command: []string{ "/bin/bash", @@ -318,6 +337,10 @@ func buildVolumeMountSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpe } result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "oradshm-vol6", MountPath: oraShm}) + if OraGsmSpex.GsmConfigData != nil && len(OraGsmSpex.GsmConfigData.Name) != 0 { + result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "-oradata-configdata", MountPath: OraGsmSpex.GsmConfigData.MountPath}) + } + if len(instance.Spec.StagePvcName) != 0 { result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "orastage-vol7", MountPath: oraStage}) } diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 3b3f1b04..a2ddfa7b 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -238,6 +238,11 @@ func buildEnvVarsSpec(instance *databasev4.ShardingDatabase, variables []databas result = append(result, corev1.EnvVar{Name: "KEY_SECRET_VOLUME", Value: oraSecretMount}) } + if checkTdeWalletFlag(instance) { + result = append(result, corev1.EnvVar{Name: "TDE_PWD_KEY", Value: instance.Spec.DbSecret.TdeKeyFileName}) + result = append(result, corev1.EnvVar{Name: "TDE_PWD_FILE", Value: instance.Spec.DbSecret.TdePwdFileName}) + } + if restype == "GSM" { if !sDirectParam { //varinfo = "director_name=sharddirector" + sDirectorCounter + ";director_region=primary;director_port=1521" @@ -865,21 +870,28 @@ func buildDirectorParams(instance *databasev4.ShardingDatabase, oraGsmSpex datab var varinfo string var dnameFlag bool = false var dportFlag bool = false + var dname string + var dport string // Get the GSM Spec and build director params. idx feild is very important to build the unique director name and regiod. Idx is GSM array index. variables = oraGsmSpex.EnvVars for _, variable := range variables { if variable.Name == "DIRECTOR_NAME" { dnameFlag = true + dname = variable.Value } if variable.Name == "DIRECTOR_PORT" { dportFlag = true + dport = variable.Value } } if !dnameFlag { - varinfo = "director_name=sharddirector" + strconv.Itoa(idx) + ";" + varinfo = "director_name=sharddirector" + oraGsmSpex.Name + ";" result = result + varinfo - } + } else { + varinfo = "director_name=" + dname + ";" + result = result + varinfo + } if oraGsmSpex.Region != "" { varinfo = "director_region=" + oraGsmSpex.Region + ";" @@ -901,7 +913,11 @@ func buildDirectorParams(instance *databasev4.ShardingDatabase, oraGsmSpex datab if !dportFlag { varinfo = "director_port=1522" result = result + varinfo - } + } else { + varinfo = "director_port=" + dport + result = result + varinfo + } + result = strings.TrimSuffix(result, ";") return result } @@ -1168,6 +1184,16 @@ func getGsmvalidateCmd() []string { return depCmd } +func getExportTDEKeyCmd(sparamStr string) []string { + var exportTDEKeyCmd []string = []string{oraDbScriptMount + "/cmdExec", "/bin/python", oraDbScriptMount + "/main.py ", "--exporttdekey=" + strconv.Quote(sparamStr)} + return exportTDEKeyCmd +} + +func getImportTDEKeyCmd(sparamStr string) []string { + var importTDEKeyCmd []string = []string{oraDbScriptMount + "/cmdExec", "/bin/python", oraDbScriptMount + "/main.py ", "--importtdekey=" + strconv.Quote(sparamStr)} + return importTDEKeyCmd +} + func getInitContainerCmd(resType string, name string, ) string { var initCmd string @@ -1530,6 +1556,15 @@ func checkTdeWalletFlag(instance *databasev4.ShardingDatabase) bool { return false } +func CheckIsTDEWalletFlag(instance *databasev4.ShardingDatabase, logger logr.Logger) bool { + LogMessages("INFO", "CheckIsTDEWalletFlag():isTdeWallet=["+instance.Spec.IsTdeWallet+"].", nil, instance, logger) + if strings.ToLower(instance.Spec.IsTdeWallet) == "enable" { + LogMessages("INFO", "CheckIsTDEWalletFlag():Returning true", nil, instance, logger) + return true + } + return false +} + func CheckIsDeleteFlag(delStr string, instance *databasev4.ShardingDatabase, logger logr.Logger) bool { if strings.ToLower(delStr) == "enable" { return true @@ -1546,3 +1581,11 @@ func getTdeWalletMountLoc(instance *databasev4.ShardingDatabase) string { } return "/tdewallet/" + instance.Name } + +func Int64Pointer(d int64) *int64 { + return &d +} + +func BoolPointer(d bool) *bool { + return &d +} diff --git a/commons/sharding/shard.go b/commons/sharding/shard.go index e48b56dd..244589ab 100644 --- a/commons/sharding/shard.go +++ b/commons/sharding/shard.go @@ -42,6 +42,7 @@ import ( "context" "reflect" "strconv" + "strings" databasev4 "github.com/oracle/oracle-database-operator/apis/database/v4" @@ -51,6 +52,8 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -138,11 +141,14 @@ func buildPodSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex da group := oraFsGroup spec := &corev1.PodSpec{ SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: BoolPointer(true), RunAsUser: &user, + RunAsGroup: &group, FSGroup: &group, }, Containers: buildContainerSpecForShard(instance, OraShardSpex), Volumes: buildVolumeSpecForShard(instance, OraShardSpex), + ServiceAccountName: instance.Spec.SrvAccountName, } if (instance.Spec.IsDownloadScripts) && (instance.Spec.ScriptsLocation != "") { @@ -187,6 +193,10 @@ func buildVolumeSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex }, } + if OraShardSpex.ShardConfigData != nil && len(OraShardSpex.ShardConfigData.Name) != 0 { + result = append(result, corev1.Volume{Name: OraShardSpex.Name + "-oradata-configdata", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OraShardSpex.ShardConfigData.Name}}}}) + } + if len(OraShardSpex.PvcName) != 0 { result = append(result, corev1.Volume{Name: OraShardSpex.Name + "oradata-vol4", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OraShardSpex.PvcName}}}) } @@ -211,12 +221,19 @@ func buildVolumeSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex func buildContainerSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex databasev4.ShardSpec) []corev1.Container { // building Continer spec var result []corev1.Container + user := oraRunAsUser + group := oraFsGroup containerSpec := corev1.Container{ Name: OraShardSpex.Name, Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ + RunAsNonRoot: BoolPointer(true), + RunAsUser: &user, + RunAsGroup: &group, + AllowPrivilegeEscalation: BoolPointer(false), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("SYS_NICE")}, + Drop: []corev1.Capability{"ALL",}, }, }, Resources: corev1.ResourceRequirements{ @@ -304,8 +321,13 @@ func buildInitContainerSpecForShard(instance *databasev4.ShardingDatabase, OraSh Name: OraShardSpex.Name + "-init1", Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ + RunAsNonRoot: BoolPointer(true), + AllowPrivilegeEscalation: BoolPointer(false), Privileged: &privFlag, RunAsUser: &uid, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL",}, + }, }, Command: []string{ "/bin/bash", @@ -334,6 +356,10 @@ func buildVolumeMountSpecForShard(instance *databasev4.ShardingDatabase, OraShar } result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "oradshm-vol6", MountPath: oraShm}) + if OraShardSpex.ShardConfigData != nil && len(OraShardSpex.ShardConfigData.Name) != 0 { + result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "-oradata-configdata", MountPath: OraShardSpex.ShardConfigData.MountPath}) + } + if len(instance.Spec.StagePvcName) != 0 { result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "orastage-vol7", MountPath: oraStage}) } @@ -510,3 +536,21 @@ func UpdateProvForShard(instance *databasev4.ShardingDatabase, OraShardSpex data } return ctrl.Result{}, nil } + +func ImportTDEKey(podName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger,) error { + var msg string + + msg = "" + _, _, err := ExecCommand(podName, getImportTDEKeyCmd(sparams), kubeClient, kubeconfig, instance, logger) + if err != nil { + msg = "Error executing getImportTDEKeyCmd : podName=[" + podName + "]. errMsg=" + err.Error() + LogMessages("INFO", msg, nil, instance, logger) + return err + } + + importArr := getImportTDEKeyCmd(sparams) + importCmd := strings.Join(importArr, " ") + msg = "Executed getImportTDEKeyCmd[" + importCmd + "] on pod " + podName + LogMessages("INFO", msg, nil, instance, logger) + return nil +} diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 90c6dd53..8917a8b8 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -588,6 +588,13 @@ spec: catalog: items: properties: + catalogConfigData: + properties: + mountPath: + type: string + name: + type: string + type: object envVars: items: properties: @@ -688,9 +695,14 @@ spec: type: string pwdFileName: type: string + tdeKeyFileName: + type: string + tdePwdFileName: + type: string required: - name - pwdFileName + - tdePwdFileName type: object fssStorageClass: type: string @@ -711,6 +723,13 @@ spec: - value type: object type: array + gsmConfigData: + properties: + mountPath: + type: string + name: + type: string + type: object imagePullPolicy: type: string isDelete: @@ -915,6 +934,8 @@ spec: type: string scriptsLocation: type: string + serviceAccountName: + type: string shard: items: properties: @@ -992,6 +1013,13 @@ spec: x-kubernetes-int-or-string: true type: object type: object + shardConfigData: + properties: + mountPath: + type: string + name: + type: string + type: object shardGroup: type: string shardRegion: @@ -1009,6 +1037,47 @@ spec: type: string shardConfigName: type: string + shardInfo: + items: + properties: + replicas: + format: int32 + type: integer + shape: + type: string + shardGroupDetails: + properties: + ShardSpace: + type: string + deployAs: + type: string + isDelete: + type: string + region: + type: string + repFactor: + type: integer + shardGroupName: + type: string + type: object + shardPreFixName: + type: string + shardSpaceDetails: + properties: + Chnuks: + type: integer + protectMode: + type: string + shardSpaceName: + type: string + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - shardPreFixName + type: object + type: array shardRegion: items: type: string @@ -1030,7 +1099,6 @@ spec: - dbImage - gsm - gsmImage - - shard type: object status: properties: diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index af3dc9f3..5bd7cc02 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -171,6 +171,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -601,7 +603,7 @@ spec: proxyFromEnvironment: type: boolean proxyUrl: - pattern: ^http(s)?://.+$ + pattern: ^(http|https|socks5)://.+$ type: string scopes: items: @@ -1131,6 +1133,8 @@ spec: - port type: object type: object + stopSignal: + type: string type: object livenessProbe: properties: @@ -2588,6 +2592,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -3018,7 +3024,7 @@ spec: proxyFromEnvironment: type: boolean proxyUrl: - pattern: ^http(s)?://.+$ + pattern: ^(http|https|socks5)://.+$ type: string scopes: items: @@ -3548,6 +3554,8 @@ spec: - port type: object type: object + stopSignal: + type: string type: object livenessProbe: properties: @@ -5005,6 +5013,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -5435,7 +5445,7 @@ spec: proxyFromEnvironment: type: boolean proxyUrl: - pattern: ^http(s)?://.+$ + pattern: ^(http|https|socks5)://.+$ type: string scopes: items: @@ -5965,6 +5975,8 @@ spec: - port type: object type: object + stopSignal: + type: string type: object livenessProbe: properties: diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 8f76c549..27229161 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -71,3 +71,19 @@ patches: # the following config is for teaching kustomize how to do kustomization for CRDs. configurations: - kustomizeconfig.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +patches: +- path: patches/webhook_in_autonomousdatabases.yaml +- path: patches/webhook_in_autonomousdatabasebackups.yaml +- path: patches/webhook_in_autonomousdatabaserestores.yaml +- path: patches/webhook_in_autonomouscontainerdatabases.yaml +- path: patches/cainjection_in_singleinstancedatabases.yaml +- path: patches/cainjection_in_shardingdatabases.yaml +- path: patches/cainjection_in_pdbs.yaml +- path: patches/cainjection_in_cdbs.yaml +- path: patches/cainjection_in_dbcssystems.yaml +- path: patches/cainjection_in_autonomousdatabases.yaml +- path: patches/cainjection_in_autonomousdatabasebackups.yaml +- path: patches/cainjection_in_autonomousdatabaserestores.yaml +- path: patches/cainjection_in_autonomouscontainerdatabases.yaml diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 74303bea..782bc9b2 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: basedb-operator-sa + newTag: Test-sharding-operator-V4 diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index f73e7b8a..c4ba40b3 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -210,26 +210,6 @@ webhooks: resources: - oraclerestdataservices sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: mshardingdatabasev1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -665,27 +645,6 @@ webhooks: resources: - oraclerestdataservices sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /validate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: vshardingdatabasev1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 1ec77253..95c33f2f 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -48,8 +48,6 @@ import ( "time" "github.com/go-logr/logr" - "github.com/oracle/oci-go-sdk/v65/common" - "github.com/oracle/oci-go-sdk/v65/ons" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -71,15 +69,6 @@ import ( shardingv1 "github.com/oracle/oracle-database-operator/commons/sharding" ) -// Struct keeping Oracle Notification Server Info -type OnsStatus struct { - Topicid string `json:"topicid,omitempty"` - Instance *databasev4.ShardingDatabase `json:"instance,omitempty"` - OnsProvider common.ConfigurationProvider `json:"onsProvider,omitempty"` - OnsProviderFlag bool `json:"onsProviderFlag,omitempty"` - Rclient ons.NotificationDataPlaneClient `json:"rclient,omitempty"` -} - // ShardingDatabaseReconciler reconciles a ShardingDatabase object type ShardingDatabaseReconciler struct { client.Client @@ -88,14 +77,10 @@ type ShardingDatabaseReconciler struct { kubeClient kubernetes.Interface kubeConfig clientcmd.ClientConfig Recorder record.EventRecorder - InCluster bool - Namespace string } -var sentFailMsg = make(map[string]bool) -var sentCompleteMsg = make(map[string]bool) - -var oshMap = make(map[string]*OnsStatus) +var exportedTDEKeys bool = false +var importedTDEKeys []bool // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=database.oracle.com,resources=shardingdatabases/status,verbs=get;update;patch @@ -147,7 +132,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req } // Fetch the ProvShard instance instance := &databasev4.ShardingDatabase{} - err = r.Client.Get(context.TODO(), req.NamespacedName, instance) + err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, instance) if err != nil { if errors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile req. @@ -158,12 +143,6 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req // Error reading the object - requeue the req. return ctrl.Result{}, err } - - instFlag := r.checkProvInstance(instance) - if !instFlag { - oshMap[instance.Name] = &OnsStatus{} - oshMap[instance.Name].Instance = instance - } defer r.setCrdLifeCycleState(instance, &result, &err, &stateType) defer r.updateShardTopologyStatus(instance) // =============================== Check Deletion TimeStamp======== @@ -182,23 +161,17 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req } } - // ======== Setting the flag and Index to be used later in this function ======== - // instFlag = r.checkProvInstance(instance) - // if !instFlag { - //r.setCrdLifeCycleState(instance, &result, &err, stateType) - //// result = resultNq - // return result, fmt.Errorf("DId not find the instance in checkProvInstance") - // } - - // ================================ OCI Notification Provider =========== - r.getOnsConfigProvider(instance) - - // =============================== Checking Namespace ============== + if len(importedTDEKeys) == 0 { + importedTDEKeys = make([]bool, int32(len(instance.Spec.Shard)), int32(len(instance.Spec.Shard))) + for i = 0; i < int32(len(instance.Spec.Shard)); i++ { + importedTDEKeys[i] = false + shardingv1.LogMessages("INFO", "Initializing importedTDEKeys to false", nil, instance, r.Log) + } + } // ======================== Validate Specs ============== err = r.validateSpex(instance) if err != nil { - //r.setCrdLifeCycleState(instance, &result, &err, stateType) result = resultNq return result, err } @@ -384,6 +357,13 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req result = resultNq return result, err } + + if shardingv1.CheckIsTDEWalletFlag(instance,r.Log) && !exportedTDEKeys { + exportTDEfname := "expTDEFile" + shardingv1.LogMessages("INFO", "Catalog calling ExportTDEKey", nil, instance, r.Log) + shardingv1.ExportTDEKey(OraCatalogSpex.Name+"-0",exportTDEfname, instance, r.kubeClient, r.kubeConfig, r.Log) + exportedTDEKeys = true + } } // ====================== Update Setup for Shard ============================== @@ -403,6 +383,14 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req return result, err } } + if shardingv1.CheckIsTDEWalletFlag(instance,r.Log) && exportedTDEKeys { + importTDEfname := "impTDEFile" + shardingv1.LogMessages("INFO", "Calling ImportTDEKey()", nil, instance, r.Log) + if !importedTDEKeys[i] { + shardingv1.ImportTDEKey(OraShardSpex.Name+"-0",importTDEfname, instance, r.kubeClient, r.kubeConfig, r.Log) + } + importedTDEKeys[i] = true + } } // ====================== Update Setup for Gsm ============================== @@ -473,32 +461,28 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate return true }, DeleteFunc: func(e event.DeleteEvent) bool { - instance := &databasev4.ShardingDatabase{} + //instance := &databasev4.ShardingDatabase{} _, podOk := e.Object.GetLabels()["statefulset.kubernetes.io/pod-name"] - if oshMap[instance.Name] != nil { - oshInst := instance - if instance.DeletionTimestamp == nil { - - if e.Object.GetLabels()[string(databasev4.ShardingDelLabelKey)] == string(databasev4.ShardingDelLabelTrueValue) { - } - - if podOk { - delObj := e.Object.(*corev1.Pod) - if e.Object.GetLabels()["type"] == "Shard" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Name { - - if delObj.DeletionTimestamp != nil { - go r.gsmInvitedNodeOp(oshInst, delObj.Name) - } + instance, _ := e.Object.DeepCopyObject().(*databasev4.ShardingDatabase) + if e.Object.GetDeletionTimestamp() == nil { + if e.Object.GetLabels()[string(databasev4.ShardingDelLabelKey)] == string(databasev4.ShardingDelLabelTrueValue) { + } + if podOk { + delObj := e.Object.(*corev1.Pod) + if e.Object.GetLabels()["type"] == "Shard" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == instance.Name { + if delObj.DeletionTimestamp != nil { + go r.gsmInvitedNodeOp(instance, delObj.Name) } + } - if e.Object.GetLabels()["type"] == "Catalog" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == oshInst.Name { + if e.Object.GetLabels()["type"] == "Catalog" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == instance.Name { - if delObj.DeletionTimestamp != nil { - go r.gsmInvitedNodeOp(oshInst, delObj.Name) - } + if delObj.DeletionTimestamp != nil { + go r.gsmInvitedNodeOp(instance, delObj.Name) } } } + } return true }, @@ -524,59 +508,6 @@ func (r *ShardingDatabaseReconciler) UpdateSecret(instance *databasev4.ShardingD return ctrl.Result{}, nil } -// ================== Function to get the Notification controller ============== -func (r *ShardingDatabaseReconciler) getOnsConfigProvider(instance *databasev4.ShardingDatabase) { - var err error - if instance.Spec.DbSecret.NsConfigMap != "" && instance.Spec.DbSecret.NsSecret != "" && oshMap[instance.Name].OnsProviderFlag != true { - cmName := instance.Spec.DbSecret.NsConfigMap - secName := instance.Spec.DbSecret.NsSecret - shardingv1.LogMessages("DEBUG", "Received parameters are "+shardingv1.GetFmtStr(cmName)+","+shardingv1.GetFmtStr(secName), nil, instance, r.Log) - region, user, tenancy, passphrase, fingerprint, topicid := shardingv1.ReadConfigMap(cmName, instance, r.Client, r.Log) - privatekey := shardingv1.ReadSecret(secName, instance, r.Client, r.Log) - - oshMap[instance.Name].Topicid = topicid - oshMap[instance.Name].OnsProvider = common.NewRawConfigurationProvider(tenancy, user, region, fingerprint, privatekey, &passphrase) - //VV instance.Spec.TopicId = topicid - oshMap[instance.Name].Rclient, err = ons.NewNotificationDataPlaneClientWithConfigurationProvider(oshMap[instance.Name].OnsProvider) - if err != nil { - msg := "Error occurred in getting the OCI notification service based client." - oshMap[instance.Name].OnsProviderFlag = false - r.Log.Error(err, msg) - shardingv1.LogMessages("Error", msg, nil, instance, r.Log) - } else { - oshMap[instance.Name].OnsProviderFlag = true - } - } -} - -func (r ShardingDatabaseReconciler) marshalOnsInfo(instance *databasev4.ShardingDatabase) (OnsStatus, error) { - onsData := OnsStatus{} - specBytes, err := instance.GetLastSuccessfulOnsInfo() - if err != nil { - shardingv1.LogMessages("Error", "error occurred while getting the data from getLastSuccessfulOnsInfo", nil, instance, r.Log) - return onsData, err - } else { - shardingv1.LogMessages("Error", "error occurred while getting the data from getLastSuccessfulOnsInfo and unmarshaling the object", nil, instance, r.Log) - err := json.Unmarshal(specBytes, &onsData) - if err != nil { - return onsData, err - } - } - return onsData, nil -} - -// ================== Function the Message ============== -func (r *ShardingDatabaseReconciler) sendMessage(instance *databasev4.ShardingDatabase, title string, body string) { - instFlag := r.checkProvInstance(instance) - if instFlag { - shardingv1.LogMessages("INFO", "sendMessage():instFlag true", nil, instance, r.Log) - if oshMap[instance.Name].OnsProviderFlag { - shardingv1.LogMessages("INFO", "sendMessage():OnsProviderFlag true", nil, instance, r.Log) - shardingv1.SendNotification(title, body, instance, oshMap[instance.Name].Topicid, oshMap[instance.Name].Rclient, r.Log) - } - } -} - func (r *ShardingDatabaseReconciler) publishEvents(instance *databasev4.ShardingDatabase, eventMsg string, state string) { if state == string(databasev4.AvailableState) || state == string(databasev4.AddingShardState) || state == string(databasev4.ShardOnlineState) || state == string(databasev4.ProvisionState) || state == string(databasev4.DeletingState) || state == string(databasev4.Terminated) { @@ -649,8 +580,6 @@ func (r *ShardingDatabaseReconciler) finalizeShardingDatabase(instance *database var i int32 var err error var pvcName string - - r.checkProvInstance(instance) sfSetFound := &appsv1.StatefulSet{} svcFound := &corev1.Service{} if len(instance.Spec.Shard) > 0 { @@ -839,27 +768,9 @@ func (r *ShardingDatabaseReconciler) finalizeShardingDatabase(instance *database } } - oshMap[instance.Name].Instance = &databasev4.ShardingDatabase{} - return nil } -// Get the current instance -func (r *ShardingDatabaseReconciler) checkProvInstance(instance *databasev4.ShardingDatabase, -) bool { - - var status bool = false - if oshMap[instance.Name] != nil { - title := "checkProvInstance()" - message := "oshMap.Instance.Name=[" + oshMap[instance.Name].Instance.Name + "]. instance.Name=[" + instance.Name + "]." - shardingv1.LogMessages("INFO", title+":"+message, nil, instance, r.Log) - if oshMap[instance.Name].Instance.Name == instance.Name { - status = true - } - } - return status -} - // =========== validate Specs ============ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev4.ShardingDatabase) error { @@ -1567,14 +1478,7 @@ func (r *ShardingDatabaseReconciler) addPrimaryShards(instance *databasev4.Shard if err != nil { r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.AddingShardErrorState)) title = instance.Namespace + ":Shard Addition Failure" - message = "TopicId:" + oshMap[instance.Name].Topicid + ":Error occurred during shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " addition." shardingv1.LogMessages("Error", title+":"+message, nil, instance, r.Log) - msgKey := instance.Namespace + "-" + OraShardSpex.Name - if sentFailMsg[msgKey] != true { - r.sendMessage(instance, title, message) - } - sentFailMsg[msgKey] = true - sentCompleteMsg[msgKey] = false deployFlag = false } } @@ -1624,15 +1528,8 @@ func (r *ShardingDatabaseReconciler) verifyShards(instance *databasev4.ShardingD // Following logic will sent a email only once if oldStateStr != string(databasev4.ShardOnlineState) { title = instance.Namespace + ":Shard Addition Completed" - message = "TopicId:" + oshMap[instance.Name].Topicid + ":Shard addition completed for shard " + shardingv1.GetFmtStr(shardSfSet.Name) + " in GSM." + message = ":Shard addition completed for shard " + shardingv1.GetFmtStr(shardSfSet.Name) + " in GSM." shardingv1.LogMessages("INFO", title+":"+message, nil, instance, r.Log) - msgKey := instance.Namespace + "-" + shardSfSet.Name - if sentCompleteMsg[msgKey] != true { - r.sendMessage(instance, title, message) - } - - sentCompleteMsg[msgKey] = true - sentFailMsg[msgKey] = false } return nil } @@ -1653,8 +1550,6 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev4.ShardingDa //gsmSfSet := &appsv1.StatefulSet{} gsmPod := &corev1.Pod{} var msg string - var title string - var message string var setLifeCycleFlag = false shardingv1.LogMessages("DEBUG", "Starting shard deletion operation.", nil, instance, r.Log) @@ -1716,9 +1611,6 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev4.ShardingDa err = shardingv1.MoveChunks(gsmPod.Name, sparams, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.ChunkMoveError)) - title = "Chunk Movement Failure" - message = "Error occurred during chunk movement in shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " deletion." - r.sendMessage(instance, title, message) instance.Spec.Shard[i].IsDelete = "failed" err = shardingv1.InstanceShardPatch(instance, instance, r.Client, i, "isDelete", "failed") if err != nil { @@ -1776,9 +1668,6 @@ func (r *ShardingDatabaseReconciler) delGsmShard(instance *databasev4.ShardingDa r.delShard(instance, shardSfSet.Name, shardSfSet, shardPod, int(i)) r.updateGsmShardStatus(instance, OraShardSpex.Name, string(databasev4.Terminated)) r.updateShardStatus(instance, int(i), string(databasev4.Terminated)) - title = "Shard Deletion Completed" - message = "Shard deletion completed for shard " + shardingv1.GetFmtStr(OraShardSpex.Name) + " in GSM." - r.sendMessage(instance, title, message) } } } diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index f26ca473..f3c00ef1 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -253,14 +253,14 @@ spec: type: integer target: properties: - k8sADB: + k8sAdb: properties: name: type: string type: object - ociADB: + ociAdb: properties: - ocid: + id: type: string type: object type: object @@ -344,14 +344,14 @@ spec: type: integer target: properties: - k8sADB: + k8sAdb: properties: name: type: string type: object - ociADB: + ociAdb: properties: - ocid: + id: type: string type: object type: object @@ -453,7 +453,7 @@ spec: type: object source: properties: - k8sADBBackup: + k8sAdbBackup: properties: name: type: string @@ -466,14 +466,14 @@ spec: type: object target: properties: - k8sADB: + k8sAdb: properties: name: type: string type: object - ociADB: + ociAdb: properties: - ocid: + id: type: string type: object type: object @@ -539,7 +539,7 @@ spec: type: object source: properties: - k8sADBBackup: + k8sAdbBackup: properties: name: type: string @@ -552,14 +552,14 @@ spec: type: object target: properties: - k8sADB: + k8sAdb: properties: name: type: string type: object - ociADB: + ociAdb: properties: - ocid: + id: type: string type: object type: object @@ -1810,8 +1810,8 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig + - jsonPath: .status.metricsConfig + name: MetricsConfig type: string - jsonPath: .status.status name: Status @@ -1831,7 +1831,7 @@ spec: type: object spec: properties: - configuration: + azureConfig: properties: configMap: properties: @@ -1843,8 +1843,19 @@ spec: type: object database: properties: + azure: + properties: + vaultID: + type: string + vaultPasswordSecret: + type: string + vaultUsernameSecret: + type: string + type: object dbConnectionString: properties: + envName: + type: string key: type: string secret: @@ -1852,243 +1863,252 @@ spec: type: object dbPassword: properties: + envName: + type: string key: type: string secret: type: string - vaultOCID: - type: string - vaultSecretName: - type: string type: object dbUser: properties: + envName: + type: string key: type: string secret: type: string type: object - dbWallet: + oci: properties: - key: + vaultID: type: string - secret: + vaultPasswordSecret: type: string type: object type: object - exporter: - properties: - deployment: - properties: - args: - items: + databases: + additionalProperties: + properties: + dbConnectionString: + properties: + envName: type: string - type: array - commands: - items: + key: type: string - type: array - env: - additionalProperties: + secret: + type: string + type: object + dbPassword: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + dbUser: + properties: + envName: + type: string + key: + type: string + secret: type: string + type: object + type: object + type: object + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podSecurityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type type: object - image: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: type: string - labels: - additionalProperties: - type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string type: object - podTemplate: + seccompProfile: properties: - labels: - additionalProperties: - type: string - type: object - securityContext: - properties: - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxChangePolicy: - type: string - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - x-kubernetes-list-type: atomic - supplementalGroupsPolicy: - type: string - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object + localhostProfile: + type: string + type: + type: string + required: + - type type: object - securityContext: + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - capabilities: - properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - privileged: - type: boolean - procMount: + gmsaCredentialSpec: type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: + gmsaCredentialSpecName: + type: string + hostProcess: type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object + runAsUserName: + type: string type: object type: object - service: + podTemplate: properties: labels: additionalProperties: type: string type: object - ports: - items: - properties: - appProtocol: - type: string - name: + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP + type: array + x-kubernetes-list-type: atomic + drop: + items: type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + exporterConfig: + properties: + configMap: + properties: + key: + type: string + name: + type: string type: object + mountPath: + type: string type: object inheritLabels: items: @@ -2104,6 +2124,8 @@ spec: type: string volume: properties: + name: + type: string persistentVolumeClaim: properties: claimName: @@ -2111,41 +2133,202 @@ spec: type: object type: object type: object - ociConfig: + metrics: properties: - configMapName: - type: string - secretName: - type: string + configMap: + items: + properties: + key: + type: string + name: + type: string + type: object + type: array type: object - prometheus: + ociConfig: properties: - serviceMonitor: + configMap: properties: - endpoints: - items: - properties: - authorization: - properties: - credentials: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: + key: + type: string + name: + type: string + type: object + mountPath: + type: string + privateKey: + properties: + secret: + type: string + type: object + type: object + replicas: + format: int32 + type: integer + service: + properties: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: + properties: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - basicAuth: + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: properties: - password: + configMap: properties: key: type: string @@ -2158,7 +2341,7 @@ spec: - key type: object x-kubernetes-map-type: atomic - username: + secret: properties: key: type: string @@ -2172,9 +2355,7 @@ spec: type: object x-kubernetes-map-type: atomic type: object - bearerTokenFile: - type: string - bearerTokenSecret: + clientSecret: properties: key: type: string @@ -2187,69 +2368,42 @@ spec: - key type: object x-kubernetes-map-type: atomic - enableHttp2: - type: boolean - filterRunning: - type: boolean - followRedirects: - type: boolean - honorLabels: - type: boolean - honorTimestamps: + endpointParams: + additionalProperties: + type: string + type: object + noProxy: + type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: type: boolean - interval: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + proxyUrl: + pattern: ^(http|https|socks5)://.+$ type: string - metricRelabelings: + scopes: items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object + type: string type: array - oauth2: + tlsConfig: properties: - clientId: + ca: properties: configMap: properties: @@ -2278,28 +2432,9 @@ spec: type: object x-kubernetes-map-type: atomic type: object - clientSecret: + cert: properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - type: object - noProxy: - type: string - proxyConnectHeader: - additionalProperties: - items: + configMap: properties: key: type: string @@ -2312,81 +2447,7 @@ spec: - key type: object x-kubernetes-map-type: atomic - type: array - type: object - x-kubernetes-map-type: atomic - proxyFromEnvironment: - type: boolean - proxyUrl: - pattern: ^(http|https|socks5)://.+$ - type: string - scopes: - items: - type: string - type: array - tlsConfig: - properties: - ca: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - type: boolean - keySecret: + secret: properties: key: type: string @@ -2399,172 +2460,155 @@ spec: - key type: object x-kubernetes-map-type: atomic - maxVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 + type: object + insecureSkipVerify: + type: boolean + keySecret: + properties: + key: type: string - serverName: + name: + default: "" type: string + optional: + type: boolean + required: + - key type: object - tokenUrl: - minLength: 1 + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - params: - additionalProperties: - items: + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: type: string - type: array type: object - path: - type: string - port: - type: string - proxyUrl: - type: string - relabelings: - items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object - type: array - scheme: - enum: - - http - - https + tokenUrl: + minLength: 1 type: string - scrapeTimeout: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - tlsConfig: + type: array + type: object + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + scheme: + enum: + - http + - https + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + tlsConfig: + properties: + ca: properties: - ca: + configMap: properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - caFile: - type: string - cert: + x-kubernetes-map-type: atomic + secret: properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - certFile: - type: string - insecureSkipVerify: - type: boolean - keyFile: - type: string - keySecret: + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: properties: key: type: string @@ -2577,941 +2621,421 @@ spec: - key type: object x-kubernetes-map-type: atomic - maxVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 + secret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: type: string - serverName: + name: + default: "" type: string + optional: + type: boolean + required: + - key type: object - trackTimestampsStaleness: - type: boolean - type: object - type: array - labels: - additionalProperties: - type: string - type: object - namespaceSelector: - properties: - any: - type: boolean - matchNames: - items: + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 type: string - type: array - type: object - type: object - type: object - replicas: - format: int32 - type: integer - sidecarVolumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - default: ext4 - type: string - kind: - type: string - readOnly: - default: false + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + trackTimestampsStaleness: type: boolean - required: - - diskName - - diskURI type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: type: string - required: - - secretName - - shareName - type: object - cephfs: + type: array + type: object + type: object + sidecar: + properties: + containers: + items: properties: - monitors: + args: items: type: string type: array x-kubernetes-list-type: atomic - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: items: properties: - key: + name: type: string - mode: - format: int32 - type: integer - path: + value: type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object required: - - key - - path + - name type: object type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: items: properties: - fieldRef: + configMapRef: properties: - apiVersion: - type: string - fieldPath: + name: + default: "" type: string - required: - - fieldPath + optional: + type: boolean type: object x-kubernetes-map-type: atomic - mode: - format: int32 - type: integer - path: + prefix: type: string - resourceFieldRef: + secretRef: properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: + name: + default: "" type: string - required: - - resource + optional: + type: boolean type: object x-kubernetes-map-type: atomic - required: - - path type: object type: array x-kubernetes-list-type: atomic - type: object - emptyDir: - properties: - medium: + image: type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - volumeClaimTemplate: + imagePullPolicy: + type: string + lifecycle: properties: - metadata: - type: object - spec: + postStart: properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: properties: - apiGroup: + host: type: string - kind: + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: type: string - name: + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: type: string required: - - kind - - name + - port type: object - x-kubernetes-map-type: atomic - dataSourceRef: + sleep: properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true required: - - kind - - name + - port type: object - resources: + type: object + preStop: + properties: + exec: properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic type: object - selector: + httpGet: properties: - matchExpressions: + host: + type: string + httpHeaders: items: properties: - key: + name: type: string - operator: + value: type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic required: - - key - - operator + - name + - value type: object type: array x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port type: object - x-kubernetes-map-type: atomic - storageClassName: + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" type: string - volumeAttributesClassName: + required: + - port + type: object + httpGet: + properties: + host: type: string - volumeMode: + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: type: string - volumeName: + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: type: string + required: + - port type: object - required: - - spec + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer type: object - type: object - fc: - properties: - fsType: + name: type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - x-kubernetes-list-type: atomic - wwids: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - image: - properties: - pullPolicy: - type: string - reference: - type: string - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - default: default - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - x-kubernetes-list-type: atomic - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - clusterTrustBundle: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - name: - type: string - optional: - type: boolean - path: - type: string - signerName: - type: string - required: - - path - type: object - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - x-kubernetes-list-type: atomic - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - x-kubernetes-list-type: atomic - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - default: /etc/ceph/keyring - type: string - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - pool: - default: rbd - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - user: - default: admin - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - default: xfs - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - sslEnabled: - type: boolean - storageMode: - default: ThinProvisioned - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: + ports: items: properties: - key: - type: string - mode: + containerPort: format: int32 type: integer - path: + hostIP: type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - sidecars: - items: - properties: - args: - items: - type: string - type: array - x-kubernetes-list-type: atomic - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - envFrom: - items: - properties: - configMapRef: - properties: + hostPort: + format: int32 + type: integer name: - default: "" type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - type: string - secretRef: - properties: - name: - default: "" + protocol: + default: TCP type: string - optional: - type: boolean + required: + - containerPort type: object - x-kubernetes-map-type: atomic - type: object - type: array - x-kubernetes-list-type: atomic - image: - type: string - imagePullPolicy: - type: string - lifecycle: - properties: - postStart: + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: properties: exec: properties: @@ -3521,6 +3045,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -3550,14 +3088,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -3570,8 +3109,134 @@ spec: required: - port type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer type: object - preStop: + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: properties: exec: properties: @@ -3581,6 +3246,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -3610,14 +3289,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -3630,455 +3310,869 @@ spec: required: - port type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer type: object - stopSignal: + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: type: string + required: + - name type: object - livenessProbe: + type: array + volumes: + items: properties: - exec: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: properties: - command: + monitors: items: type: string type: array x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors type: object - failureThreshold: - format: int32 - type: integer - grpc: + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: properties: - port: + defaultMode: format: int32 type: integer - service: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: default: "" type: string - required: - - port + optional: + type: boolean type: object - httpGet: + x-kubernetes-map-type: atomic + csi: properties: - host: + driver: type: string - httpHeaders: + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: items: properties: - name: - type: string - value: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic required: - - name - - value + - path type: object type: array x-kubernetes-list-type: atomic - path: + type: object + emptyDir: + properties: + medium: type: string - port: + sizeLimit: anyOf: - type: integer - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - scheme: + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: type: string - required: - - port + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + flexVolume: properties: - host: + driver: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic required: - - port + - driver type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - name: - type: string - ports: - items: - properties: - containerPort: - format: int32 - type: integer - hostIP: - type: string - hostPort: - format: int32 - type: integer - name: - type: string - protocol: - default: TCP - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - properties: - exec: + flocker: properties: - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic + datasetName: + type: string + datasetUUID: + type: string type: object - failureThreshold: - format: int32 - type: integer - grpc: + gcePersistentDisk: properties: - port: + fsType: + type: string + partition: format: int32 type: integer - service: - default: "" + pdName: type: string + readOnly: + type: boolean required: - - port + - pdName type: object - httpGet: + gitRepo: properties: - host: + directory: type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - path: + repository: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + revision: type: string required: - - port + - repository type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + glusterfs: properties: - host: + endpoints: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + path: + type: string + readOnly: + type: boolean required: - - port - type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - resizePolicy: - items: - properties: - resourceName: - type: string - restartPolicy: - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + - endpoints + - path type: object - type: object - restartPolicy: - type: string - securityContext: - properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: + hostPath: properties: - localhostProfile: + path: type: string type: type: string required: - - type + - path type: object - capabilities: + image: properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: items: type: string type: array x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal type: object - privileged: - type: boolean - procMount: + name: type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: + nfs: properties: - level: - type: string - role: + path: type: string - type: + readOnly: + type: boolean + server: type: string - user: + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: type: string + readOnly: + type: boolean + required: + - claimName type: object - seccompProfile: + photonPersistentDisk: properties: - localhostProfile: + fsType: type: string - type: + pdID: type: string required: - - type + - pdID type: object - windowsOptions: + portworxVolume: properties: - gmsaCredentialSpec: + fsType: + type: string + readOnly: + type: boolean + volumeID: type: string - gmsaCredentialSpecName: + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: type: string - hostProcess: + readOnly: type: boolean - runAsUserName: + registry: + type: string + tenant: type: string + user: + type: string + volume: + type: string + required: + - registry + - volume type: object - type: object - startupProbe: - properties: - exec: + rbd: properties: - command: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: items: type: string type: array x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors type: object - failureThreshold: - format: int32 - type: integer - grpc: + scaleIO: properties: - port: - format: int32 - type: integer - service: - default: "" + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: type: string required: - - port + - gateway + - secretRef + - system type: object - httpGet: + secret: properties: - host: - type: string - httpHeaders: + defaultMode: + format: int32 + type: integer + items: items: properties: - name: + key: type: string - value: + mode: + format: int32 + type: integer + path: type: string required: - - name - - value + - key + - path type: object type: array x-kubernetes-list-type: atomic - path: + optional: + type: boolean + secretName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: type: string - required: - - port type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + vsphereVolume: properties: - host: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true required: - - port + - volumePath type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer + required: + - name type: object - stdin: - type: boolean - stdinOnce: - type: boolean - terminationMessagePath: - type: string - terminationMessagePolicy: - type: string - tty: - type: boolean - volumeDevices: - items: - properties: - devicePath: - type: string - name: - type: string - required: - - devicePath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - devicePath - x-kubernetes-list-type: map - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - recursiveReadOnly: - type: string - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - mountPath - x-kubernetes-list-type: map - workingDir: - type: string - required: - - name - type: object - type: array + type: array + type: object + wallet: + properties: + additional: + items: + properties: + mountPath: + type: string + name: + type: string + secret: + type: string + type: object + type: array + mountPath: + type: string + secret: + type: string + type: object type: object status: properties: @@ -4118,7 +4212,7 @@ spec: - type type: object type: array - exporterConfig: + metricsConfig: type: string replicas: type: integer @@ -4128,7 +4222,7 @@ spec: type: string required: - conditions - - exporterConfig + - metricsConfig - version type: object type: object @@ -4137,8 +4231,8 @@ spec: subresources: status: {} - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig + - jsonPath: .status.metricsConfig + name: MetricsConfig type: string - jsonPath: .status.status name: Status @@ -4158,7 +4252,7 @@ spec: type: object spec: properties: - configuration: + azureConfig: properties: configMap: properties: @@ -4170,8 +4264,19 @@ spec: type: object database: properties: + azure: + properties: + vaultID: + type: string + vaultPasswordSecret: + type: string + vaultUsernameSecret: + type: string + type: object dbConnectionString: properties: + envName: + type: string key: type: string secret: @@ -4179,243 +4284,252 @@ spec: type: object dbPassword: properties: + envName: + type: string key: type: string secret: type: string - vaultOCID: - type: string - vaultSecretName: - type: string type: object dbUser: properties: + envName: + type: string key: type: string secret: type: string type: object - dbWallet: + oci: properties: - key: + vaultID: type: string - secret: + vaultPasswordSecret: type: string type: object type: object - exporter: - properties: - deployment: - properties: - args: - items: + databases: + additionalProperties: + properties: + dbConnectionString: + properties: + envName: type: string - type: array - commands: - items: + key: type: string - type: array - env: - additionalProperties: + secret: type: string - type: object - image: - type: string - labels: - additionalProperties: + type: object + dbPassword: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + dbUser: + properties: + envName: + type: string + key: + type: string + secret: type: string + type: object + type: object + type: object + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podSecurityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type type: object - podTemplate: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: properties: - labels: - additionalProperties: - type: string - type: object - securityContext: - properties: - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxChangePolicy: - type: string - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - x-kubernetes-list-type: atomic - supplementalGroupsPolicy: - type: string - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object + level: + type: string + role: + type: string + type: + type: string + user: + type: string type: object - securityContext: + seccompProfile: properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - capabilities: - properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - privileged: - type: boolean - procMount: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object type: object type: object - service: + podTemplate: properties: labels: additionalProperties: type: string type: object - ports: - items: - properties: - appProtocol: - type: string - name: + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP + type: array + x-kubernetes-list-type: atomic + drop: + items: type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + exporterConfig: + properties: + configMap: + properties: + key: + type: string + name: + type: string type: object + mountPath: + type: string type: object inheritLabels: items: @@ -4431,6 +4545,8 @@ spec: type: string volume: properties: + name: + type: string persistentVolumeClaim: properties: claimName: @@ -4438,70 +4554,109 @@ spec: type: object type: object type: object + metrics: + properties: + configMap: + items: + properties: + key: + type: string + name: + type: string + type: object + type: array + type: object ociConfig: properties: - configMapName: - type: string - secretName: + configMap: + properties: + key: + type: string + name: + type: string + type: object + mountPath: type: string + privateKey: + properties: + secret: + type: string + type: object type: object - prometheus: + replicas: + format: int32 + type: integer + service: properties: - serviceMonitor: - properties: - endpoints: - items: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: properties: - authorization: + credentials: properties: - credentials: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: + key: + type: string + name: + default: "" type: string + optional: + type: boolean + required: + - key type: object - basicAuth: + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: properties: - password: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - bearerTokenFile: - type: string - bearerTokenSecret: + x-kubernetes-map-type: atomic + username: properties: key: type: string @@ -4514,98 +4669,87 @@ spec: - key type: object x-kubernetes-map-type: atomic - enableHttp2: - type: boolean - filterRunning: - type: boolean - followRedirects: - type: boolean - honorLabels: - type: boolean - honorTimestamps: - type: boolean - interval: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: type: string - metricRelabelings: - items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object - type: array - oauth2: - properties: - clientId: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: + properties: + configMap: properties: key: type: string @@ -4618,211 +4762,66 @@ spec: - key type: object x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - type: object - noProxy: - type: string - proxyConnectHeader: - additionalProperties: - items: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: array - type: object - x-kubernetes-map-type: atomic - proxyFromEnvironment: - type: boolean - proxyUrl: - pattern: ^(http|https|socks5)://.+$ - type: string - scopes: - items: - type: string - type: array - tlsConfig: + secret: properties: - ca: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - type: boolean - keySecret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - maxVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 + key: type: string - serverName: + name: + default: "" type: string + optional: + type: boolean + required: + - key type: object - tokenUrl: - minLength: 1 + x-kubernetes-map-type: atomic + type: object + clientSecret: + properties: + key: + type: string + name: + default: "" type: string + optional: + type: boolean required: - - clientId - - clientSecret - - tokenUrl + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string type: object - params: + noProxy: + type: string + proxyConnectHeader: additionalProperties: items: - type: string + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic type: array type: object - path: - type: string - port: - type: string + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^(http|https|socks5)://.+$ type: string - relabelings: + scopes: items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object + type: string type: array - scheme: - enum: - - http - - https - type: string - scrapeTimeout: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true tlsConfig: properties: ca: @@ -4854,8 +4853,6 @@ spec: type: object x-kubernetes-map-type: atomic type: object - caFile: - type: string cert: properties: configMap: @@ -4885,12 +4882,8 @@ spec: type: object x-kubernetes-map-type: atomic type: object - certFile: - type: string insecureSkipVerify: type: boolean - keyFile: - type: string keySecret: properties: key: @@ -4907,587 +4900,305 @@ spec: maxVersion: enum: - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - serverName: - type: string - type: object - trackTimestampsStaleness: - type: boolean - type: object - type: array - labels: - additionalProperties: - type: string - type: object - namespaceSelector: - properties: - any: - type: boolean - matchNames: - items: - type: string - type: array - type: object - type: object - type: object - replicas: - format: int32 - type: integer - sidecarVolumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - default: ext4 - type: string - kind: - type: string - readOnly: - default: false - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - default: "" + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + tokenUrl: + minLength: 1 type: string + required: + - clientId + - clientSecret + - tokenUrl type: object - x-kubernetes-map-type: atomic - readOnly: - type: boolean - volumeAttributes: + params: additionalProperties: - type: string + items: + type: string + type: array type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: items: properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 type: integer - path: + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path type: object type: array - x-kubernetes-list-type: atomic - type: object - emptyDir: - properties: - medium: + scheme: + enum: + - http + - https + type: string + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ type: string - sizeLimit: + targetPort: anyOf: - type: integer - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - volumeClaimTemplate: + tlsConfig: properties: - metadata: - type: object - spec: + ca: properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: + configMap: properties: - apiGroup: - type: string - kind: + key: type: string name: + default: "" type: string + optional: + type: boolean required: - - kind - - name + - key type: object x-kubernetes-map-type: atomic - dataSourceRef: + secret: properties: - apiGroup: - type: string - kind: + key: type: string name: + default: "" type: string - namespace: - type: string + optional: + type: boolean required: - - kind - - name + - key type: object - resources: + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - selector: + x-kubernetes-map-type: atomic + secret: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string type: object - required: - - spec - type: object - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - x-kubernetes-list-type: atomic - wwids: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" + certFile: type: string - type: object - x-kubernetes-map-type: atomic - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - image: - properties: - pullPolicy: - type: string - reference: - type: string - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - default: default - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - x-kubernetes-list-type: atomic - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" + insecureSkipVerify: + type: boolean + keyFile: type: string - type: object - x-kubernetes-map-type: atomic - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: + keySecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + type: string + type: object + trackTimestampsStaleness: type: boolean - volumeID: - type: string - required: - - volumeID type: object - projected: + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: + type: string + type: array + type: object + type: object + sidecar: + properties: + containers: + items: properties: - defaultMode: - format: int32 - type: integer - sources: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: items: properties: - clusterTrustBundle: + name: + type: string + value: + type: string + valueFrom: properties: - labelSelector: + configMapKeyRef: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic - name: - type: string - optional: - type: boolean - path: - type: string - signerName: - type: string - required: - - path type: object - configMap: + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic name: default: "" type: string @@ -5495,66 +5206,10 @@ spec: type: boolean type: object x-kubernetes-map-type: atomic - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - x-kubernetes-list-type: atomic - type: object - secret: + prefix: + type: string + secretRef: properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic name: default: "" type: string @@ -5562,283 +5217,246 @@ spec: type: boolean type: object x-kubernetes-map-type: atomic - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object type: object type: array x-kubernetes-list-type: atomic - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string image: type: string - keyring: - default: /etc/ceph/keyring - type: string - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - pool: - default: rbd + imagePullPolicy: type: string - readOnly: - type: boolean - secretRef: + lifecycle: properties: - name: - default: "" + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: type: string type: object - x-kubernetes-map-type: atomic - user: - default: admin - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - default: xfs - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: + livenessProbe: properties: - name: - default: "" - type: string + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer type: object - x-kubernetes-map-type: atomic - sslEnabled: - type: boolean - storageMode: - default: ThinProvisioned - type: string - storagePool: - type: string - system: - type: string - volumeName: + name: type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: + ports: items: properties: - key: - type: string - mode: + containerPort: format: int32 type: integer - path: + hostIP: type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - sidecars: - items: - properties: - args: - items: - type: string - type: array - x-kubernetes-list-type: atomic - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - envFrom: - items: - properties: - configMapRef: - properties: + hostPort: + format: int32 + type: integer name: - default: "" type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - type: string - secretRef: - properties: - name: - default: "" + protocol: + default: TCP type: string - optional: - type: boolean + required: + - containerPort type: object - x-kubernetes-map-type: atomic - type: object - type: array - x-kubernetes-list-type: atomic - image: - type: string - imagePullPolicy: - type: string - lifecycle: - properties: - postStart: + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: properties: exec: properties: @@ -5848,6 +5466,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -5877,14 +5509,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -5897,8 +5530,134 @@ spec: required: - port type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object - preStop: + startupProbe: properties: exec: properties: @@ -5908,6 +5667,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -5937,14 +5710,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -5957,455 +5731,869 @@ spec: required: - port type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer type: object - stopSignal: + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: type: string + required: + - name type: object - livenessProbe: + type: array + volumes: + items: properties: - exec: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: properties: - command: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: items: type: string type: array x-kubernetes-list-type: atomic type: object - failureThreshold: - format: int32 - type: integer - grpc: + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: properties: - port: + fsType: + type: string + partition: format: int32 type: integer - service: - default: "" + pdName: type: string + readOnly: + type: boolean required: - - port + - pdName type: object - httpGet: + gitRepo: properties: - host: + directory: type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - path: + repository: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + revision: type: string required: - - port + - repository type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + glusterfs: properties: - host: + endpoints: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + path: + type: string + readOnly: + type: boolean required: - - port + - endpoints + - path type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - name: - type: string - ports: - items: - properties: - containerPort: - format: int32 - type: integer - hostIP: - type: string - hostPort: - format: int32 - type: integer - name: - type: string - protocol: - default: TCP - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - properties: - exec: + hostPath: properties: - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic + path: + type: string + type: + type: string + required: + - path type: object - failureThreshold: - format: int32 - type: integer - grpc: + image: properties: - port: - format: int32 - type: integer - service: - default: "" + pullPolicy: + type: string + reference: type: string - required: - - port type: object - httpGet: + iscsi: properties: - host: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: type: string - httpHeaders: + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object + type: string type: array x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: path: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + readOnly: + type: boolean + server: type: string required: - - port + - path + - server type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + persistentVolumeClaim: properties: - host: + claimName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + readOnly: + type: boolean required: - - port - type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - resizePolicy: - items: - properties: - resourceName: - type: string - restartPolicy: - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + - claimName type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID type: object - type: object - restartPolicy: - type: string - securityContext: - properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: + portworxVolume: properties: - localhostProfile: + fsType: type: string - type: + readOnly: + type: boolean + volumeID: type: string required: - - type + - volumeID type: object - capabilities: + projected: properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: + defaultMode: + format: int32 + type: integer + sources: items: - type: string + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object type: array x-kubernetes-list-type: atomic type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: + quobyte: properties: - level: + group: type: string - role: + readOnly: + type: boolean + registry: type: string - type: + tenant: type: string user: type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: + volume: type: string required: - - type + - registry + - volume type: object - windowsOptions: + rbd: properties: - gmsaCredentialSpec: + fsType: type: string - gmsaCredentialSpecName: + image: type: string - hostProcess: - type: boolean - runAsUserName: + keyring: + default: /etc/ceph/keyring type: string - type: object - type: object - startupProbe: - properties: - exec: - properties: - command: + monitors: items: type: string type: array x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors type: object - failureThreshold: - format: int32 - type: integer - grpc: + scaleIO: properties: - port: - format: int32 - type: integer - service: - default: "" + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: type: string required: - - port + - gateway + - secretRef + - system type: object - httpGet: + secret: properties: - host: - type: string - httpHeaders: + defaultMode: + format: int32 + type: integer + items: items: properties: - name: + key: type: string - value: + mode: + format: int32 + type: integer + path: type: string required: - - name - - value + - key + - path type: object type: array x-kubernetes-list-type: atomic - path: + optional: + type: boolean + secretName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: type: string - required: - - port type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + vsphereVolume: properties: - host: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true required: - - port + - volumePath type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer + required: + - name type: object - stdin: - type: boolean - stdinOnce: - type: boolean - terminationMessagePath: - type: string - terminationMessagePolicy: - type: string - tty: - type: boolean - volumeDevices: - items: - properties: - devicePath: - type: string - name: - type: string - required: - - devicePath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - devicePath - x-kubernetes-list-type: map - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - recursiveReadOnly: - type: string - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - mountPath - x-kubernetes-list-type: map - workingDir: - type: string - required: - - name - type: object - type: array + type: array + type: object + wallet: + properties: + additional: + items: + properties: + mountPath: + type: string + name: + type: string + secret: + type: string + type: object + type: array + mountPath: + type: string + secret: + type: string + type: object type: object status: properties: @@ -6445,7 +6633,7 @@ spec: - type type: object type: array - exporterConfig: + metricsConfig: type: string replicas: type: integer @@ -6455,7 +6643,7 @@ spec: type: string required: - conditions - - exporterConfig + - metricsConfig - version type: object type: object @@ -6464,8 +6652,8 @@ spec: subresources: status: {} - additionalPrinterColumns: - - jsonPath: .status.exporterConfig - name: ExporterConfig + - jsonPath: .status.metricsConfig + name: MetricsConfig type: string - jsonPath: .status.status name: Status @@ -6485,7 +6673,7 @@ spec: type: object spec: properties: - configuration: + azureConfig: properties: configMap: properties: @@ -6497,8 +6685,19 @@ spec: type: object database: properties: + azure: + properties: + vaultID: + type: string + vaultPasswordSecret: + type: string + vaultUsernameSecret: + type: string + type: object dbConnectionString: properties: + envName: + type: string key: type: string secret: @@ -6506,243 +6705,252 @@ spec: type: object dbPassword: properties: + envName: + type: string key: type: string secret: type: string - vaultOCID: - type: string - vaultSecretName: - type: string type: object dbUser: properties: + envName: + type: string key: type: string secret: type: string type: object - dbWallet: + oci: properties: - key: + vaultID: type: string - secret: + vaultPasswordSecret: type: string type: object type: object - exporter: - properties: - deployment: - properties: - args: - items: + databases: + additionalProperties: + properties: + dbConnectionString: + properties: + envName: type: string - type: array - commands: - items: + key: type: string - type: array - env: - additionalProperties: + secret: + type: string + type: object + dbPassword: + properties: + envName: + type: string + key: type: string + secret: + type: string + type: object + dbUser: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + type: object + type: object + deployment: + properties: + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string + type: object + image: + type: string + labels: + additionalProperties: + type: string + type: object + podSecurityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type type: object - image: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: type: string - labels: - additionalProperties: - type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string type: object - podTemplate: + seccompProfile: properties: - labels: - additionalProperties: - type: string - type: object - securityContext: - properties: - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxChangePolicy: - type: string - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - x-kubernetes-list-type: atomic - supplementalGroupsPolicy: - type: string - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object + localhostProfile: + type: string + type: + type: string + required: + - type type: object - securityContext: + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - capabilities: - properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - privileged: - type: boolean - procMount: + gmsaCredentialSpec: type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: + gmsaCredentialSpecName: + type: string + hostProcess: type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object + runAsUserName: + type: string type: object type: object - service: + podTemplate: properties: labels: additionalProperties: type: string type: object - ports: - items: - properties: - appProtocol: - type: string - name: + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP + type: array + x-kubernetes-list-type: atomic + drop: + items: type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + type: object + exporterConfig: + properties: + configMap: + properties: + key: + type: string + name: + type: string type: object + mountPath: + type: string type: object inheritLabels: items: @@ -6758,6 +6966,8 @@ spec: type: string volume: properties: + name: + type: string persistentVolumeClaim: properties: claimName: @@ -6765,41 +6975,202 @@ spec: type: object type: object type: object + metrics: + properties: + configMap: + items: + properties: + key: + type: string + name: + type: string + type: object + type: array + type: object ociConfig: properties: - configMapName: - type: string - secretName: + configMap: + properties: + key: + type: string + name: + type: string + type: object + mountPath: type: string + privateKey: + properties: + secret: + type: string + type: object type: object - prometheus: + replicas: + format: int32 + type: integer + service: properties: - serviceMonitor: - properties: - endpoints: - items: + labels: + additionalProperties: + type: string + type: object + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + type: object + serviceMonitor: + properties: + endpoints: + items: + properties: + authorization: properties: - authorization: + credentials: properties: - credentials: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + type: string + type: object + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - basicAuth: + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + type: string + bearerTokenSecret: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + type: boolean + filterRunning: + type: boolean + followRedirects: + type: boolean + honorLabels: + type: boolean + honorTimestamps: + type: boolean + interval: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + items: + properties: + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 + type: integer + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + type: string + type: object + type: array + oauth2: + properties: + clientId: properties: - password: + configMap: properties: key: type: string @@ -6812,7 +7183,7 @@ spec: - key type: object x-kubernetes-map-type: atomic - username: + secret: properties: key: type: string @@ -6826,9 +7197,7 @@ spec: type: object x-kubernetes-map-type: atomic type: object - bearerTokenFile: - type: string - bearerTokenSecret: + clientSecret: properties: key: type: string @@ -6841,98 +7210,15 @@ spec: - key type: object x-kubernetes-map-type: atomic - enableHttp2: - type: boolean - filterRunning: - type: boolean - followRedirects: - type: boolean - honorLabels: - type: boolean - honorTimestamps: - type: boolean - interval: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + endpointParams: + additionalProperties: + type: string + type: object + noProxy: type: string - metricRelabelings: - items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object - type: array - oauth2: - properties: - clientId: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: + proxyConnectHeader: + additionalProperties: + items: properties: key: type: string @@ -6945,211 +7231,18 @@ spec: - key type: object x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - type: object - noProxy: - type: string - proxyConnectHeader: - additionalProperties: - items: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: array - type: object - x-kubernetes-map-type: atomic - proxyFromEnvironment: - type: boolean - proxyUrl: - pattern: ^(http|https|socks5)://.+$ - type: string - scopes: - items: - type: string - type: array - tlsConfig: - properties: - ca: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - properties: - configMap: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - type: boolean - keySecret: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - maxVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - serverName: - type: string - type: object - tokenUrl: - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - params: - additionalProperties: - items: - type: string type: array type: object - path: - type: string - port: - type: string + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^(http|https|socks5)://.+$ type: string - relabelings: + scopes: items: - properties: - action: - default: replace - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - format: int64 - type: integer - regex: - type: string - replacement: - type: string - separator: - type: string - sourceLabels: - items: - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - type: string - type: object + type: string type: array - scheme: - enum: - - http - - https - type: string - scrapeTimeout: - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true tlsConfig: properties: ca: @@ -7181,8 +7274,6 @@ spec: type: object x-kubernetes-map-type: atomic type: object - caFile: - type: string cert: properties: configMap: @@ -7212,12 +7303,8 @@ spec: type: object x-kubernetes-map-type: atomic type: object - certFile: - type: string insecureSkipVerify: type: boolean - keyFile: - type: string keySecret: properties: key: @@ -7245,576 +7332,294 @@ spec: - TLS12 - TLS13 type: string - serverName: - type: string - type: object - trackTimestampsStaleness: - type: boolean - type: object - type: array - labels: - additionalProperties: - type: string - type: object - namespaceSelector: - properties: - any: - type: boolean - matchNames: - items: - type: string - type: array - type: object - type: object - type: object - replicas: - format: int32 - type: integer - sidecarVolumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - default: ext4 - type: string - kind: - type: string - readOnly: - default: false - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" - type: string - type: object - x-kubernetes-map-type: atomic - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - default: "" + serverName: + type: string + type: object + tokenUrl: + minLength: 1 type: string + required: + - clientId + - clientSecret + - tokenUrl type: object - x-kubernetes-map-type: atomic - readOnly: - type: boolean - volumeAttributes: + params: additionalProperties: - type: string + items: + type: string + type: array type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: + path: + type: string + port: + type: string + proxyUrl: + type: string + relabelings: items: properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 + action: + default: replace + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + format: int64 type: integer - path: + regex: + type: string + replacement: + type: string + separator: + type: string + sourceLabels: + items: + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path type: object type: array - x-kubernetes-list-type: atomic - type: object - emptyDir: - properties: - medium: + scheme: + enum: + - http + - https type: string - sizeLimit: + scrapeTimeout: + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: anyOf: - type: integer - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - volumeClaimTemplate: + tlsConfig: properties: - metadata: - type: object - spec: + ca: properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: + configMap: properties: - apiGroup: - type: string - kind: + key: type: string name: + default: "" type: string + optional: + type: boolean required: - - kind - - name + - key type: object x-kubernetes-map-type: atomic - dataSourceRef: + secret: properties: - apiGroup: - type: string - kind: + key: type: string name: + default: "" type: string - namespace: - type: string + optional: + type: boolean required: - - kind - - name + - key type: object - resources: + x-kubernetes-map-type: atomic + type: object + caFile: + type: string + cert: + properties: + configMap: properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object - selector: + x-kubernetes-map-type: atomic + secret: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: + type: object + certFile: + type: string + insecureSkipVerify: + type: boolean + keyFile: + type: string + keySecret: + properties: + key: type: string - volumeName: + name: + default: "" type: string + optional: + type: boolean + required: + - key type: object - required: - - spec - type: object - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - x-kubernetes-list-type: atomic - wwids: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" + x-kubernetes-map-type: atomic + maxVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 type: string - type: object - x-kubernetes-map-type: atomic - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - image: - properties: - pullPolicy: - type: string - reference: - type: string - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - default: default - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - x-kubernetes-list-type: atomic - readOnly: - type: boolean - secretRef: - properties: - name: - default: "" + minVersion: + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: type: string type: object - x-kubernetes-map-type: atomic - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: + trackTimestampsStaleness: type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: + type: array + labels: + additionalProperties: + type: string + type: object + namespaceSelector: + properties: + any: + type: boolean + matchNames: + items: type: string - required: - - volumeID - type: object - projected: + type: array + type: object + type: object + sidecar: + properties: + containers: + items: properties: - defaultMode: - format: int32 - type: integer - sources: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: items: properties: - clusterTrustBundle: + name: + type: string + value: + type: string + valueFrom: properties: - labelSelector: + configMapKeyRef: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic - name: - type: string - optional: - type: boolean - path: - type: string - signerName: - type: string - required: - - path type: object - configMap: + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic name: default: "" type: string @@ -7822,66 +7627,10 @@ spec: type: boolean type: object x-kubernetes-map-type: atomic - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - x-kubernetes-list-type: atomic - type: object - secret: + prefix: + type: string + secretRef: properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - x-kubernetes-list-type: atomic name: default: "" type: string @@ -7889,283 +7638,447 @@ spec: type: boolean type: object x-kubernetes-map-type: atomic - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object type: object type: array x-kubernetes-list-type: atomic - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string image: type: string - keyring: - default: /etc/ceph/keyring - type: string - monitors: - items: - type: string - type: array - x-kubernetes-list-type: atomic - pool: - default: rbd + imagePullPolicy: type: string - readOnly: - type: boolean - secretRef: + lifecycle: properties: - name: - default: "" + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: type: string type: object - x-kubernetes-map-type: atomic - user: - default: admin - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - default: xfs - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: + livenessProbe: properties: - name: - default: "" - type: string + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer type: object - x-kubernetes-map-type: atomic - sslEnabled: - type: boolean - storageMode: - default: ThinProvisioned - type: string - storagePool: - type: string - system: - type: string - volumeName: + name: type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: + ports: items: properties: - key: + containerPort: + format: int32 + type: integer + hostIP: type: string - mode: + hostPort: format: int32 type: integer - path: + name: + type: string + protocol: + default: TCP type: string required: - - key - - path + - containerPort type: object type: array - x-kubernetes-list-type: atomic - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: properties: - name: - default: "" - type: string + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer type: object - x-kubernetes-map-type: atomic - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - sidecars: - items: - properties: - args: - items: - type: string - type: array - x-kubernetes-list-type: atomic - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: + resizePolicy: + items: properties: - configMapKeyRef: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: properties: - key: - type: string name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - properties: - containerName: type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: + request: type: string required: - - resource + - name type: object - x-kubernetes-map-type: atomic - secretKeyRef: - properties: - key: + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: type: string - name: - default: "" + type: array + x-kubernetes-list-type: atomic + drop: + items: type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - envFrom: - items: - properties: - configMapRef: - properties: - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - type: string - secretRef: - properties: - name: - default: "" - type: string - optional: - type: boolean - type: object - x-kubernetes-map-type: atomic - type: object - type: array - x-kubernetes-list-type: atomic - image: - type: string - imagePullPolicy: - type: string - lifecycle: - properties: - postStart: + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: properties: exec: properties: @@ -8175,6 +8088,20 @@ spec: type: array x-kubernetes-list-type: atomic type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object httpGet: properties: host: @@ -8204,14 +8131,15 @@ spec: required: - port type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer tcpSocket: properties: host: @@ -8224,515 +8152,869 @@ spec: required: - port type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID type: object - preStop: + azureDisk: properties: - exec: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: properties: - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic + name: + default: "" + type: string type: object - httpGet: + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: properties: - host: + name: + default: "" type: string - httpHeaders: - items: + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: properties: - name: + apiVersion: type: string - value: + fieldPath: type: string required: - - name - - value + - fieldPath type: object - type: array - x-kubernetes-list-type: atomic - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - sleep: - properties: - seconds: - format: int64 - type: integer - required: - - seconds - type: object - tcpSocket: + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object required: - - port + - spec type: object type: object - stopSignal: - type: string - type: object - livenessProbe: - properties: - exec: + fc: properties: - command: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: items: type: string type: array x-kubernetes-list-type: atomic type: object - failureThreshold: - format: int32 - type: integer - grpc: + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: properties: - port: + fsType: + type: string + partition: format: int32 type: integer - service: - default: "" + pdName: type: string + readOnly: + type: boolean required: - - port + - pdName type: object - httpGet: + gitRepo: properties: - host: + directory: type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - path: + repository: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + revision: type: string required: - - port + - repository type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + glusterfs: properties: - host: + endpoints: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + path: + type: string + readOnly: + type: boolean required: - - port + - endpoints + - path type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - name: - type: string - ports: - items: - properties: - containerPort: - format: int32 - type: integer - hostIP: - type: string - hostPort: - format: int32 - type: integer - name: - type: string - protocol: - default: TCP - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - properties: - exec: + hostPath: properties: - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic + path: + type: string + type: + type: string + required: + - path type: object - failureThreshold: - format: int32 - type: integer - grpc: + image: properties: - port: - format: int32 - type: integer - service: - default: "" + pullPolicy: + type: string + reference: type: string - required: - - port type: object - httpGet: + iscsi: properties: - host: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: type: string - httpHeaders: + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object + type: string type: array x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: path: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + readOnly: + type: boolean + server: type: string required: - - port + - path + - server type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + persistentVolumeClaim: properties: - host: + claimName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true + readOnly: + type: boolean required: - - port - type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer - type: object - resizePolicy: - items: - properties: - resourceName: - type: string - restartPolicy: - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + - claimName type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID type: object - type: object - restartPolicy: - type: string - securityContext: - properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: + portworxVolume: properties: - localhostProfile: + fsType: type: string - type: + readOnly: + type: boolean + volumeID: type: string required: - - type + - volumeID type: object - capabilities: + projected: properties: - add: - items: - type: string - type: array - x-kubernetes-list-type: atomic - drop: + defaultMode: + format: int32 + type: integer + sources: items: - type: string + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object type: array x-kubernetes-list-type: atomic type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: + quobyte: properties: - level: + group: type: string - role: + readOnly: + type: boolean + registry: type: string - type: + tenant: type: string user: type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: + volume: type: string required: - - type + - registry + - volume type: object - windowsOptions: + rbd: properties: - gmsaCredentialSpec: + fsType: type: string - gmsaCredentialSpecName: + image: type: string - hostProcess: - type: boolean - runAsUserName: + keyring: + default: /etc/ceph/keyring type: string - type: object - type: object - startupProbe: - properties: - exec: - properties: - command: + monitors: items: type: string type: array x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors type: object - failureThreshold: - format: int32 - type: integer - grpc: + scaleIO: properties: - port: - format: int32 - type: integer - service: - default: "" + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: type: string required: - - port + - gateway + - secretRef + - system type: object - httpGet: + secret: properties: - host: - type: string - httpHeaders: + defaultMode: + format: int32 + type: integer + items: items: properties: - name: + key: type: string - value: + mode: + format: int32 + type: integer + path: type: string required: - - name - - value + - key + - path type: object type: array x-kubernetes-list-type: atomic - path: + optional: + type: boolean + secretName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: type: string - required: - - port type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + vsphereVolume: properties: - host: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true required: - - port + - volumePath type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer + required: + - name type: object - stdin: - type: boolean - stdinOnce: - type: boolean - terminationMessagePath: - type: string - terminationMessagePolicy: - type: string - tty: - type: boolean - volumeDevices: - items: - properties: - devicePath: - type: string - name: - type: string - required: - - devicePath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - devicePath - x-kubernetes-list-type: map - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - recursiveReadOnly: - type: string - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - x-kubernetes-list-map-keys: - - mountPath - x-kubernetes-list-type: map - workingDir: - type: string - required: - - name - type: object - type: array + type: array + type: object + wallet: + properties: + additional: + items: + properties: + mountPath: + type: string + name: + type: string + secret: + type: string + type: object + type: array + mountPath: + type: string + secret: + type: string + type: object type: object status: properties: @@ -8772,7 +9054,7 @@ spec: - type type: object type: array - exporterConfig: + metricsConfig: type: string replicas: type: integer @@ -8782,7 +9064,7 @@ spec: type: string required: - conditions - - exporterConfig + - metricsConfig - version type: object type: object @@ -14423,6 +14705,13 @@ spec: catalog: items: properties: + catalogConfigData: + properties: + mountPath: + type: string + name: + type: string + type: object envVars: items: properties: @@ -14523,9 +14812,14 @@ spec: type: string pwdFileName: type: string + tdeKeyFileName: + type: string + tdePwdFileName: + type: string required: - name - pwdFileName + - tdePwdFileName type: object fssStorageClass: type: string @@ -14546,6 +14840,13 @@ spec: - value type: object type: array + gsmConfigData: + properties: + mountPath: + type: string + name: + type: string + type: object imagePullPolicy: type: string isDelete: @@ -14750,6 +15051,8 @@ spec: type: string scriptsLocation: type: string + serviceAccountName: + type: string shard: items: properties: @@ -14827,6 +15130,13 @@ spec: x-kubernetes-int-or-string: true type: object type: object + shardConfigData: + properties: + mountPath: + type: string + name: + type: string + type: object shardGroup: type: string shardRegion: @@ -14844,6 +15154,47 @@ spec: type: string shardConfigName: type: string + shardInfo: + items: + properties: + replicas: + format: int32 + type: integer + shape: + type: string + shardGroupDetails: + properties: + ShardSpace: + type: string + deployAs: + type: string + isDelete: + type: string + region: + type: string + repFactor: + type: integer + shardGroupName: + type: string + type: object + shardPreFixName: + type: string + shardSpaceDetails: + properties: + Chnuks: + type: integer + protectMode: + type: string + shardSpaceName: + type: string + type: object + storageSizeInGb: + format: int32 + type: integer + required: + - shardPreFixName + type: object + type: array shardRegion: items: type: string @@ -14865,7 +15216,6 @@ spec: - dbImage - gsm - gsmImage - - shard type: object status: properties: @@ -16292,26 +16642,6 @@ webhooks: resources: - oraclerestdataservices sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: mshardingdatabasev1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -16709,27 +17039,6 @@ webhooks: resources: - oraclerestdataservices sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v1alpha1-shardingdatabase - failurePolicy: Fail - name: vshardingdatabasev1alpha1.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - - DELETE - resources: - - shardingdatabases - sideEffects: None - admissionReviewVersions: - v1 - v1beta1 @@ -16818,7 +17127,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:Test-sharding-operator-V4 imagePullPolicy: Always name: manager ports: From fd30c2049cf9bc53029793ca7bcbabf6f7b9e449 Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Fri, 15 Aug 2025 16:29:14 +0000 Subject: [PATCH 265/414] fix in documentation for basedb --- README.md | 145 +++++++++++++++++++------------------------- docs/dbcs/README.md | 8 +-- 2 files changed, 66 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index ba10691d..7ec52d13 100644 --- a/README.md +++ b/README.md @@ -4,107 +4,86 @@ As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released the _Oracle Database Operator for Kubernetes_ (`OraOperator` or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating the management of the Oracle Database lifecycle. -## Supported Database Configurations in V2.0 -In this v2.0 production release, `OraOperator` supports the following database configurations, and controllers: +## Supported Database Configurations v2.0.0 +In this v2.0 production release, `OraOperator` supports the following database configurations and controllers: -* Oracle Autonomous Database: +* **Oracle Autonomous Database:** * Oracle Autonomous Database shared Oracle Cloud Infrastructure (OCI) (ADB-S) * Oracle Autonomous Database on dedicated Cloud infrastructure (ADB-D) - * Oracle Autonomous Container Database (ACD), the infrastructure for provisioning Autonomous Databases. -* Containerized Single Instance databases (SIDB) deployed in the Oracle Kubernetes Engine (OKE) and any k8s where OraOperator is deployed -* Containerized Oracle Globally Distributed Databases(GDD) deployed in OKE and any k8s where OraOperator is deployed -* Oracle Multitenant Databases (CDB/PDBs) -* Oracle Base Database Service (OBDS) on Oracle Cloud Infrastructure (OCI) -* Oracle Data Guard -* Oracle Database Observability -* Oracle Database Rest Service (ORDS) instances -* Oracle Restart - -## New Lifecycle Features in V2.0 Release (Controllers Enhancements) -* ORDSSERVICES + * Oracle Autonomous Container Database (ACD), the infrastructure for provisioning Autonomous Databases +* **Containerized Single Instance databases (SIDB)** deployed in the Oracle Kubernetes Engine (OKE) and any Kubernetes where OraOperator is deployed +* **Containerized Oracle Globally Distributed Databases (GDD)** deployed in OKE and any Kubernetes where OraOperator is deployed +* **Oracle Multitenant Databases (CDB/PDBs)** +* **Oracle Base Database Service (OBDS)** on Oracle Cloud Infrastructure (OCI) +* **Oracle Data Guard** +* **Oracle Database Observability** +* **Oracle Database Rest Service (ORDS) instances** +* **Oracle Restart** +* **Oracle Private AI Controller** +* **Oracle Sharding** + +--- + +## Lifecycle Features (Controllers Enhancements) +* **ORDS Services** - Install on SIDB and ADB - - Provision and Delete ORDS instances -* SIDB + - Provision and delete ORDS instances +* **SIDB** - Oracle Database 23ai Free support - Oracle Database 23ai Free-lite support - SIDB resource management - True Cache support for Free SIDB databases (Preview) - Observer for FastStartFailover with Data Guard - Snapshot Standby support in Data Guard setup -* Globally Distributed Database : Support for Oracle Database 23ai Raft replication -* Autonomous Database: support for Database cloning -* Multitenant DB: - - ORDS-based Controller: assertive deletion policy. - - New LRES based Controller (ARM & AM) +* **Globally Distributed Database** + - Support for Oracle Database 23ai Raft replication +* **Autonomous Database** + - Support for Database cloning +* **Multitenant DB** + - ORDS-based Controller: Assertive deletion policy + - New LRES-based Controller (ARM & AM) - PDBs settings with init parameters config map - - Assertive deletion policy. -* Database Observability (preview) + - Assertive deletion policy +* **Database Observability (Preview)** - Support for Database Logs (in addition to Metrics) - Support for the latest Exporter container images - -* Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation, Clone, Backup, Restore, Dataguard Setup, Patching and Upgrade. -* Prometheus label config (bug fix) - -## New Product Features -*The Operator itself, as a product, brings the following new features: + - Bug Fix: Prometheus label config +* **Oracle Base Database Service** + - Support for Oracle Database 23ai Cloning, using KMS Vaults + - PDB creation + - Clone, Backup, Restore + - Data Guard Setup + - Patching and Upgrade +* **Oracle Restart** + - Support for Oracle Database 19c +* **Oracle Private AI** + +--- + +## Product Features +* Upgraded Kubernetes API version to v4 * Published on `operatorhub.io` * Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) * Validated on Google Kubernetes Engine -## Overall Features Summary -* Oracle Database Rest Service (ORDS) instances - -## New Product Features in V2.0 -The Operator itself, as a product, brings the following new features: -* Upgraded Kubernetes API version to V4 -* Published on `operatorhub.io` -* Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) -* Validated on Google Kubernetes Engine - -## New Services and Lifecycle Enhancements (Controllers Enhancements) in V2.0 -* ORDSSERVICES - - Install on SIDB and ADB - - Provision and Delete ORDS instances -* SIDB - - Oracle Database 23ai Free support - - Oracle Database 23ai Free-lite support - - SIDB resource management - - True Cache support for Free SIDB databases (Preview) - - Observer for FastStartFailover with Data Guard - - Snapshot Standby support in Data Guard setup -* Globally Distributed Database : Support for Oracle Database 23ai Raft replication -* Autonomous Database: support for Database cloning -* Multitenant DB: - - ORDS-based Controller: assertive deletion policy. - - New LRES based Controller (ARM & AM) - - PDBs settings with init parameters config map - - Assertive deletion policy. -* Database Observability (preview) - - Support for Database Logs (in addition to Metrics) - - Support for the latest Exporter container images - - Bug Fix: Prometheus label config -* Oracle Base Database Service: support for Oracle Database 23ai Cloning, using KMS Vaults, PDB creation. - -## New Product Features -* The Operator itself, as a product, brings the following new features: -* Published on `operatorhub.io` -* Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) -* Validated on Google Kubernetes Engine +--- ## Overall Features Summary - -As of release v2.0, the Oracle Database Operator for Kubernetes ( OraOperator) supports the following lifecycle operations: - -* ADB-S/ADB-D: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning. -* ACD: Provision, bind, restart, terminate (soft/hard) -* SIDB: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (basic console), Oracle REST Data Service (ORDS) to support REST based SQL, PDB management, SQL Developer Web, Application Express (Apex), Resource management, True Cache, Observer for FastStartFailover (Data Guard), and Snapshot Standby (Data Guard) -* ORDS Services: Provision and delete ORDS instances -* Globally Distrib. (Sharded): Provision/deploy sharded databases and the shard topology, Add a new shard, Delete an existing shard, Raft replication. -* Oracle Multitenant Database (choice of controller): Bind to a CDB, Create a  PDB, Plug a  PDB, Unplug a PDB, Delete a PDB, Clone a PDB, Open/Close a PDB, Assertive deletion policy -* Oracle Base Database Service (OBDS): Provision, Scale Shape Up/Down, Scale Storage Up, Terminate and Update License, Cloning, PDB creation, using KMS Vaults on Oracle Cloud Infrastructure (OCI), Clone, Backup, Restore, Dataguard Setup, Patching and Upgrade -* Oracle Data Guard: Provision a Standby for the SIDB resource, Create a Data Guard Configuration, Perform a Switchover, Patch Primary and Standby databases in Data Guard Configuration -* Oracle Database Observability: create, patch, delete `databaseObserver` resources (Logs and Metrics) -* Watch over a set of namespaces or all the namespaces in the cluster using the `WATCH_NAMESPACE` environment variable of the operator deployment +As of v2.0.0, the Oracle Database Operator for Kubernetes (`OraOperator`) supports the following lifecycle operations: + +* **ADB-S/ADB-D**: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning +* **ACD**: Provision, bind, restart, terminate (soft/hard) +* **SIDB**: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (basic console), Oracle REST Data Service (ORDS), PDB management, SQL Developer Web, Application Express (Apex), Resource management, True Cache, Observer for FastStartFailover (Data Guard), Snapshot Standby (Data Guard) +* **Oracle Restart**: Provision and manage Oracle Restart environments +* **ORDS Services**: Provision and delete ORDS instances +* **Globally Distributed (Sharded)**: Provision/deploy sharded databases and topology, add/delete shards, Raft replication +* **Oracle Multitenant Database**: Bind to a CDB, create/plug/unplug/delete/clone/open/close PDBs, assertive deletion policy +* **Oracle Base Database Service (OBDS)**: Provision, scale shape/storage, terminate/update license, cloning, PDB creation, using KMS Vaults, backup/restore, Data Guard setup, patching/upgrade +* **Oracle Data Guard**: Provision standby for SIDB, create Data Guard configuration, perform switchover, patch primary/standby +* **Oracle Database Observability**: Create, patch, delete `databaseObserver` (logs and metrics) +* **Oracle Restart**: Provision, add & delete asm disks, enable ons ports, custom storage class support, existing pvc support +* **Oracle Private AI** +* Watch namespaces using the `WATCH_NAMESPACE` environment variable ## Release Status @@ -237,7 +216,7 @@ The following quickstarts are designed for specific database configurations: * [Oracle Multitenant Database](./docs/multitenant/README.md) * [Oracle Base Database Service (OBDS)](./docs/dbcs/README.md) * [ORDS Services (ORDSSRVS)](./docs/ordsservices/README.md) -* [Oracle Restart Database](./docs/oraclerestart/README.md) +* [Oracle Restart](./docs/oraclerestart/README.md) The following quickstart is designed for non-database configurations: diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md index 9b64d30a..4be4e95a 100644 --- a/docs/dbcs/README.md +++ b/docs/dbcs/README.md @@ -108,21 +108,21 @@ kubectl create secret generic oci-privatekey --from-file=privatekey=/root/.oci/o ``` -## 3. Create a Kubernetes secret named `admin-password`; This passward must meet the minimum passward requirements for the OCI OBDS Service. +## 3. Create a Kubernetes secret named `admin-password`; This password must meet the minimum password requirements for the OCI OBDS Service. For example: ```bash -#-- assuming the passward has been added to a text file named "admin-password": +#-- assuming the password has been added to a text file named "admin-password": kubectl create secret generic admin-password --from-file=./admin-password -n default ``` -## 4. Create a Kubernetes secret named `tde-password`; this passward must meet the minimum passward requirements for the OCI OBDS Service. +## 4. Create a Kubernetes secret named `tde-password`; this password must meet the minimum password requirements for the OCI OBDS Service. For example: ```bash -# -- assuming the passward has been added to a text file named "tde-password": +# -- assuming the password has been added to a text file named "tde-password": kubectl create secret generic tde-password --from-file=./tde-password -n default ``` From 35a89d3d1adbc8a5169ad8e669eb6360773c7767 Mon Sep 17 00:00:00 2001 From: Geela Rajahimansh Date: Fri, 15 Aug 2025 18:03:40 +0000 Subject: [PATCH 266/414] Feature/psaini update --- apis/omlai/v4/privateai_types.go | 3 +- apis/omlai/v4/privateai_webhook.go | 30 ++++-- commons/omlai/aibuilder.go | 97 ++++++++++++++++++- .../bases/omlai.oracle.com_privateais.yaml | 1 + go.mod | 2 +- oracle-database-operator.yaml | 1 + 6 files changed, 122 insertions(+), 12 deletions(-) diff --git a/apis/omlai/v4/privateai_types.go b/apis/omlai/v4/privateai_types.go index d3d0ecf4..358a2943 100644 --- a/apis/omlai/v4/privateai_types.go +++ b/apis/omlai/v4/privateai_types.go @@ -50,7 +50,8 @@ import ( type PrivateAiSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - PaiConfigFile *PaiConfigMap `json:"paiConfigFile,omitempty"` + PaiConfigFile *PaiConfigMap `json:"paiConfigFile,omitempty"` + // +kubebuilder:default=true PaiEnableAuthentication bool `json:"paiEnableAuthentication,omitempty"` PaiSecret *PaiSecretSpec `json:"paiSecret,omitempty"` IsExternalSvc bool `json:"isExternalSvc,omitempty"` diff --git a/apis/omlai/v4/privateai_webhook.go b/apis/omlai/v4/privateai_webhook.go index 0d8e82f0..9599b093 100644 --- a/apis/omlai/v4/privateai_webhook.go +++ b/apis/omlai/v4/privateai_webhook.go @@ -109,9 +109,9 @@ func (d *PrivateAiCustomDefaulter) Default(ctx context.Context, obj runtime.Obje privateai.Spec.PaiHTTPPort = 0 } - if privateai.Spec.PaiAuthentication { - if privateai.Spec.PaiSecret.Name == "" { - return fmt.Errorf("PaiAuthentication is ture but paisecret is empty") + if privateai.Spec.PaiEnableAuthentication { + if privateai.Spec.PaiSecret == nil || privateai.Spec.PaiSecret.Name == "" { + return fmt.Errorf("paiEnableAuthentication is true but paiSecret.name is empty") } } @@ -130,12 +130,17 @@ func (d *PrivateAiCustomDefaulter) Default(ctx context.Context, obj runtime.Obje privateai.Spec.PaiService.PortMappings = append(privateai.Spec.PaiService.PortMappings, portInfo) } // set default MountLocation for PaiConfigFile - if privateai.Spec.PaiConfigFile.MountLocation == "" { - privateai.Spec.PaiConfigFile.MountLocation = "/oml/config" + if privateai.Spec.PaiConfigFile != nil { + if privateai.Spec.PaiConfigFile.MountLocation == "" { + privateai.Spec.PaiConfigFile.MountLocation = "/oml/config" + } } + // set default MountLocation for PaiSecret - if privateai.Spec.PaiSecret.MountLocation == "" { - privateai.Spec.PaiSecret.MountLocation = "/oml/ssl" + if privateai.Spec.PaiSecret != nil { + if privateai.Spec.PaiSecret.MountLocation == "" { + privateai.Spec.PaiSecret.MountLocation = "/oml/ssl" + } } return nil @@ -165,6 +170,12 @@ func (v *PrivateAiCustomValidator) ValidateCreate(ctx context.Context, obj runti } privateailog.Info("Validation for PrivateAi upon creation", "name", privateai.GetName()) + if privateai.Spec.PaiEnableAuthentication { + if privateai.Spec.PaiSecret == nil || privateai.Spec.PaiSecret.Name == "" { + return nil, fmt.Errorf("paiEnableAuthentication=true requires paiSecret.name to be set") + } + } + // TODO(user): fill in your validation logic upon object creation. return nil, nil @@ -177,6 +188,11 @@ func (v *PrivateAiCustomValidator) ValidateUpdate(ctx context.Context, oldObj, n return nil, fmt.Errorf("expected a PrivateAi object for the newObj but got %T", newObj) } privateailog.Info("Validation for PrivateAi upon update", "name", privateai.GetName()) + if privateai.Spec.PaiEnableAuthentication { + if privateai.Spec.PaiSecret == nil || privateai.Spec.PaiSecret.Name == "" { + return nil, fmt.Errorf("paiEnableAuthentication=true requires paiSecret.name to be set") + } + } // TODO(user): fill in your validation logic upon object update. diff --git a/commons/omlai/aibuilder.go b/commons/omlai/aibuilder.go index 2b6d7a45..42030323 100644 --- a/commons/omlai/aibuilder.go +++ b/commons/omlai/aibuilder.go @@ -2,8 +2,10 @@ package commons import ( "context" + "fmt" "reflect" "strconv" + "strings" "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" @@ -92,8 +94,9 @@ func buildPodSpecForPrivateAI(instance *privateaiv4.PrivateAi) *corev1.PodSpec { RunAsUser: int64Ptr(2001), RunAsGroup: int64Ptr(2001), }, - Containers: buildContainerSpecForPrivateAI(instance), - Volumes: buildVolumeSpecForPrivateAI(instance), + InitContainers: buildInitContainerSpecForPrivateAI(instance), + Containers: buildContainerSpecForPrivateAI(instance), + Volumes: buildVolumeSpecForPrivateAI(instance), } if len(instance.Spec.PaiImagePullSecret) > 0 { @@ -110,6 +113,58 @@ func buildPodSpecForPrivateAI(instance *privateaiv4.PrivateAi) *corev1.PodSpec { return spec } +// Init container: run as root, chown all pvcList mount paths +func buildInitContainerSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.Container { + if len(instance.Spec.PvcList) == 0 { + return nil + } + + // Build a single chown command across all mount paths + var cmds []string + for _, mountPath := range instance.Spec.PvcList { + if mountPath != "" { + cmds = append(cmds, fmt.Sprintf("chown -R 2001:2001 %q || true", mountPath)) + } + } + chownCmd := strings.Join(cmds, " && ") + + privileged := true + runAsRoot := int64(0) + + return []corev1.Container{ + { + Name: instance.Name + "-init", + Image: instance.Spec.PaiImage, + ImagePullPolicy: corev1.PullIfNotPresent, + Command: []string{"/bin/sh", "-c", chownCmd}, + SecurityContext: &corev1.SecurityContext{ + Privileged: &privileged, + RunAsUser: &runAsRoot, + RunAsGroup: &runAsRoot, + }, + VolumeMounts: pvcOnlyVolumeMounts(instance), // mount only PVCs we need to chown + }, + } +} + +// only the pvcList mounts (used by the init container) +func pvcOnlyVolumeMounts(instance *privateaiv4.PrivateAi) []corev1.VolumeMount { + var vms []corev1.VolumeMount + if len(instance.Spec.PvcList) > 0 { + for claimName, mountPath := range instance.Spec.PvcList { + if mountPath == "" { + continue + } + vms = append(vms, corev1.VolumeMount{ + Name: fmt.Sprintf("%s-%s-vol", instance.Name, claimName), + MountPath: mountPath, + ReadOnly: false, + }) + } + } + return vms +} + func buildContainerSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.Container { container := corev1.Container{ Name: instance.Name, @@ -189,6 +244,19 @@ func buildVolumeSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.Volum VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: instance.Name + "-oradata-vol4"}}, }) } + if len(instance.Spec.PvcList) > 0 { + for claimName := range instance.Spec.PvcList { + vols = append(vols, corev1.Volume{ + Name: fmt.Sprintf("%s-%s-vol", instance.Name, claimName), + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: claimName, // PVC name from CR + ReadOnly: false, + }, + }, + }) + } + } vols = append(vols, corev1.Volume{ Name: instance.Name + "-logs-vol", @@ -226,6 +294,18 @@ func buildVolumeMountSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1. ReadOnly: true, }) } + if len(instance.Spec.PvcList) > 0 { + for claimName, mountPath := range instance.Spec.PvcList { + if mountPath == "" { + continue + } + vms = append(vms, corev1.VolumeMount{ + Name: fmt.Sprintf("%s-%s-vol", instance.Name, claimName), + MountPath: mountPath, // e.g /oml/models + ReadOnly: false, + }) + } + } // NEW: Logs mount logMount := defaultLogMount @@ -294,7 +374,7 @@ func buildEnvVarsForPrivateAI(instance *privateaiv4.PrivateAi, envVars []private } if !omlConfigFileFlag { - if instance.Spec.PaiConfigFile.MountLocation != "" && instance.Spec.PaiConfigFile.Name != "" { + if instance.Spec.PaiConfigFile != nil && instance.Spec.PaiConfigFile.Name != "" && instance.Spec.PaiConfigFile.MountLocation != "" { envs = append(envs, corev1.EnvVar{ Name: "OML_CONFIG_FILE", Value: instance.Spec.PaiConfigFile.MountLocation + "/" + "config.json", @@ -310,6 +390,17 @@ func buildEnvVarsForPrivateAI(instance *privateaiv4.PrivateAi, envVars []private }) } } + if instance.Spec.PaiEnableAuthentication { + envs = append(envs, corev1.EnvVar{ + Name: "OML_AUTHENTICATION_ENABLED", + Value: "true", + }) + } else { + envs = append(envs, corev1.EnvVar{ + Name: "OML_AUTHENTICATION_ENABLED", + Value: "false", + }) + } return envs } diff --git a/config/crd/bases/omlai.oracle.com_privateais.yaml b/config/crd/bases/omlai.oracle.com_privateais.yaml index bb044b2d..e8deef09 100644 --- a/config/crd/bases/omlai.oracle.com_privateais.yaml +++ b/config/crd/bases/omlai.oracle.com_privateais.yaml @@ -107,6 +107,7 @@ spec: type: string type: object paiEnableAuthentication: + default: true type: boolean paiHTTPEnabled: type: boolean diff --git a/go.mod b/go.mod index 4bd74705..d0505d29 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( k8s.io/cli-runtime v0.33.3 k8s.io/client-go v0.33.3 k8s.io/kubectl v0.33.3 + k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 sigs.k8s.io/controller-runtime v0.21.0 sigs.k8s.io/yaml v1.5.0 ) @@ -98,7 +99,6 @@ require ( k8s.io/component-helpers v0.33.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect - k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/kustomize/api v0.19.0 // indirect sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index f3c00ef1..2da525a8 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -13919,6 +13919,7 @@ spec: type: string type: object paiEnableAuthentication: + default: true type: boolean paiHTTPEnabled: type: boolean From 96c4d4084ea382e7c17359593806440edc908fac Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Sat, 16 Aug 2025 05:19:04 +0000 Subject: [PATCH 267/414] Optionalfield --- apis/database/v4/dbcssystem_types.go | 6 +++--- .../crd/bases/database.oracle.com_dbcssystems.yaml | 12 ++++-------- oracle-database-operator.yaml | 12 ++++-------- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/apis/database/v4/dbcssystem_types.go b/apis/database/v4/dbcssystem_types.go index a2de4a8a..50934bd8 100644 --- a/apis/database/v4/dbcssystem_types.go +++ b/apis/database/v4/dbcssystem_types.go @@ -218,7 +218,7 @@ type DbCloneConfig struct { TdeWalletPasswordSecret string `json:"tdeWalletPasswordSecret,omitempty"` DbName string `json:"dbName"` HostName string `json:"hostName"` - DbUniqueName string `json:"dbDbUniqueName"` + DbUniqueName string `json:"dbUniqueName,omitempty"` DisplayName string `json:"displayName"` LicenseModel string `json:"licenseModel,omitempty"` Domain string `json:"domain,omitempty"` @@ -256,8 +256,8 @@ type DbCloneStatus struct { Id *string `json:"id,omitempty"` DbAdminPasswordSecret string `json:"dbAdminPasswordSecret,omitempty"` DbName string `json:"dbName,omitempty"` - HostName string `json:"hostName"` - DbUniqueName string `json:"dbDbUniqueName"` + HostName string `json:"hostName,omitempty"` + DbUniqueName string `json:"dbUniqueName,omitempty"` DisplayName string `json:"displayName,omitempty"` LicenseModel string `json:"licenseModel,omitempty"` Domain string `json:"domain,omitempty"` diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index 8dc0f1ea..8ce84a61 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -471,10 +471,10 @@ spec: properties: dbAdminPasswordSecret: type: string - dbDbUniqueName: - type: string dbName: type: string + dbUniqueName: + type: string displayName: type: string domain: @@ -502,7 +502,6 @@ spec: tdeWalletPasswordSecret: type: string required: - - dbDbUniqueName - dbName - displayName - hostName @@ -742,10 +741,10 @@ spec: properties: dbAdminPasswordSecret: type: string - dbDbUniqueName: - type: string dbName: type: string + dbUniqueName: + type: string displayName: type: string domain: @@ -762,9 +761,6 @@ spec: type: array subnetId: type: string - required: - - dbDbUniqueName - - hostName type: object dbEdition: type: string diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index f3c00ef1..ca00315e 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -9760,10 +9760,10 @@ spec: properties: dbAdminPasswordSecret: type: string - dbDbUniqueName: - type: string dbName: type: string + dbUniqueName: + type: string displayName: type: string domain: @@ -9791,7 +9791,6 @@ spec: tdeWalletPasswordSecret: type: string required: - - dbDbUniqueName - dbName - displayName - hostName @@ -10031,10 +10030,10 @@ spec: properties: dbAdminPasswordSecret: type: string - dbDbUniqueName: - type: string dbName: type: string + dbUniqueName: + type: string displayName: type: string domain: @@ -10051,9 +10050,6 @@ spec: type: array subnetId: type: string - required: - - dbDbUniqueName - - hostName type: object dbEdition: type: string From 60e056ab36c8f9319bff7ea187f658a6e8f90963 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 18 Aug 2025 04:43:18 +0000 Subject: [PATCH 268/414] Added fixes --- commons/oraclerestart/oraclerestartprov.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index d51f28a9..c6033873 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -555,7 +555,7 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, fmt.Println("No Location is passed for the software storage in" + OracleRestartSpex.Name) } - var mountLoc string + //var mountLoc string // Check if ConfigParams is not nil if instance.Spec.ConfigParams != nil { @@ -580,7 +580,7 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, if instance.Spec.ConfigParams.OPatchLocation != "" { result = append(result, corev1.VolumeMount{ Name: OracleRestartSpex.Name + "-oradata-opatch-vol", - MountPath: mountLoc, + MountPath: instance.Spec.ConfigParams.OPatchLocation, }) } } From 04be36cf556b2c3720ed1b6ebfb438bc9c378749 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 18 Aug 2025 05:38:54 +0000 Subject: [PATCH 269/414] Added fixes --- config/manager/kustomization.yaml | 2 +- controllers/database/oraclerestart_controller.go | 2 +- oracle-database-operator.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 782bc9b2..1d870700 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: Test-sharding-operator-V4 + newTag: orestart-operator-mltp1 diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index dd1accab..cbd49e97 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -1975,7 +1975,7 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or } if instance.Spec.ConfigParams.OPatchLocation != "" { - data = append(data, "OPATCH_ZIP_FILE="+utils.OraOPatchStageLocation+"/"+instance.Spec.ConfigParams.OPatchSwZipFile) + data = append(data, "OPATCH_ZIP_FILE="+instance.Spec.ConfigParams.OPatchLocation+"/"+instance.Spec.ConfigParams.OPatchSwZipFile) } if instance.Spec.ConfigParams.OneOffLocation != "" { data = append(data, "ONEOFF_FOLDER_NAME="+instance.Spec.ConfigParams.OneOffLocation) diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 0708928c..5780c144 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -17124,7 +17124,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:Test-sharding-operator-V4 + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-mltp1 imagePullPolicy: Always name: manager ports: From 15aa29bd809c3548665e46d3f04e0155fcad312d Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Mon, 18 Aug 2025 09:45:54 +0000 Subject: [PATCH 270/414] Autodiscover optimization+docs --- commons/k8s/utils.go | 5 +- config/manager/kustomization.yaml | 4 +- controllers/database/lrest_controller.go | 3 +- controllers/database/lrpdb_controller.go | 25 +- docs/multitenant/NamespaceSeg.md | 14 - docs/multitenant/README.md | 723 +++++++++++++- .../images/Generalschema2.jpg | Bin docs/multitenant/images/KubectlGetSchema.jpg | Bin 0 -> 206768 bytes docs/multitenant/images/KubectlGetSchema2.jpg | Bin 0 -> 187365 bytes .../images/UsecaseSchema.jpg | Bin docs/multitenant/images/plsqlmap.jpg | Bin 0 -> 94000 bytes docs/multitenant/lrest-based/README.md | 501 ---------- .../lrest-based/usecase/config-map-pdb.yaml | 11 - .../multitenant/usecase02/README.md | 0 docs/multitenant/ords-based/NamespaceSeg.md | 14 - docs/multitenant/ords-based/README.md | 411 -------- .../ords-based/images/K8S_NAMESPACE_SEG.png | Bin 269014 -> 0 bytes .../ords-based/images/K8S_SECURE1.png | Bin 129940 -> 0 bytes .../ords-based/images/K8S_SECURE2.png | Bin 130742 -> 0 bytes .../ords-based/images/K8S_SECURE3.png | Bin 130498 -> 0 bytes .../ords-based/images/K8S_SECURE4.png | Bin 132342 -> 0 bytes .../ords-based/images/makerunall.png | Bin 211874 -> 0 bytes .../ords-based/images/makesecrets_1_1.png | Bin 117953 -> 0 bytes .../multitenant/ords-based/openssl_schema.jpg | Bin 54221 -> 0 bytes .../example_setup_using_oci_oke_cluster.md | 38 - .../multinamespace/cdb_create.yaml | 48 - .../multinamespace/pdb_clone.yaml | 54 -- .../multinamespace/pdb_close.yaml | 48 - .../multinamespace/pdb_create.yaml | 50 - .../multinamespace/pdb_delete.yaml | 39 - .../provisioning/multinamespace/pdb_open.yaml | 47 - .../provisioning/multinamespace/pdb_plug.yaml | 51 - .../multinamespace/pdb_unplug.yaml | 43 - .../ords-based/provisioning/ords_image.md | 81 -- .../provisioning/quickOKEcreation.md | 136 --- .../singlenamespace/cdb_create.yaml | 49 - .../singlenamespace/cdb_secret.yaml | 17 - .../singlenamespace/pdb_clone.yaml | 60 -- .../singlenamespace/pdb_close.yaml | 48 - .../singlenamespace/pdb_create.yaml | 51 - .../singlenamespace/pdb_delete.yaml | 39 - .../singlenamespace/pdb_open.yaml | 47 - .../singlenamespace/pdb_plug.yaml | 55 -- .../singlenamespace/pdb_secret.yaml | 16 - .../singlenamespace/pdb_unplug.yaml | 49 - docs/multitenant/ords-based/usecase/README.md | 112 --- .../usecase/cdbnamespace_binding.yaml | 13 - .../usecase/clone_pdb1_resource.yaml | 50 - .../usecase/clone_pdb2_resource.yaml | 50 - .../usecase/close_pdb1_resource.yaml | 47 - .../usecase/close_pdb2_resource.yaml | 47 - .../usecase/close_pdb3_resource.yaml | 47 - .../ords-based/usecase/create_ords_pod.yaml | 48 - .../usecase/create_pdb1_resource.yaml | 51 - .../usecase/create_pdb2_resource.yaml | 51 - .../usecase/delete_pdb1_resource.yaml | 45 - .../usecase/delete_pdb2_resource.yaml | 45 - docs/multitenant/ords-based/usecase/makefile | 915 ------------------ .../ords-based/usecase/map_pdb1_resource.yaml | 49 - .../ords-based/usecase/map_pdb2_resource.yaml | 49 - .../ords-based/usecase/map_pdb3_resource.yaml | 49 - .../usecase/open_pdb1_resource.yaml | 47 - .../usecase/open_pdb2_resource.yaml | 47 - .../usecase/open_pdb3_resource.yaml | 47 - .../ords-based/usecase/parameters.txt | 61 -- .../usecase/pdbnamespace_binding.yaml | 13 - .../usecase/plug_pdb1_resource.yaml | 53 - .../usecase/unplug_pdb1_resource.yaml | 46 - .../ords-based/usecase01/README.md | 516 ---------- docs/multitenant/ords-based/usecase01/ca.crt | 25 - docs/multitenant/ords-based/usecase01/ca.key | 27 - docs/multitenant/ords-based/usecase01/ca.srl | 1 - .../ords-based/usecase01/cdb_create.yaml | 44 - .../ords-based/usecase01/cdb_secret.yaml | 17 - .../usecase01/clone_pdb1_resource.yaml | 50 - .../usecase01/clone_pdb2_resource.yaml | 50 - .../usecase01/close_pdb1_resource.yaml | 47 - .../usecase01/close_pdb2_resource.yaml | 47 - .../usecase01/close_pdb3_resource.yaml | 47 - .../ords-based/usecase01/create_ords_pod.yaml | 48 - .../usecase01/create_pdb1_resource.yaml | 51 - .../usecase01/create_pdb2_resource.yaml | 51 - .../usecase01/delete_pdb1_resource.yaml | 45 - .../usecase01/delete_pdb2_resource.yaml | 45 - .../ords-based/usecase01/extfile.txt | 1 - .../usecase01/logfiles/BuildImage.log | 896 ----------------- .../usecase01/logfiles/ImagePush.log | 11 - .../ords-based/usecase01/logfiles/cdb.log | 372 ------- .../usecase01/logfiles/cdb_creation.log | 357 ------- .../usecase01/logfiles/openssl_execution.log | 19 - .../usecase01/logfiles/ordsconfig.log | 39 - .../usecase01/logfiles/tagandpush.log | 14 - .../ords-based/usecase01/logfiles/testapi.log | 62 -- .../multitenant/ords-based/usecase01/makefile | 906 ----------------- .../usecase01/map_pdb1_resource.yaml | 49 - .../usecase01/map_pdb2_resource.yaml | 49 - .../usecase01/map_pdb3_resource.yaml | 49 - .../usecase01/open_pdb1_resource.yaml | 47 - .../usecase01/open_pdb2_resource.yaml | 47 - .../usecase01/open_pdb3_resource.yaml | 47 - ...acle-database-operator-system_binding.yaml | 13 - .../usecase01/oracle-database-operator.yaml | 1 - .../ords-based/usecase01/parameters.txt | 61 -- .../ords-based/usecase01/pdb_close.yaml | 44 - .../ords-based/usecase01/pdb_create.yaml | 47 - .../ords-based/usecase01/pdb_delete.yaml | 34 - .../ords-based/usecase01/pdb_map.yaml | 45 - .../ords-based/usecase01/pdb_open.yaml | 43 - .../ords-based/usecase01/pdb_secret.yaml | 16 - .../usecase01/plug_pdb1_resource.yaml | 53 - .../ords-based/usecase01/server.csr | 18 - .../ords-based/usecase01/tde_secret.yaml | 17 - docs/multitenant/ords-based/usecase01/tls.crt | 24 - docs/multitenant/ords-based/usecase01/tls.key | 28 - .../usecase01/unplug_pdb1_resource.yaml | 46 - .../ords-based/usecase02/README.md | 523 ---------- .../ords-based/usecase02/pdb_clone.yaml | 50 - .../ords-based/usecase02/pdb_plug.yaml | 53 - .../ords-based/usecase02/pdb_plugtde.yaml | 56 -- .../ords-based/usecase02/pdb_unplug.yaml | 46 - .../ords-based/usecase02/pdb_unplugtde.yaml | 54 -- docs/multitenant/provisioning/ords_image.md | 81 -- .../{lrest-based => }/usecase/README.md | 0 .../usecase/altersystem_pdb1_resource.yaml | 3 - .../usecase/cdbnamespace_binding.yaml | 0 .../usecase/clone_pdb1_resource.yaml | 3 +- .../usecase/clone_pdb2_resource.yaml | 3 +- .../usecase/close_pdb1_resource.yaml | 1 - .../usecase/close_pdb2_resource.yaml | 1 - .../usecase/close_pdb3_resource.yaml | 1 - .../usecase/config_map_pdb.yaml | 0 .../multitenant/usecase/config_map_plsql.yaml | 29 + .../usecase/create_lrest_pod.yaml | 9 + .../usecase/create_pdb1_resource.yaml | 3 +- .../usecase/create_pdb2_resource.yaml | 3 +- .../usecase/delete_pdb1_resource.yaml | 2 +- .../usecase/delete_pdb2_resource.yaml | 2 +- .../usecase/delete_pdb3_resource.yaml | 45 + .../{lrest-based => }/usecase/makefile | 38 +- .../usecase/map_pdb1_resource.yaml | 3 +- .../usecase/map_pdb2_resource.yaml | 3 +- .../usecase/map_pdb3_resource.yaml | 3 +- .../usecase/open_pdb1_resource.yaml | 1 - .../usecase/open_pdb2_resource.yaml | 1 - .../usecase/open_pdb3_resource.yaml | 1 - .../{lrest-based => }/usecase/parameters.txt | 12 +- .../usecase/pdbnamespace_binding.yaml | 0 .../usecase/plug_pdb1_resource.yaml | 7 +- .../multitenant/usecase/security_context.yaml | 93 ++ .../usecase/unplug_pdb1_resource.yaml | 4 +- .../usecase01/logfiles/BuildImage.log | 896 ----------------- .../usecase01/logfiles/openssl_execution.log | 19 - .../usecase01/logfiles/ordsconfig.log | 39 - docs/multitenant/usecase01/makefile | 284 ------ docs/multitenant/usecase03/Dockerfile | 80 -- .../usecase03/NamespaceSegregation.png | Bin 270813 -> 0 bytes docs/multitenant/usecase03/README.md | 268 ----- docs/multitenant/usecase03/cdb_create.yaml | 44 - .../usecase03/cdb_creation_log.txt | 336 ------- docs/multitenant/usecase03/cdb_secret.yaml | 17 - docs/multitenant/usecase03/gentlscert.sh | 23 - docs/multitenant/usecase03/makefile | 285 ------ .../usecase03/ns_namespace_cdb.yaml | 7 - .../usecase03/ns_namespace_pdb.yaml | 7 - .../usecase03/operator_creation_log.txt | 27 - docs/multitenant/usecase03/pdb_create.yaml | 46 - .../usecase03/pdb_creation_log.txt | 6 - docs/multitenant/usecase03/pdb_secret.yaml | 16 - docs/multitenant/usecase03/runOrdsSSL.sh | 190 ---- oracle-database-operator.yaml | 2 +- 170 files changed, 953 insertions(+), 12098 deletions(-) delete mode 100644 docs/multitenant/NamespaceSeg.md rename docs/multitenant/{lrest-based => }/images/Generalschema2.jpg (100%) create mode 100644 docs/multitenant/images/KubectlGetSchema.jpg create mode 100644 docs/multitenant/images/KubectlGetSchema2.jpg rename docs/multitenant/{lrest-based => }/images/UsecaseSchema.jpg (100%) create mode 100644 docs/multitenant/images/plsqlmap.jpg delete mode 100644 docs/multitenant/lrest-based/README.md delete mode 100644 docs/multitenant/lrest-based/usecase/config-map-pdb.yaml delete mode 100644 docs/multitenant/multitenant/usecase02/README.md delete mode 100644 docs/multitenant/ords-based/NamespaceSeg.md delete mode 100644 docs/multitenant/ords-based/README.md delete mode 100644 docs/multitenant/ords-based/images/K8S_NAMESPACE_SEG.png delete mode 100644 docs/multitenant/ords-based/images/K8S_SECURE1.png delete mode 100644 docs/multitenant/ords-based/images/K8S_SECURE2.png delete mode 100644 docs/multitenant/ords-based/images/K8S_SECURE3.png delete mode 100644 docs/multitenant/ords-based/images/K8S_SECURE4.png delete mode 100644 docs/multitenant/ords-based/images/makerunall.png delete mode 100644 docs/multitenant/ords-based/images/makesecrets_1_1.png delete mode 100644 docs/multitenant/ords-based/openssl_schema.jpg delete mode 100644 docs/multitenant/ords-based/provisioning/example_setup_using_oci_oke_cluster.md delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_plug.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_unplug.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/ords_image.md delete mode 100644 docs/multitenant/ords-based/provisioning/quickOKEcreation.md delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml delete mode 100644 docs/multitenant/ords-based/usecase/README.md delete mode 100644 docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml delete mode 100644 docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/create_ords_pod.yaml delete mode 100644 docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/makefile delete mode 100644 docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/parameters.txt delete mode 100644 docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml delete mode 100644 docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/README.md delete mode 100644 docs/multitenant/ords-based/usecase01/ca.crt delete mode 100644 docs/multitenant/ords-based/usecase01/ca.key delete mode 100644 docs/multitenant/ords-based/usecase01/ca.srl delete mode 100644 docs/multitenant/ords-based/usecase01/cdb_create.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/cdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/create_ords_pod.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/extfile.txt delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/ImagePush.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/cdb.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/testapi.log delete mode 100644 docs/multitenant/ords-based/usecase01/makefile delete mode 100644 docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml delete mode 120000 docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/parameters.txt delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_close.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_create.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_delete.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_map.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_open.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/server.csr delete mode 100644 docs/multitenant/ords-based/usecase01/tde_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/tls.crt delete mode 100644 docs/multitenant/ords-based/usecase01/tls.key delete mode 100644 docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase02/README.md delete mode 100644 docs/multitenant/ords-based/usecase02/pdb_clone.yaml delete mode 100644 docs/multitenant/ords-based/usecase02/pdb_plug.yaml delete mode 100644 docs/multitenant/ords-based/usecase02/pdb_plugtde.yaml delete mode 100644 docs/multitenant/ords-based/usecase02/pdb_unplug.yaml delete mode 100644 docs/multitenant/ords-based/usecase02/pdb_unplugtde.yaml delete mode 100644 docs/multitenant/provisioning/ords_image.md rename docs/multitenant/{lrest-based => }/usecase/README.md (100%) rename docs/multitenant/{lrest-based => }/usecase/altersystem_pdb1_resource.yaml (97%) rename docs/multitenant/{lrest-based => }/usecase/cdbnamespace_binding.yaml (100%) rename docs/multitenant/{lrest-based => }/usecase/clone_pdb1_resource.yaml (95%) rename docs/multitenant/{lrest-based => }/usecase/clone_pdb2_resource.yaml (95%) rename docs/multitenant/{lrest-based => }/usecase/close_pdb1_resource.yaml (97%) rename docs/multitenant/{lrest-based => }/usecase/close_pdb2_resource.yaml (97%) rename docs/multitenant/{lrest-based => }/usecase/close_pdb3_resource.yaml (97%) rename docs/multitenant/{lrest-based => }/usecase/config_map_pdb.yaml (100%) create mode 100644 docs/multitenant/usecase/config_map_plsql.yaml rename docs/multitenant/{lrest-based => }/usecase/create_lrest_pod.yaml (86%) rename docs/multitenant/{lrest-based => }/usecase/create_pdb1_resource.yaml (95%) rename docs/multitenant/{lrest-based => }/usecase/create_pdb2_resource.yaml (95%) rename docs/multitenant/{lrest-based => }/usecase/delete_pdb1_resource.yaml (97%) rename docs/multitenant/{lrest-based => }/usecase/delete_pdb2_resource.yaml (97%) create mode 100644 docs/multitenant/usecase/delete_pdb3_resource.yaml rename docs/multitenant/{lrest-based => }/usecase/makefile (96%) rename docs/multitenant/{lrest-based => }/usecase/map_pdb1_resource.yaml (95%) rename docs/multitenant/{lrest-based => }/usecase/map_pdb2_resource.yaml (95%) rename docs/multitenant/{lrest-based => }/usecase/map_pdb3_resource.yaml (95%) rename docs/multitenant/{lrest-based => }/usecase/open_pdb1_resource.yaml (97%) rename docs/multitenant/{lrest-based => }/usecase/open_pdb2_resource.yaml (97%) rename docs/multitenant/{lrest-based => }/usecase/open_pdb3_resource.yaml (97%) rename docs/multitenant/{lrest-based => }/usecase/parameters.txt (91%) rename docs/multitenant/{lrest-based => }/usecase/pdbnamespace_binding.yaml (100%) rename docs/multitenant/{lrest-based => }/usecase/plug_pdb1_resource.yaml (91%) create mode 100644 docs/multitenant/usecase/security_context.yaml rename docs/multitenant/{lrest-based => }/usecase/unplug_pdb1_resource.yaml (93%) delete mode 100644 docs/multitenant/usecase01/logfiles/BuildImage.log delete mode 100644 docs/multitenant/usecase01/logfiles/openssl_execution.log delete mode 100644 docs/multitenant/usecase01/logfiles/ordsconfig.log delete mode 100644 docs/multitenant/usecase01/makefile delete mode 100644 docs/multitenant/usecase03/Dockerfile delete mode 100644 docs/multitenant/usecase03/NamespaceSegregation.png delete mode 100644 docs/multitenant/usecase03/README.md delete mode 100644 docs/multitenant/usecase03/cdb_create.yaml delete mode 100644 docs/multitenant/usecase03/cdb_creation_log.txt delete mode 100644 docs/multitenant/usecase03/cdb_secret.yaml delete mode 100644 docs/multitenant/usecase03/gentlscert.sh delete mode 100644 docs/multitenant/usecase03/makefile delete mode 100644 docs/multitenant/usecase03/ns_namespace_cdb.yaml delete mode 100644 docs/multitenant/usecase03/ns_namespace_pdb.yaml delete mode 100644 docs/multitenant/usecase03/operator_creation_log.txt delete mode 100644 docs/multitenant/usecase03/pdb_create.yaml delete mode 100644 docs/multitenant/usecase03/pdb_creation_log.txt delete mode 100644 docs/multitenant/usecase03/pdb_secret.yaml delete mode 100644 docs/multitenant/usecase03/runOrdsSSL.sh diff --git a/commons/k8s/utils.go b/commons/k8s/utils.go index 70b2f810..0d6ea83b 100644 --- a/commons/k8s/utils.go +++ b/commons/k8s/utils.go @@ -86,10 +86,9 @@ func Patch(kubeClient client.Client, obj client.Object, path string, value inter } func Int64Pointer(d int64) *int64 { - return &d + return &d } func BoolPointer(d bool) *bool { - return &d + return &d } - diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 1d870700..eb2cad70 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: orestart-operator-mltp1 + newName: lin.ocir.io/intsanjaysingh/operator/dboperator_master_250730 + newTag: latest diff --git a/controllers/database/lrest_controller.go b/controllers/database/lrest_controller.go index f3dd313f..63fb38c1 100644 --- a/controllers/database/lrest_controller.go +++ b/controllers/database/lrest_controller.go @@ -1305,7 +1305,8 @@ func (r *LRESTReconciler) LrpdbCreation(ctx context.Context, req ctrl.Request, l "adminpdbPass": WebUseObj, /* Place holder */ "fileNameConversions": "NONE", "imperativeLrpdbDeletion": true, - "pdbBitMask": PDBCRT, + "reststate": PDBAUT, + "pdbState": "RESET", }, }, } diff --git a/controllers/database/lrpdb_controller.go b/controllers/database/lrpdb_controller.go index a0b6f874..cd7f7b00 100644 --- a/controllers/database/lrpdb_controller.go +++ b/controllers/database/lrpdb_controller.go @@ -185,23 +185,6 @@ type PLSQLPayLoad struct { Sqltokens []string } -var ( - lrpdbPhaseCreate = "Creating" - lrpdbPhasePlug = "Plugging" - lrpdbPhaseUnplug = "Unplugging" - lrpdbPhaseClone = "Cloning" - lrpdbPhaseFinish = "Finishing" - lrpdbPhaseReady = "Ready" - lrpdbPhaseDelete = "Deleting" - lrpdbPhaseModify = "Modifying" - lrpdbPhaseMap = "Mapping" - lrpdbPhaseStatus = "CheckingState" - lrpdbPhaseFail = "Failed" - lrpdbPhaseAlterPlug = "AlterPlugDb" - lrpdbPhaseSpare = "NoAction" - lrpdbPhaseApplySql = "ApplySqlCode" -) - const LRPDBFinalizer = "database.oracle.com/LRPDBfinalizer" var tde_Password string @@ -765,8 +748,6 @@ func (r *LRPDBReconciler) OpenLRPDB(ctx context.Context, req ctrl.Request, lrpdb } - lrpdb.Status.Msg = "open:[op. completed]" - log.Info("Successfully modified LRPDB state", "LRPDB Name", lrpdb.Spec.LRPDBName) /* After database openining we reapply the config map if warning is present */ @@ -777,6 +758,7 @@ func (r *LRPDBReconciler) OpenLRPDB(ctx context.Context, req ctrl.Request, lrpdb } } + lrpdb.Status.Msg = "open:[op. completed]" lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBOPN) lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) r.UpdateStatus(ctx, req, lrpdb) @@ -2338,8 +2320,13 @@ func (r *LRPDBReconciler) ApplyConfigMap(ctx context.Context, req ctrl.Request, } + if err := r.Update(ctx, lrpdb); err != nil { + log.Error(err, "Cannot rest lrpdb Spec :"+lrpdb.Name, "err", err.Error()) + } + lrpdb.Status.CmBitstat = bis(lrpdb.Status.CmBitstat, MPAPPL) lrpdb.Status.CmBitStatStr = CMBitmaskprint(lrpdb.Status.CmBitstat) + r.UpdateStatus(ctx, req, lrpdb) return Cardinality, nil } diff --git a/docs/multitenant/NamespaceSeg.md b/docs/multitenant/NamespaceSeg.md deleted file mode 100644 index 6738fe56..00000000 --- a/docs/multitenant/NamespaceSeg.md +++ /dev/null @@ -1,14 +0,0 @@ - - -# Namespace segregation - -With the namespace segregation pdb controller and cdb controller run in different namespaces. The new functionality introduces a new parameter (the cdb namespace) in pdb crd definition. In case you don't need the namespace segregation you have to sepcify the namespace name that you are using for yours crd and pods anyway. Refer to usercase01 and usecase02 to see single namespace configuration. Refer to usecase03 to see examples of namespace segregation. - -# Secrets - -In order to use multiple namespace we need to create approriate secrets in each namespace. Tls certificate secrets must be created in all namespaces (db-ca db-tls). - -![general_schema](./images/K8S_NAMESPACE_SEG.png) - - - diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index 0d3057fc..0e8c0239 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -1,11 +1,718 @@ -# Multitenant Controllers + -Starting from OraOperator version 1.2.0, there are two classes of multitenant controllers: one based on [ORDS](https://www.oracle.com/uk/database/technologies/appdev/rest.html) and another based on a dedicated REST server for the operator, called LREST. In both cases, the features remains unchanged (a part from CRD name changes). A pod running a REST server (either LREST or ORDS) acts as the proxy server connected to the container database (CDB) for all incoming kubectl requests. We plan to discontinue the ORDS based controller, in the next release; no regression (a part form CRD name changes). -## What are the differences +# LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT + -- Regarding the YAML file, the parameters for the existing functionalities are unchanged. -- The **CRD** names are different: for controllers based on [ORDS](./ords-based/README.md), we have **PDB** and **CDB**, while for controllers based on [LREST](./lrest-based/README.md), we have **LRPDB** and **LREST**. -- If you use an LREST-based controller, there is no need to manually create the REST server pod. The image is available for download on OCR. -- Controllers based on **LREST** allow you to manage PDB parameters using kubectl. -- ORDS controllers currently do not support ORDS version 24.1. +- [LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT](#lrest-based-multitenant-controllers-for-pdb-life-cycle-management) + - [WHAT'S NEW](#whats-new) + - [Kubectl get lrpdb format](#kubectl-get-lrpdb-format) + - [Pdb status table](#pdb-status-table) + - [STEP BY STEP CONFIGURATION](#step-by-step-configuration) + - [Multiple namespace setup](#multiple-namespace-setup) + - [Create the operator](#create-the-operator) + - [Container database setup](#container-database-setup) + - [Apply rolebinding](#apply-rolebinding) + - [Certificate and credentials](#certificate-and-credentials) + - [Private key 🔑](#private-key-) + - [Public Key 🔑](#public-key-) + - [Certificates](#certificates) + - [Create secrets for certificate and keys](#create-secrets-for-certificate-and-keys) + - [Create secrets with encrypted password](#create-secrets-with-encrypted-password) + - [Create lrest pod](#create-lrest-pod) + - [Openshift configuration](#openshift-configuration) + - [Create PDB](#create-pdb) + - [pdb config map](#pdb-config-map) + - [Open PDB](#open-pdb) + - [Close PDB](#close-pdb) + - [Clone PDB](#clone-pdb) + - [Unplug PDB](#unplug-pdb) + - [Plug PDB](#plug-pdb) + - [Delete PDB](#delete-pdb) + - [SQL/PLSQL SCRIPT EXECUTION](#sqlplsql-script-execution) + - [Apply plsql configmap](#apply-plsql-configmap) + - [Limitation](#limitation) + - [TROUBLESHOOTING](#troubleshooting) + - [Get rid of error status](#get-rid-of-error-status) + - [UPGRADE EXISTING INSTALLATION](#upgrade-existing-installation) + - [KNOWN ISSUE](#known-issue) + + + + + +**Lrpdb** and **lrest** are two controllers for PDB lifecycle management (**PDBLCM**). They rely on a dedicated REST server (Lite Rest Server) Container image to run. The `lrest` controller is available on the Oracle Container Registry (OCR). The container database can be anywhere (on-premises or in the Cloud). + +![generaleschema](./images/Generalschema2.jpg) + +## WHAT'S NEW + +- Version 2.0 introduces the semplification requested by the [issue 170](https://github.com/oracle/oracle-database-operator/issues/170). The **action** fileds no longer exists. This change brings a big semplification in terms of code and symplifies the life cycle command execution. The *kubectl get lrpdb* commands exposes the status of CRD which is reppresented by a bitmask. + +![kubectlget_format](./images/KubectlGetSchema2.jpg) + +- **Map** command is no longer available; start the lrest using the **autodoscovery** option. If a pluggable database is created manually via command line then the lrest detects the new pdb and automatically creates the CRD. + +- [sql/plsql sript execution](#sqlplsql-script-execution) this functionality enables the capability of sql/plsql code execution. + +### Kubectl get lrpdb format +| Name | Description | +|---------------|---------------------------------------------------------| +| NAME | The name of the **CRD** | +| CDB NAME | The name of the container DB | +| PDB NAME | The name of the **Pluggable database** | +| PDB STATE | The pdb open mode | +| PDB SIZE | Size of the Pluggable database | +| MESSAGE | Message to verify the progress of the current request | +| RESTRICED | Bool variable: database open in restricted mode | +| LAST SQLCODE | Sqlcode of the last command (see [OCIErrorGet](https://docs.oracle.com/en/database/oracle/oracle-database/19/lnoci/miscellaneous-functions.html#GUID-4B99087C-74F6-498A-8310-D6645172390A)) | +| LAST PLSQL | Sqlcode of the last plsql execution | +| BITMASK STATUS| The status of pdb represented using a bitmask | +| CONNECT_STRING| The tns string for pdb connection | + +### Pdb status table + +| Name | Value | Description | +|---------|-----------|---------------------------------------------------| +| PDBCRT |0x00000001 | Create PDB | +| PDBOPN |0x00000002 | Open pdb read write | +| PDBCLS |0x00000004 | Close pdb | +| PDBDIC |0x00000008 | Drop pdb include datafiles | +| OCIHDL |0x00000010 | OCI handle allocation (**for future use**) | +| OCICON |0x00000020 | Rdbms connection (**for future use**) | +| FNALAZ |0x00000040 | Finalizer configured | +| **ERROR CODES** | +| PDBCRE |0x00001000 | PDB creation error | +| PDBOPE |0x00002000 | PDB open error | +| PDBCLE |0x00004000 | PDB close error | +| OCIHDE |0x00008000 | Allocation Handle Error (**for future use**) | +| OCICOE |0x00010000 | CDB connection Error (**for future use**) | +| PDBPLE |0x00080000 | Plug Error | +| **OTHER INFO** | +| PDBAUT | 0x01000000 | Autodisover | + +> Note that in case of error codes the reconciliation loop does not take any action see +[Get rid of error status](#get-rid-of-error-status); human action is required. + +## STEP BY STEP CONFIGURATION +Complete each of these steps in the order given. + +### Multiple namespace setup + +Before proceeding with controllers setup, ensure that the Oracle Database Operator (operator) is configured to work with multiple namespaces, as specified in the [README](../../../README.md). +In this document, each controller is running in a dedicated namespace: lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains all the files reported in this document. + +Configure the **WACTH_NAMESPACE** list of the operator `yaml` file + +```bash +sed -i 's/value: ""/value: "oracle-database-operator-system,pdbnamespace,cdbnamespace"/g' oracle-database-operator.yaml +``` + +### Create the operator +Run the following command: + +```bash +kubectl apply -f oracle-database-operator.yaml +``` +Check the controller: +```bash +kubectl get pods -n oracle-database-operator-system +NAME READY STATUS RESTARTS AGE +oracle-database-operator-controller-manager-796c9b87df-6xn7c 1/1 Running 0 22m +oracle-database-operator-controller-manager-796c9b87df-sckf2 1/1 Running 0 22m +oracle-database-operator-controller-manager-796c9b87df-t4qns 1/1 Running 0 22m +``` +### Container database setup + +On the container database, use the following commands to configure the account for PDB administration: + +```sql +alter session set "_oracle_script"=true; +create user identified by ; +grant create session to container=all; +grant sysdba to container=all; +``` + +### Apply rolebinding + + +Apply the following files : [`pdbnamespace_binding.yaml`](./usecase/pdbnamespace_binding.yaml) [`cdbnamespace_binding.yaml`](./usecase/cdbnamespace_binding.yaml) +```bash +kubectl apply -f pdbnamespace_binding.yaml +kubectl apply -f cdbnamespace_binding.yaml +``` + +### Certificate and credentials +You must create the public key, private key, certificates and Kubernetes Secrets for the security configuration. + +#### Private key 🔑 +> Note: Only private key **PCKS8** format is supported by LREST controllers. Before you start configuration, ensure that you can use it. If you are using [`openssl3`](https://docs.openssl.org/master/) then `pcks8` is generated by default. If it is not already generated, then use the following command to create a `pcks8` private key + +```bash +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out private.key +``` +#### Public Key 🔑 +Create the public key. + +```bash +/usr/bin/openssl rsa -in private.key -outform PEM -pubout -out public.pem +``` +#### Certificates +Create certificates. +```bash +openssl req -new -x509 -days 365 -key private.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out ca.crt +``` +```bash +openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=cdb-dev-lrest.cdbnamespace" -out server.csr +``` +```bash +/usr/bin/echo "subjectAltName=DNS:cdb-dev-lrest.cdbnamespace,DNS:www.example.com" > extfile.txt +``` +```bash +/usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey private.key -CAcreateserial -out tls.crt +``` + +### Create secrets for certificate and keys +Create the Kubernetes Secrets. + +```bash +kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system +kubectl create secret generic db-ca --from-file="ca.crt" -n oracle-database-operator-system +kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n cdbnamespace +kubectl create secret generic db-ca --from-file="ca.crt" -n cdbnamespace +kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n pdbnamespace +kubectl create secret generic db-ca --from-file="ca.crt" -n pdbnamespace +``` + +```bash +kubectl create secret tls prvkey --key="private.key" --cert=ca.crt -n cdbnamespace +kubectl create secret generic pubkey --from-file=publicKey=public.pem -n cdbnamespace +kubectl create secret generic prvkey --from-file=privateKey="private.key" -n pdbnamespace +``` + +### Create secrets with encrypted password + +In this example, we create the Secrets for each credential (username and password) + +| secret usr | secrets pwd | credential description | +| -----------|-------------|-----------------------------------------------------------| +| **dbuser** |**dbpass** | the administrative user created on the container database | +| **wbuser** |**wbpass** | the user for https authentication | +| **pdbusr** |**pdbpwd** | the administrative user of the pdbs | + + +```bash +echo "[ADMINUSERNAME]" > dbuser.txt +echo "[ADMINUSERNAME PASSWORD]" > dbpass.txt +echo "[WEBUSER]" > wbuser.txt +echo "[WEBUSER PASSWORD]" > wbpass.txt +echo "[PDBUSERNAME]" > pdbusr.txt +echo "[PDBUSERNAME PASSWORD]" > pdbpwd.txt + +## Encrypt the credentials +openssl rsautl -encrypt -pubin -inkey public.pem -in dbuser.txt |base64 > e_dbuser.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in dbpass.txt |base64 > e_dbpass.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in wbuser.txt |base64 > e_wbuser.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in wbpass.txt |base64 > e_wbpass.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in pdbusr.txt |base64 > e_pdbusr.txt +openssl rsautl -encrypt -pubin -inkey public.pem -in pdbpwd.txt |base64 > e_pdbpwd.txt + +kubectl create secret generic dbuser --from-file=e_dbuser.txt -n cdbnamespace +kubectl create secret generic dbpass --from-file=e_dbpass.txt -n cdbnamespace +kubectl create secret generic wbuser --from-file=e_wbuser.txt -n cdbnamespace +kubectl create secret generic wbpass --from-file=e_wbpass.txt -n cdbnamespace +kubectl create secret generic wbuser --from-file=e_wbuser.txt -n pdbnamespace +kubectl create secret generic wbpass --from-file=e_wbpass.txt -n pdbnamespace +kubectl create secret generic pdbusr --from-file=e_pdbusr.txt -n pdbnamespace +kubectl create secret generic pdbpwd --from-file=e_pdbpwd.txt -n pdbnamespace + +rm dbuser.txt dbpass.txt wbuser.txt wbpass.txt pdbusr.txt pdbpwd.txt \ + e_dbuser.txt e_dbpass.txt e_wbuser.txt e_wbpass.txt e_pdbusr.txt e_pdbpwd.txt +``` + +### Create lrest pod + +To create the REST pod and monitor its processing, use the `yaml` file [`create_lrest_pod.yaml`](./usecase/create_lrest_pod.yaml) + +Ensure that you update the **lrestImage** with the latest version available on the [Oracle Container Registry (OCR)](https://container-registry.oracle.com/ords/f?p=113:4:104288359787984:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1283,1283,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,1,0&cs=3076h-hg1qX3eJANBcUHBNBCmYWjMvxLkZyTAhDn2e8VR8Gxb_a-I8jZLhf9j6gmnimHwlP_a0OQjX6vjBfSAqQ) + +```bash +--> for amd64 +lrestImage: container-registry.oracle.com/database/operator:lrest-241210-amd64 + +--> for arm64 +lrestImage: container-registry.oracle.com/database/operator:lrest-241210-arm64 +``` + +```bash +kubectl apply -f create_lrest_pod.yaml +``` + +monitor the file processing: + +```bash +kubectl get pods -n cdbnamespace --watch +NAME READY STATUS RESTARTS AGE +cdb-dev-lrest-rs-9gvx2 0/1 Pending 0 0s +cdb-dev-lrest-rs-9gvx2 0/1 Pending 0 0s +cdb-dev-lrest-rs-9gvx2 0/1 ContainerCreating 0 0s +cdb-dev-lrest-rs-9gvx2 1/1 Running 0 2s + +kubectl get lrest -n cdbnamespace +NAME CDB NAME DB SERVER DB PORT TNS STRING REPLICAS STATUS MESSAGE +cdb-dev DB12 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) 1 Ready +``` + +Check the Pod logs: + +```bash +/usr/local/go/bin/kubectl logs -f `/usr/local/go/bin/kubectl get pods -n cdbnamespace|grep lrest|cut -d ' ' -f 1` -n cdbnamespace +``` + +Output example: + +```text +... +... +2024/09/05 12:44:09 wallet file /opt/oracle/lrest/walletfile exists completed +2024/09/05 12:44:09 call: C.ReadWallet +LENCHECK: 7 11 7 8 +2024/09/05 12:44:09 ===== DUMP INFO ==== +00000000 28 44 45 53 43 52 49 50 54 49 4f 4e 3d 28 43 4f |(DESCRIPTION=(CO| +00000010 4e 4e 45 43 54 5f 54 49 4d 45 4f 55 54 3d 39 30 |NNECT_TIMEOUT=90| +00000020 29 28 52 45 54 52 59 5f 43 4f 55 4e 54 3d 33 30 |)(RETRY_COUNT=30| +00000030 29 28 52 45 54 52 59 5f 44 45 4c 41 59 3d 31 30 |)(RETRY_DELAY=10| +00000040 29 28 54 52 41 4e 53 50 4f 52 54 5f 43 4f 4e 4e |)(TRANSPORT_CONN| +00000050 45 43 54 5f 54 49 4d 45 4f 55 54 3d 37 30 29 28 |ECT_TIMEOUT=70)(| +00000060 4c 4f 41 44 5f 42 41 4c 4c 41 4e 43 45 3d 4f 4e |LOAD_BALLANCE=ON| +00000070 29 28 41 44 44 52 45 53 53 3d 28 50 52 4f 54 4f |)(ADDRESS=(PROTO| +00000080 43 4f 4c 3d 54 43 50 29 28 48 4f 53 54 3d 73 63 |COL=TCP)(HOST=sc| +00000090 61 6e 31 32 2e 74 65 73 74 72 61 63 2e 63 6f 6d |an12.testrac.com| +000000a0 29 28 50 4f 52 54 3d 31 35 32 31 29 28 49 50 3d |)(PORT=1521)(IP=| +000000b0 56 34 5f 4f 4e 4c 59 29 29 28 4c 4f 41 44 5f 42 |V4_ONLY))(LOAD_B| +000000c0 41 4c 4c 41 4e 43 45 3d 4f 4e 29 28 41 44 44 52 |ALLANCE=ON)(ADDR| +000000d0 45 53 53 3d 28 50 52 4f 54 4f 43 4f 4c 3d 54 43 |ESS=(PROTOCOL=TC| +000000e0 50 29 28 48 4f 53 54 3d 73 63 61 6e 33 34 2e 74 |P)(HOST=scan34.t| +000000f0 65 73 74 72 61 63 2e 63 6f 6d 29 28 50 4f 52 54 |estrac.com)(PORT| +00000100 3d 31 35 32 31 29 28 49 50 3d 56 34 5f 4f 4e 4c |=1521)(IP=V4_ONL| +00000110 59 29 29 28 43 4f 4e 4e 45 43 54 5f 44 41 54 41 |Y))(CONNECT_DATA| +00000120 3d 28 53 45 52 56 45 52 3d 44 45 44 49 43 41 54 |=(SERVER=DEDICAT| +00000130 45 44 29 28 53 45 52 56 49 43 45 5f 4e 41 4d 45 |ED)(SERVICE_NAME| +00000140 3d 54 45 53 54 4f 52 44 53 29 29 29 |=TESTORDS)))| +00000000 2f 6f 70 74 2f 6f 72 61 63 6c 65 2f 6c 72 65 73 |/opt/oracle/lres| +00000010 74 2f 77 61 6c 6c 65 74 66 69 6c 65 |t/walletfile| +2024/09/05 12:44:09 Get credential from wallet +7 +8 +2024/09/05 12:44:09 dbuser: restdba webuser :welcome +2024/09/05 12:44:09 Connections Handle +2024/09/05 12:44:09 Working Session Aarry dbhanlde=0x1944120 +2024/09/05 12:44:09 Monitor Session Array dbhanlde=0x1a4af70 +2024/09/05 12:44:09 Open cursors +Parsing sqltext=select inst_id,con_id,open_mode,nvl(restricted,'NONE'),total_size from gv$pdbs where inst_id = SYS_CONTEXT('USERENV','INSTANCE') and name =upper(:b1) +Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 +2024/09/05 12:44:11 Protocol=https +2024/09/05 12:44:11 starting HTTPS/SSL server +2024/09/05 12:44:11 ==== TLS CONFIGURATION === +2024/09/05 12:44:11 srv=0xc000106000 +2024/09/05 12:44:11 cfg=0xc0000a2058 +2024/09/05 12:44:11 mux=0xc0000a2050 +2024/09/05 12:44:11 tls.minversion=771 +2024/09/05 12:44:11 CipherSuites=[49200 49172 157 53] +2024/09/05 12:44:11 cer=/opt/oracle/lrest/certificates/tls.crt +2024/09/05 12:44:11 key=/opt/oracle/lrest/certificates/tls.key +2024/09/05 12:44:11 ========================== +2024/09/05 12:44:11 HTTPS: Listening port=8888 +2024/09/05 12:44:23 call BasicAuth Succeded +2024/09/05 12:44:23 HTTP: [1:0] Invalid credential <-- This message can be ignored + +``` + +**lrest Pod creation** - parameters list +| Name | Dcription | +--------------------------|-------------------------------------------------------------------------------| +|cdbName | Name of the container database (db) | +|lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** use the latest label availble on OCR | +|dbTnsurl | The string of the tns alias to connect to cdb. Attention: remove all white space from string | +|deletePdbCascade | Delete all of the PDBs associated to a CDB resource when the CDB resource is dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | +|autodiscover | boolean parameter: enable the capability of automatic CRD/LRPDB creation if a PDB is manually created via CLI | +|cdbAdminUser | Secret: the administrative (admin) user | +|cdbAdminPwd | Secret: the admin user password | +|webServerUser | Secret: the HTTPS user | +|webServerPwd | Secret: the HTTPS user password | +|cdbTlsCrt | Secret: the `tls.crt ` | +|cdbPubKey | Secret: the public key | +|cdbPrvKey | Secret: the private key | +|loadBalancer | Expose lrest pod ip | +|clusterip | Assigne a cluster ip | +|trace_level_client | Turn on the sqlnet **trace_level_client** | + +### Openshift configuration + +For the open shift installation you need to do the following +- Before lrest pod creation; create a security context by appling the following yaml file [security_context.yaml](./usecase/security_context.yaml) mind to specify the correnct namespace and the service name account. + +```yaml +[...] +apiVersion: v1 +kind: ServiceAccount +metadata: + name: lrest-sa + namespace: cdbnamespace +[...] +``` + +- Specify `serviceAccountName` parameter in the lrest server yaml file + +```yaml +[...] + serviceAccountName: lrest-sa +[...] +``` + + +### Create PDB + +To create a pluggable database, apply the yaml file [`create_pdb1_resource.yaml`](./usecase/create_pdb1_resource.yaml) + +```bash +kubectl apply -f create_pdb1_resource.yaml +``` +Check the status of the resource and the PDB existence on the container db: + +```bash +kubectl get lrpdb -n pdbnamespace +NAME CONNECT_STRING CDB NAME LRPDB NAME LRPDB STATE LRPDB SIZE STATUS MESSAGE LAST SQLCODE +lrpdb1 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) DB12 pdbdev MOUNTED 2G Ready Success +``` + +```bash +SQL> show pdbs + + CON_ID CON_NAME OPEN MODE RESTRICTED +---------- ------------------------------ ---------- ---------- + 2 PDB$SEED READ ONLY NO + 3 PDBDEV MOUNTED +SQL> +``` +``Note that after creation, the PDB is not open. You must explicitly open it using a dedicated `yaml` file. + +**pdb creation** - parameters list + +| Name | Dcription | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database | +|pdbName | Name of the PDB that you want to create | +|assertiveLrpdbDeletion | Boolean: Turn on the imperative approach on PDB resource deletion | +|adminpdbUser | Secret: PDB admin user | +|adminpdbPass | Secret: password of PDB admin user | +|lrpdbTlsKey | Secret: `tls.key ` | +|lrpdbTlsCrt | Secret: `tls.crt` | +|lrpdbTlsCat | Secret: `ca.crt` | +|webServerUser | Secret: the HTTPS user | +|webServerPwd | Secret: the HTTPS user password | +|cdbPrvKey | Secret: private key | +|cdbPubKey | Secret: public key | +|pdbconfigmap | kubernetes config map that contains the PDB initialization (init) parameters | + +> NOTE: **assertiveLrpdbDeletion** must be specified for the following PDB actions **CLONE** **CREATE** **PLUG** **MAP**. + +🔥 **assertiveLrpdbDeletion** drops pluggable database using **INCLUDE DATAFILES** option + +All of the parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** must be specified in all PDB lifecycle management `yaml` files. To simplify presentation of requirements, we will not include them in the subsequent tables. + + +#### pdb config map + +By using **pdbconfigmap** it is possible to specify a kubernetes `configmap` with init PDB parameters. The config map payload has the following format: + + +``` +;; +;; +;; +.... +.... +;; +``` + +Example of `configmap` creation: + +```bash +cat < parameters.txt +session_cached_cursors;100;spfile +open_cursors;100;spfile +db_file_multiblock_read_count;16;spfile +EOF + +kubectl create configmap config-map-pdb -n pdbnamespace --from-file=./parameters.txt + +kubectl describe configmap config-map-pdb -n pdbnamespace +Name: config-map-pdb +Namespace: pdbnamespace +Labels: +Annotations: + +Data +==== +parameters.txt: +---- +session_cached_cursors;100;spfile +open_cursors;100;spfile +db_file_multiblock_read_count;16;spfile +test_invalid_parameter;16;spfile +``` + +- If specified, the `configmap` is applied during PDB **cloning**, **opening** and **plugging** +- The `configmap` is not monitored by the reconciliation loop; this feature will be available in future releases. This means that if someone decides to manually alter an init parameter, then the operator does not take any actions to syncronize PDB configuration with the `configmap`. +- **Alter system parameter feature** will be available in future releases. +- An application error with the `configmap` (for whatever reason) does not stop processes from completing. A warning with the associated SQL code is reported in the log file. + +- **PDB configmap bitmap** status is not reported by the *kubectl get lrpdb* command; in order to verifiy configmap status you need to describe the resource. + +| Name | Value | Description | +|---------|-----------|---------------------------------------------------| +| MPAPPL | 0x00000001|The map config has been applyed | +| MPSYNC | 0x00000002|The map config is in sync with v$parameters where is default=flase (**not yet available**)| +| MPEMPT | 0x00000004| The map is empty - not specify | +| MPWARN | 0x00000008| Map applied with warnings | +| MPINIT | 0x00000010| Config map init + + + +### Open PDB + +To open the PDB, use the file [`open_pdb1_resource.yaml`](./usecase/open_pdb1_resource.yaml): + +```bash +kubectl apply -f open_pdb1_resource.yaml +``` + + **pdb opening** - parameters list + +| Name | Description/Value | +|-------------------------|------------------------------------------------------------------------| +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | +|pdbName | Name of the pluggable database (PDB) that you are creating | +|pdbState | Use `OPEN` to open the PDB | +|modifyOption | Use **READ WRITE** to open the PDB | +|modifyOprion2 | Default is NONE, set to **RESTRICT** to open the pdb in restrited mode | + +**Imperative command:** + +```bash +kubectl patch lrpdb [lrpdb_resource_name] -n [ppdb_namespace] -p \ + '{"spec":{"pdbState":"OPEN","modifyOption":"READ WRITE","modifyOption2":"NONE"}}' --type=merge +``` + +### Close PDB + +To close the PDB, use the file [`close_pdb1_resource.yaml`](./usecase/close_pdb1_resource.yaml): + +```bash +kubectl apply -f close_pdb1_resource.yaml +``` +**pdb closing** - parameters list +| Name | Description/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | +|pdbName | Name of the pluggable database (PDB) that you want to create | +|pdbState | Use `CLOSE` to close the PDB | +|modifyOption | Use **IMMEDIATE** to close the PDB | + + +**Imperative command:** + +```bash +kubectl patch lrpdb [lrpdb_resource_name] -n [ppdb_namespace] -p \ + '{"spec":{"pdbState":"CLOSE","modifyOption":"IMMEDIATE"}}' --type=merge +``` + +### Clone PDB ### + +To clone the PDB, use the file [`clone_pdb1_resource.yaml`](./usecase/clone_pdb1_resource.yaml): + +```bash +kubeclt apply -f clone_pdb1_resource.yaml +``` +**pdb cloning** - parameters list +| Name | Description/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | +|pdbName | The name of the new pluggable database (PDB) | +|`srcPdbName` | The name of the source PDB | +|fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | +|totalSize | Set **unlimited** for cloning | +|tempSize | Set **unlimited** for cloning | +|pdbconfigmap | kubernetes **configmap** which contains the PDB init parameters | + + +### Unplug PDB + +To unplug the PDB, use the file [`unplug_pdb1_resource.yaml`](./usecase/unplug_pdb1_resource.yaml): + +**pdb unplugging** +| Name | Description/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | +|pdbName | Name of the pluggable database (PDB) | +|xmlFileName | Unplug xmlfile path | +|pdbState | `UNPLUG` + +### Plug PDB + +To plug in the PDB, use the file [`plug_pdb1_resource.yaml`](./usecase/plug_pdb1_resource.yaml). In this example, we plug in the PDB that was unpluged in the previous step: + +**pdb plugging** +| Name | Description/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB)| | +|pdbName | Name of the pluggable database (PDB) | +|**xmlFileName** | XML file path | +|fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | +|sourceFileNameConversion | See parameter [SOURCE_FILE_NAME_CONVERT](https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/CREATE-PLUGGABLE-DATABASE.html#GUID-F2DBA8DD-EEA8-4BB7-A07F-78DC04DB1FFC__CCHEJFID) documentation | +|pdbconfigmap | Kubernetes `configmap` that contains the PDB init parameters | +|pdbState | `PLUG` + +### Delete PDB + +To delete the PDB, use the file [`delete_pdb1_resource.yaml`](./usecase/delete_pdb1_resource.yaml) + +**pdb deletion** + +| Name | Dcription/Value | +|-------------------------|-------------------------------------------------------------------------------| +|cdbResName | REST server resource name | +|cdbNamespace | Namespace of the REST server | +|cdbName | Name of the container database (CDB) | +|pdbState | `DELETE` | +|dropAction | **INCLUDING** - Including datafiles or **NONE** | + +**Imperative command** + +```bash +kubectl delete lrpdb -n +``` +## SQL/PLSQL SCRIPT EXECUTION + + +Plsql and sql script can be stored in a kubernetes configmap, each block can be tagged with a label as describe in the following example. + +```yaml + +## PLSQL / SQL Block config schema + +apiVersion +kind CofigMap + name: + namespace: +data: +:| + +:| + +[...] + + +``` + +![plsqlblock](./images/plsqlmap.jpg) + +The sql and plsqlcode must be indented using tab (makefile stile). The code blocks will be executed following the alphabetic tag order. + +### Apply plsql configmap + +```bash +kubectl patch lrpdb pdb1 -n pdbnamespace -p '{"spec":{"codeconfigmap":""}}' --type=merge +``` + +The **kubectl get** commands shows only the return code of the last plsql code executed. +If you need to see the overall status of the whole config map execution check the events history as reported in the following example + +```bash +/usr/bin/kubectl patch lrpdb pdb1 -n pdbnamespace -p \ + '{"spec":{"codeconfigmap":"sql-map-example1"}}' --type=merge +lrpdb.database.oracle.com/pdb1 patched + + +/usr/bin/kubectl get events --sort-by='.lastTimestamp' -n pdbnamespace +LAST SEEN TYPE REASON OBJECT MESSAGE +66s Normal Created lrpdb/pdb1 LRPDB 'pdbdev' created successfully +66s Normal Created lrpdb/pdb1 PDB 'pdbdev' imperative pdb deletion turned on +57s Normal Modify lrpdb/pdb1 Info:'pdbdev OPEN ' +50s Normal Modified lrpdb/pdb1 'pdbdev' modified successfully 'OPEN' +38s Warning lrpdbinfo lrpdb/pdb1 pdb=pdbdev:test_invalid_parameter:16:spfile:2065 +11s Normal APPLYSQL-143002 lrpdb/pdb1 CODE:SQLCODE '[plblock1.sql]':'0' +8s Normal APPLYSQL-143005 lrpdb/pdb1 CODE:SQLCODE '[plblock2.sql]':'0' +5s Normal APPLYSQL-143008 lrpdb/pdb1 CODE:SQLCODE '[plblock3.sql]':'0' +2s Normal APPLYSQL-143011 lrpdb/pdb1 CODE:SQLCODE '[plblock4.sql]':'0' +``` +The message format for the **APPLYSQL** is `CODE:SQLCODE '[]':''` + +### Limitation + +- All the objects in the plsql confgmap must be rapresented in the form `.`. Due to this constraint it's not possible to rename table + +```bash ++----------------------------------------------------------------------+ +| plblock1.sql: | | +| rename plsqltestuser.k8splsqltab to plsqltestuser.tablerename |--------------+ ++----------------------------------------------------------------------+ | + | + + +3m55s Warning APPLYSQL-100536 lrpdb/pdb1 CODE:SQLCODE '[plblock1.sql]':'1765' + +``` + +- The nummber of code lines is limited the configmap capability. To workaround this limitation you can use mode configmap. + +## TROUBLESHOOTING + +### Get rid of error status + +If for some reason you get an operation failure it's possible to manually fix the problem and then rest the bitmask status to re-execute the operation. Consider the following example: the unplug command does not complete successfully because xmlfile already exists ; **ORA-65170** and **PDBUPE** flag is on. You can remove the file and then rest the bitmask status in order to retry the operation. + +```text +RESOURCE STATUS: +~~~~~~~~~~~~~~~~ +kubectl get lrpdb -n pdbnamespace +NAME CDB NAME PDB NAME PDB STATE PDB SIZE MESSAGE RESTRICTED LAST SQLCODE LAST PLSQL BITMASK STATUS CONNECT_STRING +pdb1 DB12 pdbdev MOUNTED 0.80G close:[ORA-65170] NONE 65170 [262213]|PDBCRT|PDBCLS|FNALAZ|PDBUPE| (DESCRIPTION=(CONNECT_TIMEOUT.... + +FIX THE PROBLEM: +~~~~~~~~~~~~~~~~ +RM THE XMLFILE + +UPDATE THE BITMASK STATUS: +~~~~~~~~~~~~~~~~~~~~~~~~~ +Calculate the bitmask status without the PDBUPE flag and patch the resource +[262213]|PDBCRT|PDBCLS|FNALAZ|PDBUPE| -> [69]|PDBCRT|PDBCLS|FNALAZ| = 0x00000001 | 0x00000004 | 0x00000040 + +------+ +kubectl patch lrpdb pdb1 -n pdbnamespace -p \ + '{"spec":{"pdbState":"RESET","reststate":69}}' --type=merge + +kubectl get lrpdb -n pdbnamespace +NAME CDB NAME PDB NAME PDB STATE PDB SIZE MESSAGE RESTRICTED LAST SQLCODE LAST PLSQL BITMASK STATUS CONNECT_STRING +pdb1 DB12 pdbdev MOUNTED 0.80G close:[ORA-65170] NONE 65170 [69]|PDBCRT|PDBCLS|FNALAZ| (DESCRIPTION=(CONNECT_TIMEOUT.... + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + [READY TO BE UNPLUGGED] +``` +## UPGRADE EXISTING INSTALLATION + +## KNOWN ISSUE + +- Error message `ORA-01005` is not reported in the lrest database login phase if password is mistakenly null. Trace log shows the message ORA-1012. + + \ No newline at end of file diff --git a/docs/multitenant/lrest-based/images/Generalschema2.jpg b/docs/multitenant/images/Generalschema2.jpg similarity index 100% rename from docs/multitenant/lrest-based/images/Generalschema2.jpg rename to docs/multitenant/images/Generalschema2.jpg diff --git a/docs/multitenant/images/KubectlGetSchema.jpg b/docs/multitenant/images/KubectlGetSchema.jpg new file mode 100644 index 0000000000000000000000000000000000000000..93b107cc4c92bbbddb5071d3c92582c0ca7ffb57 GIT binary patch literal 206768 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 z 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

#N%6u{LazDD?+Sr^fbdP4nocznTMUdH0K{C5 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 z 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 H?^FK+jj=-) literal 0 HcmV?d00001 diff --git a/docs/multitenant/images/KubectlGetSchema2.jpg b/docs/multitenant/images/KubectlGetSchema2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9df197c85b9375e25caf2cfb387c4c94814f6961 GIT binary patch literal 187365 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 *BK>+4 z4BG;5paK>I0uYAu0=wtm5P44VOwI2Z7c7|x*p&)Sg)5HuPnglsbbwR|?ga31EK_3P zDbDt4+R?#)3hg)}%n*BKqa&%pGGWN%1HwCvpHUpvHHM6$IZtb?*F?>^?k*MZaED=2 zYQZ=^hjDr}u0I1=@9|kP6c^7|*iSI^*H6EdatN{-?-85ostr`nT$UX}a0!k}S4ggne9fPqRVb%jb!> z>BMIXD8JT`DVC(o?F{7BVgYLl1WWF7ByeCH*@EwA;=9+FF=s9EqaB9M9XFus!X=OL z;6|n9Qul%lEwtME*$cvfxUzy^!u-qlkmFY>)I7tcxs;96i0b;T!BV_jmw6QfItk8b z2_rG34DNeCB_69J3L9<$7VX>n(s{JF4A9S5!)_WDBBTD($sCW~N;?=Ef7lzy6fq(X z_nwO7aWqAQ<$oP0vj4#Kjs;|R8Fe?CQKW@Jx4!uOVAds%{#W1l9Ei?XQRR*uXC;CW z+d{y()h(p_i2PBtix$#2=TwPW69tqMnyVbwo7n5^m8)gYTz#bIJjTpREQ8@o!x+iuTQ zzGEkZfpPumREBAMnJUY3ftIMnGH-w-6W30Mj?Dzc&ZpIp)bjbK&qxNIUtEV}aeW8n z-Q+Xl&=$5mCyBotXOvfdu2ph>5QFYBZMy)|ppOshgZMW?7XB*Q3wSu-3P$&Wa=dYw zy%Hrx5r(^xWqj7msy0UYIu0}8URH?T1N580Zq{nYVEOW|(~-p4Zzc1!sk0G* zZgOTbx?!G4eC9`p%rtN}B{91M1EJX<_}n;_*JQjIaqt3nSQN)&@hWu_@~_W zyc7M5n4C}oBmMhYv7BWLgOqCMIK02r-ILU->GArE^yR9Z1AVGI zU+pS8g?ex?)&$7w#2;gn5$kZUP_bP-lhJl5+|WGT<64e{>gA-%5(A{ z7sHEe)r!-v`B)Fg^b90TkEAFNI)QL%9 zcmWx(Qp80nK12MYb;{hBY)X2exz+vW(}{3fC1aa;O{(y6jgA|0+3?!^)MXW@}ehh^xAK6C=5Q^7R-U0Kzr`EYqPib-);vC-_>Jf=F zw-FjBBMOdoCWB7DjZ zCLDxEtwDOsoDu*7k>JjP49YUv0<4l(ul?I84Cv=*0V|iwk*`^VO z(l6DfUk4|T399cMo>N6IHqF_ByCyIlBAMv$wXGYm%_D@|TFr;Y*7(YyJdQ_e22jLD zwNLHuMG%gwTY~O(p$Yy7Yu2Vs#?oW~Dm8-E+My1Coo980N#R1Tk`fsQ zlQE+M?nezYC#*X9pQOHVTYfFzi@tF^CIT&Ws->_Fo@`SeQED5YwuI;?iD9I;VWlh? z#8xul0mF~##L|WuUq!n-OO%4i<19m$>E4w35RvCuT5C~@gjYoI)FZ|9O34|85;pY} z09ujcxOR4cjs4M#`wtJllm`VDx4JgK^kOb7#rf|VQzeSLz|@j7qFi_n*4LilR9Vk7 zc_E68yCW(@^)yT+_&aj8fqMRnC z=utNi#%;}n_AvT}vrKknv!peuopBAbHgn>>wl`)uKCUeb`y5ZEK&tM=k|vD8$Wn*n zEMQn0L_fwgHhjlZ(=Pu4?Og;|r2TmidX47qB^11^GrgBKD6>&JFB-rjXGTfOV+W>P zHsH8+k%hNqViC@r+U9%eeQ_dVi~&*4H#n}XlwBz_aQ22*X&Q4Ljl(pdm1N$=;(!HF$jAHSq zMKa(3`n-QRS5oM9X9;^@{(O`}ia)P4G>7jRQ_OQ7YK?%rJtVg8HzA7NES}!yyrV@=Zv?I1l%L-YShZ(-O4>SyOogW6&*Cv2+ zP$*C!IQ{);KwHLA76lAQu%`q&6 zU&HpN(kq<%n1?b5^t}3*_1)~6-deYz5rLaVG-WU`iCxysiI|cGONNID4jyUIXRLbb z-gMd`pD)wmitWk0(CF`wud{OU#2{^oMJ_TLO*kH9vb2u-KosW`ss{HwotuTCznN|_ z)zB6%M9#6H-XG|-$xPd@oK5czZ09*+oxAF#811;z1oG{(X8Ubq6|~wL4YbA^3S9U^#VW>)QHTLmzeP%ENN^wQaTYxmOab08mQlaEUg~sqq z4J8`Cm_M$PE?u`8!E;(EFN4D4t!(X#7c}2cv4oD9)kwVuwJ6&G@Q@n$_}ZsvOmYks z7Ri~+)tu0%T0uVh!ysMrv>_*W%Ig+I#{)7y-P=>16ep5%qyXUqy{i^S0F9P9jZqQ?#2P`Eq(Bz#CJ4tsRIgC6?&2u<|#l1TIPdE|NV9ro!X3L9Tr zcA?Qt2gN{Mu)uJ_Tp;B7?HJg^UI_yQFROJq#v$FRxNxeit&z!mYr4wM_vPX%zDsg? z$*_v3^^z=NUXbSg-J;~d#TbvbTA+eRuQHrIv4w2`!lpW|Sz9!?_Du1|eH~wePgsenofjKqb#r_M@8loL!0sFEif}L2}7U zo<25jdtS~M9uzxwNe$(?K9B7qZ_M!TDyrkrpc2*(iwC@s%8HD0|JrS z`|i{yKOwfwe0ASDJAaW^S(RWESZyP;4ipj_mP5$tv+I{GDnhQ{!?W&Sb#$JDqC z#+U8>x0-aw*^sr0l!R*><|F5i|2nZ6M;l za#Q&4g?STOf@ye6RteEYDtBf}TY&=)@uzM?sFp0GeU z8$oY}p?VS%^{$tN~m_14ILWaEOL^ zERAazDn7`2cC_R~A=$Ox(6+A)8M(gaaiKQoJ10QOWDARmoE0rAZc9o28sw$*xKQJV zDIH0w0>N8@YP6V!_H;Q^M(`657 zCK5-v*6(mPsYkl!#dne`{6oF#7``L~o+sVxlYx-NwtIUCWd*O^xH6}0FDaMf5;0R2 zZg4P-VV98d787JyXgN@++a>i%#@7G?0OUvoqV#er*?IKqwMw^!_h*m=c6-_u?Rt4C zJwvz;t-RM`PYfnzZu>XC|4Cj7T<9Nyo!>a20sG;1Rn@ya&@boGUuQvuWIm_?leV}GfOhW2;%UH4;=LuFD3jWRwh$A z2Fd6EKp@QQN597@Xw`9(d-h%ZY?)WzxsaD>|KOoDg?GVMBip+AC(ri5#27w2G6|S( zFAwF-=_WIv`^7Owwv!Bp1Q;?{%YnD(28_4$bJ zk9zrLhXdMws|uao)cMn4mxye`BdG}=y!lC=H%KlO+OU>Edd_HwsXMN|BFJIohAq-V zGV@>?K5?yTM5}j>qhy`ddx{v4PfYY*8sfyKTva*9!OY+o?vV&3)L)D^K_@`+_XWK= zXt0Xz-GAU=(NObJ+lXz4Em?)7$MIaY3FEl$87?d0b>K?-H68-Kz4CkQhD~bnoIIq) zgD{Dp1z1IHZBK>m9XHxm%X$K}C-w4oDJ1Y?nxY4#nG-tY5^g2`&rQw0p3R)DxsUaS zy3Uq9lke%{r|-b*CSTF_6C-jY!)#KN@Fh%fn~$3LFl^yZ%gYqel$yRZRY|UM`x8_} zoL1dnzK$+(H6ijBkzpuD7QQ6(r5oBAw`^DTA|c#(k9WwMpXKqUj^8D2r@*0p&DrTh zNNq63LQ;JQsqmA*!Kz_g>Q0{nWtV!U82MDhEn6*CK-a6w`gH~nRp$|DUJ$_Q@B6FF4K5M z$TlxjnU)g!gdse(?3|IMfIVO)%CaR+AD}PiASn4Vq$^pA9*$N6bX`dwFT9j?k8W`` z3ePUfzw5d5yniGp7@QQI!k+CvBVETpU*i|EE2Xu|>Eo>%4U?cb=$^$j`=<(^UpG9Y z{6>4v+R1kB-7e`^c}FVy*n%yG*~UhhM7B=S?oBJ@0N|{>t~1?MQe3k;cb$ zi^NN6p!jd0?)zPtRgUbU^jy67;R(O$&pXU`*RZSX@i&(**>17n+W&VCM&8InWAcmS zSgTuk>l2f3V^GA_bQP!gzU)Z^GFzy zdWd3KG4Utl*k0Fu~UyV8w!AfOuriTiB3` zlix6*VIS9)!Tp_>+8UXiQXSyJQ;s~|%OTL;BkdI@6=wqwciObe|F;o7Gwh%W?CqD5 zS|W=`NiMdT1nJ9(q=alL1AIciIG>DS31E~Bc|zAZlQc1~KR2UIr#m2Zno(=K*~7Y_ z>27R?%$)*qhGnyOmN1@{`^U`nH9ka*mkr}z7fTrSzWhFUe8rufb>F2bHQ#Zm8M~{~ zG3fF>I!mxRdyr9C+I5}i^-IrPD$F#0_k)wMkOotSYU zSSseq-mS8jBJ~Mv$4Y>X7L0WeV!`lwau1OAs#_2@Sws2~r`>)j(O+=IHJ2=Aa=W#~ z&5nlv{1@Z(bsHHI&L1Wk850lS4;$?-e#G#DnnzCYa^oQa;gjZq#Tk`leM1}NiNeLW-RUMD|69iBzqYvkO_LYP!RiCr6-1!=cxvNWGnx_= z4l=NnIb$4HA?pADCn@)%nm_mz>A3!MtsIT!W5+)KIRBXBefQ%pKwXmO^V^&$U!Eh~ zwYk&dH>YIppZ@|lz9#tvn1H)B|0jd>VB5VQDO`QkA2a5Yl@@nN)=f84^2@GhykiB;V^vSH9;dkpXJuo_6KPJz+Cz8AeDo#($p0c@+# za-)0Nb}Y9g?=r$Q*W#Zo>o9IUXsiwb^W&Gzt*i4XQoe*Lkg)*Lln{9EtwNzH`fJcZ z)2+2In&QQjVan_8$;lmz<*z69c5WXoe%u!wMlaAG_sDs=@~5|G9#8#h>$}x|Pu%ji zOoqSlH90;k7mruBDHqiin{X5@Y{`9*mE*JRH7dcWLd>R*)sJ_H$gmG2V+LTMueIQ= zw8N!8%h8F)CO`X_w{&j@KWuS}|Jk|kkKZN#(&%4*^B>!k*1dEdPRZ*Z?5Mg?cZ~c6 z_~uxV;V_>#FK)n#nsE1#^6V76PLK54N_jNtmE|I#txq}VjS>CfemjYvnk zgPaY^TxVC26jP3Z$6L8Nii-6*3VpmjuT+dGhzr7WHEjc5%XO^F1hn6O{Cl|n9y2Y!k0U5*WlqnAU6MA-3UcvX0f^BEOqg3|;@3}De=kn+?)*Z}eu&1>aBa~V85kByp0d9rt!>7RU2g3mW0D?$ks8uxzQT| zfQJTtviSP<8}L827g|4~sd~pycb>BvraZigAN@l;``}z_BPLyCqjW2AyiA8gV9kLg zo?f70#ZDEqb!9v}%63>nCyO-kGA3NtPUwwrQ0x!W{WrVwZ!X1TM)vP-*`Yobx_(@p z#i@wf32`Y$2VQo!z8Jg?HgHuQAjowz@Gf*3^G_8sbF4Bn4L78SX6+Fa1Q23NYS$$6 za$iP4{&>QNV>FjKHeOixGopGn>YR@py^YbzUWhXbQyQ-_%<%a0Rq}(_=e(=}rj6Qe z2jcE+Gp=o3__>qj72Z@Y4cl?`7J!YwSpeqP-;t&L$$$8FU;d6d>`wy3f63DJ2k+v) zWNEAUgLm;?v$VC#)`euXNt!tYFi@mhlsZ%f56vI-8ft%E=y z$@Qz$Hoj-tQbvdFfuA>qmkVx`#Ohje~9dvynVZ(oK-DASn<~niGS_I zwYgpB&0jzKZ*;qJP+Nd%K73y|R9!x4q(Ss_M{*csM{AcvMn?7nqADsg*v9pI$xh`L z;Pm$WFMy6RdJX>zF({pJ{7teyI!rBq#tJ#DBt}zrI|ld!njr}V^4~C`e*s4HpP(C>e{krJMrzHV)V+0${r4Jj zFc-go&OG|r?u2F?t&v1GQuy575{)r2jPv|w1!h+DzsaQvM9zqE8S$N8F0cpW852w! z72GG|X@m}Xfxr zVWeT0jk8@+ldv2tqbbQGb8kk|!JH~LbdfwR0x}&+H~0R8w4aWE#!9`b$OL_W zl35y3+NRn`LAKPS!U)(cV^|&~DX4C)g?t$0(RgIL7!s+M)_V}#P!lmv8CR4pmX+%A zeD;AHwHwJgw(h(W(^g_cR@Y@H>>{(T#hx&-b#Kv73sN7J4#DxNHnd}M&1phTq946h zpqy`ymQ%IDY&~C(g9G#hSL~3g8=~{{YSwle#X0k70iy=ob!GQys81o`@#6g_Hts38 zSaJ{JO+rH`9$&FFG;f{%0t_5JNaUhzId_w7%w4m;G^>gBQ$ax53Xsc9cA+hY z*3NOorABVYgR~Ee;wPqC_vI^3R?-&|R2e>;E%!8g$L1N=Ex0U`-mM%mIak)KmU0d% zHV|o?#m8d}qQ)hNwf0-wJ%1c1^lf8us}GHww-7ay{^s}eIC$30lW0^m;!I_@WGV#w z*l51cc>kuq*7&+eL|yPZefI^KM)YLz4~#WctE*?qJk;EksY%ixC0( z6SpDw_$oqYB2w@)(5!xaeo#X+(zQ04+b4G+^($16oboX+erQO=w46QLL@emLKOO8L-8J)43d_qB5RYZLWVCJy+@rH8^l zV*&|3!#yGt$bbTm0he3)Op`%k=Xg=1wZ<;BXwWOEepZ)M19Yw}cLBsqsnPj$GA1Jj z0U)1p8FA^5sBg)BRZe`4QE9lSJ2S;<4B0Ki7D=@M2sr@+i8j_@06Y>noLKY23d^(~ zT7b?i7<<15)zzfUQzBFR0@8XW4v38GVq6%Nj#@N8^k|PtXmGn|zxdThdi#Wl{SNcN zdgwZbaes>rmLA+T6i$Z#9Lh=Kq3uz3Uwz-7W)HXNuZ4xizPyzczrs%Zjy0NY^lrga zgJ96G>PZWDG;?(zAkg2pp{b!3mVzX&NXIX4`sTpsXTBf&?4R#nLH{Am_{WUW_cFRf zOg?BaFA@jmJ=^mSE`7T9-SebteAizp`*~q+eNot{t)v|9;J`XZ~uAEI(Y{=x%sUYmN;FCm1#Pcji~%yAQ!owy0I|>!Uq96q9ho_P5Z2Vv@E?LHllNOP2dgWDrf&4c z4-&GN$_?Z1HDV~?Sw)0)7R)k=OUYU>4!^rRm-ODGKWbb%-O10*_scmq3B)vA0u984 zdGj_{G430%Kh39vc`Onmq^S~MNj+NR6tpFn0KCUL%hHx-uME!(GtrO${E3#moh5}v z<5HvZ$ZfABr5yOZDK4#5c2hi0#Xe6fIvJnq@nGt;zfF_&Prhm9Zi2k}QQ4utY@|WI zFD|)k)g>KWJB<+w11-{=33hv_x}VJkEKsu#!m8#Oay@GaDA8aIhd2Q8i~`{==dn8L zOcQpTJJhiuWdynJpX7x5mQ-8vKw3*!$Bl$?F@XV#)EFeJ;AhPW*nRgaZvW6AJ^!IW z@bCZDU3P1ZHMcB=HznT3W_LuZ>hIpZt{ESY4}WckoHZ+m&@t+Ah;CKzCphBleKetE z0U`iTt|paP<)_MxWlfX_;|An-6pM~_jt044Iji_x@Q$->bB1V{xIb4711BVK z21!_nIW|E6Awx#biCoQRWJYFSg`fazB(%Qr&SkNM5Cvua8o3OV)$HD=0tK2P1AnL) z{iN$l;8Znw@Ko#DXu9Oei}}E)R#p85&O0qqxrHVM_S;ZoP>fifnS=VW1t**q&oLa+ zod23zCK%LIB%VaWg_+j(I&W)0AjS6-V!RE=^`i~}U zdt*afuIi7|gYV;CHz~4q>gN9Ven1v^xnasZ4GcLvrufiKrOmrdpg%V0qNLvs?@j4P zjT@)inMj0(%LF~%No0M=?h5`A46E3^8Q<{%l``LCJ3BGRpx|{&LN_UZR3J3=z0uX> zx%T0M=(XcmG%m&;`bp!T54*qCO(z4)78I>98eD68t3|HXd0K)ix7YNyogGe^|Dx4 zkP4UO;d~WRZ`(cQI+S4Uf3Ww~adB_io@n8O5VUX!PNBiwDO`fP1$VdL7ThV^9SU~~ z0TNt;J0!R#cm$GH=bXMBy62p^{pNG0-@N%;{wv|PcP-y*uk~GG0Du9)@oRAL2O@@| z{1-nH<>|eLpaA?edi~*du{hetP=R`w_xQhUCx;Hkrvb3SKCw>NsoMKcQq1(5HE3MmQake z`|+2b)&z1|pM8GqdG+#1)h~MKe(Tvh5-?Ovd}FlxmK6H2N^BM+bE|(#_XrYn~$&gscE;Us^GD=Fh@Xsz3T|w5{e+cSVxm;jTMt~CFt>14G?GXkhc&F| zGLfg0!>ZqXz4s#&0wkV*+SV@Ov8m!i_pe?EhfCBLtYO1+&Ap`vblx0R48I002(P(w#atOW)hdDPf0&N zpc(#D(-ZAWAFJ!gy9P_I)zhuXb3O@o`J9-{mod`33f(hj>l+-9qE&~xIDzjz!1Je*^tk5@w2#HE z{o#$<9@s?2M&_0PNe#P|G;aAK%iy4l5M*OiQdDNlH5r!q{k3~?awYilEuqe?Gg43P z>LS4UKbnIVsc;&0h zB)N#1bXnA387j@x?S9%9=@uf}<|qlR{6bgQUqTTH3}^cS1TzgerA|zFd{5i*_RVD) zu>Z|n@BdPahlWIRuS?2C=m|q!VRw3_9^7#$VQ3n6C2yJIPK-OO0W{Ikg9P;0&loRS zd>=MH=bbQqoM)lBX_m4M;kugXdCBT>B`aDtbJ&cprI%;BHXJNmn_D1Nw0c7P`0r#qI#+g?AezZ4 z%@dQ+uEnq;IH=pzL6cSHR^{@6SUiYPrwl!pja%_$-(!A%50q0$~g2C+er1W`e& zsHj$)3C03WrfXL3)&+hi$L#}sda&~7oRhUMd%79b4f}lD$kr;aPZQ0N>)U&+s>)fp zx9H#7yj@{kT;~;Kt}uudSlV_cKFf}bL|dQrTdW=YDal=}ITMFVWQEYUI3mP*oeE0J zj;{V&$2_Kj6MDMen^JB121m4Wx4)Te^*(Bei}W*2fn0G1clln{B{;e3+7`NwLL4-Txk4Y3q02GL|=Y9KGv3P~*V zkf;zwSE>R@7?Nb$GxT^tzf!k#kzJU)<<Kb-$v=VShy3E~Sas|qZ$;z*CoHIAX7 z5lUKa#HUp}fNdQ96$aWs;qPbsK3`Z9Bw+8(Z#r)`H20FdbN>EPtLMl;QZZYq zqm?fOE<12%c!&B$o?M6PegSkV6U#Cg8b3nO(C>W$5MPsItXph7D7#_l@A55L^azJp zhVOr&got!?HtTVWTkjVrobiWPmj?(sYb6;!R%&oUl-042@Nf||5%5$ff+K0Mc0c9| zODs6>%?zxxbSYO)dqpyzM7G4cf0f{oNzfcEvCj%gM%*(}03j)Mh3}!~@%z_0`qr(r zVGpvp*Wv#r12}=dyz)aagdeNxlv7Q2c*O5SN#MHNHOsi19if!f>g+=GxWhTxQVJ?h zh=^J=M5$DNLftCcF~2Tk-FTW-@8|1_=Alj@9h0jrpUX9myhABVr*59g7|sunRf8Kh zgOhb(J|Fu8i`*mrBN=%1Aas0?3{3s<#Cvv3DN1j`FKwt6E6EF3+X#rqbU<1KzT$^*A!PS)1X_Gc*2C`<`5-wk;13RD z3jnrCEqSXPtq?jq5@pz1l1M-h>izX6AzQ5EExFRtCFpT>q>qc z=^Ub>E5(kiHWge&5Ay3>;V+oMe>Ov`pvCpl-qmfoEGeF=9dANQ zm)&|82psS-Vd-(f!hnZo74Y@zp^vvwTI2Oxr*|MwGRLQc!AXuIQw~$M6BfZBd=c7F z%>_>{_&T~{oV8=J=RJPU$%_6G!GAR{6n=?N+Ov;b_`{Xm!&V!3@gZl7xSrp0qHMZ1#);b2YtAYs-zY36G(B5yw;%s7=<`65$l&F3V z4*?esmv7OsA;l>v7L`^q*r25W6`YJnXAcLWt@bPdZD6hap@`3Uq%U2`!o+ysuzpwM zhpFS^2ge=gkpheHoWQXxf+k}uT*)2qlLj*e-VS?XoNeRhOp* zTHS{lbJHKpP8`ucpnV=@u2%2Y6F$qnO|(5YgvWR#LbrpOvjtGcSOx5K`)XLHb)aTI zul55xxRFy+i)>o-e**fT=ZCBXQ2^}yvG

LejKVm?;e7+?MM_I}TgZT|Q@L3Z`$ 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 literal 0 HcmV?d00001 diff --git a/docs/multitenant/lrest-based/images/UsecaseSchema.jpg b/docs/multitenant/images/UsecaseSchema.jpg similarity index 100% rename from docs/multitenant/lrest-based/images/UsecaseSchema.jpg rename to docs/multitenant/images/UsecaseSchema.jpg diff --git a/docs/multitenant/images/plsqlmap.jpg b/docs/multitenant/images/plsqlmap.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cef79daaf49fabd4e7cd1dda7287ad74ea482606 GIT binary patch literal 94000 zcmeFZ1yq~cwl5wirATRuJ1wrE#T|Ab1QM(XPH=ZhfZ|Yr;uhSCyStROxH}Z5xE3!` zDE#T(cX#h|?>?{pd+r|Zy)oWd8Tm4o&G}nv%3ArdzFbXQeFEHrf+1i48X5qAcKrfe zEuu|8q@?toz?HxddFkIOIsw-S;}HO0W9x{7%Sb-Z(A0Wx>(g&NzSkKTIXwOT`A^a{ z-{bM`+yQ_Q;6G{ezbeKyHgPbzZm@U#WZLFQX%le+aBff27tEznciFN&^0yqHR z02zSf_wirnuajLm03i4U0JstUN10(f08rx#0FX`nQO58F0J!S|08|hBQTE52JT*WX z{K^jF`igF93IH7B004NJ007}20Dyz|weI@zPq00>uA;ieWq18D16Tu001p5VfGxlX zzVudm9J$4lXt} z9zNckyZ7$l-^000KuB^b`!v8)$R@ zbbPcM_-I$H0P5@MzJc~@`hB=JF>hgE+(5_vUTt^}0Ki1M#*K}IhmCpT78*7H4gChj zbrXC7LM$R$ViHm^IxdZ&;m0sFb*IRv7M?{Wb(sA?hORA`*l@0G1A{`uGL{)s? zkt%OqxJA#GzHdYddUZ7gxP9YV82$}>fCS(&FE~~x!Xjo9eGgTatOG;n1|VU0bIPb> zdmW!FQi})vK^61!U-`#hLzs8RJP$xhR{$oV&`s0Rdk@%7O8otold|JD9b?ql(iI-c zk+h8G4RH;46j`Y%N0)w=l zUXc#tG(RZLUnpOx?c=SJtJo4m&EIsC2q}37soT2ygCeC(+H7)&8PZvppg|9e**#=E z(6F_sbT$UPqTn@YI_x&dp+s~aAS9nC(pUBMfglqHHjmZnjKe7hB}eG_n0+#HUe@1l z|2UsH^TqE66`Tm;tARho?WHH|b4X?%*@#tDAF;Nv({Pi=e)u>VJGZZmK9Ou%U@EFc z0Y^Ds=`ObY(}d8QbCGqy1=h9=^rH%#!`xNPpO$T7%+NlnGAR!m^Hy?o7~CFyDTVbE zl@9Mr;sm~K-LWlENnw18Syf1v-h-_$|3T;OlGX08ty{|vs@*Bi^8xbq4b`J7z|A1c z?BN6luH@d&sOgX>B1O+f1={)nZ3G0{ExH$`ZxbJHyPrwTTuPU)el$JWC>%j!6Ud+K zgj=>UB~`Y9TDf5g{TnE}xtmvjcPl8DATPC!W{urv#Zlw9ZrDBPt1_(4Ka}&Un#Qjc z)>Ia~9#gMpY5F8it^Q+X)`KG8qKNO~Vog2TxN>9e$;7}47^+rFs+%XMEVI`i1%?76 z;XJZ#b~$z8X&PYdxQRB=9=~|WT9r-u@*D*WuZ4Je!;5t4nJ?NO^%p6{4H1fg%gxQ2 z_?Op8rB-_O63ie!>U-qdl7^wFUAMeX@+w;Y#J{S`b9_20o$x=WQ6iE#iJXm$Snc zE4UTfTTF;T@Jb9pbn15}vwH_=_|4sZrN}r!VHKM(RJBTl*y)Vtlm4b_5n-iZRWA zWZmMcd=tRn-VsrkG8_NU!O0EbZ}<468a_!cAMdYD{-5NN+Xh&v<*wY_i1i^C3<+{e z^)F#IND%INq?gNEK|Ugvxt)h`E;H^Tl+`_Hj9L(X$ef1xaL232uuH$@O<(2203;(S z`;JCrI$r_4B7(6kQqg&r-weVvr7|PMdIKQz(^r7n zZYPNy^usFvoA;%xwmDBDZ9dy-a&PWnCCG#gTBu16LE$2~7q_{*-WUtx8Bv7bzV&t^33B=Z{1DniV6$ z;k7xI#mz1lcmJsPgQt@!6rl`9tlWS0zb z%ww)2_kJ9laNO`gGr@3Z@%<&cYU6sY*K>fe<)5~w{~huMW5mfu(-|G}6(Evgu)_gT zxZsw#BkkGVrZ8p&n~IZ#C)ym3x~G{+PnhatdT-SLXAvkO3Q@DdxXvPVU}xY*e#;t$ zF#?aA5@qM1l-B+tP2^P2j^qS-$$|vnR2BEUWAiTd>8<8my*Y^|2hOI&N3X4FEVhly zPH_jb-rUq{_CnjsCp`%pr6JtA>-*$gqt~0+goa`A;@J$wu=l#YK=hEjdV7O}t`2Sv zGX~37Os#7|!o?1IZrcvb8i>2x=NL<-OI9{5+j9OeRxB7x^~ACV^QZebY2Gg19{+$q z5Iv?nmH@xznh7M}&WsKk%wnAj+jy6VcnISmJwSnlB8-mvy8IQQ7ONxiRuVnf#fzsI z@5W4Hb<_bfh6xOjAArIA}sF;o6ZlWT)ydvS0-h+Mt))uEz2*z*L&yv@(?R50D! zBsL11nGodXMKE~rUty5%e7?c|q|fv-SEu0~X_|Zv+j^vOu0_Ai;r84j7!uzRL``V1 zI#A%N3p1j{uYNgG#rGf)r38V9TTLoac(m1RJUzNJFJDC89Yw6l6 zRUc&H@28@XSDW-q950^R_Rs72T-;btdANH&SM+xvk|b~60E1*r8=m>3Nz1`0j#!_J z;Bk%F@UJ|fhjWSCh82jAf0N&51!^QPLfs%W?a?e3_eCyO1g~r{J&aOTFlO% z7;Qq|6j+M%`CA-gA~8IBoUfaWR`g{hU1j-6bG8~|d{y*F`h5Cm#gAwp0q5w}nb!ENgBLSa4Lf=60H}c}5C| z5Lq!3*Pu6M)SfE67bljy-2eLOJD}|ErjD!kL=~ony0!y)TK@nJts_>sK1U<2E*n_X zh$XSA!j}}@U?T>5mxIifQUf+bfc@(`aCP!l!HY<{fRFwGON@lCL1`I=g3IzEgjucn z;#)(xTN!HQThk6C(?G_`JOn<;DuiClKggbPK?-oH{HqheJH@%xp-7I34g<@6Jjy zJ6Q)vOY}+Uboch+S#JeJ?Xyj%%z#5(Kf|MPm%>`BSVq{AQ)ubNK7R_seeC!W6MM@! zqDOaqAANW1y|m{ZtiIotzT8GNy-Am2y*9+SB22pJAYI=FygZVR6tJ6F?Ssu|(kzGZ z@kXIIAbeH?>dz5Mqvk_p!r%v5Me z@l99uRm%23=ttmbIyKcL^S!S3%Jui`uDiZ6(kWren+A&USwyy-k<>NG_6h|~MG$&eYV;Zp4?cOZRoL?B zMG}G}L2bNxhAX2Z*q7ymk7N_}xg0KC7aup1zsPpeSe6~e3Y)OWfVl<_qkva{bG#}h zIZd^Q^a@ORO1uCOD*sv~O~uiq%>1N0(REhhWCP{*mN0s8!lVLGQlb1@e^gR5Q)E(3 zx5*{zEL9Uru9uD1pi7P;@-#i|tm<4!3+>}Fks@n-UTIoW_Z46y!>QynIJQkBBesqB zZePjNU80>(Jt7qe{&(h9zmVS~47AO+rra+1CM=+J1@Q0kP_7s9fIYdG`u1jDaeZZ9 zTI$g1_;n>(I7~2mGyB88S^j%2|7}G7_MZO@VS{*xCnFGLAx4)z(PP`n5q1U8rA^)B z^i6p=3K`o89Vg*s?;ud)KONAq-<tWok+%cJMhQLrk*=_DHa$9if z^0w$pgsrlI?`SRY>cfb_m$76XUUIm|iyYtkP=Y4DsRZ4jDp7J1D~fwy_R zAKAR#12C6*|2zBlA8pSXTe!893CN|23ajBHD7gH)%wSJ|WI7u}lA}mcYl_jX>@46a=2}EDmN_STo>j=SNxSt?C}p)*!OP7+(Hw+ zXQbDxi~4H}#-)PTgRdwq363IWcZ}x!3z`DMp8Q}X3A(}f;=Q5|*~h)u%+Os2nhR;2 zBNew-KZZl_fSltFy5ocPcRp_!oPQvv{=q5&(53&BFb$LMf^mbs;dkV3ez5ZK$CSJA zJQ?f%l1WI>h;|Am%KA_oL=6Q-*>FdZ$2Pj(`vSl#G&0D>h0E&a7i;DM&?0{5{cj@B zZlroP8+$Xzf9v+ydoj?oz%7Ad@>-G7)>~+Zmvg*J;rk_VSJH1tWDoh?3nKG5*Gh_U zJ^55fUxFdyc$6Z0Ox9NpGdf`8@rq}Lw8GFOi`}i#%-5_eZ;S22V0)^zMfL2H9dddXk7Jh$Vm7r$Xmmu z%Th1B-Ra2xvUHYBJbLWehsOGOka*KAbGtW>9{z!ghFdFky)4<2PNGl%8kXgxQ)>5b zfdB8z;L2S5!Skg2 z^oLcIX^y^C>HQ*gqv5?>J^PxD;woK6!r4%ib={iHXv`JB+83p!H;f|MII49F6p6>~ zw<^{qHCykkiw$2tN9D3s@!6wCE4{*D!0!62F-T&AAXff1fZar>%1|pC6Q%g_Y_4F_ zA-eXkf-?WT;|@A~UR=mtof(&hc4gznP6kwVyaI~HI5Ei*-LzEWN8{+-Q<022B78wW z3Z|OU&SXUvGBI|pZ^2`{;Kz&GbaYVMuenO~ymsW&zdIzKD(`EUd9bmWkPf^1N6oH8 zZpM_6!7IQMHcM4cy)Le%->!^*rTwxeBISwp!liTbcSrnFr{5a>gOC3e$7>(Is4t!nh()aXeSH5J z?+Eke(hqSh?J~cI5C6aN@gGB_;THbg#}zkVXV*TS{Qnamy{l7`zo*BY!TDeGb^qWZ zYV7oToVgBbvHv*;{iyuY*k{=!I18%rekRxOuY%BzVfqD!{vKKhf15$(p8^c|Eu8** zB+0`W2iE~wR~G^zLG3h;T2XPa{6W{xvUf$bSp=sL($ojoY%RoqMUJLe?lcGc(irqN?D z!&j{NG_#{rPST0k#n%dOQV8jXr~EtE(%E%8GKcV0MLL9`{&}g8-Rt8s`~T{i{MY@~ zbp5^E<-GeFQWtMKKKXO*Nyt)oUFx?SRdrV(rE{o6Jy*?hKoj!KU-S9fO3|Vnb1rBq zN>H)q5Q>-^C)lAL5qD2m$?c%tAGU`n zUi+j+pCaD_9#Z|iAgQ*7?(SOHoqcq;cpMt|=UgCuBa)Qo3Q)P3zst;e1+b7g0rT$x zNc&a(w)yxcjJS)Vt^lvboetpFQt9FEy%x9zP_&mT^uP_JQ!z6=#)ro!K8{;Egj8pA z_jlkdH66)cD0l70+7Hz3&e2^a5x4+uE=Bzl=Kospjbu04Z=bL4s0r_Q`*RIR{X{`*dGOnLm4IWLb|TT~|YhR@P4jiV)K z%`GzH&&I3p8EV95tEs--$njvEa`29A9ue+ww|01#)5h>iT6e}obJR1Y0oL`3!7l!d zoCFJA3OJ~eLc!#?t(poe0&Xoy47-zrHlTSI;;}j+<@Aanb40MRE+x9b4E+FY8IL8M zzm*WBoDNYDKzM3bT?-w@W=iKRDQzt2S1#5RPt{qs^^Vlddn?wrA0NC7lno*~;}0le z#~Q|4=JmBN;^?*>PQmOw=6>uRpG84;?inEQ%)rM_>iObV&SW9X_>DQb_JWVYbF1na z4)r0H&uYT$k1EvZ{44 zgX;J>-%HiB&Wbci?L;b+n8%rHc(=~hlQi)uC<1G7K^?ebyP<~*ovv1*8M{3!Bgc^l z<<5^`Ti<&08i}(kQ)a5-3S|4RilZDL7}D6Y$ut@*Fkqh!B{0?oTap23=orR0Q$NWM zegrrW(In?N-w%qLG;b+2=T0(W=qk;F*4x-l(h z+F`I){uF}4C82#ghl-c{XA0qLu785M&KHg ztqPf})hE#ePm5?{jVSiu6Lh;S~;-hi< zW_2SP)YbQCbZ{8dj8FX2B1KaeoW<5OonSGUPg5zhT`RecyC#($oV$aQZmR2NBO%Yp zzTk-i9r($j#F!YqB7cGW(r=9BNr0AZL0(OIvcVf}Rep<`ZuV zmZ@$BgN+}FnXe-c%H%*Bb8@l9HRP`~gEW`LhHCw%qWlRGnREEdr9^8WKHmx~86s#I zrY*SXDEy$kTdX7Ep>@aZK9kW-pBY6^Z=n1X-lz=<+35G1D!#4*We{!}G4YL?o`qUU zF`K$E`d`$X#*~Whh;f=MfOe+hCJC1FSc7*}BvllV0T6q|J^Ta&)W5Bkojh2k%sRDt zV1KC7uv`@tK#gB^i+7D>B(A$|Y2Qc72#euD^EFnAc+VCF4`xNdgs1 zhWMNpdMh&WJsc_gv{E7F`75TAj43h*@u*!2DL!9l6Lu&QtfFGoG`lNv??H^^B=j-K zk|+g~V|Zn`KXO83M0)g2hzpGaO5J2^Qc=zi@k=I$4ctDjw_PPLv#uAUdZ^{#6P^J} zv)L0&;d@$GL!#Bk*lOvd)2AO`X6$^&u+08W@pfh%SsE8@$KX3wriDf;Cy=G`YcNPB zamOsd(RNHsz2A-B0@G83%#OdYhsLBZ#=bj9bCfnuDKhfR!a95xt$NrMn8l{fgFIKB zPZPTB%^kKAK16INrD_z*I~awUGCK1E_ezQMFRZ%qd&evIqVLd1-=}gLinW|94SH`~ zJ1IDv$&FIBfK`rkT94Hw-1R>>_+@?Q&Yg+^?WB(ip!jFoAT<#JsWE2ksn#xSpAT?C zjMy$_Dm@F1q={p`xI;sSDg?YDsfIpdl*GcKjHfE|d^On%?U z9?evazDiW^NX55h2wnd`RLwD6a}8qfl?=N9G?ydx`p96QNTFL`wL%7`O#8xAnrz$? ztf@t42peyf5@jO;NP2SO;e@Hr0-3nIa;nOVR)c}UtLFC_mY~-v-35z{w$+;fKvc*f zY&(k}fYLQw*IsR8E?fxgJU3LA<>X|NI6S4XG}HS+kz%=7*@EBXY1{b?TL(eZrcg&S z*wQ+>9AqbhTO6*LBH@%r-WzDqS+Nsnz7c`!)-OHhp|PkAdzn^u`%OUlGt^_!nzVp!_g!PKb1!XRCEX3+Jc8?a-i;$@R{7O@=Y2;DR#I@RQ+^O^`iW+G4!#m z%qn@x!@`r&BmM^dx(=94Y7Y_)@5|+ zsWn-#TfUj}@tXwM2_7Kkn{LPA1~8k$Oc+ty8x>;82n+WV%UC=hZ6^QXinPnH1yqD+ z9xXl(5r1rBms60apw(v*s$>-2sUI9`V(8x)LGQpruL-uumNVprJmpVmjX6(Q^*GN- zrsX4f2OWyG#GulgPNYid=WbZ;q%$hb*IAT$uDS3%K4Ucf0k_e zJwQ(gUXo8?7PiHds%PP)%I^V9LQyQpdExroO5Ss)S%(i+MjVqSGwBVo=dRC&jM4L2 zVT~5H+%pqSq-k$UaQ=jxL``G#^gjx~*MSn#vOaD!m1)gn7ye2n}(_Is0vuikz2RL6tE_LBIWg1C*wm10o6nvYqt(nQaf92ki~eM z9>|&=ucK5{lipD-yp}O z&3?DND*vmOv~}C)Qe^-{_M(Shztpx!?wRD$``~Htl7|N{uPsZIg-)H|buU(T9`jUw z-e6>kEu1L$Zq`rAE?9zG;ZTqp7StBEtq z-BTIMnF0>^*yN*8B%6uQ6gbK!o?*ln-rAYEu@#@MSgQ5jmy)n!K1{`0{UEztJ0Nx2 z$4Y-@CGvHaJ%9Ss(T#}0PL#A}%U7i}QZcsuUMP({_dHH_xpw0H&4Z0C{g$wq^=^bk zV5WH8j$o|TCqzjWUR^Jz7GZS@m5M5aZ_fH#WQ_FzlerTod-|#E0}Y7Uv}VH`5$m9p z>uzPS6|z?7^$8AcfOHQh4GjTs0Leukwz1+xpEGy2Otfh(tCCZ$P>a`|I<9#hRWoj} z#$Bz{Z4cpOiqaN8JN6G&J?l3+YvJaDVm_wM+3FS->~&dpC#MgLRJH?FQ5I9ZIu~}$ z1Vdm1&PwLBfhF!amivG3QLW(@G9@pRNyJo@u?&frC_|_-Jqv7TF`_cx zz47;d_x0`sU|9Bl5+DDuuZhlIMrttPFXKAQE=*)oU~Z`(WV!Huqx3gHhCKjo$MqW~St3vS<9g`jz_|yM-IGWxZvAYgy`0 z73Z&wK`uq2K1EDmQ1*Cb>JdwOr-MU;e|bEPcA7- z`k9l%P}eRVQZB9DAgs!6Bzi$mzE1NCZZ8YEyxe`okf0leHq8N(3Xg1SH|Sqzw{c9G zN6GTgrjVqYo~e=h0o}q-pj2hducEDw-=h4K={Pmjv0p}&l+_*vy#5+_>_`w3TRWOO zD%oScPi;%$bdp9WRhlvcB#Y3&NdZSJ*uF0YWv8|2U`X?j6S&kjBOcc@zjO}KHU?>X z$h0-B@)So+S>-60hnhw3JzwJ4?iaP7+1&*UM-mAQfC;1>YQF`UHkU@Wti!m988 zf|os&TQy3Mm+PnQO?bbmet?5jZ6(S5oGee=h-;Z`=-8<2miaD4ZI`9^Ej%di<~N|3 zxyq&r8p=P+=KNOC6G?Zg1Uexz z!NSYTGi7a6c|={bxx2Z#kd&m#9sjrh>8;QbkKkpp?9nM`kq z%Hs~A)_Q#bh1YyH*rkgNYZ#u)J>le+3GQ~2F3;>Sl`^Y{nC8nft&u&QjB1uWc}&){ zvr*8|7C4&KDo?DbRWEPgK~XpO^GJ%Kd3AQYK~O zI~fnT0>vD-nJt**w_ma31n5QA?>we)G=1iZKVf&@JcAG^LbR#&5of?L!vrYZGFi7~ zF*LJC3U1uhKCt#CY1>*g7xgf`X+jk(zJh{x$?gQVArqmxynC4_ZBtMde(o`_YnB>W zw-Ht+3gjk>^B@v7?Z}{ctvp!5Ry|6ULd-6o4c*RM*%2V-D6l2P8)FHFi1nD*)mUI< zuKR%*4RJ<^w!{J!yTqbhG}kT%=oc3zmK}t=Z6=TBygq*ezN5}Z2t5z z@YdaIu4;rUkTOsm8{cLHH>Js7YeTB!%cO0Pan-To)`$Fr@}!Y-9`^fQ?Tcc8Pt1$#XZeo_n{YlCN9IM~S5J%Kvffq^$Du^>5@uM184XR3O*@Ii^5*Ox_XzQ*Hds6c z%K2-%cS2|(utX0KC|!apj9!>;$Plc%nkqAX7P|K$s?mVUqIF@-ZzNx+<1*Z_#BCWwj)yh@c zOfSg{a)p=U;Kr(I}0|xzzYpi*uRa9$n?;`tHZ|V>Dr)Sv^c)71dBHqNDS2oISFhjzrc=PH3u5pFv$^tA$#c)INSJsR#o6UEU!%XuO1gWs<|eUBe6$oqZ;# ztz}?Pba@)JksrtG^yfe&iRY0`b4twKTj(DQnfEEZakl4dPTY-|>am~P!%s8odSyD( z8}ni^G`GKOe0@#|7CBueI@tqj^0FN*D(RFuWV-W)t@v$jJiq8zkQ{w!_zK~cp`_Sq z0;`2G^Xg~iP_M@^N_UX$H4?)^LZ?E-BO+NAggQ5UFyKFdbC<(h^FV*HhKKRZGoyT- zLZ^Cx@KNLAZ6$u>s9kDe6}#2Jy|xEsaF^BwUoUU|?XB&E3HgtY#nIpX9MQIlbwLwD zmqH&5hOAbhFMPU^*x*?PS=%FUp6*)S{id(M0lK8OThK}DYudH;`S_UaZ!!$-u7!S$ z@wKm7%o4Zwrpz3R=&at9J`el}9D|h*Tyc3y{#E13JH1X|r_dVk0Yz}iY`|*2R>sL?)_B&sd^S$CZXc&mstRYj zYF5!pbj5R5(d6r!vA8$=Q(J8=*ehIXWpTf_Gz_2ekVFp!I9)1KJ;gRM9Y3|mP9YxAWH;0#Ag@N6(!|X20S_#r#MGH z#dNC-R$hD>$tb`lE!{o&B($t0#`g1Z8*}P=$h;kM$E{N=#ZJNkDJjE9I zynNt%S;|2|=Z#;pSKPNc?_4cu^(Gn(je^7@j^C!Y%bYrHGkA;IHe{KPT&#hxFR6-h zZS^yB>cU5YTYnQhe`_ir(x^|PvRnlF4otk7fqu>+Jtmsl%XUm~ZJFG7Z;ivs;Tw=J z^;N=Wo_e(izCn#&q<;qIp#7&-c^i&P%p5FNtG;$<+uzhx61;gp|I<+J zw<=Hli>i)0jN96^fFwr7fVP`?yw%6Oj~70it<*P_Lv`_rBZX=u#MV12-C@r`C)*i1 z%v6tyg_ZR)JTw|Vx5~iBhMD^T&NvsBHj^JG`AXx9UwYhh`#GeE+(~s02r4llJ$=PG zS4Lx@N8s7HVdTIkhTWRTuKBO;OEm&6!1K)w0*Rh;li0;CRWLAq9?LJfUghExlvpw; zgz*^;cWyHXfxb?Jhq-14YN>RcHK;75hMWB7RTcE{u>l&?}-zJ&kusk-T*UBfgj-yrn)^PdOv`|&?r&Tp5y zIK=SPUB(0qeCT@?Kg%wgu+HlF*Y_m2hO_xqKiLWWs?E=_*7?3m=_-fPHJzl;YY_aDRfN(qIV4*S|@0e8h+ZbD@M^<;MydNW=Xm%?bPB2$d z*?^>9;^3Q^1JGVP_?vFJ{C3Ah+v}P3f8&k|;0DLW{a{G%BP}-))|JW1W0PP`W0yXl z+31iRF`TGe#)3a4z_iLW03(Bkj-D%$A{5CdnGknJzU$Fs6mdKWQF~P6#H;$O-T>-t zTc>CpPuHQ+6AcAz9?5JOl{qVqo5w_r8@0vb=RD0Lq9e>Lk2ZUQ6p_r?i-UG=vU1Fq z%i|PDG}US|Kins0WCX-^^9q(T!8aH!892Io%=;Xpbq+ZlsOaCmwjbsXTw)r!-EzNU zD>cT&U_jF zNBr7yGT{?qeAU&~=!gg5Lr-g`!~vRX!B&cKLwKDSTS8Q#{jj|< zkljKK($EjVe~4T-GK#K~_lgkQu}tPkgbehSe42kpN>Yx5wb;koG$VBiLRsrzItW;@ zB!yiB<+EWcYZR!&;!B9u7xns=T}4e)x?Vgh3bGx0v6H1oOU^>=jQOsdxr6KC#T{x| z)yoJN)jCdr8=TB0s*8cr^nhxm@})Q_gn1{Yui9_vX5M9x>%c(Am*5jX^IFGyW)-8a zHmK1pJJ*^=#oSdNyf9ozIR(mf+zk8hCL9=^)^>;6A;V>+KE`e95bsSMk&g3QgOKS2 zx%$nC8vQo}C`0QywZ{6jvbr5tx}gdd_Qxa5hXUFhS%Zyw1X2 z7h(&FR5zUtnU4GlMc^+m_%S0~yv4dpW5c^rB7R2CEE{jedNaKXXM zohItw25DzDca4=Jw-0NUK@SDs?WN;RJL|*g={u{rJLD_`vnDrtLTD8e)HYQnSzDR= zeOY=x*MGU*Dd&4~6mLC6Fxe+LFbt&}+Z<5dcHP(ysVQP`Nbk@ToQ6dA*t65Yqsx`N zXok5fV#hd=xy-WpH?KLbVW zot?cj9h9d+##Y=D!!oV_-8WQ8AL`zE2tF`*lk9RA`A5JzYP4wQg@q1wL_m#^9LI~R@^D^ye zL8d%5oHiIbtYjlr4t$!KFwqAu##oI*A=#crmK>vPmmKubi{ zI|BlQX3F&EW@FC`%y!-|waRnE-o<1HcTZ|Zag_jV=Pn{lja<#NCVO$m#+~9vVujt2)&@?U7NOA~dv@-? zsDv2P96=?Y0UzV9Ch#vQpx$xsI?~vU19yLLtCEl#p+UJt4K#$ySrhrquy5sEfbxVs z@kt1iGAC54V2C4b3!9}&D=N~ymD!d+ZPhaoKW-goF3OG6EJ$b7qSv%Jr{g1WUdiNA zb@ZkHL!PWbH6nr>H@qqjhfa!Bt7aMr?C~NUv-L}e^Y8N(Fvj(!_yAkh81%>)$t zQHOe3OJH#<7xhZq@^%hO#2RlFf;7CS#>%hIiBQIlrGia2qTVOrJl~xNk{UoyQJ?k6 z9vnYr99KkCqp1bYutrr`o9x^bt_?%444jgpeCZ!zNXOPiF$&J{n>^y2vwHv@RB4JQ_ z!{nwshW;=E6aBNIcGS*p;_G>qNh#9 z(Kj>|i7=@}W=uE>=FdU15v?KJFn&MP9%gO+(WVw2V5d(^(UI;LI75F5H*Co)w|8P{ zg~Bg!vR^K$@_-|ozjnKNy36K>J2T&?m^O?RtYH5PKR&0YOP?;C{cR1RGi7>*r&+mx zPf72{Ou8XTD>Ah?Uv-)j@^adKS~gLh`K5dk0CgC7Dj+O%XI_gSiSx&a5>CS zO{lf&nf)RtWs%Lk2LbLD%?_B@aYzT-SIC8=639nSA$Wv{>&p@h&QN`*G^>2f{R`j* z>|ftg{VfOfh?7t^B@YUrkI(jSy~BX@nq#oC z@N)pM{PVwQ-+0Gq-S4Eb=3=Yv@=urq9SI6FIZH{;SsFJcAXzVjA-POJs6 zs#w&dhPH-MIrAkSvd{c6OJ|sId}^%_ft@T+v@AN;Rnkqx!dGm^N`N}DJFmLbpu!N> zbjH7fk*jOfHDH0xshrRCoB*(8y_DIMx>&EVnmG=-$t99LK%d86PPQ|0>VyorJdAN-?G@*Ee$VneC1dH!qIR?aHOjxQo}LOAKJ2RP5{ja@l7$j+xCygja+< z8WE2ZX3cR^&N*f93fEHG-n?a`t}9#@NoCPpJzSZvM9v+#b*q92qWP3$3pilNGbZX$ zf8WD959*3Wyygw8U1Q5rgQPSr9MV(waDX4`*qx^bq2T6L`HdXWVuK3@l0>O zihhc?&fO-7eY<3b$-DPC{pRnV*7VnH2dJv{<85PNFRcuoFI@ph&OVBT?;ost?q30d zJhoCgpW7LIgYj-VFLVX>zGiqc?UWNPCpl|v_Y9pF=W9R3;jY-!WfS(1dSS{;X`Wi z5kR0k5PaL~O=#2FbWPlG5@XEmZOp7$(ksA<%%$Jfo%MIu|A;1WB%yKe{JC30`W4_J z_X;rh&EQ81nkRt2t6Y_KL+u8x^70nFqmo!G)IV3uOEswu%fOe=!`#4+TA8a$JJTK+ z9!gKoGw(1r4q((S?tcRS{OjT3Z+WmI4gZ-Y?c@{Z+e=Y&WqQwC6YZ(Q^XUCzzuA_J zz1vtMSZs&_7kVAud*(SEt{39CRT>A`R-O~y2;e8)jo{7Ey(qj6hWHWq;b)a{{Z1!` zm!$XNM*)E1|Jfl5s=Wl^?X4^?lQ0vD2m^#>u-dyj+*!gt+*O6AAq>8xsdI<6rd}H% z58s={c#vEHsF^Q5C{ojLSTh>IW%FZ-ePeuug>7Jx1@`ip?OucnW#6`WRY2~r&Pa_A zRTb&#*2*S8+Uf1#ftT}3nOA@~nj5?opK4AwBv$ciVW+Hx@33VERqcm_vya>MZr=Sv z$ooC=tkj$DIv`hoM~^c0lr_EZ1M+Lvla<{KPtpB8@yLneUI9WPFZEr%^S%Q7%AD+1 zj##Z@+K!5OZ_7M>d)~$VZ1Vc)mk^Qf%&+bB`v_h+Y7K7_oGaOwB47@4c1+aFlB*jz6cSpuLQr4IGz?A$};7j-i07%M}A?s z#5n5sG;lh3fA!W2F|JddOMaQNm&Zh2I93mvk*5g-p#ft8)ja(vdM}RiFP^XM-V^UD z%b4=cAPY5%*~rofQ1FEjkF{rp&`E`LNm(%FMuDtmp)Y>*TO*dyHC=KjH1TlF_xuP7Oj%@b2ajaz>mN~nm$q39CU?VVd zYGuR9Ftov@2z*jI{V~}98me1w@gGHSpaza5zg%mK)v1Ug_%u{Q$&G%j4-8?7kEXiR zwN+jTS9))ARKwkSbS`Jl)(}ize4<#x&`z#3Sldh4Z9do%K*yK!e0Tu}nYid!eaJ8E z;I}m5ScN@AWjaY-rP5WAuC`Pt#4&40uf)g|0ENP}71_NEQc=1{3Gr8t)3b2z^_N{l zvLL5go+QOJATZO33?EBSWuUaI0z4Re@NMdYdrx>Io=S)fqgDr1-hj4132TZpx~;%hCxw+*$oK!rhj$B-GRt%*fCF z>q-t!Dasg%t>vD+VH0SwBAx(-u^P|`;loT)RIO1TF(YBwXRE0emuU24y}MQ$%M}K} z8O02l_2%&K$|6?Di1(H86eQZZJ8jcK3C7>C%%BIw%l22mwM9 zP^4Gs9i&U|9rfkj-+J$N?pnY5)_v>!=C7GqduC5&W@Wy6f7(ogzifcCi>m+KdZ%fk z$6?e%(MZH&P;8q{P)!*J-K(WS&6Kt!sB6k&Nvr9a*I(U-rzWepSCkt8QEw`hQ}+l8 z(C)z-ja{nIW2%wCl(7+!)(QQ#;A){T$znkE<|)C2AbVCjIm`AUj_;i)InCGTTC z(~($bNXtEvG+j$Z@sKQ%;T10@*VG}6su0STdqscHe7e%dPP><$uOpYE>b;tB zu;F)9ixV5_o9Gr8hOK?u<|k7bZdZl|v1^Bc@{o*U-Z1{{x1N+ez+)Z>4-W}Ej*t2> zOm*28q69Q`Z$)?a0y7t|Vzb?5g4?c8Is+pLcLtvgw&}kv5E1>c)&dA)R+V0M1C$mHi-Ls}%h$JI?pj{V?@3U-0PfFWkAde-{Wp z;qA6T<|?RZVbCVdMA3QcZPxFbYHIv(DlKWb#lZxFI=o448V$H6T(7lck}iriIhsjI z!&aQ`70A&cQa(lzaYCaehUI&k zH_jl}5zwkcOdnDwyIj5QkT|BPi@`HVt)}BMMplVO z?wEGviKoa0>ioyCn|U=|L>*En;}xIeJ=OXe0AQfTGGX1oz?|pcuML7Z$kF)zlIsnvY54fH_o!Ik3FOPO9R=a$~*o6yByq>4|+YKkJaW=9^*zA zO!t0O>RHtGDcNk-8JItRrCe=ycqja{h&`9VFWWMk5Sb%?o^)mZaY2Al2SGb2n$Q(? z&Tsij;^o9@E>`a2)|pylu)>?B&b-l;0#E8UC z-i$5jFEQ{@5LtKm7I{Yg*iZR>`!|hy{wskSQ~}gC@mRPN5!vEI;)^x_gMBz|MfEGgtU1!1dr4Md_GK%?~+6Gvnxjl5h}< z-I~m19X3lQ{{O$QVN!T}hD6h>CTi`{Oa2wS@QWf0^6n=}|L4BaD|&nOYd^`aLZRk| z`d#Lnz`3{!4T}FGz5SQ+u>Y+V`)~AN>hrnI$)*mF2%8HdxHaSTUi>W`w0^cuRsSY5 z6$j-KorpN$Jj`sX^X*|S8v3wuJ-0XC2*6St7?RB8GFPW}k!jbbt_uvOjNc&6X&r26QSY!=tWYm&K6s z)sK3^F=B`nUh;8d0DEv%nkT@OAAU@2-75J&5#ETz46j41D)rj&-i?_^PMhlt}aU zL;s|?HQxHo-mqZ?aqzMCL5wIFv%-HEtk@|Z)b z1A!7N)MPHaBhkf;);RrV*Y7jGZs5zTQ$);1J(b7viV{!~>*;P1qn#ya&q_C~2I76K z5?sxk2X3hdiC-v)hu~5@obTd&JGlx)g;%JFUx+_ciKC_B#cg|a4=eJa{*O$T6}+_M zM#7nFWHD*ts;&`ZRRZqdg=rf$1-_C-Dey_zRPH)e_DN-7#n^$0hz^S&sg7S4lgA!F zX4uR*cF7^D>2Bj8i63m70tsUoYWXc?ocz|VIA)lOLTlz&ncHt@LzW;Tv0dTWN_-H5 z!mKLgN%ROhA*0xI%_VMh%)--R{E{kFKyz~x8QY!f+|GKHNDQDvgRS|vdj=#>h&w>w zs7u|JjX-1;&)KvH3P=$3^W9kGH1U=m;(-}4i{=?gIWF08N)j3JF?Xw1JKFUGk2&O7U75ROkLggsaTns|Oh*d94OHnxa3q~@Ez%~* zgDOm8w6K4cO6?l{g?Ziv@NhRo)S6A2g%HLB?%k}h=I8=(ID61#FL)^j!=z#tu=WO; z2GrtgSM&TXkCmmg2}2TB24B)6ihvS?LngiGf)h^NoYEY_ww(l~)0$k@g;hOpx8pER zVGPP@v@sFDcbD_}WP}r`Ycz%Ek{>Bp-5xfl@qBS2ENQ-|Pyh z!_H}lO>{-Bz&rr`qj9b*{VX6#W+ zx2QtQ=@d5Nk!O;~m6gOfeTpEVt_B(#s=>Xqg+DXZjliHyb!%S5LjehVF9KA}4p`K! zbzE|LntL58OcFJWGM^jFAdtA+DlKWStedG@atv#Fe~QVi$hRVm5*p6t5RoX@7O?1WEKSRunA+MPbBE;fx}8=Q{BTrrvZSy zr#WQ!j433Il383{*E|@T7w9mUYtj)-sNkd3cUW3`fCdsnC0iFGqNibI$s>J%2?UUp z{&@f!_yc#hp7@-3-ir~wB}w{t=d2fgA|SMa$8{XNyQp|^FODUo=!l9X=tgr(9@1yG z5i=tiuT?MJ?(A2RV&+u|1WZ%LCUR&mIb!VoJohi;7v~+_A`LN^wRSA4jigt6Ok(9z zi^NtLtd}Enz1PH+?C2Ny?f|NnkKLy+llX4X0KF$$H(tZ@d*xo%B8V6NL*S+4+i#QR z3=A?HtJOnxoQ6vAd1cpVz@&Irtcc*pq2*F%-ZWUuZ4x!ynY5iXWP*M}J+dqd{Db&l_eET-^bdu5zbKlv`!|1( z1v%(n6h6y)KB_ZybE2tJ8p15u0U+IV17Pf4j$NeUk~##yNg?%=DD zi&utn!YKJZe_lxv^5xmlAke7QBdk)Zlk}~t3p=3EctBUxkAW_eEcq%`v8=mi_KKP; zGft32cub2B0}ztvsOtQ6R;Twkh_R%9TkL{bQpwG1rXauwohYuw-nyvYrK*H-h)DXh zCN@%qQJ;1kL8*sb%hf*G(E|n%^m_D%Siaf7MJTC&`~b zn7r$U(y~gdKHS#DYz(gTn@MddaL&gpgAWpjKHy1$Gg4);X(eItVm$s?j^FdZJJ^PE zLqgl1y%50?#M|INud=?tNjj#O77ScxR#YMdi`#J?Q2IOz2kS%j@1m#CVGi)YdScwQ_NQu7S*i8L%E<>JX-2ZCdPUIW;_^tnBQ`{wp!!i(W#?w$R@I>vmzktpSa+|v{`62p z7;Ef4c*_dm_)$~3-4?1F5bBz;4rueyIIqn7t&f}wb=pP zxy70Se3;#;Yl-ss!4y`f%q!@k^x@E*hdiA9=CjLnsJ6K_#;X*lS4WOaRjCUI2HAKu zf>8jS`D=9@I#mz+SuCa@;~Vsu5trv>rw0aEw(+}u^b3P<`G@6jxt0jAMFHZsB9@)^ zI9VHNrx=m=^GMj)Xlw3Xu1Wr?bcklWm@%MJXQIPNq$i3gwxa?bh1;?#j?YPWzL!$t%oB(Ug0{8JlzrmeQiWzRudGMmz(-gbq7}KFZq2g`Ei~(mM&aE2(lA5jX2PauO z8hJr+p2HnNe5Gr3de6a*X1*n1*V%&jtwx=J9aFIpn31rrI)Dd6E&}wDQ=u~lHu@?1 z;Vf_8F%8#9sbc$K_jj+1-R!KQ&!K{Os*dwEqqU@ER>9f@0pxD4q6R-KBFYK(p_t`2 z&5ts}zc&qd2yB>)#^|?N1&rO^WU}v^lA!MarDi-Q+|-iq^YG;HdhC2C<4<^6PE`^N zv_Ql!8=gPYm*uOWRuvaq;?N+Wl)X)znmG{6K!9qCDrq%GzwxW9`Jecoa#_nEDO6=9 zB$u%MVehBLCB2F9t2cD`AN0S+6P^||+=`@@bE?hLPBOM>1Kk$iBO#Sb`}hy6{4RVX zq>f*!yp@E`qE1KjUhE{ujR?GLD}D>scgjhgE)nHBk_oug(%$`?B`$2Tkj2itP-AH$ zDc)SSyItskzqwFhyY>c6k-noi;l`^g8nta@$eB;yJa5bfSu1S(1=W3kbFst+t2QrOr_l!zZe^{{Z zhw`WO%A`Ug{n@S8jf~(q$%mtiIZ`{keS8bLlPk6M)1Bg}K1MXOLqxVsLoUy-qE90A zYA5+`3hydL^kiKGS=#T{*pz$y00@{HTqw=Fkf<^ODJxb z%U?)HHqbl9(c@{NfnZf)iek>VfRWef(?M@(BYZ_!b{*ZFOlJ}O!9=rLNV0PEd12o& zR2T6)q0oV>aJ#LDi!8A-TSlf<+vNp*&I_HHbhj!ke7HQ@MH-UAiq`em$my_wIh9J~ z7S8R-Xi)JRUFR?>QBg{hO^@wti>^-2WxkY0V;IsS^n<5Kk620m9e%Qar2A!{KD4d| zKY&G{Bj+n}b#yY3gu+;LR>0~XX}H9nF1Y`PC`Mf!B<0cVUE^7Ux=wT zxG%70%THoMsNm(6Wj|u68p{?el*6NuOJ91|t+3y+4Hmf?-PI@0xwBz0(64rA zcBAOJ;BnjJ7)y@FdLdb2R~)hrR_ha?pT4mS<PSLFB)VmG4BXGAWSdp<5r?Kz+TBq2G^T^o#GD+%(y!mS0 z${1r6<9b7B=OYbwo{oSNw~RraO7k$7yrzN%IODS>oDyhX`i_0iWr(WOiCAE%W zS8$YSbyfQScYNENHA{6!MZGSbv-6u9-JEG{*Y!+#X0AxMDxD8$^t+>@Ka(|U+cA$= z0m6{vy)!{(U=DP92ZRIE;GM5!qouHJD=a<^k+O{B3N`32Prvp}hwmH=$z#)N**S9rf)T;JW$IZg|d-?aBM2FAt zTL>#J-zY=FB-jVb`*|`K_o^{mABmokc_Seh^`!O%K8=IU@rpa6-Zyj*sf`(x;Lkm` zzAtL?(a32^xGmKm7MP?YG1sC417PCp%jDu^)mtZzvpV29ketPc{PSN<-2Hn97c<%7 z)~CtAl{+(Ut{n2sLt3<~GWW;Tq2(U}A_+35E6C7@|H)JxNHtT4e(l?VYeuS($F%e; z7d;s|y`lHyUtanDaHlj$!6s&*@u^3lG%!gI4H3|vhAr2w(M6&~g*kX??3%<~sQpa# z@~`e@9kkKVT!F=J9m4WY-?Dx>_zq<-y}HH8&01z~n3PDpS^o-mvn>bkO3Q=jCtq+< z{VZe}&)T>`^Cc4>KqgyX>!VCY&Byt8VYQEoLV5Y8F z-59C#pPfDXOhzdMU(PaCt6qOUJZ=#H5+fxv0#_#WS@H{{dHAcYA@P+#T=CV0#R^&|M z=N;8^%Y{ny*AIf;B>Xh~NYNPlI3%@rpRGxzXwk~6bW0PlURZc0p*4umE#Tdi={+Bb zLWR{=b}kvQ$V%bfKz$pBIdU2b%GSezzn!x(UF?{>1~QU^|@ zS6-}Y2N(B-riq4(LG+E{ExYp70lFN_Jbeoi9x|b3mHK4n*k?#+o@_IH zsAbKhQ;sT~HAWw*goMs=zt*jdF;CMAV6m!ZbdVJl3fD?SPcwFbPRJ-DamOjatSxND zR1j(@V))9HA<}U_85F}K&M={&qJm!FOpMw#>vVTSz`T^GnzP_9aLc1rCu04o(Fbf! zlT=Peg>58fI*b$F^;|yLgxw1EX-)JkH5E%EUoqoXdSlg6O4{sQJh&rbZCB#d8yoEN z!#>_gG?%U{jnz!+QRBTY=EL@!5tn4;yfpMtc2K>36@hrr_|r%*I?jo-bxSe7y73!5}S6lQ*u9BJFBU+YZ{@72(r5*)!_ zd_=n;>T+`n+Y>P+QkEJiCzoW4@1JeB`IQ6!Rn-!*Wv~q~67c6BGi2HD0*u4};u?1- z`Jh9Te;SY9aV!SI`-um$&S3NgSJw&z3Iy$3kdbN5E%_SV#MQaIlZYj%-uYf@bL4%j z#OEkfwT|5bZD60xb)4KHrJSulP&OB@rjuKXdB0cu)l4yoD-LgHVy0RZ7i1SS0Pk~N z%qLfJw1#GliEOb+Kug)kUGF%tlEWKkc8NVktB&D2JmK9M{wkBo$ zBRMAwKj+u!BrvwAz=4^Q{6&DFtsa5W{-(KSbyC`kx&`sW)T|TBs+NM&Qm-@|ELI$b z&#*1dbZ5F~XlXU1tCDl*tz&i+o2Fho>_cgwcA|Iz@X6eY$IL1pcZe&8v-Fpm#Lq1V zXC>O<-JhJMmFJ;Km!tQJ6cWA6Wi`_)3sT-PqulBnbM%;O+hx_Irs7*3IpOm0$noE6 zpoMCtoukurR`r3uC|co$K~D>U{N8UVX7bgV>?-(cwSo9K2S*u_;d}s$9oukVZ=7It3Oy zUh1e-y8F+x@?erI3rPbZy|~gbqy?0H&SRoM<-3z-?xg*Xkvs~Dm;Zlsp?`ky|1DJu z?L!;Pqr_XT{nCm845xM}HMCzgFjBrjW1Ei}Kn3oQ-QXP=;i- z$uE^9V=XPtAHckJySu3FS1{8u)1)bXTze7pY{88(;}42E%YXj!I{)<=^Vqx9x>5y% zQPjfzI(^;VET(#rV~O*d;WRBN`}EnnR+j+N?W?g{KPEFPF8dX(q}ICVn(agOzOmy{ z%_Xoh%8;BFZz9H_v`?Ap#fmcIm|RU%!)i%IMH&WriLlfEb8z`?lzDUNr(YBeqGN(j zz@1AM`sP7+kKKXZ49MRr<8N}$v2wyi5_g)RsF&JC&j~9 z?SDM8zdJhrkNlkp2z{}oN24>pZ)y;S6K5RGRv1w#o@rhhZa@gGp!3XUVPe4PM0DDE zn~CS=Bbi5g&H7FC>c=sX1dDgeQ+k6-G9!)>dOAB57hlrub(p{lfP_|WzB^lBuqfB5 zx55{$iFVbvrN`a0811oTZNKLz440{rtplGAtt%cU^Trga3i8f=fFAG)Ea zvtcEVeb3V&HFrMFS41Vznovdo>- z5tVDw5}x^DLbY`2sb4vL4&07Mw`%OXW~W3flKDnQps0;$C5KaAK({QnCMhrC9Pyx`kat$K z`^95yzU}NI8P^TwacP_bDXsPgyx5yQ*%SM&6FC-pi%tvTTO-Ao+4hb+J;}MqH4&=! zTns;@rFJ(d=O?MVpgt<><$W%tUDwqSuqyy+T8~Yy>2j&4$a>(QaAV&ww;(=-q!dY6 zip9)1zqYVqpr1U<#2Ot{qRV(0VB4^zC5-LJDNDEKa$djU{p{BcfMhbtIjcC$6Zyrn zvu6v568*lPX(Ru*Dh-^X;Pg|R16_$0)t!oQ0CQ2NyK}}a5PJ$-QpdVh>Y=M#p=|s@ z^xN-9Q>IS$03#?xC;+Wfa3UNqP&II&Rpk)z9Ub{6{^}P6M%Px7P-tV9x z%#z#16vkxZ%biS0vVmnLv8Q#;MuOHB&|MY`7Pb**Q{>;Fx343plP8 zaT6)P;PAeDh_6HI5ULHPmUft;$2QykP~Iyh2rxhXIIvmR5joDLS1} zb__Cvo9XH3E)FH~3BJFc19eudxQhpftE8GGamSE)Bl#W-S86vrW*_z%G92+>$KBE zv|NF+6ahf?m4?2GhiJI#R0aeL@N+bJmejq;pQ+`QuF`BEzezPALQ90yS$pUH=HZ#PUzJw&)TXr)K8HX)`tY1(k?d@PWnilG2Ccvx34lh zzB`^!M_OM1voQROT0haED+0B$+7zX-Z05kNezwT9sl2$wDhx#(HF8v^*^2{5OK>PI z9p;nJoww^vs#c>J1VE0SBP6W z183YYeU#ls$qsfhL&>BR><0T7#D+1@((7~^Qrels)M?^cNz>hW3W$%1bn)srf5wyn zuQtR#@MX>AE8Ku-p6Y||a;s8lKD^5$KfYWWGqd&TGJSw9&NJ&l8c;Sr>=%U{;mnET z!m5K*sj&Sidbdsbei^s2vyxzGTh&Y$vlj>oqd&~U_)>AM3b;r|oOnJ{&slM_E!Bn! zsNp}%LFR@`+O7Qiq^5^hqn!`#*U%k)w8cH7EgA7Iu7ZGB*5Fm{@T*9^tgj9vyB8i< z(ZZ+i#-z1O+|d0Bo*tIHi2F0MR58*WY*AK;;zXPy7VS6|Gd$)0hxII%xTg2L#AU2o zUWWV4YobDBsjH7X^j^?;fZTAm$Z$d)?f!eHSE6J)epcbSFSmqx5Tt6Cxz=FCT#TT$ zqGpNDGli$HaA_6b4!BeUM=O~H#PI8=+gzNShu$>e3L)#EkK~)4{T_=hF54J~a*L&< z3~A5$VRG4wmp8jNO7;pm<2!zbS6YFh94q2I?a$Az)G!zsCt+p7%i0s@_gl z{%;=#$4OOgtU^soJ$(G2A+uIe7kkID_X#4rolR|zQ{*OB0Evr)Pt{xv;DtJ>)SYBm z`sZv3NZVF|DJY)&>vH=4zW3i^t{dvZi?wK*donV`!_&yUGM23d^Wi^*HCe?R`?op~ z+Jgj^1r<5Bh)TqFJIo!tDppNfhZ2iYfx4p{VKE z^%do=lsr9t&}Dy>Z82kNu?Iff-ybTMck+wkM9t1!(LKF#_Mi4_3j{?Z_PPPO2r@}fK+mj81)twLa**K={?`}R z-agxG1+qTIZIhVrhKw;`PPWR+zCSR~Cr+mGB^#kgZb=T?CzUm-KABeM%qktl?t*Po zjGJzY(cMd3AsbNTp~Tk1bbH4$Jancf%HwJ^U#B!?y**I=igOz_%zMB-Tyn{ar5#Il z8)Y_W%e4RIt{W*=Ew_04>W$&Dt*qarT2&5z(#_X8c}$6|MI=n`!~CIkGQK!`QhuYNY=TU-#ITZx|RHK+BXcUri>55i5TCh z*`~R zZzGkyh7wS!vMiNk;=>Pub?A zOQB_`{wW&o6@fyUcumo!+v@Hv%i38da61T{svHXhzZNeTL593bAjSm_O$@ zM7;nm`93uyPPU3w`vz2kfvyF;U67oavj*`I1{;wlI)zls2E~-bzPZ!F6b1%qZp;hR z{P1PXj@#(9jWB~!q;0E;*9RO*gTY|i!y-z&1tM+Mdw|K&9=Ng+*BaE4`_B-$R&-W& zrY|`}uHqxb`hlq`d*i(cS45q@U)f`m$5l^*m}2yf^ad2j`F89gEcxH_Gyfw;&V_;* z#1&@|w#E7{8$P`tkQ)YExfst;Xlke}jlXZWJ=>i#>Uz=n<9+a1D>{kX<>7v#$kpZy$@=N$ zKhAh_$AG;!d`xwlT5Xt2NKt59mra|m%sFwjYm6BO9~;C(iOkthn(Wo0w_%YM3lHt* zF84;CMb2M_NfLQq1uY~LJWDlr_ECH8hF=razH_?DUdHDIVUEpHOJg1P+B;KRfbu8& zoFxcaONoI+U}WmFXAw@}N?dEv=jTGqR`_d{{C%>moNN1u%HEAD_*vU8iVrRB{yC5% zwLlSn-SGLa!Yi_S(TqrA{O%Y1pFBZ3Omm%06jm?Vg@3%JCFhhWhYvrj<_$bUXSMGB z_`*6#{f~2hi`k0$b`y9-CQpnnu>P0RX$8&DHYSkuPMwvtrvB(r%?uxvMC4eHUgNk; zCPUgoQ*f2Q6DJ(Cq8XW&4@cvf7o_dH~Sk9?`KKjaUN*KK$Z4WXQW z5Z#a@$S;a@S!o8*X<4e~!1|1R)&D@qf4ZLKLdK-kQ19cNPxL>_mm#Ol`)8+FAk*Ibw zu#{<871%_cPP;4HrId~`ar)9?SBGy`Sp_Q$p0BT*Jw|`%q<+1BO{8L7dXVSokl?eM zwd)Vxj&|koLv=CP`WQ)$G&q7=g{T$r6}89(2Axi{7b)}BhLczm%?fRiHG9g!2J{!d z8&=tm)CwXQ8yOB%r5P%ree55yz`)9C`Z|OOe8ik?tp4tzzFeIY0^d7oT1SgJWE`P( zfG;=!Vm)B4!>t%$k4m@YZXmx#hw7W6K5Oe@@36kysXg$%s={iALW__0S)9|yZ1DD> zOF6!9J>#qMo09L}tAlTpl`6Mu>;mFK+pUNtw{M_9G?>s6mRNqn6d$0>% z?TYa@-_3PE23S?VN*r?^wslae*K~P5@1kBHHREWktF*FW0_q$vrX|0o3fXB7{at`S ziMYqeSzQoq_=}?Mx0iLU4?k*NXc=Y;7^0_X?;o1YWWHZ_4nvPwnp|rVU-jH-u<3-O zx&5Xib;+ae^ZRQcDljOjs`;HddWX0+!p!&%_O8dupySixuv%Gx8N^&*3GZLmN=T^t zGToyE7(!-~{e5W~!_~`z8An2+r#5zONuzuhg9V$nVKuwT9QOfi2lhP}$B!$F)LI2EV*)AIQ{LX@#{PCLns*Oy<2CaR65-=LQxVKp z>k>#abBcjWHG_v8u?K+BYOzPzc^M#hE9|>ehyd(#*X;y21Btpd3Uk8>gQwPcKF@2**Nh-zcNq#uaq^rVw;e+3G#h>%JLjfN zqF?-?5GnRBYN(xtWGnwg`E^j|X`*2dP)xaUjo@U;FyJW$8_W0|zbZeGSJ8NpQ@&@m zCd8PMoo!!%jB!k!u-UYI_s17dQSOQ%EtnB31B-Ou5qQb6Zr!g#m&IdMu-FCZG9f@h z_I8Pp=3(-5TXtyMFo$TigCRyUnx@!PawQJkwl2a|o@v*?ov66cTB zb?dLc-(X9(qRvHqMEmBqaW^KbDci+WRy#c~yQ9!@m{hCCrFo%qBs9F(ozt;S6RsAD zF_%S0L|Cd<(-O*ufovFMWNw1!+9a@I`^)4sf+Lqd$Kr~+h#yqklfmHBkMG`$Kb0KU z1GA|enaU_ek5_DAP2UoYJs@?ZX%8Ab(?SY)Ov^k+y?L>QtA8yW@OyFIWLre9V~jr3 zJUYoaDTg@@?PG176ViZnJCJo|Uu7l!zu^ruaCw1W6xa&|g&m&9I#rWJlJn|G4zxEY zBbK^M4U_1*#6O{d@9zM~nbjd#=@I%D3L6SkMYA%;aPV+?-p%K5uA2l@U3~h|^HQ2p z0qA8e(q7TU~dd+)2qcriisaJMio%?H7fl z$1jS#;}|JJu%JN0Ii3f9s+BO;F#KraDpROpjH@uCwl*p_JSwWU^n@&j6I^mgWMGNR zIn_=6HyV8Xn*>V{qj7Uq)B_FfRQqKa-VZ3{-}-l$i*UCz9&qC}=gHv&9Om7C@d}>^ zaf!&TU1*(G0fsK4yK(44a!#PdJ}b&CtNWblel`26TD`2`t`={U`TvV4#a#yj;+<$a zeYa_NU7#sHdimYVO5tmr0ul%V3HlM<@kJr^3P4UB;hJDIriOu@Yr)j*N6<`W?J{cJ9yuJ_NQY3^P)>|w9i;$AwvT^HXoVCp__5HYC447&viYJ;XO=fwB!HRn-TRiLcvS zT%2I=HWx>_r8&isHQ85J@A5Paa8x+%eY718IGdC3#g zM(ga)-qu`eePaImg(T~71zh*|_FSEli;?8~Q7m660Z`3{S)6SF6!ME|E-NEnTQeC(*c={` zli5NHHB(!yeW5X9jQht{n%}){i>3NJFZN1jpc)vmk{0i`j#P}YD1k8$yc(ONqkHgu z(cv28xfm}OW?AM@P?M2dkIj=VO~L3h8?qS&&o0C`iK^8SebcY+1JD~@;vf?UmWF$t zEwcj3gczi9X8j$}Iy?T!ke~3M(O+}mq0|-ut-ik~E{8R2=;*?KQT$wlS6Fx)?nn?g zVzc~GE~Kw5D6o|e>Y~Il*7K7KZ4937ic#g-K68K=oEXdTyak+BRo$*Z5k{+lj!gyk z%J`fKQf5vVGq{5g88NoWMXxen)7Zay%+&rp5!>JGp1A_AMTfHqKqfYH2@f0}l&Jm* z*UceKvPN7Q6kD10U3c9N!amGFUY8kRz4u3K_$2QRp`=KWvOOv=K3iBOfZ+Yu4)^*j z^5aiU=LjYXv4V4MlSJ*M9XY6;N2Mg^7rmlwPOu5)p6Zw8=E)xv8%iu#eP!huESTr~ z{=3%Lqi{-wegrMus0C8O{Ql>4@LkF}lb=aTw@GoA2OJ-K_3)Z3)ord?BN=sA_4jCepZglgQHH5G8(+v$`1au_@Fy2v@peT;9 zsSOJ%Oo7O!7v`CcvH2xjA<1Zs$5eSFlwpmr`pGbYI%<6X9qr;LwKbytn#x($t#WP*pzJSL>M9mfl^!stq)L^E$mCv~{D{k1P zdW>#Om1xeY^Rj+w$cej=HcPq(Zr9pF;J9N%OHl0wi?RQ3c(d?u{J741^^vNzk4CeH zBVn9)RDj4_>+*t%*Gx|@r_l64etTnmI($&X#vex77=PVtLsyNrfzO($_IdFE>*^Z< zauntU!=#HwRiGsaGJ@PHJOtGCV4T5#wS4(>Yzx_SPYN78EYamfr7E1e++QT$JM*(B zq`a;Gm3-VD#k4$&JWZ%8EOc^}I{c{lWEz7(QRgFNM_@oi?}DHH|6r$^zkC}P&X6k zimBn8IXMiG>a*5i5Ec9}{rmgAWQX%r$q-wnvgkEc*_^Q-*khhN5u-KEoAN7|v&Sqr%ie zBTIOa#SA>ZgDEE`#j{MpYAR@HT@xIYm9|~-{epsgt^pfAyKM^i5uZOV zPcr^8WQ8$k%_NI>3RwlJYSl>!gKr^W5fP;)0lz5z0)nUb9lGQN59^9X8qUfWt)(hg zw8M%oinmKIJ+b`a?UIq#tN4)iZUW*mMDbK8rLvb$>8dVZK|2o_lO7dg(Jd0G!RAfr zVxT0DW0?^toA4z2ivoDkt|b%rn0eoEY~tpvpadhE(^`#tG27C#MyBvmn|0R{(P!l! zb5p0cMV|qBsyg}_rH03dz}0&%Uu_7d2bB~JiaXL*%lAl}hn>p!bB4T03%Iqldx$LI5L|Q#x9aF~E2ZmTg@?4>GS^)ch#tu#hO%g2 zfFRpS&UJnb`A=?Xg_GQp!Ar-6V*#o%0W9stSb<10*lK+UCaL~o&Z6kdrD}Vzy4cuT z`Bs>`T)RVZr*1ByhAvN*w*nTaoE;B+{-70EO^Z35!M#3@uK0S!=hX_;qE1upD&7x4 zn)LhgZy4knIzCp~Uxb94+RoEW$7+a$Vu8&Wr;jYUK;vt3b)}OemB+ueyLje<{```{ zuB!5aWssDb#ysJXOX{kRIfX}l^Gt{4_C$p$7`9vMNNJDW-Ats#MmV|O3wErtott)b zJ+JlNn(iy#lYDE{J3O}3yJVnNlrPm`e&gvh?_R%j?$QBbI<(1zAjbwUlOroG?hnN& zd(*uKgFluwsuc7ct*IARza|g3yx5N~5#hxfqn1yS;Z#U$m16AVyIHXv^^+WT9Tt*_ z4e?k{`}msH`*zX@{Zc?9e|gtdZpBUE8&#wkeXm34>iDM?C>tEkJ#`wNfyZ!iZQj~I z6O5H>5>05=ztSyocDvyUC)Qmf(z(ZM>M50R`?Sn1Er|2~McsQqHMQ+~qu2#2NRhrF z1PBn2-eF5mXeM+BC=db!0#cQ#Tcw0f2!Vtqz4u;3rMJ*)C`y;AfD}R9U(VU5>~r7u z?z{KhGsgSg${5L7V`a^`=E|J^tTq3?@`^Ktu#woI&~z50&?TUm4No%dAcgPNUxFT_ z(88~iB&X{ar!hzrUtW60Di!FXvu3$-fBaO5VS$pM7<>bDE_AW_rZ)P5ceRubLW&JPyIMX)sVAB$6S_Xf*V+75A~z{u6QXU8m984mt6z?1Q_8jk z>YB0cRso5*nr-Qv*)6UDg?(k8RT7E}VmcstPn6TGw(nU0Qcj2%x?f4tJ=Gr|niZA=S_KR{Cq; zSGNJ?ha?+kgW@1fe;;q+A;;;VNt?Qhn@?xQ1^4Ld39sC!j;AkyJCF!p4=}4IztVTQ; zZ;{v~HqtLWdAch8K}FNcK}K%|d_Yfs$*w@0>?{eUV43RbAw-sGQ^$ISD5u1yZ#u^j z35Gj9n@c;c&Gb`Tp>ubi+_vy|Gu}Sfo>^d50G=LiU^<`E0}f-#KlWm zro}E;_SZyPeuXcFO%BWvda2Xkx+2iK~zUIkFq zA|mZ}RK~bVVKro~i5sf#JyIu8^5=ho$0iNcu}mA+Hg~#>5Y@P`@cXz_<$>WWDN-cn z#5kplGe7xqnp$wPVMXAxyI5eG-w~*o_0?<{y(<)M3594^(z^vU)0+L4x@zb(dhv)K zl1Er`F9K`nxA|PKX6{c-PMjB1#8ckD2W5rv6-Ua9^z9hpthm;qPp-w1*I1($nz!Cl z88;7Vl|OZ!kKfSO($}~INblmjyc|W=BegUd$U(tJt4&KrZAePWHtnfxUR}KKFBThX zlA_WAEMBa^d5Y2RsgOudcn+&{W4wEN-P!FQ<@(+mJ-h8T7h*tRDMq3-K6-4-lx%(> zT-M_Bs7GAr_1Fpu6n2!OAuFTAU-BiW17d&)!LK&7dK9t-tEPeok!F>Bes|mKx;z@m z=IuFA`;&ja`Krb^L415X7jxvN~9{<^6m6Y!<$ zTj#(69C6`A-#FrZbJUdhxZfDBW^WZ#P-@`as{x~EI*PGYnxDpyvLU^A-3Ij$=F9F& z(@o_ND3rm)5(|R3p&TpeZ-E{3O+UqI+xkwLF`)}0pODE1F3nw5pySC=Qs zQ`OXMXTDG%4_l$%%9*lDPJ3esRaBx!n%9-N-(oY2A}szz@@WSgS?}@i?W8iQ zY!J-cmDOV6;Re<(+Ffn?DEOs{t_Kw3BI6gDSovrVj6ej`_2xn(9!lnIdzb5pa$)&D z7>N4~PPOX?YV(bC`@oiRqHyh~9M;S#fCL|v6ERfbpZ@N##m$EAQ9>b8oJPf#Q`t#8Hjd^_BH75v%bt~G z#dj%~>?9Y#1p!*lM;U4}x?#(h7`?zurS+cLk=Cqq-;~=QJ z-VX%KV2`w%m|X>FIZ`|N>G-3&{Hj5!LYZv^E^ehywvJ%J7AWI46CHt5aGX_2aebfU z&d%+VZSS*Lv+v)B;vjnK*@~2x$s57S1(t~gUSg|AHc@Ny`IN!`#fomC!SaBK>X)^F8SK_05cKmLG4}DU3>gnr*z$pLySfa%l4K z##E7Cf2P4!lYgHy8<>rqty3>OHl0IO>`O`jsdz^)=pV=5JUBF5j{Lb0y>sp3wMI#) zvg*3j$?egn_fC@^o>TvX3`wPK^-yc(-!A5>$2GaBrbA8fk+{gkiZxx1oVO04`RbhXr>N5-HWoZR0gW*F!SmkjQfJ_-`RE^9;c8 zsQJ6ncNjVfbFC1w#vJAkvg%xX){U2BpgM2QGLplihU|Dk%w70P=O4-KUh*1FF*6-h zD(a5s6)6CN)@|7sUrNAAs15}VO@U`z9l@U_bh3-y1@H)w;fE&2h5iL)tT$`3;e}*3 zQT7oNwk{cGsnn5D4a8K|nem9n27wqF=Hd`2tgDn2pU{MjB&RN|ssgirJBn_2GrkGm zC*Xx^zOQHn&`rV!ACroPU0A%jKf=}X>ci|EVdDCVH#*F;fSCsMO(Y3ePGVh`RXo=$ z_Syedax2s1&?U9QjVrQ7kyecColiQL&pyV`U3{X|I|(f}P8eaj_fdpJs0<9VMyp=C zbcZfzr!KHmRDw2$yT(Os+>8s9ID-+{(q%3EQd$7PE%K}PNzC(M_VKygO{GJFN|g2j zKeqkUDh$8Z6K4NkV9>FrCY}hvdc+U>es7H*L(h01KXeWk9a}x9ydZ12{0UU_5E`~~ zC3>>qln$LVntAr?Y2_~JyhbB9v6#;|J=h{IM8gV_{ls1SrA~K8X4dft6Z@<1aqSMI z9;2pRui`{#i=dkqa&Dz0u*|4w!s{V%-OpP8t1F2E5ggiRAS%*Vd+(*RkScB6G8ES2 z)*4j$8AfFN zvIAcBDN}ht%KcgpJp6QSEB19>VUQ4a4G=vgJaRJ7SDd0W>1p-sT3J>5{$N+Van@ut4d z+|FTzOOlKzrEvXvz&yJ$j9Pa&mqD9n3Q=HvtDR%f7Qm8GAfV zTAdy3_Ol;s^X21Q|=}n)>cjlx%Rqe5&-vJkqFB%BE5oCJ2~py>HoEk!6sux&$uGu9zPy(QzOn}g=bLrlQLoSMDN5jVOuK0IUq+l2x@rn zu;hL+Hx2z$f*E%Gunh5$Xw46?>EDc4X^Lb-r+ahj2Vap9GeN*{Ys_*muRKY_>pmiyEH z55dHL495TM`?-IWDg9RoQ?1`Vs&2hK#c!UK?U7{XKT+uq!!+mj`u-^C&r0Vp5r5u_ zpl|VHk)&EzY!-bM08#Va+Q(WlUuakcjY9^iMH~$)iQfngZWGpEE97=>Os-bwZ8YDW z@T;hvdYD^)t0pCYo;508k@`Q2;@u)HpR(qE6+_OL zo{Q}&js5v;O&pil2jLq>!_6Jg(5pBUT4E90tQYpNNQ%~Iudl6L+^w3>?Q0^T`253} zk$z2*Y3VmB01_-vMdlCKwr-A_thBA27FtP5+Ne}8?GRNj-te&Hb9$Uwf&d9go3LEE zm*$$gP~YnNs@4_M`cLPBKN&YfqF`6Df3kb%YvLYFe~0*8%v&@g+I^q24(i0+rrJpg zVQN`3Y{8IY!vQTj@;}q%za`jA3Dp)$>wOO`s4@RyTP0#DBz<67l$9kwu_0SgXb#C0}S@k-7^wwjDem107gz?w`j=` zX-w6sCHhXiC>1-Y?|iG?eRNSvKGG@Jaz3)*=tiWjzDZOBF>H=MWmVS}AzHs$hd>Bz zFM&sc7n7%_dv828H8Q&VF8oMA*s^G{&qbKQFR$KUM?l%`hlD>!z@%2CZ3y9HKk-33 zA0vY`$Cr{J+2V3fMa~X-(#YC(tc0kOm`zLL$_BRx>cQ3wKNNVKxx!+4`3GPYThn= zVE%E7d-}FTx@sornc9(l;-kCY(bH6udul7NlOza*KR_O)o2kg?d8|$1DTeM)A2_VcKPk zGZ1LScD3L0FL;)&E4d~n-Wh#YiW0qRD<7#lp3h#@j_9*=W^bwGrh{u}HWG>3HeQmf zZ86arRZcE}vf_PHe2J2o#}k2F>vxAfm3S)cNUZbb#ie!$OHk!6`z&+B(s)~0v*X#tCicCVxFT4*%LNb5JX3&NgoLgXf?3RqUV zecM*fRyk0W`waff-N2l}#=`Dx{{8%g(zTD0{g#b=&ea}mOT1F~Ncs#sl>lF_@}6I^ zo0nI={%+5Dls)`w9XE9QB}dUD-HaP$Sku|WeRW+NRIj%o=&MLSALQ&X3Vi@|$jNsj zk5MEjQOEo?yd9^d8a*gf)xE%UROXcEn);je^l>--sB98hdQet)+cm-2CAyUgpWx!% ziMtpxe&GEu`^&d@hI?sjCU0bNrDGNGA7(Xb#x?_HAaymtZbx)MUlq;*Cj^PhhPUou zzSOVeBjcL3=CJhMMT^rj*-uH|DtdNoJdQ}toR~71dB5n?nAyT5XHN#CYi_%j=f}C4 z+ia733$gJMa=Wvzec7+s+`cirJ=16M2ZPi#nRyKX2gJx<<|M7bZWO(l$D4~Q2f{d7CjevFIR)B=B6qvL^wwd57sYx*&F<%d%|YKf%V zIy2xshM_VVbO=39Bcf2kgx|~HzE7HXDb8QDtH+&TK|kJXt#Qpvk^VKmklnbZQ_bnL+$?HF zBH5kSLIFXJ#J0;uS5DE$*pFQ{mklh4@1w8^^K7xH%k<0iVK@fGxAvQbuk{+%?N!w= zwhf1fjP}g};E*otH3=9g8oI-UVX$G*r=rQ^GP|Vhf>i7uvhAh#K;Ndh3vhWzI^-5! zd$BwyBu`kWmg1p%92rHnj^ISnY-%xXebkx;tjav{h^q{jgx zGK>cB!hnvIL{|5@m0;UnRQ7TXgT(-mk?v1y3qQ97v%B(ZiH{1k6`$*^Ifn6lSTgz1 zp;>AqUezepUh2n??1I$<*IDbr8IsRrLh_%$m&rLjLmop4DUiYvZ@#byB*h`l+@6We z5raM(VWIM0)<9TJO@?d;es-sp{-yd{j?5-@&|txcL+G>0#xwiV51%+ke|8_f9ma$~ zIl#YRkJ(E&Y`w!r_F;n`=s;p7#D!VeZe>g#Qkq=QOx}jyY=ifd88sC zd!|NbG1bcKfFJDz{jMmJ0hX+wYc7b9P44Lf<}MLxETGQa7Emj~xY>hxdo7?8r8gUD z)S=-o>P|2*byk5${(1;M6U=kx&9bAk*%(RaDNGxvjG_($5;VeQ@7FmDSu$^QLC0$n zw5*aI9P6HOPf|hGyGci?r9WRv=hT`$RatN|P~&q5y6d{TQ+aL1Xum1tH2rf6NubzM z-tU5|`sTLBJzsdTvzHu_Z-A2w4O- zRxiz>8AZ$!l|DU&!X7?pT8>n%8wtYBbxHlapfK{ju7g`mv3J9g$--QddcG#c&%D66h{2|JIx;*_~su2L`ETAn#5NfAmBLog8up)+2Wda-Ud~B zEgwWIK2Vl-QL^O=x<1pxoN*)iK`)F#BaXbpc4<{E1pdbU6ac#(shpeel)x^zUcM>Jg%S9ZThc0=>{C>DLyay$|DlveSV24{=! z-`Re$Xs2n6mn4BYJ&tDx1I^d;njF-otrFeXmFtk!nei;0fz2GCH^rV;UVnu^Nzb+1`UF?v{`~GE2 zZk`5KchNO2@bb(1#JP_S-gjza_M+BJJpH38qxv`yHZQ7Xei~C8w5aALq?J8Me&M`E zI)69`R$HB-FeTXFFYqrxnQGg~aF!p#Gm4EmQ&Jd5Y$tMCqE9-)u$>n8kUaQLRb%Tkynj#TvCUN z3h|FX^xGey;hZxqGKJ`PP^0tReuhgq$w;fGQg?H!s4xc|twb!*vQ6b?NX8mD zM6T|2fY@W5*jxWH7KEwde)n8NHA9+<7^gcgp8C2{#ueDFZ5tI7M!ndzLqrfnvQ=`6 zV*oci35&VuOtMUqXG{q!$2^ z5;KC+k@k{=zXi|l)?sXnF$QxQ4sTgbZ+v0dK683^q|hDrkc`TMO?AxwOEU}OeoKo6!M6lxXL6q?SEr4N>uu7|m{94} z|L>Rc@qSqpM;vlrQnv}Z9?-?Ubm z;Tm(gqN0xa_*<~rRj01n=$sA>G%v4hlMa^1FiS70_J_^dU%&lVm$g3}+{B)Ea%2}UsZAC_)co|8pRz&{lXzr8p)hkN%?K<&mtX%Ihua~)pw9LeI#!|$#c z2N(Y2!$wVIJ$ig6^YUlY)`UCOvrz886d?AO0$yJJRGT{uJW8hw)Mc%=Ea}+HfC;xas*ic1bxrH$QLjN0BzKW!_ zuq2gpbU@s;3csFp&BDl1T;g{c-2-7urQo8`OW$vP>{ln3yzW+mVA$ZfIdkBIdb?%M8K-|E1)ngpW2J)?r>+_!+ zXj3lJZ#KyRfIlEK@nk2m<7J>o2^t8=5>OnFi$i=RVF(=-cM)rO** z{h;oZ+_(nq?&7a4HIrc_WH_*x4Xq5XEFV`uCYRKUb*}=TISg2;Xft-fo^I#yxDE7p z?*0aWJ!N>ywIoHPb4S45%S$A!DKDmRNr-5uqKod-tlXd6hTrAGJmB`pHCnzC2yMW+dH96u%)23ZnN9?sMQz_`Z|2E@Q8b$Al8Tsaq14Iu ztG<|(N>A+N6^6%ifavjoaH3^2s+Nriq0c$f8yE_5l&$bm8oN%ZjG*S}Fg}|xcfX8| zf8%(|_aQtH%(tZrn7`7IF14#kLCWv9YU|Cqj4vHp-}@?r>7)33ozl+N#o@;cK=sV} zz;=t=YtXptSv$C1tUZ9|G{-A1M*pEbCi?5U;eID+4ozGB8xjxE+_Y?ogO(f;?sfpl zC5h?q0k@ZCC0BJXF(F$>a8oDpNy(5x^={@wnsQ>~BCiIDE{kkp0^2+^6QZn|AQ(+* z*`jqFq^WWYORO0$ubs;I`zd|0=ym4P*TjfW^2>$cRa@cu_QmiN3x)^KRR+lXz>y?C z++mCjIPc8-*8XWJM^ArceM^XiH4lGS{6MjTNR2aWH?|JKaLXRzn!r12H&mrGl)FZq zO7d@C4#*-T*D^jy5B723<&)WpCj79jO=ziA%4K_i_n;>caL3x~tT`JH_2_xDsO0G9 z`zhUJu2U+5J54`bS@;X$M)B^2%a|)GGU*Hw;C^!B(G*6nVs46e28f=?^K0@FQ!Waq zq>7`lj24X8wlb!d+s^_`G`&nYzKl907>A`5;L_;;mf_e|AuZ`RZ{PBlBT&oqC#){e@1cVkRgbVV4EwD%gKC)J&8hM`%@VL!HQ8A8&4c|3yEIjI z7kk90ck-y>)jRA0+0e+S(lr~`?|o{c)lgjUV*IwMrBVL3yr`|{g^I7qlRp&vr(|-X z@83=~^E5t-Us$@&Q$JN5*0xUjg_YY*@%q4ke_ZlfyQ|1%hWy(LzvXpfgc2AOtzN{K zSQvPchLmtYm){g*1S|9ZPD7?53cf4b8!)Dh0Y>TcLi5H{2E_X!{eiiUZ}zP=L^>ET zYzeAdYQULEk%9!50828di#w1VUv_S^%n&j0SGvVbgEWFB`)#FT4&jZMH(b&WeayGq z6-L<7)L566;c*%$YW9^+yY)Q_Tx!>~KjM?ct~{@)0%g9zwpfdK)sy}$TMl1hGhbS) zmraKi*U~-C*F?fSroQ?69aVBNRTTl0xhhRXwCp0>ypziA4ABaGB1?OtG>{*%2B!Tm zh}1yAPJ)YSJq`_!+FyWUuYItewO2m6_s;jZ1x?^GBvwKZzN9j{t@5g2th4|hb~|eh zpCH;!B`5%RzTSO)zQsQ}zp$tOrs3PkTjYMcr0@li}l=w})8R@Gk(;^voN)%15k^6n_P|sp32kvsuhh{^!frYR-3@ufJj>wKQ}$F~xNAvaA^A3pB`d+p=PX z|Ar!!IRi{_o(->`>&Ntepr7`we8k$HFsAv8-nFCUbFfqh^wCT;{;?%VlOAG;U0P8< zZ|}EuZ0xk910E?%5Nstj&%afD@P)2Gz}Y#~{>vip8_Ym2Qik0rJ(@}YUxtS_P*xQO z49>C%t>6Yjcd2=k(Nl_lNoY4*+^N>AnaOy)43MStKJ|(!14gYOsgI3r<3L0pO@bwz7e1 zkFJka+jyktHHk_rs0I%Rv-eV63Dzwh6|Y&g>#p+($#{Q&CQJYYqFr-qK_LvrDCebG zGqh+1!jb|rUVr*^R!F1N_wh32-trgt%Nf*}GpsNRu&u%ijzU(<&3)oG|Mfx5thINx zsk6XCTUN5jk5|=)aZO)DS<~)kSY^cNmJgfDnr?ke|DEeC zD}Cctx}gszB)OUqpRZ&#m34iV6cXAB2x`taZyhPpOKjXm5b#+>X>ZaY+)j_0e-)rH z6FC$x*l@F7PLki1KTkn)ZDqepaB7Eh;i*=owl@kja^-TGC|Igrt}Z0rHEW@Z2WBKk zBV214o1au4yDUXqQ?&J9nz|d@3eLXS>)iD_%>s`;P%EcUWrNkt=F_(+bSTNHt-7jk zTo2_6HA(Pw@2-zoeoSzz1g-*6`a~kDYbiCseWB+3!1-zxqpzbFktiHj=Cas$Cq7wh z2u)GQmIi9!bcG)$wIyAje8ETD4TNm(@N5(hJ$C6aIWlfcV|u0Q?DE?~UT~yny(pBS zwv>QRWR+V4<{LX1tZ7h&Ft~tw97*TBFWI#ldk<#n@lL@!!l8-46FtVrr#mTCtWR(U zkR=Nb@Xa655Yh4SUJ1bV9pfqE8773wKrn?V&AY=3pci_|ZM*VpAmlY!NNfXeyVMv` zbb&&1{{QyL8o~wr)^6D*^tC-gdBZ&g1=sotP@RcnTh4U&G|-E|h6*8$+==|a zes@*FSc7=k0_!yY)%x0a39UG5_x^RttaMN##FEirp7y{Wp9rc}rNS;fCF;(Q@eF$5 zJv{|J3i?Ii?NyCEof5S2410IwIxN4J&Uu``NtiJ>C6?(HE?76=^(pcG3DRx_v* zEJuT^o8O)Ox=b)m5=tpB3G4%zB?w_R#L_G@R(b?Ez*37~yf5ws2c7$N1WYjD@dQa3 zGQshTwy#M1dOb^J$A0=|PrgQxHki}Cpf}3kUOpn8W-_rC+LH$J6NJTQ241$RI6ul! zEHBWfqcikX@+R?>^O+Ap1ks%+(bYEc>1f~fEhrxd^*HoM<)4j8pt=T;*Z^w!C>;D~ zNx`7fmbE5zO5jcAyIW#mNsqL{B}N@O9i&Gg8^Z7ppqsy(Hg^JBC7n_&e2_n)tzY_{ zd!cFU|5hhH)>gsDJ$8#xuWpUqYqof-DAge)vGj7iLp47%hn{aJ838d8l4f-cv~ojf zX;wVHZgc2QhTx;7biRO+35JDJN8ZsI&p`bq_RJ7aZJ8e5`|w#5DnSwo=}@g=s*EQk z)+aU|ys?RJoHig?bdV$O`I(E-Cj5$O)9aHc!g&G;2p%I$FH#p3-z6nZ;$K2XwNRTa znH2R!h3mp{aADWXN>RQ#LPz-LIs=&lGyob`Q|t-Xi|Xk)yB>vc;2~RVBvrNX*^+L= zIN&@j14oCh*no|!^C!&5ZZrx>z8UtpChHe{=cdSLyYu$_CjExM)m@73PAE1Ky~4?z zkw5&k&rU?>KvpCHv}z31`y}>IRkmabcsx!)){N9F`BT{V8Y^?~&Yav_N>Ae{So+Wz zTRgCigyF;UT(n|2h@v7$@AfhgWu1=p6^{A1 zD>p`IkF|#MLXDBqmq;ggTpGs7=+$Yekt?X?j+3QfiE#L+M(Gg-@Gj5JLM5M?=9Q-Z zo)0#Ovf#^`Y`m9n8Gs>@^vEEA+dq3uyR^`z3#RMOUK*L0f1P2fjptQ#0F;uguIJW5 zo)JR7m9f&ZO}(3ST2dYgp%oLqeHc9yQ>MpA{+%Y}s)oEH!-%y(aOBN*dCPF`qUZI+fdMlPtYbL!(mFu3{aVBIiBwh(80eef|9xV%6F)uWg4v z+0Q5|T!?=QM>W5Vr2w zs1nWRl@silKO3)9e}eIkDkJa>Ss3XaH-O8c>M!%&WK_FGwkuBk*d*USjUixWsisNK z%wl5Q%NQnZOSAe0ad_ncyce+_V|Zr>qIpmCi7?UKW{{6$8s+@8 zY=SZuW59#z_|#ZBF(190bX{KK2LcaO2VoJ0td;j>J%yO}v@ocg9%+?Dxh&7_oN3c% zQD{j_Z3M!|PMlaP%R8@ElEEeXxmO*#s~38!EWv=AZB8Q#LVpK|0~oaXpGzsc=?Q^d zjB$3?uT?nCF0;y%xHn5@C1axe0K=Jb?r|xuSpb;l7GDTGl237R=6*TD27+bZr-N89O`Rwj8xSj>CyS z$o-t;WvLA2$*97X-C5zWG^3Zf#}Ov#Pv}LQl4Kesm-683x&|O?$6}CT1wtbXpCw>a z$%+fFqq)1Br1o$JLd%gCEjU4E4Sr|W)#k}Bseo{6%bk&;CN^X_JvlKhSW)HQ5*T>p zFfynb)+WT8@PrN&Yh4&RWWnO8sVmZb3N>L&CCf7=Lf9?}WC23P6^bu&5XSTyn+J+O zV5?Fas4+r&;<+T$DBh9pn??K!RD;b0nQSX*fNF-$nEuN3vF_g#v9;i}L}X*KhPSaLA5ah3(G zr%yAwUQBQy4?6%ND&{Ot2d8wu_A6eO@hlP2(J|d;Bubh;Z;bz4i4{=gJUa*WWF;6#f4r+z=tivO9jf5=(Jm>m7xj9^Hr255-k367?f&Ph*NbyiKOUw9;@?tq z#w)lR^t@KWpEc_A`y2hdWii#O`y(e>_uSbls_Jdn89oBMw;rT*Hs|BflKApC{B8cl zbeeNdSLyx>Brd9h2kA(3?{eOVyz+*uXf1=9QKMOt$pNE#@d5xND*5dpj~#RN5DT5x z7e6@zvT);0>TZ0*U$|a{asTsH{+_40G$ryF zUJ-r93*8P3cWzYOPnx;)`Z5!wnAA;hnIF7nl^<(td(x4-CxX%oqca*}k18B}LW(p7 zg^nkuo{SqGrpIxtKWj36q;G&Nk#Gh)OX7mGp->uV}aI_O>>DU zhJD5cF1H@L)oRNy*69pGcWjm|JA8hpVI6SYTD;%2FIXv-rI!*j14Sv=+eqH+PxffA zF~ZIZ0bggRYt^{r>}sIkY>?tFhqwrSt5{nLu&=ViCG6p3U1_I7Vb9}wsX7X`)r#{0 zYiUiUjW@_y0=Da|6EP*CkukKZ#*09mOvY{=P~5Lw>=%Cg>87b;-2NKUY@v^oL=?fA zme=Go(xzX^tmYp^qJhOV-;mN^<+f4}#hqVkD&v{Ds#;RZGJzol|lel>(6mCT| zF47q&GG+UGIj@SOS(dW42Nsh6+U;$mg>XtlIBWRB4P!GVCi_xsH4J6;XMuuPsNuEH zX!NIedL%*YXL$qU_SkbfqM=UVNM))jUt7)6>7vC=^CK~tH`=s~50Kiv>JsK=5=Iy= zNY7xdkZz{>L#^jXK@X5&q0iqXh;PJD`s48D#Gk(?%9gFgYx zXj!ptddrIQ=L|Z&zH|XRZiNUiejG@D_xt$NDg$3dvxzw8biYRf9LeZSgzE0dX>vzR zdd#_wB5~BKojk+&OJ&0hCiLE#aDopymB0m5SV@i@zgvkPe*$4NL`QdKh8amzfqM0@d2`u>#MH!^S_$Za zINO7n0-QFbmUTN77O`TYiKD~wE7bTVeY3VZ{Bd_oqzusljpP}Eqca@sX5Vz>J10w^`tddFJ(elD z9>zFmy0!~eS|mK`X=IMULkEUfcGTA8Zn1e{5ZBVtOucoUb5Wzm(w(UKNj6w&lEC|q z(2RZgBJiAqD0oKkXRzN@;>bH8Hb`pEc#vY=fa9;|>od^}BLfQbi+w(Y-CvEKwAB5{UBIfF{i&;a_ag^L(x@uZ?4@7 z&KCo{$|iF;(;oWM3o9Y zz6#&kLmzFPHt&&wKo|kgPtQKJ6kDYnHg;qkJX#~y)#2%A>fM@dJ#kK1VU>+1#hT}n z$~@(j9gFXUHny|^AA3+=d%~Ebx$jFEuu-!Mm1-g-!eSsNl&Lvl!e;tjuUbvWRVZ=*#98s5tM-i6B6(;Gpx za<8&1XP0yfB&BeXVLMud56Hz#9;_{CY?v3UPzRmpyUR6O&3;gIs3nbAb8zYm448rpaO}HVZ8>2 z2twjQ?#av#6;ITI09HB9!QW{dey8zU^*C1ivF>6 z75!R5X9xn_b@SwV?qF_8Lg(HrL~L?F3Qlr!tWF+(B$vO$LS8M(-p)z6Jl0?N6jw%N zi0sK^!T7?lTpmTw-RV*<_eC6fmI9%#s-B0xF<_f&7MHo7AxX#( zAO(Pey?E)x1#49<(@iM)hrv{ZFjI`h%_81YoL%GlczmkdzQy$DNN$yccfHgeE7zLZ z$8)dV3SMW<&W^FZrRRZQn|iRfG5J&^%RjXJjW(SS(bz1jrQ=g1skJJM6}NBlZp+4- z=$l_Z+!}54%lHWN^}v=0k<+$uxAFZ0J0^QZQc)w;{}2{!6*$44YYFe zr8pY2iJ8dw{}F+aX)a4YfaU2+Bd65#uPI5ESRT+^@x0Kt2-%6x^mR$B-^=4FuSQn& z-t2qX;K-556meEPFdM#j2;oqAyX6y8`a6x`?IdP^kT6fb7V+C)(=Kv-Fo)lVhDMnB z1o+=ruAJoHMxB(8V6FkI@l@!M&7&&^<&aZ20%5{0_tASgV0L%^9u}(&yRjQ{y4GeVO7{}N*#8}D_%-4<@MP^xkxnJ)l zl!(|t6!K{q;;<-3PLu{d{&OSOhhZH~W+}RIwoVVB9fe4?X1YpYw1IIWC|IhzYPVKL zKz_)>Yko<$*wUo)f3f%8aZPS%yD*BPA|f5>0tr1Rp;wijgktCrigXBsUZh#+oe%?lK9a3)tc^n0?DAe>f3CN@`?&k zqz<0+QlQDG z$^$07>2m|rb>bimW|e?F-ni@&b79u373y=AFvJ;PA`#rCMADzp?ZEt3!zW zLV^um;3*kc<1+%veSj&Y0nH;q1F{@)k3u{J#@W%Q?$4aJ%=Lq<4N@j1m4vm#ZM+Yi zf!(^G%3&jjI}L)DZu40a{ZAwVs>%p27r|)wl2nTVQ}mrJy>Tk;i7D0fk|7N{78r^< z#?Hx*oCdu>6wo8m=*$W<);*hrmBw#BM`p55^yiXhS~kFOeaCc35epziKqiUKouB<$#h=(~uK_FEyblWQ`%Mqf=0&LX!`V-NRf28i^uk(HJmo~g?a4*ns4Yr}3s z=J)hB*Ha8Ef7b?XCt%G&hz`w>(tw3FIIK8e@ca%i7&Og%j+pv-!|z(G z$P3pbOwT448s058wW3thRo`vLgy}8Ic!u7? zN&{>8HOFmjCySaIJd0)Co8aaK%WPLvl~BkG=X)APw(8+_^}CjHIb8U$D6 z#8(-!v8dW41_P-&H zV%VB70Yjk2HOEipiYY;079Pf?er|DzO(tp|#-_kCiKZn36zomeW5POjUR4f^HP#26 zKc6S5WX4`s)Eh_XDQ{+Gb*Hg12-PJn_7sL<=o5v55KOnX79;G19UnNNypv?HtHa9` z`?&1<96dvp=!i@1E*=eKX_XAwikR#rwdusRSyN07@G!RRI;te4Ob6k#mJR- z0_8b7E@fD7zoVnJT#g~_`x2P=G42Zunu{~wnk{?Xnz%okVdk7QvavNI`39pNX_W(7 zyXnCiQp$!)6r$^(K+7$mlcNZW@^;DjGItbS^{oB9i%WR{)vkLp{#p%=(QCuwd7-l? zCwMU(UOyMt7eBNu^&L710nhbcPz?Ws$TKv%hhw=%Aqv`*H;Wjt)Zxh?kFv+rDJUB{ zR~c}P(%lb5yN?^w!gwPBC$Hq5k4d+T z9N-`ic~_zkw&0jNFxJV3PG*LF#z1D|1jO^e_t8WqqX0V1D;;FFJTtbw$b5r69$iTp zJGxW1^ZSqqM5jZPvO+hEhpcr}19= ziG-{rgdJq6;RDKWRH@JimJ8_%s`Bm-5Oe|We{go>&2SCjpR;9--o|;4Uf>pe^vmb_vQG8ET^W#Sh%SHORU{;s-gyFiHUDKM{A*xA#00%5t3kI zq-7DUl+)v0y95nNkw9kAzpEU551d>?2$enH;ZQ{9^;^4^kE^OuJ~`I%y)@85(&w+d zRb?U_w;~;aRh|Id2&}MPfDoCodC$!Rxo}%UGS?kc=h=-jJ2J}s)|Kwwa~c}k`0gM9X=jPLi)rEe{3tvWP^*QfbWg>NwZ$7<)%e=_N=$;} z8gLlqY>=b7`c_a&cH)^$ygP(-J_)I!KufXo_|bDgG|=Y zykbKJ#Lqq_Fx1S31f{P??dw}h)VVJ;knK*{FtwZa#G+VvMm0a+?S~}kN{$y;IP7b+ z)p@xm)GF=IrHOlp?fPw5DaN}hRA~!3Xg*6@Kq|Oe_19JHUUk2G=3OPN(!Dbw@}r@( zbugYSO@dUwV4XV+nzlyzsZP;C0w?NO!=XVxG*Xi#QZl0JemsULL1+N2AmXpDTJh3m z%}c7qMa7czW>{!XluCS!1nnr^Lz+@GtsiRcd^&e7M~7ue)Geu^Heg7bR_!{DW%^>V zofcd;HSACT+1O;ud@|zXoSwyYz9W$JhEzK_9Cu&i6H1mTm64i}dd!1-qQ>7zb6S;BkIZrGpZNLBR zwQEUTn&hf!iV38bBDBG$RyQz-P+lfS+AfBk*LeL{1Iq+cTjv9T?HfymDI(Oo{DE^7%C8qmto`lF z>Wo{T-8lPV?ARfLXZJHe+iT5vZ9VB7b%baV^}1$viiz31$)rMI3C$8^bvU!HSTve+Qn$s1$Mr2= zsziZrQ|drRK-i>A9BBxHldEcR?=<_?+5jKAv!|0t%c~L+emgQ*L2) z-o2RwHcV05*XKl);GTDWI``TUax~MRpbfT8;M^9}%YjeafeVD51me5J6V=8gwpNwEN1*E(KbLos*a|}1$+1B7=^yO6@ zx`}b@^C;?Z!&liMy?cu}$!eO|S{Zw53dx=N9AX^7CUG^g#%cjm=P%+oCvV?OD@`zt zl~=Iib;Q>!9IMup1kW>ghzqk2*Jqe*8kycsAfWedmf}dS0z40AzX^X5qvrk4vP0Fut+t@g)83mR4h!EON8_i zEG>dxd{|%KgLqyY<=03jhwSqZzkQ7-*DEq3kBQz^LPayKdi)7NIosT1%ro7`1|6II zlwx)rsw4o`ncU#^djAp%2H%)(jc20JC|zv0FBtTGWZiq(l%UiIjxQ873C%Nek>s_# zo}S*v*i;AxjhiBvVe$5-k?*3NRbOZD0!G}HW)qbw=MOyqRdXqe)excbmPl6;>6sRf zVGq!qv2{m2TuFQBNAh7t55Hm9fe@Swm~Ez%Fy8AyRd};LzFG#)FLYvhu9Y_VA>JlM z%F3Ie8@5stXKpoRtR=ARE9W|0b5^7erjxd{3a9&W&gJ^+x}Napq{?t~P_pO8s_l1L zk@4m9!iM68vS^r4U*rVN9=2Lbzg)p7lrQRWCfPBxWp+a*w3+n!*!^nZL_<~@rn>Fm ze8T)At?Oewt~P~DQd>Zxh-etvafZ+2J#$Wmc%B|rr5_z%h6#aqVUpLcW8_tgfFgH# zOTB&S;_e7RE25}ZeE^u{))->)O;v1(G<9m!{Wb?Uw0tlSYg9F9{D}zUPnlU z^xZc$3w~iVJKi8c9aHBtlkoU#u>_ndpH zBa!ojl2cHZaOb!d%`;L#%@Wo)%&PN+hsTiwU{`&>+1mIHZjQxboFz^N^Cs83+meLP zIZ%ybZa7d?Wv=2ROuDKFR~7Ae+-zz`>tJtl-}Z|eKb(gB^>w;P{|1VhS%~|Q#L-$I z%ov@#=B$}J8q9RX%I>6Ipl9)=$59EYzYs8YPhTeM)^&{&x{GCf#YCl@p;CSWA1Y_v z2{=>U{8|R0>s4VQkbhKk0}wtHIHvJJYTQhITtGut#;0%ZiSGGmo5aoO*sqg7{kF<* zPi_{_bAg{ooL1}6j(9hQRn6j0THWyUaa`F8`x{cJm5)Ab##$l@aqdw_p^HFv1#L~r zg<3sjq(hdiup5Vch8%+9C?12w%A`3E+ zt;`eki8Bz5FgmnB_e=wze}Nl3J-k2KGyfvH?WmBJJ+>Yn_cPN0`kI=yS4^Q$BMQaGXE16pOu;lPwz&+wQ1#{xWo5;uZ*GEx4Dl)ul}m5__K22H==T4uG+_#x1_*a_wCmUPyc^VkmHw8tNuFbV3#%c zR(+7ogV(SAO#jlRI#%AFrTkwmm@!`@Z+NvhZuZ)(Xw||~{*5g4xb3Ugf__oVWSNwF zM1f7{E>rTk^2Y`LF0J_=EBK#onyQ6jRaO*Y-~WI+!R#SVt-G^y9W7MylNkHrCezNt zc(3>k^YDiOu(+ZAtYr!ZbvWmkd+a`jLryZ|>GOU=gLO$|liDJyHSy)|rP4&Ic0c1Uh+fZK|+L5&ki zKGT9h-$sf-Z${w?f+mlm7T@hnU>d9g)~?C2)!@t(KVNZ?NRkLtjj08)wjxG}RKFC( zbuLpdtA?N$t(qv`^U;qc*sx1_lFlv_obU*3+61#Z*D+%cZ4~I;5}5lIg=Mjh&L<6w zmeH@9MMpldl@#Pg#IB_Rfh`mSY4`Wf1(*gA*c<$t9o4dI?lF{&HdS|SLLkps&&AR( zs53k1UIunmP6-S1Os~-C7H~I#Au=Au*d0kW?~ly)vG3`~YQbW;_2~!NULV$d7I=Bb ztTkq5m#QadY0=PbdwJLRQgLzrRQvQ~j?5Io+96e5FCY&hht#>>baA5E+F;nix+WRT zSx4W0F}fFVrOyS*BK{2H2rEf_-hurVrEL{ueM4%3mU%p_0Ro|KE`z1?ibB&*o4`Hp zbI5v^DXF5W@VpHr_sIp_t2{8u1m2k3RQ}fU3C!V1v%>AbesNui7($t&D$^6C>L8qy z;n1{pv);9m@|_5Z`k4=Eet~TgcZrv^djp*oG(S+6(gHWB?$;7tjuB+G^wi30qcr-O z-rd_x_fR^w4B)BZ6Zf_F(-|nn7Wax;xEHHCi>KjGr%)A#+k|o|?%Gw@a*B{=GHIUZ1{K@+zlpz~w_h?WIOZ3%$NWoXvjsYHma_?U}_zdm00qOnTm(u=6oTv3Ktd&Qp6!C+gbX~=6DtE|h3%(l6Fmo>sD z!G2dO3D^2{XtbW2szQi5D}oB4+rcHK<&{NsFIQGZuTQjBJL4+13%#$xvZx(JSL@mw z{cBck+@f_tslCk6p*`ZQlpt@IDI3r)udu79XZOb$l*Jq(V`tJr5o#Am%OpbZd~TQ| z_v^i#E-DqrjE>|1W*iRN1owKQMc1T}z7AR$5HQ#KeWJOSSvnc0yJC_9N(2d6Ay`JrVG+F}!STg|h zm0uBWfxlQ4igPX@&DY{EezG#tWqb#Iw&ApaqCJ7>Xu9%bU&e!$KP;Yj6R*aT3M14I z#RcY`Zox8xbefux#QQ+~U?FoY5&e0dM5Fp4E;Dx!9%J99dzC4H<>J9>2hSRF3y0u- zZ=NxMF*CrV=>^WJXOELit;rt{irTyz;!A8*6ldaHDr@lSsEGvrFqU0-Dy~**Y^Jte zBD{W9fsr$*Tz2T%+uQm?q-V7)SU2FY@J&L-T{7*Z zylQn&TSay)OzziVLCMA@bH-pDO~8B@8asKA1`!HJD`RMob!MOseL=cO`FmINZew#_@} zop-ykWG#Y zU@Z?*H6s(M)eu3#bJZH)IdhxmvP=Z69=OkhP@M>Lw!vUE?pU?h0zr*w>HoGohw7vz z3VlS#A9L-z@d%UN@u4`nOxGu8Q86R_DZH`oKo*fUGjXi-rnw+`$V!7Acz50*ryKP| zVm#xEi6IjcKG0EtT4)d5Uh(WH1a0RhzFqFRWE1o{^Xr51)s(W@ZB3ksEyeSUSqL)= ziY;Aj8qXXj{j^9XiRBL0U=O&!9&5A(%47a^WD?p-&0Q5g5T2hBX_JdfU=YFQ7N;u+ zlUj&81Kk@7G7dJehfe!}e{ zAeavvXno&4v%}@!WndkhX&HAb4y)p=>RR8P;g5r>Vc+nt%C;q65ttEt`_>pW8&c3D zIUk6+SwwK243kadGO=G7;cbhYWkB0HV4Yb)E5&fp{e+w_TWW@_`M4_QF5z4x={I(d z`PuXRK1`J9v$JVv{WI9eQ#PBB;#r~?1~8u^60I0hsYcp`gaCmrZ!ovxjF#)!REw+* z6XuKN$WLl3^x0~7f;7UY`WJ{oRph9vm|&bH5Aq(mWHg79z;Ikj4%*6+TOByGITehu z4LbEz4I%`6Q+Z0$jo!BHQ#Kbiu4=Mxv+oKtHANgdD|5NZ$XZMK!Dg5%=d)QM(ri8^ zABBvm$IkQ{*>#hyLv0pkAkni1GqU{(6Eu)PtVvAs&J%XEI|$t{aZBBM4M3EgC&FfH zG&Dvd6`U=8YrxaSIZ>GHbDRft6P*TF#Iy^Gq`zJgzhwc^Cq$j0$NPFG=duNf=QgPc|u%_QRTg)YY5vqW_?! zp@VFs&z1skW?oyxGsD8L`FhKVZ7L1e)qs0vRsBsO0U;OIMgkE`gD~003KPAn1bvT1 z?<#x0Z)zFS>Odb~=~xcIs))hQb=p_bz#W^11JLbDJ;u5<$*(y|u^9HgHJPW1F}xK! z*+3VyJtpdiN7n@Biy{driNgCqT}B;l^X6qdJx;4{Cn*sI^VO?{_R`{$xssfpde<4+ zqXJ5LbcQkJh|t^*sZ^(muZ7GR5=|Xu!K@*(Fh@~aE~QZ>(;j3ZP)AQw2IZU$>(?1| zrH$O3QO%wX#gPg*O@7w_gj&Zt=$U2}LTksPUl}-$4L-}T>w6~3HFPeSrj9uR?;`1Is9p_x@2 ztX+iJWy5jWF*bzHB%P+E$|`_?59wrW=s9@B<=NoAj)MSRmwUbIIUyZG4v7-Xuc0eI z;Hu09!#qy}AD03^5V?HHV>yN_7PwJ0zqr)!dDNY*LD*TZfw|%;=j5EjY-}8eXK{YQ zT`C*C&#wv*+kGplKCFTxCS4{cHV%Iiqth3uMB;Y4t=A)0LbUGEY?e>uCyF~ieY>*8H)^U$@OA}pD~p_H&ene2PRi&qMx;V4 zSd@eO8Fkh;WhE=bN}mnW_$({%O6d4%Qx=fM{6w;kzE=~W6`02FThc^kcJ0crCtO&_ zHcN%VCoQK|-Yk7|P1EKCqLkBXXG=dG85Fyrk|9C^=XVs=78|Wf`I0W+uAId8SxN^9 z>B&+lQSga%0o@SlwHYajoMO!{_0ev>WY&cKR`^2~)p^~bQJm4{~Edy<*R`6)& zOIjhv{0?us`d&nEAu=0)muJ@3*Eg8ZH{KBJ3Qxd#Bq#^7lTvb}L%fp93vvn*jxpzUMH@z&6S*6EoNnN%;=uaVt1oWO;8qB_ zj=sU+=n?sTr>Y($RI)4Oh74)8Q7mFmhfihX3w2!B()|(x#6vUQbLAe3m7Dq5veLa} z;+zdM9K0zAN~uq#@%Svh=DI78w_whXA*AagDa> zMj)bF)gv{Z^}I$yrx0E7)*tt#nxc^m#-j4W!avKv7cjdyw)860NlBEzf?s9k(dd?^{3h=m>W%39`NuVZ*$dGDXUro55y!i z%R@CGZ{JF4PB>@7-|OYS+t#LM>X6IP_oS$8f>UkyKAZF9&&%|KbGP$f)RWSX8HXXk9#DH zP#9X_!p;O1wXS^Ab7Zx=Z6m~Jb8lQJ} z&H;tNArv7XuyQaz0%&-h!YNrQfe8J?>M;l58?B@4w5P`Jy`RXQRQ8KiO|+z>&<9c^ z)CT451gl9B8J6_ki1W67VGjdA^g;>+xVh)5<4!MhkO})YNrOp9ME>VO*6)4eQaJTz zI+=a{VTyhwoCrW z$A9N;3bERYLfe<|<8rEFmwRMh-LAbioK-d^D`{?+Z z!B%m?{A&3E|6F=(;iO+^tJXkbkhROL8&SdaDurq=`$NDtynT6B+d&Ic3{ykPE|SbR zPQAP?!R^Zj_V8Bm=6)0iFU}=fT+)KOlT3>=(GJ2OQm>X!$|h}gUa`BJU!O$LNNyCP z9QqNf{Dp=A0&#Itg&P^!dRz)R4eSBFz}xk}YzX7=Gj#47=8n1!KVJ8IAgPNX*)9WI$^1Vj+d5 z+Ql)%OLNqIz>3t-5`Rg!@f(e1d!c^8u^WIGEJmtSg@?J?76ix?mfCW+$rXc0%$vMc z#xv_A(dcCf=Q)}CMH_FO&y!|&TF-1oL($|=mEFABZ|)iRDUoelCRpvQ&DcvMYEP14 z)aD6j(6hvERj{e}39AX^k;u+E;X>k>o-%zDobWeZaV?j3zLY)DZr{Cd}&oaA+I9n z>0`214;^h)2q#9W+Bq1=vl3;Zqnrt9I;S8~f=dJ+S9)(>>#NvCskXeFjiTXxhnwY+ zl^0p#Uz5va%F=-vyb)@hp9cb3yCk4b2EHt{%C6~TFg0-0t59X|+q?lZd~+BNBrA1g zyl}yuZ0Lk1dCtEI2Enn|0W4@$(YQQ~uEOJ4Dh$cR}w#4)8s zzY%CMbMh7ucvGHCH(wfNp&+-OU5zS#qQh{cda1KfORoA^`22i$U%BiXC>WZAseZ#3caR<67lGJcb~B*H6EN)-TWJYIukjGge5b z%9U)ey91V`;#5}I9*b{!P(e3`ppE%>eY6nbja^)4mXN&sfWu8?@U^oD{DYmy*U$}C zd4(eXNc?Qw3VMiiuzaq#c+s&<7|>s20G zriVO*QBF1!of)kB@R74#<6?6*TLUxFmw-TQ{oJn`5-U;GhDLdncC~{lN}t!A2LwYj zV7m#O)v@hv3$_m8?l{xf0ToUNH-jE2A!zLW!`%Vbj#A zz2!dTI^xBR(@ccB|B8!*Moo90qkvOznqpvZ7H){7f_3L75-^vlBW$D#T}DycCgg1h zZVa>bD2^41h$=>EnicJ^mYdf1$6P7F7HXx1CPTW8x$az#M?$^?TDY6PSgiBHuiyE1ZUd1za=>FPHA6&%UEr>vzxJYDAkJV@0?^!fX*i+5nX&i#|u;vYTw51n)Jv_v)EDXiAg_Sft$QI5y#>F*^4cCu`J3ZH{Te>dzg`Y%aw zqNtPT2r|^Lyf|G3soJOCSRaPp-ln7Pw#R^BQ#%Utxe)Qd*v>4dVJI`A`iN&jea2g>o?q z?dt{^hMq!pVgw^es883H*QP-7m2p{mONdh(-*{alEn%~Y*L$V2 zLF-fPT%8O{Y{TFQkV{{fIMv!_aM3-3hWv)cp=zOySY5xAzBDc}ytER37{LiM_{4f1 z8akhRafKNdn07BZiA|H5A1dG6p_0==&t5vzTt%zV)$W?j>=8XY5h(&QqE!RMSbWjk@>K6C2TJ$ zoPv#h4_CB7q$3dj?}1W&6F0n*}+$h^JSaH$n#>R% zM~p2Syazd_^M*NdWgDX+C~2_SaDo?Ff{WoBhpg4+1!+l>*mYYTRov*1UUyEXqr-*< zrUXr-Nnp*Hi(tu}s>s~3u#Ab#X$UVOq&bJTrB+TlmZp!cczl?3CS`dXH!PlBR4}0- zPI;#|9ODAUpcxyeT^rcHP>JW^JO%}?jY*M7EGQe~-t6>8rtl3)a*`{(gPF?l!@^BQ zE4gNr@zi^&)*E>q0BAd>yda#@Uii=!XjndLnU|ZY?pA+Eh^ba}az1hT(?Sk$qch|g z(lb4>5UhTo`X>_71kM@lB{52clM=1^#z{>laZo-TQdDc*u6}8+(|}Q#;TfF=gg0Oi%L zc>{;HI@A_PW7w~R=wzE|_$C@b0BHXT{tLa+@lS-W)Glc^D;*78)?3W(AFpF|pOdDm zcsBzLujf`neKNPW(9{&^&iY^}@yc#(cS}6!m6<#e5{lcu4YYsb^Z!)T@R_Ga;a@i2 z*>5<#XfJU6eWaczzY(prp}nb8RCa4w;6+u$={^a`nwoMw?0+GXx0aR;vH>^T>#SP>O7D1f%2{ST)1nT?3U!>3L$2-n4?+c zn-t&!$>nZ7LyrjY9kb;tn%N!!qb_DhKybcCnHOQ5?~P%z>a3~#L91d1mvBEoR)BIT z-6PmQaBo=Dg*$8l;CAp6Ny(&4-8VT!Q={mD;xG`i{f5kTLt>UpeCjv}NvZlj8RWHF z>T#`^p$~kTzRHAtxcMM~BS@a&>#px-?<;TbW`FK@;Po9NQ*`*9i||D%tBi`|hxOt= zsrlcdwfQF%|2vQVL;o`Tmy+54o7h(V?`-|IxpDqWR5B6TEN=ZNPyF!r8^XWK)n`K( zySNYYkDgB$X6@5k%yfLa|LAudrT?T_;v?y)Y>vgzmBY%7&rjLtq%Wu+#F&hl`gdQm zP;(x7o7Gfap`0fT;#Bwh$j<^qE`KqLs}BDpU%SJO66U&-+ZKM(r!^4S%yu!~Y5p5I z&{jvc0ORcC@ZEvq7_zP}W+~SyI!K;lnmQG71Bi@Bgz1+Q0PR9FdYgZ{} zOWu`b1b%&JK-?$@q1>IDfF?LPg#-6P&* z{lC`xz3)Z;;CsAY1z-Qb_ZUQf_VmA4ui|gMm#jZ`@kie?`qlTk{t_mQdJoJDomu)z zj+D0F`h-rC(*7I|QA|I_L)g#W`pY*Yp>h3x;G))lkDy<^L5!diZ?p1@KaU_G`@cre z+Jw)m=EgV53auB2C{dAHzfS!9pJVPXyA2>NjpR=)pL_x>K>KX)(Y$L{@VU^JHx2VvHT ztB#H<@9AokGw9Bf-bpWqgH&KsP)`3k!iv7x(97&m{rdZ#V-wB0r^}bP1l~%I;<7hh zYd#`58e%P7qj8#U>R?V&Uj39i-#8Arq_t+gX*d9HExR96T|c}Q7!s~*hE?~`V{4aM zyI5?vkZCuscC|=-Y3(NxM6tK)>HXmP3MRNQrHR<^koS})uYxzHXaUI?(|^W1_xrW# z?+;|3tL%q=-TcPqt-tYA#qikumihJH(aHVuckKU*Pl@Y9WcPcK3-${Q=RZ$rHC+nM z?37XNKj=|-(*qbh&KZ4&al+kZNgGoSr( zTl=FyFsLcy+2Ca{zVLpRPDV$Lqh!Z3{QR?)X1_h({_e1y*W#pw+sd$G$)LNuiSzVm zl7aEV2kUs%+;e#Jej*VXO-eo+Z|4ZeqA^gl3&PKRV@oQSG;z8hJb~~9t}@;DK4$vB zSZw^_HrJ{esyczjN43x3yCKa0#=hbzSxK?wsH6My3WD^YU21MP_Fzz0p<*H5eY`QT zVYV?%J?e_(TY^!EN2w?!_cB)vAnHl9p1vUzT7bsk?vq@-e{%Cr$N7Ki!8w%~n=sou z^P;hLBRhj;)=?d9r@68079ShC4Tucz;jqYmfwNIaRasSOedLBT5mxcpRMB3SvG){O z#VA|7Px6Z*vuwZpd7tdXt30LQhW)5hnv1(Uv>;yjXE{ zghQws>8>V1U|O9R(oVKW@~<)X@}R|@jU0C&PNIUs)UT4q&Z-F0sC5yD zti;?l8GJ>)6CF_8vwUR&&7g4GNo~rq9w4KeJL>;M*?_6FrC8-rHeN6bG#Qba5enwWc5Rp|` zG26^tmoiqE6Jtb^Ee`>`#SfJ0g9~rkDEVFOieI&#L(ou#A#~Vn$C*HYfZT6`(MF^^ ze}Ka>kM!FZ?)n%;L=yQEkwmwM@S(>mRrLjU$K*W}=S!qA){>|Oi_dM~oJd58CBm`W zCgT`9a2C%*M6!iys;^8vZz9{%XD5VBs_jx}|Ju8&zgofdEEkV)+;CsX&6HYJU%l6s zGg?u7#vVESron0!Ovz{KlW6WZCyEMN5cyg&X2iMU%tLor{$@^*_rF&3TXT|k$regs zb+I;5^2%vvn>rJi=A^0wY}?-=72hZcSJ%xd0wiiw;+0pRCRedRE%>G%M{h=itNp>~ zA5q}`06g_q+pvo;P9l^AN!LA1|Iv!KmjQ<`Z z*8Zz0e(Sdx5nB(gq(h>vGBj_OX+A6KyQXd6*DBMSx1{pW`#HeWuD|upiR01%8iD;j zBfWm*vQXG#je&P5y~WJB`}}4VwxX+Im_jJRebBTs7oEhE3h0U8d9d{3JX1fY6*m*3xsW#Q3;CoS+Pms%OY_kF0P8yNxxk^2z-qAq5e z<>eKst*N$vWw1bYlYC6AfrC8M)u$TS7Nz!F$AQ92>SEy)27t>))_U02{wd9#h*YXS z3DFOv4}S5!t2cfJEc_$0!s7_c4+WM%q>Nv6*DdVpxG~?OHdY zhaY|R0WwF2|LA|B7Jux2BtK5cpY`+1kI{VL52KmoCE1VB#PP?`M8fn#i2N?&SbqFl z@@)QxF1E>8e$*b(l{-~-HM-xvm6e_6cND%@(e&uYvHVHFI%!+e!Kd^$w4)wnJlUIXyg}uv*}QDt z7bRw(F_)p5oxBkcu-3-(kAnf<`WeZnH>{ArGRc1PH2+;;W)PQTv2(eHhR>*HO;Ym5 z(RGo5^=>O&7nwJpcGM6ccc4F{O^khQIvcF8_zLi2g*W5=utJx&|6)ND*5ISqpv1?P zTy;?M=FQpFRxG-XBD(h+61!2CMQ%Dw6#O!cqdV84cq z6Ip0cSgJaPQaTU<$Xfwa4GOlC2OntooR9K4EZfe}4oK>cJj#20a8jg$m>~;P>&Tl2 z9Q)wtR9d@!9H>ayzh@EW*j2CM)r1O09u!LW@}x%hT3KK8Sd7H-=h`I@_E4oFbEC6? zI)Q!E@<-RdxF0rgOGV>wle)!nme~6JkDcG5~dX$3VKA}_*fj{fk z)hu+d?Z)P_x`S$GSx^FqC`6PZO>piwo-aInpnJ8lD0V}CR!VoqZfPE0oT|(0NP!M1mPfEz~T@N9XQ#sAjSeC0-zF z{hMl!mk&KP>dd{qH)0!dTX=N#yD{oe3`DC}YYUFUpt-n2MZsL0qc0k+eYmM3Z+j)y zpk~x~ka|}Quy#quL$5`Xe}VpPLs6c0LX(bf7ZDjvFFKs7N?d9D-S6oNQ%f5sR0v7& zN7{)hTYsUmfYcYsbtVQD zB+wBQ-K0^wdwsF(bdvE~6Ll+d{)Esrw@>cIucP}5BKw!9=CuAi)SsP6pI_miLbbYpD52{rvKQXs{Q4WWqXuZB@OV zFXkgp`U=1e^xYmLu%e?|k@=+{YRLMPJI?D#@k?iME=C9nSe^SM>=KQoBC0F52kNU~ zH_t0Yw>2EXulgmmAj)MVU15!be)CFFE>Pc?HzecXcfbuVaVVnUXEWg6{Na2vut`i7 zA=v2K#W}#us7~{)=<(k2Y^kqyc4a*#X1bSjtntEoPjy2vc$KysZpP-P;O+{?kMvWO zxvCxqaK+#yCBH7Z6C4Coe?Wz*#^b`{wfN=ir!xkk!=6G3g?j4mb2?({HT03aEd1HM zfn!civl%^+Nu~;ww;XI9{Q55W&0BB|KVx{?AK*8(qUICu&4i21n@=ySo?!!2DY}4s zi|Ta4ZnTAPq(u9xM*p%br2YG%{7+KUznse!$cAc1u1XtQGMiYpt{P(63*FNEYTQa{ z+){U{3!-CqXH^l`j(%KvQE>mEX<6$3VfFC8wP0L&Ec#?ZSFobT|K=8*%=D@YG``O$ zKF>sW;s!Q}x*)tsyKX=!fTXvOQH{8o2MRW(8nzbA2&*ULf7as4JsI)CgtUuN1l;N* zcnayf5=Eknc~|6=9T#L%nV2|`dQfgi!f`@Eay?Jz)z`iJPLN`231w_bUnznp5`wv& zORz}@2$>CfdY{F?JwrXGQn0FsE7RA@+gT>FHuv@QA_8a2po-~%N@ZZ>a32YYvE1GN zC;w0XD;pU9{r#cBuiKAG;3^6XCG4mWn~-?>K4(lNY`qtl<1KO@op(AKuZoRnTT6_D zCO4{&`sX>7Tsyu6m-xay-k(z6G8C=l%i!O(2+i%Qv2>w{;Mp4N7P<>V34jI<;7lB| znX}qT7A>fTfEm=}Pb3hLrZ}GBeW}16@1Xe;HO((-0YhhpDm(PnX4b~d)OrNP8JnFY z3>eLrXO3kXJW!A&M-5KpG*XknYEV9;4d%ih$Ui@6DD+fhJj`Eletde1jYra(MVt^7 zYiP2edylS?Cf6D`HB{jcUc1gJ4Q7A=nd4RXULzHR+3g+UH*b)-EZk!7@P9{fZgGD*s!{$z;CgX|Jb-Z=m(*|Zy zT^W|Tz)3*OrMUuBFQBCChPAB=pr>v=y5IVZWliL|?-M;Fy&ic$p(vAG>sHZ6yo!lV zPQqCyFmXW(fxEFpU&kmb+auI zyS)#KEhJpjr9wo&GpLS2yE;DP<${XVQ4j`p(ldD z#|9jv>3OTeMCM#W5xe*f8qI|{c!^TO?K6WW8R(^(2O#N^9&x*&I5oSKNgd{%Y&&p{ zsHI&<2OmnkL1}eAp}awhJK|u2pSPj-HAIH2^5%|4lu->Y&CtR9O1BoS&Kvx;iuDhq zVSf2#Y_>BtPqj@Ti2*Dw==f}JyIfrT|7!2MqngUve&dXzgNh0Y4po8BAxP-e5fBIg zlwJZN5K5@hLI+2BhfqTuA)$9dLKBqU5ePLD=>`y`cfFbQe(Rf=_pWs>U%U68_xyL( zI{WP3-sgFKd!J`NMMJI-0BHXR7nSUSW)?ScGz1y(gIcS?_XMHwp?bD^MyZh~qN`S_ zSm!8PGB4GvQYx)AjCIKYa8>LhF$GyEX0KINQ`)PSMX!5LSBAlgix_}y&TZ&TSmC;iaKk%mX^Q6*_MHeXdRYekm7Ey+(bJyR)uU+v3LlFIHSDLmU20{p*YT0Lmp*3qexGa6}g@P=0q_+#td-5t(rT1os?zkLPdVO z-cEkGPQb`q3vCdC+W>j_JRBd6z#f;5C@JRwQ&yFpG@sIVzhY*}?0ZjRTJH3*0qqgL zw0(Au$IG(xmkr?-5$DSXi7Zjj3uFG2FD)7$QcYSD509W_)V*6h|C?7HEq z2n1KAZumxQwR;SgwNeN=m8yW?cnErL2?&%*^^N_YW4T+p&whHh(IyYc_3dF~iOQRD z|JG~Xod_P=zH*sPxN3N{u_>oiqy5KO8GQNr^Q^^b((t))M4H7fTe{lVz6J}^B-=05 zsYP7isfrk-T!i*r#|J}|<9)=FWyfZwAD2bxHIJJ(V6#f)i9c+_=1DEP9J0ZsAi? za^V9a*=qIh>_@+Fu^W{U&UPopgip&s z$@dGstGUgbZb0EZpBO)FYUo@~T03ufr$-mNAg_B&D#=@h5tTbh98o=$?F_K|{6NYW ziQG&>15NmKg{;Uwh?pRmn~Itu)sx#o_uTZN9Hz@UWJ9TTLPHuf&%;XPmGDQ%CpUqc zm(tR3C8p|xxG?VLJM3xOIo9_?wMAM2zPOAH zjq})$7l(ItV20?_*M{{n$f31F;Z(fUq|OSw$Acz334-1)~Z@qj@2 zeG$PS$5m?vH=k7Wc#qTFHFO*-zK`M*UKONJV@on8P~jXUHVdlm(Ql|7#n)mD7^ViX zllGV+WJ8Eb%v>I*z=pLPH<88G(=cJbpBM*8yb6{M9I zmbXk&Vo6OS4iokd>e)DXJ0M!BuJvlpuAV7VOh1cBYTyxzpjyBc{$cC!K@xp;&T|jN zByIM6RSdEKl$e-*H=u-hCVC|^6y^#^>|F02>qI~?K}KI4jGZh+B!Zp}jh^|$u1}<+ z)+Z!{^i@`n+x(eCBfdq@Kw{tI;UgaW=PbZgzP+#kUS6^6{2FZ!<SB92ww2pO9D|@|Rf!c^Y$#)&`YxA)Iqeu(Srb1EVE7S!a;PR? zr0Nwr)>2OgY&TPXkb)F@w(>5z+6blHpIk{$9bFX>i5~CTatJsYsuC|VA~R&t?RAf2 zd;WrPt=BTt;jz92t7f-qFM^6SXwvr2a;WL5L1<}Ot;-A*AZ;#WBe?oPcO3gVvz3BU zs8!Rj_9O3%V-98UZPTAw@ zs0Gy1n)7v{)j3O0#tR$qXQDRxY`SaU>g&BGY~=MmL=8hqLRN}~yi&Dd=o1)!#FZNJ z#03Z=lH%`&{5fdQ<=X78Z9)(2QbOM7@G2=_YJtB^C{-`n`7rert40V44>@d(T+0(ZyT&EiwPuZdfNu!OJHYz%YA7 zDBpXI>6+M04I%C~A#gXg0hx(Xg7;;sDm52-SP@9=0 zGnQf=c=xg7!FT>jn_;(i9XrDpBd$|Ye4}7S4D@8JEzEJp5r9FnOw29|GGYX_RyMuo zw?!Dc-I@q2VA1;xx7}nXnxTX;*2sO#9|197?Ga_E-p~+M58{nVn^N{ix=82nBD^6G z4;xmOvJum;YK&A%OTBN~eDXrwf#apf%}9{ zeKMIRB#fr2yuuPQH=;f^^P2v5n}g(fb=bFxxM!Y-qtZt(N=4TcBCs$cB?R4oZNNw( z3~0R)-YMy3-U9bxmXkuDDJ0UG_E>z1*<-%<6KUmKsN;?~zT`eihD>1?LRC$wtPaai zf6zhv2w38~eyVH8vX!xFz@X_A!%WCxLywwdZra!QTuXV4Xft4&Gk0*Ig{V>g3^MkL zi|mNCGOF=X4zwzm+Ppqk7F)TYwNX7~^dy+K9lMM94!|i>QY7R@_vbmlp;oej1#TSc zl>oP9l?3x=xX);5$Kpfrv?WSqY`@%vrfiL%+7BreHTR8SQ6{ECDXEfoW3~0MG!knx zx_IQ2%#p`sfhCTFh(Th8+Ik;72+nybjr_DKpTvwS&GOGp3ZqS>w$9^<&(%WHhH1X~ zF->Sg5B*7#=K$gn$oIrLuPxt60kryXqiVX3hWg5Sx(nAvaG&Z_#}=-%ZUj@&OF!$- z^Ri5;YBYRgpR<=&OXv+apKB!!SFoYisadYZ5_pbfdINN7f`aAFaz*409{^lqn{1e) z9C}S+)Elrltb!bq>UND`c{S!VE5PMuk_JzLkOxzQaer#BBaarN#G)i&%eAgPDIx{DhxUz4MX-gu#>K^fRVY`x@(2tvnUVNWSS|JRKVwQ?E z?s{~o^F~^2*-?+`a2-44LU`iWX)%}xC9CGc+gCxBjB#M=rllLhQ~hyF54tPt1?}@! zc1nTR8=YzEwEGsMWdj;1hHLh}wHgs7;kjar0sahbDqrB_Q9XwJEvR$=KUmpzAf{y3 zCy%TfP`vnIJvy(>x`6gIggbqz$QnvnK6JumnQoW8d${hvjy@K8oEJ>|c?+VgyPiR* zIGjGf3t_~AX2YXS0IbaSG=w#ztX74+hN{CRqZs@ql2$*(&I{&#K1^IYYzN(khS=SV zJN$vw$Sq|F|0#nGi3HQTE(4bvQfaa2w=3A-`J>AzJqmu`+=c;{&OVK2iqO68QcW+Y zCN5QF^p!XTY$b&A4>1smb513yqrNXtDA{O5IYBwvX(0N@ZwY zFn!{*UVQy2Em!N4QKi(9Z1+CYXw)gM8CKLdt!FrifOTKw>ZA(y-^h{ zY`k9CjePq-H#rNG(oOUcRjD%n6_v+a*s`ezrK2_K{a_(nhAOr(^FgPo&fMW3Hlv7M zP7k*pj*k1d*|mK!8!vVfR58KF*W#vivvNS}qCEZGjh-AcUI~eaE@gRC-Po1iZ3Nzx z6%D7a|2%#Nl`ExLW3h10f9~8Za*5fv;x*-^ettru)CV0CA zoV*dF)ls*&;L4Nkai88e+8cP=6xNlN zFzPjYb}!JjL$YT+#xLOWG|7vP)1Nv^T{+~>QHmOCdC{e~zzrEXVyz-4%Kah+Q#I{p z;}#A8c`bQdfv)k&iaekH>=ihRRogud*PZMFNsAgD9*XbRkK~iF>)Rvnny8c(_bWZhVT@+Gd5};56UIIVMNtR?PbSOtWf!>70?Eyz}w& z72KFli3+>JiPW|wIF+t3wp@B7IAsODYON$VHalc+BVWO-FhPrE;2R8Az!tTn`2P8^ zwA!jf9C67M@aA2K`Eveqa6~uA8KIH1AlFr&a11Jv8Q9sDdC|->I+^}{oG45LigeZx~ak3OQY9%!Yol} zhn0Psydrpzb~MU1eyc}}-?0zZPq_2zk9{I+T0$bSSQqBsu>vpAi%))oHMqshM)gQ$ z!BQaef&8Tm2lo<5Rep>0X_|F4<&C*TQ3{UfY#L$6lhwD)?S7p;jqon>5-Ya-?B(aq zQcnRtX1Pb*?wGi$gFDEPYAEZwk86(Wey%4)uC$ar&h#!apCBUi`w&(S65Gp8_5D=d zYR_xe_>mwYZ#4tfPw$%o`TVTZ4VpI8;_UiElkIj@pOOSJz4B1u1o`r6!5(Fmv6>}0 z@a43GwU(bIreKb}u_*)NcQNQY;oP(sMwCsx3nyeI$`w2lB8dxP`083FB89hmwHMpS zWs&u`FA=!1PhfTNW9A~TE^6Z;_mbdBDAJ3Ejc3#6cNw(EDQ4jLpDcmc*b>4H1HZc0 zpjgu$`%uVXhHZ-02tlXQVK;kn_YAe&!4U8%G)37vFb zZ5kD52V1Q;G+XqKETGeU1pvP|yMlIObc=F(Yxp~tBwn@WfaIwA8$Nv*GD+strp?zS zWgz)2;L;O^=S$p#vy|E9P5%-A^?~kYZ6>gF!2`@L}Z2u}Szc8!G zLlVoGzFBm4Lr~meLntE2#ELM=ZqWZET3X^B^Y1E2p&FP56M@8aFek54as!5TtfQ@- z{+;=>S(1%@L(!$E&GCG$Up)^7OtpIZR_b}D_dHNm`9;aUpv`%JT`sh@TLga=5vxE~ zu0H@=q4_ZJ#mM=X$Ph2?e=mq|V=L zu@`Y;zetFQph`CA?%Kzlms#lsZX~H$j=O{Tw`3H>bZ$@*N8vbhUufwq*QDCW$Ig{v zm8~rfR;63RdsLUeGsF1#(Mct=`PkS0fHK?nEGM9o$2qe(ji@}JkG9Sos+iky{KX;qHxhHLT_VQz(LFPf| z)}?rn&~r~mrY+2g*!`Wa&3AtOqL}}v>*m#c?;+d2G@_XAe;s7npT#*hBz__W_O%|V zbG*GtNoK_?d4Ft8?)Sg6qMTd*DCspSOfngC9||E&B0yC;@sE-!KnQa@kyKBrJv<No$c z`|nSOC@j_S+W)y3j^%INuhsmWrLKPY11UY#zIVU}&z#ZwOHr3knp$j~0ZiHd#8UqO zpi<4&v3qHisO72S=H#Au^0ws0EAk!xp6J@=&UZQq%yR+D8q`BmiRfezI5PEvri+w{D~!WKMa~6o~t-g)rv88 zbeQ64k7c!>k=kR|B;7mR!wP*NI7|2s`47G{%vX#_YX1)K;SO%;cb&)5LkClAg>5`s zh6~aESSSAX(UOIT#6RYyEGnw2ed9i^@85^}o6>vf=kKCtB&DkghL@_d>8I_Pv~RyY zi~#!5+^YDiNc_<7!MH@sm$J@e9cugkW3qw{|NOu*e+r1iE9xN|>XWT$iCM^=-J_KS zKdxJU3gYj75s*26gj}Y?Mx*V#2lsH{cR*^$*R)4}_8RmTQC9Q<%pAO&FaMn9Ps~xy z`}X`^>x=V0CGqB;l1%;aN4KfAfJS`G|1+Rr3+Io>zIMGEHXG%d@;^Pjb2B=s3neR;pdOg8|aPf6SN-+TSfI0yXSf=B-AV|Y+G^|Y@r zelyci)Lx@3@5$YSwth8k`J^6_#dknJQsU`n(%`&@bNyg=sT0c#;~~0R;EuRxxJwFMiGWm%!Sp%zp6K85Bnr`-vKK&Af|rEH(fO3gknMi0MwUX{nx?% z?F|O5;bU=g7B`0)U9WGKXCd!U$wm5hnF@2YzTM$U+L%v9(X$!#Y$BGH>--%HVA z^EWL2sT;Z|zIwwF35g@+hhx~XCOoz;bS0ugaRX{=JDw<7L}ez3JB?Ue5FDFS9OHP< z5XYCx`(lDt$09hho@PGSVZ88QCVN#r>>hq?dW3oR{+_e+4}&_v5z!V>?!^tRNeKe3 zzzlwYWg~2;r1a0W6XS6+hWaugGhH!M&Ef(qNQSO-|7=C2)PWKC`J*ztX&7mBzCF77 z+;I8}UA`2U>&=#t4s+yO9d64E&Uv@FqHWq{dYixURF*XLidI(in~u|hY0lto#epis zJ>J!o$(l>I)BdL>)NCX~8!oy>>x3I}?Ag(MZ5&OIJLYw7yk#j*Nh|MCQFgzsveff> zeH`N*KLL6F9U@9>t+SsOk%W zJHafA_p290k6-uZY`lxYbR#|iXA#+?#O0#PwG2+aJKAI=40spgwDQ!QI!`+0}-bg>Jb z!b=LyF;Q1dLtR#LY~qJGcirO%-}oI<%@2+V=u5Fus1!jFe?gI)knGR24ah{!5q+(T zU<=gE44sNd7EXWf3?x{fbkWpumov&lNUna#4^r|3hXT}0L2Z;{J@0o60$z4C9pc~U z$QiAT(q^)|(4SFcwmdWAM=s`uTld^pMY)b9D?KJ{SP=)_)U;+2s;7aA_$lOl=ZrvF zaNoYAeUpCLa(8-Iy_LEvzt7@my?=a?_DRbvM2>xXdE4f$vo%2V>*%pYV+b-(X0T<7 zjdSF2Pq$+lT8E*Oeg}%=<%GnKet}N*S|VG)?};AyzD`e37+RuL{g)mncG8#Oz*+J~&Nym)ul)Je zw<2B!Hqfza<{T%=gPK8|MX2V-psasdV zdITpi$y|z0A9HP}AI#)%s&W?gxy_?ep_K8DS~| zL6P}+E`qM5_+li`^zlZDteDYaM&Px*fd$hnp=@t#FokS~FT$ZM5e!x*Rkd^c>aK7N z#hL1SDY~~Vh?(YYHC}(SFx*h>qJjB=H=zaq^g^8F&$c!`%33qqj7O0lEMJU3zb##a z0ledXSwiz#j7p4*N*x((4f7Wuz^0s57s9p=xfls#UP5(+mdNTjFFZ##@z-E?heRtQ zKq$rwQRp^@K#RV3V+SHuJ)+PzMYoHcP%bEwIz{zFsO0Mb`k+Z|Q+`PN;Kk)Q>PJbW zIs=>Crj2tHMX{ML#-D*R7a{=gkIZhI0>;)S#x{?x%T05-f98T>O7kv)?OV~sIAHe` z*zoI^O{C))|ENR=(!JSxHHC(AL{a%d6#5NlmAc+J;Z-KwlOT4>wKr{9f4`%^^^}bV zyC1+hDnAin8aA~$*AGZoDA>vHytetxGb^xKf#Szt~h=GEIuy}YhCaxALM_h^&nN_I=;@Q~`S6^Kz bOrYnlOs3tW#6WU-*18o><53YD->3c;8?mX4 literal 0 HcmV?d00001 diff --git a/docs/multitenant/lrest-based/README.md b/docs/multitenant/lrest-based/README.md deleted file mode 100644 index d9b72a9d..00000000 --- a/docs/multitenant/lrest-based/README.md +++ /dev/null @@ -1,501 +0,0 @@ - - - -# LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT - - -- [LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT](#lrest-based-multitenant-controllers-for-pdb-life-cycle-management) - - [STEP BY STEP CONFIGURATION](#step-by-step-configuration) - - [Multiple namespace setup](#multiple-namespace-setup) - - [Create the operator](#create-the-operator) - - [Container database setup](#container-database-setup) - - [Apply rolebinding](#apply-rolebinding) - - [Certificate and credentials](#certificate-and-credentials) - - [Private key 🔑](#private-key-) - - [Public Key 🔑](#public-key-) - - [Certificates](#certificates) - - [Create secrets for certificate and keys](#create-secrets-for-certificate-and-keys) - - [Create secrets with encrypted password](#create-secrets-with-encrypted-password) - - [Create lrest pod](#create-lrest-pod) - - [Create PDB](#create-pdb) - - [pdb config map ](#pdb-config-map) - - [Open PDB](#open-pdb) - - [Close PDB](#close-pdb) - - [Clone PDB](#clone-pdb) - - [Unplug PDB](#unplug-pdb) - - [Plug PDB](#plug-pdb) - - [Delete PDB](#delete-pdb) - - [Map PDB](#map-pdb) - - - - - -**Lrpdb** and **lrest** are two controllers for PDB lifecycle management (**PDBLCM**). They rely on a dedicated REST server (Lite Rest Server) Container image to run. The `lrest` controller is available on the Oracle Container Registry (OCR). The container database can be anywhere (on-premises or in the Cloud). - -![generaleschema](./images/Generalschema2.jpg) - -## STEP BY STEP CONFIGURATION -Complete each of these steps in the order given. - -### Multiple namespace setup - -Before proceeding with controllers setup, ensure that the Oracle Database Operator (operator) is configured to work with multiple namespaces, as specified in the [README](../../../README.md). -In this document, each controller is running in a dedicated namespace: lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains all the files reported in this document. - -Configure the **WACTH_NAMESPACE** list of the operator `yaml` file - -```bash -sed -i 's/value: ""/value: "oracle-database-operator-system,pdbnamespace,cdbnamespace"/g' oracle-database-operator.yaml -``` - -### Create the operator -Run the following command: - -```bash -kubectl apply -f oracle-database-operator.yaml -``` -Check the controller: -```bash -kubectl get pods -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -oracle-database-operator-controller-manager-796c9b87df-6xn7c 1/1 Running 0 22m -oracle-database-operator-controller-manager-796c9b87df-sckf2 1/1 Running 0 22m -oracle-database-operator-controller-manager-796c9b87df-t4qns 1/1 Running 0 22m -``` -### Container database setup - -On the container database, use the following commands to configure the account for PDB administration: - -```sql -alter session set "_oracle_script"=true; -create user identified by ; -grant create session to container=all; -grant sysdba to container=all; -``` - - -### Apply rolebinding - - -Apply the following files : [`pdbnamespace_binding.yaml`](./usecase/pdbnamespace_binding.yaml) [`cdbnamespace_binding.yaml`](./usecase/cdbnamespace_binding.yaml) -```bash -kubectl apply -f pdbnamespace_binding.yaml -kubectl apply -f cdbnamespace_binding.yaml -``` - -### Certificate and credentials -You must create the public key, private key, certificates and Kubernetes Secrets for the security configuration. - -#### Private key 🔑 -> Note: Only private key **PCKS8** format is supported by LREST controllers. Before you start configuration, ensure that you can use it. If you are using [`openssl3`](https://docs.openssl.org/master/) then `pcks8` is generated by default. If it is not already generated, then use the following command to create a `pcks8` private key - -```bash -openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out private.key -``` -#### Public Key 🔑 -Create the public key. - -```bash -/usr/bin/openssl rsa -in private.key -outform PEM -pubout -out public.pem -``` -#### Certificates -Create certificates. -```bash -openssl req -new -x509 -days 365 -key private.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out ca.crt -``` -```bash -openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=cdb-dev-lrest.cdbnamespace" -out server.csr -``` -```bash -/usr/bin/echo "subjectAltName=DNS:cdb-dev-lrest.cdbnamespace,DNS:www.example.com" > extfile.txt -``` -```bash -/usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey private.key -CAcreateserial -out tls.crt -``` - -### Create secrets for certificate and keys -Create the Kubernetes Secrets. - -```bash -kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system -kubectl create secret generic db-ca --from-file="ca.crt" -n oracle-database-operator-system -kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n cdbnamespace -kubectl create secret generic db-ca --from-file="ca.crt" -n cdbnamespace -kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n pdbnamespace -kubectl create secret generic db-ca --from-file="ca.crt" -n pdbnamespace -``` - -```bash -kubectl create secret tls prvkey --key="private.key" --cert=ca.crt -n cdbnamespace -kubectl create secret generic pubkey --from-file=publicKey=public.pem -n cdbnamespace -kubectl create secret generic prvkey --from-file=privateKey="private.key" -n pdbnamespace -``` - -### Create secrets with encrypted password - -In this example, we create the Secrets for each credential (username and password) - -| secret usr | secrets pwd | credential description | -| -----------|-------------|-----------------------------------------------------------| -| **dbuser** |**dbpass** | the administrative user created on the container database | -| **wbuser** |**wbpass** | the user for https authentication | -| **pdbusr** |**pdbpwd** | the administrative user of the pdbs | - - -```bash -echo "[ADMINUSERNAME]" > dbuser.txt -echo "[ADMINUSERNAME PASSWORD]" > dbpass.txt -echo "[WEBUSER]" > wbuser.txt -echo "[WEBUSER PASSWORD]" > wbpass.txt -echo "[PDBUSERNAME]" > pdbusr.txt -echo "[PDBUSERNAME PASSWORD]" > pdbpwd.txt - -## Encrypt the credentials -openssl rsautl -encrypt -pubin -inkey public.pem -in dbuser.txt |base64 > e_dbuser.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in dbpass.txt |base64 > e_dbpass.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in wbuser.txt |base64 > e_wbuser.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in wbpass.txt |base64 > e_wbpass.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in pdbusr.txt |base64 > e_pdbusr.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in pdbpwd.txt |base64 > e_pdbpwd.txt - -kubectl create secret generic dbuser --from-file=e_dbuser.txt -n cdbnamespace -kubectl create secret generic dbpass --from-file=e_dbpass.txt -n cdbnamespace -kubectl create secret generic wbuser --from-file=e_wbuser.txt -n cdbnamespace -kubectl create secret generic wbpass --from-file=e_wbpass.txt -n cdbnamespace -kubectl create secret generic wbuser --from-file=e_wbuser.txt -n pdbnamespace -kubectl create secret generic wbpass --from-file=e_wbpass.txt -n pdbnamespace -kubectl create secret generic pdbusr --from-file=e_pdbusr.txt -n pdbnamespace -kubectl create secret generic pdbpwd --from-file=e_pdbpwd.txt -n pdbnamespace - -rm dbuser.txt dbpass.txt wbuser.txt wbpass.txt pdbusr.txt pdbpwd.txt \ - e_dbuser.txt e_dbpass.txt e_wbuser.txt e_wbpass.txt e_pdbusr.txt e_pdbpwd.txt -``` - -### Create lrest pod - -To create the REST pod and monitor its processing, use the `yaml` file [`create_lrest_pod.yaml`](./usecase/create_lrest_pod.yaml) - -Ensure that you update the **lrestImage** with the latest version available on the [Oracle Container Registry (OCR)](https://container-registry.oracle.com/ords/f?p=113:4:104288359787984:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1283,1283,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,1,0&cs=3076h-hg1qX3eJANBcUHBNBCmYWjMvxLkZyTAhDn2e8VR8Gxb_a-I8jZLhf9j6gmnimHwlP_a0OQjX6vjBfSAqQ) - -```bash ---> for amd64 -lrestImage: container-registry.oracle.com/database/operator:lrest-241210-amd64 - ---> for arm64 -lrestImage: container-registry.oracle.com/database/operator:lrest-241210-arm64 -``` - -```bash -kubectl apply -f create_lrest_pod.yaml -``` - -monitor the file processing: - -```bash -kubectl get pods -n cdbnamespace --watch -NAME READY STATUS RESTARTS AGE -cdb-dev-lrest-rs-9gvx2 0/1 Pending 0 0s -cdb-dev-lrest-rs-9gvx2 0/1 Pending 0 0s -cdb-dev-lrest-rs-9gvx2 0/1 ContainerCreating 0 0s -cdb-dev-lrest-rs-9gvx2 1/1 Running 0 2s - -kubectl get lrest -n cdbnamespace -NAME CDB NAME DB SERVER DB PORT TNS STRING REPLICAS STATUS MESSAGE -cdb-dev DB12 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) 1 Ready -``` - -Check the Pod logs: - -```bash -/usr/local/go/bin/kubectl logs -f `/usr/local/go/bin/kubectl get pods -n cdbnamespace|grep lrest|cut -d ' ' -f 1` -n cdbnamespace -``` - -Output example: - -```text -... -... -2024/09/05 12:44:09 wallet file /opt/oracle/lrest/walletfile exists completed -2024/09/05 12:44:09 call: C.ReadWallet -LENCHECK: 7 11 7 8 -2024/09/05 12:44:09 ===== DUMP INFO ==== -00000000 28 44 45 53 43 52 49 50 54 49 4f 4e 3d 28 43 4f |(DESCRIPTION=(CO| -00000010 4e 4e 45 43 54 5f 54 49 4d 45 4f 55 54 3d 39 30 |NNECT_TIMEOUT=90| -00000020 29 28 52 45 54 52 59 5f 43 4f 55 4e 54 3d 33 30 |)(RETRY_COUNT=30| -00000030 29 28 52 45 54 52 59 5f 44 45 4c 41 59 3d 31 30 |)(RETRY_DELAY=10| -00000040 29 28 54 52 41 4e 53 50 4f 52 54 5f 43 4f 4e 4e |)(TRANSPORT_CONN| -00000050 45 43 54 5f 54 49 4d 45 4f 55 54 3d 37 30 29 28 |ECT_TIMEOUT=70)(| -00000060 4c 4f 41 44 5f 42 41 4c 4c 41 4e 43 45 3d 4f 4e |LOAD_BALLANCE=ON| -00000070 29 28 41 44 44 52 45 53 53 3d 28 50 52 4f 54 4f |)(ADDRESS=(PROTO| -00000080 43 4f 4c 3d 54 43 50 29 28 48 4f 53 54 3d 73 63 |COL=TCP)(HOST=sc| -00000090 61 6e 31 32 2e 74 65 73 74 72 61 63 2e 63 6f 6d |an12.testrac.com| -000000a0 29 28 50 4f 52 54 3d 31 35 32 31 29 28 49 50 3d |)(PORT=1521)(IP=| -000000b0 56 34 5f 4f 4e 4c 59 29 29 28 4c 4f 41 44 5f 42 |V4_ONLY))(LOAD_B| -000000c0 41 4c 4c 41 4e 43 45 3d 4f 4e 29 28 41 44 44 52 |ALLANCE=ON)(ADDR| -000000d0 45 53 53 3d 28 50 52 4f 54 4f 43 4f 4c 3d 54 43 |ESS=(PROTOCOL=TC| -000000e0 50 29 28 48 4f 53 54 3d 73 63 61 6e 33 34 2e 74 |P)(HOST=scan34.t| -000000f0 65 73 74 72 61 63 2e 63 6f 6d 29 28 50 4f 52 54 |estrac.com)(PORT| -00000100 3d 31 35 32 31 29 28 49 50 3d 56 34 5f 4f 4e 4c |=1521)(IP=V4_ONL| -00000110 59 29 29 28 43 4f 4e 4e 45 43 54 5f 44 41 54 41 |Y))(CONNECT_DATA| -00000120 3d 28 53 45 52 56 45 52 3d 44 45 44 49 43 41 54 |=(SERVER=DEDICAT| -00000130 45 44 29 28 53 45 52 56 49 43 45 5f 4e 41 4d 45 |ED)(SERVICE_NAME| -00000140 3d 54 45 53 54 4f 52 44 53 29 29 29 |=TESTORDS)))| -00000000 2f 6f 70 74 2f 6f 72 61 63 6c 65 2f 6c 72 65 73 |/opt/oracle/lres| -00000010 74 2f 77 61 6c 6c 65 74 66 69 6c 65 |t/walletfile| -2024/09/05 12:44:09 Get credential from wallet -7 -8 -2024/09/05 12:44:09 dbuser: restdba webuser :welcome -2024/09/05 12:44:09 Connections Handle -2024/09/05 12:44:09 Working Session Aarry dbhanlde=0x1944120 -2024/09/05 12:44:09 Monitor Session Array dbhanlde=0x1a4af70 -2024/09/05 12:44:09 Open cursors -Parsing sqltext=select inst_id,con_id,open_mode,nvl(restricted,'NONE'),total_size from gv$pdbs where inst_id = SYS_CONTEXT('USERENV','INSTANCE') and name =upper(:b1) -Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 -2024/09/05 12:44:11 Protocol=https -2024/09/05 12:44:11 starting HTTPS/SSL server -2024/09/05 12:44:11 ==== TLS CONFIGURATION === -2024/09/05 12:44:11 srv=0xc000106000 -2024/09/05 12:44:11 cfg=0xc0000a2058 -2024/09/05 12:44:11 mux=0xc0000a2050 -2024/09/05 12:44:11 tls.minversion=771 -2024/09/05 12:44:11 CipherSuites=[49200 49172 157 53] -2024/09/05 12:44:11 cer=/opt/oracle/lrest/certificates/tls.crt -2024/09/05 12:44:11 key=/opt/oracle/lrest/certificates/tls.key -2024/09/05 12:44:11 ========================== -2024/09/05 12:44:11 HTTPS: Listening port=8888 -2024/09/05 12:44:23 call BasicAuth Succeded -2024/09/05 12:44:23 HTTP: [1:0] Invalid credential <-- This message can be ignored - -``` - -**lrest Pod creation** - parameters list -| Name | Dcription | ---------------------------|-------------------------------------------------------------------------------| -|cdbName | Name of the container database (db) | -|lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** use the latest label availble on OCR | -|dbTnsurl | The string of the tns alias to connect to cdb. Attention: remove all white space from string | -|deletePdbCascade | Delete all of the PDBs associated to a CDB resource when the CDB resource is dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | -|cdbAdminUser | Secret: the administrative (admin) user | -|fileNameConversions | Use file name conversion if you are not using ASM | -|cdbAdminPwd | Secret: the admin user password | -|webServerUser | Secret: the HTTPS user | -|webServerPwd | Secret: the HTTPS user password | -|cdbTlsCrt | Secret: the `tls.crt ` | -|cdbPubKey | Secret: the public key | -|cdbPrvKey | Secret: the private key | - - - - -### Create PDB - -To create a pluggable database, apply the yaml file [`create_pdb1_resource.yaml`](./usecase/create_pdb1_resource.yaml) - -```bash -kubectl apply -f create_pdb1_resource.yaml -``` -Check the status of the resource and the PDB existence on the container db: - -```bash -kubectl get lrpdb -n pdbnamespace -NAME CONNECT_STRING CDB NAME LRPDB NAME LRPDB STATE LRPDB SIZE STATUS MESSAGE LAST SQLCODE -lrpdb1 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) DB12 pdbdev MOUNTED 2G Ready Success -``` - -```bash -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ ONLY NO - 3 PDBDEV MOUNTED -SQL> -``` -``Note that after creation, the PDB is not open. You must explicitly open it using a dedicated `yaml` file. - -**pdb creation** - parameters list - -| Name | Dcription | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database | -|pdbName | Name of the PDB that you want to create | -|assertiveLrpdbDeletion | Boolean: Turn on the imperative approach on PDB resource deletion | -|adminpdbUser | Secret: PDB admin user | -|adminpdbPass | Secret: password of PDB admin user | -|lrpdbTlsKey | Secret: `tls.key ` | -|lrpdbTlsCrt | Secret: `tls.crt` | -|lrpdbTlsCat | Secret: `ca.crt` | -|webServerUser | Secret: the HTTPS user | -|webServerPwd | Secret: the HTTPS user password | -|cdbPrvKey | Secret: private key | -|cdbPubKey | Secret: public key | -|pdbconfigmap | kubernetes config map that contains the PDB initialization (init) parameters | - -> NOTE: **assertiveLrpdbDeletion** must be specified for the following PDB actions **CLONE** **CREATE** **PLUG** **MAP**. - -🔥 **assertiveLrpdbDeletion** drops pluggable database using **INCLUDE DATAFILES** option - -All of the parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** must be specified in all PDB lifecycle management `yaml` files. To simplify presentation of requirements, we will not include them in the subsequent tables. - - -#### pdb config map - -By using **pdbconfigmap** it is possible to specify a kubernetes `configmap` with init PDB parameters. The config map payload has the following format: - - -``` -;; -;; -;; -.... -.... -;; -``` - -Example of `configmap` creation: - -```bash -cat < parameters.txt -session_cached_cursors;100;spfile -open_cursors;100;spfile -db_file_multiblock_read_count;16;spfile -EOF - -kubectl create configmap config-map-pdb -n pdbnamespace --from-file=./parameters.txt - -kubectl describe configmap config-map-pdb -n pdbnamespace -Name: config-map-pdb -Namespace: pdbnamespace -Labels: -Annotations: - -Data -==== -parameters.txt: ----- -session_cached_cursors;100;spfile -open_cursors;100;spfile -db_file_multiblock_read_count;16;spfile -test_invalid_parameter;16;spfile -``` - -- If specified, the `configmap` is applied during PDB **cloning**, **opening** and **plugging** -- The `configmap` is not monitored by the reconciliation loop; this feature will be available in future releases. This means that if someone decides to manually alter an init parameter, then the operator does not take any actions to syncronize PDB configuration with the `configmap`. -- **Alter system parameter feature** will be available in future releases. -- An application error with the `configmap` (for whatever reason) does not stop processes from completing. A warning with the associated SQL code is reported in the log file. - - - -### Open PDB - -To open the PDB, use the file [`open_pdb1_resource.yaml`](./usecase/open_pdb1_resource.yaml): - -```bash -kubectl apply -f open_pdb1_resource.yaml -``` - - **pdb opening** - parameters list - -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|pdbName | Name of the pluggable database (PDB) that you are creating | -|action | Use **Modify** to open the PDB | -|pdbState | Use **OPEN** to open the PDB | -|modifyOption | Use **READ WRITE** to open the PDB | - -### Close PDB - -To close the PDB, use the file [`close_pdb1_resource.yaml`](./usecase/close_pdb1_resource.yaml): - -```bash -kubectl apply -f close_pdb1_resource.yaml -``` -**pdb closing** - parameters list -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|pdbName | Name of the pluggable database (PDB) that you want to create | -|action | Use **Modify** to close the PDB | -|pdbState | Use **CLOSE** to close the PDB | -|modifyOption | Use **IMMEDIATE** to close the PDB | - -### Clone PDB ### - -To clone the PDB, use the file [`clone_pdb1_resource.yaml`](./usecase/clone_pdb1_resource.yaml): - -```bash -kubeclt apply -f clone_pdb1_resource.yaml -``` -**pdb cloning** - parameters list -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|pdbName | The name of the new pluggable database (PDB) | -|srcPdbName | The name of the source PDB | -|fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | -|totalSize | Set **unlimited** for cloning | -|tempSize | Set **unlimited** for cloning | -|pdbconfigmap | kubernetes `configmap` which contains the PDB init parameters | -|action | Use **clone** to clone the PDB | - -### Unplug PDB - -To unplug the PDB, use the file [`unplug_pdb1_resource.yaml`](./usecase/unplug_pdb1_resource.yaml): - -**pdb unplugging** -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|pdbName | Name of the pluggable database (PDB)| -### Plug PDB - -To plug in the PDB, use the file [`plug_pdb1_resource.yaml`](./usecase/plug_pdb1_resource.yaml). In this example, we plug in the PDB that was unpluged in the previous step: - -**pdb plugging** -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB)| | -|pdbName | Name of the pluggable database (PDB) | -|**xmlFileName** | Path of the XML file | -|action | **plug** | -|fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | -|sourceFileNameConversion | See parameter [SOURCE_FILE_NAME_CONVERT](https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/CREATE-PLUGGABLE-DATABASE.html#GUID-F2DBA8DD-EEA8-4BB7-A07F-78DC04DB1FFC__CCHEJFID) documentation | -|pdbconfigmap | Kubernetes `configmap` that contains the PDB init parameters | - -### Delete PDB - -To delete the PDB, use the file [`delete_pdb1_resource.yaml`](./usecase/delete_pdb1_resource.yaml) - -**pdb deletion** - -| Name | Dcription/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|action | **Delete** | -|dropAction | **INCLUDING** - Including datafiles or **NONE** | - - -### Map PDB - -If you need to create a CRD for an existing PDB, then you can use the map option by applying the file [`map_pdb1_resource.yaml`](./usecase/map_pdb1_resource.yaml) -Map functionality can be used in a situation where you have a pdb which is not registered in the operator as a CRD. It's a temporary solution while waiting the autodiscovery to be available. - - - diff --git a/docs/multitenant/lrest-based/usecase/config-map-pdb.yaml b/docs/multitenant/lrest-based/usecase/config-map-pdb.yaml deleted file mode 100644 index 2769b498..00000000 --- a/docs/multitenant/lrest-based/usecase/config-map-pdb.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: config-map-pdb - namespace: pdbnamespace -data: - rdbmsparameters.txt: | - session_cached_cursors;100;spfile - open_cursors;100;spfile - db_file_multiblock_read_count;16;spfile - test_invalid_parameter;16;spfile diff --git a/docs/multitenant/multitenant/usecase02/README.md b/docs/multitenant/multitenant/usecase02/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/multitenant/ords-based/NamespaceSeg.md b/docs/multitenant/ords-based/NamespaceSeg.md deleted file mode 100644 index 6738fe56..00000000 --- a/docs/multitenant/ords-based/NamespaceSeg.md +++ /dev/null @@ -1,14 +0,0 @@ - - -# Namespace segregation - -With the namespace segregation pdb controller and cdb controller run in different namespaces. The new functionality introduces a new parameter (the cdb namespace) in pdb crd definition. In case you don't need the namespace segregation you have to sepcify the namespace name that you are using for yours crd and pods anyway. Refer to usercase01 and usecase02 to see single namespace configuration. Refer to usecase03 to see examples of namespace segregation. - -# Secrets - -In order to use multiple namespace we need to create approriate secrets in each namespace. Tls certificate secrets must be created in all namespaces (db-ca db-tls). - -![general_schema](./images/K8S_NAMESPACE_SEG.png) - - - diff --git a/docs/multitenant/ords-based/README.md b/docs/multitenant/ords-based/README.md deleted file mode 100644 index 992748bd..00000000 --- a/docs/multitenant/ords-based/README.md +++ /dev/null @@ -1,411 +0,0 @@ - - -# Oracle Multitenant Database Controllers - -The Oracle Database Operator for Kubernetes uses two controllers to manage the [Pluggable Database lifecycle][oradocpdb] - -- CDB controller -- PDB controller - -By using CDB/PDB controllers, you can perform the following actions **CREATE**, **MODIFY(OPEN/COSE)**, **DELETE**, **CLONE**, **PLUG** and **UNPLUG** against pluggable database - -Examples are located under the following directories: - -- the directories [`usecase`](./usecase/) and [`usecase01`](./usecase01/) contain a [configuration file](./usecase/parameters.txt) where you can specify all the details of your environment. A [`makefile`](./usecase/makefile) takes this file as input to generate all of the `yaml` files. There is no need to edit `yaml` files one by one. -- [Singlenamespace provisioning](./provisioning/singlenamespace/) This file contains base example files that you can use to manage the PDB and CDB within a single namespace. -- [Multinamespace provisioning](./provisioning/multinamespace/) This file contains base example files that you can use to manage the PDB and CDB in different namespaces. -- [Usecase01](./usecase01/README.md) [Usecase02](./usecase02/README.md) This file contains other step-by-step examples; - -Automatic `yaml` generation is not available for the directory `usecase02` and provisioning directories. - -**NOTE** the CDB controller is not intended to manage the container database. The CDB controller is meant to provide a pod with a REST server connected to the container database that you can use to manage PDBs. - - -## Macro steps for setup - -- Deploy the Oracle Database Operator (operator, or `OraOperator`) -- [Create Ords based image for CDB pod](./provisioning/ords_image.md) -- [Container RDBMB user creation](#prepare-the-container-database-for-pdb-lifecycle-management-pdb-lm) -- Create certificates for https connection -- Create secrets for credentials and certificates -- Create CDB pod using the Ords based image - -## Oracle DB Operator Multitenant Database Controller Deployment - -To deploy `OraOperator`, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. - -After the **Oracle Database Operator** is deployed, you can see the Oracle Database (DB) Operator Pods running in the Kubernetes Cluster. The multitenant controllers are deployed as part of the `OraOperator` deployment. You can see the CRDs (Custom Resource Definition) for the CDB and PDBs in the list of CRDs. The following output is an example of such a deployment: - -```bash -[root@test-server oracle-database-operator]# kubectl get ns -NAME STATUS AGE -cert-manager Active 32h -default Active 245d -kube-node-lease Active 245d -kube-public Active 245d -kube-system Active 245d -oracle-database-operator-system Active 24h <---- namespace to deploy the Oracle Database Operator - -[root@test-server oracle-database-operator]# kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 0 28s - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 29s -service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 29s - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 29s - -NAME DESIRED CURRENT READY AGE -replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 29s -[root@docker-test-server oracle-database-operator]# - -[root@test-server oracle-database-operator]# kubectl get crd -NAME CREATED AT -autonomouscontainerdatabases.database.oracle.com 2022-06-22T01:21:36Z -autonomousdatabasebackups.database.oracle.com 2022-06-22T01:21:36Z -autonomousdatabaserestores.database.oracle.com 2022-06-22T01:21:37Z -autonomousdatabases.database.oracle.com 2022-06-22T01:21:37Z -cdbs.database.oracle.com 2022-06-22T01:21:37Z <---- -certificaterequests.cert-manager.io 2022-06-21T17:03:46Z -certificates.cert-manager.io 2022-06-21T17:03:47Z -challenges.acme.cert-manager.io 2022-06-21T17:03:47Z -clusterissuers.cert-manager.io 2022-06-21T17:03:48Z -dbcssystems.database.oracle.com 2022-06-22T01:21:38Z -issuers.cert-manager.io 2022-06-21T17:03:49Z -oraclerestdataservices.database.oracle.com 2022-06-22T01:21:38Z -orders.acme.cert-manager.io 2022-06-21T17:03:49Z -pdbs.database.oracle.com 2022-06-22T01:21:39Z <--- -shardingdatabases.database.oracle.com 2022-06-22T01:21:39Z -singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z -``` - - -## Prerequisites to manage PDB Life Cycle using Oracle DB Operator Multitenant Database Controller - -* [Prepare the container database (CDB) for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) -* [Oracle REST Data Service or ORDS Image](#oracle-rest-data-service-ords-image) -* [Kubernetes Secrets](#kubernetes-secrets) -* [Kubernetes CRD for CDB](#cdb-crd) -* [Kubernetes CRD for PDB](#pdb-crd) - -## Prepare the container database for PDB Lifecycle Management (PDB-LM) - -Pluggable Database (PDB) management operations are performed in the Container Database (CDB). These operations include **create**, **clone**, **plug**, **unplug**, **delete**, **modify** and **map pdb**. - -To perform PDB lifecycle management operations, you must first use the following steps to define the default CDB administrator credentials on target CDBs: - -Create the CDB administrator user and grant the required privileges. In this example, the user is `C##DBAPI_CDB_ADMIN`. However, any suitable common username can be used. - -```SQL -SQL> conn /as sysdba - --- Create following users at the database level: - -ALTER SESSION SET "_oracle_script"=true; -DROP USER C##DBAPI_CDB_ADMIN cascade; -CREATE USER C##DBAPI_CDB_ADMIN IDENTIFIED BY CONTAINER=ALL ACCOUNT UNLOCK; -GRANT SYSOPER TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; -GRANT SYSDBA TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; -GRANT CREATE SESSION TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; - - --- Verify the account status of the following usernames. They should not be in locked status: - -col username for a30 -col account_status for a30 -select username, account_status from dba_users where username in ('ORDS_PUBLIC_USER','C##DBAPI_CDB_ADMIN','APEX_PUBLIC_USER','APEX_REST_PUBLIC_USER'); -``` - -## OCI OKE (Kubernetes Cluster) - -You can use an [OKE in Oracle Cloud Infrastructure][okelink] to configure the controllers for PDB lifecycle management. **Note that there is no restriction about container database location; it can be anywhere (on Cloud or on-premises).** -To quickly create an OKE cluster in your OCI cloud environment you can use the following [link](./provisioning/quickOKEcreation.md). -In this setup example [provisioning example setup](./provisioning/example_setup_using_oci_oke_cluster.md), the Container Database is running on an OCI Exadata Database Cluster. - - -## Oracle REST Data Service (ORDS) Image - -The PDB Database controllers require a pod running a dedicated REST server image based on [ORDS][ordsdoc]. Read the following [document on ORDS images](./provisioning/ords_image.md) to build the ORDS images. - - -## Kubernetes Secrets - - Multitenant Controllers use Kubernetes Secrets to store the required credential and HTTPS certificates. - - **Note** In multi-namespace environments you must create specific Secrets for each namespaces. - -### Secrets for CERTIFICATES - -Create the certificates and key on your local host, and then use them to create the Kubernetes Secret. - -```bash -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost Root CA " -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost" -out server.csr -echo "subjectAltName=DNS:cdb-dev-ords,DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -``` - -```bash -kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system -kubectl create secret generic db-ca --from-file=ca.crt -n oracle-database-operator-system -``` - -image_not_found - -**Note:** Remove temporary files after successfful Secret creation. - -### Secrets for CDB CRD - - **Note:** base64 encoded secrets are no longer supported; use OpenSSL secrets as documented in the following section. After successful creation of the CDB Resource, the CDB and PDB Secrets can be deleted from the Kubernetes system. Don't leave plaintext files containing sensitive data on disk. After loading the Secret, remove the plaintext file or move it to secure storage. - - ```bash - -export PRVKEY=ca.key -export PUBKEY=public.pem -WBUSERFILE=wbuser.txt -WBPASSFILE=wbpass.txt -CDBUSRFILE=cdbusr.txt -CDBPWDFILE=cdbpwd.txt -SYSPWDFILE=syspwd.txt -ORDPWDFILE=ordpwd.txt -PDBUSRFILE=pdbusr.txt -PDBPWDFILE=pdbpwd.txt - -# Webuser credential -echo [WBUSER] > ${WBUSERFILE} -echo [WBPASS] > ${WBPASSFILE} - -# CDB admin user credentioan -echo [CDBPWD] > ${CDBPWDFILE} -echo [CDBUSR] > ${CDBUSRFILE} - -# SYS Password -echo [SYSPWD] > ${SYSPWDFILE} - -# Ords Password -echo [ORDPWD] > ${ORDPWDFILE} - -## PDB admin credential -echo [PDBUSR] > ${PDBUSRFILE} -echo [PDBPWD] > ${PDBPWDFILE} - -#Secrets creation for pub and priv keys -openssl rsa -in ${PRVKEY} -outform PEM -pubout -out ${PUBKEY} -kubectl create secret generic pubkey --from-file=publicKey=${PUBKEY} -n ${CDBNAMESPACE} -kubectl create secret generic prvkey --from-file=privateKey=${PRVKEY} -n ${CDBNAMESPACE} -kubectl create secret generic prvkey --from-file=privateKey="${PRVKEY}" -n ${PDBNAMESPACE} - -#Password encryption -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${WBUSERFILE} |base64 > e_${WBUSERFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${WBPASSFILE} |base64 > e_${WBPASSFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${CDBPWDFILE} |base64 > e_${CDBPWDFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${CDBUSRFILE} |base64 > e_${CDBUSRFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${SYSPWDFILE} |base64 > e_${SYSPWDFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${ORDPWDFILE} |base64 > e_${ORDPWDFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${PDBUSRFILE} |base64 > e_${PDBUSRFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${PDBPWDFILE} |base64 > e_${PDBPWDFILE} - -#Ecrypted secrets creation -kubectl create secret generic wbuser --from-file=e_${WBUSERFILE} -n ${CDBNAMESPACE} -kubectl create secret generic wbpass --from-file=e_${WBPASSFILE} -n ${CDBNAMESPACE} -kubectl create secret generic wbuser --from-file=e_${WBUSERFILE} -n ${PDBNAMESPACE} -kubectl create secret generic wbpass --from-file=e_${WBPASSFILE} -n ${PDBNAMESPACE} -kubectl create secret generic cdbpwd --from-file=e_${CDBPWDFILE} -n ${CDBNAMESPACE} -kubectl create secret generic cdbusr --from-file=e_${CDBUSRFILE} -n ${CDBNAMESPACE} -kubectl create secret generic syspwd --from-file=e_${SYSPWDFILE} -n ${CDBNAMESPACE} -kubectl create secret generic ordpwd --from-file=e_${ORDPWDFILE} -n ${CDBNAMESPACE} -kubectl create secret generic pdbusr --from-file=e_${PDBUSRFILE} -n ${PDBNAMESPACE} -kubectl create secret generic pdbpwd --from-file=e_${PDBPWDFILE} -n ${PDBNAMESPACE} - -#Get rid of the swap files -rm ${WBUSERFILE} ${WBPASSFILE} ${CDBPWDFILE} ${CDBUSRFILE} \ - ${SYSPWDFILE} ${ORDPWDFILE} ${PDBUSRFILE} ${PDBPWDFILE} \ - e_${WBUSERFILE} e_${WBPASSFILE} e_${CDBPWDFILE} e_${CDBUSRFILE} \ - e_${SYSPWDFILE} e_${ORDPWDFILE} e_${PDBUSRFILE} e_${PDBPWDFILE} -``` - -Check Secrets details - -```bash -kubectl describe secrets syspwd -n cdbnamespace -Name: syspwd -Namespace: cdbnamespace -Labels: -Annotations: - -Type: Opaque - -Data -==== -e_syspwd.txt: 349 bytes -``` -Example of `yaml` file Secret section: - -```yaml -[...] - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" -[...] -``` - -## CDB CRD - -The Oracle Database Operator Multitenant Controller creates the CDB as a custom resource object kind that models a target CDB as a native Kubernetes object. This object kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. Each CDB resource follows the CDB CRD as defined here: [`config/crd/bases/database.oracle.com_cdbs.yaml`](../../../config/crd/bases/database.oracle.com_cdbs.yaml) - -To create a CDB CRD, use this example`.yaml` file: [`cdb_create.yaml`](./provisioning/singlenamespace/cdb_create.yaml) - -**Note:** The password and username fields in this *cdb.yaml* Yaml are the Kubernetes Secrets created earlier in this procedure. For more information, see the section [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). To understand more about creating secrets for pulling images from a Docker private registry, see [Kubernetes Private Registry Documentation]( https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). - -Create a CDB CRD Resource example - -```bash -kubectl apply -f cdb_create.yaml -``` - -see [usecase01][uc01] and [usecase02][uc02] for more information about file configuration - -## PDB CRD - -The Oracle Database Operator Multitenant Controller creates the PDB object kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. You cannot have more than one Kubernetes resource for a target PDB. This PDB resource can be used to perform PDB-LM operations by specifying the action attribute in the PDB Specs. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -Yaml file [pdb_create.yaml](../multitenant/provisioning/singlenamespace/pdb_create.yaml) to create a pdb - -```bash -kubectl apply -f pdb_create.yaml -``` - -## CRD TABLE PARAMETERS - -| yaml file parameters | value | description /ords parameter | CRD | -|------------------ |--------------------------- |-------------------------------------------------------------------------------|-----------| -| dbserver | or | [--db-hostname][1] | CDB | -| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | CDB | -| port | | [--db-port][2] | CDB | -| cdbName | | Container Name | CDB | -| name | | ORDS podname prefix in `cdb.yaml` | CDB | -| name | | Pdb resource in `pdb.yaml` | PDB | -| ordsImage | ords-dboper:latest | ORDS pod public container registry | CDB | -| pdbName | | Pluggable database (PDB) name | Container database (CDB) | -| servicename | | [--db-servicename][3] | CDB | -| sysadmin_user | | [--admin-user][adminuser] | CDB | -| sysadmin_pwd | | [--password-stdin][pwdstdin] | CDB | -| cdbadmin_user | | [db.cdb.adminUser][1] | CDB | -| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | CDB | -| webserver_user | | [https user][http] NOT A DB USER | CDB PDB | -| webserver_pwd | | [http user password][http] | CDB PDB | -| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | CDB | -| pdbTlsKey | | [standalone.https.cert.key][key] | PDB | -| pdbTlsCrt | | [standalone.https.cert][cr] | PDB | -| pdbTlsCat | | certificate authority | PDB | -| cdbTlsKey | | [standalone.https.cert.key][key] | CDB | -| cdbTlsCrt | | [standalone.https.cert][cr] | CDB | -| cdbTlsCat | | Certificate authority | CDB | -| cdbOrdsPrvKey | | Private key | CDB | -| pdbOrdsPrvKey | | Private key | PDB | -| xmlFileName | | Path for the unplug and plug operation | PDB | -| srcPdbName | | Name of the database that you want to be cloned | PDB | -| action | | Create open close delete clone plug unplug and map | PDB | -| deletePdbCascade | boolean | Delete PDBs cascade during CDB deletion | CDB | -| assertivePdbDeletion | boolean | Deleting the PDB crd means deleting the PDB as well | PDB | -| fileNameConversions | | Used for database cloning | PDB | -| totalSize | | `dbsize` | PDB | -| pdbState | | Change PDB state | PDB | -| modifyOption | | To be used along with `pdbState` | PDB | -| dropAction | | Delete datafiles during PDB deletion | PDB | -| sourceFileNameConversions | | [sourceFileNameConversions(optional): string][4] | PDB | -| tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | N/A | -| tdeExport | | [tdeExport] | N/A ] -| tdeSecret | | [tdeSecret][tdeSecret] | N/A | -| tdePassword | | [tdeSecret][tdeSecret] | N/A | - - - - -## Usecases files list - -### Single Namespace - -1. [Create CDB](./provisioning/singlenamespace/cdb_create.yaml) -2. [Create PDB](./provisioning/singlenamespace/pdb_create.yaml) -3. [Clone PDB](./provisioning/singlenamespace/pdb_clone.yaml) -4. [Open PDB](./provisioning/singlenamespace/pdb_open.yaml) -4. [Close PDB](./provisioning/singlenamespace/pdb_close.yaml) -5. [Delete PDB](./provisioning/singlenamespace/pdb_delete.yaml) -6. [Unplug PDB](./provisioning/singlenamespace/pdb_unplug.yaml) -7. [Plug PDB](./provisioning/singlenamespace/pdb_plug.yaml) - -### Multiple namespace (cdbnamespace,dbnamespace) - -1. [Create CDB](./provisioning/multinamespace/cdb_create.yaml) -2. [Create PDB](./provisioning/multinamespace/pdb_create.yaml) -3. [Clone PDB](./provisioning/multinamespace/pdb_clone.yaml) -4. [Open PDB](./provisioning/multinamespace/pdb_open.yaml) -4. [Close PDB](./provisioning/multinamespace/pdb_close.yaml) -5. [Delete PDB](./provisioning/multinamespace/pdb_delete.yaml) -6. [Unplug PDB](./provisioning/multinamespace/pdb_unplug.yaml) - -## Known issues - - - ORDS installation failure if pluaggable databases in the container db are not openedS - - - Version 1.1.0: encoded password for https authentication may include carriage return as consequence the https request fails with http 404 error. W/A generate encoded password using **printf** instead of **echo**. - - - pdb controller authentication suddenly fails without any system change. Check the certificate expiration date **openssl .... -days 365** - - - Nothing happens after applying cdb yaml files: Make sure to have properly configured the WHATCH_NAMESPACE list in the operator yaml file - - [okelink]:https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm - - [ordsdoc]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.1/index.html - - [uc01]:./usecase01/README.md - - [uc02]:./usecase02/README.md - - [oradocpdb]:https://docs.oracle.com/en/database/oracle/oracle-database/21/multi/introduction-to-the-multitenant-architecture.html#GUID-AB84D6C9-4BBE-4D36-992F-2BB85739329F - - [1]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation - - [2]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation - - [3]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-DAA027FA-A4A6-43E1-B8DD-C92B330C2341:~:text=%2D%2Ddb%2Dservicename%20%3Cstring%3E - - [4]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.3/orrst/op-database-pdbs-post.html - -[adminuser]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22:~:text=Table%202%2D6%20Command%20Options%20for%20Uninstall%20CLI - -[public_user]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/using-multitenant-architecture-oracle-rest-data-services.html#GUID-E64A141A-A71F-4979-8D33-C5F8496D3C19:~:text=Preinstallation%20Tasks%20for%20Oracle%20REST%20Data%20Services%20CDB%20Installation - -[key]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=standalone.https.cert.key - -[cr]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0 - -[cdbadminpwd]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=Table%20C%2D1%20Oracle%20REST%20Data%20Services%20Configuration%20Settings - - -[pwdstdin]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-88479C84-CAC1-4133-A33E-7995A645EC05:~:text=default%20database%20pool.-,2.1.4.1%20Understanding%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation,-Table%202%2D2 - -[http]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-BEECC057-A8F5-4EAB-B88E-9828C2809CD8:~:text=Example%3A%20delete%20%5B%2D%2Dglobal%5D-,user%20add,-Add%20a%20user - -[dbtnsurl]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22 - -[tdeKeystorePath]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/21.4/orrst/op-database-pdbs-pdb_name-post.html - -[tdeSecret]:https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ADMINISTER-KEY-MANAGEMENT.html#GUID-E5B2746F-19DC-4E94-83EC-A6A5C84A3EA9 -~ - - - - - diff --git a/docs/multitenant/ords-based/images/K8S_NAMESPACE_SEG.png b/docs/multitenant/ords-based/images/K8S_NAMESPACE_SEG.png deleted file mode 100644 index 594471ed54a6c980c9e63ab2f538c40a32a5dcb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269014 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

{$Kdx|9JL& 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 Z&B+Jld>=kvz=MAjuBcwlyJ#Hv{{cF13`_t3 diff --git a/docs/multitenant/ords-based/images/K8S_SECURE2.png b/docs/multitenant/ords-based/images/K8S_SECURE2.png deleted file mode 100644 index b9713d7cbbaa2cb1d7dc6634e8fef304aba79a99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130742 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

FcZ;+!m)=0*A!M^af8enjqQ3Ut|;QL7H6xvnR*~ z4BjklDAi42{(^@HMn>eyRV3#rq>};VSwMLQkxo!a?VNxRXlqQ`tOp~a2Cp^^ZZT4A zYP3Ms-s9Ff2d-!mGGJVUNF6lJ$Po4l0nh=9o}}wJs5G&4Y^otMs~|8`Z70+m=s_33 z*$2zH7%1r|K|&N&4bW`vffJC~Ptmi5z{vy9bo5yu;YNR*<%zbZg^1nzRRr46*{XR8 zlgB_n#ruJbhiR*J-X|M`ZY%=T4i>n2+q%;^uS&sri`>XoZL1Q(pT%E0$riH$A7CH= zAlC-WVDy$+aDZFP@Y;Er7w53$W<2Gi&ZIxwshL1?b_seVnoiN<~`6}S6b zF++%d&da_(&I&ZkW$OFaz#x#-0CdTH*9jDwf*ypwOn(VJnAM;-kT5m zf+Wy#MEHk1@8pv$&=K_)pGf&L6-{&OBq>DB_D>JARx%#H<1%wRuX<$lhT)oLc+4TT zd#A{3>LXuvm8*)HKOegoJ{^t!#-o<^F+_;V;>6=< zp@*msC`f{5b!_YD(f~%`Fyc~??$(Et!)B~-dvpDAbF8GTgV-Rg?_nZdILMlsL^N}x z@RB*}d7ak=?0?WD1_|!eJ$8j59`{+s@0ajd(2hsOkz10e!Ipy9391nD&q0j$V-pkqow zowd&oJjw7J&YKYVu5U=GD)z;qx3xR&o!v|W*yB*-LoT;95Bh7vF?5=p(%L!CeX2Lh z@fr^RuIE%QQ{QDoqqB3T!aT!+9ubsWmY&V_ zRo=a*LQBKKDw8wGcNUJfdFirD&26D{RZ<}1EyR^g-Mc#lhJ|R}7c|4IAxB@p%s=6H zJM`I_3={1L3x2a*J72L55?Vb8!q=yrcKa8s57?ub+#AB+9(vJlA;2;%^c$m! zQoEl##6-wTw%RT9mWuZW`r*}0baU`Z3C63}Pe(GgM1Jk}yi>w}YrR0cYPkD z$K1DEzeeC|gQ-|JBS1m*{2fRvUGHU6WDo-_$#Gq-aGtk}h=?!(0z{N93l27s^p^c|=g06EvO}mSgh^Nct`DVGJCZq=BrvXw!RLk0Qwqyjhp(tav zWsVnbX&>$n#2>!a11s^Bn40BujSWGD$ArM`)nRBbkv3xwcs%}|Ojk&>XW-pznXJbAv6pIaf=Y88GNOQ^0%o7#!IjOz%JlA^5za9VExK;6#qc_wke zUojoAru;!L%GW*6GmJns9EK(j_>=zfiW9G|pZ{Xb?Ckqqtmn=m0@K<8qF!a|-BpBxu}@^~9)rE}K{m*5S_HAL z;okk4>qnyNo@;GHo_&7y7p<7=?ce(A=Q;t{2HMyZr)1Xh@ zde|>Y-L}4H1O50Bs#enz_)4HMNI{)kC4#g zUMzq#9!$`FvxmEx(2$-INHdzjT|MkG$zd@HoW^%>Hb~bJNbb(p)jrDk)_i-tdxq(2+ksjpxaK`oQp2~2TtTu07_GIOakpUj~7mq{RhL;olDjCmo7fHBx~t4 z2rGvNAvDG4i8`hO;Syenvc$@|yB!n8B*J^7e;$0=hlS(d8x(>maaJOHSD z*b>ra&uIAjmNm^1ZshvK`%mLj6B-mUX7I#|SE0*AMVc8PvY0W%KZim}UtnFx!c^pm zK&_CNVLSIB2JPMrFcWZSEDrJo@2!+4Eq>uI=I^gNQ+kw8vQI5#s z<_g>uSW*0CltmBbCc8ffX+~{jJX|1CWS|;yiVjQnSV>+4J_uN0_q8j`TYVm8#JqoW zj`b`c^lBz|;TdA@cxdtZUfq4U`TNjl;dK4MYudXOD*)b}$Yi)6=xoGQJ*FYzQcAw0 zyC;qMi{nnxYmB&WjBS|F!=E$iXb(eg9y=X5mUZw#@5sC|p)r6_waOBE^|jo2gL=jd zQZFYZPbeMYE8LlWc}G0g8FY&H+=N1`%YjRn2O!3MO1#AKajc{#t%OvqB!SG6zNP!* zT@kDF6kN1rcvYkv*Wi8I^W=3bv60|8Wlu@;8yZgH7sC2Y|0)BxMDhDgE4Ut6oAu4g zqJCgx*onc(CjRCI<@e;8eyy{2Aig**=f2YZ0Cqdgc%Y*3y=RJWQ-PPrz5jHpQEV(p z$oy-hZ7ev7De2u?6WU*h9LyCmX+tASac+;fE?0o5lOYLw0%6*HW@h|+aq3M%LEbXt z>7o-{7kubP|8r?4GyB zp#uIxVe}n5`Lm`+M^8e!SRWzj8}^81U=^Q%H`$UHVGxQ?Q<87YTl$y+vHVbCW2E^= zIXZDIszNuwN4NlOZNwC}agi(PzNXEn51Y6Iz-~u* z4_akWNsab9Q#bW*$n*Up@g#HKdGFlwK3`__6SvSYTA-vkNrv-8G7h>InC!+>2_6H|Ys@Tq#75<4lRXwz z0^vaeG6%Tf+<1$zFf>ug`<&0D&{PdFMZ7DP*rwO2I4x68jM5H|0)vv)sKFK~g@zjy z_sP%Q@JZ(>J5FRw{a}pqyexyCg~B}yct-5FQ7ENZ(#jBCiYhW4{=yty^q1drpS&>I z(IvEZ*pqKRjstQRani}%U0sn}w8qa)?BRKYII#V_7j>_NM)p1)WPJXuY9s}|C~o(pU-82o@V!8q+Z#<2sQFSuT0Z)ZQ@fbF z0l@$csuJnhqi5)42H%-H`;@FV@Gfel4H7we<#-^;7lt1xh)-v**dBQj4nr0vf9`6) za56+tcScEzgOs}fK2NEx{r1cA-a9$0&2O%12Ak*k@G(;O!;<^?&bMDaHzQpnmy*8C zG-v6#pC;%o*l3qX_s<4OlK4VUscg_nN%y0ep4Y58Fw@#EZI@l84?Wq2S+3Y0pBxyU zXJ1bdvjo24a2X3)|E7blR-%ghkny$ON6#N=Ma~@VBntWZD=v~j@Ux`|%&2x&&9`^w z27or-9wa`#f;vyF2`zbqGVFkNAytR2fZqoqdUmIi0i$M6G6ls;<*cy zmrUK{3tSUYA#RU@-;5uJn^*Kdi9GL;t72`}$naKjn@ZBT0gTD8OY?aUqwBfll^5)I zHdJH6Hdn>Rp_M^!B`GdD!DR7{ytn@3#ZQrIcU>P9$MQpNq0q(T!NrPo$eekLm-Kg_2>?(yh6Wy_p4&OF|j zOsz&-b-oX>1lf;t#~dcF#Y^1iq8HgBMJjowe;tG*k%L62o*7FJ;6xlV7kOHPbBlSJ zTcS8GR?N$zG`psAnbN46#~CH-9NmP3r9DolL0UeTBzGjL(58~D;tQfHC>R1iSvVDR z$q=xiKJMM%_apT7x@Wn2>eT=FA62Q60otBUs%8a6ESY?!9be@OfwBTsuHCEDrn#*T z;zm=ZR&*Wap62?q62FB_Bj{SsXE7BIKN3(0uJ4jQVqe-ZVh-s`w(sjG9(33Z**7mE zx%_e@e?DI4u)_W`Sz|?U(rZOYtHaoHMGt50eVgdReBXQ8+;&7E*wmy`Roca^fsd)r z&GVKwg?jr=MykE@UhNQB3Z_d4xDjeS^}vnwFme`Re!Qa+^1#+qU?mPTulzS2x7>xQ zP;)}&^-H7?&4OdR{09z+V`qAr-mC2?;9tPx@8AqLj|6Urki8&axb~rvQ9rq?WePB_ zXzr%Kq`-)0j?>TgiQ*FWwa76&dSn-b|epWWVSkm=M9i+9;4GWzZ ze&VF2furs8sr=}LX@9%Z`8z=~nY!gh<;C%@s>XFI(OgEUn9IGaI-@QqbA>ULY2dBr zk7&5vy^n)V?Rz2_%qlrU*Nm8@UZ`|W|9AoX|2n;7Wd5MdBh^0{;yOcB_NUtrj7~G4 z@cli!wn>nQ;%Fv%F<><$6os>Om#Aa~{ig!4XLBD~LqAR?L8qyZPa9}XvQD$dV=wF0 zt!zx@wCD&^lt?01N3|fcuHfj6t?O=1nFjuEEkH4wM<08vbW*2GbpHH0pYmRkSK>Hv zv)luApKGhLtySs8(0$+b&gwL`KKr?gh>+d{8!B}R{!`H@LzFpRf?7dDIrGn*_dj;A zH$c4PjIVY(YiFLrKP#ZW`!C?3PQ4WKuRTe_)B$ITzi-}VF(RSN<+#r<{-6Hw39iyY ziSTM`!ry-|cxa3=hJ86Yv;Vh`M#3WzH8QTxwRz@SS*JgF3bN(2!ELAo1h1e8vtkroL-X~_*DDUE;@RMkG+|NDM{*>XR-P7X7xBq2r{hOy!go5gW{CY*FBvD$O(5v_|@MY0g zu|Lnv|M2vrGZ}e7i=pG&n_Q@FM||-TAu&#sDs3m>A8@sy;}SI*a%83;e-~uo2WdO1!Z9===Z$Z z)9uSWrs2Ic4#Zwd4*)bQo6BGCU1Ihys8)>qLD>IZbIf;mF#E&@r@Z6xD%ZR@-h|4p z%)Ubp*Jbz%>tj?BHRk%IZK_&BBCo*;5Zb|FdA@HchFx*xoah;VP^zh@2LKe(84_|Mf=walu86XrX>b z{x!Rph=}vuG2^*YleUHRyABUnx_2cW78G1FlDm~6w-fnS?o2kO4(r+OrpR_rjV8hL zgiG>N;a~YN4lM9t^gnu#PU^BZJyNJDmkIJDo7LC(D_4JxhK$b?a(JMEw^iw}f;bT? z9hYZ6kn+7}RnZyGLhZ_HF-WlLX!sv1 z8G3R-0fEveg0~i54^FV3{pcDE$dL%~gdNR@6h5rq>LVNMCkO9Sm9yb<>2pS2$FpEw zXXJC{e%UZJy`XCk`ixDC1pddy5j3fo!-?jmUoxCkF;U((@^~0ub$}}C zI-GlR(k6Gp19LOrf^0;=a`KC{Vxx^OJbCS%DxK;u*i?gqcZiCBdC~J5*MwNL&3nBN zB_aFcPweQP`fLGeC)gak&(Q9oHG^8SypO;4^-OZ0NE^0E@;zpLAU$< zop+7W-=EAZ9qa$0QZkR|zMuXRkwQslIUs7aM5kY{aiT=flKCfQ!TSwMp0E1TRD(?r zNySn7V=`QcY)UdA>oLyitPg8a(-(AXXRxCHwT*6njxiOTR@522{Q1gI9X$~~1T=x$^dK}K!L(hlxx9;C&;`Kt#I)7paGZ)az6caRb z{&>wKimL__SXUl~J43)8Q@&O6EfnBs zLIz_1mQCe?Fk*~Y8K?^myOMnfRO3?3U``o@*yHuYhnIkFXajTiTEX0Ma-V}$SS)<+ zQ~7qoMkq*b6mHy)5t?Lw`~K^LioWZ1Z(+bXkb?xmHLKdjJE>RvKwu~xz-O~u(?kfy z@r0)cjhBQPz_F&2)&i&<8cE>qus;E)cbD&LAn1idz-N#z>)UaOZUBu??r7KDpF0G~ z`Y!(bDdCRj3uqI%gMq2z9&ik@7(;*wBN+TrUh06e-Q5c$7+8;k*{t{GDC~gEZRgqT zk8M>!G@es@2;la~HU99#1Taomz~$qWqxlmUTT<>S^d*!QF3Nt%*abHY8@P*7^B=gK zp6Q_9uN&Ga{E#^g%ooH!rJmB_G{e_LP23qrM<@J|>%?K<9N+@6Yx$B=%IinJdCog2 zSoFaI)T4Eq;o~)KVb@x*Xh8j4dd6wEw;qoCdh{X0GwjP*)OoqokKT($&L5s69{Uxs z8ck_6WR*PxVH`RMYfy{6NBHPgpIMv)nK%>F5M)uaCdmfA7*NeTUJqSB)Z)=JJwiA|t)yE<<>N%)IAaRyXV7~mIfBzuEqbws}v zG&OBNv`aL;gNnmR_+!EcxTcYj5Pk!7~!y1Q@o~nL@@_u(Rm11*RT%QaQCpdfgcV!Gg}CT1p*{n{j}Zp-_Zk z5x8W+ZNN+FPvU#u(4`|VM>PxFR)M#Oy`G>je+ZAR1GX2rnPz{hCYM75dh_M6hVN_; zhHRNplcm^$@d73Nfy9q!peJWWoq7fbPOn`fmUI1uIl&`h5c#EzxRkzP8K|G@8xNR9 zPKZF*rL~O}T@F8GeI@heNS#IStJ*Jk1|`V!DC}08uLOEG_}d)0HX{WldyCyB;8l#7 zDa5_PeR$!#IbKnboqFNOy@aeE^aByMt&CBeL>P=&fT%J7yb>;;9Xt+Lx%2V&UT@rZ zul9I_3Xm=bPq!!E+vS&VkFEp&+gK|wG||+JwU>B7~FXw#PbGCXSF}X%v?PLwklk}ec?bgK~P$ zOmaiWB4sfSkuA~?c$d`z_CHdIiFgc&%@;f00DUfsq=~nKOUrVa@M6+ZM_h?dA3P*H zK~i8H=$YVstXMJ z1CWuY#TE~pGobQ4i(K1>DFmJNLLj(LGV}sz=^?=9U6_5e%`pb9o?>?mm>LN{zANVG z&W8ZR*z29hBVI<9qkB`e!`bF(DB{Fy8n{&UsQtrkef$6nh8!WU0VW^)~g?Fl!`u3L-*B{uoNHA`6rt$W}^E8aC-%c#yM)@@= zON|WN56DxanWCzQdBVxa-P;}IpUB0wW1n7;A#rW@vuFT6m0KTSzo@if5;$41uh8(q z+g6A2V+NRML=nwgsmkL15JNsCg5$ue1&Y5|JBQFui@B)tl`INj20-bPC zwnOUJOot36fuZ8f+b|-6wG&4U56#v#r{*0BL**h%$woC3tsZ}EpAXysMHE39uz9_FJ36~hDYE^vZ9jY2E%M3loi~s$!-!WQZS_L zxCN^e;F$zcUyXqo*V8Sfp=e_~^jXX28zyPxyy_P&l2GF=fqRqtAOq!N$`AY6?;g9p zQV%g^E~nZ)rz{DxoZ#K~F~#_B282kH{{XuVMN_nlqSw>_;JSA8yMVqV%b&KD&jT27 zn>OkUiv|aCY_rlHooT>2p5D-3HFle#c*=juM6=MG{mD=b)*9Gvew!>{fsyv|SCJ=u zmA+-i>+5hYAK%RaEoTeCWp%8Qw^o-G)W(adMk)`QkC-*Xul9T4*G?9fgwTbZi!^0b z@<-IAJ8IO(@bL+8>ff|8^Kj^VN{bNCt78UtmL3HmmbTAF6Pe;YfXNc=704%yyT8mU z=8>PI8)*-!WGKO`L+8B@4B?Fn96A1vPIOh*F!w8wAvZp+s02F46>7{P)cTQ&2%6G5Rx zpT~DxW1p}}(Q^CV_XOUj*9of3XK@&Lj4Z>k z>lM8rYFyf%xuwaZ`BW}8>EP}Tfj6?-p8#fzl(#_gwypy2BWw)zq~6%{3PUUR)xZE? z!rE*ivhb?-@H#74_*`ju)d5(7)LP|IR<&$Yx9f|Z%>>F0QnI%J1_ygdpa6cmK)jX7 z8@QD*A-zlYA&lm!uk|#{W%nz~b^NRvT*+37Kpj%ESkosZNL!63jYSx{nWq})+H5a; z@FkCDMMN@A@9K@rSQaPyt%w;vxHoA<`5P&8pl^{-X}C;wlBms`IK!+PX);&Iv*(X+ z%IjR($d9d)sI6REnjqV_{g}n<`eVxrHr?d1x21IvQa7SbKfPgkFUpQAu&Lt&kB3>#z!&g;J768b8aoTvZRXu4s2az5xU8BYI{N@Y2$0PVOzKw2HHwV7|ZRtBA&g zFdbExsIG_^B8p(~#tcxldii;nfi zcsmCOZd{&JO=ZsJ2M)=INZ^)XEzf?r%@j!94V>*HNJEotvFIIHgtiyO8&I-e;-bh# zTmZ*Xtd=aYN4>R&yn&4Y&E{to*5D)g;O*~?gYHgAzB31d)GvI06_WdC;M68(BA=b8 zUwsv2>G??l@4OBaU1A0ImM#bf4S$AH?0?$(u2wV$kZZ2y`B0(@dXmCCp;;O=ipAI4 zQ#S#;{~WWfeo1eomVr>P63W+7{k+Ch`h+1#l26?O3*& zu#!#w$dUAEO>KKl|>%9muUhd&_-7}`td(D-{+4|w~v*!!t<7?+dRiYWO= zcikHbuGgv?YXlqhuvpw|3*LjkJ@8&EWAffk*2;&(KT|3YbpsEH7U{rb0{o{`$AGdLQZocCm7G0II0Yls zomM4JTZw{Gtj&FUwLIYP%l!&TS+N%wa;Yo_q{*dGyn4|MthdI-E6mI=-!T*9Digyb z-$XgzS@RA6&hQ?+-{j5GTB^XmkT<;9%)@9xhDsR{ej-?INzdJ8ga5FryffW8R)H) zU>E{}1y=JXL2twU(pB$(!wIq5NwHkVAJ*OW2@o~)?WZXguQCY9((bguG3YC`-(=-s zw&J#^tQW*V?6$UpQOT;P+)rV~TGW3fYRZ~^JQ%k~d1&fo3HPl((m49)c9iWzsgWhZ z_axWUksj=kMc$}>Sx${xH7#e4;F$BLf!=M$erzUNw#}r6b_AHq;C;pm##W=E4U~U3 z8}#xIf``_WcyAxHZoS*MJe;lh&`WEJ@>vm6ZtxtFRtxl)p1AMySDSAb)Z_fRjW86r z-CChj^=HN0hm9U4t3$bb*jTN$7SYzM2P$bJXTWH7^M2xqUmWO^kjv>#olh9|hlrZ% z5}iAy?tc!0;vE*MP@BtOyf_nY;Z`}E{<>Kz^asP5rE30af+1u3i`@x%(i)d9eHDJr zQVUF1VvefNA%^%~kDSmf2**D0GKF8At;Knf@+81?<876OM+?Jwz|W05#hAzu^4S-L zt0ywQ4kPHPOFv|8aWb9ktvT~iyqUS(k$$q<$C;wiwD^&oJf#ZT*#tF$Fk||@v2(wWyEysX$Tk>@8vXzYLT{fxDgzr?AzH;?6rG?&2 zfxXp_=|jkesE5(g0OV>dpJS3s*NwX8WxmLCxmoR(SvWC~)ljo@cX{`tFOTsG_mQ4z z_p{VKoV*#wCgBU_>?EI=#Im=5 zmISXqV-RSK1O`a&Ydf&XyA1EF1esgxD3maYv7MIm@qNs_Q!l#~vc4iRvX4RDbDww( zcb;>!mS^ciM!mZGPptZq1m(#yVlAPnRDEt@brmzJWFv07`}#lK{c8>HPTXF)AhJlX z!F%Jhl%yLQlHi{rXcD@@y#_KJ4EZ##%{vZ*n3CTpo7mfNNk}&BcQ!#zN=rGXSlrq#0E z_J2~nD|YG5Hf2BHildd9$xUJ%Blo+E(p z(jVAl-0vw^5ES<>TMC(;Dqya^YvER4@Nbk!Vx4K+y}|`GBB$mywq&tb(5LSYY|21 z161FHB|=fR8ji_M4G5;gkpjB}&eS0yrqYuFNC5-CgLvFzIesdTA6!xnmjR@jJ>qEv`^``5?#&wp+d z3sno=FF@`jCIQo^Vn}xjHp0J^cdA>`ajsd$2lL2lr3(38#A8)6Fm5;8IirI`ue-yu9 z=fP)t{Q4;U^^$KHJMf4|+WwM={p))F=Su{ioH2fx*m(paX;F3fW>5Aa$C5*>8&b>(C3pa&W`O0}i$ z5g|f=#Q(3CGx`Mez09Wn&yKO4kjPS8;Q8no;{O|!`(J+cP9fH73Cl~B3gM<1eUzdW zagGB{j=!>O^-=R9OFM#R?`{-vD`O+RC=C1j*LCv8H3a#n%x06hM9J72+UI@y3qJsD z-~V4}Qb5?U(+dBUr9f09fkxtGH&|cJ@?Y-J-(T2BqC2x(kzFx=>6>i{02e&z#%&+* z7icZ|pjY5ZqUK%l{?a%9FIGAzAh-p>oq*Z8t=eYf;>Gzr4>q7v_5^7^Ql@;9B0zlJ zqYK+F+<0^M8rk1{S?`|*qB?QGZwNsOhkx1c!TSFqGYiN7tkcaZ&6;9O$rEP#_g{2P zKw%zP)-Uvp69KAlms9XJXpo^!m)HeI^S+CQlw;%Cp%BLPZ!;?MQ z{@w;sf+;S}_6O7UlH;LiS;3GE#j2P2VK38NCkQYd5X9Ke>4T z`@P%2>SxHR((Skn`ZSM;YrhVK)W@i913si*|Gv2X&)YDniyD!#4RJj|7_=0nAmpnY zK$>04&^WHS*=Gd!P-Kds6Np(Lp%I?6&fA=WgYY900M`wcYR_mX(U1bJ=tJrcKpljF z93cglO8qL!2|vy$z*Ectpbyh+K>w$i@(-nR37>;-(-m;P$RUm)@X@UiqmHuM8*Ig@+wURzKZg1j*X2YOUoaZL(vf5A&|XU>eYIo!Iwn zo2eRKt!m`T-<0ytB<+#%*c?Cho@+#bh5%t(2>x;86-(zFh?m^dafJP+vo*(q5NZbV ztY3yVR%63RojwDBaHnD=o(nf%7@ z80I21kLfJ{xmjg7gVoX{G{=A^-uhTu>J|SA?#Hyf@L?bxGy^Go7_ItNnWVK#3qamW z7Yd%JXckYqGyLOj!vN`-mDp@XaDs-|4eS-*)^dV}%CAZ-r<(OV6sNx0)pz*O<+Gy* z6MWf78~4E{&n8X;vejkBD^y^}Biwobvl4zdx+30ZV5|q6@z4h7+k7=L{ z%nW1|4X8c>LkOdd-g_~W0RjFl_3p$|gL%<`!&d3%vMUFm|K#?}bZA&Ba51Llxhfit zFFgrUqK18|SjqB5A7I<62UxTzReb?S(VG|oZ{GMcm~$rO^OL+mLv_xA7Fuw4F@X#( zsU|6py#B0r|9P9d&Ws|925cki0yN@Jhd?Cu^%0mMq5rOK2jLaia|q%ZOOWzPt;FM_ z;3ITunrbMkUw+9>#H8`w;hPZT&(V4dp9>~#$mV)hnt)h+Fwwb)2ZJ5z!TX2UK%Z%>X}O zH=hB$w#mn9ab`Vsx8C+q@NJ3;K|L|-uXYhh|2 zW3cD?*eJyFUh4*YIXiah=!<5LIxwT8EJQvl9w8wleZKya(7qI zKXP+$bsU!}g9c>UXt^u41Y?ZhgyuZ>w2;0aN1|bbSspCJW+*m&m}2Ovve>#3Zx_@6 zdjzg?pMw9o7F7@#LyF9`?vtl%k6&mQ5E^(x418YE=o3Md|9t8m|pzW5gCf*t8xg;Q9dG6Lq6LR64jW3Jj#X zfo)Vjm$2)RZv>ayZS6pgnq3&-x?t_-qp`p}cI7+L33;Rud^cuZt0bf<`j~oQGhz}m zQp)vNIF455Z!pm0;=-amI0510!u=g$qJ7Tql5gGSyx*rW?5$J|?KpYTKf#L<3a0P9 z7EUqfG$8uO_x*<}W#n7>?T=S>33rSU0(=wBW6?Mm<*B54{h?3Og-A2+LwwcjHV4$aVc6hZq-{R z*kP2}!SB^Odbz|f5HUVkX)K6*$YW@h@2V1ejRC&9sL?(Q8P6O;VfObQKK{a<8viLL z7@U4$K2xlZ7n_-y*|KqJ7?5T_pB0%^wn@+#{wh2VvoXVWfE<-KtG$~ZZ}O@0ON!iv zx$eiowf_8sS9$E@4H1*|rSaa?{mVa&w{#G~Tpb$zA$@Fy#22-OI zwIgd3rcMF<`f4?d&8n2)f~Yff3}T8DMyB8=U$O2*&55e8>!FFsauA2s=(OeCx_^uD z-E-K}>n6fnK8(x^w5BTQ*hy?OWy%N!ld7d;14Ws-@r+l-{)hMAH6RL=AA1OMB3Or2 z$5{aYN^>u;eEWU(m;251Y-Cm4N(om}FDV9XL}m}Y!FnQlC&V>cATa;%=S;L$-w*KG z+-R~XSc(*3T)k%ww(pc0knP4mYZk9tl(h(VnYQM6yxaPWylhE!Os8Ta|EA%v#qbzx zOKG+vzGgR;{MKA{6lGW7ir=0sAf@{>DApDqSP`O`9(d09lXuL@6E0s@G5%J6s^X+t zD?vGmqS^lSb-x#wbz>4Tpx{I2^#&*!cD3v~so*wFVwyf_=O4NFe!TD_8`)B=`DMnE zMm@n|M=r0J7Nf1hgIpHaf-jrSWgzJww(%mJ25#srLBiMSVBY zh2XNVenMgn@i^NCTKU)QR}&7}d&zSt%=EVE{{K(?}^<&Dz z3)etdVfuM$U(r;7T6KbrArk9qwAIbfI#nv{5N?gJvDxrIAnQBaD#=hb21;pr0T}nL>GIi#P^%=dA{D_ySw{io9R6glW^MFA2XqC(Qfl;I zlVzX9>mQ#{=BDg-!M`T)^(;K@waOA&_|i)~MXK+b`SA*jN-Nba$xXGfE;Js#)-X=z zCSz8eveZr`l{pBkn$oC`Gq3FD?HAt=katsm@!kll&k_}q9B(Dt)BF-wC*x8{@<0FZ z&z}1?!c@f5z~XBfUCL02*GWF!P8*C#5v{e05%uxv5JTuQ#qzhGkRHqjd{6K8CUY(L zbMLQ{34m!5M#k`1EE+Xst4V3SiE4%?*?>i!1IM~1TbRGVelk&{M_!Cjq$y2SQm}HT zHI7Nh(WG6OJ>~fPRyxHzdsdIlV23#sDjP$}jc4R6a~>0Pp;p|CB3UmmE&HyiHGz88 zK?K=yb%B4*rTZB}B0mP_yq`j+U^#Gech!5q=wbW2ldNd`j4V%u_Q#0g#VTZ}cb&OA zpb@xGGt+7J)ZeskBRv=os8|ptqLOGT15%wQg=N~SFN^yQ??&UCnm$62<(O{3tjEnz z_O)Elin%X-h&-96o>%Y%L5S^kz)6$ZaMKJj@RR(2?Zcj=jih+Ho!G-_(grEVDBUy( zsD$1w7B_lqUB|$mO~W@3adjxfz+2P>%}E^@_`Y@OGB zOTs-lv;pd^>VUi7!24h~S^!XA^_!KwreUE}V((nhKxQ$u_;j#ID+SCOShGT)s?viD z^IbchYboDwYU~N(X)aWToc{o8S z@AB}xuV(~Ha(M0#lA2zPdKx{N@ih}vYj(-!r*929UVajm!Ik$mlJhXQR#iB57 zyMImaZvH!g#C%5zYt}?5JGQ(SGZ?7&xV`VN(ME^gp9-*kA9aP1)L-;xKvnPR$UI+E zj=TY@Muy%ECzGB92YpHcv+c*-*cCxhAMSa5B8hE%57G z7*QOBupTDxAhbwJ?4Nf~ctY=ByFy^K^&X%OMO!vwTpR>bEQxv-m*;};=ebb1T8BV3@*VCzv`kDzV}YlM_?>_g zr6~m^@-c5XuQr^*)chEX1SXb4BEbA)7svuZd2(&TkzYcH?h;%ERm{43@izugd~c7>-c$ zDUL32nN?*glJ9oHFR4+suUaV>mc%AAZqH(+hessAY$v3aI1JGFZgIuy{t)q!PUi?@ zi2mvxuKy$Lo8wo}xGKhN*E5KK_rSmZBOxQs)ZQ|QX~j{uOlmTX%+toH!mhY-OqGw@ zxu*%=)jR)Gr~X@UL#*{n-0_v;dRi4TB;2IOds%iS4&@|CuoTR(d2=UoZSdNdnZi8x zVL4C7cUgILu!*jVp)`|6Y_~)$z2v<%{W8Zk6EIw&s8i;9rf9~cke!=2(HR9eOx6HVGtlaJnWO(J?8g;&kdkb{S`+PiHQ!#AL-5obD>Yw=D@C3%>?fc)WR}(Ok%9y{U*xVh1;`r1TJaZx(5LHyw{J`OLc%?Ido`a}7Bpko7Sc+< zB6kk*Xk7zAA%7g?>ryDowdPMWwpr>t>CNpFLXRk3JPRj#rIOQ3OyBNs`ZtKcv+Ri#?jxA3Es|ppifwT_vWb`cpu110u*a$&|uRg$1%!#sm0ijKmZX?W8 z6dg^VCkg{?9$V-Ib2Tq2D*e(nlu%Uz)beqJD|$cTK#UGpe_qQgoP>jO>!4w zNcR+a1=W7rP#3rKk+b67l}Jxo)YGxImW{?)}@)kV%&(!;!JEr`NXmhR>5Cd+L6}A&P(ldA4fG!JvrtQz`*LL32-I6H86RvGVuWKm2)$sW$aP4_p=4jW zRIpTCcu?n92gJr;@-v8Rp@MWjsZOAR!3F{H z+J>4vzrs9tP|&p&Pp6jgUz>1$XI2x)Uh;F>;19pbLoJISehHkDGZTM%_3)lu$rX=S zmKz_8TpqD_z5=>k7!;Wviqe$_1m%B2#9E}(9t{L|1#AP4f^`mBD)AE3=jS!0Rbi#` zTikvWY%;0G_7K>K_diC{N{FSWElquXd9ufQUt`is@9HT~Xj`nf=5qHG#CRJGyPdIm zQ-rin-RM3O?IVVgQGj>3k2s@Gd><$uLVz|U3}B9ex=|wTP~Qi|p;}BS7 zA{b%}X~f{Pm}vzY9OcT2`tW3p1C?C;K0PGhjhRM7C^^Mj4N4qEGaM#7p7|3eu%)OX?YESN8Wn zD)NZS)mJz7=i);>r=j*5^BJCID?217;hPZ{gcI+VKjl^ZbPrg86Fv}EdIOPljENq= z0^97fpeB){mF=+vfV*~sH2V@y+QOf#Z-D6(x>FY4K$HdbGzej_r*IrN0cgidl6{B? zw?qy82nlQ#uId(drXK-aAa;|_AvW%&GL=yyTZXeNZsQaXiX5>1%`a=+f(FHoco}CO zu&q`>N(p>)i=NQeWzQAezv^3sfYIBxGC-b;%XQkH%mCeU5{v5nTc-sbSz~B~9(K2< zt{I-N`5s=+$6x~zDRk7n?}>f&k)G^xl2~7QAzeoBMLV$$SSXDBV2TBG+TQaJ!fwz# zxgNBC2Or}qDkp(9RBf`a@?Mh4B7qE^+S3`7S@PS#cUYr;K&KTgK*rds1wN3`)?juN zX8G89fmO1AXz)ViH>E=vpLe_u^{OWIO zEs78L!fL+`pujpjC}7hEaY&&`_9F%AVD5%mOUY3svdM4XRzZ*bQg`e9gIus1 zz5Bbk;mZjiwUTaXPn;#$s$r## zka$CyqR*fhwiWU5Q}=;HesT%Bajp*pl8cO|q2S0@>H~*xE7W~c>gZVIZULyvAj36$ z3-_gEi@ReYHDhX~ODE;To9CZ;uXq;l7r)b8)w6AA!o>~%&GUR!h|Dwe4w+(HX@}7w zt&b0(i11vHP2z%7`Kadj8xHJ4M?Fj4r3%5L7U6}$K)eltxT}^}sTRNMYQ3UGkiuLW z(bn06polaOP!=lmj;sWU`AO+w!lrMl_+I7}S#ahFJ8ow*ob)8dWALSshZY$^G6yiV z#M7_hEhxbRH3P#Ky^w#45>U=5Mk9Qj=E~yG_fA!w^_>UtqjI`)8CgrPpKb{b>;2Hu zFS(s|_s5Geqv34;F0rrjAD;kOIi~z8S$cWrcaix=a^FG3a*G-&&mmMv&4Ik5;vQ~G z)il$tyV`iBKUrTzNC42xB+Gj_!W2H@$I!QL^hqQRmzu31@Hf?6q>e`Dp0Ju6(*9lF zp$v8BEm1WB&GyFsaiz-tcBTHYkpJA}*^^oTnr2|DyWek#8gAYeiiZQTv#c}3*lI;F zEg*sLF^HUL`^8hhku9nDcf_*ZfZUb8pIFbYKN0MlvaFzM1|OXxOdFCtZg{yi< zc9+H^rN~^u_XjTI^ieTgfPw^|$rKb8Q8(@p&g>q9P#Lx0W<0vh+HSq>2iic>*Y5z{ z){gQOc#71`hrw$9W$pkmYqv^`!6)Hs=Nl2G@fbHj;W?#ptYgrak+=TTMj!=u2X28` z7g8~@c6+qllG28o_|#U#ETu_cipR|?j&%#{BEOY5=U#9cc#}Ax)Bp^wYZpbe;|RK^-u zc&V^BhfFVqEHVeGDz0rm_*bqlXj1wLfjwFuDAonA{nUCBD5euY#=81}Gt1qPfQ$X? zAux%AU1wR9?J4mZWIH)n2O_$vgyQ*(KCnq|UjR6o`~o~%C6v8jVhv0v6^;aRVg{nw zKBWj1T#AC1?&|z51e0IWgc}ozpC0Yd2~C(MAi)V60>zny$}3`W=T9F<4)+GD`vJ9n z8^AM+QVVHBA})z)pOr?SBno$M>lI=e<9IO3BHz9;H9(@ z6&~Uhy_!jZ%J!7C%LQxziVP{0!RY;qOVT!SuaVkFLM++K$ zx!iNs99U*6b~To5uM7OaQ4H1*hVmr(~I-ZJ}`@9LtBJU__x)=w_gbF zKPA3?0Twnk$l#Y7x3B6aLj0-TxQ+{GXmqz?i93Iu9DlxWzqYEN(A!rAb=n2tHoHY? zxVeNps=@bgE1+2~5sa42?&}laFsaOqfdzx}Wl5xrr*#HW`Ges{U-igMm!N_Tsi!a` zZ*Angl$7U1F7Wz-Uac84XP>JM!Cl6L1~=K0>mwJ{mNyhAETz1BlkYm&k}r{^g{Ars zJDIgB;G!gWTPK`EfW*(FbwH3pNsNr?siONBPTFX9;N}S@ZxS33Oa*1YM>oMNKMM?M z81P_NG9OwW@Z#NwtfBKm${Z-O(3p_K&6(T2u1>j`8uV) z=$&X6NnETihUjf9|UeQ)|i`!Y{O-i!gcJG7Tc`?C(!Nz!F> z{YT+XB24 z#MbnF>LpYrc^ddXDqLK%Z$0vKA4Q!sZ;Yyi#e`WzFxa@;UK8p5DcqheM(N4eQ z^sG3T(R*l)MHcbE2Iv!MOG-9f=18MgV~b)_xU%Xo_g*thk%5&OfToX#H))0vC4rQ= ze2SrhhQ|TTS`V6^0FtjnwNjVi&i37p!Rwj)Th1me$ClIbZK&)@obkVI8IaOLhESS> z+0RwXoY$1bEtSZJf@cC^h(ZaulQrrdw#!8$KTtenU~u0Ze@%-J+m%*$&h(m(A&zfd z6QOL@dLR`Z(8r=WZrsWj00g#tBrX6=ri9fb`ixlUu_AVV#T^1}AG!%G@S11nc#&?K zm6?RYUL)L~T78>r?v-@c$%2z>h{%$Xj~a!nOs7>*n{FkL_X~wdCUI!~cG%u%*U4;% zS5|me$U3__C(_8JgY@cLG0YQk8GV9%eCZG}K=%|02xaPPz6i&9Zr5u@lggNur7`W8 zOpqC{YyTNCW_FN|v*}>?GY7mo%oB3CK&!_(91ZvL6yab)7bN2R=c5sNQ-|oW2rG}k z*x~zipjBVk^#t)l*a2R8M3Je;`Y|J42NHA`T2y{Qt;4q({aDN{P_-J8z+i!CW@H2g ztvifU^KW`Gir4s60QSbT&d|U($)~gp66{D;@HF-on^W#O-3_g`gAu_*cBi(0RTNjS z?+2(hgaW}DCmdb`Csqq~4f6ifl8 z1+(W^@Wu(K6VT*p<;to#887^1u&HO=1+mGx`W8ugGvK79UAbN0SFwKtPO*09VJ~90 z3mff8a43)!Mg4?1Q#uFUKY{EdWh!tF%0Ouf+f5+Zh}+2&_l(__tja8(``tt4Fu<%6 zv8?#tG0>EE-3)3_o}EZBCIG1f@1oRe`~R^+_+6NdqM!!D6%+eOg>do{QXJ|}M0Eh^ zvc5c7=d$psC~`b?5cz;*l&6)?8-R8ca`2Vjbd8dtY4>T<-qD1lFiljiwjQXHeI<>h211M(i^x)Ztw`sg_(tm3&i^6~;X=6_1@cBQT z82_+3LF7rGx8n{Vfsugt=p^ju(}GIV-yZ;z9qB{!Z&vbe$x9KiIe2=Es0+Uf;i^*k z^F#M(pvG9O41BNsd-wkjK)}EJGf@Mmz1($|{}C8h8x=jWnQpwr%+5CH}h? z?N3FH^cwj1s#X>n|LvRp_P45%0#SGDJ9Y;C?Dv1Kw?F^(pTG5wANc>#Ww&HhJ>G%t z2zy$K_Blpk##r>9&*I--JNVzj1RxVThnLablVFDQ?mk{RwDJGrfmwT@S$0$m687Wm z`0R%Ng}W3ab{jm~*esEBu76l^|LLE%;HcOLyqyf+_WybH;Gm{q0ht#{ni>7Sc-;T= zJAZ%v>$ISP3iTY2F#QWF0k~B`s7oH(faPzi$$xrxe*ICrIB+A|#8+r5{>R;eR?{6N zuzbIMCbp{jD|atc3EaK8Ot(0_zp&Vg^3ayw=55tXfO~TdturQ@5FMYEsPj^+;%Np$ zwe>WoyIzIAUQWju>9XLn|8=R-Ca93Hf2s~x*hvR&;TVv3S)pVpu-r)p0lhT_m5;4Q zY%8$SMStf>6uO0Y_tOWNqFuZctZ{+)lg*~PN6ATAmoY9qnuH(U_m>vXTU z$TjVTc0s%EFtlR=1&F{c+fnwu-*FRfT+KN3xWK2UB8O_NQz}U0@qX0iw>C7WLH^ND zj*JO_{^HbWMVue$StQ*nZaN9IF7+fCqzMA?KH(Eshhr; zhbH6aK~8N13zy*&*E5e?j-8Kp))-B8nrA{Jc?bvwpNLA{5<6|yrF*_VkBbdef}Vabk1hEeu|qok zQp&q$W~B9F96p}uY`L=C!c6EMRG50a48tWh~sYw6j&dvBU1_*IXI+I>g{Ftr;P z>6U*S9ZYX+*EU(&Z#kTe6RJt$QJ1W50yErBVr)KCaHl;lz8F#K9&4xrvx zK$d_5wbovs*S5TA_7tq(pr2|vuTI;5VwB?mRIK(8-~q(3aYtc=nH~?MksVObGf+mV z9m(W7FHN+7nyi?-msPO(x#TOaQIAU zjM`*Uzof%qf$fq8zYjC<{s&2Z!^*>W2J;?s4Q-=ut|w$B*|9jGZCS?*@VamOxtGte z=4w7Jv;{ASCH88b(D0kCu!WV8h;d75zAZb=lxt1n!5#cI;2OfAr=NF-n7asgUqU~| zY0vGv8Da}Z%pA>i8DHWiXc`Fi2`c5WZ*IHK2+uw53Jty7hHq@W7R~>8b&UC_?W==6 zrp)1MD+J>5%T5??fqzu_EZi;8aODz=Dw=#gby9ObH{N0qKxlpWsDy(+F=U z6M9~3rVH*j0m3xeKm-YF9_91_r`&Zg0=2*y zC{vUC0>W!q=Z%|Bb4*j~{#*2gu22)#Lrt;sY{O1%44zdmwNTf)7R;kl^MU{mp!mCA z8VhN8rohV0RZkba_#ho?*8D!n8Mh;VX$D^fet`PEYh-%#1t-7reTE1ze`R{le0+He zuoLm6kHG=>;z}mIQNWqjGT+P2JN+J0z{_}ILCXKt{sswb+U7Jn>2 zq@ag3j|)WGZzA?k=kj-~XuqdB4UXAY>8zgiw3$G)T<2v{miy?jO&Kwvj@tYGc_kwLN=QG_zb@Tr@5D8_Q7U(rHAF7lSkKm6h63v*V*)s=_2PbZil?OULpDE zMh{1-?XyQ}6r#I>&T}PF3c3>87h;BoKL<^QKB-Iy@%VID%_a5intCbhq>5fBl0P9z zuK&4@oFB7Tb6Gf$;x+zRJa~19J9U7*sfo4z=+xeCHiUG3pkhvNn%T{0_h(jOy|lN7 zbL)syup&7@blHKZdacB*bb*ZO<<`0KuKj@J{V)9|-eL>)nzzhm!WyM0XudYAMYV{2 z93Ol$h!@pMEU@BzZqOXvn{A2sKGt~B+b4*$8DITT_Uee2YItl~T&j-{^Pkd~su=fMcW41u*O8z?#kui0F0+yrzXm$OD}ImN*a(qMU;Qqwhg! zeYi8-v~a`}HgL2`df!lMyYZIH-PH@ipkqLZU_!FDanPMET%ks5VaxX8$LD>024%Ca z9y!%aSk3{hcHEH~h?};i05O)o652797by^7Fv5r@twPa0080&{i1`)IZj!MOx)v~e zh)W}Oi^ONZ8K46lC{ps3-G|RD9x*w*KF3U|X4(zs%9o7A(mI2NV2~R+%cSy}$eOqE z3G7URSD(Vb7u*$ebf%>$0HG?r@B{1*du`gODq|iE$iJU(M;F)vR|k1ycQf8KtQTu# z3|byzkP}A2(ju~C8m)PFqJ#1dgn#qj(sIm!U$qi^lup?yB0imjyh(s$I3r&!_QHorhChx^V+r@syrBZ8bU24}g zE2R(F9frv6MAN_YVlX}*C@R0WyB}g)sD_tqF1yD_TlxaF%DrAAf4uQJ)}$(b1?Log zmldC^IAE|@u1)FmJwZVB_>JmM)LnVcItNC7Fq=#*erWBusu-FeWXP!|*~}E%Px;xy zy;8K9iHsZZ>9W?79Cdb8%R zY(qQAMwxHMdPX9t+PU=IalEelex+1n=`ibNMxxWSly`>9CiYw_-ejn@qFQvXV>Vjv zeYMKWOA*@>=Qc|k+3fa|S<^{HHk9-8?4M$Y%KKx;wV>W$6FPM~&)6FehwE6scd4Iq z`6Zo^xLWzdnWvXglAh^yK!21hC1N_5`ok z75>;F+&-S~Ldp|W_t(h!XAc+puO|2v0D%S1GhIVocjc$;ntmbzyia-?wOa+d2^{2! zIE~$>m-%u&$P%qeI5sJ?MlT9fdAh14-*)7E)HlCr<<#**po`-4Fpjd@MY)s5I7eaJ zKPjX#eUv@R?IVjZO9(^}9<1(q>Pk+aBue@N1{w0jm|=}hSn){qHfTM%=gh`_63L*C z6m2z2pw^7#S`QDgvLnD)3`0%iDAKoql|5>rF(b~kKd>(afLKx>^on#hRP1Sv zPDXc)y_7jBdBXq_cs)9{7-nC_Mx^`z=jKeio5)RhTq+^XX^Qs!%2EUK(*;E-M|M+@ zKnA%`wBknR1lX7uG*5>DZuxN23{-+6Uy|YRTB^vGEv}AV$Wgm+D~3VpQv3-Nxdvsy zcuf!RzYY7~eRBOC=cj0m08H8Y@6})4x>1@=MKvMGUw2X}MuYUk$n z;>bn>xFI>xa>VrI-@t^QV{L62Bh_2N)tVvSO8y^rZy8qQw!IG%3nWEBNd=UW?vPfu zK`RP^w3J9UNTY~~G%P?`1q>t>N=OPym!O0!LMf3B38{BX_jd1derKPvKmM=l{o;kN zSUl^Q^BHrDd)(u`m5C3UQZZFUMNwmmL3plV>EzV9&HaS>(&PBUYW6n*UqW*aM)))u9$r~Eks*vU=|d^rE8<) zspk@OT+O(8<@>Ptme}Ixd<=n+w)R@3wByA&YbDijKxKkVwyeq+tyU~=)VQj<-Mr2QknXy5Hl;2-TQDH``yU% zuCCemF3IK(!G6B`T0|-FHle~w0e!C<)Q5XljbzEUb`IqQNH%TxZH6vv=LggijNU1{ zSVvdxMqMPGENB;nX$z2X2w9gm8cuDh7>ps9aL;e25-U+l87I?P2CU{gn?d|0u{2E~YAdX5+tZWZA(}011f{B+Rd4X^YJ}xRh zdRzYbX1iG`XZejQ*)yJ*Rrv$i7q3MNh_4-Lt3A{-8lkeW;4_qb=!bioQh2fIy_px=;;Vk zLP9<#hIN#KD9F{*6%|N{*A?OiG-anAdmTxl8uSepTd6pEJ_E_|MW3c`l1}bEa9%lx zo2#bGtZMnxzC`1hjU$9A?LMWvQOVc9vz?as5l@=eRbP#fgk>&=t|J9%^}?2`-L72N z&!OM5wjb{a9^o`!q3wK(bd!oK7OXZi4Mi;PFB@5#dW&6WN>DHv0PtN+l|43Ey~ha* zhQj6|pUI@0T0XZg!OqAb(GV)`(N3P+*ku0YSEz{>VWW1&p|}zXw^utd_pXi`n-$mSdNbElc*DcX`>x&L~;0u2|}XHxa-{K zHE&BWl-a`r6^O!&9}%h7^Ny~x1LrfS2YJ9^)G^BVcwr5S&B^;6KQw7Y%7vS#6rXx-7EE=$812YZsqrinZ@87IqGIzY zILTQz|J(2pfkcbSF06WRv5nldk0G_QD*If&b4K}YH3#g;@J`2-Q*v%b+biv?TB|%A z^WCNywtu={M=RnlMy@Or8@H?=-NdRWM!rIST`;!BOh0@vq*_h&%td;c8f zRj`W`DyBcuEbjMlF>Lwx_60O*?Tc)l7H#E_c&CjfZ!eo{?#1&*lv8 zQVEK0uGz*Mku!;LPno-%&bh9RwJZLZwJF@Bv1eH-z0zW{uiZ9gM2M><=Wv;CYqOs~ zQ{JEgleOoEG_`l8ejWqelbA)CbE>!5qT}2(4Kk~`*SZ@BTUyyj6~%VO+jLV1^f?_Xzw5HQjw?0wxl4$hZPfgH z^)Vbt&$I&XbKruz1dMPY$K(P)#PjW9Llf>{I4IO(ahwG;t`0{Z`*JE(WXCPTe!c9+ zVx@&bG4Vn6#B&pTO8qP*GC4<N#$E|A-|SjaX9Q+o)bcmZsju z^AqzQAg{fw-Ruk;+56@rE>%s#<)9eyu7-DV9$^>)W^G>8TOwA%<~);>3oZ^F)N3{z zUM3$2gtKzHMZLQHy~ncsg1=zClYfPIR1qt#W$Bbwt7LSR1QKbS4*ZS4!Rz5-1^1Qy z%Q)Ms;j2&KySjnp7IN<24$Q%v&9xe%H2k7U84vULsj}&pU}CiRYIjRexp*#h`%`Fg z@vPxekzY)v*LVKdowTQKrwY>nL& z7ZuTT`c#z?V^Ph00#7pO!1|g~zi^N~O?)O9)3y_Bkuij|$Pr7$-gUkmq|!e)t(Zvb zpc!1@GZxoqBuTEUlyE;@*8fv$?x%s_Eulj@eV0X(#Z7iyIg<+cxyK*&8dvDZ*v3YD z?s=R$|43yapD#RPE=^_Mjb0xmVJ6}vTyw<`7w@jxhTW%`-OXv$^EWJFG;Rzi$u`(c z=jUwiZ+Mb5Hz+%dK=Y|YR<^Xuvc(trtH)GscZ`YS>|@@j*Uye{wybV+SX-G{ zmndv*;dc(#eVo@KXX85IawW6Mo92;Htf_$g5GVT(&xRefM(~7RdGTHr9;<}vRUA_+cwKMcF$WY#9u_Krn1hv*$el*(zT1EYtQ z>Q1|`mfdOHu#`;=!&7a(SwW+s#(LI=F(i$_&7`9Dj7pYyVdsQj$gpg|imJuK7F%p$ z@>~TPz&*p&jRLpO>Zdm%ZdWMDTjf(Z1H}0(U zUtgi?r8w640Yf&^bIFN8P24N_6HoKDPDTU?QoLM09nz*}8btEt-NKujdm3W>=9m@N z@UE0YcMTenjjf;a$bA2FSt&%YJfZ#CAb%6-;QDZSh(_$vOUaKyev8Q>3QbZ9S1XfD z>Km67uFi(k(W~^2itZ7hi($NO7gqd$10Rd2*Hv#86P7dy4=ySTk) z=RI$3vYl-?XTfoWrmc#CwTD@_%iXg)H@~wPUAgU9wJ9hN9oN%VyjP{Vm-ED@y$Ka; zuQ~f$EtZGkSJYa1m6&Rih2D#`x(^MQy?UH~nP-kZH9@SjF?HS8H+O4v0W#fcV?Ss3c zF-XSuObFjGYM`j4ezoBMhYEbBx#(#g~3;H9w)elQ)(qoOGYM(r%jm z^Y-Jh)kk-Iq%J;CEV3^HHQ$Rd>W=m2{B6=}_2>I7nVVgOtV9*LMsHp?87^AEC_Muf zamiT^PM+$|c|D{lq=$%|g_le{435Q~{df;KUdr`PLJn{ma;5&(oQbyNhU7g{*krQq zGp89%<> zzR-3ngTs74+d*bP3Mht1Kl&CL+r^q$9lGMs$9pMHrd(iB=l-~ww;#@<%sbT7Ia4=& zno6Lhs)$w0v+2x>a{*MqQeOu8A}#UQ{pgmmqbKxMy#cGl z9}pGy^q%nd=DjLv{nVO>Q#T@6Onlk7u*a2~u%wIS9O*ZrB<;4WVZphB(}}Co>4gWJ z=4dy5>>ei<*;!~|GkcE}R3+*vYtfmI)PHQkd1Cb4q!!U9o5$tKXXD(`0`ewApD4`s zR$mPiebrRb*6+}`kDI_y_dHH$uxTkR=1}BDtf@n>hNf6V!0vLsc2;iDJi~Iu`Qdj0 z$#NYfO`_~hcFDXoD%{ncW&0A^@;cAL=e(9nwAHTGz~Gp>`PCnV0G<6A&2K=O`Y>^5 zNWFNz>Mmje9r8?=-^O#E?JPRVAloFuEhlAgrVL7;Mt4%8B8*m3gk|fRZ!Zr7rGa{X zf53yCllgJ^w3Hyj&aAkjvea6G69mJ74RxfSmi=&O*Nd7tg?r#A+dfDq$`6GKKFpgVL2d z_Mo)w55Y@$mg4?#oR7-!$xlYEZTaD00>;;#dRzCxNBVxY#VLnV?`W1GDjSA)|#k>o>9E6AbQ1qK8b3m>0SKS30fNRSMB+W1;#2wz1mTzLFJk((tr z7oi+w4tjPyz`7M!7Fl20{*jNv3x0j2{E>vQyC?hCHzYWNtf+0w2X$c||J=;~^`8u0 zO2!fU=Ci|ozwi40`a>L~?+B0M|MLrNWBJhYx{yM`EC{&AQlBe5d;j+}?-Qw+o`lf@?%{_X|;_Pq}rgv1r!znYfv{@Mcn_T&Ha#-v^? zU5g6;v>4|zSjibjUYy-ir*YwbE$`38Z>ANgF~CO5|DHAc%U1l4>-c`IhQLulmeJwf z|NQ*u_s(NsU(9UwLUC=yXITNrt>XbqFN6v%?s zcQ-SXl>(`Nc)>`G+QeiNI7HMUw>ap+mdb2Dc@#mx|Kg>#9sq)Me9v=f0rF3F0;(zY z3b&s#jJ}0}Ei8wh{)bU0r5H*UVNM79khXr#VaNc3y$)%zpk~{{AEw!MN~eV{)!#=R z5P14l)D#q(5ZLZPhcRMVf(#H_oIi(m2urbE9*m)y#cD{A!^oG;It&q8CzT|UBOzDh z_wXeb(Y;4<#Aab+h=Sv6#QiX2>TCu=(a+VTiv)_kJ$rr|^wldqtia5TxFIwTv$8+d zharYzE|6OfUitX5$qf2D`q-!UF06{zOa`*;XnmZ-Zl@gl)DB%aAv~0$T#j&UOI#g( zKbQ8rd(ec=d@bwWZ=Q@QipTn|VFJLu4`%LaK{$k77kVe9Wr!AmS_lY7^{s?vWkbd& zOdgrw%H7=((@LkZTMK915^GMOkl)I+9u6c z!YGg#OxR>z&Nmh%(AZZjTD-D*#%SJPRs4MKfOhMTqV%GZ%ex@{zPL+S&2kO(aWnN( z^L{>}7gT>n5lZ(>J!-vZQmi_h_rTAz>HTdBxw7Dz#rMERkYz2 zIUP498)mY!GQkR6RBa7fzZ5^a!Jq}eZtn5r?cGd}YRWtvMsGIcJsJK+E%-U2QOkh9 zX)PE494v~^QuzJ>&7!$|3~Dv|p;8nJX^X$uMRHv>(2fnt_=J438?RdpljY&kEhcFb z+lyiQiy}hB&*2RvgqZ{l&B$-0#gfPYFFzzJ!B}>9ph(>S&h0NAw0iIE1WM_bT3jvK z#k}^8+=ltSZsM$B{Rx=oCPQ?6st?Poo3YqZZsqnJ+WS^%$c6&-&m8EOy815UCGU{) zxwewY6v>~MLi2M!HK$PDd4AmWClw#5lP(@*)Z~`Y?=2(T)%TFGu0;qO@g%glh&iY<5{gR(Cbj;~`HLYZ%Wm|#6WBK}Vf7R|rN5GdK zjRf^paI1t~G4#>xV*?ZWEyK~`zN=yHI>Z~o2>eCEI}YhDBjH<`zP*4q+?V^*DAr??}UB_Da z0k&%*PK~F02ZpJ_gtn^3ij;>jrXUcutta*%LyGVUQ5pVH6sxfORM?aqdMTp!iExzAZ;S61?-oFkiXeQfZbdSBv7pqd!x=# zkX#H#^irgH{L^2R`k<`^-76gHH!|~ z^GD4S)Gy)4n}|4BV#g<87D^_3%7)`&RGiA8b%w=I&Y%ydGe*!*c0_MZ8a zB3Q$SPA63L<-HsyE$W_dozcez9`;*I zES4Ba?AXsBW(X|1ei^wpj=?SQHWuE?`d@An;xxNE?0)ngnSP+IqbR+#z{GLx+=AS& z^|Ra#%JjmkMqPeK_N7_5BmTSfJ;WzG{L-M@hE{hxAToy(t(pB683T5>SH_h_peTcGg|1=z2$nI z+My#Gu^LP%#HdhKc63-&Ly2W-@_wMOw8?^8(1mfQ^uD+ik%Sn-LH;%R1 zV*+^1TRR{Ku-?orTORY#czT67DK;*cOZlvgWpgL}K;yACYG%UG-%fut4#am z)Ros7Ay{i!{b-9*SG$4h#<)+&R{cE;;vJhCfO)h7#GM!%?^E@sBTLLo<#A`TXCxzE zTzPfRGK*KRt?;OF!+L{-=BRSdBoK}+Tb1-H+k2eBsX3|!H&j%EK%~X+gK=-~2+T^e z?F{i|f;|isS>hYqu4MXmjlOj@_n8V~6Z2}iBD9jkH@&RQlrSUK-?4u{D=-1;z!GV3 z;^IzOZoOjE@$?-x5PfOaWSUwjYS-9@d9(|mw%*i*q9%l+I=^(nl-=q1fBj-CXnU( zGvx4fj*xI`B{8Q3Y=nydSJfpTF>>Qq2kEcX==37-fMYt5_bfU7Wfs>3ri)SojbvQc z&WIE~9DQQ7dE`!}XuJGQcp1ZZ(3Zm1<)W45&AH8Fg$ zU-?_l3j-BiBOEL5YzkISv*hkLIj!~&sgeuE*w&A3EWAF+R_aSBE{*+X=tY>K(IoZO z5!*!d(>Rfq>nnAoPg`3&3d1o2lv4;bHE};fn}UP7Ku2MsyY|CGu^Nl)F0wfsb%-j1^`?3x9nVoQk1JVaBu2n0LTTVkzjgDq*i5x1dk5Ml=hVc9^>4S@JKW1K zJWd*CHnH4jeT&M9sPd3fePP}?b8no(hEj)A?8g>H{P?RC*V}|A?1F23b&nU@F{Ed@ zrP-O}Miyq+*a>%Z+ilJGDx!Lx`8C1lqveYi+i2S)xooQ`J`~o-vH2d774A9WW;a}n z7i5z5ziPVU-uqR5uA)6dnGX#lU^m}Mr1qW#sKh4->Iy#Ns7yQ-`agf<${98kB0Y#) zw4eA6hMc^9-+`<0z4+p(?Bv_snsS0z#&*B_qxnp130==EHY{2CLm^Xl&+rvEk-@5P zn871tJHa2sR1&7Hc!0sXGm^4 zH20QsjbgR$dB9N^`9@ZZpEp|F+~63$Jg`+`EDN{>r0p?hn=Z9m31!dkA9*|`))IAE zrOc@3EQfdkyG&9vrj?O0(H|smmJ8tUeK#4`V9ClHq+K7#C1YYdDqir0l;v%q2e~qB zE01k5Z6k)4{F=DGAlK7w-NtCD$*Z5xv`#z3u=OOkI%ad4N(H0fNCr1zuL7JIraRgJ(5of z@(bR;_%n4UIJ<6CpYFHXNnbo}J_9Va`h$b+U>)`)oY*4UJd7iKc1DA^EY(uU+Ls<) z_|z5GUX~PWf3zjNMl?Nr#;bKpsWx<6#A0VI^Cjq!UqGO0F5@I1@tXcSGlil9(qy4!}!c4|B%ekn)3&M!bzVz4JXk;GM8U zN@xE)x{>wbM(VIH#1Zs4Nz8tDgrTM%KWKr0I1w~YElRA`#VDTnayn$eM%syEzaQ zV<51cY~!eZ9kujZN}VZP*OJvMwl59}A9c>WHOytW{)LDntEe>NZNhiAlzg}2Y=59H z8O{_@W(}UQ&Ch7rP+zElz_f(2?Qtjs)Uz}^75F66`B&cGTs(LL%;H5 zf31L4vE^y8`^S4NcO+O|KHSutI5yB+=KP7#HNJ*M zu$uuql|kHOO*i@y@V4Yr6;tLqhmU*M`RFJHSOhF3nGJO7_E}VmInhJot@cb2nJA zZ?A*ivFC-em}kp_E9ZM$enLSK7(#zxv54d0$xdC)4X!1rrRvqxy-lfxA8gem7~YMk z19GBIs42y36q{dh!E)2?Ud#TB&zk9>Z2mHaQ0+VWrsB&*%lcd(;=YqRN&X4!MOcdB z0!O@%%*|qiRn+;<|t0&{+EggU9vHWSG0603= zNsfuCWphq$TQIo@omb}&X$`lq;qAGKbx!+4HzHra_vY2NKJ8#Ri`wirZ|`BQ>npWf zc4G?8>}R(*y>^rGWf`Fe(IZKtt62952kHFd5jx~^xLrzt0_7&gyj&gyR?GfNAEV7L zf9zlzNP^O{($X)R?}s;U3rP;X<3!%&RXLqGpLjowPKipD^zF&zMhUS;ycad46oY-@cQ|`*UV73ovrDNMIZom^73$7}7v%ir1_=3AqAlacuZfl9^Y2;(mT+I1y+}x+ zO70ZXMB}+IUJO8nNCZO>+|i%PI1y)KzJAfh@zf}fA~3XeW=o> z!9^y8oi5O_%x)7(yDutIT&e3v3tEKAZ|!bPD)sAsS;qA=3_iqom5L{Ae7#x!y6jY( zauI~zZ=p4)zE?B3P#aNCLXC6g9gHUpg39*0a)06sqZ-DSm+#@xJ=>5V)HV7X z6bewi#6fH7t5!cbW*F?cN3|H+yY8U;iv3wg-+^m0L~*5p+{Zrk9mZF`-p_u%J$z@& zf5P)S^8f3a4|Fd&ou`9BKLKIA@nlZ+1Hz#PF|SNW4jkKWIuog*tDj|I_h5y3?#mDq zI<<(6trno|;)P^fPSm=wvRPAzD4t&(OPP%8ZU(EB+}M|*G~KutiT;Vzxf{p2KYsfdt1?^%B7G2g&vYwjFM{S58_+$XQA7j)Q@)@l)1?;8gX%_Efos-YKQ6 z_Rk8zZ!i(Om$3t$viXs}wpb|!H7dR8gho|a{Z`&P`*tm@MlF-Hi{ggOn+@vMent#F z9%X%d2MImk+VJ-Vsr|$+FFhv+GIEF+%gGL|0E)}^sQx4(iUYJVfVi1IovBA9Co|%|s~a9dZxwge zN6nx@PqsAh1fxtRAOSX{BYg!CETS;}%omue38Xy+0i0W`7rJfpd14S~hyo&24w)WE zA$oDNAry&}Mt$D|#ljFcXL34cH2=wBFyuYYga~A?}4Y$&ZyO zx|?J+*gjN*srh9ykT%Yx0=xJt z>oo6dPURgK?I6j1UwYFYu%q|br4}~7l?y3psi7l+#Ct=3K}_7NZz~2ldP&p1N6U#`jWO4W4L6YW~u`_S2j(V&$nc#XoeTju=R|<&m8Vo z+I4|)U*C(TBSKDXEU(h!phNIoGq5mOYRGG})X3t7_M9t#!Ok;!M3HejoTonMUI{d9 z1#t+v+0w+5cgH~aR0vLXI_ItD(ArbT1-upk$fDwrBkG&0v*5rxvrkSI4Sgggm(SqT zWM*G>Uv&yCEFhyd0mQ~R4J9uTl|FzHH>|n#HpmEBn9(qaJ+>Gv^$9>{gy)KDW@z~` zZ;|~)<)9x&l&3b4Ks@#=@XyTGVRWBZzY=27f&fb?B#QOJG+R{#e|8R@)*Nze-QNyZ zyFM5!`WB|r4v>v3rQV$9Kp=t_&{w(5`%ORqr0qy`$C<15D%I~DILrjJ9<5A$UxzoL*=yn2bT zGv{*1k3&+v79rTDU$+i^N<3sA7 zGJH{*;l-!31Oy_RD!-k$)LlMD*;cnQ^W$qQZ7b-CBwOyE!{>Z(EwZA&ok-F^PVs@5 zP|UOX$G7L}K0Pz|8&1l2%pZ?NMh&FK2l>gIA359@>X1+sb{)-mu|t6exOZ{l`t2_x zXH1#v+?~1?;2hT*r=IjNulC=rg~R0DTv)D?SUG@xn-3Fst?Ey1BMtvWQfmi2)6yfi zSQmeoDJn4k4~iuxysr&o6gS&Z=oRjvR|%}P+zNkq!Klb*ZO&Ud^7njX z)7YC%E!{m%x@4X|L;2?ZYlF`$8KJ=V+E-;?Hqh$ZYLd`4;^6$llEl87m5gBdqAo{E zablrM(KN@C6yegA8~3K3=gy)&)f0U}p?gtpZ2xSo@bN33FM&y%EP88oQ9iN$ZzTlM zn~R5|V)_v#8Ez*q{p8y7RizM7?R<_igBjt?Kn@v3FRDkh#Qq_yMG6Z?EQc6Z}Bvp}m zQIa1wLNP5P4@349sn|Pz4+$>U8I&Tf&^hSlP4exja-`+!pPk?P{*HEK2Irg-{&Qo8tQpbeSZ<&>OGH?fP3&~?#M z;2U6zf5`aS8VP)oo@(|M29VadFADiXRKO@;=EWS%LiHp1FU!Ke8zK3dng;57_s3hQ zKZhQ2IrkO(wekK$fgrg=O~vuniSyk`vJuT22eF8B$mD+4>nW()8szJlzb^xWF>!;LHbPXSyemUfOf37v*!W7we~y=f#8Ef?A5tXj(5!0C&#U52dEG z4OG)^HH3RGxEbWbhi30iFB8+qi>!S6_%$BW8`|9)r%{ot%QuA>19EBVxRnBqfb&DM)a%IUoKMSDwXbpA-Geny(# z!;RlQcH(4$3FOB?>`PS`D~01hcyi)rRmmqvn+}04q|Pu z7TjbiQ*$s3sW<}^3sC}?>O6Z}ve!_YluUmZ^|TU&2N>zUqP86-G66x+3!KX#rjy8> znG(ibTl2kM44oOnB#o5W{4yT#yf`@E4YE+bCxBDQ1Uu93QEabTvA1F+C zji>_i9Yx{iApe6$nj#r12@U{_U(_#{y{%b1YM=GEzqiy2c0S6ppj!(%qz;- z%b9+eNB1>6Fjr-d4-~IEwSNRz7!bXgJJ}F9+U_-o#HtLfpxYNFz2AbL3XS=grMmZ! zujd!mGn>JrL?o$i4~Xa_C=+bJ5kLi*vfYZ6p_6J|0E~#5J%3S5fd*ikryu0J@j-Lbzh#Bs!(O{78ya zZ+ELOCaTK~wd=V0qs>U`#{=g(Q_^ix+@QTpg@&fO>-jm}k=61&q7SK|XDbMtJe&6C{X>E!->ii+uY zy_&@4Lv{GV$ZmhXOFw(w8KRUDwykoLRev7jf8^o67acX~ghcmXHv$x6m`&@;A)`_g z#fIZ)Iv^;KT66u1Y{EG2Q#B4#Rkh8}&Y+`}iFlHM3DmV&EpJKSxrI|_pST?PmsgI% zifFEM%pPap$}-I%qGw!P){>P!40EZoJo1r6Y=1tnG2`;TaMcjD5%*gjo zV$8y(q|2!~cpZJ=R1bk^y4%p}C8;ljRKjzVvAjZy+y-X@GOhJ351I9|Rt%!C)ScQ^ zNv1M{dN^%f7L9;I#Lbtwk4>jA)0eYibCe?XC}BFfN1QViDP}2$S_F#JGYlo)45B$E zS=xe$N_cn!7a&_;Azrl@TqGv(ALUA6q#!@QEXyF~y`O<`y z=@d4mc82Z5M0a<^p-2F6T0R0G=raTO*iMyfITW zV9--la2O^lUA{u8KQj-_yhwAdB47Q6d3fUQk3VkWo>(u^=?^n(IQA-+1tp6 ze9uR%W9}O{C}&Vf+JhbFVxe_Mnls*dD95h!k3%V&Q^Y{K-Qvu}PiAX6LAQ@Btlzm{ zrkrj}?c6Y?Tw=>*z6rOY7E>9dgiL{`l6%;wi4f>uQTD?yfURatekFpfzs%Q|@n-4yN1T!;{F`MbBf90l4RhU_{hZzJ zNnA!D8Pc~${65v$2&RhotFA}>7rpmbkB6Ue2SDbu8E zdjvr5Kl`y7k>9(QOFsm&X(pcL`QI}#@lX`g?Z&>Q9pS#2K!u00au3m~gVb|f)>Z7j zuk*h73QVsbq)X(gy8)G2N4W~7B5aCdzE<;(K(?O(IB>W2d^FPjYb*ZCS3$Q0Yh+5` zh%)ek8o>PS)%s(_f5tpA#*y!?zel{ka)Mu9eKjjsmC-eK5>HzF(|Ra((}~m!tVERk|LQ_Byom=Lqzwp3n)fHlTVD5? zyct;+XVcx1Bz~*s#g*H?vuxAy@!qK?{#v)c`6{EJ1_`(gLiTqfN~Nwk9y2VS7?E$x)ruhzq#`{Nkb0_e7cfIAqE zW{aY*v2c55Mdl!IX6mM4p5YT>vORYa>fL>bBm_iz| zu|qanW{~_wD1ngX4H+;qs|Bi-YTM-@O8>K<#BM#_ru%tq+1@oo&iT*9$hb>!X<}>N zv*wh6MWaF(q(mYzG{B3r?sF)g{fQ$ZVoL!490&a{UH*u`_!H?^Y7YyWdX(s0x(e)| zfCJ*sm;ZR=0OJ0NFLTxN=(ilD>9BpsH1dL6XY-PB5G^6mec-Rv2%NYT!{nEq%V(89Sv)c{Yvqu&Z^T*2xZ*5Ef zKIrv1Jj@a4j8w3@!^%ufKOA|9*m+R0F^~-B&#;6Pm*KX{Qhsfk&NvL8kH21$YdOm3 zKuc2)Id+%<`_l|L*&!ueJ#dGr1G-L0j=S{Aypsm_Z;HYs}W>e?q7R_L# zbn*+gIOgwVOxFfKc=8E?e;h6QW>`Ah;TIp=jFFzZ8M0Sr%0c|ba16#u--bA40SKD) z%55M_$#fHkREZGr@u`A0{PmL{G*O3~+K>P_WEp}P>o4uPh|Zc{sL zdcvzJe} z<2Bu+>!mwiDrNvk3?HT=Z$$JKGbDM7RQ)qz4ot?hYIVk*k$ehtuUp_5_?BuIWP{5- z>|HRDoDHSW32End?YOAs9#b)57`J?;coB4Y?`)5?$IgF8C4L^j9D1j1;i;m=B-^}m zXn<6eX)XHtzF9TvYbx>EQIKI;S0}%^4b5wchlm@5rV|h}qi{ucH zGU_p)_?}wO%=i4Quugycf~6J6Y22}Vu>R$2V4$$FuRboGejPl@P7V|YMCP`w9uHfS8i8=ibcAoSIla2}o|D&#Nf1tV*<5O^5(!ZWCBGYKmw zhP^K=F^rhPrG=pzW#>wP;ahJPadk(kAOOGJ=^LVUcGC@_h#QEBhb{XLxy|VTIsAih~h%c zwa^lE6fxweh>G&;j+;BEYi@q7e|K^+zHIWTehA{Z4P~6OEQFXMugl;Ak+i7jfUjmv zQdOBvG~bn!VSk_=oOW!0dY}19iNoQJ95nZNOdKgWG;EI>VxL|4b-~wCh%g@IZd0@Y z*ZCT4152F!J@EV_-%@J|8(3M?Bm;mM%{3r(W94gSp?Skk>VXZ$%No?S6F(Y=#cDky zAWXnx4Iu1?2!fBarGclJvpo>`?`_;XLL~4WNnTvm-k3$1<<87{KR6ZlC z8gf9|3{4Z+Zj&8N#?F5iyz=tS)~N&*+A`7T8FRk+g79||?Uu)fE#olI$qjKC`0{LW2_rj?^$zBah9Y_~fDJO$ZLy zAHip(F`S7G{DK8FM3lGSB+j!q+Cl!rf+gQ|(yYv`rI*I@z+|!S@_QvumPIc2%n*ucrm)h{JY?gQWB99Lh6B{;l{s~87LSr#rB-Sd-?Es2qWv`dbdLnb6O^D0C!-)o^Tt3G*G2In zQlLg6R@hUqzU#4tc#6;ujGk7i-OJF0*YAlQ#?urav zHB~{YO-SQ3i-R5>4*XGXZU+7CZr@72{N}sBj0=YaDtGrCa zVYQJ((0WScdi30X{=%%?VG|5f#~p1w!1lvWPzQ6|*Lf?W2<_b)dLYR%>XpF4$(0Y1 z-AvF)M;JwDX^^k?A&G0}E7aDJii?Q=lW+peghu~1I%gK~2DHRjhdYdhkr!T3K`8h` zPfoI_IwC=-!m)#Iyz9ACt7j5g$LL^5Bng94@G?b^IL2S$8C}%g8{ypg?78TfPly_+ z)^Z^!$nFzJ1`2v;-JXSHjIDu}UxH2l&6dN_b1iy976h)&1+X$(LSvpZCv5#A-RFlC z7UY`$98o)V{z`^yJ5AA~NBWJ_ z8OeB%bY)5>Q!^B5%1lc>*?^n)k%gF{VrFY&vzzf8&a~-SoIupX|$s}G+YgtGs zStU<*OejCa#lvq!g?z$e_ETGzVA9*T8$xcqD*}`ZYsWk(}Q#j7}NMxtfuB5$2DQWlaJm975fWb?!@*I&%`@UM*ZMq$JGQ%G0`nb)}1R zry5r6TLuXy%y(Yfk$Q%83-ez~UR5TLIV>!mirs89dJn`IeeQC_;O33s^481hCau=c z4X)>jifA$zN;~)t4uLhU*&uS#JX+CfDD4>$&!Frlo%-!&P@kjMdtrZwJhY}3wit|n-&wMNcOP&P_ z?bI}Yv}&y2QuPE1!hd9<*_P>c_J6-4B*_2IFSL!NEv=M;Lc-0+uHxW=4OCF4AP?J`Oj-% z(1Wa1{I>01L7o4+i%;$;>(Firyf-}Po$!0@^k3HvesKK)pge+C%lZE4CZEsXgS-6v z;Uw;#K%NX`K>#CPoVL51^*`XH-{0lZY54OxD~A6#k9*+BX#kmC-I;y%zwa0PH0T)s znlJ99wV=uU|9!Eqj=^rEFEon(hu1+YJqUkZ=e?``Ki?;Mz_@yj7C--cB=)Zb3)&(= z{Y*4YxLmDIlY@H!b0R7)9Fqu#uJCwDALKRzD2Nh8Rr-85!W&R;W7yzV#rM}o|MM#dRTwvfZFt$IOn0I@z7#;Cs{t@X zNrluxvo?iZj(`uZ$=4SY8f=gh^g$0xZF`LL6dSmmBVt6oe>F61k#?ibwZra`;%|(C zAA>NX7lKj3KHUd!KoIrbo_M=je*);E4wbA>KR?*i_1*3W*eQBaN;c3el{1iDQpB8i zEwF_2{H%7+>wWATYcSnIiOh$*!1|>YAxCUQ11D?N8aX1VkfY7|TJ`Q>V>qQcL6Tp2 zUvVy0_`G4yR{xp)VJp=UeLt71vnY+PKr}4c_tJtqNRxgHm{+>xNr*JfM1&)n1-dR z<}59T;yJF|`x0*@nmz=0f$n)cwp_WL3N>O8|6!yQiM3`8jf)oo;UX0AYYWn@!i0^M zJ*hYK0==t6%Gn@N_65k`LIB?jMOH+u(9#c*^G*XSOxr+H&`Wji)uXE(>*0jJ|25@aqwg0Zkrd1JF@S-m;!UVYRp%txH+yqj{S zjeEJ(615p%BVMNB&Pl4nQ=jIjOSnI(A=S@`7$u(pv=E%?<@wwD_Y*g}lt9p%OODTa zQ;rB?+^4g)5zkMw&J#G;RB$&<5@B}JcN%sg72IE++>A1y@4Yo;4f=qfoqgxB_6H(+ zj#1aVcj8Ln(+e*VS%ft1=>wH4jl8g&@VA0QP$QOEs*lM6lG$CN$_k{pH;FGL0R+2+ z41SPm@XKrOAC7I$&;=)2aQy7)Lu2)xWAtpx>!%o`PZ$b!p3AreN|^0B@R?>J1v8S8 zqDSPZCnUayzqtGU7<~}nE`jq<0(uVP6Cv)>7TLS@PWVP#s(Hk53WqaQ z=BqG~TTdXQcykX;nq@!ST!DIt*Pio=Ek2eS>|rHRH-Y}yBFkKrqfAU|DsL2AZeu%8 zJS$*6vlO-iXlPjV%XN}gHfB|cTwo_Si~47-LA@*wJjlfa`RWfv;U+i;7&cThE(zW6 z(e@o%s}WvB0*X5iC-bmCI3X4DOe%4o?)eO$30bPF`#t?S3uf2Lt!T6waaU#LQO6ot z4=#XAb-j0!z+auF|5(QJgG3l19$?EMYKPN@3uoU^^&*fEjdwchAdhUBuNgOMVY@K# zYq$*l0E}}lJosk?{94_bIb%nD)27oAL#27Cz$s2K%#m5RyW|)P8{mC-_##KeI>0QM z5J-%s#OrOpV2p;LHcE`X0o=`ZYbp_Z{DG?RnzSW%h!0!`>Ki4JZ3mtTS1r<%y;&BQ zZIuq3%J-!+2#r|_@Ni~wutp?O((!@6qtg{vb8+dPRC$YZP3n&^EA-bF1_he08A zL`Xm1#_44rNwTCZ{Tdr_+N5nr1XDwj7pZ6%LYlFsHv~F`8(uvW9Hd#j`?x})Kkwo8 z0r7yHN+BziNuaLY;EKW;HaZ*#Fmz$@e7w+Lg=mUNMe;5@`}ALKEhb6XU!fYIZ5`Ld zYAJ3!eoX1#wA_-aBp>HB5lKgAjJwfPVGoX0i9n7rH%(BA!JmGcDuJkkTU;${)M*z_ z29JN8hQ@Kk^mrskQo~Oo`Z@s@|LWcUejI*eFF-TnHC+8KEey4vCD{#1WV{AuV1uY> z6BXC-dt=X#DkZ^JqyD{X9WeDNEP!;?`Qit88L^$@8r-OZZ>q5q2nEW=A zn8mp!BejZ#k!8%a?zN3mfT<3=D^I{xlabSlmwG#xGn*4VZYi2UI^y$JkCY}$?R@R~ z?>Wj7hahO9%j?F?bY-atyi=p+VM<1auAxrVbhjZ(M{2umb}YGik8nIMIT@Nz`HToU z-3X7?ms0W(l=q9ZodlOAEfeg=^4eC6HB199SmN=3_F>htIWgTEAw}TqO*&VR*-GR|?>tG{-CfVd zQ$J-%Wjt|YAi<;-v42{>Xfz1O{GkB$G?QjB2mv&{@j9-UV!x1LB7E>{E&#d#cjGrkkmf` zp=~;9P6;FYcXbZSx+QWQOBrzd@xXS{u$!l|Wfj36QwBxyF@hCf$1fBGcQl=JD^)gY7-6udY|at`I><$C_&7 zADs!m*R79zu!BlEL&+0Xq;@%D#M_5&e9c76v3uie>*HrqH&OdbGY5kSs2~#?_@CPk zh|&UwAfyG^PGOECQg7m|IBu?S12z?HD)+{HcjAPy?Zih1vfs$jW*^hX!?Y)> zG)MrnD)0Z>C%?rmUx1~T)cU29CHH_XrUR}3eL8o#*WA>p1?TjRSnN^B&E5HQTH&5} zqcYD2UYpZ^vre<-?mhE7X8(QQ2=^n?+NRqvuh)AipSZod{Jq2j)f3)}_Y{_H{nQs( zx>o%*KWFJ1?)lu0J;v+TUd&J9oL@Q|MgP&J|W?kHa0=)=L_-gOqB%!BynRePePl|nsaUm6AG&(FHLy;HRZ#k%H5CqtizsXCB>^m z7V6G>D58Dq#xmfgBX()`wgH<0i@L?<*q?m_Y%{##`Ls-XO=t47Z*%SHfJ4=WJG{e+ zHXghSy85$Ddfnu*z=iI{71~Fhp5eMu&&ag==c~u*+vl8#eRoIm$|Lzn z46hnzFA{WE2b`eIRm{{r`)}>JT;&bk$``zwBvjY0-hB0l;px2iRNzp>Q|-@p81hrE z#JIuR=~%mvjadQ-6-R-~S2d<(<^k{7lmM2MreAJt*yYi@XUg_vQ5wlF%vz>ROI_#j zEU)yS%yER_@-L_2HqhQ{qODs#W zi_Y9FseQSa@m|Ow#0V)4x2`a5Y@4)mHk05L-_8A(Gq--cvBoUykrnPN0`Fa~(gSu5 zU$uQhFO)Dll~=leLlsepWvUqM8+5(EL#}|Gt-BX+oZ5mE%#Bfkz@ESMnRNsQN<>zG zmg}vOzSWGo+J>bMZD1chyY=Q&jR4a|(29e?$Le8gg{NZ~4?+r{6~&B6c1P;8Zm0bQ zcJ9(bfJepy$HZ<4&M+#Cs(63cAGCxaJH2(CD~>3Dgy;e{77pO}UT)`ws(oQoU)^WO zUwR|P1FsfPZ~(XLd7E6{w=t<8 diff --git a/docs/multitenant/ords-based/images/K8S_SECURE3.png b/docs/multitenant/ords-based/images/K8S_SECURE3.png deleted file mode 100644 index b70123c12a2035af0533c17eaff662d2f11acb01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130498 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%0AV_A*$tYHe|B^myZN)Q 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 U`ptZk@GAJ(SlL+??IA|~8+Afp&;S4c diff --git a/docs/multitenant/ords-based/images/makerunall.png b/docs/multitenant/ords-based/images/makerunall.png deleted file mode 100644 index ab856f90ca3a29bbaece7f4d80b0056801243613..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 211874 zcmdSB2T+r17cLq_K|w^6CJG3sAfSk10fZ=31Zg52A|TR1sY*#yq)11kC?y~u(xijb zNLNvMM|zds2_?x{5%>P=5$3-}|ohtfzc0Rg~mtsadHJ2n4PC zO=&d*V$WFwg5v8wN_gcmv4tM~wZ~pkUVR_@&u!m*ANcvGgN&BL9oq*E&W3g-2vZwd zYm>9~#&#wqHuh$=4)YW^2?XKE~ueVPCp~pW2LVZXQ=XbW~iAM?&MEi|sWpe5AC9d;|Wf#5+x!;1?ehh5ZwD zSMbsm9pU1lq-VJyqiTllLmsxUDlSQVYjgd_d@hrvW9P@Rbz5X=YRQLGjYIS?8oZxb zj2jakzAHcYOsJN{*k7pQwnjK_b?q({-m|KQ;pg8k)^;hcDCxW~&)>%*Ifx>1z3FbC z#kcF;@<)#D#yS>=AC42MiV&1*@}#NUdV0}xu*k(Rv-ZS$OSeWpj7|v?F$hs&#W@s`Ec#070PQHH*S2=)G`we?K#H6-pk=}TBM?*pQ)|Gx1e^M zo#xf*!Me}a_b@YDpK*#GA2w2PbrqDYiM%$T6h-O%XylOHFEx~RCHdRevg95LC9#P( z1YR;wWT7^}5ht4ph77CNes1HORktu=aSoexE^WJ1#F~gruZD8UJAz{Zyk=&~4|@D{ zrGigqOm$Y4_ZVmy(#%&6sjN-YA>sm^c(;0oHJA0zqj48cA6GZ7+$E;?)6(OmYsUFU zSVpggR~-Jv56|yzh^?tVdp1(-jt4WTlXSS{H3ijhC(7+v1c$ifLqpCP)Z-h1IJ?2? zdkCwUk2jjR@mLAku&Ah+VhYj{Gk&JWGx!~sqNPP;roG)gF|*)co4!mAgH60K`8C_Q zK96}UanCf7aND;rdX3`g0FkiJpG_fX)8}g*5D@9Ke$K0G<22&Bp>+WLe7Rprp^afr zUkOXpo0L{`r-=QbXl&4c@D=Wr2DZ|b#kwqwv|R{7Y3U)Q7lY4tBMzO*COIlKAeZFi zXzFSE*tyz~aNnw2x19EDZ#3e6l)3KvUVqWtZD*N{{RvB7#LNx%`u7SU1gpysmSHhj zT{`A8N$yDA+1YsYGls^-w3e28)6*HV3MkX9N0D)@PE9B9SW!>7gL(%JRF7%r-ZNPb zDwZuLNxc5}@eAES*>=}?LoK&bgQKvU>H=naLpWIW@rP0Oi#dNw+cuRlGFCmz?49dD z8DCa(RM4jPbYfW+T3qk_?W&|-t*Q+4yOQ@GS*WZHI9+3^v?WFxi{xMn;uwjJU_KXk z%-efx4Dl3m>vm1t0Fg;^yg>%7awyMvzH0}mlAkw~>trw(22am;T&7@YOA7)) z$ItgrC;mFT5iyu2`K*QNn{TtJP>^0_|yBSGqwv;0gQyd7E(tEqU9Xb3EuFoN_ zU>K`*=k^r$&cR_Z#tG!u(5dnwJk{LpPMvUYaEQyur%0MFh@U<8^zz`4~#Xeh%-#EW!liL_I<&WqT=SxyjPM2*sME4**;0rNQ zpSyFj9@#x#o72}8YGV`kr7|%=JWXBim%3-b{*f3&dKM@bbbbXzVjM_L9GpXus1YB2 z=I!qna~j-gPcy0O>{~;otgcyaFCa-gh`8B#jGThWDTa;?eDVN^J%YG_ZC+WXP_Ku3 zVLR9F`Rxa7kZoUOP>J*O>pNw}ROnTEk?<28$e5`>wknU77OVk>ICY^X%~=iPpSqDf z5wsE1McQ~1K+VsurL8++c$;btO{EfLUbC{kD%0ACdR&roOyW3o(%~OS%+TJaRHsh4 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 D4UFOA> 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 sLIlI>es1C)7S}^Z@9@*K% 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 RK2h*>P)%1gNBQWb{{_V`YY_kd diff --git a/docs/multitenant/ords-based/images/makesecrets_1_1.png b/docs/multitenant/ords-based/images/makesecrets_1_1.png deleted file mode 100644 index f0f6f21569bfa17aab7bd37827d97aa041bb06ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117953 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 diff --git a/docs/multitenant/ords-based/openssl_schema.jpg b/docs/multitenant/ords-based/openssl_schema.jpg deleted file mode 100644 index 4453d52f27eaa83c6891a9ba6c8bef418b89ec8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54221 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 - -# Build ORDS Docker Image - -This file contains the steps to create an ORDS based image to be used solely by the PDB life cycle multitentant controllers. - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -#### Clone the software using git: - -> Under directory ./oracle-database-operator/ords you will find the [Dockerfile](../../../ords/Dockerfile) and [runOrdsSSL.sh](../../../ords/runOrdsSSL.sh) required to build the image. - -```sh - git clone git@orahub.oci.oraclecorp.com:rac-docker-dev/oracle-database-operator.git - cd oracle-database-operator/ords/ -``` - -#### Login to the registry: container-registry.oracle.com - -**NOTE:** To login to this registry, you will need to the URL https://container-registry.oracle.com , Sign in, then click on "Java" and then accept the agreement. - -```bash -docker login container-registry.oracle.com -``` - -#### Login to the your container registry - -Login to a repo where you want to push your docker image (if needed) to pull during deployment in your environment. - -```bash -docker login -``` - -#### Build the image - -Build the docker image by using below command: - -```bash -docker build -t oracle/ords-dboper:latest . -``` -> If your are working behind a proxy mind to specify https_proxy and http_proxy during image creation - -Check the docker image details using: - -```bash -docker images -``` - -> OUTPUT EXAMPLE -```bash -REPOSITORY TAG IMAGE ID CREATED SIZE -oracle/ords-dboper latest fdb17aa242f8 4 hours ago 1.46GB - -``` - -#### Tag and push the image - -Tag and push the image to your image repository. - -NOTE: We have the repo as `phx.ocir.io//oracle/ords:latest`. Please change as per your environment. - -```bash -docker tag oracle/ords-dboper:ords-latest phx.ocir.io//oracle/ords:latest -docker push phx.ocir.io//oracle/ords:latest -``` - -#### In case of private image - -If you the image not be public then yuo need to create a secret containing the password of your image repository. -Create a Kubernetes Secret for your docker repository to pull the image during deployment using the below command: - -```bash -kubectl create secret generic container-registry-secret --from-file=.dockerconfigjson=./.docker/config.json --type=kubernetes.io/dockerconfigjson -n oracle-database-operator-system -``` - -Use the parameter `ordsImagePullSecret` to specify the container secrets in pod creation yaml file - -#### [Image createion example](../usecase01/logfiles/BuildImage.log) - - - diff --git a/docs/multitenant/ords-based/provisioning/quickOKEcreation.md b/docs/multitenant/ords-based/provisioning/quickOKEcreation.md deleted file mode 100644 index 19d9323e..00000000 --- a/docs/multitenant/ords-based/provisioning/quickOKEcreation.md +++ /dev/null @@ -1,136 +0,0 @@ - - -### Quick Oke creation script - -Use this script to create quickly an OKE cluster in your OCI. - -#### Prerequisties: -- ocicli is properly configured on your client -- make is installed on your client -- vnc is already configured -- ssh key is configured (public key available under directory ~/.ssh) -- edit make providing all the information about your compartment, vnc,subnet,lb subnet and nd subnet (exported variables in the header section) - - -#### Execution: - -```bash -make all -``` - -Monitor the OKE from OCI console - -#### Makefile -```makefile -.EXPORT_ALL_VARIABLES: - -export CMPID=[.... COMPARTMENT ID.............] -export VNCID=[.... VNC ID ....................] -export ENDID=[.... SUBNET END POINT ID .......] -export LBSID=[.....LB SUBNET ID...............] -export NDSID=[.....NODE SUBNET ID.............] - - -#ssh public key -export KEYFL=~/.ssh/id_rsa.pub - -#cluster version -export KSVER=v1.27.2 - -#cluster name -export CLUNM=myoke - -#pool name -export PLNAM=Pool1 - -#logfile -export LOGFILE=./clustoke.log - -#shape -export SHAPE=VM.Standard.E4.Flex - -OCI=/home/oracle/bin/oci -CUT=/usr/bin/cut -KUBECTL=/usr/bin/kubectl -CAT=/usr/bin/cat - -all: cluster waitcluster pool waitpool config desccluster - -cluster: - @echo " - CREATING CLUSTER " - @$(OCI) ce cluster create \ - --compartment-id $(CMPID) \ - --kubernetes-version $(KSVER) \ - --name $(CLUNM) \ - --vcn-id $(VNCID) \ - --endpoint-subnet-id $(ENDID) \ - --service-lb-subnet-ids '["'$(LBSID)'"]' \ - --endpoint-public-ip-enabled true \ - --persistent-volume-freeform-tags '{"$(CLUNM)" : "OKE"}' 1>$(LOGFILE) 2>&1 - -waitcluster: - @while [ `$(OCI) ce cluster list --compartment-id $(CMPID) \ - --name $(CLUNM) --lifecycle-state ACTIVE --query data[0].id \ - --raw-output |wc -l ` -eq 0 ] ; do sleep 5 ; done - @echo " - CLUSTER CREATED" - - -pool: - @echo " - CREATING POOL" - @$(eval PBKEY :=$(shell $(CAT) $(KEYFL)|grep -v " PUBLIC KEY")) - @$(OCI) ce node-pool create \ - --cluster-id `$(OCI) ce cluster list --compartment-id $(CMPID) \ - --name $(CLUNM) --lifecycle-state ACTIVE --query data[0].id --raw-output` \ - --compartment-id $(CMPID) \ - --kubernetes-version $(KSVER) \ - --name $(PLNAM) \ - --node-shape $(SHAPE) \ - --node-shape-config '{"memoryInGBs": 8.0, "ocpus": 1.0}' \ - --node-image-id `$(OCI) compute image list \ - --operating-system 'Oracle Linux' --operating-system-version 7.9 \ - --sort-by TIMECREATED --compartment-id $(CMPID) --shape $(SHAPE) \ - --query data[1].id --raw-output` \ - --node-boot-volume-size-in-gbs 50 \ - --ssh-public-key "$(PBKEY)" \ - --size 3 \ - --placement-configs '[{"availabilityDomain": "'`oci iam availability-domain list \ - --compartment-id $(CMPID) \ - --query data[0].name --raw-output`'", "subnetId": "'$(NDSID)'"}]' 1>>$(LOGFILE) 2>&1 - -waitpool: - $(eval CLSID :=$(shell $(OCI) ce cluster list --compartment-id $(CMPID) \ - --name $(CLUNM) --lifecycle-state ACTIVE --query data[0].id --raw-output)) - @while [ `$(OCI) ce node-pool list --compartment-id $(CMPID) \ - --lifecycle-state ACTIVE --cluster-id $(CLSID) \ - --query data[0].id --raw-output |wc -l ` -eq 0 ] ; do sleep 5 ; done - @sleep 10 - $(eval PLLID :=$(shell $(OCI) ce node-pool list --compartment-id $(CMPID) \ - --lifecycle-state ACTIVE --cluster-id $(CLSID) --query data[0].id --raw-output)) - @echo " - POOL CREATED" - -config: - @$(OCI) ce cluster create-kubeconfig --cluster-id \ - `$(OCI) ce cluster list \ - --compartment-id $(CMPID) --name $(CLUNM) --lifecycle-state ACTIVE \ - --query data[0].id --raw-output` \ - --file $(HOME)/.kube/config --region \ - `$(OCI) ce cluster list \ - --compartment-id $(CMPID) --name $(CLUNM) --lifecycle-state ACTIVE \ - --query data[0].id --raw-output|$(CUT) -f4 -d. ` \ - --token-version 2.0.0 --kube-endpoint PUBLIC_ENDPOINT - @echo " - KUBECTL PUBLIC ENDPOINT CONFIGURED" - - -desccluster: - @$(eval TMPSP := $(shell date "+%y/%m/%d:%H:%M" )) - $(KUBECTL) get nodes -o wide - $(KUBECTL) get storageclass - -checkvol: - $(OCI) bv volume list \ - --compartment-id $(CMPID) \ - --lifecycle-state AVAILABLE \ - --query 'data[?"freeform-tags".stackgres == '\''OKE'\''].id' -``` - - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml deleted file mode 100644 index 5e020de6..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "[...]" - key: "[...]" - ordsPwd: - secret: - secretName: "[...]" - key: "[...]" - cdbAdminUser: - secret: - secretName: "[...]" - key: "[...]" - cdbAdminPwd: - secret: - secretName: "[...]" - key: "[...]" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - cdbTlsKey: - secret: - secretName: "[...]" - key: "[...]" - cdbTlsCrt: - secret: - secretName: "[...]" - key: "[...]" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml deleted file mode 100644 index 567b90a4..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - cdbadmin_user: ".....base64 encoded password...." - cdbadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml deleted file mode 100644 index 964d1e5e..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_clone.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdb2_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Clone" diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml deleted file mode 100644 index 06d92469..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml deleted file mode 100644 index 2744223e..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - assertivePdbDeletion: true - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml deleted file mode 100644 index 523ac1cb..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml deleted file mode 100644 index 866db3e4..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml deleted file mode 100644 index e6605276..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - action: "Plug" - assertivePdbDeletion: true - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml deleted file mode 100644 index 60d95d76..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_secret.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - sysadmin_user: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml deleted file mode 100644 index 4e404efe..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - diff --git a/docs/multitenant/ords-based/usecase/README.md b/docs/multitenant/ords-based/usecase/README.md deleted file mode 100644 index b6f5e590..00000000 --- a/docs/multitenant/ords-based/usecase/README.md +++ /dev/null @@ -1,112 +0,0 @@ - - - -# Use case directory - -The use case directory contains the yaml files to test the multitenant controller functionalities: create ords pod and pdb operation *create / open / close / unplug / plug / delete / clone /map / parameter session* -In this exampl the cdb and pdbs resources are depolyed in different namespaces - -## Makefile helper - -Customizing yaml files (tns alias / credential / namespaces name etc...) is a long procedure prone to human error. A simple [makefile](../usecase/makefile) is available to quickly and safely configure yaml files with your system environment information. Just edit the [parameter file](../usecase/parameters.txt) before proceding. - -```text -[👉 CHECK PARAMETERS..................] -TNSALIAS...............:(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELA.... -ORDPWD.................:[Password for ORDS_PUBLIC_USER ] -SYSPWD.................:[SYS password] -WBUSER.................:[username for https authentication] -WBPASS.................:[password for https authentication] -PDBUSR.................:[pdb admin user] -PDBPWD.................:[pdb admin password] -CDBUSR.................:[cdb admin user e.g. C##DBAPI_CDB_ADMIN] -CDBPWD.................:[cdb admin password] -PDBNAMESPACE...........:[namespace for pdb] -CDBNAMESPACE...........:[namespace for cdb] -COMPANY................:oracle -APIVERSION.............:v4 ---> do not edit -``` - -⚠ **WARNING: The makefile is intended to speed up the usecase directory configuartion only, it is not supported, the editing and configuration of yaml files for production system is left up to the end user** - -### Pre requisistes: - -- Make sure that **kubectl** is properly configured. -- Make sure that all requirements listed in the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) -- Make sure that administrative user on the container database is configured as documented. - -### Commands - -Review your configuraton running ```make check```; if all the parameters are correct then you can proceed with yaml files and certificates generation - -By excuting command ```make operator``` You will have in your directory an operator yaml file with the WATCH LIST required to operate with multiple namespaces. -Note that the yaml file is not applyed; you need to manually execute ```kubectl apply -f oracle-database-operator.yaml```. - -```bash -make operator -``` -You can generate all the other yaml files for pdb life cycle management using ```make genyaml``` - -```bash -make genyaml -``` - -list of generated yaml files - -```text --rw-r--r-- 1 mmalvezz g900 137142 Nov 13 09:35 oracle-database-operator.yaml --rw-r--r-- 1 mmalvezz g900 321 Nov 13 10:27 create_cdb_secrets.yaml --rw-r--r-- 1 mmalvezz g900 234 Nov 13 10:27 create_pdb_secrets.yaml --rw-r--r-- 1 mmalvezz g900 381 Nov 13 10:27 pdbnamespace_binding.yaml --rw-r--r-- 1 mmalvezz g900 381 Nov 13 10:27 cdbnamespace_binding.yaml --rw-r--r-- 1 mmalvezz g900 1267 Nov 13 10:27 create_ords_pod.yaml --rw-r--r-- 1 mmalvezz g900 935 Nov 13 10:27 create_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 935 Nov 13 10:27 create_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 open_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 open_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 845 Nov 13 10:27 open_pdb3_resource.yaml --rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 close_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 close_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 846 Nov 13 10:27 close_pdb3_resource.yaml --rw-r--r-- 1 mmalvezz g900 927 Nov 13 10:27 clone_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 928 Nov 13 10:27 clone_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 802 Nov 13 10:27 delete_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 802 Nov 13 10:27 delete_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 824 Nov 13 10:27 unplug_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 992 Nov 13 10:27 plug_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 887 Nov 13 10:27 map_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 887 Nov 13 10:27 map_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 890 Nov 13 10:27 map_pdb3_resource.yaml -``` - -The command ```make secretes ``` will configure database secrets credential and certificates secretes - -```bash -make secrets -``` - - - -The makefile includes other different targets that can be used to test the various pdb operations available. E.g. - -```makefile -run03.2: - @$(call msg,"clone pdb2-->pdb4") - $(KUBECTL) apply -f $(PDBCLONE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb2-->pdb4 completed") - $(KUBECTL) get pdb pdb3 -n $(PDBNAMESPACE) -``` -The target ```run03.2``` clones pdb2 into pdb4 and wait for ```$TEST_EXEC_TIMEOUT``` for the operation to complete. - -### Output executions:. - -```make secrets``` - -![image](../images/makesecrets_1_1.png) - - - -```make runall``` executes different pdb operations including the cdb controller creation - -![image](../images/makerunall.png) \ No newline at end of file diff --git a/docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml b/docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml deleted file mode 100644 index 5fd355f4..00000000 --- a/docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: cdbnamespace -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml deleted file mode 100644 index 5723f7c6..00000000 --- a/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml deleted file mode 100644 index 2b9fc70a..00000000 --- a/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb4 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml deleted file mode 100644 index ae837ce0..00000000 --- a/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml deleted file mode 100644 index 1b5d1324..00000000 --- a/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml deleted file mode 100644 index f4a32938..00000000 --- a/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: ""new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_ords_pod.yaml b/docs/multitenant/ords-based/usecase/create_ords_pod.yaml deleted file mode 100644 index ad196c9d..00000000 --- a/docs/multitenant/ords-based/usecase/create_ords_pod.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: CDB -metadata: - name: cdb-dev - namespace: cdbnamespace -spec: - cdbName: "DB12" - ordsImage: _your_container_registry/ords-dboper:latest - ordsImagePullPolicy: "Always" - dbTnsurl : "T H I S I S J U S T A N E X A M P L E (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - replicas: 1 - deletePdbCascade: true - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" - cdbAdminUser: - secret: - secretName: "cdbusr" - key: "e_cdbusr.txt" - cdbAdminPwd: - secret: - secretName: "cdbpwd" - key: "e_cdbpwd.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml deleted file mode 100644 index 84e910e0..00000000 --- a/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml deleted file mode 100644 index 0a71c7c3..00000000 --- a/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml deleted file mode 100644 index 3aba580c..00000000 --- a/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml deleted file mode 100644 index 59b50a64..00000000 --- a/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/makefile b/docs/multitenant/ords-based/usecase/makefile deleted file mode 100644 index dc881598..00000000 --- a/docs/multitenant/ords-based/usecase/makefile +++ /dev/null @@ -1,915 +0,0 @@ -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# | | | | ___| |_ __ ___ _ __ -# | |_| |/ _ \ | '_ \ / _ \ '__| -# | _ | __/ | |_) | __/ | -# |_| |_|\___|_| .__/ \___|_| -# |_| -# -# WARNING: Using this makefile helps you to customize yaml -# files. Edit parameters.txt with your enviroment -# informartion and execute the following steps -# -# 1) make operator -# it configures the operator yaml files with the -# watch namelist required by the multitenant controllers -# -# 2) make genyaml -# It automatically creates all the yaml files based on the -# information available in the parameters file -# -# 3) make secrets -# It configure the required secrets necessary to operate -# with pdbs multitenant controllers -# -# 4) make runall01 -# Start a series of operation create open close delete and so on -# -# LIST OF GENERAED YAML FILE -# -# ----------------------------- ---------------------------------- -# oracle-database-operator.yaml : oracle database operator -# cdbnamespace_binding.yaml : role binding for cdbnamespace -# pdbnamespace_binding.yaml : role binding for pdbnamespace -# create_ords_pod.yaml : create rest server pod -# create_pdb1_resource.yaml : create first pluggable database -# create_pdb2_resource.yaml : create second pluggable database -# open_pdb1_resource.yaml : open first pluggable database -# open_pdb2_resource.yaml : open second pluggable database -# close_pdb1_resource.yaml : close first pluggable database -# close_pdb2_resource.yaml : close second pluggable database -# clone_pdb_resource.yaml : clone thrid pluggable database -# clone_pdb2_resource.yaml : clone 4th pluggable database -# delete_pdb1_resource.yaml : delete first pluggable database -# delete_pdb2_resource.yaml : delete sencond pluggable database -# delete_pdb3_resource.yaml : delete thrid pluggable database -# unplug_pdb1_resource.yaml : unplug first pluggable database -# plug_pdb1_resource.yaml : plug first pluggable database -# map_pdb1_resource.yaml : map the first pluggable database -# config_map.yam : pdb parameters array -# -DATE := `date "+%y%m%d%H%M%S"` -###################### -# PARAMETER SECTIONS # -###################### - -export PARAMETERS=parameters.txt -export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) -export ORDPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDPWD|cut -d : -f 2) -export SYSPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep SYSPWD|cut -d : -f 2) -export WBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBUSER|cut -d : -f 2) -export WBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBPASS|cut -d : -f 2) -export PDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBUSR|cut -d : -f 2) -export PDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBPWD|cut -d : -f 2) -export CDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBUSR|cut -d : -f 2) -export CDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBPWD|cut -d : -f 2) -export PDBNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBNAMESPACE|cut -d : -f 2) -export CDBNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBNAMESPACE|cut -d : -f 2) -export ORDSIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDSIMG|cut -d : -f 2,3) -export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) -export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) -export OPRNAMESPACE=oracle-database-operator-system -export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml -export TEST_EXEC_TIMEOUT=3m -export IMAGE=oracle/ords-dboper:latest -export ORDSIMGDIR=../../../../ords - -REST_SERVER=ords -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -PRVKEY=ca.key -PUBKEY=public.pem -COMPANY=oracle -RUNTIME=/usr/bin/podman - -################# -### FILE LIST ### -################# - -export ORDS_POD=create_ords_pod.yaml - -export CDB_SECRETS=create_cdb_secrets.yaml -export PDB_SECRETS=create_pdb_secrets.yaml - -export PDBCRE1=create_pdb1_resource.yaml -export PDBCRE2=create_pdb2_resource.yaml - -export PDBCLOSE1=close_pdb1_resource.yaml -export PDBCLOSE2=close_pdb2_resource.yaml -export PDBCLOSE3=close_pdb3_resource.yaml - -export PDBOPEN1=open_pdb1_resource.yaml -export PDBOPEN2=open_pdb2_resource.yaml -export PDBOPEN3=open_pdb3_resource.yaml - -export PDBCLONE1=clone_pdb1_resource.yaml -export PDBCLONE2=clone_pdb2_resource.yaml - -export PDBDELETE1=delete_pdb1_resource.yaml -export PDBDELETE2=delete_pdb2_resource.yaml -export PDBDELETE3=delete_pdb3_resource.yaml - -export PDBUNPLUG1=unplug_pdb1_resource.yaml -export PDBPLUG1=plug_pdb1_resource.yaml - -export PDBMAP1=map_pdb1_resource.yaml -export PDBMAP2=map_pdb2_resource.yaml -export PDBMAP3=map_pdb3_resource.yaml - -export PDBMAP1=map_pdb1_resource.yaml -export PDBMAP2=map_pdb2_resource.yaml -export PDBMAP3=map_pdb3_resource.yaml - - -##BINARIES -export KUBECTL=/usr/bin/kubectl -OPENSSL=/usr/bin/openssl -ECHO=/usr/bin/echo -RM=/usr/bin/rm -CP=/usr/bin/cp -TAR=/usr/bin/tar -MKDIR=/usr/bin/mkdir -SED=/usr/bin/sed - -define msg -@printf "\033[31;7m%s\033[0m\r" "......................................]" -@printf "\033[31;7m[\xF0\x9F\x91\x89 %s\033[0m\n" $(1) -endef - -check: - $(call msg,"CHECK PARAMETERS") - @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) - @printf "ORDPWD.................:%s\n" $(ORDPWD) - @printf "SYSPWD.................:%s\n" $(SYSPWD) - @printf "WBUSER.................:%s\n" $(WBUSER) - @printf "WBPASS.................:%s\n" $(WBPASS) - @printf "PDBUSR.................:%s\n" $(PDBUSR) - @printf "PDBPWD.................:%s\n" $(PDBPWD) - @printf "CDBUSR.................:%s\n" $(CDBUSR) - @printf "CDBPWD.................:%s\n" $(CDBPWD) - @printf "PDBNAMESPACE...........:%s\n" $(PDBNAMESPACE) - @printf "CDBNAMESPACE...........:%s\n" $(CDBNAMESPACE) - @printf "COMPANY................:%s\n" $(COMPANY) - @printf "APIVERSION.............:%s\n" $(APIVERSION) - - -tlscrt: - $(call msg,"TLS GENERATION") - #$(OPENSSL) genrsa -out $(PRVKEY) 2048 - $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) - $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) \ - -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj \ - "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(CDBNAMESPACE)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(CDBNAMESPACE)" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - -tlssec: - $(call msg,"GENERATE TLS SECRET") - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(CDBNAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDBNAMESPACE) - - -delsec: - $(call msg,"CLEAN OLD SECRETS") - $(eval SECRETSP:=$(shell kubectl get secrets -n $(PDBNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) - $(eval SECRETSL:=$(shell kubectl get secrets -n $(CDBNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) - @[ "${SECRETSP}" ] && ( \ - printf "Deleteing secrets in namespace -n $(PDBNAMESPACE)\n") &&\ - ($(KUBECTL) delete secret $(SECRETSP) -n $(PDBNAMESPACE))\ - || ( echo "No screts in namespace $(PDBNAMESPACE)") - @[ "${SECRETSL}" ] && ( \ - printf "Deleteing secrets in namespace -n $(CDBNAMESPACE)\n") &&\ - ($(KUBECTL) delete secret $(SECRETSL) -n $(CDBNAMESPACE))\ - || ( echo "No screts in namespace $(PDBNAMESPACE)") - - -###### ENCRYPTED SECRETS ###### -export PRVKEY=ca.key -export PUBKEY=public.pem -WBUSERFILE=wbuser.txt -WBPASSFILE=wbpass.txt -CDBUSRFILE=cdbusr.txt -CDBPWDFILE=cdbpwd.txt -SYSPWDFILE=syspwd.txt -ORDPWDFILE=ordpwd.txt -PDBUSRFILE=pdbusr.txt -PDBPWDFILE=pdbpwd.txt - - - -secrets: delsec tlscrt tlssec - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey="$(PRVKEY)" -n $(PDBNAMESPACE) - @$(ECHO) $(WBUSER) > $(WBUSERFILE) - @$(ECHO) $(WBPASS) > $(WBPASSFILE) - @$(ECHO) $(CDBPWD) > $(CDBPWDFILE) - @$(ECHO) $(CDBUSR) > $(CDBUSRFILE) - @$(ECHO) $(SYSPWD) > $(SYSPWDFILE) - @$(ECHO) $(ORDPWD) > $(ORDPWDFILE) - @$(ECHO) $(PDBUSR) > $(PDBUSRFILE) - @$(ECHO) $(PDBPWD) > $(PDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBPWDFILE) |base64 > e_$(CDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBUSRFILE) |base64 > e_$(CDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE) |base64 > e_$(SYSPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(ORDPWDFILE) |base64 > e_$(ORDPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic cdbpwd --from-file=e_$(CDBPWDFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic cdbusr --from-file=e_$(CDBUSRFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic syspwd --from-file=e_$(SYSPWDFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic ordpwd --from-file=e_$(ORDPWDFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(PDBNAMESPACE) - $(RM) $(WBUSERFILE) $(WBPASSFILE) $(CDBPWDFILE) $(CDBUSRFILE) $(SYSPWDFILE) $(ORDPWDFILE) $(PDBUSRFILE) $(PDBPWDFILE) - $(RM) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(CDBPWDFILE) e_$(CDBUSRFILE) e_$(SYSPWDFILE) e_$(ORDPWDFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) - - -### YAML FILE SECTION ### -operator: - $(CP) ${ORACLE_OPERATOR_YAML} . - ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG - $(SED) -i 's/value: ""/value: $(OPRNAMESPACE),$(PDBNAMESPACE),$(CDBNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` - - -define _script00 -cat < authsection01.yaml - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" - cdbAdminUser: - secret: - secretName: "cdbusr" - key: "e_cdbusr.txt" - cdbAdminPwd: - secret: - secretName: "cdbpwd" - key: "e_cdbpwd.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - -cat< authsection02.yaml - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - - -cat < ${PDBNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: ${PDBNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -cat < ${CDBNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: ${CDBNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -endef -export script00 = $(value _script00) -secyaml: - @ eval "$$script00" - -#echo ords pod creation -define _script01 -cat < ${ORDS_POD} -apiVersion: database.oracle.com/${APIVERSION} -kind: CDB -metadata: - name: cdb-dev - namespace: ${CDBNAMESPACE} -spec: - cdbName: "DB12" - ordsImage: ${ORDSIMG} - ordsImagePullPolicy: "Always" - dbTnsurl : ${TNSALIAS} - replicas: 1 - deletePdbCascade: true -EOF - -cat authsection01.yaml >> ${ORDS_POD} - -endef -export script01 = $(value _script01) - - -define _script02 - -cat <${PDBCRE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat < ${PDBCRE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat <${PDBOPEN1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBOPEN2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBOPEN3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBCLOSE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${PDBCLOSE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${PDBCLOSE3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: ""new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat < ${PDBCLONE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" -EOF - -cat < ${PDBCLONE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb4 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" -EOF - - -cat < ${PDBDELETE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${PDBDELETE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${PDBUNPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" -EOF - -cat <${PDBPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" -EOF - -cat <${PDBMAP1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - -cat <${PDBMAP2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - - -cat <${PDBMAP3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - - -## Auth information -for _file in ${PDBCRE1} ${PDBCRE2} ${PDBOPEN1} ${PDBOPEN2} ${PDBOPEN3} ${PDBCLOSE1} ${PDBCLOSE2} ${PDBCLOSE3} ${PDBCLONE1} ${PDBCLONE2} ${PDBDELETE1} ${PDBDELETE2} ${PDBUNPLUG1} ${PDBPLUG1} ${PDBMAP1} ${PDBMAP2} ${PDBMAP3} -do -ls -ltr ${_file} - cat authsection02.yaml >> ${_file} -done -rm authsection02.yaml -rm authsection01.yaml -endef - -export script02 = $(value _script02) - -genyaml: secyaml - @ eval "$$script01" - @ eval "$$script02" - -cleanyaml: - - $(RM) $(PDBMAP3) $(PDBMAP2) $(PDBMAP1) $(PDBPLUG1) $(PDBUNPLUG1) $(PDBDELETE2) $(PDBDELETE1) $(PDBCLONE2) $(PDBCLONE1) $(PDBCLOSE3) $(PDBCLOSE2) $(PDBCLOSE1) $(PDBOPEN3) $(PDBOPEN2) $(PDBOPEN1) $(PDBCRE2) $(PDBCRE1) $(ORDS_POD) $(CDB_SECRETS) $(PDB_SECRETS) - - $(RM) ${PDBNAMESPACE}_binding.yaml ${CDBNAMESPACE}_binding.yaml - - -cleancrt: - - $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl - - -################# -### PACKAGING ### -################# - -pkg: - - $(RM) -rf /tmp/pkgtestplan - $(MKDIR) /tmp/pkgtestplan - $(CP) -R * /tmp/pkgtestplan - $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ - $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan - -################ -### diag ### -################ - -login: - $(KUBECTL) exec `$(KUBECTL) get pods -n $(CDBNAMESPACE)|grep ords|cut -d ' ' -f 1` -n $(CDBNAMESPACE) -it -- /bin/bash - - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - -####################################################### -#### TEST SECTION #### -####################################################### - -run00: - @$(call msg,"cdb pod creation") - - $(KUBECTL) delete cdb cdb-dev -n $(CDBNAMESPACE) - $(KUBECTL) apply -f $(ORDS_POD) - time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" cdb cdb-dev -n $(CDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"cdb pod completed") - $(KUBECTL) get cdb -n $(CDBNAMESPACE) - $(KUBECTL) get pod -n $(CDBNAMESPACE) - -run01.1: - @$(call msg,"pdb pdb1 creation") - $(KUBECTL) apply -f $(PDBCRE1) - time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 creation completed") - $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) - -run01.2: - @$(call msg, "pdb pdb2 creation") - $(KUBECTL) apply -f $(PDBCRE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb2 creation completed") - $(KUBECTL) get pdb pdb2 -n $(PDBNAMESPACE) - -run02.1: - @$(call msg, "pdb pdb1 open") - $(KUBECTL) apply -f $(PDBOPEN1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 open completed") - $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) - -run02.2: - @$(call msg,"pdb pdb2 open") - $(KUBECTL) apply -f $(PDBOPEN2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb2 open completed") - $(KUBECTL) get pdb pdb2 -n $(PDBNAMESPACE) - - -run03.1: - @$(call msg,"clone pdb1-->pdb3") - $(KUBECTL) apply -f $(PDBCLONE1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb3 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb1-->pdb3 completed") - $(KUBECTL) get pdb pdb3 -n $(PDBNAMESPACE) - - -run03.2: - @$(call msg,"clone pdb2-->pdb4") - $(KUBECTL) apply -f $(PDBCLONE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb2-->pdb4 completed") - $(KUBECTL) get pdb pdb3 -n $(PDBNAMESPACE) - - -run04.1: - @$(call msg,"pdb pdb1 close") - $(KUBECTL) apply -f $(PDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 close completed") - $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) - -run04.2: - @$(call msg,"pdb pdb2 close") - $(KUBECTL) apply -f $(PDBCLOSE2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb2 close completed") - $(KUBECTL) get pdb pdb2 -n $(PDBNAMESPACE) - -run05.1: - @$(call msg,"pdb pdb1 unplug") - $(KUBECTL) apply -f $(PDBUNPLUG1) - $(KUBECTL) wait --for=delete pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb1 unplug completed") - -run06.1: - @$(call msg, "pdb pdb1 plug") - $(KUBECTL) apply -f $(PDBPLUG1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 plug completed") - $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) - -run07.1: - @$(call msg,"pdb pdb1 delete ") - - $(KUBECTL) apply -f $(PDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) apply -f $(PDBDELETE1) - $(KUBECTL) wait --for=delete pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb1 delete") - $(KUBECTL) get pdb -n $(PDBNAMESPACE) - -run99.1: - $(KUBECTL) delete cdb cdb-dev -n cdbnamespace - $(KUBECTL) wait --for=delete cdb cdb-dev -n $(CDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) get cdb -n cdbnamespaace - $(KUBECTL) get pdb -n pdbnamespaace - - -## SEQ | ACTION -## ----+---------------- -## 00 | create ords pod -## 01 | create pdb -## 02 | open pdb -## 03 | clone pdb -## 04 | close pdb -## 05 | unpug pdb -## 06 | plug pdb -## 07 | delete pdb (declarative) - - -runall01: run00 run01.1 run01.2 run03.1 run03.2 run04.1 run05.1 run06.1 run02.1 run07.1 - - -###### BUILD ORDS IMAGE ###### - -createimage: - $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) - -createimageproxy: - $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) - -tagimage: - @echo "TAG IMAGE" - $(RUNTIME) tag $(IMAGE) $(ORDSIMG) - -push: - $(RUNTIME) push $(ORDSIMG) - - diff --git a/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml deleted file mode 100644 index b71b59d5..00000000 --- a/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml deleted file mode 100644 index 75d056d0..00000000 --- a/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml deleted file mode 100644 index 3523aa68..00000000 --- a/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml deleted file mode 100644 index 93a1d43a..00000000 --- a/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml deleted file mode 100644 index deb27f9a..00000000 --- a/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml deleted file mode 100644 index 586f2f57..00000000 --- a/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/parameters.txt b/docs/multitenant/ords-based/usecase/parameters.txt deleted file mode 100644 index 64dc3759..00000000 --- a/docs/multitenant/ords-based/usecase/parameters.txt +++ /dev/null @@ -1,61 +0,0 @@ - -######################## -## REST SERVER IMAGE ### -######################## - -ORDSIMG:_your_container_registry/ords-dboper:latest - -############################## -## TNS URL FOR CDB CREATION ## -############################## -TNSALIAS:"T H I S I S J U S T A N E X A M P L E (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - -########################################### -## ORDS PUBLIC USER ## -########################################### -ORDPWD:change_me_please - -########################################### -## SYSPASSWORD ## -########################################### -SYSPWD:change_me_please - -####################### -## HTTPS CREDENTIAL ### -####################### - -WBUSER:change_me_please -WBPASS:change_me_please - -##################### -## PDB ADMIN USER ### -##################### - -PDBUSR:change_me_please -PDBPWD:change_me_please - -##################### -## CDB ADMIN USER ### -##################### - -CDBUSR:C##DBAPI_CDB_ADMIN -CDBPWD:change_me_please - -################### -### NAMESPACES #### -################### - -PDBNAMESPACE:pdbnamespace -CDBNAMESPACE:cdbnamespace - -#################### -### COMPANY NAME ### -#################### - -COMPANY:oracle - -#################### -### APIVERSION ### -#################### - -APIVERSION:v4 diff --git a/docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml b/docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml deleted file mode 100644 index 5af79ed6..00000000 --- a/docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: pdbnamespace -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml deleted file mode 100644 index 9eb5ed77..00000000 --- a/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml deleted file mode 100644 index 0036d5f7..00000000 --- a/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/README.md b/docs/multitenant/ords-based/usecase01/README.md deleted file mode 100644 index 0020541c..00000000 --- a/docs/multitenant/ords-based/usecase01/README.md +++ /dev/null @@ -1,516 +0,0 @@ - - - -# STEP BY STEP USE CASE - -- [STEP BY STEP USE CASE](#step-by-step-use-case) - - [INTRODUCTION](#introduction) - - [OPERATIONAL STEPS](#operational-steps) - - [Download latest version from github ](#download-latest-version-from-github-) - - [Upload webhook certificates ](#upload-webhook-certificates-) - - [Create the dboperator ](#create-the-dboperator-) - - [Create secret for container registry](#create-secret-for-container-registry) - - [Build ords immage ](#build-ords-immage-) - - [Database Configuration](#database-configuration) - - [Create CDB secret](#create-cdb-secret) - - [Create Certificates](#create-certificates) - - [Apply cdb.yaml](#apply-cdbyaml) - - [CDB - Logs and throuble shutting](#cdb---logs-and-throuble-shutting) - - [Create PDB secret](#create-pdb-secret) - - [Apply pdb yaml file to create pdb](#apply-pdb-yaml-file-to-create-pdb) - - [Other actions](#other-actions) - - [Imperative approach on pdb deletion - will be avilable in 1.2.0 ](#imperative-approach-on-pdb-deletion) - - - -##### INTRODUCTION - -This readme is a step by step guide used to implement database multi tenant operator. It assumes that a kubernets cluster and a database server are already available (no matter if single instance or RAC). kubectl must be configured in order to reach k8s cluster. - -The following table reports the parameters required to configure and use oracle multi tenant controller for pluggable database lifecycle management. - -| yaml file parameters | value | description /ords parameter | -|-------------- |--------------------------- |-------------------------------------------------| -| dbserver | or | [--db-hostname][1] | -| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | -| port | | [--db-port][2] | -| cdbName | | Container Name | -| name | | Ords podname prefix in cdb.yaml | -| name | | pdb resource in pdb.yaml | -| ordsImage | /ords-dboper:latest|My public container registry | -| pdbName | | Pluggable database name | -| servicename | | [--db-servicename][3] | -| sysadmin_user | | [--admin-user][adminuser] | -| sysadmin_pwd | | [--password-stdin][pwdstdin] | -| cdbadmin_user | | [db.cdb.adminUser][1] | -| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | -| webserver_user| | [https user][http] NOT A DB USER | -| webserver_pwd | | [http user password][http] | -| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | -| pdbTlsKey | | [standalone.https.cert.key][key] | -| pdbTlsCrt | | [standalone.https.cert][cr] | -| pdbTlsCat | | certificate authority | -| cdbOrdsPrvKey | | private key (cdb crd) | -| pdbOrdsPrvKey | | private key (pdb crd) | -| assertivePdbDeletion | boolean | [turn on imperative approach on crd deleteion][imperative] | - -> A [makfile](./makefile) is available to sped up the command execution for the multitenant setup and test. See the comments in the header of file - -### OPERATIONAL STEPS ----- - - -#### Download latest version from github - - -```bash -git clone https://github.com/oracle/oracle-database-operator.git -``` - -If golang compiler is installed on your environment and you've got a public container registry then you can compile the operator, upload to the registry and use it - -```bash - -cd oracle-database-operator -make generate -make manifests -make install -make docker-build IMG=/operator:latest - -make operator-yaml IMG=operator:latest -``` - -> **NOTE:** The last make executions recreates the **oracle-database-operator.yaml** with the **image:** parameter pointing to your public container registry. If you don't have a golang compilation environment you can use the **oracle-database-operator.yaml** provided in the github distribution. Check [operator installation documentation](../installation/OPERATOR_INSTALLATION_README.md ) for more details. - -> **NOTE:** If you are using oracle-container-registry make sure to accept the license agreement otherwise the operator image pull fails. ----- - -#### Upload webhook certificates - -```bash -kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -``` - -#### Create the dboperator - -```bash -cd oracle-database-operator -/usr/bin/kubectl apply -f oracle-database-operator.yaml -``` -+ Check the status of the operator - -```bash -/usr/bin/kubectl get pods -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -oracle-database-operator-controller-manager-557ff6c659-g7t66 1/1 Running 0 10s -oracle-database-operator-controller-manager-557ff6c659-rssmj 1/1 Running 0 10s -oracle-database-operator-controller-manager-557ff6c659-xpswv 1/1 Running 0 10s - -``` ----- - -#### Create secret for container registry - -+ Make sure to login to your container registry and then create the secret for you container registry. - -```bash -docker login **** -/usr/bin/kubectl create secret generic container-registry-secret --from-file=.dockerconfigjson=/home/oracle/.docker/config.json --type=kubernetes.io/dockerconfigjson -n oracle-database-operator-system -``` - -+ Check secret - -```bash -kubectl get secret -n oracle-database-operator-system -NAME TYPE DATA AGE -container-registry-secret kubernetes.io/dockerconfigjson 1 19s -webhook-server-cert kubernetes.io/tls -``` ----- - -#### Build ords immage - -+ Build the ords image, downloading ords software is no longer needed; just build the image and push it to your repository - -```bash -cd oracle-database-operator/ords -docker build -t oracle/ords-dboper:latest . -``` - -[Example of execution](./logfiles/BuildImage.log) -+ Login to your container registry and push the ords image. - -```bash -docker tag /ords-dboper:latest -docker push /ords-dboper:latest -``` -[Example of execution](./logfiles/tagandpush.log) - ----- - -#### Database Configuration - -+ Configure Database - -Connect as sysdba and execute the following script in order to create the required ords accounts. - -```sql -ALTER SESSION SET "_oracle_script"=true; -DROP USER cascade; -CREATE USER IDENTIFIED BY CONTAINER=ALL ACCOUNT UNLOCK; -GRANT SYSOPER TO CONTAINER = ALL; -GRANT SYSDBA TO CONTAINER = ALL; -GRANT CREATE SESSION TO CONTAINER = ALL; -``` ----- -#### Create Certificates - -+ Create certificates: At this stage we need to create certificates on our local machine and upload into kubernetes cluster by creating new secrets. - - - -```text - - +-----------+ - | openssl | - +-----------+ - | - | - +-----------+ - | tls.key | - | tls.crt +------------+ - | ca.crt | | - +-----------+ | - | - +------------------------|---------------------------+ - |KUBERNETES +------+--------+ | - |CLUSTER +---|kubernet secret|---+ | - | | +---------------+ | | - | | | | - | +----------+---+ https +--+----------+ | - | |ORDS CONTAINER|<-------------->| PDB/POD | | - | +----------+---+ +-------------+ | - | cdb.yaml | pdb.yaml | - +-------------|--------------------------------------+ - | - | - +-----------+ - | DB SERVER | - +-----------+ - -``` - -```bash - -openssl genrsa -out 2048 -openssl req -new -x509 -days 365 -key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out -openssl req -newkey rsa:2048 -nodes -keyout -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=-ords" -out server.csr -/usr/bin/echo "subjectAltName=DNS:-ords,DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA -CAkey -CAcreateserial -out - -kubectl create secret tls db-tls --key="" --cert="" -n oracle-database-operator-system -kubectl create secret generic db-ca --from-file= -n oracle-database-operator-system - -``` - -[Example of execution:](./logfiles/openssl_execution.log) - -#### CDB and PDB credential - -Refer to the [landing page](../README.md) to implement openssl encrpted secrets. - ----- - -#### Apply cdb.yaml - - -**note:** - Before creating the CDB pod make sure that all the pluggable databases in the container DB are open. - - -+ Create ords container - -```bash -/usr/bin/kubectl apply -f create_ords_pod.yaml -n oracle-database-operator-system -``` -Example: **create_ords_pod.yaml** - -```yaml -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" - cdbAdminUser: - secret: - secretName: "cdbusr" - key: "e_cdbusr.txt" - cdbAdminPwd: - secret: - secretName: "cdbpwd" - key: "e_cdbpwd.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - - -``` -> **Note** if you are working in dataguard environment with multiple sites (AC/DR) specifying the host name (dbServer/dbPort/serviceName) may not be the suitable solution for this kind of configuration, use **dbTnsurl** instead. Specify the whole tns string which includes the hosts/scan list. - -``` - +----------+ - ____| standbyB | - | | scanB | (DESCRIPTION= - +----------+ | +----------+ (CONNECT_TIMEOUT=90) - | primary |_______| (RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70) - | scanA | | +----------+ (TRANSPORT_CONNECT_TIMEOUT=10)(LOAD_BALLANCE=ON) - +----------+ |___| stanbyC | (ADDRESS=(PROTOCOL=TCP)(HOST=scanA.testrac.com)(PORT=1521)(IP=V4_ONLY)) - | scanC | (ADDRESS=(PROTOCOL=TCP)(HOST=scanB.testrac.com)(PORT=1521)(IP=V4_ONLY)) - +----------+ (ADDRESS=(PROTOCOL=TCP)(HOST=scanC.testrac.com)(PORT=1521)(IP=V4_ONLY)) - (CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) - - - dbtnsurl:((DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(TRANS...... -``` - -[create_ords_pod.yaml example](./create_ords_pod.yaml) - - ----- - -#### CDB - Logs and throuble shutting - -+ Check the status of ords container - -```bash -/usr/bin/kubectl get pods -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -cdb-dev-ords-rs-m9ggp 0/1 ContainerCreating 0 67s <----- -oracle-database-operator-controller-manager-557ff6c659-g7t66 1/1 Running 0 11m -oracle-database-operator-controller-manager-557ff6c659-rssmj 1/1 Running 0 11m -oracle-database-operator-controller-manager-557ff6c659-xpswv 1/1 Running 0 11m -``` -+ Make sure that the cdb container is running - -```bash -/usr/bin/kubectl get pods -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -cdb-dev-ords-rs-dnshz 1/1 Running 0 31s -oracle-database-operator-controller-manager-557ff6c659-9bjfl 1/1 Running 0 2m42s -oracle-database-operator-controller-manager-557ff6c659-cx8hd 1/1 Running 0 2m42s -oracle-database-operator-controller-manager-557ff6c659-rq9xs 1/1 Running 0 2m42s -``` -+ Check the status of the services - -```bash -kubectl get cdb -n oracle-database-operator-system -NAME CDB NAME DB SERVER DB PORT REPLICAS STATUS MESSAGE -[.....................................................] Ready -``` -+ Use log file to trouble shutting - -```bash -/usr/bin/kubectl logs `/usr/bin/kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system -``` -[Example of cdb creation log](./logfiles/cdb_creation.log) - -+ Test REST API from the pod. By querying the metadata catalog you can verify the status of https setting - -```bash - /usr/bin/kubectl exec -it `/usr/bin/kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system -i -t -- /usr/bin/curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ -``` -[Example of execution](./logfiles/testapi.log) - -+ Verify the pod environment varaibles - ```bash - kubectl set env pods --all --list -n oracle-database-operator-system - ``` - -+ Connect to cdb pod - -```bash - kubectl exec -it `kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system bash -``` -+ Dump ords server configuration - -```bash -/usr/bin/kubectl exec -it `/usr/bin/kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system -i -t -- /usr/local/bin/ords --config /etc/ords/config config list -``` -[Example of executions](./logfiles/ordsconfig.log) - ------ -#### Apply pdb yaml file to create pdb - -```bash -/usr/bin/kubectl apply -f create_pdb1_resource.yaml -n oracle-database-operator-system -``` - -Example: **create_pdb1_resource.yaml** - -```yaml -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -``` - -+ Monitor the pdb creation status until message is success - -```bash -kubectl get pdbs --all-namespaces=true - - +-----------------------------------------+ +-----------------------------------------+ - | STATUS MESSAGE |______\ | STATUS MESSAGE | - | Creating Waiting for PDB to be created | / | Ready Success | - +-----------------------------------------+ +-----------------------------------------+ - -NAMESPACE NAME DBSERVER CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system 1G Creating Waiting for PDB to be created - -[wait sometimes] - -kubectl get pdbs --all-namespaces=true -NAMESPACE NAME DBSERVER CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -oracle-database-operator-system pdb1 READ WRITE 1G Ready Success -``` - -Connect to the hosts and verify the PDB creation. - -```text -[oracle@racnode1 ~]$ sqlplus '/as sysdba' -[...] -Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production -Version 19.15.0.0.0 - - -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ ONLY NO - 3 PDBDEV READ WRITE NO - -``` -Check controller log to debug pluggable database life cycle actions in case of problem - -```bash -kubectl logs -f $(kubectl get pods -n oracle-database-operator-system|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1) -n oracle-database-operator-system -``` - ---- - -#### Other actions - -Configure and use other yaml files to perform pluggable database life cycle managment action **pdb_open.yaml** **pdb_close.yaml** - -> **Note** sql command *"alter pluggable database open instances=all;"* acts only on closed databases, so you don't get any oracle error in case of execution against an pluggable database already opened - -#### Imperative approach on pdb deletion - -If **assertivePdbDeletion** is true then the command execution **kubectl delete pdbs crd_pdb_name** automatically deletes the pluggable database on the container database. By default this option is disabled. You can use this option during **create**,**map**,**plug** and **clone** operation. If the option is disabled then **kubectl delete** only deletes the crd but not the pluggable on the container db. Database deletion uses the option **including datafiles**. -If you drop the CRD without dropping the pluggable database and you need to recreate the CRD then you can use the [pdb_map.yaml](./pdb_map.yaml) - - -[1]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation - -[2]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation - -[3]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-DAA027FA-A4A6-43E1-B8DD-C92B330C2341:~:text=%2D%2Ddb%2Dservicename%20%3Cstring%3E - -[adminuser]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22:~:text=Table%202%2D6%20Command%20Options%20for%20Uninstall%20CLI - -[public_user]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/using-multitenant-architecture-oracle-rest-data-services.html#GUID-E64A141A-A71F-4979-8D33-C5F8496D3C19:~:text=Preinstallation%20Tasks%20for%20Oracle%20REST%20Data%20Services%20CDB%20Installation - -[key]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=standalone.https.cert.key - -[cr]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0 - -[cdbadminpwd]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=Table%20C%2D1%20Oracle%20REST%20Data%20Services%20Configuration%20Settings - -[pwdstdin]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-88479C84-CAC1-4133-A33E-7995A645EC05:~:text=default%20database%20pool.-,2.1.4.1%20Understanding%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation,-Table%202%2D2 - -[http]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-BEECC057-A8F5-4EAB-B88E-9828C2809CD8:~:text=Example%3A%20delete%20%5B%2D%2Dglobal%5D-,user%20add,-Add%20a%20user - -[dbtnsurl]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22 - -[imperative]:https://kubernetes.io/docs/concepts/overview/working-with-objects/object-management/ - - diff --git a/docs/multitenant/ords-based/usecase01/ca.crt b/docs/multitenant/ords-based/usecase01/ca.crt deleted file mode 100644 index cc9aa8bb..00000000 --- a/docs/multitenant/ords-based/usecase01/ca.crt +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEJTCCAw2gAwIBAgIUNXPtpnNEFBCMcnxRP5kJsBDpafcwDQYJKoZIhvcNAQEL -BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH -DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k -ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE -AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx -NTMyMzVaMIGhMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG -A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j -ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxHDAa -BgNVBAMME2xvY2FsaG9zdCAgUm9vdCBDQSAwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQCmnGVApwUBF1kpqcyr2nYeED0VKvefpoHLtxHSP+vP0lWhW7NU -NJlb1YuUagjJ4/rpGRQmPxcVU51n3aAW3a5qHazIpNxNa3fvgB1rMOPFxGmdel2d -8lIt+u19q19DknX/GNgH9Mog8RcyZyPeA7d2icT8TBo74ognr+8p68O3CjBHQ8EM -SnRQR7/bh1c10Uia317ilKvs+I7oErTq5JFLeIuPDdAJ6UncaeblTf1XJ/1FrpHG -fSS7xmR8x0/MblBQlku4eImYmN35g+eRgf8bLDDwC+GPzDnAqqMLjx6h2N+btDxr -tnn05qyqmN9G08uUlP4d4BXi9ISb/toYypklAgMBAAGjUzBRMB0GA1UdDgQWBBS+ -a4X2XTmdPivdQtqDWNpfOtHypDAfBgNVHSMEGDAWgBS+a4X2XTmdPivdQtqDWNpf -OtHypDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZIrGBNdSw -pe+1agefHfaR8hjZQiXBxdwHM1gR2LWOaFzMS8Q/eRETHTO6+VwQ0/FNaXbAqgqk -G317gZMXS5ZmXuOi28fTpAQtuzokkEKpoK0puTnbXOKGA2QSbBlpSFPqb3aJXvVt -afXFQb5P/0mhr4kuVt7Ech82WM/o5ryFgObygDayDmLatTp+VaRmBZPksnSMhslq -3zPyS7bx2YhbPTLkDxq8Mfr/Msxme8LvSXUpFf4PpQ5zwp1RE32gekct6eRQLmqU -5LXY2aPtqpMF0fBpcwPWbqA9gOYCRKcvXXIr+u1x8hf6Er6grZegHkM9TQ8s0hJd -sxi5tK0lPMHJ ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase01/ca.key b/docs/multitenant/ords-based/usecase01/ca.key deleted file mode 100644 index 1a0ef89d..00000000 --- a/docs/multitenant/ords-based/usecase01/ca.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAppxlQKcFARdZKanMq9p2HhA9FSr3n6aBy7cR0j/rz9JVoVuz -VDSZW9WLlGoIyeP66RkUJj8XFVOdZ92gFt2uah2syKTcTWt374AdazDjxcRpnXpd -nfJSLfrtfatfQ5J1/xjYB/TKIPEXMmcj3gO3donE/EwaO+KIJ6/vKevDtwowR0PB -DEp0UEe/24dXNdFImt9e4pSr7PiO6BK06uSRS3iLjw3QCelJ3Gnm5U39Vyf9Ra6R -xn0ku8ZkfMdPzG5QUJZLuHiJmJjd+YPnkYH/Gyww8Avhj8w5wKqjC48eodjfm7Q8 -a7Z59OasqpjfRtPLlJT+HeAV4vSEm/7aGMqZJQIDAQABAoIBAGXRGYdjCgnarOBr -Jeq3vIsuvUVcVqs35AYMQFXOPltoXHAZTAPfiQC4BW6TRf+q1MDyVH/y+jZMPNsm -cxjGLDopHFgZd4/QZyDzmAbTf75yA2D7UI6fcV0sBUpRGgx/SqC0HADwtT1gWB6z -LRYWC13jX4AXOcjy7OXj/DIQJDCMivedt3dv0rDWJUcBCnVot5tr6zjycefxGKa8 -mG9LZQb3x71FxwpFUau3WLDSwOjtXCeMytaGXnGmIiofJmXnFi0KA4ApzKL7QV6I -cCBS1WBLLXeVM9vOfrtzKVLWGe0qADyLm35p5Fnl3j+vimkk8h/2DEvCZ75c987m -O3PEgdkCgYEA0Scg+KINTA78sdZL5v2+8fT4b+EfoCgUqfr10ReUPKrz3HfrVHcj -7Vf00RT52TkfmkL3mIdLyBUzQ9vzPgweo1o4yKCKNCpR9G3ydNW+KI5jSYnq2efz -Gpe3wTt+8YoyCgm9eUxNWjfO9fipS91sSotY0PovkBohj9aezfcWp1sCgYEAy+3n -MIvW/9PoYxCvQ9fDGLvx3B4/uy0ZYPh7j5edDuaRzwFd2YXUysXhJVuqTp0KT2tv -dRPFRE9Oq5N8e5ITIUiKLQ5PIRNBZm8CiAof+XS1fIuU+MTDaTfXwyGQo0xSg8MB -ITnJulmUlkcTWEtGyBi9sIjor5ve8kqvyrdAKX8CgYA9ZUUSd0978jJPad6iEf6J -PCXpgaYs91cJhre+BzPmkzA+mZ0lEEwlkdo1vfiRwWj7eYkA50Zhl4eS9e/zWM9t -mEBu9GFdasbf/55amZvWf+W5YpjkGmiMd9jjCjn7YVvLAozyHGngf91q6vGXaYou -X7VUsvxfSqxrcs7vGwc1XQKBgB0qaD80MMqj5v+MGlTsndWCw8OEe/7sI04QG7Pc -rjS8Wyws+NwsXNOnW1z5cDEQGrJjHiyzaCot4YV+cXZG3P+MnV52RnDnjRn2VHla -YVpPC8nFOMgfdAcvWmdo/IOuXbrEf/vdhPFm8G5Ruf2NvpDNoQuHeSfsdgVXEy89 -6CpHAoGBAMZInYD0XjcnZNqiQnQdcIJN3CqDIU76Z45OOpcUrYrvTos2xhGLrRI5 -qrk5Od/sovJfse+oUIIbgsABieqtyfxM03iu8fvbahIY6Un1iw2KN9t+mcPrSZJK -jTXKf7XxZ1+yN9kvohdLc65ySyXFSm++glDq8WGrmnOtLUlr0oMm ------END RSA PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase01/ca.srl b/docs/multitenant/ords-based/usecase01/ca.srl deleted file mode 100644 index 7c9868bb..00000000 --- a/docs/multitenant/ords-based/usecase01/ca.srl +++ /dev/null @@ -1 +0,0 @@ -77D97AB4C4B6D5A9377B84B455D3E16348C6DE04 diff --git a/docs/multitenant/ords-based/usecase01/cdb_create.yaml b/docs/multitenant/ords-based/usecase01/cdb_create.yaml deleted file mode 100644 index 01fc0a18..00000000 --- a/docs/multitenant/ords-based/usecase01/cdb_create.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - diff --git a/docs/multitenant/ords-based/usecase01/cdb_secret.yaml b/docs/multitenant/ords-based/usecase01/cdb_secret.yaml deleted file mode 100644 index 567b90a4..00000000 --- a/docs/multitenant/ords-based/usecase01/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - cdbadmin_user: ".....base64 encoded password...." - cdbadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." diff --git a/docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml deleted file mode 100644 index 3cc2c3dd..00000000 --- a/docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml deleted file mode 100644 index 28a4eab6..00000000 --- a/docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb4 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml deleted file mode 100644 index a5c3cf59..00000000 --- a/docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml deleted file mode 100644 index 7fa15111..00000000 --- a/docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml deleted file mode 100644 index fa7cf009..00000000 --- a/docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: ""new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/create_ords_pod.yaml b/docs/multitenant/ords-based/usecase01/create_ords_pod.yaml deleted file mode 100644 index e39c4c56..00000000 --- a/docs/multitenant/ords-based/usecase01/create_ords_pod.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: _your_container_registry/ords-dboper:latest - ordsImagePullPolicy: "Always" - dbTnsurl : "T H I S I S J U S T A N E X A M P L E ....(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - replicas: 1 - deletePdbCascade: true - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" - cdbAdminUser: - secret: - secretName: "cdbusr" - key: "e_cdbusr.txt" - cdbAdminPwd: - secret: - secretName: "cdbpwd" - key: "e_cdbpwd.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml deleted file mode 100644 index 044d466b..00000000 --- a/docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml deleted file mode 100644 index eb36aaa2..00000000 --- a/docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml deleted file mode 100644 index b0816929..00000000 --- a/docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml deleted file mode 100644 index d2ad95cc..00000000 --- a/docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/extfile.txt b/docs/multitenant/ords-based/usecase01/extfile.txt deleted file mode 100644 index c51d22a3..00000000 --- a/docs/multitenant/ords-based/usecase01/extfile.txt +++ /dev/null @@ -1 +0,0 @@ -subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com diff --git a/docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log b/docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log deleted file mode 100644 index f35c66d8..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log +++ /dev/null @@ -1,896 +0,0 @@ -/usr/bin/docker build -t oracle/ords-dboper:latest ../../../ords -Sending build context to Docker daemon 13.82kB -Step 1/12 : FROM container-registry.oracle.com/java/jdk:latest - ---> b8457e2f0b73 -Step 2/12 : ENV ORDS_HOME=/opt/oracle/ords/ RUN_FILE="runOrdsSSL.sh" ORDSVERSION=23.4.0-8 - ---> Using cache - ---> 3317a16cd6f8 -Step 3/12 : COPY $RUN_FILE $ORDS_HOME - ---> 7995edec33cc -Step 4/12 : RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && yum -y install java-11-openjdk-devel && yum -y install iproute && yum clean all - ---> Running in fe168b01f3ad -Oracle Linux 8 BaseOS Latest (x86_64) 91 MB/s | 79 MB 00:00 -Oracle Linux 8 Application Stream (x86_64) 69 MB/s | 62 MB 00:00 -Last metadata expiration check: 0:00:12 ago on Tue 20 Aug 2024 08:54:50 AM UTC. -Package yum-utils-4.0.21-23.0.1.el8.noarch is already installed. -Package tar-2:1.30-9.el8.x86_64 is already installed. -Package vim-minimal-2:8.0.1763-19.0.1.el8_6.4.x86_64 is already installed. -Package procps-ng-3.3.15-14.0.1.el8.x86_64 is already installed. -Package curl-7.61.1-33.el8_9.5.x86_64 is already installed. -Dependencies resolved. -================================================================================ - Package Arch Version Repository Size -================================================================================ -Installing: - bind-utils x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 453 k - expect x86_64 5.45.4-5.el8 ol8_baseos_latest 266 k - hostname x86_64 3.20-6.el8 ol8_baseos_latest 32 k - lsof x86_64 4.93.2-1.el8 ol8_baseos_latest 253 k - net-tools x86_64 2.0-0.52.20160912git.el8 ol8_baseos_latest 322 k - openssl x86_64 1:1.1.1k-12.el8_9 ol8_baseos_latest 710 k - sudo x86_64 1.9.5p2-1.el8_9 ol8_baseos_latest 1.0 M - tree x86_64 1.7.0-15.el8 ol8_baseos_latest 59 k - unzip x86_64 6.0-46.0.1.el8 ol8_baseos_latest 196 k - wget x86_64 1.19.5-12.0.1.el8_10 ol8_appstream 733 k - which x86_64 2.21-20.el8 ol8_baseos_latest 50 k - zip x86_64 3.0-23.el8 ol8_baseos_latest 270 k -Upgrading: - curl x86_64 7.61.1-34.el8 ol8_baseos_latest 352 k - dnf-plugins-core noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 76 k - libcurl x86_64 7.61.1-34.el8 ol8_baseos_latest 303 k - python3-dnf-plugins-core - noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 263 k - yum-utils noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 75 k -Installing dependencies: - bind-libs x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 176 k - bind-libs-lite x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 1.2 M - bind-license noarch 32:9.11.36-16.el8_10.2 ol8_appstream 104 k - fstrm x86_64 0.6.1-3.el8 ol8_appstream 29 k - libmaxminddb x86_64 1.2.0-10.el8_9.1 ol8_appstream 32 k - libmetalink x86_64 0.1.3-7.el8 ol8_baseos_latest 32 k - protobuf-c x86_64 1.3.0-8.el8 ol8_appstream 37 k - python3-bind noarch 32:9.11.36-16.el8_10.2 ol8_appstream 151 k - python3-ply noarch 3.9-9.el8 ol8_baseos_latest 111 k - tcl x86_64 1:8.6.8-2.el8 ol8_baseos_latest 1.1 M -Installing weak dependencies: - geolite2-city noarch 20180605-1.el8 ol8_appstream 19 M - geolite2-country noarch 20180605-1.el8 ol8_appstream 1.0 M - -Transaction Summary -================================================================================ -Install 24 Packages -Upgrade 5 Packages - -Total download size: 28 M -Downloading Packages: -(1/29): hostname-3.20-6.el8.x86_64.rpm 268 kB/s | 32 kB 00:00 -(2/29): libmetalink-0.1.3-7.el8.x86_64.rpm 257 kB/s | 32 kB 00:00 -(3/29): expect-5.45.4-5.el8.x86_64.rpm 1.4 MB/s | 266 kB 00:00 -(4/29): lsof-4.93.2-1.el8.x86_64.rpm 3.2 MB/s | 253 kB 00:00 -(5/29): net-tools-2.0-0.52.20160912git.el8.x86_ 3.6 MB/s | 322 kB 00:00 -(6/29): python3-ply-3.9-9.el8.noarch.rpm 2.7 MB/s | 111 kB 00:00 -(7/29): openssl-1.1.1k-12.el8_9.x86_64.rpm 10 MB/s | 710 kB 00:00 -(8/29): tree-1.7.0-15.el8.x86_64.rpm 2.2 MB/s | 59 kB 00:00 -(9/29): sudo-1.9.5p2-1.el8_9.x86_64.rpm 14 MB/s | 1.0 MB 00:00 -(10/29): unzip-6.0-46.0.1.el8.x86_64.rpm 6.8 MB/s | 196 kB 00:00 -(11/29): which-2.21-20.el8.x86_64.rpm 2.0 MB/s | 50 kB 00:00 -(12/29): tcl-8.6.8-2.el8.x86_64.rpm 13 MB/s | 1.1 MB 00:00 -(13/29): bind-libs-9.11.36-16.el8_10.2.x86_64.r 6.7 MB/s | 176 kB 00:00 -(14/29): zip-3.0-23.el8.x86_64.rpm 8.4 MB/s | 270 kB 00:00 -(15/29): bind-libs-lite-9.11.36-16.el8_10.2.x86 29 MB/s | 1.2 MB 00:00 -(16/29): bind-license-9.11.36-16.el8_10.2.noarc 3.3 MB/s | 104 kB 00:00 -(17/29): bind-utils-9.11.36-16.el8_10.2.x86_64. 13 MB/s | 453 kB 00:00 -(18/29): fstrm-0.6.1-3.el8.x86_64.rpm 1.2 MB/s | 29 kB 00:00 -(19/29): libmaxminddb-1.2.0-10.el8_9.1.x86_64.r 1.3 MB/s | 32 kB 00:00 -(20/29): geolite2-country-20180605-1.el8.noarch 17 MB/s | 1.0 MB 00:00 -(21/29): protobuf-c-1.3.0-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 -(22/29): python3-bind-9.11.36-16.el8_10.2.noarc 5.8 MB/s | 151 kB 00:00 -(23/29): wget-1.19.5-12.0.1.el8_10.x86_64.rpm 17 MB/s | 733 kB 00:00 -(24/29): curl-7.61.1-34.el8.x86_64.rpm 12 MB/s | 352 kB 00:00 -(25/29): dnf-plugins-core-4.0.21-25.0.1.el8.noa 2.4 MB/s | 76 kB 00:00 -(26/29): libcurl-7.61.1-34.el8.x86_64.rpm 8.6 MB/s | 303 kB 00:00 -(27/29): python3-dnf-plugins-core-4.0.21-25.0.1 9.8 MB/s | 263 kB 00:00 -(28/29): yum-utils-4.0.21-25.0.1.el8.noarch.rpm 3.0 MB/s | 75 kB 00:00 -(29/29): geolite2-city-20180605-1.el8.noarch.rp 66 MB/s | 19 MB 00:00 --------------------------------------------------------------------------------- -Total 43 MB/s | 28 MB 00:00 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Preparing : 1/1 - Running scriptlet: protobuf-c-1.3.0-8.el8.x86_64 1/1 - Installing : protobuf-c-1.3.0-8.el8.x86_64 1/34 - Installing : fstrm-0.6.1-3.el8.x86_64 2/34 - Installing : bind-license-32:9.11.36-16.el8_10.2.noarch 3/34 - Upgrading : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 4/34 - Upgrading : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 5/34 - Upgrading : libcurl-7.61.1-34.el8.x86_64 6/34 - Installing : geolite2-country-20180605-1.el8.noarch 7/34 - Installing : geolite2-city-20180605-1.el8.noarch 8/34 - Installing : libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 - Running scriptlet: libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 - Installing : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 10/34 - Installing : bind-libs-32:9.11.36-16.el8_10.2.x86_64 11/34 - Installing : unzip-6.0-46.0.1.el8.x86_64 12/34 - Installing : tcl-1:8.6.8-2.el8.x86_64 13/34 - Running scriptlet: tcl-1:8.6.8-2.el8.x86_64 13/34 - Installing : python3-ply-3.9-9.el8.noarch 14/34 - Installing : python3-bind-32:9.11.36-16.el8_10.2.noarch 15/34 - Installing : libmetalink-0.1.3-7.el8.x86_64 16/34 - Installing : wget-1.19.5-12.0.1.el8_10.x86_64 17/34 - Running scriptlet: wget-1.19.5-12.0.1.el8_10.x86_64 17/34 - Installing : bind-utils-32:9.11.36-16.el8_10.2.x86_64 18/34 - Installing : expect-5.45.4-5.el8.x86_64 19/34 - Installing : zip-3.0-23.el8.x86_64 20/34 - Upgrading : curl-7.61.1-34.el8.x86_64 21/34 - Upgrading : yum-utils-4.0.21-25.0.1.el8.noarch 22/34 - Installing : which-2.21-20.el8.x86_64 23/34 - Installing : tree-1.7.0-15.el8.x86_64 24/34 - Installing : sudo-1.9.5p2-1.el8_9.x86_64 25/34 - Running scriptlet: sudo-1.9.5p2-1.el8_9.x86_64 25/34 - Installing : openssl-1:1.1.1k-12.el8_9.x86_64 26/34 - Installing : net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 - Running scriptlet: net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 - Installing : lsof-4.93.2-1.el8.x86_64 28/34 - Installing : hostname-3.20-6.el8.x86_64 29/34 - Running scriptlet: hostname-3.20-6.el8.x86_64 29/34 - Cleanup : curl-7.61.1-33.el8_9.5.x86_64 30/34 - Cleanup : yum-utils-4.0.21-23.0.1.el8.noarch 31/34 - Cleanup : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 - Cleanup : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 33/34 - Cleanup : libcurl-7.61.1-33.el8_9.5.x86_64 34/34 - Running scriptlet: libcurl-7.61.1-33.el8_9.5.x86_64 34/34 - Verifying : expect-5.45.4-5.el8.x86_64 1/34 - Verifying : hostname-3.20-6.el8.x86_64 2/34 - Verifying : libmetalink-0.1.3-7.el8.x86_64 3/34 - Verifying : lsof-4.93.2-1.el8.x86_64 4/34 - Verifying : net-tools-2.0-0.52.20160912git.el8.x86_64 5/34 - Verifying : openssl-1:1.1.1k-12.el8_9.x86_64 6/34 - Verifying : python3-ply-3.9-9.el8.noarch 7/34 - Verifying : sudo-1.9.5p2-1.el8_9.x86_64 8/34 - Verifying : tcl-1:8.6.8-2.el8.x86_64 9/34 - Verifying : tree-1.7.0-15.el8.x86_64 10/34 - Verifying : unzip-6.0-46.0.1.el8.x86_64 11/34 - Verifying : which-2.21-20.el8.x86_64 12/34 - Verifying : zip-3.0-23.el8.x86_64 13/34 - Verifying : bind-libs-32:9.11.36-16.el8_10.2.x86_64 14/34 - Verifying : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 15/34 - Verifying : bind-license-32:9.11.36-16.el8_10.2.noarch 16/34 - Verifying : bind-utils-32:9.11.36-16.el8_10.2.x86_64 17/34 - Verifying : fstrm-0.6.1-3.el8.x86_64 18/34 - Verifying : geolite2-city-20180605-1.el8.noarch 19/34 - Verifying : geolite2-country-20180605-1.el8.noarch 20/34 - Verifying : libmaxminddb-1.2.0-10.el8_9.1.x86_64 21/34 - Verifying : protobuf-c-1.3.0-8.el8.x86_64 22/34 - Verifying : python3-bind-32:9.11.36-16.el8_10.2.noarch 23/34 - Verifying : wget-1.19.5-12.0.1.el8_10.x86_64 24/34 - Verifying : curl-7.61.1-34.el8.x86_64 25/34 - Verifying : curl-7.61.1-33.el8_9.5.x86_64 26/34 - Verifying : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 27/34 - Verifying : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 28/34 - Verifying : libcurl-7.61.1-34.el8.x86_64 29/34 - Verifying : libcurl-7.61.1-33.el8_9.5.x86_64 30/34 - Verifying : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 31/34 - Verifying : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 - Verifying : yum-utils-4.0.21-25.0.1.el8.noarch 33/34 - Verifying : yum-utils-4.0.21-23.0.1.el8.noarch 34/34 - -Upgraded: - curl-7.61.1-34.el8.x86_64 - dnf-plugins-core-4.0.21-25.0.1.el8.noarch - libcurl-7.61.1-34.el8.x86_64 - python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch - yum-utils-4.0.21-25.0.1.el8.noarch -Installed: - bind-libs-32:9.11.36-16.el8_10.2.x86_64 - bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 - bind-license-32:9.11.36-16.el8_10.2.noarch - bind-utils-32:9.11.36-16.el8_10.2.x86_64 - expect-5.45.4-5.el8.x86_64 - fstrm-0.6.1-3.el8.x86_64 - geolite2-city-20180605-1.el8.noarch - geolite2-country-20180605-1.el8.noarch - hostname-3.20-6.el8.x86_64 - libmaxminddb-1.2.0-10.el8_9.1.x86_64 - libmetalink-0.1.3-7.el8.x86_64 - lsof-4.93.2-1.el8.x86_64 - net-tools-2.0-0.52.20160912git.el8.x86_64 - openssl-1:1.1.1k-12.el8_9.x86_64 - protobuf-c-1.3.0-8.el8.x86_64 - python3-bind-32:9.11.36-16.el8_10.2.noarch - python3-ply-3.9-9.el8.noarch - sudo-1.9.5p2-1.el8_9.x86_64 - tcl-1:8.6.8-2.el8.x86_64 - tree-1.7.0-15.el8.x86_64 - unzip-6.0-46.0.1.el8.x86_64 - wget-1.19.5-12.0.1.el8_10.x86_64 - which-2.21-20.el8.x86_64 - zip-3.0-23.el8.x86_64 - -Complete! -Adding repo from: http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 -created by dnf config-manager from http://yum.o 496 kB/s | 139 kB 00:00 -Last metadata expiration check: 0:00:01 ago on Tue 20 Aug 2024 08:55:14 AM UTC. -Dependencies resolved. -============================================================================================== - Package Arch Version Repository Size -============================================================================================== -Installing: - java-11-openjdk-devel x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 3.4 M -Installing dependencies: - adwaita-cursor-theme noarch 3.28.0-3.el8 ol8_appstream 647 k - adwaita-icon-theme noarch 3.28.0-3.el8 ol8_appstream 11 M - alsa-lib x86_64 1.2.10-2.el8 ol8_appstream 500 k - at-spi2-atk x86_64 2.26.2-1.el8 ol8_appstream 89 k - at-spi2-core x86_64 2.28.0-1.el8 ol8_appstream 169 k - atk x86_64 2.28.1-1.el8 ol8_appstream 272 k - avahi-libs x86_64 0.7-27.el8 ol8_baseos_latest 61 k - cairo x86_64 1.15.12-6.el8 ol8_appstream 719 k - cairo-gobject x86_64 1.15.12-6.el8 ol8_appstream 33 k - colord-libs x86_64 1.4.2-1.el8 ol8_appstream 236 k - copy-jdk-configs noarch 4.0-2.el8 ol8_appstream 30 k - cpio x86_64 2.12-11.el8 ol8_baseos_latest 266 k - crypto-policies-scripts noarch 20230731-1.git3177e06.el8 ol8_baseos_latest 84 k - cups-libs x86_64 1:2.2.6-60.el8_10 ol8_baseos_latest 435 k - dracut x86_64 049-233.git20240115.0.1.el8 ol8_baseos_latest 382 k - file x86_64 5.33-25.el8 ol8_baseos_latest 77 k - fribidi x86_64 1.0.4-9.el8 ol8_appstream 89 k - gdk-pixbuf2 x86_64 2.36.12-6.el8_10 ol8_baseos_latest 465 k - gdk-pixbuf2-modules x86_64 2.36.12-6.el8_10 ol8_appstream 108 k - gettext x86_64 0.19.8.1-17.el8 ol8_baseos_latest 1.1 M - gettext-libs x86_64 0.19.8.1-17.el8 ol8_baseos_latest 312 k - glib-networking x86_64 2.56.1-1.1.el8 ol8_baseos_latest 155 k - graphite2 x86_64 1.3.10-10.el8 ol8_appstream 122 k - grub2-common noarch 1:2.02-156.0.2.el8 ol8_baseos_latest 897 k - grub2-tools x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 2.0 M - grub2-tools-minimal x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 215 k - gsettings-desktop-schemas x86_64 3.32.0-6.el8 ol8_baseos_latest 633 k - gtk-update-icon-cache x86_64 3.22.30-11.el8 ol8_appstream 32 k - harfbuzz x86_64 1.7.5-4.el8 ol8_appstream 295 k - hicolor-icon-theme noarch 0.17-2.el8 ol8_appstream 48 k - jasper-libs x86_64 2.0.14-5.el8 ol8_appstream 167 k - java-11-openjdk x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 475 k - java-11-openjdk-headless x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 42 M - javapackages-filesystem noarch 5.3.0-1.module+el8+5136+7ff78f74 ol8_appstream 30 k - jbigkit-libs x86_64 2.1-14.el8 ol8_appstream 55 k - json-glib x86_64 1.4.4-1.el8 ol8_baseos_latest 144 k - kbd-legacy noarch 2.0.4-11.el8 ol8_baseos_latest 481 k - kbd-misc noarch 2.0.4-11.el8 ol8_baseos_latest 1.5 M - lcms2 x86_64 2.9-2.el8 ol8_appstream 164 k - libX11 x86_64 1.6.8-8.el8 ol8_appstream 611 k - libX11-common noarch 1.6.8-8.el8 ol8_appstream 157 k - libXau x86_64 1.0.9-3.el8 ol8_appstream 37 k - libXcomposite x86_64 0.4.4-14.el8 ol8_appstream 28 k - libXcursor x86_64 1.1.15-3.el8 ol8_appstream 36 k - libXdamage x86_64 1.1.4-14.el8 ol8_appstream 27 k - libXext x86_64 1.3.4-1.el8 ol8_appstream 45 k - libXfixes x86_64 5.0.3-7.el8 ol8_appstream 25 k - libXft x86_64 2.3.3-1.el8 ol8_appstream 67 k - libXi x86_64 1.7.10-1.el8 ol8_appstream 49 k - libXinerama x86_64 1.1.4-1.el8 ol8_appstream 15 k - libXrandr x86_64 1.5.2-1.el8 ol8_appstream 34 k - libXrender x86_64 0.9.10-7.el8 ol8_appstream 33 k - libXtst x86_64 1.2.3-7.el8 ol8_appstream 22 k - libcroco x86_64 0.6.12-4.el8_2.1 ol8_baseos_latest 113 k - libdatrie x86_64 0.2.9-7.el8 ol8_appstream 33 k - libepoxy x86_64 1.5.8-1.el8 ol8_appstream 225 k - libfontenc x86_64 1.1.3-8.el8 ol8_appstream 37 k - libgomp x86_64 8.5.0-22.0.1.el8_10 ol8_baseos_latest 218 k - libgusb x86_64 0.3.0-1.el8 ol8_baseos_latest 49 k - libjpeg-turbo x86_64 1.5.3-12.el8 ol8_appstream 157 k - libkcapi x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 52 k - libkcapi-hmaccalc x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 31 k - libmodman x86_64 2.0.1-17.el8 ol8_baseos_latest 36 k - libpkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 35 k - libproxy x86_64 0.4.15-5.2.el8 ol8_baseos_latest 75 k - libsoup x86_64 2.62.3-5.el8 ol8_baseos_latest 424 k - libthai x86_64 0.1.27-2.el8 ol8_appstream 203 k - libtiff x86_64 4.0.9-32.el8_10 ol8_appstream 189 k - libwayland-client x86_64 1.21.0-1.el8 ol8_appstream 41 k - libwayland-cursor x86_64 1.21.0-1.el8 ol8_appstream 26 k - libwayland-egl x86_64 1.21.0-1.el8 ol8_appstream 19 k - libxcb x86_64 1.13.1-1.el8 ol8_appstream 231 k - libxkbcommon x86_64 0.9.1-1.el8 ol8_appstream 116 k - lksctp-tools x86_64 1.0.18-3.el8 ol8_baseos_latest 100 k - lua x86_64 5.3.4-12.el8 ol8_appstream 192 k - nspr x86_64 4.35.0-1.el8_8 ol8_appstream 143 k - nss x86_64 3.90.0-7.el8_10 ol8_appstream 750 k - nss-softokn x86_64 3.90.0-7.el8_10 ol8_appstream 1.2 M - nss-softokn-freebl x86_64 3.90.0-7.el8_10 ol8_appstream 375 k - nss-sysinit x86_64 3.90.0-7.el8_10 ol8_appstream 74 k - nss-util x86_64 3.90.0-7.el8_10 ol8_appstream 139 k - os-prober x86_64 1.74-9.0.1.el8 ol8_baseos_latest 51 k - pango x86_64 1.42.4-8.el8 ol8_appstream 297 k - pixman x86_64 0.38.4-4.el8 ol8_appstream 256 k - pkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 38 k - pkgconf-m4 noarch 1.4.2-1.el8 ol8_baseos_latest 17 k - pkgconf-pkg-config x86_64 1.4.2-1.el8 ol8_baseos_latest 15 k - rest x86_64 0.8.1-2.el8 ol8_appstream 70 k - shared-mime-info x86_64 1.9-4.el8 ol8_baseos_latest 328 k - systemd-udev x86_64 239-78.0.4.el8 ol8_baseos_latest 1.6 M - ttmkfdir x86_64 3.0.9-54.el8 ol8_appstream 62 k - tzdata-java noarch 2024a-1.0.1.el8 ol8_appstream 186 k - xkeyboard-config noarch 2.28-1.el8 ol8_appstream 782 k - xorg-x11-font-utils x86_64 1:7.5-41.el8 ol8_appstream 104 k - xorg-x11-fonts-Type1 noarch 7.5-19.el8 ol8_appstream 522 k - xz x86_64 5.2.4-4.el8_6 ol8_baseos_latest 153 k -Installing weak dependencies: - abattis-cantarell-fonts noarch 0.0.25-6.el8 ol8_appstream 155 k - dconf x86_64 0.28.0-4.0.1.el8 ol8_appstream 108 k - dejavu-sans-mono-fonts noarch 2.35-7.el8 ol8_baseos_latest 447 k - grubby x86_64 8.40-49.0.2.el8 ol8_baseos_latest 50 k - gtk3 x86_64 3.22.30-11.el8 ol8_appstream 4.5 M - hardlink x86_64 1:1.3-6.el8 ol8_baseos_latest 29 k - kbd x86_64 2.0.4-11.el8 ol8_baseos_latest 390 k - memstrack x86_64 0.2.5-2.el8 ol8_baseos_latest 51 k - pigz x86_64 2.4-4.el8 ol8_baseos_latest 80 k -Enabling module streams: - javapackages-runtime 201801 - -Transaction Summary -============================================================================================== -Install 106 Packages - -Total download size: 86 M -Installed size: 312 M -Downloading Packages: -(1/106): crypto-policies-scripts-20230731-1.git 862 kB/s | 84 kB 00:00 -(2/106): avahi-libs-0.7-27.el8.x86_64.rpm 602 kB/s | 61 kB 00:00 -(3/106): cpio-2.12-11.el8.x86_64.rpm 1.8 MB/s | 266 kB 00:00 -(4/106): cups-libs-2.2.6-60.el8_10.x86_64.rpm 5.7 MB/s | 435 kB 00:00 -(5/106): dejavu-sans-mono-fonts-2.35-7.el8.noar 5.1 MB/s | 447 kB 00:00 -(6/106): dracut-049-233.git20240115.0.1.el8.x86 7.0 MB/s | 382 kB 00:00 -(7/106): gdk-pixbuf2-2.36.12-6.el8_10.x86_64.rp 12 MB/s | 465 kB 00:00 -(8/106): gettext-libs-0.19.8.1-17.el8.x86_64.rp 9.3 MB/s | 312 kB 00:00 -(9/106): gettext-0.19.8.1-17.el8.x86_64.rpm 16 MB/s | 1.1 MB 00:00 -(10/106): glib-networking-2.56.1-1.1.el8.x86_64 6.0 MB/s | 155 kB 00:00 -(11/106): grub2-common-2.02-156.0.2.el8.noarch. 26 MB/s | 897 kB 00:00 -(12/106): grub2-tools-minimal-2.02-156.0.2.el8. 8.2 MB/s | 215 kB 00:00 -(13/106): grubby-8.40-49.0.2.el8.x86_64.rpm 2.1 MB/s | 50 kB 00:00 -(14/106): grub2-tools-2.02-156.0.2.el8.x86_64.r 26 MB/s | 2.0 MB 00:00 -(15/106): gsettings-desktop-schemas-3.32.0-6.el 19 MB/s | 633 kB 00:00 -(16/106): hardlink-1.3-6.el8.x86_64.rpm 1.1 MB/s | 29 kB 00:00 -(17/106): json-glib-1.4.4-1.el8.x86_64.rpm 5.9 MB/s | 144 kB 00:00 -(18/106): kbd-2.0.4-11.el8.x86_64.rpm 14 MB/s | 390 kB 00:00 -(19/106): kbd-legacy-2.0.4-11.el8.noarch.rpm 17 MB/s | 481 kB 00:00 -(20/106): kbd-misc-2.0.4-11.el8.noarch.rpm 41 MB/s | 1.5 MB 00:00 -(21/106): libcroco-0.6.12-4.el8_2.1.x86_64.rpm 4.7 MB/s | 113 kB 00:00 -(22/106): libgomp-8.5.0-22.0.1.el8_10.x86_64.rp 9.1 MB/s | 218 kB 00:00 -(23/106): libgusb-0.3.0-1.el8.x86_64.rpm 2.1 MB/s | 49 kB 00:00 -(24/106): libkcapi-1.4.0-2.0.1.el8.x86_64.rpm 1.6 MB/s | 52 kB 00:00 -(25/106): libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86 822 kB/s | 31 kB 00:00 -(26/106): libmodman-2.0.1-17.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 -(27/106): libpkgconf-1.4.2-1.el8.x86_64.rpm 1.2 MB/s | 35 kB 00:00 -(28/106): libproxy-0.4.15-5.2.el8.x86_64.rpm 3.0 MB/s | 75 kB 00:00 -(29/106): libsoup-2.62.3-5.el8.x86_64.rpm 15 MB/s | 424 kB 00:00 -(30/106): lksctp-tools-1.0.18-3.el8.x86_64.rpm 3.5 MB/s | 100 kB 00:00 -(31/106): memstrack-0.2.5-2.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 -(32/106): os-prober-1.74-9.0.1.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 -(33/106): pigz-2.4-4.el8.x86_64.rpm 3.5 MB/s | 80 kB 00:00 -(34/106): pkgconf-1.4.2-1.el8.x86_64.rpm 1.7 MB/s | 38 kB 00:00 -(35/106): pkgconf-m4-1.4.2-1.el8.noarch.rpm 761 kB/s | 17 kB 00:00 -(36/106): pkgconf-pkg-config-1.4.2-1.el8.x86_64 691 kB/s | 15 kB 00:00 -(37/106): shared-mime-info-1.9-4.el8.x86_64.rpm 13 MB/s | 328 kB 00:00 -(38/106): systemd-udev-239-78.0.4.el8.x86_64.rp 32 MB/s | 1.6 MB 00:00 -(39/106): xz-5.2.4-4.el8_6.x86_64.rpm 5.2 MB/s | 153 kB 00:00 -(40/106): abattis-cantarell-fonts-0.0.25-6.el8. 6.4 MB/s | 155 kB 00:00 -(41/106): adwaita-cursor-theme-3.28.0-3.el8.noa 22 MB/s | 647 kB 00:00 -(42/106): alsa-lib-1.2.10-2.el8.x86_64.rpm 18 MB/s | 500 kB 00:00 -(43/106): at-spi2-atk-2.26.2-1.el8.x86_64.rpm 3.8 MB/s | 89 kB 00:00 -(44/106): at-spi2-core-2.28.0-1.el8.x86_64.rpm 6.9 MB/s | 169 kB 00:00 -(45/106): atk-2.28.1-1.el8.x86_64.rpm 9.2 MB/s | 272 kB 00:00 -(46/106): cairo-1.15.12-6.el8.x86_64.rpm 24 MB/s | 719 kB 00:00 -(47/106): adwaita-icon-theme-3.28.0-3.el8.noarc 65 MB/s | 11 MB 00:00 -(48/106): cairo-gobject-1.15.12-6.el8.x86_64.rp 914 kB/s | 33 kB 00:00 -(49/106): colord-libs-1.4.2-1.el8.x86_64.rpm 9.5 MB/s | 236 kB 00:00 -(50/106): copy-jdk-configs-4.0-2.el8.noarch.rpm 1.1 MB/s | 30 kB 00:00 -(51/106): dconf-0.28.0-4.0.1.el8.x86_64.rpm 4.4 MB/s | 108 kB 00:00 -(52/106): fribidi-1.0.4-9.el8.x86_64.rpm 3.9 MB/s | 89 kB 00:00 -(53/106): graphite2-1.3.10-10.el8.x86_64.rpm 5.1 MB/s | 122 kB 00:00 -(54/106): gdk-pixbuf2-modules-2.36.12-6.el8_10. 3.6 MB/s | 108 kB 00:00 -(55/106): gtk-update-icon-cache-3.22.30-11.el8. 1.4 MB/s | 32 kB 00:00 -(56/106): harfbuzz-1.7.5-4.el8.x86_64.rpm 11 MB/s | 295 kB 00:00 -(57/106): gtk3-3.22.30-11.el8.x86_64.rpm 68 MB/s | 4.5 MB 00:00 -(58/106): hicolor-icon-theme-0.17-2.el8.noarch. 2.1 MB/s | 48 kB 00:00 -(59/106): java-11-openjdk-11.0.24.0.8-3.0.1.el8 17 MB/s | 475 kB 00:00 -(60/106): jasper-libs-2.0.14-5.el8.x86_64.rpm 5.0 MB/s | 167 kB 00:00 -(61/106): java-11-openjdk-devel-11.0.24.0.8-3.0 61 MB/s | 3.4 MB 00:00 -(62/106): javapackages-filesystem-5.3.0-1.modul 1.2 MB/s | 30 kB 00:00 -(63/106): jbigkit-libs-2.1-14.el8.x86_64.rpm 2.1 MB/s | 55 kB 00:00 -(64/106): lcms2-2.9-2.el8.x86_64.rpm 3.8 MB/s | 164 kB 00:00 -(65/106): libX11-1.6.8-8.el8.x86_64.rpm 20 MB/s | 611 kB 00:00 -(66/106): libX11-common-1.6.8-8.el8.noarch.rpm 6.8 MB/s | 157 kB 00:00 -(67/106): libXau-1.0.9-3.el8.x86_64.rpm 1.6 MB/s | 37 kB 00:00 -(68/106): libXcomposite-0.4.4-14.el8.x86_64.rpm 1.3 MB/s | 28 kB 00:00 -(69/106): libXcursor-1.1.15-3.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 -(70/106): libXdamage-1.1.4-14.el8.x86_64.rpm 1.2 MB/s | 27 kB 00:00 -(71/106): libXext-1.3.4-1.el8.x86_64.rpm 2.0 MB/s | 45 kB 00:00 -(72/106): libXfixes-5.0.3-7.el8.x86_64.rpm 1.1 MB/s | 25 kB 00:00 -(73/106): libXft-2.3.3-1.el8.x86_64.rpm 2.9 MB/s | 67 kB 00:00 -(74/106): libXi-1.7.10-1.el8.x86_64.rpm 2.2 MB/s | 49 kB 00:00 -(75/106): libXinerama-1.1.4-1.el8.x86_64.rpm 717 kB/s | 15 kB 00:00 -(76/106): libXrandr-1.5.2-1.el8.x86_64.rpm 1.5 MB/s | 34 kB 00:00 -(77/106): libXrender-0.9.10-7.el8.x86_64.rpm 1.4 MB/s | 33 kB 00:00 -(78/106): libXtst-1.2.3-7.el8.x86_64.rpm 957 kB/s | 22 kB 00:00 -(79/106): java-11-openjdk-headless-11.0.24.0.8- 71 MB/s | 42 MB 00:00 -(80/106): libdatrie-0.2.9-7.el8.x86_64.rpm 274 kB/s | 33 kB 00:00 -(81/106): libepoxy-1.5.8-1.el8.x86_64.rpm 9.1 MB/s | 225 kB 00:00 -(82/106): libfontenc-1.1.3-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 -(83/106): libthai-0.1.27-2.el8.x86_64.rpm 8.2 MB/s | 203 kB 00:00 -(84/106): libjpeg-turbo-1.5.3-12.el8.x86_64.rpm 5.1 MB/s | 157 kB 00:00 -(85/106): libtiff-4.0.9-32.el8_10.x86_64.rpm 7.8 MB/s | 189 kB 00:00 -(86/106): libwayland-client-1.21.0-1.el8.x86_64 1.7 MB/s | 41 kB 00:00 -(87/106): libwayland-cursor-1.21.0-1.el8.x86_64 1.2 MB/s | 26 kB 00:00 -(88/106): libwayland-egl-1.21.0-1.el8.x86_64.rp 801 kB/s | 19 kB 00:00 -(89/106): libxcb-1.13.1-1.el8.x86_64.rpm 9.7 MB/s | 231 kB 00:00 -(90/106): libxkbcommon-0.9.1-1.el8.x86_64.rpm 5.0 MB/s | 116 kB 00:00 -(91/106): nspr-4.35.0-1.el8_8.x86_64.rpm 6.0 MB/s | 143 kB 00:00 -(92/106): lua-5.3.4-12.el8.x86_64.rpm 5.9 MB/s | 192 kB 00:00 -(93/106): nss-softokn-3.90.0-7.el8_10.x86_64.rp 38 MB/s | 1.2 MB 00:00 -(94/106): nss-3.90.0-7.el8_10.x86_64.rpm 17 MB/s | 750 kB 00:00 -(95/106): nss-softokn-freebl-3.90.0-7.el8_10.x8 14 MB/s | 375 kB 00:00 -(96/106): nss-sysinit-3.90.0-7.el8_10.x86_64.rp 3.2 MB/s | 74 kB 00:00 -(97/106): nss-util-3.90.0-7.el8_10.x86_64.rpm 5.8 MB/s | 139 kB 00:00 -(98/106): pango-1.42.4-8.el8.x86_64.rpm 11 MB/s | 297 kB 00:00 -(99/106): pixman-0.38.4-4.el8.x86_64.rpm 10 MB/s | 256 kB 00:00 -(100/106): rest-0.8.1-2.el8.x86_64.rpm 3.1 MB/s | 70 kB 00:00 -(101/106): ttmkfdir-3.0.9-54.el8.x86_64.rpm 2.5 MB/s | 62 kB 00:00 -(102/106): tzdata-java-2024a-1.0.1.el8.noarch.r 7.4 MB/s | 186 kB 00:00 -(103/106): xkeyboard-config-2.28-1.el8.noarch.r 27 MB/s | 782 kB 00:00 -(104/106): xorg-x11-font-utils-7.5-41.el8.x86_6 3.9 MB/s | 104 kB 00:00 -(105/106): xorg-x11-fonts-Type1-7.5-19.el8.noar 1.3 MB/s | 522 kB 00:00 -(106/106): file-5.33-25.el8.x86_64.rpm 26 kB/s | 77 kB 00:02 --------------------------------------------------------------------------------- -Total 27 MB/s | 86 MB 00:03 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 1/1 - Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86 1/1 - Preparing : 1/1 - Installing : nspr-4.35.0-1.el8_8.x86_64 1/106 - Running scriptlet: nspr-4.35.0-1.el8_8.x86_64 1/106 - Installing : nss-util-3.90.0-7.el8_10.x86_64 2/106 - Installing : libjpeg-turbo-1.5.3-12.el8.x86_64 3/106 - Installing : pixman-0.38.4-4.el8.x86_64 4/106 - Installing : libwayland-client-1.21.0-1.el8.x86_64 5/106 - Installing : atk-2.28.1-1.el8.x86_64 6/106 - Installing : libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 - Running scriptlet: libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 - Installing : libcroco-0.6.12-4.el8_2.1.x86_64 8/106 - Running scriptlet: libcroco-0.6.12-4.el8_2.1.x86_64 8/106 - Installing : grub2-common-1:2.02-156.0.2.el8.noarch 9/106 - Installing : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 - Installing : gettext-0.19.8.1-17.el8.x86_64 11/106 - Running scriptlet: gettext-0.19.8.1-17.el8.x86_64 11/106 - Installing : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 12/106 - Installing : libwayland-cursor-1.21.0-1.el8.x86_64 13/106 - Installing : jasper-libs-2.0.14-5.el8.x86_64 14/106 - Installing : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 15/106 - Installing : nss-softokn-3.90.0-7.el8_10.x86_64 16/106 - Installing : xkeyboard-config-2.28-1.el8.noarch 17/106 - Installing : libxkbcommon-0.9.1-1.el8.x86_64 18/106 - Installing : tzdata-java-2024a-1.0.1.el8.noarch 19/106 - Installing : ttmkfdir-3.0.9-54.el8.x86_64 20/106 - Installing : lua-5.3.4-12.el8.x86_64 21/106 - Installing : copy-jdk-configs-4.0-2.el8.noarch 22/106 - Installing : libwayland-egl-1.21.0-1.el8.x86_64 23/106 - Installing : libfontenc-1.1.3-8.el8.x86_64 24/106 - Installing : libepoxy-1.5.8-1.el8.x86_64 25/106 - Installing : libdatrie-0.2.9-7.el8.x86_64 26/106 - Running scriptlet: libdatrie-0.2.9-7.el8.x86_64 26/106 - Installing : libthai-0.1.27-2.el8.x86_64 27/106 - Running scriptlet: libthai-0.1.27-2.el8.x86_64 27/106 - Installing : libXau-1.0.9-3.el8.x86_64 28/106 - Installing : libxcb-1.13.1-1.el8.x86_64 29/106 - Installing : libX11-common-1.6.8-8.el8.noarch 30/106 - Installing : libX11-1.6.8-8.el8.x86_64 31/106 - Installing : libXext-1.3.4-1.el8.x86_64 32/106 - Installing : libXrender-0.9.10-7.el8.x86_64 33/106 - Installing : cairo-1.15.12-6.el8.x86_64 34/106 - Installing : libXi-1.7.10-1.el8.x86_64 35/106 - Installing : libXfixes-5.0.3-7.el8.x86_64 36/106 - Installing : libXtst-1.2.3-7.el8.x86_64 37/106 - Installing : libXcomposite-0.4.4-14.el8.x86_64 38/106 - Installing : at-spi2-core-2.28.0-1.el8.x86_64 39/106 - Running scriptlet: at-spi2-core-2.28.0-1.el8.x86_64 39/106 - Installing : at-spi2-atk-2.26.2-1.el8.x86_64 40/106 - Running scriptlet: at-spi2-atk-2.26.2-1.el8.x86_64 40/106 - Installing : libXcursor-1.1.15-3.el8.x86_64 41/106 - Installing : libXdamage-1.1.4-14.el8.x86_64 42/106 - Installing : cairo-gobject-1.15.12-6.el8.x86_64 43/106 - Installing : libXft-2.3.3-1.el8.x86_64 44/106 - Installing : libXrandr-1.5.2-1.el8.x86_64 45/106 - Installing : libXinerama-1.1.4-1.el8.x86_64 46/106 - Installing : lcms2-2.9-2.el8.x86_64 47/106 - Running scriptlet: lcms2-2.9-2.el8.x86_64 47/106 - Installing : jbigkit-libs-2.1-14.el8.x86_64 48/106 - Running scriptlet: jbigkit-libs-2.1-14.el8.x86_64 48/106 - Installing : libtiff-4.0.9-32.el8_10.x86_64 49/106 - Installing : javapackages-filesystem-5.3.0-1.module+el8+5136+ 50/106 - Installing : hicolor-icon-theme-0.17-2.el8.noarch 51/106 - Installing : graphite2-1.3.10-10.el8.x86_64 52/106 - Installing : harfbuzz-1.7.5-4.el8.x86_64 53/106 - Running scriptlet: harfbuzz-1.7.5-4.el8.x86_64 53/106 - Installing : fribidi-1.0.4-9.el8.x86_64 54/106 - Installing : pango-1.42.4-8.el8.x86_64 55/106 - Running scriptlet: pango-1.42.4-8.el8.x86_64 55/106 - Installing : dconf-0.28.0-4.0.1.el8.x86_64 56/106 - Installing : alsa-lib-1.2.10-2.el8.x86_64 57/106 - Running scriptlet: alsa-lib-1.2.10-2.el8.x86_64 57/106 - Installing : adwaita-cursor-theme-3.28.0-3.el8.noarch 58/106 - Installing : adwaita-icon-theme-3.28.0-3.el8.noarch 59/106 - Installing : abattis-cantarell-fonts-0.0.25-6.el8.noarch 60/106 - Installing : xz-5.2.4-4.el8_6.x86_64 61/106 - Installing : shared-mime-info-1.9-4.el8.x86_64 62/106 - Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 62/106 - Installing : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 - Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 - Installing : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 64/106 - Installing : gtk-update-icon-cache-3.22.30-11.el8.x86_64 65/106 - Installing : pkgconf-m4-1.4.2-1.el8.noarch 66/106 - Installing : pigz-2.4-4.el8.x86_64 67/106 - Installing : memstrack-0.2.5-2.el8.x86_64 68/106 - Installing : lksctp-tools-1.0.18-3.el8.x86_64 69/106 - Running scriptlet: lksctp-tools-1.0.18-3.el8.x86_64 69/106 - Installing : libpkgconf-1.4.2-1.el8.x86_64 70/106 - Installing : pkgconf-1.4.2-1.el8.x86_64 71/106 - Installing : pkgconf-pkg-config-1.4.2-1.el8.x86_64 72/106 - Installing : xorg-x11-font-utils-1:7.5-41.el8.x86_64 73/106 - Installing : xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 - Running scriptlet: xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 - Installing : libmodman-2.0.1-17.el8.x86_64 75/106 - Running scriptlet: libmodman-2.0.1-17.el8.x86_64 75/106 - Installing : libproxy-0.4.15-5.2.el8.x86_64 76/106 - Running scriptlet: libproxy-0.4.15-5.2.el8.x86_64 76/106 - Installing : libkcapi-1.4.0-2.0.1.el8.x86_64 77/106 - Installing : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 78/106 - Installing : libgusb-0.3.0-1.el8.x86_64 79/106 - Installing : colord-libs-1.4.2-1.el8.x86_64 80/106 - Installing : kbd-misc-2.0.4-11.el8.noarch 81/106 - Installing : kbd-legacy-2.0.4-11.el8.noarch 82/106 - Installing : kbd-2.0.4-11.el8.x86_64 83/106 - Installing : systemd-udev-239-78.0.4.el8.x86_64 84/106 - Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 84/106 - Installing : os-prober-1.74-9.0.1.el8.x86_64 85/106 - Installing : json-glib-1.4.4-1.el8.x86_64 86/106 - Installing : hardlink-1:1.3-6.el8.x86_64 87/106 - Installing : file-5.33-25.el8.x86_64 88/106 - Installing : dejavu-sans-mono-fonts-2.35-7.el8.noarch 89/106 - Installing : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 90/106 - Installing : glib-networking-2.56.1-1.1.el8.x86_64 91/106 - Installing : libsoup-2.62.3-5.el8.x86_64 92/106 - Installing : rest-0.8.1-2.el8.x86_64 93/106 - Running scriptlet: rest-0.8.1-2.el8.x86_64 93/106 - Installing : cpio-2.12-11.el8.x86_64 94/106 - Installing : dracut-049-233.git20240115.0.1.el8.x86_64 95/106 - Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 - Installing : grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 - Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 - Installing : grubby-8.40-49.0.2.el8.x86_64 97/106 - Installing : crypto-policies-scripts-20230731-1.git3177e06.el 98/106 - Installing : nss-sysinit-3.90.0-7.el8_10.x86_64 99/106 - Installing : nss-3.90.0-7.el8_10.x86_64 100/106 - Installing : avahi-libs-0.7-27.el8.x86_64 101/106 - Installing : cups-libs-1:2.2.6-60.el8_10.x86_64 102/106 - Installing : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 - Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 - Installing : gtk3-3.22.30-11.el8.x86_64 104/106 - Installing : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 - Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 - Installing : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 - Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 - Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 106/106 - Running scriptlet: dconf-0.28.0-4.0.1.el8.x86_64 106/106 - Running scriptlet: crypto-policies-scripts-20230731-1.git3177e06.el 106/106 - Running scriptlet: nss-3.90.0-7.el8_10.x86_64 106/106 - Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 106/106 - Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 106/106 - Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 - Running scriptlet: hicolor-icon-theme-0.17-2.el8.noarch 106/106 - Running scriptlet: adwaita-icon-theme-3.28.0-3.el8.noarch 106/106 - Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 106/106 - Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 106/106 - Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 106/106 - Verifying : avahi-libs-0.7-27.el8.x86_64 1/106 - Verifying : cpio-2.12-11.el8.x86_64 2/106 - Verifying : crypto-policies-scripts-20230731-1.git3177e06.el 3/106 - Verifying : cups-libs-1:2.2.6-60.el8_10.x86_64 4/106 - Verifying : dejavu-sans-mono-fonts-2.35-7.el8.noarch 5/106 - Verifying : dracut-049-233.git20240115.0.1.el8.x86_64 6/106 - Verifying : file-5.33-25.el8.x86_64 7/106 - Verifying : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 8/106 - Verifying : gettext-0.19.8.1-17.el8.x86_64 9/106 - Verifying : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 - Verifying : glib-networking-2.56.1-1.1.el8.x86_64 11/106 - Verifying : grub2-common-1:2.02-156.0.2.el8.noarch 12/106 - Verifying : grub2-tools-1:2.02-156.0.2.el8.x86_64 13/106 - Verifying : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 14/106 - Verifying : grubby-8.40-49.0.2.el8.x86_64 15/106 - Verifying : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 16/106 - Verifying : hardlink-1:1.3-6.el8.x86_64 17/106 - Verifying : json-glib-1.4.4-1.el8.x86_64 18/106 - Verifying : kbd-2.0.4-11.el8.x86_64 19/106 - Verifying : kbd-legacy-2.0.4-11.el8.noarch 20/106 - Verifying : kbd-misc-2.0.4-11.el8.noarch 21/106 - Verifying : libcroco-0.6.12-4.el8_2.1.x86_64 22/106 - Verifying : libgomp-8.5.0-22.0.1.el8_10.x86_64 23/106 - Verifying : libgusb-0.3.0-1.el8.x86_64 24/106 - Verifying : libkcapi-1.4.0-2.0.1.el8.x86_64 25/106 - Verifying : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 26/106 - Verifying : libmodman-2.0.1-17.el8.x86_64 27/106 - Verifying : libpkgconf-1.4.2-1.el8.x86_64 28/106 - Verifying : libproxy-0.4.15-5.2.el8.x86_64 29/106 - Verifying : libsoup-2.62.3-5.el8.x86_64 30/106 - Verifying : lksctp-tools-1.0.18-3.el8.x86_64 31/106 - Verifying : memstrack-0.2.5-2.el8.x86_64 32/106 - Verifying : os-prober-1.74-9.0.1.el8.x86_64 33/106 - Verifying : pigz-2.4-4.el8.x86_64 34/106 - Verifying : pkgconf-1.4.2-1.el8.x86_64 35/106 - Verifying : pkgconf-m4-1.4.2-1.el8.noarch 36/106 - Verifying : pkgconf-pkg-config-1.4.2-1.el8.x86_64 37/106 - Verifying : shared-mime-info-1.9-4.el8.x86_64 38/106 - Verifying : systemd-udev-239-78.0.4.el8.x86_64 39/106 - Verifying : xz-5.2.4-4.el8_6.x86_64 40/106 - Verifying : abattis-cantarell-fonts-0.0.25-6.el8.noarch 41/106 - Verifying : adwaita-cursor-theme-3.28.0-3.el8.noarch 42/106 - Verifying : adwaita-icon-theme-3.28.0-3.el8.noarch 43/106 - Verifying : alsa-lib-1.2.10-2.el8.x86_64 44/106 - Verifying : at-spi2-atk-2.26.2-1.el8.x86_64 45/106 - Verifying : at-spi2-core-2.28.0-1.el8.x86_64 46/106 - Verifying : atk-2.28.1-1.el8.x86_64 47/106 - Verifying : cairo-1.15.12-6.el8.x86_64 48/106 - Verifying : cairo-gobject-1.15.12-6.el8.x86_64 49/106 - Verifying : colord-libs-1.4.2-1.el8.x86_64 50/106 - Verifying : copy-jdk-configs-4.0-2.el8.noarch 51/106 - Verifying : dconf-0.28.0-4.0.1.el8.x86_64 52/106 - Verifying : fribidi-1.0.4-9.el8.x86_64 53/106 - Verifying : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 54/106 - Verifying : graphite2-1.3.10-10.el8.x86_64 55/106 - Verifying : gtk-update-icon-cache-3.22.30-11.el8.x86_64 56/106 - Verifying : gtk3-3.22.30-11.el8.x86_64 57/106 - Verifying : harfbuzz-1.7.5-4.el8.x86_64 58/106 - Verifying : hicolor-icon-theme-0.17-2.el8.noarch 59/106 - Verifying : jasper-libs-2.0.14-5.el8.x86_64 60/106 - Verifying : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 61/106 - Verifying : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 62/106 - Verifying : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 63/106 - Verifying : javapackages-filesystem-5.3.0-1.module+el8+5136+ 64/106 - Verifying : jbigkit-libs-2.1-14.el8.x86_64 65/106 - Verifying : lcms2-2.9-2.el8.x86_64 66/106 - Verifying : libX11-1.6.8-8.el8.x86_64 67/106 - Verifying : libX11-common-1.6.8-8.el8.noarch 68/106 - Verifying : libXau-1.0.9-3.el8.x86_64 69/106 - Verifying : libXcomposite-0.4.4-14.el8.x86_64 70/106 - Verifying : libXcursor-1.1.15-3.el8.x86_64 71/106 - Verifying : libXdamage-1.1.4-14.el8.x86_64 72/106 - Verifying : libXext-1.3.4-1.el8.x86_64 73/106 - Verifying : libXfixes-5.0.3-7.el8.x86_64 74/106 - Verifying : libXft-2.3.3-1.el8.x86_64 75/106 - Verifying : libXi-1.7.10-1.el8.x86_64 76/106 - Verifying : libXinerama-1.1.4-1.el8.x86_64 77/106 - Verifying : libXrandr-1.5.2-1.el8.x86_64 78/106 - Verifying : libXrender-0.9.10-7.el8.x86_64 79/106 - Verifying : libXtst-1.2.3-7.el8.x86_64 80/106 - Verifying : libdatrie-0.2.9-7.el8.x86_64 81/106 - Verifying : libepoxy-1.5.8-1.el8.x86_64 82/106 - Verifying : libfontenc-1.1.3-8.el8.x86_64 83/106 - Verifying : libjpeg-turbo-1.5.3-12.el8.x86_64 84/106 - Verifying : libthai-0.1.27-2.el8.x86_64 85/106 - Verifying : libtiff-4.0.9-32.el8_10.x86_64 86/106 - Verifying : libwayland-client-1.21.0-1.el8.x86_64 87/106 - Verifying : libwayland-cursor-1.21.0-1.el8.x86_64 88/106 - Verifying : libwayland-egl-1.21.0-1.el8.x86_64 89/106 - Verifying : libxcb-1.13.1-1.el8.x86_64 90/106 - Verifying : libxkbcommon-0.9.1-1.el8.x86_64 91/106 - Verifying : lua-5.3.4-12.el8.x86_64 92/106 - Verifying : nspr-4.35.0-1.el8_8.x86_64 93/106 - Verifying : nss-3.90.0-7.el8_10.x86_64 94/106 - Verifying : nss-softokn-3.90.0-7.el8_10.x86_64 95/106 - Verifying : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 96/106 - Verifying : nss-sysinit-3.90.0-7.el8_10.x86_64 97/106 - Verifying : nss-util-3.90.0-7.el8_10.x86_64 98/106 - Verifying : pango-1.42.4-8.el8.x86_64 99/106 - Verifying : pixman-0.38.4-4.el8.x86_64 100/106 - Verifying : rest-0.8.1-2.el8.x86_64 101/106 - Verifying : ttmkfdir-3.0.9-54.el8.x86_64 102/106 - Verifying : tzdata-java-2024a-1.0.1.el8.noarch 103/106 - Verifying : xkeyboard-config-2.28-1.el8.noarch 104/106 - Verifying : xorg-x11-font-utils-1:7.5-41.el8.x86_64 105/106 - Verifying : xorg-x11-fonts-Type1-7.5-19.el8.noarch 106/106 - -Installed: - abattis-cantarell-fonts-0.0.25-6.el8.noarch - adwaita-cursor-theme-3.28.0-3.el8.noarch - adwaita-icon-theme-3.28.0-3.el8.noarch - alsa-lib-1.2.10-2.el8.x86_64 - at-spi2-atk-2.26.2-1.el8.x86_64 - at-spi2-core-2.28.0-1.el8.x86_64 - atk-2.28.1-1.el8.x86_64 - avahi-libs-0.7-27.el8.x86_64 - cairo-1.15.12-6.el8.x86_64 - cairo-gobject-1.15.12-6.el8.x86_64 - colord-libs-1.4.2-1.el8.x86_64 - copy-jdk-configs-4.0-2.el8.noarch - cpio-2.12-11.el8.x86_64 - crypto-policies-scripts-20230731-1.git3177e06.el8.noarch - cups-libs-1:2.2.6-60.el8_10.x86_64 - dconf-0.28.0-4.0.1.el8.x86_64 - dejavu-sans-mono-fonts-2.35-7.el8.noarch - dracut-049-233.git20240115.0.1.el8.x86_64 - file-5.33-25.el8.x86_64 - fribidi-1.0.4-9.el8.x86_64 - gdk-pixbuf2-2.36.12-6.el8_10.x86_64 - gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 - gettext-0.19.8.1-17.el8.x86_64 - gettext-libs-0.19.8.1-17.el8.x86_64 - glib-networking-2.56.1-1.1.el8.x86_64 - graphite2-1.3.10-10.el8.x86_64 - grub2-common-1:2.02-156.0.2.el8.noarch - grub2-tools-1:2.02-156.0.2.el8.x86_64 - grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 - grubby-8.40-49.0.2.el8.x86_64 - gsettings-desktop-schemas-3.32.0-6.el8.x86_64 - gtk-update-icon-cache-3.22.30-11.el8.x86_64 - gtk3-3.22.30-11.el8.x86_64 - hardlink-1:1.3-6.el8.x86_64 - harfbuzz-1.7.5-4.el8.x86_64 - hicolor-icon-theme-0.17-2.el8.noarch - jasper-libs-2.0.14-5.el8.x86_64 - java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 - java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x86_64 - java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86_64 - javapackages-filesystem-5.3.0-1.module+el8+5136+7ff78f74.noarch - jbigkit-libs-2.1-14.el8.x86_64 - json-glib-1.4.4-1.el8.x86_64 - kbd-2.0.4-11.el8.x86_64 - kbd-legacy-2.0.4-11.el8.noarch - kbd-misc-2.0.4-11.el8.noarch - lcms2-2.9-2.el8.x86_64 - libX11-1.6.8-8.el8.x86_64 - libX11-common-1.6.8-8.el8.noarch - libXau-1.0.9-3.el8.x86_64 - libXcomposite-0.4.4-14.el8.x86_64 - libXcursor-1.1.15-3.el8.x86_64 - libXdamage-1.1.4-14.el8.x86_64 - libXext-1.3.4-1.el8.x86_64 - libXfixes-5.0.3-7.el8.x86_64 - libXft-2.3.3-1.el8.x86_64 - libXi-1.7.10-1.el8.x86_64 - libXinerama-1.1.4-1.el8.x86_64 - libXrandr-1.5.2-1.el8.x86_64 - libXrender-0.9.10-7.el8.x86_64 - libXtst-1.2.3-7.el8.x86_64 - libcroco-0.6.12-4.el8_2.1.x86_64 - libdatrie-0.2.9-7.el8.x86_64 - libepoxy-1.5.8-1.el8.x86_64 - libfontenc-1.1.3-8.el8.x86_64 - libgomp-8.5.0-22.0.1.el8_10.x86_64 - libgusb-0.3.0-1.el8.x86_64 - libjpeg-turbo-1.5.3-12.el8.x86_64 - libkcapi-1.4.0-2.0.1.el8.x86_64 - libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 - libmodman-2.0.1-17.el8.x86_64 - libpkgconf-1.4.2-1.el8.x86_64 - libproxy-0.4.15-5.2.el8.x86_64 - libsoup-2.62.3-5.el8.x86_64 - libthai-0.1.27-2.el8.x86_64 - libtiff-4.0.9-32.el8_10.x86_64 - libwayland-client-1.21.0-1.el8.x86_64 - libwayland-cursor-1.21.0-1.el8.x86_64 - libwayland-egl-1.21.0-1.el8.x86_64 - libxcb-1.13.1-1.el8.x86_64 - libxkbcommon-0.9.1-1.el8.x86_64 - lksctp-tools-1.0.18-3.el8.x86_64 - lua-5.3.4-12.el8.x86_64 - memstrack-0.2.5-2.el8.x86_64 - nspr-4.35.0-1.el8_8.x86_64 - nss-3.90.0-7.el8_10.x86_64 - nss-softokn-3.90.0-7.el8_10.x86_64 - nss-softokn-freebl-3.90.0-7.el8_10.x86_64 - nss-sysinit-3.90.0-7.el8_10.x86_64 - nss-util-3.90.0-7.el8_10.x86_64 - os-prober-1.74-9.0.1.el8.x86_64 - pango-1.42.4-8.el8.x86_64 - pigz-2.4-4.el8.x86_64 - pixman-0.38.4-4.el8.x86_64 - pkgconf-1.4.2-1.el8.x86_64 - pkgconf-m4-1.4.2-1.el8.noarch - pkgconf-pkg-config-1.4.2-1.el8.x86_64 - rest-0.8.1-2.el8.x86_64 - shared-mime-info-1.9-4.el8.x86_64 - systemd-udev-239-78.0.4.el8.x86_64 - ttmkfdir-3.0.9-54.el8.x86_64 - tzdata-java-2024a-1.0.1.el8.noarch - xkeyboard-config-2.28-1.el8.noarch - xorg-x11-font-utils-1:7.5-41.el8.x86_64 - xorg-x11-fonts-Type1-7.5-19.el8.noarch - xz-5.2.4-4.el8_6.x86_64 - -Complete! -Last metadata expiration check: 0:00:23 ago on Tue 20 Aug 2024 08:55:14 AM UTC. -Package iproute-6.2.0-5.el8_9.x86_64 is already installed. -Dependencies resolved. -================================================================================ - Package Architecture Version Repository Size -================================================================================ -Upgrading: - iproute x86_64 6.2.0-6.el8_10 ol8_baseos_latest 853 k - -Transaction Summary -================================================================================ -Upgrade 1 Package - -Total download size: 853 k -Downloading Packages: -iproute-6.2.0-6.el8_10.x86_64.rpm 4.2 MB/s | 853 kB 00:00 --------------------------------------------------------------------------------- -Total 4.2 MB/s | 853 kB 00:00 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Preparing : 1/1 - Upgrading : iproute-6.2.0-6.el8_10.x86_64 1/2 - Cleanup : iproute-6.2.0-5.el8_9.x86_64 2/2 - Running scriptlet: iproute-6.2.0-5.el8_9.x86_64 2/2 - Verifying : iproute-6.2.0-6.el8_10.x86_64 1/2 - Verifying : iproute-6.2.0-5.el8_9.x86_64 2/2 - -Upgraded: - iproute-6.2.0-6.el8_10.x86_64 - -Complete! -24 files removed -Removing intermediate container fe168b01f3ad - ---> 791878694a50 -Step 5/12 : RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm - ---> Running in 59d7143da358 - % Total % Received % Xferd Average Speed Time Time Time Current - Dload Upload Total Spent Left Speed -100 108M 100 108M 0 0 1440k 0 0:01:16 0:01:16 --:--:-- 1578k -Removing intermediate container 59d7143da358 - ---> 17c4534293e5 -Step 6/12 : RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm - ---> Running in 84b1cbffdc51 -Verifying... ######################################## -Preparing... ######################################## -Updating / installing... -ords-23.4.0-8.el8 ######################################## -INFO: Before starting ORDS service, run the below command as user oracle: - ords --config /etc/ords/config install -Removing intermediate container 84b1cbffdc51 - ---> 6e7151b79588 -Step 7/12 : RUN mkdir -p $ORDS_HOME/doc_root && mkdir -p $ORDS_HOME/error && mkdir -p $ORDS_HOME/secrets && chmod ug+x $ORDS_HOME/*.sh && groupadd -g 54322 dba && usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && chown -R oracle:dba $ORDS_HOME && echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - ---> Running in 66e5db5f343f -Removing intermediate container 66e5db5f343f - ---> 0523dc897bf4 -Step 8/12 : USER oracle - ---> Running in ffda8495ac77 -Removing intermediate container ffda8495ac77 - ---> 162acd4d0b93 -Step 9/12 : WORKDIR /home/oracle - ---> Running in 8c14310ffbc7 -Removing intermediate container 8c14310ffbc7 - ---> c8dae809e772 -Step 10/12 : VOLUME ["$ORDS_HOME/config/ords"] - ---> Running in ed64548fd997 -Removing intermediate container ed64548fd997 - ---> 22e2c99247b0 -Step 11/12 : EXPOSE 8888 - ---> Running in 921f7c85d61d -Removing intermediate container 921f7c85d61d - ---> e5d503c92224 -Step 12/12 : CMD $ORDS_HOME/$RUN_FILE - ---> Running in cad487298d63 -Removing intermediate container cad487298d63 - ---> fdb17aa242f8 -Successfully built fdb17aa242f8 -Successfully tagged oracle/ords-dboper:latest -08:57:18 oracle@mitk01:# - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/ImagePush.log b/docs/multitenant/ords-based/usecase01/logfiles/ImagePush.log deleted file mode 100644 index 9b8df426..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/ImagePush.log +++ /dev/null @@ -1,11 +0,0 @@ -/usr/bin/docker tag oracle/ords-dboper:latest /ords-dboper:latest -/usr/bin/docker push /ords-dboper:latest -The push refers to repository [/ords-dboper] -aef18205865c: Pushing [=============================> ] 56.55MB/95.45MB -2564d855e579: Pushing [=======> ] 57.08MB/357.6MB -a70a4f9a73c3: Pushed -f283c83ba6ac: Pushed -8c6709989678: Pushing [=======> ] 52.58MB/332.7MB -5bfd57d8f58a: Pushing [========> ] 37.47MB/229.2MB - - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/cdb.log b/docs/multitenant/ords-based/usecase01/logfiles/cdb.log deleted file mode 100644 index c75e9bf8..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/cdb.log +++ /dev/null @@ -1,372 +0,0 @@ -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M -NOT_INSTALLED=2 - SETUP -==================================================== -CONFIG=/etc/ords/config -+ export ORDS_LOGS=/tmp -+ ORDS_LOGS=/tmp -+ '[' -f /opt/oracle/ords//secrets/webserver_user ']' -++ cat /opt/oracle/ords//secrets/webserver_user -+ WEBSERVER_USER=.... -+ '[' -f /opt/oracle/ords//secrets/webserver_pwd ']' -++ cat /opt/oracle/ords//secrets/webserver_pwd -+ WEBSERVER_PASSWORD=.... -+ '[' -f /opt/oracle/ords//secrets/cdbadmin_user ']' -++ cat /opt/oracle/ords//secrets/cdbadmin_user -+ CDBADMIN_USER=.... -+ '[' -f /opt/oracle/ords//secrets/cdbadmin_pwd ']' -++ cat /opt/oracle/ords//secrets/cdbadmin_pwd -+ CDBADMIN_PWD=.... -+ '[' -f /opt/oracle/ords//secrets/sysadmin_pwd ']' -++ cat /opt/oracle/ords//secrets/sysadmin_pwd -+ SYSDBA_PASSWORD=..... -+ '[' -f /opt/oracle/ords//secrets/sysadmin_pwd ']' -++ cat /opt/oracle/ords//secrets/ords_pwd -+ ORDS_PASSWORD=.... -+ setupHTTPS -+ rm -rf /home/oracle/keystore -+ '[' '!' -d /home/oracle/keystore ']' -+ mkdir /home/oracle/keystore -+ cd /home/oracle/keystore -+ cat -+ rm /home/oracle/keystore/PASSWORD -+ ls -ltr /home/oracle/keystore -total 0 -+ SetParameter -+ /usr/local/bin/ords --config /etc/ords/config config set security.requestValidationFunction false -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:22 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: security.requestValidationFunction was set to: false in configuration: default -+ /usr/local/bin/ords --config /etc/ords/config config set jdbc.MaxLimit 100 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:23 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.MaxLimit was set to: 100 in configuration: default -+ /usr/local/bin/ords --config /etc/ords/config config set jdbc.InitialLimit 50 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:24 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.InitialLimit was set to: 50 in configuration: default -+ /usr/local/bin/ords --config /etc/ords/config config set error.externalPath /opt/oracle/ords/error -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:26 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: error.externalPath was set to: /opt/oracle/ords/error -+ /usr/local/bin/ords --config /etc/ords/config config set standalone.access.log /home/oracle -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:27 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.access.log was set to: /home/oracle -+ /usr/local/bin/ords --config /etc/ords/config config set standalone.https.port 8888 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:28 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.port was set to: 8888 -+ /usr/local/bin/ords --config /etc/ords/config config set standalone.https.cert /opt/oracle/ords//secrets/tls.crt -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:29 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert was set to: /opt/oracle/ords//secrets/tls.crt -+ /usr/local/bin/ords --config /etc/ords/config config set standalone.https.cert.key /opt/oracle/ords//secrets/tls.key -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:31 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert.key was set to: /opt/oracle/ords//secrets/tls.key -+ /usr/local/bin/ords --config /etc/ords/config config set restEnabledSql.active true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:32 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: restEnabledSql.active was set to: true in configuration: default -+ /usr/local/bin/ords --config /etc/ords/config config set security.verifySSL true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:33 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: security.verifySSL was set to: true -+ /usr/local/bin/ords --config /etc/ords/config config set database.api.enabled true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:34 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.enabled was set to: true -+ /usr/local/bin/ords --config /etc/ords/config config set plsql.gateway.mode false -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:35 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -Invalid VALUE argument false for KEY plsql.gateway.mode. -+ /usr/local/bin/ords --config /etc/ords/config config set database.api.management.services.disabled false -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:37 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.management.services.disabled was set to: false -+ /usr/local/bin/ords --config /etc/ords/config config set misc.pagination.maxRows 1000 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:38 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: misc.pagination.maxRows was set to: 1000 in configuration: default -+ /usr/local/bin/ords --config /etc/ords/config config set db.cdb.adminUser 'C##DBAPI_CDB_ADMIN AS SYSDBA' -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:39 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser was set to: C##DBAPI_CDB_ADMIN AS SYSDBA in configuration: default -+ /usr/local/bin/ords --config /etc/ords/config config secret --password-stdin db.cdb.adminUser.password -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:40 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser.password was set to: ****** in configuration: default -+ /usr/local/bin/ords --config /etc/ords/config config user add --password-stdin sql_admin 'SQL Administrator, System Administrator' -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:42 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -Created user sql_admin in file /etc/ords/config/global/credentials -+ /usr/local/bin/ords --config /etc/ords/config install --admin-user 'SYS AS SYSDBA' --db-hostname racnode1.testrac.com --db-port 1521 --db-servicename TESTORDS --feature-db-api true --feature-rest-enabled-sql true --log-folder /tmp --proxy-user --password-stdin -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:45:43 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -Oracle REST Data Services - Non-Interactive Install -Connecting to database user: SYS AS SYSDBA url: jdbc:oracle:thin:@//racnode1.testrac.com:1521/TESTORDS - -Retrieving information.. -Your database connection is to a CDB. ORDS common user ORDS_PUBLIC_USER will be created in the CDB. ORDS schema will be installed in the PDBs. -Root CDB$ROOT - create ORDS common user -PDB PDB$SEED - install ORDS 22.3.0.r2781755 -PDB PDB$SEED - configure PL/SQL gateway user APEX_PUBLIC_USER in ORDS version 22.3.0.r2781755 - -The setting named: db.connectionType was set to: basic in configuration: default -The setting named: db.hostname was set to: racnode1.testrac.com in configuration: default -The setting named: db.port was set to: 1521 in configuration: default -The setting named: db.servicename was set to: TESTORDS in configuration: default -The setting named: db.serviceNameSuffix was set to: in configuration: default -The setting named: plsql.gateway.mode was set to: proxied in configuration: default -The setting named: db.username was set to: ORDS_PUBLIC_USER in configuration: default -The setting named: db.password was set to: ****** in configuration: default -The setting named: security.requestValidationFunction was set to: wwv_flow_epg_include_modules.authorize in configuration: default -2022-10-11T07:45:45.885Z INFO Installing Oracle REST Data Services version 22.3.0.r2781755 in CDB$ROOT -2022-10-11T07:45:46.703Z INFO ... Verified database prerequisites -2022-10-11T07:45:46.946Z INFO ... Created Oracle REST Data Services proxy user -2022-10-11T07:45:46.979Z INFO Completed installation for Oracle REST Data Services version 22.3.0.r2781755. Elapsed time: 00:00:01.71 - -2022-10-11T07:45:46.986Z INFO Installing Oracle REST Data Services version 22.3.0.r2781755 in PDB$SEED -2022-10-11T07:45:47.078Z INFO ... Verified database prerequisites -2022-10-11T07:45:47.290Z INFO ... Created Oracle REST Data Services proxy user -2022-10-11T07:45:47.741Z INFO ... Created Oracle REST Data Services schema -2022-10-11T07:45:48.097Z INFO ... Granted privileges to Oracle REST Data Services -2022-10-11T07:45:51.848Z INFO ... Created Oracle REST Data Services database objects -2022-10-11T07:46:00.829Z INFO Completed installation for Oracle REST Data Services version 22.3.0.r2781755. Elapsed time: 00:00:13.841 - -2022-10-11T07:46:00.898Z INFO Completed configuring PL/SQL gateway user for Oracle REST Data Services version 22.3.0.r2781755. Elapsed time: 00:00:00.68 - -2022-10-11T07:46:00.898Z INFO Completed CDB installation for Oracle REST Data Services version 22.3.0.r2781755. Total elapsed time: 00:00:15.17 - -2022-10-11T07:46:00.898Z INFO Log file written to /tmp/ords_cdb_install_2022-10-11_074545_78000.log -2022-10-11T07:46:00.901Z INFO To run in standalone mode, use the ords serve command: -2022-10-11T07:46:00.901Z INFO ords --config /etc/ords/config serve -2022-10-11T07:46:00.901Z INFO Visit the ORDS Documentation to access tutorials, developer guides and more to help you get started with the new ORDS Command Line Interface (http://oracle.com/rest). -+ '[' 0 -ne 0 ']' -+ StartUp -+ /usr/local/bin/ords --config /etc/ords/config serve --port 8888 --secure -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 22.3 Production on Tue Oct 11 07:46:02 2022 - -Copyright (c) 2010, 2022, Oracle. - -Configuration: - /etc/ords/config/ - -2022-10-11T07:46:02.286Z INFO HTTPS and HTTPS/2 listening on host: 0.0.0.0 port: 8888 -2022-10-11T07:46:02.302Z INFO Disabling document root because the specified folder does not exist: /etc/ords/config/global/doc_root -2022-10-11T07:46:04.636Z INFO Configuration properties for: |default|lo| -db.servicename=TESTORDS -db.serviceNameSuffix= -java.specification.version=19 -conf.use.wallet=true -database.api.management.services.disabled=false -sun.jnu.encoding=UTF-8 -user.region=US -java.class.path=/opt/oracle/ords/ords.war -java.vm.vendor=Oracle Corporation -standalone.https.cert.key=/opt/oracle/ords//secrets/tls.key -sun.arch.data.model=64 -nashorn.args=--no-deprecation-warning -java.vendor.url=https://java.oracle.com/ -resource.templates.enabled=false -user.timezone=UTC -db.port=1521 -java.vm.specification.version=19 -os.name=Linux -sun.java.launcher=SUN_STANDARD -user.country=US -sun.boot.library.path=/usr/java/jdk-19/lib -sun.java.command=/opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure -jdk.debug=release -sun.cpu.endian=little -user.home=/home/oracle -oracle.dbtools.launcher.executable.jar.path=/opt/oracle/ords/ords.war -user.language=en -db.cdb.adminUser.password=****** -java.specification.vendor=Oracle Corporation -java.version.date=2022-09-20 -database.api.enabled=true -java.home=/usr/java/jdk-19 -db.username=ORDS_PUBLIC_USER -file.separator=/ -java.vm.compressedOopsMode=32-bit -line.separator= - -restEnabledSql.active=true -java.specification.name=Java Platform API Specification -java.vm.specification.vendor=Oracle Corporation -java.awt.headless=true -standalone.https.cert=/opt/oracle/ords//secrets/tls.crt -db.hostname=racnode1.testrac.com -db.password=****** -sun.management.compiler=HotSpot 64-Bit Tiered Compilers -security.requestValidationFunction=wwv_flow_epg_include_modules.authorize -misc.pagination.maxRows=1000 -java.runtime.version=19+36-2238 -user.name=oracle -error.externalPath=/opt/oracle/ords/error -stdout.encoding=UTF-8 -path.separator=: -db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA -os.version=5.4.17-2136.308.9.el7uek.x86_64 -java.runtime.name=Java(TM) SE Runtime Environment -file.encoding=UTF-8 -plsql.gateway.mode=proxied -security.verifySSL=true -standalone.https.port=8888 -java.vm.name=Java HotSpot(TM) 64-Bit Server VM -java.vendor.url.bug=https://bugreport.java.com/bugreport/ -java.io.tmpdir=/tmp -oracle.dbtools.cmdline.ShellCommand=ords -java.version=19 -user.dir=/home/oracle/keystore -os.arch=amd64 -java.vm.specification.name=Java Virtual Machine Specification -jdbc.MaxLimit=100 -oracle.dbtools.cmdline.home=/opt/oracle/ords -native.encoding=UTF-8 -java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib -java.vendor=Oracle Corporation -java.vm.info=mixed mode, sharing -stderr.encoding=UTF-8 -java.vm.version=19+36-2238 -sun.io.unicode.encoding=UnicodeLittle -jdbc.InitialLimit=50 -db.connectionType=basic -java.class.version=63.0 -standalone.access.log=/home/oracle - -2022-10-11T07:46:06.669Z INFO Oracle REST Data Services initialized -Oracle REST Data Services version : 22.3.0.r2781755 -Oracle REST Data Services server info: jetty/10.0.11 -Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 19+36-2238 - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log b/docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log deleted file mode 100644 index b4602f54..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log +++ /dev/null @@ -1,357 +0,0 @@ -/usr/local/go/bin/kubectl logs -f `/usr/local/go/bin/kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. -ORDSVERSIN:23.4.0-8 -NOT_INSTALLED=2 - SETUP -==================================================== -CONFIG=/etc/ords/config -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:16 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.connectionType was set to: customurl in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:18 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.customURL was set to: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:20 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: security.requestValidationFunction was set to: false in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:22 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.MaxLimit was set to: 100 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:24 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.InitialLimit was set to: 50 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:25 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: error.externalPath was set to: /opt/oracle/ords/error -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:27 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.access.log was set to: /home/oracle -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:29 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.port was set to: 8888 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:31 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert was set to: /opt/oracle/ords//secrets/tls.crt -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:33 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert.key was set to: /opt/oracle/ords//secrets/tls.key -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:35 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: restEnabledSql.active was set to: true in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:37 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: security.verifySSL was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:39 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.enabled was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:41 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: plsql.gateway.mode was set to: disabled in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:43 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.management.services.disabled was set to: false -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:45 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: misc.pagination.maxRows was set to: 1000 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:47 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser was set to: C##DBAPI_CDB_ADMIN AS SYSDBA in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:49 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser.password was set to: ****** in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:51 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Created user welcome in file /etc/ords/config/global/credentials -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:53 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Oracle REST Data Services - Non-Interactive Install - -Retrieving information... -Completed verifying Oracle REST Data Services schema version 23.4.0.r3461619. -Connecting to database user: ORDS_PUBLIC_USER url: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -The setting named: db.serviceNameSuffix was set to: in configuration: default -The setting named: db.username was set to: ORDS_PUBLIC_USER in configuration: default -The setting named: db.password was set to: ****** in configuration: default -The setting named: security.requestValidationFunction was set to: ords_util.authorize_plsql_gateway in configuration: default -2024-08-20T07:21:57.563Z INFO Oracle REST Data Services schema version 23.4.0.r3461619 is installed. -2024-08-20T07:21:57.565Z INFO To run in standalone mode, use the ords serve command: -2024-08-20T07:21:57.565Z INFO ords --config /etc/ords/config serve -2024-08-20T07:21:57.565Z INFO Visit the ORDS Documentation to access tutorials, developer guides and more to help you get started with the new ORDS Command Line Interface (http://oracle.com/rest). -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:59 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -2024-08-20T07:21:59.739Z INFO HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8080 -2024-08-20T07:21:59.741Z INFO HTTPS and HTTPS/2 listening on host: 0.0.0.0 port: 8888 -2024-08-20T07:21:59.765Z INFO Disabling document root because the specified folder does not exist: /etc/ords/config/global/doc_root -2024-08-20T07:21:59.765Z INFO Default forwarding from / to contextRoot configured. -2024-08-20T07:22:05.313Z INFO Configuration properties for: |default|lo| -db.serviceNameSuffix= -java.specification.version=22 -conf.use.wallet=true -database.api.management.services.disabled=false -sun.jnu.encoding=UTF-8 -user.region=US -java.class.path=/opt/oracle/ords/ords.war -java.vm.vendor=Oracle Corporation -standalone.https.cert.key=/opt/oracle/ords//secrets/tls.key -sun.arch.data.model=64 -nashorn.args=--no-deprecation-warning -java.vendor.url=https://java.oracle.com/ -resource.templates.enabled=false -user.timezone=UTC -java.vm.specification.version=22 -os.name=Linux -sun.java.launcher=SUN_STANDARD -user.country=US -sun.boot.library.path=/usr/java/jdk-22/lib -sun.java.command=/opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure -jdk.debug=release -sun.cpu.endian=little -user.home=/home/oracle -oracle.dbtools.launcher.executable.jar.path=/opt/oracle/ords/ords.war -user.language=en -db.cdb.adminUser.password=****** -java.specification.vendor=Oracle Corporation -java.version.date=2024-07-16 -database.api.enabled=true -java.home=/usr/java/jdk-22 -db.username=ORDS_PUBLIC_USER -file.separator=/ -java.vm.compressedOopsMode=32-bit -line.separator= - -restEnabledSql.active=true -java.specification.name=Java Platform API Specification -java.vm.specification.vendor=Oracle Corporation -java.awt.headless=true -standalone.https.cert=/opt/oracle/ords//secrets/tls.crt -db.password=****** -sun.management.compiler=HotSpot 64-Bit Tiered Compilers -security.requestValidationFunction=ords_util.authorize_plsql_gateway -misc.pagination.maxRows=1000 -java.runtime.version=22.0.2+9-70 -user.name=oracle -error.externalPath=/opt/oracle/ords/error -stdout.encoding=UTF-8 -path.separator=: -db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA -os.version=5.4.17-2136.329.3.1.el7uek.x86_64 -java.runtime.name=Java(TM) SE Runtime Environment -file.encoding=UTF-8 -plsql.gateway.mode=disabled -security.verifySSL=true -standalone.https.port=8888 -java.vm.name=Java HotSpot(TM) 64-Bit Server VM -java.vendor.url.bug=https://bugreport.java.com/bugreport/ -java.io.tmpdir=/tmp -oracle.dbtools.cmdline.ShellCommand=ords -java.version=22.0.2 -user.dir=/home/oracle -os.arch=amd64 -java.vm.specification.name=Java Virtual Machine Specification -jdbc.MaxLimit=100 -oracle.dbtools.cmdline.home=/opt/oracle/ords -native.encoding=UTF-8 -java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib -java.vendor=Oracle Corporation -java.vm.info=mixed mode, sharing -stderr.encoding=UTF-8 -java.vm.version=22.0.2+9-70 -sun.io.unicode.encoding=UnicodeLittle -jdbc.InitialLimit=50 -db.connectionType=customurl -java.class.version=66.0 -db.customURL=jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -standalone.access.log=/home/oracle - -2024-08-20T07:22:09.268Z INFO - -Mapped local pools from /etc/ords/config/databases: - /ords/ => default => VALID - - -2024-08-20T07:22:09.414Z INFO Oracle REST Data Services initialized -Oracle REST Data Services version : 23.4.0.r3461619 -Oracle REST Data Services server info: jetty/10.0.18 -Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 22.0.2+9-70 - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log b/docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log deleted file mode 100644 index e3915a21..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log +++ /dev/null @@ -1,19 +0,0 @@ -CREATING TLS CERTIFICATES -/usr/bin/openssl genrsa -out ca.key 2048 -Generating RSA private key, 2048 bit long modulus (2 primes) -......................+++++ -..................................................+++++ -e is 65537 (0x010001) -/usr/bin/openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost Root CA " -out ca.crt -/usr/bin/openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost" -out server.csr -Generating a RSA private key -...........+++++ -...........................................+++++ -writing new private key to 'tls.key' ------ -/usr/bin/echo "subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com" > extfile.txt -/usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -Signature ok -subject=C = US, ST = California, L = SanFrancisco, O = "oracle ", CN = "cdb-dev-ords.oracle-database-operator-system ", CN = localhost -Getting CA Private Key - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log b/docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log deleted file mode 100644 index b787b752..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log +++ /dev/null @@ -1,39 +0,0 @@ -ORDS: Release 23.4 Production on Tue Aug 20 07:48:44 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Database pool: default - -Setting Value Source ------------------------------------------ -------------------------------------------------- ----------- -database.api.enabled true Global -database.api.management.services.disabled false Global -db.cdb.adminUser C##DBAPI_CDB_ADMIN AS SYSDBA Pool -db.cdb.adminUser.password ****** Pool Wallet -db.connectionType customurl Pool -db.customURL jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90 Pool - )(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNEC - T_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL= - TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONL - Y))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST= - scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNEC - T_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -db.password ****** Pool Wallet -db.serviceNameSuffix Pool -db.username ORDS_PUBLIC_USER Pool -error.externalPath /opt/oracle/ords/error Global -jdbc.InitialLimit 50 Pool -jdbc.MaxLimit 100 Pool -misc.pagination.maxRows 1000 Pool -plsql.gateway.mode disabled Pool -restEnabledSql.active true Pool -security.requestValidationFunction ords_util.authorize_plsql_gateway Pool -security.verifySSL true Global -standalone.access.log /home/oracle Global -standalone.https.cert /opt/oracle/ords//secrets/tls.crt Global -standalone.https.cert.key /opt/oracle/ords//secrets/tls.key Global -standalone.https.port 8888 Global - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log b/docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log deleted file mode 100644 index 232d5bb2..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log +++ /dev/null @@ -1,14 +0,0 @@ -/usr/bin/docker tag oracle/ords-dboper:latest [.......]/ords-dboper:latest - -/usr/bin/docker push [your container registry]/ords-dboper:latest -The push refers to repository [your container registry] -0405aac3af1c: Pushed -6be46e8e1e21: Pushed -c9884830a66d: Pushed -a46244557bb9: Pushing [===========================> ] 261.8MB/469.9MB -f988845e261e: Pushed -fe07ec0b1f5a: Layer already exists -2ac63de5f950: Layer already exists -386cd7a64c01: Layer already exists -826c69252b8b: Layer already exists - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/testapi.log b/docs/multitenant/ords-based/usecase01/logfiles/testapi.log deleted file mode 100644 index cb42ecc3..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/testapi.log +++ /dev/null @@ -1,62 +0,0 @@ -kubectl exec -it `kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system -i -t -- /usr/bin/curl -sSkv -k -X GET https://localhost:8888/ords/_/db-api/stable/metadata-catalog/ -* Trying ::1... -* TCP_NODELAY set -* Connected to localhost (::1) port 8888 (#0) -* ALPN, offering h2 -* ALPN, offering http/1.1 -* successfully set certificate verify locations: -* CAfile: /etc/pki/tls/certs/ca-bundle.crt - CApath: none -* TLSv1.3 (OUT), TLS handshake, Client hello (1): -* TLSv1.3 (IN), TLS handshake, Server hello (2): -* TLSv1.3 (IN), TLS handshake, [no content] (0): -* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): -* TLSv1.3 (IN), TLS handshake, Certificate (11): -* TLSv1.3 (IN), TLS handshake, CERT verify (15): -* TLSv1.3 (IN), TLS handshake, Finished (20): -* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): -* TLSv1.3 (OUT), TLS handshake, [no content] (0): -* TLSv1.3 (OUT), TLS handshake, Finished (20): -* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 -* ALPN, server accepted to use h2 -* Server certificate: -* subject: C=US; ST=California; L=SanFrancisco; O=oracle ; CN=cdb-dev-ords.oracle-database-operator-system ; CN=localhost -* start date: Aug 20 07:14:04 2024 GMT -* expire date: Aug 20 07:14:04 2025 GMT -* issuer: C=US; ST=California; L=SanFrancisco; O=oracle ; CN=cdb-dev-ords.oracle-database-operator-system ; CN=localhost Root CA -* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway. -* Using HTTP2, server supports multi-use -* Connection state changed (HTTP/2 confirmed) -* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 -* TLSv1.3 (OUT), TLS app data, [no content] (0): -* TLSv1.3 (OUT), TLS app data, [no content] (0): -* TLSv1.3 (OUT), TLS app data, [no content] (0): -* Using Stream ID: 1 (easy handle 0x55d14a7dea90) -* TLSv1.3 (OUT), TLS app data, [no content] (0): -> GET /ords/_/db-api/stable/metadata-catalog/ HTTP/2 -> Host: localhost:8888 -> User-Agent: curl/7.61.1 -> Accept: */* -> -* TLSv1.3 (IN), TLS handshake, [no content] (0): -* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): -* TLSv1.3 (IN), TLS handshake, [no content] (0): -* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): -* TLSv1.3 (IN), TLS app data, [no content] (0): -* Connection state changed (MAX_CONCURRENT_STREAMS == 128)! -* TLSv1.3 (OUT), TLS app data, [no content] (0): -* TLSv1.3 (IN), TLS app data, [no content] (0): -* TLSv1.3 (IN), TLS app data, [no content] (0): -< HTTP/2 200 -< content-type: application/json -< -* TLSv1.3 (IN), TLS handshake, [no content] (0): -* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): -* TLSv1.3 (IN), TLS app data, [no content] (0): -* TLSv1.3 (IN), TLS app data, [no content] (0): -* TLSv1.3 (IN), TLS app data, [no content] (0): -* TLSv1.3 (IN), TLS app data, [no content] (0): -* TLSv1.3 (IN), TLS app data, [no content] (0): -* TLSv1.3 (IN), TLS app data, [no content] (0): -* Connection #0 to host localhost left intact -{"items":[{"name":"default","links":[{"rel":"canonical","href":"https://localhost:8888/ords/_/db-api/stable/metadata-catalog/openapi.json","mediaType":"application/vnd.oai.openapi+json;version=3.0"}]}],"links":[{"rel":"self","href":"https://localhost:8888/ords/_/db-api/stable/metadata-catalog/"},{"rel":"describes","href":"https://localhost:8888/ords/_/db-api/stable/"}]} diff --git a/docs/multitenant/ords-based/usecase01/makefile b/docs/multitenant/ords-based/usecase01/makefile deleted file mode 100644 index ec454e28..00000000 --- a/docs/multitenant/ords-based/usecase01/makefile +++ /dev/null @@ -1,906 +0,0 @@ -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# | | | | ___| |_ __ ___ _ __ -# | |_| |/ _ \ | '_ \ / _ \ '__| -# | _ | __/ | |_) | __/ | -# |_| |_|\___|_| .__/ \___|_| -# |_| -# -# WARNING: Using this makefile helps you to customize yaml -# files. Edit parameters.txt with your enviroment -# informartion and execute the following steps -# -# 1) make operator -# it configures the operator yaml files with the -# watch namelist required by the multitenant controllers -# -# 2) make genyaml -# It automatically creates all the yaml files based on the -# information available in the parameters file -# -# 3) make secrets -# It configure the required secrets necessary to operate -# with pdbs multitenant controllers -# -# 4) make runall01 -# Start a series of operation create open close delete and so on -# -# LIST OF GENERAED YAML FILE -# -# ----------------------------- ---------------------------------- -# oracle-database-operator.yaml : oracle database operator -# cdbnamespace_binding.yaml : role binding for cdbnamespace -# pdbnamespace_binding.yaml : role binding for pdbnamespace -# create_cdb_secret.yaml : create secrets for ords server pod -# create_pdb_secret.yaml : create secrets for pluggable database -# create_ords_pod.yaml : create rest server pod -# create_pdb1_resource.yaml : create first pluggable database -# create_pdb2_resource.yaml : create second pluggable database -# open_pdb1_resource.yaml : open first pluggable database -# open_pdb2_resource.yaml : open second pluggable database -# close_pdb1_resource.yaml : close first pluggable database -# close_pdb2_resource.yaml : close second pluggable database -# clone_pdb_resource.yaml : clone thrid pluggable database -# clone_pdb2_resource.yaml : clone 4th pluggable database -# delete_pdb1_resource.yaml : delete first pluggable database -# delete_pdb2_resource.yaml : delete sencond pluggable database -# delete_pdb3_resource.yaml : delete thrid pluggable database -# unplug_pdb1_resource.yaml : unplug first pluggable database -# plug_pdb1_resource.yaml : plug first pluggable database -# map_pdb1_resource.yaml : map the first pluggable database -# config_map.yam : pdb parameters array -# -DATE := `date "+%y%m%d%H%M%S"` -###################### -# PARAMETER SECTIONS # -###################### - -export PARAMETERS=parameters.txt -export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) -export ORDPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDPWD|cut -d : -f 2) -export SYSPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep SYSPWD|cut -d : -f 2) -export WBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBUSER|cut -d : -f 2) -export WBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBPASS|cut -d : -f 2) -export PDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBUSR|cut -d : -f 2) -export PDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBPWD|cut -d : -f 2) -export CDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBUSR|cut -d : -f 2) -export CDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBPWD|cut -d : -f 2) -export OPRNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPRNAMESPACE|cut -d : -f 2) -export OPRNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPRNAMESPACE|cut -d : -f 2) -export ORDSIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDSIMG|cut -d : -f 2,3) -export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) -export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) -export OPRNAMESPACE=oracle-database-operator-system -export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml -export TEST_EXEC_TIMEOUT=3m -export IMAGE=oracle/ords-dboper:latest -export ORDSIMGDIR=../../../../ords - -REST_SERVER=ords -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -PRVKEY=ca.key -PUBKEY=public.pem -COMPANY=oracle -RUNTIME=/usr/bin/podman - -################# -### FILE LIST ### -################# - -export ORDS_POD=create_ords_pod.yaml - -export CDB_SECRETS=create_cdb_secrets.yaml -export PDB_SECRETS=create_pdb_secrets.yaml - -export PDBCRE1=create_pdb1_resource.yaml -export PDBCRE2=create_pdb2_resource.yaml - -export PDBCLOSE1=close_pdb1_resource.yaml -export PDBCLOSE2=close_pdb2_resource.yaml -export PDBCLOSE3=close_pdb3_resource.yaml - -export PDBOPEN1=open_pdb1_resource.yaml -export PDBOPEN2=open_pdb2_resource.yaml -export PDBOPEN3=open_pdb3_resource.yaml - -export PDBCLONE1=clone_pdb1_resource.yaml -export PDBCLONE2=clone_pdb2_resource.yaml - -export PDBDELETE1=delete_pdb1_resource.yaml -export PDBDELETE2=delete_pdb2_resource.yaml -export PDBDELETE3=delete_pdb3_resource.yaml - -export PDBUNPLUG1=unplug_pdb1_resource.yaml -export PDBPLUG1=plug_pdb1_resource.yaml - -export PDBMAP1=map_pdb1_resource.yaml -export PDBMAP2=map_pdb2_resource.yaml -export PDBMAP3=map_pdb3_resource.yaml - -export PDBMAP1=map_pdb1_resource.yaml -export PDBMAP2=map_pdb2_resource.yaml -export PDBMAP3=map_pdb3_resource.yaml - - -##BINARIES -export KUBECTL=/usr/bin/kubectl -OPENSSL=/usr/bin/openssl -ECHO=/usr/bin/echo -RM=/usr/bin/rm -CP=/usr/bin/cp -TAR=/usr/bin/tar -MKDIR=/usr/bin/mkdir -SED=/usr/bin/sed - -define msg -@printf "\033[31;7m%s\033[0m\r" "......................................]" -@printf "\033[31;7m[\xF0\x9F\x91\x89 %s\033[0m\n" $(1) -endef - -check: - $(call msg,"CHECK PARAMETERS") - @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) - @printf "ORDPWD.................:%s\n" $(ORDPWD) - @printf "SYSPWD.................:%s\n" $(SYSPWD) - @printf "WBUSER.................:%s\n" $(WBUSER) - @printf "WBPASS.................:%s\n" $(WBPASS) - @printf "PDBUSR.................:%s\n" $(PDBUSR) - @printf "PDBPWD.................:%s\n" $(PDBPWD) - @printf "CDBUSR.................:%s\n" $(CDBUSR) - @printf "CDBPWD.................:%s\n" $(CDBPWD) - @printf "OPRNAMESPACE...........:%s\n" $(OPRNAMESPACE) - @printf "COMPANY................:%s\n" $(COMPANY) - @printf "APIVERSION.............:%s\n" $(APIVERSION) - - -tlscrt: - $(call msg,"TLS GENERATION") - #$(OPENSSL) genrsa -out $(PRVKEY) 2048 - $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) - $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) \ - -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj \ - "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(OPRNAMESPACE)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(OPRNAMESPACE)" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - -tlssec: - $(call msg,"GENERATE TLS SECRET") - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) - - -delsec: - $(call msg,"CLEAN OLD SECRETS") - $(eval SECRETSP:=$(shell kubectl get secrets -n $(OPRNAMESPACE) -o custom-columns=":metadata.name" --no-headers|grep -v webhook-server-cert) ) - @[ "${SECRETSP}" ] && ( \ - printf "Deleteing secrets in namespace -n $(OPRNAMESPACE)\n") &&\ - ($(KUBECTL) delete secret $(SECRETSP) -n $(OPRNAMESPACE))\ - || ( echo "No screts in namespace $(OPRNAMESPACE)") - - -###### ENCRYPTED SECRETS ###### -export PRVKEY=ca.key -export PUBKEY=public.pem -WBUSERFILE=wbuser.txt -WBPASSFILE=wbpass.txt -CDBUSRFILE=cdbusr.txt -CDBPWDFILE=cdbpwd.txt -SYSPWDFILE=syspwd.txt -ORDPWDFILE=ordpwd.txt -PDBUSRFILE=pdbusr.txt -PDBPWDFILE=pdbpwd.txt - - - -secrets: delsec tlscrt tlssec - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(OPRNAMESPACE) - @$(ECHO) $(WBUSER) > $(WBUSERFILE) - @$(ECHO) $(WBPASS) > $(WBPASSFILE) - @$(ECHO) $(CDBPWD) > $(CDBPWDFILE) - @$(ECHO) $(CDBUSR) > $(CDBUSRFILE) - @$(ECHO) $(SYSPWD) > $(SYSPWDFILE) - @$(ECHO) $(ORDPWD) > $(ORDPWDFILE) - @$(ECHO) $(PDBUSR) > $(PDBUSRFILE) - @$(ECHO) $(PDBPWD) > $(PDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBPWDFILE) |base64 > e_$(CDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBUSRFILE) |base64 > e_$(CDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE) |base64 > e_$(SYSPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(ORDPWDFILE) |base64 > e_$(ORDPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic cdbpwd --from-file=e_$(CDBPWDFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic cdbusr --from-file=e_$(CDBUSRFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic syspwd --from-file=e_$(SYSPWDFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic ordpwd --from-file=e_$(ORDPWDFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(OPRNAMESPACE) - $(RM) $(WBUSERFILE) $(WBPASSFILE) $(CDBPWDFILE) $(CDBUSRFILE) $(SYSPWDFILE) $(ORDPWDFILE) $(PDBUSRFILE) $(PDBPWDFILE) - $(RM) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(CDBPWDFILE) e_$(CDBUSRFILE) e_$(SYSPWDFILE) e_$(ORDPWDFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) - - -### YAML FILE SECTION ### -operator: - $(CP) ${ORACLE_OPERATOR_YAML} . - ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG - $(SED) -i 's/value: ""/value: $(OPRNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` - - -define _script00 -cat < authsection01.yaml - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" - cdbAdminUser: - secret: - secretName: "cdbusr" - key: "e_cdbusr.txt" - cdbAdminPwd: - secret: - secretName: "cdbpwd" - key: "e_cdbpwd.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - -cat< authsection02.yaml - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - - -cat < ${OPRNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: ${OPRNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -cat < ${OPRNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: ${OPRNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -endef -export script00 = $(value _script00) -secyaml: - @ eval "$$script00" - -#echo ords pod creation -define _script01 -cat < ${ORDS_POD} -apiVersion: database.oracle.com/${APIVERSION} -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: ${ORDSIMG} - ordsImagePullPolicy: "Always" - dbTnsurl : ${TNSALIAS} - replicas: 1 - deletePdbCascade: true -EOF - -cat authsection01.yaml >> ${ORDS_POD} - -endef -export script01 = $(value _script01) - - -define _script02 - -cat <${PDBCRE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat < ${PDBCRE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat <${PDBOPEN1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBOPEN2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBOPEN3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBCLOSE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${PDBCLOSE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${PDBCLOSE3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: ""new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat < ${PDBCLONE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" -EOF - -cat < ${PDBCLONE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb4 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" -EOF - - -cat < ${PDBDELETE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${PDBDELETE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${PDBUNPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" -EOF - -cat <${PDBPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" -EOF - -cat <${PDBMAP1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - -cat <${PDBMAP2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - - -cat <${PDBMAP3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - - -## Auth information -for _file in ${PDBCRE1} ${PDBCRE2} ${PDBOPEN1} ${PDBOPEN2} ${PDBOPEN3} ${PDBCLOSE1} ${PDBCLOSE2} ${PDBCLOSE3} ${PDBCLONE1} ${PDBCLONE2} ${PDBDELETE1} ${PDBDELETE2} ${PDBUNPLUG1} ${PDBPLUG1} ${PDBMAP1} ${PDBMAP2} ${PDBMAP3} -do -ls -ltr ${_file} - cat authsection02.yaml >> ${_file} -done -rm authsection02.yaml -rm authsection01.yaml -endef - -export script02 = $(value _script02) - -genyaml: secyaml - @ eval "$$script01" - @ eval "$$script02" - -cleanyaml: - - $(RM) $(PDBMAP3) $(PDBMAP2) $(PDBMAP1) $(PDBPLUG1) $(PDBUNPLUG1) $(PDBDELETE2) $(PDBDELETE1) $(PDBCLONE2) $(PDBCLONE1) $(PDBCLOSE3) $(PDBCLOSE2) $(PDBCLOSE1) $(PDBOPEN3) $(PDBOPEN2) $(PDBOPEN1) $(PDBCRE2) $(PDBCRE1) $(ORDS_POD) $(CDB_SECRETS) $(PDB_SECRETS) - - $(RM) ${OPRNAMESPACE}_binding.yaml ${OPRNAMESPACE}_binding.yaml - - -cleancrt: - - $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl - - -################# -### PACKAGING ### -################# - -pkg: - - $(RM) -rf /tmp/pkgtestplan - $(MKDIR) /tmp/pkgtestplan - $(CP) -R * /tmp/pkgtestplan - $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ - $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan - -################ -### diag ### -################ - -login: - $(KUBECTL) exec `$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep ords|cut -d ' ' -f 1` -n $(OPRNAMESPACE) -it -- /bin/bash - - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - -####################################################### -#### TEST SECTION #### -####################################################### - -run00: - @$(call msg,"cdb pod creation") - - $(KUBECTL) delete cdb cdb-dev -n $(OPRNAMESPACE) - $(KUBECTL) apply -f $(ORDS_POD) - time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" cdb cdb-dev -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"cdb pod completed") - $(KUBECTL) get cdb -n $(OPRNAMESPACE) - $(KUBECTL) get pod -n $(OPRNAMESPACE) - -run01.1: - @$(call msg,"pdb pdb1 creation") - $(KUBECTL) apply -f $(PDBCRE1) - time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 creation completed") - $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) - -run01.2: - @$(call msg, "pdb pdb2 creation") - $(KUBECTL) apply -f $(PDBCRE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb2 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb2 creation completed") - $(KUBECTL) get pdb pdb2 -n $(OPRNAMESPACE) - -run02.1: - @$(call msg, "pdb pdb1 open") - $(KUBECTL) apply -f $(PDBOPEN1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 open completed") - $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) - -run02.2: - @$(call msg,"pdb pdb2 open") - $(KUBECTL) apply -f $(PDBOPEN2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb2 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb2 open completed") - $(KUBECTL) get pdb pdb2 -n $(OPRNAMESPACE) - - -run03.1: - @$(call msg,"clone pdb1-->pdb3") - $(KUBECTL) apply -f $(PDBCLONE1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb3 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb1-->pdb3 completed") - $(KUBECTL) get pdb pdb3 -n $(OPRNAMESPACE) - - -run03.2: - @$(call msg,"clone pdb2-->pdb4") - $(KUBECTL) apply -f $(PDBCLONE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb4 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb2-->pdb4 completed") - $(KUBECTL) get pdb pdb3 -n $(OPRNAMESPACE) - - -run04.1: - @$(call msg,"pdb pdb1 close") - $(KUBECTL) apply -f $(PDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 close completed") - $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) - -run04.2: - @$(call msg,"pdb pdb2 close") - $(KUBECTL) apply -f $(PDBCLOSE2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb2 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb2 close completed") - $(KUBECTL) get pdb pdb2 -n $(OPRNAMESPACE) - -run05.1: - @$(call msg,"pdb pdb1 unplug") - $(KUBECTL) apply -f $(PDBUNPLUG1) - $(KUBECTL) wait --for=delete pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb1 unplug completed") - -run06.1: - @$(call msg, "pdb pdb1 plug") - $(KUBECTL) apply -f $(PDBPLUG1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 plug completed") - $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) - -run07.1: - @$(call msg,"pdb pdb1 delete ") - - $(KUBECTL) apply -f $(PDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) apply -f $(PDBDELETE1) - $(KUBECTL) wait --for=delete pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb1 delete") - $(KUBECTL) get pdb -n $(OPRNAMESPACE) - -run99.1: - $(KUBECTL) delete cdb cdb-dev -n cdbnamespace - $(KUBECTL) wait --for=delete cdb cdb-dev -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) get cdb -n cdbnamespaace - $(KUBECTL) get pdb -n pdbnamespaace - - -## SEQ | ACTION -## ----+---------------- -## 00 | create ords pod -## 01 | create pdb -## 02 | open pdb -## 03 | clone pdb -## 04 | close pdb -## 05 | unpug pdb -## 06 | plug pdb -## 07 | delete pdb (declarative) - - -runall01: run00 run01.1 run01.2 run03.1 run03.2 run04.1 run05.1 run06.1 run02.1 run07.1 - - -###### BUILD ORDS IMAGE ###### - -createimage: - $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) - -createimageproxy: - $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) - -tagimage: - @echo "TAG IMAGE" - $(RUNTIME) tag $(IMAGE) $(ORDSIMG) - -push: - $(RUNTIME) push $(ORDSIMG) - - diff --git a/docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml deleted file mode 100644 index 18cb35b1..00000000 --- a/docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml deleted file mode 100644 index 85899597..00000000 --- a/docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml deleted file mode 100644 index 9c2c1cd3..00000000 --- a/docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "new_clone" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml deleted file mode 100644 index 63a0a49c..00000000 --- a/docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml deleted file mode 100644 index 8c4eed0d..00000000 --- a/docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml deleted file mode 100644 index 5f0e4b77..00000000 --- a/docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml b/docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml deleted file mode 100644 index 79e44269..00000000 --- a/docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml b/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml deleted file mode 120000 index d5bae7bc..00000000 --- a/docs/multitenant/ords-based/usecase01/oracle-database-operator.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../oracle-database-operator.yaml \ No newline at end of file diff --git a/docs/multitenant/ords-based/usecase01/parameters.txt b/docs/multitenant/ords-based/usecase01/parameters.txt deleted file mode 100644 index 0a7b394a..00000000 --- a/docs/multitenant/ords-based/usecase01/parameters.txt +++ /dev/null @@ -1,61 +0,0 @@ - -######################## -## REST SERVER IMAGE ### -######################## - -ORDSIMG:_your_container_registry/ords-dboper:latest - -############################## -## TNS URL FOR CDB CREATION ## -############################## -TNSALIAS:"T H I S I S J U S T A N E X A M P L E ....(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - -########################################### -## ORDS PUBLIC USER ## -########################################### -ORDPWD:Change_me_please - -########################################### -## SYSPASSWORD ## -########################################### -SYSPWD:Change_me_please - -####################### -## HTTPS CREDENTIAL ### -####################### - -WBUSER:Change_me_please -WBPASS:Change_me_please - -##################### -## PDB ADMIN USER ### -##################### - -PDBUSR:Change_me_please -PDBPWD:Change_me_please - -##################### -## CDB ADMIN USER ### -##################### - -CDBUSR:C##DBAPI_CDB_ADMIN -CDBPWD:Change_me_please - -################### -### NAMESPACES #### -################### - -PDBNAMESPACE:pdbnamespace -CDBNAMESPACE:cdbnamespace - -#################### -### COMPANY NAME ### -#################### - -COMPANY:oracle - -#################### -### APIVERSION ### -#################### - -APIVERSION:v4 diff --git a/docs/multitenant/ords-based/usecase01/pdb_close.yaml b/docs/multitenant/ords-based/usecase01/pdb_close.yaml deleted file mode 100644 index 5917d33a..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_close.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - diff --git a/docs/multitenant/ords-based/usecase01/pdb_create.yaml b/docs/multitenant/ords-based/usecase01/pdb_create.yaml deleted file mode 100644 index be3581ad..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_create.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - assertivePdbDeletion: true - diff --git a/docs/multitenant/ords-based/usecase01/pdb_delete.yaml b/docs/multitenant/ords-based/usecase01/pdb_delete.yaml deleted file mode 100644 index c22b546a..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_delete.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - diff --git a/docs/multitenant/ords-based/usecase01/pdb_map.yaml b/docs/multitenant/ords-based/usecase01/pdb_map.yaml deleted file mode 100644 index 3300a7fa..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_map.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - assertivePdbDeletion: true diff --git a/docs/multitenant/ords-based/usecase01/pdb_open.yaml b/docs/multitenant/ords-based/usecase01/pdb_open.yaml deleted file mode 100644 index 25fdccc4..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_open.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" diff --git a/docs/multitenant/ords-based/usecase01/pdb_secret.yaml b/docs/multitenant/ords-based/usecase01/pdb_secret.yaml deleted file mode 100644 index 60d95d76..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_secret.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - sysadmin_user: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." - diff --git a/docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml deleted file mode 100644 index 0e86e10c..00000000 --- a/docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/server.csr b/docs/multitenant/ords-based/usecase01/server.csr deleted file mode 100644 index e308d301..00000000 --- a/docs/multitenant/ords-based/usecase01/server.csr +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIC3TCCAcUCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh -MRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNV -BAMMLWNkYi1kZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVt -IDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAm9nlNSQNsPTVqH57MkWKZEyaVtzVKQ8Z3oDK6hWXfB24p0jVj6sTOJkf -NVAxnqmU8DpW3odpbU6qWe/n+B5vJpqdXUGdsq9NKyus2fGb/xf1UnskpA2FUuWZ -o3upyCFxDAOvE4eZUzlxIn+54XXaNAdQiU9E8VXPr5YxrvZ15T/xCXLtJPs/RCOF -cJ8+gvZGcjMbdP16auJDVWZzBaur3eKbiHN7LXNCCRzGO++dv0kGY8vH7MyFfgp3 -qYBiSHS3WDiFUJjYIvfa8lLfP1hnlCyHn8TnU9gjGjmd1YcccSKqWIAT24wPUKVU -Lme4n91jxDPp7g8nRtDw0Smj9gYCtQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEB -AGOG/9IJJRvT2JLcuzE5Arai1XHc6Jh65iuDRqXQav47Bz38FFF2gZNO69gzDmhq -6k7tie+5bPcAHuuJZ0dAa71a9SLjKl+XNkkI0vS6te6OK3DCVUoMqNCk5VdwrJw0 -RORbKUwgLEG6mu80Gc/6wCdeR/36hoYTMeNPjm6M9e+X5ppsXqxCNsgDxasJFT82 -FejuJE2sZ6RCradlDToUHNS1dMLoW0WAIISqOmrDvEI6snm9ZZr3Sxo1auEtpI6v -NllBM4AgEghy/2mAtke+By4WHCfXBpxEGv9S7ATqJHYrR5Qa3nwx0eojWW1vmn0/ -aEzslX1tAH6oz2jA6QZ0sNo= ------END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/ords-based/usecase01/tde_secret.yaml b/docs/multitenant/ords-based/usecase01/tde_secret.yaml deleted file mode 100644 index 7cf66c03..00000000 --- a/docs/multitenant/ords-based/usecase01/tde_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: tde1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - tdepassword: "bW1hbHZlenoK" - tdesecret: "bW1hbHZlenoK" - - - - diff --git a/docs/multitenant/ords-based/usecase01/tls.crt b/docs/multitenant/ords-based/usecase01/tls.crt deleted file mode 100644 index 6bf8aef4..00000000 --- a/docs/multitenant/ords-based/usecase01/tls.crt +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEFDCCAvygAwIBAgIUd9l6tMS21ak3e4S0VdPhY0jG3gQwDQYJKoZIhvcNAQEL -BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH -DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k -ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE -AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx -NTMyMzVaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG -A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j -ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxEjAQ -BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AJvZ5TUkDbD01ah+ezJFimRMmlbc1SkPGd6AyuoVl3wduKdI1Y+rEziZHzVQMZ6p -lPA6Vt6HaW1Oqlnv5/gebyaanV1BnbKvTSsrrNnxm/8X9VJ7JKQNhVLlmaN7qcgh -cQwDrxOHmVM5cSJ/ueF12jQHUIlPRPFVz6+WMa72deU/8Qly7ST7P0QjhXCfPoL2 -RnIzG3T9emriQ1VmcwWrq93im4hzey1zQgkcxjvvnb9JBmPLx+zMhX4Kd6mAYkh0 -t1g4hVCY2CL32vJS3z9YZ5Qsh5/E51PYIxo5ndWHHHEiqliAE9uMD1ClVC5nuJ/d -Y8Qz6e4PJ0bQ8NEpo/YGArUCAwEAAaNMMEowSAYDVR0RBEEwP4IsY2RiLWRldi1v -cmRzLm9yYWNsZS1kYXRhYmFzZS1vcGVyYXRvci1zeXN0ZW2CD3d3dy5leGFtcGxl -LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAh7Lsu2ITS6Bc2q/Ef4No5Us0Vo9BWKoL -AlrfQPjsv1erMGsyEEyZ0Cg8l3QrXlscQ1ESvx0BnRGjoqZGE4+PoVZTEYSkokXP -aAr69epPzXQRyyAGCg5GeL6IFAj1AzqJGNnKOrPaLpcTri4MboiWmW+MHmgLdyPK -iwl8bNa8841nK/L/m6QET15BI+MIAvn7pgcpztum5jmkB+eceXzXnKUGg77TaFiX -bXqVBR4EvexC4DgUfQJI4zJLFdcH/GHxCpaaXNjbXeVz1ZK/qo2TCrXp2UXVrznU -9VTUuCaQA2VYZCitvAbupt+1OvMFYhWiIAroJSmzrvH4oK+IXgY6GA== ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase01/tls.key b/docs/multitenant/ords-based/usecase01/tls.key deleted file mode 100644 index 666c5639..00000000 --- a/docs/multitenant/ords-based/usecase01/tls.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb2eU1JA2w9NWo -fnsyRYpkTJpW3NUpDxnegMrqFZd8HbinSNWPqxM4mR81UDGeqZTwOlbeh2ltTqpZ -7+f4Hm8mmp1dQZ2yr00rK6zZ8Zv/F/VSeySkDYVS5Zmje6nIIXEMA68Th5lTOXEi -f7nhddo0B1CJT0TxVc+vljGu9nXlP/EJcu0k+z9EI4Vwnz6C9kZyMxt0/Xpq4kNV -ZnMFq6vd4puIc3stc0IJHMY7752/SQZjy8fszIV+CnepgGJIdLdYOIVQmNgi99ry -Ut8/WGeULIefxOdT2CMaOZ3VhxxxIqpYgBPbjA9QpVQuZ7if3WPEM+nuDydG0PDR -KaP2BgK1AgMBAAECggEAKUwl1l0FW7yk2Q8a6glPUKCTzSybN1QPEMyj+D9ccsEV -aw57uKQmZbr9cA0d+OMK2lU7K6BKKXLM5SQTHcZCwcH6rPl0JiMZmbTrCp1hLslU -clS7MtV6XKsGeTGNncBuyjY3sD8gO9NezTt3L+0gsuS1TI06wZBxhh+QbsJUHzjW -bC3mNjD4SqXree4Snp05nlFaT2s2isIjj25mKDwBu8IX0BN2VjsaSiQcjb8Dmzmu -42Xh7bcWBebns8Ehuq9TIl6ZjQht+pmVOMlB862baVpW/9CxkknzM+UQhIkXTSJk -Jt/mGeO89V4/Zh2N4ixIOE1hw87EvRFBoYh2VF58QQKBgQDMujXYblh+eEdsB1LG -kY0LerFHuQgdzifYmjPl0jtBsWDmh5i6q9PRUs2JZ/Fsq4QMQ8SLinGzaIBq5FKr -CL067X5blrFA9H0D6exJI3iHBTQpeMFwtqvu3j+zpCmgzonaUDQrczUpc0hxU7YI -/jhDe9LSWknPrzzMoWWKuy0sTQKBgQDC4g8F2krqm9Q5ug8bRKTAvMrY0skFIwrP -5LXBq9C8YCnLnT4S4tYQfbnWaBeG7YpkkmkZe30c9MUjsr1OHZbo+jlxHBU+oRYZ -e1j0UorVGt7FfNe/zjW0fLd72CBO741EDvV6pVeItkAwH6P5/cbRu085dwvyFbxv -JmOaYddECQKBgQCuid6YG1NE10SE3CV89uAZtktny18ZEgY0ixrNx5MPaaskPtw9 -4Xofjol+qOhR7lQQpMHu+WQAQYqiFvBHspapo4pDiVCrAQWIDamNnTkHW69h3/qD -HqmsZzxF6iI3X351akVf+cOMCCXtwCGEvz+2gN12ytT8w/iAuOS6BuP3TQKBgBlf -v57+diSn13EQtajSPjVOH4ctorjFgEHjQHsP+OSeDLMTLSLeYArTo9+zu+R4hz1j -BsYnmvmrMQPd4OIL3jtFYTdF9coqxSraMZHWMXdfwUOrZpf1rG5skqNQV5yPejAz -Vmj6oDQPrrnVVM9W6I0kO0N7KZYCmH9MW0mdlZ6pAoGAB60f2sk35VUBpvh7qzTY -70WDbNnCCU3I3KZ7LCUwUPWzGLQwMXRlAb5ZMheT/SGPChX4QXCNUCjXkR3Am3NO -yURHqZIRy0bwZRVjYnlCtc9YQ8pB0isZ1z2a9FXRD75o2WboFZ+VsG0FU81IE2ZO -gW802gT76NRnz851B7/nFNs= ------END PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml deleted file mode 100644 index 61fe915d..00000000 --- a/docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/README.md b/docs/multitenant/ords-based/usecase02/README.md deleted file mode 100644 index 39978747..00000000 --- a/docs/multitenant/ords-based/usecase02/README.md +++ /dev/null @@ -1,523 +0,0 @@ - - - -# UNPLUG - PLUG - CLONE - -- [UNPLUG - PLUG - CLONE](#unplug---plug---clone) - - [INTRODUCTION](#introduction) - - [UNPLUG DATABASE](#unplug-database) - - [PLUG DATABASE](#plug-database) - - [CLONE PDB](#clone-pdb) - -### INTRODUCTION - -> ☞ The examples of this folder are based on single namespace **oracle-database-operator-system** - -This page explains how to plug and unplug database a pdb; it assumes that you have already configured a pluggable database (see [usecase01](../usecase01/README.md)). Check yaml parameters in the CRD tables in the main [README](../README.md) file. - -```text - - - +--------------------------------+ - UNPLUG PDB PLUG PDB | CLONE PDB | - | | - +-----------+ +-----------+ | +-----------+ +----------+ | - | PDB | | PDB | | | PDB | |CLONED PDB| | - +----+------+ +----+------+ | +----+------+ +----------+ | - | | | | | | -+----> UNPLUG -----+ +--> PLUG | CLONE ---------+ | -| | | | | | | | -| +----+------+ | | +----+------+ | +----+------+ | -| | Container | | | | Container | | | Container | | -| | | | | | | | | | | -| +-----------+ | | +-----------+ | +-----------+ | -| | | | | -| +------+----+ | | kubectk apply -f pdb_clone.yaml| -| | | | | | -| +------|-----------|--------+ | +--------------------------------+ -| | +----+----+ +--+------+ | | -| | |xml file | |DB FILES | |--+ -| | +---------+ +---------+ | | -| +---------------------------+ | -| | -| | -+- kubectl apply -f pdb_unplug.yaml | - | - kubectl apply -f pdb_plug.yaml-----+ -``` - -### UNPLUG DATABASE - -Use the following command to check kubernets pdb resources. Note that the output of the commands can be tailored to meet your needs. Just check the structure of pdb resource **kubectl get pdbs -n oracle-database-operator-system -o=json** and modify the script accordingly. For the sake of simplicity put this command in a single script **checkpdbs.sh**. - -```bash -kubectl get pdbs -n oracle-database-operator-system -o=jsonpath='{range .items[*]} -{"\n==================================================================\n"} -{"CDB="}{.metadata.labels.cdb} -{"K8SNAME="}{.metadata.name} -{"PDBNAME="}{.spec.pdbName} -{"OPENMODE="}{.status.openMode} -{"ACTION="}{.status.action} -{"MSG="}{.status.msg} -{"\n"}{end}' -``` - -We assume that the pluggable database pdbdev is already configured and opened in read write mode - -```bash -./checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE=READ WRITE -ACTION=CREATE -MSG=Success - -``` - -Prepare a new yaml file **pdb_unplug.yaml** to unplug the pdbdev database. Make sure that the path of the xml file is correct and check the existence of all the required secrets. Do not reuse an existing xml files. - -```yaml -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -#pdb_unplug.yaml -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdbunplug.xml" - action: "Unplug" - [ secret sections ] -``` - -Close the pluggable database by applying the following yaml file **pdb_close.yaml** - -```yaml -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -#pdb_close.yaml -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - [secret section] -``` - -```bash -kubectl apply -f pdb_close.yaml -pdb.database.oracle.com/pdb1 configured - -sh checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE=MOUNTED -ACTION=MODIFY -MSG=Success -``` -After that apply the unplug file **pdb_unplug.yaml** ; The resource is no longer available once the unplug operation is completed. - -```bash -kubectl apply -f pdb_unplug.yaml -pdb.database.oracle.com/pdb1 configured - -sh checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE=MOUNTED -ACTION=MODIFY -MSG=Waiting for PDB to be unplugged -``` - -Check kubernets log files and the database alert log - -```text -/usr/bin/kubectl logs -f pod/`/usr/bin/kubectl get pods -n oracle-database-operator-system|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n oracle-database-operator-system -[...] -base-oracle-com-v1alpha1-pdb", "UID": "6f469423-85e5-4287-94d5-3d91a04b621e", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2023-01-03T14:04:05Z INFO pdb-webhook ValidateUpdate-Validating PDB spec for : pdb1 -2023-01-03T14:04:05Z INFO pdb-webhook validateCommon {"name": "pdb1"} -2023-01-03T14:04:05Z INFO pdb-webhook Valdiating PDB Resource Action : UNPLUG -2023-01-03T14:04:05Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "6f469423-85e5-4287-94d5-3d91a04b621e", "allowed": true} - - -[database alert log] -Domain Action Reconfiguration complete (total time 0.0 secs) -Completed: ALTER PLUGGABLE DATABASE "pdbdev" UNPLUG INTO '/tmp/pdbunplug.xml' -DROP PLUGGABLE DATABASE "pdbdev" KEEP DATAFILES -2023-01-03T14:04:05.518845+00:00 -Deleted Oracle managed file +DATA/DB12/F146D9482AA0260FE0531514000AB1BC/TEMPFILE/temp.266.1125061101 -2023-01-03T14:04:05.547820+00:00 -Stopped service pdbdev -Completed: DROP PLUGGABLE DATABASE "pdbdev" KEEP DATAFILES - -``` - - -login to the server and check xml file existence. Verify the datafile path on the ASM filesystem. - -```bash -ls -ltr /tmp/pdbunplug.xml --rw-r--r--. 1 oracle asmadmin 8007 Jan 3 14:04 /tmp/pdbunplug.xml -[..] -cat /tmp/pdbunplug.xml |grep path - +DATA/DB12/F146D9482AA0260FE0531514000AB1BC/DATAFILE/system.353.1125061021 - +DATA/DB12/F146D9482AA0260FE0531514000AB1BC/DATAFILE/sysaux.328.1125061021 - +DATA/DB12/F146D9482AA0260FE0531514000AB1BC/DATAFILE/undotbs1.347.1125061021 - +DATA/DB12/F146D9482AA0260FE0531514000AB1BC/TEMPFILE/temp.266.1125061101 - +DATA/DB12/F146D9482AA0260FE0531514000AB1BC/DATAFILE/undo_2.318.1125061021 -[..] -asmcmd ls -l +DATA/DB12/F146D9482AA0260FE0531514000AB1BC/DATAFILE/system.353.1125061021 -Type Redund Striped Time Sys Name -DATAFILE UNPROT COARSE JAN 03 14:00:00 Y system.353.1125061021 -``` - -### PLUG DATABASE - -Prepare a new yaml file **pdb_plug.yaml** to plug the database back into the container. - -```yaml -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -# pdb_plug.yaml -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdbunplug.xml" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - action: "Plug" - [secrets section] -``` -Apply **pdb_plug.yaml** - -```bash -kubectl apply -f pdb_plug.yaml -[...] -sh checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE= -ACTION= -MSG=Waiting for PDB to be plugged -[...] -sh checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE=READ WRITE -ACTION=PLUG -MSG=Success -``` - -Check kubernets log files and the database alert log - -```text -/usr/bin/kubectl logs -f pod/`/usr/bin/kubectl get pods -n oracle-database-operator-system|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n oracle-database-operator-system - -2023-01-03T14:33:51Z INFO pdb-webhook ValidateCreate-Validating PDB spec for : pdb1 -2023-01-03T14:33:51Z INFO pdb-webhook validateCommon {"name": "pdb1"} -2023-01-03T14:33:51Z INFO pdb-webhook Valdiating PDB Resource Action : PLUG -2023-01-03T14:33:51Z INFO pdb-webhook PDB Resource : pdb1 successfully validated for Action : PLUG -2023-01-03T14:33:51Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "fccac7ba-7540-42ff-93b2-46675506a098", "allowed": true} -2023-01-03T14:34:16Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "UID": "766dadcc-aeea-4a80-bc17-e957b4a44d3c", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2023-01-03T14:34:16Z INFO pdb-webhook Setting default values in PDB spec for : pdb1 -2023-01-03T14:34:16Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "766dadcc-aeea-4a80-bc17-e957b4a44d3c", "allowed": true} - -[database alert log] -... -All grantable enqueues granted -freeing rdom 3 -freeing the fusion rht of pdb 3 -freeing the pdb enqueue rht -Domain Action Reconfiguration complete (total time 0.0 secs) -Completed: CREATE PLUGGABLE DATABASE "pdbdev" - USING '/tmp/pdbunplug.xml' - SOURCE_FILE_NAME_CONVERT=NONE - MOVE - FILE_NAME_CONVERT=NONE - STORAGE UNLIMITED TEMPFILE REUSE - -2023-01-03T14:35:41.500186+00:00 -ALTER PLUGGABLE DATABASE "pdbdev" OPEN READ WRITE INSTANCES=ALL -2023-01-03T14:35:41.503482+00:00 -PDBDEV(3):Pluggable database PDBDEV opening in read write -PDBDEV(3):SUPLOG: Initialize PDB SUPLOG SGA, old value 0x0, new value 0x18 -PDBDEV(3):Autotune of undo retention is turned on -... -``` -### CLONE PDB - -Prepare and apply a new yaml file **pdb_clone.yaml** to clone the existing pluggable database. - -```yaml -#pdb_clone.yaml -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdb2-clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - action: "Clone" - [secret section] - -``` -```bash -kubectl apply -f pdb_clone.yaml -pdb.database.oracle.com/pdb2 created -[oracle@mitk01 https.ords.22]$ sh checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE=READ WRITE -ACTION=PLUG -MSG=Success -================================================================== -CDB=cdb-dev -K8SNAME=pdb2 -PDBNAME=pdb2-clone -OPENMODE= -ACTION= -MSG=Waiting for PDB to be cloned -[...] -[.wait sometimes..] - sh checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE=READ WRITE -ACTION=PLUG -MSG=Success -================================================================== -CDB=cdb-dev -K8SNAME=pdb2 -PDBNAME=pdb2-clone -OPENMODE=READ WRITE -ACTION=CLONE -MSG=Success -``` -log info - -```text -[kubernets log] -2023-01-03T15:13:31Z INFO pdb-webhook - asClone : false -2023-01-03T15:13:31Z INFO pdb-webhook - getScript : false -2023-01-03T15:13:31Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/mutate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "7c17a715-7e4e-47d4-ad42-dcb37526bb3e", "allowed": true} -2023-01-03T15:13:31Z DEBUG controller-runtime.webhook.webhooks received request {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "UID": "11e0d49c-afaa-47ac-a301-f1fdd1e70173", "kind": "database.oracle.com/v1alpha1, Kind=PDB", "resource": {"group":"database.oracle.com","version":"v1alpha1","resource":"pdbs"}} -2023-01-03T15:13:31Z INFO pdb-webhook ValidateCreate-Validating PDB spec for : pdb2 -2023-01-03T15:13:31Z INFO pdb-webhook validateCommon {"name": "pdb2"} -2023-01-03T15:13:31Z INFO pdb-webhook Valdiating PDB Resource Action : CLONE -2023-01-03T15:13:31Z INFO pdb-webhook PDB Resource : pdb2 successfully validated for Action : CLONE -2023-01-03T15:13:31Z DEBUG controller-runtime.webhook.webhooks wrote response {"webhook": "/validate-database-oracle-com-v1alpha1-pdb", "code": 200, "reason": "", "UID": "11e0d49c-afaa-47ac-a301-f1fdd1e70173", "allowed": true} - -[database alert log] -Domain Action Reconfiguration complete (total time 0.0 secs) -2023-01-03T15:15:00.670436+00:00 -Completed: CREATE PLUGGABLE DATABASE "pdb2-clone" FROM "pdbdev" - STORAGE UNLIMITED - TEMPFILE REUSE - FILE_NAME_CONVERT=NONE -ALTER PLUGGABLE DATABASE "pdbdev" CLOSE IMMEDIATE INSTANCES=ALL -2023-01-03T15:15:00.684271+00:00 -PDBDEV(3):Pluggable database PDBDEV closing -PDBDEV(3):JIT: pid 8235 requesting stop -PDBDEV(3):Buffer Cache flush started: 3 -PDBDEV(3):Buffer Cache flush finished: 3 - -``` -### UNPLUG AND PLUG WITH TDE - - - - -> ⚠ __WARNING FOR THE TDE USERS__ ⚠ According to the [ords documentation](https://docs.oracle.com/en/database/oracle/oracle-database/21/dbrst/op-database-pdbs-pdb_name-post.html) the plug and unplug operation with tde is supported only if ords runs on the same host of the database which is not the case of operator where ords runs on an isolated pods. Do not use pdb controller for unplug and plug operation with tde in production environments. - - - -You can use unplug and plug database with TDE; in order to do that you have to specify a key store path and create new kubernets secret for TDE using the following yaml file. **tde_secrete.yaml**. - -```yaml -#tde_secret -apiVersion: v1 -kind: Secret -metadata: - name: tde1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - tdepassword: "...." - tdesecret: "...." -``` - -```bash -kubectl apply -f tde_secret.yaml -``` - -The file to unplug and plug database with TDE are the following - - -```yaml -#pdb_unplugtde.yaml -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: - key: "sysadmin_user" - adminPwd: - secret: - secretName: pdb1-secret - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - tdePassword: - secret: - secretName: "tde1-secret" - key: "tdepassword" - tdeSecret: - secret: - secretName: "tde1-secret" - key: "tdesecret" - totalSize: 1G - tempSize: 1G - unlimitedStorage: true - reuseTempFile: true - fileNameConversions: NONE - action: "Unplug" - xmlFileName: "/home/oracle/unplugpdb.xml" - tdeExport: true -``` - -```yaml -#pdb_plugtde.ymal -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: pdb1-secret - key: "sysadmin_user" - adminPwd: - secret: - secretName: pdb1-secret - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - tdePassword: - secret: - secretName: "tde1-secret" - key: "tdepassword" - tdeSecret: - secret: - secretName: "tde1-secret" - key: "tdesecret" - totalSize: 1G - tempSize: "100M" - unlimitedStorage: true - reuseTempFile: true - fileNameConversions: NONE - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - action: "Plug" - xmlFileName: /home/oracle/unplugpdb.xml - tdeImport: true - tdeKeystorePath: /home/oracle/keystore - -``` - - - - - - diff --git a/docs/multitenant/ords-based/usecase02/pdb_clone.yaml b/docs/multitenant/ords-based/usecase02/pdb_clone.yaml deleted file mode 100644 index 5723f7c6..00000000 --- a/docs/multitenant/ords-based/usecase02/pdb_clone.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/pdb_plug.yaml b/docs/multitenant/ords-based/usecase02/pdb_plug.yaml deleted file mode 100644 index 9eb5ed77..00000000 --- a/docs/multitenant/ords-based/usecase02/pdb_plug.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/pdb_plugtde.yaml b/docs/multitenant/ords-based/usecase02/pdb_plugtde.yaml deleted file mode 100644 index 995be538..00000000 --- a/docs/multitenant/ords-based/usecase02/pdb_plugtde.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# - -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: pdb1-secret - key: "sysadmin_user" - adminPwd: - secret: - secretName: pdb1-secret - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - tdePassword: - secret: - secretName: "tde1-secret" - key: "tdepassword" - tdeSecret: - secret: - secretName: "tde1-secret" - key: "tdesecret" - totalSize: 1G - tempSize: "100M" - unlimitedStorage: true - reuseTempFile: true - fileNameConversions: NONE - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - action: "Plug" - xmlFileName: /home/oracle/unplugpdb.xml - tdeImport: true - tdeKeystorePath: /home/oracle/keystore - diff --git a/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml b/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml deleted file mode 100644 index 0036d5f7..00000000 --- a/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/pdb_unplugtde.yaml b/docs/multitenant/ords-based/usecase02/pdb_unplugtde.yaml deleted file mode 100644 index 2eacc5b7..00000000 --- a/docs/multitenant/ords-based/usecase02/pdb_unplugtde.yaml +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# - -apiVersion: database.oracle.com/v4 -Kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: pdb1-secret - key: "sysadmin_user" - adminPwd: - secret: - secretName: pdb1-secret - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - tdePassword: - secret: - secretName: "tde1-secret" - key: "tdepassword" - tdeSecret: - secret: - secretName: "tde1-secret" - key: "tdesecret" - totalSize: 1G - tempSize: 1G - unlimitedStorage: true - reuseTempFile: true - fileNameConversions: NONE - action: "Unplug" - xmlFileName: "/home/oracle/unplugpdb.xml" - tdeExport: true - tdeKeystorePath: "/home/oracle/keystore" - diff --git a/docs/multitenant/provisioning/ords_image.md b/docs/multitenant/provisioning/ords_image.md deleted file mode 100644 index e2d1dcef..00000000 --- a/docs/multitenant/provisioning/ords_image.md +++ /dev/null @@ -1,81 +0,0 @@ - - -# Build ORDS Docker Image - -This file contains the steps to create an ORDS based image to be used solely by the PDB life cycle multitentant controllers. - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -#### Clone the software using git: - -> Under directory ./oracle-database-operator/ords you will find the [Dockerfile](../../../ords/Dockerfile) and [runOrdsSSL.sh](../../../ords/runOrdsSSL.sh) required to build the image. - -```sh - git clone git@orahub.oci.oraclecorp.com:rac-docker-dev/oracle-database-operator.git - cd oracle-database-operator/ords/ -``` - -#### Login to the registry: container-registry.oracle.com - -**NOTE:** To login to this registry, you will need to the URL https://container-registry.oracle.com , Sign in, then click on "Java" and then accept the agreement. - -```bash -docker login container-registry.oracle.com -``` - -#### Login to the your container registry - -Login to a repo where you want to push your docker image (if needed) to pull during deployment in your environment. - -```bash -docker login -``` - -#### Build the image - -Build the docker image by using below command: - -```bash -docker build -t oracle/ords-dboper:latest . -``` -> If your are working behind a proxy mind to specify https_proxy and http_proxy during image creation - -Check the docker image details using: - -```bash -docker images -``` - -> OUTPUT EXAMPLE -```bash -REPOSITORY TAG IMAGE ID CREATED SIZE -oracle/ords-dboper latest fdb17aa242f8 4 hours ago 1.46GB - -``` - -#### Tag and push the image - -Tag and push the image to your image repository. - -NOTE: We have the repo as `phx.ocir.io//oracle/ords:latest`. Please change as per your environment. - -```bash -docker tag oracle/ords-dboper:ords-latest phx.ocir.io//oracle/ords:latest -docker push phx.ocir.io//oracle/ords:latest -``` - -#### In case of private image - -If you the image not be public then yuo need to create a secret containing the password of your image repository. -Create a Kubernetes Secret for your docker repository to pull the image during deployment using the below command: - -```bash -kubectl create secret generic container-registry-secret --from-file=.dockerconfigjson=./.docker/config.json --type=kubernetes.io/dockerconfigjson -n oracle-database-operator-system -``` - -Use the parameter `ordsImagePullSecret` to specify the container secrets in pod creation yaml file - -#### [Image createion example](../usecase01/logfiles/BuildImage.log) - - - diff --git a/docs/multitenant/lrest-based/usecase/README.md b/docs/multitenant/usecase/README.md similarity index 100% rename from docs/multitenant/lrest-based/usecase/README.md rename to docs/multitenant/usecase/README.md diff --git a/docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml b/docs/multitenant/usecase/altersystem_pdb1_resource.yaml similarity index 97% rename from docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml rename to docs/multitenant/usecase/altersystem_pdb1_resource.yaml index 0467a948..7e4deaa5 100644 --- a/docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml +++ b/docs/multitenant/usecase/altersystem_pdb1_resource.yaml @@ -10,12 +10,9 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbdev" - action: "Alter" alterSystemParameter : "cpu_count" alterSystemValue : "3" parameterScope : "memory" - - adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml b/docs/multitenant/usecase/cdbnamespace_binding.yaml similarity index 100% rename from docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml rename to docs/multitenant/usecase/cdbnamespace_binding.yaml diff --git a/docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml b/docs/multitenant/usecase/clone_pdb1_resource.yaml similarity index 95% rename from docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml rename to docs/multitenant/usecase/clone_pdb1_resource.yaml index 2c4afe13..dfeac7ab 100644 --- a/docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml +++ b/docs/multitenant/usecase/clone_pdb1_resource.yaml @@ -15,8 +15,7 @@ spec: totalSize: "UNLIMITED" tempSize: "UNLIMITED" pdbconfigmap: "config-map-pdb" - assertiveLrpdbDeletion: true - action: "Clone" + imperativeLrpdbDeletion: true adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml b/docs/multitenant/usecase/clone_pdb2_resource.yaml similarity index 95% rename from docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml rename to docs/multitenant/usecase/clone_pdb2_resource.yaml index 16255a87..aeabc9d0 100644 --- a/docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml +++ b/docs/multitenant/usecase/clone_pdb2_resource.yaml @@ -15,8 +15,7 @@ spec: totalSize: "UNLIMITED" tempSize: "UNLIMITED" pdbconfigmap: "config-map-pdb" - assertiveLrpdbDeletion: true - action: "Clone" + imperativeLrpdbDeletion: true adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml b/docs/multitenant/usecase/close_pdb1_resource.yaml similarity index 97% rename from docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml rename to docs/multitenant/usecase/close_pdb1_resource.yaml index 87f7383d..4f5c9cb4 100644 --- a/docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml +++ b/docs/multitenant/usecase/close_pdb1_resource.yaml @@ -12,7 +12,6 @@ spec: pdbName: "pdbdev" pdbState: "CLOSE" modifyOption: "IMMEDIATE" - action: "Modify" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml b/docs/multitenant/usecase/close_pdb2_resource.yaml similarity index 97% rename from docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml rename to docs/multitenant/usecase/close_pdb2_resource.yaml index 0743bd8c..c37c8184 100644 --- a/docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml +++ b/docs/multitenant/usecase/close_pdb2_resource.yaml @@ -12,7 +12,6 @@ spec: pdbName: "pdbprd" pdbState: "CLOSE" modifyOption: "IMMEDIATE" - action: "Modify" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml b/docs/multitenant/usecase/close_pdb3_resource.yaml similarity index 97% rename from docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml rename to docs/multitenant/usecase/close_pdb3_resource.yaml index 6ba34a7b..6b201d95 100644 --- a/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml +++ b/docs/multitenant/usecase/close_pdb3_resource.yaml @@ -12,7 +12,6 @@ spec: pdbName: "new_clone" pdbState: "CLOSE" modifyOption: "IMMEDIATE" - action: "Modify" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/config_map_pdb.yaml b/docs/multitenant/usecase/config_map_pdb.yaml similarity index 100% rename from docs/multitenant/lrest-based/usecase/config_map_pdb.yaml rename to docs/multitenant/usecase/config_map_pdb.yaml diff --git a/docs/multitenant/usecase/config_map_plsql.yaml b/docs/multitenant/usecase/config_map_plsql.yaml new file mode 100644 index 00000000..d7b8000c --- /dev/null +++ b/docs/multitenant/usecase/config_map_plsql.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: + namespace: pdbnamespace +data: + plblock1.sql: | + create user k8stestuser identified by tkstestuser + plblock2.sql: | + grant dba to k8stestuser + plblock3.sql: | + create table k8stestuser.rndnum (c1 number ,c2 number) + plblock4.sql: | + alter session set events '942 trace name errorstack level 3' + plblock5.sql: | + create or replace procedure k8stestuser.gennum + as + begin + for cnt in 1..100 + loop + insert into k8stestuser.rndnum values (dbms_random.value(1,100), + dbms_random.value(1,100)); + end loop; + commit; + end; + plblock6.sql: | + begin + k8stestuser.gennum; + end; diff --git a/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml b/docs/multitenant/usecase/create_lrest_pod.yaml similarity index 86% rename from docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml rename to docs/multitenant/usecase/create_lrest_pod.yaml index b80c1c56..323ce053 100644 --- a/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml +++ b/docs/multitenant/usecase/create_lrest_pod.yaml @@ -10,6 +10,11 @@ spec: dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" replicas: 1 deletePdbCascade: true + autodiscover: + namespaceAutoDiscover: pdbnamespace + clusterIp: false + loadBalancer: false + trace_level_client : 16 cdbAdminUser: secret: secretName: "dbuser" @@ -34,6 +39,10 @@ spec: secret: secretName: "db-tls" key: "tls.crt" + cdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" cdbPubKey: secret: secretName: "pubkey" diff --git a/docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml b/docs/multitenant/usecase/create_pdb1_resource.yaml similarity index 95% rename from docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml rename to docs/multitenant/usecase/create_pdb1_resource.yaml index fa58d36a..1ce6476c 100644 --- a/docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml +++ b/docs/multitenant/usecase/create_pdb1_resource.yaml @@ -10,14 +10,13 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbdev" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true fileNameConversions: "NONE" unlimitedStorage: false pdbconfigmap: "config-map-pdb" tdeImport: false totalSize: "2G" tempSize: "800M" - action: "Create" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml b/docs/multitenant/usecase/create_pdb2_resource.yaml similarity index 95% rename from docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml rename to docs/multitenant/usecase/create_pdb2_resource.yaml index 02d5763b..a966de91 100644 --- a/docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml +++ b/docs/multitenant/usecase/create_pdb2_resource.yaml @@ -10,14 +10,13 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbprd" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true fileNameConversions: "NONE" unlimitedStorage: false pdbconfigmap: "config-map-pdb" tdeImport: false totalSize: "2G" tempSize: "800M" - action: "Create" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml b/docs/multitenant/usecase/delete_pdb1_resource.yaml similarity index 97% rename from docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml rename to docs/multitenant/usecase/delete_pdb1_resource.yaml index 1a3c328a..2831dd29 100644 --- a/docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml +++ b/docs/multitenant/usecase/delete_pdb1_resource.yaml @@ -9,7 +9,7 @@ spec: cdbResName: "cdb-dev" cdbNamespace: "cdbnamespace" pdbName: "pdbdev" - action: "Delete" + pdbState: "DELETE" dropAction: "INCLUDING" adminpdbUser: secret: diff --git a/docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml b/docs/multitenant/usecase/delete_pdb2_resource.yaml similarity index 97% rename from docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml rename to docs/multitenant/usecase/delete_pdb2_resource.yaml index 747641d4..8924645e 100644 --- a/docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml +++ b/docs/multitenant/usecase/delete_pdb2_resource.yaml @@ -9,7 +9,7 @@ spec: cdbResName: "cdb-dev" cdbNamespace: "cdbnamespace" pdbName: "pdbprd" - action: "Delete" + pdbState: "DELETE" dropAction: "INCLUDING" adminpdbUser: secret: diff --git a/docs/multitenant/usecase/delete_pdb3_resource.yaml b/docs/multitenant/usecase/delete_pdb3_resource.yaml new file mode 100644 index 00000000..1261245b --- /dev/null +++ b/docs/multitenant/usecase/delete_pdb3_resource.yaml @@ -0,0 +1,45 @@ +apiVersion: database.oracle.com/v4 +kind: LRPDB +metadata: + name: pdb3 + namespace: pdbnamespace + labels: + cdb: cdb-dev +spec: + cdbResName: "cdb-dev" + cdbNamespace: "cdbnamespace" + pdbName: "new_clone" + pdbState: "DELETE" + dropAction: "INCLUDING" + adminpdbUser: + secret: + secretName: "pdbusr" + key: "e_pdbusr.txt" + adminpdbPass: + secret: + secretName: "pdbpwd" + key: "e_pdbpwd.txt" + lrpdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + lrpdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + lrpdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + webServerUser: + secret: + secretName: "wbuser" + key: "e_wbuser.txt" + webServerPwd: + secret: + secretName: "wbpass" + key: "e_wbpass.txt" + cdbPrvKey: + secret: + secretName: "prvkey" + key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/makefile b/docs/multitenant/usecase/makefile similarity index 96% rename from docs/multitenant/lrest-based/usecase/makefile rename to docs/multitenant/usecase/makefile index 5a74c669..44b71cf0 100644 --- a/docs/multitenant/lrest-based/usecase/makefile +++ b/docs/multitenant/usecase/makefile @@ -77,6 +77,7 @@ export OPENSHIFT=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPENSHIFT|cut -d : - export PLSQLMAP=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PLSQLMAP|cut -d : -f 2) export OPRNAMESPACE=oracle-database-operator-system export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml +export AUTODISCOVER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep AUTODISCOVER|cut -d : -f 2) export TEST_EXEC_TIMEOUT=3m REST_SERVER=lrest @@ -169,6 +170,7 @@ check: @printf "SERVICENAMEACCOUNT.....:%s\n" $(SERVICENAM) @printf "APIVERSION.............:%s\n" $(APIVERSION) @printf "PLSQLMAP...............:%s\n" $(PLSQLMAP) + @printf "AUTODISCOVER...........:%s\n" $(AUTODISCOVER) define msg @printf "\033[31;7m%s\033[0m\r" "......................................]" @@ -353,7 +355,7 @@ spec: dbTnsurl : ${TNSALIAS} replicas: 1 deletePdbCascade: true - autodiscover: true + autodiscover: ${AUTODISCOVER} namespaceAutoDiscover: ${PDBNAMESPACE} clusterIp: false loadBalancer: false @@ -643,6 +645,7 @@ spec: cdbResName: "cdb-dev" cdbNamespace: "${LRSNAMESPACE}" pdbName: "new_clone" + pdbState: "DELETE" dropAction: "INCLUDING" EOF @@ -964,7 +967,7 @@ reloadop: $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - -dump: +dumpoperator: @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) @$(eval DIAGFILE := ./opdmp.$(TMPSP)) @>$(DIAGFILE) @@ -974,6 +977,13 @@ dump: $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) +dumplrest: + @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) + @$(eval DIAGFILE := ./lrestdump.$(TMPSP)) + @>$(DIAGFILE) + $(KUBECTL) logs `$(KUBECTL) get pods -o custom-columns=:metadata.name -n $(LRSNAMESPACE) --no-headers ` -n $(LRSNAMESPACE) |strings > $(DIAGFILE) + + listimage: $(KUBECTL) get pods --all-namespaces -o jsonpath="{.items[*].spec['initContainers', 'containers'][*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c @@ -990,14 +1000,25 @@ CLOSEMSG="close:[op. completed]" CLONEMSG="clone:[op. completed]" PLUGMSG="plug:[op. completed]" -run00: - @$(call msg,"lrest pod creation") +cleanlrest: + @$(call msg,"lrest deletion") - $(KUBECTL) delete lrest cdb-dev -n $(LRSNAMESPACE) + - $(KUBECTL) delete replicaset.apps/cdb-dev-lrest-rs -n $(LRSNAMESPACE) + $(KUBECTL) wait --for=delete lrest cdb-dev -n $(LRSNAMESPACE) --timeout=10m + $(KUBECTL) wait --for=delete pod/* -n $(LRSNAMESPACE) --timeout=10m + +toplrest: + $(KUBECTL) top pod `$(KUBECTL) get pods -o custom-columns=:metadata.name -n $(LRSNAMESPACE) --no-headers ` -n $(LRSNAMESPACE) + + +run00: cleanlrest + @$(call msg,"lrest pod creation") $(KUBECTL) apply -f $(LREST_POD) $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) @$(call msg,"lrest pod completed") $(KUBECTL) get lrest -n $(LRSNAMESPACE) $(KUBECTL) get pod -n $(LRSNAMESPACE) + $(KUBECTL) logs `$(KUBECTL) get pods -o custom-columns=:metadata.name -n $(LRSNAMESPACE) --no-headers ` -n $(LRSNAMESPACE) run01.1: @$(call msg,"lrpdb pdb1 creation") @@ -1201,15 +1222,14 @@ pdbsize: # openpdb2rs - pdb2 open restrict - imperative # -runall01: tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run03.1 run03.2 run04.1 run04.2 run05.1 run06.1 run07.1 -runall02: tkapplyinit run00 run01.1 openpdb1 -runall03: tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 openpdb1rs +runall01: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run03.1 run03.2 run04.1 run04.2 run05.1 run06.1 run07.1 +runall02: genyaml tkapplyinit run00 run01.1 openpdb1 +runall03: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 openpdb1rs $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb1 $(MAKE) -f $(MAKEFILE) open LRPDBNAME=pdb1 $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb1 $(MAKE) -f $(MAKEFILE) run05.1 run06.1 - -runall04: tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 run05.1 run06.1 closepdb1 +runall04: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 run05.1 run06.1 closepdb1 diff --git a/docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml b/docs/multitenant/usecase/map_pdb1_resource.yaml similarity index 95% rename from docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml rename to docs/multitenant/usecase/map_pdb1_resource.yaml index 2cd57b87..177b6915 100644 --- a/docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml +++ b/docs/multitenant/usecase/map_pdb1_resource.yaml @@ -10,11 +10,10 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbdev" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true fileNameConversions: "NONE" totalSize: "1G" tempSize: "100M" - action: "Map" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml b/docs/multitenant/usecase/map_pdb2_resource.yaml similarity index 95% rename from docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml rename to docs/multitenant/usecase/map_pdb2_resource.yaml index bab614cf..3e8028e7 100644 --- a/docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml +++ b/docs/multitenant/usecase/map_pdb2_resource.yaml @@ -10,11 +10,10 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbprd" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true fileNameConversions: "NONE" totalSize: "1G" tempSize: "100M" - action: "Map" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml b/docs/multitenant/usecase/map_pdb3_resource.yaml similarity index 95% rename from docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml rename to docs/multitenant/usecase/map_pdb3_resource.yaml index 7bbae48d..df69929a 100644 --- a/docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml +++ b/docs/multitenant/usecase/map_pdb3_resource.yaml @@ -10,11 +10,10 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "new_clone" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true fileNameConversions: "NONE" totalSize: "1G" tempSize: "100M" - action: "Map" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml b/docs/multitenant/usecase/open_pdb1_resource.yaml similarity index 97% rename from docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml rename to docs/multitenant/usecase/open_pdb1_resource.yaml index a845a0bd..8b6dac1f 100644 --- a/docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml +++ b/docs/multitenant/usecase/open_pdb1_resource.yaml @@ -10,7 +10,6 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbdev" - action: "Modify" pdbState: "OPEN" modifyOption: "READ WRITE" adminpdbUser: diff --git a/docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml b/docs/multitenant/usecase/open_pdb2_resource.yaml similarity index 97% rename from docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml rename to docs/multitenant/usecase/open_pdb2_resource.yaml index 9356184f..c2d24d50 100644 --- a/docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml +++ b/docs/multitenant/usecase/open_pdb2_resource.yaml @@ -10,7 +10,6 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbprd" - action: "Modify" pdbState: "OPEN" modifyOption: "READ WRITE" adminpdbUser: diff --git a/docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml b/docs/multitenant/usecase/open_pdb3_resource.yaml similarity index 97% rename from docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml rename to docs/multitenant/usecase/open_pdb3_resource.yaml index 1b8024ba..393a7a6f 100644 --- a/docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml +++ b/docs/multitenant/usecase/open_pdb3_resource.yaml @@ -10,7 +10,6 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "new_clone" - action: "Modify" pdbState: "OPEN" modifyOption: "READ WRITE" adminpdbUser: diff --git a/docs/multitenant/lrest-based/usecase/parameters.txt b/docs/multitenant/usecase/parameters.txt similarity index 91% rename from docs/multitenant/lrest-based/usecase/parameters.txt rename to docs/multitenant/usecase/parameters.txt index 1f21ed38..aa823346 100644 --- a/docs/multitenant/lrest-based/usecase/parameters.txt +++ b/docs/multitenant/usecase/parameters.txt @@ -14,22 +14,22 @@ TNSALIAS:"(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRAN ## CDB USER FOR PDB LIFECYCLE MANAGMENT ### ########################################### -DBUSER:restdba -DBPASS:CLWKO655321 +DBUSER:_username_ +DBPASS:_password_ ####################### ## HTTPS CREDENTIAL ### ####################### -WBUSER:welcome -WBPASS:welcome1 +WBUSER:_username_ +WBPASS:_password_ ##################### ## PDB ADMIN USER ### ##################### -PDBUSR:Citizenkane -PDBPWD:Rosebud +PDBUSR:_username_ +PDBPWD:_password_ ################### ### NAMESPACES #### diff --git a/docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml b/docs/multitenant/usecase/pdbnamespace_binding.yaml similarity index 100% rename from docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml rename to docs/multitenant/usecase/pdbnamespace_binding.yaml diff --git a/docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml b/docs/multitenant/usecase/plug_pdb1_resource.yaml similarity index 91% rename from docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml rename to docs/multitenant/usecase/plug_pdb1_resource.yaml index d7d310db..c8b8657e 100644 --- a/docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml +++ b/docs/multitenant/usecase/plug_pdb1_resource.yaml @@ -10,16 +10,15 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" + pdbState: "PLUG" + xmlFileName: "/var/tmp/pdb.4165325.xml" fileNameConversions: "NONE" sourceFileNameConversions: "NONE" copyAction: "MOVE" totalSize: "1G" tempSize: "100M" - assertiveLrpdbDeletion: true + imperativeLrpdbDeletion: true pdbconfigmap: "config-map-pdb" - action: "Plug" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/usecase/security_context.yaml b/docs/multitenant/usecase/security_context.yaml new file mode 100644 index 00000000..61d7637d --- /dev/null +++ b/docs/multitenant/usecase/security_context.yaml @@ -0,0 +1,93 @@ +# +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- + +# Create a Security Context Contraint +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + name: lrest-scc +allowPrivilegedContainer: false +allowedCapabilities: + - SYS_NICE +runAsUser: + type: MustRunAs + uid: 54321 +seLinuxContext: + type: RunAsAny +fsGroup: + type: MustRunAs + ranges: + - min: 54321 + max: 54321 +supplementalGroups: + type: MustRunAs + ranges: + - min: 54321 + max: 54321 +--- + +# Create a Security Context Contraint +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + name: lrest-root-user-scc +allowPrivilegedContainer: false +allowedCapabilities: + - SYS_NICE +runAsUser: + type: MustRunAsRange + uidRangeMin: 0 + uidRangeMax: 54321 +seLinuxContext: + type: RunAsAny +fsGroup: + type: MustRunAs + ranges: + - min: 0 + max: 54325 +supplementalGroups: + type: MustRunAs + ranges: + - min: 0 + max: 54321 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: lrest-sa + namespace: cdbnamespace +--- + +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: use-lrest-scc + namespace: cdbnamespace +rules: + - apiGroups: + - security.openshift.io + verbs: + - use + resources: + - securitycontextconstraints + resourceNames: + - lrest-scc + - lrest-root-user-scc +--- + +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: use-lrest-scc + namespace: cdbnamespace +subjects: + - kind: ServiceAccount + name: lrest-sa + namespace: cdbnamespace +roleRef: + kind: Role + name: use-lrest-scc + apiGroup: rbac.authorization.k8s.io diff --git a/docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml b/docs/multitenant/usecase/unplug_pdb1_resource.yaml similarity index 93% rename from docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml rename to docs/multitenant/usecase/unplug_pdb1_resource.yaml index a5da5a57..4bc2a96e 100644 --- a/docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml +++ b/docs/multitenant/usecase/unplug_pdb1_resource.yaml @@ -10,8 +10,8 @@ spec: cdbNamespace: "cdbnamespace" cdbName: "DB12" pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" + pdbState: "UNPLUG" + xmlFileName: "/var/tmp/pdb.4165325.xml" adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/usecase01/logfiles/BuildImage.log b/docs/multitenant/usecase01/logfiles/BuildImage.log deleted file mode 100644 index f35c66d8..00000000 --- a/docs/multitenant/usecase01/logfiles/BuildImage.log +++ /dev/null @@ -1,896 +0,0 @@ -/usr/bin/docker build -t oracle/ords-dboper:latest ../../../ords -Sending build context to Docker daemon 13.82kB -Step 1/12 : FROM container-registry.oracle.com/java/jdk:latest - ---> b8457e2f0b73 -Step 2/12 : ENV ORDS_HOME=/opt/oracle/ords/ RUN_FILE="runOrdsSSL.sh" ORDSVERSION=23.4.0-8 - ---> Using cache - ---> 3317a16cd6f8 -Step 3/12 : COPY $RUN_FILE $ORDS_HOME - ---> 7995edec33cc -Step 4/12 : RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && yum -y install java-11-openjdk-devel && yum -y install iproute && yum clean all - ---> Running in fe168b01f3ad -Oracle Linux 8 BaseOS Latest (x86_64) 91 MB/s | 79 MB 00:00 -Oracle Linux 8 Application Stream (x86_64) 69 MB/s | 62 MB 00:00 -Last metadata expiration check: 0:00:12 ago on Tue 20 Aug 2024 08:54:50 AM UTC. -Package yum-utils-4.0.21-23.0.1.el8.noarch is already installed. -Package tar-2:1.30-9.el8.x86_64 is already installed. -Package vim-minimal-2:8.0.1763-19.0.1.el8_6.4.x86_64 is already installed. -Package procps-ng-3.3.15-14.0.1.el8.x86_64 is already installed. -Package curl-7.61.1-33.el8_9.5.x86_64 is already installed. -Dependencies resolved. -================================================================================ - Package Arch Version Repository Size -================================================================================ -Installing: - bind-utils x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 453 k - expect x86_64 5.45.4-5.el8 ol8_baseos_latest 266 k - hostname x86_64 3.20-6.el8 ol8_baseos_latest 32 k - lsof x86_64 4.93.2-1.el8 ol8_baseos_latest 253 k - net-tools x86_64 2.0-0.52.20160912git.el8 ol8_baseos_latest 322 k - openssl x86_64 1:1.1.1k-12.el8_9 ol8_baseos_latest 710 k - sudo x86_64 1.9.5p2-1.el8_9 ol8_baseos_latest 1.0 M - tree x86_64 1.7.0-15.el8 ol8_baseos_latest 59 k - unzip x86_64 6.0-46.0.1.el8 ol8_baseos_latest 196 k - wget x86_64 1.19.5-12.0.1.el8_10 ol8_appstream 733 k - which x86_64 2.21-20.el8 ol8_baseos_latest 50 k - zip x86_64 3.0-23.el8 ol8_baseos_latest 270 k -Upgrading: - curl x86_64 7.61.1-34.el8 ol8_baseos_latest 352 k - dnf-plugins-core noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 76 k - libcurl x86_64 7.61.1-34.el8 ol8_baseos_latest 303 k - python3-dnf-plugins-core - noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 263 k - yum-utils noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 75 k -Installing dependencies: - bind-libs x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 176 k - bind-libs-lite x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 1.2 M - bind-license noarch 32:9.11.36-16.el8_10.2 ol8_appstream 104 k - fstrm x86_64 0.6.1-3.el8 ol8_appstream 29 k - libmaxminddb x86_64 1.2.0-10.el8_9.1 ol8_appstream 32 k - libmetalink x86_64 0.1.3-7.el8 ol8_baseos_latest 32 k - protobuf-c x86_64 1.3.0-8.el8 ol8_appstream 37 k - python3-bind noarch 32:9.11.36-16.el8_10.2 ol8_appstream 151 k - python3-ply noarch 3.9-9.el8 ol8_baseos_latest 111 k - tcl x86_64 1:8.6.8-2.el8 ol8_baseos_latest 1.1 M -Installing weak dependencies: - geolite2-city noarch 20180605-1.el8 ol8_appstream 19 M - geolite2-country noarch 20180605-1.el8 ol8_appstream 1.0 M - -Transaction Summary -================================================================================ -Install 24 Packages -Upgrade 5 Packages - -Total download size: 28 M -Downloading Packages: -(1/29): hostname-3.20-6.el8.x86_64.rpm 268 kB/s | 32 kB 00:00 -(2/29): libmetalink-0.1.3-7.el8.x86_64.rpm 257 kB/s | 32 kB 00:00 -(3/29): expect-5.45.4-5.el8.x86_64.rpm 1.4 MB/s | 266 kB 00:00 -(4/29): lsof-4.93.2-1.el8.x86_64.rpm 3.2 MB/s | 253 kB 00:00 -(5/29): net-tools-2.0-0.52.20160912git.el8.x86_ 3.6 MB/s | 322 kB 00:00 -(6/29): python3-ply-3.9-9.el8.noarch.rpm 2.7 MB/s | 111 kB 00:00 -(7/29): openssl-1.1.1k-12.el8_9.x86_64.rpm 10 MB/s | 710 kB 00:00 -(8/29): tree-1.7.0-15.el8.x86_64.rpm 2.2 MB/s | 59 kB 00:00 -(9/29): sudo-1.9.5p2-1.el8_9.x86_64.rpm 14 MB/s | 1.0 MB 00:00 -(10/29): unzip-6.0-46.0.1.el8.x86_64.rpm 6.8 MB/s | 196 kB 00:00 -(11/29): which-2.21-20.el8.x86_64.rpm 2.0 MB/s | 50 kB 00:00 -(12/29): tcl-8.6.8-2.el8.x86_64.rpm 13 MB/s | 1.1 MB 00:00 -(13/29): bind-libs-9.11.36-16.el8_10.2.x86_64.r 6.7 MB/s | 176 kB 00:00 -(14/29): zip-3.0-23.el8.x86_64.rpm 8.4 MB/s | 270 kB 00:00 -(15/29): bind-libs-lite-9.11.36-16.el8_10.2.x86 29 MB/s | 1.2 MB 00:00 -(16/29): bind-license-9.11.36-16.el8_10.2.noarc 3.3 MB/s | 104 kB 00:00 -(17/29): bind-utils-9.11.36-16.el8_10.2.x86_64. 13 MB/s | 453 kB 00:00 -(18/29): fstrm-0.6.1-3.el8.x86_64.rpm 1.2 MB/s | 29 kB 00:00 -(19/29): libmaxminddb-1.2.0-10.el8_9.1.x86_64.r 1.3 MB/s | 32 kB 00:00 -(20/29): geolite2-country-20180605-1.el8.noarch 17 MB/s | 1.0 MB 00:00 -(21/29): protobuf-c-1.3.0-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 -(22/29): python3-bind-9.11.36-16.el8_10.2.noarc 5.8 MB/s | 151 kB 00:00 -(23/29): wget-1.19.5-12.0.1.el8_10.x86_64.rpm 17 MB/s | 733 kB 00:00 -(24/29): curl-7.61.1-34.el8.x86_64.rpm 12 MB/s | 352 kB 00:00 -(25/29): dnf-plugins-core-4.0.21-25.0.1.el8.noa 2.4 MB/s | 76 kB 00:00 -(26/29): libcurl-7.61.1-34.el8.x86_64.rpm 8.6 MB/s | 303 kB 00:00 -(27/29): python3-dnf-plugins-core-4.0.21-25.0.1 9.8 MB/s | 263 kB 00:00 -(28/29): yum-utils-4.0.21-25.0.1.el8.noarch.rpm 3.0 MB/s | 75 kB 00:00 -(29/29): geolite2-city-20180605-1.el8.noarch.rp 66 MB/s | 19 MB 00:00 --------------------------------------------------------------------------------- -Total 43 MB/s | 28 MB 00:00 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Preparing : 1/1 - Running scriptlet: protobuf-c-1.3.0-8.el8.x86_64 1/1 - Installing : protobuf-c-1.3.0-8.el8.x86_64 1/34 - Installing : fstrm-0.6.1-3.el8.x86_64 2/34 - Installing : bind-license-32:9.11.36-16.el8_10.2.noarch 3/34 - Upgrading : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 4/34 - Upgrading : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 5/34 - Upgrading : libcurl-7.61.1-34.el8.x86_64 6/34 - Installing : geolite2-country-20180605-1.el8.noarch 7/34 - Installing : geolite2-city-20180605-1.el8.noarch 8/34 - Installing : libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 - Running scriptlet: libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 - Installing : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 10/34 - Installing : bind-libs-32:9.11.36-16.el8_10.2.x86_64 11/34 - Installing : unzip-6.0-46.0.1.el8.x86_64 12/34 - Installing : tcl-1:8.6.8-2.el8.x86_64 13/34 - Running scriptlet: tcl-1:8.6.8-2.el8.x86_64 13/34 - Installing : python3-ply-3.9-9.el8.noarch 14/34 - Installing : python3-bind-32:9.11.36-16.el8_10.2.noarch 15/34 - Installing : libmetalink-0.1.3-7.el8.x86_64 16/34 - Installing : wget-1.19.5-12.0.1.el8_10.x86_64 17/34 - Running scriptlet: wget-1.19.5-12.0.1.el8_10.x86_64 17/34 - Installing : bind-utils-32:9.11.36-16.el8_10.2.x86_64 18/34 - Installing : expect-5.45.4-5.el8.x86_64 19/34 - Installing : zip-3.0-23.el8.x86_64 20/34 - Upgrading : curl-7.61.1-34.el8.x86_64 21/34 - Upgrading : yum-utils-4.0.21-25.0.1.el8.noarch 22/34 - Installing : which-2.21-20.el8.x86_64 23/34 - Installing : tree-1.7.0-15.el8.x86_64 24/34 - Installing : sudo-1.9.5p2-1.el8_9.x86_64 25/34 - Running scriptlet: sudo-1.9.5p2-1.el8_9.x86_64 25/34 - Installing : openssl-1:1.1.1k-12.el8_9.x86_64 26/34 - Installing : net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 - Running scriptlet: net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 - Installing : lsof-4.93.2-1.el8.x86_64 28/34 - Installing : hostname-3.20-6.el8.x86_64 29/34 - Running scriptlet: hostname-3.20-6.el8.x86_64 29/34 - Cleanup : curl-7.61.1-33.el8_9.5.x86_64 30/34 - Cleanup : yum-utils-4.0.21-23.0.1.el8.noarch 31/34 - Cleanup : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 - Cleanup : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 33/34 - Cleanup : libcurl-7.61.1-33.el8_9.5.x86_64 34/34 - Running scriptlet: libcurl-7.61.1-33.el8_9.5.x86_64 34/34 - Verifying : expect-5.45.4-5.el8.x86_64 1/34 - Verifying : hostname-3.20-6.el8.x86_64 2/34 - Verifying : libmetalink-0.1.3-7.el8.x86_64 3/34 - Verifying : lsof-4.93.2-1.el8.x86_64 4/34 - Verifying : net-tools-2.0-0.52.20160912git.el8.x86_64 5/34 - Verifying : openssl-1:1.1.1k-12.el8_9.x86_64 6/34 - Verifying : python3-ply-3.9-9.el8.noarch 7/34 - Verifying : sudo-1.9.5p2-1.el8_9.x86_64 8/34 - Verifying : tcl-1:8.6.8-2.el8.x86_64 9/34 - Verifying : tree-1.7.0-15.el8.x86_64 10/34 - Verifying : unzip-6.0-46.0.1.el8.x86_64 11/34 - Verifying : which-2.21-20.el8.x86_64 12/34 - Verifying : zip-3.0-23.el8.x86_64 13/34 - Verifying : bind-libs-32:9.11.36-16.el8_10.2.x86_64 14/34 - Verifying : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 15/34 - Verifying : bind-license-32:9.11.36-16.el8_10.2.noarch 16/34 - Verifying : bind-utils-32:9.11.36-16.el8_10.2.x86_64 17/34 - Verifying : fstrm-0.6.1-3.el8.x86_64 18/34 - Verifying : geolite2-city-20180605-1.el8.noarch 19/34 - Verifying : geolite2-country-20180605-1.el8.noarch 20/34 - Verifying : libmaxminddb-1.2.0-10.el8_9.1.x86_64 21/34 - Verifying : protobuf-c-1.3.0-8.el8.x86_64 22/34 - Verifying : python3-bind-32:9.11.36-16.el8_10.2.noarch 23/34 - Verifying : wget-1.19.5-12.0.1.el8_10.x86_64 24/34 - Verifying : curl-7.61.1-34.el8.x86_64 25/34 - Verifying : curl-7.61.1-33.el8_9.5.x86_64 26/34 - Verifying : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 27/34 - Verifying : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 28/34 - Verifying : libcurl-7.61.1-34.el8.x86_64 29/34 - Verifying : libcurl-7.61.1-33.el8_9.5.x86_64 30/34 - Verifying : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 31/34 - Verifying : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 - Verifying : yum-utils-4.0.21-25.0.1.el8.noarch 33/34 - Verifying : yum-utils-4.0.21-23.0.1.el8.noarch 34/34 - -Upgraded: - curl-7.61.1-34.el8.x86_64 - dnf-plugins-core-4.0.21-25.0.1.el8.noarch - libcurl-7.61.1-34.el8.x86_64 - python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch - yum-utils-4.0.21-25.0.1.el8.noarch -Installed: - bind-libs-32:9.11.36-16.el8_10.2.x86_64 - bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 - bind-license-32:9.11.36-16.el8_10.2.noarch - bind-utils-32:9.11.36-16.el8_10.2.x86_64 - expect-5.45.4-5.el8.x86_64 - fstrm-0.6.1-3.el8.x86_64 - geolite2-city-20180605-1.el8.noarch - geolite2-country-20180605-1.el8.noarch - hostname-3.20-6.el8.x86_64 - libmaxminddb-1.2.0-10.el8_9.1.x86_64 - libmetalink-0.1.3-7.el8.x86_64 - lsof-4.93.2-1.el8.x86_64 - net-tools-2.0-0.52.20160912git.el8.x86_64 - openssl-1:1.1.1k-12.el8_9.x86_64 - protobuf-c-1.3.0-8.el8.x86_64 - python3-bind-32:9.11.36-16.el8_10.2.noarch - python3-ply-3.9-9.el8.noarch - sudo-1.9.5p2-1.el8_9.x86_64 - tcl-1:8.6.8-2.el8.x86_64 - tree-1.7.0-15.el8.x86_64 - unzip-6.0-46.0.1.el8.x86_64 - wget-1.19.5-12.0.1.el8_10.x86_64 - which-2.21-20.el8.x86_64 - zip-3.0-23.el8.x86_64 - -Complete! -Adding repo from: http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 -created by dnf config-manager from http://yum.o 496 kB/s | 139 kB 00:00 -Last metadata expiration check: 0:00:01 ago on Tue 20 Aug 2024 08:55:14 AM UTC. -Dependencies resolved. -============================================================================================== - Package Arch Version Repository Size -============================================================================================== -Installing: - java-11-openjdk-devel x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 3.4 M -Installing dependencies: - adwaita-cursor-theme noarch 3.28.0-3.el8 ol8_appstream 647 k - adwaita-icon-theme noarch 3.28.0-3.el8 ol8_appstream 11 M - alsa-lib x86_64 1.2.10-2.el8 ol8_appstream 500 k - at-spi2-atk x86_64 2.26.2-1.el8 ol8_appstream 89 k - at-spi2-core x86_64 2.28.0-1.el8 ol8_appstream 169 k - atk x86_64 2.28.1-1.el8 ol8_appstream 272 k - avahi-libs x86_64 0.7-27.el8 ol8_baseos_latest 61 k - cairo x86_64 1.15.12-6.el8 ol8_appstream 719 k - cairo-gobject x86_64 1.15.12-6.el8 ol8_appstream 33 k - colord-libs x86_64 1.4.2-1.el8 ol8_appstream 236 k - copy-jdk-configs noarch 4.0-2.el8 ol8_appstream 30 k - cpio x86_64 2.12-11.el8 ol8_baseos_latest 266 k - crypto-policies-scripts noarch 20230731-1.git3177e06.el8 ol8_baseos_latest 84 k - cups-libs x86_64 1:2.2.6-60.el8_10 ol8_baseos_latest 435 k - dracut x86_64 049-233.git20240115.0.1.el8 ol8_baseos_latest 382 k - file x86_64 5.33-25.el8 ol8_baseos_latest 77 k - fribidi x86_64 1.0.4-9.el8 ol8_appstream 89 k - gdk-pixbuf2 x86_64 2.36.12-6.el8_10 ol8_baseos_latest 465 k - gdk-pixbuf2-modules x86_64 2.36.12-6.el8_10 ol8_appstream 108 k - gettext x86_64 0.19.8.1-17.el8 ol8_baseos_latest 1.1 M - gettext-libs x86_64 0.19.8.1-17.el8 ol8_baseos_latest 312 k - glib-networking x86_64 2.56.1-1.1.el8 ol8_baseos_latest 155 k - graphite2 x86_64 1.3.10-10.el8 ol8_appstream 122 k - grub2-common noarch 1:2.02-156.0.2.el8 ol8_baseos_latest 897 k - grub2-tools x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 2.0 M - grub2-tools-minimal x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 215 k - gsettings-desktop-schemas x86_64 3.32.0-6.el8 ol8_baseos_latest 633 k - gtk-update-icon-cache x86_64 3.22.30-11.el8 ol8_appstream 32 k - harfbuzz x86_64 1.7.5-4.el8 ol8_appstream 295 k - hicolor-icon-theme noarch 0.17-2.el8 ol8_appstream 48 k - jasper-libs x86_64 2.0.14-5.el8 ol8_appstream 167 k - java-11-openjdk x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 475 k - java-11-openjdk-headless x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 42 M - javapackages-filesystem noarch 5.3.0-1.module+el8+5136+7ff78f74 ol8_appstream 30 k - jbigkit-libs x86_64 2.1-14.el8 ol8_appstream 55 k - json-glib x86_64 1.4.4-1.el8 ol8_baseos_latest 144 k - kbd-legacy noarch 2.0.4-11.el8 ol8_baseos_latest 481 k - kbd-misc noarch 2.0.4-11.el8 ol8_baseos_latest 1.5 M - lcms2 x86_64 2.9-2.el8 ol8_appstream 164 k - libX11 x86_64 1.6.8-8.el8 ol8_appstream 611 k - libX11-common noarch 1.6.8-8.el8 ol8_appstream 157 k - libXau x86_64 1.0.9-3.el8 ol8_appstream 37 k - libXcomposite x86_64 0.4.4-14.el8 ol8_appstream 28 k - libXcursor x86_64 1.1.15-3.el8 ol8_appstream 36 k - libXdamage x86_64 1.1.4-14.el8 ol8_appstream 27 k - libXext x86_64 1.3.4-1.el8 ol8_appstream 45 k - libXfixes x86_64 5.0.3-7.el8 ol8_appstream 25 k - libXft x86_64 2.3.3-1.el8 ol8_appstream 67 k - libXi x86_64 1.7.10-1.el8 ol8_appstream 49 k - libXinerama x86_64 1.1.4-1.el8 ol8_appstream 15 k - libXrandr x86_64 1.5.2-1.el8 ol8_appstream 34 k - libXrender x86_64 0.9.10-7.el8 ol8_appstream 33 k - libXtst x86_64 1.2.3-7.el8 ol8_appstream 22 k - libcroco x86_64 0.6.12-4.el8_2.1 ol8_baseos_latest 113 k - libdatrie x86_64 0.2.9-7.el8 ol8_appstream 33 k - libepoxy x86_64 1.5.8-1.el8 ol8_appstream 225 k - libfontenc x86_64 1.1.3-8.el8 ol8_appstream 37 k - libgomp x86_64 8.5.0-22.0.1.el8_10 ol8_baseos_latest 218 k - libgusb x86_64 0.3.0-1.el8 ol8_baseos_latest 49 k - libjpeg-turbo x86_64 1.5.3-12.el8 ol8_appstream 157 k - libkcapi x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 52 k - libkcapi-hmaccalc x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 31 k - libmodman x86_64 2.0.1-17.el8 ol8_baseos_latest 36 k - libpkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 35 k - libproxy x86_64 0.4.15-5.2.el8 ol8_baseos_latest 75 k - libsoup x86_64 2.62.3-5.el8 ol8_baseos_latest 424 k - libthai x86_64 0.1.27-2.el8 ol8_appstream 203 k - libtiff x86_64 4.0.9-32.el8_10 ol8_appstream 189 k - libwayland-client x86_64 1.21.0-1.el8 ol8_appstream 41 k - libwayland-cursor x86_64 1.21.0-1.el8 ol8_appstream 26 k - libwayland-egl x86_64 1.21.0-1.el8 ol8_appstream 19 k - libxcb x86_64 1.13.1-1.el8 ol8_appstream 231 k - libxkbcommon x86_64 0.9.1-1.el8 ol8_appstream 116 k - lksctp-tools x86_64 1.0.18-3.el8 ol8_baseos_latest 100 k - lua x86_64 5.3.4-12.el8 ol8_appstream 192 k - nspr x86_64 4.35.0-1.el8_8 ol8_appstream 143 k - nss x86_64 3.90.0-7.el8_10 ol8_appstream 750 k - nss-softokn x86_64 3.90.0-7.el8_10 ol8_appstream 1.2 M - nss-softokn-freebl x86_64 3.90.0-7.el8_10 ol8_appstream 375 k - nss-sysinit x86_64 3.90.0-7.el8_10 ol8_appstream 74 k - nss-util x86_64 3.90.0-7.el8_10 ol8_appstream 139 k - os-prober x86_64 1.74-9.0.1.el8 ol8_baseos_latest 51 k - pango x86_64 1.42.4-8.el8 ol8_appstream 297 k - pixman x86_64 0.38.4-4.el8 ol8_appstream 256 k - pkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 38 k - pkgconf-m4 noarch 1.4.2-1.el8 ol8_baseos_latest 17 k - pkgconf-pkg-config x86_64 1.4.2-1.el8 ol8_baseos_latest 15 k - rest x86_64 0.8.1-2.el8 ol8_appstream 70 k - shared-mime-info x86_64 1.9-4.el8 ol8_baseos_latest 328 k - systemd-udev x86_64 239-78.0.4.el8 ol8_baseos_latest 1.6 M - ttmkfdir x86_64 3.0.9-54.el8 ol8_appstream 62 k - tzdata-java noarch 2024a-1.0.1.el8 ol8_appstream 186 k - xkeyboard-config noarch 2.28-1.el8 ol8_appstream 782 k - xorg-x11-font-utils x86_64 1:7.5-41.el8 ol8_appstream 104 k - xorg-x11-fonts-Type1 noarch 7.5-19.el8 ol8_appstream 522 k - xz x86_64 5.2.4-4.el8_6 ol8_baseos_latest 153 k -Installing weak dependencies: - abattis-cantarell-fonts noarch 0.0.25-6.el8 ol8_appstream 155 k - dconf x86_64 0.28.0-4.0.1.el8 ol8_appstream 108 k - dejavu-sans-mono-fonts noarch 2.35-7.el8 ol8_baseos_latest 447 k - grubby x86_64 8.40-49.0.2.el8 ol8_baseos_latest 50 k - gtk3 x86_64 3.22.30-11.el8 ol8_appstream 4.5 M - hardlink x86_64 1:1.3-6.el8 ol8_baseos_latest 29 k - kbd x86_64 2.0.4-11.el8 ol8_baseos_latest 390 k - memstrack x86_64 0.2.5-2.el8 ol8_baseos_latest 51 k - pigz x86_64 2.4-4.el8 ol8_baseos_latest 80 k -Enabling module streams: - javapackages-runtime 201801 - -Transaction Summary -============================================================================================== -Install 106 Packages - -Total download size: 86 M -Installed size: 312 M -Downloading Packages: -(1/106): crypto-policies-scripts-20230731-1.git 862 kB/s | 84 kB 00:00 -(2/106): avahi-libs-0.7-27.el8.x86_64.rpm 602 kB/s | 61 kB 00:00 -(3/106): cpio-2.12-11.el8.x86_64.rpm 1.8 MB/s | 266 kB 00:00 -(4/106): cups-libs-2.2.6-60.el8_10.x86_64.rpm 5.7 MB/s | 435 kB 00:00 -(5/106): dejavu-sans-mono-fonts-2.35-7.el8.noar 5.1 MB/s | 447 kB 00:00 -(6/106): dracut-049-233.git20240115.0.1.el8.x86 7.0 MB/s | 382 kB 00:00 -(7/106): gdk-pixbuf2-2.36.12-6.el8_10.x86_64.rp 12 MB/s | 465 kB 00:00 -(8/106): gettext-libs-0.19.8.1-17.el8.x86_64.rp 9.3 MB/s | 312 kB 00:00 -(9/106): gettext-0.19.8.1-17.el8.x86_64.rpm 16 MB/s | 1.1 MB 00:00 -(10/106): glib-networking-2.56.1-1.1.el8.x86_64 6.0 MB/s | 155 kB 00:00 -(11/106): grub2-common-2.02-156.0.2.el8.noarch. 26 MB/s | 897 kB 00:00 -(12/106): grub2-tools-minimal-2.02-156.0.2.el8. 8.2 MB/s | 215 kB 00:00 -(13/106): grubby-8.40-49.0.2.el8.x86_64.rpm 2.1 MB/s | 50 kB 00:00 -(14/106): grub2-tools-2.02-156.0.2.el8.x86_64.r 26 MB/s | 2.0 MB 00:00 -(15/106): gsettings-desktop-schemas-3.32.0-6.el 19 MB/s | 633 kB 00:00 -(16/106): hardlink-1.3-6.el8.x86_64.rpm 1.1 MB/s | 29 kB 00:00 -(17/106): json-glib-1.4.4-1.el8.x86_64.rpm 5.9 MB/s | 144 kB 00:00 -(18/106): kbd-2.0.4-11.el8.x86_64.rpm 14 MB/s | 390 kB 00:00 -(19/106): kbd-legacy-2.0.4-11.el8.noarch.rpm 17 MB/s | 481 kB 00:00 -(20/106): kbd-misc-2.0.4-11.el8.noarch.rpm 41 MB/s | 1.5 MB 00:00 -(21/106): libcroco-0.6.12-4.el8_2.1.x86_64.rpm 4.7 MB/s | 113 kB 00:00 -(22/106): libgomp-8.5.0-22.0.1.el8_10.x86_64.rp 9.1 MB/s | 218 kB 00:00 -(23/106): libgusb-0.3.0-1.el8.x86_64.rpm 2.1 MB/s | 49 kB 00:00 -(24/106): libkcapi-1.4.0-2.0.1.el8.x86_64.rpm 1.6 MB/s | 52 kB 00:00 -(25/106): libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86 822 kB/s | 31 kB 00:00 -(26/106): libmodman-2.0.1-17.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 -(27/106): libpkgconf-1.4.2-1.el8.x86_64.rpm 1.2 MB/s | 35 kB 00:00 -(28/106): libproxy-0.4.15-5.2.el8.x86_64.rpm 3.0 MB/s | 75 kB 00:00 -(29/106): libsoup-2.62.3-5.el8.x86_64.rpm 15 MB/s | 424 kB 00:00 -(30/106): lksctp-tools-1.0.18-3.el8.x86_64.rpm 3.5 MB/s | 100 kB 00:00 -(31/106): memstrack-0.2.5-2.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 -(32/106): os-prober-1.74-9.0.1.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 -(33/106): pigz-2.4-4.el8.x86_64.rpm 3.5 MB/s | 80 kB 00:00 -(34/106): pkgconf-1.4.2-1.el8.x86_64.rpm 1.7 MB/s | 38 kB 00:00 -(35/106): pkgconf-m4-1.4.2-1.el8.noarch.rpm 761 kB/s | 17 kB 00:00 -(36/106): pkgconf-pkg-config-1.4.2-1.el8.x86_64 691 kB/s | 15 kB 00:00 -(37/106): shared-mime-info-1.9-4.el8.x86_64.rpm 13 MB/s | 328 kB 00:00 -(38/106): systemd-udev-239-78.0.4.el8.x86_64.rp 32 MB/s | 1.6 MB 00:00 -(39/106): xz-5.2.4-4.el8_6.x86_64.rpm 5.2 MB/s | 153 kB 00:00 -(40/106): abattis-cantarell-fonts-0.0.25-6.el8. 6.4 MB/s | 155 kB 00:00 -(41/106): adwaita-cursor-theme-3.28.0-3.el8.noa 22 MB/s | 647 kB 00:00 -(42/106): alsa-lib-1.2.10-2.el8.x86_64.rpm 18 MB/s | 500 kB 00:00 -(43/106): at-spi2-atk-2.26.2-1.el8.x86_64.rpm 3.8 MB/s | 89 kB 00:00 -(44/106): at-spi2-core-2.28.0-1.el8.x86_64.rpm 6.9 MB/s | 169 kB 00:00 -(45/106): atk-2.28.1-1.el8.x86_64.rpm 9.2 MB/s | 272 kB 00:00 -(46/106): cairo-1.15.12-6.el8.x86_64.rpm 24 MB/s | 719 kB 00:00 -(47/106): adwaita-icon-theme-3.28.0-3.el8.noarc 65 MB/s | 11 MB 00:00 -(48/106): cairo-gobject-1.15.12-6.el8.x86_64.rp 914 kB/s | 33 kB 00:00 -(49/106): colord-libs-1.4.2-1.el8.x86_64.rpm 9.5 MB/s | 236 kB 00:00 -(50/106): copy-jdk-configs-4.0-2.el8.noarch.rpm 1.1 MB/s | 30 kB 00:00 -(51/106): dconf-0.28.0-4.0.1.el8.x86_64.rpm 4.4 MB/s | 108 kB 00:00 -(52/106): fribidi-1.0.4-9.el8.x86_64.rpm 3.9 MB/s | 89 kB 00:00 -(53/106): graphite2-1.3.10-10.el8.x86_64.rpm 5.1 MB/s | 122 kB 00:00 -(54/106): gdk-pixbuf2-modules-2.36.12-6.el8_10. 3.6 MB/s | 108 kB 00:00 -(55/106): gtk-update-icon-cache-3.22.30-11.el8. 1.4 MB/s | 32 kB 00:00 -(56/106): harfbuzz-1.7.5-4.el8.x86_64.rpm 11 MB/s | 295 kB 00:00 -(57/106): gtk3-3.22.30-11.el8.x86_64.rpm 68 MB/s | 4.5 MB 00:00 -(58/106): hicolor-icon-theme-0.17-2.el8.noarch. 2.1 MB/s | 48 kB 00:00 -(59/106): java-11-openjdk-11.0.24.0.8-3.0.1.el8 17 MB/s | 475 kB 00:00 -(60/106): jasper-libs-2.0.14-5.el8.x86_64.rpm 5.0 MB/s | 167 kB 00:00 -(61/106): java-11-openjdk-devel-11.0.24.0.8-3.0 61 MB/s | 3.4 MB 00:00 -(62/106): javapackages-filesystem-5.3.0-1.modul 1.2 MB/s | 30 kB 00:00 -(63/106): jbigkit-libs-2.1-14.el8.x86_64.rpm 2.1 MB/s | 55 kB 00:00 -(64/106): lcms2-2.9-2.el8.x86_64.rpm 3.8 MB/s | 164 kB 00:00 -(65/106): libX11-1.6.8-8.el8.x86_64.rpm 20 MB/s | 611 kB 00:00 -(66/106): libX11-common-1.6.8-8.el8.noarch.rpm 6.8 MB/s | 157 kB 00:00 -(67/106): libXau-1.0.9-3.el8.x86_64.rpm 1.6 MB/s | 37 kB 00:00 -(68/106): libXcomposite-0.4.4-14.el8.x86_64.rpm 1.3 MB/s | 28 kB 00:00 -(69/106): libXcursor-1.1.15-3.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 -(70/106): libXdamage-1.1.4-14.el8.x86_64.rpm 1.2 MB/s | 27 kB 00:00 -(71/106): libXext-1.3.4-1.el8.x86_64.rpm 2.0 MB/s | 45 kB 00:00 -(72/106): libXfixes-5.0.3-7.el8.x86_64.rpm 1.1 MB/s | 25 kB 00:00 -(73/106): libXft-2.3.3-1.el8.x86_64.rpm 2.9 MB/s | 67 kB 00:00 -(74/106): libXi-1.7.10-1.el8.x86_64.rpm 2.2 MB/s | 49 kB 00:00 -(75/106): libXinerama-1.1.4-1.el8.x86_64.rpm 717 kB/s | 15 kB 00:00 -(76/106): libXrandr-1.5.2-1.el8.x86_64.rpm 1.5 MB/s | 34 kB 00:00 -(77/106): libXrender-0.9.10-7.el8.x86_64.rpm 1.4 MB/s | 33 kB 00:00 -(78/106): libXtst-1.2.3-7.el8.x86_64.rpm 957 kB/s | 22 kB 00:00 -(79/106): java-11-openjdk-headless-11.0.24.0.8- 71 MB/s | 42 MB 00:00 -(80/106): libdatrie-0.2.9-7.el8.x86_64.rpm 274 kB/s | 33 kB 00:00 -(81/106): libepoxy-1.5.8-1.el8.x86_64.rpm 9.1 MB/s | 225 kB 00:00 -(82/106): libfontenc-1.1.3-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 -(83/106): libthai-0.1.27-2.el8.x86_64.rpm 8.2 MB/s | 203 kB 00:00 -(84/106): libjpeg-turbo-1.5.3-12.el8.x86_64.rpm 5.1 MB/s | 157 kB 00:00 -(85/106): libtiff-4.0.9-32.el8_10.x86_64.rpm 7.8 MB/s | 189 kB 00:00 -(86/106): libwayland-client-1.21.0-1.el8.x86_64 1.7 MB/s | 41 kB 00:00 -(87/106): libwayland-cursor-1.21.0-1.el8.x86_64 1.2 MB/s | 26 kB 00:00 -(88/106): libwayland-egl-1.21.0-1.el8.x86_64.rp 801 kB/s | 19 kB 00:00 -(89/106): libxcb-1.13.1-1.el8.x86_64.rpm 9.7 MB/s | 231 kB 00:00 -(90/106): libxkbcommon-0.9.1-1.el8.x86_64.rpm 5.0 MB/s | 116 kB 00:00 -(91/106): nspr-4.35.0-1.el8_8.x86_64.rpm 6.0 MB/s | 143 kB 00:00 -(92/106): lua-5.3.4-12.el8.x86_64.rpm 5.9 MB/s | 192 kB 00:00 -(93/106): nss-softokn-3.90.0-7.el8_10.x86_64.rp 38 MB/s | 1.2 MB 00:00 -(94/106): nss-3.90.0-7.el8_10.x86_64.rpm 17 MB/s | 750 kB 00:00 -(95/106): nss-softokn-freebl-3.90.0-7.el8_10.x8 14 MB/s | 375 kB 00:00 -(96/106): nss-sysinit-3.90.0-7.el8_10.x86_64.rp 3.2 MB/s | 74 kB 00:00 -(97/106): nss-util-3.90.0-7.el8_10.x86_64.rpm 5.8 MB/s | 139 kB 00:00 -(98/106): pango-1.42.4-8.el8.x86_64.rpm 11 MB/s | 297 kB 00:00 -(99/106): pixman-0.38.4-4.el8.x86_64.rpm 10 MB/s | 256 kB 00:00 -(100/106): rest-0.8.1-2.el8.x86_64.rpm 3.1 MB/s | 70 kB 00:00 -(101/106): ttmkfdir-3.0.9-54.el8.x86_64.rpm 2.5 MB/s | 62 kB 00:00 -(102/106): tzdata-java-2024a-1.0.1.el8.noarch.r 7.4 MB/s | 186 kB 00:00 -(103/106): xkeyboard-config-2.28-1.el8.noarch.r 27 MB/s | 782 kB 00:00 -(104/106): xorg-x11-font-utils-7.5-41.el8.x86_6 3.9 MB/s | 104 kB 00:00 -(105/106): xorg-x11-fonts-Type1-7.5-19.el8.noar 1.3 MB/s | 522 kB 00:00 -(106/106): file-5.33-25.el8.x86_64.rpm 26 kB/s | 77 kB 00:02 --------------------------------------------------------------------------------- -Total 27 MB/s | 86 MB 00:03 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 1/1 - Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86 1/1 - Preparing : 1/1 - Installing : nspr-4.35.0-1.el8_8.x86_64 1/106 - Running scriptlet: nspr-4.35.0-1.el8_8.x86_64 1/106 - Installing : nss-util-3.90.0-7.el8_10.x86_64 2/106 - Installing : libjpeg-turbo-1.5.3-12.el8.x86_64 3/106 - Installing : pixman-0.38.4-4.el8.x86_64 4/106 - Installing : libwayland-client-1.21.0-1.el8.x86_64 5/106 - Installing : atk-2.28.1-1.el8.x86_64 6/106 - Installing : libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 - Running scriptlet: libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 - Installing : libcroco-0.6.12-4.el8_2.1.x86_64 8/106 - Running scriptlet: libcroco-0.6.12-4.el8_2.1.x86_64 8/106 - Installing : grub2-common-1:2.02-156.0.2.el8.noarch 9/106 - Installing : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 - Installing : gettext-0.19.8.1-17.el8.x86_64 11/106 - Running scriptlet: gettext-0.19.8.1-17.el8.x86_64 11/106 - Installing : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 12/106 - Installing : libwayland-cursor-1.21.0-1.el8.x86_64 13/106 - Installing : jasper-libs-2.0.14-5.el8.x86_64 14/106 - Installing : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 15/106 - Installing : nss-softokn-3.90.0-7.el8_10.x86_64 16/106 - Installing : xkeyboard-config-2.28-1.el8.noarch 17/106 - Installing : libxkbcommon-0.9.1-1.el8.x86_64 18/106 - Installing : tzdata-java-2024a-1.0.1.el8.noarch 19/106 - Installing : ttmkfdir-3.0.9-54.el8.x86_64 20/106 - Installing : lua-5.3.4-12.el8.x86_64 21/106 - Installing : copy-jdk-configs-4.0-2.el8.noarch 22/106 - Installing : libwayland-egl-1.21.0-1.el8.x86_64 23/106 - Installing : libfontenc-1.1.3-8.el8.x86_64 24/106 - Installing : libepoxy-1.5.8-1.el8.x86_64 25/106 - Installing : libdatrie-0.2.9-7.el8.x86_64 26/106 - Running scriptlet: libdatrie-0.2.9-7.el8.x86_64 26/106 - Installing : libthai-0.1.27-2.el8.x86_64 27/106 - Running scriptlet: libthai-0.1.27-2.el8.x86_64 27/106 - Installing : libXau-1.0.9-3.el8.x86_64 28/106 - Installing : libxcb-1.13.1-1.el8.x86_64 29/106 - Installing : libX11-common-1.6.8-8.el8.noarch 30/106 - Installing : libX11-1.6.8-8.el8.x86_64 31/106 - Installing : libXext-1.3.4-1.el8.x86_64 32/106 - Installing : libXrender-0.9.10-7.el8.x86_64 33/106 - Installing : cairo-1.15.12-6.el8.x86_64 34/106 - Installing : libXi-1.7.10-1.el8.x86_64 35/106 - Installing : libXfixes-5.0.3-7.el8.x86_64 36/106 - Installing : libXtst-1.2.3-7.el8.x86_64 37/106 - Installing : libXcomposite-0.4.4-14.el8.x86_64 38/106 - Installing : at-spi2-core-2.28.0-1.el8.x86_64 39/106 - Running scriptlet: at-spi2-core-2.28.0-1.el8.x86_64 39/106 - Installing : at-spi2-atk-2.26.2-1.el8.x86_64 40/106 - Running scriptlet: at-spi2-atk-2.26.2-1.el8.x86_64 40/106 - Installing : libXcursor-1.1.15-3.el8.x86_64 41/106 - Installing : libXdamage-1.1.4-14.el8.x86_64 42/106 - Installing : cairo-gobject-1.15.12-6.el8.x86_64 43/106 - Installing : libXft-2.3.3-1.el8.x86_64 44/106 - Installing : libXrandr-1.5.2-1.el8.x86_64 45/106 - Installing : libXinerama-1.1.4-1.el8.x86_64 46/106 - Installing : lcms2-2.9-2.el8.x86_64 47/106 - Running scriptlet: lcms2-2.9-2.el8.x86_64 47/106 - Installing : jbigkit-libs-2.1-14.el8.x86_64 48/106 - Running scriptlet: jbigkit-libs-2.1-14.el8.x86_64 48/106 - Installing : libtiff-4.0.9-32.el8_10.x86_64 49/106 - Installing : javapackages-filesystem-5.3.0-1.module+el8+5136+ 50/106 - Installing : hicolor-icon-theme-0.17-2.el8.noarch 51/106 - Installing : graphite2-1.3.10-10.el8.x86_64 52/106 - Installing : harfbuzz-1.7.5-4.el8.x86_64 53/106 - Running scriptlet: harfbuzz-1.7.5-4.el8.x86_64 53/106 - Installing : fribidi-1.0.4-9.el8.x86_64 54/106 - Installing : pango-1.42.4-8.el8.x86_64 55/106 - Running scriptlet: pango-1.42.4-8.el8.x86_64 55/106 - Installing : dconf-0.28.0-4.0.1.el8.x86_64 56/106 - Installing : alsa-lib-1.2.10-2.el8.x86_64 57/106 - Running scriptlet: alsa-lib-1.2.10-2.el8.x86_64 57/106 - Installing : adwaita-cursor-theme-3.28.0-3.el8.noarch 58/106 - Installing : adwaita-icon-theme-3.28.0-3.el8.noarch 59/106 - Installing : abattis-cantarell-fonts-0.0.25-6.el8.noarch 60/106 - Installing : xz-5.2.4-4.el8_6.x86_64 61/106 - Installing : shared-mime-info-1.9-4.el8.x86_64 62/106 - Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 62/106 - Installing : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 - Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 - Installing : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 64/106 - Installing : gtk-update-icon-cache-3.22.30-11.el8.x86_64 65/106 - Installing : pkgconf-m4-1.4.2-1.el8.noarch 66/106 - Installing : pigz-2.4-4.el8.x86_64 67/106 - Installing : memstrack-0.2.5-2.el8.x86_64 68/106 - Installing : lksctp-tools-1.0.18-3.el8.x86_64 69/106 - Running scriptlet: lksctp-tools-1.0.18-3.el8.x86_64 69/106 - Installing : libpkgconf-1.4.2-1.el8.x86_64 70/106 - Installing : pkgconf-1.4.2-1.el8.x86_64 71/106 - Installing : pkgconf-pkg-config-1.4.2-1.el8.x86_64 72/106 - Installing : xorg-x11-font-utils-1:7.5-41.el8.x86_64 73/106 - Installing : xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 - Running scriptlet: xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 - Installing : libmodman-2.0.1-17.el8.x86_64 75/106 - Running scriptlet: libmodman-2.0.1-17.el8.x86_64 75/106 - Installing : libproxy-0.4.15-5.2.el8.x86_64 76/106 - Running scriptlet: libproxy-0.4.15-5.2.el8.x86_64 76/106 - Installing : libkcapi-1.4.0-2.0.1.el8.x86_64 77/106 - Installing : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 78/106 - Installing : libgusb-0.3.0-1.el8.x86_64 79/106 - Installing : colord-libs-1.4.2-1.el8.x86_64 80/106 - Installing : kbd-misc-2.0.4-11.el8.noarch 81/106 - Installing : kbd-legacy-2.0.4-11.el8.noarch 82/106 - Installing : kbd-2.0.4-11.el8.x86_64 83/106 - Installing : systemd-udev-239-78.0.4.el8.x86_64 84/106 - Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 84/106 - Installing : os-prober-1.74-9.0.1.el8.x86_64 85/106 - Installing : json-glib-1.4.4-1.el8.x86_64 86/106 - Installing : hardlink-1:1.3-6.el8.x86_64 87/106 - Installing : file-5.33-25.el8.x86_64 88/106 - Installing : dejavu-sans-mono-fonts-2.35-7.el8.noarch 89/106 - Installing : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 90/106 - Installing : glib-networking-2.56.1-1.1.el8.x86_64 91/106 - Installing : libsoup-2.62.3-5.el8.x86_64 92/106 - Installing : rest-0.8.1-2.el8.x86_64 93/106 - Running scriptlet: rest-0.8.1-2.el8.x86_64 93/106 - Installing : cpio-2.12-11.el8.x86_64 94/106 - Installing : dracut-049-233.git20240115.0.1.el8.x86_64 95/106 - Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 - Installing : grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 - Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 - Installing : grubby-8.40-49.0.2.el8.x86_64 97/106 - Installing : crypto-policies-scripts-20230731-1.git3177e06.el 98/106 - Installing : nss-sysinit-3.90.0-7.el8_10.x86_64 99/106 - Installing : nss-3.90.0-7.el8_10.x86_64 100/106 - Installing : avahi-libs-0.7-27.el8.x86_64 101/106 - Installing : cups-libs-1:2.2.6-60.el8_10.x86_64 102/106 - Installing : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 - Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 - Installing : gtk3-3.22.30-11.el8.x86_64 104/106 - Installing : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 - Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 - Installing : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 - Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 - Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 106/106 - Running scriptlet: dconf-0.28.0-4.0.1.el8.x86_64 106/106 - Running scriptlet: crypto-policies-scripts-20230731-1.git3177e06.el 106/106 - Running scriptlet: nss-3.90.0-7.el8_10.x86_64 106/106 - Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 106/106 - Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 106/106 - Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 - Running scriptlet: hicolor-icon-theme-0.17-2.el8.noarch 106/106 - Running scriptlet: adwaita-icon-theme-3.28.0-3.el8.noarch 106/106 - Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 106/106 - Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 106/106 - Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 106/106 - Verifying : avahi-libs-0.7-27.el8.x86_64 1/106 - Verifying : cpio-2.12-11.el8.x86_64 2/106 - Verifying : crypto-policies-scripts-20230731-1.git3177e06.el 3/106 - Verifying : cups-libs-1:2.2.6-60.el8_10.x86_64 4/106 - Verifying : dejavu-sans-mono-fonts-2.35-7.el8.noarch 5/106 - Verifying : dracut-049-233.git20240115.0.1.el8.x86_64 6/106 - Verifying : file-5.33-25.el8.x86_64 7/106 - Verifying : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 8/106 - Verifying : gettext-0.19.8.1-17.el8.x86_64 9/106 - Verifying : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 - Verifying : glib-networking-2.56.1-1.1.el8.x86_64 11/106 - Verifying : grub2-common-1:2.02-156.0.2.el8.noarch 12/106 - Verifying : grub2-tools-1:2.02-156.0.2.el8.x86_64 13/106 - Verifying : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 14/106 - Verifying : grubby-8.40-49.0.2.el8.x86_64 15/106 - Verifying : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 16/106 - Verifying : hardlink-1:1.3-6.el8.x86_64 17/106 - Verifying : json-glib-1.4.4-1.el8.x86_64 18/106 - Verifying : kbd-2.0.4-11.el8.x86_64 19/106 - Verifying : kbd-legacy-2.0.4-11.el8.noarch 20/106 - Verifying : kbd-misc-2.0.4-11.el8.noarch 21/106 - Verifying : libcroco-0.6.12-4.el8_2.1.x86_64 22/106 - Verifying : libgomp-8.5.0-22.0.1.el8_10.x86_64 23/106 - Verifying : libgusb-0.3.0-1.el8.x86_64 24/106 - Verifying : libkcapi-1.4.0-2.0.1.el8.x86_64 25/106 - Verifying : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 26/106 - Verifying : libmodman-2.0.1-17.el8.x86_64 27/106 - Verifying : libpkgconf-1.4.2-1.el8.x86_64 28/106 - Verifying : libproxy-0.4.15-5.2.el8.x86_64 29/106 - Verifying : libsoup-2.62.3-5.el8.x86_64 30/106 - Verifying : lksctp-tools-1.0.18-3.el8.x86_64 31/106 - Verifying : memstrack-0.2.5-2.el8.x86_64 32/106 - Verifying : os-prober-1.74-9.0.1.el8.x86_64 33/106 - Verifying : pigz-2.4-4.el8.x86_64 34/106 - Verifying : pkgconf-1.4.2-1.el8.x86_64 35/106 - Verifying : pkgconf-m4-1.4.2-1.el8.noarch 36/106 - Verifying : pkgconf-pkg-config-1.4.2-1.el8.x86_64 37/106 - Verifying : shared-mime-info-1.9-4.el8.x86_64 38/106 - Verifying : systemd-udev-239-78.0.4.el8.x86_64 39/106 - Verifying : xz-5.2.4-4.el8_6.x86_64 40/106 - Verifying : abattis-cantarell-fonts-0.0.25-6.el8.noarch 41/106 - Verifying : adwaita-cursor-theme-3.28.0-3.el8.noarch 42/106 - Verifying : adwaita-icon-theme-3.28.0-3.el8.noarch 43/106 - Verifying : alsa-lib-1.2.10-2.el8.x86_64 44/106 - Verifying : at-spi2-atk-2.26.2-1.el8.x86_64 45/106 - Verifying : at-spi2-core-2.28.0-1.el8.x86_64 46/106 - Verifying : atk-2.28.1-1.el8.x86_64 47/106 - Verifying : cairo-1.15.12-6.el8.x86_64 48/106 - Verifying : cairo-gobject-1.15.12-6.el8.x86_64 49/106 - Verifying : colord-libs-1.4.2-1.el8.x86_64 50/106 - Verifying : copy-jdk-configs-4.0-2.el8.noarch 51/106 - Verifying : dconf-0.28.0-4.0.1.el8.x86_64 52/106 - Verifying : fribidi-1.0.4-9.el8.x86_64 53/106 - Verifying : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 54/106 - Verifying : graphite2-1.3.10-10.el8.x86_64 55/106 - Verifying : gtk-update-icon-cache-3.22.30-11.el8.x86_64 56/106 - Verifying : gtk3-3.22.30-11.el8.x86_64 57/106 - Verifying : harfbuzz-1.7.5-4.el8.x86_64 58/106 - Verifying : hicolor-icon-theme-0.17-2.el8.noarch 59/106 - Verifying : jasper-libs-2.0.14-5.el8.x86_64 60/106 - Verifying : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 61/106 - Verifying : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 62/106 - Verifying : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 63/106 - Verifying : javapackages-filesystem-5.3.0-1.module+el8+5136+ 64/106 - Verifying : jbigkit-libs-2.1-14.el8.x86_64 65/106 - Verifying : lcms2-2.9-2.el8.x86_64 66/106 - Verifying : libX11-1.6.8-8.el8.x86_64 67/106 - Verifying : libX11-common-1.6.8-8.el8.noarch 68/106 - Verifying : libXau-1.0.9-3.el8.x86_64 69/106 - Verifying : libXcomposite-0.4.4-14.el8.x86_64 70/106 - Verifying : libXcursor-1.1.15-3.el8.x86_64 71/106 - Verifying : libXdamage-1.1.4-14.el8.x86_64 72/106 - Verifying : libXext-1.3.4-1.el8.x86_64 73/106 - Verifying : libXfixes-5.0.3-7.el8.x86_64 74/106 - Verifying : libXft-2.3.3-1.el8.x86_64 75/106 - Verifying : libXi-1.7.10-1.el8.x86_64 76/106 - Verifying : libXinerama-1.1.4-1.el8.x86_64 77/106 - Verifying : libXrandr-1.5.2-1.el8.x86_64 78/106 - Verifying : libXrender-0.9.10-7.el8.x86_64 79/106 - Verifying : libXtst-1.2.3-7.el8.x86_64 80/106 - Verifying : libdatrie-0.2.9-7.el8.x86_64 81/106 - Verifying : libepoxy-1.5.8-1.el8.x86_64 82/106 - Verifying : libfontenc-1.1.3-8.el8.x86_64 83/106 - Verifying : libjpeg-turbo-1.5.3-12.el8.x86_64 84/106 - Verifying : libthai-0.1.27-2.el8.x86_64 85/106 - Verifying : libtiff-4.0.9-32.el8_10.x86_64 86/106 - Verifying : libwayland-client-1.21.0-1.el8.x86_64 87/106 - Verifying : libwayland-cursor-1.21.0-1.el8.x86_64 88/106 - Verifying : libwayland-egl-1.21.0-1.el8.x86_64 89/106 - Verifying : libxcb-1.13.1-1.el8.x86_64 90/106 - Verifying : libxkbcommon-0.9.1-1.el8.x86_64 91/106 - Verifying : lua-5.3.4-12.el8.x86_64 92/106 - Verifying : nspr-4.35.0-1.el8_8.x86_64 93/106 - Verifying : nss-3.90.0-7.el8_10.x86_64 94/106 - Verifying : nss-softokn-3.90.0-7.el8_10.x86_64 95/106 - Verifying : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 96/106 - Verifying : nss-sysinit-3.90.0-7.el8_10.x86_64 97/106 - Verifying : nss-util-3.90.0-7.el8_10.x86_64 98/106 - Verifying : pango-1.42.4-8.el8.x86_64 99/106 - Verifying : pixman-0.38.4-4.el8.x86_64 100/106 - Verifying : rest-0.8.1-2.el8.x86_64 101/106 - Verifying : ttmkfdir-3.0.9-54.el8.x86_64 102/106 - Verifying : tzdata-java-2024a-1.0.1.el8.noarch 103/106 - Verifying : xkeyboard-config-2.28-1.el8.noarch 104/106 - Verifying : xorg-x11-font-utils-1:7.5-41.el8.x86_64 105/106 - Verifying : xorg-x11-fonts-Type1-7.5-19.el8.noarch 106/106 - -Installed: - abattis-cantarell-fonts-0.0.25-6.el8.noarch - adwaita-cursor-theme-3.28.0-3.el8.noarch - adwaita-icon-theme-3.28.0-3.el8.noarch - alsa-lib-1.2.10-2.el8.x86_64 - at-spi2-atk-2.26.2-1.el8.x86_64 - at-spi2-core-2.28.0-1.el8.x86_64 - atk-2.28.1-1.el8.x86_64 - avahi-libs-0.7-27.el8.x86_64 - cairo-1.15.12-6.el8.x86_64 - cairo-gobject-1.15.12-6.el8.x86_64 - colord-libs-1.4.2-1.el8.x86_64 - copy-jdk-configs-4.0-2.el8.noarch - cpio-2.12-11.el8.x86_64 - crypto-policies-scripts-20230731-1.git3177e06.el8.noarch - cups-libs-1:2.2.6-60.el8_10.x86_64 - dconf-0.28.0-4.0.1.el8.x86_64 - dejavu-sans-mono-fonts-2.35-7.el8.noarch - dracut-049-233.git20240115.0.1.el8.x86_64 - file-5.33-25.el8.x86_64 - fribidi-1.0.4-9.el8.x86_64 - gdk-pixbuf2-2.36.12-6.el8_10.x86_64 - gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 - gettext-0.19.8.1-17.el8.x86_64 - gettext-libs-0.19.8.1-17.el8.x86_64 - glib-networking-2.56.1-1.1.el8.x86_64 - graphite2-1.3.10-10.el8.x86_64 - grub2-common-1:2.02-156.0.2.el8.noarch - grub2-tools-1:2.02-156.0.2.el8.x86_64 - grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 - grubby-8.40-49.0.2.el8.x86_64 - gsettings-desktop-schemas-3.32.0-6.el8.x86_64 - gtk-update-icon-cache-3.22.30-11.el8.x86_64 - gtk3-3.22.30-11.el8.x86_64 - hardlink-1:1.3-6.el8.x86_64 - harfbuzz-1.7.5-4.el8.x86_64 - hicolor-icon-theme-0.17-2.el8.noarch - jasper-libs-2.0.14-5.el8.x86_64 - java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 - java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x86_64 - java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86_64 - javapackages-filesystem-5.3.0-1.module+el8+5136+7ff78f74.noarch - jbigkit-libs-2.1-14.el8.x86_64 - json-glib-1.4.4-1.el8.x86_64 - kbd-2.0.4-11.el8.x86_64 - kbd-legacy-2.0.4-11.el8.noarch - kbd-misc-2.0.4-11.el8.noarch - lcms2-2.9-2.el8.x86_64 - libX11-1.6.8-8.el8.x86_64 - libX11-common-1.6.8-8.el8.noarch - libXau-1.0.9-3.el8.x86_64 - libXcomposite-0.4.4-14.el8.x86_64 - libXcursor-1.1.15-3.el8.x86_64 - libXdamage-1.1.4-14.el8.x86_64 - libXext-1.3.4-1.el8.x86_64 - libXfixes-5.0.3-7.el8.x86_64 - libXft-2.3.3-1.el8.x86_64 - libXi-1.7.10-1.el8.x86_64 - libXinerama-1.1.4-1.el8.x86_64 - libXrandr-1.5.2-1.el8.x86_64 - libXrender-0.9.10-7.el8.x86_64 - libXtst-1.2.3-7.el8.x86_64 - libcroco-0.6.12-4.el8_2.1.x86_64 - libdatrie-0.2.9-7.el8.x86_64 - libepoxy-1.5.8-1.el8.x86_64 - libfontenc-1.1.3-8.el8.x86_64 - libgomp-8.5.0-22.0.1.el8_10.x86_64 - libgusb-0.3.0-1.el8.x86_64 - libjpeg-turbo-1.5.3-12.el8.x86_64 - libkcapi-1.4.0-2.0.1.el8.x86_64 - libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 - libmodman-2.0.1-17.el8.x86_64 - libpkgconf-1.4.2-1.el8.x86_64 - libproxy-0.4.15-5.2.el8.x86_64 - libsoup-2.62.3-5.el8.x86_64 - libthai-0.1.27-2.el8.x86_64 - libtiff-4.0.9-32.el8_10.x86_64 - libwayland-client-1.21.0-1.el8.x86_64 - libwayland-cursor-1.21.0-1.el8.x86_64 - libwayland-egl-1.21.0-1.el8.x86_64 - libxcb-1.13.1-1.el8.x86_64 - libxkbcommon-0.9.1-1.el8.x86_64 - lksctp-tools-1.0.18-3.el8.x86_64 - lua-5.3.4-12.el8.x86_64 - memstrack-0.2.5-2.el8.x86_64 - nspr-4.35.0-1.el8_8.x86_64 - nss-3.90.0-7.el8_10.x86_64 - nss-softokn-3.90.0-7.el8_10.x86_64 - nss-softokn-freebl-3.90.0-7.el8_10.x86_64 - nss-sysinit-3.90.0-7.el8_10.x86_64 - nss-util-3.90.0-7.el8_10.x86_64 - os-prober-1.74-9.0.1.el8.x86_64 - pango-1.42.4-8.el8.x86_64 - pigz-2.4-4.el8.x86_64 - pixman-0.38.4-4.el8.x86_64 - pkgconf-1.4.2-1.el8.x86_64 - pkgconf-m4-1.4.2-1.el8.noarch - pkgconf-pkg-config-1.4.2-1.el8.x86_64 - rest-0.8.1-2.el8.x86_64 - shared-mime-info-1.9-4.el8.x86_64 - systemd-udev-239-78.0.4.el8.x86_64 - ttmkfdir-3.0.9-54.el8.x86_64 - tzdata-java-2024a-1.0.1.el8.noarch - xkeyboard-config-2.28-1.el8.noarch - xorg-x11-font-utils-1:7.5-41.el8.x86_64 - xorg-x11-fonts-Type1-7.5-19.el8.noarch - xz-5.2.4-4.el8_6.x86_64 - -Complete! -Last metadata expiration check: 0:00:23 ago on Tue 20 Aug 2024 08:55:14 AM UTC. -Package iproute-6.2.0-5.el8_9.x86_64 is already installed. -Dependencies resolved. -================================================================================ - Package Architecture Version Repository Size -================================================================================ -Upgrading: - iproute x86_64 6.2.0-6.el8_10 ol8_baseos_latest 853 k - -Transaction Summary -================================================================================ -Upgrade 1 Package - -Total download size: 853 k -Downloading Packages: -iproute-6.2.0-6.el8_10.x86_64.rpm 4.2 MB/s | 853 kB 00:00 --------------------------------------------------------------------------------- -Total 4.2 MB/s | 853 kB 00:00 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Preparing : 1/1 - Upgrading : iproute-6.2.0-6.el8_10.x86_64 1/2 - Cleanup : iproute-6.2.0-5.el8_9.x86_64 2/2 - Running scriptlet: iproute-6.2.0-5.el8_9.x86_64 2/2 - Verifying : iproute-6.2.0-6.el8_10.x86_64 1/2 - Verifying : iproute-6.2.0-5.el8_9.x86_64 2/2 - -Upgraded: - iproute-6.2.0-6.el8_10.x86_64 - -Complete! -24 files removed -Removing intermediate container fe168b01f3ad - ---> 791878694a50 -Step 5/12 : RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm - ---> Running in 59d7143da358 - % Total % Received % Xferd Average Speed Time Time Time Current - Dload Upload Total Spent Left Speed -100 108M 100 108M 0 0 1440k 0 0:01:16 0:01:16 --:--:-- 1578k -Removing intermediate container 59d7143da358 - ---> 17c4534293e5 -Step 6/12 : RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm - ---> Running in 84b1cbffdc51 -Verifying... ######################################## -Preparing... ######################################## -Updating / installing... -ords-23.4.0-8.el8 ######################################## -INFO: Before starting ORDS service, run the below command as user oracle: - ords --config /etc/ords/config install -Removing intermediate container 84b1cbffdc51 - ---> 6e7151b79588 -Step 7/12 : RUN mkdir -p $ORDS_HOME/doc_root && mkdir -p $ORDS_HOME/error && mkdir -p $ORDS_HOME/secrets && chmod ug+x $ORDS_HOME/*.sh && groupadd -g 54322 dba && usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && chown -R oracle:dba $ORDS_HOME && echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - ---> Running in 66e5db5f343f -Removing intermediate container 66e5db5f343f - ---> 0523dc897bf4 -Step 8/12 : USER oracle - ---> Running in ffda8495ac77 -Removing intermediate container ffda8495ac77 - ---> 162acd4d0b93 -Step 9/12 : WORKDIR /home/oracle - ---> Running in 8c14310ffbc7 -Removing intermediate container 8c14310ffbc7 - ---> c8dae809e772 -Step 10/12 : VOLUME ["$ORDS_HOME/config/ords"] - ---> Running in ed64548fd997 -Removing intermediate container ed64548fd997 - ---> 22e2c99247b0 -Step 11/12 : EXPOSE 8888 - ---> Running in 921f7c85d61d -Removing intermediate container 921f7c85d61d - ---> e5d503c92224 -Step 12/12 : CMD $ORDS_HOME/$RUN_FILE - ---> Running in cad487298d63 -Removing intermediate container cad487298d63 - ---> fdb17aa242f8 -Successfully built fdb17aa242f8 -Successfully tagged oracle/ords-dboper:latest -08:57:18 oracle@mitk01:# - diff --git a/docs/multitenant/usecase01/logfiles/openssl_execution.log b/docs/multitenant/usecase01/logfiles/openssl_execution.log deleted file mode 100644 index e3915a21..00000000 --- a/docs/multitenant/usecase01/logfiles/openssl_execution.log +++ /dev/null @@ -1,19 +0,0 @@ -CREATING TLS CERTIFICATES -/usr/bin/openssl genrsa -out ca.key 2048 -Generating RSA private key, 2048 bit long modulus (2 primes) -......................+++++ -..................................................+++++ -e is 65537 (0x010001) -/usr/bin/openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost Root CA " -out ca.crt -/usr/bin/openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost" -out server.csr -Generating a RSA private key -...........+++++ -...........................................+++++ -writing new private key to 'tls.key' ------ -/usr/bin/echo "subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com" > extfile.txt -/usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -Signature ok -subject=C = US, ST = California, L = SanFrancisco, O = "oracle ", CN = "cdb-dev-ords.oracle-database-operator-system ", CN = localhost -Getting CA Private Key - diff --git a/docs/multitenant/usecase01/logfiles/ordsconfig.log b/docs/multitenant/usecase01/logfiles/ordsconfig.log deleted file mode 100644 index b787b752..00000000 --- a/docs/multitenant/usecase01/logfiles/ordsconfig.log +++ /dev/null @@ -1,39 +0,0 @@ -ORDS: Release 23.4 Production on Tue Aug 20 07:48:44 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Database pool: default - -Setting Value Source ------------------------------------------ -------------------------------------------------- ----------- -database.api.enabled true Global -database.api.management.services.disabled false Global -db.cdb.adminUser C##DBAPI_CDB_ADMIN AS SYSDBA Pool -db.cdb.adminUser.password ****** Pool Wallet -db.connectionType customurl Pool -db.customURL jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90 Pool - )(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNEC - T_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL= - TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONL - Y))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST= - scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNEC - T_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -db.password ****** Pool Wallet -db.serviceNameSuffix Pool -db.username ORDS_PUBLIC_USER Pool -error.externalPath /opt/oracle/ords/error Global -jdbc.InitialLimit 50 Pool -jdbc.MaxLimit 100 Pool -misc.pagination.maxRows 1000 Pool -plsql.gateway.mode disabled Pool -restEnabledSql.active true Pool -security.requestValidationFunction ords_util.authorize_plsql_gateway Pool -security.verifySSL true Global -standalone.access.log /home/oracle Global -standalone.https.cert /opt/oracle/ords//secrets/tls.crt Global -standalone.https.cert.key /opt/oracle/ords//secrets/tls.key Global -standalone.https.port 8888 Global - diff --git a/docs/multitenant/usecase01/makefile b/docs/multitenant/usecase01/makefile deleted file mode 100644 index d4176c75..00000000 --- a/docs/multitenant/usecase01/makefile +++ /dev/null @@ -1,284 +0,0 @@ -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# -# ___ -# / _ \ _ __ _ __ _ __ ___ _ __ ___ -# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ -# | |_| | | | | |_) | | | __/ | | | | | -# \___/|_| |_| .__/|_| \___|_| |_| |_| -# |_| -# ____ _ _ _ -# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ -# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| -# | |__| (_) | | | | |_| | | (_) | | | __/ | -# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| -# -# -# This makefile helps to speed up the kubectl commands executions to deploy and test -# the OnPremises operator. Although it has few functionality you can adapt to your needs -# by adding much more targets. -# -# Quick start: -# ~~~~~~~~~~~ -# -# - Copy files of tab.1 in the makefile directory. -# - Edit the secret files and other yaml files with the correct credential as -# specified in the documentation. -# - Edit makefile updating variables of tab.2 -# - Execute commands of tab.3 "make step1" "make step2" "make step3".... -# -# Tab.1 - List of required files -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Opertaor yaml file | -# +-----------------------------+---------------------------------------------+ -# |cdb_secret.yaml | Secret file for the rest server pod | -# +-----------------------------+---------------------------------------------+ -# |pdb_secret.yaml | Secret file for the pdb creation | -# +-----------------------------+---------------------------------------------+ -# |tde_secret.yaml | Secret file for the tablepsace enc. | -# +-----------------------------+---------------------------------------------+ -# |cdb_create.yaml | Rest server pod creation | -# +-----------------------------+---------------------------------------------+ -# |pdb_create.yaml | Pluggable database creation | -# +-----------------------------+---------------------------------------------+ -# |pdb_close.yaml | Close pluggable database | -# +-----------------------------+---------------------------------------------+ -# |pdb_open.yaml | Open pluggable database | -# +-----------------------------+---------------------------------------------+ -# |pdb_map.yaml | Map an existing pdb | -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Database operator | -# +-----------------------------+---------------------------------------------+ -# |Dockerfiles | Dockerfile for CBD | -# +-----------------------------+---------------------------------------------+ -# |runOrdsSSL.sh | Init script executed by Dockerfile | -# +-----------------------------+---------------------------------------------+ -# -# Tab.2 - List of variables -# ~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |OCIR | Your image registry | -# +-----------------------------+---------------------------------------------+ -# |OCIRPATH | Path of the image in your registry | -# +-----------------------------+---------------------------------------------+ -# -# Tab.3 - Execution steps -# ~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# | MAKEFILE TARGETS LIST | -# | ----- ooo ----- | -# | - TARGET - - DESCRIPTION - | -# +-----------------------------+-------------------------------------+-------+ -# |step1 | Build rest server images | | -# +-----------------------------+-------------------------------------+ REST | -# |step2 | Tag the immages | SRV | -# +-----------------------------+-------------------------------------+ IMG | -# |step3 | Push the image into the repository | | -# +-----------------------------+-------------------------------------+-------+ -# |step4 | Load webhook certmanager | DB | -# +-----------------------------+-------------------------------------+ OPER | -# |step5 | Create the db operator | | -# +-----------------------------+-------------------------------------+-------+ -# |step6 | Create tls certificates | T | -# +-----------------------------+-------------------------------------+ L | -# |step7 | Create tls secret | S | -# +-----------------------------+---------------------------------------------+ -# |step8 | Create database secrets | -# +-----------------------------+---------------------------------------------+ -# |step9 | Create restserver pod | -# | | +---------------------------------------------+ -# | +---> checkstep9 | Monitor the executions | -# +-----------------------------+---------------------------------------------+ -# |step10 | Create pluggable database | -# | | +---------------------------------------------+ -# | +---> checkpdb | Monitor PDB status | -# +-----------------------------+---------------------------------------------+ -# |step11 | Close pluggable database | -# +-----------------------------+---------------------------------------------+ -# |step12 | Open pluggable database | -# +-----------------------------+---------------------------------------------+ -# |step13 | Map pluggable database | -# +-----------------------------+---------------------------------------------+ -# | Before testing step13 delete the crd: | -# | kubectl delete pdb pdb1 -n oracle-database-operator-system | -# +---------------------------------------------------------------------------+ -# |step14 | delete pdb | -# +-----------------------------+---------------------------------------------+ -# | DIAGNOSTIC TARGETS | -# +-----------------------------+---------------------------------------------+ -# | dump | Dump pods info into a file | -# +-----------------------------+---------------------------------------------+ -# | reloadop | Reload the db operator | -# +-----------------------------+---------------------------------------------+ -# | login | Login into cdb pod | -# +-----------------------------+---------------------------------------------+ - - -################ TAB 2 VARIABLES ############ -OCIR=[...........YOUR REGISTRY...........] -OCIRPATH=[...PATH IN YOUR REGISTRY.....]/$(REST_SERVER)-dboper:$(ORDSVERSION) -############################################# -REST_SERVER=ords -ORDSVERSION=latest -DOCKER=/usr/bin/docker -KUBECTL=/usr/bin/kubectl -ORDS=/usr/local/bin/ords -CONFIG=/etc/ords/config -IMAGE=oracle/$(REST_SERVER)-dboper:$(ORDSVERSION) -DBOPERATOR=oracle-database-operator.yaml -URLPATH=/_/db-api/stable/database/pdbs/ -OPENSSL=/usr/bin/openssl -ORDSPORT=8888 -MAKE=/usr/bin/make -DOCKERFILE=../../../ords/Dockerfile -RUNSCRIPT=../../../ords/runOrdsSSL.sh -ORDSIMGDIR=../../../ords -RM=/usr/bin/rm -CP=/usr/bin/cp -ECHO=/usr/bin/echo -NAMESPACE=oracle-database-operator-system -CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -CDB_SECRET=cdb_secret.yaml -PDB_SECRET=pdb_secret.yaml -TDE_SECRET=tde_secret.yaml -CDB=cdb_create.yaml -PDB=pdb_create.yaml -PDB_CLOSE=pdb_close.yaml -PDB_OPEN=pdb_open.yaml -PDB_MAP=pdb_map.yaml -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -COMPANY=oracle -LOCALHOST=localhost -RESTPREFIX=cdb-dev - -step1: createimage -step2: tagimage -step3: push -step4: certmanager -step5: dboperator -step6: tlscert -step7: tlssecret -step8: dbsecret -step9: cdb -step10: pdb -step11: close -step12: open -step13: map -step14: delete - -checkstep9: checkcdb - - -createimage: - $(DOCKER) build -t $(IMAGE) $(ORDSIMGDIR) - -tagimage: - @echo "TAG IMAGE" - $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) - -push: - @echo "PUSH IMAGE INTO THE REGISTRY" - $(DOCKER) push $(OCIR)$(OCIRPATH) - -certmanager: - @echo "WEBHOOK CERT MANAGER" - $(KUBECTL) apply -f $(CERTMANAGER) - -dboperator: - @echo "ORACLE DATABASE OPERATOR" - $(KUBECTL) apply -f $(DBOPERATOR) - - -#C: Country -#ST: State -#L: locality (city) -#O: Organization Name Organization Unit -#CN: Common Name - -tlscert: - @echo "CREATING TLS CERTIFICATES" - $(OPENSSL) genrsa -out ca.key 2048 - $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE) /CN=$(LOCALHOST)" -out server.csr - $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER).$(NAMESPACE),DNS:www.example.com" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) - -tlssecret: - @echo "CREATING TLS SECRETS" - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(NAMESPACE) - -dbsecret: - @echo "CREATING DB SECRETS" - $(KUBECTL) apply -f $(CDB_SECRET) -n $(NAMESPACE) - $(KUBECTL) apply -f $(PDB_SECRET) -n $(NAMESPACE) - $(KUBECTL) apply -f $(TDE_SECRET) -n $(NAMESPACE) - -cdb: - @echo "CREATING REST SRV POD" - $(KUBECTL) apply -f $(CDB) - -checkcdb: - $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(NAMESPACE) - -pdb: - $(KUBECTL) apply -f $(PDB) - -close: - $(KUBECTL) apply -f $(PDB_CLOSE) - -open: - $(KUBECTL) apply -f $(PDB_OPEN) - -map: - $(KUBECTL) apply -f $(PDB_MAP) - -checkpdb: - $(KUBECTL) get pdbs -n $(NAMESPACE) - -delete: - $(KUBECTL) apply -f pdb_delete.yaml - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) - @echo "CDB LOG DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs `$(KUBECTL) get pods -n $(NAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(NAMESPACE) >>$(DIAGFILE) - @echo "SECRET DMP" >>$(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) get secrets -o yaml -n $(NAMESPACE) >> $(DIAGFILE) - @echo "CDB/PDB DMP" >> $(DIAGFILE) - $(KUBECTL) get pdbs -o yaml -n $(NAMESPACE) >> $(DIAGFILE) - $(KUBECTL) get cdb -o yaml -n $(NAMESPACE) >> $(DIAGFILE) - @echo "CLUSTER INFO" >> $(DIAGFILE) - $(KUBECTL) get nodes -o wide - $(KUBECTL) get svc --namespace=kube-system - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(NAMESPACE) -o yaml | kubectl replace --force -f - - -login: - $(KUBECTL) exec -it `$(KUBECTL) get pods -n $(NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(NAMESPACE) bash - diff --git a/docs/multitenant/usecase03/Dockerfile b/docs/multitenant/usecase03/Dockerfile deleted file mode 100644 index 772a7e6d..00000000 --- a/docs/multitenant/usecase03/Dockerfile +++ /dev/null @@ -1,80 +0,0 @@ -## Copyright (c) 2022 Oracle and/or its affiliates. -## -## The Universal Permissive License (UPL), Version 1.0 -## -## Subject to the condition set forth below, permission is hereby granted to any -## person obtaining a copy of this software, associated documentation and/or data -## (collectively the "Software"), free of charge and under any and all copyright -## rights in the Software, and any and all patent rights owned or freely -## licensable by each licensor hereunder covering either (i) the unmodified -## Software as contributed to or provided by such licensor, or (ii) the Larger -## Works (as defined below), to deal in both -## -## (a) the Software, and -## (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -## one is included with the Software (each a "Larger Work" to which the Software -## is contributed by such licensors), -## -## without restriction, including without limitation the rights to copy, create -## derivative works of, display, perform, and distribute the Software and make, -## use, sell, offer for sale, import, export, have made, and have sold the -## Software and the Larger Work(s), and to sublicense the foregoing rights on -## either these or other terms. -## -## This license is subject to the following condition: -## The above copyright notice and either this complete permission notice or at -## a minimum a reference to the UPL must be included in all copies or -## substantial portions of the Software. -## -## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -## SOFTWARE. - -FROM container-registry.oracle.com/java/jdk:latest - -# Environment variables required for this build (do NOT change) -# ------------------------------------------------------------- -ENV ORDS_HOME=/opt/oracle/ords/ \ - RUN_FILE="runOrdsSSL.sh" \ - ORDSVERSION=23.4.0-8 - -# Copy binaries -# ------------- -COPY $RUN_FILE $ORDS_HOME - -RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && \ - yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && \ - yum -y install java-11-openjdk-devel && \ - yum -y install iproute && \ - yum clean all - -RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm - -RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm - -# Setup filesystem and oracle user -# -------------------------------- -RUN mkdir -p $ORDS_HOME/doc_root && \ - mkdir -p $ORDS_HOME/error && \ - mkdir -p $ORDS_HOME/secrets && \ - chmod ug+x $ORDS_HOME/*.sh && \ - groupadd -g 54322 dba && \ - usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && \ - chown -R oracle:dba $ORDS_HOME && \ - echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - -# Finalize setup -# ------------------- -USER oracle -WORKDIR /home/oracle - -VOLUME ["$ORDS_HOME/config/ords"] -EXPOSE 8888 - -# Define default command to start Ords Services -CMD $ORDS_HOME/$RUN_FILE - diff --git a/docs/multitenant/usecase03/NamespaceSegregation.png b/docs/multitenant/usecase03/NamespaceSegregation.png deleted file mode 100644 index bcb0ae77818e87ed64bf248bc0c173a4aac28c73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 270813 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≪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 S*V+lde;zJA&et8oss9JAh>QII diff --git a/docs/multitenant/usecase03/README.md b/docs/multitenant/usecase03/README.md deleted file mode 100644 index c06368cd..00000000 --- a/docs/multitenant/usecase03/README.md +++ /dev/null @@ -1,268 +0,0 @@ - - - -# STEP BY STEP (NAMESPACE SEGREGATION) - -- [STEP BY STEP (NAMESPACE SEGREGATION)](#step-by-step-namespace-segregation) - - [INTRODUCTION](#introduction) - - [GIT CLONE ORACLE DATABASE OPERATOR PROJECT](#git-clone-oracle-database-operator-project) - - [NAMESPACE CREATION](#namespace-creation) - - [WEBHOOK CERTIFICATES](#webhook-certificates) - - [ORACLE DATABASE OPERATOR](#oracle-database-operator) - - [CREATE PDB AND CDB SECRETS](#create-pdb-and-cdb-secrets) - - [CREATE TLS CERTIFICATE](#create-tls-certificate) - - [REST SERVER IMAGE CREATION](#rest-server-image-creation) - - [CDB POD CREATION](#cdb-pod-creation) - - [PDB CREATION](#pdb-creation) - - [MAKEFILE](#makefile) - - -### INTRODUCTION - -> ☞ This folder contains the yaml files required to configure and manage cdb and pdb in different namespaces. The main change here is the possibility to specify the namespace where CDB will be created, this implies the introduction of new parameter at PDB level in order to specify the CDB namespace. - -Tasks performed in the usecase03 are the same ones of the other usecase01 with the exception that controller pods cdb pods and pdb crd are running in different namespaces. You must be aware of the fact that secrets must be created in the proper namespaces; cdb secrets go into cdb namespace , pdb secrets go into pdbnamespace while certificate secrets need to be created in every namespace. - - -| yaml file parameters | value | description /ords parameter | -|-------------- |--------------------------- |-------------------------------------------------| -| ☞ cdbNamespace | | Cdb namespace | -| dbserver | or | [--db-hostname][1] | -| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | -| port | | [--db-port][2] | -| cdbName | | Container Name | -| name | | Ords podname prefix in cdb.yaml | -| name | | pdb resource in pdb.yaml | -| ordsImage | /ords-dboper:latest|My public container registry | -| pdbName | | Pluggable database name | -| servicename | | [--db-servicename][3] | -| sysadmin_user | | [--admin-user][adminuser] | -| sysadmin_pwd | | [--password-stdin][pwdstdin] | -| cdbadmin_user | | [db.cdb.adminUser][1] | -| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | -| webserver_user| | [https user][http] NOT A DB USER | -| webserver_pwd | | [http user password][http] | -| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | -| pdbTlsKey | | [standalone.https.cert.key][key] | -| pdbTlsCrt | | [standalone.https.cert][cr] | -| pdbTlsCat | | certificate authority | -| xmlFileName | | path for the unplug and plug operation | -| srcPdbName | | name of the database to be cloned | -| fileNameConversions | | used for database cloning | -| tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | -| tdeExport | | [tdeExport] | -| tdeSecret | | [tdeSecret][tdeSecret] | -| tdePassword | | [tdeSecret][tdeSecret] | -| assertivePdbDeletion | boolean | [turn on imperative approach on crd deleteion][imperative] | - -![generla schema](./NamespaceSegregation.png) - -### GIT CLONE ORACLE DATABASE OPERATOR PROJECT - -```bash -git clone https://github.com/oracle/oracle-database-operator.git -cd oracle-database-operator/docs/multitenant/usecase03 -``` -### NAMESPACE CREATION - -We need first to create two different namespaces (**cdbnamespace**,**pdbnamespace**) using ns_pdb_namespace.yaml and ns_cdb_namespace.yaml - -```bash -kubectl apply -f ns_pdb_namespace.yaml -kubectl apply -f ns_cdb_namespace.yaml -``` - -### WEBHOOK CERTIFICATES -Create cert manager and verify the status - -```bash -kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -``` - -```bash -kubectl get pods --namespace cert-manager -NAME READY STATUS RESTARTS AGE -cert-manager-75997f4b44-4nf5c 1/1 Running 1 9d -cert-manager-cainjector-769785cd7b-mzfq5 1/1 Running 1 9d -cert-manager-webhook-6bc9944d78-tslrp 1/1 Running 1 9d -``` - -### ORACLE DATABASE OPERATOR - -Create the oracle database operator using oracle-database-operator.yaml -```bash -cd oracle-database-operator -kubectl apply -f oracle-database-operator.yaml -cd - -``` - -[operator creation log](operator_creation_log.txt) -### CREATE PDB AND CDB SECRETS - -Update secrets files with your base64 encodede password. - -```bash -echo ImAdemoPassword | base64 -SW1BZGVtb1Bhc3N3b3JkCg== -``` -Apply the cdb_secret and pdb_secret yaml file to generate credential information in each namespace. - -``` -kubectl apply -f cdb_secret.yaml -kubectl apply -f pdb_secret.yaml -``` -> ☞ Note that https credential needs to be replicated in any secret file. It is possible to improve configuration by creating a dedicated namespace for https credential in order to specify this information only once. - -Namespace segregation enables the capability of deploying and manages pluggable database without the cdb administrative passwords. - -### CREATE TLS CERTIFICATE - -Here follow an example of script shell that can be used to create secret certificates in each namespace involved in the kubernets multi tenant architecture - -```bash -#!/bin/bash -export CDB_NAMESPACE=cdbnamespace -export PDB_NAMESPACE=pdbnamespace -export OPR_NAMESPACE=oracle-database-operator-system -export SKEY=tls.key -export SCRT=tls.crt -export CART=ca.crt -export COMPANY=oracle -export REST_SERVER=ords - -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr -echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} - -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} -``` -after all secrets creation you shoud have the following pattern - -```bash -kubectl get secrets -n oracle-database-operator-system -NAME TYPE DATA AGE -db-ca Opaque 1 6d5h -db-tls kubernetes.io/tls 2 6d5h -webhook-server-cert kubernetes.io/tls 3 6d15h - - -kubectl get secrets -n cdbnamespace -NAME TYPE DATA AGE -cdb1-secret Opaque 6 6d15h -db-ca Opaque 1 6d6h -db-tls kubernetes.io/tls 2 6d6h - - -kubectl get secrets -n pdbnamespace -NAME TYPE DATA AGE -db-ca Opaque 1 6d6h -db-tls kubernetes.io/tls 2 6d6h -pdb1-secret Opaque 4 2d16h -tde1-secret Opaque 2 22h -``` -### REST SERVER IMAGE CREATION - -```bash -cd oracle-database-operator/ords -docker build -t oracle/ords-dboper:latest . -docker tag oracle/ords-dboper:latest [path_of_your_registry]/ords-dboper:latest -docker push [path_of_your_registry]/ords-dboper.latest -cd - -``` - -### CDB POD CREATION - -**note:** - Before creating the CDB pod make sure that all the pluggable databases in the container DB are open. - - - -Update the cdb_create.yaml with the path of the image generated before to create CDB pod - -```bash -kubectl apply -f cdb_create.yaml -``` - -Verify the status of the operation and cdb pod existence using the following commands - -```bash -## check the pod creation -kubectl get pods -n cdbnamespace - -## check the rest server log after pod creation -kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace - -##login to the pod for further debug and information gathering -kubectl exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash -``` - -[log cdb creation](./cdb_creation_log.txt) - -### PDB CREATION - -Apply the the pdb_create.yaml file to create a new pdb , after pdb creation you should be able to get pdb details using **kubectl get** command - -```bash -kubectl apply -f pdb_create.yaml -``` - -```bash -#!/bin/bash -#checkpdbs.sh -kubectl get pdbs -n pdbnamespace -o=jsonpath='{range .items[*]} -{"\n==================================================================\n"} -{"CDB="}{.metadata.labels.cdb} -{"K8SNAME="}{.metadata.name} -{"PDBNAME="}{.spec.pdbName} -{"OPENMODE="}{.status.openMode} -{"ACTION="}{.status.action} -{"MSG="}{.status.msg} -{"\n"}{end}' -``` - -```bash -./checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE=READ WRITE -ACTION=CREATE -MSG=Success - -``` -[pdb creation log](./pdb_creation_log.txt) - -### MAKEFILE - -In order to facilitate the command execution use the [makefile](./makefile) available target details are exposed in the following tables. - -|target |Action | -|-----------------------------|-------------------------------------| -|step1 | Build rest server images | -|step2 | Tag the immages | -|step3 | Push the image into the repository | -|step4 | Load webhook certmanager | -|step5 | Create the db operator | -|step6 | Create tls certificates | -|step7 | Create tls secret | -|step8 | Create database secrets | -|step9 | Create restserver pod | -|checkstep9 | Monitor the executions | -|step10 | Create pluggable database | -|checkpdb | Monitor PDB status | -|dump | Dump pods info into a file | -|reloadop | Reload the db operator | -|login | Login into cdb pod | - -[imperative]:https://kubernetes.io/docs/concepts/overview/working-with-objects/object-management/ - - - diff --git a/docs/multitenant/usecase03/cdb_create.yaml b/docs/multitenant/usecase03/cdb_create.yaml deleted file mode 100644 index d3b5e04f..00000000 --- a/docs/multitenant/usecase03/cdb_create.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: cdbnamespace -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - diff --git a/docs/multitenant/usecase03/cdb_creation_log.txt b/docs/multitenant/usecase03/cdb_creation_log.txt deleted file mode 100644 index 8c7dc161..00000000 --- a/docs/multitenant/usecase03/cdb_creation_log.txt +++ /dev/null @@ -1,336 +0,0 @@ -kubectl get pods -n cdbnamespace -NAME READY STATUS RESTARTS AGE -cdb-dev-ords-rs-pgqqh 0/1 ContainerCreating 0 1s - -kubectl get pods -n cdbnamespace -NAME READY STATUS RESTARTS AGE -cdb-dev-ords-rs-pgqqh 1/1 Running 0 6s - -kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M -NOT_INSTALLED=2 - SETUP -==================================================== -CONFIG=/etc/ords/config -total 0 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:20 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.connectionType was set to: customurl in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:21 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.customURL was set to: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:23 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: security.requestValidationFunction was set to: false in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:25 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.MaxLimit was set to: 100 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:27 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.InitialLimit was set to: 50 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:29 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: error.externalPath was set to: /opt/oracle/ords/error -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:31 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.access.log was set to: /home/oracle -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:32 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.port was set to: 8888 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:34 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert was set to: /opt/oracle/ords//secrets/tls.crt -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:36 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert.key was set to: /opt/oracle/ords//secrets/tls.key -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:38 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: restEnabledSql.active was set to: true in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:40 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: security.verifySSL was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:42 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.enabled was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:43 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: plsql.gateway.mode was set to: disabled in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:45 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.management.services.disabled was set to: false -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:47 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: misc.pagination.maxRows was set to: 1000 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:49 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser was set to: C##DBAPI_CDB_ADMIN AS SYSDBA in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:51 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser.password was set to: ****** in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:53 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Created user welcome in file /etc/ords/config/global/credentials -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:55 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Oracle REST Data Services - Non-Interactive Install - -Retrieving information.. -Completed verifying Oracle REST Data Services schema version 23.3.0.r2891830. -Connecting to database user: ORDS_PUBLIC_USER url: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -The setting named: db.serviceNameSuffix was set to: in configuration: default -The setting named: db.username was set to: ORDS_PUBLIC_USER in configuration: default -The setting named: db.password was set to: ****** in configuration: default -The setting named: security.requestValidationFunction was set to: ords_util.authorize_plsql_gateway in configuration: default -2024-01-25T17:17:58.898Z INFO Oracle REST Data Services schema version 23.3.0.r2891830 is installed. -2024-01-25T17:17:58.900Z INFO To run in standalone mode, use the ords serve command: -2024-01-25T17:17:58.900Z INFO ords --config /etc/ords/config serve -2024-01-25T17:17:58.900Z INFO Visit the ORDS Documentation to access tutorials, developer guides and more to help you get started with the new ORDS Command Line Interface (http://oracle.com/rest). -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:18:00 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -2024-01-25T17:18:00.960Z INFO HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8080 -2024-01-25T17:18:00.963Z INFO HTTPS and HTTPS/2 listening on host: 0.0.0.0 port: 8888 -2024-01-25T17:18:00.980Z INFO Disabling document root because the specified folder does not exist: /etc/ords/config/global/doc_root -2024-01-25T17:18:00.981Z INFO Default forwarding from / to contextRoot configured. -2024-01-25T17:18:06.634Z INFO Configuration properties for: |default|lo| -db.serviceNameSuffix= -java.specification.version=21 -conf.use.wallet=true -database.api.management.services.disabled=false -sun.jnu.encoding=UTF-8 -user.region=US -java.class.path=/opt/oracle/ords/ords.war -java.vm.vendor=Oracle Corporation -standalone.https.cert.key=/opt/oracle/ords//secrets/tls.key -sun.arch.data.model=64 -nashorn.args=--no-deprecation-warning -java.vendor.url=https://java.oracle.com/ -resource.templates.enabled=false -user.timezone=UTC -java.vm.specification.version=21 -os.name=Linux -sun.java.launcher=SUN_STANDARD -user.country=US -sun.boot.library.path=/usr/java/jdk-21/lib -sun.java.command=/opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure -jdk.debug=release -sun.cpu.endian=little -user.home=/home/oracle -oracle.dbtools.launcher.executable.jar.path=/opt/oracle/ords/ords.war -user.language=en -db.cdb.adminUser.password=****** -java.specification.vendor=Oracle Corporation -java.version.date=2023-10-17 -database.api.enabled=true -java.home=/usr/java/jdk-21 -db.username=ORDS_PUBLIC_USER -file.separator=/ -java.vm.compressedOopsMode=32-bit -line.separator= - -restEnabledSql.active=true -java.specification.name=Java Platform API Specification -java.vm.specification.vendor=Oracle Corporation -java.awt.headless=true -standalone.https.cert=/opt/oracle/ords//secrets/tls.crt -db.password=****** -sun.management.compiler=HotSpot 64-Bit Tiered Compilers -security.requestValidationFunction=ords_util.authorize_plsql_gateway -misc.pagination.maxRows=1000 -java.runtime.version=21.0.1+12-LTS-29 -user.name=oracle -error.externalPath=/opt/oracle/ords/error -stdout.encoding=UTF-8 -path.separator=: -db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA -os.version=5.4.17-2136.323.8.1.el7uek.x86_64 -java.runtime.name=Java(TM) SE Runtime Environment -file.encoding=UTF-8 -plsql.gateway.mode=disabled -security.verifySSL=true -standalone.https.port=8888 -java.vm.name=Java HotSpot(TM) 64-Bit Server VM -java.vendor.url.bug=https://bugreport.java.com/bugreport/ -java.io.tmpdir=/tmp -oracle.dbtools.cmdline.ShellCommand=ords -java.version=21.0.1 -user.dir=/home/oracle/keystore -os.arch=amd64 -java.vm.specification.name=Java Virtual Machine Specification -jdbc.MaxLimit=100 -oracle.dbtools.cmdline.home=/opt/oracle/ords -native.encoding=UTF-8 -java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib -java.vendor=Oracle Corporation -java.vm.info=mixed mode, sharing -stderr.encoding=UTF-8 -java.vm.version=21.0.1+12-LTS-29 -sun.io.unicode.encoding=UnicodeLittle -jdbc.InitialLimit=50 -db.connectionType=customurl -java.class.version=65.0 -db.customURL=jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -standalone.access.log=/home/oracle - -2024-01-25T17:18:10.381Z INFO - -Mapped local pools from /etc/ords/config/databases: - /ords/ => default => VALID - - -2024-01-25T17:18:10.532Z INFO Oracle REST Data Services initialized -Oracle REST Data Services version : 23.3.0.r2891830 -Oracle REST Data Services server info: jetty/10.0.17 -Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 21.0.1+12-LTS-29 - - -exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash -[oracle@cdb-dev-ords-rs-pgqqh ~]$ ps -ef|grep java -oracle 1147 1116 10 17:17 ? 00:00:21 /usr/java/jdk-21/bin/java -Doracle.dbtools.cmdline.home=/opt/oracle/ords -Duser.language=en -Duser.region=US -Dfile.encoding=UTF-8 -Djava.awt.headless=true -Dnashorn.args=--no-deprecation-warning -Doracle.dbtools.cmdline.ShellCommand=ords -Duser.timezone=UTC -jar /opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure -oracle 1227 1200 0 17:21 pts/0 00:00:00 grep --color=auto java diff --git a/docs/multitenant/usecase03/cdb_secret.yaml b/docs/multitenant/usecase03/cdb_secret.yaml deleted file mode 100644 index 8f1b6fc9..00000000 --- a/docs/multitenant/usecase03/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: cdbnamespace -type: Opaque -data: - ords_pwd: "[...base64 encoded password...]" - sysadmin_pwd: "[...base64 encoded password...]" - cdbadmin_user: "[...base64 encoded password...]" - cdbadmin_pwd: "[...base64 encoded password...]" - webserver_user: "[...base64 encoded password...]" - webserver_pwd: "[...base64 encoded password...]" diff --git a/docs/multitenant/usecase03/gentlscert.sh b/docs/multitenant/usecase03/gentlscert.sh deleted file mode 100644 index 49e29147..00000000 --- a/docs/multitenant/usecase03/gentlscert.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -export CDB_NAMESPACE=cdbnamespace -export PDB_NAMESPACE=pdbnamespace -export OPR_NAMESPACE=oracle-database-operator-system -export SKEY=tls.key -export SCRT=tls.crt -export CART=ca.crt -export COMPANY=oracle -export REST_SERVER=ords - -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr -echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} - -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} - diff --git a/docs/multitenant/usecase03/makefile b/docs/multitenant/usecase03/makefile deleted file mode 100644 index 7270a5e0..00000000 --- a/docs/multitenant/usecase03/makefile +++ /dev/null @@ -1,285 +0,0 @@ -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# -# ___ -# / _ \ _ __ _ __ _ __ ___ _ __ ___ -# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ -# | |_| | | | | |_) | | | __/ | | | | | -# \___/|_| |_| .__/|_| \___|_| |_| |_| -# |_| -# ____ _ _ _ -# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ -# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| -# | |__| (_) | | | | |_| | | (_) | | | __/ | -# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| -# -# -# This makefile helps to speed up the kubectl commands executions to deploy and test -# the mutlitenant operator. Although it has few functionality you can adapt to your needs -# by adding much more targets. -# -# Quick start: -# ~~~~~~~~~~~ -# -# - Copy files of tab.1 in the makefile directory. -# - Edit the secret files and other yaml files with the correct credential as -# specified in the documentation. -# - Edit makefile updating variables of tab.2 -# - Execute commands of tab.3 "make step1" "make step2" "make step3".... -# -# Tab.1 - List of required files -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Opertaor yaml file | -# +-----------------------------+---------------------------------------------+ -# |cdb_secret.yaml | Secret file for the rest server pod | -# +-----------------------------+---------------------------------------------+ -# |pdb_secret.yaml | Secret file for the pdb creation | -# +-----------------------------+---------------------------------------------+ -# |cdb_create.yaml | Rest server pod creation | -# +-----------------------------+---------------------------------------------+ -# |pdb_create.yaml | Pluggable database creation | -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Database operator | -# +-----------------------------+---------------------------------------------+ -# |Dockerfiles | Dockerfile for CBD | -# +-----------------------------+---------------------------------------------+ -# |runOrdsSSL.sh | Init script executed by Dockerfile | -# +-----------------------------+---------------------------------------------+ -# -# Tab.2 - List of variables -# ~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |OCIR | Your image registry | -# +-----------------------------+---------------------------------------------+ -# |OCIRPATH | Path of the image in your registry | -# +-----------------------------+---------------------------------------------+ -# -# Tab.3 - Execution steps -# ~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# | MAKEFILE TARGETS LIST | -# | ----- ooo ----- | -# | - TARGET - - DESCRIPTION - | -# +-----------------------------+-------------------------------------+-------+ -# |step1 | Build rest server images | | -# +-----------------------------+-------------------------------------+ REST | -# |step2 | Tag the immages | SRV | -# +-----------------------------+-------------------------------------+ IMG | -# |step3 | Push the image into the repository | | -# +-----------------------------+-------------------------------------+-------+ -# |step4 | Load webhook certmanager | DB | -# +-----------------------------+-------------------------------------+ OPER | -# |step5 | Create the db operator | | -# +-----------------------------+-------------------------------------+-------+ -# |step6 | Create tls certificates | T | -# +-----------------------------+-------------------------------------+ L | -# |step7 | Create tls secret | S | -# +-----------------------------+---------------------------------------------+ -# |step8 | Create database secrets | -# +-----------------------------+---------------------------------------------+ -# |step9 | Create restserver pod | -# | | +---------------------------------------------+ -# | +---> checkstep9 | Monitor the executions | -# +-----------------------------+---------------------------------------------+ -# |step10 | Create pluggable database | -# | | +---------------------------------------------+ -# | +---> checkpdb | Monitor PDB status | -# +-----------------------------+---------------------------------------------+ -# | DIAGNOSTIC TARGETS | -# +-----------------------------+---------------------------------------------+ -# | dump | Dump pods info into a file | -# +-----------------------------+---------------------------------------------+ -# | reloadop | Reload the db operator | -# +-----------------------------+---------------------------------------------+ -# | login | Login into cdb pod | -# +-----------------------------+---------------------------------------------+ - - -################ TAB 2 VARIABLES ############ -REST_SERVER=ords -ORDSVERSION=latest - -OCIR=[container registry] -OCIRPATH=$(REST_SERVER)-dboper:$(ORDSVERSION) - -#examples: -#OCIR=lin.ocir.io -#OCIRPATH=/sampletenancy/samplepath/sampledir/$(REST_SERVER)-dboper:$(ORDSVERSION) -############################################# -DOCKER=/usr/bin/docker -KUBECTL=/usr/bin/kubectl -ORDS=/usr/local/bin/ords -CONFIG=/etc/ords/config -IMAGE=oracle/$(REST_SERVER)-dboper:$(ORDSVERSION) -DBOPERATOR=oracle-database-operator.yaml -URLPATH=/_/db-api/stable/database/pdbs/ -OPENSSL=/usr/bin/openssl -ORDSPORT=8888 -MAKE=/usr/bin/make -DOCKERFILE=../../../ords/Dockerfile -RUNSCRIPT=../../../ords/runOrdsSSL.sh -RM=/usr/bin/rm -CP=/bin/cp -ECHO=/usr/bin/echo -CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -CDB_SECRET_YAML=cdb_secret.yaml -PDB_SECRET_YAML=pdb_secret.yaml -TDE_SECRET_YAML=tde_secret.yaml -CDB_NAMESPACE_YAML=ns_namespace_cdb.yaml -PDB_NAMESPACE_YAML=ns_namespace_pdb.yaml -OPR_NAMESPACE=oracle-database-operator-system -PDB_NAMESPACE=$(shell grep namespace $(PDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') -CDB_NAMESPACE=$(shell grep namespace $(CDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') -CDB=cdb_create.yaml -PDB=pdb_create.yaml -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -COMPANY=oracle -LOCALHOST=localhost -RESTPREFIX=cdb-dev - - -step1: createimage -step2: tagimage -step3: push -step4: certmanager -step5: dboperator -step6: tlscert -step7: tlssecret -step8: dbsecret -step9: cdb -step10: pdb - -checkstep9: checkcdb - - -createimage: - @echo "BUILDING CDB IMAGES" - $(CP) $(DOCKERFILE) . - $(CP) $(RUNSCRIPT) . - $(DOCKER) build -t $(IMAGE) . - -tagimage: - @echo "TAG IMAGE" - $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) - -push: - @echo "PUSH IMAGE INTO THE REGISTRY" - $(DOCKER) push $(OCIR)$(OCIRPATH) - -certmanager: - @echo "WEBHOOK CERT MANAGER" - $(KUBECTL) apply -f $(CERTMANAGER) - -dboperator: - @echo "ORACLE DATABASE OPERATOR" - $(KUBECTL) apply -f $(DBOPERATOR) - -namespace: - $(KUBECTL) get namespaces - $(KUBECTL) apply -f $(CDB_NAMESPACE_YAML) - $(KUBECTL) apply -f $(PDB_NAMESPACE_YAML) - $(KUBECTL) get namespaces - - -tlscert: - @echo "CREATING TLS CERTIFICATES" - $(OPENSSL) genrsa -out ca.key 2048 - $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST)" -out server.csr - $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE),DNS:www.example.com" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) - - -tlssecret: - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDB_NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(CDB_NAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDB_NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDB_NAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPR_NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPR_NAMESPACE) - - -dbsecret: - @echo "CREATING DB SECRETS" - $(KUBECTL) apply -f $(CDB_SECRET_YAML) - $(KUBECTL) apply -f $(PDB_SECRET_YAML) - $(KUBECTL) apply -f $(TDE_SECRET_YAML) - - -cdb: - @echo "CREATING REST SRV POD" - $(KUBECTL) apply -f $(CDB) - -checkcdb: - $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) - -pdb: - $(KUBECTL) apply -f $(PDB) - -checkpdb: - $(KUBECTL) get pdbs -n $(OPR_NAMESPACE) - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - @echo "CDB LOG DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs `$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - @echo "SECRET DMP" >>$(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) get secrets -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) - @echo "CDB/PDB DMP" >> $(DIAGFILE) - $(KUBECTL) get pdbs -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) - $(KUBECTL) get cdb -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) - @echo "CLUSTER INFO" >> $(DIAGFILE) - $(KUBECTL) get nodes -o wide - $(KUBECTL) get svc --namespace=kube-system - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - - -login: - $(KUBECTL) exec -it `$(KUBECTL) get pods -n $(CDB_NAMESPACE) |grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) bash - -cdblog: - $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) - - - -xlog1: - $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) - -xlog2: - $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) - -xlog3: - $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) - -checkdep: - $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(OPR_NAMESPACE) - $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(CBD_NAMESPACE) - $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(PDB_NAMESPACE) - - - diff --git a/docs/multitenant/usecase03/ns_namespace_cdb.yaml b/docs/multitenant/usecase03/ns_namespace_cdb.yaml deleted file mode 100644 index f4c6d77b..00000000 --- a/docs/multitenant/usecase03/ns_namespace_cdb.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: cdbnamespace - diff --git a/docs/multitenant/usecase03/ns_namespace_pdb.yaml b/docs/multitenant/usecase03/ns_namespace_pdb.yaml deleted file mode 100644 index b22245f9..00000000 --- a/docs/multitenant/usecase03/ns_namespace_pdb.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: pdbnamespace - diff --git a/docs/multitenant/usecase03/operator_creation_log.txt b/docs/multitenant/usecase03/operator_creation_log.txt deleted file mode 100644 index 36ed02ac..00000000 --- a/docs/multitenant/usecase03/operator_creation_log.txt +++ /dev/null @@ -1,27 +0,0 @@ -kubectl apply -f oracle-database-operator.yaml -namespace/oracle-database-operator-system created -customresourcedefinition.apiextensions.k8s.io/autonomouscontainerdatabases.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/autonomousdatabasebackups.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/autonomousdatabaserestores.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/autonomousdatabases.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/cdbs.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/dataguardbrokers.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/dbcssystems.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/oraclerestdataservices.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/pdbs.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/shardingdatabases.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/singleinstancedatabases.database.oracle.com configured -role.rbac.authorization.k8s.io/oracle-database-operator-leader-election-role created -clusterrole.rbac.authorization.k8s.io/oracle-database-operator-manager-role created -clusterrole.rbac.authorization.k8s.io/oracle-database-operator-metrics-reader created -clusterrole.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-role created -rolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-leader-election-rolebinding created -clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-manager-rolebinding created -clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-rolebinding created -service/oracle-database-operator-controller-manager-metrics-service created -service/oracle-database-operator-webhook-service created -certificate.cert-manager.io/oracle-database-operator-serving-cert created -issuer.cert-manager.io/oracle-database-operator-selfsigned-issuer created -mutatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-mutating-webhook-configuration created -validatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-validating-webhook-configuration created -deployment.apps/oracle-database-operator-controller-manager created diff --git a/docs/multitenant/usecase03/pdb_create.yaml b/docs/multitenant/usecase03/pdb_create.yaml deleted file mode 100644 index 200f3712..00000000 --- a/docs/multitenant/usecase03/pdb_create.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - diff --git a/docs/multitenant/usecase03/pdb_creation_log.txt b/docs/multitenant/usecase03/pdb_creation_log.txt deleted file mode 100644 index 71d0eb4f..00000000 --- a/docs/multitenant/usecase03/pdb_creation_log.txt +++ /dev/null @@ -1,6 +0,0 @@ -kubectl apply -f pdb_create.yaml -pdb.database.oracle.com/pdb1 created - -kubectl get pdbs -n pdbnamespace -NAME CONNECT_STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -pdb1 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) DB12 pdbdev READ WRITE 0.78G Ready Success diff --git a/docs/multitenant/usecase03/pdb_secret.yaml b/docs/multitenant/usecase03/pdb_secret.yaml deleted file mode 100644 index f1dfdac6..00000000 --- a/docs/multitenant/usecase03/pdb_secret.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: pdbnamespace -type: Opaque -data: - sysadmin_user: "[...base64 encoded password...]" - sysadmin_pwd: "[...base64 encoded password...]" - webserver_user: "[...base64 encoded password...]" - webserver_pwd: "[...base64 encoded password...]" - diff --git a/docs/multitenant/usecase03/runOrdsSSL.sh b/docs/multitenant/usecase03/runOrdsSSL.sh deleted file mode 100644 index 35f1b77b..00000000 --- a/docs/multitenant/usecase03/runOrdsSSL.sh +++ /dev/null @@ -1,190 +0,0 @@ -#!/bin/bash - -cat <$TNSNAME - - -function SetParameter() { - ##ords config info <--- Use this command to get the list - -[[ ! -z "${ORACLE_HOST}" && -z "${DBTNSURL}" ]] && { - $ORDS --config ${CONFIG} config set db.hostname ${ORACLE_HOST:-racnode1} - $ORDS --config ${CONFIG} config set db.port ${ORACLE_PORT:-1521} - $ORDS --config ${CONFIG} config set db.servicename ${ORACLE_SERVICE:-TESTORDS} -} - -[[ -z "${ORACLE_HOST}" && ! -z "${DBTNSURL}" ]] && { - #$ORDS --config ${CONFIG} config set db.tnsAliasName ${TNSALIAS} - #$ORDS --config ${CONFIG} config set db.tnsDirectory ${TNS_ADMIN} - #$ORDS --config ${CONFIG} config set db.connectionType tns - - $ORDS --config ${CONFIG} config set db.connectionType customurl - $ORDS --config ${CONFIG} config set db.customURL jdbc:oracle:thin:@${DBTNSURL} -} - - $ORDS --config ${CONFIG} config set security.requestValidationFunction false - $ORDS --config ${CONFIG} config set jdbc.MaxLimit 100 - $ORDS --config ${CONFIG} config set jdbc.InitialLimit 50 - $ORDS --config ${CONFIG} config set error.externalPath ${ERRORFOLDER} - $ORDS --config ${CONFIG} config set standalone.access.log /home/oracle - $ORDS --config ${CONFIG} config set standalone.https.port 8888 - $ORDS --config ${CONFIG} config set standalone.https.cert ${CERTIFICATE} - $ORDS --config ${CONFIG} config set standalone.https.cert.key ${KEY} - $ORDS --config ${CONFIG} config set restEnabledSql.active true - $ORDS --config ${CONFIG} config set security.verifySSL true - $ORDS --config ${CONFIG} config set database.api.enabled true - $ORDS --config ${CONFIG} config set plsql.gateway.mode disabled - $ORDS --config ${CONFIG} config set database.api.management.services.disabled false - $ORDS --config ${CONFIG} config set misc.pagination.maxRows 1000 - $ORDS --config ${CONFIG} config set db.cdb.adminUser "${CDBADMIN_USER:-C##DBAPI_CDB_ADMIN} AS SYSDBA" - $ORDS --config ${CONFIG} config secret --password-stdin db.cdb.adminUser.password << EOF -${CDBADMIN_PWD:-PROVIDE_A_PASSWORD} -EOF - -$ORDS --config ${CONFIG} config user add --password-stdin ${WEBSERVER_USER:-ordspdbadmin} "SQL Administrator, System Administrator" <${CKF} 2>&1 -echo "checkfile" >> ${CKF} -NOT_INSTALLED=`cat ${CKF} | grep "INFO: The" |wc -l ` -echo NOT_INSTALLED=$NOT_INSTALLED - - -function StartUp () { - $ORDS --config $CONFIG serve --port 8888 --secure -} - -# Check whether ords is already setup -if [ $NOT_INSTALLED -ne 0 ] -then - echo " SETUP " - setupOrds; - StartUp; -fi - -if [ $NOT_INSTALLED -eq 0 ] -then - echo " STARTUP " - StartUp; -fi - - diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 5780c144..7f980c0d 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -17124,7 +17124,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-mltp1 + image: lin.ocir.io/intsanjaysingh/operator/dboperator_master_250730:latest imagePullPolicy: Always name: manager ports: From 83a1fd335192d0a61ae3483c52436d5761088be6 Mon Sep 17 00:00:00 2001 From: marcstef Date: Mon, 18 Aug 2025 12:12:22 +0000 Subject: [PATCH 271/414] OrdsSrvs 2.0 - code warnings --- config/manager/kustomization.yaml | 4 ++-- controllers/database/ordssrvs_ordsconfig.go | 4 ++-- oracle-database-operator.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index eb2cad70..ef6a3133 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lin.ocir.io/intsanjaysingh/operator/dboperator_master_250730 - newTag: latest + newName: lin.ocir.io/intsanjaysingh/marcstef/oo + newTag: 2.0-3 diff --git a/controllers/database/ordssrvs_ordsconfig.go b/controllers/database/ordssrvs_ordsconfig.go index a078fec7..5cd29536 100644 --- a/controllers/database/ordssrvs_ordsconfig.go +++ b/controllers/database/ordssrvs_ordsconfig.go @@ -96,7 +96,7 @@ func (r *OrdsSrvsReconciler) ConfigMapDefine(ctx context.Context, ords *dbapi.Or ` ` + ordsSABase + `/config/certficate/` + ords.Spec.GlobalSettings.CertSecret.CertificateKey + `` + "\n" } defData = map[string]string{ - "settings.xml": fmt.Sprintf(`` + "\n" + + "settings.xml": fmt.Sprint(`` + "\n" + `` + "\n" + `` + "\n" + conditionalEntry("cache.metadata.graphql.expireAfterAccess", ords.Spec.GlobalSettings.CacheMetadataGraphQLExpireAfterAccess) + @@ -168,7 +168,7 @@ func (r *OrdsSrvsReconciler) ConfigMapDefine(ctx context.Context, ords *dbapi.Or defDBNetworkPath = ` ` + ordsSABase + `/config/databases/` + poolName + `/network/admin/` + "\n" } defData = map[string]string{ - "pool.xml": fmt.Sprintf(`` + "\n" + + "pool.xml": fmt.Sprint(`` + "\n" + `` + "\n" + `` + "\n" + ` ` + ords.Spec.PoolSettings[poolIndex].DBUsername + `` + "\n" + diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 7f980c0d..f9c85e37 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -17124,7 +17124,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: lin.ocir.io/intsanjaysingh/operator/dboperator_master_250730:latest + image: lin.ocir.io/intsanjaysingh/marcstef/oo:2.0-3 imagePullPolicy: Always name: manager ports: From 011fed013159927551e5d191e92f429c8a9b5de2 Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Mon, 18 Aug 2025 15:51:49 +0000 Subject: [PATCH 272/414] Privateai newfeatures --- .../Oracle_Machine_Learning_AI_models.htm | 320 ++++++++++++++++++ docs/privateai/README.md | 30 +- docs/privateai/access_privateai.md | 11 +- .../configmap_multi_model_filesystem.md | 17 + docs/privateai/configmap_multi_model_https.md | 17 + .../privateai/configmap_single_model_https.md | 17 + docs/privateai/create_oci_fss_based_pvc.md | 29 ++ docs/privateai/deploy_privateai_internallb.md | 4 +- ...ateai_multi_model_filesystem_internallb.md | 68 ++++ ..._privateai_multi_model_https_internallb.md | 58 ++++ ..._multi_model_https_internallb_add_model.md | 14 + ...lti_model_https_internallb_remove_model.md | 18 + docs/privateai/deploy_privateai_publiclb.md | 2 +- docs/privateai/provisioning/StorageClass.yaml | 7 + .../multi_model_filesystem_config.json | 31 ++ .../multi_model_https_config.json | 22 ++ docs/privateai/provisioning/oke-pv.yaml | 17 + docs/privateai/provisioning/oke-pvc.yaml | 13 + docs/privateai/provisioning/oml-ssl-pwd | 1 + .../pai_sample_internallb.yaml | 0 .../pai_sample_internallb_replace_cert.yaml | 0 ...ple_multi_model_filesystem_internallb.yaml | 65 ++++ ...el_filesystem_internallb_replace_cert.yaml | 69 ++++ ...i_sample_multi_model_https_internallb.yaml | 61 ++++ ...i_model_https_internallb_replace_cert.yaml | 65 ++++ .../pai_sample_publiclb.yaml | 0 .../pai_sample_scale_in.yaml | 0 .../pai_sample_scale_up.yaml | 0 .../{ => provisioning}/pai_secret.sh | 0 .../{ => provisioning}/pai_secret_new.sh | 0 .../single_model_https_config.json} | 0 docs/privateai/scale_in_privateai.md | 4 +- docs/privateai/scale_up_privateai.md | 4 +- 33 files changed, 940 insertions(+), 24 deletions(-) create mode 100644 docs/privateai/Oracle_Machine_Learning_AI_models.htm create mode 100644 docs/privateai/configmap_multi_model_filesystem.md create mode 100644 docs/privateai/configmap_multi_model_https.md create mode 100644 docs/privateai/configmap_single_model_https.md create mode 100644 docs/privateai/create_oci_fss_based_pvc.md create mode 100644 docs/privateai/deploy_privateai_multi_model_filesystem_internallb.md create mode 100644 docs/privateai/deploy_privateai_multi_model_https_internallb.md create mode 100644 docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md create mode 100644 docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md create mode 100644 docs/privateai/provisioning/StorageClass.yaml create mode 100644 docs/privateai/provisioning/multi_model_filesystem_config.json create mode 100644 docs/privateai/provisioning/multi_model_https_config.json create mode 100644 docs/privateai/provisioning/oke-pv.yaml create mode 100644 docs/privateai/provisioning/oke-pvc.yaml create mode 100644 docs/privateai/provisioning/oml-ssl-pwd rename docs/privateai/{ => provisioning}/pai_sample_internallb.yaml (100%) rename docs/privateai/{ => provisioning}/pai_sample_internallb_replace_cert.yaml (100%) create mode 100644 docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb.yaml create mode 100644 docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb_replace_cert.yaml create mode 100644 docs/privateai/provisioning/pai_sample_multi_model_https_internallb.yaml create mode 100644 docs/privateai/provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml rename docs/privateai/{ => provisioning}/pai_sample_publiclb.yaml (100%) rename docs/privateai/{ => provisioning}/pai_sample_scale_in.yaml (100%) rename docs/privateai/{ => provisioning}/pai_sample_scale_up.yaml (100%) rename docs/privateai/{ => provisioning}/pai_secret.sh (100%) mode change 100644 => 100755 rename docs/privateai/{ => provisioning}/pai_secret_new.sh (100%) mode change 100644 => 100755 rename docs/privateai/{config.json => provisioning/single_model_https_config.json} (100%) diff --git a/docs/privateai/Oracle_Machine_Learning_AI_models.htm b/docs/privateai/Oracle_Machine_Learning_AI_models.htm new file mode 100644 index 00000000..4f2cf923 --- /dev/null +++ b/docs/privateai/Oracle_Machine_Learning_AI_models.htm @@ -0,0 +1,320 @@ + + + + + + + + + + + + +
+ +

Oracle Machine Learning AI models

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Model name

+
+

Description

+
+

Dimensions

+
+

Size (MB)

+
+

Last Updated

+
+

Download

+
+

all_MiniLM_L12_v2

+
+

This is a SentenceTransformers model suitable for semantic similarity search and + clustering use cases.

+
+

384

+
+

116.92

+
+

7/15/2024

+
+

link

+
+

multilingual_e5_small

+
+

This is a multilingual sentence-transformer model + that supports 100. Supports text embedding, translation, or multilingual + understanding. Optimized for efficiency, it can handle multiple languages + while maintaining a smaller size for faster inference and reduced + computational requirements.

+
+

384

+
+

76.8

+
+

9/9/2024

+
+

link

+
+

clip_vit_base_patch32_txt

+
+

This is a text encoder that produces embeddings compatible with the CLIP image encoder. Enables text-to-image search and similarity matching between text descriptions and images.

+
+

512

+
+

243.57

+
+

1/29/2021

+
+

link

+
+

clip_vit_base_patch32_img

+
+

This is an image encoder that produces embeddings compatible with the CLIP text encoder. Enables image-to-text search and similarity matching between images and text descriptions.

+
+

512

+
+

335.38

+
+

1/29/2021

+
+

link

+
+ +

 

+ +

To learn how +to install these in your Oracle Database, see:

+ +

        +Documentation: Oracle +AI Vector Search User's Guide

+ +

        +Blog: Now +Available! Pre-built Embedding Generation model for Oracle Database 23ai

+ +
+ + + + \ No newline at end of file diff --git a/docs/privateai/README.md b/docs/privateai/README.md index d68a7c58..d9a88f66 100644 --- a/docs/privateai/README.md +++ b/docs/privateai/README.md @@ -65,17 +65,11 @@ The pre-built preivateAI container image is available on [Oracle Container Regis ### 5. Create a configmap for the Oracle PrivateAI Deployment -Create a `config.json` file. This file has the link for the AI Model File. [config.json](./config.json) is an example of this file. +In this step, a configmap will be created which has the details of the AI Model File. The configmap will be created according to the type of the PrivateAI Deployment. Below are examples of configmap used in the later examples: -Create a configmap using the above file as below: -```sh -kubectl create configmap omlconfigjson --from-file=config.json -n pai -``` - -You can check the details of the configmap as below: -```sh -kubectl get configmap -n pai -``` +- [Configmap using Single AI Model with HTTPS URL](./configmap_single_model_https.md) +- [Configmap using Multiple AI Models with HTTPS URL](./configmap_multi_model_https.md) +- [Configmap using Multiple AI Models on File System](./configmap_multi_model_filesystem.md) ### 6. Reserve LoadBalancer Public IP @@ -93,13 +87,15 @@ kubectl get configmap -n pai **IMPORTANT:** Make sure the version of `openssl` in the Oracle PrivateAI image is compatible with the `openssl` version on the machine where you will run the openssl commands to generate the encrypted password file during the deployment. -Create a file `oml-ssl-pwd` with the password you want to use. This password will be used in the next step. The script [pai_secret.sh](./pai_secret.sh) has the command to generate the required keys and an SSL certificate. +Create a file `oml-ssl-pwd` with the password you want to use. This password will be used in the next step. The script [pai_secret.sh](./provisioning/pai_secret.sh) has the command to generate the required keys and an SSL certificate. Use the Shell Script `pai_secret.sh` to create the required secrets for the Oracle PrivateAI Container Deployment. Run this file as below and enter the password when prompted. In case of the Public LoadBalancer, use the reserved Public IP as the `common name`. ```sh +cd provisioning +echo "" > oml-ssl-pwd ./pai_secret.sh ``` @@ -117,9 +113,17 @@ After you have the above prerequisites completed, you can proceed to the next se ## Quick Start -Please refer to [Deploy PrivateAI in OKE cluster using Public LB](./deploy_privateai_publiclb.md) for the details to deploy the PrivateAI Container Pod in an OKE Cluster using OCI Public LoadBalancer. +There are multiple use case possible for deploying the PrivateAI container in Kubernetes Cluster covered by below examples: + +**NOTE:** All the below deployments are using an OCI OKE Cluster. -Please refer to [Deploy PrivateAI in OKE cluster using Internal LB](./deploy_privateai_internallb.md) for the details to deploy the PrivateAI Container Pod in an OKE Cluster using OCI Internal LoadBalancer. +- [PrivateAI Container using OCI Public LoadBalancer](./deploy_privateai_publiclb.md) +- PrivateAI Container using an Internal LoadBalancer + - [PrivateAI Container using Single AI Model with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_internallb.md) + - [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md) + - [Add New Model](./deploy_privateai_multi_model_https_internallb_add_model.md) + - [Remove an existing model](./deploy_privateai_multi_model_https_internallb_remove_model.md) + - [PrivateAI Container using Multiple AI Models on File System and an Internal LoadBalancer](./deploy_privateai_multi_model_filesystem_internallb.md) ## Accessing the PrivateAI Container Pod in Kubernetes diff --git a/docs/privateai/access_privateai.md b/docs/privateai/access_privateai.md index ec0525f9..ebdd3e6a 100644 --- a/docs/privateai/access_privateai.md +++ b/docs/privateai/access_privateai.md @@ -3,19 +3,20 @@ **IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace and you have: - The Reserved Public IP in case of the Public LoadBalancer - The Private IP in case of case of the Internal LoadBalancer +- The AI Model details to be used in the URL to access ## Accessing the PrivateAI Container in Kubernetes using REST Calls Please follow the below steps to access: 1. Retrieve API Key -When you have used the file [pai_secret.sh](./pai_secret.sh) during the deployment, you will have the file named `api-key` generated in the same location. Copy this file `api-key` to the machine where you want to run the API Call to the Model Endpoint. +When you have used the file [pai_secret.sh](./provisioning/pai_secret.sh) during the deployment, you will have the file named `api-key` generated in the same location. Copy this file `api-key` to the machine where you want to run the API Call to the Model Endpoint. 2. Keep the LoadBalancer Reserved Public IP or the Private IP ready. You can use this IP in the API Endpoint call in the next step. 3. Assume the Loadbalancer Reserved Public IP from the last step is `129.xxx.xxx.xxx`, you can use the below example command to make an API Endpoint Call: ```sh - curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score + curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_l6_txt/score ``` **NOTE:** In case of the Private LoadBalancer, use the Internal IP in place of the IP `129.xxx.xxx.xxx` in above example. @@ -27,6 +28,8 @@ In case you want to use SSL authentication while accessing the PrivateAI Contain 1. Copy the `cert.pem` generated when you had run `pai_secret.sh` script, to the machine where you want to run the API Call to the Model Endpoint. 2. Use this key file while running the below modified example command to make an API Endpoint Call: ```sh - curl --cacert cert.pem --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score + curl --cacert cert.pem --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_l6_txt/score ``` -**NOTE:** In case of the Private LoadBalancer, use the Internal IP in place of the IP `129.xxx.xxx.xxx` in above example. \ No newline at end of file +**NOTE:** +- In case of the Private LoadBalancer, use the Internal IP in place of the IP `129.xxx.xxx.xxx` in above example. +- Replace the details of the AI Model in the above URL with the Model deployed. \ No newline at end of file diff --git a/docs/privateai/configmap_multi_model_filesystem.md b/docs/privateai/configmap_multi_model_filesystem.md new file mode 100644 index 00000000..d315f275 --- /dev/null +++ b/docs/privateai/configmap_multi_model_filesystem.md @@ -0,0 +1,17 @@ +# Configmap using Multiple AI Models on File System + +Create a `config.json` file to create a configmap. This file has the HTTPS link for the AI Model File. + +You can use the example file [multi_model_filesystem_config.json](./provisioning/multi_model_filesystem_config.json). + +Rename the file `multi_model_filesystem_config.json` to `config.json`. + +Create a configmap using the above file as below: +```sh +kubectl create configmap multiconfigjson --from-file=config.json -n pai +``` + +You can check the details of the configmap as below: +```sh +kubectl get configmap -n pai +``` \ No newline at end of file diff --git a/docs/privateai/configmap_multi_model_https.md b/docs/privateai/configmap_multi_model_https.md new file mode 100644 index 00000000..45a95508 --- /dev/null +++ b/docs/privateai/configmap_multi_model_https.md @@ -0,0 +1,17 @@ +# Configmap using Multiple AI Models with HTTPS URL + +Create a `config.json` file to create a configmap. This file has the HTTPS link for the AI Model File. + +You can use the example file [multi_model_https_config.json](./provisioning/multi_model_https_config.json). + +Rename the file `multi_model_https_config.json` to `config.json`. + +Create a configmap using the above file as below: +```sh +kubectl create configmap multiconfigjson --from-file=config.json -n pai +``` + +You can check the details of the configmap as below: +```sh +kubectl get configmap -n pai +``` \ No newline at end of file diff --git a/docs/privateai/configmap_single_model_https.md b/docs/privateai/configmap_single_model_https.md new file mode 100644 index 00000000..3665e38b --- /dev/null +++ b/docs/privateai/configmap_single_model_https.md @@ -0,0 +1,17 @@ +# Configmap using Single AI Model with HTTPS URL + +Create a `config.json` file to create a configmap. This file has the HTTPS link for the AI Model File. + +You can use the example file [single_model_https_config.json](./provisioning/single_model_https_config.json). + +Rename the file `single_model_https_config.json` to `config.json`. + +Create a configmap using the above file as below: +```sh +kubectl create configmap omlconfigjson --from-file=config.json -n pai +``` + +You can check the details of the configmap as below: +```sh +kubectl get configmap -n pai +``` \ No newline at end of file diff --git a/docs/privateai/create_oci_fss_based_pvc.md b/docs/privateai/create_oci_fss_based_pvc.md new file mode 100644 index 00000000..e5c98c9d --- /dev/null +++ b/docs/privateai/create_oci_fss_based_pvc.md @@ -0,0 +1,29 @@ +# Create OCI FSS based PVC + +The Persistent Volume (PV) is created using `oci-fss` Storage Class. Please refer to the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingpersistentvolumeclaim_Provisioning_PVCs_on_FSS.htm) for OCI File Storage. A Persistent Volume Claim (PVC) is created for this PV. For the PV with `oci-fss` Storage Class to be used, an OCI File System Storage and a Mount Target is created in OCI before creating the PV. + +**Important:** The VCN of the OKE Cluster and the File System Storage in OCI Must be same in order for the OKE Cluster Nodes to access the FSS Mount Target. + +1. Provision an OCI File Systems in the same VCN which is used by the OKE Cluster on which you want to deploy the PrivateAI Container. Please refer [here](https://docs.oracle.com/en-us/iaas/Content/File/Tasks/create-file-system.htm) for the details of this step. + +2. Once the OCI File System is created, get the details of the `Export Path` and `Mount Target` (the IP and OCID of Mount Target) information. It will be required in the next step. + +3. Create a YAML file for the Storage Class. Please refer to the sample file: [StorageClass.yaml](./provisioning/StorageClass.yaml). You will need to provide the `OCID` of the mount target in this file. + +4. Create a YAML file for the Persistent Volume (PV). It will use the `oci-fss` storage class. Please refer to the sample file: [oke-pv.yaml](./provisioning/oke-pv.yaml). You will need to provide the `Export Path` and Mount Target `IP` in this file. + +5. Create a YAML file for the Persistent Volume Claim (PVC). It will use the PV created in previous step. Please refer to the sample file: [oke-pvc.yaml](./provisioning/oke-pvc.yaml) + +6. Apply the above YAML files to create the PVC using `oci-fss` based File System. + ```sh + kubectl apply -f StorageClass.yaml + kubectl apply -f oke-pv.yaml + kubectl apply -f oke-pvc.yaml + ``` + +7. Check the details of the PVC created using `oci-fss` based File System: + ```sh + kubectl get sc + kubectl get pv -n pai + kubectl get pvc -n pai + ``` \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_internallb.md b/docs/privateai/deploy_privateai_internallb.md index ddc6a7e2..8d5f44dc 100644 --- a/docs/privateai/deploy_privateai_internallb.md +++ b/docs/privateai/deploy_privateai_internallb.md @@ -8,7 +8,7 @@ Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. In th If you want to use the OCI Internal LoadBalancer, then you will need to follow the below steps: -1. Deploy the [pai_sample_internallb.yaml](./pai_sample_internallb.yaml) file: +1. Deploy the [pai_sample_internallb.yaml](./provisioning/pai_sample_internallb.yaml) file: ```sh kubectl apply -f pai_sample_internallb.yaml ``` @@ -37,7 +37,7 @@ In case, you want the internal LoadBalancer to be created as an OCI load balance 3. Use the file [pai_secret_new.sh](./pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While using this script, use the IP noted in Step 2 for `common name` while generating the SSL certificate. -4. Apply the modified file [pai_sample_internallb_replace_cert.yaml](./pai_sample_internallb_replace_cert.yaml) to replace the Internal LoadBalancer Certificate: +4. Apply the modified file [pai_sample_internallb_replace_cert.yaml](./provisioning/pai_sample_internallb_replace_cert.yaml) to replace the Internal LoadBalancer Certificate: ```sh kubectl apply -f pai_sample_internallb_replace_cert.yaml ``` diff --git a/docs/privateai/deploy_privateai_multi_model_filesystem_internallb.md b/docs/privateai/deploy_privateai_multi_model_filesystem_internallb.md new file mode 100644 index 00000000..4061da85 --- /dev/null +++ b/docs/privateai/deploy_privateai_multi_model_filesystem_internallb.md @@ -0,0 +1,68 @@ +# Deploy PrivateAI in OKE cluster using Multiple AI Models on File System and an Internal LoadBalancer + +Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. The PrivateAI Container is deployed using multiple AI Models and the model files are accessible to the PrivateAI Pod using Mounted Persistent Volume(PV). In this example, the deployment uses the YAML file based on `OCI OKE` cluster. + +Please refer to [Create OCI FSS based PVC](./create_oci_fss_based_pvc.md) for the steps to create an `oci-fss` based PVC to use in the current case. + +**IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. + +**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available as of now. Please check the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). + +If you want to use the OCI Internal LoadBalancer, then you will need to follow the below steps: + +1. Refer to the file [Oracle Machine Learning AI models](./Oracle_Machine_Learning_AI_models.htm) to get details of the AI Model files. Download the files and upload them to the OCI File System you have created in above step. For Example: You can mount the same File System to an OCI Compute VM in the same VCN using below entry in `/etc/fstab` file + ```sh + 10.0.XX.XX:/oml_models /mnt nfs defaults 0 0 + ``` + +2. Make sure you have created the [configmap](./configmap_multi_model_filesystem.md) + +3. Deploy the [pai_sample_multi_model_filesystem_internallb.yaml](./provisioning/pai_sample_multi_model_filesystem_internallb.yaml) file: + ```sh + kubectl apply -f pai_sample_multi_model_filesystem_internallb.yaml + ``` + This will provision the PrivateAI Container in the OKE cluster using Internal LoadBalancer with Ephemeral Private IP. + + The PrivateAI Pod will mount the PVC `/oml/models` which is created earlier using `oci-fss` storage class. + +4. Check the status of the deployment and note the IP under field `EXTERNAL-IP` for `service/pai-sample-svc`. + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n pai + + # Check the logs of a particular pod: + kubectl logs -f -n pai $(kubectl get pod -n pai -l app.kubernetes.io/name=pai-sample -o jsonpath='{.items[0].metadata.name}') + ``` + +In this case, the internal LoadBalancer is created as an OCI load balancer with a private IP address, hosted on the subnet specified for load balancers when the OKE cluster was created. + +In case, you want the internal LoadBalancer to be created as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created, you need to add following annotations in the above .yaml file: + +```sh + pailbAnnotation: + # Specify the OCID of the alternate Subnet + service.beta.kubernetes.io/oci-load-balancer-subnet1: "ocid1.subnet.oc1..aaaaaa....vdfw" +``` + +**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. In order to avoid a hostname mismatch error while using the `cert.pem` file to make a authenicated connection, we will need to replace this SSL certificate with a new certificate which has the `common name` set to the IP of the Internal LoadBalancer. + +5. Use the file [pai_secret_new.sh](./provisioning/pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While using this script, use the IP noted in Step 2 for `common name` while generating the SSL certificate. + +```sh +cd provisioning +./pai_secret_new.sh +``` + +6. Apply the modified file [pai_sample_multi_model_https_internallb_replace_cert.yaml](./provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml) to replace the Internal LoadBalancer Certificate: +```sh +kubectl apply -f pai_sample_multi_model_https_internallb_replace_cert.yaml +``` +**NOTE:** This step will result in termination of the existing PrivateAI Container Pod and creation of new Pod while the Internal LoadBalancer IP will not change. + +7. After this change, you will be able to access the PrivateAI Container using the Internal LoadBalancer IP using an authenticated connection using the `cert.pem` file from the new SSL certificate. + +**NOTE:** The file `/oml/config/config.json` inside the running Kubernetes Pod will have the details of the AI Models currently Deployed. You can use the below steps to confirm: +```sh +kubectl exec -it -n pai $(kubectl get pod -n pai -l app.kubernetes.io/name=pai-sample -o jsonpath='{.items[0].metadata.name}') -- /bin/bash +cat /oml/config/config.json +``` \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_multi_model_https_internallb.md b/docs/privateai/deploy_privateai_multi_model_https_internallb.md new file mode 100644 index 00000000..f1fef388 --- /dev/null +++ b/docs/privateai/deploy_privateai_multi_model_https_internallb.md @@ -0,0 +1,58 @@ +# Deploy PrivateAI in OKE cluster using Multiple AI Models with HTTPS URL and an Internal LoadBalancer + +Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. The PrivateAI Container is deployed using multiple AI Models and HTTPS URLs for those model files are provided using a configmap. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. + +**IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. + +**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available as of now. Please check the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). + +If you want to use the OCI Internal LoadBalancer, then you will need to follow the below steps: + +1. Make sure you have created the [configmap](./configmap_multi_model_https.md) +2. Deploy the [pai_sample_multi_model_https_internallb.yaml](./provisioning/pai_sample_multi_model_https_internallb.yaml) file: + ```sh + kubectl apply -f pai_sample_multi_model_https_internallb.yaml + ``` + This will provision the PrivateAI Container in the OKE cluster using Internal LoadBalancer with Ephemeral Private IP. + +3. Check the status of the deployment and note the IP under field `EXTERNAL-IP` for `service/pai-sample-svc`. + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n pai + + # Check the logs of a particular pod: + kubectl logs -f -n pai $(kubectl get pod -n pai -l app.kubernetes.io/name=pai-sample -o jsonpath='{.items[0].metadata.name}') + ``` + +In this case, the internal LoadBalancer is created as an OCI load balancer with a private IP address, hosted on the subnet specified for load balancers when the OKE cluster was created. + +In case, you want the internal LoadBalancer to be created as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created, you need to add following annotations in the above .yaml file: + +```sh + pailbAnnotation: + # Specify the OCID of the alternate Subnet + service.beta.kubernetes.io/oci-load-balancer-subnet1: "ocid1.subnet.oc1..aaaaaa....vdfw" +``` + +**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. In order to avoid a hostname mismatch error while using the `cert.pem` file to make a authenicated connection, we will need to replace this SSL certificate with a new certificate which has the `common name` set to the IP of the Internal LoadBalancer. + +4. Use the file [pai_secret_new.sh](./provisioning/pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While using this script, use the IP noted in Step 2 for `common name` while generating the SSL certificate. + +```sh +cd provisioning +./pai_secret_new.sh +``` + +5. Apply the modified file [pai_sample_multi_model_https_internallb_replace_cert.yaml](./provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml) to replace the Internal LoadBalancer Certificate: + ```sh + kubectl apply -f pai_sample_multi_model_https_internallb_replace_cert.yaml + ``` +**NOTE:** This step will result in termination of the existing PrivateAI Container Pod and creation of new Pod while the Internal LoadBalancer IP will not change. + +6. After this change, you will be able to access the PrivateAI Container using the Internal LoadBalancer IP using an authenticated connection using the `cert.pem` file from the new SSL certificate. + +**NOTE:** The file `/oml/config/config.json` inside the running Kubernetes Pod will have the details of the AI Models currently Deployed. You can use the below steps to confirm: +```sh +kubectl exec -it -n pai $(kubectl get pod -n pai -l app.kubernetes.io/name=pai-sample -o jsonpath='{.items[0].metadata.name}') -- /bin/bash +cat /oml/config/config.json +``` \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md b/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md new file mode 100644 index 00000000..17621e9f --- /dev/null +++ b/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md @@ -0,0 +1,14 @@ +# ADD New AI Model to PrivateAI deployed in OKE cluster + +The existing PrivateAI Container is deployed in case [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md) using multiple AI Models and HTTPS URLs for those model files are provided using a configmap. + +In this example, an additional AI Model is deployed to the existing deployment. + +1. Use the below command to edit the configmap used by the existing PrivateAI Deployment to add a new AI Model: + ```sh + kubectl edit configmap multiconfigjson -n pai + ``` + +2. Wait for few minutes and you should see configmap updated inside the Pod. + +**NOTE:** The Pod will not be restarted in this case. The Application inside the Pod is periodically checking for Configmap changes. \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md b/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md new file mode 100644 index 00000000..34c1fb9f --- /dev/null +++ b/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md @@ -0,0 +1,18 @@ +# Remove an existing Model from PrivateAI deployed in OKE cluster + +The existing PrivateAI Container is deployed in case [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md) using multiple AI Models and HTTPS URLs for those model files are provided using a configmap. + +or + +Additional AI Models have been added to the existing PrivateAI Deployment using [Add New Model](./deploy_privateai_multi_model_https_internallb_add_model.md). + +In this example, an existing AI Model is removed from the existing PrivateAI deployment. + +1. Use the below command to edit the configmap used by the existing PrivateAI Deployment to removew an existing AI Model: + ```sh + kubectl edit configmap multiconfigjson -n pai + ``` + +2. Wait for few minutes and you should see configmap updated inside the Pod. + +**NOTE:** The Pod will not be restarted in this case. The Application inside the Pod is periodically checking for Configmap changes. \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_publiclb.md b/docs/privateai/deploy_privateai_publiclb.md index 77650048..591f3fff 100644 --- a/docs/privateai/deploy_privateai_publiclb.md +++ b/docs/privateai/deploy_privateai_publiclb.md @@ -6,7 +6,7 @@ Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. In th **NOTE:** Modify the file `pai_sample_publiclb.yaml` with the actual Reserved Public IP before deployment. -Use the file: [pai_sample_publiclb.yaml](./pai_sample_publiclb.yaml) for this use case as below: +Use the file: [pai_sample_publiclb.yaml](./provisioning/pai_sample_publiclb.yaml) for this use case as below: 1. Deploy the `pai_sample_publiclb.yaml` file: ```sh diff --git a/docs/privateai/provisioning/StorageClass.yaml b/docs/privateai/provisioning/StorageClass.yaml new file mode 100644 index 00000000..435fd74d --- /dev/null +++ b/docs/privateai/provisioning/StorageClass.yaml @@ -0,0 +1,7 @@ +kind: StorageClass +apiVersion: storage.k8s.io/v1 +metadata: + name: oci-fss +provisioner: oracle.com/oci-fss +parameters: + mntTargetId: ocid1.mounttarget.oc1.phx..................... diff --git a/docs/privateai/provisioning/multi_model_filesystem_config.json b/docs/privateai/provisioning/multi_model_filesystem_config.json new file mode 100644 index 00000000..da60f5f2 --- /dev/null +++ b/docs/privateai/provisioning/multi_model_filesystem_config.json @@ -0,0 +1,31 @@ +{ + "environment": { + "OML_ONNX_INTRA_OP_NUM_THREADS": 1 + }, + "models": [ + { + "modelname": "all_minilm_l12_v2", + "modelfile": "all_MiniLM_L12_v2.zip", + "modelfunction": "EMBEDDING", + "cache_on_startup": true + }, + { + "modelname": "multilingual_e5_small", + "modelfile": "multilingual_e5_small.zip", + "modelfunction": "EMBEDDING", + "cache_on_startup": true + }, + { + "modelname": "clip_vit_base_patch32_txt", + "modelfile": "clip_vit_base_patch32_txt.zip", + "modelfunction": "EMBEDDING", + "cache_on_startup": true + }, + { + "modelname": "clip_vit_base_patch32_img", + "modelfile": "clip_vit_base_patch32_img.zip", + "modelfunction": "EMBEDDING", + "cache_on_startup": true + } + ] +} diff --git a/docs/privateai/provisioning/multi_model_https_config.json b/docs/privateai/provisioning/multi_model_https_config.json new file mode 100644 index 00000000..67124e57 --- /dev/null +++ b/docs/privateai/provisioning/multi_model_https_config.json @@ -0,0 +1,22 @@ +{ + "environment": { + "OML_ONNX_INTRA_OP_NUM_THREADS": 1 + }, + "models": [ + { + "modelname": "all_minilm_l6_txt", + "modelfile": "https://objectstorage.us-ashburn-1.oraclecloud.com/p/wBcvPtKxMfxmf7BMEcWbRWaXb249Tt1OVR6UPJRXzejXFlHu9XMkh7H6G0HV0uq9/n/intsanjaysingh/b/onnxmodel/o/ALL-MINILM-L6-V2.onnxALL-MINILM-L6-V2.onnx", + "modelfunction": "EMBEDDING" + }, + { + "modelname": "clip_vit_base32", + "modelfile": "https://adwc4pm.objectstorage.us-ashburn-1.oci.customer-oci.com/p/uOaue-BqfWh1z2jwYTJcT0k4H-rihCW0kMbkcHsK0PrHC9MFKhqfSr88n3SSQXsB/n/adwc4pm/b/OML-ai-models/o/clip_vit_base_patch32_txt_augmented.zip", + "modelfunction": "EMBEDDING" + }, + { + "modelname": "clip_vit_base_patch32_img", + "modelfile": "https://adwc4pm.objectstorage.us-ashburn-1.oci.customer-oci.com/p/FiHMIQJ8esEufIbv96CpoE2dzlZHZhn0g6Nx63LYAHELKUUB-2GUumRgVkzT-DmV/n/adwc4pm/b/OML-ai-models/o/clip_vit_base_patch32_img_augmented.zip", + "modelfunction": "EMBEDDING" + } + ] +} diff --git a/docs/privateai/provisioning/oke-pv.yaml b/docs/privateai/provisioning/oke-pv.yaml new file mode 100644 index 00000000..69ecc079 --- /dev/null +++ b/docs/privateai/provisioning/oke-pv.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: oke-fsspv + namespace: pai +spec: + storageClassName: oci-fss + capacity: + storage: 100Gi + accessModes: + - ReadWriteMany + mountOptions: + - nosuid + nfs: + server: 10.0.XX.XX + path: "/oml_models" + readOnly: false diff --git a/docs/privateai/provisioning/oke-pvc.yaml b/docs/privateai/provisioning/oke-pvc.yaml new file mode 100644 index 00000000..5f771909 --- /dev/null +++ b/docs/privateai/provisioning/oke-pvc.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: oke-fsspvc + namespace: pai +spec: + storageClassName: oci-fss + accessModes: + - ReadWriteMany + resources: + requests: + storage: 100Gi + volumeName: oke-fsspv diff --git a/docs/privateai/provisioning/oml-ssl-pwd b/docs/privateai/provisioning/oml-ssl-pwd new file mode 100644 index 00000000..7a879403 --- /dev/null +++ b/docs/privateai/provisioning/oml-ssl-pwd @@ -0,0 +1 @@ +ORacle_19c diff --git a/docs/privateai/pai_sample_internallb.yaml b/docs/privateai/provisioning/pai_sample_internallb.yaml similarity index 100% rename from docs/privateai/pai_sample_internallb.yaml rename to docs/privateai/provisioning/pai_sample_internallb.yaml diff --git a/docs/privateai/pai_sample_internallb_replace_cert.yaml b/docs/privateai/provisioning/pai_sample_internallb_replace_cert.yaml similarity index 100% rename from docs/privateai/pai_sample_internallb_replace_cert.yaml rename to docs/privateai/provisioning/pai_sample_internallb_replace_cert.yaml diff --git a/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb.yaml b/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb.yaml new file mode 100644 index 00000000..e1e2306d --- /dev/null +++ b/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb.yaml @@ -0,0 +1,65 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +# Specifies the custom resource API version +apiVersion: omlai.oracle.com/v4 + +# Defines the type of resource (Private AI) +kind: PrivateAi + +# Provide identification and scoping metadata +metadata: + # Name of Private AI resource + name: pai-sample + # Kubernetes Namespace where this resource will be deployed + namespace: pai + +# Main specification for Private AI +spec: + + # Configuration for Private AI + paiConfigFile: + # Configmap having the links for the AI Model Files + name: multiconfigjson + # Location inside the Pod to which the file containining the configmap will be mounted to + mountLocation: /oml/config + + # Specify the Kubernetes Secret Details + paiSecret: + name: paisecret + mountLocation: /oml/ssl + + # If you want to enable Authentication for the Private AI Access + paiEnableAuthentication: true + + # If LoadBalancer will be used + isExternalSvc: true + + # OCI Cloud Storage Class + storageClass: oci-bv + + # Details of the oci-fss storage class based PVC + pvcList: + oke-fsspvc: /oml/models + + # Container Image of the Private AI Container + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:omlai-1.10.0 + + # Name and port details of the Private AI Service once deployed + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + + # If Internal Load Balancer will be used + paiInternalLB: true + + # Storage Allocation for the Pod + storageSizeInGb: 30 + + # Replica Count + replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb_replace_cert.yaml b/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb_replace_cert.yaml new file mode 100644 index 00000000..22f257e3 --- /dev/null +++ b/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb_replace_cert.yaml @@ -0,0 +1,69 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +# Specifies the custom resource API version +apiVersion: omlai.oracle.com/v4 + +# Defines the type of resource (Private AI) +kind: PrivateAi + +# Provide identification and scoping metadata +metadata: + # Name of Private AI resource + name: pai-sample + # Kubernetes Namespace where this resource will be deployed + namespace: pai + +# Main specification for Private AI +spec: + + # Configuration for Private AI + paiConfigFile: + # Configmap having the links for the AI Model Files + name: multiconfigjson + # Location inside the Pod to which the file containining the configmap will be mounted to + mountLocation: /oml/config + + # Specify the Kubernetes Secret Details + paiSecret: + name: paisecretnew + mountLocation: /oml/ssl + + # If you want to enable Authentication for the Private AI Access + paiEnableAuthentication: true + + # If LoadBalancer will be used + isExternalSvc: true + + # OCI Cloud Storage Class + storageClass: oci-bv + + # Details of the oci-fss storage class based PVC + pvcList: + oke-fsspvc: /oml/models + + # Container Image of the Private AI Container + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:omlai-1.10.0 + + # Name and port details of the Private AI Service once deployed + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + + # annotation to replace the certificate for the OCI Internal LoadBalancer + pailbAnnotation: + service.beta.kubernetes.io/oci-load-balancer-tls-secret: paisecretnew + + # If Internal Load Balancer will be used + paiInternalLB: true + + # Storage Allocation for the Pod + storageSizeInGb: 30 + + # Replica Count + replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_multi_model_https_internallb.yaml b/docs/privateai/provisioning/pai_sample_multi_model_https_internallb.yaml new file mode 100644 index 00000000..c89c2859 --- /dev/null +++ b/docs/privateai/provisioning/pai_sample_multi_model_https_internallb.yaml @@ -0,0 +1,61 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +# Specifies the custom resource API version +apiVersion: omlai.oracle.com/v4 + +# Defines the type of resource (Private AI) +kind: PrivateAi + +# Provide identification and scoping metadata +metadata: + # Name of Private AI resource + name: pai-sample + # Kubernetes Namespace where this resource will be deployed + namespace: pai + +# Main specification for Private AI +spec: + + # Configuration for Private AI + paiConfigFile: + # Configmap having the links for the AI Model Files + name: multiconfigjson + # Location inside the Pod to which the file containining the configmap will be mounted to + mountLocation: /oml/config + + # Specify the Kubernetes Secret Details + paiSecret: + name: paisecret + mountLocation: /oml/ssl + + # If you want to enable Authentication for the Private AI Access + paiEnableAuthentication: true + + # If LoadBalancer will be used + isExternalSvc: true + + # OCI Cloud Storage Class + storageClass: oci-bv + + # Container Image of the Private AI Container + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:omlai-1.10.0 + + # Name and port details of the Private AI Service once deployed + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + + # If Internal Load Balancer will be used + paiInternalLB: true + + # Storage Allocation for the Pod + storageSizeInGb: 30 + + # Replica Count + replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml b/docs/privateai/provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml new file mode 100644 index 00000000..3ce50165 --- /dev/null +++ b/docs/privateai/provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml @@ -0,0 +1,65 @@ +# +# Copyright (c) 2022, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +--- +# Specifies the custom resource API version +apiVersion: omlai.oracle.com/v4 + +# Defines the type of resource (Private AI) +kind: PrivateAi + +# Provide identification and scoping metadata +metadata: + # Name of Private AI resource + name: pai-sample + # Kubernetes Namespace where this resource will be deployed + namespace: pai + +# Main specification for Private AI +spec: + + # Configuration for Private AI + paiConfigFile: + # Configmap having the links for the AI Model Files + name: multiconfigjson + # Location inside the Pod to which the file containining the configmap will be mounted to + mountLocation: /oml/config + + # Specify the Kubernetes Secret Details + paiSecret: + name: paisecretnew + mountLocation: /oml/ssl + + # If you want to enable Authentication for the Private AI Access + paiEnableAuthentication: true + + # If LoadBalancer will be used + isExternalSvc: true + + # OCI Cloud Storage Class + storageClass: oci-bv + + # Container Image of the Private AI Container + paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:omlai-1.10.0 + + # Name and port details of the Private AI Service once deployed + paiService: + name: paisvc + portMappings: + - port: 443 + targetPort: 8443 + protocol: TCP + + # annotation to replace the certificate for the OCI Internal LoadBalancer + pailbAnnotation: + service.beta.kubernetes.io/oci-load-balancer-tls-secret: paisecretnew + + # If Internal Load Balancer will be used + paiInternalLB: true + + # Storage Allocation for the Pod + storageSizeInGb: 30 + + # Replica Count + replicas: 1 diff --git a/docs/privateai/pai_sample_publiclb.yaml b/docs/privateai/provisioning/pai_sample_publiclb.yaml similarity index 100% rename from docs/privateai/pai_sample_publiclb.yaml rename to docs/privateai/provisioning/pai_sample_publiclb.yaml diff --git a/docs/privateai/pai_sample_scale_in.yaml b/docs/privateai/provisioning/pai_sample_scale_in.yaml similarity index 100% rename from docs/privateai/pai_sample_scale_in.yaml rename to docs/privateai/provisioning/pai_sample_scale_in.yaml diff --git a/docs/privateai/pai_sample_scale_up.yaml b/docs/privateai/provisioning/pai_sample_scale_up.yaml similarity index 100% rename from docs/privateai/pai_sample_scale_up.yaml rename to docs/privateai/provisioning/pai_sample_scale_up.yaml diff --git a/docs/privateai/pai_secret.sh b/docs/privateai/provisioning/pai_secret.sh old mode 100644 new mode 100755 similarity index 100% rename from docs/privateai/pai_secret.sh rename to docs/privateai/provisioning/pai_secret.sh diff --git a/docs/privateai/pai_secret_new.sh b/docs/privateai/provisioning/pai_secret_new.sh old mode 100644 new mode 100755 similarity index 100% rename from docs/privateai/pai_secret_new.sh rename to docs/privateai/provisioning/pai_secret_new.sh diff --git a/docs/privateai/config.json b/docs/privateai/provisioning/single_model_https_config.json similarity index 100% rename from docs/privateai/config.json rename to docs/privateai/provisioning/single_model_https_config.json diff --git a/docs/privateai/scale_in_privateai.md b/docs/privateai/scale_in_privateai.md index 817c79b4..04d797d7 100644 --- a/docs/privateai/scale_in_privateai.md +++ b/docs/privateai/scale_in_privateai.md @@ -1,10 +1,10 @@ # Scale-In an existing deployment of Oracle PrivateAI Container -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=3` using the file [pai_sample_scale_up.yaml](./pai_sample_scale_up.yaml) +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=3` using the file [pai_sample_scale_up.yaml](./provisioning/pai_sample_scale_up.yaml) In this example, we will Scale In an existing deployment with `replicas=3` to `replicas=2`. -Use the file: [pai_sample_scale_in.yaml](./pai_sample_scale_in.yaml) for this use case as below: +Use the file: [pai_sample_scale_in.yaml](./provisioning/pai_sample_scale_in.yaml) for this use case as below: 1. Check the status of the deployment: ```sh diff --git a/docs/privateai/scale_up_privateai.md b/docs/privateai/scale_up_privateai.md index c480c70f..72eea049 100644 --- a/docs/privateai/scale_up_privateai.md +++ b/docs/privateai/scale_up_privateai.md @@ -1,10 +1,10 @@ # Scale-Up an existing deployment of Oracle PrivateAI Container -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=1` using the file [pai_sample_publiclb.yaml](./pai_sample_publiclb.yaml) +**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=1` using the file [pai_sample_publiclb.yaml](./provisioning/pai_sample_publiclb.yaml) In this example, we will Scale Up an existing deployment with `replicas=1` to `replicas=3`. -Use the file: [pai_sample_scale_up.yaml](./pai_sample_scale_up.yaml) for this use case as below: +Use the file: [pai_sample_scale_up.yaml](./provisioning/pai_sample_scale_up.yaml) for this use case as below: 1. Check the status of the deployment: ```sh From f7bae4bba806aa592d1d06d701ac39af406a720a Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Mon, 18 Aug 2025 13:05:08 -0500 Subject: [PATCH 273/414] Added fix for Name and Namespace not found --- .../v1/databaseobserver_webhook.go | 50 ++++++++++--------- .../v1alpha1/databaseobserver_webhook.go | 50 ++++++++++--------- .../v4/databaseobserver_webhook.go | 46 +++++++++-------- 3 files changed, 76 insertions(+), 70 deletions(-) diff --git a/apis/observability/v1/databaseobserver_webhook.go b/apis/observability/v1/databaseobserver_webhook.go index 4ec6a8ab..809549d3 100644 --- a/apis/observability/v1/databaseobserver_webhook.go +++ b/apis/observability/v1/databaseobserver_webhook.go @@ -40,8 +40,6 @@ package v1 import ( "context" - "strings" - dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -82,7 +80,8 @@ var _ webhook.CustomDefaulter = &DatabaseObserver{} // Default implements webhook.CustomDefaulter so a webhook will be registered for the type func (r *DatabaseObserver) Default(ctx context.Context, obj runtime.Object) error { - databaseobserverlog.Info("default", "name", r.Name) + obs := obj.(*DatabaseObserver) + databaseobserverlog.Info("default", "name", obs.Name) // TODO(user): fill in your defaulting logic. return nil @@ -95,47 +94,48 @@ var _ webhook.CustomValidator = &DatabaseObserver{} // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - databaseobserverlog.Info("validate create", "name", r.Name) + obs := obj.(*DatabaseObserver) + databaseobserverlog.Info("validate create", "name", obs.Name) var e field.ErrorList ns := dbcommons.GetWatchNamespaces() // Check for namespace/cluster scope access - if _, isDesiredNamespaceWithinScope := ns[r.Namespace]; !isDesiredNamespaceWithinScope && len(ns) > 0 { + if _, isDesiredNamespaceWithinScope := ns[obs.Namespace]; !isDesiredNamespaceWithinScope && len(ns) > 0 { e = append(e, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), obs.Namespace, "Oracle database operator doesn't watch over this namespace")) } // The other vault field must have value if one does - if (r.Spec.Database.OCIVault.VaultID != "" && r.Spec.Database.OCIVault.VaultPasswordSecret == "") || - (r.Spec.Database.OCIVault.VaultPasswordSecret != "" && r.Spec.Database.OCIVault.VaultID == "") { + if (obs.Spec.Database.OCIVault.VaultID != "" && obs.Spec.Database.OCIVault.VaultPasswordSecret == "") || + (obs.Spec.Database.OCIVault.VaultPasswordSecret != "" && obs.Spec.Database.OCIVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("oci"), r.Spec.Database.OCIVault, + field.Invalid(field.NewPath("spec").Child("database").Child("oci"), obs.Spec.Database.OCIVault, ErrorSpecValidationMissingVaultField)) } // The other vault field must have value if one does - if (r.Spec.Database.AzureVault.VaultID != "" && (r.Spec.Database.AzureVault.VaultPasswordSecret == "" && r.Spec.Database.AzureVault.VaultUsernameSecret == "")) || - (r.Spec.Database.AzureVault.VaultPasswordSecret != "" && r.Spec.Database.AzureVault.VaultID == "") || - (r.Spec.Database.AzureVault.VaultUsernameSecret != "" && r.Spec.Database.AzureVault.VaultID == "") { + if (obs.Spec.Database.AzureVault.VaultID != "" && (obs.Spec.Database.AzureVault.VaultPasswordSecret == "" && obs.Spec.Database.AzureVault.VaultUsernameSecret == "")) || + (obs.Spec.Database.AzureVault.VaultPasswordSecret != "" && obs.Spec.Database.AzureVault.VaultID == "") || + (obs.Spec.Database.AzureVault.VaultUsernameSecret != "" && obs.Spec.Database.AzureVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("azure"), r.Spec.Database.AzureVault, + field.Invalid(field.NewPath("spec").Child("database").Child("azure"), obs.Spec.Database.AzureVault, ErrorSpecValidationMissingVaultField)) } // disallow usage of any other image than the observability-exporter // temporarily disabled - //if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { + //if obs.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(obs.Spec.Deployment.ExporterImage, AllowedExporterImage) { // e = append(e, - // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, + // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), obs.Spec.Deployment.ExporterImage, // ErrorSpecExporterImageNotAllowed)) //} // Return if any errors if len(e) > 0 { - return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, obs.Name, e) } return nil, nil @@ -143,25 +143,27 @@ func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Objec // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type func (r *DatabaseObserver) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - databaseobserverlog.Info("validate update", "name", r.Name) + obs := newObj.(*DatabaseObserver) + databaseobserverlog.Info("validate update", "name", obs.Name) var e field.ErrorList // disallow usage of any other image than the observability-exporter - if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { - e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, - ErrorSpecExporterImageNotAllowed)) - } + //if obs.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(obs.Spec.Deployment.ExporterImage, AllowedExporterImage) { + // e = append(e, + // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), obs.Spec.Deployment.ExporterImage, + // ErrorSpecExporterImageNotAllowed)) + //} // Return if any errors if len(e) > 0 { - return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, obs.Name, e) } return nil, nil } // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type func (r *DatabaseObserver) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - databaseobserverlog.Info("validate delete", "name", r.Name) + obs := obj.(*DatabaseObserver) + databaseobserverlog.Info("validate delete", "name", obs.Name) return nil, nil } diff --git a/apis/observability/v1alpha1/databaseobserver_webhook.go b/apis/observability/v1alpha1/databaseobserver_webhook.go index 9a11a0f3..da683b6b 100644 --- a/apis/observability/v1alpha1/databaseobserver_webhook.go +++ b/apis/observability/v1alpha1/databaseobserver_webhook.go @@ -40,8 +40,6 @@ package v1alpha1 import ( "context" - "strings" - dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -82,7 +80,8 @@ var _ webhook.CustomDefaulter = &DatabaseObserver{} // Default implements webhook.CustomDefaulter so a webhook will be registered for the type func (r *DatabaseObserver) Default(ctx context.Context, obj runtime.Object) error { - databaseobserverlog.Info("default", "name", r.Name) + obs := obj.(*DatabaseObserver) + databaseobserverlog.Info("default", "name", obs.Name) // TODO(user): fill in your defaulting logic. return nil @@ -95,48 +94,49 @@ var _ webhook.CustomValidator = &DatabaseObserver{} // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - databaseobserverlog.Info("validate create", "name", r.Name) + obs := obj.(*DatabaseObserver) + databaseobserverlog.Info("validate create", "name", obs.Name) var e field.ErrorList ns := dbcommons.GetWatchNamespaces() // Check for namespace/cluster scope access - if _, isDesiredNamespaceWithinScope := ns[r.Namespace]; !isDesiredNamespaceWithinScope && len(ns) > 0 { + if _, isDesiredNamespaceWithinScope := ns[obs.Namespace]; !isDesiredNamespaceWithinScope && len(ns) > 0 { e = append(e, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), obs.Namespace, "Oracle database operator doesn't watch over this namespace")) } // The other vault field must have value if one does - if (r.Spec.Database.OCIVault.VaultID != "" && r.Spec.Database.OCIVault.VaultPasswordSecret == "") || - (r.Spec.Database.OCIVault.VaultPasswordSecret != "" && r.Spec.Database.OCIVault.VaultID == "") { + if (obs.Spec.Database.OCIVault.VaultID != "" && obs.Spec.Database.OCIVault.VaultPasswordSecret == "") || + (obs.Spec.Database.OCIVault.VaultPasswordSecret != "" && obs.Spec.Database.OCIVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("oci"), r.Spec.Database.OCIVault, + field.Invalid(field.NewPath("spec").Child("database").Child("oci"), obs.Spec.Database.OCIVault, ErrorSpecValidationMissingVaultField)) } // The other vault field must have value if one does - if (r.Spec.Database.AzureVault.VaultID != "" && (r.Spec.Database.AzureVault.VaultPasswordSecret == "" && r.Spec.Database.AzureVault.VaultUsernameSecret == "")) || - (r.Spec.Database.AzureVault.VaultPasswordSecret != "" && r.Spec.Database.AzureVault.VaultID == "") || - (r.Spec.Database.AzureVault.VaultUsernameSecret != "" && r.Spec.Database.AzureVault.VaultID == "") { + if (obs.Spec.Database.AzureVault.VaultID != "" && (obs.Spec.Database.AzureVault.VaultPasswordSecret == "" && obs.Spec.Database.AzureVault.VaultUsernameSecret == "")) || + (obs.Spec.Database.AzureVault.VaultPasswordSecret != "" && obs.Spec.Database.AzureVault.VaultID == "") || + (obs.Spec.Database.AzureVault.VaultUsernameSecret != "" && obs.Spec.Database.AzureVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("azure"), r.Spec.Database.AzureVault, + field.Invalid(field.NewPath("spec").Child("database").Child("azure"), obs.Spec.Database.AzureVault, ErrorSpecValidationMissingVaultField)) } // disallow usage of any other image than the observability-exporter // temporarily disabled - //if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { + //if obs.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(obs.Spec.Deployment.ExporterImage, AllowedExporterImage) { // e = append(e, - // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, + // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), obs.Spec.Deployment.ExporterImage, // ErrorSpecExporterImageNotAllowed)) //} // Return if any errors if len(e) > 0 { - return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, obs.Name, e) } return nil, nil @@ -144,25 +144,27 @@ func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Objec // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type func (r *DatabaseObserver) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - databaseobserverlog.Info("validate update", "name", r.Name) + obs := newObj.(*DatabaseObserver) + databaseobserverlog.Info("validate update", "name", obs.Name) var e field.ErrorList // disallow usage of any other image than the observability-exporter - if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { - e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, - ErrorSpecExporterImageNotAllowed)) - } + //if obs.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(obs.Spec.Deployment.ExporterImage, AllowedExporterImage) { + // e = append(e, + // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), obs.Spec.Deployment.ExporterImage, + // ErrorSpecExporterImageNotAllowed)) + //} // Return if any errors if len(e) > 0 { - return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, obs.Name, e) } return nil, nil } // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type func (r *DatabaseObserver) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - databaseobserverlog.Info("validate delete", "name", r.Name) + obs := obj.(*DatabaseObserver) + databaseobserverlog.Info("validate delete", "name", obs.Name) return nil, nil } diff --git a/apis/observability/v4/databaseobserver_webhook.go b/apis/observability/v4/databaseobserver_webhook.go index 91418c7d..3cb8be87 100644 --- a/apis/observability/v4/databaseobserver_webhook.go +++ b/apis/observability/v4/databaseobserver_webhook.go @@ -40,8 +40,6 @@ package v4 import ( "context" - "strings" - dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -82,7 +80,8 @@ var _ webhook.CustomDefaulter = &DatabaseObserver{} // Default implements webhook.CustomDefaulter so a webhook will be registered for the type func (r *DatabaseObserver) Default(ctx context.Context, obj runtime.Object) error { - databaseobserverlog.Info("default", "name", r.Name) + obs := obj.(*DatabaseObserver) + databaseobserverlog.Info("default", "name", obs.Name) return nil } @@ -93,34 +92,35 @@ var _ webhook.CustomValidator = &DatabaseObserver{} // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - databaseobserverlog.Info("validate create", "name", r.Name) + obs := obj.(*DatabaseObserver) + databaseobserverlog.Info("validate create", "name", obs.Name) var e field.ErrorList ns := dbcommons.GetWatchNamespaces() // Check for namespace/cluster scope access - if _, isDesiredNamespaceWithinScope := ns[r.Namespace]; !isDesiredNamespaceWithinScope && len(ns) > 0 { + if _, isDesiredNamespaceWithinScope := ns[obs.Namespace]; !isDesiredNamespaceWithinScope && len(ns) > 0 { e = append(e, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + field.Invalid(field.NewPath("metadata").Child("namespace"), obs.Namespace, "Oracle database operator doesn't watch over this namespace")) } // The other vault field must have value if one does - if (r.Spec.Database.OCIVault.VaultID != "" && r.Spec.Database.OCIVault.VaultPasswordSecret == "") || - (r.Spec.Database.OCIVault.VaultPasswordSecret != "" && r.Spec.Database.OCIVault.VaultID == "") { + if (obs.Spec.Database.OCIVault.VaultID != "" && obs.Spec.Database.OCIVault.VaultPasswordSecret == "") || + (obs.Spec.Database.OCIVault.VaultPasswordSecret != "" && obs.Spec.Database.OCIVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("oci"), r.Spec.Database.OCIVault, + field.Invalid(field.NewPath("spec").Child("database").Child("oci"), obs.Spec.Database.OCIVault, ErrorSpecValidationMissingVaultField)) } // The other vault field must have value if one does - if (r.Spec.Database.AzureVault.VaultID != "" && (r.Spec.Database.AzureVault.VaultPasswordSecret == "" && r.Spec.Database.AzureVault.VaultUsernameSecret == "")) || - (r.Spec.Database.AzureVault.VaultPasswordSecret != "" && r.Spec.Database.AzureVault.VaultID == "") || - (r.Spec.Database.AzureVault.VaultUsernameSecret != "" && r.Spec.Database.AzureVault.VaultID == "") { + if (obs.Spec.Database.AzureVault.VaultID != "" && (obs.Spec.Database.AzureVault.VaultPasswordSecret == "" && obs.Spec.Database.AzureVault.VaultUsernameSecret == "")) || + (obs.Spec.Database.AzureVault.VaultPasswordSecret != "" && obs.Spec.Database.AzureVault.VaultID == "") || + (obs.Spec.Database.AzureVault.VaultUsernameSecret != "" && obs.Spec.Database.AzureVault.VaultID == "") { e = append(e, - field.Invalid(field.NewPath("spec").Child("database").Child("azure"), r.Spec.Database.AzureVault, + field.Invalid(field.NewPath("spec").Child("database").Child("azure"), obs.Spec.Database.AzureVault, ErrorSpecValidationMissingVaultField)) } @@ -134,7 +134,7 @@ func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Objec // Return if any errors if len(e) > 0 { - return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, obs.Name, e) } return nil, nil @@ -142,25 +142,27 @@ func (r *DatabaseObserver) ValidateCreate(ctx context.Context, obj runtime.Objec // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type func (r *DatabaseObserver) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - databaseobserverlog.Info("validate update", "name", r.Name) + obs := newObj.(*DatabaseObserver) + databaseobserverlog.Info("validate update", "name", obs.Name) var e field.ErrorList // disallow usage of any other image than the observability-exporter - if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(r.Spec.Deployment.ExporterImage, AllowedExporterImage) { - e = append(e, - field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), r.Spec.Deployment.ExporterImage, - ErrorSpecExporterImageNotAllowed)) - } + //if r.Spec.Deployment.ExporterImage != "" && !strings.HasPrefix(obs.Spec.Deployment.ExporterImage, AllowedExporterImage) { + // e = append(e, + // field.Invalid(field.NewPath("spec").Child("exporter").Child("image"), obs.Spec.Deployment.ExporterImage, + // ErrorSpecExporterImageNotAllowed)) + //} // Return if any errors if len(e) > 0 { - return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, r.Name, e) + return nil, apierrors.NewInvalid(schema.GroupKind{Group: "observability.oracle.com", Kind: "DatabaseObserver"}, obs.Name, e) } return nil, nil } // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type func (r *DatabaseObserver) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - databaseobserverlog.Info("validate delete", "name", r.Name) + obs := obj.(*DatabaseObserver) + databaseobserverlog.Info("validate delete", "name", obs.Name) return nil, nil } From bb3d55c3a2b185636c247c960b890918be6e1bfe Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Tue, 19 Aug 2025 17:24:39 +0000 Subject: [PATCH 274/414] orestart scale down disk fix --- apis/database/v4/oraclerestart_types.go | 20 +-- apis/database/v4/zz_generated.deepcopy.go | 5 - commons/oraclerestart/oraclerestartcommon.go | 76 +++++++--- commons/oraclerestart/oraclerestartstatus.go | 14 +- .../database.oracle.com_oraclerestarts.yaml | 6 +- config/manager/kustomization.yaml | 4 +- .../database/oraclerestart_controller.go | 134 +++++++++++------- oracle-database-operator.yaml | 8 +- 8 files changed, 158 insertions(+), 109 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 821af981..6915b307 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -227,16 +227,16 @@ type ServiceSpec struct { type OracleRestartStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - DbName string `json:"DbName,omitempty"` - ConnectString string `json:"connectString,omitempty"` - PdbConnectString string `json:"pdbConnectString,omitempty"` - OracleRestartNodes []*OracleRestartNodestatus `json:"OracleRestartNodes,omitempty"` - ReleaseUpdate string `json:"releaseUpdate,omitempty"` - Role string `json:"role,omitempty"` - DbState string `json:"dbState,omitempty"` - State string `json:"state,omitempty"` - InstallNode string `json:"installNode,omitempty"` - ClientEtcHost []string `json:"clientEtcHost,omitempty"` + DbName string `json:"DbName,omitempty"` + ConnectString string `json:"connectString,omitempty"` + PdbConnectString string `json:"pdbConnectString,omitempty"` + ExternalConnectString string `json:"externalConnectString,omitempty"` + OracleRestartNodes []*OracleRestartNodestatus `json:"OracleRestartNodes,omitempty"` + ReleaseUpdate string `json:"releaseUpdate,omitempty"` + Role string `json:"role,omitempty"` + DbState string `json:"dbState,omitempty"` + State string `json:"state,omitempty"` + InstallNode string `json:"installNode,omitempty"` // +patchMergeKey=type // +patchStrategy=merge diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 3b94bb70..7d186ffd 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -3380,11 +3380,6 @@ func (in *OracleRestartStatus) DeepCopyInto(out *OracleRestartStatus) { } } } - if in.ClientEtcHost != nil { - in, out := &in.ClientEtcHost, &out.ClientEtcHost - *out = make([]string, len(*in)) - copy(*out, *in) - } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]v1.Condition, len(*in)) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 560ef924..a33b4786 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -663,6 +663,26 @@ func getAsmInstState(podName string, instance *oraclerestart.OracleRestart, spec } return AsmStorageStatus } +func GetAsmInstState(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +) *oraclerestart.AsmInstanceStatus { + AsmStorageStatus := &oraclerestart.AsmInstanceStatus{} + diskGroup := getAsmDiskgroup(podName, instance, specidx, kubeClient, kubeConfig, logger) + if diskGroup == "Pending" { + return AsmStorageStatus + } + dglist := strings.Split(diskGroup, ",") + for _, dg := range dglist { + asmdg := oraclerestart.AsmDiskgroupStatus{} + disks := getAsmDisks(podName, string(dg), instance, specidx, kubeClient, kubeConfig, logger) + redundancy := getAsmDgRedundancy(podName, string(dg), instance, specidx, kubeClient, kubeConfig, logger) + + asmdg.Name = string(dg) + asmdg.Disks = disks + asmdg.Redundancy = redundancy + AsmStorageStatus.Diskgroup = append(AsmStorageStatus.Diskgroup, asmdg) + } + return AsmStorageStatus +} func getAsmDiskgroup(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, ) string { @@ -796,19 +816,19 @@ func getConnStr(podName string, instance *oraclerestart.OracleRestart, specidx i return strings.TrimSpace(stdoutput) } -func getExternalConnStr(podName string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, +func getExternalConnStr( + podName string, + instance *oraclerestart.OracleRestart, + specidx int, + kubeClient kubernetes.Interface, + kubeConfig clientcmd.ClientConfig, + logger logr.Logger, ) string { - // stdoutput, _, err := ExecCommand(podName, getConnStrCmd(), kubeClient, kubeConfig, instance, logger) - // if err != nil { - // msg := "Pending" - // LogMessages("DEBUG", msg, err, instance, logger) - // return msg - // } - // return strings.TrimSpace(stdoutput) - // Fetch the OracleRestartNode-scan-lsnr service - svc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), "OracleRestartNode-scan-lsnr", metav1.GetOptions{}) + + // Get the dbmc1 NodePort service + svc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), instance.Spec.InstDetails.Name, metav1.GetOptions{}) if err != nil { - msg := "Failed to get OracleRestartNode-scan-lsnr service" + msg := "Failed to get dbmc1 service" LogMessages("DEBUG", msg, err, instance, logger) return "Pending" } @@ -821,15 +841,40 @@ func getExternalConnStr(podName string, instance *oraclerestart.OracleRestart, s break } } - if nodePort == 0 { - msg := "Failed to find NodePort for port 1521 in OracleRestartNode-scan-lsnr service" + msg := "Failed to find NodePort for port 1521 in dbmc1 service" + LogMessages("DEBUG", msg, err, instance, logger) + return "Pending" + } + + // Get the external IP of the first node in the cluster + nodeList, err := kubeClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) + if err != nil || len(nodeList.Items) == 0 { + msg := "Failed to list cluster nodes" + LogMessages("DEBUG", msg, err, instance, logger) + return "Pending" + } + var nodeIP string + for _, addr := range nodeList.Items[0].Status.Addresses { + // Try to get an ExternalIP, fallback to InternalIP if needed + if addr.Type == corev1.NodeExternalIP { + nodeIP = addr.Address + break + } else if addr.Type == corev1.NodeInternalIP && nodeIP == "" { + nodeIP = addr.Address + } + } + if nodeIP == "" { + msg := "Failed to get node IP address" LogMessages("DEBUG", msg, err, instance, logger) return "Pending" } + // Replace with your actual Oracle service name if different + serviceName := instance.Spec.ServiceDetails.Name + // Construct the external connect string - externalConnectString := fmt.Sprintf("OracleRestartNode-scan.%s.svc.cluster.local:%d/%s", instance.Namespace, nodePort, instance.Spec.ServiceDetails.Name) + externalConnectString := fmt.Sprintf("%s:%d/%s", nodeIP, nodePort, serviceName) return externalConnectString } @@ -853,9 +898,6 @@ func getClientEtcHost(podNames []string, instance *oraclerestart.OracleRestart, clientEtcHost = append(clientEtcHost, line) } - // Update instance status with the new ClientEtcHost - instance.Status.ClientEtcHost = clientEtcHost - // Assuming you are returning clientEtcHost as well for any further processing return clientEtcHost } diff --git a/commons/oraclerestart/oraclerestartstatus.go b/commons/oraclerestart/oraclerestartstatus.go index 480f68d2..a295b32f 100644 --- a/commons/oraclerestart/oraclerestartstatus.go +++ b/commons/oraclerestart/oraclerestartstatus.go @@ -229,7 +229,7 @@ func UpdateoraclerestartdbStatusData(OracleRestart *oraclerestartdb.OracleRestar OracleRestart.Status.ReleaseUpdate = getDBVersion(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) OracleRestart.Status.ConnectString = getConnStr(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) OracleRestart.Status.PdbConnectString = getPdbConnStr(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) - OracleRestart.Status.ClientEtcHost = getClientEtcHost(podNames, OracleRestart, 0, kubeClient, kubeConfig, logger, nodeDetails) + OracleRestart.Status.ExternalConnectString = getExternalConnStr(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) OracleRestart.Status.DbSecret = OracleRestart.Spec.DbSecret OracleRestart.Status.AsmDetails = getAsmInstState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) @@ -259,18 +259,6 @@ func contains(instance *oraclerestartdb.OracleRestart, oracleRestart *oraclerest return index, false } -// func getVipDetails(instance *oraclerestartdb.OracleRestart, oracleRestart *oraclerestartdb.OracleRestartNodestatus, oraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, rclient client.Client) map[string]string { -// strMap := make(map[string]string) -// _, err := CheckRacSvc(instance, "vip", oraRestartSpex, oraRestartSpex.VipSvcName, rclient) -// if err == nil { -// strMap["Name"] = oraRestartSpex.VipSvcName -// // See if service already exists and create if it doesn't -// } - -// return strMap - -// } - func getcrsAsmDeviceList(instance *oraclerestartdb.OracleRestart, oracleRestart *oraclerestartdb.OracleRestartNodestatus, oraRestartSpex oraclerestartdb.OracleRestartInstDetailSpec, rclient client.Client, kubeConfig clientcmd.ClientConfig, logger logr.Logger, kubeClient kubernetes.Interface) string { asmList := "" var err error diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index b65c444a..2702a8c6 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -775,10 +775,6 @@ spec: type: object type: array type: object - clientEtcHost: - items: - type: string - type: array conditions: items: properties: @@ -944,6 +940,8 @@ spec: type: object dbState: type: string + externalConnectString: + type: string externalSvcType: type: string image: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index ef6a3133..2726acb4 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lin.ocir.io/intsanjaysingh/marcstef/oo - newTag: 2.0-3 + newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database + newTag: restart-operator-sa diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index cbd49e97..ce2b22e6 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -161,7 +161,6 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques oracleRestart.Status.ReleaseUpdate = string(oraclerestartdb.OracleRestartFieldNotDefined) oracleRestart.Status.ConfigParams.DbHome = string(oraclerestartdb.OracleRestartFieldNotDefined) oracleRestart.Status.ConfigParams.GridHome = string(oraclerestartdb.OracleRestartFieldNotDefined) - oracleRestart.Status.ClientEtcHost = []string{string(oraclerestartdb.OracleRestartFieldNotDefined)} r.Status().Update(ctx, oracleRestart) } @@ -403,53 +402,55 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } case isDiskChanged && !isNewSetup: - err = r.validateASMDisks(oracleRestart, ctx) - if err != nil { - result = resultQ - r.Log.Info(err.Error()) - err = nilErr - return result, err - } - if len(oracleRestart.Spec.StorageClass) == 0 { - if ready, err := checkDaemonSetStatus(ctx, r, oracleRestart); err != nil || !ready { - r.Log.Info("Any of provided ASM Disks are invalid, pls check disk-check daemon set for logs. Fix the asm disk to the valid one and redeploy.") - err = r.cleanupDaemonSet(oracleRestart, ctx) - if err != nil { - result = resultQ - r.Log.Info(err.Error()) - err = nilErr - return result, err - } - addedAsmDisksMap := make(map[string]bool) - for _, disk := range addedAsmDisks { - addedAsmDisksMap[disk] = true - } - for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for index, diskName := range diskBySize.DiskNames { - if _, ok := addedAsmDisksMap[diskName]; ok { - r.Log.Info("Found disk at index", "index", index) - - err = oraclerestartcommon.DelORestartPVC(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) - if err != nil { - return resultQ, err - } - - err = oraclerestartcommon.DelORestartPv(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) - if err != nil { - return resultQ, err + if len(addedAsmDisks) > 0 { + err = r.validateASMDisks(oracleRestart, ctx) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } + if len(oracleRestart.Spec.StorageClass) == 0 { + if ready, err := checkDaemonSetStatus(ctx, r, oracleRestart); err != nil || !ready { + r.Log.Info("Any of provided ASM Disks are invalid, pls check disk-check daemon set for logs. Fix the asm disk to the valid one and redeploy.") + err = r.cleanupDaemonSet(oracleRestart, ctx) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } + addedAsmDisksMap := make(map[string]bool) + for _, disk := range addedAsmDisks { + addedAsmDisksMap[disk] = true + } + for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for index, diskName := range diskBySize.DiskNames { + if _, ok := addedAsmDisksMap[diskName]; ok { + r.Log.Info("Found disk at index", "index", index) + + err = oraclerestartcommon.DelORestartPVC(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + if err != nil { + return resultQ, err + } + + err = oraclerestartcommon.DelORestartPv(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + if err != nil { + return resultQ, err + } } } } - } - if err = r.SetCurrentSpec(ctx, oracleRestart, req); err != nil { - r.Log.Error(err, "Failed to set current spec annotation") - oracleRestart.Spec.IsFailed = true + if err = r.SetCurrentSpec(ctx, oracleRestart, req); err != nil { + r.Log.Error(err, "Failed to set current spec annotation") + oracleRestart.Spec.IsFailed = true + return resultQ, err + } return resultQ, err + } else { + r.Log.Info("Provided ASM Disks are valid, proceeding further") } - return resultQ, err - } else { - r.Log.Info("Provided ASM Disks are valid, proceeding further") } } cmName := oracleRestart.Spec.InstDetails.Name + oracleRestart.Name + "-cmap" @@ -472,14 +473,16 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } } } - err = r.cleanupDaemonSet(oracleRestart, ctx) - if err != nil { - result = resultQ - r.Log.Info(err.Error()) - err = nilErr - return result, err - } + if len(addedAsmDisks) > 0 { + err = r.cleanupDaemonSet(oracleRestart, ctx) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } + } if oracleRestartInst.OnsTargetPort != nil { OraRestartSpex := oracleRestart.Spec.InstDetails @@ -499,7 +502,6 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques if err != nil { return ctrl.Result{}, err } - r.Log.Info("ONS Started") } @@ -1830,6 +1832,7 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or var addnodeFlag bool data = append(data, "OP_TYPE=setuprac") + // removing this breaks HAS label build setup data = append(data, "IGNORE_CRS_PREREQS=TRUE") data = append(data, "IGNORE_DB_PREREQS=TRUE") @@ -1966,8 +1969,6 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or if instance.Spec.ConfigParams.RuPatchLocation != "" { data = append(data, "APPLY_RU_LOCATION="+instance.Spec.ConfigParams.RuPatchLocation) - } else { - data = append(data, "APPLY_RU_LOCATION="+utils.OraRuPatchStageLocation) } if instance.Spec.ConfigParams.RuFolderName != "" { @@ -2613,6 +2614,31 @@ func (r *OracleRestartReconciler) createOrReplaceSfsAsm(ctx context.Context, req // isDelete := false inUse := false if len(removedAsmDisks) > 0 { + OraRacSpex := oracleRestart.Spec.InstDetails + racSfSet, err := oraclerestartcommon.CheckSfset(OraRacSpex.Name, oracleRestart, r.Client) + if err != nil { + errMsg := fmt.Errorf("failed to retrieve StatefulSet for RAC database '%s': %w", OraRacSpex.Name, err) + r.Log.Error(err, errMsg.Error()) + return reconcile.Result{}, errMsg + } + + // Step 2: Get the Pod list + podList, err := oraclerestartcommon.GetPodList(racSfSet.Name, oracleRestart, r.Client, OraRacSpex) + if err != nil { + errMsg := fmt.Errorf("failed to retrieve pod list for StatefulSet '%s': %w", racSfSet.Name, err) + r.Log.Error(err, errMsg.Error()) + return reconcile.Result{}, errMsg + } + if len(podList.Items) == 0 { + errMsg := fmt.Errorf("no pods found for StatefulSet '%s'", racSfSet.Name) + r.Log.Error(errMsg, "Empty pod list") + return reconcile.Result{}, errMsg + } + + // Step 3: Use last pod to get ASM state + podName := podList.Items[len(podList.Items)-1].Name + oracleRestart.Status.AsmDetails = oraclerestartcommon.GetAsmInstState(podName, oracleRestart, 0, r.kubeClient, r.kubeConfig, r.Log) + // Check if removed disks are in use in any diskgroup asmInstanceStatus := oracleRestart.Status.AsmDetails // isDelete = true @@ -3395,7 +3421,7 @@ func (r *OracleRestartReconciler) updateONS(ctx context.Context, podList *corev1 podName := pod.Name cmd := fmt.Sprintf("python3 /opt/scripts/startup/scripts/main.py --ons=%s", onsState) - reqLogger.Info("Executing command to update ONS", "Pod.Name", podName, "Command", cmd) + // reqLogger.Info("Executing command to update ONS", "Pod.Name", podName, "Command", cmd) stdout, stderr, err := oraclerestartcommon.ExecCommand( podName, @@ -3410,6 +3436,8 @@ func (r *OracleRestartReconciler) updateONS(ctx context.Context, podList *corev1 reqLogger.Error(err, "Failed to execute command", "Pod.Name", podName, "Command", cmd, "Stdout", stdout, "Stderr", stderr) return err } + r.Log.Info("ONS Running successfully", "podName", podName) } + return nil } diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index f9c85e37..de951ce3 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -11631,10 +11631,6 @@ spec: type: object type: array type: object - clientEtcHost: - items: - type: string - type: array conditions: items: properties: @@ -11800,6 +11796,8 @@ spec: type: object dbState: type: string + externalConnectString: + type: string externalSvcType: type: string image: @@ -17124,7 +17122,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: lin.ocir.io/intsanjaysingh/marcstef/oo:2.0-3 + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:restart-operator-sa imagePullPolicy: Always name: manager ports: From 3b574f8ca1bb88ed7b281563d1fe39cff6e5d7d4 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Tue, 19 Aug 2025 20:36:42 +0000 Subject: [PATCH 275/414] unsed variable cleanup --- controllers/database/lrest_controller.go | 34 ++++-------- controllers/database/lrpdb_controller.go | 66 +++++++++++++----------- docs/multitenant/usecase/makefile | 16 +++++- 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/controllers/database/lrest_controller.go b/controllers/database/lrest_controller.go index 63fb38c1..f8682e54 100644 --- a/controllers/database/lrest_controller.go +++ b/controllers/database/lrest_controller.go @@ -304,6 +304,7 @@ func (r *LRESTReconciler) validateLRESTPods2(ctx context.Context, req ctrl.Reque } +/* Un-used function func (r *LRESTReconciler) validateLRESTPods(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { log := r.Log.WithValues("validateLRESTPod", req.NamespacedName) @@ -360,6 +361,7 @@ func (r *LRESTReconciler) validateLRESTPods(ctx context.Context, req ctrl.Reques lrest.Status.Msg = "" return nil } +*/ /* *********************** @@ -474,7 +476,7 @@ func (r *LRESTReconciler) createReplicaSetSpec(lrest *dbapi.LREST) *appsv1.Repli - Evaluate change in Spec post creation and instantiation /******************************************************* */ -func (r *LRESTReconciler) deleteReplicaSet(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { +func (r *LRESTReconciler) deleteReplicaSet(req ctrl.Request, lrest *dbapi.LREST) error { log := r.Log.WithValues("deleteReplicaSet", req.NamespacedName) k_client, err := kubernetes.NewForConfig(r.Config) @@ -538,7 +540,7 @@ func (r *LRESTReconciler) evaluateSpecChange(ctx context.Context, req ctrl.Reque if lrestSpecChange { // Delete existing ReplicaSet - err = r.deleteReplicaSet(ctx, req, lrest) + err = r.deleteReplicaSet(req, lrest) if err != nil { return err } @@ -692,7 +694,7 @@ func (r *LRESTReconciler) manageLRESTDeletion(ctx context.Context, req ctrl.Requ } } - err := r.deleteLRESTInstance(ctx, req, lrest) + err := r.deleteLRESTInstance(req, lrest) if err != nil { log.Info("Could not delete LREST Resource", "LREST Name", lrest.Spec.LRESTName, "err", err.Error()) return err @@ -708,7 +710,7 @@ func (r *LRESTReconciler) manageLRESTDeletion(ctx context.Context, req ctrl.Requ /*********************************************** */ -func (r *LRESTReconciler) deleteLRESTInstance(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) error { +func (r *LRESTReconciler) deleteLRESTInstance(req ctrl.Request, lrest *dbapi.LREST) error { log := r.Log.WithValues("deleteLRESTInstance", req.NamespacedName) @@ -809,23 +811,15 @@ func (r *LRESTReconciler) checkSecret(ctx context.Context, req ctrl.Request, lre /* ************************************************ - Delete Secrets - /*********************************************** -*/ + - No longer used +************************************************/ +/* func (r *LRESTReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, lrest *dbapi.LREST) { log := r.Log.WithValues("deleteSecrets", req.NamespacedName) log.Info("Deleting LREST secrets") secret := &corev1.Secret{} - /* - err := r.Get(ctx, types.NamespacedName{Name: lrest.Spec.SysAdminPwd.Secret.SecretName, Namespace: lrest.Namespace}, secret) - if err == nil { - err := r.Delete(ctx, secret) - if err == nil { - log.Info("Deleted the secret : " + lrest.Spec.SysAdminPwd.Secret.SecretName) - } - } - */ err := r.Get(ctx, types.NamespacedName{Name: lrest.Spec.LRESTAdminUser.Secret.SecretName, Namespace: lrest.Namespace}, secret) if err == nil { @@ -842,15 +836,6 @@ func (r *LRESTReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, l log.Info("Deleted the secret : " + lrest.Spec.LRESTAdminPwd.Secret.SecretName) } } - /* - err = r.Get(ctx, types.NamespacedName{Name: lrest.Spec.LRESTPwd.Secret.SecretName, Namespace: lrest.Namespace}, secret) - if err == nil { - err := r.Delete(ctx, secret) - if err == nil { - log.Info("Deleted the secret : " + lrest.Spec.LRESTPwd.Secret.SecretName) - } - } - */ err = r.Get(ctx, types.NamespacedName{Name: lrest.Spec.WebLrestServerUser.Secret.SecretName, Namespace: lrest.Namespace}, secret) if err == nil { @@ -868,6 +853,7 @@ func (r *LRESTReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, l } } } +*/ /* ************************************************************* diff --git a/controllers/database/lrpdb_controller.go b/controllers/database/lrpdb_controller.go index cd7f7b00..2f44fb39 100644 --- a/controllers/database/lrpdb_controller.go +++ b/controllers/database/lrpdb_controller.go @@ -134,6 +134,7 @@ func bis(bitmask int, bitval int) int { return bitmask } +/* func bit(bitmask int, bitval int) bool { if bitmask&bitval != 0 { return true @@ -141,11 +142,14 @@ func bit(bitmask int, bitval int) bool { return false } } +*/ +/* func bid(bitmask int, bitval int) int { bitmask ^= ((bitval) & (bitmask)) return bitmask } +*/ // LRPDBReconciler reconciles a LRPDB object type LRPDBReconciler struct { @@ -752,7 +756,7 @@ func (r *LRPDBReconciler) OpenLRPDB(ctx context.Context, req ctrl.Request, lrpdb /* After database openining we reapply the config map if warning is present */ if lrpdb.Spec.LRPDBState == "OPEN" { - if bit(lrpdb.Status.CmBitstat, MPWARN|MPINIT) { + if Bit(lrpdb.Status.CmBitstat, MPWARN|MPINIT) { log.Info("re-apply config map") r.ApplyConfigMap(ctx, req, lrpdb) @@ -1039,7 +1043,7 @@ func (r *LRPDBReconciler) DeleteLRPDBDeclarative(ctx context.Context, req ctrl.R ********************************************************************* */ -func (r *LRPDBReconciler) checkPDBforCloninig(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB, targetPdbName string) (int, error) { +func (r *LRPDBReconciler) checkPDBforCloninig(ctx context.Context, req ctrl.Request, targetPdbName string) (int, error) { log := r.Log.WithValues("checkPDBforCloninig", req.NamespacedName) var pdbCounter int pdbCounter = 0 @@ -1117,7 +1121,7 @@ func (r *LRPDBReconciler) CloneLRPDB(ctx context.Context, req ctrl.Request, lrpd //* check the existence of lrpdb.Spec.SrcLRPDBName // var allErrs field.ErrorList - pdbCounter, _ := r.checkPDBforCloninig(ctx, req, lrpdb, lrpdb.Spec.SrcLRPDBName) + pdbCounter, _ := r.checkPDBforCloninig(ctx, req, lrpdb.Spec.SrcLRPDBName) if pdbCounter == 0 { log.Info("target pdb " + lrpdb.Spec.SrcLRPDBName + " does not exists or is not open") allErrs = append(allErrs, field.NotFound(field.NewPath("Spec").Child("LRPDBName"), " "+lrpdb.Spec.LRPDBName+" does not exist : failure")) @@ -1200,9 +1204,10 @@ func (r *LRPDBReconciler) CloneLRPDB(ctx context.Context, req ctrl.Request, lrpd /* ************************************************************** - Check for Duplicate LRPDB. Same LRPDB name on the same LREST resource. - + - Decomissioned function ************************************************************** */ +/* func (r *LRPDBReconciler) checkDuplicateLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { log := r.Log.WithValues("checkDuplicateLRPDB", req.NamespacedName) @@ -1241,6 +1246,7 @@ func (r *LRPDBReconciler) checkDuplicateLRPDB(ctx context.Context, req ctrl.Requ } return nil } +*/ /* ********************************************************************* @@ -1280,9 +1286,10 @@ func (r *LRPDBReconciler) getLRESTResource(ctx context.Context, req ctrl.Request /* ********************************************************************* - GET THE LREST POD FOR THE LREST MENTIONED IN THE LRPDB SPEC - + - Decommissione function ********************************************************************* */ +/* func (r *LRPDBReconciler) getLRESTPod(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) (corev1.Pod, error) { log := r.Log.WithValues("getLRESTPod", req.NamespacedName) @@ -1307,6 +1314,7 @@ func (r *LRPDBReconciler) getLRESTPod(ctx context.Context, req ctrl.Request, lrp log.Info("Found LREST Pod for LREST", "Name", lrestResName, "Pod Name", lrestPod.Name, "LREST Container hostname", lrestPod.Spec.Hostname) return lrestPod, nil } +*/ /* ********************************************************************* @@ -1634,10 +1642,6 @@ func (r *LRPDBReconciler) execPLSQL(ctx context.Context, req ctrl.Request, lrpdb log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) return err } - if err != nil { - log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) - return err - } r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) @@ -1657,7 +1661,7 @@ func (r *LRPDBReconciler) execPLSQL(ctx context.Context, req ctrl.Request, lrpdb r.Recorder.Eventf(lrpdb, EvLevel, formatted, " CODE:SQLCODE '%s':'%d'", skey, lrpdb.Status.SqlCode) /* sql execution complete successfully than report the name of the tag */ - if lrpdb.Status.SqlCode == 0 && err == nil { + if lrpdb.Status.SqlCode == 0 { lrpdb.Status.LastPLSQL = skey r.UpdateStatus(ctx, req, lrpdb) /* reset code buffer */ @@ -1732,9 +1736,10 @@ func (r *LRPDBReconciler) getLRPDBState(ctx context.Context, req ctrl.Request, l /* ************************************************ - Map Database LRPDB to Kubernetes LRPDB CR + - Decommissioned function +************************************************/ -/*********************************************** -*/ +/* func (r *LRPDBReconciler) mapLRPDB(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { log := r.Log.WithValues("mapLRPDB", req.NamespacedName) @@ -1790,12 +1795,14 @@ func (r *LRPDBReconciler) mapLRPDB(ctx context.Context, req ctrl.Request, lrpdb log.Info("Successfully mapped LRPDB to Kubernetes resource", "LRPDB Name", lrpdb.Spec.LRPDBName) return nil } +*/ /* ************************************************ - Delete a LRPDB - /*********************************************** -*/ + - Decommissioned function +************************************************/ +/* func (r *LRPDBReconciler) deleteLRPDB2(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { log := r.Log.WithValues("deleteLRPDB2", req.NamespacedName) @@ -1803,7 +1810,6 @@ func (r *LRPDBReconciler) deleteLRPDB2(ctx context.Context, req ctrl.Request, lr errstate := r.getLRPDBState(ctx, req, lrpdb) if errstate != nil { if lrpdb.Status.SqlCode == 1403 { - // BUG 36752336: log.Info("Database does not exists ") r.Delete(context.Background(), lrpdb, client.GracePeriodSeconds(1)) return nil @@ -1815,7 +1821,6 @@ func (r *LRPDBReconciler) deleteLRPDB2(ctx context.Context, req ctrl.Request, lr } log.Error(errstate, "Failed to update status for :"+lrpdb.Name, "err", errstate.Error()) return errstate - //* if the pdb does not exists delete the crd *// } @@ -1858,17 +1863,17 @@ func (r *LRPDBReconciler) deleteLRPDB2(ctx context.Context, req ctrl.Request, lr log.Info("Successfully deleted LRPDB resource") return nil } +*/ /* ************************************************ - Check LRPDB deletion - /*********************************************** -*/ + - Decommissioned function +*********************************************** +/* func (r *LRPDBReconciler) manageLRPDBDeletion(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { log := r.Log.WithValues("manageLRPDBDeletion", req.NamespacedName) - // Check if the LRPDB instance is marked to be deleted, which is - // indicated by the deletion timestamp being set. isLRPDBMarkedToBeDeleted := lrpdb.GetDeletionTimestamp() != nil if isLRPDBMarkedToBeDeleted { log.Info("Marked to be deleted") @@ -1876,8 +1881,6 @@ func (r *LRPDBReconciler) manageLRPDBDeletion(ctx context.Context, req ctrl.Requ r.Status().Update(ctx, lrpdb) if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { - // Remove LRPDBFinalizer. Once all finalizers have been - // removed, the object will be deleted. log.Info("Removing finalizer") controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) err := r.Update(ctx, lrpdb) @@ -1890,7 +1893,6 @@ func (r *LRPDBReconciler) manageLRPDBDeletion(ctx context.Context, req ctrl.Requ } } - // Add finalizer for this CR if !controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { log.Info("Adding finalizer") controllerutil.AddFinalizer(lrpdb, LRPDBFinalizer) @@ -1903,13 +1905,15 @@ func (r *LRPDBReconciler) manageLRPDBDeletion(ctx context.Context, req ctrl.Requ } return nil } +*/ /* ************************************************ - Finalization logic for LRPDBFinalizer - + - Decommisssioned function *********************************************** */ +/* func (r *LRPDBReconciler) deleteLRPDBInstance(req ctrl.Request, ctx context.Context, lrpdb *dbapi.LRPDB) error { log := r.Log.WithValues("deleteLRPDBInstance", req.NamespacedName) @@ -1949,6 +1953,7 @@ func (r *LRPDBReconciler) deleteLRPDBInstance(req ctrl.Request, ctx context.Cont log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdbName) return nil } +*/ /* *********************************************************** @@ -2095,6 +2100,7 @@ func (r *LRPDBReconciler) getEncriptedSecret(ctx context.Context, req ctrl.Reque return DecVal, nil } +/* func (r *LRPDBReconciler) manageLRPDBDeletion2(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) error { log := r.Log.WithValues("manageLRPDBDeletion", req.NamespacedName) if lrpdb.ObjectMeta.DeletionTimestamp.IsZero() { @@ -2137,10 +2143,11 @@ func (r *LRPDBReconciler) manageLRPDBDeletion2(ctx context.Context, req ctrl.Req log.Info("Call Delete()") _, errdelete := r.callAPI(ctx, req, lrpdb, url, valuesdrop, "DELETE") if errdelete != nil { - log.Error(errdelete, "Fail to delete lrpdb :"+lrpdb.Name, "err", err.Error()) + log.Error(errdelete, "Fail to delete lrpdb :"+lrpdb.Name, "err", errdelete.Error()) return errdelete } - } /* END OF ASSERTIVE SECTION */ + + } log.Info("Marked to be deleted") lrpdb.Status.Status = true @@ -2159,6 +2166,7 @@ func (r *LRPDBReconciler) manageLRPDBDeletion2(ctx context.Context, req ctrl.Req return nil } +*/ func (r *LRPDBReconciler) InitConfigMap(ctx context.Context, req ctrl.Request, lrpdb *dbapi.LRPDB) *corev1.ConfigMap { log := r.Log.WithValues("InitConfigMap", req.NamespacedName) @@ -2339,7 +2347,7 @@ func (r *LRPDBReconciler) ManageConfigMapForCloningAndPlugin(ctx context.Context then we need to iniialized the init mask. This is the case for pdb generated by clone and plug action */ - if lrpdb.Spec.Action != "CREATE" && lrpdb.Spec.Action != "APPLYSQL" && lrpdb.Spec.PDBConfigMap != "" && bit(lrpdb.Status.CmBitstat, MPINIT) == false { + if lrpdb.Spec.Action != "CREATE" && lrpdb.Spec.Action != "APPLYSQL" && lrpdb.Spec.PDBConfigMap != "" && Bit(lrpdb.Status.CmBitstat, MPINIT) == false { if r.InitConfigMap(ctx, req, lrpdb) == nil { log.Info("Cannot initialize config map for pdb.........:" + lrpdb.Spec.LRPDBName) return nil @@ -2994,7 +3002,7 @@ func NewCallAPISQL(intr interface{}, ctx context.Context, req ctrl.Request, lrpd json.Unmarshal([]byte(TestBuffer), &jsonMap) jsonValue, _ := json.Marshal(jsonMap) Httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) - if bit(lrpdb.Spec.Debug, DBGAPI) { + if Bit(lrpdb.Spec.Debug, DBGAPI) { fmt.Println("=========================PLSQLDEBUG==============================") fmt.Println(string(jsonValue)) fmt.Println("=========================PLSQLDEBUG==============================") @@ -3009,7 +3017,7 @@ func NewCallAPISQL(intr interface{}, ctx context.Context, req ctrl.Request, lrpd if ok3 { jsonValue, _ := json.Marshal(payloadpdb) Httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) - if bit(lrpdb.Spec.Debug, DBGAPI) { + if Bit(lrpdb.Spec.Debug, DBGAPI) { fmt.Println("=========================PLSQLDEBUG==============================") fmt.Println(string(jsonValue)) fmt.Println("=========================PLSQLDEBUG==============================") diff --git a/docs/multitenant/usecase/makefile b/docs/multitenant/usecase/makefile index 44b71cf0..55ba60a5 100644 --- a/docs/multitenant/usecase/makefile +++ b/docs/multitenant/usecase/makefile @@ -946,7 +946,7 @@ pkg: - $(RM) -rf /tmp/pkgtestplan $(MKDIR) /tmp/pkgtestplan $(CP) -R * /tmp/pkgtestplan - $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ + $(CP) ../../../oracle-database-operator.yaml /tmp/pkgtestplan/ $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan ################ @@ -1185,6 +1185,20 @@ tkplsqlexec: $(KUBECTL) get events -n $(PDBNAMESPACE) --sort-by='.lastTimestamp' --field-selector involvedObject.name=pdb1|grep APPLYSQL +tkplsqlexec01: + @$(call msg,"delete config map ") + -- $(KUBECTL) delete configmap $(PLSQLMAP) -n $(PDBNAMESPACE) + @$(call msg,"recreating config sql map") + $(KUBECTL) apply -f $(CONFIG_MAP_SQL) + $(KUBECTL) describe configmap $(PLSQLMAP) -n $(PDBNAMESPACE) + @$(call msg,"applying config sql map") + $(KUBECTL) patch lrpdb pdb1 -n $(PDBNAMESPACE) -p \ + '{"spec":{"codeconfigmap":"$(PLSQLMAP)"}}' --type=merge + sleep 10 + $(KUBECTL) get events --sort-by='.lastTimestamp' -n $(PDBNAMESPACE) + $(KUBECTL) get events -n $(PDBNAMESPACE) --sort-by='.lastTimestamp' --field-selector involvedObject.name=pdb1|grep APPLYSQL + + tkaudosicov: @$(call msg,"==>AUTODISCOVER TESTING<==") From 9e2bd8b11a02cb7c3a310ef25a1ccef42734fae3 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Wed, 20 Aug 2025 14:03:13 +0000 Subject: [PATCH 276/414] multitenant usecase makefile --- .../multitenant/usecase/config_map_plsql.yaml | 2 +- .../multitenant/usecase/create_lrest_pod.yaml | 2 +- docs/multitenant/usecase/makefile | 112 +++++++++++++++++- docs/multitenant/usecase/node_rbac.yaml | 27 +++++ docs/multitenant/usecase/parameters.txt | 38 +++++- .../usecase/plug_pdb1_resource.yaml | 2 +- .../usecase/unplug_pdb1_resource.yaml | 2 +- 7 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 docs/multitenant/usecase/node_rbac.yaml diff --git a/docs/multitenant/usecase/config_map_plsql.yaml b/docs/multitenant/usecase/config_map_plsql.yaml index d7b8000c..1a4f998e 100644 --- a/docs/multitenant/usecase/config_map_plsql.yaml +++ b/docs/multitenant/usecase/config_map_plsql.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - name: + name: sql-map-example1 namespace: pdbnamespace data: plblock1.sql: | diff --git a/docs/multitenant/usecase/create_lrest_pod.yaml b/docs/multitenant/usecase/create_lrest_pod.yaml index 323ce053..926a4462 100644 --- a/docs/multitenant/usecase/create_lrest_pod.yaml +++ b/docs/multitenant/usecase/create_lrest_pod.yaml @@ -10,7 +10,7 @@ spec: dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" replicas: 1 deletePdbCascade: true - autodiscover: + autodiscover: false namespaceAutoDiscover: pdbnamespace clusterIp: false loadBalancer: false diff --git a/docs/multitenant/usecase/makefile b/docs/multitenant/usecase/makefile index 55ba60a5..7862fd09 100644 --- a/docs/multitenant/usecase/makefile +++ b/docs/multitenant/usecase/makefile @@ -15,7 +15,7 @@ # files. Edit parameters.txt with your enviroment # informartion and execute the following steps # -# 1) make operator +# 1) make opsetup # it configures the operator yaml files with the # watch namelist required by the multitenant controllers # @@ -60,7 +60,8 @@ DATE := `date "+%y%m%d%H%M%S"` ###################### export PARAMETERS=parameters.txt -export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) +#export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) +export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|awk -F ':' '{$$1="" ;printf("%s",$$2) }') export DBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep DBUSER|cut -d : -f 2) export DBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep DBPASS|cut -d : -f 2) export WBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBUSER|cut -d : -f 2) @@ -76,8 +77,10 @@ export SERVICENAM=$(shell cat $(PARAMETERS)|grep -v ^\#|grep SERVICENAMEACCOUNT| export OPENSHIFT=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPENSHIFT|cut -d : -f 2) export PLSQLMAP=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PLSQLMAP|cut -d : -f 2) export OPRNAMESPACE=oracle-database-operator-system -export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml +export CODE_TREE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CODE_TREE|cut -d : -f 2) +export ORACLE_OPERATOR_YAML=$(CODE_TREE)/oracle-database-operator.yaml export AUTODISCOVER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep AUTODISCOVER|cut -d : -f 2) +export CERT_MANAGER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CERT_MANAGER| awk -F ':' '{$$1="" ;printf("%s:%s",$$2,$$3) }') export TEST_EXEC_TIMEOUT=3m REST_SERVER=lrest @@ -136,6 +139,7 @@ export LRPDBMAP2=map_pdb2_resource.yaml export LRPDBMAP3=map_pdb3_resource.yaml export SECURITYCTX=security_context.yaml +export NODE_RBAC=node_rbac.yaml export ALTERSYSTEMYAML=altersystem_pdb1_resource.yaml export CONFIG_MAP=config_map_pdb.yaml @@ -157,6 +161,7 @@ MAKE=/usr/bin/make MAKEFILE=./makefile check: + @printf "LRESTIMG...............:%s\n" $(LRESTIMG) @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) @printf "DBUSER.................:%s\n" $(DBUSER) @printf "DBPASS.................:%s\n" $(DBPASS) @@ -171,6 +176,10 @@ check: @printf "APIVERSION.............:%s\n" $(APIVERSION) @printf "PLSQLMAP...............:%s\n" $(PLSQLMAP) @printf "AUTODISCOVER...........:%s\n" $(AUTODISCOVER) + @printf "ORACLE_OPERATOR_YAML...:%s\n" $(ORACLE_OPERATOR_YAML) + @printf "CODE_TREE..............:%s\n" $(CODE_TREE) + @printf "CERT_MANAGER...........:%s\n" $(CERT_MANAGER) + @printf "OPENSHIFT..............:%s\n" $(OPENSHIFT) define msg @printf "\033[31;7m%s\033[0m\r" "......................................]" @@ -258,12 +267,70 @@ endef export opr = $(value _opr) -operator: +opsetup: prsyaml crtmgrappl nscrt bndappl opapply rbacap + +#opclean: rbacdl opdelete bnddel crtmgrdel +opclean: rbacdl bnddel crtmgrdel opdelete + +opapply: + @$(call msg,"Applying operator yaml file") + $(KUBECTL) apply -f `basename ${ORACLE_OPERATOR_YAML}` + +opdelete: + - $(KUBECTL) delete -f `basename ${ORACLE_OPERATOR_YAML}` + +prsyaml: # @ eval "$$opr" $(CP) ${ORACLE_OPERATOR_YAML} . ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG $(SED) -i 's/value: ""/value: $(OPRNAMESPACE),$(PDBNAMESPACE),$(LRSNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` +### Certmanager ### +crtmgrappl: + @$(call msg,"Install cert-manager") + $(KUBECTL) apply -f $(CERT_MANAGER) + $(KUBECTL) wait --for jsonpath='{.webhooks[0].clientConfig.caBundle'} \ + validatingwebhookconfiguration cert-manager-webhook --timeout=$(TEST_EXEC_TIMEOUT) + @$(call msg,"Cert manager application completed") + +crtmgrdel: + @$(call msg,"Deleting cert manager $(CERT_MANAGER)") + - $(KUBECTL) delete -f $(CERT_MANAGER) + +### Namespaces creation ### +nscrt: + @$(call msg,"Namespace scope: creating namespace") + - $(KUBECTL) create namespace $(LRSNAMESPACE) + - $(KUBECTL) create namespace $(PDBNAMESPACE) + +### Namespace deletion ### +nsdel: + @$(call msg,"Deleting namespaces $(LRSNAMESPACE) $(PDBNAMESPACE)") + $(KUBECTL) delete namespace $(LRSNAMESPACE) + $(KUBECTL) delete namespace $(PDBNAMESPACE) + +### Namespace Scoped Deployment ### +bndappl: + @$(call msg," Namespace Scoped Deployment: Binding $(PDBNAMESPACE) $(LRSNAMESPACE)") + - $(KUBECTL) apply -f $(PDBNAMESPACE)_binding.yaml + - $(KUBECTL) apply -f $(LRSNAMESPACE)_binding.yaml + +### Namespace Scope Remove ### +bnddel: + @$(call msg," Namespace Scoped remove $(PDBNAMESPACE) $(LRSNAMESPACE)") + - $(KUBECTL) delete -f $(PDBNAMESPACE)_binding.yaml + - $(KUBECTL) delete -f $(LRSNAMESPACE)_binding.yaml + +### Cluster Role Binfing ### +rbacap: + @$(call msg," ClusterRole and ClusterRoleBinding for NodePort services") + - $(KUBECTL) apply -f ${NODE_RBAC} + +### Remove Cluster Role Binding ### +rbacdl: + @$(call msg," Clean $(NODE_RBAC)") + - $(KUBECTL) delete -f ${NODE_RBAC} + define _script00 cat < authsection.yaml @@ -334,6 +401,36 @@ subjects: namespace: oracle-database-operator-system EOF +cat < ${NODE_RBAC} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role-node +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-manager-role-node-cluster-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role-node +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- +EOF + endef export script00 = $(value _script00) secyaml: @@ -816,7 +913,7 @@ EOF ## Auth information -for _file in ${LRPDBCRE1} ${LRPDBCRE2} ${LRPDBOPEN1} ${LRPDBOPEN2} ${LRPDBOPEN3} ${LRPDBCLOSE1} ${LRPDBCLOSE2} ${LRPDBCLOSE3} ${LRPDBCLONE1} ${LRPDBCLONE2} ${LRPDBDELETE1} ${LRPDBDELETE2} ${LRPDBDELETE3} ${LRPDBUNPLUG1} ${LRPDBPLUG1} ${LRPDBMAP1} ${LRPDBMAP2} ${LRPDBMAP3} ${ALTERSYSTEMYAML} +for _file in ${LRPDBCRE1} ${LRPDBCRE2} ${LRPDBOPEN1} ${LRPDBOPEN2} ${LRPDBOPEN3} ${LRPDBCLOSE1} ${LRPDBCLOSE2} ${LRPDBCLOSE3} ${LRPDBCLONE1} ${LRPDBCLONE2} ${LRPDBDELETE1} ${LRPDBDELETE2} ${LRPDBDELETE3} ${LRPDBUNPLUG1} ${LRPDBPLUG1} ${LRPDBMAP1} ${LRPDBMAP2} ${LRPDBMAP3} ${ALTERSYSTEMYAML} do ls -ltr ${_file} cat authsection.yaml >> ${_file} @@ -990,6 +1087,9 @@ listimage: getvaliduid: $(KUBECTL) get namespace $(LRSNAMESPACE) -ojson | jq '.metadata.annotations | with_entries(select(.key | contains("sa.scc")))' +mgrrestart: + $(KUBECTL) rollout restart -n $(OPRNAMESPACE) deployment oracle-database-operator-controller-manager + ####################################################### #### TEST SECTION #### @@ -1244,7 +1344,7 @@ runall03: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run0 $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb1 $(MAKE) -f $(MAKEFILE) run05.1 run06.1 runall04: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 run05.1 run06.1 closepdb1 - +runall05: genyaml tkapplyinit run00 run01.1 run02.1 run03.1 diff --git a/docs/multitenant/usecase/node_rbac.yaml b/docs/multitenant/usecase/node_rbac.yaml new file mode 100644 index 00000000..ac474873 --- /dev/null +++ b/docs/multitenant/usecase/node_rbac.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oracle-database-operator-manager-role-node +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: oracle-database-operator-manager-role-node-cluster-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: oracle-database-operator-manager-role-node +subjects: +- kind: ServiceAccount + name: default + namespace: oracle-database-operator-system +--- diff --git a/docs/multitenant/usecase/parameters.txt b/docs/multitenant/usecase/parameters.txt index aa823346..710c43fd 100644 --- a/docs/multitenant/usecase/parameters.txt +++ b/docs/multitenant/usecase/parameters.txt @@ -2,7 +2,6 @@ ######################## ## REST SERVER IMAGE ### ######################## - LRESTIMG:container-registry.oracle.com/database/operator:lrest-241210-amd64 ############################## @@ -17,6 +16,22 @@ TNSALIAS:"(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRAN DBUSER:_username_ DBPASS:_password_ +######################### +## SERVICENAME ACCOUNT ## +######################### +SERVICENAMEACCOUNT:_your_service_account_ + + +######################### +## OPENSHIFT ## +######################### +OPENSHIFT:false + +######################### +## PLSQL CONFIGMAP ## +######################### +PLSQLMAP:sql-map-example1 + ####################### ## HTTPS CREDENTIAL ### ####################### @@ -34,19 +49,32 @@ PDBPWD:_password_ ################### ### NAMESPACES #### ################### - PDBNAMESPACE:pdbnamespace LRSNAMESPACE:cdbnamespace - #################### ### COMPANY NAME ### #################### - COMPANY:oracle +########################## +### CODE TREE ### +########################## +CODE_TREE:/path/oracle-database-operator + +########################## +### PDB AUTODISCOVER ### +########################## +AUTODISCOVER:false + #################### ### APIVERSION ### #################### - APIVERSION:v4 + +########################## +### CERT_MANAGER ### +########################## +CERT_MANAGER:https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml + + diff --git a/docs/multitenant/usecase/plug_pdb1_resource.yaml b/docs/multitenant/usecase/plug_pdb1_resource.yaml index c8b8657e..360e17b8 100644 --- a/docs/multitenant/usecase/plug_pdb1_resource.yaml +++ b/docs/multitenant/usecase/plug_pdb1_resource.yaml @@ -11,7 +11,7 @@ spec: cdbName: "DB12" pdbName: "pdbdev" pdbState: "PLUG" - xmlFileName: "/var/tmp/pdb.4165325.xml" + xmlFileName: "/var/tmp/pdb.339548.xml" fileNameConversions: "NONE" sourceFileNameConversions: "NONE" copyAction: "MOVE" diff --git a/docs/multitenant/usecase/unplug_pdb1_resource.yaml b/docs/multitenant/usecase/unplug_pdb1_resource.yaml index 4bc2a96e..375e1fec 100644 --- a/docs/multitenant/usecase/unplug_pdb1_resource.yaml +++ b/docs/multitenant/usecase/unplug_pdb1_resource.yaml @@ -11,7 +11,7 @@ spec: cdbName: "DB12" pdbName: "pdbdev" pdbState: "UNPLUG" - xmlFileName: "/var/tmp/pdb.4165325.xml" + xmlFileName: "/var/tmp/pdb.339548.xml" adminpdbUser: secret: secretName: "pdbusr" From 01af681cf69d3328a6553cb354c87a5c57135f9a Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Thu, 21 Aug 2025 09:42:16 +0000 Subject: [PATCH 277/414] multi tenant controller doc --- docs/multitenant/README.md | 118 ++++++++++++------- docs/multitenant/images/usecaseschema.jpg | Bin 0 -> 124714 bytes docs/multitenant/usecase/README.md | 136 ++++++++++++++++++---- 3 files changed, 188 insertions(+), 66 deletions(-) create mode 100644 docs/multitenant/images/usecaseschema.jpg diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index 0e8c0239..d665e2b8 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -1,47 +1,62 @@ -# LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT + -- [LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT](#lrest-based-multitenant-controllers-for-pdb-life-cycle-management) - - [WHAT'S NEW](#whats-new) - - [Kubectl get lrpdb format](#kubectl-get-lrpdb-format) - - [Pdb status table](#pdb-status-table) - - [STEP BY STEP CONFIGURATION](#step-by-step-configuration) - - [Multiple namespace setup](#multiple-namespace-setup) - - [Create the operator](#create-the-operator) - - [Container database setup](#container-database-setup) - - [Apply rolebinding](#apply-rolebinding) - - [Certificate and credentials](#certificate-and-credentials) - - [Private key 🔑](#private-key-) - - [Public Key 🔑](#public-key-) - - [Certificates](#certificates) - - [Create secrets for certificate and keys](#create-secrets-for-certificate-and-keys) - - [Create secrets with encrypted password](#create-secrets-with-encrypted-password) - - [Create lrest pod](#create-lrest-pod) - - [Openshift configuration](#openshift-configuration) - - [Create PDB](#create-pdb) - - [pdb config map](#pdb-config-map) - - [Open PDB](#open-pdb) - - [Close PDB](#close-pdb) - - [Clone PDB](#clone-pdb) - - [Unplug PDB](#unplug-pdb) - - [Plug PDB](#plug-pdb) - - [Delete PDB](#delete-pdb) - - [SQL/PLSQL SCRIPT EXECUTION](#sqlplsql-script-execution) - - [Apply plsql configmap](#apply-plsql-configmap) - - [Limitation](#limitation) - - [TROUBLESHOOTING](#troubleshooting) - - [Get rid of error status](#get-rid-of-error-status) - - [UPGRADE EXISTING INSTALLATION](#upgrade-existing-installation) - - [KNOWN ISSUE](#known-issue) +- [WHAT'S NEW](#whats-new) + - [Kubectl get lrpdb format](#kubectl-get-lrpdb-format) + - [Pdb status table](#pdb-status-table) +- [STEP BY STEP CONFIGURATION](#step-by-step-configuration) + - [Multiple namespace setup](#multiple-namespace-setup) + - [Apply rolebinding](#apply-rolebinding) + - [Create the operator](#create-the-operator) + - [ClusterRole and ClusterRoleBinding for NodePort services](#clusterrole-and-clusterrolebinding-for-nodeport-services) + - [Container database setup](#container-database-setup) + - [Public Key 🔑](#public-key-) + - [Certificates](#certificates) + - [Create secrets for certificate and keys](#create-secrets-for-certificate-and-keys) + - [Create secrets with encrypted password](#create-secrets-with-encrypted-password) + - [Create lrest pod](#create-lrest-pod) + - [Openshift configuration](#openshift-configuration) + - [Create PDB](#create-pdb) + - [pdb config map](#pdb-config-map) + - [Open PDB](#open-pdb) + - [Close PDB](#close-pdb) + - [Clone PDB](#clone-pdb) + - [Unplug PDB](#unplug-pdb) + - [Plug PDB](#plug-pdb) + - [Delete PDB](#delete-pdb) +- [SQL/PLSQL SCRIPT EXECUTION](#sqlplsql-script-execution) + - [Apply plsql configmap](#apply-plsql-configmap) + - [Limitation](#limitation) +- [TROUBLESHOOTING](#troubleshooting) + - [Get rid of error status](#get-rid-of-error-status) +- [UPGRADE EXISTING INSTALLATION](#upgrade-existing-installation) +- [KNOWN ISSUE](#known-issue) + + + - [Create PDB](#create-pdb) + - [pdb config map](#pdb-config-map) + - [Open PDB](#open-pdb) + - [Close PDB](#close-pdb) + - [Clone PDB](#clone-pdb) + - [Unplug PDB](#unplug-pdb) + - [Plug PDB](#plug-pdb) + - [Delete PDB](#delete-pdb) +- [SQL/PLSQL SCRIPT EXECUTION](#sqlplsql-script-execution) + - [Apply plsql configmap](#apply-plsql-configmap) + - [Limitation](#limitation) +- [TROUBLESHOOTING](#troubleshooting) + - [Get rid of error status](#get-rid-of-error-status) +- [UPGRADE EXISTING INSTALLATION](#upgrade-existing-installation) +- [KNOWN ISSUE](#known-issue) -**Lrpdb** and **lrest** are two controllers for PDB lifecycle management (**PDBLCM**). They rely on a dedicated REST server (Lite Rest Server) Container image to run. The `lrest` controller is available on the Oracle Container Registry (OCR). The container database can be anywhere (on-premises or in the Cloud). +**Lrpdb** and **lrest** are two controllers for PDB lifecycle management (**PDBLCM**). They rely on a dedicated REST server (Lite Rest Server) container image. The `lrest` controller is available on the Oracle Container Registry (OCR). The container database can be anywhere (on-premises or in the Cloud). ![generaleschema](./images/Generalschema2.jpg) @@ -107,6 +122,14 @@ Configure the **WACTH_NAMESPACE** list of the operator `yaml` file ```bash sed -i 's/value: ""/value: "oracle-database-operator-system,pdbnamespace,cdbnamespace"/g' oracle-database-operator.yaml ``` +### Apply rolebinding + + +Apply the following files : [`pdbnamespace_binding.yaml`](./usecase/pdbnamespace_binding.yaml) [`cdbnamespace_binding.yaml`](./usecase/cdbnamespace_binding.yaml) +```bash +kubectl apply -f pdbnamespace_binding.yaml +kubectl apply -f cdbnamespace_binding.yaml +``` ### Create the operator Run the following command: @@ -122,6 +145,16 @@ oracle-database-operator-controller-manager-796c9b87df-6xn7c 1/1 Running oracle-database-operator-controller-manager-796c9b87df-sckf2 1/1 Running 0 22m oracle-database-operator-controller-manager-796c9b87df-t4qns 1/1 Running 0 22m ``` + +### ClusterRole and ClusterRoleBinding for NodePort services + +To expose services on each node's IP and port (the NodePort), apply the node-rbac.yaml. Note that this step is not required for LoadBalancer services. + +```bash + kubectl apply -f rbac/node-rbac.yaml +``` + + ### Container database setup On the container database, use the following commands to configure the account for PDB administration: @@ -133,20 +166,13 @@ grant create session to container=all; grant sysdba to container=all; ``` -### Apply rolebinding - - -Apply the following files : [`pdbnamespace_binding.yaml`](./usecase/pdbnamespace_binding.yaml) [`cdbnamespace_binding.yaml`](./usecase/cdbnamespace_binding.yaml) -```bash -kubectl apply -f pdbnamespace_binding.yaml -kubectl apply -f cdbnamespace_binding.yaml ``` ### Certificate and credentials You must create the public key, private key, certificates and Kubernetes Secrets for the security configuration. #### Private key 🔑 -> Note: Only private key **PCKS8** format is supported by LREST controllers. Before you start configuration, ensure that you can use it. If you are using [`openssl3`](https://docs.openssl.org/master/) then `pcks8` is generated by default. If it is not already generated, then use the following command to create a `pcks8` private key +> Note: Only private key **PCKS8** format is supported by LREST controllers. Before you start configuration, ensure that you can use it. If you are using [`openssl3`](https://docs.openssl.org/master/) then `pcks8` is generated by default. If it is not already generated, then use the following command to create a `pcks8` private key ```bash openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out private.key @@ -350,7 +376,7 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 ### Openshift configuration For the open shift installation you need to do the following -- Before lrest pod creation; create a security context by appling the following yaml file [security_context.yaml](./usecase/security_context.yaml) mind to specify the correnct namespace and the service name account. +- Before lrest pod creation; create a security context by appling the following yaml file [security_context.yaml](./usecase/security_context.yaml) mind to specify the correnct **namespace** and the **service name account**. ```yaml [...] @@ -421,12 +447,12 @@ SQL> 🔥 **assertiveLrpdbDeletion** drops pluggable database using **INCLUDE DATAFILES** option -All of the parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** must be specified in all PDB lifecycle management `yaml` files. To simplify presentation of requirements, we will not include them in the subsequent tables. +The following parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** must be specified in all PDB lifecycle management `yaml` files. For the sake of presentation they will be omitted in the subsequent tables. #### pdb config map -By using **pdbconfigmap** it is possible to specify a kubernetes `configmap` with init PDB parameters. The config map payload has the following format: +**pdbconfigmap** parameters specifies a kubernetes `configmap` with init PDB parameters. The config map payload has the following format: ``` @@ -711,6 +737,8 @@ pdb1 DB12 pdbdev MOUNTED 0.80G close:[ORA-65170] NONE ``` ## UPGRADE EXISTING INSTALLATION +In order to migrante an existing installation to the new version of controller you can laverage on the autodiscover installation. Patch all your lrpdb resources by setting **assertiveLrpdbDeletion** fasle. After that you can delete lrest resource and all lrpdbs. Upgrade the operator and create lrest server paying attenction to set **autodiscover** true and the namespace used by the autodiscovery mechanism (**namespaceAutoDiscover**). + ## KNOWN ISSUE - Error message `ORA-01005` is not reported in the lrest database login phase if password is mistakenly null. Trace log shows the message ORA-1012. diff --git a/docs/multitenant/images/usecaseschema.jpg b/docs/multitenant/images/usecaseschema.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0c9b81102f3b07c5f3b6f6d2ee460b2acb0cf0e7 GIT binary patch literal 124714 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 z%0D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

qrX5I8PDvpztUaQ|W^7vr?7D;tTxF|< z=E=moCKLeDKR%ZHOnvhOUmRoWhn+?Y9A8qLaa4`(+s%0pPHa4(-y?MUnc5&UH?S0Iz2)&54i&f_BWkn(CfxfnGTfz;NG`ZpKS zyFbfoz1MtJd-0V^_@4G&@@(cO97V9(4KH3|Ae6a$s}gJ&a&|?%d}^O<=gMvkZZDCw z@Rs*;tVm$9CPpL4*?!hdw^0>bXH&LH4#rguC=xsKnQElBXbgoSzw<=hn}6(=I>;r# z$?!hftPtd7EpT6AokL^SHSdkt-nK?5BUEinMxpz&UUS>T&rc7U8~{^GJ(sXg2|v(F z;+B;q^-YMi`{dRI><~If{ZZO?2ncRpxDS72%=~S_zWIRlZb^AOI%M}5fMG>$IqJ9M zdz4ztV3@T!wiJUeHlLj!g;dEq`#UJ0Z=vLG`9+k;F&wQ_ac^dA%8J^(NjZd&stoL!`3Nb ze4QsFrI>vD=0O7{H?LjYiQlk9Za6Qc9$eg%|)VSB9e@3~2JW)aw5q^;2QBrf#_1 z>^~Omd+Rts-&^V-{VDkzBAg;~jyJro;R(@W(iG-Aof+w6f5dFxJGa_B%(pO0*`MEz z^fcAZ=<-mC8+F~*fuHr+o4o ztJD5Rh2fb$%cQ~m2Q}+kTcO=&@pd;xwocqPq*`jcnq>Dck*~d48j**V89rsnnf>}9 zQY6AMLJdno?u5#--kIX-T?Zwo{bu>GQrB+S%ga4x(#?j@+-vG=H=M%`$?@$~lI1ax z&a+lo<#o|S+NRqHUFIe06YN>l3!bb%2hOCynWFu`6ztzy2%DN)wzK0i*4+eh-Dbw39n1w+RS+GP3`#gF&wXDViSj!G zY{QxN@ZI zVJw3xdnZ@9%?ujaH4Apq?UO5{5DNv!NI|7_TJg=3S1%Z~s%5%71umNWQbY&b+nb*^ zF78dhs{rh({>3&D*kTr;hHYIv&}%{I&B~i{L0ClfeDy=@!gtvMftHRT4om7eys3=0 z223NLOJt$Z2$*7#U{lCr81)4jI55*zP^^6+Tir!bYNlxVCWc8iHWeG(+t$3eG9|tY z*5wM*J)RZHmx({V0Qc&H1W9OXzG;W>D1d^G-iI@3L_1`@UoUAE4#EnayPFbWlK~7C z_ewiHlFllIaOIf_F#G5znkesvy;A&;v$$V-r21+oXr$7A`2&r}V>f=`%EU+J5{#Q( z(Hqfa42YZ*4L2KCK0a5DKIi`is=1D~86*-J?gq)OJ) zt?jU6aNy_po3;;2X{v32M1qR|d7C`2Wp&e9r^BGxEQXL!8dE(SgDC_HaTQ*vft<|( z&BvbO)EOSQRF$8>ga??XB)O8!4+Nhp_s?wIz1(;5BMNb{7YBS3sPh$VllNrACiU&2 zQ}|$Jsr>z(NbubrW{c8Z%0AD-dDFYj3g!xWkFeE`lYttR-^Jg~-P%$1_}sD%Fl!UE z=lU&XhQSXtk)`$j-tIS57(a&SOa`Z58nHvvjnv4Z zCGNSHiB~EDJ?oy&>D;D*op*BwXv`R@zsINH^45WIY zFi-K=lqaoj)o>>mMW)}!u0+FQ5c)UFrfw@RRphLzgFfIU?S)ll;0}5sc(cz6n~FlC zOs{w0r)NYBD`}jt>fDV99ET|$fBN{YI!q4C3$^OCQ?1;75QNNfG;vI($-rVjxRNz$ z6_rfg%)ooyDp0dzE3GM@?5g}}r+a$r`8BMi9`{vDEv42php7kBaN`a+Lq)L_ABDg^ zB=XvgTnQfY37(al7anV9dR=gdCAnX!d!Nt0l-YFgk(ijqeGc^^XjolO z1{%$V4ZydoTeQz=TcP=$#&hN+6pi7QmEui#>!m$a$R3`CqJV?v@TPkY3MQ1@*&G9l zEkB@EH{5K6Z97{{sa|T3YvL&hOy-R_GPqKJ(%Z06G3`x9z zg8P9C+7bzK$9n-Y$!ilb7GVDv_=9X><48B3{Re!NH6bVE@+Ta`yWL=hEhi9}85e0og8rYbju#p`WVB z)`n^-w!4)d5$P{wQ=&tayqEG5axzZ2V8dB3lnw_c_q5|jBjtfbOgZ5ZAt^&^SkuVi z$)lWKvrh25o;$0MY**HkF8>*huh=Du&DC*9WmcZgYxV#)D&a>}@=oCepKQX$ZN)nd z#M>2kxSe=SAi>%CrRp;3sP3Qq9zDl@0Cpz6lwa){%LN&T?Yy4^kHtiWo%ik=LuPfb zaC3-1z#GB1$nZnw)#Ql>_N=Xq)9Je$#fxZDGli7;d?+c;kpX|K->_EN_i|wO8hzeAl2yxnsPfrJItpH8Jj z;fJIv@SVLeBGWn$BifNe@I-mu|C!Jp{U56Pe@Xxssxtq&wzBZ%N^WF~%OLN(;je4d zZz8D#V_I1IQhq^`_v3i~+t%p+)rM|b`Ooof!{2R>|2h8mAE9T&crl_D5sD_U>Wp9G zq5fm@F(?sUCB43?dXyCPMtJFy8g7Q!3wU0?|ANabIp~n$C+qLz+aL(jI}4X&+@gOY zq`~5=O7;ye{#ZVN6x6OzT?4ujoA(@}4pqO+^+29&#vVka zpNiy!jZ^pPNX5C!$#jO4@=>52lbY0l*Y%8;?1|=ZX2f1!iCM*W1piQ-QjRl8pU@x< zKQ8WqKTz_G%Y7j3sK#~ExNm(c4sk-)n@j8$WZKl43W+<>i5Ja7$7jSYoeBPT%_m#U z)FOy;MDicVJkzO)XcLh`r`3maPuVRcJ^qOKHEK6}w%&|{#@H~pI+zo!ny85Z58ruX z_D=BtmpqcfN+}%un^v8k3L>d`!v_mU|8A}0^o@$L*>TrNPycm*@7rD2x0HpVsh-IV zy_(C=;Xc3ae3PsX041gTn|lKZJLq4`{*=_zt=Z&i9y^q|-ZPS4c}Pww#4n?j2fIyo zYBF7af_0Jv`@xb}f>7x2m3BjJi5Q zMJKpPUw$X>ui`HPIlg1m@&7!aF}%%Fs75N>pd+41ESepFE&)GzJ7NV`UT-GX&Rr&b zeLa8oT?0A+m$2C^4y}yK^_19iT@toG7+ng|KX+inR@NyHS@1;K{0l$l9EBG;!OfL& zPy6^X9J*e0Q%UIUEf;_RLFH>L(NhA`PXE*18~$;rOe^^|OG9mpQ_=i%F?j?bCQ0)j zO>|09J_>jj&#`nllr;3+U)S6yv(RTkh`%G-_WhNq?ZP3C zrC9qUfh#d)eZNd|hJL-=*}6tYBmTFs=C-06BQ+*!-0JiV1j*x%n8eEeeM0Kc70dwY|>z<)5%$nFL~przNY z;Zf9N(=ek_qiU8$tl;EEnL;6ZaY5PvDBUe@var|*=+>MAU7cRzG)@o)nZj& z3@&bwzehqtbby32aR~R~XC^Wshfk?Hkt*ibPN^WF#r36iP1D1KFmx^~$3d8k`2Dx7 z^=f_taYvF#v)w!=p>egLe=6b6H}*P2a1j{xUS%x<&z)`qmwwRV7!$^UAXGxQ%# zLU>a-ks%y9`s&e~IdNtii#O%5F8NWXjNZhsR4pfa%6Ed6qgJ7Rwlvgty z&-MYSn_g#gl;C+mrExPSdB%Fp9lcj%qaAmWU`_#1bi+Wb>_{!A-Ub#sqbe!Gm2ZAk zR#-Q=RU6m-(ON>*XMHf;UBlDu34}wA<{Jg3oe&%9hR_0;CvI1^&vM<+f*8>Wz$%vh zp9F2a|DMFT3V%;`Hs=4kPAqRg7B9luC%D^&_5ax0Tr-`?TzfJws6<(F;feH$EJg=M*y|b*0C|#RI3}jt~fkL5Asp>$juwN zL`!fH!Nx)n1l3#+><@U;_ukFxc-!f;BZ~~e=t_^sITD$b1SA=-hM+5WrP174~rm1aI5 zQ)*~~A@#zZMsmFBcrz`zceHZUqEqVQfgoxXn-;dxDKEp2Tgf{oane4V7Uqbq(?hVT z0ju;nJF5ySh(2Js7v;f|V&f!|ppwqCwH7XLJF&g-t4T&5u522G*KyL(p= zKKFRTW+$tEe8GiuA;K#4+(5jM++RngAl$&)a@Ai_ z4FRg&ZjyGx=K-CrjWHRcQ)P$Z%PdMi!D>ojYijy0@$(kMbO&~&x4CL;H}&}3NX4m> zdebp5KWv8P$|0qDQ*4`nKcKQUI87;;x+aynA&=iXF0Uc^wvZO(WO>O^#gY;2&jJ>v z1$gKx%2NjUhMWSq2|lK}K5Z)>FVL&$HcA{QY7z;%vZ0`YCGCS%(S_tuPYq5a#{P+$ zNmpaEA2aGa5IY#{QE#Ey&>qgmec_sNUzDrg%&smKaMML$GMHL1d-b+{IFr??p-(EI z;;_Fnzr9~LdgE+*0(n6R!L|TiIaWyUo3K3ae@%SJ6^%!eEB=G~3?75AC8@OE&fmxx<$Y(s!` za{BjzhVEg>N(YPSZ-Sx%IU@ODPgj4p4HBl)guoPZDftAu8D?Dt@8^8FYAiHMt8p*T z#%_{v!r`ABLJ@^YcoeK>i(Y^LUnN%hS;uYwz0-B8aT0XTI)N|N1h2 z>W)!*We)UWW99MTo>JGEX?pkf%gF)HbpJWeg;sOhP9Nc%`#_L_|6P5hhPt{M%FK7P zPg#yqqv>N;_mS!EJEJ3T?lFp}w&8H&pj*jxA(iSGYt_leS^VlTUz|*N4C0Pj@_?m` zPMKK#n>JDALq9bUG}O(;8j) zZKlI7sq=u#K8vjnZbFeVVl`Mpe{t_pCZ)&Dd55*Z9b&18=BjorzNs;*aNVO+@PLss z{gukR94Vx|_Njz@V@&MbCgGO`Ri~^VBmp4mn$&kA-j z4(O`Lm!JHuzgA~&@`G?=-B!FrLM4A5e5k7Q)C+`wgWTFy0u^#^1e<-ATfzd0zE<@? zbrjAay+2F4Dhh56ezi*3rkz>eII^yd25n{;?!HY6D+)iWaEIfM=}~~}GTwNe(ouNs z#vkb1Rq*9&>j_uvP=lbLN+$K3`0#MtoRhymbEuP)ySjq;CcE>x3PT|^=y|ZcsGYzV zr4vgA#^3N;7t>$YM81e@ij-&hNyutjRIN}VMfV8CAc>~wExMh9Y z0*i*DX2xjl@AC)0;RpWSYrf*HhE+iGAnwk3T4$Wd4S(X}{IJ30>}W7*y{%z#zMJ!&c8KjWz1ko8f+LmKA&#TrgtP(h&e3>*cB^FtJy z^rj+3T3yIoEG{epB%B27>^3>%Z!NkBk$VLT;28rL{5xJP>eph2SND!I(vtemA}u6`2JTBKdM{+}>5Kj*3dV`8BKws_hnVsQUe{BYCS za3I@>FUNYLjQCZX=T#58+y|`=WM*ENfxX<^q% z$n>S~GIpo(Du6JxeL(ZC2(u#RfAP*+b-zg|{rRG|L-mkzK5DR)judZu^u+0>Pn@>0 zyK6n*;PhkiP~05ZZE&0Z;PWvE!;L$Bzqt!6zW#NMl(k_&;QseUD$f~Y3eN(&VBe!! zK{e^{Y}W#pxSNyU=h?4)Oj{A-V#P5J{fzbyZuR$>J@>cVrUCO?xpcCN1G8!-aZ;D( z0dnHMb{KTfBzoe#Qv(lI_RDNPyu|(9*c6nynaHRJ09=@nBJlAvV5Y=8kecCI)sXas z9a*~BrjL03i|XI3E8Iq8Gw^;tvxp@Q<9S%1d0;3?H0*0bWuBT9zdQxkF=3MDIg;lq z4M$fae>4bJU@4$EKj#6&m()uq=S#{lk^_*z4=0dAV!Msn$%%u06j zg1e>#diP*(?}m+Y0AJd3CroZBDb&K_aGvbBVeYp{TtHyNd}Jw;(IZ|n2PQ09Fu<)$ zJ!~l|Bl$MBI)jfl2*f_3p-cIZkB)QY4yo5|nvGV$PIqS!^E9pQJ_dCWCPhcYav9tC zWSFVCQ6xp~!+e$Wjvzhy*X8&6>jsHguKPDxd)HR?JUtsNBbxY$x#U5x@Rto7-SYle zI&r+(;^HkXzJX^^>CraSJ+8C%&%6eXBa#jrBbFea#Gc&XnAbqvTJMA|-0F`2-11Nl zta=B8&6d(#sn16XD866dqNg@*^=PCz*<L04Vu#V3DbtXuobP4PWxhVcWi zQ*RY-ljY?@+D11z5-Twl#f8v)2+SVjwtsLYMp29`_42WEr@~UhqdIF1OyM{h8}S0y zdch|tvnmf?ML^=9Hl-;)=I0#$j@x#f7yp)9)n9?LTQciSbqXmj7fvN@>I$L18;8ZX z*h&?B4F%oR({2X*oiJS!{RzFPPBwRA&8F+IKsu$d(1qwXam^!Wo*3oix~OCr*WK$v zPpb&qyrSroAN;!|OO^GRO@#b~47<%lF#2iAl1k45>V)Ue*ARwNmI4Db&uxD|VNUq1 zfAhjs0sqa&Y*-~@&p+m`+fNz1S`vSLRrD}b%~gh0bfe}y zdx`~=de_k-wohSlLRtNeGVQ2#i%NDsbdUKFQU=(p+-=b|Lh|I?w=?s7OpgMc@Yi_; zO4;XAU7iFuV{++yNb%?E7J6Z;q&4zg`cZRTsA-!=;$yd~2B^DDjhGsy7b7;n4Iswn zv%0A4yEMXj{vq9FiaG{e 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 literal 0 HcmV?d00001 diff --git a/docs/multitenant/usecase/README.md b/docs/multitenant/usecase/README.md index 98897d7b..2baa65b0 100644 --- a/docs/multitenant/usecase/README.md +++ b/docs/multitenant/usecase/README.md @@ -2,14 +2,36 @@ # Use case directory + -The use case directory contains the `yaml` files to test the multitenant controller functionalities: create `lrest` pod, and create PDB operations *create / open / close / unplug / plug / delete / clone /map / parameter session* +- [Use case directory](#use-case-directory) + - [Prerequisites](#prerequisites) + - [Operator setup](#operator-setup) + - [Secrets creation](#secrets-creation) + - [Yaml file creation](#yaml-file-creation) + - [Run testcase](#run-testcase) + - [Makefile targets table](#makefile-targets-table) + - [Diag commands and troubleshooting](#diag-commands-and-troubleshooting) + - [Connect to rest server pod](#connect-to-rest-server-pod) + - [Lrest pod log](#lrest-pod-log) + - [Monitor control plane](#monitor-control-plane) + - [Error decrypting credential](#error-decrypting-credential) + - [Crd details](#crd-details) -## Makefile helper + -Customizing `yaml` files (tns alias / credential / namespaces name, and so on) is a long procedure that is prone to human error. A simple [`makefile`](../usecase/makefile) is available to quickly and safely configure `yaml` files with your system environment information. Just edit the [parameter file](../usecase/parameters.txt) before proceding. +The use case directory contains a makefile to automatically install the Oracle Database Operator (namespace scope configuration) and generate the yaml files to test pdb life cycle management in two different namespaces (one for lrest pod the other one for pdb crd). To simplify and speed up the execution you just need to edit a [parameter file](../usecase/parameters.txt) with all the information about your environment. +Subsequent steps are the operator installation, the secret installation, the yaml files generation and finally the tests execution. + +![generalschema](../images/usecaseschema.jpg) + +**parameter file table of contents** ```text + Check the latest version available<--------------+ + | + +-----+ +LRESTIMG...............:container-registry.oracle.com/database/operator:lrest-241210-amd64 TNSALIAS...............:[Tnsalias do not use quotes and avoid space in the string --> (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELA....] DBUSER.................:[CDB admin user] DBPASS.................:[CDB admin user password] @@ -20,43 +42,116 @@ PDBPWD.................:[PDB admin user password] PDBNAMESPACE...........:[pdb namespace] LRSNAMESPACE...........:[cdb namespace] COMPANY................:[your company name] -APIVERSION.............:v4 --> do not edit +APIVERSION.............:[v4 --> do not edit] +SERVICENAMEACCOUNT.....:[service account - for openshift ] +AUTODISCOVER...........:[boolean: check for pdb with no crd ] +CODE_TREE..............:[Is the path of the directory with the original operator yaml file] +CERT_MANAGER...........:https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml +OPENSHIFT..............:[boolean] ``` -⚠ **WARNING: The makefile is only intended to speed up the usecase directory configuration. Use of this file for production purposes is not supported. The editing and configuration of yaml files for production system is left up to the end user** +Verify parameters using ``make check`` command. -### Prerequisistes: +### Prerequisites -- Ensure that **kubectl** is properly configured. -- Ensure that all requirements listed in the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) +- Ensure that **kubectl** is properly installed on your client. +- Even if the takes care of the requirements setup read carefully the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) - Ensure that the administrative user (admin) on the container database is configured as documented. +eg + +```sql +-- Connect to the container and creates the administrative user +alter session set "_oracle_script"=true; +create user [DBUSER] identified by [DBPASS]; +grant create session to restdba container=all; +grant sysdba to restdba container=all; +``` + +### Operator setup + ```bash -make operator +make opsetup ``` -This command creates the `operator-database-operator.yaml` file in the local directory, and set up the `watchnamespace` list. Note that the `yaml` file is not applied. +The make target **make opsetup** does the following actions: +- Creates a copy the original **oracle-database-operator.yaml** and updates the WATCH_NAMESPACE list with the pdbnamespace and cdbnamespace values. +- [Applies the certmaneger](../../../README.md#install-cert-manager) +- Creates two namespaces: one for the lrest pod and the other one for the pdb controller. +- [Namespace Scoped Deployment](../../../README.md#2-namespace-scoped-deployment) +- Applies the oracle-database-operator.yaml +- [ClusterRole and ClusterRoleBinding for NodePort services](../../../README.md#clusterrole-and-clusterrolebinding-for-nodeport-services) + +👉 **If your are running on Openshift you need to manually apply the [service context file](./security_context.yaml)** + +### Secrets creation ```bash make secrets ``` -This command creates all of the Secrets with the encrypted credentials. +**make secrets** creates secrets encrypting the credential specified in the parameters + +### Yaml file creation ```bash make genyaml ``` -*make genyaml* generates the required `yaml` files to work with multitenant controllers. +**make genyaml** generates the required `yaml` files to work with multitenant controllers. +### Run testcase -![image](../images/UsecaseSchema.jpg) +```bash +make runall00 +``` +You can run **make runall00** to test all the functionality the multitenant controller + +### Makefile targets table + + | target | action | additional info | + |-----------------|---------------------|-----------------| + |tkapplyinit | config map creation | | + |run00 | lrest pod creation | | + |run01.1 | pdb1 creation | | + |run01.2 | pdb2 creation | | + |run02.1 | pdb1 open | declarative | + |run02.2 | pdb2 open | declarative | + |run03.1 | pdb1 clone | declarative | + |run04.1 | pdb1 close | declarative | + |run04.2 | pdb2 close | declatative | + |run05.1 | pdb1 unplug | declarative | + |run06.1 | pdb1 plug | declarative | + |openpdb1 | pdb1 open | imperative | + |openpdb2 | pdb2 open | imperative | + |closepdb1 | pdb1 close | imperative | + |closepdb2 | pdb2 close | imperative | + |openpdb1rs | pdb1 open restrict | imperative | + |openpdb2rs | pdb2 open restrict | imperative | + |tkaudosicov | test autodiscovery | | + |tkplsqlexec | test sql/plsql | | + |tkapplyinit | apply init map | | + |listimage | images available on the cluster| | + |dumpoperator | dump operator log | | + |dumplrest | dump lrest log | | + |login | connect to lrest pod| | + |reloadod | reload operator img | | + |mgrrestart | manager restart | | + |**opsetup** | install the operator| | + |**secrets** | create secrets | | + |**genyaml** | generate the yaml files| | + |opclean | deintall the operator| | ## Diag commands and troubleshooting -### Connect to rest server pod +#### Connect to rest server pod ```bash /usr/bin/kubectl exec -n -it -- /bin/bash ``` +#### Lrest pod log + +```bash +kubectl logs `kubectl get pods -o custom-columns=:metadata.name -n cdbnamespace --no-headers ` -n cdbnamespace +``` ```bash ## example ## @@ -69,7 +164,7 @@ kubectl exec cdb-dev-lrest-rs-fnw99 -n cdbnamespace -it -- /bin/bash [oracle@cdb-dev-lrest-rs-fnw99 ~]$ ``` -### Monitor control plane +#### Monitor control plane ```bash kubectl logs -f -l control-plane=controller-manager -n oracle-database-operator-system @@ -92,24 +187,21 @@ I1029 10:07:20.189724 1 leaderelection.go:250] attempting to acquire leade ``` -### Error decrypting credential +#### Error decrypting credential -The following is an example of a resource creation failure due to decription error: +In the following example you can see a resource creation failure due to a decryption issue ```text 2024-10-30T10:09:08Z INFO controllers.LRPDB getEncriptedSecret :pdbusr {"getEncriptedSecret": {"name":"lrpdb1","namespace":"pdbnamespace"}} 2024-10-30T10:09:08Z ERROR controllers.LRPDB Failed to parse private key - x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format) {"DecryptWithPrivKey": {"name":"lrpdb1","namespace":"pdbnamespace"}, "error": "x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format)"} ``` - **Solution**: Ensure you use **PCKS8** format during private key generation. If you are not using `openssl3`, then run this command: ```bash openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > mykey ``` - -### Crd details - +#### Crd details Use the **describe** option to obtain `crd` information ```bash @@ -137,3 +229,5 @@ Events: Warning Done 15s (x12 over 2m25s) LRPDB cdb-dev ``` + + \ No newline at end of file From 2a40e7e3026ab1b36682a716b47a3c8066a5498b Mon Sep 17 00:00:00 2001 From: marcstef Date: Thu, 21 Aug 2025 12:33:56 +0000 Subject: [PATCH 278/414] OrdsSrvs 2.0 documentation --- docs/ordsservices/README.md | 103 +++++++++++----- docs/ordsservices/autoupgrade.md | 116 ++++++++++++++++-- .../examples/ordssrvs-sa-scc.yaml | 50 ++++++++ docs/ordsservices/examples/ordssrvs-sa.yaml | 5 + docs/ordsservices/examples/ordssrvs.yaml | 32 +++++ 5 files changed, 266 insertions(+), 40 deletions(-) create mode 100644 docs/ordsservices/examples/ordssrvs-sa-scc.yaml create mode 100644 docs/ordsservices/examples/ordssrvs-sa.yaml create mode 100644 docs/ordsservices/examples/ordssrvs.yaml diff --git a/docs/ordsservices/README.md b/docs/ordsservices/README.md index 3117eb00..9fcac846 100644 --- a/docs/ordsservices/README.md +++ b/docs/ordsservices/README.md @@ -1,10 +1,10 @@ -# Oracle Rest Data Services (ORDSSRVS) Controller for Kubernetes - ORDS Life cycle management +# Oracle Rest Data Services (OrdsSrvs) Controller for Kubernetes - ORDS Life cycle management ## Description -The ORDSRVS controller extends the Kubernetes API with a Custom Resource (CR) and Controller for automating Oracle Rest Data -Services (ORDS) lifecycle management. Using the ORDS controller, you can easily migrate existing, or create new, ORDS implementations +The OrdsSrvs controller extends the Kubernetes API with a Custom Resource (CR) and Controller for automating Oracle Rest Data +Services (ORDS) lifecycle management. Using the OrdsSrvs controller, you can easily migrate existing, or create new, ORDS implementations into an existing Kubernetes cluster. This controller allows you to run what would otherwise be an On-Premises ORDS middle-tier, configured as you require, inside Kubernetes with the additional ability of the controller to perform automatic ORDS/APEX install/upgrades inside the database. @@ -17,66 +17,103 @@ The custom RestDataServices resource supports the following configurations as a * Single OrdsSrvs resource with multiple database pools* * Multiple OrdsSrvs resources, each with one database pool * Multiple OrdsSrvs resources, each with multiple database pools* +* ORDS and APEX database schemas [automatic installation/upgrade](./autoupgrade.md) *See [Limitations](#limitations) -It supports the majority of ORDS configuration settings as per the [API Documentation](./api.md). +ORDS Version supported : 25.1.0+ +OrdsSrvs controller supports the majority of ORDS configuration settings as per the [API Documentation](./api.md). -The ORDS and APEX schemas can be [automatically installed/upgraded](./autoupgrade.md) into the Oracle Database by the ORDS controller. -ORDS Version support: -* 25.1.0+ +### Prerequisites -Oracle Database Version: -* 19c -* 23ai (incl. 23ai Free) + This chapter outlines the necessary requirements that must be satisfied to successfully deploy and operate the OrdsSrvs controller within your Kubernetes cluster. -### Prerequisites +#### Oracle Database Operator -1. Oracle Database Operator +Before installing the OrdsSrvs controller, ensure that the Oracle Database Operator (OraOperator) is installed in your Kubernetes environment. Please follow the detailed installation steps provided in the [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) to complete this process. The OraOperator must be properly configured and running, as OrdsSrvs depends on its services for functionality. - Install the Oracle Database Operator (OraOperator) using the instructions in the [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) file. -1. Namespace +#### Namespace Namespace Scoped Deployment - For a dedicated namespace deployment of the ORDSSRVS controller, refer to the "Namespace Scoped Deployment" section in the OraOperator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md#2-namespace-scoped-deployment). +For a dedicated namespace deployment of the OrdsSrvs controller, refer to the "Namespace Scoped Deployment" section in the OraOperator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md#2-namespace-scoped-deployment). +The following examples demonstrate deploying the controller to the ordsnamespace namespace. - The following examples deploy the controller to the 'ordsnamespace' namespace. +Create the namespace: - Create the namespace: - ```bash - kubectl create namespace ordsnamespace - ``` +```bash +kubectl create namespace ordsnamespace +``` - Apply namespace role binding [ordsnamespace-role-binding.yaml](./ordsnamespace-role-binding.yaml): - ```bash - kubectl apply -f ordsnamespace-role-binding.yaml - ``` +Apply namespace role binding [ordsnamespace-role-binding.yaml](./ordsnamespace-role-binding.yaml): - Edit OraOperator to add the namespace under WATCH_NAMESPACE: - ```yaml - - name: WATCH_NAMESPACE +```bash +kubectl apply -f ordsnamespace-role-binding.yaml +``` + +Edit OraOperator to add the namespace under WATCH_NAMESPACE: +```yaml + - name: WATCH_NAMESPACE value: "default,,ordsnamespace" - ``` +``` + +### OpenShift Security Context Constraints + +If you are deploying the OrdsSrvs controller on OpenShift, ensure that the appropriate Security Context Constraints (SCCs) are configured. This involves assigning privileged SCCs to the service accounts used by OrdsSrvs to permit required operations. + +#### Create a Service Account + +This account will be used to assign the necessary Security Context Constraints (SCCs) for the controller’s operation. +Below is an example [YAML](./examples/ordssrvs-sa.yaml) manifest to create a service account named "ordssrvs-sa" in the "ordsnamespace" namespace: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ordssrvs-sa + namespace: ordsnamespace +``` + +#### Create a Custom Security Context Constraint (SCC) + +To configure the required security permissions, use the attached [YAML](./examples/ordssrvs-sa-scc.yaml) file to create a custom Security Context Constraint (SCC) and bind it to the "ordssrvs-sa" service account. +This will ensure the service account has the necessary permissions for the OrdsSrvs controller to operate on OpenShift. + +#### Set serviceAccountName in OrdsSrvs + +Ensure that the OrdsSrvs controller uses the dedicated service account you created. In the deployment manifest for OrdsSrvs, specify the serviceAccountName field with the name of your service account (e.g., ordssrvs-sa) as in this [example](./examples/ordssrvs.yaml). + +```yaml +apiVersion: database.oracle.com/v4 +kind: OrdsSrvs +metadata: + name: ordssrvs + namespace: ordsnamespace +spec: + ... + globalSettings: + ... + poolSettings: + ... + serviceAccountName: ordssrvs-sa +``` + ### Common configuration examples -A few common configuration examples can be used to quickly familiarise yourself with the ORDS Custom Resource Definition. +A few common configuration examples can be used to quickly familiarise yourself with the OrdsSrvs Custom Resource Definition. The "Conclusion" section of each example highlights specific settings to enable functionality that maybe of interest. -Before - * [Pre-existing Database](./examples/existing_db.md) * [Containerised Single Instance Database (SIDB)](./examples/sidb_container.md) * [Multidatabase using a TNS Names file](./examples/multi_pool.md) * [Autonomous Database using the OraOperator](./examples/adb_oraoper.md) *See [Limitations](#limitations) * [Autonomous Database without the OraOperator](./examples/adb.md) * [Oracle API for MongoDB Support](./examples/mongo_api.md) +* [ORDS and APEX database schemas automatic installation/upgrade](./autoupgrade.md) Running through all examples in the same Kubernetes cluster illustrates the ability to run multiple ORDS instances with a variety of different configurations. -If you have a specific use-case that is not covered and would like it to be feel free to contribute it via a Pull Request. - ### Limitations When connecting to a mTLS enabled ADB and using the Oracontroller to retreive the Wallet, it is currently not supported to have multiple, different databases supported by the single RestDataServices resource. This is due to a requirement to set the `TNS_ADMIN` parameter at the Pod level ([#97](https://github.com/oracle/oracle-database-controller/issues/97)). diff --git a/docs/ordsservices/autoupgrade.md b/docs/ordsservices/autoupgrade.md index 7f99f349..3defdc42 100644 --- a/docs/ordsservices/autoupgrade.md +++ b/docs/ordsservices/autoupgrade.md @@ -1,10 +1,114 @@ -# AutoUpgrade +# ORDS and APEX AutoUpgrade -Each pool can be configured to automatically install and upgrade the ORDS and/or APEX schemas in the database. -The ORDS version is based on the ORDS image used for the RestDataServices resource. -To get the APEX installation files, you can choose to download them from the latest version available or use the provided URL. +Each pool can be configured to automatically install and upgrade the ORDS and/or APEX schemas in the database. + +## ORDS autoUpgrade + +The ORDS version is determined by the ORDS image used for the RestDataServices resource. +ORDS schema installation and upgrade can be activated at the pool level: + +```yaml +apiVersion: database.oracle.com/v1 +kind: OrdsSrvs +metadata: + name: ordspoc-server +spec: + ... + poolSettings: + - poolName: pdb1 + autoUpgradeORDS: true +``` + +## APEX autoUpgrade + +ORDS image does **not** contain APEX installation files. +APEX installation files can be provided to the pod in two ways: + + - automatic download + - external storage (PersistenceVolume) + + +### APEX installation automatic download + +The ORDS container can download the latest APEX version from the Oracle Container Registry (OCR) or a custom URL. +Downloading APEX installation files requires the Kubernetes worker node to have internet access. +APEX download is performed at the container level and may be enabled or disabled for each pool. + +```yaml +apiVersion: database.oracle.com/v1 +kind: OrdsSrvs +metadata: + name: ordspoc-server +spec: + ... + globalSettings: + downloadAPEX : true + downloadUrlAPEX : https://download.oracle.com/otn_software/apex/apex_24.2.zip + encPrivKey: + ... + poolSettings: + - poolName: pdb1 + autoUpgradeAPEX: true + ... + - poolName: pdb2 + autoUpgradeAPEX: false + ... +``` + +If you do not specify a download URL (downloadUrlAPEX), the default value is used: +https://download.oracle.com/otn_software/apex/apex-latest.zip + + +### APEX installation files on external storage + +Alternatively, you can provide APEX installation files in a dedicated PersistentVolume containing a single apex.zip file. + +You can download apex.zip from: +https://www.oracle.com/tools/downloads/apex-downloads/ + +```yaml +apiVersion: database.oracle.com/v4 +kind: OrdsSrvs +metadata: + name: ordssrvs + namespace: testcase +spec: + ... + globalSettings: + apex.download : false + apex.installation.persistence: + volumeName : apexpv + storageClass : + size : 20Gi + accessMode : ReadWriteMany + ... + poolSettings: + - poolName: default + autoUpgradeAPEX: true +``` + +The OrdsSrvs controller will create a PersistentVolumeClaim (PVC) for the PV and mount it in the pod’s container at /opt/oracle/apex. + +The volume can be static or dynamic. If the volume is empty, the init container will wait until it finds apex.zip at the mount point. +The init container logs the following message: + +``` bash +Missing /opt/oracle/apex/apex.zip, manually copy apex.zip in /opt/oracle/apex on the init container of the pod +``` + +You can copy the apex.zip file into the container while the init script is waiting: + +``` bash +kubectl cp /tmp/apex.zip :/tmp -c ordssrvs-init -n ordsnamespace +kubectl exec -c ordssrvs-init -n ordsnamespace -- mv /tmp/apex.zip /opt/oracle/apex +``` + + + +## Example: ORDS autoUpgrade and APEX download/autoUpgrade + +In the following manifest example: -For example, in the below manifest: * APEX installation files will be downloaded from latest version. * `Pool: pdb1` is configured to automatically install/ugrade both ORDS and APEX to version 25.1.0 * `Pool: pdb2` will install or upgrade ORDS @@ -52,8 +156,6 @@ spec: secretName: pdb3-ords-auth-enc ``` -If you don't specify a download URL (downloadUrlAPEX), the default value will be used: -https://download.oracle.com/otn_software/apex/apex-latest.zip ## Minimum Privileges for Admin User diff --git a/docs/ordsservices/examples/ordssrvs-sa-scc.yaml b/docs/ordsservices/examples/ordssrvs-sa-scc.yaml new file mode 100644 index 00000000..0f854f59 --- /dev/null +++ b/docs/ordsservices/examples/ordssrvs-sa-scc.yaml @@ -0,0 +1,50 @@ +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + name: ordssrvs-scc + namespace: ordsnamespace +allowPrivilegedContainer: false +runAsUser: + type: MustRunAs + uid: 54321 +seLinuxContext: + type: RunAsAny +fsGroup: + type: MustRunAs + ranges: + - min: 54321 + max: 54321 +supplementalGroups: + type: MustRunAs + ranges: + - min: 54321 + max: 54321 +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ordssrvs-role + namespace: ordsnamespace +rules: + - apiGroups: + - security.openshift.io + verbs: + - use + resources: + - securitycontextconstraints + resourceNames: + - ordssrvs-scc +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ordssrvs-rb + namespace: ordsnamespace +subjects: + - kind: ServiceAccount + name: ordssrvs-sa + namespace: ordsnamespace +roleRef: + kind: Role + name: ordssrvs-role + apiGroup: rbac.authorization.k8s.io diff --git a/docs/ordsservices/examples/ordssrvs-sa.yaml b/docs/ordsservices/examples/ordssrvs-sa.yaml new file mode 100644 index 00000000..2eb59a18 --- /dev/null +++ b/docs/ordsservices/examples/ordssrvs-sa.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ordssrvs-sa + namespace: ordsnamespace diff --git a/docs/ordsservices/examples/ordssrvs.yaml b/docs/ordsservices/examples/ordssrvs.yaml new file mode 100644 index 00000000..2f40d945 --- /dev/null +++ b/docs/ordsservices/examples/ordssrvs.yaml @@ -0,0 +1,32 @@ +apiVersion: database.oracle.com/v4 +kind: OrdsSrvs +metadata: + name: ordssrvs + namespace: ordsnamespace +spec: + image: container-registry.oracle.com/database/ords:latest + forceRestart: true + encPrivKey: + secretName: prvkey + passwordKey: privateKey + globalSettings: + database.api.enabled: true + mongo.enabled: true + apex.download : false + db.invalidPoolTimeout: 1m + poolSettings: + - poolName: default + autoUpgradeORDS: true + autoUpgradeAPEX: false + feature.sdw: true + restEnabledSql.active: true + plsql.gateway.mode: direct + db.connectionType: customurl + db.customURL: jdbc:oracle:thin:@//:1521/FREEPDB1 + db.username: ORDS_PUBLIC_USER + db.secret: + secretName: db-auth-enc + db.adminUser: SYS + db.adminUser.secret: + secretName: db-auth-enc + serviceAccountName: ordssrvs-sa From 37815b4c467af6a17d96352344c0e83d10aa0fdd Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Thu, 21 Aug 2025 17:03:11 +0000 Subject: [PATCH 279/414] Orestartfix --- apis/database/v4/oraclerestart_types.go | 4 +-- apis/database/v4/zz_generated.deepcopy.go | 6 +++- commons/oraclerestart/oraclerestartcommon.go | 5 +++- .../database.oracle.com_oraclerestarts.yaml | 4 --- .../database/oraclerestart_controller.go | 28 ++++++++++--------- .../provisioning/oraclerestart_prov.yaml | 5 ++++ .../oraclerestart_prov_nodeports.yaml | 6 ++++ .../oraclerestart_prov_onsport.yaml | 6 ++++ .../oraclerestart_prov_rupatch.yaml | 6 ++++ .../oraclerestart_prov_rupatch_pvc.yaml | 5 ++++ .../oraclerestart_prov_storage.yaml | 6 ++++ .../orestart_prov_asm_disk_addition.yaml | 5 ++++ .../orestart_prov_asm_disk_deletion.yaml | 5 ++++ oracle-database-operator.yaml | 4 --- 14 files changed, 70 insertions(+), 25 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 6915b307..0810d20e 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -142,7 +142,7 @@ type InitParams struct { } type OracleRestartInstDetailSpec struct { - Name string `json:"name"` + Name string `json:"name,omitempty"` // Name of the Oracle Restart Instance HostSwLocation string `json:"hostSwLocation,omitempty"` SwLocStorageSizeInGb int `json:"swLocStorageSizeInGb,omitempty"` WorkerNode []string `json:"workerNode,omitempty"` @@ -243,7 +243,7 @@ type OracleRestartStatus struct { // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` - InstDetails OracleRestartInstDetailSpec `json:"instDetails,omitempty"` + InstDetails *OracleRestartInstDetailSpec `json:"instDetails,omitempty"` ConfigParams *InitParams `json:"configParams,omitempty"` AsmDetails *AsmInstanceStatus `json:"asmDetails,omitempty"` NfsStorageDetails *corev1.NFSVolumeSource `json:"nfsStorageDetails,omitempty"` diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 7d186ffd..b3e1a141 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -3387,7 +3387,11 @@ func (in *OracleRestartStatus) DeepCopyInto(out *OracleRestartStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.InstDetails.DeepCopyInto(&out.InstDetails) + if in.InstDetails != nil { + in, out := &in.InstDetails, &out.InstDetails + *out = new(OracleRestartInstDetailSpec) + (*in).DeepCopyInto(*out) + } if in.ConfigParams != nil { in, out := &in.ConfigParams, &out.ConfigParams *out = new(InitParams) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index a33b4786..57961599 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -824,7 +824,10 @@ func getExternalConnStr( kubeConfig clientcmd.ClientConfig, logger logr.Logger, ) string { - + // If NodePort service not defined in spec, don’t expose external conn + if len(instance.Spec.InstDetails.NodePortSvc) == 0 { + return "" + } // Get the dbmc1 NodePort service svc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), instance.Spec.InstDetails.Name, metav1.GetOptions{}) if err != nil { diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index 2702a8c6..a284469a 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -363,8 +363,6 @@ spec: items: type: string type: array - required: - - name type: object isDebug: type: string @@ -1122,8 +1120,6 @@ spec: items: type: string type: array - required: - - name type: object installNode: type: string diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index ce2b22e6..eda17d5c 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -158,6 +158,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques oracleRestart.Status.Role = string(oraclerestartdb.OracleRestartFieldNotDefined) oracleRestart.Status.ConnectString = string(oraclerestartdb.OracleRestartFieldNotDefined) oracleRestart.Status.PdbConnectString = string(oraclerestartdb.OracleRestartFieldNotDefined) + oracleRestart.Status.ExternalConnectString = string(oraclerestartdb.OracleRestartFieldNotDefined) oracleRestart.Status.ReleaseUpdate = string(oraclerestartdb.OracleRestartFieldNotDefined) oracleRestart.Status.ConfigParams.DbHome = string(oraclerestartdb.OracleRestartFieldNotDefined) oracleRestart.Status.ConfigParams.GridHome = string(oraclerestartdb.OracleRestartFieldNotDefined) @@ -625,20 +626,15 @@ func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oracleres r.Log.Info("Error during Oracle Restart update", "err1", err1) } - errMsg := func() string { - if *err != nil { - return (*err).Error() - } - return "no reconcile errors" - }() var condition metav1.Condition + if *completed { condition = metav1.Condition{ Type: string(oraclerestartdb.CrdReconcileCompeleteState), LastTransitionTime: metav1.Now(), ObservedGeneration: oracleRestart.GetGeneration(), Reason: string(oraclerestartdb.OracleRestartCrdReconcileCompleteReason), - Message: errMsg, + Message: "reconcile completed successfully", // no error text Status: metav1.ConditionTrue, } } else if *blocked { @@ -647,7 +643,7 @@ func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oracleres LastTransitionTime: metav1.Now(), ObservedGeneration: oracleRestart.GetGeneration(), Reason: string(oraclerestartdb.OracleRestartCrdReconcileWaitingReason), - Message: errMsg, + Message: "reconcile is waiting on dependencies", // neutral message Status: metav1.ConditionTrue, } } else if result.Requeue { @@ -656,7 +652,7 @@ func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oracleres LastTransitionTime: metav1.Now(), ObservedGeneration: oracleRestart.GetGeneration(), Reason: string(oraclerestartdb.CrdReconcileQueuedReason), - Message: errMsg, + Message: "reconcile has been queued", // neutral message Status: metav1.ConditionTrue, } } else if err != nil && *err != nil { @@ -665,7 +661,7 @@ func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oracleres LastTransitionTime: metav1.Now(), ObservedGeneration: oracleRestart.GetGeneration(), Reason: string(oraclerestartdb.CrdReconcileErrorReason), - Message: errMsg, + Message: (*err).Error(), // show actual error only here Status: metav1.ConditionTrue, } } else { @@ -677,10 +673,12 @@ func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oracleres } meta.SetStatusCondition(&oracleRestart.Status.Conditions, condition) - if oracleRestart.Status.State == string(oraclerestartdb.OracleRestartPodAvailableState) && condition.Type == string(oraclerestartdb.CrdReconcileCompeleteState) { + if oracleRestart.Status.State == string(oraclerestartdb.OracleRestartPodAvailableState) && + condition.Type == string(oraclerestartdb.CrdReconcileCompeleteState) { r.Log.Info("All validations and updation are completed. Changing State to AVAILABLE") oracleRestart.Status.State = string(oraclerestartdb.OracleRestartAvailableState) } + for attempt := 0; attempt < maxRetries; attempt++ { // Fetch the latest version of the object latestInstance := &oraclerestartdb.OracleRestart{} @@ -1833,8 +1831,12 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or data = append(data, "OP_TYPE=setuprac") // removing this breaks HAS label build setup - data = append(data, "IGNORE_CRS_PREREQS=TRUE") - data = append(data, "IGNORE_DB_PREREQS=TRUE") + // data = append(data, "IGNORE_CRS_PREREQS=TRUE") + // data = append(data, "IGNORE_DB_PREREQS=TRUE") + // --- Pick ALL envVars directly from CR spec --- + for _, e := range instance.Spec.InstDetails.EnvVars { + data = append(data, fmt.Sprintf("%s=%s", e.Name, e.Value)) + } // Service Parameters if instance.Spec.ServiceDetails.Name != "" { diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov.yaml index 074ba0d5..f80b3e58 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov.yaml @@ -18,6 +18,11 @@ spec: # Worker nodes where the instance should be scheduled workerNode: - 10.0.10.108 # IP address of the target node for deployment + envVars: + - name: IGNORE_CRS_PREREQS + value: "true" + - name: IGNORE_DB_PREREQS + value: "true" # ASM (Automatic Storage Management) storage configuration asmStorageDetails: diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml index 9101cae6..0b1de6d2 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml @@ -18,6 +18,12 @@ spec: # Target worker node(s) for this instance workerNode: - 10.0.10.108 # IP address of the node where DB will run + + envVars: + - name: IGNORE_CRS_PREREQS + value: "true" + - name: IGNORE_DB_PREREQS + value: "true" # NodePort service configuration for exposing DB port externally nodePortSvc: diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml index 72235212..c2822c01 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml @@ -24,6 +24,12 @@ spec: # List of worker node IPs where instance will be deployed workerNode: - 10.0.10.108 + + envVars: + - name: IGNORE_CRS_PREREQS + value: "true" + - name: IGNORE_DB_PREREQS + value: "true" # NodePort service configuration nodePortSvc: - svcType: nodeport # Type of service (NodePort) diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml index c107bcbb..9cf349e2 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml @@ -19,6 +19,12 @@ spec: workerNode: - 10.0.10.108 # IP address of the node where DB will run + envVars: + - name: IGNORE_CRS_PREREQS + value: "true" + - name: IGNORE_DB_PREREQS + value: "true" + # NodePort service configuration for exposing DB port externally nodePortSvc: - svcType: nodeport # Service type (NodePort to expose externally) diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml index a08fc19c..fa69203d 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml @@ -22,6 +22,11 @@ spec: # IP address of the worker node where the instance should run workerNode: - 10.0.10.108 + envVars: + - name: IGNORE_CRS_PREREQS + value: "true" + - name: IGNORE_DB_PREREQS + value: "true" # Service definition to expose the DB nodePortSvc: - # Type of Kubernetes service diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml index d0d3abdb..5289112c 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml @@ -18,6 +18,12 @@ spec: workerNode: - 02-00-17-01-73-a0 # Node hostname where the pod should be scheduled + envVars: + - name: IGNORE_CRS_PREREQS + value: "true" + - name: IGNORE_DB_PREREQS + value: "true" + # NodePort service configuration nodePortSvc: - svcType: nodeport # Type of Kubernetes service to expose the Oracle DB diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml index ffe264e6..04a0d7c5 100644 --- a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml @@ -21,6 +21,11 @@ spec: # List of worker node IPs where Oracle will run workerNode: - 10.0.10.108 + envVars: + - name: IGNORE_CRS_PREREQS + value: "true" + - name: IGNORE_DB_PREREQS + value: "true" # Port used for ONS (Oracle Notification Service) onsTargetPort: 30200 # Kubernetes NodePort service details for listener access diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml index 0fb8087d..5bb9b617 100644 --- a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml @@ -21,6 +21,11 @@ spec: # List of worker node IPs where Oracle will run workerNode: - 10.0.10.108 + envVars: + - name: IGNORE_CRS_PREREQS + value: "true" + - name: IGNORE_DB_PREREQS + value: "true" # Port used for ONS (Oracle Notification Service) onsTargetPort: 30200 # Kubernetes NodePort service details for listener access diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index de951ce3..377bcc3c 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -11219,8 +11219,6 @@ spec: items: type: string type: array - required: - - name type: object isDebug: type: string @@ -11978,8 +11976,6 @@ spec: items: type: string type: array - required: - - name type: object installNode: type: string From 404fe691de273f07a426c33c4ca4f6d458d0db6f Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Thu, 21 Aug 2025 17:03:45 +0000 Subject: [PATCH 280/414] fix for PROVISIONING state to AVAILABLE --- apis/database/v4/dbcssystem_types.go | 16 +- apis/database/v4/zz_generated.deepcopy.go | 6 +- commons/dbcssystem/dbcs_reconciler.go | 227 ++++++++--- .../database.oracle.com_dbcssystems.yaml | 2 + config/manager/kustomization.yaml | 2 +- controllers/database/dbcssystem_controller.go | 384 ++++++++++++++---- .../bind_to_existing_dbcs_system.yaml | 19 +- .../disable_dataguard_to_database.md | 11 +- docs/dbcs/provisioning/patch_dbcs_system.yaml | 20 +- docs/dbcs/provisioning/patching_database.md | 11 +- .../provisioning/upgrade_dbcs_system.yaml | 44 +- docs/dbcs/provisioning/upgrading_database.md | 8 +- oracle-database-operator.yaml | 4 +- 13 files changed, 583 insertions(+), 171 deletions(-) diff --git a/apis/database/v4/dbcssystem_types.go b/apis/database/v4/dbcssystem_types.go index 50934bd8..8836c9fb 100644 --- a/apis/database/v4/dbcssystem_types.go +++ b/apis/database/v4/dbcssystem_types.go @@ -161,6 +161,7 @@ type DbcsSystemStatus struct { StorageManagement string `json:"storageManagement,omitempty"` NodeCount int `json:"nodeCount,omitempty"` CpuCoreCount int `json:"cpuCoreCount,omitempty"` + DbVersion string `json:"dbVersion,omitempty"` DbEdition string `json:"dbEdition,omitempty"` TimeZone string `json:"timeZone,omitempty"` @@ -177,7 +178,7 @@ type DbcsSystemStatus struct { KMSDetailsStatus KMSDetailsStatus `json:"kmsDetailsStatus,omitempty"` DbCloneStatus DbCloneStatus `json:"dbCloneStatus,omitempty"` PdbDetailsStatus []PDBDetailsStatus `json:"pdbDetailsStatus,omitempty"` - DataGuardStatus DataGuardStatus `json:"dataGuardStatus,omitempty"` + DataGuardStatus *DataGuardStatus `json:"dataGuardStatus,omitempty"` Backups []BackupInfo `json:"backups,omitempty"` Message string `json:"message,omitempty"` } @@ -296,12 +297,13 @@ type DbcsSystemList struct { type LifecycleState string const ( - Available LifecycleState = "AVAILABLE" - Failed LifecycleState = "FAILED" - Update LifecycleState = "UPDATING" - Provision LifecycleState = "PROVISIONING" - Terminate LifecycleState = "TERMINATED" - Upgrade LifecycleState = "UPGRADING" + Available LifecycleState = "AVAILABLE" + Failed LifecycleState = "FAILED" + Update LifecycleState = "UPDATING" + Provision LifecycleState = "PROVISIONING" + Terminate LifecycleState = "TERMINATING" + TerminatedState LifecycleState = "TERMINATED" + Upgrade LifecycleState = "UPGRADING" ) const lastSuccessfulSpec = "lastSuccessfulSpec" diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 7d186ffd..41b6c7e7 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -1776,7 +1776,11 @@ func (in *DbcsSystemStatus) DeepCopyInto(out *DbcsSystemStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.DataGuardStatus.DeepCopyInto(&out.DataGuardStatus) + if in.DataGuardStatus != nil { + in, out := &in.DataGuardStatus, &out.DataGuardStatus + *out = new(DataGuardStatus) + (*in).DeepCopyInto(*out) + } if in.Backups != nil { in, out := &in.Backups, &out.Backups *out = make([]BackupInfo, len(*in)) diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 13f72e5e..b0d82cc3 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -231,6 +231,10 @@ func CloneAndGetDbcsId(compartmentId string, logger logr.Logger, kubeClient clie if err != nil { return "", err } + + if compartmentId == "" { + compartmentId = *existingDbSystem.CompartmentId + } logger.Info("Retrieved existing Db System Details from OCI using Spec.Id") // // Create the clone request payload @@ -324,11 +328,44 @@ func PatchDBSystem( dbHomeId, patchId string) error { dbSystemId := dbcs.Spec.Id + // Check if patch is already applied or in progress then return + historyResp, err := dbClient.ListDbSystemPatchHistoryEntries(ctx, database.ListDbSystemPatchHistoryEntriesRequest{ + DbSystemId: dbSystemId, + }) + if err != nil { + logger.Error(err, "Failed to get patch history entries", "DBSystemID", dbSystemId) + return fmt.Errorf("failed to get patch history entries: %w", err) + } + + for _, entry := range historyResp.Items { + if entry.PatchId != nil && *entry.PatchId == patchId { + if entry.LifecycleState == database.PatchHistoryEntrySummaryLifecycleStateSucceeded { + logger.Info("Patch already applied, skipping", "PatchID", patchId) + return nil + } + if entry.LifecycleState == database.PatchHistoryEntrySummaryLifecycleStateInProgress { + logger.Info("Patch in progress, waiting until it completes", "PatchID", patchId) + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { + logger.Error(statusErr, "Failed to update lifecycle state to Provisioning") + return statusErr + } + // Wait for the patch to complete + return CheckPatchState(ctx, logger, dbClient, dbSystemId, patchId) + } + if entry.LifecycleState == database.PatchHistoryEntrySummaryLifecycleStateFailed { + logger.Error(fmt.Errorf("patch failed"), "Patch failed", "PatchID", patchId) + return fmt.Errorf("patch %s failed", patchId) + } + } + } + logger.Info("Starting patch process for DB System", "DBSystemID", dbSystemId) patchesResp, err := dbClient.ListDbSystemPatches(ctx, database.ListDbSystemPatchesRequest{ DbSystemId: dbSystemId, }) + if err != nil { logger.Error(err, "Failed to list patches for DB System", "DBSystemID", dbSystemId) return fmt.Errorf("failed to list patches for DB System: %w", err) @@ -359,27 +396,6 @@ func PatchDBSystem( logger.Error(nil, "Patch ID not found in available patches", "PatchID", patchId) return fmt.Errorf("patch ID %s not found in available DB System patches", patchId) } - // Check if patch is already applied or in progress then return - historyResp, err := dbClient.ListDbSystemPatchHistoryEntries(ctx, database.ListDbSystemPatchHistoryEntriesRequest{ - DbSystemId: dbSystemId, - }) - if err != nil { - logger.Error(err, "Failed to get patch history entries", "DBSystemID", dbSystemId) - return fmt.Errorf("failed to get patch history entries: %w", err) - } - - for _, entry := range historyResp.Items { - if entry.PatchId != nil && *entry.PatchId == patchId { - if entry.LifecycleState == database.PatchHistoryEntrySummaryLifecycleStateSucceeded { - logger.Info("Patch already applied, skipping", "PatchID", patchId) - return nil - } - if entry.LifecycleState == database.PatchHistoryEntrySummaryLifecycleStateInProgress { - logger.Info("Patch already in progress, skipping", "PatchID", patchId) - return nil - } - } - } updateDetails := database.UpdateDbSystemDetails{ Version: &database.PatchDetails{ @@ -402,13 +418,13 @@ func PatchDBSystem( logger.Info("Patch applied to DB System", "WorkRequestID", *updateResp.OpcWorkRequestId, "PatchID", patchId) // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Update, nwClient, wrClient); statusErr != nil { logger.Error(statusErr, "Failed to update lifecycle state to Provisioning") return statusErr } // Check the state - _, err = CheckResourceState(logger, dbClient, *updateResp.DbSystem.Id, string(databasev4.Provision), string(databasev4.Available)) + _, err = CheckResourceState(logger, dbClient, *updateResp.DbSystem.Id, string(databasev4.Update), string(databasev4.Available)) if err != nil { logger.Error(err, "Failed to verify DB System state post patching") return err @@ -419,12 +435,59 @@ func PatchDBSystem( return nil } -func safeBool(ptr *bool) bool { - if ptr != nil { - return *ptr + +// CheckPatchState waits for a specific patch to finish applying. +// It polls the patch history until the patch is SUCCEEDED or FAILED. +func CheckPatchState(ctx context.Context, logger logr.Logger, dbClient database.DatabaseClient, dbSystemId *string, patchId string) error { + // Maximum wait duration: 120 minutes + timeout := 120 * time.Minute + start := time.Now() + + for { + // Timeout guard + if time.Since(start) > timeout { + msg := fmt.Sprintf("timed out after %v waiting for patch %s to complete", timeout, patchId) + logger.Error(fmt.Errorf("timeout"), msg, "DBSystemID", *dbSystemId) + return fmt.Errorf(msg) + } + + historyResp, err := dbClient.ListDbSystemPatchHistoryEntries(ctx, database.ListDbSystemPatchHistoryEntriesRequest{ + DbSystemId: dbSystemId, + }) + if err != nil { + logger.Error(err, "Failed to get patch history entries", "DBSystemID", *dbSystemId) + return fmt.Errorf("failed to get patch history entries: %w", err) + } + + var found bool + for _, entry := range historyResp.Items { + if entry.PatchId != nil && *entry.PatchId == patchId { + found = true + switch entry.LifecycleState { + case database.PatchHistoryEntrySummaryLifecycleStateSucceeded: + logger.Info("Patch succeeded", "PatchID", patchId) + return nil + + case database.PatchHistoryEntrySummaryLifecycleStateFailed: + logger.Error(fmt.Errorf("patch failed"), "Patch failed", "PatchID", patchId) + return fmt.Errorf("patch %s failed", patchId) + + case database.PatchHistoryEntrySummaryLifecycleStateInProgress: + logger.Info("Patch still in progress, waiting", "PatchID", patchId) + time.Sleep(60 * time.Second) + continue + } + } + } + + if !found { + logger.Info("Patch ID not found in history yet, waiting", "PatchID", patchId) + time.Sleep(60 * time.Second) + continue + } } - return false } + func UpgradeDatabaseVersion( ctx context.Context, compartmentId string, @@ -558,8 +621,9 @@ func UpgradeDatabaseVersion( DbHomeId: &dbHomeId, }) if err != nil { - return fmt.Errorf("failed to get DB Home: %w", err) dbcs.Status.Message = "Failed to get DB Home" + return fmt.Errorf("failed to get DB Home: %w", err) + } currentDbVersion := dbHomeResp.DbHome.DbVersion @@ -705,6 +769,9 @@ func CloneFromBackupAndGetDbcsId( logger.Error(err, "DBSystemId not found") return "", err } + if compartmentId == "" { + compartmentId = *existingDatabase.CompartmentId + } // Fetch the existing DB system details existingDbSystem, err := dbClient.GetDbSystem(ctx, database.GetDbSystemRequest{ @@ -850,6 +917,10 @@ func CloneFromDatabaseAndGetDbcsId(compartmentId string, logger logr.Logger, kub logger.Error(err, "DBSystemId not found") return "", err } + dbcs.Spec.Id = dbSystemId + if compartmentId == "" { + compartmentId = *existingDatabase.CompartmentId + } // Fetch the existing DB system details existingDbSystem, err := dbClient.GetDbSystem(ctx, database.GetDbSystemRequest{ @@ -867,6 +938,11 @@ func CloneFromDatabaseAndGetDbcsId(compartmentId string, logger logr.Logger, kub return "", err } + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + return "", statusErr + } + // Create the clone request payload cloneRequest := database.LaunchDbSystemFromDatabaseDetails{ CompartmentId: existingDatabase.CompartmentId, @@ -920,15 +996,19 @@ func CloneFromDatabaseAndGetDbcsId(compartmentId string, logger logr.Logger, kub LaunchDbSystemDetails: cloneRequest, }) if err != nil { + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Failed, nwClient, wrClient); statusErr != nil { + return "", statusErr + } return "", err } dbcs.Status.DbCloneStatus.Id = response.DbSystem.Id - // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { - return "", statusErr - } + // // Change the phase to "Provisioning" + // if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + // return "", statusErr + // } // Check the state _, err = CheckResourceState(logger, dbClient, *response.DbSystem.Id, string(databasev4.Provision), string(databasev4.Available)) @@ -1216,6 +1296,7 @@ func mergeInstancesFromLatest(instance, latestInstance *databasev4.DbcsSystem) e func mergeStructFields(instanceField, latestField reflect.Value) { for i := 0; i < instanceField.NumField(); i++ { subField := instanceField.Type().Field(i) + instanceSubField := instanceField.Field(i) latestSubField := latestField.Field(i) @@ -1239,6 +1320,7 @@ func mergeStructFields(instanceField, latestField reflect.Value) { } } } + } func isExported(field reflect.StructField) bool { @@ -1368,6 +1450,9 @@ func GetDbSystemId(logger logr.Logger, dbClient database.DatabaseClient, dbcs *d if response.DbSystem.KmsKeyId != nil { dbcs.Status.KMSDetailsStatus.KeyId = *response.DbSystem.KmsKeyId } + if response.DbSystem.Version != nil { + dbcs.Status.DbVersion = *response.DbSystem.Version + } err = PopulateDBDetails(logger, dbClient, dbcs) if err != nil { logger.Info("Error Occurred while collecting the DB details") @@ -1833,48 +1918,65 @@ func GetDataGuardAssociationState(logger logr.Logger, dbClient database.Database } } -func CheckResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id string, currentState string, expectedState string) (string, error) { - // The database OCID is not available when the provisioning is onging. - // Retry until the new DbcsSystem is ready. - +// CheckResourceState waits until the resource moves from a transient state (e.g. PROVISIONING, UPDATING) +// to the expected state (e.g. AVAILABLE). It retries until success, timeout (120 minutes), or unexpected state occurs. +func CheckResourceState(logger logr.Logger, dbClient database.DatabaseClient, id string, transientState string, expectedState string) (string, error) { var state string var err error + + timeout := 120 * time.Minute + start := time.Now() + for { - state, err = GetResourceState(logger, dbClient, Id) + // Timeout guard + if time.Since(start) > timeout { + msg := fmt.Sprintf("timed out after %v waiting for DB System %s to reach state %s (last known state: %s)", + timeout, id, expectedState, state) + logger.Error(fmt.Errorf("timeout"), msg, "Id", id) + return state, errors.New(msg) + } + + state, err = GetResourceState(logger, dbClient, id) if err != nil { - logger.Info("Error occurred while collecting the resource life cycle state") + logger.Error(err, "Error occurred while collecting the resource lifecycle state", "Id", id) return "", err } - if string(state) == expectedState { - break - } else if string(state) == currentState { - logger.Info("DB System current state is still:" + string(state) + ". Sleeping for 60 seconds.") + + switch state { + case expectedState: + logger.Info("DB System reached expected state", "State", state, "Id", id) + return state, nil + // Explicitly handle UPDATING as transient state (for patching) + case string(database.DbSystemLifecycleStateUpdating): + logger.Info("DB System is still updating ", "State", state, "Id", id) time.Sleep(60 * time.Second) continue - } else { - msg := "DB System current state " + string(state) + " is not matching " + expectedState - logger.Info(msg) - return "", errors.New(msg) + case transientState: + logger.Info("DB System still in transient state", "State", state, "Id", id) + time.Sleep(60 * time.Second) // sleep before re-checking + continue + + default: + msg := fmt.Sprintf("DB System state %s is not matching expected state %s", state, expectedState) + logger.Error(errors.New(msg), "Unexpected DB System state", "Id", id) + return state, errors.New(msg) } } - - return "", nil } -func GetResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id string) (string, error) { - - dbcsId := Id - dbcsReq := database.GetDbSystemRequest{ - DbSystemId: &dbcsId, +// GetResourceState fetches the current lifecycle state of the DbSystem from OCI. +func GetResourceState(logger logr.Logger, dbClient database.DatabaseClient, id string) (string, error) { + req := database.GetDbSystemRequest{ + DbSystemId: common.String(id), } - response, err := dbClient.GetDbSystem(context.TODO(), dbcsReq) + resp, err := dbClient.GetDbSystem(context.TODO(), req) if err != nil { return "", err } - state := string(response.LifecycleState) - + state := string(resp.LifecycleState) + logger.Info("Fetched DB System lifecycle state", "State", state, "Id", id) return state, nil } @@ -1885,11 +1987,6 @@ func SetDBCSStatus(state databasev4.LifecycleState, compartmentId string, dbClie return nil } - if dbcs.Spec.Id == nil { - dbcs.Status.State = "FAILED" - return nil - } - dbcsId := *dbcs.Spec.Id dbcsReq := database.GetDbSystemRequest{ @@ -1900,6 +1997,7 @@ func SetDBCSStatus(state databasev4.LifecycleState, compartmentId string, dbClie if err != nil { return err } + compartmentId = *resp.CompartmentId dbcs.Status.AvailabilityDomain = *resp.AvailabilityDomain dbcs.Status.CpuCoreCount = *resp.CpuCoreCount @@ -1920,6 +2018,7 @@ func SetDBCSStatus(state databasev4.LifecycleState, compartmentId string, dbClie dbcs.Status.Network.ListenerPort = resp.ListenerPort dbcs.Status.Network.HostName = *resp.Hostname dbcs.Status.Network.DomainName = *resp.Domain + dbcs.Status.DbVersion = *resp.Version if dbcs.Spec.KMSConfig != nil && dbcs.Spec.KMSConfig.CompartmentId != "" { dbcs.Status.KMSDetailsStatus.CompartmentId = dbcs.Spec.KMSConfig.CompartmentId dbcs.Status.KMSDetailsStatus.VaultName = dbcs.Spec.KMSConfig.VaultName @@ -2090,6 +2189,12 @@ func ValidateSpex(logger logr.Logger, kubeClient client.Client, dbClient databas } // Check if last Successful update nil or not if lastSuccSpec == nil { + if dbcs == nil { + return fmt.Errorf("DbcsSystem is nil") + } + if dbcs.Spec.DbSystem == nil { + return fmt.Errorf("DbSystem spec is missing in DbcsSystem %s", dbcs.Name) + } if dbcs.Spec.DbSystem.DbVersion != "" { _, err = GetDbLatestVersion(dbClient, dbcs, "") if err != nil { diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index 8ce84a61..f7134796 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -783,6 +783,8 @@ spec: type: string type: object type: array + dbVersion: + type: string displayName: type: string id: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2726acb4..74303bea 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: restart-operator-sa + newTag: basedb-operator-sa diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index 7538d235..d5aa286b 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -110,10 +110,13 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) dbcsInst := &databasev4.DbcsSystem{} r.Logger.Info("Reconciling DbSystemDetails", "name", req.NamespacedName) - if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, dbcsInst); err != nil { - if !errors.IsNotFound(err) { - return ctrl.Result{}, err + if err := r.KubeClient.Get(ctx, req.NamespacedName, dbcsInst); err != nil { + if errors.IsNotFound(err) { + // CR was deleted → stop reconciling + r.Logger.Info("DbcsSystem resource not found.", "name", req.NamespacedName) + return ctrl.Result{}, nil } + return ctrl.Result{}, err } // Create oci-go-sdk client @@ -536,8 +539,6 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) dbcsInst.Status.Message = err.Error() return ctrl.Result{}, statusErr } - - return ctrl.Result{}, nil } case dbcsInst.Spec.IsUpgrade: @@ -585,6 +586,8 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) var dbSystemId string // Executing DB Cloning Process, if defined. Do not repeat cloning again when Status has Id present. if setupCloning && dbcsInst.Status.DbCloneStatus.Id == nil { + // if setupCloning { + switch { case dbcsInst.Spec.SetupDBCloning && dbcsInst.Spec.DbBackupId != nil: @@ -629,6 +632,10 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) if dbcsInst.Spec.Id == nil && lastSuccessfullSpec == nil { // If no DbcsSystem ID specified, create a new DB System // ======================== Validate Specs ============== + if dbcsInst == nil { + // Safety guard + return ctrl.Result{}, nil + } err = dbcsv4.ValidateSpex(r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.Recorder) if err != nil { dbcsInst.Status.Message = err.Error() @@ -780,7 +787,13 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) switch { case setupDataguard: // Data Guard Creation Flow - if err := r.EnableDataGuard(ctx, r.Logger, r.dbClient, dbcsInst, r.KubeClient, r.nwClient, r.wrClient, *dbcsInst.Spec.DataGuard.PrimaryDatabaseId, &dbcsInst.Spec.DataGuard); err != nil { + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + if err := r.EnableDataGuard(ctx, compartmentId, r.Logger, r.dbClient, dbcsInst, r.KubeClient, r.nwClient, r.wrClient, *dbcsInst.Spec.DataGuard.PrimaryDatabaseId, &dbcsInst.Spec.DataGuard); err != nil { r.Logger.Error(err, "Failed to enable Data Guard and update DbcsSystem ID") // Update status to Failed @@ -795,15 +808,23 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) return reconcile.Result{}, err } - dbSystemId = *dbcsInst.Status.DataGuardStatus.PeerDbSystemId + if dbcsInst.Status.DataGuardStatus != nil && dbcsInst.Status.DataGuardStatus.PeerDbSystemId != nil { + dbSystemId = *dbcsInst.Status.DataGuardStatus.PeerDbSystemId + assignDBCSID(dbcsInst, dbSystemId) + } if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { return ctrl.Result{}, err } - assignDBCSID(dbcsInst, dbSystemId) case isDeleteDataguard: // Data Guard Deletion Flow - if err := r.DeleteDataGuard(ctx, r.Logger, r.dbClient, dbcsInst); err != nil { + compartmentId, err := r.getCompartmentIDByDbSystemID(ctx, *dbcsInst.Spec.Id) + if err != nil { + fmt.Printf("Failed to get compartment ID: %v\n", err) + dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, err + } + if err := r.DeleteDataGuard(ctx, compartmentId, r.Logger, r.dbClient, dbcsInst); err != nil { r.Logger.Error(err, "Failed to delete Data Guard") // Update status to Failed @@ -839,7 +860,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, statusErr } - r.Logger.Info("DBInst after assignment", "dbcsInst:->", dbcsInst) + // r.Logger.Info("DBInst after assignment", "dbcsInst:->", dbcsInst) // Check if specified PDB exists or needs to be created exists, err := r.validatePDBExistence(dbcsInst) @@ -1001,65 +1022,204 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) func (r *DbcsSystemReconciler) DeleteDataGuard( ctx context.Context, + compartmentId string, log logr.Logger, dbClient database.DatabaseClient, dbcsInst *databasev4.DbcsSystem, ) error { dataGuardStatus := dbcsInst.Status.DataGuardStatus var peerDbSystemId *string - if dataGuardStatus.PeerDbSystemId != nil { + + // Prefer runtime status, fallback to spec if available + if dataGuardStatus != nil && dataGuardStatus.PeerDbSystemId != nil { peerDbSystemId = dataGuardStatus.PeerDbSystemId } - if dbcsInst.Spec.DataGuard.PeerDbSystemId != nil { peerDbSystemId = dbcsInst.Spec.DataGuard.PeerDbSystemId } + if peerDbSystemId == nil { - log.Info("Skipping Data Guard deletion — peer DB System ID is nil") - return nil + msg := "Skipping Data Guard deletion — peer DB System ID is nil" + log.Info(msg) + dbcsInst.Status.Message = msg + dbcsInst.Status.DataGuardStatus = nil + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + return fmt.Errorf(msg) + } + if dbcsInst.Status.DataGuardStatus == nil { + dbcsInst.Status.DataGuardStatus = &databasev4.DataGuardStatus{} + } + status := dbcsInst.Status.DataGuardStatus + + // Use Spec to fill necessary details + spec := dbcsInst.Spec.DataGuard + + // Populate Status from Spec (if present) + status.DbAdminPasswordSecret = spec.DbAdminPasswordSecret + status.PeerDbSystemId = spec.PeerDbSystemId + status.PrimaryDatabaseId = dbcsInst.Spec.Id + status.ProtectionMode = spec.ProtectionMode + status.TransportType = spec.TransportType + status.PeerRole = spec.PeerRole + status.Shape = spec.Shape + status.SubnetId = spec.SubnetId + + // Get peer DB system state + getDbSystemResp, err := dbClient.GetDbSystem(ctx, database.GetDbSystemRequest{ + DbSystemId: peerDbSystemId, + }) + if err != nil { + log.Error(err, "Failed to fetch peer DB system status") + dbcsInst.Status.Message = "Failed to fetch peer DB system status" + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + return err } - if peerDbSystemId != nil { - // / Fetch the DB system details to check its current lifecycle state - getDbSystemReq := database.GetDbSystemRequest{ - DbSystemId: peerDbSystemId, // The peer DB system ID we want to check + + switch getDbSystemResp.DbSystem.LifecycleState { + + case database.DbSystemLifecycleStateTerminated: + log.Info("Peer DB system is already terminated.", "peerDbSystemId", *peerDbSystemId) + + terminated := string(database.DbSystemLifecycleStateTerminated) + dbcsInst.Status.DataGuardStatus.LifecycleState = &terminated + details := "Peer DB system already terminated" + dbcsInst.Status.DataGuardStatus.LifecycleDetails = &details + + dbcsInst.Status.Message = "Data Guard peer already terminated" + + if statusErr := dbcsv4.SetLifecycleState( + compartmentId, r.KubeClient, r.dbClient, dbcsInst, + databasev4.Available, r.nwClient, r.wrClient, + ); statusErr != nil { + return statusErr + } + + return r.KubeClient.Status().Update(ctx, dbcsInst) + + case database.DbSystemLifecycleStateTerminating: + // Wait until it becomes TERMINATED (with timeout) + log.Info("Peer DB system is in TERMINATING state, waiting for it to reach TERMINATED...", + "peerDbSystemId", *peerDbSystemId) + status := dbcsInst.Status.DataGuardStatus + + // Use Spec to fill necessary details + spec := dbcsInst.Spec.DataGuard + + // Populate Status from Spec (if present) + status.DbAdminPasswordSecret = spec.DbAdminPasswordSecret + status.PeerDbSystemId = spec.PeerDbSystemId + status.PrimaryDatabaseId = dbcsInst.Spec.Id + status.ProtectionMode = spec.ProtectionMode + status.TransportType = spec.TransportType + status.PeerRole = spec.PeerRole + status.Shape = spec.Shape + status.SubnetId = spec.SubnetId + terminatingState := string(database.DbSystemLifecycleStateTerminating) + status.LifecycleState = &terminatingState + status.LifecycleDetails = common.String("Dataguard Peer DB system is terminating...") + dbcsInst.Status.State = databasev4.Update + dbcsInst.Status.Message = "Peer DB system is terminating..." + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + + pollInterval := 30 * time.Second + maxWait := 30 * time.Minute + timeout := time.After(maxWait) + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-timeout: + dbcsInst.Status.Message = "Timeout while waiting for peer DB system termination" + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + return fmt.Errorf("timed out waiting for peer DB system %s to terminate", *peerDbSystemId) + + case <-time.After(pollInterval): + latest, err := dbClient.GetDbSystem(ctx, database.GetDbSystemRequest{ + DbSystemId: peerDbSystemId, + }) + if err != nil { + log.Error(err, "Failed to poll DB system lifecycle state") + return err + } + + switch latest.DbSystem.LifecycleState { + case database.DbSystemLifecycleStateTerminated: + log.Info("Peer DB system successfully terminated", "peerDbSystemId", *peerDbSystemId) + goto Cleanup + case database.DbSystemLifecycleStateFailed: + dbcsInst.Status.Message = "Peer DB system termination failed" + dbcsInst.Status.State = databasev4.Available + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + return fmt.Errorf("DB system termination failed for peer %s", *peerDbSystemId) + default: + log.Info("Peer DB system still terminating...", + "peerDbSystemId", *peerDbSystemId, + "lifecycleState", latest.DbSystem.LifecycleState) + } + } } - getDbSystemResp, err := dbClient.GetDbSystem(ctx, getDbSystemReq) + default: + // Active or other state → initiate termination + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, + dbcsInst, databasev4.Update, r.nwClient, r.wrClient); statusErr != nil { + return statusErr + } + + log.Info("Terminating peer DB system", "peerDbSystemId", *peerDbSystemId) + status := dbcsInst.Status.DataGuardStatus + + // Use Spec to fill necessary details + spec := dbcsInst.Spec.DataGuard + + // Populate Status from Spec (if present) + status.DbAdminPasswordSecret = spec.DbAdminPasswordSecret + status.PeerDbSystemId = spec.PeerDbSystemId + status.PrimaryDatabaseId = dbcsInst.Spec.Id + status.ProtectionMode = spec.ProtectionMode + status.TransportType = spec.TransportType + status.PeerRole = spec.PeerRole + status.Shape = spec.Shape + status.SubnetId = spec.SubnetId + terminatingState := string(database.DbSystemLifecycleStateTerminating) + status.LifecycleState = &terminatingState + status.LifecycleDetails = common.String("Dataguard Peer DB system is terminating...") + dbcsInst.Status.State = databasev4.Update + dbcsInst.Status.Message = "Peer DB system is terminating..." + + dbcsInst.Status.Message = "Initiating peer DB system termination" + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + + termSysResp, err := dbClient.TerminateDbSystem(ctx, database.TerminateDbSystemRequest{ + DbSystemId: peerDbSystemId, + }) if err != nil { - log.Error(err, "Failed to fetch peer DB system status") + log.Error(err, "Failed to initiate termination of peer DB System") + dbcsInst.Status.Message = "Failed to initiate peer DB system termination" + _ = r.KubeClient.Status().Update(ctx, dbcsInst) return err } - // Check the lifecycle state of the peer DB system - if getDbSystemResp.DbSystem.LifecycleState == database.DbSystemLifecycleStateTerminated { - log.Info("Peer DB system is already terminated. No action needed.", "peerDbSystemId", *peerDbSystemId) - dbcsInst.Status.DataGuardStatus = databasev4.DataGuardStatus{} - if err := r.KubeClient.Status().Update(ctx, dbcsInst); err != nil { - log.Error(err, "Failed to update DB status after deleting Data Guard") - return err - } - return nil + if err := r.waitForWorkRequest(ctx, log, termSysResp.OpcWorkRequestId); err != nil { + log.Error(err, "Peer DB system termination failed or timed out") + dbcsInst.Status.Message = "Peer DB system termination failed or timed out" + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + return err + } + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, + dbcsInst, databasev4.Available, r.nwClient, r.wrClient); statusErr != nil { + return statusErr } } - log.Info("Terminating peer DB system", "peerDbSystemId", *peerDbSystemId) - termSysReq := database.TerminateDbSystemRequest{ - DbSystemId: peerDbSystemId, - } - - termSysResp, err := dbClient.TerminateDbSystem(ctx, termSysReq) - if err != nil { - log.Error(err, "Failed to initiate termination of peer DB System") - return err - } - - if err := r.waitForWorkRequest(ctx, log, termSysResp.OpcWorkRequestId); err != nil { - log.Error(err, "Peer DB system termination failed or timed out") - return err +Cleanup: + // Final cleanup — clear DataGuardStatus + dbcsInst.Status.Message = "Data Guard peer deleted successfully" + if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, + dbcsInst, databasev4.Available, r.nwClient, r.wrClient); statusErr != nil { + return statusErr } - - // Clear out DataGuardStatus === - dbcsInst.Status.DataGuardStatus = databasev4.DataGuardStatus{} if err := r.KubeClient.Status().Update(ctx, dbcsInst); err != nil { log.Error(err, "Failed to update DB status after deleting Data Guard") return err @@ -1506,8 +1666,8 @@ func (r *DbcsSystemReconciler) getDataGuardStatusAndUpdate( } if len(listResp.Items) == 0 { - log.Info("No Data Guard associations found") - dbcsInst.Status.DataGuardStatus = databasev4.DataGuardStatus{} // reset to empty + // log.Info("No Data Guard associations found") + dbcsInst.Status.DataGuardStatus = &databasev4.DataGuardStatus{} // reset to empty return nil } @@ -1528,7 +1688,7 @@ func (r *DbcsSystemReconciler) getDataGuardStatusAndUpdate( PeerDataGuardAssociationId: dg.Id, } - dbcsInst.Status.DataGuardStatus = status + dbcsInst.Status.DataGuardStatus = &status log.Info("Updated DataGuardStatus in CR", "DataGuardAssociationId", *dg.Id) return nil @@ -1552,12 +1712,6 @@ func (r *DbcsSystemReconciler) getPluggableDatabaseDetails(ctx context.Context, // Create a map to track existing PDBDetailsStatus by PdbName pdbDetailsMap := make(map[string]databasev4.PDBConfigStatus) - // Populate the map with existing PDBDetailsStatus from dbcsInst.Status.PdbDetailsStatus - // for _, existingPdbDetails := range dbcsInst.Status.PdbDetailsStatus { - // for _, existingPdbConfig := range existingPdbDetails.PDBConfigStatus { - // pdbDetailsMap[*existingPdbConfig.PdbName] = existingPdbConfig - // } - // } // Convert databaseIds array to a set for quick lookup databaseIdsSet := make(map[string]struct{}) for _, id := range databaseIds { @@ -1644,6 +1798,7 @@ func (r *DbcsSystemReconciler) doesPluggableDatabaseExist(ctx context.Context, c // Enable Dataguard func (r *DbcsSystemReconciler) EnableDataGuard( ctx context.Context, + compartmentId string, log logr.Logger, dbClient database.DatabaseClient, dbcsSystem *databasev4.DbcsSystem, @@ -1674,11 +1829,14 @@ func (r *DbcsSystemReconciler) EnableDataGuard( } databaseAdminPassword, err := r.getSecret(ctx, dbcsSystem.Namespace, *dataGuardConfig.DbAdminPasswordSecret) + if err != nil { + return err + } // Trim newline character from the password databaseAdminPassword = strings.TrimSpace(databaseAdminPassword) if updateFlag { - exists, err := r.checkExistingDataGuardAssociation(ctx, dbClient, dbcsSystem, databaseID) + exists, err := r.checkExistingDataGuardAssociation(ctx, dbClient, dbcsSystem, databaseID, r.KubeClient, r.nwClient, r.wrClient) if err != nil { return err } @@ -1687,6 +1845,10 @@ func (r *DbcsSystemReconciler) EnableDataGuard( log.Info("Skipping Data Guard creation as it already exists for the database", "DatabaseID", databaseID) return nil } + // Change the phase to "Provisioning" + if statusErr := dbcsv4.SetLifecycleState(compartmentId, kubeClient, dbClient, dbcsSystem, databasev4.Update, nwClient, wrClient); statusErr != nil { + return statusErr + } request := database.CreateDataGuardAssociationRequest{ DatabaseId: common.String(databaseID), @@ -1710,7 +1872,32 @@ func (r *DbcsSystemReconciler) EnableDataGuard( return err } - r.Logger.Info("Data Guard association created successfully.") + r.Logger.Info("Data Guard association creation started") + status := dbcsSystem.Status.DataGuardStatus + + // Use Spec to fill necessary details + spec := dbcsSystem.Spec.DataGuard + + // Populate Status from Spec (if present) + if dbcsSystem.Spec.DataGuard.DbAdminPasswordSecret != nil { + status.DbAdminPasswordSecret = dbcsSystem.Spec.DataGuard.DbAdminPasswordSecret + } + + status.PeerDbSystemId = spec.PeerDbSystemId + status.PrimaryDatabaseId = dbcsSystem.Spec.Id + status.ProtectionMode = spec.ProtectionMode + status.TransportType = spec.TransportType + status.PeerRole = spec.PeerRole + status.Shape = spec.Shape + status.SubnetId = spec.SubnetId + provisioningState := string(database.DbSystemLifecycleStateProvisioning) + status.LifecycleState = &provisioningState + status.LifecycleDetails = common.String("Dataguard Peer DB system is Provisioning...") + dbcsSystem.Status.State = databasev4.Update + dbcsSystem.Status.Message = "Peer DB system is provisioning..." + + dbcsSystem.Status.Message = "Initiating peer DB system provisioning for Data Guard association" + _ = r.KubeClient.Status().Update(ctx, dbcsSystem) // Extract the DataGuardAssociation ID from the response associationId := *response.DataGuardAssociation.Id @@ -1729,7 +1916,7 @@ func (r *DbcsSystemReconciler) EnableDataGuard( r.Logger.Info("No DataGuard update required; configurations match") } - _, err = r.checkExistingDataGuardAssociation(ctx, dbClient, dbcsSystem, databaseID) + _, err = r.checkExistingDataGuardAssociation(ctx, dbClient, dbcsSystem, databaseID, r.KubeClient, r.nwClient, r.wrClient) if err != nil { return err } @@ -1738,7 +1925,15 @@ func (r *DbcsSystemReconciler) EnableDataGuard( } // Get Dataguard Details -func (r *DbcsSystemReconciler) checkExistingDataGuardAssociation(ctx context.Context, dbClient database.DatabaseClient, dbcsSystem *databasev4.DbcsSystem, databaseID string) (bool, error) { +func (r *DbcsSystemReconciler) checkExistingDataGuardAssociation( + ctx context.Context, + dbClient database.DatabaseClient, + dbcsSystem *databasev4.DbcsSystem, + databaseID string, + kubeClient client.Client, + nwClient core.VirtualNetworkClient, + wrClient workrequests.WorkRequestClient, +) (bool, error) { request := database.ListDataGuardAssociationsRequest{ DatabaseId: common.String(databaseID), @@ -1749,12 +1944,16 @@ func (r *DbcsSystemReconciler) checkExistingDataGuardAssociation(ctx context.Con return false, fmt.Errorf("failed to list Data Guard associations: %w", err) } - // Check if any Data Guard associations are present if len(response.Items) > 0 { - r.Logger.Info("Data Guard association found for the database", "DatabaseID", databaseID) - item := response.Items[0] - status := &dbcsSystem.Status.DataGuardStatus + + // r.Logger.Info("Data Guard association found for the database", "DatabaseID", databaseID) + + // Ensure DataGuardStatus struct exists + if dbcsSystem.Status.DataGuardStatus == nil { + dbcsSystem.Status.DataGuardStatus = &databasev4.DataGuardStatus{} + } + status := dbcsSystem.Status.DataGuardStatus if item.PeerDbSystemId != nil { status.PeerDbSystemId = item.PeerDbSystemId @@ -1771,23 +1970,23 @@ func (r *DbcsSystemReconciler) checkExistingDataGuardAssociation(ctx context.Con } if item.PeerRole != "" { - status.PeerRole = (*string)(&item.PeerRole) - } - - if item.PeerDbHomeId != nil { - status.PeerDbHomeId = item.PeerDbHomeId + s := string(item.PeerRole) + status.PeerRole = &s } if item.ProtectionMode != "" { - status.ProtectionMode = (*string)(&item.ProtectionMode) + s := string(item.ProtectionMode) + status.ProtectionMode = &s } if item.TransportType != "" { - status.TransportType = (*string)(&item.TransportType) + s := string(item.TransportType) + status.TransportType = &s } if item.LifecycleState != "" { - status.LifecycleState = (*string)(&item.LifecycleState) + s := string(item.LifecycleState) + status.LifecycleState = &s } if item.PeerDataGuardAssociationId != nil { @@ -1796,6 +1995,8 @@ func (r *DbcsSystemReconciler) checkExistingDataGuardAssociation(ctx context.Con if item.LifecycleDetails != nil { status.LifecycleDetails = item.LifecycleDetails + } else { + status.LifecycleDetails = common.String("Dataguard association enabled for the database") } if item.Id != nil { @@ -1806,12 +2007,45 @@ func (r *DbcsSystemReconciler) checkExistingDataGuardAssociation(ctx context.Con status.PeerDatabaseId = item.PeerDatabaseId } - return true, nil - } + if item.LifecycleState == database.DataGuardAssociationSummaryLifecycleStateAvailable { + status.LifecycleDetails = common.String("Data Guard association is available") - r.Logger.Info("No Data Guard association found for the database", "DatabaseID", databaseID) - return false, nil + r.Logger.Info("Data Guard association is available", "DatabaseID", databaseID) + + if err := r.KubeClient.Status().Update(ctx, dbcsSystem); err != nil { + return false, fmt.Errorf("failed to update DbcsSystem status: %w", err) + } + + return true, nil + } + + if item.LifecycleState == database.DataGuardAssociationSummaryLifecycleStateFailed { + if item.LifecycleDetails != nil { + status.LifecycleDetails = item.LifecycleDetails + } else { + status.LifecycleDetails = common.String("Data Guard association is failed") + } + + _ = r.KubeClient.Status().Update(ctx, dbcsSystem) + + return false, fmt.Errorf("data guard association failed: %s", *status.LifecycleDetails) + } + + if item.LifecycleState == database.DataGuardAssociationSummaryLifecycleStateProvisioning { + status.LifecycleDetails = common.String("Data Guard association is getting provisioned") + + if err := r.KubeClient.Status().Update(ctx, dbcsSystem); err != nil { + return false, fmt.Errorf("failed to update DbcsSystem status during provisioning: %w", err) + } + + r.Logger.Info("Data Guard association is provisioning", "DatabaseID", databaseID) + return true, nil + } + } else { + return false, nil + } + return false, nil } // Function to create KMS vault diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml index 6ff24bc8..05885fdb 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml @@ -1,8 +1,21 @@ +# The API group and version this custom resource belongs to (Oracle Database Operator CRD v4) apiVersion: database.oracle.com/v4 + +# The type of resource (DbcsSystem manages an OCI DB System) kind: DbcsSystem + +# Standard Kubernetes object metadata metadata: + # The name of this DbcsSystem resource (must be unique within the namespace) name: dbcssystem-existing + +# Specification of the desired state for the DbcsSystem spec: - id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" - ociConfigMap: "oci-cred" - ociSecret: "oci-privatekey" + # The OCID of the existing DB System in OCI that this resource manages + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq" + + # The name of the ConfigMap containing OCI credentials (tenancy, region, etc.) + ociConfigMap: "oci-cred-mumbai" + + # The name of the Secret containing the OCI private key used for authentication + ociSecret: "oci-privatekey" \ No newline at end of file diff --git a/docs/dbcs/provisioning/disable_dataguard_to_database.md b/docs/dbcs/provisioning/disable_dataguard_to_database.md index acf4287c..f6682e03 100644 --- a/docs/dbcs/provisioning/disable_dataguard_to_database.md +++ b/docs/dbcs/provisioning/disable_dataguard_to_database.md @@ -27,7 +27,14 @@ NOTE: Check the DB Operator Pod name in your environment. ``` [root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` +3. Describe the Kubernetes object to see more details +```bash +kubectl describe dbcssystems.database.oracle.com dbcssystem-existing +``` +More precisely +```bash +kubectl get dbcssystems.database.oracle.com dbcssystem-existing -o jsonpath='{.status.dataGuardStatus.lifecycleState}' +TERMINATED +``` -## Sample Output -[Here](./disable_dataguard_in_database_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. diff --git a/docs/dbcs/provisioning/patch_dbcs_system.yaml b/docs/dbcs/provisioning/patch_dbcs_system.yaml index c9b44a8f..6f6e6ed0 100644 --- a/docs/dbcs/provisioning/patch_dbcs_system.yaml +++ b/docs/dbcs/provisioning/patch_dbcs_system.yaml @@ -1,11 +1,29 @@ +# The API group and version this custom resource belongs to (Oracle Database Operator CRD v4) apiVersion: database.oracle.com/v4 + +# The type of resource (DbcsSystem manages an OCI DB System) kind: DbcsSystem + +# Standard Kubernetes object metadata metadata: + # The name of this DbcsSystem resource (must be unique within the namespace) name: dbcssystem-existing + +# Specification of the desired state for the DbcsSystem spec: + # The OCID of the existing DB System in OCI that this resource manages id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq" + + # The name of the ConfigMap containing OCI credentials (tenancy, region, etc.) ociConfigMap: "oci-cred-mumbai" + + # The name of the Secret containing the OCI private key used for authentication ociSecret: "oci-privatekey" + + # Indicates that this CR should apply a patch/update instead of creating a new DB system isPatch: true + + # DB system-specific configuration (nested object) dbSystem: - dbPatchOcid: "ocid1.dbupdate.oc1.ap-mumbai-1.anrg6ljrt5t4sqqa2z6zz3vxd4u2gmkvrofeatpufsbw332yksvuzc2xmgxq" \ No newline at end of file + # The OCID of the database patch (db update) you want to apply to this DB system + dbPatchOcid: "ocid1.dbupdate.oc1.ap-mumbai-1.anrg6ljrt5t4sqqa2z6zz3vxd4u2gmkvrofeatpufsbw332yksvuzc2xmgxq" diff --git a/docs/dbcs/provisioning/patching_database.md b/docs/dbcs/provisioning/patching_database.md index 89422adc..f8ea1182 100644 --- a/docs/dbcs/provisioning/patching_database.md +++ b/docs/dbcs/provisioning/patching_database.md @@ -26,7 +26,7 @@ Use the file: [patch_dbcs_system.yaml](./patch_dbcs_system.yaml) for this use ca 1. Deploy the .yaml file: ```sh [root@docker-test-server DBCS]# kubectl apply -f patch_dbcs_system.yaml -dbcssystem.database.oracle.com/dbcssystem-patch configured +dbcssystem.database.oracle.com/dbcssystem-existing configured ``` 2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. @@ -36,7 +36,10 @@ NOTE: Check the DB Operator Pod name in your environment. ``` [root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` +3. Check details of kubernetes object post patching is complete and verify patched version is same as expected- +```bash +kubectl describe dbcssystems.database.oracle.com dbcssystem-existing -## Sample Output - -[Here](./patch_dbcs_system_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +kubectl get dbcssystems.database.oracle.com dbcssystem-existing -o jsonpath='{.status.dbVersion}' +19.28.0.0.0 +``` \ No newline at end of file diff --git a/docs/dbcs/provisioning/upgrade_dbcs_system.yaml b/docs/dbcs/provisioning/upgrade_dbcs_system.yaml index 6a100180..38b3df6f 100644 --- a/docs/dbcs/provisioning/upgrade_dbcs_system.yaml +++ b/docs/dbcs/provisioning/upgrade_dbcs_system.yaml @@ -1,12 +1,32 @@ -apiVersion: database.oracle.com/v4 -kind: DbcsSystem -metadata: - name: dbcssystem-existing -spec: - id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq" - ociConfigMap: "oci-cred-mumbai" - ociSecret: "oci-privatekey" - isUpgrade: true - databaseId: "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a" - dbSystem: - dbUpgradeVersion: "23.6.0.24.10" \ No newline at end of file +# API group and version for Oracle DBCS operator +apiVersion: database.oracle.com/v4 + +# Kubernetes resource kind — represents a DB system in OCI +kind: DbcsSystem + +# Standard Kubernetes metadata (object name, namespace, labels etc.) +metadata: + # The name of this DbcsSystem object inside the cluster + name: dbcssystem-existing + +# Specification of the DBCS system +spec: + # OCID of the existing DB System in OCI (the one we want to manage/upgrade) + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyagag6vwgkdcf5g6srfykrsqnfnryfskfhblntsjypiajq" + + # Name of the ConfigMap that holds OCI credentials (tenancy, user, fingerprint, region) + ociConfigMap: "oci-cred-mumbai" + + # Name of the Secret that contains the private key for authentication + ociSecret: "oci-privatekey" + + # Flag indicating this spec is meant to perform an upgrade on the DB system + isUpgrade: true + + # OCID of the specific database inside the DB System (not the DB system itself) + databaseId: "ocid1.database.oc1.ap-mumbai-1.anrg6ljrabf7htya2mo5an5blhlvx5euyqnazi45hli7zqs5uig7juzgme5a" + + # dbSystem section holds DB system upgrade target details + dbSystem: + # Target database version for upgrade (new GI/DB version) + dbUpgradeVersion: "23.6.0.24.10" diff --git a/docs/dbcs/provisioning/upgrading_database.md b/docs/dbcs/provisioning/upgrading_database.md index 041c352c..706f1af7 100644 --- a/docs/dbcs/provisioning/upgrading_database.md +++ b/docs/dbcs/provisioning/upgrading_database.md @@ -38,6 +38,8 @@ NOTE: Check the DB Operator Pod name in your environment. [root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` -## Sample Output - -[Here](./upgrade_dbcs_system_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +3. Describe kubernetes object to see correct Db Version post upgrade is completed- +```bash +kubectl get dbcssystems.database.oracle.com dbcssystem-existing +kubectl get dbcssystems.database.oracle.com dbcssystem-existing -o jsonpath='{.status.dbVersion}' +``` \ No newline at end of file diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index de951ce3..4efc3ace 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10072,6 +10072,8 @@ spec: type: string type: object type: array + dbVersion: + type: string displayName: type: string id: @@ -17122,7 +17124,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:restart-operator-sa + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa imagePullPolicy: Always name: manager ports: From 73046764fbb0df9af570d8f2c267e73fe983a2a7 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 22 Aug 2025 15:22:47 +0000 Subject: [PATCH 281/414] pdb/cdb decomission --- apis/database/v1alpha1/cdb_types.go | 190 - apis/database/v1alpha1/pdb_types.go | 236 - .../v1alpha1/zz_generated.deepcopy.go | 602 -- apis/database/v4/cdb_types.go | 191 - apis/database/v4/cdb_webhook.go | 229 - apis/database/v4/pdb_types.go | 237 - apis/database/v4/pdb_webhook.go | 374 - apis/database/v4/zz_generated.deepcopy.go | 602 -- .../crd/bases/database.oracle.com_cdbs.yaml | 492 - .../crd/bases/database.oracle.com_pdbs.yaml | 634 -- config/crd/kustomization.yaml | 8 - config/manager/kustomization.yaml | 4 +- config/rbac/cdb_editor_role.yaml | 24 - config/rbac/cdb_viewer_role.yaml | 20 - config/rbac/pdb_editor_role.yaml | 24 - config/rbac/pdb_viewer_role.yaml | 20 - config/rbac/role.yaml | 6 - config/samples/multitenant/cdb.yaml | 43 - config/samples/multitenant/cdb_secret.yaml | 17 - config/samples/multitenant/pdb_clone.yaml | 27 - config/samples/multitenant/pdb_create.yaml | 27 - config/samples/multitenant/pdb_delete.yaml | 16 - config/samples/multitenant/pdb_modify.yaml | 22 - config/samples/multitenant/pdb_plug.yaml | 19 - config/samples/multitenant/pdb_secret.yaml | 13 - config/samples/multitenant/pdb_unplug.yaml | 27 - config/webhook/manifests.yaml | 84 - controllers/database/cdb_controller.go | 1093 --- controllers/database/pdb_controller.go | 1631 ---- main.go | 42 - oracle-database-operator.yaml | 8060 +++++++---------- ords/Dockerfile | 86 - ords/runOrdsSSL.sh | 197 - 33 files changed, 3423 insertions(+), 11874 deletions(-) delete mode 100644 apis/database/v1alpha1/cdb_types.go delete mode 100644 apis/database/v1alpha1/pdb_types.go delete mode 100644 apis/database/v4/cdb_types.go delete mode 100644 apis/database/v4/cdb_webhook.go delete mode 100644 apis/database/v4/pdb_types.go delete mode 100644 apis/database/v4/pdb_webhook.go delete mode 100644 config/crd/bases/database.oracle.com_cdbs.yaml delete mode 100644 config/crd/bases/database.oracle.com_pdbs.yaml delete mode 100644 config/rbac/cdb_editor_role.yaml delete mode 100644 config/rbac/cdb_viewer_role.yaml delete mode 100644 config/rbac/pdb_editor_role.yaml delete mode 100644 config/rbac/pdb_viewer_role.yaml delete mode 100644 config/samples/multitenant/cdb.yaml delete mode 100644 config/samples/multitenant/cdb_secret.yaml delete mode 100644 config/samples/multitenant/pdb_clone.yaml delete mode 100644 config/samples/multitenant/pdb_create.yaml delete mode 100644 config/samples/multitenant/pdb_delete.yaml delete mode 100644 config/samples/multitenant/pdb_modify.yaml delete mode 100644 config/samples/multitenant/pdb_plug.yaml delete mode 100644 config/samples/multitenant/pdb_secret.yaml delete mode 100644 config/samples/multitenant/pdb_unplug.yaml delete mode 100644 controllers/database/cdb_controller.go delete mode 100644 controllers/database/pdb_controller.go delete mode 100644 ords/Dockerfile delete mode 100644 ords/runOrdsSSL.sh diff --git a/apis/database/v1alpha1/cdb_types.go b/apis/database/v1alpha1/cdb_types.go deleted file mode 100644 index f97df391..00000000 --- a/apis/database/v1alpha1/cdb_types.go +++ /dev/null @@ -1,190 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// CDBSpec defines the desired state of CDB -type CDBSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Name of the CDB - CDBName string `json:"cdbName,omitempty"` - // Name of the CDB Service - ServiceName string `json:"serviceName,omitempty"` - - // Password for the CDB System Administrator - SysAdminPwd CDBSysAdminPassword `json:"sysAdminPwd,omitempty"` - // User in the root container with sysdba priviledges to manage PDB lifecycle - CDBAdminUser CDBAdminUser `json:"cdbAdminUser,omitempty"` - // Password for the CDB Administrator to manage PDB lifecycle - CDBAdminPwd CDBAdminPassword `json:"cdbAdminPwd,omitempty"` - - CDBTlsKey CDBTLSKEY `json:"cdbTlsKey,omitempty"` - CDBTlsCrt CDBTLSCRT `json:"cdbTlsCrt,omitempty"` - - // Password for user ORDS_PUBLIC_USER - ORDSPwd ORDSPassword `json:"ordsPwd,omitempty"` - // ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. - ORDSPort int `json:"ordsPort,omitempty"` - // ORDS Image Name - ORDSImage string `json:"ordsImage,omitempty"` - // The name of the image pull secret in case of a private docker repository. - ORDSImagePullSecret string `json:"ordsImagePullSecret,omitempty"` - // ORDS Image Pull Policy - // +kubebuilder:validation:Enum=Always;Never - ORDSImagePullPolicy string `json:"ordsImagePullPolicy,omitempty"` - // Number of ORDS Containers to create - Replicas int `json:"replicas,omitempty"` - // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - WebServerUser WebServerUser `json:"webServerUser,omitempty"` - // Password for the Web Server User - WebServerPwd WebServerPassword `json:"webServerPwd,omitempty"` - // Name of the DB server - DBServer string `json:"dbServer,omitempty"` - // DB server port - DBPort int `json:"dbPort,omitempty"` - // Node Selector for running the Pod - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - DeletePDBCascade bool `json:"deletePdbCascade,omitempty"` - DBTnsurl string `json:"dbTnsurl,omitempty"` - CDBPubKey CDBPUBKEY `json:"cdbOrdsPubKey,omitempty"` - CDBPriKey CDBPRIVKEY `json:"cdbOrdsPrvKey,omitempty"` -} - -// CDBSecret defines the secretName -type CDBSecret struct { - SecretName string `json:"secretName"` - Key string `json:"key"` -} - -// CDBSysAdminPassword defines the secret containing SysAdmin Password mapped to key 'sysAdminPwd' for CDB -type CDBSysAdminPassword struct { - Secret CDBSecret `json:"secret"` -} - -// CDBAdminUser defines the secret containing CDB Administrator User mapped to key 'cdbAdminUser' to manage PDB lifecycle -type CDBAdminUser struct { - Secret CDBSecret `json:"secret"` -} - -// CDBAdminPassword defines the secret containing CDB Administrator Password mapped to key 'cdbAdminPwd' to manage PDB lifecycle -type CDBAdminPassword struct { - Secret CDBSecret `json:"secret"` -} - -// ORDSPassword defines the secret containing ORDS_PUBLIC_USER Password mapped to key 'ordsPwd' -type ORDSPassword struct { - Secret CDBSecret `json:"secret"` -} - -// WebServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle -type WebServerUser struct { - Secret CDBSecret `json:"secret"` -} - -// WebServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle -type WebServerPassword struct { - Secret CDBSecret `json:"secret"` -} - -type CDBTLSKEY struct { - Secret CDBSecret `json:"secret"` -} - -type CDBTLSCRT struct { - Secret CDBSecret `json:"secret"` -} - -type CDBPUBKEY struct { - Secret CDBSecret `json:"secret"` -} - -type CDBPRIVKEY struct { - Secret CDBSecret `json:"secret"` -} - -// CDBStatus defines the observed state of CDB -type CDBStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Phase of the CDB Resource - Phase string `json:"phase"` - // CDB Resource Status - Status bool `json:"status"` - // Message - Msg string `json:"msg,omitempty"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" -// +kubebuilder:printcolumn:JSONPath=".spec.dbServer",name="DB Server",type="string",description=" Name of the DB Server" -// +kubebuilder:printcolumn:JSONPath=".spec.dbPort",name="DB Port",type="integer",description="DB server port" -// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="Replicas",type="integer",description="Replicas" -// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the CDB Resource" -// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" -// +kubebuilder:printcolumn:JSONPath=".spec.dbTnsurl",name="TNS STRING",type="string",description=" string of the tnsalias" -// +kubebuilder:resource:path=cdbs,scope=Namespaced - -// CDB is the Schema for the cdbs API -type CDB struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec CDBSpec `json:"spec,omitempty"` - Status CDBStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// CDBList contains a list of CDB -type CDBList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []CDB `json:"items"` -} - -func init() { - SchemeBuilder.Register(&CDB{}, &CDBList{}) -} diff --git a/apis/database/v1alpha1/pdb_types.go b/apis/database/v1alpha1/pdb_types.go deleted file mode 100644 index 8b966c38..00000000 --- a/apis/database/v1alpha1/pdb_types.go +++ /dev/null @@ -1,236 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// PDBSpec defines the desired state of PDB -type PDBSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - PDBTlsKey PDBTLSKEY `json:"pdbTlsKey,omitempty"` - PDBTlsCrt PDBTLSCRT `json:"pdbTlsCrt,omitempty"` - PDBTlsCat PDBTLSCAT `json:"pdbTlsCat,omitempty"` - - // CDB Namespace - CDBNamespace string `json:"cdbNamespace,omitempty"` - // Name of the CDB Custom Resource that runs the ORDS container - CDBResName string `json:"cdbResName,omitempty"` - // Name of the CDB - CDBName string `json:"cdbName,omitempty"` - // The name of the new PDB. Relevant for both Create and Plug Actions. - PDBName string `json:"pdbName,omitempty"` - // Name of the Source PDB from which to clone - SrcPDBName string `json:"srcPdbName,omitempty"` - // The administrator username for the new PDB. This property is required when the Action property is Create. - AdminName PDBAdminName `json:"adminName,omitempty"` - // The administrator password for the new PDB. This property is required when the Action property is Create. - AdminPwd PDBAdminPassword `json:"adminPwd,omitempty"` - // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - WebServerUsr WebServerUserPDB `json:"webServerUser,omitempty"` - // Password for the Web ServerPDB User - WebServerPwd WebServerPasswordPDB `json:"webServerPwd,omitempty"` - // Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. - FileNameConversions string `json:"fileNameConversions,omitempty"` - // This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. - SourceFileNameConversions string `json:"sourceFileNameConversions,omitempty"` - // XML metadata filename to be used for Plug or Unplug operations - XMLFileName string `json:"xmlFileName,omitempty"` - // To copy files or not while cloning a PDB - // +kubebuilder:validation:Enum=COPY;NOCOPY;MOVE - CopyAction string `json:"copyAction,omitempty"` - // Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). - // +kubebuilder:validation:Enum=INCLUDING;KEEP - DropAction string `json:"dropAction,omitempty"` - // A Path specified for sparse clone snapshot copy. (Optional) - SparseClonePath string `json:"sparseClonePath,omitempty"` - // Whether to reuse temp file - ReuseTempFile *bool `json:"reuseTempFile,omitempty"` - // Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. - UnlimitedStorage *bool `json:"unlimitedStorage,omitempty"` - // Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. - AsClone *bool `json:"asClone,omitempty"` - // Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - TotalSize string `json:"totalSize,omitempty"` - // Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - TempSize string `json:"tempSize,omitempty"` - // TDE import for plug operations - TDEImport *bool `json:"tdeImport,omitempty"` - // TDE export for unplug operations - TDEExport *bool `json:"tdeExport,omitempty"` - // TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations - TDEPassword TDEPwd `json:"tdePassword,omitempty"` - // TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - TDEKeystorePath string `json:"tdeKeystorePath,omitempty"` - // TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - TDESecret TDESecret `json:"tdeSecret,omitempty"` - // Whether you need the script only or execute the script - GetScript *bool `json:"getScript,omitempty"` - // Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR. - // +kubebuilder:validation:Enum=Create;Clone;Plug;Unplug;Delete;Modify;Status;Map - Action string `json:"action"` - // Extra options for opening and closing a PDB - // +kubebuilder:validation:Enum=IMMEDIATE;NORMAL;READ ONLY;READ WRITE;RESTRICTED - ModifyOption string `json:"modifyOption,omitempty"` - // The target state of the PDB - // +kubebuilder:validation:Enum=OPEN;CLOSE - PDBState string `json:"pdbState,omitempty"` - // turn on the assertive approach to delete pdb resource - // kubectl delete pdb ..... automatically triggers the pluggable database - // deletion - AssertivePdbDeletion bool `json:"assertivePdbDeletion,omitempty"` - PDBPubKey PDBPUBKEY `json:"pdbOrdsPubKey,omitempty"` - PDBPriKey PDBPRIVKEY `json:"pdbOrdsPrvKey,omitempty"` -} - -// PDBAdminName defines the secret containing Sys Admin User mapped to key 'adminName' for PDB -type PDBAdminName struct { - Secret PDBSecret `json:"secret"` -} - -// PDBAdminPassword defines the secret containing Sys Admin Password mapped to key 'adminPwd' for PDB -type PDBAdminPassword struct { - Secret PDBSecret `json:"secret"` -} - -// TDEPwd defines the secret containing TDE Wallet Password mapped to key 'tdePassword' for PDB -type TDEPwd struct { - Secret PDBSecret `json:"secret"` -} - -// TDESecret defines the secret containing TDE Secret to key 'tdeSecret' for PDB -type TDESecret struct { - Secret PDBSecret `json:"secret"` -} - -// WebServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle - -type WebServerUserPDB struct { - Secret PDBSecret `json:"secret"` -} - -// WebServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle -type WebServerPasswordPDB struct { - Secret PDBSecret `json:"secret"` -} - -// PDBSecret defines the secretName -type PDBSecret struct { - SecretName string `json:"secretName"` - Key string `json:"key"` -} - -type PDBTLSKEY struct { - Secret PDBSecret `json:"secret"` -} - -type PDBTLSCRT struct { - Secret PDBSecret `json:"secret"` -} - -type PDBTLSCAT struct { - Secret PDBSecret `json:"secret"` -} - -// PDBStatus defines the observed state of PDB -type PDBStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // PDB Connect String - ConnString string `json:"connString,omitempty"` - // Phase of the PDB Resource - Phase string `json:"phase"` - // PDB Resource Status - Status bool `json:"status"` - // Total size of the PDB - TotalSize string `json:"totalSize,omitempty"` - // Open mode of the PDB - OpenMode string `json:"openMode,omitempty"` - // Modify Option of the PDB - ModifyOption string `json:"modifyOption,omitempty"` - // Message - Msg string `json:"msg,omitempty"` - // Last Completed Action - Action string `json:"action,omitempty"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" -// +kubebuilder:printcolumn:JSONPath=".spec.pdbName",name="PDB Name",type="string",description="Name of the PDB" -// +kubebuilder:printcolumn:JSONPath=".status.openMode",name="PDB State",type="string",description="PDB Open Mode" -// +kubebuilder:printcolumn:JSONPath=".status.totalSize",name="PDB Size",type="string",description="Total Size of the PDB" -// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the PDB Resource" -// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" -// +kubebuilder:printcolumn:JSONPath=".status.connString",name="Connect_String",type="string",description="The connect string to be used" -// +kubebuilder:resource:path=pdbs,scope=Namespaced - -// PDB is the Schema for the pdbs API -type PDB struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec PDBSpec `json:"spec,omitempty"` - Status PDBStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// PDBList contains a list of PDB -type PDBList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []PDB `json:"items"` -} - -type PDBPUBKEY struct { - Secret PDBSecret `json:"secret"` -} - -type PDBPRIVKEY struct { - Secret PDBSecret `json:"secret"` -} - -func init() { - SchemeBuilder.Register(&PDB{}, &PDBList{}) -} diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 661fb197..511a2912 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -683,239 +683,6 @@ func (in *Backupconfig) DeepCopy() *Backupconfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDB) DeepCopyInto(out *CDB) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDB. -func (in *CDB) DeepCopy() *CDB { - if in == nil { - return nil - } - out := new(CDB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CDB) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBAdminPassword) DeepCopyInto(out *CDBAdminPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminPassword. -func (in *CDBAdminPassword) DeepCopy() *CDBAdminPassword { - if in == nil { - return nil - } - out := new(CDBAdminPassword) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBAdminUser) DeepCopyInto(out *CDBAdminUser) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminUser. -func (in *CDBAdminUser) DeepCopy() *CDBAdminUser { - if in == nil { - return nil - } - out := new(CDBAdminUser) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBList) DeepCopyInto(out *CDBList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]CDB, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBList. -func (in *CDBList) DeepCopy() *CDBList { - if in == nil { - return nil - } - out := new(CDBList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CDBList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBPRIVKEY) DeepCopyInto(out *CDBPRIVKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBPRIVKEY. -func (in *CDBPRIVKEY) DeepCopy() *CDBPRIVKEY { - if in == nil { - return nil - } - out := new(CDBPRIVKEY) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBPUBKEY) DeepCopyInto(out *CDBPUBKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBPUBKEY. -func (in *CDBPUBKEY) DeepCopy() *CDBPUBKEY { - if in == nil { - return nil - } - out := new(CDBPUBKEY) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBSecret) DeepCopyInto(out *CDBSecret) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSecret. -func (in *CDBSecret) DeepCopy() *CDBSecret { - if in == nil { - return nil - } - out := new(CDBSecret) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBSpec) DeepCopyInto(out *CDBSpec) { - *out = *in - out.SysAdminPwd = in.SysAdminPwd - out.CDBAdminUser = in.CDBAdminUser - out.CDBAdminPwd = in.CDBAdminPwd - out.CDBTlsKey = in.CDBTlsKey - out.CDBTlsCrt = in.CDBTlsCrt - out.ORDSPwd = in.ORDSPwd - out.WebServerUser = in.WebServerUser - out.WebServerPwd = in.WebServerPwd - if in.NodeSelector != nil { - in, out := &in.NodeSelector, &out.NodeSelector - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - out.CDBPubKey = in.CDBPubKey - out.CDBPriKey = in.CDBPriKey -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSpec. -func (in *CDBSpec) DeepCopy() *CDBSpec { - if in == nil { - return nil - } - out := new(CDBSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBStatus) DeepCopyInto(out *CDBStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBStatus. -func (in *CDBStatus) DeepCopy() *CDBStatus { - if in == nil { - return nil - } - out := new(CDBStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBSysAdminPassword) DeepCopyInto(out *CDBSysAdminPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSysAdminPassword. -func (in *CDBSysAdminPassword) DeepCopy() *CDBSysAdminPassword { - if in == nil { - return nil - } - out := new(CDBSysAdminPassword) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBTLSCRT) DeepCopyInto(out *CDBTLSCRT) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSCRT. -func (in *CDBTLSCRT) DeepCopy() *CDBTLSCRT { - if in == nil { - return nil - } - out := new(CDBTLSCRT) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBTLSKEY) DeepCopyInto(out *CDBTLSKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSKEY. -func (in *CDBTLSKEY) DeepCopy() *CDBTLSKEY { - if in == nil { - return nil - } - out := new(CDBTLSKEY) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CatalogSpec) DeepCopyInto(out *CatalogSpec) { *out = *in @@ -1703,22 +1470,6 @@ func (in *KMSDetailsStatus) DeepCopy() *KMSDetailsStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ORDSPassword. -func (in *ORDSPassword) DeepCopy() *ORDSPassword { - if in == nil { - return nil - } - out := new(ORDSPassword) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OciAcdSpec) DeepCopyInto(out *OciAcdSpec) { *out = *in @@ -1987,65 +1738,6 @@ func (in *OracleRestDataServiceStatus) DeepCopy() *OracleRestDataServiceStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDB) DeepCopyInto(out *PDB) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDB. -func (in *PDB) DeepCopy() *PDB { - if in == nil { - return nil - } - out := new(PDB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PDB) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBAdminName) DeepCopyInto(out *PDBAdminName) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminName. -func (in *PDBAdminName) DeepCopy() *PDBAdminName { - if in == nil { - return nil - } - out := new(PDBAdminName) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBAdminPassword) DeepCopyInto(out *PDBAdminPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminPassword. -func (in *PDBAdminPassword) DeepCopy() *PDBAdminPassword { - if in == nil { - return nil - } - out := new(PDBAdminPassword) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { *out = *in @@ -2157,204 +1849,6 @@ func (in *PDBDetailsStatus) DeepCopy() *PDBDetailsStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBList) DeepCopyInto(out *PDBList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PDB, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBList. -func (in *PDBList) DeepCopy() *PDBList { - if in == nil { - return nil - } - out := new(PDBList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PDBList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBPRIVKEY) DeepCopyInto(out *PDBPRIVKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBPRIVKEY. -func (in *PDBPRIVKEY) DeepCopy() *PDBPRIVKEY { - if in == nil { - return nil - } - out := new(PDBPRIVKEY) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBPUBKEY) DeepCopyInto(out *PDBPUBKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBPUBKEY. -func (in *PDBPUBKEY) DeepCopy() *PDBPUBKEY { - if in == nil { - return nil - } - out := new(PDBPUBKEY) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBSecret) DeepCopyInto(out *PDBSecret) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSecret. -func (in *PDBSecret) DeepCopy() *PDBSecret { - if in == nil { - return nil - } - out := new(PDBSecret) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBSpec) DeepCopyInto(out *PDBSpec) { - *out = *in - out.PDBTlsKey = in.PDBTlsKey - out.PDBTlsCrt = in.PDBTlsCrt - out.PDBTlsCat = in.PDBTlsCat - out.AdminName = in.AdminName - out.AdminPwd = in.AdminPwd - out.WebServerUsr = in.WebServerUsr - out.WebServerPwd = in.WebServerPwd - if in.ReuseTempFile != nil { - in, out := &in.ReuseTempFile, &out.ReuseTempFile - *out = new(bool) - **out = **in - } - if in.UnlimitedStorage != nil { - in, out := &in.UnlimitedStorage, &out.UnlimitedStorage - *out = new(bool) - **out = **in - } - if in.AsClone != nil { - in, out := &in.AsClone, &out.AsClone - *out = new(bool) - **out = **in - } - if in.TDEImport != nil { - in, out := &in.TDEImport, &out.TDEImport - *out = new(bool) - **out = **in - } - if in.TDEExport != nil { - in, out := &in.TDEExport, &out.TDEExport - *out = new(bool) - **out = **in - } - out.TDEPassword = in.TDEPassword - out.TDESecret = in.TDESecret - if in.GetScript != nil { - in, out := &in.GetScript, &out.GetScript - *out = new(bool) - **out = **in - } - out.PDBPubKey = in.PDBPubKey - out.PDBPriKey = in.PDBPriKey -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSpec. -func (in *PDBSpec) DeepCopy() *PDBSpec { - if in == nil { - return nil - } - out := new(PDBSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBStatus) DeepCopyInto(out *PDBStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBStatus. -func (in *PDBStatus) DeepCopy() *PDBStatus { - if in == nil { - return nil - } - out := new(PDBStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBTLSCAT) DeepCopyInto(out *PDBTLSCAT) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCAT. -func (in *PDBTLSCAT) DeepCopy() *PDBTLSCAT { - if in == nil { - return nil - } - out := new(PDBTLSCAT) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBTLSCRT) DeepCopyInto(out *PDBTLSCRT) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCRT. -func (in *PDBTLSCRT) DeepCopy() *PDBTLSCRT { - if in == nil { - return nil - } - out := new(PDBTLSCRT) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBTLSKEY) DeepCopyInto(out *PDBTLSKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSKEY. -func (in *PDBTLSKEY) DeepCopy() *PDBTLSKEY { - if in == nil { - return nil - } - out := new(PDBTLSKEY) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PasswordSpec) DeepCopyInto(out *PasswordSpec) { *out = *in @@ -2920,38 +2414,6 @@ func (in *SourceSpec) DeepCopy() *SourceSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TDEPwd) DeepCopyInto(out *TDEPwd) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDEPwd. -func (in *TDEPwd) DeepCopy() *TDEPwd { - if in == nil { - return nil - } - out := new(TDEPwd) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TDESecret) DeepCopyInto(out *TDESecret) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDESecret. -func (in *TDESecret) DeepCopy() *TDESecret { - if in == nil { - return nil - } - out := new(TDESecret) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in @@ -3024,67 +2486,3 @@ func (in *WalletSpec) DeepCopy() *WalletSpec { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerPassword) DeepCopyInto(out *WebServerPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPassword. -func (in *WebServerPassword) DeepCopy() *WebServerPassword { - if in == nil { - return nil - } - out := new(WebServerPassword) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerPasswordPDB) DeepCopyInto(out *WebServerPasswordPDB) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPasswordPDB. -func (in *WebServerPasswordPDB) DeepCopy() *WebServerPasswordPDB { - if in == nil { - return nil - } - out := new(WebServerPasswordPDB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerUser) DeepCopyInto(out *WebServerUser) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUser. -func (in *WebServerUser) DeepCopy() *WebServerUser { - if in == nil { - return nil - } - out := new(WebServerUser) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerUserPDB) DeepCopyInto(out *WebServerUserPDB) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUserPDB. -func (in *WebServerUserPDB) DeepCopy() *WebServerUserPDB { - if in == nil { - return nil - } - out := new(WebServerUserPDB) - in.DeepCopyInto(out) - return out -} diff --git a/apis/database/v4/cdb_types.go b/apis/database/v4/cdb_types.go deleted file mode 100644 index ce3f6f28..00000000 --- a/apis/database/v4/cdb_types.go +++ /dev/null @@ -1,191 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// CDBSpec defines the desired state of CDB -type CDBSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Name of the CDB - CDBName string `json:"cdbName,omitempty"` - // Name of the CDB Service - ServiceName string `json:"serviceName,omitempty"` - - // Password for the CDB System Administrator - SysAdminPwd CDBSysAdminPassword `json:"sysAdminPwd,omitempty"` - // User in the root container with sysdba priviledges to manage PDB lifecycle - CDBAdminUser CDBAdminUser `json:"cdbAdminUser,omitempty"` - // Password for the CDB Administrator to manage PDB lifecycle - CDBAdminPwd CDBAdminPassword `json:"cdbAdminPwd,omitempty"` - - CDBTlsKey CDBTLSKEY `json:"cdbTlsKey,omitempty"` - CDBTlsCrt CDBTLSCRT `json:"cdbTlsCrt,omitempty"` - - // Password for user ORDS_PUBLIC_USER - ORDSPwd ORDSPassword `json:"ordsPwd,omitempty"` - // ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. - ORDSPort int `json:"ordsPort,omitempty"` - // ORDS Image Name - ORDSImage string `json:"ordsImage,omitempty"` - // The name of the image pull secret in case of a private docker repository. - ORDSImagePullSecret string `json:"ordsImagePullSecret,omitempty"` - // ORDS Image Pull Policy - // +kubebuilder:validation:Enum=Always;Never - ORDSImagePullPolicy string `json:"ordsImagePullPolicy,omitempty"` - // Number of ORDS Containers to create - Replicas int `json:"replicas,omitempty"` - // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - WebServerUser WebServerUser `json:"webServerUser,omitempty"` - // Password for the Web Server User - WebServerPwd WebServerPassword `json:"webServerPwd,omitempty"` - // Name of the DB server - DBServer string `json:"dbServer,omitempty"` - // DB server port - DBPort int `json:"dbPort,omitempty"` - // Node Selector for running the Pod - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - DeletePDBCascade bool `json:"deletePdbCascade,omitempty"` - DBTnsurl string `json:"dbTnsurl,omitempty"` - CDBPubKey CDBPUBKEY `json:"cdbOrdsPubKey,omitempty"` - CDBPriKey CDBPRIVKEY `json:"cdbOrdsPrvKey,omitempty"` -} - -// CDBSecret defines the secretName -type CDBSecret struct { - SecretName string `json:"secretName"` - Key string `json:"key"` -} - -// CDBSysAdminPassword defines the secret containing SysAdmin Password mapped to key 'sysAdminPwd' for CDB -type CDBSysAdminPassword struct { - Secret CDBSecret `json:"secret"` -} - -// CDBAdminUser defines the secret containing CDB Administrator User mapped to key 'cdbAdminUser' to manage PDB lifecycle -type CDBAdminUser struct { - Secret CDBSecret `json:"secret"` -} - -// CDBAdminPassword defines the secret containing CDB Administrator Password mapped to key 'cdbAdminPwd' to manage PDB lifecycle -type CDBAdminPassword struct { - Secret CDBSecret `json:"secret"` -} - -// ORDSPassword defines the secret containing ORDS_PUBLIC_USER Password mapped to key 'ordsPwd' -type ORDSPassword struct { - Secret CDBSecret `json:"secret"` -} - -// WebServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle -type WebServerUser struct { - Secret CDBSecret `json:"secret"` -} - -// WebServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle -type WebServerPassword struct { - Secret CDBSecret `json:"secret"` -} - -type CDBTLSKEY struct { - Secret CDBSecret `json:"secret"` -} - -type CDBTLSCRT struct { - Secret CDBSecret `json:"secret"` -} - -type CDBPUBKEY struct { - Secret CDBSecret `json:"secret"` -} - -type CDBPRIVKEY struct { - Secret CDBSecret `json:"secret"` -} - -// CDBStatus defines the observed state of CDB -type CDBStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Phase of the CDB Resource - Phase string `json:"phase"` - // CDB Resource Status - Status bool `json:"status"` - // Message - Msg string `json:"msg,omitempty"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" -// +kubebuilder:printcolumn:JSONPath=".spec.dbServer",name="DB Server",type="string",description=" Name of the DB Server" -// +kubebuilder:printcolumn:JSONPath=".spec.dbPort",name="DB Port",type="integer",description="DB server port" -// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="Replicas",type="integer",description="Replicas" -// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the CDB Resource" -// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" -// +kubebuilder:printcolumn:JSONPath=".spec.dbTnsurl",name="TNS STRING",type="string",description=" string of the tnsalias" -// +kubebuilder:resource:path=cdbs,scope=Namespaced -// +kubebuilder:storageversion - -// CDB is the Schema for the cdbs API -type CDB struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec CDBSpec `json:"spec,omitempty"` - Status CDBStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// CDBList contains a list of CDB -type CDBList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []CDB `json:"items"` -} - -func init() { - SchemeBuilder.Register(&CDB{}, &CDBList{}) -} diff --git a/apis/database/v4/cdb_webhook.go b/apis/database/v4/cdb_webhook.go deleted file mode 100644 index e736ece1..00000000 --- a/apis/database/v4/cdb_webhook.go +++ /dev/null @@ -1,229 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - "context" - "reflect" - "strings" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// log is for logging in this package. -var cdblog = logf.Log.WithName("cdb-webhook") - -func (r *CDB) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - WithDefaulter(r). - WithValidator(r). - Complete() -} - -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-cdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v4,name=mcdb.kb.io,admissionReviewVersions={v1,v1beta1} - -var _ webhook.CustomDefaulter = &CDB{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *CDB) Default(ctx context.Context, obj runtime.Object) error { - cdblog.Info("Setting default values in CDB spec for : " + r.Name) - - if r.Spec.ORDSPort == 0 { - r.Spec.ORDSPort = 8888 - } - - if r.Spec.Replicas == 0 { - r.Spec.Replicas = 1 - } - - return nil -} - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:path=/validate-database-oracle-com-v4-cdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v4,name=vcdb.kb.io,admissionReviewVersions={v1,v1beta1} - -var _ webhook.CustomValidator = &CDB{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - cdblog.Info("ValidateCreate", "name", r.Name) - - var allErrs field.ErrorList - - if r.Spec.ServiceName == "" && r.Spec.DBServer != "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("serviceName"), "Please specify CDB Service name")) - } - - if reflect.ValueOf(r.Spec.CDBTlsKey).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbTlsKey"), "Please specify CDB Tls key(secret)")) - } - - if reflect.ValueOf(r.Spec.CDBTlsCrt).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbTlsCrt"), "Please specify CDB Tls Certificate(secret)")) - } - - if reflect.ValueOf(r.Spec.CDBPriKey).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("CDBPriKey"), "Please specify CDB CDBPriKey(secret)")) - } - - /*if r.Spec.SCANName == "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("scanName"), "Please specify SCAN Name for CDB")) - }*/ - - if (r.Spec.DBServer == "" && r.Spec.DBTnsurl == "") || (r.Spec.DBServer != "" && r.Spec.DBTnsurl != "") { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbServer"), "Please specify Database Server Name/IP Address or tnsalias string")) - } - - if r.Spec.DBTnsurl != "" && (r.Spec.DBServer != "" || r.Spec.DBPort != 0 || r.Spec.ServiceName != "") { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbServer"), "DBtnsurl is orthogonal to (DBServer,DBport,Services)")) - } - - if r.Spec.DBPort == 0 && r.Spec.DBServer != "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbPort"), "Please specify DB Server Port")) - } - if r.Spec.DBPort < 0 && r.Spec.DBServer != "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbPort"), "Please specify a valid DB Server Port")) - } - if r.Spec.ORDSPort < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("ordsPort"), "Please specify a valid ORDS Port")) - } - if r.Spec.Replicas < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("replicas"), "Please specify a valid value for Replicas")) - } - if r.Spec.ORDSImage == "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("ordsImage"), "Please specify name of ORDS Image to be used")) - } - if reflect.ValueOf(r.Spec.CDBAdminUser).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbAdminUser"), "Please specify user in the root container with sysdba priviledges to manage PDB lifecycle")) - } - if reflect.ValueOf(r.Spec.CDBAdminPwd).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbAdminPwd"), "Please specify password for the CDB Administrator to manage PDB lifecycle")) - } - if reflect.ValueOf(r.Spec.ORDSPwd).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("ordsPwd"), "Please specify password for user ORDS_PUBLIC_USER")) - } - if reflect.ValueOf(r.Spec.WebServerUser).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("webServerUser"), "Please specify the Web Server User having SQL Administrator role")) - } - if reflect.ValueOf(r.Spec.WebServerPwd).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("webServerPwd"), "Please specify password for the Web Server User having SQL Administrator role")) - } - if len(allErrs) == 0 { - return nil, nil - } - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "CDB"}, - r.Name, allErrs) -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { - cdblog.Info("validate update", "name", r.Name) - - isCDBMarkedToBeDeleted := r.GetDeletionTimestamp() != nil - if isCDBMarkedToBeDeleted { - return nil, nil - } - - var allErrs field.ErrorList - - // Check for updation errors - oldCDB, ok := old.(*CDB) - if !ok { - return nil, nil - } - - if r.Spec.DBPort < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbPort"), "Please specify a valid DB Server Port")) - } - if r.Spec.ORDSPort < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("ordsPort"), "Please specify a valid ORDS Port")) - } - if r.Spec.Replicas < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("replicas"), "Please specify a valid value for Replicas")) - } - if !strings.EqualFold(oldCDB.Spec.ServiceName, r.Spec.ServiceName) { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("replicas"), "cannot be changed")) - } - - if len(allErrs) == 0 { - return nil, nil - } - - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "CDB"}, - r.Name, allErrs) -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - cdblog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. - return nil, nil -} diff --git a/apis/database/v4/pdb_types.go b/apis/database/v4/pdb_types.go deleted file mode 100644 index 16021f12..00000000 --- a/apis/database/v4/pdb_types.go +++ /dev/null @@ -1,237 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// PDBSpec defines the desired state of PDB -type PDBSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - PDBTlsKey PDBTLSKEY `json:"pdbTlsKey,omitempty"` - PDBTlsCrt PDBTLSCRT `json:"pdbTlsCrt,omitempty"` - PDBTlsCat PDBTLSCAT `json:"pdbTlsCat,omitempty"` - - // CDB Namespace - CDBNamespace string `json:"cdbNamespace,omitempty"` - // Name of the CDB Custom Resource that runs the ORDS container - CDBResName string `json:"cdbResName,omitempty"` - // Name of the CDB - CDBName string `json:"cdbName,omitempty"` - // The name of the new PDB. Relevant for both Create and Plug Actions. - PDBName string `json:"pdbName,omitempty"` - // Name of the Source PDB from which to clone - SrcPDBName string `json:"srcPdbName,omitempty"` - // The administrator username for the new PDB. This property is required when the Action property is Create. - AdminName PDBAdminName `json:"adminName,omitempty"` - // The administrator password for the new PDB. This property is required when the Action property is Create. - AdminPwd PDBAdminPassword `json:"adminPwd,omitempty"` - // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - WebServerUsr WebServerUserPDB `json:"webServerUser,omitempty"` - // Password for the Web ServerPDB User - WebServerPwd WebServerPasswordPDB `json:"webServerPwd,omitempty"` - // Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. - FileNameConversions string `json:"fileNameConversions,omitempty"` - // This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. - SourceFileNameConversions string `json:"sourceFileNameConversions,omitempty"` - // XML metadata filename to be used for Plug or Unplug operations - XMLFileName string `json:"xmlFileName,omitempty"` - // To copy files or not while cloning a PDB - // +kubebuilder:validation:Enum=COPY;NOCOPY;MOVE - CopyAction string `json:"copyAction,omitempty"` - // Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). - // +kubebuilder:validation:Enum=INCLUDING;KEEP - DropAction string `json:"dropAction,omitempty"` - // A Path specified for sparse clone snapshot copy. (Optional) - SparseClonePath string `json:"sparseClonePath,omitempty"` - // Whether to reuse temp file - ReuseTempFile *bool `json:"reuseTempFile,omitempty"` - // Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. - UnlimitedStorage *bool `json:"unlimitedStorage,omitempty"` - // Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. - AsClone *bool `json:"asClone,omitempty"` - // Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - TotalSize string `json:"totalSize,omitempty"` - // Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - TempSize string `json:"tempSize,omitempty"` - // TDE import for plug operations - TDEImport *bool `json:"tdeImport,omitempty"` - // TDE export for unplug operations - TDEExport *bool `json:"tdeExport,omitempty"` - // TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations - TDEPassword TDEPwd `json:"tdePassword,omitempty"` - // TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - TDEKeystorePath string `json:"tdeKeystorePath,omitempty"` - // TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - TDESecret TDESecret `json:"tdeSecret,omitempty"` - // Whether you need the script only or execute the script - GetScript *bool `json:"getScript,omitempty"` - // Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR. - // +kubebuilder:validation:Enum=Create;Clone;Plug;Unplug;Delete;Modify;Status;Map - Action string `json:"action"` - // Extra options for opening and closing a PDB - // +kubebuilder:validation:Enum=IMMEDIATE;NORMAL;READ ONLY;READ WRITE;RESTRICTED - ModifyOption string `json:"modifyOption,omitempty"` - // The target state of the PDB - // +kubebuilder:validation:Enum=OPEN;CLOSE - PDBState string `json:"pdbState,omitempty"` - // turn on the assertive approach to delete pdb resource - // kubectl delete pdb ..... automatically triggers the pluggable database - // deletion - AssertivePdbDeletion bool `json:"assertivePdbDeletion,omitempty"` - PDBPubKey PDBPUBKEY `json:"pdbOrdsPubKey,omitempty"` - PDBPriKey PDBPRIVKEY `json:"pdbOrdsPrvKey,omitempty"` -} - -// PDBAdminName defines the secret containing Sys Admin User mapped to key 'adminName' for PDB -type PDBAdminName struct { - Secret PDBSecret `json:"secret"` -} - -// PDBAdminPassword defines the secret containing Sys Admin Password mapped to key 'adminPwd' for PDB -type PDBAdminPassword struct { - Secret PDBSecret `json:"secret"` -} - -// TDEPwd defines the secret containing TDE Wallet Password mapped to key 'tdePassword' for PDB -type TDEPwd struct { - Secret PDBSecret `json:"secret"` -} - -// TDESecret defines the secret containing TDE Secret to key 'tdeSecret' for PDB -type TDESecret struct { - Secret PDBSecret `json:"secret"` -} - -// WebServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle - -type WebServerUserPDB struct { - Secret PDBSecret `json:"secret"` -} - -// WebServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle -type WebServerPasswordPDB struct { - Secret PDBSecret `json:"secret"` -} - -// PDBSecret defines the secretName -type PDBSecret struct { - SecretName string `json:"secretName"` - Key string `json:"key"` -} - -type PDBTLSKEY struct { - Secret PDBSecret `json:"secret"` -} - -type PDBTLSCRT struct { - Secret PDBSecret `json:"secret"` -} - -type PDBTLSCAT struct { - Secret PDBSecret `json:"secret"` -} - -// PDBStatus defines the observed state of PDB -type PDBStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // PDB Connect String - ConnString string `json:"connString,omitempty"` - // Phase of the PDB Resource - Phase string `json:"phase"` - // PDB Resource Status - Status bool `json:"status"` - // Total size of the PDB - TotalSize string `json:"totalSize,omitempty"` - // Open mode of the PDB - OpenMode string `json:"openMode,omitempty"` - // Modify Option of the PDB - ModifyOption string `json:"modifyOption,omitempty"` - // Message - Msg string `json:"msg,omitempty"` - // Last Completed Action - Action string `json:"action,omitempty"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" -// +kubebuilder:printcolumn:JSONPath=".spec.pdbName",name="PDB Name",type="string",description="Name of the PDB" -// +kubebuilder:printcolumn:JSONPath=".status.openMode",name="PDB State",type="string",description="PDB Open Mode" -// +kubebuilder:printcolumn:JSONPath=".status.totalSize",name="PDB Size",type="string",description="Total Size of the PDB" -// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the PDB Resource" -// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" -// +kubebuilder:printcolumn:JSONPath=".status.connString",name="Connect_String",type="string",description="The connect string to be used" -// +kubebuilder:resource:path=pdbs,scope=Namespaced -// +kubebuilder:storageversion - -// PDB is the Schema for the pdbs API -type PDB struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec PDBSpec `json:"spec,omitempty"` - Status PDBStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// PDBList contains a list of PDB -type PDBList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []PDB `json:"items"` -} - -type PDBPUBKEY struct { - Secret PDBSecret `json:"secret"` -} - -type PDBPRIVKEY struct { - Secret PDBSecret `json:"secret"` -} - -func init() { - SchemeBuilder.Register(&PDB{}, &PDBList{}) -} diff --git a/apis/database/v4/pdb_webhook.go b/apis/database/v4/pdb_webhook.go deleted file mode 100644 index d447fbd8..00000000 --- a/apis/database/v4/pdb_webhook.go +++ /dev/null @@ -1,374 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -/* MODIFIED (MM/DD/YY) -** rcitton 07/14/22 - 33822886 - */ - -package v4 - -import ( - "context" - "reflect" - "strconv" - "strings" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// log is for logging in this package. -var pdblog = logf.Log.WithName("pdb-webhook") - -func (r *PDB) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - WithDefaulter(r). - WithValidator(r). - Complete() -} - -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-pdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v4,name=mpdb.kb.io,admissionReviewVersions={v1,v1beta1} - -var _ webhook.CustomDefaulter = &PDB{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *PDB) Default(ctx context.Context, obj runtime.Object) error { - pdblog.Info("Setting default values in PDB spec for : " + r.Name) - - action := strings.ToUpper(r.Spec.Action) - - if action == "DELETE" { - if r.Spec.DropAction == "" { - r.Spec.DropAction = "INCLUDING" - pdblog.Info(" - dropAction : INCLUDING") - } - } else if action != "MODIFY" && action != "STATUS" { - if r.Spec.ReuseTempFile == nil { - r.Spec.ReuseTempFile = new(bool) - *r.Spec.ReuseTempFile = true - pdblog.Info(" - reuseTempFile : " + strconv.FormatBool(*(r.Spec.ReuseTempFile))) - } - if r.Spec.UnlimitedStorage == nil { - r.Spec.UnlimitedStorage = new(bool) - *r.Spec.UnlimitedStorage = true - pdblog.Info(" - unlimitedStorage : " + strconv.FormatBool(*(r.Spec.UnlimitedStorage))) - } - if r.Spec.TDEImport == nil { - r.Spec.TDEImport = new(bool) - *r.Spec.TDEImport = false - pdblog.Info(" - tdeImport : " + strconv.FormatBool(*(r.Spec.TDEImport))) - } - if r.Spec.TDEExport == nil { - r.Spec.TDEExport = new(bool) - *r.Spec.TDEExport = false - pdblog.Info(" - tdeExport : " + strconv.FormatBool(*(r.Spec.TDEExport))) - } - if r.Spec.AsClone == nil { - r.Spec.AsClone = new(bool) - *r.Spec.AsClone = false - pdblog.Info(" - asClone : " + strconv.FormatBool(*(r.Spec.AsClone))) - } - - } - - if r.Spec.GetScript == nil { - r.Spec.GetScript = new(bool) - *r.Spec.GetScript = false - pdblog.Info(" - getScript : " + strconv.FormatBool(*(r.Spec.GetScript))) - } - - return nil -} - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:path=/validate-database-oracle-com-v4-pdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v4,name=vpdb.kb.io,admissionReviewVersions={v1,v1beta1} - -var _ webhook.CustomValidator = &PDB{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - pdblog.Info("ValidateCreate-Validating PDB spec for : " + r.Name) - - var allErrs field.ErrorList - - r.validateCommon(&allErrs) - - r.validateAction(&allErrs) - - action := strings.ToUpper(r.Spec.Action) - - if len(allErrs) == 0 { - pdblog.Info("PDB Resource : " + r.Name + " successfully validated for Action : " + action) - return nil, nil - } - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "PDB"}, - r.Name, allErrs) -} - -// Validate Action for required parameters -func (r *PDB) validateAction(allErrs *field.ErrorList) { - action := strings.ToUpper(r.Spec.Action) - - pdblog.Info("Valdiating PDB Resource Action : " + action) - - if reflect.ValueOf(r.Spec.PDBTlsKey).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbTlsKey"), "Please specify PDB Tls Key(secret)")) - } - - if reflect.ValueOf(r.Spec.PDBTlsCrt).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbTlsCrt"), "Please specify PDB Tls Certificate(secret)")) - } - - if reflect.ValueOf(r.Spec.PDBTlsCat).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbTlsCat"), "Please specify PDB Tls Certificate Authority(secret)")) - } - if reflect.ValueOf(r.Spec.PDBPriKey).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbOrdsPrvKey"), "Please specify PDB Tls Certificate Authority(secret)")) - } - - switch action { - case "DELETE": - /* BUG 36752336 - LREST OPERATOR - DELETE NON-EXISTENT PDB SHOWS LRPDB CREATED MESSAGE */ - if r.Status.OpenMode == "READ WRITE" { - pdblog.Info("Cannot delete: pdb is open ") - *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+r.Spec.PDBName+" "+r.Status.OpenMode)) - } - r.CheckObjExistence("DELETE", allErrs, r) - case "CREATE": - if reflect.ValueOf(r.Spec.AdminName).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("adminName"), "Please specify PDB System Administrator user")) - } - if reflect.ValueOf(r.Spec.AdminPwd).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("adminPwd"), "Please specify PDB System Administrator Password")) - } - if reflect.ValueOf(r.Spec.WebServerUsr).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("WebServerUser"), "Please specify the http webServerUser")) - } - if reflect.ValueOf(r.Spec.WebServerPwd).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("webServerPwd"), "Please specify the http webserverPassword")) - } - - if r.Spec.FileNameConversions == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("fileNameConversions"), "Please specify a value for fileNameConversions. Values can be a filename convert pattern or NONE")) - } - if r.Spec.TotalSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("totalSize"), "When the storage is not UNLIMITED the Total Size must be specified")) - } - if r.Spec.TempSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) - } - if *(r.Spec.TDEImport) { - r.validateTDEInfo(allErrs) - } - case "CLONE": - // Sample Err: The PDB "pdb1-clone" is invalid: spec.srcPdbName: Required value: Please specify source PDB for Cloning - if r.Spec.SrcPDBName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("srcPdbName"), "Please specify source PDB name for Cloning")) - } - if r.Spec.TotalSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("totalSize"), "When the storage is not UNLIMITED the Total Size must be specified")) - } - if r.Spec.TempSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) - } - /* We don't need this check as ords open the pdb before cloninig */ - /* - if r.Status.OpenMode == "MOUNTED" { - pdblog.Info("Cannot clone: pdb is mount ") - *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+r.Spec.PDBName+" "+r.Status.OpenMode)) - } - */ - case "PLUG": - if r.Spec.XMLFileName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("xmlFileName"), "Please specify XML metadata filename")) - } - if r.Spec.FileNameConversions == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("fileNameConversions"), "Please specify a value for fileNameConversions. Values can be a filename convert pattern or NONE")) - } - if r.Spec.SourceFileNameConversions == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("sourceFileNameConversions"), "Please specify a value for sourceFileNameConversions. Values can be a filename convert pattern or NONE")) - } - if r.Spec.CopyAction == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("copyAction"), "Please specify a value for copyAction. Values can be COPY, NOCOPY or MOVE")) - } - if *(r.Spec.TDEImport) { - r.validateTDEInfo(allErrs) - } - case "UNPLUG": - if r.Spec.XMLFileName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("xmlFileName"), "Please specify XML metadata filename")) - } - if *(r.Spec.TDEExport) { - r.validateTDEInfo(allErrs) - } - if r.Status.OpenMode == "READ WRITE" { - pdblog.Info("Cannot unplug: pdb is open ") - *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+r.Spec.PDBName+" "+r.Status.OpenMode)) - } - r.CheckObjExistence("UNPLUG", allErrs, r) - case "MODIFY": - if r.Spec.PDBState == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbState"), "Please specify target state of PDB")) - } - if r.Spec.ModifyOption == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("modifyOption"), "Please specify an option for opening/closing a PDB")) - } - r.CheckObjExistence("MODIY", allErrs, r) - } -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { - pdblog.Info("ValidateUpdate-Validating PDB spec for : " + r.Name) - - isPDBMarkedToBeDeleted := r.GetDeletionTimestamp() != nil - if isPDBMarkedToBeDeleted { - return nil, nil - } - - var allErrs field.ErrorList - action := strings.ToUpper(r.Spec.Action) - - // If PDB CR has been created and in Ready state, only allow updates if the "action" value has changed as well - if (r.Status.Phase == "Ready") && (r.Status.Action != "MODIFY") && (r.Status.Action != "STATUS") && (r.Status.Action == action) { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("action"), "New action also needs to be specified after PDB is in Ready state")) - } else { - - // Check Common Validations - r.validateCommon(&allErrs) - - // Validate required parameters for Action specified - r.validateAction(&allErrs) - - // Check TDE requirements - if (action != "DELETE") && (action != "MODIFY") && (action != "STATUS") && (*(r.Spec.TDEImport) || *(r.Spec.TDEExport)) { - r.validateTDEInfo(&allErrs) - } - } - - if len(allErrs) == 0 { - return nil, nil - } - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "PDB"}, - r.Name, allErrs) -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - pdblog.Info("ValidateDelete-Validating PDB spec for : " + r.Name) - - // TODO(user): fill in your validation logic upon object deletion. - return nil, nil -} - -// Validate common specs needed for all PDB Actions -func (r *PDB) validateCommon(allErrs *field.ErrorList) { - pdblog.Info("validateCommon", "name", r.Name) - - if r.Spec.Action == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("action"), "Please specify PDB operation to be performed")) - } - if r.Spec.CDBResName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("cdbResName"), "Please specify the name of the CDB Kubernetes resource to use for PDB operations")) - } - if r.Spec.PDBName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbName"), "Please specify name of the PDB to be created")) - } -} - -// Validate TDE information for Create, Plug and Unplug Actions -func (r *PDB) validateTDEInfo(allErrs *field.ErrorList) { - pdblog.Info("validateTDEInfo", "name", r.Name) - - if reflect.ValueOf(r.Spec.TDEPassword).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tdePassword"), "Please specify a value for tdePassword.")) - } - if r.Spec.TDEKeystorePath == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tdeKeystorePath"), "Please specify a value for tdeKeystorePath.")) - } - if reflect.ValueOf(r.Spec.TDESecret).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tdeSecret"), "Please specify a value for tdeSecret.")) - } - -} - -func (r *PDB) CheckObjExistence(action string, allErrs *field.ErrorList, pdb *PDB) { - /* BUG 36752465 - lrest operator - open non-existent pdb creates a lrpdb with status failed */ - pdblog.Info("Action [" + action + "] checkin " + pdb.Spec.PDBName + " existence") - if pdb.Status.OpenMode == "" { - *allErrs = append(*allErrs, field.NotFound(field.NewPath("Spec").Child("PDBName"), " "+pdb.Spec.PDBName+" does not exist : action "+action+" failure")) - - } -} diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 75ca6734..e58351a8 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -794,239 +794,6 @@ func (in *Backupconfig) DeepCopy() *Backupconfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDB) DeepCopyInto(out *CDB) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDB. -func (in *CDB) DeepCopy() *CDB { - if in == nil { - return nil - } - out := new(CDB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CDB) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBAdminPassword) DeepCopyInto(out *CDBAdminPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminPassword. -func (in *CDBAdminPassword) DeepCopy() *CDBAdminPassword { - if in == nil { - return nil - } - out := new(CDBAdminPassword) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBAdminUser) DeepCopyInto(out *CDBAdminUser) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBAdminUser. -func (in *CDBAdminUser) DeepCopy() *CDBAdminUser { - if in == nil { - return nil - } - out := new(CDBAdminUser) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBList) DeepCopyInto(out *CDBList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]CDB, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBList. -func (in *CDBList) DeepCopy() *CDBList { - if in == nil { - return nil - } - out := new(CDBList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CDBList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBPRIVKEY) DeepCopyInto(out *CDBPRIVKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBPRIVKEY. -func (in *CDBPRIVKEY) DeepCopy() *CDBPRIVKEY { - if in == nil { - return nil - } - out := new(CDBPRIVKEY) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBPUBKEY) DeepCopyInto(out *CDBPUBKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBPUBKEY. -func (in *CDBPUBKEY) DeepCopy() *CDBPUBKEY { - if in == nil { - return nil - } - out := new(CDBPUBKEY) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBSecret) DeepCopyInto(out *CDBSecret) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSecret. -func (in *CDBSecret) DeepCopy() *CDBSecret { - if in == nil { - return nil - } - out := new(CDBSecret) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBSpec) DeepCopyInto(out *CDBSpec) { - *out = *in - out.SysAdminPwd = in.SysAdminPwd - out.CDBAdminUser = in.CDBAdminUser - out.CDBAdminPwd = in.CDBAdminPwd - out.CDBTlsKey = in.CDBTlsKey - out.CDBTlsCrt = in.CDBTlsCrt - out.ORDSPwd = in.ORDSPwd - out.WebServerUser = in.WebServerUser - out.WebServerPwd = in.WebServerPwd - if in.NodeSelector != nil { - in, out := &in.NodeSelector, &out.NodeSelector - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - out.CDBPubKey = in.CDBPubKey - out.CDBPriKey = in.CDBPriKey -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSpec. -func (in *CDBSpec) DeepCopy() *CDBSpec { - if in == nil { - return nil - } - out := new(CDBSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBStatus) DeepCopyInto(out *CDBStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBStatus. -func (in *CDBStatus) DeepCopy() *CDBStatus { - if in == nil { - return nil - } - out := new(CDBStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBSysAdminPassword) DeepCopyInto(out *CDBSysAdminPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBSysAdminPassword. -func (in *CDBSysAdminPassword) DeepCopy() *CDBSysAdminPassword { - if in == nil { - return nil - } - out := new(CDBSysAdminPassword) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBTLSCRT) DeepCopyInto(out *CDBTLSCRT) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSCRT. -func (in *CDBTLSCRT) DeepCopy() *CDBTLSCRT { - if in == nil { - return nil - } - out := new(CDBTLSCRT) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CDBTLSKEY) DeepCopyInto(out *CDBTLSKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CDBTLSKEY. -func (in *CDBTLSKEY) DeepCopy() *CDBTLSKEY { - if in == nil { - return nil - } - out := new(CDBTLSKEY) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CatalogSpec) DeepCopyInto(out *CatalogSpec) { *out = *in @@ -2770,22 +2537,6 @@ func (in *NetworkDetailSpec) DeepCopy() *NetworkDetailSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ORDSPassword) DeepCopyInto(out *ORDSPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ORDSPassword. -func (in *ORDSPassword) DeepCopy() *ORDSPassword { - if in == nil { - return nil - } - out := new(ORDSPassword) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OciAcdSpec) DeepCopyInto(out *OciAcdSpec) { *out = *in @@ -3583,65 +3334,6 @@ func (in *OrdsSrvsStatus) DeepCopy() *OrdsSrvsStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDB) DeepCopyInto(out *PDB) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDB. -func (in *PDB) DeepCopy() *PDB { - if in == nil { - return nil - } - out := new(PDB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PDB) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBAdminName) DeepCopyInto(out *PDBAdminName) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminName. -func (in *PDBAdminName) DeepCopy() *PDBAdminName { - if in == nil { - return nil - } - out := new(PDBAdminName) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBAdminPassword) DeepCopyInto(out *PDBAdminPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBAdminPassword. -func (in *PDBAdminPassword) DeepCopy() *PDBAdminPassword { - if in == nil { - return nil - } - out := new(PDBAdminPassword) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PDBConfig) DeepCopyInto(out *PDBConfig) { *out = *in @@ -3753,204 +3445,6 @@ func (in *PDBDetailsStatus) DeepCopy() *PDBDetailsStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBList) DeepCopyInto(out *PDBList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PDB, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBList. -func (in *PDBList) DeepCopy() *PDBList { - if in == nil { - return nil - } - out := new(PDBList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PDBList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBPRIVKEY) DeepCopyInto(out *PDBPRIVKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBPRIVKEY. -func (in *PDBPRIVKEY) DeepCopy() *PDBPRIVKEY { - if in == nil { - return nil - } - out := new(PDBPRIVKEY) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBPUBKEY) DeepCopyInto(out *PDBPUBKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBPUBKEY. -func (in *PDBPUBKEY) DeepCopy() *PDBPUBKEY { - if in == nil { - return nil - } - out := new(PDBPUBKEY) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBSecret) DeepCopyInto(out *PDBSecret) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSecret. -func (in *PDBSecret) DeepCopy() *PDBSecret { - if in == nil { - return nil - } - out := new(PDBSecret) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBSpec) DeepCopyInto(out *PDBSpec) { - *out = *in - out.PDBTlsKey = in.PDBTlsKey - out.PDBTlsCrt = in.PDBTlsCrt - out.PDBTlsCat = in.PDBTlsCat - out.AdminName = in.AdminName - out.AdminPwd = in.AdminPwd - out.WebServerUsr = in.WebServerUsr - out.WebServerPwd = in.WebServerPwd - if in.ReuseTempFile != nil { - in, out := &in.ReuseTempFile, &out.ReuseTempFile - *out = new(bool) - **out = **in - } - if in.UnlimitedStorage != nil { - in, out := &in.UnlimitedStorage, &out.UnlimitedStorage - *out = new(bool) - **out = **in - } - if in.AsClone != nil { - in, out := &in.AsClone, &out.AsClone - *out = new(bool) - **out = **in - } - if in.TDEImport != nil { - in, out := &in.TDEImport, &out.TDEImport - *out = new(bool) - **out = **in - } - if in.TDEExport != nil { - in, out := &in.TDEExport, &out.TDEExport - *out = new(bool) - **out = **in - } - out.TDEPassword = in.TDEPassword - out.TDESecret = in.TDESecret - if in.GetScript != nil { - in, out := &in.GetScript, &out.GetScript - *out = new(bool) - **out = **in - } - out.PDBPubKey = in.PDBPubKey - out.PDBPriKey = in.PDBPriKey -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBSpec. -func (in *PDBSpec) DeepCopy() *PDBSpec { - if in == nil { - return nil - } - out := new(PDBSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBStatus) DeepCopyInto(out *PDBStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBStatus. -func (in *PDBStatus) DeepCopy() *PDBStatus { - if in == nil { - return nil - } - out := new(PDBStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBTLSCAT) DeepCopyInto(out *PDBTLSCAT) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCAT. -func (in *PDBTLSCAT) DeepCopy() *PDBTLSCAT { - if in == nil { - return nil - } - out := new(PDBTLSCAT) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBTLSCRT) DeepCopyInto(out *PDBTLSCRT) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSCRT. -func (in *PDBTLSCRT) DeepCopy() *PDBTLSCRT { - if in == nil { - return nil - } - out := new(PDBTLSCRT) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PDBTLSKEY) DeepCopyInto(out *PDBTLSKEY) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PDBTLSKEY. -func (in *PDBTLSKEY) DeepCopy() *PDBTLSKEY { - if in == nil { - return nil - } - out := new(PDBTLSKEY) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PITSpec) DeepCopyInto(out *PITSpec) { *out = *in @@ -4811,38 +4305,6 @@ func (in *SourceSpec) DeepCopy() *SourceSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TDEPwd) DeepCopyInto(out *TDEPwd) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDEPwd. -func (in *TDEPwd) DeepCopy() *TDEPwd { - if in == nil { - return nil - } - out := new(TDEPwd) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TDESecret) DeepCopyInto(out *TDESecret) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TDESecret. -func (in *TDESecret) DeepCopy() *TDESecret { - if in == nil { - return nil - } - out := new(TDESecret) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TNSAdminSecret) DeepCopyInto(out *TNSAdminSecret) { *out = *in @@ -4994,67 +4456,3 @@ func (in *WebLrpdbServerUser) DeepCopy() *WebLrpdbServerUser { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerPassword) DeepCopyInto(out *WebServerPassword) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPassword. -func (in *WebServerPassword) DeepCopy() *WebServerPassword { - if in == nil { - return nil - } - out := new(WebServerPassword) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerPasswordPDB) DeepCopyInto(out *WebServerPasswordPDB) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerPasswordPDB. -func (in *WebServerPasswordPDB) DeepCopy() *WebServerPasswordPDB { - if in == nil { - return nil - } - out := new(WebServerPasswordPDB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerUser) DeepCopyInto(out *WebServerUser) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUser. -func (in *WebServerUser) DeepCopy() *WebServerUser { - if in == nil { - return nil - } - out := new(WebServerUser) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebServerUserPDB) DeepCopyInto(out *WebServerUserPDB) { - *out = *in - out.Secret = in.Secret -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebServerUserPDB. -func (in *WebServerUserPDB) DeepCopy() *WebServerUserPDB { - if in == nil { - return nil - } - out := new(WebServerUserPDB) - in.DeepCopyInto(out) - return out -} diff --git a/config/crd/bases/database.oracle.com_cdbs.yaml b/config/crd/bases/database.oracle.com_cdbs.yaml deleted file mode 100644 index 924946ee..00000000 --- a/config/crd/bases/database.oracle.com_cdbs.yaml +++ /dev/null @@ -1,492 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - name: cdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbOrdsPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbOrdsPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - ordsImage: - type: string - ordsImagePullPolicy: - enum: - - Always - - Never - type: string - ordsImagePullSecret: - type: string - ordsPort: - type: integer - ordsPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbOrdsPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbOrdsPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - ordsImage: - type: string - ordsImagePullPolicy: - enum: - - Always - - Never - type: string - ordsImagePullSecret: - type: string - ordsPort: - type: integer - ordsPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/config/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml deleted file mode 100644 index b2f37ac9..00000000 --- a/config/crd/bases/database.oracle.com_pdbs.yaml +++ /dev/null @@ -1,634 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - name: pdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - type: boolean - assertivePdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - type: string - pdbOrdsPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbOrdsPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbState: - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - type: object - status: - properties: - action: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - status: - type: boolean - totalSize: - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - type: boolean - assertivePdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: - type: boolean - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - type: string - pdbOrdsPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbOrdsPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbState: - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - type: string - required: - - action - type: object - status: - properties: - action: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: - type: string - status: - type: boolean - totalSize: - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 27229161..de38cc77 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -11,8 +11,6 @@ resources: - bases/database.oracle.com_autonomousdatabaserestores.yaml - bases/database.oracle.com_singleinstancedatabases.yaml - bases/database.oracle.com_shardingdatabases.yaml -- bases/database.oracle.com_pdbs.yaml -- bases/database.oracle.com_cdbs.yaml - bases/database.oracle.com_oraclerestdataservices.yaml - bases/database.oracle.com_autonomouscontainerdatabases.yaml - bases/database.oracle.com_dbcssystems.yaml @@ -32,8 +30,6 @@ patches: #- patches/webhook_in_provshards.yaml #- patches/webhook_in_singleinstancedatabases.yaml #- patches/webhook_in_shardingdatabases.yaml -#- patches/webhook_in_pdbs.yaml -#- patches/webhook_in_cdbs.yaml #- patches/webhook_in_oraclerestdataservices.yaml #- patches/webhook_in_dbcssystems.yaml #- patches/webhook_in_dataguardbrokers.yaml @@ -58,8 +54,6 @@ patches: #- patches/cainjection_in_ordssrvs.yaml - path: patches/cainjection_in_singleinstancedatabases.yaml - path: patches/cainjection_in_shardingdatabases.yaml -- path: patches/cainjection_in_pdbs.yaml -- path: patches/cainjection_in_cdbs.yaml - path: patches/cainjection_in_dbcssystems.yaml - path: patches/cainjection_in_autonomousdatabases.yaml - path: patches/cainjection_in_autonomousdatabasebackups.yaml @@ -80,8 +74,6 @@ patches: - path: patches/webhook_in_autonomouscontainerdatabases.yaml - path: patches/cainjection_in_singleinstancedatabases.yaml - path: patches/cainjection_in_shardingdatabases.yaml -- path: patches/cainjection_in_pdbs.yaml -- path: patches/cainjection_in_cdbs.yaml - path: patches/cainjection_in_dbcssystems.yaml - path: patches/cainjection_in_autonomousdatabases.yaml - path: patches/cainjection_in_autonomousdatabasebackups.yaml diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 74303bea..3f509a8c 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: basedb-operator-sa + newName: lin.ocir.io/intsanjaysingh/operator/dboperator_master_orahub + newTag: "250822" diff --git a/config/rbac/cdb_editor_role.yaml b/config/rbac/cdb_editor_role.yaml deleted file mode 100644 index 244ddff2..00000000 --- a/config/rbac/cdb_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit cdbs. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: cdb-editor-role -rules: -- apiGroups: - - database.oracle.com - resources: - - cdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - cdbs/status - verbs: - - get diff --git a/config/rbac/cdb_viewer_role.yaml b/config/rbac/cdb_viewer_role.yaml deleted file mode 100644 index 78a84283..00000000 --- a/config/rbac/cdb_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view cdbs. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: cdb-viewer-role -rules: -- apiGroups: - - database.oracle.com - resources: - - cdbs - verbs: - - get - - list - - watch -- apiGroups: - - database.oracle.com - resources: - - cdbs/status - verbs: - - get diff --git a/config/rbac/pdb_editor_role.yaml b/config/rbac/pdb_editor_role.yaml deleted file mode 100644 index 7d668e4a..00000000 --- a/config/rbac/pdb_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit pdbs. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: pdb-editor-role -rules: -- apiGroups: - - database.oracle.com - resources: - - pdbs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - database.oracle.com - resources: - - pdbs/status - verbs: - - get diff --git a/config/rbac/pdb_viewer_role.yaml b/config/rbac/pdb_viewer_role.yaml deleted file mode 100644 index 5fcf68c9..00000000 --- a/config/rbac/pdb_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view pdbs. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: pdb-viewer-role -rules: -- apiGroups: - - database.oracle.com - resources: - - pdbs - verbs: - - get - - list - - watch -- apiGroups: - - database.oracle.com - resources: - - pdbs/status - verbs: - - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 576945f8..7bb96a74 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -99,7 +99,6 @@ rules: resources: - autonomouscontainerdatabases - autonomousdatabases - - cdbs - dataguardbrokers - dbcssystems - events @@ -108,7 +107,6 @@ rules: - oraclerestarts - oraclerestdataservices - ordssrvs - - pdbs - shardingdatabases - singleinstancedatabases verbs: @@ -125,7 +123,6 @@ rules: - autonomouscontainerdatabases/status - autonomousdatabasebackups/status - autonomousdatabaserestores/status - - cdbs/status - dataguardbrokers/status - dbcssystems/status - lrests/status @@ -133,7 +130,6 @@ rules: - oraclerestarts/status - oraclerestdataservices/status - ordssrvs/status - - pdbs/status - shardingdatabases/status - singleinstancedatabases/status verbs: @@ -162,7 +158,6 @@ rules: - apiGroups: - database.oracle.com resources: - - cdbs/finalizers - dataguardbrokers/finalizers - lrests/finalizers - oraclerestdataservices/finalizers @@ -176,7 +171,6 @@ rules: - dbcssystems/finalizers - lrpdbs/finalizers - oraclerestarts/finalizers - - pdbs/finalizers - shardingdatabases/finalizers verbs: - create diff --git a/config/samples/multitenant/cdb.yaml b/config/samples/multitenant/cdb.yaml deleted file mode 100644 index e3513d12..00000000 --- a/config/samples/multitenant/cdb.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "devcdb" - dbServer: "172.17.0.4" - dbPort: 1521 - replicas: 1 - ordsImage: "" - ordsImagePullPolicy: "Always" - # Uncomment Below Secret Format for accessing ords image from private docker registry - # ordsImagePullSecret: "" - serviceName: "devdb.example.com" - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" diff --git a/config/samples/multitenant/cdb_secret.yaml b/config/samples/multitenant/cdb_secret.yaml deleted file mode 100644 index e270100d..00000000 --- a/config/samples/multitenant/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: "[base64 encode value]" - sysadmin_pwd: "[base64 encode value]" - cdbadmin_user: "[base64 encode value]" - cdbadmin_pwd: "[base64 encode value]" - webserver_user: "[base64 encode values]" - webserver_pwd: "[base64 encode values]" diff --git a/config/samples/multitenant/pdb_clone.yaml b/config/samples/multitenant/pdb_clone.yaml deleted file mode 100644 index f36e904d..00000000 --- a/config/samples/multitenant/pdb_clone.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1-clone - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "devcdb" - pdbName: "pdbdevclone" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Clone" diff --git a/config/samples/multitenant/pdb_create.yaml b/config/samples/multitenant/pdb_create.yaml deleted file mode 100644 index 2be31acf..00000000 --- a/config/samples/multitenant/pdb_create.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "devcdb" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Create" diff --git a/config/samples/multitenant/pdb_delete.yaml b/config/samples/multitenant/pdb_delete.yaml deleted file mode 100644 index 6c5299c0..00000000 --- a/config/samples/multitenant/pdb_delete.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" \ No newline at end of file diff --git a/config/samples/multitenant/pdb_modify.yaml b/config/samples/multitenant/pdb_modify.yaml deleted file mode 100644 index feac2dbf..00000000 --- a/config/samples/multitenant/pdb_modify.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbName: "democdb" - pdbName: "demotest" - action: "Modify" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - - # To Open an existing PDB, uncomment the below lines and comment the two lines above - #pdbState: "OPEN" - #modifyOption: "READ WRITE" \ No newline at end of file diff --git a/config/samples/multitenant/pdb_plug.yaml b/config/samples/multitenant/pdb_plug.yaml deleted file mode 100644 index b48c4ffc..00000000 --- a/config/samples/multitenant/pdb_plug.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - pdbName: "pdbdev" - xmlFileName: "/opt/oracle/oradata/pdbdev.xml" - sourceFileNameConversions: "NONE" - fileNameConversions: "NONE" - copyAction: "NOCOPY" - action: "Plug" \ No newline at end of file diff --git a/config/samples/multitenant/pdb_secret.yaml b/config/samples/multitenant/pdb_secret.yaml deleted file mode 100644 index 8a3202d9..00000000 --- a/config/samples/multitenant/pdb_secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - sysadmin_user: "[ base64 encode value]" - sysadmin_pwd: "[ base64 encode value]" diff --git a/config/samples/multitenant/pdb_unplug.yaml b/config/samples/multitenant/pdb_unplug.yaml deleted file mode 100644 index 21d7b187..00000000 --- a/config/samples/multitenant/pdb_unplug.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - pdbName: "pdbdev" - xmlFileName: "/opt/oracle/oradata/demotest_pdb.xml" - action: "Unplug" - tdeExport: true - tdeSecret: - secret: - secretName: "pdb1-secret" - key: "tde_secret" - tdeKeystorePath: "/opt/oracle/test" - tdePassword: - secret: - secretName: "pdb1-secret" - key: "tde_pwd" - getScript: true \ No newline at end of file diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index c4ba40b3..a225b1d9 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -4,27 +4,6 @@ kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-configuration webhooks: -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: mcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -107,27 +86,6 @@ webhooks: resources: - oraclerestarts sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: mpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -397,27 +355,6 @@ webhooks: resources: - autonomousdatabases sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /validate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: vcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None - admissionReviewVersions: - v4 - v1beta1 @@ -481,27 +418,6 @@ webhooks: resources: - oraclerestarts sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /validate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: vpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/controllers/database/cdb_controller.go b/controllers/database/cdb_controller.go deleted file mode 100644 index 6c5fc747..00000000 --- a/controllers/database/cdb_controller.go +++ /dev/null @@ -1,1093 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package controllers - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - //"fmt" - "strconv" - "strings" - "time" - - "github.com/go-logr/logr" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" - - dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" - dbcommons "github.com/oracle/oracle-database-operator/commons/database" -) - -// CDBReconciler reconciles a CDB object -type CDBReconciler struct { - client.Client - Scheme *runtime.Scheme - Config *rest.Config - Log logr.Logger - Interval time.Duration - Recorder record.EventRecorder -} - -var ( - cdbPhaseInit = "Initializing" - cdbPhasePod = "CreatingPod" - cdbPhaseValPod = "ValidatingPods" - cdbPhaseService = "CreatingService" - cdbPhaseSecrets = "DeletingSecrets" - cdbPhaseReady = "Ready" - cdbPhaseDelete = "Deleting" - cdbPhaseFail = "Failed" -) - -const CDBFinalizer = "database.oracle.com/CDBfinalizer" - -//+kubebuilder:rbac:groups=database.oracle.com,resources=cdbs,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=database.oracle.com,resources=cdbs/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=database.oracle.com,resources=cdbs/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=pods;pods/log;pods/exec;services;configmaps;events;replicasets,verbs=create;delete;get;list;patch;update;watch -//+kubebuilder:rbac:groups=core,resources=pods;secrets;services;configmaps;namespaces,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=apps,resources=replicasets,verbs=get;list;watch;create;update;patch;delete - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the CDB object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile -func (r *CDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - - log := r.Log.WithValues("multitenantoperator", req.NamespacedName) - log.Info("Reconcile requested") - - reconcilePeriod := r.Interval * time.Second - requeueY := ctrl.Result{Requeue: true, RequeueAfter: reconcilePeriod} - requeueN := ctrl.Result{} - - var err error - cdb := &dbapi.CDB{} - - // Execute for every reconcile - defer func() { - log.Info("DEFER", "Name", cdb.Name, "Phase", cdb.Status.Phase, "Status", strconv.FormatBool(cdb.Status.Status)) - if !cdb.Status.Status { - if err := r.Status().Update(ctx, cdb); err != nil { - log.Error(err, "Failed to update status for :"+cdb.Name, "err", err.Error()) - } - } - }() - - err = r.Client.Get(context.TODO(), req.NamespacedName, cdb) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("CDB Resource Not found", "Name", cdb.Name) - // Request object not found, could have been deleted after reconcile req. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - cdb.Status.Status = true - return requeueN, nil - } - // Error reading the object - requeue the req. - return requeueY, err - } - - log.Info("Res Status:", "Name", cdb.Name, "Phase", cdb.Status.Phase, "Status", strconv.FormatBool(cdb.Status.Status)) - - // Finalizer section - err = r.manageCDBDeletion(ctx, req, cdb) - if err != nil { - log.Info("Reconcile queued") - return requeueY, nil - } - - // If post-creation, CDB spec is changed, check and take appropriate action - if (cdb.Status.Phase == cdbPhaseReady) && cdb.Status.Status { - r.evaluateSpecChange(ctx, req, cdb) - } - - if !cdb.Status.Status { - phase := cdb.Status.Phase - log.Info("Current Phase:"+phase, "Name", cdb.Name) - - switch phase { - case cdbPhaseInit: - err = r.verifySecrets(ctx, req, cdb) - if err != nil { - cdb.Status.Phase = cdbPhaseFail - return requeueN, nil - } - cdb.Status.Phase = cdbPhasePod - case cdbPhasePod: - // Create ORDS PODs - err = r.createORDSInstances(ctx, req, cdb) - if err != nil { - log.Info("Reconcile queued") - return requeueY, nil - } - cdb.Status.Phase = cdbPhaseValPod - case cdbPhaseValPod: - // Validate ORDS PODs - err = r.validateORDSPods(ctx, req, cdb) - if err != nil { - if cdb.Status.Phase == cdbPhaseFail { - return requeueN, nil - } - log.Info("Reconcile queued") - return requeueY, nil - } - cdb.Status.Phase = cdbPhaseService - case cdbPhaseService: - // Create ORDS Service - err = r.createORDSSVC(ctx, req, cdb) - if err != nil { - log.Info("Reconcile queued") - return requeueY, nil - } - //cdb.Status.Phase = cdbPhaseSecrets - cdb.Status.Phase = cdbPhaseReady - case cdbPhaseSecrets: - // Delete CDB Secrets - //r.deleteSecrets(ctx, req, cdb) - cdb.Status.Phase = cdbPhaseReady - cdb.Status.Msg = "Success" - case cdbPhaseReady: - cdb.Status.Status = true - r.Status().Update(ctx, cdb) - return requeueN, nil - default: - cdb.Status.Phase = cdbPhaseInit - log.Info("DEFAULT:", "Name", cdb.Name, "Phase", phase, "Status", strconv.FormatBool(cdb.Status.Status)) - } - - if err := r.Status().Update(ctx, cdb); err != nil { - log.Error(err, "Failed to update status for :"+cdb.Name, "err", err.Error()) - } - return requeueY, nil - } - - log.Info("Reconcile completed") - return requeueN, nil -} - -/* -********************************************************* - - Create a ReplicaSet for pods based on the ORDS container - /******************************************************* -*/ -func (r *CDBReconciler) createORDSInstances(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - - log := r.Log.WithValues("createORDSInstances", req.NamespacedName) - - replicaSet := r.createReplicaSetSpec(cdb) - - foundRS := &appsv1.ReplicaSet{} - err := r.Get(context.TODO(), types.NamespacedName{Name: replicaSet.Name, Namespace: cdb.Namespace}, foundRS) - if err != nil && apierrors.IsNotFound(err) { - log.Info("Creating ORDS Replicaset: " + replicaSet.Name) - err = r.Create(ctx, replicaSet) - if err != nil { - log.Error(err, "Failed to create ReplicaSet for :"+cdb.Name, "Namespace", replicaSet.Namespace, "Name", replicaSet.Name) - return err - } - } else if err != nil { - log.Error(err, "Replicaset : "+replicaSet.Name+" already exists.") - return err - } - - // Set CDB instance as the owner and controller - ctrl.SetControllerReference(cdb, replicaSet, r.Scheme) - - log.Info("Created ORDS ReplicaSet successfully") - r.Recorder.Eventf(cdb, corev1.EventTypeNormal, "CreatedORDSReplicaSet", "Created ORDS Replicaset (Replicas - %s) for %s", strconv.Itoa(cdb.Spec.Replicas), cdb.Name) - return nil -} - -/* -************************************************ - - Validate ORDS Pod. Check if there are any errors - /*********************************************** -*/ -func (r *CDBReconciler) validateORDSPods(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - - log := r.Log.WithValues("validateORDSPod", req.NamespacedName) - - log.Info("Validating Pod creation for :" + cdb.Name) - - podName := cdb.Name + "-ords" - podList := &corev1.PodList{} - listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingLabels{"name": podName}} - - // List retrieves list of objects for a given namespace and list options. - err := r.List(ctx, podList, listOpts...) - if err != nil { - log.Info("Failed to list pods of: "+podName, "Namespace", req.Namespace) - return err - } - - if len(podList.Items) == 0 { - log.Info("No pods found for: "+podName, "Namespace", req.Namespace) - cdb.Status.Msg = "Waiting for ORDS Pod(s) to start" - return errors.New("Waiting for ORDS pods to start") - } - - /* /opt/oracle/ords/secrets/$TLSKEY /opt/oracle/ords/secrets/$TLSCRT */ - getORDSStatus := " curl --cert /opt/oracle/ords/secrets/tls.crt --key /opt/oracle/ords/secrets/tls.key -sSkv -k -X GET https://localhost:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/stable/metadata-catalog/ || curl --cert /opt/oracle/ords/secrets/tls.crt --key /opt/oracle/ords/secrets/tls.key -sSkv -X GET http://localhost:" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/stable/metadata-catalog/ " - readyPods := 0 - for _, pod := range podList.Items { - if pod.Status.Phase == corev1.PodRunning { - // Get ORDS Status - out, err := dbcommons.ExecCommand(r, r.Config, pod.Name, pod.Namespace, "", ctx, req, false, "bash", "-c", getORDSStatus) - if strings.Contains(out, "HTTP/1.1 200 OK") || strings.Contains(strings.ToUpper(err.Error()), "HTTP/1.1 200 OK") || - strings.Contains(out, "HTTP/2") || strings.Contains(strings.ToUpper(err.Error()), " HTTP/2") { - readyPods++ - } else if strings.Contains(out, "HTTP/1.1 404 Not Found") || strings.Contains(strings.ToUpper(err.Error()), "HTTP/1.1 404 NOT FOUND") || strings.Contains(strings.ToUpper(err.Error()), "HTTP/2 404") || strings.Contains(strings.ToUpper(err.Error()), "Failed to connect to localhost") { - // Check if DB connection parameters are correct - getORDSInstallStatus := " grep -q 'Failed to' /tmp/ords_install.log; echo $?;" - out, _ := dbcommons.ExecCommand(r, r.Config, pod.Name, pod.Namespace, "", ctx, req, false, "bash", "-c", getORDSInstallStatus) - if strings.TrimSpace(out) == "0" { - cdb.Status.Msg = "Check DB connection parameters" - cdb.Status.Phase = cdbPhaseFail - // Delete existing ReplicaSet - r.deleteReplicaSet(ctx, req, cdb) - return errors.New("Check DB connection parameters") - } - } - } - } - - if readyPods != cdb.Spec.Replicas { - log.Info("Replicas: "+strconv.Itoa(cdb.Spec.Replicas), "Ready Pods: ", readyPods) - cdb.Status.Msg = "Waiting for ORDS Pod(s) to be ready" - return errors.New("Waiting for ORDS pods to be ready") - } - - cdb.Status.Msg = "" - return nil -} - -/* -*********************** - - Create Pod spec - -/*********************** -*/ -func (r *CDBReconciler) createPodSpec(cdb *dbapi.CDB) corev1.PodSpec { - - podSpec := corev1.PodSpec{ - Volumes: []corev1.Volume{{ - Name: "secrets", - VolumeSource: corev1.VolumeSource{ - Projected: &corev1.ProjectedVolumeSource{ - DefaultMode: func() *int32 { i := int32(0666); return &i }(), - Sources: []corev1.VolumeProjection{ - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cdb.Spec.SysAdminPwd.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: cdb.Spec.SysAdminPwd.Secret.Key, - Path: cdb.Spec.SysAdminPwd.Secret.Key, - }, - }, - }, - }, - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cdb.Spec.CDBAdminUser.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: cdb.Spec.CDBAdminUser.Secret.Key, - Path: cdb.Spec.CDBAdminUser.Secret.Key, - }, - }, - }, - }, - /***/ - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cdb.Spec.CDBTlsKey.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: cdb.Spec.CDBTlsKey.Secret.Key, - Path: cdb.Spec.CDBTlsKey.Secret.Key, - }, - }, - }, - }, - - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cdb.Spec.CDBTlsCrt.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: cdb.Spec.CDBTlsCrt.Secret.Key, - Path: cdb.Spec.CDBTlsCrt.Secret.Key, - }, - }, - }, - }, - - /***/ - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cdb.Spec.CDBAdminPwd.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: cdb.Spec.CDBAdminPwd.Secret.Key, - Path: cdb.Spec.CDBAdminPwd.Secret.Key, - }, - }, - }, - }, - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cdb.Spec.ORDSPwd.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: cdb.Spec.ORDSPwd.Secret.Key, - Path: cdb.Spec.ORDSPwd.Secret.Key, - }, - }, - }, - }, - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cdb.Spec.WebServerUser.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: cdb.Spec.WebServerUser.Secret.Key, - Path: cdb.Spec.WebServerUser.Secret.Key, - }, - }, - }, - }, - { - Secret: &corev1.SecretProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cdb.Spec.WebServerPwd.Secret.SecretName, - }, - Items: []corev1.KeyToPath{ - { - Key: cdb.Spec.WebServerPwd.Secret.Key, - Path: cdb.Spec.WebServerPwd.Secret.Key, - }, - }, - }, - }, - }, - }, - }, - }}, - Containers: []corev1.Container{{ - Name: cdb.Name + "-ords", - Image: cdb.Spec.ORDSImage, - VolumeMounts: []corev1.VolumeMount{{ - MountPath: "/opt/oracle/ords/secrets", - Name: "secrets", - ReadOnly: true, - }}, - Env: func() []corev1.EnvVar { - return []corev1.EnvVar{ - { - Name: "ORACLE_HOST", - Value: cdb.Spec.DBServer, - }, - { - Name: "DBTNSURL", - Value: cdb.Spec.DBTnsurl, - }, - { - Name: "TLSCRT", - Value: cdb.Spec.CDBTlsCrt.Secret.Key, - }, - { - Name: "TLSKEY", - Value: cdb.Spec.CDBTlsKey.Secret.Key, - }, - { - Name: "ORACLE_PORT", - Value: strconv.Itoa(cdb.Spec.DBPort), - }, - { - Name: "ORDS_PORT", - Value: strconv.Itoa(cdb.Spec.ORDSPort), - }, - { - Name: "ORACLE_SERVICE", - Value: cdb.Spec.ServiceName, - }, - { - Name: "ORACLE_PWD_KEY", - Value: cdb.Spec.SysAdminPwd.Secret.Key, - }, - { - Name: "CDBADMIN_USER_KEY", - Value: cdb.Spec.CDBAdminUser.Secret.Key, - }, - { - Name: "CDBADMIN_PWD_KEY", - Value: cdb.Spec.CDBAdminPwd.Secret.Key, - }, - { - Name: "ORDS_PWD_KEY", - Value: cdb.Spec.ORDSPwd.Secret.Key, - }, - { - Name: "WEBSERVER_USER_KEY", - Value: cdb.Spec.WebServerUser.Secret.Key, - }, - { - Name: "WEBSERVER_PASSWORD_KEY", - Value: cdb.Spec.WebServerPwd.Secret.Key, - }, - { - Name: "R1", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cdb.Spec.CDBPriKey.Secret.SecretName, - }, - Key: cdb.Spec.CDBPriKey.Secret.Key, - }, - }, - }, - } - }(), - }}, - - NodeSelector: func() map[string]string { - ns := make(map[string]string) - if len(cdb.Spec.NodeSelector) != 0 { - for key, value := range cdb.Spec.NodeSelector { - ns[key] = value - } - } - return ns - }(), - } - - if len(cdb.Spec.ORDSImagePullSecret) > 0 { - podSpec.ImagePullSecrets = []corev1.LocalObjectReference{ - { - Name: cdb.Spec.ORDSImagePullSecret, - }, - } - } - - podSpec.Containers[0].ImagePullPolicy = corev1.PullAlways - - if len(cdb.Spec.ORDSImagePullPolicy) > 0 { - if strings.ToUpper(cdb.Spec.ORDSImagePullPolicy) == "NEVER" { - podSpec.Containers[0].ImagePullPolicy = corev1.PullNever - } - } - - return podSpec -} - -/* -*********************** - - Create ReplicaSet spec - -/*********************** -*/ -func (r *CDBReconciler) createReplicaSetSpec(cdb *dbapi.CDB) *appsv1.ReplicaSet { - - replicas := int32(cdb.Spec.Replicas) - podSpec := r.createPodSpec(cdb) - - replicaSet := &appsv1.ReplicaSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: cdb.Name + "-ords-rs", - Namespace: cdb.Namespace, - Labels: map[string]string{ - "name": cdb.Name + "-ords-rs", - }, - }, - Spec: appsv1.ReplicaSetSpec{ - Replicas: &replicas, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Name: cdb.Name + "-ords", - Namespace: cdb.Namespace, - Labels: map[string]string{ - "name": cdb.Name + "-ords", - }, - }, - Spec: podSpec, - }, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "name": cdb.Name + "-ords", - }, - }, - }, - } - - return replicaSet -} - -/* -********************************************************* - - Evaluate change in Spec post creation and instantiation - /******************************************************* -*/ -func (r *CDBReconciler) deleteReplicaSet(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - log := r.Log.WithValues("deleteReplicaSet", req.NamespacedName) - - k_client, err := kubernetes.NewForConfig(r.Config) - if err != nil { - log.Error(err, "Kubernetes Config Error") - return err - } - - replicaSetName := cdb.Name + "-ords-rs" - err = k_client.AppsV1().ReplicaSets(cdb.Namespace).Delete(context.TODO(), replicaSetName, metav1.DeleteOptions{}) - if err != nil { - log.Info("Could not delete ReplicaSet", "RS Name", replicaSetName, "err", err.Error()) - if !strings.Contains(strings.ToUpper(err.Error()), "NOT FOUND") { - return err - } - } else { - log.Info("Successfully deleted ORDS ReplicaSet", "RS Name", replicaSetName) - } - - return nil -} - -/* -********************************************************* - - Evaluate change in Spec post creation and instantiation - /******************************************************* -*/ -func (r *CDBReconciler) evaluateSpecChange(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - log := r.Log.WithValues("evaluateSpecChange", req.NamespacedName) - - // List the Pods matching the PodTemplate Labels - podName := cdb.Name + "-ords" - podList := &corev1.PodList{} - listOpts := []client.ListOption{client.InNamespace(req.Namespace), client.MatchingLabels{"name": podName}} - - // List retrieves list of objects for a given namespace and list options. - err := r.List(ctx, podList, listOpts...) - if err != nil { - log.Info("Failed to list pods of: "+podName, "Namespace", req.Namespace) - return err - } - - var foundPod corev1.Pod - for _, pod := range podList.Items { - foundPod = pod - break - } - - ordsSpecChange := false - for _, envVar := range foundPod.Spec.Containers[0].Env { - if envVar.Name == "ORACLE_HOST" && envVar.Value != cdb.Spec.DBServer { - ordsSpecChange = true - } else if envVar.Name == "ORACLE_PORT" && envVar.Value != strconv.Itoa(cdb.Spec.DBPort) { - ordsSpecChange = true - } else if envVar.Name == "ORDS_PORT" && envVar.Value != strconv.Itoa(cdb.Spec.ORDSPort) { - ordsSpecChange = true - } else if envVar.Name == "ORACLE_SERVICE" && envVar.Value != cdb.Spec.ServiceName { - ordsSpecChange = true - } - } - - if ordsSpecChange { - // Delete existing ReplicaSet - err = r.deleteReplicaSet(ctx, req, cdb) - if err != nil { - return err - } - - cdb.Status.Phase = cdbPhaseInit - cdb.Status.Status = false - r.Status().Update(ctx, cdb) - } else { - // Update the RS if the value of "replicas" is changed - replicaSetName := cdb.Name + "-ords-rs" - - foundRS := &appsv1.ReplicaSet{} - err := r.Get(context.TODO(), types.NamespacedName{Name: replicaSetName, Namespace: cdb.Namespace}, foundRS) - if err != nil { - log.Error(err, "Unable to get ORDS Replicaset: "+replicaSetName) - return err - } - - // Check if number of replicas have changed - replicas := int32(cdb.Spec.Replicas) - if cdb.Spec.Replicas != int(*(foundRS.Spec.Replicas)) { - log.Info("Existing Replicas: " + strconv.Itoa(int(*(foundRS.Spec.Replicas))) + ", New Replicas: " + strconv.Itoa(cdb.Spec.Replicas)) - foundRS.Spec.Replicas = &replicas - err = r.Update(ctx, foundRS) - if err != nil { - log.Error(err, "Failed to update ReplicaSet for :"+cdb.Name, "Namespace", cdb.Namespace, "Name", replicaSetName) - return err - } - cdb.Status.Phase = cdbPhaseValPod - cdb.Status.Status = false - r.Status().Update(ctx, cdb) - } - } - - return nil -} - -/* -************************************************ - - Create a Cluster Service for ORDS CDB Pod - /*********************************************** -*/ -func (r *CDBReconciler) createORDSSVC(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - - log := r.Log.WithValues("createORDSSVC", req.NamespacedName) - - foundSvc := &corev1.Service{} - err := r.Get(context.TODO(), types.NamespacedName{Name: cdb.Name + "-ords", Namespace: cdb.Namespace}, foundSvc) - if err != nil && apierrors.IsNotFound(err) { - svc := r.createSvcSpec(cdb) - - log.Info("Creating a new Cluster Service for: "+cdb.Name, "Svc.Namespace", svc.Namespace, "Service.Name", svc.Name) - err := r.Create(ctx, svc) - if err != nil { - log.Error(err, "Failed to create new Cluster Service for: "+cdb.Name, "Svc.Namespace", svc.Namespace, "Service.Name", svc.Name) - return err - } - - log.Info("Created ORDS Cluster Service successfully") - r.Recorder.Eventf(cdb, corev1.EventTypeNormal, "CreatedORDSService", "Created ORDS Service for %s", cdb.Name) - } else { - log.Info("ORDS Cluster Service already exists") - } - - return nil -} - -/* -*********************** - - Create Service spec - /*********************** -*/ -func (r *CDBReconciler) createSvcSpec(cdb *dbapi.CDB) *corev1.Service { - - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: cdb.Name + "-ords", - Namespace: cdb.Namespace, - }, - Spec: corev1.ServiceSpec{ - Selector: map[string]string{ - "name": cdb.Name + "-ords", - }, - ClusterIP: corev1.ClusterIPNone, - }, - } - // Set CDB instance as the owner and controller - ctrl.SetControllerReference(cdb, svc, r.Scheme) - return svc -} - -/* -************************************************ - - Check CDB deletion - -/*********************************************** -*/ -func (r *CDBReconciler) manageCDBDeletion(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - log := r.Log.WithValues("manageCDBDeletion", req.NamespacedName) - - /* REGISTER FINALIZER */ - if cdb.ObjectMeta.DeletionTimestamp.IsZero() { - if !controllerutil.ContainsFinalizer(cdb, CDBFinalizer) { - controllerutil.AddFinalizer(cdb, CDBFinalizer) - if err := r.Update(ctx, cdb); err != nil { - return err - } - } - - } else { - log.Info("cdb set to be deleted") - cdb.Status.Phase = cdbPhaseDelete - cdb.Status.Status = true - r.Status().Update(ctx, cdb) - - if controllerutil.ContainsFinalizer(cdb, CDBFinalizer) { - - if err := r.DeletePDBS(ctx, req, cdb); err != nil { - log.Info("Cannot delete pdbs") - return err - } - - controllerutil.RemoveFinalizer(cdb, CDBFinalizer) - if err := r.Update(ctx, cdb); err != nil { - return err - } - } - - err := r.deleteCDBInstance(ctx, req, cdb) - if err != nil { - log.Info("Could not delete CDB Resource", "CDB Name", cdb.Spec.CDBName, "err", err.Error()) - return err - } - - } - return nil -} - -/* -************************************************ - - Delete CDB Resource - -/*********************************************** -*/ -func (r *CDBReconciler) deleteCDBInstance(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - - log := r.Log.WithValues("deleteCDBInstance", req.NamespacedName) - - k_client, err := kubernetes.NewForConfig(r.Config) - if err != nil { - log.Error(err, "Kubernetes Config Error") - } - - replicaSetName := cdb.Name + "-ords-rs" - - err = k_client.AppsV1().ReplicaSets(cdb.Namespace).Delete(context.TODO(), replicaSetName, metav1.DeleteOptions{}) - if err != nil { - log.Info("Could not delete ReplicaSet", "RS Name", replicaSetName, "err", err.Error()) - if !strings.Contains(strings.ToUpper(err.Error()), "NOT FOUND") { - return err - } - } else { - log.Info("Successfully deleted ORDS ReplicaSet", "RS Name", replicaSetName) - } - - r.Recorder.Eventf(cdb, corev1.EventTypeNormal, "DeletedORDSReplicaSet", "Deleted ORDS ReplicaSet for %s", cdb.Name) - - svcName := cdb.Name + "-ords" - - err = k_client.CoreV1().Services(cdb.Namespace).Delete(context.TODO(), svcName, metav1.DeleteOptions{}) - if err != nil { - log.Info("Could not delete Service", "Service Name", svcName, "err", err.Error()) - if !strings.Contains(strings.ToUpper(err.Error()), "NOT FOUND") { - return err - } - } else { - r.Recorder.Eventf(cdb, corev1.EventTypeNormal, "DeletedORDSService", "Deleted ORDS Service for %s", cdb.Name) - log.Info("Successfully deleted ORDS Service", "Service Name", svcName) - } - - log.Info("Successfully deleted CDB resource", "CDB Name", cdb.Spec.CDBName) - return nil -} - -/* -************************************************ - - Get Secret Key for a Secret Name - -/*********************************************** -*/ -func (r *CDBReconciler) verifySecrets(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - - log := r.Log.WithValues("verifySecrets", req.NamespacedName) - - if err := r.checkSecret(ctx, req, cdb, cdb.Spec.SysAdminPwd.Secret.SecretName); err != nil { - return err - } - if err := r.checkSecret(ctx, req, cdb, cdb.Spec.CDBAdminUser.Secret.SecretName); err != nil { - return err - } - if err := r.checkSecret(ctx, req, cdb, cdb.Spec.CDBAdminPwd.Secret.SecretName); err != nil { - return err - } - if err := r.checkSecret(ctx, req, cdb, cdb.Spec.ORDSPwd.Secret.SecretName); err != nil { - return err - } - if err := r.checkSecret(ctx, req, cdb, cdb.Spec.WebServerUser.Secret.SecretName); err != nil { - return err - } - if err := r.checkSecret(ctx, req, cdb, cdb.Spec.WebServerPwd.Secret.SecretName); err != nil { - return err - } - if err := r.checkSecret(ctx, req, cdb, cdb.Spec.CDBPriKey.Secret.SecretName); err != nil { - return err - } - - cdb.Status.Msg = "" - log.Info("Verified secrets successfully") - return nil -} - -/* -************************************************ - - Get Secret Key for a Secret Name - -/*********************************************** -*/ -func (r *CDBReconciler) checkSecret(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB, secretName string) error { - - log := r.Log.WithValues("checkSecret", req.NamespacedName) - - secret := &corev1.Secret{} - err := r.Get(ctx, types.NamespacedName{Name: secretName, Namespace: cdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + secretName) - cdb.Status.Msg = "Secret not found:" + secretName - return err - } - log.Error(err, "Unable to get the secret.") - return err - } - - return nil -} - -/* -************************************************ - - Delete Secrets - -/*********************************************** -*/ -func (r *CDBReconciler) deleteSecrets(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) { - - log := r.Log.WithValues("deleteSecrets", req.NamespacedName) - - log.Info("Deleting CDB secrets") - secret := &corev1.Secret{} - err := r.Get(ctx, types.NamespacedName{Name: cdb.Spec.SysAdminPwd.Secret.SecretName, Namespace: cdb.Namespace}, secret) - if err == nil { - err := r.Delete(ctx, secret) - if err == nil { - log.Info("Deleted the secret : " + cdb.Spec.SysAdminPwd.Secret.SecretName) - } - } - - err = r.Get(ctx, types.NamespacedName{Name: cdb.Spec.CDBAdminUser.Secret.SecretName, Namespace: cdb.Namespace}, secret) - if err == nil { - err := r.Delete(ctx, secret) - if err == nil { - log.Info("Deleted the secret : " + cdb.Spec.CDBAdminUser.Secret.SecretName) - } - } - - err = r.Get(ctx, types.NamespacedName{Name: cdb.Spec.CDBAdminPwd.Secret.SecretName, Namespace: cdb.Namespace}, secret) - if err == nil { - err := r.Delete(ctx, secret) - if err == nil { - log.Info("Deleted the secret : " + cdb.Spec.CDBAdminPwd.Secret.SecretName) - } - } - - err = r.Get(ctx, types.NamespacedName{Name: cdb.Spec.ORDSPwd.Secret.SecretName, Namespace: cdb.Namespace}, secret) - if err == nil { - err := r.Delete(ctx, secret) - if err == nil { - log.Info("Deleted the secret : " + cdb.Spec.ORDSPwd.Secret.SecretName) - } - } - - err = r.Get(ctx, types.NamespacedName{Name: cdb.Spec.WebServerUser.Secret.SecretName, Namespace: cdb.Namespace}, secret) - if err == nil { - err := r.Delete(ctx, secret) - if err == nil { - log.Info("Deleted the secret : " + cdb.Spec.WebServerUser.Secret.SecretName) - } - } - - err = r.Get(ctx, types.NamespacedName{Name: cdb.Spec.WebServerPwd.Secret.SecretName, Namespace: cdb.Namespace}, secret) - if err == nil { - err := r.Delete(ctx, secret) - if err == nil { - log.Info("Deleted the secret : " + cdb.Spec.WebServerPwd.Secret.SecretName) - } - } -} - -/* Delete cascade option */ - -/* -************************************************************* - - SetupWithManager sets up the controller with the Manager. -/************************************************************ -*/ - -func (r *CDBReconciler) DeletePDBS(ctx context.Context, req ctrl.Request, cdb *dbapi.CDB) error { - log := r.Log.WithValues("DeletePDBS", req.NamespacedName) - - /* =================== DELETE CASCADE ================ */ - if cdb.Spec.DeletePDBCascade == true { - log.Info("DELETE PDB CASCADE OPTION") - pdbList := &dbapi.PDBList{} - listOpts := []client.ListOption{} - err := r.List(ctx, pdbList, listOpts...) - if err != nil { - log.Info("Failed to get the list of pdbs") - } - - var url string - if err == nil { - for _, pdbitem := range pdbList.Items { - log.Info("pdbitem.Spec.CDBName : " + pdbitem.Spec.CDBName) - log.Info("pdbitem.Spec.CDBNamespace: " + pdbitem.Spec.CDBNamespace) - log.Info("cdb.Spec.CDBName : " + cdb.Spec.CDBName) - log.Info("cdb.Namespace : " + cdb.Namespace) - if pdbitem.Spec.CDBName == cdb.Spec.CDBName && pdbitem.Spec.CDBNamespace == cdb.Namespace { - fmt.Printf("DeletePDBS Call Delete function for %s %s\n", pdbitem.Name, pdbitem.Spec.PDBName) - - var objmap map[string]interface{} /* Used for the return payload */ - values := map[string]string{ - "state": "CLOSE", - "modifyOption": "IMMEDIATE", - "getScript": "FALSE", - } - - //url := "https://" + pdbitem.Spec.CDBResName + "-cdb." + pdbitem.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/database/pdbs/" + pdbitem.Spec.PDBName - url = "https://" + pdbitem.Spec.CDBResName + "-ords." + pdbitem.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbitem.Spec.PDBName + "/status" - - log.Info("callAPI(URL):" + url) - log.Info("pdbitem.Status.OpenMode" + pdbitem.Status.OpenMode) - - if pdbitem.Status.OpenMode != "MOUNTED" { - - log.Info("Force pdb closure") - respData, errapi := NewCallApi(r, ctx, req, &pdbitem, url, values, "POST") - - fmt.Printf("Debug NEWCALL:%s\n", respData) - if err := json.Unmarshal([]byte(respData), &objmap); err != nil { - log.Error(err, "failed to get respData from callAPI", "err", err.Error()) - return err - } - - if errapi != nil { - log.Error(err, "callAPI cannot close pdb "+pdbitem.Spec.PDBName, "err", err.Error()) - return err - } - - r.Recorder.Eventf(cdb, corev1.EventTypeNormal, "close pdb", "pdbname=%s", pdbitem.Spec.PDBName) - } - - /* start dropping pdb */ - log.Info("Drop pluggable database") - values = map[string]string{ - "action": "INCLUDING", - "getScript": "FALSE", - } - url = "https://" + pdbitem.Spec.CDBResName + "-ords." + pdbitem.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbitem.Spec.PDBName + "/" - respData, errapi := NewCallApi(r, ctx, req, &pdbitem, url, values, "DELETE") - - if err := json.Unmarshal([]byte(respData), &objmap); err != nil { - log.Error(err, "failed to get respData from callAPI", "err", err.Error()) - return err - } - - if errapi != nil { - log.Error(err, "callAPI cannot drop pdb "+pdbitem.Spec.PDBName, "err", err.Error()) - return err - } - r.Recorder.Eventf(cdb, corev1.EventTypeNormal, "drop pdb", "pdbname=%s", pdbitem.Spec.PDBName) - - err = r.Delete(context.Background(), &pdbitem, client.GracePeriodSeconds(0)) - if err != nil { - log.Info("Could not delete PDB resource", "err", err.Error()) - return err - } - - } /* check pdb name */ - } /* end of loop */ - } - - } - /* ================================================ */ - return nil -} - -func (r *CDBReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&dbapi.CDB{}). - Owns(&appsv1.ReplicaSet{}). //Watch for deleted RS owned by this controller - WithEventFilter(predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - // Ignore updates to CR status in which case metadata.Generation does not change - return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() - }, - DeleteFunc: func(e event.DeleteEvent) bool { - // Evaluates to false if the object has been confirmed deleted. - //return !e.DeleteStateUnknown - return false - }, - }). - WithOptions(controller.Options{MaxConcurrentReconciles: 100}). - Complete(r) -} diff --git a/controllers/database/pdb_controller.go b/controllers/database/pdb_controller.go deleted file mode 100644 index c61c3073..00000000 --- a/controllers/database/pdb_controller.go +++ /dev/null @@ -1,1631 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package controllers - -import ( - "bytes" - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - - //"encoding/pem" - "errors" - "fmt" - "io/ioutil" - "net/http" - "regexp" - "strconv" - "strings" - "time" - - dbapi "github.com/oracle/oracle-database-operator/apis/database/v4" - lrcommons "github.com/oracle/oracle-database-operator/commons/multitenant/lrest" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - - //metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" -) - -// PDBReconciler reconciles a PDB object -type PDBReconciler struct { - client.Client - Log logr.Logger - Scheme *runtime.Scheme - Interval time.Duration - Recorder record.EventRecorder -} - -type controllers struct { - Pdbc PDBReconciler - Cdbc CDBReconciler -} - -type RESTSQLCollection struct { - Env struct { - DefaultTimeZone string `json:"defaultTimeZone,omitempty"` - } `json:"env"` - Items []SQLItem `json:"items"` -} - -type SQLItem struct { - StatementId int `json:"statementId,omitempty"` - Response []string `json:"response"` - ErrorCode int `json:"errorCode,omitempty"` - ErrorLine int `json:"errorLine,omitempty"` - ErrorColumn int `json:"errorColumn,omitempty"` - ErrorDetails string `json:"errorDetails,omitempty"` - Result int `json:"result,omitempty"` -} - -type ORDSError struct { - Code string `json:"code,omitempty"` - Message string `json:"message,omitempty"` - Type string `json:"type,omitempty"` - Instance string `json:"instance,omitempty"` -} - -var ( - pdbPhaseCreate = "Creating" - pdbPhasePlug = "Plugging" - pdbPhaseUnplug = "Unplugging" - pdbPhaseClone = "Cloning" - pdbPhaseFinish = "Finishing" - pdbPhaseReady = "Ready" - pdbPhaseDelete = "Deleting" - pdbPhaseModify = "Modifying" - pdbPhaseMap = "Mapping" - pdbPhaseStatus = "CheckingState" - pdbPhaseFail = "Failed" -) - -const PDBFinalizer = "database.oracle.com/PDBfinalizer" -const ONE = 1 -const ZERO = 0 - -var tdePassword string -var tdeSecret string -var floodcontrol bool = false -var assertivePdbDeletion bool = false /* Global variable for assertive pdb deletion */ - -//+kubebuilder:rbac:groups=database.oracle.com,resources=pdbs,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=database.oracle.com,resources=pdbs/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=database.oracle.com,resources=pdbs/finalizers,verbs=get;create;update;patch;delete - -// +kubebuilder:rbac:groups=core,resources=pods;pods/log;pods/exec;secrets;containers;services;events;configmaps;namespaces,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=core,resources=pods/exec,verbs=create -// +kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups='',resources=statefulsets/finalizers,verbs=get;list;watch;create;update;patch;delete - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the PDB object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile - -func (r *PDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := r.Log.WithValues("multitenantoperator", req.NamespacedName) - log.Info("Reconcile requested") - - reconcilePeriod := r.Interval * time.Second - requeueY := ctrl.Result{Requeue: true, RequeueAfter: reconcilePeriod} - requeueN := ctrl.Result{} - - var err error - pdb := &dbapi.PDB{} - - // Execute for every reconcile - defer func() { - //log.Info("DEFER PDB", "Name", pdb.Name, "Phase", pdb.Status.Phase, "Status", strconv.FormatBool(pdb.Status.Status)) - if !pdb.Status.Status { - if pdb.Status.Phase == pdbPhaseReady { - pdb.Status.Status = true - } - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - } - }() - - err = r.Client.Get(context.TODO(), req.NamespacedName, pdb) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("PDB Resource Not found", "Name", pdb.Name) - // Request object not found, could have been deleted after reconcile req. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - pdb.Status.Status = true - return requeueN, nil - } - // Error reading the object - requeue the req. - return requeueY, err - } - - // Finalizer section - err = r.managePDBDeletion2(ctx, req, pdb) - if err != nil { - log.Info("managePDBDeletion2 Error Deleting resource ") - return requeueY, nil - } - - // Check for Duplicate PDB - if !pdb.Status.Status { - err = r.checkDuplicatePDB(ctx, req, pdb) - if err != nil { - return requeueN, nil - } - } - - action := strings.ToUpper(pdb.Spec.Action) - - if pdb.Status.Phase == pdbPhaseReady { - //log.Info("PDB:", "Name", pdb.Name, "Phase", pdb.Status.Phase, "Status", strconv.FormatBool(pdb.Status.Status)) - if (pdb.Status.Action != "") && (action == "MODIFY" || action == "STATUS" || pdb.Status.Action != action) { - pdb.Status.Status = false - } else { - err = r.getPDBState(ctx, req, pdb) - if err != nil { - pdb.Status.Phase = pdbPhaseFail - } else { - pdb.Status.Phase = pdbPhaseReady - pdb.Status.Msg = "Success" - } - r.Status().Update(ctx, pdb) - } - } - - if !pdb.Status.Status { - r.validatePhase(ctx, req, pdb) - phase := pdb.Status.Phase - log.Info("PDB:", "Name", pdb.Name, "Phase", phase, "Status", strconv.FormatBool(pdb.Status.Status)) - - switch phase { - case pdbPhaseCreate: - err = r.createPDB(ctx, req, pdb) - case pdbPhaseClone: - err = r.clonePDB(ctx, req, pdb) - case pdbPhasePlug: - err = r.plugPDB(ctx, req, pdb) - case pdbPhaseUnplug: - err = r.unplugPDB(ctx, req, pdb) - case pdbPhaseModify: - err = r.modifyPDB(ctx, req, pdb) - case pdbPhaseDelete: - err = r.deletePDB(ctx, req, pdb) - case pdbPhaseStatus: - err = r.getPDBState(ctx, req, pdb) - case pdbPhaseMap: - err = r.mapPDB(ctx, req, pdb) - case pdbPhaseFail: - err = r.mapPDB(ctx, req, pdb) - default: - log.Info("DEFAULT:", "Name", pdb.Name, "Phase", phase, "Status", strconv.FormatBool(pdb.Status.Status)) - return requeueN, nil - } - pdb.Status.Action = strings.ToUpper(pdb.Spec.Action) - if err != nil { - pdb.Status.Phase = pdbPhaseFail - } else { - pdb.Status.Phase = pdbPhaseReady - pdb.Status.Msg = "Success" - } - } - - log.Info("Reconcile completed") - return requeueY, nil -} - -/* -************************************************ - - Validate the PDB Spec - /*********************************************** -*/ -func (r *PDBReconciler) validatePhase(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) { - - log := r.Log.WithValues("validatePhase", req.NamespacedName) - - action := strings.ToUpper(pdb.Spec.Action) - - log.Info("Validating PDB phase for: "+pdb.Name, "Action", action) - - switch action { - case "CREATE": - pdb.Status.Phase = pdbPhaseCreate - case "CLONE": - pdb.Status.Phase = pdbPhaseClone - case "PLUG": - pdb.Status.Phase = pdbPhasePlug - case "UNPLUG": - pdb.Status.Phase = pdbPhaseUnplug - case "MODIFY": - pdb.Status.Phase = pdbPhaseModify - case "DELETE": - pdb.Status.Phase = pdbPhaseDelete - case "STATUS": - pdb.Status.Phase = pdbPhaseStatus - case "MAP": - pdb.Status.Phase = pdbPhaseMap - } - - log.Info("Validation complete") -} - -/* -*************************************************************** - - Check for Duplicate PDB. Same PDB name on the same CDB resource. - /************************************************************** -*/ -func (r *PDBReconciler) checkDuplicatePDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - - // Name of the CDB CR that holds the ORDS container - cdbResName := pdb.Spec.CDBResName - - log := r.Log.WithValues("checkDuplicatePDB", pdb.Spec.CDBNamespace) - pdbResName := pdb.Spec.PDBName - - pdbList := &dbapi.PDBList{} - - listOpts := []client.ListOption{client.InNamespace(pdb.Spec.CDBNamespace), client.MatchingFields{"spec.pdbName": pdbResName}} - - // List retrieves list of objects for a given namespace and list options. - err := r.List(ctx, pdbList, listOpts...) - if err != nil { - log.Info("Failed to list pdbs", "Namespace", pdb.Spec.CDBNamespace, "Error", err) - return err - } - - if len(pdbList.Items) == 0 { - log.Info("No pdbs found for PDBName: "+pdbResName, "CDBResName", cdbResName) - return nil - } - - for _, p := range pdbList.Items { - log.Info("Found PDB: " + p.Name) - if (p.Name != pdb.Name) && (p.Spec.CDBResName == cdbResName) { - log.Info("Duplicate PDB found") - pdb.Status.Msg = "PDB Resource already exists" - pdb.Status.Status = false - pdb.Status.Phase = pdbPhaseFail - return errors.New("Duplicate PDB found") - } - } - return nil -} - -/* -*************************************************************** - - Get the Custom Resource for the CDB mentioned in the PDB Spec - /************************************************************** -*/ -func (r *PDBReconciler) getCDBResource(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) (dbapi.CDB, error) { - - log := r.Log.WithValues("getCDBResource", req.NamespacedName) - - var cdb dbapi.CDB // CDB CR corresponding to the CDB name specified in the PDB spec - - // Name of the CDB CR that holds the ORDS container - cdbResName := pdb.Spec.CDBResName - cdbNamespace := pdb.Spec.CDBNamespace - - // Get CDB CR corresponding to the CDB name specified in the PDB spec - err := r.Get(context.Background(), client.ObjectKey{ - Namespace: cdbNamespace, - Name: cdbResName, - }, &cdb) - - if err != nil { - log.Info("Failed to get CRD for CDB", "Name", cdbResName, "Namespace", pdb.Spec.CDBNamespace, "Error", err.Error()) - pdb.Status.Msg = "Unable to get CRD for CDB : " + cdbResName - r.Status().Update(ctx, pdb) - return cdb, err - } - - log.Info("Found CR for CDB", "Name", cdbResName, "CR Name", cdb.Name) - return cdb, nil -} - -/* -*************************************************************** - - Get the ORDS Pod for the CDB mentioned in the PDB Spec - /************************************************************** -*/ -func (r *PDBReconciler) getORDSPod(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) (corev1.Pod, error) { - - log := r.Log.WithValues("getORDSPod", req.NamespacedName) - - var cdbPod corev1.Pod // ORDS Pod container with connection to the concerned CDB - - // Name of the CDB CR that holds the ORDS container - cdbResName := pdb.Spec.CDBResName - - // Get ORDS Pod associated with the CDB Name specified in the PDB Spec - err := r.Get(context.Background(), client.ObjectKey{ - Namespace: pdb.Spec.CDBNamespace, - Name: cdbResName + "-ords", - }, &cdbPod) - - if err != nil { - log.Info("Failed to get Pod for CDB", "Name", cdbResName, "Namespace", pdb.Spec.CDBNamespace, "Error", err.Error()) - pdb.Status.Msg = "Unable to get ORDS Pod for CDB : " + cdbResName - return cdbPod, err - } - - log.Info("Found ORDS Pod for CDB", "Name", cdbResName, "Pod Name", cdbPod.Name, "ORDS Container hostname", cdbPod.Spec.Hostname) - return cdbPod, nil -} - -/* -************************************************ - - Get Secret Key for a Secret Name - /*********************************************** -*/ -func (r *PDBReconciler) getSecret(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB, secretName string, keyName string) (string, error) { - - log := r.Log.WithValues("getSecret", req.NamespacedName) - - secret := &corev1.Secret{} - err := r.Get(ctx, types.NamespacedName{Name: secretName, Namespace: pdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + secretName) - pdb.Status.Msg = "Secret not found:" + secretName - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - - return string(secret.Data[keyName]), nil -} - -/* -************************************************ - - Issue a REST API Call to the ORDS container - -*********************************************** -*/ -func (r *PDBReconciler) callAPI(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB, url string, payload map[string]string, action string) (string, error) { - log := r.Log.WithValues("callAPI", req.NamespacedName) - - var err error - - secret := &corev1.Secret{} - - err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBTlsKey.Secret.SecretName, Namespace: pdb.Namespace}, secret) - - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.PDBTlsKey.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - rsaKeyPEM := secret.Data[pdb.Spec.PDBTlsKey.Secret.Key] - - err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBTlsCrt.Secret.SecretName, Namespace: pdb.Namespace}, secret) - - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.PDBTlsCrt.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - - rsaCertPEM := secret.Data[pdb.Spec.PDBTlsCrt.Secret.Key] - - err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBTlsCat.Secret.SecretName, Namespace: pdb.Namespace}, secret) - - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.PDBTlsCat.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - - caCert := secret.Data[pdb.Spec.PDBTlsCat.Secret.Key] - - certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) - if err != nil { - pdb.Status.Msg = "Error tls.X509KeyPair" - return "", err - } - - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) - - tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, RootCAs: caCertPool} - - tr := &http.Transport{TLSClientConfig: tlsConf} - - httpclient := &http.Client{Transport: tr} - - log.Info("Issuing REST call", "URL", url, "Action", action) - - /* - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return "", err - } - */ - - err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.WebServerUsr.Secret.SecretName, Namespace: pdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.WebServerUsr.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - - webUser := string(secret.Data[pdb.Spec.WebServerUsr.Secret.Key]) - webUser = strings.TrimSpace(webUser) - - secret = &corev1.Secret{} - err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.WebServerPwd.Secret.SecretName, Namespace: pdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.WebServerPwd.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - webUserPwd := string(secret.Data[pdb.Spec.WebServerPwd.Secret.Key]) - webUserPwd = strings.TrimSpace(webUserPwd) - - var httpreq *http.Request - if action == "GET" { - httpreq, err = http.NewRequest(action, url, nil) - } else { - jsonValue, _ := json.Marshal(payload) - httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) - } - - if err != nil { - log.Info("Unable to create HTTP Request for PDB : "+pdb.Name, "err", err.Error()) - return "", err - } - - httpreq.Header.Add("Accept", "application/json") - httpreq.Header.Add("Content-Type", "application/json") - httpreq.SetBasicAuth(webUser, webUserPwd) - - resp, err := httpclient.Do(httpreq) - if err != nil { - errmsg := err.Error() - log.Error(err, "Failed - Could not connect to ORDS Pod", "err", err.Error()) - pdb.Status.Msg = "Error: Could not connect to ORDS Pod" - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSError", errmsg) - return "", err - } - - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "Done", pdb.Spec.CDBResName) - if resp.StatusCode != http.StatusOK { - bb, _ := ioutil.ReadAll(resp.Body) - - if resp.StatusCode == 404 { - pdb.Status.ConnString = "" - pdb.Status.Msg = pdb.Spec.PDBName + " not found" - - } else { - if floodcontrol == false { - pdb.Status.Msg = "ORDS Error - HTTP Status Code:" + strconv.Itoa(resp.StatusCode) - } - } - - if floodcontrol == false { - log.Info("ORDS Error - HTTP Status Code :"+strconv.Itoa(resp.StatusCode), "Err", string(bb)) - } - - var apiErr ORDSError - json.Unmarshal([]byte(bb), &apiErr) - if floodcontrol == false { - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSError", "Failed: %s", apiErr.Message) - } - //fmt.Printf("%+v", apiErr) - //fmt.Println(string(bb)) - floodcontrol = true - return "", errors.New("ORDS Error") - } - floodcontrol = false - - defer resp.Body.Close() - - bodyBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - fmt.Print(err.Error()) - } - respData := string(bodyBytes) - //fmt.Println(string(bodyBytes)) - - var apiResponse RESTSQLCollection - json.Unmarshal([]byte(bodyBytes), &apiResponse) - //fmt.Printf("%#v", apiResponse) - //fmt.Printf("%+v", apiResponse) - - errFound := false - for _, sqlItem := range apiResponse.Items { - if sqlItem.ErrorDetails != "" { - log.Info("ORDS Error - Oracle Error Code :" + strconv.Itoa(sqlItem.ErrorCode)) - if !errFound { - pdb.Status.Msg = sqlItem.ErrorDetails - } - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "OraError", "%s", sqlItem.ErrorDetails) - errFound = true - } - } - - if errFound { - return "", errors.New("Oracle Error") - } - - return respData, nil -} - -/* -************************************************ - - Create a PDB - -*********************************************** -*/ -func (r *PDBReconciler) createPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - - log := r.Log.WithValues("createPDB", req.NamespacedName) - - var err error - var tdePassword string - var tdeSecret string - - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return err - } - - /*** BEGIN GET ENCPASS ***/ - secret := &corev1.Secret{} - - err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.AdminName.Secret.SecretName, Namespace: pdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.AdminName.Secret.SecretName) - return err - } - log.Error(err, "Unable to get the secret.") - return err - } - pdbAdminNameEnc := string(secret.Data[pdb.Spec.AdminName.Secret.Key]) - pdbAdminNameEnc = strings.TrimSpace(pdbAdminNameEnc) - - err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBPriKey.Secret.SecretName, Namespace: pdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.PDBPriKey.Secret.SecretName) - return err - } - log.Error(err, "Unable to get the secret.") - return err - } - privKey := string(secret.Data[pdb.Spec.PDBPriKey.Secret.Key]) - pdbAdminName, err := lrcommons.CommonDecryptWithPrivKey2(privKey, pdbAdminNameEnc, req) - - // Get Web Server User Password - secret = &corev1.Secret{} - err = r.Get(ctx, types.NamespacedName{Name: pdb.Spec.AdminPwd.Secret.SecretName, Namespace: pdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.AdminPwd.Secret.SecretName) - return err - } - log.Error(err, "Unable to get the secret.") - return err - } - pdbAdminPwdEnc := string(secret.Data[pdb.Spec.AdminPwd.Secret.Key]) - pdbAdminPwdEnc = strings.TrimSpace(pdbAdminPwdEnc) - pdbAdminPwd, err := lrcommons.CommonDecryptWithPrivKey2(privKey, pdbAdminPwdEnc, req) - pdbAdminName = strings.TrimSuffix(pdbAdminName, "\n") - pdbAdminPwd = strings.TrimSuffix(pdbAdminPwd, "\n") - /*** END GET ENCPASS ***/ - - log.Info("====================> " + pdbAdminName + ":" + pdbAdminPwd) - /* Prevent creating an existing pdb */ - err = r.getPDBState(ctx, req, pdb) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Check PDB not existence completed", "PDB Name", pdb.Spec.PDBName) - } - - } else { - log.Info("Database already exists ", "PDB Name", pdb.Spec.PDBName) - return nil - } - - values := map[string]string{ - "method": "CREATE", - "pdb_name": pdb.Spec.PDBName, - "adminName": pdbAdminName, - "adminPwd": pdbAdminPwd, - "fileNameConversions": pdb.Spec.FileNameConversions, - "reuseTempFile": strconv.FormatBool(*(pdb.Spec.ReuseTempFile)), - "unlimitedStorage": strconv.FormatBool(*(pdb.Spec.UnlimitedStorage)), - "totalSize": pdb.Spec.TotalSize, - "tempSize": pdb.Spec.TempSize, - "getScript": strconv.FormatBool(*(pdb.Spec.GetScript))} - - if *(pdb.Spec.TDEImport) { - tdePassword, err = r.getSecret(ctx, req, pdb, pdb.Spec.TDEPassword.Secret.SecretName, pdb.Spec.TDEPassword.Secret.Key) - if err != nil { - return err - } - tdeSecret, err = r.getSecret(ctx, req, pdb, pdb.Spec.TDESecret.Secret.SecretName, pdb.Spec.TDESecret.Secret.Key) - if err != nil { - return err - } - - tdeSecret = tdeSecret[:len(tdeSecret)-1] - tdePassword = tdeSecret[:len(tdePassword)-1] - values["tdePassword"] = tdePassword - values["tdeKeystorePath"] = pdb.Spec.TDEKeystorePath - values["tdeSecret"] = tdeSecret - } - - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" - - pdb.Status.TotalSize = pdb.Spec.TotalSize - pdb.Status.Phase = pdbPhaseCreate - pdb.Status.Msg = "Waiting for PDB to be created" - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") - if err != nil { - log.Error(err, "callAPI error", "err", err.Error()) - return err - } - - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Created", "PDB '%s' created successfully", pdb.Spec.PDBName) - - if cdb.Spec.DBServer != "" { - pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName - } else { - pdb.Status.ConnString = cdb.Spec.DBTnsurl - ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) - } - - assertivePdbDeletion = pdb.Spec.AssertivePdbDeletion - if pdb.Spec.AssertivePdbDeletion == true { - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Created", "PDB '%s' assertive pdb deletion turned on", pdb.Spec.PDBName) - } - log.Info("New connect strinng", "tnsurl", cdb.Spec.DBTnsurl) - log.Info("Created PDB Resource", "PDB Name", pdb.Spec.PDBName) - r.getPDBState(ctx, req, pdb) - return nil -} - -/* -************************************************ - - Clone a PDB - -*********************************************** -*/ -func (r *PDBReconciler) clonePDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - - if pdb.Spec.PDBName == pdb.Spec.SrcPDBName { - return nil - } - - log := r.Log.WithValues("clonePDB", req.NamespacedName) - - var err error - - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return err - } - - /* Prevent cloning an existing pdb */ - err = r.getPDBState(ctx, req, pdb) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Check PDB not existence completed", "PDB Name", pdb.Spec.PDBName) - } - - } else { - log.Info("Database already exists ", "PDB Name", pdb.Spec.PDBName) - return nil - } - - values := map[string]string{ - "method": "CLONE", - "clonePDBName": pdb.Spec.PDBName, - "reuseTempFile": strconv.FormatBool(*(pdb.Spec.ReuseTempFile)), - "unlimitedStorage": strconv.FormatBool(*(pdb.Spec.UnlimitedStorage)), - "getScript": strconv.FormatBool(*(pdb.Spec.GetScript))} - - if pdb.Spec.SparseClonePath != "" { - values["sparseClonePath"] = pdb.Spec.SparseClonePath - } - if pdb.Spec.FileNameConversions != "" { - values["fileNameConversions"] = pdb.Spec.FileNameConversions - } - if pdb.Spec.TotalSize != "" { - values["totalSize"] = pdb.Spec.TotalSize - } - if pdb.Spec.TempSize != "" { - values["tempSize"] = pdb.Spec.TempSize - } - - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.SrcPDBName + "/" - - pdb.Status.Phase = pdbPhaseClone - pdb.Status.Msg = "Waiting for PDB to be cloned" - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") - if err != nil { - return err - } - - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Created", "PDB '%s' cloned successfully", pdb.Spec.PDBName) - - if cdb.Spec.DBServer != "" { - pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName - } else { - pdb.Status.ConnString = cdb.Spec.DBTnsurl - ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) - } - - assertivePdbDeletion = pdb.Spec.AssertivePdbDeletion - if pdb.Spec.AssertivePdbDeletion == true { - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Clone", "PDB '%s' assertive pdb deletion turned on", pdb.Spec.PDBName) - } - - log.Info("Cloned PDB successfully", "Source PDB Name", pdb.Spec.SrcPDBName, "Clone PDB Name", pdb.Spec.PDBName) - r.getPDBState(ctx, req, pdb) - return nil -} - -/* -************************************************ - - Plug a PDB - -*********************************************** -*/ -func (r *PDBReconciler) plugPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - - log := r.Log.WithValues("plugPDB", req.NamespacedName) - - var err error - var tdePassword string - var tdeSecret string - - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return err - } - - values := map[string]string{ - "method": "PLUG", - "xmlFileName": pdb.Spec.XMLFileName, - "pdb_name": pdb.Spec.PDBName, - //"adminName": pdbAdminName, - //"adminPwd": pdbAdminPwd, - "sourceFileNameConversions": pdb.Spec.SourceFileNameConversions, - "copyAction": pdb.Spec.CopyAction, - "fileNameConversions": pdb.Spec.FileNameConversions, - "unlimitedStorage": strconv.FormatBool(*(pdb.Spec.UnlimitedStorage)), - "reuseTempFile": strconv.FormatBool(*(pdb.Spec.ReuseTempFile)), - "totalSize": pdb.Spec.TotalSize, - "tempSize": pdb.Spec.TempSize, - "getScript": strconv.FormatBool(*(pdb.Spec.GetScript))} - - if *(pdb.Spec.TDEImport) { - tdePassword, err = r.getSecret(ctx, req, pdb, pdb.Spec.TDEPassword.Secret.SecretName, pdb.Spec.TDEPassword.Secret.Key) - if err != nil { - return err - } - tdeSecret, err = r.getSecret(ctx, req, pdb, pdb.Spec.TDESecret.Secret.SecretName, pdb.Spec.TDESecret.Secret.Key) - if err != nil { - return err - } - - tdeSecret = tdeSecret[:len(tdeSecret)-1] - tdePassword = tdeSecret[:len(tdePassword)-1] - values["tdePassword"] = tdePassword - values["tdeKeystorePath"] = pdb.Spec.TDEKeystorePath - values["tdeSecret"] = tdeSecret - values["tdeImport"] = strconv.FormatBool(*(pdb.Spec.TDEImport)) - } - if *(pdb.Spec.AsClone) { - values["asClone"] = strconv.FormatBool(*(pdb.Spec.AsClone)) - } - - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" - - pdb.Status.TotalSize = pdb.Spec.TotalSize - pdb.Status.Phase = pdbPhasePlug - pdb.Status.Msg = "Waiting for PDB to be plugged" - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") - if err != nil { - return err - } - - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Created", "PDB '%s' plugged successfully", pdb.Spec.PDBName) - - if cdb.Spec.DBServer != "" { - pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName - } else { - pdb.Status.ConnString = cdb.Spec.DBTnsurl - ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) - } - - assertivePdbDeletion = pdb.Spec.AssertivePdbDeletion - if pdb.Spec.AssertivePdbDeletion == true { - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Plugged", "PDB '%s' assertive pdb deletion turned on", pdb.Spec.PDBName) - } - - log.Info("Successfully plugged PDB", "PDB Name", pdb.Spec.PDBName) - r.getPDBState(ctx, req, pdb) - return nil -} - -/* -************************************************ - - Unplug a PDB - -*********************************************** -*/ -func (r *PDBReconciler) unplugPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - - log := r.Log.WithValues("unplugPDB", req.NamespacedName) - - var err error - var tdePassword string - var tdeSecret string - - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return err - } - - values := map[string]string{ - "method": "UNPLUG", - "xmlFileName": pdb.Spec.XMLFileName, - "getScript": strconv.FormatBool(*(pdb.Spec.GetScript))} - - if *(pdb.Spec.TDEExport) { - // Get the TDE Password - tdePassword, err = r.getSecret(ctx, req, pdb, pdb.Spec.TDEPassword.Secret.SecretName, pdb.Spec.TDEPassword.Secret.Key) - if err != nil { - return err - } - tdeSecret, err = r.getSecret(ctx, req, pdb, pdb.Spec.TDESecret.Secret.SecretName, pdb.Spec.TDESecret.Secret.Key) - if err != nil { - return err - } - - tdeSecret = tdeSecret[:len(tdeSecret)-1] - tdePassword = tdeSecret[:len(tdePassword)-1] - values["tdePassword"] = tdePassword - values["tdeKeystorePath"] = pdb.Spec.TDEKeystorePath - values["tdeSecret"] = tdeSecret - values["tdeExport"] = strconv.FormatBool(*(pdb.Spec.TDEExport)) - } - - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdb.Spec.PDBName + "/" - - log.Info("CallAPI(url)", "url", url) - - pdb.Status.Phase = pdbPhaseUnplug - pdb.Status.Msg = "Waiting for PDB to be unplugged" - - if cdb.Spec.DBServer != "" { - pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName - } else { - pdb.Status.ConnString = cdb.Spec.DBTnsurl - ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) - } - - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") - if err != nil { - return err - } - - if controllerutil.ContainsFinalizer(pdb, PDBFinalizer) { - log.Info("Removing finalizer") - controllerutil.RemoveFinalizer(pdb, PDBFinalizer) - err := r.Update(ctx, pdb) - if err != nil { - log.Info("Could not remove finalizer", "err", err.Error()) - return err - } - pdb.Status.Status = true - err = r.Delete(context.Background(), pdb, client.GracePeriodSeconds(1)) - if err != nil { - log.Info("Could not delete PDB resource", "err", err.Error()) - return err - } - } - - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Unplugged", "PDB '%s' unplugged successfully", pdb.Spec.PDBName) - - log.Info("Successfully unplugged PDB resource") - return nil -} - -/* -************************************************ - - Modify a PDB state - /*********************************************** -*/ -func (r *PDBReconciler) modifyPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - - log := r.Log.WithValues("modifyPDB", req.NamespacedName) - - var err error - - err = r.getPDBState(ctx, req, pdb) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Warning PDB does not exist", "PDB Name", pdb.Spec.PDBName) - return nil - } - return err - } - - if pdb.Status.OpenMode == "READ WRITE" && pdb.Spec.PDBState == "OPEN" && pdb.Spec.ModifyOption == "READ WRITE" { - /* Database is already open no action required */ - return nil - } - - if pdb.Status.OpenMode == "MOUNTED" && pdb.Spec.PDBState == "CLOSE" && pdb.Spec.ModifyOption == "IMMEDIATE" { - /* Database is already close no action required */ - return nil - } - - // To prevent Reconcile from Modifying again whenever the Operator gets re-started - /* - modOption := pdb.Spec.PDBState + "-" + pdb.Spec.ModifyOption - if pdb.Status.ModifyOption == modOption { - return nil - } - */ - - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return err - } - - values := map[string]string{ - "state": pdb.Spec.PDBState, - "modifyOption": pdb.Spec.ModifyOption, - "getScript": strconv.FormatBool(*(pdb.Spec.GetScript))} - log.Info("MODIFY PDB", "pdb.Spec.PDBState=", pdb.Spec.PDBState, "pdb.Spec.ModifyOption=", pdb.Spec.ModifyOption) - log.Info("PDB STATUS OPENMODE", "pdb.Status.OpenMode=", pdb.Status.OpenMode) - - pdbName := pdb.Spec.PDBName - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" - - pdb.Status.Phase = pdbPhaseModify - pdb.Status.ModifyOption = pdb.Spec.PDBState + "-" + pdb.Spec.ModifyOption - pdb.Status.Msg = "Waiting for PDB to be modified" - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - _, err = NewCallApi(r, ctx, req, pdb, url, values, "POST") - if err != nil { - return err - } - - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Modified", "PDB '%s' modified successfully", pdb.Spec.PDBName) - - if cdb.Spec.DBServer != "" { - pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName - } else { - pdb.Status.ConnString = cdb.Spec.DBTnsurl - } - - log.Info("Successfully modified PDB state", "PDB Name", pdb.Spec.PDBName) - r.getPDBState(ctx, req, pdb) - return nil -} - -/* -************************************************ - - Get PDB State - /*********************************************** -*/ -func (r *PDBReconciler) getPDBState(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - - log := r.Log.WithValues("getPDBState", req.NamespacedName) - - var err error - - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return err - } - - pdbName := pdb.Spec.PDBName - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" - - pdb.Status.Msg = "Getting PDB state" - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - - respData, err := NewCallApi(r, ctx, req, pdb, url, nil, "GET") - - if err != nil { - pdb.Status.OpenMode = "UNKNOWN" - pdb.Status.Msg = "CHECK PDB STATUS" - pdb.Status.Status = false - return err - } - - var objmap map[string]interface{} - if err := json.Unmarshal([]byte(respData), &objmap); err != nil { - log.Error(err, "Failed to get state of PDB :"+pdbName, "err", err.Error()) - } - - pdb.Status.OpenMode = objmap["open_mode"].(string) - - if pdb.Status.OpenMode == "READ WRITE" { - err := r.mapPDB(ctx, req, pdb) - if err != nil { - log.Info("Fail to Map resource getting PDB state") - } - } - - log.Info("Successfully obtained PDB state", "PDB Name", pdb.Spec.PDBName, "State", objmap["open_mode"].(string)) - return nil -} - -/* -************************************************ - - Map Database PDB to Kubernetes PDB CR - /*********************************************** -*/ -func (r *PDBReconciler) mapPDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - - log := r.Log.WithValues("mapPDB", req.NamespacedName) - - var err error - - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return err - } - - pdbName := pdb.Spec.PDBName - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" - - pdb.Status.Msg = "Mapping PDB" - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - - respData, err := NewCallApi(r, ctx, req, pdb, url, nil, "GET") - - if err != nil { - pdb.Status.OpenMode = "UNKNOWN" - return err - } - - var objmap map[string]interface{} - if err := json.Unmarshal([]byte(respData), &objmap); err != nil { - log.Error(err, "Failed to get state of PDB :"+pdbName, "err", err.Error()) - } - - totSizeInBytes := objmap["total_size"].(float64) - totSizeInGB := totSizeInBytes / 1024 / 1024 / 1024 - - pdb.Status.OpenMode = objmap["open_mode"].(string) - pdb.Status.TotalSize = fmt.Sprintf("%.2f", totSizeInGB) + "G" - assertivePdbDeletion = pdb.Spec.AssertivePdbDeletion - if pdb.Spec.AssertivePdbDeletion == true { - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Mapped", "PDB '%s' assertive pdb deletion turned on", pdb.Spec.PDBName) - } - - if cdb.Spec.DBServer != "" { - pdb.Status.ConnString = cdb.Spec.DBServer + ":" + strconv.Itoa(cdb.Spec.DBPort) + "/" + pdb.Spec.PDBName - } else { - pdb.Status.ConnString = cdb.Spec.DBTnsurl - ParseTnsAlias(&(pdb.Status.ConnString), &(pdb.Spec.PDBName)) - } - - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - - log.Info("Successfully mapped PDB to Kubernetes resource", "PDB Name", pdb.Spec.PDBName) - return nil -} - -/* -************************************************ - - Delete a PDB - /*********************************************** -*/ -func (r *PDBReconciler) deletePDB(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - - log := r.Log.WithValues("deletePDB", req.NamespacedName) - - err := r.deletePDBInstance(req, ctx, pdb) - if err != nil { - log.Info("Could not delete PDB", "PDB Name", pdb.Spec.PDBName, "err", err.Error()) - return err - } - - if controllerutil.ContainsFinalizer(pdb, PDBFinalizer) { - log.Info("Removing finalizer") - controllerutil.RemoveFinalizer(pdb, PDBFinalizer) - err := r.Update(ctx, pdb) - if err != nil { - log.Info("Could not remove finalizer", "err", err.Error()) - return err - } - pdb.Status.Status = true - err = r.Delete(context.Background(), pdb, client.GracePeriodSeconds(1)) - if err != nil { - log.Info("Could not delete PDB resource", "err", err.Error()) - return err - } - } - - r.Recorder.Eventf(pdb, corev1.EventTypeNormal, "Deleted", "PDB '%s' dropped successfully", pdb.Spec.PDBName) - - log.Info("Successfully deleted PDB resource") - return nil -} - -/************************************************* - - Check PDB deletion -**************************************************/ - -func (r *PDBReconciler) managePDBDeletion2(ctx context.Context, req ctrl.Request, pdb *dbapi.PDB) error { - log := r.Log.WithValues("managePDBDeletion", req.NamespacedName) - if pdb.ObjectMeta.DeletionTimestamp.IsZero() { - if !controllerutil.ContainsFinalizer(pdb, PDBFinalizer) { - controllerutil.AddFinalizer(pdb, PDBFinalizer) - if err := r.Update(ctx, pdb); err != nil { - return err - } - } - } else { - log.Info("Pdb marked to be delted") - if controllerutil.ContainsFinalizer(pdb, PDBFinalizer) { - if assertivePdbDeletion == true { - log.Info("Deleting pdb CRD: Assertive approach is turned on ") - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - log.Error(err, "Cannont find cdb resource ", "err", err.Error()) - return err - } - - var errclose error - pdbName := pdb.Spec.PDBName - if pdb.Status.OpenMode == "READ WRITE" { - valuesclose := map[string]string{ - "state": "CLOSE", - "modifyOption": "IMMEDIATE", - "getScript": "FALSE"} - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/status" - _, errclose = NewCallApi(r, ctx, req, pdb, url, valuesclose, "POST") - if errclose != nil { - log.Info("Warning error closing pdb continue anyway") - } - } - - if errclose == nil { - valuesdrop := map[string]string{ - "action": "INCLUDING", - "getScript": "FALSE"} - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" - - log.Info("Call Delete()") - _, errdelete := NewCallApi(r, ctx, req, pdb, url, valuesdrop, "DELETE") - if errdelete != nil { - log.Error(errdelete, "Fail to delete pdb :"+pdb.Name, "err", errdelete.Error()) - return errdelete - } - } - - } /* END OF ASSERTIVE SECTION */ - - log.Info("Marked to be deleted") - pdb.Status.Phase = pdbPhaseDelete - pdb.Status.Status = true - r.Status().Update(ctx, pdb) - - controllerutil.RemoveFinalizer(pdb, PDBFinalizer) - if err := r.Update(ctx, pdb); err != nil { - log.Info("Cannot remove finalizer") - return err - } - - } - - return nil - } - - return nil -} - -/* -************************************************ - - Finalization logic for PDBFinalizer - /*********************************************** -*/ -func (r *PDBReconciler) deletePDBInstance(req ctrl.Request, ctx context.Context, pdb *dbapi.PDB) error { - - log := r.Log.WithValues("deletePDBInstance", req.NamespacedName) - - var err error - - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return err - } - - values := map[string]string{ - "action": "KEEP", - "getScript": strconv.FormatBool(*(pdb.Spec.GetScript))} - - if pdb.Spec.DropAction != "" { - values["action"] = pdb.Spec.DropAction - } - - pdbName := pdb.Spec.PDBName - url := "https://" + pdb.Spec.CDBResName + "-ords." + pdb.Spec.CDBNamespace + ":" + strconv.Itoa(cdb.Spec.ORDSPort) + "/ords/_/db-api/latest/database/pdbs/" + pdbName + "/" - - pdb.Status.Phase = pdbPhaseDelete - pdb.Status.Msg = "Waiting for PDB to be deleted" - if err := r.Status().Update(ctx, pdb); err != nil { - log.Error(err, "Failed to update status for :"+pdb.Name, "err", err.Error()) - } - _, err = NewCallApi(r, ctx, req, pdb, url, values, "DELETE") - if err != nil { - pdb.Status.ConnString = "" - return err - } - - log.Info("Successfully dropped PDB", "PDB Name", pdbName) - return nil -} - -/* -************************************************************* - - SetupWithManager sets up the controller with the Manager. - /************************************************************ -*/ -func (r *PDBReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&dbapi.PDB{}). - WithEventFilter(predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - // Ignore updates to CR status in which case metadata.Generation does not change - return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() - }, - DeleteFunc: func(e event.DeleteEvent) bool { - // Evaluates to false if the object has been confirmed deleted. - //return !e.DeleteStateUnknown - return false - }, - }). - WithOptions(controller.Options{MaxConcurrentReconciles: 100}). - Complete(r) -} - -/************************************************************* -Enh 35357707 - PROVIDE THE PDB TNSALIAS INFORMATION -**************************************************************/ - -func ParseTnsAlias(tns *string, pdbsrv *string) { - var swaptns string - fmt.Printf("Analyzing string [%s]\n", *tns) - fmt.Printf("Relacing srv [%s]\n", *pdbsrv) - - if strings.Contains(strings.ToUpper(*tns), "SERVICE_NAME") == false { - fmt.Print("Cannot generate tns alias for pdb") - return - } - - if strings.Contains(strings.ToUpper(*tns), "ORACLE_SID") == true { - fmt.Print("Cannot generate tns alias for pdb") - return - } - - swaptns = fmt.Sprintf("SERVICE_NAME=%s", *pdbsrv) - tnsreg := regexp.MustCompile(`SERVICE_NAME=\w+`) - *tns = tnsreg.ReplaceAllString(*tns, swaptns) - - fmt.Printf("Newstring [%s]\n", *tns) - -} - -func NewCallApi(intr interface{}, ctx context.Context, req ctrl.Request, pdb *dbapi.PDB, url string, payload map[string]string, action string) (string, error) { - - var c client.Client - var r logr.Logger - var e record.EventRecorder - var err error - - recpdb, ok1 := intr.(*PDBReconciler) - if ok1 { - fmt.Printf("func NewCallApi ((*PDBReconciler),......)\n") - c = recpdb.Client - e = recpdb.Recorder - r = recpdb.Log - } - - reccdb, ok2 := intr.(*CDBReconciler) - if ok2 { - fmt.Printf("func NewCallApi ((*CDBReconciler),......)\n") - c = reccdb.Client - e = reccdb.Recorder - r = reccdb.Log - } - - secret := &corev1.Secret{} - - log := r.WithValues("NewCallApi", req.NamespacedName) - log.Info("Call c.Get") - err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBTlsKey.Secret.SecretName, Namespace: pdb.Namespace}, secret) - - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.PDBTlsKey.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - rsaKeyPEM := secret.Data[pdb.Spec.PDBTlsKey.Secret.Key] - - err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBTlsCrt.Secret.SecretName, Namespace: pdb.Namespace}, secret) - - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.PDBTlsCrt.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - - rsaCertPEM := secret.Data[pdb.Spec.PDBTlsCrt.Secret.Key] - - err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBTlsCat.Secret.SecretName, Namespace: pdb.Namespace}, secret) - - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.PDBTlsCat.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - - caCert := secret.Data[pdb.Spec.PDBTlsCat.Secret.Key] - /* - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSINFO", string(rsaKeyPEM)) - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSINFO", string(rsaCertPEM)) - r.Recorder.Eventf(pdb, corev1.EventTypeWarning, "ORDSINFO", string(caCert)) - */ - - certificate, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM)) - if err != nil { - pdb.Status.Msg = "Error tls.X509KeyPair" - return "", err - } - - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) - - tlsConf := &tls.Config{Certificates: []tls.Certificate{certificate}, RootCAs: caCertPool} - - tr := &http.Transport{TLSClientConfig: tlsConf} - - httpclient := &http.Client{Transport: tr} - - log.Info("Issuing REST call", "URL", url, "Action", action) - - /* - cdb, err := r.getCDBResource(ctx, req, pdb) - if err != nil { - return "", err - } - */ - - err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.WebServerUsr.Secret.SecretName, Namespace: pdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.WebServerUsr.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - webUserEnc := string(secret.Data[pdb.Spec.WebServerUsr.Secret.Key]) - webUserEnc = strings.TrimSpace(webUserEnc) - - err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.PDBPriKey.Secret.SecretName, Namespace: pdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.PDBPriKey.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - privKey := string(secret.Data[pdb.Spec.PDBPriKey.Secret.Key]) - webUser, err := lrcommons.CommonDecryptWithPrivKey2(privKey, webUserEnc, req) - - // Get Web Server User Password - secret = &corev1.Secret{} - err = c.Get(ctx, types.NamespacedName{Name: pdb.Spec.WebServerPwd.Secret.SecretName, Namespace: pdb.Namespace}, secret) - if err != nil { - if apierrors.IsNotFound(err) { - log.Info("Secret not found:" + pdb.Spec.WebServerPwd.Secret.SecretName) - return "", err - } - log.Error(err, "Unable to get the secret.") - return "", err - } - webUserPwdEnc := string(secret.Data[pdb.Spec.WebServerPwd.Secret.Key]) - webUserPwdEnc = strings.TrimSpace(webUserPwdEnc) - webUserPwd, err := lrcommons.CommonDecryptWithPrivKey2(privKey, webUserPwdEnc, req) - /////////////////////////////////////////////////////////////////////////////////// - - var httpreq *http.Request - if action == "GET" { - httpreq, err = http.NewRequest(action, url, nil) - } else { - jsonValue, _ := json.Marshal(payload) - httpreq, err = http.NewRequest(action, url, bytes.NewBuffer(jsonValue)) - } - - if err != nil { - log.Info("Unable to create HTTP Request for PDB : "+pdb.Name, "err", err.Error()) - return "", err - } - - httpreq.Header.Add("Accept", "application/json") - httpreq.Header.Add("Content-Type", "application/json") - httpreq.SetBasicAuth(webUser, webUserPwd) - - resp, err := httpclient.Do(httpreq) - if err != nil { - errmsg := err.Error() - log.Error(err, "Failed - Could not connect to ORDS Pod", "err", err.Error()) - pdb.Status.Msg = "Error: Could not connect to ORDS Pod" - e.Eventf(pdb, corev1.EventTypeWarning, "ORDSError", errmsg) - return "", err - } - - e.Eventf(pdb, corev1.EventTypeWarning, "Done", pdb.Spec.CDBResName) - if resp.StatusCode != http.StatusOK { - bb, _ := ioutil.ReadAll(resp.Body) - - if resp.StatusCode == 404 { - pdb.Status.ConnString = "" - pdb.Status.Msg = pdb.Spec.PDBName + " not found" - - } else { - if floodcontrol == false { - pdb.Status.Msg = "ORDS Error - HTTP Status Code:" + strconv.Itoa(resp.StatusCode) - } - } - - if floodcontrol == false { - log.Info("ORDS Error - HTTP Status Code :"+strconv.Itoa(resp.StatusCode), "Err", string(bb)) - } - - var apiErr ORDSError - json.Unmarshal([]byte(bb), &apiErr) - if floodcontrol == false { - e.Eventf(pdb, corev1.EventTypeWarning, "ORDSError", "Failed: %s", apiErr.Message) - } - //fmt.Printf("%+v", apiErr) - //fmt.Println(string(bb)) - floodcontrol = true - return "", errors.New("ORDS Error") - } - floodcontrol = false - - defer resp.Body.Close() - - bodyBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - fmt.Print(err.Error()) - } - respData := string(bodyBytes) - //fmt.Println(string(bodyBytes)) - - var apiResponse RESTSQLCollection - json.Unmarshal([]byte(bodyBytes), &apiResponse) - //fmt.Printf("%#v", apiResponse) - //fmt.Printf("%+v", apiResponse) - - errFound := false - for _, sqlItem := range apiResponse.Items { - if sqlItem.ErrorDetails != "" { - log.Info("ORDS Error - Oracle Error Code :" + strconv.Itoa(sqlItem.ErrorCode)) - if !errFound { - pdb.Status.Msg = sqlItem.ErrorDetails - } - e.Eventf(pdb, corev1.EventTypeWarning, "OraError", "%s", sqlItem.ErrorDetails) - errFound = true - } - } - - if errFound { - return "", errors.New("Oracle Error") - } - - return respData, nil -} diff --git a/main.go b/main.go index 38f0e994..0b49deef 100644 --- a/main.go +++ b/main.go @@ -248,18 +248,10 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "OracleRestDataService") os.Exit(1) } - if err = (&databasev4.PDB{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "PDB") - os.Exit(1) - } if err = (&databasev4.LRPDB{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "LRPDB") os.Exit(1) } - if err = (&databasev4.CDB{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "CDB") - os.Exit(1) - } if err = (&databasev4.LREST{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "LREST") os.Exit(1) @@ -350,18 +342,6 @@ func main() { } } - // PDB Reconciler - if err = (&databasecontroller.PDBReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("PDB"), - Interval: time.Duration(i), - Recorder: mgr.GetEventRecorderFor("PDB"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "PDB") - os.Exit(1) - } - // LRPDBR Reconciler if err = (&databasecontroller.LRPDBReconciler{ Client: mgr.GetClient(), @@ -374,19 +354,6 @@ func main() { os.Exit(1) } - // CDB Reconciler - if err = (&databasecontroller.CDBReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Config: mgr.GetConfig(), - Log: ctrl.Log.WithName("controllers").WithName("CDB"), - Interval: time.Duration(i), - Recorder: mgr.GetEventRecorderFor("CDB"), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "CDB") - os.Exit(1) - } - // LREST Reconciler if err = (&databasecontroller.LRESTReconciler{ Client: mgr.GetClient(), @@ -447,15 +414,6 @@ func main() { } // +kubebuilder:scaffold:builder - // Add index for PDB CR to enable mgr to cache PDBs - indexFunc := func(obj client.Object) []string { - return []string{obj.(*databasev4.PDB).Spec.PDBName} - } - if err = cache.IndexField(context.TODO(), &databasev4.PDB{}, "spec.pdbName", indexFunc); err != nil { - setupLog.Error(err, "unable to create index function for ", "controller", "PDB") - os.Exit(1) - } - indexFunc2 := func(obj client.Object) []string { return []string{obj.(*databasev4.LRPDB).Spec.LRPDBName} } diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index d2648d20..e945dbb2 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -1303,46 +1303,30 @@ metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 - name: cdbs.database.oracle.com + name: databaseobservers.observability.oracle.com spec: - group: database.oracle.com + group: observability.oracle.com names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb + kind: DatabaseObserver + listKind: DatabaseObserverList + plural: databaseobservers + shortNames: + - dbobserver + - dbobservers + singular: databaseobserver scope: Namespaced versions: - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server + - jsonPath: .status.metricsConfig + name: MetricsConfig type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase + - jsonPath: .status.status name: Status type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING + - jsonPath: .status.version + name: Version type: string - name: v1alpha1 + name: v1 schema: openAPIV3Schema: properties: @@ -1354,909 +1338,432 @@ spec: type: object spec: properties: - cdbAdminPwd: + azureConfig: properties: - secret: + configMap: properties: key: type: string - secretName: + name: type: string - required: - - key - - secretName type: object - required: - - secret type: object - cdbAdminUser: + database: properties: - secret: + azure: properties: - key: + vaultID: type: string - secretName: + vaultPasswordSecret: + type: string + vaultUsernameSecret: type: string - required: - - key - - secretName type: object - required: - - secret - type: object - cdbName: - type: string - cdbOrdsPrvKey: - properties: - secret: + dbConnectionString: properties: + envName: + type: string key: type: string - secretName: + secret: type: string - required: - - key - - secretName type: object - required: - - secret - type: object - cdbOrdsPubKey: - properties: - secret: + dbPassword: properties: + envName: + type: string key: type: string - secretName: + secret: type: string - required: - - key - - secretName type: object - required: - - secret - type: object - cdbTlsCrt: - properties: - secret: + dbUser: properties: + envName: + type: string key: type: string - secretName: + secret: type: string - required: - - key - - secretName type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: + oci: properties: - key: + vaultID: type: string - secretName: + vaultPasswordSecret: type: string - required: - - key - - secretName type: object - required: - - secret type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - nodeSelector: + databases: additionalProperties: - type: string + properties: + dbConnectionString: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + dbPassword: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + dbUser: + properties: + envName: + type: string + key: + type: string + secret: + type: string + type: object + type: object type: object - ordsImage: - type: string - ordsImagePullPolicy: - enum: - - Always - - Never - type: string - ordsImagePullSecret: - type: string - ordsPort: - type: integer - ordsPwd: + deployment: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName + args: + items: + type: string + type: array + commands: + items: + type: string + type: array + env: + additionalProperties: + type: string type: object - required: - - secret - type: object - replicas: - type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName + image: + type: string + labels: + additionalProperties: + type: string type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: + podSecurityContext: properties: - key: - type: string - secretName: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: type: string - secretName: + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: type: string - required: - - key - - secretName + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - cdbAdminPwd: - properties: - secret: + podTemplate: properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName + labels: + additionalProperties: + type: string + type: object type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: + securityContext: properties: - key: - type: string - secretName: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: type: string - required: - - key - - secretName + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object type: object - required: - - secret type: object - cdbName: - type: string - cdbOrdsPrvKey: + exporterConfig: properties: - secret: + configMap: properties: key: type: string - secretName: + name: type: string - required: - - key - - secretName type: object - required: - - secret + mountPath: + type: string type: object - cdbOrdsPubKey: + inheritLabels: + items: + type: string + type: array + log: properties: - secret: + destination: + type: string + disable: + type: boolean + filename: + type: string + volume: properties: - key: - type: string - secretName: + name: type: string - required: - - key - - secretName + persistentVolumeClaim: + properties: + claimName: + type: string + type: object type: object - required: - - secret type: object - cdbTlsCrt: + metrics: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + configMap: + items: + properties: + key: + type: string + name: + type: string + type: object + type: array type: object - cdbTlsKey: + ociConfig: properties: - secret: + configMap: properties: key: type: string - secretName: + name: type: string - required: - - key - - secretName type: object - required: - - secret - type: object - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - ordsImage: - type: string - ordsImagePullPolicy: - enum: - - Always - - Never - type: string - ordsImagePullSecret: - type: string - ordsPort: - type: integer - ordsPwd: - properties: - secret: + mountPath: + type: string + privateKey: properties: - key: - type: string - secretName: + secret: type: string - required: - - key - - secretName type: object - required: - - secret type: object replicas: + format: int32 type: integer - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - properties: - msg: - type: string - phase: - type: string - status: - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 - name: databaseobservers.observability.oracle.com -spec: - group: observability.oracle.com - names: - kind: DatabaseObserver - listKind: DatabaseObserverList - plural: databaseobservers - shortNames: - - dbobserver - - dbobservers - singular: databaseobserver - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.metricsConfig - name: MetricsConfig - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.version - name: Version - type: string - name: v1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - azureConfig: - properties: - configMap: - properties: - key: - type: string - name: - type: string - type: object - type: object - database: + service: properties: - azure: - properties: - vaultID: - type: string - vaultPasswordSecret: - type: string - vaultUsernameSecret: - type: string - type: object - dbConnectionString: - properties: - envName: - type: string - key: - type: string - secret: - type: string - type: object - dbPassword: - properties: - envName: - type: string - key: - type: string - secret: - type: string - type: object - dbUser: - properties: - envName: - type: string - key: - type: string - secret: - type: string - type: object - oci: - properties: - vaultID: - type: string - vaultPasswordSecret: - type: string + labels: + additionalProperties: + type: string type: object - type: object - databases: - additionalProperties: - properties: - dbConnectionString: + ports: + items: properties: - envName: - type: string - key: + appProtocol: type: string - secret: + name: type: string - type: object - dbPassword: - properties: - envName: - type: string - key: - type: string - secret: - type: string - type: object - dbUser: - properties: - envName: - type: string - key: - type: string - secret: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port type: object - type: object + type: array type: object - deployment: + serviceMonitor: properties: - args: - items: - type: string - type: array - commands: + endpoints: items: - type: string - type: array - env: - additionalProperties: - type: string - type: object - image: - type: string - labels: - additionalProperties: - type: string - type: object - podSecurityContext: - properties: - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxChangePolicy: - type: string - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - x-kubernetes-list-type: atomic - supplementalGroupsPolicy: - type: string - sysctls: - items: + properties: + authorization: properties: - name: - type: string - value: + credentials: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: type: string - required: - - name - - value type: object - type: array - x-kubernetes-list-type: atomic - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object - podTemplate: - properties: - labels: - additionalProperties: + basicAuth: + properties: + password: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: type: string - type: object - type: object - securityContext: - properties: - allowPrivilegeEscalation: - type: boolean - appArmorProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - capabilities: - properties: - add: - items: + bearerTokenSecret: + properties: + key: type: string - type: array - x-kubernetes-list-type: atomic - drop: - items: - type: string - type: array - x-kubernetes-list-type: atomic - type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - hostProcess: - type: boolean - runAsUserName: - type: string - type: object - type: object - type: object - exporterConfig: - properties: - configMap: - properties: - key: - type: string - name: - type: string - type: object - mountPath: - type: string - type: object - inheritLabels: - items: - type: string - type: array - log: - properties: - destination: - type: string - disable: - type: boolean - filename: - type: string - volume: - properties: - name: - type: string - persistentVolumeClaim: - properties: - claimName: - type: string - type: object - type: object - type: object - metrics: - properties: - configMap: - items: - properties: - key: - type: string - name: - type: string - type: object - type: array - type: object - ociConfig: - properties: - configMap: - properties: - key: - type: string - name: - type: string - type: object - mountPath: - type: string - privateKey: - properties: - secret: - type: string - type: object - type: object - replicas: - format: int32 - type: integer - service: - properties: - labels: - additionalProperties: - type: string - type: object - ports: - items: - properties: - appProtocol: - type: string - name: - type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - type: object - serviceMonitor: - properties: - endpoints: - items: - properties: - authorization: - properties: - credentials: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - type: string - type: object - basicAuth: - properties: - password: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenFile: - type: string - bearerTokenSecret: - properties: - key: - type: string - name: - default: "" + name: + default: "" type: string optional: type: boolean @@ -8995,1470 +8502,77 @@ spec: required: - name type: object - type: array - type: object - wallet: - properties: - additional: - items: - properties: - mountPath: - type: string - name: - type: string - secret: - type: string - type: object - type: array - mountPath: - type: string - secret: - type: string - type: object - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - metricsConfig: - type: string - replicas: - type: integer - status: - type: string - version: - type: string - required: - - conditions - - metricsConfig - - version - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - shortNames: - - dgbroker - - dgbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.fastStartFailover - name: FSFO - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - fastStartFailover: - type: boolean - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - properties: - clusterConnectString: - type: string - databasesInDataguardConfig: - additionalProperties: - type: string - type: object - externalConnectString: - type: string - fastStartFailover: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.fastStartFailover - name: FSFO - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - fastStartFailover: - type: boolean - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - properties: - clusterConnectString: - type: string - databasesInDataguardConfig: - additionalProperties: - type: string - type: object - externalConnectString: - type: string - fastStartFailover: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 - name: dbcssystems.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DbcsSystem - listKind: DbcsSystemList - plural: dbcssystems - singular: dbcssystem - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - databaseId: - type: string - dbBackupId: - type: string - dbClone: - properties: - dbAdminPaswordSecret: - type: string - dbDbUniqueName: - type: string - dbName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - privateIp: - type: string - sidPrefix: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - tdeWalletPasswordSecret: - type: string - required: - - dbDbUniqueName - - dbName - - displayName - - hostName - - subnetId - type: object - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - ociConfigMap: - type: string - ociSecret: - type: string - pdbConfigs: - items: - properties: - freeformTags: - additionalProperties: - type: string - type: object - isDelete: - type: boolean - pdbAdminPassword: - type: string - pdbName: - type: string - pluggableDatabaseId: - type: string - shouldPdbAdminAccountBeLocked: - type: boolean - tdeWalletPassword: - type: string - required: - - freeformTags - - pdbAdminPassword - - pdbName - - shouldPdbAdminAccountBeLocked - - tdeWalletPassword - type: object - type: array - setupDBCloning: - type: boolean - required: - - ociConfigMap - type: object - status: - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbCloneStatus: - properties: - dbAdminPaswordSecret: - type: string - dbDbUniqueName: - type: string - dbName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - id: - type: string - licenseModel: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - required: - - dbDbUniqueName - - hostName - type: object - dbEdition: - type: string - dbInfo: - items: - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - kmsDetailsStatus: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyId: - type: string - keyName: - type: string - managementEndpoint: - type: string - vaultId: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - pdbDetailsStatus: - items: - properties: - pdbConfigStatus: - items: - properties: - freeformTags: - additionalProperties: - type: string - type: object - pdbName: - type: string - pdbState: - type: string - pluggableDatabaseId: - type: string - shouldPdbAdminAccountBeLocked: - type: boolean - required: - - freeformTags - - pdbName - - shouldPdbAdminAccountBeLocked - type: object - type: array - type: object - type: array - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.displayName - name: Display Name - type: string - - jsonPath: .status.dbInfo[0].dbName - name: DB Name - type: string - - jsonPath: .status.state - name: State - type: string - - jsonPath: .status.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .status.dataStorageSizeInGBs - name: Storage (TB) - type: integer - - jsonPath: .status.recoStorageSizeInGB - name: Reco Storage (GB) - type: integer - - jsonPath: .status.storageManagement - name: Storage Mgmt - type: string - - jsonPath: .status.dbInfo[0].connectionString - name: ConnString - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - dataGuard: - properties: - availabilityDomain: - type: string - dbAdminPasswordSecret: - type: string - dbName: - type: string - dbSystemFreeformTags: - additionalProperties: - type: string - type: object - displayName: - type: string - enabled: - type: boolean - hostName: - type: string - isDelete: - type: boolean - peerDbHomeId: - type: string - peerDbSystemId: - type: string - peerRole: - type: string - primaryDatabaseId: - type: string - protectionMode: - type: string - shape: - type: string - sidPrefix: - type: string - subnetId: - type: string - transportType: - type: string - type: object - databaseId: - type: string - dbBackupId: - type: string - dbClone: - properties: - dbAdminPasswordSecret: - type: string - dbName: - type: string - dbUniqueName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - privateIp: - type: string - sidPrefix: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - tdeWalletPasswordSecret: - type: string - required: - - dbName - - displayName - - hostName - - subnetId - type: object - dbSystem: - properties: - availabilityDomain: - type: string - backupDisplayName: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPasswordSecret: - type: string - dbBackupConfig: - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbHomeId: - type: string - dbName: - type: string - dbPatchOcid: - type: string - dbUniqueName: - type: string - dbUpgradeVersion: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - restoreConfig: - properties: - latest: - type: boolean - scn: - type: string - timestamp: - format: date-time - type: string - type: object - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - type: object - enableBackup: - type: boolean - hardLink: - type: boolean - id: - type: string - isPatch: - type: boolean - isUpgrade: - type: boolean - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - ociConfigMap: - type: string - ociSecret: - type: string - pdbConfigs: - items: - properties: - freeformTags: - additionalProperties: - type: string - type: object - isDelete: - type: boolean - pdbAdminPassword: - type: string - pdbName: - type: string - pluggableDatabaseId: - type: string - shouldPdbAdminAccountBeLocked: - type: boolean - tdeWalletPassword: - type: string - required: - - freeformTags - - pdbAdminPassword - - pdbName - - shouldPdbAdminAccountBeLocked - - tdeWalletPassword - type: object - type: array - setupDBCloning: - type: boolean - required: - - ociConfigMap - type: object - status: - properties: - availabilityDomain: - type: string - backups: - items: - properties: - backupId: - type: string - name: - type: string - timestamp: - type: string - required: - - backupId - - name - - timestamp - type: object - type: array - cpuCoreCount: - type: integer - dataGuardStatus: - properties: - dbAdminPasswordSecret: - type: string - dbName: - type: string - dbWorkload: - type: string - id: - type: string - isActiveDataGuardEnabled: - type: boolean - lifecycleDetails: - type: string - lifecycleState: - type: string - peerDataGuardAssociationId: - type: string - peerDatabaseId: - type: string - peerDbHomeId: - type: string - peerDbSystemId: - type: string - peerRole: - type: string - primaryDatabaseId: - type: string - protectionMode: - type: string - shape: - type: string - subnetId: - type: string - transportType: - type: string - type: object - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbCloneStatus: - properties: - dbAdminPasswordSecret: - type: string - dbName: - type: string - dbUniqueName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - id: - type: string - licenseModel: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - type: object - dbEdition: - type: string - dbInfo: - items: - properties: - connectionString: - type: string - connectionStringLong: - type: string - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - dbVersion: - type: string - displayName: - type: string - id: - type: string - kmsDetailsStatus: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyId: - type: string - keyName: - type: string - managementEndpoint: - type: string - vaultId: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - message: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - pdbDetailsStatus: - items: - properties: - pdbConfigStatus: - items: - properties: - freeformTags: - additionalProperties: - type: string - type: object - pdbName: - type: string - pdbState: - type: string - pluggableDatabaseId: - type: string - shouldPdbAdminAccountBeLocked: - type: boolean - type: object - type: array - type: object - type: array - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 - name: lrests.database.oracle.com -spec: - group: database.oracle.com - names: - kind: LREST - listKind: LRESTList - plural: lrests - singular: lrest - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the LREST - jsonPath: .spec.cdbName - name: CDB NAME - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the LREST Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message if any - jsonPath: .status.msg - name: Message - type: string - - description: string of the tnsalias - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - autodiscover: - type: boolean - cdbAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - clusterIp: - default: false - type: boolean - dbPort: - type: integer - dbServer: - type: string - dbTnsurl: - type: string - deletePdbCascade: - type: boolean - loadBalancer: - default: false - type: boolean - lrestImage: - type: string - lrestImagePullPolicy: - enum: - - Always - - Never - type: string - lrestImagePullSecret: - type: string - lrestPort: - default: 8888 - type: integer - lrestPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - namespaceAutoDiscover: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - replicas: - type: integer - serviceAccountName: - type: string - serviceName: - type: string - sysAdminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - trace_level_client: - default: 0 - type: integer - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + type: array type: object - webServerUser: + wallet: properties: + additional: + items: + properties: + mountPath: + type: string + name: + type: string + secret: + type: string + type: object + type: array + mountPath: + type: string secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + type: string type: object type: object status: properties: - msg: - type: string - phase: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + metricsConfig: type: string + replicas: + type: integer status: - type: boolean + type: string + version: + type: string required: - - phase - - status + - conditions + - metricsConfig + - version type: object type: object served: true @@ -10472,58 +8586,47 @@ metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 - name: lrpdbs.database.oracle.com + name: dataguardbrokers.database.oracle.com spec: group: database.oracle.com names: - kind: LRPDB - listKind: LRPDBList - plural: lrpdbs - singular: lrpdb + kind: DataguardBroker + listKind: DataguardBrokerList + plural: dataguardbrokers + shortNames: + - dgbroker + - dgbrokers + singular: dataguardbroker scope: Namespaced versions: - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name + - jsonPath: .status.primaryDatabase + name: Primary type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State + - jsonPath: .status.standbyDatabases + name: Standbys type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size + - jsonPath: .spec.protectionMode + name: Protection Mode type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 type: string - - description: open restricted - jsonPath: .status.restricted - name: Restricted + - jsonPath: .status.externalConnectString + name: Connect Str type: string - - description: last sqlcode - jsonPath: .status.sqlCode - name: last sqlcode - type: integer - - description: last plsql applied - jsonPath: .status.lastplsql - name: last PLSQL + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 type: string - - description: Bitmask status - jsonPath: .status.pdbBitMaskStr - name: BITMASK STATUS + - jsonPath: .status.status + name: Status type: string - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String + - jsonPath: .status.fastStartFailover + name: FSFO type: string - name: v4 + name: v1alpha1 schema: openAPIV3Schema: properties: @@ -10535,362 +8638,576 @@ spec: type: object spec: properties: - action: - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbPass: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminpdbUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + fastStartFailover: + type: boolean + loadBalancer: + type: boolean + nodeSelector: + additionalProperties: + type: string type: object - alterSystem: - type: string - alterSystemParameter: + primaryDatabaseRef: type: string - alterSystemValue: + protectionMode: + enum: + - MaxPerformance + - MaxAvailability type: string - asClone: - type: boolean - cdbName: + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: type: string - cdbNamespace: + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: type: string - cdbPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + databasesInDataguardConfig: + additionalProperties: + type: string type: object - cdbResName: + externalConnectString: type: string - codeconfigmap: + fastStartFailover: type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE + primaryDatabase: type: string - debug: - type: integer - dropAction: - enum: - - INCLUDING - - KEEP + primaryDatabaseRef: type: string - fileNameConversions: + protectionMode: type: string - getScript: - default: false + standbyDatabases: + type: string + status: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.primaryDatabase + name: Primary + type: string + - jsonPath: .status.standbyDatabases + name: Standbys + type: string + - jsonPath: .spec.protectionMode + name: Protection Mode + type: string + - jsonPath: .status.clusterConnectString + name: Cluster Connect Str + priority: 1 + type: string + - jsonPath: .status.externalConnectString + name: Connect Str + type: string + - jsonPath: .spec.primaryDatabaseRef + name: Primary Database + priority: 1 + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.fastStartFailover + name: FSFO + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + fastStartFailover: type: boolean - imperativeLrpdbDeletion: + loadBalancer: type: boolean - lrpdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - lrpdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + nodeSelector: + additionalProperties: + type: string type: object - modifyOption: + primaryDatabaseRef: + type: string + protectionMode: enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED + - MaxPerformance + - MaxAvailability type: string - modifyOption2: - default: NONE + serviceAnnotations: + additionalProperties: + type: string + type: object + setAsPrimaryDatabase: type: string - parameterScope: + standbyDatabaseRefs: + items: + type: string + type: array + required: + - primaryDatabaseRef + - protectionMode + - standbyDatabaseRefs + type: object + status: + properties: + clusterConnectString: type: string - pdbName: + databasesInDataguardConfig: + additionalProperties: + type: string + type: object + externalConnectString: type: string - pdbState: - enum: - - OPEN - - CLOSE - - ALTER - - DELETE - - UNPLUG - - PLUG - - CLONE - - RESET - - NONE + fastStartFailover: type: string - pdbconfigmap: + primaryDatabase: type: string - plsqlexemode: - type: integer - reststate: - type: integer - reuseTempFile: - default: true - type: boolean - sourceFileNameConversions: + primaryDatabaseRef: type: string - sparseClonePath: + protectionMode: type: string - srcPdbName: + standbyDatabases: type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: + status: type: string - tdePassword: + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: dbcssystems.database.oracle.com +spec: + group: database.oracle.com + names: + kind: DbcsSystem + listKind: DbcsSystemList + plural: dbcssystems + singular: dbcssystem + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + databaseId: + type: string + dbBackupId: + type: string + dbClone: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string + tdeWalletPasswordSecret: + type: string required: - - secret + - dbDbUniqueName + - dbName + - displayName + - hostName + - subnetId type: object - tdeSecret: + dbSystem: properties: - secret: + availabilityDomain: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: + type: integer + dbAdminPaswordSecret: + type: string + dbBackupConfig: properties: - key: + autoBackupEnabled: + type: boolean + autoBackupWindow: type: string - secretName: + backupDestinationDetails: type: string - required: - - key - - secretName + recoveryWindowsInDays: + type: integer type: object - required: - - secret - type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: - default: true - type: boolean - webServerPwd: - properties: - secret: + dbDomain: + type: string + dbEdition: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbVersion: + type: string + dbWorkload: + type: string + diskRedundancy: + type: string + displayName: + type: string + domain: + type: string + faultDomains: + items: + type: string + type: array + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsConfig: properties: - key: + compartmentId: type: string - secretName: + encryptionAlgo: type: string - required: - - key - - secretName + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: + type: string + nodeCount: + type: integer + pdbName: + type: string + privateIp: + type: string + shape: + type: string + sshPublicKeys: + items: + type: string + type: array + storageManagement: + type: string + subnetId: + type: string + tags: + additionalProperties: + type: string type: object + tdeWalletPasswordSecret: + type: string + timeZone: + type: string required: - - secret + - availabilityDomain + - compartmentId + - dbAdminPaswordSecret + - hostName + - shape + - subnetId type: object - webServerUser: + hardLink: + type: boolean + id: + type: string + kmsConfig: properties: - secret: - properties: - key: - type: string - secretName: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + ociConfigMap: + type: string + ociSecret: + type: string + pdbConfigs: + items: + properties: + freeformTags: + additionalProperties: type: string - required: - - key - - secretName - type: object + type: object + isDelete: + type: boolean + pdbAdminPassword: + type: string + pdbName: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + tdeWalletPassword: + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean + required: + - ociConfigMap + type: object + status: + properties: + availabilityDomain: + type: string + cpuCoreCount: + type: integer + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbCloneStatus: + properties: + dbAdminPaswordSecret: + type: string + dbDbUniqueName: + type: string + dbName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + id: + type: string + licenseModel: + type: string + sshPublicKeys: + items: + type: string + type: array + subnetId: + type: string required: - - secret + - dbDbUniqueName + - hostName type: object - xmlFileName: + dbEdition: + type: string + dbInfo: + items: + properties: + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + displayName: type: string - type: object - status: - properties: - action: + id: type: string - alterSystem: + kmsDetailsStatus: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyId: + type: string + keyName: + type: string + managementEndpoint: + type: string + vaultId: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + licenseModel: type: string - bitstat: + network: + properties: + clientSubnet: + type: string + domainName: + type: string + hostName: + type: string + listenerPort: + type: integer + networkSG: + type: string + scanDnsName: + type: string + vcnName: + type: string + type: object + nodeCount: type: integer - bitstatstr: - type: string - connString: - type: string - lastplsql: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - pdbBitMask: + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string + type: object + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + required: + - freeformTags + - pdbName + - shouldPdbAdminAccountBeLocked + type: object + type: array + type: object + type: array + recoStorageSizeInGB: type: integer - pdbBitMaskStr: + shape: type: string - phase: + state: type: string - restricted: + storageManagement: type: string - sqlCode: - type: integer - status: - type: boolean - totalSize: + subnetId: + type: string + timeZone: type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: + type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + required: + - operationId + - operationType + type: object + type: array required: - - phase - - sqlCode - - status + - state type: object type: object served: true - storage: true + storage: false subresources: status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 - name: oraclerestarts.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestart - listKind: OracleRestartList - plural: oraclerestarts - singular: oraclerestart - scope: Namespaced - versions: - additionalPrinterColumns: - - jsonPath: .status.configParams.dbName - name: DbName - type: string - - jsonPath: .status.dbState - name: DbState - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version + - jsonPath: .status.displayName + name: Display Name type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str + - jsonPath: .status.dbInfo[0].dbName + name: DB Name type: string - jsonPath: .status.state name: State type: string + - jsonPath: .status.cpuCoreCount + name: OCPUs + type: integer + - jsonPath: .status.dataStorageSizeInGBs + name: Storage (TB) + type: integer + - jsonPath: .status.recoStorageSizeInGB + name: Reco Storage (GB) + type: integer + - jsonPath: .status.storageManagement + name: Storage Mgmt + type: string + - jsonPath: .status.dbInfo[0].connectionString + name: ConnString + type: string name: v4 schema: openAPIV3Schema: @@ -10903,773 +9220,1212 @@ spec: type: object spec: properties: - asmStorageDetails: + dataGuard: properties: - autoUpdate: + availabilityDomain: type: string - disksBySize: + dbAdminPasswordSecret: + type: string + dbName: + type: string + dbSystemFreeformTags: + additionalProperties: + type: string + type: object + displayName: + type: string + enabled: + type: boolean + hostName: + type: string + isDelete: + type: boolean + peerDbHomeId: + type: string + peerDbSystemId: + type: string + peerRole: + type: string + primaryDatabaseId: + type: string + protectionMode: + type: string + shape: + type: string + sidPrefix: + type: string + subnetId: + type: string + transportType: + type: string + type: object + databaseId: + type: string + dbBackupId: + type: string + dbClone: + properties: + dbAdminPasswordSecret: + type: string + dbName: + type: string + dbUniqueName: + type: string + displayName: + type: string + domain: + type: string + hostName: + type: string + initialDataStorageSizeInGB: + type: integer + kmsKeyId: + type: string + kmsKeyVersionId: + type: string + licenseModel: + type: string + privateIp: + type: string + sidPrefix: + type: string + sshPublicKeys: items: - properties: - diskNames: - items: - type: string - type: array - storageSizeInGb: - type: integer - type: object + type: string type: array + subnetId: + type: string + tdeWalletPasswordSecret: + type: string + required: + - dbName + - displayName + - hostName + - subnetId type: object - configParams: + dbSystem: properties: - cpuCount: + availabilityDomain: + type: string + backupDisplayName: + type: string + backupSubnetId: + type: string + clusterName: + type: string + compartmentId: + type: string + cpuCoreCount: type: integer - crsAsmDeviceList: + dbAdminPasswordSecret: type: string - crsAsmDiskDg: + dbBackupConfig: + properties: + autoBackupEnabled: + type: boolean + autoBackupWindow: + type: string + backupDestinationDetails: + type: string + recoveryWindowsInDays: + type: integer + type: object + dbDomain: type: string - crsAsmDiskDgRedundancy: + dbEdition: type: string - dbAsmDeviceList: + dbHomeId: type: string - dbAsmDiskDgRedundancy: + dbName: type: string - dbBase: + dbPatchOcid: type: string - dbCharSet: + dbUniqueName: type: string - dbConfigType: + dbUpgradeVersion: type: string - dbDataFileDestDg: + dbVersion: type: string - dbHome: + dbWorkload: type: string - dbName: + diskRedundancy: type: string - dbRecoveryFileDest: + displayName: type: string - dbRecoveryFileDestSize: + domain: type: string - dbRedoFileSize: + faultDomains: + items: + type: string + type: array + hostName: type: string - dbResponseFile: + initialDataStorageSizeInGB: + type: integer + kmsConfig: properties: - configMapName: + compartmentId: type: string - name: + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: type: string type: object - dbStorageType: + licenseModel: type: string - dbSwZipFile: + nodeCount: + type: integer + pdbName: type: string - dbType: + privateIp: type: string - dbUniqueName: + restoreConfig: + properties: + latest: + type: boolean + scn: + type: string + timestamp: + format: date-time + type: string + type: object + shape: type: string - enableArchiveLog: + sshPublicKeys: + items: + type: string + type: array + storageManagement: type: string - gridBase: + subnetId: type: string - gridHome: + tags: + additionalProperties: + type: string + type: object + tdeWalletPasswordSecret: type: string - gridResponseFile: - properties: - configMapName: - type: string - name: + timeZone: + type: string + type: object + enableBackup: + type: boolean + hardLink: + type: boolean + id: + type: string + isPatch: + type: boolean + isUpgrade: + type: boolean + kmsConfig: + properties: + compartmentId: + type: string + encryptionAlgo: + type: string + keyName: + type: string + vaultName: + type: string + vaultType: + type: string + type: object + ociConfigMap: + type: string + ociSecret: + type: string + pdbConfigs: + items: + properties: + freeformTags: + additionalProperties: type: string - type: object - gridSwZipFile: + type: object + isDelete: + type: boolean + pdbAdminPassword: + type: string + pdbName: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + tdeWalletPassword: + type: string + required: + - freeformTags + - pdbAdminPassword + - pdbName + - shouldPdbAdminAccountBeLocked + - tdeWalletPassword + type: object + type: array + setupDBCloning: + type: boolean + required: + - ociConfigMap + type: object + status: + properties: + availabilityDomain: + type: string + backups: + items: + properties: + backupId: + type: string + name: + type: string + timestamp: + type: string + required: + - backupId + - name + - timestamp + type: object + type: array + cpuCoreCount: + type: integer + dataGuardStatus: + properties: + dbAdminPasswordSecret: type: string - hostSwStageLocation: + dbName: type: string - inventory: + dbWorkload: type: string - oPatchLocation: + id: type: string - oPatchSwZipFile: + isActiveDataGuardEnabled: + type: boolean + lifecycleDetails: type: string - oneOffIds: + lifecycleState: type: string - oneOffLocation: + peerDataGuardAssociationId: type: string - opType: + peerDatabaseId: type: string - pdbName: + peerDbHomeId: type: string - pgaSize: + peerDbSystemId: type: string - processes: - type: integer - recoAsmDeviceList: + peerRole: type: string - recoAsmDiskDgRedudancy: + primaryDatabaseId: type: string - redoAsmDeviceList: + protectionMode: type: string - redoAsmDiskDgRedundancy: + shape: type: string - ruFolderName: + subnetId: type: string - ruPatchLocation: + transportType: type: string - sgaSize: + type: object + dataStoragePercentage: + type: integer + dataStorageSizeInGBs: + type: integer + dbCloneStatus: + properties: + dbAdminPasswordSecret: type: string - stagingSoftwareLocation: + dbName: + type: string + dbUniqueName: + type: string + displayName: + type: string + domain: + type: string + hostName: type: string - swMountLocation: + id: type: string - swStagePvc: + licenseModel: type: string - swStagePvcMountLocation: + sshPublicKeys: + items: + type: string + type: array + subnetId: type: string type: object - dbSecret: + dbEdition: + type: string + dbInfo: + items: + properties: + connectionString: + type: string + connectionStringLong: + type: string + dbHomeId: + type: string + dbName: + type: string + dbUniqueName: + type: string + dbWorkload: + type: string + id: + type: string + type: object + type: array + dbVersion: + type: string + displayName: + type: string + id: + type: string + kmsDetailsStatus: properties: - encryptionType: + compartmentId: type: string - keyFileMountLocation: + encryptionAlgo: type: string - keyFileName: + keyId: type: string - keySecretName: + keyName: type: string - name: + managementEndpoint: type: string - pwdFileMountLocation: + vaultId: type: string - pwdFileName: + vaultName: + type: string + vaultType: type: string type: object - externalSvcType: - type: string - image: - type: string - imagePullPolicy: + licenseModel: type: string - imagePullSecret: + message: type: string - instDetails: + network: properties: - envFile: - type: string - envVars: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - properties: - key: - type: string - name: - default: "" - type: string - optional: - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - hostSwLocation: + clientSubnet: type: string - isDelete: + domainName: type: string - isForceDelete: + hostName: type: string - isKeepPVC: + listenerPort: + type: integer + networkSG: type: string - label: + scanDnsName: type: string - name: + vcnName: type: string - nodePortSvc: - items: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer + type: object + nodeCount: + type: integer + pdbDetailsStatus: + items: + properties: + pdbConfigStatus: + items: + properties: + freeformTags: + additionalProperties: + type: string type: object - type: array - svcType: - type: string - type: object - type: array - onsLocalPort: - format: int32 - type: integer - onsTargetPort: - format: int32 - type: integer - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - type: object - type: array - pvcName: - additionalProperties: + pdbName: + type: string + pdbState: + type: string + pluggableDatabaseId: + type: string + shouldPdbAdminAccountBeLocked: + type: boolean + type: object + type: array + type: object + type: array + recoStorageSizeInGB: + type: integer + shape: + type: string + state: + type: string + storageManagement: + type: string + subnetId: + type: string + timeZone: + type: string + workRequests: + items: + properties: + operationId: + type: string + operationType: + type: string + percentComplete: type: string + timeAccepted: + type: string + timeFinished: + type: string + timeStarted: + type: string + type: object + type: array + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: lrests.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LREST + listKind: LRESTList + plural: lrests + singular: lrest + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the LREST + jsonPath: .spec.cdbName + name: CDB NAME + type: string + - description: ' Name of the DB Server' + jsonPath: .spec.dbServer + name: DB Server + type: string + - description: DB server port + jsonPath: .spec.dbPort + name: DB Port + type: integer + - description: Replicas + jsonPath: .spec.replicas + name: Replicas + type: integer + - description: Status of the LREST Resource + jsonPath: .status.phase + name: Status + type: string + - description: Error message if any + jsonPath: .status.msg + name: Message + type: string + - description: string of the tnsalias + jsonPath: .spec.dbTnsurl + name: TNS STRING + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autodiscover: + type: boolean + cdbAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName type: object - resources: + required: + - secret + type: object + cdbAdminUser: + properties: + secret: properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object + key: + type: string + secretName: + type: string + required: + - key + - secretName type: object - swLocStorageSizeInGb: - type: integer - workerNode: - items: - type: string - type: array + required: + - secret type: object - isDebug: + cdbName: type: string - isDeleteOraPvc: + cdbPrvKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbPubKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCat: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsCrt: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbTlsKey: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + clusterIp: + default: false + type: boolean + dbPort: + type: integer + dbServer: type: string - isDeleteTopology: + dbTnsurl: type: string - isFailed: + deletePdbCascade: type: boolean - isManual: + loadBalancer: + default: false type: boolean - nfsStorageDetails: + lrestImage: + type: string + lrestImagePullPolicy: + enum: + - Always + - Never + type: string + lrestImagePullSecret: + type: string + lrestPort: + default: 8888 + type: integer + lrestPwd: properties: - path: - type: string - readOnly: - type: boolean - server: - type: string + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + namespaceAutoDiscover: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + replicas: + type: integer + serviceAccountName: + type: string + serviceName: + type: string + sysAdminPwd: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object required: - - path - - server + - secret type: object - readinessProbe: + trace_level_client: + default: 0 + type: integer + webServerPwd: properties: - exec: + secret: properties: - command: - items: - type: string - type: array - x-kubernetes-list-type: atomic + key: + type: string + secretName: + type: string + required: + - key + - secretName type: object - failureThreshold: - format: int32 - type: integer - grpc: + required: + - secret + type: object + webServerUser: + properties: + secret: properties: - port: - format: int32 - type: integer - service: - default: "" + key: + type: string + secretName: type: string required: - - port + - key + - secretName type: object - httpGet: + required: + - secret + type: object + type: object + status: + properties: + msg: + type: string + phase: + type: string + status: + type: boolean + required: + - phase + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: lrpdbs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: LRPDB + listKind: LRPDBList + plural: lrpdbs + singular: lrpdb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Name of the CDB + jsonPath: .spec.cdbName + name: CDB Name + type: string + - description: Name of the PDB + jsonPath: .spec.pdbName + name: PDB Name + type: string + - description: PDB Open Mode + jsonPath: .status.openMode + name: PDB State + type: string + - description: Total Size of the PDB + jsonPath: .status.totalSize + name: PDB Size + type: string + - description: Error message, if any + jsonPath: .status.msg + name: Message + type: string + - description: open restricted + jsonPath: .status.restricted + name: Restricted + type: string + - description: last sqlcode + jsonPath: .status.sqlCode + name: last sqlcode + type: integer + - description: last plsql applied + jsonPath: .status.lastplsql + name: last PLSQL + type: string + - description: Bitmask status + jsonPath: .status.pdbBitMaskStr + name: BITMASK STATUS + type: string + - description: The connect string to be used + jsonPath: .status.connString + name: Connect_String + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + action: + type: string + adminName: + properties: + secret: properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - path: + key: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: + secretName: type: string required: - - port + - key + - secretName type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: + required: + - secret + type: object + adminPwd: + properties: + secret: properties: - host: + key: + type: string + secretName: type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true required: - - port + - key + - secretName type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - timeoutSeconds: - format: int32 - type: integer + required: + - secret type: object - resources: + adminpdbPass: properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName type: object + required: + - secret type: object - scriptsGetCmd: - type: string - scriptsLocation: - type: string - securityContext: + adminpdbUser: properties: - appArmorProfile: + secret: properties: - localhostProfile: + key: type: string - type: + secretName: type: string required: - - type + - key + - secretName type: object - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxChangePolicy: - type: string - seLinuxOptions: + required: + - secret + type: object + alterSystem: + type: string + alterSystemParameter: + type: string + alterSystemValue: + type: string + asClone: + type: boolean + cdbName: + type: string + cdbNamespace: + type: string + cdbPrvKey: + properties: + secret: properties: - level: + key: type: string - role: + secretName: type: string - type: + required: + - key + - secretName + type: object + required: + - secret + type: object + cdbResName: + type: string + codeconfigmap: + type: string + copyAction: + enum: + - COPY + - NOCOPY + - MOVE + type: string + debug: + type: integer + dropAction: + enum: + - INCLUDING + - KEEP + type: string + fileNameConversions: + type: string + getScript: + default: false + type: boolean + imperativeLrpdbDeletion: + type: boolean + lrpdbTlsCat: + properties: + secret: + properties: + key: type: string - user: + secretName: type: string + required: + - key + - secretName type: object - seccompProfile: + required: + - secret + type: object + lrpdbTlsCrt: + properties: + secret: properties: - localhostProfile: + key: type: string - type: + secretName: type: string required: - - type + - key + - secretName type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - x-kubernetes-list-type: atomic - supplementalGroupsPolicy: - type: string - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - windowsOptions: + required: + - secret + type: object + lrpdbTlsKey: + properties: + secret: properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: + key: type: string - hostProcess: - type: boolean - runAsUserName: + secretName: type: string + required: + - key + - secretName type: object + required: + - secret type: object - serviceAccountName: + modifyOption: + enum: + - IMMEDIATE + - NORMAL + - READ ONLY + - READ WRITE + - RESTRICTED type: string - serviceDetails: - properties: - available: - items: - type: string - type: array - cardinality: - type: string - clbGoal: - type: string - commitOutComeFastPath: - type: string - commitOutcome: - type: string - drainTimeOut: - type: integer - dtp: - type: string - edition: - type: string - failBack: - type: string - failOverDelay: - type: integer - failOverRestore: - type: string - failOverRetry: - type: integer - failOverType: - type: string - name: - type: string - notification: - type: string - pdb: - type: string - preferred: - items: - type: string - type: array - retenion: - type: integer - rlbGoal: - type: string - role: - type: string - sessionState: - type: string - stopOption: - type: string - svcState: - type: string - tafPolicy: - type: string + modifyOption2: + default: NONE + type: string + parameterScope: + type: string + pdbName: + type: string + pdbState: + enum: + - OPEN + - CLOSE + - ALTER + - DELETE + - UNPLUG + - PLUG + - CLONE + - RESET + - NONE + type: string + pdbconfigmap: + type: string + plsqlexemode: + type: integer + reststate: + type: integer + reuseTempFile: + default: true + type: boolean + sourceFileNameConversions: + type: string + sparseClonePath: + type: string + srcPdbName: + type: string + tdeExport: + type: boolean + tdeImport: + type: boolean + tdeKeystorePath: + type: string + tdePassword: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object required: - - name + - secret type: object - sshKeySecret: + tdeSecret: properties: - keyMountLocation: - type: string - name: - type: string - privKeySecretName: - type: string - pubKeySecretName: - type: string + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object required: - - name + - secret type: object - storageClass: + tempSize: type: string - storageSizeInGB: - type: integer - tdeWalletSecret: + totalSize: + type: string + unlimitedStorage: + default: true + type: boolean + webServerPwd: properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret type: object - useNfsforSwStorage: + webServerUser: + properties: + secret: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - secret + type: object + xmlFileName: type: string - required: - - instDetails - - securityContext type: object status: properties: - DbName: + action: type: string - OracleRestartNodes: - items: - properties: - name: - type: string - nodeDetails: - properties: - InstanceState: - type: string - PodState: - type: string - clusterState: - type: string - isDelete: - type: string - mountedDevices: - items: - type: string - type: array - nodePortSvc: - items: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - type: object - type: array - svcType: - type: string - type: object - type: array - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - type: object - type: array - pvcName: - additionalProperties: - type: string - type: object - state: - type: string - workerNode: - type: string - type: object - type: object - type: array - asmDetails: + alterSystem: + type: string + bitstat: + type: integer + bitstatstr: + type: string + connString: + type: string + lastplsql: + type: string + modifyOption: + type: string + msg: + type: string + openMode: + type: string + pdbBitMask: + type: integer + pdbBitMaskStr: + type: string + phase: + type: string + restricted: + type: string + sqlCode: + type: integer + status: + type: boolean + totalSize: + type: string + required: + - phase + - sqlCode + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: oraclerestarts.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OracleRestart + listKind: OracleRestartList + plural: oraclerestarts + singular: oraclerestart + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.configParams.dbName + name: DbName + type: string + - jsonPath: .status.dbState + name: DbState + type: string + - jsonPath: .status.role + name: Role + type: string + - jsonPath: .status.releaseUpdate + name: Version + type: string + - jsonPath: .status.pdbConnectString + name: Pdb Connect Str + type: string + - jsonPath: .status.state + name: State + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + asmStorageDetails: properties: - diskgroup: + autoUpdate: + type: string + disksBySize: items: properties: - disks: + diskNames: items: type: string type: array - name: - type: string - redundancy: - type: string + storageSizeInGb: + type: integer type: object type: array type: object - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map configParams: properties: cpuCount: @@ -11775,8 +10531,6 @@ spec: swStagePvcMountLocation: type: string type: object - connectString: - type: string dbSecret: properties: encryptionType: @@ -11794,10 +10548,6 @@ spec: pwdFileName: type: string type: object - dbState: - type: string - externalConnectString: - type: string externalSvcType: type: string image: @@ -11979,14 +10729,16 @@ spec: type: string type: array type: object - installNode: - type: string isDebug: type: string isDeleteOraPvc: type: string isDeleteTopology: type: string + isFailed: + type: boolean + isManual: + type: boolean nfsStorageDetails: properties: path: @@ -11999,10 +10751,6 @@ spec: - path - server type: object - oldSpec: - type: string - pdbConnectString: - type: string readinessProbe: properties: exec: @@ -12084,8 +10832,6 @@ spec: format: int32 type: integer type: object - releaseUpdate: - type: string resources: properties: claims: @@ -12119,8 +10865,6 @@ spec: x-kubernetes-int-or-string: true type: object type: object - role: - type: string scriptsGetCmd: type: string scriptsLocation: @@ -12204,6 +10948,8 @@ spec: type: string type: object type: object + serviceAccountName: + type: string serviceDetails: properties: available: @@ -12274,8 +11020,6 @@ spec: required: - name type: object - state: - type: string storageClass: type: string storageSizeInGB: @@ -12290,876 +11034,778 @@ spec: type: string keySecretName: type: string - name: + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: + type: string + required: + - instDetails + - securityContext + type: object + status: + properties: + DbName: + type: string + OracleRestartNodes: + items: + properties: + name: + type: string + nodeDetails: + properties: + InstanceState: + type: string + PodState: + type: string + clusterState: + type: string + isDelete: + type: string + mountedDevices: + items: + type: string + type: array + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + svcType: + type: string + type: object + type: array + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + pvcName: + additionalProperties: + type: string + type: object + state: + type: string + workerNode: + type: string + type: object + type: object + type: array + asmDetails: + properties: + diskgroup: + items: + properties: + disks: + items: + type: string + type: array + name: + type: string + redundancy: + type: string + type: object + type: array + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + configParams: + properties: + cpuCount: + type: integer + crsAsmDeviceList: + type: string + crsAsmDiskDg: + type: string + crsAsmDiskDgRedundancy: + type: string + dbAsmDeviceList: + type: string + dbAsmDiskDgRedundancy: + type: string + dbBase: + type: string + dbCharSet: + type: string + dbConfigType: + type: string + dbDataFileDestDg: + type: string + dbHome: + type: string + dbName: + type: string + dbRecoveryFileDest: + type: string + dbRecoveryFileDestSize: + type: string + dbRedoFileSize: + type: string + dbResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + dbStorageType: + type: string + dbSwZipFile: type: string - pwdFileMountLocation: + dbType: type: string - pwdFileName: + dbUniqueName: type: string - type: object - useNfsforSwStorage: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 - name: oraclerestdataservices.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestDataService - listKind: OracleRestDataServiceList - plural: oraclerestdataservices - shortNames: - - ords - singular: oraclerestdataservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - - jsonPath: .status.mongoDbApiAccessUrl - name: MongoDbApi Access URL - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd + enableArchiveLog: type: string - secretName: + gridBase: type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - properties: - pullFrom: + gridHome: type: string - pullSecrets: + gridResponseFile: + properties: + configMapName: + type: string + name: + type: string + type: object + gridSwZipFile: type: string - version: + hostSwStageLocation: type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - mongoDbApi: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - oracleService: - type: string - ordsPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd + inventory: type: string - secretName: + oPatchLocation: type: string - required: - - secretName - type: object - ordsUser: - type: string - persistence: - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany + oPatchSwZipFile: type: string - setWritePermissions: - type: boolean - size: + oneOffIds: type: string - storageClass: + oneOffLocation: type: string - volumeName: + opType: type: string - type: object - readinessCheckPeriod: - type: integer - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: - type: string - urlMapping: - type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - required: - - adminPassword - - databaseRef - - ordsPassword - type: object - status: - properties: - apexConfigured: - type: boolean - apexUrl: - type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: - type: string - databaseApiUrl: - type: string - databaseRef: - type: string - image: - properties: - pullFrom: + pdbName: type: string - pullSecrets: + pgaSize: type: string - version: + processes: + type: integer + recoAsmDeviceList: type: string - required: - - pullFrom - type: object - loadBalancer: - type: string - mongoDbApi: - type: boolean - mongoDbApiAccessUrl: - type: string - ordsInstalled: - type: boolean - replicas: - type: integer - serviceIP: - type: string - status: - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - - jsonPath: .status.mongoDbApiAccessUrl - name: MongoDbApi Access URL - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - adminPassword: - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd + recoAsmDiskDgRedudancy: type: string - secretName: + redoAsmDeviceList: type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - properties: - pullFrom: + redoAsmDiskDgRedundancy: type: string - pullSecrets: + ruFolderName: type: string - version: + ruPatchLocation: + type: string + sgaSize: + type: string + stagingSoftwareLocation: + type: string + swMountLocation: + type: string + swStagePvc: + type: string + swStagePvcMountLocation: type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - mongoDbApi: - type: boolean - nodeSelector: - additionalProperties: - type: string type: object - oracleService: + connectString: type: string - ordsPassword: + dbSecret: properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd + encryptionType: type: string - secretName: + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: type: string - required: - - secretName type: object - ordsUser: + dbState: type: string - persistence: + externalConnectString: + type: string + externalSvcType: + type: string + image: + type: string + imagePullPolicy: + type: string + imagePullSecret: + type: string + instDetails: properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany + envFile: type: string - setWritePermissions: - type: boolean - size: + envVars: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + hostSwLocation: type: string - storageClass: + isDelete: type: string - volumeName: + isForceDelete: type: string - type: object - readinessCheckPeriod: - type: integer - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: + isKeepPVC: + type: string + label: + type: string + name: + type: string + nodePortSvc: + items: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + svcType: + type: string + type: object + type: array + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + pvcName: + additionalProperties: type: string - urlMapping: + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + swLocStorageSizeInGb: + type: integer + workerNode: + items: type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string + type: array type: object - required: - - adminPassword - - databaseRef - - ordsPassword - type: object - status: - properties: - apexConfigured: - type: boolean - apexUrl: + installNode: type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: + isDebug: type: string - databaseApiUrl: + isDeleteOraPvc: type: string - databaseRef: + isDeleteTopology: type: string - image: + nfsStorageDetails: properties: - pullFrom: - type: string - pullSecrets: + path: type: string - version: + readOnly: + type: boolean + server: type: string required: - - pullFrom + - path + - server type: object - loadBalancer: - type: string - mongoDbApi: - type: boolean - mongoDbApiAccessUrl: - type: string - ordsInstalled: - type: boolean - replicas: - type: integer - serviceIP: + oldSpec: type: string - status: + pdbConnectString: type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 - name: ordssrvs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OrdsSrvs - listKind: OrdsSrvsList - plural: ordssrvs - singular: ordssrvs - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: status - type: string - - jsonPath: .status.workloadType - name: workloadType - type: string - - jsonPath: .status.ordsVersion - name: ordsVersion - type: string - - jsonPath: .status.httpPort - name: httpPort - type: integer - - jsonPath: .status.httpsPort - name: httpsPort - type: integer - - jsonPath: .status.mongoPort - name: MongoPort - type: integer - - jsonPath: .status.restartRequired - name: restartRequired - type: boolean - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.ordsInstalled - name: OrdsInstalled - type: boolean - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - encPrivKey: - properties: - passwordKey: - default: password - type: string - secretName: - type: string - required: - - secretName - type: object - forceRestart: - type: boolean - globalSettings: + readinessProbe: properties: - apex.download: - default: false - type: boolean - apex.download.url: - default: https://download.oracle.com/otn_software/apex/apex-latest.zip - type: string - apex.installation.persistence: + exec: properties: - accessMode: - default: ReadWriteOnce - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - size: - default: 1Gi - type: string - storageClass: - type: string - volumeName: - type: string + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic type: object - cache.metadata.enabled: - type: boolean - cache.metadata.graphql.expireAfterAccess: - type: string - cache.metadata.graphql.expireAfterWrite: - type: string - cache.metadata.jwks.enabled: - type: boolean - cache.metadata.jwks.expireAfterAccess: - type: string - cache.metadata.jwks.expireAfterWrite: - type: string - cache.metadata.jwks.initialCapacity: - format: int32 - type: integer - cache.metadata.jwks.maximumSize: + failureThreshold: format: int32 type: integer - cache.metadata.timeout: - type: string - certSecret: + grpc: properties: - cert: - type: string - key: - type: string - secretName: + port: + format: int32 + type: integer + service: + default: "" type: string required: - - cert - - key - - secretName + - port type: object - database.api.enabled: - type: boolean - database.api.management.services.disabled: - type: boolean - db.invalidPoolTimeout: - type: string - debug.printDebugToScreen: - type: boolean - enable.mongo.access.log: - default: false - type: boolean - enable.standalone.access.log: - default: false - type: boolean - error.responseFormat: - type: string - feature.grahpql.max.nesting.depth: - format: int32 - type: integer - icap.port: - format: int32 - type: integer - icap.secure.port: - format: int32 - type: integer - icap.server: - type: string - log.procedure: - type: boolean - mongo.enabled: - type: boolean - mongo.idle.timeout: - type: string - mongo.op.timeout: - type: string - mongo.port: - default: 27017 + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: format: int32 type: integer - request.traceHeaderName: - type: string - security.credentials.attempts: + periodSeconds: format: int32 type: integer - security.credentials.lock.time: - type: string - security.disableDefaultExclusionList: - type: boolean - security.exclusionList: - type: string - security.externalSessionTrustedOrigins: - type: string - security.forceHTTPS: - type: boolean - security.httpsHeaderCheck: - type: string - security.inclusionList: - type: string - security.maxEntries: + successThreshold: format: int32 type: integer - security.verifySSL: - type: boolean - standalone.context.path: - default: /ords - type: string - standalone.http.port: - default: 8080 - format: int32 + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 type: integer - standalone.https.host: - type: string - standalone.https.port: - default: 8443 + timeoutSeconds: format: int32 type: integer - standalone.stop.timeout: - type: string type: object - image: - type: string - imagePullPolicy: - default: IfNotPresent - enum: - - IfNotPresent - - Always - - Never - type: string - imagePullSecrets: + releaseUpdate: type: string - poolSettings: - items: - properties: - apex.security.administrator.roles: - type: string - apex.security.user.roles: - type: string - autoUpgradeAPEX: - default: false - type: boolean - autoUpgradeORDS: - default: false - type: boolean - db.adminUser: - type: string - db.adminUser.secret: - properties: - passwordKey: - default: password - type: string - secretName: - type: string - required: - - secretName - type: object - db.cdb.adminUser: - type: string - db.cdb.adminUser.secret: - properties: - passwordKey: - default: password - type: string - secretName: - type: string - required: - - secretName - type: object - db.connectionType: - enum: - - basic - - tns - - customurl - type: string - db.credentialsSource: - enum: - - pool - - request - type: string - db.customURL: - type: string - db.hostname: - type: string - db.poolDestroyTimeout: - type: string - db.port: - format: int32 - type: integer - db.secret: - properties: - passwordKey: - default: password - type: string - secretName: - type: string - required: - - secretName - type: object - db.servicename: - type: string - db.sid: - type: string - db.tnsAliasName: - type: string - db.username: - default: ORDS_PUBLIC_USER - type: string - db.wallet.zip.service: - type: string - dbWalletSecret: - properties: - secretName: - type: string - walletName: - type: string - required: - - secretName - - walletName - type: object - debug.trackResources: - type: boolean - feature.openservicebroker.exclude: - type: boolean - feature.sdw: - type: boolean - http.cookie.filter: - type: string - jdbc.DriverType: - enum: - - thin - - oci8 - type: string - jdbc.InactivityTimeout: - format: int32 - type: integer - jdbc.InitialLimit: - format: int32 - type: integer - jdbc.MaxConnectionReuseCount: - format: int32 - type: integer - jdbc.MaxConnectionReuseTime: - type: string - jdbc.MaxLimit: - format: int32 - type: integer - jdbc.MaxStatementsLimit: - format: int32 - type: integer - jdbc.MinLimit: - format: int32 - type: integer - jdbc.SecondsToTrustIdleConnection: - format: int32 - type: integer - jdbc.auth.admin.role: - type: string - jdbc.auth.enabled: - type: boolean - jdbc.cleanup.mode: - type: string - jdbc.statementTimeout: - format: int32 - type: integer - misc.defaultPage: - type: string - misc.pagination.maxRows: - format: int32 - type: integer - owa.trace.sql: - type: boolean - plsql.gateway.mode: - enum: - - disabled - - direct - - proxied - type: string - poolName: - type: string - procedure.preProcess: - type: string - procedure.rest.preHook: - type: string - procedurePostProcess: - type: string - restEnabledSql.active: - type: boolean - security.jwks.connection.timeout: - type: string - security.jwks.read.timeout: - type: string - security.jwks.refresh.interval: - type: string - security.jwks.size: - format: int32 - type: integer - security.jwt.allowed.age: - type: string - security.jwt.allowed.skew: - type: string - security.jwt.profile.enabled: - type: boolean - security.requestAuthenticationFunction: - type: string - security.requestValidationFunction: - default: ords_util.authorize_plsql_gateway - type: string - security.validationFunctionType: - enum: - - plsql - - javascript - type: string - soda.defaultLimit: - type: string - soda.maxLimit: - type: string - tnsAdminSecret: + resources: + properties: + claims: + items: properties: - secretName: + name: + type: string + request: type: string required: - - secretName + - name type: object - required: - - db.secret - - poolName - type: object - type: array - replicas: - default: 1 - format: int32 - minimum: 1 - type: integer - serviceAccountName: + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + role: type: string - workloadType: - default: Deployment - enum: - - Deployment - - StatefulSet - - DaemonSet + scriptsGetCmd: type: string - required: - - globalSettings - - image - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: + scriptsLocation: + type: string + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: format: int64 - minimum: 0 type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceDetails: + properties: + available: + items: type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: array + cardinality: + type: string + clbGoal: + type: string + commitOutComeFastPath: + type: string + commitOutcome: + type: string + drainTimeOut: + type: integer + dtp: + type: string + edition: + type: string + failBack: + type: string + failOverDelay: + type: integer + failOverRestore: + type: string + failOverRetry: + type: integer + failOverType: + type: string + name: + type: string + notification: + type: string + pdb: + type: string + preferred: + items: type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - httpPort: - format: int32 - type: integer - httpsPort: - format: int32 - type: integer - mongoPort: - format: int32 - type: integer - ordsInstalled: - type: boolean - ordsVersion: + type: array + retenion: + type: integer + rlbGoal: + type: string + role: + type: string + sessionState: + type: string + stopOption: + type: string + svcState: + type: string + tafPolicy: + type: string + required: + - name + type: object + sshKeySecret: + properties: + keyMountLocation: + type: string + name: + type: string + privKeySecretName: + type: string + pubKeySecretName: + type: string + required: + - name + type: object + state: type: string - restartRequired: - type: boolean - status: + storageClass: type: string - workloadType: + storageSizeInGB: + type: integer + tdeWalletSecret: + properties: + encryptionType: + type: string + keyFileMountLocation: + type: string + keyFileName: + type: string + keySecretName: + type: string + name: + type: string + pwdFileMountLocation: + type: string + pwdFileName: + type: string + type: object + useNfsforSwStorage: type: string - required: - - restartRequired type: object type: object served: true @@ -13173,44 +11819,36 @@ metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert controller-gen.kubebuilder.io/version: v0.16.5 - name: pdbs.database.oracle.com + name: oraclerestdataservices.database.oracle.com spec: group: database.oracle.com names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb + kind: OracleRestDataService + listKind: OracleRestDataServiceList + plural: oraclerestdataservices + shortNames: + - ords + singular: oraclerestdataservice scope: Namespaced versions: - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name + - jsonPath: .status.status + name: Status type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State + - jsonPath: .spec.databaseRef + name: Database type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size + - jsonPath: .status.databaseApiUrl + name: Database API URL type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message + - jsonPath: .status.apexUrl + name: Apex URL type: string - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String + - jsonPath: .status.mongoDbApiAccessUrl + name: MongoDbApi Access URL type: string name: v1alpha1 schema: @@ -13224,303 +11862,366 @@ spec: type: object spec: properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: + adminPassword: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string required: - - secret + - secretName type: object - adminPwd: + databaseRef: + type: string + image: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string required: - - secret + - pullFrom type: object - asClone: - type: boolean - assertivePdbDeletion: + loadBalancer: type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: + mongoDbApi: type: boolean - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - type: string - pdbOrdsPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbOrdsPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + nodeSelector: + additionalProperties: + type: string type: object - pdbState: - enum: - - OPEN - - CLOSE + oracleService: type: string - pdbTlsCat: + ordsPassword: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string required: - - secret + - secretName type: object - pdbTlsCrt: + ordsUser: + type: string + persistence: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeName: + type: string type: object - pdbTlsKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + readinessCheckPeriod: + type: integer + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: + type: string + serviceAnnotations: + additionalProperties: + type: string type: object - reuseTempFile: + required: + - adminPassword + - databaseRef + - ordsPassword + type: object + status: + properties: + apexConfigured: type: boolean - sourceFileNameConversions: + apexUrl: type: string - sparseClonePath: + commonUsersCreated: + type: boolean + databaseActionsUrl: type: string - srcPdbName: + databaseApiUrl: type: string - tdeExport: + databaseRef: + type: string + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: + type: string + mongoDbApi: type: boolean - tdeImport: + mongoDbApiAccessUrl: + type: string + ordsInstalled: type: boolean - tdeKeystorePath: + replicas: + type: integer + serviceIP: type: string - tdePassword: + status: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.databaseRef + name: Database + type: string + - jsonPath: .status.databaseApiUrl + name: Database API URL + type: string + - jsonPath: .status.databaseActionsUrl + name: Database Actions URL + type: string + - jsonPath: .status.apexUrl + name: Apex URL + type: string + - jsonPath: .status.mongoDbApiAccessUrl + name: MongoDbApi Access URL + type: string + name: v4 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + adminPassword: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string required: - - secret + - secretName type: object - tdeSecret: + databaseRef: + type: string + image: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string required: - - secret + - pullFrom type: object - tempSize: - type: string - totalSize: - type: string - unlimitedStorage: + loadBalancer: type: boolean - webServerPwd: + mongoDbApi: + type: boolean + nodeSelector: + additionalProperties: + type: string + type: object + oracleService: + type: string + ordsPassword: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object + keepSecret: + type: boolean + secretKey: + default: oracle_pwd + type: string + secretName: + type: string required: - - secret + - secretName type: object - webServerUser: + ordsUser: + type: string + persistence: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret + accessMode: + enum: + - ReadWriteOnce + - ReadWriteMany + type: string + setWritePermissions: + type: boolean + size: + type: string + storageClass: + type: string + volumeName: + type: string type: object - xmlFileName: + readinessCheckPeriod: + type: integer + replicas: + minimum: 1 + type: integer + restEnableSchemas: + items: + properties: + enable: + type: boolean + pdbName: + type: string + schemaName: + type: string + urlMapping: + type: string + required: + - enable + - schemaName + type: object + type: array + serviceAccountName: type: string + serviceAnnotations: + additionalProperties: + type: string + type: object required: - - action + - adminPassword + - databaseRef + - ordsPassword type: object status: properties: - action: + apexConfigured: + type: boolean + apexUrl: type: string - connString: + commonUsersCreated: + type: boolean + databaseActionsUrl: type: string - modifyOption: + databaseApiUrl: type: string - msg: + databaseRef: type: string - openMode: + image: + properties: + pullFrom: + type: string + pullSecrets: + type: string + version: + type: string + required: + - pullFrom + type: object + loadBalancer: type: string - phase: + mongoDbApi: + type: boolean + mongoDbApiAccessUrl: + type: string + ordsInstalled: + type: boolean + replicas: + type: integer + serviceIP: type: string status: - type: boolean - totalSize: type: string - required: - - phase - - status type: object type: object served: true - storage: false + storage: true subresources: status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.16.5 + name: ordssrvs.database.oracle.com +spec: + group: database.oracle.com + names: + kind: OrdsSrvs + listKind: OrdsSrvsList + plural: ordssrvs + singular: ordssrvs + scope: Namespaced + versions: - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status + - jsonPath: .status.status + name: status type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message + - jsonPath: .status.workloadType + name: workloadType type: string - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String + - jsonPath: .status.ordsVersion + name: ordsVersion type: string + - jsonPath: .status.httpPort + name: httpPort + type: integer + - jsonPath: .status.httpsPort + name: httpsPort + type: integer + - jsonPath: .status.mongoPort + name: MongoPort + type: integer + - jsonPath: .status.restartRequired + name: restartRequired + type: boolean + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.ordsInstalled + name: OrdsInstalled + type: boolean name: v4 schema: openAPIV3Schema: @@ -13533,268 +12234,439 @@ spec: type: object spec: properties: - action: - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: + encPrivKey: properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object + passwordKey: + default: password + type: string + secretName: + type: string required: - - secret + - secretName type: object - asClone: - type: boolean - assertivePdbDeletion: - type: boolean - cdbName: - type: string - cdbNamespace: - type: string - cdbResName: - type: string - copyAction: - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - type: string - getScript: + forceRestart: type: boolean - modifyOption: - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - type: string - pdbOrdsPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbOrdsPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbState: - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: + globalSettings: properties: - secret: + apex.download: + default: false + type: boolean + apex.download.url: + default: https://download.oracle.com/otn_software/apex/apex-latest.zip + type: string + apex.installation.persistence: properties: - key: + accessMode: + default: ReadWriteOnce + enum: + - ReadWriteOnce + - ReadWriteMany type: string - secretName: + size: + default: 1Gi type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - type: boolean - sourceFileNameConversions: - type: string - sparseClonePath: - type: string - srcPdbName: - type: string - tdeExport: - type: boolean - tdeImport: - type: boolean - tdeKeystorePath: - type: string - tdePassword: - properties: - secret: - properties: - key: + storageClass: type: string - secretName: + volumeName: type: string - required: - - key - - secretName type: object - required: - - secret - type: object - tdeSecret: - properties: - secret: + cache.metadata.enabled: + type: boolean + cache.metadata.graphql.expireAfterAccess: + type: string + cache.metadata.graphql.expireAfterWrite: + type: string + cache.metadata.jwks.enabled: + type: boolean + cache.metadata.jwks.expireAfterAccess: + type: string + cache.metadata.jwks.expireAfterWrite: + type: string + cache.metadata.jwks.initialCapacity: + format: int32 + type: integer + cache.metadata.jwks.maximumSize: + format: int32 + type: integer + cache.metadata.timeout: + type: string + certSecret: properties: + cert: + type: string key: type: string secretName: type: string required: + - cert - key - secretName type: object - required: - - secret + database.api.enabled: + type: boolean + database.api.management.services.disabled: + type: boolean + db.invalidPoolTimeout: + type: string + debug.printDebugToScreen: + type: boolean + enable.mongo.access.log: + default: false + type: boolean + enable.standalone.access.log: + default: false + type: boolean + error.responseFormat: + type: string + feature.grahpql.max.nesting.depth: + format: int32 + type: integer + icap.port: + format: int32 + type: integer + icap.secure.port: + format: int32 + type: integer + icap.server: + type: string + log.procedure: + type: boolean + mongo.enabled: + type: boolean + mongo.idle.timeout: + type: string + mongo.op.timeout: + type: string + mongo.port: + default: 27017 + format: int32 + type: integer + request.traceHeaderName: + type: string + security.credentials.attempts: + format: int32 + type: integer + security.credentials.lock.time: + type: string + security.disableDefaultExclusionList: + type: boolean + security.exclusionList: + type: string + security.externalSessionTrustedOrigins: + type: string + security.forceHTTPS: + type: boolean + security.httpsHeaderCheck: + type: string + security.inclusionList: + type: string + security.maxEntries: + format: int32 + type: integer + security.verifySSL: + type: boolean + standalone.context.path: + default: /ords + type: string + standalone.http.port: + default: 8080 + format: int32 + type: integer + standalone.https.host: + type: string + standalone.https.port: + default: 8443 + format: int32 + type: integer + standalone.stop.timeout: + type: string type: object - tempSize: + image: type: string - totalSize: + imagePullPolicy: + default: IfNotPresent + enum: + - IfNotPresent + - Always + - Never + type: string + imagePullSecrets: + type: string + poolSettings: + items: + properties: + apex.security.administrator.roles: + type: string + apex.security.user.roles: + type: string + autoUpgradeAPEX: + default: false + type: boolean + autoUpgradeORDS: + default: false + type: boolean + db.adminUser: + type: string + db.adminUser.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.cdb.adminUser: + type: string + db.cdb.adminUser.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.connectionType: + enum: + - basic + - tns + - customurl + type: string + db.credentialsSource: + enum: + - pool + - request + type: string + db.customURL: + type: string + db.hostname: + type: string + db.poolDestroyTimeout: + type: string + db.port: + format: int32 + type: integer + db.secret: + properties: + passwordKey: + default: password + type: string + secretName: + type: string + required: + - secretName + type: object + db.servicename: + type: string + db.sid: + type: string + db.tnsAliasName: + type: string + db.username: + default: ORDS_PUBLIC_USER + type: string + db.wallet.zip.service: + type: string + dbWalletSecret: + properties: + secretName: + type: string + walletName: + type: string + required: + - secretName + - walletName + type: object + debug.trackResources: + type: boolean + feature.openservicebroker.exclude: + type: boolean + feature.sdw: + type: boolean + http.cookie.filter: + type: string + jdbc.DriverType: + enum: + - thin + - oci8 + type: string + jdbc.InactivityTimeout: + format: int32 + type: integer + jdbc.InitialLimit: + format: int32 + type: integer + jdbc.MaxConnectionReuseCount: + format: int32 + type: integer + jdbc.MaxConnectionReuseTime: + type: string + jdbc.MaxLimit: + format: int32 + type: integer + jdbc.MaxStatementsLimit: + format: int32 + type: integer + jdbc.MinLimit: + format: int32 + type: integer + jdbc.SecondsToTrustIdleConnection: + format: int32 + type: integer + jdbc.auth.admin.role: + type: string + jdbc.auth.enabled: + type: boolean + jdbc.cleanup.mode: + type: string + jdbc.statementTimeout: + format: int32 + type: integer + misc.defaultPage: + type: string + misc.pagination.maxRows: + format: int32 + type: integer + owa.trace.sql: + type: boolean + plsql.gateway.mode: + enum: + - disabled + - direct + - proxied + type: string + poolName: + type: string + procedure.preProcess: + type: string + procedure.rest.preHook: + type: string + procedurePostProcess: + type: string + restEnabledSql.active: + type: boolean + security.jwks.connection.timeout: + type: string + security.jwks.read.timeout: + type: string + security.jwks.refresh.interval: + type: string + security.jwks.size: + format: int32 + type: integer + security.jwt.allowed.age: + type: string + security.jwt.allowed.skew: + type: string + security.jwt.profile.enabled: + type: boolean + security.requestAuthenticationFunction: + type: string + security.requestValidationFunction: + default: ords_util.authorize_plsql_gateway + type: string + security.validationFunctionType: + enum: + - plsql + - javascript + type: string + soda.defaultLimit: + type: string + soda.maxLimit: + type: string + tnsAdminSecret: + properties: + secretName: + type: string + required: + - secretName + type: object + required: + - db.secret + - poolName + type: object + type: array + replicas: + default: 1 + format: int32 + minimum: 1 + type: integer + serviceAccountName: type: string - unlimitedStorage: - type: boolean - webServerPwd: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: + workloadType: + default: Deployment + enum: + - Deployment + - StatefulSet + - DaemonSet type: string required: - - action + - globalSettings + - image type: object status: properties: - action: - type: string - connString: - type: string - modifyOption: - type: string - msg: - type: string - openMode: - type: string - phase: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + httpPort: + format: int32 + type: integer + httpsPort: + format: int32 + type: integer + mongoPort: + format: int32 + type: integer + ordsInstalled: + type: boolean + ordsVersion: type: string - status: + restartRequired: type: boolean - totalSize: + status: + type: string + workloadType: type: string required: - - phase - - status + - restartRequired type: object type: object served: true @@ -16145,7 +15017,6 @@ rules: resources: - autonomouscontainerdatabases - autonomousdatabases - - cdbs - dataguardbrokers - dbcssystems - events @@ -16154,7 +15025,6 @@ rules: - oraclerestarts - oraclerestdataservices - ordssrvs - - pdbs - shardingdatabases - singleinstancedatabases verbs: @@ -16171,7 +15041,6 @@ rules: - autonomouscontainerdatabases/status - autonomousdatabasebackups/status - autonomousdatabaserestores/status - - cdbs/status - dataguardbrokers/status - dbcssystems/status - lrests/status @@ -16179,7 +15048,6 @@ rules: - oraclerestarts/status - oraclerestdataservices/status - ordssrvs/status - - pdbs/status - shardingdatabases/status - singleinstancedatabases/status verbs: @@ -16208,7 +15076,6 @@ rules: - apiGroups: - database.oracle.com resources: - - cdbs/finalizers - dataguardbrokers/finalizers - lrests/finalizers - oraclerestdataservices/finalizers @@ -16222,7 +15089,6 @@ rules: - dbcssystems/finalizers - lrpdbs/finalizers - oraclerestarts/finalizers - - pdbs/finalizers - shardingdatabases/finalizers verbs: - create @@ -16429,27 +15295,6 @@ metadata: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert name: oracle-database-operator-mutating-webhook-configuration webhooks: -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: mcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -16532,27 +15377,6 @@ webhooks: resources: - oraclerestarts sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: mpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -16784,27 +15608,6 @@ webhooks: resources: - autonomousdatabases sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-cdb - failurePolicy: Fail - name: vcdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - cdbs - sideEffects: None - admissionReviewVersions: - v4 - v1beta1 @@ -16868,27 +15671,6 @@ webhooks: resources: - oraclerestarts sideEffects: None -- admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-database-oracle-com-v4-pdb - failurePolicy: Fail - name: vpdb.kb.io - rules: - - apiGroups: - - database.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - pdbs - sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -17120,7 +15902,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa + image: lin.ocir.io/intsanjaysingh/operator/dboperator_master_orahub:250822 imagePullPolicy: Always name: manager ports: diff --git a/ords/Dockerfile b/ords/Dockerfile deleted file mode 100644 index 25ba08ec..00000000 --- a/ords/Dockerfile +++ /dev/null @@ -1,86 +0,0 @@ -## Copyright (c) 2022 Oracle and/or its affiliates. -## -## The Universal Permissive License (UPL), Version 1.0 -## -## Subject to the condition set forth below, permission is hereby granted to any -## person obtaining a copy of this software, associated documentation and/or data -## (collectively the "Software"), free of charge and under any and all copyright -## rights in the Software, and any and all patent rights owned or freely -## licensable by each licensor hereunder covering either (i) the unmodified -## Software as contributed to or provided by such licensor, or (ii) the Larger -## Works (as defined below), to deal in both -## -## (a) the Software, and -## (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -## one is included with the Software (each a "Larger Work" to which the Software -## is contributed by such licensors), -## -## without restriction, including without limitation the rights to copy, create -## derivative works of, display, perform, and distribute the Software and make, -## use, sell, offer for sale, import, export, have made, and have sold the -## Software and the Larger Work(s), and to sublicense the foregoing rights on -## either these or other terms. -## -## This license is subject to the following condition: -## The above copyright notice and either this complete permission notice or at -## a minimum a reference to the UPL must be included in all copies or -## substantial portions of the Software. -## -## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -## SOFTWARE. - -FROM container-registry.oracle.com/java/jdk:latest - -# Environment variables required for this build (do NOT change) -# ------------------------------------------------------------- -ENV ORDS_HOME=/opt/oracle/ords/ \ - RUN_FILE="runOrdsSSL.sh" \ - ORDSVERSION=23.4.0-8 \ - JAVA=17 -#see https://www.oracle.com/tools/ords/ords-relnotes-23.4.0.html - -# Copy binaries -# ------------- -COPY $RUN_FILE $ORDS_HOME - -RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && \ - yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && \ - yum -y install java-$JAVA-openjdk-devel && \ - yum -y install iproute && \ - yum clean all - -RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm - -RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm - -# Setup filesystem and oracle user -# -------------------------------- -RUN mkdir -p $ORDS_HOME/doc_root && \ - mkdir -p $ORDS_HOME/error && \ - mkdir -p $ORDS_HOME/secrets && \ - chmod ug+x $ORDS_HOME/*.sh && \ - groupadd -g 54322 dba && \ - usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && \ - chown -R oracle:dba $ORDS_HOME -# echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - -RUN echo "unset R1" >> /home/oracle/.bashrc && \ - chown root:root /home/oracle/.bashrc && chmod +r /home/oracle/.bashrc - -# Finalize setup -# ------------------- -USER oracle -WORKDIR /home/oracle - - -VOLUME ["$ORDS_HOME/config/ords"] -EXPOSE 8888 - -# Define default command to start Ords Services -CMD $ORDS_HOME/$RUN_FILE - diff --git a/ords/runOrdsSSL.sh b/ords/runOrdsSSL.sh deleted file mode 100644 index 07e2b931..00000000 --- a/ords/runOrdsSSL.sh +++ /dev/null @@ -1,197 +0,0 @@ -#!/bin/bash - -cat <$TNSNAME - - -function SetParameter() { - ##ords config info <--- Use this command to get the list - -[[ ! -z "${ORACLE_HOST}" && -z "${DBTNSURL}" ]] && { - $ORDS --config ${CONFIG} config set db.hostname ${ORACLE_HOST:-racnode1} - $ORDS --config ${CONFIG} config set db.port ${ORACLE_PORT:-1521} - $ORDS --config ${CONFIG} config set db.servicename ${ORACLE_SERVICE:-TESTORDS} -} - -[[ -z "${ORACLE_HOST}" && ! -z "${DBTNSURL}" ]] && { - #$ORDS --config ${CONFIG} config set db.tnsAliasName ${TNSALIAS} - #$ORDS --config ${CONFIG} config set db.tnsDirectory ${TNS_ADMIN} - #$ORDS --config ${CONFIG} config set db.connectionType tns - - $ORDS --config ${CONFIG} config set db.connectionType customurl - $ORDS --config ${CONFIG} config set db.customURL jdbc:oracle:thin:@${DBTNSURL} -} - - $ORDS --config ${CONFIG} config set security.requestValidationFunction false - $ORDS --config ${CONFIG} config set jdbc.MaxLimit 100 - $ORDS --config ${CONFIG} config set jdbc.InitialLimit 50 - $ORDS --config ${CONFIG} config set error.externalPath ${ERRORFOLDER} - $ORDS --config ${CONFIG} config set standalone.access.log /home/oracle - $ORDS --config ${CONFIG} config set standalone.https.port 8888 - $ORDS --config ${CONFIG} config set standalone.https.cert ${CERTIFICATE} - $ORDS --config ${CONFIG} config set standalone.https.cert.key ${KEY} - $ORDS --config ${CONFIG} config set restEnabledSql.active true - $ORDS --config ${CONFIG} config set security.verifySSL true - $ORDS --config ${CONFIG} config set database.api.enabled true - $ORDS --config ${CONFIG} config set plsql.gateway.mode disabled - $ORDS --config ${CONFIG} config set database.api.management.services.disabled false - $ORDS --config ${CONFIG} config set misc.pagination.maxRows 1000 - $ORDS --config ${CONFIG} config set db.cdb.adminUser "${CDBADMIN_USER:-C##DBAPI_CDB_ADMIN} AS SYSDBA" - $ORDS --config ${CONFIG} config secret --password-stdin db.cdb.adminUser.password << EOF -${CDBADMIN_PWD:-PROVIDE_A_PASSWORD} -EOF - -$ORDS --config ${CONFIG} config user add --password-stdin ${WEBSERVER_USER:-ordspdbadmin} "SQL Administrator, System Administrator" < $ORDS_HOME/k.txt - - -export ORDS_LOGS=/tmp - - [ -f $ORDS_HOME/secrets/$WEBSERVER_USER_KEY ] && - { - WEBSERVER_USER=$(cat /opt/oracle/ords/secrets/${WEBSERVER_USER_KEY}|base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) - } - - [ -f $ORDS_HOME/secrets/$WEBSERVER_PASSWORD_KEY ] && - { - WEBSERVER_PASSWORD=$(cat /opt/oracle/ords/secrets/${WEBSERVER_PASSWORD_KEY}|base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) - } - - [ -f $ORDS_HOME/secrets/$CDBADMIN_USER_KEY ] && - { - CDBADMIN_USER=$(cat /opt/oracle/ords/secrets/${CDBADMIN_USER_KEY} | base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) - } - - [ -f $ORDS_HOME/secrets/$CDBADMIN_PWD_KEY ] && - { - CDBADMIN_PWD=$(cat /opt/oracle/ords/secrets/${CDBADMIN_PWD_KEY} | base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) - } - - [ -f $ORDS_HOME/secrets/$ORACLE_PWD_KEY ] && - { - #SYSDBA_PASSWORD=`cat $ORDS_HOME/secrets/$ORACLE_PWD_KEY` - SYSDBA_PASSWORD=$(cat $ORDS_HOME/secrets/${ORACLE_PWD_KEY} | base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) - } - - [ -f $ORDS_HOME/secrets/$ORACLE_PWD_KEY ] && - { - #ORDS_PASSWORD=`cat $ORDS_HOME/secrets/$ORDS_PWD_KEY` - ORDS_PASSWORD=$(cat $ORDS_HOME/secrets/${ORDS_PWD_KEY} | base64 --decode |openssl rsautl -decrypt -out swap -inkey $ORDS_HOME/k.txt -in - ; cat swap ;rm swap) - } - - -SetParameter; -$ORDS --config ${CONFIG} install \ - --admin-user ${SYSDBA_USER:-"SYS AS SYSDBA"} \ - --feature-db-api true \ - --feature-rest-enabled-sql true \ - --log-folder ${ORDS_LOGS} \ - --proxy-user \ - --password-stdin <${CKF} 2>&1 -echo "checkfile" >> ${CKF} -NOT_INSTALLED=`cat ${CKF} | grep "INFO: The" |wc -l ` -echo NOT_INSTALLED=$NOT_INSTALLED - - -function StartUp () { - $ORDS --config $CONFIG serve --port 8888 --secure -} - -# Check whether ords is already setup -if [ $NOT_INSTALLED -ne 0 ] -then - echo " SETUP " - setupOrds; - StartUp; -fi - -if [ $NOT_INSTALLED -eq 0 ] -then - echo " STARTUP " - StartUp; -fi - - From afba3bd9a14f677a29788a4ba269775f807b787b Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Mon, 25 Aug 2025 06:44:44 +0000 Subject: [PATCH 282/414] Basedbfix --- apis/database/v4/dbcssystem_types.go | 5 +- apis/database/v4/dbcssystem_webhook.go | 142 ++++++------- apis/database/v4/zz_generated.deepcopy.go | 15 ++ commons/dbcssystem/dbcs_reconciler.go | 55 +++++- .../database.oracle.com_dbcssystems.yaml | 13 +- config/manager/kustomization.yaml | 4 +- config/webhook/manifests.yaml | 21 ++ controllers/database/dbcssystem_controller.go | 186 ++++++++++++++++-- oracle-database-operator.yaml | 36 ++-- 9 files changed, 351 insertions(+), 126 deletions(-) diff --git a/apis/database/v4/dbcssystem_types.go b/apis/database/v4/dbcssystem_types.go index 8836c9fb..7f163c81 100644 --- a/apis/database/v4/dbcssystem_types.go +++ b/apis/database/v4/dbcssystem_types.go @@ -273,10 +273,7 @@ type DbCloneStatus struct { // +kubebuilder:printcolumn:name="Display Name",type="string",JSONPath=".status.displayName" // +kubebuilder:printcolumn:name="DB Name",type="string",JSONPath=".status.dbInfo[0].dbName" // +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.state" -// +kubebuilder:printcolumn:name="OCPUs",type="integer",JSONPath=".status.cpuCoreCount" -// +kubebuilder:printcolumn:name="Storage (TB)",type="integer",JSONPath=".status.dataStorageSizeInGBs" -// +kubebuilder:printcolumn:name="Reco Storage (GB)",type="integer",JSONPath=".status.recoStorageSizeInGB" -// +kubebuilder:printcolumn:name="Storage Mgmt",type="string",JSONPath=".status.storageManagement" +// +kubebuilder:printcolumn:name="DB Version",type="string",JSONPath=".status.dbInfo[0].dbVersion" // +kubebuilder:printcolumn:name="ConnString",type="string",JSONPath=".status.dbInfo[0].connectionString" type DbcsSystem struct { metav1.TypeMeta `json:",inline"` diff --git a/apis/database/v4/dbcssystem_webhook.go b/apis/database/v4/dbcssystem_webhook.go index 59f8315b..a6b3766f 100644 --- a/apis/database/v4/dbcssystem_webhook.go +++ b/apis/database/v4/dbcssystem_webhook.go @@ -1,41 +1,3 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - package v4 import ( @@ -47,72 +9,99 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + admission "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. -var dbcssystemlog = logf.Log.WithName("dbcssystem-resource") +var dbcssystemlog = log.Log.WithName("dbcssystem-resource") + +// DbcsSystemWebhook wraps a client for validations/defaults. +type DbcsSystemWebhook struct { + client.Client +} +// Ensure our webhook struct satisfies the interfaces +var _ webhook.CustomValidator = &DbcsSystemWebhook{} +var _ webhook.CustomDefaulter = &DbcsSystemWebhook{} + +// Register the webhook with the manager func (r *DbcsSystem) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(&DbcsSystem{}). - WithDefaulter(r). - WithValidator(r). + WithDefaulter(&DbcsSystemWebhook{Client: mgr.GetClient()}). + WithValidator(&DbcsSystemWebhook{Client: mgr.GetClient()}). Complete() } -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-dbcssystem,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=dbcssystems,verbs=create;update,versions=v4,name=mdbcssystemv4.kb.io,admissionReviewVersions=v1 +// +kubebuilder:webhook:path=/mutate-database-oracle-com-v4-dbcssystem,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=dbcssystems,verbs=create;update,versions=v4,name=mdbcssystemv4.kb.io,admissionReviewVersions=v1 -var _ webhook.CustomDefaulter = &DbcsSystem{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *DbcsSystem) Default(ctx context.Context, obj runtime.Object) error { - dbcssystemlog.Info("default", "name", r.Name) - - // TODO(user): fill in your defaulting logic. +// Default implements webhook.CustomDefaulter +func (w *DbcsSystemWebhook) Default(ctx context.Context, obj runtime.Object) error { + dbcssystemlog.Info("defaulting", "gvk", obj.GetObjectKind().GroupVersionKind().Kind) return nil } -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. - // +kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv4.kb.io,admissionReviewVersions=v1 -var _ webhook.CustomValidator = &DbcsSystem{} -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *DbcsSystem) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - dbcssystemlog.Info("validate create", "name", r.Name) +// ValidateCreate implements webhook.CustomValidator +func (w *DbcsSystemWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + dbcssystemlog.Info("validate create") + + dbcs, ok := obj.(*DbcsSystem) + if !ok { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast object to DbcsSystem")) + } + + // Block creation if resource is already in transitional states + blockedStates := map[string]bool{ + "PROVISIONING": true, + "UPDATING": true, + "TERMINATING": true, + } + + if blockedStates[string(dbcs.Status.State)] { + return nil, apierrors.NewForbidden( + schema.GroupResource{ + Group: "database.oracle.com", + Resource: "DbcsSystem", + }, + dbcs.Name, + fmt.Errorf("creation of DbcsSystem is not allowed while resource is in state %q", dbcs.Status.State), + ) + } return nil, nil } -// // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *DbcsSystem) ValidateUpdate(ctx context.Context, old, newObj runtime.Object) (admission.Warnings, error) { - dbcssystemlog.Info("validate update", "name", r.Name) +// ValidateUpdate implements webhook.CustomValidator +func (w *DbcsSystemWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + dbcssystemlog.Info("validate update") - // Type cast the old object to *DbcsSystem - oldDbcs, ok := old.(*DbcsSystem) - if !ok { - return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast old object to DbcsSystem")) + oldDbcs, ok1 := oldObj.(*DbcsSystem) + newDbcs, ok2 := newObj.(*DbcsSystem) + if !ok1 || !ok2 { + return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast objects to DbcsSystem")) } + // Block spec updates in non-available states blockedStates := map[string]bool{ - "UPDATING": true, - "FAILED": true, + "UPDATING": true, + "PROVISIONING": true, + "TERMINATING": true, } - if blockedStates[string(r.Status.State)] { - if !reflect.DeepEqual(oldDbcs.Spec, r.Spec) { + if blockedStates[string(newDbcs.Status.State)] { + if !reflect.DeepEqual(oldDbcs.Spec, newDbcs.Spec) { return nil, apierrors.NewForbidden( schema.GroupResource{ Group: "database.oracle.com", Resource: "DbcsSystem", }, - r.Name, - fmt.Errorf("updates to DbcsSystem Spec are not allowed while resource is in state %q", r.Status.State), + newDbcs.Name, + fmt.Errorf("updates to DbcsSystem Spec are not allowed while resource is in state %q", newDbcs.Status.State), ) } } @@ -120,10 +109,9 @@ func (r *DbcsSystem) ValidateUpdate(ctx context.Context, old, newObj runtime.Obj return nil, nil } -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *DbcsSystem) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - dbcssystemlog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. +// ValidateDelete implements webhook.CustomValidator +func (w *DbcsSystemWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + dbcssystemlog.Info("validate delete") + // TODO: Add delete validation if needed return nil, nil } diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index e58351a8..481c6028 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -1565,6 +1565,21 @@ func (in *DbcsSystemStatus) DeepCopy() *DbcsSystemStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DbcsSystemWebhook) DeepCopyInto(out *DbcsSystemWebhook) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemWebhook. +func (in *DbcsSystemWebhook) DeepCopy() *DbcsSystemWebhook { + if in == nil { + return nil + } + out := new(DbcsSystemWebhook) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DiskBySize) DeepCopyInto(out *DiskBySize) { *out = *in diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index b0d82cc3..7b327ad2 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -745,6 +745,7 @@ func CloneFromBackupAndGetDbcsId( var dbAdminPassword string var tdePassword string logger.Info("Starting the clone process for DBCS from backup", "dbcs", dbcs) + // time.Sleep(600 * time.Second) backupResp, err := dbClient.GetBackup(ctx, database.GetBackupRequest{ BackupId: dbcs.Spec.DbBackupId, }) @@ -937,6 +938,51 @@ func CloneFromDatabaseAndGetDbcsId(compartmentId string, logger logr.Logger, kub logger.Error(err, "Failed to get SSH public key") return "", err } + // Before creating the clone request payload, check if a valid backup exists + backupsResp, err := dbClient.ListBackups(ctx, database.ListBackupsRequest{ + DatabaseId: dbcs.Spec.DatabaseId, + }) + if err != nil { + logger.Error(err, "Failed to list backups for database", "DatabaseId", dbcs.Spec.DatabaseId) + return "", fmt.Errorf("failed to list backups for database %s: %w", dbcs.Spec.DatabaseId, err) + } + + if len(backupsResp.Items) == 0 { + msg := fmt.Sprintf("no backups found for database %s, cannot proceed with cloning", dbcs.Spec.DatabaseId) + logger.Error(nil, msg) + return "", fmt.Errorf(msg) + } + + // Optional: ensure at least one backup is in the same AD + validBackupFound := false + for _, backup := range backupsResp.Items { + if backup.LifecycleState == database.BackupSummaryLifecycleStateActive && + backup.AvailabilityDomain != nil && + *backup.AvailabilityDomain == *existingDbSystem.DbSystem.AvailabilityDomain { + + validBackupFound = true + logger.Info("Found valid backup for cloning", + "BackupId", *backup.Id, + "AvailabilityDomain", *backup.AvailabilityDomain, + "LifecycleState", backup.LifecycleState) + break + } + + } + + if !validBackupFound { + msg := fmt.Sprintf("no valid backups for database %s found in same AD %s, cannot proceed with cloning", + *dbcs.Spec.DatabaseId, *existingDbSystem.DbSystem.AvailabilityDomain) + logger.Error(nil, msg) + return "", fmt.Errorf(msg) + } + + logger.Info("Valid backup found for cloning", "DatabaseId", dbcs.Spec.DatabaseId) + + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + return "", statusErr + } // Change the phase to "Provisioning" if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { @@ -998,7 +1044,7 @@ func CloneFromDatabaseAndGetDbcsId(compartmentId string, logger logr.Logger, kub if err != nil { // Change the phase to "Provisioning" if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Failed, nwClient, wrClient); statusErr != nil { - return "", statusErr + return "", err } return "", err } @@ -2018,7 +2064,12 @@ func SetDBCSStatus(state databasev4.LifecycleState, compartmentId string, dbClie dbcs.Status.Network.ListenerPort = resp.ListenerPort dbcs.Status.Network.HostName = *resp.Hostname dbcs.Status.Network.DomainName = *resp.Domain - dbcs.Status.DbVersion = *resp.Version + if resp.Version != nil { + dbcs.Status.DbVersion = *resp.Version + } else { + dbcs.Status.DbVersion = "" + } + if dbcs.Spec.KMSConfig != nil && dbcs.Spec.KMSConfig.CompartmentId != "" { dbcs.Status.KMSDetailsStatus.CompartmentId = dbcs.Spec.KMSConfig.CompartmentId dbcs.Status.KMSDetailsStatus.VaultName = dbcs.Spec.KMSConfig.VaultName diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index f7134796..91a66bcf 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -397,17 +397,8 @@ spec: - jsonPath: .status.state name: State type: string - - jsonPath: .status.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .status.dataStorageSizeInGBs - name: Storage (TB) - type: integer - - jsonPath: .status.recoStorageSizeInGB - name: Reco Storage (GB) - type: integer - - jsonPath: .status.storageManagement - name: Storage Mgmt + - jsonPath: .status.dbInfo[0].dbVersion + name: DB Version type: string - jsonPath: .status.dbInfo[0].connectionString name: ConnString diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 3f509a8c..74303bea 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lin.ocir.io/intsanjaysingh/operator/dboperator_master_orahub - newTag: "250822" + newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database + newTag: basedb-operator-sa diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index a225b1d9..58a5d2b1 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -355,6 +355,27 @@ webhooks: resources: - autonomousdatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-database-oracle-com-v4-dbcssystem + failurePolicy: Fail + name: vdbcssystemv4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - dbcssystems + sideEffects: None - admissionReviewVersions: - v4 - v1beta1 diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index d5aa286b..bd0971e6 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -607,8 +607,9 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) dbSystemId, err = dbcsv4.CloneFromDatabaseAndGetDbcsId(compartmentId, r.Logger, r.KubeClient, r.dbClient, dbcsInst, r.nwClient, r.wrClient) if err != nil { r.Logger.Error(err, "Fail to clone db system from DatabaseID provided") + dbcsInst.Status.Message = err.Error() if statusErr := dbcsv4.SetLifecycleState(compartmentId, r.KubeClient, r.dbClient, dbcsInst, databasev4.Failed, r.nwClient, r.wrClient); statusErr != nil { - dbcsInst.Status.Message = err.Error() + return ctrl.Result{}, statusErr } @@ -834,9 +835,6 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, nil } - default: - // Skip Data Guard handling - r.Logger.Info("Skipping Data Guard setup and deletion - no action required") } // Update the last succesful spec @@ -852,7 +850,6 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } else if setupDataguard { dbSystemId = *dbcsInst.Status.DataGuardStatus.PeerDbSystemId } - //assignDBCSID(dbcsInst,dbcsI)k get pods -n $odo // Change the phase to "Available" assignDBCSID(dbcsInst, dbSystemId) @@ -1056,7 +1053,7 @@ func (r *DbcsSystemReconciler) DeleteDataGuard( // Populate Status from Spec (if present) status.DbAdminPasswordSecret = spec.DbAdminPasswordSecret - status.PeerDbSystemId = spec.PeerDbSystemId + // status.PeerDbSystemId = spec.PeerDbSystemId status.PrimaryDatabaseId = dbcsInst.Spec.Id status.ProtectionMode = spec.ProtectionMode status.TransportType = spec.TransportType @@ -1064,17 +1061,140 @@ func (r *DbcsSystemReconciler) DeleteDataGuard( status.Shape = spec.Shape status.SubnetId = spec.SubnetId - // Get peer DB system state getDbSystemResp, err := dbClient.GetDbSystem(ctx, database.GetDbSystemRequest{ DbSystemId: peerDbSystemId, }) if err != nil { + if svcErr, ok := err.(common.ServiceError); ok && svcErr.GetHTTPStatusCode() == 404 { + msg := fmt.Sprintf("Peer DB System %s already terminated or not found, cleaning up Data Guardstatus", + func(s *string) string { + if s == nil { + return "" + } + return *s + }(peerDbSystemId), + ) + log.Info(msg) + dbcsInst.Status.Message = msg + dbcsInst.Status.DataGuardStatus = nil + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + return nil + } + log.Error(err, "Failed to fetch peer DB system status") dbcsInst.Status.Message = "Failed to fetch peer DB system status" _ = r.KubeClient.Status().Update(ctx, dbcsInst) return err } + var primaryDatabaseId *string + + // List DB Homes + dbHomesResp, err := dbClient.ListDbHomes(ctx, database.ListDbHomesRequest{ + CompartmentId: &compartmentId, + DbSystemId: dbcsInst.Spec.Id, + }) + if err != nil { + return fmt.Errorf("failed to list DB Homes for system %s: %w", *dbcsInst.Spec.Id, err) + } + + // Iterate DB Homes + for _, home := range dbHomesResp.Items { + dbsResp, err := dbClient.ListDatabases(ctx, database.ListDatabasesRequest{ + CompartmentId: &compartmentId, + DbHomeId: home.Id, + }) + if err != nil { + return fmt.Errorf("failed to list databases for DB Home %s: %w", *home.Id, err) + } + + for _, db := range dbsResp.Items { + // List DG associations for this DB + dgResp, err := dbClient.ListDataGuardAssociations(ctx, database.ListDataGuardAssociationsRequest{ + DatabaseId: db.Id, + }) + if err != nil { + return fmt.Errorf("failed to list Data Guard associations for DB %s: %w", *db.Id, err) + } + + for _, assoc := range dgResp.Items { + if assoc.Role == database.DataGuardAssociationSummaryRolePrimary { + primaryDatabaseId = db.Id + if dbcsInst.Status.DataGuardStatus == nil { + dbcsInst.Status.DataGuardStatus = &databasev4.DataGuardStatus{} + } + dbcsInst.Status.DataGuardStatus.PrimaryDatabaseId = primaryDatabaseId + dbcsInst.Status.DataGuardStatus.PeerDbSystemId = assoc.PeerDbSystemId + break + } + } + + if primaryDatabaseId != nil { + break + } + } + + if primaryDatabaseId != nil { + break + } + } + + if primaryDatabaseId == nil { + msg := fmt.Sprintf( + "Skipping Data Guard deletion — peer DB %s is not associated with primary DB %s", + strVal(peerDbSystemId), + strVal(primaryDatabaseId), + ) + log.Info(msg) + + dbcsInst.Status.Message = msg + + dbcsInst.Status.Message = msg + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + return fmt.Errorf(msg) + } + + // Verify Data Guard association exists + listAssocResp, err := dbClient.ListDataGuardAssociations(ctx, database.ListDataGuardAssociationsRequest{ + DatabaseId: primaryDatabaseId, + }) + if err != nil { + log.Error(err, "Failed to list Data Guard associations for primary DB") + dbcsInst.Status.Message = "Failed to list Data Guard associations" + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + return err + } + + var association *database.DataGuardAssociationSummary + for _, assoc := range listAssocResp.Items { + if assoc.PeerDbSystemId != nil && *assoc.PeerDbSystemId == *peerDbSystemId { + association = &assoc + break + } + } + + if association == nil { + msg := fmt.Sprintf( + "Skipping Data Guard deletion — peer DB %s is not associated with primary DB %s", + *peerDbSystemId, primaryDatabaseId, + ) + log.Info(msg) + dbcsInst.Status.Message = msg + if dbcsInst.Status.DataGuardStatus == nil { + dbcsInst.Status.DataGuardStatus = &databasev4.DataGuardStatus{} + } + dbcsInst.Status.DataGuardStatus.LifecycleDetails = &msg + + _ = r.KubeClient.Status().Update(ctx, dbcsInst) + + return fmt.Errorf(msg) + } + + // At this point, we know the primary and peer DB are actually in Data Guard + log.Info("Confirmed Data Guard association, proceeding with deletion", + "primary", primaryDatabaseId, + "peer", *peerDbSystemId) + switch getDbSystemResp.DbSystem.LifecycleState { case database.DbSystemLifecycleStateTerminated: @@ -1107,7 +1227,7 @@ func (r *DbcsSystemReconciler) DeleteDataGuard( // Populate Status from Spec (if present) status.DbAdminPasswordSecret = spec.DbAdminPasswordSecret - status.PeerDbSystemId = spec.PeerDbSystemId + // status.PeerDbSystemId = spec.PeerDbSystemId status.PrimaryDatabaseId = dbcsInst.Spec.Id status.ProtectionMode = spec.ProtectionMode status.TransportType = spec.TransportType @@ -1175,7 +1295,7 @@ func (r *DbcsSystemReconciler) DeleteDataGuard( // Populate Status from Spec (if present) status.DbAdminPasswordSecret = spec.DbAdminPasswordSecret - status.PeerDbSystemId = spec.PeerDbSystemId + // status.PeerDbSystemId = spec.PeerDbSystemId status.PrimaryDatabaseId = dbcsInst.Spec.Id status.ProtectionMode = spec.ProtectionMode status.TransportType = spec.TransportType @@ -1227,6 +1347,12 @@ Cleanup: log.Info("Successfully deleted Data Guard association") return nil } +func strVal(s *string) string { + if s == nil { + return "" + } + return *s +} func (r *DbcsSystemReconciler) waitForWorkRequest( ctx context.Context, @@ -1873,26 +1999,50 @@ func (r *DbcsSystemReconciler) EnableDataGuard( } r.Logger.Info("Data Guard association creation started") + if dbcsSystem.Status.DataGuardStatus == nil { + dbcsSystem.Status.DataGuardStatus = &databasev4.DataGuardStatus{} + } status := dbcsSystem.Status.DataGuardStatus // Use Spec to fill necessary details spec := dbcsSystem.Spec.DataGuard // Populate Status from Spec (if present) - if dbcsSystem.Spec.DataGuard.DbAdminPasswordSecret != nil { - status.DbAdminPasswordSecret = dbcsSystem.Spec.DataGuard.DbAdminPasswordSecret + if spec.DbAdminPasswordSecret != nil { + status.DbAdminPasswordSecret = spec.DbAdminPasswordSecret } - status.PeerDbSystemId = spec.PeerDbSystemId - status.PrimaryDatabaseId = dbcsSystem.Spec.Id - status.ProtectionMode = spec.ProtectionMode - status.TransportType = spec.TransportType - status.PeerRole = spec.PeerRole - status.Shape = spec.Shape - status.SubnetId = spec.SubnetId + if spec.PeerDbSystemId != nil { + status.PeerDbSystemId = spec.PeerDbSystemId + } + if dbcsSystem.Spec.Id != nil { + status.PrimaryDatabaseId = dbcsSystem.Spec.Id + } + if spec.ProtectionMode != nil { + status.ProtectionMode = spec.ProtectionMode + } + + if spec.TransportType != nil { + status.TransportType = spec.TransportType + } + + if spec.PeerRole != nil { + status.PeerRole = spec.PeerRole + } + + if spec.Shape != nil { + status.Shape = spec.Shape + } + + if spec.SubnetId != nil { + status.SubnetId = spec.SubnetId + } + + // Mark lifecycle as provisioning provisioningState := string(database.DbSystemLifecycleStateProvisioning) status.LifecycleState = &provisioningState status.LifecycleDetails = common.String("Dataguard Peer DB system is Provisioning...") + dbcsSystem.Status.State = databasev4.Update dbcsSystem.Status.Message = "Peer DB system is provisioning..." diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index e945dbb2..123ab291 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -9193,17 +9193,8 @@ spec: - jsonPath: .status.state name: State type: string - - jsonPath: .status.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .status.dataStorageSizeInGBs - name: Storage (TB) - type: integer - - jsonPath: .status.recoStorageSizeInGB - name: Reco Storage (GB) - type: integer - - jsonPath: .status.storageManagement - name: Storage Mgmt + - jsonPath: .status.dbInfo[0].dbVersion + name: DB Version type: string - jsonPath: .status.dbInfo[0].connectionString name: ConnString @@ -15608,6 +15599,27 @@ webhooks: resources: - autonomousdatabases sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: oracle-database-operator-webhook-service + namespace: oracle-database-operator-system + path: /validate-database-oracle-com-v4-dbcssystem + failurePolicy: Fail + name: vdbcssystemv4.kb.io + rules: + - apiGroups: + - database.oracle.com + apiVersions: + - v4 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - dbcssystems + sideEffects: None - admissionReviewVersions: - v4 - v1beta1 @@ -15902,7 +15914,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: lin.ocir.io/intsanjaysingh/operator/dboperator_master_orahub:250822 + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa imagePullPolicy: Always name: manager ports: From a5bf78509485962b32f5f5b9bee3ef2885aa7cc1 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Mon, 25 Aug 2025 12:31:46 +0000 Subject: [PATCH 283/414] fix compilation --- apis/database/v4/dbcssystem_webhook.go | 77 +++++++++++------------ apis/database/v4/zz_generated.deepcopy.go | 15 ----- 2 files changed, 35 insertions(+), 57 deletions(-) diff --git a/apis/database/v4/dbcssystem_webhook.go b/apis/database/v4/dbcssystem_webhook.go index a6b3766f..1fc85f80 100644 --- a/apis/database/v4/dbcssystem_webhook.go +++ b/apis/database/v4/dbcssystem_webhook.go @@ -9,67 +9,64 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" + logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" admission "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. -var dbcssystemlog = log.Log.WithName("dbcssystem-resource") +var dbcssystemlog = logf.Log.WithName("dbcssystem-resource") -// DbcsSystemWebhook wraps a client for validations/defaults. -type DbcsSystemWebhook struct { - client.Client -} - -// Ensure our webhook struct satisfies the interfaces -var _ webhook.CustomValidator = &DbcsSystemWebhook{} -var _ webhook.CustomDefaulter = &DbcsSystemWebhook{} - -// Register the webhook with the manager +// SetupWebhookWithManager registers the webhook with the manager. func (r *DbcsSystem) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(&DbcsSystem{}). - WithDefaulter(&DbcsSystemWebhook{Client: mgr.GetClient()}). - WithValidator(&DbcsSystemWebhook{Client: mgr.GetClient()}). + WithDefaulter(r). + WithValidator(r). Complete() } +// Ensure our CRD type implements the webhook interfaces +var _ webhook.CustomValidator = &DbcsSystem{} +var _ webhook.CustomDefaulter = &DbcsSystem{} + // +kubebuilder:webhook:path=/mutate-database-oracle-com-v4-dbcssystem,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=dbcssystems,verbs=create;update,versions=v4,name=mdbcssystemv4.kb.io,admissionReviewVersions=v1 // Default implements webhook.CustomDefaulter -func (w *DbcsSystemWebhook) Default(ctx context.Context, obj runtime.Object) error { - dbcssystemlog.Info("defaulting", "gvk", obj.GetObjectKind().GroupVersionKind().Kind) +func (r *DbcsSystem) Default(ctx context.Context, obj runtime.Object) error { + cr, ok := obj.(*DbcsSystem) + if !ok { + return fmt.Errorf("expected *DbcsSystem but got %T", obj) + } + + dbcssystemlog.Info("default", "name", cr.Name) + + // TODO: add your defaulting logic here return nil } // +kubebuilder:webhook:verbs=create;update;delete,path=/validate-database-oracle-com-v4-dbcssystem,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=dbcssystems,versions=v4,name=vdbcssystemv4.kb.io,admissionReviewVersions=v1 // ValidateCreate implements webhook.CustomValidator -func (w *DbcsSystemWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { +func (r *DbcsSystem) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { dbcssystemlog.Info("validate create") - dbcs, ok := obj.(*DbcsSystem) + cr, ok := obj.(*DbcsSystem) if !ok { - return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast object to DbcsSystem")) + return nil, apierrors.NewInternalError(fmt.Errorf("expected *DbcsSystem but got %T", obj)) } - // Block creation if resource is already in transitional states blockedStates := map[string]bool{ "PROVISIONING": true, "UPDATING": true, "TERMINATING": true, } - if blockedStates[string(dbcs.Status.State)] { + if blockedStates[string(cr.Status.State)] { return nil, apierrors.NewForbidden( - schema.GroupResource{ - Group: "database.oracle.com", - Resource: "DbcsSystem", - }, - dbcs.Name, - fmt.Errorf("creation of DbcsSystem is not allowed while resource is in state %q", dbcs.Status.State), + schema.GroupResource{Group: "database.oracle.com", Resource: "DbcsSystem"}, + cr.Name, + fmt.Errorf("creation not allowed while resource is in state %q", cr.Status.State), ) } @@ -77,31 +74,27 @@ func (w *DbcsSystemWebhook) ValidateCreate(ctx context.Context, obj runtime.Obje } // ValidateUpdate implements webhook.CustomValidator -func (w *DbcsSystemWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { +func (r *DbcsSystem) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { dbcssystemlog.Info("validate update") - oldDbcs, ok1 := oldObj.(*DbcsSystem) - newDbcs, ok2 := newObj.(*DbcsSystem) + oldCr, ok1 := oldObj.(*DbcsSystem) + newCr, ok2 := newObj.(*DbcsSystem) if !ok1 || !ok2 { - return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast objects to DbcsSystem")) + return nil, apierrors.NewInternalError(fmt.Errorf("expected *DbcsSystem but got %T/%T", oldObj, newObj)) } - // Block spec updates in non-available states blockedStates := map[string]bool{ "UPDATING": true, "PROVISIONING": true, "TERMINATING": true, } - if blockedStates[string(newDbcs.Status.State)] { - if !reflect.DeepEqual(oldDbcs.Spec, newDbcs.Spec) { + if blockedStates[string(newCr.Status.State)] { + if !reflect.DeepEqual(oldCr.Spec, newCr.Spec) { return nil, apierrors.NewForbidden( - schema.GroupResource{ - Group: "database.oracle.com", - Resource: "DbcsSystem", - }, - newDbcs.Name, - fmt.Errorf("updates to DbcsSystem Spec are not allowed while resource is in state %q", newDbcs.Status.State), + schema.GroupResource{Group: "database.oracle.com", Resource: "DbcsSystem"}, + newCr.Name, + fmt.Errorf("spec updates not allowed while resource is in state %q", newCr.Status.State), ) } } @@ -110,7 +103,7 @@ func (w *DbcsSystemWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj r } // ValidateDelete implements webhook.CustomValidator -func (w *DbcsSystemWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { +func (r *DbcsSystem) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { dbcssystemlog.Info("validate delete") // TODO: Add delete validation if needed return nil, nil diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index 481c6028..e58351a8 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -1565,21 +1565,6 @@ func (in *DbcsSystemStatus) DeepCopy() *DbcsSystemStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DbcsSystemWebhook) DeepCopyInto(out *DbcsSystemWebhook) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbcsSystemWebhook. -func (in *DbcsSystemWebhook) DeepCopy() *DbcsSystemWebhook { - if in == nil { - return nil - } - out := new(DbcsSystemWebhook) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DiskBySize) DeepCopyInto(out *DiskBySize) { *out = *in From 88b5b71201c64f2ca157911d398a36c276355dd5 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Mon, 25 Aug 2025 13:12:20 +0000 Subject: [PATCH 284/414] fix for autoupdate --- config/manager/kustomization.yaml | 2 +- controllers/database/oraclerestart_controller.go | 7 ++++++- oracle-database-operator.yaml | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 74303bea..2726acb4 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: basedb-operator-sa + newTag: restart-operator-sa diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index eda17d5c..8d6efc28 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -469,7 +469,12 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques oracleRestart.Spec.InstDetails.EnvFile = cmName result, err = r.createOrReplaceSfsAsm(ctx, req, oracleRestart, oraclerestartcommon.BuildStatefulSetForOracleRestart(oracleRestart, oracleRestart.Spec.InstDetails, r.Client), autoUpdate, index, isLast, oldSpec) if err != nil { - result = resultNq + if autoUpdate { + result = resultQ + } else { + result = resultNq + } + result = resultQ return result, err } } diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 123ab291..74599f7d 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -15914,7 +15914,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:restart-operator-sa imagePullPolicy: Always name: manager ports: From bd4ee609610fd9e73912049e7e1fd0a56037b4ea Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Tue, 26 Aug 2025 03:37:32 +0000 Subject: [PATCH 285/414] fix for dbclone from backup --- commons/dbcssystem/dbcs_reconciler.go | 23 ++++++----- config/manager/kustomization.yaml | 2 +- controllers/database/dbcssystem_controller.go | 38 ++++++++++--------- oracle-database-operator.yaml | 2 +- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 7b327ad2..db8a9e62 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -745,7 +745,6 @@ func CloneFromBackupAndGetDbcsId( var dbAdminPassword string var tdePassword string logger.Info("Starting the clone process for DBCS from backup", "dbcs", dbcs) - // time.Sleep(600 * time.Second) backupResp, err := dbClient.GetBackup(ctx, database.GetBackupRequest{ BackupId: dbcs.Spec.DbBackupId, }) @@ -804,6 +803,11 @@ func CloneFromBackupAndGetDbcsId( return "", err } + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + return "", statusErr + } + // Create the clone request payload cloneRequest := database.LaunchDbSystemFromBackupDetails{ CompartmentId: existingDbSystem.DbSystem.CompartmentId, @@ -857,15 +861,19 @@ func CloneFromBackupAndGetDbcsId( LaunchDbSystemDetails: cloneRequest, }) if err != nil { + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Failed, nwClient, wrClient); statusErr != nil { + return "", err + } return "", err } dbcs.Status.DbCloneStatus.Id = response.DbSystem.Id - // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { - return "", statusErr - } + // // Change the phase to "Provisioning" + // if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { + // return "", statusErr + // } // Check the state _, err = CheckResourceState(logger, dbClient, *response.DbSystem.Id, string(databasev4.Provision), string(databasev4.Available)) @@ -984,11 +992,6 @@ func CloneFromDatabaseAndGetDbcsId(compartmentId string, logger logr.Logger, kub return "", statusErr } - // Change the phase to "Provisioning" - if statusErr := SetLifecycleState(compartmentId, kubeClient, dbClient, dbcs, databasev4.Provision, nwClient, wrClient); statusErr != nil { - return "", statusErr - } - // Create the clone request payload cloneRequest := database.LaunchDbSystemFromDatabaseDetails{ CompartmentId: existingDatabase.CompartmentId, diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2726acb4..74303bea 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: restart-operator-sa + newTag: basedb-operator-sa diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index bd0971e6..03b74744 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -109,7 +109,7 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // Get the dbcs instance from the cluster dbcsInst := &databasev4.DbcsSystem{} r.Logger.Info("Reconciling DbSystemDetails", "name", req.NamespacedName) - + // time.Sleep(200000 * time.Second) // to avoid conflict condition during rapid changes if err := r.KubeClient.Get(ctx, req.NamespacedName, dbcsInst); err != nil { if errors.IsNotFound(err) { // CR was deleted → stop reconciling @@ -494,23 +494,6 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) } } - setupCloning := false - // Check if SetupDBCloning is true and ensure one of the required fields is provided - if dbcsInst.Spec.SetupDBCloning { - // If SetupDBCloning is true, at least one of Id, DbBackupId, or DatabaseId must be non-nil - if dbcsInst.Spec.Id == nil && dbcsInst.Spec.DbBackupId == nil && dbcsInst.Spec.DatabaseId == nil { - // If none of the required fields are set, log an error and exit the function - r.Logger.Error(err, "SetupDBCloning is defined but other necessary details (Id, DbBackupId, DatabaseId) are not present. Refer README.md file for instructions.") - dbcsInst.Status.Message = "SetupDBCloning is defined but other necessary details (Id, DbBackupId, DatabaseId) are not present. Refer README.md file for instructions." - return ctrl.Result{}, nil - } - // If the condition is met, proceed with cloning setup - setupCloning = true - } else { - // If SetupDBCloning is false, continue as usual without cloning - setupCloning = false - } - switch { case dbcsInst.Spec.IsPatch && dbcsInst.Spec.IsUpgrade: errMsg := "Both IsPatch and IsUpgrade are set. Only one operation can be performed at a time." @@ -583,6 +566,25 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) // All required fields are present, enable setup setupDataguard = true } + + //-----------------Clone Setup---------------------------------// + + setupCloning := false + // Check if SetupDBCloning is true and ensure one of the required fields is provided + if dbcsInst.Spec.SetupDBCloning { + // If SetupDBCloning is true, at least one of Id, DbBackupId, or DatabaseId must be non-nil + if dbcsInst.Spec.Id == nil && dbcsInst.Spec.DbBackupId == nil && dbcsInst.Spec.DatabaseId == nil { + // If none of the required fields are set, log an error and exit the function + r.Logger.Error(err, "SetupDBCloning is defined but other necessary details (Id, DbBackupId, DatabaseId) are not present. Refer README.md file for instructions.") + dbcsInst.Status.Message = "SetupDBCloning is defined but other necessary details (Id, DbBackupId, DatabaseId) are not present. Refer README.md file for instructions." + return ctrl.Result{}, nil + } + // If the condition is met, proceed with cloning setup + setupCloning = true + } else { + // If SetupDBCloning is false, continue as usual without cloning + setupCloning = false + } var dbSystemId string // Executing DB Cloning Process, if defined. Do not repeat cloning again when Status has Id present. if setupCloning && dbcsInst.Status.DbCloneStatus.Id == nil { diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 74599f7d..123ab291 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -15914,7 +15914,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:restart-operator-sa + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa imagePullPolicy: Always name: manager ports: From b93c088c0b91c886cd3b63039e5f7af922eacca9 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Tue, 26 Aug 2025 06:44:19 +0000 Subject: [PATCH 286/414] fix for dbclone --- commons/dbcssystem/dbcs_reconciler.go | 3 +++ controllers/database/dbcssystem_controller.go | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index db8a9e62..87b8e806 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -753,6 +753,7 @@ func CloneFromBackupAndGetDbcsId( fmt.Println("Error getting backup details:", err) return "", err } + // Extract the DatabaseId from the backup details databaseId := backupResp.Backup.DatabaseId // Fetch the existing Database details existingDatabase, err := dbClient.GetDatabase(ctx, database.GetDatabaseRequest{ @@ -769,6 +770,8 @@ func CloneFromBackupAndGetDbcsId( logger.Error(err, "DBSystemId not found") return "", err } + dbcs.Spec.Id = dbSystemId + if compartmentId == "" { compartmentId = *existingDatabase.CompartmentId } diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index 03b74744..a30f86d4 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -105,11 +105,9 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) var err error resultNq := ctrl.Result{Requeue: false} resultQ := ctrl.Result{Requeue: true, RequeueAfter: 60 * time.Second} - // Get the dbcs instance from the cluster dbcsInst := &databasev4.DbcsSystem{} r.Logger.Info("Reconciling DbSystemDetails", "name", req.NamespacedName) - // time.Sleep(200000 * time.Second) // to avoid conflict condition during rapid changes if err := r.KubeClient.Get(ctx, req.NamespacedName, dbcsInst); err != nil { if errors.IsNotFound(err) { // CR was deleted → stop reconciling From 3b221fcaba1a11cd8a528fde67b35a8cfeec416c Mon Sep 17 00:00:00 2001 From: harayuanwang Date: Thu, 28 Aug 2025 14:08:30 +0000 Subject: [PATCH 287/414] Add known issues for ADB controller --- docs/adb/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/adb/README.md b/docs/adb/README.md index e23c3c7b..5b73b7bb 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -623,3 +623,24 @@ To check the logs, use these steps: ```sh kubectl logs -f pod/oracle-database-operator-controller-manager-78666fdddb-s4xcm -n oracle-database-operator-system ``` + +## Known Issues + +### Failed to validate Wallet: "read-only file system" + +In some environments, e.g. OKE using the Operator add-on, the operator fails to validate the wallet due to encountering a **read-only file system** error. This prevents successful wallet validation and can disrupt operator functionality. + +For example, logs from the controller pod may show: + +```text +"error": "Failed to validate Wallet: open /tmp/wallet1208873634.zip: read-only file system" +``` + +#### Workaround + +* Ensure wallet directories and mounted volumes have correct **read-write** permissions. +* Confirm that file system mounts used by the operator are writable. + +#### Reference + +See GitHub issue **#193** in the [oracle-database-operator repository](https://github.com/oracle/oracle-database-operator/issues/193) for details and steps to work around. From af36f5cf1d534d05824f6a207c24a28e98162670 Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Thu, 28 Aug 2025 15:54:08 +0000 Subject: [PATCH 288/414] dbcs fix --- commons/dbcssystem/dbcs_reconciler.go | 10 +++--- controllers/database/dbcssystem_controller.go | 35 ++++++++++++++----- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 87b8e806..e8e4d68e 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -440,7 +440,7 @@ func PatchDBSystem( // It polls the patch history until the patch is SUCCEEDED or FAILED. func CheckPatchState(ctx context.Context, logger logr.Logger, dbClient database.DatabaseClient, dbSystemId *string, patchId string) error { // Maximum wait duration: 120 minutes - timeout := 120 * time.Minute + timeout := 240 * time.Minute start := time.Now() for { @@ -1754,7 +1754,7 @@ func UpdateDbcsSystemIdInst(compartmentId string, log logr.Logger, dbClient data // // Wait for the database to reach the desired state after migration, timeout for 2 hours // Define timeout and check interval - timeout := 2 * time.Hour + timeout := 4 * time.Hour checkInterval := 1 * time.Minute err = WaitForDatabaseState(log, dbClient, databaseID, "AVAILABLE", timeout, checkInterval) @@ -1976,7 +1976,7 @@ func CheckResourceState(logger logr.Logger, dbClient database.DatabaseClient, id var state string var err error - timeout := 120 * time.Minute + timeout := 240 * time.Minute start := time.Now() for { @@ -2410,7 +2410,7 @@ func CreateDbcsBackup( // ---- Wait for backup to complete ---- logger.Info("Waiting for backup to reach ACTIVE state...") - waitDuration := 60 * time.Minute + waitDuration := 240 * time.Minute pollInterval := 30 * time.Second timeout := time.After(waitDuration) ticker := time.NewTicker(pollInterval) @@ -2543,7 +2543,7 @@ func RestoreDbcsToPoint( workRequestId := restoreResp.OpcWorkRequestId logger.Info("Restore initiated", "WorkRequestID", *workRequestId) - timeout := time.After(60 * time.Minute) + timeout := time.After(240 * time.Minute) ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() diff --git a/controllers/database/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go index a30f86d4..6b4a135a 100644 --- a/controllers/database/dbcssystem_controller.go +++ b/controllers/database/dbcssystem_controller.go @@ -750,11 +750,28 @@ func (r *DbcsSystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) dbcsInst.Status.Message = err.Error() return ctrl.Result{}, err } + // Example: assume you have dataGuardRetryCount as an int - err = r.getDataGuardStatusAndUpdate(ctx, dbcsInst, databaseIds[0], *dbcsInst.Spec.Id) - if err != nil { - fmt.Printf("Failed to get dataguard details: %v\n", err) - return ctrl.Result{}, err + if len(databaseIds) > 0 && dbcsInst.Spec.Id != nil { + dataGuardRetryCount := 0 + if dataGuardRetryCount < 5 { + err = r.getDataGuardStatusAndUpdate(ctx, dbcsInst, databaseIds[0], *dbcsInst.Spec.Id) + if err != nil { + dataGuardRetryCount++ + // persist the updated retry count + + fmt.Printf("Failed to get dataguard details (attempt %d/5): %v\n", + dataGuardRetryCount, err) + return ctrl.Result{Requeue: true}, err + } + // reset retry count on success + dataGuardRetryCount = 0 + } else { + fmt.Println("Max retries (5) reached. Failing DataGuard status update.") + return ctrl.Result{}, fmt.Errorf("max retries reached for DataGuard status update") + } + } else { + fmt.Println("Skipping DataGuard status update as DatabaseIds or DbcsSystem ID is nil") } if err := dbcsv4.UpdateDbcsSystemIdInst(compartmentId, r.Logger, r.dbClient, dbcsInst, r.KubeClient, r.nwClient, r.wrClient, databaseIds[0]); err != nil { @@ -1041,7 +1058,7 @@ func (r *DbcsSystemReconciler) DeleteDataGuard( dbcsInst.Status.Message = msg dbcsInst.Status.DataGuardStatus = nil _ = r.KubeClient.Status().Update(ctx, dbcsInst) - return fmt.Errorf(msg) + return nil } if dbcsInst.Status.DataGuardStatus == nil { dbcsInst.Status.DataGuardStatus = &databasev4.DataGuardStatus{} @@ -1151,7 +1168,7 @@ func (r *DbcsSystemReconciler) DeleteDataGuard( dbcsInst.Status.Message = msg _ = r.KubeClient.Status().Update(ctx, dbcsInst) - return fmt.Errorf(msg) + return nil } // Verify Data Guard association exists @@ -1242,7 +1259,7 @@ func (r *DbcsSystemReconciler) DeleteDataGuard( _ = r.KubeClient.Status().Update(ctx, dbcsInst) pollInterval := 30 * time.Second - maxWait := 30 * time.Minute + maxWait := 120 * time.Minute timeout := time.After(maxWait) for { @@ -1364,7 +1381,7 @@ func (r *DbcsSystemReconciler) waitForWorkRequest( } log.Info("Waiting for work request to complete", "workRequestID", *workRequestID) - timeout := time.After(60 * time.Minute) + timeout := time.After(120 * time.Minute) pollInterval := 30 * time.Second for { @@ -2242,7 +2259,7 @@ func (r *DbcsSystemReconciler) createKMSVault(ctx context.Context, kmsConfig *da return nil, err } // Wait until vault becomes active or timeout - timeout := time.After(5 * time.Minute) // Example timeout: 5 minutes + timeout := time.After(15 * time.Minute) // Example timeout: 5 minutes ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() From 601cb75d31fa448f9a1162554d5d4ff291c733a7 Mon Sep 17 00:00:00 2001 From: norman_japheth_aberin Date: Thu, 28 Aug 2025 17:41:10 +0000 Subject: [PATCH 289/414] Fixed check for Azure --- commons/observability/utils.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/commons/observability/utils.go b/commons/observability/utils.go index 2a227226..693c9718 100644 --- a/commons/observability/utils.go +++ b/commons/observability/utils.go @@ -514,7 +514,7 @@ func AddSingleDatabaseEnvs(a *api.DatabaseObserver, e map[string]string, source } // Add OCI Vault Required Values - if IsUsingOCIVault(o) && a.Spec.OCIConfig.ConfigMap.Name != "" { + if a.Spec.OCIConfig.ConfigMap.Name != "" { ociConfig := a.Spec.OCIConfig.ConfigMap.Name source = AddEnv(source, e, EnvVarOCIVaultPrivateKeyPath, DefaultVaultPrivateKeyAbsolutePath) source = AddEnvFromConfigMap(source, e, EnvVarOCIVaultFingerprint, DefaultOCIConfigFingerprintKey, ociConfig) @@ -523,8 +523,8 @@ func AddSingleDatabaseEnvs(a *api.DatabaseObserver, e map[string]string, source source = AddEnvFromConfigMap(source, e, EnvVarOCIVaultRegion, DefaultOCIConfigRegionKey, ociConfig) } - // Add OCI Vault Required Values - if IsUsingAzureVault(z, VaultIDProvided) && a.Spec.AzureConfig.ConfigMap.Name != "" { + // Add Azure Vault Required Values + if a.Spec.AzureConfig.ConfigMap.Name != "" { azureConfig := a.Spec.AzureConfig.ConfigMap.Name source = AddEnvFromConfigMap(source, e, EnvVarAzureTenantID, DefaultAzureConfigTenantId, azureConfig) source = AddEnvFromConfigMap(source, e, EnvVarAzureClientID, DefaultAzureConfigClientId, azureConfig) From 858d63c27932e534208d405f5393824e05675f3e Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 29 Aug 2025 08:16:08 +0000 Subject: [PATCH 290/414] multi tnt. doc correction --- docs/multitenant/README.md | 77 ++++++++++++------------------ docs/multitenant/usecase/README.md | 70 ++++++++++++++------------- 2 files changed, 67 insertions(+), 80 deletions(-) diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index d665e2b8..46b9e9d1 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -1,5 +1,3 @@ - - @@ -13,6 +11,8 @@ - [Create the operator](#create-the-operator) - [ClusterRole and ClusterRoleBinding for NodePort services](#clusterrole-and-clusterrolebinding-for-nodeport-services) - [Container database setup](#container-database-setup) + - [Certificate and credentials](#certificate-and-credentials) + - [Private key 🔑](#private-key-) - [Public Key 🔑](#public-key-) - [Certificates](#certificates) - [Create secrets for certificate and keys](#create-secrets-for-certificate-and-keys) @@ -36,41 +36,26 @@ - [KNOWN ISSUE](#known-issue) - - [Create PDB](#create-pdb) - - [pdb config map](#pdb-config-map) - - [Open PDB](#open-pdb) - - [Close PDB](#close-pdb) - - [Clone PDB](#clone-pdb) - - [Unplug PDB](#unplug-pdb) - - [Plug PDB](#plug-pdb) - - [Delete PDB](#delete-pdb) -- [SQL/PLSQL SCRIPT EXECUTION](#sqlplsql-script-execution) - - [Apply plsql configmap](#apply-plsql-configmap) - - [Limitation](#limitation) -- [TROUBLESHOOTING](#troubleshooting) - - [Get rid of error status](#get-rid-of-error-status) -- [UPGRADE EXISTING INSTALLATION](#upgrade-existing-installation) -- [KNOWN ISSUE](#known-issue) - + **Lrpdb** and **lrest** are two controllers for PDB lifecycle management (**PDBLCM**). They rely on a dedicated REST server (Lite Rest Server) container image. The `lrest` controller is available on the Oracle Container Registry (OCR). The container database can be anywhere (on-premises or in the Cloud). ![generaleschema](./images/Generalschema2.jpg) -## WHAT'S NEW +## 1. WHAT'S NEW -- Version 2.0 introduces the semplification requested by the [issue 170](https://github.com/oracle/oracle-database-operator/issues/170). The **action** fileds no longer exists. This change brings a big semplification in terms of code and symplifies the life cycle command execution. The *kubectl get lrpdb* commands exposes the status of CRD which is reppresented by a bitmask. +- Version 2.0 mplements the request described in the github [issue 170](https://github.com/oracle/oracle-database-operator/issues/170): remove The **action** fileds to reduce the number of input directives and simplif the logic of the reconciliation loop. In addition to that the *kubectl get lrpdb* commands exposes a bitmask which represent the CRD status. ![kubectlget_format](./images/KubectlGetSchema2.jpg) -- **Map** command is no longer available; start the lrest using the **autodoscovery** option. If a pluggable database is created manually via command line then the lrest detects the new pdb and automatically creates the CRD. +- The **Map** action has been replaced by the **autodoscovery** option. If a pluggable database is created manually via command line then the lrest detects the new pdb and automatically creates the CRD. - [sql/plsql sript execution](#sqlplsql-script-execution) this functionality enables the capability of sql/plsql code execution. -### Kubectl get lrpdb format +### 1.1. Kubectl get lrpdb format | Name | Description | |---------------|---------------------------------------------------------| | NAME | The name of the **CRD** | @@ -85,7 +70,7 @@ | BITMASK STATUS| The status of pdb represented using a bitmask | | CONNECT_STRING| The tns string for pdb connection | -### Pdb status table +### 1.2. Pdb status table | Name | Value | Description | |---------|-----------|---------------------------------------------------| @@ -106,23 +91,23 @@ | **OTHER INFO** | | PDBAUT | 0x01000000 | Autodisover | -> Note that in case of error codes the reconciliation loop does not take any action see +> In case of error codes the reconciliation loop does not take any action see [Get rid of error status](#get-rid-of-error-status); human action is required. -## STEP BY STEP CONFIGURATION +## 2. STEP BY STEP CONFIGURATION Complete each of these steps in the order given. -### Multiple namespace setup +### 2.1. Multiple namespace setup Before proceeding with controllers setup, ensure that the Oracle Database Operator (operator) is configured to work with multiple namespaces, as specified in the [README](../../../README.md). -In this document, each controller is running in a dedicated namespace: lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains all the files reported in this document. +In this document, each controller is running in a dedicated namespace: lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains sample files and additional scripts for yaml files customization. Configure the **WACTH_NAMESPACE** list of the operator `yaml` file ```bash sed -i 's/value: ""/value: "oracle-database-operator-system,pdbnamespace,cdbnamespace"/g' oracle-database-operator.yaml ``` -### Apply rolebinding +### 2.2. Apply rolebinding Apply the following files : [`pdbnamespace_binding.yaml`](./usecase/pdbnamespace_binding.yaml) [`cdbnamespace_binding.yaml`](./usecase/cdbnamespace_binding.yaml) @@ -131,7 +116,7 @@ kubectl apply -f pdbnamespace_binding.yaml kubectl apply -f cdbnamespace_binding.yaml ``` -### Create the operator +### 2.3. Create the operator Run the following command: ```bash @@ -146,7 +131,7 @@ oracle-database-operator-controller-manager-796c9b87df-sckf2 1/1 Running oracle-database-operator-controller-manager-796c9b87df-t4qns 1/1 Running 0 22m ``` -### ClusterRole and ClusterRoleBinding for NodePort services +### 2.4. ClusterRole and ClusterRoleBinding for NodePort services To expose services on each node's IP and port (the NodePort), apply the node-rbac.yaml. Note that this step is not required for LoadBalancer services. @@ -155,7 +140,7 @@ To expose services on each node's IP and port (the NodePort), apply the node-rba ``` -### Container database setup +### 2.5. Container database setup On the container database, use the following commands to configure the account for PDB administration: @@ -166,8 +151,6 @@ grant create session to container=all; grant sysdba to container=all; ``` -``` - ### Certificate and credentials You must create the public key, private key, certificates and Kubernetes Secrets for the security configuration. @@ -235,7 +218,7 @@ echo "[WEBUSER PASSWORD]" > wbpass.txt echo "[PDBUSERNAME]" > pdbusr.txt echo "[PDBUSERNAME PASSWORD]" > pdbpwd.txt -## Encrypt the credentials +## 3. Encrypt the credentials openssl rsautl -encrypt -pubin -inkey public.pem -in dbuser.txt |base64 > e_dbuser.txt openssl rsautl -encrypt -pubin -inkey public.pem -in dbpass.txt |base64 > e_dbpass.txt openssl rsautl -encrypt -pubin -inkey public.pem -in wbuser.txt |base64 > e_wbuser.txt @@ -376,7 +359,7 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 ### Openshift configuration For the open shift installation you need to do the following -- Before lrest pod creation; create a security context by appling the following yaml file [security_context.yaml](./usecase/security_context.yaml) mind to specify the correnct **namespace** and the **service name account**. +- Before lrest pod creation; create a security context by appling the yaml file [security_context.yaml](./usecase/security_context.yaml): mind to specify the correnct **namespace** and the **service name account**. ```yaml [...] @@ -421,7 +404,8 @@ SQL> show pdbs 3 PDBDEV MOUNTED SQL> ``` -``Note that after creation, the PDB is not open. You must explicitly open it using a dedicated `yaml` file. + +> Note that after creation, the PDB is not open. You must explicitly open it using a dedicated `yaml` file. **pdb creation** - parameters list @@ -443,7 +427,7 @@ SQL> |cdbPubKey | Secret: public key | |pdbconfigmap | kubernetes config map that contains the PDB initialization (init) parameters | -> NOTE: **assertiveLrpdbDeletion** must be specified for the following PDB actions **CLONE** **CREATE** **PLUG** **MAP**. +> NOTE: **assertiveLrpdbDeletion** needs to be explicitly set for PDB **CLONE** **CREATE** **PLUG** . 🔥 **assertiveLrpdbDeletion** drops pluggable database using **INCLUDE DATAFILES** option @@ -494,9 +478,9 @@ test_invalid_parameter;16;spfile - If specified, the `configmap` is applied during PDB **cloning**, **opening** and **plugging** - The `configmap` is not monitored by the reconciliation loop; this feature will be available in future releases. This means that if someone decides to manually alter an init parameter, then the operator does not take any actions to syncronize PDB configuration with the `configmap`. - **Alter system parameter feature** will be available in future releases. -- An application error with the `configmap` (for whatever reason) does not stop processes from completing. A warning with the associated SQL code is reported in the log file. +- A `configmap` misconfiguration (typo, invalid parameter, invalid value) does not stop the operation. A warning with the associated SQL code is written in the log file. -- **PDB configmap bitmap** status is not reported by the *kubectl get lrpdb* command; in order to verifiy configmap status you need to describe the resource. +- **PDB configmap bitmap** status is not reported by the *kubectl get lrpdb* command; You can describe the resource to verify the bitmap status (*kubectl describe lrpdb ....*). | Name | Value | Description | |---------|-----------|---------------------------------------------------| @@ -634,11 +618,11 @@ kubectl delete lrpdb -n ## SQL/PLSQL SCRIPT EXECUTION -Plsql and sql script can be stored in a kubernetes configmap, each block can be tagged with a label as describe in the following example. +Plsql and sql script can be stored in a kubernetes configmap, each block can be tagged with a label as describe in the example. ```yaml -## PLSQL / SQL Block config schema +## 4. PLSQL / SQL Block config schema apiVersion kind CofigMap @@ -664,8 +648,7 @@ The sql and plsqlcode must be indented using tab (makefile stile). The code bloc kubectl patch lrpdb pdb1 -n pdbnamespace -p '{"spec":{"codeconfigmap":""}}' --type=merge ``` -The **kubectl get** commands shows only the return code of the last plsql code executed. -If you need to see the overall status of the whole config map execution check the events history as reported in the following example +The **kubectl get** commands shows only the return code of the last plsql code executed. Describe the resource if you need to verify the overall status of the whole config map execution; see the events history in the example ```bash /usr/bin/kubectl patch lrpdb pdb1 -n pdbnamespace -p \ @@ -702,7 +685,7 @@ The message format for the **APPLYSQL** is `CODE:SQLCODE '[]':' \ No newline at end of file + diff --git a/docs/multitenant/usecase/README.md b/docs/multitenant/usecase/README.md index 2baa65b0..358ea1ed 100644 --- a/docs/multitenant/usecase/README.md +++ b/docs/multitenant/usecase/README.md @@ -1,28 +1,31 @@ - - + +* 1. [Prerequisites](#Prerequisites) +* 2. [Operator setup](#Operatorsetup) +* 3. [Secrets creation](#Secretscreation) +* 4. [Yaml file creation](#Yamlfilecreation) +* 5. [Run testcase](#Runtestcase) +* 6. [Makefile targets table](#Makefiletargetstable) +* 7. [Diag commands and troubleshooting](#Diagcommandsandtroubleshooting) + * 7.1. [Connect to rest server pod](#Connecttorestserverpod) + * 7.2. [Lrest pod log](#Lrestpodlog) + * 7.3. [Monitor control plane](#Monitorcontrolplane) + * 7.4. [Error decrypting credential](#Errordecryptingcredential) + * 7.5. [Crd details](#Crddetails) + + + -# Use case directory - + -- [Use case directory](#use-case-directory) - - [Prerequisites](#prerequisites) - - [Operator setup](#operator-setup) - - [Secrets creation](#secrets-creation) - - [Yaml file creation](#yaml-file-creation) - - [Run testcase](#run-testcase) - - [Makefile targets table](#makefile-targets-table) - - [Diag commands and troubleshooting](#diag-commands-and-troubleshooting) - - [Connect to rest server pod](#connect-to-rest-server-pod) - - [Lrest pod log](#lrest-pod-log) - - [Monitor control plane](#monitor-control-plane) - - [Error decrypting credential](#error-decrypting-credential) - - [Crd details](#crd-details) - +# Use case directory -The use case directory contains a makefile to automatically install the Oracle Database Operator (namespace scope configuration) and generate the yaml files to test pdb life cycle management in two different namespaces (one for lrest pod the other one for pdb crd). To simplify and speed up the execution you just need to edit a [parameter file](../usecase/parameters.txt) with all the information about your environment. -Subsequent steps are the operator installation, the secret installation, the yaml files generation and finally the tests execution. +The use case directory contains a makefile to automatically install the Oracle Database Operator (namespace scope configuration) and generate the yaml files to test pdb life cycle management in two different namespaces (one for lrest pod the other one for pdb crd). To simplify and speed up the execution you just need to edit a [parameter file](../usecase/parameters.txt) with all the information about your environment. The makefile script uses parameter file to generate all the yaml file required to test the controllers. +After parameters setup there is the operator installation (**make opsetup**) , the secrets installation (**make secrets**), the yaml file generation (**make genyaml**). ![generalschema](../images/usecaseschema.jpg) @@ -52,10 +55,10 @@ OPENSHIFT..............:[boolean] Verify parameters using ``make check`` command. -### Prerequisites +## 1. Prerequisites - Ensure that **kubectl** is properly installed on your client. -- Even if the takes care of the requirements setup read carefully the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) +- Even if the makefile automation, read carefully the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md). (role binding,webcert,etc) - Ensure that the administrative user (admin) on the container database is configured as documented. eg @@ -68,7 +71,7 @@ grant create session to restdba container=all; grant sysdba to restdba container=all; ``` -### Operator setup +## 2. Operator setup ```bash make opsetup @@ -83,21 +86,21 @@ The make target **make opsetup** does the following actions: 👉 **If your are running on Openshift you need to manually apply the [service context file](./security_context.yaml)** -### Secrets creation +## 3. Secrets creation ```bash make secrets ``` **make secrets** creates secrets encrypting the credential specified in the parameters -### Yaml file creation +## 4. Yaml file creation ```bash make genyaml ``` **make genyaml** generates the required `yaml` files to work with multitenant controllers. -### Run testcase +## 5. Run testcase ```bash make runall00 @@ -105,7 +108,7 @@ make runall00 You can run **make runall00** to test all the functionality the multitenant controller -### Makefile targets table +## 6. Makefile targets table | target | action | additional info | |-----------------|---------------------|-----------------| @@ -139,15 +142,16 @@ You can run **make runall00** to test all the functionality the multitenant cont |**secrets** | create secrets | | |**genyaml** | generate the yaml files| | |opclean | deintall the operator| | -## Diag commands and troubleshooting -#### Connect to rest server pod +## 7. Diag commands and troubleshooting + +### 7.1. Connect to rest server pod ```bash /usr/bin/kubectl exec -n -it -- /bin/bash ``` -#### Lrest pod log +### 7.2. Lrest pod log ```bash kubectl logs `kubectl get pods -o custom-columns=:metadata.name -n cdbnamespace --no-headers ` -n cdbnamespace @@ -164,7 +168,7 @@ kubectl exec cdb-dev-lrest-rs-fnw99 -n cdbnamespace -it -- /bin/bash [oracle@cdb-dev-lrest-rs-fnw99 ~]$ ``` -#### Monitor control plane +### 7.3. Monitor control plane ```bash kubectl logs -f -l control-plane=controller-manager -n oracle-database-operator-system @@ -187,7 +191,7 @@ I1029 10:07:20.189724 1 leaderelection.go:250] attempting to acquire leade ``` -#### Error decrypting credential +### 7.4. Error decrypting credential In the following example you can see a resource creation failure due to a decryption issue @@ -201,7 +205,7 @@ In the following example you can see a resource creation failure due to a decryp ```bash openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > mykey ``` -#### Crd details +### 7.5. Crd details Use the **describe** option to obtain `crd` information ```bash From 836c6053dd307a0b6f91b80af2ccafa82701588a Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Fri, 29 Aug 2025 12:11:38 +0000 Subject: [PATCH 291/414] upgrade fix --- commons/dbcssystem/dbcs_reconciler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index e8e4d68e..0ee0da49 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -567,7 +567,7 @@ func UpgradeDatabaseVersion( } // Step 4: Check status - _, err = CheckResourceState(logger, dbClient, databaseId, string(databasev4.Provision), string(databasev4.Available)) + _, err = CheckResourceState(logger, dbClient, *upgradeResp.Id, string(databasev4.Provision), string(databasev4.Available)) if err != nil { logger.Error(err, "Failed to verify database state post upgrade") dbcs.Status.Message = "Failed to verify database state post upgrade" @@ -716,7 +716,7 @@ func UpgradeDatabaseVersion( } // Step 4: Check status - _, err = CheckResourceState(logger, dbClient, databaseId, string(databasev4.Provision), string(databasev4.Available)) + _, err = CheckResourceState(logger, dbClient, *upgradeRespDb.DbSystemId, string(databasev4.Provision), string(databasev4.Available)) if err != nil { logger.Error(err, "Failed to verify database state post upgrade") dbcs.Status.Message = "Upgrade Database Completed successfully." From a2886805f06e1b00cb232f4c43020ddb37bad5ae Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Fri, 29 Aug 2025 16:28:32 +0000 Subject: [PATCH 292/414] upgrade fix --- commons/dbcssystem/dbcs_reconciler.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 0ee0da49..4aa6929b 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -2003,6 +2003,10 @@ func CheckResourceState(logger logr.Logger, dbClient database.DatabaseClient, id logger.Info("DB System is still updating ", "State", state, "Id", id) time.Sleep(60 * time.Second) continue + case string(database.DbSystemLifecycleStateUpgrading): + logger.Info("DB System is still upgrading ", "State", state, "Id", id) + time.Sleep(60 * time.Second) + continue case transientState: logger.Info("DB System still in transient state", "State", state, "Id", id) time.Sleep(60 * time.Second) // sleep before re-checking From d9d4972ea365439e9c4d58b3280ec12c431e8a20 Mon Sep 17 00:00:00 2001 From: Ting-Lan Wang Date: Fri, 29 Aug 2025 13:23:19 -0400 Subject: [PATCH 293/414] Remove unused webhook rule --- .../v1alpha1/autonomousdatabasebackup_webhook.go | 5 ----- .../autonomousdatabasebackup_webhook_test.go | 9 --------- apis/database/v4/autonomousdatabasebackup_webhook.go | 5 ----- .../database/autonomousdatabase_controller.go | 12 ++++++++++++ 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go index 004d6f92..d4291135 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go @@ -92,11 +92,6 @@ func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runti field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if backup.Spec.Target.K8sAdb.Name != nil && backup.Spec.Target.OciAdb.Id != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sAdb or ociAdb, but not both")) - } - if len(allErrs) == 0 { return nil, nil } diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go index d9a254a7..f36b1fff 100644 --- a/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go @@ -80,15 +80,6 @@ var _ = Describe("test AutonomousDatabaseBackup webhook", func() { validateInvalidTest(backup, false, errMsg) }) - - It("Should specify either k8sAdb or ociAdb, but not both", func() { - var errMsg string = "specify either k8sAdb or ociAdb, but not both" - - backup.Spec.Target.K8sAdb.Name = common.String("fake-target-adb") - backup.Spec.Target.OciAdb.Id = common.String("fake.ocid1.autonomousdatabase.oc1...") - - validateInvalidTest(backup, false, errMsg) - }) }) Describe("Test ValidateUpdate of the AutonomousDatabaseBackup validating webhook", func() { diff --git a/apis/database/v4/autonomousdatabasebackup_webhook.go b/apis/database/v4/autonomousdatabasebackup_webhook.go index 1ab8cfc1..345d186a 100644 --- a/apis/database/v4/autonomousdatabasebackup_webhook.go +++ b/apis/database/v4/autonomousdatabasebackup_webhook.go @@ -93,11 +93,6 @@ func (r *AutonomousDatabaseBackup) ValidateCreate(ctx context.Context, obj runti field.Forbidden(field.NewPath("spec").Child("target"), "target ADB is empty")) } - if backup.Spec.Target.K8sAdb.Name != nil && backup.Spec.Target.OciAdb.Id != nil { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("target"), "specify either k8sAdb or ociAdb, but not both")) - } - if len(allErrs) == 0 { return nil, nil } diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 5d982a36..2c89c905 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -303,6 +303,18 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R specChanged = true } + /****************************************************************** + * Sync AutonomousDatabase Backups from OCI. + * The backups will not be synced when the lifecycle state is + * TERMINATING or TERMINATED. + ******************************************************************/ + if desiredAdb.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminating && + desiredAdb.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminated { + if err := r.syncBackupResources(logger, desiredAdb); err != nil { + return r.manageError(logger.WithName("syncBackupResources"), desiredAdb, err) + } + } + /***************************************************** * Validate Wallet *****************************************************/ From f7d6646bca1d0a90ea24682c828296c414bcc857 Mon Sep 17 00:00:00 2001 From: Ting-Lan Wang Date: Fri, 29 Aug 2025 14:55:05 -0400 Subject: [PATCH 294/414] Update Openshift support section and README.md --- README.md | 4 ++-- docs/adb/README.md | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ec52d13..936f8c37 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ In this v2.0 production release, `OraOperator` supports the following database c * **Globally Distributed Database** - Support for Oracle Database 23ai Raft replication * **Autonomous Database** - - Support for Database cloning + - Support for Database manual failover and switchover * **Multitenant DB** - ORDS-based Controller: Assertive deletion policy - New LRES-based Controller (ARM & AM) @@ -71,7 +71,7 @@ In this v2.0 production release, `OraOperator` supports the following database c ## Overall Features Summary As of v2.0.0, the Oracle Database Operator for Kubernetes (`OraOperator`) supports the following lifecycle operations: -* **ADB-S/ADB-D**: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning +* **ADB-S/ADB-D**: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning, manual failover, switchover * **ACD**: Provision, bind, restart, terminate (soft/hard) * **SIDB**: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (basic console), Oracle REST Data Service (ORDS), PDB management, SQL Developer Web, Application Express (Apex), Resource management, True Cache, Observer for FastStartFailover (Data Guard), Snapshot Standby (Data Guard) * **Oracle Restart**: Provision and manage Oracle Restart environments diff --git a/docs/adb/README.md b/docs/adb/README.md index 5b73b7bb..4b2412ef 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -596,6 +596,18 @@ Autonomous Database controller uses Kubernetes objects such as: The defintion of all the Kubernetes Objects, which are to be used by the Oracle Autonomous Database Controller, comes from the `oracle-database-operator.yaml` file which is applied to deploy the **Oracle Database Operator**. +## OpenShift Support + +The Autonomous Database (ADB) Controller has been tested on OpenShift clusters and verified to work with the following use cases: + +* **Create** – Provision a new Autonomous Database +* **Sync (Binding)** – Synchronize with existing Autonomous Database resources +* **Update** – Apply configuration changes +* **Stop** – Stop an Autonomous Database instance +* **Start** – Start an Autonomous Database instance +* **Terminate** – Delete an Autonomous Database instance +* **Clone** – Create a clone from an existing Autonomous Database + ## Debugging and troubleshooting ### Show the details of the resource From f53b497782e20491fcfb37c461f91c5e59b83814 Mon Sep 17 00:00:00 2001 From: Ting-Lan Wang Date: Fri, 29 Aug 2025 16:57:39 -0400 Subject: [PATCH 295/414] Fix hard delete --- controllers/database/autonomousdatabase_controller.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 2c89c905..989e7b5d 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -278,6 +278,7 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, desiredAdb, ADB_FINALIZER); err != nil { return emptyResult, fmt.Errorf("Failed to remove finalizer to Autonomous Database "+desiredAdb.Name+": %w", err) } + return emptyResult, nil } else { // Remove the Autonomous Database in OCI. // Change the action to Terminate and proceed with the rest of the reconcile logic @@ -329,7 +330,7 @@ func (r *AutonomousDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R /****************************************************************** * Update the Autonomous Database at the end of every reconcile. ******************************************************************/ - if specChanged { + if specChanged && desiredAdb.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminating { if err := r.KubeClient.Update(context.TODO(), desiredAdb); err != nil { return r.manageError( logger.WithName("updateSpec"), From 42ac8453945abf946935ca133269524934ffc3a2 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Sun, 31 Aug 2025 19:55:33 +0000 Subject: [PATCH 296/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 9 ++- commons/oraclerestart/oraclerestartprov.go | 71 +++++++------------ .../database/oraclerestart_controller.go | 16 +++-- 3 files changed, 43 insertions(+), 53 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 0810d20e..2f2355a9 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -79,6 +79,7 @@ type OracleRestartSpec struct { IsManual bool `json:"isManual,omitempty"` SrvAccountName string `json:"serviceAccountName,omitempty"` StorageClass string `json:"storageClass,omitempty"` + LbService OracleRestartNodePortSvc `json:"lbService,omitempty"` } type AsmDiskDetails struct { @@ -270,9 +271,11 @@ type OracleRestartStatus struct { } type OracleRestartNodePortSvc struct { - PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` - SvcName string `json:"name,omitempty"` - SvcType string `json:"svcType,omitempty"` + PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` + SvcName string `json:"name,omitempty"` + SvcType string `json:"svcType,omitempty"` + SvcLBIP string `json:"paiLBIP,omitempty"` + SvcAnnotation map[string]string `json:"pailbAnnotation,omitempty"` } type OracleRestartPortMapping struct { diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index c6033873..4fed2e31 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -694,52 +694,44 @@ func BuildExternalServiceDefForOracleRestart(instance *oraclerestart.OracleResta var npSvc oraclerestart.OracleRestartNodePortSvc - if OracleRestartSpex.PortMappings != nil { - npSvc = OracleRestartSpex.NodePortSvc[index] - npSvc.PortMappings = OracleRestartSpex.PortMappings - } - service := &corev1.Service{ ObjectMeta: buildSvcObjectMetaForOracleRestart(instance, index, OracleRestartSpex, opType), Spec: corev1.ServiceSpec{}, } - var exSvcPorts oraclerestart.OracleRestartPortMapping - var exSvc oraclerestart.OracleRestartNodePortSvc + // If user is setting Node Port Service if opType == "nodeport" { - // service.Spec.ClusterIP = string(corev1.ServiceTypeNodePort) - if strings.EqualFold(npSvc.SvcType, "vip") { - service.Spec.Selector = getSvcLabelsForOracleRestart(0, OracleRestartSpex) - } else if strings.EqualFold(npSvc.SvcType, "scan") { - service.Spec.Selector = buildLabelsForOracleRestart(instance, "OracleRestart") - } else { - service.Spec.Selector = getSvcLabelsForOracleRestart(0, OracleRestartSpex) + if OracleRestartSpex.PortMappings != nil { + npSvc = OracleRestartSpex.NodePortSvc[index] + npSvc.PortMappings = OracleRestartSpex.PortMappings } - } - if opType == "onssvc" { - //exSvc.SvcName = OracleRestartSpex.Name + "-0-ons" - exSvc.SvcType = svctype + service.Spec.Type = corev1.ServiceTypeNodePort + } else if opType == "lbsvc" { + npSvc = instance.Spec.LbService + npSvc.PortMappings = instance.Spec.LbService.PortMappings + service.Spec.Type = corev1.ServiceTypeClusterIP + } else if opType == "onssvc" { + var exSvcPorts oraclerestart.OracleRestartPortMapping + var exSvc oraclerestart.OracleRestartNodePortSvc + npSvc.SvcType = svctype exSvcPorts.NodePort = *OracleRestartSpex.OnsTargetPort + npSvc.PortMappings[index].NodePort = *OracleRestartSpex.OnsTargetPort if OracleRestartSpex.OnsLocalPort != nil { exSvcPorts.Port = *OracleRestartSpex.OnsLocalPort } else { exSvcPorts.Port = 6200 } - exSvc.PortMappings = append(exSvc.PortMappings, exSvcPorts) - service.Spec.Selector = getSvcLabelsForOracleRestart(0, OracleRestartSpex) npSvc = exSvc } - service.Spec.Ports = buildOracleRestartSvcPortsDef(npSvc) - - if svctype == "nodeport" { - service.Spec.Type = corev1.ServiceTypeNodePort + //service.Name = npSvc.SvcName + if len(instance.Spec.LbService.SvcAnnotation) != 0 { + service.Annotations = instance.Spec.LbService.SvcAnnotation } - if svctype == "lbservice" { - service.Spec.Type = corev1.ServiceTypeClusterIP - } + service.Spec.Selector = getSvcLabelsForOracleRestart(0, OracleRestartSpex) + service.Spec.Ports = buildOracleRestartSvcPortsDef(npSvc) // build Service Ports Specs to be exposed. If the PortMappings is not set then default ports will be exposed. @@ -752,21 +744,12 @@ func buildSvcObjectMetaForOracleRestart(instance *oraclerestart.OracleRestart, r //var svcName string var labelStr map[string]string - if strings.ToUpper(svctype) == "VIP" || strings.ToUpper(svctype) == "LOCAL" || strings.ToUpper(svctype) == "ONSSVC" || strings.ToUpper(svctype) == "LSNRSVC" { - labelStr = getSvcLabelsForOracleRestart(replicaCount, OracleRestartSpex) - } else if strings.ToUpper(svctype) == "SCAN" { - labelStr = nil - } else { - labelStr = nil - } + labelStr = getSvcLabelsForOracleRestart(replicaCount, OracleRestartSpex) objmeta := metav1.ObjectMeta{ Name: getOracleRestartSvcName(instance, OracleRestartSpex, svctype), Namespace: instance.Namespace, - } - - if labelStr != nil { - objmeta.Labels = labelStr + Labels: labelStr, } return objmeta @@ -777,16 +760,12 @@ func getOracleRestartSvcName(instance *oraclerestart.OracleRestart, OracleRestar switch svcType { case "local": return OracleRestartSpex.Name + "-0" - // case "vip": - // return OracleRestartSpex.VipSvcName - // case "scan": - // return instance.Spec.ScanSvcName case "onssvc": return OracleRestartSpex.Name + "-0-ons" - case "lsnrsvc": - return OracleRestartSpex.Name + "-0-lsnr" - // case "scansvc": - // return instance.Spec.ScanSvcName + "-lsnr" + case "lbsvc": + return OracleRestartSpex.Name + "-0-lbsvc" + case "nodeport": + return OracleRestartSpex.Name + "-0-npsvc" default: return OracleRestartSpex.Name diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 8d6efc28..524add2a 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -270,6 +270,14 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } + if len(oracleRestart.Spec.LbService.SvcName) != 0 && len(oracleRestart.Spec.LbService.PortMappings) != 0 { + result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildExternalServiceDefForOracleRestart(oracleRestart, 0, oracleRestart.Spec.InstDetails, svcType, "lbservice")) + if err != nil { + result = resultNq + return result, err + } + } + r.ensureAsmStorageStatus(oracleRestart) isNewSetup := true for _, diskgroup := range oracleRestart.Status.AsmDetails.Diskgroup { @@ -2115,19 +2123,19 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or data = append(data, "DB_RECOVERY_FILE_DEST_SIZE="+instance.Spec.ConfigParams.DbRecoveryFileDestSize) } if instance.Spec.ConfigParams.DBAsmDiskDgRedundancy != "" { - data = append(data, "DB_ASMDG_PROPERTIES="+"redudancy="+instance.Spec.ConfigParams.DBAsmDiskDgRedundancy) + data = append(data, "DB_ASMDG_PROPERTIES="+"redudancy:"+instance.Spec.ConfigParams.DBAsmDiskDgRedundancy) } if instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy != "" { - data = append(data, "REDO_ASMDG_PROPETRIES="+"redudancy="+instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy) + data = append(data, "REDO_ASMDG_PROPETRIES="+"redudancy:"+instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy) } if instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy != "" { - data = append(data, "RECO_ASMDG_PROPETRIES="+"redudancy="+instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy) + data = append(data, "RECO_ASMDG_PROPETRIES="+"redudancy:"+instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy) } if instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy != "" { - data = append(data, "CRS_ASMDG_REDUNDANCY="+"redudancy="+instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy) + data = append(data, "CRS_ASMDG_REDUNDANCY="+"redudancy:"+instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy) } } From ef2b18451e8fd2bb8579f51385ebc03d7b21cf67 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Sun, 31 Aug 2025 21:34:42 +0000 Subject: [PATCH 297/414] autodiscovery fix, RFC1123, documentation bug --- controllers/database/lrest_controller.go | 29 ++- controllers/database/lrpdb_controller.go | 233 +++++++++++++---------- docs/multitenant/README.md | 127 ++++++------ docs/multitenant/usecase/makefile | 53 +++++- 4 files changed, 269 insertions(+), 173 deletions(-) diff --git a/controllers/database/lrest_controller.go b/controllers/database/lrest_controller.go index f8682e54..a7b07816 100644 --- a/controllers/database/lrest_controller.go +++ b/controllers/database/lrest_controller.go @@ -291,8 +291,24 @@ func (r *LRESTReconciler) validateLRESTPods2(ctx context.Context, req ctrl.Reque log := r.Log.WithValues("validateLRESTPod2", req.NamespacedName) log.Info("Validating Pod creation for :" + lrest.Name) - /* Just check the number of pdbs to verify lrest server status */ - _, err := r.SelectFromVpdbs(ctx, req, lrest) + /* + _, err := r.SelectFromVpdbs(ctx, req, lrest) + if err != nil { + log.Info("LREST is not ready ", "Namespace", req.Namespace) + lrest.Status.Msg = "Waiting for LREST Pod(s) to be read" + return errors.New("Waiting for LREST pods to be ready") + } + */ + + /* Using a smarter and ligther method to validate the pod + No need to read the whole v$pdbs*/ + RestPort := lrest.Spec.LRESTPort + RestName := lrest.Name + "-lrest" + RestNmsp := lrest.Namespace + Ip := RestName + "." + RestNmsp + ":" + strconv.Itoa(RestPort) + + url := "https://" + Ip + "/database/pdbs/PDB$SEED/status/" + _, err := NewCallAPIAllPdbs(r, ctx, req, lrest, url, nil, "GET") if err != nil { log.Info("LREST is not ready ", "Namespace", req.Namespace) lrest.Status.Msg = "Waiting for LREST Pod(s) to be read" @@ -1265,12 +1281,15 @@ func (r *LRESTReconciler) LrpdbCreation(ctx context.Context, req ctrl.Request, l } log.Info("NamesSpaceAutoDiscover := " + NamesSpaceAutoDiscover) + Resname := "atd-" + strings.ToLower(dbinfo[idx].(map[string]interface{})["name"].(string)) + Resname = strings.ReplaceAll(Resname, "_", "-") + obj := &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "database.oracle.com/v4", "kind": "LRPDB", "metadata": map[string]interface{}{ - "name": "atd-" + strings.ToLower(dbinfo[idx].(map[string]interface{})["name"].(string)), + "name": Resname, "namespace": NamesSpaceAutoDiscover, }, "spec": map[string]interface{}{ @@ -1313,7 +1332,7 @@ func (r *LRESTReconciler) LrpdbCreation(ctx context.Context, req ctrl.Request, l } else { log.Info("Custom resource created successfully ") fmt.Printf("obj:%s\n", result) - r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "LrpdbCreation", "created lrpdb:%s", "atd-"+dbinfo[idx].(map[string]interface{})["name"].(string)) + r.Recorder.Eventf(lrest, corev1.EventTypeNormal, "LrpdbCreation", "created lrpdb:%s", Resname) } @@ -1321,7 +1340,7 @@ func (r *LRESTReconciler) LrpdbCreation(ctx context.Context, req ctrl.Request, l err = r.Get(context.Background(), client.ObjectKey{ Namespace: NamesSpaceAutoDiscover, - Name: "atd-" + strings.ToLower(dbinfo[idx].(map[string]interface{})["name"].(string)), + Name: Resname, }, &lrpdb) lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBAUT) diff --git a/controllers/database/lrpdb_controller.go b/controllers/database/lrpdb_controller.go index 2f44fb39..10c92739 100644 --- a/controllers/database/lrpdb_controller.go +++ b/controllers/database/lrpdb_controller.go @@ -208,6 +208,7 @@ var globalsqlcode int //+kubebuilder:rbac:groups=database.oracle.com,resources=events,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=database.oracle.com,resources=lrpdbs/status,verbs=get;update;patch //+kubebuilder:rbac:groups=database.oracle.com,resources=lrpdbs/finalizers,verbs=get;create;update;patch;delete +//+kubebuilder:rbac:groups=database.oracle.com,resources=lrpdbs/configmaps,verbs=get;create;update;patch;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -243,7 +244,7 @@ func (r *LRPDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl } /**** CREATE ****/ - if Bit(lrpdb.Status.PDBBitMask, PDBCRT|PDBCRE) == false && lrpdb.Spec.SrcLRPDBName == "" && lrpdb.Spec.XMLFileName == "" { + if Bit(lrpdb.Status.PDBBitMask, PDBCRT) == false && Bit(lrpdb.Status.PDBBitMask, PDBCRE) == false && lrpdb.Spec.SrcLRPDBName == "" && lrpdb.Spec.XMLFileName == "" { log.Info("REC. LOOP: create pdb") err = r.CreateLRPDB(ctx, req, lrpdb) if err != nil { @@ -263,7 +264,7 @@ func (r *LRPDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl if Bit(lrpdb.Status.PDBBitMask, FNALAZ) == false && Bit(lrpdb.Status.PDBBitMask, PDBCRT) == true { if lrpdb.ObjectMeta.DeletionTimestamp.IsZero() { if !controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { - log.Info("REC. LOOP: add finalizer") + log.Info("add finalizer:" + lrpdb.Spec.LRPDBName) controllerutil.AddFinalizer(lrpdb, LRPDBFinalizer) if err := r.Update(ctx, lrpdb); err != nil { log.Info("Cannot add finalizer") @@ -278,7 +279,7 @@ func (r *LRPDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl } /**** OPEN ****/ - if lrpdb.Spec.LRPDBState == "OPEN" && Bit(lrpdb.Status.PDBBitMask, PDBOPN|PDBOPE) == false { + if lrpdb.Spec.LRPDBState == "OPEN" && Bit(lrpdb.Status.PDBBitMask, PDBOPN) == false && Bit(lrpdb.Status.PDBBitMask, PDBOPE) == false { log.Info("REC. LOOP: open pdb") err = r.OpenLRPDB(ctx, req, lrpdb) if err != nil { @@ -402,8 +403,19 @@ func (r *LRPDBReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl if lrpdb.Spec.PDBBitMask != 0 && lrpdb.Spec.LRPDBState == "RESET" { log.Info("REC. LOOP: reset state") lrpdb.Status.PDBBitMask = lrpdb.Spec.PDBBitMask + log.Info("lrpdb.Status.PDBBitMask:" + strconv.Itoa(lrpdb.Status.PDBBitMask)) + log.Info("lrpdb.Spec.PDBBitMask:" + strconv.Itoa(lrpdb.Spec.PDBBitMask)) + if Bit(lrpdb.Spec.PDBBitMask, PDBAUT) == true { + log.Info("reset state PDBAUT") + if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, FNALAZ) + } + + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCRT) + } lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) r.UpdateStatus(ctx, req, lrpdb) + lrpdb.Spec.PDBBitMask = 0 lrpdb.Spec.LRPDBState = "NONE" @@ -871,65 +883,68 @@ func (r *LRPDBReconciler) DeleteLRPDB(ctx context.Context, req ctrl.Request, lrp return err } - /* Close the pdb if it's open */ - if Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true { - valuesclose := map[string]string{ - "state": "CLOSE", - "modifyOption": "IMMEDIATE", - "getScript": "FALSE"} - lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName - respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, valuesclose, "POST") - r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) - if lrpdb.Status.SqlCode != 0 { - oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) - lrpdb.Status.Msg = "close:[" + oer + "]" + if lrpdb.Spec.ImperativeLrpdbDeletion == true { + /* Close the pdb if it's open */ + if Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true { + valuesclose := map[string]string{ + "state": "CLOSE", + "modifyOption": "IMMEDIATE", + "getScript": "FALSE"} + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, valuesclose, "POST") + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + if lrpdb.Status.SqlCode != 0 { + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "close:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + } + if err != nil { + log.Info("Warning error closing lrpdb continue anyway") + + } + lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBOPN) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCLS) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) r.UpdateStatus(ctx, req, lrpdb) - } - if err != nil { - log.Info("Warning error closing lrpdb continue anyway") } - lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBOPN) - lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCLS) - lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) - r.UpdateStatus(ctx, req, lrpdb) - } + values := map[string]string{ + "action": "INCLUDING", + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - values := map[string]string{ - "action": "INCLUDING", - "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + if lrpdb.Spec.DropAction != "" { + values["action"] = lrpdb.Spec.DropAction + } - if lrpdb.Spec.DropAction != "" { - values["action"] = lrpdb.Spec.DropAction - } + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName - lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "DELETE") + if err != nil { + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + return err + } - respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "DELETE") - if err != nil { - log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) - return err - } + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + if lrpdb.Status.SqlCode != 0 { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, FNALAE) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "delete:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + return err + } else { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBDIC) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) + } - r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) - globalsqlcode = lrpdb.Status.SqlCode - if lrpdb.Status.SqlCode != 0 { - lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, FNALAE) - lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) - oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) - lrpdb.Status.Msg = "delete:[" + oer + "]" - r.UpdateStatus(ctx, req, lrpdb) - return err - } else { - lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBDIC) - lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) - r.UpdateStatus(ctx, req, lrpdb) } - log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdbName) + log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdb.Spec.LRPDBName) controllerutil.RemoveFinalizer(lrpdb, LRPDBFinalizer) if err := r.Update(ctx, lrpdb); err != nil { @@ -958,65 +973,66 @@ func (r *LRPDBReconciler) DeleteLRPDBDeclarative(ctx context.Context, req ctrl.R return err } - /* Close the pdb if it's open */ - if Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true { - valuesclose := map[string]string{ - "state": "CLOSE", - "modifyOption": "IMMEDIATE", - "getScript": "FALSE"} - lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName - respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, valuesclose, "POST") - r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) - if lrpdb.Status.SqlCode != 0 { - oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) - lrpdb.Status.Msg = "close:[" + oer + "]" + if lrpdb.Spec.ImperativeLrpdbDeletion == true { + /* Close the pdb if it's open */ + if Bit(lrpdb.Status.PDBBitMask, PDBOPN) == true { + valuesclose := map[string]string{ + "state": "CLOSE", + "modifyOption": "IMMEDIATE", + "getScript": "FALSE"} + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, valuesclose, "POST") + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + if lrpdb.Status.SqlCode != 0 { + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "close:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + } + if err != nil { + log.Info("Warning error closing lrpdb continue anyway") + + } + lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBOPN) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCLS) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) r.UpdateStatus(ctx, req, lrpdb) - } - if err != nil { - log.Info("Warning error closing lrpdb continue anyway") } - lrpdb.Status.PDBBitMask = Bid(lrpdb.Status.PDBBitMask, PDBOPN) - lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCLS) - lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) - r.UpdateStatus(ctx, req, lrpdb) - } - - values := map[string]string{ - "action": "INCLUDING", - "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} + values := map[string]string{ + "action": "INCLUDING", + "getScript": strconv.FormatBool(*(lrpdb.Spec.GetScript))} - if lrpdb.Spec.DropAction != "" { - values["action"] = lrpdb.Spec.DropAction - } + if lrpdb.Spec.DropAction != "" { + values["action"] = lrpdb.Spec.DropAction + } - lrpdbName := lrpdb.Spec.LRPDBName - url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName + lrpdbName := lrpdb.Spec.LRPDBName + url := r.BaseUrl(ctx, req, lrpdb, lrest) + lrpdbName - respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "DELETE") - if err != nil { - log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) - return err - } + respData, err := NewCallAPISQL(r, ctx, req, lrpdb, url, values, "DELETE") + if err != nil { + log.Error(err, "Failure NewCallAPISQL( "+url+")", "err", err.Error()) + return err + } - r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) - globalsqlcode = lrpdb.Status.SqlCode - if lrpdb.Status.SqlCode != 0 { - lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, FNALAE) - lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) - oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) - lrpdb.Status.Msg = "delete:[" + oer + "]" - r.UpdateStatus(ctx, req, lrpdb) - return err - } else { - lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBDIC) - lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) - r.UpdateStatus(ctx, req, lrpdb) + r.GetSqlCode(respData, &(lrpdb.Status.SqlCode)) + globalsqlcode = lrpdb.Status.SqlCode + if lrpdb.Status.SqlCode != 0 { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, FNALAE) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + oer := fmt.Sprintf("ORA-%d", lrpdb.Status.SqlCode) + lrpdb.Status.Msg = "delete:[" + oer + "]" + r.UpdateStatus(ctx, req, lrpdb) + return err + } else { + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBDIC) + lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) + r.UpdateStatus(ctx, req, lrpdb) + } } - - log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdbName) + log.Info("Successfully dropped LRPDB", "LRPDB Name", lrpdb.Spec.LRPDBName) if controllerutil.ContainsFinalizer(lrpdb, LRPDBFinalizer) { log.Info("Removing finalizer") @@ -1356,7 +1372,7 @@ func (r *LRPDBReconciler) CreateLRPDB(ctx context.Context, req ctrl.Request, lrp return err } /* If it's not created by lrest autodiscover */ - if Bit(lrpdb.Status.PDBBitMask, PDBAUT) == false { + if Bit(lrpdb.Status.PDBBitMask, PDBAUT) == false && lrpdb.Spec.PDBBitMask == 0 { var err error var tde_Password string @@ -1477,6 +1493,7 @@ func (r *LRPDBReconciler) CreateLRPDB(ctx context.Context, req ctrl.Request, lrp } else { log.Info("CRD created by autodiscover") lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBCRT) + lrpdb.Status.PDBBitMask = Bis(lrpdb.Status.PDBBitMask, PDBAUT) lrpdb.Status.PDBBitMaskStr = Bitmaskprint(lrpdb.Status.PDBBitMask) lrpdb.Status.ConnString = strings.TrimSpace(lrest.Spec.DBTnsurl) parseTnsAlias(&(lrpdb.Status.ConnString), &(lrpdb.Spec.LRPDBName)) @@ -2186,6 +2203,10 @@ func (r *LRPDBReconciler) InitConfigMap(ctx context.Context, req ctrl.Request, l log.Info("Generating an empty configmap") globalconfigmap = "configmap-" + lrpdb.Spec.LRPDBName + "-default" + // RFC 1123 + globalconfigmap = strings.ToLower(globalconfigmap) + globalconfigmap = strings.ReplaceAll(globalconfigmap, "_", "-") + DbParameters := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ Kind: "configmap", @@ -2203,8 +2224,14 @@ func (r *LRPDBReconciler) InitConfigMap(ctx context.Context, req ctrl.Request, l return nil } + if err := r.Create(ctx, DbParameters); err != nil { + log.Error(err, "Failed to create the default configmap", "Namespace", lrpdb.Namespace, "Default configmap", globalconfigmap) + return nil + } + /* Update Spec.PDBConfigMap */ - lrpdb.Spec.PDBConfigMap = "configmap" + lrpdb.Spec.LRPDBName + "default" + //lrpdb.Spec.PDBConfigMap = "configmap" + lrpdb.Spec.LRPDBName + "default" + lrpdb.Spec.PDBConfigMap = globalconfigmap if err := r.Update(ctx, lrpdb); err != nil { log.Error(err, "Failure updating Spec.PDBConfigMap ", "err", err.Error()) return nil diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index 46b9e9d1..ce2691ea 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -1,41 +1,42 @@ - - - - -- [WHAT'S NEW](#whats-new) - - [Kubectl get lrpdb format](#kubectl-get-lrpdb-format) - - [Pdb status table](#pdb-status-table) -- [STEP BY STEP CONFIGURATION](#step-by-step-configuration) - - [Multiple namespace setup](#multiple-namespace-setup) - - [Apply rolebinding](#apply-rolebinding) - - [Create the operator](#create-the-operator) - - [ClusterRole and ClusterRoleBinding for NodePort services](#clusterrole-and-clusterrolebinding-for-nodeport-services) - - [Container database setup](#container-database-setup) - - [Certificate and credentials](#certificate-and-credentials) - - [Private key 🔑](#private-key-) - - [Public Key 🔑](#public-key-) - - [Certificates](#certificates) - - [Create secrets for certificate and keys](#create-secrets-for-certificate-and-keys) - - [Create secrets with encrypted password](#create-secrets-with-encrypted-password) - - [Create lrest pod](#create-lrest-pod) - - [Openshift configuration](#openshift-configuration) - - [Create PDB](#create-pdb) - - [pdb config map](#pdb-config-map) - - [Open PDB](#open-pdb) - - [Close PDB](#close-pdb) - - [Clone PDB](#clone-pdb) - - [Unplug PDB](#unplug-pdb) - - [Plug PDB](#plug-pdb) - - [Delete PDB](#delete-pdb) -- [SQL/PLSQL SCRIPT EXECUTION](#sqlplsql-script-execution) - - [Apply plsql configmap](#apply-plsql-configmap) - - [Limitation](#limitation) -- [TROUBLESHOOTING](#troubleshooting) - - [Get rid of error status](#get-rid-of-error-status) -- [UPGRADE EXISTING INSTALLATION](#upgrade-existing-installation) -- [KNOWN ISSUE](#known-issue) - - + +* 1. [WHAT'S NEW](#WHATSNEW) + * 1.1. [Kubectl get lrpdb format](#Kubectlgetlrpdbformat) + * 1.2. [Pdb status table](#Pdbstatustable) +* 2. [STEP BY STEP CONFIGURATION](#STEPBYSTEPCONFIGURATION) + * 2.1. [Multiple namespace setup](#Multiplenamespacesetup) + * 2.2. [Apply rolebinding](#Applyrolebinding) + * 2.3. [Create the operator](#Createtheoperator) + * 2.4. [ClusterRole and ClusterRoleBinding for NodePort services](#ClusterRoleandClusterRoleBindingforNodePortservices) + * 2.5. [Container database setup](#Containerdatabasesetup) + * 2.6. [Certificate and credentials](#Certificateandcredentials) + * 2.6.1. [Private key 🔑](#Privatekey) + * 2.6.2. [Public Key 🔑](#PublicKey) + * 2.6.3. [Certificates](#Certificates) + * 2.7. [Create secrets for certificate and keys](#Createsecretsforcertificateandkeys) + * 2.8. [Create secrets with encrypted password](#Createsecretswithencryptedpassword) + * 2.9. [Create lrest pod](#Createlrestpod) + * 2.10. [Openshift configuration](#Openshiftconfiguration) + * 2.11. [Create PDB](#CreatePDB) + * 2.11.1. [pdb config map](#pdbconfigmap) + * 2.12. [Open PDB](#OpenPDB) + * 2.13. [Close PDB](#ClosePDB) + * 2.14. [Clone PDB ###](#ClonePDB) + * 2.15. [Unplug PDB](#UnplugPDB) + * 2.16. [Plug PDB](#PlugPDB) + * 2.17. [Delete PDB](#DeletePDB) +* 3. [SQL/PLSQL SCRIPT EXECUTION](#SQLPLSQLSCRIPTEXECUTION) + * 3.1. [Apply plsql configmap](#Applyplsqlconfigmap) + * 3.2. [Limitation](#Limitation) +* 4. [TROUBLESHOOTING](#TROUBLESHOOTING) + * 4.1. [Get rid of error status](#Getridoferrorstatus) +* 5. [UPGRADE EXISTING INSTALLATION](#UPGRADEEXISTINGINSTALLATION) +* 6. [KNOWN ISSUES](#KNOWNISSUES) + + + @@ -151,22 +152,22 @@ grant create session to container=all; grant sysdba to container=all; ``` -### Certificate and credentials +### 2.6. Certificate and credentials You must create the public key, private key, certificates and Kubernetes Secrets for the security configuration. -#### Private key 🔑 +#### 2.6.1. Private key 🔑 > Note: Only private key **PCKS8** format is supported by LREST controllers. Before you start configuration, ensure that you can use it. If you are using [`openssl3`](https://docs.openssl.org/master/) then `pcks8` is generated by default. If it is not already generated, then use the following command to create a `pcks8` private key ```bash openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out private.key ``` -#### Public Key 🔑 +#### 2.6.2. Public Key 🔑 Create the public key. ```bash /usr/bin/openssl rsa -in private.key -outform PEM -pubout -out public.pem ``` -#### Certificates +#### 2.6.3. Certificates Create certificates. ```bash openssl req -new -x509 -days 365 -key private.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out ca.crt @@ -181,7 +182,7 @@ openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=CN/ST=GD/L=SZ/O=or /usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey private.key -CAcreateserial -out tls.crt ``` -### Create secrets for certificate and keys +### 2.7. Create secrets for certificate and keys Create the Kubernetes Secrets. ```bash @@ -199,7 +200,7 @@ kubectl create secret generic pubkey --from-file=publicKey=public.pem -n cdbname kubectl create secret generic prvkey --from-file=privateKey="private.key" -n pdbnamespace ``` -### Create secrets with encrypted password +### 2.8. Create secrets with encrypted password In this example, we create the Secrets for each credential (username and password) @@ -239,7 +240,7 @@ rm dbuser.txt dbpass.txt wbuser.txt wbpass.txt pdbusr.txt pdbpwd.txt \ e_dbuser.txt e_dbpass.txt e_wbuser.txt e_wbpass.txt e_pdbusr.txt e_pdbpwd.txt ``` -### Create lrest pod +### 2.9. Create lrest pod To create the REST pod and monitor its processing, use the `yaml` file [`create_lrest_pod.yaml`](./usecase/create_lrest_pod.yaml) @@ -343,7 +344,7 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 |cdbName | Name of the container database (db) | |lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** use the latest label availble on OCR | |dbTnsurl | The string of the tns alias to connect to cdb. Attention: remove all white space from string | -|deletePdbCascade | Delete all of the PDBs associated to a CDB resource when the CDB resource is dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | +|deletePdbCascade | Delete all of the PDBs associated to a CDB resource when the CDB resource is dropped | |autodiscover | boolean parameter: enable the capability of automatic CRD/LRPDB creation if a PDB is manually created via CLI | |cdbAdminUser | Secret: the administrative (admin) user | |cdbAdminPwd | Secret: the admin user password | @@ -356,7 +357,7 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 |clusterip | Assigne a cluster ip | |trace_level_client | Turn on the sqlnet **trace_level_client** | -### Openshift configuration +### 2.10. Openshift configuration For the open shift installation you need to do the following - Before lrest pod creation; create a security context by appling the yaml file [security_context.yaml](./usecase/security_context.yaml): mind to specify the correnct **namespace** and the **service name account**. @@ -380,7 +381,7 @@ metadata: ``` -### Create PDB +### 2.11. Create PDB To create a pluggable database, apply the yaml file [`create_pdb1_resource.yaml`](./usecase/create_pdb1_resource.yaml) @@ -415,7 +416,7 @@ SQL> |cdbNamespace | Namespace of the REST server | |cdbName | Name of the container database | |pdbName | Name of the PDB that you want to create | -|assertiveLrpdbDeletion | Boolean: Turn on the imperative approach on PDB resource deletion | +|assertiveLrpdbDeletion | Boolean: true - CRD and PDB are deleted both ; false - olny CRD is deleted | |adminpdbUser | Secret: PDB admin user | |adminpdbPass | Secret: password of PDB admin user | |lrpdbTlsKey | Secret: `tls.key ` | @@ -434,7 +435,7 @@ SQL> The following parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** must be specified in all PDB lifecycle management `yaml` files. For the sake of presentation they will be omitted in the subsequent tables. -#### pdb config map +#### 2.11.1. pdb config map **pdbconfigmap** parameters specifies a kubernetes `configmap` with init PDB parameters. The config map payload has the following format: @@ -492,7 +493,7 @@ test_invalid_parameter;16;spfile -### Open PDB +### 2.12. Open PDB To open the PDB, use the file [`open_pdb1_resource.yaml`](./usecase/open_pdb1_resource.yaml): @@ -519,7 +520,7 @@ kubectl patch lrpdb [lrpdb_resource_name] -n [ppdb_namespace] -p \ '{"spec":{"pdbState":"OPEN","modifyOption":"READ WRITE","modifyOption2":"NONE"}}' --type=merge ``` -### Close PDB +### 2.13. Close PDB To close the PDB, use the file [`close_pdb1_resource.yaml`](./usecase/close_pdb1_resource.yaml): @@ -544,7 +545,7 @@ kubectl patch lrpdb [lrpdb_resource_name] -n [ppdb_namespace] -p \ '{"spec":{"pdbState":"CLOSE","modifyOption":"IMMEDIATE"}}' --type=merge ``` -### Clone PDB ### +### 2.14. Clone PDB ### To clone the PDB, use the file [`clone_pdb1_resource.yaml`](./usecase/clone_pdb1_resource.yaml): @@ -565,7 +566,7 @@ kubeclt apply -f clone_pdb1_resource.yaml |pdbconfigmap | kubernetes **configmap** which contains the PDB init parameters | -### Unplug PDB +### 2.15. Unplug PDB To unplug the PDB, use the file [`unplug_pdb1_resource.yaml`](./usecase/unplug_pdb1_resource.yaml): @@ -579,7 +580,7 @@ To unplug the PDB, use the file [`unplug_pdb1_resource.yaml`](./usecase/unplug_p |xmlFileName | Unplug xmlfile path | |pdbState | `UNPLUG` -### Plug PDB +### 2.16. Plug PDB To plug in the PDB, use the file [`plug_pdb1_resource.yaml`](./usecase/plug_pdb1_resource.yaml). In this example, we plug in the PDB that was unpluged in the previous step: @@ -596,7 +597,7 @@ To plug in the PDB, use the file [`plug_pdb1_resource.yaml`](./usecase/plug_pdb1 |pdbconfigmap | Kubernetes `configmap` that contains the PDB init parameters | |pdbState | `PLUG` -### Delete PDB +### 2.17. Delete PDB To delete the PDB, use the file [`delete_pdb1_resource.yaml`](./usecase/delete_pdb1_resource.yaml) @@ -615,7 +616,7 @@ To delete the PDB, use the file [`delete_pdb1_resource.yaml`](./usecase/delete_p ```bash kubectl delete lrpdb -n ``` -## SQL/PLSQL SCRIPT EXECUTION +## 3. SQL/PLSQL SCRIPT EXECUTION Plsql and sql script can be stored in a kubernetes configmap, each block can be tagged with a label as describe in the example. @@ -642,7 +643,7 @@ data: The sql and plsqlcode must be indented using tab (makefile stile). The code blocks will be executed following the alphabetic tag order. -### Apply plsql configmap +### 3.1. Apply plsql configmap ```bash kubectl patch lrpdb pdb1 -n pdbnamespace -p '{"spec":{"codeconfigmap":""}}' --type=merge @@ -670,7 +671,7 @@ LAST SEEN TYPE REASON OBJECT MESSAGE ``` The message format for the **APPLYSQL** is `CODE:SQLCODE '[]':''` -### Limitation +### 3.2. Limitation - All the objects in the plsql confgmap must be rapresented in the form `.`. Due to this constraint it's not possible to rename table @@ -687,9 +688,9 @@ The message format for the **APPLYSQL** is `CODE:SQLCODE '[]':'TROUBLESHOOTING -### Get rid of error status +### 4.1. Get rid of error status If for some reason you get an operation failure it's possible to manually fix the problem and then rest the bitmask status to re-execute the operation. Consider the following example: the unplug command does not complete successfully because xmlfile already exists ; **ORA-65170** and **PDBUPE** flag is on. You can remove the file and then rest the bitmask status in order to retry the operation. @@ -718,11 +719,11 @@ pdb1 DB12 pdbdev MOUNTED 0.80G close:[ORA-65170] NONE ^^^^^^^^^^^^^^^^^^^^^^^^^^ [READY TO BE UNPLUGGED] ``` -## UPGRADE EXISTING INSTALLATION +## 5. UPGRADE EXISTING INSTALLATION In order to migrante an existing installation to the new version of controller you can laverage on the autodiscover installation. Patch all your lrpdb resources by setting **assertiveLrpdbDeletion** false. After that you can delete lrest resource and all lrpdbs. Upgrade the operator and create lrest server paying attenction to set **autodiscover** true and the namespace used by the autodiscovery mechanism (**namespaceAutoDiscover**). -## KNOWN ISSUES +## 6. KNOWN ISSUES - Error message `ORA-01005` is not reported in the lrest database login phase if password is mistakenly null. Trace log shows the message ORA-1012. diff --git a/docs/multitenant/usecase/makefile b/docs/multitenant/usecase/makefile index 7862fd09..d564b292 100644 --- a/docs/multitenant/usecase/makefile +++ b/docs/multitenant/usecase/makefile @@ -1080,6 +1080,9 @@ dumplrest: @>$(DIAGFILE) $(KUBECTL) logs `$(KUBECTL) get pods -o custom-columns=:metadata.name -n $(LRSNAMESPACE) --no-headers ` -n $(LRSNAMESPACE) |strings > $(DIAGFILE) +loglrest: + $(KUBECTL) logs -f `$(KUBECTL) get pods -o custom-columns=:metadata.name -n $(LRSNAMESPACE) --no-headers ` -n $(LRSNAMESPACE) + listimage: $(KUBECTL) get pods --all-namespaces -o jsonpath="{.items[*].spec['initContainers', 'containers'][*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c @@ -1159,6 +1162,11 @@ close: @$(call msg, "lrpdb $(LRPDBNAME) completed") $(KUBECTL) get lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) +altercpu: + @$(call msg, "lrpdb $(LRPDBNAME) alter cpu") + $(KUBECTL) patch lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) -p \ + '{"spec":{"alterSystemParameter":"cpu_count","alterSystemValue":"3","parameterScope":"memory"}}' --type=merge + openpdb1: $(MAKE) -f $(MAKEFILE) open LRPDBNAME=pdb1 @@ -1292,11 +1300,11 @@ tkplsqlexec01: $(KUBECTL) apply -f $(CONFIG_MAP_SQL) $(KUBECTL) describe configmap $(PLSQLMAP) -n $(PDBNAMESPACE) @$(call msg,"applying config sql map") - $(KUBECTL) patch lrpdb pdb1 -n $(PDBNAMESPACE) -p \ + $(KUBECTL) patch lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) -p \ '{"spec":{"codeconfigmap":"$(PLSQLMAP)"}}' --type=merge sleep 10 $(KUBECTL) get events --sort-by='.lastTimestamp' -n $(PDBNAMESPACE) - $(KUBECTL) get events -n $(PDBNAMESPACE) --sort-by='.lastTimestamp' --field-selector involvedObject.name=pdb1|grep APPLYSQL + $(KUBECTL) get events -n $(PDBNAMESPACE) --sort-by='.lastTimestamp' --field-selector involvedObject.name=$(LRPDBNAME)|grep APPLYSQL @@ -1312,9 +1320,50 @@ tkaudosicov: lrpdb atd-pdbdev -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) $(MAKE) -f $(MAKEFILE) genyaml ASSERTDELETION=true +# Proceudure to recreate restserver +tklrestew: + @$(call msg,"Recrete resetserver") + @$(call msg,"disable autodiscover and delete cascade") + $(KUBECTL) patch lrest cdb-dev -n $(LRSNAMESPACE) -p '{"spec":{"autodiscover":false,"deletePdbCascade":false}}' --type=merge + $(KUBECTL) wait --for jsonpath='{.spec.autodiscover'}=false lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + $(KUBECTL) wait --for jsonpath='{.spec.deletePdbCascade'}=false lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + sleep 10 + $(MAKE) -f $(MAKEFILE) run00 + +# Turn ON/OFF autodiscovery +# ON: make tkautd AUTOD=true +# OFF: make tkautd AUTOD=false +tkautd: + @$(call msg,"autodiscover $(AUTOD)") + $(KUBECTL) patch lrest cdb-dev -n $(LRSNAMESPACE) -p '{"spec":{"autodiscover":$(AUTOD)}}' --type=merge + $(KUBECTL) wait --for jsonpath='{.spec.autodiscover'}=$(AUTOD) lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + +# Turn ON/OFF lrest pdb delete cascade +# ON: make tkdelcs DELETECS=true +# OFF: make tkdelcs DELETECS=false +tkdelcs: + @$(call msg,"delete pdb cascade $(DELETECS)") + $(KUBECTL) patch lrest cdb-dev -n $(LRSNAMESPACE) -p '{"spec":{"deletePdbCascade":$(DELETECS)}}' --type=merge + $(KUBECTL) wait --for jsonpath='{.spec.deletePdbCascade'}=$(DELETECS) lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + +# Turn ON/OFF lrest pdb imperativeLrpdb deletion (delete CRD & PDB) +# ON: make tkdelcrd DELETECRD=true LRPDBNAME= +# OFF: make tkdelcrd DELETECRD=false LRPDNNAME= +tkdelcrd: + @$(call msg,"imperativeLrpdbDeletion $(DELETECRD)") + $(KUBECTL) patch lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) \ + -p '{"spec":{"imperativeLrpdbDeletion":$(DELETECRD)}}' --type=merge + $(KUBECTL) wait --for jsonpath='{.spec.imperativeLrpdbDeletion'}=$(DELETECRD) lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) + pdbsize: $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.pdbName}{"\t"}{.spec.totalSize}{"\t"}{.status.totalSize}{"\n"}{end}' +reset: + $(KUBECTL) patch lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) \ + -p '{"spec":{"pdbState":"RESET","reststate":$(RESETVALUE)}}' --type=merge + $(KUBECTL) get lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) + + # tkapplyinit - config map creation From 5dba2bde6fe42f28155245b1f03cf111ea57c7db Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Sun, 31 Aug 2025 22:49:44 +0000 Subject: [PATCH 298/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 2f2355a9..caa38706 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -274,8 +274,8 @@ type OracleRestartNodePortSvc struct { PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` SvcName string `json:"name,omitempty"` SvcType string `json:"svcType,omitempty"` - SvcLBIP string `json:"paiLBIP,omitempty"` - SvcAnnotation map[string]string `json:"pailbAnnotation,omitempty"` + SvcLBIP string `json:"svcLBIP,omitempty"` + SvcAnnotation map[string]string `json:"svcAnnotation,omitempty"` } type OracleRestartPortMapping struct { From 5cf380aa917076997186a45884ab64fbfb901449 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 1 Sep 2025 17:07:22 +0000 Subject: [PATCH 299/414] Added fixes --- commons/oraclerestart/oraclerestartprov.go | 4 +- .../database/oraclerestart_controller.go | 6 ++- oracle-database-operator.yaml | 50 ++++++++++++++++++- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 4fed2e31..f941d1c2 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -706,10 +706,10 @@ func BuildExternalServiceDefForOracleRestart(instance *oraclerestart.OracleResta npSvc.PortMappings = OracleRestartSpex.PortMappings } service.Spec.Type = corev1.ServiceTypeNodePort - } else if opType == "lbsvc" { + } else if opType == "lbservice" { npSvc = instance.Spec.LbService npSvc.PortMappings = instance.Spec.LbService.PortMappings - service.Spec.Type = corev1.ServiceTypeClusterIP + service.Spec.Type = corev1.ServiceTypeLoadBalancer } else if opType == "onssvc" { var exSvcPorts oraclerestart.OracleRestartPortMapping var exSvc oraclerestart.OracleRestartNodePortSvc diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 524add2a..7a29b901 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -242,7 +242,9 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques if oracleRestart.Spec.ExternalSvcType != nil { svcType = *oracleRestart.Spec.ExternalSvcType } else { - svcType = "nodeport" + if len(oracleRestart.Spec.LbService.PortMappings) == 0 { + svcType = "nodeport" + } } result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildServiceDefForOracleRestart(oracleRestart, 0, oracleRestart.Spec.InstDetails, "local")) if err != nil { @@ -270,7 +272,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } - if len(oracleRestart.Spec.LbService.SvcName) != 0 && len(oracleRestart.Spec.LbService.PortMappings) != 0 { + if len(oracleRestart.Spec.LbService.PortMappings) != 0 { result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildExternalServiceDefForOracleRestart(oracleRestart, 0, oracleRestart.Spec.InstDetails, svcType, "lbservice")) if err != nil { result = resultNq diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 123ab291..b9980bf0 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10650,6 +10650,12 @@ spec: type: integer type: object type: array + svcAnnotation: + additionalProperties: + type: string + type: object + svcLBIP: + type: string svcType: type: string type: object @@ -10730,6 +10736,35 @@ spec: type: boolean isManual: type: boolean + lbService: + properties: + name: + type: string + portMappings: + items: + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + type: string + targetPort: + format: int32 + type: integer + type: object + type: array + svcAnnotation: + additionalProperties: + type: string + type: object + svcLBIP: + type: string + svcType: + type: string + type: object nfsStorageDetails: properties: path: @@ -11082,6 +11117,12 @@ spec: type: integer type: object type: array + svcAnnotation: + additionalProperties: + type: string + type: object + svcLBIP: + type: string svcType: type: string type: object @@ -11407,6 +11448,12 @@ spec: type: integer type: object type: array + svcAnnotation: + additionalProperties: + type: string + type: object + svcLBIP: + type: string svcType: type: string type: object @@ -15078,6 +15125,7 @@ rules: - database.oracle.com resources: - dbcssystems/finalizers + - lrpdbs/configmaps - lrpdbs/finalizers - oraclerestarts/finalizers - shardingdatabases/finalizers @@ -15914,7 +15962,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:basedb-operator-sa + image: dbcs-dev-docker.dockerhub-phx.oci.oraclecorp.com/oracle/database-operator:5dba2bde imagePullPolicy: Always name: manager ports: From b8692d005a67c5fcdc889880123619d849f976f5 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 1 Sep 2025 18:41:08 +0000 Subject: [PATCH 300/414] Added fix --- controllers/database/oraclerestart_controller.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 7a29b901..a512bcef 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -2125,19 +2125,19 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or data = append(data, "DB_RECOVERY_FILE_DEST_SIZE="+instance.Spec.ConfigParams.DbRecoveryFileDestSize) } if instance.Spec.ConfigParams.DBAsmDiskDgRedundancy != "" { - data = append(data, "DB_ASMDG_PROPERTIES="+"redudancy:"+instance.Spec.ConfigParams.DBAsmDiskDgRedundancy) + data = append(data, "DB_ASMDG_PROPERTIES="+"redundancy:"+instance.Spec.ConfigParams.DBAsmDiskDgRedundancy) } if instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy != "" { - data = append(data, "REDO_ASMDG_PROPETRIES="+"redudancy:"+instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy) + data = append(data, "REDO_ASMDG_PROPETRIES="+"redundancy:"+instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy) } if instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy != "" { - data = append(data, "RECO_ASMDG_PROPETRIES="+"redudancy:"+instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy) + data = append(data, "RECO_ASMDG_PROPETRIES="+"redundancy:"+instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy) } if instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy != "" { - data = append(data, "CRS_ASMDG_REDUNDANCY="+"redudancy:"+instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy) + data = append(data, "CRS_ASMDG_REDUNDANCY="+"redundancy:"+instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy) } } From 2911a39c7f7b33a6704e58ec96f18ac1faa887b8 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 2 Sep 2025 01:48:43 +0000 Subject: [PATCH 301/414] Added fixes --- controllers/database/oraclerestart_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index a512bcef..067be640 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -2137,7 +2137,7 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or } if instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy != "" { - data = append(data, "CRS_ASMDG_REDUNDANCY="+"redundancy:"+instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy) + data = append(data, "CRS_ASMDG_REDUNDANCY="+"redundancy="+instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy) } } From 72ab8c24e0008db480ea5ec639076cfcb1f21e59 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 2 Sep 2025 05:32:51 +0000 Subject: [PATCH 302/414] Added fixes --- controllers/database/oraclerestart_controller.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 067be640..4c268ebe 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -2129,11 +2129,11 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or } if instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy != "" { - data = append(data, "REDO_ASMDG_PROPETRIES="+"redundancy:"+instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy) + data = append(data, "REDO_ASMDG_PROPERTIES="+"redundancy:"+instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy) } if instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy != "" { - data = append(data, "RECO_ASMDG_PROPETRIES="+"redundancy:"+instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy) + data = append(data, "RECO_ASMDG_PROPERTIES="+"redundancy:"+instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy) } if instance.Spec.ConfigParams.CrsAsmDiskDgRedundancy != "" { From 40db295ab2bec2f039288f3c79532d53a14e976d Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Wed, 3 Sep 2025 21:20:13 +0000 Subject: [PATCH 303/414] documentation improvment --- docs/multitenant/README.md | 72 ++++++++++++++++++- docs/multitenant/usecase/README.md | 11 +++ .../usecase/delete_pdb1_resource.yaml | 1 + .../usecase/delete_pdb2_resource.yaml | 1 + .../usecase/delete_pdb3_resource.yaml | 1 + docs/multitenant/usecase/makefile | 35 +++++++-- .../usecase/plug_pdb1_resource.yaml | 2 +- .../usecase/unplug_pdb1_resource.yaml | 2 +- 8 files changed, 116 insertions(+), 9 deletions(-) diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index ce2691ea..fb2a5abb 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -345,7 +345,8 @@ Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 |lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** use the latest label availble on OCR | |dbTnsurl | The string of the tns alias to connect to cdb. Attention: remove all white space from string | |deletePdbCascade | Delete all of the PDBs associated to a CDB resource when the CDB resource is dropped | -|autodiscover | boolean parameter: enable the capability of automatic CRD/LRPDB creation if a PDB is manually created via CLI | +|autodiscover | boolean parameter: enable the capability of automatic CRD/LRPDB creation if a PDB is manually created via CLI | +|namespaceAutoDiscover | Namespace name used by autodiscery | |cdbAdminUser | Secret: the administrative (admin) user | |cdbAdminPwd | Secret: the admin user password | |webServerUser | Secret: the HTTPS user | @@ -610,12 +611,30 @@ To delete the PDB, use the file [`delete_pdb1_resource.yaml`](./usecase/delete_p |cdbName | Name of the container database (CDB) | |pdbState | `DELETE` | |dropAction | **INCLUDING** - Including datafiles or **NONE** | +|imperativeLrpdbDeletion | boolean: if true pdb and k8s resource will be deleted if false only resource is deleted| + + +In order to delete crd and pdbs using yaml file the **imperativeLrpdbDeletion: true** must be specified in the yaml. **If the parameter is not specified PDB will not be deleted; regardless the setting during creation**. Imperative command (kubectl delete lrpdb ) acts according to imperativeLrdbDeletion setting. You can check the imperativeLrpdbDeletion setting using **Imperative command** ```bash kubectl delete lrpdb -n ``` + +**Check imperativelrpdbdeletion setting** +```bash +/usr/bin/kubectl get lrpdb -n pdbnamespace \ + -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.spec.pdbName}{" "}{.status.openMode}{" "}{.spec.imperativeLrpdbDeletion}{" "}{"\t\t"}{"\n"}{end}'| sed 's/READ WRITE/READ_WRITE/g' |awk ' BEGIN { printf( "%-20s %-10s %-10s %10s\n","CRD","PDB NAME","OPEN MODE","IMPERATIVELRPDBDELETION"); \ + printf( "%-20s %-10s %-10s %-23s\n","--------------------","----------","----------","-----------------------");\ + } { printf("%-20s %-10s %-10s %-23s\n",$1,$2,$3,$4) }' + +CRD PDB NAME OPEN MODE IMPERATIVELRPDBDELETION +-------------------- ---------- ---------- ----------------------- +pdb1 pdbdev READ_WRITE true +pdb2 pdbprd MOUNTED true +``` + ## 3. SQL/PLSQL SCRIPT EXECUTION @@ -721,10 +740,59 @@ pdb1 DB12 pdbdev MOUNTED 0.80G close:[ORA-65170] NONE ``` ## 5. UPGRADE EXISTING INSTALLATION -In order to migrante an existing installation to the new version of controller you can laverage on the autodiscover installation. Patch all your lrpdb resources by setting **assertiveLrpdbDeletion** false. After that you can delete lrest resource and all lrpdbs. Upgrade the operator and create lrest server paying attenction to set **autodiscover** true and the namespace used by the autodiscovery mechanism (**namespaceAutoDiscover**). +In order to migrante an existing installation to the new version of controller you can laverage on the autodiscover installation. Patch all your lrpdb resources by setting **assertiveLrpdbDeletion** false. After that you can delete lrest resource and all lrpdbs. Upgrade the operator and create lrest server with **autodiscover** and **namespaceAutoDiscover** configured. + +- For each CRD/LRPDB turn off **assertiveLrpdbDeletion** + +```bash +kubectl patch lrpdb -n -p '{"spec":{"imperativeLrpdbDeletion":false}}' --type=merge + +kubectl wait --for jsonpath='{.spec.imperativeLrpdbDeletion'}=false lrpdb -n --timeout=3m + +``` + +- Delete **LRPDB** resource + +```bash +kubectl delete lrpdb -n +``` + +- Delete **LREST** resource +```bash +kubectl delete rest -n +``` +- Upgrade the operator + +```bash +kubectl replace -f oracle-database-database.yaml +``` + +- (**A**) deploy **LREST** controller + +``` +kubectl apply -f create_lrest_pod.yaml +``` +- Turn on the **autodiscover** + +```bash +kubectl patch lrest -n -p '{"spec":{"autodiscover":true}}' --type=merge +kubectl patch lrest -n -p '{"spec":{"namespaceAutoDiscover":"}}' --type=merge +``` +Check the lrpdb resource existence + +- Turn off the **autodiscover** + +```bash +kubectl patch lrest cdb-dev -n -p '{"spec":{"autodiscover":false}}' --type=merge +``` + +## DEPLOY MULTITENANT CONTROLLERS ON CDB WITH EXISTINGS PDBS + +- To deploy multitenant controllers on a container database with existing pdbs start the previous procedure at point (**A**) ## 6. KNOWN ISSUES - Error message `ORA-01005` is not reported in the lrest database login phase if password is mistakenly null. Trace log shows the message ORA-1012. +- diff --git a/docs/multitenant/usecase/README.md b/docs/multitenant/usecase/README.md index 358ea1ed..062200ee 100644 --- a/docs/multitenant/usecase/README.md +++ b/docs/multitenant/usecase/README.md @@ -18,6 +18,7 @@ /vscode-markdown-toc-config --> + @@ -132,6 +133,9 @@ You can run **make runall00** to test all the functionality the multitenant cont |tkaudosicov | test autodiscovery | | |tkplsqlexec | test sql/plsql | | |tkapplyinit | apply init map | | + |altercpu | alter cpu_count | make altercpu LRPDBNAME=_lrpdb resource_ CPU_COUNT=_cpu count value_ | + |open | open pdb [imperative]| make open LRPDBNAME=_lrpdb resource_ | + |close | close pdb [imperative]| make close LRPDBNAME=_lrpdb resource_ | |listimage | images available on the cluster| | |dumpoperator | dump operator log | | |dumplrest | dump lrest log | | @@ -142,6 +146,13 @@ You can run **make runall00** to test all the functionality the multitenant cont |**secrets** | create secrets | | |**genyaml** | generate the yaml files| | |opclean | deintall the operator| | + |cleanlrest | drop lrest resource | **lrest name hard coded** | + |rest | rest status bitmask | make rest LRPDBNAME=_resname_ RESETVALUE=_new bitmask value_ | + |tkautd | Turn on/off autodiscover | make tkautd AUTOD=true/false | + |tkdelcs | Turn on/off pdb delete cascade | make tkdelcs DELETECS=true/false | + |tkdelcrd |Turn ON/OFF lrest pdb imperativeLrpdb deletion | make tkdelcrd DELETECRD=true/false LRPDBNAME=_lrpdb resource name_ | + | checkimpdel | report of imperative delete setting || + ## 7. Diag commands and troubleshooting diff --git a/docs/multitenant/usecase/delete_pdb1_resource.yaml b/docs/multitenant/usecase/delete_pdb1_resource.yaml index 2831dd29..751de900 100644 --- a/docs/multitenant/usecase/delete_pdb1_resource.yaml +++ b/docs/multitenant/usecase/delete_pdb1_resource.yaml @@ -11,6 +11,7 @@ spec: pdbName: "pdbdev" pdbState: "DELETE" dropAction: "INCLUDING" + imperativeLrpdbDeletion: true adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/usecase/delete_pdb2_resource.yaml b/docs/multitenant/usecase/delete_pdb2_resource.yaml index 8924645e..01118ea0 100644 --- a/docs/multitenant/usecase/delete_pdb2_resource.yaml +++ b/docs/multitenant/usecase/delete_pdb2_resource.yaml @@ -11,6 +11,7 @@ spec: pdbName: "pdbprd" pdbState: "DELETE" dropAction: "INCLUDING" + imperativeLrpdbDeletion: true adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/usecase/delete_pdb3_resource.yaml b/docs/multitenant/usecase/delete_pdb3_resource.yaml index 1261245b..9c57c5c1 100644 --- a/docs/multitenant/usecase/delete_pdb3_resource.yaml +++ b/docs/multitenant/usecase/delete_pdb3_resource.yaml @@ -11,6 +11,7 @@ spec: pdbName: "new_clone" pdbState: "DELETE" dropAction: "INCLUDING" + imperativeLrpdbDeletion: true adminpdbUser: secret: secretName: "pdbusr" diff --git a/docs/multitenant/usecase/makefile b/docs/multitenant/usecase/makefile index d564b292..655fb45c 100644 --- a/docs/multitenant/usecase/makefile +++ b/docs/multitenant/usecase/makefile @@ -712,6 +712,7 @@ spec: pdbName: "pdbdev" pdbState: "DELETE" dropAction: "INCLUDING" + imperativeLrpdbDeletion: ${ASSERTDELETION} EOF cat < ${LRPDBDELETE2} @@ -728,6 +729,7 @@ spec: pdbName: "pdbprd" pdbState: "DELETE" dropAction: "INCLUDING" + imperativeLrpdbDeletion: ${ASSERTDELETION} EOF cat < ${LRPDBDELETE3} @@ -744,6 +746,7 @@ spec: pdbName: "new_clone" pdbState: "DELETE" dropAction: "INCLUDING" + imperativeLrpdbDeletion: ${ASSERTDELETION} EOF cat < ${LRPDBUNPLUG1} @@ -1355,12 +1358,20 @@ tkdelcrd: -p '{"spec":{"imperativeLrpdbDeletion":$(DELETECRD)}}' --type=merge $(KUBECTL) wait --for jsonpath='{.spec.imperativeLrpdbDeletion'}=$(DELETECRD) lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) -pdbsize: +checkpdbsize: $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.pdbName}{"\t"}{.spec.totalSize}{"\t"}{.status.totalSize}{"\n"}{end}' +checkimpdel: + $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) \ + -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.spec.pdbName}{" "}{.status.openMode}{" "}{.spec.imperativeLrpdbDeletion}{" "}{"\t\t"}{"\n"}{end}'| sed 's/READ WRITE/READ_WRITE/g' |awk ' BEGIN { printf( "%-20s %-10s %-10s %10s\n","CRD","PDB NAME","OPEN MODE","IMPERATIVELRPDBDELETION"); \ + printf( "%-20s %-10s %-10s %-23s\n","--------------------","----------","----------","-----------------------");\ + } { printf("%-20s %-10s %-10s %-23s\n",$$1,$$2,$$3,$$4) }' + reset: + @$(call msg,"Reset state $(LRPDBNAME):$(RESETVALUE)") $(KUBECTL) patch lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) \ - -p '{"spec":{"pdbState":"RESET","reststate":$(RESETVALUE)}}' --type=merge + -p '{"spec":{"pdbState":"RESET","reststate":$(RESETVALUE)}}' \ + --type=merge $(KUBECTL) get lrpdb $(LRPDBNAME) -n $(PDBNAMESPACE) @@ -1385,15 +1396,29 @@ reset: # openpdb2rs - pdb2 open restrict - imperative # -runall01: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run03.1 run03.2 run04.1 run04.2 run05.1 run06.1 run07.1 +runall01: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 \ + run03.1 run03.2 run04.1 run04.2 run05.1 run06.1 run07.1 runall02: genyaml tkapplyinit run00 run01.1 openpdb1 -runall03: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 openpdb1rs +runall03: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 \ + run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 openpdb1rs $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb1 $(MAKE) -f $(MAKEFILE) open LRPDBNAME=pdb1 $(MAKE) -f $(MAKEFILE) close LRPDBNAME=pdb1 $(MAKE) -f $(MAKEFILE) run05.1 run06.1 -runall04: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 run04.2 openpdb1 openpdb2 closepdb1 closepdb2 run05.1 run06.1 closepdb1 +runall04: genyaml tkapplyinit run00 run01.1 run01.2 run02.1 run02.2 run04.1 \ + run04.2 openpdb1 \ + openpdb2 closepdb1 closepdb2 run05.1 run06.1 closepdb1 runall05: genyaml tkapplyinit run00 run01.1 run02.1 run03.1 +## Delete cloned pdb +## setup:create/open/clone +runall06: run01.1 run02.1 run03.1 + $(KUBECTL) apply -f $(LRPDBDELETE3) + $(KUBECTL) apply -f $(LRPDBDELETE1) + + + + + diff --git a/docs/multitenant/usecase/plug_pdb1_resource.yaml b/docs/multitenant/usecase/plug_pdb1_resource.yaml index 360e17b8..7ce0d60e 100644 --- a/docs/multitenant/usecase/plug_pdb1_resource.yaml +++ b/docs/multitenant/usecase/plug_pdb1_resource.yaml @@ -11,7 +11,7 @@ spec: cdbName: "DB12" pdbName: "pdbdev" pdbState: "PLUG" - xmlFileName: "/var/tmp/pdb.339548.xml" + xmlFileName: "/var/tmp/pdb.4095290.xml" fileNameConversions: "NONE" sourceFileNameConversions: "NONE" copyAction: "MOVE" diff --git a/docs/multitenant/usecase/unplug_pdb1_resource.yaml b/docs/multitenant/usecase/unplug_pdb1_resource.yaml index 375e1fec..f48fe74b 100644 --- a/docs/multitenant/usecase/unplug_pdb1_resource.yaml +++ b/docs/multitenant/usecase/unplug_pdb1_resource.yaml @@ -11,7 +11,7 @@ spec: cdbName: "DB12" pdbName: "pdbdev" pdbState: "UNPLUG" - xmlFileName: "/var/tmp/pdb.339548.xml" + xmlFileName: "/var/tmp/pdb.4095290.xml" adminpdbUser: secret: secretName: "pdbusr" From b00b70bac29f947a5e3f1fff3b7b297da446be61 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 4 Sep 2025 04:42:56 +0000 Subject: [PATCH 304/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 106 ++++++++---------- apis/database/v4/oraclerestart_webhook.go | 10 +- commons/oraclerestart/oraclerestartcommon.go | 54 ++++----- commons/oraclerestart/oraclerestartprov.go | 47 ++++---- commons/oraclerestart/utils/utils.go | 2 +- .../database/oraclerestart_controller.go | 104 ++++------------- 6 files changed, 123 insertions(+), 200 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index caa38706..523dcc39 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -52,34 +52,34 @@ import ( type OracleRestartSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - - InstDetails OracleRestartInstDetailSpec `json:"instDetails"` - ConfigParams *InitParams `json:"configParams,omitempty"` - AsmStorageDetails *AsmDiskDetails `json:"asmStorageDetails,omitempty"` - NfsStorageDetails *corev1.NFSVolumeSource `json:"nfsStorageDetails,omitempty"` - UseNfsforSwStorage string `json:"useNfsforSwStorage,omitempty"` - StorageSizeInGB int `json:"storageSizeInGB,omitempty"` - Image string `json:"image,omitempty"` - ImagePullSecret string `json:"imagePullSecret,omitempty"` - ScriptsLocation string `json:"scriptsLocation,omitempty"` - IsDeleteOraPvc string `json:"isDeleteOraPvc,omitempty"` - SshKeySecret *OracleRestartSshSecretDetails `json:"sshKeySecret,omitempty"` - ImagePullPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` - ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` - ScriptsGetCmd string `json:"scriptsGetCmd,omitempty"` - IsDebug string `json:"isDebug,omitempty"` - SecurityContext *corev1.PodSecurityContext `json:"securityContext"` - IsDeleteTopolgy string `json:"isDeleteTopology,omitempty"` - ExternalSvcType *string `json:"externalSvcType,omitempty"` - DbSecret *OracleRestartDbPwdSecretDetails `json:"dbSecret,omitempty"` - TdeWalletSecret *OracleRestartDbPwdSecretDetails `json:"tdeWalletSecret,omitempty"` - ServiceDetails ServiceSpec `json:"serviceDetails,omitempty"` - Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` - IsFailed bool `json:"isFailed,omitempty"` - IsManual bool `json:"isManual,omitempty"` - SrvAccountName string `json:"serviceAccountName,omitempty"` - StorageClass string `json:"storageClass,omitempty"` - LbService OracleRestartNodePortSvc `json:"lbService,omitempty"` + InstDetails OracleRestartInstDetailSpec `json:"instDetails"` + ConfigParams *InitParams `json:"configParams,omitempty"` + AsmStorageDetails *AsmDiskDetails `json:"asmStorageDetails,omitempty"` + Image string `json:"image,omitempty"` + ImagePullSecret string `json:"imagePullSecret,omitempty"` + ScriptsLocation string `json:"scriptsLocation,omitempty"` + SshKeySecret *OracleRestartSshSecretDetails `json:"sshKeySecret,omitempty"` + // +kubebuilder:validation:Enum=Always;IfNotPresent;Never + // +kubebuilder:validation:default="Always" + ImagePullPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` + ScriptsGetCmd string `json:"scriptsGetCmd,omitempty"` + IsDebug string `json:"isDebug,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext"` + IsDeleteTopolgy string `json:"isDeleteTopology,omitempty"` + DbSecret *OracleRestartDbPwdSecretDetails `json:"dbSecret,omitempty"` + TdeWalletSecret *OracleRestartDbPwdSecretDetails `json:"tdeWalletSecret,omitempty"` + ServiceDetails ServiceSpec `json:"serviceDetails,omitempty"` + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` + IsFailed bool `json:"isFailed,omitempty"` + IsManual bool `json:"isManual,omitempty"` + SrvAccountName string `json:"serviceAccountName,omitempty"` + StorageClass string `json:"storageClass,omitempty"` + LbService OracleRestartNodePortSvc `json:"lbService,omitempty"` + NodePortSvc OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if + // +kubebuilder:validation:Enum=true;false + // +kubebuilder:default=true + EnableOns string `json:"enableOns,omitempty"` } type AsmDiskDetails struct { @@ -143,22 +143,18 @@ type InitParams struct { } type OracleRestartInstDetailSpec struct { - Name string `json:"name,omitempty"` // Name of the Oracle Restart Instance - HostSwLocation string `json:"hostSwLocation,omitempty"` - SwLocStorageSizeInGb int `json:"swLocStorageSizeInGb,omitempty"` - WorkerNode []string `json:"workerNode,omitempty"` - EnvVars []corev1.EnvVar `json:"envVars,omitempty"` //Optional Env variables for Shards - Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` //Optional resource requiremen - Label string `json:"label,omitempty"` - IsDelete string `json:"isDelete,omitempty"` - IsForceDelete string `json:"isForceDelete,omitempty"` - IsKeepPVC string `json:"isKeepPVC,omitempty"` - PvcName map[string]string `json:"pvcName,omitempty"` - NodePortSvc []OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if - PortMappings []OracleRestartPortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created. The service is created if there is at least - EnvFile string `json:"envFile,omitempty"` - OnsTargetPort *int32 `json:"onsTargetPort,omitempty"` // Port that will be exposed on the service. - OnsLocalPort *int32 `json:"onsLocalPort,omitempty"` // Port that will be exposed on the service. + Name string `json:"name,omitempty"` // Name of the Oracle Restart Instance + HostSwLocation string `json:"hostSwLocation,omitempty"` + SwLocStorageSizeInGb int `json:"swLocStorageSizeInGb,omitempty"` + WorkerNode []string `json:"workerNode,omitempty"` + EnvVars []corev1.EnvVar `json:"envVars,omitempty"` //Optional Env variables for Shards + Label string `json:"label,omitempty"` + IsDelete string `json:"isDelete,omitempty"` + IsForceDelete string `json:"isForceDelete,omitempty"` + EnvFile string `json:"envFile,omitempty"` + // +kubebuilder:validation:default="delete" + IsKeepPVC string `json:"isKeepPVC,omitempty"` + PvcName map[string]string `json:"pvcName,omitempty"` } // Responsefile Name @@ -167,16 +163,6 @@ type ResponseFile struct { Name string `json:"name,omitempty"` } -// NetworkDetailsSPec defines the OracleRestart network - -type NetworkDetailSpec struct { - Name string `json:"name,omitempty"` - IPs []string `json:"ips,omitempty"` - Interface string `json:"interface,omitempty"` - Namespace string `json:"namespace,omitempty"` - Mac string `json:"mac,omitempty"` -} - // OracleRestart DB Secret Details type OracleRestartDbPwdSecretDetails struct { Name string `json:"name,omitempty"` // Name of the secret. @@ -276,13 +262,17 @@ type OracleRestartNodePortSvc struct { SvcType string `json:"svcType,omitempty"` SvcLBIP string `json:"svcLBIP,omitempty"` SvcAnnotation map[string]string `json:"svcAnnotation,omitempty"` + OnsTargetPort *int32 `json:"onsTargetPort,omitempty"` // Port that will be exposed on the service. + OnsLocalPort *int32 `json:"onsLocalPort,omitempty"` // Port that will be exposed on the service. } type OracleRestartPortMapping struct { - Port int32 `json:"port,omitempty"` - TargetPort int32 `json:"targetPort,omitempty"` - Protocol corev1.Protocol `json:"protocol,omitempty"` - NodePort int32 `json:"nodePort,omitempty"` + Port int32 `json:"port,omitempty"` + TargetPort int32 `json:"targetPort,omitempty"` + // +kubebuilder:validation:Enum=TCP;UDP;SCTP + // +kubebuilder:default=TCP + Protocol corev1.Protocol `json:"protocol,omitempty"` + NodePort int32 `json:"nodePort,omitempty"` } type OracleRestartNodestatus struct { diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index 29e3e453..cf35cd2d 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -545,12 +545,10 @@ func (r *OracleRestart) validateGeneric() field.ErrorList { "DbSwZipFile cannot be empty")) } - if !utils.CheckStatusFlag(r.Spec.UseNfsforSwStorage) { - if cfg.HostSwStageLocation == "" && r.Spec.StorageClass == "" { - validationErrs = append(validationErrs, - field.Invalid(cfgPath.Child("HostSwStageLocation"), cfg.HostSwStageLocation, - "Either HostSwStageLocation or StorageClass must be specified")) - } + if cfg.HostSwStageLocation == "" && r.Spec.StorageClass == "" { + validationErrs = append(validationErrs, + field.Invalid(cfgPath.Child("HostSwStageLocation"), cfg.HostSwStageLocation, + "Either HostSwStageLocation or StorageClass must be specified")) } if r.Spec.ConfigParams.RuPatchLocation != "" { diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 57961599..91d37510 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -85,29 +85,32 @@ func checkAbsPath(location string) bool { } // FUnction to build the svc definition for RAC -func buildContainerPortsDef(instance *oraclerestart.OracleRestart, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.ContainerPort { +func buildContainerPortsDef(instance *oraclerestart.OracleRestart) []corev1.ContainerPort { var result []corev1.ContainerPort - if len(OraRestartSpex.PortMappings) > 0 { - for _, portMapping := range OraRestartSpex.PortMappings { - name := generatePortMapping(portMapping) - if len(name) > 15 { - name = name[:15] - } - containerPort := - corev1.ContainerPort{ - Protocol: portMapping.Protocol, - ContainerPort: portMapping.Port, - Name: name, + + /* + if len(OraRestartSpexPorts) > 0 { + for _, portMapping := range OraRestartSpexPorts { + name := generatePortMapping(portMapping) + if len(name) > 15 { + name = name[:15] } - result = append(result, containerPort) - } - } else { - result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraDBPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraDBPort))}) - result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraLsnrPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraLsnrPort))}) - result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraSSHPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraSSHPort))}) - result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraLocalOnsPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraLocalOnsPort))}) - result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraOemPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraOemPort))}) - } + containerPort := + corev1.ContainerPort{ + Protocol: portMapping.Protocol, + ContainerPort: portMapping.Port, + Name: name, + } + result = append(result, containerPort) + } + } else { + */ + + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraDBPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraDBPort))}) + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraLsnrPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraLsnrPort))}) + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraSSHPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraSSHPort))}) + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraLocalOnsPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraLocalOnsPort))}) + result = append(result, corev1.ContainerPort{Protocol: corev1.ProtocolTCP, ContainerPort: utils.OraOemPort, Name: truncateName(fmt.Sprintf("%s-%d", "tcp", utils.OraOemPort))}) return result } @@ -455,11 +458,8 @@ func DelORestartPv(instance *oraclerestart.OracleRestart, index int, diskName st func CheckORestartSvc(instance *oraclerestart.OracleRestart, svcType string, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec, svcName string, kClient client.Client) (*corev1.Service, error) { svcFound := &corev1.Service{} var name string - if svcType == "nodeport" { - name = svcName - } else { - name = getOracleRestartSvcName(instance, OraRestartSpex, svcType) - } + + name = getOracleRestartSvcName(instance, OraRestartSpex, svcType) err := kClient.Get(context.TODO(), types.NamespacedName{ Name: name, @@ -825,7 +825,7 @@ func getExternalConnStr( logger logr.Logger, ) string { // If NodePort service not defined in spec, don’t expose external conn - if len(instance.Spec.InstDetails.NodePortSvc) == 0 { + if len(instance.Spec.NodePortSvc.PortMappings) == 0 { return "" } // Get the dbmc1 NodePort service diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index f941d1c2..042fc46f 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -436,7 +436,7 @@ func buildContainerSpecForOracleRestart(instance *oraclerestart.OracleRestart, O containerSpec.ReadinessProbe = instance.Spec.ReadinessProbe } // building Complete Container Spec - containerSpec.Ports = buildContainerPortsDef(instance, OracleRestartSpex) + containerSpec.Ports = buildContainerPortsDef(instance) result = []corev1.Container{ containerSpec, @@ -701,34 +701,22 @@ func BuildExternalServiceDefForOracleRestart(instance *oraclerestart.OracleResta // If user is setting Node Port Service if opType == "nodeport" { - if OracleRestartSpex.PortMappings != nil { - npSvc = OracleRestartSpex.NodePortSvc[index] - npSvc.PortMappings = OracleRestartSpex.PortMappings - } + npSvc = instance.Spec.NodePortSvc + npSvc.PortMappings = instance.Spec.NodePortSvc.PortMappings service.Spec.Type = corev1.ServiceTypeNodePort + if len(instance.Spec.NodePortSvc.SvcAnnotation) != 0 { + service.Annotations = instance.Spec.NodePortSvc.SvcAnnotation + } } else if opType == "lbservice" { npSvc = instance.Spec.LbService npSvc.PortMappings = instance.Spec.LbService.PortMappings service.Spec.Type = corev1.ServiceTypeLoadBalancer - } else if opType == "onssvc" { - var exSvcPorts oraclerestart.OracleRestartPortMapping - var exSvc oraclerestart.OracleRestartNodePortSvc - npSvc.SvcType = svctype - exSvcPorts.NodePort = *OracleRestartSpex.OnsTargetPort - npSvc.PortMappings[index].NodePort = *OracleRestartSpex.OnsTargetPort - if OracleRestartSpex.OnsLocalPort != nil { - exSvcPorts.Port = *OracleRestartSpex.OnsLocalPort - } else { - exSvcPorts.Port = 6200 + if len(instance.Spec.LbService.SvcAnnotation) != 0 { + service.Annotations = instance.Spec.LbService.SvcAnnotation } - exSvc.PortMappings = append(exSvc.PortMappings, exSvcPorts) - npSvc = exSvc } //service.Name = npSvc.SvcName - if len(instance.Spec.LbService.SvcAnnotation) != 0 { - service.Annotations = instance.Spec.LbService.SvcAnnotation - } service.Spec.Selector = getSvcLabelsForOracleRestart(0, OracleRestartSpex) service.Spec.Ports = buildOracleRestartSvcPortsDef(npSvc) @@ -759,16 +747,21 @@ func getOracleRestartSvcName(instance *oraclerestart.OracleRestart, OracleRestar switch svcType { case "local": - return OracleRestartSpex.Name + "-0" - case "onssvc": - return OracleRestartSpex.Name + "-0-ons" - case "lbsvc": - return OracleRestartSpex.Name + "-0-lbsvc" + return OracleRestartSpex.Name + "-0-local" + case "lbservice": + if instance.Spec.LbService.SvcName != "" { + return instance.Spec.LbService.SvcName + "-0-lbsvc" + } else { + return OracleRestartSpex.Name + "-0-lbsvc" + } case "nodeport": - return OracleRestartSpex.Name + "-0-npsvc" + if instance.Spec.NodePortSvc.SvcName != "" { + return instance.Spec.NodePortSvc.SvcName + "-0-npsvc" + } else { + return OracleRestartSpex.Name + "-0-npsvc" + } default: return OracleRestartSpex.Name - } } diff --git a/commons/oraclerestart/utils/utils.go b/commons/oraclerestart/utils/utils.go index 1d7580be..850f5110 100644 --- a/commons/oraclerestart/utils/utils.go +++ b/commons/oraclerestart/utils/utils.go @@ -136,7 +136,7 @@ func CheckStringInList(str1 string, arr []string) bool { func CheckStatusFlag(flagStr string) bool { - if strings.ToLower(flagStr) == "force" { + if strings.ToLower(flagStr) == "delete" { return true } diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 4c268ebe..d721d535 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -112,7 +112,6 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques blocked := false // var svcType string var nilErr error = nil - var oracleRestartInst oraclerestartdb.OracleRestartInstDetailSpec resultNq := ctrl.Result{Requeue: false} resultQ := ctrl.Result{Requeue: true, RequeueAfter: 60 * time.Second} @@ -133,7 +132,6 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques oracleRestart.Spec.IsFailed = true return resultQ, err } - oracleRestartInst = oracleRestart.Spec.InstDetails // Retrieve the old spec from annotations oldSpec, err := r.GetOldSpec(oracleRestart) @@ -181,12 +179,14 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // cleanup RAC Instance + // This is a special case if user wants just delete the pod and recreate it _, err = r.cleanupOracleRestartInstance(req, ctx, oracleRestart) if err != nil { result = resultQ r.Log.Info(err.Error()) return result, nilErr } + // debugging err = checkOracleRestartState(oracleRestart) if err != nil { @@ -239,37 +239,18 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } var svcType string - if oracleRestart.Spec.ExternalSvcType != nil { - svcType = *oracleRestart.Spec.ExternalSvcType - } else { - if len(oracleRestart.Spec.LbService.PortMappings) == 0 { - svcType = "nodeport" - } - } result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildServiceDefForOracleRestart(oracleRestart, 0, oracleRestart.Spec.InstDetails, "local")) if err != nil { result = resultNq return result, err } - if oracleRestartInst.NodePortSvc != nil { - for index := range oracleRestartInst.NodePortSvc { - result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildExternalServiceDefForOracleRestart(oracleRestart, int32(index), oracleRestart.Spec.InstDetails, svcType, "nodeport")) - if err != nil { - result = resultNq - return result, err - } - - } - } - - if oracleRestartInst.OnsTargetPort != nil { - result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildExternalServiceDefForOracleRestart(oracleRestart, 0, oracleRestart.Spec.InstDetails, svcType, "onssvc")) + if len(oracleRestart.Spec.NodePortSvc.PortMappings) != 0 { + result, err = r.createOrReplaceService(ctx, oracleRestart, oraclerestartcommon.BuildExternalServiceDefForOracleRestart(oracleRestart, 0, oracleRestart.Spec.InstDetails, svcType, "nodeport")) if err != nil { result = resultNq return result, err } - } if len(oracleRestart.Spec.LbService.PortMappings) != 0 { @@ -499,8 +480,8 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques return result, err } } - if oracleRestartInst.OnsTargetPort != nil { + if oracleRestart.Spec.EnableOns == "true" { OraRestartSpex := oracleRestart.Spec.InstDetails orestartSfSet, err := oraclerestartcommon.CheckSfset(OraRestartSpex.Name, oracleRestart, r.Client) if err != nil { @@ -514,11 +495,11 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques r.Log.Error(err, "Failed to list pods") return ctrl.Result{}, err } + err = r.updateONS(ctx, podList, oracleRestart, "start") if err != nil { return ctrl.Result{}, err } - } completed = true @@ -3120,7 +3101,7 @@ func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, } } - svcTypes := []string{"local"} + svcTypes := []string{"onssvc", "lbservice", "nodeport"} for _, svcType := range svcTypes { svcFound, err := oraclerestartcommon.CheckORestartSvc(oracleRestart, svcType, oraRestartSpex, "", r.Client) if err == nil { @@ -3130,28 +3111,6 @@ func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, } } } - svcFound, err := oraclerestartcommon.CheckORestartSvc(oracleRestart, "onssvc", oracleRestart.Spec.InstDetails, "", r.Client) - if err == nil { - // See if service already exists and create if it doesn't - err = r.Client.Delete(context.Background(), svcFound) - if err != nil { - return err - } - } - - if oraRestartSpex.NodePortSvc != nil { - for index := range oraRestartSpex.NodePortSvc { - svcFound, err := oraclerestartcommon.CheckORestartSvc(oracleRestart, "nodeport", oraRestartSpex, oraRestartSpex.NodePortSvc[index].SvcName, r.Client) - if err == nil { - log.Info("Deleting ORestart Service " + svcFound.Name) - if err := r.Client.Delete(context.Background(), svcFound); err != nil { - return err - } - } - } - } - - // Delete ConfigMap log.Info("Successfully cleaned up OracleRestart") return nil @@ -3250,15 +3209,6 @@ func (r *OracleRestartReconciler) deleteOracleRestartInst(OraRestartSpex oracler } } - svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "vip", OraRestartSpex, "", r.Client) - if err == nil { - // See if service already exists and create if it doesn't - err = r.Client.Delete(context.Background(), svcFound) - if err != nil { - return err - } - } - svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "local", OraRestartSpex, "", r.Client) if err == nil { // See if service already exists and create if it doesn't @@ -3268,34 +3218,26 @@ func (r *OracleRestartReconciler) deleteOracleRestartInst(OraRestartSpex oracler } } - svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "onssvc", OraRestartSpex, "", r.Client) - if err == nil { - // See if service already exists and create if it doesn't - err = r.Client.Delete(context.Background(), svcFound) - if err != nil { - return err - } - } - - svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "lsnrsvc", OraRestartSpex, "", r.Client) - if err == nil { - // See if service already exists and create if it doesn't - err = r.Client.Delete(context.Background(), svcFound) - if err != nil { - return err + //NodePort Service + if len(oracleRestart.Spec.NodePortSvc.PortMappings) != 0 { + svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "nodeport", OraRestartSpex, "", r.Client) + if err == nil { + // See if service already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err + } } } //NodePort Service - if OraRestartSpex.NodePortSvc != nil { - for index := range OraRestartSpex.NodePortSvc { - svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "nodeport", OraRestartSpex, OraRestartSpex.NodePortSvc[index].SvcName, r.Client) - if err == nil { - // See if service already exists and create if it doesn't - err = r.Client.Delete(context.Background(), svcFound) - if err != nil { - return err - } + if len(oracleRestart.Spec.LbService.PortMappings) != 0 { + svcFound, err = oraclerestartcommon.CheckORestartSvc(oracleRestart, "lbservice", OraRestartSpex, "", r.Client) + if err == nil { + // See if service already exists and create if it doesn't + err = r.Client.Delete(context.Background(), svcFound) + if err != nil { + return err } } } From 4c2185ab952bec58bcb6154e6153a5ee86f4d907 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 4 Sep 2025 05:17:19 +0000 Subject: [PATCH 305/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 4 +- apis/database/v4/zz_generated.deepcopy.go | 76 +-- .../database.oracle.com_oraclerestarts.yaml | 296 +++++----- ...vability.oracle.com_databaseobservers.yaml | 246 ++++++++ config/manager/kustomization.yaml | 2 +- config/rbac/role.yaml | 1 + .../database/oraclerestart_controller.go | 2 +- go.mod | 122 ++-- go.sum | 164 ++++++ oracle-database-operator.yaml | 533 ++++++++++++------ 10 files changed, 983 insertions(+), 463 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 523dcc39..1fd58ea1 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -77,8 +77,8 @@ type OracleRestartSpec struct { StorageClass string `json:"storageClass,omitempty"` LbService OracleRestartNodePortSvc `json:"lbService,omitempty"` NodePortSvc OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if - // +kubebuilder:validation:Enum=true;false - // +kubebuilder:default=true + // +kubebuilder:validation:Enum=enable;disable + // +kubebuilder:default="enable" EnableOns string `json:"enableOns,omitempty"` } diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index e58351a8..c69f656a 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -2517,26 +2517,6 @@ func (in *LTDESecret) DeepCopy() *LTDESecret { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NetworkDetailSpec) DeepCopyInto(out *NetworkDetailSpec) { - *out = *in - if in.IPs != nil { - in, out := &in.IPs, &out.IPs - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkDetailSpec. -func (in *NetworkDetailSpec) DeepCopy() *NetworkDetailSpec { - if in == nil { - return nil - } - out := new(NetworkDetailSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OciAcdSpec) DeepCopyInto(out *OciAcdSpec) { *out = *in @@ -2862,11 +2842,6 @@ func (in *OracleRestartInstDetailSpec) DeepCopyInto(out *OracleRestartInstDetail (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = new(corev1.ResourceRequirements) - (*in).DeepCopyInto(*out) - } if in.PvcName != nil { in, out := &in.PvcName, &out.PvcName *out = make(map[string]string, len(*in)) @@ -2874,28 +2849,6 @@ func (in *OracleRestartInstDetailSpec) DeepCopyInto(out *OracleRestartInstDetail (*out)[key] = val } } - if in.NodePortSvc != nil { - in, out := &in.NodePortSvc, &out.NodePortSvc - *out = make([]OracleRestartNodePortSvc, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.PortMappings != nil { - in, out := &in.PortMappings, &out.PortMappings - *out = make([]OracleRestartPortMapping, len(*in)) - copy(*out, *in) - } - if in.OnsTargetPort != nil { - in, out := &in.OnsTargetPort, &out.OnsTargetPort - *out = new(int32) - **out = **in - } - if in.OnsLocalPort != nil { - in, out := &in.OnsLocalPort, &out.OnsLocalPort - *out = new(int32) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartInstDetailSpec. @@ -2987,6 +2940,23 @@ func (in *OracleRestartNodePortSvc) DeepCopyInto(out *OracleRestartNodePortSvc) *out = make([]OracleRestartPortMapping, len(*in)) copy(*out, *in) } + if in.SvcAnnotation != nil { + in, out := &in.SvcAnnotation, &out.SvcAnnotation + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.OnsTargetPort != nil { + in, out := &in.OnsTargetPort, &out.OnsTargetPort + *out = new(int32) + **out = **in + } + if in.OnsLocalPort != nil { + in, out := &in.OnsLocalPort, &out.OnsLocalPort + *out = new(int32) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartNodePortSvc. @@ -3048,11 +3018,6 @@ func (in *OracleRestartSpec) DeepCopyInto(out *OracleRestartSpec) { *out = new(AsmDiskDetails) (*in).DeepCopyInto(*out) } - if in.NfsStorageDetails != nil { - in, out := &in.NfsStorageDetails, &out.NfsStorageDetails - *out = new(corev1.NFSVolumeSource) - **out = **in - } if in.SshKeySecret != nil { in, out := &in.SshKeySecret, &out.SshKeySecret *out = new(OracleRestartSshSecretDetails) @@ -3073,11 +3038,6 @@ func (in *OracleRestartSpec) DeepCopyInto(out *OracleRestartSpec) { *out = new(corev1.PodSecurityContext) (*in).DeepCopyInto(*out) } - if in.ExternalSvcType != nil { - in, out := &in.ExternalSvcType, &out.ExternalSvcType - *out = new(string) - **out = **in - } if in.DbSecret != nil { in, out := &in.DbSecret, &out.DbSecret *out = new(OracleRestartDbPwdSecretDetails) @@ -3094,6 +3054,8 @@ func (in *OracleRestartSpec) DeepCopyInto(out *OracleRestartSpec) { *out = new(corev1.ResourceRequirements) (*in).DeepCopyInto(*out) } + in.LbService.DeepCopyInto(&out.LbService) + in.NodePortSvc.DeepCopyInto(&out.NodePortSvc) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestartSpec. diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index a284469a..5612f00f 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -183,11 +183,19 @@ spec: pwdFileName: type: string type: object - externalSvcType: + enableOns: + default: enable + enum: + - enable + - disable type: string image: type: string imagePullPolicy: + enum: + - Always + - IfNotPresent + - Never type: string imagePullSecret: type: string @@ -227,6 +235,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -273,31 +298,29 @@ spec: type: string name: type: string - nodePortSvc: + pvcName: + additionalProperties: + type: string + type: object + swLocStorageSizeInGb: + type: integer + workerNode: items: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - type: object - type: array - svcType: - type: string - type: object + type: string type: array + type: object + isDebug: + type: string + isDeleteTopology: + type: string + isFailed: + type: boolean + isManual: + type: boolean + lbService: + properties: + name: + type: string onsLocalPort: format: int32 type: integer @@ -314,77 +337,65 @@ spec: format: int32 type: integer protocol: + default: TCP + enum: + - TCP + - UDP + - SCTP type: string targetPort: format: int32 type: integer type: object type: array - pvcName: + svcAnnotation: additionalProperties: type: string type: object - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - swLocStorageSizeInGb: + svcLBIP: + type: string + svcType: + type: string + type: object + nodePortSvc: + properties: + name: + type: string + onsLocalPort: + format: int32 type: integer - workerNode: + onsTargetPort: + format: int32 + type: integer + portMappings: items: - type: string + properties: + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + enum: + - TCP + - UDP + - SCTP + type: string + targetPort: + format: int32 + type: integer + type: object type: array - type: object - isDebug: - type: string - isDeleteOraPvc: - type: string - isDeleteTopology: - type: string - isFailed: - type: boolean - isManual: - type: boolean - nfsStorageDetails: - properties: - path: + svcAnnotation: + additionalProperties: + type: string + type: object + svcLBIP: type: string - readOnly: - type: boolean - server: + svcType: type: string - required: - - path - - server type: object readinessProbe: properties: @@ -657,8 +668,6 @@ spec: type: object storageClass: type: string - storageSizeInGB: - type: integer tdeWalletSecret: properties: encryptionType: @@ -676,8 +685,6 @@ spec: pwdFileName: type: string type: object - useNfsforSwStorage: - type: string required: - instDetails - securityContext @@ -710,6 +717,12 @@ spec: properties: name: type: string + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer portMappings: items: properties: @@ -720,12 +733,23 @@ spec: format: int32 type: integer protocol: + default: TCP + enum: + - TCP + - UDP + - SCTP type: string targetPort: format: int32 type: integer type: object type: array + svcAnnotation: + additionalProperties: + type: string + type: object + svcLBIP: + type: string svcType: type: string type: object @@ -740,6 +764,11 @@ spec: format: int32 type: integer protocol: + default: TCP + enum: + - TCP + - UDP + - SCTP type: string targetPort: format: int32 @@ -984,6 +1013,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -1030,90 +1076,10 @@ spec: type: string name: type: string - nodePortSvc: - items: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - type: object - type: array - svcType: - type: string - type: object - type: array - onsLocalPort: - format: int32 - type: integer - onsTargetPort: - format: int32 - type: integer - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - type: object - type: array pvcName: additionalProperties: type: string type: object - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object swLocStorageSizeInGb: type: integer workerNode: diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index 5bd7cc02..baf2bd7d 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -532,6 +532,8 @@ spec: type: string type: object type: array + noProxy: + type: string oauth2: properties: clientId: @@ -719,7 +721,28 @@ spec: type: string port: type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^(http|https|socks5)://.+$ type: string relabelings: items: @@ -944,6 +967,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -1372,6 +1412,29 @@ spec: type: object restartPolicy: type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: properties: allowPrivilegeEscalation: @@ -2175,6 +2238,25 @@ spec: type: array x-kubernetes-list-type: atomic type: object + podCertificate: + properties: + certificateChainPath: + type: string + credentialBundlePath: + type: string + keyPath: + type: string + keyType: + type: string + maxExpirationSeconds: + format: int32 + type: integer + signerName: + type: string + required: + - keyType + - signerName + type: object secret: properties: items: @@ -2953,6 +3035,8 @@ spec: type: string type: object type: array + noProxy: + type: string oauth2: properties: clientId: @@ -3140,7 +3224,28 @@ spec: type: string port: type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^(http|https|socks5)://.+$ type: string relabelings: items: @@ -3365,6 +3470,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -3793,6 +3915,29 @@ spec: type: object restartPolicy: type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: properties: allowPrivilegeEscalation: @@ -4596,6 +4741,25 @@ spec: type: array x-kubernetes-list-type: atomic type: object + podCertificate: + properties: + certificateChainPath: + type: string + credentialBundlePath: + type: string + keyPath: + type: string + keyType: + type: string + maxExpirationSeconds: + format: int32 + type: integer + signerName: + type: string + required: + - keyType + - signerName + type: object secret: properties: items: @@ -5374,6 +5538,8 @@ spec: type: string type: object type: array + noProxy: + type: string oauth2: properties: clientId: @@ -5561,7 +5727,28 @@ spec: type: string port: type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^(http|https|socks5)://.+$ type: string relabelings: items: @@ -5786,6 +5973,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -6214,6 +6418,29 @@ spec: type: object restartPolicy: type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: properties: allowPrivilegeEscalation: @@ -7017,6 +7244,25 @@ spec: type: array x-kubernetes-list-type: atomic type: object + podCertificate: + properties: + certificateChainPath: + type: string + credentialBundlePath: + type: string + keyPath: + type: string + keyType: + type: string + maxExpirationSeconds: + format: int32 + type: integer + signerName: + type: string + required: + - keyType + - signerName + type: object secret: properties: items: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 74303bea..1d870700 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: basedb-operator-sa + newTag: orestart-operator-mltp1 diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 7bb96a74..0282587c 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -169,6 +169,7 @@ rules: - database.oracle.com resources: - dbcssystems/finalizers + - lrpdbs/configmaps - lrpdbs/finalizers - oraclerestarts/finalizers - shardingdatabases/finalizers diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index d721d535..bb1499e4 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -481,7 +481,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } } - if oracleRestart.Spec.EnableOns == "true" { + if oracleRestart.Spec.EnableOns == "enable" { OraRestartSpex := oracleRestart.Spec.InstDetails orestartSfSet, err := oraclerestartcommon.CheckSfset(OraRestartSpex.Name, oracleRestart, r.Client) if err != nil { diff --git a/go.mod b/go.mod index d0505d29..5b8720a5 100644 --- a/go.mod +++ b/go.mod @@ -4,104 +4,120 @@ go 1.24.4 require ( github.com/go-logr/logr v1.4.3 - github.com/onsi/ginkgo/v2 v2.23.4 - github.com/onsi/gomega v1.37.0 - github.com/oracle/oci-go-sdk/v65 v65.95.0 - github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0 + github.com/onsi/ginkgo/v2 v2.25.2 + github.com/onsi/gomega v1.38.2 + github.com/oracle/oci-go-sdk/v65 v65.99.2 + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.85.0 go.uber.org/zap v1.27.0 - golang.org/x/text v0.27.0 - k8s.io/api v0.33.3 - k8s.io/apimachinery v0.33.3 - k8s.io/cli-runtime v0.33.3 - k8s.io/client-go v0.33.3 - k8s.io/kubectl v0.33.3 - k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 - sigs.k8s.io/controller-runtime v0.21.0 - sigs.k8s.io/yaml v1.5.0 + golang.org/x/text v0.28.0 + k8s.io/api v0.34.0 + k8s.io/apimachinery v0.34.0 + k8s.io/cli-runtime v0.34.0 + k8s.io/client-go v0.34.0 + k8s.io/kubectl v0.34.0 + k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d + sigs.k8s.io/controller-runtime v0.22.0 + sigs.k8s.io/yaml v1.6.0 ) require ( - github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/chai2010/gettext-go v1.0.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/camelcase v1.0.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.8.0 // indirect - github.com/go-errors/errors v1.4.2 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-errors/errors v1.5.1 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/jsonpointer v0.22.0 // indirect + github.com/go-openapi/jsonreference v0.21.1 // indirect + github.com/go-openapi/swag v0.24.1 // indirect + github.com/go-openapi/swag/cmdutils v0.24.0 // indirect + github.com/go-openapi/swag/conv v0.24.0 // indirect + github.com/go-openapi/swag/fileutils v0.24.0 // indirect + github.com/go-openapi/swag/jsonname v0.24.0 // indirect + github.com/go-openapi/swag/jsonutils v0.24.0 // indirect + github.com/go-openapi/swag/loading v0.24.0 // indirect + github.com/go-openapi/swag/mangling v0.24.0 // indirect + github.com/go-openapi/swag/netutils v0.24.0 // indirect + github.com/go-openapi/swag/stringutils v0.24.0 // indirect + github.com/go-openapi/swag/typeutils v0.24.0 // indirect + github.com/go-openapi/swag/yamlutils v0.24.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/gofrs/flock v0.8.1 // indirect + github.com/gofrs/flock v0.12.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/google/pprof v0.0.0-20250903194437-c28834ac2320 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/mailru/easyjson v0.9.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/moby/spdystream v0.5.0 // indirect - github.com/moby/term v0.5.0 // indirect + github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.22.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.66.0 // indirect + github.com/prometheus/procfs v0.17.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sony/gobreaker v0.5.0 // indirect - github.com/spf13/cobra v1.8.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/sony/gobreaker v1.0.0 // indirect + github.com/spf13/cobra v1.10.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect - go.yaml.in/yaml/v3 v3.0.3 // indirect - golang.org/x/crypto v0.39.0 // indirect - golang.org/x/net v0.41.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/term v0.32.0 // indirect - golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.34.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/time v0.12.0 // indirect + golang.org/x/tools v0.36.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.33.1 // indirect - k8s.io/component-base v0.33.3 // indirect - k8s.io/component-helpers v0.33.3 // indirect + k8s.io/apiextensions-apiserver v0.34.0 // indirect + k8s.io/component-base v0.34.0 // indirect + k8s.io/component-helpers v0.34.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect - sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect - sigs.k8s.io/kustomize/api v0.19.0 // indirect - sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect + k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/kustomize/api v0.20.1 // indirect + sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) diff --git a/go.sum b/go.sum index 5a933f26..d7213b43 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,11 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -12,7 +16,10 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +github.com/chai2010/gettext-go v1.0.3 h1:9liNh8t+u26xl5ddmWLmsOsdNLwkdRTg5AG+JnTiM80= +github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -22,6 +29,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= @@ -32,10 +41,16 @@ github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8 github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -43,22 +58,55 @@ github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonpointer v0.22.0 h1:TmMhghgNef9YXxTu1tOopo+0BGEytxA+okbry0HjZsM= +github.com/go-openapi/jsonpointer v0.22.0/go.mod h1:xt3jV88UtExdIkkL7NloURjRQjbeUgcxFblMjq2iaiU= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/jsonreference v0.21.1 h1:bSKrcl8819zKiOgxkbVNRUBIr6Wwj9KYrDbMjRs0cDA= +github.com/go-openapi/jsonreference v0.21.1/go.mod h1:PWs8rO4xxTUqKGu+lEvvCxD5k2X7QYkKAepJyCmSTT8= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.24.1 h1:DPdYTZKo6AQCRqzwr/kGkxJzHhpKxZ9i/oX0zag+MF8= +github.com/go-openapi/swag v0.24.1/go.mod h1:sm8I3lCPlspsBBwUm1t5oZeWZS0s7m/A+Psg0ooRU0A= +github.com/go-openapi/swag/cmdutils v0.24.0 h1:KlRCffHwXFI6E5MV9n8o8zBRElpY4uK4yWyAMWETo9I= +github.com/go-openapi/swag/cmdutils v0.24.0/go.mod h1:uxib2FAeQMByyHomTlsP8h1TtPd54Msu2ZDU/H5Vuf8= +github.com/go-openapi/swag/conv v0.24.0 h1:ejB9+7yogkWly6pnruRX45D1/6J+ZxRu92YFivx54ik= +github.com/go-openapi/swag/conv v0.24.0/go.mod h1:jbn140mZd7EW2g8a8Y5bwm8/Wy1slLySQQ0ND6DPc2c= +github.com/go-openapi/swag/fileutils v0.24.0 h1:U9pCpqp4RUytnD689Ek/N1d2N/a//XCeqoH508H5oak= +github.com/go-openapi/swag/fileutils v0.24.0/go.mod h1:3SCrCSBHyP1/N+3oErQ1gP+OX1GV2QYFSnrTbzwli90= +github.com/go-openapi/swag/jsonname v0.24.0 h1:2wKS9bgRV/xB8c62Qg16w4AUiIrqqiniJFtZGi3dg5k= +github.com/go-openapi/swag/jsonname v0.24.0/go.mod h1:GXqrPzGJe611P7LG4QB9JKPtUZ7flE4DOVechNaDd7Q= +github.com/go-openapi/swag/jsonutils v0.24.0 h1:F1vE1q4pg1xtO3HTyJYRmEuJ4jmIp2iZ30bzW5XgZts= +github.com/go-openapi/swag/jsonutils v0.24.0/go.mod h1:vBowZtF5Z4DDApIoxcIVfR8v0l9oq5PpYRUuteVu6f0= +github.com/go-openapi/swag/loading v0.24.0 h1:ln/fWTwJp2Zkj5DdaX4JPiddFC5CHQpvaBKycOlceYc= +github.com/go-openapi/swag/loading v0.24.0/go.mod h1:gShCN4woKZYIxPxbfbyHgjXAhO61m88tmjy0lp/LkJk= +github.com/go-openapi/swag/mangling v0.24.0 h1:PGOQpViCOUroIeak/Uj/sjGAq9LADS3mOyjznmHy2pk= +github.com/go-openapi/swag/mangling v0.24.0/go.mod h1:Jm5Go9LHkycsz0wfoaBDkdc4CkpuSnIEf62brzyCbhc= +github.com/go-openapi/swag/netutils v0.24.0 h1:Bz02HRjYv8046Ycg/w80q3g9QCWeIqTvlyOjQPDjD8w= +github.com/go-openapi/swag/netutils v0.24.0/go.mod h1:WRgiHcYTnx+IqfMCtu0hy9oOaPR0HnPbmArSRN1SkZM= +github.com/go-openapi/swag/stringutils v0.24.0 h1:i4Z/Jawf9EvXOLUbT97O0HbPUja18VdBxeadyAqS1FM= +github.com/go-openapi/swag/stringutils v0.24.0/go.mod h1:5nUXB4xA0kw2df5PRipZDslPJgJut+NjL7D25zPZ/4w= +github.com/go-openapi/swag/typeutils v0.24.0 h1:d3szEGzGDf4L2y1gYOSSLeK6h46F+zibnEas2Jm/wIw= +github.com/go-openapi/swag/typeutils v0.24.0/go.mod h1:q8C3Kmk/vh2VhpCLaoR2MVWOGP8y7Jc8l82qCTd1DYI= +github.com/go-openapi/swag/yamlutils v0.24.0 h1:bhw4894A7Iw6ne+639hsBNRHg9iZg/ISrOVr+sJGp4c= +github.com/go-openapi/swag/yamlutils v0.24.0/go.mod h1:DpKv5aYuaGm/sULePoeiG8uwMpZSfReo1HR3Ik0yaG8= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -66,12 +114,16 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/pprof v0.0.0-20250903194437-c28834ac2320 h1:c7ayAhbRP9HnEl/hg/WQOM9s0snWztfW6feWXZbGHw0= +github.com/google/pprof v0.0.0-20250903194437-c28834ac2320/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -99,17 +151,23 @@ github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffkt github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -118,10 +176,16 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/ginkgo/v2 v2.25.2 h1:hepmgwx1D+llZleKQDMEvy8vIlCxMGt7W5ZxDjIEhsw= +github.com/onsi/ginkgo/v2 v2.25.2/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/oracle/oci-go-sdk/v65 v65.95.0 h1:fI+/mfJOS2DkQ+/AFSyJAfn1XFR4TTGm2AhN6xbsi00= github.com/oracle/oci-go-sdk/v65 v65.95.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= +github.com/oracle/oci-go-sdk/v65 v65.99.2 h1:W1HzD28r+xqqT57FpH7ymOFE3BM/p4J/Ia1SfDf55nM= +github.com/oracle/oci-go-sdk/v65 v65.99.2/go.mod h1:RGiXfpDDmRRlLtqlStTzeBjjdUNXyqm3KXKyLCm3A/Q= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -132,14 +196,24 @@ github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4 github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0 h1:j9Ce3W6X6Tzi0QnSap+YzGwpqJLJGP/7xV6P9f86jjM= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0/go.mod h1:sSxwdmprUfmRfTknPc4KIjUd2ZIc/kirw4UdXNhOauM= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.85.0 h1:oY+F5FZFmCjCyzkHWPjVQpzvnvEB/0FP+iyzDUUlqFc= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.85.0/go.mod h1:VB7wtBmDT6W2RJHzsvPZlBId+EnmeQA0d33fFTXvraM= github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= +github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -148,10 +222,17 @@ github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ= +github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -166,6 +247,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= @@ -187,18 +269,28 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -207,16 +299,26 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -229,89 +331,151 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE= +k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug= k8s.io/apiextensions-apiserver v0.33.1 h1:N7ccbSlRN6I2QBcXevB73PixX2dQNIW0ZRuguEE91zI= k8s.io/apiextensions-apiserver v0.33.1/go.mod h1:uNQ52z1A1Gu75QSa+pFK5bcXc4hq7lpOXbweZgi4dqA= +k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc= +k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0= k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0= +k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= k8s.io/cli-runtime v0.33.3 h1:Dgy4vPjNIu8LMJBSvs8W0LcdV0PX/8aGG1DA1W8lklA= k8s.io/cli-runtime v0.33.3/go.mod h1:yklhLklD4vLS8HNGgC9wGiuHWze4g7x6XQZ+8edsKEo= +k8s.io/cli-runtime v0.34.0 h1:N2/rUlJg6TMEBgtQ3SDRJwa8XyKUizwjlOknT1mB2Cw= +k8s.io/cli-runtime v0.34.0/go.mod h1:t/skRecS73Piv+J+FmWIQA2N2/rDjdYSQzEE67LUUs8= k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo= +k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY= k8s.io/component-base v0.33.3 h1:mlAuyJqyPlKZM7FyaoM/LcunZaaY353RXiOd2+B5tGA= k8s.io/component-base v0.33.3/go.mod h1:ktBVsBzkI3imDuxYXmVxZ2zxJnYTZ4HAsVj9iF09qp4= +k8s.io/component-base v0.34.0 h1:bS8Ua3zlJzapklsB1dZgjEJuJEeHjj8yTu1gxE2zQX8= +k8s.io/component-base v0.34.0/go.mod h1:RSCqUdvIjjrEm81epPcjQ/DS+49fADvGSCkIP3IC6vg= k8s.io/component-helpers v0.33.3 h1:fjWVORSQfI0WKzPeIFSju/gMD9sybwXBJ7oPbqQu6eM= k8s.io/component-helpers v0.33.3/go.mod h1:7iwv+Y9Guw6X4RrnNQOyQlXcvJrVjPveHVqUA5dm31c= +k8s.io/component-helpers v0.34.0 h1:5T7P9XGMoUy1JDNKzHf0p/upYbeUf8ZaSf9jbx0QlIo= +k8s.io/component-helpers v0.34.0/go.mod h1:kaOyl5tdtnymriYcVZg4uwDBe2d1wlIpXyDkt6sVnt4= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f h1:wyRlmLgBSXi3kgawro8klrMRljXeRo1HFkQRs+meYfs= +k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/kubectl v0.33.3 h1:r/phHvH1iU7gO/l7tTjQk2K01ER7/OAJi8uFHHyWSac= k8s.io/kubectl v0.33.3/go.mod h1:euj2bG56L6kUGOE/ckZbCoudPwuj4Kud7BR0GzyNiT0= +k8s.io/kubectl v0.34.0 h1:NcXz4TPTaUwhiX4LU+6r6udrlm0NsVnSkP3R9t0dmxs= +k8s.io/kubectl v0.34.0/go.mod h1:bmd0W5i+HuG7/p5sqicr0Li0rR2iIhXL0oUyLF3OjR4= k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/controller-runtime v0.22.0 h1:mTOfibb8Hxwpx3xEkR56i7xSjB+nH4hZG37SrlCY5e0= +sigs.k8s.io/controller-runtime v0.22.0/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ= sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o= +sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= +sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM= sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA= sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY= +sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78= +sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ= sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index b9980bf0..9dddde5e 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -1831,6 +1831,8 @@ spec: type: string type: object type: array + noProxy: + type: string oauth2: properties: clientId: @@ -2018,7 +2020,28 @@ spec: type: string port: type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^(http|https|socks5)://.+$ type: string relabelings: items: @@ -2243,6 +2266,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -2671,6 +2711,29 @@ spec: type: object restartPolicy: type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: properties: allowPrivilegeEscalation: @@ -3474,6 +3537,25 @@ spec: type: array x-kubernetes-list-type: atomic type: object + podCertificate: + properties: + certificateChainPath: + type: string + credentialBundlePath: + type: string + keyPath: + type: string + keyType: + type: string + maxExpirationSeconds: + format: int32 + type: integer + signerName: + type: string + required: + - keyType + - signerName + type: object secret: properties: items: @@ -4252,6 +4334,8 @@ spec: type: string type: object type: array + noProxy: + type: string oauth2: properties: clientId: @@ -4439,7 +4523,28 @@ spec: type: string port: type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^(http|https|socks5)://.+$ type: string relabelings: items: @@ -4664,6 +4769,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -5092,6 +5214,29 @@ spec: type: object restartPolicy: type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: properties: allowPrivilegeEscalation: @@ -5895,6 +6040,25 @@ spec: type: array x-kubernetes-list-type: atomic type: object + podCertificate: + properties: + certificateChainPath: + type: string + credentialBundlePath: + type: string + keyPath: + type: string + keyType: + type: string + maxExpirationSeconds: + format: int32 + type: integer + signerName: + type: string + required: + - keyType + - signerName + type: object secret: properties: items: @@ -6673,6 +6837,8 @@ spec: type: string type: object type: array + noProxy: + type: string oauth2: properties: clientId: @@ -6860,7 +7026,28 @@ spec: type: string port: type: string + proxyConnectHeader: + additionalProperties: + items: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + type: boolean proxyUrl: + pattern: ^(http|https|socks5)://.+$ type: string relabelings: items: @@ -7085,6 +7272,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -7513,6 +7717,29 @@ spec: type: object restartPolicy: type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: properties: allowPrivilegeEscalation: @@ -8316,6 +8543,25 @@ spec: type: array x-kubernetes-list-type: atomic type: object + podCertificate: + properties: + certificateChainPath: + type: string + credentialBundlePath: + type: string + keyPath: + type: string + keyType: + type: string + maxExpirationSeconds: + format: int32 + type: integer + signerName: + type: string + required: + - keyType + - signerName + type: object secret: properties: items: @@ -10539,11 +10785,19 @@ spec: pwdFileName: type: string type: object - externalSvcType: + enableOns: + default: enable + enum: + - enable + - disable type: string image: type: string imagePullPolicy: + enum: + - Always + - IfNotPresent + - Never type: string imagePullSecret: type: string @@ -10583,6 +10837,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -10629,37 +10900,29 @@ spec: type: string name: type: string - nodePortSvc: + pvcName: + additionalProperties: + type: string + type: object + swLocStorageSizeInGb: + type: integer + workerNode: items: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - type: object - type: array - svcAnnotation: - additionalProperties: - type: string - type: object - svcLBIP: - type: string - svcType: - type: string - type: object + type: string type: array + type: object + isDebug: + type: string + isDeleteTopology: + type: string + isFailed: + type: boolean + isManual: + type: boolean + lbService: + properties: + name: + type: string onsLocalPort: format: int32 type: integer @@ -10676,70 +10939,36 @@ spec: format: int32 type: integer protocol: + default: TCP + enum: + - TCP + - UDP + - SCTP type: string targetPort: format: int32 type: integer type: object type: array - pvcName: + svcAnnotation: additionalProperties: type: string type: object - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - swLocStorageSizeInGb: - type: integer - workerNode: - items: - type: string - type: array + svcLBIP: + type: string + svcType: + type: string type: object - isDebug: - type: string - isDeleteOraPvc: - type: string - isDeleteTopology: - type: string - isFailed: - type: boolean - isManual: - type: boolean - lbService: + nodePortSvc: properties: name: type: string + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer portMappings: items: properties: @@ -10750,6 +10979,11 @@ spec: format: int32 type: integer protocol: + default: TCP + enum: + - TCP + - UDP + - SCTP type: string targetPort: format: int32 @@ -10765,18 +10999,6 @@ spec: svcType: type: string type: object - nfsStorageDetails: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object readinessProbe: properties: exec: @@ -11048,8 +11270,6 @@ spec: type: object storageClass: type: string - storageSizeInGB: - type: integer tdeWalletSecret: properties: encryptionType: @@ -11067,8 +11287,6 @@ spec: pwdFileName: type: string type: object - useNfsforSwStorage: - type: string required: - instDetails - securityContext @@ -11101,6 +11319,12 @@ spec: properties: name: type: string + onsLocalPort: + format: int32 + type: integer + onsTargetPort: + format: int32 + type: integer portMappings: items: properties: @@ -11111,6 +11335,11 @@ spec: format: int32 type: integer protocol: + default: TCP + enum: + - TCP + - UDP + - SCTP type: string targetPort: format: int32 @@ -11137,6 +11366,11 @@ spec: format: int32 type: integer protocol: + default: TCP + enum: + - TCP + - UDP + - SCTP type: string targetPort: format: int32 @@ -11381,6 +11615,23 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: properties: containerName: @@ -11427,96 +11678,10 @@ spec: type: string name: type: string - nodePortSvc: - items: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - type: object - type: array - svcAnnotation: - additionalProperties: - type: string - type: object - svcLBIP: - type: string - svcType: - type: string - type: object - type: array - onsLocalPort: - format: int32 - type: integer - onsTargetPort: - format: int32 - type: integer - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - type: object - type: array pvcName: additionalProperties: type: string type: object - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object swLocStorageSizeInGb: type: integer workerNode: @@ -15962,7 +16127,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: dbcs-dev-docker.dockerhub-phx.oci.oraclecorp.com/oracle/database-operator:5dba2bde + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-mltp1 imagePullPolicy: Always name: manager ports: From cae8276f5c941261d4a1f627fd33196fbb407661 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 4 Sep 2025 05:59:52 +0000 Subject: [PATCH 306/414] Added pvc fixes --- commons/oraclerestart/oraclerestartcommon.go | 2 +- commons/oraclerestart/oraclerestartprov.go | 2 +- controllers/database/oraclerestart_controller.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 91d37510..3252f9d2 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -423,7 +423,7 @@ func DelORestartPVC(instance *oraclerestart.OracleRestart, index int, diskName s func DelRestartSwPvc(instance *oraclerestart.OracleRestart, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec, kClient client.Client, logger logr.Logger) error { - pvcName := OraRestartSpex.Name + "-oradata-sw-vol-" + OraRestartSpex.Name + "-0" + pvcName := OraRestartSpex.Name + "-oradata-sw-vol-pvc-" + OraRestartSpex.Name + "-0" LogMessages("DEBUG", "Inside the delPvc and received param: "+GetFmtStr(pvcName), nil, instance, logger) pvcFound, err := checkPvc(pvcName, instance, kClient) if err != nil { diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 042fc46f..6ce5b9d9 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -1006,7 +1006,7 @@ func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.Oracl func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) corev1.PersistentVolumeClaim { // If user-provided PVC name exists, skip volume claim template creation - pvcName := OracleRestartSpex.Name + "-oradata-sw-vol-pvc" + pvcName := OracleRestartSpex.Name + "-oradata-sw-vol-pvc-" return corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Name: pvcName, diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index bb1499e4..9107f310 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -3101,7 +3101,7 @@ func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, } } - svcTypes := []string{"onssvc", "lbservice", "nodeport"} + svcTypes := []string{"local", "lbservice", "nodeport"} for _, svcType := range svcTypes { svcFound, err := oraclerestartcommon.CheckORestartSvc(oracleRestart, svcType, oraRestartSpex, "", r.Client) if err == nil { From ffb6745e4f3e19365ad5b5ea51fd49f34e1ba8ee Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 4 Sep 2025 07:18:51 +0000 Subject: [PATCH 307/414] Added fixes --- commons/oraclerestart/oraclerestartprov.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 6ce5b9d9..042fc46f 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -1006,7 +1006,7 @@ func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.Oracl func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) corev1.PersistentVolumeClaim { // If user-provided PVC name exists, skip volume claim template creation - pvcName := OracleRestartSpex.Name + "-oradata-sw-vol-pvc-" + pvcName := OracleRestartSpex.Name + "-oradata-sw-vol-pvc" return corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Name: pvcName, From f8ec9c08b519789df3c60adcdedb518e5cd48662 Mon Sep 17 00:00:00 2001 From: marcstef Date: Fri, 5 Sep 2025 09:17:49 +0000 Subject: [PATCH 308/414] APEX download site --- docs/ordsservices/autoupgrade.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ordsservices/autoupgrade.md b/docs/ordsservices/autoupgrade.md index 3defdc42..55d431cb 100644 --- a/docs/ordsservices/autoupgrade.md +++ b/docs/ordsservices/autoupgrade.md @@ -30,9 +30,9 @@ APEX installation files can be provided to the pod in two ways: ### APEX installation automatic download -The ORDS container can download the latest APEX version from the Oracle Container Registry (OCR) or a custom URL. -Downloading APEX installation files requires the Kubernetes worker node to have internet access. -APEX download is performed at the container level and may be enabled or disabled for each pool. +The ORDS container can download the latest APEX version either from "Oracle APEX Downloads" or a specified custom URL. +To download APEX installation files, the Kubernetes worker node must have internet access. +The APEX download is defined globally, and upgrades can be enabled or disabled for each pool individually. ```yaml apiVersion: database.oracle.com/v1 From b96ef491b78d942a332884ab8cbf1bdfc0ae9917 Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Fri, 5 Sep 2025 14:14:43 +0000 Subject: [PATCH 309/414] test cases fix --- apis/database/v4/oraclerestart_webhook.go | 172 ++++++++++++++ commons/oraclerestart/oraclerestartcommon.go | 216 +++++++----------- config/manager/kustomization.yaml | 2 +- .../database/oraclerestart_controller.go | 48 +++- .../oraclerestart_prov_rupatch.yaml | 5 +- .../prerequisites_oracle_restart_db.md | 1 + go.mod | 3 - go.sum | 121 +--------- oracle-database-operator.yaml | 2 +- 9 files changed, 295 insertions(+), 275 deletions(-) diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index cf35cd2d..c1760db0 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -196,6 +196,30 @@ func (r *OracleRestart) ValidateCreate(ctx context.Context, obj runtime.Object) deviceWarnings = append(deviceWarnings, w...) validationErrs = append(validationErrs, errs...) + if cr.Spec.ConfigParams != nil { + // CRS + validationErrs = append(validationErrs, + cr.validateAsmRedundancyAndDisks( + cr.Spec.ConfigParams.CrsAsmDeviceList, + cr.Spec.ConfigParams.CrsAsmDiskDgRedundancy, + "crsAsmDeviceList")..., + ) + // DB + validationErrs = append(validationErrs, + cr.validateAsmRedundancyAndDisks( + cr.Spec.ConfigParams.DbAsmDeviceList, + cr.Spec.ConfigParams.DBAsmDiskDgRedundancy, + "dbAsmDeviceList")..., + ) + // RECO + validationErrs = append(validationErrs, + cr.validateAsmRedundancyAndDisks( + cr.Spec.ConfigParams.RecoAsmDeviceList, + cr.Spec.ConfigParams.RecoAsmDiskDgRedundancy, + "recoAsmDeviceList")..., + ) + } + for _, warning := range deviceWarnings { warnings = append(warnings, warning) } @@ -268,6 +292,31 @@ func (r *OracleRestart) ValidateUpdate(ctx context.Context, oldObj, newObj runti validationErrs = append(validationErrs, newCr.validateUpdateAsmStorage(old)...) validationErrs = append(validationErrs, newCr.validateUpdateGeneric(old)...) + if old.Spec.ConfigParams != nil && newCr.Spec.ConfigParams != nil { + + // CRS + if err := validateRedundancyOnUpdate(old.Spec.ConfigParams.CrsAsmDiskDgRedundancy, newCr.Spec.ConfigParams.CrsAsmDiskDgRedundancy, "crsAsmDiskDgRedundancy"); err != nil { + validationErrs = append(validationErrs, err) + } + // DB + if err := validateRedundancyOnUpdate(old.Spec.ConfigParams.DBAsmDiskDgRedundancy, newCr.Spec.ConfigParams.DBAsmDiskDgRedundancy, "dbAsmDiskDgRedundancy"); err != nil { + validationErrs = append(validationErrs, err) + } + // RECO + if err := validateRedundancyOnUpdate(old.Spec.ConfigParams.RecoAsmDiskDgRedundancy, newCr.Spec.ConfigParams.RecoAsmDiskDgRedundancy, "recoAsmDiskDgRedundancy"); err != nil { + validationErrs = append(validationErrs, err) + } + } + + if old.Spec.AsmStorageDetails != nil && newCr.Spec.AsmStorageDetails != nil { + errs := validateAsmNoDiskResize( + old.Spec.AsmStorageDetails.DisksBySize, + newCr.Spec.AsmStorageDetails.DisksBySize, + field.NewPath("spec").Child("asmStorageDetails").Child("disksBySize"), + ) + validationErrs = append(validationErrs, errs...) + } + if len(validationErrs) > 0 { return nil, apierrors.NewInvalid( schema.GroupKind{Group: "database.oracle.com", Kind: "OracleRestart"}, @@ -277,6 +326,74 @@ func (r *OracleRestart) ValidateUpdate(ctx context.Context, oldObj, newObj runti return nil, nil } +func redundancyChanged(oldRed, newRed string) bool { + // Normalize and compare (case-insensitive, trim spaces) + return strings.ToUpper(strings.TrimSpace(oldRed)) != strings.ToUpper(strings.TrimSpace(newRed)) +} + +func validateRedundancyOnUpdate( + oldRed, newRed, fieldName string, +) *field.Error { + normalizedOld := strings.ToUpper(strings.TrimSpace(oldRed)) + normalizedNew := strings.ToUpper(strings.TrimSpace(newRed)) + + if normalizedOld == "" { + // Old value not set, treat as EXTERNAL only + if normalizedNew != "" && normalizedNew != "EXTERNAL" { + return field.Invalid( + field.NewPath("spec").Child("configParams").Child(fieldName), + newRed, + "Redundancy was not set before (treated as EXTERNAL); it can only be EXTERNAL now.", + ) + } + } else { + // Old value set; do not allow changes + if redundancyChanged(normalizedOld, normalizedNew) { + return field.Invalid( + field.NewPath("spec").Child("configParams").Child(fieldName), + newRed, + fmt.Sprintf("Changing redundancy value for ASM diskgroup (%s) is not allowed on update.", fieldName), + ) + } + } + return nil +} + +func validateAsmNoDiskResize( + oldDisks, newDisks []DiskBySize, + fieldPath *field.Path, +) field.ErrorList { + var errs field.ErrorList + + // Build map of disk name -> size for old and new + oldDiskMap := make(map[string]int) + for _, disk := range oldDisks { + for _, name := range disk.DiskNames { + oldDiskMap[name] = disk.StorageSizeInGb + } + } + newDiskMap := make(map[string]int) + for _, disk := range newDisks { + for _, name := range disk.DiskNames { + newDiskMap[name] = disk.StorageSizeInGb + } + } + + // For disks present in both old and new, size must not change + for name, oldSize := range oldDiskMap { + if newSize, exists := newDiskMap[name]; exists { + if newSize != oldSize { + errs = append(errs, field.Invalid( + fieldPath, + name, + fmt.Sprintf("cannot change size of existing ASM disk %s (old: %dGB, new: %dGB)", name, oldSize, newSize), + )) + } + } + } + return errs +} + func (r *OracleRestart) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { cr, ok := obj.(*OracleRestart) if !ok { @@ -842,5 +959,60 @@ func contains(slice []string, item string) bool { } return false } +func getDeviceCount(deviceList string) int { + if deviceList == "" { + return 0 + } + devices := strings.Split(deviceList, ",") + count := 0 + for _, d := range devices { + if strings.TrimSpace(d) != "" { + count++ + } + } + return count +} + +func (r *OracleRestart) validateAsmRedundancyAndDisks( + devList, redundancy, paramField string, +) field.ErrorList { + var errs field.ErrorList + diskCount := getDeviceCount(devList) + + // Only validate if at least ONE of devList or redundancy is set/non-empty + if strings.TrimSpace(redundancy) == "" { + // Both are empty, nothing to validate + return errs + } + + switch strings.ToUpper(redundancy) { + case "EXTERNAL": + // No restrictions + return errs + case "NORMAL": + if diskCount < 2 || diskCount%2 != 0 { + errs = append(errs, field.Invalid( + field.NewPath("spec").Child("configParams").Child(paramField), + devList, + "NORMAL redundancy requires disk count in multiples of 2 (min 2)", + )) + } + case "HIGH": + if diskCount < 3 || diskCount%3 != 0 { + errs = append(errs, field.Invalid( + field.NewPath("spec").Child("configParams").Child(paramField), + devList, + "HIGH redundancy requires disk count in multiples of 3 (min 3)", + )) + } + default: + errs = append(errs, field.Invalid( + field.NewPath("spec").Child("configParams").Child(paramField), + redundancy, + "Invalid redundancy type; must be EXTERNAL, NORMAL, or HIGH", + )) + } + return errs +} // =========================== Update specs checks block ends Here ======================= diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 3252f9d2..b048d204 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -824,61 +824,92 @@ func getExternalConnStr( kubeConfig clientcmd.ClientConfig, logger logr.Logger, ) string { - // If NodePort service not defined in spec, don’t expose external conn - if len(instance.Spec.NodePortSvc.PortMappings) == 0 { + switch { + // Case 1: Neither service defined + case len(instance.Spec.NodePortSvc.PortMappings) == 0 && len(instance.Spec.LbService.PortMappings) == 0: return "" - } - // Get the dbmc1 NodePort service - svc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), instance.Spec.InstDetails.Name, metav1.GetOptions{}) - if err != nil { - msg := "Failed to get dbmc1 service" - LogMessages("DEBUG", msg, err, instance, logger) - return "Pending" - } - // Find the NodePort for port 1521 - var nodePort int32 - for _, port := range svc.Spec.Ports { - if port.Port == 1521 { - nodePort = port.NodePort - break + // Case 2: LoadBalancer service defined, try to use it + case len(instance.Spec.LbService.PortMappings) != 0: + lbServiceName := instance.Spec.LbService.SvcName + lbSvc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), lbServiceName, metav1.GetOptions{}) + lbExtIP := "" + var lbPort int32 + if err == nil && lbSvc.Spec.Type == corev1.ServiceTypeLoadBalancer { + // Extract external IP or hostname + for _, ingress := range lbSvc.Status.LoadBalancer.Ingress { + if ingress.IP != "" { + lbExtIP = ingress.IP + break + } else if ingress.Hostname != "" { + lbExtIP = ingress.Hostname + break + } + } + // Find port 1521 + for _, port := range lbSvc.Spec.Ports { + if port.Port == 1521 { + lbPort = port.Port + break + } + } } - } - if nodePort == 0 { - msg := "Failed to find NodePort for port 1521 in dbmc1 service" - LogMessages("DEBUG", msg, err, instance, logger) - return "Pending" - } + // Prefer LoadBalancer if available + if lbExtIP != "" && lbPort != 0 { + serviceName := instance.Spec.ServiceDetails.Name + return fmt.Sprintf("EXTERNAL: %s:%d/%s", lbExtIP, lbPort, serviceName) + } + // fallthrough to NodePort if LB unavailable + fallthrough - // Get the external IP of the first node in the cluster - nodeList, err := kubeClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) - if err != nil || len(nodeList.Items) == 0 { - msg := "Failed to list cluster nodes" - LogMessages("DEBUG", msg, err, instance, logger) - return "Pending" - } - var nodeIP string - for _, addr := range nodeList.Items[0].Status.Addresses { - // Try to get an ExternalIP, fallback to InternalIP if needed - if addr.Type == corev1.NodeExternalIP { - nodeIP = addr.Address - break - } else if addr.Type == corev1.NodeInternalIP && nodeIP == "" { - nodeIP = addr.Address + // Case 3: NodePort defined or fallback + default: + svc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), instance.Spec.InstDetails.Name, metav1.GetOptions{}) + if err != nil { + msg := "Failed to get dbmc1 service" + LogMessages("DEBUG", msg, err, instance, logger) + return "Pending" } - } - if nodeIP == "" { - msg := "Failed to get node IP address" - LogMessages("DEBUG", msg, err, instance, logger) - return "Pending" - } - // Replace with your actual Oracle service name if different - serviceName := instance.Spec.ServiceDetails.Name + // Find NodePort for 1521 + var nodePort int32 + for _, port := range svc.Spec.Ports { + if port.Port == 1521 { + nodePort = port.NodePort + break + } + } + if nodePort == 0 { + msg := "Failed to find NodePort for port 1521 in dbmc1 service" + LogMessages("DEBUG", msg, err, instance, logger) + return "Pending" + } + + // Get first node external/internal IP + nodeList, err := kubeClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) + if err != nil || len(nodeList.Items) == 0 { + msg := "Failed to list cluster nodes" + LogMessages("DEBUG", msg, err, instance, logger) + return "Pending" + } + var nodeIP string + for _, addr := range nodeList.Items[0].Status.Addresses { + if addr.Type == corev1.NodeExternalIP { + nodeIP = addr.Address + break + } else if addr.Type == corev1.NodeInternalIP && nodeIP == "" { + nodeIP = addr.Address + } + } + if nodeIP == "" { + msg := "Failed to get node IP address" + LogMessages("DEBUG", msg, err, instance, logger) + return "Pending" + } - // Construct the external connect string - externalConnectString := fmt.Sprintf("%s:%d/%s", nodeIP, nodePort, serviceName) - return externalConnectString + serviceName := instance.Spec.ServiceDetails.Name + return fmt.Sprintf("%s:%d/%s", nodeIP, nodePort, serviceName) + } } func getClientEtcHost(podNames []string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, nodeDetails map[string]*corev1.Node) []string { @@ -988,95 +1019,6 @@ func getGridHome(podName string, instance *oraclerestart.OracleRestart, kubeClie return strings.TrimSpace(stdoutput), nil } -// func getASMListDisks(podName string, instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger) []string { -// gridHome, err := getGridHome(podName, instance, kubeClient, kubeConfig, logger) -// if err != nil { -// msg := "Error retrieving GRID_HOME" -// LogMessages("DEBUG", msg, err, instance, logger) -// return []string{msg} -// } -// stdoutput, _, err := ExecCommand(podName, getASMListDisksCommand(gridHome), kubeClient, kubeConfig, instance, logger) -// if err != nil { -// msg := "Error retrieving ASM disks" -// LogMessages("DEBUG", msg, err, instance, logger) -// return []string{msg} -// } - -// // Split the output by new lines, remove carriage returns and filter out the header -// var disks []string -// lines := strings.Split(strings.TrimSpace(stdoutput), "\n") -// for _, line := range lines { -// // Remove any carriage returns and trim spaces -// line = strings.TrimSpace(strings.Trim(line, "\r")) -// if line == "Path" { -// continue // Skip the header -// } -// if line != "" { -// disks = append(disks, line) -// } -// } - -// return disks -// } - -// func GetCrsNodes(instance *oraclerestart.OracleRestart, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, kClient client.Client) (string, string, string, string, string) { - -// var new_crs_nodes []string -// var new_crs_nodes_list []string -// var existing_crs_nodes_healthy []string -// var existing_crs_nodes_not_healthy []string -// var install_node string -// var install_node_flag bool - -// if len(instance.Spec.InstDetails) > 0 { -// for _, OraRestartSpex := range instance.Spec.InstDetails { -// if !utils.CheckStatusFlag(OraRestartSpex.IsDelete) { -// _, err := CheckSfset(OraRestartSpex.Name, instance, kClient) -// if err != nil { -// new_crs_nodes = append(new_crs_nodes, ("pubhost:" + OraRestartSpex.Name + "-0" + "," + "viphost:" + OraRestartSpex.VipSvcName)) -// new_crs_nodes_list = append(new_crs_nodes_list, OraRestartSpex.Name+"-0") -// if !install_node_flag { -// install_node = OraRestartSpex.Name + "-0" -// install_node_flag = true -// } -// } -// } -// } -// // Updating OracleRestartNode status - -// if len(instance.Status.OracleRestartNodes) > 0 { -// for _, oraRacStatusSpex := range instance.Status.OracleRestartNodes { -// _, err := CheckSfset(strings.Split(oraRacStatusSpex.Name, "-")[0], instance, kClient) -// if err != nil { -// newRacStatus := delOracleRestartNodestatus(instance, oraRacStatusSpex.Name) -// instance.Status.OracleRestartNodes = newRacStatus -// } -// } -// // ====Updating the OracleRestartNode status block ends here==== - -// /// The loop check for healthy and non healthy nodes - -// for _, oraRacStatusSpex := range instance.Status.OracleRestartNodes { -// if oraRacStatusSpex.NodeDetails.ClusterState == "HEALTHY" { -// if !checkElem(existing_crs_nodes_healthy, oraRacStatusSpex.Name) { -// existing_crs_nodes_healthy = append(existing_crs_nodes_healthy, oraRacStatusSpex.Name) -// } -// } else { -// if !checkElem(existing_crs_nodes_not_healthy, oraRacStatusSpex.Name) { -// existing_crs_nodes_not_healthy = append(existing_crs_nodes_not_healthy, oraRacStatusSpex.Name) -// } -// } -// } -// } - -// } -// //External for loop ends here - -// // Main if condition ends here -// return strings.Join(new_crs_nodes[:], ";"), strings.Join(existing_crs_nodes_healthy, ","), strings.Join(existing_crs_nodes_not_healthy, ","), install_node, strings.Join(new_crs_nodes_list, ",") - -// } - func GetAsmDevices(instance *oraclerestart.OracleRestart) string { var asmDisk []string if instance.Spec.AsmStorageDetails != nil { diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 1d870700..6173a20a 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: orestart-operator-mltp1 + newTag: orestart-operator-sa diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 9107f310..b70f4d1a 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -387,7 +387,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques oracleRestart.Spec.InstDetails.EnvFile = cmName dep := oraclerestartcommon.BuildStatefulSetForOracleRestart(oracleRestart, oracleRestart.Spec.InstDetails, r.Client) - result, err = r.createOrReplaceSfs(ctx, req, oracleRestart, dep, index, isLast, oldState) + result, err = r.createOrReplaceSfs(ctx, req, *oracleRestart, dep, index, isLast, oldState) if err != nil { result = resultNq return result, err @@ -2547,12 +2547,16 @@ func checkDaemonSetStatus(ctx context.Context, r *OracleRestartReconciler, oracl } // ================================== CREATE FUNCTIONS ============================= -// This function create a PVC set in the yaml file -func (r *OracleRestartReconciler) createOrReplaceSfs(ctx context.Context, req ctrl.Request, oracleRestart *oraclerestartdb.OracleRestart, - dep *appsv1.StatefulSet, index int, isLast bool, oldState string, +func (r *OracleRestartReconciler) createOrReplaceSfs( + ctx context.Context, + req ctrl.Request, + oracleRestart oraclerestartdb.OracleRestart, + dep *appsv1.StatefulSet, + index int, + isLast bool, + oldState string, ) (ctrl.Result, error) { reqLogger := r.Log.WithValues("Instance.Namespace", oracleRestart.Namespace, "Instance.Name", oracleRestart.Name) - found := &appsv1.StatefulSet{} err := r.Get(ctx, types.NamespacedName{ @@ -2561,15 +2565,15 @@ func (r *OracleRestartReconciler) createOrReplaceSfs(ctx context.Context, req ct }, found) jsn, _ := json.Marshal(dep) - oraclerestartcommon.LogMessages("DEBUG", string(jsn), nil, oracleRestart, r.Log) + oraclerestartcommon.LogMessages("DEBUG", string(jsn), nil, &oracleRestart, r.Log) + if err != nil && apierrors.IsNotFound(err) { - // Create the StatefulSet - r.updateOracleRestartInstStatus(oracleRestart, ctx, req, oracleRestart.Spec.InstDetails, string(oraclerestartdb.OracleRestartProvisionState), r.Client, true) + // CREATE + r.updateOracleRestartInstStatus(&oracleRestart, ctx, req, oracleRestart.Spec.InstDetails, + string(oraclerestartdb.OracleRestartProvisionState), r.Client, true) reqLogger.Info("Creating a StatefulSet Normally", "StatefulSetName", dep.Name) err = r.Create(ctx, dep) - if err != nil { - // StatefulSet creation failed oracleRestart.Spec.IsFailed = true reqLogger.Error(err, "Failed to create StatefulSet", "StatefulSet.Namespace", dep.Namespace, "StatefulSet.Name", dep.Name) return ctrl.Result{}, err @@ -2578,13 +2582,33 @@ func (r *OracleRestartReconciler) createOrReplaceSfs(ctx context.Context, req ct return ctrl.Result{}, nil } } else if err != nil { - // Error that isn't due to the StatefulSet not existing + // Any other Get error reqLogger.Error(err, "Failed to find the StatefulSet details") return ctrl.Result{}, err + } else { + // UPDATE only if resource requirements have changed + foundRes := found.Spec.Template.Spec.Containers[0].Resources + depRes := dep.Spec.Template.Spec.Containers[0].Resources + + if !reflect.DeepEqual(foundRes, depRes) { + // Copy metadata fields that must be preserved + dep.ResourceVersion = found.ResourceVersion + dep.UID = found.UID + dep.CreationTimestamp = found.CreationTimestamp + dep.ManagedFields = found.ManagedFields + dep.Status = found.Status + + reqLogger.Info("Updating StatefulSet due to resource change", "StatefulSetName", dep.Name) + err = r.Update(ctx, dep) + if err != nil { + oracleRestart.Spec.IsFailed = true + reqLogger.Error(err, "Failed to update StatefulSet", "StatefulSet.Namespace", dep.Namespace, "StatefulSet.Name", dep.Name) + return ctrl.Result{}, err + } + } } return ctrl.Result{}, nil - } // ================================== CREATE FUNCTIONS ============================= diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml index 9cf349e2..3440480f 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml @@ -59,7 +59,7 @@ spec: # Image for Oracle Restart database container # image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0703 - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0723 # Latest image to use + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim # Latest image to use imagePullPolicy: Always # Always pull the image when starting the pod # Optional: ServiceAccount for RBAC and security policies (mandatory in OpenShift) @@ -100,6 +100,7 @@ spec: inventory: "/u01/app/oraInventory" # Path to oraInventory gridSwZipFile: "grid_home.zip" # Grid software ZIP file dbSwZipFile: "db_home.zip" # Database software ZIP file + oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" sgaSize: "3G" # Size of SGA memory pgaSize: "1G" # Size of PGA memory processes: 2000 # Number of Oracle processes @@ -111,3 +112,5 @@ spec: # Directory containing the unzipped RU patch (includes PatchSearch.xml) ruPatchLocation: /scratch/software/ru_patch #<--- RU patch unzipped folder + + oPatchLocation: "/scratch/software/19c/19.28" diff --git a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md index dc2fdcf7..ccc7cda1 100644 --- a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md @@ -97,6 +97,7 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will net.core.wmem_default = 262144 net.core.wmem_max = 1048576 fs.aio-max-nr = 1048576 + vm.nr_hugepages=16384 ``` 3. Run the following commands: * `# sysctl -a` diff --git a/go.mod b/go.mod index 5b8720a5..dc3d8655 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,6 @@ require ( github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/pprof v0.0.0-20250903194437-c28834ac2320 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect @@ -78,7 +77,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.23.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -118,6 +116,5 @@ require ( sigs.k8s.io/kustomize/api v0.20.1 // indirect sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) diff --git a/go.sum b/go.sum index d7213b43..86aff3a6 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= -github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= @@ -14,21 +12,15 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= -github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chai2010/gettext-go v1.0.3 h1:9liNh8t+u26xl5ddmWLmsOsdNLwkdRTg5AG+JnTiM80= github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= @@ -39,34 +31,20 @@ github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2 github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= -github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonpointer v0.22.0 h1:TmMhghgNef9YXxTu1tOopo+0BGEytxA+okbry0HjZsM= github.com/go-openapi/jsonpointer v0.22.0/go.mod h1:xt3jV88UtExdIkkL7NloURjRQjbeUgcxFblMjq2iaiU= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/jsonreference v0.21.1 h1:bSKrcl8819zKiOgxkbVNRUBIr6Wwj9KYrDbMjRs0cDA= github.com/go-openapi/jsonreference v0.21.1/go.mod h1:PWs8rO4xxTUqKGu+lEvvCxD5k2X7QYkKAepJyCmSTT8= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/swag v0.24.1 h1:DPdYTZKo6AQCRqzwr/kGkxJzHhpKxZ9i/oX0zag+MF8= github.com/go-openapi/swag v0.24.1/go.mod h1:sm8I3lCPlspsBBwUm1t5oZeWZS0s7m/A+Psg0ooRU0A= github.com/go-openapi/swag/cmdutils v0.24.0 h1:KlRCffHwXFI6E5MV9n8o8zBRElpY4uK4yWyAMWETo9I= @@ -93,7 +71,6 @@ github.com/go-openapi/swag/yamlutils v0.24.0 h1:bhw4894A7Iw6ne+639hsBNRHg9iZg/IS github.com/go-openapi/swag/yamlutils v0.24.0/go.mod h1:DpKv5aYuaGm/sULePoeiG8uwMpZSfReo1HR3Ik0yaG8= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= @@ -101,23 +78,16 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/pprof v0.0.0-20250903194437-c28834ac2320 h1:c7ayAhbRP9HnEl/hg/WQOM9s0snWztfW6feWXZbGHw0= github.com/google/pprof v0.0.0-20250903194437-c28834ac2320/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= @@ -136,11 +106,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -149,22 +116,17 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= @@ -174,16 +136,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= -github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/ginkgo/v2 v2.25.2 h1:hepmgwx1D+llZleKQDMEvy8vIlCxMGt7W5ZxDjIEhsw= github.com/onsi/ginkgo/v2 v2.25.2/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/oracle/oci-go-sdk/v65 v65.95.0 h1:fI+/mfJOS2DkQ+/AFSyJAfn1XFR4TTGm2AhN6xbsi00= -github.com/oracle/oci-go-sdk/v65 v65.95.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= github.com/oracle/oci-go-sdk/v65 v65.99.2 h1:W1HzD28r+xqqT57FpH7ymOFE3BM/p4J/Ia1SfDf55nM= github.com/oracle/oci-go-sdk/v65 v65.99.2/go.mod h1:RGiXfpDDmRRlLtqlStTzeBjjdUNXyqm3KXKyLCm3A/Q= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -194,24 +150,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0 h1:j9Ce3W6X6Tzi0QnSap+YzGwpqJLJGP/7xV6P9f86jjM= -github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0/go.mod h1:sSxwdmprUfmRfTknPc4KIjUd2ZIc/kirw4UdXNhOauM= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.85.0 h1:oY+F5FZFmCjCyzkHWPjVQpzvnvEB/0FP+iyzDUUlqFc= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.85.0/go.mod h1:VB7wtBmDT6W2RJHzsvPZlBId+EnmeQA0d33fFTXvraM= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY= github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= @@ -220,16 +166,11 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ= github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -243,11 +184,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= @@ -267,8 +206,6 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= -go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= -go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -280,8 +217,6 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -302,12 +237,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -336,8 +267,6 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= @@ -350,8 +279,6 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -363,12 +290,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -379,27 +302,19 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -409,73 +324,39 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= -k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE= k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug= -k8s.io/apiextensions-apiserver v0.33.1 h1:N7ccbSlRN6I2QBcXevB73PixX2dQNIW0ZRuguEE91zI= -k8s.io/apiextensions-apiserver v0.33.1/go.mod h1:uNQ52z1A1Gu75QSa+pFK5bcXc4hq7lpOXbweZgi4dqA= k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc= k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0= -k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= -k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0= k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/cli-runtime v0.33.3 h1:Dgy4vPjNIu8LMJBSvs8W0LcdV0PX/8aGG1DA1W8lklA= -k8s.io/cli-runtime v0.33.3/go.mod h1:yklhLklD4vLS8HNGgC9wGiuHWze4g7x6XQZ+8edsKEo= k8s.io/cli-runtime v0.34.0 h1:N2/rUlJg6TMEBgtQ3SDRJwa8XyKUizwjlOknT1mB2Cw= k8s.io/cli-runtime v0.34.0/go.mod h1:t/skRecS73Piv+J+FmWIQA2N2/rDjdYSQzEE67LUUs8= -k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= -k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo= k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY= -k8s.io/component-base v0.33.3 h1:mlAuyJqyPlKZM7FyaoM/LcunZaaY353RXiOd2+B5tGA= -k8s.io/component-base v0.33.3/go.mod h1:ktBVsBzkI3imDuxYXmVxZ2zxJnYTZ4HAsVj9iF09qp4= k8s.io/component-base v0.34.0 h1:bS8Ua3zlJzapklsB1dZgjEJuJEeHjj8yTu1gxE2zQX8= k8s.io/component-base v0.34.0/go.mod h1:RSCqUdvIjjrEm81epPcjQ/DS+49fADvGSCkIP3IC6vg= -k8s.io/component-helpers v0.33.3 h1:fjWVORSQfI0WKzPeIFSju/gMD9sybwXBJ7oPbqQu6eM= -k8s.io/component-helpers v0.33.3/go.mod h1:7iwv+Y9Guw6X4RrnNQOyQlXcvJrVjPveHVqUA5dm31c= k8s.io/component-helpers v0.34.0 h1:5T7P9XGMoUy1JDNKzHf0p/upYbeUf8ZaSf9jbx0QlIo= k8s.io/component-helpers v0.34.0/go.mod h1:kaOyl5tdtnymriYcVZg4uwDBe2d1wlIpXyDkt6sVnt4= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f h1:wyRlmLgBSXi3kgawro8klrMRljXeRo1HFkQRs+meYfs= k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= -k8s.io/kubectl v0.33.3 h1:r/phHvH1iU7gO/l7tTjQk2K01ER7/OAJi8uFHHyWSac= -k8s.io/kubectl v0.33.3/go.mod h1:euj2bG56L6kUGOE/ckZbCoudPwuj4Kud7BR0GzyNiT0= k8s.io/kubectl v0.34.0 h1:NcXz4TPTaUwhiX4LU+6r6udrlm0NsVnSkP3R9t0dmxs= k8s.io/kubectl v0.34.0/go.mod h1:bmd0W5i+HuG7/p5sqicr0Li0rR2iIhXL0oUyLF3OjR4= -k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= -k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0= k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= sigs.k8s.io/controller-runtime v0.22.0 h1:mTOfibb8Hxwpx3xEkR56i7xSjB+nH4hZG37SrlCY5e0= sigs.k8s.io/controller-runtime v0.22.0/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ= -sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o= sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM= -sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA= -sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY= sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78= sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= -sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ= -sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 9dddde5e..efa48922 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -16127,7 +16127,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-mltp1 + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-sa imagePullPolicy: Always name: manager ports: From 9c04241fa4b28dcfb294a7c5a3858b4e67a3fcd2 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Fri, 5 Sep 2025 19:29:59 +0000 Subject: [PATCH 310/414] Added RuMode --- apis/database/v4/shardingdatabase_types.go | 35 +++++++++++----------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/apis/database/v4/shardingdatabase_types.go b/apis/database/v4/shardingdatabase_types.go index f42b9bbe..3b33f64a 100644 --- a/apis/database/v4/shardingdatabase_types.go +++ b/apis/database/v4/shardingdatabase_types.go @@ -95,8 +95,8 @@ type ShardingDatabaseSpec struct { TdeWalletPvcMountLocation string `json:"tdeWalletPvcMountLocation,omitempty"` DbEdition string `json:"dbEdition,omitempty"` TopicId string `json:"topicId,omitempty"` - SrvAccountName string `json:"serviceAccountName,omitempty"` - ShardInfo []ShardingDetails `json:"shardInfo,omitempty"` + SrvAccountName string `json:"serviceAccountName,omitempty"` + ShardInfo []ShardingDetails `json:"shardInfo,omitempty"` } // To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 @@ -271,6 +271,7 @@ type GsmServiceSpec struct { TableFamily string `json:"tableFamily,omitempty"` Retention string `json:"retention,omitempty"` TfaPolicy string `json:"tfaPolicy,omitempty"` + RuMode string `json:"ruMode,omitempty"` } // Secret Details @@ -316,25 +317,25 @@ type ConfigMapData struct { // Shard structures based on managed Replicas type ShardingDetails struct { - ShardPreFixName string `json:"shardPreFixName"` - Shape string `json:"shape,omitempty"` - Replicas int32 `json:"replicas,omitempty"` - StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` - ShardGroupDetails *ShardGroup `json:"shardGroupDetails,omitempty"` - ShardSpaceDetails *ShardSpace `json:"shardSpaceDetails,omitempty"` + ShardPreFixName string `json:"shardPreFixName"` + Shape string `json:"shape,omitempty"` + Replicas int32 `json:"replicas,omitempty"` + StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` + ShardGroupDetails *ShardGroup `json:"shardGroupDetails,omitempty"` + ShardSpaceDetails *ShardSpace `json:"shardSpaceDetails,omitempty"` } type ShardGroup struct { - ShardGroupName string `json:"shardGroupName,omitempty"` - Region string `json:"region,omitempty"` - RepFactor int `json:"repFactor,omitempty"` - ShardSpace string `json:"ShardSpace,omitempty"` - DeployAs string `json:"deployAs,omitempty"` - IsDelete string `json:"isDelete,omitempty"` + ShardGroupName string `json:"shardGroupName,omitempty"` + Region string `json:"region,omitempty"` + RepFactor int `json:"repFactor,omitempty"` + ShardSpace string `json:"ShardSpace,omitempty"` + DeployAs string `json:"deployAs,omitempty"` + IsDelete string `json:"isDelete,omitempty"` } type ShardSpace struct { - ShardSpaceName string `json:"shardSpaceName,omitempty"` - Chunks int `json:"Chnuks,omitempty"` - ProtectMode string `json:"protectMode,omitempty"` //Possible Values MAXPROTECTION, MAXAVAILABILITY,MAXPERFORMANCE + ShardSpaceName string `json:"shardSpaceName,omitempty"` + Chunks int `json:"Chnuks,omitempty"` + ProtectMode string `json:"protectMode,omitempty"` //Possible Values MAXPROTECTION, MAXAVAILABILITY,MAXPERFORMANCE } type ShardStatusMapKeys string From 25ca217d8cffde5fb0860701fe0d14fd9a3b6cbc Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Fri, 5 Sep 2025 19:45:08 +0000 Subject: [PATCH 311/414] Added service_mode in sharding for thr bug#38391854 --- commons/sharding/scommon.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index a2ddfa7b..6e0a587b 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -238,10 +238,10 @@ func buildEnvVarsSpec(instance *databasev4.ShardingDatabase, variables []databas result = append(result, corev1.EnvVar{Name: "KEY_SECRET_VOLUME", Value: oraSecretMount}) } - if checkTdeWalletFlag(instance) { + if checkTdeWalletFlag(instance) { result = append(result, corev1.EnvVar{Name: "TDE_PWD_KEY", Value: instance.Spec.DbSecret.TdeKeyFileName}) result = append(result, corev1.EnvVar{Name: "TDE_PWD_FILE", Value: instance.Spec.DbSecret.TdePwdFileName}) - } + } if restype == "GSM" { if !sDirectParam { @@ -283,11 +283,14 @@ func buildEnvVarsSpec(instance *databasev4.ShardingDatabase, variables []databas if len(instance.Spec.GsmService) > 0 { svc = "" for i := 0; i < len(instance.Spec.GsmService); i++ { - svc = svc + "service_name=" + instance.Spec.GsmService[i].Name + ";" + svc = svc + "service_name=" + instance.Spec.GsmService[i].Name if len(instance.Spec.GsmService[i].Role) != 0 { - svc = svc + "service_role=" + instance.Spec.GsmService[i].Role + svc = svc + ";service_role=" + instance.Spec.GsmService[i].Role } else { - svc = svc + "service_role=primary" + svc = svc + ";service_role=primary" + } + if len(instance.Spec.GsmService[i].RuMode) != 0 { + svc = svc + ";service_mode=" + instance.Spec.GsmService[i].Role } result = append(result, corev1.EnvVar{Name: "SERVICE" + fmt.Sprint(i) + "_PARAMS", Value: svc}) svc = "" @@ -878,11 +881,11 @@ func buildDirectorParams(instance *databasev4.ShardingDatabase, oraGsmSpex datab for _, variable := range variables { if variable.Name == "DIRECTOR_NAME" { dnameFlag = true - dname = variable.Value + dname = variable.Value } if variable.Name == "DIRECTOR_PORT" { dportFlag = true - dport = variable.Value + dport = variable.Value } } if !dnameFlag { @@ -891,7 +894,7 @@ func buildDirectorParams(instance *databasev4.ShardingDatabase, oraGsmSpex datab } else { varinfo = "director_name=" + dname + ";" result = result + varinfo - } + } if oraGsmSpex.Region != "" { varinfo = "director_region=" + oraGsmSpex.Region + ";" @@ -916,7 +919,7 @@ func buildDirectorParams(instance *databasev4.ShardingDatabase, oraGsmSpex datab } else { varinfo = "director_port=" + dport result = result + varinfo - } + } result = strings.TrimSuffix(result, ";") return result From e863eb9ef2cbfc3432a33024088e6ccc2d5ad41b Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Fri, 5 Sep 2025 20:04:14 +0000 Subject: [PATCH 312/414] Added fix for diskcount check for normal redudancy and HIGH redudancy --- apis/database/v4/oraclerestart_webhook.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index c1760db0..526d69a3 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -987,22 +987,27 @@ func (r *OracleRestart) validateAsmRedundancyAndDisks( switch strings.ToUpper(redundancy) { case "EXTERNAL": - // No restrictions - return errs + if diskCount < 1 { + errs = append(errs, field.Invalid( + field.NewPath("spec").Child("configParams").Child(paramField), + devList, + "EXTERNAL redundancy requires disk count minimum 1", + )) + } case "NORMAL": - if diskCount < 2 || diskCount%2 != 0 { + if diskCount < 2 { errs = append(errs, field.Invalid( field.NewPath("spec").Child("configParams").Child(paramField), devList, - "NORMAL redundancy requires disk count in multiples of 2 (min 2)", + "NORMAL redundancy requires disk count minimum 2", )) } case "HIGH": - if diskCount < 3 || diskCount%3 != 0 { + if diskCount < 3 { errs = append(errs, field.Invalid( field.NewPath("spec").Child("configParams").Child(paramField), devList, - "HIGH redundancy requires disk count in multiples of 3 (min 3)", + "HIGH redundancy requires disk count minimum 3", )) } default: From b0ec305b39314255a34c35e4fa6fcc7d4e88c8bd Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 8 Sep 2025 01:23:54 +0000 Subject: [PATCH 313/414] Added fixes --- apis/database/v4/oraclerestart_webhook.go | 22 ++++++ commons/oraclerestart/oraclerestartcommon.go | 3 + commons/oraclerestart/oraclerestartprov.go | 2 +- .../database/oraclerestart_controller.go | 71 +++++++++++++++---- 4 files changed, 83 insertions(+), 15 deletions(-) diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index 526d69a3..b3d6f147 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -251,6 +251,20 @@ func (r *OracleRestart) ValidateUpdate(ctx context.Context, oldObj, newObj runti } } + if old.Spec.StorageClass != newCr.Spec.StorageClass { + + return nil, apierrors.NewForbidden( + schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, + newCr.Name, fmt.Errorf("updates to the storageclass is forbidden: %s", old.Spec.StorageClass)) + } + + if old.Spec.InstDetails.SwLocStorageSizeInGb < newCr.Spec.InstDetails.SwLocStorageSizeInGb { + + return nil, apierrors.NewForbidden( + schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, + newCr.Name, fmt.Errorf("SwLocStorageSizeInGb Storage size shrink is not allowed. Old value : %d and New value: %d. ", old.Spec.InstDetails.SwLocStorageSizeInGb, newCr.Spec.InstDetails.SwLocStorageSizeInGb)) + } + isDiskChanged := !reflect.DeepEqual(old.Spec.AsmStorageDetails.DisksBySize, newCr.Spec.AsmStorageDetails.DisksBySize) if isDiskChanged { if old.Spec.ConfigParams.HostSwStageLocation != newCr.Spec.ConfigParams.HostSwStageLocation || @@ -557,6 +571,14 @@ func (r *OracleRestart) validateGeneric() field.ErrorList { } } + if r.Spec.StorageClass != "" { + if r.Spec.InstDetails.SwLocStorageSizeInGb < 60 { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("InstDetails").Child("SwLocStorageSizeInGb"), r.Spec.InstDetails.SwLocStorageSizeInGb, + "SwLocStorageSizeInGb must be greater than 60GB")) + } + } + if r.Spec.ConfigParams == nil { validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("ConfigParams"), r.Spec.ConfigParams, diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index b048d204..92c02d78 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -1366,3 +1366,6 @@ func GetHealthyNodeCounts(instance *oraclerestart.OracleRestart) (int, error) { } return 0, fmt.Errorf("healthy cluster node counts are not matching with total cluster nodes") } +func GetSwPvcName(name string) string { + return name + "-oradata-sw-vol-pvc" +} diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 042fc46f..be483476 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -1006,7 +1006,7 @@ func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.Oracl func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) corev1.PersistentVolumeClaim { // If user-provided PVC name exists, skip volume claim template creation - pvcName := OracleRestartSpex.Name + "-oradata-sw-vol-pvc" + pvcName := GetSwPvcName(OracleRestartSpex.Name) return corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Name: pvcName, diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index b70f4d1a..6f23e850 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -53,11 +53,13 @@ import ( oraclerestartdb "github.com/oracle/oracle-database-operator/apis/database/v4" oraclerestartcommon "github.com/oracle/oracle-database-operator/commons/oraclerestart" utils "github.com/oracle/oracle-database-operator/commons/oraclerestart/utils" + "github.com/prometheus/common/log" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -502,6 +504,11 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } } + err = r.expandStorageClassSWVolume(ctx, oracleRestart, oldSpec) + if err != nil { + return ctrl.Result{}, err + } + completed = true // // Update the current spec after successful reconciliation if err = r.SetCurrentSpec(ctx, oracleRestart, req); err != nil { @@ -3180,13 +3187,11 @@ func (r *OracleRestartReconciler) deleteOracleRestartInst(OraRestartSpex oracler // delete steps that the operator needs to do before the CR can be deleted. //var i int32 - var nodeCount int var err error var cmName string - var healthyNode string - nodeCount, err = oraclerestartcommon.GetHealthyNodeCounts(oracleRestart) - healthyNode, err = oraclerestartcommon.GetHealthyNode(oracleRestart) + //nodeCount, err = oraclerestartcommon.GetHealthyNodeCounts(oracleRestart) + //healthyNode, err = oraclerestartcommon.GetHealthyNode(oracleRestart) if err != nil { return fmt.Errorf("no healthy node found in the cluster to perform delete node operator. manual intervention required") } @@ -3266,16 +3271,6 @@ func (r *OracleRestartReconciler) deleteOracleRestartInst(OraRestartSpex oracler } } - // This block will execute only if asm cardinailty is set to 3. - if nodeCount == 3 { - err = oraclerestartcommon.UpdateAsmCount(oracleRestart.Spec.ConfigParams.GridHome, healthyNode, oracleRestart, r.kubeClient, r.kubeConfig, r.Log) - if err != nil { - log.Info("erorr occurred while updating the asm count") - } else { - log.Info("Updated the asm cardinality successfully") - } - } - log.Info("Successfully cleaned up OracleRestartInstance") return nil } @@ -3424,3 +3419,51 @@ func (r *OracleRestartReconciler) updateONS(ctx context.Context, podList *corev1 return nil } +func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context, instance *oraclerestartdb.OracleRestart, oldSpec *oraclerestartdb.OracleRestartSpec) error { + + if oldSpec != nil { + if instance.Spec.InstDetails.SwLocStorageSizeInGb > oldSpec.InstDetails.SwLocStorageSizeInGb { + storageClass := &storagev1.StorageClass{} + pvc := &corev1.PersistentVolumeClaim{} + + if instance.Spec.StorageClass != "" { + + err := r.Get(ctx, types.NamespacedName{Name: instance.Spec.StorageClass}, storageClass) + if err != nil { + return fmt.Errorf("error while fetching the storage class") + } + + err = r.Get(ctx, types.NamespacedName{ + Name: oraclerestartcommon.GetSwPvcName(instance.Name), + Namespace: instance.Namespace, + }, pvc) + + if err == nil { + if storageClass.AllowVolumeExpansion == nil || !*storageClass.AllowVolumeExpansion { + r.Recorder.Eventf(instance, corev1.EventTypeWarning, "PVC not resizable", "The storage class doesn't support volume expansion") + return fmt.Errorf("the storage class %s doesn't support volume expansion", instance.Spec.StorageClass) + } + + newPVCSize := resource.MustParse(strconv.Itoa(instance.Spec.InstDetails.SwLocStorageSizeInGb) + "Gi") + newPVCSizeAdd := &newPVCSize + + if newPVCSizeAdd.Cmp(pvc.Spec.Resources.Requests["storage"]) < 0 { + r.Recorder.Eventf(instance, corev1.EventTypeWarning, "Cannot Resize PVC", "Forbidden: field can not be less than previous value") + return fmt.Errorf("Resizing PVC to lower size volume not allowed") + } + + pvc.Spec.Resources.Requests["storage"] = resource.MustParse(strconv.Itoa(instance.Spec.InstDetails.SwLocStorageSizeInGb) + "Gi") + log.Info("Updating PVC", "pvc", pvc.Name, "volume", pvc.Spec.VolumeName) + r.Recorder.Eventf(instance, corev1.EventTypeNormal, "Updating PVC - volume expansion", "Resizing the pvc for storage expansion") + err = r.Update(ctx, pvc) + if err != nil { + log.Error(err, "Error while updating the PVCs") + return fmt.Errorf("error while updating the PVCs") + } + + } + } + } + } + return nil +} From acea82d5c7867927ed48c029cc2cefa64698e4c2 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 8 Sep 2025 02:27:10 +0000 Subject: [PATCH 314/414] Added fixes --- controllers/database/oraclerestart_controller.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 6f23e850..05ed2098 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -53,7 +53,6 @@ import ( oraclerestartdb "github.com/oracle/oracle-database-operator/apis/database/v4" oraclerestartcommon "github.com/oracle/oracle-database-operator/commons/oraclerestart" utils "github.com/oracle/oracle-database-operator/commons/oraclerestart/utils" - "github.com/prometheus/common/log" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" @@ -3453,11 +3452,10 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context } pvc.Spec.Resources.Requests["storage"] = resource.MustParse(strconv.Itoa(instance.Spec.InstDetails.SwLocStorageSizeInGb) + "Gi") - log.Info("Updating PVC", "pvc", pvc.Name, "volume", pvc.Spec.VolumeName) + fmt.Printf("Updating PVC", "pvc", pvc.Name, "volume", pvc.Spec.VolumeName) r.Recorder.Eventf(instance, corev1.EventTypeNormal, "Updating PVC - volume expansion", "Resizing the pvc for storage expansion") err = r.Update(ctx, pvc) if err != nil { - log.Error(err, "Error while updating the PVCs") return fmt.Errorf("error while updating the PVCs") } From 92332450d27ec49e3989bbdcd28dc26a44e7f8c2 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 8 Sep 2025 02:36:14 +0000 Subject: [PATCH 315/414] Added fixes --- commons/sharding/scommon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 6e0a587b..f284e5c6 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -290,7 +290,7 @@ func buildEnvVarsSpec(instance *databasev4.ShardingDatabase, variables []databas svc = svc + ";service_role=primary" } if len(instance.Spec.GsmService[i].RuMode) != 0 { - svc = svc + ";service_mode=" + instance.Spec.GsmService[i].Role + svc = svc + ";service_mode=" + instance.Spec.GsmService[i].RuMode } result = append(result, corev1.EnvVar{Name: "SERVICE" + fmt.Sprint(i) + "_PARAMS", Value: svc}) svc = "" From da197ba09700019222ea1d05c7a9b4a59c1b4e6e Mon Sep 17 00:00:00 2001 From: racpack Date: Mon, 8 Sep 2025 03:12:28 +0000 Subject: [PATCH 316/414] Update file README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 936f8c37..960133b8 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,7 @@ In this v2.0 production release, `OraOperator` supports the following database c * **Oracle Database Observability** * **Oracle Database Rest Service (ORDS) instances** * **Oracle Restart** -* **Oracle Private AI Controller** -* **Oracle Sharding** +* **Oracle Globally Distributed Database** --- @@ -37,6 +36,7 @@ In this v2.0 production release, `OraOperator` supports the following database c - Snapshot Standby support in Data Guard setup * **Globally Distributed Database** - Support for Oracle Database 23ai Raft replication + - Oracle Database 23ai Free support * **Autonomous Database** - Support for Database manual failover and switchover * **Multitenant DB** @@ -56,7 +56,6 @@ In this v2.0 production release, `OraOperator` supports the following database c - Patching and Upgrade * **Oracle Restart** - Support for Oracle Database 19c -* **Oracle Private AI** --- @@ -81,8 +80,7 @@ As of v2.0.0, the Oracle Database Operator for Kubernetes (`OraOperator`) suppor * **Oracle Base Database Service (OBDS)**: Provision, scale shape/storage, terminate/update license, cloning, PDB creation, using KMS Vaults, backup/restore, Data Guard setup, patching/upgrade * **Oracle Data Guard**: Provision standby for SIDB, create Data Guard configuration, perform switchover, patch primary/standby * **Oracle Database Observability**: Create, patch, delete `databaseObserver` (logs and metrics) -* **Oracle Restart**: Provision, add & delete asm disks, enable ons ports, custom storage class support, existing pvc support -* **Oracle Private AI** +* **Oracle Restart**: Provision, add & delete asm disks, enable ons ports, custom storage class support, existing pvc support, load balancer support * Watch namespaces using the `WATCH_NAMESPACE` environment variable @@ -93,13 +91,14 @@ This production release has been installed and tested on: The previous releases were tested on the following platforms: -* [Oracle Container Engine for Kubernetes (OKE)](https://www.oracle.com/cloud-native/container-engine-kubernetes/) with Kubernetes 1.24 -* [Oracle Linux Cloud Native Environment(OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) 1.6 +* [Oracle Container Engine for Kubernetes (OKE)](https://www.oracle.com/cloud-native/container-engine-kubernetes/) with Kubernetes 1.30 or later +* [Oracle Linux Cloud Native Environment(OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) 1.9 or later * [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) * [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/) * [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) * [Red Hat OKD](https://www.okd.io/) * [Minikube](https://minikube.sigs.k8s.io/docs/) with version v1.29.0 +* [Redhat Openshift](https://www.redhat.com/en/technologies/cloud-computing/openshift) with version v4.16 or later ## Prerequisites @@ -295,6 +294,7 @@ YAML file templates are available under [`/config/samples`](./config/samples/). * [Oracle Globally Distributed Database](https://docs.oracle.com/en/database/oracle/oracle-database/21/shard/index.html) * [Oracle Database Cloud Service](https://docs.oracle.com/en/database/database-cloud-services.html) + ## Contributing This project welcomes contributions from the community. Before submitting a pull request, please [review our contribution guide](./CONTRIBUTING.md) From 59fa1f5546b2ca3f805642942395e789a626e29c Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 8 Sep 2025 04:49:41 +0000 Subject: [PATCH 317/414] Added fixes --- apis/omlai/v4/groupversion_info.go | 58 -- apis/omlai/v4/privateai_types.go | 211 ------ apis/omlai/v4/privateai_webhook.go | 213 ------ apis/omlai/v4/privateai_webhook_test.go | 107 --- apis/omlai/v4/webhook_suite_test.go | 184 ------ apis/omlai/v4/zz_generated.deepcopy.go | 322 --------- .../bases/omlai.oracle.com_privateais.yaml | 305 --------- config/crd/kustomization.yaml | 1 - config/rbac/omlai_privateai_admin_role.yaml | 27 - config/rbac/omlai_privateai_editor_role.yaml | 33 - config/rbac/omlai_privateai_viewer_role.yaml | 29 - config/samples/omlai_v4_privateai.yaml | 9 - controllers/omlai/privateai_controller.go | 623 ------------------ .../omlai/privateai_controller_test.go | 106 --- controllers/omlai/suite_test.go | 138 ---- main.go | 19 - 16 files changed, 2385 deletions(-) delete mode 100644 apis/omlai/v4/groupversion_info.go delete mode 100644 apis/omlai/v4/privateai_types.go delete mode 100644 apis/omlai/v4/privateai_webhook.go delete mode 100644 apis/omlai/v4/privateai_webhook_test.go delete mode 100644 apis/omlai/v4/webhook_suite_test.go delete mode 100644 apis/omlai/v4/zz_generated.deepcopy.go delete mode 100644 config/crd/bases/omlai.oracle.com_privateais.yaml delete mode 100644 config/rbac/omlai_privateai_admin_role.yaml delete mode 100644 config/rbac/omlai_privateai_editor_role.yaml delete mode 100644 config/rbac/omlai_privateai_viewer_role.yaml delete mode 100644 config/samples/omlai_v4_privateai.yaml delete mode 100644 controllers/omlai/privateai_controller.go delete mode 100644 controllers/omlai/privateai_controller_test.go delete mode 100644 controllers/omlai/suite_test.go diff --git a/apis/omlai/v4/groupversion_info.go b/apis/omlai/v4/groupversion_info.go deleted file mode 100644 index 1ea9b97b..00000000 --- a/apis/omlai/v4/groupversion_info.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -// Package v4 contains API Schema definitions for the omlai v4 API group. -// +kubebuilder:object:generate=true -// +groupName=omlai.oracle.com -package v4 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // GroupVersion is group version used to register these objects. - GroupVersion = schema.GroupVersion{Group: "omlai.oracle.com", Version: "v4"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme. - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} - - // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme -) diff --git a/apis/omlai/v4/privateai_types.go b/apis/omlai/v4/privateai_types.go deleted file mode 100644 index 358a2943..00000000 --- a/apis/omlai/v4/privateai_types.go +++ /dev/null @@ -1,211 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// PrivateAiSpec defines the desired state of PrivateAi. -type PrivateAiSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - PaiConfigFile *PaiConfigMap `json:"paiConfigFile,omitempty"` - // +kubebuilder:default=true - PaiEnableAuthentication bool `json:"paiEnableAuthentication,omitempty"` - PaiSecret *PaiSecretSpec `json:"paiSecret,omitempty"` - IsExternalSvc bool `json:"isExternalSvc,omitempty"` - StorageClass string `json:"storageClass,omitempty"` - PvcList map[string]string `json:"pvcList,omitempty"` - PaiImage string `json:"paiImage,omitempty"` - PaiImagePullSecret string `json:"paiImagePullSecret,omitempty"` - IsDebug bool `json:"isDebug,omitempty"` - ReadinessCheckPeriod int `json:"readinessCheckPeriod,omitempty"` - LivenessCheckPeriod int `json:"livenessCheckPeriod,omitempty"` - IsDownloadScripts bool `json:"isDownloadScripts,omitempty"` - PaiService PaiServiceSpec `json:"paiService,omitempty"` - StorageSizeInGb int32 `json:"storageSizeInGb,omitempty"` - EnvVars []EnvironmentVariable `json:"envVars,omitempty"` - Replicas int32 `json:"replicas,omitempty"` - Resources *corev1.ResourceRequirements `json:"resources,omitempty"` - NodePortSvc []PaiNodePortSvc `json:"nodePortSvc,omitempty"` - PortMappings []PaiPortMapping `json:"portMappings,omitempty"` - IsDeleteOraPvc bool `json:"isDeleteOraPvc,omitempty"` - PaiLogLocation string `json:"paiLogLocation,omitempty"` - PaiHTTPEnabled bool `json:"paiHTTPEnabled,omitempty"` - PaiHTTPSEnabled bool `json:"paiHTTPSEnabled,omitempty"` - PaiHTTPPort int32 `json:"paiHTTPPort,omitempty"` - PaiHTTPSPort int32 `json:"paiHTTPSPort,omitempty"` - PaiAuthentication bool `json:"paiAuthentication,omitempty"` - PaiLBPort int32 `json:"paiLBPort,omitempty"` - PaiLBIP string `json:"paiLBIP,omitempty"` - PaiInternalLB bool `json:"paiInternalLB,omitempty"` - PailbAnnotation map[string]string `json:"pailbAnnotation,omitempty"` -} - -// Secret Details -type PaiSecretSpec struct { - Name string `json:"name,omitempty"` - MountLocation string `json:"mountLocation,omitempty"` -} - -// Env Variable -type EnvironmentVariable struct { - Name string `json:"name"` // Name of the variable. Must be a C_IDENTIFIER. - Value string `json:"value"` // Value of the variable, as defined in Kubernetes core API. -} - -// Service Spec -type PaiServiceSpec struct { - PortMappings []PaiPortMapping `json:"portMappings,omitempty"` // Port mappings for the service that is created - SvcName string `json:"name,omitempty"` - SvcType string `json:"svcType,omitempty"` -} - -// Config Map -type PaiConfigMap struct { - Name string `json:"name,omitempty"` - MountLocation string `json:"mountLocation,omitempty"` -} - -// Node Port Svc -type PaiNodePortSvc struct { - PortMappings []PaiPortMapping `json:"portMappings,omitempty"` // Port mappings for the service - SvcName string `json:"name,omitempty"` - SvcType string `json:"svcType,omitempty"` -} - -// Port Mapping -type PaiPortMapping struct { - Port int32 `json:"port"` - TargetPort int32 `json:"targetPort"` // Docker image port for the application - Protocol corev1.Protocol `json:"protocol"` // IP protocol for the mapping, e.g., "TCP" or "UDP" - NodePort int32 `json:"nodePort,omitempty"` -} - -// PrivateAiStatus defines the observed state of PrivateAi. -type PrivateAiStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - Status string `json:"status,omitempty"` - Replicas int `json:"replicas,omitempty"` - ReleaseUpdate string `json:"releaseUpdate,omitempty"` - ApiKey string `json:"apiKey,omitempty"` - Certpem string `json:"certpem,omitempty"` - LoadBalancerIP string `json:"loadBalancerIP,omitempty"` - PodIP string `json:"podIP,omitempty"` - NodeIP string `json:"NodeIP,omitempty"` - ClusterIP string `json:"clusterIP,omitempty"` - // +patchMergeKey=type - // +patchStrategy=merge - // +listType=map - // +listMapKey=type - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -//+kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string -//+kubebuilder:printcolumn:JSONPath=".status.replicas",name="Replicas",type=number -//+kubebuilder:printcolumn:JSONPath=".status.apikey",name="ApiKey",type=string -//+kubebuilder:printcolumn:JSONPath=".status.podip",name="PodIP",type=string -//+kubebuilder:printcolumn:JSONPath=".status.loadbalancerip",name="LbIP",type=string -//+kubebuilder:printcolumn:JSONPath=".status.ReleaseUpdate",name="ReleaseUpdate",type=string,priority=1 - -// PrivateAi is the Schema for the privateais API. -type PrivateAi struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec PrivateAiSpec `json:"spec,omitempty"` - Status PrivateAiStatus `json:"status,omitempty"` -} - -const ReconcileError string = "ReconcileError" - -const ReconcileErrorReason string = "LastReconcileCycleFailed" - -const ReconcileQueued string = "ReconcileQueued" - -const ReconcileQueuedReason string = "LastReconcileCycleQueued" - -const ReconcileCompelete string = "ReconcileComplete" - -const ReconcileCompleteReason string = "LastReconcileCycleCompleted" - -const ReconcileBlocked string = "ReconcileBlocked" - -const ReconcileBlockedReason string = "LastReconcileCycleBlocked" - -const StatusPending string = "Pending" - -const StatusCreating string = "Creating" - -const StatusNotReady string = "Unhealthy" - -const StatusPatching string = "Patching" - -const StatusUpdating string = "Updating" - -const StatusReady string = "Healthy" - -const StatusError string = "Error" - -const StatusUnknown string = "Unknown" - -const ValueUnavailable string = "Unavailable" - -const NoExternalIp string = "Node ExternalIP unavailable" - -// +kubebuilder:object:root=true - -// PrivateAiList contains a list of PrivateAi. -type PrivateAiList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []PrivateAi `json:"items"` -} - -func init() { - SchemeBuilder.Register(&PrivateAi{}, &PrivateAiList{}) -} diff --git a/apis/omlai/v4/privateai_webhook.go b/apis/omlai/v4/privateai_webhook.go deleted file mode 100644 index 9599b093..00000000 --- a/apis/omlai/v4/privateai_webhook.go +++ /dev/null @@ -1,213 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - "context" - "fmt" - - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// nolint:unused -// log is for logging in this package. -var privateailog = logf.Log.WithName("privateai-resource") - -// SetupPrivateAiWebhookWithManager registers the webhook for PrivateAi in the manager. -func SetupPrivateAiWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr).For(&PrivateAi{}). - WithValidator(&PrivateAiCustomValidator{}). - WithDefaulter(&PrivateAiCustomDefaulter{}). - Complete() -} - -// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -// +kubebuilder:webhook:path=/mutate-omlai-oracle-com-v4-privateai,mutating=true,failurePolicy=fail,sideEffects=None,groups=omlai.oracle.com,resources=privateais,verbs=create;update,versions=v4,name=mprivateai-v4.kb.io,admissionReviewVersions=v1 - -// PrivateAiCustomDefaulter struct is responsible for setting default values on the custom resource of the -// Kind PrivateAi when those are created or updated. -// -// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, -// as it is used only for temporary operations and does not need to be deeply copied. -type PrivateAiCustomDefaulter struct { - // TODO(user): Add more fields as needed for defaulting -} - -var _ webhook.CustomDefaulter = &PrivateAiCustomDefaulter{} - -// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind PrivateAi. -func (d *PrivateAiCustomDefaulter) Default(ctx context.Context, obj runtime.Object) error { - privateai, ok := obj.(*PrivateAi) - - if !ok { - return fmt.Errorf("expected an PrivateAi object but got %T", obj) - } - privateailog.Info("Defaulting for PrivateAi", "name", privateai.GetName()) - - // TODO(user): fill in your defaulting logic. - - if !privateai.Spec.PaiHTTPEnabled && !privateai.Spec.PaiHTTPSEnabled { - privateai.Spec.PaiHTTPSEnabled = true - } - if privateai.Spec.PaiHTTPEnabled && privateai.Spec.PaiHTTPSEnabled { - return fmt.Errorf("paiHTTPEnabled and PaiHTTPSEnabled cannot be true") - } - if privateai.Spec.PaiHTTPEnabled { - if privateai.Spec.PaiHTTPPort == 0 { - privateai.Spec.PaiHTTPPort = 8080 - } - privateai.Spec.PaiHTTPSEnabled = false - privateai.Spec.PaiHTTPSPort = 0 - - } else { - privateai.Spec.PaiHTTPSEnabled = true - if privateai.Spec.PaiHTTPSPort == 0 { - privateai.Spec.PaiHTTPSPort = 8443 - } - privateai.Spec.PaiHTTPEnabled = false - privateai.Spec.PaiHTTPPort = 0 - } - - if privateai.Spec.PaiEnableAuthentication { - if privateai.Spec.PaiSecret == nil || privateai.Spec.PaiSecret.Name == "" { - return fmt.Errorf("paiEnableAuthentication is true but paiSecret.name is empty") - } - } - - if len(privateai.Spec.PaiService.PortMappings) == 0 { - portInfo := PaiPortMapping{} - - portInfo.Port = 443 - - if privateai.Spec.PaiHTTPEnabled { - portInfo.TargetPort = privateai.Spec.PaiHTTPPort - } else { - portInfo.TargetPort = privateai.Spec.PaiHTTPSPort - } - portInfo.Protocol = "TCP" - - privateai.Spec.PaiService.PortMappings = append(privateai.Spec.PaiService.PortMappings, portInfo) - } - // set default MountLocation for PaiConfigFile - if privateai.Spec.PaiConfigFile != nil { - if privateai.Spec.PaiConfigFile.MountLocation == "" { - privateai.Spec.PaiConfigFile.MountLocation = "/oml/config" - } - } - - // set default MountLocation for PaiSecret - if privateai.Spec.PaiSecret != nil { - if privateai.Spec.PaiSecret.MountLocation == "" { - privateai.Spec.PaiSecret.MountLocation = "/oml/ssl" - } - } - - return nil -} - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here. -// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook. -// +kubebuilder:webhook:path=/validate-omlai-oracle-com-v4-privateai,mutating=false,failurePolicy=fail,sideEffects=None,groups=omlai.oracle.com,resources=privateais,verbs=create;update,versions=v4,name=vprivateai-v4.kb.io,admissionReviewVersions=v1 - -// PrivateAiCustomValidator struct is responsible for validating the PrivateAi resource -// when it is created, updated, or deleted. -// -// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, -// as this struct is used only for temporary operations and does not need to be deeply copied. -type PrivateAiCustomValidator struct { - // TODO(user): Add more fields as needed for validation -} - -var _ webhook.CustomValidator = &PrivateAiCustomValidator{} - -// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type PrivateAi. -func (v *PrivateAiCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - privateai, ok := obj.(*PrivateAi) - if !ok { - return nil, fmt.Errorf("expected a PrivateAi object but got %T", obj) - } - privateailog.Info("Validation for PrivateAi upon creation", "name", privateai.GetName()) - - if privateai.Spec.PaiEnableAuthentication { - if privateai.Spec.PaiSecret == nil || privateai.Spec.PaiSecret.Name == "" { - return nil, fmt.Errorf("paiEnableAuthentication=true requires paiSecret.name to be set") - } - } - - // TODO(user): fill in your validation logic upon object creation. - - return nil, nil -} - -// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type PrivateAi. -func (v *PrivateAiCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - privateai, ok := newObj.(*PrivateAi) - if !ok { - return nil, fmt.Errorf("expected a PrivateAi object for the newObj but got %T", newObj) - } - privateailog.Info("Validation for PrivateAi upon update", "name", privateai.GetName()) - if privateai.Spec.PaiEnableAuthentication { - if privateai.Spec.PaiSecret == nil || privateai.Spec.PaiSecret.Name == "" { - return nil, fmt.Errorf("paiEnableAuthentication=true requires paiSecret.name to be set") - } - } - - // TODO(user): fill in your validation logic upon object update. - - return nil, nil -} - -// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type PrivateAi. -func (v *PrivateAiCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - privateai, ok := obj.(*PrivateAi) - if !ok { - return nil, fmt.Errorf("expected a PrivateAi object but got %T", obj) - } - privateailog.Info("Validation for PrivateAi upon deletion", "name", privateai.GetName()) - - // TODO(user): fill in your validation logic upon object deletion. - - return nil, nil -} diff --git a/apis/omlai/v4/privateai_webhook_test.go b/apis/omlai/v4/privateai_webhook_test.go deleted file mode 100644 index c9bb59a2..00000000 --- a/apis/omlai/v4/privateai_webhook_test.go +++ /dev/null @@ -1,107 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - // TODO (user): Add any additional imports if needed -) - -var _ = Describe("PrivateAi Webhook", func() { - var ( - obj *PrivateAi - oldObj *PrivateAi - validator PrivateAiCustomValidator - defaulter PrivateAiCustomDefaulter - ) - - BeforeEach(func() { - obj = &PrivateAi{} - oldObj = &PrivateAi{} - validator = PrivateAiCustomValidator{} - Expect(validator).NotTo(BeNil(), "Expected validator to be initialized") - defaulter = PrivateAiCustomDefaulter{} - Expect(defaulter).NotTo(BeNil(), "Expected defaulter to be initialized") - Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized") - Expect(obj).NotTo(BeNil(), "Expected obj to be initialized") - // TODO (user): Add any setup logic common to all tests - }) - - AfterEach(func() { - // TODO (user): Add any teardown logic common to all tests - }) - - Context("When creating PrivateAi under Defaulting Webhook", func() { - // TODO (user): Add logic for defaulting webhooks - // Example: - // It("Should apply defaults when a required field is empty", func() { - // By("simulating a scenario where defaults should be applied") - // obj.SomeFieldWithDefault = "" - // By("calling the Default method to apply defaults") - // defaulter.Default(ctx, obj) - // By("checking that the default values are set") - // Expect(obj.SomeFieldWithDefault).To(Equal("default_value")) - // }) - }) - - Context("When creating or updating PrivateAi under Validating Webhook", func() { - // TODO (user): Add logic for validating webhooks - // Example: - // It("Should deny creation if a required field is missing", func() { - // By("simulating an invalid creation scenario") - // obj.SomeRequiredField = "" - // Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred()) - // }) - // - // It("Should admit creation if all required fields are present", func() { - // By("simulating an invalid creation scenario") - // obj.SomeRequiredField = "valid_value" - // Expect(validator.ValidateCreate(ctx, obj)).To(BeNil()) - // }) - // - // It("Should validate updates correctly", func() { - // By("simulating a valid update scenario") - // oldObj.SomeRequiredField = "updated_value" - // obj.SomeRequiredField = "updated_value" - // Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil()) - // }) - }) - -}) diff --git a/apis/omlai/v4/webhook_suite_test.go b/apis/omlai/v4/webhook_suite_test.go deleted file mode 100644 index 7473e9e4..00000000 --- a/apis/omlai/v4/webhook_suite_test.go +++ /dev/null @@ -1,184 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - "context" - "crypto/tls" - "fmt" - "net" - "os" - "path/filepath" - "testing" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - "sigs.k8s.io/controller-runtime/pkg/webhook" - // +kubebuilder:scaffold:imports -) - -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var ( - ctx context.Context - cancel context.CancelFunc - k8sClient client.Client - cfg *rest.Config - testEnv *envtest.Environment -) - -func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "Webhook Suite") -} - -var _ = BeforeSuite(func() { - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - ctx, cancel = context.WithCancel(context.TODO()) - - var err error - err = AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crd", "bases")}, - ErrorIfCRDPathMissing: false, - - WebhookInstallOptions: envtest.WebhookInstallOptions{ - Paths: []string{filepath.Join("..", "..", "..", "..", "config", "webhook")}, - }, - } - - // Retrieve the first found binary directory to allow running tests from IDEs - if getFirstFoundEnvTestBinaryDir() != "" { - testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() - } - - // cfg is defined in this file globally. - cfg, err = testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(k8sClient).NotTo(BeNil()) - - // start webhook server using Manager. - webhookInstallOptions := &testEnv.WebhookInstallOptions - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme.Scheme, - WebhookServer: webhook.NewServer(webhook.Options{ - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - }), - LeaderElection: false, - Metrics: metricsserver.Options{BindAddress: "0"}, - }) - Expect(err).NotTo(HaveOccurred()) - - err = SetupPrivateAiWebhookWithManager(mgr) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:webhook - - go func() { - defer GinkgoRecover() - err = mgr.Start(ctx) - Expect(err).NotTo(HaveOccurred()) - }() - - // wait for the webhook server to get ready. - dialer := &net.Dialer{Timeout: time.Second} - addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) - Eventually(func() error { - conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) - if err != nil { - return err - } - - return conn.Close() - }).Should(Succeed()) -}) - -var _ = AfterSuite(func() { - By("tearing down the test environment") - cancel() - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) -}) - -// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. -// ENVTEST-based tests depend on specific binaries, usually located in paths set by -// controller-runtime. When running tests directly (e.g., via an IDE) without using -// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. -// -// This function streamlines the process by finding the required binaries, similar to -// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are -// properly set up, run 'make setup-envtest' beforehand. -func getFirstFoundEnvTestBinaryDir() string { - basePath := filepath.Join("..", "..", "..", "..", "bin", "k8s") - entries, err := os.ReadDir(basePath) - if err != nil { - logf.Log.Error(err, "Failed to read directory", "path", basePath) - return "" - } - for _, entry := range entries { - if entry.IsDir() { - return filepath.Join(basePath, entry.Name()) - } - } - return "" -} diff --git a/apis/omlai/v4/zz_generated.deepcopy.go b/apis/omlai/v4/zz_generated.deepcopy.go deleted file mode 100644 index 061a7344..00000000 --- a/apis/omlai/v4/zz_generated.deepcopy.go +++ /dev/null @@ -1,322 +0,0 @@ -//go:build !ignore_autogenerated - -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -// Code generated by controller-gen. DO NOT EDIT. - -package v4 - -import ( - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvironmentVariable) DeepCopyInto(out *EnvironmentVariable) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentVariable. -func (in *EnvironmentVariable) DeepCopy() *EnvironmentVariable { - if in == nil { - return nil - } - out := new(EnvironmentVariable) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PaiConfigMap) DeepCopyInto(out *PaiConfigMap) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiConfigMap. -func (in *PaiConfigMap) DeepCopy() *PaiConfigMap { - if in == nil { - return nil - } - out := new(PaiConfigMap) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PaiNodePortSvc) DeepCopyInto(out *PaiNodePortSvc) { - *out = *in - if in.PortMappings != nil { - in, out := &in.PortMappings, &out.PortMappings - *out = make([]PaiPortMapping, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiNodePortSvc. -func (in *PaiNodePortSvc) DeepCopy() *PaiNodePortSvc { - if in == nil { - return nil - } - out := new(PaiNodePortSvc) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PaiPortMapping) DeepCopyInto(out *PaiPortMapping) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiPortMapping. -func (in *PaiPortMapping) DeepCopy() *PaiPortMapping { - if in == nil { - return nil - } - out := new(PaiPortMapping) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PaiSecretSpec) DeepCopyInto(out *PaiSecretSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiSecretSpec. -func (in *PaiSecretSpec) DeepCopy() *PaiSecretSpec { - if in == nil { - return nil - } - out := new(PaiSecretSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PaiServiceSpec) DeepCopyInto(out *PaiServiceSpec) { - *out = *in - if in.PortMappings != nil { - in, out := &in.PortMappings, &out.PortMappings - *out = make([]PaiPortMapping, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PaiServiceSpec. -func (in *PaiServiceSpec) DeepCopy() *PaiServiceSpec { - if in == nil { - return nil - } - out := new(PaiServiceSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrivateAi) DeepCopyInto(out *PrivateAi) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAi. -func (in *PrivateAi) DeepCopy() *PrivateAi { - if in == nil { - return nil - } - out := new(PrivateAi) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PrivateAi) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrivateAiCustomDefaulter) DeepCopyInto(out *PrivateAiCustomDefaulter) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiCustomDefaulter. -func (in *PrivateAiCustomDefaulter) DeepCopy() *PrivateAiCustomDefaulter { - if in == nil { - return nil - } - out := new(PrivateAiCustomDefaulter) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrivateAiCustomValidator) DeepCopyInto(out *PrivateAiCustomValidator) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiCustomValidator. -func (in *PrivateAiCustomValidator) DeepCopy() *PrivateAiCustomValidator { - if in == nil { - return nil - } - out := new(PrivateAiCustomValidator) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrivateAiList) DeepCopyInto(out *PrivateAiList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PrivateAi, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiList. -func (in *PrivateAiList) DeepCopy() *PrivateAiList { - if in == nil { - return nil - } - out := new(PrivateAiList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PrivateAiList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrivateAiSpec) DeepCopyInto(out *PrivateAiSpec) { - *out = *in - if in.PaiConfigFile != nil { - in, out := &in.PaiConfigFile, &out.PaiConfigFile - *out = new(PaiConfigMap) - **out = **in - } - if in.PaiSecret != nil { - in, out := &in.PaiSecret, &out.PaiSecret - *out = new(PaiSecretSpec) - **out = **in - } - if in.PvcList != nil { - in, out := &in.PvcList, &out.PvcList - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.PaiService.DeepCopyInto(&out.PaiService) - if in.EnvVars != nil { - in, out := &in.EnvVars, &out.EnvVars - *out = make([]EnvironmentVariable, len(*in)) - copy(*out, *in) - } - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = new(v1.ResourceRequirements) - (*in).DeepCopyInto(*out) - } - if in.NodePortSvc != nil { - in, out := &in.NodePortSvc, &out.NodePortSvc - *out = make([]PaiNodePortSvc, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.PortMappings != nil { - in, out := &in.PortMappings, &out.PortMappings - *out = make([]PaiPortMapping, len(*in)) - copy(*out, *in) - } - if in.PailbAnnotation != nil { - in, out := &in.PailbAnnotation, &out.PailbAnnotation - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiSpec. -func (in *PrivateAiSpec) DeepCopy() *PrivateAiSpec { - if in == nil { - return nil - } - out := new(PrivateAiSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrivateAiStatus) DeepCopyInto(out *PrivateAiStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateAiStatus. -func (in *PrivateAiStatus) DeepCopy() *PrivateAiStatus { - if in == nil { - return nil - } - out := new(PrivateAiStatus) - in.DeepCopyInto(out) - return out -} diff --git a/config/crd/bases/omlai.oracle.com_privateais.yaml b/config/crd/bases/omlai.oracle.com_privateais.yaml deleted file mode 100644 index e8deef09..00000000 --- a/config/crd/bases/omlai.oracle.com_privateais.yaml +++ /dev/null @@ -1,305 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - name: privateais.omlai.oracle.com -spec: - group: omlai.oracle.com - names: - kind: PrivateAi - listKind: PrivateAiList - plural: privateais - singular: privateai - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.replicas - name: Replicas - type: number - - jsonPath: .status.apikey - name: ApiKey - type: string - - jsonPath: .status.podip - name: PodIP - type: string - - jsonPath: .status.loadbalancerip - name: LbIP - type: string - - jsonPath: .status.ReleaseUpdate - name: ReleaseUpdate - priority: 1 - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - livenessCheckPeriod: - type: integer - nodePortSvc: - items: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - svcType: - type: string - type: object - type: array - paiAuthentication: - type: boolean - paiConfigFile: - properties: - mountLocation: - type: string - name: - type: string - type: object - paiEnableAuthentication: - default: true - type: boolean - paiHTTPEnabled: - type: boolean - paiHTTPPort: - format: int32 - type: integer - paiHTTPSEnabled: - type: boolean - paiHTTPSPort: - format: int32 - type: integer - paiImage: - type: string - paiImagePullSecret: - type: string - paiInternalLB: - type: boolean - paiLBIP: - type: string - paiLBPort: - format: int32 - type: integer - paiLogLocation: - type: string - paiSecret: - properties: - mountLocation: - type: string - name: - type: string - type: object - paiService: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - svcType: - type: string - type: object - pailbAnnotation: - additionalProperties: - type: string - type: object - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - pvcList: - additionalProperties: - type: string - type: object - readinessCheckPeriod: - type: integer - replicas: - format: int32 - type: integer - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageClass: - type: string - storageSizeInGb: - format: int32 - type: integer - type: object - status: - properties: - NodeIP: - type: string - apiKey: - type: string - certpem: - type: string - clusterIP: - type: string - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - loadBalancerIP: - type: string - podIP: - type: string - releaseUpdate: - type: string - replicas: - type: integer - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index de38cc77..59f9af93 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -19,7 +19,6 @@ resources: - bases/database.oracle.com_lrests.yaml - bases/database.oracle.com_lrpdbs.yaml - bases/database.oracle.com_ordssrvs.yaml -- bases/omlai.oracle.com_privateais.yaml - bases/database.oracle.com_oraclerestarts.yaml # +kubebuilder:scaffold:crdkustomizeresource diff --git a/config/rbac/omlai_privateai_admin_role.yaml b/config/rbac/omlai_privateai_admin_role.yaml deleted file mode 100644 index 5de0e698..00000000 --- a/config/rbac/omlai_privateai_admin_role.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# This rule is not used by the project oracle-database-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants full permissions ('*') over omlai.oracle.com. -# This role is intended for users authorized to modify roles and bindings within the cluster, -# enabling them to delegate specific permissions to other users or groups as needed. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: oracle-database-operator - app.kubernetes.io/managed-by: kustomize - name: omlai-privateai-admin-role -rules: -- apiGroups: - - omlai.oracle.com - resources: - - privateais - verbs: - - '*' -- apiGroups: - - omlai.oracle.com - resources: - - privateais/status - verbs: - - get diff --git a/config/rbac/omlai_privateai_editor_role.yaml b/config/rbac/omlai_privateai_editor_role.yaml deleted file mode 100644 index a41b49f0..00000000 --- a/config/rbac/omlai_privateai_editor_role.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# This rule is not used by the project oracle-database-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants permissions to create, update, and delete resources within the omlai.oracle.com. -# This role is intended for users who need to manage these resources -# but should not control RBAC or manage permissions for others. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: oracle-database-operator - app.kubernetes.io/managed-by: kustomize - name: omlai-privateai-editor-role -rules: -- apiGroups: - - omlai.oracle.com - resources: - - privateais - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - omlai.oracle.com - resources: - - privateais/status - verbs: - - get diff --git a/config/rbac/omlai_privateai_viewer_role.yaml b/config/rbac/omlai_privateai_viewer_role.yaml deleted file mode 100644 index 7b255cd5..00000000 --- a/config/rbac/omlai_privateai_viewer_role.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# This rule is not used by the project oracle-database-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants read-only access to omlai.oracle.com resources. -# This role is intended for users who need visibility into these resources -# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: oracle-database-operator - app.kubernetes.io/managed-by: kustomize - name: omlai-privateai-viewer-role -rules: -- apiGroups: - - omlai.oracle.com - resources: - - privateais - verbs: - - get - - list - - watch -- apiGroups: - - omlai.oracle.com - resources: - - privateais/status - verbs: - - get diff --git a/config/samples/omlai_v4_privateai.yaml b/config/samples/omlai_v4_privateai.yaml deleted file mode 100644 index ea495ac0..00000000 --- a/config/samples/omlai_v4_privateai.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: omlai.oracle.com/v4 -kind: PrivateAi -metadata: - labels: - app.kubernetes.io/name: oracle-database-operator - app.kubernetes.io/managed-by: kustomize - name: privateai-sample -spec: - # TODO(user): Add fields here diff --git a/controllers/omlai/privateai_controller.go b/controllers/omlai/privateai_controller.go deleted file mode 100644 index 6e8a370e..00000000 --- a/controllers/omlai/privateai_controller.go +++ /dev/null @@ -1,623 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package omlai - -import ( - "context" - "encoding/json" - "fmt" - - // "strconv" - "time" - - "github.com/go-logr/logr" - omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" - aicommons "github.com/oracle/oracle-database-operator/commons/omlai" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - logf "sigs.k8s.io/controller-runtime/pkg/log" -) - -// PrivateAiReconciler reconciles a PrivateAi object -type PrivateAiReconciler struct { - client.Client - Scheme *runtime.Scheme - Log logr.Logger - Config *rest.Config - Recorder record.EventRecorder -} - -// To requeue after 15 secs allowing graceful state changes -var requeueY ctrl.Result = ctrl.Result{Requeue: true, RequeueAfter: 15 * time.Second} -var requeueN ctrl.Result = ctrl.Result{} - -var resultNq = ctrl.Result{Requeue: false} -var resultQ = ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second} - -const PrivateAiFinalizer = "omlai.oracle.com/privateaifinalizer" - -// +kubebuilder:rbac:groups=omlai.oracle.com,resources=privateais,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=omlai.oracle.com,resources=privateais/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=omlai.oracle.com,resources=privateais/finalizers,verbs=update -// +kubebuilder:rbac:groups=core,resources=pods;pods/log;pods/exec;secrets;containers;services;events;configmaps;persistentvolumeclaims;namespaces,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=core,resources=pods/exec,verbs=create - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the PrivateAi object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.4/pkg/reconcile -func (r *PrivateAiReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = logf.FromContext(ctx) - - // TODO(user): your logic here - r.Log.Info("Reconcile requested") - var result ctrl.Result - var err error - completed := false - blocked := false - - privateAiInst := &omlaiv4.PrivateAi{} - defer r.updateReconcileStatus(privateAiInst, ctx, &result, &err, &blocked, &completed) - - err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, privateAiInst) - if err != nil { - if apierrors.IsNotFound(err) { - r.Log.Info("Resource not found") - return requeueN, nil - } - r.Log.Error(err, err.Error()) - return requeueY, err - } - - /* Initialize Status */ - if privateAiInst.Status.Status == "" { - privateAiInst.Status.Status = omlaiv4.StatusPending - privateAiInst.Status.Replicas = 0 - privateAiInst.Status.ReleaseUpdate = omlaiv4.ValueUnavailable - r.Status().Update(ctx, privateAiInst) - } - // Validation Http/Https - if privateAiInst.Spec.PaiHTTPEnabled && privateAiInst.Spec.PaiHTTPSEnabled { - return requeueN, fmt.Errorf("paiHTTPEnabled and PaiHTTPSEnabled cannot be true") - } - if !privateAiInst.Spec.PaiHTTPEnabled && !privateAiInst.Spec.PaiHTTPSEnabled { - privateAiInst.Spec.PaiHTTPEnabled = true - } - - // Validation PaiAuthentication - if privateAiInst.Spec.PaiEnableAuthentication { - if privateAiInst.Spec.PaiSecret.Name == "" { - return requeueN, fmt.Errorf("PaiAuthentication is enabled but no PaiSecret is defined") - } - apiKey, certPem := aicommons.ReadSecret(privateAiInst.Spec.PaiSecret.Name, privateAiInst, r.Client, r.Log) - if apiKey == "" || apiKey == "NONE" { - return requeueN, fmt.Errorf("PaiAuthentication is enabled but apikey is not found in secret") - } - if certPem == "NONE" { - r.Log.Info("PaiAuthentication is enabled but cert.pem not found in secret") // optional soft warning - } - privateAiInst.Status.ApiKey = apiKey - privateAiInst.Status.Certpem = certPem - r.Status().Update(ctx, privateAiInst) - } - - // Manage SingleInstanceDatabase Deletion - err, isPrivateAiDelTrue := r.managePrivateAiDeletion(privateAiInst) - if err != nil { - //r.setCrdLifeCycleState(instance, &result, &err, stateType) - result = resultNq - if isPrivateAiDelTrue == true { - err = nil - return result, err - } else { - return result, err - } - } - - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, nil - } - if err != nil { - r.Log.Error(err, err.Error()) - return result, err - } - - // First validate - result, err = r.validate(privateAiInst, ctx, req) - if result.Requeue { - r.Log.Info("Spec validation failed, Reconcile queued") - return result, nil - } - if err != nil { - r.Log.Info("Spec validation failed") - return result, nil - } - - // Create PVC - claims := aicommons.VolumeClaimTemplatesForPrivateAi(privateAiInst) - for i := 0; i < len(claims); i++ { - result, err = r.createPvc(privateAiInst, &claims[i]) - if err != nil { - result = resultNq - return result, err - } - } - - // ========================= Service Setup For Catalog=================== - // Following check and loop will make sure to create the service - result, err = r.createService(privateAiInst, aicommons.BuildServiceDefForPrivateAi(privateAiInst, "local")) - if err != nil { - result = resultNq - return result, err - } - if privateAiInst.Spec.IsExternalSvc { - result, err = r.createService(privateAiInst, aicommons.BuildServiceDefForPrivateAi(privateAiInst, "external")) - if err != nil { - result = resultNq - return result, err - } - } - - // See if DeploymentSets already exists and create if it doesn't - desiredDeploy := aicommons.BuildDeploySetForPrivateAI(privateAiInst) - - foundDeploy := &appsv1.Deployment{} - err = r.Client.Get(ctx, types.NamespacedName{ - Name: desiredDeploy.Name, - Namespace: desiredDeploy.Namespace, - }, foundDeploy) - - if err != nil && apierrors.IsNotFound(err) { - // didn't found create New deployment - result, err = r.deployPrivateAiDeploymentSet(privateAiInst, desiredDeploy) - if err != nil { - return resultNq, err - } - - } else if err == nil { - //if exists check Pods & run - podList := &corev1.PodList{} - err = r.Client.List(ctx, podList, - client.InNamespace(privateAiInst.Namespace), - client.MatchingLabels(foundDeploy.Spec.Selector.MatchLabels), - ) - if err != nil { - return resultNq, err - } - - var foundPod *corev1.Pod - if len(podList.Items) > 0 { - foundPod = &podList.Items[0] - } else { - foundPod = &corev1.Pod{} - } - - // _, err = aicommons.ManageReplicas(privateAiInst, r.Client, r.Config, foundDeploy, podList, r.Log) - _, err = aicommons.ManageReplicas(r, privateAiInst, r.Client, r.Config, foundDeploy, podList, ctx, req, r.Log) - - if err != nil { - return resultNq, err - } - - _, err = aicommons.UpdateDeploySetForPrivateAI(privateAiInst, privateAiInst.Spec, r.Client, r.Config, foundDeploy, foundPod, r.Log) - if err != nil { - return resultNq, err - } - - } else { - return resultNq, err - } - - completed = true - r.Log.Info("Reconcile completed") - - return requeueN, nil -} - -// ############################################################################# -// -// Update each reconcile condtion/status -// -// ############################################################################# -func (r *PrivateAiReconciler) updateReconcileStatus(m *omlaiv4.PrivateAi, ctx context.Context, - result *ctrl.Result, err *error, blocked *bool, completed *bool) { - - // Always refresh status before a reconcile - defer r.Status().Update(ctx, m) - - m.Status.Replicas = int(m.Spec.Replicas) - m.Status.Status = omlaiv4.StatusReady - m.Status.ReleaseUpdate = "V2.0" - //m.Status.ApiKey = m.Spec.PaiSecret.Name - //m.Status.PodIP = - // m.Status.LoadBalancerIP = - - errMsg := func() string { - if *err != nil { - return (*err).Error() - } - return "no reconcile errors" - }() - var condition metav1.Condition - if *completed { - condition = metav1.Condition{ - Type: omlaiv4.ReconcileCompelete, - LastTransitionTime: metav1.Now(), - ObservedGeneration: m.GetGeneration(), - Reason: omlaiv4.ReconcileCompleteReason, - Message: errMsg, - Status: metav1.ConditionTrue, - } - } else if *blocked { - condition = metav1.Condition{ - Type: omlaiv4.ReconcileBlocked, - LastTransitionTime: metav1.Now(), - ObservedGeneration: m.GetGeneration(), - Reason: omlaiv4.ReconcileBlockedReason, - Message: errMsg, - Status: metav1.ConditionTrue, - } - } else if result.Requeue { - condition = metav1.Condition{ - Type: omlaiv4.ReconcileQueued, - LastTransitionTime: metav1.Now(), - ObservedGeneration: m.GetGeneration(), - Reason: omlaiv4.ReconcileQueuedReason, - Message: errMsg, - Status: metav1.ConditionTrue, - } - } else if *err != nil { - condition = metav1.Condition{ - Type: omlaiv4.ReconcileError, - LastTransitionTime: metav1.Now(), - ObservedGeneration: m.GetGeneration(), - Reason: omlaiv4.ReconcileErrorReason, - Message: errMsg, - Status: metav1.ConditionTrue, - } - } else { - return - } - if len(m.Status.Conditions) > 0 { - meta.RemoveStatusCondition(&m.Status.Conditions, condition.Type) - } - meta.SetStatusCondition(&m.Status.Conditions, condition) -} - -// ############################################################################# -// -// Validate the CRD specs -// -// ############################################################################# -func (r *PrivateAiReconciler) validate(m *omlaiv4.PrivateAi, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - //var err error - //eventReason := "Spec Error" - //var eventMsgs []string - - r.Log.Info("Entering reconcile validation") - - r.Log.Info("Completed reconcile validation") - - return requeueN, nil - -} - -// SetupWithManager sets up the controller with the Manager. -func (r *PrivateAiReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&omlaiv4.PrivateAi{}). - Named("omlai-privateai"). - Owns(&appsv1.Deployment{}). - Owns(&corev1.Service{}). - Owns(&corev1.Secret{}). - WithOptions(controller.Options{MaxConcurrentReconciles: 50}). - Complete(r) -} - -// This function deploy the DeploymentSet -func (r *PrivateAiReconciler) deployPrivateAiDeploymentSet(instance *omlaiv4.PrivateAi, - dep *appsv1.Deployment, -) (ctrl.Result, error) { - - reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) - message := "Inside the deployDeploymentSet function" - aicommons.LogMessages("DEBUG", message, nil, instance, r.Log) - // See if DeploymentSets already exists and create if it doesn't - // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) - // This happens during unit test cases - for i := 0; i < 5; i++ { - if r.Scheme == nil { - time.Sleep(time.Second * 40) - } else { - break - } - } - controllerutil.SetControllerReference(instance, dep, r.Scheme) - found := &appsv1.Deployment{} - err := r.Client.Get(context.TODO(), types.NamespacedName{ - Name: dep.Name, - Namespace: instance.Namespace, - }, found) - jsn, _ := json.Marshal(dep) - aicommons.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) - - if err != nil && errors.IsNotFound(err) { - - // Create the DeploymentSet - reqLogger.Info("Creating Deployment Shard") - err = r.Client.Create(context.TODO(), dep) - - message := "Inside the create Deployment set block to create DeploymentSet " + aicommons.GetFmtStr(dep.Name) - aicommons.LogMessages("DEBUG", message, nil, instance, r.Log) - - if err != nil { - // DeploymentSet failed - reqLogger.Error(err, "Failed to create DeploymentSet", "DeploymentSet.space", dep.Namespace, "DeploymentSet.Name", dep.Name) - //instance.Status.ShardStatus[dep.Name] = "Deployment Failed" - return ctrl.Result{}, err - } - } else if err != nil { - // Error that isn't due to the StaefulSet not existing - reqLogger.Error(err, "Failed to get DeploymentSet") - return ctrl.Result{}, err - } - - message = "DeploymentSet Exist " + aicommons.GetFmtStr(dep.Name) + " already exist" - aicommons.LogMessages("DEBUG", message, nil, instance, r.Log) - - return ctrl.Result{}, nil -} - -// ================================== CREATE FUNCTIONS ============================= -// This function create a service based isExtern parameter set in the yaml file -func (r *PrivateAiReconciler) createService(instance *omlaiv4.PrivateAi, - dep *corev1.Service, -) (ctrl.Result, error) { - reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) - // See if Service already exists and create if it doesn't - // We are getting error on nil pointer segment when r.scheme is null - // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) - // This happens during unit test cases - for i := 0; i < 5; i++ { - if r.Scheme == nil { - time.Sleep(time.Second * 40) - } else { - break - } - } - controllerutil.SetControllerReference(instance, dep, r.Scheme) - found := &corev1.Service{} - - err := r.Client.Get(context.TODO(), types.NamespacedName{ - Name: dep.Name, - Namespace: instance.Namespace, - }, found) - - jsn, _ := json.Marshal(dep) - aicommons.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) - if err != nil && errors.IsNotFound(err) { - // Create the Service - reqLogger.Info("Creating a service") - err = r.Client.Create(context.TODO(), dep) - if err != nil { - // Service creation failed - reqLogger.Error(err, "Failed to create Service", "Service.space", dep.Namespace, "Service.Name", dep.Name) - return ctrl.Result{}, err - } else { - // Service creation was successful - return ctrl.Result{Requeue: true}, nil - } - } else if err != nil { - // Error that isn't due to the Service not existing - reqLogger.Error(err, "Failed to find the Service details") - return ctrl.Result{}, err - } - - return ctrl.Result{}, nil -} - -// ================================== CREATE FUNCTIONS ============================= -// This function create a PVC based isExtern parameter set in the yaml file -func (r *PrivateAiReconciler) createPvc(instance *omlaiv4.PrivateAi, - dep *corev1.PersistentVolumeClaim, -) (ctrl.Result, error) { - reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) - // See if Service already exists and create if it doesn't - // We are getting error on nil pointer segment when r.scheme is null - // Error : invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference) - // This happens during unit test cases - for i := 0; i < 5; i++ { - if r.Scheme == nil { - time.Sleep(time.Second * 40) - } else { - break - } - } - controllerutil.SetControllerReference(instance, dep, r.Scheme) - found := &corev1.PersistentVolumeClaim{} - - err := r.Client.Get(context.TODO(), types.NamespacedName{ - Name: dep.Name, - Namespace: instance.Namespace, - }, found) - - jsn, _ := json.Marshal(dep) - aicommons.LogMessages("DEBUG", string(jsn), nil, instance, r.Log) - if err != nil && errors.IsNotFound(err) { - // Create the Service - reqLogger.Info("Creating PVC") - err = r.Client.Create(context.TODO(), dep) - if err != nil { - // Service creation failed - reqLogger.Error(err, "Failed to create PVC", "PVC.namespace", dep.Namespace, "PVC.Name", dep.Name) - return ctrl.Result{}, err - } else { - // Service creation was successful - return ctrl.Result{Requeue: true}, nil - } - } else if err != nil { - // Error that isn't due to the Service not existing - reqLogger.Error(err, "Failed to find the Service details") - return ctrl.Result{}, err - } - - return ctrl.Result{}, nil -} - -// FInalizer - -// ================== Function to check insytance deletion timestamp and activate the finalizer code ======== -func (r *PrivateAiReconciler) managePrivateAiDeletion(instance *omlaiv4.PrivateAi) (error, bool) { - - isPrivateToBeDeleted := instance.GetDeletionTimestamp() != nil - if isPrivateToBeDeleted { - if controllerutil.ContainsFinalizer(instance, PrivateAiFinalizer) { - // Run finalization logic for finalizer. If the - // finalization logic fails, don't remove the finalizer so - // that we can retry during the next reconciliation. - if err := r.finalizePrivateAi(instance); err != nil { - return err, false - } - - // Remove finalizer. Once all finalizers have been - // removed, the object will be deleted. - controllerutil.RemoveFinalizer(instance, PrivateAiFinalizer) - err := r.Client.Update(context.TODO(), instance) - if err != nil { - return err, false - } - } - // Send true because delete is in progress and it is a custom delete message - // We don't need to print custom err stack as we are deleting the topology - return fmt.Errorf("delete of the sharding topology is in progress"), true - } - - // Add finalizer for this CR - if instance.DeletionTimestamp == nil { - if !controllerutil.ContainsFinalizer(instance, PrivateAiFinalizer) { - if err := r.addFinalizer(instance); err != nil { - return err, false - } - } - } - - return nil, false -} - -// ========================== FInalizer Section =================== -func (r *PrivateAiReconciler) addFinalizer(instance *omlaiv4.PrivateAi) error { - reqLogger := r.Log.WithValues("instance.Namespace", instance.Namespace, "instance.Name", instance.Name) - controllerutil.AddFinalizer(instance, PrivateAiFinalizer) - - // Update CR - err := r.Client.Update(context.TODO(), instance) - if err != nil { - reqLogger.Error(err, "Failed to update Sharding Database with finalizer") - return err - } - return nil -} - -func (r *PrivateAiReconciler) finalizePrivateAi(instance *omlaiv4.PrivateAi) error { - // TODO(user): Add the cleanup steps that the operator needs to do before the CR - // can be deleted. Examples of finalizers include performing backups and deleting - // resources that are not owned by this CR, like a PVC. - - var err error - var pvcName string - - //r.checkProvInstance(instance) - depSetFound := &appsv1.Deployment{} - svcFound := &corev1.Service{} - depSetFound, err = aicommons.CheckDepSet(instance, r.Client) - if err == nil { - // See if StatefulSets already exists and create if it doesn't - err = r.Client.Delete(context.Background(), depSetFound) - if err != nil { - return err - } - if instance.Spec.IsDeleteOraPvc && len(instance.Spec.StorageClass) > 0 { - pvcName = instance.Name + "-oradata-vol4-" + instance.Name + "-0" - err = aicommons.DelPvc(pvcName, instance, r.Client, r.Log) - if err != nil { - return err - } - } - - if instance.Spec.IsExternalSvc { - // svcFound, err = aicommons.CheckSvc(instance.Name+strconv.FormatInt(int64(0), 10)+"-svc", instance, r.Client) - svcFound, err = aicommons.CheckSvc(instance.Name+"-svc", instance, r.Client) - - if err == nil { - // See if StatefulSets already exists and create if it doesn't - err = r.Client.Delete(context.Background(), svcFound) - if err != nil { - return err - } - } - } - svcFound, err = aicommons.CheckSvc(instance.Name, instance, r.Client) - if err == nil { - // See if StatefulSets already exists and create if it doesn't - err = r.Client.Delete(context.Background(), svcFound) - if err != nil { - return err - } - } - } - return nil -} diff --git a/controllers/omlai/privateai_controller_test.go b/controllers/omlai/privateai_controller_test.go deleted file mode 100644 index 70364c7e..00000000 --- a/controllers/omlai/privateai_controller_test.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package omlai - -import ( - "context" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" -) - -var _ = Describe("PrivateAi Controller", func() { - Context("When reconciling a resource", func() { - const resourceName = "test-resource" - - ctx := context.Background() - - typeNamespacedName := types.NamespacedName{ - Name: resourceName, - Namespace: "default", // TODO(user):Modify as needed - } - privateai := &omlaiv4.PrivateAi{} - - BeforeEach(func() { - By("creating the custom resource for the Kind PrivateAi") - err := k8sClient.Get(ctx, typeNamespacedName, privateai) - if err != nil && errors.IsNotFound(err) { - resource := &omlaiv4.PrivateAi{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourceName, - Namespace: "default", - }, - // TODO(user): Specify other spec details if needed. - } - Expect(k8sClient.Create(ctx, resource)).To(Succeed()) - } - }) - - AfterEach(func() { - // TODO(user): Cleanup logic after each test, like removing the resource instance. - resource := &omlaiv4.PrivateAi{} - err := k8sClient.Get(ctx, typeNamespacedName, resource) - Expect(err).NotTo(HaveOccurred()) - - By("Cleanup the specific resource instance PrivateAi") - Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) - }) - It("should successfully reconcile the resource", func() { - By("Reconciling the created resource") - controllerReconciler := &PrivateAiReconciler{ - Client: k8sClient, - Scheme: k8sClient.Scheme(), - } - - _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ - NamespacedName: typeNamespacedName, - }) - Expect(err).NotTo(HaveOccurred()) - // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. - // Example: If you expect a certain status condition after reconciliation, verify it here. - }) - }) -}) diff --git a/controllers/omlai/suite_test.go b/controllers/omlai/suite_test.go deleted file mode 100644 index c1d5dda7..00000000 --- a/controllers/omlai/suite_test.go +++ /dev/null @@ -1,138 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package omlai - -import ( - "context" - "os" - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" - // +kubebuilder:scaffold:imports -) - -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var ( - ctx context.Context - cancel context.CancelFunc - testEnv *envtest.Environment - cfg *rest.Config - k8sClient client.Client -) - -func TestControllers(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "Controller Suite") -} - -var _ = BeforeSuite(func() { - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - ctx, cancel = context.WithCancel(context.TODO()) - - var err error - err = omlaiv4.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, - ErrorIfCRDPathMissing: true, - } - - // Retrieve the first found binary directory to allow running tests from IDEs - if getFirstFoundEnvTestBinaryDir() != "" { - testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() - } - - // cfg is defined in this file globally. - cfg, err = testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(k8sClient).NotTo(BeNil()) -}) - -var _ = AfterSuite(func() { - By("tearing down the test environment") - cancel() - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) -}) - -// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. -// ENVTEST-based tests depend on specific binaries, usually located in paths set by -// controller-runtime. When running tests directly (e.g., via an IDE) without using -// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. -// -// This function streamlines the process by finding the required binaries, similar to -// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are -// properly set up, run 'make setup-envtest' beforehand. -func getFirstFoundEnvTestBinaryDir() string { - basePath := filepath.Join("..", "..", "..", "bin", "k8s") - entries, err := os.ReadDir(basePath) - if err != nil { - logf.Log.Error(err, "Failed to read directory", "path", basePath) - return "" - } - for _, entry := range entries { - if entry.IsDir() { - return filepath.Join(basePath, entry.Name()) - } - } - return "" -} diff --git a/main.go b/main.go index 0b49deef..e717978a 100644 --- a/main.go +++ b/main.go @@ -72,10 +72,7 @@ import ( observabilityv1 "github.com/oracle/oracle-database-operator/apis/observability/v1" observabilityv1alpha1 "github.com/oracle/oracle-database-operator/apis/observability/v1alpha1" observabilityv4 "github.com/oracle/oracle-database-operator/apis/observability/v4" - omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" - webhookomlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" observabilitycontroller "github.com/oracle/oracle-database-operator/controllers/observability" - omlaicontroller "github.com/oracle/oracle-database-operator/controllers/omlai" // +kubebuilder:scaffold:imports ) @@ -92,7 +89,6 @@ func init() { utilruntime.Must(databasev4.AddToScheme(scheme)) utilruntime.Must(observabilityv1.AddToScheme(scheme)) utilruntime.Must(observabilityv4.AddToScheme(scheme)) - utilruntime.Must(omlaiv4.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -397,21 +393,6 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "DatabaseObserver") os.Exit(1) } - if err = (&omlaicontroller.PrivateAiReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Config: mgr.GetConfig(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "PrivateAi") - os.Exit(1) - } - // nolint:goconst - if os.Getenv("ENABLE_WEBHOOKS") != "false" { - if err = webhookomlaiv4.SetupPrivateAiWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "PrivateAi") - os.Exit(1) - } - } // +kubebuilder:scaffold:builder indexFunc2 := func(obj client.Object) []string { From 7012aabea0c531357fbdd14cff50a16e69bbba60 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 8 Sep 2025 04:50:54 +0000 Subject: [PATCH 318/414] Added fixes --- commons/omlai/acommon.go | 225 --------------- commons/omlai/aibuilder.go | 577 ------------------------------------- commons/omlai/exec.go | 125 -------- 3 files changed, 927 deletions(-) delete mode 100644 commons/omlai/acommon.go delete mode 100644 commons/omlai/aibuilder.go delete mode 100644 commons/omlai/exec.go diff --git a/commons/omlai/acommon.go b/commons/omlai/acommon.go deleted file mode 100644 index 282e8eb0..00000000 --- a/commons/omlai/acommon.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package commons - -import ( - "context" - "fmt" - "strings" - - "github.com/go-logr/logr" - omlaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/rand" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - piDataMount = "/stage" - defaultLogMount = "/oml/logs" -) - -func LogMessages(msgtype string, msg string, err error, instance *omlaiv4.PrivateAi, logger logr.Logger) { - // setting logrus formatter - //logrus.SetFormatter(&logrus.JSONFormatter{}) - //logrus.SetOutput(os.Stdout) - - if msgtype == "DEBUG" && instance.Spec.IsDebug == true { - if err != nil { - logger.Error(err, msg) - } else { - logger.Info(msg) - } - } else if msgtype == "INFO" { - logger.Info(msg) - } else if msgtype == "Error" { - logger.Error(err, msg) - } -} - -func getOwnerRef(instance *omlaiv4.PrivateAi, -) []metav1.OwnerReference { - - var ownerRef []metav1.OwnerReference - ownerRef = append(ownerRef, metav1.OwnerReference{Kind: instance.GroupVersionKind().Kind, APIVersion: instance.APIVersion, Name: instance.Name, UID: types.UID(instance.UID)}) - return ownerRef -} - -// FUnction to build the svc definition for catalog/shard and GSM -func buildSvcPortsDef(instance *omlaiv4.PrivateAi) []corev1.ServicePort { - var result []corev1.ServicePort - if len(instance.Spec.PaiService.PortMappings) > 0 { - for _, portMapping := range instance.Spec.PaiService.PortMappings { - servicePort := - corev1.ServicePort{ - Protocol: portMapping.Protocol, - Port: portMapping.Port, - Name: generatePortMapping(portMapping), - TargetPort: intstr.IntOrString{ - Type: intstr.Int, - IntVal: portMapping.TargetPort, - }, - } - result = append(result, servicePort) - } - } - return result -} - -// Function to generate the port mapping -func generatePortMapping(portMapping omlaiv4.PaiPortMapping) string { - return generateName(fmt.Sprintf("%s-%d-%d-", "tcp", - portMapping.Port, portMapping.TargetPort)) -} - -// Function to generate the Name -func generateName(base string) string { - maxNameLength := 50 - randomLength := 5 - maxGeneratedLength := maxNameLength - randomLength - if len(base) > maxGeneratedLength { - base = base[:maxGeneratedLength] - } - return fmt.Sprintf("%s%s", base, rand.String(randomLength)) -} - -func GetFmtStr(pstr string, -) string { - return "[" + pstr + "]" -} - -func CheckDepSet(instance *omlaiv4.PrivateAi, kClient client.Client) (*appsv1.Deployment, error) { - sfSetFound := &appsv1.Deployment{} - err := kClient.Get(context.TODO(), types.NamespacedName{ - Name: instance.Name, - Namespace: instance.Namespace, - }, sfSetFound) - if err != nil { - return sfSetFound, err - } - return sfSetFound, nil -} - -func DelPvc(pvcName string, instance *omlaiv4.PrivateAi, kClient client.Client, logger logr.Logger) error { - - LogMessages("DEBUG", "Inside the delPvc and received param: "+GetFmtStr(pvcName), nil, instance, logger) - pvcFound, err := checkPvc(pvcName, instance, kClient) - if err != nil { - LogMessages("DEBUG", "Error occurred in finding the pvc claim!", nil, instance, logger) - return err - } - err = kClient.Delete(context.Background(), pvcFound) - if err != nil { - LogMessages("DEBUG", "Error occurred in deleting the pvc claim!", nil, instance, logger) - return err - } - return nil -} - -func CheckSvc(svcName string, instance *omlaiv4.PrivateAi, kClient client.Client) (*corev1.Service, error) { - // If this is a PrivateAi instance - if instance.Kind == "PrivateAi" && !strings.HasSuffix(svcName, "-svc") { - svcName = instance.Name + "-svc" - } - - svcFound := &corev1.Service{} - err := kClient.Get(context.TODO(), types.NamespacedName{ - Name: svcName, - Namespace: instance.Namespace, - }, svcFound) - if err != nil { - return svcFound, err - } - return svcFound, nil -} - -func checkPvc(pvcName string, instance *omlaiv4.PrivateAi, kClient client.Client) (*corev1.PersistentVolumeClaim, error) { - pvcFound := &corev1.PersistentVolumeClaim{} - err := kClient.Get(context.TODO(), types.NamespacedName{ - Name: pvcName, - Namespace: instance.Namespace, - }, pvcFound) - if err != nil { - return pvcFound, err - } - return pvcFound, nil -} - -func ReadSecret(secName string, instance *omlaiv4.PrivateAi, kClient client.Client, logger logr.Logger, -) (string, string) { - - var apiKeyVal string - var certPemVal string - sc := &corev1.Secret{} - //var err error - - // Reading a Secret - var err error = kClient.Get(context.TODO(), types.NamespacedName{ - Name: secName, - Namespace: instance.Namespace, - }, sc) - - if err != nil { - return "NONE", "NONE" - } - - // Secret Evaluation - for k, val := range sc.Data { - if k == "api-key" { - apiKeyVal = string(val) - LogMessages("DEBUG", "Key : "+GetFmtStr(k)+" Value : "+GetFmtStr(apiKeyVal)+" Val: "+GetFmtStr(string(val)), nil, instance, logger) - } - if k == "cert.pem" { - certPemVal = string(val) - LogMessages("DEBUG", "Key : "+GetFmtStr(k)+" Value : "+GetFmtStr(certPemVal)+" Val: "+GetFmtStr(string(val)), nil, instance, logger) - } - } - if apiKeyVal == "" { - apiKeyVal = "NONE" - } - if certPemVal == "" { - certPemVal = "NONE" - } - - return apiKeyVal, certPemVal -} diff --git a/commons/omlai/aibuilder.go b/commons/omlai/aibuilder.go deleted file mode 100644 index 42030323..00000000 --- a/commons/omlai/aibuilder.go +++ /dev/null @@ -1,577 +0,0 @@ -package commons - -import ( - "context" - "fmt" - "reflect" - "strconv" - "strings" - - "github.com/go-logr/logr" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/rest" - "k8s.io/utils/pointer" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - privateaiv4 "github.com/oracle/oracle-database-operator/apis/omlai/v4" // TODO -) - -func buildLabelsForPrivateAi(instance *privateaiv4.PrivateAi, label, name string) map[string]string { - return map[string]string{ - "app.kubernetes.io/instance": "PrivateAi-" + instance.Name, - "app.kubernetes.io/name": instance.Name, - "app.kubernetes.io/component": getLabelForPrivateAI(instance), - "app.kubernetes.io/managed-by": "Oracle-Database-Operator", - "app.kubernetes.io/offline-status": "false", - } -} - -func getLabelForPrivateAI(instance *privateaiv4.PrivateAi) string { - return "Oml-" + "PrivateAi-" + instance.Name -} - -func BuildDeploySetForPrivateAI(instance *privateaiv4.PrivateAi) *appsv1.Deployment { - return &appsv1.Deployment{ - TypeMeta: buildTypeMetaForPrivateAI(), - ObjectMeta: buildObjectMetaForPrivateAI(instance), - Spec: *buildDeploymentSpecForPrivateAI(instance), - } -} - -func buildTypeMetaForPrivateAI() metav1.TypeMeta { - return metav1.TypeMeta{ - Kind: "Deployment", - APIVersion: "apps/v1", - } -} - -func buildObjectMetaForPrivateAI(instance *privateaiv4.PrivateAi) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Name: instance.Name, - Namespace: instance.Namespace, - OwnerReferences: getOwnerRefPrivateAI(instance), - Labels: buildLabelsForPrivateAi(instance, "privateai", instance.Name), - } -} - -func buildDeploymentSpecForPrivateAI(instance *privateaiv4.PrivateAi) *appsv1.DeploymentSpec { - var replicas int32 = 1 - if instance.Spec.Replicas > 0 { - replicas = instance.Spec.Replicas - } else { - replicas = 1 - } - - return &appsv1.DeploymentSpec{ - Replicas: &replicas, - RevisionHistoryLimit: pointer.Int32(0), - Selector: &metav1.LabelSelector{ - MatchLabels: buildLabelsForPrivateAi(instance, "privateai", instance.Name), - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: buildLabelsForPrivateAi(instance, "privateai", instance.Name), - }, - Spec: *buildPodSpecForPrivateAI(instance), - }, - } -} - -func int64Ptr(i int64) *int64 { - return &i -} - -func buildPodSpecForPrivateAI(instance *privateaiv4.PrivateAi) *corev1.PodSpec { - spec := &corev1.PodSpec{ - // SecurityContext: &corev1.PodSecurityContext{FsGroup: int64(2001), RunAsUser: int64(2001), RunAsGroup(2001)}, - SecurityContext: &corev1.PodSecurityContext{ - FSGroup: int64Ptr(2001), - RunAsUser: int64Ptr(2001), - RunAsGroup: int64Ptr(2001), - }, - InitContainers: buildInitContainerSpecForPrivateAI(instance), - Containers: buildContainerSpecForPrivateAI(instance), - Volumes: buildVolumeSpecForPrivateAI(instance), - } - - if len(instance.Spec.PaiImagePullSecret) > 0 { - spec.ImagePullSecrets = []corev1.LocalObjectReference{ - {Name: instance.Spec.PaiImagePullSecret}, - } - } - - // Add NodeSelector if provided ?? TODO - // if len(paiSpec.NodeSelector) > 0 { - // spec.NodeSelector = paiSpec.NodeSelector - // } - - return spec -} - -// Init container: run as root, chown all pvcList mount paths -func buildInitContainerSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.Container { - if len(instance.Spec.PvcList) == 0 { - return nil - } - - // Build a single chown command across all mount paths - var cmds []string - for _, mountPath := range instance.Spec.PvcList { - if mountPath != "" { - cmds = append(cmds, fmt.Sprintf("chown -R 2001:2001 %q || true", mountPath)) - } - } - chownCmd := strings.Join(cmds, " && ") - - privileged := true - runAsRoot := int64(0) - - return []corev1.Container{ - { - Name: instance.Name + "-init", - Image: instance.Spec.PaiImage, - ImagePullPolicy: corev1.PullIfNotPresent, - Command: []string{"/bin/sh", "-c", chownCmd}, - SecurityContext: &corev1.SecurityContext{ - Privileged: &privileged, - RunAsUser: &runAsRoot, - RunAsGroup: &runAsRoot, - }, - VolumeMounts: pvcOnlyVolumeMounts(instance), // mount only PVCs we need to chown - }, - } -} - -// only the pvcList mounts (used by the init container) -func pvcOnlyVolumeMounts(instance *privateaiv4.PrivateAi) []corev1.VolumeMount { - var vms []corev1.VolumeMount - if len(instance.Spec.PvcList) > 0 { - for claimName, mountPath := range instance.Spec.PvcList { - if mountPath == "" { - continue - } - vms = append(vms, corev1.VolumeMount{ - Name: fmt.Sprintf("%s-%s-vol", instance.Name, claimName), - MountPath: mountPath, - ReadOnly: false, - }) - } - } - return vms -} - -func buildContainerSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.Container { - container := corev1.Container{ - Name: instance.Name, - Image: instance.Spec.PaiImage, - Resources: corev1.ResourceRequirements{}, - VolumeMounts: buildVolumeMountSpecForPrivateAI(instance), - Env: buildEnvVarsForPrivateAI(instance, instance.Spec.EnvVars), - ImagePullPolicy: corev1.PullIfNotPresent, - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"sh", "-c", "! /bin/test -f /tmp/unhealthy"}, - }, - }, - InitialDelaySeconds: 5, - PeriodSeconds: 5, - }, - } - var appPort int32 - if instance.Spec.Resources != nil { - container.Resources = *instance.Spec.Resources - } - if instance.Spec.PaiHTTPEnabled { - container.Env = append(container.Env, corev1.EnvVar{Name: "OML_HTTP_ENABLED", Value: "true"}, corev1.EnvVar{Name: "OML_HTTPS_ENABLED", Value: "false"}) - appPort = instance.Spec.PaiHTTPPort - - } else { - // HTTPS - container.Env = append(container.Env, corev1.EnvVar{Name: "OML_HTTP_ENABLED", Value: "false"}, corev1.EnvVar{Name: "OML_HTTPS_ENABLED", Value: "true"}) - appPort = instance.Spec.PaiHTTPSPort - } - - container.LivenessProbe = &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - TCPSocket: &corev1.TCPSocketAction{ - Port: intstr.FromInt(int(appPort)), - }, - }, - InitialDelaySeconds: 60, - TimeoutSeconds: 3, - SuccessThreshold: 1, - FailureThreshold: 3, - PeriodSeconds: 30, - } - container.Ports = []corev1.ContainerPort{ - { - ContainerPort: appPort, - Protocol: corev1.ProtocolTCP, - }, - } - return []corev1.Container{container} -} - -func buildVolumeSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.Volume { - var vols []corev1.Volume - if instance.Spec.PaiSecret != nil && instance.Spec.PaiSecret.Name != "" { - vols = append(vols, corev1.Volume{ - Name: instance.Name + "secret-vol", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{SecretName: instance.Spec.PaiSecret.Name}, - }, - }) - } - - if instance.Spec.PaiConfigFile != nil && instance.Spec.PaiConfigFile.Name != "" { - vols = append(vols, corev1.Volume{ - Name: instance.Name + "configmap-vol", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: instance.Spec.PaiConfigFile.Name}}, - }, - }) - } - - if instance.Spec.StorageClass != "" { - vols = append(vols, corev1.Volume{ - Name: instance.Name + "-oradata-vol4", - VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: instance.Name + "-oradata-vol4"}}, - }) - } - if len(instance.Spec.PvcList) > 0 { - for claimName := range instance.Spec.PvcList { - vols = append(vols, corev1.Volume{ - Name: fmt.Sprintf("%s-%s-vol", instance.Name, claimName), - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: claimName, // PVC name from CR - ReadOnly: false, - }, - }, - }) - } - } - - vols = append(vols, corev1.Volume{ - Name: instance.Name + "-logs-vol", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }) - - return vols -} - -func buildVolumeMountSpecForPrivateAI(instance *privateaiv4.PrivateAi) []corev1.VolumeMount { - var vms []corev1.VolumeMount - - // Main data PVC mount - if instance.Spec.StorageClass != "" { - vms = append(vms, corev1.VolumeMount{ - Name: instance.Name + "-oradata-vol4", - MountPath: piDataMount, - }) - } - - if instance.Spec.PaiSecret != nil { - vms = append(vms, corev1.VolumeMount{ - Name: instance.Name + "secret-vol", - MountPath: instance.Spec.PaiSecret.MountLocation, - ReadOnly: true, - }) - } - - if instance.Spec.PaiConfigFile != nil { - vms = append(vms, corev1.VolumeMount{ - Name: instance.Name + "configmap-vol", - MountPath: instance.Spec.PaiConfigFile.MountLocation, - ReadOnly: true, - }) - } - if len(instance.Spec.PvcList) > 0 { - for claimName, mountPath := range instance.Spec.PvcList { - if mountPath == "" { - continue - } - vms = append(vms, corev1.VolumeMount{ - Name: fmt.Sprintf("%s-%s-vol", instance.Name, claimName), - MountPath: mountPath, // e.g /oml/models - ReadOnly: false, - }) - } - } - - // NEW: Logs mount - logMount := defaultLogMount - if instance.Spec.PaiLogLocation != "" { - logMount = instance.Spec.PaiLogLocation - } - - vms = append(vms, corev1.VolumeMount{ - Name: instance.Name + "-logs-vol", - MountPath: logMount, - }) - - return vms -} - -func VolumeClaimTemplatesForPrivateAi(instance *privateaiv4.PrivateAi) []corev1.PersistentVolumeClaim { - - var claims []corev1.PersistentVolumeClaim - - claims = []corev1.PersistentVolumeClaim{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: instance.Name + "-oradata-vol4", - Namespace: instance.Namespace, - OwnerReferences: getOwnerRef(instance), - Labels: buildLabelsForPrivateAi(instance, "privateai", instance.Name), - }, - Spec: corev1.PersistentVolumeClaimSpec{ - AccessModes: []corev1.PersistentVolumeAccessMode{ - corev1.ReadWriteOnce, - }, - StorageClassName: &instance.Spec.StorageClass, - Resources: corev1.VolumeResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceStorage: resource.MustParse(strconv.FormatInt(int64(instance.Spec.StorageSizeInGb), 10) + "Gi"), - }, - }, - }, - }, - } - - // if len(instance.Spec.PvcList) != 0 { - - //claims = append(claims, slices.Collect(maps.Values(instance.Spec.PvcList))) - // } - - return claims -} - -func buildEnvVarsForPrivateAI(instance *privateaiv4.PrivateAi, envVars []privateaiv4.EnvironmentVariable) []corev1.EnvVar { - var envs []corev1.EnvVar - var omlConfigFileFlag bool - var omlSecretFlag bool - - for _, ev := range envVars { - if ev.Name == "OML_CONFIG_FILE" { - omlConfigFileFlag = true - } - if ev.Name == "OML_SECRETS_MOUNTPOINT" { - omlSecretFlag = true - } - envs = append(envs, corev1.EnvVar{ - Name: ev.Name, - Value: ev.Value, - }) - } - - if !omlConfigFileFlag { - if instance.Spec.PaiConfigFile != nil && instance.Spec.PaiConfigFile.Name != "" && instance.Spec.PaiConfigFile.MountLocation != "" { - envs = append(envs, corev1.EnvVar{ - Name: "OML_CONFIG_FILE", - Value: instance.Spec.PaiConfigFile.MountLocation + "/" + "config.json", - }) - } - } - - if !omlSecretFlag { - if instance.Spec.PaiSecret.Name != "" && instance.Spec.PaiSecret.MountLocation != "" { - envs = append(envs, corev1.EnvVar{ - Name: "OML_SECRETS_MOUNTPOINT", - Value: instance.Spec.PaiSecret.MountLocation, - }) - } - } - if instance.Spec.PaiEnableAuthentication { - envs = append(envs, corev1.EnvVar{ - Name: "OML_AUTHENTICATION_ENABLED", - Value: "true", - }) - } else { - envs = append(envs, corev1.EnvVar{ - Name: "OML_AUTHENTICATION_ENABLED", - Value: "false", - }) - } - - return envs -} - -func getOwnerRefPrivateAI(instance *privateaiv4.PrivateAi) []metav1.OwnerReference { - return []metav1.OwnerReference{ - *metav1.NewControllerRef(instance, privateaiv4.GroupVersion.WithKind("PrivateAI")), - } -} - -func BuildServiceDefForPrivateAi(instance *privateaiv4.PrivateAi, svctype string) *corev1.Service { - //service := &corev1.Service{} - service := &corev1.Service{ - ObjectMeta: buildSvcObjectMetaForPrivateAi(instance, svctype), - Spec: corev1.ServiceSpec{}, - } - - // Check if user want External Svc on each replica pod - if svctype == "external" { - service.Spec.Type = corev1.ServiceTypeLoadBalancer - service.Spec.Selector = buildLabelsForPrivateAi(instance, "privateai", instance.Name) - // internal LB condition - if instance.Spec.PaiInternalLB { - if service.ObjectMeta.Annotations == nil { - service.ObjectMeta.Annotations = make(map[string]string) - } - - // Use custom annotations if provided - if len(instance.Spec.PailbAnnotation) > 0 { - for k, v := range instance.Spec.PailbAnnotation { - service.ObjectMeta.Annotations[k] = v - } - } - - // default internal LB annotations if not already set - if _, ok := service.ObjectMeta.Annotations["oci.oraclecloud.com/load-balancer-type"]; !ok { - service.ObjectMeta.Annotations["oci.oraclecloud.com/load-balancer-type"] = "lb" - } - if _, ok := service.ObjectMeta.Annotations["service.beta.kubernetes.io/oci-load-balancer-internal"]; !ok { - service.ObjectMeta.Annotations["service.beta.kubernetes.io/oci-load-balancer-internal"] = "true" - } - } - } - - if svctype == "local" { - service.Spec.ClusterIP = corev1.ClusterIPNone - service.Spec.Selector = buildLabelsForPrivateAi(instance, "privateai", instance.Name) - } - - // build Service Ports Specs to be exposed. If the PortMappings is not set then default ports will be exposed. - service.Spec.Ports = buildSvcPortsDef(instance) - if instance.Spec.PaiLBIP != "" { - service.Spec.LoadBalancerIP = instance.Spec.PaiLBIP - } - return service -} - -// Function to build Service ObjectMeta -func buildSvcObjectMetaForPrivateAi(instance *privateaiv4.PrivateAi, svctype string) metav1.ObjectMeta { - // building objectMeta - var svcName string - if svctype == "local" { - svcName = instance.Name - } - - if svctype == "external" { - svcName = instance.Name + "-svc" // consistent single svc name - } - - objmeta := metav1.ObjectMeta{ - Name: svcName, - Namespace: instance.Namespace, - OwnerReferences: getOwnerRef(instance), - Labels: buildSvcLabelsForPrivateAi(instance, svctype, instance.Name), - } - return objmeta -} - -func buildSvcLabelsForPrivateAi(instance *privateaiv4.PrivateAi, svctype, name string) map[string]string { - labelMap := buildLabelsForPrivateAi(instance, "privateai", instance.Name) - labelMap["app.kubernetes.io/servicetype"] = svctype - - return labelMap -} - -// Update Section -func ManageReplicas( - r client.Reader, - instance *privateaiv4.PrivateAi, - kClient client.Client, - Config *rest.Config, - deploy *appsv1.Deployment, - podList *corev1.PodList, - ctx context.Context, - req ctrl.Request, - logger logr.Logger, -) (ctrl.Result, error) { - var desired int32 = 1 - if instance.Spec.Replicas > 0 { - desired = instance.Spec.Replicas - } - - current := *deploy.Spec.Replicas - - if deploy.Spec.Replicas != nil && current != desired { - LogMessages("DEBUG", "Deployment replicas mismatch. Updating deployment...", nil, instance, logger) - - newDeploy := BuildDeploySetForPrivateAI(instance) - newDeploy.Spec.Replicas = &desired - err := kClient.Update(context.Background(), newDeploy) - if err != nil { - LogMessages("ERROR", "Failed to update Deployment with new replica count", err, instance, logger) - return ctrl.Result{}, err - } - } - - // Re-mark pods based on diff - diff := current - desired - - for i := range podList.Items { - pod := &podList.Items[i] - - if diff > 0 { - // Mark pod as unhealthy - touchCmd := []string{"/bin/touch", "/tmp/unhealthy"} - _, err := ExecCommand(r, Config, pod.Name, pod.Namespace, pod.Spec.Containers[0].Name, ctx, req, false, touchCmd) - if err != nil { - LogMessages("ERROR", "Failed to mark pod as unhealthy", err, instance, logger) - return ctrl.Result{}, err - } - diff-- - } else { - // Heal pod if previously unready - removeCmd := []string{"rm", "-f", "/tmp/unhealthy"} - _, err := ExecCommand(r, Config, pod.Name, pod.Namespace, pod.Spec.Containers[0].Name, ctx, req, false, removeCmd) - if err != nil { - LogMessages("ERROR", "Failed to heal pod back to ready state", err, instance, logger) - return ctrl.Result{}, err - } - } - } - - return ctrl.Result{}, nil -} - -// Update Section -func UpdateDeploySetForPrivateAI( - instance *privateaiv4.PrivateAi, - paiSpec privateaiv4.PrivateAiSpec, - kClient client.Client, - Config *rest.Config, - deploy *appsv1.Deployment, - pod *corev1.Pod, - logger logr.Logger, -) (ctrl.Result, error) { - for i := range pod.Spec.Containers { - if pod.Spec.Containers[i].Name == deploy.Name { - contRes := pod.Spec.Containers[i].Resources - paiRes := paiSpec.Resources - if !reflect.DeepEqual(contRes, paiRes) { - LogMessages("DEBUG", "Container resources have changed. Updating deployment...", nil, instance, logger) - - // Update the deployment with new spec - err := kClient.Update(context.Background(), BuildDeploySetForPrivateAI(instance)) - if err != nil { - LogMessages("ERROR", "Failed to update deployment with new spec", err, instance, logger) - return ctrl.Result{}, err - } - } - } - } - - return ctrl.Result{}, nil -} diff --git a/commons/omlai/exec.go b/commons/omlai/exec.go deleted file mode 100644 index 6973490d..00000000 --- a/commons/omlai/exec.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package commons - -import ( - "bytes" - "context" - "fmt" - "strings" - - // TODO - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/remotecommand" -) - -// ExecCMDInContainer execute command in first container of a pod -func ExecCommand( - r client.Reader, - config *rest.Config, - podName string, - namespace string, - containerName string, - ctx context.Context, - req ctrl.Request, - nologCommand bool, - command []string, -) (string, error) { - - log := ctrl.Log.WithValues("ExecCommand", req.NamespacedName) - - if !nologCommand { - log.Info("Executing Command:") - log.Info(strings.Join(command, " ")) - } - - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return "", fmt.Errorf("failed to build clientset: %w", err) - } - - restClient := clientset.CoreV1().RESTClient() - if restClient == nil { - return "", fmt.Errorf("failed to get RESTClient") - } - - // Get the pod to ensure it exists. - pod := &corev1.Pod{} - err = r.Get(ctx, client.ObjectKey{Namespace: namespace, Name: podName}, pod) - if err != nil { - return "", fmt.Errorf("could not find pod to exec in: %w", err) - } - - // Build the exec request - reqExec := restClient.Post(). - Resource("pods"). - Name(podName). - Namespace(namespace). - SubResource("exec"). - VersionedParams(&corev1.PodExecOptions{ - Command: command, - Container: containerName, - Stdout: true, - Stderr: true, - }, scheme.ParameterCodec) - - exec, err := remotecommand.NewSPDYExecutor(config, "POST", reqExec.URL()) - if err != nil { - return "", fmt.Errorf("failed to create executor: %w", err) - } - - var stdout, stderr bytes.Buffer - err = exec.Stream(remotecommand.StreamOptions{ - Stdout: &stdout, - Stderr: &stderr, - Tty: false, - }) - if err != nil { - return stdout.String(), fmt.Errorf("stream error: %w, stderr: %s", err, stderr.String()) - } - - return stdout.String(), nil -} From ca1730fdf92ac934bb724e0c14c4da28eefc8b89 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 8 Sep 2025 05:02:13 +0000 Subject: [PATCH 319/414] Added fixes --- apis/database/v4/oraclerestart_webhook.go | 2 +- ...database.oracle.com_shardingdatabases.yaml | 2 + config/rbac/role.yaml | 26 ------------ config/webhook/manifests.yaml | 40 ------------------- 4 files changed, 3 insertions(+), 67 deletions(-) diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index b3d6f147..abf294a4 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -258,7 +258,7 @@ func (r *OracleRestart) ValidateUpdate(ctx context.Context, oldObj, newObj runti newCr.Name, fmt.Errorf("updates to the storageclass is forbidden: %s", old.Spec.StorageClass)) } - if old.Spec.InstDetails.SwLocStorageSizeInGb < newCr.Spec.InstDetails.SwLocStorageSizeInGb { + if newCr.Spec.InstDetails.SwLocStorageSizeInGb < old.Spec.InstDetails.SwLocStorageSizeInGb { return nil, apierrors.NewForbidden( schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 8917a8b8..c25b642c 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -851,6 +851,8 @@ spec: type: string role: type: string + ruMode: + type: string sessionState: type: string sqlTransactionProfile: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 0282587c..e6086c1d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -217,32 +217,6 @@ rules: - get - patch - update -- apiGroups: - - omlai.oracle.com - resources: - - privateais - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - omlai.oracle.com - resources: - - privateais/finalizers - verbs: - - update -- apiGroups: - - omlai.oracle.com - resources: - - privateais/status - verbs: - - get - - patch - - update - apiGroups: - storage.k8s.io resources: diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 58a5d2b1..43539626 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -249,26 +249,6 @@ webhooks: resources: - databaseobservers sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-omlai-oracle-com-v4-privateai - failurePolicy: Fail - name: mprivateai-v4.kb.io - rules: - - apiGroups: - - omlai.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - privateais - sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -664,23 +644,3 @@ webhooks: resources: - databaseobservers sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /validate-omlai-oracle-com-v4-privateai - failurePolicy: Fail - name: vprivateai-v4.kb.io - rules: - - apiGroups: - - omlai.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - privateais - sideEffects: None From a48d1eb45002c51fba7a96034ca603f7d7b6e3fc Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Mon, 8 Sep 2025 19:15:07 +0000 Subject: [PATCH 320/414] oneoff fix --- apis/database/v4/oraclerestart_types.go | 3 +- .../database.oracle.com_oraclerestarts.yaml | 12 +- .../database/oraclerestart_controller.go | 88 ++++++--- docs/oraclerestart/README.md | 16 +- .../provisioning/oraclerestart_prov_lb.yaml | 106 +++++++++++ .../oraclerestart_prov_onsport.yaml | 28 ++- ...tart_prov_rupatch_oneoff_storageclass.yaml | 154 ++++++++++++++++ .../provisioning/orestart_oneoffs_object.txt | 169 ++++++++++++++++++ .../provisioning_oracle_restart_db_lb.md | 44 +++++ .../provisioning_oracle_restart_db_onsport.md | 41 +---- ...oning_oracle_restart_db_rupatch_oneoffs.md | 46 +++++ oracle-database-operator.yaml | 14 +- 12 files changed, 621 insertions(+), 100 deletions(-) create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_lb.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml create mode 100644 docs/oraclerestart/provisioning/orestart_oneoffs_object.txt create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_db_lb.md create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 1fd58ea1..53e5d196 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -139,7 +139,8 @@ type InitParams struct { SwStagePvc string `json:"swStagePvc,omitempty"` SwStagePvcMountLocation string `json:"swStagePvcMountLocation,omitempty"` OneOffLocation string `json:"oneOffLocation,omitempty"` - OneOffIds string `json:"oneOffIds,omitempty"` + DbOneOffIds string `json:"dbOneOffIds,omitempty"` + GridOneOffIds string `json:"gridOneOffIds,omitempty"` } type OracleRestartInstDetailSpec struct { diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index 5612f00f..aab8cb3d 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -87,6 +87,8 @@ spec: type: string dbName: type: string + dbOneOffIds: + type: string dbRecoveryFileDest: type: string dbRecoveryFileDestSize: @@ -114,6 +116,8 @@ spec: type: string gridHome: type: string + gridOneOffIds: + type: string gridResponseFile: properties: configMapName: @@ -131,8 +135,6 @@ spec: type: string oPatchSwZipFile: type: string - oneOffIds: - type: string oneOffLocation: type: string opType: @@ -867,6 +869,8 @@ spec: type: string dbName: type: string + dbOneOffIds: + type: string dbRecoveryFileDest: type: string dbRecoveryFileDestSize: @@ -894,6 +898,8 @@ spec: type: string gridHome: type: string + gridOneOffIds: + type: string gridResponseFile: properties: configMapName: @@ -911,8 +917,6 @@ spec: type: string oPatchSwZipFile: type: string - oneOffIds: - type: string oneOffLocation: type: string opType: diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 05ed2098..c465a2b1 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -375,7 +375,10 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques case isNewSetup || !isDiskChanged: cmName := oracleRestart.Spec.InstDetails.Name + oracleRestart.Name + "-cmap" cm := oraclerestartcommon.ConfigMapSpecs(oracleRestart, configMapData, cmName) - result, err = r.createConfigMap(ctx, oracleRestart, cm) + result, configmapEnvKeyChanged, err := r.createConfigMap(ctx, *oracleRestart, cm) + if err != nil { + // handle error + } if err != nil { result = resultNq return result, err @@ -388,7 +391,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques oracleRestart.Spec.InstDetails.EnvFile = cmName dep := oraclerestartcommon.BuildStatefulSetForOracleRestart(oracleRestart, oracleRestart.Spec.InstDetails, r.Client) - result, err = r.createOrReplaceSfs(ctx, req, *oracleRestart, dep, index, isLast, oldState) + result, err = r.createOrReplaceSfs(ctx, req, *oracleRestart, dep, index, isLast, oldState, configmapEnvKeyChanged) if err != nil { result = resultNq return result, err @@ -482,11 +485,10 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } } - if oracleRestart.Spec.EnableOns == "enable" { + if oracleRestart.Spec.EnableOns == "enable" || oracleRestart.Spec.EnableOns == "disable" { OraRestartSpex := oracleRestart.Spec.InstDetails orestartSfSet, err := oraclerestartcommon.CheckSfset(OraRestartSpex.Name, oracleRestart, r.Client) if err != nil { - //msg := "Unable to find Oracle Restart statefulset " + oraclerestartcommon.GetFmtStr(OraRestartSpex.Name) + "." r.updateOracleRestartInstStatus(oracleRestart, ctx, req, OraRestartSpex, string(oraclerestartdb.StatefulSetNotFound), r.Client, false) return ctrl.Result{}, err } @@ -496,8 +498,13 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques r.Log.Error(err, "Failed to list pods") return ctrl.Result{}, err } + // default is to start + onsOp := "start" + if oracleRestart.Spec.EnableOns == "disable" { + onsOp = "stop" + } - err = r.updateONS(ctx, podList, oracleRestart, "start") + err = r.updateONS(ctx, podList, oracleRestart, onsOp) if err != nil { return ctrl.Result{}, err } @@ -1832,9 +1839,6 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or var addnodeFlag bool data = append(data, "OP_TYPE=setuprac") - // removing this breaks HAS label build setup - // data = append(data, "IGNORE_CRS_PREREQS=TRUE") - // data = append(data, "IGNORE_DB_PREREQS=TRUE") // --- Pick ALL envVars directly from CR spec --- for _, e := range instance.Spec.InstDetails.EnvVars { data = append(data, fmt.Sprintf("%s=%s", e.Name, e.Value)) @@ -1985,8 +1989,12 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or if instance.Spec.ConfigParams.OneOffLocation != "" { data = append(data, "ONEOFF_FOLDER_NAME="+instance.Spec.ConfigParams.OneOffLocation) } - if instance.Spec.ConfigParams.OneOffIds != "" { - data = append(data, "ONEOFF_IDS="+instance.Spec.ConfigParams.OneOffIds) + if instance.Spec.ConfigParams.DbOneOffIds != "" { + data = append(data, "DB_ONEOFF_IDS="+instance.Spec.ConfigParams.DbOneOffIds) + } + + if instance.Spec.ConfigParams.GridOneOffIds != "" { + data = append(data, "GRID_ONEOFF_IDS="+instance.Spec.ConfigParams.GridOneOffIds) } if instance.Spec.ConfigParams.DbSwZipFile != "" { @@ -2137,36 +2145,46 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or // Create the configmap -func (r *OracleRestartReconciler) createConfigMap(ctx context.Context, instance *oraclerestartdb.OracleRestart, cm *corev1.ConfigMap) (ctrl.Result, error) { +func (r *OracleRestartReconciler) createConfigMap( + ctx context.Context, + instance oraclerestartdb.OracleRestart, + cm *corev1.ConfigMap, +) (ctrl.Result, bool, error) { // Added `bool` return reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) found := &corev1.ConfigMap{} - err := r.Get(ctx, types.NamespacedName{ Name: cm.Name, Namespace: instance.Namespace, }, found) - if err != nil && apierrors.IsNotFound(err) { - // Create the Service + // ConfigMap does not exist - create it reqLogger.Info("Creating Configmap Normally") - err = r.Create(ctx, cm) - if err != nil { - // Service creation failed - reqLogger.Error(err, "failed to create configmap", " namespace", instance.Namespace) - return ctrl.Result{}, nil - } else { - // Service creation was successful - return ctrl.Result{Requeue: true}, nil + if err = r.Create(ctx, cm); err != nil { + reqLogger.Error(err, "failed to create configmap", "namespace", instance.Namespace) + return ctrl.Result{}, false, err } + return ctrl.Result{Requeue: true}, true, nil // Indicate configmap was created } else if err != nil { - // Error that isn't due to the Service not existing - reqLogger.Error(err, "failed to find the configmap details") - return ctrl.Result{}, err + // Error getting ConfigMap + reqLogger.Error(err, "failed to find the configmap details") + return ctrl.Result{}, false, err } - return ctrl.Result{}, nil + // At this point, ConfigMap exists: found + // Compare data and update if needed only for environment variables changes + if found.Data["envfile"] != cm.Data["envfile"] { + reqLogger.Info("ConfigMap env key changed, updating") + found.Data["envfile"] = cm.Data["envfile"] + if err := r.Update(ctx, found); err != nil { + reqLogger.Error(err, "failed to update configmap", "namespace", instance.Namespace) + return ctrl.Result{}, false, err + } + return ctrl.Result{Requeue: true}, true, nil // Indicate data was changed + } + // No changes needed + return ctrl.Result{}, false, nil } // This function create a service based isExtern parameter set in the yaml file @@ -2561,6 +2579,7 @@ func (r *OracleRestartReconciler) createOrReplaceSfs( index int, isLast bool, oldState string, + configmapChanged bool, ) (ctrl.Result, error) { reqLogger := r.Log.WithValues("Instance.Namespace", oracleRestart.Namespace, "Instance.Name", oracleRestart.Name) found := &appsv1.StatefulSet{} @@ -2592,11 +2611,13 @@ func (r *OracleRestartReconciler) createOrReplaceSfs( reqLogger.Error(err, "Failed to find the StatefulSet details") return ctrl.Result{}, err } else { - // UPDATE only if resource requirements have changed + // Compare resource requirements foundRes := found.Spec.Template.Spec.Containers[0].Resources depRes := dep.Spec.Template.Spec.Containers[0].Resources + resourcesChanged := !reflect.DeepEqual(foundRes, depRes) - if !reflect.DeepEqual(foundRes, depRes) { + // Compare configMap relevant data (example: pass in variable configmapChanged) + if resourcesChanged || configmapChanged { // Copy metadata fields that must be preserved dep.ResourceVersion = found.ResourceVersion dep.UID = found.UID @@ -2604,7 +2625,16 @@ func (r *OracleRestartReconciler) createOrReplaceSfs( dep.ManagedFields = found.ManagedFields dep.Status = found.Status - reqLogger.Info("Updating StatefulSet due to resource change", "StatefulSetName", dep.Name) + reason := "unknown" + if resourcesChanged && configmapChanged { + reason = "resource and configmap change" + } else if resourcesChanged { + reason = "resource change" + } else if configmapChanged { + reason = "configmap change" + } + + reqLogger.Info("Updating StatefulSet due to "+reason, "StatefulSetName", dep.Name) err = r.Update(ctx, dep) if err != nil { oracleRestart.Spec.IsFailed = true diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md index 0172b35b..13d8b065 100644 --- a/docs/oraclerestart/README.md +++ b/docs/oraclerestart/README.md @@ -42,13 +42,15 @@ In order to become familiar with Oracle Restart on containers, you can refer [th Deploy Oracle Restart Database YAML files using Kubernetes Cluster on your Oracle Kubernetes Engine Environment (OKE). There are multiple use case possible for deploying the Oracle Restart Database. [1. Provisioning an Oracle Restart Database](./provisioning/provisioning_oracle_restart_db.md) -[2. Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) -[3. Provisioning an Oracle Restart Database with OnsPort Service](./provisioning/provisioning_oracle_restart_db_onsport.md) -[4. Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning/provisioning_oracle_restart_db_rupatch.md) -[5. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) -[6. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) -[7. Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database](./provisioning/add_asm_disk_to_an_existing_restart_database.md) -[8. Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database](./provisioning/delete_asm_disks_from_an_existing_restart_database.md) +[2. Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) +[3. Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning/provisioning_oracle_restart_db_rupatch.md) +[4. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) +[5. Provisioning an Oracle Restart Database with Load Balancer Service](./provisioning/provisioning_oracle_restart_db_lb.md) +[6. Provisioning an Oracle Restart Database with OnsPort Service](./provisioning/provisioning_oracle_restart_db_onsport.md) +[7. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) +[8. Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database](./provisioning/add_asm_disk_to_an_existing_restart_database.md) +[9. Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database](./provisioning/delete_asm_disks_from_an_existing_restart_database.md) +[10. Provisioning an Oracle Restart Database with RU Patch and One Offs with Custom Storage Class](./provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md) ## Connecting to Oracle Restart Database diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_lb.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_lb.yaml new file mode 100644 index 00000000..b0d68399 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_lb.yaml @@ -0,0 +1,106 @@ +# API version for the custom resource definition +apiVersion: database.oracle.com/v4 + +# Kind of resource being defined +kind: OracleRestart + +# Metadata section includes the name and namespace of the resource +metadata: + # Name of this OracleRestart resource instance + name: oraclerestart-sample + # Kubernetes namespace where this resource resides + namespace: orestart + +# Specification for the OracleRestart resource +spec: + # Details about the Oracle instance + instDetails: + # Name of the Oracle instance + name: dbmc1 + # Path where Oracle software will be installed on the host + hostSwLocation: /scratch/orestart/ + # List of worker node IPs where instance will be deployed + workerNode: + - 10.0.10.108 + envVars: + - name: IGNORE_CRS_PREREQS + value: "true" + - name: IGNORE_DB_PREREQS + value: "true" + # ASM (Automatic Storage Management) disk configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Total size of disk group in GB + diskNames: # List of physical disk paths to be used + - /dev/oracleoci/oraclevdd + - /dev/oracleoci/oraclevde + + # SSH key secret references used for host access + sshKeySecret: + name: ssh-key-secret # Name of the Kubernetes secret + privKeySecretName: ssh-privkey # Private SSH key secret + pubKeySecretName: ssh-pubkey # Public SSH key secret + + # Database user credentials secret + dbSecret: + name: db-user-pass-pkutl # Name of secret containing DB credentials + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Container image for Oracle Restart + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0729 + + # Policy to always pull the image when starting + imagePullPolicy: Always + + # Details of the database service + serviceDetails: + name: soepdb # Name of the database service + + # Kubernetes resource requests and limits + resources: + requests: + memory: "16Gi" # Memory requested by container + cpu: "2" # CPUs requested by container + limits: + memory: "16Gi" # Maximum memory allowed + cpu: "2" # Maximum CPUs allowed + + # OS-level kernel parameters to be set in the container + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" # Shared memory pages + - name: kernel.sem + value: "250 32000 100 128" # Semaphore settings + - name: kernel.shmmax + value: "8589934592" # Max shared memory segment (8GB) + - name: kernel.shmmni + value: "4096" # Number of shared memory segments + lbService: + name: orestartsvc + svcType: lbservice + portMappings: + - port: 1521 + targetPort: 1521 + protocol: TCP + - port: 6200 + targetPort: 6200 + protocol: TCP + # Configuration parameters for Oracle software + configParams: + gridHome: "/u01/app/19c/grid" # Path to Grid Infrastructure home + gridBase: "/u01/app/grid" # Base directory for Grid Infrastructure + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle Database home + dbBase: "/u01/app/oracle" # Oracle Database base directory + crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # ASM device list + inventory: "/u01/app/oraInventory" # Oracle inventory location + gridSwZipFile: "grid_home.zip" # Grid software zip filename + dbSwZipFile: "db_home.zip" # DB software zip filename + sgaSize: "3G" # Size of SGA (System Global Area) + pgaSize: "1G" # Size of PGA (Program Global Area) + processes: 2000 # Maximum number of DB processes + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Name of the Oracle database + hostSwStageLocation: "/scratch/software/19c/19.3.0/" # Location to stage installation software + ruPatchLocation: /scratch/software/19c/19.28/ # Location for Release Update patch \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml index c2822c01..ea4b8ac6 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml @@ -19,28 +19,14 @@ spec: name: dbmc1 # Path where Oracle software will be installed on the host hostSwLocation: /scratch/orestart/ - # Target port for Oracle Notification Services (ONS) - onsTargetPort: 30200 # List of worker node IPs where instance will be deployed workerNode: - 10.0.10.108 - envVars: - name: IGNORE_CRS_PREREQS value: "true" - name: IGNORE_DB_PREREQS value: "true" - # NodePort service configuration - nodePortSvc: - - svcType: nodeport # Type of service (NodePort) - name: dbmc1 # Name of the NodePort service - # Port mappings between container and host - portMappings: - - port: 1521 # Internal port for Oracle DB - targetPort: 1521 # Target port inside the container - protocol: TCP # Protocol used - nodePort: 30007 # Exposed NodePort on host - # ASM (Automatic Storage Management) disk configuration asmStorageDetails: disksBySize: @@ -91,7 +77,19 @@ spec: value: "8589934592" # Max shared memory segment (8GB) - name: kernel.shmmni value: "4096" # Number of shared memory segments - + lbService: + name: orestartsvc + svcType: lbservice + onsTargetPort: 30200 + portMappings: + - port: 1521 + targetPort: 1521 + protocol: TCP + - port: 6200 + targetPort: 6200 + protocol: TCP + # Target port for Oracle Notification Services (ONS) + enableOns: "enable" # Configuration parameters for Oracle software configParams: gridHome: "/u01/app/19c/grid" # Path to Grid Infrastructure home diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml new file mode 100644 index 00000000..31f7058f --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml @@ -0,0 +1,154 @@ +# API version for the Oracle Database operator +apiVersion: database.oracle.com/v4 +# Specifies the resource type +kind: OracleRestart +# Metadata section for naming and scoping +metadata: + # Name of the OracleRestart instance + name: oraclerestart-sample + # Kubernetes namespace to deploy in + namespace: orestart +# Specification for the OracleRestart custom resource +spec: + # Instance details section + instDetails: + # The logical/cluster name for the instance + name: dbmc1 + # List of worker nodes IPs for deployment + workerNode: + - 10.0.10.108 + # Storage size for Oracle software location, in GB + swLocStorageSizeInGb: 300 + # Environment variables passed to the container + envVars: + # Ignore CRS prerequisites + - name: IGNORE_CRS_PREREQS + value: "true" + # Ignore Database prerequisites + - name: IGNORE_DB_PREREQS + value: "true" + # StorageClass for block volumes + storageClass: "oci-bv" + # ASM Storage configuration for Oracle Automatic Storage Management + asmStorageDetails: + # List ASM disks by size in GB + disksBySize: + - storageSizeInGb: 50 + # Physical device names for ASM disks + diskNames: + - /dev/asm-disk1 + - /dev/asm-disk2 + # SSH key secrets (for setup and access) + sshKeySecret: + # Kubernetes secret containing SSH keys + name: ssh-key-secret + # Secret name for private key + privKeySecretName: ssh-privkey + # Secret name for public key + pubKeySecretName: ssh-pubkey + # Database user/password secret info + dbSecret: + # Secret containing DB user credentials + name: db-user-pass-pkutl + # Private key filename (for decrypting password) + keyFileName: key.pem + # Encrypted password file name + pwdFileName: pwdfile.enc + # Database Docker image to use + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-oneoff + # Pull policy for the image: always pull + imagePullPolicy: Always + # Details for the Oracle service + serviceDetails: + # Oracle DB service name + name: soepdb + # Kubernetes resource requests (guaranteed) for container + resources: + requests: + # Requested memory + memory: "16Gi" + # Requested vCPU + cpu: "2" + # Resource limits (max allowed) + limits: + # Maximum memory + memory: "16Gi" + # Maximum CPU + cpu: "2" + # Define sysctls for the container security context + securityContext: + # Kernel parameter: max shared memory pages + sysctls: + - name: kernel.shmall + value: "2097152" + # Kernel parameter: semaphores + - name: kernel.sem + value: "250 32000 100 128" + # Kernel parameter: max shared memory size (bytes) + - name: kernel.shmmax + value: "8589934592" + # Kernel parameter: max shared memory segments + - name: kernel.shmmni + value: "4096" + # Load balancer service configuration + lbService: + # Service name + name: orestartsvc + # Type of service: load balancer + svcType: lbservice + # Port mappings for service + portMappings: + # Oracle listener port + - port: 1521 + targetPort: 1521 + protocol: TCP + # Additional database port + - port: 6200 + targetPort: 6200 + protocol: TCP + # Extra configuration parameters for grid and database + configParams: + # Grid Infrastructure home path + gridHome: "/u01/app/19c/grid" + # Grid base directory + gridBase: "/u01/app/grid" + # Oracle DB home path + dbHome: "/u01/app/oracle/product/19c/dbhome_1" + # Oracle base directory + dbBase: "/u01/app/oracle" + # Comma-separated list of ASM device paths + crsAsmDeviceList: "/dev/asm-disk1,/dev/asm-disk2" + # Inventory location for Oracle software + inventory: "/u01/app/oraInventory" + # Grid software installation archive + gridSwZipFile: "grid_home.zip" + # Oracle database software archive + dbSwZipFile: "db_home.zip" + # OPatch (Oracle patch tool) archive + oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" + # SGA (System Global Area) size for Oracle DB + sgaSize: "3G" + # PGA (Program Global Area) size for Oracle DB + pgaSize: "1G" + # Maximum number of DB processes + processes: 2000 + # Number of CPUs allocated for DB + cpuCount: 4 + # Oracle database name (CDB) + dbName: "PORCLCDB" + # PVC for software staging area + swStagePvc: pv-stage-vol-claim + # Mount location for the software stage + swStagePvcMountLocation: "/stage" + # Host path for software staging + hostSwStageLocation: "/stage/software/19c/19.3.0" + # RU patch file location + ruPatchLocation: "/stage/software/19c/new/RU/37957391" + # OPatch working directory + oPatchLocation: "/stage/software/19c" + # One-off patch files directory + oneOffLocation: "/stage/software/19c/new/oneoff" + # Comma-separated DB one-off patch IDs + dbOneOffIds: "34436514" # DB one-offs comma separated + # Comma-separated Grid one-off patch IDs + gridOneOffIds: "38336965" # Grid one-offs comma separated \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_oneoffs_object.txt b/docs/oraclerestart/provisioning/orestart_oneoffs_object.txt new file mode 100644 index 00000000..47fe2a8e --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_oneoffs_object.txt @@ -0,0 +1,169 @@ +kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":300,"workerNode":["10.0.10.108"],"envVars":[{"name":"IGNORE_CRS_PREREQS","value":"tr... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-09-07T17:23:34Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 30411716 + UID: 588ce846-5b82-4b61-b0fc-e281e7ba1433 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/asm-disk1 + /dev/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db One Off Ids: 34436514 + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid One Off Ids: 38336965 + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /stage/software/19c/19.3.0 + Inventory: /u01/app/oraInventory + O Patch Location: /stage/software/19c + O Patch Sw Zip File: p6880880_190000_Linux-x86-64.zip + One Off Location: /stage/software/19c/new/oneoff + Pga Size: 1G + Processes: 2000 + Ru Patch Location: /stage/software/19c/new/RU/37957391 + Sga Size: 3G + Sw Mount Location: /u01 + Sw Stage Pvc: pv-stage-vol-claim + Sw Stage Pvc Mount Location: /stage + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Enable Ons: enable + Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-oneoff + Image Pull Policy: Always + Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Name: dbmc1 + Sw Loc Storage Size In Gb: 300 + Worker Node: + 10.0.10.108 + Lb Service: + Name: orestartsvc + Port Mappings: + Port: 1521 + Protocol: TCP + Target Port: 1521 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: lbservice + Node Port Svc: + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey + Storage Class: oci-bv +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/asm-disk1 + /dev/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/asm-disk1,/dev/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-09-07T19:59:57Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-09-08T06:44:04Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: Pending + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_lb.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_lb.md new file mode 100644 index 00000000..05c08289 --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_lb.md @@ -0,0 +1,44 @@ +# Provisioning an Oracle Restart Database with Load Balancer Service Enabled + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. +The responsefile is generated by the controller based on input parameters specified in the .yaml file. In this use case, we are deploying the Oracle Restart with Load Balancer Service + +This example uses `oraclerestart_prov_lb.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +In this example, + * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). + * When you are building the image yourself, update the image value in the `oraclerestart_prov_lb.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + + +Use the file: [oraclerestart_prov_lb.yaml](./oraclerestart_prov_lb.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_lb.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_lb.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_lb_object.txt) +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md index c31b37c7..71259abb 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md @@ -1,4 +1,4 @@ -# Provisioning an Oracle Restart Database with NodePort Service +# Provisioning an Oracle Restart Database with OnsPort Enabled In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the controller based on input parameters specified in the .yaml file. In this use case, we are deploying the Oracle Restart with Node Port Service for the ONS. @@ -42,42 +42,3 @@ Use the file: [oraclerestart_prov_onsport.yaml](./oraclerestart_prov_onsport.yam ``` 3. Check Details of Kubernetes CRD Object as in this [example](./orestart_ons_object.txt) 4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. -` to provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). - * When you are building the image yourself, update the image value in the `oraclerestart_prov_onsport.yaml` file to point to the container image you have built. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. - - -Use the file: [oraclerestart_prov_onsport.yaml](./oraclerestart_prov_onsport.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_onsport.yaml` file: - ```sh - kubectl apply -f oraclerestart_prov_onsport.yaml - oraclerestart.database.oracle.com/oraclerestart-sample created - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n orestart - - # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" - =============================== - ORACLE DATABASE IS READY TO USE - =============================== - ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_nodeport_object.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md new file mode 100644 index 00000000..33fe8631 --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md @@ -0,0 +1,46 @@ +# Provisioning an Oracle Restart Database with RU Patch and One Offs with Custom Storage Class + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is +generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. + +This example uses `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Persistent volumes created automatically based on specified disks for Oracle Restart storage +* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. +* Directory where the **Release Update (RU) patch** has been unzipped as `ruPatchLocation`. This folder **must contain `PatchSearch.xml`**, which is used by the installer to detect and apply the patch. Example: `ruPatchLocation: "/stage/software/19c/new/RU/37957391"` +* Directory where latest opatch zip file is kept e.g `oPatchLocation: "/stage/software/19c"` +* Directory where One-off patch files is kept e.g `oneOffLocation: "/stage/software/19c/new/oneoff"` +* Comma-separated DB one-off patch IDs to be applied e.g `dbOneOffIds: "34436514"` +* Comma-separated Grid one-off patch IDs to be applied e.g `gridOneOffIds: "38336965"` + +In this example, + * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` file to point to your own container registry base container image. + * The disks procured on the containers using customer storage class for the Oracle Restart storage are `/dev/asm-disk1` and `/dev/asm-disk2`. + * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + + +Use the file: [oraclerestart_prov_rupatch_oneoff_storageclass.yaml](./oraclerestart_prov_rupatch_oneoff_storageclass.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_rupatch_oneoff_storageclass.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_oneoffs_object.txt) \ No newline at end of file diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index efa48922..b13f6e37 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10689,6 +10689,8 @@ spec: type: string dbName: type: string + dbOneOffIds: + type: string dbRecoveryFileDest: type: string dbRecoveryFileDestSize: @@ -10716,6 +10718,8 @@ spec: type: string gridHome: type: string + gridOneOffIds: + type: string gridResponseFile: properties: configMapName: @@ -10733,8 +10737,6 @@ spec: type: string oPatchSwZipFile: type: string - oneOffIds: - type: string oneOffLocation: type: string opType: @@ -11469,6 +11471,8 @@ spec: type: string dbName: type: string + dbOneOffIds: + type: string dbRecoveryFileDest: type: string dbRecoveryFileDestSize: @@ -11496,6 +11500,8 @@ spec: type: string gridHome: type: string + gridOneOffIds: + type: string gridResponseFile: properties: configMapName: @@ -11513,8 +11519,6 @@ spec: type: string oPatchSwZipFile: type: string - oneOffIds: - type: string oneOffLocation: type: string opType: @@ -14036,6 +14040,8 @@ spec: type: string role: type: string + ruMode: + type: string sessionState: type: string sqlTransactionProfile: From 351b31939ffeb93cab1b24d6975240a1078589bb Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 8 Sep 2025 19:20:00 +0000 Subject: [PATCH 321/414] Added fixes --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index dc3d8655..8a56918c 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( k8s.io/cli-runtime v0.34.0 k8s.io/client-go v0.34.0 k8s.io/kubectl v0.34.0 - k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d + k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect sigs.k8s.io/controller-runtime v0.22.0 sigs.k8s.io/yaml v1.6.0 ) From fa3f64f0f97cc2ed5c4744ba045e9f01da9d8daf Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 19:58:10 +0000 Subject: [PATCH 322/414] Update ACD.md minor edit. --- docs/adb/ACD.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adb/ACD.md b/docs/adb/ACD.md index 81ee1a65..5f06eb56 100644 --- a/docs/adb/ACD.md +++ b/docs/adb/ACD.md @@ -1,10 +1,10 @@ # Managing Oracle Autonomous Container Databases on Dedicated Exadata Infrastructure -Oracle Database Operator for Kubernetes (`OraOperator`) includes the Oracle Autonomous Container Database Controller. Autonomous Container Database is one of the resources of Oracle Autonomous Database dedicated Exadata infrastructure feature. You can create multiple Autonomous Container Database resources in a single Autonomous Exadata VM Cluster resource, but you must create at least one before you can create any Autonomous Databases. +Oracle Database Operator for Kubernetes (`OraOperator`) includes the Oracle Autonomous Container Database Controller. Autonomous Container Database is one of the resources of the Oracle Autonomous Database on Dedicated Exadata Infrastructure feature. You can create multiple Autonomous Container Database resources in a single Autonomous Exadata VM Cluster resource, but you must create at least one before you can create any Autonomous Databases. -Before you use the Oracle Database Operator for Kubernetes (the operator), ensure your system meets all of the Oracle Autonomous Database (ADB) Prerequisites [ADB_PREREQUISITES](./../adb/ADB_PREREQUISITES.md). +Before you use the Oracle Database Operator for Kubernetes (the operator), ensure that your system meets all of the Oracle Autonomous Database (ADB) Prerequisites [ADB_PREREQUISITES](./../adb/ADB_PREREQUISITES.md). -As indicated in the prerequisites (see above), to interact with OCI services, either the cluster has to be authorized using Principal Instance, or using the API Key Authentication by specifying the configMap and the secret under the `ociConfig` field. +As indicated in the prerequisites (see above), to interact with OCI services, either the cluster must be authorized by using Principal Instance, or by using the API Key Authentication and specifying the configMap and the secret under the `ociConfig` field. ## Required Permissions From 31bae68c3aa7f9a9c3d5c34d538accb0b0a6cddf Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 20:18:57 +0000 Subject: [PATCH 323/414] Update backup_of_database.md minor edits --- docs/dbcs/provisioning/backup_of_database.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/dbcs/provisioning/backup_of_database.md b/docs/dbcs/provisioning/backup_of_database.md index ed948e02..6d17f2ef 100644 --- a/docs/dbcs/provisioning/backup_of_database.md +++ b/docs/dbcs/provisioning/backup_of_database.md @@ -1,20 +1,20 @@ # Create Backup of Existing Database of DB System in OCI OBDS Service -In this use case, an existing OCI OBDS system deployed earlier with existing Database is going to have full manual backup in OCI Base OBDS Service using existing Compartment ID and DB System Id. +In this example, an existing OCI OBDS system previously deployed with an existing Database is updated to have a full manual backup in the OCI Base OBDS Service using the existing Compartment ID and database system ID. -As an pre-requisite, get the details of OCID of database of an existing OBDS System which you want to backup. +To use this example on your system, before you begin, obtain the details of the database OCID for the existing OBDS System which you want to backup. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `backup_of_database.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `backup_of_database.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following configuration: - OCID of existing as DB System as `id` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - enableBackup: as `true` -- Specification of backup prefix as - Details of `backupDisplayName`. This is optional field. If it is not provided, controller will use keyword `backup` as prefix. In both cases of displayName whether its provided or not, suffix of backup name is timestamp to have uniqueness of backup name of database created. -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). +- Specification of backup prefix as - Details of `backupDisplayName`. This field is optional. If it is not provided, then the controller uses the keyword `backup` as the prefix. In both cases of displayName, whether you provide a backup display name or not, the suffix of the backup name is the timestamp. The timestamp ensure that the backup name created for the database is unique. +**NOTE:** For the details of the parameters to be used in the .yaml file, see [DBCS controller parameters](./dbcs_controller_parameters.md). -Use the file: [backup_of_database.yaml](./backup_of_database.yaml) for this use case as below: +Use the file: [backup_of_database.yaml](./backup_of_database.yaml) for this use case. Example: 1. Deploy the .yaml file: ```sh @@ -22,7 +22,7 @@ Use the file: [backup_of_database.yaml](./backup_of_database.yaml) for this use dbcssystem.database.oracle.com/dbcssystem-backup configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of PDBs. +2. Monitor the Oracle Database Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of PDBs. NOTE: Check the DB Operator Pod name in your environment. @@ -32,4 +32,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./backup_database_sample_output.log) is the sample output for backup an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. \ No newline at end of file +[Backup Database Sample Output Log](./backup_database_sample_output.log) is an example output for a backup an existing OBDS System deployed in OCI using the Oracle Database Operator OBDS Controller. \ No newline at end of file From 2dcc18356f5bf7fbecbde35965a4615b2fcde095 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 21:39:23 +0000 Subject: [PATCH 324/414] Update README.md Minor style edits --- docs/observability/README.md | 137 +++++++++++++++++------------------ 1 file changed, 68 insertions(+), 69 deletions(-) diff --git a/docs/observability/README.md b/docs/observability/README.md index 1a0b7d71..a087c892 100644 --- a/docs/observability/README.md +++ b/docs/observability/README.md @@ -77,18 +77,18 @@ The `DatabaseObserver` custom resource has the following prerequisites: kubectl api-resources | grep smon ``` -2. A pre-existing Oracle Database and the proper database grants and privileges. +2. A preexisting Oracle Database, and the proper database grants and privileges. - The controller exports metrics through SQL queries that the user can control - and specify through _toml_ files. The necessary access privileges to the tables used in the queries + and specify through the _toml_ files. The necessary access privileges to the tables used in the queries are not provided and applied automatically. ## The DatabaseObserver Custom Resource -The Oracle Database Operator (__v1.0.0__ or later) includes the Oracle Database Observability controller, which automates -the deployment and setting up of the Oracle Database exporter and the related resources to make Oracle Databases observable. +Oracle Database Operator (__v1.0.0__ or later) includes the Oracle Database Observability controller, which automates +the deployment and configuration of the Oracle Database exporter and the related resources to make Oracle Databases observable. The Observability Controller introduces the `databaseobserver` APIs. -To list the available APIs included in the +To list the available APIs included in the Database Operator, you can run the following command: ```bash @@ -102,7 +102,7 @@ Learn about the different and configurable fields available in this release of t ### Configuration Fields Related to Managing Database Credentials -The following fields below are available for configuring the exporter to successfully connect to your database(s). +The following fields are available for configuring the exporter to successfully connect to databases: | Attribute | Type | Required? | Example | |---------------------------------------------------|--------|:------------|----------------------| @@ -130,25 +130,25 @@ The following fields below are available for configuring the exporter to success | `spec.wallet.additional[].secret` | string | Conditional | _db02-wallet_ | | `spec.wallet.additional[].mountPath` | string | Conditional | _"/wallet/db02"_ | -These fields enable you to define connection details for a single or multiple databases. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). +These fields enable you to define connection details for a single database, or for multiple databases. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). -1. About `spec.database` - For configuring the database username, password and connection string. +1. `spec.database` - Use to configure the database username, password and connection string. The environment variables set by the controller can be customized through the `envName` field. Both `envName` and `key` fields are optional. -2. About `spec.databases` - For configuring multiple database credentials. The keys are used as a prefix for environment -variables, for example, a key of `MYDB` will produce environment variables `MYDB_USERNAME` and `MYDB_PASSWORD`. +2. `spec.databases` - Use to configure multiple database credentials. The keys are used as a prefix for environment +variables. For example, a key of `MYDB` will produce the environment variables `MYDB_USERNAME` and `MYDB_PASSWORD`. -3. About `spec.wallet` - You can configure the wallet with which to connect to the Oracle database, if applicable. The field `mountPath` -allows you to control where the wallet is to be mounted. Meanwhile, additional wallets can be mounted in the array `additional`, used for multi +3. `spec.wallet` - Use to configure the wallet with which you want to connect to the Oracle Database, if applicable. The field `mountPath` +enables you to control where the wallet is to be mounted. Meanwhile, additional wallets can be mounted in the array `additional`, used for multi database configuration. -To learn more about configuring the database connection, proceed to the section on [Connecting to the Database](#connecting-to-the-database). +To learn more about configuring the database connection, see [Connecting to the Database](#connecting-to-the-database). ### Configuration Fields Related to Vault Usage -The following fields below are available for configuring the exporter to use the vault when connecting to your database(s). +The following fields are available for configuring the exporter to use the vault when connecting to databases. | Attribute | Type | Required? | Example | |-------------------------------------------|--------|:------------|-----------------------------------------| @@ -170,17 +170,17 @@ These fields enable you to define vault details for OCI or Azure. For default va > Note: For multiple database configuration with Vault integration, use the exporter config file. See instructions on the [Exporter Config File](#defining-an-exporter-config-file). -2. About `spec.database.azure.` - for configuring the Azure Vault details for the default database. +2. `spec.database.azure.` - Use to configure the Azure Vault details for the default database. -3. About `spec.ociConfig` - for configuring the authentication of requests made to the Oracle Cloud done by the exporter. The configMap should contain -the actual _config_ file use by the OCI CLI (usually found in ~/.oci/config). See the official documentation on [configuring the OCI CLI (external link)](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliconfigure.htm#Configuring_the_CLI). +3. `spec.ociConfig` - Use to configure the authentication of requests made to the Oracle Cloud performed by the exporter. The configMap should contain +the actual _config_ file use by the OCI CLI (usually found in ~/.oci/config). See the Oracle Cloud Infrastructure documentation on [configuring the OCI CLI (external link)](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliconfigure.htm#Configuring_the_CLI). > Note: The OCI profile used is [DEFAULT]. Under the profile DEFAULT, set `key_file=/.oci/.pem`. 4. About `spec.azureConfig` - for configuring the authentication of requests made to the Azure Cloud done by the exporter. The configMap should contain the keys `tenantId`, `clientId` and `clientSecret`, which are used to create the related environment variables. -To learn more about configuring an integration with a vault for database authentication, proceed to the section on [Authenticating with Vaults](#database-authentication-with-vaults-in-the-cloud). +To learn more about configuring an integration with a vault for database authentication, see [Authenticating with Vaults](#database-authentication-with-vaults-in-the-cloud). ### Configuration Fields Related to the Deployment, Pod, Service and ServiceMonitor Resources When you create a `DatabaseObserver` resource, the controller creates and manages the following resources: @@ -209,28 +209,28 @@ The following fields below are available for configuring the controller-managed These fields enable you to define deployment, service and serviceMonitor details. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). -1. About `spec.deployment` - for configuring deployment details such as custom environment variables through `env` and arguments through `args`. +1. `spec.deployment` - Use to configure deployment details such as custom environment variables through `env` and arguments through `args`. The container image can also be replaced with a later or older version of the exporter using the `image` field. For security related configurations, the `securityContext` and `podSecurityContext` are available. -2. About `spec.service` - for configuring a customized service resource. +2. `spec.service` - Use to configure a customized service resource. -3. About `spec.serviceMonitor` - for configuring the serviceMonitor resource. +3. `spec.serviceMonitor` - Use to configure the serviceMonitor resource. -> Note: It is important and required to set the prometheus label inside `serviceMonitor.labels`. Make sure the label set is included in the serviceMonitorSelector field of your Prometheus CR. +> Note: It is an essential requirement that you set the Prometheus label inside `serviceMonitor.labels`. Ensure that the label set is included in the serviceMonitorSelector field of your Prometheus CR. -5. About `spec.replicas` - for configuring the number of replicas to deploy +5. `spec.replicas` - Use to configure the number of replicas to deploy -6. About `spec.inheritLabels` - for configuring all resources created and managed to inherit the labels from databaseobserver CR. +6. `spec.inheritLabels` - Use to configure all resources created and managed so that they inherit the labels from databaseobserver CR. -To learn more about configuring the managed resources like the Deployment, Service and more, proceed to the section on [Customizing Resources and Available Configuration Options](#customizing-resources-and-available-configuration-options) +To learn more about configuring managed resources, such as the Deployment, Service and other services, see [Customizing Resources and Available Configuration Options](#customizing-resources-and-available-configuration-options) ### Configuration Fields Related to Metrics Export -The following fields below are available for configuring the metrics exported from the database. +The following fields are available for configuring the metrics exported from the database. | Attribute | Type | Required? | Example | |---------------------------------|--------|:------------|-------------------------| @@ -238,12 +238,12 @@ The following fields below are available for configuring the metrics exported fr | `spec.metrics.configMap[].name` | string | Conditional | _custom-metrics-config_ | These fields enable you to define configMap sources for metrics. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). -1. About `metrics.configMap[]` - for configuring an array of configs which contain the TOML files. This creates a volume with multiple source files mounted in the same directory. +1. `metrics.configMap[]` - Use to configure an array of configs that will contain the TOML files. This field creates a volume with multiple source files mounted in the same directory. -To learn more about configuring the metrics export, proceed to the section on [Defining an Exporter Config File](#defining-an-exporter-config-file). +To learn more about configuring the metrics export, see [Defining an Exporter Config File](#defining-an-exporter-config-file). ### Configuration Fields Related to Logs Export -The following fields below are available for configuring how the alert.log is exported from the database. +The following fields are available for configuring how the `alert.log` is exported from the database. | Attribute | Type | Required? | Example | |--------------------------------------------------|--------|:------------|--------------| @@ -256,30 +256,29 @@ The following fields below are available for configuring how the alert.log is ex | `spc.sidecar.volumes[]` | array | Conditional | - | These fields enable you to define log details and sidecar resources. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). -1. About `spec.sidecar.containers` - for configuring an array of containers as a sidecar to the observability -exporter container, such as promtail. The field `sidecar.containers` enable you to list containers as you would normally for deployments. +1. `spec.sidecar.containers` - Use to configure an array of containers as a sidecar to the observability +exporter container, such as promtail. The field `sidecar.containers` enables you to list containers as you would normally for deployments. +2. `spec.sidecar.volumes` - Use to configure extra volumes related to your sidecar containers. -2. About `spec.sidecar.volumes` - for configuring extra volumes related to your sidecar containers. +3. `spec.log.disable` - Use to disable the log volume creation -3. About `spec.log.disable` - for disabling the log volume creation +4. `spec.log.filename` - Use to specify a custom filename for the log file -4. About `spec.log.filename` - for specifying a custom filename for the log file +5. `spec.log.destination` - Use to configure a custom destination for the log volume. -5. About `spec.log.destination` - for configuring a custom destination for the log volume. - -6. About `spec.log.volume` - for configuring the log volume in which the logs are extracted into by the exporter and read from. -If a persistentVolumeClaim is not provided, an emptyDir is created instead. Meanwhile, the field `log.volume.name` can be used to define the name of the volume +6. `spec.log.volume` - Use to configure the log volume into which the exporter extracts the logs, and from which the logs are read. +If a persistentVolumeClaim is not provided, then an emptyDir is created instead. Meanwhile, the field `log.volume.name` can be used to define the name of the volume to reference. -To learn more about configuring the log export, proceed to the section on [Scraping Logs](#scraping-logs). +To learn more about configuring the log export, see [Scraping Logs](#scraping-logs). ### Configuration Fields Related to the Exporter Config File -The following fields below are available for configuring the exporter through a config-file. +The following fields are available for configuring the exporter through a config-file: | Attribute | Type | Required? | Example | |-------------------------------------|--------|:------------|-------------------| @@ -288,18 +287,18 @@ The following fields below are available for configuring the exporter through a These fields enable you to define the exporter config-file. For default values, environment variables and default behavior, see [defaults](#environment-variables-and-default-values). -1. About `spec.exporterConfig` - for configuring the exporter with a config file containing database, log and metrics details. You can use +1. `spec.exporterConfig` - Use to configure the exporter with a config file containing database, log and metrics details. You can use the `mountPath` field to define a custom location for the config file. > Note: The CONFIG_FILE environment variable or the --config.file args must be set to the desired location of the config file. -To learn more about configuring the config-file for the exporter, proceed to the section on [Defining an Exporter Config File](#defining-an-exporter-config-file). +To learn more about configuring the config-file for the exporter, see [Defining an Exporter Config File](#defining-an-exporter-config-file). ## DatabaseObserver Operations ### Create Resource -Follow the steps below to create a new `databaseObserver` resource object. +Use the following steps to create a new `databaseObserver` resource object. -1. To begin, creating a `databaseObserver` requires you to create and to provide Kubernetes Secrets containing your database connection details. Replace the values and +1. When you create a `databaseObserver`, you are required to create and to provide Kubernetes Secrets containing your database connection details. Replace the values and create a single secret by running the following command: ```bash kubectl create secret generic db-secret \ @@ -319,7 +318,7 @@ You can also choose to create the wallet secret from a local directory containin kubectl create secret generic db-wallet --from-file= ``` -3. Finally, update the `databaseObserver` manifest with the resources you have created. You can use the example _minimal_ manifest +3. Update the `databaseObserver` manifest with the resources that you have created. You can use the example _minimal_ manifest inside [config/samples/observability/v4](../../config/samples/observability/v4/databaseobserver_minimal.yaml) to specify and create your databaseObserver object with a YAML file. @@ -479,9 +478,9 @@ kubectl create cm config-file --from-file=config.yaml ``` ### Multiple Database Configuration -To configure the observability exporter to export metrics and logs from multiple Oracle Databases, it is required to use an exporter config file, -configure the _databaseobserver_ YAML file with a combined wallet (if applicable) and use the -`spec.databases` field __instead__ of `spec.database`. The field `spec.databases` is a map with keys used for naming environment variables +To configure the observability exporter to export metrics and logs from multiple Oracle Databases, __instead__ of `spec.database`, you must use an exporter config file, +configure the _databaseobserver_ YAML file with a combined wallet (if applicable), and then use the +`spec.databases` field . The field `spec.databases` is a map with keys used for naming environment variables and identifying groups of credentials. For example, to define two databases, in the _databaseobserver_ YAML file, you can have the following configuration: @@ -522,8 +521,8 @@ spec: configMap: name: config-file ``` -Each database is configured under `spec.databases` and multiple wallets are defined in the shared directory `/dbwallet` -as an example. See the next section on [configuring a combined wallet](#configuring-wallets-for-multiple-databases). +Each database is configured under `spec.databases`, and multiple wallets are defined in the shared directory `/dbwallet` +as an example. To configure a combined wallet for multiple databases, see [configuring a combined wallet](#configuring-wallets-for-multiple-databases). In the configuration file _config-file_, db1 and db2 are configured with the credentials provided as environment variables through the __databaseobserver__ YAML file as secrets. @@ -543,7 +542,7 @@ databases: url: db2_tp ``` -Run the following command, to create the configMap: +To create the configMap, run the following command: ```bash kubectl create cm config-file --from-file=config.yaml @@ -565,13 +564,13 @@ create a combined wallet: ``` -3. Place the combined tnsnames.ora file and one of the sqlnet.ora inside a combined directory. +3. Place the combined `tnsnames.ora` file and one of the `sqlnet.ora` files inside a combined directory. -4. Take wallet files (.sso, .p12, .pem) and place them in separate directories. +4. Take wallet files (`.sso`, `.p12`, `.pem`) and place them in separate directories. -The resulting wallet directory structure should look like the following, where wallet files for each database are in separate directories: +The resulting wallet directory structure should look similar to the following, where wallet files for each database are in separate directories: ```bash # example_dbwallet @@ -587,27 +586,27 @@ example_dbwallet ├── ewallet.p12 └── ewallet.pem ``` -Now returning to the YAML file, for this example: +Return to the YAML file for the next example. -5. Set the combined _tnsnames.ora_ is set under `spec.wallet.secret` and the -specific database wallet files (.sso, .p12, .pem) are set under `.spec.wallet.additional`. +5. Set the combined _tnsnames.ora_ under `spec.wallet.secret` and set the +specific database wallet files (.sso, .p12, .pem) under `.spec.wallet.additional`. -6. Finally, set the TNS_ADMIN to the location of the tnsnames.ora. +6. Finally, set the TNS_ADMIN to the location of the `tnsnames.ora`. -> Note: When setting the name under `spec.wallets.additional[].name`, provide a unique name other than `creds`, as this is the default volume name. +> Note: When setting the name under `spec.wallets.additional[].name`, you must provide a unique name other than `creds`, because this is the default volume name. To learn more about this requirement, you can consult the [official documentation of the exporter](https://github.com/oracle/oracle-db-appdev-monitoring?tab=readme-ov-file#configuring-connections-for-multiple-databases-using-oracle-database-wallets). ## Database Authentication with Vaults in the Cloud -Cloud vault resources can be used for storing sensitive database credentials. In this release, the following vaults are supported: +You can use Cloud Vault resources to store sensitive database credentials. In this release, the following vaults are supported: - OCI Vault - Azure Vault ### OCI Vault Configuration The OCI Vault can be used to store the database credentials. This release supports storing the Oracle Database password in the OCI Vault. -Now when configuring the vault, the following are required for you to provide: +When you configure the Vault, you must provide the following: - Vault details: - The string OCID of the Vault used - The string name of the OCI Vault Secret containing the password @@ -619,7 +618,7 @@ The observability exporter needs to authenticate requests to retrieve the databa the OCI CLI config file and the __DEFAULT profile is used__. > Note: The exporter uses the DEFAULT profile. -For example, the OCI CLI config file could appear as follows: +For example, the OCI CLI config file can appear as follows: ```bash [DEFAULT] user= @@ -629,7 +628,7 @@ tenancy= region= ``` -> Note: Ensure the key_file is in `/.oci` and the private key file matches. +> Note: Ensure that the key_file is in `/.oci` and that the private key file matches. You can create the configmap with the following command: ```bash @@ -641,7 +640,7 @@ You can create the secret with the following command: kubectl create secret generic oci-privatekey --from-file=private.pem ``` -Finally, to configure the exporter to use OCI Vault for retrieving the password, you can configure the following fields in the YAML file: +Finally, to configure the exporter to use an OCI Vault for retrieving the password, you can configure the following fields in the YAML file: ```yaml spec: database: @@ -1074,7 +1073,7 @@ spec: ### Security Contexts -The security context defines privilege and access control settings for a pod container, If these privileges and access control setting need to be updated in the pod, then the same field is available on the `databaseObserver` spec. You can set this object under deployment: `spec.deployment.securityContext`. +The security context defines privilege and access control settings for a pod container. If these privileges and access control setting need to be updated in the pod, then the same field is available on the `databaseObserver` spec. You can set this object under deployment: `spec.deployment.securityContext`. ```yaml spec: @@ -1184,15 +1183,15 @@ Follow these steps to check the logs. > Using the Azure Vault to store only the username or the password will likely produce the same error. __WORKAROUND:__
-As the OCI Vault feature is not functioning for v2 to v2.0.2 of the exporter, please use kubernetes secrets. -For Azure users, only retrieval of both username and password from the vault is supported. +Because the OCI Vault feature is not functioning for v2 to v2.0.2 of the exporter, Oracle recomends that you use Kubernetes secrets. +For Azure users, only retrieval of both username and password from the Vault is supported. Retrieving only one (username or password) of the credentials will lead to an error. __WORKAROUND AFTER V2.0.2__: -Once a new version of the exporter is released with the fix, set the field `deployment.image` to the new version of the exporter. +When a new version of the exporter is released with the fix, set the field `deployment.image` to the new version of the exporter. ## Resources -For further information on the Oracle Databases logs and metrics Exporter container image, +For further information about the Oracle Databases logs and metrics Exporter container image, consult the official repository documentations: - [GitHub - Unified Observability for Oracle Database Project](https://github.com/oracle/oracle-db-appdev-monitoring) From a72202b38308119b31bfe95feda78ddf689cca83 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 8 Sep 2025 21:48:27 +0000 Subject: [PATCH 325/414] Added fix for status change and software expansion --- commons/oraclerestart/oraclerestartstatus.go | 6 ++++-- controllers/database/oraclerestart_controller.go | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/commons/oraclerestart/oraclerestartstatus.go b/commons/oraclerestart/oraclerestartstatus.go index a295b32f..8f13fbc3 100644 --- a/commons/oraclerestart/oraclerestartstatus.go +++ b/commons/oraclerestart/oraclerestartstatus.go @@ -40,6 +40,7 @@ package commons import ( "context" + "fmt" "strings" "github.com/go-logr/logr" @@ -114,8 +115,6 @@ func UpdateOracleRestartInstStatusData( orestartNodeDetails.PodState = state orestartNodeDetails.State = "OPEN" oracleRestart.Name = podName - - // orestartNodeDetails.VipDetails = getVipDetails(OracleRestart, oracleRestart, oraRestartSpex, kClient) orestartNodeDetails.InstanceState = getDbInstState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) orestartNodeDetails.MountedDevices = getMountedDevices(podName, OracleRestart.Namespace, oracleRestart, oraRestartSpex, kClient, kubeConfig, logger, kubeClient) oracleRestart.NodeDetails = orestartNodeDetails @@ -192,10 +191,12 @@ func UpdateOracleRestartInstState(instance *oraclerestartdb.OracleRestart, podNa func UpdateoraclerestartdbTopologyState(instance *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, podName string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger) { OracleRestart := &oraclerestartdb.OracleRestart{} + fmt.Printf("I m inUpdateoraclerestartdbTopologyState") if len(instance.Status.OracleRestartNodes) > 0 { state := map[string]struct{}{} for _, v := range instance.Status.OracleRestartNodes { inst_state := strings.ToLower(strings.TrimSpace(v.NodeDetails.State)) + fmt.Printf("inst_state", inst_state) state[inst_state] = struct{}{} } if len(state) == 0 { @@ -216,6 +217,7 @@ func UpdateoraclerestartdbTopologyState(instance *oraclerestartdb.OracleRestart, OracleRestart.Status.State = string(oraclerestartdb.OracleRestartAvailableState) } instance.Status.State = OracleRestart.Status.State + fmt.Printf("instance.Status.State", instance.Status.State) } } diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index c465a2b1..65959118 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -3450,8 +3450,10 @@ func (r *OracleRestartReconciler) updateONS(ctx context.Context, podList *corev1 } func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context, instance *oraclerestartdb.OracleRestart, oldSpec *oraclerestartdb.OracleRestartSpec) error { + fmt.Printf("Received OldSpec", oldSpec.InstDetails.SwLocStorageSizeInGb) if oldSpec != nil { if instance.Spec.InstDetails.SwLocStorageSizeInGb > oldSpec.InstDetails.SwLocStorageSizeInGb { + fmt.Printf("Inside OldSpec and newSpec Change", oldSpec.InstDetails.SwLocStorageSizeInGb, instance.Spec.InstDetails.SwLocStorageSizeInGb) storageClass := &storagev1.StorageClass{} pvc := &corev1.PersistentVolumeClaim{} @@ -3467,6 +3469,8 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context Namespace: instance.Namespace, }, pvc) + fmt.Printf("PvcName set to ", pvc.Name) + if err == nil { if storageClass.AllowVolumeExpansion == nil || !*storageClass.AllowVolumeExpansion { r.Recorder.Eventf(instance, corev1.EventTypeWarning, "PVC not resizable", "The storage class doesn't support volume expansion") @@ -3476,6 +3480,7 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context newPVCSize := resource.MustParse(strconv.Itoa(instance.Spec.InstDetails.SwLocStorageSizeInGb) + "Gi") newPVCSizeAdd := &newPVCSize + fmt.Printf("New PvcSize set to ", newPVCSizeAdd) if newPVCSizeAdd.Cmp(pvc.Spec.Resources.Requests["storage"]) < 0 { r.Recorder.Eventf(instance, corev1.EventTypeWarning, "Cannot Resize PVC", "Forbidden: field can not be less than previous value") return fmt.Errorf("Resizing PVC to lower size volume not allowed") From 5a9cb060ea1eec339d4ed9f3d31bb6db22795ad7 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 21:56:31 +0000 Subject: [PATCH 326/414] Update access_privateai.md minor style edits. --- docs/privateai/access_privateai.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/privateai/access_privateai.md b/docs/privateai/access_privateai.md index ebdd3e6a..c4e84844 100644 --- a/docs/privateai/access_privateai.md +++ b/docs/privateai/access_privateai.md @@ -1,35 +1,35 @@ # Accessing the PrivateAI Container Pod in Kubernetes -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace and you have: -- The Reserved Public IP in case of the Public LoadBalancer -- The Private IP in case of case of the Internal LoadBalancer -- The AI Model details to be used in the URL to access +**IMPORTANT:** To use this example, you must have an existing Oracle PrivateAI Container Deployment in the `pai` namespace, and you must have the following: +- The Reserved Public IP for the Public LoadBalancer +- The Private IP for the Internal LoadBalancer +- The AI Model details that you want to be used in the URL to access ## Accessing the PrivateAI Container in Kubernetes using REST Calls -Please follow the below steps to access: +Complete the following steps: -1. Retrieve API Key +1. Retrieve the API Key -When you have used the file [pai_secret.sh](./provisioning/pai_secret.sh) during the deployment, you will have the file named `api-key` generated in the same location. Copy this file `api-key` to the machine where you want to run the API Call to the Model Endpoint. +When you have used the file [pai_secret.sh](./provisioning/pai_secret.sh) during the deployment, you have a file named `api-key` generated in the same location. Copy this `api-key` file to the machine where you want to run the API Call to the Model Endpoint. 2. Keep the LoadBalancer Reserved Public IP or the Private IP ready. You can use this IP in the API Endpoint call in the next step. -3. Assume the Loadbalancer Reserved Public IP from the last step is `129.xxx.xxx.xxx`, you can use the below example command to make an API Endpoint Call: +3. If we assume the Loadbalancer Reserved Public IP from the previous step is `129.xxx.xxx.xxx`, you can use the following example command to see how to make an API Endpoint Call: ```sh curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_l6_txt/score ``` -**NOTE:** In case of the Private LoadBalancer, use the Internal IP in place of the IP `129.xxx.xxx.xxx` in above example. +**NOTE:** If you have a Private LoadBalancer, then use the Internal IP in place of the IP `129.xxx.xxx.xxx` in the preceding example. ## Accessing the PrivateAI Container using REST Calls with SSL certificate -In case you want to use SSL authentication while accessing the PrivateAI Container in Kubernetes using SSL certificate, then you will need to follow below additional steps: +To use SSL authentication while accessing the PrivateAI Container in Kubernetes using SSL certificate, complete the following additional steps: -1. Copy the `cert.pem` generated when you had run `pai_secret.sh` script, to the machine where you want to run the API Call to the Model Endpoint. -2. Use this key file while running the below modified example command to make an API Endpoint Call: +1. Copy the `cert.pem` generated when you ran the `pai_secret.sh` script to the machine where you want to run the API Call to the Model Endpoint. +2. Use this key file to run the following modified example command to make an API Endpoint Call: ```sh curl --cacert cert.pem --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_l6_txt/score ``` **NOTE:** -- In case of the Private LoadBalancer, use the Internal IP in place of the IP `129.xxx.xxx.xxx` in above example. -- Replace the details of the AI Model in the above URL with the Model deployed. \ No newline at end of file +- If you have a Private LoadBalancer, then use the Internal IP in place of the IP `129.xxx.xxx.xxx` in the preceding example. +- Replace the details of the AI Model in the URL used in the exmaple with the Model deployed. \ No newline at end of file From d824bdd30e48bd7791cb6efb0fee59edbe3a126e Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 21:59:48 +0000 Subject: [PATCH 327/414] Update api_endpoint.md minor style edits. --- docs/privateai/api_endpoint.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/privateai/api_endpoint.md b/docs/privateai/api_endpoint.md index 94dca74a..a424c1ce 100644 --- a/docs/privateai/api_endpoint.md +++ b/docs/privateai/api_endpoint.md @@ -2,15 +2,15 @@ **IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace. -Please follow the below steps to test: +Complete the following steps: 1. Retrieve API Key -When you have used the file [pai_secret.sh](./pai_secret.sh) during the deployment, you will have the file named `api-key` generated in the same location. Copy this file `api-key` to the machine where you want to run the API Call to the Model Endpoint. +When you have used the file [pai_secret.sh](./pai_secret.sh) during the deployment, the file `api-key` is generated in the same location. Copy this `api-key` file to the machine where you want to run the API Call to the Model Endpoint. -2. Get the Loadbalancer External IP from the existing deployment of Oracle PrivateAI Container for the service `service/pai-sample-svc`. You can use this IP in the API Endpoint call in the next step. +2. Obtain the Loadbalancer External IP from the existing deployment of Oracle PrivateAI Container for the service `service/pai-sample-svc`. You can use this IP in the API Endpoint call in the next step. -3. Assume the Loadbalancer Extenral IP from the last step is `141.xxx.xxx.xxx`, you can use the below command to make an API Endpoint Call: +3. If we assume the Loadbalancer Extenral IP from the last step is `141.xxx.xxx.xxx`, then the following commmand is an example of how to make an API Endpoint Call: ```sh curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /home/opc/api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://141.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score ``` \ No newline at end of file From d8ab7cdcc4f519b58c90b25cb461a8faf7a2ae38 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 22:09:50 +0000 Subject: [PATCH 328/414] Update configmap_multi_model_filesystem.md Minor style edits. --- docs/privateai/configmap_multi_model_filesystem.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/privateai/configmap_multi_model_filesystem.md b/docs/privateai/configmap_multi_model_filesystem.md index d315f275..53a88833 100644 --- a/docs/privateai/configmap_multi_model_filesystem.md +++ b/docs/privateai/configmap_multi_model_filesystem.md @@ -6,12 +6,12 @@ You can use the example file [multi_model_filesystem_config.json](./provisioning Rename the file `multi_model_filesystem_config.json` to `config.json`. -Create a configmap using the above file as below: +Create a configmap using that renamed file. Example: ```sh kubectl create configmap multiconfigjson --from-file=config.json -n pai ``` -You can check the details of the configmap as below: +You can check the details of the configmap. Example: ```sh kubectl get configmap -n pai ``` \ No newline at end of file From 5bf54f966f42aeb4e0e80137e4dec813da25e367 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 22:12:02 +0000 Subject: [PATCH 329/414] Update configmap_single_model_https.md Minor style edits. --- docs/privateai/configmap_single_model_https.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/privateai/configmap_single_model_https.md b/docs/privateai/configmap_single_model_https.md index 3665e38b..73b459c4 100644 --- a/docs/privateai/configmap_single_model_https.md +++ b/docs/privateai/configmap_single_model_https.md @@ -6,12 +6,12 @@ You can use the example file [single_model_https_config.json](./provisioning/sin Rename the file `single_model_https_config.json` to `config.json`. -Create a configmap using the above file as below: +Create a configmap using the renamed file. Example: ```sh kubectl create configmap omlconfigjson --from-file=config.json -n pai ``` -You can check the details of the configmap as below: +You can check the details of the configmap. Example: ```sh kubectl get configmap -n pai ``` \ No newline at end of file From 49aab8d38f6ee51f0558867311737f943c8d85d1 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 22:19:35 +0000 Subject: [PATCH 330/414] Update create_oci_fss_based_pvc.md Minor style edits. --- docs/privateai/create_oci_fss_based_pvc.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/privateai/create_oci_fss_based_pvc.md b/docs/privateai/create_oci_fss_based_pvc.md index e5c98c9d..363f929c 100644 --- a/docs/privateai/create_oci_fss_based_pvc.md +++ b/docs/privateai/create_oci_fss_based_pvc.md @@ -1,20 +1,22 @@ # Create OCI FSS based PVC -The Persistent Volume (PV) is created using `oci-fss` Storage Class. Please refer to the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingpersistentvolumeclaim_Provisioning_PVCs_on_FSS.htm) for OCI File Storage. A Persistent Volume Claim (PVC) is created for this PV. For the PV with `oci-fss` Storage Class to be used, an OCI File System Storage and a Mount Target is created in OCI before creating the PV. +The Persistent Volume (PV) is created using `oci-fss` Storage Class. For more information, see [Creating Persistent Volume Claims](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingpersistentvolumeclaim_Provisioning_PVCs_on_FSS.htm) for OCI File Storage. A Persistent Volume Claim (PVC) is created for this PV. To use the PV with `oci-fss` Storage Class, you must create an OCI File System Storage and a Mount Target in OCI before creating the PV. -**Important:** The VCN of the OKE Cluster and the File System Storage in OCI Must be same in order for the OKE Cluster Nodes to access the FSS Mount Target. +**Important:** For the OKE Cluster Nodes to access the FSS Mount Target, the VCN of the OKE Cluster and the File System Storage in OCI must be the same. -1. Provision an OCI File Systems in the same VCN which is used by the OKE Cluster on which you want to deploy the PrivateAI Container. Please refer [here](https://docs.oracle.com/en-us/iaas/Content/File/Tasks/create-file-system.htm) for the details of this step. +Complete the following steps: -2. Once the OCI File System is created, get the details of the `Export Path` and `Mount Target` (the IP and OCID of Mount Target) information. It will be required in the next step. +1. Provision an OCI File Systems in the same VCN that is used by the OKE Cluster on which you want to deploy the PrivateAI Container. For more information, see [Create File System](https://docs.oracle.com/en-us/iaas/Content/File/Tasks/create-file-system.htm) for the details of this step. -3. Create a YAML file for the Storage Class. Please refer to the sample file: [StorageClass.yaml](./provisioning/StorageClass.yaml). You will need to provide the `OCID` of the mount target in this file. +2. After the OCI File System is created, obtain the details of the `Export Path` and `Mount Target` (the IP and OCID of Mount Target). This information is required in the next step. -4. Create a YAML file for the Persistent Volume (PV). It will use the `oci-fss` storage class. Please refer to the sample file: [oke-pv.yaml](./provisioning/oke-pv.yaml). You will need to provide the `Export Path` and Mount Target `IP` in this file. +3. Create a YAML file for the Storage Class. To see how to do this, refer to the following example file: [StorageClass.yaml](./provisioning/StorageClass.yaml). You will need to provide the `OCID` of the mount target in this file. -5. Create a YAML file for the Persistent Volume Claim (PVC). It will use the PV created in previous step. Please refer to the sample file: [oke-pvc.yaml](./provisioning/oke-pvc.yaml) +4. Create a YAML file for the Persistent Volume (PV). This configuration will use the `oci-fss` storage class. To see how to do this, refer to the following example file: [oke-pv.yaml](./provisioning/oke-pv.yaml). You will need to provide the `Export Path` and Mount Target `IP` in this file. -6. Apply the above YAML files to create the PVC using `oci-fss` based File System. +5. Create a YAML file for the Persistent Volume Claim (PVC). In this file, we use the PV created in the previous step. To see how to do this, refer to the following example file: [oke-pvc.yaml](./provisioning/oke-pvc.yaml) + +6. Apply the YAML files you have created in the preceding steps to create the PVC using `oci-fss` based File System: ```sh kubectl apply -f StorageClass.yaml kubectl apply -f oke-pv.yaml From 761a71150f38f79b1431c826b6023b9d2e436bbb Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 22:26:13 +0000 Subject: [PATCH 331/414] Update deploy_privateai_internallb.md Minor style edits. --- docs/privateai/deploy_privateai_internallb.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/privateai/deploy_privateai_internallb.md b/docs/privateai/deploy_privateai_internallb.md index 8d5f44dc..8b06d1d7 100644 --- a/docs/privateai/deploy_privateai_internallb.md +++ b/docs/privateai/deploy_privateai_internallb.md @@ -2,11 +2,11 @@ Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. -**IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. +**IMPORTANT:** Ensure that you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. -**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available as of now. Please check the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). +**NOTE:** The option to reserve a Private IP and use that IP with an OCI Internal LoadBalancer is not available at this time. For more information, see [the OCI documentation about configuring load balancers](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). -If you want to use the OCI Internal LoadBalancer, then you will need to follow the below steps: +If you want to use the OCI Internal LoadBalancer, then you must complete the following steps: 1. Deploy the [pai_sample_internallb.yaml](./provisioning/pai_sample_internallb.yaml) file: ```sh @@ -25,7 +25,7 @@ If you want to use the OCI Internal LoadBalancer, then you will need to follow t In this case, the internal LoadBalancer is created as an OCI load balancer with a private IP address, hosted on the subnet specified for load balancers when the OKE cluster was created. -In case, you want the internal LoadBalancer to be created as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created, you need to add following annotations in the above .yaml file: +With our example, we want to create the internal LoadBalancer as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created. To do this, you must add the following annotations in the `.yaml` file: ```sh pailbAnnotation: @@ -33,14 +33,14 @@ In case, you want the internal LoadBalancer to be created as an OCI load balance service.beta.kubernetes.io/oci-load-balancer-subnet1: "ocid1.subnet.oc1..aaaaaa....vdfw" ``` -**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. In order to avoid a hostname mismatch error while using the `cert.pem` file to make a authenicated connection, we will need to replace this SSL certificate with a new certificate which has the `common name` set to the IP of the Internal LoadBalancer. +**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. To avoid a hostname mismatch error while using the `cert.pem` file to make a authenticated connection, we must replace this SSL certificate with a new certificate that has the `common name` set to the IP of the Internal LoadBalancer. -3. Use the file [pai_secret_new.sh](./pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While using this script, use the IP noted in Step 2 for `common name` while generating the SSL certificate. +3. Use the file [pai_secret_new.sh](./pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While using this script, use the IP from Step 2 for `common name` while generating the SSL certificate. 4. Apply the modified file [pai_sample_internallb_replace_cert.yaml](./provisioning/pai_sample_internallb_replace_cert.yaml) to replace the Internal LoadBalancer Certificate: ```sh kubectl apply -f pai_sample_internallb_replace_cert.yaml ``` -**NOTE:** This step will result in termination of the existing PrivateAI Container Pod and creation of new Pod while the Internal LoadBalancer IP will not change. +**NOTE:** This step will result in termination of the existing PrivateAI Container Pod and creation of new Pod. The Internal LoadBalancer IP will not change. -5. After this change, you will be able to access the PrivateAI Container using the Internal LoadBalancer IP using an authenticated connection using the `cert.pem` file from the new SSL certificate. \ No newline at end of file +5. After this change, you are now able to access the PrivateAI Container using the Internal LoadBalancer IP using an authenticated connection, which uses the `cert.pem` file from the new SSL certificate. \ No newline at end of file From 949c4e1cda1967242e3e558dc1c878b3dfd384b5 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 23:14:07 +0000 Subject: [PATCH 332/414] Update deploy_privateai_multi_model_https_internallb.md Minor style edits. --- ...loy_privateai_multi_model_https_internallb.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/privateai/deploy_privateai_multi_model_https_internallb.md b/docs/privateai/deploy_privateai_multi_model_https_internallb.md index f1fef388..bf03eb03 100644 --- a/docs/privateai/deploy_privateai_multi_model_https_internallb.md +++ b/docs/privateai/deploy_privateai_multi_model_https_internallb.md @@ -1,14 +1,14 @@ # Deploy PrivateAI in OKE cluster using Multiple AI Models with HTTPS URL and an Internal LoadBalancer -Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. The PrivateAI Container is deployed using multiple AI Models and HTTPS URLs for those model files are provided using a configmap. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. +Deploy Oracle PrivateAI Container on your Cloud-based Kubernetes cluster. The PrivateAI Container is deployed using multiple AI Models and HTTPS URLs for those model files are provided using a configmap. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. **IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. -**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available as of now. Please check the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). +**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available at this time. For more information, see [Configuring Load Balancers](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). -If you want to use the OCI Internal LoadBalancer, then you will need to follow the below steps: +To use the OCI Internal LoadBalancer, complete the following steps: -1. Make sure you have created the [configmap](./configmap_multi_model_https.md) +1. Ensure you have created the [configmap](./configmap_multi_model_https.md) 2. Deploy the [pai_sample_multi_model_https_internallb.yaml](./provisioning/pai_sample_multi_model_https_internallb.yaml) file: ```sh kubectl apply -f pai_sample_multi_model_https_internallb.yaml @@ -26,7 +26,7 @@ If you want to use the OCI Internal LoadBalancer, then you will need to follow t In this case, the internal LoadBalancer is created as an OCI load balancer with a private IP address, hosted on the subnet specified for load balancers when the OKE cluster was created. -In case, you want the internal LoadBalancer to be created as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created, you need to add following annotations in the above .yaml file: +If you want the internal LoadBalancer to be created as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created, then you must add following annotations to the .yaml file: ```sh pailbAnnotation: @@ -34,9 +34,9 @@ In case, you want the internal LoadBalancer to be created as an OCI load balance service.beta.kubernetes.io/oci-load-balancer-subnet1: "ocid1.subnet.oc1..aaaaaa....vdfw" ``` -**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. In order to avoid a hostname mismatch error while using the `cert.pem` file to make a authenicated connection, we will need to replace this SSL certificate with a new certificate which has the `common name` set to the IP of the Internal LoadBalancer. +**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. In order to avoid a hostname mismatch error while using the `cert.pem` file to make a authenicated connection, you must replace this SSL certificate with a new certificate that has the `common name` set to the IP of the Internal LoadBalancer. -4. Use the file [pai_secret_new.sh](./provisioning/pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While using this script, use the IP noted in Step 2 for `common name` while generating the SSL certificate. +4. Use the file [pai_secret_new.sh](./provisioning/pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While configuring this script, use the IP noted in Step 2 for the `common name` when generating the SSL certificate. ```sh cd provisioning @@ -49,7 +49,7 @@ cd provisioning ``` **NOTE:** This step will result in termination of the existing PrivateAI Container Pod and creation of new Pod while the Internal LoadBalancer IP will not change. -6. After this change, you will be able to access the PrivateAI Container using the Internal LoadBalancer IP using an authenticated connection using the `cert.pem` file from the new SSL certificate. +6. After this change, you should be able to access the PrivateAI Container using the Internal LoadBalancer IP where you obtain an authenticated connection using the `cert.pem` file from the new SSL certificate. **NOTE:** The file `/oml/config/config.json` inside the running Kubernetes Pod will have the details of the AI Models currently Deployed. You can use the below steps to confirm: ```sh From 9feaa30450bdc8dc5879aeb6f8d8899a43ffe12f Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 23:17:06 +0000 Subject: [PATCH 333/414] Update deploy_privateai_multi_model_https_internallb_add_model.md Minor style ed9ts --- ...ploy_privateai_multi_model_https_internallb_add_model.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md b/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md index 17621e9f..43e93ecb 100644 --- a/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md +++ b/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md @@ -1,14 +1,14 @@ # ADD New AI Model to PrivateAI deployed in OKE cluster -The existing PrivateAI Container is deployed in case [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md) using multiple AI Models and HTTPS URLs for those model files are provided using a configmap. +The existing PrivateAI Container is deployed in the task example [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md), using multiple AI Models. The HTTPS URLs for those model files are provided using a configmap. In this example, an additional AI Model is deployed to the existing deployment. -1. Use the below command to edit the configmap used by the existing PrivateAI Deployment to add a new AI Model: +1. Use the following command to edit the configmap used by the existing PrivateAI Deployment to add a new AI Model: ```sh kubectl edit configmap multiconfigjson -n pai ``` -2. Wait for few minutes and you should see configmap updated inside the Pod. +2. Wait for few minutes. You should see the `configmap` updated inside the Pod. **NOTE:** The Pod will not be restarted in this case. The Application inside the Pod is periodically checking for Configmap changes. \ No newline at end of file From 2af0e2a697c752241315f74997e0016249046b82 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 23:20:58 +0000 Subject: [PATCH 334/414] Update deploy_privateai_multi_model_https_internallb_remove_model.md minor style edits --- ...teai_multi_model_https_internallb_remove_model.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md b/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md index 34c1fb9f..16093c9a 100644 --- a/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md +++ b/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md @@ -1,18 +1,14 @@ # Remove an existing Model from PrivateAI deployed in OKE cluster -The existing PrivateAI Container is deployed in case [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md) using multiple AI Models and HTTPS URLs for those model files are provided using a configmap. +The existing PrivateAI Container is deployed in the task example [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md) using multiple AI Models. The HTTPS URLs for those model files are provided by using a `configmap`. You can also have added additional AI Models to the existing PrivateAI Deployment using [Add New Model](./deploy_privateai_multi_model_https_internallb_add_model.md). -or +In this example, we now remove an AI Model from the existing PrivateAI deployment. The steps are as follows: -Additional AI Models have been added to the existing PrivateAI Deployment using [Add New Model](./deploy_privateai_multi_model_https_internallb_add_model.md). - -In this example, an existing AI Model is removed from the existing PrivateAI deployment. - -1. Use the below command to edit the configmap used by the existing PrivateAI Deployment to removew an existing AI Model: +1. Use the following command to edit the `configmap` used by the existing PrivateAI Deployment to removew an existing AI Model: ```sh kubectl edit configmap multiconfigjson -n pai ``` -2. Wait for few minutes and you should see configmap updated inside the Pod. +2. Wait for few minutes. You should see configmap updated inside the Pod. **NOTE:** The Pod will not be restarted in this case. The Application inside the Pod is periodically checking for Configmap changes. \ No newline at end of file From bf41eaf9bc2f9e4173c679da293b1dcf8e9cf1b9 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 23:22:38 +0000 Subject: [PATCH 335/414] Update deploy_privateai_publiclb.md Minor style changes --- docs/privateai/deploy_privateai_publiclb.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/privateai/deploy_privateai_publiclb.md b/docs/privateai/deploy_privateai_publiclb.md index 591f3fff..0561e9d2 100644 --- a/docs/privateai/deploy_privateai_publiclb.md +++ b/docs/privateai/deploy_privateai_publiclb.md @@ -2,11 +2,11 @@ Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. -**IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. +**IMPORTANT:** Ensure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. **NOTE:** Modify the file `pai_sample_publiclb.yaml` with the actual Reserved Public IP before deployment. -Use the file: [pai_sample_publiclb.yaml](./provisioning/pai_sample_publiclb.yaml) for this use case as below: +In this example, we use the file [pai_sample_publiclb.yaml](./provisioning/pai_sample_publiclb.yaml) for this use case: 1. Deploy the `pai_sample_publiclb.yaml` file: ```sh From 32b3a318053e9c0daa70eea439bc313d92032dc1 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Mon, 8 Sep 2025 23:27:36 +0000 Subject: [PATCH 336/414] Update scale_in_privateai.md Minor style edit --- docs/privateai/scale_in_privateai.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/privateai/scale_in_privateai.md b/docs/privateai/scale_in_privateai.md index 04d797d7..2ea9f989 100644 --- a/docs/privateai/scale_in_privateai.md +++ b/docs/privateai/scale_in_privateai.md @@ -2,9 +2,9 @@ **IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=3` using the file [pai_sample_scale_up.yaml](./provisioning/pai_sample_scale_up.yaml) -In this example, we will Scale In an existing deployment with `replicas=3` to `replicas=2`. +In this example, we will Reduce the number of allocated resources by using the `scale in` command an existing deployment. We will update `replicas=3` to `replicas=2`. -Use the file: [pai_sample_scale_in.yaml](./provisioning/pai_sample_scale_in.yaml) for this use case as below: +For this use case, we will update the following file: [pai_sample_scale_in.yaml](./provisioning/pai_sample_scale_in.yaml). Example: 1. Check the status of the deployment: ```sh @@ -21,5 +21,5 @@ Use the file: [pai_sample_scale_in.yaml](./provisioning/pai_sample_scale_in.yaml kubectl get all -n pai ``` - You will see, Kubernetes Pods are reduced in number once the scale in is done automatically. + As a result of this procedure, the Kubernetes Pods are reduced in number after the scale in is completed automatically. \ No newline at end of file From 8532cc544ccfc41b62122f1bf80c8cd1f25db83f Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Tue, 9 Sep 2025 00:00:11 +0000 Subject: [PATCH 337/414] Update scale_up_privateai.md minor style edits. --- docs/privateai/scale_up_privateai.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/privateai/scale_up_privateai.md b/docs/privateai/scale_up_privateai.md index 72eea049..f18335b3 100644 --- a/docs/privateai/scale_up_privateai.md +++ b/docs/privateai/scale_up_privateai.md @@ -2,9 +2,9 @@ **IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=1` using the file [pai_sample_publiclb.yaml](./provisioning/pai_sample_publiclb.yaml) -In this example, we will Scale Up an existing deployment with `replicas=1` to `replicas=3`. +In this example, we will increase the allocated resources in an existing deployment by using the Kubernetees scale up option from `replicas=1` to `replicas=3`. -Use the file: [pai_sample_scale_up.yaml](./provisioning/pai_sample_scale_up.yaml) for this use case as below: +Use the file: [pai_sample_scale_up.yaml](./provisioning/pai_sample_scale_up.yaml) for this use case. Example: 1. Check the status of the deployment: ```sh @@ -21,5 +21,5 @@ Use the file: [pai_sample_scale_up.yaml](./provisioning/pai_sample_scale_up.yaml kubectl get all -n pai ``` - You will see, additional Kubernetes Pods getting deployed once the scale up is done automatically. +As a result of this procedure, you will see additional Kubernetes Pods being deployed after the scale up operation is automatically completed. \ No newline at end of file From 469503264df1332c41d222fd409ed6e933f5dea7 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 9 Sep 2025 05:48:56 +0000 Subject: [PATCH 338/414] Added fixes --- controllers/database/oraclerestart_controller.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 65959118..75b4def7 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -3449,9 +3449,10 @@ func (r *OracleRestartReconciler) updateONS(ctx context.Context, podList *corev1 return nil } func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context, instance *oraclerestartdb.OracleRestart, oldSpec *oraclerestartdb.OracleRestartSpec) error { + //reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) - fmt.Printf("Received OldSpec", oldSpec.InstDetails.SwLocStorageSizeInGb) if oldSpec != nil { + fmt.Printf("Received OldSpec", oldSpec.InstDetails.SwLocStorageSizeInGb) if instance.Spec.InstDetails.SwLocStorageSizeInGb > oldSpec.InstDetails.SwLocStorageSizeInGb { fmt.Printf("Inside OldSpec and newSpec Change", oldSpec.InstDetails.SwLocStorageSizeInGb, instance.Spec.InstDetails.SwLocStorageSizeInGb) storageClass := &storagev1.StorageClass{} @@ -3464,8 +3465,9 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context return fmt.Errorf("error while fetching the storage class") } + pvcName := oraclerestartcommon.GetSwPvcName(instance.Spec.InstDetails.Name) + "-" + instance.Spec.InstDetails.Name + "-0" err = r.Get(ctx, types.NamespacedName{ - Name: oraclerestartcommon.GetSwPvcName(instance.Name), + Name: pvcName, Namespace: instance.Namespace, }, pvc) @@ -3482,13 +3484,11 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context fmt.Printf("New PvcSize set to ", newPVCSizeAdd) if newPVCSizeAdd.Cmp(pvc.Spec.Resources.Requests["storage"]) < 0 { - r.Recorder.Eventf(instance, corev1.EventTypeWarning, "Cannot Resize PVC", "Forbidden: field can not be less than previous value") return fmt.Errorf("Resizing PVC to lower size volume not allowed") } pvc.Spec.Resources.Requests["storage"] = resource.MustParse(strconv.Itoa(instance.Spec.InstDetails.SwLocStorageSizeInGb) + "Gi") fmt.Printf("Updating PVC", "pvc", pvc.Name, "volume", pvc.Spec.VolumeName) - r.Recorder.Eventf(instance, corev1.EventTypeNormal, "Updating PVC - volume expansion", "Resizing the pvc for storage expansion") err = r.Update(ctx, pvc) if err != nil { return fmt.Errorf("error while updating the PVCs") From b96c78a5f6d4f1a6ee8c0d7a297ac520d75f91ea Mon Sep 17 00:00:00 2001 From: Matteo Malvezzi Date: Tue, 9 Sep 2025 08:34:54 +0000 Subject: [PATCH 339/414] Mmalvezz oeap sha256 --- commons/multitenant/lrest/common.go | 4 +- config/manager/kustomization.yaml | 4 +- controllers/database/.lrest_controller.go.swp | Bin 0 -> 16384 bytes controllers/database/lrpdb_controller.go | 4 +- controllers/database/ordssrvs_controller.go | 3 +- docs/multitenant/README.md | 12 +- docs/multitenant/usecase/makefile | 14 +- oracle-database-operator.yaml | 374 +----------------- 8 files changed, 24 insertions(+), 391 deletions(-) create mode 100644 controllers/database/.lrest_controller.go.swp diff --git a/commons/multitenant/lrest/common.go b/commons/multitenant/lrest/common.go index 4ae7b627..51194d13 100644 --- a/commons/multitenant/lrest/common.go +++ b/commons/multitenant/lrest/common.go @@ -39,7 +39,9 @@ package lrest import ( + "crypto/rand" "crypto/rsa" + "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/pem" @@ -73,7 +75,7 @@ func CommonDecryptWithPrivKey2(Key string, Buffer string, req ctrl.Request) (str return "", err } - decryptedB, err := rsa.DecryptPKCS1v15(nil, pkcs8PrivateKey.(*rsa.PrivateKey), encString64) + decryptedB, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, pkcs8PrivateKey.(*rsa.PrivateKey), encString64, nil) if err != nil { fmt.Printf("Failed to decrypt string %s\n", err.Error()) return "", err diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 6173a20a..4e8254b1 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: orestart-operator-sa + newName: lin.ocir.io/intsanjaysingh/operator/dboperator_master_orahub + newTag: oeap_sha256 diff --git a/controllers/database/.lrest_controller.go.swp b/controllers/database/.lrest_controller.go.swp new file mode 100644 index 0000000000000000000000000000000000000000..0e90e15f39882e9f2885dd74a05411b4d6af81fc GIT binary patch literal 16384 zcmeHNON<;x8SXp?3?Yeu11MZ7gNSDAakqDI9yqel>~62O#k?{PFDrqpo}QYS_V!~` zT{F956d?pCC>U`=9C@C|0Vz=sC?d)QDS-nB0*C{U3xX3;AUqSo_g7c1XUAr7AmV^~ zn(|6hOo|5txicU4#a)On}Q9yCuG_#HEhXCC|R_}+6j8)w$aMi{!mj9V5ZHv2eYBpB%W-?Yjv- zd7$LQkrZ(daFJM<)CvSojKdN0(c=fvlZZwEPoV{+T8D-?cpRub>^y>i zEp+kyKU-hFdY5552Ye6sJa8F!2>9KdhVez93EToad52*<3VaIK1>OTZ|2D(;8t`S{ zVc;gpO@^T4yfQ@}TXuL55I9tAD}b>K8`EARr=#$N&+0Xo1c za69mOths*(d<*yj5Cdz#+kqcpJ^yuJ3P`{LeuG8#<3Jxc20V>DhwlL21}*~;Z~?FZ z6L>d38!<1UFMb9568Jelee(p6{R-;LLu-t~5l-YwhTmCMq)Xoo{7Zf`DjG9auo6Nr z3T^JiV!#Cx>cX^VJd#x z4Sf!j(jB2qAl-4{-Mdeki9A2y8?5*$uIKv(}#=uRfdl8}nhnu|kw6rZF? z*P{Q_XrUPu){BS{le?lAmq_9nN-e864AJu>4}34}0d%)DmU2S1a7}+O()>8faaE&G zJcy{sBQGAnxOFj!BU1Ood?I6O|GrZnSh$seYMsUp-&bb)bCp9j#MyDnIDVXUnw#D2YTGvHrrGPV<|b>lnbTnF%|^ps?>fx}+jhEJth;3&Xp;)v z_c}IRHgVl%8+NtkG|qLXG789G&8e6*p)pNG0rCcRTn@+c3cRHymYgOA_XT4Xewppv!ZZ#2CwXwk(&4$z1Y{Lb+ zZa2Cnh1O^?`#f$K4oB8%S)@+ARdei(3N+Shy$y1D4Qw`=UHDXYy7mU*Q=EbAcD2!U z$e&GCx7+JmxXpMX*NEAyUzpQjt#ea`&I3#J1;zy))I0I7um3!6gu53_DRo>9h4=E5J1C5vK-k-*$&z&KtD z@RA|NegcJX5?~@^ICdKPNdnI@)mSiBrskkX<0wkda6-}0`$p#05a&Ty`=BL`ny?qA4Wp?PYFY07=chIq?m{!rO-%ZJ)fr0NhU4H8yz6ZFRr&t62yk!r{bxHsS!OvwH|l8Ovn zA)d1O3NcWHR}&G>d>SvyJRw)GEb#r*ihoh*XI7*8sM2yR6L<;bNVz?V7|jg@=fRu|l{`A@ zjr5tJKp87$VHd#)DN>hs=mR`%$ZYE7Vj~Ar0vSXmr-A*sWWU+I2ayXb0Ffh^Y7<}Wj6V)inW*p=)aSEbG0@Ebg zK(E1R7$lorMVVhtv-l$hsZ|?4U>{}+@SUUWvt_W0?_&&%X0OjHUDRU$ABT=0e#>E@C)ev4EU+8 z_hYJ?|8fR$266^+266^+266^+266^+266^+266`e#|%7zy>NW^EMWiKv}s?zgnjM` zD{i{jiBYTV=>#iyjxUd{oF;1L>`d{5`l@O++z|U@v@?#KPyDYfl<~RIA5sB3bCwCS zqpFt5W>1+ad2+vkZ6EA-l^;@C_nc*sAK3F;WP~raO1l?rVQWW@ajv|e zoGu76`#76^svVY;A~wX-$Az@8i%%`vuAp5EINRP>t9P(jnM^r@*F{y~?E4a2t4kHs PiK4j}V}gBpUN-&-Pp=lz literal 0 HcmV?d00001 diff --git a/controllers/database/lrpdb_controller.go b/controllers/database/lrpdb_controller.go index 10c92739..5b12fa66 100644 --- a/controllers/database/lrpdb_controller.go +++ b/controllers/database/lrpdb_controller.go @@ -41,7 +41,9 @@ package controllers import ( "bytes" "context" + "crypto/rand" "crypto/rsa" + "crypto/sha256" "crypto/tls" "crypto/x509" "encoding/base64" @@ -2054,7 +2056,7 @@ func (r *LRPDBReconciler) DecryptWithPrivKey(Key string, Buffer string, req ctrl return "", err } - decryptedB, err := rsa.DecryptPKCS1v15(nil, pkcs8PrivateKey.(*rsa.PrivateKey), encString64) + decryptedB, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, pkcs8PrivateKey.(*rsa.PrivateKey), encString64, nil) if err != nil { log.Error(err, "Failed to decrypt string - "+err.Error()) return "", err diff --git a/controllers/database/ordssrvs_controller.go b/controllers/database/ordssrvs_controller.go index b85b2b60..db831805 100644 --- a/controllers/database/ordssrvs_controller.go +++ b/controllers/database/ordssrvs_controller.go @@ -40,6 +40,7 @@ package controllers import ( "context" + "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" @@ -1244,7 +1245,7 @@ func CommonDecryptWithPrivKey(Key string, Buffer string) (string, error) { fmt.Printf("======================================\n") } - decryptedB, err := rsa.DecryptPKCS1v15(nil, pkcs8PrivateKey.(*rsa.PrivateKey), encString64) + decryptedB, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, pkcs8PrivateKey.(*rsa.PrivateKey), encString64, nil) if err != nil { fmt.Printf("Failed to decrypt string %s\n", err.Error()) return "", err diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index fb2a5abb..8d9e7024 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -220,12 +220,12 @@ echo "[PDBUSERNAME]" > pdbusr.txt echo "[PDBUSERNAME PASSWORD]" > pdbpwd.txt ## 3. Encrypt the credentials -openssl rsautl -encrypt -pubin -inkey public.pem -in dbuser.txt |base64 > e_dbuser.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in dbpass.txt |base64 > e_dbpass.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in wbuser.txt |base64 > e_wbuser.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in wbpass.txt |base64 > e_wbpass.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in pdbusr.txt |base64 > e_pdbusr.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in pdbpwd.txt |base64 > e_pdbpwd.txt +openssl pkeyutl -encrypt -pubin -inkey public.pem -in dbuser.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_dbuser.txt +openssl pkeyutl -encrypt -pubin -inkey public.pem -in dbpass.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_dbpass.txt +openssl pkeyutl -encrypt -pubin -inkey public.pem -in wbuser.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_wbuser.txt +openssl pkeyutl -encrypt -pubin -inkey public.pem -in wbpass.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_wbpass.txt +openssl pkeyutl -encrypt -pubin -inkey public.pem -in pdbusr.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_pdbusr.txt +openssl pkeyutl -encrypt -pubin -inkey public.pem -in pdbpwd.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_pdbpwd.txt kubectl create secret generic dbuser --from-file=e_dbuser.txt -n cdbnamespace kubectl create secret generic dbpass --from-file=e_dbpass.txt -n cdbnamespace diff --git a/docs/multitenant/usecase/makefile b/docs/multitenant/usecase/makefile index 655fb45c..e6ea4be4 100644 --- a/docs/multitenant/usecase/makefile +++ b/docs/multitenant/usecase/makefile @@ -215,12 +215,12 @@ secrets: tls delsecrets @$(ECHO) $(WBPASS) > $(WBPASSFILE) @$(ECHO) $(PDBUSR) > $(PDBUSRFILE) @$(ECHO) $(PDBPWD) > $(PDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBUSERFILE) |base64 > e_$(DBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBPASSFILE) |base64 > e_$(DBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(DBUSERFILE) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_$(DBUSERFILE) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(DBPASSFILE) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_$(DBPASSFILE) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_$(WBUSERFILE) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_$(WBPASSFILE) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_$(PDBUSRFILE) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_$(PDBPWDFILE) $(KUBECTL) create secret generic dbuser --from-file=e_$(DBUSERFILE) -n $(LRSNAMESPACE) $(KUBECTL) create secret generic dbpass --from-file=e_$(DBPASSFILE) -n $(LRSNAMESPACE) $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(LRSNAMESPACE) @@ -456,7 +456,7 @@ spec: namespaceAutoDiscover: ${PDBNAMESPACE} clusterIp: false loadBalancer: false - trace_level_client : 16 + trace_level_client : 0 EOF if [[ ${OPENSHIFT} == "true" ]];then diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index b13f6e37..19e414b1 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -12883,312 +12883,6 @@ spec: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 - name: privateais.omlai.oracle.com -spec: - group: omlai.oracle.com - names: - kind: PrivateAi - listKind: PrivateAiList - plural: privateais - singular: privateai - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.replicas - name: Replicas - type: number - - jsonPath: .status.apikey - name: ApiKey - type: string - - jsonPath: .status.podip - name: PodIP - type: string - - jsonPath: .status.loadbalancerip - name: LbIP - type: string - - jsonPath: .status.ReleaseUpdate - name: ReleaseUpdate - priority: 1 - type: string - name: v4 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - envVars: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - livenessCheckPeriod: - type: integer - nodePortSvc: - items: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - svcType: - type: string - type: object - type: array - paiAuthentication: - type: boolean - paiConfigFile: - properties: - mountLocation: - type: string - name: - type: string - type: object - paiEnableAuthentication: - default: true - type: boolean - paiHTTPEnabled: - type: boolean - paiHTTPPort: - format: int32 - type: integer - paiHTTPSEnabled: - type: boolean - paiHTTPSPort: - format: int32 - type: integer - paiImage: - type: string - paiImagePullSecret: - type: string - paiInternalLB: - type: boolean - paiLBIP: - type: string - paiLBPort: - format: int32 - type: integer - paiLogLocation: - type: string - paiSecret: - properties: - mountLocation: - type: string - name: - type: string - type: object - paiService: - properties: - name: - type: string - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - svcType: - type: string - type: object - pailbAnnotation: - additionalProperties: - type: string - type: object - portMappings: - items: - properties: - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - pvcList: - additionalProperties: - type: string - type: object - readinessCheckPeriod: - type: integer - replicas: - format: int32 - type: integer - resources: - properties: - claims: - items: - properties: - name: - type: string - request: - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - storageClass: - type: string - storageSizeInGb: - format: int32 - type: integer - type: object - status: - properties: - NodeIP: - type: string - apiKey: - type: string - certpem: - type: string - clusterIP: - type: string - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - loadBalancerIP: - type: string - podIP: - type: string - releaseUpdate: - type: string - replicas: - type: integer - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert @@ -15344,32 +15038,6 @@ rules: - get - patch - update -- apiGroups: - - omlai.oracle.com - resources: - - privateais - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - omlai.oracle.com - resources: - - privateais/finalizers - verbs: - - update -- apiGroups: - - omlai.oracle.com - resources: - - privateais/status - verbs: - - get - - patch - - update - apiGroups: - storage.k8s.io resources: @@ -15710,26 +15378,6 @@ webhooks: resources: - databaseobservers sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /mutate-omlai-oracle-com-v4-privateai - failurePolicy: Fail - name: mprivateai-v4.kb.io - rules: - - apiGroups: - - omlai.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - privateais - sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -16087,26 +15735,6 @@ webhooks: resources: - databaseobservers sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: oracle-database-operator-webhook-service - namespace: oracle-database-operator-system - path: /validate-omlai-oracle-com-v4-privateai - failurePolicy: Fail - name: vprivateai-v4.kb.io - rules: - - apiGroups: - - omlai.oracle.com - apiVersions: - - v4 - operations: - - CREATE - - UPDATE - resources: - - privateais - sideEffects: None --- apiVersion: apps/v1 kind: Deployment @@ -16133,7 +15761,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-sa + image: lin.ocir.io/intsanjaysingh/operator/dboperator_master_orahub:oeap_sha256 imagePullPolicy: Always name: manager ports: From e2a4b84ead750684e246bb0b1f3fe7c2120a5a92 Mon Sep 17 00:00:00 2001 From: Marco Stefanetti Date: Tue, 9 Sep 2025 12:34:14 +0000 Subject: [PATCH 340/414] Marcstef ordssrvs 2.0 fix oaep docs --- docs/ordsservices/README.md | 36 ++++++++++++++------ docs/ordsservices/examples/adb.md | 10 +++--- docs/ordsservices/examples/adb_oraoper.md | 8 ++--- docs/ordsservices/examples/existing_db.md | 9 ++--- docs/ordsservices/examples/mongo_api.md | 8 ++--- docs/ordsservices/examples/multi_pool.md | 8 ++--- docs/ordsservices/examples/sidb_container.md | 9 +++-- docs/ordsservices/usecase01/makefile | 8 ++--- 8 files changed, 54 insertions(+), 42 deletions(-) diff --git a/docs/ordsservices/README.md b/docs/ordsservices/README.md index 9fcac846..d012d4ef 100644 --- a/docs/ordsservices/README.md +++ b/docs/ordsservices/README.md @@ -25,16 +25,16 @@ ORDS Version supported : 25.1.0+ OrdsSrvs controller supports the majority of ORDS configuration settings as per the [API Documentation](./api.md). -### Prerequisites +## Prerequisites This chapter outlines the necessary requirements that must be satisfied to successfully deploy and operate the OrdsSrvs controller within your Kubernetes cluster. -#### Oracle Database Operator +### Oracle Database Operator Before installing the OrdsSrvs controller, ensure that the Oracle Database Operator (OraOperator) is installed in your Kubernetes environment. Please follow the detailed installation steps provided in the [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md) to complete this process. The OraOperator must be properly configured and running, as OrdsSrvs depends on its services for functionality. -#### Namespace Namespace Scoped Deployment +### Namespace Namespace Scoped Deployment For a dedicated namespace deployment of the OrdsSrvs controller, refer to the "Namespace Scoped Deployment" section in the OraOperator [README](https://github.com/oracle/oracle-database-operator/blob/main/README.md#2-namespace-scoped-deployment). The following examples demonstrate deploying the controller to the ordsnamespace namespace. @@ -57,11 +57,11 @@ Edit OraOperator to add the namespace under WATCH_NAMESPACE: value: "default,,ordsnamespace" ``` -### OpenShift Security Context Constraints +## OpenShift Security Context Constraints If you are deploying the OrdsSrvs controller on OpenShift, ensure that the appropriate Security Context Constraints (SCCs) are configured. This involves assigning privileged SCCs to the service accounts used by OrdsSrvs to permit required operations. -#### Create a Service Account +### Create a Service Account This account will be used to assign the necessary Security Context Constraints (SCCs) for the controller’s operation. Below is an example [YAML](./examples/ordssrvs-sa.yaml) manifest to create a service account named "ordssrvs-sa" in the "ordsnamespace" namespace: @@ -74,12 +74,12 @@ metadata: namespace: ordsnamespace ``` -#### Create a Custom Security Context Constraint (SCC) +### Create a Custom Security Context Constraint (SCC) To configure the required security permissions, use the attached [YAML](./examples/ordssrvs-sa-scc.yaml) file to create a custom Security Context Constraint (SCC) and bind it to the "ordssrvs-sa" service account. This will ensure the service account has the necessary permissions for the OrdsSrvs controller to operate on OpenShift. -#### Set serviceAccountName in OrdsSrvs +### Set serviceAccountName in OrdsSrvs Ensure that the OrdsSrvs controller uses the dedicated service account you created. In the deployment manifest for OrdsSrvs, specify the serviceAccountName field with the name of your service account (e.g., ordssrvs-sa) as in this [example](./examples/ordssrvs.yaml). @@ -99,7 +99,7 @@ spec: ``` -### Common configuration examples +## Common configuration examples A few common configuration examples can be used to quickly familiarise yourself with the OrdsSrvs Custom Resource Definition. The "Conclusion" section of each example highlights specific settings to enable functionality that maybe of interest. @@ -114,11 +114,27 @@ The "Conclusion" section of each example highlights specific settings to enable Running through all examples in the same Kubernetes cluster illustrates the ability to run multiple ORDS instances with a variety of different configurations. -### Limitations + +## Change Log + +### Version 2.0 + + - ORDS image 25.1+ + - OpenShift installation + - APEX installation files from download url or PersistenceVolume + - BUGFIX [#181 enhancement, init script refactoring and improved logging](https://github.com/oracle/oracle-database-operator/issues/181) + - BUGFIX [#186 autoUpgradeORDS: true and autoUpgradeAPEX: false not working](https://github.com/oracle/oracle-database-operator/issues/186) + - BUGFIX [#188 int64 for configuration settings of type Duration](https://github.com/oracle/oracle-database-operator/issues/188) + +### Upgrading from 1.2 to 2.0 +Password secrets must be recreated when upgrading the operator from version 1.2 to 2.0. +Secrets can be recreated either before or after the operator upgrade. Restarting the OrdsSrvs deployment is not required for this procedure; however, if the deployment does need to be restarted for any reason, ensure secrets are recreated first. + +## Limitations When connecting to a mTLS enabled ADB and using the Oracontroller to retreive the Wallet, it is currently not supported to have multiple, different databases supported by the single RestDataServices resource. This is due to a requirement to set the `TNS_ADMIN` parameter at the Pod level ([#97](https://github.com/oracle/oracle-database-controller/issues/97)). -### Troubleshooting +## Troubleshooting See [Troubleshooting](./TROUBLESHOOTING.md) ## Contributing diff --git a/docs/ordsservices/examples/adb.md b/docs/ordsservices/examples/adb.md index 076a2a8e..ab87a3b5 100644 --- a/docs/ordsservices/examples/adb.md +++ b/docs/ordsservices/examples/adb.md @@ -21,13 +21,13 @@ kubectl create secret generic adb-wallet \ Create a Secret for the ADB ADMIN password, replacing with the real password: ```bash -echo ${ADMIN_PASSWORD} > adb-db-auth-enc -openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key +echo ${ADMIN_PASSWORD} > db-auth +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key openssl rsa -in ca.key -outform PEM -pubout -out public.pem kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace -openssl rsautl -encrypt -pubin -inkey public.pem -in adb-db-auth-enc |base64 > e_adb-db-auth-enc -kubectl create secret generic adb-oraoper-db-auth-enc --from-file=password=e_adb-db-auth-enc -n ordsnamespace -rm adb-db-auth-enc e_adb-db-auth-enc +openssl pkeyutl -encrypt -pubin -inkey public.pem -in db-auth -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_db-auth +kubectl create secret generic adb-oraoper-db-auth-enc --from-file=password=e_db-auth -n ordsnamespace +rm db-auth e_db-auth ``` ### Create RestDataServices Resource diff --git a/docs/ordsservices/examples/adb_oraoper.md b/docs/ordsservices/examples/adb_oraoper.md index 718afa11..36180194 100644 --- a/docs/ordsservices/examples/adb_oraoper.md +++ b/docs/ordsservices/examples/adb_oraoper.md @@ -74,13 +74,13 @@ kubectl create secret generic adb-oraoper-db-auth \ ### Create encrypted password ```bash -echo ${DB_PWD} > adb-db-auth-enc +echo ${DB_PWD} > db-auth openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key openssl rsa -in ca.key -outform PEM -pubout -out public.pem kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace -openssl rsautl -encrypt -pubin -inkey public.pem -in adb-db-auth-enc |base64 > e_adb-db-auth-enc -kubectl create secret generic adb-oraoper-db-auth-enc --from-file=password=e_adb-db-auth-enc -n ordsnamespace -rm adb-db-auth-enc e_adb-db-auth-enc +openssl pkeyutl -encrypt -pubin -inkey public.pem -in db-auth -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_db-auth +kubectl create secret generic adb-oraoper-db-auth-enc --from-file=password=e_db-auth -n ordsnamespace +rm db-auth e_db-auth ``` ### Create OrdsSrvs Resource diff --git a/docs/ordsservices/examples/existing_db.md b/docs/ordsservices/examples/existing_db.md index a4e6511f..a43849e2 100644 --- a/docs/ordsservices/examples/existing_db.md +++ b/docs/ordsservices/examples/existing_db.md @@ -16,17 +16,14 @@ export CONN_STRING=:/ ```bash DB_PWD= - openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > ca.key openssl rsa -in ca.key -outform PEM -pubout -out public.pem kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace echo "${DB_PWD}" > db-auth -openssl rsautl -encrypt -pubin -inkey public.pem -in db-auth |base64 > e_db-auth-enc -kubectl create secret generic db-auth-enc --from-file=password=e_db-auth-enc -n ordsnamespace - -rm db-auth e_db-auth-enc - +openssl pkeyutl -encrypt -pubin -inkey public.pem -in db-auth -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_db-auth +kubectl create secret generic db-auth-enc --from-file=password=e_db-auth -n ordsnamespace +rm db-auth e_db-auth ``` ### Create ordssrvs Resource diff --git a/docs/ordsservices/examples/mongo_api.md b/docs/ordsservices/examples/mongo_api.md index 87f3154b..c7ea2695 100644 --- a/docs/ordsservices/examples/mongo_api.md +++ b/docs/ordsservices/examples/mongo_api.md @@ -40,10 +40,10 @@ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keyg openssl rsa -in ca.key -outform PEM -pubout -out public.pem kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace -echo "${DB_PWD}" > sidb-db-auth-enc -openssl rsautl -encrypt -pubin -inkey public.pem -in sidb-db-auth-enc |base64 > e_sidb-db-auth-enc -kubectl create secret generic sidb-db-auth-enc --from-file=password=e_sidb-db-auth-enc -n ordsnamespace -rm sidb-db-auth-enc e_sidb-db-auth-enc +echo "${DB_PWD}" > db-auth +openssl pkeyutl -encrypt -pubin -inkey public.pem -in db-auth -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_db-auth +kubectl create secret generic sidb-db-auth-enc --from-file=password=e_db-auth -n ordsnamespace +rm db-auth e_db-auth ``` ### Create ordssrvs Resource diff --git a/docs/ordsservices/examples/multi_pool.md b/docs/ordsservices/examples/multi_pool.md index ba77093d..b7d3441d 100644 --- a/docs/ordsservices/examples/multi_pool.md +++ b/docs/ordsservices/examples/multi_pool.md @@ -55,7 +55,7 @@ The following secret will be used for PDB1: ```bash echo "THIS_IS_A_PASSWORD" > ordspwdfile -openssl rsautl -encrypt -pubin -inkey public.pem -in ordspwdfile |base64 > e_ordspwdfile +openssl pkeyutl -encrypt -pubin -inkey public.pem -in ordspwdfile -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_ordspwdfile kubectl create secret generic pdb1-ords-auth-enc --from-file=password=e_ordspwdfile -n ordsnamespace rm ordspwdfile e_ordspwdfile ``` @@ -64,7 +64,7 @@ The following secret will be used for PDB2: ```bash echo "THIS_IS_A_PASSWORD" > ordspwdfile -openssl rsautl -encrypt -pubin -inkey public.pem -in ordspwdfile |base64 > e_ordspwdfile +openssl pkeyutl -encrypt -pubin -inkey public.pem -in ordspwdfile -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_ordspwdfile kubectl create secret generic pdb2-ords-auth-enc --from-file=password=e_ordspwdfile -n ordsnamespace rm ordspwdfile e_ordspwdfile ``` @@ -73,7 +73,7 @@ The following secret will be used for PDB3 and PDB4: ```bash echo "THIS_IS_A_PASSWORD" > ordspwdfile -openssl rsautl -encrypt -pubin -inkey public.pem -in ordspwdfile |base64 > e_ordspwdfile +openssl pkeyutl -encrypt -pubin -inkey public.pem -in ordspwdfile -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_ordspwdfile kubectl create secret generic multi-ords-auth-enc --from-file=password=e_ordspwdfile -n ordsnamespace rm ordspwdfile e_ordspwdfile ``` @@ -86,7 +86,7 @@ In this example, only PDB1 will be set for [AutoUpgrade](../autoupgrade.md), the ```bash echo "THIS_IS_A_PASSWORD" > syspwdfile -openssl rsautl -encrypt -pubin -inkey public.pem -in syspwdfile |base64 > e_syspwdfile +openssl pkeyutl -encrypt -pubin -inkey public.pem -in syspwdfile -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_syspwdfile kubectl create secret generic pdb1-priv-auth-enc --from-file=password=e_syspwdfile -n ordsnamespace rm syspwdfile e_syspwdfile ``` diff --git a/docs/ordsservices/examples/sidb_container.md b/docs/ordsservices/examples/sidb_container.md index da515332..b493e516 100644 --- a/docs/ordsservices/examples/sidb_container.md +++ b/docs/ordsservices/examples/sidb_container.md @@ -50,13 +50,12 @@ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keyg openssl rsa -in ca.key -outform PEM -pubout -out public.pem kubectl create secret generic prvkey --from-file=privateKey=ca.key -n ordsnamespace -echo "${DB_PWD}" > sidb-db-auth -openssl rsautl -encrypt -pubin -inkey public.pem -in sidb-db-auth |base64 > e_sidb-db-auth -kubectl create secret generic sidb-db-auth-enc --from-file=password=e_sidb-db-auth -n ordsnamespace -rm sidb-db-auth e_sidb-db-auth +echo "${DB_PWD}" > db-auth +openssl pkeyutl -encrypt -pubin -inkey public.pem -in db-auth -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 |base64 > e_db-auth +kubectl create secret generic sidb-db-auth-enc --from-file=password=e_db-auth -n ordsnamespace +rm db-auth e_db-auth ``` - ### Create RestDataServices Resource 1. Retrieve the Connection String from the containerised SIDB. diff --git a/docs/ordsservices/usecase01/makefile b/docs/ordsservices/usecase01/makefile index fd4b68f9..c16a7c30 100644 --- a/docs/ordsservices/usecase01/makefile +++ b/docs/ordsservices/usecase01/makefile @@ -386,7 +386,7 @@ step5a: $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(ORDSNAMESPACE) $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(ORDSNAMESPACE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SIDB_PASSWORD_FILE) |base64 > e_$(SIDB_PASSWORD_FILE) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(SIDB_PASSWORD_FILE) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256|base64 > e_$(SIDB_PASSWORD_FILE) $(KUBECTL) create secret generic $(SIDB_SECRET) --from-literal=password=$(SIDB_PASSWORD) -n $(OPRNAMESPACE) $(KUBECTL) create secret generic $(ORDS_SECRET) --from-file=password=e_$(SIDB_PASSWORD_FILE) -n $(ORDSNAMESPACE) $(RM) e_$(SIDB_PASSWORD_FILE) $(SIDB_PASSWORD_FILE) @@ -674,9 +674,9 @@ step13a: $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) #$(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(ORDSNAMESPACE) $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(ORDSNAMESPACE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE1) |base64 > e_$(SYSPWDFILE1) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE2) |base64 > e_$(SYSPWDFILE2) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(ORDPWDFILE) |base64 > e_$(ORDPWDFILE) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE1) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256|base64 > e_$(SYSPWDFILE1) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE2) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256|base64 > e_$(SYSPWDFILE2) + $(OPENSSL) pkeyutl -encrypt -pubin -inkey $(PUBKEY) -in $(ORDPWDFILE) -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256|base64 > e_$(ORDPWDFILE) $(KUBECTL) create secret generic $(PDB1_PRIV_AUTH_SECRET) --from-file=password=e_$(SYSPWDFILE1) -n $(ORDSNAMESPACE) $(KUBECTL) create secret generic $(PDB2_PRIV_AUTH_SECRET) --from-file=password=e_$(SYSPWDFILE2) -n $(ORDSNAMESPACE) $(KUBECTL) create secret generic $(MULTI_ORDS_AUTH_SECRET) --from-file=password=e_$(ORDPWDFILE) -n $(ORDSNAMESPACE) From 836a141fa13b010922efaec8e820eeecbcf20038 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 9 Sep 2025 22:10:06 +0000 Subject: [PATCH 341/414] Added fixes and removed zapr --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index 8a56918c..52ce1112 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,6 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.5.1 // indirect - github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.22.0 // indirect github.com/go-openapi/jsonreference v0.21.1 // indirect github.com/go-openapi/swag v0.24.1 // indirect From 1568dacc36d2ce485b6b43f6225860e391a2bb3e Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 9 Sep 2025 22:14:46 +0000 Subject: [PATCH 342/414] Added fixes --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index 52ce1112..8a56918c 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.5.1 // indirect + github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.22.0 // indirect github.com/go-openapi/jsonreference v0.21.1 // indirect github.com/go-openapi/swag v0.24.1 // indirect From 8c5def780647a13113f724851a31f1a12b11bee3 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Wed, 10 Sep 2025 22:10:29 +0000 Subject: [PATCH 343/414] usecase makefile new parameters --- docs/multitenant/usecase/makefile | 155 +++++++++++++++++++++--- docs/multitenant/usecase/parameters.txt | 9 +- 2 files changed, 146 insertions(+), 18 deletions(-) diff --git a/docs/multitenant/usecase/makefile b/docs/multitenant/usecase/makefile index e6ea4be4..648abb01 100644 --- a/docs/multitenant/usecase/makefile +++ b/docs/multitenant/usecase/makefile @@ -54,6 +54,62 @@ # altersystem_pdb1_resource.yaml : chage cpu_count count parameter for the first pdb # config_map_plsql.yaml : plsql code # +#[BEGIN TABLE] +# +# TARGETS DEPENDENCIES +# +#|level 1 |level 2 |level 3 |description | +#|----------------|----------------|---------------|-------------------------------------| +#|**opsetup** | | |install the operator | +#| | prsyaml | |configure the watch list | +#| | crtmgrappl | |apply the certmanager | +#| | nscrt | |namespace creation | +#| | bndappl | |role binding | +#| | opapply | |apply operator yaml file | +#| | rbacap | |apply rbac | +#+----------------+----------------+---------------+-------------------------------------+ +#|**secrets** | | |create certificates and secrets | +#| | tkl | |tls cert for https | +#| | delsecres | |cleanup existing secrets | +#+----------------+----------------+---------------+-------------------------------------+ +#|**genyaml** | | |generate yaml files | +#| | secyaml | |not documented | +#+----------------+----------------+---------------+-------------------------------------+ +# +# OTHER TARGETS +# +#|target | description | +#+----------------+----------------------------------------------------------------------+ +#|dumplrest | dump lrest | +#+----------------+----------------------------------------------------------------------+ +#|dumpoperator | dump operator logs | +#+----------------+----------------------------------------------------------------------+ +#|check | print parameters value | +#+----------------+----------------------------------------------------------------------+ +#|tklrestnew | online rest server recreation | +#+----------------+----------------------------------------------------------------------+ +#|mgrrestart | restart manager | +#+----------------+----------------------------------------------------------------------+ +#|listimage | list images available on the cluster | +#+----------------+----------------------------------------------------------------------+ +#|loglrest | tail -f log lrest | +#+----------------+----------------------------------------------------------------------+ +#|cleanyaml | clean yaml files | +#+----------------+----------------------------------------------------------------------+ +#|pkg | tar this directory | +#+----------------+----------------------------------------------------------------------+ +#|run00 | delete and recreate the lrest pod | +#+----------------+----------------------------------------------------------------------+ +#|tkautd | autodiscovery turn on/off *make tkautd AUTOD=false/true* | +#+----------------+----------------------------------------------------------------------+ +#|tkdelcs | lrest pdb delete cascade (on/off) *make tkdelcs DELETECS=true/false* | +#+----------------+----------------------------------------------------------------------+ +#|tkdelcrd | imperative deletion *make tkdelcrd DELETECRD=false LRPDNNAME=* | +#+----------------+----------------------------------------------------------------------+ + +#[END TABLE] + + DATE := `date "+%y%m%d%H%M%S"` ###################### # PARAMETER SECTIONS # @@ -71,6 +127,7 @@ export PDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBPWD|cut -d : -f 2) export PDBNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBNAMESPACE|cut -d : -f 2) export LRSNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep LRSNAMESPACE|cut -d : -f 2) export LRESTIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep LRESTIMG|cut -d : -f 2,3) +export OPRIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPRIMG|cut -d : -f 2,3) export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) export SERVICENAM=$(shell cat $(PARAMETERS)|grep -v ^\#|grep SERVICENAMEACCOUNT|cut -d : -f 2) @@ -80,7 +137,8 @@ export OPRNAMESPACE=oracle-database-operator-system export CODE_TREE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CODE_TREE|cut -d : -f 2) export ORACLE_OPERATOR_YAML=$(CODE_TREE)/oracle-database-operator.yaml export AUTODISCOVER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep AUTODISCOVER|cut -d : -f 2) -export CERT_MANAGER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CERT_MANAGER| awk -F ':' '{$$1="" ;printf("%s:%s",$$2,$$3) }') +export CERT_MANAGER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CERT_MANAGER| awk -F ':' '{$$1="" ;\ + if ($$2 == "https" ) {printf("%s:%s",$$2,$$3) } else {printf ("%s",$$2)}}') export TEST_EXEC_TIMEOUT=3m REST_SERVER=lrest @@ -103,9 +161,9 @@ PDBPWDFILE=pdbpwd.txt export ASSERTDELETION=true -################# -### FILE LIST ### -################# +####################### +### BEGIN FILE LIST ### +####################### export LREST_POD=create_lrest_pod.yaml @@ -157,11 +215,13 @@ CP=/usr/bin/cp TAR=/usr/bin/tar MKDIR=/usr/bin/mkdir SED=/usr/bin/sed +DIFF=/usr/bin/diff MAKE=/usr/bin/make MAKEFILE=./makefile check: @printf "LRESTIMG...............:%s\n" $(LRESTIMG) + @printf "OPRIMG.................:%s\n" $(OPRIMG) @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) @printf "DBUSER.................:%s\n" $(DBUSER) @printf "DBPASS.................:%s\n" $(DBPASS) @@ -186,6 +246,21 @@ define msg @printf "\033[31;7m[\xF0\x9F\x91\x89 %s\033[0m\n" $(1) endef +define _gendoc +echo producing markdown tables +cat ./makefile |awk 'BEGIN { PRINTLINE=0; } +{ +if (( PRINTLINE==1 ) && ( $0 !~ /+-----/ ) && ( $0 !~ / TABLE/)) { print; } +if ( $0 ~ /BEGIN TABLE/ ) { PRINTLINE=1; } +if ( $0 ~ /END TABLE/ ) { PRINTLINE=0; } +} ' |sed 's/#//g' > toc_targets.md +cat toc_targets.md +endef + +export gendoc = $(value _gendoc) +docs: + eval "$$gendoc" + tls: $(call msg,"TLS GENERATION") #$(OPENSSL) genrsa -out $(PRVKEY) 2048 @@ -255,14 +330,35 @@ cleanCert: ### YAML FILE SECTION ### define _opr + + cp ${ORACLE_OPERATOR_YAML} . export OPBASENAME=`basename ${ORACLE_OPERATOR_YAML}` -#export PDBNAMESPACE=$(cat ${PARAMETERS}|grep -v ^\#|grep PDBNAMESPACE|cut -d : -f 2) +echo "code path..........: ${ORACLE_OPERATOR_YAML}" +echo "basename...........: ${OPBASENAME}" + +if [[ ${CODE_TREE:-"OFF"} == "OFF" ]] ; then +echo "code tree not available" +if [[ ! -f ${OPBASENAME} ]]; then + echo "ERROR: oracle-database-operator.yaml not available" + exit 1 + fi +cp ${OPBASENAME} ${OPBASENAME}.ORIG +else +echo "code tree..........: ${CODE_TREE}" +cp ${ORACLE_OPERATOR_YAML} . +cp ${OPBASENAME} ${OPBASENAME}.ORIG +sed -i 's/value: ""/value: '${OPRNAMESPACE}','${PDBNAMESPACE}','${LRSNAMESPACE}'/g' `basename ${ORACLE_OPERATOR_YAML}` +fi + +if [[ ${OPRIMG:-"OFF"} != "OFF" ]] ; then + export IMG=`cat ${OPBASENAME}| awk '( $1 == "image:" ) && ( NF == 2) { printf("%s",$2); }'` + echo "ORIGINALIMG........: ${IMG}" + echo "NEWIMAG............: ${OPRIMG}" + sed -i 's!image: '${IMG}'!image: '${OPRIMG}'!g' ${OPBASENAME} +fi -cp ${OPBASENAME} ${OPBASENAME}.ORIGNINAL -printf "\n\t\xF0\x9F\x91\x89 ${OPBASENAME}\n\n" -printf "\n\t\xF0\x9F\x91\x89 ${PDBNAMESPACE}\n\n" -sed -i 's/value: ""/value: ${OPRNAMESPACE},$PDBNAMESPACE,${LRSNAMESPACE}/g' ${OPBASENAME} +diff ${OPBASENAME} ${OPBASENAME}.ORIG||true endef export opr = $(value _opr) @@ -280,10 +376,7 @@ opdelete: - $(KUBECTL) delete -f `basename ${ORACLE_OPERATOR_YAML}` prsyaml: -# @ eval "$$opr" - $(CP) ${ORACLE_OPERATOR_YAML} . - ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG - $(SED) -i 's/value: ""/value: $(OPRNAMESPACE),$(PDBNAMESPACE),$(LRSNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` + @ eval "$$opr" ### Certmanager ### crtmgrappl: @@ -1023,13 +1116,43 @@ EOF endef +define _envfile +cat <tkpc_mmk8slenv.sh +export TNSALIAS=${TNSALIAS} +export DBUSER=${DBUSER} +export DBPASS=${DBPASS} +export WBUSER=${WBUSER} +export WBPASS=${WBPASS} +export PDBUSR=${PDBUSR} +export PDBPWD=${PDBPWD} +export PDBNAMESPACE=${PDBNAMESPACE} +export LRSNAMESPACE=${LRSNAMESPACE} +export LRESTIMG=${LRESTIMG} +export COMPANY=${COMPANY} +export APIVERSION=${APIVERSION} +export SERVICENAM=${SERVICENAM} +export OPENSHIFT=${OPENSHIFT} +export PLSQLMAP=${PLSQLMAP} +export OPRNAMESPACE=oracle-database-operator-system +export CODE_TREE=${CODE_TREE} +export ORACLE_OPERATOR_YAML=${ORACLE_OPERATOR_YAML} +export AUTODISCOVER=${AUTODISCOVER} +export CERT_MANAGER=${CERT_MANAGER} +export TEST_EXEC_TIMEOUT=3m +EOF +endef + export script02 = $(value _script02) export script03 = $(value _script03) +export script04 = $(value _envfile) + + genyaml: secyaml @ eval "$$script01" @ eval "$$script02" @ eval "$$script03" + @ eval "$$script04" cleanyaml: - $(RM) $(LRPDBMAP3) $(LRPDBMAP2) $(LRPDBMAP1) $(LRPDBPLUG1) $(LRPDBUNPLUG1) $(LRPDBDELETE2) $(LRPDBDELETE1) $(LRPDBDELETE3) $(LRPDBCLONE2) $(LRPDBCLONE1) $(LRPDBCLOSE3) $(LRPDBCLOSE2) $(LRPDBCLOSE1) $(LRPDBOPEN3) $(LRPDBOPEN2) $(LRPDBOPEN1) $(LRPDBCRE2) $(LRPDBCRE1) $(LREST_POD) ${ALTERSYSTEMYAML} @@ -1046,7 +1169,7 @@ pkg: - $(RM) -rf /tmp/pkgtestplan $(MKDIR) /tmp/pkgtestplan $(CP) -R * /tmp/pkgtestplan - $(CP) ../../../oracle-database-operator.yaml /tmp/pkgtestplan/ + $(CP) $(ORACLE_OPERATOR_YAML) /tmp/pkgtestplan/ $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan ################ @@ -1309,8 +1432,6 @@ tkplsqlexec01: $(KUBECTL) get events --sort-by='.lastTimestamp' -n $(PDBNAMESPACE) $(KUBECTL) get events -n $(PDBNAMESPACE) --sort-by='.lastTimestamp' --field-selector involvedObject.name=$(LRPDBNAME)|grep APPLYSQL - - tkaudosicov: @$(call msg,"==>AUTODISCOVER TESTING<==") $(MAKE) -f $(MAKEFILE) genyaml ASSERTDELETION=false @@ -1324,7 +1445,7 @@ tkaudosicov: $(MAKE) -f $(MAKEFILE) genyaml ASSERTDELETION=true # Proceudure to recreate restserver -tklrestew: +tklrestnew: @$(call msg,"Recrete resetserver") @$(call msg,"disable autodiscover and delete cascade") $(KUBECTL) patch lrest cdb-dev -n $(LRSNAMESPACE) -p '{"spec":{"autodiscover":false,"deletePdbCascade":false}}' --type=merge diff --git a/docs/multitenant/usecase/parameters.txt b/docs/multitenant/usecase/parameters.txt index 710c43fd..34a64c6f 100644 --- a/docs/multitenant/usecase/parameters.txt +++ b/docs/multitenant/usecase/parameters.txt @@ -60,7 +60,14 @@ COMPANY:oracle ########################## ### CODE TREE ### ########################## -CODE_TREE:/path/oracle-database-operator +CODE_TREE:../../../../oracle-database-operator + +######################## +## OPERATOR IMAGE ### +######################## +#Use this parameter if you need to change the image +#OPRIMG:lin.ocir.io/intsanjaysingh/operator/dboperator_master_orahub:test + ########################## ### PDB AUTODISCOVER ### From e252279819beb10b81f3a911f45568389da03f80 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Wed, 10 Sep 2025 22:36:49 +0000 Subject: [PATCH 344/414] usecase makefile glitch --- docs/multitenant/usecase/makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/multitenant/usecase/makefile b/docs/multitenant/usecase/makefile index 648abb01..a05f3e47 100644 --- a/docs/multitenant/usecase/makefile +++ b/docs/multitenant/usecase/makefile @@ -332,7 +332,6 @@ cleanCert: define _opr -cp ${ORACLE_OPERATOR_YAML} . export OPBASENAME=`basename ${ORACLE_OPERATOR_YAML}` echo "code path..........: ${ORACLE_OPERATOR_YAML}" echo "basename...........: ${OPBASENAME}" @@ -348,9 +347,10 @@ else echo "code tree..........: ${CODE_TREE}" cp ${ORACLE_OPERATOR_YAML} . cp ${OPBASENAME} ${OPBASENAME}.ORIG -sed -i 's/value: ""/value: '${OPRNAMESPACE}','${PDBNAMESPACE}','${LRSNAMESPACE}'/g' `basename ${ORACLE_OPERATOR_YAML}` fi +sed -i 's/value: ""/value: '${OPRNAMESPACE}','${PDBNAMESPACE}','${LRSNAMESPACE}'/g' `basename ${ORACLE_OPERATOR_YAML}` + if [[ ${OPRIMG:-"OFF"} != "OFF" ]] ; then export IMG=`cat ${OPBASENAME}| awk '( $1 == "image:" ) && ( NF == 2) { printf("%s",$2); }'` echo "ORIGINALIMG........: ${IMG}" From 29f700a0398696ad0740795231276797e0639101 Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Thu, 11 Sep 2025 05:30:28 +0000 Subject: [PATCH 345/414] Sharding validations --- docs/sharding/README.md | 14 +-- .../sharding_provisioning_with_db_events.yaml | 82 ++++++++++---- ...harding_provisioning_with_free_images.yaml | 72 +++++++++--- docs/sharding/provisioning/oraclesi.yaml | 26 ++--- .../provisioning/oraclesi_pvc_commented.yaml | 28 ++--- ...y_cloning_db_from_gold_image_across_ads.md | 58 ---------- ...ing_by_cloning_db_gold_image_in_same_ad.md | 54 --------- ...ding_provisioning_with_chunks_specified.md | 44 -------- ...rding_scale_in_delete_an_existing_shard.md | 51 --------- .../snr_ssharding_scale_out_add_shards.md | 38 ------- .../snr_ssharding_shard_prov.yaml | 74 ++++++++++--- .../snr_ssharding_shard_prov_chunks.yaml | 61 ---------- .../snr_ssharding_shard_prov_clone.yaml | 83 -------------- ...ssharding_shard_prov_clone_across_ads.yaml | 91 --------------- .../snr_ssharding_shard_prov_delshard.yaml | 69 ------------ .../snr_ssharding_shard_prov_extshard.yaml | 68 ------------ .../snr_ssharding_shard_prov_memory_cpu.yaml | 88 +++++++++++---- ...sharding_shard_prov_send_notification.yaml | 98 ++++++++++++----- ...ding_provisioning_with_chunks_specified.md | 2 +- ..._provisioning_with_control_on_resources.md | 2 +- ...ith_notification_using_oci_notification.md | 2 +- ...ding_provisioning_without_db_gold_image.md | 2 +- ...rding_scale_in_delete_an_existing_shard.md | 2 +- .../ssharding_scale_out_add_shards.md | 2 +- .../system_sharding/ssharding_shard_prov.yaml | 72 ++++++++---- .../ssharding_shard_prov_chunks.yaml | 78 +++++++++---- .../ssharding_shard_prov_clone.yaml | 90 ++++++++++----- ...ssharding_shard_prov_clone_across_ads.yaml | 98 +++++++++++------ .../ssharding_shard_prov_delshard.yaml | 76 +++++++++---- .../ssharding_shard_prov_extshard.yaml | 78 +++++++++---- .../ssharding_shard_prov_memory_cpu.yaml | 88 ++++++++++----- ...sharding_shard_prov_send_notification.yaml | 97 +++++++++++----- ..._provisioning_with_control_on_resources.md | 2 +- ...ith_notification_using_oci_notification.md | 2 +- ...ding_provisioning_without_db_gold_image.md | 2 +- ...rding_scale_in_delete_an_existing_shard.md | 2 +- .../udsharding_scale_out_add_shards.md | 2 +- .../udsharding_shard_prov.yaml | 76 +++++++++---- .../udsharding_shard_prov_clone.yaml | 90 ++++++++++----- ...dsharding_shard_prov_clone_across_ads.yaml | 96 +++++++++++----- .../udsharding_shard_prov_delshard.yaml | 78 +++++++++---- .../udsharding_shard_prov_extshard.yaml | 81 ++++++++++---- .../udsharding_shard_prov_memory_cpu.yaml | 87 +++++++++++---- ...sharding_shard_prov_send_notification.yaml | 104 +++++++++++++----- 44 files changed, 1262 insertions(+), 1148 deletions(-) delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml diff --git a/docs/sharding/README.md b/docs/sharding/README.md index 487d9ec3..47c5853e 100644 --- a/docs/sharding/README.md +++ b/docs/sharding/README.md @@ -132,6 +132,7 @@ If you want to use Oracle Database 23ai Free Image for Database and GSM for depl * For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. * Provisioning the Oracle Globally Distributed Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. * Total number of chunks for FREE Database defaults to `12` if `CATALOG_CHUNKS` parameter is not specified. This default value is determined considering limitation of 12 GB of user data on disk for oracle free database. +* Oracle 23ai FREE Database supports maximum three shards. Please refer [here](https://docs.oracle.com/en/database/oracle/oracle-database/23/dblic/Licensing-Information.html) ## Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding in a Cloud-Based Kubernetes Cluster @@ -169,18 +170,15 @@ In this example, the deployment uses the YAML file based on `OCI OKE` cluster. T Deploy Oracle Globally Distributed Database Topology with `System-Managed Sharding` and with `RAFT Replication` enabled on your Cloud based Kubernetes cluster. -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** +**NOTE:** RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version. +**NOTE:** RAFT Replication requires atleast three shards. Oracle 23ai FREE Database supports maximum three shards. Please refer [here](https://docs.oracle.com/en/database/oracle/oracle-database/23/dblic/Licensing-Information.html) In this example, the deployment uses the YAML file based on `OCI OKE` cluster. There are multiple use case possible for deploying the Oracle Globally Distributed Database Topology covered by below examples: [1. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled without Database Gold Image](./provisioning/snr_system_sharding/snr_ssharding_provisioning_without_db_gold_image.md) -[2. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled with number of chunks specified](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md) -[3. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled with additional control on resources like Memory and CPU allocated to Pods](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md) -[4. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md) -[5. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled by cloning database from your own Database Gold Image across Availability Domains(ADs)](./provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md) -[6. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled send Notification using OCI Notification Service](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md) -[7. Scale Out - Add Shards to an existing Oracle Globally Distributed Database provisioned earlier with System-Managed Sharding and RAFT replication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md) -[8. Scale In - Delete an existing Shard from a working Oracle Globally Distributed Database provisioned earlier with System-Managed Sharding and RAFT reolication enabled](./provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md) +[2. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled with additional control on resources like Memory and CPU allocated to Pods](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_control_on_resources.md) +[3. Provisioning Oracle Globally Distributed Database Topology with System-Managed Sharding and Raft replication enabled send Notification using OCI Notification Service](./provisioning/snr_system_sharding/snr_ssharding_provisioning_with_notification_using_oci_notification.md) + ## Connecting to Oracle Globally Distributed Database diff --git a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml index 40ad600a..fcf5939f 100644 --- a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml +++ b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml @@ -3,18 +3,29 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database + + # Defined Environment Variable "DB_EVENTS" and its Value as the event tracing to be enabled at the Database Level envVars: - name: "DB_EVENTS" value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" @@ -25,7 +36,7 @@ spec: shardRegion: primary envVars: - name: "DB_EVENTS" - value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" + value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" - name: shard3 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -34,35 +45,58 @@ spec: envVars: - name: "DB_EVENTS" value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - envVars: + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + envVars: - name: "DB_EVENTS" - value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" + value: "10798 trace name context forever, level 7:scope=spfile;immediate trace name GWM_TRACE level 263" + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise:latest + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.27.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.27.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml index dadd619a..a2258f96 100644 --- a/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml +++ b/docs/sharding/provisioning/free/sharding_provisioning_with_free_images.yaml @@ -3,18 +3,27 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -25,33 +34,60 @@ spec: imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci + + # Database Container Image to be used for Catalog and Shard Database dbImage: container-registry.oracle.com/database/free:latest + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred + + # GSM Container Image to be used for GSM Pods gsmImage: container-registry.oracle.com/database/gsm:latest + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Specify the Database Edition as "free" for Oracle Database FREE dbEdition: "free" + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary + ruMode: READWRITE # Specify the ruMode for the service as READWRITE in RAFT Replication Setup - name: oltp_ro_svc role: primary + ruMode: READONLY # Specify the ruMode for the service as READWRITE in RAFT Replication Setup diff --git a/docs/sharding/provisioning/oraclesi.yaml b/docs/sharding/provisioning/oraclesi.yaml index cac70ffa..fa36a10a 100644 --- a/docs/sharding/provisioning/oraclesi.yaml +++ b/docs/sharding/provisioning/oraclesi.yaml @@ -5,10 +5,10 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: oshard-gold-image-pvc21c + name: oshard-gold-image-pvc19c namespace: shns labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: accessModes: - ReadWriteOnce @@ -18,28 +18,28 @@ spec: storageClassName: oci selector: matchLabels: - topology.kubernetes.io/zone: "PHX-AD-1" + topology.kubernetes.io/zone: "PHX-AD-3" --- apiVersion: apps/v1 kind: StatefulSet metadata: - name: oshard21cdb + name: oshard19cdb namespace: shns labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: selector: matchLabels: - app: oshard21cdb-dep + app: oshard19cdb-dep serviceName: gold-shard template: metadata: labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: containers: - - image: container-registry.oracle.com/database/enterprise:latest - name: oshard21cdb + - image: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + name: oshard19cdb ports: - containerPort: 1521 name: db1-dbport @@ -68,17 +68,17 @@ spec: volumes: - name: data persistentVolumeClaim: - claimName: oshard-gold-image-pvc21c + claimName: oshard-gold-image-pvc19c - name: dshm emptyDir: medium: Memory nodeSelector: - topology.kubernetes.io/zone: "PHX-AD-1" + topology.kubernetes.io/zone: "PHX-AD-3" --- apiVersion: v1 kind: Service metadata: - name: oshard21cdb + name: oshard19cdb namespace: shns spec: ports: @@ -95,4 +95,4 @@ spec: port: 6234 targetPort: db1-onsrport selector: - app: oshard21cdb-dep \ No newline at end of file + app: oshard19cdb-dep diff --git a/docs/sharding/provisioning/oraclesi_pvc_commented.yaml b/docs/sharding/provisioning/oraclesi_pvc_commented.yaml index 43b50a5e..a54e07a5 100644 --- a/docs/sharding/provisioning/oraclesi_pvc_commented.yaml +++ b/docs/sharding/provisioning/oraclesi_pvc_commented.yaml @@ -5,10 +5,10 @@ #apiVersion: v1 #kind: PersistentVolumeClaim #metadata: -# name: oshard-gold-image-pvc21c +# name: oshard-gold-image-pvc19c # namespace: shns # labels: -# app: oshard21cdb-dep +# app: oshard19cdb-dep #spec: # accessModes: # - ReadWriteOnce @@ -18,28 +18,28 @@ # storageClassName: oci # selector: # matchLabels: -# topology.kubernetes.io/zone: "PHX-AD-1" -#--- +# topology.kubernetes.io/zone: "PHX-AD-3" +--- apiVersion: apps/v1 kind: StatefulSet metadata: - name: oshard21cdb + name: oshard19cdb namespace: shns labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: selector: matchLabels: - app: oshard21cdb-dep + app: oshard19cdb-dep serviceName: gold-shard template: metadata: labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: containers: - - image: container-registry.oracle.com/database/enterprise:latest - name: oshard21cdb + - image: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + name: oshard19cdb ports: - containerPort: 1521 name: db1-dbport @@ -68,17 +68,17 @@ spec: volumes: - name: data persistentVolumeClaim: - claimName: oshard-gold-image-pvc21c + claimName: oshard-gold-image-pvc19c - name: dshm emptyDir: medium: Memory nodeSelector: - topology.kubernetes.io/zone: "PHX-AD-1" + topology.kubernetes.io/zone: "PHX-AD-3" --- apiVersion: v1 kind: Service metadata: - name: oshard21cdb + name: oshard19cdb namespace: shns spec: ports: @@ -95,4 +95,4 @@ spec: port: 6234 targetPort: db1-onsrport selector: - app: oshard21cdb-dep \ No newline at end of file + app: oshard19cdb-dep diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md deleted file mode 100644 index 9ffebad9..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md +++ /dev/null @@ -1,58 +0,0 @@ -# Provisioning System managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image across Availability Domains(ADs) - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this test case, you provision the System managed Sharding Topology with Raft replication enabled while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. - -This use case applies when you want to provision the database Pods on a Kubernetes Node in any availability domain (AD), which can also be different from the availability domain (AD) of the Block Volume that has the Oracle Database Gold Image provisioned earlier. - -Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup across ADs. - -NOTE: - -* Cloning from Block Volume Backup in OCI enables the new Persistent Volumes to be created in other ADs. -* To specify the AD where you want to provision the database Pod, use the tag `nodeSelector` and the POD will be provisioned in a node running in that AD. -* To specify GSM containers, you can also use the tag `nodeSelector` to specify the AD. -* Before you can provision with the Gold Image, you need the OCID of the Persistent Volume that has the Oracle Database Gold Image. - -1. Check the OCID of the Persistent Volume provisioned for the Oracle Database Gold Image: - ```sh - kubectl get pv -n shns - ``` -2. Create a Block Volume Backup for this Block Volume, and use the OCID of the Block Volume Backup in the next step. This example uses `snr_ssharding_shard_prov_clone_across_ads.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume which had the Gold Image. -* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` -* `RAFT Replication` enabled - -NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned across multiple Availability Domains by cloning the database. - -**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone_across_ads.yaml`. - * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - -Use the file: [snr_ssharding_shard_prov_clone_across_ads.yaml](./snr_ssharding_shard_prov_clone_across_ads.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_clone_across_ads.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_clone_across_ads.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md deleted file mode 100644 index 054d760e..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md +++ /dev/null @@ -1,54 +0,0 @@ -# Provisioning System managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD) - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this test case, you provision the System managed Sharding Topology with Raft replication enabled while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. - -This use case applies when you are cloning from a Block Volume, and you can clone _only_ in the same availability domain (AD). The result is that the cloned shard database PODs can be created _only_ in the same AD where the Gold Image Block Volume is present. - -Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup. - -**NOTE** For this step, the Persistent Volume that has the Oracle Database Gold Image is identified using its OCID. - -1. Check the OCID of the Persistent Volume provisioned earlier using below command: - - ```sh - kubectl get pv -n shns - ``` - -2. This example uses `snr_ssharding_shard_prov_clone.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* Database Cloning from the Database Gold Image present in Persistent Volume having OCID: `ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq` -* `RAFT Replication` enabled - -NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned in the same Availability Domain `PHX-AD-1` by cloning the database. - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone.yaml`. - * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - -**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. - -Use the file: [snr_ssharding_shard_prov_clone.yaml](./snr_ssharding_shard_prov_clone.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_clone.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_clone.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md deleted file mode 100644 index 253d099b..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md +++ /dev/null @@ -1,44 +0,0 @@ -# Provisioning System-Managed Sharding Topology with Raft replication enabled with number of chunks specified - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System-Managed with RAFT Replication enabled is deployed using Oracle Sharding controller. - -**NOTE** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. - -By default, the System-Managed with RAFT Replication deploys the Sharded Database with 360 chunks per Shard Database (because there are 3 chunks created for each replication unit). In this example, the Sharded Database will be deployed with non-default number of chunks specified using parameter `CATALOG_CHUNKS`. - -This example uses `snr_ssharding_shard_prov_chunks.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Total number of chunks as `120` specified by variable `CATALOG_CHUNKS` (it will be 120 chunks per shard) -* Namespace: `shns` -* `RAFT Replication` enabled - - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - - -Use the file: [snr_ssharding_shard_prov_chunks.yaml](./snr_ssharding_shard_prov_chunks.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_chunks.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_chunks.yaml - ``` -1. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md deleted file mode 100644 index fc093654..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md +++ /dev/null @@ -1,51 +0,0 @@ -# Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT reolication enabled - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with System-Managed with RAFT Replication enabled provisioned using Oracle Database Sharding controller. - -**NOTE** The deletion of a shard is done after verifying the Chunks have been moved out of that shard. - -In this use case, the existing database Sharding is having: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Five sharding Pods: `shard1`,`shard2`,`shard3`,`shard4` and `shard5` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* `RAFT Replication` enabled - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_delshard.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - -NOTE: Use tag `isDelete: enable` to delete the shard you want. - -This use case deletes the shard `shard4` from the above Sharding Topology. - -Use the file: [snr_ssharding_shard_prov_delshard.yaml](./snr_ssharding_shard_prov_delshard.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_delshard.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_delshard.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - -**NOTE:** After you apply `snr_ssharding_shard_prov_delshard.yaml`, the change may not be visible immediately. When the shard is removed, first the chunks will be moved out of that shard that is going to be deleted. - -To monitor the chunk movement, use the following command: - -```sh -# Switch to the primary GSM Container: -kubectl exec -i -t gsm1-0 -n shns /bin/bash - -# Check the status of the chunks and repeat to observe the chunk movement: -gdsctl config chunks -``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md deleted file mode 100644 index 3461bf13..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md +++ /dev/null @@ -1,38 +0,0 @@ -# Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT replication enabled - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with System-Managed with RAFT Replication enabled provisioned earlier using Oracle Database Sharding controller. - -In this use case, the existing Oracle Database sharding topology is having: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* `RAFT Replication` enabled - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_extshard.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - -This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. - -Use the file: [snr_ssharding_shard_prov_extshard.yaml](./snr_ssharding_shard_prov_extshard.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_extshard.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_extshard.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard4-0": - kubectl logs -f pod/shard4-0 -n shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml index 53b93a0d..2f7b6ebe 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov.yaml @@ -3,18 +3,27 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -25,34 +34,63 @@ spec: imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci + + # Database Container Image to be used for Catalog and Shard Database dbImage: container-registry.oracle.com/database/free:latest + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred + + # GSM Container Image to be used for GSM Pods gsmImage: container-registry.oracle.com/database/gsm:latest + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Specify the Database Edition as "free" for Oracle Database FREE dbEdition: "free" + + # Parameter value for RAFT Replication for the Oracle Globally Distributed Database replicationType: "native" + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary + ruMode: READWRITE # Specify the ruMode for the service as READWRITE in RAFT Replication Setup - name: oltp_ro_svc role: primary + ruMode: READONLY # Specify the ruMode for the service as READWRITE in RAFT Replication Setup diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml deleted file mode 100644 index 0230eac2..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - envVars: - - name: "CATALOG_CHUNKS" - value: "120" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml deleted file mode 100644 index fcc18da0..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - isClone: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml deleted file mode 100644 index 0663f8a5..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml +++ /dev/null @@ -1,91 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - isClone: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml deleted file mode 100644 index ce194246..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard4 - isDelete: enable - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard5 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml deleted file mode 100644 index 8848b8c7..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml +++ /dev/null @@ -1,68 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard4 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard5 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml index dce4ba29..191fd6dc 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml @@ -3,27 +3,40 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + + # Memory and CPU allocation for the Shard Database pod resources: requests: memory: "1000Mi" cpu: "1000m" + + # SGA and PGA values for the Shard Database envVars: - name: "INIT_SGA_SIZE" value: "600" - name: "INIT_PGA_SIZE" - value: "400" - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + value: "400" + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 resources: @@ -34,7 +47,7 @@ spec: - name: "INIT_SGA_SIZE" value: "600" - name: "INIT_PGA_SIZE" - value: "400" + value: "400" imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary @@ -48,42 +61,71 @@ spec: - name: "INIT_SGA_SIZE" value: "600" - name: "INIT_PGA_SIZE" - value: "400" + value: "400" imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod resources: requests: memory: "1000Mi" - cpu: "1000m" - imagePullPolicy: "Always" + cpu: "1000m" + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci + + # Database Container Image to be used for Catalog and Shard Database dbImage: container-registry.oracle.com/database/free:latest + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred + + # GSM Container Image to be used for GSM Pods gsmImage: container-registry.oracle.com/database/gsm:latest + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Specify the Database Edition as "free" for Oracle Database FREE dbEdition: "free" - replicationType: "native" + + # Parameter value for RAFT Replication for the Oracle Globally Distributed Database + replicationType: "native" + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary + ruMode: READWRITE # Specify the ruMode for the service as READWRITE in RAFT Replication Setup - name: oltp_ro_svc role: primary + ruMode: READONLY # Specify the ruMode for the service as READWRITE in RAFT Replication Setup \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml index 2b410e8b..55690010 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml @@ -3,24 +3,33 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created. It should be same as the nodeSelector. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq # Specify the OCID of the Block Volume Backup which has the Database Gold Image + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 nodeSelector: @@ -28,7 +37,7 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary @@ -39,47 +48,82 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod nodeSelector: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci + + # Database Container Image to be used for Catalog and Shard Database dbImage: container-registry.oracle.com/database/free:latest + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred + + # GSM Container Image to be used for GSM Pods gsmImage: container-registry.oracle.com/database/gsm:latest + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Specify the Database Edition as "free" for Oracle Database FREE dbEdition: "free" - replicationType: "native" + + # Parameter value for RAFT Replication for the Oracle Globally Distributed Database + replicationType: "native" + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True - isClone: True + + # To specify if Catalog and Shard Databases will be deployed by cloning from the Gold Image + isClone: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Specify the configmap created with the credentials of the OCI User Account which will be used for sending the OCI Notification nsConfigMap: onsconfigmap - nsSecret: my-secret + + # Specify the secret which has the privatekey for the OCI User Account to be used for sending the OCI Notification + nsSecret: my-secret + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary + ruMode: READWRITE # Specify the ruMode for the service as READWRITE in RAFT Replication Setup - name: oltp_ro_svc role: primary + ruMode: READONLY # Specify the ruMode for the service as READWRITE in RAFT Replication Setup \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md index 649fc7c4..5e287e60 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_chunks_specified.md @@ -21,7 +21,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md index d284bf9b..2e3d516f 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md @@ -17,7 +17,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_memory_cpu.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md index e77718f4..f903c401 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md @@ -65,7 +65,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_send_notification.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md index 1ecb0ec1..011b553f 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md @@ -18,7 +18,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [ssharding_shard_prov.yaml](./ssharding_shard_prov.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md index 889de98c..da7cf3fe 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_scale_in_delete_an_existing_shard.md @@ -17,7 +17,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_delshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. NOTE: Use tag `isDelete: enable` to delete the shard you want. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md index 5086d887..32ecfc7c 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md @@ -15,7 +15,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_extshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml index 1bdb9ce5..d09e93c7 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml @@ -3,18 +3,27 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -25,30 +34,53 @@ spec: imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml index 868e8bc1..c261a1cb 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_chunks.yaml @@ -3,18 +3,27 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -25,33 +34,58 @@ spec: imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Specify the environment variables for the Catalog Database envVars: - - name: "CATALOG_CHUNKS" - value: "120" + - name: "CATALOG_CHUNKS" # Specify variable for the the number of chunks to be configured + value: "120" # Specify the value of the variable + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml index 3cafeba7..6bc1917f 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml @@ -3,24 +3,33 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created. It should be same as the nodeSelector. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq # Specify the OCID of the Block Volume which has the Database Gold Image + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 nodeSelector: @@ -28,7 +37,7 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary @@ -39,43 +48,68 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod nodeSelector: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True - isClone: True + + # To specify if Catalog and Shard Databases will be deployed by cloning from the Gold Image + isClone: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml index d7ec6365..c0b4760e 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml @@ -3,24 +3,33 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created.It should be same as the nodeSelector. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq # Specify the OCID of the Block Volume Backup which has the Database Gold Image + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 nodeSelector: @@ -28,7 +37,7 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary @@ -39,51 +48,76 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod nodeSelector: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created. It should be same as the nodeSelector. + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database nodeSelector: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True - isClone: True + + # To specify if Catalog and Shard Databases will be deployed by cloning from the Gold Image + isClone: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml index 1017a9d5..01619fd9 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml @@ -3,18 +3,27 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -26,7 +35,7 @@ spec: shardGroup: shardgroup1 shardRegion: primary - name: shard4 - isDelete: enable + isDelete: enable # Specify this parameter "isDelete: enable" for the shard which has to be deleted during the Scale In Operation storageSizeInGb: 50 imagePullPolicy: "Always" shardGroup: shardgroup1 @@ -36,32 +45,55 @@ spec: imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml index d23052fb..078a7858 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml @@ -3,18 +3,27 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -25,6 +34,8 @@ spec: imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the Additional two shards to be added during the Scale Out Operation - name: shard4 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -34,33 +45,56 @@ spec: storageSizeInGb: 50 imagePullPolicy: "Always" shardGroup: shardgroup1 - shardRegion: primary + shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml index 075919f7..8876084a 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml @@ -3,27 +3,40 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + + # Memory and CPU allocation for the Shard Database pod resources: requests: memory: "1000Mi" cpu: "1000m" + + # SGA and PGA values for the Shard Database envVars: - name: "INIT_SGA_SIZE" value: "600" - name: "INIT_PGA_SIZE" - value: "400" - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + value: "400" + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 resources: @@ -34,7 +47,7 @@ spec: - name: "INIT_SGA_SIZE" value: "600" - name: "INIT_PGA_SIZE" - value: "400" + value: "400" imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary @@ -48,40 +61,65 @@ spec: - name: "INIT_SGA_SIZE" value: "600" - name: "INIT_PGA_SIZE" - value: "400" + value: "400" imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + + # Memory and CPU allocation for the Catalog Database pod resources: requests: memory: "1000Mi" - cpu: "1000m" - imagePullPolicy: "Always" + cpu: "1000m" + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml index aea6fc7c..abda743c 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml @@ -3,24 +3,33 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created. It should be same as the nodeSelector. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq # Specify the OCID of the Block Volume Backup which has the Database Gold Image + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardGroup: shardgroup1 # Shard Group name for the Shard Database + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 nodeSelector: @@ -28,7 +37,7 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary @@ -39,46 +48,74 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq imagePullPolicy: "Always" shardGroup: shardgroup1 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod nodeSelector: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True - isClone: True + + # To specify if Catalog and Shard Databases will be deployed by cloning from the Gold Image + isClone: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Specify the configmap created with the credentials of the OCI User Account which will be used for sending the OCI Notification nsConfigMap: onsconfigmap - nsSecret: my-secret + + # Specify the secret which has the privatekey for the OCI User Account to be used for sending the OCI Notification + nsSecret: my-secret + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary - + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md index 638b7124..9e19f4be 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md @@ -18,7 +18,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_memory_cpu.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md index fe1ca870..f342987f 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md @@ -66,7 +66,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_send_notification.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md index b0378e04..cd831469 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md @@ -19,7 +19,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. Use the file: [udsharding_shard_prov.yaml](./udsharding_shard_prov.yaml) for this use case as below: diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md index 673e455e..28094146 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md @@ -16,7 +16,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_delshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. **NOTE:** Use tag `isDelete: enable` to delete the shard you want. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md index abdc53ff..fbc57190 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md @@ -16,7 +16,7 @@ In this example, we are using pre-built Oracle Database and Global Data Services * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_extshard.yaml`. * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. + * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml index c9f20eb3..6553fdb3 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml @@ -3,18 +3,27 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardSpace: sspace1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardSpace: sspace1 # Shard Space name for the Shard Database with User Defined Sharding + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -25,33 +34,58 @@ spec: imagePullPolicy: "Always" shardSpace: sspace3 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Type of the Sharding. By Default, it is System Sharding unless specified to be USER or NATIVE shardingType: USER + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml index d7e5ce78..522f6cd9 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml @@ -3,24 +3,33 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - nodeSelector: + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created. It should be same as the nodeSelector. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - shardSpace: sspace1 - shardRegion: primary + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq # Specify the OCID of the Block Volume which has the Database Gold Image + shardSpace: sspace1 # Shard Space name for the Shard Database with User Defined Sharding + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -40,43 +49,70 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq shardSpace: sspace3 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image nodeSelector: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Type of the Sharding. By Default, it is System Sharding unless specified to be USER or NATIVE shardingType: USER + + # To specify if Catalog and Shard Databases will be deployed by cloning from the Gold Image + isClone: True + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True - isClone: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml index ae02c7fe..0fc7ed0d 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml @@ -3,24 +3,33 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - nodeSelector: + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created. It should be same as the nodeSelector. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - shardSpace: sspace1 - shardRegion: primary + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq # Specify the OCID of the Block Volume Backup which has the Database Gold Image + shardSpace: sspace1 # Shard Space name for the Shard Database with User Defined Sharding + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -40,51 +49,78 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq shardSpace: sspace3 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image nodeSelector: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - nodeSelector: + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created. It should be same as the nodeSelector. + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database nodeSelector: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Type of the Sharding. By Default, it is System Sharding unless specified to be USER or NATIVE shardingType: USER + + # To specify if Catalog and Shard Databases will be deployed by cloning from the Gold Image + isClone: True + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True - isClone: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml index d83bf546..4f858b87 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml @@ -3,18 +3,27 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardSpace: sspace1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardSpace: sspace1 # Shard Space name for the Shard Database with User Defined Sharding + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -26,43 +35,68 @@ spec: shardSpace: sspace3 shardRegion: primary - name: shard4 + isDelete: enable # Specify this parameter "isDelete: enable" for the shard which has to be deleted during the Scale In Operation storageSizeInGb: 50 imagePullPolicy: "Always" shardSpace: sspace4 shardRegion: primary - isDelete: enable - name: shard5 storageSizeInGb: 50 imagePullPolicy: "Always" shardSpace: sspace5 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Type of the Sharding. By Default, it is System Sharding unless specified to be USER or NATIVE shardingType: USER + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml index 7526feb7..0929ae89 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml @@ -2,18 +2,28 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # +--- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardSpace: sspace1 - shardRegion: primary + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardSpace: sspace1 # Shard Space name for the Shard Database with User Defined Sharding + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -24,6 +34,8 @@ spec: imagePullPolicy: "Always" shardSpace: sspace3 shardRegion: primary + + # Details of the Additional two shards to be added during the Scale Out Operation - name: shard4 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -33,34 +45,59 @@ spec: storageSizeInGb: 50 imagePullPolicy: "Always" shardSpace: sspace5 - shardRegion: primary + shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Type of the Sharding. By Default, it is System Sharding unless specified to be USER or NATIVE shardingType: USER + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml index 8be81d39..a2b16ebb 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml @@ -3,27 +3,40 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + + # Memory and CPU allocation for the Shard Database pod resources: requests: memory: "1000Mi" cpu: "1000m" + + # SGA and PGA values for the Shard Database envVars: - name: "INIT_SGA_SIZE" value: "600" - name: "INIT_PGA_SIZE" - value: "400" - imagePullPolicy: "Always" - shardSpace: sspace1 - shardRegion: primary + value: "400" + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + shardSpace: sspace1 # Shard Space name for the Shard Database with User Defined Sharding + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 resources: @@ -34,7 +47,7 @@ spec: - name: "INIT_SGA_SIZE" value: "600" - name: "INIT_PGA_SIZE" - value: "400" + value: "400" imagePullPolicy: "Always" shardSpace: sspace2 shardRegion: primary @@ -48,41 +61,67 @@ spec: - name: "INIT_SGA_SIZE" value: "600" - name: "INIT_PGA_SIZE" - value: "400" + value: "400" imagePullPolicy: "Always" shardSpace: sspace3 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image resources: requests: memory: "1000Mi" cpu: "1000m" - imagePullPolicy: "Always" + imagePullPolicy: "Always" + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Type of the Sharding. By Default, it is System Sharding unless specified to be USER or NATIVE shardingType: USER + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml index 4dda6db9..163e77b8 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml @@ -3,24 +3,33 @@ # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # --- +# API version of the ShardingDatabase Custom Resource apiVersion: database.oracle.com/v4 + +# Resource type being created kind: ShardingDatabase + +# Metadata section to define the resource's name and namespace metadata: - name: shardingdatabase-sample - namespace: shns + name: shardingdatabase-sample # Name of the ShardingDatabase instance + namespace: shns # Kubernetes namespace to deploy into + +# Specs for the ShardingDatabase instance spec: + + # Details of the individual shard databases shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - nodeSelector: + - name: shard1 # Name of the shard database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created. It should be same as the nodeSelector. "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - shardSpace: sspace1 - shardRegion: primary + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq # Specify the OCID of the Block Volume Backup which has the Database Gold Image + shardSpace: sspace1 # Shard Space name for the Shard Database with User Defined Sharding + shardRegion: primary # Name of the region for the Shard Database - name: shard2 storageSizeInGb: 50 imagePullPolicy: "Always" @@ -40,45 +49,84 @@ spec: pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq shardSpace: sspace3 shardRegion: primary + + # Details of the individual shard databases catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" + - name: catalog # Name of the Catalog database + storageSizeInGb: 50 # Size of the Disk Storage allocated to the Shard Database pod + imagePullPolicy: "Always" # Image Pull Policy for the Database Container Image nodeSelector: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvMatchLabels: "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq + + # Details of the GSM Pods gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby + - name: gsm1 # Name of the Primary Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: primary # Name of the Primary region for the Shard Database + nodeSelector: # Node Selector the worker node to deploy the Pod. Worker Node with this label will be selected for the Pod. + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: # To specify the availability domain where the Persistent Volume will be created. It should be same as the nodeSelector. + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + + - name: gsm2 # Name of the Standby Global Service Manager + imagePullPolicy: "Always" # Image Pull Policy for the GSM Container Image + storageSizeInGb: 50 # Size of the Disk Storage allocated to the GSM pod + region: standby # Name of the Standby region for the Shard Database + nodeSelector: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + pvMatchLabels: + "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" + + # Kubernetes Storage Class Name storageClass: oci - dbImage: container-registry.oracle.com/database/enterprise_ru:19.25.0.0 + + # Database Container Image to be used for Catalog and Shard Database + dbImage: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 + + # Database Conrainer Image Pull Secret dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm_ru:19.25.0.0 + + # GSM Container Image to be used for GSM Pods + gsmImage: container-registry.oracle.com/database/gsm_ru:19.28.0.0 + + # GSM Conrainer Image Pull Secret gsmImagePullSecret: ocr-reg-cred + + # Type of the Sharding. By Default, it is System Sharding unless specified to be USER or NATIVE shardingType: USER + + # To specify if Catalog and Shard Databases will be deployed by cloning from the Gold Image + isClone: True + + # Whether an External Load Balancer has to be configured for the Catalog, Shard and GSM Pods isExternalSvc: False + + # If set to true, the Persistent Volumes will be deleted when the deployment for the Oracle Globally Distributed Database is deleted isDeleteOraPvc: True - isClone: True + + # Details of the Database Secret dbSecret: name: db-user-pass-rsa pwdFileName: pwdfile.enc keyFileName: key.pem + + # Specify the configmap created with the credentials of the OCI User Account which will be used for sending the OCI Notification nsConfigMap: onsconfigmap - nsSecret: my-secret + + # Specify the secret which has the privatekey for the OCI User Account to be used for sending the OCI Notification + nsSecret: my-secret + + # Details of Services to be created for the Oracle Globally Distributed Database Resource gsmService: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file From b7edec69a2ff6fc15f572c89b681596c470f2052 Mon Sep 17 00:00:00 2001 From: racpack Date: Thu, 11 Sep 2025 15:08:10 +0000 Subject: [PATCH 346/414] Update file README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 960133b8..5d0a8646 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,6 @@ As of v2.0.0, the Oracle Database Operator for Kubernetes (`OraOperator`) suppor * **ADB-S/ADB-D**: Provision, bind, start, stop, terminate (soft/hard), scale (up/down), long-term backup, manual restore, cloning, manual failover, switchover * **ACD**: Provision, bind, restart, terminate (soft/hard) * **SIDB**: Provision, clone, patch (in-place/out-of-place), update database initialization parameters, update database configuration (Flashback, archiving), Oracle Enterprise Manager (EM) Express (basic console), Oracle REST Data Service (ORDS), PDB management, SQL Developer Web, Application Express (Apex), Resource management, True Cache, Observer for FastStartFailover (Data Guard), Snapshot Standby (Data Guard) -* **Oracle Restart**: Provision and manage Oracle Restart environments * **ORDS Services**: Provision and delete ORDS instances * **Globally Distributed (Sharded)**: Provision/deploy sharded databases and topology, add/delete shards, Raft replication * **Oracle Multitenant Database**: Bind to a CDB, create/plug/unplug/delete/clone/open/close PDBs, assertive deletion policy @@ -92,13 +91,14 @@ This production release has been installed and tested on: The previous releases were tested on the following platforms: * [Oracle Container Engine for Kubernetes (OKE)](https://www.oracle.com/cloud-native/container-engine-kubernetes/) with Kubernetes 1.30 or later +* [Redhat Openshift](https://www.redhat.com/en/technologies/cloud-computing/openshift) with version v4.16 or later * [Oracle Linux Cloud Native Environment(OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) 1.9 or later * [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) * [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/) * [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) * [Red Hat OKD](https://www.okd.io/) * [Minikube](https://minikube.sigs.k8s.io/docs/) with version v1.29.0 -* [Redhat Openshift](https://www.redhat.com/en/technologies/cloud-computing/openshift) with version v4.16 or later + ## Prerequisites From 57725292da99663a250fc67823c6dc102c12e718 Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Thu, 11 Sep 2025 15:58:00 +0000 Subject: [PATCH 347/414] Orestart validations --- docs/oraclerestart/README.md | 58 +- ...sm_disk_to_an_existing_restart_database.md | 94 ++- .../asm_addition_autoupdate_false.txt | 424 ++++++++++++ .../asm_addition_autoupdate_true.txt | 423 ++++++++++++ .../provisioning/asm_disk_deletion.txt | 453 +++++++++++++ .../provisioning/asm_disk_deletion1.txt | 619 ++++++++++++++++++ ...change_memory_cpu_for_oracle_restart_db.md | 55 ++ ...e_sw_storage_size_for_oracle_restart_db.md | 54 ++ docs/oraclerestart/provisioning/cleanup.md | 7 +- .../create_kubernetes_secret_for_db_user.md | 4 +- .../provisioning/database_connection.md | 57 +- docs/oraclerestart/provisioning/debugging.md | 2 +- ...disks_from_an_existing_restart_database.md | 57 +- .../provisioning/known_issues.md | 5 + .../provisioning/nfs_pv_stage_vol.yaml | 39 ++ .../provisioning/oraclerestart_prov.yaml | 27 +- .../provisioning/oraclerestart_prov_lb.yaml | 106 --- .../oraclerestart_prov_loadbalancer.yaml | 114 ++++ ...oraclerestart_prov_multiple_diskgroups.txt | 248 +++++++ ...raclerestart_prov_multiple_diskgroups.yaml | 126 ++++ ...ov_multiple_diskgroups_with_redundancy.txt | 272 ++++++++ ...v_multiple_diskgroups_with_redundancy.yaml | 133 ++++ .../oraclerestart_prov_nodeports.yaml | 105 +-- ...aclerestart_prov_nodeports_mcpu_change.txt | 389 +++++++++++ ...clerestart_prov_nodeports_mcpu_change.yaml | 117 ++++ .../oraclerestart_prov_onsport.yaml | 109 --- .../oraclerestart_prov_rupatch.yaml | 108 +-- ...tart_prov_rupatch_oneoff_storageclass.yaml | 260 ++++---- .../oraclerestart_prov_rupatch_pvc.yaml | 208 +++--- .../oraclerestart_prov_storage.yaml | 111 ---- .../oraclerestart_prov_storage_class.yaml | 120 ++++ ...ov_storage_class_after_sw_home_resize.yaml | 123 ++++ ...v_storage_class_before_sw_home_resize.yaml | 123 ++++ ...=> orestart_db_rupatch_oneoffs_object.txt} | 70 +- ...t.txt => orestart_loadbalancer_object.txt} | 93 +-- .../provisioning/orestart_nodeport_object.txt | 39 +- .../provisioning/orestart_object.txt | 274 ++++---- .../orestart_prov_asm_disk_addition.yaml | 180 +++-- ...ov_asm_disk_addition_autoupdate_false.yaml | 120 ++++ .../orestart_prov_asm_disk_deletion.yaml | 172 +++-- .../provisioning/orestart_rupatch_object.txt | 72 +- .../orestart_rupatch_pvc_object.txt | 133 ++-- ....txt => orestart_storage_class_object.txt} | 77 ++- ...rage_class_object_after_sw_home_resize.txt | 193 ++++++ ...age_class_object_before_sw_home_resize.txt | 194 ++++++ .../prerequisites_oracle_restart_db.md | 109 +-- .../provisioning_oracle_restart_db.md | 24 +- .../provisioning_oracle_restart_db_lb.md | 44 -- ...isioning_oracle_restart_db_loadbalancer.md | 47 ++ ...provisioning_oracle_restart_db_nodeport.md | 23 +- .../provisioning_oracle_restart_db_onsport.md | 44 -- .../provisioning_oracle_restart_db_rupatch.md | 47 +- ...oning_oracle_restart_db_rupatch_oneoffs.md | 25 +- ...ning_oracle_restart_multiple_diskgroups.md | 46 ++ ...art_multiple_diskgroups_with_redundancy.md | 49 ++ ...provisioning_oracle_restart_rupatch_pvc.md | 59 +- ...ovisioning_oracle_restart_storage_class.md | 28 +- 57 files changed, 5718 insertions(+), 1594 deletions(-) create mode 100644 docs/oraclerestart/provisioning/asm_addition_autoupdate_false.txt create mode 100644 docs/oraclerestart/provisioning/asm_addition_autoupdate_true.txt create mode 100644 docs/oraclerestart/provisioning/asm_disk_deletion.txt create mode 100644 docs/oraclerestart/provisioning/asm_disk_deletion1.txt create mode 100644 docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md create mode 100644 docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md create mode 100644 docs/oraclerestart/provisioning/nfs_pv_stage_vol.yaml delete mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_lb.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_loadbalancer.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.txt create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.txt create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.txt create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.yaml delete mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml delete mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml rename docs/oraclerestart/provisioning/{orestart_oneoffs_object.txt => orestart_db_rupatch_oneoffs_object.txt} (76%) rename docs/oraclerestart/provisioning/{orestart_ons_object.txt => orestart_loadbalancer_object.txt} (62%) create mode 100644 docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition_autoupdate_false.yaml rename docs/oraclerestart/provisioning/{orestart_storage_object.txt => orestart_storage_class_object.txt} (60%) create mode 100644 docs/oraclerestart/provisioning/orestart_storage_class_object_after_sw_home_resize.txt create mode 100644 docs/oraclerestart/provisioning/orestart_storage_class_object_before_sw_home_resize.txt delete mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_db_lb.md create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md delete mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md create mode 100644 docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md index 13d8b065..20842f16 100644 --- a/docs/oraclerestart/README.md +++ b/docs/oraclerestart/README.md @@ -6,61 +6,71 @@ For more information on Oracle Restart Database 19c refer to the [Oracle Databas Kubernetes provides infrastructure building blocks such as compute, storage and networks. Kubernetes makes the infrastructure available as code. It enables rapid provisioning of multi-node topolgies. Additionally, Kubernetes also provides statefulsets, which are the workload API objects that are used to manage stateful applications like Oracle Restarts, Single Instance Oracle Database, and other Oracle features and configurations. -The Oracle Restart Database Controller in Oracle Database Operator deploys Oracle Restart as a statefulset in the Kubernetes Clusters, using Oracle Restart Slim Image. The Oracle Restart Database Controller manages the typical lifecycle of Oracle Restart Database in a Kubernetes cluster, as shown below: +The Oracle Restart Controller in Oracle Database Operator deploys Oracle Restart Database as a statefulset in the Kubernetes Clusters, using Oracle Restart Slim Image. The Oracle Restart Controller manages the typical lifecycle of Oracle Restart Database in a Kubernetes cluster, as shown below: -* Create Oracle Restart database +* Create Oracle Restart Database * Install and Configure Oracle Grid Infrastructure - * Install and Configure Oracle Restart Database + * Install and Configure Oracle Database * Create Persistent Storage, along with Statefulset * Create Services * Oracle Restart Instances Cleanup -The Oracle Restart Database Controller provides end-to-end automation of Oracle Restart Database Deployment in a Kubernetes Cluster. +The Oracle Restart Controller provides end-to-end automation for the deployment of Oracle Restart Database in a Kubernetes Cluster. -## Using Oracle Restart Database Controller +## Using Oracle Restart Controller -To create a Oracle database, complete the steps in the following sections: +To create an Oracle Database, complete the steps in the following sections: -1. [Prerequisites for running Oracle Restart Database Controller](#prerequisites-for-running-oracle-restart-database-controller) -2. [Provisioning Oracle Restart database in a Oracle Kubernetes Engine Environment](#provisioning-oracle-restart-database-in-a-oracle-kubernetes-engine-environment) +1. [Prerequisites for running Oracle Restart Controller](#prerequisites-for-running-oracle-restart-controller) +2. [Provisioning Oracle Restart database in a Oracle Kubernetes Engine Environment](#provisioning-oracle-restart-database-in-an-oracle-kubernetes-engine-environment) 3. [Connecting to Oracle Restart Database](#connecting-to-oracle-restart-database) 4. [Known Issues](#known-issues) -5. [Debugging and Troubleshooting](#debugging-and-troubleshooting) +5. [Cleanup](#cleanup) +6. [Debugging and Troubleshooting](#debugging-and-troubleshooting) **Note** Before proceeding to the next section, you must complete the instructions given in each section based on your enviornment. -### Prerequisites for running Oracle Restart Database Controller +### Prerequisites for running Oracle Restart Controller **IMPORTANT :** You must make the changes specified in this section before you proceed to the next section. In order to become familiar with Oracle Restart on containers, you can refer [this documentation](https://github.com/oracle/docker-images/blob/main/OracleDatabase/RAC/OracleRealApplicationClusters/docs/orestart/README.md) before proceeding further. -[Pre-requisites for running Oracle Restart Database Controller](./provisioning/prerequisites_oracle_restart_db.md) +[Pre-requisites for running Oracle Restart Controller](./provisioning/prerequisites_oracle_restart_db.md) -## Provisioning Oracle Restart database in a Oracle Kubernetes Engine Environment +## Provisioning Oracle Restart database in an Oracle Kubernetes Engine Environment Deploy Oracle Restart Database YAML files using Kubernetes Cluster on your Oracle Kubernetes Engine Environment (OKE). There are multiple use case possible for deploying the Oracle Restart Database. [1. Provisioning an Oracle Restart Database](./provisioning/provisioning_oracle_restart_db.md) -[2. Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) -[3. Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning/provisioning_oracle_restart_db_rupatch.md) -[4. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) -[5. Provisioning an Oracle Restart Database with Load Balancer Service](./provisioning/provisioning_oracle_restart_db_lb.md) -[6. Provisioning an Oracle Restart Database with OnsPort Service](./provisioning/provisioning_oracle_restart_db_onsport.md) -[7. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) -[8. Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database](./provisioning/add_asm_disk_to_an_existing_restart_database.md) -[9. Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database](./provisioning/delete_asm_disks_from_an_existing_restart_database.md) -[10. Provisioning an Oracle Restart Database with RU Patch and One Offs with Custom Storage Class](./provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md) +[2. Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) +[3. Provisioning an Oracle Restart Database with Load Balancer](./provisioning/provisioning_oracle_restart_db_loadbalancer.md) +[4. Change Memory and CPU allocation for an earlier provisioned Oracle Restart Database](./provisioning/change_memory_cpu_for_oracle_restart_db.md) +[5. Change the size of Software Storage Location for an existing Oracle Restart Database](./provisioning/change_sw_storage_size_for_oracle_restart_db.md) +[6. Provisioning an Oracle Restart Database with Custom Storage Class](./provisioning/provisioning_oracle_restart_storage_class.md) +[7. Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning/provisioning_oracle_restart_db_rupatch.md) +[8. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) +[9. Provisioning an Oracle Restart Database with RU Patch and One Offs with Custom Storage Class](./provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md) +[10. Provisioning an Oracle Restart Database with multiple diskgroups](./provisioning/provisioning_oracle_restart_multiple_diskgroups.md) +[11. Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy](./provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md) +[12. Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database](./provisioning/add_asm_disk_to_an_existing_restart_database.md) +[13. Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database](./provisioning/delete_asm_disks_from_an_existing_restart_database.md) + +**NOTE:** Resizing of the `ASM Disks` is NOT allowed. You can add new ASM Disks to an exising Oracle Restart Database. ## Connecting to Oracle Restart Database -After the Oracle Restart Database has been provisioned using the Oracle Restart Database Controller in Oracle Database Operator, you can follow the steps in this document to connect to the Oracle Restart Database: [Database Connectivity](./provisioning/database_connection.md) +After the Oracle Restart database has been provisioned using the Oracle Restart Controller in Oracle Database Operator, you can follow the steps in this document to connect to the Oracle Restart Database: [Database Connectivity](./provisioning/database_connection.md) + +## Known Issues + +Please refer to this document for any known issues related to deploying Oracle Restart Database using Oracle Restart Controller: [Known Issues](./provisioning/known_issues.md) ## Cleanup -Steps to cleanup Oracle Restart Database Controller deployed using above document in Oracle Database Kubernetes Operator are documented in this page: [Cleanup](./provisioning/cleanup.md) +Steps to cleanup Oracle Restart Database deployed using Oracle Restart Controller in this document in Oracle Database Kubernetes Operator are documented in this page: [Cleanup](./provisioning/cleanup.md) ## Debugging and Troubleshooting -To debug the Oracle Restart database provisioned using the Oracle Restart Database Controller in Oracle Database Kubernetes Operator, follow this document: [Debugging and troubleshooting](./provisioning/debugging.md) \ No newline at end of file +To debug the Oracle Restart Database provisioned using the Oracle Restart Controller in Oracle Database Kubernetes Operator, follow this document: [Debugging and troubleshooting](./provisioning/debugging.md) \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md index 214bfd28..e2d9e315 100644 --- a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md +++ b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md @@ -1,27 +1,31 @@ -# Adding ASM Disk - Add an ASM Disk to an existing Oracle Restart Database +# Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database -This use case demonstrates adding a new ASM Disks to an existing Oracle Restart Database provisioned earlier using Oracle Restart Database Controller. +This use case demonstrates adding a new ASM Disks to an existing Oracle Restart Database provisioned earlier using Oracle Restart Controller. -In this use case, the existing Oracle Restart Database Deployed on a Kubernetes Cluster is having: - -This example uses `oraclerestart_prov.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: +In this use case, the existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: * 1 Node Oracle Restart * Headless services for Oracle Restart - * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes + * Oracle Restart Node hostname +* Node Port 30007 mapped to port 1521 for Database Listener +* Persistent volumes created automatically based on specified disks for Oracle ASM storage +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. * Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. -This use case will be adding a new ASM Disks which will added to the existing Oracle Restart Database. The existing Oracle Restart Database has been deployed using the file [./oraclerestart_prov_rupatch.yaml](././oraclerestart_prov_rupatch.yaml) from Case 4 [Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning_oracle_restart_db_rupatch.md) In this example, - * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch.yaml` file to point to your own container registry base container image. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` , `/dev/oracleoci/oraclevde` and new scaled out disk ``/dev/oracleoci/oraclevdf` + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. - * Similar settings for `hostSwStageLocation` and `hostSwLocation` also apply to the worker node. + * In this example, two new disks will be added to the existing Oracle Restart Database Deployment. For this purpose, the disks on the worker nodes which will be used are `/dev/disk/by-partlabel/asm-disk3` and `/dev/disk/by-partlabel/asm-disk4`. + * Default value in yaml file is `autoUpdate: "true"`, which will delete and recreate the pod with updated ASM disks in the Oracle Restart Deployment. In this case, the new disks will be automatically added to the existing Diskgroup. + * If the value in yaml file is set to `autoUpdate: "false"`, the Oracle Restart Database Pod is recreated, but the additional disks are `NOT` added to the ASM Disk Group automatically. + + +## When autoUpdate is set to true Use the file: [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml) for this use case as below: @@ -29,8 +33,7 @@ Use the file: [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_ad ```sh kubectl apply -f orestart_prov_asm_disk_addition.yaml ``` -Note: - - Default value in yaml file is `autoUpdate: "true"`, which will delete and recreate pods with updated ASM disks in the Oracle Restart Database. In this case, the new disks will be added to the existing Diskgroup in the Oracle Restart Database. +In this case, the new disks will be added to the existing Diskgroup in the Oracle Restart Database. 2. Check the status of the deployment: ```sh @@ -40,46 +43,25 @@ Note: # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" ``` - 3. Samples logs in [logs](./logs/asm_addition_logs.txt) when the [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml) file is applied with option `autoUpdate: true`. - - Describe the Restart Object to see new ASM Disks in Status: - - ```bash - $kubectl get oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart -o json | jq '.status.asmDetails.diskgroup' - [ - { - "disks": [ - "/dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde,/dev/oracleoci/oraclevdy" - ], - "name": "DATA", - "redundancy": "EXTERN" - } - ] - - [grid@dbmc1-0 ~]$ export ORACLE_HOME=/u01/app/19c/grid - [grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM - [grid@dbmc1-0 ~]$ export PATH=$ORACLE_HOME/bin:$PATH - [grid@dbmc1-0 ~]$ - [grid@dbmc1-0 ~]$ /u01/app/19c/grid/bin/asmcmd lsdsk - Path - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde - /dev/oracleoci/oraclevdy - [grid@dbmc1-0 ~]$ + 3. Samples logs in [logs](./logs/asm_addition_autoupdate_true.txt) for disk addition with option `autoUpdate: true`. + + +## When autoUpdate is set to false + +Use the file: [orestart_prov_asm_disk_addition_autoupdate_false.yaml](./orestart_prov_asm_disk_addition_autoupdate_false.yaml) for this use case as below: + +1. Deploy the `orestart_prov_asm_disk_addition_autoupdate_false.yaml` file: + ```sh + kubectl apply -f orestart_prov_asm_disk_addition_autoupdate_false.yaml ``` +In this case, new disks are added to Oracle Restart Database Object Statefulset and Pods are recreated, but this disk is not added to the ASM Disk Group. -4. Sample [logs](./logs/asm_addition_disable_autoupdate_log.txt) when the [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml) file is applied by changing option as `autoUpdate: "false"` from default `autoUpdate: "true"` - - **Note:** New disk `/dev/disk/by-partlabel/ocne_asm_disk_03` is added to Restart Object Statefulset and Pods are recreated, but this disk is not added to the ASM Disk Group. - - ```bash - [grid@dbmc1-0 ~]$ export ORACLE_HOME=/u01/app/19c/grid - [grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM - [grid@dbmc1-0 ~]$ export PATH=$ORACLE_HOME/bin:$PATH - [grid@dbmc1-0 ~]$ - [grid@dbmc1-0 ~]$ /u01/app/19c/grid/bin/asmcmd lsdsk - Path - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde - [grid@dbmc1-0 ~]$ - ``` \ No newline at end of file +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + ``` + 3. Samples logs in [logs](./logs/asm_addition_autoupdate_false.txt) for disk addition with option `autoUpdate: false`. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/asm_addition_autoupdate_false.txt b/docs/oraclerestart/provisioning/asm_addition_autoupdate_false.txt new file mode 100644 index 00000000..6f3301e8 --- /dev/null +++ b/docs/oraclerestart/provisioning/asm_addition_autoupdate_false.txt @@ -0,0 +1,424 @@ +#### Status before the additional ASM Disks additional + +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-21T15:03:39Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 225385451 + UID: 29deacda-d177-48d2-8092-30a5adbfcdf1 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-21T15:14:22Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-21T15:24:02Z + Message: no reconcile errors + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 +/dev/disk/by-partlabel/asm-disk2 +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ ls -lrt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 49 Aug 21 15:24 /dev/disk/by-partlabel/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 33 Aug 21 15:24 /dev/disk/by-partlabel/asm-disk1 + + + +### Apply the file "orestart_prov_asm_disk_addition_autoupdate_false.yaml" to add two new ASM Disks + +$ kubectl logs -f pod/oracle-database-operator-controller-manager-bc8dd8f68-pwfdk -n oracle-database-operator-system +. +. +2025-08-21T15:26:44Z INFO controllers.OracleRestart Detected Addition of ASM Disks: {"addedAsmDisks": ["/dev/disk/by-partlabel/asm-disk3", "/dev/disk/by-partlabel/asm-disk4"]} +2025-08-21T15:26:44Z INFO controllers.OracleRestart Initialized autoUpdate from provided specification {"autoUpdate": false} +2025-08-21T15:26:44Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-21T15:26:44Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-1-oraclerestart-sample"} +2025-08-21T15:26:44Z INFO controllers.OracleRestart Creating a new PV {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-2-oraclerestart-sample"} +2025-08-21T15:26:44Z INFO controllers.OracleRestart Creating a new PV {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-3-oraclerestart-sample"} +2025-08-21T15:26:44Z INFO controllers.OracleRestart Creating a PVC {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample"} +2025-08-21T15:26:44Z INFO controllers.OracleRestart Creating a PVC {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample"} +2025-08-21T15:26:44Z INFO controllers.OracleRestart Validate New ASM Disks +2025-08-21T15:26:44Z INFO controllers.OracleRestart Creating DaemonSet: {"desiredDaemonSet.Name": "disk-check-daemonset"} +2025-08-21T15:26:54Z INFO controllers.OracleRestart Provided ASM Disks are valid, proceeding further +2025-08-21T15:26:54Z INFO controllers.OracleRestart Updating existing configmap +2025-08-21T15:26:54Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-21T15:26:54Z INFO controllers.OracleRestart Change State to UPDATING +2025-08-21T15:26:54Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T15:26:54Z INFO controllers.OracleRestart StatefulSet spec differs for volume devices, updating StatefulSet (pods may be recreated) {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T15:26:54Z INFO controllers.OracleRestart StatefulSet update applied, waiting for pod recreation {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T15:26:54Z INFO controllers.OracleRestart Waiting for StatefulSet update to be applied {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T15:26:59Z INFO controllers.OracleRestart StatefulSet update is applied successfully {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T15:27:59Z INFO controllers.OracleRestart All Pods are running {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T15:28:14Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T15:28:44Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T15:29:14Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T15:29:44Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T15:30:14Z INFO controllers.OracleRestart Pod is ready {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T15:30:19Z INFO controllers.OracleRestart DaemonSet deleted successfully {"DaemonSet.Name": "disk-check-daemonset"} +2025-08-21T15:30:19Z INFO controllers.OracleRestart Oracle Restart Object annotations updated with current spec annotation +2025-08-21T15:30:19Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T15:30:31Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T15:30:31Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-21T15:30:50Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T15:30:50Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T15:30:50Z INFO controllers.OracleRestart Updated Oracle Restart instance status successfully {"Instance": "oraclerestart-sample"} +2025-08-21T15:30:50Z INFO controllers.OracleRestart Returning from updateReconcileStatus +2025-08-21T15:30:50Z INFO controllers.OracleRestart Reconcile requested +2025-08-21T15:30:50Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-21T15:30:50Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-21T15:30:50Z INFO controllers.OracleRestart Initialized autoUpdate from provided specification {"autoUpdate": false} +2025-08-21T15:30:50Z INFO controllers.OracleRestart Oracle Restart Object annotations updated with current spec annotation +2025-08-21T15:30:50Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T15:31:01Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T15:31:01Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-21T15:31:20Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T15:31:20Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T15:31:20Z INFO controllers.OracleRestart Updated Oracle Restart instance status successfully {"Instance": "oraclerestart-sample"} +2025-08-21T15:31:20Z INFO controllers.OracleRestart Returning from updateReconcileStatus +2025-08-21T15:31:20Z INFO controllers.OracleRestart Reconcile requested +2025-08-21T15:31:20Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-21T15:31:20Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-21T15:31:20Z INFO controllers.OracleRestart Initialized autoUpdate from provided specification {"autoUpdate": false} +2025-08-21T15:31:20Z INFO controllers.OracleRestart Annotations are already up to date. Skipping update. +2025-08-21T15:31:20Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T15:31:32Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T15:31:32Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-21T15:31:51Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T15:31:51Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T15:31:51Z INFO controllers.OracleRestart Updated Oracle Restart instance status successfully {"Instance": "oraclerestart-sample"} +2025-08-21T15:31:51Z INFO controllers.OracleRestart Returning from updateReconcileStatus +2025-08-21T15:31:51Z INFO controllers.OracleRestart Reconcile requested +2025-08-21T15:31:51Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-21T15:31:51Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-21T15:31:51Z INFO controllers.OracleRestart Initialized autoUpdate from provided specification {"autoUpdate": false} +2025-08-21T15:31:51Z INFO controllers.OracleRestart Annotations are already up to date. Skipping update. +2025-08-21T15:31:51Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T15:32:02Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T15:32:02Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status + + + +############################################################### +### During this operation, the "pod/dbmc1-0" will be recreated +############################################################### + + + +### Status after the Disks are added with "autoUpdate=true" + +[jpverma@phoenix497729 provisioning]$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-21T15:03:39Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 2 + Resource Version: 225388030 + UID: 29deacda-d177-48d2-8092-30a5adbfcdf1 +Spec: + Asm Storage Details: + Auto Update: false + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + /dev/disk/by-partlabel/asm-disk3 + /dev/disk/by-partlabel/asm-disk4 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + /dev/disk/by-partlabel/asm-disk3 + /dev/disk/by-partlabel/asm-disk4 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-21T15:14:22Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-21T15:32:21Z + Message: no reconcile errors + Observed Generation: 2 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + + +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 +/dev/disk/by-partlabel/asm-disk2 + + +[grid@dbmc1-0 ~]$ ls -lrt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 81 Aug 21 15:27 /dev/disk/by-partlabel/asm-disk4 +brw-rw----. 1 grid asmadmin 8, 65 Aug 21 15:27 /dev/disk/by-partlabel/asm-disk3 +brw-rw----. 1 grid asmadmin 8, 49 Aug 21 15:34 /dev/disk/by-partlabel/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 33 Aug 21 15:34 /dev/disk/by-partlabel/asm-disk1 \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/asm_addition_autoupdate_true.txt b/docs/oraclerestart/provisioning/asm_addition_autoupdate_true.txt new file mode 100644 index 00000000..1f025fc0 --- /dev/null +++ b/docs/oraclerestart/provisioning/asm_addition_autoupdate_true.txt @@ -0,0 +1,423 @@ +#### Status before the additional ASM Disks additional + +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-21T14:24:02Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 225373653 + UID: 9f7225ed-1e97-40f6-bd1d-4efa5acb3007 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-21T14:36:35Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-21T14:45:26Z + Message: no reconcile errors + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 +/dev/disk/by-partlabel/asm-disk2 + +[grid@dbmc1-0 ~]$ ls -lrt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 49 Aug 21 14:46 /dev/disk/by-partlabel/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 33 Aug 21 14:46 /dev/disk/by-partlabel/asm-disk1 + + + +### Apply the file "orestart_prov_asm_disk_addition.yaml" to add two new ASM Disks + +$ kubectl logs -f pod/oracle-database-operator-controller-manager-bc8dd8f68-pwfdk -n oracle-database-operator-system +. +. +2025-08-21T14:49:32Z INFO controllers.OracleRestart Detected Addition of ASM Disks: {"addedAsmDisks": ["/dev/disk/by-partlabel/asm-disk3", "/dev/disk/by-partlabel/asm-disk4"]} +2025-08-21T14:49:32Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-21T14:49:32Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-21T14:49:32Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-1-oraclerestart-sample"} +2025-08-21T14:49:32Z INFO controllers.OracleRestart Creating a new PV {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-2-oraclerestart-sample"} +2025-08-21T14:49:32Z INFO controllers.OracleRestart Creating a new PV {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-3-oraclerestart-sample"} +2025-08-21T14:49:32Z INFO controllers.OracleRestart Creating a PVC {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample"} +2025-08-21T14:49:32Z INFO controllers.OracleRestart Creating a PVC {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample"} +2025-08-21T14:49:32Z INFO controllers.OracleRestart Validate New ASM Disks +2025-08-21T14:49:32Z INFO controllers.OracleRestart Creating DaemonSet: {"desiredDaemonSet.Name": "disk-check-daemonset"} +2025-08-21T14:49:42Z INFO controllers.OracleRestart Provided ASM Disks are valid, proceeding further +2025-08-21T14:49:42Z INFO controllers.OracleRestart Updating existing configmap +2025-08-21T14:49:42Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-21T14:49:42Z INFO controllers.OracleRestart Change State to UPDATING +2025-08-21T14:49:42Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T14:49:42Z INFO controllers.OracleRestart StatefulSet spec differs for volume devices, updating StatefulSet (pods may be recreated) {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T14:49:42Z INFO controllers.OracleRestart StatefulSet update applied, waiting for pod recreation {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T14:49:42Z INFO controllers.OracleRestart Waiting for StatefulSet update to be applied {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T14:49:47Z INFO controllers.OracleRestart StatefulSet update is applied successfully {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T14:50:47Z INFO controllers.OracleRestart All Pods are running {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T14:51:02Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T14:51:32Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T14:52:02Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T14:52:32Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T14:53:02Z INFO controllers.OracleRestart Pod is ready {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T14:53:02Z INFO controllers.OracleRestart New disk to be added to CRS ASM device list {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "disk": "/dev/disk/by-partlabel/asm-disk3"} +2025-08-21T14:53:02Z INFO controllers.OracleRestart New disk to be added to CRS ASM device list {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "disk": "/dev/disk/by-partlabel/asm-disk4"} +2025-08-21T14:53:03Z INFO controllers.OracleRestart Executing command to add disk {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0", "Command": "python3 /opt/scripts/startup/scripts/main.py --updateasmdevices=\"diskname=/dev/disk/by-partlabel/asm-disk3;diskgroup=DATA;processtype=addition\""} +2025-08-21T14:53:06Z INFO controllers.OracleRestart Executing command to add disk {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0", "Command": "python3 /opt/scripts/startup/scripts/main.py --updateasmdevices=\"diskname=/dev/disk/by-partlabel/asm-disk4;diskgroup=DATA;processtype=addition\""} +2025-08-21T14:53:16Z INFO controllers.OracleRestart New Disks added to CRS Disks Group {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample"} +2025-08-21T14:53:16Z INFO controllers.OracleRestart DaemonSet deleted successfully {"DaemonSet.Name": "disk-check-daemonset"} +2025-08-21T14:53:16Z INFO OracleRestart-resource validate update {"name": "oraclerestart-sample"} +2025-08-21T14:53:16Z INFO OracleRestart-resource validate create {"name": "oraclerestart-sample"} +2025-08-21T14:53:16Z INFO controllers.OracleRestart Oracle Restart Object annotations updated with current spec annotation +2025-08-21T14:53:16Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T14:53:28Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T14:53:28Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-21T14:53:48Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T14:53:48Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T14:53:48Z INFO controllers.OracleRestart Updated Oracle Restart instance status successfully {"Instance": "oraclerestart-sample"} +2025-08-21T14:53:48Z INFO controllers.OracleRestart Returning from updateReconcileStatus +2025-08-21T14:53:48Z INFO controllers.OracleRestart Reconcile requested +2025-08-21T14:53:48Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-21T14:53:48Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-21T14:53:48Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-21T14:53:48Z INFO OracleRestart-resource validate update {"name": "oraclerestart-sample"} +2025-08-21T14:53:48Z INFO OracleRestart-resource validate create {"name": "oraclerestart-sample"} +2025-08-21T14:53:48Z INFO controllers.OracleRestart Oracle Restart Object annotations updated with current spec annotation +2025-08-21T14:53:48Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T14:53:59Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T14:53:59Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-21T14:54:19Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T14:54:19Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T14:54:19Z INFO controllers.OracleRestart Updated Oracle Restart instance status successfully {"Instance": "oraclerestart-sample"} +2025-08-21T14:54:19Z INFO controllers.OracleRestart Returning from updateReconcileStatus +2025-08-21T14:54:19Z INFO controllers.OracleRestart Reconcile requested +2025-08-21T14:54:19Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-21T14:54:19Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-21T14:54:19Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-21T14:54:19Z INFO controllers.OracleRestart Annotations are already up to date. Skipping update. +2025-08-21T14:54:19Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T14:54:31Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T14:54:31Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status + + + +############################################################### +### During this operation, the "pod/dbmc1-0" will be recreated +############################################################### + + + +### Status after the Disks are added with "autoUpdate=true" + +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-21T14:24:02Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 2 + Resource Version: 225377019 + UID: 9f7225ed-1e97-40f6-bd1d-4efa5acb3007 +Spec: + Asm Storage Details: + Auto Update: true + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + /dev/disk/by-partlabel/asm-disk3 + /dev/disk/by-partlabel/asm-disk4 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + /dev/disk/by-partlabel/asm-disk3 + /dev/disk/by-partlabel/asm-disk4 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-21T14:36:35Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-21T14:56:23Z + Message: no reconcile errors + Observed Generation: 2 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + + +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 +/dev/disk/by-partlabel/asm-disk2 +/dev/disk/by-partlabel/asm-disk3 +/dev/disk/by-partlabel/asm-disk4 + + +[grid@dbmc1-0 ~]$ ls -lrt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 81 Aug 21 14:57 /dev/disk/by-partlabel/asm-disk4 +brw-rw----. 1 grid asmadmin 8, 65 Aug 21 14:57 /dev/disk/by-partlabel/asm-disk3 +brw-rw----. 1 grid asmadmin 8, 33 Aug 21 14:57 /dev/disk/by-partlabel/asm-disk1 +brw-rw----. 1 grid asmadmin 8, 49 Aug 21 14:57 /dev/disk/by-partlabel/asm-disk2 \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/asm_disk_deletion.txt b/docs/oraclerestart/provisioning/asm_disk_deletion.txt new file mode 100644 index 00000000..7b0929c6 --- /dev/null +++ b/docs/oraclerestart/provisioning/asm_disk_deletion.txt @@ -0,0 +1,453 @@ +#### Status before the ASM Disk deletion + +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-21T15:36:59Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 225402792 + UID: 872a653b-2751-475d-848e-b0d2af5504b6 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-21T15:49:46Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-21T16:21:04Z + Message: no reconcile errors + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 +/dev/disk/by-partlabel/asm-disk2 + +[grid@dbmc1-0 ~]$ ls -lrt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 33 Aug 21 16:21 /dev/disk/by-partlabel/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 49 Aug 21 16:21 /dev/disk/by-partlabel/asm-disk1 + + +### Drop the disk the ASM Level using "ALTER DISKGROUP DROP DISK" command as below: + +[grid@dbmc1-0 ~]$ sqlplus "/ as sysasm" + +SQL*Plus: Release 19.0.0.0.0 - Production on Thu Aug 21 16:23:43 2025 +Version 19.28.0.0.0 + +Copyright (c) 1982, 2025, Oracle. All rights reserved. + + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> set lines 200 +SQL> col path format a50 +SQL> select group_number, disk_number, name, total_mb, free_mb, path from v$asm_disk; + +GROUP_NUMBER DISK_NUMBER NAME TOTAL_MB FREE_MB PATH +------------ ----------- ------------------------------ ---------- ---------- -------------------------------------------------- + 1 1 DATA_0001 51196 46452 /dev/disk/by-partlabel/asm-disk2 + 1 0 DATA_0000 51196 46464 /dev/disk/by-partlabel/asm-disk1 + +SQL> ALTER DISKGROUP DATA DROP DISK 'DATA_0001' REBALANCE POWER 10; + +Diskgroup altered. + +SQL> select EST_MINUTES,EST_WORK,ACTUAL,SOFAR from v$asm_operation; + +EST_MINUTES EST_WORK ACTUAL SOFAR +----------- ---------- ---------- ---------- + 0 0 10 0 + 0 1183 10 122 + 0 0 10 0 + +SQL> select EST_MINUTES,EST_WORK,ACTUAL,SOFAR from v$asm_operation; + +EST_MINUTES EST_WORK ACTUAL SOFAR +----------- ---------- ---------- ---------- + 0 0 10 0 + 0 1183 10 723 + 0 0 10 0 + +SQL> select EST_MINUTES,EST_WORK,ACTUAL,SOFAR from v$asm_operation; + +no rows selected + +SQL> select group_number, disk_number, name, total_mb, free_mb, path from v$asm_disk; + +GROUP_NUMBER DISK_NUMBER NAME TOTAL_MB FREE_MB PATH +------------ ----------- ------------------------------ ---------- ---------- -------------------------------------------------- + 0 0 0 0 /dev/disk/by-partlabel/asm-disk2 + 1 0 DATA_0000 51196 41732 /dev/disk/by-partlabel/asm-disk1 + + +### Apply the file "orestart_prov_asm_disk_deletion.yaml" to delete an exising ASM Disk + +$ kubectl logs -f pod/oracle-database-operator-controller-manager-bc8dd8f68-pwfdk -n oracle-database-operator-system +. +. +2025-08-21T16:31:12Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-21T16:31:12Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-21T16:31:12Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-21T16:31:12Z INFO controllers.OracleRestart Updating existing configmap +2025-08-21T16:31:12Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-21T16:31:15Z INFO controllers.OracleRestart Change State to UPDATING +2025-08-21T16:31:15Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T16:31:15Z INFO controllers.OracleRestart StatefulSet spec differs for volume devices, updating StatefulSet (pods may be recreated) {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T16:31:15Z INFO controllers.OracleRestart StatefulSet update applied, waiting for pod recreation {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T16:31:15Z INFO controllers.OracleRestart StatefulSet update is applied successfully {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} + + +2025-08-21T16:32:15Z INFO controllers.OracleRestart All Pods are running {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-21T16:32:30Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T16:33:00Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T16:33:30Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T16:34:00Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T16:34:30Z INFO controllers.OracleRestart Pod is ready {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-21T16:34:30Z INFO controllers.OracleRestart Successfully deleted PVC {"PVC.Name": "asm-pvc-disk-1-oraclerestart-sample"} +2025-08-21T16:34:30Z INFO controllers.OracleRestart Successfully deleted PV {"PV.Name": "asm-pv-disk-1-oraclerestart-sample"} +2025-08-21T16:34:30Z INFO OracleRestart-resource validate update {"name": "oraclerestart-sample"} +2025-08-21T16:34:30Z INFO OracleRestart-resource validate create {"name": "oraclerestart-sample"} +2025-08-21T16:34:30Z INFO controllers.OracleRestart Oracle Restart Object annotations updated with current spec annotation +2025-08-21T16:34:30Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T16:34:42Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T16:34:42Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-21T16:35:01Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T16:35:01Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T16:35:01Z INFO controllers.OracleRestart Updated Oracle Restart instance status successfully {"Instance": "oraclerestart-sample"} +2025-08-21T16:35:01Z INFO controllers.OracleRestart Returning from updateReconcileStatus +2025-08-21T16:35:01Z INFO controllers.OracleRestart Reconcile requested +2025-08-21T16:35:01Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-21T16:35:01Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-21T16:35:01Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-21T16:35:01Z INFO OracleRestart-resource validate update {"name": "oraclerestart-sample"} +2025-08-21T16:35:01Z INFO OracleRestart-resource validate create {"name": "oraclerestart-sample"} +2025-08-21T16:35:01Z INFO controllers.OracleRestart Oracle Restart Object annotations updated with current spec annotation +2025-08-21T16:35:01Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T16:35:12Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T16:35:12Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-21T16:35:31Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T16:35:31Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-21T16:35:31Z INFO controllers.OracleRestart Updated Oracle Restart instance status successfully {"Instance": "oraclerestart-sample"} +2025-08-21T16:35:31Z INFO controllers.OracleRestart Returning from updateReconcileStatus +2025-08-21T16:35:31Z INFO controllers.OracleRestart Reconcile requested +2025-08-21T16:35:31Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-21T16:35:31Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-21T16:35:31Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-21T16:35:31Z INFO controllers.OracleRestart Annotations are already up to date. Skipping update. +2025-08-21T16:35:31Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-21T16:35:42Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-21T16:35:42Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status + + + +############################################################### +### During this operation, the "pod/dbmc1-0" will be recreated +############################################################### + + + +### Status after the Disk removed: + +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-21T15:36:59Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 2 + Resource Version: 225407022 + UID: 872a653b-2751-475d-848e-b0d2af5504b6 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: PODAVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-21T15:49:46Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-21T16:35:01Z + Message: no reconcile errors + Observed Generation: 2 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + + +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 + + +[grid@dbmc1-0 ~]$ ls -lrt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 49 Aug 21 16:36 /dev/disk/by-partlabel/asm-disk1 \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/asm_disk_deletion1.txt b/docs/oraclerestart/provisioning/asm_disk_deletion1.txt new file mode 100644 index 00000000..aa2c6631 --- /dev/null +++ b/docs/oraclerestart/provisioning/asm_disk_deletion1.txt @@ -0,0 +1,619 @@ +#### Status before the ASM Disk deletion + +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-22T05:09:46Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 226218859 + UID: e3378459-8aa7-4c05-98b2-9c03a44b3a57 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-22T05:22:19Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-23T13:45:49Z + Message: no reconcile errors + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 +/dev/disk/by-partlabel/asm-disk2 + +[grid@dbmc1-0 ~]$ ls -lrt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 33 Aug 22 11:17 /dev/disk/by-partlabel/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 49 Aug 22 11:17 /dev/disk/by-partlabel/asm-disk1 + + +### Apply the file "orestart_prov_asm_disk_deletion.yaml" to delete an exising ASM Disk +### When the above .yaml file is applied, Operator will check if the disk is in use before deletion +### As the disk is not deleted at the ASM level and rebalance has NOT been done, the Operator will be waiting for the disk to be deleted at the ASM Level + + +$ kubectl logs -f pod/oracle-database-operator-controller-manager-bc8dd8f68-pwfdk -n oracle-database-operator-system +. +. +2025-08-23T13:48:51Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:48:51Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:48:51Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:48:51Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:48:51Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:48:54Z INFO controllers.OracleRestart Disk is in use and cannot be removed. Must be manually removed before proceeding {"disk": "/dev/disk/by-partlabel/asm-disk2", "diskgroup": "DATA"} +2025-08-23T13:49:05Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:49:05Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-23T13:49:24Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:49:24Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:49:24Z ERROR Reconciler error {"controller": "oraclerestart", "controllerGroup": "database.oracle.com", "controllerKind": "OracleRestart", "OracleRestart": {"name":"oraclerestart-sample","namespace":"orestart"}, "namespace": "orestart", "name": "oraclerestart-sample", "reconcileID": "ae8e9e7d-ee7c-4cae-b19a-63c57471a0d7", "error": "disk '/dev/disk/by-partlabel/asm-disk2' is part of diskgroup 'DATA' and must be manually removed before proceeding"} +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:353 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:300 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.1 + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:202 +2025-08-23T13:49:24Z INFO controllers.OracleRestart Reconcile requested +2025-08-23T13:49:24Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-23T13:49:24Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-23T13:49:24Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:49:24Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:49:24Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:49:24Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:49:24Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:49:27Z INFO controllers.OracleRestart Disk is in use and cannot be removed. Must be manually removed before proceeding {"disk": "/dev/disk/by-partlabel/asm-disk2", "diskgroup": "DATA"} +2025-08-23T13:49:38Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:49:38Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-23T13:49:57Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:49:57Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:49:57Z ERROR Reconciler error {"controller": "oraclerestart", "controllerGroup": "database.oracle.com", "controllerKind": "OracleRestart", "OracleRestart": {"name":"oraclerestart-sample","namespace":"orestart"}, "namespace": "orestart", "name": "oraclerestart-sample", "reconcileID": "e379037b-4a89-415c-b3f5-aad5e72ff818", "error": "disk '/dev/disk/by-partlabel/asm-disk2' is part of diskgroup 'DATA' and must be manually removed before proceeding"} +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:353 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:300 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.1 + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:202 +2025-08-23T13:49:57Z INFO controllers.OracleRestart Reconcile requested +2025-08-23T13:49:57Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-23T13:49:57Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-23T13:49:57Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:49:57Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:49:57Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:49:57Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:49:57Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:49:59Z INFO controllers.OracleRestart Disk is in use and cannot be removed. Must be manually removed before proceeding {"disk": "/dev/disk/by-partlabel/asm-disk2", "diskgroup": "DATA"} +2025-08-23T13:50:11Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:50:11Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-23T13:50:36Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:50:36Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:50:36Z ERROR Reconciler error {"controller": "oraclerestart", "controllerGroup": "database.oracle.com", "controllerKind": "OracleRestart", "OracleRestart": {"name":"oraclerestart-sample","namespace":"orestart"}, "namespace": "orestart", "name": "oraclerestart-sample", "reconcileID": "e51d673e-6475-445e-bb4f-21e175057290", "error": "disk '/dev/disk/by-partlabel/asm-disk2' is part of diskgroup 'DATA' and must be manually removed before proceeding"} +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:353 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:300 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.1 + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:202 +2025-08-23T13:50:36Z INFO controllers.OracleRestart Reconcile requested +2025-08-23T13:50:36Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-23T13:50:36Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-23T13:50:36Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:50:36Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:50:36Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:50:36Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:50:36Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:50:43Z INFO controllers.OracleRestart Disk is in use and cannot be removed. Must be manually removed before proceeding {"disk": "/dev/disk/by-partlabel/asm-disk2", "diskgroup": "DATA"} +2025-08-23T13:50:54Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:50:54Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-23T13:51:14Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:51:14Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:51:14Z ERROR Reconciler error {"controller": "oraclerestart", "controllerGroup": "database.oracle.com", "controllerKind": "OracleRestart", "OracleRestart": {"name":"oraclerestart-sample","namespace":"orestart"}, "namespace": "orestart", "name": "oraclerestart-sample", "reconcileID": "9fd9fffb-ea21-44a6-9fde-7481106503ca", "error": "disk '/dev/disk/by-partlabel/asm-disk2' is part of diskgroup 'DATA' and must be manually removed before proceeding"} +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:353 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:300 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.1 + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:202 +2025-08-23T13:51:14Z INFO controllers.OracleRestart Reconcile requested +2025-08-23T13:51:14Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-23T13:51:14Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-23T13:51:14Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:51:14Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:51:14Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:51:14Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:51:14Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:51:18Z INFO controllers.OracleRestart Disk is in use and cannot be removed. Must be manually removed before proceeding {"disk": "/dev/disk/by-partlabel/asm-disk2", "diskgroup": "DATA"} +2025-08-23T13:51:30Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:51:30Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-23T13:51:51Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:51:51Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:51:51Z ERROR Reconciler error {"controller": "oraclerestart", "controllerGroup": "database.oracle.com", "controllerKind": "OracleRestart", "OracleRestart": {"name":"oraclerestart-sample","namespace":"orestart"}, "namespace": "orestart", "name": "oraclerestart-sample", "reconcileID": "39f5168e-cf8e-4d0d-990e-e291b024cbc8", "error": "disk '/dev/disk/by-partlabel/asm-disk2' is part of diskgroup 'DATA' and must be manually removed before proceeding"} +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:353 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:300 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.1 + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:202 +2025-08-23T13:51:51Z INFO controllers.OracleRestart Reconcile requested +2025-08-23T13:51:51Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-23T13:51:51Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-23T13:51:51Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:51:51Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:51:51Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:51:51Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:51:51Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:51:55Z INFO controllers.OracleRestart Disk is in use and cannot be removed. Must be manually removed before proceeding {"disk": "/dev/disk/by-partlabel/asm-disk2", "diskgroup": "DATA"} +2025-08-23T13:52:06Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:52:06Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-23T13:52:28Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:52:28Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:52:28Z ERROR Reconciler error {"controller": "oraclerestart", "controllerGroup": "database.oracle.com", "controllerKind": "OracleRestart", "OracleRestart": {"name":"oraclerestart-sample","namespace":"orestart"}, "namespace": "orestart", "name": "oraclerestart-sample", "reconcileID": "6120c33d-b761-4661-b2f9-a45042e50790", "error": "disk '/dev/disk/by-partlabel/asm-disk2' is part of diskgroup 'DATA' and must be manually removed before proceeding"} +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:353 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:300 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.1 + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:202 +2025-08-23T13:52:28Z INFO controllers.OracleRestart Reconcile requested +2025-08-23T13:52:28Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-23T13:52:28Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-23T13:52:28Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:52:28Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:52:28Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:52:28Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:52:28Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:52:32Z INFO controllers.OracleRestart Disk is in use and cannot be removed. Must be manually removed before proceeding {"disk": "/dev/disk/by-partlabel/asm-disk2", "diskgroup": "DATA"} +2025-08-23T13:52:44Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:52:44Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-23T13:53:05Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:53:05Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:53:05Z ERROR Reconciler error {"controller": "oraclerestart", "controllerGroup": "database.oracle.com", "controllerKind": "OracleRestart", "OracleRestart": {"name":"oraclerestart-sample","namespace":"orestart"}, "namespace": "orestart", "name": "oraclerestart-sample", "reconcileID": "8cd9f41c-c062-44ab-8436-781b70e41602", "error": "disk '/dev/disk/by-partlabel/asm-disk2' is part of diskgroup 'DATA' and must be manually removed before proceeding"} +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:353 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:300 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.1 + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:202 +2025-08-23T13:53:05Z INFO controllers.OracleRestart Reconcile requested +2025-08-23T13:53:05Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-23T13:53:05Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-23T13:53:05Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:53:05Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:53:05Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:53:05Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:53:05Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:53:10Z INFO controllers.OracleRestart Disk is in use and cannot be removed. Must be manually removed before proceeding {"disk": "/dev/disk/by-partlabel/asm-disk2", "diskgroup": "DATA"} +2025-08-23T13:53:21Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:53:21Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-23T13:53:43Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:53:43Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:53:43Z ERROR Reconciler error {"controller": "oraclerestart", "controllerGroup": "database.oracle.com", "controllerKind": "OracleRestart", "OracleRestart": {"name":"oraclerestart-sample","namespace":"orestart"}, "namespace": "orestart", "name": "oraclerestart-sample", "reconcileID": "f29aaac7-f09b-4036-9adb-3615fb8357d7", "error": "disk '/dev/disk/by-partlabel/asm-disk2' is part of diskgroup 'DATA' and must be manually removed before proceeding"} +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:353 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:300 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.1 + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:202 +2025-08-23T13:53:43Z INFO controllers.OracleRestart Reconcile requested +2025-08-23T13:53:43Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-23T13:53:43Z INFO controllers.OracleRestart Completed reconcile validation +2025-08-23T13:53:43Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:53:43Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:53:43Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:53:43Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:53:43Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:53:47Z INFO controllers.OracleRestart Disk is in use and cannot be removed. Must be manually removed before proceeding {"disk": "/dev/disk/by-partlabel/asm-disk2", "diskgroup": "DATA"} +2025-08-23T13:53:59Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:53:59Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status +2025-08-23T13:54:18Z INFO controllers.OracleRestart Updating Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:54:18Z INFO controllers.OracleRestart Updated Oracle Restart instance status with validateoraclerestartdb {"Instance": "oraclerestart-sample"} +2025-08-23T13:54:18Z ERROR Reconciler error {"controller": "oraclerestart", "controllerGroup": "database.oracle.com", "controllerKind": "OracleRestart", "OracleRestart": {"name":"oraclerestart-sample","namespace":"orestart"}, "namespace": "orestart", "name": "oraclerestart-sample", "reconcileID": "c656d21f-6cb5-4006-9cfa-9d3800fbdda3", "error": "disk '/dev/disk/by-partlabel/asm-disk2' is part of diskgroup 'DATA' and must be manually removed before proceeding"} +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:353 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:300 +sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.1 + /gomod-cache/sigs.k8s.io/controller-runtime@v0.21.0/pkg/internal/controller/controller.go:202 +2025-08-23T13:54:18Z INFO controllers.OracleRestart Reconcile requested +2025-08-23T13:54:18Z INFO controllers.OracleRestart Entering reconcile validation +2025-08-23T13:54:18Z INFO controllers.OracleRestart Completed reconcile validation + + + +### Now, in the meanwhile, when the Operator is waiting, if the disk is dropped at the ASM Level using "ALTER DISKGROUP DROP DISK" command as below, it will be detected by the Operator: + +[grid@dbmc1-0 ~]$ sqlplus "/ as sysasm" + +SQL*Plus: Release 19.0.0.0.0 - Production on Sat Aug 23 13:49:55 2025 +Version 19.28.0.0.0 + +Copyright (c) 1982, 2025, Oracle. All rights reserved. + + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> set lines 200 +SQL> col path format a50 +SQL> select group_number, disk_number, name, total_mb, free_mb, path from v$asm_disk; + +GROUP_NUMBER DISK_NUMBER NAME TOTAL_MB FREE_MB PATH +------------ ----------- ------------------------------ ---------- ---------- -------------------------------------------------- + 1 1 DATA_0001 51196 46452 /dev/disk/by-partlabel/asm-disk2 + 1 0 DATA_0000 51196 46464 /dev/disk/by-partlabel/asm-disk1 + +SQL> ALTER DISKGROUP DATA DROP DISK 'DATA_0001' REBALANCE POWER 10; + +Diskgroup altered. + +SQL> select EST_MINUTES,EST_WORK,ACTUAL,SOFAR from v$asm_operation; + +EST_MINUTES EST_WORK ACTUAL SOFAR +----------- ---------- ---------- ---------- + 0 0 10 0 + 0 1183 10 122 + 0 0 10 0 + +SQL> select EST_MINUTES,EST_WORK,ACTUAL,SOFAR from v$asm_operation; + +EST_MINUTES EST_WORK ACTUAL SOFAR +----------- ---------- ---------- ---------- + 0 0 10 0 + 0 1183 10 723 + 0 0 10 0 + +SQL> select EST_MINUTES,EST_WORK,ACTUAL,SOFAR from v$asm_operation; + +no rows selected + +SQL> select group_number, disk_number, name, total_mb, free_mb, path from v$asm_disk; + +GROUP_NUMBER DISK_NUMBER NAME TOTAL_MB FREE_MB PATH +------------ ----------- ------------------------------ ---------- ---------- -------------------------------------------------- + 0 0 0 0 /dev/disk/by-partlabel/asm-disk2 + 1 0 DATA_0000 51196 41732 /dev/disk/by-partlabel/asm-disk1 + + + + +### When the Operator detectes the Disk is deleted, it will update the Statefulset as well: + +$ kubectl logs -f pod/oracle-database-operator-controller-manager-bc8dd8f68-pwfdk -n oracle-database-operator-system +. +. +2025-08-23T13:54:18Z INFO controllers.OracleRestart Detected Removal of ASM Disks: {"removedAsmDisks": ["/dev/disk/by-partlabel/asm-disk2"]} +2025-08-23T13:54:18Z INFO controllers.OracleRestart Initialized autoUpdate as true (default) +2025-08-23T13:54:18Z INFO controllers.OracleRestart PV Found {"Instance.Namespace": "orestart", "Instance.Name": "oraclerestart-sample", "dep.Name": "asm-pv-disk-0-oraclerestart-sample"} +2025-08-23T13:54:18Z INFO controllers.OracleRestart Updating existing configmap +2025-08-23T13:54:18Z INFO controllers.OracleRestart Config Map updated successfully with new asm details +2025-08-23T13:54:20Z INFO controllers.OracleRestart Change State to UPDATING +2025-08-23T13:54:20Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:54:20Z INFO controllers.OracleRestart StatefulSet spec differs for volume devices, updating StatefulSet (pods may be recreated) {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-23T13:54:20Z INFO controllers.OracleRestart StatefulSet update applied, waiting for pod recreation {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-23T13:54:20Z INFO controllers.OracleRestart Waiting for StatefulSet update to be applied {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-23T13:54:25Z INFO controllers.OracleRestart StatefulSet update is applied successfully {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-23T13:55:25Z INFO controllers.OracleRestart All Pods are running {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "StatefulSet.Namespace": "orestart", "StatefulSet.Name": "dbmc1"} +2025-08-23T13:55:40Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-23T13:56:10Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-23T13:56:40Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-23T13:57:10Z INFO controllers.OracleRestart Pod is not ready yet {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-23T13:57:40Z INFO controllers.OracleRestart Pod is ready {"oracleRestart.Namespace": "orestart", "oracleRestart.Name": "oraclerestart-sample", "Pod.Name": "dbmc1-0"} +2025-08-23T13:57:40Z INFO controllers.OracleRestart Successfully deleted PVC {"PVC.Name": "asm-pvc-disk-1-oraclerestart-sample"} +2025-08-23T13:57:40Z INFO controllers.OracleRestart Successfully deleted PV {"PV.Name": "asm-pv-disk-1-oraclerestart-sample"} +2025-08-23T13:57:40Z INFO controllers.OracleRestart Oracle Restart Object annotations updated with current spec annotation +2025-08-23T13:57:40Z INFO controllers.OracleRestart Reconcile completed. Requeuing.... +2025-08-23T13:57:52Z INFO controllers.OracleRestart Oracle Restart Object updated with updateOracleRestartInstStatus +2025-08-23T13:57:52Z INFO controllers.OracleRestart Completed Update of Oracle Restart instance status + + + + +############################################################### +### During this operation, the "pod/dbmc1-0" will be recreated +############################################################### + + + +### Status after the Disk removed: + +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-22T05:09:46Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 2 + Resource Version: 226226031 + UID: e3378459-8aa7-4c05-98b2-9c03a44b3a57 +Spec: + Asm Storage Details: + Auto Update: true + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: Always + Inst Details: + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-22T05:22:19Z + Message: oracle restart database is in a restricted state: PROVISIONING + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-23T14:06:52Z + Message: no reconcile errors + Observed Generation: 2 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Inst Details: + Name: + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + + +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 + + +[grid@dbmc1-0 ~]$ ls -lrt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 49 Aug 23 14:10 /dev/disk/by-partlabel/asm-disk1 \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md b/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md new file mode 100644 index 00000000..00277350 --- /dev/null +++ b/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md @@ -0,0 +1,55 @@ +# Change Memory and CPU allocation for an earlier provisioned Oracle Restart Database + +In this use case, the Oracle Grid Infrastructure and Oracle Database are initially deployed automatically using Oracle Restart Controller. The responsefile is generated by the controller based on input parameters specified in the .yaml file. + +In this use case, the memory and cpu allocation for that existing Oracle Restart Database Pod is changed. + +This example uses `oraclerestart_prov_nodeports.yaml` to provision the initial Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart Node hostname +* Node Port 30007 mapped to port 1521 for Database Listener +* Persistent volumes created automatically based on specified disks for Oracle ASM storage +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +In this example, + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. + +Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_nodeports.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_nodeports.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +4. Use the file: [oraclerestart_prov_nodeports_mcpu_change.yaml](./oraclerestart_prov_nodeports_mcpu_change.yaml) to change the Memory and CPU allocation for the existing Oracle Restart Database Pod: + ```sh + kubectl apply -f oraclerestart_prov_nodeports_mcpu_change.yaml + oraclerestart.database.oracle.com/oraclerestart-sample configured + ``` + + You will notice that the exiting Oracle Restart Database Pod will be recreated with updated Memory and CPU allocation. + +5. Check Details of Kubernetes CRD Object before and after the change as in this [example](./oraclerestart_prov_nodeports_mcpu_change.txt). It also has the details of the memory and cpu inside the pod before and after the change. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md b/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md new file mode 100644 index 00000000..8da37cf5 --- /dev/null +++ b/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md @@ -0,0 +1,54 @@ +# Change the size of Software Storage Location for an existing Oracle Restart Database + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are initially deployed automatically using Oracle Restart Controller with Custom Storage Class. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. + +In this use case, the Software Home Location for Grid Infrastructure and Database, the ASM Disks are provisioned as Persistent Volumes using custom storage class during the initial deployment. An updated .yaml file is applied to `increase` the size of the Software Home Location. + +**NOTE:** The `decrease` in the size of Software Home Location for an existing Oracle Restart Database is `not allowed`. + +This example uses `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` to initially provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart node hostname +* Node Port 30007 mapped to port 1521 for Database Listener +* Persistent volumes for ASM Disks created automatically using the Storage Class for Oracle ASM storage +* Persistent volume for Software location is created automatically using the Storage Class. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using the corresponding Persistent Volume Claim. Its size is specified by `swLocStorageSizeInGb`. +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. +* Name of Custom Storage Class is specified by `storageClass`. + +In this example, + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` file to point to the container image you have built. + * The disks provisioned using custom storage class are mounted inside the Oracle Restart Pod as `/dev/asm-disk1` and `/dev/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. + +Use the file: [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_storage_class_before_sw_home_resize.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_storage_class_object_before_sw_home_resize.txt) +4. In order to `increase` the size of Software Home Location, you can use the updated file [oraclerestart_prov_storage_class_after_sw_home_resize.yaml](./oraclerestart_prov_storage_class_after_sw_home_resize.yaml). +5. Deploy the `oraclerestart_prov_storage_class_after_sw_home_resize.yaml` file: + ```sh + $ kubectl apply -f oraclerestart_prov_storage_class_after_sw_home_resize.yaml + oraclerestart.database.oracle.com/oraclerestart-sample configured + ``` + You will notice Persistent Volume for the Software Location has been resized. You can check Details of updated Kubernetes CRD Object as in this [example](./orestart_storage_class_object_after_sw_home_resize.txt) diff --git a/docs/oraclerestart/provisioning/cleanup.md b/docs/oraclerestart/provisioning/cleanup.md index a438e37e..34a8593f 100644 --- a/docs/oraclerestart/provisioning/cleanup.md +++ b/docs/oraclerestart/provisioning/cleanup.md @@ -5,7 +5,7 @@ In order to delete and cleanup the Oracle Restart Database deployed using Oracle This example uses `oraclerestart_prov.yaml` to cleanup an Oracle Restart Database which was initially used for deployment: -1. Delete the `oraclerestart_prov.yaml` file: +1. Use the `oraclerestart_prov.yaml` file to delete an existing deployment: ```sh kubectl delete -f oraclerestart_prov.yaml oraclerestart.database.oracle.com/oraclerestart-sample deleted @@ -14,4 +14,7 @@ This example uses `oraclerestart_prov.yaml` to cleanup an Oracle Restart Databas ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart - ``` \ No newline at end of file + ``` +3. If your deleted deployment used software location specified by `hostSwLocation` from worker node, then in order to reuse this location in next deployment, you will need to clear it at the worker node level. + +4. If your deleted deployment used ASM Disks from the worker node, then in order to reuse the same disks for the next deployment, you will need to clear the disks at the worker node level using `dd` command. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md index 5ce19953..3f0d2d05 100644 --- a/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md +++ b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md @@ -1,6 +1,8 @@ # Create Kubernetes secret for db user -Create a Kubernetes secret named `db-user-pass` using a password in a text file and then encrypt it using an `openssl` key. The text file will be removed after secret is created. You need to make sure openssl must be installed on worker nodes. +Create a Kubernetes secret named `db-user-pass-pkutl` using a password in a text file and then encrypt it using an `openssl` key. The text file will be removed after secret is created. You need to make sure openssl must be installed on worker nodes. + +**NOTE:** The openssl version on the system where you run the below commands to generate the secret and the openssl version of the Oracle Restart Slim Image must be compatible with each other. ```sh mkdir /tmp/.secrets/ diff --git a/docs/oraclerestart/provisioning/database_connection.md b/docs/oraclerestart/provisioning/database_connection.md index c48d8e18..2cef9640 100644 --- a/docs/oraclerestart/provisioning/database_connection.md +++ b/docs/oraclerestart/provisioning/database_connection.md @@ -1,6 +1,7 @@ # Database Connectivity -## Database Connection to Oracle Restart Database +Depending on whether the Oracle Restart Database has been deployed using Oracle Restart Controller with a NodePort Service or a Load Balancer Service etc, you can refer to the below examples to connect to the Oracle Restart Database: + ## Database Connection to Oracle Restart Database with NodePort Service The Oracle Database with NodePort service deployed by Oracle Restart Controller can be reached using the Worker Node IP and the Port of the Node Port service. Use the below steps: @@ -15,7 +16,7 @@ service/dbmc1 NodePort 10.96.53.210 1521:30007/TCP 5h46 service/dbmc1-0 ClusterIP None 171m statefulset.kubernetes.io/pod-name=dbmc1-0 NAME READY AGE CONTAINERS IMAGES -statefulset.apps/dbmc1 1/1 5h46m dbmc1 localhost/oracle/database-rac:19.3.0-slim +statefulset.apps/dbmc1 1/1 5h46m dbmc1 localhost/oracle/database-orestart:19.3.0-slim ``` In this case, the port 1521 from the pod is mapped to port 30007 on the worker node. To make the connection from outside, you will need to open the port 30007 on the worker node for INGRESS. @@ -45,3 +46,55 @@ INSTANCE_NAME HOST_NAME DATABASE_TYPE PORCLCDB dbmc1-0 SINGLE ``` +## Database Connection to Oracle Restart Database with Load Balancer + +In this case, the Oracle Restart Database is deployed with an External Load Balancer and the deployment has a Public IP Assigned from the External Load Balancer Service. + +Once the deployment is completed, you will be able to make a database connection as below: + +1. Get the Details of the deployment: +```sh +$ kubectl get all -n orestart -o wide +NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES +pod/dbmc1-0 1/1 Running 0 14m 10.244.0.41 10.0.10.58 + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR +service/dbmc1 LoadBalancer 10.96.34.208 XXX.XX.XX.XX 1521:30433/TCP,6200:30656/TCP 14m statefulset.kubernetes.io/pod-name=dbmc1-0 +service/dbmc1-0 ClusterIP None 14m statefulset.kubernetes.io/pod-name=dbmc1-0 + +NAME READY AGE CONTAINERS IMAGES +statefulset.apps/dbmc1 1/1 14m dbmc1 localhost/oracle/database-orestart:19.3.0-slim +``` +In this case, you will be able to make a remote database connection using the Load Balancer target port 1521. + +2. For the above deployment, you will be able to make an SQLPLUS database connection to this Oracle Restart Database from a remote client as below: + +```sh +bash-4.4$ sqlplus system/@//:1521/PORCLCDB + +SQL*Plus: Release 21.0.0.0.0 - Production on Tue Sep 2 04:57:56 2025 +Version 21.19.0.0.0 + +Copyright (c) 1982, 2022, Oracle. All rights reserved. + +Last Successful login time: Tue Sep 02 2025 04:53:52 +00:00 + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> +SQL> set lines 200 +SQL> col HOST_NAME format a40 +SQL> select INSTANCE_NAME,HOST_NAME, DATABASE_TYPE from v$instance; + +INSTANCE_NAME HOST_NAME DATABASE_TYPE +---------------- ---------------------------------------- --------------- +PORCLCDB dbmc1-0 SINGLE +``` + +## Database Connection to Oracle Restart Database without NodePort Service + +In this case, the Oracle Restart Database will NOT be reachable using the Public IP of the worker node and thus, will not be reachable from outside the Kuberenetes Cluster. + +In this case, an application deployed with in the Kubernetes Cluster will be able to reach the Oracle Restart Database on Port 1521. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/debugging.md b/docs/oraclerestart/provisioning/debugging.md index 0e7a00de..57084823 100644 --- a/docs/oraclerestart/provisioning/debugging.md +++ b/docs/oraclerestart/provisioning/debugging.md @@ -37,5 +37,5 @@ kubectl logs -f pod/dbmc1-0 -n orestart To check the details of the CRS logs or the RDBMS instance logs at the host level, switch to the corresponding Kubernetes container using the command like below: ```sh -kubectl exec -it dbmc1-0 -n orestart -- tail -f /tmp/orod/oracle_rac_setup.log +kubectl exec -it dbmc1-0 -n orestart -- tail -f /tmp/orod/oracle_db_setup.log ``` \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md b/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md index 94d4d719..bed4bacb 100644 --- a/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md +++ b/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md @@ -1,27 +1,30 @@ -# Delete ASM Disk - Delete an ASM Disk to an existing Oracle Restart Database +# Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database -This use case demonstrates deleting a ASM Disk from an existing Oracle Restart Database provisioned earlier using Oracle Restart Database Controller. +This use case demonstrates deleting a ASM Disk from an existing Oracle Restart Database provisioned earlier using Oracle Restart Controller. In this use case, the existing Oracle Restart Database Deployed on a Kubernetes Cluster is having: -This example uses `oraclerestart_prov.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: +In this use case, the existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: * 1 Node Oracle Restart * Headless services for Oracle Restart - * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes + * Oracle Restart Node hostname +* Node Port 30007 mapped to port 1521 for Database Listener +* Persistent volumes created automatically based on specified disks for Oracle ASM storage +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. * Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. -This use case will be deleting a ASM Disk which will deleted to the existing Oracle Restart Database. The existing Oracle Restart Database has been deployed using the file [./oraclerestart_prov_rupatch.yaml](././oraclerestart_prov_rupatch.yaml) from Case 4 [Provisioning an Oracle Restart Database with RU Patch on FileSystem](./provisioning_oracle_restart_db_rupatch.md) In this example, - * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch.yaml` file to point to your own container registry base container image. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd`. Disk `/dev/oracleoci/oraclevde` is removed manually and not in use. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. - * Similar settings for `hostSwStageLocation` and `hostSwLocation` also apply to the worker node. + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + * Before deleting the disk, you will need to remove the disk at the ASM Level using "ALTER DISKGROUP DROP DISK" command. + * IF the disk is first NOT removed at the ASM Level and the .yaml file to delete the disk is applied, to avoid any data loss, the operator will NOT delete the disk from the Stateful Set and the Oracle Restart Database Pod will not be recreated. Operator will be in wait state until the disk is deleted at the ASM Level. + * In this example, out of the two disks mentioned above, the disk `/dev/disk/by-partlabel/asm-disk2` will be deleted from the existing Oracle Restart Database Deployment. Use the file: [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) for this use case as below: @@ -29,8 +32,8 @@ Use the file: [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_de ```sh kubectl apply -f orestart_prov_asm_disk_deletion.yaml ``` -Note: - - Default value in yaml file is `autoUpdate: "true"`, which will delete and recreate pods with updated ASM disks in the Oracle Restart Database. In this case, the new disks will be added to the existing Diskgroup in the Oracle Restart Database. + +In this case, the disk will be deleted and Oracle Restart Database Pod will be recreated. 2. Check the status of the deployment: ```sh @@ -40,28 +43,8 @@ Note: # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" ``` - 3. Samples logs in [logs](./logs/asm_addition_logs.txt) when the [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) file is applied where `/dev/oracleoci/oraclevde` is removed and not in use. If its in use, operator will show error in logs and it wont be able to remove it. - - Describe the Restart Object to see new ASM Disks in Status: +3. Samples logs in [logs](./asm_disk_deletion.txt) when the disk `/dev/disk/by-partlabel/asm-disk2` is removed at the ASM Level first and then[orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) file is applied to remove it from the Stateful set. - ```bash - $kubectl get oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart -o json | jq '.status.asmDetails.diskgroup' - [ - { - "disks": [ - "/dev/oracleoci/oraclevdd" - ], - "name": "DATA", - "redundancy": "EXTERN" - } - ] +4. Samples logs in [logs](./asm_disk_deletion1.txt) when the disk `/dev/disk/by-partlabel/asm-disk2` is tried to be removed from Stateful Set using the file [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) but this disk is NOT yet removed at the ASM Level. - [grid@dbmc1-0 ~]$ export ORACLE_HOME=/u01/app/19c/grid - [grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM - [grid@dbmc1-0 ~]$ export PATH=$ORACLE_HOME/bin:$PATH - [grid@dbmc1-0 ~]$ - [grid@dbmc1-0 ~]$ /u01/app/19c/grid/bin/asmcmd lsdsk - Path - /dev/oracleoci/oraclevdd - [grid@dbmc1-0 ~]$ - ``` +In this case, the Operator will be waiting for the disk to be deleted at the ASM Level. Once it detects the Disk has been deleted at the ASM Level and it is safe to proceed, it will remove the disk from the Stateful Set. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/known_issues.md b/docs/oraclerestart/provisioning/known_issues.md index e69de29b..a2a7d966 100644 --- a/docs/oraclerestart/provisioning/known_issues.md +++ b/docs/oraclerestart/provisioning/known_issues.md @@ -0,0 +1,5 @@ +# Known Issues + +This document lists any known issues when the Oracle Restart Database is provisioned using the Oracle Restart controller. + +- If you describe the resource `oraclerestarts.database.oracle.com/oraclerestart-sample` in the namespace used for Oracle Restart Database provisioning, sometimes it may report the `State` under `Service Details` as `FAILED` while the Service is up and running. Similarly, the `Instance State` may report as `NOTAVAILABLE` while the Oracle Restart Database Instance is up and running fine. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/nfs_pv_stage_vol.yaml b/docs/oraclerestart/provisioning/nfs_pv_stage_vol.yaml new file mode 100644 index 00000000..5525f3e5 --- /dev/null +++ b/docs/oraclerestart/provisioning/nfs_pv_stage_vol.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: pv-stage-vol1 + namespace: orestart +spec: + storageClassName: fss-dyn-storage + capacity: + storage: 500Gi + accessModes: + - ReadOnlyMany + nfs: + path: /stage + server: 10.0.10.212 + persistentVolumeReclaimPolicy: Retain + mountOptions: + - rw + - nointr + - hard + - bg + - tcp + - actimeo=0 + - vers=3 + - rsize=1048576 + - wsize=1048576 + - timeo=600 +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: pv-stage-vol-claim + namespace: orestart +spec: + storageClassName: fss-dyn-storage + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 500Gi diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov.yaml index f80b3e58..c798e8e2 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov.yaml @@ -17,20 +17,22 @@ spec: # Worker nodes where the instance should be scheduled workerNode: - - 10.0.10.108 # IP address of the target node for deployment + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install envVars: - - name: IGNORE_CRS_PREREQS + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. value: "true" - - name: IGNORE_DB_PREREQS + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. value: "true" # ASM (Automatic Storage Management) storage configuration asmStorageDetails: disksBySize: - - storageSizeInGb: 50 # Logical group size (in GB) for ASM disk group + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage diskNames: - - /dev/oracleoci/oraclevdd # ASM disk device path 1 - - /dev/oracleoci/oraclevde # ASM disk device path 2 + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 + - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 # SSH key secret used for remote access and automation sshKeySecret: @@ -45,11 +47,12 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-rac:19.3.0-slim - # image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim # <-- Replace with your image registry + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry - # Policy to always pull image (useful during testing or if image might change) - imagePullPolicy: Always + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent # Optional service account for security context permissions # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes @@ -85,7 +88,7 @@ spec: gridBase: "/u01/app/grid" # Grid base directory dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path dbBase: "/u01/app/oracle" # Oracle base directory - crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # Comma-separated list of ASM device files + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM device files inventory: "/u01/app/oraInventory" # Inventory location gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP dbSwZipFile: "db_home.zip" # Database software ZIP @@ -94,4 +97,4 @@ spec: processes: 2000 # Oracle process limit cpuCount: 4 # Number of CPUs to allocate dbName: "PORCLCDB" # Oracle database name - hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged \ No newline at end of file + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_lb.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_lb.yaml deleted file mode 100644 index b0d68399..00000000 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_lb.yaml +++ /dev/null @@ -1,106 +0,0 @@ -# API version for the custom resource definition -apiVersion: database.oracle.com/v4 - -# Kind of resource being defined -kind: OracleRestart - -# Metadata section includes the name and namespace of the resource -metadata: - # Name of this OracleRestart resource instance - name: oraclerestart-sample - # Kubernetes namespace where this resource resides - namespace: orestart - -# Specification for the OracleRestart resource -spec: - # Details about the Oracle instance - instDetails: - # Name of the Oracle instance - name: dbmc1 - # Path where Oracle software will be installed on the host - hostSwLocation: /scratch/orestart/ - # List of worker node IPs where instance will be deployed - workerNode: - - 10.0.10.108 - envVars: - - name: IGNORE_CRS_PREREQS - value: "true" - - name: IGNORE_DB_PREREQS - value: "true" - # ASM (Automatic Storage Management) disk configuration - asmStorageDetails: - disksBySize: - - storageSizeInGb: 50 # Total size of disk group in GB - diskNames: # List of physical disk paths to be used - - /dev/oracleoci/oraclevdd - - /dev/oracleoci/oraclevde - - # SSH key secret references used for host access - sshKeySecret: - name: ssh-key-secret # Name of the Kubernetes secret - privKeySecretName: ssh-privkey # Private SSH key secret - pubKeySecretName: ssh-pubkey # Public SSH key secret - - # Database user credentials secret - dbSecret: - name: db-user-pass-pkutl # Name of secret containing DB credentials - keyFileName: key.pem # Key file name inside secret - pwdFileName: pwdfile.enc # Password file name inside secret - - # Container image for Oracle Restart - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0729 - - # Policy to always pull the image when starting - imagePullPolicy: Always - - # Details of the database service - serviceDetails: - name: soepdb # Name of the database service - - # Kubernetes resource requests and limits - resources: - requests: - memory: "16Gi" # Memory requested by container - cpu: "2" # CPUs requested by container - limits: - memory: "16Gi" # Maximum memory allowed - cpu: "2" # Maximum CPUs allowed - - # OS-level kernel parameters to be set in the container - securityContext: - sysctls: - - name: kernel.shmall - value: "2097152" # Shared memory pages - - name: kernel.sem - value: "250 32000 100 128" # Semaphore settings - - name: kernel.shmmax - value: "8589934592" # Max shared memory segment (8GB) - - name: kernel.shmmni - value: "4096" # Number of shared memory segments - lbService: - name: orestartsvc - svcType: lbservice - portMappings: - - port: 1521 - targetPort: 1521 - protocol: TCP - - port: 6200 - targetPort: 6200 - protocol: TCP - # Configuration parameters for Oracle software - configParams: - gridHome: "/u01/app/19c/grid" # Path to Grid Infrastructure home - gridBase: "/u01/app/grid" # Base directory for Grid Infrastructure - dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle Database home - dbBase: "/u01/app/oracle" # Oracle Database base directory - crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # ASM device list - inventory: "/u01/app/oraInventory" # Oracle inventory location - gridSwZipFile: "grid_home.zip" # Grid software zip filename - dbSwZipFile: "db_home.zip" # DB software zip filename - sgaSize: "3G" # Size of SGA (System Global Area) - pgaSize: "1G" # Size of PGA (Program Global Area) - processes: 2000 # Maximum number of DB processes - cpuCount: 4 # Number of CPUs to allocate - dbName: "PORCLCDB" # Name of the Oracle database - hostSwStageLocation: "/scratch/software/19c/19.3.0/" # Location to stage installation software - ruPatchLocation: /scratch/software/19c/19.28/ # Location for Release Update patch \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_loadbalancer.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_loadbalancer.yaml new file mode 100644 index 00000000..2dc17fe8 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_loadbalancer.yaml @@ -0,0 +1,114 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # Load Balancer service configuration + lbService: + svcType: lbservice # Service type set to Load Balancer + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + - port: 6200 # Port inside container (Oracle ONS port) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 + - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.txt b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.txt new file mode 100644 index 00000000..6df1d826 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.txt @@ -0,0 +1,248 @@ +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-26T18:55:04Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 227602633 + UID: b6e11aa4-c2d0-4bc2-9832-f3ffe6e9730c +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + /dev/disk/by-partlabel/asm-disk3 + /dev/disk/by-partlabel/asm-disk4 + /dev/disk/by-partlabel/asm-disk5 + /dev/disk/by-partlabel/asm-disk6 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +CRSDATA + Crs Asm Disk Dg Redundancy: external + Db Asm Device List: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DBDATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +RECO + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Reco Asm Device List: /dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent + Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + /dev/disk/by-partlabel/asm-disk3 + /dev/disk/by-partlabel/asm-disk4 + /dev/disk/by-partlabel/asm-disk5 + /dev/disk/by-partlabel/asm-disk6 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + Name: CRSDATA + Redundancy: EXTERN + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + Name: RECO + Redundancy: EXTERN + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + Name: DBDATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-26T19:06:09Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-26T19:14:51Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +CRSDATA + Crs Asm Disk Dg Redundancy: external + Db Asm Device List: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DBDATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +RECO + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Reco Asm Device List: /dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + + + +[grid@dbmc1-0 ~]$ ls -rlt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 33 Aug 26 19:02 /dev/disk/by-partlabel/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 49 Aug 26 19:15 /dev/disk/by-partlabel/asm-disk4 +brw-rw----. 1 grid asmadmin 8, 65 Aug 26 19:16 /dev/disk/by-partlabel/asm-disk6 +brw-rw----. 1 grid asmadmin 8, 113 Aug 26 19:16 /dev/disk/by-partlabel/asm-disk5 +brw-rw----. 1 grid asmadmin 8, 81 Aug 26 19:16 /dev/disk/by-partlabel/asm-disk1 +brw-rw----. 1 grid asmadmin 8, 97 Aug 26 19:16 /dev/disk/by-partlabel/asm-disk3 +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdg +State Type Rebal Sector Logical_Sector Block AU Total_MB Free_MB Req_mir_free_MB Usable_file_MB Offline_disks Voting_files Name +MOUNTED EXTERN N 512 512 4096 4194304 102392 102276 0 102276 0 N CRSDATA/ +MOUNTED EXTERN N 512 512 4096 1048576 102396 96243 0 96243 0 N DBDATA/ +MOUNTED EXTERN N 512 512 4096 1048576 102396 99226 0 99226 0 N RECO/ +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 +/dev/disk/by-partlabel/asm-disk2 +/dev/disk/by-partlabel/asm-disk3 +/dev/disk/by-partlabel/asm-disk4 +/dev/disk/by-partlabel/asm-disk5 +/dev/disk/by-partlabel/asm-disk6 +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ sqlplus "/ as sysdba" + +SQL*Plus: Release 19.0.0.0.0 - Production on Tue Aug 26 19:16:34 2025 +Version 19.28.0.0.0 + +Copyright (c) 1982, 2025, Oracle. All rights reserved. + + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> set lines 200 +SQL> col DISKGROUP_NAME format a20 +SQL> col PATH format a50 +SQL> SELECT + dg.NAME AS DISKGROUP_NAME, + d.PATH, + d.MOUNT_STATUS, + d.HEADER_STATUS, + d.STATE, + d.TOTAL_MB, + d.FREE_MB +FROM + V$ASM_DISK d +JOIN + V$ASM_DISKGROUP dg ON d.GROUP_NUMBER = dg.GROUP_NUMBER +ORDER BY + dg.NAME, d.DISK_NUMBER; 2 3 4 5 6 7 8 9 10 11 12 13 14 + +DISKGROUP_NAME PATH MOUNT_S HEADER_STATU STATE TOTAL_MB FREE_MB +-------------------- -------------------------------------------------- ------- ------------ -------- ---------- ---------- +CRSDATA /dev/disk/by-partlabel/asm-disk1 CACHED MEMBER NORMAL 51196 51140 +CRSDATA /dev/disk/by-partlabel/asm-disk2 CACHED MEMBER NORMAL 51196 51136 +DBDATA /dev/disk/by-partlabel/asm-disk3 CACHED MEMBER NORMAL 51198 48123 +DBDATA /dev/disk/by-partlabel/asm-disk4 CACHED MEMBER NORMAL 51198 48120 +RECO /dev/disk/by-partlabel/asm-disk5 CACHED MEMBER NORMAL 51198 49614 +RECO /dev/disk/by-partlabel/asm-disk6 CACHED MEMBER NORMAL 51198 49612 + +6 rows selected. + +SQL> \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml new file mode 100644 index 00000000..c7efada6 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml @@ -0,0 +1,126 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 + - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 + - /dev/disk/by-partlabel/asm-disk3 # ASM disk device path 3 + - /dev/disk/by-partlabel/asm-disk4 # ASM disk device path 4 + - /dev/disk/by-partlabel/asm-disk5 # ASM disk device path 5 + - /dev/disk/by-partlabel/asm-disk6 # ASM disk device path 6 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM devices for CRSDATA Diskgroup + dbAsmDeviceList: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 # Comma-separated list of ASM devices for DATA Diskgroup + recoAsmDeviceList: /dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 # Comma-separated list of ASM devices for RECO Diskgroup + crsAsmDiskDg: "+CRSDATA" # Name of the Diskgroup for Voting Disks + dbRecoveryFileDest: "+RECO" # Name of the Diskgroup for Recovery Files + dbDataFileDestDg: "+DBDATA" # Name of the Diskgroup for Database Files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.txt b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.txt new file mode 100644 index 00000000..8ad007fa --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.txt @@ -0,0 +1,272 @@ +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-09-02T17:05:56Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 230588194 + UID: 63430f74-7889-4e7e-8f23-7860f394ff58 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + /dev/disk/by-partlabel/asm-disk3 + /dev/disk/by-partlabel/asm-disk4 + /dev/disk/by-partlabel/asm-disk5 + /dev/disk/by-partlabel/asm-disk6 + /dev/disk/by-partlabel/asm-disk7 + /dev/disk/by-partlabel/asm-disk8 + /dev/disk/by-partlabel/asm-disk9 + /dev/disk/by-partlabel/asm-disk10 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +CRSDATA + Crs Asm Disk Dg Redundancy: EXTERNAL + Db Asm Device List: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + Db Asm Disk Dg Redundancy: NORMAL + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DBDATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +RECO + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Reco Asm Device List: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9,/dev/disk/by-partlabel/asm-disk10 + Reco Asm Disk Dg Redudancy: NORMAL + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent + Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Node Port Svc: + Name: dbmc1-service-nodeport + Svc Type: nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Worker Node: + 10.0.10.58 + Lb Service: + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: NOTAVAILABLE + Pod State: PODAVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + /dev/disk/by-partlabel/asm-disk3 + /dev/disk/by-partlabel/asm-disk4 + /dev/disk/by-partlabel/asm-disk5 + /dev/disk/by-partlabel/asm-disk6 + /dev/disk/by-partlabel/asm-disk7 + /dev/disk/by-partlabel/asm-disk8 + /dev/disk/by-partlabel/asm-disk9 + /dev/disk/by-partlabel/asm-disk10 + State: FAILED + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk10,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9 + Name: CRSDATA + Redundancy: EXTERN + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk10,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9 + Name: RECO + Redundancy: NORMAL + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk10,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9 + Name: DBDATA + Redundancy: NORMAL + Conditions: + Last Transition Time: 2025-09-02T17:05:56Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Last Transition Time: 2025-09-02T17:32:22Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +CRSDATA + Crs Asm Disk Dg Redundancy: EXTERNAL + Db Asm Device List: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + Db Asm Disk Dg Redundancy: NORMAL + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DBDATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +RECO + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Reco Asm Device List: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9,/dev/disk/by-partlabel/asm-disk10 + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: Pending + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: FAILED +Events: + + + + +[grid@dbmc1-0 ~]$ ls -rlt /dev/disk/by-partlabel/asm-disk* +brw-rw----. 1 grid asmadmin 8, 33 Sep 2 17:13 /dev/disk/by-partlabel/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 97 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk1 +brw-rw----. 1 grid asmadmin 8, 145 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk9 +brw-rw----. 1 grid asmadmin 8, 81 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk4 +brw-rw----. 1 grid asmadmin 8, 65 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk8 +brw-rw----. 1 grid asmadmin 8, 129 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk7 +brw-rw----. 1 grid asmadmin 8, 113 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk6 +brw-rw----. 1 grid asmadmin 8, 49 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk5 +brw-rw----. 1 grid asmadmin 8, 177 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk3 +brw-rw----. 1 grid asmadmin 8, 161 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk10 +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdg +State Type Rebal Sector Logical_Sector Block AU Total_MB Free_MB Req_mir_free_MB Usable_file_MB Offline_disks Voting_files Name +MOUNTED EXTERN N 512 512 4096 4194304 102392 102276 0 102276 0 N CRSDATA/ +MOUNTED NORMAL N 512 512 4096 1048576 204792 192279 51198 70540 0 N DBDATA/ +MOUNTED NORMAL N 512 512 4096 1048576 204792 198267 51198 73534 0 N RECO/ +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/disk/by-partlabel/asm-disk1 +/dev/disk/by-partlabel/asm-disk10 +/dev/disk/by-partlabel/asm-disk2 +/dev/disk/by-partlabel/asm-disk3 +/dev/disk/by-partlabel/asm-disk4 +/dev/disk/by-partlabel/asm-disk5 +/dev/disk/by-partlabel/asm-disk6 +/dev/disk/by-partlabel/asm-disk7 +/dev/disk/by-partlabel/asm-disk8 +/dev/disk/by-partlabel/asm-disk9 +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ sqlplus "/ as sysdba" + +SQL*Plus: Release 19.0.0.0.0 - Production on Tue Sep 2 17:35:52 2025 +Version 19.28.0.0.0 + +Copyright (c) 1982, 2025, Oracle. All rights reserved. + + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> set lines 200 +SQL> col DISKGROUP_NAME format a20 +SQL> col PATH format a50 +SQL> SELECT + dg.NAME AS DISKGROUP_NAME, + d.PATH, + d.MOUNT_STATUS, + d.HEADER_STATUS, + d.STATE, + d.TOTAL_MB, + d.FREE_MB +FROM + V$ASM_DISK d +JOIN + V$ASM_DISKGROUP dg ON d.GROUP_NUMBER = dg.GROUP_NUMBER +ORDER BY + dg.NAME, d.DISK_NUMBER; 2 3 4 5 6 7 8 9 10 11 12 13 14 + +DISKGROUP_NAME PATH MOUNT_S HEADER_STATU STATE TOTAL_MB FREE_MB +-------------------- -------------------------------------------------- ------- ------------ -------- ---------- ---------- +CRSDATA /dev/disk/by-partlabel/asm-disk1 CACHED MEMBER NORMAL 51196 51140 +CRSDATA /dev/disk/by-partlabel/asm-disk2 CACHED MEMBER NORMAL 51196 51136 +DBDATA /dev/disk/by-partlabel/asm-disk3 CACHED MEMBER NORMAL 51198 48073 +DBDATA /dev/disk/by-partlabel/asm-disk4 CACHED MEMBER NORMAL 51198 48070 +DBDATA /dev/disk/by-partlabel/asm-disk5 CACHED MEMBER NORMAL 51198 48071 +DBDATA /dev/disk/by-partlabel/asm-disk6 CACHED MEMBER NORMAL 51198 48065 +RECO /dev/disk/by-partlabel/asm-disk7 CACHED MEMBER NORMAL 51198 49565 +RECO /dev/disk/by-partlabel/asm-disk8 CACHED MEMBER NORMAL 51198 49568 +RECO /dev/disk/by-partlabel/asm-disk9 CACHED MEMBER NORMAL 51198 49566 +RECO /dev/disk/by-partlabel/asm-disk10 CACHED MEMBER NORMAL 51198 49568 + +10 rows selected. + +SQL> \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml new file mode 100644 index 00000000..1125fcff --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml @@ -0,0 +1,133 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 + - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 + - /dev/disk/by-partlabel/asm-disk3 # ASM disk device path 3 + - /dev/disk/by-partlabel/asm-disk4 # ASM disk device path 4 + - /dev/disk/by-partlabel/asm-disk5 # ASM disk device path 5 + - /dev/disk/by-partlabel/asm-disk6 # ASM disk device path 6 + - /dev/disk/by-partlabel/asm-disk7 # ASM disk device path 7 + - /dev/disk/by-partlabel/asm-disk8 # ASM disk device path 8 + - /dev/disk/by-partlabel/asm-disk9 # ASM disk device path 9 + - /dev/disk/by-partlabel/asm-disk10 # ASM disk device path 10 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM devices for CRSDATA Diskgroup + dbAsmDeviceList: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 # Comma-separated list of ASM devices for DATA Diskgroup + recoAsmDeviceList: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9,/dev/disk/by-partlabel/asm-disk10 # Comma-separated list of ASM devices for RECO Diskgroup + crsAsmDiskDg: "+CRSDATA" # Name of the Diskgroup for Voting Disks + dbRecoveryFileDest: "+RECO" # Name of the Diskgroup for Recovery Files + dbDataFileDestDg: "+DBDATA" # Name of the Diskgroup for Database Files + crsAsmDiskDgRedundancy: "EXTERNAL" # Specify the Redudancy for the diskgroup with CRS Files + dbAsmDiskDgRedundancy: "NORMAL" # Specify the Redudancy for the diskgroup with Database Files + recoAsmDiskDgRedudancy: "NORMAL" # Specify the Redudancy for the diskgroup with Recovery Files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml index 0b1de6d2..0757a1cf 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml @@ -1,34 +1,35 @@ -# API version for OracleRestart custom resource +# API version of the OracleRestart Custom Resource apiVersion: database.oracle.com/v4 -# The kind of Kubernetes object being defined +# Resource type being created kind: OracleRestart -# Metadata contains identifying information for the resource +# Metadata section to define the resource's name and namespace metadata: - name: oraclerestart-sample # Name of the OracleRestart resource - namespace: orestart # Kubernetes namespace for deployment + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into spec: - # Instance configuration for Oracle Restart + # Instance details for Oracle Restart instDetails: name: dbmc1 # Logical name of the OracleRestart instance hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME - # Target worker node(s) for this instance + # Worker nodes where the instance should be scheduled workerNode: - - 10.0.10.108 # IP address of the node where DB will run - + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install envVars: - - name: IGNORE_CRS_PREREQS + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. value: "true" - - name: IGNORE_DB_PREREQS + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. value: "true" - # NodePort service configuration for exposing DB port externally - nodePortSvc: - - svcType: nodeport # Service type (NodePort to expose externally) - name: dbmc1-service-nodeport # Name of the Kubernetes Service + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) # Mapping between container and host ports portMappings: @@ -36,14 +37,19 @@ spec: targetPort: 1521 # Target port for the service protocol: TCP # Protocol type (TCP for Oracle) nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" - # ASM (Automatic Storage Management) disk group configuration + # ASM (Automatic Storage Management) storage configuration asmStorageDetails: disksBySize: - - storageSizeInGb: 50 # ASM group size + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage diskNames: - - /dev/oracleoci/oraclevdd # Device for ASM disk 1 - - /dev/oracleoci/oraclevde # Device for ASM disk 2 + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 + - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 # SSH key secret used for remote access and automation sshKeySecret: @@ -57,30 +63,31 @@ spec: keyFileName: key.pem # Key file name inside secret pwdFileName: pwdfile.enc # Password file name inside secret - # Oracle RAC slim image to be used for container creation - image: localhost/oracle/database-rac:19.3.0-slim - # image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim # <-- Replace with your custom registry if applicable + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry - # Ensures the latest version of the image is always pulled - imagePullPolicy: Always + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent - # Optional: ServiceAccount for RBAC and security policies (mandatory in OpenShift) - # serviceAccountName: oraclerestart # <-- Uncomment for OpenShift environments + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes - # Oracle database service information + # Service-specific database name and related details serviceDetails: - name: soepdb # Logical name of the Oracle service (CDB or PDB) + name: soepdb # Name of the Oracle service (pluggable DB) - # Resource requests and limits for CPU and memory + # Resource requests and limits for memory and CPU resources: requests: - memory: "16Gi" # Minimum required memory - cpu: "2" # Minimum required CPU cores + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required limits: - memory: "16Gi" # Max allowed memory - cpu: "2" # Max allowed CPU cores + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed - # System-level kernel parameters needed for Oracle database + # Kernel-level system control parameters for Oracle securityContext: sysctls: - name: kernel.shmall @@ -92,19 +99,19 @@ spec: - name: kernel.shmmni value: "4096" - # Configuration for Oracle Grid and Database software + # Configuration parameters for Oracle Grid and Database homes configParams: - gridHome: "/u01/app/19c/grid" # Path for Oracle Grid Infrastructure home - gridBase: "/u01/app/grid" # Base path for Grid Infrastructure - dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle Database software home - dbBase: "/u01/app/oracle" # Oracle base directory - crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # Comma-separated list of ASM device files - inventory: "/u01/app/oraInventory" # Path to oraInventory - gridSwZipFile: "grid_home.zip" # Grid software ZIP file - dbSwZipFile: "db_home.zip" # Database software ZIP file - sgaSize: "3G" # Oracle SGA size - pgaSize: "1G" # Oracle PGA size - processes: 2000 # Number of Oracle background/user processes - cpuCount: 4 # Number of CPUs Oracle should use - dbName: "PORCLCDB" # Database name - hostSwStageLocation: /scratch/software/19c/HAS_19.28.0.0.0OCWRU_LINUX.X64_250626/ # Host software staging location + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.txt b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.txt new file mode 100644 index 00000000..ebb7218e --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.txt @@ -0,0 +1,389 @@ +$ kubectl apply -f oraclerestart_prov_nodeports.yaml + +-- Before the memory and cpu change: +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-09-10T15:45:13Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 234001859 + UID: 4b261940-9e2e-4ee0-8c21-75a57eb1b0ed +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Enable Ons: enable + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent + Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Worker Node: + 10.0.10.58 + Lb Service: + Node Port Svc: + Name: dbmc1-service-nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Node Port: 30200 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: nodeport + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-09-10T15:57:27Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-09-10T20:38:23Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: Pending + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + +[root@dbmc1-0 rac-work-dir]# cd /sys/fs/cgroup/memory +[root@dbmc1-0 memory]# cat memory.limit_in_bytes +17179869184 +[root@dbmc1-0 memory]# +[root@dbmc1-0 memory]# cd /sys/fs/cgroup/cpu/ +[root@dbmc1-0 cpu]# cat cpu.cfs_quota_us +200000 +[root@dbmc1-0 cpu]# + + + + + +-- Attempt the memory and cpu change: +$ kubectl apply -f oraclerestart_prov_nodeports_mcpu_change.yaml + +-- After the memory and cpu change: +-- The Oracle Restart Database Pod will be recreated: + +$ kubectl get all -n orestart +NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES +pod/dbmc1-0 0/1 Running 0 13s 10.244.0.124 10.0.10.58 + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR +service/dbmc1-0-local ClusterIP None 5h3m statefulset.kubernetes.io/pod-name=dbmc1-0 +service/dbmc1-service-nodeport-0-npsvc NodePort 10.96.249.147 1521:30007/TCP,6200:30200/TCP 5h3m statefulset.kubernetes.io/pod-name=dbmc1-0 + +NAME READY AGE CONTAINERS IMAGES +statefulset.apps/dbmc1 0/1 5h3m dbmc1 localhost/oracle/database-orestart:19.3.0-slim + + + +-- After some time: +$ kubectl get all -n orestart +NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES +pod/dbmc1-0 1/1 Running 0 3m 10.244.0.124 10.0.10.58 + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR +service/dbmc1-0-local ClusterIP None 5h17m statefulset.kubernetes.io/pod-name=dbmc1-0 +service/dbmc1-service-nodeport-0-npsvc NodePort 10.96.249.147 1521:30007/TCP,6200:30200/TCP 5h17m statefulset.kubernetes.io/pod-name=dbmc1-0 + +NAME READY AGE CONTAINERS IMAGES +statefulset.apps/dbmc1 1/1 5h17m dbmc1 localhost/oracle/database-orestart:19.3.0-slim + + + +-- Memory and CPU changes will be reflected as below: + +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-09-10T15:45:13Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 2 + Resource Version: 234009515 + UID: 4b261940-9e2e-4ee0-8c21-75a57eb1b0ed +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Enable Ons: enable + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent + Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Worker Node: + 10.0.10.58 + Lb Service: + Node Port Svc: + Name: dbmc1-service-nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Node Port: 30200 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: nodeport + Resources: + Limits: + Cpu: 4 + Memory: 20Gi + Requests: + Cpu: 4 + Memory: 20Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-09-10T15:57:27Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-09-10T21:03:50Z + Message: reconcile completed successfully + Observed Generation: 2 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: Pending + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + + + +[root@dbmc1-0 rac-work-dir]# cd /sys/fs/cgroup/memory +[root@dbmc1-0 memory]# cat memory.limit_in_bytes +21474836480 +[root@dbmc1-0 memory]# +[root@dbmc1-0 memory]# cd /sys/fs/cgroup/cpu/ +[root@dbmc1-0 cpu]# cat cpu.cfs_quota_us +400000 +[root@dbmc1-0 cpu]# \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.yaml new file mode 100644 index 00000000..71a409ac --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.yaml @@ -0,0 +1,117 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 + - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "20Gi" # Minimum memory required + cpu: "4" # Minimum CPU required + limits: + memory: "20Gi" # Max memory allowed + cpu: "4" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml deleted file mode 100644 index ea4b8ac6..00000000 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_onsport.yaml +++ /dev/null @@ -1,109 +0,0 @@ -# API version for the custom resource definition -apiVersion: database.oracle.com/v4 - -# Kind of resource being defined -kind: OracleRestart - -# Metadata section includes the name and namespace of the resource -metadata: - # Name of this OracleRestart resource instance - name: oraclerestart-sample - # Kubernetes namespace where this resource resides - namespace: orestart - -# Specification for the OracleRestart resource -spec: - # Details about the Oracle instance - instDetails: - # Name of the Oracle instance - name: dbmc1 - # Path where Oracle software will be installed on the host - hostSwLocation: /scratch/orestart/ - # List of worker node IPs where instance will be deployed - workerNode: - - 10.0.10.108 - envVars: - - name: IGNORE_CRS_PREREQS - value: "true" - - name: IGNORE_DB_PREREQS - value: "true" - # ASM (Automatic Storage Management) disk configuration - asmStorageDetails: - disksBySize: - - storageSizeInGb: 50 # Total size of disk group in GB - diskNames: # List of physical disk paths to be used - - /dev/oracleoci/oraclevdd - - /dev/oracleoci/oraclevde - - # SSH key secret references used for host access - sshKeySecret: - name: ssh-key-secret # Name of the Kubernetes secret - privKeySecretName: ssh-privkey # Private SSH key secret - pubKeySecretName: ssh-pubkey # Public SSH key secret - - # Database user credentials secret - dbSecret: - name: db-user-pass-pkutl # Name of secret containing DB credentials - keyFileName: key.pem # Key file name inside secret - pwdFileName: pwdfile.enc # Password file name inside secret - - # Container image for Oracle Restart - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0729 - - # Policy to always pull the image when starting - imagePullPolicy: Always - - # Details of the database service - serviceDetails: - name: soepdb # Name of the database service - - # Kubernetes resource requests and limits - resources: - requests: - memory: "16Gi" # Memory requested by container - cpu: "2" # CPUs requested by container - limits: - memory: "16Gi" # Maximum memory allowed - cpu: "2" # Maximum CPUs allowed - - # OS-level kernel parameters to be set in the container - securityContext: - sysctls: - - name: kernel.shmall - value: "2097152" # Shared memory pages - - name: kernel.sem - value: "250 32000 100 128" # Semaphore settings - - name: kernel.shmmax - value: "8589934592" # Max shared memory segment (8GB) - - name: kernel.shmmni - value: "4096" # Number of shared memory segments - lbService: - name: orestartsvc - svcType: lbservice - onsTargetPort: 30200 - portMappings: - - port: 1521 - targetPort: 1521 - protocol: TCP - - port: 6200 - targetPort: 6200 - protocol: TCP - # Target port for Oracle Notification Services (ONS) - enableOns: "enable" - # Configuration parameters for Oracle software - configParams: - gridHome: "/u01/app/19c/grid" # Path to Grid Infrastructure home - gridBase: "/u01/app/grid" # Base directory for Grid Infrastructure - dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle Database home - dbBase: "/u01/app/oracle" # Oracle Database base directory - crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # ASM device list - inventory: "/u01/app/oraInventory" # Oracle inventory location - gridSwZipFile: "grid_home.zip" # Grid software zip filename - dbSwZipFile: "db_home.zip" # DB software zip filename - sgaSize: "3G" # Size of SGA (System Global Area) - pgaSize: "1G" # Size of PGA (Program Global Area) - processes: 2000 # Maximum number of DB processes - cpuCount: 4 # Number of CPUs to allocate - dbName: "PORCLCDB" # Name of the Oracle database - hostSwStageLocation: "/scratch/software/19c/19.3.0/" # Location to stage installation software - ruPatchLocation: /scratch/software/19c/19.28/ # Location for Release Update patch \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml index 3440480f..034d7d7b 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml @@ -1,34 +1,35 @@ # API version of the OracleRestart Custom Resource apiVersion: database.oracle.com/v4 -# Kind of Kubernetes object being defined +# Resource type being created kind: OracleRestart -# Metadata section: name and namespace of the OracleRestart object +# Metadata section to define the resource's name and namespace metadata: name: oraclerestart-sample # Name of the OracleRestart instance namespace: orestart # Kubernetes namespace to deploy into spec: - # Instance configuration for Oracle Restart + # Instance details for Oracle Restart instDetails: name: dbmc1 # Logical name of the OracleRestart instance hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME - # Target worker node(s) for this instance + # Worker nodes where the instance should be scheduled workerNode: - - 10.0.10.108 # IP address of the node where DB will run + - 10.0.10.58 # IP address of the target node for deployment + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install envVars: - - name: IGNORE_CRS_PREREQS + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. value: "true" - - name: IGNORE_DB_PREREQS + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. value: "true" - # NodePort service configuration for exposing DB port externally - nodePortSvc: - - svcType: nodeport # Service type (NodePort to expose externally) - name: dbmc1 # Name of the Kubernetes Service + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) # Mapping between container and host ports portMappings: @@ -36,14 +37,19 @@ spec: targetPort: 1521 # Target port for the service protocol: TCP # Protocol type (TCP for Oracle) nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" - # ASM (Automatic Storage Management) disk group configuration + # ASM (Automatic Storage Management) storage configuration asmStorageDetails: disksBySize: - - storageSizeInGb: 50 # Size of each ASM disk + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage diskNames: - - /dev/oracleoci/oraclevdd # Device for ASM disk 1 - - /dev/oracleoci/oraclevde # Device for ASM disk 2 + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 + - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 # SSH key secret used for remote access and automation sshKeySecret: @@ -57,28 +63,31 @@ spec: keyFileName: key.pem # Key file name inside secret pwdFileName: pwdfile.enc # Password file name inside secret - # Image for Oracle Restart database container - # image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0703 - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim # Latest image to use - imagePullPolicy: Always # Always pull the image when starting the pod + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent - # Optional: ServiceAccount for RBAC and security policies (mandatory in OpenShift) - # serviceAccountName: oraclerestart <-- optional normally for normal kubernetes cluster but mandatory for Openshift cluster to allow permissions in security contexts + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes - # Service name exposed for the database + # Service-specific database name and related details serviceDetails: - name: soepdb + name: soepdb # Name of the Oracle service (pluggable DB) - # Resource requests and limits for CPU and memory + # Resource requests and limits for memory and CPU resources: requests: - memory: "16Gi" # Minimum required memory - cpu: "2" # Minimum required CPU cores + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required limits: - memory: "16Gi" # Max allowed memory - cpu: "2" # Max allowed CPU cores + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed - # Security context for kernel parameters tuning + # Kernel-level system control parameters for Oracle securityContext: sysctls: - name: kernel.shmall @@ -90,27 +99,22 @@ spec: - name: kernel.shmmni value: "4096" - # Configuration for Oracle Grid and Database software + # Configuration parameters for Oracle Grid and Database homes configParams: - gridHome: "/u01/app/19c/grid" # Path for Oracle Grid Infrastructure home - gridBase: "/u01/app/grid" # Base path for Grid Infrastructure - dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle Database software home - dbBase: "/u01/app/oracle" # Oracle base directory - crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # Comma-separated list of ASM device files - inventory: "/u01/app/oraInventory" # Path to oraInventory - gridSwZipFile: "grid_home.zip" # Grid software ZIP file - dbSwZipFile: "db_home.zip" # Database software ZIP file - oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" - sgaSize: "3G" # Size of SGA memory - pgaSize: "1G" # Size of PGA memory - processes: 2000 # Number of Oracle processes - cpuCount: 4 # Number of CPUs to allocate - dbName: "PORCLCDB" # Database name - - # Location where Base Release Software .zip files are staged - hostSwStageLocation: /scratch/software/stage # Base Release Software Location - - # Directory containing the unzipped RU patch (includes PatchSearch.xml) - ruPatchLocation: /scratch/software/ru_patch #<--- RU patch unzipped folder - - oPatchLocation: "/scratch/software/19c/19.28" + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" # Opatch software ZIP + hostSwStageLocation: /scratch/software/stage/19.3.0 # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod + ruPatchLocation: /scratch/software/ru_patch/37957391 # Directory containing the unzipped RU patch, same location will be mounted inside the Oracle Restart Pod + oPatchLocation: /scratch/software/opatch # Location of the Opatch Software, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml index 31f7058f..ec292230 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml @@ -1,154 +1,130 @@ -# API version for the Oracle Database operator +# API version of the OracleRestart Custom Resource apiVersion: database.oracle.com/v4 -# Specifies the resource type + +# Resource type being created kind: OracleRestart -# Metadata section for naming and scoping + +# Metadata section to define the resource's name and namespace metadata: - # Name of the OracleRestart instance - name: oraclerestart-sample - # Kubernetes namespace to deploy in - namespace: orestart -# Specification for the OracleRestart custom resource + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + spec: - # Instance details section + # Instance details for Oracle Restart instDetails: - # The logical/cluster name for the instance - name: dbmc1 - # List of worker nodes IPs for deployment + name: dbmc1 # Logical name of the OracleRestart instance + + # Worker nodes where the instance should be scheduled workerNode: - - 10.0.10.108 - # Storage size for Oracle software location, in GB - swLocStorageSizeInGb: 300 - # Environment variables passed to the container - envVars: - # Ignore CRS prerequisites - - name: IGNORE_CRS_PREREQS - value: "true" - # Ignore Database prerequisites - - name: IGNORE_DB_PREREQS - value: "true" - # StorageClass for block volumes - storageClass: "oci-bv" - # ASM Storage configuration for Oracle Automatic Storage Management + - 10.0.10.58 # IP address of the target node for deployment + + # Size of the storage location for software. This parameter will be effective when the local SW location is NOT specified by hostSwLocation + swLocStorageSizeInGb: 300 + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # Optional: Set this to enable dynamic provisioning of PVCs + storageClass: "oci-bv" + + # ASM (Automatic Storage Management) storage configuration asmStorageDetails: - # List ASM disks by size in GB disksBySize: - - storageSizeInGb: 50 - # Physical device names for ASM disks - diskNames: - - /dev/asm-disk1 - - /dev/asm-disk2 - # SSH key secrets (for setup and access) + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/asm-disk1 # Device for ASM disk 1 + - /dev/asm-disk2 # Device for ASM disk 2 + + # SSH key secret used for remote access and automation sshKeySecret: - # Kubernetes secret containing SSH keys - name: ssh-key-secret - # Secret name for private key - privKeySecretName: ssh-privkey - # Secret name for public key - pubKeySecretName: ssh-pubkey - # Database user/password secret info + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials dbSecret: - # Secret containing DB user credentials - name: db-user-pass-pkutl - # Private key filename (for decrypting password) - keyFileName: key.pem - # Encrypted password file name - pwdFileName: pwdfile.enc - # Database Docker image to use - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-oneoff - # Pull policy for the image: always pull - imagePullPolicy: Always - # Details for the Oracle service + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details serviceDetails: - # Oracle DB service name - name: soepdb - # Kubernetes resource requests (guaranteed) for container - resources: - requests: - # Requested memory - memory: "16Gi" - # Requested vCPU - cpu: "2" - # Resource limits (max allowed) - limits: - # Maximum memory - memory: "16Gi" - # Maximum CPU - cpu: "2" - # Define sysctls for the container security context - securityContext: - # Kernel parameter: max shared memory pages - sysctls: - - name: kernel.shmall - value: "2097152" - # Kernel parameter: semaphores - - name: kernel.sem - value: "250 32000 100 128" - # Kernel parameter: max shared memory size (bytes) - - name: kernel.shmmax - value: "8589934592" - # Kernel parameter: max shared memory segments - - name: kernel.shmmni - value: "4096" - # Load balancer service configuration - lbService: - # Service name - name: orestartsvc - # Type of service: load balancer - svcType: lbservice - # Port mappings for service - portMappings: - # Oracle listener port - - port: 1521 - targetPort: 1521 - protocol: TCP - # Additional database port - - port: 6200 - targetPort: 6200 - protocol: TCP - # Extra configuration parameters for grid and database + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes configParams: - # Grid Infrastructure home path - gridHome: "/u01/app/19c/grid" - # Grid base directory - gridBase: "/u01/app/grid" - # Oracle DB home path - dbHome: "/u01/app/oracle/product/19c/dbhome_1" - # Oracle base directory - dbBase: "/u01/app/oracle" - # Comma-separated list of ASM device paths - crsAsmDeviceList: "/dev/asm-disk1,/dev/asm-disk2" - # Inventory location for Oracle software - inventory: "/u01/app/oraInventory" - # Grid software installation archive - gridSwZipFile: "grid_home.zip" - # Oracle database software archive - dbSwZipFile: "db_home.zip" - # OPatch (Oracle patch tool) archive - oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" - # SGA (System Global Area) size for Oracle DB - sgaSize: "3G" - # PGA (Program Global Area) size for Oracle DB - pgaSize: "1G" - # Maximum number of DB processes - processes: 2000 - # Number of CPUs allocated for DB - cpuCount: 4 - # Oracle database name (CDB) - dbName: "PORCLCDB" - # PVC for software staging area - swStagePvc: pv-stage-vol-claim - # Mount location for the software stage - swStagePvcMountLocation: "/stage" - # Host path for software staging - hostSwStageLocation: "/stage/software/19c/19.3.0" - # RU patch file location - ruPatchLocation: "/stage/software/19c/new/RU/37957391" - # OPatch working directory - oPatchLocation: "/stage/software/19c" - # One-off patch files directory - oneOffLocation: "/stage/software/19c/new/oneoff" - # Comma-separated DB one-off patch IDs - dbOneOffIds: "34436514" # DB one-offs comma separated - # Comma-separated Grid one-off patch IDs - gridOneOffIds: "38336965" # Grid one-offs comma separated \ No newline at end of file + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/asm-disk1,/dev/asm-disk2 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" # Opatch software ZIP + swStagePvc: pv-stage-vol-claim # PVC name for the Software Location mounting the NFS + swStagePvcMountLocation: "/stage" # Mount point for the Software Location inside the Pod + hostSwStageLocation: /stage/software/19c/19.3.0 # Base Release Software Location on the NFS based Persistent Volume, Pod will also have this software in the same path + ruPatchLocation: /stage/software/19c/19.28/37957391 # Directory containing the unzipped RU patch on the NFS based Persistent Volume, Pod will also have this software in the same path + oPatchLocation: /stage/software/19c # Location of the Opatch Software on the NFS based Persistent Volume, Pod will also have this software in the same path + oneOffLocation: /stage/software/19c/oneoff # One-off patch files directory where all the one-off patches for GI and RDBMS Home are unzipped + gridOneOffIds: "38336965,34436514" # Comma-separated Grid one-off patch IDs + dbOneOffIds: "38336965,34436514" # Comma-separated DB one-off patch IDs \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml index fa69203d..3ff71907 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml @@ -1,157 +1,127 @@ -# API group and version of the custom resource +# API version of the OracleRestart Custom Resource apiVersion: database.oracle.com/v4 -# Specifies the kind of custom resource +# Resource type being created kind: OracleRestart -# Metadata about the custom resource +# Metadata section to define the resource's name and namespace metadata: - # Name of the OracleRestart instance - name: oraclerestart-sample - # Namespace in which the instance will be deployed - namespace: orestart + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into -# Specification of the OracleRestart instance spec: - # Instance-specific configuration + # Instance details for Oracle Restart instDetails: - # Logical name of the OracleRestart DB instance - name: dbmc1 - # Base path on the host where software will be installed - hostSwLocation: /scratch/orestart/ - # IP address of the worker node where the instance should run + name: dbmc1 # Logical name of the OracleRestart instance + + # Worker nodes where the instance should be scheduled workerNode: - - 10.0.10.108 + - 10.0.10.58 # IP address of the target node for deployment + + # Size of the storage location for software. This parameter will be effective when the local SW location is NOT specified by hostSwLocation + swLocStorageSizeInGb: 300 + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install envVars: - - name: IGNORE_CRS_PREREQS + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. value: "true" - - name: IGNORE_DB_PREREQS + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. value: "true" - # Service definition to expose the DB - nodePortSvc: - - # Type of Kubernetes service - svcType: nodeport - # Name of the service - name: dbmc1 - # Port mappings for the service - portMappings: - - # Port exposed by the service - port: 1521 - # Container port - targetPort: 1521 - # Protocol used for communication - protocol: TCP - # Node port exposed on the worker node - nodePort: 30007 - # Optional: Set this to enable dynamic provisioning of PVCs - # storageClass: "oci-bv" + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) - # PVCs for RU patch and staged host software - pvcName: - # Mount path for the RU patch files - rupatch-location-pvc: "/scratch/software/19c/19.28/" - # Mount path for the staged host software - hostsw-stage-pvc: "/scratch/stage/software" + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # Optional: Set this to enable dynamic provisioning of PVCs + storageClass: "oci-bv" - # ASM disk configuration + # ASM (Automatic Storage Management) storage configuration asmStorageDetails: - # Disks grouped by size disksBySize: - - # Size (in Gi) of each ASM disk - storageSizeInGb: 50 - # List of block devices to be used as ASM disks + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage diskNames: - - /dev/oracleoci/oraclevdd - - /dev/oracleoci/oraclevde + - /dev/asm-disk1 # Device for ASM disk 1 + - /dev/asm-disk2 # Device for ASM disk 2 - # SSH key secret configuration + # SSH key secret used for remote access and automation sshKeySecret: - # Name of the secret containing SSH keys - name: ssh-key-secret - # Private key filename inside the secret - privKeySecretName: ssh-privkey - # Public key filename inside the secret - pubKeySecretName: ssh-pubkey - - # Secret for database passwords and key files + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials dbSecret: - # Name of the Kubernetes secret - name: db-user-pass-pkutl - # Encrypted key file name inside the secret - keyFileName: key.pem - # Encrypted password file name inside the secret - pwdFileName: pwdfile.enc + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry - # Podman image to use for the Oracle database - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0725 + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent - # Always pull the latest image - imagePullPolicy: Always + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes - # Service details for database registration + # Service-specific database name and related details serviceDetails: - # Name of the database service - name: soepdb + name: soepdb # Name of the Oracle service (pluggable DB) - # Resource requests and limits for the pod + # Resource requests and limits for memory and CPU resources: requests: - # Minimum memory allocation - memory: "16Gi" - # Minimum CPU allocation - cpu: "2" + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required limits: - # Maximum memory allocation - memory: "16Gi" - # Maximum CPU allocation - cpu: "2" + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed - # Security settings and kernel parameters + # Kernel-level system control parameters for Oracle securityContext: sysctls: - - # Shared memory pages - name: kernel.shmall + - name: kernel.shmall value: "2097152" - - # Semaphore settings - name: kernel.sem + - name: kernel.sem value: "250 32000 100 128" - - # Maximum shared memory segment size - name: kernel.shmmax + - name: kernel.shmmax value: "8589934592" - - # Number of shared memory segments - name: kernel.shmmni + - name: kernel.shmmni value: "4096" - # Configuration parameters for Oracle Grid and DB + # Configuration parameters for Oracle Grid and Database homes configParams: - # Path to the Oracle Grid home - gridHome: "/u01/app/19c/grid" - # Path to the Oracle Grid base - gridBase: "/u01/app/grid" - # Path to the Oracle DB home - dbHome: "/u01/app/oracle/product/19c/dbhome_1" - # Path to the Oracle DB base - dbBase: "/u01/app/oracle" - # Comma-separated list of block devices for ASM - crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde - # Path to Oracle Inventory - inventory: "/u01/app/oraInventory" - # Filename of the Grid software ZIP - gridSwZipFile: "grid_home.zip" - # Filename of the DB software ZIP - dbSwZipFile: "db_home.zip" - # SGA memory size - sgaSize: "3G" - # PGA memory size - pgaSize: "1G" - # Number of database processes - processes: 2000 - # Number of CPUs Oracle will see - cpuCount: 4 - # Name of the Oracle database - dbName: "PORCLCDB" - # Reference to PVC for software staging - hostSwStageLocation: "hostsw-stage-pvc" - # Reference to PVC for patch files - ruPatchLocation: "rupatch-location-pvc" + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/asm-disk1,/dev/asm-disk2 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" # Opatch software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + swStagePvc: pv-stage-vol-claim # PVC name for the Software Location mounting the NFS + swStagePvcMountLocation: "/stage" # Mount point for the Software Location inside the Pod + hostSwStageLocation: /stage/software/19c/19.3.0 # Base Release Software Location on the NFS based Persistent Volume, Pod will also have this software in the same path + ruPatchLocation: /stage/software/19c/19.28/37957391 # Directory containing the unzipped RU patch on the NFS based Persistent Volume, Pod will also have this software in the same path + oPatchLocation: /stage/software/19c # Location of the Opatch Software on the NFS based Persistent Volume, Pod will also have this software in the same path diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml deleted file mode 100644 index 5289112c..00000000 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_storage.yaml +++ /dev/null @@ -1,111 +0,0 @@ -# API version for the OracleRestart custom resource -apiVersion: database.oracle.com/v4 - -# Kind of the custom resource being defined -kind: OracleRestart - -# Metadata section: defines the name and namespace of the resource -metadata: - name: oraclerestart-sample # Name of the OracleRestart instance - namespace: orestart # Namespace where it will be deployed - -# Specification of the OracleRestart instance -spec: - # Instance-level details for this OracleRestart deployment - instDetails: - name: dbmc1 # Unique name of the OracleRestart instance - hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME - workerNode: - - 02-00-17-01-73-a0 # Node hostname where the pod should be scheduled - - envVars: - - name: IGNORE_CRS_PREREQS - value: "true" - - name: IGNORE_DB_PREREQS - value: "true" - - # NodePort service configuration - nodePortSvc: - - svcType: nodeport # Type of Kubernetes service to expose the Oracle DB - name: dbmc1-service-nodeport # Name of the service - - # Port mapping for the database service - portMappings: - - port: 1521 # Internal port inside the pod - targetPort: 1521 # Target port of the container - protocol: TCP # Protocol used - nodePort: 30007 # NodePort exposed on the worker node - - # Custom Storage class to be used for dynamic provisioning (if applicable) - storageClass: "ocs-storagecluster-ceph-rbd" - - # ASM disk configuration, grouped by disk size - asmStorageDetails: - disksBySize: - - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage - diskNames: - - /dev/oracleoci/oraclevdd # First disk path for ASM - - /dev/oracleoci/oraclevde # Second disk path for ASM - - # SSH secrets for inter-node communication and remote execution - sshKeySecret: - name: ssh-key-secret # Base secret name - privKeySecretName: ssh-privkey # Name of secret containing the private key - pubKeySecretName: ssh-pubkey # Name of secret containing the public key - - # Secret containing encrypted DB password and key - dbSecret: - name: db-user-pass-pkutl # Secret name - keyFileName: key.pem # Name of the key file inside the secret - pwdFileName: pwdfile.enc # Name of the encrypted password file inside the secret - - # Oracle image used to create the container - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0703 - - # Image pull policy (Always pull the latest version) - imagePullPolicy: Always - - # Kubernetes service account to be used for running the pod - serviceAccountName: oraclerestart - - # Logical name of the database service inside Oracle - serviceDetails: - name: soepdb - - # Resource requests and limits for CPU and memory - resources: - requests: - memory: "16Gi" # Memory requested by the container - cpu: "2" # CPU requested by the container - limits: - memory: "16Gi" # Maximum memory container can use - cpu: "2" # Maximum CPU container can use - - # Kernel parameters to be set inside the pod - securityContext: - sysctls: - - name: kernel.shmall - value: "2097152" - - name: kernel.sem - value: "250 32000 100 128" - - name: kernel.shmmax - value: "8589934592" - - name: kernel.shmmni - value: "4096" - - # Configuration parameters required for Oracle Grid and DB installation - configParams: - gridHome: "/u01/app/19c/grid" # Path to Grid home - gridBase: "/u01/app/grid" # Path to Grid base - dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Path to DB home - dbBase: "/u01/app/oracle" # Path to DB base - crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde # List of ASM disks - inventory: "/u01/app/oraInventory" # Oracle Inventory location - gridSwZipFile: "grid_home.zip" # Grid software zip filename - dbSwZipFile: "db_home.zip" # DB software zip filename - sgaSize: "3G" # Size of the SGA memory pool - pgaSize: "1G" # Size of the PGA memory pool - processes: 2000 # Maximum number of processes - cpuCount: 4 # CPU count for the instance - dbName: "PORCLCDB" # Oracle database name - hostSwStageLocation: /scratch/software/stage # Staging location for unzipping Oracle software diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml new file mode 100644 index 00000000..4ef150d3 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml @@ -0,0 +1,120 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # Custom Storage class to be used for dynamic provisioning (if applicable) + storageClass: "oci-bv" + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/asm-disk1 # ASM disk device path 1 + - /dev/asm-disk2 # ASM disk device path 2 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/asm-disk1,/dev/asm-disk2 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml new file mode 100644 index 00000000..7f114e1d --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml @@ -0,0 +1,123 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.58 # IP address of the target node for deployment + + # Size of the storage location for software. This parameter will be effective when the local SW location is NOT specified by hostSwLocation + swLocStorageSizeInGb: 400 + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # Custom Storage class to be used for dynamic provisioning (if applicable) + storageClass: "oci-bv" + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/asm-disk1 # Device for ASM disk 1 + - /dev/asm-disk2 # Device for ASM disk 2 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/asm-disk1,/dev/asm-disk2 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod + diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml new file mode 100644 index 00000000..ff1fec2f --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml @@ -0,0 +1,123 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.58 # IP address of the target node for deployment + + # Size of the storage location for software. This parameter will be effective when the local SW location is NOT specified by hostSwLocation + swLocStorageSizeInGb: 300 + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # Custom Storage class to be used for dynamic provisioning (if applicable) + storageClass: "oci-bv" + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/asm-disk1 # Device for ASM disk 1 + - /dev/asm-disk2 # Device for ASM disk 2 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/asm-disk1,/dev/asm-disk2 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod + diff --git a/docs/oraclerestart/provisioning/orestart_oneoffs_object.txt b/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt similarity index 76% rename from docs/oraclerestart/provisioning/orestart_oneoffs_object.txt rename to docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt index 47fe2a8e..825d13d0 100644 --- a/docs/oraclerestart/provisioning/orestart_oneoffs_object.txt +++ b/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt @@ -1,18 +1,18 @@ -kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart Name: oraclerestart-sample Namespace: orestart Labels: Annotations: OracleRestarts.database.oracle.com/old-spec: - {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":300,"workerNode":["10.0.10.108"],"envVars":[{"name":"IGNORE_CRS_PREREQS","value":"tr... + {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":300,"workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","value":"tru... API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-09-07T17:23:34Z + Creation Timestamp: 2025-09-11T04:55:37Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 30411716 - UID: 588ce846-5b82-4b61-b0fc-e281e7ba1433 + Resource Version: 234329900 + UID: 5ed0c257-dfb3-40da-9e97-46dabf65e5ac Spec: Asm Storage Details: Disks By Size: @@ -30,23 +30,23 @@ Spec: Db Data File Dest Dg: +DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db One Off Ids: 34436514 + Db One Off Ids: 38336965,34436514 Db Recovery File Dest: +DATA Db Response File: Db Sw Zip File: db_home.zip Grid Base: /u01/app/grid Grid Home: /u01/app/19c/grid - Grid One Off Ids: 38336965 + Grid One Off Ids: 38336965,34436514 Grid Response File: Grid Sw Zip File: grid_home.zip Host Sw Stage Location: /stage/software/19c/19.3.0 Inventory: /u01/app/oraInventory O Patch Location: /stage/software/19c O Patch Sw Zip File: p6880880_190000_Linux-x86-64.zip - One Off Location: /stage/software/19c/new/oneoff + One Off Location: /stage/software/19c/oneoff Pga Size: 1G Processes: 2000 - Ru Patch Location: /stage/software/19c/new/RU/37957391 + Ru Patch Location: /stage/software/19c/19.28/37957391 Sga Size: 3G Sw Mount Location: /u01 Sw Stage Pvc: pv-stage-vol-claim @@ -58,8 +58,8 @@ Spec: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Enable Ons: enable - Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-oneoff - Image Pull Policy: Always + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent Inst Details: Env Vars: Name: IGNORE_CRS_PREREQS @@ -69,18 +69,20 @@ Spec: Name: dbmc1 Sw Loc Storage Size In Gb: 300 Worker Node: - 10.0.10.108 + 10.0.10.58 Lb Service: - Name: orestartsvc + Node Port Svc: + Name: dbmc1-service-nodeport Port Mappings: + Node Port: 30007 Port: 1521 Protocol: TCP Target Port: 1521 + Node Port: 30200 Port: 6200 Protocol: TCP Target Port: 6200 - Svc Type: lbservice - Node Port Svc: + Svc Type: nodeport Resources: Limits: Cpu: 2 @@ -124,18 +126,18 @@ Status: Name: DATA Redundancy: EXTERN Conditions: - Last Transition Time: 2025-09-07T19:59:57Z - Message: reconcile has been queued - Observed Generation: 1 - Reason: LastReconcileCycleQueued - Status: True - Type: ReconcileQueued - Last Transition Time: 2025-09-08T06:44:04Z + Last Transition Time: 2025-09-11T04:55:37Z Message: reconcile completed successfully Observed Generation: 1 Reason: LastReconcileCycleCompleted Status: True Type: ReconcileComplete + Last Transition Time: 2025-09-11T14:46:43Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued Config Params: Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 Crs Asm Disk Dg: +DATA @@ -166,4 +168,26 @@ Status: Name: soepdb Svc State: service soepdb is running State: AVAILABLE -Events: \ No newline at end of file +Events: + + + +[grid@dbmc1-0 ~]$ $ORACLE_HOME/OPatch/opatch lspatches +34436514;DBCA REPORTS INCORRECT MEMORY IN PODMAN CONTAINERS +38336965;OCW Interim patch for 38336965 +38124772;TOMCAT RELEASE UPDATE 19.0.0.0.0 (38124772) +37962938;ACFS RELEASE UPDATE 19.28.0.0.0 (37962938) +37960098;Database Release Update : 19.28.0.0.250715 (37960098) +36758186;DBWLM RELEASE UPDATE 19.0.0.0.0 (36758186) + +OPatch succeeded. + + + + +[oracle@dbmc1-0 ~]$ $ORACLE_HOME/OPatch/opatch lspatches +37962946;OCW RELEASE UPDATE 19.28.0.0.0 (37962946) +37960098;Database Release Update : 19.28.0.0.250715 (37960098) + +OPatch succeeded. +[oracle@dbmc1-0 ~]$ \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_ons_object.txt b/docs/oraclerestart/provisioning/orestart_loadbalancer_object.txt similarity index 62% rename from docs/oraclerestart/provisioning/orestart_ons_object.txt rename to docs/oraclerestart/provisioning/orestart_loadbalancer_object.txt index 254adb43..b018eaf0 100644 --- a/docs/oraclerestart/provisioning/orestart_ons_object.txt +++ b/docs/oraclerestart/provisioning/orestart_loadbalancer_object.txt @@ -3,26 +3,26 @@ Name: oraclerestart-sample Namespace: orestart Labels: Annotations: OracleRestarts.database.oracle.com/old-spec: - {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"nodePortSvc":[{"name":"dbmc1","svcType"... + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","... API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-07-29T15:21:49Z + Creation Timestamp: 2025-09-02T19:48:48Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 12701496 - UID: 5c0506e6-3674-4284-b828-d462b8c17b64 + Resource Version: 230654736 + UID: 38d28453-0906-4e31-b1cf-b0c2980293fa Spec: Asm Storage Details: Disks By Size: Disk Names: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 Storage Size In Gb: 50 Config Params: Cpu Count: 4 - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -37,11 +37,10 @@ Spec: Grid Home: /u01/app/19c/grid Grid Response File: Grid Sw Zip File: grid_home.zip - Host Sw Stage Location: /scratch/software/19c/19.3.0/ + Host Sw Stage Location: /scratch/software/stage Inventory: /u01/app/oraInventory Pga Size: 1G Processes: 2000 - Ru Patch Location: /scratch/software/19c/19.28/ Sga Size: 3G Sw Mount Location: /u01 Db Secret: @@ -50,22 +49,27 @@ Spec: Name: db-user-pass-pkutl Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc - Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0729 - Image Pull Policy: Always + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true Host Sw Location: /scratch/orestart/ Name: dbmc1 - Node Port Svc: - Name: dbmc1 - Svc Type: nodeport - Ons Target Port: 30200 + Worker Node: + 10.0.10.58 + Lb Service: Port Mappings: - Node Port: 30007 Port: 1521 Protocol: TCP Target Port: 1521 - Worker Node: - 10.0.10.108 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: lbservice Resources: Limits: Cpu: 2 @@ -94,30 +98,34 @@ Status: Oracle Restart Nodes: Name: dbmc1-0 Node Details: - Instance State: NOTAVAILABLE - Pod State: PODAVAILABLE - Cluster State: NOT HEALTHY + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY Mounted Devices: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde - State: FAILED + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + State: AVAILABLE Asm Details: Diskgroup: Disks: - /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Name: DATA Redundancy: EXTERN - Client Etc Host: - 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local Conditions: - Last Transition Time: 2025-07-29T16:11:50Z - Message: oracle restart database is in a restricted state: FAILED + Last Transition Time: 2025-09-02T20:06:26Z + Message: reconcile has been queued Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued + Last Transition Time: 2025-09-02T21:13:28Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete Config Params: - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -138,25 +146,24 @@ Status: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Db State: OPEN - Inst Details: - Name: - Pdb Connect String: dbmc1-0:1521/ORCLPDB - Release Update: 19.3.0.0.0 - Role: PRIMARY + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY Service Details: Name: soepdb Svc State: service soepdb is running - State: FAILED + State: AVAILABLE Events: -orestart/ $ kubectl get all -n orestart + + +$ kubectl get all -n orestart NAME READY STATUS RESTARTS AGE -pod/dbmc1-0 1/1 Running 0 52m +pod/dbmc1-0 1/1 Running 0 85m -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/dbmc1 NodePort 10.96.177.66 1521:30007/TCP 52m -service/dbmc1-0 ClusterIP None 52m -service/dbmc1-0-ons NodePort 10.96.146.166 6200:30200/TCP 5h5m +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/dbmc1 LoadBalancer 10.96.60.91 XXX.XXX.XXX.XX 1521:32083/TCP,6200:30857/TCP 85m +service/dbmc1-0 ClusterIP None 85m NAME READY AGE -statefulset.apps/dbmc1 1/1 52m \ No newline at end of file +statefulset.apps/dbmc1 1/1 85m \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_nodeport_object.txt b/docs/oraclerestart/provisioning/orestart_nodeport_object.txt index 88d2fc36..ef1385ac 100644 --- a/docs/oraclerestart/provisioning/orestart_nodeport_object.txt +++ b/docs/oraclerestart/provisioning/orestart_nodeport_object.txt @@ -1,28 +1,28 @@ -kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart Name: oraclerestart-sample Namespace: orestart Labels: Annotations: OracleRestarts.database.oracle.com/old-spec: - {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"nodePortSvc":[{"name":"dbmc1-service-no... + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-07-21T06:32:13Z + Creation Timestamp: 2025-08-20T01:53:13Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 8946152 - UID: c861883d-285b-47ef-ae14-032540149729 + Resource Version: 224754865 + UID: f9404b92-c1b2-400d-8fff-9bda472a7d54 Spec: Asm Storage Details: Disks By Size: Disk Names: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 Storage Size In Gb: 50 Config Params: Cpu Count: 4 - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -37,7 +37,7 @@ Spec: Grid Home: /u01/app/19c/grid Grid Response File: Grid Sw Zip File: grid_home.zip - Host Sw Stage Location: /scratch/software/19c/HAS_19.28.0.0.0OCWRU_LINUX.X64_250626/ + Host Sw Stage Location: /scratch/software/stage Inventory: /u01/app/oraInventory Pga Size: 1G Processes: 2000 @@ -49,7 +49,7 @@ Spec: Name: db-user-pass-pkutl Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc - Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0718 + Image: localhost/oracle/database-orestart:19.3.0-slim Image Pull Policy: Always Inst Details: Host Sw Location: /scratch/orestart/ @@ -63,7 +63,7 @@ Spec: Protocol: TCP Target Port: 1521 Worker Node: - 10.0.10.108 + 10.0.10.58 Resources: Limits: Cpu: 2 @@ -96,32 +96,30 @@ Status: Pod State: AVAILABLE Cluster State: HEALTHY Mounted Devices: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 State: AVAILABLE Asm Details: Diskgroup: Disks: - /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Name: DATA Redundancy: EXTERN - Client Etc Host: - 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local Conditions: - Last Transition Time: 2025-07-21T06:45:22Z + Last Transition Time: 2025-08-20T02:04:46Z Message: oracle restart database is in a restricted state: PROVISIONING Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued - Last Transition Time: 2025-07-21T06:58:29Z + Last Transition Time: 2025-08-20T04:22:18Z Message: no reconcile errors Observed Generation: 1 Reason: LastReconcileCycleCompleted Status: True Type: ReconcileComplete Config Params: - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -142,8 +140,9 @@ Status: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb Inst Details: - Name: + Name: Pdb Connect String: dbmc1-0:1521/ORCLPDB Release Update: 19.28.0.0.0 Role: PRIMARY diff --git a/docs/oraclerestart/provisioning/orestart_object.txt b/docs/oraclerestart/provisioning/orestart_object.txt index b1d9428c..41fb5c50 100644 --- a/docs/oraclerestart/provisioning/orestart_object.txt +++ b/docs/oraclerestart/provisioning/orestart_object.txt @@ -1,127 +1,147 @@ -kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart - Name: oraclerestart-sample - Namespace: orestart - Labels: - Annotations: OracleRestarts.database.oracle.com/old-spec: - {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"envFile":"dbmc1oraclerestart-sample-cma... - API Version: database.oracle.com/v4 - Kind: OracleRestart - Metadata: - Creation Timestamp: 2025-07-02T06:51:16Z - Finalizers: - database.oracle.com/oraclerestartfinalizer - Generation: 1 - Resource Version: 386393 - UID: d9faa000-bb7f-42b7-83d8-cf43f6bf30e9 - Spec: - Asm Storage Details: - Disks By Size: - Disk Names: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde - Storage Size In Gb: 50 - Config Params: - Cpu Count: 4 - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde - Db Base: /u01/app/oracle - Db Home: /u01/app/oracle/product/19c/dbhome_1 - Db Name: PORCLCDB - Db Sw Zip File: db_home.zip - Grid Base: /u01/app/grid - Grid Home: /u01/app/19c/grid - Grid Sw Zip File: grid_home.zip - Host Sw Stage Location: /scratch/software/19c/HAS_19.28.0.0.0OCWRU_LINUX.X64_250626/ - Inventory: /u01/app/oraInventory - Pga Size: 1G - Processes: 2000 - Sga Size: 3G - Db Secret: - Key File Name: key.pem - Name: db-user-pass-pkutl - Pwd File Name: pwdfile.enc - Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0630 - Image Pull Policy: Always - Inst Details: - Host Sw Location: /scratch/orestart/ - Name: dbmc1 - Worker Node: - 10.0.10.108 - Resources: - Limits: - Cpu: 2 - Memory: 16Gi - Requests: - Cpu: 2 - Memory: 16Gi - Security Context: - Sysctls: - Name: kernel.shmall - Value: 2097152 - Name: kernel.sem - Value: 250 32000 100 128 - Name: kernel.shmmax - Value: 8589934592 - Name: kernel.shmmni - Value: 4096 - Service Details: - Name: soepdb - Ssh Key Secret: - Name: ssh-key-secret - Priv Key Secret Name: ssh-privkey - Pub Key Secret Name: ssh-pubkey - Status: - Oracle Restart Nodes: - Name: dbmc1-0 - Node Details: - Instance State: OPEN - Pod State: AVAILABLE - Cluster State: Pending - Mounted Devices: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde - State: AVAILABLE - Asm Details: - Diskgroup: - Disks: - NOT READY - Name: NOT READY - Redundancy: NOT READY - Client Etc Host: - 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local - Conditions: - Last Transition Time: 2025-07-02T08:03:17Z - Message: no reconcile errors - Observed Generation: 1 - Reason: LastReconcileCycleCompleted - Status: True - Type: ReconcileComplete - Config Params: - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde - Crs Asm Disk Dg: +DATA - Crs Asm Disk Dg Redundancy: external - Db Base: /u01/app/oracle - Db Data File Dest Dg: +DATA - Db Home: /u01/app/oracle/product/19c/dbhome_1 - Db Name: PORCLCDB - Db Recovery File Dest: +DATA - Db Response File: - Grid Base: /u01/app/grid - Grid Home: /u01/app/19c/grid - Grid Response File: - Inventory: /u01/app/oraInventory - Connect String: dbmc1-0:1521/PORCLCDB - Db Secret: - Key File Name: key.pem - Name: db-user-pass-pkutl - Pwd File Name: pwdfile.enc - Db State: OPEN - Inst Details: - Name: - Pdb Connect String: dbmc1-0:1521/ORCLPDB - Release Update: 19.28.0.0.0 - Role: PRIMARY - Service Details: - Name: soepdb - Svc State: service soepdb is running - State: AVAILABLE - Events: \ No newline at end of file +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-08-27T03:31:52Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 227759597 + UID: 41235735-cadc-49d3-b198-56e6bb4a1d9f +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent + Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Host Sw Location: /scratch/orestart/ + Name: dbmc1 + Worker Node: + 10.0.10.58 + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-08-27T03:43:52Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-08-27T03:58:18Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml index 04a0d7c5..9a62a7b2 100644 --- a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml @@ -1,132 +1,120 @@ -# API version of the custom resource definition +# API version of the OracleRestart Custom Resource apiVersion: database.oracle.com/v4 -# Kind of custom resource — OracleRestart for single-instance DB with ASM +# Resource type being created kind: OracleRestart -# Metadata section: name and namespace of the resource +# Metadata section to define the resource's name and namespace metadata: - # Name of the OracleRestart custom resource - name: oraclerestart-sample - # Kubernetes namespace where it's deployed - namespace: orestart + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into spec: - # Details about the Oracle instance and where it will be deployed + # Instance details for Oracle Restart instDetails: - # Name of the instance or logical host name - name: dbmc1 - # Path on the host for staging software - hostSwLocation: /scratch/orestart/ - # List of worker node IPs where Oracle will run + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Worker nodes where the instance should be scheduled workerNode: - - 10.0.10.108 + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install envVars: - - name: IGNORE_CRS_PREREQS + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. value: "true" - - name: IGNORE_DB_PREREQS + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. value: "true" - # Port used for ONS (Oracle Notification Service) - onsTargetPort: 30200 - # Kubernetes NodePort service details for listener access - nodePortSvc: - - svcType: nodeport # Service type exposed via NodePort - name: dbmc1 # Name of the service - - # Port mappings for Oracle listener - portMappings: - - port: 1521 # Port used inside the container - targetPort: 1521 # Target port for the service - protocol: TCP # Protocol used - nodePort: 30007 # Exposed NodePort on Kubernetes - # ASM disk configuration — list of disks and sizes + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # ASM (Automatic Storage Management) storage configuration asmStorageDetails: disksBySize: - - storageSizeInGb: 50 # Size of each disk group in GB - diskNames: # Physical disk device paths - - /dev/oracleoci/oraclevdd - - /dev/oracleoci/oraclevde - - /dev/oracleoci/oraclevdy - - # SSH key secret used for secure access during automation or install + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 + - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 + - /dev/disk/by-partlabel/asm-disk3 # ASM disk device path 3 + - /dev/disk/by-partlabel/asm-disk4 # ASM disk device path 4 + autoUpdate: "true" + + # SSH key secret used for remote access and automation sshKeySecret: - name: ssh-key-secret # Name of the secret containing SSH keys - privKeySecretName: ssh-privkey # Private key name in the secret - pubKeySecretName: ssh-pubkey # Public key name in the secret + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret - # Database user secret containing wallet and credentials + # Secret containing DB user credentials dbSecret: - name: db-user-pass-pkutl # Name of the secret with DB credentials - keyFileName: key.pem # Key file name in the secret - pwdFileName: pwdfile.enc # Password file (encrypted) + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret - # Container image for Oracle software - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0803 + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry - # Always pull the latest image - imagePullPolicy: Always + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent - # Optional: Service account name, required for OpenShift due to SCC enforcement - # serviceAccountName: oraclerestart + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes - # Configuration for the PDB service inside the DB + # Service-specific database name and related details serviceDetails: - name: soepdb # Name of the PDB service + name: soepdb # Name of the Oracle service (pluggable DB) - # Resource limits and requests for CPU and memory + # Resource requests and limits for memory and CPU resources: requests: - memory: "16Gi" # Minimum guaranteed memory - cpu: "2" # Minimum guaranteed CPU + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required limits: - memory: "16Gi" # Max memory limit - cpu: "2" # Max CPU limit + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed - # Security-related kernel parameters required by Oracle + # Kernel-level system control parameters for Oracle securityContext: sysctls: - - name: kernel.shmall # Total shared memory (pages) + - name: kernel.shmall value: "2097152" - - name: kernel.sem # Semaphore settings + - name: kernel.sem value: "250 32000 100 128" - - name: kernel.shmmax # Max size of one shared memory segment (bytes) + - name: kernel.shmmax value: "8589934592" - - name: kernel.shmmni # Max number of shared memory segments + - name: kernel.shmmni value: "4096" - # Oracle Grid and Database configuration parameters + # Configuration parameters for Oracle Grid and Database homes configParams: - gridHome: "/u01/app/19c/grid" # ORACLE_HOME for Grid Infrastructure - gridBase: "/u01/app/grid" # Base location for Grid - dbHome: "/u01/app/oracle/product/19c/dbhome_1" # ORACLE_HOME for Database - dbBase: "/u01/app/oracle" # Base location for Database - - # ASM disk devices for CRS/ASM configuration - crsAsmDeviceList: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde,/dev/oracleoci/oraclevdy - - # Oracle Inventory location - inventory: "/u01/app/oraInventory" - - # Software zip files to be staged and extracted during install - gridSwZipFile: "grid_home.zip" - dbSwZipFile: "db_home.zip" - oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" - - # Memory and process settings for the database - sgaSize: "3G" # Shared Global Area size - pgaSize: "1G" # Program Global Area size - processes: 2000 # Max number of Oracle processes - cpuCount: 4 # Number of CPUs to configure inside DB - - # Database name (CDB) - dbName: "PORCLCDB" - - # Path to staged Oracle software - hostSwStageLocation: "/scratch/software/19c/19.3.0/" - - # Location for RU patching (Release Update) - ruPatchLocation: "/scratch/software/19c/19.28/37957391" - - # Location where OPatch utility is available - oPatchLocation: "/scratch/software/19c" + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition_autoupdate_false.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition_autoupdate_false.yaml new file mode 100644 index 00000000..d990a1ab --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition_autoupdate_false.yaml @@ -0,0 +1,120 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 + - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 + - /dev/disk/by-partlabel/asm-disk3 # ASM disk device path 3 + - /dev/disk/by-partlabel/asm-disk4 # ASM disk device path 4 + autoUpdate: "false" + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml index 5bb9b617..ee75872d 100644 --- a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml @@ -1,130 +1,116 @@ -# API version of the custom resource definition +# API version of the OracleRestart Custom Resource apiVersion: database.oracle.com/v4 -# Kind of custom resource — OracleRestart for single-instance DB with ASM +# Resource type being created kind: OracleRestart -# Metadata section: name and namespace of the resource +# Metadata section to define the resource's name and namespace metadata: - # Name of the OracleRestart custom resource - name: oraclerestart-sample - # Kubernetes namespace where it's deployed - namespace: orestart + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into spec: - # Details about the Oracle instance and where it will be deployed + # Instance details for Oracle Restart instDetails: - # Name of the instance or logical host name - name: dbmc1 - # Path on the host for staging software - hostSwLocation: /scratch/orestart/ - # List of worker node IPs where Oracle will run + name: dbmc1 # Logical name of the OracleRestart instance + hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME + + # Worker nodes where the instance should be scheduled workerNode: - - 10.0.10.108 + - 10.0.10.58 # IP address of the target node for deployment + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install envVars: - - name: IGNORE_CRS_PREREQS + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. value: "true" - - name: IGNORE_DB_PREREQS + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. value: "true" - # Port used for ONS (Oracle Notification Service) - onsTargetPort: 30200 - # Kubernetes NodePort service details for listener access - nodePortSvc: - - svcType: nodeport # Service type exposed via NodePort - name: dbmc1 # Name of the service - - # Port mappings for Oracle listener - portMappings: - - port: 1521 # Port used inside the container - targetPort: 1521 # Target port for the service - protocol: TCP # Protocol used - nodePort: 30007 # Exposed NodePort on Kubernetes - # ASM disk configuration — list of disks and sizes + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # ASM (Automatic Storage Management) storage configuration asmStorageDetails: disksBySize: - - storageSizeInGb: 50 # Size of each disk group in GB - diskNames: # Physical disk device paths - - /dev/oracleoci/oraclevdd + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + diskNames: + - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 - # SSH key secret used for secure access during automation or install + # SSH key secret used for remote access and automation sshKeySecret: - name: ssh-key-secret # Name of the secret containing SSH keys - privKeySecretName: ssh-privkey # Private key name in the secret - pubKeySecretName: ssh-pubkey # Public key name in the secret + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret - # Database user secret containing wallet and credentials + # Secret containing DB user credentials dbSecret: - name: db-user-pass-pkutl # Name of the secret with DB credentials - keyFileName: key.pem # Key file name in the secret - pwdFileName: pwdfile.enc # Password file (encrypted) + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret - # Container image for Oracle software - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0803 + # Image for Oracle Restart container + image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry - # Always pull the latest image - imagePullPolicy: Always + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent - # Optional: Service account name, required for OpenShift due to SCC enforcement - # serviceAccountName: oraclerestart + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes - # Configuration for the PDB service inside the DB + # Service-specific database name and related details serviceDetails: - name: soepdb # Name of the PDB service + name: soepdb # Name of the Oracle service (pluggable DB) - # Resource limits and requests for CPU and memory + # Resource requests and limits for memory and CPU resources: requests: - memory: "16Gi" # Minimum guaranteed memory - cpu: "2" # Minimum guaranteed CPU + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required limits: - memory: "16Gi" # Max memory limit - cpu: "2" # Max CPU limit + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed - # Security-related kernel parameters required by Oracle + # Kernel-level system control parameters for Oracle securityContext: sysctls: - - name: kernel.shmall # Total shared memory (pages) + - name: kernel.shmall value: "2097152" - - name: kernel.sem # Semaphore settings + - name: kernel.sem value: "250 32000 100 128" - - name: kernel.shmmax # Max size of one shared memory segment (bytes) + - name: kernel.shmmax value: "8589934592" - - name: kernel.shmmni # Max number of shared memory segments + - name: kernel.shmmni value: "4096" - # Oracle Grid and Database configuration parameters + # Configuration parameters for Oracle Grid and Database homes configParams: - gridHome: "/u01/app/19c/grid" # ORACLE_HOME for Grid Infrastructure - gridBase: "/u01/app/grid" # Base location for Grid - dbHome: "/u01/app/oracle/product/19c/dbhome_1" # ORACLE_HOME for Database - dbBase: "/u01/app/oracle" # Base location for Database - - # ASM disk devices for CRS/ASM configuration - crsAsmDeviceList: /dev/oracleoci/oraclevdd - - # Oracle Inventory location - inventory: "/u01/app/oraInventory" - - # Software zip files to be staged and extracted during install - gridSwZipFile: "grid_home.zip" - dbSwZipFile: "db_home.zip" - oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" - - # Memory and process settings for the database - sgaSize: "3G" # Shared Global Area size - pgaSize: "1G" # Program Global Area size - processes: 2000 # Max number of Oracle processes - cpuCount: 4 # Number of CPUs to configure inside DB - - # Database name (CDB) - dbName: "PORCLCDB" - - # Path to staged Oracle software - hostSwStageLocation: "/scratch/software/19c/19.3.0/" - - # Location for RU patching (Release Update) - ruPatchLocation: "/scratch/software/19c/19.28/37957391" - - # Location where OPatch utility is available - oPatchLocation: "/scratch/software/19c" + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1 # Comma-separated list of ASM device files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/orestart_rupatch_object.txt b/docs/oraclerestart/provisioning/orestart_rupatch_object.txt index f5d4344b..c2868d6b 100644 --- a/docs/oraclerestart/provisioning/orestart_rupatch_object.txt +++ b/docs/oraclerestart/provisioning/orestart_rupatch_object.txt @@ -1,28 +1,28 @@ -orestart/ $ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart Name: oraclerestart-sample Namespace: orestart Labels: Annotations: OracleRestarts.database.oracle.com/old-spec: - {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"nodePortSvc":[{"name":"dbmc1","svcType"... + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-07-23T18:13:54Z + Creation Timestamp: 2025-08-20T05:11:47Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 10086875 - UID: f33a787c-9414-4ed9-9233-839992d185b1 + Resource Version: 225105545 + UID: 6da86e89-0c6b-4c4d-aa14-c04c72071388 Spec: Asm Storage Details: Disks By Size: Disk Names: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 Storage Size In Gb: 50 Config Params: Cpu Count: 4 - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -37,14 +37,13 @@ Spec: Grid Home: /u01/app/19c/grid Grid Response File: Grid Sw Zip File: grid_home.zip - Host Sw Stage Location: /scratch/software/19c/19.3.0/ + Host Sw Stage Location: /scratch/software/stage Inventory: /u01/app/oraInventory - O Patch Location: /scratch/software/19c/ + O Patch Location: /scratch/software/opatch O Patch Sw Zip File: p6880880_190000_Linux-x86-64.zip Pga Size: 1G Processes: 2000 - Ru Folder Name: 37957391 - Ru Patch Location: /scratch/software/19c/19.28/ + Ru Patch Location: /scratch/software/ru_patch/37957391 Sga Size: 3G Sw Mount Location: /u01 Db Secret: @@ -53,13 +52,13 @@ Spec: Name: db-user-pass-pkutl Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc - Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0723 + Image: localhost/oracle/database-orestart:19.3.0-slim Image Pull Policy: Always Inst Details: Host Sw Location: /scratch/orestart/ Name: dbmc1 Node Port Svc: - Name: dbmc1 + Name: dbmc1-service-nodeport Svc Type: nodeport Port Mappings: Node Port: 30007 @@ -67,7 +66,7 @@ Spec: Protocol: TCP Target Port: 1521 Worker Node: - 10.0.10.108 + 10.0.10.58 Resources: Limits: Cpu: 2 @@ -98,34 +97,32 @@ Status: Node Details: Instance State: OPEN Pod State: AVAILABLE - Cluster State: NOT HEALTHY + Cluster State: GI_NOT_INSTALLED_OR_CONFIGURED Mounted Devices: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde + /dev/disk/by-partlabel/asm-disk1 + /dev/disk/by-partlabel/asm-disk2 State: AVAILABLE Asm Details: Diskgroup: Disks: - /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Name: DATA Redundancy: EXTERN - Client Etc Host: - 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local Conditions: - Last Transition Time: 2025-07-23T18:26:09Z + Last Transition Time: 2025-08-20T06:23:57Z Message: oracle restart database is in a restricted state: PROVISIONING Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued - Last Transition Time: 2025-07-23T18:29:18Z + Last Transition Time: 2025-08-20T23:52:35Z Message: no reconcile errors Observed Generation: 1 Reason: LastReconcileCycleCompleted Status: True Type: ReconcileComplete Config Params: - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -146,13 +143,34 @@ Status: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb Inst Details: - Name: + Name: Pdb Connect String: dbmc1-0:1521/ORCLPDB - Release Update: 19.3.0.0.0 + Release Update: 19.28.0.0.0 Role: PRIMARY Service Details: Name: soepdb Svc State: service soepdb is running State: AVAILABLE -Events: \ No newline at end of file +Events: + + + +# When the 19.28 RU Patch 37957391 is applied to both the GI HOME and RDBMS HOME, you can check the status of the patches applied once the deployment is completed: + +[grid@dbmc1-0 ~]$ $ORACLE_HOME/OPatch/opatch lspatches +37962946;OCW RELEASE UPDATE 19.28.0.0.0 (37962946) +37962938;ACFS RELEASE UPDATE 19.28.0.0.0 (37962938) +37960098;Database Release Update : 19.28.0.0.250715 (37960098) +37954209;TOMCAT RELEASE UPDATE 19.0.0.0.0 (37954209) +36758186;DBWLM RELEASE UPDATE 19.0.0.0.0 (36758186) + +OPatch succeeded. + + +[oracle@dbmc1-0 ~]$ $ORACLE_HOME/OPatch/opatch lspatches +37962946;OCW RELEASE UPDATE 19.28.0.0.0 (37962946) +37960098;Database Release Update : 19.28.0.0.250715 (37960098) + +OPatch succeeded. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt b/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt index acf97279..ab94bb8e 100644 --- a/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt +++ b/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt @@ -1,36 +1,28 @@ -orestart/ $ kubectl get pvc -n orestart -NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE -asm-pvc-disk-0-oraclerestart-sample Bound csi-4afa96ce-0222-4108-9202-6dea85fde52a 50Gi RWO oci-bv 24s -asm-pvc-disk-1-oraclerestart-sample Bound csi-b7a2cd8b-972f-4244-b047-80db0c4132f1 50Gi RWO oci-bv 24s -hostsw-stage-pvc Bound hostsw-stage-pv 50Gi RWX hostsw-stage-class 15h -rupatch-location-pvc Bound rupatch-location-pv 50Gi RWX rupatch-location-class 15h -orestart/ $ - -orestart/ $ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart Name: oraclerestart-sample Namespace: orestart Labels: Annotations: OracleRestarts.database.oracle.com/old-spec: - {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.108"],"pvcName":{"hostsw-stage-pvc":"/scratch/... + {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":300,"workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nodeport","svc... API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-07-26T05:49:37Z + Creation Timestamp: 2025-08-21T22:09:44Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 11192638 - UID: 85a6418a-bac0-4808-99d5-8d2dc48ee836 + Resource Version: 225580366 + UID: 3ce94373-a848-4b0c-8191-55783f146b8a Spec: Asm Storage Details: Disks By Size: Disk Names: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde + /dev/asm-disk1 + /dev/asm-disk2 Storage Size In Gb: 50 Config Params: Cpu Count: 4 - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -44,38 +36,39 @@ Spec: Grid Base: /u01/app/grid Grid Home: /u01/app/19c/grid Grid Response File: - Grid Sw Zip File: grid_home.zip - Host Sw Stage Location: hostsw-stage-pvc - Inventory: /u01/app/oraInventory - Pga Size: 1G - Processes: 2000 - Ru Patch Location: rupatch-location-pvc - Sga Size: 3G - Sw Mount Location: /u01 + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /stage/software/19c/19.3.0 + Inventory: /u01/app/oraInventory + O Patch Location: /stage/software/19c + O Patch Sw Zip File: p6880880_190000_Linux-x86-64.zip + Pga Size: 1G + Processes: 2000 + Ru Patch Location: /stage/software/19c/19.28/37957391 + Sga Size: 3G + Sw Mount Location: /u01 + Sw Stage Pvc: pv-stage-vol-claim + Sw Stage Pvc Mount Location: /stage Db Secret: Key File Mount Location: /mnt/.dbsecrets Key File Name: key.pem Name: db-user-pass-pkutl Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc - Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0725 + Image: localhost/oracle/database-orestart:19.3.0-slim Image Pull Policy: Always Inst Details: - Host Sw Location: /scratch/orestart/ - Name: dbmc1 + Name: dbmc1 Node Port Svc: - Name: dbmc1 + Name: dbmc1-service-nodeport Svc Type: nodeport Port Mappings: - Node Port: 30007 - Port: 1521 - Protocol: TCP - Target Port: 1521 - Pvc Name: - Hostsw - Stage - Pvc: /scratch/stage/software - Rupatch - Location - Pvc: /scratch/software/19c/19.28/ + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Sw Loc Storage Size In Gb: 300 Worker Node: - 10.0.10.108 + 10.0.10.58 Resources: Limits: Cpu: 2 @@ -100,43 +93,39 @@ Spec: Name: ssh-key-secret Priv Key Secret Name: ssh-privkey Pub Key Secret Name: ssh-pubkey + Storage Class: oci-bv Status: Oracle Restart Nodes: Name: dbmc1-0 Node Details: Instance State: OPEN Pod State: AVAILABLE - Cluster State: NOT HEALTHY + Cluster State: HEALTHY Mounted Devices: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde - Pvc Name: - Hostsw - Stage - Pvc: /scratch/stage/software - Rupatch - Location - Pvc: /scratch/software/19c/19.28/ - State: AVAILABLE + /dev/asm-disk1 + /dev/asm-disk2 + State: AVAILABLE Asm Details: Diskgroup: Disks: - /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + /dev/asm-disk1,/dev/asm-disk2 Name: DATA Redundancy: EXTERN - Client Etc Host: - 150.136.178.0 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local Conditions: - Last Transition Time: 2025-07-26T05:56:51Z + Last Transition Time: 2025-08-22T01:09:05Z Message: oracle restart database is in a restricted state: PROVISIONING Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued - Last Transition Time: 2025-07-26T06:27:52Z + Last Transition Time: 2025-08-22T02:12:50Z Message: no reconcile errors Observed Generation: 1 Reason: LastReconcileCycleCompleted Status: True Type: ReconcileComplete Config Params: - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -157,13 +146,53 @@ Status: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb Inst Details: - Name: + Name: Pdb Connect String: dbmc1-0:1521/ORCLPDB - Release Update: 19.3.0.0.0 + Release Update: 19.28.0.0.0 Role: PRIMARY Service Details: Name: soepdb Svc State: service soepdb is running State: AVAILABLE -Events: \ No newline at end of file +Events: + + + +$ kubectl get pvc -n orestart +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-disk-0-oraclerestart-sample Bound csi-ce9f7e26-d889-40bc-97c0-1e16bc63d949 50Gi RWO oci-bv 4h3m +asm-pvc-disk-1-oraclerestart-sample Bound csi-35c89048-7aba-460e-ad19-a0c1356f75a3 50Gi RWO oci-bv 4h3m +dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-62f17e8a-1d0b-43a8-9d82-9ae81648ca07 300Gi RWO oci-bv 4h3m +pv-stage-vol-claim Bound pv-stage-vol1 500Gi ROX fss-dyn-storage 8h + + + +[root@dbmc1-0 rac-work-dir]# ls -lrt /stage/software/19c/ +total 71681 +drwxr-xr-x. 3 root root 3 Aug 19 01:20 19.28 +-rw-r--r--. 1 root root 72539776 Aug 19 01:21 p6880880_190000_Linux-x86-64.zip +drwxr-xr-x. 2 root root 3 Aug 19 03:56 19.3.0 +[root@dbmc1-0 rac-work-dir]# +[root@dbmc1-0 rac-work-dir]# ls -lrt /stage/software/19c/19.3.0/ +total 6774784 +-rw-r--r--. 1 root root 3059705302 Aug 19 01:10 db_home.zip +-rw-r--r--. 1 root root 2889184573 Aug 19 01:11 grid_home.zip +[root@dbmc1-0 rac-work-dir]# ls -lrt /stage/software/19c/19.28/ +total 3851265 +drwxr-xr-x. 8 root root 9 May 28 05:03 37957391 +-rw-rw-r--. 1 root root 2921864 May 28 05:41 PatchSearch.xml +-rw-r--r--. 1 root root 3940126990 Aug 19 01:14 p37957391_190000_Linux-x86-64.zip +[root@dbmc1-0 rac-work-dir]# ls -lrt /stage/software/19c/19.28/37957391/ +total 171 +drwxr-xr-x. 5 root root 4 May 28 05:03 37962946 +drwxr-xr-x. 5 root root 4 May 28 05:04 37962938 +drwxr-xr-x. 5 root root 5 May 28 05:04 37960098 +drwxr-xr-x. 4 root root 3 May 28 05:04 37954209 +drwxr-xr-x. 4 root root 3 May 28 05:04 36758186 +drwxr-xr-x. 2 root root 13 May 28 05:08 automation +-rwxr-xr-x. 1 root root 0 May 28 05:08 README.txt +-rwxr-xr-x. 1 root root 152522 May 28 05:18 README.html +-rwxr-xr-x. 1 root root 5824 May 28 05:35 bundle.xml +[root@dbmc1-0 rac-work-dir]# \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_storage_object.txt b/docs/oraclerestart/provisioning/orestart_storage_class_object.txt similarity index 60% rename from docs/oraclerestart/provisioning/orestart_storage_object.txt rename to docs/oraclerestart/provisioning/orestart_storage_class_object.txt index eb21b853..596dd480 100644 --- a/docs/oraclerestart/provisioning/orestart_storage_object.txt +++ b/docs/oraclerestart/provisioning/orestart_storage_class_object.txt @@ -1,28 +1,28 @@ -kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart Name: oraclerestart-sample Namespace: orestart Labels: Annotations: OracleRestarts.database.oracle.com/old-spec: - {"instDetails":{"name":"dbmc1","hostSwLocation":"/var/scratch/orestart/","workerNode":["02-00-17-01-73-a0"],"nodePortSvc":[{"name":"dbmc1-... + {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-07-22T07:13:32Z + Creation Timestamp: 2025-08-21T00:19:11Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 38605572 - UID: 0585a6e0-1dea-4212-b8e2-919e3f84f6a8 + Resource Version: 225180605 + UID: ad9bc196-6448-47e7-9616-ad3144f393ca Spec: Asm Storage Details: Disks By Size: Disk Names: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde + /dev/asm-disk1 + /dev/asm-disk2 Storage Size In Gb: 50 Config Params: Cpu Count: 4 - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -37,7 +37,7 @@ Spec: Grid Home: /u01/app/19c/grid Grid Response File: Grid Sw Zip File: grid_home.zip - Host Sw Stage Location: /var/scratch/software/stage/ + Host Sw Stage Location: /scratch/software/stage Inventory: /u01/app/oraInventory Pga Size: 1G Processes: 2000 @@ -49,10 +49,10 @@ Spec: Name: db-user-pass-pkutl Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc - Image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0703 + Image: localhost/oracle/database-orestart:19.3.0-slim Image Pull Policy: Always Inst Details: - Host Sw Location: /var/scratch/orestart/ + Host Sw Location: /scratch/orestart/ Name: dbmc1 Node Port Svc: Name: dbmc1-service-nodeport @@ -63,7 +63,7 @@ Spec: Protocol: TCP Target Port: 1521 Worker Node: - 02-00-17-01-73-a0 + 10.0.10.58 Resources: Limits: Cpu: 2 @@ -73,15 +73,14 @@ Spec: Memory: 16Gi Security Context: Sysctls: - Name: kernel.shmall - Value: 2097152 - Name: kernel.sem - Value: 250 32000 100 128 - Name: kernel.shmmax - Value: 8589934592 - Name: kernel.shmmni - Value: 4096 - Service Account Name: oraclerestart + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 Service Details: Name: soepdb Ssh Key Secret: @@ -89,7 +88,7 @@ Spec: Name: ssh-key-secret Priv Key Secret Name: ssh-privkey Pub Key Secret Name: ssh-pubkey - Storage Class: ocs-storagecluster-ceph-rbd + Storage Class: oci-bv Status: Oracle Restart Nodes: Name: dbmc1-0 @@ -98,32 +97,30 @@ Status: Pod State: AVAILABLE Cluster State: HEALTHY Mounted Devices: - /dev/oracleoci/oraclevdd - /dev/oracleoci/oraclevde + /dev/asm-disk1 + /dev/asm-disk2 State: AVAILABLE Asm Details: Diskgroup: Disks: - /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + /dev/asm-disk1,/dev/asm-disk2 Name: DATA Redundancy: EXTERN - Client Etc Host: - 10.0.26.45 dbmc1-0.rac.svc.cluster.local dbmc1-0-vip.rac.svc.cluster.local OracleRestartNode-scan.rac.svc.cluster.local Conditions: - Last Transition Time: 2025-07-22T07:26:46Z + Last Transition Time: 2025-08-21T00:32:23Z Message: oracle restart database is in a restricted state: PROVISIONING Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued - Last Transition Time: 2025-07-22T08:41:30Z + Last Transition Time: 2025-08-21T04:04:12Z Message: no reconcile errors Observed Generation: 1 Reason: LastReconcileCycleCompleted Status: True Type: ReconcileComplete Config Params: - Crs Asm Device List: /dev/oracleoci/oraclevdd,/dev/oracleoci/oraclevde + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 Crs Asm Disk Dg: +DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle @@ -144,8 +141,9 @@ Status: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb Inst Details: - Name: + Name: Pdb Connect String: dbmc1-0:1521/ORCLPDB Release Update: 19.28.0.0.0 Role: PRIMARY @@ -156,11 +154,12 @@ Status: Events: -[root@ldboper-bastian orestart]# kubectl get pvc -n orestart -NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE -asm-pvc-disk-0-oraclerestart-sample Bound asm-pv-disk-1-oraclerestart-sample 50Gi RWO ocs-storagecluster-ceph-rbd 6m21s -asm-pvc-disk-1-oraclerestart-sample Bound asm-pv-disk-0-oraclerestart-sample 50Gi RWO ocs-storagecluster-ceph-rbd 6m21s -[root@ldboper-bastian orestart]# kubectl get pv -n orestart -NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE -asm-pv-disk-0-oraclerestart-sample 50Gi RWO Retain Bound orestart/asm-pvc-disk-1-oraclerestart-sample ocs-storagecluster-ceph-rbd 6m28s -asm-pv-disk-1-oraclerestart-sample 50Gi RWO Retain Bound orestart/asm-pvc-disk-0-oraclerestart-sample ocs-storagecluster-ceph-rbd 6m28s \ No newline at end of file +$ kubectl get pvc -n orestart +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-disk-0-oraclerestart-sample Bound csi-a2bcf917-dffb-46aa-b0b9-7fbb2b9499b7 50Gi RWO oci-bv 3h45m +asm-pvc-disk-1-oraclerestart-sample Bound csi-e5abda8e-abde-4399-9e97-d72cc3bc3659 50Gi RWO oci-bv 3h45m + +$ kubectl get pv -n orestart +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE +csi-a2bcf917-dffb-46aa-b0b9-7fbb2b9499b7 50Gi RWO Delete Bound orestart/asm-pvc-disk-0-oraclerestart-sample oci-bv 3h45m +csi-e5abda8e-abde-4399-9e97-d72cc3bc3659 50Gi RWO Delete Bound orestart/asm-pvc-disk-1-oraclerestart-sample oci-bv 3h45m \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_storage_class_object_after_sw_home_resize.txt b/docs/oraclerestart/provisioning/orestart_storage_class_object_after_sw_home_resize.txt new file mode 100644 index 00000000..2f8e35b2 --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_storage_class_object_after_sw_home_resize.txt @@ -0,0 +1,193 @@ +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":400,"workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","value":"tru... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-09-10T23:21:18Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 2 + Resource Version: 234059474 + UID: 0525c7ac-e02f-4471-8e96-40cd5d4645ff +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/asm-disk1 + /dev/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Enable Ons: enable + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent + Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Name: dbmc1 + Sw Loc Storage Size In Gb: 400 + Worker Node: + 10.0.10.58 + Lb Service: + Node Port Svc: + Name: dbmc1-service-nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Node Port: 30200 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: nodeport + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey + Storage Class: oci-bv +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/asm-disk1 + /dev/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/asm-disk1,/dev/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-09-10T23:34:06Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-09-10T23:49:53Z + Message: reconcile completed successfully + Observed Generation: 2 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: Pending + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + +$ kubectl get pvc -n orestart +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-disk-0-oraclerestart-sample Bound csi-851752b8-35e7-4117-a054-0fde0cd78fb6 50Gi RWO oci-bv 29m +asm-pvc-disk-1-oraclerestart-sample Bound csi-51633544-2e93-4067-a93d-4ba66da7117e 50Gi RWO oci-bv 29m +dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-8b07994d-166f-459d-ac4f-17a4fe52d69b 400Gi RWO oci-bv 29m + + + +[root@dbmc1-0 rac-work-dir]# df -h +Filesystem Size Used Avail Use% Mounted on +overlay 489G 25G 465G 6% / +tmpfs 64M 0 64M 0% /dev +tmpfs 46G 0 46G 0% /sys/fs/cgroup +tmpfs 45G 4.1G 41G 9% /etc/hostname +tmpfs 46G 112K 46G 1% /run +/dev/sdn 393G 14G 380G 4% /u01 +tmpfs 83G 8.0K 83G 1% /mnt/.ssh +tmpfs 83G 8.0K 83G 1% /mnt/.dbsecrets +tmpfs 83G 1.1G 82G 2% /dev/shm +/dev/mapper/ocivolume-root 489G 25G 465G 6% /etc/hosts +/dev/sdb1 196G 31G 156G 17% /scratch/software/stage +tmpfs 46G 0 46G 0% /run/lock +tmpfs 46G 12M 46G 1% /tmp +tmpfs 46G 32M 46G 1% /var/log/journal +tmpfs 46G 0 46G 0% /proc/acpi +tmpfs 46G 0 46G 0% /proc/scsi +tmpfs 46G 0 46G 0% /sys/firmware +tmpfs 46G 0 46G 0% /sys/fs/selinux \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_storage_class_object_before_sw_home_resize.txt b/docs/oraclerestart/provisioning/orestart_storage_class_object_before_sw_home_resize.txt new file mode 100644 index 00000000..b2cb5a3e --- /dev/null +++ b/docs/oraclerestart/provisioning/orestart_storage_class_object_before_sw_home_resize.txt @@ -0,0 +1,194 @@ +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":300,"workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","value":"tru... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-09-10T23:21:18Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 234057060 + UID: 0525c7ac-e02f-4471-8e96-40cd5d4645ff +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/asm-disk1 + /dev/asm-disk2 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pga Size: 1G + Processes: 2000 + Sga Size: 3G + Sw Mount Location: /u01 + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Enable Ons: enable + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent + Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Name: dbmc1 + Sw Loc Storage Size In Gb: 300 + Worker Node: + 10.0.10.58 + Lb Service: + Node Port Svc: + Name: dbmc1-service-nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Node Port: 30200 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: nodeport + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey + Storage Class: oci-bv +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/asm-disk1 + /dev/asm-disk2 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/asm-disk1,/dev/asm-disk2 + Name: DATA + Redundancy: EXTERN + Conditions: + Last Transition Time: 2025-09-10T23:34:06Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-09-10T23:41:51Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 + Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg Redundancy: external + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +DATA + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: Pending + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + +$ kubectl get pvc -n orestart +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-disk-0-oraclerestart-sample Bound csi-851752b8-35e7-4117-a054-0fde0cd78fb6 50Gi RWO oci-bv 23m +asm-pvc-disk-1-oraclerestart-sample Bound csi-51633544-2e93-4067-a93d-4ba66da7117e 50Gi RWO oci-bv 23m +dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-8b07994d-166f-459d-ac4f-17a4fe52d69b 300Gi RWO oci-bv 23m + + + + +[root@dbmc1-0 rac-work-dir]# df -h +Filesystem Size Used Avail Use% Mounted on +overlay 489G 25G 465G 5% / +tmpfs 64M 0 64M 0% /dev +tmpfs 46G 0 46G 0% /sys/fs/cgroup +tmpfs 45G 4.1G 41G 9% /etc/hostname +tmpfs 46G 96K 46G 1% /run +/dev/sdn 295G 14G 281G 5% /u01 +tmpfs 83G 8.0K 83G 1% /mnt/.ssh +tmpfs 83G 8.0K 83G 1% /mnt/.dbsecrets +tmpfs 83G 1.1G 82G 2% /dev/shm +/dev/mapper/ocivolume-root 489G 25G 465G 5% /etc/hosts +/dev/sdb1 196G 31G 156G 17% /scratch/software/stage +tmpfs 46G 0 46G 0% /run/lock +tmpfs 46G 8.2M 46G 1% /tmp +tmpfs 46G 24M 46G 1% /var/log/journal +tmpfs 46G 0 46G 0% /proc/acpi +tmpfs 46G 0 46G 0% /proc/scsi +tmpfs 46G 0 46G 0% /sys/firmware +tmpfs 46G 0 46G 0% /sys/fs/selinux \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md index ccc7cda1..67a8ccba 100644 --- a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md @@ -1,5 +1,6 @@ # Prerequisites for running Oracle Restart Database Controller * [Kubernetes Cluster Requirements](#kubernetes-cluster-requirements) + * [Mandatory roles and privileges requirements for Oracle Restart Database Controller](#mandatory-roles-and-privileges-requirements-for-oracle-restart-database-controller) * [Prerequisites for Oracle Restart on OKE](#prerequisites-for-oracle-restart-on-oke) * [Preparing to Install Oracle Restart on OKE](#preparing-to-install-oracle-restart-on-oke) * [Worker Node Preparation for Oracle Restart on OKE](#worker-node-preparation-for-oracle-restart-on-oke) @@ -8,7 +9,7 @@ + [Set up SELinux Module on Worker Nodes](#set-up-selinux-module-on-worker-nodes) * [Create a namespace for the Oracle Restart Setup](#create-a-namespace-for-the-oracle-restart-setup) * [Install Cert Manager and Setup Access Permissions](#install-cert-manager-and-setup-access-permissions) - * [OpenShift Security Context Constraints](#openshift-security-context-constraints) + * [Additional requirements for OpenShift Security Context Constraints](#additional-requirements-for-openshift-security-context-constraints) * [Deploy Oracle Database Operator](#deploy-oracle-database-operator) * [Oracle Restart Database Slim Image](#oracle-restart-database-slim-image) * [Create a Kubernetes secret for the Oracle Restart Database installation owner for the Oracle Restart Database Deployment](#create-a-kubernetes-secret-for-the-oracle-restart-database-installation-owner-for-the-oracle-restart-database-deployment) @@ -17,10 +18,23 @@ To deploy Oracle Restart Database using Oracle Restart Database Controller in Or If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will require: - #### Kubernetes Cluster Requirements + ## Kubernetes Cluster Requirements You must ensure that your Kubernetes Cluster meets the necessary requirements for Oracle Restart Database deployment. The minimum required OKE cluster version is 1.33.1 or higher. Refer documentation for details [Oracle Kubernetes Engine](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm) cluster. - #### Prerequisites for Oracle Restart on OKE + ## Mandatory roles and privileges requirements for Oracle Restart Database Controller + + Oracle Sharding Database Controller uses Kubernetes objects such as :- + + | Resources | Verbs | + | --- | --- | + | Pods | create delete get list patch update watch | + | Containers | create delete get list patch update watch | + | PersistentVolumeClaims | create delete get list patch update watch | + | Services | create delete get list patch update watch | + | Secrets | create delete get list patch update watch | + | Events | create patch | + + ## Prerequisites for Oracle Restart on OKE Before proceeding with the Oracle Restart Database deployment, ensure that you have completed the necessary prerequisites on your OKE cluster. This includes setting up the required infrastructure and configuring the necessary components. To use these instructions, you should have background knowledge of the technology and operating system. * Verify that all necessary dependencies are installed and up-to-date. You should be familiar with the following technologies: * Linux @@ -31,7 +45,7 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will * Oracle Automatic Storage Management (Oracle ASM) - #### Preparing to Install Oracle Restart on OKE + ## Preparing to Install Oracle Restart on OKE To prepare for the Oracle Restart Database installation, follow these steps: * Ensure that your OKE cluster is properly configured and meets the necessary requirements. Each pod that you deploy as part of your cluster must satisfy the minimum hardware requirements of the Oracle Restart Database and Oracle Grid Infrastructure software. If you are planning to install Oracle Grid Infrastructure and Oracle Restart database software on data volumes exposed from your environment, then you must have at least 50 GB space allocated for the Oracle Restart on OKE Pod. @@ -48,14 +62,14 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will * Oracle Linux for Operator, control plane, and Worker nodes on Oracle Linux 8 (Linux-x86-64) Update 10 or later updates - #### Worker Node Preparation for Oracle Restart on OKE + ## Worker Node Preparation for Oracle Restart on OKE * When configuring your Worker Nodes, follow these guidelines, and see the configuration Oracle used for testing. Each OKE worker node must have sufficient resources to support the intended number of Oracle Database Pods, each of which must meet at least the minimum requirements for Oracle Grid Infrastructure servers hosting an Oracle Database node. - * The Oracle Database Pods in this example configuration were created on the machine worker-1 for Oracle Restart: + * The Oracle Database Pods in this example configuration were created on the machine `10.0.10.58` for Oracle Restart: - Oracle Database Node - - Worker Node: worker-1 + - Worker Node: 10.0.10.58 - Container Pod: dbmc1-0 * Worker node has the following configuration: @@ -78,13 +92,13 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will - **If you want to use the devices from the worker nodes for ASM storage, you will need to mark any default StorageClass as non-default in your Kubernetes Cluster.** - ##### Download Oracle Grid Infrastructure and Oracle Database Software + ### Download Oracle Grid Infrastructure and Oracle Database Software You need to download the Oracle Grid Infrastructure and Oracle Database software and stage it on the worker nodes. The Oracle Restart Database Controller will handle mounting the software inside the Pod. * The Oracle Database Container does not contain any Oracle software binaries. Download the following software from the [Oracle Technology Network](https://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html). - Oracle Grid Infrastructure 19c (19.28) for Linux x86-64 - Oracle Database 19c (19.28) for Linux x86-64 - ##### Prepare the Worker Node for Oracle Restart Deployment + ### Prepare the Worker Node for Oracle Restart Deployment To prepare the worker node for Oracle Restart Database deployment, follow these steps: Before you install Oracle Restart inside the OKE Pods, you must update the system configuration. @@ -103,11 +117,11 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will * `# sysctl -a` * `# sysctl –p` 4. Verify that the swap memory is disabled by running: - ```sh - [root@worker-1~]# free -m - ..... - Swap: 0 0 0 - ``` + ```sh + # free -m + ..... + Swap: 0 0 0 + ``` 5. Enable kernel parameters at the Kubelet level, so that kernel parameters can be set at the Pod level. This is a one-time activity. * In the `/etc/systemd/system/kubelet.service.d/00-default.conf` file of OKE Worker nodes, add below environment variable: ```txt @@ -130,20 +144,25 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will * On worker node: + `# mkdir -p /scratch/orestart/` + `# mkdir -p /scratch/software/stage` + + For the case where you are installing Oracle Base Release with RU Patch, create the required mount points for Base Release Software, for the location to unzip the RU Patch etc: + * For Example, for Release 19c with 19.28 RU, on worker node: + + `# mkdir -p /stage/software/19c/19.3.0` + + `# mkdir -p /stage/software/19c/19.28` 7. Download the Oracle Grid Infrastructure and Oracle RDBMS Software .zip files. Copy those files to the worker node at the staging location `/scratch/software/stage/` - ##### Set up SELinux Module on Worker Nodes + ### Set up SELinux Module on Worker Nodes Traditional Unix security uses discretionary access control (DAC). SELinux is an example of mandatory access control. SELinux restricts many commands from the Oracle Restart Pod that are not allowed to run, which results in permission denied errors. To avoid such errors, you must create an SELinux policy package to allow certain commands. To set up the SELinux module on the worker node, follow these steps as `root` user to create an SELinux policy package on the worker node: 1. Verify that SELinux is enabled on the Worker node. For example: ```sh - [root@worker-1]# getenforce + # getenforce enforcing ``` - 2. Install the SELinux devel package on the Worker nodes: `[root@worker-1]# dnf install selinux-policy-devel` + 2. Install the SELinux devel package on the Worker nodes: `# dnf install selinux-policy-devel` 3. Create a file `oradb-oke.te` under `/var/opt` on the Worker nodes with the below content: ```sh module oradb-oke 1.0; @@ -176,7 +195,7 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will * `semodule -i oradb-oke.pp` * `semodule -l | grep oradb-oke` 5. Configure the SELinux context for the required worker node directory and files: - * On worker-1: + * On worker node: + `# semanage fcontext -a -t container_file_t /scratch/orestart` + `# sudo restorecon -vF /scratch/orestart` + `# semanage fcontext -a -t container_file_t /scratch/software/stage/grid_home.zip` @@ -184,11 +203,13 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will + `# semanage fcontext -a -t container_file_t /scratch/software/stage/db_home.zip` + `# sudo restorecon -vF /scratch/software/stage/db_home.zip` -**Note**: Change these paths and file names as per location of your environment for setting Oracle Restart and names of the software .zip files. +**Note:** Change these paths and file names as per location of your environment for setting Oracle Restart and names of the software .zip files. + +**Note:** In case of Oracle Base Release and RU Patch software, you will need to run similar commands for the corresponding .zip files. **Note**: To use Oracle Restart Database Controller, ensure that your system is provisioned with a supported Kubernetes release. Refer to the [Release Status Section](../../README.md#release-status). -### Create a namespace for the Oracle Restart Setup +## Create a namespace for the Oracle Restart Setup Create a Kubernetes namespace named `orestart`. All the resources belonging to the Oracle Restart Database will be provisioned in this namespace named `orestart`. For example: @@ -201,7 +222,7 @@ Create a Kubernetes namespace named `orestart`. All the resources belonging to t ``` If you want, you can choose any name for the namespace to deploy Oracle Restart Database. -### Install Cert Manager and Setup Access Permissions +## Install Cert Manager and Setup Access Permissions Before using Oracle Restart Database Controller, ensure you have completed the prerequisites which includes: @@ -210,23 +231,25 @@ Before using Oracle Restart Database Controller, ensure you have completed the p Refer to the section [Prerequisites](../../../README.md#prerequisites) -### OpenShift Security Context Constraints +## Additional requirements for OpenShift Security Context Constraints + +Apart from the same steps listed above for setting up Oracle Restart Database using Oracle Restart Database Controller on an OKE Cluster, there are some additional steps required to setup Oracle Restart Database using Oracle Restart Database Controller on an OpenShift Cluster. OpenShift requires additional Security Context Constraints (SCC) for deploying and managing the `oraclerestarts.database.oracle.com` resource. To create the appropriate SCCs before deploying the `oraclerestarts.database.oracle.com` resource, complete these steps: 1. Apply the file [custom-kubeletconfig.yaml](../../config/samples/orestart/custom-kubeletconfig.yaml) with cluster-admin user privileges. ```sh - oc apply -f custom-kubeletconfig.yaml + oc apply -f custom-kubeletconfig.yaml ``` Watch the worker MCP update: ```sh - watch oc get mcp - # Wait for: UPDATING = False ,UPDATED = True ,DEGRADED = False - oc label mcp worker custom-kubelet=enable-unsafe-sysctls - oc get kubeletconfig - NAME AGE - enable-unsafe-sysctls 10m + watch oc get mcp + # Wait for: UPDATING = False ,UPDATED = True ,DEGRADED = False + oc label mcp worker custom-kubelet=enable-unsafe-sysctls + oc get kubeletconfig + NAME AGE + enable-unsafe-sysctls 10m ``` **Note:** OpenShift recommends that you should not deploy in namespaces starting with `kube`, `openshift` and the `default` namespace. @@ -240,11 +263,11 @@ OpenShift requires additional Security Context Constraints (SCC) for deploying a 3. Apply the file [custom-scc.yaml](../../config/samples/orestart/custom-scc.yaml) with cluster-admin user privileges. ```sh - oc apply -f custom-scc.yaml - oc adm policy add-scc-to-user privileged -z oraclerestart -n orestart + oc apply -f custom-scc.yaml + oc adm policy add-scc-to-user privileged -z oraclerestart -n orestart ``` -### Deploy Oracle Database Operator +## Deploy Oracle Database Operator After you have completed the prerequisite steps, you can install the operator. To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the previous step. @@ -254,27 +277,17 @@ kubectl apply -f oracle-database-operator.yaml For more details, please refer to [Install Oracle DB Operator](../../../README.md#install-oracle-db-operator) -### Oracle Restart Database Slim Image -Choose one of the following deployment options: - - **Use Oracle-Supplied Container Images:** - The Oracle Restart Database Controller uses Oracle Restart Database Slim Image to provision the Oracle Restart Database. - - You can also download the pre-built Oracle Restart Database Slim Image `phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0630`. This image is functionally tested and evaluated with various use cases of Oracle Restart Database on an OKE Kubernetes Cluster. - - You can either download this image and push it to your Container Images Repository, or, if your Kubernetes cluster can reach OCR, you can download this image directly from OCR. - - **OR** +## Oracle Restart Database Slim Image - **Build your own Oracle Restart Database Slim Image:** - You can build this image using instructions provided on Oracle's official OraHub Repositories: - * [Oracle Restart Database Image](https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images) + #### Build your own Oracle Restart Database Slim Image + You can build this image using instructions provided in below documentation: + * [Building Oracle RAC Database Container Slim Image](https://github.com/oracle/docker-images/blob/main/OracleDatabase/RAC/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) After the image is ready, push it to your Container Images Repository, so that you can pull this image during Oracle Restart Database provisioning.. -**Note**: In the Oracle Restart Database provisioning sample .yaml files, we are using Oracle Restart Database slim image available on `phx.ocir.io/intsanjaysingh/db-repo/oracle/database-rac:19.3.0-slim-0630`. +**Note**: In the Oracle Restart Database provisioning sample .yaml files, we are using Oracle Restart Database slim image `localhost/oracle/database-orestart:19.3.0-slim`. -### Create a Kubernetes secret for the Oracle Restart Database installation owner for the Oracle Restart Database Deployment +## Create a Kubernetes secret for the Oracle Restart Database installation owner for the Oracle Restart Database Deployment Create a Kubernetes secret named `db-user-pass` in `orestart` namespace using these steps: [Create Kubernetes Secret](./create_kubernetes_secret_for_db_user.md) diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md index 868db290..0bebaed6 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md @@ -3,25 +3,25 @@ In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. -This example uses `oraclerestart_prov.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: +This example uses `oraclerestart_prov.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* 1 Node for Oracle Restart +* Headless services for Oracle Restart. + * Oracle Database Node hostname. +* Persistent volumes created automatically based on specified disks for Oracle ASM Storage. +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. * Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - In this example, - * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov.yaml` file to point to the container image you have built. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. - Use the file: [oraclerestart_prov.yaml](./oraclerestart_prov.yaml) for this use case as below: 1. Deploy the `oraclerestart_prov.yaml` file: diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_lb.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_lb.md deleted file mode 100644 index 05c08289..00000000 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_lb.md +++ /dev/null @@ -1,44 +0,0 @@ -# Provisioning an Oracle Restart Database with Load Balancer Service Enabled - -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. -The responsefile is generated by the controller based on input parameters specified in the .yaml file. In this use case, we are deploying the Oracle Restart with Load Balancer Service - -This example uses `oraclerestart_prov_lb.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). - * When you are building the image yourself, update the image value in the `oraclerestart_prov_lb.yaml` file to point to the container image you have built. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. - - -Use the file: [oraclerestart_prov_lb.yaml](./oraclerestart_prov_lb.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_lb.yaml` file: - ```sh - kubectl apply -f oraclerestart_prov_lb.yaml - oraclerestart.database.oracle.com/oraclerestart-sample created - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n orestart - - # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" - =============================== - ORACLE DATABASE IS READY TO USE - =============================== - ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_lb_object.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md new file mode 100644 index 00000000..26f00a19 --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md @@ -0,0 +1,47 @@ +# Provisioning an Oracle Restart Database with Load Balancer + +In this use case, the Oracle Grid Infrastructure and Oracle Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the controller based on input parameters specified in the .yaml file. + +In this use case, Oracle Restart is deployed with Load Balancer Service and ou will be able to make a remote database connection using the Load Balancer target port 1521. + +This example uses `oraclerestart_prov_loadbalancer.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + +* 1 Node Oracle Restart +* Headless services for Oracle Restart + * Oracle Restart Node hostname +* Load Balancer Service with target port 1521 +* Persistent volumes created automatically based on specified disks for Oracle ASM storage +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +In this example, + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_loadbalancer.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. + +Use the file: [oraclerestart_prov_loadbalancer.yaml](./oraclerestart_prov_loadbalancer.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_loadbalancer.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_loadbalancer.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_loadbalancer_object.txt) +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md index 23d97944..621a691d 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md @@ -1,28 +1,31 @@ # Provisioning an Oracle Restart Database with NodePort Service -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the controller based on input parameters specified in the .yaml file. -In this use case, we are deploying the Oracle Restart with Node Port Service. +In this use case, the Oracle Grid Infrastructure and Oracle Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the controller based on input parameters specified in the .yaml file. + +In this use case, Oracle Restart is deployed with Node Port Service. A node port exposes the service on a static port on the node IP address and NodePorts are in the 30000-32767 range by default. This example uses `oraclerestart_prov_nodeports.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: * 1 Node Oracle Restart * Headless services for Oracle Restart - * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes + * Oracle Restart Node hostname +* Node Port 30007 mapped to port 1521 for Database Listener +* Persistent volumes created automatically based on specified disks for Oracle ASM storage +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. * Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. In this example, - * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. - Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for this use case as below: 1. Deploy the `oraclerestart_prov_nodeports.yaml` file: diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md deleted file mode 100644 index 71259abb..00000000 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_onsport.md +++ /dev/null @@ -1,44 +0,0 @@ -# Provisioning an Oracle Restart Database with OnsPort Enabled - -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. -The responsefile is generated by the controller based on input parameters specified in the .yaml file. In this use case, we are deploying the Oracle Restart with Node Port Service for the ONS. - -This example uses `oraclerestart_prov_onsport.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * We are using Oracle Restart Database Slim Image `localhost/oracle/database-rac:19.3.0-slim` built using files from [GitHub location](./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image). - * When you are building the image yourself, update the image value in the `oraclerestart_prov_onsport.yaml` file to point to the container image you have built. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. - - -Use the file: [oraclerestart_prov_onsport.yaml](./oraclerestart_prov_onsport.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_onsport.yaml` file: - ```sh - kubectl apply -f oraclerestart_prov_onsport.yaml - oraclerestart.database.oracle.com/oraclerestart-sample created - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n orestart - - # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": - kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" - =============================== - ORACLE DATABASE IS READY TO USE - =============================== - ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_ons_object.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md index 3bffedc4..19313d32 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md @@ -3,24 +3,31 @@ In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. +In this use case, before the installation of Oracle Restart, the GRID HOME and RDBMS HOME are patched with the provided `RU Patch`. + This example uses `oraclerestart_prov_rupatch.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: * 1 Node Oracle Restart * Headless services for Oracle Restart - * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes + * Oracle Restart Node hostname +* Node Port 30007 mapped to port 1521 for Database Listener +* Persistent volumes created automatically based on specified disks for Oracle ASM storage +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. * Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. -* Directory where the **Release Update (RU) patch** has been unzipped as `ruPatchLocation`. This folder **must contain `PatchSearch.xml`**, which is used by the installer to detect and apply the patch. Example: `/scratch/software/19c/19.28/` +* Directory where the **Release Update (RU) patch** has been unzipped is specified by `ruPatchLocation`. + * For Example: To apply the 19.28 RU Patch `37957391`, if you have unzipped the RU Patch .zip file `p37957391_190000_Linux-x86-64.zip` to location `/scratch/software/19c/19.28/` on the worker node, then set this parameter `ruPatchLocation` to value `/scratch/software/ru_patch/37957391`. + * Set the permission on the unzipped RU software directory to be `755` recursively. In this example, - * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch.yaml` file to point to your own container registry base container image. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_rupatch.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. - Use the file: [oraclerestart_prov_rupatch.yaml](./oraclerestart_prov_rupatch.yaml) for this use case as below: 1. Deploy the `oraclerestart_prov_rupatch.yaml` file: @@ -40,24 +47,4 @@ Use the file: [oraclerestart_prov_rupatch.yaml](./oraclerestart_prov_rupatch.yam =============================== ``` 3. Check Details of Kubernetes CRD Object as in this [example](./orestart_rupatch_object.txt) -4. In this case, the port 1521 from the pod is mapped to port 30007 on the worker node. To make the connection from outside, you will need to open the port 30007 on the worker node for INGRESS. - -Once done, you will be able to make an SQLPLUS database connection to this Oracle Restart Database from a remote client as below -```sh -bash-4.4$ sqlplus system/Oracle_23ai@//129.146.0.149:30007/PORCLCDB - -SQL*Plus: Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems on Sat Jul 19 04:02:48 2025 -Version 23.9.0.25.09 -Copyright (c) 1982, 2025, Oracle. All rights reserved. -Last Successful login time: Sat Jul 19 2025 00:20:14 +00:00 -Connected to: -Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production -Version 19.28.0.0.0 -SQL> -SQL> set lines 200 -SQL> col HOST_NAME format a40 -SQL> select INSTANCE_NAME,HOST_NAME, DATABASE_TYPE from v$instance; -INSTANCE_NAME HOST_NAME DATABASE_TYPE ----------------- ---------------------------------------- --------------- -PORCLCDB dbmc1-0 SINGLE -``` +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md index 33fe8631..7ba19604 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md @@ -8,16 +8,21 @@ This example uses `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` to provi * 1 Node Oracle Restart * Headless services for Oracle Restart * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Node Port 30007 mapped to port 1521 for Database Listener. +* Persistent volumes created automatically based on specified disks for Oracle ASM storage. +* Software location and Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS). * Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. -* Directory where the **Release Update (RU) patch** has been unzipped as `ruPatchLocation`. This folder **must contain `PatchSearch.xml`**, which is used by the installer to detect and apply the patch. Example: `ruPatchLocation: "/stage/software/19c/new/RU/37957391"` -* Directory where latest opatch zip file is kept e.g `oPatchLocation: "/stage/software/19c"` -* Directory where One-off patch files is kept e.g `oneOffLocation: "/stage/software/19c/new/oneoff"` -* Comma-separated DB one-off patch IDs to be applied e.g `dbOneOffIds: "34436514"` -* Comma-separated Grid one-off patch IDs to be applied e.g `gridOneOffIds: "38336965"` +* Mount point for the Software Stage Location inside the Pod is specified by `swStagePvcMountLocation`. +* Staged Software location inside the Pod is specified by `hostSwStageLocation`. It is assumed that the Grid Infrastructure and RDBMS Binaries are already available in this path on the Persistent Volume used. +* The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `storageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. +* Directory where the **Release Update (RU) patch** has been unzipped is specified by `ruPatchLocation`. + * For Example: To apply the 19.28 RU Patch `37957391`, if you have unzipped the RU Patch .zip file `p37957391_190000_Linux-x86-64.zip` to location `/scratch/software/19c/19.28/` on the worker node, then set this parameter `ruPatchLocation` to value `/scratch/software/ru_patch/37957391`. + * Set the permission on the unzipped RU software directory to be `755` recursively. +* Directory, where latest opatch zip file is available, is specified by `oPatchLocation`. +* Directory, where unzipped One-off patch files are available, is specified by `oneOffLocation`. +* Specify Comma-separated one-off patch IDs to be applied to the GI HOME using `gridOneOffIds`. For Example: `gridOneOffIds: "38336965,34436514"` +* Specify Comma-separated one-off patch IDs to be applied to the RDBMS HOME using `dbOneOffIds`. For Example: `dbOneOffIds: "38336965,34436514"` + In this example, * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` file to point to your own container registry base container image. @@ -43,4 +48,4 @@ Use the file: [oraclerestart_prov_rupatch_oneoff_storageclass.yaml](./oraclerest ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_oneoffs_object.txt) \ No newline at end of file +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_db_rupatch_oneoffs_object.txt) \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md new file mode 100644 index 00000000..b9a8f2ce --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md @@ -0,0 +1,46 @@ +# Provisioning an Oracle Restart Database with multiple diskgroups + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files and Recovery Area Files. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. + +This example uses `oraclerestart_prov_multiple_diskgroups.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: + +* 1 Node for Oracle Restart +* Headless services for Oracle Restart. + * Oracle Database Node hostname. +* Persistent volumes created automatically based on specified disks for Oracle ASM Storage. +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +In this example, + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_multiple_diskgroups.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` to`/dev/disk/by-partlabel/asm-disk6`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + * The Diskgroup for CRS files is specified by `crsAsmDiskDg` and the disks on the worker nodes for this diskgroup are specified by `crsAsmDeviceList`. + * The Diskgroup for Database files is specified by `dbDataFileDestDg` and the disks on the worker nodes for this diskgroup are specified by `dbAsmDeviceList`. + * The Diskgroup for Recovery Area files is specified by `dbRecoveryFileDest` and the disks on the worker nodes for this diskgroup are specified by `recoAsmDeviceList`. + + +Use the file: [oraclerestart_prov_multiple_diskgroups.yaml](./oraclerestart_prov_multiple_diskgroups.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_multiple_diskgroups.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_multiple_diskgroups.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./oraclerestart_prov_multiple_diskgroups.txt) +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md new file mode 100644 index 00000000..ad7541a5 --- /dev/null +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md @@ -0,0 +1,49 @@ +# Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy + +In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files and Recovery Area Files. Different Disk Groups have different redundancy levels. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. + +This example uses `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: + +* 1 Node for Oracle Restart +* Headless services for Oracle Restart. + * Oracle Database Node hostname. +* Persistent volumes created automatically based on specified disks for Oracle ASM Storage. +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. +* Namespace: `orestart` +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. +* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +In this example, + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` file to point to the container image you have built. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` to`/dev/disk/by-partlabel/asm-disk6`. + * Specify the size of disk devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + * The Diskgroup for CRS files is specified by `crsAsmDiskDg` and the disks on the worker nodes for this diskgroup are specified by `crsAsmDeviceList`. + * The Diskgroup for Database files is specified by `dbDataFileDestDg` and the disks on the worker nodes for this diskgroup are specified by `dbAsmDeviceList`. + * The Diskgroup for Recovery Area files is specified by `dbRecoveryFileDest` and the disks on the worker nodes for this diskgroup are specified by `recoAsmDeviceList`. + * Redundancy level for the diskgroup with CRS files is mentioned by `crsAsmDiskDgRedundancy`. + * Redundancy level for the diskgroup with Database files is mentioned by `dbAsmDiskDgRedundancy`. + * Redundancy level for the diskgroup with Recovery files is mentioned by `recoAsmDiskDgRedudancy`. + + +Use the file: [oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml](./oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml) for this use case as below: + +1. Deploy the `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +2. Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +3. Check Details of Kubernetes CRD Object as in this [example](./oraclerestart_prov_multiple_diskgroups_with_redundancy.txt) +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md index 4c43c0e8..ab8e35c1 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md @@ -1,35 +1,32 @@ # Provisioning an Oracle Restart Database with RU Patch on Existing PVC In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is -generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. In this case, ru patches already have associated pv and pvcs created bind together. E.g -```sh -orestart/ $ kubectl get pvc -n orestart -NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE -hostsw-stage-pvc Bound hostsw-stage-pv 50Gi RWX hostsw-stage-class 9m15s -rupatch-location-pvc Bound rupatch-location-pv 50Gi RWX rupatch-location-class 9m15s -orestart/ $ kubectl get pv -n orestart -NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE -hostsw-stage-pv 50Gi RWX Retain Bound orestart/hostsw-stage-pvc hostsw-stage-class 16m -rupatch-location-pv 50Gi RWX Retain Bound orestart/rupatch-location-pvc rupatch-location-class 16m -``` +generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. In this case, no storage location from the worker node is used for ASM Disks or GI HOME or RDBMS HOME or Software Staging etc. This example uses `oraclerestart_prov_rupatch_pvc.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: * 1 Node Oracle Restart * Headless services for Oracle Restart - * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes + * Oracle Restart Node hostname +* Node Port 30007 mapped to port 1521 for Database Listener +* Persistent volumes for ASM Disks created automatically using the Storage Class (specified using `storageClass`) for Oracle ASM storage. +* Software location and Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS) * Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostsw-stage-pvc`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. -* Directory where the **Release Update (RU) patch** has been unzipped is refered here with pvc name `rupatch-location-pvc`. This pvc bind location **must contain `PatchSearch.xml`**, which is used by the installer to detect and apply the patch. +* Mount point for the Software Stage Location inside the Pod is specified by `swStagePvcMountLocation`. +* Staged Software location inside the Pod is specified by `hostSwStageLocation`. It is assumed that the Grid Infrastructure and RDBMS Binaries are already available in this path on the Persistent Volume used. +* The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `storageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. +* Location where the `RU Patch` has been unzipped on the mounted PV is specified by `ruPatchLocation`. +* Path to the Opatch Software compatible with the RU Patch is specified using `oPatchLocation`. In this example, - * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch_pvc.yaml` file to point to your own container registry base container image. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_rupatch_pvc.yaml` file to point to the container image you have built. + * Use the file [nfs_pv_stage_vol.yaml](./nfs_pv_stage_vol.yaml) to mount the Network File System as a Persistent Volume named `pv-stage-vol1`. It is assumed this NFS has the required GI and RDBMS Base Software, unzipped RU Patch binaries and Opatch binaries in the specified location. In current case, an OCI File System is used with its export path as `/stage` and Mount Target IP as `10.0.10.212`. + * The disk names for Oracle ASM storage are specified as `/dev/asm-disk1` and `/dev/asm-disk2`. These will be mounted using the Persistent Volumes. + * Specify the size of these devices using the parameter `storageSizeInGb`. Size is by-default in GBs. + +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. - Use the file: [oraclerestart_prov_rupatch_pvc.yaml](./oraclerestart_prov_rupatch_pvc.yaml) for this use case as below: 1. Deploy the `oraclerestart_prov_rupatch_pvc.yaml` file: @@ -49,24 +46,4 @@ Use the file: [oraclerestart_prov_rupatch_pvc.yaml](./oraclerestart_prov_rupatch =============================== ``` 3. Check Details of Kubernetes CRD Object as in this [example](./orestart_rupatch_pvc_object.txt) -4. In this case, the port 1521 from the pod is mapped to port 30007 on the worker node. To make the connection from outside, you will need to open the port 30007 on the worker node for INGRESS. - -Once done, you will be able to make an SQLPLUS database connection to this Oracle Restart Database from a remote client as below -```sh -bash-4.4$ sqlplus system/Oracle_23ai@//129.146.0.149:30007/PORCLCDB - -SQL*Plus: Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems on Sat Jul 19 04:02:48 2025 -Version 23.9.0.25.09 -Copyright (c) 1982, 2025, Oracle. All rights reserved. -Last Successful login time: Sat Jul 19 2025 00:20:14 +00:00 -Connected to: -Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production -Version 19.28.0.0.0 -SQL> -SQL> set lines 200 -SQL> col HOST_NAME format a40 -SQL> select INSTANCE_NAME,HOST_NAME, DATABASE_TYPE from v$instance; -INSTANCE_NAME HOST_NAME DATABASE_TYPE ----------------- ---------------------------------------- --------------- -PORCLCDB dbmc1-0 SINGLE -``` +4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md index 43a0c35b..62b950ac 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md @@ -2,30 +2,34 @@ In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller with Custom Storage Class. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. -This example uses `oraclerestart_prov_storage.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: +In this use case, the ASM Disks are provisioned as Persistent Volumes using custom storage class during the deployment. + +This example uses `oraclerestart_prov_storage_class.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: * 1 Node Oracle Restart * Headless services for Oracle Restart * Oracle Restart node hostname -* Persistent volumes created automatically based on specified disks for Oracle Restart storage -* Software Persistent Volumes and Staged Software Persistent Volumes using the specified location on the corresponding worker nodes +* Node Port 30007 mapped to port 1521 for Database Listener +* Persistent volumes for ASM Disks created automatically using the Storage Class for Oracle ASM storage +* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node * Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation` and we have copied the Grid Infrastructure and RDBMS Binaries to this location on the worker nodes +* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. * Name of Custom Storage Class is specified by `storageClass`. - In this example, - * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_storage.yaml` file to point to your own container registry base container image. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/oracleoci/oraclevdd` and `/dev/oracleoci/oraclevde`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_storage_class.yaml` file to point to the container image you have built. + * The disks provisioned using custom storage class are mounted inside the Oracle Restart Pod as `/dev/asm-disk1` and `/dev/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. -Use the file: [oraclerestart_prov_storage.yaml](./oraclerestart_prov_storage.yaml) for this use case as below: +Use the file: [oraclerestart_prov_storage_class.yaml](./oraclerestart_prov_storage_class.yaml) for this use case as below: -1. Deploy the `oraclerestart_prov_storage.yaml` file: +1. Deploy the `oraclerestart_prov_storage_class.yaml` file: ```sh - kubectl apply -f oraclerestart_prov_storage.yaml + kubectl apply -f oraclerestart_prov_storage_class.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` 2. Check the status of the deployment: @@ -39,4 +43,4 @@ Use the file: [oraclerestart_prov_storage.yaml](./oraclerestart_prov_storage.yam ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_storage_object.txt) +3. Check Details of Kubernetes CRD Object as in this [example](./orestart_storage_class_object.txt) From 334b4abcb188b1260e17905dfdd916a1eff1484e Mon Sep 17 00:00:00 2001 From: racpack Date: Thu, 11 Sep 2025 17:37:45 +0000 Subject: [PATCH 348/414] Update 2 files - /docs/oraclerestart/README.md - /docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md --- docs/oraclerestart/README.md | 10 ++--- .../prerequisites_oracle_restart_db.md | 44 ++++++++----------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md index 20842f16..e1ef5d39 100644 --- a/docs/oraclerestart/README.md +++ b/docs/oraclerestart/README.md @@ -4,19 +4,17 @@ Oracle Restart is an option to the award-winning Oracle Database Enterprise Edit For more information on Oracle Restart Database 19c refer to the [Oracle Database Documentation](http://docs.oracle.com/en/database/). -Kubernetes provides infrastructure building blocks such as compute, storage and networks. Kubernetes makes the infrastructure available as code. It enables rapid provisioning of multi-node topolgies. Additionally, Kubernetes also provides statefulsets, which are the workload API objects that are used to manage stateful applications like Oracle Restarts, Single Instance Oracle Database, and other Oracle features and configurations. +Kubernetes provides essential infrastructure building blocks, including compute, storage, and networking resources, and exposes them as code for infrastructure automation. This approach enables rapid provisioning of multi-node topologies. Additionally, Kubernetes offers the **StatefulSet** workload API object—ideal for managing stateful applications such as Oracle Restart, Single Instance Oracle Databases, and other Oracle features or configurations that require persistent storage and stable network identities. -The Oracle Restart Controller in Oracle Database Operator deploys Oracle Restart Database as a statefulset in the Kubernetes Clusters, using Oracle Restart Slim Image. The Oracle Restart Controller manages the typical lifecycle of Oracle Restart Database in a Kubernetes cluster, as shown below: +The Oracle Restart Controller in the Oracle Database Operator deploys Oracle Databases as a StatefulSet within Kubernetes clusters, utilizing the Oracle Restart Slim Image. The Oracle Restart Controller manages the typical lifecycle operations of an Oracle Database in a Kubernetes environment, including deployment, monitoring, scaling, upgrades, and deletion, as illustrated below: -* Create Oracle Restart Database +* Create Oracle Database * Install and Configure Oracle Grid Infrastructure * Install and Configure Oracle Database * Create Persistent Storage, along with Statefulset * Create Services * Oracle Restart Instances Cleanup -The Oracle Restart Controller provides end-to-end automation for the deployment of Oracle Restart Database in a Kubernetes Cluster. - ## Using Oracle Restart Controller To create an Oracle Database, complete the steps in the following sections: @@ -34,7 +32,7 @@ To create an Oracle Database, complete the steps in the following sections: **IMPORTANT :** You must make the changes specified in this section before you proceed to the next section. -In order to become familiar with Oracle Restart on containers, you can refer [this documentation](https://github.com/oracle/docker-images/blob/main/OracleDatabase/RAC/OracleRealApplicationClusters/docs/orestart/README.md) before proceeding further. +To become familiar with Oracle Restart in containerized environments, review the [this documentation](https://github.com/oracle/docker-images/blob/main/OracleDatabase/RAC/OracleRealApplicationClusters/docs/orestart/README.md) before proceeding further [Pre-requisites for running Oracle Restart Controller](./provisioning/prerequisites_oracle_restart_db.md) diff --git a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md index 67a8ccba..317f8550 100644 --- a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md @@ -14,16 +14,16 @@ * [Oracle Restart Database Slim Image](#oracle-restart-database-slim-image) * [Create a Kubernetes secret for the Oracle Restart Database installation owner for the Oracle Restart Database Deployment](#create-a-kubernetes-secret-for-the-oracle-restart-database-installation-owner-for-the-oracle-restart-database-deployment) -To deploy Oracle Restart Database using Oracle Restart Database Controller in Oracle Database Operator, you need a Kubernetes Cluster like an Oracle Kubernetes Engine(OKE). +To deploy an Oracle Database using the Oracle Restart Database Controller in the Oracle Database Operator, you require a Kubernetes cluster such as Oracle Kubernetes Engine (OKE). If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will require: ## Kubernetes Cluster Requirements - You must ensure that your Kubernetes Cluster meets the necessary requirements for Oracle Restart Database deployment. The minimum required OKE cluster version is 1.33.1 or higher. Refer documentation for details [Oracle Kubernetes Engine](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm) cluster. + You must ensure that your Kubernetes Cluster meets the necessary requirements for Oracle Restart Database deployment. The minimum required OKE cluster version is 1.33 or higher. Refer documentation for details [Oracle Kubernetes Engine](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm) cluster. ## Mandatory roles and privileges requirements for Oracle Restart Database Controller - Oracle Sharding Database Controller uses Kubernetes objects such as :- + Oracle Restart Database Controller uses Kubernetes objects such as :- | Resources | Verbs | | --- | --- | @@ -35,7 +35,7 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will | Events | create patch | ## Prerequisites for Oracle Restart on OKE - Before proceeding with the Oracle Restart Database deployment, ensure that you have completed the necessary prerequisites on your OKE cluster. This includes setting up the required infrastructure and configuring the necessary components. To use these instructions, you should have background knowledge of the technology and operating system. + Before proceeding with the Oracle Database deployment, ensure that you have completed all required prerequisites on your Oracle Kubernetes Engine (OKE) cluster. This includes setting up the necessary infrastructure and configuring all relevant components. To effectively use these instructions, you should have background knowledge of both Kubernetes technology and the underlying operating system. * Verify that all necessary dependencies are installed and up-to-date. You should be familiar with the following technologies: * Linux * Kubernetes @@ -44,47 +44,41 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will * Oracle Grid Infrastructure installation * Oracle Automatic Storage Management (Oracle ASM) - ## Preparing to Install Oracle Restart on OKE To prepare for the Oracle Restart Database installation, follow these steps: * Ensure that your OKE cluster is properly configured and meets the necessary requirements. - Each pod that you deploy as part of your cluster must satisfy the minimum hardware requirements of the Oracle Restart Database and Oracle Grid Infrastructure software. If you are planning to install Oracle Grid Infrastructure and Oracle Restart database software on data volumes exposed from your environment, then you must have at least 50 GB space allocated for the Oracle Restart on OKE Pod. - + Each pod that you deploy as part of your cluster must satisfy the minimum hardware requirements of the Oracle Restart Database and Oracle Grid Infrastructure software. If you are planning to install Oracle Grid Infrastructure and Oracle Restart database software on host local volumes exposed from your environment, then you must have at least 50 GB space allocated for the Oracle Restart on OKE Pod. * In addition to the standard memory (RAM) required for Oracle Linux (Linux-x86-64), the Oracle Grid Infrastructure and Oracle Database instances, Oracle recommends that you provide an additional 2 GB of RAM to each Kubernetes node for the control plane. - * Database storage for Oracle Database on OKE must use Oracle Automatic Storage Management (Oracle ASM) configured on block storage. - * Oracle Database on Kubernetes is currently supported with the following releases: - * Oracle Grid Infrastructure Release 19.28 or later release updates - * Oracle Database Release 19.28 or later - * Oracle Kubernetes Engine Environment 1.33.1 or later - * Unbreakable Enterprise Kernel Release 7 UEKR7 (Kernel Release 5.15.0-202.135.2.el9uek.x86_64 ) or later updates - * Oracle Linux for Operator, control plane, and Worker nodes on Oracle Linux 8 (Linux-x86-64) Update 10 or later updates - - + Prerequisite Software and Environment Versions: + * Oracle Grid Infrastructure: Release 19.28 or later + * Oracle Database: Release 19.28 or later + * Oracle Kubernetes Engine (OKE): Version 1.33 or later + * Unbreakable Enterprise Kernel (UEK): Release 7 UEKR7 (Kernel Release 5.15.0-202.135.2.el9uek.x86_64) or later + * Oracle Linux: For Operator, control plane, and worker nodes—Oracle Linux 8 (Linux-x86-64) Update 10 or later + ## Worker Node Preparation for Oracle Restart on OKE * When configuring your Worker Nodes, follow these guidelines, and see the configuration Oracle used for testing. - Each OKE worker node must have sufficient resources to support the intended number of Oracle Database Pods, each of which must meet at least the minimum requirements for Oracle Grid Infrastructure servers hosting an Oracle Database node. - * The Oracle Database Pods in this example configuration were created on the machine `10.0.10.58` for Oracle Restart: - Oracle Database Node - Worker Node: 10.0.10.58 - Container Pod: dbmc1-0 - * Worker node has the following configuration: - - RAM:16GB + - RAM:32GB - Operating system disk: - Ensure that your storage has at least the following available space: - / (Root): 40 GB - /scratch/orestart/: 80 GB (the worker node directory which will be used for /u01 to store Oracle Grid Infrastructure and Oracle Database homes) - - /var/lib/containers: 50 GB xfs - - /scratch/software/stage (the worker node directory for staging Oracle Grid Infrastructure and Oracle RDBMS software) + - /var/lib/containers: 100 GB xfs - Oracle Linux 8.10 with Unbreakable Enterprise Kernel Release UEKR7 (Kernel Release 5.15.0-308.179.6.el8uek.x86_64) or later - Network Cards: - ens3: Default network interface. The Oracle Restart pod will use this network interface for cluster public network. - - - Block devices: + **Notes** + - You only need to configure the following storage locations on the worker node **if you are not using a StorageClass** for the software volume and ASM disks. + - /scratch/software/stage (the worker node directory will be exposed as local host volume to the Oracle Restart pod for staging Oracle Grid Infrastructure and Oracle RDBMS software) + - Block devices: - You can use any supported storage options for Oracle Grid Infrastructure. Ensure that your storage has at least the following space available: - /dev/oracleoci/oraclevdd (50 GB) - /dev/oracleoci/oraclevde (50 GB) @@ -140,7 +134,7 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will * Restart Kubelet: `# systemctl restart kubelet` * Check the Kubelet status: `# systemctl status kubelet` - 6. Create the mount points on the worker node to be mounted to Oracle Restart Pod for Oracle GI + RDBMS HOME and the Software Staging Location + 6.Skip this step if you are using a StorageClass.Otherwise, create the necessary mount points on the worker node. These mount points will be used by the Oracle Restart pod for Oracle Grid Infrastructure and RDBMS Home, as well as for the software staging location. * On worker node: + `# mkdir -p /scratch/orestart/` + `# mkdir -p /scratch/software/stage` From 0bcd77ad4e1eac7938c79fe70c489726e38ea7a3 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 11 Sep 2025 22:04:15 +0000 Subject: [PATCH 349/414] Added fixes for latest golang --- Makefile | 6 +++--- go.mod | 26 +++++++++++++------------- go.sum | 48 ++++++++++++++++++++++++------------------------ 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index f50d879a..198754b1 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ build: generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go -GOLANG_VERSION ?= 1.24.4 +GOLANG_VERSION ?= 1.25.1 DOCKER ?= podman ## Download golang in the Dockerfile if BUILD_INTERNAL is set to true. ## Otherwise, use golang image from docker hub as the builder. @@ -145,8 +145,8 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions -KUSTOMIZE_VERSION ?= v5.4.3 -CONTROLLER_TOOLS_VERSION ?= v0.16.5 +KUSTOMIZE_VERSION ?= v5.7 +CONTROLLER_TOOLS_VERSION ?= v0.17 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize diff --git a/go.mod b/go.mod index 8a56918c..1c9c56ec 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,22 @@ module github.com/oracle/oracle-database-operator -go 1.24.4 +go 1.25.1 require ( github.com/go-logr/logr v1.4.3 github.com/onsi/ginkgo/v2 v2.25.2 github.com/onsi/gomega v1.38.2 - github.com/oracle/oci-go-sdk/v65 v65.99.2 + github.com/oracle/oci-go-sdk/v65 v65.99.1 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.85.0 go.uber.org/zap v1.27.0 - golang.org/x/text v0.28.0 - k8s.io/api v0.34.0 - k8s.io/apimachinery v0.34.0 - k8s.io/cli-runtime v0.34.0 - k8s.io/client-go v0.34.0 - k8s.io/kubectl v0.34.0 - k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect - sigs.k8s.io/controller-runtime v0.22.0 + golang.org/x/text v0.29.0 + k8s.io/api v0.34.1 + k8s.io/apimachinery v0.34.1 + k8s.io/cli-runtime v0.34.1 + k8s.io/client-go v0.34.1 + k8s.io/kubectl v0.34.1 + k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect + sigs.k8s.io/controller-runtime v0.22.1 sigs.k8s.io/yaml v1.6.0 ) @@ -96,7 +96,7 @@ require ( golang.org/x/crypto v0.41.0 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.16.0 // indirect + golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/term v0.34.0 // indirect golang.org/x/time v0.12.0 // indirect @@ -108,8 +108,8 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.34.0 // indirect - k8s.io/component-base v0.34.0 // indirect - k8s.io/component-helpers v0.34.0 // indirect + k8s.io/component-base v0.34.1 // indirect + k8s.io/component-helpers v0.34.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect diff --git a/go.sum b/go.sum index 86aff3a6..ec9b90c0 100644 --- a/go.sum +++ b/go.sum @@ -140,8 +140,8 @@ github.com/onsi/ginkgo/v2 v2.25.2 h1:hepmgwx1D+llZleKQDMEvy8vIlCxMGt7W5ZxDjIEhsw github.com/onsi/ginkgo/v2 v2.25.2/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/oracle/oci-go-sdk/v65 v65.99.2 h1:W1HzD28r+xqqT57FpH7ymOFE3BM/p4J/Ia1SfDf55nM= -github.com/oracle/oci-go-sdk/v65 v65.99.2/go.mod h1:RGiXfpDDmRRlLtqlStTzeBjjdUNXyqm3KXKyLCm3A/Q= +github.com/oracle/oci-go-sdk/v65 v65.99.1 h1:fWNC7Ef1XQ3m7QyWa7848eHnGR8c4O3+t9k//hLOkqU= +github.com/oracle/oci-go-sdk/v65 v65.99.1/go.mod h1:RGiXfpDDmRRlLtqlStTzeBjjdUNXyqm3KXKyLCm3A/Q= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -250,8 +250,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -290,8 +290,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -324,30 +324,30 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE= -k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug= +k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= +k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc= k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0= -k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0= -k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/cli-runtime v0.34.0 h1:N2/rUlJg6TMEBgtQ3SDRJwa8XyKUizwjlOknT1mB2Cw= -k8s.io/cli-runtime v0.34.0/go.mod h1:t/skRecS73Piv+J+FmWIQA2N2/rDjdYSQzEE67LUUs8= -k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo= -k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY= -k8s.io/component-base v0.34.0 h1:bS8Ua3zlJzapklsB1dZgjEJuJEeHjj8yTu1gxE2zQX8= -k8s.io/component-base v0.34.0/go.mod h1:RSCqUdvIjjrEm81epPcjQ/DS+49fADvGSCkIP3IC6vg= -k8s.io/component-helpers v0.34.0 h1:5T7P9XGMoUy1JDNKzHf0p/upYbeUf8ZaSf9jbx0QlIo= -k8s.io/component-helpers v0.34.0/go.mod h1:kaOyl5tdtnymriYcVZg4uwDBe2d1wlIpXyDkt6sVnt4= +k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= +k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/cli-runtime v0.34.1 h1:btlgAgTrYd4sk8vJTRG6zVtqBKt9ZMDeQZo2PIzbL7M= +k8s.io/cli-runtime v0.34.1/go.mod h1:aVA65c+f0MZiMUPbseU/M9l1Wo2byeaGwUuQEQVVveE= +k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= +k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= +k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= +k8s.io/component-helpers v0.34.1 h1:gWhH3CCdwAx5P3oJqZKb4Lg5FYZTWVbdWtOI8n9U4XY= +k8s.io/component-helpers v0.34.1/go.mod h1:4VgnUH7UA/shuBur+OWoQC0xfb69sy/93ss0ybZqm3c= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f h1:wyRlmLgBSXi3kgawro8klrMRljXeRo1HFkQRs+meYfs= k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= -k8s.io/kubectl v0.34.0 h1:NcXz4TPTaUwhiX4LU+6r6udrlm0NsVnSkP3R9t0dmxs= -k8s.io/kubectl v0.34.0/go.mod h1:bmd0W5i+HuG7/p5sqicr0Li0rR2iIhXL0oUyLF3OjR4= -k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0= -k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.22.0 h1:mTOfibb8Hxwpx3xEkR56i7xSjB+nH4hZG37SrlCY5e0= -sigs.k8s.io/controller-runtime v0.22.0/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY= +k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI= +k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV24Eqg= +sigs.k8s.io/controller-runtime v0.22.1/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= From 8c8e960e973246edac1d07f7bcf02360b5866f53 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 11 Sep 2025 22:28:06 +0000 Subject: [PATCH 350/414] Added fixes --- Makefile | 2 +- ...acle.com_autonomouscontainerdatabases.yaml | 2 +- ....oracle.com_autonomousdatabasebackups.yaml | 2 +- ...oracle.com_autonomousdatabaserestores.yaml | 2 +- ...tabase.oracle.com_autonomousdatabases.yaml | 2 +- .../database.oracle.com_dataguardbrokers.yaml | 2 +- .../database.oracle.com_dbcssystems.yaml | 2 +- .../crd/bases/database.oracle.com_lrests.yaml | 2 +- .../crd/bases/database.oracle.com_lrpdbs.yaml | 2 +- .../database.oracle.com_oraclerestarts.yaml | 2 +- ...ase.oracle.com_oraclerestdataservices.yaml | 2 +- .../bases/database.oracle.com_ordssrvs.yaml | 2 +- ...database.oracle.com_shardingdatabases.yaml | 2 +- ...se.oracle.com_singleinstancedatabases.yaml | 2 +- ...vability.oracle.com_databaseobservers.yaml | 2 +- config/manager/kustomization.yaml | 4 +-- oracle-database-operator.yaml | 30 +++++++++---------- 17 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Makefile b/Makefile index 198754b1..643155bc 100644 --- a/Makefile +++ b/Makefile @@ -145,7 +145,7 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions -KUSTOMIZE_VERSION ?= v5.7 +KUSTOMIZE_VERSION ?= v5.7.1 CONTROLLER_TOOLS_VERSION ?= v0.17 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" diff --git a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml index 1e078b63..78907768 100644 --- a/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: autonomouscontainerdatabases.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml index 86abc9e7..c4c49b93 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: autonomousdatabasebackups.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml index edcac8ce..d59dfe2d 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: autonomousdatabaserestores.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index 75aa6644..d8fdd561 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: autonomousdatabases.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml index 40cc0e4a..c71e94c0 100644 --- a/config/crd/bases/database.oracle.com_dataguardbrokers.yaml +++ b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: dataguardbrokers.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml index 91a66bcf..351287e5 100644 --- a/config/crd/bases/database.oracle.com_dbcssystems.yaml +++ b/config/crd/bases/database.oracle.com_dbcssystems.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: dbcssystems.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_lrests.yaml b/config/crd/bases/database.oracle.com_lrests.yaml index 14d59978..4edf8f84 100644 --- a/config/crd/bases/database.oracle.com_lrests.yaml +++ b/config/crd/bases/database.oracle.com_lrests.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: lrests.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_lrpdbs.yaml b/config/crd/bases/database.oracle.com_lrpdbs.yaml index a9449ab3..b58d9d89 100644 --- a/config/crd/bases/database.oracle.com_lrpdbs.yaml +++ b/config/crd/bases/database.oracle.com_lrpdbs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: lrpdbs.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index aab8cb3d..3f849e39 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: oraclerestarts.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml index e4c3f379..82479941 100644 --- a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: oraclerestdataservices.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_ordssrvs.yaml b/config/crd/bases/database.oracle.com_ordssrvs.yaml index ec7ba279..bb2208d6 100644 --- a/config/crd/bases/database.oracle.com_ordssrvs.yaml +++ b/config/crd/bases/database.oracle.com_ordssrvs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: ordssrvs.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index c25b642c..df7ab8ed 100644 --- a/config/crd/bases/database.oracle.com_shardingdatabases.yaml +++ b/config/crd/bases/database.oracle.com_shardingdatabases.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: shardingdatabases.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index 4bd2f0b7..247a5308 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: singleinstancedatabases.database.oracle.com spec: group: database.oracle.com diff --git a/config/crd/bases/observability.oracle.com_databaseobservers.yaml b/config/crd/bases/observability.oracle.com_databaseobservers.yaml index baf2bd7d..200902bc 100644 --- a/config/crd/bases/observability.oracle.com_databaseobservers.yaml +++ b/config/crd/bases/observability.oracle.com_databaseobservers.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: databaseobservers.observability.oracle.com spec: group: observability.oracle.com diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 4e8254b1..1d870700 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,5 +8,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lin.ocir.io/intsanjaysingh/operator/dboperator_master_orahub - newTag: oeap_sha256 + newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database + newTag: orestart-operator-mltp1 diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 19e414b1..684db406 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10,7 +10,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: autonomouscontainerdatabases.database.oracle.com spec: conversion: @@ -182,7 +182,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: autonomousdatabasebackups.database.oracle.com spec: conversion: @@ -396,7 +396,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: autonomousdatabaserestores.database.oracle.com spec: conversion: @@ -600,7 +600,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: autonomousdatabases.database.oracle.com spec: conversion: @@ -1302,7 +1302,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: databaseobservers.observability.oracle.com spec: group: observability.oracle.com @@ -8831,7 +8831,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: dataguardbrokers.database.oracle.com spec: group: database.oracle.com @@ -9045,7 +9045,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: dbcssystems.database.oracle.com spec: group: database.oracle.com @@ -9929,7 +9929,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: lrests.database.oracle.com spec: group: database.oracle.com @@ -10215,7 +10215,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: lrpdbs.database.oracle.com spec: group: database.oracle.com @@ -10605,7 +10605,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: oraclerestarts.database.oracle.com spec: group: database.oracle.com @@ -12025,7 +12025,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: oraclerestdataservices.database.oracle.com spec: group: database.oracle.com @@ -12390,7 +12390,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: ordssrvs.database.oracle.com spec: group: database.oracle.com @@ -12886,7 +12886,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: shardingdatabases.database.oracle.com spec: group: database.oracle.com @@ -14065,7 +14065,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: oracle-database-operator-system/oracle-database-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.3 name: singleinstancedatabases.database.oracle.com spec: group: database.oracle.com @@ -15761,7 +15761,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: lin.ocir.io/intsanjaysingh/operator/dboperator_master_orahub:oeap_sha256 + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-mltp1 imagePullPolicy: Always name: manager ports: From 3bb073c23ecb0fa47dbf105a5813ef0cfba1baa1 Mon Sep 17 00:00:00 2001 From: Mohammed Yunus Date: Fri, 12 Sep 2025 04:29:57 +0000 Subject: [PATCH 351/414] Update .gitlab-ci.yml --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dff89e54..15a73ac1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,7 @@ build-operator: OP_YAML: oracle-database-operator.yaml BUILD_INTERNAL: "true" script: - - export GOLANG_VERSION=1.24.4 + - export GOLANG_VERSION=1.25.1 - export GOROOT=$(go${GOLANG_VERSION} env GOROOT) - export PATH="${GOROOT}/bin:${PATH}" - make operator-yaml IMG=$IMAGE GOLANG_VERSION=$GOLANG_VERSION From bd395c3901e42e5b09232e7a9c23cf031e7cf582 Mon Sep 17 00:00:00 2001 From: mmalvezz Date: Fri, 12 Sep 2025 06:53:53 +0000 Subject: [PATCH 352/414] jpg case-sensitive paths --- .../{UsecaseSchema.jpg => old_testcase_wf.jpg} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/multitenant/images/{UsecaseSchema.jpg => old_testcase_wf.jpg} (100%) diff --git a/docs/multitenant/images/UsecaseSchema.jpg b/docs/multitenant/images/old_testcase_wf.jpg similarity index 100% rename from docs/multitenant/images/UsecaseSchema.jpg rename to docs/multitenant/images/old_testcase_wf.jpg From 39d05ac6708b931969019b7c0969835742b38e7a Mon Sep 17 00:00:00 2001 From: Saurabh Ahuja Date: Fri, 12 Sep 2025 13:48:09 +0000 Subject: [PATCH 353/414] block device oneoff fix --- apis/database/v4/oraclerestart_webhook.go | 2 +- commons/oraclerestart/oraclerestartprov.go | 16 ++++++++++++++++ config/manager/kustomization.yaml | 2 +- oracle-database-operator.yaml | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index abf294a4..0fc41d75 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -247,7 +247,7 @@ func (r *OracleRestart) ValidateUpdate(ctx context.Context, oldObj, newObj runti if !reflect.DeepEqual(old.Spec, newCr.Spec) { return nil, apierrors.NewForbidden( schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, - newCr.Name, fmt.Errorf("updates to RAC Spec are not allowed while RAC is in state %s", newCr.Status.State)) + newCr.Name, fmt.Errorf("updates to Oracle Restart Spec is not allowed while its in state %s", newCr.Status.State)) } } diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index be483476..737d72bb 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -357,6 +357,16 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac }, }) } + if instance.Spec.ConfigParams.OneOffLocation != "" { + result = append(result, corev1.Volume{ + Name: OracleRestartSpex.Name + "-oradata-oneoff-vol", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: instance.Spec.ConfigParams.OneOffLocation, + }, + }, + }) + } } if len(OracleRestartSpex.PvcName) != 0 { @@ -583,6 +593,12 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, MountPath: instance.Spec.ConfigParams.OPatchLocation, }) } + if instance.Spec.ConfigParams.OneOffLocation != "" { + result = append(result, corev1.VolumeMount{ + Name: OracleRestartSpex.Name + "-oradata-oneoff-vol", + MountPath: instance.Spec.ConfigParams.OneOffLocation, + }) + } } } diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 1d870700..6173a20a 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: orestart-operator-mltp1 + newTag: orestart-operator-sa diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 684db406..2d076f64 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -15761,7 +15761,7 @@ spec: env: - name: WATCH_NAMESPACE value: "" - image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-mltp1 + image: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:orestart-operator-sa imagePullPolicy: Always name: manager ports: From 47b574ecfbcfbd7153e0ee400b06d664cda0f1a8 Mon Sep 17 00:00:00 2001 From: kuassi_mensah Date: Fri, 12 Sep 2025 16:00:35 +0000 Subject: [PATCH 354/414] Update 2 files - /THIRD_PARTY_LICENSES.txt - /README.md --- README.md | 2 +- THIRD_PARTY_LICENSES.txt | 40 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 5d0a8646..5ffdc726 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ In this v2.0 production release, `OraOperator` supports the following database c * Upgraded Kubernetes API version to v4 * Published on `operatorhub.io` * Operator Lifecycle Manager (OLM) support (install from `operatorhub.io`) -* Validated on Google Kubernetes Engine +* OpenShift certified --- diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt index 14e4308f..d03e4432 100644 --- a/THIRD_PARTY_LICENSES.txt +++ b/THIRD_PARTY_LICENSES.txt @@ -1,5 +1,5 @@ ------------------------------------- -Operator SDK 1.37.0 +Operator SDK 1.40.0 https://github.com/operator-framework/operator-sdk Apache 2.0 @@ -208,7 +208,7 @@ Apache License: limitations under the License. ------------------------------ - GO lang 1.23.3 + GO lang 1.25.1 https://github.com/golang @@ -241,18 +241,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------- -apimachinery 0.31.3 +apimachinery 0.32.2 https://github.com/kubernetes/apimachinery/tr Apache 2.0 ------------------------- -controller-runtime 0.19.3 +controller-runtime 0.21.0 https://github.com/kubernetes-sigs/controller-runtime/releases/tag/v0.16.3 Apache 2.0 ------------------------- -golang 1.23.3 -https://github.com/golang/go/releases/tag/go1.21.4 +golang 1.25.1 +https://github.com/golang/go/releases/tag/go1.25.1 BSD 2-clause or 3-clause License: @@ -1006,13 +1006,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. limitations under the License. -------------------------- -Logr 1.4.2 +Logr 1.4.3 https://pkg.go.dev/github.com/go-logr/logr -https://github.com/go-logr/logr/tree/v1.3.0 +https://github.com/go-logr/logr/tree/v1.4.3 Apache 2.0 License ------------------------- -OCI Go SDK 65.77.1 +OCI Go SDK 65.95.0 https://github.com/oracle/oci-go-sdk/releases/tag/v65.53.0 Dual-License: UPL + Apache 2.0 @@ -1063,7 +1063,7 @@ The Universal Permissive License (UPL), Version 1.0 ------------------------- -ginkgo 2.202. +ginkgo 2.23.4 https://github.com/onsi/ginkgo/releases/tag/v2.13.1 MIT ------------------------------------ @@ -1073,48 +1073,48 @@ MIT License Copyright (c) 2013-2014 Onsi Fakhouri ---------------------------- -gomega 1.34.2 +gomega 1.37.0 http://onsi.github.io/gomega/ MIT ------------------------- -Kubernetes api 0.31.3 +Kubernetes api 0.33.2 https://pkg.go.dev/k8s.io/api Apache 2.0 ---------------------------------- -Kubernetes apimachinery 0.31.3 +Kubernetes apimachinery 0.32.2 https://pkg.go.dev/k8s.io/apimachinery Apache 2.0 ----------------------------------- -Kubernetes client-go 0.31.3 +Kubernetes client-go 0.33.2 https://pkg.go.dev/k8s.io/client-go Apache 2.0 ------------------------------------- -Kubernetes controller-runtime project 0.19.3 +Kubernetes controller-runtime project 0.21.0 https://pkg.go.dev/sigs.k8s.io/controller-runtime Apache 2.0 ------------------------------------ -kubernetes-sigs/yaml 1.4.0 -https://github.com/kubernetes-sigs/yaml/tree/v1.3.0 +kubernetes-sigs/yaml 1.5.0 +https://github.com/kubernetes-sigs/yaml/tree/v1.5.0 MIT ------------------------- -OCI SDK for Go 65.77.1 +OCI SDK for Go 65.95.0 https://github.com/oracle/oci-go-sdk Multiple Licenses: Apache 2.0, UPL ------------------------------ -Operator Lifecycle Manager (OLM) 0.30.0 +Operator Lifecycle Manager (OLM) 0.32.0 github.com/operator-framework/operator-lifecycle-manager Apache 2.0 ------------------------------------ -Prometheus Operator 0.78.2 +Prometheus Operator 0.83.0 https://github.com/prometheus-operator/prometheus-operator Apache 2.0 From 96d8ac85a086650b17bc2085704a6406a4ec6565 Mon Sep 17 00:00:00 2001 From: kuassi_mensah Date: Fri, 12 Sep 2025 16:05:34 +0000 Subject: [PATCH 355/414] Update file README.md --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5ffdc726..c9a019bf 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,10 @@ In this v2.0 production release, `OraOperator` supports the following database c - New LRES-based Controller (ARM & AM) - PDBs settings with init parameters config map - Assertive deletion policy -* **Database Observability (Preview)** - - Support for Database Logs (in addition to Metrics) +* **Database Observability ** + - Support for Database Logs and Metrics - Support for the latest Exporter container images - - Bug Fix: Prometheus label config + * **Oracle Base Database Service** - Support for Oracle Database 23ai Cloning, using KMS Vaults - PDB creation @@ -86,13 +86,10 @@ As of v2.0.0, the Oracle Database Operator for Kubernetes (`OraOperator`) suppor ## Release Status This production release has been installed and tested on: -* [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) - -The previous releases were tested on the following platforms: - * [Oracle Container Engine for Kubernetes (OKE)](https://www.oracle.com/cloud-native/container-engine-kubernetes/) with Kubernetes 1.30 or later * [Redhat Openshift](https://www.redhat.com/en/technologies/cloud-computing/openshift) with version v4.16 or later * [Oracle Linux Cloud Native Environment(OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) 1.9 or later +* [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) * [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) * [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/) * [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) From 80e4f7c488f3b7a654aa113a573b4994abb36d17 Mon Sep 17 00:00:00 2001 From: racpack Date: Sat, 13 Sep 2025 05:03:04 +0000 Subject: [PATCH 356/414] Update 49 files --- ...sm_disk_to_an_existing_restart_database.md | 69 ++-- ...change_memory_cpu_for_oracle_restart_db.md | 55 +-- ...e_sw_storage_size_for_oracle_restart_db.md | 61 ++-- ...disks_from_an_existing_restart_database.md | 49 ++- .../provisioning/oraclerestart_prov.yaml | 2 +- .../oraclerestart_prov_loadbalancer.yaml | 2 +- ...raclerestart_prov_multiple_diskgroups.yaml | 2 +- ...v_multiple_diskgroups_with_redundancy.yaml | 2 +- .../oraclerestart_prov_nodeports.yaml | 2 +- ...clerestart_prov_nodeports_mcpu_change.yaml | 2 +- .../oraclerestart_prov_rupatch.yaml | 2 +- ...tart_prov_rupatch_oneoff_storageclass.yaml | 4 +- .../oraclerestart_prov_rupatch_pvc.yaml | 2 +- .../oraclerestart_prov_storage_class.yaml | 2 +- ...ov_storage_class_after_sw_home_resize.yaml | 2 +- ...v_storage_class_before_sw_home_resize.yaml | 2 +- .../orestart_db_rupatch_oneoffs_object.txt | 26 +- .../orestart_prov_asm_disk_addition.yaml | 2 +- ...ov_asm_disk_addition_autoupdate_false.yaml | 2 +- .../orestart_prov_asm_disk_deletion.yaml | 2 +- .../prerequisites_oracle_restart_db.md | 202 +++++------ .../provisioning_oracle_restart_db.md | 43 ++- ...isioning_oracle_restart_db_loadbalancer.md | 50 ++- ...provisioning_oracle_restart_db_nodeport.md | 49 ++- .../provisioning_oracle_restart_db_rupatch.md | 54 ++- ...oning_oracle_restart_db_rupatch_oneoffs.md | 61 ++-- ...ning_oracle_restart_multiple_diskgroups.md | 43 ++- ...art_multiple_diskgroups_with_redundancy.md | 41 ++- ...provisioning_oracle_restart_rupatch_pvc.md | 50 ++- ...ovisioning_oracle_restart_storage_class.md | 45 ++- .../Oracle_Machine_Learning_AI_models.htm | 320 ------------------ docs/privateai/README.md | 136 -------- docs/privateai/access_privateai.md | 35 -- docs/privateai/api_endpoint.md | 16 - .../configmap_multi_model_filesystem.md | 17 - docs/privateai/configmap_multi_model_https.md | 17 - .../privateai/configmap_single_model_https.md | 17 - docs/privateai/create_oci_fss_based_pvc.md | 31 -- docs/privateai/debug_privateai.md | 14 - docs/privateai/deploy_privateai_internallb.md | 46 --- ...ateai_multi_model_filesystem_internallb.md | 68 ---- ..._privateai_multi_model_https_internallb.md | 58 ---- ..._multi_model_https_internallb_add_model.md | 14 - ...lti_model_https_internallb_remove_model.md | 14 - docs/privateai/deploy_privateai_publiclb.md | 23 -- docs/privateai/provisioning/StorageClass.yaml | 7 - .../multi_model_filesystem_config.json | 31 -- .../multi_model_https_config.json | 22 -- docs/privateai/provisioning/oke-pv.yaml | 17 - docs/privateai/provisioning/oke-pvc.yaml | 13 - docs/privateai/provisioning/oml-ssl-pwd | 1 - .../provisioning/pai_sample_internallb.yaml | 61 ---- .../pai_sample_internallb_replace_cert.yaml | 65 ---- ...ple_multi_model_filesystem_internallb.yaml | 65 ---- ...el_filesystem_internallb_replace_cert.yaml | 69 ---- ...i_sample_multi_model_https_internallb.yaml | 61 ---- ...i_model_https_internallb_replace_cert.yaml | 65 ---- .../provisioning/pai_sample_publiclb.yaml | 61 ---- .../provisioning/pai_sample_scale_in.yaml | 61 ---- .../provisioning/pai_sample_scale_up.yaml | 61 ---- docs/privateai/provisioning/pai_secret.sh | 26 -- docs/privateai/provisioning/pai_secret_new.sh | 26 -- .../single_model_https_config.json | 16 - docs/privateai/scale_in_privateai.md | 25 -- docs/privateai/scale_up_privateai.md | 25 -- 65 files changed, 449 insertions(+), 2085 deletions(-) delete mode 100644 docs/privateai/Oracle_Machine_Learning_AI_models.htm delete mode 100644 docs/privateai/README.md delete mode 100644 docs/privateai/access_privateai.md delete mode 100644 docs/privateai/api_endpoint.md delete mode 100644 docs/privateai/configmap_multi_model_filesystem.md delete mode 100644 docs/privateai/configmap_multi_model_https.md delete mode 100644 docs/privateai/configmap_single_model_https.md delete mode 100644 docs/privateai/create_oci_fss_based_pvc.md delete mode 100644 docs/privateai/debug_privateai.md delete mode 100644 docs/privateai/deploy_privateai_internallb.md delete mode 100644 docs/privateai/deploy_privateai_multi_model_filesystem_internallb.md delete mode 100644 docs/privateai/deploy_privateai_multi_model_https_internallb.md delete mode 100644 docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md delete mode 100644 docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md delete mode 100644 docs/privateai/deploy_privateai_publiclb.md delete mode 100644 docs/privateai/provisioning/StorageClass.yaml delete mode 100644 docs/privateai/provisioning/multi_model_filesystem_config.json delete mode 100644 docs/privateai/provisioning/multi_model_https_config.json delete mode 100644 docs/privateai/provisioning/oke-pv.yaml delete mode 100644 docs/privateai/provisioning/oke-pvc.yaml delete mode 100644 docs/privateai/provisioning/oml-ssl-pwd delete mode 100644 docs/privateai/provisioning/pai_sample_internallb.yaml delete mode 100644 docs/privateai/provisioning/pai_sample_internallb_replace_cert.yaml delete mode 100644 docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb.yaml delete mode 100644 docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb_replace_cert.yaml delete mode 100644 docs/privateai/provisioning/pai_sample_multi_model_https_internallb.yaml delete mode 100644 docs/privateai/provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml delete mode 100644 docs/privateai/provisioning/pai_sample_publiclb.yaml delete mode 100644 docs/privateai/provisioning/pai_sample_scale_in.yaml delete mode 100644 docs/privateai/provisioning/pai_sample_scale_up.yaml delete mode 100755 docs/privateai/provisioning/pai_secret.sh delete mode 100755 docs/privateai/provisioning/pai_secret_new.sh delete mode 100644 docs/privateai/provisioning/single_model_https_config.json delete mode 100644 docs/privateai/scale_in_privateai.md delete mode 100644 docs/privateai/scale_up_privateai.md diff --git a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md index e2d9e315..d74f07be 100644 --- a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md +++ b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md @@ -1,41 +1,41 @@ -# Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database - -This use case demonstrates adding a new ASM Disks to an existing Oracle Restart Database provisioned earlier using Oracle Restart Controller. - -In this use case, the existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart Node hostname -* Node Port 30007 mapped to port 1521 for Database Listener -* Persistent volumes created automatically based on specified disks for Oracle ASM storage -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. - * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. +## Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database + +### In this usecase: + +* You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. Now, you need to expand ASM storage by adding new ASM disks. +* The existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: + * Oracle Restart Pod + * Headless services for Oracle Restart + * Oracle Restart Node hostname + * Node Port 30007 mapped to port 1521 for Database Listener. If you are using Loadbalancer then you will see LB service. + * Persistent volumes created automatically based on specified disks for Oracle ASM storage + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + +### General Steps + * If you are using storage class to dynamically provision the ASM disks, you do not need to allocate block devices. Otherwise, need to allocate block devices to worker node where Oracle restart databae pod is running. You need to clean the new ASM disks using `dd` command. + * Update the Oracle Restart Custom Resource. Edit the custom resource YAML (oraclerestarts.database.oracle.com) to reference the new PVCs/disks under the appropriate ASM configuration. + +### In this Example: + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). + * The exisitng disks on the worker nodes for the ASM which are being used in Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. - * In this example, two new disks will be added to the existing Oracle Restart Database Deployment. For this purpose, the disks on the worker nodes which will be used are `/dev/disk/by-partlabel/asm-disk3` and `/dev/disk/by-partlabel/asm-disk4`. + * Skip this steps, if you are using **storage class to dynamically provision ASM disks**.In this example, two new disks will be added to the existing Oracle Restart Database Deployment. For this purpose, the disks on the worker nodes which will be used are `/dev/disk/by-partlabel/asm-disk3` and `/dev/disk/by-partlabel/asm-disk4`. + * Update the corresponding device list in initParams section. * Default value in yaml file is `autoUpdate: "true"`, which will delete and recreate the pod with updated ASM disks in the Oracle Restart Deployment. In this case, the new disks will be automatically added to the existing Diskgroup. * If the value in yaml file is set to `autoUpdate: "false"`, the Oracle Restart Database Pod is recreated, but the additional disks are `NOT` added to the ASM Disk Group automatically. ## When autoUpdate is set to true - -Use the file: [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml) for this use case as below: - -1. Deploy the `orestart_prov_asm_disk_addition.yaml` file: +* Use the file: [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml) for this use case as below: +* Deploy the `orestart_prov_asm_disk_addition.yaml` file: ```sh kubectl apply -f orestart_prov_asm_disk_addition.yaml ``` In this case, the new disks will be added to the existing Diskgroup in the Oracle Restart Database. - -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -43,20 +43,17 @@ In this case, the new disks will be added to the existing Diskgroup in the Oracl # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" ``` - 3. Samples logs in [logs](./logs/asm_addition_autoupdate_true.txt) for disk addition with option `autoUpdate: true`. + * Samples logs in [logs](./logs/asm_addition_autoupdate_true.txt) for disk addition with option `autoUpdate: true`. ## When autoUpdate is set to false - -Use the file: [orestart_prov_asm_disk_addition_autoupdate_false.yaml](./orestart_prov_asm_disk_addition_autoupdate_false.yaml) for this use case as below: - -1. Deploy the `orestart_prov_asm_disk_addition_autoupdate_false.yaml` file: +* Use the file: [orestart_prov_asm_disk_addition_autoupdate_false.yaml](./orestart_prov_asm_disk_addition_autoupdate_false.yaml) for this use case as below: +* Deploy the `orestart_prov_asm_disk_addition_autoupdate_false.yaml` file: ```sh kubectl apply -f orestart_prov_asm_disk_addition_autoupdate_false.yaml ``` In this case, new disks are added to Oracle Restart Database Object Statefulset and Pods are recreated, but this disk is not added to the ASM Disk Group. - -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -64,4 +61,4 @@ In this case, new disks are added to Oracle Restart Database Object Statefulset # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" ``` - 3. Samples logs in [logs](./logs/asm_addition_autoupdate_false.txt) for disk addition with option `autoUpdate: false`. \ No newline at end of file + * Samples logs in [logs](./logs/asm_addition_autoupdate_false.txt) for disk addition with option `autoUpdate: false`. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md b/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md index 00277350..d3dd5f2f 100644 --- a/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md @@ -1,38 +1,40 @@ # Change Memory and CPU allocation for an earlier provisioned Oracle Restart Database -In this use case, the Oracle Grid Infrastructure and Oracle Database are initially deployed automatically using Oracle Restart Controller. The responsefile is generated by the controller based on input parameters specified in the .yaml file. - -In this use case, the memory and cpu allocation for that existing Oracle Restart Database Pod is changed. - -This example uses `oraclerestart_prov_nodeports.yaml` to provision the initial Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart Node hostname -* Node Port 30007 mapped to port 1521 for Database Listener -* Persistent volumes created automatically based on specified disks for Oracle ASM storage -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. +### In this Usecase: +* You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. +* Change the memory and CPU allocation for an existing Oracle Restart Database Pod, you can do so by updating resource requests and limits in the Custom Resource YAML associated with your Oracle Restart instance. +* This example uses `oraclerestart_prov_nodeports.yaml` to provision the initial Oracle Restart Database using Oracle Restart Controller with: + + * Oracle Restart Pod + * Headless services for Oracle Restart + * Oracle Restart Node hostname + * Node Port 30007 mapped to port 1521 for Database Listener. If you are using Loadbalancer service then you will see lbservice. + * Persistent volumes created automatically based on specified disks for Oracle ASM storage + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. If you are using Storageclass then the software volume is dynamically provisioned. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. If you are using, exisitng NFS based PVC for the staged software, the pramater is `swStagePvcMountLocation` under `configParams`. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + + +### In this Example: + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. **NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. -Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_nodeports.yaml` file: +### Steps - Deploy the Oracle Restart Database +* Skip this step if you have already deployed the Oracle Restart database using storage class. +* Use the file: [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) for this use case as below: +* Update the Oracle Restart container image. In this example, we have `dbocir/oracle/database-orestart:19.3.0-slim` in [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) file to point to the container image you have built. +* Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for this use case as below: +* Deploy the `oraclerestart_prov_nodeports.yaml` file: ```sh kubectl apply -f oraclerestart_prov_nodeports.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -43,8 +45,9 @@ Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports ORACLE DATABASE IS READY TO USE =============================== ``` -3. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. -4. Use the file: [oraclerestart_prov_nodeports_mcpu_change.yaml](./oraclerestart_prov_nodeports_mcpu_change.yaml) to change the Memory and CPU allocation for the existing Oracle Restart Database Pod: +* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +### Steps - Modify the Memory and CPU in the Oracle Restart Database Pod +* Use the file: [oraclerestart_prov_nodeports_mcpu_change.yaml](./oraclerestart_prov_nodeports_mcpu_change.yaml) to change the Memory and CPU allocation for the existing Oracle Restart Database Pod: ```sh kubectl apply -f oraclerestart_prov_nodeports_mcpu_change.yaml oraclerestart.database.oracle.com/oraclerestart-sample configured @@ -52,4 +55,4 @@ Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports You will notice that the exiting Oracle Restart Database Pod will be recreated with updated Memory and CPU allocation. -5. Check Details of Kubernetes CRD Object before and after the change as in this [example](./oraclerestart_prov_nodeports_mcpu_change.txt). It also has the details of the memory and cpu inside the pod before and after the change. \ No newline at end of file +* Check Details of Kubernetes CRD Object before and after the change as in this [example](./oraclerestart_prov_nodeports_mcpu_change.txt). It also has the details of the memory and cpu inside the pod before and after the change. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md b/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md index 8da37cf5..0cdfd276 100644 --- a/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md @@ -1,39 +1,37 @@ # Change the size of Software Storage Location for an existing Oracle Restart Database -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are initially deployed automatically using Oracle Restart Controller with Custom Storage Class. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. - -In this use case, the Software Home Location for Grid Infrastructure and Database, the ASM Disks are provisioned as Persistent Volumes using custom storage class during the initial deployment. An updated .yaml file is applied to `increase` the size of the Software Home Location. - -**NOTE:** The `decrease` in the size of Software Home Location for an existing Oracle Restart Database is `not allowed`. - -This example uses `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` to initially provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart node hostname -* Node Port 30007 mapped to port 1521 for Database Listener -* Persistent volumes for ASM Disks created automatically using the Storage Class for Oracle ASM storage -* Persistent volume for Software location is created automatically using the Storage Class. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using the corresponding Persistent Volume Claim. Its size is specified by `swLocStorageSizeInGb`. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Name of Custom Storage Class is specified by `storageClass`. - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. - * When you are building the image yourself, update the image value in the `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` file to point to the container image you have built. - * The disks provisioned using custom storage class are mounted inside the Oracle Restart Pod as `/dev/asm-disk1` and `/dev/asm-disk2`. - * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. +### In this Usecase: +* You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. +* Change the software volume size where Oracle binaries are installed for an existing Oracle Restart Database Pod, you can do so by updating in the Custom Resource YAML associated with your Oracle Restart instance. +* This example uses `oraclerestart_prov_nodeports.yaml` to provision the initial Oracle Restart Database using Oracle Restart Controller with: + + * Oracle Restart Pod + * Headless services for Oracle Restart + * Oracle Restart Node hostname + * Node Port 30007 mapped to port 1521 for Database Listener. If you are using Loadbalancer service then you will see lbservice. + * Persistent volumes created automatically based on specified disks for Oracle ASM storage + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. If you are using Storageclass then the software volume is dynamically provisioned. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. If you are using, exisitng NFS based PVC for the staged software, the pramater is `swStagePvcMountLocation` under `configParams`. + * You will be using storageclass to dynamically allcate the storage. using the storage class **oci-bv**. + +### In this Example: + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). + * The disks provisioned using storageclass are mounted inside the Oracle Restart Pod as `/dev/asm-disk1` and `/dev/asm-disk2`. + * Specify the size of these devices along with names using the parameter `swLocStorageSizeInGb`. Size is by-default in GBs. **NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. -Use the file: [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` file: +### Steps - Deploy the Oracle Restart Database +* Skip this step if you have already deployed the Oracle Restart database using storage class. +* Use the file: [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) for this use case as below: +* Update the Oracle Restart container image. In this example, we have `dbocir/oracle/database-orestart:19.3.0-slim` in [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) file to point to the container image you have built. +* Deploy the `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` file: ```sh kubectl apply -f oraclerestart_prov_storage_class_before_sw_home_resize.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -44,9 +42,12 @@ Use the file: [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./or ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_storage_class_object_before_sw_home_resize.txt) -4. In order to `increase` the size of Software Home Location, you can use the updated file [oraclerestart_prov_storage_class_after_sw_home_resize.yaml](./oraclerestart_prov_storage_class_after_sw_home_resize.yaml). -5. Deploy the `oraclerestart_prov_storage_class_after_sw_home_resize.yaml` file: +* Check Details of Kubernetes CRD Object as in this [example](./orestart_storage_class_object_before_sw_home_resize.txt) + +### Steps - Update the Software Home Location in Oracle Restart Database +* In order to `increase` the size of Software Home Location, you can use the updated file [oraclerestart_prov_storage_class_after_sw_home_resize.yaml](./oraclerestart_prov_storage_class_after_sw_home_resize.yaml). +* Update the Oracle Restart container image. In this example, we have `dbocir/oracle/database-orestart:19.3.0-slim` in [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) file to point to the container image you have built. +* Deploy the `oraclerestart_prov_storage_class_after_sw_home_resize.yaml` file: ```sh $ kubectl apply -f oraclerestart_prov_storage_class_after_sw_home_resize.yaml oraclerestart.database.oracle.com/oraclerestart-sample configured diff --git a/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md b/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md index bed4bacb..cc513f10 100644 --- a/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md +++ b/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md @@ -1,41 +1,38 @@ # Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database -This use case demonstrates deleting a ASM Disk from an existing Oracle Restart Database provisioned earlier using Oracle Restart Controller. - -In this use case, the existing Oracle Restart Database Deployed on a Kubernetes Cluster is having: - -In this use case, the existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart Node hostname -* Node Port 30007 mapped to port 1521 for Database Listener -* Persistent volumes created automatically based on specified disks for Oracle ASM storage -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. - * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. +### In this usecase: + +* You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. Now, you need to remove ASM disks from the ASM storage. +* The existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: + * Oracle Restart Pod + * Headless services for Oracle Restart + * Oracle Restart Node hostname + * Node Port 30007 mapped to port 1521 for Database Listener. If you are using Loadbalancer then you will see LB service. + * Persistent volumes created automatically based on specified disks for Oracle ASM storage + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + +### In this exapmple: + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. - * Before deleting the disk, you will need to remove the disk at the ASM Level using "ALTER DISKGROUP DROP DISK" command. - * IF the disk is first NOT removed at the ASM Level and the .yaml file to delete the disk is applied, to avoid any data loss, the operator will NOT delete the disk from the Stateful Set and the Oracle Restart Database Pod will not be recreated. Operator will be in wait state until the disk is deleted at the ASM Level. + * Before deleting the disk, **you will need to remove the disk at the ASM Level** using `ALTER DISKGROUP DROP DISK` command. + * If the disk is first NOT removed at the ASM Level and the .yaml file to delete the disk is applied, to avoid any data loss, the operator will NOT delete the disk from the Stateful Set and the Oracle Restart Database Pod will not be recreated. Operator will be in wait state until the disk is deleted at the ASM Level. * In this example, out of the two disks mentioned above, the disk `/dev/disk/by-partlabel/asm-disk2` will be deleted from the existing Oracle Restart Database Deployment. +### Steps: Delete the ASM Disk Use the file: [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) for this use case as below: -1. Deploy the `orestart_prov_asm_disk_deletion.yaml` file: +* Deploy the `orestart_prov_asm_disk_deletion.yaml` file: ```sh kubectl apply -f orestart_prov_asm_disk_deletion.yaml ``` In this case, the disk will be deleted and Oracle Restart Database Pod will be recreated. -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -43,8 +40,8 @@ In this case, the disk will be deleted and Oracle Restart Database Pod will be r # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" ``` -3. Samples logs in [logs](./asm_disk_deletion.txt) when the disk `/dev/disk/by-partlabel/asm-disk2` is removed at the ASM Level first and then[orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) file is applied to remove it from the Stateful set. +* Samples logs in [logs](./asm_disk_deletion.txt) when the disk `/dev/disk/by-partlabel/asm-disk2` is removed at the ASM Level first and then[orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) file is applied to remove it from the Stateful set. -4. Samples logs in [logs](./asm_disk_deletion1.txt) when the disk `/dev/disk/by-partlabel/asm-disk2` is tried to be removed from Stateful Set using the file [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) but this disk is NOT yet removed at the ASM Level. +* Samples logs in [logs](./asm_disk_deletion1.txt) when the disk `/dev/disk/by-partlabel/asm-disk2` is tried to be removed from Stateful Set using the file [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) but this disk is NOT yet removed at the ASM Level. In this case, the Operator will be waiting for the disk to be deleted at the ASM Level. Once it detects the Disk has been deleted at the ASM Level and it is safe to proceed, it will remove the disk from the Stateful Set. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov.yaml index c798e8e2..aa1989d8 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov.yaml @@ -47,7 +47,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_loadbalancer.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_loadbalancer.yaml index 2dc17fe8..5bd64c6a 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_loadbalancer.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_loadbalancer.yaml @@ -61,7 +61,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml index c7efada6..f566767c 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml @@ -68,7 +68,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml index 1125fcff..0abd8d9d 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml @@ -72,7 +72,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml index 0757a1cf..02aa274c 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports.yaml @@ -64,7 +64,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.yaml index 71a409ac..de342075 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_nodeports_mcpu_change.yaml @@ -64,7 +64,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml index 034d7d7b..f8ba6484 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch.yaml @@ -64,7 +64,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml index ec292230..e9926f66 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml @@ -69,7 +69,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. @@ -126,5 +126,5 @@ spec: ruPatchLocation: /stage/software/19c/19.28/37957391 # Directory containing the unzipped RU patch on the NFS based Persistent Volume, Pod will also have this software in the same path oPatchLocation: /stage/software/19c # Location of the Opatch Software on the NFS based Persistent Volume, Pod will also have this software in the same path oneOffLocation: /stage/software/19c/oneoff # One-off patch files directory where all the one-off patches for GI and RDBMS Home are unzipped - gridOneOffIds: "38336965,34436514" # Comma-separated Grid one-off patch IDs + gridOneOffIds: "38336965" # Comma-separated Grid one-off patch IDs dbOneOffIds: "38336965,34436514" # Comma-separated DB one-off patch IDs \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml index 3ff71907..48546cb9 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml @@ -69,7 +69,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml index 4ef150d3..158bea75 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml @@ -67,7 +67,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml index 7f114e1d..ef2c0527 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml @@ -69,7 +69,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml index ff1fec2f..6ba159ff 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml @@ -69,7 +69,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt b/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt index 825d13d0..1d47f9fd 100644 --- a/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt +++ b/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt @@ -7,12 +7,12 @@ Annotations: OracleRestarts.database.oracle.com/old-spec: API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-09-11T04:55:37Z + Creation Timestamp: 2025-09-12T14:19:42Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 234329900 - UID: 5ed0c257-dfb3-40da-9e97-46dabf65e5ac + Resource Version: 234821692 + UID: a8f02440-e8ed-40d3-9b85-85110f4065f4 Spec: Asm Storage Details: Disks By Size: @@ -36,7 +36,7 @@ Spec: Db Sw Zip File: db_home.zip Grid Base: /u01/app/grid Grid Home: /u01/app/19c/grid - Grid One Off Ids: 38336965,34436514 + Grid One Off Ids: 38336965 Grid Response File: Grid Sw Zip File: grid_home.zip Host Sw Stage Location: /stage/software/19c/19.3.0 @@ -113,12 +113,12 @@ Status: Name: dbmc1-0 Node Details: Instance State: OPEN - Pod State: AVAILABLE + Pod State: PODAVAILABLE Cluster State: HEALTHY Mounted Devices: /dev/asm-disk1 /dev/asm-disk2 - State: AVAILABLE + State: FAILED Asm Details: Diskgroup: Disks: @@ -126,13 +126,13 @@ Status: Name: DATA Redundancy: EXTERN Conditions: - Last Transition Time: 2025-09-11T04:55:37Z + Last Transition Time: 2025-09-12T15:33:28Z Message: reconcile completed successfully Observed Generation: 1 Reason: LastReconcileCycleCompleted Status: True Type: ReconcileComplete - Last Transition Time: 2025-09-11T14:46:43Z + Last Transition Time: 2025-09-12T18:06:27Z Message: reconcile has been queued Observed Generation: 1 Reason: LastReconcileCycleQueued @@ -170,10 +170,8 @@ Status: State: AVAILABLE Events: - - +-- Patches Details for GI HOME and RDBMS HOME: [grid@dbmc1-0 ~]$ $ORACLE_HOME/OPatch/opatch lspatches -34436514;DBCA REPORTS INCORRECT MEMORY IN PODMAN CONTAINERS 38336965;OCW Interim patch for 38336965 38124772;TOMCAT RELEASE UPDATE 19.0.0.0.0 (38124772) 37962938;ACFS RELEASE UPDATE 19.28.0.0.0 (37962938) @@ -186,8 +184,8 @@ OPatch succeeded. [oracle@dbmc1-0 ~]$ $ORACLE_HOME/OPatch/opatch lspatches -37962946;OCW RELEASE UPDATE 19.28.0.0.0 (37962946) +34436514;DBCA REPORTS INCORRECT MEMORY IN PODMAN CONTAINERS +38336965;OCW Interim patch for 38336965 37960098;Database Release Update : 19.28.0.0.250715 (37960098) -OPatch succeeded. -[oracle@dbmc1-0 ~]$ \ No newline at end of file +OPatch succeeded. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml index 9a62a7b2..8f36a496 100644 --- a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition.yaml @@ -67,7 +67,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition_autoupdate_false.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition_autoupdate_false.yaml index d990a1ab..532cdc00 100644 --- a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition_autoupdate_false.yaml +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_addition_autoupdate_false.yaml @@ -67,7 +67,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml index ee75872d..a91d967b 100644 --- a/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml +++ b/docs/oraclerestart/provisioning/orestart_prov_asm_disk_deletion.yaml @@ -63,7 +63,7 @@ spec: pwdFileName: pwdfile.enc # Password file name inside secret # Image for Oracle Restart container - image: localhost/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry # Policy to pull image # Use the value as "Always" if you want use the latest version of the image from the container registry. diff --git a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md index 317f8550..a99ce094 100644 --- a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md @@ -22,7 +22,6 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will You must ensure that your Kubernetes Cluster meets the necessary requirements for Oracle Restart Database deployment. The minimum required OKE cluster version is 1.33 or higher. Refer documentation for details [Oracle Kubernetes Engine](https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm) cluster. ## Mandatory roles and privileges requirements for Oracle Restart Database Controller - Oracle Restart Database Controller uses Kubernetes objects such as :- | Resources | Verbs | @@ -46,58 +45,71 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will ## Preparing to Install Oracle Restart on OKE To prepare for the Oracle Restart Database installation, follow these steps: - * Ensure that your OKE cluster is properly configured and meets the necessary requirements. - Each pod that you deploy as part of your cluster must satisfy the minimum hardware requirements of the Oracle Restart Database and Oracle Grid Infrastructure software. If you are planning to install Oracle Grid Infrastructure and Oracle Restart database software on host local volumes exposed from your environment, then you must have at least 50 GB space allocated for the Oracle Restart on OKE Pod. - * In addition to the standard memory (RAM) required for Oracle Linux (Linux-x86-64), the Oracle Grid Infrastructure and Oracle Database instances, Oracle recommends that you provide an additional 2 GB of RAM to each Kubernetes node for the control plane. - * Database storage for Oracle Database on OKE must use Oracle Automatic Storage Management (Oracle ASM) configured on block storage. + * Ensure that your OKE cluster is properly configured and meets the necessary requirements. + Each pod that you deploy as part of your cluster must satisfy the minimum hardware requirements of the Oracle Restart Database and Oracle Grid Infrastructure software. If you are planning to install Oracle Grid Infrastructure and Oracle Restart database software on host local volumes exposed from your environment, then you must have at least 50 GB space allocated for the Oracle Restart on OKE Pod. + * In addition to the standard memory (RAM) required for Oracle Linux (Linux-x86-64), the Oracle Grid Infrastructure and Oracle Database instances, Oracle recommends that you provide an additional 2 GB of RAM to each Kubernetes node for the control plane. + * Database storage for Oracle Database on OKE must use Oracle Automatic Storage Management (Oracle ASM) configured on block storage. - Prerequisite Software and Environment Versions: - * Oracle Grid Infrastructure: Release 19.28 or later - * Oracle Database: Release 19.28 or later - * Oracle Kubernetes Engine (OKE): Version 1.33 or later - * Unbreakable Enterprise Kernel (UEK): Release 7 UEKR7 (Kernel Release 5.15.0-202.135.2.el9uek.x86_64) or later - * Oracle Linux: For Operator, control plane, and worker nodes—Oracle Linux 8 (Linux-x86-64) Update 10 or later + * Prerequisite Software and Environment Versions: + * Oracle Grid Infrastructure: Release 19.28 or later + * Oracle Database: Release 19.28 or later + * Oracle Kubernetes Engine (OKE): Version 1.33 or later + * Unbreakable Enterprise Kernel (UEK): Release 7 UEKR7 (Kernel Release 5.15.0-202.135.2.el9uek.x86_64) or later + * Oracle Linux: For Operator, control plane, and worker nodes—Oracle Linux 8 (Linux-x86-64) Update 10 or later ## Worker Node Preparation for Oracle Restart on OKE - * When configuring your Worker Nodes, follow these guidelines, and see the configuration Oracle used for testing. - Each OKE worker node must have sufficient resources to support the intended number of Oracle Database Pods, each of which must meet at least the minimum requirements for Oracle Grid Infrastructure servers hosting an Oracle Database node. - * The Oracle Database Pods in this example configuration were created on the machine `10.0.10.58` for Oracle Restart: - - Oracle Database Node - - Worker Node: 10.0.10.58 - - Container Pod: dbmc1-0 - * Worker node has the following configuration: - - RAM:32GB - - Operating system disk: - - Ensure that your storage has at least the following available space: - - / (Root): 40 GB - - /scratch/orestart/: 80 GB (the worker node directory which will be used for /u01 to store Oracle Grid Infrastructure and Oracle Database homes) - - /var/lib/containers: 100 GB xfs - - Oracle Linux 8.10 with Unbreakable Enterprise Kernel Release UEKR7 (Kernel Release 5.15.0-308.179.6.el8uek.x86_64) or later - - Network Cards: - - ens3: Default network interface. The Oracle Restart pod will use this network interface for cluster public network. - **Notes** - - You only need to configure the following storage locations on the worker node **if you are not using a StorageClass** for the software volume and ASM disks. - - /scratch/software/stage (the worker node directory will be exposed as local host volume to the Oracle Restart pod for staging Oracle Grid Infrastructure and Oracle RDBMS software) - - Block devices: - - You can use any supported storage options for Oracle Grid Infrastructure. Ensure that your storage has at least the following space available: - - /dev/oracleoci/oraclevdd (50 GB) - - /dev/oracleoci/oraclevde (50 GB) - - **Make sure the devices you are using for ASM Storage are cleared of any data from a previous usage or installation.** - - **If you want to use the devices from the worker nodes for ASM storage, you will need to mark any default StorageClass as non-default in your Kubernetes Cluster.** - + * When configuring your Worker Nodes, follow these guidelines, and see the configuration Oracle used for testing. + Each OKE worker node must have sufficient resources to support the intended number of Oracle Database Pods, each of which must meet at least the minimum requirements for Oracle Grid Infrastructure servers hosting an Oracle Database node. + * The Oracle Database Pods in this example configuration were created on the machine `10.0.10.58` for Oracle Restart: + - Oracle Database Node + - Worker Node: 10.0.10.58 + - Container Pod: dbmc1-0 + * Worker node has the following configuration: + - RAM:32GB + - Operating system disk: + - Ensure that your storage has at least the following available space: + - / (Root): 40 GB + - /scratch/orestart/: 80 GB (the worker node directory which will be used for /u01 to store Oracle Grid Infrastructure and Oracle Database homes) + - /var/lib/containers: 100 GB xfs + - Oracle Linux 8.10 with Unbreakable Enterprise Kernel Release UEKR7 (Kernel Release 5.15.0-308.179.6.el8uek.x86_64) or later + - Network Cards: + - ens3: Default network interface. The Oracle Restart pod will use this network interface for cluster public network. + - Skip this step **if you are using a StorageClass**, You only need to configure the following storage locations on the worker node for the software volume and ASM disks. + - /scratch/software/stage (the worker node directory will be exposed as local host volume to the Oracle Restart pod for staging Oracle Grid Infrastructure and Oracle RDBMS software) + - Block devices: + - You can use any supported storage options for Oracle Grid Infrastructure. Ensure that your storage has at least the following space available: + - /dev/oracleoci/oraclevdd (50 GB) + - /dev/oracleoci/oraclevde (50 GB) + + **Notes** + - Make sure the devices you are using for ASM Storage are cleared of any data from a previous usage or installation.** + - If you want to use the devices from the worker nodes for ASM storage, you will need to mark any default StorageClass as non-default in your Kubernetes Cluster.** ### Download Oracle Grid Infrastructure and Oracle Database Software - You need to download the Oracle Grid Infrastructure and Oracle Database software and stage it on the worker nodes. The Oracle Restart Database Controller will handle mounting the software inside the Pod. + You need to download the Oracle Grid Infrastructure and Oracle Database software, you can make it available inside the Pod using following steps: + + * Prepare the Persistent Volume (PVC) for Staging: + * If you plan to use an existing Persistent Volume Claim (PVC) as a staging area, ensure it is pre-created and available before deployment. + * NFS (Network File System) is commonly used as a backing storage for staging, since it allows multiple pods/nodes to access the staged files. + * Copy Software to Staging Location: + * Download the Oracle Grid Infrastructure and Oracle Database installation media from Oracle's official sources. + * Copy (stage) these files onto the PVC (or NFS volume) you intend to use. If you opt to stage on a worker node's local storage, ensure sufficient space and security practices. + * Mounting in Pod: + * The Oracle Restart Database Controller is responsible for mounting the staged software into the Pod at runtime, making the installers available for use during installation or upgrade tasks. + * You should define the appropriate volume mounts in your deployment YAML manifests to ensure the pod sees the staged content. + * The Oracle Database Container does not contain any Oracle software binaries. Download the following software from the [Oracle Technology Network](https://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html). - Oracle Grid Infrastructure 19c (19.28) for Linux x86-64 - Oracle Database 19c (19.28) for Linux x86-64 - ### Prepare the Worker Node for Oracle Restart Deployment - To prepare the worker node for Oracle Restart Database deployment, follow these steps: - Before you install Oracle Restart inside the OKE Pods, you must update the system configuration. - - 1. Log in as root. - 2. Use the vim editor to update `/etc/sysctl.conf` parameters to the following values: + ### Worker Node Preparation Checklist + Preparing the worker node is a critical foundation for a secure and successful Oracle Restart Database deployment in a Kubernetes environmen. These steps need to be executed by Kuberernetes administrator as root user on worker nodes and follow these steps: + + #### System Requirements + * Verify OS and Kernel Versions: Ensure your node’s operating system and kernel version are supported by Oracle Database and Kubernetes. + * Resource Allocation: Confirm the node has sufficient CPU, memory, and storage for Oracle Grid and Database. + #### Kernel and System Settings + * Use the vim editor to update `/etc/sysctl.conf` parameters to the following values: ```sh fs.file-max = 6815744 net.core.rmem_default = 262144 @@ -107,16 +119,16 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will fs.aio-max-nr = 1048576 vm.nr_hugepages=16384 ``` - 3. Run the following commands: + * Run the following commands: * `# sysctl -a` * `# sysctl –p` - 4. Verify that the swap memory is disabled by running: + * Verify that the swap memory is disabled by running: ```sh # free -m ..... Swap: 0 0 0 ``` - 5. Enable kernel parameters at the Kubelet level, so that kernel parameters can be set at the Pod level. This is a one-time activity. + * Enable kernel parameters at the Kubelet level, so that kernel parameters can be set at the Pod level. This is a one-time activity. * In the `/etc/systemd/system/kubelet.service.d/00-default.conf` file of OKE Worker nodes, add below environment variable: ```txt Environment="KUBELET_EXTRA_ARGS=--fail-swap-on=false --allowed-unsafe-sysctls='kernel.shm*,net.*,kernel.sem'" @@ -134,44 +146,43 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will * Restart Kubelet: `# systemctl restart kubelet` * Check the Kubelet status: `# systemctl status kubelet` - 6.Skip this step if you are using a StorageClass.Otherwise, create the necessary mount points on the worker node. These mount points will be used by the Oracle Restart pod for Oracle Grid Infrastructure and RDBMS Home, as well as for the software staging location. + * Skip this step **if you are using a StorageClass**.Otherwise, create the necessary mount points on the worker node. These mount points will be used by the Oracle Restart pod for Oracle Grid Infrastructure and RDBMS Home, as well as for the software staging location. * On worker node: + `# mkdir -p /scratch/orestart/` + `# mkdir -p /scratch/software/stage` - - For the case where you are installing Oracle Base Release with RU Patch, create the required mount points for Base Release Software, for the location to unzip the RU Patch etc: - * For Example, for Release 19c with 19.28 RU, on worker node: + * For the case where you are installing Oracle Base Release with RU Patch, create the required mount points for Base Release Software, for the location to unzip the RU Patch etc: + * For Example, for Release 19c with 19.28 RU, on worker node: + `# mkdir -p /stage/software/19c/19.3.0` + `# mkdir -p /stage/software/19c/19.28` - 7. Download the Oracle Grid Infrastructure and Oracle RDBMS Software .zip files. Copy those files to the worker node at the staging location `/scratch/software/stage/` + * Download the Oracle Grid Infrastructure and Oracle RDBMS Software .zip files. Copy those files to the worker node at the staging location `/scratch/software/stage/` - ### Set up SELinux Module on Worker Nodes + #### Security Controls Traditional Unix security uses discretionary access control (DAC). SELinux is an example of mandatory access control. SELinux restricts many commands from the Oracle Restart Pod that are not allowed to run, which results in permission denied errors. To avoid such errors, you must create an SELinux policy package to allow certain commands. To set up the SELinux module on the worker node, follow these steps as `root` user to create an SELinux policy package on the worker node: - 1. Verify that SELinux is enabled on the Worker node. For example: + * Verify that SELinux is enabled on the Worker node. For example: ```sh # getenforce enforcing ``` - 2. Install the SELinux devel package on the Worker nodes: `# dnf install selinux-policy-devel` - 3. Create a file `oradb-oke.te` under `/var/opt` on the Worker nodes with the below content: + * Install the SELinux devel package on the Worker nodes: `# dnf install selinux-policy-devel` + * Create a file `oradb-oke.te` under `/var/opt` on the Worker nodes with the below content: ```sh module oradb-oke 1.0; require { - type kernel_t; - class system syslog_read; - type container_runtime_t; - type container_init_t; - class file getattr; - type container_file_t; - type lib_t; - type textrel_shlib_t; - type bin_t; - class file { execmod execute map setattr }; + type kernel_t; + class system syslog_read; + type container_runtime_t; + type container_init_t; + class file getattr; + type container_file_t; + type lib_t; + type textrel_shlib_t; + type bin_t; + class file { execmod execute map setattr }; } #============= container_init_t ============== @@ -183,28 +194,25 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will allow container_init_t textrel_shlib_t:file setattr; allow container_init_t kernel_t:system syslog_read; ``` -4. Create and install the policy package: - * `# cd /var/opt` - * `make -f /usr/share/selinux/devel/Makefile oradb-oke.pp` - * `semodule -i oradb-oke.pp` - * `semodule -l | grep oradb-oke` -5. Configure the SELinux context for the required worker node directory and files: - * On worker node: - + `# semanage fcontext -a -t container_file_t /scratch/orestart` - + `# sudo restorecon -vF /scratch/orestart` - + `# semanage fcontext -a -t container_file_t /scratch/software/stage/grid_home.zip` - + `# sudo restorecon -vF /scratch/software/stage/grid_home.zip` - + `# semanage fcontext -a -t container_file_t /scratch/software/stage/db_home.zip` - + `# sudo restorecon -vF /scratch/software/stage/db_home.zip` - -**Note:** Change these paths and file names as per location of your environment for setting Oracle Restart and names of the software .zip files. - -**Note:** In case of Oracle Base Release and RU Patch software, you will need to run similar commands for the corresponding .zip files. - -**Note**: To use Oracle Restart Database Controller, ensure that your system is provisioned with a supported Kubernetes release. Refer to the [Release Status Section](../../README.md#release-status). +* Create and install the policy package: + * `# cd /var/opt` + * `make -f /usr/share/selinux/devel/Makefile oradb-oke.pp` + * `semodule -i oradb-oke.pp` + * `semodule -l | grep oradb-oke` +* Skip this step, **if you are using storgaclass**. Configure the SELinux context for the required worker node directory and files: + * On worker node: + + `# semanage fcontext -a -t container_file_t /scratch/orestart` + + `# sudo restorecon -vF /scratch/orestart` + + `# semanage fcontext -a -t container_file_t /scratch/software/stage/grid_home.zip` + + `# sudo restorecon -vF /scratch/software/stage/grid_home.zip` + + `# semanage fcontext -a -t container_file_t /scratch/software/stage/db_home.zip` + + `# sudo restorecon -vF /scratch/software/stage/db_home.zip` + + **Notes:** + * Change these paths and file names as per location of your environment for setting Oracle Restart and names of the software .zip files. + * In case of Oracle Base Release and RU Patch software, you will need to run similar commands for the corresponding .zip files. ## Create a namespace for the Oracle Restart Setup - Create a Kubernetes namespace named `orestart`. All the resources belonging to the Oracle Restart Database will be provisioned in this namespace named `orestart`. For example: ```sh @@ -217,7 +225,6 @@ Create a Kubernetes namespace named `orestart`. All the resources belonging to t If you want, you can choose any name for the namespace to deploy Oracle Restart Database. ## Install Cert Manager and Setup Access Permissions - Before using Oracle Restart Database Controller, ensure you have completed the prerequisites which includes: * The installation of cert-manager @@ -226,12 +233,13 @@ Before using Oracle Restart Database Controller, ensure you have completed the p Refer to the section [Prerequisites](../../../README.md#prerequisites) ## Additional requirements for OpenShift Security Context Constraints +When you deploy Oracle Restart Database using the Oracle Restart Database Controller on an OpenShift cluster, you must account for OpenShift's stricter security model, especially around Security Context Constraints (SCCs). Apart from the same steps listed above for setting up Oracle Restart Database using Oracle Restart Database Controller on an OKE Cluster, there are some additional steps required to setup Oracle Restart Database using Oracle Restart Database Controller on an OpenShift Cluster. OpenShift requires additional Security Context Constraints (SCC) for deploying and managing the `oraclerestarts.database.oracle.com` resource. To create the appropriate SCCs before deploying the `oraclerestarts.database.oracle.com` resource, complete these steps: - 1. Apply the file [custom-kubeletconfig.yaml](../../config/samples/orestart/custom-kubeletconfig.yaml) with cluster-admin user privileges. + * Apply the file [custom-kubeletconfig.yaml](../../config/samples/orestart/custom-kubeletconfig.yaml) with cluster-admin user privileges. ```sh oc apply -f custom-kubeletconfig.yaml @@ -248,13 +256,13 @@ OpenShift requires additional Security Context Constraints (SCC) for deploying a **Note:** OpenShift recommends that you should not deploy in namespaces starting with `kube`, `openshift` and the `default` namespace. - 2. Create service account to be used for Openshift cluster to be used for `oraclerestarts.database.oracle.com` resource. + * Create service account to be used for Openshift cluster to be used for `oraclerestarts.database.oracle.com` resource. ```sh oc create serviceaccount oraclerestart -n orestart ``` Note: We are using `oraclerestart` as service account name, you can change and make sure to use same in yaml file while creating `oraclerestarts.database.oracle.com` resource and step 3 below. - 3. Apply the file [custom-scc.yaml](../../config/samples/orestart/custom-scc.yaml) with cluster-admin user privileges. + * Apply the file [custom-scc.yaml](../../config/samples/orestart/custom-scc.yaml) with cluster-admin user privileges. ```sh oc apply -f custom-scc.yaml @@ -262,7 +270,6 @@ OpenShift requires additional Security Context Constraints (SCC) for deploying a ``` ## Deploy Oracle Database Operator - After you have completed the prerequisite steps, you can install the operator. To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the previous step. ```sh @@ -277,14 +284,13 @@ For more details, please refer to [Install Oracle DB Operator](../../../README.m You can build this image using instructions provided in below documentation: * [Building Oracle RAC Database Container Slim Image](https://github.com/oracle/docker-images/blob/main/OracleDatabase/RAC/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) -After the image is ready, push it to your Container Images Repository, so that you can pull this image during Oracle Restart Database provisioning.. +After the image is ready, push it to your private container images repository, so that you can pull this image during Oracle Restart Database provisioning.. -**Note**: In the Oracle Restart Database provisioning sample .yaml files, we are using Oracle Restart Database slim image `localhost/oracle/database-orestart:19.3.0-slim`. +**Note**: In the Oracle Restart Database provisioning sample .yaml files, we are using Oracle Restart Database slim image `odbcir/oracle/database-orestart:19.3.0-slim`. ## Create a Kubernetes secret for the Oracle Restart Database installation owner for the Oracle Restart Database Deployment + * Create a Kubernetes secret named `db-user-pass` in `orestart` namespace using these steps: [Create Kubernetes Secret](./create_kubernetes_secret_for_db_user.md) + * Once the setup completes, you can change the password inside the pod for Oracle sys user. + * Create a Kubernetes secret named `ssh-key-secret` in `orestart` namespace using these steps: [Create Kubernetes Secret for SSH Key](./create_kubernetes_secret_for_ssh_setup.md) -Create a Kubernetes secret named `db-user-pass` in `orestart` namespace using these steps: [Create Kubernetes Secret](./create_kubernetes_secret_for_db_user.md) - -Create a Kubernetes secret named `ssh-key-secret` in `orestart` namespace using these steps: [Create Kubernetes Secret for SSH Key](./create_kubernetes_secret_for_ssh_setup.md) - -After you have the above prerequsites completed, you can proceed to the next section for your environment to provision the Oracle Restart Database. +After you have the above prerequsites completed, you can proceed to the next section for your environment to provision the Oracle Restart Database. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md index 0bebaed6..f3290e57 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md @@ -1,35 +1,32 @@ # Provisioning an Oracle Restart Database - -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is -generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. - -This example uses `oraclerestart_prov.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: - -* 1 Node for Oracle Restart -* Headless services for Oracle Restart. - * Oracle Database Node hostname. -* Persistent volumes created automatically based on specified disks for Oracle ASM Storage. -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. +### In this Usecase: +* In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. +* This example uses `oraclerestart_prov.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: + * Oracle Restart Pod + * Headless services for Oracle Restart. + * Oracle Database Node hostname. + * Persistent volumes created automatically based on specified disks for Oracle ASM Storage. + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + +### In this Example: + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `dbocir/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov.yaml` file to point to the container image you have built. * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. **NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. -Use the file: [oraclerestart_prov.yaml](./oraclerestart_prov.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov.yaml` file: +### Steps: Deploy Oracle Restart Database +* Use the file: [oraclerestart_prov.yaml](./oraclerestart_prov.yaml) for this use case as below: +* Deploy the `oraclerestart_prov.yaml` file: ```sh kubectl apply -f oraclerestart_prov.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -40,5 +37,5 @@ Use the file: [oraclerestart_prov.yaml](./oraclerestart_prov.yaml) for this use ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_object.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +* Check Details of Kubernetes CRD Object as in this [example](./orestart_object.txt) +* efer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md index 26f00a19..c443a259 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md @@ -1,38 +1,34 @@ # Provisioning an Oracle Restart Database with Load Balancer -In this use case, the Oracle Grid Infrastructure and Oracle Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the controller based on input parameters specified in the .yaml file. - -In this use case, Oracle Restart is deployed with Load Balancer Service and ou will be able to make a remote database connection using the Load Balancer target port 1521. - -This example uses `oraclerestart_prov_loadbalancer.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart Node hostname -* Load Balancer Service with target port 1521 -* Persistent volumes created automatically based on specified disks for Oracle ASM storage -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. - * When you are building the image yourself, update the image value in the `oraclerestart_prov_loadbalancer.yaml` file to point to the container image you have built. +### In this Usecase: +* In this use case, the Oracle Grid Infrastructure and Oracle Database are deployed automatically using Oracle Restart Controller. +* When Oracle Restart is deployed using the Oracle Restart Controller with a Load Balancer Service, the database is exposed externally via the cloud provider’s (e.g., Oracle Cloud) load balancer. This setup allows you to connect remotely to the database using the load balancer’s external IP and the target port—commonly 1521 (the Oracle default). +* This example uses `oraclerestart_prov_loadbalancer.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + * Oracle Restart Pod + * Headless services for Oracle Restart + * Oracle Restart Node hostname + * Load Balancer Service with target port 1521 + * Persistent volumes created automatically based on specified disks for Oracle ASM storage + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + +### In this Example: + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. **NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. - -Use the file: [oraclerestart_prov_loadbalancer.yaml](./oraclerestart_prov_loadbalancer.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_loadbalancer.yaml` file: +### Steps: Provision Oracle Restart Database +* Use the file: [oraclerestart_prov_loadbalancer.yaml](./oraclerestart_prov_loadbalancer.yaml) for this use case as below: +* When you are building the image yourself, update the image value in the `oraclerestart_prov_loadbalancer.yaml` file to point to the container image you have built. +* Deploy the `oraclerestart_prov_loadbalancer.yaml` file: ```sh kubectl apply -f oraclerestart_prov_loadbalancer.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -43,5 +39,5 @@ Use the file: [oraclerestart_prov_loadbalancer.yaml](./oraclerestart_prov_loadba ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_loadbalancer_object.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +* Check Details of Kubernetes CRD Object as in this [example](./orestart_loadbalancer_object.txt) +* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md index 621a691d..25dbae89 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md @@ -1,39 +1,34 @@ # Provisioning an Oracle Restart Database with NodePort Service - -In this use case, the Oracle Grid Infrastructure and Oracle Database are deployed automatically using Oracle Restart Controller. The responsefile is generated by the controller based on input parameters specified in the .yaml file. - -In this use case, Oracle Restart is deployed with Node Port Service. - -A node port exposes the service on a static port on the node IP address and NodePorts are in the 30000-32767 range by default. -This example uses `oraclerestart_prov_nodeports.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart Node hostname -* Node Port 30007 mapped to port 1521 for Database Listener -* Persistent volumes created automatically based on specified disks for Oracle ASM storage -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. +### In this Usecase: +* In this use case, the Oracle Grid Infrastructure and Oracle Database are deployed automatically using Oracle Restart Controller. In this use case, Oracle Restart is deployed with Node Port Service. +* A node port exposes the service on a static port on the node IP address and NodePorts are in the 30000-32767 range by default. +* This example uses `oraclerestart_prov_nodeports.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + * Oracle Restart Pod + * Headless services for Oracle Restart + * Oracle Restart Node hostname + * Node Port 30007 mapped to port 1521 for Database Listener + * Persistent volumes created automatically based on specified disks for Oracle ASM storage + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + +### In this Example: + * Oracle Restart Database slim image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `dbocir/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. **NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. -Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_nodeports.yaml` file: +### Steps: Deploy Oracle Restart Database +* Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for this use case as below: +* Deploy the `oraclerestart_prov_nodeports.yaml` file: ```sh kubectl apply -f oraclerestart_prov_nodeports.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -44,5 +39,5 @@ Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_nodeport_object.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +* Check Details of Kubernetes CRD Object as in this [example](./orestart_nodeport_object.txt) +* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md index 19313d32..efcd10c9 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md @@ -1,41 +1,37 @@ # Provisioning an Oracle Restart Database with RU Patch on FileSystem - -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is -generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. - -In this use case, before the installation of Oracle Restart, the GRID HOME and RDBMS HOME are patched with the provided `RU Patch`. - -This example uses `oraclerestart_prov_rupatch.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart Node hostname -* Node Port 30007 mapped to port 1521 for Database Listener -* Persistent volumes created automatically based on specified disks for Oracle ASM storage -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. -* Directory where the **Release Update (RU) patch** has been unzipped is specified by `ruPatchLocation`. - * For Example: To apply the 19.28 RU Patch `37957391`, if you have unzipped the RU Patch .zip file `p37957391_190000_Linux-x86-64.zip` to location `/scratch/software/19c/19.28/` on the worker node, then set this parameter `ruPatchLocation` to value `/scratch/software/ru_patch/37957391`. - * Set the permission on the unzipped RU software directory to be `755` recursively. - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. +### In this Usecase: +* In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. +* In this use case, before the installation of Oracle Restart, the GRID HOME and RDBMS HOME are patched with the provided `RU Patch`. +* This example uses `oraclerestart_prov_rupatch.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + * Oracle Restart Pod + * Headless services for Oracle Restart + * Oracle Restart Node hostname + * Node Port 30007 mapped to port 1521 for Database Listener + * Persistent volumes created automatically based on specified disks for Oracle ASM storage + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + * Directory where the **Release Update (RU) patch** has been unzipped is specified by `ruPatchLocation`. + * For Example: To apply the 19.28 RU Patch `37957391`, if you have unzipped the RU Patch .zip file `p37957391_190000_Linux-x86-64.zip` to location `/scratch/software/19c/19.28/` on the worker node, then set this parameter `ruPatchLocation` to value `/scratch/software/ru_patch/37957391`. + * Set the permission on the unzipped RU software directory to be `755` recursively. + +### In this Example: + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `dbocir/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_rupatch.yaml` file to point to the container image you have built. * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. **NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. -Use the file: [oraclerestart_prov_rupatch.yaml](./oraclerestart_prov_rupatch.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_rupatch.yaml` file: +### Steps: Deploy Oracle Restart Database +* Use the file: [oraclerestart_prov_rupatch.yaml](./oraclerestart_prov_rupatch.yaml) for this use case as below: +* Deploy the `oraclerestart_prov_rupatch.yaml` file: ```sh kubectl apply -f oraclerestart_prov_rupatch.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -46,5 +42,5 @@ Use the file: [oraclerestart_prov_rupatch.yaml](./oraclerestart_prov_rupatch.yam ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_rupatch_object.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +* Check Details of Kubernetes CRD Object as in this [example](./orestart_rupatch_object.txt) +* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md index 7ba19604..9e2aa071 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md @@ -1,43 +1,38 @@ # Provisioning an Oracle Restart Database with RU Patch and One Offs with Custom Storage Class - -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is -generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. - -This example uses `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart node hostname -* Node Port 30007 mapped to port 1521 for Database Listener. -* Persistent volumes created automatically based on specified disks for Oracle ASM storage. -* Software location and Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS). -* Namespace: `orestart` -* Mount point for the Software Stage Location inside the Pod is specified by `swStagePvcMountLocation`. -* Staged Software location inside the Pod is specified by `hostSwStageLocation`. It is assumed that the Grid Infrastructure and RDBMS Binaries are already available in this path on the Persistent Volume used. -* The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `storageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. -* Directory where the **Release Update (RU) patch** has been unzipped is specified by `ruPatchLocation`. - * For Example: To apply the 19.28 RU Patch `37957391`, if you have unzipped the RU Patch .zip file `p37957391_190000_Linux-x86-64.zip` to location `/scratch/software/19c/19.28/` on the worker node, then set this parameter `ruPatchLocation` to value `/scratch/software/ru_patch/37957391`. - * Set the permission on the unzipped RU software directory to be `755` recursively. -* Directory, where latest opatch zip file is available, is specified by `oPatchLocation`. -* Directory, where unzipped One-off patch files are available, is specified by `oneOffLocation`. -* Specify Comma-separated one-off patch IDs to be applied to the GI HOME using `gridOneOffIds`. For Example: `gridOneOffIds: "38336965,34436514"` -* Specify Comma-separated one-off patch IDs to be applied to the RDBMS HOME using `dbOneOffIds`. For Example: `dbOneOffIds: "38336965,34436514"` - - -In this example, - * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `localhost/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` file to point to your own container registry base container image. +### In this Usecase: +* In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. +* This example uses `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + * Oracle Restart Pod + * Headless services for Oracle Restart + * Oracle Restart node hostname + * Node Port 30007 mapped to port 1521 for Database Listener. + * Persistent volumes created automatically based on specified disks for Oracle ASM storage. + * Software location and Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS). + * Namespace: `orestart` + * Mount point for the Software Stage Location inside the Pod is specified by `swStagePvcMountLocation`. + * Staged Software location inside the Pod is specified by `hostSwStageLocation`. It is assumed that the Grid Infrastructure and RDBMS Binaries are already available in this path on the Persistent Volume used. + * The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `storageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. + * Directory where the **Release Update (RU) patch** has been unzipped is specified by `ruPatchLocation`. + * For Example: To apply the 19.28 RU Patch `37957391`, if you have unzipped the RU Patch .zip file `p37957391_190000_Linux-x86-64.zip` to location `/scratch/software/19c/19.28/` on the worker node, then set this parameter `ruPatchLocation` to value `/scratch/software/ru_patch/37957391`. + * Set the permission on the unzipped RU software directory to be `755` recursively. + * Directory, where latest opatch zip file is available, is specified by `oPatchLocation`. + * Directory, where unzipped One-off patch files are available, is specified by `oneOffLocation`. + * Specify Comma-separated one-off patch IDs to be applied to the GI HOME using `gridOneOffIds`. For Example: `gridOneOffIds: "38336965,34436514"` + * Specify Comma-separated one-off patch IDs to be applied to the RDBMS HOME using `dbOneOffIds`. For Example: `dbOneOffIds: "38336965,34436514"` + +### In this example, + * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `dbocir/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` file to point to your own container registry base container image. * The disks procured on the containers using customer storage class for the Oracle Restart storage are `/dev/asm-disk1` and `/dev/asm-disk2`. * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. - -Use the file: [oraclerestart_prov_rupatch_oneoff_storageclass.yaml](./oraclerestart_prov_rupatch_oneoff_storageclass.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` file: +### Steps: Deploy Oracle Restart Database +* Use the file: [oraclerestart_prov_rupatch_oneoff_storageclass.yaml](./oraclerestart_prov_rupatch_oneoff_storageclass.yaml) for this use case as below: +* Deploy the `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` file: ```sh kubectl apply -f oraclerestart_prov_rupatch_oneoff_storageclass.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -48,4 +43,4 @@ Use the file: [oraclerestart_prov_rupatch_oneoff_storageclass.yaml](./oraclerest ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_db_rupatch_oneoffs_object.txt) \ No newline at end of file +* Check Details of Kubernetes CRD Object as in this [example](./orestart_db_rupatch_oneoffs_object.txt) \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md index b9a8f2ce..1c0b9edc 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md @@ -1,21 +1,17 @@ # Provisioning an Oracle Restart Database with multiple diskgroups - -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files and Recovery Area Files. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. - -This example uses `oraclerestart_prov_multiple_diskgroups.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: - -* 1 Node for Oracle Restart -* Headless services for Oracle Restart. - * Oracle Database Node hostname. -* Persistent volumes created automatically based on specified disks for Oracle ASM Storage. -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. +### In this Usecase: +* In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files and Recovery Area Files. +* This example uses `oraclerestart_prov_multiple_diskgroups.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: + * Oracle Restart Database Pod + * Headless services for Oracle Restart. + * Oracle Database Node hostname. + * Persistent volumes created automatically based on specified disks for Oracle ASM Storage. + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. +### In this Example: + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `dbocir/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_multiple_diskgroups.yaml` file to point to the container image you have built. * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` to`/dev/disk/by-partlabel/asm-disk6`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. @@ -23,15 +19,14 @@ In this example, * The Diskgroup for Database files is specified by `dbDataFileDestDg` and the disks on the worker nodes for this diskgroup are specified by `dbAsmDeviceList`. * The Diskgroup for Recovery Area files is specified by `dbRecoveryFileDest` and the disks on the worker nodes for this diskgroup are specified by `recoAsmDeviceList`. - -Use the file: [oraclerestart_prov_multiple_diskgroups.yaml](./oraclerestart_prov_multiple_diskgroups.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_multiple_diskgroups.yaml` file: +### Steps: Deploy Oracle Restart Database +* Use the file: [oraclerestart_prov_multiple_diskgroups.yaml](./oraclerestart_prov_multiple_diskgroups.yaml) for this use case as below: +* Deploy the `oraclerestart_prov_multiple_diskgroups.yaml` file: ```sh kubectl apply -f oraclerestart_prov_multiple_diskgroups.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -42,5 +37,5 @@ Use the file: [oraclerestart_prov_multiple_diskgroups.yaml](./oraclerestart_prov ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./oraclerestart_prov_multiple_diskgroups.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +* Check Details of Kubernetes CRD Object as in this [example](./oraclerestart_prov_multiple_diskgroups.txt) +* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md index ad7541a5..5b6b6e32 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md @@ -1,21 +1,19 @@ # Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy - -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files and Recovery Area Files. Different Disk Groups have different redundancy levels. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. +# In this Usecase: +The Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files and Recovery Area Files. Different Disk Groups have different redundancy levels. This example uses `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: - -* 1 Node for Oracle Restart -* Headless services for Oracle Restart. - * Oracle Database Node hostname. -* Persistent volumes created automatically based on specified disks for Oracle ASM Storage. -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * Oracle Restart Pod + * Headless services for Oracle Restart. + * Oracle Database Node hostname. + * Persistent volumes created automatically based on specified disks for Oracle ASM Storage. + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + +### In this example, + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` file to point to the container image you have built. * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` to`/dev/disk/by-partlabel/asm-disk6`. * Specify the size of disk devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. @@ -26,15 +24,14 @@ In this example, * Redundancy level for the diskgroup with Database files is mentioned by `dbAsmDiskDgRedundancy`. * Redundancy level for the diskgroup with Recovery files is mentioned by `recoAsmDiskDgRedudancy`. - -Use the file: [oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml](./oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` file: +### Steps: Deploy Oracle Restart Database +* Use the file: [oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml](./oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml) for this use case as below: +* Deploy the `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` file: ```sh kubectl apply -f oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -45,5 +42,5 @@ Use the file: [oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml](./or ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./oraclerestart_prov_multiple_diskgroups_with_redundancy.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +* Check Details of Kubernetes CRD Object as in this [example](./oraclerestart_prov_multiple_diskgroups_with_redundancy.txt) +* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md index ab8e35c1..c290530b 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md @@ -1,25 +1,23 @@ # Provisioning an Oracle Restart Database with RU Patch on Existing PVC -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. The responsefile is -generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. In this case, no storage location from the worker node is used for ASM Disks or GI HOME or RDBMS HOME or Software Staging etc. - -This example uses `oraclerestart_prov_rupatch_pvc.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart Node hostname -* Node Port 30007 mapped to port 1521 for Database Listener -* Persistent volumes for ASM Disks created automatically using the Storage Class (specified using `storageClass`) for Oracle ASM storage. -* Software location and Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS) -* Namespace: `orestart` -* Mount point for the Software Stage Location inside the Pod is specified by `swStagePvcMountLocation`. -* Staged Software location inside the Pod is specified by `hostSwStageLocation`. It is assumed that the Grid Infrastructure and RDBMS Binaries are already available in this path on the Persistent Volume used. -* The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `storageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. -* Location where the `RU Patch` has been unzipped on the mounted PV is specified by `ruPatchLocation`. -* Path to the Opatch Software compatible with the RU Patch is specified using `oPatchLocation`. - -In this example, - * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. +### In this Usecase: +* The Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this case, no storage location from the worker node is used for ASM Disks or GI HOME or RDBMS HOME or Software Staging etc. +* This example uses `oraclerestart_prov_rupatch_pvc.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + * Oracle Restart Database pod + * Headless services for Oracle Restart + * Oracle Restart Node hostname + * Node Port 30007 mapped to port 1521 for Database Listener + * Persistent volumes for ASM Disks created automatically using the Storage Class (specified using `storageClass`) for Oracle ASM storage. + * Software location and Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS) + * Namespace: `orestart` + * Mount point for the Software Stage Location inside the Pod is specified by `swStagePvcMountLocation`. + * Staged Software location inside the Pod is specified by `hostSwStageLocation`. It is assumed that the Grid Infrastructure and RDBMS Binaries are already available in this path on the Persistent Volume used. + * The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `storageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. + * Location where the `RU Patch` has been unzipped on the mounted PV is specified by `ruPatchLocation`. + * Path to the Opatch Software compatible with the RU Patch is specified using `oPatchLocation`. + +### In this Example: + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `dbocir/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_rupatch_pvc.yaml` file to point to the container image you have built. * Use the file [nfs_pv_stage_vol.yaml](./nfs_pv_stage_vol.yaml) to mount the Network File System as a Persistent Volume named `pv-stage-vol1`. It is assumed this NFS has the required GI and RDBMS Base Software, unzipped RU Patch binaries and Opatch binaries in the specified location. In current case, an OCI File System is used with its export path as `/stage` and Mount Target IP as `10.0.10.212`. * The disk names for Oracle ASM storage are specified as `/dev/asm-disk1` and `/dev/asm-disk2`. These will be mounted using the Persistent Volumes. @@ -27,14 +25,14 @@ In this example, **NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. -Use the file: [oraclerestart_prov_rupatch_pvc.yaml](./oraclerestart_prov_rupatch_pvc.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_rupatch_pvc.yaml` file: +### Steps: Deploy Oracle Restart Database +* Use the file: [oraclerestart_prov_rupatch_pvc.yaml](./oraclerestart_prov_rupatch_pvc.yaml) for this use case as below: +* Deploy the `oraclerestart_prov_rupatch_pvc.yaml` file: ```sh kubectl apply -f oraclerestart_prov_rupatch_pvc.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -45,5 +43,5 @@ Use the file: [oraclerestart_prov_rupatch_pvc.yaml](./oraclerestart_prov_rupatch ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_rupatch_pvc_object.txt) -4. Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +* Check Details of Kubernetes CRD Object as in this [example](./orestart_rupatch_pvc_object.txt) +* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md index 62b950ac..463f0aec 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md @@ -1,23 +1,20 @@ # Provisioning an Oracle Restart Database with Custom Storage Class - -In this use case, the Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller with Custom Storage Class. The responsefile is generated by the Oracle Restart Controller based on input parameters specified in the .yaml file. - -In this use case, the ASM Disks are provisioned as Persistent Volumes using custom storage class during the deployment. - -This example uses `oraclerestart_prov_storage_class.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: - -* 1 Node Oracle Restart -* Headless services for Oracle Restart - * Oracle Restart node hostname -* Node Port 30007 mapped to port 1521 for Database Listener -* Persistent volumes for ASM Disks created automatically using the Storage Class for Oracle ASM storage -* Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node -* Namespace: `orestart` -* Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. -* Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. -* Name of Custom Storage Class is specified by `storageClass`. - -In this example, +### In this Usecase: +* The Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller with Custom Storage Class. +* In this use case, the ASM Disks are provisioned as Persistent Volumes using custom storage class during the deployment. +* This example uses `oraclerestart_prov_storage_class.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: + * Oracle Restart Pod + * Headless services for Oracle Restart + * Oracle Restart node hostname + * Node Port 30007 mapped to port 1521 for Database Listener + * Persistent volumes for ASM Disks created automatically using the Storage Class for Oracle ASM storage + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. + * Name of Custom Storage Class is specified by `storageClass`. + +### In this Example: * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_storage_class.yaml` file to point to the container image you have built. * The disks provisioned using custom storage class are mounted inside the Oracle Restart Pod as `/dev/asm-disk1` and `/dev/asm-disk2`. @@ -25,14 +22,14 @@ In this example, **NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. -Use the file: [oraclerestart_prov_storage_class.yaml](./oraclerestart_prov_storage_class.yaml) for this use case as below: - -1. Deploy the `oraclerestart_prov_storage_class.yaml` file: +### Steps: Deploy Oracle Restart Database +* Use the file: [oraclerestart_prov_storage_class.yaml](./oraclerestart_prov_storage_class.yaml) for this use case as below: +* Deploy the `oraclerestart_prov_storage_class.yaml` file: ```sh kubectl apply -f oraclerestart_prov_storage_class.yaml oraclerestart.database.oracle.com/oraclerestart-sample created ``` -2. Check the status of the deployment: +* Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: kubectl get all -n orestart @@ -43,4 +40,4 @@ Use the file: [oraclerestart_prov_storage_class.yaml](./oraclerestart_prov_stora ORACLE DATABASE IS READY TO USE =============================== ``` -3. Check Details of Kubernetes CRD Object as in this [example](./orestart_storage_class_object.txt) +* Check Details of Kubernetes CRD Object as in this [example](./orestart_storage_class_object.txt) diff --git a/docs/privateai/Oracle_Machine_Learning_AI_models.htm b/docs/privateai/Oracle_Machine_Learning_AI_models.htm deleted file mode 100644 index 4f2cf923..00000000 --- a/docs/privateai/Oracle_Machine_Learning_AI_models.htm +++ /dev/null @@ -1,320 +0,0 @@ - - - - - - - - - - - - -

- -

Oracle Machine Learning AI models

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Model name

-
-

Description

-
-

Dimensions

-
-

Size (MB)

-
-

Last Updated

-
-

Download

-
-

all_MiniLM_L12_v2

-
-

This is a SentenceTransformers model suitable for semantic similarity search and - clustering use cases.

-
-

384

-
-

116.92

-
-

7/15/2024

-
-

link

-
-

multilingual_e5_small

-
-

This is a multilingual sentence-transformer model - that supports 100. Supports text embedding, translation, or multilingual - understanding. Optimized for efficiency, it can handle multiple languages - while maintaining a smaller size for faster inference and reduced - computational requirements.

-
-

384

-
-

76.8

-
-

9/9/2024

-
-

link

-
-

clip_vit_base_patch32_txt

-
-

This is a text encoder that produces embeddings compatible with the CLIP image encoder. Enables text-to-image search and similarity matching between text descriptions and images.

-
-

512

-
-

243.57

-
-

1/29/2021

-
-

link

-
-

clip_vit_base_patch32_img

-
-

This is an image encoder that produces embeddings compatible with the CLIP text encoder. Enables image-to-text search and similarity matching between images and text descriptions.

-
-

512

-
-

335.38

-
-

1/29/2021

-
-

link

-
- -

 

- -

To learn how -to install these in your Oracle Database, see:

- -

        -Documentation: Oracle -AI Vector Search User's Guide

- -

        -Blog: Now -Available! Pre-built Embedding Generation model for Oracle Database 23ai

- -
- - - - \ No newline at end of file diff --git a/docs/privateai/README.md b/docs/privateai/README.md deleted file mode 100644 index d9a88f66..00000000 --- a/docs/privateai/README.md +++ /dev/null @@ -1,136 +0,0 @@ -# Using Oracle PrivateAI Controller with Oracle Database Operator for Kubernetes - -Oracle PrivateAI Controller automates the deployment and usage of the Oracle PrivateAI Container. AI Container project aims to deliver to customers a lightweight containerized web service that provides an interface for performing inference on ONNX format models via REST. The AI container will help offload expensive AI computation (e.g., embedding generation) outside the database. This addresses requests made by AI Vector Search customers who would prefer to use the database compute primarily for indexing and search. - -Kubernetes provides infrastructure building blocks, such as compute, storage, and networks. Kubernetes makes the infrastructure available as code. which are a key resource object for managing and updating applications. Deployments enable declarative updates, ensuring a specified number of application replicas are running, and handle scaling, rolling updates, and rollbacks automatically. - - -The PrivateAI controller in Oracle Database Operator deploys Oracle PrivateAI container as a deploymentset in the Kubernetes clusters, using Oracle PrivateAI Container image. The Oracle PrivateAI controller provides end-to-end automation of Oracle PrivateAI Container deployment in Kubernetes clusters. - -## Using Oracle Database Operator PrivateAI Controller - -Following sections provide the details for deploying Oracle PrivateAI container using Oracle Database Operator PrivateAI Controller with different use cases: - -* [Prerequisites for running Oracle PrivateAI Controller](#prerequisites-for-running-oracle-privartai-controller) -* [Quick Start](#quick-start) -* [Accessing PrivateAI Container Pods](#accessing-the-privateai-container-pod-in-kubernetes) -* [Debugging and Troubleshooting](#debugging-and-troubleshooting) - -**Note:** Before proceeding to the next section, you must complete the instructions given in each section, based on your enviornment, before proceeding to next section. - -## Prerequisites for running Oracle PrivartAI Controller - -**IMPORTANT:** You must make the changes specified in this section before you proceed to the next section. - -### 1. Kubernetes Cluster: To deploy Oracle PrivateAI controller with Oracle Database Operator, you need a Kubernetes Cluster which can be one of the following: - -* A Cloud-based Kubernetes cluster, such as [OCI on Container Engine for Kubernetes (OKE)](https://www.oracle.com/cloud-native/container-engine-kubernetes/) or -* An On-Premises Kubernetes Cluster, such as [Oracle Linux Cloud Native Environment (OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) cluster. - -To use Oracle PrivateAI Controller, ensure that your system is provisioned with a supported Kubernetes release. Refer to the [Release Status Section](../../README.md#release-status). - -#### Mandatory roles and privileges requirements for Oracle PrivateAI Controller - - Oracle PrivateAI Controller uses Kubernetes objects such as :- - - | Resources | Verbs | - | --- | --- | - | Pods | create delete get list patch update watch | - | Containers | create delete get list patch update watch | - | PersistentVolumeClaims | create delete get list patch update watch | - | Services | create delete get list patch update watch | - | Secrets | create delete get list patch update watch | - | Events | create patch | - -### 2. Deploy Oracle Database Operator - -To deploy Oracle Database Operator in a Kubernetes cluster, go to the section [Install Oracle DB Operator](../../README.md#install-oracle-db-operator) in the README.md, and complete the operator deployment before you proceed further. If you have already deployed the operator, then proceed to the next section. - -**IMPORTANT:** Make sure you have completed the steps for [Role Binding for access management](../../README.md#role-binding-for-access-management) as well before installing the Oracle DB Operator. - -### 3. Oracle PrivateAI Container Image -The pre-built preivateAI container image is available on [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::) and fully supported by Oracle for production uses. - -### 4. Create a namespace for the Oracle PrivateAI Setup - - Create a Kubernetes namespace named `pai`. All the resources belonging to the Oracle PrivateAI topology setup will be provisioned in this namespace named `pai`. For example: - - ```sh - #### Create the namespace - kubectl create ns pai - - #### Check the created namespace - kubectl get ns - ``` - -### 5. Create a configmap for the Oracle PrivateAI Deployment - -In this step, a configmap will be created which has the details of the AI Model File. The configmap will be created according to the type of the PrivateAI Deployment. Below are examples of configmap used in the later examples: - -- [Configmap using Single AI Model with HTTPS URL](./configmap_single_model_https.md) -- [Configmap using Multiple AI Models with HTTPS URL](./configmap_multi_model_https.md) -- [Configmap using Multiple AI Models on File System](./configmap_multi_model_filesystem.md) - -### 6. Reserve LoadBalancer Public IP - -- The SSL certificate used during the PrivateAI Container Deployment will need a common name(hostname or IP) to be specified during the certificate creation. -- Later, for a secure communication with the PrivateAI Container Deployed in a Kuberentes Cluster, the client will use the same `cert.pem` file and will send the connection request to same hostname or IP. -- If you are deploying PrivateAI Container on an OKE cluster, you will need to reserve a Public IP in OCI. - OCI allows provisioning a Public LoadBalancer and assigning a reserved public ip to it. Please the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). -- To reserve a Public IP in OCI, refer to [OCI LoadBalancer Documentation](https://docs.public.oneportal.content.oci.oraclecloud.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm) for the details. -- Once you have reserved the Public IP, use this Public IP as `Common Name` while generating the openssl certificate in the next step. - -**NOTE:** This step is required only if you are going to deploy the PrivateAI Container on OKE Cluster using OCI Public LoadBalancer. - -**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available as of now. Please check the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). - -### 7. Create Kubernetes secret for the Oracle PrivateAI Deployment - -**IMPORTANT:** Make sure the version of `openssl` in the Oracle PrivateAI image is compatible with the `openssl` version on the machine where you will run the openssl commands to generate the encrypted password file during the deployment. - -Create a file `oml-ssl-pwd` with the password you want to use. This password will be used in the next step. The script [pai_secret.sh](./provisioning/pai_secret.sh) has the command to generate the required keys and an SSL certificate. - -Use the Shell Script `pai_secret.sh` to create the required secrets for the Oracle PrivateAI Container Deployment. Run this file as below and enter the password when prompted. - -In case of the Public LoadBalancer, use the reserved Public IP as the `common name`. - -```sh -cd provisioning -echo "" > oml-ssl-pwd -./pai_secret.sh -``` - -**NOTE:** In case of the Internal LoadBalancer, we can not use a reserved Private IP. In this case, you can leave `common name` empty. - -Use below command to check the Kubernetes Secret Created: - -```sh -kubectl get secret -n pai -kubectl describe secret paisecret -n pai -``` - -After you have the above prerequisites completed, you can proceed to the next section for your environment to deploy the Oracle PrivateAI Controller. - - -## Quick Start - -There are multiple use case possible for deploying the PrivateAI container in Kubernetes Cluster covered by below examples: - -**NOTE:** All the below deployments are using an OCI OKE Cluster. - -- [PrivateAI Container using OCI Public LoadBalancer](./deploy_privateai_publiclb.md) -- PrivateAI Container using an Internal LoadBalancer - - [PrivateAI Container using Single AI Model with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_internallb.md) - - [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md) - - [Add New Model](./deploy_privateai_multi_model_https_internallb_add_model.md) - - [Remove an existing model](./deploy_privateai_multi_model_https_internallb_remove_model.md) - - [PrivateAI Container using Multiple AI Models on File System and an Internal LoadBalancer](./deploy_privateai_multi_model_filesystem_internallb.md) - -## Accessing the PrivateAI Container Pod in Kubernetes - -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace in Kuberentes Cluster and you have the Reserved Public IP of the LoadBalancer. - -Please refer to [this page](./access_privateai.md) for the details to access the PrivateAI Container Pod in Kubernetes. - -## Debugging and Troubleshooting - -Please refer to [this page](./debug_privateai.md) for the details to access the PrivateAI Container Pod in Kubernetes. diff --git a/docs/privateai/access_privateai.md b/docs/privateai/access_privateai.md deleted file mode 100644 index c4e84844..00000000 --- a/docs/privateai/access_privateai.md +++ /dev/null @@ -1,35 +0,0 @@ -# Accessing the PrivateAI Container Pod in Kubernetes - -**IMPORTANT:** To use this example, you must have an existing Oracle PrivateAI Container Deployment in the `pai` namespace, and you must have the following: -- The Reserved Public IP for the Public LoadBalancer -- The Private IP for the Internal LoadBalancer -- The AI Model details that you want to be used in the URL to access - -## Accessing the PrivateAI Container in Kubernetes using REST Calls -Complete the following steps: - -1. Retrieve the API Key - -When you have used the file [pai_secret.sh](./provisioning/pai_secret.sh) during the deployment, you have a file named `api-key` generated in the same location. Copy this `api-key` file to the machine where you want to run the API Call to the Model Endpoint. - -2. Keep the LoadBalancer Reserved Public IP or the Private IP ready. You can use this IP in the API Endpoint call in the next step. - -3. If we assume the Loadbalancer Reserved Public IP from the previous step is `129.xxx.xxx.xxx`, you can use the following example command to see how to make an API Endpoint Call: - ```sh - curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_l6_txt/score - ``` - -**NOTE:** If you have a Private LoadBalancer, then use the Internal IP in place of the IP `129.xxx.xxx.xxx` in the preceding example. - -## Accessing the PrivateAI Container using REST Calls with SSL certificate - -To use SSL authentication while accessing the PrivateAI Container in Kubernetes using SSL certificate, complete the following additional steps: - -1. Copy the `cert.pem` generated when you ran the `pai_secret.sh` script to the machine where you want to run the API Call to the Model Endpoint. -2. Use this key file to run the following modified example command to make an API Endpoint Call: - ```sh - curl --cacert cert.pem --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://129.xxx.xxx.xxx:443/omlmodels/all_minilm_l6_txt/score - ``` -**NOTE:** -- If you have a Private LoadBalancer, then use the Internal IP in place of the IP `129.xxx.xxx.xxx` in the preceding example. -- Replace the details of the AI Model in the URL used in the exmaple with the Model deployed. \ No newline at end of file diff --git a/docs/privateai/api_endpoint.md b/docs/privateai/api_endpoint.md deleted file mode 100644 index a424c1ce..00000000 --- a/docs/privateai/api_endpoint.md +++ /dev/null @@ -1,16 +0,0 @@ -# Test Oracle PrivateAI Container Deployment using API Endpoint - -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment in the `pai` namespace. - -Complete the following steps: - -1. Retrieve API Key - -When you have used the file [pai_secret.sh](./pai_secret.sh) during the deployment, the file `api-key` is generated in the same location. Copy this `api-key` file to the machine where you want to run the API Call to the Model Endpoint. - -2. Obtain the Loadbalancer External IP from the existing deployment of Oracle PrivateAI Container for the service `service/pai-sample-svc`. You can use this IP in the API Endpoint call in the next step. - -3. If we assume the Loadbalancer Extenral IP from the last step is `141.xxx.xxx.xxx`, then the following commmand is an example of how to make an API Endpoint Call: - ```sh - curl -k --noproxy '*' -v -X POST --header "Content-Type: application/json" --header "Authorization: Bearer `cat /home/opc/api-key`" -d '{"input": {"textList":["The quick brown fox jumped over the fence.","Another test sentence"]}}' https://141.xxx.xxx.xxx:443/omlmodels/all_minilm_v6/score - ``` \ No newline at end of file diff --git a/docs/privateai/configmap_multi_model_filesystem.md b/docs/privateai/configmap_multi_model_filesystem.md deleted file mode 100644 index 53a88833..00000000 --- a/docs/privateai/configmap_multi_model_filesystem.md +++ /dev/null @@ -1,17 +0,0 @@ -# Configmap using Multiple AI Models on File System - -Create a `config.json` file to create a configmap. This file has the HTTPS link for the AI Model File. - -You can use the example file [multi_model_filesystem_config.json](./provisioning/multi_model_filesystem_config.json). - -Rename the file `multi_model_filesystem_config.json` to `config.json`. - -Create a configmap using that renamed file. Example: -```sh -kubectl create configmap multiconfigjson --from-file=config.json -n pai -``` - -You can check the details of the configmap. Example: -```sh -kubectl get configmap -n pai -``` \ No newline at end of file diff --git a/docs/privateai/configmap_multi_model_https.md b/docs/privateai/configmap_multi_model_https.md deleted file mode 100644 index 45a95508..00000000 --- a/docs/privateai/configmap_multi_model_https.md +++ /dev/null @@ -1,17 +0,0 @@ -# Configmap using Multiple AI Models with HTTPS URL - -Create a `config.json` file to create a configmap. This file has the HTTPS link for the AI Model File. - -You can use the example file [multi_model_https_config.json](./provisioning/multi_model_https_config.json). - -Rename the file `multi_model_https_config.json` to `config.json`. - -Create a configmap using the above file as below: -```sh -kubectl create configmap multiconfigjson --from-file=config.json -n pai -``` - -You can check the details of the configmap as below: -```sh -kubectl get configmap -n pai -``` \ No newline at end of file diff --git a/docs/privateai/configmap_single_model_https.md b/docs/privateai/configmap_single_model_https.md deleted file mode 100644 index 73b459c4..00000000 --- a/docs/privateai/configmap_single_model_https.md +++ /dev/null @@ -1,17 +0,0 @@ -# Configmap using Single AI Model with HTTPS URL - -Create a `config.json` file to create a configmap. This file has the HTTPS link for the AI Model File. - -You can use the example file [single_model_https_config.json](./provisioning/single_model_https_config.json). - -Rename the file `single_model_https_config.json` to `config.json`. - -Create a configmap using the renamed file. Example: -```sh -kubectl create configmap omlconfigjson --from-file=config.json -n pai -``` - -You can check the details of the configmap. Example: -```sh -kubectl get configmap -n pai -``` \ No newline at end of file diff --git a/docs/privateai/create_oci_fss_based_pvc.md b/docs/privateai/create_oci_fss_based_pvc.md deleted file mode 100644 index 363f929c..00000000 --- a/docs/privateai/create_oci_fss_based_pvc.md +++ /dev/null @@ -1,31 +0,0 @@ -# Create OCI FSS based PVC - -The Persistent Volume (PV) is created using `oci-fss` Storage Class. For more information, see [Creating Persistent Volume Claims](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingpersistentvolumeclaim_Provisioning_PVCs_on_FSS.htm) for OCI File Storage. A Persistent Volume Claim (PVC) is created for this PV. To use the PV with `oci-fss` Storage Class, you must create an OCI File System Storage and a Mount Target in OCI before creating the PV. - -**Important:** For the OKE Cluster Nodes to access the FSS Mount Target, the VCN of the OKE Cluster and the File System Storage in OCI must be the same. - -Complete the following steps: - -1. Provision an OCI File Systems in the same VCN that is used by the OKE Cluster on which you want to deploy the PrivateAI Container. For more information, see [Create File System](https://docs.oracle.com/en-us/iaas/Content/File/Tasks/create-file-system.htm) for the details of this step. - -2. After the OCI File System is created, obtain the details of the `Export Path` and `Mount Target` (the IP and OCID of Mount Target). This information is required in the next step. - -3. Create a YAML file for the Storage Class. To see how to do this, refer to the following example file: [StorageClass.yaml](./provisioning/StorageClass.yaml). You will need to provide the `OCID` of the mount target in this file. - -4. Create a YAML file for the Persistent Volume (PV). This configuration will use the `oci-fss` storage class. To see how to do this, refer to the following example file: [oke-pv.yaml](./provisioning/oke-pv.yaml). You will need to provide the `Export Path` and Mount Target `IP` in this file. - -5. Create a YAML file for the Persistent Volume Claim (PVC). In this file, we use the PV created in the previous step. To see how to do this, refer to the following example file: [oke-pvc.yaml](./provisioning/oke-pvc.yaml) - -6. Apply the YAML files you have created in the preceding steps to create the PVC using `oci-fss` based File System: - ```sh - kubectl apply -f StorageClass.yaml - kubectl apply -f oke-pv.yaml - kubectl apply -f oke-pvc.yaml - ``` - -7. Check the details of the PVC created using `oci-fss` based File System: - ```sh - kubectl get sc - kubectl get pv -n pai - kubectl get pvc -n pai - ``` \ No newline at end of file diff --git a/docs/privateai/debug_privateai.md b/docs/privateai/debug_privateai.md deleted file mode 100644 index c02a2204..00000000 --- a/docs/privateai/debug_privateai.md +++ /dev/null @@ -1,14 +0,0 @@ -# Debug and Triubleshoot the PrivateAI Container Pod in Kubernetes - -You can use the below commands to debug and troubleshoot the issues listed in this document: - -## To check the logs of the PrivateAI Container Pod - -Use the below command to get the logs of the PrivateAI Container Pod deployed in the Kubernetes Cluster using PrivateAI Controller: - ```sh - - Get the name of the PrivateAI Container Pod deployed in the namespace "pai" - kubectl get pod -n pai - - - Get the logs of the PrivateAI Container Pod deployed in the namespace "pai" - kubectl logs -f pod/ -n pai - ``` \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_internallb.md b/docs/privateai/deploy_privateai_internallb.md deleted file mode 100644 index 8b06d1d7..00000000 --- a/docs/privateai/deploy_privateai_internallb.md +++ /dev/null @@ -1,46 +0,0 @@ -# Deploy PrivateAI in OKE cluster using Internal LB - -Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. - -**IMPORTANT:** Ensure that you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. - -**NOTE:** The option to reserve a Private IP and use that IP with an OCI Internal LoadBalancer is not available at this time. For more information, see [the OCI documentation about configuring load balancers](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). - -If you want to use the OCI Internal LoadBalancer, then you must complete the following steps: - -1. Deploy the [pai_sample_internallb.yaml](./provisioning/pai_sample_internallb.yaml) file: - ```sh - kubectl apply -f pai_sample_internallb.yaml - ``` - This will provision the PrivateAI Container in the OKE cluster using Internal LoadBalancer with Ephemeral Private IP. - -2. Check the status of the deployment and note the IP under field `EXTERNAL-IP` for `service/pai-sample-svc`. - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n pai - - # Check the logs of a particular pod. For example, to check status of pod "pai-sample-b669d7897-nkkhz": - kubectl logs pod/pai-sample-b669d7897-nkkhz -n pai - ``` - -In this case, the internal LoadBalancer is created as an OCI load balancer with a private IP address, hosted on the subnet specified for load balancers when the OKE cluster was created. - -With our example, we want to create the internal LoadBalancer as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created. To do this, you must add the following annotations in the `.yaml` file: - -```sh - pailbAnnotation: - # Specify the OCID of the alternate Subnet - service.beta.kubernetes.io/oci-load-balancer-subnet1: "ocid1.subnet.oc1..aaaaaa....vdfw" -``` - -**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. To avoid a hostname mismatch error while using the `cert.pem` file to make a authenticated connection, we must replace this SSL certificate with a new certificate that has the `common name` set to the IP of the Internal LoadBalancer. - -3. Use the file [pai_secret_new.sh](./pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While using this script, use the IP from Step 2 for `common name` while generating the SSL certificate. - -4. Apply the modified file [pai_sample_internallb_replace_cert.yaml](./provisioning/pai_sample_internallb_replace_cert.yaml) to replace the Internal LoadBalancer Certificate: - ```sh - kubectl apply -f pai_sample_internallb_replace_cert.yaml - ``` -**NOTE:** This step will result in termination of the existing PrivateAI Container Pod and creation of new Pod. The Internal LoadBalancer IP will not change. - -5. After this change, you are now able to access the PrivateAI Container using the Internal LoadBalancer IP using an authenticated connection, which uses the `cert.pem` file from the new SSL certificate. \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_multi_model_filesystem_internallb.md b/docs/privateai/deploy_privateai_multi_model_filesystem_internallb.md deleted file mode 100644 index 4061da85..00000000 --- a/docs/privateai/deploy_privateai_multi_model_filesystem_internallb.md +++ /dev/null @@ -1,68 +0,0 @@ -# Deploy PrivateAI in OKE cluster using Multiple AI Models on File System and an Internal LoadBalancer - -Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. The PrivateAI Container is deployed using multiple AI Models and the model files are accessible to the PrivateAI Pod using Mounted Persistent Volume(PV). In this example, the deployment uses the YAML file based on `OCI OKE` cluster. - -Please refer to [Create OCI FSS based PVC](./create_oci_fss_based_pvc.md) for the steps to create an `oci-fss` based PVC to use in the current case. - -**IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. - -**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available as of now. Please check the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). - -If you want to use the OCI Internal LoadBalancer, then you will need to follow the below steps: - -1. Refer to the file [Oracle Machine Learning AI models](./Oracle_Machine_Learning_AI_models.htm) to get details of the AI Model files. Download the files and upload them to the OCI File System you have created in above step. For Example: You can mount the same File System to an OCI Compute VM in the same VCN using below entry in `/etc/fstab` file - ```sh - 10.0.XX.XX:/oml_models /mnt nfs defaults 0 0 - ``` - -2. Make sure you have created the [configmap](./configmap_multi_model_filesystem.md) - -3. Deploy the [pai_sample_multi_model_filesystem_internallb.yaml](./provisioning/pai_sample_multi_model_filesystem_internallb.yaml) file: - ```sh - kubectl apply -f pai_sample_multi_model_filesystem_internallb.yaml - ``` - This will provision the PrivateAI Container in the OKE cluster using Internal LoadBalancer with Ephemeral Private IP. - - The PrivateAI Pod will mount the PVC `/oml/models` which is created earlier using `oci-fss` storage class. - -4. Check the status of the deployment and note the IP under field `EXTERNAL-IP` for `service/pai-sample-svc`. - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n pai - - # Check the logs of a particular pod: - kubectl logs -f -n pai $(kubectl get pod -n pai -l app.kubernetes.io/name=pai-sample -o jsonpath='{.items[0].metadata.name}') - ``` - -In this case, the internal LoadBalancer is created as an OCI load balancer with a private IP address, hosted on the subnet specified for load balancers when the OKE cluster was created. - -In case, you want the internal LoadBalancer to be created as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created, you need to add following annotations in the above .yaml file: - -```sh - pailbAnnotation: - # Specify the OCID of the alternate Subnet - service.beta.kubernetes.io/oci-load-balancer-subnet1: "ocid1.subnet.oc1..aaaaaa....vdfw" -``` - -**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. In order to avoid a hostname mismatch error while using the `cert.pem` file to make a authenicated connection, we will need to replace this SSL certificate with a new certificate which has the `common name` set to the IP of the Internal LoadBalancer. - -5. Use the file [pai_secret_new.sh](./provisioning/pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While using this script, use the IP noted in Step 2 for `common name` while generating the SSL certificate. - -```sh -cd provisioning -./pai_secret_new.sh -``` - -6. Apply the modified file [pai_sample_multi_model_https_internallb_replace_cert.yaml](./provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml) to replace the Internal LoadBalancer Certificate: -```sh -kubectl apply -f pai_sample_multi_model_https_internallb_replace_cert.yaml -``` -**NOTE:** This step will result in termination of the existing PrivateAI Container Pod and creation of new Pod while the Internal LoadBalancer IP will not change. - -7. After this change, you will be able to access the PrivateAI Container using the Internal LoadBalancer IP using an authenticated connection using the `cert.pem` file from the new SSL certificate. - -**NOTE:** The file `/oml/config/config.json` inside the running Kubernetes Pod will have the details of the AI Models currently Deployed. You can use the below steps to confirm: -```sh -kubectl exec -it -n pai $(kubectl get pod -n pai -l app.kubernetes.io/name=pai-sample -o jsonpath='{.items[0].metadata.name}') -- /bin/bash -cat /oml/config/config.json -``` \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_multi_model_https_internallb.md b/docs/privateai/deploy_privateai_multi_model_https_internallb.md deleted file mode 100644 index bf03eb03..00000000 --- a/docs/privateai/deploy_privateai_multi_model_https_internallb.md +++ /dev/null @@ -1,58 +0,0 @@ -# Deploy PrivateAI in OKE cluster using Multiple AI Models with HTTPS URL and an Internal LoadBalancer - -Deploy Oracle PrivateAI Container on your Cloud-based Kubernetes cluster. The PrivateAI Container is deployed using multiple AI Models and HTTPS URLs for those model files are provided using a configmap. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. - -**IMPORTANT:** Make sure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. - -**NOTE:** The option to reserve a Private IP and use that with an OCI Internal LoadBalancer is not available at this time. For more information, see [Configuring Load Balancers](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengconfiguringloadbalancersnetworkloadbalancers-subtopic.htm). - -To use the OCI Internal LoadBalancer, complete the following steps: - -1. Ensure you have created the [configmap](./configmap_multi_model_https.md) -2. Deploy the [pai_sample_multi_model_https_internallb.yaml](./provisioning/pai_sample_multi_model_https_internallb.yaml) file: - ```sh - kubectl apply -f pai_sample_multi_model_https_internallb.yaml - ``` - This will provision the PrivateAI Container in the OKE cluster using Internal LoadBalancer with Ephemeral Private IP. - -3. Check the status of the deployment and note the IP under field `EXTERNAL-IP` for `service/pai-sample-svc`. - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n pai - - # Check the logs of a particular pod: - kubectl logs -f -n pai $(kubectl get pod -n pai -l app.kubernetes.io/name=pai-sample -o jsonpath='{.items[0].metadata.name}') - ``` - -In this case, the internal LoadBalancer is created as an OCI load balancer with a private IP address, hosted on the subnet specified for load balancers when the OKE cluster was created. - -If you want the internal LoadBalancer to be created as an OCI load balancer with a private IP address, hosted on the alternative subnet to the one specified for load balancers when the OKE cluster was created, then you must add following annotations to the .yaml file: - -```sh - pailbAnnotation: - # Specify the OCID of the alternate Subnet - service.beta.kubernetes.io/oci-load-balancer-subnet1: "ocid1.subnet.oc1..aaaaaa....vdfw" -``` - -**NOTE:** At this stage, the SSL certificate used in the deployment has the `common name` as empty. In order to avoid a hostname mismatch error while using the `cert.pem` file to make a authenicated connection, you must replace this SSL certificate with a new certificate that has the `common name` set to the IP of the Internal LoadBalancer. - -4. Use the file [pai_secret_new.sh](./provisioning/pai_secret_new.sh) to generate a new Kubernetes secret `paisecretnew`. While configuring this script, use the IP noted in Step 2 for the `common name` when generating the SSL certificate. - -```sh -cd provisioning -./pai_secret_new.sh -``` - -5. Apply the modified file [pai_sample_multi_model_https_internallb_replace_cert.yaml](./provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml) to replace the Internal LoadBalancer Certificate: - ```sh - kubectl apply -f pai_sample_multi_model_https_internallb_replace_cert.yaml - ``` -**NOTE:** This step will result in termination of the existing PrivateAI Container Pod and creation of new Pod while the Internal LoadBalancer IP will not change. - -6. After this change, you should be able to access the PrivateAI Container using the Internal LoadBalancer IP where you obtain an authenticated connection using the `cert.pem` file from the new SSL certificate. - -**NOTE:** The file `/oml/config/config.json` inside the running Kubernetes Pod will have the details of the AI Models currently Deployed. You can use the below steps to confirm: -```sh -kubectl exec -it -n pai $(kubectl get pod -n pai -l app.kubernetes.io/name=pai-sample -o jsonpath='{.items[0].metadata.name}') -- /bin/bash -cat /oml/config/config.json -``` \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md b/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md deleted file mode 100644 index 43e93ecb..00000000 --- a/docs/privateai/deploy_privateai_multi_model_https_internallb_add_model.md +++ /dev/null @@ -1,14 +0,0 @@ -# ADD New AI Model to PrivateAI deployed in OKE cluster - -The existing PrivateAI Container is deployed in the task example [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md), using multiple AI Models. The HTTPS URLs for those model files are provided using a configmap. - -In this example, an additional AI Model is deployed to the existing deployment. - -1. Use the following command to edit the configmap used by the existing PrivateAI Deployment to add a new AI Model: - ```sh - kubectl edit configmap multiconfigjson -n pai - ``` - -2. Wait for few minutes. You should see the `configmap` updated inside the Pod. - -**NOTE:** The Pod will not be restarted in this case. The Application inside the Pod is periodically checking for Configmap changes. \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md b/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md deleted file mode 100644 index 16093c9a..00000000 --- a/docs/privateai/deploy_privateai_multi_model_https_internallb_remove_model.md +++ /dev/null @@ -1,14 +0,0 @@ -# Remove an existing Model from PrivateAI deployed in OKE cluster - -The existing PrivateAI Container is deployed in the task example [PrivateAI Container using Multiple AI Models with HTTPS URL and an Internal LoadBalancer](./deploy_privateai_multi_model_https_internallb.md) using multiple AI Models. The HTTPS URLs for those model files are provided by using a `configmap`. You can also have added additional AI Models to the existing PrivateAI Deployment using [Add New Model](./deploy_privateai_multi_model_https_internallb_add_model.md). - -In this example, we now remove an AI Model from the existing PrivateAI deployment. The steps are as follows: - -1. Use the following command to edit the `configmap` used by the existing PrivateAI Deployment to removew an existing AI Model: - ```sh - kubectl edit configmap multiconfigjson -n pai - ``` - -2. Wait for few minutes. You should see configmap updated inside the Pod. - -**NOTE:** The Pod will not be restarted in this case. The Application inside the Pod is periodically checking for Configmap changes. \ No newline at end of file diff --git a/docs/privateai/deploy_privateai_publiclb.md b/docs/privateai/deploy_privateai_publiclb.md deleted file mode 100644 index 0561e9d2..00000000 --- a/docs/privateai/deploy_privateai_publiclb.md +++ /dev/null @@ -1,23 +0,0 @@ -# Deploying Oracle PrivateAI Container using Public LoadBalancer - -Deploy Oracle PrivateAI Container on your Cloud based Kubernetes cluster. In this example, the deployment uses the YAML file based on `OCI OKE` cluster. - -**IMPORTANT:** Ensure you have completed the steps for [Prerequisites for running Oracle PrivartAI Controller](./README.md#prerequisites-for-running-oracle-privartai-controller) before using Oracle PrivateAI Controller. - -**NOTE:** Modify the file `pai_sample_publiclb.yaml` with the actual Reserved Public IP before deployment. - -In this example, we use the file [pai_sample_publiclb.yaml](./provisioning/pai_sample_publiclb.yaml) for this use case: - -1. Deploy the `pai_sample_publiclb.yaml` file: - ```sh - kubectl apply -f pai_sample_publiclb.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n pai - - # Check the logs of a particular pod. For example, to check status of pod "pai-sample-b669d7897-nkkhz": - kubectl logs pod/pai-sample-b669d7897-nkkhz -n pai - ``` - \ No newline at end of file diff --git a/docs/privateai/provisioning/StorageClass.yaml b/docs/privateai/provisioning/StorageClass.yaml deleted file mode 100644 index 435fd74d..00000000 --- a/docs/privateai/provisioning/StorageClass.yaml +++ /dev/null @@ -1,7 +0,0 @@ -kind: StorageClass -apiVersion: storage.k8s.io/v1 -metadata: - name: oci-fss -provisioner: oracle.com/oci-fss -parameters: - mntTargetId: ocid1.mounttarget.oc1.phx..................... diff --git a/docs/privateai/provisioning/multi_model_filesystem_config.json b/docs/privateai/provisioning/multi_model_filesystem_config.json deleted file mode 100644 index da60f5f2..00000000 --- a/docs/privateai/provisioning/multi_model_filesystem_config.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "environment": { - "OML_ONNX_INTRA_OP_NUM_THREADS": 1 - }, - "models": [ - { - "modelname": "all_minilm_l12_v2", - "modelfile": "all_MiniLM_L12_v2.zip", - "modelfunction": "EMBEDDING", - "cache_on_startup": true - }, - { - "modelname": "multilingual_e5_small", - "modelfile": "multilingual_e5_small.zip", - "modelfunction": "EMBEDDING", - "cache_on_startup": true - }, - { - "modelname": "clip_vit_base_patch32_txt", - "modelfile": "clip_vit_base_patch32_txt.zip", - "modelfunction": "EMBEDDING", - "cache_on_startup": true - }, - { - "modelname": "clip_vit_base_patch32_img", - "modelfile": "clip_vit_base_patch32_img.zip", - "modelfunction": "EMBEDDING", - "cache_on_startup": true - } - ] -} diff --git a/docs/privateai/provisioning/multi_model_https_config.json b/docs/privateai/provisioning/multi_model_https_config.json deleted file mode 100644 index 67124e57..00000000 --- a/docs/privateai/provisioning/multi_model_https_config.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "environment": { - "OML_ONNX_INTRA_OP_NUM_THREADS": 1 - }, - "models": [ - { - "modelname": "all_minilm_l6_txt", - "modelfile": "https://objectstorage.us-ashburn-1.oraclecloud.com/p/wBcvPtKxMfxmf7BMEcWbRWaXb249Tt1OVR6UPJRXzejXFlHu9XMkh7H6G0HV0uq9/n/intsanjaysingh/b/onnxmodel/o/ALL-MINILM-L6-V2.onnxALL-MINILM-L6-V2.onnx", - "modelfunction": "EMBEDDING" - }, - { - "modelname": "clip_vit_base32", - "modelfile": "https://adwc4pm.objectstorage.us-ashburn-1.oci.customer-oci.com/p/uOaue-BqfWh1z2jwYTJcT0k4H-rihCW0kMbkcHsK0PrHC9MFKhqfSr88n3SSQXsB/n/adwc4pm/b/OML-ai-models/o/clip_vit_base_patch32_txt_augmented.zip", - "modelfunction": "EMBEDDING" - }, - { - "modelname": "clip_vit_base_patch32_img", - "modelfile": "https://adwc4pm.objectstorage.us-ashburn-1.oci.customer-oci.com/p/FiHMIQJ8esEufIbv96CpoE2dzlZHZhn0g6Nx63LYAHELKUUB-2GUumRgVkzT-DmV/n/adwc4pm/b/OML-ai-models/o/clip_vit_base_patch32_img_augmented.zip", - "modelfunction": "EMBEDDING" - } - ] -} diff --git a/docs/privateai/provisioning/oke-pv.yaml b/docs/privateai/provisioning/oke-pv.yaml deleted file mode 100644 index 69ecc079..00000000 --- a/docs/privateai/provisioning/oke-pv.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: PersistentVolume -metadata: - name: oke-fsspv - namespace: pai -spec: - storageClassName: oci-fss - capacity: - storage: 100Gi - accessModes: - - ReadWriteMany - mountOptions: - - nosuid - nfs: - server: 10.0.XX.XX - path: "/oml_models" - readOnly: false diff --git a/docs/privateai/provisioning/oke-pvc.yaml b/docs/privateai/provisioning/oke-pvc.yaml deleted file mode 100644 index 5f771909..00000000 --- a/docs/privateai/provisioning/oke-pvc.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: oke-fsspvc - namespace: pai -spec: - storageClassName: oci-fss - accessModes: - - ReadWriteMany - resources: - requests: - storage: 100Gi - volumeName: oke-fsspv diff --git a/docs/privateai/provisioning/oml-ssl-pwd b/docs/privateai/provisioning/oml-ssl-pwd deleted file mode 100644 index 7a879403..00000000 --- a/docs/privateai/provisioning/oml-ssl-pwd +++ /dev/null @@ -1 +0,0 @@ -ORacle_19c diff --git a/docs/privateai/provisioning/pai_sample_internallb.yaml b/docs/privateai/provisioning/pai_sample_internallb.yaml deleted file mode 100644 index 0cd265a7..00000000 --- a/docs/privateai/provisioning/pai_sample_internallb.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -# Specifies the custom resource API version -apiVersion: omlai.oracle.com/v4 - -# Defines the type of resource (Private AI) -kind: PrivateAi - -# Provide identification and scoping metadata -metadata: - # Name of Private AI resource - name: pai-sample - # Kubernetes Namespace where this resource will be deployed - namespace: pai - -# Main specification for Private AI -spec: - - # Configuration for Private AI - paiConfigFile: - # Configmap having the link for the AI Model File - name: omlconfigjson - # Location inside the Pod to which the file containining the configmap will be mounted to - mountLocation: /oml/config - - # Specify the Kubernetes Secret Details - paiSecret: - name: paisecret - mountLocation: /oml/ssl - - # If you want to enable Authentication for the Private AI Access - paiEnableAuthentication: true - - # If LoadBalancer will be used - isExternalSvc: true - - # OCI Cloud Storage Class - storageClass: oci-bv - - # Container Image of the Private AI Container - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 - - # Name and port details of the Private AI Service once deployed - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - - # If Internal Load Balancer will be used - paiInternalLB: true - - # Storage Allocation for the Pod - storageSizeInGb: 30 - - # Replica Count - replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_internallb_replace_cert.yaml b/docs/privateai/provisioning/pai_sample_internallb_replace_cert.yaml deleted file mode 100644 index 93b680b1..00000000 --- a/docs/privateai/provisioning/pai_sample_internallb_replace_cert.yaml +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -# Specifies the custom resource API version -apiVersion: omlai.oracle.com/v4 - -# Defines the type of resource (Private AI) -kind: PrivateAi - -# Provide identification and scoping metadata -metadata: - # Name of Private AI resource - name: pai-sample - # Kubernetes Namespace where this resource will be deployed - namespace: pai - -# Main specification for Private AI -spec: - - # Configuration for Private AI - paiConfigFile: - # Configmap having the link for the AI Model File - name: omlconfigjson - # Location inside the Pod to which the file containining the configmap will be mounted to - mountLocation: /oml/config - - # Specify the Kubernetes Secret Details - paiSecret: - name: paisecretnew - mountLocation: /oml/ssl - - # If you want to enable Authentication for the Private AI Access - paiEnableAuthentication: true - - # If LoadBalancer will be used - isExternalSvc: true - - # OCI Cloud Storage Class - storageClass: oci-bv - - # Container Image of the Private AI Container - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 - - # Name and port details of the Private AI Service once deployed - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - - # annotation to replace the certificate for the OCI Internal LoadBalancer - pailbAnnotation: - service.beta.kubernetes.io/oci-load-balancer-tls-secret: paisecretnew - - # If Internal Load Balancer will be used - paiInternalLB: true - - # Storage Allocation for the Pod - storageSizeInGb: 30 - - # Replica Count - replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb.yaml b/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb.yaml deleted file mode 100644 index e1e2306d..00000000 --- a/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb.yaml +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -# Specifies the custom resource API version -apiVersion: omlai.oracle.com/v4 - -# Defines the type of resource (Private AI) -kind: PrivateAi - -# Provide identification and scoping metadata -metadata: - # Name of Private AI resource - name: pai-sample - # Kubernetes Namespace where this resource will be deployed - namespace: pai - -# Main specification for Private AI -spec: - - # Configuration for Private AI - paiConfigFile: - # Configmap having the links for the AI Model Files - name: multiconfigjson - # Location inside the Pod to which the file containining the configmap will be mounted to - mountLocation: /oml/config - - # Specify the Kubernetes Secret Details - paiSecret: - name: paisecret - mountLocation: /oml/ssl - - # If you want to enable Authentication for the Private AI Access - paiEnableAuthentication: true - - # If LoadBalancer will be used - isExternalSvc: true - - # OCI Cloud Storage Class - storageClass: oci-bv - - # Details of the oci-fss storage class based PVC - pvcList: - oke-fsspvc: /oml/models - - # Container Image of the Private AI Container - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:omlai-1.10.0 - - # Name and port details of the Private AI Service once deployed - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - - # If Internal Load Balancer will be used - paiInternalLB: true - - # Storage Allocation for the Pod - storageSizeInGb: 30 - - # Replica Count - replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb_replace_cert.yaml b/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb_replace_cert.yaml deleted file mode 100644 index 22f257e3..00000000 --- a/docs/privateai/provisioning/pai_sample_multi_model_filesystem_internallb_replace_cert.yaml +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -# Specifies the custom resource API version -apiVersion: omlai.oracle.com/v4 - -# Defines the type of resource (Private AI) -kind: PrivateAi - -# Provide identification and scoping metadata -metadata: - # Name of Private AI resource - name: pai-sample - # Kubernetes Namespace where this resource will be deployed - namespace: pai - -# Main specification for Private AI -spec: - - # Configuration for Private AI - paiConfigFile: - # Configmap having the links for the AI Model Files - name: multiconfigjson - # Location inside the Pod to which the file containining the configmap will be mounted to - mountLocation: /oml/config - - # Specify the Kubernetes Secret Details - paiSecret: - name: paisecretnew - mountLocation: /oml/ssl - - # If you want to enable Authentication for the Private AI Access - paiEnableAuthentication: true - - # If LoadBalancer will be used - isExternalSvc: true - - # OCI Cloud Storage Class - storageClass: oci-bv - - # Details of the oci-fss storage class based PVC - pvcList: - oke-fsspvc: /oml/models - - # Container Image of the Private AI Container - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:omlai-1.10.0 - - # Name and port details of the Private AI Service once deployed - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - - # annotation to replace the certificate for the OCI Internal LoadBalancer - pailbAnnotation: - service.beta.kubernetes.io/oci-load-balancer-tls-secret: paisecretnew - - # If Internal Load Balancer will be used - paiInternalLB: true - - # Storage Allocation for the Pod - storageSizeInGb: 30 - - # Replica Count - replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_multi_model_https_internallb.yaml b/docs/privateai/provisioning/pai_sample_multi_model_https_internallb.yaml deleted file mode 100644 index c89c2859..00000000 --- a/docs/privateai/provisioning/pai_sample_multi_model_https_internallb.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -# Specifies the custom resource API version -apiVersion: omlai.oracle.com/v4 - -# Defines the type of resource (Private AI) -kind: PrivateAi - -# Provide identification and scoping metadata -metadata: - # Name of Private AI resource - name: pai-sample - # Kubernetes Namespace where this resource will be deployed - namespace: pai - -# Main specification for Private AI -spec: - - # Configuration for Private AI - paiConfigFile: - # Configmap having the links for the AI Model Files - name: multiconfigjson - # Location inside the Pod to which the file containining the configmap will be mounted to - mountLocation: /oml/config - - # Specify the Kubernetes Secret Details - paiSecret: - name: paisecret - mountLocation: /oml/ssl - - # If you want to enable Authentication for the Private AI Access - paiEnableAuthentication: true - - # If LoadBalancer will be used - isExternalSvc: true - - # OCI Cloud Storage Class - storageClass: oci-bv - - # Container Image of the Private AI Container - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:omlai-1.10.0 - - # Name and port details of the Private AI Service once deployed - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - - # If Internal Load Balancer will be used - paiInternalLB: true - - # Storage Allocation for the Pod - storageSizeInGb: 30 - - # Replica Count - replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml b/docs/privateai/provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml deleted file mode 100644 index 3ce50165..00000000 --- a/docs/privateai/provisioning/pai_sample_multi_model_https_internallb_replace_cert.yaml +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -# Specifies the custom resource API version -apiVersion: omlai.oracle.com/v4 - -# Defines the type of resource (Private AI) -kind: PrivateAi - -# Provide identification and scoping metadata -metadata: - # Name of Private AI resource - name: pai-sample - # Kubernetes Namespace where this resource will be deployed - namespace: pai - -# Main specification for Private AI -spec: - - # Configuration for Private AI - paiConfigFile: - # Configmap having the links for the AI Model Files - name: multiconfigjson - # Location inside the Pod to which the file containining the configmap will be mounted to - mountLocation: /oml/config - - # Specify the Kubernetes Secret Details - paiSecret: - name: paisecretnew - mountLocation: /oml/ssl - - # If you want to enable Authentication for the Private AI Access - paiEnableAuthentication: true - - # If LoadBalancer will be used - isExternalSvc: true - - # OCI Cloud Storage Class - storageClass: oci-bv - - # Container Image of the Private AI Container - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:omlai-1.10.0 - - # Name and port details of the Private AI Service once deployed - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - - # annotation to replace the certificate for the OCI Internal LoadBalancer - pailbAnnotation: - service.beta.kubernetes.io/oci-load-balancer-tls-secret: paisecretnew - - # If Internal Load Balancer will be used - paiInternalLB: true - - # Storage Allocation for the Pod - storageSizeInGb: 30 - - # Replica Count - replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_publiclb.yaml b/docs/privateai/provisioning/pai_sample_publiclb.yaml deleted file mode 100644 index d205cb64..00000000 --- a/docs/privateai/provisioning/pai_sample_publiclb.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -# Specifies the custom resource API version -apiVersion: omlai.oracle.com/v4 - -# Defines the type of resource (Private AI) -kind: PrivateAi - -# Provide identification and scoping metadata -metadata: - # Name of Private AI resource - name: pai-sample - # Kubernetes Namespace where this resource will be deployed - namespace: pai - -# Main specification for Private AI -spec: - - # Configuration for Private AI - paiConfigFile: - # Configmap having the link for the AI Model File - name: omlconfigjson - # Location inside the Pod to which the file containining the configmap will be mounted to - mountLocation: /oml/config - - # Specify the Kubernetes Secret Details - paiSecret: - name: paisecret - mountLocation: /oml/ssl - - # If you want to enable Authentication for the Private AI Access - paiEnableAuthentication: true - - # If LoadBalancer will be used - isExternalSvc: true - - # OCI Cloud Storage Class - storageClass: oci-bv - - # Container Image of the Private AI Container - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 - - # Name and port details of the Private AI Service once deployed - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - - # Public Reserved IP of the Public LoadBalancer - paiLBIP: 129.xxx.xxx.xxx - - # Storage Allocation for the Pod - storageSizeInGb: 30 - - # Replica Count - replicas: 1 diff --git a/docs/privateai/provisioning/pai_sample_scale_in.yaml b/docs/privateai/provisioning/pai_sample_scale_in.yaml deleted file mode 100644 index 87aa4708..00000000 --- a/docs/privateai/provisioning/pai_sample_scale_in.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -# Specifies the custom resource API version -apiVersion: omlai.oracle.com/v4 - -# Defines the type of resource (Private AI) -kind: PrivateAi - -# Provide identification and scoping metadata -metadata: - # Name of Private AI resource - name: pai-sample - # Kubernetes Namespace where this resource will be deployed - namespace: pai - -# Main specification for Private AI -spec: - - # Configuration for Private AI - paiConfigFile: - # Configmap having the link for the AI Model File - name: omlconfigjson - # Location inside the Pod to which the file containining the configmap will be mounted to - mountLocation: /oml/config - - # Specify the Kubernetes Secret Details - paiSecret: - name: paisecret - mountLocation: /oml/ssl - - # If you want to enable Authentication for the Private AI Access - paiEnableAuthentication: true - - # If LoadBalancer will be used - isExternalSvc: true - - # OCI Cloud Storage Class - storageClass: oci-bv - - # Container Image of the Private AI Container - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 - - # Name and port details of the Private AI Service once deployed - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - - # Public Reserved IP of the Public LoadBalancer - paiLBIP: 129.xxx.xxx.xxx - - # Storage Allocation for the Pod - storageSizeInGb: 30 - - # Replica Count - replicas: 2 diff --git a/docs/privateai/provisioning/pai_sample_scale_up.yaml b/docs/privateai/provisioning/pai_sample_scale_up.yaml deleted file mode 100644 index 0bc25223..00000000 --- a/docs/privateai/provisioning/pai_sample_scale_up.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -# Specifies the custom resource API version -apiVersion: omlai.oracle.com/v4 - -# Defines the type of resource (Private AI) -kind: PrivateAi - -# Provide identification and scoping metadata -metadata: - # Name of Private AI resource - name: pai-sample - # Kubernetes Namespace where this resource will be deployed - namespace: pai - -# Main specification for Private AI -spec: - - # Configuration for Private AI - paiConfigFile: - # Configmap having the link for the AI Model File - name: omlconfigjson - # Location inside the Pod to which the file containining the configmap will be mounted to - mountLocation: /oml/config - - # Specify the Kubernetes Secret Details - paiSecret: - name: paisecret - mountLocation: /oml/ssl - - # If you want to enable Authentication for the Private AI Access - paiEnableAuthentication: true - - # If LoadBalancer will be used - isExternalSvc: true - - # OCI Cloud Storage Class - storageClass: oci-bv - - # Container Image of the Private AI Container - paiImage: phx.ocir.io/intsanjaysingh/db-repo/oracle/database:1.8.10 - - # Name and port details of the Private AI Service once deployed - paiService: - name: paisvc - portMappings: - - port: 443 - targetPort: 8443 - protocol: TCP - - # Public Reserved IP of the Public LoadBalancer - paiLBIP: 129.xxx.xxx.xxx - - # Storage Allocation for the Pod - storageSizeInGb: 30 - - # Replica Count - replicas: 3 diff --git a/docs/privateai/provisioning/pai_secret.sh b/docs/privateai/provisioning/pai_secret.sh deleted file mode 100755 index 377dbe71..00000000 --- a/docs/privateai/provisioning/pai_secret.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -CURR_DIR=`pwd` -KEYSTORE_SECRET=keystore -API_SECRET=api-key -OMLSECRET=omlsslpwd -NAMESPACE=pai -SECRET_NAME=paisecret - -rm -f ${CURR_DIR}/key.pem -rm -f ${CURR_DIR}/cert.pem -rm -f ${CURR_DIR}/api-key -rm -f ${CURR_DIR}/key.pub - -head -c 32 /dev/urandom | xxd -p | tr -d '\n' | head -c 64 > api-key -openssl genrsa -out ${CURR_DIR}/key.pem -openssl rsa -in ${CURR_DIR}/key.pem -out ${CURR_DIR}/key.pub -pubout -openssl req -new -x509 -key ${CURR_DIR}/key.pem -out ${CURR_DIR}/cert.pem -days 365 - -# Generate keystore. Enter the password when prompted, and be sure to remember it. -openssl pkcs12 -export -inkey ${CURR_DIR}/key.pem -in ${CURR_DIR}/cert.pem -name mykey -out ${CURR_DIR}/keystore - -kubectl delete secret $SECRET_NAME -n $NAMESPACE -kubectl create secret generic $SECRET_NAME --from-file=keystore --from-file=api-key --from-file=oml-ssl-pwd -n $NAMESPACE -#kubectl create secret generic $API_SECRET --from-file=api-key.txt -n $NAMESPACE -#kubectl create secret generic $OMLSECRET --from-file=oml-ssl-pwd -n $NAMESPACE \ No newline at end of file diff --git a/docs/privateai/provisioning/pai_secret_new.sh b/docs/privateai/provisioning/pai_secret_new.sh deleted file mode 100755 index a59e2dbb..00000000 --- a/docs/privateai/provisioning/pai_secret_new.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -CURR_DIR=`pwd` -KEYSTORE_SECRET=keystore -API_SECRET=api-key -OMLSECRET=omlsslpwd -NAMESPACE=pai -SECRET_NAME=paisecretnew - -rm -f ${CURR_DIR}/key.pem -rm -f ${CURR_DIR}/cert.pem -rm -f ${CURR_DIR}/api-key -rm -f ${CURR_DIR}/key.pub - -head -c 32 /dev/urandom | xxd -p | tr -d '\n' | head -c 64 > api-key -openssl genrsa -out ${CURR_DIR}/key.pem -openssl rsa -in ${CURR_DIR}/key.pem -out ${CURR_DIR}/key.pub -pubout -openssl req -new -x509 -key ${CURR_DIR}/key.pem -out ${CURR_DIR}/cert.pem -days 365 - -# Generate keystore. Enter the password when prompted, and be sure to remember it. -openssl pkcs12 -export -inkey ${CURR_DIR}/key.pem -in ${CURR_DIR}/cert.pem -name mykey -out ${CURR_DIR}/keystore - -kubectl delete secret $SECRET_NAME -n $NAMESPACE -kubectl create secret generic $SECRET_NAME --from-file=keystore --from-file=api-key --from-file=oml-ssl-pwd -n $NAMESPACE -#kubectl create secret generic $API_SECRET --from-file=api-key.txt -n $NAMESPACE -#kubectl create secret generic $OMLSECRET --from-file=oml-ssl-pwd -n $NAMESPACE diff --git a/docs/privateai/provisioning/single_model_https_config.json b/docs/privateai/provisioning/single_model_https_config.json deleted file mode 100644 index b5b8b5d9..00000000 --- a/docs/privateai/provisioning/single_model_https_config.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "environment":{ - "OML_ENABLE_AUTHENTICATION":true - }, - "models": [ - { - "modelname":"all_minilm_v6", - "modelfile":"https://objectstorage.us-ashburn-1.oraclecloud.com/p/wBcvPtKxMfxmf7BMEcWbRWaXb249Tt1OVR6UPJRXzejXFlHu9XMkh7H6G0HV0uq9/n/intsanjaysingh/b/onnxmodel/o/ALL-MINILM-L6-V2.onnxALL-MINILM-L6-V2.onnx", - "modeltype":"ONNX_TXT", - "metadata":{ - "function":"feature_extraction" - }, - "loadOnStartup":true - } - ] -} diff --git a/docs/privateai/scale_in_privateai.md b/docs/privateai/scale_in_privateai.md deleted file mode 100644 index 2ea9f989..00000000 --- a/docs/privateai/scale_in_privateai.md +++ /dev/null @@ -1,25 +0,0 @@ -# Scale-In an existing deployment of Oracle PrivateAI Container - -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=3` using the file [pai_sample_scale_up.yaml](./provisioning/pai_sample_scale_up.yaml) - -In this example, we will Reduce the number of allocated resources by using the `scale in` command an existing deployment. We will update `replicas=3` to `replicas=2`. - -For this use case, we will update the following file: [pai_sample_scale_in.yaml](./provisioning/pai_sample_scale_in.yaml). Example: - -1. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n pai - ``` -2. Apply the `pai_sample_scale_in.yaml` file to scale in: - ```sh - kubectl apply -f pai_sample_scale_in.yaml - ``` -3. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n pai - ``` - - As a result of this procedure, the Kubernetes Pods are reduced in number after the scale in is completed automatically. - \ No newline at end of file diff --git a/docs/privateai/scale_up_privateai.md b/docs/privateai/scale_up_privateai.md deleted file mode 100644 index f18335b3..00000000 --- a/docs/privateai/scale_up_privateai.md +++ /dev/null @@ -1,25 +0,0 @@ -# Scale-Up an existing deployment of Oracle PrivateAI Container - -**IMPORTANT:** This example assumes that you have an existing Oracle PrivateAI Container Deployment with `replicas=1` using the file [pai_sample_publiclb.yaml](./provisioning/pai_sample_publiclb.yaml) - -In this example, we will increase the allocated resources in an existing deployment by using the Kubernetees scale up option from `replicas=1` to `replicas=3`. - -Use the file: [pai_sample_scale_up.yaml](./provisioning/pai_sample_scale_up.yaml) for this use case. Example: - -1. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n pai - ``` -2. Deploy the `pai_sample_scale_up.yaml` file to Scale Up: - ```sh - kubectl apply -f pai_sample_scale_up.yaml - ``` -3. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n pai - ``` - -As a result of this procedure, you will see additional Kubernetes Pods being deployed after the scale up operation is automatically completed. - \ No newline at end of file From 50daac8e54133cf1f1889d02e2b51f691c03a5bd Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Thu, 18 Sep 2025 07:06:36 +0000 Subject: [PATCH 357/414] nodeport fix --- commons/oraclerestart/oraclerestartcommon.go | 53 ++++++++------------ 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 92c02d78..92a1507d 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -824,6 +824,7 @@ func getExternalConnStr( kubeConfig clientcmd.ClientConfig, logger logr.Logger, ) string { + switch { // Case 1: Neither service defined case len(instance.Spec.NodePortSvc.PortMappings) == 0 && len(instance.Spec.LbService.PortMappings) == 0: @@ -831,12 +832,11 @@ func getExternalConnStr( // Case 2: LoadBalancer service defined, try to use it case len(instance.Spec.LbService.PortMappings) != 0: - lbServiceName := instance.Spec.LbService.SvcName + lbServiceName := instance.Spec.LbService.SvcName + "-0-lbsvc" lbSvc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), lbServiceName, metav1.GetOptions{}) - lbExtIP := "" - var lbPort int32 if err == nil && lbSvc.Spec.Type == corev1.ServiceTypeLoadBalancer { // Extract external IP or hostname + var lbExtIP string for _, ingress := range lbSvc.Status.LoadBalancer.Ingress { if ingress.IP != "" { lbExtIP = ingress.IP @@ -847,50 +847,42 @@ func getExternalConnStr( } } // Find port 1521 + var lbPort int32 for _, port := range lbSvc.Spec.Ports { if port.Port == 1521 { lbPort = port.Port break } } + if lbExtIP != "" && lbPort != 0 { + serviceName := instance.Spec.ServiceDetails.Name + return fmt.Sprintf("EXTERNAL: %s:%d/%s", lbExtIP, lbPort, serviceName) + } } - // Prefer LoadBalancer if available - if lbExtIP != "" && lbPort != 0 { - serviceName := instance.Spec.ServiceDetails.Name - return fmt.Sprintf("EXTERNAL: %s:%d/%s", lbExtIP, lbPort, serviceName) - } - // fallthrough to NodePort if LB unavailable - fallthrough + return "" - // Case 3: NodePort defined or fallback - default: - svc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), instance.Spec.InstDetails.Name, metav1.GetOptions{}) - if err != nil { - msg := "Failed to get dbmc1 service" - LogMessages("DEBUG", msg, err, instance, logger) - return "Pending" + // Case 3: NodePort service defined, try to use it + case len(instance.Spec.NodePortSvc.PortMappings) != 0: + npServiceName := instance.Spec.NodePortSvc.SvcName + "-0-npsvc" + npSvc, err := kubeClient.CoreV1().Services(instance.Namespace).Get(context.TODO(), npServiceName, metav1.GetOptions{}) + if err != nil || npSvc.Spec.Type != corev1.ServiceTypeNodePort { + return "" } - - // Find NodePort for 1521 + // Find port 1521 var nodePort int32 - for _, port := range svc.Spec.Ports { + for _, port := range npSvc.Spec.Ports { if port.Port == 1521 { nodePort = port.NodePort break } } if nodePort == 0 { - msg := "Failed to find NodePort for port 1521 in dbmc1 service" - LogMessages("DEBUG", msg, err, instance, logger) - return "Pending" + return "" } - // Get first node external/internal IP nodeList, err := kubeClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) if err != nil || len(nodeList.Items) == 0 { - msg := "Failed to list cluster nodes" - LogMessages("DEBUG", msg, err, instance, logger) - return "Pending" + return "" } var nodeIP string for _, addr := range nodeList.Items[0].Status.Addresses { @@ -902,14 +894,13 @@ func getExternalConnStr( } } if nodeIP == "" { - msg := "Failed to get node IP address" - LogMessages("DEBUG", msg, err, instance, logger) - return "Pending" + return "" } - serviceName := instance.Spec.ServiceDetails.Name return fmt.Sprintf("%s:%d/%s", nodeIP, nodePort, serviceName) } + + return "" } func getClientEtcHost(podNames []string, instance *oraclerestart.OracleRestart, specidx int, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger, nodeDetails map[string]*corev1.Node) []string { From 5dba6f58a5f511178de4d5af23d670e594813c44 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 23 Sep 2025 15:05:53 +0000 Subject: [PATCH 358/414] Added fixes --- .../database/shardingdatabase_controller.go | 57 +++++++------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 95c33f2f..198a37a6 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -161,13 +161,13 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req } } - if len(importedTDEKeys) == 0 { - importedTDEKeys = make([]bool, int32(len(instance.Spec.Shard)), int32(len(instance.Spec.Shard))) - for i = 0; i < int32(len(instance.Spec.Shard)); i++ { - importedTDEKeys[i] = false - shardingv1.LogMessages("INFO", "Initializing importedTDEKeys to false", nil, instance, r.Log) - } - } + if len(importedTDEKeys) == 0 { + importedTDEKeys = make([]bool, int32(len(instance.Spec.Shard)), int32(len(instance.Spec.Shard))) + for i = 0; i < int32(len(instance.Spec.Shard)); i++ { + importedTDEKeys[i] = false + shardingv1.LogMessages("INFO", "Initializing importedTDEKeys to false", nil, instance, r.Log) + } + } // ======================== Validate Specs ============== err = r.validateSpex(instance) @@ -358,12 +358,12 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req return result, err } - if shardingv1.CheckIsTDEWalletFlag(instance,r.Log) && !exportedTDEKeys { - exportTDEfname := "expTDEFile" - shardingv1.LogMessages("INFO", "Catalog calling ExportTDEKey", nil, instance, r.Log) - shardingv1.ExportTDEKey(OraCatalogSpex.Name+"-0",exportTDEfname, instance, r.kubeClient, r.kubeConfig, r.Log) - exportedTDEKeys = true - } + if shardingv1.CheckIsTDEWalletFlag(instance, r.Log) && !exportedTDEKeys { + exportTDEfname := "expTDEFile" + shardingv1.LogMessages("INFO", "Catalog calling ExportTDEKey", nil, instance, r.Log) + shardingv1.ExportTDEKey(OraCatalogSpex.Name+"-0", exportTDEfname, instance, r.kubeClient, r.kubeConfig, r.Log) + exportedTDEKeys = true + } } // ====================== Update Setup for Shard ============================== @@ -383,14 +383,14 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req return result, err } } - if shardingv1.CheckIsTDEWalletFlag(instance,r.Log) && exportedTDEKeys { - importTDEfname := "impTDEFile" - shardingv1.LogMessages("INFO", "Calling ImportTDEKey()", nil, instance, r.Log) - if !importedTDEKeys[i] { - shardingv1.ImportTDEKey(OraShardSpex.Name+"-0",importTDEfname, instance, r.kubeClient, r.kubeConfig, r.Log) - } - importedTDEKeys[i] = true - } + if shardingv1.CheckIsTDEWalletFlag(instance, r.Log) && exportedTDEKeys { + importTDEfname := "impTDEFile" + shardingv1.LogMessages("INFO", "Calling ImportTDEKey()", nil, instance, r.Log) + if !importedTDEKeys[i] { + shardingv1.ImportTDEKey(OraShardSpex.Name+"-0", importTDEfname, instance, r.kubeClient, r.kubeConfig, r.Log) + } + importedTDEKeys[i] = true + } } // ====================== Update Setup for Gsm ============================== @@ -443,21 +443,6 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate return true }, UpdateFunc: func(e event.UpdateEvent) bool { - instance := &databasev4.ShardingDatabase{} - if old, ok := e.ObjectOld.(*corev1.Secret); ok { - if new, ok := e.ObjectNew.(*corev1.Secret); ok { - oshInst := instance - if (new.Name == oshInst.Spec.DbSecret.Name) && (new.Name == old.Name) { - _, ok := old.Data[oshInst.Spec.DbSecret.PwdFileName] - if ok { - if !reflect.DeepEqual(old.Data[oshInst.Spec.DbSecret.PwdFileName], new.Data[oshInst.Spec.DbSecret.PwdFileName]) { - shardingv1.LogMessages("INFO", "Secret Changed", nil, oshInst, r.Log) - } - } - shardingv1.LogMessages("INFO", "Secret update block", nil, oshInst, r.Log) - } - } - } return true }, DeleteFunc: func(e event.DeleteEvent) bool { From abcfbef69d9772b5d11924e0aff331d3fcaee401 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 25 Sep 2025 05:22:17 +0000 Subject: [PATCH 359/414] Added fixes for Redhat Catalogue --- Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dockerfile b/Dockerfile index c41063c2..6277c27d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,11 @@ WORKDIR / COPY --from=builder /workspace/manager . COPY ords/ords_init.sh . COPY ords/ords_start.sh . +COPY bundle/metadata/annotations.yaml /metadata/annotations.yaml +COPY bundle/manifests/*.yaml /manifests/ +COPY LICENSE.txt /licenses/ +COPY THIRD_PARTY_LICENSES_DOCKER.txt /licenses/ +COPY THIRD_PARTY_LICENSES.txt /licenses/ RUN useradd -u 1002 nonroot USER nonroot From 371ed3c25af98f7c912ef50d257aa7f16a6865be Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 25 Sep 2025 05:35:11 +0000 Subject: [PATCH 360/414] Added fixes --- Dockerfile | 11 +++++++++++ ...racle-database-operator.clusterserviceversion.yaml | 2 +- ...racle-database-operator.clusterserviceversion.yaml | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6277c27d..3383ca52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,6 +40,17 @@ RUN --mount=type=cache,target=/go-cache --mount=type=cache,target=/gomod-cache C # Use oraclelinux:9-slim as default base image to package the manager binary FROM ${RUNNER_IMG} +# Labels +# ------ +LABEL "provider"="Oracle" \ + "issues"="https://github.com/oracle/oracle-database-operator/issues" \ + "maintainer"="paramdeep.saini@oracle.com, sanjay.singh@oracle.com, kuassi.mensah@oracle.com" \ + "version"="2.0" \ + "description"="DB Operator Image V2.0" \ + "vendor"="Oracle Coporation" \ + "release"="2.0" \ + "summary"="Oracle Database Operator 2.0" \ + "name"="oracle-database-operator.v2.0" ARG CI_COMMIT_SHA ARG CI_COMMIT_BRANCH ENV COMMIT_SHA=${CI_COMMIT_SHA} \ diff --git a/bundle/manifests/oracle-database-operator.clusterserviceversion.yaml b/bundle/manifests/oracle-database-operator.clusterserviceversion.yaml index c28637bd..b6299ff7 100644 --- a/bundle/manifests/oracle-database-operator.clusterserviceversion.yaml +++ b/bundle/manifests/oracle-database-operator.clusterserviceversion.yaml @@ -246,7 +246,7 @@ metadata: createdAt: "2025-04-10T20:09:48Z" operators.operatorframework.io/builder: operator-sdk-v1.39.2 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 - name: oracle-database-operator.v1.2.0 + name: oracle-database-operator.v2.0 namespace: oracle-database-operator-system spec: apiservicedefinitions: {} diff --git a/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml b/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml index b1a0cc31..79cb8430 100644 --- a/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/oracle-database-operator.clusterserviceversion.yaml @@ -5,10 +5,10 @@ metadata: alm-examples: '[]' capabilities: Seamless Upgrades categories: Database - containerImage: container-registry.oracle.com/database/operator:1.2.0 + containerImage: container-registry.oracle.com/database/operator:2.0 operators.operatorframework.io/builder: operator-sdk-v1.39.2 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 - name: oracle-database-operator.v1.2.0 + name: oracle-database-operator.v2.0 namespace: oracle-database-operator-system spec: apiservicedefinitions: {} From c3ed51bfacd4d506787e333951f990556cb38070 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 25 Sep 2025 15:30:34 +0000 Subject: [PATCH 361/414] Added fix for sharding nil pointer exception --- .../database/shardingdatabase_controller.go | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index 198a37a6..3d3ca021 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -446,29 +446,29 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate return true }, DeleteFunc: func(e event.DeleteEvent) bool { - //instance := &databasev4.ShardingDatabase{} - _, podOk := e.Object.GetLabels()["statefulset.kubernetes.io/pod-name"] - instance, _ := e.Object.DeepCopyObject().(*databasev4.ShardingDatabase) - if e.Object.GetDeletionTimestamp() == nil { - if e.Object.GetLabels()[string(databasev4.ShardingDelLabelKey)] == string(databasev4.ShardingDelLabelTrueValue) { - } - if podOk { - delObj := e.Object.(*corev1.Pod) - if e.Object.GetLabels()["type"] == "Shard" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == instance.Name { - if delObj.DeletionTimestamp != nil { - go r.gsmInvitedNodeOp(instance, delObj.Name) - } - } - - if e.Object.GetLabels()["type"] == "Catalog" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == instance.Name { - - if delObj.DeletionTimestamp != nil { - go r.gsmInvitedNodeOp(instance, delObj.Name) - } - } - } - - } + // //instance := &databasev4.ShardingDatabase{} + // _, podOk := e.Object.GetLabels()["statefulset.kubernetes.io/pod-name"] + // instance, _ := e.Object.DeepCopyObject().(*databasev4.ShardingDatabase) + // if e.Object.GetDeletionTimestamp() == nil { + // if e.Object.GetLabels()[string(databasev4.ShardingDelLabelKey)] == string(databasev4.ShardingDelLabelTrueValue) { + // } + // if podOk { + // delObj := e.Object.(*corev1.Pod) + // if e.Object.GetLabels()["type"] == "Shard" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == instance.Name { + // if delObj.DeletionTimestamp != nil { + // go r.gsmInvitedNodeOp(instance, delObj.Name) + // } + // } + + // if e.Object.GetLabels()["type"] == "Catalog" && e.Object.GetLabels()["app"] == "OracleSharding" && e.Object.GetLabels()["oralabel"] == instance.Name { + + // if delObj.DeletionTimestamp != nil { + // go r.gsmInvitedNodeOp(instance, delObj.Name) + // } + // } + // } + + // } return true }, } From 73882d4f5f85d40579cb3c362085ca31f1d20118 Mon Sep 17 00:00:00 2001 From: kuassi_mensah Date: Fri, 26 Sep 2025 16:23:20 +0000 Subject: [PATCH 362/414] Update file README.md --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index c9a019bf..649433d6 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,24 @@ As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released the _Oracle Database Operator for Kubernetes_ (`OraOperator` or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating the management of the Oracle Database lifecycle. +## WHat's New in v2.0.0 + +* **RedHat OpenShift Certification** + * Validation of OraOperator and Controllers + * Inclusion in RedHat Operators Catalog +* **Restart Controller** + * Provision, add & delete asm disks, and more +* **ORDS Service** + * ServiceAccount and OpenShift support + * Auto download of APEX installation files and APEX image on a Persistent Volume +* **Integrations** + * Private Cloud Appliance (PCA) + * Compute Cloud@Customer (C3) +* **Bug fixes** + * Bugs filed through Oracle Support + * GitHub issues + + ## Supported Database Configurations v2.0.0 In this v2.0 production release, `OraOperator` supports the following database configurations and controllers: From 930580c78ab239451571ab368c74a81fd62ce819 Mon Sep 17 00:00:00 2001 From: kuassi_mensah Date: Fri, 26 Sep 2025 16:23:48 +0000 Subject: [PATCH 363/414] Update file README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 649433d6..da6a179e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ As part of Oracle's resolution to make Oracle Database Kubernetes native (that is, observable and operable by Kubernetes), Oracle released the _Oracle Database Operator for Kubernetes_ (`OraOperator` or the operator). OraOperator extends the Kubernetes API with custom resources and controllers for automating the management of the Oracle Database lifecycle. -## WHat's New in v2.0.0 +## What's New in v2.0.0 * **RedHat OpenShift Certification** * Validation of OraOperator and Controllers From 10d32ab140efa9fb0f27a3f51d9d3a4ce9f72825 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 29 Sep 2025 01:05:24 +0000 Subject: [PATCH 364/414] Added fixes --- Dockerfile | 2 -- Makefile | 2 +- bundle.Dockerfile | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3383ca52..299e0e6f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,8 +59,6 @@ WORKDIR / COPY --from=builder /workspace/manager . COPY ords/ords_init.sh . COPY ords/ords_start.sh . -COPY bundle/metadata/annotations.yaml /metadata/annotations.yaml -COPY bundle/manifests/*.yaml /manifests/ COPY LICENSE.txt /licenses/ COPY THIRD_PARTY_LICENSES_DOCKER.txt /licenses/ COPY THIRD_PARTY_LICENSES.txt /licenses/ diff --git a/Makefile b/Makefile index 643155bc..d27bb5f2 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # # Current Operator version -VERSION ?= 1.2.0 +VERSION ?= 2.0 # Default bundle image tag BUNDLE_IMG ?= controller-bundle:$(VERSION) # Options for 'bundle-build' diff --git a/bundle.Dockerfile b/bundle.Dockerfile index f64398cf..609d09c0 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -6,7 +6,7 @@ LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=oracle-database-operator LABEL operators.operatorframework.io.bundle.channels.v1=alpha -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.39.2 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.40.0 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 From acd8f147e615f08f807f551591bae22fd045aec2 Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Tue, 30 Sep 2025 14:42:50 +0000 Subject: [PATCH 365/414] fix for invalid disk --- commons/oraclerestart/oraclerestartprov.go | 60 ++++++++++++++----- commons/oraclerestart/oraclerestartstatus.go | 16 +++-- .../database/oraclerestart_controller.go | 29 +++++---- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 737d72bb..9c72dc54 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -633,18 +633,39 @@ func VolumePVCForASM(instance *oraclerestart.OracleRestart, index int, diskName }, }, } - - // Check if a StorageClass is defined and set it + var scName *string if len(instance.Spec.StorageClass) != 0 { - asmPvc.Spec.StorageClassName = &instance.Spec.StorageClass + scName = &instance.Spec.StorageClass } else { - // If no StorageClass, use the LabelSelector based on disk size - asmPvc.Spec.Selector = &metav1.LabelSelector{MatchLabels: buildLabelsForAsmPv(instance, string(diskName), index)} - asmPvc.Spec.StorageClassName = nil + + // Try to fetch the cluster's default StorageClass + if defaultSC, err := GetDefaultStorageClass(context.TODO(), k8sClient); err == nil && defaultSC != "" { + scName = &defaultSC + } else { + // No StorageClass, so use label selector and statically bound PVs + asmPvc.Spec.Selector = &metav1.LabelSelector{ + MatchLabels: buildLabelsForAsmPv(instance, string(diskName), index), + } + scName = nil + } } + asmPvc.Spec.StorageClassName = scName return asmPvc } +func GetDefaultStorageClass(ctx context.Context, k8sClient client.Client) (string, error) { + var scList storagev1.StorageClassList + if err := k8sClient.List(ctx, &scList); err != nil { + return "", err + } + for _, sc := range scList.Items { + if sc.Annotations["storageclass.kubernetes.io/is-default-class"] == "true" || + sc.Annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true" { + return sc.Name, nil + } + } + return "", nil // No default StorageClass found +} func VolumePVForASM(instance *oraclerestart.OracleRestart, index int, diskName string, size int, asmStorage *oraclerestart.AsmDiskDetails, k8sClient client.Client) *corev1.PersistentVolume { volumeBlock := corev1.PersistentVolumeBlock @@ -665,16 +686,25 @@ func VolumePVForASM(instance *oraclerestart.OracleRestart, index int, diskName s }, } + var scName *string if len(instance.Spec.StorageClass) != 0 { - asmPvc.Spec.StorageClassName = instance.Spec.StorageClass + scName = &instance.Spec.StorageClass asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} - } else { + // Try to fetch the cluster's default StorageClass + if defaultSC, err := GetDefaultStorageClass(context.TODO(), k8sClient); err == nil && defaultSC != "" { + scName = &defaultSC + } else { + // No StorageClass, so use label selector and statically bound PVs + scName = nil + } asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} } + asmPvc.Spec.StorageClassName = *scName + return asmPvc } @@ -892,7 +922,6 @@ func BuildDiskCheckDaemonSet(OracleRestart *oraclerestart.OracleRestart) *appsv1 }) } - // Join the disk names into a space-separated string // Flatten the DisksBySize map to get a single slice of all disk names diskNamesSlice := flattenDisksBySize(&OracleRestart.Spec) @@ -947,6 +976,7 @@ func BuildDiskCheckDaemonSet(OracleRestart *oraclerestart.OracleRestart) *appsv1 "done; " + "sleep 3600", }, + VolumeDevices: volumeDevices, }, }, @@ -1009,12 +1039,12 @@ func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.Oracl return true // fallback to static if we can't query SCs } - for _, sc := range scList.Items { - if sc.Annotations["storageclass.kubernetes.io/is-default-class"] == "true" || - sc.Annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true" { - return false // dynamic provisioning is available - } - } + // for _, sc := range scList.Items { + // if sc.Annotations["storageclass.kubernetes.io/is-default-class"] == "true" || + // sc.Annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true" { + // return false // dynamic provisioning is available + // } + // } return true // no default SC → use static } diff --git a/commons/oraclerestart/oraclerestartstatus.go b/commons/oraclerestart/oraclerestartstatus.go index 8f13fbc3..a9dbf94f 100644 --- a/commons/oraclerestart/oraclerestartstatus.go +++ b/commons/oraclerestart/oraclerestartstatus.go @@ -40,7 +40,6 @@ package commons import ( "context" - "fmt" "strings" "github.com/go-logr/logr" @@ -86,7 +85,14 @@ func UpdateOracleRestartInstStatusData( OracleRestart.Status.State = state } if state == string(oraclerestartdb.OracleRestartFailedState) { - OracleRestart.Status.State = state + clusterState := getClusterState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + instanceState := getDbInstState(podName, OracleRestart, 0, kubeClient, kubeConfig, logger) + if clusterState == "HEALTHY" && instanceState == "OPEN" { // cluster is healthy and database is also fine + OracleRestart.Status.State = string(oraclerestartdb.OracleRestartAvailableState) + state = string(oraclerestartdb.OracleRestartAvailableState) + } else { + OracleRestart.Status.State = state + } } if state == string(oraclerestartdb.OracleRestartManualState) { OracleRestart.Status.State = state @@ -191,12 +197,12 @@ func UpdateOracleRestartInstState(instance *oraclerestartdb.OracleRestart, podNa func UpdateoraclerestartdbTopologyState(instance *oraclerestartdb.OracleRestart, ctx context.Context, req ctrl.Request, podName string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, logger logr.Logger) { OracleRestart := &oraclerestartdb.OracleRestart{} - fmt.Printf("I m inUpdateoraclerestartdbTopologyState") + // fmt.Printf("I m inUpdateoraclerestartdbTopologyState") if len(instance.Status.OracleRestartNodes) > 0 { state := map[string]struct{}{} for _, v := range instance.Status.OracleRestartNodes { inst_state := strings.ToLower(strings.TrimSpace(v.NodeDetails.State)) - fmt.Printf("inst_state", inst_state) + // fmt.Printf("inst_state", inst_state) state[inst_state] = struct{}{} } if len(state) == 0 { @@ -217,7 +223,7 @@ func UpdateoraclerestartdbTopologyState(instance *oraclerestartdb.OracleRestart, OracleRestart.Status.State = string(oraclerestartdb.OracleRestartAvailableState) } instance.Status.State = OracleRestart.Status.State - fmt.Printf("instance.Status.State", instance.Status.State) + // fmt.Printf("instance.Status.State", instance.Status.State) } } diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 75b4def7..4d10f840 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -264,8 +264,13 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques r.ensureAsmStorageStatus(oracleRestart) isNewSetup := true - for _, diskgroup := range oracleRestart.Status.AsmDetails.Diskgroup { - if len(diskgroup.Disks) > 0 && oracleRestart.Status.AsmDetails.Diskgroup[0].Name != "Pending" { + for _, n := range oracleRestart.Status.OracleRestartNodes { + if n.NodeDetails.State == "AVAILABLE" && + n.NodeDetails.InstanceState == "OPEN" && + n.NodeDetails.PodState == "AVAILABLE" && + n.NodeDetails.ClusterState == "HEALTHY" && + len(n.NodeDetails.MountedDevices) > 0 { + // Found at least one node that is healthy and operational isNewSetup = false break } @@ -372,7 +377,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques oldState := oracleRestart.Status.State if !utils.CheckStatusFlag(oracleRestart.Spec.InstDetails.IsDelete) { switch { - case isNewSetup || !isDiskChanged: + case isNewSetup && !isDiskChanged: cmName := oracleRestart.Spec.InstDetails.Name + oracleRestart.Name + "-cmap" cm := oraclerestartcommon.ConfigMapSpecs(oracleRestart, configMapData, cmName) result, configmapEnvKeyChanged, err := r.createConfigMap(ctx, *oracleRestart, cm) @@ -408,7 +413,8 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } if len(oracleRestart.Spec.StorageClass) == 0 { if ready, err := checkDaemonSetStatus(ctx, r, oracleRestart); err != nil || !ready { - r.Log.Info("Any of provided ASM Disks are invalid, pls check disk-check daemon set for logs. Fix the asm disk to the valid one and redeploy.") + msg := "Any of provided ASM Disks are invalid, pls check disk-check daemon set for logs. Fix the asm disk to the valid one and redeploy." + r.Log.Info(msg) err = r.cleanupDaemonSet(oracleRestart, ctx) if err != nil { result = resultQ @@ -423,7 +429,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { for index, diskName := range diskBySize.DiskNames { if _, ok := addedAsmDisksMap[diskName]; ok { - r.Log.Info("Found disk at index", "index", index) + // r.Log.Info("Found disk at index", "index", index) err = oraclerestartcommon.DelORestartPVC(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) if err != nil { @@ -443,7 +449,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques oracleRestart.Spec.IsFailed = true return resultQ, err } - return resultQ, err + return result, errors.New(msg) } else { r.Log.Info("Provided ASM Disks are valid, proceeding further") } @@ -710,13 +716,6 @@ func (r *OracleRestartReconciler) updateReconcileStatus(oracleRestart *oracleres oracleRestart.ResourceVersion = latestInstance.ResourceVersion err = r.Client.Status().Patch(ctx, oracleRestart, client.MergeFrom(latestInstance)) - // patchData, err1 := json.Marshal(map[string]interface{}{ - // "status": oracleRestart.Status, - // }) - // if err1 != nil { - // r.Log.Error(err1, "Failed to marshal Status field") - // } - // err2 := r.Client.Status().Patch(ctx, OracleRestart, client.RawPatch(types.MergePatchType, patchData)) if err != nil { if apierrors.IsConflict(err) { r.Log.Info("Conflict detected, retrying update...", "attempt", attempt+1) @@ -3452,7 +3451,7 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context //reqLogger := r.Log.WithValues("Instance.Namespace", instance.Namespace, "Instance.Name", instance.Name) if oldSpec != nil { - fmt.Printf("Received OldSpec", oldSpec.InstDetails.SwLocStorageSizeInGb) + // fmt.Printf("Received OldSpec", oldSpec.InstDetails.SwLocStorageSizeInGb) if instance.Spec.InstDetails.SwLocStorageSizeInGb > oldSpec.InstDetails.SwLocStorageSizeInGb { fmt.Printf("Inside OldSpec and newSpec Change", oldSpec.InstDetails.SwLocStorageSizeInGb, instance.Spec.InstDetails.SwLocStorageSizeInGb) storageClass := &storagev1.StorageClass{} @@ -3471,7 +3470,7 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context Namespace: instance.Namespace, }, pvc) - fmt.Printf("PvcName set to ", pvc.Name) + // fmt.Printf("PvcName set to ", pvc.Name) if err == nil { if storageClass.AllowVolumeExpansion == nil || !*storageClass.AllowVolumeExpansion { From c731dc448ac1b42687975695d42d2289462dc09c Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Sat, 4 Oct 2025 19:47:09 +0000 Subject: [PATCH 366/414] Added fixes --- Makefile | 3 +-- go.mod | 1 + go.sum | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index bbb941a4..d27bb5f2 100644 --- a/Makefile +++ b/Makefile @@ -124,8 +124,7 @@ operator-yaml: manifests kustomize sed -i.bak -e '/^apiVersion: apps\/v1/,/---/d' "$(OPERATOR_YAML)" (echo --- && sed '/^apiVersion: apps\/v1/,/---/!d' "$(OPERATOR_YAML).bak") >> "$(OPERATOR_YAML)" rm "$(OPERATOR_YAML).bak" - -minikube-operator-yaml: IMG:=localhost:5000/$(IMG) + minikube-operator-yaml: operator-yaml sed -i.bak 's/\(replicas.\) 3/\1 1/g' "$(OPERATOR_YAML)" rm "$(OPERATOR_YAML).bak" diff --git a/go.mod b/go.mod index 1c9c56ec..59f1af19 100644 --- a/go.mod +++ b/go.mod @@ -118,3 +118,4 @@ require ( sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) + diff --git a/go.sum b/go.sum index a5fcf768..ec9b90c0 100644 --- a/go.sum +++ b/go.sum @@ -146,6 +146,7 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= From 6ad723b2c72c5af6853add91149006ac4d308782 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Sat, 4 Oct 2025 19:54:22 +0000 Subject: [PATCH 367/414] Added fixes --- .../v1alpha1/zz_generated.deepcopy.go | 86 +------------------ apis/database/v4/zz_generated.deepcopy.go | 8 ++ .../observability/v1/zz_generated.deepcopy.go | 1 + .../v1alpha1/zz_generated.deepcopy.go | 1 + .../observability/v4/zz_generated.deepcopy.go | 1 + 5 files changed, 12 insertions(+), 85 deletions(-) diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 6c67799c..672fa331 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1555,91 +1555,6 @@ func (in *OciSecretSpec) DeepCopy() *OciSecretSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OciAcdSpec) DeepCopyInto(out *OciAcdSpec) { - *out = *in - if in.Id != nil { - in, out := &in.Id, &out.Id - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciAcdSpec. -func (in *OciAcdSpec) DeepCopy() *OciAcdSpec { - if in == nil { - return nil - } - out := new(OciAcdSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OciAdbSpec) DeepCopyInto(out *OciAdbSpec) { - *out = *in - if in.Ocid != nil { - in, out := &in.Ocid, &out.Ocid - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciAdbSpec. -func (in *OciAdbSpec) DeepCopy() *OciAdbSpec { - if in == nil { - return nil - } - out := new(OciAdbSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OciConfigSpec) DeepCopyInto(out *OciConfigSpec) { - *out = *in - if in.ConfigMapName != nil { - in, out := &in.ConfigMapName, &out.ConfigMapName - *out = new(string) - **out = **in - } - if in.SecretName != nil { - in, out := &in.SecretName, &out.SecretName - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciConfigSpec. -func (in *OciConfigSpec) DeepCopy() *OciConfigSpec { - if in == nil { - return nil - } - out := new(OciConfigSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OciSecretSpec) DeepCopyInto(out *OciSecretSpec) { - *out = *in - if in.Id != nil { - in, out := &in.Id, &out.Id - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciSecretSpec. -func (in *OciSecretSpec) DeepCopy() *OciSecretSpec { - if in == nil { - return nil - } - out := new(OciSecretSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OracleRestDataService) DeepCopyInto(out *OracleRestDataService) { *out = *in @@ -2571,3 +2486,4 @@ func (in *WalletSpec) DeepCopy() *WalletSpec { in.DeepCopyInto(out) return out } + diff --git a/apis/database/v4/zz_generated.deepcopy.go b/apis/database/v4/zz_generated.deepcopy.go index d50c9761..c69f656a 100644 --- a/apis/database/v4/zz_generated.deepcopy.go +++ b/apis/database/v4/zz_generated.deepcopy.go @@ -3550,6 +3550,14 @@ func (in *PoolSettings) DeepCopyInto(out *PoolSettings) { *out = new(int32) **out = **in } + if in.JDBCMinLimit != nil { + in, out := &in.JDBCMinLimit, &out.JDBCMinLimit + *out = new(int32) + **out = **in + } + if in.JDBCStatementTimeout != nil { + in, out := &in.JDBCStatementTimeout, &out.JDBCStatementTimeout + *out = new(int32) **out = **in } if in.MiscPaginationMaxRows != nil { diff --git a/apis/observability/v1/zz_generated.deepcopy.go b/apis/observability/v1/zz_generated.deepcopy.go index 0991c186..4c8cf74a 100644 --- a/apis/observability/v1/zz_generated.deepcopy.go +++ b/apis/observability/v1/zz_generated.deepcopy.go @@ -595,3 +595,4 @@ func (in *WalletSecret) DeepCopy() *WalletSecret { in.DeepCopyInto(out) return out } + diff --git a/apis/observability/v1alpha1/zz_generated.deepcopy.go b/apis/observability/v1alpha1/zz_generated.deepcopy.go index c5e76104..c016a126 100644 --- a/apis/observability/v1alpha1/zz_generated.deepcopy.go +++ b/apis/observability/v1alpha1/zz_generated.deepcopy.go @@ -595,3 +595,4 @@ func (in *WalletSecret) DeepCopy() *WalletSecret { in.DeepCopyInto(out) return out } + diff --git a/apis/observability/v4/zz_generated.deepcopy.go b/apis/observability/v4/zz_generated.deepcopy.go index 1d3e1c11..28ba80a8 100644 --- a/apis/observability/v4/zz_generated.deepcopy.go +++ b/apis/observability/v4/zz_generated.deepcopy.go @@ -595,3 +595,4 @@ func (in *WalletSecret) DeepCopy() *WalletSecret { in.DeepCopyInto(out) return out } + From 34c7583adc9cfc5bb4492a84900141516ebe2fae Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Sat, 4 Oct 2025 20:02:34 +0000 Subject: [PATCH 368/414] Added fixes --- .../autonomousdatabaserestore_webhook.go | 13 - .../v1alpha1/dataguardbroker_webhook.go | 4 +- .../v1alpha1/shardingdatabase_webhook.go | 1 + .../singleinstancedatabase_webhook.go | 4 +- .../v1alpha1/zz_generated.deepcopy.go | 1 - apis/database/v4/cdb_types.go | 191 --------- apis/database/v4/cdb_webhook.go | 224 ----------- apis/database/v4/dbcssystem_webhook.go | 38 -- apis/database/v4/pdb_types.go | 237 ----------- apis/database/v4/pdb_webhook.go | 369 ------------------ apis/database/v4/shardingdatabase_webhook.go | 31 +- .../v1/databaseobserver_webhook.go | 1 - .../observability/v1/zz_generated.deepcopy.go | 1 - .../v1alpha1/databaseobserver_webhook.go | 1 - .../v1alpha1/zz_generated.deepcopy.go | 1 - .../v4/databaseobserver_webhook.go | 1 - .../observability/v4/zz_generated.deepcopy.go | 1 - commons/dbcssystem/dbcs_reconciler.go | 3 + commons/observability/utils.go | 5 +- commons/oraclerestart/oraclerestartprov.go | 4 +- commons/sharding/catalog.go | 52 +-- commons/sharding/gsm.go | 46 +-- commons/sharding/scommon.go | 1 + commons/sharding/shard.go | 58 +-- controllers/database/lrest_controller.go | 1 + .../database/shardingdatabase_controller.go | 15 - .../singleinstancedatabase_controller.go | 1 + .../databaseobserver_controller.go | 4 +- main.go | 12 - 29 files changed, 113 insertions(+), 1208 deletions(-) delete mode 100644 apis/database/v4/cdb_types.go delete mode 100644 apis/database/v4/cdb_webhook.go delete mode 100644 apis/database/v4/pdb_types.go delete mode 100644 apis/database/v4/pdb_webhook.go diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go index df232377..e8696045 100644 --- a/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go @@ -89,19 +89,6 @@ func (r *AutonomousDatabaseRestore) ValidateCreate(ctx context.Context, obj runt } } - namespaces := dbcommons.GetWatchNamespaces() - _, hasEmptyString := namespaces[""] - isClusterScoped := len(namespaces) == 1 && hasEmptyString - if !isClusterScoped { - _, containsNamespace := namespaces[r.Namespace] - // Check if the allowed namespaces maps contains the required namespace - if len(namespaces) != 0 && !containsNamespace { - allErrs = append(allErrs, - field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, - "Oracle database operator doesn't watch over this namespace")) - } - } - // Validate the target ADB if restore.Spec.Target.K8sAdb.Name == nil && restore.Spec.Target.OciAdb.Id == nil { allErrs = append(allErrs, diff --git a/apis/database/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go index 34c5e9f5..c9cc7bc7 100644 --- a/apis/database/v1alpha1/dataguardbroker_webhook.go +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -40,9 +40,9 @@ package v1alpha1 import ( "context" - "fmt" "strconv" "strings" + "fmt" dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -113,7 +113,7 @@ var _ webhook.CustomValidator = &DataguardBroker{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *DataguardBroker) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - dg, ok := obj.(*DataguardBroker) + dg, ok := obj.(*DataguardBroker) if !ok { return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast obj object to DataguardBroker")) } diff --git a/apis/database/v1alpha1/shardingdatabase_webhook.go b/apis/database/v1alpha1/shardingdatabase_webhook.go index fc679d35..f5f25ecc 100644 --- a/apis/database/v1alpha1/shardingdatabase_webhook.go +++ b/apis/database/v1alpha1/shardingdatabase_webhook.go @@ -39,6 +39,7 @@ package v1alpha1 import ( + ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" ) diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index fb83d0b2..d0cd88aa 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -40,10 +40,10 @@ package v1alpha1 import ( "context" - "fmt" "strconv" "strings" "time" + "fmt" dbcommons "github.com/oracle/oracle-database-operator/commons/database" @@ -561,7 +561,7 @@ func (r *SingleInstanceDatabase) ValidateDelete(ctx context.Context, obj runtime if !ok { return nil, apierrors.NewInternalError(fmt.Errorf("failed to cast obj object to SingleInstanceDatabase")) } - + singleinstancedatabaselog.Info("validate delete", "name", sidb.Name) var allErrs field.ErrorList if sidb.Status.OrdsReference != "" { diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 672fa331..511a2912 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -2486,4 +2486,3 @@ func (in *WalletSpec) DeepCopy() *WalletSpec { in.DeepCopyInto(out) return out } - diff --git a/apis/database/v4/cdb_types.go b/apis/database/v4/cdb_types.go deleted file mode 100644 index ce3f6f28..00000000 --- a/apis/database/v4/cdb_types.go +++ /dev/null @@ -1,191 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// CDBSpec defines the desired state of CDB -type CDBSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Name of the CDB - CDBName string `json:"cdbName,omitempty"` - // Name of the CDB Service - ServiceName string `json:"serviceName,omitempty"` - - // Password for the CDB System Administrator - SysAdminPwd CDBSysAdminPassword `json:"sysAdminPwd,omitempty"` - // User in the root container with sysdba priviledges to manage PDB lifecycle - CDBAdminUser CDBAdminUser `json:"cdbAdminUser,omitempty"` - // Password for the CDB Administrator to manage PDB lifecycle - CDBAdminPwd CDBAdminPassword `json:"cdbAdminPwd,omitempty"` - - CDBTlsKey CDBTLSKEY `json:"cdbTlsKey,omitempty"` - CDBTlsCrt CDBTLSCRT `json:"cdbTlsCrt,omitempty"` - - // Password for user ORDS_PUBLIC_USER - ORDSPwd ORDSPassword `json:"ordsPwd,omitempty"` - // ORDS server port. For now, keep it as 8888. TO BE USED IN FUTURE RELEASE. - ORDSPort int `json:"ordsPort,omitempty"` - // ORDS Image Name - ORDSImage string `json:"ordsImage,omitempty"` - // The name of the image pull secret in case of a private docker repository. - ORDSImagePullSecret string `json:"ordsImagePullSecret,omitempty"` - // ORDS Image Pull Policy - // +kubebuilder:validation:Enum=Always;Never - ORDSImagePullPolicy string `json:"ordsImagePullPolicy,omitempty"` - // Number of ORDS Containers to create - Replicas int `json:"replicas,omitempty"` - // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - WebServerUser WebServerUser `json:"webServerUser,omitempty"` - // Password for the Web Server User - WebServerPwd WebServerPassword `json:"webServerPwd,omitempty"` - // Name of the DB server - DBServer string `json:"dbServer,omitempty"` - // DB server port - DBPort int `json:"dbPort,omitempty"` - // Node Selector for running the Pod - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - DeletePDBCascade bool `json:"deletePdbCascade,omitempty"` - DBTnsurl string `json:"dbTnsurl,omitempty"` - CDBPubKey CDBPUBKEY `json:"cdbOrdsPubKey,omitempty"` - CDBPriKey CDBPRIVKEY `json:"cdbOrdsPrvKey,omitempty"` -} - -// CDBSecret defines the secretName -type CDBSecret struct { - SecretName string `json:"secretName"` - Key string `json:"key"` -} - -// CDBSysAdminPassword defines the secret containing SysAdmin Password mapped to key 'sysAdminPwd' for CDB -type CDBSysAdminPassword struct { - Secret CDBSecret `json:"secret"` -} - -// CDBAdminUser defines the secret containing CDB Administrator User mapped to key 'cdbAdminUser' to manage PDB lifecycle -type CDBAdminUser struct { - Secret CDBSecret `json:"secret"` -} - -// CDBAdminPassword defines the secret containing CDB Administrator Password mapped to key 'cdbAdminPwd' to manage PDB lifecycle -type CDBAdminPassword struct { - Secret CDBSecret `json:"secret"` -} - -// ORDSPassword defines the secret containing ORDS_PUBLIC_USER Password mapped to key 'ordsPwd' -type ORDSPassword struct { - Secret CDBSecret `json:"secret"` -} - -// WebServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle -type WebServerUser struct { - Secret CDBSecret `json:"secret"` -} - -// WebServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle -type WebServerPassword struct { - Secret CDBSecret `json:"secret"` -} - -type CDBTLSKEY struct { - Secret CDBSecret `json:"secret"` -} - -type CDBTLSCRT struct { - Secret CDBSecret `json:"secret"` -} - -type CDBPUBKEY struct { - Secret CDBSecret `json:"secret"` -} - -type CDBPRIVKEY struct { - Secret CDBSecret `json:"secret"` -} - -// CDBStatus defines the observed state of CDB -type CDBStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Phase of the CDB Resource - Phase string `json:"phase"` - // CDB Resource Status - Status bool `json:"status"` - // Message - Msg string `json:"msg,omitempty"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" -// +kubebuilder:printcolumn:JSONPath=".spec.dbServer",name="DB Server",type="string",description=" Name of the DB Server" -// +kubebuilder:printcolumn:JSONPath=".spec.dbPort",name="DB Port",type="integer",description="DB server port" -// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="Replicas",type="integer",description="Replicas" -// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the CDB Resource" -// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" -// +kubebuilder:printcolumn:JSONPath=".spec.dbTnsurl",name="TNS STRING",type="string",description=" string of the tnsalias" -// +kubebuilder:resource:path=cdbs,scope=Namespaced -// +kubebuilder:storageversion - -// CDB is the Schema for the cdbs API -type CDB struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec CDBSpec `json:"spec,omitempty"` - Status CDBStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// CDBList contains a list of CDB -type CDBList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []CDB `json:"items"` -} - -func init() { - SchemeBuilder.Register(&CDB{}, &CDBList{}) -} diff --git a/apis/database/v4/cdb_webhook.go b/apis/database/v4/cdb_webhook.go deleted file mode 100644 index 235b2627..00000000 --- a/apis/database/v4/cdb_webhook.go +++ /dev/null @@ -1,224 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - "reflect" - "strings" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// log is for logging in this package. -var cdblog = logf.Log.WithName("cdb-webhook") - -func (r *CDB) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-cdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v4,name=mcdb.kb.io,admissionReviewVersions={v1,v1beta1} - -var _ webhook.Defaulter = &CDB{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *CDB) Default() { - cdblog.Info("Setting default values in CDB spec for : " + r.Name) - - if r.Spec.ORDSPort == 0 { - r.Spec.ORDSPort = 8888 - } - - if r.Spec.Replicas == 0 { - r.Spec.Replicas = 1 - } -} - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:path=/validate-database-oracle-com-v4-cdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=cdbs,verbs=create;update,versions=v4,name=vcdb.kb.io,admissionReviewVersions={v1,v1beta1} - -var _ webhook.Validator = &CDB{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateCreate() (admission.Warnings, error) { - cdblog.Info("ValidateCreate", "name", r.Name) - - var allErrs field.ErrorList - - if r.Spec.ServiceName == "" && r.Spec.DBServer != "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("serviceName"), "Please specify CDB Service name")) - } - - if reflect.ValueOf(r.Spec.CDBTlsKey).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbTlsKey"), "Please specify CDB Tls key(secret)")) - } - - if reflect.ValueOf(r.Spec.CDBTlsCrt).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbTlsCrt"), "Please specify CDB Tls Certificate(secret)")) - } - - if reflect.ValueOf(r.Spec.CDBPriKey).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("CDBPriKey"), "Please specify CDB CDBPriKey(secret)")) - } - - /*if r.Spec.SCANName == "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("scanName"), "Please specify SCAN Name for CDB")) - }*/ - - if (r.Spec.DBServer == "" && r.Spec.DBTnsurl == "") || (r.Spec.DBServer != "" && r.Spec.DBTnsurl != "") { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbServer"), "Please specify Database Server Name/IP Address or tnsalias string")) - } - - if r.Spec.DBTnsurl != "" && (r.Spec.DBServer != "" || r.Spec.DBPort != 0 || r.Spec.ServiceName != "") { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbServer"), "DBtnsurl is orthogonal to (DBServer,DBport,Services)")) - } - - if r.Spec.DBPort == 0 && r.Spec.DBServer != "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbPort"), "Please specify DB Server Port")) - } - if r.Spec.DBPort < 0 && r.Spec.DBServer != "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbPort"), "Please specify a valid DB Server Port")) - } - if r.Spec.ORDSPort < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("ordsPort"), "Please specify a valid ORDS Port")) - } - if r.Spec.Replicas < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("replicas"), "Please specify a valid value for Replicas")) - } - if r.Spec.ORDSImage == "" { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("ordsImage"), "Please specify name of ORDS Image to be used")) - } - if reflect.ValueOf(r.Spec.CDBAdminUser).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbAdminUser"), "Please specify user in the root container with sysdba priviledges to manage PDB lifecycle")) - } - if reflect.ValueOf(r.Spec.CDBAdminPwd).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("cdbAdminPwd"), "Please specify password for the CDB Administrator to manage PDB lifecycle")) - } - if reflect.ValueOf(r.Spec.ORDSPwd).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("ordsPwd"), "Please specify password for user ORDS_PUBLIC_USER")) - } - if reflect.ValueOf(r.Spec.WebServerUser).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("webServerUser"), "Please specify the Web Server User having SQL Administrator role")) - } - if reflect.ValueOf(r.Spec.WebServerPwd).IsZero() { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("webServerPwd"), "Please specify password for the Web Server User having SQL Administrator role")) - } - if len(allErrs) == 0 { - return nil, nil - } - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "CDB"}, - r.Name, allErrs) -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - cdblog.Info("validate update", "name", r.Name) - - isCDBMarkedToBeDeleted := r.GetDeletionTimestamp() != nil - if isCDBMarkedToBeDeleted { - return nil, nil - } - - var allErrs field.ErrorList - - // Check for updation errors - oldCDB, ok := old.(*CDB) - if !ok { - return nil, nil - } - - if r.Spec.DBPort < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("dbPort"), "Please specify a valid DB Server Port")) - } - if r.Spec.ORDSPort < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("ordsPort"), "Please specify a valid ORDS Port")) - } - if r.Spec.Replicas < 0 { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("replicas"), "Please specify a valid value for Replicas")) - } - if !strings.EqualFold(oldCDB.Spec.ServiceName, r.Spec.ServiceName) { - allErrs = append(allErrs, - field.Forbidden(field.NewPath("spec").Child("replicas"), "cannot be changed")) - } - - if len(allErrs) == 0 { - return nil, nil - } - - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "CDB"}, - r.Name, allErrs) -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *CDB) ValidateDelete() (admission.Warnings, error) { - cdblog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. - return nil, nil -} diff --git a/apis/database/v4/dbcssystem_webhook.go b/apis/database/v4/dbcssystem_webhook.go index 37761ef5..1fc85f80 100644 --- a/apis/database/v4/dbcssystem_webhook.go +++ b/apis/database/v4/dbcssystem_webhook.go @@ -1,41 +1,3 @@ -/* -** Copyright (c) 2022-2024 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - package v4 import ( diff --git a/apis/database/v4/pdb_types.go b/apis/database/v4/pdb_types.go deleted file mode 100644 index 16021f12..00000000 --- a/apis/database/v4/pdb_types.go +++ /dev/null @@ -1,237 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -package v4 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// PDBSpec defines the desired state of PDB -type PDBSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - PDBTlsKey PDBTLSKEY `json:"pdbTlsKey,omitempty"` - PDBTlsCrt PDBTLSCRT `json:"pdbTlsCrt,omitempty"` - PDBTlsCat PDBTLSCAT `json:"pdbTlsCat,omitempty"` - - // CDB Namespace - CDBNamespace string `json:"cdbNamespace,omitempty"` - // Name of the CDB Custom Resource that runs the ORDS container - CDBResName string `json:"cdbResName,omitempty"` - // Name of the CDB - CDBName string `json:"cdbName,omitempty"` - // The name of the new PDB. Relevant for both Create and Plug Actions. - PDBName string `json:"pdbName,omitempty"` - // Name of the Source PDB from which to clone - SrcPDBName string `json:"srcPdbName,omitempty"` - // The administrator username for the new PDB. This property is required when the Action property is Create. - AdminName PDBAdminName `json:"adminName,omitempty"` - // The administrator password for the new PDB. This property is required when the Action property is Create. - AdminPwd PDBAdminPassword `json:"adminPwd,omitempty"` - // Web Server User with SQL Administrator role to allow us to authenticate to the PDB Lifecycle Management REST endpoints - WebServerUsr WebServerUserPDB `json:"webServerUser,omitempty"` - // Password for the Web ServerPDB User - WebServerPwd WebServerPasswordPDB `json:"webServerPwd,omitempty"` - // Relevant for Create and Plug operations. As defined in the Oracle Multitenant Database documentation. Values can be a filename convert pattern or NONE. - FileNameConversions string `json:"fileNameConversions,omitempty"` - // This property is required when the Action property is Plug. As defined in the Oracle Multitenant Database documentation. Values can be a source filename convert pattern or NONE. - SourceFileNameConversions string `json:"sourceFileNameConversions,omitempty"` - // XML metadata filename to be used for Plug or Unplug operations - XMLFileName string `json:"xmlFileName,omitempty"` - // To copy files or not while cloning a PDB - // +kubebuilder:validation:Enum=COPY;NOCOPY;MOVE - CopyAction string `json:"copyAction,omitempty"` - // Specify if datafiles should be removed or not. The value can be INCLUDING or KEEP (default). - // +kubebuilder:validation:Enum=INCLUDING;KEEP - DropAction string `json:"dropAction,omitempty"` - // A Path specified for sparse clone snapshot copy. (Optional) - SparseClonePath string `json:"sparseClonePath,omitempty"` - // Whether to reuse temp file - ReuseTempFile *bool `json:"reuseTempFile,omitempty"` - // Relevant for Create and Plug operations. True for unlimited storage. Even when set to true, totalSize and tempSize MUST be specified in the request if Action is Create. - UnlimitedStorage *bool `json:"unlimitedStorage,omitempty"` - // Indicate if 'AS CLONE' option should be used in the command to plug in a PDB. This property is applicable when the Action property is PLUG but not required. - AsClone *bool `json:"asClone,omitempty"` - // Relevant for create and plug operations. Total size as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - TotalSize string `json:"totalSize,omitempty"` - // Relevant for Create and Clone operations. Total size for temporary tablespace as defined in the Oracle Multitenant Database documentation. See size_clause description in Database SQL Language Reference documentation. - TempSize string `json:"tempSize,omitempty"` - // TDE import for plug operations - TDEImport *bool `json:"tdeImport,omitempty"` - // TDE export for unplug operations - TDEExport *bool `json:"tdeExport,omitempty"` - // TDE password if the tdeImport or tdeExport flag is set to true. Can be used in create, plug or unplug operations - TDEPassword TDEPwd `json:"tdePassword,omitempty"` - // TDE keystore path is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - TDEKeystorePath string `json:"tdeKeystorePath,omitempty"` - // TDE secret is required if the tdeImport or tdeExport flag is set to true. Can be used in plug or unplug operations. - TDESecret TDESecret `json:"tdeSecret,omitempty"` - // Whether you need the script only or execute the script - GetScript *bool `json:"getScript,omitempty"` - // Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. Map is used to map a Databse PDB to a Kubernetes PDB CR. - // +kubebuilder:validation:Enum=Create;Clone;Plug;Unplug;Delete;Modify;Status;Map - Action string `json:"action"` - // Extra options for opening and closing a PDB - // +kubebuilder:validation:Enum=IMMEDIATE;NORMAL;READ ONLY;READ WRITE;RESTRICTED - ModifyOption string `json:"modifyOption,omitempty"` - // The target state of the PDB - // +kubebuilder:validation:Enum=OPEN;CLOSE - PDBState string `json:"pdbState,omitempty"` - // turn on the assertive approach to delete pdb resource - // kubectl delete pdb ..... automatically triggers the pluggable database - // deletion - AssertivePdbDeletion bool `json:"assertivePdbDeletion,omitempty"` - PDBPubKey PDBPUBKEY `json:"pdbOrdsPubKey,omitempty"` - PDBPriKey PDBPRIVKEY `json:"pdbOrdsPrvKey,omitempty"` -} - -// PDBAdminName defines the secret containing Sys Admin User mapped to key 'adminName' for PDB -type PDBAdminName struct { - Secret PDBSecret `json:"secret"` -} - -// PDBAdminPassword defines the secret containing Sys Admin Password mapped to key 'adminPwd' for PDB -type PDBAdminPassword struct { - Secret PDBSecret `json:"secret"` -} - -// TDEPwd defines the secret containing TDE Wallet Password mapped to key 'tdePassword' for PDB -type TDEPwd struct { - Secret PDBSecret `json:"secret"` -} - -// TDESecret defines the secret containing TDE Secret to key 'tdeSecret' for PDB -type TDESecret struct { - Secret PDBSecret `json:"secret"` -} - -// WebServerUser defines the secret containing Web Server User mapped to key 'webServerUser' to manage PDB lifecycle - -type WebServerUserPDB struct { - Secret PDBSecret `json:"secret"` -} - -// WebServerPassword defines the secret containing password for Web Server User mapped to key 'webServerPwd' to manage PDB lifecycle -type WebServerPasswordPDB struct { - Secret PDBSecret `json:"secret"` -} - -// PDBSecret defines the secretName -type PDBSecret struct { - SecretName string `json:"secretName"` - Key string `json:"key"` -} - -type PDBTLSKEY struct { - Secret PDBSecret `json:"secret"` -} - -type PDBTLSCRT struct { - Secret PDBSecret `json:"secret"` -} - -type PDBTLSCAT struct { - Secret PDBSecret `json:"secret"` -} - -// PDBStatus defines the observed state of PDB -type PDBStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // PDB Connect String - ConnString string `json:"connString,omitempty"` - // Phase of the PDB Resource - Phase string `json:"phase"` - // PDB Resource Status - Status bool `json:"status"` - // Total size of the PDB - TotalSize string `json:"totalSize,omitempty"` - // Open mode of the PDB - OpenMode string `json:"openMode,omitempty"` - // Modify Option of the PDB - ModifyOption string `json:"modifyOption,omitempty"` - // Message - Msg string `json:"msg,omitempty"` - // Last Completed Action - Action string `json:"action,omitempty"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:JSONPath=".spec.cdbName",name="CDB Name",type="string",description="Name of the CDB" -// +kubebuilder:printcolumn:JSONPath=".spec.pdbName",name="PDB Name",type="string",description="Name of the PDB" -// +kubebuilder:printcolumn:JSONPath=".status.openMode",name="PDB State",type="string",description="PDB Open Mode" -// +kubebuilder:printcolumn:JSONPath=".status.totalSize",name="PDB Size",type="string",description="Total Size of the PDB" -// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Status",type="string",description="Status of the PDB Resource" -// +kubebuilder:printcolumn:JSONPath=".status.msg",name="Message",type="string",description="Error message, if any" -// +kubebuilder:printcolumn:JSONPath=".status.connString",name="Connect_String",type="string",description="The connect string to be used" -// +kubebuilder:resource:path=pdbs,scope=Namespaced -// +kubebuilder:storageversion - -// PDB is the Schema for the pdbs API -type PDB struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec PDBSpec `json:"spec,omitempty"` - Status PDBStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// PDBList contains a list of PDB -type PDBList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []PDB `json:"items"` -} - -type PDBPUBKEY struct { - Secret PDBSecret `json:"secret"` -} - -type PDBPRIVKEY struct { - Secret PDBSecret `json:"secret"` -} - -func init() { - SchemeBuilder.Register(&PDB{}, &PDBList{}) -} diff --git a/apis/database/v4/pdb_webhook.go b/apis/database/v4/pdb_webhook.go deleted file mode 100644 index f651accf..00000000 --- a/apis/database/v4/pdb_webhook.go +++ /dev/null @@ -1,369 +0,0 @@ -/* -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. - */ - -/* MODIFIED (MM/DD/YY) -** rcitton 07/14/22 - 33822886 - */ - -package v4 - -import ( - "reflect" - "strconv" - "strings" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// log is for logging in this package. -var pdblog = logf.Log.WithName("pdb-webhook") - -func (r *PDB) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - -//+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-pdb,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v4,name=mpdb.kb.io,admissionReviewVersions={v1,v1beta1} - -var _ webhook.Defaulter = &PDB{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *PDB) Default() { - pdblog.Info("Setting default values in PDB spec for : " + r.Name) - - action := strings.ToUpper(r.Spec.Action) - - if action == "DELETE" { - if r.Spec.DropAction == "" { - r.Spec.DropAction = "INCLUDING" - pdblog.Info(" - dropAction : INCLUDING") - } - } else if action != "MODIFY" && action != "STATUS" { - if r.Spec.ReuseTempFile == nil { - r.Spec.ReuseTempFile = new(bool) - *r.Spec.ReuseTempFile = true - pdblog.Info(" - reuseTempFile : " + strconv.FormatBool(*(r.Spec.ReuseTempFile))) - } - if r.Spec.UnlimitedStorage == nil { - r.Spec.UnlimitedStorage = new(bool) - *r.Spec.UnlimitedStorage = true - pdblog.Info(" - unlimitedStorage : " + strconv.FormatBool(*(r.Spec.UnlimitedStorage))) - } - if r.Spec.TDEImport == nil { - r.Spec.TDEImport = new(bool) - *r.Spec.TDEImport = false - pdblog.Info(" - tdeImport : " + strconv.FormatBool(*(r.Spec.TDEImport))) - } - if r.Spec.TDEExport == nil { - r.Spec.TDEExport = new(bool) - *r.Spec.TDEExport = false - pdblog.Info(" - tdeExport : " + strconv.FormatBool(*(r.Spec.TDEExport))) - } - if r.Spec.AsClone == nil { - r.Spec.AsClone = new(bool) - *r.Spec.AsClone = false - pdblog.Info(" - asClone : " + strconv.FormatBool(*(r.Spec.AsClone))) - } - - } - - if r.Spec.GetScript == nil { - r.Spec.GetScript = new(bool) - *r.Spec.GetScript = false - pdblog.Info(" - getScript : " + strconv.FormatBool(*(r.Spec.GetScript))) - } -} - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:path=/validate-database-oracle-com-v4-pdb,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=pdbs,verbs=create;update,versions=v4,name=vpdb.kb.io,admissionReviewVersions={v1,v1beta1} - -var _ webhook.Validator = &PDB{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateCreate() (admission.Warnings, error) { - pdblog.Info("ValidateCreate-Validating PDB spec for : " + r.Name) - - var allErrs field.ErrorList - - r.validateCommon(&allErrs) - - r.validateAction(&allErrs) - - action := strings.ToUpper(r.Spec.Action) - - if len(allErrs) == 0 { - pdblog.Info("PDB Resource : " + r.Name + " successfully validated for Action : " + action) - return nil, nil - } - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "PDB"}, - r.Name, allErrs) -} - -// Validate Action for required parameters -func (r *PDB) validateAction(allErrs *field.ErrorList) { - action := strings.ToUpper(r.Spec.Action) - - pdblog.Info("Valdiating PDB Resource Action : " + action) - - if reflect.ValueOf(r.Spec.PDBTlsKey).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbTlsKey"), "Please specify PDB Tls Key(secret)")) - } - - if reflect.ValueOf(r.Spec.PDBTlsCrt).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbTlsCrt"), "Please specify PDB Tls Certificate(secret)")) - } - - if reflect.ValueOf(r.Spec.PDBTlsCat).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbTlsCat"), "Please specify PDB Tls Certificate Authority(secret)")) - } - if reflect.ValueOf(r.Spec.PDBPriKey).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbOrdsPrvKey"), "Please specify PDB Tls Certificate Authority(secret)")) - } - - switch action { - case "DELETE": - /* BUG 36752336 - LREST OPERATOR - DELETE NON-EXISTENT PDB SHOWS LRPDB CREATED MESSAGE */ - if r.Status.OpenMode == "READ WRITE" { - pdblog.Info("Cannot delete: pdb is open ") - *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+r.Spec.PDBName+" "+r.Status.OpenMode)) - } - r.CheckObjExistence("DELETE", allErrs, r) - case "CREATE": - if reflect.ValueOf(r.Spec.AdminName).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("adminName"), "Please specify PDB System Administrator user")) - } - if reflect.ValueOf(r.Spec.AdminPwd).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("adminPwd"), "Please specify PDB System Administrator Password")) - } - if reflect.ValueOf(r.Spec.WebServerUsr).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("WebServerUser"), "Please specify the http webServerUser")) - } - if reflect.ValueOf(r.Spec.WebServerPwd).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("webServerPwd"), "Please specify the http webserverPassword")) - } - - if r.Spec.FileNameConversions == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("fileNameConversions"), "Please specify a value for fileNameConversions. Values can be a filename convert pattern or NONE")) - } - if r.Spec.TotalSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("totalSize"), "When the storage is not UNLIMITED the Total Size must be specified")) - } - if r.Spec.TempSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) - } - if *(r.Spec.TDEImport) { - r.validateTDEInfo(allErrs) - } - case "CLONE": - // Sample Err: The PDB "pdb1-clone" is invalid: spec.srcPdbName: Required value: Please specify source PDB for Cloning - if r.Spec.SrcPDBName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("srcPdbName"), "Please specify source PDB name for Cloning")) - } - if r.Spec.TotalSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("totalSize"), "When the storage is not UNLIMITED the Total Size must be specified")) - } - if r.Spec.TempSize == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tempSize"), "When the storage is not UNLIMITED the Temp Size must be specified")) - } - /* We don't need this check as ords open the pdb before cloninig */ - /* - if r.Status.OpenMode == "MOUNTED" { - pdblog.Info("Cannot clone: pdb is mount ") - *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+r.Spec.PDBName+" "+r.Status.OpenMode)) - } - */ - case "PLUG": - if r.Spec.XMLFileName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("xmlFileName"), "Please specify XML metadata filename")) - } - if r.Spec.FileNameConversions == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("fileNameConversions"), "Please specify a value for fileNameConversions. Values can be a filename convert pattern or NONE")) - } - if r.Spec.SourceFileNameConversions == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("sourceFileNameConversions"), "Please specify a value for sourceFileNameConversions. Values can be a filename convert pattern or NONE")) - } - if r.Spec.CopyAction == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("copyAction"), "Please specify a value for copyAction. Values can be COPY, NOCOPY or MOVE")) - } - if *(r.Spec.TDEImport) { - r.validateTDEInfo(allErrs) - } - case "UNPLUG": - if r.Spec.XMLFileName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("xmlFileName"), "Please specify XML metadata filename")) - } - if *(r.Spec.TDEExport) { - r.validateTDEInfo(allErrs) - } - if r.Status.OpenMode == "READ WRITE" { - pdblog.Info("Cannot unplug: pdb is open ") - *allErrs = append(*allErrs, field.Invalid(field.NewPath("status").Child("OpenMode"), "READ WRITE", "pdb "+r.Spec.PDBName+" "+r.Status.OpenMode)) - } - r.CheckObjExistence("UNPLUG", allErrs, r) - case "MODIFY": - if r.Spec.PDBState == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbState"), "Please specify target state of PDB")) - } - if r.Spec.ModifyOption == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("modifyOption"), "Please specify an option for opening/closing a PDB")) - } - r.CheckObjExistence("MODIY", allErrs, r) - } -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { - pdblog.Info("ValidateUpdate-Validating PDB spec for : " + r.Name) - - isPDBMarkedToBeDeleted := r.GetDeletionTimestamp() != nil - if isPDBMarkedToBeDeleted { - return nil, nil - } - - var allErrs field.ErrorList - action := strings.ToUpper(r.Spec.Action) - - // If PDB CR has been created and in Ready state, only allow updates if the "action" value has changed as well - if (r.Status.Phase == "Ready") && (r.Status.Action != "MODIFY") && (r.Status.Action != "STATUS") && (r.Status.Action == action) { - allErrs = append(allErrs, - field.Required(field.NewPath("spec").Child("action"), "New action also needs to be specified after PDB is in Ready state")) - } else { - - // Check Common Validations - r.validateCommon(&allErrs) - - // Validate required parameters for Action specified - r.validateAction(&allErrs) - - // Check TDE requirements - if (action != "DELETE") && (action != "MODIFY") && (action != "STATUS") && (*(r.Spec.TDEImport) || *(r.Spec.TDEExport)) { - r.validateTDEInfo(&allErrs) - } - } - - if len(allErrs) == 0 { - return nil, nil - } - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "PDB"}, - r.Name, allErrs) -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *PDB) ValidateDelete() (admission.Warnings, error) { - pdblog.Info("ValidateDelete-Validating PDB spec for : " + r.Name) - - // TODO(user): fill in your validation logic upon object deletion. - return nil, nil -} - -// Validate common specs needed for all PDB Actions -func (r *PDB) validateCommon(allErrs *field.ErrorList) { - pdblog.Info("validateCommon", "name", r.Name) - - if r.Spec.Action == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("action"), "Please specify PDB operation to be performed")) - } - if r.Spec.CDBResName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("cdbResName"), "Please specify the name of the CDB Kubernetes resource to use for PDB operations")) - } - if r.Spec.PDBName == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("pdbName"), "Please specify name of the PDB to be created")) - } -} - -// Validate TDE information for Create, Plug and Unplug Actions -func (r *PDB) validateTDEInfo(allErrs *field.ErrorList) { - pdblog.Info("validateTDEInfo", "name", r.Name) - - if reflect.ValueOf(r.Spec.TDEPassword).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tdePassword"), "Please specify a value for tdePassword.")) - } - if r.Spec.TDEKeystorePath == "" { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tdeKeystorePath"), "Please specify a value for tdeKeystorePath.")) - } - if reflect.ValueOf(r.Spec.TDESecret).IsZero() { - *allErrs = append(*allErrs, - field.Required(field.NewPath("spec").Child("tdeSecret"), "Please specify a value for tdeSecret.")) - } - -} - -func (r *PDB) CheckObjExistence(action string, allErrs *field.ErrorList, pdb *PDB) { - /* BUG 36752465 - lrest operator - open non-existent pdb creates a lrpdb with status failed */ - pdblog.Info("Action [" + action + "] checkin " + pdb.Spec.PDBName + " existence") - if pdb.Status.OpenMode == "" { - *allErrs = append(*allErrs, field.NotFound(field.NewPath("Spec").Child("PDBName"), " "+pdb.Spec.PDBName+" does not exist : action "+action+" failure")) - - } -} diff --git a/apis/database/v4/shardingdatabase_webhook.go b/apis/database/v4/shardingdatabase_webhook.go index 071578e2..56922a7b 100644 --- a/apis/database/v4/shardingdatabase_webhook.go +++ b/apis/database/v4/shardingdatabase_webhook.go @@ -63,13 +63,14 @@ var shardingdatabaselog = logf.Log.WithName("shardingdatabase-resource") func (r *ShardingDatabase) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(&ShardingDatabase{}). - WithDefaulter(r). + WithDefaulter(r). WithValidator(r). Complete() } var _ webhook.CustomDefaulter = &ShardingDatabase{} + // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! //+kubebuilder:webhook:path=/mutate-database-oracle-com-v4-shardingdatabase,mutating=true,failurePolicy=fail,sideEffects=none,groups=database.oracle.com,resources=shardingdatabases,verbs=create;update,versions=v4,name=mshardingdatabasev4.kb.io,admissionReviewVersions=v1 @@ -77,16 +78,17 @@ var _ webhook.CustomDefaulter = &ShardingDatabase{} // Default implements webhook.Defaulter so a webhook will be registered for the type func (r *ShardingDatabase) Default(ctx context.Context, obj runtime.Object) error { - cr, ok := obj.(*ShardingDatabase) + cr , ok := obj.(*ShardingDatabase) - if !ok { - return fmt.Errorf("xpected obj.*ShardingDatabase but got %T", obj) - } + if !ok { + return fmt.Errorf("xpected obj.*ShardingDatabase but got %T", obj) + } shardingdatabaselog.Info("default", "name", cr.Name) var replicas int32 + // TODO(user): fill in your defaulting logic. if cr.Spec.GsmDevMode != "" { cr.Spec.GsmDevMode = "dev" @@ -137,15 +139,16 @@ func (r *ShardingDatabase) ValidateCreate(ctx context.Context, obj runtime.Objec // Check Secret configuration var validationErr field.ErrorList var validationErrs1 field.ErrorList - cr, ok := obj.(*ShardingDatabase) - - if !ok { - // return fmt.Errorf("xpected obj.*ShardingDatabase but got %T", obj) - validationErr = append(validationErr, field.Invalid(field.NewPath("obj"), "obj", "Expected obj.*ShardingDatabase.")) - return nil, apierrors.NewInvalid( - schema.GroupKind{Group: "database.oracle.com", Kind: "ShardingDatabase"}, - cr.Name, validationErr) - } + cr , ok := obj.(*ShardingDatabase) + + if !ok { +// return fmt.Errorf("xpected obj.*ShardingDatabase but got %T", obj) + validationErr = append(validationErr,field.Invalid(field.NewPath("obj"),"obj","Expected obj.*ShardingDatabase.")) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "database.oracle.com", Kind: "ShardingDatabase"}, + cr.Name, validationErr) + } + //namespaces := db.GetWatchNamespaces() //_, containsNamespace := namespaces[r.Namespace] diff --git a/apis/observability/v1/databaseobserver_webhook.go b/apis/observability/v1/databaseobserver_webhook.go index 550d97e1..809549d3 100644 --- a/apis/observability/v1/databaseobserver_webhook.go +++ b/apis/observability/v1/databaseobserver_webhook.go @@ -40,7 +40,6 @@ package v1 import ( "context" - dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" diff --git a/apis/observability/v1/zz_generated.deepcopy.go b/apis/observability/v1/zz_generated.deepcopy.go index 4c8cf74a..0991c186 100644 --- a/apis/observability/v1/zz_generated.deepcopy.go +++ b/apis/observability/v1/zz_generated.deepcopy.go @@ -595,4 +595,3 @@ func (in *WalletSecret) DeepCopy() *WalletSecret { in.DeepCopyInto(out) return out } - diff --git a/apis/observability/v1alpha1/databaseobserver_webhook.go b/apis/observability/v1alpha1/databaseobserver_webhook.go index 99054d29..da683b6b 100644 --- a/apis/observability/v1alpha1/databaseobserver_webhook.go +++ b/apis/observability/v1alpha1/databaseobserver_webhook.go @@ -40,7 +40,6 @@ package v1alpha1 import ( "context" - dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" diff --git a/apis/observability/v1alpha1/zz_generated.deepcopy.go b/apis/observability/v1alpha1/zz_generated.deepcopy.go index c016a126..c5e76104 100644 --- a/apis/observability/v1alpha1/zz_generated.deepcopy.go +++ b/apis/observability/v1alpha1/zz_generated.deepcopy.go @@ -595,4 +595,3 @@ func (in *WalletSecret) DeepCopy() *WalletSecret { in.DeepCopyInto(out) return out } - diff --git a/apis/observability/v4/databaseobserver_webhook.go b/apis/observability/v4/databaseobserver_webhook.go index d868297b..3cb8be87 100644 --- a/apis/observability/v4/databaseobserver_webhook.go +++ b/apis/observability/v4/databaseobserver_webhook.go @@ -40,7 +40,6 @@ package v4 import ( "context" - dbcommons "github.com/oracle/oracle-database-operator/commons/database" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" diff --git a/apis/observability/v4/zz_generated.deepcopy.go b/apis/observability/v4/zz_generated.deepcopy.go index 28ba80a8..1d3e1c11 100644 --- a/apis/observability/v4/zz_generated.deepcopy.go +++ b/apis/observability/v4/zz_generated.deepcopy.go @@ -595,4 +595,3 @@ func (in *WalletSecret) DeepCopy() *WalletSecret { in.DeepCopyInto(out) return out } - diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go index 0f03532e..4aa6929b 100644 --- a/commons/dbcssystem/dbcs_reconciler.go +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -1348,6 +1348,7 @@ func mergeInstancesFromLatest(instance, latestInstance *databasev4.DbcsSystem) e func mergeStructFields(instanceField, latestField reflect.Value) { for i := 0; i < instanceField.NumField(); i++ { subField := instanceField.Type().Field(i) + instanceSubField := instanceField.Field(i) latestSubField := latestField.Field(i) @@ -1371,6 +1372,7 @@ func mergeStructFields(instanceField, latestField reflect.Value) { } } } + } func isExported(field reflect.StructField) bool { @@ -1770,6 +1772,7 @@ func UpdateDbcsSystemIdInst(compartmentId string, log logr.Logger, dbClient data } log.Info("Details of updateFlag after validations is " + fmt.Sprint(updateFlag)) + if updateFlag { cdbId := *dbcs.Status.DbInfo[0].Id // Ensure DB system is AVAILABLE diff --git a/commons/observability/utils.go b/commons/observability/utils.go index e3ed6a68..693c9718 100644 --- a/commons/observability/utils.go +++ b/commons/observability/utils.go @@ -1,13 +1,12 @@ package observability import ( - "path/filepath" - "strings" - api "github.com/oracle/oracle-database-operator/apis/observability/v4" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" + "path/filepath" + "strings" ) func AddSidecarContainers(a *api.DatabaseObserver, listing *[]corev1.Container) { diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 9c72dc54..fc517a3a 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -703,7 +703,9 @@ func VolumePVForASM(instance *oraclerestart.OracleRestart, index int, diskName s asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} } - asmPvc.Spec.StorageClassName = *scName + if scName != nil { + asmPvc.Spec.StorageClassName = *scName + } return asmPvc } diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index d7035c62..db7ae50f 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -49,9 +49,9 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -139,14 +139,14 @@ func buildPodSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalogSpe group := oraFsGroup spec := &corev1.PodSpec{ SecurityContext: &corev1.PodSecurityContext{ - RunAsNonRoot: BoolPointer(true), - RunAsUser: &user, - RunAsGroup: &group, - FSGroup: &group, + RunAsNonRoot: BoolPointer(true), + RunAsUser: &user, + RunAsGroup: &group, + FSGroup: &group, }, - Containers: buildContainerSpecForCatalog(instance, OraCatalogSpex), - Volumes: buildVolumeSpecForCatalog(instance, OraCatalogSpex), - ServiceAccountName: instance.Spec.SrvAccountName, + Containers: buildContainerSpecForCatalog(instance, OraCatalogSpex), + Volumes: buildVolumeSpecForCatalog(instance, OraCatalogSpex), + ServiceAccountName: instance.Spec.SrvAccountName, } if (instance.Spec.IsDownloadScripts) && (instance.Spec.ScriptsLocation != "") { @@ -190,9 +190,9 @@ func buildVolumeSpecForCatalog(instance *databasev4.ShardingDatabase, OraCatalog }, } - if OraCatalogSpex.CatalogConfigData != nil && len(OraCatalogSpex.CatalogConfigData.Name) != 0 { - result = append(result, corev1.Volume{Name: OraCatalogSpex.Name + "-oradata-configdata", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OraCatalogSpex.CatalogConfigData.Name}}}}) - } + if OraCatalogSpex.CatalogConfigData != nil && len(OraCatalogSpex.CatalogConfigData.Name) != 0 { + result = append(result, corev1.Volume{Name: OraCatalogSpex.Name + "-oradata-configdata", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OraCatalogSpex.CatalogConfigData.Name}}}}) + } if len(OraCatalogSpex.PvcName) != 0 { result = append(result, corev1.Volume{Name: OraCatalogSpex.Name + "oradata-vol4", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OraCatalogSpex.PvcName}}}) @@ -225,13 +225,13 @@ func buildContainerSpecForCatalog(instance *databasev4.ShardingDatabase, OraCata Name: OraCatalogSpex.Name, Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ - RunAsNonRoot: BoolPointer(true), - RunAsUser: &user, - RunAsGroup: &group, - AllowPrivilegeEscalation: BoolPointer(false), + RunAsNonRoot: BoolPointer(true), + RunAsUser: &user, + RunAsGroup: &group, + AllowPrivilegeEscalation: BoolPointer(false), Capabilities: &corev1.Capabilities{ - Add: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("SYS_NICE")}, - Drop: []corev1.Capability{"ALL"}, + Add: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("SYS_NICE")}, + Drop: []corev1.Capability{"ALL",}, }, }, Resources: corev1.ResourceRequirements{ @@ -315,12 +315,12 @@ func buildInitContainerSpecForCatalog(instance *databasev4.ShardingDatabase, Ora Name: OraCatalogSpex.Name + "-init1", Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ - RunAsNonRoot: BoolPointer(true), - AllowPrivilegeEscalation: BoolPointer(false), - Privileged: &privFlag, - RunAsUser: &uid, + RunAsNonRoot: BoolPointer(true), + AllowPrivilegeEscalation: BoolPointer(false), + Privileged: &privFlag, + RunAsUser: &uid, Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"ALL"}, + Drop: []corev1.Capability{"ALL",}, }, }, Command: []string{ @@ -351,8 +351,8 @@ func buildVolumeMountSpecForCatalog(instance *databasev4.ShardingDatabase, OraCa result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "oradshm-vol6", MountPath: oraShm}) if OraCatalogSpex.CatalogConfigData != nil && len(OraCatalogSpex.CatalogConfigData.Name) != 0 { - result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "-oradata-configdata", MountPath: OraCatalogSpex.CatalogConfigData.MountPath}) - } + result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "-oradata-configdata", MountPath: OraCatalogSpex.CatalogConfigData.MountPath}) + } if len(instance.Spec.StagePvcName) != 0 { result = append(result, corev1.VolumeMount{Name: OraCatalogSpex.Name + "orastage-vol7", MountPath: oraStage}) @@ -552,10 +552,10 @@ func UpdateProvForCatalog(instance *databasev4.ShardingDatabase, return ctrl.Result{}, nil } -func ExportTDEKey(podName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger) error { +func ExportTDEKey(podName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger,) error { var msg string - msg = "" + msg = "" _, _, err := ExecCommand(podName, getExportTDEKeyCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { msg = "Error executing getExportTDEKeyCmd : podName=[" + podName + "]. errMsg=" + err.Error() diff --git a/commons/sharding/gsm.go b/commons/sharding/gsm.go index 2a59ea29..5e0d5c46 100644 --- a/commons/sharding/gsm.go +++ b/commons/sharding/gsm.go @@ -142,14 +142,14 @@ func buildPodSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex databa group := oraFsGroup spec := &corev1.PodSpec{ SecurityContext: &corev1.PodSecurityContext{ - RunAsNonRoot: BoolPointer(true), - RunAsUser: &user, - RunAsGroup: &group, - FSGroup: &group, + RunAsNonRoot: BoolPointer(true), + RunAsUser: &user, + RunAsGroup: &group, + FSGroup: &group, }, - Containers: buildContainerSpecForGsm(instance, OraGsmSpex), - Volumes: buildVolumeSpecForGsm(instance, OraGsmSpex), - ServiceAccountName: instance.Spec.SrvAccountName, + Containers: buildContainerSpecForGsm(instance, OraGsmSpex), + Volumes: buildVolumeSpecForGsm(instance, OraGsmSpex), + ServiceAccountName: instance.Spec.SrvAccountName, } if (instance.Spec.IsDownloadScripts) && (instance.Spec.ScriptsLocation != "") { @@ -193,8 +193,8 @@ func buildVolumeSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex dat } if OraGsmSpex.GsmConfigData != nil && len(OraGsmSpex.GsmConfigData.Name) != 0 { - result = append(result, corev1.Volume{Name: OraGsmSpex.Name + "-oradata-configdata", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OraGsmSpex.GsmConfigData.Name}}}}) - } + result = append(result, corev1.Volume{Name: OraGsmSpex.Name + "-oradata-configdata", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OraGsmSpex.GsmConfigData.Name}}}}) + } if len(OraGsmSpex.PvcName) != 0 { result = append(result, corev1.Volume{Name: OraGsmSpex.Name + "oradata-vol4", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OraGsmSpex.PvcName}}}) @@ -232,13 +232,13 @@ func buildContainerSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpex Name: OraGsmSpex.Name, Image: instance.Spec.GsmImage, SecurityContext: &corev1.SecurityContext{ - RunAsNonRoot: BoolPointer(true), - RunAsUser: &user, - RunAsGroup: &group, - AllowPrivilegeEscalation: BoolPointer(false), + RunAsNonRoot: BoolPointer(true), + RunAsUser: &user, + RunAsGroup: &group, + AllowPrivilegeEscalation: BoolPointer(false), Capabilities: &corev1.Capabilities{ - Add: []corev1.Capability{"NET_RAW"}, - Drop: []corev1.Capability{"ALL"}, + Add: []corev1.Capability{"NET_RAW"}, + Drop: []corev1.Capability{"ALL",}, }, }, Resources: corev1.ResourceRequirements{ @@ -302,12 +302,12 @@ func buildInitContainerSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmS Name: OraGsmSpex.Name + "-init1", Image: instance.Spec.GsmImage, SecurityContext: &corev1.SecurityContext{ - RunAsNonRoot: BoolPointer(true), - AllowPrivilegeEscalation: BoolPointer(false), - Privileged: &privFlag, - RunAsUser: &uid, + RunAsNonRoot: BoolPointer(true), + AllowPrivilegeEscalation: BoolPointer(false), + Privileged: &privFlag, + RunAsUser: &uid, Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"ALL"}, + Drop: []corev1.Capability{"ALL",}, }, }, Command: []string{ @@ -337,9 +337,9 @@ func buildVolumeMountSpecForGsm(instance *databasev4.ShardingDatabase, OraGsmSpe } result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "oradshm-vol6", MountPath: oraShm}) - if OraGsmSpex.GsmConfigData != nil && len(OraGsmSpex.GsmConfigData.Name) != 0 { - result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "-oradata-configdata", MountPath: OraGsmSpex.GsmConfigData.MountPath}) - } + if OraGsmSpex.GsmConfigData != nil && len(OraGsmSpex.GsmConfigData.Name) != 0 { + result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "-oradata-configdata", MountPath: OraGsmSpex.GsmConfigData.MountPath}) + } if len(instance.Spec.StagePvcName) != 0 { result = append(result, corev1.VolumeMount{Name: OraGsmSpex.Name + "orastage-vol7", MountPath: oraStage}) diff --git a/commons/sharding/scommon.go b/commons/sharding/scommon.go index 9b0cc35b..f284e5c6 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -920,6 +920,7 @@ func buildDirectorParams(instance *databasev4.ShardingDatabase, oraGsmSpex datab varinfo = "director_port=" + dport result = result + varinfo } + result = strings.TrimSuffix(result, ";") return result } diff --git a/commons/sharding/shard.go b/commons/sharding/shard.go index ad35d599..244589ab 100644 --- a/commons/sharding/shard.go +++ b/commons/sharding/shard.go @@ -51,9 +51,9 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -141,14 +141,14 @@ func buildPodSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex da group := oraFsGroup spec := &corev1.PodSpec{ SecurityContext: &corev1.PodSecurityContext{ - RunAsNonRoot: BoolPointer(true), - RunAsUser: &user, - RunAsGroup: &group, - FSGroup: &group, + RunAsNonRoot: BoolPointer(true), + RunAsUser: &user, + RunAsGroup: &group, + FSGroup: &group, }, - Containers: buildContainerSpecForShard(instance, OraShardSpex), - Volumes: buildVolumeSpecForShard(instance, OraShardSpex), - ServiceAccountName: instance.Spec.SrvAccountName, + Containers: buildContainerSpecForShard(instance, OraShardSpex), + Volumes: buildVolumeSpecForShard(instance, OraShardSpex), + ServiceAccountName: instance.Spec.SrvAccountName, } if (instance.Spec.IsDownloadScripts) && (instance.Spec.ScriptsLocation != "") { @@ -193,9 +193,9 @@ func buildVolumeSpecForShard(instance *databasev4.ShardingDatabase, OraShardSpex }, } - if OraShardSpex.ShardConfigData != nil && len(OraShardSpex.ShardConfigData.Name) != 0 { - result = append(result, corev1.Volume{Name: OraShardSpex.Name + "-oradata-configdata", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OraShardSpex.ShardConfigData.Name}}}}) - } + if OraShardSpex.ShardConfigData != nil && len(OraShardSpex.ShardConfigData.Name) != 0 { + result = append(result, corev1.Volume{Name: OraShardSpex.Name + "-oradata-configdata", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: OraShardSpex.ShardConfigData.Name}}}}) + } if len(OraShardSpex.PvcName) != 0 { result = append(result, corev1.Volume{Name: OraShardSpex.Name + "oradata-vol4", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OraShardSpex.PvcName}}}) @@ -227,13 +227,13 @@ func buildContainerSpecForShard(instance *databasev4.ShardingDatabase, OraShardS Name: OraShardSpex.Name, Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ - RunAsNonRoot: BoolPointer(true), - RunAsUser: &user, - RunAsGroup: &group, - AllowPrivilegeEscalation: BoolPointer(false), + RunAsNonRoot: BoolPointer(true), + RunAsUser: &user, + RunAsGroup: &group, + AllowPrivilegeEscalation: BoolPointer(false), Capabilities: &corev1.Capabilities{ - Add: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("SYS_NICE")}, - Drop: []corev1.Capability{"ALL"}, + Add: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("SYS_NICE")}, + Drop: []corev1.Capability{"ALL",}, }, }, Resources: corev1.ResourceRequirements{ @@ -321,12 +321,12 @@ func buildInitContainerSpecForShard(instance *databasev4.ShardingDatabase, OraSh Name: OraShardSpex.Name + "-init1", Image: instance.Spec.DbImage, SecurityContext: &corev1.SecurityContext{ - RunAsNonRoot: BoolPointer(true), - AllowPrivilegeEscalation: BoolPointer(false), - Privileged: &privFlag, - RunAsUser: &uid, + RunAsNonRoot: BoolPointer(true), + AllowPrivilegeEscalation: BoolPointer(false), + Privileged: &privFlag, + RunAsUser: &uid, Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"ALL"}, + Drop: []corev1.Capability{"ALL",}, }, }, Command: []string{ @@ -356,9 +356,9 @@ func buildVolumeMountSpecForShard(instance *databasev4.ShardingDatabase, OraShar } result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "oradshm-vol6", MountPath: oraShm}) - if OraShardSpex.ShardConfigData != nil && len(OraShardSpex.ShardConfigData.Name) != 0 { - result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "-oradata-configdata", MountPath: OraShardSpex.ShardConfigData.MountPath}) - } + if OraShardSpex.ShardConfigData != nil && len(OraShardSpex.ShardConfigData.Name) != 0 { + result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "-oradata-configdata", MountPath: OraShardSpex.ShardConfigData.MountPath}) + } if len(instance.Spec.StagePvcName) != 0 { result = append(result, corev1.VolumeMount{Name: OraShardSpex.Name + "orastage-vol7", MountPath: oraStage}) @@ -537,10 +537,10 @@ func UpdateProvForShard(instance *databasev4.ShardingDatabase, OraShardSpex data return ctrl.Result{}, nil } -func ImportTDEKey(podName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger) error { +func ImportTDEKey(podName string, sparams string, instance *databasev4.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger,) error { var msg string - msg = "" + msg = "" _, _, err := ExecCommand(podName, getImportTDEKeyCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { msg = "Error executing getImportTDEKeyCmd : podName=[" + podName + "]. errMsg=" + err.Error() @@ -548,8 +548,8 @@ func ImportTDEKey(podName string, sparams string, instance *databasev4.ShardingD return err } - importArr := getImportTDEKeyCmd(sparams) - importCmd := strings.Join(importArr, " ") + importArr := getImportTDEKeyCmd(sparams) + importCmd := strings.Join(importArr, " ") msg = "Executed getImportTDEKeyCmd[" + importCmd + "] on pod " + podName LogMessages("INFO", msg, nil, instance, logger) return nil diff --git a/controllers/database/lrest_controller.go b/controllers/database/lrest_controller.go index c73bb988..a7b07816 100644 --- a/controllers/database/lrest_controller.go +++ b/controllers/database/lrest_controller.go @@ -241,6 +241,7 @@ func (r *LRESTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl if err := r.Status().Update(ctx, lrest); err != nil { log.Error(err, "Failed to update status for :"+lrest.Name, "err", err.Error()) } + return requeueY, nil } diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index b5363b6d..3d3ca021 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -443,21 +443,6 @@ func (r *ShardingDatabaseReconciler) eventFilterPredicate() predicate.Predicate return true }, UpdateFunc: func(e event.UpdateEvent) bool { - instance := &databasev4.ShardingDatabase{} - if old, ok := e.ObjectOld.(*corev1.Secret); ok { - if new, ok := e.ObjectNew.(*corev1.Secret); ok { - oshInst := instance - if (new.Name == oshInst.Spec.DbSecret.Name) && (new.Name == old.Name) { - _, ok := old.Data[oshInst.Spec.DbSecret.PwdFileName] - if ok { - if !reflect.DeepEqual(old.Data[oshInst.Spec.DbSecret.PwdFileName], new.Data[oshInst.Spec.DbSecret.PwdFileName]) { - shardingv1.LogMessages("INFO", "Secret Changed", nil, oshInst, r.Log) - } - } - shardingv1.LogMessages("INFO", "Secret update block", nil, oshInst, r.Log) - } - } - } return true }, DeleteFunc: func(e event.DeleteEvent) bool { diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index 0f5c8466..b9352d83 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -3241,6 +3241,7 @@ func (r *SingleInstanceDatabaseReconciler) manageConvPhysicalToSnapshot(ctx cont if err != nil { return requeueY, err } + if sidbReadyPod.Name == "" { log.Info("No ready Pod for the requested singleinstancedatabase") return requeueY, nil diff --git a/controllers/observability/databaseobserver_controller.go b/controllers/observability/databaseobserver_controller.go index 2f96ac4c..468f35c5 100644 --- a/controllers/observability/databaseobserver_controller.go +++ b/controllers/observability/databaseobserver_controller.go @@ -41,8 +41,6 @@ package controllers import ( "context" "errors" - "time" - "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -56,6 +54,7 @@ import ( "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "time" api "github.com/oracle/oracle-database-operator/apis/observability/v4" constants "github.com/oracle/oracle-database-operator/commons/observability" @@ -504,6 +503,7 @@ func (r *DatabaseObserverReconciler) validateCustomResourceReadiness(ctx context Message: constants.MessageCRValidationWaiting, }) a.Status.Status = string(constants.StatusObservabilityPending) + } else if meta.IsStatusConditionFalse(a.Status.Conditions, constants.IsExporterDeploymentReady) || meta.IsStatusConditionFalse(a.Status.Conditions, constants.IsExporterServiceReady) || meta.IsStatusConditionFalse(a.Status.Conditions, constants.IsExporterServiceMonitorReady) { diff --git a/main.go b/main.go index b2012e76..e717978a 100644 --- a/main.go +++ b/main.go @@ -252,10 +252,6 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "LREST") os.Exit(1) } - if err = (&databasev4.LREST{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "LREST") - os.Exit(1) - } if err = (&databasev1alpha1.AutonomousDatabase{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "AutonomousDatabase") os.Exit(1) @@ -407,14 +403,6 @@ func main() { os.Exit(1) } - indexFunc2 := func(obj client.Object) []string { - return []string{obj.(*databasev4.LRPDB).Spec.LRPDBName} - } - if err = cache.IndexField(context.TODO(), &databasev4.LRPDB{}, "spec.pdbName", indexFunc2); err != nil { - setupLog.Error(err, "unable to create index function for ", "controller", "LRPDB") - os.Exit(1) - } - setupLog.Info("starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { setupLog.Error(err, "problem running manager") From dff454d789d404aa62822b03b63ce64bc255cc97 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Sun, 5 Oct 2025 00:47:40 +0000 Subject: [PATCH 369/414] Added files --- config/database.oracle.com_DbcsSystem.yaml | 433 --------- ...acle.com_autonomouscontainerdatabases.yaml | 117 --- ....oracle.com_autonomousdatabasebackups.yaml | 138 --- ...oracle.com_autonomousdatabaserestores.yaml | 138 --- ...tabase.oracle.com_autonomousdatabases.yaml | 324 ------- config/database.oracle.com_cdbs.yaml | 270 ------ .../database.oracle.com_dataguardbrokers.yaml | 134 --- ...ase.oracle.com_oraclerestdataservices.yaml | 224 ----- config/database.oracle.com_pdbs.yaml | 383 -------- ...database.oracle.com_shardingdatabases.yaml | 688 ------------- ...se.oracle.com_singleinstancedatabases.yaml | 421 -------- config/manager/kustomization.yaml | 2 +- config/rbac/role.yaml | 26 - .../adb/autonomousdatabase_wallet.yaml | 27 - .../databaseobserver_custom_config.yaml | 28 - .../databaseobserver_minimal.yaml | 22 - .../observability/databaseobserver_vault.yaml | 25 - .../v1alpha1/databaseobserver_minimal.yaml | 2 +- .../v1alpha1/databaseobserver_vault.yaml | 30 - .../v4/databaseobserver_custom_config.yaml | 2 +- .../v4/databaseobserver_vault.yaml | 39 - config/webhook/manifests.yaml | 2 +- .../bind_to_existing_dbcs_system.yaml | 2 +- ..._dbcs_system_from_backup_sample_output.log | 2 +- ...bcs_system_from_database_sample_output.log | 2 +- .../clone_dbcs_system_sample_output.log | 2 +- .../scale_up_dbcs_system_shape.yaml | 10 +- docs/multitenant/NamespaceSeg.md | 14 - docs/multitenant/README.md | 1 - .../images/Generalschema2.jpg | Bin .../images/KubectlGetSchema.jpg | Bin .../images/KubectlGetSchema2.jpg | Bin .../images/old_testcase_wf.jpg | Bin .../{ords-based => }/images/plsqlmap.jpg | Bin .../{ords-based => }/images/usecaseschema.jpg | Bin docs/multitenant/lrest-based/README.md | 501 ---------- .../lrest-based/images/UsecaseSchema.jpg | Bin 185861 -> 0 bytes .../multitenant/lrest-based/usecase/README.md | 139 --- .../usecase/altersystem_pdb1_resource.yaml | 50 - .../usecase/cdbnamespace_binding.yaml | 13 - .../usecase/clone_pdb1_resource.yaml | 51 - .../usecase/clone_pdb2_resource.yaml | 51 - .../usecase/close_pdb1_resource.yaml | 47 - .../usecase/close_pdb2_resource.yaml | 47 - .../usecase/close_pdb3_resource.yaml | 47 - .../lrest-based/usecase/config-map-pdb.yaml | 11 - .../lrest-based/usecase/config_map_pdb.yaml | 11 - .../lrest-based/usecase/create_lrest_pod.yaml | 44 - .../usecase/create_pdb1_resource.yaml | 52 - .../usecase/create_pdb2_resource.yaml | 52 - .../usecase/delete_pdb1_resource.yaml | 45 - .../usecase/delete_pdb2_resource.yaml | 45 - docs/multitenant/lrest-based/usecase/makefile | 911 ----------------- .../usecase/map_pdb1_resource.yaml | 49 - .../usecase/map_pdb2_resource.yaml | 49 - .../usecase/map_pdb3_resource.yaml | 49 - .../usecase/open_pdb1_resource.yaml | 47 - .../usecase/open_pdb2_resource.yaml | 47 - .../usecase/open_pdb3_resource.yaml | 47 - .../lrest-based/usecase/parameters.txt | 52 - .../usecase/pdbnamespace_binding.yaml | 13 - .../usecase/plug_pdb1_resource.yaml | 54 -- .../usecase/unplug_pdb1_resource.yaml | 46 - docs/multitenant/ords-based/NamespaceSeg.md | 14 - docs/multitenant/ords-based/README.md | 411 -------- .../ords-based/images/Generalschema2.jpg | Bin 96239 -> 0 bytes .../ords-based/images/K8S_NAMESPACE_SEG.png | Bin 269014 -> 0 bytes .../ords-based/images/makerunall.png | Bin 211874 -> 0 bytes .../ords-based/images/makesecrets_1_1.png | Bin 117953 -> 0 bytes .../multinamespace/cdb_create.yaml | 48 - .../multinamespace/pdb_clone.yaml | 54 -- .../multinamespace/pdb_close.yaml | 48 - .../multinamespace/pdb_create.yaml | 50 - .../multinamespace/pdb_delete.yaml | 39 - .../provisioning/multinamespace/pdb_open.yaml | 47 - .../ords-based/provisioning/ords_image.md | 81 -- .../provisioning/quickOKEcreation.md | 136 --- .../singlenamespace/cdb_create.yaml | 49 - .../singlenamespace/cdb_secret.yaml | 17 - .../singlenamespace/pdb_close.yaml | 48 - .../singlenamespace/pdb_create.yaml | 51 - .../singlenamespace/pdb_delete.yaml | 39 - .../singlenamespace/pdb_open.yaml | 47 - .../singlenamespace/pdb_plug.yaml | 55 -- .../singlenamespace/pdb_unplug.yaml | 49 - docs/multitenant/ords-based/usecase/README.md | 112 --- .../usecase/cdbnamespace_binding.yaml | 13 - .../usecase/clone_pdb1_resource.yaml | 50 - .../usecase/clone_pdb2_resource.yaml | 50 - .../usecase/close_pdb1_resource.yaml | 47 - .../usecase/close_pdb2_resource.yaml | 47 - .../usecase/close_pdb3_resource.yaml | 47 - .../ords-based/usecase/create_ords_pod.yaml | 48 - .../usecase/create_pdb1_resource.yaml | 51 - .../usecase/create_pdb2_resource.yaml | 51 - .../usecase/delete_pdb1_resource.yaml | 45 - .../usecase/delete_pdb2_resource.yaml | 45 - docs/multitenant/ords-based/usecase/makefile | 915 ------------------ .../ords-based/usecase/map_pdb1_resource.yaml | 49 - .../ords-based/usecase/map_pdb2_resource.yaml | 49 - .../ords-based/usecase/map_pdb3_resource.yaml | 49 - .../usecase/open_pdb1_resource.yaml | 47 - .../usecase/open_pdb2_resource.yaml | 47 - .../usecase/open_pdb3_resource.yaml | 47 - .../ords-based/usecase/parameters.txt | 61 -- .../usecase/pdbnamespace_binding.yaml | 13 - .../usecase/plug_pdb1_resource.yaml | 53 - .../usecase/unplug_pdb1_resource.yaml | 46 - docs/multitenant/ords-based/usecase01/ca.crt | 25 - docs/multitenant/ords-based/usecase01/ca.key | 27 - docs/multitenant/ords-based/usecase01/ca.srl | 1 - .../ords-based/usecase01/cdb_create.yaml | 44 - .../ords-based/usecase01/cdb_secret.yaml | 17 - .../usecase01/clone_pdb1_resource.yaml | 50 - .../usecase01/clone_pdb2_resource.yaml | 50 - .../usecase01/close_pdb1_resource.yaml | 47 - .../usecase01/close_pdb2_resource.yaml | 47 - .../usecase01/close_pdb3_resource.yaml | 47 - .../ords-based/usecase01/create_ords_pod.yaml | 48 - .../usecase01/create_pdb1_resource.yaml | 51 - .../usecase01/create_pdb2_resource.yaml | 51 - .../usecase01/delete_pdb1_resource.yaml | 45 - .../usecase01/delete_pdb2_resource.yaml | 45 - .../ords-based/usecase01/extfile.txt | 1 - .../usecase01/logfiles/BuildImage.log | 896 ----------------- .../usecase01/logfiles/cdb_creation.log | 357 ------- .../usecase01/logfiles/openssl_execution.log | 19 - .../usecase01/logfiles/ordsconfig.log | 39 - .../usecase01/logfiles/tagandpush.log | 14 - .../multitenant/ords-based/usecase01/makefile | 906 ----------------- .../usecase01/map_pdb1_resource.yaml | 49 - .../usecase01/map_pdb2_resource.yaml | 49 - .../usecase01/map_pdb3_resource.yaml | 49 - .../usecase01/open_pdb1_resource.yaml | 47 - .../usecase01/open_pdb2_resource.yaml | 47 - .../usecase01/open_pdb3_resource.yaml | 47 - ...acle-database-operator-system_binding.yaml | 13 - .../ords-based/usecase01/parameters.txt | 61 -- .../ords-based/usecase01/pdb_close.yaml | 44 - .../ords-based/usecase01/pdb_create.yaml | 47 - .../ords-based/usecase01/pdb_delete.yaml | 34 - .../ords-based/usecase01/pdb_map.yaml | 45 - .../ords-based/usecase01/pdb_open.yaml | 43 - .../ords-based/usecase01/pdb_secret.yaml | 16 - .../usecase01/plug_pdb1_resource.yaml | 53 - .../ords-based/usecase01/server.csr | 18 - docs/multitenant/ords-based/usecase01/tls.crt | 24 - docs/multitenant/ords-based/usecase01/tls.key | 28 - .../usecase01/unplug_pdb1_resource.yaml | 46 - .../ords-based/usecase02/pdb_clone.yaml | 50 - .../ords-based/usecase02/pdb_plug.yaml | 53 - .../ords-based/usecase02/pdb_unplug.yaml | 46 - docs/multitenant/usecase03/Dockerfile | 80 -- .../usecase03/NamespaceSegregation.png | Bin 270813 -> 0 bytes docs/multitenant/usecase03/README.md | 268 ----- docs/multitenant/usecase03/cdb_create.yaml | 44 - .../usecase03/cdb_creation_log.txt | 336 ------- docs/multitenant/usecase03/cdb_secret.yaml | 17 - docs/multitenant/usecase03/gentlscert.sh | 23 - docs/multitenant/usecase03/makefile | 285 ------ .../usecase03/ns_namespace_cdb.yaml | 7 - .../usecase03/ns_namespace_pdb.yaml | 7 - .../usecase03/operator_creation_log.txt | 27 - docs/multitenant/usecase03/pdb_create.yaml | 46 - .../usecase03/pdb_creation_log.txt | 6 - docs/multitenant/usecase03/pdb_secret.yaml | 16 - docs/multitenant/usecase03/runOrdsSSL.sh | 190 ---- .../sharding_provisioning_with_db_events.yaml | 2 +- docs/sharding/provisioning/oraclesi.yaml | 16 +- .../provisioning/oraclesi_pvc_commented.yaml | 12 +- ...y_cloning_db_from_gold_image_across_ads.md | 58 -- ...ing_by_cloning_db_gold_image_in_same_ad.md | 54 -- ...ding_provisioning_with_chunks_specified.md | 44 - ...rding_scale_in_delete_an_existing_shard.md | 51 - .../snr_ssharding_scale_out_add_shards.md | 38 - .../snr_ssharding_shard_prov_chunks.yaml | 61 -- .../snr_ssharding_shard_prov_clone.yaml | 83 -- ...ssharding_shard_prov_clone_across_ads.yaml | 91 -- .../snr_ssharding_shard_prov_delshard.yaml | 69 -- .../snr_ssharding_shard_prov_extshard.yaml | 68 -- .../snr_ssharding_shard_prov_memory_cpu.yaml | 2 +- ...sharding_shard_prov_send_notification.yaml | 2 +- .../ssharding_shard_prov_clone.yaml | 2 +- ...ssharding_shard_prov_clone_across_ads.yaml | 2 +- .../ssharding_shard_prov_delshard.yaml | 2 +- .../ssharding_shard_prov_extshard.yaml | 2 +- .../ssharding_shard_prov_memory_cpu.yaml | 2 +- ...sharding_shard_prov_send_notification.yaml | 2 +- .../udsharding_shard_prov.yaml | 2 +- .../udsharding_shard_prov_clone.yaml | 2 +- ...dsharding_shard_prov_clone_across_ads.yaml | 2 +- .../udsharding_shard_prov_delshard.yaml | 2 +- .../udsharding_shard_prov_extshard.yaml | 2 +- .../udsharding_shard_prov_memory_cpu.yaml | 2 +- ...sharding_shard_prov_send_notification.yaml | 2 +- 195 files changed, 46 insertions(+), 15062 deletions(-) delete mode 100644 config/database.oracle.com_DbcsSystem.yaml delete mode 100644 config/database.oracle.com_autonomouscontainerdatabases.yaml delete mode 100644 config/database.oracle.com_autonomousdatabasebackups.yaml delete mode 100644 config/database.oracle.com_autonomousdatabaserestores.yaml delete mode 100644 config/database.oracle.com_autonomousdatabases.yaml delete mode 100644 config/database.oracle.com_cdbs.yaml delete mode 100644 config/database.oracle.com_dataguardbrokers.yaml delete mode 100644 config/database.oracle.com_oraclerestdataservices.yaml delete mode 100644 config/database.oracle.com_pdbs.yaml delete mode 100644 config/database.oracle.com_shardingdatabases.yaml delete mode 100644 config/database.oracle.com_singleinstancedatabases.yaml delete mode 100644 config/samples/adb/autonomousdatabase_wallet.yaml delete mode 100644 config/samples/observability/databaseobserver_custom_config.yaml delete mode 100644 config/samples/observability/databaseobserver_minimal.yaml delete mode 100644 config/samples/observability/databaseobserver_vault.yaml delete mode 100644 config/samples/observability/v1alpha1/databaseobserver_vault.yaml delete mode 100644 config/samples/observability/v4/databaseobserver_vault.yaml delete mode 100644 docs/multitenant/NamespaceSeg.md rename docs/multitenant/{lrest-based => }/images/Generalschema2.jpg (100%) rename docs/multitenant/{ords-based => }/images/KubectlGetSchema.jpg (100%) rename docs/multitenant/{ords-based => }/images/KubectlGetSchema2.jpg (100%) rename docs/multitenant/{ords-based => }/images/old_testcase_wf.jpg (100%) rename docs/multitenant/{ords-based => }/images/plsqlmap.jpg (100%) rename docs/multitenant/{ords-based => }/images/usecaseschema.jpg (100%) delete mode 100644 docs/multitenant/lrest-based/README.md delete mode 100644 docs/multitenant/lrest-based/images/UsecaseSchema.jpg delete mode 100644 docs/multitenant/lrest-based/usecase/README.md delete mode 100644 docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/config-map-pdb.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/config_map_pdb.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/makefile delete mode 100644 docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/parameters.txt delete mode 100644 docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml delete mode 100644 docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/NamespaceSeg.md delete mode 100644 docs/multitenant/ords-based/README.md delete mode 100644 docs/multitenant/ords-based/images/Generalschema2.jpg delete mode 100644 docs/multitenant/ords-based/images/K8S_NAMESPACE_SEG.png delete mode 100644 docs/multitenant/ords-based/images/makerunall.png delete mode 100644 docs/multitenant/ords-based/images/makesecrets_1_1.png delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/ords_image.md delete mode 100644 docs/multitenant/ords-based/provisioning/quickOKEcreation.md delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml delete mode 100644 docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml delete mode 100644 docs/multitenant/ords-based/usecase/README.md delete mode 100644 docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml delete mode 100644 docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/create_ords_pod.yaml delete mode 100644 docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/makefile delete mode 100644 docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/parameters.txt delete mode 100644 docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml delete mode 100644 docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/ca.crt delete mode 100644 docs/multitenant/ords-based/usecase01/ca.key delete mode 100644 docs/multitenant/ords-based/usecase01/ca.srl delete mode 100644 docs/multitenant/ords-based/usecase01/cdb_create.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/cdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/create_ords_pod.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/extfile.txt delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log delete mode 100644 docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log delete mode 100644 docs/multitenant/ords-based/usecase01/makefile delete mode 100644 docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/parameters.txt delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_close.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_create.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_delete.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_map.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_open.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/pdb_secret.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase01/server.csr delete mode 100644 docs/multitenant/ords-based/usecase01/tls.crt delete mode 100644 docs/multitenant/ords-based/usecase01/tls.key delete mode 100644 docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml delete mode 100644 docs/multitenant/ords-based/usecase02/pdb_clone.yaml delete mode 100644 docs/multitenant/ords-based/usecase02/pdb_plug.yaml delete mode 100644 docs/multitenant/ords-based/usecase02/pdb_unplug.yaml delete mode 100644 docs/multitenant/usecase03/Dockerfile delete mode 100644 docs/multitenant/usecase03/NamespaceSegregation.png delete mode 100644 docs/multitenant/usecase03/README.md delete mode 100644 docs/multitenant/usecase03/cdb_create.yaml delete mode 100644 docs/multitenant/usecase03/cdb_creation_log.txt delete mode 100644 docs/multitenant/usecase03/cdb_secret.yaml delete mode 100644 docs/multitenant/usecase03/gentlscert.sh delete mode 100644 docs/multitenant/usecase03/makefile delete mode 100644 docs/multitenant/usecase03/ns_namespace_cdb.yaml delete mode 100644 docs/multitenant/usecase03/ns_namespace_pdb.yaml delete mode 100644 docs/multitenant/usecase03/operator_creation_log.txt delete mode 100644 docs/multitenant/usecase03/pdb_create.yaml delete mode 100644 docs/multitenant/usecase03/pdb_creation_log.txt delete mode 100644 docs/multitenant/usecase03/pdb_secret.yaml delete mode 100644 docs/multitenant/usecase03/runOrdsSSL.sh delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml delete mode 100644 docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml diff --git a/config/database.oracle.com_DbcsSystem.yaml b/config/database.oracle.com_DbcsSystem.yaml deleted file mode 100644 index c342c363..00000000 --- a/config/database.oracle.com_DbcsSystem.yaml +++ /dev/null @@ -1,433 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - creationTimestamp: null - name: DbcsSystem.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DbcsSystem - listKind: DbcsSystemList - plural: DbcsSystem - singular: dbcssystem - scope: Namespaced - versions: - - name: v4 - schema: - openAPIV3Schema: - description: DbcsSystem is the Schema for the dbcssystems API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: DbcsSystemSpec defines the desired state of DbcsSystem - properties: - databaseId: - type: string - dbBackupId: - type: string - dbClone: - description: DbCloneConfig defines the configuration for the database - clone - properties: - dbAdminPaswordSecret: - type: string - dbDbUniqueName: - type: string - dbName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsKeyId: - type: string - kmsKeyVersionId: - type: string - licenseModel: - type: string - privateIp: - type: string - sidPrefix: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - tdeWalletPasswordSecret: - type: string - required: - - dbDbUniqueName - - dbName - - displayName - - hostName - - subnetId - type: object - dbSystem: - properties: - availabilityDomain: - type: string - backupSubnetId: - type: string - clusterName: - type: string - compartmentId: - type: string - cpuCoreCount: - type: integer - dbAdminPaswordSecret: - type: string - dbBackupConfig: - description: DB Backup Config Network Struct - properties: - autoBackupEnabled: - type: boolean - autoBackupWindow: - type: string - backupDestinationDetails: - type: string - recoveryWindowsInDays: - type: integer - type: object - dbDomain: - type: string - dbEdition: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbVersion: - type: string - dbWorkload: - type: string - diskRedundancy: - type: string - displayName: - type: string - domain: - type: string - faultDomains: - items: - type: string - type: array - hostName: - type: string - initialDataStorageSizeInGB: - type: integer - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - nodeCount: - type: integer - pdbName: - type: string - privateIp: - type: string - shape: - type: string - sshPublicKeys: - items: - type: string - type: array - storageManagement: - type: string - subnetId: - type: string - tags: - additionalProperties: - type: string - type: object - tdeWalletPasswordSecret: - type: string - timeZone: - type: string - required: - - availabilityDomain - - compartmentId - - dbAdminPaswordSecret - - hostName - - shape - - subnetId - type: object - hardLink: - type: boolean - id: - type: string - kmsConfig: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyName: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - ociConfigMap: - type: string - ociSecret: - type: string - pdbConfigs: - items: - description: PDBConfig defines details of PDB struct for DBCS systems - properties: - freeformTags: - additionalProperties: - type: string - description: '// Free-form tags for this resource. Each tag - is a simple key-value pair with no predefined name, type, - or namespace. // For more information, see Resource Tags (https://docs.cloud.oracle.com/Content/General/Concepts/resourcetags.htm). - // Example: `{"Department": "Finance"}`' - type: object - isDelete: - description: To specify whether to delete the PDB - type: boolean - pdbAdminPassword: - description: // A strong password for PDB Admin. The password - must be at least nine characters and contain at least two - uppercase, two lowercase, two numbers, and two special characters. - The special characters must be _, \#, or -. - type: string - pdbName: - description: The name for the pluggable database (PDB). The - name is unique in the context of a Database. The name must - begin with an alphabetic character and can contain a maximum - of thirty alphanumeric characters. Special characters are - not permitted. The pluggable database name should not be same - as the container database name. - type: string - pluggableDatabaseId: - description: The OCID of the PDB for deletion purposes. - type: string - shouldPdbAdminAccountBeLocked: - description: // The locked mode of the pluggable database admin - account. If false, the user needs to provide the PDB Admin - Password to connect to it. // If true, the pluggable database - will be locked and user cannot login to it. - type: boolean - tdeWalletPassword: - description: // The existing TDE wallet password of the CDB. - type: string - required: - - freeformTags - - pdbAdminPassword - - pdbName - - shouldPdbAdminAccountBeLocked - - tdeWalletPassword - type: object - type: array - setupDBCloning: - type: boolean - required: - - ociConfigMap - type: object - status: - description: DbcsSystemStatus defines the observed state of DbcsSystem - properties: - availabilityDomain: - type: string - cpuCoreCount: - type: integer - dataStoragePercentage: - type: integer - dataStorageSizeInGBs: - type: integer - dbCloneStatus: - description: DbCloneStatus defines the observed state of DbClone - properties: - dbAdminPaswordSecret: - type: string - dbDbUniqueName: - type: string - dbName: - type: string - displayName: - type: string - domain: - type: string - hostName: - type: string - id: - type: string - licenseModel: - type: string - sshPublicKeys: - items: - type: string - type: array - subnetId: - type: string - required: - - dbDbUniqueName - - hostName - type: object - dbEdition: - type: string - dbInfo: - items: - description: DbcsSystemStatus defines the observed state of DbcsSystem - properties: - dbHomeId: - type: string - dbName: - type: string - dbUniqueName: - type: string - dbWorkload: - type: string - id: - type: string - type: object - type: array - displayName: - type: string - id: - type: string - kmsDetailsStatus: - properties: - compartmentId: - type: string - encryptionAlgo: - type: string - keyId: - type: string - keyName: - type: string - managementEndpoint: - type: string - vaultId: - type: string - vaultName: - type: string - vaultType: - type: string - type: object - licenseModel: - type: string - network: - properties: - clientSubnet: - type: string - domainName: - type: string - hostName: - type: string - listenerPort: - type: integer - networkSG: - type: string - scanDnsName: - type: string - vcnName: - type: string - type: object - nodeCount: - type: integer - pdbDetailsStatus: - items: - properties: - pdbConfigStatus: - items: - properties: - freeformTags: - additionalProperties: - type: string - type: object - pdbName: - type: string - pdbState: - type: string - pluggableDatabaseId: - type: string - shouldPdbAdminAccountBeLocked: - type: boolean - required: - - freeformTags - - pdbName - - shouldPdbAdminAccountBeLocked - type: object - type: array - type: object - type: array - recoStorageSizeInGB: - type: integer - shape: - type: string - state: - type: string - storageManagement: - type: string - subnetId: - type: string - timeZone: - type: string - workRequests: - items: - properties: - operationId: - type: string - operationType: - type: string - percentComplete: - type: string - timeAccepted: - type: string - timeFinished: - type: string - timeStarted: - type: string - required: - - operationId - - operationType - type: object - type: array - required: - - state - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_autonomouscontainerdatabases.yaml b/config/database.oracle.com_autonomouscontainerdatabases.yaml deleted file mode 100644 index bac3a28c..00000000 --- a/config/database.oracle.com_autonomouscontainerdatabases.yaml +++ /dev/null @@ -1,117 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomouscontainerdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousContainerDatabase - listKind: AutonomousContainerDatabaseList - plural: autonomouscontainerdatabases - shortNames: - - acd - - acds - singular: autonomouscontainerdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.displayName - name: DisplayName - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousContainerDatabase is the Schema for the autonomouscontainerdatabases - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AutonomousContainerDatabaseSpec defines the desired state - of AutonomousContainerDatabase - properties: - action: - enum: - - SYNC - - RESTART - - TERMINATE - type: string - autonomousContainerDatabaseOCID: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' - type: string - autonomousExadataVMClusterOCID: - type: string - compartmentOCID: - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - hardLink: - default: false - type: boolean - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - patchModel: - description: 'AutonomousContainerDatabasePatchModelEnum Enum with - underlying type: string' - enum: - - RELEASE_UPDATES - - RELEASE_UPDATE_REVISIONS - type: string - type: object - status: - description: AutonomousContainerDatabaseStatus defines the observed state - of AutonomousContainerDatabase - properties: - lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' - type: string - timeCreated: - type: string - required: - - lifecycleState - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_autonomousdatabasebackups.yaml b/config/database.oracle.com_autonomousdatabasebackups.yaml deleted file mode 100644 index a5c37507..00000000 --- a/config/database.oracle.com_autonomousdatabasebackups.yaml +++ /dev/null @@ -1,138 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabasebackups.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseBackup - listKind: AutonomousDatabaseBackupList - plural: autonomousdatabasebackups - shortNames: - - adbbu - - adbbus - singular: autonomousdatabasebackup - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .status.dbDisplayName - name: DB DisplayName - type: string - - jsonPath: .status.type - name: Type - type: string - - jsonPath: .status.timeStarted - name: Started - type: string - - jsonPath: .status.timeEnded - name: Ended - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabaseBackup is the Schema for the autonomousdatabasebackups - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AutonomousDatabaseBackupSpec defines the desired state of - AutonomousDatabaseBackup - properties: - autonomousDatabaseBackupOCID: - type: string - displayName: - type: string - isLongTermBackup: - type: boolean - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - retentionPeriodInDays: - type: integer - target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' - properties: - k8sADB: - description: "*********************** *\tADB spec ***********************" - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - type: object - status: - description: AutonomousDatabaseBackupStatus defines the observed state - of AutonomousDatabaseBackup - properties: - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - dbDisplayName: - type: string - dbName: - type: string - isAutomatic: - type: boolean - lifecycleState: - description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with - underlying type: string' - type: string - timeEnded: - type: string - timeStarted: - type: string - type: - description: 'AutonomousDatabaseBackupTypeEnum Enum with underlying - type: string' - type: string - required: - - autonomousDatabaseOCID - - compartmentOCID - - dbDisplayName - - dbName - - isAutomatic - - lifecycleState - - type - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_autonomousdatabaserestores.yaml b/config/database.oracle.com_autonomousdatabaserestores.yaml deleted file mode 100644 index 5e9f2c73..00000000 --- a/config/database.oracle.com_autonomousdatabaserestores.yaml +++ /dev/null @@ -1,138 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabaserestores.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabaseRestore - listKind: AutonomousDatabaseRestoreList - plural: autonomousdatabaserestores - shortNames: - - adbr - - adbrs - singular: autonomousdatabaserestore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.displayName - name: DbDisplayName - type: string - - jsonPath: .status.dbName - name: DbName - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabaseRestore is the Schema for the autonomousdatabaserestores - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AutonomousDatabaseRestoreSpec defines the desired state of - AutonomousDatabaseRestore - properties: - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - source: - properties: - k8sADBBackup: - description: 'EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO - OWN! NOTE: json tags are required. Any new fields you add must - have json tags for the fields to be serialized.' - properties: - name: - type: string - type: object - pointInTime: - properties: - timestamp: - description: 'The timestamp must follow this format: YYYY-MM-DD - HH:MM:SS GMT' - type: string - type: object - type: object - target: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' - properties: - k8sADB: - description: "*********************** *\tADB spec ***********************" - properties: - name: - type: string - type: object - ociADB: - properties: - ocid: - type: string - type: object - type: object - required: - - source - - target - type: object - status: - description: AutonomousDatabaseRestoreStatus defines the observed state - of AutonomousDatabaseRestore - properties: - dbName: - type: string - displayName: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' - type: string - status: - description: 'WorkRequestStatusEnum Enum with underlying type: string' - type: string - timeAccepted: - type: string - timeEnded: - type: string - timeStarted: - type: string - workRequestOCID: - type: string - required: - - dbName - - displayName - - status - - workRequestOCID - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_autonomousdatabases.yaml b/config/database.oracle.com_autonomousdatabases.yaml deleted file mode 100644 index f77407f3..00000000 --- a/config/database.oracle.com_autonomousdatabases.yaml +++ /dev/null @@ -1,324 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: autonomousdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases - shortNames: - - adb - - adbs - singular: autonomousdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.details.displayName - name: Display Name - type: string - - jsonPath: .spec.details.dbName - name: Db Name - type: string - - jsonPath: .status.lifecycleState - name: State - type: string - - jsonPath: .spec.details.isDedicated - name: Dedicated - type: string - - jsonPath: .spec.details.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .spec.details.dataStorageSizeInTBs - name: Storage (TB) - type: integer - - jsonPath: .spec.details.dbWorkload - name: Workload Type - type: string - - jsonPath: .status.timeCreated - name: Created - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: AutonomousDatabase is the Schema for the autonomousdatabases - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase - Important: Run "make" to regenerate code after modifying this file' - properties: - details: - description: AutonomousDatabaseDetails defines the detail information - of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase - properties: - adminPassword: - properties: - k8sSecret: - description: "*********************** *\tSecret specs ***********************" - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - autonomousContainerDatabase: - description: ACDSpec defines the spec of the target for backup/restore - runs. The name could be the name of an AutonomousDatabase or - an AutonomousDatabaseBackup - properties: - k8sACD: - description: "*********************** *\tACD specs ***********************" - properties: - name: - type: string - type: object - ociACD: - properties: - ocid: - type: string - type: object - type: object - autonomousDatabaseOCID: - type: string - compartmentOCID: - type: string - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbName: - type: string - dbVersion: - type: string - dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying - type: string' - enum: - - OLTP - - DW - - AJD - - APEX - type: string - displayName: - type: string - freeformTags: - additionalProperties: - type: string - type: object - isAutoScalingEnabled: - type: boolean - isDedicated: - type: boolean - licenseModel: - description: 'AutonomousDatabaseLicenseModelEnum Enum with underlying - type: string' - enum: - - LICENSE_INCLUDED - - BRING_YOUR_OWN_LICENSE - type: string - lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying - type: string' - type: string - networkAccess: - properties: - accessControlList: - items: - type: string - type: array - accessType: - enum: - - "" - - PUBLIC - - RESTRICTED - - PRIVATE - type: string - isAccessControlEnabled: - type: boolean - isMTLSConnectionRequired: - type: boolean - privateEndpoint: - properties: - hostnamePrefix: - type: string - nsgOCIDs: - items: - type: string - type: array - subnetOCID: - type: string - type: object - type: object - wallet: - properties: - name: - type: string - password: - properties: - k8sSecret: - description: "*********************** *\tSecret specs - ***********************" - properties: - name: - type: string - type: object - ociSecret: - properties: - ocid: - type: string - type: object - type: object - type: object - type: object - hardLink: - default: false - type: boolean - ociConfig: - description: "*********************** *\tOCI config ***********************" - properties: - configMapName: - type: string - secretName: - type: string - type: object - required: - - details - type: object - status: - description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase - properties: - allConnectionStrings: - items: - properties: - connectionStrings: - items: - properties: - connectionString: - type: string - tnsName: - type: string - type: object - type: array - tlsAuthentication: - type: string - required: - - connectionStrings - type: object - type: array - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - lifecycleState: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' - type: string - timeCreated: - type: string - walletExpiringDate: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_cdbs.yaml b/config/database.oracle.com_cdbs.yaml deleted file mode 100644 index 6b1c350c..00000000 --- a/config/database.oracle.com_cdbs.yaml +++ /dev/null @@ -1,270 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: cdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: CDB - listKind: CDBList - plural: cdbs - singular: cdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: ' Name of the DB Server' - jsonPath: .spec.dbServer - name: DB Server - type: string - - description: DB server port - jsonPath: .spec.dbPort - name: DB Port - type: integer - - description: ' string of the tnsalias' - jsonPath: .spec.dbTnsurl - name: TNS STRING - type: string - - description: Replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: Status of the CDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: CDB is the Schema for the cdbs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: CDBSpec defines the desired state of CDB - properties: - cdbAdminPwd: - description: Password for the CDB Administrator to manage PDB lifecycle - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbAdminUser: - description: User in the root container with sysdba priviledges to - manage PDB lifecycle - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbName: - description: Name of the CDB - type: string - cdbTlsCrt: - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - cdbTlsKey: - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - dbPort: - description: DB server port - type: integer - dbServer: - description: Name of the DB server - type: string - dbTnsurl: - type: string - nodeSelector: - additionalProperties: - type: string - description: Node Selector for running the Pod - type: object - ordsImage: - description: ORDS Image Name - type: string - ordsImagePullPolicy: - description: ORDS Image Pull Policy - enum: - - Always - - Never - type: string - ordsImagePullSecret: - description: The name of the image pull secret in case of a private - docker repository. - type: string - ordsPort: - description: ORDS server port. For now, keep it as 8888. TO BE USED - IN FUTURE RELEASE. - type: integer - ordsPwd: - description: Password for user ORDS_PUBLIC_USER - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - replicas: - description: Number of ORDS Containers to create - type: integer - serviceName: - description: Name of the CDB Service - type: string - sysAdminPwd: - description: Password for the CDB System Administrator - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerPwd: - description: Password for the Web Server User - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints - properties: - secret: - description: CDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - type: object - status: - description: CDBStatus defines the observed state of CDB - properties: - msg: - description: Message - type: string - phase: - description: Phase of the CDB Resource - type: string - status: - description: CDB Resource Status - type: boolean - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_dataguardbrokers.yaml b/config/database.oracle.com_dataguardbrokers.yaml deleted file mode 100644 index f19a3e22..00000000 --- a/config/database.oracle.com_dataguardbrokers.yaml +++ /dev/null @@ -1,134 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: dataguardbrokers.database.oracle.com -spec: - group: database.oracle.com - names: - kind: DataguardBroker - listKind: DataguardBrokerList - plural: dataguardbrokers - singular: dataguardbroker - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.primaryDatabase - name: Primary - type: string - - jsonPath: .status.standbyDatabases - name: Standbys - type: string - - jsonPath: .spec.protectionMode - name: Protection Mode - type: string - - jsonPath: .status.clusterConnectString - name: Cluster Connect Str - priority: 1 - type: string - - jsonPath: .status.externalConnectString - name: Connect Str - type: string - - jsonPath: .spec.primaryDatabaseRef - name: Primary Database - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: DataguardBroker is the Schema for the dataguardbrokers API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: DataguardBrokerSpec defines the desired state of DataguardBroker - properties: - fastStartFailOver: - properties: - enable: - type: boolean - strategy: - items: - description: FSFO strategy - properties: - sourceDatabaseRef: - type: string - targetDatabaseRefs: - type: string - type: object - type: array - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - primaryDatabaseRef: - type: string - protectionMode: - enum: - - MaxPerformance - - MaxAvailability - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - setAsPrimaryDatabase: - type: string - standbyDatabaseRefs: - items: - type: string - type: array - required: - - primaryDatabaseRef - - protectionMode - - standbyDatabaseRefs - type: object - status: - description: DataguardBrokerStatus defines the observed state of DataguardBroker - properties: - clusterConnectString: - type: string - externalConnectString: - type: string - primaryDatabase: - type: string - primaryDatabaseRef: - type: string - protectionMode: - type: string - standbyDatabases: - type: string - status: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_oraclerestdataservices.yaml b/config/database.oracle.com_oraclerestdataservices.yaml deleted file mode 100644 index 121383fd..00000000 --- a/config/database.oracle.com_oraclerestdataservices.yaml +++ /dev/null @@ -1,224 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: oraclerestdataservices.database.oracle.com -spec: - group: database.oracle.com - names: - kind: OracleRestDataService - listKind: OracleRestDataServiceList - plural: oraclerestdataservices - singular: oraclerestdataservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .spec.databaseRef - name: Database - type: string - - jsonPath: .status.databaseApiUrl - name: Database API URL - type: string - - jsonPath: .status.databaseActionsUrl - name: Database Actions URL - type: string - - jsonPath: .status.apexUrl - name: Apex URL - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: OracleRestDataService is the Schema for the oraclerestdataservices - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: OracleRestDataServiceSpec defines the desired state of OracleRestDataService - properties: - adminPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - apexPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - databaseRef: - type: string - image: - description: OracleRestDataServiceImage defines the Image source and - pullSecrets for POD - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - oracleService: - type: string - ordsPassword: - description: OracleRestDataServicePassword defines the secret containing - Password mapped to secretKey - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - ordsUser: - type: string - persistence: - description: OracleRestDataServicePersistence defines the storage - releated params - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - size: - type: string - storageClass: - type: string - volumeName: - type: string - type: object - replicas: - minimum: 1 - type: integer - restEnableSchemas: - items: - description: OracleRestDataServicePDBSchemas defines the PDB Schemas - to be ORDS Enabled - properties: - enable: - type: boolean - pdbName: - type: string - schemaName: - type: string - urlMapping: - type: string - required: - - enable - - schemaName - type: object - type: array - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - required: - - adminPassword - - databaseRef - - ordsPassword - type: object - status: - description: OracleRestDataServiceStatus defines the observed state of - OracleRestDataService - properties: - apexConfigured: - type: boolean - apexUrl: - type: string - commonUsersCreated: - type: boolean - databaseActionsUrl: - type: string - databaseApiUrl: - type: string - databaseRef: - type: string - image: - description: OracleRestDataServiceImage defines the Image source and - pullSecrets for POD - properties: - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - loadBalancer: - type: string - ordsInstalled: - type: boolean - replicas: - type: integer - serviceIP: - type: string - status: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_pdbs.yaml b/config/database.oracle.com_pdbs.yaml deleted file mode 100644 index 85af8c1b..00000000 --- a/config/database.oracle.com_pdbs.yaml +++ /dev/null @@ -1,383 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: pdbs.database.oracle.com -spec: - group: database.oracle.com - names: - kind: PDB - listKind: PDBList - plural: pdbs - singular: pdb - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The connect string to be used - jsonPath: .status.connString - name: Connect_String - type: string - - description: Name of the CDB - jsonPath: .spec.cdbName - name: CDB Name - type: string - - description: Name of the PDB - jsonPath: .spec.pdbName - name: PDB Name - type: string - - description: PDB Open Mode - jsonPath: .status.openMode - name: PDB State - type: string - - description: Total Size of the PDB - jsonPath: .status.totalSize - name: PDB Size - type: string - - description: Status of the PDB Resource - jsonPath: .status.phase - name: Status - type: string - - description: Error message, if any - jsonPath: .status.msg - name: Message - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: PDB is the Schema for the pdbs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: PDBSpec defines the desired state of PDB - properties: - action: - description: 'Action to be taken: Create/Clone/Plug/Unplug/Delete/Modify/Status/Map. - Map is used to map a Databse PDB to a Kubernetes PDB CR.' - enum: - - Create - - Clone - - Plug - - Unplug - - Delete - - Modify - - Status - - Map - type: string - adminName: - description: The administrator username for the new PDB. This property - is required when the Action property is Create. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - adminPwd: - description: The administrator password for the new PDB. This property - is required when the Action property is Create. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - asClone: - description: Indicate if 'AS CLONE' option should be used in the command - to plug in a PDB. This property is applicable when the Action property - is PLUG but not required. - type: boolean - assertivePdbDeletion: - description: turn on the assertive approach to delete pdb resource - kubectl delete pdb ..... automatically triggers the pluggable database - deletion - type: boolean - cdbName: - description: Name of the CDB - type: string - cdbNamespace: - description: CDB Namespace - type: string - cdbResName: - description: Name of the CDB Custom Resource that runs the ORDS container - type: string - copyAction: - description: To copy files or not while cloning a PDB - enum: - - COPY - - NOCOPY - - MOVE - type: string - dropAction: - description: Specify if datafiles should be removed or not. The value - can be INCLUDING or KEEP (default). - enum: - - INCLUDING - - KEEP - type: string - fileNameConversions: - description: Relevant for Create and Plug operations. As defined in - the Oracle Multitenant Database documentation. Values can be a - filename convert pattern or NONE. - type: string - getScript: - description: Whether you need the script only or execute the script - type: boolean - modifyOption: - description: Extra options for opening and closing a PDB - enum: - - IMMEDIATE - - NORMAL - - READ ONLY - - READ WRITE - - RESTRICTED - type: string - pdbName: - description: The name of the new PDB. Relevant for both Create and - Plug Actions. - type: string - pdbState: - description: The target state of the PDB - enum: - - OPEN - - CLOSE - type: string - pdbTlsCat: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsCrt: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbTlsKey: - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - reuseTempFile: - description: Whether to reuse temp file - type: boolean - sourceFileNameConversions: - description: This property is required when the Action property is - Plug. As defined in the Oracle Multitenant Database documentation. - Values can be a source filename convert pattern or NONE. - type: string - sparseClonePath: - description: A Path specified for sparse clone snapshot copy. (Optional) - type: string - srcPdbName: - description: Name of the Source PDB from which to clone - type: string - tdeExport: - description: TDE export for unplug operations - type: boolean - tdeImport: - description: TDE import for plug operations - type: boolean - tdeKeystorePath: - description: TDE keystore path is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. - type: string - tdePassword: - description: TDE password if the tdeImport or tdeExport flag is set - to true. Can be used in create, plug or unplug operations - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tdeSecret: - description: TDE secret is required if the tdeImport or tdeExport - flag is set to true. Can be used in plug or unplug operations. - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - tempSize: - description: Relevant for Create and Clone operations. Total size - for temporary tablespace as defined in the Oracle Multitenant Database - documentation. See size_clause description in Database SQL Language - Reference documentation. - type: string - totalSize: - description: Relevant for create and plug operations. Total size as - defined in the Oracle Multitenant Database documentation. See size_clause - description in Database SQL Language Reference documentation. - type: string - unlimitedStorage: - description: Relevant for Create and Plug operations. True for unlimited - storage. Even when set to true, totalSize and tempSize MUST be specified - in the request if Action is Create. - type: boolean - webServerPwd: - description: Password for the Web ServerPDB User - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - webServerUser: - description: Web Server User with SQL Administrator role to allow - us to authenticate to the PDB Lifecycle Management REST endpoints - properties: - secret: - description: PDBSecret defines the secretName - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - xmlFileName: - description: XML metadata filename to be used for Plug or Unplug operations - type: string - required: - - action - type: object - status: - description: PDBStatus defines the observed state of PDB - properties: - action: - description: Last Completed Action - type: string - connString: - description: PDB Connect String - type: string - modifyOption: - description: Modify Option of the PDB - type: string - msg: - description: Message - type: string - openMode: - description: Open mode of the PDB - type: string - phase: - description: Phase of the PDB Resource - type: string - status: - description: PDB Resource Status - type: boolean - totalSize: - description: Total size of the PDB - type: string - required: - - phase - - status - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_shardingdatabases.yaml b/config/database.oracle.com_shardingdatabases.yaml deleted file mode 100644 index bb9bbd38..00000000 --- a/config/database.oracle.com_shardingdatabases.yaml +++ /dev/null @@ -1,688 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - creationTimestamp: null - name: shardingdatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: ShardingDatabase - listKind: ShardingDatabaseList - plural: shardingdatabases - singular: shardingdatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.gsm.state - name: Gsm State - type: string - - jsonPath: .status.gsm.services - name: Services - type: string - - jsonPath: .status.gsm.shards - name: shards - priority: 1 - type: string - name: v4 - schema: - openAPIV3Schema: - description: ShardingDatabase is the Schema for the shardingdatabases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ShardingDatabaseSpec defines the desired state of ShardingDatabase - properties: - InvitedNodeSubnet: - type: string - catalog: - items: - description: CatalogSpec defines the desired state of CatalogSpec - properties: - envVars: - items: - description: EnvironmentVariable represents a named variable - accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string - isDelete: - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - description: ResourceRequirements describes the compute resource - requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - dbEdition: - type: string - dbImage: - type: string - dbImagePullSecret: - type: string - dbSecret: - description: Secret Details - properties: - encryptionType: - type: string - keyFileMountLocation: - type: string - keyFileName: - type: string - keySecretName: - type: string - name: - type: string - nsConfigMap: - type: string - nsSecret: - type: string - pwdFileMountLocation: - type: string - pwdFileName: - type: string - required: - - name - - pwdFileName - type: object - fssStorageClass: - type: string - gsm: - items: - description: GsmSpec defines the desired state of GsmSpec - properties: - directorName: - type: string - envVars: - description: Replicas int32 `json:"replicas,omitempty"` // - Gsm Replicas. If you set OraGsmPvcName then it is set default - to 1. - items: - description: EnvironmentVariable represents a named variable - accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string - isDelete: - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - region: - type: string - resources: - description: ResourceRequirements describes the compute resource - requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - gsmDevMode: - type: string - gsmImage: - type: string - gsmImagePullSecret: - type: string - gsmService: - items: - description: Service Definition - properties: - available: - type: string - clbGoal: - type: string - commitOutcome: - type: string - drainTimeout: - type: string - dtp: - type: string - edition: - type: string - failoverDelay: - type: string - failoverMethod: - type: string - failoverPrimary: - type: string - failoverRestore: - type: string - failoverRetry: - type: string - failoverType: - type: string - gdsPool: - type: string - lag: - type: integer - locality: - type: string - name: - type: string - notification: - type: string - pdbName: - type: string - policy: - type: string - preferred: - type: string - prferredAll: - type: string - regionFailover: - type: string - retention: - type: string - role: - type: string - sessionState: - type: string - sqlTransactionProfile: - type: string - stopOption: - type: string - tableFamily: - type: string - tfaPolicy: - type: string - required: - - name - type: object - type: array - gsmShardGroup: - items: - properties: - deployAs: - type: string - name: - type: string - region: - type: string - required: - - name - type: object - type: array - gsmShardSpace: - items: - description: ShardSpace Specs - properties: - chunks: - type: integer - name: - type: string - protectionMode: - type: string - shardGroup: - type: string - required: - - name - type: object - type: array - invitedNodeSubnetFlag: - type: string - isClone: - type: boolean - isDataGuard: - type: boolean - isDebug: - type: boolean - isDeleteOraPvc: - type: boolean - isDownloadScripts: - type: boolean - isExternalSvc: - type: boolean - isTdeWallet: - type: string - liveinessCheckPeriod: - type: integer - namespace: - type: string - portMappings: - items: - description: PortMapping is a specification of port mapping for - an application deployment. - properties: - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - format: int32 - type: integer - required: - - port - - protocol - - targetPort - type: object - type: array - readinessCheckPeriod: - type: integer - replicationType: - type: string - scriptsLocation: - type: string - shard: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' - items: - description: ShardSpec is a specification of Shards for an application - deployment. - properties: - deployAs: - type: string - envVars: - items: - description: EnvironmentVariable represents a named variable - accessible for containers. - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string - isDelete: - enum: - - enable - - disable - - failed - - force - type: string - label: - type: string - name: - type: string - nodeSelector: - additionalProperties: - type: string - type: object - pvAnnotations: - additionalProperties: - type: string - type: object - pvMatchLabels: - additionalProperties: - type: string - type: object - pvcName: - type: string - resources: - description: ResourceRequirements describes the compute resource - requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - shardGroup: - type: string - shardRegion: - type: string - shardSpace: - type: string - storageSizeInGb: - format: int32 - type: integer - required: - - name - type: object - type: array - shardBuddyRegion: - type: string - shardConfigName: - type: string - shardRegion: - items: - type: string - type: array - shardingType: - type: string - stagePvcName: - type: string - storageClass: - type: string - tdeWalletPvc: - type: string - tdeWalletPvcMountLocation: - type: string - required: - - catalog - - dbImage - - gsm - - gsmImage - - shard - type: object - status: - description: To understand Metav1.Condition, please refer the link https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1 - ShardingDatabaseStatus defines the observed state of ShardingDatabase - properties: - catalogs: - additionalProperties: - type: string - type: object - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gsm: - properties: - details: - additionalProperties: - type: string - type: object - externalConnectStr: - type: string - internalConnectStr: - type: string - services: - type: string - shards: - additionalProperties: - type: string - type: object - state: - type: string - type: object - shards: - additionalProperties: - type: string - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/database.oracle.com_singleinstancedatabases.yaml b/config/database.oracle.com_singleinstancedatabases.yaml deleted file mode 100644 index 1c011e17..00000000 --- a/config/database.oracle.com_singleinstancedatabases.yaml +++ /dev/null @@ -1,421 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: singleinstancedatabases.database.oracle.com -spec: - group: database.oracle.com - names: - kind: SingleInstanceDatabase - listKind: SingleInstanceDatabaseList - plural: singleinstancedatabases - singular: singleinstancedatabase - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.edition - name: Edition - type: string - - jsonPath: .status.sid - name: Sid - priority: 1 - type: string - - jsonPath: .status.status - name: Status - type: string - - jsonPath: .status.role - name: Role - type: string - - jsonPath: .status.releaseUpdate - name: Version - type: string - - jsonPath: .status.connectString - name: Connect Str - type: string - - jsonPath: .status.pdbConnectString - name: Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.tcpsConnectString - name: TCPS Connect Str - type: string - - jsonPath: .status.tcpsPdbConnectString - name: TCPS Pdb Connect Str - priority: 1 - type: string - - jsonPath: .status.oemExpressUrl - name: Oem Express Url - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: SingleInstanceDatabase is the Schema for the singleinstancedatabases - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: SingleInstanceDatabaseSpec defines the desired state of SingleInstanceDatabase - properties: - adminPassword: - description: SingleInsatnceAdminPassword defines the secret containing - Admin Password mapped to secretKey for Database - properties: - keepSecret: - type: boolean - secretKey: - default: oracle_pwd - type: string - secretName: - type: string - required: - - secretName - type: object - archiveLog: - type: boolean - charset: - type: string - createAs: - enum: - - primary - - standby - - clone - type: string - dgBrokerConfigured: - type: boolean - edition: - enum: - - standard - - enterprise - - express - - free - type: string - enableTCPS: - type: boolean - flashBack: - type: boolean - forceLog: - type: boolean - image: - description: SingleInstanceDatabaseImage defines the Image source - and pullSecrets for POD - properties: - prebuiltDB: - type: boolean - pullFrom: - type: string - pullSecrets: - type: string - version: - type: string - required: - - pullFrom - type: object - initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - listenerPort: - type: integer - loadBalancer: - type: boolean - nodeSelector: - additionalProperties: - type: string - type: object - pdbName: - type: string - persistence: - description: SingleInstanceDatabasePersistence defines the storage - size and class for PVC - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - primaryDatabaseRef: - type: string - readinessCheckPeriod: - type: integer - replicas: - type: integer - resources: - properties: - limits: - properties: - cpu: - type: string - memory: - type: string - type: object - requests: - properties: - cpu: - type: string - memory: - type: string - type: object - type: object - serviceAccountName: - type: string - serviceAnnotations: - additionalProperties: - type: string - type: object - sid: - description: SID must be alphanumeric (no special characters, only - a-z, A-Z, 0-9), and no longer than 12 characters. - maxLength: 12 - pattern: ^[a-zA-Z0-9]+$ - type: string - tcpsCertRenewInterval: - type: string - tcpsListenerPort: - type: integer - tcpsTlsSecret: - type: string - required: - - image - type: object - status: - description: SingleInstanceDatabaseStatus defines the observed state of - SingleInstanceDatabase - properties: - apexInstalled: - type: boolean - archiveLog: - type: string - certCreationTimestamp: - type: string - certRenewInterval: - type: string - charset: - type: string - clientWalletLoc: - type: string - clusterConnectString: - type: string - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - connectString: - type: string - createdAs: - type: string - datafilesCreated: - default: "false" - type: string - datafilesPatched: - default: "false" - type: string - dgBrokerConfigured: - type: boolean - edition: - type: string - flashBack: - type: string - forceLog: - type: string - initParams: - description: SingleInstanceDatabaseInitParams defines the Init Parameters - properties: - cpuCount: - type: integer - pgaAggregateTarget: - type: integer - processes: - type: integer - sgaTarget: - type: integer - type: object - initPgaSize: - type: integer - initSgaSize: - type: integer - isTcpsEnabled: - default: false - type: boolean - nodes: - items: - type: string - type: array - oemExpressUrl: - type: string - ordsReference: - type: string - pdbConnectString: - type: string - pdbName: - type: string - persistence: - description: SingleInstanceDatabasePersistence defines the storage - size and class for PVC - properties: - accessMode: - enum: - - ReadWriteOnce - - ReadWriteMany - type: string - datafilesVolumeName: - type: string - scriptsVolumeName: - type: string - setWritePermissions: - type: boolean - size: - type: string - storageClass: - type: string - volumeClaimAnnotation: - type: string - type: object - prebuiltDB: - type: boolean - primaryDatabase: - type: string - releaseUpdate: - type: string - replicas: - type: integer - role: - type: string - sid: - type: string - standbyDatabases: - additionalProperties: - type: string - type: object - status: - type: string - tcpsConnectString: - type: string - tcpsPdbConnectString: - type: string - tcpsTlsSecret: - default: "" - type: string - required: - - isTcpsEnabled - - persistence - - tcpsTlsSecret - type: object - type: object - served: true - storage: true - subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.replicas - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2689fe1e..6173a20a 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: phx.ocir.io/intsanjaysingh/db-repo/oracle/database - newTag: orestart-operator-sa \ No newline at end of file + newTag: orestart-operator-sa diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index fb8848b1..e6086c1d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -49,32 +49,6 @@ rules: - secrets/status verbs: - get -- apiGroups: - - "" - resources: - - configmaps/status - - daemonsets/status - - deployments/status - - services/status - - statefulsets/status - verbs: - - get - - patch - - update -- apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - secrets/status - verbs: - - get - apiGroups: - '''''' resources: diff --git a/config/samples/adb/autonomousdatabase_wallet.yaml b/config/samples/adb/autonomousdatabase_wallet.yaml deleted file mode 100644 index 84136647..00000000 --- a/config/samples/adb/autonomousdatabase_wallet.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v1alpha1 -kind: AutonomousDatabase -metadata: - name: autonomousdatabase-sample -spec: - action: Update - details: - id: ocid1.autonomousdatabase... - wallet: - # Insert a name of the secret where you want the wallet to be stored. The default name is -instance-wallet. - name: instance-wallet - password: - # Comment out k8sSecret and uncomment ociSecret if you pass the admin password using OCI Secret. - k8sSecret: - # The Name of the K8s secret where you want to hold the password of the ADMIN account. - name: instance-wallet-password - # ociSecret: - # # The OCID of the OCI Secret that holds the password of the ADMIN account. It should start with ocid1.vaultsecret... . - # id: ocid1.vaultsecret... - # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey diff --git a/config/samples/observability/databaseobserver_custom_config.yaml b/config/samples/observability/databaseobserver_custom_config.yaml deleted file mode 100644 index 1e9fff47..00000000 --- a/config/samples/observability/databaseobserver_custom_config.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# example -apiVersion: observability.oracle.com/v1alpha1 -kind: DatabaseObserver -metadata: - name: obs-sample - namespace: observer -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - key: "password" - secret: db-secret - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallet - - exporter: - configuration: - configmap: - key: "config.toml" - configmapName: "devcm-oradevdb-config" \ No newline at end of file diff --git a/config/samples/observability/databaseobserver_minimal.yaml b/config/samples/observability/databaseobserver_minimal.yaml deleted file mode 100644 index 2eeaf3ab..00000000 --- a/config/samples/observability/databaseobserver_minimal.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# example -apiVersion: observability.oracle.com/v1alpha1 -kind: DatabaseObserver -metadata: - name: obs-sample - namespace: observer -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - key: "password" - secret: db-secret - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallets \ No newline at end of file diff --git a/config/samples/observability/databaseobserver_vault.yaml b/config/samples/observability/databaseobserver_vault.yaml deleted file mode 100644 index fa2e09d4..00000000 --- a/config/samples/observability/databaseobserver_vault.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# example -apiVersion: observability.oracle.com/v1alpha1 -kind: DatabaseObserver -metadata: - name: obs-sample -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - vaultSecretName: sample_secret - vaultOCID: ocid1.vault.oc1.. - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallet - - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml b/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml index 2eaa9956..9c2044bf 100644 --- a/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml +++ b/config/samples/observability/v1alpha1/databaseobserver_minimal.yaml @@ -20,4 +20,4 @@ spec: serviceMonitor: labels: - release: prometheus + release: prometheus \ No newline at end of file diff --git a/config/samples/observability/v1alpha1/databaseobserver_vault.yaml b/config/samples/observability/v1alpha1/databaseobserver_vault.yaml deleted file mode 100644 index 2fc3c9f0..00000000 --- a/config/samples/observability/v1alpha1/databaseobserver_vault.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# example -apiVersion: observability.oracle.com/v1alpha1 -kind: DatabaseObserver -metadata: - name: obs-sample -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - vaultSecretName: sample_secret - vaultOCID: ocid1.vault.oc1.. - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallet - - prometheus: - serviceMonitor: - labels: - release: prometheus - - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/observability/v4/databaseobserver_custom_config.yaml b/config/samples/observability/v4/databaseobserver_custom_config.yaml index 2c915843..e82f7478 100644 --- a/config/samples/observability/v4/databaseobserver_custom_config.yaml +++ b/config/samples/observability/v4/databaseobserver_custom_config.yaml @@ -43,4 +43,4 @@ spec: serviceMonitor: labels: - release: prometheus + release: prometheus \ No newline at end of file diff --git a/config/samples/observability/v4/databaseobserver_vault.yaml b/config/samples/observability/v4/databaseobserver_vault.yaml deleted file mode 100644 index 4f5845f6..00000000 --- a/config/samples/observability/v4/databaseobserver_vault.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# example -apiVersion: observability.oracle.com/v4 -kind: DatabaseObserver -metadata: - name: obs-sample - labels: - app.kubernetes.io/name: observability-exporter - app.kubernetes.io/instance: obs-sample - app.kubernetes.io/version: latest -spec: - database: - dbUser: - key: "username" - secret: db-secret - - dbPassword: - vaultSecretName: sample_secret - vaultOCID: ocid1.vault.oc1.. - - dbConnectionString: - key: "connection" - secret: db-secret - - dbWallet: - secret: instance-wallet - - inherit_labels: - - app.kubernetes.io/name - - app.kubernetes.io/instance - - app.kubernetes.io/version - - prometheus: - serviceMonitor: - labels: - release: prometheus - - ociConfig: - configMapName: oci-cred - secretName: oci-privatekey \ No newline at end of file diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 32b3a397..43539626 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -513,7 +513,7 @@ webhooks: - apiGroups: - database.oracle.com apiVersions: - - v4 + - v1alpha1 operations: - CREATE - UPDATE diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml index 9a3a6861..05885fdb 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml @@ -18,4 +18,4 @@ spec: ociConfigMap: "oci-cred-mumbai" # The name of the Secret containing the OCI private key used for authentication - ociSecret: "oci-privatekey" + ociSecret: "oci-privatekey" \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log b/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log index 4d170908..43480edc 100644 --- a/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_backup_sample_output.log @@ -136,4 +136,4 @@ Status: Storage Management: ASM Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq Time Zone: UTC -Events: +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log b/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log index 181414ab..9889daa2 100644 --- a/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log +++ b/docs/dbcs/provisioning/clone_dbcs_system_from_database_sample_output.log @@ -100,4 +100,4 @@ Status: Storage Management: ASM Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq Time Zone: UTC -Events: +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log b/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log index 624a7fbc..b85588db 100644 --- a/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log +++ b/docs/dbcs/provisioning/clone_dbcs_system_sample_output.log @@ -120,4 +120,4 @@ Status: Storage Management: ASM Subnet Id: ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq Time Zone: UTC -Events: +Events: \ No newline at end of file diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml index 8500833f..8d78fc5d 100644 --- a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml @@ -3,18 +3,24 @@ kind: DbcsSystem metadata: name: dbcssystem-existing spec: - +<<<<<<< HEAD + id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyamvvn4n2yg6gv6s5c42qgfhtxrzcbdootuiki4wb3s5yq" +======= id: "ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa" +>>>>>>> origin/master ociConfigMap: "oci-cred" ociSecret: "oci-privatekey" dbSystem: availabilityDomain: "OLou:AP-MUMBAI-1-AD-1" compartmentId: "ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a" dbAdminPasswordSecret: "admin-password" +<<<<<<< HEAD + hostName: "host12345" +======= hostName: "host1234" +>>>>>>> origin/master shape: "VM.Standard2.2" domain: "sub0ea2e07ef.cluster1.oraclevcn.com" sshPublicKeys: - "oci-publickey" subnetId: "ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaaklvzufcgma65ibn4al7xukjmyfz7nnrpaewfzbffz5zrnz3htweq" - diff --git a/docs/multitenant/NamespaceSeg.md b/docs/multitenant/NamespaceSeg.md deleted file mode 100644 index 6738fe56..00000000 --- a/docs/multitenant/NamespaceSeg.md +++ /dev/null @@ -1,14 +0,0 @@ - - -# Namespace segregation - -With the namespace segregation pdb controller and cdb controller run in different namespaces. The new functionality introduces a new parameter (the cdb namespace) in pdb crd definition. In case you don't need the namespace segregation you have to sepcify the namespace name that you are using for yours crd and pods anyway. Refer to usercase01 and usecase02 to see single namespace configuration. Refer to usecase03 to see examples of namespace segregation. - -# Secrets - -In order to use multiple namespace we need to create approriate secrets in each namespace. Tls certificate secrets must be created in all namespaces (db-ca db-tls). - -![general_schema](./images/K8S_NAMESPACE_SEG.png) - - - diff --git a/docs/multitenant/README.md b/docs/multitenant/README.md index 18c3e4a5..8d9e7024 100644 --- a/docs/multitenant/README.md +++ b/docs/multitenant/README.md @@ -38,7 +38,6 @@ /vscode-markdown-toc-config --> -Starting from OraOperator version 1.2.0, there are two classes of multitenant controllers: one based on [ORDS](https://www.oracle.com/uk/database/technologies/appdev/rest.html) and another based on a dedicated REST server for the operator, called LREST. In both cases, the features remains unchanged (a part from CRD name changes). A pod running a REST server (either LREST or ORDS) acts as the proxy server connected to the container database (CDB) for all incoming kubectl requests. We plan to discontinue the ORDS based controller, in the next release; no regression (a part form CRD name changes). diff --git a/docs/multitenant/lrest-based/images/Generalschema2.jpg b/docs/multitenant/images/Generalschema2.jpg similarity index 100% rename from docs/multitenant/lrest-based/images/Generalschema2.jpg rename to docs/multitenant/images/Generalschema2.jpg diff --git a/docs/multitenant/ords-based/images/KubectlGetSchema.jpg b/docs/multitenant/images/KubectlGetSchema.jpg similarity index 100% rename from docs/multitenant/ords-based/images/KubectlGetSchema.jpg rename to docs/multitenant/images/KubectlGetSchema.jpg diff --git a/docs/multitenant/ords-based/images/KubectlGetSchema2.jpg b/docs/multitenant/images/KubectlGetSchema2.jpg similarity index 100% rename from docs/multitenant/ords-based/images/KubectlGetSchema2.jpg rename to docs/multitenant/images/KubectlGetSchema2.jpg diff --git a/docs/multitenant/ords-based/images/old_testcase_wf.jpg b/docs/multitenant/images/old_testcase_wf.jpg similarity index 100% rename from docs/multitenant/ords-based/images/old_testcase_wf.jpg rename to docs/multitenant/images/old_testcase_wf.jpg diff --git a/docs/multitenant/ords-based/images/plsqlmap.jpg b/docs/multitenant/images/plsqlmap.jpg similarity index 100% rename from docs/multitenant/ords-based/images/plsqlmap.jpg rename to docs/multitenant/images/plsqlmap.jpg diff --git a/docs/multitenant/ords-based/images/usecaseschema.jpg b/docs/multitenant/images/usecaseschema.jpg similarity index 100% rename from docs/multitenant/ords-based/images/usecaseschema.jpg rename to docs/multitenant/images/usecaseschema.jpg diff --git a/docs/multitenant/lrest-based/README.md b/docs/multitenant/lrest-based/README.md deleted file mode 100644 index d9b72a9d..00000000 --- a/docs/multitenant/lrest-based/README.md +++ /dev/null @@ -1,501 +0,0 @@ - - - -# LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT - - -- [LREST BASED MULTITENANT CONTROLLERS FOR PDB LIFE CYCLE MANAGEMENT](#lrest-based-multitenant-controllers-for-pdb-life-cycle-management) - - [STEP BY STEP CONFIGURATION](#step-by-step-configuration) - - [Multiple namespace setup](#multiple-namespace-setup) - - [Create the operator](#create-the-operator) - - [Container database setup](#container-database-setup) - - [Apply rolebinding](#apply-rolebinding) - - [Certificate and credentials](#certificate-and-credentials) - - [Private key 🔑](#private-key-) - - [Public Key 🔑](#public-key-) - - [Certificates](#certificates) - - [Create secrets for certificate and keys](#create-secrets-for-certificate-and-keys) - - [Create secrets with encrypted password](#create-secrets-with-encrypted-password) - - [Create lrest pod](#create-lrest-pod) - - [Create PDB](#create-pdb) - - [pdb config map ](#pdb-config-map) - - [Open PDB](#open-pdb) - - [Close PDB](#close-pdb) - - [Clone PDB](#clone-pdb) - - [Unplug PDB](#unplug-pdb) - - [Plug PDB](#plug-pdb) - - [Delete PDB](#delete-pdb) - - [Map PDB](#map-pdb) - - - - - -**Lrpdb** and **lrest** are two controllers for PDB lifecycle management (**PDBLCM**). They rely on a dedicated REST server (Lite Rest Server) Container image to run. The `lrest` controller is available on the Oracle Container Registry (OCR). The container database can be anywhere (on-premises or in the Cloud). - -![generaleschema](./images/Generalschema2.jpg) - -## STEP BY STEP CONFIGURATION -Complete each of these steps in the order given. - -### Multiple namespace setup - -Before proceeding with controllers setup, ensure that the Oracle Database Operator (operator) is configured to work with multiple namespaces, as specified in the [README](../../../README.md). -In this document, each controller is running in a dedicated namespace: lrest controller is running in **cdbnamespace** , lrpdb controller is running in **pdbnamespace**. The [usecase directory](./usecase/README.md) contains all the files reported in this document. - -Configure the **WACTH_NAMESPACE** list of the operator `yaml` file - -```bash -sed -i 's/value: ""/value: "oracle-database-operator-system,pdbnamespace,cdbnamespace"/g' oracle-database-operator.yaml -``` - -### Create the operator -Run the following command: - -```bash -kubectl apply -f oracle-database-operator.yaml -``` -Check the controller: -```bash -kubectl get pods -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -oracle-database-operator-controller-manager-796c9b87df-6xn7c 1/1 Running 0 22m -oracle-database-operator-controller-manager-796c9b87df-sckf2 1/1 Running 0 22m -oracle-database-operator-controller-manager-796c9b87df-t4qns 1/1 Running 0 22m -``` -### Container database setup - -On the container database, use the following commands to configure the account for PDB administration: - -```sql -alter session set "_oracle_script"=true; -create user identified by ; -grant create session to container=all; -grant sysdba to container=all; -``` - - -### Apply rolebinding - - -Apply the following files : [`pdbnamespace_binding.yaml`](./usecase/pdbnamespace_binding.yaml) [`cdbnamespace_binding.yaml`](./usecase/cdbnamespace_binding.yaml) -```bash -kubectl apply -f pdbnamespace_binding.yaml -kubectl apply -f cdbnamespace_binding.yaml -``` - -### Certificate and credentials -You must create the public key, private key, certificates and Kubernetes Secrets for the security configuration. - -#### Private key 🔑 -> Note: Only private key **PCKS8** format is supported by LREST controllers. Before you start configuration, ensure that you can use it. If you are using [`openssl3`](https://docs.openssl.org/master/) then `pcks8` is generated by default. If it is not already generated, then use the following command to create a `pcks8` private key - -```bash -openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out private.key -``` -#### Public Key 🔑 -Create the public key. - -```bash -/usr/bin/openssl rsa -in private.key -outform PEM -pubout -out public.pem -``` -#### Certificates -Create certificates. -```bash -openssl req -new -x509 -days 365 -key private.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=oracle Root CA" -out ca.crt -``` -```bash -openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=CN/ST=GD/L=SZ/O=oracle, Inc./CN=cdb-dev-lrest.cdbnamespace" -out server.csr -``` -```bash -/usr/bin/echo "subjectAltName=DNS:cdb-dev-lrest.cdbnamespace,DNS:www.example.com" > extfile.txt -``` -```bash -/usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey private.key -CAcreateserial -out tls.crt -``` - -### Create secrets for certificate and keys -Create the Kubernetes Secrets. - -```bash -kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system -kubectl create secret generic db-ca --from-file="ca.crt" -n oracle-database-operator-system -kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n cdbnamespace -kubectl create secret generic db-ca --from-file="ca.crt" -n cdbnamespace -kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n pdbnamespace -kubectl create secret generic db-ca --from-file="ca.crt" -n pdbnamespace -``` - -```bash -kubectl create secret tls prvkey --key="private.key" --cert=ca.crt -n cdbnamespace -kubectl create secret generic pubkey --from-file=publicKey=public.pem -n cdbnamespace -kubectl create secret generic prvkey --from-file=privateKey="private.key" -n pdbnamespace -``` - -### Create secrets with encrypted password - -In this example, we create the Secrets for each credential (username and password) - -| secret usr | secrets pwd | credential description | -| -----------|-------------|-----------------------------------------------------------| -| **dbuser** |**dbpass** | the administrative user created on the container database | -| **wbuser** |**wbpass** | the user for https authentication | -| **pdbusr** |**pdbpwd** | the administrative user of the pdbs | - - -```bash -echo "[ADMINUSERNAME]" > dbuser.txt -echo "[ADMINUSERNAME PASSWORD]" > dbpass.txt -echo "[WEBUSER]" > wbuser.txt -echo "[WEBUSER PASSWORD]" > wbpass.txt -echo "[PDBUSERNAME]" > pdbusr.txt -echo "[PDBUSERNAME PASSWORD]" > pdbpwd.txt - -## Encrypt the credentials -openssl rsautl -encrypt -pubin -inkey public.pem -in dbuser.txt |base64 > e_dbuser.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in dbpass.txt |base64 > e_dbpass.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in wbuser.txt |base64 > e_wbuser.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in wbpass.txt |base64 > e_wbpass.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in pdbusr.txt |base64 > e_pdbusr.txt -openssl rsautl -encrypt -pubin -inkey public.pem -in pdbpwd.txt |base64 > e_pdbpwd.txt - -kubectl create secret generic dbuser --from-file=e_dbuser.txt -n cdbnamespace -kubectl create secret generic dbpass --from-file=e_dbpass.txt -n cdbnamespace -kubectl create secret generic wbuser --from-file=e_wbuser.txt -n cdbnamespace -kubectl create secret generic wbpass --from-file=e_wbpass.txt -n cdbnamespace -kubectl create secret generic wbuser --from-file=e_wbuser.txt -n pdbnamespace -kubectl create secret generic wbpass --from-file=e_wbpass.txt -n pdbnamespace -kubectl create secret generic pdbusr --from-file=e_pdbusr.txt -n pdbnamespace -kubectl create secret generic pdbpwd --from-file=e_pdbpwd.txt -n pdbnamespace - -rm dbuser.txt dbpass.txt wbuser.txt wbpass.txt pdbusr.txt pdbpwd.txt \ - e_dbuser.txt e_dbpass.txt e_wbuser.txt e_wbpass.txt e_pdbusr.txt e_pdbpwd.txt -``` - -### Create lrest pod - -To create the REST pod and monitor its processing, use the `yaml` file [`create_lrest_pod.yaml`](./usecase/create_lrest_pod.yaml) - -Ensure that you update the **lrestImage** with the latest version available on the [Oracle Container Registry (OCR)](https://container-registry.oracle.com/ords/f?p=113:4:104288359787984:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1283,1283,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,This%20image%20is%20part%20of%20and%20for%20use%20with%20the%20Oracle%20Database%20Operator%20for%20Kubernetes,1,0&cs=3076h-hg1qX3eJANBcUHBNBCmYWjMvxLkZyTAhDn2e8VR8Gxb_a-I8jZLhf9j6gmnimHwlP_a0OQjX6vjBfSAqQ) - -```bash ---> for amd64 -lrestImage: container-registry.oracle.com/database/operator:lrest-241210-amd64 - ---> for arm64 -lrestImage: container-registry.oracle.com/database/operator:lrest-241210-arm64 -``` - -```bash -kubectl apply -f create_lrest_pod.yaml -``` - -monitor the file processing: - -```bash -kubectl get pods -n cdbnamespace --watch -NAME READY STATUS RESTARTS AGE -cdb-dev-lrest-rs-9gvx2 0/1 Pending 0 0s -cdb-dev-lrest-rs-9gvx2 0/1 Pending 0 0s -cdb-dev-lrest-rs-9gvx2 0/1 ContainerCreating 0 0s -cdb-dev-lrest-rs-9gvx2 1/1 Running 0 2s - -kubectl get lrest -n cdbnamespace -NAME CDB NAME DB SERVER DB PORT TNS STRING REPLICAS STATUS MESSAGE -cdb-dev DB12 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) 1 Ready -``` - -Check the Pod logs: - -```bash -/usr/local/go/bin/kubectl logs -f `/usr/local/go/bin/kubectl get pods -n cdbnamespace|grep lrest|cut -d ' ' -f 1` -n cdbnamespace -``` - -Output example: - -```text -... -... -2024/09/05 12:44:09 wallet file /opt/oracle/lrest/walletfile exists completed -2024/09/05 12:44:09 call: C.ReadWallet -LENCHECK: 7 11 7 8 -2024/09/05 12:44:09 ===== DUMP INFO ==== -00000000 28 44 45 53 43 52 49 50 54 49 4f 4e 3d 28 43 4f |(DESCRIPTION=(CO| -00000010 4e 4e 45 43 54 5f 54 49 4d 45 4f 55 54 3d 39 30 |NNECT_TIMEOUT=90| -00000020 29 28 52 45 54 52 59 5f 43 4f 55 4e 54 3d 33 30 |)(RETRY_COUNT=30| -00000030 29 28 52 45 54 52 59 5f 44 45 4c 41 59 3d 31 30 |)(RETRY_DELAY=10| -00000040 29 28 54 52 41 4e 53 50 4f 52 54 5f 43 4f 4e 4e |)(TRANSPORT_CONN| -00000050 45 43 54 5f 54 49 4d 45 4f 55 54 3d 37 30 29 28 |ECT_TIMEOUT=70)(| -00000060 4c 4f 41 44 5f 42 41 4c 4c 41 4e 43 45 3d 4f 4e |LOAD_BALLANCE=ON| -00000070 29 28 41 44 44 52 45 53 53 3d 28 50 52 4f 54 4f |)(ADDRESS=(PROTO| -00000080 43 4f 4c 3d 54 43 50 29 28 48 4f 53 54 3d 73 63 |COL=TCP)(HOST=sc| -00000090 61 6e 31 32 2e 74 65 73 74 72 61 63 2e 63 6f 6d |an12.testrac.com| -000000a0 29 28 50 4f 52 54 3d 31 35 32 31 29 28 49 50 3d |)(PORT=1521)(IP=| -000000b0 56 34 5f 4f 4e 4c 59 29 29 28 4c 4f 41 44 5f 42 |V4_ONLY))(LOAD_B| -000000c0 41 4c 4c 41 4e 43 45 3d 4f 4e 29 28 41 44 44 52 |ALLANCE=ON)(ADDR| -000000d0 45 53 53 3d 28 50 52 4f 54 4f 43 4f 4c 3d 54 43 |ESS=(PROTOCOL=TC| -000000e0 50 29 28 48 4f 53 54 3d 73 63 61 6e 33 34 2e 74 |P)(HOST=scan34.t| -000000f0 65 73 74 72 61 63 2e 63 6f 6d 29 28 50 4f 52 54 |estrac.com)(PORT| -00000100 3d 31 35 32 31 29 28 49 50 3d 56 34 5f 4f 4e 4c |=1521)(IP=V4_ONL| -00000110 59 29 29 28 43 4f 4e 4e 45 43 54 5f 44 41 54 41 |Y))(CONNECT_DATA| -00000120 3d 28 53 45 52 56 45 52 3d 44 45 44 49 43 41 54 |=(SERVER=DEDICAT| -00000130 45 44 29 28 53 45 52 56 49 43 45 5f 4e 41 4d 45 |ED)(SERVICE_NAME| -00000140 3d 54 45 53 54 4f 52 44 53 29 29 29 |=TESTORDS)))| -00000000 2f 6f 70 74 2f 6f 72 61 63 6c 65 2f 6c 72 65 73 |/opt/oracle/lres| -00000010 74 2f 77 61 6c 6c 65 74 66 69 6c 65 |t/walletfile| -2024/09/05 12:44:09 Get credential from wallet -7 -8 -2024/09/05 12:44:09 dbuser: restdba webuser :welcome -2024/09/05 12:44:09 Connections Handle -2024/09/05 12:44:09 Working Session Aarry dbhanlde=0x1944120 -2024/09/05 12:44:09 Monitor Session Array dbhanlde=0x1a4af70 -2024/09/05 12:44:09 Open cursors -Parsing sqltext=select inst_id,con_id,open_mode,nvl(restricted,'NONE'),total_size from gv$pdbs where inst_id = SYS_CONTEXT('USERENV','INSTANCE') and name =upper(:b1) -Parsing sqltext=select count(*) from pdb_plug_in_violations where name =:b1 -2024/09/05 12:44:11 Protocol=https -2024/09/05 12:44:11 starting HTTPS/SSL server -2024/09/05 12:44:11 ==== TLS CONFIGURATION === -2024/09/05 12:44:11 srv=0xc000106000 -2024/09/05 12:44:11 cfg=0xc0000a2058 -2024/09/05 12:44:11 mux=0xc0000a2050 -2024/09/05 12:44:11 tls.minversion=771 -2024/09/05 12:44:11 CipherSuites=[49200 49172 157 53] -2024/09/05 12:44:11 cer=/opt/oracle/lrest/certificates/tls.crt -2024/09/05 12:44:11 key=/opt/oracle/lrest/certificates/tls.key -2024/09/05 12:44:11 ========================== -2024/09/05 12:44:11 HTTPS: Listening port=8888 -2024/09/05 12:44:23 call BasicAuth Succeded -2024/09/05 12:44:23 HTTP: [1:0] Invalid credential <-- This message can be ignored - -``` - -**lrest Pod creation** - parameters list -| Name | Dcription | ---------------------------|-------------------------------------------------------------------------------| -|cdbName | Name of the container database (db) | -|lrestImage (DO NOT EDIT) | **container-registry.oracle.com/database/lrest-dboper:latest** use the latest label availble on OCR | -|dbTnsurl | The string of the tns alias to connect to cdb. Attention: remove all white space from string | -|deletePdbCascade | Delete all of the PDBs associated to a CDB resource when the CDB resource is dropped using [imperative approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/imperative-command/) | -|cdbAdminUser | Secret: the administrative (admin) user | -|fileNameConversions | Use file name conversion if you are not using ASM | -|cdbAdminPwd | Secret: the admin user password | -|webServerUser | Secret: the HTTPS user | -|webServerPwd | Secret: the HTTPS user password | -|cdbTlsCrt | Secret: the `tls.crt ` | -|cdbPubKey | Secret: the public key | -|cdbPrvKey | Secret: the private key | - - - - -### Create PDB - -To create a pluggable database, apply the yaml file [`create_pdb1_resource.yaml`](./usecase/create_pdb1_resource.yaml) - -```bash -kubectl apply -f create_pdb1_resource.yaml -``` -Check the status of the resource and the PDB existence on the container db: - -```bash -kubectl get lrpdb -n pdbnamespace -NAME CONNECT_STRING CDB NAME LRPDB NAME LRPDB STATE LRPDB SIZE STATUS MESSAGE LAST SQLCODE -lrpdb1 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) DB12 pdbdev MOUNTED 2G Ready Success -``` - -```bash -SQL> show pdbs - - CON_ID CON_NAME OPEN MODE RESTRICTED ----------- ------------------------------ ---------- ---------- - 2 PDB$SEED READ ONLY NO - 3 PDBDEV MOUNTED -SQL> -``` -``Note that after creation, the PDB is not open. You must explicitly open it using a dedicated `yaml` file. - -**pdb creation** - parameters list - -| Name | Dcription | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database | -|pdbName | Name of the PDB that you want to create | -|assertiveLrpdbDeletion | Boolean: Turn on the imperative approach on PDB resource deletion | -|adminpdbUser | Secret: PDB admin user | -|adminpdbPass | Secret: password of PDB admin user | -|lrpdbTlsKey | Secret: `tls.key ` | -|lrpdbTlsCrt | Secret: `tls.crt` | -|lrpdbTlsCat | Secret: `ca.crt` | -|webServerUser | Secret: the HTTPS user | -|webServerPwd | Secret: the HTTPS user password | -|cdbPrvKey | Secret: private key | -|cdbPubKey | Secret: public key | -|pdbconfigmap | kubernetes config map that contains the PDB initialization (init) parameters | - -> NOTE: **assertiveLrpdbDeletion** must be specified for the following PDB actions **CLONE** **CREATE** **PLUG** **MAP**. - -🔥 **assertiveLrpdbDeletion** drops pluggable database using **INCLUDE DATAFILES** option - -All of the parameters **adminpdbUser** **adminpdbPass** **lrpdbTlsKey** **lrpdbTlsCrt** **lrpdbTlsCat** **webServerUser** **webServerPwd** **cdbPrvKey** **cdbPubKey** must be specified in all PDB lifecycle management `yaml` files. To simplify presentation of requirements, we will not include them in the subsequent tables. - - -#### pdb config map - -By using **pdbconfigmap** it is possible to specify a kubernetes `configmap` with init PDB parameters. The config map payload has the following format: - - -``` -;; -;; -;; -.... -.... -;; -``` - -Example of `configmap` creation: - -```bash -cat < parameters.txt -session_cached_cursors;100;spfile -open_cursors;100;spfile -db_file_multiblock_read_count;16;spfile -EOF - -kubectl create configmap config-map-pdb -n pdbnamespace --from-file=./parameters.txt - -kubectl describe configmap config-map-pdb -n pdbnamespace -Name: config-map-pdb -Namespace: pdbnamespace -Labels: -Annotations: - -Data -==== -parameters.txt: ----- -session_cached_cursors;100;spfile -open_cursors;100;spfile -db_file_multiblock_read_count;16;spfile -test_invalid_parameter;16;spfile -``` - -- If specified, the `configmap` is applied during PDB **cloning**, **opening** and **plugging** -- The `configmap` is not monitored by the reconciliation loop; this feature will be available in future releases. This means that if someone decides to manually alter an init parameter, then the operator does not take any actions to syncronize PDB configuration with the `configmap`. -- **Alter system parameter feature** will be available in future releases. -- An application error with the `configmap` (for whatever reason) does not stop processes from completing. A warning with the associated SQL code is reported in the log file. - - - -### Open PDB - -To open the PDB, use the file [`open_pdb1_resource.yaml`](./usecase/open_pdb1_resource.yaml): - -```bash -kubectl apply -f open_pdb1_resource.yaml -``` - - **pdb opening** - parameters list - -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|pdbName | Name of the pluggable database (PDB) that you are creating | -|action | Use **Modify** to open the PDB | -|pdbState | Use **OPEN** to open the PDB | -|modifyOption | Use **READ WRITE** to open the PDB | - -### Close PDB - -To close the PDB, use the file [`close_pdb1_resource.yaml`](./usecase/close_pdb1_resource.yaml): - -```bash -kubectl apply -f close_pdb1_resource.yaml -``` -**pdb closing** - parameters list -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|pdbName | Name of the pluggable database (PDB) that you want to create | -|action | Use **Modify** to close the PDB | -|pdbState | Use **CLOSE** to close the PDB | -|modifyOption | Use **IMMEDIATE** to close the PDB | - -### Clone PDB ### - -To clone the PDB, use the file [`clone_pdb1_resource.yaml`](./usecase/clone_pdb1_resource.yaml): - -```bash -kubeclt apply -f clone_pdb1_resource.yaml -``` -**pdb cloning** - parameters list -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|pdbName | The name of the new pluggable database (PDB) | -|srcPdbName | The name of the source PDB | -|fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | -|totalSize | Set **unlimited** for cloning | -|tempSize | Set **unlimited** for cloning | -|pdbconfigmap | kubernetes `configmap` which contains the PDB init parameters | -|action | Use **clone** to clone the PDB | - -### Unplug PDB - -To unplug the PDB, use the file [`unplug_pdb1_resource.yaml`](./usecase/unplug_pdb1_resource.yaml): - -**pdb unplugging** -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|pdbName | Name of the pluggable database (PDB)| -### Plug PDB - -To plug in the PDB, use the file [`plug_pdb1_resource.yaml`](./usecase/plug_pdb1_resource.yaml). In this example, we plug in the PDB that was unpluged in the previous step: - -**pdb plugging** -| Name | Description/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB)| | -|pdbName | Name of the pluggable database (PDB) | -|**xmlFileName** | Path of the XML file | -|action | **plug** | -|fileNameConversions | File name convert pattern **("path1","path2")** or **NONE** | -|sourceFileNameConversion | See parameter [SOURCE_FILE_NAME_CONVERT](https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/CREATE-PLUGGABLE-DATABASE.html#GUID-F2DBA8DD-EEA8-4BB7-A07F-78DC04DB1FFC__CCHEJFID) documentation | -|pdbconfigmap | Kubernetes `configmap` that contains the PDB init parameters | - -### Delete PDB - -To delete the PDB, use the file [`delete_pdb1_resource.yaml`](./usecase/delete_pdb1_resource.yaml) - -**pdb deletion** - -| Name | Dcription/Value | -|-------------------------|-------------------------------------------------------------------------------| -|cdbResName | REST server resource name | -|cdbNamespace | Namespace of the REST server | -|cdbName | Name of the container database (CDB) | -|action | **Delete** | -|dropAction | **INCLUDING** - Including datafiles or **NONE** | - - -### Map PDB - -If you need to create a CRD for an existing PDB, then you can use the map option by applying the file [`map_pdb1_resource.yaml`](./usecase/map_pdb1_resource.yaml) -Map functionality can be used in a situation where you have a pdb which is not registered in the operator as a CRD. It's a temporary solution while waiting the autodiscovery to be available. - - - diff --git a/docs/multitenant/lrest-based/images/UsecaseSchema.jpg b/docs/multitenant/lrest-based/images/UsecaseSchema.jpg deleted file mode 100644 index 14eb0d86bd9e1b14de202aca8312c814793d78a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 185861 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 StsFA*^K#nnXn{ybpV_(Ly`TetC!4<|*X^PQXi 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 qc}usJvBQm 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%0ACdR&roOyW3o(%~OS%+TJaRHsh4 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 D4UFOA> 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 sLIlI>es1C)7S}^Z@9@*K% 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 RK2h*>P)%1gNBQWb{{_V`YY_kd diff --git a/docs/multitenant/ords-based/images/makesecrets_1_1.png b/docs/multitenant/ords-based/images/makesecrets_1_1.png deleted file mode 100644 index f0f6f21569bfa17aab7bd37827d97aa041bb06ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117953 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 diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml deleted file mode 100644 index 8ace42e8..00000000 --- a/docs/multitenant/ords-based/provisioning/multinamespace/cdb_create.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: CDB -metadata: - name: cdb-dev - namespace: cdbnamespace -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "[...]" - key: "[...]" - ordsPwd: - secret: - secretName: "[...]" - key: "[...]" - cdbAdminUser: - secret: - secretName: "[...]" - key: "[...]" - cdbAdminPwd: - secret: - secretName: "[...]" - key: "[...]" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml deleted file mode 100644 index 4dac1aea..00000000 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_clone.yaml +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdb2_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Clone" diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml deleted file mode 100644 index 44b1a086..00000000 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_close.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml deleted file mode 100644 index 2bf2189b..00000000 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_create.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml deleted file mode 100644 index 296c9feb..00000000 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_delete.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - - diff --git a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml b/docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml deleted file mode 100644 index 9f85f0b5..00000000 --- a/docs/multitenant/ords-based/provisioning/multinamespace/pdb_open.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" diff --git a/docs/multitenant/ords-based/provisioning/ords_image.md b/docs/multitenant/ords-based/provisioning/ords_image.md deleted file mode 100644 index e2d1dcef..00000000 --- a/docs/multitenant/ords-based/provisioning/ords_image.md +++ /dev/null @@ -1,81 +0,0 @@ - - -# Build ORDS Docker Image - -This file contains the steps to create an ORDS based image to be used solely by the PDB life cycle multitentant controllers. - -**NOTE:** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-manage-pdb-life-cycle-using-oracle-db-operator-on-prem-database-controller) steps. - -#### Clone the software using git: - -> Under directory ./oracle-database-operator/ords you will find the [Dockerfile](../../../ords/Dockerfile) and [runOrdsSSL.sh](../../../ords/runOrdsSSL.sh) required to build the image. - -```sh - git clone git@orahub.oci.oraclecorp.com:rac-docker-dev/oracle-database-operator.git - cd oracle-database-operator/ords/ -``` - -#### Login to the registry: container-registry.oracle.com - -**NOTE:** To login to this registry, you will need to the URL https://container-registry.oracle.com , Sign in, then click on "Java" and then accept the agreement. - -```bash -docker login container-registry.oracle.com -``` - -#### Login to the your container registry - -Login to a repo where you want to push your docker image (if needed) to pull during deployment in your environment. - -```bash -docker login -``` - -#### Build the image - -Build the docker image by using below command: - -```bash -docker build -t oracle/ords-dboper:latest . -``` -> If your are working behind a proxy mind to specify https_proxy and http_proxy during image creation - -Check the docker image details using: - -```bash -docker images -``` - -> OUTPUT EXAMPLE -```bash -REPOSITORY TAG IMAGE ID CREATED SIZE -oracle/ords-dboper latest fdb17aa242f8 4 hours ago 1.46GB - -``` - -#### Tag and push the image - -Tag and push the image to your image repository. - -NOTE: We have the repo as `phx.ocir.io//oracle/ords:latest`. Please change as per your environment. - -```bash -docker tag oracle/ords-dboper:ords-latest phx.ocir.io//oracle/ords:latest -docker push phx.ocir.io//oracle/ords:latest -``` - -#### In case of private image - -If you the image not be public then yuo need to create a secret containing the password of your image repository. -Create a Kubernetes Secret for your docker repository to pull the image during deployment using the below command: - -```bash -kubectl create secret generic container-registry-secret --from-file=.dockerconfigjson=./.docker/config.json --type=kubernetes.io/dockerconfigjson -n oracle-database-operator-system -``` - -Use the parameter `ordsImagePullSecret` to specify the container secrets in pod creation yaml file - -#### [Image createion example](../usecase01/logfiles/BuildImage.log) - - - diff --git a/docs/multitenant/ords-based/provisioning/quickOKEcreation.md b/docs/multitenant/ords-based/provisioning/quickOKEcreation.md deleted file mode 100644 index 19d9323e..00000000 --- a/docs/multitenant/ords-based/provisioning/quickOKEcreation.md +++ /dev/null @@ -1,136 +0,0 @@ - - -### Quick Oke creation script - -Use this script to create quickly an OKE cluster in your OCI. - -#### Prerequisties: -- ocicli is properly configured on your client -- make is installed on your client -- vnc is already configured -- ssh key is configured (public key available under directory ~/.ssh) -- edit make providing all the information about your compartment, vnc,subnet,lb subnet and nd subnet (exported variables in the header section) - - -#### Execution: - -```bash -make all -``` - -Monitor the OKE from OCI console - -#### Makefile -```makefile -.EXPORT_ALL_VARIABLES: - -export CMPID=[.... COMPARTMENT ID.............] -export VNCID=[.... VNC ID ....................] -export ENDID=[.... SUBNET END POINT ID .......] -export LBSID=[.....LB SUBNET ID...............] -export NDSID=[.....NODE SUBNET ID.............] - - -#ssh public key -export KEYFL=~/.ssh/id_rsa.pub - -#cluster version -export KSVER=v1.27.2 - -#cluster name -export CLUNM=myoke - -#pool name -export PLNAM=Pool1 - -#logfile -export LOGFILE=./clustoke.log - -#shape -export SHAPE=VM.Standard.E4.Flex - -OCI=/home/oracle/bin/oci -CUT=/usr/bin/cut -KUBECTL=/usr/bin/kubectl -CAT=/usr/bin/cat - -all: cluster waitcluster pool waitpool config desccluster - -cluster: - @echo " - CREATING CLUSTER " - @$(OCI) ce cluster create \ - --compartment-id $(CMPID) \ - --kubernetes-version $(KSVER) \ - --name $(CLUNM) \ - --vcn-id $(VNCID) \ - --endpoint-subnet-id $(ENDID) \ - --service-lb-subnet-ids '["'$(LBSID)'"]' \ - --endpoint-public-ip-enabled true \ - --persistent-volume-freeform-tags '{"$(CLUNM)" : "OKE"}' 1>$(LOGFILE) 2>&1 - -waitcluster: - @while [ `$(OCI) ce cluster list --compartment-id $(CMPID) \ - --name $(CLUNM) --lifecycle-state ACTIVE --query data[0].id \ - --raw-output |wc -l ` -eq 0 ] ; do sleep 5 ; done - @echo " - CLUSTER CREATED" - - -pool: - @echo " - CREATING POOL" - @$(eval PBKEY :=$(shell $(CAT) $(KEYFL)|grep -v " PUBLIC KEY")) - @$(OCI) ce node-pool create \ - --cluster-id `$(OCI) ce cluster list --compartment-id $(CMPID) \ - --name $(CLUNM) --lifecycle-state ACTIVE --query data[0].id --raw-output` \ - --compartment-id $(CMPID) \ - --kubernetes-version $(KSVER) \ - --name $(PLNAM) \ - --node-shape $(SHAPE) \ - --node-shape-config '{"memoryInGBs": 8.0, "ocpus": 1.0}' \ - --node-image-id `$(OCI) compute image list \ - --operating-system 'Oracle Linux' --operating-system-version 7.9 \ - --sort-by TIMECREATED --compartment-id $(CMPID) --shape $(SHAPE) \ - --query data[1].id --raw-output` \ - --node-boot-volume-size-in-gbs 50 \ - --ssh-public-key "$(PBKEY)" \ - --size 3 \ - --placement-configs '[{"availabilityDomain": "'`oci iam availability-domain list \ - --compartment-id $(CMPID) \ - --query data[0].name --raw-output`'", "subnetId": "'$(NDSID)'"}]' 1>>$(LOGFILE) 2>&1 - -waitpool: - $(eval CLSID :=$(shell $(OCI) ce cluster list --compartment-id $(CMPID) \ - --name $(CLUNM) --lifecycle-state ACTIVE --query data[0].id --raw-output)) - @while [ `$(OCI) ce node-pool list --compartment-id $(CMPID) \ - --lifecycle-state ACTIVE --cluster-id $(CLSID) \ - --query data[0].id --raw-output |wc -l ` -eq 0 ] ; do sleep 5 ; done - @sleep 10 - $(eval PLLID :=$(shell $(OCI) ce node-pool list --compartment-id $(CMPID) \ - --lifecycle-state ACTIVE --cluster-id $(CLSID) --query data[0].id --raw-output)) - @echo " - POOL CREATED" - -config: - @$(OCI) ce cluster create-kubeconfig --cluster-id \ - `$(OCI) ce cluster list \ - --compartment-id $(CMPID) --name $(CLUNM) --lifecycle-state ACTIVE \ - --query data[0].id --raw-output` \ - --file $(HOME)/.kube/config --region \ - `$(OCI) ce cluster list \ - --compartment-id $(CMPID) --name $(CLUNM) --lifecycle-state ACTIVE \ - --query data[0].id --raw-output|$(CUT) -f4 -d. ` \ - --token-version 2.0.0 --kube-endpoint PUBLIC_ENDPOINT - @echo " - KUBECTL PUBLIC ENDPOINT CONFIGURED" - - -desccluster: - @$(eval TMPSP := $(shell date "+%y/%m/%d:%H:%M" )) - $(KUBECTL) get nodes -o wide - $(KUBECTL) get storageclass - -checkvol: - $(OCI) bv volume list \ - --compartment-id $(CMPID) \ - --lifecycle-state AVAILABLE \ - --query 'data[?"freeform-tags".stackgres == '\''OKE'\''].id' -``` - - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml deleted file mode 100644 index 5e020de6..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_create.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "[...]" - key: "[...]" - ordsPwd: - secret: - secretName: "[...]" - key: "[...]" - cdbAdminUser: - secret: - secretName: "[...]" - key: "[...]" - cdbAdminPwd: - secret: - secretName: "[...]" - key: "[...]" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - cdbTlsKey: - secret: - secretName: "[...]" - key: "[...]" - cdbTlsCrt: - secret: - secretName: "[...]" - key: "[...]" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml deleted file mode 100644 index 567b90a4..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - cdbadmin_user: ".....base64 encoded password...." - cdbadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml deleted file mode 100644 index 06d92469..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_close.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml deleted file mode 100644 index 2744223e..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_create.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - assertivePdbDeletion: true - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml deleted file mode 100644 index 523ac1cb..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_delete.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml deleted file mode 100644 index 866db3e4..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_open.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "[...]" - key: "[...]" - adminPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml deleted file mode 100644 index e6605276..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_plug.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - action: "Plug" - assertivePdbDeletion: true - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - diff --git a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml b/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml deleted file mode 100644 index 4e404efe..00000000 --- a/docs/multitenant/ords-based/provisioning/singlenamespace/pdb_unplug.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - secretName: "[...]" - key: "[...]" - webServerPwd: - secret: - secretName: "[...]" - key: "[...]" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" - diff --git a/docs/multitenant/ords-based/usecase/README.md b/docs/multitenant/ords-based/usecase/README.md deleted file mode 100644 index b6f5e590..00000000 --- a/docs/multitenant/ords-based/usecase/README.md +++ /dev/null @@ -1,112 +0,0 @@ - - - -# Use case directory - -The use case directory contains the yaml files to test the multitenant controller functionalities: create ords pod and pdb operation *create / open / close / unplug / plug / delete / clone /map / parameter session* -In this exampl the cdb and pdbs resources are depolyed in different namespaces - -## Makefile helper - -Customizing yaml files (tns alias / credential / namespaces name etc...) is a long procedure prone to human error. A simple [makefile](../usecase/makefile) is available to quickly and safely configure yaml files with your system environment information. Just edit the [parameter file](../usecase/parameters.txt) before proceding. - -```text -[👉 CHECK PARAMETERS..................] -TNSALIAS...............:(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELA.... -ORDPWD.................:[Password for ORDS_PUBLIC_USER ] -SYSPWD.................:[SYS password] -WBUSER.................:[username for https authentication] -WBPASS.................:[password for https authentication] -PDBUSR.................:[pdb admin user] -PDBPWD.................:[pdb admin password] -CDBUSR.................:[cdb admin user e.g. C##DBAPI_CDB_ADMIN] -CDBPWD.................:[cdb admin password] -PDBNAMESPACE...........:[namespace for pdb] -CDBNAMESPACE...........:[namespace for cdb] -COMPANY................:oracle -APIVERSION.............:v4 ---> do not edit -``` - -⚠ **WARNING: The makefile is intended to speed up the usecase directory configuartion only, it is not supported, the editing and configuration of yaml files for production system is left up to the end user** - -### Pre requisistes: - -- Make sure that **kubectl** is properly configured. -- Make sure that all requirements listed in the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) -- Make sure that administrative user on the container database is configured as documented. - -### Commands - -Review your configuraton running ```make check```; if all the parameters are correct then you can proceed with yaml files and certificates generation - -By excuting command ```make operator``` You will have in your directory an operator yaml file with the WATCH LIST required to operate with multiple namespaces. -Note that the yaml file is not applyed; you need to manually execute ```kubectl apply -f oracle-database-operator.yaml```. - -```bash -make operator -``` -You can generate all the other yaml files for pdb life cycle management using ```make genyaml``` - -```bash -make genyaml -``` - -list of generated yaml files - -```text --rw-r--r-- 1 mmalvezz g900 137142 Nov 13 09:35 oracle-database-operator.yaml --rw-r--r-- 1 mmalvezz g900 321 Nov 13 10:27 create_cdb_secrets.yaml --rw-r--r-- 1 mmalvezz g900 234 Nov 13 10:27 create_pdb_secrets.yaml --rw-r--r-- 1 mmalvezz g900 381 Nov 13 10:27 pdbnamespace_binding.yaml --rw-r--r-- 1 mmalvezz g900 381 Nov 13 10:27 cdbnamespace_binding.yaml --rw-r--r-- 1 mmalvezz g900 1267 Nov 13 10:27 create_ords_pod.yaml --rw-r--r-- 1 mmalvezz g900 935 Nov 13 10:27 create_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 935 Nov 13 10:27 create_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 open_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 open_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 845 Nov 13 10:27 open_pdb3_resource.yaml --rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 close_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 842 Nov 13 10:27 close_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 846 Nov 13 10:27 close_pdb3_resource.yaml --rw-r--r-- 1 mmalvezz g900 927 Nov 13 10:27 clone_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 928 Nov 13 10:27 clone_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 802 Nov 13 10:27 delete_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 802 Nov 13 10:27 delete_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 824 Nov 13 10:27 unplug_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 992 Nov 13 10:27 plug_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 887 Nov 13 10:27 map_pdb1_resource.yaml --rw-r--r-- 1 mmalvezz g900 887 Nov 13 10:27 map_pdb2_resource.yaml --rw-r--r-- 1 mmalvezz g900 890 Nov 13 10:27 map_pdb3_resource.yaml -``` - -The command ```make secretes ``` will configure database secrets credential and certificates secretes - -```bash -make secrets -``` - - - -The makefile includes other different targets that can be used to test the various pdb operations available. E.g. - -```makefile -run03.2: - @$(call msg,"clone pdb2-->pdb4") - $(KUBECTL) apply -f $(PDBCLONE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb2-->pdb4 completed") - $(KUBECTL) get pdb pdb3 -n $(PDBNAMESPACE) -``` -The target ```run03.2``` clones pdb2 into pdb4 and wait for ```$TEST_EXEC_TIMEOUT``` for the operation to complete. - -### Output executions:. - -```make secrets``` - -![image](../images/makesecrets_1_1.png) - - - -```make runall``` executes different pdb operations including the cdb controller creation - -![image](../images/makerunall.png) \ No newline at end of file diff --git a/docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml b/docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml deleted file mode 100644 index 5fd355f4..00000000 --- a/docs/multitenant/ords-based/usecase/cdbnamespace_binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: cdbnamespace -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml deleted file mode 100644 index 5723f7c6..00000000 --- a/docs/multitenant/ords-based/usecase/clone_pdb1_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml deleted file mode 100644 index 2b9fc70a..00000000 --- a/docs/multitenant/ords-based/usecase/clone_pdb2_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb4 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml deleted file mode 100644 index ae837ce0..00000000 --- a/docs/multitenant/ords-based/usecase/close_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml deleted file mode 100644 index 1b5d1324..00000000 --- a/docs/multitenant/ords-based/usecase/close_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml deleted file mode 100644 index f4a32938..00000000 --- a/docs/multitenant/ords-based/usecase/close_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: ""new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_ords_pod.yaml b/docs/multitenant/ords-based/usecase/create_ords_pod.yaml deleted file mode 100644 index ad196c9d..00000000 --- a/docs/multitenant/ords-based/usecase/create_ords_pod.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: CDB -metadata: - name: cdb-dev - namespace: cdbnamespace -spec: - cdbName: "DB12" - ordsImage: _your_container_registry/ords-dboper:latest - ordsImagePullPolicy: "Always" - dbTnsurl : "T H I S I S J U S T A N E X A M P L E (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - replicas: 1 - deletePdbCascade: true - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" - cdbAdminUser: - secret: - secretName: "cdbusr" - key: "e_cdbusr.txt" - cdbAdminPwd: - secret: - secretName: "cdbpwd" - key: "e_cdbpwd.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml deleted file mode 100644 index 84e910e0..00000000 --- a/docs/multitenant/ords-based/usecase/create_pdb1_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml deleted file mode 100644 index 0a71c7c3..00000000 --- a/docs/multitenant/ords-based/usecase/create_pdb2_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml deleted file mode 100644 index 3aba580c..00000000 --- a/docs/multitenant/ords-based/usecase/delete_pdb1_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml deleted file mode 100644 index 59b50a64..00000000 --- a/docs/multitenant/ords-based/usecase/delete_pdb2_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/makefile b/docs/multitenant/ords-based/usecase/makefile deleted file mode 100644 index dc881598..00000000 --- a/docs/multitenant/ords-based/usecase/makefile +++ /dev/null @@ -1,915 +0,0 @@ -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# | | | | ___| |_ __ ___ _ __ -# | |_| |/ _ \ | '_ \ / _ \ '__| -# | _ | __/ | |_) | __/ | -# |_| |_|\___|_| .__/ \___|_| -# |_| -# -# WARNING: Using this makefile helps you to customize yaml -# files. Edit parameters.txt with your enviroment -# informartion and execute the following steps -# -# 1) make operator -# it configures the operator yaml files with the -# watch namelist required by the multitenant controllers -# -# 2) make genyaml -# It automatically creates all the yaml files based on the -# information available in the parameters file -# -# 3) make secrets -# It configure the required secrets necessary to operate -# with pdbs multitenant controllers -# -# 4) make runall01 -# Start a series of operation create open close delete and so on -# -# LIST OF GENERAED YAML FILE -# -# ----------------------------- ---------------------------------- -# oracle-database-operator.yaml : oracle database operator -# cdbnamespace_binding.yaml : role binding for cdbnamespace -# pdbnamespace_binding.yaml : role binding for pdbnamespace -# create_ords_pod.yaml : create rest server pod -# create_pdb1_resource.yaml : create first pluggable database -# create_pdb2_resource.yaml : create second pluggable database -# open_pdb1_resource.yaml : open first pluggable database -# open_pdb2_resource.yaml : open second pluggable database -# close_pdb1_resource.yaml : close first pluggable database -# close_pdb2_resource.yaml : close second pluggable database -# clone_pdb_resource.yaml : clone thrid pluggable database -# clone_pdb2_resource.yaml : clone 4th pluggable database -# delete_pdb1_resource.yaml : delete first pluggable database -# delete_pdb2_resource.yaml : delete sencond pluggable database -# delete_pdb3_resource.yaml : delete thrid pluggable database -# unplug_pdb1_resource.yaml : unplug first pluggable database -# plug_pdb1_resource.yaml : plug first pluggable database -# map_pdb1_resource.yaml : map the first pluggable database -# config_map.yam : pdb parameters array -# -DATE := `date "+%y%m%d%H%M%S"` -###################### -# PARAMETER SECTIONS # -###################### - -export PARAMETERS=parameters.txt -export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) -export ORDPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDPWD|cut -d : -f 2) -export SYSPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep SYSPWD|cut -d : -f 2) -export WBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBUSER|cut -d : -f 2) -export WBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBPASS|cut -d : -f 2) -export PDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBUSR|cut -d : -f 2) -export PDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBPWD|cut -d : -f 2) -export CDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBUSR|cut -d : -f 2) -export CDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBPWD|cut -d : -f 2) -export PDBNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBNAMESPACE|cut -d : -f 2) -export CDBNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBNAMESPACE|cut -d : -f 2) -export ORDSIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDSIMG|cut -d : -f 2,3) -export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) -export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) -export OPRNAMESPACE=oracle-database-operator-system -export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml -export TEST_EXEC_TIMEOUT=3m -export IMAGE=oracle/ords-dboper:latest -export ORDSIMGDIR=../../../../ords - -REST_SERVER=ords -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -PRVKEY=ca.key -PUBKEY=public.pem -COMPANY=oracle -RUNTIME=/usr/bin/podman - -################# -### FILE LIST ### -################# - -export ORDS_POD=create_ords_pod.yaml - -export CDB_SECRETS=create_cdb_secrets.yaml -export PDB_SECRETS=create_pdb_secrets.yaml - -export PDBCRE1=create_pdb1_resource.yaml -export PDBCRE2=create_pdb2_resource.yaml - -export PDBCLOSE1=close_pdb1_resource.yaml -export PDBCLOSE2=close_pdb2_resource.yaml -export PDBCLOSE3=close_pdb3_resource.yaml - -export PDBOPEN1=open_pdb1_resource.yaml -export PDBOPEN2=open_pdb2_resource.yaml -export PDBOPEN3=open_pdb3_resource.yaml - -export PDBCLONE1=clone_pdb1_resource.yaml -export PDBCLONE2=clone_pdb2_resource.yaml - -export PDBDELETE1=delete_pdb1_resource.yaml -export PDBDELETE2=delete_pdb2_resource.yaml -export PDBDELETE3=delete_pdb3_resource.yaml - -export PDBUNPLUG1=unplug_pdb1_resource.yaml -export PDBPLUG1=plug_pdb1_resource.yaml - -export PDBMAP1=map_pdb1_resource.yaml -export PDBMAP2=map_pdb2_resource.yaml -export PDBMAP3=map_pdb3_resource.yaml - -export PDBMAP1=map_pdb1_resource.yaml -export PDBMAP2=map_pdb2_resource.yaml -export PDBMAP3=map_pdb3_resource.yaml - - -##BINARIES -export KUBECTL=/usr/bin/kubectl -OPENSSL=/usr/bin/openssl -ECHO=/usr/bin/echo -RM=/usr/bin/rm -CP=/usr/bin/cp -TAR=/usr/bin/tar -MKDIR=/usr/bin/mkdir -SED=/usr/bin/sed - -define msg -@printf "\033[31;7m%s\033[0m\r" "......................................]" -@printf "\033[31;7m[\xF0\x9F\x91\x89 %s\033[0m\n" $(1) -endef - -check: - $(call msg,"CHECK PARAMETERS") - @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) - @printf "ORDPWD.................:%s\n" $(ORDPWD) - @printf "SYSPWD.................:%s\n" $(SYSPWD) - @printf "WBUSER.................:%s\n" $(WBUSER) - @printf "WBPASS.................:%s\n" $(WBPASS) - @printf "PDBUSR.................:%s\n" $(PDBUSR) - @printf "PDBPWD.................:%s\n" $(PDBPWD) - @printf "CDBUSR.................:%s\n" $(CDBUSR) - @printf "CDBPWD.................:%s\n" $(CDBPWD) - @printf "PDBNAMESPACE...........:%s\n" $(PDBNAMESPACE) - @printf "CDBNAMESPACE...........:%s\n" $(CDBNAMESPACE) - @printf "COMPANY................:%s\n" $(COMPANY) - @printf "APIVERSION.............:%s\n" $(APIVERSION) - - -tlscrt: - $(call msg,"TLS GENERATION") - #$(OPENSSL) genrsa -out $(PRVKEY) 2048 - $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) - $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) \ - -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj \ - "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(CDBNAMESPACE)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(CDBNAMESPACE)" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - -tlssec: - $(call msg,"GENERATE TLS SECRET") - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(CDBNAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDBNAMESPACE) - - -delsec: - $(call msg,"CLEAN OLD SECRETS") - $(eval SECRETSP:=$(shell kubectl get secrets -n $(PDBNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) - $(eval SECRETSL:=$(shell kubectl get secrets -n $(CDBNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) - @[ "${SECRETSP}" ] && ( \ - printf "Deleteing secrets in namespace -n $(PDBNAMESPACE)\n") &&\ - ($(KUBECTL) delete secret $(SECRETSP) -n $(PDBNAMESPACE))\ - || ( echo "No screts in namespace $(PDBNAMESPACE)") - @[ "${SECRETSL}" ] && ( \ - printf "Deleteing secrets in namespace -n $(CDBNAMESPACE)\n") &&\ - ($(KUBECTL) delete secret $(SECRETSL) -n $(CDBNAMESPACE))\ - || ( echo "No screts in namespace $(PDBNAMESPACE)") - - -###### ENCRYPTED SECRETS ###### -export PRVKEY=ca.key -export PUBKEY=public.pem -WBUSERFILE=wbuser.txt -WBPASSFILE=wbpass.txt -CDBUSRFILE=cdbusr.txt -CDBPWDFILE=cdbpwd.txt -SYSPWDFILE=syspwd.txt -ORDPWDFILE=ordpwd.txt -PDBUSRFILE=pdbusr.txt -PDBPWDFILE=pdbpwd.txt - - - -secrets: delsec tlscrt tlssec - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey="$(PRVKEY)" -n $(PDBNAMESPACE) - @$(ECHO) $(WBUSER) > $(WBUSERFILE) - @$(ECHO) $(WBPASS) > $(WBPASSFILE) - @$(ECHO) $(CDBPWD) > $(CDBPWDFILE) - @$(ECHO) $(CDBUSR) > $(CDBUSRFILE) - @$(ECHO) $(SYSPWD) > $(SYSPWDFILE) - @$(ECHO) $(ORDPWD) > $(ORDPWDFILE) - @$(ECHO) $(PDBUSR) > $(PDBUSRFILE) - @$(ECHO) $(PDBPWD) > $(PDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBPWDFILE) |base64 > e_$(CDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBUSRFILE) |base64 > e_$(CDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE) |base64 > e_$(SYSPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(ORDPWDFILE) |base64 > e_$(ORDPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic cdbpwd --from-file=e_$(CDBPWDFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic cdbusr --from-file=e_$(CDBUSRFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic syspwd --from-file=e_$(SYSPWDFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic ordpwd --from-file=e_$(ORDPWDFILE) -n $(CDBNAMESPACE) - $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(PDBNAMESPACE) - $(RM) $(WBUSERFILE) $(WBPASSFILE) $(CDBPWDFILE) $(CDBUSRFILE) $(SYSPWDFILE) $(ORDPWDFILE) $(PDBUSRFILE) $(PDBPWDFILE) - $(RM) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(CDBPWDFILE) e_$(CDBUSRFILE) e_$(SYSPWDFILE) e_$(ORDPWDFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) - - -### YAML FILE SECTION ### -operator: - $(CP) ${ORACLE_OPERATOR_YAML} . - ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG - $(SED) -i 's/value: ""/value: $(OPRNAMESPACE),$(PDBNAMESPACE),$(CDBNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` - - -define _script00 -cat < authsection01.yaml - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" - cdbAdminUser: - secret: - secretName: "cdbusr" - key: "e_cdbusr.txt" - cdbAdminPwd: - secret: - secretName: "cdbpwd" - key: "e_cdbpwd.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - -cat< authsection02.yaml - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - - -cat < ${PDBNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: ${PDBNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -cat < ${CDBNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: ${CDBNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -endef -export script00 = $(value _script00) -secyaml: - @ eval "$$script00" - -#echo ords pod creation -define _script01 -cat < ${ORDS_POD} -apiVersion: database.oracle.com/${APIVERSION} -kind: CDB -metadata: - name: cdb-dev - namespace: ${CDBNAMESPACE} -spec: - cdbName: "DB12" - ordsImage: ${ORDSIMG} - ordsImagePullPolicy: "Always" - dbTnsurl : ${TNSALIAS} - replicas: 1 - deletePdbCascade: true -EOF - -cat authsection01.yaml >> ${ORDS_POD} - -endef -export script01 = $(value _script01) - - -define _script02 - -cat <${PDBCRE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat < ${PDBCRE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat <${PDBOPEN1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBOPEN2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBOPEN3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBCLOSE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${PDBCLOSE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${PDBCLOSE3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: ""new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat < ${PDBCLONE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" -EOF - -cat < ${PDBCLONE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb4 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" -EOF - - -cat < ${PDBDELETE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${PDBDELETE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${PDBUNPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" -EOF - -cat <${PDBPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" -EOF - -cat <${PDBMAP1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - -cat <${PDBMAP2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - - -cat <${PDBMAP3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${CDBNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - - -## Auth information -for _file in ${PDBCRE1} ${PDBCRE2} ${PDBOPEN1} ${PDBOPEN2} ${PDBOPEN3} ${PDBCLOSE1} ${PDBCLOSE2} ${PDBCLOSE3} ${PDBCLONE1} ${PDBCLONE2} ${PDBDELETE1} ${PDBDELETE2} ${PDBUNPLUG1} ${PDBPLUG1} ${PDBMAP1} ${PDBMAP2} ${PDBMAP3} -do -ls -ltr ${_file} - cat authsection02.yaml >> ${_file} -done -rm authsection02.yaml -rm authsection01.yaml -endef - -export script02 = $(value _script02) - -genyaml: secyaml - @ eval "$$script01" - @ eval "$$script02" - -cleanyaml: - - $(RM) $(PDBMAP3) $(PDBMAP2) $(PDBMAP1) $(PDBPLUG1) $(PDBUNPLUG1) $(PDBDELETE2) $(PDBDELETE1) $(PDBCLONE2) $(PDBCLONE1) $(PDBCLOSE3) $(PDBCLOSE2) $(PDBCLOSE1) $(PDBOPEN3) $(PDBOPEN2) $(PDBOPEN1) $(PDBCRE2) $(PDBCRE1) $(ORDS_POD) $(CDB_SECRETS) $(PDB_SECRETS) - - $(RM) ${PDBNAMESPACE}_binding.yaml ${CDBNAMESPACE}_binding.yaml - - -cleancrt: - - $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl - - -################# -### PACKAGING ### -################# - -pkg: - - $(RM) -rf /tmp/pkgtestplan - $(MKDIR) /tmp/pkgtestplan - $(CP) -R * /tmp/pkgtestplan - $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ - $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan - -################ -### diag ### -################ - -login: - $(KUBECTL) exec `$(KUBECTL) get pods -n $(CDBNAMESPACE)|grep ords|cut -d ' ' -f 1` -n $(CDBNAMESPACE) -it -- /bin/bash - - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - -####################################################### -#### TEST SECTION #### -####################################################### - -run00: - @$(call msg,"cdb pod creation") - - $(KUBECTL) delete cdb cdb-dev -n $(CDBNAMESPACE) - $(KUBECTL) apply -f $(ORDS_POD) - time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" cdb cdb-dev -n $(CDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"cdb pod completed") - $(KUBECTL) get cdb -n $(CDBNAMESPACE) - $(KUBECTL) get pod -n $(CDBNAMESPACE) - -run01.1: - @$(call msg,"pdb pdb1 creation") - $(KUBECTL) apply -f $(PDBCRE1) - time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 creation completed") - $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) - -run01.2: - @$(call msg, "pdb pdb2 creation") - $(KUBECTL) apply -f $(PDBCRE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb2 creation completed") - $(KUBECTL) get pdb pdb2 -n $(PDBNAMESPACE) - -run02.1: - @$(call msg, "pdb pdb1 open") - $(KUBECTL) apply -f $(PDBOPEN1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 open completed") - $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) - -run02.2: - @$(call msg,"pdb pdb2 open") - $(KUBECTL) apply -f $(PDBOPEN2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb2 open completed") - $(KUBECTL) get pdb pdb2 -n $(PDBNAMESPACE) - - -run03.1: - @$(call msg,"clone pdb1-->pdb3") - $(KUBECTL) apply -f $(PDBCLONE1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb3 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb1-->pdb3 completed") - $(KUBECTL) get pdb pdb3 -n $(PDBNAMESPACE) - - -run03.2: - @$(call msg,"clone pdb2-->pdb4") - $(KUBECTL) apply -f $(PDBCLONE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb2-->pdb4 completed") - $(KUBECTL) get pdb pdb3 -n $(PDBNAMESPACE) - - -run04.1: - @$(call msg,"pdb pdb1 close") - $(KUBECTL) apply -f $(PDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 close completed") - $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) - -run04.2: - @$(call msg,"pdb pdb2 close") - $(KUBECTL) apply -f $(PDBCLOSE2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb2 close completed") - $(KUBECTL) get pdb pdb2 -n $(PDBNAMESPACE) - -run05.1: - @$(call msg,"pdb pdb1 unplug") - $(KUBECTL) apply -f $(PDBUNPLUG1) - $(KUBECTL) wait --for=delete pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb1 unplug completed") - -run06.1: - @$(call msg, "pdb pdb1 plug") - $(KUBECTL) apply -f $(PDBPLUG1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 plug completed") - $(KUBECTL) get pdb pdb1 -n $(PDBNAMESPACE) - -run07.1: - @$(call msg,"pdb pdb1 delete ") - - $(KUBECTL) apply -f $(PDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) apply -f $(PDBDELETE1) - $(KUBECTL) wait --for=delete pdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb1 delete") - $(KUBECTL) get pdb -n $(PDBNAMESPACE) - -run99.1: - $(KUBECTL) delete cdb cdb-dev -n cdbnamespace - $(KUBECTL) wait --for=delete cdb cdb-dev -n $(CDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) get cdb -n cdbnamespaace - $(KUBECTL) get pdb -n pdbnamespaace - - -## SEQ | ACTION -## ----+---------------- -## 00 | create ords pod -## 01 | create pdb -## 02 | open pdb -## 03 | clone pdb -## 04 | close pdb -## 05 | unpug pdb -## 06 | plug pdb -## 07 | delete pdb (declarative) - - -runall01: run00 run01.1 run01.2 run03.1 run03.2 run04.1 run05.1 run06.1 run02.1 run07.1 - - -###### BUILD ORDS IMAGE ###### - -createimage: - $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) - -createimageproxy: - $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) - -tagimage: - @echo "TAG IMAGE" - $(RUNTIME) tag $(IMAGE) $(ORDSIMG) - -push: - $(RUNTIME) push $(ORDSIMG) - - diff --git a/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml deleted file mode 100644 index b71b59d5..00000000 --- a/docs/multitenant/ords-based/usecase/map_pdb1_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml deleted file mode 100644 index 75d056d0..00000000 --- a/docs/multitenant/ords-based/usecase/map_pdb2_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml deleted file mode 100644 index 3523aa68..00000000 --- a/docs/multitenant/ords-based/usecase/map_pdb3_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml deleted file mode 100644 index 93a1d43a..00000000 --- a/docs/multitenant/ords-based/usecase/open_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml deleted file mode 100644 index deb27f9a..00000000 --- a/docs/multitenant/ords-based/usecase/open_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml deleted file mode 100644 index 586f2f57..00000000 --- a/docs/multitenant/ords-based/usecase/open_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/parameters.txt b/docs/multitenant/ords-based/usecase/parameters.txt deleted file mode 100644 index 64dc3759..00000000 --- a/docs/multitenant/ords-based/usecase/parameters.txt +++ /dev/null @@ -1,61 +0,0 @@ - -######################## -## REST SERVER IMAGE ### -######################## - -ORDSIMG:_your_container_registry/ords-dboper:latest - -############################## -## TNS URL FOR CDB CREATION ## -############################## -TNSALIAS:"T H I S I S J U S T A N E X A M P L E (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - -########################################### -## ORDS PUBLIC USER ## -########################################### -ORDPWD:change_me_please - -########################################### -## SYSPASSWORD ## -########################################### -SYSPWD:change_me_please - -####################### -## HTTPS CREDENTIAL ### -####################### - -WBUSER:change_me_please -WBPASS:change_me_please - -##################### -## PDB ADMIN USER ### -##################### - -PDBUSR:change_me_please -PDBPWD:change_me_please - -##################### -## CDB ADMIN USER ### -##################### - -CDBUSR:C##DBAPI_CDB_ADMIN -CDBPWD:change_me_please - -################### -### NAMESPACES #### -################### - -PDBNAMESPACE:pdbnamespace -CDBNAMESPACE:cdbnamespace - -#################### -### COMPANY NAME ### -#################### - -COMPANY:oracle - -#################### -### APIVERSION ### -#################### - -APIVERSION:v4 diff --git a/docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml b/docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml deleted file mode 100644 index 5af79ed6..00000000 --- a/docs/multitenant/ords-based/usecase/pdbnamespace_binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: pdbnamespace -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml deleted file mode 100644 index 9eb5ed77..00000000 --- a/docs/multitenant/ords-based/usecase/plug_pdb1_resource.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml deleted file mode 100644 index 0036d5f7..00000000 --- a/docs/multitenant/ords-based/usecase/unplug_pdb1_resource.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/ca.crt b/docs/multitenant/ords-based/usecase01/ca.crt deleted file mode 100644 index cc9aa8bb..00000000 --- a/docs/multitenant/ords-based/usecase01/ca.crt +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEJTCCAw2gAwIBAgIUNXPtpnNEFBCMcnxRP5kJsBDpafcwDQYJKoZIhvcNAQEL -BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH -DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k -ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE -AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx -NTMyMzVaMIGhMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG -A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j -ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxHDAa -BgNVBAMME2xvY2FsaG9zdCAgUm9vdCBDQSAwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQCmnGVApwUBF1kpqcyr2nYeED0VKvefpoHLtxHSP+vP0lWhW7NU -NJlb1YuUagjJ4/rpGRQmPxcVU51n3aAW3a5qHazIpNxNa3fvgB1rMOPFxGmdel2d -8lIt+u19q19DknX/GNgH9Mog8RcyZyPeA7d2icT8TBo74ognr+8p68O3CjBHQ8EM -SnRQR7/bh1c10Uia317ilKvs+I7oErTq5JFLeIuPDdAJ6UncaeblTf1XJ/1FrpHG -fSS7xmR8x0/MblBQlku4eImYmN35g+eRgf8bLDDwC+GPzDnAqqMLjx6h2N+btDxr -tnn05qyqmN9G08uUlP4d4BXi9ISb/toYypklAgMBAAGjUzBRMB0GA1UdDgQWBBS+ -a4X2XTmdPivdQtqDWNpfOtHypDAfBgNVHSMEGDAWgBS+a4X2XTmdPivdQtqDWNpf -OtHypDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZIrGBNdSw -pe+1agefHfaR8hjZQiXBxdwHM1gR2LWOaFzMS8Q/eRETHTO6+VwQ0/FNaXbAqgqk -G317gZMXS5ZmXuOi28fTpAQtuzokkEKpoK0puTnbXOKGA2QSbBlpSFPqb3aJXvVt -afXFQb5P/0mhr4kuVt7Ech82WM/o5ryFgObygDayDmLatTp+VaRmBZPksnSMhslq -3zPyS7bx2YhbPTLkDxq8Mfr/Msxme8LvSXUpFf4PpQ5zwp1RE32gekct6eRQLmqU -5LXY2aPtqpMF0fBpcwPWbqA9gOYCRKcvXXIr+u1x8hf6Er6grZegHkM9TQ8s0hJd -sxi5tK0lPMHJ ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase01/ca.key b/docs/multitenant/ords-based/usecase01/ca.key deleted file mode 100644 index 1a0ef89d..00000000 --- a/docs/multitenant/ords-based/usecase01/ca.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAppxlQKcFARdZKanMq9p2HhA9FSr3n6aBy7cR0j/rz9JVoVuz -VDSZW9WLlGoIyeP66RkUJj8XFVOdZ92gFt2uah2syKTcTWt374AdazDjxcRpnXpd -nfJSLfrtfatfQ5J1/xjYB/TKIPEXMmcj3gO3donE/EwaO+KIJ6/vKevDtwowR0PB -DEp0UEe/24dXNdFImt9e4pSr7PiO6BK06uSRS3iLjw3QCelJ3Gnm5U39Vyf9Ra6R -xn0ku8ZkfMdPzG5QUJZLuHiJmJjd+YPnkYH/Gyww8Avhj8w5wKqjC48eodjfm7Q8 -a7Z59OasqpjfRtPLlJT+HeAV4vSEm/7aGMqZJQIDAQABAoIBAGXRGYdjCgnarOBr -Jeq3vIsuvUVcVqs35AYMQFXOPltoXHAZTAPfiQC4BW6TRf+q1MDyVH/y+jZMPNsm -cxjGLDopHFgZd4/QZyDzmAbTf75yA2D7UI6fcV0sBUpRGgx/SqC0HADwtT1gWB6z -LRYWC13jX4AXOcjy7OXj/DIQJDCMivedt3dv0rDWJUcBCnVot5tr6zjycefxGKa8 -mG9LZQb3x71FxwpFUau3WLDSwOjtXCeMytaGXnGmIiofJmXnFi0KA4ApzKL7QV6I -cCBS1WBLLXeVM9vOfrtzKVLWGe0qADyLm35p5Fnl3j+vimkk8h/2DEvCZ75c987m -O3PEgdkCgYEA0Scg+KINTA78sdZL5v2+8fT4b+EfoCgUqfr10ReUPKrz3HfrVHcj -7Vf00RT52TkfmkL3mIdLyBUzQ9vzPgweo1o4yKCKNCpR9G3ydNW+KI5jSYnq2efz -Gpe3wTt+8YoyCgm9eUxNWjfO9fipS91sSotY0PovkBohj9aezfcWp1sCgYEAy+3n -MIvW/9PoYxCvQ9fDGLvx3B4/uy0ZYPh7j5edDuaRzwFd2YXUysXhJVuqTp0KT2tv -dRPFRE9Oq5N8e5ITIUiKLQ5PIRNBZm8CiAof+XS1fIuU+MTDaTfXwyGQo0xSg8MB -ITnJulmUlkcTWEtGyBi9sIjor5ve8kqvyrdAKX8CgYA9ZUUSd0978jJPad6iEf6J -PCXpgaYs91cJhre+BzPmkzA+mZ0lEEwlkdo1vfiRwWj7eYkA50Zhl4eS9e/zWM9t -mEBu9GFdasbf/55amZvWf+W5YpjkGmiMd9jjCjn7YVvLAozyHGngf91q6vGXaYou -X7VUsvxfSqxrcs7vGwc1XQKBgB0qaD80MMqj5v+MGlTsndWCw8OEe/7sI04QG7Pc -rjS8Wyws+NwsXNOnW1z5cDEQGrJjHiyzaCot4YV+cXZG3P+MnV52RnDnjRn2VHla -YVpPC8nFOMgfdAcvWmdo/IOuXbrEf/vdhPFm8G5Ruf2NvpDNoQuHeSfsdgVXEy89 -6CpHAoGBAMZInYD0XjcnZNqiQnQdcIJN3CqDIU76Z45OOpcUrYrvTos2xhGLrRI5 -qrk5Od/sovJfse+oUIIbgsABieqtyfxM03iu8fvbahIY6Un1iw2KN9t+mcPrSZJK -jTXKf7XxZ1+yN9kvohdLc65ySyXFSm++glDq8WGrmnOtLUlr0oMm ------END RSA PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase01/ca.srl b/docs/multitenant/ords-based/usecase01/ca.srl deleted file mode 100644 index 7c9868bb..00000000 --- a/docs/multitenant/ords-based/usecase01/ca.srl +++ /dev/null @@ -1 +0,0 @@ -77D97AB4C4B6D5A9377B84B455D3E16348C6DE04 diff --git a/docs/multitenant/ords-based/usecase01/cdb_create.yaml b/docs/multitenant/ords-based/usecase01/cdb_create.yaml deleted file mode 100644 index 01fc0a18..00000000 --- a/docs/multitenant/ords-based/usecase01/cdb_create.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - diff --git a/docs/multitenant/ords-based/usecase01/cdb_secret.yaml b/docs/multitenant/ords-based/usecase01/cdb_secret.yaml deleted file mode 100644 index 567b90a4..00000000 --- a/docs/multitenant/ords-based/usecase01/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - ords_pwd: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - cdbadmin_user: ".....base64 encoded password...." - cdbadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." diff --git a/docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml deleted file mode 100644 index 3cc2c3dd..00000000 --- a/docs/multitenant/ords-based/usecase01/clone_pdb1_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml deleted file mode 100644 index 28a4eab6..00000000 --- a/docs/multitenant/ords-based/usecase01/clone_pdb2_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb4 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml deleted file mode 100644 index a5c3cf59..00000000 --- a/docs/multitenant/ords-based/usecase01/close_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml deleted file mode 100644 index 7fa15111..00000000 --- a/docs/multitenant/ords-based/usecase01/close_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml deleted file mode 100644 index fa7cf009..00000000 --- a/docs/multitenant/ords-based/usecase01/close_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: ""new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/create_ords_pod.yaml b/docs/multitenant/ords-based/usecase01/create_ords_pod.yaml deleted file mode 100644 index e39c4c56..00000000 --- a/docs/multitenant/ords-based/usecase01/create_ords_pod.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: _your_container_registry/ords-dboper:latest - ordsImagePullPolicy: "Always" - dbTnsurl : "T H I S I S J U S T A N E X A M P L E ....(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - replicas: 1 - deletePdbCascade: true - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" - cdbAdminUser: - secret: - secretName: "cdbusr" - key: "e_cdbusr.txt" - cdbAdminPwd: - secret: - secretName: "cdbpwd" - key: "e_cdbpwd.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml deleted file mode 100644 index 044d466b..00000000 --- a/docs/multitenant/ords-based/usecase01/create_pdb1_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml deleted file mode 100644 index eb36aaa2..00000000 --- a/docs/multitenant/ords-based/usecase01/create_pdb2_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml deleted file mode 100644 index b0816929..00000000 --- a/docs/multitenant/ords-based/usecase01/delete_pdb1_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml deleted file mode 100644 index d2ad95cc..00000000 --- a/docs/multitenant/ords-based/usecase01/delete_pdb2_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/extfile.txt b/docs/multitenant/ords-based/usecase01/extfile.txt deleted file mode 100644 index c51d22a3..00000000 --- a/docs/multitenant/ords-based/usecase01/extfile.txt +++ /dev/null @@ -1 +0,0 @@ -subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com diff --git a/docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log b/docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log deleted file mode 100644 index f35c66d8..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/BuildImage.log +++ /dev/null @@ -1,896 +0,0 @@ -/usr/bin/docker build -t oracle/ords-dboper:latest ../../../ords -Sending build context to Docker daemon 13.82kB -Step 1/12 : FROM container-registry.oracle.com/java/jdk:latest - ---> b8457e2f0b73 -Step 2/12 : ENV ORDS_HOME=/opt/oracle/ords/ RUN_FILE="runOrdsSSL.sh" ORDSVERSION=23.4.0-8 - ---> Using cache - ---> 3317a16cd6f8 -Step 3/12 : COPY $RUN_FILE $ORDS_HOME - ---> 7995edec33cc -Step 4/12 : RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && yum -y install java-11-openjdk-devel && yum -y install iproute && yum clean all - ---> Running in fe168b01f3ad -Oracle Linux 8 BaseOS Latest (x86_64) 91 MB/s | 79 MB 00:00 -Oracle Linux 8 Application Stream (x86_64) 69 MB/s | 62 MB 00:00 -Last metadata expiration check: 0:00:12 ago on Tue 20 Aug 2024 08:54:50 AM UTC. -Package yum-utils-4.0.21-23.0.1.el8.noarch is already installed. -Package tar-2:1.30-9.el8.x86_64 is already installed. -Package vim-minimal-2:8.0.1763-19.0.1.el8_6.4.x86_64 is already installed. -Package procps-ng-3.3.15-14.0.1.el8.x86_64 is already installed. -Package curl-7.61.1-33.el8_9.5.x86_64 is already installed. -Dependencies resolved. -================================================================================ - Package Arch Version Repository Size -================================================================================ -Installing: - bind-utils x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 453 k - expect x86_64 5.45.4-5.el8 ol8_baseos_latest 266 k - hostname x86_64 3.20-6.el8 ol8_baseos_latest 32 k - lsof x86_64 4.93.2-1.el8 ol8_baseos_latest 253 k - net-tools x86_64 2.0-0.52.20160912git.el8 ol8_baseos_latest 322 k - openssl x86_64 1:1.1.1k-12.el8_9 ol8_baseos_latest 710 k - sudo x86_64 1.9.5p2-1.el8_9 ol8_baseos_latest 1.0 M - tree x86_64 1.7.0-15.el8 ol8_baseos_latest 59 k - unzip x86_64 6.0-46.0.1.el8 ol8_baseos_latest 196 k - wget x86_64 1.19.5-12.0.1.el8_10 ol8_appstream 733 k - which x86_64 2.21-20.el8 ol8_baseos_latest 50 k - zip x86_64 3.0-23.el8 ol8_baseos_latest 270 k -Upgrading: - curl x86_64 7.61.1-34.el8 ol8_baseos_latest 352 k - dnf-plugins-core noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 76 k - libcurl x86_64 7.61.1-34.el8 ol8_baseos_latest 303 k - python3-dnf-plugins-core - noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 263 k - yum-utils noarch 4.0.21-25.0.1.el8 ol8_baseos_latest 75 k -Installing dependencies: - bind-libs x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 176 k - bind-libs-lite x86_64 32:9.11.36-16.el8_10.2 ol8_appstream 1.2 M - bind-license noarch 32:9.11.36-16.el8_10.2 ol8_appstream 104 k - fstrm x86_64 0.6.1-3.el8 ol8_appstream 29 k - libmaxminddb x86_64 1.2.0-10.el8_9.1 ol8_appstream 32 k - libmetalink x86_64 0.1.3-7.el8 ol8_baseos_latest 32 k - protobuf-c x86_64 1.3.0-8.el8 ol8_appstream 37 k - python3-bind noarch 32:9.11.36-16.el8_10.2 ol8_appstream 151 k - python3-ply noarch 3.9-9.el8 ol8_baseos_latest 111 k - tcl x86_64 1:8.6.8-2.el8 ol8_baseos_latest 1.1 M -Installing weak dependencies: - geolite2-city noarch 20180605-1.el8 ol8_appstream 19 M - geolite2-country noarch 20180605-1.el8 ol8_appstream 1.0 M - -Transaction Summary -================================================================================ -Install 24 Packages -Upgrade 5 Packages - -Total download size: 28 M -Downloading Packages: -(1/29): hostname-3.20-6.el8.x86_64.rpm 268 kB/s | 32 kB 00:00 -(2/29): libmetalink-0.1.3-7.el8.x86_64.rpm 257 kB/s | 32 kB 00:00 -(3/29): expect-5.45.4-5.el8.x86_64.rpm 1.4 MB/s | 266 kB 00:00 -(4/29): lsof-4.93.2-1.el8.x86_64.rpm 3.2 MB/s | 253 kB 00:00 -(5/29): net-tools-2.0-0.52.20160912git.el8.x86_ 3.6 MB/s | 322 kB 00:00 -(6/29): python3-ply-3.9-9.el8.noarch.rpm 2.7 MB/s | 111 kB 00:00 -(7/29): openssl-1.1.1k-12.el8_9.x86_64.rpm 10 MB/s | 710 kB 00:00 -(8/29): tree-1.7.0-15.el8.x86_64.rpm 2.2 MB/s | 59 kB 00:00 -(9/29): sudo-1.9.5p2-1.el8_9.x86_64.rpm 14 MB/s | 1.0 MB 00:00 -(10/29): unzip-6.0-46.0.1.el8.x86_64.rpm 6.8 MB/s | 196 kB 00:00 -(11/29): which-2.21-20.el8.x86_64.rpm 2.0 MB/s | 50 kB 00:00 -(12/29): tcl-8.6.8-2.el8.x86_64.rpm 13 MB/s | 1.1 MB 00:00 -(13/29): bind-libs-9.11.36-16.el8_10.2.x86_64.r 6.7 MB/s | 176 kB 00:00 -(14/29): zip-3.0-23.el8.x86_64.rpm 8.4 MB/s | 270 kB 00:00 -(15/29): bind-libs-lite-9.11.36-16.el8_10.2.x86 29 MB/s | 1.2 MB 00:00 -(16/29): bind-license-9.11.36-16.el8_10.2.noarc 3.3 MB/s | 104 kB 00:00 -(17/29): bind-utils-9.11.36-16.el8_10.2.x86_64. 13 MB/s | 453 kB 00:00 -(18/29): fstrm-0.6.1-3.el8.x86_64.rpm 1.2 MB/s | 29 kB 00:00 -(19/29): libmaxminddb-1.2.0-10.el8_9.1.x86_64.r 1.3 MB/s | 32 kB 00:00 -(20/29): geolite2-country-20180605-1.el8.noarch 17 MB/s | 1.0 MB 00:00 -(21/29): protobuf-c-1.3.0-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 -(22/29): python3-bind-9.11.36-16.el8_10.2.noarc 5.8 MB/s | 151 kB 00:00 -(23/29): wget-1.19.5-12.0.1.el8_10.x86_64.rpm 17 MB/s | 733 kB 00:00 -(24/29): curl-7.61.1-34.el8.x86_64.rpm 12 MB/s | 352 kB 00:00 -(25/29): dnf-plugins-core-4.0.21-25.0.1.el8.noa 2.4 MB/s | 76 kB 00:00 -(26/29): libcurl-7.61.1-34.el8.x86_64.rpm 8.6 MB/s | 303 kB 00:00 -(27/29): python3-dnf-plugins-core-4.0.21-25.0.1 9.8 MB/s | 263 kB 00:00 -(28/29): yum-utils-4.0.21-25.0.1.el8.noarch.rpm 3.0 MB/s | 75 kB 00:00 -(29/29): geolite2-city-20180605-1.el8.noarch.rp 66 MB/s | 19 MB 00:00 --------------------------------------------------------------------------------- -Total 43 MB/s | 28 MB 00:00 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Preparing : 1/1 - Running scriptlet: protobuf-c-1.3.0-8.el8.x86_64 1/1 - Installing : protobuf-c-1.3.0-8.el8.x86_64 1/34 - Installing : fstrm-0.6.1-3.el8.x86_64 2/34 - Installing : bind-license-32:9.11.36-16.el8_10.2.noarch 3/34 - Upgrading : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 4/34 - Upgrading : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 5/34 - Upgrading : libcurl-7.61.1-34.el8.x86_64 6/34 - Installing : geolite2-country-20180605-1.el8.noarch 7/34 - Installing : geolite2-city-20180605-1.el8.noarch 8/34 - Installing : libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 - Running scriptlet: libmaxminddb-1.2.0-10.el8_9.1.x86_64 9/34 - Installing : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 10/34 - Installing : bind-libs-32:9.11.36-16.el8_10.2.x86_64 11/34 - Installing : unzip-6.0-46.0.1.el8.x86_64 12/34 - Installing : tcl-1:8.6.8-2.el8.x86_64 13/34 - Running scriptlet: tcl-1:8.6.8-2.el8.x86_64 13/34 - Installing : python3-ply-3.9-9.el8.noarch 14/34 - Installing : python3-bind-32:9.11.36-16.el8_10.2.noarch 15/34 - Installing : libmetalink-0.1.3-7.el8.x86_64 16/34 - Installing : wget-1.19.5-12.0.1.el8_10.x86_64 17/34 - Running scriptlet: wget-1.19.5-12.0.1.el8_10.x86_64 17/34 - Installing : bind-utils-32:9.11.36-16.el8_10.2.x86_64 18/34 - Installing : expect-5.45.4-5.el8.x86_64 19/34 - Installing : zip-3.0-23.el8.x86_64 20/34 - Upgrading : curl-7.61.1-34.el8.x86_64 21/34 - Upgrading : yum-utils-4.0.21-25.0.1.el8.noarch 22/34 - Installing : which-2.21-20.el8.x86_64 23/34 - Installing : tree-1.7.0-15.el8.x86_64 24/34 - Installing : sudo-1.9.5p2-1.el8_9.x86_64 25/34 - Running scriptlet: sudo-1.9.5p2-1.el8_9.x86_64 25/34 - Installing : openssl-1:1.1.1k-12.el8_9.x86_64 26/34 - Installing : net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 - Running scriptlet: net-tools-2.0-0.52.20160912git.el8.x86_64 27/34 - Installing : lsof-4.93.2-1.el8.x86_64 28/34 - Installing : hostname-3.20-6.el8.x86_64 29/34 - Running scriptlet: hostname-3.20-6.el8.x86_64 29/34 - Cleanup : curl-7.61.1-33.el8_9.5.x86_64 30/34 - Cleanup : yum-utils-4.0.21-23.0.1.el8.noarch 31/34 - Cleanup : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 - Cleanup : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 33/34 - Cleanup : libcurl-7.61.1-33.el8_9.5.x86_64 34/34 - Running scriptlet: libcurl-7.61.1-33.el8_9.5.x86_64 34/34 - Verifying : expect-5.45.4-5.el8.x86_64 1/34 - Verifying : hostname-3.20-6.el8.x86_64 2/34 - Verifying : libmetalink-0.1.3-7.el8.x86_64 3/34 - Verifying : lsof-4.93.2-1.el8.x86_64 4/34 - Verifying : net-tools-2.0-0.52.20160912git.el8.x86_64 5/34 - Verifying : openssl-1:1.1.1k-12.el8_9.x86_64 6/34 - Verifying : python3-ply-3.9-9.el8.noarch 7/34 - Verifying : sudo-1.9.5p2-1.el8_9.x86_64 8/34 - Verifying : tcl-1:8.6.8-2.el8.x86_64 9/34 - Verifying : tree-1.7.0-15.el8.x86_64 10/34 - Verifying : unzip-6.0-46.0.1.el8.x86_64 11/34 - Verifying : which-2.21-20.el8.x86_64 12/34 - Verifying : zip-3.0-23.el8.x86_64 13/34 - Verifying : bind-libs-32:9.11.36-16.el8_10.2.x86_64 14/34 - Verifying : bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 15/34 - Verifying : bind-license-32:9.11.36-16.el8_10.2.noarch 16/34 - Verifying : bind-utils-32:9.11.36-16.el8_10.2.x86_64 17/34 - Verifying : fstrm-0.6.1-3.el8.x86_64 18/34 - Verifying : geolite2-city-20180605-1.el8.noarch 19/34 - Verifying : geolite2-country-20180605-1.el8.noarch 20/34 - Verifying : libmaxminddb-1.2.0-10.el8_9.1.x86_64 21/34 - Verifying : protobuf-c-1.3.0-8.el8.x86_64 22/34 - Verifying : python3-bind-32:9.11.36-16.el8_10.2.noarch 23/34 - Verifying : wget-1.19.5-12.0.1.el8_10.x86_64 24/34 - Verifying : curl-7.61.1-34.el8.x86_64 25/34 - Verifying : curl-7.61.1-33.el8_9.5.x86_64 26/34 - Verifying : dnf-plugins-core-4.0.21-25.0.1.el8.noarch 27/34 - Verifying : dnf-plugins-core-4.0.21-23.0.1.el8.noarch 28/34 - Verifying : libcurl-7.61.1-34.el8.x86_64 29/34 - Verifying : libcurl-7.61.1-33.el8_9.5.x86_64 30/34 - Verifying : python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch 31/34 - Verifying : python3-dnf-plugins-core-4.0.21-23.0.1.el8.noarch 32/34 - Verifying : yum-utils-4.0.21-25.0.1.el8.noarch 33/34 - Verifying : yum-utils-4.0.21-23.0.1.el8.noarch 34/34 - -Upgraded: - curl-7.61.1-34.el8.x86_64 - dnf-plugins-core-4.0.21-25.0.1.el8.noarch - libcurl-7.61.1-34.el8.x86_64 - python3-dnf-plugins-core-4.0.21-25.0.1.el8.noarch - yum-utils-4.0.21-25.0.1.el8.noarch -Installed: - bind-libs-32:9.11.36-16.el8_10.2.x86_64 - bind-libs-lite-32:9.11.36-16.el8_10.2.x86_64 - bind-license-32:9.11.36-16.el8_10.2.noarch - bind-utils-32:9.11.36-16.el8_10.2.x86_64 - expect-5.45.4-5.el8.x86_64 - fstrm-0.6.1-3.el8.x86_64 - geolite2-city-20180605-1.el8.noarch - geolite2-country-20180605-1.el8.noarch - hostname-3.20-6.el8.x86_64 - libmaxminddb-1.2.0-10.el8_9.1.x86_64 - libmetalink-0.1.3-7.el8.x86_64 - lsof-4.93.2-1.el8.x86_64 - net-tools-2.0-0.52.20160912git.el8.x86_64 - openssl-1:1.1.1k-12.el8_9.x86_64 - protobuf-c-1.3.0-8.el8.x86_64 - python3-bind-32:9.11.36-16.el8_10.2.noarch - python3-ply-3.9-9.el8.noarch - sudo-1.9.5p2-1.el8_9.x86_64 - tcl-1:8.6.8-2.el8.x86_64 - tree-1.7.0-15.el8.x86_64 - unzip-6.0-46.0.1.el8.x86_64 - wget-1.19.5-12.0.1.el8_10.x86_64 - which-2.21-20.el8.x86_64 - zip-3.0-23.el8.x86_64 - -Complete! -Adding repo from: http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 -created by dnf config-manager from http://yum.o 496 kB/s | 139 kB 00:00 -Last metadata expiration check: 0:00:01 ago on Tue 20 Aug 2024 08:55:14 AM UTC. -Dependencies resolved. -============================================================================================== - Package Arch Version Repository Size -============================================================================================== -Installing: - java-11-openjdk-devel x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 3.4 M -Installing dependencies: - adwaita-cursor-theme noarch 3.28.0-3.el8 ol8_appstream 647 k - adwaita-icon-theme noarch 3.28.0-3.el8 ol8_appstream 11 M - alsa-lib x86_64 1.2.10-2.el8 ol8_appstream 500 k - at-spi2-atk x86_64 2.26.2-1.el8 ol8_appstream 89 k - at-spi2-core x86_64 2.28.0-1.el8 ol8_appstream 169 k - atk x86_64 2.28.1-1.el8 ol8_appstream 272 k - avahi-libs x86_64 0.7-27.el8 ol8_baseos_latest 61 k - cairo x86_64 1.15.12-6.el8 ol8_appstream 719 k - cairo-gobject x86_64 1.15.12-6.el8 ol8_appstream 33 k - colord-libs x86_64 1.4.2-1.el8 ol8_appstream 236 k - copy-jdk-configs noarch 4.0-2.el8 ol8_appstream 30 k - cpio x86_64 2.12-11.el8 ol8_baseos_latest 266 k - crypto-policies-scripts noarch 20230731-1.git3177e06.el8 ol8_baseos_latest 84 k - cups-libs x86_64 1:2.2.6-60.el8_10 ol8_baseos_latest 435 k - dracut x86_64 049-233.git20240115.0.1.el8 ol8_baseos_latest 382 k - file x86_64 5.33-25.el8 ol8_baseos_latest 77 k - fribidi x86_64 1.0.4-9.el8 ol8_appstream 89 k - gdk-pixbuf2 x86_64 2.36.12-6.el8_10 ol8_baseos_latest 465 k - gdk-pixbuf2-modules x86_64 2.36.12-6.el8_10 ol8_appstream 108 k - gettext x86_64 0.19.8.1-17.el8 ol8_baseos_latest 1.1 M - gettext-libs x86_64 0.19.8.1-17.el8 ol8_baseos_latest 312 k - glib-networking x86_64 2.56.1-1.1.el8 ol8_baseos_latest 155 k - graphite2 x86_64 1.3.10-10.el8 ol8_appstream 122 k - grub2-common noarch 1:2.02-156.0.2.el8 ol8_baseos_latest 897 k - grub2-tools x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 2.0 M - grub2-tools-minimal x86_64 1:2.02-156.0.2.el8 ol8_baseos_latest 215 k - gsettings-desktop-schemas x86_64 3.32.0-6.el8 ol8_baseos_latest 633 k - gtk-update-icon-cache x86_64 3.22.30-11.el8 ol8_appstream 32 k - harfbuzz x86_64 1.7.5-4.el8 ol8_appstream 295 k - hicolor-icon-theme noarch 0.17-2.el8 ol8_appstream 48 k - jasper-libs x86_64 2.0.14-5.el8 ol8_appstream 167 k - java-11-openjdk x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 475 k - java-11-openjdk-headless x86_64 1:11.0.24.0.8-3.0.1.el8 ol8_appstream 42 M - javapackages-filesystem noarch 5.3.0-1.module+el8+5136+7ff78f74 ol8_appstream 30 k - jbigkit-libs x86_64 2.1-14.el8 ol8_appstream 55 k - json-glib x86_64 1.4.4-1.el8 ol8_baseos_latest 144 k - kbd-legacy noarch 2.0.4-11.el8 ol8_baseos_latest 481 k - kbd-misc noarch 2.0.4-11.el8 ol8_baseos_latest 1.5 M - lcms2 x86_64 2.9-2.el8 ol8_appstream 164 k - libX11 x86_64 1.6.8-8.el8 ol8_appstream 611 k - libX11-common noarch 1.6.8-8.el8 ol8_appstream 157 k - libXau x86_64 1.0.9-3.el8 ol8_appstream 37 k - libXcomposite x86_64 0.4.4-14.el8 ol8_appstream 28 k - libXcursor x86_64 1.1.15-3.el8 ol8_appstream 36 k - libXdamage x86_64 1.1.4-14.el8 ol8_appstream 27 k - libXext x86_64 1.3.4-1.el8 ol8_appstream 45 k - libXfixes x86_64 5.0.3-7.el8 ol8_appstream 25 k - libXft x86_64 2.3.3-1.el8 ol8_appstream 67 k - libXi x86_64 1.7.10-1.el8 ol8_appstream 49 k - libXinerama x86_64 1.1.4-1.el8 ol8_appstream 15 k - libXrandr x86_64 1.5.2-1.el8 ol8_appstream 34 k - libXrender x86_64 0.9.10-7.el8 ol8_appstream 33 k - libXtst x86_64 1.2.3-7.el8 ol8_appstream 22 k - libcroco x86_64 0.6.12-4.el8_2.1 ol8_baseos_latest 113 k - libdatrie x86_64 0.2.9-7.el8 ol8_appstream 33 k - libepoxy x86_64 1.5.8-1.el8 ol8_appstream 225 k - libfontenc x86_64 1.1.3-8.el8 ol8_appstream 37 k - libgomp x86_64 8.5.0-22.0.1.el8_10 ol8_baseos_latest 218 k - libgusb x86_64 0.3.0-1.el8 ol8_baseos_latest 49 k - libjpeg-turbo x86_64 1.5.3-12.el8 ol8_appstream 157 k - libkcapi x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 52 k - libkcapi-hmaccalc x86_64 1.4.0-2.0.1.el8 ol8_baseos_latest 31 k - libmodman x86_64 2.0.1-17.el8 ol8_baseos_latest 36 k - libpkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 35 k - libproxy x86_64 0.4.15-5.2.el8 ol8_baseos_latest 75 k - libsoup x86_64 2.62.3-5.el8 ol8_baseos_latest 424 k - libthai x86_64 0.1.27-2.el8 ol8_appstream 203 k - libtiff x86_64 4.0.9-32.el8_10 ol8_appstream 189 k - libwayland-client x86_64 1.21.0-1.el8 ol8_appstream 41 k - libwayland-cursor x86_64 1.21.0-1.el8 ol8_appstream 26 k - libwayland-egl x86_64 1.21.0-1.el8 ol8_appstream 19 k - libxcb x86_64 1.13.1-1.el8 ol8_appstream 231 k - libxkbcommon x86_64 0.9.1-1.el8 ol8_appstream 116 k - lksctp-tools x86_64 1.0.18-3.el8 ol8_baseos_latest 100 k - lua x86_64 5.3.4-12.el8 ol8_appstream 192 k - nspr x86_64 4.35.0-1.el8_8 ol8_appstream 143 k - nss x86_64 3.90.0-7.el8_10 ol8_appstream 750 k - nss-softokn x86_64 3.90.0-7.el8_10 ol8_appstream 1.2 M - nss-softokn-freebl x86_64 3.90.0-7.el8_10 ol8_appstream 375 k - nss-sysinit x86_64 3.90.0-7.el8_10 ol8_appstream 74 k - nss-util x86_64 3.90.0-7.el8_10 ol8_appstream 139 k - os-prober x86_64 1.74-9.0.1.el8 ol8_baseos_latest 51 k - pango x86_64 1.42.4-8.el8 ol8_appstream 297 k - pixman x86_64 0.38.4-4.el8 ol8_appstream 256 k - pkgconf x86_64 1.4.2-1.el8 ol8_baseos_latest 38 k - pkgconf-m4 noarch 1.4.2-1.el8 ol8_baseos_latest 17 k - pkgconf-pkg-config x86_64 1.4.2-1.el8 ol8_baseos_latest 15 k - rest x86_64 0.8.1-2.el8 ol8_appstream 70 k - shared-mime-info x86_64 1.9-4.el8 ol8_baseos_latest 328 k - systemd-udev x86_64 239-78.0.4.el8 ol8_baseos_latest 1.6 M - ttmkfdir x86_64 3.0.9-54.el8 ol8_appstream 62 k - tzdata-java noarch 2024a-1.0.1.el8 ol8_appstream 186 k - xkeyboard-config noarch 2.28-1.el8 ol8_appstream 782 k - xorg-x11-font-utils x86_64 1:7.5-41.el8 ol8_appstream 104 k - xorg-x11-fonts-Type1 noarch 7.5-19.el8 ol8_appstream 522 k - xz x86_64 5.2.4-4.el8_6 ol8_baseos_latest 153 k -Installing weak dependencies: - abattis-cantarell-fonts noarch 0.0.25-6.el8 ol8_appstream 155 k - dconf x86_64 0.28.0-4.0.1.el8 ol8_appstream 108 k - dejavu-sans-mono-fonts noarch 2.35-7.el8 ol8_baseos_latest 447 k - grubby x86_64 8.40-49.0.2.el8 ol8_baseos_latest 50 k - gtk3 x86_64 3.22.30-11.el8 ol8_appstream 4.5 M - hardlink x86_64 1:1.3-6.el8 ol8_baseos_latest 29 k - kbd x86_64 2.0.4-11.el8 ol8_baseos_latest 390 k - memstrack x86_64 0.2.5-2.el8 ol8_baseos_latest 51 k - pigz x86_64 2.4-4.el8 ol8_baseos_latest 80 k -Enabling module streams: - javapackages-runtime 201801 - -Transaction Summary -============================================================================================== -Install 106 Packages - -Total download size: 86 M -Installed size: 312 M -Downloading Packages: -(1/106): crypto-policies-scripts-20230731-1.git 862 kB/s | 84 kB 00:00 -(2/106): avahi-libs-0.7-27.el8.x86_64.rpm 602 kB/s | 61 kB 00:00 -(3/106): cpio-2.12-11.el8.x86_64.rpm 1.8 MB/s | 266 kB 00:00 -(4/106): cups-libs-2.2.6-60.el8_10.x86_64.rpm 5.7 MB/s | 435 kB 00:00 -(5/106): dejavu-sans-mono-fonts-2.35-7.el8.noar 5.1 MB/s | 447 kB 00:00 -(6/106): dracut-049-233.git20240115.0.1.el8.x86 7.0 MB/s | 382 kB 00:00 -(7/106): gdk-pixbuf2-2.36.12-6.el8_10.x86_64.rp 12 MB/s | 465 kB 00:00 -(8/106): gettext-libs-0.19.8.1-17.el8.x86_64.rp 9.3 MB/s | 312 kB 00:00 -(9/106): gettext-0.19.8.1-17.el8.x86_64.rpm 16 MB/s | 1.1 MB 00:00 -(10/106): glib-networking-2.56.1-1.1.el8.x86_64 6.0 MB/s | 155 kB 00:00 -(11/106): grub2-common-2.02-156.0.2.el8.noarch. 26 MB/s | 897 kB 00:00 -(12/106): grub2-tools-minimal-2.02-156.0.2.el8. 8.2 MB/s | 215 kB 00:00 -(13/106): grubby-8.40-49.0.2.el8.x86_64.rpm 2.1 MB/s | 50 kB 00:00 -(14/106): grub2-tools-2.02-156.0.2.el8.x86_64.r 26 MB/s | 2.0 MB 00:00 -(15/106): gsettings-desktop-schemas-3.32.0-6.el 19 MB/s | 633 kB 00:00 -(16/106): hardlink-1.3-6.el8.x86_64.rpm 1.1 MB/s | 29 kB 00:00 -(17/106): json-glib-1.4.4-1.el8.x86_64.rpm 5.9 MB/s | 144 kB 00:00 -(18/106): kbd-2.0.4-11.el8.x86_64.rpm 14 MB/s | 390 kB 00:00 -(19/106): kbd-legacy-2.0.4-11.el8.noarch.rpm 17 MB/s | 481 kB 00:00 -(20/106): kbd-misc-2.0.4-11.el8.noarch.rpm 41 MB/s | 1.5 MB 00:00 -(21/106): libcroco-0.6.12-4.el8_2.1.x86_64.rpm 4.7 MB/s | 113 kB 00:00 -(22/106): libgomp-8.5.0-22.0.1.el8_10.x86_64.rp 9.1 MB/s | 218 kB 00:00 -(23/106): libgusb-0.3.0-1.el8.x86_64.rpm 2.1 MB/s | 49 kB 00:00 -(24/106): libkcapi-1.4.0-2.0.1.el8.x86_64.rpm 1.6 MB/s | 52 kB 00:00 -(25/106): libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86 822 kB/s | 31 kB 00:00 -(26/106): libmodman-2.0.1-17.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 -(27/106): libpkgconf-1.4.2-1.el8.x86_64.rpm 1.2 MB/s | 35 kB 00:00 -(28/106): libproxy-0.4.15-5.2.el8.x86_64.rpm 3.0 MB/s | 75 kB 00:00 -(29/106): libsoup-2.62.3-5.el8.x86_64.rpm 15 MB/s | 424 kB 00:00 -(30/106): lksctp-tools-1.0.18-3.el8.x86_64.rpm 3.5 MB/s | 100 kB 00:00 -(31/106): memstrack-0.2.5-2.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 -(32/106): os-prober-1.74-9.0.1.el8.x86_64.rpm 2.2 MB/s | 51 kB 00:00 -(33/106): pigz-2.4-4.el8.x86_64.rpm 3.5 MB/s | 80 kB 00:00 -(34/106): pkgconf-1.4.2-1.el8.x86_64.rpm 1.7 MB/s | 38 kB 00:00 -(35/106): pkgconf-m4-1.4.2-1.el8.noarch.rpm 761 kB/s | 17 kB 00:00 -(36/106): pkgconf-pkg-config-1.4.2-1.el8.x86_64 691 kB/s | 15 kB 00:00 -(37/106): shared-mime-info-1.9-4.el8.x86_64.rpm 13 MB/s | 328 kB 00:00 -(38/106): systemd-udev-239-78.0.4.el8.x86_64.rp 32 MB/s | 1.6 MB 00:00 -(39/106): xz-5.2.4-4.el8_6.x86_64.rpm 5.2 MB/s | 153 kB 00:00 -(40/106): abattis-cantarell-fonts-0.0.25-6.el8. 6.4 MB/s | 155 kB 00:00 -(41/106): adwaita-cursor-theme-3.28.0-3.el8.noa 22 MB/s | 647 kB 00:00 -(42/106): alsa-lib-1.2.10-2.el8.x86_64.rpm 18 MB/s | 500 kB 00:00 -(43/106): at-spi2-atk-2.26.2-1.el8.x86_64.rpm 3.8 MB/s | 89 kB 00:00 -(44/106): at-spi2-core-2.28.0-1.el8.x86_64.rpm 6.9 MB/s | 169 kB 00:00 -(45/106): atk-2.28.1-1.el8.x86_64.rpm 9.2 MB/s | 272 kB 00:00 -(46/106): cairo-1.15.12-6.el8.x86_64.rpm 24 MB/s | 719 kB 00:00 -(47/106): adwaita-icon-theme-3.28.0-3.el8.noarc 65 MB/s | 11 MB 00:00 -(48/106): cairo-gobject-1.15.12-6.el8.x86_64.rp 914 kB/s | 33 kB 00:00 -(49/106): colord-libs-1.4.2-1.el8.x86_64.rpm 9.5 MB/s | 236 kB 00:00 -(50/106): copy-jdk-configs-4.0-2.el8.noarch.rpm 1.1 MB/s | 30 kB 00:00 -(51/106): dconf-0.28.0-4.0.1.el8.x86_64.rpm 4.4 MB/s | 108 kB 00:00 -(52/106): fribidi-1.0.4-9.el8.x86_64.rpm 3.9 MB/s | 89 kB 00:00 -(53/106): graphite2-1.3.10-10.el8.x86_64.rpm 5.1 MB/s | 122 kB 00:00 -(54/106): gdk-pixbuf2-modules-2.36.12-6.el8_10. 3.6 MB/s | 108 kB 00:00 -(55/106): gtk-update-icon-cache-3.22.30-11.el8. 1.4 MB/s | 32 kB 00:00 -(56/106): harfbuzz-1.7.5-4.el8.x86_64.rpm 11 MB/s | 295 kB 00:00 -(57/106): gtk3-3.22.30-11.el8.x86_64.rpm 68 MB/s | 4.5 MB 00:00 -(58/106): hicolor-icon-theme-0.17-2.el8.noarch. 2.1 MB/s | 48 kB 00:00 -(59/106): java-11-openjdk-11.0.24.0.8-3.0.1.el8 17 MB/s | 475 kB 00:00 -(60/106): jasper-libs-2.0.14-5.el8.x86_64.rpm 5.0 MB/s | 167 kB 00:00 -(61/106): java-11-openjdk-devel-11.0.24.0.8-3.0 61 MB/s | 3.4 MB 00:00 -(62/106): javapackages-filesystem-5.3.0-1.modul 1.2 MB/s | 30 kB 00:00 -(63/106): jbigkit-libs-2.1-14.el8.x86_64.rpm 2.1 MB/s | 55 kB 00:00 -(64/106): lcms2-2.9-2.el8.x86_64.rpm 3.8 MB/s | 164 kB 00:00 -(65/106): libX11-1.6.8-8.el8.x86_64.rpm 20 MB/s | 611 kB 00:00 -(66/106): libX11-common-1.6.8-8.el8.noarch.rpm 6.8 MB/s | 157 kB 00:00 -(67/106): libXau-1.0.9-3.el8.x86_64.rpm 1.6 MB/s | 37 kB 00:00 -(68/106): libXcomposite-0.4.4-14.el8.x86_64.rpm 1.3 MB/s | 28 kB 00:00 -(69/106): libXcursor-1.1.15-3.el8.x86_64.rpm 1.6 MB/s | 36 kB 00:00 -(70/106): libXdamage-1.1.4-14.el8.x86_64.rpm 1.2 MB/s | 27 kB 00:00 -(71/106): libXext-1.3.4-1.el8.x86_64.rpm 2.0 MB/s | 45 kB 00:00 -(72/106): libXfixes-5.0.3-7.el8.x86_64.rpm 1.1 MB/s | 25 kB 00:00 -(73/106): libXft-2.3.3-1.el8.x86_64.rpm 2.9 MB/s | 67 kB 00:00 -(74/106): libXi-1.7.10-1.el8.x86_64.rpm 2.2 MB/s | 49 kB 00:00 -(75/106): libXinerama-1.1.4-1.el8.x86_64.rpm 717 kB/s | 15 kB 00:00 -(76/106): libXrandr-1.5.2-1.el8.x86_64.rpm 1.5 MB/s | 34 kB 00:00 -(77/106): libXrender-0.9.10-7.el8.x86_64.rpm 1.4 MB/s | 33 kB 00:00 -(78/106): libXtst-1.2.3-7.el8.x86_64.rpm 957 kB/s | 22 kB 00:00 -(79/106): java-11-openjdk-headless-11.0.24.0.8- 71 MB/s | 42 MB 00:00 -(80/106): libdatrie-0.2.9-7.el8.x86_64.rpm 274 kB/s | 33 kB 00:00 -(81/106): libepoxy-1.5.8-1.el8.x86_64.rpm 9.1 MB/s | 225 kB 00:00 -(82/106): libfontenc-1.1.3-8.el8.x86_64.rpm 1.5 MB/s | 37 kB 00:00 -(83/106): libthai-0.1.27-2.el8.x86_64.rpm 8.2 MB/s | 203 kB 00:00 -(84/106): libjpeg-turbo-1.5.3-12.el8.x86_64.rpm 5.1 MB/s | 157 kB 00:00 -(85/106): libtiff-4.0.9-32.el8_10.x86_64.rpm 7.8 MB/s | 189 kB 00:00 -(86/106): libwayland-client-1.21.0-1.el8.x86_64 1.7 MB/s | 41 kB 00:00 -(87/106): libwayland-cursor-1.21.0-1.el8.x86_64 1.2 MB/s | 26 kB 00:00 -(88/106): libwayland-egl-1.21.0-1.el8.x86_64.rp 801 kB/s | 19 kB 00:00 -(89/106): libxcb-1.13.1-1.el8.x86_64.rpm 9.7 MB/s | 231 kB 00:00 -(90/106): libxkbcommon-0.9.1-1.el8.x86_64.rpm 5.0 MB/s | 116 kB 00:00 -(91/106): nspr-4.35.0-1.el8_8.x86_64.rpm 6.0 MB/s | 143 kB 00:00 -(92/106): lua-5.3.4-12.el8.x86_64.rpm 5.9 MB/s | 192 kB 00:00 -(93/106): nss-softokn-3.90.0-7.el8_10.x86_64.rp 38 MB/s | 1.2 MB 00:00 -(94/106): nss-3.90.0-7.el8_10.x86_64.rpm 17 MB/s | 750 kB 00:00 -(95/106): nss-softokn-freebl-3.90.0-7.el8_10.x8 14 MB/s | 375 kB 00:00 -(96/106): nss-sysinit-3.90.0-7.el8_10.x86_64.rp 3.2 MB/s | 74 kB 00:00 -(97/106): nss-util-3.90.0-7.el8_10.x86_64.rpm 5.8 MB/s | 139 kB 00:00 -(98/106): pango-1.42.4-8.el8.x86_64.rpm 11 MB/s | 297 kB 00:00 -(99/106): pixman-0.38.4-4.el8.x86_64.rpm 10 MB/s | 256 kB 00:00 -(100/106): rest-0.8.1-2.el8.x86_64.rpm 3.1 MB/s | 70 kB 00:00 -(101/106): ttmkfdir-3.0.9-54.el8.x86_64.rpm 2.5 MB/s | 62 kB 00:00 -(102/106): tzdata-java-2024a-1.0.1.el8.noarch.r 7.4 MB/s | 186 kB 00:00 -(103/106): xkeyboard-config-2.28-1.el8.noarch.r 27 MB/s | 782 kB 00:00 -(104/106): xorg-x11-font-utils-7.5-41.el8.x86_6 3.9 MB/s | 104 kB 00:00 -(105/106): xorg-x11-fonts-Type1-7.5-19.el8.noar 1.3 MB/s | 522 kB 00:00 -(106/106): file-5.33-25.el8.x86_64.rpm 26 kB/s | 77 kB 00:02 --------------------------------------------------------------------------------- -Total 27 MB/s | 86 MB 00:03 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 1/1 - Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86 1/1 - Preparing : 1/1 - Installing : nspr-4.35.0-1.el8_8.x86_64 1/106 - Running scriptlet: nspr-4.35.0-1.el8_8.x86_64 1/106 - Installing : nss-util-3.90.0-7.el8_10.x86_64 2/106 - Installing : libjpeg-turbo-1.5.3-12.el8.x86_64 3/106 - Installing : pixman-0.38.4-4.el8.x86_64 4/106 - Installing : libwayland-client-1.21.0-1.el8.x86_64 5/106 - Installing : atk-2.28.1-1.el8.x86_64 6/106 - Installing : libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 - Running scriptlet: libgomp-8.5.0-22.0.1.el8_10.x86_64 7/106 - Installing : libcroco-0.6.12-4.el8_2.1.x86_64 8/106 - Running scriptlet: libcroco-0.6.12-4.el8_2.1.x86_64 8/106 - Installing : grub2-common-1:2.02-156.0.2.el8.noarch 9/106 - Installing : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 - Installing : gettext-0.19.8.1-17.el8.x86_64 11/106 - Running scriptlet: gettext-0.19.8.1-17.el8.x86_64 11/106 - Installing : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 12/106 - Installing : libwayland-cursor-1.21.0-1.el8.x86_64 13/106 - Installing : jasper-libs-2.0.14-5.el8.x86_64 14/106 - Installing : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 15/106 - Installing : nss-softokn-3.90.0-7.el8_10.x86_64 16/106 - Installing : xkeyboard-config-2.28-1.el8.noarch 17/106 - Installing : libxkbcommon-0.9.1-1.el8.x86_64 18/106 - Installing : tzdata-java-2024a-1.0.1.el8.noarch 19/106 - Installing : ttmkfdir-3.0.9-54.el8.x86_64 20/106 - Installing : lua-5.3.4-12.el8.x86_64 21/106 - Installing : copy-jdk-configs-4.0-2.el8.noarch 22/106 - Installing : libwayland-egl-1.21.0-1.el8.x86_64 23/106 - Installing : libfontenc-1.1.3-8.el8.x86_64 24/106 - Installing : libepoxy-1.5.8-1.el8.x86_64 25/106 - Installing : libdatrie-0.2.9-7.el8.x86_64 26/106 - Running scriptlet: libdatrie-0.2.9-7.el8.x86_64 26/106 - Installing : libthai-0.1.27-2.el8.x86_64 27/106 - Running scriptlet: libthai-0.1.27-2.el8.x86_64 27/106 - Installing : libXau-1.0.9-3.el8.x86_64 28/106 - Installing : libxcb-1.13.1-1.el8.x86_64 29/106 - Installing : libX11-common-1.6.8-8.el8.noarch 30/106 - Installing : libX11-1.6.8-8.el8.x86_64 31/106 - Installing : libXext-1.3.4-1.el8.x86_64 32/106 - Installing : libXrender-0.9.10-7.el8.x86_64 33/106 - Installing : cairo-1.15.12-6.el8.x86_64 34/106 - Installing : libXi-1.7.10-1.el8.x86_64 35/106 - Installing : libXfixes-5.0.3-7.el8.x86_64 36/106 - Installing : libXtst-1.2.3-7.el8.x86_64 37/106 - Installing : libXcomposite-0.4.4-14.el8.x86_64 38/106 - Installing : at-spi2-core-2.28.0-1.el8.x86_64 39/106 - Running scriptlet: at-spi2-core-2.28.0-1.el8.x86_64 39/106 - Installing : at-spi2-atk-2.26.2-1.el8.x86_64 40/106 - Running scriptlet: at-spi2-atk-2.26.2-1.el8.x86_64 40/106 - Installing : libXcursor-1.1.15-3.el8.x86_64 41/106 - Installing : libXdamage-1.1.4-14.el8.x86_64 42/106 - Installing : cairo-gobject-1.15.12-6.el8.x86_64 43/106 - Installing : libXft-2.3.3-1.el8.x86_64 44/106 - Installing : libXrandr-1.5.2-1.el8.x86_64 45/106 - Installing : libXinerama-1.1.4-1.el8.x86_64 46/106 - Installing : lcms2-2.9-2.el8.x86_64 47/106 - Running scriptlet: lcms2-2.9-2.el8.x86_64 47/106 - Installing : jbigkit-libs-2.1-14.el8.x86_64 48/106 - Running scriptlet: jbigkit-libs-2.1-14.el8.x86_64 48/106 - Installing : libtiff-4.0.9-32.el8_10.x86_64 49/106 - Installing : javapackages-filesystem-5.3.0-1.module+el8+5136+ 50/106 - Installing : hicolor-icon-theme-0.17-2.el8.noarch 51/106 - Installing : graphite2-1.3.10-10.el8.x86_64 52/106 - Installing : harfbuzz-1.7.5-4.el8.x86_64 53/106 - Running scriptlet: harfbuzz-1.7.5-4.el8.x86_64 53/106 - Installing : fribidi-1.0.4-9.el8.x86_64 54/106 - Installing : pango-1.42.4-8.el8.x86_64 55/106 - Running scriptlet: pango-1.42.4-8.el8.x86_64 55/106 - Installing : dconf-0.28.0-4.0.1.el8.x86_64 56/106 - Installing : alsa-lib-1.2.10-2.el8.x86_64 57/106 - Running scriptlet: alsa-lib-1.2.10-2.el8.x86_64 57/106 - Installing : adwaita-cursor-theme-3.28.0-3.el8.noarch 58/106 - Installing : adwaita-icon-theme-3.28.0-3.el8.noarch 59/106 - Installing : abattis-cantarell-fonts-0.0.25-6.el8.noarch 60/106 - Installing : xz-5.2.4-4.el8_6.x86_64 61/106 - Installing : shared-mime-info-1.9-4.el8.x86_64 62/106 - Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 62/106 - Installing : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 - Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 63/106 - Installing : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 64/106 - Installing : gtk-update-icon-cache-3.22.30-11.el8.x86_64 65/106 - Installing : pkgconf-m4-1.4.2-1.el8.noarch 66/106 - Installing : pigz-2.4-4.el8.x86_64 67/106 - Installing : memstrack-0.2.5-2.el8.x86_64 68/106 - Installing : lksctp-tools-1.0.18-3.el8.x86_64 69/106 - Running scriptlet: lksctp-tools-1.0.18-3.el8.x86_64 69/106 - Installing : libpkgconf-1.4.2-1.el8.x86_64 70/106 - Installing : pkgconf-1.4.2-1.el8.x86_64 71/106 - Installing : pkgconf-pkg-config-1.4.2-1.el8.x86_64 72/106 - Installing : xorg-x11-font-utils-1:7.5-41.el8.x86_64 73/106 - Installing : xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 - Running scriptlet: xorg-x11-fonts-Type1-7.5-19.el8.noarch 74/106 - Installing : libmodman-2.0.1-17.el8.x86_64 75/106 - Running scriptlet: libmodman-2.0.1-17.el8.x86_64 75/106 - Installing : libproxy-0.4.15-5.2.el8.x86_64 76/106 - Running scriptlet: libproxy-0.4.15-5.2.el8.x86_64 76/106 - Installing : libkcapi-1.4.0-2.0.1.el8.x86_64 77/106 - Installing : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 78/106 - Installing : libgusb-0.3.0-1.el8.x86_64 79/106 - Installing : colord-libs-1.4.2-1.el8.x86_64 80/106 - Installing : kbd-misc-2.0.4-11.el8.noarch 81/106 - Installing : kbd-legacy-2.0.4-11.el8.noarch 82/106 - Installing : kbd-2.0.4-11.el8.x86_64 83/106 - Installing : systemd-udev-239-78.0.4.el8.x86_64 84/106 - Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 84/106 - Installing : os-prober-1.74-9.0.1.el8.x86_64 85/106 - Installing : json-glib-1.4.4-1.el8.x86_64 86/106 - Installing : hardlink-1:1.3-6.el8.x86_64 87/106 - Installing : file-5.33-25.el8.x86_64 88/106 - Installing : dejavu-sans-mono-fonts-2.35-7.el8.noarch 89/106 - Installing : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 90/106 - Installing : glib-networking-2.56.1-1.1.el8.x86_64 91/106 - Installing : libsoup-2.62.3-5.el8.x86_64 92/106 - Installing : rest-0.8.1-2.el8.x86_64 93/106 - Running scriptlet: rest-0.8.1-2.el8.x86_64 93/106 - Installing : cpio-2.12-11.el8.x86_64 94/106 - Installing : dracut-049-233.git20240115.0.1.el8.x86_64 95/106 - Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 - Installing : grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 - Running scriptlet: grub2-tools-1:2.02-156.0.2.el8.x86_64 96/106 - Installing : grubby-8.40-49.0.2.el8.x86_64 97/106 - Installing : crypto-policies-scripts-20230731-1.git3177e06.el 98/106 - Installing : nss-sysinit-3.90.0-7.el8_10.x86_64 99/106 - Installing : nss-3.90.0-7.el8_10.x86_64 100/106 - Installing : avahi-libs-0.7-27.el8.x86_64 101/106 - Installing : cups-libs-1:2.2.6-60.el8_10.x86_64 102/106 - Installing : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 - Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 103/106 - Installing : gtk3-3.22.30-11.el8.x86_64 104/106 - Installing : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 - Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 105/106 - Installing : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 - Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 - Running scriptlet: copy-jdk-configs-4.0-2.el8.noarch 106/106 - Running scriptlet: dconf-0.28.0-4.0.1.el8.x86_64 106/106 - Running scriptlet: crypto-policies-scripts-20230731-1.git3177e06.el 106/106 - Running scriptlet: nss-3.90.0-7.el8_10.x86_64 106/106 - Running scriptlet: java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 106/106 - Running scriptlet: java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 106/106 - Running scriptlet: java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 106/106 - Running scriptlet: hicolor-icon-theme-0.17-2.el8.noarch 106/106 - Running scriptlet: adwaita-icon-theme-3.28.0-3.el8.noarch 106/106 - Running scriptlet: shared-mime-info-1.9-4.el8.x86_64 106/106 - Running scriptlet: gdk-pixbuf2-2.36.12-6.el8_10.x86_64 106/106 - Running scriptlet: systemd-udev-239-78.0.4.el8.x86_64 106/106 - Verifying : avahi-libs-0.7-27.el8.x86_64 1/106 - Verifying : cpio-2.12-11.el8.x86_64 2/106 - Verifying : crypto-policies-scripts-20230731-1.git3177e06.el 3/106 - Verifying : cups-libs-1:2.2.6-60.el8_10.x86_64 4/106 - Verifying : dejavu-sans-mono-fonts-2.35-7.el8.noarch 5/106 - Verifying : dracut-049-233.git20240115.0.1.el8.x86_64 6/106 - Verifying : file-5.33-25.el8.x86_64 7/106 - Verifying : gdk-pixbuf2-2.36.12-6.el8_10.x86_64 8/106 - Verifying : gettext-0.19.8.1-17.el8.x86_64 9/106 - Verifying : gettext-libs-0.19.8.1-17.el8.x86_64 10/106 - Verifying : glib-networking-2.56.1-1.1.el8.x86_64 11/106 - Verifying : grub2-common-1:2.02-156.0.2.el8.noarch 12/106 - Verifying : grub2-tools-1:2.02-156.0.2.el8.x86_64 13/106 - Verifying : grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 14/106 - Verifying : grubby-8.40-49.0.2.el8.x86_64 15/106 - Verifying : gsettings-desktop-schemas-3.32.0-6.el8.x86_64 16/106 - Verifying : hardlink-1:1.3-6.el8.x86_64 17/106 - Verifying : json-glib-1.4.4-1.el8.x86_64 18/106 - Verifying : kbd-2.0.4-11.el8.x86_64 19/106 - Verifying : kbd-legacy-2.0.4-11.el8.noarch 20/106 - Verifying : kbd-misc-2.0.4-11.el8.noarch 21/106 - Verifying : libcroco-0.6.12-4.el8_2.1.x86_64 22/106 - Verifying : libgomp-8.5.0-22.0.1.el8_10.x86_64 23/106 - Verifying : libgusb-0.3.0-1.el8.x86_64 24/106 - Verifying : libkcapi-1.4.0-2.0.1.el8.x86_64 25/106 - Verifying : libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 26/106 - Verifying : libmodman-2.0.1-17.el8.x86_64 27/106 - Verifying : libpkgconf-1.4.2-1.el8.x86_64 28/106 - Verifying : libproxy-0.4.15-5.2.el8.x86_64 29/106 - Verifying : libsoup-2.62.3-5.el8.x86_64 30/106 - Verifying : lksctp-tools-1.0.18-3.el8.x86_64 31/106 - Verifying : memstrack-0.2.5-2.el8.x86_64 32/106 - Verifying : os-prober-1.74-9.0.1.el8.x86_64 33/106 - Verifying : pigz-2.4-4.el8.x86_64 34/106 - Verifying : pkgconf-1.4.2-1.el8.x86_64 35/106 - Verifying : pkgconf-m4-1.4.2-1.el8.noarch 36/106 - Verifying : pkgconf-pkg-config-1.4.2-1.el8.x86_64 37/106 - Verifying : shared-mime-info-1.9-4.el8.x86_64 38/106 - Verifying : systemd-udev-239-78.0.4.el8.x86_64 39/106 - Verifying : xz-5.2.4-4.el8_6.x86_64 40/106 - Verifying : abattis-cantarell-fonts-0.0.25-6.el8.noarch 41/106 - Verifying : adwaita-cursor-theme-3.28.0-3.el8.noarch 42/106 - Verifying : adwaita-icon-theme-3.28.0-3.el8.noarch 43/106 - Verifying : alsa-lib-1.2.10-2.el8.x86_64 44/106 - Verifying : at-spi2-atk-2.26.2-1.el8.x86_64 45/106 - Verifying : at-spi2-core-2.28.0-1.el8.x86_64 46/106 - Verifying : atk-2.28.1-1.el8.x86_64 47/106 - Verifying : cairo-1.15.12-6.el8.x86_64 48/106 - Verifying : cairo-gobject-1.15.12-6.el8.x86_64 49/106 - Verifying : colord-libs-1.4.2-1.el8.x86_64 50/106 - Verifying : copy-jdk-configs-4.0-2.el8.noarch 51/106 - Verifying : dconf-0.28.0-4.0.1.el8.x86_64 52/106 - Verifying : fribidi-1.0.4-9.el8.x86_64 53/106 - Verifying : gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 54/106 - Verifying : graphite2-1.3.10-10.el8.x86_64 55/106 - Verifying : gtk-update-icon-cache-3.22.30-11.el8.x86_64 56/106 - Verifying : gtk3-3.22.30-11.el8.x86_64 57/106 - Verifying : harfbuzz-1.7.5-4.el8.x86_64 58/106 - Verifying : hicolor-icon-theme-0.17-2.el8.noarch 59/106 - Verifying : jasper-libs-2.0.14-5.el8.x86_64 60/106 - Verifying : java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 61/106 - Verifying : java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x8 62/106 - Verifying : java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8 63/106 - Verifying : javapackages-filesystem-5.3.0-1.module+el8+5136+ 64/106 - Verifying : jbigkit-libs-2.1-14.el8.x86_64 65/106 - Verifying : lcms2-2.9-2.el8.x86_64 66/106 - Verifying : libX11-1.6.8-8.el8.x86_64 67/106 - Verifying : libX11-common-1.6.8-8.el8.noarch 68/106 - Verifying : libXau-1.0.9-3.el8.x86_64 69/106 - Verifying : libXcomposite-0.4.4-14.el8.x86_64 70/106 - Verifying : libXcursor-1.1.15-3.el8.x86_64 71/106 - Verifying : libXdamage-1.1.4-14.el8.x86_64 72/106 - Verifying : libXext-1.3.4-1.el8.x86_64 73/106 - Verifying : libXfixes-5.0.3-7.el8.x86_64 74/106 - Verifying : libXft-2.3.3-1.el8.x86_64 75/106 - Verifying : libXi-1.7.10-1.el8.x86_64 76/106 - Verifying : libXinerama-1.1.4-1.el8.x86_64 77/106 - Verifying : libXrandr-1.5.2-1.el8.x86_64 78/106 - Verifying : libXrender-0.9.10-7.el8.x86_64 79/106 - Verifying : libXtst-1.2.3-7.el8.x86_64 80/106 - Verifying : libdatrie-0.2.9-7.el8.x86_64 81/106 - Verifying : libepoxy-1.5.8-1.el8.x86_64 82/106 - Verifying : libfontenc-1.1.3-8.el8.x86_64 83/106 - Verifying : libjpeg-turbo-1.5.3-12.el8.x86_64 84/106 - Verifying : libthai-0.1.27-2.el8.x86_64 85/106 - Verifying : libtiff-4.0.9-32.el8_10.x86_64 86/106 - Verifying : libwayland-client-1.21.0-1.el8.x86_64 87/106 - Verifying : libwayland-cursor-1.21.0-1.el8.x86_64 88/106 - Verifying : libwayland-egl-1.21.0-1.el8.x86_64 89/106 - Verifying : libxcb-1.13.1-1.el8.x86_64 90/106 - Verifying : libxkbcommon-0.9.1-1.el8.x86_64 91/106 - Verifying : lua-5.3.4-12.el8.x86_64 92/106 - Verifying : nspr-4.35.0-1.el8_8.x86_64 93/106 - Verifying : nss-3.90.0-7.el8_10.x86_64 94/106 - Verifying : nss-softokn-3.90.0-7.el8_10.x86_64 95/106 - Verifying : nss-softokn-freebl-3.90.0-7.el8_10.x86_64 96/106 - Verifying : nss-sysinit-3.90.0-7.el8_10.x86_64 97/106 - Verifying : nss-util-3.90.0-7.el8_10.x86_64 98/106 - Verifying : pango-1.42.4-8.el8.x86_64 99/106 - Verifying : pixman-0.38.4-4.el8.x86_64 100/106 - Verifying : rest-0.8.1-2.el8.x86_64 101/106 - Verifying : ttmkfdir-3.0.9-54.el8.x86_64 102/106 - Verifying : tzdata-java-2024a-1.0.1.el8.noarch 103/106 - Verifying : xkeyboard-config-2.28-1.el8.noarch 104/106 - Verifying : xorg-x11-font-utils-1:7.5-41.el8.x86_64 105/106 - Verifying : xorg-x11-fonts-Type1-7.5-19.el8.noarch 106/106 - -Installed: - abattis-cantarell-fonts-0.0.25-6.el8.noarch - adwaita-cursor-theme-3.28.0-3.el8.noarch - adwaita-icon-theme-3.28.0-3.el8.noarch - alsa-lib-1.2.10-2.el8.x86_64 - at-spi2-atk-2.26.2-1.el8.x86_64 - at-spi2-core-2.28.0-1.el8.x86_64 - atk-2.28.1-1.el8.x86_64 - avahi-libs-0.7-27.el8.x86_64 - cairo-1.15.12-6.el8.x86_64 - cairo-gobject-1.15.12-6.el8.x86_64 - colord-libs-1.4.2-1.el8.x86_64 - copy-jdk-configs-4.0-2.el8.noarch - cpio-2.12-11.el8.x86_64 - crypto-policies-scripts-20230731-1.git3177e06.el8.noarch - cups-libs-1:2.2.6-60.el8_10.x86_64 - dconf-0.28.0-4.0.1.el8.x86_64 - dejavu-sans-mono-fonts-2.35-7.el8.noarch - dracut-049-233.git20240115.0.1.el8.x86_64 - file-5.33-25.el8.x86_64 - fribidi-1.0.4-9.el8.x86_64 - gdk-pixbuf2-2.36.12-6.el8_10.x86_64 - gdk-pixbuf2-modules-2.36.12-6.el8_10.x86_64 - gettext-0.19.8.1-17.el8.x86_64 - gettext-libs-0.19.8.1-17.el8.x86_64 - glib-networking-2.56.1-1.1.el8.x86_64 - graphite2-1.3.10-10.el8.x86_64 - grub2-common-1:2.02-156.0.2.el8.noarch - grub2-tools-1:2.02-156.0.2.el8.x86_64 - grub2-tools-minimal-1:2.02-156.0.2.el8.x86_64 - grubby-8.40-49.0.2.el8.x86_64 - gsettings-desktop-schemas-3.32.0-6.el8.x86_64 - gtk-update-icon-cache-3.22.30-11.el8.x86_64 - gtk3-3.22.30-11.el8.x86_64 - hardlink-1:1.3-6.el8.x86_64 - harfbuzz-1.7.5-4.el8.x86_64 - hicolor-icon-theme-0.17-2.el8.noarch - jasper-libs-2.0.14-5.el8.x86_64 - java-11-openjdk-1:11.0.24.0.8-3.0.1.el8.x86_64 - java-11-openjdk-devel-1:11.0.24.0.8-3.0.1.el8.x86_64 - java-11-openjdk-headless-1:11.0.24.0.8-3.0.1.el8.x86_64 - javapackages-filesystem-5.3.0-1.module+el8+5136+7ff78f74.noarch - jbigkit-libs-2.1-14.el8.x86_64 - json-glib-1.4.4-1.el8.x86_64 - kbd-2.0.4-11.el8.x86_64 - kbd-legacy-2.0.4-11.el8.noarch - kbd-misc-2.0.4-11.el8.noarch - lcms2-2.9-2.el8.x86_64 - libX11-1.6.8-8.el8.x86_64 - libX11-common-1.6.8-8.el8.noarch - libXau-1.0.9-3.el8.x86_64 - libXcomposite-0.4.4-14.el8.x86_64 - libXcursor-1.1.15-3.el8.x86_64 - libXdamage-1.1.4-14.el8.x86_64 - libXext-1.3.4-1.el8.x86_64 - libXfixes-5.0.3-7.el8.x86_64 - libXft-2.3.3-1.el8.x86_64 - libXi-1.7.10-1.el8.x86_64 - libXinerama-1.1.4-1.el8.x86_64 - libXrandr-1.5.2-1.el8.x86_64 - libXrender-0.9.10-7.el8.x86_64 - libXtst-1.2.3-7.el8.x86_64 - libcroco-0.6.12-4.el8_2.1.x86_64 - libdatrie-0.2.9-7.el8.x86_64 - libepoxy-1.5.8-1.el8.x86_64 - libfontenc-1.1.3-8.el8.x86_64 - libgomp-8.5.0-22.0.1.el8_10.x86_64 - libgusb-0.3.0-1.el8.x86_64 - libjpeg-turbo-1.5.3-12.el8.x86_64 - libkcapi-1.4.0-2.0.1.el8.x86_64 - libkcapi-hmaccalc-1.4.0-2.0.1.el8.x86_64 - libmodman-2.0.1-17.el8.x86_64 - libpkgconf-1.4.2-1.el8.x86_64 - libproxy-0.4.15-5.2.el8.x86_64 - libsoup-2.62.3-5.el8.x86_64 - libthai-0.1.27-2.el8.x86_64 - libtiff-4.0.9-32.el8_10.x86_64 - libwayland-client-1.21.0-1.el8.x86_64 - libwayland-cursor-1.21.0-1.el8.x86_64 - libwayland-egl-1.21.0-1.el8.x86_64 - libxcb-1.13.1-1.el8.x86_64 - libxkbcommon-0.9.1-1.el8.x86_64 - lksctp-tools-1.0.18-3.el8.x86_64 - lua-5.3.4-12.el8.x86_64 - memstrack-0.2.5-2.el8.x86_64 - nspr-4.35.0-1.el8_8.x86_64 - nss-3.90.0-7.el8_10.x86_64 - nss-softokn-3.90.0-7.el8_10.x86_64 - nss-softokn-freebl-3.90.0-7.el8_10.x86_64 - nss-sysinit-3.90.0-7.el8_10.x86_64 - nss-util-3.90.0-7.el8_10.x86_64 - os-prober-1.74-9.0.1.el8.x86_64 - pango-1.42.4-8.el8.x86_64 - pigz-2.4-4.el8.x86_64 - pixman-0.38.4-4.el8.x86_64 - pkgconf-1.4.2-1.el8.x86_64 - pkgconf-m4-1.4.2-1.el8.noarch - pkgconf-pkg-config-1.4.2-1.el8.x86_64 - rest-0.8.1-2.el8.x86_64 - shared-mime-info-1.9-4.el8.x86_64 - systemd-udev-239-78.0.4.el8.x86_64 - ttmkfdir-3.0.9-54.el8.x86_64 - tzdata-java-2024a-1.0.1.el8.noarch - xkeyboard-config-2.28-1.el8.noarch - xorg-x11-font-utils-1:7.5-41.el8.x86_64 - xorg-x11-fonts-Type1-7.5-19.el8.noarch - xz-5.2.4-4.el8_6.x86_64 - -Complete! -Last metadata expiration check: 0:00:23 ago on Tue 20 Aug 2024 08:55:14 AM UTC. -Package iproute-6.2.0-5.el8_9.x86_64 is already installed. -Dependencies resolved. -================================================================================ - Package Architecture Version Repository Size -================================================================================ -Upgrading: - iproute x86_64 6.2.0-6.el8_10 ol8_baseos_latest 853 k - -Transaction Summary -================================================================================ -Upgrade 1 Package - -Total download size: 853 k -Downloading Packages: -iproute-6.2.0-6.el8_10.x86_64.rpm 4.2 MB/s | 853 kB 00:00 --------------------------------------------------------------------------------- -Total 4.2 MB/s | 853 kB 00:00 -Running transaction check -Transaction check succeeded. -Running transaction test -Transaction test succeeded. -Running transaction - Preparing : 1/1 - Upgrading : iproute-6.2.0-6.el8_10.x86_64 1/2 - Cleanup : iproute-6.2.0-5.el8_9.x86_64 2/2 - Running scriptlet: iproute-6.2.0-5.el8_9.x86_64 2/2 - Verifying : iproute-6.2.0-6.el8_10.x86_64 1/2 - Verifying : iproute-6.2.0-5.el8_9.x86_64 2/2 - -Upgraded: - iproute-6.2.0-6.el8_10.x86_64 - -Complete! -24 files removed -Removing intermediate container fe168b01f3ad - ---> 791878694a50 -Step 5/12 : RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm - ---> Running in 59d7143da358 - % Total % Received % Xferd Average Speed Time Time Time Current - Dload Upload Total Spent Left Speed -100 108M 100 108M 0 0 1440k 0 0:01:16 0:01:16 --:--:-- 1578k -Removing intermediate container 59d7143da358 - ---> 17c4534293e5 -Step 6/12 : RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm - ---> Running in 84b1cbffdc51 -Verifying... ######################################## -Preparing... ######################################## -Updating / installing... -ords-23.4.0-8.el8 ######################################## -INFO: Before starting ORDS service, run the below command as user oracle: - ords --config /etc/ords/config install -Removing intermediate container 84b1cbffdc51 - ---> 6e7151b79588 -Step 7/12 : RUN mkdir -p $ORDS_HOME/doc_root && mkdir -p $ORDS_HOME/error && mkdir -p $ORDS_HOME/secrets && chmod ug+x $ORDS_HOME/*.sh && groupadd -g 54322 dba && usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && chown -R oracle:dba $ORDS_HOME && echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - ---> Running in 66e5db5f343f -Removing intermediate container 66e5db5f343f - ---> 0523dc897bf4 -Step 8/12 : USER oracle - ---> Running in ffda8495ac77 -Removing intermediate container ffda8495ac77 - ---> 162acd4d0b93 -Step 9/12 : WORKDIR /home/oracle - ---> Running in 8c14310ffbc7 -Removing intermediate container 8c14310ffbc7 - ---> c8dae809e772 -Step 10/12 : VOLUME ["$ORDS_HOME/config/ords"] - ---> Running in ed64548fd997 -Removing intermediate container ed64548fd997 - ---> 22e2c99247b0 -Step 11/12 : EXPOSE 8888 - ---> Running in 921f7c85d61d -Removing intermediate container 921f7c85d61d - ---> e5d503c92224 -Step 12/12 : CMD $ORDS_HOME/$RUN_FILE - ---> Running in cad487298d63 -Removing intermediate container cad487298d63 - ---> fdb17aa242f8 -Successfully built fdb17aa242f8 -Successfully tagged oracle/ords-dboper:latest -08:57:18 oracle@mitk01:# - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log b/docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log deleted file mode 100644 index b4602f54..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/cdb_creation.log +++ /dev/null @@ -1,357 +0,0 @@ -/usr/local/go/bin/kubectl logs -f `/usr/local/go/bin/kubectl get pods -n oracle-database-operator-system|grep ords|cut -d ' ' -f 1` -n oracle-database-operator-system -** Copyright (c) 2022 Oracle and/or its affiliates. -** -** The Universal Permissive License (UPL), Version 1.0 -** -** Subject to the condition set forth below, permission is hereby granted to any -** person obtaining a copy of this software, associated documentation and/or data -** (collectively the "Software"), free of charge and under any and all copyright -** rights in the Software, and any and all patent rights owned or freely -** licensable by each licensor hereunder covering either (i) the unmodified -** Software as contributed to or provided by such licensor, or (ii) the Larger -** Works (as defined below), to deal in both -** -** (a) the Software, and -** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -** one is included with the Software (each a "Larger Work" to which the Software -** is contributed by such licensors), -** -** without restriction, including without limitation the rights to copy, create -** derivative works of, display, perform, and distribute the Software and make, -** use, sell, offer for sale, import, export, have made, and have sold the -** Software and the Larger Work(s), and to sublicense the foregoing rights on -** either these or other terms. -** -** This license is subject to the following condition: -** The above copyright notice and either this complete permission notice or at -** a minimum a reference to the UPL must be included in all copies or -** substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -** SOFTWARE. -ORDSVERSIN:23.4.0-8 -NOT_INSTALLED=2 - SETUP -==================================================== -CONFIG=/etc/ords/config -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:16 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.connectionType was set to: customurl in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:18 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.customURL was set to: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:20 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: security.requestValidationFunction was set to: false in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:22 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.MaxLimit was set to: 100 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:24 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.InitialLimit was set to: 50 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:25 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: error.externalPath was set to: /opt/oracle/ords/error -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:27 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.access.log was set to: /home/oracle -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:29 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.port was set to: 8888 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:31 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert was set to: /opt/oracle/ords//secrets/tls.crt -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:33 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert.key was set to: /opt/oracle/ords//secrets/tls.key -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:35 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: restEnabledSql.active was set to: true in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:37 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: security.verifySSL was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:39 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.enabled was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:41 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: plsql.gateway.mode was set to: disabled in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:43 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.management.services.disabled was set to: false -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:45 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: misc.pagination.maxRows was set to: 1000 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:47 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser was set to: C##DBAPI_CDB_ADMIN AS SYSDBA in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:49 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser.password was set to: ****** in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:51 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Created user welcome in file /etc/ords/config/global/credentials -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:53 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Oracle REST Data Services - Non-Interactive Install - -Retrieving information... -Completed verifying Oracle REST Data Services schema version 23.4.0.r3461619. -Connecting to database user: ORDS_PUBLIC_USER url: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -The setting named: db.serviceNameSuffix was set to: in configuration: default -The setting named: db.username was set to: ORDS_PUBLIC_USER in configuration: default -The setting named: db.password was set to: ****** in configuration: default -The setting named: security.requestValidationFunction was set to: ords_util.authorize_plsql_gateway in configuration: default -2024-08-20T07:21:57.563Z INFO Oracle REST Data Services schema version 23.4.0.r3461619 is installed. -2024-08-20T07:21:57.565Z INFO To run in standalone mode, use the ords serve command: -2024-08-20T07:21:57.565Z INFO ords --config /etc/ords/config serve -2024-08-20T07:21:57.565Z INFO Visit the ORDS Documentation to access tutorials, developer guides and more to help you get started with the new ORDS Command Line Interface (http://oracle.com/rest). -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.4 Production on Tue Aug 20 07:21:59 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -2024-08-20T07:21:59.739Z INFO HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8080 -2024-08-20T07:21:59.741Z INFO HTTPS and HTTPS/2 listening on host: 0.0.0.0 port: 8888 -2024-08-20T07:21:59.765Z INFO Disabling document root because the specified folder does not exist: /etc/ords/config/global/doc_root -2024-08-20T07:21:59.765Z INFO Default forwarding from / to contextRoot configured. -2024-08-20T07:22:05.313Z INFO Configuration properties for: |default|lo| -db.serviceNameSuffix= -java.specification.version=22 -conf.use.wallet=true -database.api.management.services.disabled=false -sun.jnu.encoding=UTF-8 -user.region=US -java.class.path=/opt/oracle/ords/ords.war -java.vm.vendor=Oracle Corporation -standalone.https.cert.key=/opt/oracle/ords//secrets/tls.key -sun.arch.data.model=64 -nashorn.args=--no-deprecation-warning -java.vendor.url=https://java.oracle.com/ -resource.templates.enabled=false -user.timezone=UTC -java.vm.specification.version=22 -os.name=Linux -sun.java.launcher=SUN_STANDARD -user.country=US -sun.boot.library.path=/usr/java/jdk-22/lib -sun.java.command=/opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure -jdk.debug=release -sun.cpu.endian=little -user.home=/home/oracle -oracle.dbtools.launcher.executable.jar.path=/opt/oracle/ords/ords.war -user.language=en -db.cdb.adminUser.password=****** -java.specification.vendor=Oracle Corporation -java.version.date=2024-07-16 -database.api.enabled=true -java.home=/usr/java/jdk-22 -db.username=ORDS_PUBLIC_USER -file.separator=/ -java.vm.compressedOopsMode=32-bit -line.separator= - -restEnabledSql.active=true -java.specification.name=Java Platform API Specification -java.vm.specification.vendor=Oracle Corporation -java.awt.headless=true -standalone.https.cert=/opt/oracle/ords//secrets/tls.crt -db.password=****** -sun.management.compiler=HotSpot 64-Bit Tiered Compilers -security.requestValidationFunction=ords_util.authorize_plsql_gateway -misc.pagination.maxRows=1000 -java.runtime.version=22.0.2+9-70 -user.name=oracle -error.externalPath=/opt/oracle/ords/error -stdout.encoding=UTF-8 -path.separator=: -db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA -os.version=5.4.17-2136.329.3.1.el7uek.x86_64 -java.runtime.name=Java(TM) SE Runtime Environment -file.encoding=UTF-8 -plsql.gateway.mode=disabled -security.verifySSL=true -standalone.https.port=8888 -java.vm.name=Java HotSpot(TM) 64-Bit Server VM -java.vendor.url.bug=https://bugreport.java.com/bugreport/ -java.io.tmpdir=/tmp -oracle.dbtools.cmdline.ShellCommand=ords -java.version=22.0.2 -user.dir=/home/oracle -os.arch=amd64 -java.vm.specification.name=Java Virtual Machine Specification -jdbc.MaxLimit=100 -oracle.dbtools.cmdline.home=/opt/oracle/ords -native.encoding=UTF-8 -java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib -java.vendor=Oracle Corporation -java.vm.info=mixed mode, sharing -stderr.encoding=UTF-8 -java.vm.version=22.0.2+9-70 -sun.io.unicode.encoding=UnicodeLittle -jdbc.InitialLimit=50 -db.connectionType=customurl -java.class.version=66.0 -db.customURL=jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -standalone.access.log=/home/oracle - -2024-08-20T07:22:09.268Z INFO - -Mapped local pools from /etc/ords/config/databases: - /ords/ => default => VALID - - -2024-08-20T07:22:09.414Z INFO Oracle REST Data Services initialized -Oracle REST Data Services version : 23.4.0.r3461619 -Oracle REST Data Services server info: jetty/10.0.18 -Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 22.0.2+9-70 - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log b/docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log deleted file mode 100644 index e3915a21..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/openssl_execution.log +++ /dev/null @@ -1,19 +0,0 @@ -CREATING TLS CERTIFICATES -/usr/bin/openssl genrsa -out ca.key 2048 -Generating RSA private key, 2048 bit long modulus (2 primes) -......................+++++ -..................................................+++++ -e is 65537 (0x010001) -/usr/bin/openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost Root CA " -out ca.crt -/usr/bin/openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords.oracle-database-operator-system /CN=localhost" -out server.csr -Generating a RSA private key -...........+++++ -...........................................+++++ -writing new private key to 'tls.key' ------ -/usr/bin/echo "subjectAltName=DNS:cdb-dev-ords.oracle-database-operator-system,DNS:www.example.com" > extfile.txt -/usr/bin/openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -Signature ok -subject=C = US, ST = California, L = SanFrancisco, O = "oracle ", CN = "cdb-dev-ords.oracle-database-operator-system ", CN = localhost -Getting CA Private Key - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log b/docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log deleted file mode 100644 index b787b752..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/ordsconfig.log +++ /dev/null @@ -1,39 +0,0 @@ -ORDS: Release 23.4 Production on Tue Aug 20 07:48:44 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Database pool: default - -Setting Value Source ------------------------------------------ -------------------------------------------------- ----------- -database.api.enabled true Global -database.api.management.services.disabled false Global -db.cdb.adminUser C##DBAPI_CDB_ADMIN AS SYSDBA Pool -db.cdb.adminUser.password ****** Pool Wallet -db.connectionType customurl Pool -db.customURL jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90 Pool - )(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNEC - T_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL= - TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONL - Y))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST= - scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNEC - T_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -db.password ****** Pool Wallet -db.serviceNameSuffix Pool -db.username ORDS_PUBLIC_USER Pool -error.externalPath /opt/oracle/ords/error Global -jdbc.InitialLimit 50 Pool -jdbc.MaxLimit 100 Pool -misc.pagination.maxRows 1000 Pool -plsql.gateway.mode disabled Pool -restEnabledSql.active true Pool -security.requestValidationFunction ords_util.authorize_plsql_gateway Pool -security.verifySSL true Global -standalone.access.log /home/oracle Global -standalone.https.cert /opt/oracle/ords//secrets/tls.crt Global -standalone.https.cert.key /opt/oracle/ords//secrets/tls.key Global -standalone.https.port 8888 Global - diff --git a/docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log b/docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log deleted file mode 100644 index 232d5bb2..00000000 --- a/docs/multitenant/ords-based/usecase01/logfiles/tagandpush.log +++ /dev/null @@ -1,14 +0,0 @@ -/usr/bin/docker tag oracle/ords-dboper:latest [.......]/ords-dboper:latest - -/usr/bin/docker push [your container registry]/ords-dboper:latest -The push refers to repository [your container registry] -0405aac3af1c: Pushed -6be46e8e1e21: Pushed -c9884830a66d: Pushed -a46244557bb9: Pushing [===========================> ] 261.8MB/469.9MB -f988845e261e: Pushed -fe07ec0b1f5a: Layer already exists -2ac63de5f950: Layer already exists -386cd7a64c01: Layer already exists -826c69252b8b: Layer already exists - diff --git a/docs/multitenant/ords-based/usecase01/makefile b/docs/multitenant/ords-based/usecase01/makefile deleted file mode 100644 index ec454e28..00000000 --- a/docs/multitenant/ords-based/usecase01/makefile +++ /dev/null @@ -1,906 +0,0 @@ -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# | | | | ___| |_ __ ___ _ __ -# | |_| |/ _ \ | '_ \ / _ \ '__| -# | _ | __/ | |_) | __/ | -# |_| |_|\___|_| .__/ \___|_| -# |_| -# -# WARNING: Using this makefile helps you to customize yaml -# files. Edit parameters.txt with your enviroment -# informartion and execute the following steps -# -# 1) make operator -# it configures the operator yaml files with the -# watch namelist required by the multitenant controllers -# -# 2) make genyaml -# It automatically creates all the yaml files based on the -# information available in the parameters file -# -# 3) make secrets -# It configure the required secrets necessary to operate -# with pdbs multitenant controllers -# -# 4) make runall01 -# Start a series of operation create open close delete and so on -# -# LIST OF GENERAED YAML FILE -# -# ----------------------------- ---------------------------------- -# oracle-database-operator.yaml : oracle database operator -# cdbnamespace_binding.yaml : role binding for cdbnamespace -# pdbnamespace_binding.yaml : role binding for pdbnamespace -# create_cdb_secret.yaml : create secrets for ords server pod -# create_pdb_secret.yaml : create secrets for pluggable database -# create_ords_pod.yaml : create rest server pod -# create_pdb1_resource.yaml : create first pluggable database -# create_pdb2_resource.yaml : create second pluggable database -# open_pdb1_resource.yaml : open first pluggable database -# open_pdb2_resource.yaml : open second pluggable database -# close_pdb1_resource.yaml : close first pluggable database -# close_pdb2_resource.yaml : close second pluggable database -# clone_pdb_resource.yaml : clone thrid pluggable database -# clone_pdb2_resource.yaml : clone 4th pluggable database -# delete_pdb1_resource.yaml : delete first pluggable database -# delete_pdb2_resource.yaml : delete sencond pluggable database -# delete_pdb3_resource.yaml : delete thrid pluggable database -# unplug_pdb1_resource.yaml : unplug first pluggable database -# plug_pdb1_resource.yaml : plug first pluggable database -# map_pdb1_resource.yaml : map the first pluggable database -# config_map.yam : pdb parameters array -# -DATE := `date "+%y%m%d%H%M%S"` -###################### -# PARAMETER SECTIONS # -###################### - -export PARAMETERS=parameters.txt -export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) -export ORDPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDPWD|cut -d : -f 2) -export SYSPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep SYSPWD|cut -d : -f 2) -export WBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBUSER|cut -d : -f 2) -export WBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBPASS|cut -d : -f 2) -export PDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBUSR|cut -d : -f 2) -export PDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBPWD|cut -d : -f 2) -export CDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBUSR|cut -d : -f 2) -export CDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep CDBPWD|cut -d : -f 2) -export OPRNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPRNAMESPACE|cut -d : -f 2) -export OPRNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep OPRNAMESPACE|cut -d : -f 2) -export ORDSIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep ORDSIMG|cut -d : -f 2,3) -export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) -export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) -export OPRNAMESPACE=oracle-database-operator-system -export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml -export TEST_EXEC_TIMEOUT=3m -export IMAGE=oracle/ords-dboper:latest -export ORDSIMGDIR=../../../../ords - -REST_SERVER=ords -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -PRVKEY=ca.key -PUBKEY=public.pem -COMPANY=oracle -RUNTIME=/usr/bin/podman - -################# -### FILE LIST ### -################# - -export ORDS_POD=create_ords_pod.yaml - -export CDB_SECRETS=create_cdb_secrets.yaml -export PDB_SECRETS=create_pdb_secrets.yaml - -export PDBCRE1=create_pdb1_resource.yaml -export PDBCRE2=create_pdb2_resource.yaml - -export PDBCLOSE1=close_pdb1_resource.yaml -export PDBCLOSE2=close_pdb2_resource.yaml -export PDBCLOSE3=close_pdb3_resource.yaml - -export PDBOPEN1=open_pdb1_resource.yaml -export PDBOPEN2=open_pdb2_resource.yaml -export PDBOPEN3=open_pdb3_resource.yaml - -export PDBCLONE1=clone_pdb1_resource.yaml -export PDBCLONE2=clone_pdb2_resource.yaml - -export PDBDELETE1=delete_pdb1_resource.yaml -export PDBDELETE2=delete_pdb2_resource.yaml -export PDBDELETE3=delete_pdb3_resource.yaml - -export PDBUNPLUG1=unplug_pdb1_resource.yaml -export PDBPLUG1=plug_pdb1_resource.yaml - -export PDBMAP1=map_pdb1_resource.yaml -export PDBMAP2=map_pdb2_resource.yaml -export PDBMAP3=map_pdb3_resource.yaml - -export PDBMAP1=map_pdb1_resource.yaml -export PDBMAP2=map_pdb2_resource.yaml -export PDBMAP3=map_pdb3_resource.yaml - - -##BINARIES -export KUBECTL=/usr/bin/kubectl -OPENSSL=/usr/bin/openssl -ECHO=/usr/bin/echo -RM=/usr/bin/rm -CP=/usr/bin/cp -TAR=/usr/bin/tar -MKDIR=/usr/bin/mkdir -SED=/usr/bin/sed - -define msg -@printf "\033[31;7m%s\033[0m\r" "......................................]" -@printf "\033[31;7m[\xF0\x9F\x91\x89 %s\033[0m\n" $(1) -endef - -check: - $(call msg,"CHECK PARAMETERS") - @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) - @printf "ORDPWD.................:%s\n" $(ORDPWD) - @printf "SYSPWD.................:%s\n" $(SYSPWD) - @printf "WBUSER.................:%s\n" $(WBUSER) - @printf "WBPASS.................:%s\n" $(WBPASS) - @printf "PDBUSR.................:%s\n" $(PDBUSR) - @printf "PDBPWD.................:%s\n" $(PDBPWD) - @printf "CDBUSR.................:%s\n" $(CDBUSR) - @printf "CDBPWD.................:%s\n" $(CDBPWD) - @printf "OPRNAMESPACE...........:%s\n" $(OPRNAMESPACE) - @printf "COMPANY................:%s\n" $(COMPANY) - @printf "APIVERSION.............:%s\n" $(APIVERSION) - - -tlscrt: - $(call msg,"TLS GENERATION") - #$(OPENSSL) genrsa -out $(PRVKEY) 2048 - $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) - $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) \ - -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj \ - "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(OPRNAMESPACE)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(OPRNAMESPACE)" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - -tlssec: - $(call msg,"GENERATE TLS SECRET") - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPRNAMESPACE) - - -delsec: - $(call msg,"CLEAN OLD SECRETS") - $(eval SECRETSP:=$(shell kubectl get secrets -n $(OPRNAMESPACE) -o custom-columns=":metadata.name" --no-headers|grep -v webhook-server-cert) ) - @[ "${SECRETSP}" ] && ( \ - printf "Deleteing secrets in namespace -n $(OPRNAMESPACE)\n") &&\ - ($(KUBECTL) delete secret $(SECRETSP) -n $(OPRNAMESPACE))\ - || ( echo "No screts in namespace $(OPRNAMESPACE)") - - -###### ENCRYPTED SECRETS ###### -export PRVKEY=ca.key -export PUBKEY=public.pem -WBUSERFILE=wbuser.txt -WBPASSFILE=wbpass.txt -CDBUSRFILE=cdbusr.txt -CDBPWDFILE=cdbpwd.txt -SYSPWDFILE=syspwd.txt -ORDPWDFILE=ordpwd.txt -PDBUSRFILE=pdbusr.txt -PDBPWDFILE=pdbpwd.txt - - - -secrets: delsec tlscrt tlssec - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(OPRNAMESPACE) - @$(ECHO) $(WBUSER) > $(WBUSERFILE) - @$(ECHO) $(WBPASS) > $(WBPASSFILE) - @$(ECHO) $(CDBPWD) > $(CDBPWDFILE) - @$(ECHO) $(CDBUSR) > $(CDBUSRFILE) - @$(ECHO) $(SYSPWD) > $(SYSPWDFILE) - @$(ECHO) $(ORDPWD) > $(ORDPWDFILE) - @$(ECHO) $(PDBUSR) > $(PDBUSRFILE) - @$(ECHO) $(PDBPWD) > $(PDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBPWDFILE) |base64 > e_$(CDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(CDBUSRFILE) |base64 > e_$(CDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(SYSPWDFILE) |base64 > e_$(SYSPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(ORDPWDFILE) |base64 > e_$(ORDPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic cdbpwd --from-file=e_$(CDBPWDFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic cdbusr --from-file=e_$(CDBUSRFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic syspwd --from-file=e_$(SYSPWDFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic ordpwd --from-file=e_$(ORDPWDFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(OPRNAMESPACE) - $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(OPRNAMESPACE) - $(RM) $(WBUSERFILE) $(WBPASSFILE) $(CDBPWDFILE) $(CDBUSRFILE) $(SYSPWDFILE) $(ORDPWDFILE) $(PDBUSRFILE) $(PDBPWDFILE) - $(RM) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(CDBPWDFILE) e_$(CDBUSRFILE) e_$(SYSPWDFILE) e_$(ORDPWDFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) - - -### YAML FILE SECTION ### -operator: - $(CP) ${ORACLE_OPERATOR_YAML} . - ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG - $(SED) -i 's/value: ""/value: $(OPRNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` - - -define _script00 -cat < authsection01.yaml - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" - cdbAdminUser: - secret: - secretName: "cdbusr" - key: "e_cdbusr.txt" - cdbAdminPwd: - secret: - secretName: "cdbpwd" - key: "e_cdbpwd.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - -cat< authsection02.yaml - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - - -cat < ${OPRNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: ${OPRNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -cat < ${OPRNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: ${OPRNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -endef -export script00 = $(value _script00) -secyaml: - @ eval "$$script00" - -#echo ords pod creation -define _script01 -cat < ${ORDS_POD} -apiVersion: database.oracle.com/${APIVERSION} -kind: CDB -metadata: - name: cdb-dev - namespace: oracle-database-operator-system -spec: - cdbName: "DB12" - ordsImage: ${ORDSIMG} - ordsImagePullPolicy: "Always" - dbTnsurl : ${TNSALIAS} - replicas: 1 - deletePdbCascade: true -EOF - -cat authsection01.yaml >> ${ORDS_POD} - -endef -export script01 = $(value _script01) - - -define _script02 - -cat <${PDBCRE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat < ${PDBCRE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat <${PDBOPEN1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBOPEN2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBOPEN3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${PDBCLOSE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${PDBCLOSE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${PDBCLOSE3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: ""new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat < ${PDBCLONE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" -EOF - -cat < ${PDBCLONE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb4 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" -EOF - - -cat < ${PDBDELETE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${PDBDELETE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${PDBUNPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" -EOF - -cat <${PDBPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" -EOF - -cat <${PDBMAP1} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb1 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - -cat <${PDBMAP2} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb2 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - - -cat <${PDBMAP3} -apiVersion: database.oracle.com/${APIVERSION} -kind: PDB -metadata: - name: pdb3 - namespace: ${OPRNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${OPRNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - - -## Auth information -for _file in ${PDBCRE1} ${PDBCRE2} ${PDBOPEN1} ${PDBOPEN2} ${PDBOPEN3} ${PDBCLOSE1} ${PDBCLOSE2} ${PDBCLOSE3} ${PDBCLONE1} ${PDBCLONE2} ${PDBDELETE1} ${PDBDELETE2} ${PDBUNPLUG1} ${PDBPLUG1} ${PDBMAP1} ${PDBMAP2} ${PDBMAP3} -do -ls -ltr ${_file} - cat authsection02.yaml >> ${_file} -done -rm authsection02.yaml -rm authsection01.yaml -endef - -export script02 = $(value _script02) - -genyaml: secyaml - @ eval "$$script01" - @ eval "$$script02" - -cleanyaml: - - $(RM) $(PDBMAP3) $(PDBMAP2) $(PDBMAP1) $(PDBPLUG1) $(PDBUNPLUG1) $(PDBDELETE2) $(PDBDELETE1) $(PDBCLONE2) $(PDBCLONE1) $(PDBCLOSE3) $(PDBCLOSE2) $(PDBCLOSE1) $(PDBOPEN3) $(PDBOPEN2) $(PDBOPEN1) $(PDBCRE2) $(PDBCRE1) $(ORDS_POD) $(CDB_SECRETS) $(PDB_SECRETS) - - $(RM) ${OPRNAMESPACE}_binding.yaml ${OPRNAMESPACE}_binding.yaml - - -cleancrt: - - $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl - - -################# -### PACKAGING ### -################# - -pkg: - - $(RM) -rf /tmp/pkgtestplan - $(MKDIR) /tmp/pkgtestplan - $(CP) -R * /tmp/pkgtestplan - $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ - $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan - -################ -### diag ### -################ - -login: - $(KUBECTL) exec `$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep ords|cut -d ' ' -f 1` -n $(OPRNAMESPACE) -it -- /bin/bash - - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - -####################################################### -#### TEST SECTION #### -####################################################### - -run00: - @$(call msg,"cdb pod creation") - - $(KUBECTL) delete cdb cdb-dev -n $(OPRNAMESPACE) - $(KUBECTL) apply -f $(ORDS_POD) - time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" cdb cdb-dev -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"cdb pod completed") - $(KUBECTL) get cdb -n $(OPRNAMESPACE) - $(KUBECTL) get pod -n $(OPRNAMESPACE) - -run01.1: - @$(call msg,"pdb pdb1 creation") - $(KUBECTL) apply -f $(PDBCRE1) - time $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 creation completed") - $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) - -run01.2: - @$(call msg, "pdb pdb2 creation") - $(KUBECTL) apply -f $(PDBCRE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb2 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb2 creation completed") - $(KUBECTL) get pdb pdb2 -n $(OPRNAMESPACE) - -run02.1: - @$(call msg, "pdb pdb1 open") - $(KUBECTL) apply -f $(PDBOPEN1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 open completed") - $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) - -run02.2: - @$(call msg,"pdb pdb2 open") - $(KUBECTL) apply -f $(PDBOPEN2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" pdb pdb2 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb2 open completed") - $(KUBECTL) get pdb pdb2 -n $(OPRNAMESPACE) - - -run03.1: - @$(call msg,"clone pdb1-->pdb3") - $(KUBECTL) apply -f $(PDBCLONE1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb3 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb1-->pdb3 completed") - $(KUBECTL) get pdb pdb3 -n $(OPRNAMESPACE) - - -run03.2: - @$(call msg,"clone pdb2-->pdb4") - $(KUBECTL) apply -f $(PDBCLONE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb4 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb2-->pdb4 completed") - $(KUBECTL) get pdb pdb3 -n $(OPRNAMESPACE) - - -run04.1: - @$(call msg,"pdb pdb1 close") - $(KUBECTL) apply -f $(PDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 close completed") - $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) - -run04.2: - @$(call msg,"pdb pdb2 close") - $(KUBECTL) apply -f $(PDBCLOSE2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb2 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb2 close completed") - $(KUBECTL) get pdb pdb2 -n $(OPRNAMESPACE) - -run05.1: - @$(call msg,"pdb pdb1 unplug") - $(KUBECTL) apply -f $(PDBUNPLUG1) - $(KUBECTL) wait --for=delete pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb1 unplug completed") - -run06.1: - @$(call msg, "pdb pdb1 plug") - $(KUBECTL) apply -f $(PDBPLUG1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "pdb pdb1 plug completed") - $(KUBECTL) get pdb pdb1 -n $(OPRNAMESPACE) - -run07.1: - @$(call msg,"pdb pdb1 delete ") - - $(KUBECTL) apply -f $(PDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) apply -f $(PDBDELETE1) - $(KUBECTL) wait --for=delete pdb pdb1 -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"pdb pdb1 delete") - $(KUBECTL) get pdb -n $(OPRNAMESPACE) - -run99.1: - $(KUBECTL) delete cdb cdb-dev -n cdbnamespace - $(KUBECTL) wait --for=delete cdb cdb-dev -n $(OPRNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) get cdb -n cdbnamespaace - $(KUBECTL) get pdb -n pdbnamespaace - - -## SEQ | ACTION -## ----+---------------- -## 00 | create ords pod -## 01 | create pdb -## 02 | open pdb -## 03 | clone pdb -## 04 | close pdb -## 05 | unpug pdb -## 06 | plug pdb -## 07 | delete pdb (declarative) - - -runall01: run00 run01.1 run01.2 run03.1 run03.2 run04.1 run05.1 run06.1 run02.1 run07.1 - - -###### BUILD ORDS IMAGE ###### - -createimage: - $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) - -createimageproxy: - $(RUNTIME) build -t $(IMAGE) $(ORDSIMGDIR) --build-arg https_proxy=$(HTTPS_PROXY) --build-arg http_proxy=$(HTTP_PROXY) - -tagimage: - @echo "TAG IMAGE" - $(RUNTIME) tag $(IMAGE) $(ORDSIMG) - -push: - $(RUNTIME) push $(ORDSIMG) - - diff --git a/docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml deleted file mode 100644 index 18cb35b1..00000000 --- a/docs/multitenant/ords-based/usecase01/map_pdb1_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml deleted file mode 100644 index 85899597..00000000 --- a/docs/multitenant/ords-based/usecase01/map_pdb2_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbprd" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml deleted file mode 100644 index 9c2c1cd3..00000000 --- a/docs/multitenant/ords-based/usecase01/map_pdb3_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "new_clone" - assertivePdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml deleted file mode 100644 index 63a0a49c..00000000 --- a/docs/multitenant/ords-based/usecase01/open_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml b/docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml deleted file mode 100644 index 8c4eed0d..00000000 --- a/docs/multitenant/ords-based/usecase01/open_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb2 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml b/docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml deleted file mode 100644 index 5f0e4b77..00000000 --- a/docs/multitenant/ords-based/usecase01/open_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml b/docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml deleted file mode 100644 index 79e44269..00000000 --- a/docs/multitenant/ords-based/usecase01/oracle-database-operator-system_binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: oracle-database-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/multitenant/ords-based/usecase01/parameters.txt b/docs/multitenant/ords-based/usecase01/parameters.txt deleted file mode 100644 index 0a7b394a..00000000 --- a/docs/multitenant/ords-based/usecase01/parameters.txt +++ /dev/null @@ -1,61 +0,0 @@ - -######################## -## REST SERVER IMAGE ### -######################## - -ORDSIMG:_your_container_registry/ords-dboper:latest - -############################## -## TNS URL FOR CDB CREATION ## -############################## -TNSALIAS:"T H I S I S J U S T A N E X A M P L E ....(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - -########################################### -## ORDS PUBLIC USER ## -########################################### -ORDPWD:Change_me_please - -########################################### -## SYSPASSWORD ## -########################################### -SYSPWD:Change_me_please - -####################### -## HTTPS CREDENTIAL ### -####################### - -WBUSER:Change_me_please -WBPASS:Change_me_please - -##################### -## PDB ADMIN USER ### -##################### - -PDBUSR:Change_me_please -PDBPWD:Change_me_please - -##################### -## CDB ADMIN USER ### -##################### - -CDBUSR:C##DBAPI_CDB_ADMIN -CDBPWD:Change_me_please - -################### -### NAMESPACES #### -################### - -PDBNAMESPACE:pdbnamespace -CDBNAMESPACE:cdbnamespace - -#################### -### COMPANY NAME ### -#################### - -COMPANY:oracle - -#################### -### APIVERSION ### -#################### - -APIVERSION:v4 diff --git a/docs/multitenant/ords-based/usecase01/pdb_close.yaml b/docs/multitenant/ords-based/usecase01/pdb_close.yaml deleted file mode 100644 index 5917d33a..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_close.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - diff --git a/docs/multitenant/ords-based/usecase01/pdb_create.yaml b/docs/multitenant/ords-based/usecase01/pdb_create.yaml deleted file mode 100644 index be3581ad..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_create.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - assertivePdbDeletion: true - diff --git a/docs/multitenant/ords-based/usecase01/pdb_delete.yaml b/docs/multitenant/ords-based/usecase01/pdb_delete.yaml deleted file mode 100644 index c22b546a..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_delete.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - diff --git a/docs/multitenant/ords-based/usecase01/pdb_map.yaml b/docs/multitenant/ords-based/usecase01/pdb_map.yaml deleted file mode 100644 index 3300a7fa..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_map.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - assertivePdbDeletion: true diff --git a/docs/multitenant/ords-based/usecase01/pdb_open.yaml b/docs/multitenant/ords-based/usecase01/pdb_open.yaml deleted file mode 100644 index 25fdccc4..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_open.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" diff --git a/docs/multitenant/ords-based/usecase01/pdb_secret.yaml b/docs/multitenant/ords-based/usecase01/pdb_secret.yaml deleted file mode 100644 index 60d95d76..00000000 --- a/docs/multitenant/ords-based/usecase01/pdb_secret.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: oracle-database-operator-system -type: Opaque -data: - sysadmin_user: ".....base64 encoded password...." - sysadmin_pwd: ".....base64 encoded password...." - webserver_user: ".....base64 encoded password...." - webserver_pwd: ".....base64 encoded password...." - diff --git a/docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml deleted file mode 100644 index 0e86e10c..00000000 --- a/docs/multitenant/ords-based/usecase01/plug_pdb1_resource.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase01/server.csr b/docs/multitenant/ords-based/usecase01/server.csr deleted file mode 100644 index e308d301..00000000 --- a/docs/multitenant/ords-based/usecase01/server.csr +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIC3TCCAcUCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh -MRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNV -BAMMLWNkYi1kZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVt -IDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAm9nlNSQNsPTVqH57MkWKZEyaVtzVKQ8Z3oDK6hWXfB24p0jVj6sTOJkf -NVAxnqmU8DpW3odpbU6qWe/n+B5vJpqdXUGdsq9NKyus2fGb/xf1UnskpA2FUuWZ -o3upyCFxDAOvE4eZUzlxIn+54XXaNAdQiU9E8VXPr5YxrvZ15T/xCXLtJPs/RCOF -cJ8+gvZGcjMbdP16auJDVWZzBaur3eKbiHN7LXNCCRzGO++dv0kGY8vH7MyFfgp3 -qYBiSHS3WDiFUJjYIvfa8lLfP1hnlCyHn8TnU9gjGjmd1YcccSKqWIAT24wPUKVU -Lme4n91jxDPp7g8nRtDw0Smj9gYCtQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEB -AGOG/9IJJRvT2JLcuzE5Arai1XHc6Jh65iuDRqXQav47Bz38FFF2gZNO69gzDmhq -6k7tie+5bPcAHuuJZ0dAa71a9SLjKl+XNkkI0vS6te6OK3DCVUoMqNCk5VdwrJw0 -RORbKUwgLEG6mu80Gc/6wCdeR/36hoYTMeNPjm6M9e+X5ppsXqxCNsgDxasJFT82 -FejuJE2sZ6RCradlDToUHNS1dMLoW0WAIISqOmrDvEI6snm9ZZr3Sxo1auEtpI6v -NllBM4AgEghy/2mAtke+By4WHCfXBpxEGv9S7ATqJHYrR5Qa3nwx0eojWW1vmn0/ -aEzslX1tAH6oz2jA6QZ0sNo= ------END CERTIFICATE REQUEST----- diff --git a/docs/multitenant/ords-based/usecase01/tls.crt b/docs/multitenant/ords-based/usecase01/tls.crt deleted file mode 100644 index 6bf8aef4..00000000 --- a/docs/multitenant/ords-based/usecase01/tls.crt +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEFDCCAvygAwIBAgIUd9l6tMS21ak3e4S0VdPhY0jG3gQwDQYJKoZIhvcNAQEL -BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQH -DAxTYW5GcmFuY2lzY28xEDAOBgNVBAoMB29yYWNsZSAxNjA0BgNVBAMMLWNkYi1k -ZXYtb3Jkcy5vcmFjbGUtZGF0YWJhc2Utb3BlcmF0b3Itc3lzdGVtIDEcMBoGA1UE -AwwTbG9jYWxob3N0ICBSb290IENBIDAeFw0yNDA4MTIxNTMyMzVaFw0yNTA4MTIx -NTMyMzVaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMG -A1UEBwwMU2FuRnJhbmNpc2NvMRAwDgYDVQQKDAdvcmFjbGUgMTYwNAYDVQQDDC1j -ZGItZGV2LW9yZHMub3JhY2xlLWRhdGFiYXNlLW9wZXJhdG9yLXN5c3RlbSAxEjAQ -BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AJvZ5TUkDbD01ah+ezJFimRMmlbc1SkPGd6AyuoVl3wduKdI1Y+rEziZHzVQMZ6p -lPA6Vt6HaW1Oqlnv5/gebyaanV1BnbKvTSsrrNnxm/8X9VJ7JKQNhVLlmaN7qcgh -cQwDrxOHmVM5cSJ/ueF12jQHUIlPRPFVz6+WMa72deU/8Qly7ST7P0QjhXCfPoL2 -RnIzG3T9emriQ1VmcwWrq93im4hzey1zQgkcxjvvnb9JBmPLx+zMhX4Kd6mAYkh0 -t1g4hVCY2CL32vJS3z9YZ5Qsh5/E51PYIxo5ndWHHHEiqliAE9uMD1ClVC5nuJ/d -Y8Qz6e4PJ0bQ8NEpo/YGArUCAwEAAaNMMEowSAYDVR0RBEEwP4IsY2RiLWRldi1v -cmRzLm9yYWNsZS1kYXRhYmFzZS1vcGVyYXRvci1zeXN0ZW2CD3d3dy5leGFtcGxl -LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAh7Lsu2ITS6Bc2q/Ef4No5Us0Vo9BWKoL -AlrfQPjsv1erMGsyEEyZ0Cg8l3QrXlscQ1ESvx0BnRGjoqZGE4+PoVZTEYSkokXP -aAr69epPzXQRyyAGCg5GeL6IFAj1AzqJGNnKOrPaLpcTri4MboiWmW+MHmgLdyPK -iwl8bNa8841nK/L/m6QET15BI+MIAvn7pgcpztum5jmkB+eceXzXnKUGg77TaFiX -bXqVBR4EvexC4DgUfQJI4zJLFdcH/GHxCpaaXNjbXeVz1ZK/qo2TCrXp2UXVrznU -9VTUuCaQA2VYZCitvAbupt+1OvMFYhWiIAroJSmzrvH4oK+IXgY6GA== ------END CERTIFICATE----- diff --git a/docs/multitenant/ords-based/usecase01/tls.key b/docs/multitenant/ords-based/usecase01/tls.key deleted file mode 100644 index 666c5639..00000000 --- a/docs/multitenant/ords-based/usecase01/tls.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb2eU1JA2w9NWo -fnsyRYpkTJpW3NUpDxnegMrqFZd8HbinSNWPqxM4mR81UDGeqZTwOlbeh2ltTqpZ -7+f4Hm8mmp1dQZ2yr00rK6zZ8Zv/F/VSeySkDYVS5Zmje6nIIXEMA68Th5lTOXEi -f7nhddo0B1CJT0TxVc+vljGu9nXlP/EJcu0k+z9EI4Vwnz6C9kZyMxt0/Xpq4kNV -ZnMFq6vd4puIc3stc0IJHMY7752/SQZjy8fszIV+CnepgGJIdLdYOIVQmNgi99ry -Ut8/WGeULIefxOdT2CMaOZ3VhxxxIqpYgBPbjA9QpVQuZ7if3WPEM+nuDydG0PDR -KaP2BgK1AgMBAAECggEAKUwl1l0FW7yk2Q8a6glPUKCTzSybN1QPEMyj+D9ccsEV -aw57uKQmZbr9cA0d+OMK2lU7K6BKKXLM5SQTHcZCwcH6rPl0JiMZmbTrCp1hLslU -clS7MtV6XKsGeTGNncBuyjY3sD8gO9NezTt3L+0gsuS1TI06wZBxhh+QbsJUHzjW -bC3mNjD4SqXree4Snp05nlFaT2s2isIjj25mKDwBu8IX0BN2VjsaSiQcjb8Dmzmu -42Xh7bcWBebns8Ehuq9TIl6ZjQht+pmVOMlB862baVpW/9CxkknzM+UQhIkXTSJk -Jt/mGeO89V4/Zh2N4ixIOE1hw87EvRFBoYh2VF58QQKBgQDMujXYblh+eEdsB1LG -kY0LerFHuQgdzifYmjPl0jtBsWDmh5i6q9PRUs2JZ/Fsq4QMQ8SLinGzaIBq5FKr -CL067X5blrFA9H0D6exJI3iHBTQpeMFwtqvu3j+zpCmgzonaUDQrczUpc0hxU7YI -/jhDe9LSWknPrzzMoWWKuy0sTQKBgQDC4g8F2krqm9Q5ug8bRKTAvMrY0skFIwrP -5LXBq9C8YCnLnT4S4tYQfbnWaBeG7YpkkmkZe30c9MUjsr1OHZbo+jlxHBU+oRYZ -e1j0UorVGt7FfNe/zjW0fLd72CBO741EDvV6pVeItkAwH6P5/cbRu085dwvyFbxv -JmOaYddECQKBgQCuid6YG1NE10SE3CV89uAZtktny18ZEgY0ixrNx5MPaaskPtw9 -4Xofjol+qOhR7lQQpMHu+WQAQYqiFvBHspapo4pDiVCrAQWIDamNnTkHW69h3/qD -HqmsZzxF6iI3X351akVf+cOMCCXtwCGEvz+2gN12ytT8w/iAuOS6BuP3TQKBgBlf -v57+diSn13EQtajSPjVOH4ctorjFgEHjQHsP+OSeDLMTLSLeYArTo9+zu+R4hz1j -BsYnmvmrMQPd4OIL3jtFYTdF9coqxSraMZHWMXdfwUOrZpf1rG5skqNQV5yPejAz -Vmj6oDQPrrnVVM9W6I0kO0N7KZYCmH9MW0mdlZ6pAoGAB60f2sk35VUBpvh7qzTY -70WDbNnCCU3I3KZ7LCUwUPWzGLQwMXRlAb5ZMheT/SGPChX4QXCNUCjXkR3Am3NO -yURHqZIRy0bwZRVjYnlCtc9YQ8pB0isZ1z2a9FXRD75o2WboFZ+VsG0FU81IE2ZO -gW802gT76NRnz851B7/nFNs= ------END PRIVATE KEY----- diff --git a/docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml b/docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml deleted file mode 100644 index 61fe915d..00000000 --- a/docs/multitenant/ords-based/usecase01/unplug_pdb1_resource.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: oracle-database-operator-system - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "oracle-database-operator-system" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/pdb_clone.yaml b/docs/multitenant/ords-based/usecase02/pdb_clone.yaml deleted file mode 100644 index 5723f7c6..00000000 --- a/docs/multitenant/ords-based/usecase02/pdb_clone.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - assertivePdbDeletion: true - action: "Clone" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/pdb_plug.yaml b/docs/multitenant/ords-based/usecase02/pdb_plug.yaml deleted file mode 100644 index 9eb5ed77..00000000 --- a/docs/multitenant/ords-based/usecase02/pdb_plug.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertivePdbDeletion: true - action: "Plug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml b/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml deleted file mode 100644 index 0036d5f7..00000000 --- a/docs/multitenant/ords-based/usecase02/pdb_unplug.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - adminName: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminPwd: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - pdbOrdsPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/usecase03/Dockerfile b/docs/multitenant/usecase03/Dockerfile deleted file mode 100644 index 772a7e6d..00000000 --- a/docs/multitenant/usecase03/Dockerfile +++ /dev/null @@ -1,80 +0,0 @@ -## Copyright (c) 2022 Oracle and/or its affiliates. -## -## The Universal Permissive License (UPL), Version 1.0 -## -## Subject to the condition set forth below, permission is hereby granted to any -## person obtaining a copy of this software, associated documentation and/or data -## (collectively the "Software"), free of charge and under any and all copyright -## rights in the Software, and any and all patent rights owned or freely -## licensable by each licensor hereunder covering either (i) the unmodified -## Software as contributed to or provided by such licensor, or (ii) the Larger -## Works (as defined below), to deal in both -## -## (a) the Software, and -## (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -## one is included with the Software (each a "Larger Work" to which the Software -## is contributed by such licensors), -## -## without restriction, including without limitation the rights to copy, create -## derivative works of, display, perform, and distribute the Software and make, -## use, sell, offer for sale, import, export, have made, and have sold the -## Software and the Larger Work(s), and to sublicense the foregoing rights on -## either these or other terms. -## -## This license is subject to the following condition: -## The above copyright notice and either this complete permission notice or at -## a minimum a reference to the UPL must be included in all copies or -## substantial portions of the Software. -## -## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -## SOFTWARE. - -FROM container-registry.oracle.com/java/jdk:latest - -# Environment variables required for this build (do NOT change) -# ------------------------------------------------------------- -ENV ORDS_HOME=/opt/oracle/ords/ \ - RUN_FILE="runOrdsSSL.sh" \ - ORDSVERSION=23.4.0-8 - -# Copy binaries -# ------------- -COPY $RUN_FILE $ORDS_HOME - -RUN yum -y install yum-utils bind-utils tree hostname openssl net-tools zip unzip tar wget vim-minimal which sudo expect procps curl lsof && \ - yum-config-manager --add-repo=http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 && \ - yum -y install java-11-openjdk-devel && \ - yum -y install iproute && \ - yum clean all - -RUN curl -o /tmp/ords-$ORDSVERSION.el8.noarch.rpm https://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64/getPackage/ords-$ORDSVERSION.el8.noarch.rpm - -RUN rpm -ivh /tmp/ords-$ORDSVERSION.el8.noarch.rpm - -# Setup filesystem and oracle user -# -------------------------------- -RUN mkdir -p $ORDS_HOME/doc_root && \ - mkdir -p $ORDS_HOME/error && \ - mkdir -p $ORDS_HOME/secrets && \ - chmod ug+x $ORDS_HOME/*.sh && \ - groupadd -g 54322 dba && \ - usermod -u 54321 -d /home/oracle -g dba -m -s /bin/bash oracle && \ - chown -R oracle:dba $ORDS_HOME && \ - echo "oracle ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - -# Finalize setup -# ------------------- -USER oracle -WORKDIR /home/oracle - -VOLUME ["$ORDS_HOME/config/ords"] -EXPOSE 8888 - -# Define default command to start Ords Services -CMD $ORDS_HOME/$RUN_FILE - diff --git a/docs/multitenant/usecase03/NamespaceSegregation.png b/docs/multitenant/usecase03/NamespaceSegregation.png deleted file mode 100644 index bcb0ae77818e87ed64bf248bc0c173a4aac28c73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 270813 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≪L8Dt0B=E%&AwZ6lnqcZfYB@lfq%2F?;j@qePKUrSz=^3zHfSOS_@sG$;F& z;JoUSlpUfb?Ks&t3FhYsblS{#@6Dv?2*i=r(L)d;*}31m^%7>+C^!{*U%#~FZf?GP zybezb11yDt=q5Z*OOtl?!v?yj%}*jm#SL88p)^HDl^C;s?u>nvXJFi>BiQu5`gN8f%{~uC9}4R?Z+9EZD4Z6(q;_M#8gu)!CVH=X8m!6D5aCt$%dd=#!5-g{ zI21Lq9%VHbc}j0<4}Ck8Hx`Yq*^7$l5c$Im%j-{=Cd>k?c@xme1&=Tasp-}U(~4gP zRC?dYmUqY&i$(!8m@D8KH$_0RII;)uNAwGB6jQ}Ts78Ny;qmNrO#4JO>~A{(m9m>s zTPOTJPvFc z{@!0zD+A+wEMo2^NN$X2+y?cp9glMsfBV&4I4!rLaV8Q`w#h#aTWBSwj^r=v)f|Sj zzllkPV@LTWWL3TP@}TbV^>$VpYii>tTg)sk8?SmB&3HVyEi}11ziOJirK&(h?s>EB z$vGGtj>g+gVE|YJX9;7|zPBo_yN{qmbW%mY9>6tfCkyCO?=L1#! zDK`)=F=CoQUvCXE>5Hj%+FHE`5#F;A(FkI<={W?*xwgl@dZtL`GDSj%^Kh~M8Zur( z?%=1vN*C@C`Vqi9)In^BU>?Gh%`#+@Vr0N5X@#)2qc>r~AUbG2*>P#pI8vWjhvZ(b zw-vb20bXpIFT$UgmzzKu=l1R34xh|j5GbG6Ex#?PG+^;ZW7_XWze+bLcyp&8{hewModRarG6_ zN*XGE2wb;I7WGOFzbltF z;pTzcbq=Z6o|e=J;e~%@cTlSbf?$#yIuYd(34%-LK`C6|<11CGnK^Y~@H>R#>r@iJ zRCPDvKw{*QBMqeKyVE|w#AD8w_nWx}DSXRcUu@rhct#l!uO#~frC=7b)1cyILt|dZ za6xkSLm^~-VJqnGNBob_82L3J5&r~vziq5Y{fsZfId3SDf6pF7N?2eYEr0L35JjG=~n8E`>1%xubUS!za<2R`? zd|NBJ=me&T8~cu@5y$*3Hcn2QTzG*6x8N1v!7&Go59jD$^mIG;Sj?!2>;~WANwrt%@U50I{ z^@&2RE4O<`|B2#Wvy_<;RaRVZ@R^wYq7pyQXdR6I^DN=o-1q7SG<=LJI;Rs!j9&Hr zHbqEeDekZ^mHk=LKuF11@rpu6Bcz|3@*3vQh-!qc0}7^TyANfc-%x+8Be3&=z`_7@ znj#QcvhaExY8VXD57KP~m3%d|oEk!Q_SrJ#8v-c_E%K*0->)o)??d9tJp!=((Okx**y7EAk^#6u$%MXTK8-cPjH3X@I>}MZv$Q2@HP*ZL7b@Ct4CTx`)FT} zsy;~T%}E!@L>3_c5{3tyTh5~GUVl(e<(+$Un3hWG^_0)x6oPM9!7z~X8kzrWOnyL8 z#w*cXv?3TXQmX4jRFLPQrzCcudy+)c<9psyjJts$1f?*>s$LXVX6ge$+i2&YZenU0 z2LcZt7y%~-XNs9IS!fSQo#t@9^UC`K>J&)FI&>yXZy(ay!a8``vwQHBTR#|EFu6IL zl7jGcO{lIIxl1c{l4h29@1@|xbKoW>RXV+X4YF+zr+5b4`^<(LSz?N}6*l@C z_H!U(h<&&lii0aftSM5sG19=mjgq&SJYhrvihpKblM$_K=T%|S?U8%{y~hDV`0dc~ zWMTwlLY4!QYNVclg~+{`TDuu?Ku3VT_O0$ssP7{)7~d(ZpY2hs?<&oeXLoX=5&GAN7x|0TxznPv%Ill=46i* z3$#gi4|1|*iT&RayR|vE=7%Y)=knvLmXdyZPP{x%9H1OvY>~Yp8Ay=|^8E1ttHNlh znsYS9vcpJ(Ac^n`*~jd&cZqZPYlXprt!`U;^*YhG8KWp zcedjeA|$bL+>K}(W0u`l$G!}SyQ~K|iV}L&7&{h?WA$Q`f~+{Fbmyx2i5q5=R1XAE z9=40%kJfNQkyy5t?-ICI-ZvNeE++D&@19ZMox>edp8JtN^%5(va>y%4ZOUVfh>ly4 zQ*W4bk4X7oAGRNfAkN!~&nq&eG8D~(VVR6THXft$f1s0eEK33zswDnDLzOeP^V`l7 z#D9nj=YQ0E?s^Cr|M0C!156GupXp{aTH#=4dj-sm)SVnN0{l*xGXE_VJmm-@#^T<0R_4Y!@4Zq^c99Wi9db3g z6H^bYQuhzbpt`%*(!h#2$S#@mI(9OfKi{z&2Ns5PpWoE~rs3@eN8tFLWEQ?BP|o6g z;MSAe3(+XI8sE13U}38vAt@Wq&yBTg=Hw#Ck{1R#2IzJzZcK~MYd5!^zKvl@*A5xD zC|dO8)pmd)ms0;4d9o@1#A0hQiJqPbzU*?-$srw0<~!gMYM#u-pv*y62X(LwZymW; zEc!tmW;EvHu>MIDf*WXd_Y{AkYz`|{q!bj0H;QtBqg8S_m#4{`?E3}I!~2$R=%~== zy`XZbUGCdAfTq_TH3(>Eg7ww<)1^5ld+rrM=B(FY(r5S02^q)0N8MQ|$6WqZrNAVP zOorGZ(D!=eN%lbOOohS^NrtNho)<6mpe#(4y}7{#bmdni=Dqb@Yl=GFL5OUn(1esYP%L` z2Pm+5rU@*GijDOreJ|&0>>$lgK!j^no9<0#re;C?9nvJ;3B><%5ms$#4Z+St^_9+R zcii5v-c&y)0H@FS3FGUptA3%j0MW+*!4yg^@IQ4{<10{9WNmF-LzfGId?f1INDT_h zjg8i2OYhopc{ea4?JC^DG=8VSBLYB5K2%Kjy;a-6Pq6KwL~4x7C=6mhBX<1|EOxmN z88o9|FTZ<2psRGU)>4L?S25j&q^?meHi72)@f1Yr&oA^%IvUd#hR;Qqvw5a$oA-mK zM8uUibP>KdW(io>?* z31lt4LO2SC;UEy}Rkb)AT0kL~)t|A{Xsf$XSfU?y{O1(91CR!viwhgW7_^xB>oYFM zsc^CUU@B$_pR;PrK}0JJ0=+{=&2i(j!6PHd4-Bdt_qFQ${;4MXdX zvl;VHJU0}hzHzFlvyGt_7K}uIA!x?cTZs^4*{Ge4yTi8aMLXhIF^59+9i`PwkFdY?$pK@BeKia}ry8IB=c$v0pYx+=Cx%oeg` z2Dum-)#)E?0ukiI4#>O%nn!?h|x96B00NsX2tx7RE;I`?W5*|mZ#A!l0pJ!M%&MDVWh)P@|6WK;P zVqCXOdfU4cNt$Qst1)#{SGGI!$a?LT8dK*P9wwmj9=KO@pzCeaX(IqO1zBm_i(V3@ z7|m9&Gwe&) z7%xV7?tp7StD8Jl97p08mA`?z$J&9DZ-l#P809p@kR>nqWGl6b4<#}}nc4lrm{jh3 z_-CBpN_UGOx0YrY&*#0&yX2|8)>e3yQm_Pg z)!j!066RvF+Sx^SbGitCPP_nBnhv8ue`+0-of2qepP{a#++*)1F&bWi8*&wYDz$)j z*NjE_#9tb~0Pu914rUk5wV|boJk#5gy>svXLUCSeDlagNjLA>*Pmrv8p{2emFU=wQ z_AOa*jSEU4K-t3)t-6?ShOLDklO|8tet+MZ24<`j?Si`dP!%BG%V~D(p2YPuvu73f zHDooTMYUXxqHWLZ4CI`vKeCQq;o)-%+jp=p>qpN%{sORlqIonWa%pzRv8>^IRyjROv3-P-Anc~x*+ zPVlaYAj(&pBvv5U;m}pI#4V8*E9lcvi8SG%qtL#)=GUWy_)lzG6|b(H+CkH{Lrpbv zth7G%aO2@H%U_rn((~!blY444mtx;18Z7i^H$gV3N0S%s#1-28z&a&jW_oUKD@PUUr)K=RbgyADjUrFx4*5XCc|F69S+aG^DDfY zNb|jhn!87Bt4=Y?3Fz`vr-K78G-Kd^guh3N`tdu&X*2U(D3ndJRUA(h^__K!nO#2= zZ}O`_OD@a7vR_40H6qeDv+8kEflx*|cFNut$3c@wW1^{nJDF+di14nzcNlD2PiaR3F;x8HQir(U0DqsdQoH*yBNKJ?D0-wXW@61^Qn82@2D5h|L_?Y)-N(?$O0Qze2cRwe z^PL3@$bFG4!fY_wZgRnpO6Rj%k0REbQ7mRH7=pC5cEAm{=i+rg&WJ@eG&EhY#__Ut zd%lGE4+{-r+|DoCPj~^USB*S^B9(hTd>nVfZOKY<=9avB33uFG7=EaPDfSup#=0Pi z-7U3OZ_Z}IMNRnAN(8{f0;DL4@_mdDi!I3Bp|kes=hMi84RV_m@S?}4@L@ztrvY~4 z4h+-(8N7h}Fd1PX>BQ%nFr&&q`%7xXv|sGMUK>YVYt49W3$Lw2z{qoWKV(vcKeriV z5lkbW5#KO4j_G1h=d^iM=bvn;sM@&tkuKrz6RXLj(r0Z-F-=={myUV6B<~}KF8ZFH zAD5@!AJ$w9uKzq%h!iB}ABaB{1(5@?gKe)xtx$56=UK{!C3SrgVOv88uq}Jtyay~k z6WTzpa;Jc@C@I(ael|Dq1AtG4aTI^|0#+f=lG=YbbxF0Yl`>U6J{bmZ&n!SBp+>oATwnM)~! z=8;8EoeUZZX+E1rm|7sR8X%O|98w+FAEQX5B<)u~(*H7}r_5Ev@Tl>{>o4_h18=3Q zXgw3@EF9TuNY0DMJ4mpVk3u}b`)l;?%l!o|$kKUe6bwEo>c8hom&aguPv&pn2VdMd zvcJ(?94Vn|hP=36vA_`@34t2y@vDL)n`AqG8>Xn2`mw3?X+~*aWti9KcyRxcuJPu| z6>jN!uKPJ&M49y@B)#5!DcOH~EP8ElOVi@dFWUhVL`qoRWN}Ic=#ZHFxXNmFeMdSs*eV}fmWIH^g%#J~xjvhO9N!AXkRA>8Oca~b%=fN1!br#NXRRAo z%$5AW>0RgK41ua7q#eYn7la~-)B0hOa6eDg+Ce*T_4ASwZPPuf)h$keMXT-i;A;_p zZ2@r$T=OVdGm;h{j$|++Nj6un-+q`l^=fonUemw_+ZhOw2FtV)MyVw6ww|9z!0sbu zCE0O&(=b;r?~`YTvcYc*jHZ$a!=mk$B6M7ScwYOn82zV6MxKztaX!F5e<*pFyPsv4 zqAZSMnL-pn14A^4Mjp2VlWQnfZ{@1z(Gl2mr4E?c?(|J|wY2>TKDTW1Ye1QgXzCFT zPbviS3h1~7n_pP6jF=XV_R`@SNWde}^$b3u5lHJhlm#n5w;1J=CeM(fDKBag22=8n zg7s*1jg>=Ml?C`-WvheZ@9G4=tV5~@(o1hBxtni(+|s$+UMMmP$MTacug={_RoSx!J;vR`-ryUKoceD$FcCk{itOEU=cTyE#8?*pSu z*kOSL*3oo90v@vmkjzFud#>n1W0VR@@;y{)#*+OtxQat5)C*v{HR8!NKCnD|)bs4a zm8)i|fUr<_Lz#5uRYdR`pt!f;e$9L|L-dmI9<)k~rQ;4$yCo9X49_bzj zY^HiETKNf(0lN{)Lx+QNhWA~p|F;KbO2sPf@FaRik_TASkpqUZN9TMrvpPIsi3G2K~o_ti~ELh>I*mVP2{iWq+0Ec>}&ZKj+)eDZWROS7X7 z3HcDn{5;C4FL!JoK;wWw4hy{vm%^gy>DeWBKr{3u9{8iND=(0JkANud2jWF!uqUe` zHFdIAiL{D58o}NJbUry+naVDYZ5on&YcP&wsF5aw=09w!?Yy`dJmQz}pFVzpn%|Em zvgY?0xmd?MxfQvRm7j3@kFi6t>3)}ifblsLi@Ll7J9BY5xC4DyL*lA?*JN^2!Hka~b_|13C$`$P%8BIEwMs+gs}80QqJbVYI=O60E~V|3u%(Tbf3ddx5-LmeuS zn*NN6=}S%(-G$jTRH;Fw2R4HBSAqB!D5mUZ@SuyAD$1~N`o?>jiH<-*x?_`y`kVA* z?|WV3f#+}LC4PzYw7;)JFVjnQRg>U4mar2RlA0#I!=UalJUfhkjaL1!qC&J{61VFW z*sx-!@U>N^GHjy2$}fG*rmlR6t8!Ixub+gLGu(&8?j%u3oZFAm$H~n46^)bG#4X-i z#^no2Br=f+jCMqv%65%znXcN};-I-!G+Cj^BKuzT zLg5B1lGTy2RD$8u;l@dOd5WNZEC=NCf7Op`nMA_n!Sln|bWT&Pw0q&oJUJ3ZSrQ%g zM`ZT@pnM_XJtEA>Lvjpm)_Q6F{j-;1frrJx@@d`v!ad8r9#LY|*AQ#t;l3A{K9|SS zPzbr3USj5qr;Q<(Jaj+z4`F)+SmMe`FSBk6BZ+TOx*^A@%@@A5ekwl zA7|NBO@k!FS-_&?t(BkNLk~MDQoCLJlk!U;-oR9f@uFXCnKz z|$^$Q;SPGtO3CO=009^aLe2yXx>xBeTX)DovOG&E%Xbs_uD5B6r?TNX+Q%~c@` S*V+lde;zJA&et8oss9JAh>QII diff --git a/docs/multitenant/usecase03/README.md b/docs/multitenant/usecase03/README.md deleted file mode 100644 index c06368cd..00000000 --- a/docs/multitenant/usecase03/README.md +++ /dev/null @@ -1,268 +0,0 @@ - - - -# STEP BY STEP (NAMESPACE SEGREGATION) - -- [STEP BY STEP (NAMESPACE SEGREGATION)](#step-by-step-namespace-segregation) - - [INTRODUCTION](#introduction) - - [GIT CLONE ORACLE DATABASE OPERATOR PROJECT](#git-clone-oracle-database-operator-project) - - [NAMESPACE CREATION](#namespace-creation) - - [WEBHOOK CERTIFICATES](#webhook-certificates) - - [ORACLE DATABASE OPERATOR](#oracle-database-operator) - - [CREATE PDB AND CDB SECRETS](#create-pdb-and-cdb-secrets) - - [CREATE TLS CERTIFICATE](#create-tls-certificate) - - [REST SERVER IMAGE CREATION](#rest-server-image-creation) - - [CDB POD CREATION](#cdb-pod-creation) - - [PDB CREATION](#pdb-creation) - - [MAKEFILE](#makefile) - - -### INTRODUCTION - -> ☞ This folder contains the yaml files required to configure and manage cdb and pdb in different namespaces. The main change here is the possibility to specify the namespace where CDB will be created, this implies the introduction of new parameter at PDB level in order to specify the CDB namespace. - -Tasks performed in the usecase03 are the same ones of the other usecase01 with the exception that controller pods cdb pods and pdb crd are running in different namespaces. You must be aware of the fact that secrets must be created in the proper namespaces; cdb secrets go into cdb namespace , pdb secrets go into pdbnamespace while certificate secrets need to be created in every namespace. - - -| yaml file parameters | value | description /ords parameter | -|-------------- |--------------------------- |-------------------------------------------------| -| ☞ cdbNamespace | | Cdb namespace | -| dbserver | or | [--db-hostname][1] | -| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | -| port | | [--db-port][2] | -| cdbName | | Container Name | -| name | | Ords podname prefix in cdb.yaml | -| name | | pdb resource in pdb.yaml | -| ordsImage | /ords-dboper:latest|My public container registry | -| pdbName | | Pluggable database name | -| servicename | | [--db-servicename][3] | -| sysadmin_user | | [--admin-user][adminuser] | -| sysadmin_pwd | | [--password-stdin][pwdstdin] | -| cdbadmin_user | | [db.cdb.adminUser][1] | -| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | -| webserver_user| | [https user][http] NOT A DB USER | -| webserver_pwd | | [http user password][http] | -| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | -| pdbTlsKey | | [standalone.https.cert.key][key] | -| pdbTlsCrt | | [standalone.https.cert][cr] | -| pdbTlsCat | | certificate authority | -| xmlFileName | | path for the unplug and plug operation | -| srcPdbName | | name of the database to be cloned | -| fileNameConversions | | used for database cloning | -| tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | -| tdeExport | | [tdeExport] | -| tdeSecret | | [tdeSecret][tdeSecret] | -| tdePassword | | [tdeSecret][tdeSecret] | -| assertivePdbDeletion | boolean | [turn on imperative approach on crd deleteion][imperative] | - -![generla schema](./NamespaceSegregation.png) - -### GIT CLONE ORACLE DATABASE OPERATOR PROJECT - -```bash -git clone https://github.com/oracle/oracle-database-operator.git -cd oracle-database-operator/docs/multitenant/usecase03 -``` -### NAMESPACE CREATION - -We need first to create two different namespaces (**cdbnamespace**,**pdbnamespace**) using ns_pdb_namespace.yaml and ns_cdb_namespace.yaml - -```bash -kubectl apply -f ns_pdb_namespace.yaml -kubectl apply -f ns_cdb_namespace.yaml -``` - -### WEBHOOK CERTIFICATES -Create cert manager and verify the status - -```bash -kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -``` - -```bash -kubectl get pods --namespace cert-manager -NAME READY STATUS RESTARTS AGE -cert-manager-75997f4b44-4nf5c 1/1 Running 1 9d -cert-manager-cainjector-769785cd7b-mzfq5 1/1 Running 1 9d -cert-manager-webhook-6bc9944d78-tslrp 1/1 Running 1 9d -``` - -### ORACLE DATABASE OPERATOR - -Create the oracle database operator using oracle-database-operator.yaml -```bash -cd oracle-database-operator -kubectl apply -f oracle-database-operator.yaml -cd - -``` - -[operator creation log](operator_creation_log.txt) -### CREATE PDB AND CDB SECRETS - -Update secrets files with your base64 encodede password. - -```bash -echo ImAdemoPassword | base64 -SW1BZGVtb1Bhc3N3b3JkCg== -``` -Apply the cdb_secret and pdb_secret yaml file to generate credential information in each namespace. - -``` -kubectl apply -f cdb_secret.yaml -kubectl apply -f pdb_secret.yaml -``` -> ☞ Note that https credential needs to be replicated in any secret file. It is possible to improve configuration by creating a dedicated namespace for https credential in order to specify this information only once. - -Namespace segregation enables the capability of deploying and manages pluggable database without the cdb administrative passwords. - -### CREATE TLS CERTIFICATE - -Here follow an example of script shell that can be used to create secret certificates in each namespace involved in the kubernets multi tenant architecture - -```bash -#!/bin/bash -export CDB_NAMESPACE=cdbnamespace -export PDB_NAMESPACE=pdbnamespace -export OPR_NAMESPACE=oracle-database-operator-system -export SKEY=tls.key -export SCRT=tls.crt -export CART=ca.crt -export COMPANY=oracle -export REST_SERVER=ords - -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr -echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} - -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} -``` -after all secrets creation you shoud have the following pattern - -```bash -kubectl get secrets -n oracle-database-operator-system -NAME TYPE DATA AGE -db-ca Opaque 1 6d5h -db-tls kubernetes.io/tls 2 6d5h -webhook-server-cert kubernetes.io/tls 3 6d15h - - -kubectl get secrets -n cdbnamespace -NAME TYPE DATA AGE -cdb1-secret Opaque 6 6d15h -db-ca Opaque 1 6d6h -db-tls kubernetes.io/tls 2 6d6h - - -kubectl get secrets -n pdbnamespace -NAME TYPE DATA AGE -db-ca Opaque 1 6d6h -db-tls kubernetes.io/tls 2 6d6h -pdb1-secret Opaque 4 2d16h -tde1-secret Opaque 2 22h -``` -### REST SERVER IMAGE CREATION - -```bash -cd oracle-database-operator/ords -docker build -t oracle/ords-dboper:latest . -docker tag oracle/ords-dboper:latest [path_of_your_registry]/ords-dboper:latest -docker push [path_of_your_registry]/ords-dboper.latest -cd - -``` - -### CDB POD CREATION - -**note:** - Before creating the CDB pod make sure that all the pluggable databases in the container DB are open. - - - -Update the cdb_create.yaml with the path of the image generated before to create CDB pod - -```bash -kubectl apply -f cdb_create.yaml -``` - -Verify the status of the operation and cdb pod existence using the following commands - -```bash -## check the pod creation -kubectl get pods -n cdbnamespace - -## check the rest server log after pod creation -kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace - -##login to the pod for further debug and information gathering -kubectl exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash -``` - -[log cdb creation](./cdb_creation_log.txt) - -### PDB CREATION - -Apply the the pdb_create.yaml file to create a new pdb , after pdb creation you should be able to get pdb details using **kubectl get** command - -```bash -kubectl apply -f pdb_create.yaml -``` - -```bash -#!/bin/bash -#checkpdbs.sh -kubectl get pdbs -n pdbnamespace -o=jsonpath='{range .items[*]} -{"\n==================================================================\n"} -{"CDB="}{.metadata.labels.cdb} -{"K8SNAME="}{.metadata.name} -{"PDBNAME="}{.spec.pdbName} -{"OPENMODE="}{.status.openMode} -{"ACTION="}{.status.action} -{"MSG="}{.status.msg} -{"\n"}{end}' -``` - -```bash -./checkpdbs.sh -================================================================== -CDB=cdb-dev -K8SNAME=pdb1 -PDBNAME=pdbdev -OPENMODE=READ WRITE -ACTION=CREATE -MSG=Success - -``` -[pdb creation log](./pdb_creation_log.txt) - -### MAKEFILE - -In order to facilitate the command execution use the [makefile](./makefile) available target details are exposed in the following tables. - -|target |Action | -|-----------------------------|-------------------------------------| -|step1 | Build rest server images | -|step2 | Tag the immages | -|step3 | Push the image into the repository | -|step4 | Load webhook certmanager | -|step5 | Create the db operator | -|step6 | Create tls certificates | -|step7 | Create tls secret | -|step8 | Create database secrets | -|step9 | Create restserver pod | -|checkstep9 | Monitor the executions | -|step10 | Create pluggable database | -|checkpdb | Monitor PDB status | -|dump | Dump pods info into a file | -|reloadop | Reload the db operator | -|login | Login into cdb pod | - -[imperative]:https://kubernetes.io/docs/concepts/overview/working-with-objects/object-management/ - - - diff --git a/docs/multitenant/usecase03/cdb_create.yaml b/docs/multitenant/usecase03/cdb_create.yaml deleted file mode 100644 index d3b5e04f..00000000 --- a/docs/multitenant/usecase03/cdb_create.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: CDB -metadata: - name: cdb-dev - namespace: cdbnamespace -spec: - cdbName: "DB12" - ordsImage: ".............your registry............./ords-dboper:latest" - ordsImagePullPolicy: "Always" - dbTnsurl : "...Container tns alias....." - replicas: 1 - sysAdminPwd: - secret: - secretName: "cdb1-secret" - key: "sysadmin_pwd" - ordsPwd: - secret: - secretName: "cdb1-secret" - key: "ords_pwd" - cdbAdminUser: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_user" - cdbAdminPwd: - secret: - secretName: "cdb1-secret" - key: "cdbadmin_pwd" - webServerUser: - secret: - secretName: "cdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "cdb1-secret" - key: "webserver_pwd" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - diff --git a/docs/multitenant/usecase03/cdb_creation_log.txt b/docs/multitenant/usecase03/cdb_creation_log.txt deleted file mode 100644 index 8c7dc161..00000000 --- a/docs/multitenant/usecase03/cdb_creation_log.txt +++ /dev/null @@ -1,336 +0,0 @@ -kubectl get pods -n cdbnamespace -NAME READY STATUS RESTARTS AGE -cdb-dev-ords-rs-pgqqh 0/1 ContainerCreating 0 1s - -kubectl get pods -n cdbnamespace -NAME READY STATUS RESTARTS AGE -cdb-dev-ords-rs-pgqqh 1/1 Running 0 6s - -kubectl logs -f `/usr/bin/kubectl get pods -n cdbnamespace|grep ords|cut -d ' ' -f 1` -n cdbnamespace -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M -NOT_INSTALLED=2 - SETUP -==================================================== -CONFIG=/etc/ords/config -total 0 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:20 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.connectionType was set to: customurl in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:21 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.customURL was set to: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:23 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: security.requestValidationFunction was set to: false in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:25 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.MaxLimit was set to: 100 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:27 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: jdbc.InitialLimit was set to: 50 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:29 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: error.externalPath was set to: /opt/oracle/ords/error -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:31 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.access.log was set to: /home/oracle -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:32 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.port was set to: 8888 -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:34 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert was set to: /opt/oracle/ords//secrets/tls.crt -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:36 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: standalone.https.cert.key was set to: /opt/oracle/ords//secrets/tls.key -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:38 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: restEnabledSql.active was set to: true in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:40 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: security.verifySSL was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:42 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.enabled was set to: true -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:43 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: plsql.gateway.mode was set to: disabled in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:45 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The global setting named: database.api.management.services.disabled was set to: false -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:47 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: misc.pagination.maxRows was set to: 1000 in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:49 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser was set to: C##DBAPI_CDB_ADMIN AS SYSDBA in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:51 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -The setting named: db.cdb.adminUser.password was set to: ****** in configuration: default -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:53 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Created user welcome in file /etc/ords/config/global/credentials -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:17:55 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -Oracle REST Data Services - Non-Interactive Install - -Retrieving information.. -Completed verifying Oracle REST Data Services schema version 23.3.0.r2891830. -Connecting to database user: ORDS_PUBLIC_USER url: jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -The setting named: db.serviceNameSuffix was set to: in configuration: default -The setting named: db.username was set to: ORDS_PUBLIC_USER in configuration: default -The setting named: db.password was set to: ****** in configuration: default -The setting named: security.requestValidationFunction was set to: ords_util.authorize_plsql_gateway in configuration: default -2024-01-25T17:17:58.898Z INFO Oracle REST Data Services schema version 23.3.0.r2891830 is installed. -2024-01-25T17:17:58.900Z INFO To run in standalone mode, use the ords serve command: -2024-01-25T17:17:58.900Z INFO ords --config /etc/ords/config serve -2024-01-25T17:17:58.900Z INFO Visit the ORDS Documentation to access tutorials, developer guides and more to help you get started with the new ORDS Command Line Interface (http://oracle.com/rest). -Picked up _JAVA_OPTIONS: -Xms1126M -Xmx1126M - -ORDS: Release 23.3 Production on Thu Jan 25 17:18:00 2024 - -Copyright (c) 2010, 2024, Oracle. - -Configuration: - /etc/ords/config/ - -2024-01-25T17:18:00.960Z INFO HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8080 -2024-01-25T17:18:00.963Z INFO HTTPS and HTTPS/2 listening on host: 0.0.0.0 port: 8888 -2024-01-25T17:18:00.980Z INFO Disabling document root because the specified folder does not exist: /etc/ords/config/global/doc_root -2024-01-25T17:18:00.981Z INFO Default forwarding from / to contextRoot configured. -2024-01-25T17:18:06.634Z INFO Configuration properties for: |default|lo| -db.serviceNameSuffix= -java.specification.version=21 -conf.use.wallet=true -database.api.management.services.disabled=false -sun.jnu.encoding=UTF-8 -user.region=US -java.class.path=/opt/oracle/ords/ords.war -java.vm.vendor=Oracle Corporation -standalone.https.cert.key=/opt/oracle/ords//secrets/tls.key -sun.arch.data.model=64 -nashorn.args=--no-deprecation-warning -java.vendor.url=https://java.oracle.com/ -resource.templates.enabled=false -user.timezone=UTC -java.vm.specification.version=21 -os.name=Linux -sun.java.launcher=SUN_STANDARD -user.country=US -sun.boot.library.path=/usr/java/jdk-21/lib -sun.java.command=/opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure -jdk.debug=release -sun.cpu.endian=little -user.home=/home/oracle -oracle.dbtools.launcher.executable.jar.path=/opt/oracle/ords/ords.war -user.language=en -db.cdb.adminUser.password=****** -java.specification.vendor=Oracle Corporation -java.version.date=2023-10-17 -database.api.enabled=true -java.home=/usr/java/jdk-21 -db.username=ORDS_PUBLIC_USER -file.separator=/ -java.vm.compressedOopsMode=32-bit -line.separator= - -restEnabledSql.active=true -java.specification.name=Java Platform API Specification -java.vm.specification.vendor=Oracle Corporation -java.awt.headless=true -standalone.https.cert=/opt/oracle/ords//secrets/tls.crt -db.password=****** -sun.management.compiler=HotSpot 64-Bit Tiered Compilers -security.requestValidationFunction=ords_util.authorize_plsql_gateway -misc.pagination.maxRows=1000 -java.runtime.version=21.0.1+12-LTS-29 -user.name=oracle -error.externalPath=/opt/oracle/ords/error -stdout.encoding=UTF-8 -path.separator=: -db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA -os.version=5.4.17-2136.323.8.1.el7uek.x86_64 -java.runtime.name=Java(TM) SE Runtime Environment -file.encoding=UTF-8 -plsql.gateway.mode=disabled -security.verifySSL=true -standalone.https.port=8888 -java.vm.name=Java HotSpot(TM) 64-Bit Server VM -java.vendor.url.bug=https://bugreport.java.com/bugreport/ -java.io.tmpdir=/tmp -oracle.dbtools.cmdline.ShellCommand=ords -java.version=21.0.1 -user.dir=/home/oracle/keystore -os.arch=amd64 -java.vm.specification.name=Java Virtual Machine Specification -jdbc.MaxLimit=100 -oracle.dbtools.cmdline.home=/opt/oracle/ords -native.encoding=UTF-8 -java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib -java.vendor=Oracle Corporation -java.vm.info=mixed mode, sharing -stderr.encoding=UTF-8 -java.vm.version=21.0.1+12-LTS-29 -sun.io.unicode.encoding=UnicodeLittle -jdbc.InitialLimit=50 -db.connectionType=customurl -java.class.version=65.0 -db.customURL=jdbc:oracle:thin:@(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS))) -standalone.access.log=/home/oracle - -2024-01-25T17:18:10.381Z INFO - -Mapped local pools from /etc/ords/config/databases: - /ords/ => default => VALID - - -2024-01-25T17:18:10.532Z INFO Oracle REST Data Services initialized -Oracle REST Data Services version : 23.3.0.r2891830 -Oracle REST Data Services server info: jetty/10.0.17 -Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 21.0.1+12-LTS-29 - - -exec -it `kubectl get pods -n cdbnamespace |grep ords|cut -d ' ' -f 1` -n cdbnamespace bash -[oracle@cdb-dev-ords-rs-pgqqh ~]$ ps -ef|grep java -oracle 1147 1116 10 17:17 ? 00:00:21 /usr/java/jdk-21/bin/java -Doracle.dbtools.cmdline.home=/opt/oracle/ords -Duser.language=en -Duser.region=US -Dfile.encoding=UTF-8 -Djava.awt.headless=true -Dnashorn.args=--no-deprecation-warning -Doracle.dbtools.cmdline.ShellCommand=ords -Duser.timezone=UTC -jar /opt/oracle/ords/ords.war --config /etc/ords/config serve --port 8888 --secure -oracle 1227 1200 0 17:21 pts/0 00:00:00 grep --color=auto java diff --git a/docs/multitenant/usecase03/cdb_secret.yaml b/docs/multitenant/usecase03/cdb_secret.yaml deleted file mode 100644 index 8f1b6fc9..00000000 --- a/docs/multitenant/usecase03/cdb_secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: cdb1-secret - namespace: cdbnamespace -type: Opaque -data: - ords_pwd: "[...base64 encoded password...]" - sysadmin_pwd: "[...base64 encoded password...]" - cdbadmin_user: "[...base64 encoded password...]" - cdbadmin_pwd: "[...base64 encoded password...]" - webserver_user: "[...base64 encoded password...]" - webserver_pwd: "[...base64 encoded password...]" diff --git a/docs/multitenant/usecase03/gentlscert.sh b/docs/multitenant/usecase03/gentlscert.sh deleted file mode 100644 index 49e29147..00000000 --- a/docs/multitenant/usecase03/gentlscert.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -export CDB_NAMESPACE=cdbnamespace -export PDB_NAMESPACE=pdbnamespace -export OPR_NAMESPACE=oracle-database-operator-system -export SKEY=tls.key -export SCRT=tls.crt -export CART=ca.crt -export COMPANY=oracle -export REST_SERVER=ords - -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=${COMPANY} Root CA" -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout ${SKEY} -subj "/C=CN/ST=GD/L=SZ/O=${COMPANY}, Inc./CN=cdb-dev-${REST_SERVER}.${CDB_NAMESPACE}" -out server.csr -echo "subjectAltName=DNS:cdb-dev-${REST_SERVER}.${CDB_NAMESPACE},DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SCRT} - -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${CDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${CDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${PDB_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${PDB_NAMESPACE} -kubectl create secret tls db-tls --key="${SKEY}" --cert="${SCRT}" -n ${OPR_NAMESPACE} -kubectl create secret generic db-ca --from-file="${CART}" -n ${OPR_NAMESPACE} - diff --git a/docs/multitenant/usecase03/makefile b/docs/multitenant/usecase03/makefile deleted file mode 100644 index 7270a5e0..00000000 --- a/docs/multitenant/usecase03/makefile +++ /dev/null @@ -1,285 +0,0 @@ -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# -# ___ -# / _ \ _ __ _ __ _ __ ___ _ __ ___ -# | | | | '_ \| '_ \| '__/ _ \ '_ ` _ \ -# | |_| | | | | |_) | | | __/ | | | | | -# \___/|_| |_| .__/|_| \___|_| |_| |_| -# |_| -# ____ _ _ _ -# / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __ -# | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| -# | |__| (_) | | | | |_| | | (_) | | | __/ | -# \____\___/|_| |_|\__|_| \___/|_|_|\___|_| -# -# -# This makefile helps to speed up the kubectl commands executions to deploy and test -# the mutlitenant operator. Although it has few functionality you can adapt to your needs -# by adding much more targets. -# -# Quick start: -# ~~~~~~~~~~~ -# -# - Copy files of tab.1 in the makefile directory. -# - Edit the secret files and other yaml files with the correct credential as -# specified in the documentation. -# - Edit makefile updating variables of tab.2 -# - Execute commands of tab.3 "make step1" "make step2" "make step3".... -# -# Tab.1 - List of required files -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Opertaor yaml file | -# +-----------------------------+---------------------------------------------+ -# |cdb_secret.yaml | Secret file for the rest server pod | -# +-----------------------------+---------------------------------------------+ -# |pdb_secret.yaml | Secret file for the pdb creation | -# +-----------------------------+---------------------------------------------+ -# |cdb_create.yaml | Rest server pod creation | -# +-----------------------------+---------------------------------------------+ -# |pdb_create.yaml | Pluggable database creation | -# +-----------------------------+---------------------------------------------+ -# |oracle-database-operator.yaml| Database operator | -# +-----------------------------+---------------------------------------------+ -# |Dockerfiles | Dockerfile for CBD | -# +-----------------------------+---------------------------------------------+ -# |runOrdsSSL.sh | Init script executed by Dockerfile | -# +-----------------------------+---------------------------------------------+ -# -# Tab.2 - List of variables -# ~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# |OCIR | Your image registry | -# +-----------------------------+---------------------------------------------+ -# |OCIRPATH | Path of the image in your registry | -# +-----------------------------+---------------------------------------------+ -# -# Tab.3 - Execution steps -# ~~~~~~~~~~~~~~~~~~~~~~~ -# -# +-----------------------------+---------------------------------------------+ -# | MAKEFILE TARGETS LIST | -# | ----- ooo ----- | -# | - TARGET - - DESCRIPTION - | -# +-----------------------------+-------------------------------------+-------+ -# |step1 | Build rest server images | | -# +-----------------------------+-------------------------------------+ REST | -# |step2 | Tag the immages | SRV | -# +-----------------------------+-------------------------------------+ IMG | -# |step3 | Push the image into the repository | | -# +-----------------------------+-------------------------------------+-------+ -# |step4 | Load webhook certmanager | DB | -# +-----------------------------+-------------------------------------+ OPER | -# |step5 | Create the db operator | | -# +-----------------------------+-------------------------------------+-------+ -# |step6 | Create tls certificates | T | -# +-----------------------------+-------------------------------------+ L | -# |step7 | Create tls secret | S | -# +-----------------------------+---------------------------------------------+ -# |step8 | Create database secrets | -# +-----------------------------+---------------------------------------------+ -# |step9 | Create restserver pod | -# | | +---------------------------------------------+ -# | +---> checkstep9 | Monitor the executions | -# +-----------------------------+---------------------------------------------+ -# |step10 | Create pluggable database | -# | | +---------------------------------------------+ -# | +---> checkpdb | Monitor PDB status | -# +-----------------------------+---------------------------------------------+ -# | DIAGNOSTIC TARGETS | -# +-----------------------------+---------------------------------------------+ -# | dump | Dump pods info into a file | -# +-----------------------------+---------------------------------------------+ -# | reloadop | Reload the db operator | -# +-----------------------------+---------------------------------------------+ -# | login | Login into cdb pod | -# +-----------------------------+---------------------------------------------+ - - -################ TAB 2 VARIABLES ############ -REST_SERVER=ords -ORDSVERSION=latest - -OCIR=[container registry] -OCIRPATH=$(REST_SERVER)-dboper:$(ORDSVERSION) - -#examples: -#OCIR=lin.ocir.io -#OCIRPATH=/sampletenancy/samplepath/sampledir/$(REST_SERVER)-dboper:$(ORDSVERSION) -############################################# -DOCKER=/usr/bin/docker -KUBECTL=/usr/bin/kubectl -ORDS=/usr/local/bin/ords -CONFIG=/etc/ords/config -IMAGE=oracle/$(REST_SERVER)-dboper:$(ORDSVERSION) -DBOPERATOR=oracle-database-operator.yaml -URLPATH=/_/db-api/stable/database/pdbs/ -OPENSSL=/usr/bin/openssl -ORDSPORT=8888 -MAKE=/usr/bin/make -DOCKERFILE=../../../ords/Dockerfile -RUNSCRIPT=../../../ords/runOrdsSSL.sh -RM=/usr/bin/rm -CP=/bin/cp -ECHO=/usr/bin/echo -CERTMANAGER=https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml -CDB_SECRET_YAML=cdb_secret.yaml -PDB_SECRET_YAML=pdb_secret.yaml -TDE_SECRET_YAML=tde_secret.yaml -CDB_NAMESPACE_YAML=ns_namespace_cdb.yaml -PDB_NAMESPACE_YAML=ns_namespace_pdb.yaml -OPR_NAMESPACE=oracle-database-operator-system -PDB_NAMESPACE=$(shell grep namespace $(PDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') -CDB_NAMESPACE=$(shell grep namespace $(CDB_NAMESPACE_YAML) |cut -d: -f 2| tr -d ' ') -CDB=cdb_create.yaml -PDB=pdb_create.yaml -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -COMPANY=oracle -LOCALHOST=localhost -RESTPREFIX=cdb-dev - - -step1: createimage -step2: tagimage -step3: push -step4: certmanager -step5: dboperator -step6: tlscert -step7: tlssecret -step8: dbsecret -step9: cdb -step10: pdb - -checkstep9: checkcdb - - -createimage: - @echo "BUILDING CDB IMAGES" - $(CP) $(DOCKERFILE) . - $(CP) $(RUNSCRIPT) . - $(DOCKER) build -t $(IMAGE) . - -tagimage: - @echo "TAG IMAGE" - $(DOCKER) tag $(IMAGE) $(OCIR)$(OCIRPATH) - -push: - @echo "PUSH IMAGE INTO THE REGISTRY" - $(DOCKER) push $(OCIR)$(OCIRPATH) - -certmanager: - @echo "WEBHOOK CERT MANAGER" - $(KUBECTL) apply -f $(CERTMANAGER) - -dboperator: - @echo "ORACLE DATABASE OPERATOR" - $(KUBECTL) apply -f $(DBOPERATOR) - -namespace: - $(KUBECTL) get namespaces - $(KUBECTL) apply -f $(CDB_NAMESPACE_YAML) - $(KUBECTL) apply -f $(PDB_NAMESPACE_YAML) - $(KUBECTL) get namespaces - - -tlscert: - @echo "CREATING TLS CERTIFICATES" - $(OPENSSL) genrsa -out ca.key 2048 - $(OPENSSL) req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST) Root CA " -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj "/C=US/ST=California/L=SanFrancisco/O=$(COMPANY) /CN=$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE) /CN=$(LOCALHOST)" -out server.csr - $(ECHO) "subjectAltName=DNS:$(RESTPREFIX)-$(REST_SERVER).$(CDB_NAMESPACE),DNS:www.example.com" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $(SCRT) - - -tlssecret: - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(CDB_NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(CDB_NAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDB_NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDB_NAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(OPR_NAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(OPR_NAMESPACE) - - -dbsecret: - @echo "CREATING DB SECRETS" - $(KUBECTL) apply -f $(CDB_SECRET_YAML) - $(KUBECTL) apply -f $(PDB_SECRET_YAML) - $(KUBECTL) apply -f $(TDE_SECRET_YAML) - - -cdb: - @echo "CREATING REST SRV POD" - $(KUBECTL) apply -f $(CDB) - -checkcdb: - $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) - -pdb: - $(KUBECTL) apply -f $(PDB) - -checkpdb: - $(KUBECTL) get pdbs -n $(OPR_NAMESPACE) - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - @echo "CDB LOG DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs `$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep $(REST_SERVER)| cut -d ' ' -f 1` -n $(OPR_NAMESPACE) >>$(DIAGFILE) - @echo "SECRET DMP" >>$(DIAGFILE) - @echo "~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) get secrets -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) - @echo "CDB/PDB DMP" >> $(DIAGFILE) - $(KUBECTL) get pdbs -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) - $(KUBECTL) get cdb -o yaml -n $(OPR_NAMESPACE) >> $(DIAGFILE) - @echo "CLUSTER INFO" >> $(DIAGFILE) - $(KUBECTL) get nodes -o wide - $(KUBECTL) get svc --namespace=kube-system - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(OPR_NAMESPACE) -o yaml | kubectl replace --force -f - - -login: - $(KUBECTL) exec -it `$(KUBECTL) get pods -n $(CDB_NAMESPACE) |grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) bash - -cdblog: - $(KUBECTL) logs -f `$(KUBECTL) get pods -n $(CDB_NAMESPACE)|grep $(REST_SERVER)|cut -d ' ' -f 1` -n $(CDB_NAMESPACE) - - - -xlog1: - $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) - -xlog2: - $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) - -xlog3: - $(KUBECTL) logs -f pod/`$(KUBECTL) get pods -n $(OPR_NAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPR_NAMESPACE) - -checkdep: - $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(OPR_NAMESPACE) - $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(CBD_NAMESPACE) - $(KUBECTL) api-resources --verbs=list --namespaced -o name | xargs -n 1 $(KUBECTL) get -n $(PDB_NAMESPACE) - - - diff --git a/docs/multitenant/usecase03/ns_namespace_cdb.yaml b/docs/multitenant/usecase03/ns_namespace_cdb.yaml deleted file mode 100644 index f4c6d77b..00000000 --- a/docs/multitenant/usecase03/ns_namespace_cdb.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: cdbnamespace - diff --git a/docs/multitenant/usecase03/ns_namespace_pdb.yaml b/docs/multitenant/usecase03/ns_namespace_pdb.yaml deleted file mode 100644 index b22245f9..00000000 --- a/docs/multitenant/usecase03/ns_namespace_pdb.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: pdbnamespace - diff --git a/docs/multitenant/usecase03/operator_creation_log.txt b/docs/multitenant/usecase03/operator_creation_log.txt deleted file mode 100644 index 36ed02ac..00000000 --- a/docs/multitenant/usecase03/operator_creation_log.txt +++ /dev/null @@ -1,27 +0,0 @@ -kubectl apply -f oracle-database-operator.yaml -namespace/oracle-database-operator-system created -customresourcedefinition.apiextensions.k8s.io/autonomouscontainerdatabases.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/autonomousdatabasebackups.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/autonomousdatabaserestores.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/autonomousdatabases.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/cdbs.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/dataguardbrokers.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/dbcssystems.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/oraclerestdataservices.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/pdbs.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/shardingdatabases.database.oracle.com configured -customresourcedefinition.apiextensions.k8s.io/singleinstancedatabases.database.oracle.com configured -role.rbac.authorization.k8s.io/oracle-database-operator-leader-election-role created -clusterrole.rbac.authorization.k8s.io/oracle-database-operator-manager-role created -clusterrole.rbac.authorization.k8s.io/oracle-database-operator-metrics-reader created -clusterrole.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-role created -rolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-leader-election-rolebinding created -clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-manager-rolebinding created -clusterrolebinding.rbac.authorization.k8s.io/oracle-database-operator-oracle-database-operator-proxy-rolebinding created -service/oracle-database-operator-controller-manager-metrics-service created -service/oracle-database-operator-webhook-service created -certificate.cert-manager.io/oracle-database-operator-serving-cert created -issuer.cert-manager.io/oracle-database-operator-selfsigned-issuer created -mutatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-mutating-webhook-configuration created -validatingwebhookconfiguration.admissionregistration.k8s.io/oracle-database-operator-validating-webhook-configuration created -deployment.apps/oracle-database-operator-controller-manager created diff --git a/docs/multitenant/usecase03/pdb_create.yaml b/docs/multitenant/usecase03/pdb_create.yaml deleted file mode 100644 index 200f3712..00000000 --- a/docs/multitenant/usecase03/pdb_create.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v1alpha1 -kind: PDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - adminName: - secret: - secretName: "pdb1-secret" - key: "sysadmin_user" - adminPwd: - secret: - secretName: "pdb1-secret" - key: "sysadmin_pwd" - pdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - pdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - pdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "pdb1-secret" - key: "webserver_user" - webServerPwd: - secret: - secretName: "pdb1-secret" - key: "webserver_pwd" - fileNameConversions: "NONE" - tdeImport: false - totalSize: "1G" - tempSize: "100M" - action: "Create" - diff --git a/docs/multitenant/usecase03/pdb_creation_log.txt b/docs/multitenant/usecase03/pdb_creation_log.txt deleted file mode 100644 index 71d0eb4f..00000000 --- a/docs/multitenant/usecase03/pdb_creation_log.txt +++ /dev/null @@ -1,6 +0,0 @@ -kubectl apply -f pdb_create.yaml -pdb.database.oracle.com/pdb1 created - -kubectl get pdbs -n pdbnamespace -NAME CONNECT_STRING CDB NAME PDB NAME PDB STATE PDB SIZE STATUS MESSAGE -pdb1 (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) DB12 pdbdev READ WRITE 0.78G Ready Success diff --git a/docs/multitenant/usecase03/pdb_secret.yaml b/docs/multitenant/usecase03/pdb_secret.yaml deleted file mode 100644 index f1dfdac6..00000000 --- a/docs/multitenant/usecase03/pdb_secret.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# -apiVersion: v1 -kind: Secret -metadata: - name: pdb1-secret - namespace: pdbnamespace -type: Opaque -data: - sysadmin_user: "[...base64 encoded password...]" - sysadmin_pwd: "[...base64 encoded password...]" - webserver_user: "[...base64 encoded password...]" - webserver_pwd: "[...base64 encoded password...]" - diff --git a/docs/multitenant/usecase03/runOrdsSSL.sh b/docs/multitenant/usecase03/runOrdsSSL.sh deleted file mode 100644 index 35f1b77b..00000000 --- a/docs/multitenant/usecase03/runOrdsSSL.sh +++ /dev/null @@ -1,190 +0,0 @@ -#!/bin/bash - -cat <$TNSNAME - - -function SetParameter() { - ##ords config info <--- Use this command to get the list - -[[ ! -z "${ORACLE_HOST}" && -z "${DBTNSURL}" ]] && { - $ORDS --config ${CONFIG} config set db.hostname ${ORACLE_HOST:-racnode1} - $ORDS --config ${CONFIG} config set db.port ${ORACLE_PORT:-1521} - $ORDS --config ${CONFIG} config set db.servicename ${ORACLE_SERVICE:-TESTORDS} -} - -[[ -z "${ORACLE_HOST}" && ! -z "${DBTNSURL}" ]] && { - #$ORDS --config ${CONFIG} config set db.tnsAliasName ${TNSALIAS} - #$ORDS --config ${CONFIG} config set db.tnsDirectory ${TNS_ADMIN} - #$ORDS --config ${CONFIG} config set db.connectionType tns - - $ORDS --config ${CONFIG} config set db.connectionType customurl - $ORDS --config ${CONFIG} config set db.customURL jdbc:oracle:thin:@${DBTNSURL} -} - - $ORDS --config ${CONFIG} config set security.requestValidationFunction false - $ORDS --config ${CONFIG} config set jdbc.MaxLimit 100 - $ORDS --config ${CONFIG} config set jdbc.InitialLimit 50 - $ORDS --config ${CONFIG} config set error.externalPath ${ERRORFOLDER} - $ORDS --config ${CONFIG} config set standalone.access.log /home/oracle - $ORDS --config ${CONFIG} config set standalone.https.port 8888 - $ORDS --config ${CONFIG} config set standalone.https.cert ${CERTIFICATE} - $ORDS --config ${CONFIG} config set standalone.https.cert.key ${KEY} - $ORDS --config ${CONFIG} config set restEnabledSql.active true - $ORDS --config ${CONFIG} config set security.verifySSL true - $ORDS --config ${CONFIG} config set database.api.enabled true - $ORDS --config ${CONFIG} config set plsql.gateway.mode disabled - $ORDS --config ${CONFIG} config set database.api.management.services.disabled false - $ORDS --config ${CONFIG} config set misc.pagination.maxRows 1000 - $ORDS --config ${CONFIG} config set db.cdb.adminUser "${CDBADMIN_USER:-C##DBAPI_CDB_ADMIN} AS SYSDBA" - $ORDS --config ${CONFIG} config secret --password-stdin db.cdb.adminUser.password << EOF -${CDBADMIN_PWD:-PROVIDE_A_PASSWORD} -EOF - -$ORDS --config ${CONFIG} config user add --password-stdin ${WEBSERVER_USER:-ordspdbadmin} "SQL Administrator, System Administrator" <${CKF} 2>&1 -echo "checkfile" >> ${CKF} -NOT_INSTALLED=`cat ${CKF} | grep "INFO: The" |wc -l ` -echo NOT_INSTALLED=$NOT_INSTALLED - - -function StartUp () { - $ORDS --config $CONFIG serve --port 8888 --secure -} - -# Check whether ords is already setup -if [ $NOT_INSTALLED -ne 0 ] -then - echo " SETUP " - setupOrds; - StartUp; -fi - -if [ $NOT_INSTALLED -eq 0 ] -then - echo " STARTUP " - StartUp; -fi - - diff --git a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml index b45a1022..fcf5939f 100644 --- a/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml +++ b/docs/sharding/provisioning/debugging/sharding_provisioning_with_db_events.yaml @@ -99,4 +99,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/oraclesi.yaml b/docs/sharding/provisioning/oraclesi.yaml index c64098e7..fa36a10a 100644 --- a/docs/sharding/provisioning/oraclesi.yaml +++ b/docs/sharding/provisioning/oraclesi.yaml @@ -5,10 +5,10 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: oshard-gold-image-pvc21c + name: oshard-gold-image-pvc19c namespace: shns labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: accessModes: - ReadWriteOnce @@ -23,19 +23,19 @@ spec: apiVersion: apps/v1 kind: StatefulSet metadata: - name: oshard21cdb + name: oshard19cdb namespace: shns labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: selector: matchLabels: - app: oshard21cdb-dep + app: oshard19cdb-dep serviceName: gold-shard template: metadata: labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: containers: - image: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 @@ -68,7 +68,7 @@ spec: volumes: - name: data persistentVolumeClaim: - claimName: oshard-gold-image-pvc21c + claimName: oshard-gold-image-pvc19c - name: dshm emptyDir: medium: Memory @@ -78,7 +78,7 @@ spec: apiVersion: v1 kind: Service metadata: - name: oshard21cdb + name: oshard19cdb namespace: shns spec: ports: diff --git a/docs/sharding/provisioning/oraclesi_pvc_commented.yaml b/docs/sharding/provisioning/oraclesi_pvc_commented.yaml index 96607ad7..a54e07a5 100644 --- a/docs/sharding/provisioning/oraclesi_pvc_commented.yaml +++ b/docs/sharding/provisioning/oraclesi_pvc_commented.yaml @@ -23,19 +23,19 @@ apiVersion: apps/v1 kind: StatefulSet metadata: - name: oshard21cdb + name: oshard19cdb namespace: shns labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: selector: matchLabels: - app: oshard21cdb-dep + app: oshard19cdb-dep serviceName: gold-shard template: metadata: labels: - app: oshard21cdb-dep + app: oshard19cdb-dep spec: containers: - image: container-registry.oracle.com/database/enterprise_ru:19.28.0.0 @@ -68,7 +68,7 @@ spec: volumes: - name: data persistentVolumeClaim: - claimName: oshard-gold-image-pvc21c + claimName: oshard-gold-image-pvc19c - name: dshm emptyDir: medium: Memory @@ -78,7 +78,7 @@ spec: apiVersion: v1 kind: Service metadata: - name: oshard21cdb + name: oshard19cdb namespace: shns spec: ports: diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md deleted file mode 100644 index 9ffebad9..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_from_gold_image_across_ads.md +++ /dev/null @@ -1,58 +0,0 @@ -# Provisioning System managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image across Availability Domains(ADs) - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this test case, you provision the System managed Sharding Topology with Raft replication enabled while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. - -This use case applies when you want to provision the database Pods on a Kubernetes Node in any availability domain (AD), which can also be different from the availability domain (AD) of the Block Volume that has the Oracle Database Gold Image provisioned earlier. - -Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup across ADs. - -NOTE: - -* Cloning from Block Volume Backup in OCI enables the new Persistent Volumes to be created in other ADs. -* To specify the AD where you want to provision the database Pod, use the tag `nodeSelector` and the POD will be provisioned in a node running in that AD. -* To specify GSM containers, you can also use the tag `nodeSelector` to specify the AD. -* Before you can provision with the Gold Image, you need the OCID of the Persistent Volume that has the Oracle Database Gold Image. - -1. Check the OCID of the Persistent Volume provisioned for the Oracle Database Gold Image: - ```sh - kubectl get pv -n shns - ``` -2. Create a Block Volume Backup for this Block Volume, and use the OCID of the Block Volume Backup in the next step. This example uses `snr_ssharding_shard_prov_clone_across_ads.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* Database Cloning from the `BLOCK VOLUME FULL BACKUP` of the Persistent Volume which had the Gold Image. -* OCID of the Block Volume Backup: `ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq` -* `RAFT Replication` enabled - -NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned across multiple Availability Domains by cloning the database. - -**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone_across_ads.yaml`. - * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - -Use the file: [snr_ssharding_shard_prov_clone_across_ads.yaml](./snr_ssharding_shard_prov_clone_across_ads.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_clone_across_ads.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_clone_across_ads.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md deleted file mode 100644 index 054d760e..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_by_cloning_db_gold_image_in_same_ad.md +++ /dev/null @@ -1,54 +0,0 @@ -# Provisioning System managed Sharding Topology with Raft replication enabled by cloning database from your own Database Gold Image in the same Availability Domain(AD) - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this test case, you provision the System managed Sharding Topology with Raft replication enabled while provisioning the Catalog and Shard Databases by cloning from an existing Oracle Database Gold Image created earlier. - -This use case applies when you are cloning from a Block Volume, and you can clone _only_ in the same availability domain (AD). The result is that the cloned shard database PODs can be created _only_ in the same AD where the Gold Image Block Volume is present. - -Choosing this option takes substantially less time during the Oracle Database Sharding Topology setup. - -**NOTE** For this step, the Persistent Volume that has the Oracle Database Gold Image is identified using its OCID. - -1. Check the OCID of the Persistent Volume provisioned earlier using below command: - - ```sh - kubectl get pv -n shns - ``` - -2. This example uses `snr_ssharding_shard_prov_clone.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* Database Cloning from the Database Gold Image present in Persistent Volume having OCID: `ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq` -* `RAFT Replication` enabled - -NOTE: In this case, the Persistent Volume with DB Gold Image was provisioned in the Availability Domain `PHX-AD-1`. The Shards and Catalog will be provisioned in the same Availability Domain `PHX-AD-1` by cloning the database. - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_clone.yaml`. - * The `dbImage` used during provisioning the Persistent Volume with Database Gold Image and the `dbImage` used for deploying the Shard or Catalog Database by cloning should be same. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - -**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. - -Use the file: [snr_ssharding_shard_prov_clone.yaml](./snr_ssharding_shard_prov_clone.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_clone.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_clone.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md deleted file mode 100644 index 253d099b..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_provisioning_with_chunks_specified.md +++ /dev/null @@ -1,44 +0,0 @@ -# Provisioning System-Managed Sharding Topology with Raft replication enabled with number of chunks specified - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -In this use case, the database is created automatically using DBCA during the provisioning of the shard databases and the catalog database when the Oracle Sharding topology with System-Managed with RAFT Replication enabled is deployed using Oracle Sharding controller. - -**NOTE** In this use case, because DBCA creates the database automatically during the deployment, the time required to create the database is greater than the time it takes when the database is created by cloning from a Database Gold Image. - -By default, the System-Managed with RAFT Replication deploys the Sharded Database with 360 chunks per Shard Database (because there are 3 chunks created for each replication unit). In this example, the Sharded Database will be deployed with non-default number of chunks specified using parameter `CATALOG_CHUNKS`. - -This example uses `snr_ssharding_shard_prov_chunks.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Total number of chunks as `120` specified by variable `CATALOG_CHUNKS` (it will be 120 chunks per shard) -* Namespace: `shns` -* `RAFT Replication` enabled - - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - - -Use the file: [snr_ssharding_shard_prov_chunks.yaml](./snr_ssharding_shard_prov_chunks.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_chunks.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_chunks.yaml - ``` -1. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard1-0": - kubectl logs -f pod/shard1-0 -n shns - ``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md deleted file mode 100644 index fc093654..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_in_delete_an_existing_shard.md +++ /dev/null @@ -1,51 +0,0 @@ -# Scale In - Delete an existing Shard from a working Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT reolication enabled - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with System-Managed with RAFT Replication enabled provisioned using Oracle Database Sharding controller. - -**NOTE** The deletion of a shard is done after verifying the Chunks have been moved out of that shard. - -In this use case, the existing database Sharding is having: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Five sharding Pods: `shard1`,`shard2`,`shard3`,`shard4` and `shard5` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* `RAFT Replication` enabled - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_delshard.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - -NOTE: Use tag `isDelete: enable` to delete the shard you want. - -This use case deletes the shard `shard4` from the above Sharding Topology. - -Use the file: [snr_ssharding_shard_prov_delshard.yaml](./snr_ssharding_shard_prov_delshard.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_delshard.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_delshard.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - -**NOTE:** After you apply `snr_ssharding_shard_prov_delshard.yaml`, the change may not be visible immediately. When the shard is removed, first the chunks will be moved out of that shard that is going to be deleted. - -To monitor the chunk movement, use the following command: - -```sh -# Switch to the primary GSM Container: -kubectl exec -i -t gsm1-0 -n shns /bin/bash - -# Check the status of the chunks and repeat to observe the chunk movement: -gdsctl config chunks -``` diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md deleted file mode 100644 index 3461bf13..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_scale_out_add_shards.md +++ /dev/null @@ -1,38 +0,0 @@ -# Scale Out - Add Shards to an existing Oracle Sharded Database provisioned earlier with System-Managed Sharding with RAFT replication enabled - -**NOTE: RAFT Replication Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** - -**IMPORTANT:** Make sure you have completed the steps for [Prerequsites for Running Oracle Sharding Database Controller](../../README.md#prerequsites-for-running-oracle-sharding-database-controller) before using Oracle Sharding Controller. - -This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with System-Managed with RAFT Replication enabled provisioned earlier using Oracle Database Sharding controller. - -In this use case, the existing Oracle Database sharding topology is having: - -* Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` -* Three sharding Pods: `shard1`, `shard2` and `shard3` -* One Catalog Pod: `catalog` -* Namespace: `shns` -* `RAFT Replication` enabled - -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `snr_ssharding_shard_prov_extshard.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * If the existing Sharding Topology was deployed using [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then the additional parameter `dbEdition: "free"` will be needed for the below .yaml file as well. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - -This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. - -Use the file: [snr_ssharding_shard_prov_extshard.yaml](./snr_ssharding_shard_prov_extshard.yaml) for this use case as below: - -1. Deploy the `snr_ssharding_shard_prov_extshard.yaml` file: - ```sh - kubectl apply -f snr_ssharding_shard_prov_extshard.yaml - ``` -2. Check the status of the deployment: - ```sh - # Check the status of the Kubernetes Pods: - kubectl get all -n shns - - # Check the logs of a particular pod. For example, to check status of pod "shard4-0": - kubectl logs -f pod/shard4-0 -n shns diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml deleted file mode 100644 index 0230eac2..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_chunks.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - envVars: - - name: "CATALOG_CHUNKS" - value: "120" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml deleted file mode 100644 index fcc18da0..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone.yaml +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volume.oc1.phx.abyhqljr3z3w72t6ay5eud7d5w3kdfhktfp6gwb6euy5tzwfaxgmbvwqlvsq - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - isClone: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml deleted file mode 100644 index 0663f8a5..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_clone_across_ads.yaml +++ /dev/null @@ -1,91 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-2" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-1" - pvAnnotations: - volume.beta.kubernetes.io/oci-volume-source: ocid1.volumebackup.oc1.phx.abyhqljrxtv7tu5swqb3lzc7vpzwbwzdktd2y4k2vjjy2srmgu2w7bqdftjq - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - nodeSelector: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - pvMatchLabels: - "failure-domain.beta.kubernetes.io/zone": "PHX-AD-3" - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - isClone: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml deleted file mode 100644 index ce194246..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_delshard.yaml +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard4 - isDelete: enable - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard5 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml deleted file mode 100644 index 8848b8c7..00000000 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_extshard.yaml +++ /dev/null @@ -1,68 +0,0 @@ -# -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# ---- -apiVersion: database.oracle.com/v4 -kind: ShardingDatabase -metadata: - name: shardingdatabase-sample - namespace: shns -spec: - shard: - - name: shard1 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard2 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard3 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard4 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - - name: shard5 - storageSizeInGb: 50 - imagePullPolicy: "Always" - shardGroup: shardgroup1 - shardRegion: primary - catalog: - - name: catalog - storageSizeInGb: 50 - imagePullPolicy: "Always" - gsm: - - name: gsm1 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: primary - - name: gsm2 - imagePullPolicy: "Always" - storageSizeInGb: 50 - region: standby - storageClass: oci - dbImage: container-registry.oracle.com/database/free:latest - dbImagePullSecret: ocr-reg-cred - gsmImage: container-registry.oracle.com/database/gsm:latest - gsmImagePullSecret: ocr-reg-cred - dbEdition: "free" - replicationType: "native" - isExternalSvc: False - isDeleteOraPvc: True - dbSecret: - name: db-user-pass-rsa - pwdFileName: pwdfile.enc - keyFileName: key.pem - gsmService: - - name: oltp_rw_svc - role: primary - - name: oltp_ro_svc - role: primary diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml index 6a91c621..191fd6dc 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_memory_cpu.yaml @@ -128,4 +128,4 @@ spec: ruMode: READWRITE # Specify the ruMode for the service as READWRITE in RAFT Replication Setup - name: oltp_ro_svc role: primary - ruMode: READONLY # Specify the ruMode for the service as READWRITE in RAFT Replication Setup + ruMode: READONLY # Specify the ruMode for the service as READWRITE in RAFT Replication Setup \ No newline at end of file diff --git a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml index e9a7546d..55690010 100644 --- a/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/snr_system_sharding/snr_ssharding_shard_prov_send_notification.yaml @@ -126,4 +126,4 @@ spec: ruMode: READWRITE # Specify the ruMode for the service as READWRITE in RAFT Replication Setup - name: oltp_ro_svc role: primary - ruMode: READONLY # Specify the ruMode for the service as READWRITE in RAFT Replication Setup + ruMode: READONLY # Specify the ruMode for the service as READWRITE in RAFT Replication Setup \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml index 9c0eec5e..6bc1917f 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone.yaml @@ -112,4 +112,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml index 9e9c802e..c0b4760e 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.yaml @@ -120,4 +120,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml index a00747e4..01619fd9 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml @@ -96,4 +96,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml index ffd4222a..078a7858 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_extshard.yaml @@ -97,4 +97,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml index 5b7586ae..8876084a 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml @@ -122,4 +122,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml index 4dbd9eaf..abda743c 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_send_notification.yaml @@ -118,4 +118,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml index 77071d59..6553fdb3 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov.yaml @@ -88,4 +88,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml index f4a670a5..522f6cd9 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone.yaml @@ -115,4 +115,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml index 38e29904..0fc7ed0d 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_clone_across_ads.yaml @@ -123,4 +123,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml index e7fbe57e..4f858b87 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml @@ -99,4 +99,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml index fd7befc1..0929ae89 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_extshard.yaml @@ -100,4 +100,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml index cf53dbca..a2b16ebb 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml @@ -124,4 +124,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml index a334522b..163e77b8 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_send_notification.yaml @@ -129,4 +129,4 @@ spec: - name: oltp_rw_svc role: primary - name: oltp_ro_svc - role: primary + role: primary \ No newline at end of file From 5ee15125e4de7a42afac2c825684eaaf32fdcc74 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Sun, 5 Oct 2025 00:50:10 +0000 Subject: [PATCH 370/414] Added files --- PROJECT | 13 ++++++ README.md | 46 ++----------------- .../manifests/database.oracle.com_lrpdbs.yaml | 30 ------------ go.mod | 1 - 4 files changed, 16 insertions(+), 74 deletions(-) diff --git a/PROJECT b/PROJECT index 7c6f9bd5..6d4826f8 100644 --- a/PROJECT +++ b/PROJECT @@ -259,6 +259,19 @@ resources: webhooks: conversion: true webhookVersion: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: oracle.com + group: omlai + kind: PrivateAi + path: github.com/oracle/oracle-database-operator/api/omlai/v4 + version: v4 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 - api: crdVersion: v1 namespaced: true diff --git a/README.md b/README.md index 7e78f7bb..da6a179e 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ This production release has been installed and tested on: * [Red Hat OKD](https://www.okd.io/) * [Minikube](https://minikube.sigs.k8s.io/docs/) with version v1.29.0 + ## Prerequisites Oracle strongly recommends that you ensure your system meets the following [Prerequisites](./PREREQUISITES.md). @@ -186,52 +187,11 @@ Oracle strongly recommends that you ensure your system meets the following [Prer To install the operator in the cluster quickly, you can apply the modified `oracle-database-operator.yaml` file from the preceding step. - This is the default mode, in which OraOperator is deployed to operate in a cluster, and to monitor all the namespaces in the cluster. - - - Grant the `serviceaccount:oracle-database-operator-system:default` clusterwide access for the resources by applying [cluster-role-binding.yaml](./rbac/cluster-role-binding.yaml) - - ```sh - kubectl apply -f rbac/cluster-role-binding.yaml - ``` - - - Next, apply the [oracle-database-operator.yaml](./oracle-database-operator.yaml) to deploy the Operator - - ```sh - kubectl apply -f oracle-database-operator.yaml - ``` - - ##### 2. Namespace Scoped Deployment - - In this mode, `OraOperator` can be deployed to operate in a namespace, and to monitor one or many namespaces. - - - Grant `serviceaccount:oracle-database-operator-system:default` service account with resource access in the required namespaces. For example, to monitor only the default namespace, apply the [`default-ns-role-binding.yaml`](./rbac/default-ns-role-binding.yaml) - - ```sh - kubectl apply -f rbac/default-ns-role-binding.yaml - ``` - To watch additional namespaces, create different role binding files for each namespace, using [default-ns-role-binding.yaml](./rbac/default-ns-role-binding.yaml) as a template, and changing the `metadata.name` and `metadata.namespace` fields - - - Next, edit the [`oracle-database-operator.yaml`](./oracle-database-operator.yaml) to add the required namespaces under `WATCH_NAMESPACE`. Use comma-delimited values for multiple namespaces. - - ```sh - - name: WATCH_NAMESPACE - value: "default" - ``` - - Finally, apply the edited [`oracle-database-operator.yaml`](./oracle-database-operator.yaml) to deploy the Operator - - ```sh - kubectl apply -f oracle-database-operator.yaml - ``` - -* ### ClusterRole and ClusterRoleBinding for NodePort services - - To expose services on each node's IP and port (the NodePort), apply the [`node-rbac.yaml`](./rbac/node-rbac.yaml). Note that this step is not required for LoadBalancer services. + Run the following command ```sh kubectl apply -f oracle-database-operator.yaml ``` -## Installation -### Install Oracle DB Operator Ensure that the operator pods are up and running. For high availability, operator pod replicas are set to a default of 3. You can scale this setting up or down. @@ -258,7 +218,7 @@ Oracle strongly recommends that you ensure your system meets the following [Prer You should see that the operator is up and running, along with the shipped controllers. For more details, see [Oracle Database Operator Installation Instructions](./docs/installation/OPERATOR_INSTALLATION_README.md). -## Documentation + ## Getting Started with the Operator (Quickstart) The following quickstarts are designed for specific database configurations: diff --git a/bundle/manifests/database.oracle.com_lrpdbs.yaml b/bundle/manifests/database.oracle.com_lrpdbs.yaml index 4ab42578..c0ccee9d 100644 --- a/bundle/manifests/database.oracle.com_lrpdbs.yaml +++ b/bundle/manifests/database.oracle.com_lrpdbs.yaml @@ -236,36 +236,6 @@ spec: type: string pdbName: type: string - pdbOrdsPrvKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object - pdbOrdsPubKey: - properties: - secret: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - secret - type: object pdbState: enum: - OPEN diff --git a/go.mod b/go.mod index 59f1af19..1c9c56ec 100644 --- a/go.mod +++ b/go.mod @@ -118,4 +118,3 @@ require ( sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) - From f043e61a14d409680181038f1944842c0cfe839f Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 6 Oct 2025 17:31:26 +0000 Subject: [PATCH 371/414] Added fixes for oracle restart --- apis/database/v4/oraclerestart_types.go | 36 ++-- apis/database/v4/oraclerestart_webhook.go | 104 +++++++++- commons/oraclerestart/oraclerestartcommon.go | 77 +++++--- commons/oraclerestart/oraclerestartprov.go | 179 +++++++++++------- .../database/oraclerestart_controller.go | 49 ++--- 5 files changed, 306 insertions(+), 139 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 53e5d196..4390fcc8 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -61,22 +61,26 @@ type OracleRestartSpec struct { SshKeySecret *OracleRestartSshSecretDetails `json:"sshKeySecret,omitempty"` // +kubebuilder:validation:Enum=Always;IfNotPresent;Never // +kubebuilder:validation:default="Always" - ImagePullPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` - ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` - ScriptsGetCmd string `json:"scriptsGetCmd,omitempty"` - IsDebug string `json:"isDebug,omitempty"` - SecurityContext *corev1.PodSecurityContext `json:"securityContext"` - IsDeleteTopolgy string `json:"isDeleteTopology,omitempty"` - DbSecret *OracleRestartDbPwdSecretDetails `json:"dbSecret,omitempty"` - TdeWalletSecret *OracleRestartDbPwdSecretDetails `json:"tdeWalletSecret,omitempty"` - ServiceDetails ServiceSpec `json:"serviceDetails,omitempty"` - Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` - IsFailed bool `json:"isFailed,omitempty"` - IsManual bool `json:"isManual,omitempty"` - SrvAccountName string `json:"serviceAccountName,omitempty"` - StorageClass string `json:"storageClass,omitempty"` - LbService OracleRestartNodePortSvc `json:"lbService,omitempty"` - NodePortSvc OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if + ImagePullPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` + ScriptsGetCmd string `json:"scriptsGetCmd,omitempty"` + IsDebug string `json:"isDebug,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext"` + IsDeleteTopolgy string `json:"isDeleteTopology,omitempty"` + DbSecret *OracleRestartDbPwdSecretDetails `json:"dbSecret,omitempty"` + TdeWalletSecret *OracleRestartDbPwdSecretDetails `json:"tdeWalletSecret,omitempty"` + ServiceDetails ServiceSpec `json:"serviceDetails,omitempty"` + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` + IsFailed bool `json:"isFailed,omitempty"` + IsManual bool `json:"isManual,omitempty"` + SrvAccountName string `json:"serviceAccountName,omitempty"` + DataDgStorageClass string `json:"dataDgStorageClass,omitempty"` + RedoDgStorageClass string `json:"redoDgStorageClass,omitempty"` + RecoDgStorageClass string `json:"recoDgStorageClass,omitempty"` + SwStorageClass string `json:"swDgStorageClass,omitempty"` + CrsDgStorageClass string `json:"crsDgStorageClass,omitempty"` + LbService OracleRestartNodePortSvc `json:"lbService,omitempty"` + NodePortSvc OracleRestartNodePortSvc `json:"nodePortSvc,omitempty"` // Port mappings for the service that is created. The service is created if // +kubebuilder:validation:Enum=enable;disable // +kubebuilder:default="enable" EnableOns string `json:"enableOns,omitempty"` diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index 0fc41d75..ac064717 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -196,6 +196,18 @@ func (r *OracleRestart) ValidateCreate(ctx context.Context, obj runtime.Object) deviceWarnings = append(deviceWarnings, w...) validationErrs = append(validationErrs, errs...) + errs = cr.validateRedoAsmDG() + validationErrs = append(validationErrs, errs...) + + errs = cr.validateRecoAsmDG() + validationErrs = append(validationErrs, errs...) + + errs = cr.validateDataAsmDG() + validationErrs = append(validationErrs, errs...) + + errs = cr.validateCrsAsmDG() + validationErrs = append(validationErrs, errs...) + if cr.Spec.ConfigParams != nil { // CRS validationErrs = append(validationErrs, @@ -251,11 +263,39 @@ func (r *OracleRestart) ValidateUpdate(ctx context.Context, oldObj, newObj runti } } - if old.Spec.StorageClass != newCr.Spec.StorageClass { + if old.Spec.DataDgStorageClass != newCr.Spec.DataDgStorageClass { + + return nil, apierrors.NewForbidden( + schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, + newCr.Name, fmt.Errorf("updates to the Data storageclass is forbidden: %s", old.Spec.DataDgStorageClass)) + } + + if old.Spec.RecoDgStorageClass != newCr.Spec.RecoDgStorageClass { + + return nil, apierrors.NewForbidden( + schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, + newCr.Name, fmt.Errorf("updates to the Reco storageclass is forbidden: %s", old.Spec.RecoDgStorageClass)) + } + + if old.Spec.RedoDgStorageClass != newCr.Spec.RedoDgStorageClass { + + return nil, apierrors.NewForbidden( + schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, + newCr.Name, fmt.Errorf("updates to the Redo storageclass is forbidden: %s", old.Spec.RedoDgStorageClass)) + } + + if old.Spec.SwStorageClass != newCr.Spec.SwStorageClass { return nil, apierrors.NewForbidden( schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, - newCr.Name, fmt.Errorf("updates to the storageclass is forbidden: %s", old.Spec.StorageClass)) + newCr.Name, fmt.Errorf("updates to the Swstorageclass is forbidden: %s", old.Spec.SwStorageClass)) + } + + if old.Spec.CrsDgStorageClass != newCr.Spec.CrsDgStorageClass { + + return nil, apierrors.NewForbidden( + schema.GroupResource{Group: "database.oracle.com", Resource: "OracleRestart"}, + newCr.Name, fmt.Errorf("updates to the CrsDgStorageClass is forbidden: %s", old.Spec.CrsDgStorageClass)) } if newCr.Spec.InstDetails.SwLocStorageSizeInGb < old.Spec.InstDetails.SwLocStorageSizeInGb { @@ -564,14 +604,14 @@ func (r *OracleRestart) validateGeneric() field.ErrorList { "Name must contain only alphanumeric characters")) } - if r.Spec.InstDetails.HostSwLocation == "" && r.Spec.StorageClass == "" { + if r.Spec.InstDetails.HostSwLocation == "" && r.Spec.SwStorageClass == "" { validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("InstDetails").Child("HostSwLocation"), r.Spec.InstDetails.HostSwLocation, "Either HostSwLocation or SwStorageClass must be specified")) } } - if r.Spec.StorageClass != "" { + if r.Spec.SwStorageClass != "" { if r.Spec.InstDetails.SwLocStorageSizeInGb < 60 { validationErrs = append(validationErrs, field.Invalid(field.NewPath("spec").Child("InstDetails").Child("SwLocStorageSizeInGb"), r.Spec.InstDetails.SwLocStorageSizeInGb, @@ -684,10 +724,10 @@ func (r *OracleRestart) validateGeneric() field.ErrorList { "DbSwZipFile cannot be empty")) } - if cfg.HostSwStageLocation == "" && r.Spec.StorageClass == "" { + if cfg.HostSwStageLocation == "" && r.Spec.SwStorageClass == "" { validationErrs = append(validationErrs, field.Invalid(cfgPath.Child("HostSwStageLocation"), cfg.HostSwStageLocation, - "Either HostSwStageLocation or StorageClass must be specified")) + "Either HostSwStageLocation or SwDgStorageClass must be specified")) } if r.Spec.ConfigParams.RuPatchLocation != "" { @@ -972,6 +1012,58 @@ func (r *OracleRestart) validateRedoAsmDeviceList() ([]string, field.ErrorList) return r.validateAsmDeviceList(r.Spec.ConfigParams.RedoAsmDeviceList, "RedoAsmDeviceList") } +func (r *OracleRestart) validateRedoAsmDG() field.ErrorList { + var validationErrs field.ErrorList + + if r.Spec.RedoDgStorageClass != "" { + if r.Spec.ConfigParams == nil || r.Spec.ConfigParams.RedoAsmDeviceList == "" { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("RedoDgStorageClass"), r.Spec.RedoDgStorageClass, fmt.Sprintf("Redo ASM diskgroup storageclass set but Spec.ConfigParams.RedoAsmDeviceList is set to empty"))) + return validationErrs + } + } + return nil +} + +func (r *OracleRestart) validateRecoAsmDG() field.ErrorList { + var validationErrs field.ErrorList + + if r.Spec.RecoDgStorageClass != "" { + if r.Spec.ConfigParams == nil || r.Spec.ConfigParams.RecoAsmDeviceList == "" { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("RecoDgStorageClass"), r.Spec.RecoDgStorageClass, fmt.Sprintf("Reco ASM diskgroup storageclass set but Spec.ConfigParams.RecoAsmDeviceList is set to empty"))) + return validationErrs + } + } + return nil +} + +func (r *OracleRestart) validateDataAsmDG() field.ErrorList { + var validationErrs field.ErrorList + + if r.Spec.DataDgStorageClass != "" { + if r.Spec.ConfigParams == nil || r.Spec.ConfigParams.DbAsmDeviceList == "" { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("DataDgStorageClass"), r.Spec.RedoDgStorageClass, fmt.Sprintf("Data ASM diskgroup storageclass set but Spec.ConfigParams.DataAsmDeviceList is set to empty"))) + return validationErrs + } + } + return nil +} + +func (r *OracleRestart) validateCrsAsmDG() field.ErrorList { + var validationErrs field.ErrorList + + if r.Spec.CrsDgStorageClass != "" { + if r.Spec.ConfigParams == nil || r.Spec.ConfigParams.CrsAsmDeviceList == "" { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("CrsDgStorageClass"), r.Spec.CrsDgStorageClass, fmt.Sprintf("Crs ASM diskgroup storageclass set but Spec.ConfigParams.CrsAsmDeviceList is set to empty"))) + return validationErrs + } + } + return nil +} + // Helper function to check if a slice contains a specific element func contains(slice []string, item string) bool { for _, elem := range slice { diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 92a1507d..c232e5be 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -44,6 +44,7 @@ import ( "errors" "fmt" "path/filepath" + "slices" "strconv" oraclerestart "github.com/oracle/oracle-database-operator/apis/database/v4" @@ -191,30 +192,21 @@ func getlabelsForRac(instance *oraclerestart.OracleRestart) map[string]string { return buildLabelsForOracleRestart(instance, "OracleRestart") } -func getAsmPvcName(index int, name string) string { +func GetAsmPvcName(name string, diskPath string, instance *oraclerestart.OracleRestart) string { - pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name + // pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name + "-" + dgType + "-" + "pvc" + dgType := CheckDiskInAsmDeviceList(instance, diskPath) + diskName := diskPath[strings.LastIndex(diskPath, ",")+1:] + pvcName := "asm-dg-" + dgType + "-" + diskName + "-pvc-" + name return pvcName - -} -func GetAsmPvcName(index int, name string) string { - - pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name - - return pvcName - } -func getAsmPvName(index int, name string) string { +func GetAsmPvName(name string, diskPath string, instance *oraclerestart.OracleRestart) string { - pvName := "asm-pv-disk-" + strconv.Itoa(index) + "-" + name - return pvName -} - -func GetAsmPvName(index int, name string) string { - - pvName := "asm-pv-disk-" + strconv.Itoa(index) + "-" + name + dgType := CheckDiskInAsmDeviceList(instance, diskPath) + diskName := diskPath[strings.LastIndex(diskPath, ",")+1:] + pvName := "asm-dg-" + dgType + "-" + diskName + "-pv-" + name return pvName } @@ -393,8 +385,8 @@ func checkPv(pvName string, instance *oraclerestart.OracleRestart, kClient clien } return pvFound, nil } -func DelORestartPVC(instance *oraclerestart.OracleRestart, index int, diskName string, disk *oraclerestart.AsmDiskDetails, kClient client.Client, logger logr.Logger) error { - pvcName := getAsmPvcName(index, instance.Name) +func DelORestartPVC(instance *oraclerestart.OracleRestart, pindex int, cindex int, diskName string, disk *oraclerestart.AsmDiskDetails, kClient client.Client, logger logr.Logger) error { + pvcName := GetAsmPvcName(instance.Name, diskName, instance) LogMessages("DEBUG", "Attempting to delete PVC: "+GetFmtStr(pvcName), nil, instance, logger) pvc, err := checkPvc(pvcName, instance, kClient) @@ -438,9 +430,9 @@ func DelRestartSwPvc(instance *oraclerestart.OracleRestart, OraRestartSpex oracl return nil } -func DelORestartPv(instance *oraclerestart.OracleRestart, index int, diskName string, disk *oraclerestart.AsmDiskDetails, kClient client.Client, logger logr.Logger) error { +func DelORestartPv(instance *oraclerestart.OracleRestart, pindex int, cindex int, diskName string, disk *oraclerestart.AsmDiskDetails, kClient client.Client, logger logr.Logger) error { - pvName := getAsmPvName(index, instance.Name) + pvName := GetAsmPvName(instance.Name, diskName, instance) LogMessages("DEBUG", "Inside the delPv and received param: "+GetFmtStr(pvName), nil, instance, logger) pvFound, err := checkPv(pvName, instance, kClient) if err != nil { @@ -1360,3 +1352,44 @@ func GetHealthyNodeCounts(instance *oraclerestart.OracleRestart) (int, error) { func GetSwPvcName(name string) string { return name + "-oradata-sw-vol-pvc" } + +func CheckDiskInAsmDeviceList(instance *oraclerestart.OracleRestart, diskName string) string { + dgDisk := []string{"CRS", "DATA", "RECO", "REDO"} + + recoDisk := strings.Split(instance.Spec.ConfigParams.RecoAsmDeviceList, ",") + redoDisk := strings.Split(instance.Spec.ConfigParams.RedoAsmDeviceList, ",") + dataDisk := strings.Split(instance.Spec.ConfigParams.DbAsmDeviceList, ",") + crsDisk := strings.Split(instance.Spec.ConfigParams.CrsAsmDeviceList, ",") + + for _, value := range dgDisk { + switch value { + case "CRS": + if slices.Contains(crsDisk, diskName) { + return "CRSDG" + } + case "DATA": + if slices.Contains(dataDisk, diskName) { + return "DATADG" + } + case "RECO": + if slices.Contains(recoDisk, diskName) { + return "RECODG" + } + case "REDO": + if slices.Contains(redoDisk, diskName) { + return "REDODG" + } + default: + return "NODG" + } + + } + return "NODG" +} + +func CheckStorageClass(instance *oraclerestart.OracleRestart) string { + if len(instance.Spec.CrsDgStorageClass) == 0 && len(instance.Spec.DataDgStorageClass) == 0 && len(instance.Spec.RecoDgStorageClass) == 0 && len(instance.Spec.RedoDgStorageClass) == 0 { + return "NOSC" + } + return "SC" +} diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index fc517a3a..8744f413 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -68,9 +68,9 @@ func buildLabelsForOracleRestart(instance *oraclerestart.OracleRestart, label st // "oralabel": getLabelForOracleRestart(instance), } -func buildLabelsForAsmPv(instance *oraclerestart.OracleRestart, label string, index int) map[string]string { +func buildLabelsForAsmPv(instance *oraclerestart.OracleRestart, diskName string) map[string]string { return map[string]string{ - "asm_vol": "block-asm-pv-" + getLabelForOracleRestart(instance) + "-" + fmt.Sprint(index), + "asm_vol": "block-asm-pv-" + getLabelForOracleRestart(instance) + "-" + diskName[strings.LastIndex(diskName, "/")+1:], } } @@ -139,11 +139,23 @@ func buildStatefulSpecForOracleRestart( }, } // Add volume claim templates if a storage class is specified - if len(instance.Spec.StorageClass) != 0 && !asmPvcsExist(instance, kClient) { - sfsetspec.VolumeClaimTemplates = ASMVolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex) + if len(instance.Spec.DataDgStorageClass) != 0 && !asmPvcsExist(instance, kClient) { + sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, instance.Spec.DataDgStorageClass)...) } - if len(instance.Spec.StorageClass) != 0 && len(instance.Spec.InstDetails.HostSwLocation) == 0 { + if len(instance.Spec.CrsDgStorageClass) != 0 && !asmPvcsExist(instance, kClient) { + sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, instance.Spec.CrsDgStorageClass)...) + } + + if len(instance.Spec.RecoDgStorageClass) != 0 && !asmPvcsExist(instance, kClient) { + sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, instance.Spec.RecoDgStorageClass)...) + } + + if len(instance.Spec.RedoDgStorageClass) != 0 && !asmPvcsExist(instance, kClient) { + sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, instance.Spec.RedoDgStorageClass)...) + } + + if len(instance.Spec.SwStorageClass) != 0 && len(instance.Spec.InstDetails.HostSwLocation) == 0 { sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, SwVolumeClaimTemplatesForOracleRestart(instance, OracleRestartSpex)) } // Add annotations to the Pod template @@ -153,21 +165,23 @@ func buildStatefulSpecForOracleRestart( } func asmPvcsExist(instance *oraclerestart.OracleRestart, kClient client.Client) bool { - for i := range instance.Spec.AsmStorageDetails.DisksBySize { - pvcName := GetAsmPvcName(i, instance.Name) - var pvc corev1.PersistentVolumeClaim - err := kClient.Get(context.TODO(), types.NamespacedName{ - Name: pvcName, - Namespace: instance.Namespace, - }, &pvc) + for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { + for _, diskName := range diskBySize.DiskNames { + pvcName := GetAsmPvcName(instance.Name, diskName, instance) + var pvc corev1.PersistentVolumeClaim + err := kClient.Get(context.TODO(), types.NamespacedName{ + Name: pvcName, + Namespace: instance.Namespace, + }, &pvc) - if err != nil { - if apierrors.IsNotFound(err) { - // If even one expected PVC is not found, treat as "not all exist" - return false + if err != nil { + if apierrors.IsNotFound(err) { + // If even one expected PVC is not found, treat as "not all exist" + return false + } + // If error is something else, assume PVCs exist to avoid accidental overwrite + return true } - // If error is something else, assume PVCs exist to avoid accidental overwrite - return true } } return true @@ -229,7 +243,7 @@ func getNodeAffinity(instance *oraclerestart.OracleRestart, OracleRestartSpex or } // Function get the Node Affinity -func getAsmNodeAffinity(instance *oraclerestart.OracleRestart, index int, disk *oraclerestart.AsmDiskDetails) *corev1.VolumeNodeAffinity { +func getAsmNodeAffinity(instance *oraclerestart.OracleRestart, disk *oraclerestart.AsmDiskDetails) *corev1.VolumeNodeAffinity { nodeAffinity := &corev1.VolumeNodeAffinity{ Required: &corev1.NodeSelector{ @@ -314,7 +328,7 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac if len(OracleRestartSpex.HostSwLocation) != 0 { result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: OracleRestartSpex.HostSwLocation}}}) } else { - if instance.Spec.StorageClass != "" { + if instance.Spec.SwStorageClass != "" { result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OracleRestartSpex.Name + "-oradata-sw-vol-pvc-" + OracleRestartSpex.Name + "-0"}}}) } } @@ -379,11 +393,11 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac // Iterate over the DisksBySize slice for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { // For each DiskBySize, append PVCs for the disks in DiskNames - for index := range diskBySize.DiskNames { + for _, diskName := range diskBySize.DiskNames { // Construct PVC name based on index and instance name - pvcName := getAsmPvcName(index, instance.Name) + pvcName := GetAsmPvcName(instance.Name, diskName, instance) result = append(result, corev1.Volume{ - Name: pvcName + "-pvc", + Name: pvcName, VolumeSource: corev1.VolumeSource{ PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: pvcName, @@ -463,8 +477,8 @@ func getAsmVolumeDevices(instance *oraclerestart.OracleRestart, OracleRestartSpe // For each disk in DiskNames, create a VolumeDevice for _, diskName := range diskBySize.DiskNames { // Create PVC name and append VolumeDevice to the result - pvcName := getAsmPvcName(len(result), instance.Name) - result = append(result, corev1.VolumeDevice{Name: pvcName + "-pvc", DevicePath: diskName}) + pvcName := GetAsmPvcName(instance.Name, diskName, instance) + result = append(result, corev1.VolumeDevice{Name: pvcName, DevicePath: diskName}) } } } @@ -559,7 +573,7 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, } if len(OracleRestartSpex.HostSwLocation) != 0 { result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-sw-vol", MountPath: instance.Spec.ConfigParams.SwMountLocation}) - } else if len(instance.Spec.StorageClass) != 0 { + } else if len(instance.Spec.SwStorageClass) != 0 { result = append(result, corev1.VolumeMount{Name: OracleRestartSpex.Name + "-oradata-sw-vol", MountPath: instance.Spec.ConfigParams.SwMountLocation}) } else { fmt.Println("No Location is passed for the software storage in" + OracleRestartSpex.Name) @@ -611,14 +625,14 @@ func buildVolumeMountSpecForOracleRestart(instance *oraclerestart.OracleRestart, return result } -func VolumePVCForASM(instance *oraclerestart.OracleRestart, index int, diskName string, size int, asmStorage *oraclerestart.AsmDiskDetails, k8sClient client.Client) *corev1.PersistentVolumeClaim { +func VolumePVCForASM(instance *oraclerestart.OracleRestart, index int, diskName string, size int, asmStorage *oraclerestart.AsmDiskDetails, pvcName string, dgType string, k8sClient client.Client) *corev1.PersistentVolumeClaim { // Set volume mode to block volumeBlock := corev1.PersistentVolumeBlock // Create PersistentVolumeClaim asmPvc := &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ - Name: getAsmPvcName(index, instance.Name), // Use size to determine index + Name: pvcName, // Use size to determine index Namespace: instance.Namespace, Labels: buildLabelsForOracleRestart(instance, "OracleRestart"), }, @@ -634,17 +648,33 @@ func VolumePVCForASM(instance *oraclerestart.OracleRestart, index int, diskName }, } var scName *string - if len(instance.Spec.StorageClass) != 0 { - scName = &instance.Spec.StorageClass - } else { + switch dgType { + case "RECO": + if len(instance.Spec.RecoDgStorageClass) != 0 { + scName = &instance.Spec.RecoDgStorageClass + } + case "REDO": + if len(instance.Spec.RedoDgStorageClass) != 0 { + scName = &instance.Spec.RedoDgStorageClass + } + case "CRS": + if len(instance.Spec.CrsDgStorageClass) != 0 { + scName = &instance.Spec.CrsDgStorageClass + } + case "DATA": + if len(instance.Spec.DataDgStorageClass) != 0 { + scName = &instance.Spec.DataDgStorageClass + } + } + if scName == nil { // Try to fetch the cluster's default StorageClass if defaultSC, err := GetDefaultStorageClass(context.TODO(), k8sClient); err == nil && defaultSC != "" { scName = &defaultSC } else { // No StorageClass, so use label selector and statically bound PVs asmPvc.Spec.Selector = &metav1.LabelSelector{ - MatchLabels: buildLabelsForAsmPv(instance, string(diskName), index), + MatchLabels: buildLabelsForAsmPv(instance, string(diskName)), } scName = nil } @@ -653,6 +683,7 @@ func VolumePVCForASM(instance *oraclerestart.OracleRestart, index int, diskName return asmPvc } + func GetDefaultStorageClass(ctx context.Context, k8sClient client.Client) (string, error) { var scList storagev1.StorageClassList if err := k8sClient.List(ctx, &scList); err != nil { @@ -667,14 +698,14 @@ func GetDefaultStorageClass(ctx context.Context, k8sClient client.Client) (strin return "", nil // No default StorageClass found } -func VolumePVForASM(instance *oraclerestart.OracleRestart, index int, diskName string, size int, asmStorage *oraclerestart.AsmDiskDetails, k8sClient client.Client) *corev1.PersistentVolume { +func VolumePVForASM(instance *oraclerestart.OracleRestart, diskName string, size int, asmStorage *oraclerestart.AsmDiskDetails, pvName string, k8sClient client.Client) *corev1.PersistentVolume { volumeBlock := corev1.PersistentVolumeBlock asmPvc := &corev1.PersistentVolume{ ObjectMeta: metav1.ObjectMeta{ - Name: getAsmPvName(index, instance.Name), + Name: pvName, Namespace: instance.Namespace, - Labels: buildLabelsForAsmPv(instance, diskName, index), + Labels: buildLabelsForAsmPv(instance, diskName), }, Spec: corev1.PersistentVolumeSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ @@ -687,22 +718,23 @@ func VolumePVForASM(instance *oraclerestart.OracleRestart, index int, diskName s } var scName *string - if len(instance.Spec.StorageClass) != 0 { - scName = &instance.Spec.StorageClass - asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) - asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} - } else { - - // Try to fetch the cluster's default StorageClass - if defaultSC, err := GetDefaultStorageClass(context.TODO(), k8sClient); err == nil && defaultSC != "" { - scName = &defaultSC + /* + if len(instance.Spec.StorageClass) != 0 { + scName = &instance.Spec.StorageClass + asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) + asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} } else { - // No StorageClass, so use label selector and statically bound PVs - scName = nil - } - asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, index, asmStorage) - asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} + */ + // Try to fetch the cluster's default StorageClass + if defaultSC, err := GetDefaultStorageClass(context.TODO(), k8sClient); err == nil && defaultSC != "" { + scName = &defaultSC + } else { + // No StorageClass, so use label selector and statically bound PVs + scName = nil } + asmPvc.Spec.NodeAffinity = getAsmNodeAffinity(instance, asmStorage) + asmPvc.Spec.PersistentVolumeSource = corev1.PersistentVolumeSource{Local: &corev1.LocalVolumeSource{Path: diskName}} + if scName != nil { asmPvc.Spec.StorageClassName = *scName } @@ -904,24 +936,27 @@ func BuildDiskCheckDaemonSet(OracleRestart *oraclerestart.OracleRestart) *appsv1 // Prepare the volume devices based on the PVCs var volumeDevices []corev1.VolumeDevice var volumes []corev1.Volume - disks := flattenDisksBySize(&OracleRestart.Spec) - for index, diskPath := range disks { - pvcName := GetAsmPvcName(index, OracleRestart.Name) - volumeName := pvcName + "-pvc" - - volumeDevices = append(volumeDevices, corev1.VolumeDevice{ - Name: volumeName, - DevicePath: diskPath, - }) + //disks := flattenDisksBySize(&OracleRestart.Spec) - volumes = append(volumes, corev1.Volume{ - Name: volumeName, - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: pvcName, + for _, diskBySize := range OracleRestart.Spec.AsmStorageDetails.DisksBySize { + for _, diskName := range diskBySize.DiskNames { + pvcName := GetAsmPvcName(OracleRestart.Name, diskName, OracleRestart) + volumeName := pvcName + + volumeDevices = append(volumeDevices, corev1.VolumeDevice{ + Name: volumeName, + DevicePath: diskName, + }) + + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvcName, + }, }, - }, - }) + }) + } } // Flatten the DisksBySize map to get a single slice of all disk names @@ -1032,7 +1067,7 @@ func CreateServiceAccountIfNotExists(instance *oraclerestart.OracleRestart, kCli } func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.OracleRestart) bool { - if instance.Spec.StorageClass != "" { + if CheckStorageClass(instance) == "NOSC" { return false } @@ -1065,7 +1100,7 @@ func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestar AccessModes: []corev1.PersistentVolumeAccessMode{ corev1.ReadWriteOnce, }, - StorageClassName: &instance.Spec.StorageClass, + StorageClassName: &instance.Spec.SwStorageClass, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(fmt.Sprintf("%dGi", OracleRestartSpex.SwLocStorageSizeInGb)), @@ -1075,7 +1110,7 @@ func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestar } } -func ASMVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) []corev1.PersistentVolumeClaim { +func ASMVolumeClaimTemplatesForDG(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, StorageClass string) []corev1.PersistentVolumeClaim { var claims []corev1.PersistentVolumeClaim mode := corev1.PersistentVolumeBlock // If user-provided PVC name exists, skip volume claim template creation @@ -1083,10 +1118,9 @@ func ASMVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleResta return claims } - index := 0 for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { - for range diskBySize.DiskNames { - pvcName := GetAsmPvcName(index, instance.Name) + for _, diskName := range diskBySize.DiskNames { + pvcName := GetAsmPvcName(instance.Name, diskName, instance) claims = append(claims, corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ @@ -1099,7 +1133,7 @@ func ASMVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleResta corev1.ReadWriteOnce, }, VolumeMode: &mode, - StorageClassName: &instance.Spec.StorageClass, + StorageClassName: &StorageClass, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(fmt.Sprintf("%dGi", diskBySize.StorageSizeInGb)), @@ -1107,7 +1141,6 @@ func ASMVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleResta }, }, }) - index++ } } diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 4d10f840..82647a85 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -320,17 +320,18 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // PV Creation - if len(oracleRestart.Spec.StorageClass) == 0 { + if oraclerestartcommon.CheckStorageClass(oracleRestart) == "NOSC" { if isNewSetup || isDiskChanged { if oracleRestart.Spec.AsmStorageDetails != nil { for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for index, diskName := range diskBySize.DiskNames { + for _, diskName := range diskBySize.DiskNames { + pvName := oraclerestartcommon.GetAsmPvName(oracleRestart.Name, diskName, oracleRestart) pvVolume := oraclerestartcommon.VolumePVForASM( oracleRestart, - index, diskName, diskBySize.StorageSizeInGb, oracleRestart.Spec.AsmStorageDetails, + pvName, r.Client, ) // if pvVolume == nil { @@ -352,13 +353,17 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques if isNewSetup || isDiskChanged { if oracleRestart.Spec.AsmStorageDetails != nil { for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for index, diskName := range diskBySize.DiskNames { + for _, diskName := range diskBySize.DiskNames { + dgType := oraclerestartcommon.CheckDiskInAsmDeviceList(oracleRestart, diskName) + pvcName := oraclerestartcommon.GetAsmPvcName(oracleRestart.Name, diskName, oracleRestart) pvcVolume := oraclerestartcommon.VolumePVCForASM( oracleRestart, - index, + diskBySize.StorageSizeInGb, diskName, diskBySize.StorageSizeInGb, oracleRestart.Spec.AsmStorageDetails, + pvcName, + dgType, r.Client, ) @@ -411,7 +416,7 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques err = nilErr return result, err } - if len(oracleRestart.Spec.StorageClass) == 0 { + if oraclerestartcommon.CheckStorageClass(oracleRestart) == "NOSC" { if ready, err := checkDaemonSetStatus(ctx, r, oracleRestart); err != nil || !ready { msg := "Any of provided ASM Disks are invalid, pls check disk-check daemon set for logs. Fix the asm disk to the valid one and redeploy." r.Log.Info(msg) @@ -426,17 +431,17 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques for _, disk := range addedAsmDisks { addedAsmDisksMap[disk] = true } - for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for index, diskName := range diskBySize.DiskNames { + for pindex, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for cindex, diskName := range diskBySize.DiskNames { if _, ok := addedAsmDisksMap[diskName]; ok { // r.Log.Info("Found disk at index", "index", index) - err = oraclerestartcommon.DelORestartPVC(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + err = oraclerestartcommon.DelORestartPVC(oracleRestart, pindex, cindex, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) if err != nil { return resultQ, err } - err = oraclerestartcommon.DelORestartPv(oracleRestart, index, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + err = oraclerestartcommon.DelORestartPv(oracleRestart, pindex, cindex, diskName, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) if err != nil { return resultQ, err } @@ -2814,10 +2819,10 @@ waitLoop: // Use oraclerestartcommon.GetAsmPvcName and oraclerestartcommon.getAsmPvName to generate PVC and PV names // Find and delete the corresponding PVC - for index, diskName := range oracleRestart.Status.OracleRestartNodes[index].NodeDetails.MountedDevices { + for _, diskName := range oracleRestart.Status.OracleRestartNodes[index].NodeDetails.MountedDevices { for _, removedAsmDisk := range removedAsmDisks { if diskName == removedAsmDisk { - pvcName := oraclerestartcommon.GetAsmPvcName(index, oracleRestart.Name) // Use the existing function + pvcName := oraclerestartcommon.GetAsmPvcName(oracleRestart.Name, diskName, oracleRestart) // Use the existing function pvc := &corev1.PersistentVolumeClaim{} err := r.Get(ctx, client.ObjectKey{ Name: pvcName, @@ -2839,7 +2844,7 @@ waitLoop: } // Find and delete the corresponding PV - pvName := oraclerestartcommon.GetAsmPvName(index, oracleRestart.Name) // Use the existing function + pvName := oraclerestartcommon.GetAsmPvName(oracleRestart.Name, diskName, oracleRestart) // Use the existing function pv := &corev1.PersistentVolume{} err = r.Get(ctx, client.ObjectKey{ Name: pvName, @@ -3136,9 +3141,9 @@ func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, if oracleRestart.Spec.AsmStorageDetails != nil { // Delete PVCs for each disk in DisksBySize - for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for index, disk := range diskBySize.DiskNames { - err = oraclerestartcommon.DelORestartPVC(oracleRestart, index, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + for pindex, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for cindex, disk := range diskBySize.DiskNames { + err = oraclerestartcommon.DelORestartPVC(oracleRestart, pindex, cindex, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) if err != nil { return err } @@ -3149,9 +3154,9 @@ func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, if oraclerestartcommon.IsStaticProvisioning(r.Client, oracleRestart) { if oracleRestart.Spec.AsmStorageDetails != nil { // Delete PVs for each disk in DisksBySize - for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for index, disk := range diskBySize.DiskNames { - err = oraclerestartcommon.DelORestartPv(oracleRestart, index, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + for pindex, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for cindex, disk := range diskBySize.DiskNames { + err = oraclerestartcommon.DelORestartPv(oracleRestart, pindex, cindex, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) if err != nil { return err } @@ -3457,9 +3462,9 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context storageClass := &storagev1.StorageClass{} pvc := &corev1.PersistentVolumeClaim{} - if instance.Spec.StorageClass != "" { + if instance.Spec.SwStorageClass != "" { - err := r.Get(ctx, types.NamespacedName{Name: instance.Spec.StorageClass}, storageClass) + err := r.Get(ctx, types.NamespacedName{Name: instance.Spec.SwStorageClass}, storageClass) if err != nil { return fmt.Errorf("error while fetching the storage class") } @@ -3475,7 +3480,7 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context if err == nil { if storageClass.AllowVolumeExpansion == nil || !*storageClass.AllowVolumeExpansion { r.Recorder.Eventf(instance, corev1.EventTypeWarning, "PVC not resizable", "The storage class doesn't support volume expansion") - return fmt.Errorf("the storage class %s doesn't support volume expansion", instance.Spec.StorageClass) + return fmt.Errorf("the storage class %s doesn't support volume expansion", instance.Spec.SwStorageClass) } newPVCSize := resource.MustParse(strconv.Itoa(instance.Spec.InstDetails.SwLocStorageSizeInGb) + "Gi") From ac952b314f713da83901dd70c32b9868bdc2bbf0 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 6 Oct 2025 17:55:49 +0000 Subject: [PATCH 372/414] Fixed Oracle Restart PDB Name --- controllers/database/oraclerestart_controller.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 82647a85..d3b88acf 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -1957,6 +1957,16 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or } } + if instance.Spec.ConfigParams.PdbName != "" { + data = append(data, "ORACLE_PDB_NAME="+instance.Spec.ConfigParams.PdbName) + } else { + if instance.Status.ConfigParams != nil { + if instance.Status.ConfigParams.PdbName != "" { + data = append(data, "ORACLE_PDB_NAME="+instance.Status.ConfigParams.PdbName) + } + } + } + if instance.Spec.ConfigParams.DbUniqueName != "" { // Configmap check is done in ValidateSpex data = append(data, "DB_UNIQUE_NAME="+instance.Spec.ConfigParams.DbUniqueName) From 8575e5fbe4f5f602877b9c4503226a95951475f1 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 6 Oct 2025 18:48:19 +0000 Subject: [PATCH 373/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 2 +- commons/oraclerestart/oraclerestartcommon.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 4390fcc8..7ed78806 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -122,7 +122,7 @@ type InitParams struct { DbDataFileDestDg string `json:"dbDataFileDestDg,omitempty"` CrsAsmDiskDgRedundancy string `json:"crsAsmDiskDgRedundancy,omitempty"` DBAsmDiskDgRedundancy string `json:"dbAsmDiskDgRedundancy,omitempty"` - RecoAsmDiskDgRedundancy string `json:"recoAsmDiskDgRedudancy,omitempty"` + RecoAsmDiskDgRedundancy string `json:"recoAsmDiskDgRedundancy,omitempty"` RedoAsmDiskDgRedudancy string `json:"redoAsmDiskDgRedundancy,omitempty"` DbName string `json:"dbName,omitempty"` PdbName string `json:"pdbName,omitempty"` diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index c232e5be..aff896f7 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -196,8 +196,8 @@ func GetAsmPvcName(name string, diskPath string, instance *oraclerestart.OracleR // pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name + "-" + dgType + "-" + "pvc" dgType := CheckDiskInAsmDeviceList(instance, diskPath) - diskName := diskPath[strings.LastIndex(diskPath, ",")+1:] - pvcName := "asm-dg-" + dgType + "-" + diskName + "-pvc-" + name + diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] + pvcName := "asm-pvc" + dgType + "-" + diskName + name return pvcName } @@ -205,8 +205,8 @@ func GetAsmPvcName(name string, diskPath string, instance *oraclerestart.OracleR func GetAsmPvName(name string, diskPath string, instance *oraclerestart.OracleRestart) string { dgType := CheckDiskInAsmDeviceList(instance, diskPath) - diskName := diskPath[strings.LastIndex(diskPath, ",")+1:] - pvName := "asm-dg-" + dgType + "-" + diskName + "-pv-" + name + diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] + pvName := "asm-pv-" + dgType + "-" + diskName + name return pvName } From acb300109ec19ad65ecf7bebbc34edb07655a8a2 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 6 Oct 2025 18:48:56 +0000 Subject: [PATCH 374/414] Added fixes --- commons/oraclerestart/oraclerestartcommon.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index aff896f7..619777ef 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -197,7 +197,7 @@ func GetAsmPvcName(name string, diskPath string, instance *oraclerestart.OracleR // pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name + "-" + dgType + "-" + "pvc" dgType := CheckDiskInAsmDeviceList(instance, diskPath) diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] - pvcName := "asm-pvc" + dgType + "-" + diskName + name + pvcName := "asm-pvc" + dgType + "-" + diskName + "-" + name return pvcName } @@ -206,7 +206,7 @@ func GetAsmPvName(name string, diskPath string, instance *oraclerestart.OracleRe dgType := CheckDiskInAsmDeviceList(instance, diskPath) diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] - pvName := "asm-pv-" + dgType + "-" + diskName + name + pvName := "asm-pv-" + dgType + "-" + diskName + "-" + name return pvName } From d8992e18de37e169d19563a2f6a570e0043ccf94 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 6 Oct 2025 21:01:10 +0000 Subject: [PATCH 375/414] Oracle Restat Fix --- apis/database/v4/oraclerestart_webhook.go | 57 ++++++++++++++++++++ commons/oraclerestart/oraclerestartcommon.go | 4 +- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index ac064717..eabb9eba 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -208,6 +208,9 @@ func (r *OracleRestart) ValidateCreate(ctx context.Context, obj runtime.Object) errs = cr.validateCrsAsmDG() validationErrs = append(validationErrs, errs...) + errs = cr.validateCrsAsmDG() + validationErrs = append(validationErrs, errs...) + if cr.Spec.ConfigParams != nil { // CRS validationErrs = append(validationErrs, @@ -591,8 +594,31 @@ func (r *OracleRestart) validateAsmStorage() field.ErrorList { } } + // Check ASM disks are not duplicate + if !r.validateAsmDiskUnqiueNames() { + validationErrs = append(validationErrs, + field.Invalid(asmPath.Child("DisksBySize"), asmPath, "Each ASM disk must be unique")) + + } + return validationErrs } + +func (r *OracleRestart) validateAsmDiskUnqiueNames() bool { + + seenDisks := make(map[string]bool) //store encounterednames + for _, disks := range r.Spec.AsmStorageDetails.DisksBySize { + for _, diskPath := range disks.DiskNames { + if seenDisks[diskPath] { + return false + } + seenDisks[diskPath] = true // disk is seen + } + } + + return true +} + func (r *OracleRestart) validateGeneric() field.ErrorList { var validationErrs field.ErrorList @@ -826,9 +852,40 @@ func (r *OracleRestart) validateUpdateServiceSpecs(old *OracleRestart) field.Err return validationErrs } + func (r *OracleRestart) validateUpdateAsmStorage(old *OracleRestart) field.ErrorList { var validationErrs field.ErrorList // Add actual validation logic here if needed + if !strings.EqualFold(old.Spec.CrsDgStorageClass, r.Spec.CrsDgStorageClass) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("CrsDgStorageClass"), + r.Spec.CrsDgStorageClass, "CrsDgStorageClass cannot be changed post creation")) + } + + if !strings.EqualFold(old.Spec.CrsDgStorageClass, r.Spec.CrsDgStorageClass) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("CrsDgStorageClass"), + r.Spec.CrsDgStorageClass, "CrsDgStorageClass cannot be changed post creation")) + } + + if !strings.EqualFold(old.Spec.DataDgStorageClass, r.Spec.DataDgStorageClass) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("DataDgStorageClass"), + r.Spec.CrsDgStorageClass, "DataDgStorageClass cannot be changed post creation")) + } + + if !strings.EqualFold(old.Spec.RedoDgStorageClass, r.Spec.RedoDgStorageClass) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("RedoDgStorageClass"), + r.Spec.CrsDgStorageClass, "RedoDgStorageClass cannot be changed post creation")) + } + + if !strings.EqualFold(old.Spec.RecoDgStorageClass, r.Spec.RecoDgStorageClass) { + validationErrs = append(validationErrs, + field.Invalid(field.NewPath("spec").Child("RecoDgStorageClass"), + r.Spec.CrsDgStorageClass, "RecoDgStorageClass cannot be changed post creation")) + } + return validationErrs } diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 619777ef..5402f263 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -197,7 +197,7 @@ func GetAsmPvcName(name string, diskPath string, instance *oraclerestart.OracleR // pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name + "-" + dgType + "-" + "pvc" dgType := CheckDiskInAsmDeviceList(instance, diskPath) diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] - pvcName := "asm-pvc" + dgType + "-" + diskName + "-" + name + pvcName := "asm-pvc" + strings.ToLower(dgType) + "-" + diskName + "-" + name return pvcName } @@ -206,7 +206,7 @@ func GetAsmPvName(name string, diskPath string, instance *oraclerestart.OracleRe dgType := CheckDiskInAsmDeviceList(instance, diskPath) diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] - pvName := "asm-pv-" + dgType + "-" + diskName + "-" + name + pvName := "asm-pv-" + strings.ToLower(dgType) + "-" + diskName + "-" + name return pvName } From 415c33e60274f185b648044df5b0478641c12ce5 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 6 Oct 2025 21:35:08 +0000 Subject: [PATCH 376/414] Added fixes --- commons/oraclerestart/oraclerestartcommon.go | 2 +- commons/oraclerestart/oraclerestartprov.go | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 5402f263..9a44d103 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -197,7 +197,7 @@ func GetAsmPvcName(name string, diskPath string, instance *oraclerestart.OracleR // pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name + "-" + dgType + "-" + "pvc" dgType := CheckDiskInAsmDeviceList(instance, diskPath) diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] - pvcName := "asm-pvc" + strings.ToLower(dgType) + "-" + diskName + "-" + name + pvcName := "asm-pvc-" + strings.ToLower(dgType) + "-" + diskName + "-" + name return pvcName } diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 8744f413..ea1a9c8e 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -140,19 +140,19 @@ func buildStatefulSpecForOracleRestart( } // Add volume claim templates if a storage class is specified if len(instance.Spec.DataDgStorageClass) != 0 && !asmPvcsExist(instance, kClient) { - sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, instance.Spec.DataDgStorageClass)...) + sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, &instance.Spec.DataDgStorageClass)...) } if len(instance.Spec.CrsDgStorageClass) != 0 && !asmPvcsExist(instance, kClient) { - sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, instance.Spec.CrsDgStorageClass)...) + sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, &instance.Spec.CrsDgStorageClass)...) } if len(instance.Spec.RecoDgStorageClass) != 0 && !asmPvcsExist(instance, kClient) { - sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, instance.Spec.RecoDgStorageClass)...) + sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, &instance.Spec.RecoDgStorageClass)...) } if len(instance.Spec.RedoDgStorageClass) != 0 && !asmPvcsExist(instance, kClient) { - sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, instance.Spec.RedoDgStorageClass)...) + sfsetspec.VolumeClaimTemplates = append(sfsetspec.VolumeClaimTemplates, ASMVolumeClaimTemplatesForDG(instance, OracleRestartSpex, &instance.Spec.RedoDgStorageClass)...) } if len(instance.Spec.SwStorageClass) != 0 && len(instance.Spec.InstDetails.HostSwLocation) == 0 { @@ -1110,7 +1110,7 @@ func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestar } } -func ASMVolumeClaimTemplatesForDG(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, StorageClass string) []corev1.PersistentVolumeClaim { +func ASMVolumeClaimTemplatesForDG(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec, StorageClass *string) []corev1.PersistentVolumeClaim { var claims []corev1.PersistentVolumeClaim mode := corev1.PersistentVolumeBlock // If user-provided PVC name exists, skip volume claim template creation @@ -1118,6 +1118,8 @@ func ASMVolumeClaimTemplatesForDG(instance *oraclerestart.OracleRestart, OracleR return claims } + fmt.Printf("INFO", "working on asm storage class "+*StorageClass) + for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { for _, diskName := range diskBySize.DiskNames { pvcName := GetAsmPvcName(instance.Name, diskName, instance) @@ -1133,7 +1135,7 @@ func ASMVolumeClaimTemplatesForDG(instance *oraclerestart.OracleRestart, OracleR corev1.ReadWriteOnce, }, VolumeMode: &mode, - StorageClassName: &StorageClass, + StorageClassName: StorageClass, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(fmt.Sprintf("%dGi", diskBySize.StorageSizeInGb)), From f557ba30af64a0266de67e01d4eaa164e2d22c91 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Mon, 6 Oct 2025 22:16:29 +0000 Subject: [PATCH 377/414] Added fixes --- .../database/oraclerestart_controller.go | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index d3b88acf..5b5f6697 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -347,30 +347,30 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } } } - } - // PVC Creation - if isNewSetup || isDiskChanged { - if oracleRestart.Spec.AsmStorageDetails != nil { - for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for _, diskName := range diskBySize.DiskNames { - dgType := oraclerestartcommon.CheckDiskInAsmDeviceList(oracleRestart, diskName) - pvcName := oraclerestartcommon.GetAsmPvcName(oracleRestart.Name, diskName, oracleRestart) - pvcVolume := oraclerestartcommon.VolumePVCForASM( - oracleRestart, - diskBySize.StorageSizeInGb, - diskName, - diskBySize.StorageSizeInGb, - oracleRestart.Spec.AsmStorageDetails, - pvcName, - dgType, - r.Client, - ) - - _, result, err = r.createOrReplaceAsmPvC(ctx, oracleRestart, pvcVolume) - if err != nil { - result = resultNq - return result, err + // PVC Creation + if isNewSetup || isDiskChanged { + if oracleRestart.Spec.AsmStorageDetails != nil { + for _, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for _, diskName := range diskBySize.DiskNames { + dgType := oraclerestartcommon.CheckDiskInAsmDeviceList(oracleRestart, diskName) + pvcName := oraclerestartcommon.GetAsmPvcName(oracleRestart.Name, diskName, oracleRestart) + pvcVolume := oraclerestartcommon.VolumePVCForASM( + oracleRestart, + diskBySize.StorageSizeInGb, + diskName, + diskBySize.StorageSizeInGb, + oracleRestart.Spec.AsmStorageDetails, + pvcName, + dgType, + r.Client, + ) + + _, result, err = r.createOrReplaceAsmPvC(ctx, oracleRestart, pvcVolume) + if err != nil { + result = resultNq + return result, err + } } } } From 036cc0065e38e32c8ae2b89194838e2a329ddcea Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 7 Oct 2025 01:06:31 +0000 Subject: [PATCH 378/414] Added fixes --- apis/database/v4/oraclerestart_types.go | 3 +- apis/database/v4/oraclerestart_webhook.go | 2 +- .../database/oraclerestart_controller.go | 51 +++++++++++-------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/apis/database/v4/oraclerestart_types.go b/apis/database/v4/oraclerestart_types.go index 7ed78806..b0ae127d 100644 --- a/apis/database/v4/oraclerestart_types.go +++ b/apis/database/v4/oraclerestart_types.go @@ -123,13 +123,14 @@ type InitParams struct { CrsAsmDiskDgRedundancy string `json:"crsAsmDiskDgRedundancy,omitempty"` DBAsmDiskDgRedundancy string `json:"dbAsmDiskDgRedundancy,omitempty"` RecoAsmDiskDgRedundancy string `json:"recoAsmDiskDgRedundancy,omitempty"` - RedoAsmDiskDgRedudancy string `json:"redoAsmDiskDgRedundancy,omitempty"` + RedoAsmDiskDgRedundancy string `json:"redoAsmDiskDgRedundancy,omitempty"` DbName string `json:"dbName,omitempty"` PdbName string `json:"pdbName,omitempty"` DbStorageType string `json:"dbStorageType,omitempty"` DbAsmDeviceList string `json:"dbAsmDeviceList,omitempty"` RecoAsmDeviceList string `json:"recoAsmDeviceList,omitempty"` RedoAsmDeviceList string `json:"redoAsmDeviceList,omitempty"` + RedoAsmDiskDg string `json:"redoAsmDiskDg,omitempty"` DbCharSet string `json:"dbCharSet,omitempty"` DbRedoFileSize string `json:"dbRedoFileSize,omitempty"` DbType string `json:"dbType,omitempty"` diff --git a/apis/database/v4/oraclerestart_webhook.go b/apis/database/v4/oraclerestart_webhook.go index eabb9eba..51e6a7a0 100644 --- a/apis/database/v4/oraclerestart_webhook.go +++ b/apis/database/v4/oraclerestart_webhook.go @@ -117,7 +117,7 @@ func (r *OracleRestart) Default(ctx context.Context, obj runtime.Object) error { if cr.Spec.ConfigParams.GridResponseFile.ConfigMapName == "" { if cr.Spec.ConfigParams.CrsAsmDiskDg == "" { - cr.Spec.ConfigParams.CrsAsmDiskDg = "+DATA" + cr.Spec.ConfigParams.CrsAsmDiskDg = "DATA" } if cr.Spec.ConfigParams.CrsAsmDiskDgRedundancy == "" { cr.Spec.ConfigParams.CrsAsmDiskDgRedundancy = "external" diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 5b5f6697..a2890d06 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -2129,6 +2129,11 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or data = append(data, "DB_RECOVERY_FILE_DEST="+instance.Spec.ConfigParams.DbRecoveryFileDest) } + if instance.Spec.ConfigParams.RedoAsmDiskDg != "" { + // Configmap check is done in ValidateSpex + data = append(data, "LOG_FILE_DEST="+instance.Spec.ConfigParams.RedoAsmDiskDg) + } + if instance.Spec.ConfigParams.DbRecoveryFileDestSize != "" { // Configmap check is done in ValidateSpex data = append(data, "DB_RECOVERY_FILE_DEST_SIZE="+instance.Spec.ConfigParams.DbRecoveryFileDestSize) @@ -2137,8 +2142,8 @@ func (r *OracleRestartReconciler) generateConfigMap(instance *oraclerestartdb.Or data = append(data, "DB_ASMDG_PROPERTIES="+"redundancy:"+instance.Spec.ConfigParams.DBAsmDiskDgRedundancy) } - if instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy != "" { - data = append(data, "REDO_ASMDG_PROPERTIES="+"redundancy:"+instance.Spec.ConfigParams.RedoAsmDiskDgRedudancy) + if instance.Spec.ConfigParams.RedoAsmDiskDgRedundancy != "" { + data = append(data, "REDO_ASMDG_PROPERTIES="+"redundancy:"+instance.Spec.ConfigParams.RedoAsmDiskDgRedundancy) } if instance.Spec.ConfigParams.RecoAsmDiskDgRedundancy != "" { @@ -2896,7 +2901,7 @@ waitLoop: } if isDiskInDeviceList(disk, oracleRestart.Spec.ConfigParams.RedoAsmDeviceList) { reqLogger.Info("New disk to be added to REDO ASM device list ", "disk", disk) - deviceDg = oracleRestart.Spec.ConfigParams.RedoAsmDiskDgRedudancy + deviceDg = oracleRestart.Spec.ConfigParams.RedoAsmDiskDgRedundancy } } if deviceDg != "" { @@ -3118,8 +3123,10 @@ func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, } } - if err := oraclerestartcommon.DelRestartSwPvc(oracleRestart, oraRestartSpex, r.Client, r.Log); err != nil { - return err + if !utils.CheckStatusFlag(oraRestartSpex.IsKeepPVC) { + if err := oraclerestartcommon.DelRestartSwPvc(oracleRestart, oraRestartSpex, r.Client, r.Log); err != nil { + return err + } } // // Deleting the DaemonSet @@ -3149,24 +3156,12 @@ func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, } } - if oracleRestart.Spec.AsmStorageDetails != nil { - // Delete PVCs for each disk in DisksBySize - for pindex, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { - for cindex, disk := range diskBySize.DiskNames { - err = oraclerestartcommon.DelORestartPVC(oracleRestart, pindex, cindex, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) - if err != nil { - return err - } - } - } - } - - if oraclerestartcommon.IsStaticProvisioning(r.Client, oracleRestart) { + if !utils.CheckStatusFlag(oraRestartSpex.IsKeepPVC) { if oracleRestart.Spec.AsmStorageDetails != nil { - // Delete PVs for each disk in DisksBySize + // Delete PVCs for each disk in DisksBySize for pindex, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { for cindex, disk := range diskBySize.DiskNames { - err = oraclerestartcommon.DelORestartPv(oracleRestart, pindex, cindex, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + err = oraclerestartcommon.DelORestartPVC(oracleRestart, pindex, cindex, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) if err != nil { return err } @@ -3175,6 +3170,22 @@ func (r *OracleRestartReconciler) cleanupOracleRestart(req ctrl.Request, } } + if !utils.CheckStatusFlag(oraRestartSpex.IsKeepPVC) { + if oraclerestartcommon.IsStaticProvisioning(r.Client, oracleRestart) { + if oracleRestart.Spec.AsmStorageDetails != nil { + // Delete PVs for each disk in DisksBySize + for pindex, diskBySize := range oracleRestart.Spec.AsmStorageDetails.DisksBySize { + for cindex, disk := range diskBySize.DiskNames { + err = oraclerestartcommon.DelORestartPv(oracleRestart, pindex, cindex, disk, oracleRestart.Spec.AsmStorageDetails, r.Client, r.Log) + if err != nil { + return err + } + } + } + } + } + } + svcTypes := []string{"local", "lbservice", "nodeport"} for _, svcType := range svcTypes { svcFound, err := oraclerestartcommon.CheckORestartSvc(oracleRestart, svcType, oraRestartSpex, "", r.Client) From 99b88b2d1b4a39c53350696359be699aa5b28f79 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 7 Oct 2025 03:30:45 +0000 Subject: [PATCH 379/414] Added fixes --- commons/oraclerestart/oraclerestartcommon.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 9a44d103..77b0f0f5 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -197,7 +197,7 @@ func GetAsmPvcName(name string, diskPath string, instance *oraclerestart.OracleR // pvcName := "asm-pvc-disk-" + strconv.Itoa(index) + "-" + name + "-" + dgType + "-" + "pvc" dgType := CheckDiskInAsmDeviceList(instance, diskPath) diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] - pvcName := "asm-pvc-" + strings.ToLower(dgType) + "-" + diskName + "-" + name + pvcName := "asm-pvc-" + strings.ToLower(dgType) + "-" + diskName + "-" + name + "-" + instance.Spec.InstDetails.Name + "-0" return pvcName } @@ -206,7 +206,7 @@ func GetAsmPvName(name string, diskPath string, instance *oraclerestart.OracleRe dgType := CheckDiskInAsmDeviceList(instance, diskPath) diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] - pvName := "asm-pv-" + strings.ToLower(dgType) + "-" + diskName + "-" + name + pvName := "asm-pv-" + strings.ToLower(dgType) + "-" + diskName + "-" + name + instance.Spec.InstDetails.Name + "-0" return pvName } From c7531d5790f0dd8b3cf4861f1afafb265238bb7c Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 7 Oct 2025 04:34:53 +0000 Subject: [PATCH 380/414] Added fixes --- bundle/metadata/annotations.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml index cf18a9f1..8a5ae70d 100644 --- a/bundle/metadata/annotations.yaml +++ b/bundle/metadata/annotations.yaml @@ -4,7 +4,7 @@ annotations: operators.operatorframework.io.bundle.manifests.v1: manifests/ operators.operatorframework.io.bundle.metadata.v1: metadata/ operators.operatorframework.io.bundle.package.v1: oracle-database-operator - operators.operatorframework.io.bundle.channels.v1: alpha - operators.operatorframework.io.metrics.builder: operator-sdk-v1.39.2 + operators.operatorframework.io.bundle.channels.v1: stable + operators.operatorframework.io.metrics.builder: operator-sdk-v1.40.0 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 From 8bcc4ece52e363bf5724011c981494be2c79fdd7 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 7 Oct 2025 14:58:32 +0000 Subject: [PATCH 381/414] Added fix for PVC name --- commons/oraclerestart/oraclerestartcommon.go | 2 +- commons/oraclerestart/oraclerestartprov.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index 77b0f0f5..b37da658 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -206,7 +206,7 @@ func GetAsmPvName(name string, diskPath string, instance *oraclerestart.OracleRe dgType := CheckDiskInAsmDeviceList(instance, diskPath) diskName := diskPath[strings.LastIndex(diskPath, "/")+1:] - pvName := "asm-pv-" + strings.ToLower(dgType) + "-" + diskName + "-" + name + instance.Spec.InstDetails.Name + "-0" + pvName := "asm-pv-" + strings.ToLower(dgType) + "-" + diskName + "-" + name + "-" + instance.Spec.InstDetails.Name + "-0" return pvName } diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index ea1a9c8e..28e8d997 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -1122,7 +1122,10 @@ func ASMVolumeClaimTemplatesForDG(instance *oraclerestart.OracleRestart, OracleR for _, diskBySize := range instance.Spec.AsmStorageDetails.DisksBySize { for _, diskName := range diskBySize.DiskNames { - pvcName := GetAsmPvcName(instance.Name, diskName, instance) + // The folowing peice of code is generating ASM PVC name because by default VolumeCLaim Template add Instance name like -dbmc1-0 + dgType := CheckDiskInAsmDeviceList(instance, diskName) + disk := diskName[strings.LastIndex(diskName, "/")+1:] + pvcName := "asm-pvc-" + strings.ToLower(dgType) + "-" + disk + "-" + instance.Name claims = append(claims, corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ From c8d3cb22f30c036680255651fd1fb800d88368d6 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Tue, 7 Oct 2025 16:46:02 +0000 Subject: [PATCH 382/414] Added fixes --- commons/oraclerestart/oraclerestartprov.go | 33 +++++++++--------- .../database/oraclerestart_controller.go | 34 ++++++++++--------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index 28e8d997..e4432ef3 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -936,27 +936,26 @@ func BuildDiskCheckDaemonSet(OracleRestart *oraclerestart.OracleRestart) *appsv1 // Prepare the volume devices based on the PVCs var volumeDevices []corev1.VolumeDevice var volumes []corev1.Volume - //disks := flattenDisksBySize(&OracleRestart.Spec) + disks := flattenDisksBySize(&OracleRestart.Spec) - for _, diskBySize := range OracleRestart.Spec.AsmStorageDetails.DisksBySize { - for _, diskName := range diskBySize.DiskNames { - pvcName := GetAsmPvcName(OracleRestart.Name, diskName, OracleRestart) - volumeName := pvcName + for _, diskPath := range disks { + pvcName := GetAsmPvcName(OracleRestart.Name, diskPath, OracleRestart) + volumeName := pvcName - volumeDevices = append(volumeDevices, corev1.VolumeDevice{ - Name: volumeName, - DevicePath: diskName, - }) + volumeDevices = append(volumeDevices, corev1.VolumeDevice{ + Name: volumeName, + DevicePath: diskPath, + }) - volumes = append(volumes, corev1.Volume{ - Name: volumeName, - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: pvcName, - }, + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvcName, }, - }) - } + }, + }) + } // Flatten the DisksBySize map to get a single slice of all disk names diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index a2890d06..2e9e51f0 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -408,15 +408,15 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } case isDiskChanged && !isNewSetup: - if len(addedAsmDisks) > 0 { - err = r.validateASMDisks(oracleRestart, ctx) - if err != nil { - result = resultQ - r.Log.Info(err.Error()) - err = nilErr - return result, err - } - if oraclerestartcommon.CheckStorageClass(oracleRestart) == "NOSC" { + if oraclerestartcommon.CheckStorageClass(oracleRestart) == "NOSC" { + if len(addedAsmDisks) > 0 { + err = r.validateASMDisks(oracleRestart, ctx) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } if ready, err := checkDaemonSetStatus(ctx, r, oracleRestart); err != nil || !ready { msg := "Any of provided ASM Disks are invalid, pls check disk-check daemon set for logs. Fix the asm disk to the valid one and redeploy." r.Log.Info(msg) @@ -485,14 +485,16 @@ func (r *OracleRestartReconciler) Reconcile(ctx context.Context, req ctrl.Reques } } } - if len(addedAsmDisks) > 0 { + if oraclerestartcommon.CheckStorageClass(oracleRestart) == "NOSC" { + if len(addedAsmDisks) > 0 { - err = r.cleanupDaemonSet(oracleRestart, ctx) - if err != nil { - result = resultQ - r.Log.Info(err.Error()) - err = nilErr - return result, err + err = r.cleanupDaemonSet(oracleRestart, ctx) + if err != nil { + result = resultQ + r.Log.Info(err.Error()) + err = nilErr + return result, err + } } } From 99d1a0c0a901d41240f987873c6f7390ace1796b Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 16:42:06 +0000 Subject: [PATCH 383/414] Update README.md minor style edit. --- docs/adb/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/adb/README.md b/docs/adb/README.md index 4b2412ef..15e78a2c 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -2,11 +2,14 @@ Before you use the Oracle Database Operator for Kubernetes (the operator), ensure that your system meets all of the Oracle Autonomous Database (ADB) Prerequisites [ADB_PREREQUISITES](./ADB_PREREQUISITES.md). -As indicated in the prerequisites (see above), to interact with OCI services, either the cluster must be authorized using Principal Instance, or the cluster must be authorized using the API Key Authentication by specifying the configMap and the secret under the `ociConfig` field. +To allow your Kubernetes cluster to interact with OCI services, your cluster must be authorized with one of the following: +- Instance Principal authentication +- API Key Authentication (specify the required configMap and Secret under `ociConfig`). + ## Required Permissions -The operator must be given the required type of access in a policy written by an administrator to manage the Autonomous Databases. For examples of Autonomous Database policies, see: [Let database and fleet admins manage Autonomous Databases](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/commonpolicies.htm#db-admins-manage-adb) +The operator requires appropriate OCI policies, written by an administrator, to manage Autonomous Databases. For examples of Autonomous Database policies, see: [Let database and fleet admins manage Autonomous Databases](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/commonpolicies.htm#db-admins-manage-adb) Permissions to view the work requests are also required, so that the operator can update the resources when the work is done. For example work request policies, see: [Viewing Work Requests](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengviewingworkrequests.htm#contengviewingworkrequests) From 3e72c3b55716631bb2d740c030354babeb8fd4ac Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 16:49:44 +0000 Subject: [PATCH 384/414] Update bind_to_existing_dbcs_system.md Minor edits. --- .../provisioning/bind_to_existing_dbcs_system.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md index eced7538..0cad1db4 100644 --- a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md @@ -1,25 +1,25 @@ # Binding to an existing OBDS System already deployed in OCI Oracle Base Database Service -In this use case, we bind the Oracle DB Operator OBDS Controller to an existing OCI OBDS System which has already been deployed earlier. This will help to manage the life cycle of that OBDS System using the Oracle DB Operator OBDS Controller. +In this use case, we bind the Oracle DB Operator OBDS Controller to an existing OCI OBDS System that has already been deployed. After you bind the Controller, you are able to use it to manage the lifecycle of that OBDS System. **NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `bind_to_existing_dbcs_system.yaml` to bind to an existing OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `bind_to_existing_dbcs_system.yaml` to bind to an existing OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCI Configmap as `oci-cred-mumbai` - OCI Secret as `oci-privatekey` - OCID of the existing OBDS System as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` -Use the file: [bind_to_existing_dbcs_system.yaml](./bind_to_existing_dbcs_system.yaml) for this use case as below: +Use the file: [bind_to_existing_dbcs_system.yaml](./bind_to_existing_dbcs_system.yaml) for this use case, as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```bash kubectl apply -f bind_to_existing_dbcs_system.yaml dbcssystem.database.oracle.com/dbcssystem-existing created ``` -2. Monitor the Oracle DB Leader Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB deployment. +2. Monitor the Oracle DB Leader Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` to follow the progress of the OBDS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. @@ -29,4 +29,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./bind_to_existing_dbcs_system_sample_output.log) is the sample output for binding to an existing OBDS System already deployed in OCI using Oracle DB Operator OBDS Controller. +[Here](./bind_to_existing_dbcs_system_sample_output.log) is an example of the output for binding to an existing OBDS System already deployed in OCI using Oracle DB Operator OBDS Controller. From 6df2e4deb0972b20671cfaca09502f1ae79be40f Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 16:52:48 +0000 Subject: [PATCH 385/414] Update clone_from_backup_dbcs.md Minor edits --- docs/dbcs/provisioning/clone_from_backup_dbcs.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/dbcs/provisioning/clone_from_backup_dbcs.md b/docs/dbcs/provisioning/clone_from_backup_dbcs.md index 4597cff7..fbc2ad5e 100644 --- a/docs/dbcs/provisioning/clone_from_backup_dbcs.md +++ b/docs/dbcs/provisioning/clone_from_backup_dbcs.md @@ -1,8 +1,8 @@ # Clone DB System from Backup of Existing DB System in OCI Oracle Base Database System (OBDS) -In this use case, an existing OCI OBDS system deployed earlier with the Backup is going to be cloned. +In this use case, you can see how to clone an existing OCI OBDS system deployed earlier with the Backup. -In order to clone OBDS to an existing OBDS system using Backup, get the details of OCID of backup in OCI OBDS. +To clone OBDS to an existing OBDS system using Backup, you must obtain the OCID details for the backup in OCI OBDS. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. @@ -15,15 +15,15 @@ This example uses `clone_dbcs_system_from_backup.yaml` to clone a Single Instanc - Specification for DB Cloning as `dbClone`-> `dbAdminPasswordSecret`,`tdeWalletPasswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`, `initialDataStorageSizeInGB` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). -Use the file: [clone_dbcs_system_from_backup.yaml](./clone_dbcs_system_from_backup.yaml) for this use case as below: +Use the file: [clone_dbcs_system_from_backup.yaml](./clone_dbcs_system_from_backup.yaml) for this use case, as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@docker-test-server OBDS]# kubectl apply -f clone_dbcs_system_from_backup.yaml dbcssystem.database.oracle.com/dbcssystem-clone created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of PDBs. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` to track the progress of the OBDS VMDB creation of PDBs. NOTE: Check the DB Operator Pod name in your environment. @@ -33,4 +33,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./clone_dbcs_system_from_backup_sample_output.log) is the sample output for cloning an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. +[Here](./clone_dbcs_system_from_backup_sample_output.log) is an example output log for cloning an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. From e614226cd18eb433f89c1106d361d67f782c660e Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 16:55:45 +0000 Subject: [PATCH 386/414] Update dataguard_to_database.md minor edits. --- docs/dbcs/provisioning/dataguard_to_database.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/dbcs/provisioning/dataguard_to_database.md b/docs/dbcs/provisioning/dataguard_to_database.md index a8bc1096..67c923b6 100644 --- a/docs/dbcs/provisioning/dataguard_to_database.md +++ b/docs/dbcs/provisioning/dataguard_to_database.md @@ -1,8 +1,8 @@ # Setup Dataguard Association to Existing Database of DB System in OCI Base DBCS Service -In this use case, an existing OCI DBCS system deployed earlier with existing Database is going to have dataguard association in OCI Base DBCS Service using existing Database ID. +In this use case, an existing OCI DBCS system previously deployed with an existing Database is provided with an Oracle Data Guard association in OCI Base DBCS Service using the existing Database ID. -As an pre-requisite, get the details of OCID of database of an existing DBCS System which you want to clone. +As a prerequisite, obtain the OCID details for the database of the existing DBCS System that you want to clone. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. @@ -14,13 +14,13 @@ This example uses `dataguard_in_database.yaml` to clone a Single Instance DBCS V Use the file: [dataguard_in_database.yaml](./dataguard_in_database.yaml) for this use case as below: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@docker-test-server DBCS]# kubectl apply -f dataguard_in_database.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` to follow the progress of the DBCS VMDB creation of PDBs. NOTE: Check the DB Operator Pod name in your environment. @@ -30,4 +30,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./dataguard_in_database_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +[Here](./dataguard_in_database_sample_output.log) is the example output log for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. From 0da42b84db0f30f11c901c9250eebd9c0cc17d25 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:02:01 +0000 Subject: [PATCH 387/414] Update patching_database.md minor style edits. --- docs/dbcs/provisioning/patching_database.md | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/dbcs/provisioning/patching_database.md b/docs/dbcs/provisioning/patching_database.md index f8ea1182..cfee83d9 100644 --- a/docs/dbcs/provisioning/patching_database.md +++ b/docs/dbcs/provisioning/patching_database.md @@ -1,42 +1,42 @@ # Patching Existing Database of DB System in OCI Base DBCS Service -In this use case, an existing OCI OBDS system deployed earlier is going to be patched in OCI Oracle Base Database System (OBDS). Its a 2 Step operation. +In this use case, see how an existing OCI OBDS system deployed earlier can be patched using the OCI Oracle Base Database System (OBDS). This is a two-step operation. -In order to patch OBDS to an existing OBDS system, get the OCID of DB System ID you want to patch. +To patch OBDS to an existing OBDS system, obtain the OCID of the database system ID that you want to patch. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will appear as follows: ```bash kubectl get dbcssystems NAME AGE dbcssystem-existing 3m33s ``` -Step 2 uses `patch_dbcs_system.yaml` to patch a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +Step 2 uses `patch_dbcs_system.yaml` to patch a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: -- OCID of existing VMDB as `id` to be patched. +- OCID of the existing VMDB as `id` to be patched. - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - `isPatch` as true -- Specification of DB System been patched as `dbPatchOcid`. These must be unique and new details for new patched DB system to be created. -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). +- Specification of the database system that you are patching as `dbPatchOcid`. The OCIDs must be unique, and you must provide new details for new patched DB system that are to be created. +**NOTE:** For the details of the parameters to be used in the `.yaml` file, see: [here](./dbcs_controller_parameters.md). -Use the file: [patch_dbcs_system.yaml](./patch_dbcs_system.yaml) for this use case as below: +Use the file: [patch_dbcs_system.yaml](./patch_dbcs_system.yaml) for this use case as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@docker-test-server DBCS]# kubectl apply -f patch_dbcs_system.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` to follow the progress of the DBCS VMDB creation of PDBs. NOTE: Check the DB Operator Pod name in your environment. ``` [root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` -3. Check details of kubernetes object post patching is complete and verify patched version is same as expected- +3. Check details of the Kubernetes object post patching to ensure that it is complete, and verify that the patched version is as expected: ```bash kubectl describe dbcssystems.database.oracle.com dbcssystem-existing From af5066f459d21b629c5ffd4133f700ff3d2c8ed4 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:12:08 +0000 Subject: [PATCH 388/414] Update restore_of_database.md minor style edits. --- docs/dbcs/provisioning/restore_of_database.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/dbcs/provisioning/restore_of_database.md b/docs/dbcs/provisioning/restore_of_database.md index 4218d0f5..3630259d 100644 --- a/docs/dbcs/provisioning/restore_of_database.md +++ b/docs/dbcs/provisioning/restore_of_database.md @@ -1,19 +1,19 @@ # Restore from Backup of Existing Database of DB System in OCI OBDS Service -In this use case, an existing OCI OBDS system deployed earlier with existing backup of Database is going to have restore in OCI Base OBDS Service using existing DB System Id. +In this use case, an existing OCI OBDS system deployed earlier with existing backup of Database is configured to use restore in OCI Base OBDS Service with an existing database system ID. -As an pre-requisite, get the details of OCID of database of an existing OBDS System which you want to backup. +As an prerequisite, obtain the OCID details for the database of an existing OBDS System that you want to back up. **NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. -This example uses `restore_of_database.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `restore_of_database.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCID of existing as DB System as `id` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -- Restore Configuration of taking restore from a backup. Provided one of `latest` , `scn` and `timestamp` under `restoreConfig` to restore to. Do not provided more than one option to restore from. -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). +- Restore Configuration with the restore taken from a backup. To perform the restoration, the backup is provided with one of `latest` , `scn` and `timestamp` under `restoreConfig`. For restorations, do not provide more than one option. +**NOTE:** For the details of the parameters to be used in the `.yaml` file, see [dBCS Controller Parameters](./dbcs_controller_parameters.md). -Use the file: [restore_of_database.yaml](./restore_of_database.yaml) for this use case as below: +Use the file: [restore_of_database.yaml](./restore_of_database.yaml) for this use case, as described in the following steps: 1. Deploy the .yaml file: ```sh @@ -31,4 +31,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./restore_database_sample_output.log) is the sample output of restore from an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. \ No newline at end of file +[Here](./restore_database_sample_output.log) is an example output log of a restore from an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. \ No newline at end of file From 620d854e81b5dee3d99dbcb9584c0f4142c31846 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:15:26 +0000 Subject: [PATCH 389/414] Update scale_up_dbcs_system_shape.md minor style edits. --- .../provisioning/scale_up_dbcs_system_shape.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md index 924a8517..b960ba54 100644 --- a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md @@ -1,8 +1,8 @@ # Scale UP the shape of an existing OBDS System -In this use case, an existing OCI OBDS system deployed earlier is scaled up for its shape using Oracle DB Operator OBDS controller. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is scaled up for its shape using Oracle DB Operator OBDS controller. This is a two-step operation. -In order to scale up an existing OBDS system, the steps will be: +To scale up an existing OBDS system, the two steps are: 1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to scale up its shape. @@ -22,17 +22,17 @@ This example uses `scale_up_dbcs_system_shape.yaml` to scale up a Single Instanc - SSH Public key for the OBDS system being deployed as `oci-publickey` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). +**NOTE:** For the details of the parameters to be used in the `.yaml` file, see: [DBCS Controller Parameters](./dbcs_controller_parameters.md). -Use the file: [scale_up_dbcs_system_shape.yaml](./scale_up_dbcs_system_shape.yaml) for this use case as below: +Use the file: [scale_up_dbcs_system_shape.yaml](./scale_up_dbcs_system_shape.yaml) for this use case as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@docker-test-server OBDS]# kubectl apply -f scale_up_dbcs_system_shape.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB Scale up. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` to see the progress of the OBDS VMDB Scale up. NOTE: Check the DB Operator Pod name in your environment. @@ -42,4 +42,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./scale_up_dbcs_system_shape_sample_output.log) is the sample output for scaling up the shape of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. +[Here](./scale_up_dbcs_system_shape_sample_output.log) is an example log output for scaling up the shape of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. From e4574692c61da8d1bc8a439f404df8d98a163521 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:16:26 +0000 Subject: [PATCH 390/414] Update scale_up_dbcs_system_shape.md --missed another "here" link. --- docs/dbcs/provisioning/scale_up_dbcs_system_shape.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md index b960ba54..5ec4f3da 100644 --- a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md @@ -42,4 +42,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./scale_up_dbcs_system_shape_sample_output.log) is an example log output for scaling up the shape of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. +[this log file](./scale_up_dbcs_system_shape_sample_output.log) is an example log output for scaling up the shape of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. From 934b53679510b531aefcfc7944700229e85cda6f Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:21:23 +0000 Subject: [PATCH 391/414] Update terminate_dbcs_system.md minor style edits. --- .../provisioning/terminate_dbcs_system.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/dbcs/provisioning/terminate_dbcs_system.md b/docs/dbcs/provisioning/terminate_dbcs_system.md index f3b19cbc..8dae2818 100644 --- a/docs/dbcs/provisioning/terminate_dbcs_system.md +++ b/docs/dbcs/provisioning/terminate_dbcs_system.md @@ -1,25 +1,25 @@ # Terminate an existing Oracle Base Database System (OBDS) -In this use case, an existing OCI OBDS system deployed earlier is terminated using Oracle DB Operator OBDS controller. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is terminated using Oracle DB Operator OBDS controller. This is a two-step operation. -In order to terminate an existing OBDS system, the steps will be: +In order to terminate an existing OBDS system, the two steps are as follows: 1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to terminate this OBDS System. -**NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE** We are assuming that before this step, you have followed the [prerequisite steps](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) to create the configmap and the secrets required during the deployment. -This example uses `terminate_dbcs_system.yaml` to terminated a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `terminate_dbcs_system.yaml` to terminated a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). +**NOTE:** For the details of the parameters to be used in the `.yaml` file, see [DBCS Controller Parameters](./dbcs_controller_parameters.md). -Use the file: [terminate_dbcs_system.yaml](./terminate_dbcs_system.yaml) for this use case as below: +Use the file: [terminate_dbcs_system.yaml](./terminate_dbcs_system.yaml) for this use case as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@test-server OBDS]# kubectl apply -f terminate_dbcs_system.yaml dbcssystem.database.oracle.com/dbcssystem-terminate created @@ -29,13 +29,13 @@ dbcssystem.database.oracle.com/dbcssystem-terminate created dbcssystem.database.oracle.com "dbcssystem-terminate" deleted ``` -2. Check the logs of Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for an update on the terminate operation been accepted. +2. Check the logs of Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for an update on the terminate operation to confirm it has been accepted. ``` [root@test-server OBDS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` -3. Check and confirm if the existing OCI OBDS system is NO longer available after sometime because of termination: +3. Give some time for the termination operation to be completed, and then check and confirm if the existing OCI OBDS system is no longer available: ``` [root@test-server OBDS]# kubectl describe dbcssystems.database.oracle.com dbcssystem-terminate @@ -43,4 +43,4 @@ dbcssystem.database.oracle.com "dbcssystem-terminate" deleted ## Sample Output -[Here](./terminate_dbcs_system_sample_output.log) is the sample output for terminating an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with minimal parameters. +[This example log](./terminate_dbcs_system_sample_output.log) is an example output log for terminating an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with minimal parameters. From f0d3dc83b507d7539282571dc09ed7189111eb62 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:25:17 +0000 Subject: [PATCH 392/414] Update delete_pdb.md minor style edits. --- docs/dbcs/provisioning/delete_pdb.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/dbcs/provisioning/delete_pdb.md b/docs/dbcs/provisioning/delete_pdb.md index 84d676bc..b9bb8b8d 100644 --- a/docs/dbcs/provisioning/delete_pdb.md +++ b/docs/dbcs/provisioning/delete_pdb.md @@ -1,32 +1,32 @@ # Delete PDB of an existing DBCS System -In this use case, an existing OCI DBCS system deployed earlier is going to have PDB/PDBs deleted. Its a 2 Step operation. +In this use case, an existing OCI DBCS system deployed earlier is going to have PDB/PDBs deleted. This is a two-step operation. -In order to create PDBs to an existing DBCS system, the steps will be: +To create PDBs and add them to an existing DBCS system, the two steps are as follows: 1. Bind the existing DBCS System to DBCS Controller. 2. Apply the change to delete PDBs. -**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE:** We are assuming that before this step, you have followed the [prerequisite steps](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) to create the configmap and the secrets required during the deployment. -As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +In the first step, you first bind the existing DBCS System to DBCS Controller following [the Bind to Existing DBCS System documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will appear as follows: ```bash kubectl get dbcssystems NAME AGE dbcssystem-existing 3m33s ``` -This example uses `deletepdb_in_existing_dbcs_system_list.yaml` to delete PDBs of a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with: +This example uses `deletepdb_in_existing_dbcs_system_list.yaml` to delete PDBs of a Single Instance DBCS VMDB using Oracle DB Operator DBCS Controller with the following: - OCID of existing VMDB as `ocid1.dbsystem.oc1.iad.anuwcljsabf7htyag4akvoakzw4qk7cae55qyp7hlffbouozvyl5ngoputza` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - PDB Name to be deleted e.g `pdb_sauahuja_11` and `pdb_sauahuja_12` -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). +**NOTE:** For the details of the parameters to be used in the `.yaml` file, see: [DBCS Controller Parameters](./dbcs_controller_parameters.md). -Use the file: [deletepdb_in_existing_dbcs_system_list.yaml](./deletepdb_in_existing_dbcs_system_list.yaml) for this use case as below: +Use the file: [deletepdb_in_existing_dbcs_system_list.yaml](./deletepdb_in_existing_dbcs_system_list.yaml) for this use case as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@docker-test-server DBCS]# kubectl apply -f deletepdb_in_existing_dbcs_system_list.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured @@ -40,11 +40,11 @@ NOTE: Check the DB Operator Pod name in your environment. [root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` -3. Remove DBCS Systems resource- +3. Remove the DBCS Systems resource- ```bash kubectl delete -f deletepdb_in_existing_dbcs_system_list.yaml ``` ## Sample Output -[Here](./deletepdb_in_existing_dbcs_system_list_sample_output.log) is the sample output for deletion of PDBs from an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. \ No newline at end of file +[This example log file](./deletepdb_in_existing_dbcs_system_list_sample_output.log) is an example log file output for deletion of PDBs from an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. \ No newline at end of file From 0a357d3f14f9282c0a53f9c7bfc6747bea119cff Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:28:04 +0000 Subject: [PATCH 393/414] Update scale_up_storage.md Minor style edits. --- docs/dbcs/provisioning/scale_up_storage.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/dbcs/provisioning/scale_up_storage.md b/docs/dbcs/provisioning/scale_up_storage.md index ff16cbf9..f5bfa8b8 100644 --- a/docs/dbcs/provisioning/scale_up_storage.md +++ b/docs/dbcs/provisioning/scale_up_storage.md @@ -1,15 +1,15 @@ # Scale UP the storage of an existing OBDS System -In this use case, an existing OCI OBDS system deployed earlier is scaled up for its storage using Oracle DB Operator OBDS controller. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is scaled up for its storage using Oracle DB Operator OBDS controller. This is a two-step operation. -In order to scale up storage of an existing OBDS system, the steps will be: +To scale up storage of an existing OBDS system, the two steps are as follows: 1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to scale up its storage. -**NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE** We are assuming that before this step, you have followed the [prerequisite steps](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) to create the configmap and the secrets required during the deployment. -This example uses `scale_up_storage.yaml` to scale up storage of an existing Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `scale_up_storage.yaml` to scale up storage of an existing Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` - OCI Configmap as `oci-cred` @@ -24,9 +24,9 @@ This example uses `scale_up_storage.yaml` to scale up storage of an existing Sin - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` -Use the file: [scale_up_storage.yaml](./scale_up_storage.yaml) for this use case as below: +Use the file: [scale_up_storage.yaml](./scale_up_storage.yaml) for this use case as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@test-server OBDS]# kubectl apply -f scale_storage.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured @@ -42,4 +42,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./scale_up_storage_sample_output.log) is the sample output for scaling up the storage of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with minimal parameters. +[this log file](./scale_up_storage_sample_output.log) is an example of an output log file for scaling up the storage of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with minimal parameters. From 6561df9859f01aa0260564f02d69430a85281e38 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:33:39 +0000 Subject: [PATCH 394/414] Update upgrading_database.md Minor style edits. --- docs/dbcs/provisioning/upgrading_database.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/dbcs/provisioning/upgrading_database.md b/docs/dbcs/provisioning/upgrading_database.md index 706f1af7..a82a922c 100644 --- a/docs/dbcs/provisioning/upgrading_database.md +++ b/docs/dbcs/provisioning/upgrading_database.md @@ -1,30 +1,30 @@ # Upgrade Existing Database of DB System in OCI Base DBCS Service -In this use case, an existing OCI OBDS system deployed earlier is going to be upgraded in OCI Oracle Base Database System (OBDS). Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is going to be upgraded in OCI Oracle Base Database System (OBDS). This is a two-step operation. -In order to upgrade OBDS to an existing OBDS system, get the OCID of DB System ID you want to upgrade. +To upgrade OBDS to an existing OBDS system, obtain the OCID of DB System ID that you want to upgrade. -**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE:** We are assuming that before this step, you have followed the [prerequisite steps](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) to create the configmap and the secrets required during the deployment. -As step 1, first bind the existing DBCS System to DBCS Controller following [documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as below- +For the first step, bind the existing DBCS System to DBCS Controller following the [Bind ot Existing DBCS System documentation](./../provisioning/bind_to_existing_dbcs_system.md). After successful binding, it will show as follows: ```bash kubectl get dbcssystems NAME AGE dbcssystem-existing 3m33s ``` -Step 2 uses `upgrade_dbcs_system.yaml` to upgrade a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +Step 2 uses `upgrade_dbcs_system.yaml` to upgrade a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCID of existing VMDB as `id` to be upgraded. - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` - `isupgrade` as true -- Specification of DB System been upgraded as `dbUpgradeVersion`. These must be unique and new details for new upgraded DB system to be created. -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). +- Specification of the DB System being upgraded as `dbUpgradeVersion`. The specification details must be unique and new for the new upgraded DB system that is created. +**NOTE:** For the details of the parameters to be used in the `.yaml` file, see: [DBCS Controller Parameters](./dbcs_controller_parameters.md). -Use the file: [upgrade_dbcs_system.yaml](./upgrade_dbcs_system.yaml) for this use case as below: +Use the file: [upgrade_dbcs_system.yaml](./upgrade_dbcs_system.yaml) for this use case as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@docker-test-server DBCS]# kubectl apply -f upgrade_dbcs_system.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured @@ -38,7 +38,7 @@ NOTE: Check the DB Operator Pod name in your environment. [root@docker-test-server DBCS]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-g2cgw -n oracle-database-operator-system ``` -3. Describe kubernetes object to see correct Db Version post upgrade is completed- +3. After the upgrade, describe the Kubernetes object to ensure that the correct database version is completed: ```bash kubectl get dbcssystems.database.oracle.com dbcssystem-existing kubectl get dbcssystems.database.oracle.com dbcssystem-existing -o jsonpath='{.status.dbVersion}' From 22929ef97a581f52a57994583c31be99d23dc09a Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:37:22 +0000 Subject: [PATCH 395/414] Update update_license.md Minor style edits. --- docs/dbcs/provisioning/update_license.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/dbcs/provisioning/update_license.md b/docs/dbcs/provisioning/update_license.md index 6f32c31b..aef88fd7 100644 --- a/docs/dbcs/provisioning/update_license.md +++ b/docs/dbcs/provisioning/update_license.md @@ -1,15 +1,15 @@ # Update License type of an existing OBDS System -In this use case, the license type of an existing OCI OBDS system deployed earlier is changed from `License Included` to `Bring your own license` using Oracle DB Operator OBDS controller. Its a 2 Step operation. +In this use case, the license type of an existing OCI OBDS system deployed earlier is changed from `License Included` to `Bring your own license` using Oracle DB Operator OBDS controller. This is a two-step operation. -In order to update the license type an existing OBDS system, the steps will be: +To update the license type an existing OBDS system, the two steps are as follows: 1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to change its license type. -**NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE** We are assuming that before this step, you have followed the [prerequisite steps](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) to create the configmap and the secrets required during the deployment. -This example uses `update_license.yaml` to change the license type of a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `update_license.yaml` to change the license type of a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` - OCI Configmap as `oci-cred` @@ -23,17 +23,17 @@ This example uses `update_license.yaml` to change the license type of a Single I - SSH Public key for the OBDS system being deployed as `oci-publickey` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). +**NOTE:** For the details of the parameters to be used in the `.yaml` file, see: [DBCS Controller Parameters](./dbcs_controller_parameters.md). -Use the file: [update_license.yaml](./update_license.yaml) for this use case as below: +Use the file: [update_license.yaml](./update_license.yaml) for this use case as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@test-server OBDS]# kubectl apply -f update_license.yaml dbcssystem.database.oracle.com/dbcssystem-existing configured ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB Scale up. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` to follow the progress of the OBDS VMDB Scale up. NOTE: Check the DB Operator Pod name in your environment. @@ -43,4 +43,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./update_license_sample_output.log) is the sample output for updating the license type an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. +[This log file](./update_license_sample_output.log) is an example output log file for updating the license type an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. From 5a89a3d8f5e599eaa887c738f2566a125a2ea711 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:41:41 +0000 Subject: [PATCH 396/414] Update scale_down_dbcs_system_shape.md minor style edits. --- .../provisioning/scale_down_dbcs_system_shape.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md index 1f03ff9f..149b8e10 100644 --- a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md @@ -1,15 +1,15 @@ # Scale Down the shape of an existing OBDS System -In this use case, an existing OCI OBDS system deployed earlier is scaled down for its shape using Oracle DB Operator OBDS controller. Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is scaled down for its shape using Oracle DB Operator OBDS controller. This is a two-step operation. -In order to scale down an existing OBDS system, the steps will be: +To scale down an existing OBDS system, the two steps are as follows: 1. Bind the existing OBDS System to OBDS Controller. 2. Apply the change to scale down its shape. -**NOTE** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE** We are assuming that before this step, you have followed the [prerequisite steps](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) to create the configmap and the secrets required during the deployment. -This example uses `scale_down_dbcs_system_shape.yaml` to scale down a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `scale_down_dbcs_system_shape.yaml` to scale down a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCID of existing VMDB as `ocid1.dbsystem.oc1.ap-mumbai-1.anrg6ljrabf7htyadgsso7aessztysrwaj5gcl3tp7ce6asijm2japyvmroa` - OCI Configmap as `oci-cred` @@ -22,9 +22,9 @@ This example uses `scale_down_dbcs_system_shape.yaml` to scale down a Single Ins - SSH Public key for the OBDS system being deployed as `oci-publickey` - OCID of the Subnet as `ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaa5zpzfax66omtbmjwlv4thruyru7focnu7fjcjksujmgwmr6vpbvq` -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). +**NOTE:** For the details of the parameters to be used in the `.yaml` file, see: [DBCS Controller Parameters](./dbcs_controller_parameters.md). -Use the file: [scale_down_dbcs_system_shape.yaml](./scale_down_dbcs_system_shape.yaml) for this use case as below: +Use the file: [scale_down_dbcs_system_shape.yaml](./scale_down_dbcs_system_shape.yaml) for this use case as follows: 1. Deploy the .yaml file: ```sh @@ -42,4 +42,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./scale_down_dbcs_system_shape_sample_output.log) is the sample output for scaling down the shape of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. +[This log file](./scale_down_dbcs_system_shape_sample_output.log) is an example output log file for scaling down the shape of an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. From 2a231b0f2dd735c218d00c03951b631469e33e8b Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:43:37 +0000 Subject: [PATCH 397/414] Update clone_from_database.md minor style edits --- docs/dbcs/provisioning/clone_from_database.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/dbcs/provisioning/clone_from_database.md b/docs/dbcs/provisioning/clone_from_database.md index 05b294b5..70b5792e 100644 --- a/docs/dbcs/provisioning/clone_from_database.md +++ b/docs/dbcs/provisioning/clone_from_database.md @@ -2,11 +2,11 @@ In this use case, an existing OCI OBDS system deployed earlier with existing Database is going to be cloned in OCI Base OBDS Service using existing Database ID. -As an pre-requisite, get the details of OCID of database of an existing OBDS System which you want to clone. +As an prerequisite, obtain the details of OCID of database of an existing OBDS System that you want to clone. -**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE:** We are assuming that before this step, you have followed the [prerequisite steps](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) to create the configmap and the secrets required during the deployment. -This example uses `clone_dbcs_system_from_database.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `clone_dbcs_system_from_database.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCID of existing as `databaseId` - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` @@ -14,7 +14,7 @@ This example uses `clone_dbcs_system_from_database.yaml` to clone a Single Insta - Specification of dbClone as - Details of new DB system for cloning `dbAdminPasswordSecret`,`tdeWalletPasswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`, `initialDataStorageSizeInGB` **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). -Use the file: [clone_dbcs_system_from_database.yaml](./clone_dbcs_system_from_database.yaml) for this use case as below: +Use the file: [clone_dbcs_system_from_database.yaml](./clone_dbcs_system_from_database.yaml) for this use case as described in the following steps: 1. Deploy the .yaml file: ```sh @@ -22,7 +22,7 @@ Use the file: [clone_dbcs_system_from_database.yaml](./clone_dbcs_system_from_da dbcssystem.database.oracle.com/dbcssystem-clone created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB creation of PDBs. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` to follow the progress of the OBDS VMDB creation of PDBs. NOTE: Check the DB Operator Pod name in your environment. @@ -32,4 +32,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./clone_dbcs_system_from_database_sample_output.log) is the sample output for cloning an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. +[This log file](./clone_dbcs_system_from_database_sample_output.log) is an example output log file for cloning an existing OBDS System deployed in OCI using Oracle DB Operator OBDS Controller. From 8d2842c5ade2b7a4c077d177b04b84b1031becc5 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 17:46:42 +0000 Subject: [PATCH 398/414] Update clone_from_existing_dbcs.md minor style edits. --- .../provisioning/clone_from_existing_dbcs.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/dbcs/provisioning/clone_from_existing_dbcs.md b/docs/dbcs/provisioning/clone_from_existing_dbcs.md index 61665188..48f2b375 100644 --- a/docs/dbcs/provisioning/clone_from_existing_dbcs.md +++ b/docs/dbcs/provisioning/clone_from_existing_dbcs.md @@ -1,12 +1,12 @@ # Clone DB System from Existing DB System in OCI Oracle Base Database System (OBDS) -In this use case, an existing OCI OBDS system deployed earlier is going to be cloned in OCI Oracle Base Database System (OBDS). Its a 2 Step operation. +In this use case, an existing OCI OBDS system deployed earlier is going to be cloned in OCI Oracle Base Database System (OBDS). It is a two-step operation. -In order to clone OBDS to an existing OBDS system, get the OCID of DB System ID you want to clone. +To clone OBDS to an existing OBDS system, obtain the OCID of the database system ID that you want to clone. -**NOTE:** We are assuming that before this step, you have followed the [prerequisite](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE:** We are assuming that before this step, you have followed the [prerequisite steps](./../README.md#prerequisites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) to create the configmap and the secrets required during the deployment. -This example uses `clone_dbcs_system.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `clone_dbcs_system.yaml` to clone a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCID of existing VMDB as `id` to be cloned. - OCI Configmap as `oci-cred` @@ -15,15 +15,15 @@ This example uses `clone_dbcs_system.yaml` to clone a Single Instance OBDS VMDB - Specification of DB System been cloned as `dbClone` -> `dbAdminPaswordSecret`, `dbName`,`hostName`,`displayName`,`licenseModel`,`domain`,`sshPublicKeys`,`subnetId`. These must be unique and new details for new cloned DB system to be created. **NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). -Use the file: [clone_dbcs_system.yaml](./clone_dbcs_system.yaml) for this use case as below: +Use the file: [clone_dbcs_system.yaml](./clone_dbcs_system.yaml) for this use case as described in the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```sh [root@docker-test-server DBCS]# kubectl apply -f clone_dbcs_system.yaml dbcssystem.database.oracle.com/dbcssystem-clone created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the DBCS VMDB creation of PDBs. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` to follow the progress of the DBCS VMDB creation of PDBs. NOTE: Check the DB Operator Pod name in your environment. @@ -33,4 +33,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./clone_dbcs_system_sample_output.log) is the sample output for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. +[This log file](./clone_dbcs_system_sample_output.log) is an example output log file for cloning an existing DBCS System deployed in OCI using Oracle DB Operator DBCS Controller. From 5cd2e8bff37f1e43bc60807aaa23c1c93afb3b33 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 18:17:37 +0000 Subject: [PATCH 399/414] Update create_dbcs_with_kms.md minor style edits. --- .../dbcs/provisioning/create_dbcs_with_kms.md | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/dbcs/provisioning/create_dbcs_with_kms.md b/docs/dbcs/provisioning/create_dbcs_with_kms.md index 97d912d4..b6005e7f 100644 --- a/docs/dbcs/provisioning/create_dbcs_with_kms.md +++ b/docs/dbcs/provisioning/create_dbcs_with_kms.md @@ -2,12 +2,13 @@ In this use case, an OCI OBDS system is deployed using Oracle DB Operator OBDS controller along with KMS Vault configuration -**NOTE** It is assumed that before this step, you have followed the [prerequisite](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) steps to create the configmap and the secrets required during the deployment. +**NOTE** We assume that before this procedure, you have followed the [prerequisite steps](./../README.md#prerequsites-to-deploy-a-dbcs-system-using-oracle-db-operator-dbcs-controller) to create the configmap and the secrets required during the deployment. ## Pre-requisites for KMS Vaults related to OBDS System -There is also other set of pre-requisites for KMS Vaults related to dynamic group and policies. Please follow instructions for same. -1. Create Dynamic group with rule `ALL {resource.compartment.id =` and give it some name. -2. Create policy in your compartment for this dynamic group to access to key/vaults by database. +You also must have completed the prerequisites for KMS Vaults related to dynamic group and policies. + +1. Create Dynamic group with rule `ALL {resource.compartment.id =` and give it a name. +2. Create a policy in your compartment for this dynamic group to grant it access to key/vaults by database. ```txt Allow dynamic-group <> to manage secret-family in compartment <> @@ -17,7 +18,7 @@ Allow dynamic-group <> to manage keys in compartment <> Allow dynamic-group <> to manage vaults in compartment <> ``` -E.g +For example: ```txt ALL {resource.compartment.id = 'ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a'} @@ -29,9 +30,9 @@ Allow dynamic-group db_dynamic_group to manage database-family in compartment sa Allow dynamic-group db_dynamic_group to manage keys in compartment sauahuja Allow dynamic-group db_dynamic_group to manage vaults in compartment sauahuja ``` -3. Do also create KMS Vault and KMS Key in order to use it during OBDS provisioning. We are going to refer those variables (`vaultName`, `keyName`) in the yaml file. +3. Create KMS Vault and KMS Keys so that you can use it during OBDS provisioning. We refer to those variables (`vaultName`, `keyName`) in the yaml file. -This example uses `dbcs_service_with_kms.yaml` to deploy a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with: +This example uses `dbcs_service_with_kms.yaml` to deploy a Single Instance OBDS VMDB using Oracle DB Operator OBDS Controller with the following: - OCI Configmap as `oci-cred` - OCI Secret as `oci-privatekey` @@ -50,17 +51,17 @@ This example uses `dbcs_service_with_kms.yaml` to deploy a Single Instance OBDS - KMS Compartment Id as `ocid1.compartment.oc1..aaaaaaaa63yqilqhgxv3dszur3a2fgwc64ohpfy43vpqjm7q5zq4q4yaw72a` - KMS Key Name as `dbkey` -**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). While giving KMS Vault make sure not to pass TDE wallet password in DB creation as either of them can be only used for encryption. +**NOTE:** For the details of the parameters used in the `.yaml` file, see: [DBCS Controller Parameters](./dbcs_controller_parameters.md). When providing the KMS Vault, ensure that you do not pass the TDE wallet password in database creation, because either of them can be used only for encryption. -Use the file: [dbcs_service_with_kms.yaml](./dbcs_service_with_kms.yaml) for this use case as below: +For the steps that follow, use this file: [dbcs_service_with_kms.yaml](./dbcs_service_with_kms.yaml). Complete the following steps: -1. Deploy the .yaml file: +1. Deploy the `.yaml` file: ```bash [root@docker-test-server OBDS]# kubectl apply -f dbcs_service_with_kms.yaml dbcssystem.database.oracle.com/dbcssystem-create created ``` -2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` for the progress of the OBDS VMDB deployment. +2. Monitor the Oracle DB Operator Pod `pod/oracle-database-operator-controller-manager-665874bd57-g2cgw` to monitor the progress of the OBDS VMDB deployment. NOTE: Check the DB Operator Pod name in your environment. @@ -70,4 +71,4 @@ NOTE: Check the DB Operator Pod name in your environment. ## Sample Output -[Here](./dbcs_service_with_kms_sample_output.log) is the sample output for a OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with KMS configurations. +[This log file](./dbcs_service_with_kms_sample_output.log) is an example output log file for a OBDS System deployed in OCI using Oracle DB Operator OBDS Controller with KMS configurations. From bdeabaec0f3a76b00579e49cd36eceadb5fecd9b Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 18:47:30 +0000 Subject: [PATCH 400/414] Update add_asm_disk_to_an_existing_restart_database.md minor style updates --- ...sm_disk_to_an_existing_restart_database.md | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md index d74f07be..cf97163a 100644 --- a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md +++ b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md @@ -1,6 +1,6 @@ ## Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database -### In this usecase: +### In this use case: * You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. Now, you need to expand ASM storage by adding new ASM disks. * The existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: @@ -15,21 +15,21 @@ * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. ### General Steps - * If you are using storage class to dynamically provision the ASM disks, you do not need to allocate block devices. Otherwise, need to allocate block devices to worker node where Oracle restart databae pod is running. You need to clean the new ASM disks using `dd` command. - * Update the Oracle Restart Custom Resource. Edit the custom resource YAML (oraclerestarts.database.oracle.com) to reference the new PVCs/disks under the appropriate ASM configuration. + * If you are using storage class to dynamically provision the ASM disks, then you do not need to allocate block devices. If you are not using storage class, then you must allocate block devices to worker nodes where the Oracle Restart database pod is running. You must clean up the new ASM disks by using the `dd` command. + * Update the Oracle Restart Custom Resource. Edit the custom resource YAML (`oraclerestarts.database.oracle.com`) to reference the new PVCs/disks under the appropriate ASM configuration. ### In this Example: - * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). - * The exisitng disks on the worker nodes for the ASM which are being used in Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. - * Skip this steps, if you are using **storage class to dynamically provision ASM disks**.In this example, two new disks will be added to the existing Oracle Restart Database Deployment. For this purpose, the disks on the worker nodes which will be used are `/dev/disk/by-partlabel/asm-disk3` and `/dev/disk/by-partlabel/asm-disk4`. - * Update the corresponding device list in initParams section. - * Default value in yaml file is `autoUpdate: "true"`, which will delete and recreate the pod with updated ASM disks in the Oracle Restart Deployment. In this case, the new disks will be automatically added to the existing Diskgroup. - * If the value in yaml file is set to `autoUpdate: "false"`, the Oracle Restart Database Pod is recreated, but the additional disks are `NOT` added to the ASM Disk Group automatically. + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used. This image is built using files from this [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). + * The existing disks on the worker nodes for the ASM that are used in Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. + * Specify the size and names of these devices using the parameter `disksBySize`. By default, size is in GBs. + * If you are using **storage class to dynamically provision ASM disks**, then you can skip these steps. If you are not using storage class, then in this example, two new disks are added to the existing Oracle Restart Database Deployment. For this purpose, the disks on the worker nodes that will be used are `/dev/disk/by-partlabel/asm-disk3` and `/dev/disk/by-partlabel/asm-disk4`. + * Update the corresponding device list in the `initParams` section. + * The default value in the `yaml` file is `autoUpdate: "true"`, which will delete and recreate the pod with updated ASM disks in the Oracle Restart Deployment. In this case, the new disks will be automatically added to the existing Diskgroup. + * If the value in the `yaml` file is set to `autoUpdate: "false"`, then the Oracle Restart Database Pod is recreated, but the additional disks are _not_ added to the ASM Disk Group automatically. ## When autoUpdate is set to true -* Use the file: [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml) for this use case as below: +* For this use case, use the file [orestart_prov_asm_disk_addition.yaml](./orestart_prov_asm_disk_addition.yaml): * Deploy the `orestart_prov_asm_disk_addition.yaml` file: ```sh kubectl apply -f orestart_prov_asm_disk_addition.yaml @@ -43,16 +43,16 @@ In this case, the new disks will be added to the existing Diskgroup in the Oracl # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" ``` - * Samples logs in [logs](./logs/asm_addition_autoupdate_true.txt) for disk addition with option `autoUpdate: true`. + * Example logs are in [ASM addition autoupdate_true logs](./logs/asm_addition_autoupdate_true.txt) for disk addition with the option `autoUpdate: true`. ## When autoUpdate is set to false -* Use the file: [orestart_prov_asm_disk_addition_autoupdate_false.yaml](./orestart_prov_asm_disk_addition_autoupdate_false.yaml) for this use case as below: +* For this use case, use the file: [orestart_prov_asm_disk_addition_autoupdate_false.yaml](./orestart_prov_asm_disk_addition_autoupdate_false.yaml): * Deploy the `orestart_prov_asm_disk_addition_autoupdate_false.yaml` file: ```sh kubectl apply -f orestart_prov_asm_disk_addition_autoupdate_false.yaml ``` -In this case, new disks are added to Oracle Restart Database Object Statefulset and Pods are recreated, but this disk is not added to the ASM Disk Group. +In this scenario, new disks are added to Oracle Restart Database Object Statefulset and Pods are recreated, but this disk is not added to the ASM Disk Group. * Check the status of the deployment: ```sh # Check the status of the Kubernetes Pods: @@ -61,4 +61,4 @@ In this case, new disks are added to Oracle Restart Database Object Statefulset # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" ``` - * Samples logs in [logs](./logs/asm_addition_autoupdate_false.txt) for disk addition with option `autoUpdate: false`. \ No newline at end of file + * Example logs are in [ASM addition autoupdate_false logs](./logs/asm_addition_autoupdate_false.txt) for disk addition with option `autoUpdate: false`. \ No newline at end of file From a01ea6c710ca9eb7753ccb3fde7675142c5dcdc9 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 18:59:26 +0000 Subject: [PATCH 401/414] Update udsharding_scale_in_delete_an_existing_shard.md minor style updates. --- ...rding_scale_in_delete_an_existing_shard.md | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md index 28094146..60a8b5c7 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md @@ -4,7 +4,7 @@ This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology with User Defined Sharding provisioned using Oracle Database Sharding controller. -In this use case, the existing database Sharding is having: +In this use case, the existing database Sharding has the following: * Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` * Five sharding Pods: `shard1`,`shard2`,`shard3`,`shard4` and `shard5` @@ -12,24 +12,24 @@ In this use case, the existing database Sharding is having: * Namespace: `shns` * User Defined Sharding is specified using `shardingType: USER` -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_delshard.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. +In this example, we are using pre-built Oracle Database and Global Data Services container images available on the [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull these images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, then you need to change the `dbImage` and `gsmImage` tags with the images you have built in your enviornment in the file `udsharding_shard_prov_delshard.yaml`. + * To understand the Database and Global Data Services Docker images prerequisites, see: [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you want to use the [Oracle Database 23ai Free image](https://www.oracle.com/database/free/get-started/) for Database and GSM, then you must add the additional parameter `dbEdition: "free"` to the `.yaml` file we show in these steps. + * Ensure that the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the `openssl` commands to generate the encrypted password file during the deployment. -**NOTE:** Use tag `isDelete: enable` to delete the shard you want. +**NOTE:** Use the tag `isDelete: enable` to delete the shard that you want to remove. -This use case deletes the shard `shard4` from the above Sharding Topology. +This use case deletes the shard `shard4` from this Sharding Topology. -Use the file: [udsharding_shard_prov_delshard.yaml](./udsharding_shard_prov_delshard.yaml) for this use case as below: +Use the file: [udsharding_shard_prov_delshard.yaml](./udsharding_shard_prov_delshard.yaml) for this use case, as described in the following steps: -1. Move out the chunks from the shard to be deleted to another shard. For example, in the current case, before deleting the `shard4`, if you want to move the chunks from `shard4` to `shard2`, then you can run the below `kubectl` command where `/u01/app/oracle/product/23ai/gsmhome_1` is the GSM HOME: +1. Move out the chunks from the shard to be deleted to another shard. For example, in the current case, before deleting `shard4`, if you want to move the chunks from `shard4` to `shard2`, then you can run the `kubectl` command where `/u01/app/oracle/product/23ai/gsmhome_1` is the GSM HOME: ```sh kubectl exec -it pod/gsm1-0 -n shns -- /u01/app/oracle/product/23ai/gsmhome_1/bin/gdsctl "move chunk -chunk all -source shard4_shard4pdb -target shard4_shard4pdb" ``` -2. Confirm the shard to be deleted (`shard4` in this case) is not having any chunk using below command: +2. To confirm that the shard that you want to be deleted (`shard4` in this case) does not have any chunks, use the following command: ```sh kubectl exec -it pod/gsm1-0 -n shns -- /u01/app/oracle/product/23ai/gsmhome_1/bin/gdsctl "config chunks" ``` @@ -46,12 +46,12 @@ Use the file: [udsharding_shard_prov_delshard.yaml](./udsharding_shard_prov_dels ``` **NOTE:** -- After you apply `udsharding_shard_prov_delshard.yaml`, the change may not be visible immediately and it may take some time for the delete operation to complete. -- If the shard, that you are trying to delete, is still having chunks, then the you will see message like below in the logs of the Oracle Database Operator Pod. +- After you apply `udsharding_shard_prov_delshard.yaml`, the change may not be visible immediately. It can take some time for the delete operation to complete. +- If the shard that you are trying to delete still contains chunks, then a message such as the following is displayed in the logs of the Oracle Database Operator Pod: ```sh INFO controllers.database.ShardingDatabase manual intervention required ``` - In this case, you will need to first move out the chunks from the shard to be deleted using Step 2 above and then apply the file in Step 3 to delete that shard. + When you see that message, you are required to first move the chunks out of the shard that you want to delete using Step 2 as described above, and then apply the file in Step 3 to delete that shard. To check the status, use the following command: ```sh From 2973935d5590bc289ac98fceea66999e3b63887f Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 19:56:54 +0000 Subject: [PATCH 402/414] Update ssharding_provisioning_without_db_gold_image.md Minor style edits. --- .../ssharding_provisioning_without_db_gold_image.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md index 011b553f..ae28a635 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md @@ -16,12 +16,12 @@ This example uses `ssharding_shard_prov.yaml` to provision an Oracle Database sh In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. + * If you plan to use images built by you, then you must change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov.yaml`. + * To understand Database and Global Data Services Docker images prerequsites, see [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you must add the additional parameter `dbEdition: "free"` to the `.yaml` file with this document. + * Ensure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. -Use the file: [ssharding_shard_prov.yaml](./ssharding_shard_prov.yaml) for this use case as below: +Use the file: [ssharding_shard_prov.yaml](./ssharding_shard_prov.yaml) for this use case: 1. Deploy the `ssharding_shard_prov.yaml` file: ```sh From 9ede931929e055c4f943ed1c55147bdf30c9040a Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 20:02:35 +0000 Subject: [PATCH 403/414] Update ssharding_provisioning_with_notification_using_oci_notification.md Minor style changes --- ...ith_notification_using_oci_notification.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md index f903c401..8cce1b18 100644 --- a/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md @@ -16,13 +16,13 @@ This example uses `ssharding_shard_prov_send_notification.yaml` to provision an **NOTE:** -* The notification will be sent using a configmap created with the credentials of the OCI user account in this use case. +* The notification will be sent using a configmap created with the credentials of the OCI user account used with this procedure. We will create a topic in Notification Service of the OCI Console and use its OCID. To do this: -1. Create a `configmap_data.txt` file, such as the following, which has the OCI User details that will be used to send notfication: +1. Create a `configmap_data.txt` file, such as the following, which has the OCI User details that will be used to send notification: ```sh user=ocid1.user.oc1........fx7omxfq @@ -31,12 +31,12 @@ To do this: region=us-phoenix-1 topicid=ocid1.onstopic.oc1.phx.aaa............6xrq ``` -2. Create a configmap using the below command using the file created above: +2. Using the file created in step 1, create a configmap with the following command: ```sh kubectl create configmap onsconfigmap --from-file=./configmap_data.txt -n shns ``` -3. Create a key file `priavatekey` having the PEM key of the OCI user being used to send notification: +3. Create a key file called `privatekey` that has the PEM key of the OCI user being used to send notification: ```sh -----BEGIN PRIVATE KEY-G---- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXYxA0DJvEwtVR @@ -61,16 +61,16 @@ To do this: kubectl describe secret my-secret -n shns ``` -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `ssharding_shard_prov_send_notification.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. +In this example, we are using pre-built Oracle Database and Global Data Services container images available on the [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull these images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, then you must exchange the `dbImage` and `gsmImage` tags for the images that you have built in your enviornment in file `ssharding_shard_prov_send_notification.yaml`. + * To understand the Database and Global Data Services Docker images prerequsites, see: [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you want to use the [Oracle Database 23ai Free image](https://www.oracle.com/database/free/get-started/) for Database and GSM, then you must add the additional parameter `dbEdition: "free"` to the `.yaml` file used in this procedure. + * Ensure that the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. -**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is _not_ supported with Oracle Database 23ai Free. -Use the file: [ssharding_shard_prov_send_notification.yaml](./ssharding_shard_prov_send_notification.yaml) for this use case as below: +Use the file: [ssharding_shard_prov_send_notification.yaml](./ssharding_shard_prov_send_notification.yaml) for this use case: 1. Deploy the `ssharding_shard_prov_send_notification.yaml` file: ```sh From d95e995b1e885efdaf6373529462072845b61f37 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 20:11:05 +0000 Subject: [PATCH 404/414] Update udsharding_provisioning_with_control_on_resources.md Minor style edits. --- ...harding_provisioning_with_control_on_resources.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md index 9e19f4be..61ce6317 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md @@ -14,14 +14,14 @@ This example uses `udsharding_shard_prov_memory_cpu.yaml` to provision an Oracle * Additional tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level * User Defined Sharding is specified using `shardingType: USER` -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) +In this example, we are using pre-built Oracle Database and Global Data Services container images available on the [Oracle Container Registry](https://container-registry.oracle.com/) * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_memory_cpu.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. + * If you plan to use images built by you, then you must change the `dbImage` and `gsmImage` tags with the images that you have built in your enviornment in file `udsharding_shard_prov_memory_cpu.yaml`. + * To understand the Database and Global Data Services Docker images prerequisites, see: [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you want to use the [Oracle Database 23ai Free image](https://www.oracle.com/database/free/get-started/) for Database and GSM, then you must add the additional parameter `dbEdition: "free"` to the `.yaml` file used with this procedure. + * Ensure that the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. -**NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs using tags `cpu` and `memory` respectively but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are `not` supported. +**NOTE:** For Oracle Database 23ai Free, you can control the `CPU` and `Memory` allocation of the PODs by using tags `cpu` and `memory` respectively, but tags `INIT_SGA_SIZE` and `INIT_PGA_SIZE` to control the SGA and PGA allocation at the database level are _not_ supported. Use the YAML file [udsharding_shard_prov_memory_cpu.yaml](./udsharding_shard_prov_memory_cpu.yaml). From 5f55ecf5522be9d0f51fa2354aa6f1db9ff92d18 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 20:17:17 +0000 Subject: [PATCH 405/414] Update udsharding_provisioning_with_notification_using_oci_notification.md Minor style edits. --- ...ith_notification_using_oci_notification.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md index f342987f..976798e2 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md @@ -17,7 +17,7 @@ This example uses `udsharding_shard_prov_send_notification.yaml` to provision an **NOTE:** -* The notification will be sent using a configmap created with the credentials of the OCI user account in this use case. +* The notification will be sent using a configmap created with the credentials of the OCI user account used in this procedure. We will create a topic in Notification Service of the OCI Console and use its OCID. @@ -37,7 +37,7 @@ To do this: kubectl create configmap onsconfigmap --from-file=./configmap_data.txt -n shns ``` -3. Create a key file `priavatekey` having the PEM key of the OCI user being used to send notification: +3. Create a key file `privatekey` that has the PEM key of the OCI user being used to send notification: ```sh -----BEGIN PRIVATE KEY-G---- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXYxA0DJvEwtVR @@ -62,16 +62,16 @@ To do this: kubectl describe secret my-secret -n shns ``` -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_send_notification.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. +In this example, we are using pre-built Oracle Database and Global Data Services container images available on the [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull these images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, then you must exchanage the `dbImage` and `gsmImage` tags for the images that you have built in your enviornment in file `udsharding_shard_prov_send_notification.yaml`. + * To understand the Database and Global Data Services Docker images prerequisites, see: [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you want to use the [Oracle Database 23ai Free image](https://www.oracle.com/database/free/get-started/) for Database and GSM, then you must add the additional parameter `dbEdition: "free"` to the _.yaml_ file used in this procedure. + * Ensure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. -**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is `NOT` supported with Oracle Database 23ai Free. +**NOTE:** Provisioning the Sharded Database using Cloning from Database Gold Image is _not_ supported with Oracle Database 23ai Free. -Use the file: [udsharding_shard_prov_send_notification.yaml](./udsharding_shard_prov_send_notification.yaml) for this use case as below: +Use the file: [udsharding_shard_prov_send_notification.yaml](./udsharding_shard_prov_send_notification.yaml) for this use case: 1. Deploy the `udsharding_shard_prov_send_notification.yaml` file: ```sh From e081aebaffc5cfc691d7e8ab882cbb283e4f9ad1 Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 20:20:23 +0000 Subject: [PATCH 406/414] Update udsharding_provisioning_without_db_gold_image.md Minor style edits. --- ...harding_provisioning_without_db_gold_image.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md index cd831469..cd187f2e 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md @@ -15,14 +15,14 @@ This example uses `udsharding_shard_prov.yaml` to provision an Oracle Database s * User Defined Sharding is specified using `shardingType: USER` -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. - -Use the file: [udsharding_shard_prov.yaml](./udsharding_shard_prov.yaml) for this use case as below: +In this example, we are using pre-built Oracle Database and Global Data Services container images available on the [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull these images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, then you must exchange the `dbImage` and `gsmImage` tags for the images that you have built in your enviornment in file `udsharding_shard_prov.yaml`. + * To understand the Database and Global Data Services docker images prerequisites, see: [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you want to use the [Oracle Database 23ai Free image](https://www.oracle.com/database/free/get-started/) for Database and GSM, then you must add the additional parameter `dbEdition: "free"` to the `.yaml` file usd in this procedure. + * Ensure that the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. + +Use the file: [udsharding_shard_prov.yaml](./udsharding_shard_prov.yaml) for this use case: 1. Deploy the `udsharding_shard_prov.yaml` file: ```sh From 0106d61858898ad86df1d039fc4a340f7d95a33a Mon Sep 17 00:00:00 2001 From: douglas_williams Date: Thu, 9 Oct 2025 20:39:41 +0000 Subject: [PATCH 407/414] Update udsharding_scale_out_add_shards.md Minor style edits. --- .../udsharding_scale_out_add_shards.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md index fbc57190..c1ec963e 100644 --- a/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md @@ -4,7 +4,7 @@ This use case demonstrates adding a new shard to an existing Oracle Database sharding topology with User Defined Sharding provisioned earlier using Oracle Database Sharding controller. -In this use case, the existing Oracle Database sharding topology is having: +In this use case, the existing Oracle Database sharding topology is as follows: * Primary GSM Pods `gsm1` and standby GSM Pod `gsm2` * Three sharding Pods: `shard1`, `shard2` and `shard3` @@ -12,16 +12,16 @@ In this use case, the existing Oracle Database sharding topology is having: * Namespace: `shns` * User Defined Sharding is specified using `shardingType: USER` -In this example, we are using pre-built Oracle Database and Global Data Services container images available on [Oracle Container Registry](https://container-registry.oracle.com/) - * To pull the above images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. - * If you plan to use images built by you, you need to change `dbImage` and `gsmImage` tag with the images you have built in your enviornment in file `udsharding_shard_prov_extshard.yaml`. - * To understand the Pre-requisite of Database and Global Data Services docker images, refer [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) - * In case you want to use the [Oracle Database 23ai Free](https://www.oracle.com/database/free/get-started/) Image for Database and GSM, then you will need to add the additional parameter `dbEdition: "free"` to the below .yaml file. - * Make sure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. +In this example, we are using pre-built Oracle Database and Global Data Services container images available on the [Oracle Container Registry](https://container-registry.oracle.com/) + * To pull these images from Oracle Container Registry, create a Kubernetes secret named `ocr-reg-cred` using your credentials with type set to `kubernetes.io/dockerconfigjson` in the namespace `shns`. + * If you plan to use images built by you, then you must exchange the `dbImage` and `gsmImage` tags for the images that you have built in your enviornment in file `udsharding_shard_prov_extshard.yaml`. + * To understand the Database and Global Data Services Docker images prerequisites, see: [Oracle Database and Global Data Services Docker Images](../../README.md#3-oracle-database-and-global-data-services-docker-images) + * If you want to use the [Oracle Database 23ai Free image](https://www.oracle.com/database/free/get-started/) for Database and GSM, then you must add the additional parameter `dbEdition: "free"` to the `.yaml` file used with this procedure. + * Ensure the version of `openssl` in the Oracle Database and Oracle GSM images is compatible with the `openssl` version on the machine where you will run the openssl commands to generated the encrypted password file during the deployment. -This use case adds two new shards `shard4`,`shard5` to above Sharding Topology. +This use case adds two new shards `shard4`,`shard5` to the Sharding Topology. -Use the file: [udsharding_shard_prov_extshard.yaml](./udsharding_shard_prov_extshard.yaml) for this use case as below: +Use the file: [udsharding_shard_prov_extshard.yaml](./udsharding_shard_prov_extshard.yaml) for this use case: 1. Deploy the `udsharding_shard_prov_extshard.yaml` file: ```sh From 7c08e6ac30b0257f2d616748830fefb6d73fc0c3 Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Thu, 9 Oct 2025 22:48:19 +0000 Subject: [PATCH 408/414] Added fixes --- commons/oraclerestart/oraclerestartcommon.go | 8 +++++--- commons/oraclerestart/oraclerestartprov.go | 3 ++- controllers/database/oraclerestart_controller.go | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/commons/oraclerestart/oraclerestartcommon.go b/commons/oraclerestart/oraclerestartcommon.go index b37da658..434f1ce9 100644 --- a/commons/oraclerestart/oraclerestartcommon.go +++ b/commons/oraclerestart/oraclerestartcommon.go @@ -415,7 +415,7 @@ func DelORestartPVC(instance *oraclerestart.OracleRestart, pindex int, cindex in func DelRestartSwPvc(instance *oraclerestart.OracleRestart, OraRestartSpex oraclerestart.OracleRestartInstDetailSpec, kClient client.Client, logger logr.Logger) error { - pvcName := OraRestartSpex.Name + "-oradata-sw-vol-pvc-" + OraRestartSpex.Name + "-0" + pvcName := GetSwPvcName(instance.Name, instance) LogMessages("DEBUG", "Inside the delPvc and received param: "+GetFmtStr(pvcName), nil, instance, logger) pvcFound, err := checkPvc(pvcName, instance, kClient) if err != nil { @@ -1349,8 +1349,10 @@ func GetHealthyNodeCounts(instance *oraclerestart.OracleRestart) (int, error) { } return 0, fmt.Errorf("healthy cluster node counts are not matching with total cluster nodes") } -func GetSwPvcName(name string) string { - return name + "-oradata-sw-vol-pvc" +func GetSwPvcName(name string, instance *oraclerestart.OracleRestart) string { + //// If you are making any change, please refer SwVolumeClaimTemplatesForOracleRestart function as we add instance.Spec.InstDetails.Name + "-0" + pvcName := "odb-sw-pvc-" + name + "-" + instance.Spec.InstDetails.Name + "-0" + return pvcName } func CheckDiskInAsmDeviceList(instance *oraclerestart.OracleRestart, diskName string) string { diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index e4432ef3..b80116f4 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -1088,7 +1088,8 @@ func IsStaticProvisioning(k8sClient client.Client, instance *oraclerestart.Oracl func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestart, OracleRestartSpex oraclerestart.OracleRestartInstDetailSpec) corev1.PersistentVolumeClaim { // If user-provided PVC name exists, skip volume claim template creation - pvcName := GetSwPvcName(OracleRestartSpex.Name) + //pvcName := GetSwPvcName(OracleRestartSpex.Name) + pvcName := "odb-sw-pvc-" + instance.Name return corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Name: pvcName, diff --git a/controllers/database/oraclerestart_controller.go b/controllers/database/oraclerestart_controller.go index 2e9e51f0..b42f8302 100644 --- a/controllers/database/oraclerestart_controller.go +++ b/controllers/database/oraclerestart_controller.go @@ -3492,7 +3492,7 @@ func (r *OracleRestartReconciler) expandStorageClassSWVolume(ctx context.Context return fmt.Errorf("error while fetching the storage class") } - pvcName := oraclerestartcommon.GetSwPvcName(instance.Spec.InstDetails.Name) + "-" + instance.Spec.InstDetails.Name + "-0" + pvcName := oraclerestartcommon.GetSwPvcName(instance.Name, instance) err = r.Get(ctx, types.NamespacedName{ Name: pvcName, Namespace: instance.Namespace, From b140cfe28f0da53d8e5b114de7fbecca10e70870 Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Thu, 9 Oct 2025 22:59:42 +0000 Subject: [PATCH 409/414] doc changes --- ...sm_disk_to_an_existing_restart_database.md | 18 +- .../asm_addition_autoupdate_false.txt | 2 +- ...change_memory_cpu_for_oracle_restart_db.md | 13 +- ...e_sw_storage_size_for_oracle_restart_db.md | 16 +- .../create_kubernetes_secret_for_ssh_setup.md | 4 +- ...oraclerestart_prov_multiple_diskgroups.txt | 131 +++++-- ...raclerestart_prov_multiple_diskgroups.yaml | 11 +- ...ov_multiple_diskgroups_with_redundancy.txt | 179 ++++++--- ...v_multiple_diskgroups_with_redundancy.yaml | 26 +- ..._redundancy_with_separate_storage_class.md | 52 +++ ...redundancy_with_separate_storage_class.txt | 344 ++++++++++++++++++ ...edundancy_with_separate_storage_class.yaml | 154 ++++++++ ...tart_prov_rupatch_oneoff_storageclass.yaml | 10 +- .../oraclerestart_prov_rupatch_pvc.yaml | 8 +- .../oraclerestart_prov_storage_class.yaml | 8 +- ...ov_storage_class_after_sw_home_resize.yaml | 8 +- ...v_storage_class_before_sw_home_resize.yaml | 8 +- .../orestart_db_rupatch_oneoffs_object.txt | 48 +-- .../orestart_rupatch_pvc_object.txt | 123 ++++--- .../orestart_storage_class_object.txt | 85 +++-- ...rage_class_object_after_sw_home_resize.txt | 52 +-- ...age_class_object_before_sw_home_resize.txt | 54 +-- .../prerequisites_oracle_restart_db.md | 27 ++ .../provisioning_oracle_restart_db.md | 2 +- ...isioning_oracle_restart_db_loadbalancer.md | 3 +- ...provisioning_oracle_restart_db_nodeport.md | 2 +- .../provisioning_oracle_restart_db_rupatch.md | 3 +- ...oning_oracle_restart_db_rupatch_oneoffs.md | 13 +- ...ning_oracle_restart_multiple_diskgroups.md | 3 +- ...art_multiple_diskgroups_with_redundancy.md | 6 +- ...provisioning_oracle_restart_rupatch_pvc.md | 10 +- ...ovisioning_oracle_restart_storage_class.md | 7 +- 32 files changed, 1106 insertions(+), 324 deletions(-) create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.txt create mode 100644 docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml diff --git a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md index cf97163a..4d8f5772 100644 --- a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md +++ b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md @@ -3,7 +3,7 @@ ### In this use case: * You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. Now, you need to expand ASM storage by adding new ASM disks. -* The existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: +* The existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: * Oracle Restart Pod * Headless services for Oracle Restart * Oracle Restart Node hostname @@ -19,13 +19,13 @@ * Update the Oracle Restart Custom Resource. Edit the custom resource YAML (`oraclerestarts.database.oracle.com`) to reference the new PVCs/disks under the appropriate ASM configuration. ### In this Example: - * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used. This image is built using files from this [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). - * The existing disks on the worker nodes for the ASM that are used in Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. - * Specify the size and names of these devices using the parameter `disksBySize`. By default, size is in GBs. - * If you are using **storage class to dynamically provision ASM disks**, then you can skip these steps. If you are not using storage class, then in this example, two new disks are added to the existing Oracle Restart Database Deployment. For this purpose, the disks on the worker nodes that will be used are `/dev/disk/by-partlabel/asm-disk3` and `/dev/disk/by-partlabel/asm-disk4`. - * Update the corresponding device list in the `initParams` section. - * The default value in the `yaml` file is `autoUpdate: "true"`, which will delete and recreate the pod with updated ASM disks in the Oracle Restart Deployment. In this case, the new disks will be automatically added to the existing Diskgroup. - * If the value in the `yaml` file is set to `autoUpdate: "false"`, then the Oracle Restart Database Pod is recreated, but the additional disks are _not_ added to the ASM Disk Group automatically. + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). + * The existing disks on the worker nodes for the ASM which are being used in Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + * Skip this steps, if you are using **storage class to dynamically provision ASM disks**. In this example, two new disks will be added to the existing Oracle Restart Database Deployment. For this purpose, the disks on the worker nodes which will be used are `/dev/disk/by-partlabel/asm-disk3` and `/dev/disk/by-partlabel/asm-disk4`. + * Update the corresponding device list in initParams section. + * Default value in yaml file is `autoUpdate: "true"`, which will delete and recreate the pod with updated ASM disks in the Oracle Restart Deployment. In this case, the new disks will be automatically added to the existing Diskgroup. + * If the value in yaml file is set to `autoUpdate: "false"`, the Oracle Restart Database Pod is recreated, but the additional disks are `NOT` added to the ASM Disk Group automatically. ## When autoUpdate is set to true @@ -61,4 +61,4 @@ In this scenario, new disks are added to Oracle Restart Database Object Stateful # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" ``` - * Example logs are in [ASM addition autoupdate_false logs](./logs/asm_addition_autoupdate_false.txt) for disk addition with option `autoUpdate: false`. \ No newline at end of file + * Example logs are in [ASM addition autoupdate_false logs](./logs/asm_addition_autoupdate_false.txt) for disk addition with option `autoUpdate: false`. diff --git a/docs/oraclerestart/provisioning/asm_addition_autoupdate_false.txt b/docs/oraclerestart/provisioning/asm_addition_autoupdate_false.txt index 6f3301e8..5e548d89 100644 --- a/docs/oraclerestart/provisioning/asm_addition_autoupdate_false.txt +++ b/docs/oraclerestart/provisioning/asm_addition_autoupdate_false.txt @@ -249,7 +249,7 @@ $ kubectl logs -f pod/oracle-database-operator-controller-manager-bc8dd8f68-pwfd ### Status after the Disks are added with "autoUpdate=true" -[jpverma@phoenix497729 provisioning]$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart Name: oraclerestart-sample Namespace: orestart Labels: diff --git a/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md b/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md index d3dd5f2f..0e897bfa 100644 --- a/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md @@ -22,13 +22,10 @@ * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. -### Steps - Deploy the Oracle Restart Database -* Skip this step if you have already deployed the Oracle Restart database using storage class. -* Use the file: [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) for this use case as below: -* Update the Oracle Restart container image. In this example, we have `dbocir/oracle/database-orestart:19.3.0-slim` in [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) file to point to the container image you have built. -* Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for this use case as below: +### Steps - Deploy initial Oracle Restart Database +* Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for the initial deployment before any cpu or memory change. * Deploy the `oraclerestart_prov_nodeports.yaml` file: ```sh kubectl apply -f oraclerestart_prov_nodeports.yaml @@ -45,7 +42,7 @@ ORACLE DATABASE IS READY TO USE =============================== ``` -* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. + ### Steps - Modify the Memory and CPU in the Oracle Restart Database Pod * Use the file: [oraclerestart_prov_nodeports_mcpu_change.yaml](./oraclerestart_prov_nodeports_mcpu_change.yaml) to change the Memory and CPU allocation for the existing Oracle Restart Database Pod: ```sh @@ -53,6 +50,6 @@ oraclerestart.database.oracle.com/oraclerestart-sample configured ``` - You will notice that the exiting Oracle Restart Database Pod will be recreated with updated Memory and CPU allocation. +**NOTE:** You will notice that the exiting Oracle Restart Database Pod will be recreated with updated Memory and CPU allocation. * Check Details of Kubernetes CRD Object before and after the change as in this [example](./oraclerestart_prov_nodeports_mcpu_change.txt). It also has the details of the memory and cpu inside the pod before and after the change. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md b/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md index 0cdfd276..56836dca 100644 --- a/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md @@ -2,17 +2,21 @@ ### In this Usecase: * You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. -* Change the software volume size where Oracle binaries are installed for an existing Oracle Restart Database Pod, you can do so by updating in the Custom Resource YAML associated with your Oracle Restart instance. -* This example uses `oraclerestart_prov_nodeports.yaml` to provision the initial Oracle Restart Database using Oracle Restart Controller with: +* The Software Home Location for Grid Infrastructure and Database, the ASM Disks are provisioned as Persistent Volumes using custom storage class during the initial deployment. An updated .yaml file is applied to `increase` the size of the Software Home Location. + +**NOTE:** The `decrease` in the size of Software Home Location for an existing Oracle Restart Database is `not allowed`. + +This example uses `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` to initially provision an Oracle Restart Database using Oracle Restart Controller with: * Oracle Restart Pod * Headless services for Oracle Restart * Oracle Restart Node hostname * Node Port 30007 mapped to port 1521 for Database Listener. If you are using Loadbalancer service then you will see lbservice. - * Persistent volumes created automatically based on specified disks for Oracle ASM storage - * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. If you are using Storageclass then the software volume is dynamically provisioned. + * Persistent volumes for ASM Disks created automatically using the Storage Class for Oracle ASM storage + * Persistent volume for Software location is created automatically using the Storage Class. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using the corresponding Persistent Volume Claim. Its size is specified by `swLocStorageSizeInGb`. * Namespace: `orestart` - * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. If you are using, exisitng NFS based PVC for the staged software, the pramater is `swStagePvcMountLocation` under `configParams`. + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * Name of Custom Storage Class is specified by `storageClass`. * You will be using storageclass to dynamically allcate the storage. using the storage class **oci-bv**. ### In this Example: @@ -20,7 +24,7 @@ * The disks provisioned using storageclass are mounted inside the Oracle Restart Pod as `/dev/asm-disk1` and `/dev/asm-disk2`. * Specify the size of these devices along with names using the parameter `swLocStorageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps - Deploy the Oracle Restart Database * Skip this step if you have already deployed the Oracle Restart database using storage class. diff --git a/docs/oraclerestart/provisioning/create_kubernetes_secret_for_ssh_setup.md b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_ssh_setup.md index 110e3542..3505f8c5 100644 --- a/docs/oraclerestart/provisioning/create_kubernetes_secret_for_ssh_setup.md +++ b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_ssh_setup.md @@ -8,8 +8,10 @@ mkdir /tmp/.secrets/ssh # Generate a private and public key ssh-keygen -t rsa -C "your_email@example.info" -f /tmp/.secrets/ssh/id_rsa +# Deleting the exisitng secret +kubectl delete secret ssh-key-secret -n orestart -# Create the Kubernetes secret in namespace "shns" +# Create the Kubernetes secret in namespace "orestart" kubectl create secret generic ssh-key-secret --from-file=ssh-privkey=/tmp/.secrets/ssh/id_rsa --from-file=ssh-pubkey=/tmp/.secrets/ssh/id_rsa.pub -n orestart # Check the secret details diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.txt b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.txt index 6df1d826..c1aac095 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.txt +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.txt @@ -7,12 +7,12 @@ Annotations: OracleRestarts.database.oracle.com/old-spec: API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-08-26T18:55:04Z + Creation Timestamp: 2025-10-09T05:13:25Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 227602633 - UID: b6e11aa4-c2d0-4bc2-9832-f3ffe6e9730c + Resource Version: 246221746 + UID: 31337564-9573-4c63-bccc-269e232ea9bb Spec: Asm Storage Details: Disks By Size: @@ -23,6 +23,8 @@ Spec: /dev/disk/by-partlabel/asm-disk4 /dev/disk/by-partlabel/asm-disk5 /dev/disk/by-partlabel/asm-disk6 + /dev/disk/by-partlabel/asm-disk7 + /dev/disk/by-partlabel/asm-disk8 Storage Size In Gb: 50 Config Params: Cpu Count: 4 @@ -44,9 +46,12 @@ Spec: Grid Sw Zip File: grid_home.zip Host Sw Stage Location: /scratch/software/stage Inventory: /u01/app/oraInventory + Pdb Name: TESTPDB Pga Size: 1G Processes: 2000 Reco Asm Device List: /dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + Redo Asm Device List: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8 + Redo Asm Disk Dg: +REDO Sga Size: 3G Sw Mount Location: /u01 Db Secret: @@ -55,6 +60,7 @@ Spec: Name: db-user-pass-pkutl Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc + Enable Ons: enable Image: localhost/oracle/database-orestart:19.3.0-slim Image Pull Policy: IfNotPresent Inst Details: @@ -65,16 +71,21 @@ Spec: Value: true Host Sw Location: /scratch/orestart/ Name: dbmc1 - Node Port Svc: - Name: dbmc1-service-nodeport - Svc Type: nodeport + Worker Node: + 10.0.10.58 + Lb Service: + Node Port Svc: + Name: dbmc1-service-nodeport Port Mappings: Node Port: 30007 Port: 1521 Protocol: TCP Target Port: 1521 - Worker Node: - 10.0.10.58 + Node Port: 30200 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: nodeport Resources: Limits: Cpu: 2 @@ -113,29 +124,35 @@ Status: /dev/disk/by-partlabel/asm-disk4 /dev/disk/by-partlabel/asm-disk5 /dev/disk/by-partlabel/asm-disk6 + /dev/disk/by-partlabel/asm-disk7 + /dev/disk/by-partlabel/asm-disk8 State: AVAILABLE Asm Details: Diskgroup: Disks: - /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8 Name: CRSDATA Redundancy: EXTERN Disks: - /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8 + Name: REDO + Redundancy: EXTERN + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8 Name: RECO Redundancy: EXTERN Disks: - /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8 Name: DBDATA Redundancy: EXTERN Conditions: - Last Transition Time: 2025-08-26T19:06:09Z + Last Transition Time: 2025-10-09T05:26:31Z Message: reconcile has been queued Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued - Last Transition Time: 2025-08-26T19:14:51Z + Last Transition Time: 2025-10-09T05:37:15Z Message: reconcile completed successfully Observed Generation: 1 Reason: LastReconcileCycleCompleted @@ -179,22 +196,26 @@ Events: [grid@dbmc1-0 ~]$ ls -rlt /dev/disk/by-partlabel/asm-disk* -brw-rw----. 1 grid asmadmin 8, 33 Aug 26 19:02 /dev/disk/by-partlabel/asm-disk2 -brw-rw----. 1 grid asmadmin 8, 49 Aug 26 19:15 /dev/disk/by-partlabel/asm-disk4 -brw-rw----. 1 grid asmadmin 8, 65 Aug 26 19:16 /dev/disk/by-partlabel/asm-disk6 -brw-rw----. 1 grid asmadmin 8, 113 Aug 26 19:16 /dev/disk/by-partlabel/asm-disk5 -brw-rw----. 1 grid asmadmin 8, 81 Aug 26 19:16 /dev/disk/by-partlabel/asm-disk1 -brw-rw----. 1 grid asmadmin 8, 97 Aug 26 19:16 /dev/disk/by-partlabel/asm-disk3 +brw-rw----. 1 grid asmadmin 8, 65 Oct 9 05:21 /dev/disk/by-partlabel/asm-disk6 +brw-rw----. 1 grid asmadmin 8, 49 Oct 9 05:21 /dev/disk/by-partlabel/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 145 Oct 9 05:38 /dev/disk/by-partlabel/asm-disk8 +brw-rw----. 1 grid asmadmin 8, 129 Oct 9 05:38 /dev/disk/by-partlabel/asm-disk1 +brw-rw----. 1 grid asmadmin 8, 33 Oct 9 05:38 /dev/disk/by-partlabel/asm-disk5 +brw-rw----. 1 grid asmadmin 8, 97 Oct 9 05:38 /dev/disk/by-partlabel/asm-disk7 +brw-rw----. 1 grid asmadmin 8, 113 Oct 9 05:38 /dev/disk/by-partlabel/asm-disk3 +brw-rw----. 1 grid asmadmin 8, 81 Oct 9 05:38 /dev/disk/by-partlabel/asm-disk4 [grid@dbmc1-0 ~]$ [grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ [grid@dbmc1-0 ~]$ asmcmd lsdg State Type Rebal Sector Logical_Sector Block AU Total_MB Free_MB Req_mir_free_MB Usable_file_MB Offline_disks Voting_files Name MOUNTED EXTERN N 512 512 4096 4194304 102392 102276 0 102276 0 N CRSDATA/ -MOUNTED EXTERN N 512 512 4096 1048576 102396 96243 0 96243 0 N DBDATA/ -MOUNTED EXTERN N 512 512 4096 1048576 102396 99226 0 99226 0 N RECO/ -[grid@dbmc1-0 ~]$ +MOUNTED EXTERN N 512 512 4096 1048576 102396 99345 0 99345 0 N DBDATA/ +MOUNTED EXTERN N 512 512 4096 1048576 102396 102329 0 102329 0 N RECO/ +MOUNTED EXTERN N 512 512 4096 1048576 102396 99226 0 99226 0 N REDO/ [grid@dbmc1-0 ~]$ [grid@dbmc1-0 ~]$ asmcmd lsdsk + Path /dev/disk/by-partlabel/asm-disk1 /dev/disk/by-partlabel/asm-disk2 @@ -202,11 +223,12 @@ Path /dev/disk/by-partlabel/asm-disk4 /dev/disk/by-partlabel/asm-disk5 /dev/disk/by-partlabel/asm-disk6 -[grid@dbmc1-0 ~]$ +/dev/disk/by-partlabel/asm-disk7 +/dev/disk/by-partlabel/asm-disk8 [grid@dbmc1-0 ~]$ [grid@dbmc1-0 ~]$ sqlplus "/ as sysdba" -SQL*Plus: Release 19.0.0.0.0 - Production on Tue Aug 26 19:16:34 2025 +SQL*Plus: Release 19.0.0.0.0 - Production on Thu Oct 9 05:38:48 2025 Version 19.28.0.0.0 Copyright (c) 1982, 2025, Oracle. All rights reserved. @@ -236,13 +258,58 @@ ORDER BY DISKGROUP_NAME PATH MOUNT_S HEADER_STATU STATE TOTAL_MB FREE_MB -------------------- -------------------------------------------------- ------- ------------ -------- ---------- ---------- -CRSDATA /dev/disk/by-partlabel/asm-disk1 CACHED MEMBER NORMAL 51196 51140 -CRSDATA /dev/disk/by-partlabel/asm-disk2 CACHED MEMBER NORMAL 51196 51136 -DBDATA /dev/disk/by-partlabel/asm-disk3 CACHED MEMBER NORMAL 51198 48123 -DBDATA /dev/disk/by-partlabel/asm-disk4 CACHED MEMBER NORMAL 51198 48120 -RECO /dev/disk/by-partlabel/asm-disk5 CACHED MEMBER NORMAL 51198 49614 -RECO /dev/disk/by-partlabel/asm-disk6 CACHED MEMBER NORMAL 51198 49612 +CRSDATA /dev/disk/by-partlabel/asm-disk1 CACHED MEMBER NORMAL 51196 51136 +CRSDATA /dev/disk/by-partlabel/asm-disk2 CACHED MEMBER NORMAL 51196 51140 +DBDATA /dev/disk/by-partlabel/asm-disk3 CACHED MEMBER NORMAL 51198 49672 +DBDATA /dev/disk/by-partlabel/asm-disk4 CACHED MEMBER NORMAL 51198 49673 +RECO /dev/disk/by-partlabel/asm-disk5 CACHED MEMBER NORMAL 51198 51165 +RECO /dev/disk/by-partlabel/asm-disk6 CACHED MEMBER NORMAL 51198 51164 +REDO /dev/disk/by-partlabel/asm-disk7 CACHED MEMBER NORMAL 51198 49616 +REDO /dev/disk/by-partlabel/asm-disk8 CACHED MEMBER NORMAL 51198 49610 + +8 rows selected. + + + + +[root@dbmc1-0 rac-work-dir]# su - oracle +Last login: Thu Oct 9 05:39:36 UTC 2025 on pts/0 +[oracle@dbmc1-0 ~]$ +[oracle@dbmc1-0 ~]$ +[oracle@dbmc1-0 ~]$ export ORACLE_SID=PORCLCDB +[oracle@dbmc1-0 ~]$ +[oracle@dbmc1-0 ~]$ sqlplus "/ as sysdba" + +SQL*Plus: Release 19.0.0.0.0 - Production on Thu Oct 9 05:39:43 2025 +Version 19.28.0.0.0 + +Copyright (c) 1982, 2025, Oracle. All rights reserved. + + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> show pdbs + + CON_ID CON_NAME OPEN MODE RESTRICTED +---------- ------------------------------ ---------- ---------- + 2 PDB$SEED READ ONLY NO + 3 TESTPDB READ WRITE NO +SQL> show parameter DB_CREATE_ONLINE_LOG_DEST_1 + +NAME TYPE VALUE +------------------------------------ ----------- ------------------------------ +db_create_online_log_dest_1 string +REDO +SQL> show parameter db_create_file_dest -6 rows selected. +NAME TYPE VALUE +------------------------------------ ----------- ------------------------------ +db_create_file_dest string +DBDATA +SQL> show parameter db_recovery_file_dest -SQL> \ No newline at end of file +NAME TYPE VALUE +------------------------------------ ----------- ------------------------------ +db_recovery_file_dest string +RECO +db_recovery_file_dest_size big integer 20058M +SQL> diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml index f566767c..d99c4abf 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups.yaml @@ -54,6 +54,8 @@ spec: - /dev/disk/by-partlabel/asm-disk4 # ASM disk device path 4 - /dev/disk/by-partlabel/asm-disk5 # ASM disk device path 5 - /dev/disk/by-partlabel/asm-disk6 # ASM disk device path 6 + - /dev/disk/by-partlabel/asm-disk7 # ASM disk device path 7 + - /dev/disk/by-partlabel/asm-disk8 # ASM disk device path 8 # SSH key secret used for remote access and automation sshKeySecret: @@ -110,11 +112,13 @@ spec: dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path dbBase: "/u01/app/oracle" # Oracle base directory crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM devices for CRSDATA Diskgroup - dbAsmDeviceList: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 # Comma-separated list of ASM devices for DATA Diskgroup - recoAsmDeviceList: /dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 # Comma-separated list of ASM devices for RECO Diskgroup - crsAsmDiskDg: "+CRSDATA" # Name of the Diskgroup for Voting Disks + dbAsmDeviceList: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4 # Comma-separated list of ASM device files for DBDATA Diskgroup for database files + recoAsmDeviceList: /dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 # Comma-separated list of ASM device files for RECO Diskgroup for Recovery Area files + redoAsmDeviceList: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8 # Comma-separated list of ASM device files for REDO Diskgroup for Redo files + crsAsmDiskDg: "+CRSDATA" # Name of the Diskgroup for CRS Files dbRecoveryFileDest: "+RECO" # Name of the Diskgroup for Recovery Files dbDataFileDestDg: "+DBDATA" # Name of the Diskgroup for Database Files + redoAsmDiskDg: "+REDO" # Name of the Diskgroup for Redo Files inventory: "/u01/app/oraInventory" # Inventory location gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP dbSwZipFile: "db_home.zip" # Database software ZIP @@ -123,4 +127,5 @@ spec: processes: 2000 # Oracle process limit cpuCount: 4 # Number of CPUs to allocate dbName: "PORCLCDB" # Oracle database name + pdbName: "TESTPDB" # PDB name, in case you want to explicitly specify the PDB Name hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.txt b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.txt index 8ad007fa..cabd436e 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.txt +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.txt @@ -7,24 +7,30 @@ Annotations: OracleRestarts.database.oracle.com/old-spec: API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-09-02T17:05:56Z + Creation Timestamp: 2025-10-09T02:27:24Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 230588194 - UID: 63430f74-7889-4e7e-8f23-7860f394ff58 + Resource Version: 246182750 + UID: 93e5d361-47f7-4be0-a953-b23010228ebd Spec: Asm Storage Details: Disks By Size: Disk Names: /dev/disk/by-partlabel/asm-disk1 /dev/disk/by-partlabel/asm-disk2 + Storage Size In Gb: 50 + Disk Names: /dev/disk/by-partlabel/asm-disk3 /dev/disk/by-partlabel/asm-disk4 /dev/disk/by-partlabel/asm-disk5 /dev/disk/by-partlabel/asm-disk6 + Storage Size In Gb: 100 + Disk Names: /dev/disk/by-partlabel/asm-disk7 /dev/disk/by-partlabel/asm-disk8 + Storage Size In Gb: 100 + Disk Names: /dev/disk/by-partlabel/asm-disk9 /dev/disk/by-partlabel/asm-disk10 Storage Size In Gb: 50 @@ -46,21 +52,26 @@ Spec: Grid Base: /u01/app/grid Grid Home: /u01/app/19c/grid Grid Response File: - Grid Sw Zip File: grid_home.zip - Host Sw Stage Location: /scratch/software/stage - Inventory: /u01/app/oraInventory - Pga Size: 1G - Processes: 2000 - Reco Asm Device List: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9,/dev/disk/by-partlabel/asm-disk10 - Reco Asm Disk Dg Redudancy: NORMAL - Sga Size: 3G - Sw Mount Location: /u01 + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pdb Name: TESTPDB + Pga Size: 1G + Processes: 2000 + Reco Asm Device List: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8 + Reco Asm Disk Dg Redundancy: EXTERNAL + Redo Asm Device List: /dev/disk/by-partlabel/asm-disk9,/dev/disk/by-partlabel/asm-disk10 + Redo Asm Disk Dg: +REDO + Redo Asm Disk Dg Redundancy: EXTERNAL + Sga Size: 3G + Sw Mount Location: /u01 Db Secret: Key File Mount Location: /mnt/.dbsecrets Key File Name: key.pem Name: db-user-pass-pkutl Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc + Enable Ons: enable Image: localhost/oracle/database-orestart:19.3.0-slim Image Pull Policy: IfNotPresent Inst Details: @@ -71,17 +82,21 @@ Spec: Value: true Host Sw Location: /scratch/orestart/ Name: dbmc1 - Node Port Svc: - Name: dbmc1-service-nodeport - Svc Type: nodeport + Worker Node: + 10.0.10.58 + Lb Service: + Node Port Svc: + Name: dbmc1-service-nodeport Port Mappings: Node Port: 30007 Port: 1521 Protocol: TCP Target Port: 1521 - Worker Node: - 10.0.10.58 - Lb Service: + Node Port: 30200 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: nodeport Resources: Limits: Cpu: 2 @@ -110,8 +125,8 @@ Status: Oracle Restart Nodes: Name: dbmc1-0 Node Details: - Instance State: NOTAVAILABLE - Pod State: PODAVAILABLE + Instance State: OPEN + Pod State: AVAILABLE Cluster State: HEALTHY Mounted Devices: /dev/disk/by-partlabel/asm-disk1 @@ -124,34 +139,38 @@ Status: /dev/disk/by-partlabel/asm-disk8 /dev/disk/by-partlabel/asm-disk9 /dev/disk/by-partlabel/asm-disk10 - State: FAILED + State: AVAILABLE Asm Details: Diskgroup: Disks: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk10,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9 Name: CRSDATA Redundancy: EXTERN + Disks: + /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk10,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9 + Name: REDO + Redundancy: EXTERN Disks: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk10,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9 Name: RECO - Redundancy: NORMAL + Redundancy: EXTERN Disks: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk10,/dev/disk/by-partlabel/asm-disk2,/dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6,/dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9 Name: DBDATA Redundancy: NORMAL Conditions: - Last Transition Time: 2025-09-02T17:05:56Z - Message: reconcile completed successfully - Observed Generation: 1 - Reason: LastReconcileCycleCompleted - Status: True - Type: ReconcileComplete - Last Transition Time: 2025-09-02T17:32:22Z + Last Transition Time: 2025-10-09T02:40:30Z Message: reconcile has been queued Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued + Last Transition Time: 2025-10-09T03:32:32Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete Config Params: Crs Asm Device List: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 Crs Asm Disk Dg: +CRSDATA @@ -168,7 +187,7 @@ Status: Grid Home: /u01/app/19c/grid Grid Response File: Inventory: /u01/app/oraInventory - Reco Asm Device List: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9,/dev/disk/by-partlabel/asm-disk10 + Reco Asm Device List: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8 Connect String: dbmc1-0:1521/PORCLCDB Db Secret: Key File Mount Location: /mnt/.dbsecrets @@ -177,38 +196,38 @@ Status: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Db State: OPEN - External Connect String: Pending + External Connect String: 129.146.0.149:30007/soepdb Pdb Connect String: dbmc1-0:1521/ORCLPDB Release Update: 19.28.0.0.0 Role: PRIMARY Service Details: Name: soepdb Svc State: service soepdb is running - State: FAILED + State: AVAILABLE Events: + [grid@dbmc1-0 ~]$ ls -rlt /dev/disk/by-partlabel/asm-disk* -brw-rw----. 1 grid asmadmin 8, 33 Sep 2 17:13 /dev/disk/by-partlabel/asm-disk2 -brw-rw----. 1 grid asmadmin 8, 97 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk1 -brw-rw----. 1 grid asmadmin 8, 145 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk9 -brw-rw----. 1 grid asmadmin 8, 81 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk4 -brw-rw----. 1 grid asmadmin 8, 65 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk8 -brw-rw----. 1 grid asmadmin 8, 129 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk7 -brw-rw----. 1 grid asmadmin 8, 113 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk6 -brw-rw----. 1 grid asmadmin 8, 49 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk5 -brw-rw----. 1 grid asmadmin 8, 177 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk3 -brw-rw----. 1 grid asmadmin 8, 161 Sep 2 17:35 /dev/disk/by-partlabel/asm-disk10 -[grid@dbmc1-0 ~]$ -[grid@dbmc1-0 ~]$ +brw-rw----. 1 grid asmadmin 8, 49 Oct 9 02:35 /dev/disk/by-partlabel/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 145 Oct 9 02:56 /dev/disk/by-partlabel/asm-disk8 +brw-rw----. 1 grid asmadmin 8, 129 Oct 9 03:32 /dev/disk/by-partlabel/asm-disk6 +brw-rw----. 1 grid asmadmin 8, 177 Oct 9 03:35 /dev/disk/by-partlabel/asm-disk10 +brw-rw----. 1 grid asmadmin 8, 161 Oct 9 03:35 /dev/disk/by-partlabel/asm-disk9 +brw-rw----. 1 grid asmadmin 8, 33 Oct 9 03:35 /dev/disk/by-partlabel/asm-disk1 +brw-rw----. 1 grid asmadmin 8, 65 Oct 9 03:35 /dev/disk/by-partlabel/asm-disk7 +brw-rw----. 1 grid asmadmin 8, 113 Oct 9 03:35 /dev/disk/by-partlabel/asm-disk5 +brw-rw----. 1 grid asmadmin 8, 81 Oct 9 03:35 /dev/disk/by-partlabel/asm-disk4 +brw-rw----. 1 grid asmadmin 8, 97 Oct 9 03:35 /dev/disk/by-partlabel/asm-disk3 [grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM [grid@dbmc1-0 ~]$ asmcmd lsdg State Type Rebal Sector Logical_Sector Block AU Total_MB Free_MB Req_mir_free_MB Usable_file_MB Offline_disks Voting_files Name MOUNTED EXTERN N 512 512 4096 4194304 102392 102276 0 102276 0 N CRSDATA/ -MOUNTED NORMAL N 512 512 4096 1048576 204792 192279 51198 70540 0 N DBDATA/ -MOUNTED NORMAL N 512 512 4096 1048576 204792 198267 51198 73534 0 N RECO/ +MOUNTED NORMAL N 512 512 4096 1048576 204792 198553 51198 73677 0 N DBDATA/ +MOUNTED EXTERN N 512 512 4096 1048576 102396 102310 0 102310 0 N RECO/ +MOUNTED EXTERN N 512 512 4096 1048576 102396 99226 0 99226 0 N REDO/ [grid@dbmc1-0 ~]$ [grid@dbmc1-0 ~]$ asmcmd lsdsk Path @@ -226,7 +245,7 @@ Path [grid@dbmc1-0 ~]$ [grid@dbmc1-0 ~]$ sqlplus "/ as sysdba" -SQL*Plus: Release 19.0.0.0.0 - Production on Tue Sep 2 17:35:52 2025 +SQL*Plus: Release 19.0.0.0.0 - Production on Thu Oct 9 03:35:52 2025 Version 19.28.0.0.0 Copyright (c) 1982, 2025, Oracle. All rights reserved. @@ -256,17 +275,61 @@ ORDER BY DISKGROUP_NAME PATH MOUNT_S HEADER_STATU STATE TOTAL_MB FREE_MB -------------------- -------------------------------------------------- ------- ------------ -------- ---------- ---------- -CRSDATA /dev/disk/by-partlabel/asm-disk1 CACHED MEMBER NORMAL 51196 51140 -CRSDATA /dev/disk/by-partlabel/asm-disk2 CACHED MEMBER NORMAL 51196 51136 -DBDATA /dev/disk/by-partlabel/asm-disk3 CACHED MEMBER NORMAL 51198 48073 -DBDATA /dev/disk/by-partlabel/asm-disk4 CACHED MEMBER NORMAL 51198 48070 -DBDATA /dev/disk/by-partlabel/asm-disk5 CACHED MEMBER NORMAL 51198 48071 -DBDATA /dev/disk/by-partlabel/asm-disk6 CACHED MEMBER NORMAL 51198 48065 -RECO /dev/disk/by-partlabel/asm-disk7 CACHED MEMBER NORMAL 51198 49565 -RECO /dev/disk/by-partlabel/asm-disk8 CACHED MEMBER NORMAL 51198 49568 -RECO /dev/disk/by-partlabel/asm-disk9 CACHED MEMBER NORMAL 51198 49566 -RECO /dev/disk/by-partlabel/asm-disk10 CACHED MEMBER NORMAL 51198 49568 +CRSDATA /dev/disk/by-partlabel/asm-disk1 CACHED MEMBER NORMAL 51196 51132 +CRSDATA /dev/disk/by-partlabel/asm-disk2 CACHED MEMBER NORMAL 51196 51144 +DBDATA /dev/disk/by-partlabel/asm-disk3 CACHED MEMBER NORMAL 51198 49635 +DBDATA /dev/disk/by-partlabel/asm-disk4 CACHED MEMBER NORMAL 51198 49634 +DBDATA /dev/disk/by-partlabel/asm-disk5 CACHED MEMBER NORMAL 51198 49644 +DBDATA /dev/disk/by-partlabel/asm-disk6 CACHED MEMBER NORMAL 51198 49640 +RECO /dev/disk/by-partlabel/asm-disk7 CACHED MEMBER NORMAL 51198 51152 +RECO /dev/disk/by-partlabel/asm-disk8 CACHED MEMBER NORMAL 51198 51158 +REDO /dev/disk/by-partlabel/asm-disk9 CACHED MEMBER NORMAL 51198 49610 +REDO /dev/disk/by-partlabel/asm-disk10 CACHED MEMBER NORMAL 51198 49616 10 rows selected. -SQL> \ No newline at end of file +SQL> + + + + + +[oracle@dbmc1-0 ~]$ export ORACLE_SID=PORCLCDB +[oracle@dbmc1-0 ~]$ sqlplus "/ as sysdba" + +SQL*Plus: Release 19.0.0.0.0 - Production on Thu Oct 9 03:45:27 2025 +Version 19.28.0.0.0 + +Copyright (c) 1982, 2025, Oracle. All rights reserved. + + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> +SQL> show pdbs + + CON_ID CON_NAME OPEN MODE RESTRICTED +---------- ------------------------------ ---------- ---------- + 2 PDB$SEED READ ONLY NO + 3 TESTPDB READ WRITE NO +SQL> +SQL> show parameter DB_CREATE_ONLINE_LOG_DEST_1 + +NAME TYPE VALUE +------------------------------------ ----------- ------------------------------ +db_create_online_log_dest_1 string +REDO +SQL> +SQL> show parameter db_create_file_dest + +NAME TYPE VALUE +------------------------------------ ----------- ------------------------------ +db_create_file_dest string +DBDATA +SQL> +SQL> show parameter db_recovery_file_dest + +NAME TYPE VALUE +------------------------------------ ----------- ------------------------------ +db_recovery_file_dest string +RECO +db_recovery_file_dest_size big integer 20058M \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml index 0abd8d9d..3fc9c56f 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml @@ -46,18 +46,24 @@ spec: # ASM (Automatic Storage Management) storage configuration asmStorageDetails: disksBySize: - - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage, will be used for CRSDATA Diskgroup diskNames: - /dev/disk/by-partlabel/asm-disk1 # ASM disk device path 1 - /dev/disk/by-partlabel/asm-disk2 # ASM disk device path 2 + - storageSizeInGb: 100 # Each disk in this group has 100Gi of storage, will be used for DATA Diskgroup + diskNames: - /dev/disk/by-partlabel/asm-disk3 # ASM disk device path 3 - /dev/disk/by-partlabel/asm-disk4 # ASM disk device path 4 - /dev/disk/by-partlabel/asm-disk5 # ASM disk device path 5 - /dev/disk/by-partlabel/asm-disk6 # ASM disk device path 6 + - storageSizeInGb: 100 # Each disk in this group has 100Gi of storage, will be used for RECO Diskgroup + diskNames: - /dev/disk/by-partlabel/asm-disk7 # ASM disk device path 7 - /dev/disk/by-partlabel/asm-disk8 # ASM disk device path 8 + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage, will be used for REDO Diskgroup + diskNames: - /dev/disk/by-partlabel/asm-disk9 # ASM disk device path 9 - - /dev/disk/by-partlabel/asm-disk10 # ASM disk device path 10 + - /dev/disk/by-partlabel/asm-disk10 # ASM disk device path 10 # SSH key secret used for remote access and automation sshKeySecret: @@ -113,15 +119,18 @@ spec: gridBase: "/u01/app/grid" # Grid base directory dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path dbBase: "/u01/app/oracle" # Oracle base directory - crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM devices for CRSDATA Diskgroup - dbAsmDeviceList: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 # Comma-separated list of ASM devices for DATA Diskgroup - recoAsmDeviceList: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8,/dev/disk/by-partlabel/asm-disk9,/dev/disk/by-partlabel/asm-disk10 # Comma-separated list of ASM devices for RECO Diskgroup - crsAsmDiskDg: "+CRSDATA" # Name of the Diskgroup for Voting Disks - dbRecoveryFileDest: "+RECO" # Name of the Diskgroup for Recovery Files + crsAsmDiskDg: "+CRSDATA" # Name of the Diskgroup for CRS Files dbDataFileDestDg: "+DBDATA" # Name of the Diskgroup for Database Files + dbRecoveryFileDest: "+RECO" # Name of the Diskgroup for Recovery Files + redoAsmDiskDg: "+REDO" # Name of the Diskgroup for Redo Files + crsAsmDeviceList: /dev/disk/by-partlabel/asm-disk1,/dev/disk/by-partlabel/asm-disk2 # Comma-separated list of ASM devices for CRSDATA Diskgroup + dbAsmDeviceList: /dev/disk/by-partlabel/asm-disk3,/dev/disk/by-partlabel/asm-disk4,/dev/disk/by-partlabel/asm-disk5,/dev/disk/by-partlabel/asm-disk6 # Comma-separated list of ASM device files for DBDATA Diskgroup for database files + recoAsmDeviceList: /dev/disk/by-partlabel/asm-disk7,/dev/disk/by-partlabel/asm-disk8 # Comma-separated list of ASM device files for RECO Diskgroup for Recovery Area files + redoAsmDeviceList: /dev/disk/by-partlabel/asm-disk9,/dev/disk/by-partlabel/asm-disk10 # Comma-separated list of ASM device files for REDO Diskgroup for Redo files crsAsmDiskDgRedundancy: "EXTERNAL" # Specify the Redudancy for the diskgroup with CRS Files dbAsmDiskDgRedundancy: "NORMAL" # Specify the Redudancy for the diskgroup with Database Files - recoAsmDiskDgRedudancy: "NORMAL" # Specify the Redudancy for the diskgroup with Recovery Files + recoAsmDiskDgRedundancy: "EXTERNAL" # Specify the Redudancy for the diskgroup with Recovery Files + redoAsmDiskDgRedundancy: "EXTERNAL" # Specify the Redudancy for the diskgroup with Redo Files inventory: "/u01/app/oraInventory" # Inventory location gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP dbSwZipFile: "db_home.zip" # Database software ZIP @@ -130,4 +139,5 @@ spec: processes: 2000 # Oracle process limit cpuCount: 4 # Number of CPUs to allocate dbName: "PORCLCDB" # Oracle database name + pdbName: "TESTPDB" # PDB name, in case you want to explicitly specify the PDB Name hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md new file mode 100644 index 00000000..55d13cd2 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md @@ -0,0 +1,52 @@ +# Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy and option to specify separate storage class +# In this Usecase: +The Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files, Recovery Area Files and Redo Log Files. Different Disk Groups have different redundancy levels. + +This example uses `oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: + * Oracle Restart Pod + * Headless services for Oracle Restart. + * Oracle Database Node hostname. + * Persistent volumes created automatically based on specified disks for Oracle ASM Storage. + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. + * Namespace: `orestart` + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. + * The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `swDgStorageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. + * Name of Custom Storage Class for Diskgroup having CRS files is specified by `crsDgStorageClass`. + * Name of Custom Storage Class for Diskgroup having Database files is specified by `dataDgStorageClass`. + * Name of Custom Storage Class for Diskgroup having Recovery Area files is specified by `recoDgStorageClass`. + * Name of Custom Storage Class for Diskgroup having Redo Log files is specified by `redoDgStorageClass`. + +### In this example, + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml` file to point to the container image you have built. + * The disks provisioned using custom storage classes are mounted inside the Oracle Restart Pod as `/dev/asm-disk1` to `/dev/asm-disk10`. + * Specify the size of disk devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + * The Diskgroup for CRS files is specified by `crsAsmDiskDg` and the disks on the worker nodes for this diskgroup are specified by `crsAsmDeviceList`. + * The Diskgroup for Database files is specified by `dbDataFileDestDg` and the disks on the worker nodes for this diskgroup are specified by `dbAsmDeviceList`. + * The Diskgroup for Recovery Area files is specified by `dbRecoveryFileDest` and the disks on the worker nodes for this diskgroup are specified by `recoAsmDeviceList`. + * The Diskgroup for Redo Log files is specified by `redoAsmDiskDg` and the disks on the worker nodes for this diskgroup are specified by `redoAsmDeviceList`. + * Redundancy level for the diskgroup with CRS files is mentioned by `crsAsmDiskDgRedundancy`. + * Redundancy level for the diskgroup with Database files is mentioned by `dbAsmDiskDgRedundancy`. + * Redundancy level for the diskgroup with Recovery files is mentioned by `recoAsmDiskDgRedudancy`. + * Redundancy level for the diskgroup with Redo Log files is mentioned by `redoAsmDiskDgRedundancy`. + +### Steps: Deploy Oracle Restart Database +* Use the file: [oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml](./oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml) for this use case as below: +* Deploy the `oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml` file: + ```sh + kubectl apply -f oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml + oraclerestart.database.oracle.com/oraclerestart-sample created + ``` +* Check the status of the deployment: + ```sh + # Check the status of the Kubernetes Pods: + kubectl get all -n orestart + + # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": + kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" + =============================== + ORACLE DATABASE IS READY TO USE + =============================== + ``` +* Check Details of Kubernetes CRD Object as in this [example](./oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.txt) +* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.txt b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.txt new file mode 100644 index 00000000..595258d8 --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.txt @@ -0,0 +1,344 @@ +$ kubectl describe oraclerestarts.database.oracle.com/oraclerestart-sample -n orestart +Name: oraclerestart-sample +Namespace: orestart +Labels: +Annotations: OracleRestarts.database.oracle.com/old-spec: + {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":300,"workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","value":"tru... +API Version: database.oracle.com/v4 +Kind: OracleRestart +Metadata: + Creation Timestamp: 2025-10-09T03:57:56Z + Finalizers: + database.oracle.com/oraclerestartfinalizer + Generation: 1 + Resource Version: 246198029 + UID: 19d43856-477c-43d2-a01f-8d1492107370 +Spec: + Asm Storage Details: + Disks By Size: + Disk Names: + /dev/asm-disk1 + /dev/asm-disk2 + Storage Size In Gb: 50 + Disk Names: + /dev/asm-disk3 + /dev/asm-disk4 + /dev/asm-disk5 + /dev/asm-disk6 + Storage Size In Gb: 100 + Disk Names: + /dev/asm-disk7 + /dev/asm-disk8 + Storage Size In Gb: 100 + Disk Names: + /dev/asm-disk9 + /dev/asm-disk10 + Storage Size In Gb: 50 + Config Params: + Cpu Count: 4 + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 + Crs Asm Disk Dg: +CRSDATA + Crs Asm Disk Dg Redundancy: EXTERNAL + Db Asm Device List: /dev/asm-disk3,/dev/asm-disk4,/dev/asm-disk5,/dev/asm-disk6 + Db Asm Disk Dg Redundancy: NORMAL + Db Base: /u01/app/oracle + Db Char Set: AL32UTF8 + Db Data File Dest Dg: +DBDATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +RECO + Db Response File: + Db Sw Zip File: db_home.zip + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Grid Sw Zip File: grid_home.zip + Host Sw Stage Location: /scratch/software/stage + Inventory: /u01/app/oraInventory + Pdb Name: TESTPDB + Pga Size: 1G + Processes: 2000 + Reco Asm Device List: /dev/asm-disk7,/dev/asm-disk8 + Reco Asm Disk Dg Redundancy: EXTERNAL + Redo Asm Device List: /dev/asm-disk9,/dev/asm-disk10 + Redo Asm Disk Dg: +REDO + Redo Asm Disk Dg Redundancy: EXTERNAL + Sga Size: 3G + Sw Mount Location: /u01 + Crs Dg Storage Class: oci-bv + Data Dg Storage Class: oci-bv + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Enable Ons: enable + Image: localhost/oracle/database-orestart:19.3.0-slim + Image Pull Policy: IfNotPresent + Inst Details: + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Name: dbmc1 + Sw Loc Storage Size In Gb: 300 + Worker Node: + 10.0.10.58 + Lb Service: + Node Port Svc: + Name: dbmc1-service-nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Node Port: 30200 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: nodeport + Reco Dg Storage Class: oci-bv + Redo Dg Storage Class: oci-bv + Resources: + Limits: + Cpu: 2 + Memory: 16Gi + Requests: + Cpu: 2 + Memory: 16Gi + Security Context: + Sysctls: + Name: kernel.shmall + Value: 2097152 + Name: kernel.sem + Value: 250 32000 100 128 + Name: kernel.shmmax + Value: 8589934592 + Name: kernel.shmmni + Value: 4096 + Service Details: + Name: soepdb + Ssh Key Secret: + Key Mount Location: /mnt/.ssh + Name: ssh-key-secret + Priv Key Secret Name: ssh-privkey + Pub Key Secret Name: ssh-pubkey + Sw Dg Storage Class: oci-bv +Status: + Oracle Restart Nodes: + Name: dbmc1-0 + Node Details: + Instance State: OPEN + Pod State: AVAILABLE + Cluster State: HEALTHY + Mounted Devices: + /dev/asm-disk1 + /dev/asm-disk2 + /dev/asm-disk3 + /dev/asm-disk4 + /dev/asm-disk5 + /dev/asm-disk6 + /dev/asm-disk7 + /dev/asm-disk8 + /dev/asm-disk9 + /dev/asm-disk10 + State: AVAILABLE + Asm Details: + Diskgroup: + Disks: + /dev/asm-disk1,/dev/asm-disk10,/dev/asm-disk2,/dev/asm-disk3,/dev/asm-disk4,/dev/asm-disk5,/dev/asm-disk6,/dev/asm-disk7,/dev/asm-disk8,/dev/asm-disk9 + Name: CRSDATA + Redundancy: EXTERN + Disks: + /dev/asm-disk1,/dev/asm-disk10,/dev/asm-disk2,/dev/asm-disk3,/dev/asm-disk4,/dev/asm-disk5,/dev/asm-disk6,/dev/asm-disk7,/dev/asm-disk8,/dev/asm-disk9 + Name: REDO + Redundancy: EXTERN + Disks: + /dev/asm-disk1,/dev/asm-disk10,/dev/asm-disk2,/dev/asm-disk3,/dev/asm-disk4,/dev/asm-disk5,/dev/asm-disk6,/dev/asm-disk7,/dev/asm-disk8,/dev/asm-disk9 + Name: RECO + Redundancy: EXTERN + Disks: + /dev/asm-disk1,/dev/asm-disk10,/dev/asm-disk2,/dev/asm-disk3,/dev/asm-disk4,/dev/asm-disk5,/dev/asm-disk6,/dev/asm-disk7,/dev/asm-disk8,/dev/asm-disk9 + Name: DBDATA + Redundancy: NORMAL + Conditions: + Last Transition Time: 2025-10-09T04:11:38Z + Message: reconcile has been queued + Observed Generation: 1 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2025-10-09T04:20:34Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Config Params: + Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 + Crs Asm Disk Dg: +CRSDATA + Crs Asm Disk Dg Redundancy: EXTERNAL + Db Asm Device List: /dev/asm-disk3,/dev/asm-disk4,/dev/asm-disk5,/dev/asm-disk6 + Db Asm Disk Dg Redundancy: NORMAL + Db Base: /u01/app/oracle + Db Data File Dest Dg: +DBDATA + Db Home: /u01/app/oracle/product/19c/dbhome_1 + Db Name: PORCLCDB + Db Recovery File Dest: +RECO + Db Response File: + Grid Base: /u01/app/grid + Grid Home: /u01/app/19c/grid + Grid Response File: + Inventory: /u01/app/oraInventory + Reco Asm Device List: /dev/asm-disk7,/dev/asm-disk8 + Connect String: dbmc1-0:1521/PORCLCDB + Db Secret: + Key File Mount Location: /mnt/.dbsecrets + Key File Name: key.pem + Name: db-user-pass-pkutl + Pwd File Mount Location: /mnt/.dbsecrets + Pwd File Name: pwdfile.enc + Db State: OPEN + External Connect String: 129.146.0.149:30007/soepdb + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY + Service Details: + Name: soepdb + Svc State: service soepdb is running + State: AVAILABLE +Events: + + + + + +[grid@dbmc1-0 ~]$ ls -rlt /dev/asm-disk* +brw-rw----. 1 grid asmadmin 65, 80 Oct 9 04:04 /dev/asm-disk2 +brw-rw----. 1 grid asmadmin 8, 224 Oct 9 04:06 /dev/asm-disk8 +brw-rw----. 1 grid asmadmin 65, 0 Oct 9 04:18 /dev/asm-disk6 +brw-rw----. 1 grid asmadmin 65, 64 Oct 9 04:21 /dev/asm-disk1 +brw-rw----. 1 grid asmadmin 65, 32 Oct 9 04:21 /dev/asm-disk9 +brw-rw----. 1 grid asmadmin 8, 208 Oct 9 04:21 /dev/asm-disk7 +brw-rw----. 1 grid asmadmin 8, 240 Oct 9 04:21 /dev/asm-disk5 +brw-rw----. 1 grid asmadmin 65, 96 Oct 9 04:21 /dev/asm-disk4 +brw-rw----. 1 grid asmadmin 65, 48 Oct 9 04:21 /dev/asm-disk3 +brw-rw----. 1 grid asmadmin 8, 192 Oct 9 04:21 /dev/asm-disk10 +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ export ORACLE_SID=+ASM +[grid@dbmc1-0 ~]$ asmcmd lsdg +State Type Rebal Sector Logical_Sector Block AU Total_MB Free_MB Req_mir_free_MB Usable_file_MB Offline_disks Voting_files Name +MOUNTED EXTERN N 512 512 4096 4194304 102400 102284 0 102284 0 N CRSDATA/ +MOUNTED NORMAL N 512 512 4096 1048576 409600 403401 102400 150500 0 N DBDATA/ +MOUNTED EXTERN N 512 512 4096 1048576 204800 204733 0 204733 0 N RECO/ +MOUNTED EXTERN N 512 512 4096 1048576 102400 99230 0 99230 0 N REDO/ +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ asmcmd lsdsk +Path +/dev/asm-disk1 +/dev/asm-disk10 +/dev/asm-disk2 +/dev/asm-disk3 +/dev/asm-disk4 +/dev/asm-disk5 +/dev/asm-disk6 +/dev/asm-disk7 +/dev/asm-disk8 +/dev/asm-disk9 +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ +[grid@dbmc1-0 ~]$ sqlplus "/ as sysdba" + +SQL*Plus: Release 19.0.0.0.0 - Production on Thu Oct 9 04:22:20 2025 +Version 19.28.0.0.0 + +Copyright (c) 1982, 2025, Oracle. All rights reserved. + + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> set lines 200 +SQL> col DISKGROUP_NAME format a20 +SQL> col PATH format a50 +SQL> SELECT + dg.NAME AS DISKGROUP_NAME, + d.PATH, + d.MOUNT_STATUS, + d.HEADER_STATUS, + d.STATE, + d.TOTAL_MB, + d.FREE_MB +FROM + V$ASM_DISK d +JOIN + V$ASM_DISKGROUP dg ON d.GROUP_NUMBER = dg.GROUP_NUMBER +ORDER BY + dg.NAME, d.DISK_NUMBER; 2 3 4 5 6 7 8 9 10 11 12 13 14 + +DISKGROUP_NAME PATH MOUNT_S HEADER_STATU STATE TOTAL_MB FREE_MB +-------------------- -------------------------------------------------- ------- ------------ -------- ---------- ---------- +CRSDATA /dev/asm-disk1 CACHED MEMBER NORMAL 51200 51144 +CRSDATA /dev/asm-disk2 CACHED MEMBER NORMAL 51200 51140 +DBDATA /dev/asm-disk3 CACHED MEMBER NORMAL 102400 100846 +DBDATA /dev/asm-disk4 CACHED MEMBER NORMAL 102400 100851 +DBDATA /dev/asm-disk5 CACHED MEMBER NORMAL 102400 100851 +DBDATA /dev/asm-disk6 CACHED MEMBER NORMAL 102400 100853 +RECO /dev/asm-disk7 CACHED MEMBER NORMAL 102400 102366 +RECO /dev/asm-disk8 CACHED MEMBER NORMAL 102400 102367 +REDO /dev/asm-disk9 CACHED MEMBER NORMAL 51200 49613 +REDO /dev/asm-disk10 CACHED MEMBER NORMAL 51200 49617 + +10 rows selected. + +SQL> + + + + + +[oracle@dbmc1-0 ~]$ export ORACLE_SID=PORCLCDB +[oracle@dbmc1-0 ~]$ sqlplus "/ as sysdba" + +SQL*Plus: Release 19.0.0.0.0 - Production on Thu Oct 9 04:23:14 2025 +Version 19.28.0.0.0 + +Copyright (c) 1982, 2025, Oracle. All rights reserved. + + +Connected to: +Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production +Version 19.28.0.0.0 + +SQL> +SQL> show pdbs + + CON_ID CON_NAME OPEN MODE RESTRICTED +---------- ------------------------------ ---------- ---------- + 2 PDB$SEED READ ONLY NO + 3 TESTPDB READ WRITE NO +SQL> +SQL> +SQL> show parameter DB_CREATE_ONLINE_LOG_DEST_1 + +NAME TYPE VALUE +------------------------------------ ----------- ------------------------------ +db_create_online_log_dest_1 string +REDO +SQL> +SQL> +SQL> show parameter db_create_file_dest + +NAME TYPE VALUE +------------------------------------ ----------- ------------------------------ +db_create_file_dest string +DBDATA +SQL> +SQL> show parameter db_recovery_file_dest + +NAME TYPE VALUE +------------------------------------ ----------- ------------------------------ +db_recovery_file_dest string +RECO +db_recovery_file_dest_size big integer 20058M diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml new file mode 100644 index 00000000..175604bf --- /dev/null +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml @@ -0,0 +1,154 @@ +# API version of the OracleRestart Custom Resource +apiVersion: database.oracle.com/v4 + +# Resource type being created +kind: OracleRestart + +# Metadata section to define the resource's name and namespace +metadata: + name: oraclerestart-sample # Name of the OracleRestart instance + namespace: orestart # Kubernetes namespace to deploy into + +spec: + # Instance details for Oracle Restart + instDetails: + name: dbmc1 # Logical name of the OracleRestart instance + + # Worker nodes where the instance should be scheduled + workerNode: + - 10.0.10.58 # IP address of the target node for deployment + + # Size of the storage location for software. This parameter will be effective when the local SW location is NOT specified by hostSwLocation + swLocStorageSizeInGb: 300 + + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install + envVars: + - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. + value: "true" + - name: IGNORE_DB_PREREQS # If set to true, it will use flags -ignorePrereq and -ignorePrereqFailure during the DB Software Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the DB Software Installation. + value: "true" + + # NodePort service configuration for exposing DB port externally + nodePortSvc: + name: dbmc1-service-nodeport # Name of the Kubernetes Service + svcType: nodeport # Service type (NodePort to expose externally) + + # Mapping between container and host ports + portMappings: + - port: 1521 # Port inside container (Oracle Listener port) + targetPort: 1521 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30007 # Host port exposed on worker node (must be in NodePort range) + - port: 6200 # Port inside container (for ONS) + targetPort: 6200 # Target port for the service + protocol: TCP # Protocol type (TCP for Oracle) + nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) + enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" + + # Custom Storage class to be used for dynamic provisioning (if applicable) + # You have separate parameters to specify the storage class for all the diskgroups (CRS, DATA, RECO, REDO) + # You have a parameter to specify the storage class for the software location for the GI and RDBMS HOME + crsDgStorageClass: "oci-bv" # Specify storage class for the CRS Diskgroup, Currently using "oci-bv" but you can specify another available storage class as well + dataDgStorageClass: "oci-bv" # Specify storage class for the DATA Diskgroup, Currently using "oci-bv" but you can specify another available storage class as well + recoDgStorageClass: "oci-bv" # Specify storage class for the RECO Diskgroup, Currently using "oci-bv" but you can specify another available storage class as well + redoDgStorageClass: "oci-bv" # Specify storage class for the REDO Diskgroup, Currently using "oci-bv" but you can specify another available storage class as well + swDgStorageClass: "oci-bv" # Specify storage class for the storage location for software, Currently using "oci-bv" but you can specify another available storage class as well + + # ASM (Automatic Storage Management) storage configuration + asmStorageDetails: + disksBySize: + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage, will be used for CRSDATA Diskgroup + diskNames: + - /dev/asm-disk1 # ASM disk device path 1 + - /dev/asm-disk2 # ASM disk device path 2 + - storageSizeInGb: 100 # Each disk in this group has 100Gi of storage, will be used for DATA Diskgroup + diskNames: + - /dev/asm-disk3 # ASM disk device path 3 + - /dev/asm-disk4 # ASM disk device path 4 + - /dev/asm-disk5 # ASM disk device path 5 + - /dev/asm-disk6 # ASM disk device path 6 + - storageSizeInGb: 100 # Each disk in this group has 100Gi of storage, will be used for RECO Diskgroup + diskNames: + - /dev/asm-disk7 # ASM disk device path 7 + - /dev/asm-disk8 # ASM disk device path 8 + - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage, will be used for REDO Diskgroup + diskNames: + - /dev/asm-disk9 # ASM disk device path 9 + - /dev/asm-disk10 # ASM disk device path 10 + + # SSH key secret used for remote access and automation + sshKeySecret: + name: ssh-key-secret # Kubernetes secret name + privKeySecretName: ssh-privkey # Private key inside secret + pubKeySecretName: ssh-pubkey # Public key inside secret + + # Secret containing DB user credentials + dbSecret: + name: db-user-pass-pkutl # Secret name + keyFileName: key.pem # Key file name inside secret + pwdFileName: pwdfile.enc # Password file name inside secret + + # Image for Oracle Restart container + image: dbocir/oracle/database-orestart:19.3.0-slim # <-- Replace with your image registry + + # Policy to pull image + # Use the value as "Always" if you want use the latest version of the image from the container registry. + # Use the value as "IfNotPresent" it the image is not already present locally. + imagePullPolicy: IfNotPresent + + # Optional service account for security context permissions + # serviceAccountName: oraclerestart # <-- Required for OpenShift; optional for vanilla Kubernetes + + # Service-specific database name and related details + serviceDetails: + name: soepdb # Name of the Oracle service (pluggable DB) + + # Resource requests and limits for memory and CPU + resources: + requests: + memory: "16Gi" # Minimum memory required + cpu: "2" # Minimum CPU required + limits: + memory: "16Gi" # Max memory allowed + cpu: "2" # Max CPU allowed + + # Kernel-level system control parameters for Oracle + securityContext: + sysctls: + - name: kernel.shmall + value: "2097152" + - name: kernel.sem + value: "250 32000 100 128" + - name: kernel.shmmax + value: "8589934592" + - name: kernel.shmmni + value: "4096" + + # Configuration parameters for Oracle Grid and Database homes + configParams: + gridHome: "/u01/app/19c/grid" # Oracle Grid home path + gridBase: "/u01/app/grid" # Grid base directory + dbHome: "/u01/app/oracle/product/19c/dbhome_1" # Oracle DB home path + dbBase: "/u01/app/oracle" # Oracle base directory + crsAsmDiskDg: "+CRSDATA" # Name of the Diskgroup for Voting Disks + dbDataFileDestDg: "+DBDATA" # Name of the Diskgroup for Database Files + dbRecoveryFileDest: "+RECO" # Name of the Diskgroup for Recovery Files + redoAsmDiskDg: "+REDO" # Name of the Diskgroup for Redo Files + crsAsmDeviceList: /dev/asm-disk1,/dev/asm-disk2 # Comma-separated list of ASM device files for CRS Diskgroup CRSDATA + dbAsmDeviceList: /dev/asm-disk3,/dev/asm-disk4,/dev/asm-disk5,/dev/asm-disk6 # Comma-separated list of ASM device files for DBDATA Diskgroup for database files + recoAsmDeviceList: /dev/asm-disk7,/dev/asm-disk8 # Comma-separated list of ASM device files for RECO Diskgroup for Recovery Area files + redoAsmDeviceList: /dev/asm-disk9,/dev/asm-disk10 # Comma-separated list of ASM device files for REDO Diskgroup for Redo files + crsAsmDiskDgRedundancy: "EXTERNAL" # Specify the Redudancy for the diskgroup with CRS Files + dbAsmDiskDgRedundancy: "NORMAL" # Specify the Redudancy for the diskgroup with Database Files + recoAsmDiskDgRedundancy: "EXTERNAL" # Specify the Redudancy for the diskgroup with Recovery Files + redoAsmDiskDgRedundancy: "EXTERNAL" # Specify the Redudancy for the diskgroup with Redo Files + inventory: "/u01/app/oraInventory" # Inventory location + gridSwZipFile: "grid_home.zip" # Grid infrastructure software ZIP + dbSwZipFile: "db_home.zip" # Database software ZIP + sgaSize: "3G" # Size of the System Global Area + pgaSize: "1G" # Size of the Program Global Area + processes: 2000 # Oracle process limit + cpuCount: 4 # Number of CPUs to allocate + dbName: "PORCLCDB" # Oracle database name + pdbName: "TESTPDB" # PDB name, in case you want to explicitly specify the PDB Name + hostSwStageLocation: /scratch/software/stage # Host location where Oracle software ZIPs are staged, same location will be mounted inside the Oracle Restart Pod diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml index e9926f66..dce27302 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_oneoff_storageclass.yaml @@ -45,8 +45,11 @@ spec: nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" - # Optional: Set this to enable dynamic provisioning of PVCs - storageClass: "oci-bv" + # Custom Storage class to be used for dynamic provisioning (if applicable) + # You have separate parameters to specify the storage class for all the diskgroups (CRS, DATA, RECO, REDO) + # You have a parameter to specify the storage class for the software location for the GI and RDBMS HOME + crsDgStorageClass: "oci-bv" # Specify storage class for the CRS Diskgroup, Currently using "oci-bv" but you can specify another available storage class as well + swDgStorageClass: "oci-bv" # Specify storage class for the storage location for software, Currently using "oci-bv" but you can specify another available storage class as well # ASM (Automatic Storage Management) storage configuration asmStorageDetails: @@ -119,6 +122,7 @@ spec: processes: 2000 # Oracle process limit cpuCount: 4 # Number of CPUs to allocate dbName: "PORCLCDB" # Oracle database name + pdbName: "TESTPDB" # PDB name, in case you want to explicitly specify the PDB Name oPatchSwZipFile: "p6880880_190000_Linux-x86-64.zip" # Opatch software ZIP swStagePvc: pv-stage-vol-claim # PVC name for the Software Location mounting the NFS swStagePvcMountLocation: "/stage" # Mount point for the Software Location inside the Pod @@ -127,4 +131,4 @@ spec: oPatchLocation: /stage/software/19c # Location of the Opatch Software on the NFS based Persistent Volume, Pod will also have this software in the same path oneOffLocation: /stage/software/19c/oneoff # One-off patch files directory where all the one-off patches for GI and RDBMS Home are unzipped gridOneOffIds: "38336965" # Comma-separated Grid one-off patch IDs - dbOneOffIds: "38336965,34436514" # Comma-separated DB one-off patch IDs \ No newline at end of file + dbOneOffIds: "38336965,34436514" # Comma-separated DB one-off patch IDs diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml index 48546cb9..9211c549 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_rupatch_pvc.yaml @@ -45,8 +45,11 @@ spec: nodePort: 30200 # Host port exposed on worker node (must be in NodePort range) enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" - # Optional: Set this to enable dynamic provisioning of PVCs - storageClass: "oci-bv" + # Custom Storage class to be used for dynamic provisioning (if applicable) + # You have separate parameters to specify the storage class for all the diskgroups (CRS, DATA, RECO, REDO) + # You have a parameter to specify the storage class for the software location for the GI and RDBMS HOME + crsDgStorageClass: "oci-bv" # Specify storage class for the CRS Diskgroup, Currently using "oci-bv" but you can specify another available storage class as well + swDgStorageClass: "oci-bv" # Specify storage class for the storage location for software, Currently using "oci-bv" but you can specify another available storage class as well # ASM (Automatic Storage Management) storage configuration asmStorageDetails: @@ -120,6 +123,7 @@ spec: processes: 2000 # Oracle process limit cpuCount: 4 # Number of CPUs to allocate dbName: "PORCLCDB" # Oracle database name + pdbName: "TESTPDB" # PDB name, in case you want to explicitly specify the PDB Name swStagePvc: pv-stage-vol-claim # PVC name for the Software Location mounting the NFS swStagePvcMountLocation: "/stage" # Mount point for the Software Location inside the Pod hostSwStageLocation: /stage/software/19c/19.3.0 # Base Release Software Location on the NFS based Persistent Volume, Pod will also have this software in the same path diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml index 158bea75..9c19754b 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class.yaml @@ -13,12 +13,14 @@ spec: # Instance details for Oracle Restart instDetails: name: dbmc1 # Logical name of the OracleRestart instance - hostSwLocation: /scratch/orestart/ # Host directory for Oracle GI HOME and Oracle RDBMS HOME # Worker nodes where the instance should be scheduled workerNode: - 10.0.10.58 # IP address of the target node for deployment + # Size of the storage location for software. This parameter will be effective when the local SW location is NOT specified by hostSwLocation + swLocStorageSizeInGb: 300 + # Environment variables in case you want to want to ignore prerequisite checks during the GI/RDBSMS Install envVars: - name: IGNORE_CRS_PREREQS # If set to true, it will use flags -ignorePreReq and -ignorePrereqFailure during the CRS Installation. Do not set this parameter in the .yaml file if you do not want to use the parameters -ignorePreReq and -ignorePrereqFailure during the CRS Installation. @@ -44,7 +46,9 @@ spec: enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" # Custom Storage class to be used for dynamic provisioning (if applicable) - storageClass: "oci-bv" + # You have separate parameters to specify the storage class for all the diskgroups (CRS, DATA, RECO, REDO) + crsDgStorageClass: "oci-bv" # Specify storage class for the CRS Diskgroup, Currently using "oci-bv" but you can specify another available storage class as well + swDgStorageClass: "oci-bv" # Specify storage class for the storage location for software, Currently using "oci-bv" but you can specify another available storage class as well # ASM (Automatic Storage Management) storage configuration asmStorageDetails: diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml index ef2c0527..1f59f3cc 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_after_sw_home_resize.yaml @@ -46,15 +46,17 @@ spec: enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" # Custom Storage class to be used for dynamic provisioning (if applicable) - storageClass: "oci-bv" + # You have separate parameters to specify the storage class for all the diskgroups (CRS, DATA, RECO, REDO) + crsDgStorageClass: "oci-bv" # Specify storage class for the CRS Diskgroup, Currently using "oci-bv" but you can specify another available storage class as well + swDgStorageClass: "oci-bv" # Specify storage class for the storage location for software, Currently using "oci-bv" but you can specify another available storage class as well # ASM (Automatic Storage Management) storage configuration asmStorageDetails: disksBySize: - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage diskNames: - - /dev/asm-disk1 # Device for ASM disk 1 - - /dev/asm-disk2 # Device for ASM disk 2 + - /dev/asm-disk1 # ASM disk device path 1 + - /dev/asm-disk2 # ASM disk device path 2 # SSH key secret used for remote access and automation sshKeySecret: diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml index 6ba159ff..1d221bd7 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_storage_class_before_sw_home_resize.yaml @@ -46,15 +46,17 @@ spec: enableOns: "enable" # If you want to have ONS notification configured on the specified port. Default value is "enable" # Custom Storage class to be used for dynamic provisioning (if applicable) - storageClass: "oci-bv" + # You have separate parameters to specify the storage class for all the diskgroups (CRS, DATA, RECO, REDO) + crsDgStorageClass: "oci-bv" # Specify storage class for the CRS Diskgroup, Currently using "oci-bv" but you can specify another available storage class as well + swDgStorageClass: "oci-bv" # Specify storage class for the storage location for software, Currently using "oci-bv" but you can specify another available storage class as well # ASM (Automatic Storage Management) storage configuration asmStorageDetails: disksBySize: - storageSizeInGb: 50 # Each disk in this group has 50Gi of storage diskNames: - - /dev/asm-disk1 # Device for ASM disk 1 - - /dev/asm-disk2 # Device for ASM disk 2 + - /dev/asm-disk1 # ASM disk device path 1 + - /dev/asm-disk2 # ASM disk device path 2 # SSH key secret used for remote access and automation sshKeySecret: diff --git a/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt b/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt index 1d47f9fd..b08bb817 100644 --- a/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt +++ b/docs/oraclerestart/provisioning/orestart_db_rupatch_oneoffs_object.txt @@ -7,12 +7,12 @@ Annotations: OracleRestarts.database.oracle.com/old-spec: API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-09-12T14:19:42Z + Creation Timestamp: 2025-10-09T05:53:39Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 234821692 - UID: a8f02440-e8ed-40d3-9b85-85110f4065f4 + Resource Version: 246360830 + UID: d37b51c4-58ae-42d9-bec4-983ce94ee540 Spec: Asm Storage Details: Disks By Size: @@ -23,15 +23,15 @@ Spec: Config Params: Cpu Count: 4 Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle Db Char Set: AL32UTF8 - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB Db One Off Ids: 38336965,34436514 - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Db Sw Zip File: db_home.zip Grid Base: /u01/app/grid @@ -44,6 +44,7 @@ Spec: O Patch Location: /stage/software/19c O Patch Sw Zip File: p6880880_190000_Linux-x86-64.zip One Off Location: /stage/software/19c/oneoff + Pdb Name: TESTPDB Pga Size: 1G Processes: 2000 Ru Patch Location: /stage/software/19c/19.28/37957391 @@ -51,6 +52,7 @@ Spec: Sw Mount Location: /u01 Sw Stage Pvc: pv-stage-vol-claim Sw Stage Pvc Mount Location: /stage + Crs Dg Storage Class: oci-bv Db Secret: Key File Mount Location: /mnt/.dbsecrets Key File Name: key.pem @@ -107,18 +109,19 @@ Spec: Name: ssh-key-secret Priv Key Secret Name: ssh-privkey Pub Key Secret Name: ssh-pubkey - Storage Class: oci-bv + Sw Dg Storage Class: oci-bv Status: Oracle Restart Nodes: Name: dbmc1-0 Node Details: Instance State: OPEN - Pod State: PODAVAILABLE + Pod State: AVAILABLE Cluster State: HEALTHY Mounted Devices: + /dev/asm-disk1 /dev/asm-disk2 - State: FAILED + State: AVAILABLE Asm Details: Diskgroup: Disks: @@ -126,27 +129,27 @@ Status: Name: DATA Redundancy: EXTERN Conditions: - Last Transition Time: 2025-09-12T15:33:28Z - Message: reconcile completed successfully - Observed Generation: 1 - Reason: LastReconcileCycleCompleted - Status: True - Type: ReconcileComplete - Last Transition Time: 2025-09-12T18:06:27Z + Last Transition Time: 2025-10-09T08:04:31Z Message: reconcile has been queued Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued + Last Transition Time: 2025-10-09T13:21:11Z + Message: reconcile completed successfully + Observed Generation: 1 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete Config Params: Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Grid Base: /u01/app/grid Grid Home: /u01/app/19c/grid @@ -160,7 +163,7 @@ Status: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Db State: OPEN - External Connect String: Pending + External Connect String: 129.146.0.149:30007/soepdb Pdb Connect String: dbmc1-0:1521/ORCLPDB Release Update: 19.28.0.0.0 Role: PRIMARY @@ -170,6 +173,9 @@ Status: State: AVAILABLE Events: + + + -- Patches Details for GI HOME and RDBMS HOME: [grid@dbmc1-0 ~]$ $ORACLE_HOME/OPatch/opatch lspatches 38336965;OCW Interim patch for 38336965 @@ -188,4 +194,4 @@ OPatch succeeded. 38336965;OCW Interim patch for 38336965 37960098;Database Release Update : 19.28.0.0.250715 (37960098) -OPatch succeeded. \ No newline at end of file +OPatch succeeded. diff --git a/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt b/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt index ab94bb8e..31bfa302 100644 --- a/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt +++ b/docs/oraclerestart/provisioning/orestart_rupatch_pvc_object.txt @@ -3,16 +3,16 @@ Name: oraclerestart-sample Namespace: orestart Labels: Annotations: OracleRestarts.database.oracle.com/old-spec: - {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":300,"workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nodeport","svc... + {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":300,"workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","value":"tru... API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-08-21T22:09:44Z + Creation Timestamp: 2025-10-09T13:31:37Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 225580366 - UID: 3ce94373-a848-4b0c-8191-55783f146b8a + Resource Version: 246434925 + UID: 0a283621-f64c-4d34-ad46-92e02376d7ad Spec: Asm Storage Details: Disks By Size: @@ -23,14 +23,14 @@ Spec: Config Params: Cpu Count: 4 Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle Db Char Set: AL32UTF8 - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Db Sw Zip File: db_home.zip Grid Base: /u01/app/grid @@ -41,6 +41,7 @@ Spec: Inventory: /u01/app/oraInventory O Patch Location: /stage/software/19c O Patch Sw Zip File: p6880880_190000_Linux-x86-64.zip + Pdb Name: TESTPDB Pga Size: 1G Processes: 2000 Ru Patch Location: /stage/software/19c/19.28/37957391 @@ -48,27 +49,39 @@ Spec: Sw Mount Location: /u01 Sw Stage Pvc: pv-stage-vol-claim Sw Stage Pvc Mount Location: /stage + Crs Dg Storage Class: oci-bv Db Secret: Key File Mount Location: /mnt/.dbsecrets Key File Name: key.pem Name: db-user-pass-pkutl Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc + Enable Ons: enable Image: localhost/oracle/database-orestart:19.3.0-slim - Image Pull Policy: Always + Image Pull Policy: IfNotPresent Inst Details: - Name: dbmc1 - Node Port Svc: - Name: dbmc1-service-nodeport - Svc Type: nodeport - Port Mappings: - Node Port: 30007 - Port: 1521 - Protocol: TCP - Target Port: 1521 + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Name: dbmc1 Sw Loc Storage Size In Gb: 300 Worker Node: 10.0.10.58 + Lb Service: + Node Port Svc: + Name: dbmc1-service-nodeport + Port Mappings: + Node Port: 30007 + Port: 1521 + Protocol: TCP + Target Port: 1521 + Node Port: 30200 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: nodeport Resources: Limits: Cpu: 2 @@ -93,7 +106,7 @@ Spec: Name: ssh-key-secret Priv Key Secret Name: ssh-privkey Pub Key Secret Name: ssh-pubkey - Storage Class: oci-bv + Sw Dg Storage Class: oci-bv Status: Oracle Restart Nodes: Name: dbmc1-0 @@ -112,27 +125,27 @@ Status: Name: DATA Redundancy: EXTERN Conditions: - Last Transition Time: 2025-08-22T01:09:05Z - Message: oracle restart database is in a restricted state: PROVISIONING + Last Transition Time: 2025-10-09T15:39:34Z + Message: reconcile has been queued Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued - Last Transition Time: 2025-08-22T02:12:50Z - Message: no reconcile errors + Last Transition Time: 2025-10-09T17:29:10Z + Message: reconcile completed successfully Observed Generation: 1 Reason: LastReconcileCycleCompleted Status: True Type: ReconcileComplete Config Params: Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Grid Base: /u01/app/grid Grid Home: /u01/app/19c/grid @@ -147,11 +160,9 @@ Status: Pwd File Name: pwdfile.enc Db State: OPEN External Connect String: 129.146.0.149:30007/soepdb - Inst Details: - Name: - Pdb Connect String: dbmc1-0:1521/ORCLPDB - Release Update: 19.28.0.0.0 - Role: PRIMARY + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY Service Details: Name: soepdb Svc State: service soepdb is running @@ -159,40 +170,40 @@ Status: Events: - $ kubectl get pvc -n orestart -NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE -asm-pvc-disk-0-oraclerestart-sample Bound csi-ce9f7e26-d889-40bc-97c0-1e16bc63d949 50Gi RWO oci-bv 4h3m -asm-pvc-disk-1-oraclerestart-sample Bound csi-35c89048-7aba-460e-ad19-a0c1356f75a3 50Gi RWO oci-bv 4h3m -dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-62f17e8a-1d0b-43a8-9d82-9ae81648ca07 300Gi RWO oci-bv 4h3m -pv-stage-vol-claim Bound pv-stage-vol1 500Gi ROX fss-dyn-storage 8h +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-crsdg-asm-disk1-oraclerestart-sample-dbmc1-0 Bound csi-dd26294f-caa3-4c1c-b5f4-a64145afe926 50Gi RWO oci-bv 3h57m +asm-pvc-crsdg-asm-disk2-oraclerestart-sample-dbmc1-0 Bound csi-972a9dc2-f49b-4475-8add-c185f4cfe05f 50Gi RWO oci-bv 3h57m +dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-6722f64f-71a2-458d-857f-921aee569b43 300Gi RWO oci-bv 3h57m +pv-stage-vol-claim Bound pv-stage-vol1 500Gi ROX fss-dyn-storage 11h [root@dbmc1-0 rac-work-dir]# ls -lrt /stage/software/19c/ -total 71681 -drwxr-xr-x. 3 root root 3 Aug 19 01:20 19.28 --rw-r--r--. 1 root root 72539776 Aug 19 01:21 p6880880_190000_Linux-x86-64.zip -drwxr-xr-x. 2 root root 3 Aug 19 03:56 19.3.0 -[root@dbmc1-0 rac-work-dir]# +total 71682 +-rwxrwxrwx. 1 2001 2001 72539776 Aug 19 01:21 p6880880_190000_Linux-x86-64.zip +drwxrwxrwx. 2 2001 2001 3 Aug 19 03:56 19.3.0 +drwxrwxrwx. 3 2001 2001 3 Sep 11 04:36 19.28 [root@dbmc1-0 rac-work-dir]# ls -lrt /stage/software/19c/19.3.0/ total 6774784 --rw-r--r--. 1 root root 3059705302 Aug 19 01:10 db_home.zip --rw-r--r--. 1 root root 2889184573 Aug 19 01:11 grid_home.zip +-rwxrwxrwx. 1 2001 2001 3059705302 Aug 19 01:10 db_home.zip +-rwxrwxrwx. 1 2001 2001 2889184573 Aug 19 01:11 grid_home.zip +-rwxrwxrwx. 1 2001 2001 987216967 Aug 19 01:13 LINUX.X64_193000_client_home.zip +[root@dbmc1-0 rac-work-dir]# [root@dbmc1-0 rac-work-dir]# ls -lrt /stage/software/19c/19.28/ -total 3851265 -drwxr-xr-x. 8 root root 9 May 28 05:03 37957391 --rw-rw-r--. 1 root root 2921864 May 28 05:41 PatchSearch.xml --rw-r--r--. 1 root root 3940126990 Aug 19 01:14 p37957391_190000_Linux-x86-64.zip +total 3847169 +drwxrwxrwx. 8 2001 2001 9 Jul 15 14:13 37957391 +-rwxrwxrwx. 1 2001 2001 2930311 Jul 15 20:49 PatchSearch.xml +-rwxrwxrwx. 1 2001 2001 3934314158 Sep 11 04:31 p37957391_190000_Linux-x86-64.zip [root@dbmc1-0 rac-work-dir]# ls -lrt /stage/software/19c/19.28/37957391/ total 171 -drwxr-xr-x. 5 root root 4 May 28 05:03 37962946 -drwxr-xr-x. 5 root root 4 May 28 05:04 37962938 -drwxr-xr-x. 5 root root 5 May 28 05:04 37960098 -drwxr-xr-x. 4 root root 3 May 28 05:04 37954209 -drwxr-xr-x. 4 root root 3 May 28 05:04 36758186 -drwxr-xr-x. 2 root root 13 May 28 05:08 automation --rwxr-xr-x. 1 root root 0 May 28 05:08 README.txt --rwxr-xr-x. 1 root root 152522 May 28 05:18 README.html --rwxr-xr-x. 1 root root 5824 May 28 05:35 bundle.xml +drwxrwxrwx. 5 2001 2001 4 Jul 15 14:13 37962946 +drwxrwxrwx. 5 2001 2001 4 Jul 15 14:14 37962938 +drwxrwxrwx. 4 2001 2001 3 Jul 15 14:14 38124772 +drwxrwxrwx. 5 2001 2001 5 Jul 15 14:14 37960098 +drwxrwxrwx. 4 2001 2001 3 Jul 15 14:14 36758186 +drwxrwxrwx. 2 2001 2001 13 Jul 15 14:19 automation +-rwxrwxrwx. 1 2001 2001 0 Jul 15 14:19 README.txt +-rwxrwxrwx. 1 2001 2001 152522 Jul 15 14:29 README.html +-rwxrwxrwx. 1 2001 2001 5824 Jul 15 14:47 bundle.xml [root@dbmc1-0 rac-work-dir]# \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_storage_class_object.txt b/docs/oraclerestart/provisioning/orestart_storage_class_object.txt index 596dd480..27fdf146 100644 --- a/docs/oraclerestart/provisioning/orestart_storage_class_object.txt +++ b/docs/oraclerestart/provisioning/orestart_storage_class_object.txt @@ -3,16 +3,16 @@ Name: oraclerestart-sample Namespace: orestart Labels: Annotations: OracleRestarts.database.oracle.com/old-spec: - {"instDetails":{"name":"dbmc1","hostSwLocation":"/scratch/orestart/","workerNode":["10.0.10.58"],"nodePortSvc":[{"name":"dbmc1-service-nod... + {"instDetails":{"name":"dbmc1","swLocStorageSizeInGb":300,"workerNode":["10.0.10.58"],"envVars":[{"name":"IGNORE_CRS_PREREQS","value":"tru... API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-08-21T00:19:11Z + Creation Timestamp: 2025-10-09T18:01:21Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 225180605 - UID: ad9bc196-6448-47e7-9616-ad3144f393ca + Resource Version: 246453216 + UID: c42b7961-6cdb-416e-a17c-616cc0c08b1f Spec: Asm Storage Details: Disks By Size: @@ -23,14 +23,14 @@ Spec: Config Params: Cpu Count: 4 Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle Db Char Set: AL32UTF8 - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Db Sw Zip File: db_home.zip Grid Base: /u01/app/grid @@ -43,27 +43,39 @@ Spec: Processes: 2000 Sga Size: 3G Sw Mount Location: /u01 + Crs Dg Storage Class: oci-bv Db Secret: Key File Mount Location: /mnt/.dbsecrets Key File Name: key.pem Name: db-user-pass-pkutl Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc + Enable Ons: enable Image: localhost/oracle/database-orestart:19.3.0-slim - Image Pull Policy: Always + Image Pull Policy: IfNotPresent Inst Details: - Host Sw Location: /scratch/orestart/ - Name: dbmc1 - Node Port Svc: - Name: dbmc1-service-nodeport - Svc Type: nodeport + Env Vars: + Name: IGNORE_CRS_PREREQS + Value: true + Name: IGNORE_DB_PREREQS + Value: true + Name: dbmc1 + Sw Loc Storage Size In Gb: 300 + Worker Node: + 10.0.10.58 + Lb Service: + Node Port Svc: + Name: dbmc1-service-nodeport Port Mappings: Node Port: 30007 Port: 1521 Protocol: TCP Target Port: 1521 - Worker Node: - 10.0.10.58 + Node Port: 30200 + Port: 6200 + Protocol: TCP + Target Port: 6200 + Svc Type: nodeport Resources: Limits: Cpu: 2 @@ -88,7 +100,7 @@ Spec: Name: ssh-key-secret Priv Key Secret Name: ssh-privkey Pub Key Secret Name: ssh-pubkey - Storage Class: oci-bv + Sw Dg Storage Class: oci-bv Status: Oracle Restart Nodes: Name: dbmc1-0 @@ -107,27 +119,27 @@ Status: Name: DATA Redundancy: EXTERN Conditions: - Last Transition Time: 2025-08-21T00:32:23Z - Message: oracle restart database is in a restricted state: PROVISIONING + Last Transition Time: 2025-10-09T18:10:31Z + Message: reconcile has been queued Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued - Last Transition Time: 2025-08-21T04:04:12Z - Message: no reconcile errors + Last Transition Time: 2025-10-09T18:28:17Z + Message: reconcile completed successfully Observed Generation: 1 Reason: LastReconcileCycleCompleted Status: True Type: ReconcileComplete Config Params: Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Grid Base: /u01/app/grid Grid Home: /u01/app/19c/grid @@ -142,11 +154,9 @@ Status: Pwd File Name: pwdfile.enc Db State: OPEN External Connect String: 129.146.0.149:30007/soepdb - Inst Details: - Name: - Pdb Connect String: dbmc1-0:1521/ORCLPDB - Release Update: 19.28.0.0.0 - Role: PRIMARY + Pdb Connect String: dbmc1-0:1521/ORCLPDB + Release Update: 19.28.0.0.0 + Role: PRIMARY Service Details: Name: soepdb Svc State: service soepdb is running @@ -154,12 +164,17 @@ Status: Events: + $ kubectl get pvc -n orestart -NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE -asm-pvc-disk-0-oraclerestart-sample Bound csi-a2bcf917-dffb-46aa-b0b9-7fbb2b9499b7 50Gi RWO oci-bv 3h45m -asm-pvc-disk-1-oraclerestart-sample Bound csi-e5abda8e-abde-4399-9e97-d72cc3bc3659 50Gi RWO oci-bv 3h45m +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-crsdg-asm-disk1-oraclerestart-sample-dbmc1-0 Bound csi-4c9b2faa-9cd5-45a5-9ca1-dd77e708f03f 50Gi RWO oci-bv 27m +asm-pvc-crsdg-asm-disk2-oraclerestart-sample-dbmc1-0 Bound csi-ed7630fc-4f79-426b-a877-ab70ca6ebb87 50Gi RWO oci-bv 27m +dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-15fbddd5-3994-4e3e-b072-c760eaa76188 300Gi RWO oci-bv 27m +pv-stage-vol-claim Bound pv-stage-vol1 500Gi ROX fss-dyn-storage 12h + -$ kubectl get pv -n orestart -NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE -csi-a2bcf917-dffb-46aa-b0b9-7fbb2b9499b7 50Gi RWO Delete Bound orestart/asm-pvc-disk-0-oraclerestart-sample oci-bv 3h45m -csi-e5abda8e-abde-4399-9e97-d72cc3bc3659 50Gi RWO Delete Bound orestart/asm-pvc-disk-1-oraclerestart-sample oci-bv 3h45m \ No newline at end of file +$ kubectl get pv +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE +csi-15fbddd5-3994-4e3e-b072-c760eaa76188 300Gi RWO Delete Bound orestart/dbmc1-oradata-sw-vol-pvc-dbmc1-0 oci-bv 27m +csi-4c9b2faa-9cd5-45a5-9ca1-dd77e708f03f 50Gi RWO Delete Bound orestart/asm-pvc-crsdg-asm-disk1-oraclerestart-sample-dbmc1-0 oci-bv 27m +csi-ed7630fc-4f79-426b-a877-ab70ca6ebb87 50Gi RWO Delete Bound orestart/asm-pvc-crsdg-asm-disk2-oraclerestart-sample-dbmc1-0 oci-bv 27m \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/orestart_storage_class_object_after_sw_home_resize.txt b/docs/oraclerestart/provisioning/orestart_storage_class_object_after_sw_home_resize.txt index 2f8e35b2..1ddb0a47 100644 --- a/docs/oraclerestart/provisioning/orestart_storage_class_object_after_sw_home_resize.txt +++ b/docs/oraclerestart/provisioning/orestart_storage_class_object_after_sw_home_resize.txt @@ -7,12 +7,12 @@ Annotations: OracleRestarts.database.oracle.com/old-spec: API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-09-10T23:21:18Z + Creation Timestamp: 2025-10-09T18:01:21Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 2 - Resource Version: 234059474 - UID: 0525c7ac-e02f-4471-8e96-40cd5d4645ff + Resource Version: 246493782 + UID: c42b7961-6cdb-416e-a17c-616cc0c08b1f Spec: Asm Storage Details: Disks By Size: @@ -23,14 +23,14 @@ Spec: Config Params: Cpu Count: 4 Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle Db Char Set: AL32UTF8 - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Db Sw Zip File: db_home.zip Grid Base: /u01/app/grid @@ -43,6 +43,7 @@ Spec: Processes: 2000 Sga Size: 3G Sw Mount Location: /u01 + Crs Dg Storage Class: oci-bv Db Secret: Key File Mount Location: /mnt/.dbsecrets Key File Name: key.pem @@ -99,7 +100,7 @@ Spec: Name: ssh-key-secret Priv Key Secret Name: ssh-privkey Pub Key Secret Name: ssh-pubkey - Storage Class: oci-bv + Sw Dg Storage Class: oci-bv Status: Oracle Restart Nodes: Name: dbmc1-0 @@ -118,13 +119,13 @@ Status: Name: DATA Redundancy: EXTERN Conditions: - Last Transition Time: 2025-09-10T23:34:06Z + Last Transition Time: 2025-10-09T18:10:31Z Message: reconcile has been queued Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued - Last Transition Time: 2025-09-10T23:49:53Z + Last Transition Time: 2025-10-09T20:44:39Z Message: reconcile completed successfully Observed Generation: 2 Reason: LastReconcileCycleCompleted @@ -132,13 +133,13 @@ Status: Type: ReconcileComplete Config Params: Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Grid Base: /u01/app/grid Grid Home: /u01/app/19c/grid @@ -152,7 +153,7 @@ Status: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Db State: OPEN - External Connect String: Pending + External Connect String: 129.146.0.149:30007/soepdb Pdb Connect String: dbmc1-0:1521/ORCLPDB Release Update: 19.28.0.0.0 Role: PRIMARY @@ -163,30 +164,33 @@ Status: Events: + $ kubectl get pvc -n orestart -NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE -asm-pvc-disk-0-oraclerestart-sample Bound csi-851752b8-35e7-4117-a054-0fde0cd78fb6 50Gi RWO oci-bv 29m -asm-pvc-disk-1-oraclerestart-sample Bound csi-51633544-2e93-4067-a93d-4ba66da7117e 50Gi RWO oci-bv 29m -dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-8b07994d-166f-459d-ac4f-17a4fe52d69b 400Gi RWO oci-bv 29m +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-crsdg-asm-disk1-oraclerestart-sample-dbmc1-0 Bound csi-4c9b2faa-9cd5-45a5-9ca1-dd77e708f03f 50Gi RWO oci-bv 164m +asm-pvc-crsdg-asm-disk2-oraclerestart-sample-dbmc1-0 Bound csi-ed7630fc-4f79-426b-a877-ab70ca6ebb87 50Gi RWO oci-bv 164m +dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-15fbddd5-3994-4e3e-b072-c760eaa76188 400Gi RWO oci-bv 164m +pv-stage-vol-claim Bound pv-stage-vol1 500Gi ROX fss-dyn-storage 14h + [root@dbmc1-0 rac-work-dir]# df -h Filesystem Size Used Avail Use% Mounted on -overlay 489G 25G 465G 6% / +overlay 489G 37G 452G 8% / tmpfs 64M 0 64M 0% /dev tmpfs 46G 0 46G 0% /sys/fs/cgroup -tmpfs 45G 4.1G 41G 9% /etc/hostname +tmpfs 45G 3.9G 42G 9% /etc/hostname tmpfs 46G 112K 46G 1% /run -/dev/sdn 393G 14G 380G 4% /u01 +/dev/sdo 393G 14G 380G 4% /u01 tmpfs 83G 8.0K 83G 1% /mnt/.ssh tmpfs 83G 8.0K 83G 1% /mnt/.dbsecrets tmpfs 83G 1.1G 82G 2% /dev/shm -/dev/mapper/ocivolume-root 489G 25G 465G 6% /etc/hosts -/dev/sdb1 196G 31G 156G 17% /scratch/software/stage +/dev/mapper/ocivolume-root 489G 37G 452G 8% /etc/hosts +/dev/sdb1 196G 55G 132G 30% /scratch/software/stage tmpfs 46G 0 46G 0% /run/lock -tmpfs 46G 12M 46G 1% /tmp -tmpfs 46G 32M 46G 1% /var/log/journal +tmpfs 46G 75M 46G 1% /tmp +tmpfs 46G 64M 46G 1% /var/log/journal tmpfs 46G 0 46G 0% /proc/acpi tmpfs 46G 0 46G 0% /proc/scsi tmpfs 46G 0 46G 0% /sys/firmware diff --git a/docs/oraclerestart/provisioning/orestart_storage_class_object_before_sw_home_resize.txt b/docs/oraclerestart/provisioning/orestart_storage_class_object_before_sw_home_resize.txt index b2cb5a3e..d7dab6c9 100644 --- a/docs/oraclerestart/provisioning/orestart_storage_class_object_before_sw_home_resize.txt +++ b/docs/oraclerestart/provisioning/orestart_storage_class_object_before_sw_home_resize.txt @@ -7,12 +7,12 @@ Annotations: OracleRestarts.database.oracle.com/old-spec: API Version: database.oracle.com/v4 Kind: OracleRestart Metadata: - Creation Timestamp: 2025-09-10T23:21:18Z + Creation Timestamp: 2025-10-09T18:01:21Z Finalizers: database.oracle.com/oraclerestartfinalizer Generation: 1 - Resource Version: 234057060 - UID: 0525c7ac-e02f-4471-8e96-40cd5d4645ff + Resource Version: 246491127 + UID: c42b7961-6cdb-416e-a17c-616cc0c08b1f Spec: Asm Storage Details: Disks By Size: @@ -23,14 +23,14 @@ Spec: Config Params: Cpu Count: 4 Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle Db Char Set: AL32UTF8 - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Db Sw Zip File: db_home.zip Grid Base: /u01/app/grid @@ -43,6 +43,7 @@ Spec: Processes: 2000 Sga Size: 3G Sw Mount Location: /u01 + Crs Dg Storage Class: oci-bv Db Secret: Key File Mount Location: /mnt/.dbsecrets Key File Name: key.pem @@ -99,7 +100,7 @@ Spec: Name: ssh-key-secret Priv Key Secret Name: ssh-privkey Pub Key Secret Name: ssh-pubkey - Storage Class: oci-bv + Sw Dg Storage Class: oci-bv Status: Oracle Restart Nodes: Name: dbmc1-0 @@ -118,13 +119,13 @@ Status: Name: DATA Redundancy: EXTERN Conditions: - Last Transition Time: 2025-09-10T23:34:06Z + Last Transition Time: 2025-10-09T18:10:31Z Message: reconcile has been queued Observed Generation: 1 Reason: LastReconcileCycleQueued Status: True Type: ReconcileQueued - Last Transition Time: 2025-09-10T23:41:51Z + Last Transition Time: 2025-10-09T20:35:50Z Message: reconcile completed successfully Observed Generation: 1 Reason: LastReconcileCycleCompleted @@ -132,13 +133,13 @@ Status: Type: ReconcileComplete Config Params: Crs Asm Device List: /dev/asm-disk1,/dev/asm-disk2 - Crs Asm Disk Dg: +DATA + Crs Asm Disk Dg: DATA Crs Asm Disk Dg Redundancy: external Db Base: /u01/app/oracle - Db Data File Dest Dg: +DATA + Db Data File Dest Dg: DATA Db Home: /u01/app/oracle/product/19c/dbhome_1 Db Name: PORCLCDB - Db Recovery File Dest: +DATA + Db Recovery File Dest: DATA Db Response File: Grid Base: /u01/app/grid Grid Home: /u01/app/19c/grid @@ -152,7 +153,7 @@ Status: Pwd File Mount Location: /mnt/.dbsecrets Pwd File Name: pwdfile.enc Db State: OPEN - External Connect String: Pending + External Connect String: 129.146.0.149:30007/soepdb Pdb Connect String: dbmc1-0:1521/ORCLPDB Release Update: 19.28.0.0.0 Role: PRIMARY @@ -164,31 +165,32 @@ Events: $ kubectl get pvc -n orestart -NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE -asm-pvc-disk-0-oraclerestart-sample Bound csi-851752b8-35e7-4117-a054-0fde0cd78fb6 50Gi RWO oci-bv 23m -asm-pvc-disk-1-oraclerestart-sample Bound csi-51633544-2e93-4067-a93d-4ba66da7117e 50Gi RWO oci-bv 23m -dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-8b07994d-166f-459d-ac4f-17a4fe52d69b 300Gi RWO oci-bv 23m +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +asm-pvc-crsdg-asm-disk1-oraclerestart-sample-dbmc1-0 Bound csi-4c9b2faa-9cd5-45a5-9ca1-dd77e708f03f 50Gi RWO oci-bv 154m +asm-pvc-crsdg-asm-disk2-oraclerestart-sample-dbmc1-0 Bound csi-ed7630fc-4f79-426b-a877-ab70ca6ebb87 50Gi RWO oci-bv 154m +dbmc1-oradata-sw-vol-pvc-dbmc1-0 Bound csi-15fbddd5-3994-4e3e-b072-c760eaa76188 300Gi RWO oci-bv 154m [root@dbmc1-0 rac-work-dir]# df -h Filesystem Size Used Avail Use% Mounted on -overlay 489G 25G 465G 5% / +overlay 489G 37G 452G 8% / tmpfs 64M 0 64M 0% /dev tmpfs 46G 0 46G 0% /sys/fs/cgroup -tmpfs 45G 4.1G 41G 9% /etc/hostname -tmpfs 46G 96K 46G 1% /run -/dev/sdn 295G 14G 281G 5% /u01 +tmpfs 45G 3.9G 42G 9% /etc/hostname +tmpfs 46G 112K 46G 1% /run +/dev/sdo 295G 14G 281G 5% /u01 tmpfs 83G 8.0K 83G 1% /mnt/.ssh tmpfs 83G 8.0K 83G 1% /mnt/.dbsecrets tmpfs 83G 1.1G 82G 2% /dev/shm -/dev/mapper/ocivolume-root 489G 25G 465G 5% /etc/hosts -/dev/sdb1 196G 31G 156G 17% /scratch/software/stage +/dev/mapper/ocivolume-root 489G 37G 452G 8% /etc/hosts +/dev/sdb1 196G 55G 132G 30% /scratch/software/stage tmpfs 46G 0 46G 0% /run/lock -tmpfs 46G 8.2M 46G 1% /tmp -tmpfs 46G 24M 46G 1% /var/log/journal +tmpfs 46G 71M 46G 1% /tmp +tmpfs 46G 64M 46G 1% /var/log/journal tmpfs 46G 0 46G 0% /proc/acpi tmpfs 46G 0 46G 0% /proc/scsi tmpfs 46G 0 46G 0% /sys/firmware -tmpfs 46G 0 46G 0% /sys/fs/selinux \ No newline at end of file +tmpfs 46G 0 46G 0% /sys/fs/selinux +[root@dbmc1-0 rac-work-dir]# \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md index a99ce094..3aa64552 100644 --- a/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/prerequisites_oracle_restart_db.md @@ -5,6 +5,7 @@ * [Preparing to Install Oracle Restart on OKE](#preparing-to-install-oracle-restart-on-oke) * [Worker Node Preparation for Oracle Restart on OKE](#worker-node-preparation-for-oracle-restart-on-oke) + [Download Oracle Grid Infrastructure and Oracle Database Software](#download-oracle-grid-infrastructure-and-oracle-database-software) + + [Permission on the software files](#permission-on-the-software-files) + [Prepare the Worker Node for Oracle Restart Deployment](#prepare-the-worker-node-for-oracle-restart-deployment) + [Set up SELinux Module on Worker Nodes](#set-up-selinux-module-on-worker-nodes) * [Create a namespace for the Oracle Restart Setup](#create-a-namespace-for-the-oracle-restart-setup) @@ -102,6 +103,16 @@ If you are using an Oracle Kubernetes Engine (OKE) Kubernetes Cluster, you will - Oracle Grid Infrastructure 19c (19.28) for Linux x86-64 - Oracle Database 19c (19.28) for Linux x86-64 + ### Permission on the software files + + Depending on the wheter you are provisioning the Oracle Restart Database using Base Release sofware or you are applying an RU patch or any one-off patch, please set the below permissions on the software files in the staging location: + + - Set the permission on the GRID Infrastructure Software and RDBMS Software .zip files to be 755. + - Set the permission on the Opatch .zip file to be 755 + - Set the permission on the unzipped RU software directory to be 755 recursively. + - Set the permission on the unzipped oneoff patch software directory to be 755 recursively. + + ### Worker Node Preparation Checklist Preparing the worker node is a critical foundation for a secure and successful Oracle Restart Database deployment in a Kubernetes environmen. These steps need to be executed by Kuberernetes administrator as root user on worker nodes and follow these steps: @@ -232,6 +243,22 @@ Before using Oracle Restart Database Controller, ensure you have completed the p Refer to the section [Prerequisites](../../../README.md#prerequisites) +Apart from the default Role Bindings for access management mentioned in above section, you will require the below mentioned role bindings: + + For exposing the database using Nodeport services, apply [RBAC](../../../rbac/node-rbac.yaml) + ```sh + kubectl apply -f rbac/node-rbac.yaml + ``` + For automatic storage expansion of block volumes, apply [RBAC](../../../rbac/storage-class-rbac.yaml) + ```sh + kubectl apply -f rbac/storage-class-rbac.yaml + ``` + For getting get, list and watch privileges on the persistent volumes, apply [RBAC](../../rbac/persistent-volume-rbac.yaml) + ```sh + kubectl apply -f rbac/persistent-volume-rbac.yaml + ``` + + ## Additional requirements for OpenShift Security Context Constraints When you deploy Oracle Restart Database using the Oracle Restart Database Controller on an OpenShift cluster, you must account for OpenShift's stricter security model, especially around Security Context Constraints (SCCs). diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md index f3290e57..bc9e29da 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md @@ -17,7 +17,7 @@ * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps: Deploy Oracle Restart Database * Use the file: [oraclerestart_prov.yaml](./oraclerestart_prov.yaml) for this use case as below: diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md index c443a259..dc44f7aa 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md @@ -19,7 +19,8 @@ * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. + ### Steps: Provision Oracle Restart Database * Use the file: [oraclerestart_prov_loadbalancer.yaml](./oraclerestart_prov_loadbalancer.yaml) for this use case as below: * When you are building the image yourself, update the image value in the `oraclerestart_prov_loadbalancer.yaml` file to point to the container image you have built. diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md index 25dbae89..78e427f9 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_nodeport.md @@ -19,7 +19,7 @@ * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps: Deploy Oracle Restart Database * Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for this use case as below: diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md index efcd10c9..09393ec1 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch.md @@ -14,7 +14,6 @@ * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. * Directory where the **Release Update (RU) patch** has been unzipped is specified by `ruPatchLocation`. * For Example: To apply the 19.28 RU Patch `37957391`, if you have unzipped the RU Patch .zip file `p37957391_190000_Linux-x86-64.zip` to location `/scratch/software/19c/19.28/` on the worker node, then set this parameter `ruPatchLocation` to value `/scratch/software/ru_patch/37957391`. - * Set the permission on the unzipped RU software directory to be `755` recursively. ### In this Example: * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `dbocir/oracle/database-orestart:19.3.0-slim`. @@ -22,7 +21,7 @@ * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps: Deploy Oracle Restart Database * Use the file: [oraclerestart_prov_rupatch.yaml](./oraclerestart_prov_rupatch.yaml) for this use case as below: diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md index 9e2aa071..86edbff1 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md @@ -7,14 +7,13 @@ * Oracle Restart node hostname * Node Port 30007 mapped to port 1521 for Database Listener. * Persistent volumes created automatically based on specified disks for Oracle ASM storage. - * Software location and Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS). + * Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS). * Namespace: `orestart` * Mount point for the Software Stage Location inside the Pod is specified by `swStagePvcMountLocation`. * Staged Software location inside the Pod is specified by `hostSwStageLocation`. It is assumed that the Grid Infrastructure and RDBMS Binaries are already available in this path on the Persistent Volume used. - * The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `storageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. + * The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `swDgStorageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. * Directory where the **Release Update (RU) patch** has been unzipped is specified by `ruPatchLocation`. * For Example: To apply the 19.28 RU Patch `37957391`, if you have unzipped the RU Patch .zip file `p37957391_190000_Linux-x86-64.zip` to location `/scratch/software/19c/19.28/` on the worker node, then set this parameter `ruPatchLocation` to value `/scratch/software/ru_patch/37957391`. - * Set the permission on the unzipped RU software directory to be `755` recursively. * Directory, where latest opatch zip file is available, is specified by `oPatchLocation`. * Directory, where unzipped One-off patch files are available, is specified by `oneOffLocation`. * Specify Comma-separated one-off patch IDs to be applied to the GI HOME using `gridOneOffIds`. For Example: `gridOneOffIds: "38336965,34436514"` @@ -22,8 +21,10 @@ ### In this example, * We are using Oracle Restart Database slim image by building it from Git location(./https://orahub.oci.oraclecorp.com/rac-docker-dev/rac-docker-images/-/blob/master/OracleRealApplicationClusters/README.md#building-oracle-rac-database-container-slim-image) i.e. `dbocir/oracle/database-rac:19.3.0-slim`. To use this in your in own environment, update the image value in the `oraclerestart_prov_rupatch_oneoff_storageclass.yaml` file to point to your own container registry base container image. - * The disks procured on the containers using customer storage class for the Oracle Restart storage are `/dev/asm-disk1` and `/dev/asm-disk2`. - * Specify the size of these devices along with names using the parameter `disksBySize`. Size is by-default in GBs. + * The disks provisioned using customer storage class (specified by `crsDgStorageClass`) for the Oracle Restart storage are `/dev/asm-disk1` and `/dev/asm-disk2`. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps: Deploy Oracle Restart Database * Use the file: [oraclerestart_prov_rupatch_oneoff_storageclass.yaml](./oraclerestart_prov_rupatch_oneoff_storageclass.yaml) for this use case as below: @@ -43,4 +44,4 @@ ORACLE DATABASE IS READY TO USE =============================== ``` -* Check Details of Kubernetes CRD Object as in this [example](./orestart_db_rupatch_oneoffs_object.txt) \ No newline at end of file +* Check Details of Kubernetes CRD Object as in this [example](./orestart_db_rupatch_oneoffs_object.txt) diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md index 1c0b9edc..0cc3800d 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups.md @@ -13,11 +13,12 @@ ### In this Example: * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `dbocir/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_multiple_diskgroups.yaml` file to point to the container image you have built. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` to`/dev/disk/by-partlabel/asm-disk6`. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` to`/dev/disk/by-partlabel/asm-disk8`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. * The Diskgroup for CRS files is specified by `crsAsmDiskDg` and the disks on the worker nodes for this diskgroup are specified by `crsAsmDeviceList`. * The Diskgroup for Database files is specified by `dbDataFileDestDg` and the disks on the worker nodes for this diskgroup are specified by `dbAsmDeviceList`. * The Diskgroup for Recovery Area files is specified by `dbRecoveryFileDest` and the disks on the worker nodes for this diskgroup are specified by `recoAsmDeviceList`. + * The Diskgroup for Redo Log files is specified by `redoAsmDiskDg` and the disks on the worker nodes for this diskgroup are specified by `redoAsmDeviceList`. ### Steps: Deploy Oracle Restart Database * Use the file: [oraclerestart_prov_multiple_diskgroups.yaml](./oraclerestart_prov_multiple_diskgroups.yaml) for this use case as below: diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md index 5b6b6e32..ecd09b16 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md @@ -1,6 +1,6 @@ # Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy # In this Usecase: -The Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files and Recovery Area Files. Different Disk Groups have different redundancy levels. +The Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files, Recovery Area Files and Redo Log Files. Different Disk Groups have different redundancy levels. This example uses `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: * Oracle Restart Pod @@ -15,14 +15,16 @@ This example uses `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` ### In this example, * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml` file to point to the container image you have built. - * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` to`/dev/disk/by-partlabel/asm-disk6`. + * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` to`/dev/disk/by-partlabel/asm-disk10`. * Specify the size of disk devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. * The Diskgroup for CRS files is specified by `crsAsmDiskDg` and the disks on the worker nodes for this diskgroup are specified by `crsAsmDeviceList`. * The Diskgroup for Database files is specified by `dbDataFileDestDg` and the disks on the worker nodes for this diskgroup are specified by `dbAsmDeviceList`. * The Diskgroup for Recovery Area files is specified by `dbRecoveryFileDest` and the disks on the worker nodes for this diskgroup are specified by `recoAsmDeviceList`. + * The Diskgroup for Redo Log files is specified by `redoAsmDiskDg` and the disks on the worker nodes for this diskgroup are specified by `redoAsmDeviceList`. * Redundancy level for the diskgroup with CRS files is mentioned by `crsAsmDiskDgRedundancy`. * Redundancy level for the diskgroup with Database files is mentioned by `dbAsmDiskDgRedundancy`. * Redundancy level for the diskgroup with Recovery files is mentioned by `recoAsmDiskDgRedudancy`. + * Redundancy level for the diskgroup with Redo Log files is mentioned by `redoAsmDiskDgRedundancy`. ### Steps: Deploy Oracle Restart Database * Use the file: [oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml](./oraclerestart_prov_multiple_diskgroups_with_redundancy.yaml) for this use case as below: diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md index c290530b..d964eb66 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_rupatch_pvc.md @@ -7,12 +7,12 @@ * Headless services for Oracle Restart * Oracle Restart Node hostname * Node Port 30007 mapped to port 1521 for Database Listener - * Persistent volumes for ASM Disks created automatically using the Storage Class (specified using `storageClass`) for Oracle ASM storage. - * Software location and Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS) + * Persistent volumes created automatically based on specified disks for Oracle ASM storage. + * Staged Software location using a mount location which is using a pre created Persistent Volume from a network file system(NFS) * Namespace: `orestart` * Mount point for the Software Stage Location inside the Pod is specified by `swStagePvcMountLocation`. * Staged Software location inside the Pod is specified by `hostSwStageLocation`. It is assumed that the Grid Infrastructure and RDBMS Binaries are already available in this path on the Persistent Volume used. - * The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `storageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. + * The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `swDgStorageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. * Location where the `RU Patch` has been unzipped on the mounted PV is specified by `ruPatchLocation`. * Path to the Opatch Software compatible with the RU Patch is specified using `oPatchLocation`. @@ -20,10 +20,10 @@ * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `dbocir/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_rupatch_pvc.yaml` file to point to the container image you have built. * Use the file [nfs_pv_stage_vol.yaml](./nfs_pv_stage_vol.yaml) to mount the Network File System as a Persistent Volume named `pv-stage-vol1`. It is assumed this NFS has the required GI and RDBMS Base Software, unzipped RU Patch binaries and Opatch binaries in the specified location. In current case, an OCI File System is used with its export path as `/stage` and Mount Target IP as `10.0.10.212`. - * The disk names for Oracle ASM storage are specified as `/dev/asm-disk1` and `/dev/asm-disk2`. These will be mounted using the Persistent Volumes. + * The disks provisioned using customer storage class (specified by `crsDgStorageClass`) for the Oracle Restart storage are `/dev/asm-disk1` and `/dev/asm-disk2`. * Specify the size of these devices using the parameter `storageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps: Deploy Oracle Restart Database * Use the file: [oraclerestart_prov_rupatch_pvc.yaml](./oraclerestart_prov_rupatch_pvc.yaml) for this use case as below: diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md index 463f0aec..acf95d4e 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_storage_class.md @@ -11,16 +11,15 @@ * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node * Namespace: `orestart` * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. - * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. - * Name of Custom Storage Class is specified by `storageClass`. + * The GI HOME and the RDBMS HOME in the Oracle Restart Pod are mounted using a Persistent Volume created using the Storage Class (specified using `storageClass`). This Persistent Volume is mounted to `/u01` inside the Pod. Size of this Persistent Volume is specified using `swLocStorageSizeInGb`. ### In this Example: * Oracle Restart Database Slim Image `localhost/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. * When you are building the image yourself, update the image value in the `oraclerestart_prov_storage_class.yaml` file to point to the container image you have built. - * The disks provisioned using custom storage class are mounted inside the Oracle Restart Pod as `/dev/asm-disk1` and `/dev/asm-disk2`. + * The disks provisioned using customer storage class (specified by `crsDgStorageClass`) for the Oracle Restart storage are `/dev/asm-disk1` and `/dev/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files and Recovery Area Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps: Deploy Oracle Restart Database * Use the file: [oraclerestart_prov_storage_class.yaml](./oraclerestart_prov_storage_class.yaml) for this use case as below: From ec44d7a65871ce4c43607e135cb9ac1530ed86a9 Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Thu, 9 Oct 2025 23:01:37 +0000 Subject: [PATCH 410/414] doc changes --- docs/oraclerestart/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md index e1ef5d39..582964b7 100644 --- a/docs/oraclerestart/README.md +++ b/docs/oraclerestart/README.md @@ -50,9 +50,10 @@ Deploy Oracle Restart Database YAML files using Kubernetes Cluster on your Oracl [8. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) [9. Provisioning an Oracle Restart Database with RU Patch and One Offs with Custom Storage Class](./provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md) [10. Provisioning an Oracle Restart Database with multiple diskgroups](./provisioning/provisioning_oracle_restart_multiple_diskgroups.md) -[11. Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy](./provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md) -[12. Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database](./provisioning/add_asm_disk_to_an_existing_restart_database.md) -[13. Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database](./provisioning/delete_asm_disks_from_an_existing_restart_database.md) +[11. Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy](./provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md) +[12. Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy and option to specify separate storage class](./provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md) +[13. Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database](./provisioning/add_asm_disk_to_an_existing_restart_database.md) +[14. Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database](./provisioning/delete_asm_disks_from_an_existing_restart_database.md) **NOTE:** Resizing of the `ASM Disks` is NOT allowed. You can add new ASM Disks to an exising Oracle Restart Database. From 1ce959879421d6ac64a9295a6a0f85f9abe9bd5e Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Thu, 9 Oct 2025 23:05:19 +0000 Subject: [PATCH 411/414] doc changes --- docs/oraclerestart/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md index 582964b7..320acb8a 100644 --- a/docs/oraclerestart/README.md +++ b/docs/oraclerestart/README.md @@ -50,7 +50,7 @@ Deploy Oracle Restart Database YAML files using Kubernetes Cluster on your Oracl [8. Provisioning an Oracle Restart Database with RU Patch on Existing PVC](./provisioning/provisioning_oracle_restart_rupatch_pvc.md) [9. Provisioning an Oracle Restart Database with RU Patch and One Offs with Custom Storage Class](./provisioning/provisioning_oracle_restart_db_rupatch_oneoffs.md) [10. Provisioning an Oracle Restart Database with multiple diskgroups](./provisioning/provisioning_oracle_restart_multiple_diskgroups.md) -[11. Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy](./provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md) +[11. Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy](./provisioning/provisioning_oracle_restart_multiple_diskgroups_with_redundancy.md) [12. Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy and option to specify separate storage class](./provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md) [13. Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database](./provisioning/add_asm_disk_to_an_existing_restart_database.md) [14. Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database](./provisioning/delete_asm_disks_from_an_existing_restart_database.md) @@ -72,4 +72,4 @@ Steps to cleanup Oracle Restart Database deployed using Oracle Restart Controlle ## Debugging and Troubleshooting -To debug the Oracle Restart Database provisioned using the Oracle Restart Controller in Oracle Database Kubernetes Operator, follow this document: [Debugging and troubleshooting](./provisioning/debugging.md) \ No newline at end of file +To debug the Oracle Restart Database provisioned using the Oracle Restart Controller in Oracle Database Kubernetes Operator, follow this document: [Debugging and troubleshooting](./provisioning/debugging.md) From cbeeb2761545a25691cbbb307da00b19e5d14daa Mon Sep 17 00:00:00 2001 From: Jyoti Verma Date: Thu, 9 Oct 2025 23:06:52 +0000 Subject: [PATCH 412/414] doc changes --- ...e_diskgroups_with_redundancy_with_separate_storage_class.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md index 55d13cd2..69d4cae3 100644 --- a/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md +++ b/docs/oraclerestart/provisioning/oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.md @@ -1,5 +1,6 @@ # Provisioning an Oracle Restart Database with multiple diskgroups with different redundancy and option to specify separate storage class -# In this Usecase: + +### In this Usecase: The Oracle Grid Infrastructure and Oracle Restart Database are deployed automatically using Oracle Restart Controller. In this example, multiple diskgroups are created for CRS Files, Database Files, Recovery Area Files and Redo Log Files. Different Disk Groups have different redundancy levels. This example uses `oraclerestart_prov_multiple_diskgroups_with_redundancy_with_separate_storage_class.yaml` to provision an Oracle Database configured with Oracle Restart using Oracle Restart Controller. The provisioning includes: From ca54e1d86fb38406c5679635c5ccd59084df00ea Mon Sep 17 00:00:00 2001 From: Paramdeep Saini Date: Fri, 10 Oct 2025 02:35:32 +0000 Subject: [PATCH 413/414] Added fixes --- commons/oraclerestart/oraclerestartprov.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/commons/oraclerestart/oraclerestartprov.go b/commons/oraclerestart/oraclerestartprov.go index b80116f4..81e8fb48 100644 --- a/commons/oraclerestart/oraclerestartprov.go +++ b/commons/oraclerestart/oraclerestartprov.go @@ -329,7 +329,7 @@ func buildVolumeSpecForOracleRestart(instance *oraclerestart.OracleRestart, Orac result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: OracleRestartSpex.HostSwLocation}}}) } else { if instance.Spec.SwStorageClass != "" { - result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: OracleRestartSpex.Name + "-oradata-sw-vol-pvc-" + OracleRestartSpex.Name + "-0"}}}) + result = append(result, corev1.Volume{Name: OracleRestartSpex.Name + "-oradata-sw-vol", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: GetSwPvcName(instance.Name, instance)}}}) } } @@ -1089,6 +1089,7 @@ func SwVolumeClaimTemplatesForOracleRestart(instance *oraclerestart.OracleRestar // If user-provided PVC name exists, skip volume claim template creation //pvcName := GetSwPvcName(OracleRestartSpex.Name) + // If you are making any change, please refer GetSwPvcName function as we add instance.Spec.InstDetails.Name + "-0" pvcName := "odb-sw-pvc-" + instance.Name return corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ From c9f5aaf6b2aead53764b557cf697057e9092d45f Mon Sep 17 00:00:00 2001 From: saurabh_ahuja Date: Fri, 10 Oct 2025 07:10:46 +0000 Subject: [PATCH 414/414] Github oper 2.0 --- .../database.oracle.com_oraclerestarts.yaml | 18 +++++++++++--- docs/oraclerestart/README.md | 10 ++++---- ...sm_disk_to_an_existing_restart_database.md | 14 +++++------ ...change_memory_cpu_for_oracle_restart_db.md | 24 +++++++++---------- ...e_sw_storage_size_for_oracle_restart_db.md | 20 ++++++++-------- docs/oraclerestart/provisioning/cleanup.md | 10 ++++---- .../create_kubernetes_secret_for_db_user.md | 4 ++-- .../provisioning/database_connection.md | 16 ++++++------- docs/oraclerestart/provisioning/debugging.md | 20 ++++++++-------- ...disks_from_an_existing_restart_database.md | 20 ++++++++-------- .../provisioning_oracle_restart_db.md | 14 +++++------ ...isioning_oracle_restart_db_loadbalancer.md | 16 ++++++------- oracle-database-operator.yaml | 18 +++++++++++--- 13 files changed, 114 insertions(+), 90 deletions(-) diff --git a/config/crd/bases/database.oracle.com_oraclerestarts.yaml b/config/crd/bases/database.oracle.com_oraclerestarts.yaml index 3f849e39..5db2113c 100644 --- a/config/crd/bases/database.oracle.com_oraclerestarts.yaml +++ b/config/crd/bases/database.oracle.com_oraclerestarts.yaml @@ -147,10 +147,12 @@ spec: type: integer recoAsmDeviceList: type: string - recoAsmDiskDgRedudancy: + recoAsmDiskDgRedundancy: type: string redoAsmDeviceList: type: string + redoAsmDiskDg: + type: string redoAsmDiskDgRedundancy: type: string ruFolderName: @@ -168,6 +170,10 @@ spec: swStagePvcMountLocation: type: string type: object + crsDgStorageClass: + type: string + dataDgStorageClass: + type: string dbSecret: properties: encryptionType: @@ -480,6 +486,10 @@ spec: format: int32 type: integer type: object + recoDgStorageClass: + type: string + redoDgStorageClass: + type: string resources: properties: claims: @@ -668,7 +678,7 @@ spec: required: - name type: object - storageClass: + swDgStorageClass: type: string tdeWalletSecret: properties: @@ -929,10 +939,12 @@ spec: type: integer recoAsmDeviceList: type: string - recoAsmDiskDgRedudancy: + recoAsmDiskDgRedundancy: type: string redoAsmDeviceList: type: string + redoAsmDiskDg: + type: string redoAsmDiskDgRedundancy: type: string ruFolderName: diff --git a/docs/oraclerestart/README.md b/docs/oraclerestart/README.md index 320acb8a..a7fbe0a1 100644 --- a/docs/oraclerestart/README.md +++ b/docs/oraclerestart/README.md @@ -6,7 +6,7 @@ For more information on Oracle Restart Database 19c refer to the [Oracle Databas Kubernetes provides essential infrastructure building blocks, including compute, storage, and networking resources, and exposes them as code for infrastructure automation. This approach enables rapid provisioning of multi-node topologies. Additionally, Kubernetes offers the **StatefulSet** workload API object—ideal for managing stateful applications such as Oracle Restart, Single Instance Oracle Databases, and other Oracle features or configurations that require persistent storage and stable network identities. -The Oracle Restart Controller in the Oracle Database Operator deploys Oracle Databases as a StatefulSet within Kubernetes clusters, utilizing the Oracle Restart Slim Image. The Oracle Restart Controller manages the typical lifecycle operations of an Oracle Database in a Kubernetes environment, including deployment, monitoring, scaling, upgrades, and deletion, as illustrated below: +The Oracle Restart Controller in the Oracle Database Operator deploys Oracle Databases as a StatefulSet within Kubernetes clusters, using the Oracle Restart Slim Image. The Oracle Restart Controller manages the typical lifecycle operations of an Oracle Database in a Kubernetes environment, including deployment, monitoring, scaling, upgrades, and deletion, as illustrated below: * Create Oracle Database * Install and Configure Oracle Grid Infrastructure @@ -34,7 +34,7 @@ To create an Oracle Database, complete the steps in the following sections: To become familiar with Oracle Restart in containerized environments, review the [this documentation](https://github.com/oracle/docker-images/blob/main/OracleDatabase/RAC/OracleRealApplicationClusters/docs/orestart/README.md) before proceeding further -[Pre-requisites for running Oracle Restart Controller](./provisioning/prerequisites_oracle_restart_db.md) +[Prerequisites for running Oracle Restart Controller](./provisioning/prerequisites_oracle_restart_db.md) ## Provisioning Oracle Restart database in an Oracle Kubernetes Engine Environment @@ -55,7 +55,7 @@ Deploy Oracle Restart Database YAML files using Kubernetes Cluster on your Oracl [13. Adding ASM Disks - Add ASM Disks to an existing Oracle Restart Database](./provisioning/add_asm_disk_to_an_existing_restart_database.md) [14. Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database](./provisioning/delete_asm_disks_from_an_existing_restart_database.md) -**NOTE:** Resizing of the `ASM Disks` is NOT allowed. You can add new ASM Disks to an exising Oracle Restart Database. +**NOTE:** Resizing of the `ASM Disks` is _not_ allowed. You can add new ASM Disks to an exising Oracle Restart Database. ## Connecting to Oracle Restart Database @@ -63,11 +63,11 @@ After the Oracle Restart database has been provisioned using the Oracle Restart ## Known Issues -Please refer to this document for any known issues related to deploying Oracle Restart Database using Oracle Restart Controller: [Known Issues](./provisioning/known_issues.md) +Refer to the Known Issues document for assistance related to issues deploying Oracle Restart Database using Oracle Restart Controller: [Known Issues](./provisioning/known_issues.md) ## Cleanup -Steps to cleanup Oracle Restart Database deployed using Oracle Restart Controller in this document in Oracle Database Kubernetes Operator are documented in this page: [Cleanup](./provisioning/cleanup.md) +Steps to clean up Oracle Restart Database deployed using Oracle Restart Controller in this document in Oracle Database Kubernetes Operator are documented in this page: [Cleanup](./provisioning/cleanup.md) ## Debugging and Troubleshooting diff --git a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md index 4d8f5772..eee3f5ae 100644 --- a/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md +++ b/docs/oraclerestart/provisioning/add_asm_disk_to_an_existing_restart_database.md @@ -19,13 +19,13 @@ * Update the Oracle Restart Custom Resource. Edit the custom resource YAML (`oraclerestarts.database.oracle.com`) to reference the new PVCs/disks under the appropriate ASM configuration. ### In this Example: - * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). - * The existing disks on the worker nodes for the ASM which are being used in Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. - * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. - * Skip this steps, if you are using **storage class to dynamically provision ASM disks**. In this example, two new disks will be added to the existing Oracle Restart Database Deployment. For this purpose, the disks on the worker nodes which will be used are `/dev/disk/by-partlabel/asm-disk3` and `/dev/disk/by-partlabel/asm-disk4`. - * Update the corresponding device list in initParams section. - * Default value in yaml file is `autoUpdate: "true"`, which will delete and recreate the pod with updated ASM disks in the Oracle Restart Deployment. In this case, the new disks will be automatically added to the existing Diskgroup. - * If the value in yaml file is set to `autoUpdate: "false"`, the Oracle Restart Database Pod is recreated, but the additional disks are `NOT` added to the ASM Disk Group automatically. + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used. The image is built using files from this [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). + * The existing disks on the worker nodes for the ASM that are used in Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. + * Specify the size and names of these devices using the parameter `storageSizeInGb`. By default, size is in GBs. + * If you are using **storage class to dynamically provision ASM disks**, then you can skip these steps. In this example, two new disks will be added to the existing Oracle Restart Database Deployment. For this purpose, the disks on the worker nodes that will be used are `/dev/disk/by-partlabel/asm-disk3` and `/dev/disk/by-partlabel/asm-disk4`. + * Update the corresponding device list in the `initParams` section. + * The default value in YAML file is `autoUpdate: "true"`, which will delete and recreate the pod with updated ASM disks in the Oracle Restart Deployment. In this case, the new disks will be automatically added to the existing Diskgroup. + * If the value in the YAML file is set to `autoUpdate: "false"`, then the Oracle Restart Database Pod is recreated, but the additional disks are _not_ added to the ASM Disk Group automatically. ## When autoUpdate is set to true diff --git a/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md b/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md index 0e897bfa..cb6e45a1 100644 --- a/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/change_memory_cpu_for_oracle_restart_db.md @@ -1,31 +1,31 @@ # Change Memory and CPU allocation for an earlier provisioned Oracle Restart Database -### In this Usecase: +### In this Use case: * You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. -* Change the memory and CPU allocation for an existing Oracle Restart Database Pod, you can do so by updating resource requests and limits in the Custom Resource YAML associated with your Oracle Restart instance. +* You can change the memory and CPU allocation for an existing Oracle Restart Database Pod by updating resource requests and limits in the Custom Resource YAML associated with your Oracle Restart instance. * This example uses `oraclerestart_prov_nodeports.yaml` to provision the initial Oracle Restart Database using Oracle Restart Controller with: * Oracle Restart Pod * Headless services for Oracle Restart * Oracle Restart Node hostname - * Node Port 30007 mapped to port 1521 for Database Listener. If you are using Loadbalancer service then you will see lbservice. - * Persistent volumes created automatically based on specified disks for Oracle ASM storage - * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. If you are using Storageclass then the software volume is dynamically provisioned. + * Node Port 30007 mapped to port 1521 for Database Listener. If you are using Loadbalancer service then you will see `lbservice`. + * Persistent volumes that are created automatically based on specified disks for Oracle ASM storage + * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. If you are using Storageclass, then the software volume is dynamically provisioned. * Namespace: `orestart` - * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. If you are using, exisitng NFS based PVC for the staged software, the pramater is `swStagePvcMountLocation` under `configParams`. + * Staged Software location on the worker nodes is specified by `hostSwStageLocation`. The Grid Infrastructure and RDBMS Binaries are copied to this location on the worker node. If you are using, existing NFS-based PVC for the staged software, then the parameter is `swStagePvcMountLocation` under `configParams`. * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. ### In this Example: - * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `localhost/oracle/database-orestart:19.3.0-slim`. - * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image you have built. + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used. It is built using files from this [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You must tag it with the name `localhost/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov_nodeports.yaml` file to point to the container image that you have built. * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. - * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + * Specify the sizes and names of these devices using the parameter `storageSizeInGb`. By default, size is in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps - Deploy initial Oracle Restart Database -* Use the file: [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for the initial deployment before any cpu or memory change. +* Use the file [oraclerestart_prov_nodeports.yaml](./oraclerestart_prov_nodeports.yaml) for the initial deployment before any CPU or memory change. * Deploy the `oraclerestart_prov_nodeports.yaml` file: ```sh kubectl apply -f oraclerestart_prov_nodeports.yaml @@ -44,7 +44,7 @@ ``` ### Steps - Modify the Memory and CPU in the Oracle Restart Database Pod -* Use the file: [oraclerestart_prov_nodeports_mcpu_change.yaml](./oraclerestart_prov_nodeports_mcpu_change.yaml) to change the Memory and CPU allocation for the existing Oracle Restart Database Pod: +* Use the file [oraclerestart_prov_nodeports_mcpu_change.yaml](./oraclerestart_prov_nodeports_mcpu_change.yaml) to change the Memory and CPU allocation for the existing Oracle Restart Database Pod: ```sh kubectl apply -f oraclerestart_prov_nodeports_mcpu_change.yaml oraclerestart.database.oracle.com/oraclerestart-sample configured diff --git a/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md b/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md index 56836dca..a93036a5 100644 --- a/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/change_sw_storage_size_for_oracle_restart_db.md @@ -1,10 +1,10 @@ # Change the size of Software Storage Location for an existing Oracle Restart Database -### In this Usecase: +### In this Use case: * You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. -* The Software Home Location for Grid Infrastructure and Database, the ASM Disks are provisioned as Persistent Volumes using custom storage class during the initial deployment. An updated .yaml file is applied to `increase` the size of the Software Home Location. +* The Software Home Location for Grid Infrastructure and Database, the ASM Disks are provisioned as Persistent Volumes using custom storage class during the initial deployment. An updated YAML file is applied to `increase` the size of the Software Home Location. -**NOTE:** The `decrease` in the size of Software Home Location for an existing Oracle Restart Database is `not allowed`. +**NOTE:** The `decrease` in the size of Software Home Location for an existing Oracle Restart Database is _not_ allowed. This example uses `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` to initially provision an Oracle Restart Database using Oracle Restart Controller with: @@ -20,15 +20,15 @@ This example uses `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` * You will be using storageclass to dynamically allcate the storage. using the storage class **oci-bv**. ### In this Example: - * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used. It is built using files from this [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). * The disks provisioned using storageclass are mounted inside the Oracle Restart Pod as `/dev/asm-disk1` and `/dev/asm-disk2`. * Specify the size of these devices along with names using the parameter `swLocStorageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps - Deploy the Oracle Restart Database * Skip this step if you have already deployed the Oracle Restart database using storage class. -* Use the file: [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) for this use case as below: +* Use the file [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) for this use case in this procedure. * Update the Oracle Restart container image. In this example, we have `dbocir/oracle/database-orestart:19.3.0-slim` in [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) file to point to the container image you have built. * Deploy the `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` file: ```sh @@ -46,14 +46,14 @@ This example uses `oraclerestart_prov_storage_class_before_sw_home_resize.yaml` ORACLE DATABASE IS READY TO USE =============================== ``` -* Check Details of Kubernetes CRD Object as in this [example](./orestart_storage_class_object_before_sw_home_resize.txt) +* Check Details of Kubernetes CRD Object as shown in this [example](./orestart_storage_class_object_before_sw_home_resize.txt) ### Steps - Update the Software Home Location in Oracle Restart Database -* In order to `increase` the size of Software Home Location, you can use the updated file [oraclerestart_prov_storage_class_after_sw_home_resize.yaml](./oraclerestart_prov_storage_class_after_sw_home_resize.yaml). -* Update the Oracle Restart container image. In this example, we have `dbocir/oracle/database-orestart:19.3.0-slim` in [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) file to point to the container image you have built. +* To `increase` the size of the Software Home Location, you can use the updated file [oraclerestart_prov_storage_class_after_sw_home_resize.yaml](./oraclerestart_prov_storage_class_after_sw_home_resize.yaml). +* Update the Oracle Restart container image. In this example, we use the file `dbocir/oracle/database-orestart:19.3.0-slim` in [oraclerestart_prov_storage_class_before_sw_home_resize.yaml](./oraclerestart_prov_storage_class_before_sw_home_resize.yaml) to point to the container image that you have built. * Deploy the `oraclerestart_prov_storage_class_after_sw_home_resize.yaml` file: ```sh $ kubectl apply -f oraclerestart_prov_storage_class_after_sw_home_resize.yaml oraclerestart.database.oracle.com/oraclerestart-sample configured ``` - You will notice Persistent Volume for the Software Location has been resized. You can check Details of updated Kubernetes CRD Object as in this [example](./orestart_storage_class_object_after_sw_home_resize.txt) + You will notice the Persistent Volume for the Software Location has been resized. You can check Details of updated Kubernetes CRD Object as shown in this [example](./orestart_storage_class_object_after_sw_home_resize.txt) diff --git a/docs/oraclerestart/provisioning/cleanup.md b/docs/oraclerestart/provisioning/cleanup.md index 34a8593f..846927ce 100644 --- a/docs/oraclerestart/provisioning/cleanup.md +++ b/docs/oraclerestart/provisioning/cleanup.md @@ -1,11 +1,11 @@ # Cleanup an Oracle Restart Database Deployed using Oracle Database Operator -In order to delete and cleanup the Oracle Restart Database deployed using Oracle Database Operator, run below command. +To delete and clean up the Oracle Restart Database deployed using Oracle Database Operator, run the following commands. -This example uses `oraclerestart_prov.yaml` to cleanup an Oracle Restart Database which was initially used for deployment: +This example uses `oraclerestart_prov.yaml` to clean up an Oracle Restart Database that was initially used for deployment: -1. Use the `oraclerestart_prov.yaml` file to delete an existing deployment: +1. Use the file `oraclerestart_prov.yaml` to delete an existing deployment: ```sh kubectl delete -f oraclerestart_prov.yaml oraclerestart.database.oracle.com/oraclerestart-sample deleted @@ -15,6 +15,6 @@ This example uses `oraclerestart_prov.yaml` to cleanup an Oracle Restart Databas # Check the status of the Kubernetes Pods: kubectl get all -n orestart ``` -3. If your deleted deployment used software location specified by `hostSwLocation` from worker node, then in order to reuse this location in next deployment, you will need to clear it at the worker node level. +3. If your deleted deployment used a software location specified by `hostSwLocation` from the worker node, then to reuse this location in your next deployment, you must clear it at the worker node level. -4. If your deleted deployment used ASM Disks from the worker node, then in order to reuse the same disks for the next deployment, you will need to clear the disks at the worker node level using `dd` command. \ No newline at end of file +4. If your deleted deployment used ASM Disks from the worker node, then to reuse the same disks for the next deployment, you must clear the disks at the worker node level using the `dd` command. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md index 3f0d2d05..90ce9c45 100644 --- a/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md +++ b/docs/oraclerestart/provisioning/create_kubernetes_secret_for_db_user.md @@ -1,8 +1,8 @@ # Create Kubernetes secret for db user -Create a Kubernetes secret named `db-user-pass-pkutl` using a password in a text file and then encrypt it using an `openssl` key. The text file will be removed after secret is created. You need to make sure openssl must be installed on worker nodes. +Create a Kubernetes secret named `db-user-pass-pkutl` using a password in a text file, and then encrypt it using an `openssl` key. The text file will be removed after the secret is created. Note that openssl _must_ be installed on worker nodes. -**NOTE:** The openssl version on the system where you run the below commands to generate the secret and the openssl version of the Oracle Restart Slim Image must be compatible with each other. +**NOTE:** The openssl version on the system where you run the commands in this procedure to generate the secret and the openssl version of the Oracle Restart Slim Image must be compatible with each other. ```sh mkdir /tmp/.secrets/ diff --git a/docs/oraclerestart/provisioning/database_connection.md b/docs/oraclerestart/provisioning/database_connection.md index 2cef9640..14d3935e 100644 --- a/docs/oraclerestart/provisioning/database_connection.md +++ b/docs/oraclerestart/provisioning/database_connection.md @@ -1,9 +1,9 @@ # Database Connectivity -Depending on whether the Oracle Restart Database has been deployed using Oracle Restart Controller with a NodePort Service or a Load Balancer Service etc, you can refer to the below examples to connect to the Oracle Restart Database: +To connect to the Oracle Restart Database, follow the example that matches your deployment method—NodePort Service, Load Balancer Service, or another supported method. ## Database Connection to Oracle Restart Database with NodePort Service -The Oracle Database with NodePort service deployed by Oracle Restart Controller can be reached using the Worker Node IP and the Port of the Node Port service. Use the below steps: +If you deployed the Oracle Restart Database with a NodePort service using the Oracle Restart Controller, then you can connect by specifying the worker node’s IP address and the port of the Node Port service. Follow these steps: 1. Get the Details of the deployment: ```sh @@ -18,9 +18,9 @@ service/dbmc1-0 ClusterIP None 171m NAME READY AGE CONTAINERS IMAGES statefulset.apps/dbmc1 1/1 5h46m dbmc1 localhost/oracle/database-orestart:19.3.0-slim ``` -In this case, the port 1521 from the pod is mapped to port 30007 on the worker node. To make the connection from outside, you will need to open the port 30007 on the worker node for INGRESS. +In this case, the port 1521 from the pod is mapped to port 30007 on the worker node. To make the connection from outside, you must open the port 30007 on the worker node for INGRESS. -2. For the above deployment, you will be able to make an SQLPLUS database connection to this Oracle Restart Database from a remote client as below: +2. With this NodePort Service deployment, you can make a SQL*Plus database connection to this Oracle Restart Database from a remote client: ```sh bash-4.4$ sqlplus system/@//:30007/PORCLCDB @@ -48,9 +48,9 @@ PORCLCDB dbmc1-0 SINGLE ## Database Connection to Oracle Restart Database with Load Balancer -In this case, the Oracle Restart Database is deployed with an External Load Balancer and the deployment has a Public IP Assigned from the External Load Balancer Service. +In this case, the Oracle Restart Database is deployed with an External Load Balancer, and the deployment has a Public IP Assigned from the External Load Balancer Service. -Once the deployment is completed, you will be able to make a database connection as below: +After the deployment is completed, you can make a database connection: 1. Get the Details of the deployment: ```sh @@ -65,9 +65,9 @@ service/dbmc1-0 ClusterIP None NAME READY AGE CONTAINERS IMAGES statefulset.apps/dbmc1 1/1 14m dbmc1 localhost/oracle/database-orestart:19.3.0-slim ``` -In this case, you will be able to make a remote database connection using the Load Balancer target port 1521. +In this case, you can make a remote database connection using the Load Balancer target port 1521. -2. For the above deployment, you will be able to make an SQLPLUS database connection to this Oracle Restart Database from a remote client as below: +2. With this Load Balancer deployment, you can make a SQL*Plus database connection to this Oracle Restart Database from a remote client: ```sh bash-4.4$ sqlplus system/@//:1521/PORCLCDB diff --git a/docs/oraclerestart/provisioning/debugging.md b/docs/oraclerestart/provisioning/debugging.md index 57084823..d943f2e7 100644 --- a/docs/oraclerestart/provisioning/debugging.md +++ b/docs/oraclerestart/provisioning/debugging.md @@ -1,40 +1,40 @@ # Debugging and Troubleshooting -When the Oracle Restart Database is provisioned using the Oracle Restart controller, the debugging of an issue with the deployment depends on at which stage the issue has been seen. +When provisioning the Oracle Restart Database with the Oracle Restart Controller, your debugging approach depends on the stage in which the issue occurs. Use the guidance below for targeted troubleshooting. Below are the possible cases and the steps to debug such an issue: ## Failure during the provisioning of Kubernetes Pods -In case the failure occurs during the provisioning, we need to check the status of the Kubernetes Pod which has failed to deployed. +If the failure occurs while creating Kubernetes Pods, start by checking the status of the affected Pod: -Use the below command to check the logs of the Pod which has a failure. For example, for failure in case of Pod `pod/dbmc1-0`, use below command: +Use the following command to check the logs of the Pod that has a failure. For example, pod `pod/dbmc1-0`, use the following command: ```sh kubectl logs -f pod/dbmc1-0 -n orestart ``` -In case the Pod has failed to provision due to an issue with the Docker Image Pull, you will see the error `Error: ErrImagePull` in above logs. +If the Pod has failed to provision due to an issue with the Docker Image Pull, then you see the error `Error: ErrImagePull` in the logs. -If the Pod has not yet got initialized, use the below command to find the reason for it: +If the Pod has not yet been initialized, then use the following command to find the reason for it: ```sh kubectl describe pod/dbmc1-0 -n orestart ``` -You will need to further troubleshoot depending upon the issue/error seen from the above step. +You will need to further troubleshoot depending on further issues or errors seen after you run `kubectl describe pod`. -## Failure in the provisioning of the Oracle Database +## Oracle Database Provisioning Failure -In case the failure occures after the Kubernetes Pods are created but during the execution of the scripts to create the Oracle database, you will need to trobleshoot that at the individual Pod level. +If the Oracle Database provisioning fails after the Kubernetes Pods have been created, but during the processing of database creation scripts, then troubleshoot the issue within the affected Pod. -Initially, check the logs of the Kubernetes Pod using the command like below (change the name of the Pod with the actual Pod) +Initially, check the logs of the Kubernetes Pod using the `kubectl logs` command (exchange the name of the Pod in this example with the actual Pod on your system): ```sh kubectl logs -f pod/dbmc1-0 -n orestart ``` -To check the details of the CRS logs or the RDBMS instance logs at the host level, switch to the corresponding Kubernetes container using the command like below: +To check the details of the CRS logs or the RDBMS instance logs at the host level, switch to the corresponding Kubernetes container using the following command: ```sh kubectl exec -it dbmc1-0 -n orestart -- tail -f /tmp/orod/oracle_db_setup.log diff --git a/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md b/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md index cc513f10..ba70aa40 100644 --- a/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md +++ b/docs/oraclerestart/provisioning/delete_asm_disks_from_an_existing_restart_database.md @@ -1,13 +1,13 @@ # Deleting ASM Disks - Delete ASM Disks from an existing Oracle Restart Database -### In this usecase: +### In this use case: * You have previously deployed an Oracle Restart Database in Kubernetes (for example, on OKE or OpenShift) using the Oracle Restart Database Controller. Now, you need to remove ASM disks from the ASM storage. -* The existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` from Case [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) using Oracle Restart Controller with: +* The existing Oracle Restart Database is deployed with Node Port Service using the file `oraclerestart_prov_nodeports.yaml` described in [Provisioning an Oracle Restart Database with NodePort Service](./provisioning/provisioning_oracle_restart_db_nodeport.md) This deployment uses Oracle Restart Controller with: * Oracle Restart Pod * Headless services for Oracle Restart * Oracle Restart Node hostname - * Node Port 30007 mapped to port 1521 for Database Listener. If you are using Loadbalancer then you will see LB service. + * Node Port 30007 mapped to port 1521 for Database Listener. If you are using Load Balancer, then you will see the LB service. * Persistent volumes created automatically based on specified disks for Oracle ASM storage * Software Persistent Volume and Staged Software Persistent Volume using the specified location on the corresponding worker node. * Namespace: `orestart` @@ -15,15 +15,15 @@ * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. ### In this exapmple: - * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used. It is built using files from this [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. - * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + * Specify the size of these devices along with names using the parameter `storageSizeInGb`. By default, size is in GBs. * Before deleting the disk, **you will need to remove the disk at the ASM Level** using `ALTER DISKGROUP DROP DISK` command. - * If the disk is first NOT removed at the ASM Level and the .yaml file to delete the disk is applied, to avoid any data loss, the operator will NOT delete the disk from the Stateful Set and the Oracle Restart Database Pod will not be recreated. Operator will be in wait state until the disk is deleted at the ASM Level. + * If you delete the disk’s YAML file before first removing the disk at the ASM level, thne the Operator will not delete the disk from the StatefulSet, and the Oracle Restart Database Pod will not be recreated. Instead, to prevent data loss, the operator will wait until you remove the disk at the ASM level before proceeding. * In this example, out of the two disks mentioned above, the disk `/dev/disk/by-partlabel/asm-disk2` will be deleted from the existing Oracle Restart Database Deployment. ### Steps: Delete the ASM Disk -Use the file: [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) for this use case as below: +Use the file: [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) for this procedure: * Deploy the `orestart_prov_asm_disk_deletion.yaml` file: ```sh @@ -40,8 +40,8 @@ In this case, the disk will be deleted and Oracle Restart Database Pod will be r # Check the logs of a particular pod. For example, to check status of pod "dbmc1-0": kubectl exec -it pod/dbmc1-0 -n orestart -- bash -c "tail -f /tmp/orod/oracle_db_setup.log" ``` -* Samples logs in [logs](./asm_disk_deletion.txt) when the disk `/dev/disk/by-partlabel/asm-disk2` is removed at the ASM Level first and then[orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) file is applied to remove it from the Stateful set. +* You can review example logs in [logs](./asm_disk_deletion.txt) when the disk `/dev/disk/by-partlabel/asm-disk2` is removed at the ASM Level first and then the file [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) is applied to remove it from the Stateful set. -* Samples logs in [logs](./asm_disk_deletion1.txt) when the disk `/dev/disk/by-partlabel/asm-disk2` is tried to be removed from Stateful Set using the file [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) but this disk is NOT yet removed at the ASM Level. +* Example logs in [logs](./asm_disk_deletion1.txt) show what happens when the attempt is made to remove the disk `/dev/disk/by-partlabel/asm-disk2` from the Stateful Set by using the file [orestart_prov_asm_disk_deletion.yaml](./orestart_prov_asm_disk_deletion.yaml) but this disk is NOT yet removed at the ASM Level. -In this case, the Operator will be waiting for the disk to be deleted at the ASM Level. Once it detects the Disk has been deleted at the ASM Level and it is safe to proceed, it will remove the disk from the Stateful Set. \ No newline at end of file +In this case, the Operator will be waiting for the disk to be deleted at the ASM Level. After it detects the Disk has been deleted at the ASM Level, and it is safe to proceed, it will remove the disk from the Stateful Set. \ No newline at end of file diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md index bc9e29da..90720156 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db.md @@ -12,15 +12,15 @@ * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. ### In this Example: - * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). Default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You need to tag it with name `dbocir/oracle/database-orestart:19.3.0-slim`. - * When you are building the image yourself, update the image value in the `oraclerestart_prov.yaml` file to point to the container image you have built. + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used. It is built using files from thsi [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). The default image created using files from this project is `localhost/oracle/database-rac:19.3.0-slim`. You must tag it with the name `dbocir/oracle/database-orestart:19.3.0-slim`. + * When you are building the image yourself, update the image value in the `oraclerestart_prov.yaml` file to point to the container image that you have built. * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. - * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. + * Specify the sizes and names of these devices using the parameter `storageSizeInGb`. By default, size is in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps: Deploy Oracle Restart Database -* Use the file: [oraclerestart_prov.yaml](./oraclerestart_prov.yaml) for this use case as below: +* Use the file [oraclerestart_prov.yaml](./oraclerestart_prov.yaml) for this procedure: * Deploy the `oraclerestart_prov.yaml` file: ```sh kubectl apply -f oraclerestart_prov.yaml @@ -37,5 +37,5 @@ ORACLE DATABASE IS READY TO USE =============================== ``` -* Check Details of Kubernetes CRD Object as in this [example](./orestart_object.txt) -* efer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +* Check Details of Kubernetes CRD Object as shown in this [example](./orestart_object.txt) +* For details about how ot connect to the Oracle Restart Database deployed in this procedure, refer to [Database Connection](./database_connection.md). diff --git a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md index dc44f7aa..ed91a65f 100644 --- a/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md +++ b/docs/oraclerestart/provisioning/provisioning_oracle_restart_db_loadbalancer.md @@ -1,9 +1,9 @@ # Provisioning an Oracle Restart Database with Load Balancer -### In this Usecase: +### In this Use case: * In this use case, the Oracle Grid Infrastructure and Oracle Database are deployed automatically using Oracle Restart Controller. -* When Oracle Restart is deployed using the Oracle Restart Controller with a Load Balancer Service, the database is exposed externally via the cloud provider’s (e.g., Oracle Cloud) load balancer. This setup allows you to connect remotely to the database using the load balancer’s external IP and the target port—commonly 1521 (the Oracle default). -* This example uses `oraclerestart_prov_loadbalancer.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: +* When Oracle Restart is deployed using the Oracle Restart Controller with a Load Balancer Service, the database is exposed externally through the cloud provider’s (for example, Oracle Cloud's) load balancer. This setup allows you to connect remotely to the database using the load balancer’s external IP and the target port, which is commonly 1521 (the Oracle default). +* This example uses the file `oraclerestart_prov_loadbalancer.yaml` to provision an Oracle Restart Database using Oracle Restart Controller with: * Oracle Restart Pod * Headless services for Oracle Restart * Oracle Restart Node hostname @@ -15,15 +15,15 @@ * Software location on the worker nodes is specified by `hostSwLocation`. The GI HOME and the RDBMS HOME in the Oracle Restart Pod will be mounted using this location on the worker node. ### In this Example: - * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used and it is built using files from [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). + * Oracle Restart Database Slim Image `dbocir/oracle/database-orestart:19.3.0-slim` is used. It is built using files from this [GitHub location](https://github.com/oracle/docker-images/tree/main/OracleDatabase/RAC/OracleRealApplicationClusters#building-oracle-rac-database-container-slim-image). * The disks on the worker nodes for the Oracle Restart storage are `/dev/disk/by-partlabel/asm-disk1` and `/dev/disk/by-partlabel/asm-disk2`. * Specify the size of these devices along with names using the parameter `storageSizeInGb`. Size is by-default in GBs. -**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, then the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. +**NOTE:** When no separate diskgroup names are specified for CRS Files, Database Files, Recovery Area Files and Redo Log Files, the default diskgroup named `+DATA` is created from the disks specified by the parameter `crsAsmDeviceList`. ### Steps: Provision Oracle Restart Database -* Use the file: [oraclerestart_prov_loadbalancer.yaml](./oraclerestart_prov_loadbalancer.yaml) for this use case as below: -* When you are building the image yourself, update the image value in the `oraclerestart_prov_loadbalancer.yaml` file to point to the container image you have built. +* Use the file [oraclerestart_prov_loadbalancer.yaml](./oraclerestart_prov_loadbalancer.yaml) for this procedure. +* When you are building the image yourself, update the image value in the `oraclerestart_prov_loadbalancer.yaml` file to point to the container image that you have built. * Deploy the `oraclerestart_prov_loadbalancer.yaml` file: ```sh kubectl apply -f oraclerestart_prov_loadbalancer.yaml @@ -41,4 +41,4 @@ =============================== ``` * Check Details of Kubernetes CRD Object as in this [example](./orestart_loadbalancer_object.txt) -* Refer to the page [Database Connection](./database_connection.md) for the details to connect to Oracle Restart Database deployed using above example. +* For details about how to connect to the Oracle Restart Database deployed using this procedure, see: [Database Connection](./database_connection.md) . diff --git a/oracle-database-operator.yaml b/oracle-database-operator.yaml index 6c3d69c6..98535d86 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -10749,10 +10749,12 @@ spec: type: integer recoAsmDeviceList: type: string - recoAsmDiskDgRedudancy: + recoAsmDiskDgRedundancy: type: string redoAsmDeviceList: type: string + redoAsmDiskDg: + type: string redoAsmDiskDgRedundancy: type: string ruFolderName: @@ -10770,6 +10772,10 @@ spec: swStagePvcMountLocation: type: string type: object + crsDgStorageClass: + type: string + dataDgStorageClass: + type: string dbSecret: properties: encryptionType: @@ -11082,6 +11088,10 @@ spec: format: int32 type: integer type: object + recoDgStorageClass: + type: string + redoDgStorageClass: + type: string resources: properties: claims: @@ -11270,7 +11280,7 @@ spec: required: - name type: object - storageClass: + swDgStorageClass: type: string tdeWalletSecret: properties: @@ -11531,10 +11541,12 @@ spec: type: integer recoAsmDeviceList: type: string - recoAsmDiskDgRedudancy: + recoAsmDiskDgRedundancy: type: string redoAsmDeviceList: type: string + redoAsmDiskDg: + type: string redoAsmDiskDgRedundancy: type: string ruFolderName:

+p?`Me)>`WtWPG- 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 - - -# Use case directory - -The use case directory contains the `yaml` files to test the multitenant controller functionalities: create `lrest` pod, and create PDB operations *create / open / close / unplug / plug / delete / clone /map / parameter session* - -## Makefile helper - -Customizing `yaml` files (tns alias / credential / namespaces name, and so on) is a long procedure that is prone to human error. A simple [`makefile`](../usecase/makefile) is available to quickly and safely configure `yaml` files with your system environment information. Just edit the [parameter file](../usecase/parameters.txt) before proceding. - -```text -TNSALIAS...............:[Tnsalias do not use quotes and avoid space in the string --> (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELA....] -DBUSER.................:[CDB admin user] -DBPASS.................:[CDB admin user password] -WBUSER.................:[HTTPS user] -WBPASS.................:[HTTPS user password] -PDBUSR.................:[PDB admin user] -PDBPWD.................:[PDB admin user password] -PDBNAMESPACE...........:[pdb namespace] -LRSNAMESPACE...........:[cdb namespace] -COMPANY................:[your company name] -APIVERSION.............:v4 --> do not edit -``` - -⚠ **WARNING: The makefile is only intended to speed up the usecase directory configuration. Use of this file for production purposes is not supported. The editing and configuration of yaml files for production system is left up to the end user** - -### Prerequisistes: - -- Ensure that **kubectl** is properly configured. -- Ensure that all requirements listed in the [operator installation page](../../../../docs/installation/OPERATOR_INSTALLATION_README.md) are implemented. (role binding,webcert,etc) -- Ensure that the administrative user (admin) on the container database is configured as documented. - -```bash -make operator -``` -This command creates the `operator-database-operator.yaml` file in the local directory, and set up the `watchnamespace` list. Note that the `yaml` file is not applied. - -```bash -make secrets -``` -This command creates all of the Secrets with the encrypted credentials. - -```bash -make genyaml -``` -*make genyaml* generates the required `yaml` files to work with multitenant controllers. - - -![image](../images/UsecaseSchema.jpg) - -## Diag commands and troubleshooting - -### Connect to rest server pod - -```bash -/usr/bin/kubectl exec -n -it -- /bin/bash -``` - - -```bash -## example ## - -kubectl get pods -n cdbnamespace -NAME READY STATUS RESTARTS AGE -cdb-dev-lrest-rs-fnw99 1/1 Running 1 (17h ago) 18h - -kubectl exec cdb-dev-lrest-rs-fnw99 -n cdbnamespace -it -- /bin/bash -[oracle@cdb-dev-lrest-rs-fnw99 ~]$ -``` - -### Monitor control plane - -```bash -kubectl logs -f -l control-plane=controller-manager -n oracle-database-operator-system -``` -```bash -## output example: ## -2024-10-28T23:54:25Z INFO lrpdb-webhook ValidateUpdate-Validating LRPDB spec for : lrpdb2 -2024-10-28T23:54:25Z INFO lrpdb-webhook validateCommon {"name": "lrpdb2"} -2024-10-28T23:54:25Z INFO lrpdb-webhook Valdiating LRPDB Resource Action : MODIFY -2024-10-29T10:07:34Z INFO lrpdb-webhook ValidateUpdate-Validating LRPDB spec for : lrpdb2 -2024-10-29T10:07:34Z INFO lrpdb-webhook ValidateUpdate-Validating LRPDB spec for : lrpdb1 -2024-10-29T16:49:15Z INFO lrpdb-webhook ValidateUpdate-Validating LRPDB spec for : lrpdb1 -2024-10-29T16:49:15Z INFO lrpdb-webhook validateCommon {"name": "lrpdb1"} -2024-10-29T16:49:15Z INFO lrpdb-webhook Valdiating LRPDB Resource Action : CREATE -2024-10-29T10:07:20Z INFO controller-runtime.certwatcher Updated current TLS certificate -2024-10-29T10:07:20Z INFO controller-runtime.webhook Serving webhook server {"host": "", "port": 9443} -2024-10-29T10:07:20Z INFO controller-runtime.certwatcher Starting certificate watcher -I1029 10:07:20.189724 1 leaderelection.go:250] attempting to acquire leader lease oracle-database-operator-system/a9d608ea.oracle.com... -2024-10-29T16:49:15Z INFO lrpdb-webhook Setting default values in LRPDB spec for : lrpdb1 - -``` - -### Error decrypting credential - -The following is an example of a resource creation failure due to decription error: - -```text -2024-10-30T10:09:08Z INFO controllers.LRPDB getEncriptedSecret :pdbusr {"getEncriptedSecret": {"name":"lrpdb1","namespace":"pdbnamespace"}} -2024-10-30T10:09:08Z ERROR controllers.LRPDB Failed to parse private key - x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format) {"DecryptWithPrivKey": {"name":"lrpdb1","namespace":"pdbnamespace"}, "error": "x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format)"} -``` - - -**Solution**: Ensure you use **PCKS8** format during private key generation. If you are not using `openssl3`, then run this command: - -```bash -openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > mykey -``` - -### Crd details - -Use the **describe** option to obtain `crd` information - -```bash -kubectl describe lrpdb lrpdb1 -n pdbnamespace -[...] - Secret: - Key: e_wbuser.txt - Secret Name: wbuser -Status: - Action: CREATE - Bitstat: 25 - Bitstatstr: |MPAPPL|MPWARN|MPINIT| - Conn String: (DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdbdev))) - Msg: Success - Open Mode: MOUNTED - Phase: Ready - Status: true - Total Size: 2G -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Created 108s LRPDB LRPDB 'pdbdev' created successfully - Normal Created 108s LRPDB PDB 'pdbdev' assertive pdb deletion turned on - Warning LRESTINFO 95s LRPDB pdb=pdbdev:test_invalid_parameter:16:spfile:2065 - Warning Done 15s (x12 over 2m25s) LRPDB cdb-dev - -``` diff --git a/docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml deleted file mode 100644 index 0467a948..00000000 --- a/docs/multitenant/lrest-based/usecase/altersystem_pdb1_resource.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Alter" - alterSystemParameter : "cpu_count" - alterSystemValue : "3" - parameterScope : "memory" - - - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml b/docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml deleted file mode 100644 index 5fd355f4..00000000 --- a/docs/multitenant/lrest-based/usecase/cdbnamespace_binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: cdbnamespace -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml deleted file mode 100644 index 2c4afe13..00000000 --- a/docs/multitenant/lrest-based/usecase/clone_pdb1_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - pdbconfigmap: "config-map-pdb" - assertiveLrpdbDeletion: true - action: "Clone" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml deleted file mode 100644 index 16255a87..00000000 --- a/docs/multitenant/lrest-based/usecase/clone_pdb2_resource.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb4 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - pdbconfigmap: "config-map-pdb" - assertiveLrpdbDeletion: true - action: "Clone" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml deleted file mode 100644 index 87f7383d..00000000 --- a/docs/multitenant/lrest-based/usecase/close_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml deleted file mode 100644 index 0743bd8c..00000000 --- a/docs/multitenant/lrest-based/usecase/close_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml b/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml deleted file mode 100644 index 6c6ca519..00000000 --- a/docs/multitenant/lrest-based/usecase/close_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: ""new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/config-map-pdb.yaml b/docs/multitenant/lrest-based/usecase/config-map-pdb.yaml deleted file mode 100644 index 2769b498..00000000 --- a/docs/multitenant/lrest-based/usecase/config-map-pdb.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: config-map-pdb - namespace: pdbnamespace -data: - rdbmsparameters.txt: | - session_cached_cursors;100;spfile - open_cursors;100;spfile - db_file_multiblock_read_count;16;spfile - test_invalid_parameter;16;spfile diff --git a/docs/multitenant/lrest-based/usecase/config_map_pdb.yaml b/docs/multitenant/lrest-based/usecase/config_map_pdb.yaml deleted file mode 100644 index 2769b498..00000000 --- a/docs/multitenant/lrest-based/usecase/config_map_pdb.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: config-map-pdb - namespace: pdbnamespace -data: - rdbmsparameters.txt: | - session_cached_cursors;100;spfile - open_cursors;100;spfile - db_file_multiblock_read_count;16;spfile - test_invalid_parameter;16;spfile diff --git a/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml b/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml deleted file mode 100644 index b80c1c56..00000000 --- a/docs/multitenant/lrest-based/usecase/create_lrest_pod.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LREST -metadata: - name: cdb-dev - namespace: cdbnamespace -spec: - cdbName: "DB12" - lrestImage: container-registry.oracle.com/database/operator:lrest-241210-amd64 - lrestImagePullPolicy: "Always" - dbTnsurl : "(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - replicas: 1 - deletePdbCascade: true - cdbAdminUser: - secret: - secretName: "dbuser" - key: "e_dbuser.txt" - cdbAdminPwd: - secret: - secretName: "dbpass" - key: "e_dbpass.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbPubKey: - secret: - secretName: "pubkey" - key: "publicKey" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml deleted file mode 100644 index fa58d36a..00000000 --- a/docs/multitenant/lrest-based/usecase/create_pdb1_resource.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - pdbconfigmap: "config-map-pdb" - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml deleted file mode 100644 index 02d5763b..00000000 --- a/docs/multitenant/lrest-based/usecase/create_pdb2_resource.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - pdbconfigmap: "config-map-pdb" - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml deleted file mode 100644 index 1a3c328a..00000000 --- a/docs/multitenant/lrest-based/usecase/delete_pdb1_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml deleted file mode 100644 index 747641d4..00000000 --- a/docs/multitenant/lrest-based/usecase/delete_pdb2_resource.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/makefile b/docs/multitenant/lrest-based/usecase/makefile deleted file mode 100644 index 1de320ad..00000000 --- a/docs/multitenant/lrest-based/usecase/makefile +++ /dev/null @@ -1,911 +0,0 @@ -# Copyright (c) 2022, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. -# __ __ _ __ _ _ -# | \/ | __ _| | _____ / _(_) | ___ -# | |\/| |/ _` | |/ / _ \ |_| | |/ _ \ -# | | | | (_| | < __/ _| | | __/ -# |_| |_|\__,_|_|\_\___|_| |_|_|\___| -# | | | | ___| |_ __ ___ _ __ -# | |_| |/ _ \ | '_ \ / _ \ '__| -# | _ | __/ | |_) | __/ | -# |_| |_|\___|_| .__/ \___|_| -# |_| -# -# WARNING: Using this makefile helps you to customize yaml -# files. Edit parameters.txt with your enviroment -# informartion and execute the following steps -# -# 1) make operator -# it configures the operator yaml files with the -# watch namelist required by the multitenant controllers -# -# 2) make secrets -# It configure the required secrets necessary to operate -# with pdbs multitenant controllers -# -# 3) make genyaml -# It automatically creates all the yaml files based on the -# information available in the parameters file -# -# LIST OF GENERAED YAML FILE -# -# ----------------------------- ---------------------------------- -# oracle-database-operator.yaml : oracle database operator -# lrestnamespace_binding.yaml : role binding for lrestnamespace -# pdbnamespace_binding.yaml : role binding for pdbnamespace -# create_lrest_secret.yaml : create secrets for rest server pod -# create_lrpdb_secret.yaml : create secrets for pluggable database -# create_lrest_pod.yaml : create rest server pod -# create_pdb1_resource.yaml : create first pluggable database -# create_pdb2_resource.yaml : create second pluggable database -# open_pdb1_resource.yaml : open first pluggable database -# open_pdb2_resource.yaml : open second pluggable database -# close_pdb1_resource.yaml : close first pluggable database -# close_pdb2_resource.yaml : close second pluggable database -# clone_lrpdb_resource.yaml : clone thrid pluggable database -# clone_pdb2_resource.yaml : clone 4th pluggable database -# delete_pdb1_resource.yaml : delete first pluggable database -# delete_pdb2_resource.yaml : delete sencond pluggable database -# delete_pdb3_resource.yaml : delete thrid pluggable database -# unplug_pdb1_resource.yaml : unplug first pluggable database -# plug_pdb1_resource.yaml : plug first pluggable database -# map_pdb1_resource.yaml : map the first pluggable database -# config_map.yam : pdb parameters array -# altersystem_pdb1_resource.yaml : chage cpu_count count parameter for the first pdb -# -DATE := `date "+%y%m%d%H%M%S"` -###################### -# PARAMETER SECTIONS # -###################### - -export PARAMETERS=parameters.txt -export TNSALIAS=$(shell cat $(PARAMETERS) |grep -v ^\#|grep TNSALIAS|cut -d : -f 2) -export DBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep DBUSER|cut -d : -f 2) -export DBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep DBPASS|cut -d : -f 2) -export WBUSER=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBUSER|cut -d : -f 2) -export WBPASS=$(shell cat $(PARAMETERS)|grep -v ^\#|grep WBPASS|cut -d : -f 2) -export PDBUSR=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBUSR|cut -d : -f 2) -export PDBPWD=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBPWD|cut -d : -f 2) -export PDBNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep PDBNAMESPACE|cut -d : -f 2) -export LRSNAMESPACE=$(shell cat $(PARAMETERS)|grep -v ^\#|grep LRSNAMESPACE|cut -d : -f 2) -export LRESTIMG=$(shell cat $(PARAMETERS)|grep -v ^\#|grep LRESTIMG|cut -d : -f 2,3) -export COMPANY=$(shell cat $(PARAMETERS)|grep -v ^\#|grep COMPANY|cut -d : -f 2) -export APIVERSION=$(shell cat $(PARAMETERS)|grep -v ^\#|grep APIVERSION|cut -d : -f 2) -export OPRNAMESPACE=oracle-database-operator-system -export ORACLE_OPERATOR_YAML=../../../../oracle-database-operator.yaml -export TEST_EXEC_TIMEOUT=3m - -REST_SERVER=lrest -SKEY=tls.key -SCRT=tls.crt -CART=ca.crt -PRVKEY=ca.key -PUBKEY=public.pem -COMPANY=oracle -DBUSERFILE=dbuser.txt -DBPASSFILE=dbpass.txt -WBUSERFILE=wbuser.txt -WBPASSFILE=wbpass.txt -PDBUSRFILE=pdbusr.txt -PDBPWDFILE=pdbpwd.txt - -################# -### FILE LIST ### -################# - -export LREST_POD=create_lrest_pod.yaml - -export LRPDBCRE1=create_pdb1_resource.yaml -export LRPDBCRE2=create_pdb2_resource.yaml - -export LRPDBCLOSE1=close_pdb1_resource.yaml -export LRPDBCLOSE2=close_pdb2_resource.yaml -export LRPDBCLOSE3=close_pdb3_resource.yaml - -export LRPDBOPEN1=open_pdb1_resource.yaml -export LRPDBOPEN2=open_pdb2_resource.yaml -export LRPDBOPEN3=open_pdb3_resource.yaml - -export LRPDBCLONE1=clone_pdb1_resource.yaml -export LRPDBCLONE2=clone_pdb2_resource.yaml - -export LRPDBDELETE1=delete_pdb1_resource.yaml -export LRPDBDELETE2=delete_pdb2_resource.yaml -export LRPDBDELETE3=delete_pdb3_resource.yaml - -export LRPDBUNPLUG1=unplug_pdb1_resource.yaml -export LRPDBPLUG1=plug_pdb1_resource.yaml - -export LRPDBMAP1=map_pdb1_resource.yaml -export LRPDBMAP2=map_pdb2_resource.yaml -export LRPDBMAP3=map_pdb3_resource.yaml - -export LRPDBMAP1=map_pdb1_resource.yaml -export LRPDBMAP2=map_pdb2_resource.yaml -export LRPDBMAP3=map_pdb3_resource.yaml - -export ALTERSYSTEMYAML=altersystem_pdb1_resource.yaml -export CONFIG_MAP=config_map_pdb.yaml - - - - -##BINARIES -export KUBECTL=/usr/bin/kubectl -OPENSSL=/usr/bin/openssl -ECHO=/usr/bin/echo -RM=/usr/bin/rm -CP=/usr/bin/cp -TAR=/usr/bin/tar -MKDIR=/usr/bin/mkdir -SED=/usr/bin/sed - -check: - @printf "TNSALIAS...............:%.60s....\n" $(TNSALIAS) - @printf "DBUSER.................:%s\n" $(DBUSER) - @printf "DBPASS.................:%s\n" $(DBPASS) - @printf "WBUSER.................:%s\n" $(WBUSER) - @printf "WBPASS.................:%s\n" $(WBPASS) - @printf "PDBUSR.................:%s\n" $(PDBUSR) - @printf "PDBPWD.................:%s\n" $(PDBPWD) - @printf "PDBNAMESPACE...........:%s\n" $(PDBNAMESPACE) - @printf "LRSNAMESPACE...........:%s\n" $(LRSNAMESPACE) - @printf "COMPANY................:%s\n" $(COMPANY) - @printf "APIVERSION.............:%s\n" $(APIVERSION) - -define msg -@printf "\033[31;7m%s\033[0m\r" "......................................]" -@printf "\033[31;7m[\xF0\x9F\x91\x89 %s\033[0m\n" $(1) -endef - -tls: - $(call msg,"TLS GENERATION") - #$(OPENSSL) genrsa -out $(PRVKEY) 2048 - $(OPENSSL) genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 > $(PRVKEY) - $(OPENSSL) req -new -x509 -days 365 -key $(PRVKEY) \ - -subj "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=$(COMPANY) Root CA" -out ca.crt - $(OPENSSL) req -newkey rsa:2048 -nodes -keyout $(SKEY) -subj \ - "/C=CN/ST=GD/L=SZ/O=$(COMPANY), Inc./CN=cdb-dev-$(REST_SERVER).$(LRSNAMESPACE)" -out server.csr - $(ECHO) "subjectAltName=DNS:cdb-dev-$(REST_SERVER).$(LRSNAMESPACE)" > extfile.txt - $(OPENSSL) x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey $(PRVKEY) -CAcreateserial -out $(SCRT) - $(OPENSSL) rsa -in $(PRVKEY) -outform PEM -pubout -out $(PUBKEY) - -secrets: tls delsecrets - $(call msg,"CREATING NEW TLS/PRVKEY/PUBKEY SECRETS") - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(LRSNAMESPACE) - $(KUBECTL) create secret tls db-tls --key="$(SKEY)" --cert="$(SCRT)" -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic db-ca --from-file="$(CART)" -n $(PDBNAMESPACE) - #$(KUBECTL) create secret tls prvkey --key="$(PRVKEY)" --cert=ca.crt -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic pubkey --from-file=publicKey=$(PUBKEY) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey=$(PRVKEY) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic prvkey --from-file=privateKey="$(PRVKEY)" -n $(PDBNAMESPACE) - $(call msg,"CREATING NEW CREDENTIAL SECRETS") - @$(ECHO) $(DBUSER) > $(DBUSERFILE) - @$(ECHO) $(DBPASS) > $(DBPASSFILE) - @$(ECHO) $(WBUSER) > $(WBUSERFILE) - @$(ECHO) $(WBPASS) > $(WBPASSFILE) - @$(ECHO) $(PDBUSR) > $(PDBUSRFILE) - @$(ECHO) $(PDBPWD) > $(PDBPWDFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBUSERFILE) |base64 > e_$(DBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(DBPASSFILE) |base64 > e_$(DBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBUSERFILE) |base64 > e_$(WBUSERFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(WBPASSFILE) |base64 > e_$(WBPASSFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBUSRFILE) |base64 > e_$(PDBUSRFILE) - $(OPENSSL) rsautl -encrypt -pubin -inkey $(PUBKEY) -in $(PDBPWDFILE) |base64 > e_$(PDBPWDFILE) - $(KUBECTL) create secret generic dbuser --from-file=e_$(DBUSERFILE) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic dbpass --from-file=e_$(DBPASSFILE) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(LRSNAMESPACE) - $(KUBECTL) create secret generic wbuser --from-file=e_$(WBUSERFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic wbpass --from-file=e_$(WBPASSFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic pdbusr --from-file=e_$(PDBUSRFILE) -n $(PDBNAMESPACE) - $(KUBECTL) create secret generic pdbpwd --from-file=e_$(PDBPWDFILE) -n $(PDBNAMESPACE) - $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl \ - $(DBUSERFILE) $(DBPASSFILE) $(WBUSERFILE) $(WBPASSFILE) $(PDBUSRFILE) $(PDBPWDFILE)\ - e_$(DBUSERFILE) e_$(DBPASSFILE) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) - $(KUBECTL) get secrets -n $(LRSNAMESPACE) - $(KUBECTL) get secrets -n $(PDBNAMESPACE) - -delsecrets: - $(call msg,"CLEAN OLD SECRETS") - $(eval SECRETSP:=$(shell kubectl get secrets -n $(PDBNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) - $(eval SECRETSL:=$(shell kubectl get secrets -n $(LRSNAMESPACE) -o custom-columns=":metadata.name" --no-headers) ) - @[ "${SECRETSP}" ] && ( \ - printf "Deleteing secrets in namespace -n $(PDBNAMESPACE)\n") &&\ - ($(KUBECTL) delete secret $(SECRETSP) -n $(PDBNAMESPACE))\ - || ( echo "No screts in namespace $(PDBNAMESPACE)") - @[ "${SECRETSL}" ] && ( \ - printf "Deleteing secrets in namespace -n $(LRSNAMESPACE)\n") &&\ - ($(KUBECTL) delete secret $(SECRETSL) -n $(LRSNAMESPACE))\ - || ( echo "No screts in namespace $(PDBNAMESPACE)") - -cleanCert: - $(RM) $(SKEY) $(SCRT) $(CART) $(PRVKEY) $(PUBKEY) server.csr extfile.txt ca.srl \ - $(DBUSERFILE) $(DBPASSFILE) $(WBUSERFILE) $(WBPASSFILE) $(PDBUSRFILE) $(PDBPWDFILE)\ - e_$(DBUSERFILE) e_$(DBPASSFILE) e_$(WBUSERFILE) e_$(WBPASSFILE) e_$(PDBUSRFILE) e_$(PDBPWDFILE) - -### YAML FILE SECTION ### -define _opr -cp ${ORACLE_OPERATOR_YAML} . -export OPBASENAME=`basename ${ORACLE_OPERATOR_YAML}` -#export PDBNAMESPACE=$(cat ${PARAMETERS}|grep -v ^\#|grep PDBNAMESPACE|cut -d : -f 2) - -cp ${OPBASENAME} ${OPBASENAME}.ORIGNINAL -printf "\n\t\xF0\x9F\x91\x89 ${OPBASENAME}\n\n" -printf "\n\t\xF0\x9F\x91\x89 ${PDBNAMESPACE}\n\n" -sed -i 's/value: ""/value: ${OPRNAMESPACE},$PDBNAMESPACE,${LRSNAMESPACE}/g' ${OPBASENAME} -endef - -export opr = $(value _opr) - -operator: -# @ eval "$$opr" - $(CP) ${ORACLE_OPERATOR_YAML} . - ${CP} `basename ${ORACLE_OPERATOR_YAML}` `basename ${ORACLE_OPERATOR_YAML}`.ORG - $(SED) -i 's/value: ""/value: $(OPRNAMESPACE),$(PDBNAMESPACE),$(LRSNAMESPACE)/g' `basename ${ORACLE_OPERATOR_YAML}` - - -define _script00 -cat < authsection.yaml - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - - -cat < ${PDBNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: ${PDBNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -cat < ${LRSNAMESPACE}_binding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding2 - namespace: ${LRSNAMESPACE} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system -EOF - -endef -export script00 = $(value _script00) -secyaml: - @ eval "$$script00" - - -#echo lrest pod creation -define _script01 -cat < ${LREST_POD} -apiVersion: database.oracle.com/${APIVERSION} -kind: LREST -metadata: - name: cdb-dev - namespace: ${LRSNAMESPACE} -spec: - cdbName: "DB12" - lrestImage: ${LRESTIMG} - lrestImagePullPolicy: "Always" - dbTnsurl : ${TNSALIAS} - replicas: 1 - deletePdbCascade: true - cdbAdminUser: - secret: - secretName: "dbuser" - key: "e_dbuser.txt" - cdbAdminPwd: - secret: - secretName: "dbpass" - key: "e_dbpass.txt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - cdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - cdbPubKey: - secret: - secretName: "pubkey" - key: "publicKey" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" -EOF - -endef -export script01 = $(value _script01) - - -define _script02 - -cat <${LRPDBCRE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - pdbconfigmap: "config-map-pdb" - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat < ${LRPDBCRE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - unlimitedStorage: false - pdbconfigmap: "config-map-pdb" - tdeImport: false - totalSize: "2G" - tempSize: "800M" - action: "Create" -EOF - -cat <${LRPDBOPEN1} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${LRPDBOPEN2} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${LRPDBOPEN3} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" -EOF - -cat <${LRPDBCLOSE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${LRPDBCLOSE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat <${LRPDBCLOSE3} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - pdbState: "CLOSE" - modifyOption: "IMMEDIATE" - action: "Modify" -EOF - -cat < ${LRPDBCLONE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - srcPdbName: "pdbdev" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - pdbconfigmap: "config-map-pdb" - assertiveLrpdbDeletion: true - action: "Clone" -EOF - -cat < ${LRPDBCLONE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb4 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone2" - srcPdbName: "pdbprd" - fileNameConversions: "NONE" - totalSize: "UNLIMITED" - tempSize: "UNLIMITED" - pdbconfigmap: "config-map-pdb" - assertiveLrpdbDeletion: true - action: "Clone" -EOF - -cat < ${LRPDBDELETE1} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - pdbName: "pdbdev" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${LRPDBDELETE2} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - pdbName: "pdbprd" - action: "Delete" - dropAction: "INCLUDING" -EOF - -cat < ${LRPDBUNPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/var/tmp/pdb.$$.xml" - action: "Unplug" -EOF - -cat <${LRPDBPLUG1} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/var/tmp/pdb.$$.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertiveLrpdbDeletion: true - pdbconfigmap: "config-map-pdb" - action: "Plug" -EOF - -cat <${LRPDBMAP1} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - -cat <${LRPDBMAP2} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb2 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbprd" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - - -cat <${LRPDBMAP3} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb3 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "new_clone" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" -EOF - -cat <${CONFIG_MAP} -apiVersion: v1 -kind: ConfigMap -metadata: - name: config-map-pdb - namespace: ${PDBNAMESPACE} -data: - rdbmsparameters.txt: | - session_cached_cursors;100;spfile - open_cursors;100;spfile - db_file_multiblock_read_count;16;spfile - test_invalid_parameter;16;spfile -EOF - - -cat < ${ALTERSYSTEMYAML} -apiVersion: database.oracle.com/${APIVERSION} -kind: LRPDB -metadata: - name: pdb1 - namespace: ${PDBNAMESPACE} - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "${LRSNAMESPACE}" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Alter" - alterSystemParameter : "cpu_count" - alterSystemValue : "3" - parameterScope : "memory" - - -EOF - -## Auth information -for _file in ${LRPDBCRE1} ${LRPDBCRE2} ${LRPDBOPEN1} ${LRPDBOPEN2} ${LRPDBOPEN3} ${LRPDBCLOSE1} ${LRPDBCLOSE2} ${LRPDBCLOSE3} ${LRPDBCLONE1} ${LRPDBCLONE2} ${LRPDBDELETE1} ${LRPDBDELETE2} ${LRPDBUNPLUG1} ${LRPDBPLUG1} ${LRPDBMAP1} ${LRPDBMAP2} ${LRPDBMAP3} ${ALTERSYSTEMYAML} -do -ls -ltr ${_file} - cat authsection.yaml >> ${_file} -done -rm authsection.yaml -endef - -export script02 = $(value _script02) - -genyaml: secyaml - @ eval "$$script01" - @ eval "$$script02" - -cleanyaml: - - $(RM) $(LRPDBMAP3) $(LRPDBMAP2) $(LRPDBMAP1) $(LRPDBPLUG1) $(LRPDBUNPLUG1) $(LRPDBDELETE2) $(LRPDBDELETE1) $(LRPDBCLONE2) $(LRPDBCLONE1) $(LRPDBCLOSE3) $(LRPDBCLOSE2) $(LRPDBCLOSE1) $(LRPDBOPEN3) $(LRPDBOPEN2) $(LRPDBOPEN1) $(LRPDBCRE2) $(LRPDBCRE1) $(LREST_POD) ${ALTERSYSTEMYAML} - - $(RM) ${CONFIG_MAP} ${PDBNAMESPACE}_binding.yaml ${LRSNAMESPACE}_binding.yaml - - - - -################# -### PACKAGING ### -################# - -pkg: - - $(RM) -rf /tmp/pkgtestplan - $(MKDIR) /tmp/pkgtestplan - $(CP) -R * /tmp/pkgtestplan - $(CP) ../../../../oracle-database-operator.yaml /tmp/pkgtestplan/ - $(TAR) -C /tmp -cvf ~/pkgtestplan_$(DATE).tar pkgtestplan - -################ -### diag ### -################ - -login: - $(KUBECTL) exec `$(KUBECTL) get pods -n $(LRSNAMESPACE)|grep rest|cut -d ' ' -f 1` -n $(LRSNAMESPACE) -it -- /bin/bash - - -reloadop: - echo "RESTARTING OPERATOR" - $(eval OP1 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1 )) - $(eval OP2 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1|cut -d ' ' -f 1 )) - $(eval OP3 := $(shell $(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1 )) - $(KUBECTL) get pod $(OP1) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP2) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - $(KUBECTL) get pod $(OP3) -n $(OPRNAMESPACE) -o yaml | kubectl replace --force -f - - - -dump: - @$(eval TMPSP := $(shell date "+%y%m%d%H%M%S" )) - @$(eval DIAGFILE := ./opdmp.$(TMPSP)) - @>$(DIAGFILE) - @echo "OPERATOR DUMP" >> $(DIAGFILE) - @echo "~~~~~~~~~~~~~" >> $(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|head -2|tail -1 | cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - $(KUBECTL) logs pod/`$(KUBECTL) get pods -n $(OPRNAMESPACE)|grep oracle-database-operator-controller|tail -1|cut -d ' ' -f 1` -n $(OPRNAMESPACE) >>$(DIAGFILE) - -####################################################### -#### TEST SECTION #### -####################################################### - -run00: - @$(call msg,"lrest pod creation") - - $(KUBECTL) delete lrest cdb-dev -n $(LRSNAMESPACE) - $(KUBECTL) apply -f $(LREST_POD) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"lrest pod completed") - $(KUBECTL) get lrest -n $(LRSNAMESPACE) - $(KUBECTL) get pod -n $(LRSNAMESPACE) - -run01.1: - @$(call msg,"lrpdb pdb1 creation") - $(KUBECTL) apply -f $(LRPDBCRE1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "lrpdb pdb1 creation completed") - $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) - -run01.2: - @$(call msg, "lrpdb pdb2 creation") - $(KUBECTL) apply -f $(LRPDBCRE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "lrpdb pdb2 creation completed") - $(KUBECTL) get lrpdb pdb2 -n $(PDBNAMESPACE) - -run02.1: - @$(call msg, "lrpdb pdb1 open") - $(KUBECTL) apply -f $(LRPDBOPEN1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "lrpdb pdb1 open completed") - $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) - -run02.2: - @$(call msg,"lrpdb pdb2 open") - $(KUBECTL) apply -f $(LRPDBOPEN2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="READ WRITE" lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"lrpdb pdb2 open completed") - $(KUBECTL) get lrpdb pdb2 -n $(PDBNAMESPACE) - - -run03.1: - @$(call msg,"clone pdb1-->pdb3") - $(KUBECTL) apply -f $(LRPDBCLONE1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb3 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb1-->pdb3 completed") - $(KUBECTL) get lrpdb pdb3 -n $(PDBNAMESPACE) - - -run03.2: - @$(call msg,"clone pdb2-->pdb4") - $(KUBECTL) apply -f $(LRPDBCLONE2) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb4 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"clone pdb2-->pdb4 completed") - $(KUBECTL) get lrpdb pdb3 -n $(PDBNAMESPACE) - - -run04.1: - @$(call msg,"lrpdb pdb1 close") - $(KUBECTL) apply -f $(LRPDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "lrpdb pdb1 close completed") - $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) - -run04.2: - @$(call msg,"lrpdb pdb2 close") - $(KUBECTL) apply -f $(LRPDBCLOSE2) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" lrpdb pdb2 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"lrpdb pdb2 close completed") - $(KUBECTL) get lrpdb pdb2 -n $(PDBNAMESPACE) - -run05.1: - @$(call msg,"lrpdb pdb1 unplug") - $(KUBECTL) apply -f $(LRPDBUNPLUG1) - $(KUBECTL) wait --for=delete lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"lrpdb pdb1 unplug completed") - -run06.1: - @$(call msg, "lrpdb pdb1 plug") - $(KUBECTL) apply -f $(LRPDBPLUG1) - $(KUBECTL) wait --for jsonpath='{.status.phase'}="Ready" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg, "lrpdb pdb1 plug completed") - $(KUBECTL) get lrpdb pdb1 -n $(PDBNAMESPACE) - -run07.1: - @$(call msg,"lrpdb pdb1 delete ") - - $(KUBECTL) apply -f $(LRPDBCLOSE1) - $(KUBECTL) wait --for jsonpath='{.status.openMode'}="MOUNTED" lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) apply -f $(LRPDBDELETE1) - $(KUBECTL) wait --for=delete lrpdb pdb1 -n $(PDBNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - @$(call msg,"lrpdb pdb1 delete") - $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) - -run99.1: - $(KUBECTL) delete lrest cdb-dev -n $(LRSNAMESPACE) - $(KUBECTL) wait --for=delete lrest cdb-dev -n $(LRSNAMESPACE) --timeout=$(TEST_EXEC_TIMEOUT) - $(KUBECTL) get lrest -n $(LRSNAMESPACE) - $(KUBECTL) get lrpdb -n $(PDBNAMESPACE) - -runall01: run00 run01.1 run01.2 run02.1 run02.2 run03.1 run03.2 run04.1 run04.2 run05.1 run06.1 run07.1 - - diff --git a/docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml deleted file mode 100644 index 2cd57b87..00000000 --- a/docs/multitenant/lrest-based/usecase/map_pdb1_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml deleted file mode 100644 index bab614cf..00000000 --- a/docs/multitenant/lrest-based/usecase/map_pdb2_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml b/docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml deleted file mode 100644 index 7bbae48d..00000000 --- a/docs/multitenant/lrest-based/usecase/map_pdb3_resource.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - assertiveLrpdbDeletion: true - fileNameConversions: "NONE" - totalSize: "1G" - tempSize: "100M" - action: "Map" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml deleted file mode 100644 index a845a0bd..00000000 --- a/docs/multitenant/lrest-based/usecase/open_pdb1_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml b/docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml deleted file mode 100644 index 9356184f..00000000 --- a/docs/multitenant/lrest-based/usecase/open_pdb2_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb2 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbprd" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml b/docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml deleted file mode 100644 index 1b8024ba..00000000 --- a/docs/multitenant/lrest-based/usecase/open_pdb3_resource.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb3 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "new_clone" - action: "Modify" - pdbState: "OPEN" - modifyOption: "READ WRITE" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/parameters.txt b/docs/multitenant/lrest-based/usecase/parameters.txt deleted file mode 100644 index 1f21ed38..00000000 --- a/docs/multitenant/lrest-based/usecase/parameters.txt +++ /dev/null @@ -1,52 +0,0 @@ - -######################## -## REST SERVER IMAGE ### -######################## - -LRESTIMG:container-registry.oracle.com/database/operator:lrest-241210-amd64 - -############################## -## TNS URL FOR CDB CREATION ## -############################## -TNSALIAS:"(DESCRIPTION=(CONNECT_TIMEOUT=90)(RETRY_COUNT=30)(RETRY_DELAY=10)(TRANSPORT_CONNECT_TIMEOUT=70)(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan12.testrac.com)(PORT=1521)(IP=V4_ONLY))(LOAD_BALLANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=scan34.testrac.com)(PORT=1521)(IP=V4_ONLY))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TESTORDS)))" - -########################################### -## CDB USER FOR PDB LIFECYCLE MANAGMENT ### -########################################### - -DBUSER:restdba -DBPASS:CLWKO655321 - -####################### -## HTTPS CREDENTIAL ### -####################### - -WBUSER:welcome -WBPASS:welcome1 - -##################### -## PDB ADMIN USER ### -##################### - -PDBUSR:Citizenkane -PDBPWD:Rosebud - -################### -### NAMESPACES #### -################### - -PDBNAMESPACE:pdbnamespace -LRSNAMESPACE:cdbnamespace - - -#################### -### COMPANY NAME ### -#################### - -COMPANY:oracle - -#################### -### APIVERSION ### -#################### - -APIVERSION:v4 diff --git a/docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml b/docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml deleted file mode 100644 index 5af79ed6..00000000 --- a/docs/multitenant/lrest-based/usecase/pdbnamespace_binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: oracle-database-operator-oracle-database-operator-manager-rolebinding1 - namespace: pdbnamespace -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: oracle-database-operator-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: oracle-database-operator-system diff --git a/docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml deleted file mode 100644 index d7d310db..00000000 --- a/docs/multitenant/lrest-based/usecase/plug_pdb1_resource.yaml +++ /dev/null @@ -1,54 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "plug" - fileNameConversions: "NONE" - sourceFileNameConversions: "NONE" - copyAction: "MOVE" - totalSize: "1G" - tempSize: "100M" - assertiveLrpdbDeletion: true - pdbconfigmap: "config-map-pdb" - action: "Plug" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml b/docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml deleted file mode 100644 index a5da5a57..00000000 --- a/docs/multitenant/lrest-based/usecase/unplug_pdb1_resource.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: database.oracle.com/v4 -kind: LRPDB -metadata: - name: pdb1 - namespace: pdbnamespace - labels: - cdb: cdb-dev -spec: - cdbResName: "cdb-dev" - cdbNamespace: "cdbnamespace" - cdbName: "DB12" - pdbName: "pdbdev" - xmlFileName: "/tmp/pdb.xml" - action: "Unplug" - adminpdbUser: - secret: - secretName: "pdbusr" - key: "e_pdbusr.txt" - adminpdbPass: - secret: - secretName: "pdbpwd" - key: "e_pdbpwd.txt" - lrpdbTlsKey: - secret: - secretName: "db-tls" - key: "tls.key" - lrpdbTlsCrt: - secret: - secretName: "db-tls" - key: "tls.crt" - lrpdbTlsCat: - secret: - secretName: "db-ca" - key: "ca.crt" - webServerUser: - secret: - secretName: "wbuser" - key: "e_wbuser.txt" - webServerPwd: - secret: - secretName: "wbpass" - key: "e_wbpass.txt" - cdbPrvKey: - secret: - secretName: "prvkey" - key: "privateKey" diff --git a/docs/multitenant/ords-based/NamespaceSeg.md b/docs/multitenant/ords-based/NamespaceSeg.md deleted file mode 100644 index 6738fe56..00000000 --- a/docs/multitenant/ords-based/NamespaceSeg.md +++ /dev/null @@ -1,14 +0,0 @@ - - -# Namespace segregation - -With the namespace segregation pdb controller and cdb controller run in different namespaces. The new functionality introduces a new parameter (the cdb namespace) in pdb crd definition. In case you don't need the namespace segregation you have to sepcify the namespace name that you are using for yours crd and pods anyway. Refer to usercase01 and usecase02 to see single namespace configuration. Refer to usecase03 to see examples of namespace segregation. - -# Secrets - -In order to use multiple namespace we need to create approriate secrets in each namespace. Tls certificate secrets must be created in all namespaces (db-ca db-tls). - -![general_schema](./images/K8S_NAMESPACE_SEG.png) - - - diff --git a/docs/multitenant/ords-based/README.md b/docs/multitenant/ords-based/README.md deleted file mode 100644 index edfd0208..00000000 --- a/docs/multitenant/ords-based/README.md +++ /dev/null @@ -1,411 +0,0 @@ - - -# Oracle Multitenant Database Controllers - -The Oracle Database Operator for Kubernetes uses two controllers to manage the [Pluggable Database lifecycle][oradocpdb] - -- CDB controller -- PDB controller - -By using CDB/PDB controllers, you can perform the following actions **CREATE**, **MODIFY(OPEN/COSE)**, **DELETE**, **CLONE**, **PLUG** and **UNPLUG** against pluggable database - -Examples are located under the following directories: - -- the directories [`Usecase`](./usecase/) and [`usecase01`](./usecase01/) contain a [configuration file](./usecase/parameters.txt) where you can specify all the details of your environment. A [`makefile`](./usecase/makefile) takes this file as input to generate all of the `yaml` files. There is no need to edit `yaml` files one by one. -- [Singlenamespace provisioning](./provisioning/singlenamespace/) This file contains base example files that you can use to manage the PDB and CDB within a single namespace. -- [Multinamespace provisioning](./provisioning/multinamespace/) This file contains base example files that you can use to manage the PDB and CDB in different namespaces. -- [Usecase01](./usecase01/README.md) [Usecase02](./usecase02/README.md) This file contains other step-by-step examples; - -Automatic `yaml` generation is not available for the directory `usecase02` and provisioning directories. - -**NOTE** the CDB controller is not intended to manage the container database. The CDB controller is meant to provide a pod with a REST server connected to the container database that you can use to manage PDBs. - - -## Macro steps for setup - -- Deploy the Oracle Database Operator (operator, or `OraOperator`) -- [Create Ords based image for CDB pod](./provisioning/ords_image.md) -- [Container RDBMB user creation](#prepare-the-container-database-for-pdb-lifecycle-management-pdb-lm) -- Create certificates for https connection -- Create secrets for credentials and certificates -- Create CDB pod using the Ords based image - -## Oracle DB Operator Multitenant Database Controller Deployment - -To deploy `OraOperator`, use this [Oracle Database Operator for Kubernetes](https://github.com/oracle/oracle-database-operator/blob/main/README.md) step-by-step procedure. - -After the **Oracle Database Operator** is deployed, you can see the Oracle Database (DB) Operator Pods running in the Kubernetes Cluster. The multitenant controllers are deployed as part of the `OraOperator` deployment. You can see the CRDs (Custom Resource Definition) for the CDB and PDBs in the list of CRDs. The following output is an example of such a deployment: - -```bash -[root@test-server oracle-database-operator]# kubectl get ns -NAME STATUS AGE -cert-manager Active 32h -default Active 245d -kube-node-lease Active 245d -kube-public Active 245d -kube-system Active 245d -oracle-database-operator-system Active 24h <---- namespace to deploy the Oracle Database Operator - -[root@test-server oracle-database-operator]# kubectl get all -n oracle-database-operator-system -NAME READY STATUS RESTARTS AGE -pod/oracle-database-operator-controller-manager-665874bd57-dlhls 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 0 28s -pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 0 28s - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 29s -service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 29s - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 29s - -NAME DESIRED CURRENT READY AGE -replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 29s -[root@docker-test-server oracle-database-operator]# - -[root@test-server oracle-database-operator]# kubectl get crd -NAME CREATED AT -autonomouscontainerdatabases.database.oracle.com 2022-06-22T01:21:36Z -autonomousdatabasebackups.database.oracle.com 2022-06-22T01:21:36Z -autonomousdatabaserestores.database.oracle.com 2022-06-22T01:21:37Z -autonomousdatabases.database.oracle.com 2022-06-22T01:21:37Z -cdbs.database.oracle.com 2022-06-22T01:21:37Z <---- -certificaterequests.cert-manager.io 2022-06-21T17:03:46Z -certificates.cert-manager.io 2022-06-21T17:03:47Z -challenges.acme.cert-manager.io 2022-06-21T17:03:47Z -clusterissuers.cert-manager.io 2022-06-21T17:03:48Z -dbcssystems.database.oracle.com 2022-06-22T01:21:38Z -issuers.cert-manager.io 2022-06-21T17:03:49Z -oraclerestdataservices.database.oracle.com 2022-06-22T01:21:38Z -orders.acme.cert-manager.io 2022-06-21T17:03:49Z -pdbs.database.oracle.com 2022-06-22T01:21:39Z <--- -shardingdatabases.database.oracle.com 2022-06-22T01:21:39Z -singleinstancedatabases.database.oracle.com 2022-06-22T01:21:40Z -``` - - -## Prerequisites to manage PDB Life Cycle using Oracle DB Operator Multitenant Database Controller - -* [Prepare the container database (CDB) for PDB Lifecycle Management or PDB-LM](#prepare-cdb-for-pdb-lifecycle-management-pdb-lm) -* [Oracle REST Data Service or ORDS Image](#oracle-rest-data-service-ords-image) -* [Kubernetes Secrets](#kubernetes-secrets) -* [Kubernetes CRD for CDB](#cdb-crd) -* [Kubernetes CRD for PDB](#pdb-crd) - -## Prepare the container database for PDB Lifecycle Management (PDB-LM) - -Pluggable Database (PDB) management operations are performed in the Container Database (CDB). These operations include **create**, **clone**, **plug**, **unplug**, **delete**, **modify** and **map pdb**. - -To perform PDB lifecycle management operations, you must first use the following steps to define the default CDB administrator credentials on target CDBs: - -Create the CDB administrator user and grant the required privileges. In this example, the user is `C##DBAPI_CDB_ADMIN`. However, any suitable common username can be used. - -```SQL -SQL> conn /as sysdba - --- Create following users at the database level: - -ALTER SESSION SET "_oracle_script"=true; -DROP USER C##DBAPI_CDB_ADMIN cascade; -CREATE USER C##DBAPI_CDB_ADMIN IDENTIFIED BY CONTAINER=ALL ACCOUNT UNLOCK; -GRANT SYSOPER TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; -GRANT SYSDBA TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; -GRANT CREATE SESSION TO C##DBAPI_CDB_ADMIN CONTAINER = ALL; - - --- Verify the account status of the following usernames. They should not be in locked status: - -col username for a30 -col account_status for a30 -select username, account_status from dba_users where username in ('ORDS_PUBLIC_USER','C##DBAPI_CDB_ADMIN','APEX_PUBLIC_USER','APEX_REST_PUBLIC_USER'); -``` - -## OCI OKE (Kubernetes Cluster) - -You can use an [OKE in Oracle Cloud Infrastructure][okelink] to configure the controllers for PDB lifecycle management. **Note that there is no restriction about container database location; it can be anywhere (on Cloud or on-premises).** -To quickly create an OKE cluster in your OCI cloud environment you can use the following [link](./provisioning/quickOKEcreation.md). -In this setup example [provisioning example setup](./provisioning/example_setup_using_oci_oke_cluster.md), the Container Database is running on an OCI Exadata Database Cluster. - - -## Oracle REST Data Service (ORDS) Image - -The PDB Database controllers require a pod running a dedicated REST server image based on [ORDS][ordsdoc]. Read the following [document on ORDS images](./provisioning/ords_image.md) to build the ORDS images. - - -## Kubernetes Secrets - - Multitenant Controllers use Kubernetes Secrets to store the required credential and HTTPS certificates. - - **Note** In multi-namespace environments you must create specific Secrets for each namespaces. - -### Secrets for CERTIFICATES - -Create the certificates and key on your local host, and then use them to create the Kubernetes Secret. - -```bash -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 365 -key ca.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost Root CA " -out ca.crt -openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/C=US/ST=California/L=SanFrancisco/O=oracle /CN=cdb-dev-ords /CN=localhost" -out server.csr -echo "subjectAltName=DNS:cdb-dev-ords,DNS:www.example.com" > extfile.txt -openssl x509 -req -extfile extfile.txt -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -``` - -```bash -kubectl create secret tls db-tls --key="tls.key" --cert="tls.crt" -n oracle-database-operator-system -kubectl create secret generic db-ca --from-file=ca.crt -n oracle-database-operator-system -``` - -image_not_found - -**Note:** Remove temporary files after successfful Secret creation. - -### Secrets for CDB CRD - - **Note:** base64 encoded secrets are no longer supported; use OpenSSL secrets as documented in the following section. After successful creation of the CDB Resource, the CDB and PDB Secrets can be deleted from the Kubernetes system. Don't leave plaintext files containing sensitive data on disk. After loading the Secret, remove the plaintext file or move it to secure storage. - - ```bash - -export PRVKEY=ca.key -export PUBKEY=public.pem -WBUSERFILE=wbuser.txt -WBPASSFILE=wbpass.txt -CDBUSRFILE=cdbusr.txt -CDBPWDFILE=cdbpwd.txt -SYSPWDFILE=syspwd.txt -ORDPWDFILE=ordpwd.txt -PDBUSRFILE=pdbusr.txt -PDBPWDFILE=pdbpwd.txt - -# Webuser credential -echo [WBUSER] > ${WBUSERFILE} -echo [WBPASS] > ${WBPASSFILE} - -# CDB admin user credentioan -echo [CDBPWD] > ${CDBPWDFILE} -echo [CDBUSR] > ${CDBUSRFILE} - -# SYS Password -echo [SYSPWD] > ${SYSPWDFILE} - -# Ords Password -echo [ORDPWD] > ${ORDPWDFILE} - -## PDB admin credential -echo [PDBUSR] > ${PDBUSRFILE} -echo [PDBPWD] > ${PDBPWDFILE} - -#Secrets creation for pub and priv keys -openssl rsa -in ${PRVKEY} -outform PEM -pubout -out ${PUBKEY} -kubectl create secret generic pubkey --from-file=publicKey=${PUBKEY} -n ${CDBNAMESPACE} -kubectl create secret generic prvkey --from-file=privateKey=${PRVKEY} -n ${CDBNAMESPACE} -kubectl create secret generic prvkey --from-file=privateKey="${PRVKEY}" -n ${PDBNAMESPACE} - -#Password encryption -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${WBUSERFILE} |base64 > e_${WBUSERFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${WBPASSFILE} |base64 > e_${WBPASSFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${CDBPWDFILE} |base64 > e_${CDBPWDFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${CDBUSRFILE} |base64 > e_${CDBUSRFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${SYSPWDFILE} |base64 > e_${SYSPWDFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${ORDPWDFILE} |base64 > e_${ORDPWDFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${PDBUSRFILE} |base64 > e_${PDBUSRFILE} -openssl rsautl -encrypt -pubin -inkey ${PUBKEY} -in ${PDBPWDFILE} |base64 > e_${PDBPWDFILE} - -#Ecrypted secrets creation -kubectl create secret generic wbuser --from-file=e_${WBUSERFILE} -n ${CDBNAMESPACE} -kubectl create secret generic wbpass --from-file=e_${WBPASSFILE} -n ${CDBNAMESPACE} -kubectl create secret generic wbuser --from-file=e_${WBUSERFILE} -n ${PDBNAMESPACE} -kubectl create secret generic wbpass --from-file=e_${WBPASSFILE} -n ${PDBNAMESPACE} -kubectl create secret generic cdbpwd --from-file=e_${CDBPWDFILE} -n ${CDBNAMESPACE} -kubectl create secret generic cdbusr --from-file=e_${CDBUSRFILE} -n ${CDBNAMESPACE} -kubectl create secret generic syspwd --from-file=e_${SYSPWDFILE} -n ${CDBNAMESPACE} -kubectl create secret generic ordpwd --from-file=e_${ORDPWDFILE} -n ${CDBNAMESPACE} -kubectl create secret generic pdbusr --from-file=e_${PDBUSRFILE} -n ${PDBNAMESPACE} -kubectl create secret generic pdbpwd --from-file=e_${PDBPWDFILE} -n ${PDBNAMESPACE} - -#Get rid of the swap files -rm ${WBUSERFILE} ${WBPASSFILE} ${CDBPWDFILE} ${CDBUSRFILE} \ - ${SYSPWDFILE} ${ORDPWDFILE} ${PDBUSRFILE} ${PDBPWDFILE} \ - e_${WBUSERFILE} e_${WBPASSFILE} e_${CDBPWDFILE} e_${CDBUSRFILE} \ - e_${SYSPWDFILE} e_${ORDPWDFILE} e_${PDBUSRFILE} e_${PDBPWDFILE} -``` - -Check Secrets details - -```bash -kubectl describe secrets syspwd -n cdbnamespace -Name: syspwd -Namespace: cdbnamespace -Labels: -Annotations: - -Type: Opaque - -Data -==== -e_syspwd.txt: 349 bytes -``` -Example of `yaml` file Secret section: - -```yaml -[...] - sysAdminPwd: - secret: - secretName: "syspwd" - key: "e_syspwd.txt" - ordsPwd: - secret: - secretName: "ordpwd" - key: "e_ordpwd.txt" -[...] -``` - -## CDB CRD - -The Oracle Database Operator Multitenant Controller creates the CDB as a custom resource object kind that models a target CDB as a native Kubernetes object. This object kind is used only to create Pods to connect to the target CDB to perform PDB-LM operations. Each CDB resource follows the CDB CRD as defined here: [`config/crd/bases/database.oracle.com_cdbs.yaml`](../../../config/crd/bases/database.oracle.com_cdbs.yaml) - -To create a CDB CRD, use this example`.yaml` file: [`cdb_create.yaml`](../multitenant/provisioning/singlenamespace/cdb_create.yaml) - -**Note:** The password and username fields in this *cdb.yaml* Yaml are the Kubernetes Secrets created earlier in this procedure. For more information, see the section [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). To understand more about creating secrets for pulling images from a Docker private registry, see [Kubernetes Private Registry Documenation]( https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). - -Create a CDB CRD Resource example - -```bash -kubectl apply -f cdb_create.yaml -``` - -see [usecase01][uc01] and usecase02[uc02] for more information about file configuration - -## PDB CRD - -The Oracle Database Operator Multitenant Controller creates the PDB object kind as a custom resource that models a PDB as a native Kubernetes object. There is a one-to-one mapping between the actual PDB and the Kubernetes PDB Custom Resource. You cannot have more than one Kubernetes resource for a target PDB. This PDB resource can be used to perform PDB-LM operations by specifying the action attribute in the PDB Specs. Each PDB resource follows the PDB CRD as defined here: [config/crd/bases/database.oracle.com_pdbs.yaml](../../../config/crd/bases/database.oracle.com_pdbs.yaml) - -Yaml file [pdb_create.yaml](../multitenant/provisioning/singlenamespace/pdb_create.yaml) to create a pdb - -```bash -kubectl apply -f pdb_create.yaml -``` - -## CRD TABLE PARAMETERS - -| yaml file parameters | value | description /ords parameter | CRD | -|------------------ |--------------------------- |-------------------------------------------------------------------------------|-----------| -| dbserver | or | [--db-hostname][1] | CDB | -| dbTnsurl | | [--db-custom-url/db.customURL][dbtnsurl] | CDB | -| port | | [--db-port][2] | CDB | -| cdbName | | Container Name | CDB | -| name | | ORDS podname prefix in `cdb.yaml` | CDB | -| name | | Pdb resource in `pdb.yaml` | PDB | -| ordsImage | ords-dboper:latest | ORDS pod public container registry | CDB | -| pdbName | | Pluggable database (PDB) name | Container database (CDB) | -| servicename | | [--db-servicename][3] | CDB | -| sysadmin_user | | [--admin-user][adminuser] | CDB | -| sysadmin_pwd | | [--password-stdin][pwdstdin] | CDB | -| cdbadmin_user | | [db.cdb.adminUser][1] | CDB | -| cdbadmin_pwd | | [db.cdb.adminUser.password][cdbadminpwd] | CDB | -| webserver_user | | [https user][http] NOT A DB USER | CDB PDB | -| webserver_pwd | | [http user password][http] | CDB PDB | -| ords_pwd | | [ORDS_PUBLIC_USER password][public_user] | CDB | -| pdbTlsKey | | [standalone.https.cert.key][key] | PDB | -| pdbTlsCrt | | [standalone.https.cert][cr] | PDB | -| pdbTlsCat | | certificate authority | PDB | -| cdbTlsKey | | [standalone.https.cert.key][key] | CDB | -| cdbTlsCrt | | [standalone.https.cert][cr] | CDB | -| cdbTlsCat | | Certificate authority | CDB | -| cdbOrdsPrvKey | | Private key | CDB | -| pdbOrdsPrvKey | | Private key | PDB | -| xmlFileName | | Path for the unplug and plug operation | PDB | -| srcPdbName | | Name of the database that you want to be cloned | PDB | -| action | | Create open close delete clone plug unplug and map | PDB | -| deletePdbCascade | boolean | Delete PDBs cascade during CDB deletion | CDB | -| assertivePdbDeletion | boolean | Deleting the PDB crd means deleting the PDB as well | PDB | -| fileNameConversions | | Used for database cloning | PDB | -| totalSize | | `dbsize` | PDB | -| pdbState | | Change PDB state | PDB | -| modifyOption | | To be used along with `pdbState` | PDB | -| dropAction | | Delete datafiles during PDB deletion | PDB | -| sourceFileNameConversions | | [sourceFileNameConversions(optional): string][4] | PDB | -| tdeKeystorePath | | [tdeKeystorePath][tdeKeystorePath] | N/A | -| tdeExport | | [tdeExport] | N/A ] -| tdeSecret | | [tdeSecret][tdeSecret] | N/A | -| tdePassword | | [tdeSecret][tdeSecret] | N/A | - - - - -## Usecases files list - -### Single Namespace - -1. [Create CDB](./provisioning/singlenamespace/cdb_create.yaml) -2. [Create PDB](./provisioning/singlenamespace/pdb_create.yaml) -3. [Clone PDB](./provisioning/singlenamespace/pdb_clone.yaml) -4. [Open PDB](./provisioning/singlenamespace/pdb_open.yaml) -4. [Close PDB](./provisioning/singlenamespace/pdb_close.yaml) -5. [Delete PDB](./provisioning/singlenamespace/pdb_delete.yaml) -6. [Unplug PDB](./provisioning/singlenamespace/pdb_unplug.yaml) -7. [Plug PDB](./provisioning/singlenamespace/pdb_plug.yaml) - -### Multiple namespace (cdbnamespace,dbnamespace) - -1. [Create CDB](./provisioning/multinamespace/cdb_create.yaml) -2. [Create PDB](./provisioning/multinamespace/pdb_create.yaml) -3. [Clone PDB](./provisioning/multinamespace/pdb_clone.yaml) -4. [Open PDB](./provisioning/multinamespace/pdb_open.yaml) -4. [Close PDB](./provisioning/multinamespace/pdb_close.yaml) -5. [Delete PDB](./provisioning/multinamespace/pdb_delete.yaml) -6. [Unplug PDB](./provisioning/multinamespace/pdb_unplug.yaml) - -## Known issues - - - ORDS installation failure if pluaggable databases in the container db are not openedS - - - Version 1.1.0: encoded password for https authentication may include carriage return as consequence the https request fails with http 404 error. W/A generate encoded password using **printf** instead of **echo**. - - - pdb controller authentication suddenly fails without any system change. Check the certificate expiration date **openssl .... -days 365** - - - Nothing happens after applying cdb yaml files: Make sure to have properly configured the WHATCH_NAMESPACE list in the operator yaml file - - [okelink]:https://docs.oracle.com/en-us/iaas/Content/ContEng/Concepts/contengoverview.htm - - [ordsdoc]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.1/index.html - - [uc01]:../multitenant/usecase01/README.md - - [uc02]:../multitenant/usecase02/README.md - - [oradocpdb]:https://docs.oracle.com/en/database/oracle/oracle-database/21/multi/introduction-to-the-multitenant-architecture.html#GUID-AB84D6C9-4BBE-4D36-992F-2BB85739329F - - [1]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation - - [2]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-E9625FAB-9BC8-468B-9FF9-443C88D76FA1:~:text=Table%202%2D2%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation - - [3]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-DAA027FA-A4A6-43E1-B8DD-C92B330C2341:~:text=%2D%2Ddb%2Dservicename%20%3Cstring%3E - - [4]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.3/orrst/op-database-pdbs-post.html - -[adminuser]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22:~:text=Table%202%2D6%20Command%20Options%20for%20Uninstall%20CLI - -[public_user]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/using-multitenant-architecture-oracle-rest-data-services.html#GUID-E64A141A-A71F-4979-8D33-C5F8496D3C19:~:text=Preinstallation%20Tasks%20for%20Oracle%20REST%20Data%20Services%20CDB%20Installation - -[key]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=standalone.https.cert.key - -[cr]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0 - -[cdbadminpwd]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/about-REST-configuration-files.html#GUID-006F916B-8594-4A78-B500-BB85F35C12A0:~:text=Table%20C%2D1%20Oracle%20REST%20Data%20Services%20Configuration%20Settings - - -[pwdstdin]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-88479C84-CAC1-4133-A33E-7995A645EC05:~:text=default%20database%20pool.-,2.1.4.1%20Understanding%20Command%20Options%20for%20Command%2DLine%20Interface%20Installation,-Table%202%2D2 - -[http]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-BEECC057-A8F5-4EAB-B88E-9828C2809CD8:~:text=Example%3A%20delete%20%5B%2D%2Dglobal%5D-,user%20add,-Add%20a%20user - -[dbtnsurl]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.2/ordig/installing-and-configuring-oracle-rest-data-services.html#GUID-A9AED253-4EEC-4E13-A0C4-B7CE82EC1C22 - -[tdeKeystorePath]:https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/21.4/orrst/op-database-pdbs-pdb_name-post.html - -[tdeSecret]:https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ADMINISTER-KEY-MANAGEMENT.html#GUID-E5B2746F-19DC-4E94-83EC-A6A5C84A3EA9 -~ - - - - - diff --git a/docs/multitenant/ords-based/images/Generalschema2.jpg b/docs/multitenant/ords-based/images/Generalschema2.jpg deleted file mode 100644 index 7e7c20c045cf40599983304a17c99bd187e9ebec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96239 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∨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